From eca89dc765641f1b1d7fbeaf50a0fb592208c43b Mon Sep 17 00:00:00 2001 From: "erik.corry@gmail.com" Date: Mon, 3 Nov 2008 10:16:05 +0000 Subject: [PATCH] Create an abstraction for the string type flags so that they can be cached. Read the objects.h change first to understand what's going on here. Review URL: http://codereview.chromium.org/9038 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@675 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/api.cc | 10 +- src/bootstrapper.cc | 2 +- src/codegen.cc | 6 +- src/compiler.cc | 13 +- src/conversions.cc | 31 ++-- src/factory.cc | 27 ++-- src/factory.h | 9 +- src/handles.cc | 10 +- src/heap.cc | 112 +++++++++------ src/heap.h | 15 +- src/jsregexp.cc | 36 ++--- src/log.cc | 8 +- src/objects-debug.cc | 9 +- src/objects-inl.h | 341 ++++++++++++++++++++++++++------------------ src/objects.cc | 341 +++++++++++++++++++++++++------------------- src/objects.h | 139 ++++++++++-------- src/parser.cc | 12 +- src/prettyprinter.cc | 5 +- src/property.h | 2 +- src/runtime.cc | 255 +++++++++++++++++++-------------- test/cctest/test-heap.cc | 7 +- test/cctest/test-strings.cc | 81 ++++++++--- 22 files changed, 883 insertions(+), 588 deletions(-) diff --git a/src/api.cc b/src/api.cc index 64326a7..3613567 100644 --- a/src/api.cc +++ b/src/api.cc @@ -2036,7 +2036,7 @@ int String::WriteAscii(char* buffer, int start, int length) { i::Handle 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; @@ -2061,7 +2061,7 @@ int String::Write(uint16_t* buffer, int start, int length) { i::Handle 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; @@ -2079,14 +2079,16 @@ int String::Write(uint16_t* buffer, int start, int length) { bool v8::String::IsExternal() { EnsureInitialized("v8::String::IsExternal()"); i::Handle str = Utils::OpenHandle(this); - return str->IsExternalTwoByteString(); + i::StringShape shape(*str); + return shape.IsExternalTwoByte(); } bool v8::String::IsExternalAscii() { EnsureInitialized("v8::String::IsExternalAscii()"); i::Handle str = Utils::OpenHandle(this); - return str->IsExternalAsciiString(); + i::StringShape shape(*str); + return shape.IsExternalAscii(); } diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index f8eb658..d08baa1 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -840,7 +840,7 @@ bool Genesis::CompileScriptCached(Vector name, // function and insert it into the cache. if (!cache->Lookup(name, &boilerplate)) { #ifdef DEBUG - ASSERT(source->IsAsciiRepresentation()); + ASSERT(StringShape(*source).IsAsciiRepresentation()); #endif Handle script_name = Factory::NewStringFromUtf8(name); boilerplate = diff --git a/src/codegen.cc b/src/codegen.cc index 47f7842..70acf65 100644 --- a/src/codegen.cc +++ b/src/codegen.cc @@ -330,12 +330,14 @@ bool CodeGenerator::CheckForInlineRuntimeCall(CallRuntime* node) { {&v8::internal::CodeGenerator::GenerateObjectEquals, "_ObjectEquals"} }; - if (node->name()->length() > 0 && node->name()->Get(0) == '_') { + Handle 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; } diff --git a/src/compiler.cc b/src/compiler.cc index 771620e..53c1a66 100644 --- a/src/compiler.cc +++ b/src/compiler.cc @@ -157,8 +157,9 @@ Handle Compiler::Compile(Handle source, 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); @@ -175,7 +176,7 @@ Handle Compiler::Compile(Handle source, 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 buf(&safe_string_input_buffer); buf->Reset(source.location()); pre_data = PreParse(buf.value(), extension); @@ -208,8 +209,10 @@ Handle Compiler::Compile(Handle source, Handle Compiler::CompileEval(Handle 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); diff --git a/src/conversions.cc b/src/conversions.cc index a1adb22..20a394a 100644 --- a/src/conversions.cc +++ b/src/conversions.cc @@ -55,7 +55,8 @@ static inline int GetChar(const char* str, int index) { static inline int GetChar(String* str, int index) { - return str->Get(index); + StringShape shape(str); + return str->Get(shape, index); } @@ -75,15 +76,18 @@ static inline const char* GetCString(const char* str, int index) { static inline const char* GetCString(String* str, int index) { - char* result = NewArray(str->length() + 1); - for (int i = index; i < str->length(); i++) { - if (str->Get(i) <= 127) { - result[i - index] = static_cast(str->Get(i)); + StringShape shape(str); + int length = str->length(shape); + char* result = NewArray(length + 1); + for (int i = index; i < length; i++) { + uc16 c = str->Get(shape, i); + if (c <= 127) { + result[i - index] = static_cast(c); } else { result[i - index] = 127; // Force number parsing to fail. } } - result[str->length() - index] = '\0'; + result[length - index] = '\0'; return result; } @@ -104,7 +108,8 @@ static inline bool IsSpace(const char* str, int index) { 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)); } @@ -116,12 +121,16 @@ static inline bool SubStringEquals(const char* str, 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 slice = - Factory::NewStringSlice(Handle(str), index, end); - return slice->IsEqualTo(Vector(other, len)); + Factory::NewStringSlice(Handle(str), shape, index, end); + return slice->IsEqualTo(Vector(other, other_length)); } diff --git a/src/factory.cc b/src/factory.cc index 8b3947f..3b3c2c3 100644 --- a/src/factory.cc +++ b/src/factory.cc @@ -90,15 +90,24 @@ Handle Factory::NewRawTwoByteString(int length, Handle Factory::NewConsString(Handle first, - Handle second) { - if (first->length() == 0) return second; - if (second->length() == 0) return first; - CALL_HEAP_FUNCTION(Heap::AllocateConsString(*first, *second), String); -} - - -Handle Factory::NewStringSlice(Handle str, int begin, int end) { - CALL_HEAP_FUNCTION(str->Slice(begin, end), String); + StringShape first_shape, + Handle 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 Factory::NewStringSlice(Handle str, + StringShape shape, + int begin, + int end) { + CALL_HEAP_FUNCTION(str->Slice(shape, begin, end), String); } diff --git a/src/factory.h b/src/factory.h index 0cd80f0..67fed57 100644 --- a/src/factory.h +++ b/src/factory.h @@ -98,11 +98,16 @@ class Factory : public AllStatic { // Create a new cons string object which consists of a pair of strings. static Handle NewConsString(Handle first, - Handle second); + StringShape first_shape, + Handle second, + StringShape second_shape); // Create a new sliced string object which represents a substring of a // backing string. - static Handle NewStringSlice(Handle str, int begin, int end); + static Handle NewStringSlice(Handle 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 diff --git a/src/handles.cc b/src/handles.cc index 5b0e1ce..b45368f 100644 --- a/src/handles.cc +++ b/src/handles.cc @@ -117,9 +117,10 @@ void TransformToFastProperties(Handle object, void FlattenString(Handle 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))); } @@ -216,7 +217,8 @@ Handle LookupSingleCharacterStringFromCode(uint32_t index) { Handle SubString(Handle 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); } diff --git a/src/heap.cc b/src/heap.cc index bb30deb..805ca09 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -777,13 +777,15 @@ void Heap::ScavengeObject(HeapObject** p, HeapObject* object) { 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()); } @@ -794,7 +796,7 @@ void Heap::ScavengeObjectSlow(HeapObject** p, HeapObject* object) { // 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. @@ -1344,32 +1346,43 @@ Object* Heap::AllocateSharedFunctionInfo(Object* name) { } -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; } } @@ -1397,24 +1410,30 @@ Object* Heap::AllocateConsString(String* first, String* second) { } -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); @@ -1429,34 +1448,42 @@ Object* Heap::AllocateSlicedString(String* buffer, int start, int end) { } -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; @@ -1525,8 +1552,9 @@ Object* Heap::LookupSingleCharacterStringFromCode(uint16_t code) { 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; } @@ -1905,9 +1933,10 @@ Object* Heap::AllocateStringFromUtf8(Vector string, // 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; } @@ -1930,8 +1959,9 @@ Object* Heap::AllocateStringFromTwoByte(Vector string, // 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; } @@ -2043,15 +2073,17 @@ Object* Heap::AllocateSymbol(unibrow::CharacterStream* buffer, reinterpret_cast(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; } diff --git a/src/heap.h b/src/heap.h index 958b4eb..dc50c73 100644 --- a/src/heap.h +++ b/src/heap.h @@ -494,7 +494,10 @@ class Heap : public AllStatic { // 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 @@ -502,7 +505,10 @@ class Heap : public AllStatic { // 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 @@ -510,7 +516,10 @@ class Heap : public AllStatic { // 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. diff --git a/src/jsregexp.cc b/src/jsregexp.cc index d8c49ea..2203b5c 100644 --- a/src/jsregexp.cc +++ b/src/jsregexp.cc @@ -129,34 +129,37 @@ Handle RegExpImpl::CachedStringToTwoByte(Handle subject) { // Converts a source string to a 16 bit flat string or a SlicedString containing // a 16 bit flat string). Handle RegExpImpl::StringToTwoByte(Handle pattern) { - if (!pattern->IsFlat()) { + StringShape shape(*pattern); + if (!pattern->IsFlat(shape)) { FlattenString(pattern); } - Handle flat_string(pattern->IsConsString() ? + Handle 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 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 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; @@ -182,13 +185,14 @@ Handle RegExpImpl::Compile(Handle re, Handle cached = CompilationCache::LookupRegExp(pattern, flags); bool in_cache = !cached.is_null(); Handle 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) { diff --git a/src/log.cc b/src/log.cc index 25fa5e5..c7b1539 100644 --- a/src/log.cc +++ b/src/log.cc @@ -350,11 +350,12 @@ void Logger::SharedLibraryEvent(const wchar_t* library_path, #ifdef ENABLE_LOGGING_AND_PROFILING void Logger::LogString(Handle 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) { @@ -430,7 +431,8 @@ void Logger::RegExpExecEvent(Handle regexp, 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 } diff --git a/src/objects-debug.cc b/src/objects-debug.cc index d67544e..f2dd3b5 100644 --- a/src/objects-debug.cc +++ b/src/objects-debug.cc @@ -465,19 +465,20 @@ void JSValue::JSValueVerify() { 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("\""); } diff --git a/src/objects-inl.h b/src/objects-inl.h index 4d37a6d..0532be3 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -114,85 +114,177 @@ bool Object::IsString() { } -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(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(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); } @@ -1128,13 +1220,15 @@ void DescriptorArray::fast_swap(FixedArray* array, int first, int second) { 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); } @@ -1268,19 +1362,27 @@ INT_ACCESSORS(Array, length, kLengthOffset) 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)); } @@ -1289,9 +1391,10 @@ void String::set_length(int value) { ASSERT(kMediumStringTag + kLongLengthShift == kMediumLengthShift); ASSERT(kLongStringTag == 0); + StringShape shape(this); WRITE_INT_FIELD(this, kLengthOffset, - value << (size_tag() + kLongLengthShift)); + value << (shape.size_tag() + kLongLengthShift)); } @@ -1305,31 +1408,34 @@ void String::set_length_field(uint32_t value) { } -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; } @@ -1339,86 +1445,29 @@ uint16_t String::Get(int index) { } -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(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: @@ -1472,7 +1521,7 @@ void SeqTwoByteString::SeqTwoByteStringSet(int index, uint16_t value) { } -int SeqTwoByteString::SeqTwoByteStringSize(Map* map) { +int SeqTwoByteString::SeqTwoByteStringSize(StringShape shape) { uint32_t length = READ_INT_FIELD(this, kLengthOffset); ASSERT(kShortStringTag + kLongLengthShift == kShortLengthShift); @@ -1481,13 +1530,13 @@ int SeqTwoByteString::SeqTwoByteStringSize(Map* map) { // 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); @@ -1496,40 +1545,50 @@ int SeqAsciiString::SeqAsciiStringSize(Map* map) { // 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); } diff --git a/src/objects.cc b/src/objects.cc index c78c65a..23c8061 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -539,25 +539,26 @@ Failure* Failure::RetryAfterGC(int requested_bytes, AllocationSpace space) { // 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. @@ -566,44 +567,48 @@ Object* String::Flatten() { 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); @@ -612,10 +617,13 @@ Object* String::Flatten() { 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); @@ -631,7 +639,8 @@ Object* String::Flatten() { void String::StringShortPrint(StringStream* accumulator) { - int len = length(); + StringShape shape(this); + int len = length(shape); if (len > kMaxMediumStringSize) { accumulator->Add("", len); return; @@ -659,7 +668,7 @@ void String::StringShortPrint(StringStream* accumulator) { } buf.Reset(this); if (ascii) { - accumulator->Add("Add("Put(buf.GetNext()); } @@ -667,7 +676,7 @@ void String::StringShortPrint(StringStream* accumulator) { } else { // Backslash indicates that the string contains control // characters and that backslashes are therefore escaped. - accumulator->Add("Add("instance_type(); if (instance_type < FIRST_NONSTRING_TYPE - && (reinterpret_cast(this)->map_representation_tag(map) - == kSeqStringTag)) { - if (reinterpret_cast(this)->is_ascii_representation_map(map)) { - return reinterpret_cast(this)->SeqAsciiStringSize(map); + && (StringShape(instance_type).IsSequential())) { + StringShape shape(instance_type); + if (shape.IsAsciiRepresentation()) { + return reinterpret_cast(this)->SeqAsciiStringSize(shape); } else { SeqTwoByteString* self = reinterpret_cast(this); - return self->SeqTwoByteStringSize(map); + return self->SeqTwoByteStringSize(shape); } } @@ -2355,7 +2364,7 @@ Object* JSObject::DefineGetterSetter(String* name, } // TryFlatten before operating on the string. - name->TryFlatten(); + name->TryFlatten(StringShape(name)); // Make sure name is not an index. uint32_t index; @@ -2994,27 +3003,21 @@ static StaticResource string_input_buffer; 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 buffer(&string_input_buffer); buffer->Reset(0, this); int result = 0; @@ -3025,23 +3028,26 @@ int String::Utf8Length() { Vector 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); @@ -3056,23 +3062,26 @@ Vector String::ToAsciiVector() { Vector 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); @@ -3152,8 +3161,9 @@ const uc16* String::GetTwoByteData() { 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: @@ -3161,12 +3171,12 @@ const uc16* String::GetTwoByteData(unsigned start) { 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()); } @@ -3267,8 +3277,9 @@ const unibrow::byte* ConsString::ConsStringReadBlock(ReadBlockBuffer* rbb, 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 && @@ -3280,7 +3291,7 @@ const unibrow::byte* ConsString::ConsStringReadBlock(ReadBlockBuffer* rbb, // 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 { @@ -3292,10 +3303,10 @@ const unibrow::byte* ConsString::ConsStringReadBlock(ReadBlockBuffer* rbb, } 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 { @@ -3327,7 +3338,7 @@ const unibrow::byte* ConsString::ConsStringReadBlock(ReadBlockBuffer* rbb, 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) { @@ -3445,9 +3456,10 @@ const unibrow::byte* String::ReadBlock(String* input, 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, @@ -3468,7 +3480,7 @@ const unibrow::byte* String::ReadBlock(String* input, offset_ptr, max_chars); case kExternalStringTag: - if (input->is_ascii_representation()) { + if (shape.IsAsciiRepresentation()) { return ExternalAsciiString::cast(input)->ExternalAsciiStringReadBlock( &rbb->remaining, offset_ptr, @@ -3507,12 +3519,13 @@ void String::ReadBlockIntoBuffer(String* input, 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); @@ -3534,7 +3547,7 @@ void String::ReadBlockIntoBuffer(String* input, max_chars); return; case kExternalStringTag: - if (input->is_ascii_representation()) { + if (shape.IsAsciiRepresentation()) { ExternalAsciiString::cast(input)-> ExternalAsciiStringReadBlockIntoBuffer(rbb, offset_ptr, max_chars); } else { @@ -3558,11 +3571,12 @@ const unibrow::byte* String::ReadBlock(String* input, 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(input->length())); + ASSERT(rbb.remaining <= static_cast(input->length(shape))); *remaining = rbb.remaining; return answer; } @@ -3573,13 +3587,14 @@ const unibrow::byte* String::ReadBlock(String** raw_input, unsigned capacity, unsigned* remaining, unsigned* offset_ptr) { + StringShape shape(*raw_input); Handle 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(input->length())); + ASSERT(rbb.remaining <= static_cast(input->length(shape))); *remaining = rbb.remaining; return rbb.util_buffer; } @@ -3598,13 +3613,14 @@ void ConsString::ConsStringReadBlockIntoBuffer(ReadBlockBuffer* rbb, 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 { @@ -3617,8 +3633,8 @@ void ConsString::ConsStringReadBlockIntoBuffer(ReadBlockBuffer* rbb, // 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 { @@ -3650,7 +3666,7 @@ void ConsString::ConsStringReadBlockIntoBuffer(ReadBlockBuffer* rbb, 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) { @@ -3670,24 +3686,29 @@ uint16_t ConsString::ConsStringGet(int index) { 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); } } @@ -3701,9 +3722,10 @@ Object* SlicedString::SlicedStringFlatten() { // 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; @@ -3712,15 +3734,17 @@ Object* SlicedString::SlicedStringFlatten() { template 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, @@ -3754,35 +3778,40 @@ void String::WriteToFlat(String* src, 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; } @@ -3799,7 +3828,8 @@ void SlicedString::SlicedStringIterateBody(ObjectVisitor* v) { 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); } @@ -3863,8 +3893,9 @@ static StringInputBuffer string_compare_buffer_b; template 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 ib(b->ToAsciiVector()); return CompareStringContents(ia, &ib); } else { @@ -3881,10 +3912,12 @@ static inline bool CompareStringContentsPartial(IteratorA* ia, String* b) { 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 @@ -3893,18 +3926,18 @@ bool String::SlowEquals(String* other) { 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(str1, len), Vector(str2, len)); } - if (this->IsFlat()) { - if (this->IsAsciiRepresentation()) { + if (this->IsFlat(this_shape)) { + if (this_shape.IsAsciiRepresentation()) { Vector vec1 = this->ToAsciiVector(); - if (other->IsFlat()) { - if (other->IsAsciiRepresentation()) { + if (other->IsFlat(other_shape)) { + if (other_shape.IsAsciiRepresentation()) { Vector vec2 = other->ToAsciiVector(); return CompareRawStringContents(vec1, vec2); } else { @@ -3919,8 +3952,8 @@ bool String::SlowEquals(String* other) { } } else { Vector vec1 = this->ToUC16Vector(); - if (other->IsFlat()) { - if (other->IsAsciiRepresentation()) { + if (other->IsFlat(other_shape)) { + if (other_shape.IsAsciiRepresentation()) { VectorIterator buf1(vec1); VectorIterator ib(other->ToAsciiVector()); return CompareStringContents(&buf1, &ib); @@ -3942,7 +3975,8 @@ bool String::SlowEquals(String* other) { 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()) { @@ -3970,13 +4004,14 @@ bool String::MarkAsUndetectable() { bool String::IsEqualTo(Vector str) { - int slen = length(); + StringShape this_shape(this); + int slen = length(this_shape); Access 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(); } @@ -4030,7 +4065,8 @@ bool String::ComputeArrayIndex(unibrow::CharacterStream* buffer, 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; @@ -4038,7 +4074,7 @@ bool String::SlowAsArrayIndex(uint32_t* index) { return true; } else { StringInputBuffer buffer(this); - return ComputeArrayIndex(&buffer, index, length()); + return ComputeArrayIndex(&buffer, index, length(shape)); } } @@ -4098,20 +4134,21 @@ uint32_t String::ComputeLengthAndHashField(unibrow::CharacterStream* buffer, } -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. @@ -4123,12 +4160,15 @@ Object* String::Slice(int start, int end) { // 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()); } } @@ -4137,9 +4177,10 @@ Object* String::Slice(int start, int end) { 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)); } } @@ -5624,12 +5665,13 @@ int JSObject::GetLocalElementKeys(FixedArray* storage, 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); @@ -5814,11 +5856,12 @@ class SymbolKey : public HashTableKey { 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. @@ -5976,7 +6019,7 @@ bool SymbolTable::LookupSymbolIfExists(String* string, String** symbol) { return false; } else { String* result = String::cast(KeyAt(entry)); - ASSERT(result->is_symbol()); + ASSERT(StringShape(result).IsSymbol()); *symbol = result; return true; } diff --git a/src/objects.h b/src/objects.h index f25a259..2fcf131 100644 --- a/src/objects.h +++ b/src/objects.h @@ -595,20 +595,16 @@ class Object BASE_EMBEDDED { 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(); @@ -3022,6 +3018,53 @@ class StringHasher { }; +// 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: @@ -3033,6 +3076,9 @@ class StringHasher { 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); @@ -3044,32 +3090,20 @@ class String: public HeapObject { 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 ToAsciiVector(); Vector ToUC16Vector(); @@ -3079,7 +3113,7 @@ class String: public HeapObject { 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); @@ -3135,24 +3169,6 @@ class String: public HeapObject { 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(); @@ -3162,7 +3178,7 @@ class String: public HeapObject { void StringPrint(); void StringVerify(); #endif - inline bool IsFlat(); + inline bool IsFlat(StringShape shape); // Layout description. static const int kLengthOffset = HeapObject::kHeaderSize; @@ -3222,6 +3238,7 @@ class String: public HeapObject { // Helper function for flattening strings. template static void WriteToFlat(String* source, + StringShape shape, sinkchar* sink, int from, int to); @@ -3262,7 +3279,9 @@ class String: public HeapObject { 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); @@ -3309,7 +3328,7 @@ class SeqAsciiString: public SeqString { // 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) { @@ -3354,7 +3373,7 @@ class SeqTwoByteString: public SeqString { // 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) { @@ -3384,14 +3403,20 @@ class SeqTwoByteString: public SeqString { // 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. @@ -3433,8 +3458,8 @@ class ConsString: public String { 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(); diff --git a/src/parser.cc b/src/parser.cc index 9090dbb..ef92e3c 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -737,10 +737,11 @@ FunctionLiteral* Parser::ParseProgram(Handle source, 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); @@ -767,7 +768,7 @@ FunctionLiteral* Parser::ParseProgram(Handle source, 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(); } @@ -789,11 +790,12 @@ FunctionLiteral* Parser::ParseLazy(Handle source, 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; diff --git a/src/prettyprinter.cc b/src/prettyprinter.cc index ecaa7c5..39af187 100644 --- a/src/prettyprinter.cc +++ b/src/prettyprinter.cc @@ -503,9 +503,10 @@ void PrettyPrinter::PrintLiteral(Handle value, bool quote) { 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()) { diff --git a/src/property.h b/src/property.h index dc72bb3..9af6cbb 100644 --- a/src/property.h +++ b/src/property.h @@ -45,7 +45,7 @@ class Descriptor BASE_EMBEDDED { } Object* KeyToSymbol() { - if (!key_->IsSymbol()) { + if (!StringShape(key_).IsSymbol()) { Object* result = Heap::LookupSymbol(key_); if (result->IsFailure()) return result; key_ = String::cast(result); diff --git a/src/runtime.cc b/src/runtime.cc index a1d6a9d..7dc2871 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -969,9 +969,12 @@ static Object* CharCodeAt(String* subject, Object* index) { // 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(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(subject->length(StringShape(subject)))) { + return Heap::nan_value(); + } + return Smi::FromInt(subject->Get(StringShape(subject), i)); } @@ -1351,39 +1354,51 @@ int Runtime::StringMatch(Handle sub, Handle 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 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 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); @@ -1415,14 +1430,17 @@ static Object* Runtime_StringLastIndexOf(Arguments args) { 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; @@ -1431,7 +1449,7 @@ static Object* Runtime_StringLastIndexOf(Arguments args) { 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; } @@ -1451,8 +1469,10 @@ static Object* Runtime_StringLocaleCompare(Arguments args) { 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) { @@ -1467,11 +1487,11 @@ static Object* Runtime_StringLocaleCompare(Arguments args) { // 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; @@ -1500,10 +1520,12 @@ static Object* Runtime_StringSlice(Arguments args) { 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); } @@ -1606,9 +1628,11 @@ static Object* Runtime_NumberToPrecision(Arguments args) { // Returns a single character string where first character equals // string->Get(index). static Handle GetCharAt(Handle string, uint32_t index) { - if (index < static_cast(string->length())) { - string->TryFlatten(); - return LookupSingleCharacterStringFromCode(string->Get(index)); + StringShape shape(*string); + if (index < static_cast(string->length(shape))) { + string->TryFlatten(shape); // Invalidates shape! + return LookupSingleCharacterStringFromCode( + string->Get(StringShape(*string), index)); } return Execution::CharAt(string, index); } @@ -1793,7 +1817,7 @@ Object* Runtime::SetObjectProperty(Handle object, result = SetElement(js_object, index, value); } else { Handle key_string = Handle::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(); @@ -1884,7 +1908,8 @@ static Object* Runtime_HasLocalProperty(Arguments args) { uint32_t index; if (key->AsArrayIndex(&index)) { String* string = String::cast(args[0]); - if (index < static_cast(string->length())) + StringShape shape(string); + if (index < static_cast(string->length(shape))) return Heap::true_value(); } } @@ -2065,7 +2090,7 @@ static Object* Runtime_StringToNumber(Arguments args) { 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)); } @@ -2095,10 +2120,11 @@ static Object* Runtime_StringFromCharCodeArray(Arguments args) { 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; } @@ -2147,7 +2173,7 @@ static Object* Runtime_URIEscape(Arguments args) { ASSERT(args.length() == 1); CONVERT_CHECKED(String, source, args[0]); - source->TryFlatten(); + source->TryFlatten(StringShape(source)); int escaped_length = 0; int length = source->length(); @@ -2177,27 +2203,28 @@ static Object* Runtime_URIEscape(Arguments args) { Object* o = Heap::AllocateRawAsciiString(escaped_length); if (o->IsFailure()) return o; String* destination = String::cast(o); + StringShape dshape(destination); int dest_position = 0; Access 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; } } @@ -2225,19 +2252,26 @@ static inline int TwoDigitHex(uint16_t character1, uint16_t character2) { } -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 { @@ -2252,15 +2286,21 @@ static Object* Runtime_URIUnescape(Arguments args) { 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; } @@ -2274,11 +2314,14 @@ static Object* Runtime_URIUnescape(Arguments args) { 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; @@ -2292,31 +2335,33 @@ static Object* Runtime_StringParseInt(Arguments args) { 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; @@ -2325,8 +2370,8 @@ static Object* Runtime_StringParseInt(Arguments args) { } } 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; } } @@ -2363,12 +2408,14 @@ static Object* ConvertCase(Arguments args, 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 @@ -2384,11 +2431,12 @@ static Object* ConvertCase(Arguments args, // 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 @@ -2405,12 +2453,12 @@ static Object* ConvertCase(Arguments args, 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(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) { @@ -2445,7 +2493,7 @@ static Object* ConvertCase(Arguments args, 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; @@ -2474,22 +2522,6 @@ static Object* Runtime_StringToUpperCase(Arguments args) { } -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); @@ -2629,8 +2661,10 @@ static Object* Runtime_StringAdd(Arguments args) { 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; @@ -2640,12 +2674,13 @@ static Object* Runtime_StringAdd(Arguments args) { Top::context()->mark_out_of_memory(); return Failure::OutOfMemoryException(); } - return Heap::AllocateConsString(str1, str2); + return Heap::AllocateConsString(str1, shape1, str2, shape2); } template static inline void StringBuilderConcatHelper(String* special, + StringShape special_shape, sinkchar* sink, FixedArray* fixed_array, int array_length) { @@ -2656,12 +2691,17 @@ static inline void StringBuilderConcatHelper(String* special, 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; } } @@ -2673,7 +2713,8 @@ static Object* Runtime_StringBuilderConcat(Arguments args) { 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(); @@ -2695,7 +2736,7 @@ static Object* Runtime_StringBuilderConcat(Arguments args) { 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); @@ -2709,13 +2750,14 @@ static Object* Runtime_StringBuilderConcat(Arguments args) { 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 { @@ -2731,6 +2773,7 @@ static Object* Runtime_StringBuilderConcat(Arguments args) { if (object->IsFailure()) return object; SeqAsciiString* answer = SeqAsciiString::cast(object); StringBuilderConcatHelper(special, + special_shape, answer->GetChars(), fixed_array, array_length); @@ -2740,6 +2783,7 @@ static Object* Runtime_StringBuilderConcat(Arguments args) { if (object->IsFailure()) return object; SeqTwoByteString* answer = SeqTwoByteString::cast(object); StringBuilderConcatHelper(special, + special_shape, answer->GetChars(), fixed_array, array_length); @@ -2934,21 +2978,24 @@ static Object* Runtime_StringCompare(Arguments args) { 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; diff --git a/test/cctest/test-heap.cc b/test/cctest/test-heap.cc index 0a4f90f..4ac1ae4 100644 --- a/test/cctest/test-heap.cc +++ b/test/cctest/test-heap.cc @@ -248,9 +248,10 @@ TEST(GarbageCollection) { static void VerifyStringAllocation(const char* string) { String* s = String::cast(Heap::AllocateStringFromUtf8(CStrVector(string))); - CHECK_EQ(static_cast(strlen(string)), s->length()); - for (int index = 0; index < s->length(); index++) { - CHECK_EQ(static_cast(string[index]), s->Get(index)); } + StringShape shape(s); + CHECK_EQ(static_cast(strlen(string)), s->length(shape)); + for (int index = 0; index < s->length(shape); index++) { + CHECK_EQ(static_cast(string[index]), s->Get(shape, index)); } } diff --git a/test/cctest/test-strings.cc b/test/cctest/test-strings.cc index 4484dd5..ec080e2 100644 --- a/test/cctest/test-strings.cc +++ b/test/cctest/test-strings.cc @@ -62,7 +62,8 @@ static void InitializeBuildingBlocks( building_blocks[i] = Factory::NewStringFromTwoByte(Vector(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; } @@ -74,7 +75,8 @@ static void InitializeBuildingBlocks( building_blocks[i] = Factory::NewStringFromAscii(Vector(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; } @@ -99,7 +101,8 @@ static void InitializeBuildingBlocks( Resource* resource = new Resource(Vector(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; } @@ -111,7 +114,8 @@ static void InitializeBuildingBlocks( building_blocks[i] = Factory::NewStringFromAscii(Vector(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; } @@ -125,9 +129,11 @@ static Handle ConstructLeft( int depth) { Handle 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; } @@ -138,9 +144,11 @@ static Handle ConstructRight( int depth) { Handle 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; } @@ -157,11 +165,19 @@ static Handle ConstructBalancedHelper( 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 part1 = + ConstructBalancedHelper(building_blocks, from, from + ((to - from) / 2)); + Handle 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)); } @@ -199,8 +215,10 @@ static void TraverseFirst(Handle s1, Handle s2, int chars) { 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); } @@ -233,10 +251,12 @@ TEST(Traverse) { printf("7\n"); Handle right_deep_slice = Factory::NewStringSlice(left_deep_asymmetric, + StringShape(*left_deep_asymmetric), left_deep_asymmetric->length() - 1050, left_deep_asymmetric->length() - 50); Handle 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"); @@ -262,7 +282,10 @@ TEST(Traverse) { static Handle SliceOf(Handle 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); } @@ -280,11 +303,19 @@ static Handle ConstructSliceTree( Handle 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 branch = Factory::NewConsString( - ConstructBalancedHelper(building_blocks, from, from + ((to - from) / 2)), - ConstructBalancedHelper(building_blocks, from + ((to - from) / 2), to)); + Handle part1 = + ConstructBalancedHelper(building_blocks, from, from + ((to - from) / 2)); + Handle part2 = + ConstructBalancedHelper(building_blocks, from + ((to - from) / 2), to); + Handle branch = Factory::NewConsString(part1, + StringShape(*part1), + part2, + StringShape(*part2)); if (gen() % 2 == 0) return branch; return(SliceOf(branch)); @@ -324,9 +355,15 @@ TEST(DeepAscii) { Factory::NewStringFromAscii(Vector(foo, DEEP_ASCII_DEPTH)); Handle 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 flat_string = Factory::NewConsString(string, foo_string); + Handle flat_string = Factory::NewConsString(string, + StringShape(*string), + foo_string, + StringShape(*foo_string)); FlattenString(flat_string); for (int i = 0; i < 500; i++) { -- 2.7.4