i::Handle<i::String> str = Utils::OpenHandle(this);
// Flatten the string for efficiency. This applies whether we are
// using StringInputBuffer or Get(i) to access the characters.
- str->TryFlatten();
+ str->TryFlatten(i::StringShape(*str));
int end = length;
if ( (length == -1) || (length > str->length() - start) )
end = str->length() - start;
i::Handle<i::String> str = Utils::OpenHandle(this);
// Flatten the string for efficiency. This applies whether we are
// using StringInputBuffer or Get(i) to access the characters.
- str->TryFlatten();
+ str->TryFlatten(i::StringShape(*str));
int end = length;
if ( (length == -1) || (length > str->length() - start) )
end = str->length() - start;
bool v8::String::IsExternal() {
EnsureInitialized("v8::String::IsExternal()");
i::Handle<i::String> str = Utils::OpenHandle(this);
- return str->IsExternalTwoByteString();
+ i::StringShape shape(*str);
+ return shape.IsExternalTwoByte();
}
bool v8::String::IsExternalAscii() {
EnsureInitialized("v8::String::IsExternalAscii()");
i::Handle<i::String> str = Utils::OpenHandle(this);
- return str->IsExternalAsciiString();
+ i::StringShape shape(*str);
+ return shape.IsExternalAscii();
}
// function and insert it into the cache.
if (!cache->Lookup(name, &boilerplate)) {
#ifdef DEBUG
- ASSERT(source->IsAsciiRepresentation());
+ ASSERT(StringShape(*source).IsAsciiRepresentation());
#endif
Handle<String> script_name = Factory::NewStringFromUtf8(name);
boilerplate =
{&v8::internal::CodeGenerator::GenerateObjectEquals,
"_ObjectEquals"}
};
- if (node->name()->length() > 0 && node->name()->Get(0) == '_') {
+ Handle<String> name = node->name();
+ StringShape shape(*name);
+ if (name->length(shape) > 0 && name->Get(shape, 0) == '_') {
for (unsigned i = 0;
i < sizeof(kInlineRuntimeLUT) / sizeof(InlineRuntimeLUT);
i++) {
const InlineRuntimeLUT* entry = kInlineRuntimeLUT + i;
- if (node->name()->IsEqualTo(CStrVector(entry->name))) {
+ if (name->IsEqualTo(CStrVector(entry->name))) {
((*this).*(entry->method))(args);
return true;
}
int line_offset, int column_offset,
v8::Extension* extension,
ScriptDataImpl* input_pre_data) {
- Counters::total_load_size.Increment(source->length());
- Counters::total_compile_size.Increment(source->length());
+ int source_length = source->length();
+ Counters::total_load_size.Increment(source_length);
+ Counters::total_compile_size.Increment(source_length);
// The VM is in the COMPILER state until exiting this function.
VMState state(COMPILER);
if (result.is_null()) {
// No cache entry found. Do pre-parsing and compile the script.
ScriptDataImpl* pre_data = input_pre_data;
- if (pre_data == NULL && source->length() >= FLAG_min_preparse_length) {
+ if (pre_data == NULL && source_length >= FLAG_min_preparse_length) {
Access<SafeStringInputBuffer> buf(&safe_string_input_buffer);
buf->Reset(source.location());
pre_data = PreParse(buf.value(), extension);
Handle<JSFunction> Compiler::CompileEval(Handle<String> source,
int line_offset,
bool is_global) {
- Counters::total_eval_size.Increment(source->length());
- Counters::total_compile_size.Increment(source->length());
+ StringShape source_shape(*source);
+ int source_length = source->length(source_shape);
+ Counters::total_eval_size.Increment(source_length);
+ Counters::total_compile_size.Increment(source_length);
// The VM is in the COMPILER state until exiting this function.
VMState state(COMPILER);
static inline int GetChar(String* str, int index) {
- return str->Get(index);
+ StringShape shape(str);
+ return str->Get(shape, index);
}
static inline const char* GetCString(String* str, int index) {
- char* result = NewArray<char>(str->length() + 1);
- for (int i = index; i < str->length(); i++) {
- if (str->Get(i) <= 127) {
- result[i - index] = static_cast<char>(str->Get(i));
+ StringShape shape(str);
+ int length = str->length(shape);
+ char* result = NewArray<char>(length + 1);
+ for (int i = index; i < length; i++) {
+ uc16 c = str->Get(shape, i);
+ if (c <= 127) {
+ result[i - index] = static_cast<char>(c);
} else {
result[i - index] = 127; // Force number parsing to fail.
}
}
- result[str->length() - index] = '\0';
+ result[length - index] = '\0';
return result;
}
static inline bool IsSpace(String* str, int index) {
- return Scanner::kIsWhiteSpace.get(str->Get(index));
+ StringShape shape(str);
+ return Scanner::kIsWhiteSpace.get(str->Get(shape, index));
}
static inline bool SubStringEquals(String* str, int index, const char* other) {
+ StringShape shape(str);
HandleScope scope;
- int len = strlen(other);
- int end = index + len < str->length() ? index + len : str->length();
+ int str_length = str->length(shape);
+ int other_length = strlen(other);
+ int end = index + other_length < str_length ?
+ index + other_length :
+ str_length;
Handle<String> slice =
- Factory::NewStringSlice(Handle<String>(str), index, end);
- return slice->IsEqualTo(Vector<const char>(other, len));
+ Factory::NewStringSlice(Handle<String>(str), shape, index, end);
+ return slice->IsEqualTo(Vector<const char>(other, other_length));
}
Handle<String> Factory::NewConsString(Handle<String> first,
- Handle<String> second) {
- if (first->length() == 0) return second;
- if (second->length() == 0) return first;
- CALL_HEAP_FUNCTION(Heap::AllocateConsString(*first, *second), String);
-}
-
-
-Handle<String> Factory::NewStringSlice(Handle<String> str, int begin, int end) {
- CALL_HEAP_FUNCTION(str->Slice(begin, end), String);
+ StringShape first_shape,
+ Handle<String> second,
+ StringShape second_shape) {
+ if (first->length(first_shape) == 0) return second;
+ if (second->length(second_shape) == 0) return first;
+ CALL_HEAP_FUNCTION(Heap::AllocateConsString(*first,
+ first_shape,
+ *second,
+ second_shape),
+ String);
+}
+
+
+Handle<String> Factory::NewStringSlice(Handle<String> str,
+ StringShape shape,
+ int begin,
+ int end) {
+ CALL_HEAP_FUNCTION(str->Slice(shape, begin, end), String);
}
// Create a new cons string object which consists of a pair of strings.
static Handle<String> NewConsString(Handle<String> first,
- Handle<String> second);
+ StringShape first_shape,
+ Handle<String> second,
+ StringShape second_shape);
// Create a new sliced string object which represents a substring of a
// backing string.
- static Handle<String> NewStringSlice(Handle<String> str, int begin, int end);
+ static Handle<String> NewStringSlice(Handle<String> str,
+ StringShape shape,
+ int begin,
+ int end);
// Creates a new external String object. There are two String encodings
// in the system: ASCII and two byte. Unlike other String types, it does
void FlattenString(Handle<String> string) {
- if (string->IsFlat()) return;
- CALL_HEAP_FUNCTION_VOID(string->Flatten());
- ASSERT(string->IsFlat());
+ StringShape shape(*string);
+ if (string->IsFlat(shape)) return;
+ CALL_HEAP_FUNCTION_VOID(string->Flatten(shape));
+ ASSERT(string->IsFlat(StringShape(*string)));
}
Handle<String> SubString(Handle<String> str, int start, int end) {
- CALL_HEAP_FUNCTION(str->Slice(start, end), String);
+ StringShape shape(*str);
+ CALL_HEAP_FUNCTION(str->Slice(shape, start, end), String);
}
return ScavengeObjectSlow(p, object);
}
+
static inline bool IsShortcutCandidate(HeapObject* object, Map* map) {
- // A ConString object with Heap::empty_string() as the right side
+ // A ConsString object with Heap::empty_string() as the right side
// is a candidate for being shortcut by the scavenger.
ASSERT(object->map() == map);
- return (map->instance_type() < FIRST_NONSTRING_TYPE) &&
- (String::cast(object)->map_representation_tag(map) == kConsStringTag) &&
- (ConsString::cast(object)->second() == Heap::empty_string());
+ if (map->instance_type() >= FIRST_NONSTRING_TYPE) return false;
+ StringShape shape(map);
+ return (shape.representation_tag() == kConsStringTag) &&
+ (ConsString::cast(object)->unchecked_second() == Heap::empty_string());
}
// Optimization: Bypass flattened ConsString objects.
if (IsShortcutCandidate(object, first_word.ToMap())) {
- object = HeapObject::cast(ConsString::cast(object)->first());
+ object = HeapObject::cast(ConsString::cast(object)->unchecked_first());
*p = object;
// After patching *p we have to repeat the checks that object is in the
// active semispace of the young generation and not already copied.
}
-Object* Heap::AllocateConsString(String* first, String* second) {
- int first_length = first->length();
- int second_length = second->length();
+Object* Heap::AllocateConsString(String* first,
+ StringShape first_shape,
+ String* second,
+ StringShape second_shape) {
+ int first_length = first->length(first_shape);
+ int second_length = second->length(second_shape);
int length = first_length + second_length;
- bool is_ascii = first->is_ascii_representation()
- && second->is_ascii_representation();
+ bool is_ascii = first_shape.IsAsciiRepresentation()
+ && second_shape.IsAsciiRepresentation();
// If the resulting string is small make a flat string.
if (length < String::kMinNonFlatLength) {
- ASSERT(first->IsFlat());
- ASSERT(second->IsFlat());
+ ASSERT(first->IsFlat(first_shape));
+ ASSERT(second->IsFlat(second_shape));
if (is_ascii) {
Object* result = AllocateRawAsciiString(length);
if (result->IsFailure()) return result;
// Copy the characters into the new object.
char* dest = SeqAsciiString::cast(result)->GetChars();
- String::WriteToFlat(first, dest, 0, first_length);
- String::WriteToFlat(second, dest + first_length, 0, second_length);
+ String::WriteToFlat(first, first_shape, dest, 0, first_length);
+ String::WriteToFlat(second,
+ second_shape,
+ dest + first_length,
+ 0,
+ second_length);
return result;
} else {
Object* result = AllocateRawTwoByteString(length);
if (result->IsFailure()) return result;
// Copy the characters into the new object.
uc16* dest = SeqTwoByteString::cast(result)->GetChars();
- String::WriteToFlat(first, dest, 0, first_length);
- String::WriteToFlat(second, dest + first_length, 0, second_length);
+ String::WriteToFlat(first, first_shape, dest, 0, first_length);
+ String::WriteToFlat(second,
+ second_shape,
+ dest + first_length,
+ 0,
+ second_length);
return result;
}
}
}
-Object* Heap::AllocateSlicedString(String* buffer, int start, int end) {
+Object* Heap::AllocateSlicedString(String* buffer,
+ StringShape buffer_shape,
+ int start,
+ int end) {
int length = end - start;
// If the resulting string is small make a sub string.
if (end - start <= String::kMinNonFlatLength) {
- return Heap::AllocateSubString(buffer, start, end);
+ return Heap::AllocateSubString(buffer, buffer_shape, start, end);
}
Map* map;
if (length <= String::kMaxShortStringSize) {
- map = buffer->is_ascii_representation() ? short_sliced_ascii_string_map()
- : short_sliced_string_map();
+ map = buffer_shape.IsAsciiRepresentation() ?
+ short_sliced_ascii_string_map() :
+ short_sliced_string_map();
} else if (length <= String::kMaxMediumStringSize) {
- map = buffer->is_ascii_representation() ? medium_sliced_ascii_string_map()
- : medium_sliced_string_map();
+ map = buffer_shape.IsAsciiRepresentation() ?
+ medium_sliced_ascii_string_map() :
+ medium_sliced_string_map();
} else {
- map = buffer->is_ascii_representation() ? long_sliced_ascii_string_map()
- : long_sliced_string_map();
+ map = buffer_shape.IsAsciiRepresentation() ?
+ long_sliced_ascii_string_map() :
+ long_sliced_string_map();
}
Object* result = Allocate(map, NEW_SPACE);
}
-Object* Heap::AllocateSubString(String* buffer, int start, int end) {
+Object* Heap::AllocateSubString(String* buffer,
+ StringShape buffer_shape,
+ int start,
+ int end) {
int length = end - start;
if (length == 1) {
- return Heap::LookupSingleCharacterStringFromCode(buffer->Get(start));
+ return Heap::LookupSingleCharacterStringFromCode(
+ buffer->Get(buffer_shape, start));
}
// Make an attempt to flatten the buffer to reduce access time.
- buffer->TryFlatten();
+ if (!buffer->IsFlat(buffer_shape)) {
+ buffer->TryFlatten(buffer_shape);
+ buffer_shape = StringShape(buffer);
+ }
- Object* result = buffer->is_ascii_representation()
+ Object* result = buffer_shape.IsAsciiRepresentation()
? AllocateRawAsciiString(length)
: AllocateRawTwoByteString(length);
if (result->IsFailure()) return result;
// Copy the characters into the new object.
String* string_result = String::cast(result);
+ StringShape result_shape(string_result);
StringHasher hasher(length);
int i = 0;
for (; i < length && hasher.is_array_index(); i++) {
- uc32 c = buffer->Get(start + i);
+ uc32 c = buffer->Get(buffer_shape, start + i);
hasher.AddCharacter(c);
- string_result->Set(i, c);
+ string_result->Set(result_shape, i, c);
}
for (; i < length; i++) {
- uc32 c = buffer->Get(start + i);
+ uc32 c = buffer->Get(buffer_shape, start + i);
hasher.AddCharacterNoIndex(c);
- string_result->Set(i, c);
+ string_result->Set(result_shape, i, c);
}
string_result->set_length_field(hasher.GetHashField());
return result;
Object* result = Heap::AllocateRawTwoByteString(1);
if (result->IsFailure()) return result;
- String::cast(result)->Set(0, code);
- return result;
+ String* answer = String::cast(result);
+ answer->Set(StringShape(answer), 0, code);
+ return answer;
}
// Convert and copy the characters into the new object.
String* string_result = String::cast(result);
decoder->Reset(string.start(), string.length());
+ StringShape result_shape(string_result);
for (int i = 0; i < chars; i++) {
uc32 r = decoder->GetNext();
- string_result->Set(i, r);
+ string_result->Set(result_shape, i, r);
}
return result;
}
// Copy the characters into the new object, which may be either ASCII or
// UTF-16.
String* string_result = String::cast(result);
+ StringShape result_shape(string_result);
for (int i = 0; i < string.length(); i++) {
- string_result->Set(i, string[i]);
+ string_result->Set(result_shape, i, string[i]);
}
return result;
}
reinterpret_cast<HeapObject*>(result)->set_map(map);
// The hash value contains the length of the string.
- String::cast(result)->set_length_field(length_field);
+ String* answer = String::cast(result);
+ StringShape answer_shape(answer);
+ answer->set_length_field(length_field);
- ASSERT_EQ(size, String::cast(result)->Size());
+ ASSERT_EQ(size, answer->Size());
// Fill in the characters.
for (int i = 0; i < chars; i++) {
- String::cast(result)->Set(i, buffer->GetNext());
+ answer->Set(answer_shape, i, buffer->GetNext());
}
- return result;
+ return answer;
}
// Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
// failed.
// Please note this does not perform a garbage collection.
- static Object* AllocateConsString(String* first, String* second);
+ static Object* AllocateConsString(String* first,
+ StringShape first_shape,
+ String* second,
+ StringShape second_shape);
// Allocates a new sliced string object which is a slice of an underlying
// string buffer stretching from the index start (inclusive) to the index
// Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
// failed.
// Please note this does not perform a garbage collection.
- static Object* AllocateSlicedString(String* buffer, int start, int end);
+ static Object* AllocateSlicedString(String* buffer,
+ StringShape buffer_shape,
+ int start,
+ int end);
// Allocates a new sub string object which is a substring of an underlying
// string buffer stretching from the index start (inclusive) to the index
// Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
// failed.
// Please note this does not perform a garbage collection.
- static Object* AllocateSubString(String* buffer, int start, int end);
+ static Object* AllocateSubString(String* buffer,
+ StringShape buffer_shape,
+ int start,
+ int end);
// Allocate a new external string object, which is backed by a string
// resource that resides outside the V8 heap.
// Converts a source string to a 16 bit flat string or a SlicedString containing
// a 16 bit flat string).
Handle<String> RegExpImpl::StringToTwoByte(Handle<String> pattern) {
- if (!pattern->IsFlat()) {
+ StringShape shape(*pattern);
+ if (!pattern->IsFlat(shape)) {
FlattenString(pattern);
}
- Handle<String> flat_string(pattern->IsConsString() ?
+ Handle<String> flat_string(shape.IsCons() ?
String::cast(ConsString::cast(*pattern)->first()) :
*pattern);
- ASSERT(!flat_string->IsConsString());
- ASSERT(flat_string->IsSeqString() || flat_string->IsSlicedString() ||
- flat_string->IsExternalString());
- if (!flat_string->IsAsciiRepresentation()) {
+ ASSERT(flat_string->IsString());
+ StringShape flat_shape(*flat_string);
+ ASSERT(!flat_shape.IsCons());
+ ASSERT(flat_shape.IsSequential() ||
+ flat_shape.IsSliced() ||
+ flat_shape.IsExternal());
+ if (!flat_shape.IsAsciiRepresentation()) {
return flat_string;
}
+ int len = flat_string->length(flat_shape);
Handle<String> two_byte_string =
- Factory::NewRawTwoByteString(flat_string->length(), TENURED);
- static StringInputBuffer convert_to_two_byte_buffer;
- convert_to_two_byte_buffer.Reset(*flat_string);
- for (int i = 0; convert_to_two_byte_buffer.has_more(); i++) {
- two_byte_string->Set(i, convert_to_two_byte_buffer.GetNext());
- }
+ Factory::NewRawTwoByteString(len, TENURED);
+ uc16* dest = SeqTwoByteString::cast(*two_byte_string)->GetChars();
+ String::WriteToFlat(*flat_string, flat_shape, dest, 0, len);
return two_byte_string;
}
static JSRegExp::Flags RegExpFlagsFromString(Handle<String> str) {
int flags = JSRegExp::NONE;
- for (int i = 0; i < str->length(); i++) {
- switch (str->Get(i)) {
+ StringShape shape(*str);
+ for (int i = 0; i < str->length(shape); i++) {
+ switch (str->Get(shape, i)) {
case 'i':
flags |= JSRegExp::IGNORE_CASE;
break;
Handle<FixedArray> cached = CompilationCache::LookupRegExp(pattern, flags);
bool in_cache = !cached.is_null();
Handle<Object> result;
+ StringShape shape(*pattern);
if (in_cache) {
re->set_data(*cached);
result = re;
} else {
bool is_atom = !flags.is_ignore_case();
- for (int i = 0; is_atom && i < pattern->length(); i++) {
- if (is_reg_exp_special_char.get(pattern->Get(i)))
+ for (int i = 0; is_atom && i < pattern->length(shape); i++) {
+ if (is_reg_exp_special_char.get(pattern->Get(shape, i)))
is_atom = false;
}
if (is_atom) {
#ifdef ENABLE_LOGGING_AND_PROFILING
void Logger::LogString(Handle<String> str) {
- int len = str->length();
+ StringShape shape(*str);
+ int len = str->length(shape);
if (len > 256)
len = 256;
for (int i = 0; i < len; i++) {
- uc32 c = str->Get(i);
+ uc32 c = str->Get(shape, i);
if (c < 32 || (c > 126 && c <= 255)) {
fprintf(logfile_, "\\x%02x", c);
} else if (c > 255) {
LogRegExpSource(regexp);
fprintf(logfile_, ",");
LogString(input_string);
- fprintf(logfile_, ",%d..%d\n", start_index, input_string->length());
+ StringShape shape(*input_string);
+ fprintf(logfile_, ",%d..%d\n", start_index, input_string->length(shape));
#endif
}
void String::StringPrint() {
- if (IsSymbol()) {
+ StringShape shape(this);
+ if (shape.IsSymbol()) {
PrintF("#");
- } else if (IsConsString()) {
+ } else if (shape.IsCons()) {
PrintF("c\"");
} else {
PrintF("\"");
}
for (int i = 0; i < length(); i++) {
- PrintF("%c", Get(i));
+ PrintF("%c", Get(shape, i));
}
- if (!IsSymbol()) PrintF("\"");
+ if (!shape.IsSymbol()) PrintF("\"");
}
}
-bool Object::IsSeqString() {
- return IsString()
- && (String::cast(this)->representation_tag() == kSeqStringTag);
+bool Object::IsSymbol() {
+ if (!this->IsHeapObject()) return false;
+ uint32_t type = HeapObject::cast(this)->map()->instance_type();
+ return (type & (kIsNotStringMask | kIsSymbolMask)) ==
+ (kStringTag | kSymbolTag);
}
-bool Object::IsSeqAsciiString() {
- return IsSeqString()
- && String::cast(this)->IsAsciiRepresentation();
+bool Object::IsConsString() {
+ if (!this->IsHeapObject()) return false;
+ uint32_t type = HeapObject::cast(this)->map()->instance_type();
+ return (type & (kIsNotStringMask | kStringRepresentationMask)) ==
+ (kStringTag | kConsStringTag);
+}
+
+
+#ifdef DEBUG
+// These are for cast checks. If you need one of these in release
+// mode you should consider using a StringShape before moving it out
+// of the ifdef
+
+bool Object::IsSeqString() {
+ if (!IsString()) return false;
+ return StringShape(String::cast(this)).IsSequential();
}
-bool String::IsSeqAsciiString() {
- return (this->representation_tag() == kSeqStringTag)
- && is_ascii_representation();
+bool Object::IsSeqAsciiString() {
+ if (!IsString()) return false;
+ StringShape shape(String::cast(this));
+ return shape.IsSequential() && shape.IsAsciiRepresentation();
}
bool Object::IsSeqTwoByteString() {
- return IsSeqString()
- && !String::cast(this)->IsAsciiRepresentation();
+ if (!IsString()) return false;
+ StringShape shape(String::cast(this));
+ return shape.IsSequential() && shape.IsTwoByteRepresentation();
}
-bool Object::IsAsciiStringRepresentation() {
- return IsString() && (String::cast(this)->is_ascii_representation());
+bool Object::IsExternalString() {
+ if (!IsString()) return false;
+ return StringShape(String::cast(this)).IsExternal();
}
-bool Object::IsTwoByteStringRepresentation() {
- return IsString() && (!String::cast(this)->is_ascii_representation());
+bool Object::IsExternalAsciiString() {
+ if (!IsString()) return false;
+ StringShape shape(String::cast(this));
+ return shape.IsExternal() && shape.IsAsciiRepresentation();
}
-bool Object::IsConsString() {
- return IsString()
- && (String::cast(this)->representation_tag() == kConsStringTag);
+bool Object::IsExternalTwoByteString() {
+ if (!IsString()) return false;
+ StringShape shape(String::cast(this));
+ return shape.IsExternal() && shape.IsTwoByteRepresentation();
}
bool Object::IsSlicedString() {
- return IsString()
- && (String::cast(this)->representation_tag() == kSlicedStringTag);
+ if (!IsString()) return false;
+ return StringShape(String::cast(this)).IsSliced();
}
-bool Object::IsExternalString() {
- return IsString()
- && (String::cast(this)->representation_tag() == kExternalStringTag);
+#endif // DEBUG
+
+
+StringShape::StringShape(String* str)
+ : type_(str->map()->instance_type())
+#ifdef DEBUG
+ , valid_(true)
+#endif // def DEBUG
+ {
+ ASSERT((type_ & kIsNotStringMask) == kStringTag);
}
-bool Object::IsExternalAsciiString() {
- return IsExternalString() && (String::cast(this)->is_ascii_representation());
+StringShape::StringShape(Map* map)
+ : type_(map->instance_type())
+#ifdef DEBUG
+ , valid_(true)
+#endif // def DEBUG
+ {
+ ASSERT((type_ & kIsNotStringMask) == kStringTag);
}
-bool Object::IsExternalTwoByteString() {
- return IsExternalString() && (!String::cast(this)->is_ascii_representation());
+StringShape::StringShape(InstanceType t)
+ : type_(static_cast<uint32_t>(t))
+#ifdef DEBUG
+ , valid_(true)
+#endif // def DEBUG
+ {
+ ASSERT((type_ & kIsNotStringMask) == kStringTag);
}
-bool Object::IsShortString() {
- return IsString() && (String::cast(this)->size_tag() == kShortStringTag);
+bool StringShape::IsSymbol() {
+ ASSERT(valid());
+ return (type_ & kIsSymbolMask) == kSymbolTag;
}
-bool Object::IsMediumString() {
- return IsString() && (String::cast(this)->size_tag() == kMediumStringTag);
+bool StringShape::IsAsciiRepresentation() {
+ return (type_ & kStringEncodingMask) == kAsciiStringTag;
}
-bool Object::IsLongString() {
- return IsString() && (String::cast(this)->size_tag() == kLongStringTag);
+bool StringShape::IsTwoByteRepresentation() {
+ return (type_ & kStringEncodingMask) == kTwoByteStringTag;
}
-bool Object::IsSymbol() {
- return IsString() && (String::cast(this)->is_symbol());
+bool StringShape::IsCons() {
+ return (type_ & kStringRepresentationMask) == kConsStringTag;
+}
+
+
+bool StringShape::IsSliced() {
+ return (type_ & kStringRepresentationMask) == kSlicedStringTag;
+}
+
+
+bool StringShape::IsExternal() {
+ return (type_ & kStringRepresentationMask) == kExternalStringTag;
+}
+
+
+bool StringShape::IsSequential() {
+ return (type_ & kStringRepresentationMask) == kSeqStringTag;
+}
+
+
+StringRepresentationTag StringShape::representation_tag() {
+ uint32_t tag = (type_ & kStringRepresentationMask);
+ return static_cast<StringRepresentationTag>(tag);
+}
+
+
+uint32_t StringShape::full_representation_tag() {
+ return (type_ & (kStringRepresentationMask | kStringEncodingMask));
+}
+
+
+uint32_t StringShape::size_tag() {
+ return (type_ & kStringSizeMask);
+}
+
+
+bool StringShape::IsSequentialAscii() {
+ return full_representation_tag() == (kSeqStringTag | kAsciiStringTag);
+}
+
+
+bool StringShape::IsSequentialTwoByte() {
+ return (type_ & (kStringRepresentationMask | kStringEncodingMask)) ==
+ (kSeqStringTag | kTwoByteStringTag);
+}
+
+
+bool StringShape::IsExternalAscii() {
+ return full_representation_tag() == (kExternalStringTag | kAsciiStringTag);
+}
+
+
+bool StringShape::IsExternalTwoByte() {
+ return (type_ & (kStringRepresentationMask | kStringEncodingMask)) ==
+ (kExternalStringTag | kTwoByteStringTag);
}
int DescriptorArray::Search(String* name) {
SLOW_ASSERT(IsSortedNoDuplicates());
+ StringShape shape(name);
+
// Check for empty descriptor array.
int nof = number_of_descriptors();
if (nof == 0) return kNotFound;
// Fast case: do linear search for small arrays.
const int kMaxElementsForLinearSearch = 8;
- if (name->IsSymbol() && nof < kMaxElementsForLinearSearch) {
+ if (shape.IsSymbol() && nof < kMaxElementsForLinearSearch) {
return LinearSearch(name, nof);
}
bool String::Equals(String* other) {
if (other == this) return true;
- if (IsSymbol() && other->IsSymbol()) return false;
- return SlowEquals(other);
+ StringShape this_shape(this);
+ StringShape other_shape(other);
+ if (this_shape.IsSymbol() && other_shape.IsSymbol()) return false;
+ return SlowEquals(this_shape, other, other_shape);
}
-int String::length() {
+int String::length(StringShape shape) {
+ ASSERT(shape.type() == StringShape(this).type());
uint32_t len = READ_INT_FIELD(this, kLengthOffset);
ASSERT(kShortStringTag + kLongLengthShift == kShortLengthShift);
ASSERT(kMediumStringTag + kLongLengthShift == kMediumLengthShift);
ASSERT(kLongStringTag == 0);
- return len >> (size_tag() + kLongLengthShift);
+ return len >> (shape.size_tag() + kLongLengthShift);
+}
+
+
+int String::length() {
+ return length(StringShape(this));
}
ASSERT(kMediumStringTag + kLongLengthShift == kMediumLengthShift);
ASSERT(kLongStringTag == 0);
+ StringShape shape(this);
WRITE_INT_FIELD(this,
kLengthOffset,
- value << (size_tag() + kLongLengthShift));
+ value << (shape.size_tag() + kLongLengthShift));
}
}
-void String::TryFlatten() {
+void String::TryFlatten(StringShape shape) {
+ ASSERT(shape.type() == StringShape(this).type());
// We don't need to flatten strings that are already flat. Since this code
// is inlined, it can be helpful in the flat case to not call out to Flatten.
- StringRepresentationTag str_type = representation_tag();
- if (str_type != kSeqStringTag && str_type != kExternalStringTag) {
- Flatten();
+ if (!IsFlat(shape)) {
+ Flatten(shape);
}
}
-uint16_t String::Get(int index) {
- ASSERT(index >= 0 && index < length());
- switch (representation_tag()) {
- case kSeqStringTag:
- return is_ascii_representation()
- ? SeqAsciiString::cast(this)->SeqAsciiStringGet(index)
- : SeqTwoByteString::cast(this)->SeqTwoByteStringGet(index);
- case kConsStringTag:
+uint16_t String::Get(StringShape shape, int index) {
+ ASSERT(shape.type() == StringShape(this).type());
+ ASSERT(index >= 0 && index < length(shape));
+ switch (shape.full_representation_tag()) {
+ case kSeqStringTag | kAsciiStringTag:
+ return SeqAsciiString::cast(this)->SeqAsciiStringGet(index);
+ case kSeqStringTag | kTwoByteStringTag:
+ return SeqTwoByteString::cast(this)->SeqTwoByteStringGet(index);
+ case kConsStringTag | kAsciiStringTag:
+ case kConsStringTag | kTwoByteStringTag:
return ConsString::cast(this)->ConsStringGet(index);
- case kSlicedStringTag:
+ case kSlicedStringTag | kAsciiStringTag:
+ case kSlicedStringTag | kTwoByteStringTag:
return SlicedString::cast(this)->SlicedStringGet(index);
- case kExternalStringTag:
- return is_ascii_representation()
- ? ExternalAsciiString::cast(this)->ExternalAsciiStringGet(index)
- : ExternalTwoByteString::cast(this)->ExternalTwoByteStringGet(index);
+ case kExternalStringTag | kAsciiStringTag:
+ return ExternalAsciiString::cast(this)->ExternalAsciiStringGet(index);
+ case kExternalStringTag | kTwoByteStringTag:
+ return ExternalTwoByteString::cast(this)->ExternalTwoByteStringGet(index);
default:
break;
}
}
-void String::Set(int index, uint16_t value) {
- ASSERT(index >= 0 && index < length());
- ASSERT(IsSeqString());
+void String::Set(StringShape shape, int index, uint16_t value) {
+ ASSERT(shape.type() == StringShape(this).type());
+ ASSERT(shape.type() == StringShape(this).type());
+ ASSERT(index >= 0 && index < length(shape));
+ ASSERT(shape.IsSequential());
- return is_ascii_representation()
+ return shape.IsAsciiRepresentation()
? SeqAsciiString::cast(this)->SeqAsciiStringSet(index, value)
: SeqTwoByteString::cast(this)->SeqTwoByteStringSet(index, value);
}
-bool String::IsAsciiRepresentation() {
- return is_ascii_representation();
-}
-
-
-bool String::StringIsConsString() {
- return representation_tag() == kConsStringTag;
-}
-
-
-bool String::StringIsSlicedString() {
- return representation_tag() == kSlicedStringTag;
-}
-
-
-uint32_t String::size_tag() {
- return map_size_tag(map());
-}
-
-
-uint32_t String::map_size_tag(Map* map) {
- return map->instance_type() & kStringSizeMask;
-}
-
-
-bool String::is_symbol() {
- return is_symbol_map(map());
-}
-
-
-bool String::is_symbol_map(Map* map) {
- return (map->instance_type() & kIsSymbolMask) != 0;
-}
-
-
-bool String::is_ascii_representation() {
- return is_ascii_representation_map(map());
-}
-
-
-bool String::is_ascii_representation_map(Map* map) {
- return (map->instance_type() & kStringEncodingMask) != 0;
-}
-
-
-int String::full_representation_tag() {
- return map()->instance_type() &
- (kStringRepresentationMask | kStringEncodingMask);
-}
-
-
-StringRepresentationTag String::representation_tag() {
- return map_representation_tag(map());
-}
-
-
-StringRepresentationTag String::map_representation_tag(Map* map) {
- uint32_t tag = map->instance_type() & kStringRepresentationMask;
- return static_cast<StringRepresentationTag>(tag);
-}
-
-
-bool String::IsFlat() {
- switch (this->representation_tag()) {
- case kConsStringTag:
+bool String::IsFlat(StringShape shape) {
+ ASSERT(shape.type() == StringShape(this).type());
+ switch (shape.representation_tag()) {
+ case kConsStringTag: {
+ String* second = ConsString::cast(this)->second();
// Only flattened strings have second part empty.
- return String::cast(ConsString::cast(this)->second())->length() == 0;
+ return second->length() == 0;
+ }
case kSlicedStringTag: {
- String* slice = String::cast(SlicedString::cast(this)->buffer());
- StringRepresentationTag tag = slice->representation_tag();
+ StringShape slice_shape = StringShape(SlicedString::cast(this)->buffer());
+ StringRepresentationTag tag = slice_shape.representation_tag();
return tag == kSeqStringTag || tag == kExternalStringTag;
}
default:
}
-int SeqTwoByteString::SeqTwoByteStringSize(Map* map) {
+int SeqTwoByteString::SeqTwoByteStringSize(StringShape shape) {
uint32_t length = READ_INT_FIELD(this, kLengthOffset);
ASSERT(kShortStringTag + kLongLengthShift == kShortLengthShift);
// Use the map (and not 'this') to compute the size tag, since
// TwoByteStringSize is called during GC when maps are encoded.
- length >>= map_size_tag(map) + kLongLengthShift;
+ length >>= shape.size_tag() + kLongLengthShift;
return SizeFor(length);
}
-int SeqAsciiString::SeqAsciiStringSize(Map* map) {
+int SeqAsciiString::SeqAsciiStringSize(StringShape shape) {
uint32_t length = READ_INT_FIELD(this, kLengthOffset);
ASSERT(kShortStringTag + kLongLengthShift == kShortLengthShift);
// Use the map (and not 'this') to compute the size tag, since
// AsciiStringSize is called during GC when maps are encoded.
- length >>= map_size_tag(map) + kLongLengthShift;
+ length >>= shape.size_tag() + kLongLengthShift;
return SizeFor(length);
}
-Object* ConsString::first() {
+String* ConsString::first() {
+ return String::cast(READ_FIELD(this, kFirstOffset));
+}
+
+
+Object* ConsString::unchecked_first() {
return READ_FIELD(this, kFirstOffset);
}
-void ConsString::set_first(Object* value, WriteBarrierMode mode) {
+void ConsString::set_first(String* value, WriteBarrierMode mode) {
WRITE_FIELD(this, kFirstOffset, value);
CONDITIONAL_WRITE_BARRIER(this, kFirstOffset, mode);
}
-Object* ConsString::second() {
+String* ConsString::second() {
+ return String::cast(READ_FIELD(this, kSecondOffset));
+}
+
+
+Object* ConsString::unchecked_second() {
return READ_FIELD(this, kSecondOffset);
}
-void ConsString::set_second(Object* value, WriteBarrierMode mode) {
+void ConsString::set_second(String* value, WriteBarrierMode mode) {
WRITE_FIELD(this, kSecondOffset, value);
CONDITIONAL_WRITE_BARRIER(this, kSecondOffset, mode);
}
-Object* SlicedString::buffer() {
- return READ_FIELD(this, kBufferOffset);
+String* SlicedString::buffer() {
+ return String::cast(READ_FIELD(this, kBufferOffset));
}
-void SlicedString::set_buffer(Object* buffer) {
+void SlicedString::set_buffer(String* buffer) {
WRITE_FIELD(this, kBufferOffset, buffer);
WRITE_BARRIER(this, kBufferOffset);
}
// We don't use the BBC's overcorrect "an historic occasion" though if
// you speak a dialect you may well say "an 'istoric occasion".
static bool AnWord(String* str) {
- if (str->length() == 0) return false; // a nothing
- int c0 = str->Get(0);
- int c1 = str->length() > 1 ? str->Get(1) : 0;
+ StringShape shape(str);
+ if (str->length(shape) == 0) return false; // A nothing.
+ int c0 = str->Get(shape, 0);
+ int c1 = str->length(shape) > 1 ? str->Get(shape, 1) : 0;
if (c0 == 'U') {
if (c1 > 'Z') {
- return true; // an Umpire, but a UTF8String, a U
+ return true; // An Umpire, but a UTF8String, a U.
}
} else if (c0 == 'A' || c0 == 'E' || c0 == 'I' || c0 == 'O') {
- return true; // an Ape, an ABCBook
+ return true; // An Ape, an ABCBook.
} else if ((c1 == 0 || (c1 >= 'A' && c1 <= 'Z')) &&
(c0 == 'F' || c0 == 'H' || c0 == 'M' || c0 == 'N' || c0 == 'R' ||
c0 == 'S' || c0 == 'X')) {
- return true; // an MP3File, an M
+ return true; // An MP3File, an M.
}
return false;
}
-Object* String::Flatten() {
+Object* String::Flatten(StringShape shape) {
#ifdef DEBUG
// Do not attempt to flatten in debug mode when allocation is not
// allowed. This is to avoid an assertion failure when allocating.
if (!Heap::IsAllocationAllowed()) return this;
#endif
- switch (representation_tag()) {
+ switch (shape.representation_tag()) {
case kSlicedStringTag: {
SlicedString* ss = SlicedString::cast(this);
// The SlicedString constructor should ensure that there are no
// SlicedStrings that are constructed directly on top of other
// SlicedStrings.
- ASSERT(!ss->buffer()->IsSlicedString());
- Object* ok = String::cast(ss->buffer())->Flatten();
+ String* buf = ss->buffer();
+ ASSERT(!buf->IsSlicedString());
+ Object* ok = buf->Flatten(StringShape(buf));
if (ok->IsFailure()) return ok;
// Under certain circumstances (TryFlatten fails in String::Slice)
// we can have a cons string under a slice. In this case we need
// to get the flat string out of the cons!
- if (String::cast(ok)->StringIsConsString()) {
+ if (StringShape(String::cast(ok)).IsCons()) {
ss->set_buffer(ConsString::cast(ok)->first());
}
return this;
}
case kConsStringTag: {
ConsString* cs = ConsString::cast(this);
- if (String::cast(cs->second())->length() == 0) {
+ if (cs->second()->length() == 0) {
return this;
}
// There's little point in putting the flat string in new space if the
// cons string is in old space. It can never get GCed until there is
// an old space GC.
PretenureFlag tenure = Heap::InNewSpace(this) ? NOT_TENURED : TENURED;
- int len = length();
+ int len = length(shape);
Object* object;
String* result;
- if (IsAsciiRepresentation()) {
+ if (shape.IsAsciiRepresentation()) {
object = Heap::AllocateRawAsciiString(len, tenure);
if (object->IsFailure()) return object;
result = String::cast(object);
- String* first = String::cast(cs->first());
- int first_length = first->length();
+ String* first = cs->first();
+ StringShape first_shape(first);
+ int first_length = first->length(first_shape);
char* dest = SeqAsciiString::cast(result)->GetChars();
- WriteToFlat(first, dest, 0, first_length);
- WriteToFlat(String::cast(cs->second()),
+ WriteToFlat(first, first_shape, dest, 0, first_length);
+ String* second = cs->second();
+ WriteToFlat(second,
+ StringShape(second),
dest + first_length,
0,
len - first_length);
if (object->IsFailure()) return object;
result = String::cast(object);
uc16* dest = SeqTwoByteString::cast(result)->GetChars();
- String* first = String::cast(cs->first());
- int first_length = first->length();
- WriteToFlat(first, dest, 0, first_length);
- WriteToFlat(String::cast(cs->second()),
+ String* first = cs->first();
+ StringShape first_shape(first);
+ int first_length = first->length(first_shape);
+ WriteToFlat(first, first_shape, dest, 0, first_length);
+ String* second = cs->second();
+ WriteToFlat(second,
+ StringShape(second),
dest + first_length,
0,
len - first_length);
void String::StringShortPrint(StringStream* accumulator) {
- int len = length();
+ StringShape shape(this);
+ int len = length(shape);
if (len > kMaxMediumStringSize) {
accumulator->Add("<Very long string[%u]>", len);
return;
}
buf.Reset(this);
if (ascii) {
- accumulator->Add("<String[%u]: ", length());
+ accumulator->Add("<String[%u]: ", length(shape));
for (int i = 0; i < len; i++) {
accumulator->Put(buf.GetNext());
}
} else {
// Backslash indicates that the string contains control
// characters and that backslashes are therefore escaped.
- accumulator->Add("<String[%u]\\: ", length());
+ accumulator->Add("<String[%u]\\: ", length(shape));
for (int i = 0; i < len; i++) {
int c = buf.GetNext();
if (c == '\n') {
InstanceType instance_type = map->instance_type();
if (instance_type < FIRST_NONSTRING_TYPE
- && (reinterpret_cast<String*>(this)->map_representation_tag(map)
- == kSeqStringTag)) {
- if (reinterpret_cast<String*>(this)->is_ascii_representation_map(map)) {
- return reinterpret_cast<SeqAsciiString*>(this)->SeqAsciiStringSize(map);
+ && (StringShape(instance_type).IsSequential())) {
+ StringShape shape(instance_type);
+ if (shape.IsAsciiRepresentation()) {
+ return reinterpret_cast<SeqAsciiString*>(this)->SeqAsciiStringSize(shape);
} else {
SeqTwoByteString* self = reinterpret_cast<SeqTwoByteString*>(this);
- return self->SeqTwoByteStringSize(map);
+ return self->SeqTwoByteStringSize(shape);
}
}
}
// TryFlatten before operating on the string.
- name->TryFlatten();
+ name->TryFlatten(StringShape(name));
// Make sure name is not an index.
uint32_t index;
bool String::LooksValid() {
- if (!Heap::Contains(this))
- return false;
- switch (representation_tag()) {
- case kSeqStringTag:
- case kConsStringTag:
- case kSlicedStringTag:
- case kExternalStringTag:
- return true;
- default:
- return false;
- }
+ if (!Heap::Contains(this)) return false;
+ return true;
}
int String::Utf8Length() {
- if (is_ascii_representation()) return length();
+ StringShape shape(this);
+ if (shape.IsAsciiRepresentation()) return length(shape);
// Attempt to flatten before accessing the string. It probably
// doesn't make Utf8Length faster, but it is very likely that
// the string will be accessed later (for example by WriteUtf8)
// so it's still a good idea.
- TryFlatten();
+ if (!IsFlat(shape)) {
+ TryFlatten(shape); // shape is now no longer valid.
+ }
Access<StringInputBuffer> buffer(&string_input_buffer);
buffer->Reset(0, this);
int result = 0;
Vector<const char> String::ToAsciiVector() {
- ASSERT(IsAsciiRepresentation());
- ASSERT(IsFlat());
+ StringShape shape(this);
+ ASSERT(shape.IsAsciiRepresentation());
+ ASSERT(IsFlat(shape));
int offset = 0;
- int length = this->length();
- StringRepresentationTag string_tag = representation_tag();
+ int length = this->length(shape);
+ StringRepresentationTag string_tag = shape.representation_tag();
String* string = this;
if (string_tag == kSlicedStringTag) {
SlicedString* sliced = SlicedString::cast(string);
offset += sliced->start();
- string = String::cast(sliced->buffer());
- string_tag = string->representation_tag();
+ string = sliced->buffer();
+ shape = StringShape(string);
+ string_tag = shape.representation_tag();
} else if (string_tag == kConsStringTag) {
ConsString* cons = ConsString::cast(string);
- ASSERT(String::cast(cons->second())->length() == 0);
- string = String::cast(cons->first());
- string_tag = string->representation_tag();
+ ASSERT(cons->second()->length(StringShape(cons->second())) == 0);
+ string = cons->first();
+ shape = StringShape(string);
+ string_tag = shape.representation_tag();
}
if (string_tag == kSeqStringTag) {
SeqAsciiString* seq = SeqAsciiString::cast(string);
Vector<const uc16> String::ToUC16Vector() {
- ASSERT(IsTwoByteStringRepresentation());
- ASSERT(IsFlat());
+ StringShape shape(this);
+ ASSERT(shape.IsTwoByteRepresentation());
+ ASSERT(IsFlat(shape));
int offset = 0;
- int length = this->length();
- StringRepresentationTag string_tag = representation_tag();
+ int length = this->length(shape);
+ StringRepresentationTag string_tag = shape.representation_tag();
String* string = this;
if (string_tag == kSlicedStringTag) {
SlicedString* sliced = SlicedString::cast(string);
offset += sliced->start();
string = String::cast(sliced->buffer());
- string_tag = string->representation_tag();
+ shape = StringShape(string);
+ string_tag = shape.representation_tag();
} else if (string_tag == kConsStringTag) {
ConsString* cons = ConsString::cast(string);
- ASSERT(String::cast(cons->second())->length() == 0);
- string = String::cast(cons->first());
- string_tag = string->representation_tag();
+ ASSERT(cons->second()->length(StringShape(cons->second())) == 0);
+ string = cons->first();
+ shape = StringShape(string);
+ string_tag = shape.representation_tag();
}
if (string_tag == kSeqStringTag) {
SeqTwoByteString* seq = SeqTwoByteString::cast(string);
const uc16* String::GetTwoByteData(unsigned start) {
- ASSERT(!IsAsciiRepresentation());
- switch (representation_tag()) {
+ StringShape shape(this);
+ ASSERT(!shape.IsAsciiRepresentation());
+ switch (shape.representation_tag()) {
case kSeqStringTag:
return SeqTwoByteString::cast(this)->SeqTwoByteStringGetData(start);
case kExternalStringTag:
ExternalTwoByteStringGetData(start);
case kSlicedStringTag: {
SlicedString* sliced_string = SlicedString::cast(this);
- String* buffer = String::cast(sliced_string->buffer());
- if (buffer->StringIsConsString()) {
- ConsString* cons_string = ConsString::cast(buffer);
+ String* buffer = sliced_string->buffer();
+ if (StringShape(buffer).IsCons()) {
+ ConsString* cs = ConsString::cast(buffer);
// Flattened string.
- ASSERT(String::cast(cons_string->second())->length() == 0);
- buffer = String::cast(cons_string->first());
+ ASSERT(cs->second()->length(StringShape(cs->second())) == 0);
+ buffer = cs->first();
}
return buffer->GetTwoByteData(start + sliced_string->start());
}
int offset_correction = 0;
while (true) {
- String* left = String::cast(current->first());
- unsigned left_length = (unsigned)left->length();
+ String* left = current->first();
+ StringShape left_shape(left);
+ unsigned left_length = (unsigned)left->length(left_shape);
if (left_length > offset &&
(max_chars <= left_length - offset ||
(rbb->capacity <= left_length - offset &&
// the point where we switch to the -IntoBuffer routines (below) in order
// to maximize the chances of delegating a big chunk of work to the
// efficient *AsciiStringReadBlock routines.
- if (left->StringIsConsString()) {
+ if (left_shape.IsCons()) {
current = ConsString::cast(left);
continue;
} else {
} else if (left_length <= offset) {
// Right hand side only - iterate unless we have reached the bottom of
// the cons tree.
- String* right = String::cast(current->second());
+ String* right = current->second();
offset -= left_length;
offset_correction += left_length;
- if (right->StringIsConsString()) {
+ if (StringShape(right).IsCons()) {
current = ConsString::cast(right);
continue;
} else {
const unibrow::byte* SlicedString::SlicedStringReadBlock(ReadBlockBuffer* rbb,
unsigned* offset_ptr,
unsigned max_chars) {
- String* backing = String::cast(buffer());
+ String* backing = buffer();
unsigned offset = start() + *offset_ptr;
unsigned length = backing->length();
if (max_chars > length - offset) {
rbb->remaining = 0;
return NULL;
}
- switch (input->representation_tag()) {
+ StringShape shape(input);
+ switch (shape.representation_tag()) {
case kSeqStringTag:
- if (input->is_ascii_representation()) {
+ if (shape.IsAsciiRepresentation()) {
SeqAsciiString* str = SeqAsciiString::cast(input);
return str->SeqAsciiStringReadBlock(&rbb->remaining,
offset_ptr,
offset_ptr,
max_chars);
case kExternalStringTag:
- if (input->is_ascii_representation()) {
+ if (shape.IsAsciiRepresentation()) {
return ExternalAsciiString::cast(input)->ExternalAsciiStringReadBlock(
&rbb->remaining,
offset_ptr,
ReadBlockBuffer* rbb,
unsigned* offset_ptr,
unsigned max_chars) {
- ASSERT(*offset_ptr <= (unsigned)input->length());
+ StringShape shape(input);
+ ASSERT(*offset_ptr <= (unsigned)input->length(shape));
if (max_chars == 0) return;
- switch (input->representation_tag()) {
+ switch (shape.representation_tag()) {
case kSeqStringTag:
- if (input->is_ascii_representation()) {
+ if (shape.IsAsciiRepresentation()) {
SeqAsciiString::cast(input)->SeqAsciiStringReadBlockIntoBuffer(rbb,
offset_ptr,
max_chars);
max_chars);
return;
case kExternalStringTag:
- if (input->is_ascii_representation()) {
+ if (shape.IsAsciiRepresentation()) {
ExternalAsciiString::cast(input)->
ExternalAsciiStringReadBlockIntoBuffer(rbb, offset_ptr, max_chars);
} else {
unsigned capacity,
unsigned* remaining,
unsigned* offset_ptr) {
- ASSERT(*offset_ptr <= (unsigned)input->length());
- unsigned chars = input->length() - *offset_ptr;
+ StringShape shape(input);
+ ASSERT(*offset_ptr <= (unsigned)input->length(shape));
+ unsigned chars = input->length(shape) - *offset_ptr;
ReadBlockBuffer rbb(util_buffer, 0, capacity, 0);
const unibrow::byte* answer = ReadBlock(input, &rbb, offset_ptr, chars);
- ASSERT(rbb.remaining <= static_cast<unsigned>(input->length()));
+ ASSERT(rbb.remaining <= static_cast<unsigned>(input->length(shape)));
*remaining = rbb.remaining;
return answer;
}
unsigned capacity,
unsigned* remaining,
unsigned* offset_ptr) {
+ StringShape shape(*raw_input);
Handle<String> input(raw_input);
- ASSERT(*offset_ptr <= (unsigned)input->length());
- unsigned chars = input->length() - *offset_ptr;
+ ASSERT(*offset_ptr <= (unsigned)input->length(shape));
+ unsigned chars = input->length(shape) - *offset_ptr;
if (chars > capacity) chars = capacity;
ReadBlockBuffer rbb(util_buffer, 0, capacity, 0);
ReadBlockIntoBuffer(*input, &rbb, offset_ptr, chars);
- ASSERT(rbb.remaining <= static_cast<unsigned>(input->length()));
+ ASSERT(rbb.remaining <= static_cast<unsigned>(input->length(shape)));
*remaining = rbb.remaining;
return rbb.util_buffer;
}
int offset_correction = 0;
while (true) {
- String* left = String::cast(current->first());
- unsigned left_length = (unsigned)left->length();
+ String* left = current->first();
+ StringShape left_shape(left);
+ unsigned left_length = (unsigned)left->length(left_shape);
if (left_length > offset &&
max_chars <= left_length - offset) {
// Left hand side only - iterate unless we have reached the bottom of
// the cons tree.
- if (left->StringIsConsString()) {
+ if (left_shape.IsCons()) {
current = ConsString::cast(left);
continue;
} else {
// the cons tree.
offset -= left_length;
offset_correction += left_length;
- String* right = String::cast(current->second());
- if (right->StringIsConsString()) {
+ String* right = current->second();
+ if (StringShape(right).IsCons()) {
current = ConsString::cast(right);
continue;
} else {
void SlicedString::SlicedStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
unsigned* offset_ptr,
unsigned max_chars) {
- String* backing = String::cast(buffer());
+ String* backing = buffer();
unsigned offset = start() + *offset_ptr;
unsigned length = backing->length();
if (max_chars > length - offset) {
ASSERT(index >= 0 && index < this->length());
// Check for a flattened cons string
- if (String::cast(second())->length() == 0) {
- return String::cast(first())->Get(index);
+ if (second()->length() == 0) {
+ String* left = first();
+ return left->Get(StringShape(left), index);
}
String* string = String::cast(this);
+ StringShape shape(string);
while (true) {
- if (string->StringIsConsString()) {
+ if (shape.IsCons()) {
ConsString* cons_string = ConsString::cast(string);
- String* left = String::cast(cons_string->first());
- if (left->length() > index) {
+ String* left = cons_string->first();
+ StringShape left_shape(left);
+ if (left->length(left_shape) > index) {
string = left;
+ shape = left_shape;
} else {
- index -= left->length();
- string = String::cast(cons_string->second());
+ index -= left->length(left_shape);
+ string = cons_string->second();
+ shape = StringShape(string);
}
} else {
- return string->Get(index);
+ return string->Get(shape, index);
}
}
// SlicedStrings that are constructed directly on top of other
// SlicedStrings.
String* buf = String::cast(buffer());
- ASSERT(!buf->StringIsSlicedString());
- if (buf->StringIsConsString()) {
- Object* ok = buf->Flatten();
+ StringShape buf_shape(buf);
+ ASSERT(!buf_shape.IsSliced());
+ if (buf_shape.IsCons()) {
+ Object* ok = buf->Flatten(buf_shape);
if (ok->IsFailure()) return ok;
}
return this;
template <typename sinkchar>
void String::WriteToFlat(String* src,
+ StringShape src_shape,
sinkchar* sink,
int f,
int t) {
String* source = src;
+ StringShape shape = src_shape;
int from = f;
int to = t;
while (true) {
- ASSERT(0 <= from && from <= to && to <= source->length());
- switch (source->full_representation_tag()) {
+ ASSERT(0 <= from && from <= to && to <= source->length(shape));
+ switch (shape.full_representation_tag()) {
case kAsciiStringTag | kExternalStringTag: {
CopyChars(sink,
ExternalAsciiString::cast(source)->resource()->data() + from,
from += start;
to += start;
source = String::cast(sliced_string->buffer());
+ shape = StringShape(source);
break;
}
case kAsciiStringTag | kConsStringTag:
case kTwoByteStringTag | kConsStringTag: {
ConsString* cons_string = ConsString::cast(source);
- String* first = String::cast(cons_string->first());
- int boundary = first->length();
+ String* first = cons_string->first();
+ StringShape first_shape(first);
+ int boundary = first->length(first_shape);
if (to - boundary >= boundary - from) {
// Right hand side is longer. Recurse over left.
if (from < boundary) {
- WriteToFlat(first, sink, from, boundary);
+ WriteToFlat(first, first_shape, sink, from, boundary);
sink += boundary - from;
from = 0;
} else {
from -= boundary;
}
to -= boundary;
- source = String::cast(cons_string->second());
+ source = cons_string->second();
+ shape = StringShape(source);
} else {
// Left hand side is longer. Recurse over right.
if (to > boundary) {
- String* second = String::cast(cons_string->second());
+ String* second = cons_string->second();
WriteToFlat(second,
+ StringShape(second),
sink + boundary - from,
0,
to - boundary);
to = boundary;
}
source = first;
+ shape = first_shape;
}
break;
}
uint16_t SlicedString::SlicedStringGet(int index) {
ASSERT(index >= 0 && index < this->length());
// Delegate to the buffer string.
- return String::cast(buffer())->Get(start() + index);
+ String* underlying = buffer();
+ return underlying->Get(StringShape(underlying), start() + index);
}
template <typename IteratorA>
static inline bool CompareStringContentsPartial(IteratorA* ia, String* b) {
- if (b->IsFlat()) {
- if (b->IsAsciiRepresentation()) {
+ StringShape b_shape(b);
+ if (b->IsFlat(b_shape)) {
+ if (b_shape.IsAsciiRepresentation()) {
VectorIterator<char> ib(b->ToAsciiVector());
return CompareStringContents(ia, &ib);
} else {
static StringInputBuffer string_compare_buffer_a;
-bool String::SlowEquals(String* other) {
+bool String::SlowEquals(StringShape this_shape,
+ String* other,
+ StringShape other_shape) {
// Fast check: negative check with lengths.
- int len = length();
- if (len != other->length()) return false;
+ int len = length(this_shape);
+ if (len != other->length(other_shape)) return false;
if (len == 0) return true;
// Fast check: if hash code is computed for both strings
if (Hash() != other->Hash()) return false;
}
- if (this->IsSeqAsciiString() && other->IsSeqAsciiString()) {
+ if (this_shape.IsSequentialAscii() && other_shape.IsSequentialAscii()) {
const char* str1 = SeqAsciiString::cast(this)->GetChars();
const char* str2 = SeqAsciiString::cast(other)->GetChars();
return CompareRawStringContents(Vector<const char>(str1, len),
Vector<const char>(str2, len));
}
- if (this->IsFlat()) {
- if (this->IsAsciiRepresentation()) {
+ if (this->IsFlat(this_shape)) {
+ if (this_shape.IsAsciiRepresentation()) {
Vector<const char> vec1 = this->ToAsciiVector();
- if (other->IsFlat()) {
- if (other->IsAsciiRepresentation()) {
+ if (other->IsFlat(other_shape)) {
+ if (other_shape.IsAsciiRepresentation()) {
Vector<const char> vec2 = other->ToAsciiVector();
return CompareRawStringContents(vec1, vec2);
} else {
}
} else {
Vector<const uc16> vec1 = this->ToUC16Vector();
- if (other->IsFlat()) {
- if (other->IsAsciiRepresentation()) {
+ if (other->IsFlat(other_shape)) {
+ if (other_shape.IsAsciiRepresentation()) {
VectorIterator<uc16> buf1(vec1);
VectorIterator<char> ib(other->ToAsciiVector());
return CompareStringContents(&buf1, &ib);
bool String::MarkAsUndetectable() {
- if (this->IsSymbol()) return false;
+ StringShape shape(this);
+ if (shape.IsSymbol()) return false;
Map* map = this->map();
if (map == Heap::short_string_map()) {
bool String::IsEqualTo(Vector<const char> str) {
- int slen = length();
+ StringShape this_shape(this);
+ int slen = length(this_shape);
Access<Scanner::Utf8Decoder> decoder(Scanner::utf8_decoder());
decoder->Reset(str.start(), str.length());
int i;
for (i = 0; i < slen && decoder->has_more(); i++) {
uc32 r = decoder->GetNext();
- if (Get(i) != r) return false;
+ if (Get(this_shape, i) != r) return false;
}
return i == slen && !decoder->has_more();
}
bool String::SlowAsArrayIndex(uint32_t* index) {
- if (length() <= kMaxCachedArrayIndexLength) {
+ StringShape shape(this);
+ if (length(shape) <= kMaxCachedArrayIndexLength) {
Hash(); // force computation of hash code
uint32_t field = length_field();
if ((field & kIsArrayIndexMask) == 0) return false;
return true;
} else {
StringInputBuffer buffer(this);
- return ComputeArrayIndex(&buffer, index, length());
+ return ComputeArrayIndex(&buffer, index, length(shape));
}
}
}
-Object* String::Slice(int start, int end) {
- if (start == 0 && end == length()) return this;
- int representation = representation_tag();
- if (representation == kSlicedStringTag) {
+Object* String::Slice(StringShape shape, int start, int end) {
+ if (start == 0 && end == length(shape)) return this;
+ if (shape.representation_tag() == kSlicedStringTag) {
// Translate slices of a SlicedString into slices of the
// underlying string buffer.
SlicedString* str = SlicedString::cast(this);
- return Heap::AllocateSlicedString(String::cast(str->buffer()),
+ String* buf = str->buffer();
+ return Heap::AllocateSlicedString(buf,
+ StringShape(buf),
str->start() + start,
str->start() + end);
}
- Object* answer = Heap::AllocateSlicedString(this, start, end);
- if (answer->IsFailure()) {
- return answer;
+ Object* result = Heap::AllocateSlicedString(this, shape, start, end);
+ if (result->IsFailure()) {
+ return result;
}
// Due to the way we retry after GC on allocation failure we are not allowed
// to fail on allocation after this point. This is the one-allocation rule.
// will succeed often enough to avoid the problem. We only have to do this
// if Heap::AllocateSlicedString actually returned a SlicedString. It will
// return flat strings for small slices for efficiency reasons.
- if (String::cast(answer)->StringIsSlicedString() &&
- representation == kConsStringTag) {
- TryFlatten();
+ String* answer = String::cast(result);
+ StringShape answer_shape(answer);
+ if (answer_shape.IsSliced() &&
+ shape.representation_tag() == kConsStringTag) {
+ TryFlatten(shape);
// If the flatten succeeded we might as well make the sliced string point
// to the flat string rather than the cons string.
- if (String::cast(ConsString::cast(this)->second())->length() == 0) {
+ String* second = ConsString::cast(this)->second();
+ if (second->length(StringShape(second)) == 0) {
SlicedString::cast(answer)->set_buffer(ConsString::cast(this)->first());
}
}
void String::PrintOn(FILE* file) {
- int length = this->length();
+ StringShape shape(this);
+ int length = this->length(shape);
for (int i = 0; i < length; i++) {
- fprintf(file, "%c", Get(i));
+ fprintf(file, "%c", Get(shape, i));
}
}
Object* val = JSValue::cast(this)->value();
if (val->IsString()) {
String* str = String::cast(val);
+ StringShape shape(str);
if (storage) {
- for (int i = 0; i < str->length(); i++) {
+ for (int i = 0; i < str->length(shape); i++) {
storage->set(counter + i, Smi::FromInt(i), SKIP_WRITE_BARRIER);
}
}
- counter += str->length();
+ counter += str->length(shape);
}
}
ASSERT(!storage || storage->length() == counter);
Object* GetObject() {
// If the string is a cons string, attempt to flatten it so that
// symbols will most often be flat strings.
- if (string_->IsConsString()) {
+ StringShape shape(string_);
+ if (shape.IsCons()) {
ConsString* cons_string = ConsString::cast(string_);
- cons_string->TryFlatten();
+ cons_string->TryFlatten(shape);
if (cons_string->second() == Heap::empty_string()) {
- string_ = String::cast(cons_string->first());
+ string_ = cons_string->first();
}
}
// Transform string to symbol if possible.
return false;
} else {
String* result = String::cast(KeyAt(entry));
- ASSERT(result->is_symbol());
+ ASSERT(StringShape(result).IsSymbol());
*symbol = result;
return true;
}
inline bool IsHeapObject();
inline bool IsHeapNumber();
inline bool IsString();
+ inline bool IsSymbol();
inline bool IsSeqString();
- inline bool IsAsciiStringRepresentation();
- inline bool IsTwoByteStringRepresentation();
- inline bool IsSeqAsciiString();
- inline bool IsSeqTwoByteString();
- inline bool IsConsString();
inline bool IsSlicedString();
inline bool IsExternalString();
- inline bool IsExternalAsciiString();
+ inline bool IsConsString();
inline bool IsExternalTwoByteString();
- inline bool IsShortString();
- inline bool IsMediumString();
- inline bool IsLongString();
- inline bool IsSymbol();
+ inline bool IsExternalAsciiString();
+ inline bool IsSeqTwoByteString();
+ inline bool IsSeqAsciiString();
+
inline bool IsNumber();
inline bool IsByteArray();
inline bool IsFailure();
};
+// The characteristics of a string are stored in its map. Retrieving these
+// few bits of information is moderately expensive, involving two memory
+// loads where the second is dependent on the first. To improve efficiency
+// the shape of the string is given its own class so that it can be retrieved
+// once and used for several string operations. A StringShape is small enough
+// to be passed by value and is immutable, but be aware that flattening a
+// string can potentially alter its shape.
+//
+// Most of the methods designed to interrogate a string as to its exact nature
+// have been made into methods on StringShape in order to encourage the use of
+// StringShape. The String class has both a length() and a length(StringShape)
+// operation. The former is simpler to type, but the latter is faster if you
+// need the StringShape for some other operation immediately before or after.
+class StringShape BASE_EMBEDDED {
+ public:
+ inline explicit StringShape(String* s);
+ inline explicit StringShape(Map* s);
+ inline explicit StringShape(InstanceType t);
+ inline bool IsAsciiRepresentation();
+ inline bool IsTwoByteRepresentation();
+ inline bool IsSequential();
+ inline bool IsExternal();
+ inline bool IsCons();
+ inline bool IsSliced();
+ inline bool IsExternalAscii();
+ inline bool IsExternalTwoByte();
+ inline bool IsSequentialAscii();
+ inline bool IsSequentialTwoByte();
+ inline bool IsSymbol();
+ inline StringRepresentationTag representation_tag();
+ inline uint32_t full_representation_tag();
+ inline uint32_t size_tag();
+#ifdef DEBUG
+ inline uint32_t type() { return type_; }
+ inline void invalidate() { valid_ = false; }
+ inline bool valid() { return valid_; }
+#else
+ inline void invalidate() { }
+#endif
+ private:
+ uint32_t type_;
+#ifdef DEBUG
+ bool valid_;
+#endif
+};
+
+
// The String abstract class captures JavaScript string values:
//
// Ecma-262:
class String: public HeapObject {
public:
// Get and set the length of the string.
+ // Fast version.
+ inline int length(StringShape shape);
+ // Easy version.
inline int length();
inline void set_length(int value);
inline void set_length_field(uint32_t value);
// Get and set individual two byte chars in the string.
- inline void Set(int index, uint16_t value);
+ inline void Set(StringShape shape, int index, uint16_t value);
// Get individual two byte char in the string. Repeated calls
// to this method are not efficient unless the string is flat.
- inline uint16_t Get(int index);
+ inline uint16_t Get(StringShape shape, int index);
// Flatten the top level ConsString that is hiding behind this
// string. This is a no-op unless the string is a ConsString or a
// SlicedString. Flatten mutates the ConsString and might return a
// failure.
- Object* Flatten();
+ Object* Flatten(StringShape shape);
// Try to flatten the string. Do not allow handling of allocation
// failures. After calling TryFlatten, the string could still be a
// ConsString.
- inline void TryFlatten();
-
- // Is this string an ascii string.
- inline bool IsAsciiRepresentation();
-
- // Specialization of this function from Object that skips the
- // string check.
- inline bool IsSeqAsciiString();
-
- // Fast testing routines that assume the receiver is a string and
- // just check whether it is a certain kind of string.
- inline bool StringIsSlicedString();
- inline bool StringIsConsString();
+ inline void TryFlatten(StringShape shape);
Vector<const char> ToAsciiVector();
Vector<const uc16> ToUC16Vector();
bool MarkAsUndetectable();
// Slice the string and return a substring.
- Object* Slice(int from, int to);
+ Object* Slice(StringShape shape, int from, int to);
// String equality operations.
inline bool Equals(String* other);
void PrintOn(FILE* out);
- // Get the size tag.
- inline uint32_t size_tag();
- static inline uint32_t map_size_tag(Map* map);
-
- // True if the string is a symbol.
- inline bool is_symbol();
- static inline bool is_symbol_map(Map* map);
-
- // True if the string is ASCII.
- inline bool is_ascii_representation();
- static inline bool is_ascii_representation_map(Map* map);
-
- // Get the representation tag.
- inline StringRepresentationTag representation_tag();
- // Get the representation and ASCII tag.
- inline int full_representation_tag();
- static inline StringRepresentationTag map_representation_tag(Map* map);
-
// For use during stack traces. Performs rudimentary sanity check.
bool LooksValid();
void StringPrint();
void StringVerify();
#endif
- inline bool IsFlat();
+ inline bool IsFlat(StringShape shape);
// Layout description.
static const int kLengthOffset = HeapObject::kHeaderSize;
// Helper function for flattening strings.
template <typename sinkchar>
static void WriteToFlat(String* source,
+ StringShape shape,
sinkchar* sink,
int from,
int to);
private:
// Slow case of String::Equals. This implementation works on any strings
// but it is most efficient on strings that are almost flat.
- bool SlowEquals(String* other);
+ bool SlowEquals(StringShape this_shape,
+ String* other,
+ StringShape other_shape);
// Slow case of AsArrayIndex.
bool SlowAsArrayIndex(uint32_t* index);
// Garbage collection support. This method is called by the
// garbage collector to compute the actual size of an AsciiString
// instance.
- inline int SeqAsciiStringSize(Map* map);
+ inline int SeqAsciiStringSize(StringShape shape);
// Computes the size for an AsciiString instance of a given length.
static int SizeFor(int length) {
// Garbage collection support. This method is called by the
// garbage collector to compute the actual size of a TwoByteString
// instance.
- inline int SeqTwoByteStringSize(Map* map);
+ inline int SeqTwoByteStringSize(StringShape shape);
// Computes the size for a TwoByteString instance of a given length.
static int SizeFor(int length) {
// values in a left-to-right depth-first traversal of the tree.
class ConsString: public String {
public:
- // First object of the cons cell.
- inline Object* first();
- inline void set_first(Object* first,
+ // First string of the cons cell.
+ inline String* first();
+ // Doesn't check that the result is a string, even in debug mode. This is
+ // useful during GC where the mark bits confuse the checks.
+ inline Object* unchecked_first();
+ inline void set_first(String* first,
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
- // Second object of the cons cell.
- inline Object* second();
- inline void set_second(Object* second,
+ // Second string of the cons cell.
+ inline String* second();
+ // Doesn't check that the result is a string, even in debug mode. This is
+ // useful during GC where the mark bits confuse the checks.
+ inline Object* unchecked_second();
+ inline void set_second(String* second,
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
// Dispatched behavior.
class SlicedString: public String {
public:
// The underlying string buffer.
- inline Object* buffer();
- inline void set_buffer(Object* buffer);
+ inline String* buffer();
+ inline void set_buffer(String* buffer);
// The start index of the slice.
inline int start();
ZoneScope zone_scope(DONT_DELETE_ON_EXIT);
StatsRateScope timer(&Counters::parse);
- Counters::total_parse_size.Increment(source->length());
+ StringShape shape(*source);
+ Counters::total_parse_size.Increment(source->length(shape));
// Initialize parser state.
- source->TryFlatten();
+ source->TryFlatten(shape);
scanner_.Init(source, stream, 0);
ASSERT(target_stack_ == NULL);
temp_scope.materialized_literal_count(),
temp_scope.contains_array_literal(),
temp_scope.expected_property_count(),
- 0, 0, source->length(), false));
+ 0, 0, source->length(shape), false));
} else if (scanner().stack_overflow()) {
Top::StackOverflow();
}
bool is_expression) {
ZoneScope zone_scope(DONT_DELETE_ON_EXIT);
StatsRateScope timer(&Counters::parse_lazy);
- Counters::total_parse_size.Increment(source->length());
+ StringShape shape(*source);
+ source->TryFlatten(shape);
+ Counters::total_parse_size.Increment(source->length(shape));
SafeStringInputBuffer buffer(source.location());
// Initialize parser state.
- source->TryFlatten();
scanner_.Init(source, &buffer, start_position);
ASSERT(target_stack_ == NULL);
mode_ = PARSE_EAGERLY;
Object* object = *value;
if (object->IsString()) {
String* string = String::cast(object);
+ StringShape shape(string);
if (quote) Print("\"");
- for (int i = 0; i < string->length(); i++) {
- Print("%c", string->Get(i));
+ for (int i = 0; i < string->length(shape); i++) {
+ Print("%c", string->Get(shape, i));
}
if (quote) Print("\"");
} else if (object == Heap::null_value()) {
}
Object* KeyToSymbol() {
- if (!key_->IsSymbol()) {
+ if (!StringShape(key_).IsSymbol()) {
Object* result = Heap::LookupSymbol(key_);
if (result->IsFailure()) return result;
key_ = String::cast(result);
// Flatten the string. If someone wants to get a char at an index
// in a cons string, it is likely that more indices will be
// accessed.
- subject->TryFlatten();
- if (i >= static_cast<uint32_t>(subject->length())) return Heap::nan_value();
- return Smi::FromInt(subject->Get(i));
+ StringShape shape(subject);
+ subject->TryFlatten(shape); // shape no longer valid!
+ if (i >= static_cast<uint32_t>(subject->length(StringShape(subject)))) {
+ return Heap::nan_value();
+ }
+ return Smi::FromInt(subject->Get(StringShape(subject), i));
}
Handle<String> pat,
int start_index) {
ASSERT(0 <= start_index);
- ASSERT(start_index <= sub->length());
+ StringShape sub_shape(*sub);
+ StringShape pat_shape(*pat);
+ ASSERT(start_index <= sub->length(sub_shape));
- int pattern_length = pat->length();
+ int pattern_length = pat->length(pat_shape);
if (pattern_length == 0) return start_index;
- int subject_length = sub->length();
+ int subject_length = sub->length(sub_shape);
if (start_index + pattern_length > subject_length) return -1;
- FlattenString(sub);
+ if (!sub->IsFlat(sub_shape)) {
+ FlattenString(sub);
+ sub_shape = StringShape(*sub);
+ }
// Searching for one specific character is common. For one
// character patterns linear search is necessary, so any smart
// algorithm is unnecessary overhead.
if (pattern_length == 1) {
AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
- if (sub->is_ascii_representation()) {
- return SingleCharIndexOf(sub->ToAsciiVector(), pat->Get(0), start_index);
+ if (sub_shape.IsAsciiRepresentation()) {
+ return SingleCharIndexOf(sub->ToAsciiVector(),
+ pat->Get(pat_shape, 0),
+ start_index);
}
- return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
+ return SingleCharIndexOf(sub->ToUC16Vector(),
+ pat->Get(pat_shape, 0),
+ start_index);
}
- FlattenString(pat);
+ if (!pat->IsFlat(pat_shape)) {
+ FlattenString(pat);
+ pat_shape = StringShape(*pat);
+ }
AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
// dispatch on type of strings
- if (pat->is_ascii_representation()) {
+ if (pat_shape.IsAsciiRepresentation()) {
Vector<const char> pat_vector = pat->ToAsciiVector();
- if (sub->is_ascii_representation()) {
+ if (sub_shape.IsAsciiRepresentation()) {
return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
}
return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
}
Vector<const uc16> pat_vector = pat->ToUC16Vector();
- if (sub->is_ascii_representation()) {
+ if (sub_shape.IsAsciiRepresentation()) {
return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
}
return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
CONVERT_CHECKED(String, pat, args[1]);
Object* index = args[2];
- sub->TryFlatten();
- pat->TryFlatten();
+ sub->TryFlatten(StringShape(sub));
+ pat->TryFlatten(StringShape(pat));
+
+ StringShape sub_shape(sub);
+ StringShape pat_shape(pat);
uint32_t start_index;
if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
- uint32_t pattern_length = pat->length();
- uint32_t sub_length = sub->length();
+ uint32_t pattern_length = pat->length(pat_shape);
+ uint32_t sub_length = sub->length(sub_shape);
if (start_index + pattern_length > sub_length) {
start_index = sub_length - pattern_length;
for (int i = start_index; i >= 0; i--) {
bool found = true;
for (uint32_t j = 0; j < pattern_length; j++) {
- if (sub->Get(i + j) != pat->Get(j)) {
+ if (sub->Get(sub_shape, i + j) != pat->Get(pat_shape, j)) {
found = false;
break;
}
CONVERT_CHECKED(String, str2, args[1]);
if (str1 == str2) return Smi::FromInt(0); // Equal.
- int str1_length = str1->length();
- int str2_length = str2->length();
+ StringShape shape1(str1);
+ StringShape shape2(str2);
+ int str1_length = str1->length(shape1);
+ int str2_length = str2->length(shape2);
// Decide trivial cases without flattening.
if (str1_length == 0) {
// No need to flatten if we are going to find the answer on the first
// character. At this point we know there is at least one character
// in each string, due to the trivial case handling above.
- int d = str1->Get(0) - str2->Get(0);
+ int d = str1->Get(shape1, 0) - str2->Get(shape2, 0);
if (d != 0) return Smi::FromInt(d);
- str1->TryFlatten();
- str2->TryFlatten();
+ str1->TryFlatten(shape1); // Shapes are no longer valid now!
+ str2->TryFlatten(shape2);
static StringInputBuffer buf1;
static StringInputBuffer buf2;
int start = FastD2I(from_number);
int end = FastD2I(to_number);
+ StringShape shape(value);
+
RUNTIME_ASSERT(end >= start);
RUNTIME_ASSERT(start >= 0);
- RUNTIME_ASSERT(end <= value->length());
- return value->Slice(start, end);
+ RUNTIME_ASSERT(end <= value->length(shape));
+ return value->Slice(shape, start, end);
}
// Returns a single character string where first character equals
// string->Get(index).
static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
- if (index < static_cast<uint32_t>(string->length())) {
- string->TryFlatten();
- return LookupSingleCharacterStringFromCode(string->Get(index));
+ StringShape shape(*string);
+ if (index < static_cast<uint32_t>(string->length(shape))) {
+ string->TryFlatten(shape); // Invalidates shape!
+ return LookupSingleCharacterStringFromCode(
+ string->Get(StringShape(*string), index));
}
return Execution::CharAt(string, index);
}
result = SetElement(js_object, index, value);
} else {
Handle<String> key_string = Handle<String>::cast(key);
- key_string->TryFlatten();
+ key_string->TryFlatten(StringShape(*key_string));
result = SetProperty(js_object, key_string, value, attr);
}
if (result.is_null()) return Failure::Exception();
uint32_t index;
if (key->AsArrayIndex(&index)) {
String* string = String::cast(args[0]);
- if (index < static_cast<uint32_t>(string->length()))
+ StringShape shape(string);
+ if (index < static_cast<uint32_t>(string->length(shape)))
return Heap::true_value();
}
}
NoHandleAllocation ha;
ASSERT(args.length() == 1);
CONVERT_CHECKED(String, subject, args[0]);
- subject->TryFlatten();
+ subject->TryFlatten(StringShape(subject));
return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
}
if (object->IsFailure()) return object;
String* result = String::cast(object);
+ StringShape result_shape(result);
for (int i = 0; i < length; i++) {
Object* element = codes->GetElement(i);
CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
- result->Set(i, chr & 0xffff);
+ result->Set(result_shape, i, chr & 0xffff);
}
return result;
}
ASSERT(args.length() == 1);
CONVERT_CHECKED(String, source, args[0]);
- source->TryFlatten();
+ source->TryFlatten(StringShape(source));
int escaped_length = 0;
int length = source->length();
Object* o = Heap::AllocateRawAsciiString(escaped_length);
if (o->IsFailure()) return o;
String* destination = String::cast(o);
+ StringShape dshape(destination);
int dest_position = 0;
Access<StringInputBuffer> buffer(&string_input_buffer);
buffer->Rewind();
while (buffer->has_more()) {
- uint16_t character = buffer->GetNext();
- if (character >= 256) {
- destination->Set(dest_position, '%');
- destination->Set(dest_position+1, 'u');
- destination->Set(dest_position+2, hex_chars[character >> 12]);
- destination->Set(dest_position+3, hex_chars[(character >> 8) & 0xf]);
- destination->Set(dest_position+4, hex_chars[(character >> 4) & 0xf]);
- destination->Set(dest_position+5, hex_chars[character & 0xf]);
+ uint16_t chr = buffer->GetNext();
+ if (chr >= 256) {
+ destination->Set(dshape, dest_position, '%');
+ destination->Set(dshape, dest_position+1, 'u');
+ destination->Set(dshape, dest_position+2, hex_chars[chr >> 12]);
+ destination->Set(dshape, dest_position+3, hex_chars[(chr >> 8) & 0xf]);
+ destination->Set(dshape, dest_position+4, hex_chars[(chr >> 4) & 0xf]);
+ destination->Set(dshape, dest_position+5, hex_chars[chr & 0xf]);
dest_position += 6;
- } else if (IsNotEscaped(character)) {
- destination->Set(dest_position, character);
+ } else if (IsNotEscaped(chr)) {
+ destination->Set(dshape, dest_position, chr);
dest_position++;
} else {
- destination->Set(dest_position, '%');
- destination->Set(dest_position+1, hex_chars[character >> 4]);
- destination->Set(dest_position+2, hex_chars[character & 0xf]);
+ destination->Set(dshape, dest_position, '%');
+ destination->Set(dshape, dest_position+1, hex_chars[chr >> 4]);
+ destination->Set(dshape, dest_position+2, hex_chars[chr & 0xf]);
dest_position += 3;
}
}
}
-static inline int Unescape(String* source, int i, int length, int* step) {
- uint16_t character = source->Get(i);
+static inline int Unescape(String* source,
+ StringShape shape,
+ int i,
+ int length,
+ int* step) {
+ uint16_t character = source->Get(shape, i);
int32_t hi, lo;
if (character == '%' &&
i <= length - 6 &&
- source->Get(i + 1) == 'u' &&
- (hi = TwoDigitHex(source->Get(i + 2), source->Get(i + 3))) != -1 &&
- (lo = TwoDigitHex(source->Get(i + 4), source->Get(i + 5))) != -1) {
+ source->Get(shape, i + 1) == 'u' &&
+ (hi = TwoDigitHex(source->Get(shape, i + 2),
+ source->Get(shape, i + 3))) != -1 &&
+ (lo = TwoDigitHex(source->Get(shape, i + 4),
+ source->Get(shape, i + 5))) != -1) {
*step = 6;
return (hi << 8) + lo;
} else if (character == '%' &&
i <= length - 3 &&
- (lo = TwoDigitHex(source->Get(i + 1), source->Get(i + 2))) != -1) {
+ (lo = TwoDigitHex(source->Get(shape, i + 1),
+ source->Get(shape, i + 2))) != -1) {
*step = 3;
return lo;
} else {
ASSERT(args.length() == 1);
CONVERT_CHECKED(String, source, args[0]);
- source->TryFlatten();
+ source->TryFlatten(StringShape(source));
+ StringShape source_shape(source);
bool ascii = true;
- int length = source->length();
+ int length = source->length(source_shape);
int unescaped_length = 0;
for (int i = 0; i < length; unescaped_length++) {
int step;
- if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode)
+ if (Unescape(source,
+ source_shape,
+ i,
+ length,
+ &step) >
+ String::kMaxAsciiCharCode)
ascii = false;
i += step;
}
Heap::AllocateRawTwoByteString(unescaped_length);
if (o->IsFailure()) return o;
String* destination = String::cast(o);
+ StringShape destination_shape(destination);
int dest_position = 0;
for (int i = 0; i < length; dest_position++) {
int step;
- destination->Set(dest_position, Unescape(source, i, length, &step));
+ destination->Set(destination_shape,
+ dest_position,
+ Unescape(source, source_shape, i, length, &step));
i += step;
}
return destination;
CONVERT_DOUBLE_CHECKED(n, args[1]);
int radix = FastD2I(n);
- s->TryFlatten();
+ s->TryFlatten(StringShape(s));
+
+ StringShape shape(s);
- int len = s->length();
+ int len = s->length(shape);
int i;
// Skip leading white space.
- for (i = 0; i < len && Scanner::kIsWhiteSpace.get(s->Get(i)); i++) ;
+ for (i = 0; i < len && Scanner::kIsWhiteSpace.get(s->Get(shape, i)); i++) ;
if (i == len) return Heap::nan_value();
// Compute the sign (default to +).
int sign = 1;
- if (s->Get(i) == '-') {
+ if (s->Get(shape, i) == '-') {
sign = -1;
i++;
- } else if (s->Get(i) == '+') {
+ } else if (s->Get(shape, i) == '+') {
i++;
}
// Compute the radix if 0.
if (radix == 0) {
radix = 10;
- if (i < len && s->Get(i) == '0') {
+ if (i < len && s->Get(shape, i) == '0') {
radix = 8;
if (i + 1 < len) {
- int c = s->Get(i + 1);
+ int c = s->Get(shape, i + 1);
if (c == 'x' || c == 'X') {
radix = 16;
i += 2;
}
} else if (radix == 16) {
// Allow 0x or 0X prefix if radix is 16.
- if (i + 1 < len && s->Get(i) == '0') {
- int c = s->Get(i + 1);
+ if (i + 1 < len && s->Get(shape, i) == '0') {
+ int c = s->Get(shape, i + 1);
if (c == 'x' || c == 'X') i += 2;
}
}
NoHandleAllocation ha;
CONVERT_CHECKED(String, s, args[0]);
- int raw_string_length = s->length();
+ s->TryFlatten(StringShape(s));
+ StringShape shape(s);
+
+ int raw_string_length = s->length(shape);
// Assume that the string is not empty; we need this assumption later
if (raw_string_length == 0) return s;
int length = raw_string_length;
- s->TryFlatten();
// We try this twice, once with the assumption that the result is
// no longer than the input and, if that assumption breaks, again
// character is also ascii. This is currently the case, but it
// might break in the future if we implement more context and locale
// dependent upper/lower conversions.
- Object* o = s->IsAsciiRepresentation()
+ Object* o = shape.IsAsciiRepresentation()
? Heap::AllocateRawAsciiString(length)
: Heap::AllocateRawTwoByteString(length);
if (o->IsFailure()) return o;
String* result = String::cast(o);
+ StringShape result_shape(result);
bool has_changed_character = false;
// Convert all characters to upper case, assuming that they will fit
int char_length = mapping->get(current, next, chars);
if (char_length == 0) {
// The case conversion of this character is the character itself.
- result->Set(i, current);
+ result->Set(result_shape, i, current);
i++;
} else if (char_length == 1) {
// Common case: converting the letter resulted in one character.
ASSERT(static_cast<uc32>(chars[0]) != current);
- result->Set(i, chars[0]);
+ result->Set(result_shape, i, chars[0]);
has_changed_character = true;
i++;
} else if (length == raw_string_length) {
goto try_convert;
} else {
for (int j = 0; j < char_length; j++) {
- result->Set(i, chars[j]);
+ result->Set(result_shape, i, chars[j]);
i++;
}
has_changed_character = true;
}
-static Object* Runtime_ConsStringFst(Arguments args) {
- NoHandleAllocation ha;
-
- CONVERT_CHECKED(ConsString, str, args[0]);
- return str->first();
-}
-
-
-static Object* Runtime_ConsStringSnd(Arguments args) {
- NoHandleAllocation ha;
-
- CONVERT_CHECKED(ConsString, str, args[0]);
- return str->second();
-}
-
-
static Object* Runtime_NumberToString(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
CONVERT_CHECKED(String, str1, args[0]);
CONVERT_CHECKED(String, str2, args[1]);
- int len1 = str1->length();
- int len2 = str2->length();
+ StringShape shape1(str1);
+ StringShape shape2(str2);
+ int len1 = str1->length(shape1);
+ int len2 = str2->length(shape2);
if (len1 == 0) return str2;
if (len2 == 0) return str1;
int length_sum = len1 + len2;
Top::context()->mark_out_of_memory();
return Failure::OutOfMemoryException();
}
- return Heap::AllocateConsString(str1, str2);
+ return Heap::AllocateConsString(str1, shape1, str2, shape2);
}
template<typename sinkchar>
static inline void StringBuilderConcatHelper(String* special,
+ StringShape special_shape,
sinkchar* sink,
FixedArray* fixed_array,
int array_length) {
int len = Smi::cast(element)->value();
int pos = len >> 11;
len &= 0x7ff;
- String::WriteToFlat(special, sink + position, pos, pos + len);
+ String::WriteToFlat(special,
+ special_shape,
+ sink + position,
+ pos,
+ pos + len);
position += len;
} else {
String* string = String::cast(element);
- int element_length = string->length();
- String::WriteToFlat(string, sink + position, 0, element_length);
+ StringShape shape(string);
+ int element_length = string->length(shape);
+ String::WriteToFlat(string, shape, sink + position, 0, element_length);
position += element_length;
}
}
ASSERT(args.length() == 2);
CONVERT_CHECKED(JSArray, array, args[0]);
CONVERT_CHECKED(String, special, args[1]);
- int special_length = special->length();
+ StringShape special_shape(special);
+ int special_length = special->length(special_shape);
Object* smi_array_length = array->length();
if (!smi_array_length->IsSmi()) {
Top::context()->mark_out_of_memory();
if (first->IsString()) return first;
}
- bool ascii = special->IsAsciiRepresentation();
+ bool ascii = special_shape.IsAsciiRepresentation();
int position = 0;
for (int i = 0; i < array_length; i++) {
Object* elt = fixed_array->get(i);
position += len;
} else if (elt->IsString()) {
String* element = String::cast(elt);
- int element_length = element->length();
+ StringShape element_shape(element);
+ int element_length = element->length(element_shape);
if (!Smi::IsValid(element_length + position)) {
Top::context()->mark_out_of_memory();
return Failure::OutOfMemoryException();
}
position += element_length;
- if (ascii && !element->IsAsciiRepresentation()) {
+ if (ascii && !element_shape.IsAsciiRepresentation()) {
ascii = false;
}
} else {
if (object->IsFailure()) return object;
SeqAsciiString* answer = SeqAsciiString::cast(object);
StringBuilderConcatHelper(special,
+ special_shape,
answer->GetChars(),
fixed_array,
array_length);
if (object->IsFailure()) return object;
SeqTwoByteString* answer = SeqTwoByteString::cast(object);
StringBuilderConcatHelper(special,
+ special_shape,
answer->GetChars(),
fixed_array,
array_length);
CONVERT_CHECKED(String, x, args[0]);
CONVERT_CHECKED(String, y, args[1]);
+ StringShape x_shape(x);
+ StringShape y_shape(y);
+
// A few fast case tests before we flatten.
if (x == y) return Smi::FromInt(EQUAL);
- if (y->length() == 0) {
- if (x->length() == 0) return Smi::FromInt(EQUAL);
+ if (y->length(y_shape) == 0) {
+ if (x->length(x_shape) == 0) return Smi::FromInt(EQUAL);
return Smi::FromInt(GREATER);
- } else if (x->length() == 0) {
+ } else if (x->length(x_shape) == 0) {
return Smi::FromInt(LESS);
}
- int d = x->Get(0) - y->Get(0);
+ int d = x->Get(x_shape, 0) - y->Get(y_shape, 0);
if (d < 0) return Smi::FromInt(LESS);
else if (d > 0) return Smi::FromInt(GREATER);
- x->TryFlatten();
- y->TryFlatten();
+ x->TryFlatten(x_shape); // Shapes are no longer valid!
+ y->TryFlatten(y_shape);
static StringInputBuffer bufx;
static StringInputBuffer bufy;
static void VerifyStringAllocation(const char* string) {
String* s = String::cast(Heap::AllocateStringFromUtf8(CStrVector(string)));
- CHECK_EQ(static_cast<int>(strlen(string)), s->length());
- for (int index = 0; index < s->length(); index++) {
- CHECK_EQ(static_cast<uint16_t>(string[index]), s->Get(index)); }
+ StringShape shape(s);
+ CHECK_EQ(static_cast<int>(strlen(string)), s->length(shape));
+ for (int index = 0; index < s->length(shape); index++) {
+ CHECK_EQ(static_cast<uint16_t>(string[index]), s->Get(shape, index)); }
}
building_blocks[i] =
Factory::NewStringFromTwoByte(Vector<const uc16>(buf, len));
for (int j = 0; j < len; j++) {
- CHECK_EQ(buf[j], building_blocks[i]->Get(j));
+ StringShape shape(*building_blocks[i]);
+ CHECK_EQ(buf[j], building_blocks[i]->Get(shape, j));
}
break;
}
building_blocks[i] =
Factory::NewStringFromAscii(Vector<const char>(buf, len));
for (int j = 0; j < len; j++) {
- CHECK_EQ(buf[j], building_blocks[i]->Get(j));
+ StringShape shape(*building_blocks[i]);
+ CHECK_EQ(buf[j], building_blocks[i]->Get(shape, j));
}
break;
}
Resource* resource = new Resource(Vector<const uc16>(buf, len));
building_blocks[i] = Factory::NewExternalStringFromTwoByte(resource);
for (int j = 0; j < len; j++) {
- CHECK_EQ(buf[j], building_blocks[i]->Get(j));
+ StringShape shape(*building_blocks[i]);
+ CHECK_EQ(buf[j], building_blocks[i]->Get(shape, j));
}
break;
}
building_blocks[i] =
Factory::NewStringFromAscii(Vector<const char>(buf, len));
for (int j = 0; j < len; j++) {
- CHECK_EQ(buf[j], building_blocks[i]->Get(j));
+ StringShape shape(*building_blocks[i]);
+ CHECK_EQ(buf[j], building_blocks[i]->Get(shape, j));
}
break;
}
int depth) {
Handle<String> answer = Factory::NewStringFromAscii(CStrVector(""));
for (int i = 0; i < depth; i++) {
- answer =
- Factory::NewConsString(answer,
- building_blocks[i % NUMBER_OF_BUILDING_BLOCKS]);
+ answer = Factory::NewConsString(
+ answer,
+ StringShape(*answer),
+ building_blocks[i % NUMBER_OF_BUILDING_BLOCKS],
+ StringShape(*building_blocks[i % NUMBER_OF_BUILDING_BLOCKS]));
}
return answer;
}
int depth) {
Handle<String> answer = Factory::NewStringFromAscii(CStrVector(""));
for (int i = depth - 1; i >= 0; i--) {
- answer =
- Factory::NewConsString(building_blocks[i % NUMBER_OF_BUILDING_BLOCKS],
- answer);
+ answer = Factory::NewConsString(
+ building_blocks[i % NUMBER_OF_BUILDING_BLOCKS],
+ StringShape(*building_blocks[i % NUMBER_OF_BUILDING_BLOCKS]),
+ answer,
+ StringShape(*answer));
}
return answer;
}
if (to - from == 2) {
return Factory::NewConsString(
building_blocks[from % NUMBER_OF_BUILDING_BLOCKS],
- building_blocks[(from+1) % NUMBER_OF_BUILDING_BLOCKS]);
+ StringShape(*building_blocks[from % NUMBER_OF_BUILDING_BLOCKS]),
+ building_blocks[(from+1) % NUMBER_OF_BUILDING_BLOCKS],
+ StringShape(*building_blocks[(from+1) % NUMBER_OF_BUILDING_BLOCKS]));
}
+ Handle<String> part1 =
+ ConstructBalancedHelper(building_blocks, from, from + ((to - from) / 2));
+ Handle<String> part2 =
+ ConstructBalancedHelper(building_blocks, from + ((to - from) / 2), to);
return Factory::NewConsString(
- ConstructBalancedHelper(building_blocks, from, from + ((to - from) / 2)),
- ConstructBalancedHelper(building_blocks, from + ((to - from) / 2), to));
+ part1,
+ StringShape(*part1),
+ part2,
+ StringShape(*part2));
}
CHECK_EQ(c, buffer2.GetNext());
i++;
}
- s1->Get(s1->length() - 1);
- s2->Get(s2->length() - 1);
+ StringShape shape1(*s1);
+ StringShape shape2(*s2);
+ s1->Get(shape1, s1->length(shape1) - 1);
+ s2->Get(shape2, s2->length(shape2) - 1);
}
printf("7\n");
Handle<String> right_deep_slice =
Factory::NewStringSlice(left_deep_asymmetric,
+ StringShape(*left_deep_asymmetric),
left_deep_asymmetric->length() - 1050,
left_deep_asymmetric->length() - 50);
Handle<String> left_deep_slice =
Factory::NewStringSlice(right_deep_asymmetric,
+ StringShape(*right_deep_asymmetric),
right_deep_asymmetric->length() - 1050,
right_deep_asymmetric->length() - 50);
printf("8\n");
static Handle<String> SliceOf(Handle<String> underlying) {
int start = gen() % underlying->length();
int end = start + gen() % (underlying->length() - start);
- return Factory::NewStringSlice(underlying, start, end);
+ return Factory::NewStringSlice(underlying,
+ StringShape(*underlying),
+ start,
+ end);
}
Handle<String> rhs = building_blocks[(from+1) % NUMBER_OF_BUILDING_BLOCKS];
if (gen() % 2 == 0)
rhs = SliceOf(rhs);
- return Factory::NewConsString(lhs, rhs);
+ return Factory::NewConsString(lhs,
+ StringShape(*lhs),
+ rhs,
+ StringShape(*rhs));
}
- Handle<String> branch = Factory::NewConsString(
- ConstructBalancedHelper(building_blocks, from, from + ((to - from) / 2)),
- ConstructBalancedHelper(building_blocks, from + ((to - from) / 2), to));
+ Handle<String> part1 =
+ ConstructBalancedHelper(building_blocks, from, from + ((to - from) / 2));
+ Handle<String> part2 =
+ ConstructBalancedHelper(building_blocks, from + ((to - from) / 2), to);
+ Handle<String> branch = Factory::NewConsString(part1,
+ StringShape(*part1),
+ part2,
+ StringShape(*part2));
if (gen() % 2 == 0)
return branch;
return(SliceOf(branch));
Factory::NewStringFromAscii(Vector<const char>(foo, DEEP_ASCII_DEPTH));
Handle<String> foo_string = Factory::NewStringFromAscii(CStrVector("foo"));
for (int i = 0; i < DEEP_ASCII_DEPTH; i += 10) {
- string = Factory::NewConsString(string, foo_string);
+ string = Factory::NewConsString(string,
+ StringShape(*string),
+ foo_string,
+ StringShape(*foo_string));
}
- Handle<String> flat_string = Factory::NewConsString(string, foo_string);
+ Handle<String> flat_string = Factory::NewConsString(string,
+ StringShape(*string),
+ foo_string,
+ StringShape(*foo_string));
FlattenString(flat_string);
for (int i = 0; i < 500; i++) {