diff --git a/src/heap.cc b/src/heap.cc index d33d91ab4a..d554a3ba68 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -2080,7 +2080,7 @@ Object* Heap::AllocateSubString(String* buffer, } // Make an attempt to flatten the buffer to reduce access time. - buffer->TryFlatten(); + buffer = buffer->TryFlattenGetString(); Object* result = buffer->IsAsciiRepresentation() ? AllocateRawAsciiString(length, pretenure ) diff --git a/src/objects-inl.h b/src/objects-inl.h index ad15104152..d82d73ec50 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -1691,13 +1691,19 @@ bool String::Equals(String* other) { Object* String::TryFlatten(PretenureFlag pretenure) { - // 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. - if (IsFlat()) return this; + if (!StringShape(this).IsCons()) return this; + ConsString* cons = ConsString::cast(this); + if (cons->second()->length() == 0) return cons->first(); return SlowTryFlatten(pretenure); } +String* String::TryFlattenGetString(PretenureFlag pretenure) { + Object* flat = TryFlatten(pretenure); + return flat->IsFailure() ? this : String::cast(flat); +} + + uint16_t String::Get(int index) { ASSERT(index >= 0 && index < length()); switch (StringShape(this).full_representation_tag()) { diff --git a/src/objects.cc b/src/objects.cc index c8acb47071..360eb28fb1 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -631,7 +631,7 @@ Object* String::SlowTryFlatten(PretenureFlag pretenure) { case kConsStringTag: { ConsString* cs = ConsString::cast(this); if (cs->second()->length() == 0) { - return this; + return cs->first(); } // 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 @@ -669,7 +669,7 @@ Object* String::SlowTryFlatten(PretenureFlag pretenure) { } cs->set_first(result); cs->set_second(Heap::empty_string()); - return this; + return result; } default: return this; @@ -4580,51 +4580,58 @@ bool String::SlowEquals(String* other) { if (Hash() != other->Hash()) return false; } - if (StringShape(this).IsSequentialAscii() && - StringShape(other).IsSequentialAscii()) { - const char* str1 = SeqAsciiString::cast(this)->GetChars(); - const char* str2 = SeqAsciiString::cast(other)->GetChars(); + // We know the strings are both non-empty. Compare the first chars + // before we try to flatten the strings. + if (this->Get(0) != other->Get(0)) return false; + + String* lhs = this->TryFlattenGetString(); + String* rhs = other->TryFlattenGetString(); + + if (StringShape(lhs).IsSequentialAscii() && + StringShape(rhs).IsSequentialAscii()) { + const char* str1 = SeqAsciiString::cast(lhs)->GetChars(); + const char* str2 = SeqAsciiString::cast(rhs)->GetChars(); return CompareRawStringContents(Vector(str1, len), Vector(str2, len)); } - if (this->IsFlat()) { + if (lhs->IsFlat()) { if (IsAsciiRepresentation()) { - Vector vec1 = this->ToAsciiVector(); - if (other->IsFlat()) { - if (other->IsAsciiRepresentation()) { - Vector vec2 = other->ToAsciiVector(); + Vector vec1 = lhs->ToAsciiVector(); + if (rhs->IsFlat()) { + if (rhs->IsAsciiRepresentation()) { + Vector vec2 = rhs->ToAsciiVector(); return CompareRawStringContents(vec1, vec2); } else { VectorIterator buf1(vec1); - VectorIterator ib(other->ToUC16Vector()); + VectorIterator ib(rhs->ToUC16Vector()); return CompareStringContents(&buf1, &ib); } } else { VectorIterator buf1(vec1); - string_compare_buffer_b.Reset(0, other); + string_compare_buffer_b.Reset(0, rhs); return CompareStringContents(&buf1, &string_compare_buffer_b); } } else { - Vector vec1 = this->ToUC16Vector(); - if (other->IsFlat()) { - if (other->IsAsciiRepresentation()) { + Vector vec1 = lhs->ToUC16Vector(); + if (rhs->IsFlat()) { + if (rhs->IsAsciiRepresentation()) { VectorIterator buf1(vec1); - VectorIterator ib(other->ToAsciiVector()); + VectorIterator ib(rhs->ToAsciiVector()); return CompareStringContents(&buf1, &ib); } else { - Vector vec2(other->ToUC16Vector()); + Vector vec2(rhs->ToUC16Vector()); return CompareRawStringContents(vec1, vec2); } } else { VectorIterator buf1(vec1); - string_compare_buffer_b.Reset(0, other); + string_compare_buffer_b.Reset(0, rhs); return CompareStringContents(&buf1, &string_compare_buffer_b); } } } else { - string_compare_buffer_a.Reset(0, this); - return CompareStringContentsPartial(&string_compare_buffer_a, other); + string_compare_buffer_a.Reset(0, lhs); + return CompareStringContentsPartial(&string_compare_buffer_a, rhs); } } @@ -7038,15 +7045,9 @@ class SymbolKey : public HashTableKey { } Object* AsObject() { - // If the string is a cons string, attempt to flatten it so that - // symbols will most often be flat strings. - if (StringShape(string_).IsCons()) { - ConsString* cons_string = ConsString::cast(string_); - cons_string->TryFlatten(); - if (cons_string->second()->length() == 0) { - string_ = cons_string->first(); - } - } + // Attempt to flatten the string, so that symbols will most often + // be flat strings. + string_ = string_->TryFlattenGetString(); // Transform string to symbol if possible. Map* map = Heap::SymbolMapForString(string_); if (map != NULL) { diff --git a/src/objects.h b/src/objects.h index 8b114a64ff..7f9c2a03db 100644 --- a/src/objects.h +++ b/src/objects.h @@ -4001,17 +4001,28 @@ class String: public HeapObject { // to this method are not efficient unless the string is flat. inline uint16_t Get(int index); - // Try to flatten the top level ConsString that is hiding behind this - // string. This is a no-op unless the string is a ConsString. Flatten - // mutates the ConsString and might return a failure. - Object* SlowTryFlatten(PretenureFlag pretenure); - - // Try to flatten the string. Checks first inline to see if it is necessary. - // Do not handle allocation failures. After calling TryFlatten, the - // string could still be a ConsString, in which case a failure is returned. - // Use FlattenString from Handles.cc to be sure to flatten. + // Try to flatten the string. Checks first inline to see if it is + // necessary. Does nothing if the string is not a cons string. + // Flattening allocates a sequential string with the same data as + // the given string and mutates the cons string to a degenerate + // form, where the first component is the new sequential string and + // the second component is the empty string. If allocation fails, + // this function returns a failure. If flattening succeeds, this + // function returns the sequential string that is now the first + // component of the cons string. + // + // Degenerate cons strings are handled specially by the garbage + // collector (see IsShortcutCandidate). + // + // Use FlattenString from Handles.cc to flatten even in case an + // allocation failure happens. inline Object* TryFlatten(PretenureFlag pretenure = NOT_TENURED); + // Convenience function. Has exactly the same behavior as + // TryFlatten(), except in the case of failure returns the original + // string. + inline String* TryFlattenGetString(PretenureFlag pretenure = NOT_TENURED); + Vector ToAsciiVector(); Vector ToUC16Vector(); @@ -4197,6 +4208,11 @@ class String: public HeapObject { unsigned max_chars); private: + // Try to flatten the top level ConsString that is hiding behind this + // string. This is a no-op unless the string is a ConsString. Flatten + // mutates the ConsString and might return a failure. + Object* SlowTryFlatten(PretenureFlag pretenure); + // 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);