Chromium Code Reviews| Index: src/objects.cc |
| diff --git a/src/objects.cc b/src/objects.cc |
| index 2d48eefd1059bbe1982bd58b1bdbdc54fa8ddc8c..0551900acc90692bfb677f366b81c7a1ba32348b 100644 |
| --- a/src/objects.cc |
| +++ b/src/objects.cc |
| @@ -2331,7 +2331,8 @@ bool String::MakeExternal(v8::String::ExternalStringResource* resource) { |
| Heap* heap = GetHeap(); |
| bool is_one_byte = this->IsOneByteRepresentation(); |
| bool is_internalized = this->IsInternalizedString(); |
| - bool has_pointers = this->IsConsString() || this->IsSlicedString(); |
| + bool has_pointers = |
| + this->IsConsString() || this->IsSlicedString() || this->IsThinString(); |
|
Igor Sheludko
2016/12/20 23:46:58
Suggestion for follow-up CL:
bool IsIndirectStrin
Jakob Kummerow
2017/01/04 12:45:06
Done (StringShape supports this already).
|
| // Morph the string to an external string by replacing the map and |
| // reinitializing the fields. This won't work if the space the existing |
| @@ -2403,7 +2404,8 @@ bool String::MakeExternal(v8::String::ExternalOneByteStringResource* resource) { |
| if (size < ExternalString::kShortSize) return false; |
| Heap* heap = GetHeap(); |
| bool is_internalized = this->IsInternalizedString(); |
| - bool has_pointers = this->IsConsString() || this->IsSlicedString(); |
| + bool has_pointers = |
| + this->IsConsString() || this->IsSlicedString() || this->IsThinString(); |
|
Igor Sheludko
2016/12/20 23:46:58
Same here.
Jakob Kummerow
2017/01/04 12:45:06
Done.
|
| // Morph the string to an external string by replacing the map and |
| // reinitializing the fields. This won't work if the space the existing |
| @@ -10768,11 +10770,7 @@ Handle<String> String::Trim(Handle<String> string, TrimMode mode) { |
| return isolate->factory()->NewSubString(string, left, right); |
| } |
| -bool String::LooksValid() { |
| - if (!GetIsolate()->heap()->Contains(this)) return false; |
| - return true; |
| -} |
| - |
| +bool String::LooksValid() { return GetIsolate()->heap()->Contains(this); } |
| // static |
| MaybeHandle<String> Name::ToFunctionName(Handle<Name> name) { |
| @@ -10906,6 +10904,10 @@ String::FlatContent String::GetFlatContent() { |
| } |
| string = cons->first(); |
| shape = StringShape(string); |
| + } else if (shape.representation_tag() == kThinStringTag) { |
| + ThinString* thin = ThinString::cast(string); |
| + string = thin->actual(); |
| + shape = StringShape(string); |
| } |
| if (shape.representation_tag() == kSlicedStringTag) { |
| SlicedString* slice = SlicedString::cast(string); |
| @@ -11000,6 +11002,7 @@ const uc16* String::GetTwoByteData(unsigned start) { |
| return slice->parent()->GetTwoByteData(start + slice->offset()); |
| } |
| case kConsStringTag: |
| + case kThinStringTag: |
| UNREACHABLE(); |
| return NULL; |
| } |
| @@ -11266,6 +11269,7 @@ uint16_t ConsString::ConsStringGet(int index) { |
| return 0; |
| } |
| +uint16_t ThinString::ThinStringGet(int index) { return actual()->Get(index); } |
| uint16_t SlicedString::SlicedStringGet(int index) { |
| return parent()->Get(offset() + index); |
| @@ -11360,6 +11364,10 @@ void String::WriteToFlat(String* src, |
| WriteToFlat(slice->parent(), sink, from + offset, to + offset); |
| return; |
| } |
| + case kOneByteStringTag | kThinStringTag: |
| + case kTwoByteStringTag | kThinStringTag: |
| + source = ThinString::cast(source)->actual(); |
| + break; |
| } |
| } |
| } |
| @@ -11581,6 +11589,17 @@ bool String::SlowEquals(String* other) { |
| if (len != other->length()) return false; |
| if (len == 0) return true; |
| + // Fast check: if at least one ThinString is involved, dereference it/them |
| + // and restart. |
| + if (this->IsThinString() || other->IsThinString()) { |
| + if (other->IsThinString()) other = ThinString::cast(other)->actual(); |
| + if (this->IsThinString()) { |
| + return ThinString::cast(this)->actual()->Equals(other); |
| + } else { |
| + return this->Equals(other); |
| + } |
| + } |
| + |
| // Fast check: if hash code is computed for both strings |
| // a fast negative check can be performed. |
| if (HasHashCode() && other->HasHashCode()) { |
| @@ -11622,6 +11641,14 @@ bool String::SlowEquals(Handle<String> one, Handle<String> two) { |
| if (one_length != two->length()) return false; |
| if (one_length == 0) return true; |
| + // Fast check: if at least one ThinString is involved, dereference it/them |
| + // and restart. |
| + if (one->IsThinString() || two->IsThinString()) { |
| + if (one->IsThinString()) one = handle(ThinString::cast(*one)->actual()); |
| + if (two->IsThinString()) two = handle(ThinString::cast(*two)->actual()); |
| + return String::Equals(one, two); |
| + } |
| + |
| // Fast check: if hash code is computed for both strings |
| // a fast negative check can be performed. |
| if (one->HasHashCode() && two->HasHashCode()) { |
| @@ -16771,6 +16798,14 @@ class InternalizedStringKey : public HashTableKey { |
| DCHECK(string_->IsInternalizedString()); |
| return string_; |
| } |
| + // External strings get special treatment, to avoid copying their contents. |
| + if (string_->IsExternalOneByteString()) { |
| + return isolate->factory() |
| + ->InternalizeExternalString<ExternalOneByteString>(string_); |
| + } else if (string_->IsExternalTwoByteString()) { |
| + return isolate->factory() |
| + ->InternalizeExternalString<ExternalTwoByteString>(string_); |
| + } |
| // Otherwise allocate a new internalized string. |
| return isolate->factory()->NewInternalizedStringImpl( |
| string_, string_->length(), string_->hash_field()); |
| @@ -16780,6 +16815,7 @@ class InternalizedStringKey : public HashTableKey { |
| return String::cast(obj)->Hash(); |
| } |
| + private: |
| Handle<String> string_; |
| }; |
| @@ -17714,6 +17750,9 @@ MaybeHandle<String> StringTable::InternalizeStringIfExists( |
| if (string->IsInternalizedString()) { |
| return string; |
| } |
| + if (string->IsThinString()) { |
| + return handle(Handle<ThinString>::cast(string)->actual(), isolate); |
| + } |
| return LookupStringIfExists(isolate, string); |
| } |
| @@ -17760,31 +17799,70 @@ void StringTable::EnsureCapacityForDeserialization(Isolate* isolate, |
| isolate->heap()->SetRootStringTable(*table); |
| } |
| +namespace { |
| + |
| +template <class StringClass> |
| +void MigrateExternalStringResource(Isolate* isolate, Handle<String> from, |
| + Handle<String> to) { |
| + Handle<StringClass> cast_from = Handle<StringClass>::cast(from); |
| + Handle<StringClass> cast_to = Handle<StringClass>::cast(to); |
| + const typename StringClass::Resource* to_resource = cast_to->resource(); |
| + if (to_resource == nullptr) { |
| + // |to| is a just-created internalized copy of |from|. Migrate the resource. |
| + cast_to->set_resource(cast_from->resource()); |
| + // Zap |from|'s resource pointer to reflect the fact that |from| has |
| + // relinquished ownership of its resource. |
| + cast_from->set_resource(nullptr); |
| + } else if (to_resource != cast_from->resource()) { |
| + // |to| already existed and has its own resource. Finalize |from|. |
| + isolate->heap()->FinalizeExternalString(*from); |
| + } |
| +} |
| + |
| +} // namespace |
| Handle<String> StringTable::LookupString(Isolate* isolate, |
| Handle<String> string) { |
| + if (string->IsThinString()) { |
| + DCHECK(Handle<ThinString>::cast(string)->actual()->IsInternalizedString()); |
| + return handle(Handle<ThinString>::cast(string)->actual(), isolate); |
| + } |
| if (string->IsConsString() && string->IsFlat()) { |
| - string = String::Flatten(string); |
| + string = handle(Handle<ConsString>::cast(string)->first(), isolate); |
| if (string->IsInternalizedString()) return string; |
| } |
| InternalizedStringKey key(string); |
| Handle<String> result = LookupKey(isolate, &key); |
| - if (string->IsConsString()) { |
| - Handle<ConsString> cons = Handle<ConsString>::cast(string); |
| - cons->set_first(*result); |
| - cons->set_second(isolate->heap()->empty_string()); |
| - } else if (string->IsSlicedString()) { |
| - STATIC_ASSERT(ConsString::kSize == SlicedString::kSize); |
| + if (string->IsExternalString()) { |
| + if (result->IsExternalOneByteString()) { |
| + MigrateExternalStringResource<ExternalOneByteString>(isolate, string, |
| + result); |
| + } else if (result->IsExternalTwoByteString()) { |
| + MigrateExternalStringResource<ExternalTwoByteString>(isolate, string, |
| + result); |
| + } |
| + } |
| + |
| + // The LookupKey() call above tries to internalize the string in-place. |
| + // In cases where that wasn't possible (e.g. new-space strings), turn them |
| + // into ThinStrings referring to their internalized versions now. |
| + if (!string->IsInternalizedString()) { |
| DisallowHeapAllocation no_gc; |
| bool one_byte = result->IsOneByteRepresentation(); |
| - Handle<Map> map = one_byte ? isolate->factory()->cons_one_byte_string_map() |
| - : isolate->factory()->cons_string_map(); |
| - string->set_map(*map); |
| - Handle<ConsString> cons = Handle<ConsString>::cast(string); |
| - cons->set_first(*result); |
| - cons->set_second(isolate->heap()->empty_string()); |
| + Handle<Map> map = one_byte ? isolate->factory()->thin_one_byte_string_map() |
| + : isolate->factory()->thin_string_map(); |
| + int old_size = string->Size(); |
| + DCHECK(old_size >= ThinString::kSize); |
| + string->synchronized_set_map(*map); |
| + Handle<ThinString> thin = Handle<ThinString>::cast(string); |
| + thin->set_actual(*result); |
| + Address thin_end = thin->address() + ThinString::kSize; |
| + int size_delta = old_size - ThinString::kSize; |
| + Heap* heap = isolate->heap(); |
|
Igor Sheludko
2016/12/20 23:46:58
Maybe wrap below lines in "if (size_delta) {" to s
Jakob Kummerow
2017/01/04 12:45:06
Done.
|
| + heap->CreateFillerObjectAt(thin_end, size_delta, ClearRecordedSlots::kNo); |
| + heap->AdjustLiveBytes(*thin, -size_delta); |
| } |
| return result; |
| } |