Create an abstraction for the string type flags so that they can be cached.
authorerik.corry@gmail.com <erik.corry@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 3 Nov 2008 10:16:05 +0000 (10:16 +0000)
committererik.corry@gmail.com <erik.corry@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 3 Nov 2008 10:16:05 +0000 (10:16 +0000)
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

22 files changed:
src/api.cc
src/bootstrapper.cc
src/codegen.cc
src/compiler.cc
src/conversions.cc
src/factory.cc
src/factory.h
src/handles.cc
src/heap.cc
src/heap.h
src/jsregexp.cc
src/log.cc
src/objects-debug.cc
src/objects-inl.h
src/objects.cc
src/objects.h
src/parser.cc
src/prettyprinter.cc
src/property.h
src/runtime.cc
test/cctest/test-heap.cc
test/cctest/test-strings.cc

index 64326a7dd17b0a7cb4c4aafc6819b5c09a6a4c1e..36135670bf2fb4422b448607d5aff7abf14eab88 100644 (file)
@@ -2036,7 +2036,7 @@ int String::WriteAscii(char* buffer, int start, int length) {
   i::Handle<i::String> str = Utils::OpenHandle(this);
   // Flatten the string for efficiency.  This applies whether we are
   // using StringInputBuffer or Get(i) to access the characters.
-  str->TryFlatten();
+  str->TryFlatten(i::StringShape(*str));
   int end = length;
   if ( (length == -1) || (length > str->length() - start) )
     end = str->length() - start;
@@ -2061,7 +2061,7 @@ int String::Write(uint16_t* buffer, int start, int length) {
   i::Handle<i::String> str = Utils::OpenHandle(this);
   // Flatten the string for efficiency.  This applies whether we are
   // using StringInputBuffer or Get(i) to access the characters.
-  str->TryFlatten();
+  str->TryFlatten(i::StringShape(*str));
   int end = length;
   if ( (length == -1) || (length > str->length() - start) )
     end = str->length() - start;
@@ -2079,14 +2079,16 @@ int String::Write(uint16_t* buffer, int start, int length) {
 bool v8::String::IsExternal() {
   EnsureInitialized("v8::String::IsExternal()");
   i::Handle<i::String> str = Utils::OpenHandle(this);
-  return str->IsExternalTwoByteString();
+  i::StringShape shape(*str);
+  return shape.IsExternalTwoByte();
 }
 
 
 bool v8::String::IsExternalAscii() {
   EnsureInitialized("v8::String::IsExternalAscii()");
   i::Handle<i::String> str = Utils::OpenHandle(this);
-  return str->IsExternalAsciiString();
+  i::StringShape shape(*str);
+  return shape.IsExternalAscii();
 }
 
 
index f8eb658d6d0a653a71082e359bba17d58bd35984..d08baa1cd1c3a55a1bace3c0fdc98ba95ba10b4c 100644 (file)
@@ -840,7 +840,7 @@ bool Genesis::CompileScriptCached(Vector<const char> name,
   // function and insert it into the cache.
   if (!cache->Lookup(name, &boilerplate)) {
 #ifdef DEBUG
-    ASSERT(source->IsAsciiRepresentation());
+    ASSERT(StringShape(*source).IsAsciiRepresentation());
 #endif
     Handle<String> script_name = Factory::NewStringFromUtf8(name);
     boilerplate =
index 47f7842c3da32be6b79150438a68426484b2beda..70acf65475db7a71979c935b870c65c750d9d784 100644 (file)
@@ -330,12 +330,14 @@ bool CodeGenerator::CheckForInlineRuntimeCall(CallRuntime* node) {
     {&v8::internal::CodeGenerator::GenerateObjectEquals,
      "_ObjectEquals"}
   };
-  if (node->name()->length() > 0 && node->name()->Get(0) == '_') {
+  Handle<String> name = node->name();
+  StringShape shape(*name);
+  if (name->length(shape) > 0 && name->Get(shape, 0) == '_') {
     for (unsigned i = 0;
          i < sizeof(kInlineRuntimeLUT) / sizeof(InlineRuntimeLUT);
          i++) {
       const InlineRuntimeLUT* entry = kInlineRuntimeLUT + i;
-      if (node->name()->IsEqualTo(CStrVector(entry->name))) {
+      if (name->IsEqualTo(CStrVector(entry->name))) {
         ((*this).*(entry->method))(args);
         return true;
       }
index 771620e0dd858d28255a70e3597a76e9767bc4fc..53c1a662a2eb4055c2bc8b0ae728899c82e9093d 100644 (file)
@@ -157,8 +157,9 @@ Handle<JSFunction> Compiler::Compile(Handle<String> 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<JSFunction> Compiler::Compile(Handle<String> 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<SafeStringInputBuffer> buf(&safe_string_input_buffer);
       buf->Reset(source.location());
       pre_data = PreParse(buf.value(), extension);
@@ -208,8 +209,10 @@ Handle<JSFunction> Compiler::Compile(Handle<String> source,
 Handle<JSFunction> Compiler::CompileEval(Handle<String> source,
                                          int line_offset,
                                          bool is_global) {
-  Counters::total_eval_size.Increment(source->length());
-  Counters::total_compile_size.Increment(source->length());
+  StringShape source_shape(*source);
+  int source_length = source->length(source_shape);
+  Counters::total_eval_size.Increment(source_length);
+  Counters::total_compile_size.Increment(source_length);
 
   // The VM is in the COMPILER state until exiting this function.
   VMState state(COMPILER);
index a1adb22e09b581c550b6d506281ea509931b395f..20a394a5894bbab05028c3d5d15e914229d3fa42 100644 (file)
@@ -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<char>(str->length() + 1);
-  for (int i = index; i < str->length(); i++) {
-    if (str->Get(i) <= 127) {
-      result[i - index] = static_cast<char>(str->Get(i));
+  StringShape shape(str);
+  int length = str->length(shape);
+  char* result = NewArray<char>(length + 1);
+  for (int i = index; i < length; i++) {
+    uc16 c = str->Get(shape, i);
+    if (c <= 127) {
+      result[i - index] = static_cast<char>(c);
     } else {
       result[i - index] = 127;  // Force number parsing to fail.
     }
   }
-  result[str->length() - index] = '\0';
+  result[length - index] = '\0';
   return result;
 }
 
@@ -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<String> slice =
-      Factory::NewStringSlice(Handle<String>(str), index, end);
-  return slice->IsEqualTo(Vector<const char>(other, len));
+      Factory::NewStringSlice(Handle<String>(str), shape, index, end);
+  return slice->IsEqualTo(Vector<const char>(other, other_length));
 }
 
 
index 8b3947fff5660255ed62cb409ae86d65089e91de..3b3c2c3e57bddb01628d9ea34a1533ee285d7374 100644 (file)
@@ -90,15 +90,24 @@ Handle<String> Factory::NewRawTwoByteString(int length,
 
 
 Handle<String> Factory::NewConsString(Handle<String> first,
-                                      Handle<String> second) {
-  if (first->length() == 0) return second;
-  if (second->length() == 0) return first;
-  CALL_HEAP_FUNCTION(Heap::AllocateConsString(*first, *second), String);
-}
-
-
-Handle<String> Factory::NewStringSlice(Handle<String> str, int begin, int end) {
-  CALL_HEAP_FUNCTION(str->Slice(begin, end), String);
+                                      StringShape first_shape,
+                                      Handle<String> second,
+                                      StringShape second_shape) {
+  if (first->length(first_shape) == 0) return second;
+  if (second->length(second_shape) == 0) return first;
+  CALL_HEAP_FUNCTION(Heap::AllocateConsString(*first,
+                                              first_shape,
+                                              *second,
+                                              second_shape),
+                     String);
+}
+
+
+Handle<String> Factory::NewStringSlice(Handle<String> str,
+                                       StringShape shape,
+                                       int begin,
+                                       int end) {
+  CALL_HEAP_FUNCTION(str->Slice(shape, begin, end), String);
 }
 
 
index 0cd80f069b0231bb1cb005da60b471ffe6875c94..67fed5746803b20b29d2e399aef167a2e11ac76e 100644 (file)
@@ -98,11 +98,16 @@ class Factory : public AllStatic {
 
   // Create a new cons string object which consists of a pair of strings.
   static Handle<String> NewConsString(Handle<String> first,
-                                      Handle<String> second);
+                                      StringShape first_shape,
+                                      Handle<String> second,
+                                      StringShape second_shape);
 
   // Create a new sliced string object which represents a substring of a
   // backing string.
-  static Handle<String> NewStringSlice(Handle<String> str, int begin, int end);
+  static Handle<String> NewStringSlice(Handle<String> str,
+                                       StringShape shape,
+                                       int begin,
+                                       int end);
 
   // Creates a new external String object.  There are two String encodings
   // in the system: ASCII and two byte.  Unlike other String types, it does
index 5b0e1ce1f64cafd3728e4e0b8d4099d19889eb2a..b45368f5617c0a756ee47a6eb52e0d82c30107ce 100644 (file)
@@ -117,9 +117,10 @@ void TransformToFastProperties(Handle<JSObject> object,
 
 
 void FlattenString(Handle<String> string) {
-  if (string->IsFlat()) return;
-  CALL_HEAP_FUNCTION_VOID(string->Flatten());
-  ASSERT(string->IsFlat());
+  StringShape shape(*string);
+  if (string->IsFlat(shape)) return;
+  CALL_HEAP_FUNCTION_VOID(string->Flatten(shape));
+  ASSERT(string->IsFlat(StringShape(*string)));
 }
 
 
@@ -216,7 +217,8 @@ Handle<Object> LookupSingleCharacterStringFromCode(uint32_t index) {
 
 
 Handle<String> SubString(Handle<String> str, int start, int end) {
-  CALL_HEAP_FUNCTION(str->Slice(start, end), String);
+  StringShape shape(*str);
+  CALL_HEAP_FUNCTION(str->Slice(shape, start, end), String);
 }
 
 
index bb30deb2cb9bd153beee997790af54ed404fcc07..805ca0948ce90778f7515f5eb305bb7a9cf62e29 100644 (file)
@@ -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<const char> 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<const uc16> 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<HeapObject*>(result)->set_map(map);
   // The hash value contains the length of the string.
-  String::cast(result)->set_length_field(length_field);
+  String* answer = String::cast(result);
+  StringShape answer_shape(answer);
+  answer->set_length_field(length_field);
 
-  ASSERT_EQ(size, String::cast(result)->Size());
+  ASSERT_EQ(size, answer->Size());
 
   // Fill in the characters.
   for (int i = 0; i < chars; i++) {
-    String::cast(result)->Set(i, buffer->GetNext());
+    answer->Set(answer_shape, i, buffer->GetNext());
   }
-  return result;
+  return answer;
 }
 
 
index 958b4eb9b87891b677bf5876c668b654f6a38109..dc50c73c91c3a62d9291789dd52bcdc869d4c9af 100644 (file)
@@ -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.
index d8c49eae4a6e33d15f72c9250fe1913ac184b7e5..2203b5ce220d7baf66dc6600d3e1d6945a50d59f 100644 (file)
@@ -129,34 +129,37 @@ Handle<String> RegExpImpl::CachedStringToTwoByte(Handle<String> subject) {
 // Converts a source string to a 16 bit flat string or a SlicedString containing
 // a 16 bit flat string).
 Handle<String> RegExpImpl::StringToTwoByte(Handle<String> pattern) {
-  if (!pattern->IsFlat()) {
+  StringShape shape(*pattern);
+  if (!pattern->IsFlat(shape)) {
     FlattenString(pattern);
   }
-  Handle<String> flat_string(pattern->IsConsString() ?
+  Handle<String> flat_string(shape.IsCons() ?
     String::cast(ConsString::cast(*pattern)->first()) :
     *pattern);
-  ASSERT(!flat_string->IsConsString());
-  ASSERT(flat_string->IsSeqString() || flat_string->IsSlicedString() ||
-         flat_string->IsExternalString());
-  if (!flat_string->IsAsciiRepresentation()) {
+  ASSERT(flat_string->IsString());
+  StringShape flat_shape(*flat_string);
+  ASSERT(!flat_shape.IsCons());
+  ASSERT(flat_shape.IsSequential() ||
+         flat_shape.IsSliced() ||
+         flat_shape.IsExternal());
+  if (!flat_shape.IsAsciiRepresentation()) {
     return flat_string;
   }
 
+  int len = flat_string->length(flat_shape);
   Handle<String> two_byte_string =
-    Factory::NewRawTwoByteString(flat_string->length(), TENURED);
-  static StringInputBuffer convert_to_two_byte_buffer;
-  convert_to_two_byte_buffer.Reset(*flat_string);
-  for (int i = 0; convert_to_two_byte_buffer.has_more(); i++) {
-    two_byte_string->Set(i, convert_to_two_byte_buffer.GetNext());
-  }
+    Factory::NewRawTwoByteString(len, TENURED);
+  uc16* dest = SeqTwoByteString::cast(*two_byte_string)->GetChars();
+  String::WriteToFlat(*flat_string, flat_shape, dest, 0, len);
   return two_byte_string;
 }
 
 
 static JSRegExp::Flags RegExpFlagsFromString(Handle<String> str) {
   int flags = JSRegExp::NONE;
-  for (int i = 0; i < str->length(); i++) {
-    switch (str->Get(i)) {
+  StringShape shape(*str);
+  for (int i = 0; i < str->length(shape); i++) {
+    switch (str->Get(shape, i)) {
       case 'i':
         flags |= JSRegExp::IGNORE_CASE;
         break;
@@ -182,13 +185,14 @@ Handle<Object> RegExpImpl::Compile(Handle<JSRegExp> re,
   Handle<FixedArray> cached = CompilationCache::LookupRegExp(pattern, flags);
   bool in_cache = !cached.is_null();
   Handle<Object> result;
+  StringShape shape(*pattern);
   if (in_cache) {
     re->set_data(*cached);
     result = re;
   } else {
     bool is_atom = !flags.is_ignore_case();
-    for (int i = 0; is_atom && i < pattern->length(); i++) {
-      if (is_reg_exp_special_char.get(pattern->Get(i)))
+    for (int i = 0; is_atom && i < pattern->length(shape); i++) {
+      if (is_reg_exp_special_char.get(pattern->Get(shape, i)))
         is_atom = false;
     }
     if (is_atom) {
index 25fa5e59a1b3b69d86bc2d5ed544f53ee24a9efb..c7b1539fe7dc9ba192ed948222f062d37d859b83 100644 (file)
@@ -350,11 +350,12 @@ void Logger::SharedLibraryEvent(const wchar_t* library_path,
 
 #ifdef ENABLE_LOGGING_AND_PROFILING
 void Logger::LogString(Handle<String> str) {
-  int len = str->length();
+  StringShape shape(*str);
+  int len = str->length(shape);
   if (len > 256)
     len = 256;
   for (int i = 0; i < len; i++) {
-    uc32 c = str->Get(i);
+    uc32 c = str->Get(shape, i);
     if (c < 32 || (c > 126 && c <= 255)) {
       fprintf(logfile_, "\\x%02x", c);
     } else if (c > 255) {
@@ -430,7 +431,8 @@ void Logger::RegExpExecEvent(Handle<JSRegExp> 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
 }
 
index d67544e7be9d191ceedf2694aad46614abeb1b54..f2dd3b5f55bc56f6fb7a78c56d356f9aa831552d 100644 (file)
@@ -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("\"");
 }
 
 
index 4d37a6db4d531b2fd06be4c78a0f02bc801b0599..0532be3aca48723c09f66c1e58c1dd1b5b573983 100644 (file)
@@ -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<uint32_t>(t))
+#ifdef DEBUG
+    , valid_(true)
+#endif  // def DEBUG
+  {
+  ASSERT((type_ & kIsNotStringMask) == kStringTag);
 }
 
 
-bool Object::IsShortString() {
-  return IsString() && (String::cast(this)->size_tag() == kShortStringTag);
+bool StringShape::IsSymbol() {
+  ASSERT(valid());
+  return (type_ & kIsSymbolMask) == kSymbolTag;
 }
 
 
-bool Object::IsMediumString() {
-  return IsString() && (String::cast(this)->size_tag() == kMediumStringTag);
+bool StringShape::IsAsciiRepresentation() {
+  return (type_ & kStringEncodingMask) == kAsciiStringTag;
 }
 
 
-bool Object::IsLongString() {
-  return IsString() && (String::cast(this)->size_tag() == kLongStringTag);
+bool StringShape::IsTwoByteRepresentation() {
+  return (type_ & kStringEncodingMask) == kTwoByteStringTag;
 }
 
 
-bool Object::IsSymbol() {
-  return IsString() && (String::cast(this)->is_symbol());
+bool StringShape::IsCons() {
+  return (type_ & kStringRepresentationMask) == kConsStringTag;
+}
+
+
+bool StringShape::IsSliced() {
+  return (type_ & kStringRepresentationMask) == kSlicedStringTag;
+}
+
+
+bool StringShape::IsExternal() {
+  return (type_ & kStringRepresentationMask) == kExternalStringTag;
+}
+
+
+bool StringShape::IsSequential() {
+  return (type_ & kStringRepresentationMask) == kSeqStringTag;
+}
+
+
+StringRepresentationTag StringShape::representation_tag() {
+  uint32_t tag = (type_ & kStringRepresentationMask);
+  return static_cast<StringRepresentationTag>(tag);
+}
+
+
+uint32_t StringShape::full_representation_tag() {
+  return (type_ & (kStringRepresentationMask | kStringEncodingMask));
+}
+
+
+uint32_t StringShape::size_tag() {
+  return (type_ & kStringSizeMask);
+}
+
+
+bool StringShape::IsSequentialAscii() {
+  return full_representation_tag() == (kSeqStringTag | kAsciiStringTag);
+}
+
+
+bool StringShape::IsSequentialTwoByte() {
+  return (type_ & (kStringRepresentationMask | kStringEncodingMask)) ==
+         (kSeqStringTag | kTwoByteStringTag);
+}
+
+
+bool StringShape::IsExternalAscii() {
+  return full_representation_tag() == (kExternalStringTag | kAsciiStringTag);
+}
+
+
+bool StringShape::IsExternalTwoByte() {
+  return (type_ & (kStringRepresentationMask | kStringEncodingMask)) ==
+         (kExternalStringTag | kTwoByteStringTag);
 }
 
 
@@ -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<StringRepresentationTag>(tag);
-}
-
-
-bool String::IsFlat() {
-  switch (this->representation_tag()) {
-    case kConsStringTag:
+bool String::IsFlat(StringShape shape) {
+  ASSERT(shape.type() == StringShape(this).type());
+  switch (shape.representation_tag()) {
+    case kConsStringTag: {
+      String* second = ConsString::cast(this)->second();
       // Only flattened strings have second part empty.
-      return String::cast(ConsString::cast(this)->second())->length() == 0;
+      return second->length() == 0;
+    }
     case kSlicedStringTag: {
-      String* slice = String::cast(SlicedString::cast(this)->buffer());
-      StringRepresentationTag tag = slice->representation_tag();
+      StringShape slice_shape = StringShape(SlicedString::cast(this)->buffer());
+      StringRepresentationTag tag = slice_shape.representation_tag();
       return tag == kSeqStringTag || tag == kExternalStringTag;
     }
     default:
@@ -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);
 }
index c78c65a7354bb279103a54f69b33350c05b395ea..23c80616e14cd6893fbb1929a077eccfdf453f06 100644 (file)
@@ -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("<Very long string[%u]>", len);
     return;
@@ -659,7 +668,7 @@ void String::StringShortPrint(StringStream* accumulator) {
   }
   buf.Reset(this);
   if (ascii) {
-    accumulator->Add("<String[%u]: ", length());
+    accumulator->Add("<String[%u]: ", length(shape));
     for (int i = 0; i < len; i++) {
       accumulator->Put(buf.GetNext());
     }
@@ -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("<String[%u]\\: ", length());
+    accumulator->Add("<String[%u]\\: ", length(shape));
     for (int i = 0; i < len; i++) {
       int c = buf.GetNext();
       if (c == '\n') {
@@ -845,13 +854,13 @@ int HeapObject::SlowSizeFromMap(Map* map) {
   InstanceType instance_type = map->instance_type();
 
   if (instance_type < FIRST_NONSTRING_TYPE
-      && (reinterpret_cast<String*>(this)->map_representation_tag(map)
-          == kSeqStringTag)) {
-    if (reinterpret_cast<String*>(this)->is_ascii_representation_map(map)) {
-      return reinterpret_cast<SeqAsciiString*>(this)->SeqAsciiStringSize(map);
+      && (StringShape(instance_type).IsSequential())) {
+    StringShape shape(instance_type);
+    if (shape.IsAsciiRepresentation()) {
+      return reinterpret_cast<SeqAsciiString*>(this)->SeqAsciiStringSize(shape);
     } else {
       SeqTwoByteString* self = reinterpret_cast<SeqTwoByteString*>(this);
-      return self->SeqTwoByteStringSize(map);
+      return self->SeqTwoByteStringSize(shape);
     }
   }
 
@@ -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<StringInputBuffer> 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<StringInputBuffer> buffer(&string_input_buffer);
   buffer->Reset(0, this);
   int result = 0;
@@ -3025,23 +3028,26 @@ int String::Utf8Length() {
 
 
 Vector<const char> String::ToAsciiVector() {
-  ASSERT(IsAsciiRepresentation());
-  ASSERT(IsFlat());
+  StringShape shape(this);
+  ASSERT(shape.IsAsciiRepresentation());
+  ASSERT(IsFlat(shape));
 
   int offset = 0;
-  int length = this->length();
-  StringRepresentationTag string_tag = representation_tag();
+  int length = this->length(shape);
+  StringRepresentationTag string_tag = shape.representation_tag();
   String* string = this;
   if (string_tag == kSlicedStringTag) {
     SlicedString* sliced = SlicedString::cast(string);
     offset += sliced->start();
-    string = String::cast(sliced->buffer());
-    string_tag = string->representation_tag();
+    string = sliced->buffer();
+    shape = StringShape(string);
+    string_tag = shape.representation_tag();
   } else if (string_tag == kConsStringTag) {
     ConsString* cons = ConsString::cast(string);
-    ASSERT(String::cast(cons->second())->length() == 0);
-    string = String::cast(cons->first());
-    string_tag = string->representation_tag();
+    ASSERT(cons->second()->length(StringShape(cons->second())) == 0);
+    string = cons->first();
+    shape = StringShape(string);
+    string_tag = shape.representation_tag();
   }
   if (string_tag == kSeqStringTag) {
     SeqAsciiString* seq = SeqAsciiString::cast(string);
@@ -3056,23 +3062,26 @@ Vector<const char> String::ToAsciiVector() {
 
 
 Vector<const uc16> String::ToUC16Vector() {
-  ASSERT(IsTwoByteStringRepresentation());
-  ASSERT(IsFlat());
+  StringShape shape(this);
+  ASSERT(shape.IsTwoByteRepresentation());
+  ASSERT(IsFlat(shape));
 
   int offset = 0;
-  int length = this->length();
-  StringRepresentationTag string_tag = representation_tag();
+  int length = this->length(shape);
+  StringRepresentationTag string_tag = shape.representation_tag();
   String* string = this;
   if (string_tag == kSlicedStringTag) {
     SlicedString* sliced = SlicedString::cast(string);
     offset += sliced->start();
     string = String::cast(sliced->buffer());
-    string_tag = string->representation_tag();
+    shape = StringShape(string);
+    string_tag = shape.representation_tag();
   } else if (string_tag == kConsStringTag) {
     ConsString* cons = ConsString::cast(string);
-    ASSERT(String::cast(cons->second())->length() == 0);
-    string = String::cast(cons->first());
-    string_tag = string->representation_tag();
+    ASSERT(cons->second()->length(StringShape(cons->second())) == 0);
+    string = cons->first();
+    shape = StringShape(string);
+    string_tag = shape.representation_tag();
   }
   if (string_tag == kSeqStringTag) {
     SeqTwoByteString* seq = SeqTwoByteString::cast(string);
@@ -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<unsigned>(input->length()));
+  ASSERT(rbb.remaining <= static_cast<unsigned>(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<String> input(raw_input);
-  ASSERT(*offset_ptr <= (unsigned)input->length());
-  unsigned chars = input->length() - *offset_ptr;
+  ASSERT(*offset_ptr <= (unsigned)input->length(shape));
+  unsigned chars = input->length(shape) - *offset_ptr;
   if (chars > capacity) chars = capacity;
   ReadBlockBuffer rbb(util_buffer, 0, capacity, 0);
   ReadBlockIntoBuffer(*input, &rbb, offset_ptr, chars);
-  ASSERT(rbb.remaining <= static_cast<unsigned>(input->length()));
+  ASSERT(rbb.remaining <= static_cast<unsigned>(input->length(shape)));
   *remaining = rbb.remaining;
   return rbb.util_buffer;
 }
@@ -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 <typename sinkchar>
 void String::WriteToFlat(String* src,
+                         StringShape src_shape,
                          sinkchar* sink,
                          int f,
                          int t) {
   String* source = src;
+  StringShape shape = src_shape;
   int from = f;
   int to = t;
   while (true) {
-    ASSERT(0 <= from && from <= to && to <= source->length());
-    switch (source->full_representation_tag()) {
+    ASSERT(0 <= from && from <= to && to <= source->length(shape));
+    switch (shape.full_representation_tag()) {
       case kAsciiStringTag | kExternalStringTag: {
         CopyChars(sink,
                   ExternalAsciiString::cast(source)->resource()->data() + from,
@@ -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 <typename IteratorA>
 static inline bool CompareStringContentsPartial(IteratorA* ia, String* b) {
-  if (b->IsFlat()) {
-    if (b->IsAsciiRepresentation()) {
+  StringShape b_shape(b);
+  if (b->IsFlat(b_shape)) {
+    if (b_shape.IsAsciiRepresentation()) {
       VectorIterator<char> ib(b->ToAsciiVector());
       return CompareStringContents(ia, &ib);
     } else {
@@ -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<const char>(str1, len),
                                     Vector<const char>(str2, len));
   }
 
-  if (this->IsFlat()) {
-    if (this->IsAsciiRepresentation()) {
+  if (this->IsFlat(this_shape)) {
+    if (this_shape.IsAsciiRepresentation()) {
       Vector<const char> vec1 = this->ToAsciiVector();
-      if (other->IsFlat()) {
-        if (other->IsAsciiRepresentation()) {
+      if (other->IsFlat(other_shape)) {
+        if (other_shape.IsAsciiRepresentation()) {
           Vector<const char> vec2 = other->ToAsciiVector();
           return CompareRawStringContents(vec1, vec2);
         } else {
@@ -3919,8 +3952,8 @@ bool String::SlowEquals(String* other) {
       }
     } else {
       Vector<const uc16> vec1 = this->ToUC16Vector();
-      if (other->IsFlat()) {
-        if (other->IsAsciiRepresentation()) {
+      if (other->IsFlat(other_shape)) {
+        if (other_shape.IsAsciiRepresentation()) {
           VectorIterator<uc16> buf1(vec1);
           VectorIterator<char> ib(other->ToAsciiVector());
           return CompareStringContents(&buf1, &ib);
@@ -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<const char> str) {
-  int slen = length();
+  StringShape this_shape(this);
+  int slen = length(this_shape);
   Access<Scanner::Utf8Decoder> decoder(Scanner::utf8_decoder());
   decoder->Reset(str.start(), str.length());
   int i;
   for (i = 0; i < slen && decoder->has_more(); i++) {
     uc32 r = decoder->GetNext();
-    if (Get(i) != r) return false;
+    if (Get(this_shape, i) != r) return false;
   }
   return i == slen && !decoder->has_more();
 }
@@ -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;
   }
index f25a259554afed957b08ed947335f1e34d5d2ff8..2fcf131b64d6bb794b6d542e43351cee18f9dedc 100644 (file)
@@ -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<const char> ToAsciiVector();
   Vector<const uc16> 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 <typename sinkchar>
   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();
index 9090dbb3eea8f5f8ee9626922fdb26528b73615c..ef92e3c2886ac4f76ab60c5cc8a8ecc976f9b03c 100644 (file)
@@ -737,10 +737,11 @@ FunctionLiteral* Parser::ParseProgram(Handle<String> 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<String> 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<String> 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;
index ecaa7c5523bd6d6ed1a0954b6082d27824cf5b88..39af187ff600cde68a8691544c60b4b0e08c7dba 100644 (file)
@@ -503,9 +503,10 @@ void PrettyPrinter::PrintLiteral(Handle<Object> 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()) {
index dc72bb3ad85f8651a723deff1404809dda0a0df5..9af6cbb43b5ae7373425ef10dde549b464a38187 100644 (file)
@@ -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);
index a1d6a9d01bce9a354efe799128b3079ae6f4748f..7dc2871d27fe79d9e5d7316d7fd7449dce0f3699 100644 (file)
@@ -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<uint32_t>(subject->length())) return Heap::nan_value();
-  return Smi::FromInt(subject->Get(i));
+  StringShape shape(subject);
+  subject->TryFlatten(shape);  // shape no longer valid!
+  if (i >= static_cast<uint32_t>(subject->length(StringShape(subject)))) {
+    return Heap::nan_value();
+  }
+  return Smi::FromInt(subject->Get(StringShape(subject), i));
 }
 
 
@@ -1351,39 +1354,51 @@ int Runtime::StringMatch(Handle<String> sub,
                          Handle<String> pat,
                          int start_index) {
   ASSERT(0 <= start_index);
-  ASSERT(start_index <= sub->length());
+  StringShape sub_shape(*sub);
+  StringShape pat_shape(*pat);
+  ASSERT(start_index <= sub->length(sub_shape));
 
-  int pattern_length = pat->length();
+  int pattern_length = pat->length(pat_shape);
   if (pattern_length == 0) return start_index;
 
-  int subject_length = sub->length();
+  int subject_length = sub->length(sub_shape);
   if (start_index + pattern_length > subject_length) return -1;
 
-  FlattenString(sub);
+  if (!sub->IsFlat(sub_shape)) {
+    FlattenString(sub);
+    sub_shape = StringShape(*sub);
+  }
   // Searching for one specific character is common.  For one
   // character patterns linear search is necessary, so any smart
   // algorithm is unnecessary overhead.
   if (pattern_length == 1) {
     AssertNoAllocation no_heap_allocation;  // ensure vectors stay valid
-    if (sub->is_ascii_representation()) {
-      return SingleCharIndexOf(sub->ToAsciiVector(), pat->Get(0), start_index);
+    if (sub_shape.IsAsciiRepresentation()) {
+      return SingleCharIndexOf(sub->ToAsciiVector(),
+                               pat->Get(pat_shape, 0),
+                               start_index);
     }
-    return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
+    return SingleCharIndexOf(sub->ToUC16Vector(),
+                             pat->Get(pat_shape, 0),
+                             start_index);
   }
 
-  FlattenString(pat);
+  if (!pat->IsFlat(pat_shape)) {
+    FlattenString(pat);
+    pat_shape = StringShape(*pat);
+  }
 
   AssertNoAllocation no_heap_allocation;  // ensure vectors stay valid
   // dispatch on type of strings
-  if (pat->is_ascii_representation()) {
+  if (pat_shape.IsAsciiRepresentation()) {
     Vector<const char> pat_vector = pat->ToAsciiVector();
-    if (sub->is_ascii_representation()) {
+    if (sub_shape.IsAsciiRepresentation()) {
       return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
     }
     return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
   }
   Vector<const uc16> pat_vector = pat->ToUC16Vector();
-  if (sub->is_ascii_representation()) {
+  if (sub_shape.IsAsciiRepresentation()) {
     return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
   }
   return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
@@ -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<Object> GetCharAt(Handle<String> string, uint32_t index) {
-  if (index < static_cast<uint32_t>(string->length())) {
-    string->TryFlatten();
-    return LookupSingleCharacterStringFromCode(string->Get(index));
+  StringShape shape(*string);
+  if (index < static_cast<uint32_t>(string->length(shape))) {
+    string->TryFlatten(shape);  // Invalidates shape!
+    return LookupSingleCharacterStringFromCode(
+        string->Get(StringShape(*string), index));
   }
   return Execution::CharAt(string, index);
 }
@@ -1793,7 +1817,7 @@ Object* Runtime::SetObjectProperty(Handle<Object> object,
       result = SetElement(js_object, index, value);
     } else {
       Handle<String> key_string = Handle<String>::cast(key);
-      key_string->TryFlatten();
+      key_string->TryFlatten(StringShape(*key_string));
       result = SetProperty(js_object, key_string, value, attr);
     }
     if (result.is_null()) return Failure::Exception();
@@ -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<uint32_t>(string->length()))
+      StringShape shape(string);
+      if (index < static_cast<uint32_t>(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<StringInputBuffer> buffer(&string_input_buffer);
   buffer->Rewind();
   while (buffer->has_more()) {
-    uint16_t character = buffer->GetNext();
-    if (character >= 256) {
-      destination->Set(dest_position, '%');
-      destination->Set(dest_position+1, 'u');
-      destination->Set(dest_position+2, hex_chars[character >> 12]);
-      destination->Set(dest_position+3, hex_chars[(character >> 8) & 0xf]);
-      destination->Set(dest_position+4, hex_chars[(character >> 4) & 0xf]);
-      destination->Set(dest_position+5, hex_chars[character & 0xf]);
+    uint16_t chr = buffer->GetNext();
+    if (chr >= 256) {
+      destination->Set(dshape, dest_position, '%');
+      destination->Set(dshape, dest_position+1, 'u');
+      destination->Set(dshape, dest_position+2, hex_chars[chr >> 12]);
+      destination->Set(dshape, dest_position+3, hex_chars[(chr >> 8) & 0xf]);
+      destination->Set(dshape, dest_position+4, hex_chars[(chr >> 4) & 0xf]);
+      destination->Set(dshape, dest_position+5, hex_chars[chr & 0xf]);
       dest_position += 6;
-    } else if (IsNotEscaped(character)) {
-      destination->Set(dest_position, character);
+    } else if (IsNotEscaped(chr)) {
+      destination->Set(dshape, dest_position, chr);
       dest_position++;
     } else {
-      destination->Set(dest_position, '%');
-      destination->Set(dest_position+1, hex_chars[character >> 4]);
-      destination->Set(dest_position+2, hex_chars[character & 0xf]);
+      destination->Set(dshape, dest_position, '%');
+      destination->Set(dshape, dest_position+1, hex_chars[chr >> 4]);
+      destination->Set(dshape, dest_position+2, hex_chars[chr & 0xf]);
       dest_position += 3;
     }
   }
@@ -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<uc32>(chars[0]) != current);
-      result->Set(i, chars[0]);
+      result->Set(result_shape, i, chars[0]);
       has_changed_character = true;
       i++;
     } else if (length == raw_string_length) {
@@ -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<typename sinkchar>
 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;
index 0a4f90f61642e5ca144c5b13689ef98188d0d27e..4ac1ae4a52ba40afb1aa9f27fe16d54ba1f9df70 100644 (file)
@@ -248,9 +248,10 @@ TEST(GarbageCollection) {
 
 static void VerifyStringAllocation(const char* string) {
   String* s = String::cast(Heap::AllocateStringFromUtf8(CStrVector(string)));
-  CHECK_EQ(static_cast<int>(strlen(string)), s->length());
-  for (int index = 0; index < s->length(); index++) {
-    CHECK_EQ(static_cast<uint16_t>(string[index]), s->Get(index));  }
+  StringShape shape(s);
+  CHECK_EQ(static_cast<int>(strlen(string)), s->length(shape));
+  for (int index = 0; index < s->length(shape); index++) {
+    CHECK_EQ(static_cast<uint16_t>(string[index]), s->Get(shape, index));  }
 }
 
 
index 4484dd5c1df19546183a4e38889ad6c68b973902..ec080e245a19f456673f4d323bb94111a7b5d3c1 100644 (file)
@@ -62,7 +62,8 @@ static void InitializeBuildingBlocks(
         building_blocks[i] =
             Factory::NewStringFromTwoByte(Vector<const uc16>(buf, len));
         for (int j = 0; j < len; j++) {
-          CHECK_EQ(buf[j], building_blocks[i]->Get(j));
+          StringShape shape(*building_blocks[i]);
+          CHECK_EQ(buf[j], building_blocks[i]->Get(shape, j));
         }
         break;
       }
@@ -74,7 +75,8 @@ static void InitializeBuildingBlocks(
         building_blocks[i] =
             Factory::NewStringFromAscii(Vector<const char>(buf, len));
         for (int j = 0; j < len; j++) {
-          CHECK_EQ(buf[j], building_blocks[i]->Get(j));
+          StringShape shape(*building_blocks[i]);
+          CHECK_EQ(buf[j], building_blocks[i]->Get(shape, j));
         }
         break;
       }
@@ -99,7 +101,8 @@ static void InitializeBuildingBlocks(
         Resource* resource = new Resource(Vector<const uc16>(buf, len));
         building_blocks[i] = Factory::NewExternalStringFromTwoByte(resource);
         for (int j = 0; j < len; j++) {
-          CHECK_EQ(buf[j], building_blocks[i]->Get(j));
+          StringShape shape(*building_blocks[i]);
+          CHECK_EQ(buf[j], building_blocks[i]->Get(shape, j));
         }
         break;
       }
@@ -111,7 +114,8 @@ static void InitializeBuildingBlocks(
         building_blocks[i] =
             Factory::NewStringFromAscii(Vector<const char>(buf, len));
         for (int j = 0; j < len; j++) {
-          CHECK_EQ(buf[j], building_blocks[i]->Get(j));
+          StringShape shape(*building_blocks[i]);
+          CHECK_EQ(buf[j], building_blocks[i]->Get(shape, j));
         }
         break;
       }
@@ -125,9 +129,11 @@ static Handle<String> ConstructLeft(
     int depth) {
   Handle<String> answer = Factory::NewStringFromAscii(CStrVector(""));
   for (int i = 0; i < depth; i++) {
-    answer =
-        Factory::NewConsString(answer,
-                               building_blocks[i % NUMBER_OF_BUILDING_BLOCKS]);
+    answer = Factory::NewConsString(
+        answer,
+        StringShape(*answer),
+        building_blocks[i % NUMBER_OF_BUILDING_BLOCKS],
+        StringShape(*building_blocks[i % NUMBER_OF_BUILDING_BLOCKS]));
   }
   return answer;
 }
@@ -138,9 +144,11 @@ static Handle<String> ConstructRight(
     int depth) {
   Handle<String> answer = Factory::NewStringFromAscii(CStrVector(""));
   for (int i = depth - 1; i >= 0; i--) {
-    answer =
-        Factory::NewConsString(building_blocks[i % NUMBER_OF_BUILDING_BLOCKS],
-                               answer);
+    answer = Factory::NewConsString(
+        building_blocks[i % NUMBER_OF_BUILDING_BLOCKS],
+        StringShape(*building_blocks[i % NUMBER_OF_BUILDING_BLOCKS]),
+        answer,
+        StringShape(*answer));
   }
   return answer;
 }
@@ -157,11 +165,19 @@ static Handle<String> 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<String> part1 =
+    ConstructBalancedHelper(building_blocks, from, from + ((to - from) / 2));
+  Handle<String> part2 =
+    ConstructBalancedHelper(building_blocks, from + ((to - from) / 2), to);
   return Factory::NewConsString(
-    ConstructBalancedHelper(building_blocks, from, from + ((to - from) / 2)),
-    ConstructBalancedHelper(building_blocks, from + ((to - from) / 2), to));
+      part1,
+      StringShape(*part1),
+      part2,
+      StringShape(*part2));
 }
 
 
@@ -199,8 +215,10 @@ static void TraverseFirst(Handle<String> s1, Handle<String> 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<String> right_deep_slice =
       Factory::NewStringSlice(left_deep_asymmetric,
+                              StringShape(*left_deep_asymmetric),
                               left_deep_asymmetric->length() - 1050,
                               left_deep_asymmetric->length() - 50);
   Handle<String> left_deep_slice =
       Factory::NewStringSlice(right_deep_asymmetric,
+                              StringShape(*right_deep_asymmetric),
                               right_deep_asymmetric->length() - 1050,
                               right_deep_asymmetric->length() - 50);
   printf("8\n");
@@ -262,7 +282,10 @@ TEST(Traverse) {
 static Handle<String> SliceOf(Handle<String> underlying) {
   int start = gen() % underlying->length();
   int end = start + gen() % (underlying->length() - start);
-  return Factory::NewStringSlice(underlying, start, end);
+  return Factory::NewStringSlice(underlying,
+                                 StringShape(*underlying),
+                                 start,
+                                 end);
 }
 
 
@@ -280,11 +303,19 @@ static Handle<String> ConstructSliceTree(
     Handle<String> rhs = building_blocks[(from+1) % NUMBER_OF_BUILDING_BLOCKS];
     if (gen() % 2 == 0)
       rhs = SliceOf(rhs);
-    return Factory::NewConsString(lhs, rhs);
+    return Factory::NewConsString(lhs,
+                                  StringShape(*lhs),
+                                  rhs,
+                                  StringShape(*rhs));
   }
-  Handle<String> branch = Factory::NewConsString(
-    ConstructBalancedHelper(building_blocks, from, from + ((to - from) / 2)),
-    ConstructBalancedHelper(building_blocks, from + ((to - from) / 2), to));
+  Handle<String> part1 =
+    ConstructBalancedHelper(building_blocks, from, from + ((to - from) / 2));
+  Handle<String> part2 =
+    ConstructBalancedHelper(building_blocks, from + ((to - from) / 2), to);
+  Handle<String> branch = Factory::NewConsString(part1,
+                                                 StringShape(*part1),
+                                                 part2,
+                                                 StringShape(*part2));
   if (gen() % 2 == 0)
     return branch;
   return(SliceOf(branch));
@@ -324,9 +355,15 @@ TEST(DeepAscii) {
       Factory::NewStringFromAscii(Vector<const char>(foo, DEEP_ASCII_DEPTH));
   Handle<String> foo_string = Factory::NewStringFromAscii(CStrVector("foo"));
   for (int i = 0; i < DEEP_ASCII_DEPTH; i += 10) {
-    string = Factory::NewConsString(string, foo_string);
+    string = Factory::NewConsString(string,
+                                    StringShape(*string),
+                                    foo_string,
+                                    StringShape(*foo_string));
   }
-  Handle<String> flat_string = Factory::NewConsString(string, foo_string);
+  Handle<String> flat_string = Factory::NewConsString(string,
+                                                      StringShape(*string),
+                                                      foo_string,
+                                                      StringShape(*foo_string));
   FlattenString(flat_string);
 
   for (int i = 0; i < 500; i++) {