When strings can change from an ASCII representation to a
authorerik.corry@gmail.com <erik.corry@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 1 May 2009 11:16:29 +0000 (11:16 +0000)
committererik.corry@gmail.com <erik.corry@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 1 May 2009 11:16:29 +0000 (11:16 +0000)
UC16 representation we need to be careful about flat strings.
Flat strings can be sliced or cons strings that have a flat
string under them, so when we ask a flat cons or a slice whether
it is ASCII or not we should ask the underlying string about
its representation.  This should fix
http://code.google.com/p/chromium/issues/detail?id=10971
Review URL: http://codereview.chromium.org/100249

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1830 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/heap.cc
src/ia32/regexp-macro-assembler-ia32.cc
src/interpreter-irregexp.cc
src/jsregexp.cc
src/log.cc
src/objects-inl.h
src/objects.cc
src/objects.h
src/runtime.cc
test/cctest/test-api.cc

index 0775a5dfebfde004e819eeb010da72afad99f386..fa225f7a5ab084309c17fc9ab9209a71bad66c87 100644 (file)
@@ -1423,8 +1423,8 @@ Object* Heap::AllocateConsString(String* first,
   int first_length = first->length();
   int second_length = second->length();
   int length = first_length + second_length;
-  bool is_ascii = StringShape(first).IsAsciiRepresentation()
-      && StringShape(second).IsAsciiRepresentation();
+  bool is_ascii = first->IsAsciiRepresentation()
+      && second->IsAsciiRepresentation();
 
   // If the resulting string is small make a flat string.
   if (length < String::kMinNonFlatLength) {
@@ -1484,15 +1484,15 @@ Object* Heap::AllocateSlicedString(String* buffer,
 
   Map* map;
   if (length <= String::kMaxShortStringSize) {
-    map = StringShape(buffer).IsAsciiRepresentation() ?
+    map = buffer->IsAsciiRepresentation() ?
       short_sliced_ascii_string_map() :
       short_sliced_string_map();
   } else if (length <= String::kMaxMediumStringSize) {
-    map = StringShape(buffer).IsAsciiRepresentation() ?
+    map = buffer->IsAsciiRepresentation() ?
       medium_sliced_ascii_string_map() :
       medium_sliced_string_map();
   } else {
-    map = StringShape(buffer).IsAsciiRepresentation() ?
+    map = buffer->IsAsciiRepresentation() ?
       long_sliced_ascii_string_map() :
       long_sliced_string_map();
   }
@@ -1524,7 +1524,7 @@ Object* Heap::AllocateSubString(String* buffer,
     buffer->TryFlatten();
   }
 
-  Object* result = StringShape(buffer).IsAsciiRepresentation()
+  Object* result = buffer->IsAsciiRepresentation()
       ? AllocateRawAsciiString(length)
       : AllocateRawTwoByteString(length);
   if (result->IsFailure()) return result;
index 83dac5469f49e6b56d2e419764c3e2a03e9d712b..2a0cefd7769341b51db5a7af5f202902c44f2d41 100644 (file)
@@ -974,7 +974,7 @@ RegExpMacroAssemblerIA32::Result RegExpMacroAssemblerIA32::Match(
   int start_offset = previous_index;
   int end_offset = subject_ptr->length();
 
-  bool is_ascii = StringShape(*subject).IsAsciiRepresentation();
+  bool is_ascii = subject->IsAsciiRepresentation();
 
   if (StringShape(subject_ptr).IsCons()) {
     subject_ptr = ConsString::cast(subject_ptr)->first();
@@ -985,7 +985,7 @@ RegExpMacroAssemblerIA32::Result RegExpMacroAssemblerIA32::Match(
     subject_ptr = slice->buffer();
   }
   // Ensure that an underlying string has the same ascii-ness.
-  ASSERT(StringShape(subject_ptr).IsAsciiRepresentation() == is_ascii);
+  ASSERT(subject_ptr->IsAsciiRepresentation() == is_ascii);
   ASSERT(subject_ptr->IsExternalString() || subject_ptr->IsSeqString());
   // String is now either Sequential or External
   int char_size_shift = is_ascii ? 0 : 1;
@@ -1112,7 +1112,7 @@ const byte* RegExpMacroAssemblerIA32::StringCharacterPosition(String* subject,
   ASSERT(subject->IsExternalString() || subject->IsSeqString());
   ASSERT(start_index >= 0);
   ASSERT(start_index <= subject->length());
-  if (StringShape(subject).IsAsciiRepresentation()) {
+  if (subject->IsAsciiRepresentation()) {
     const byte* address;
     if (StringShape(subject).IsExternal()) {
       const char* data = ExternalAsciiString::cast(subject)->resource()->data();
@@ -1152,7 +1152,7 @@ int RegExpMacroAssemblerIA32::CheckStackGuardState(Address* return_address,
 
   Handle<String> subject(frame_entry<String*>(re_frame, kInputString));
   // Current string.
-  bool is_ascii = StringShape(*subject).IsAsciiRepresentation();
+  bool is_ascii = subject->IsAsciiRepresentation();
 
   ASSERT(re_code->instruction_start() <= *return_address);
   ASSERT(*return_address <=
@@ -1171,7 +1171,7 @@ int RegExpMacroAssemblerIA32::CheckStackGuardState(Address* return_address,
   }
 
   // String might have changed.
-  if (StringShape(*subject).IsAsciiRepresentation() != is_ascii) {
+  if (subject->IsAsciiRepresentation() != is_ascii) {
     // If we changed between an ASCII and an UC16 string, the specialized
     // code cannot be used, and we need to restart regexp matching from
     // scratch (including, potentially, compiling a new version of the code).
index 1cc5c5a1d65cb11f466055ac50e3bff59fe0810d..70680a861e47953cb568c41c44d87d00f9e02357 100644 (file)
@@ -574,7 +574,7 @@ bool IrregexpInterpreter::Match(Handle<ByteArray> code_array,
   AssertNoAllocation a;
   const byte* code_base = code_array->GetDataStartAddress();
   uc16 previous_char = '\n';
-  if (StringShape(*subject).IsAsciiRepresentation()) {
+  if (subject->IsAsciiRepresentation()) {
     Vector<const char> subject_vector = subject->ToAsciiVector();
     if (start_position != 0) previous_char = subject_vector[start_position - 1];
     return RawMatch(code_base,
index 58d556eb5d1e69c4d098d71eff1bc59aeda69d76..1757f5241c5cc1a37cbc70f2c1471d016df392c8 100644 (file)
@@ -440,7 +440,7 @@ Handle<Object> RegExpImpl::IrregexpExec(Handle<JSRegExp> jsregexp,
 #ifdef V8_ARCH_IA32
     RegExpMacroAssemblerIA32::Result res;
     do {
-      bool is_ascii = StringShape(*subject).IsAsciiRepresentation();
+      bool is_ascii = subject->IsAsciiRepresentation();
       if (!EnsureCompiledIrregexp(jsregexp, is_ascii)) {
         return Handle<Object>::null();
       }
@@ -463,7 +463,7 @@ Handle<Object> RegExpImpl::IrregexpExec(Handle<JSRegExp> jsregexp,
     rc = (res == RegExpMacroAssemblerIA32::SUCCESS);
 #endif
   } else {
-    bool is_ascii = StringShape(*subject).IsAsciiRepresentation();
+    bool is_ascii = subject->IsAsciiRepresentation();
     if (!EnsureCompiledIrregexp(jsregexp, is_ascii)) {
       return Handle<Object>::null();
     }
index f23b73b2f802d59714ac6720e2615dcf272b8bc4..4d4dfa70e5f47a61e8f92fa558c5665862ef3c7c 100644 (file)
@@ -366,7 +366,7 @@ void LogMessageBuilder::AppendDetailed(String* str, bool show_impl_info) {
   if (len > 0x1000)
     len = 0x1000;
   if (show_impl_info) {
-    Append(StringShape(str).IsAsciiRepresentation() ? 'a' : '2');
+    Append(str->IsAsciiRepresentation() ? 'a' : '2');
     if (StringShape(str).IsExternal())
       Append('e');
     if (StringShape(str).IsSymbol())
index ff64d65084d3d1d3c7e7e4d29b349aae5afdd542..c1d01824be5aba6244ec0b3355f4363a9210118f 100644 (file)
@@ -144,14 +144,14 @@ bool Object::IsSeqString() {
 bool Object::IsSeqAsciiString() {
   if (!IsString()) return false;
   return StringShape(String::cast(this)).IsSequential() &&
-         StringShape(String::cast(this)).IsAsciiRepresentation();
+         String::cast(this)->IsAsciiRepresentation();
 }
 
 
 bool Object::IsSeqTwoByteString() {
   if (!IsString()) return false;
   return StringShape(String::cast(this)).IsSequential() &&
-         StringShape(String::cast(this)).IsTwoByteRepresentation();
+         String::cast(this)->IsTwoByteRepresentation();
 }
 
 
@@ -164,14 +164,14 @@ bool Object::IsExternalString() {
 bool Object::IsExternalAsciiString() {
   if (!IsString()) return false;
   return StringShape(String::cast(this)).IsExternal() &&
-         StringShape(String::cast(this)).IsAsciiRepresentation();
+         String::cast(this)->IsAsciiRepresentation();
 }
 
 
 bool Object::IsExternalTwoByteString() {
   if (!IsString()) return false;
   return StringShape(String::cast(this)).IsExternal() &&
-         StringShape(String::cast(this)).IsTwoByteRepresentation();
+         String::cast(this)->IsTwoByteRepresentation();
 }
 
 
@@ -211,13 +211,28 @@ bool StringShape::IsSymbol() {
 }
 
 
-bool StringShape::IsAsciiRepresentation() {
-  return (type_ & kStringEncodingMask) == kAsciiStringTag;
+bool String::IsAsciiRepresentation() {
+  uint32_t type = map()->instance_type();
+  if ((type & kStringRepresentationMask) == kSlicedStringTag) {
+    return SlicedString::cast(this)->buffer()->IsAsciiRepresentation();
+  }
+  if ((type & kStringRepresentationMask) == kConsStringTag &&
+      ConsString::cast(this)->second()->length() == 0) {
+    return ConsString::cast(this)->first()->IsAsciiRepresentation();
+  }
+  return (type & kStringEncodingMask) == kAsciiStringTag;
 }
 
 
-bool StringShape::IsTwoByteRepresentation() {
-  return (type_ & kStringEncodingMask) == kTwoByteStringTag;
+bool String::IsTwoByteRepresentation() {
+  uint32_t type = map()->instance_type();
+  if ((type & kStringRepresentationMask) == kSlicedStringTag) {
+    return SlicedString::cast(this)->buffer()->IsTwoByteRepresentation();
+  } else if ((type & kStringRepresentationMask) == kConsStringTag &&
+             ConsString::cast(this)->second()->length() == 0) {
+    return ConsString::cast(this)->first()->IsTwoByteRepresentation();
+  }
+  return (type & kStringEncodingMask) == kTwoByteStringTag;
 }
 
 
@@ -1476,7 +1491,7 @@ void String::Set(int index, uint16_t value) {
   ASSERT(index >= 0 && index < length());
   ASSERT(StringShape(this).IsSequential());
 
-  return StringShape(this).IsAsciiRepresentation()
+  return this->IsAsciiRepresentation()
       ? SeqAsciiString::cast(this)->SeqAsciiStringSet(index, value)
       : SeqTwoByteString::cast(this)->SeqTwoByteStringSet(index, value);
 }
@@ -1576,11 +1591,6 @@ int SeqAsciiString::SeqAsciiStringSize(InstanceType instance_type) {
 
 
 String* ConsString::first() {
-  ASSERT(String::cast(READ_FIELD(this, kSecondOffset))->length() != 0 ||
-      StringShape(
-          String::cast(
-              READ_FIELD(this, kFirstOffset))).IsAsciiRepresentation()
-          == StringShape(this).IsAsciiRepresentation());
   return String::cast(READ_FIELD(this, kFirstOffset));
 }
 
@@ -1613,10 +1623,6 @@ void ConsString::set_second(String* value, WriteBarrierMode mode) {
 
 
 String* SlicedString::buffer() {
-  ASSERT(
-      StringShape(
-          String::cast(READ_FIELD(this, kBufferOffset))).IsAsciiRepresentation()
-      == StringShape(this).IsAsciiRepresentation());
   return String::cast(READ_FIELD(this, kBufferOffset));
 }
 
index 4d01e855aa47ad0f9bb8e01f41bbaa30f50d9362..80977c1db30e1e8e9cbf9f213fee2f1369cf52ab 100644 (file)
@@ -602,8 +602,6 @@ Object* String::TryFlatten() {
       if (StringShape(String::cast(ok)).IsCons()) {
         ss->set_buffer(ConsString::cast(ok)->first());
       }
-      ASSERT(StringShape(this).IsAsciiRepresentation() ==
-          StringShape(ss->buffer()).IsAsciiRepresentation());
       return this;
     }
     case kConsStringTag: {
@@ -618,7 +616,7 @@ Object* String::TryFlatten() {
       int len = length();
       Object* object;
       String* result;
-      if (StringShape(this).IsAsciiRepresentation()) {
+      if (IsAsciiRepresentation()) {
         object = Heap::AllocateRawAsciiString(len, tenure);
         if (object->IsFailure()) return object;
         result = String::cast(object);
@@ -956,10 +954,11 @@ int HeapObject::SlowSizeFromMap(Map* map) {
   // Avoid calling functions such as FixedArray::cast during GC, which
   // read map pointer of this object again.
   InstanceType instance_type = map->instance_type();
+  uint32_t type = static_cast<uint32_t>(instance_type);
 
   if (instance_type < FIRST_NONSTRING_TYPE
       && (StringShape(instance_type).IsSequential())) {
-    if (StringShape(instance_type).IsAsciiRepresentation()) {
+    if ((type & kStringEncodingMask) == kAsciiStringTag) {
       SeqAsciiString* seq_ascii_this = reinterpret_cast<SeqAsciiString*>(this);
       return seq_ascii_this->SeqAsciiStringSize(instance_type);
     } else {
@@ -3235,7 +3234,7 @@ bool String::LooksValid() {
 
 
 int String::Utf8Length() {
-  if (StringShape(this).IsAsciiRepresentation()) return length();
+  if (IsAsciiRepresentation()) return length();
   // 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)
@@ -3251,7 +3250,7 @@ int String::Utf8Length() {
 
 
 Vector<const char> String::ToAsciiVector() {
-  ASSERT(StringShape(this).IsAsciiRepresentation());
+  ASSERT(IsAsciiRepresentation());
   ASSERT(IsFlat());
 
   int offset = 0;
@@ -3282,7 +3281,7 @@ Vector<const char> String::ToAsciiVector() {
 
 
 Vector<const uc16> String::ToUC16Vector() {
-  ASSERT(StringShape(this).IsTwoByteRepresentation());
+  ASSERT(IsTwoByteRepresentation());
   ASSERT(IsFlat());
 
   int offset = 0;
@@ -3385,7 +3384,7 @@ const uc16* String::GetTwoByteData() {
 
 
 const uc16* String::GetTwoByteData(unsigned start) {
-  ASSERT(!StringShape(this).IsAsciiRepresentation());
+  ASSERT(!IsAsciiRepresentation());
   switch (StringShape(this).representation_tag()) {
     case kSeqStringTag:
       return SeqTwoByteString::cast(this)->SeqTwoByteStringGetData(start);
@@ -3680,7 +3679,7 @@ const unibrow::byte* String::ReadBlock(String* input,
   }
   switch (StringShape(input).representation_tag()) {
     case kSeqStringTag:
-      if (StringShape(input).IsAsciiRepresentation()) {
+      if (input->IsAsciiRepresentation()) {
         SeqAsciiString* str = SeqAsciiString::cast(input);
         return str->SeqAsciiStringReadBlock(&rbb->remaining,
                                             offset_ptr,
@@ -3701,7 +3700,7 @@ const unibrow::byte* String::ReadBlock(String* input,
                                                               offset_ptr,
                                                               max_chars);
     case kExternalStringTag:
-      if (StringShape(input).IsAsciiRepresentation()) {
+      if (input->IsAsciiRepresentation()) {
         return ExternalAsciiString::cast(input)->ExternalAsciiStringReadBlock(
             &rbb->remaining,
             offset_ptr,
@@ -3754,7 +3753,7 @@ void FlatStringReader::RefreshState() {
   if (str_ == NULL) return;
   Handle<String> str(str_);
   ASSERT(str->IsFlat());
-  is_ascii_ = StringShape(*str).IsAsciiRepresentation();
+  is_ascii_ = str->IsAsciiRepresentation();
   if (is_ascii_) {
     start_ = str->ToAsciiVector().start();
   } else {
@@ -3795,7 +3794,7 @@ void String::ReadBlockIntoBuffer(String* input,
 
   switch (StringShape(input).representation_tag()) {
     case kSeqStringTag:
-      if (StringShape(input).IsAsciiRepresentation()) {
+      if (input->IsAsciiRepresentation()) {
         SeqAsciiString::cast(input)->SeqAsciiStringReadBlockIntoBuffer(rbb,
                                                                  offset_ptr,
                                                                  max_chars);
@@ -3817,7 +3816,7 @@ void String::ReadBlockIntoBuffer(String* input,
                                                                  max_chars);
       return;
     case kExternalStringTag:
-      if (StringShape(input).IsAsciiRepresentation()) {
+      if (input->IsAsciiRepresentation()) {
          ExternalAsciiString::cast(input)->
              ExternalAsciiStringReadBlockIntoBuffer(rbb, offset_ptr, max_chars);
        } else {
@@ -4147,7 +4146,7 @@ static StringInputBuffer string_compare_buffer_b;
 template <typename IteratorA>
 static inline bool CompareStringContentsPartial(IteratorA* ia, String* b) {
   if (b->IsFlat()) {
-    if (StringShape(b).IsAsciiRepresentation()) {
+    if (b->IsAsciiRepresentation()) {
       VectorIterator<char> ib(b->ToAsciiVector());
       return CompareStringContents(ia, &ib);
     } else {
@@ -4187,10 +4186,10 @@ bool String::SlowEquals(String* other) {
   }
 
   if (this->IsFlat()) {
-    if (StringShape(this).IsAsciiRepresentation()) {
+    if (IsAsciiRepresentation()) {
       Vector<const char> vec1 = this->ToAsciiVector();
       if (other->IsFlat()) {
-        if (StringShape(other).IsAsciiRepresentation()) {
+        if (other->IsAsciiRepresentation()) {
           Vector<const char> vec2 = other->ToAsciiVector();
           return CompareRawStringContents(vec1, vec2);
         } else {
@@ -4209,7 +4208,7 @@ bool String::SlowEquals(String* other) {
       Vector<const uc16> vec1 = this->ToUC16Vector();
       if (CheckVectorForBug9746(vec1)) return false;
       if (other->IsFlat()) {
-        if (StringShape(other).IsAsciiRepresentation()) {
+        if (other->IsAsciiRepresentation()) {
           VectorIterator<uc16> buf1(vec1);
           VectorIterator<char> ib(other->ToAsciiVector());
           return CompareStringContents(&buf1, &ib);
index f3580f94bd8595985f75672ab601965e002aee94..1e38383fc7792017665dae7adf37a15805033498 100644 (file)
@@ -3207,8 +3207,6 @@ class StringShape BASE_EMBEDDED {
   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();
@@ -3260,6 +3258,9 @@ class String: public HeapObject {
   inline uint32_t length_field();
   inline void set_length_field(uint32_t value);
 
+  inline bool IsAsciiRepresentation();
+  inline bool IsTwoByteRepresentation();
+
   // Get and set individual two byte chars in the string.
   inline void Set(int index, uint16_t value);
   // Get individual two byte char in the string.  Repeated calls
index 29656ddf647e6b646f0bd98240783254f70a9658..3a738dfc521667a41970f6762cfec8118cd420ae 100644 (file)
@@ -1282,7 +1282,7 @@ class ReplacementStringBuilder {
         parts_(Factory::NewFixedArray(estimated_part_count)),
         part_count_(0),
         character_count_(0),
-        is_ascii_(StringShape(*subject).IsAsciiRepresentation()) {
+        is_ascii_(subject->IsAsciiRepresentation()) {
     // Require a non-zero initial size. Ensures that doubling the size to
     // extend the array will work.
     ASSERT(estimated_part_count > 0);
@@ -1326,7 +1326,7 @@ class ReplacementStringBuilder {
     int length = string->length();
     ASSERT(length > 0);
     AddElement(*string);
-    if (!StringShape(*string).IsAsciiRepresentation()) {
+    if (!string->IsAsciiRepresentation()) {
       is_ascii_ = false;
     }
     IncrementCharacterCount(length);
@@ -1583,14 +1583,14 @@ void CompiledReplacement::Compile(Handle<String> replacement,
                                   int capture_count,
                                   int subject_length) {
   ASSERT(replacement->IsFlat());
-  if (StringShape(*replacement).IsAsciiRepresentation()) {
+  if (replacement->IsAsciiRepresentation()) {
     AssertNoAllocation no_alloc;
     ParseReplacementPattern(&parts_,
                             replacement->ToAsciiVector(),
                             capture_count,
                             subject_length);
   } else {
-    ASSERT(StringShape(*replacement).IsTwoByteRepresentation());
+    ASSERT(replacement->IsTwoByteRepresentation());
     AssertNoAllocation no_alloc;
 
     ParseReplacementPattern(&parts_,
@@ -2165,7 +2165,7 @@ int Runtime::StringMatch(Handle<String> sub,
   // algorithm is unnecessary overhead.
   if (pattern_length == 1) {
     AssertNoAllocation no_heap_allocation;  // ensure vectors stay valid
-    if (StringShape(*sub).IsAsciiRepresentation()) {
+    if (sub->IsAsciiRepresentation()) {
       uc16 pchar = pat->Get(0);
       if (pchar > String::kMaxAsciiCharCode) {
         return -1;
@@ -2190,15 +2190,15 @@ int Runtime::StringMatch(Handle<String> sub,
 
   AssertNoAllocation no_heap_allocation;  // ensure vectors stay valid
   // dispatch on type of strings
-  if (StringShape(*pat).IsAsciiRepresentation()) {
+  if (pat->IsAsciiRepresentation()) {
     Vector<const char> pat_vector = pat->ToAsciiVector();
-    if (StringShape(*sub).IsAsciiRepresentation()) {
+    if (sub->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 (StringShape(*sub).IsAsciiRepresentation()) {
+  if (sub->IsAsciiRepresentation()) {
     return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
   }
   return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
@@ -3329,7 +3329,7 @@ static Object* ConvertCaseHelper(String* s,
   // 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 = StringShape(s).IsAsciiRepresentation()
+  Object* o = s->IsAsciiRepresentation()
       ? Heap::AllocateRawAsciiString(length)
       : Heap::AllocateRawTwoByteString(length);
   if (o->IsFailure()) return o;
@@ -3680,7 +3680,7 @@ static Object* Runtime_StringBuilderConcat(Arguments args) {
     if (first->IsString()) return first;
   }
 
-  bool ascii = StringShape(special).IsAsciiRepresentation();
+  bool ascii = special->IsAsciiRepresentation();
   int position = 0;
   for (int i = 0; i < array_length; i++) {
     Object* elt = fixed_array->get(i);
@@ -3700,7 +3700,7 @@ static Object* Runtime_StringBuilderConcat(Arguments args) {
         return Failure::OutOfMemoryException();
       }
       position += element_length;
-      if (ascii && !StringShape(element).IsAsciiRepresentation()) {
+      if (ascii && !element->IsAsciiRepresentation()) {
         ascii = false;
       }
     } else {
@@ -4757,10 +4757,10 @@ static Object* Runtime_DateParseString(Arguments args) {
   FixedArray* output_array = output->elements();
   RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
   bool result;
-  if (StringShape(*str).IsAsciiRepresentation()) {
+  if (str->IsAsciiRepresentation()) {
     result = DateParser::Parse(str->ToAsciiVector(), output_array);
   } else {
-    ASSERT(StringShape(*str).IsTwoByteRepresentation());
+    ASSERT(str->IsTwoByteRepresentation());
     result = DateParser::Parse(str->ToUC16Vector(), output_array);
   }
 
@@ -6574,9 +6574,9 @@ static bool IsExternalStringValid(Object* str) {
   if (!str->IsString() || !StringShape(String::cast(str)).IsExternal()) {
     return true;
   }
-  if (StringShape(String::cast(str)).IsAsciiRepresentation()) {
+  if (String::cast(str)->IsAsciiRepresentation()) {
     return ExternalAsciiString::cast(str)->resource() != NULL;
-  } else if (StringShape(String::cast(str)).IsTwoByteRepresentation()) {
+  } else if (String::cast(str)->IsTwoByteRepresentation()) {
     return ExternalTwoByteString::cast(str)->resource() != NULL;
   } else {
     return true;
index c42fce36e2c3472955cecf5fde2156dc08cdb004..2b956a493ac378958d0e61588ace510b598d1b9b 100644 (file)
@@ -420,7 +420,7 @@ class TestAsciiResource: public String::ExternalAsciiStringResource {
  public:
   static int dispose_count;
 
-  explicit TestAsciiResource(char* data)
+  explicit TestAsciiResource(const char* data)
       : data_(data),
         length_(strlen(data)) { }
 
@@ -437,7 +437,7 @@ class TestAsciiResource: public String::ExternalAsciiStringResource {
     return length_;
   }
  private:
-  char* data_;
+  const char* data_;
   size_t length_;
 };
 
@@ -6153,6 +6153,117 @@ TEST(ObjectClone) {
 }
 
 
+class AsciiVectorResource : public v8::String::ExternalAsciiStringResource {
+ public:
+  explicit AsciiVectorResource(i::Vector<const char> vector)
+      : data_(vector) {}
+  virtual ~AsciiVectorResource() {}
+  virtual size_t length() const { return data_.length(); }
+  virtual const char* data() const { return data_.start(); }
+ private:
+  i::Vector<const char> data_;
+};
+
+
+class UC16VectorResource : public v8::String::ExternalStringResource {
+ public:
+  explicit UC16VectorResource(i::Vector<const i::uc16> vector)
+      : data_(vector) {}
+  virtual ~UC16VectorResource() {}
+  virtual size_t length() const { return data_.length(); }
+  virtual const i::uc16* data() const { return data_.start(); }
+ private:
+  i::Vector<const i::uc16> data_;
+};
+
+
+static void MorphAString(i::String* string,
+                         AsciiVectorResource* ascii_resource,
+                         UC16VectorResource* uc16_resource) {
+  CHECK(i::StringShape(string).IsExternal());
+  if (string->IsAsciiRepresentation()) {
+    // Check old map is not symbol or long.
+    CHECK(string->map() == i::Heap::short_external_ascii_string_map() ||
+          string->map() == i::Heap::medium_external_ascii_string_map());
+    // Morph external string to be TwoByte string.
+    if (string->length() <= i::String::kMaxShortStringSize) {
+      string->set_map(i::Heap::short_external_string_map());
+    } else {
+      string->set_map(i::Heap::medium_external_string_map());
+    }
+    i::ExternalTwoByteString* morphed =
+         i::ExternalTwoByteString::cast(string);
+    morphed->set_resource(uc16_resource);
+  } else {
+    // Check old map is not symbol or long.
+    CHECK(string->map() == i::Heap::short_external_string_map() ||
+          string->map() == i::Heap::medium_external_string_map());
+    // Morph external string to be ASCII string.
+    if (string->length() <= i::String::kMaxShortStringSize) {
+      string->set_map(i::Heap::short_external_ascii_string_map());
+    } else {
+      string->set_map(i::Heap::medium_external_ascii_string_map());
+    }
+    i::ExternalAsciiString* morphed =
+         i::ExternalAsciiString::cast(string);
+    morphed->set_resource(ascii_resource);
+  }
+}
+
+
+// Test that we can still flatten a string if the components it is built up
+// from have been turned into 16 bit strings in the mean time.
+THREADED_TEST(MorphCompositeStringTest) {
+  const char* c_string = "Now is the time for all good men"
+                         " to come to the aid of the party";
+  uint16_t* two_byte_string = AsciiToTwoByteString(c_string);
+  {
+    v8::HandleScope scope;
+    LocalContext env;
+    AsciiVectorResource ascii_resource(
+        i::Vector<const char>(c_string, strlen(c_string)));
+    UC16VectorResource uc16_resource(
+        i::Vector<const uint16_t>(two_byte_string, strlen(c_string)));
+
+    Local<String> lhs(v8::Utils::ToLocal(
+        i::Factory::NewExternalStringFromAscii(&ascii_resource)));
+    Local<String> rhs(v8::Utils::ToLocal(
+        i::Factory::NewExternalStringFromAscii(&ascii_resource)));
+
+    env->Global()->Set(v8_str("lhs"), lhs);
+    env->Global()->Set(v8_str("rhs"), rhs);
+
+    CompileRun(
+        "var cons = lhs + rhs;"
+        "var slice = lhs.substring(1, lhs.length - 1);"
+        "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);");
+
+    MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource);
+    MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource);
+
+    // Now do some stuff to make sure the strings are flattened, etc.
+    CompileRun(
+        "/[^a-z]/.test(cons);"
+        "/[^a-z]/.test(slice);"
+        "/[^a-z]/.test(slice_on_cons);");
+    const char* expected_cons =
+        "Now is the time for all good men to come to the aid of the party"
+        "Now is the time for all good men to come to the aid of the party";
+    const char* expected_slice =
+        "ow is the time for all good men to come to the aid of the part";
+    const char* expected_slice_on_cons =
+        "ow is the time for all good men to come to the aid of the party"
+        "Now is the time for all good men to come to the aid of the part";
+    CHECK_EQ(String::New(expected_cons),
+             env->Global()->Get(v8_str("cons")));
+    CHECK_EQ(String::New(expected_slice),
+             env->Global()->Get(v8_str("slice")));
+    CHECK_EQ(String::New(expected_slice_on_cons),
+             env->Global()->Get(v8_str("slice_on_cons")));
+  }
+}
+
+
 class RegExpStringModificationTest {
  public:
   RegExpStringModificationTest()
@@ -6197,26 +6308,6 @@ class RegExpStringModificationTest {
   }
  private:
 
-  class AsciiVectorResource : public v8::String::ExternalAsciiStringResource {
-   public:
-    explicit AsciiVectorResource(i::Vector<const char> vector)
-        : data_(vector) {}
-    virtual ~AsciiVectorResource() {}
-    virtual size_t length() const { return data_.length(); }
-    virtual const char* data() const { return data_.start(); }
-   private:
-    i::Vector<const char> data_;
-  };
-  class UC16VectorResource : public v8::String::ExternalStringResource {
-   public:
-    explicit UC16VectorResource(i::Vector<const i::uc16> vector)
-        : data_(vector) {}
-    virtual ~UC16VectorResource() {}
-    virtual size_t length() const { return data_.length(); }
-    virtual const i::uc16* data() const { return data_.start(); }
-   private:
-    i::Vector<const i::uc16> data_;
-  };
   // Number of string modifications required.
   static const int kRequiredModifications = 5;
   static const int kMaxModifications = 100;
@@ -6240,25 +6331,7 @@ class RegExpStringModificationTest {
         v8::Locker lock;
         // Swap string between ascii and two-byte representation.
         i::String* string = *input_;
-        CHECK(i::StringShape(string).IsExternal());
-        if (i::StringShape(string).IsAsciiRepresentation()) {
-          // Morph external string to be TwoByte string.
-          i::ExternalAsciiString* ext_string =
-              i::ExternalAsciiString::cast(string);
-          i::ExternalTwoByteString* morphed =
-              reinterpret_cast<i::ExternalTwoByteString*>(ext_string);
-          morphed->map()->set_instance_type(i::SHORT_EXTERNAL_STRING_TYPE);
-          morphed->set_resource(&uc16_resource_);
-        } else {
-          // Morph external string to be ASCII string.
-          i::ExternalTwoByteString* ext_string =
-              i::ExternalTwoByteString::cast(string);
-          i::ExternalAsciiString* morphed =
-              reinterpret_cast<i::ExternalAsciiString*>(ext_string);
-          morphed->map()->set_instance_type(
-              i::SHORT_EXTERNAL_ASCII_STRING_TYPE);
-          morphed->set_resource(&ascii_resource_);
-        }
+        MorphAString(string, &ascii_resource_, &uc16_resource_);
         morphs_++;
       }
       i::OS::Sleep(1);