| Index: src/objects.cc
 | 
| diff --git a/src/objects.cc b/src/objects.cc
 | 
| index a4a2a2997163059118108e55d0debad896c389fe..48df7f52103b5cead21d6268342d86e38c1f2e85 100644
 | 
| --- a/src/objects.cc
 | 
| +++ b/src/objects.cc
 | 
| @@ -2536,7 +2536,7 @@ 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 = StringShape(this).IsIndirect();
 | 
|  
 | 
|    // Morph the string to an external string by replacing the map and
 | 
|    // reinitializing the fields.  This won't work if the space the existing
 | 
| @@ -2608,7 +2608,7 @@ 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 = StringShape(this).IsIndirect();
 | 
|  
 | 
|    // Morph the string to an external string by replacing the map and
 | 
|    // reinitializing the fields.  This won't work if the space the existing
 | 
| @@ -10370,10 +10370,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) {
 | 
| @@ -10507,8 +10504,7 @@ String::FlatContent String::GetFlatContent() {
 | 
|      }
 | 
|      string = cons->first();
 | 
|      shape = StringShape(string);
 | 
| -  }
 | 
| -  if (shape.representation_tag() == kSlicedStringTag) {
 | 
| +  } else if (shape.representation_tag() == kSlicedStringTag) {
 | 
|      SlicedString* slice = SlicedString::cast(string);
 | 
|      offset = slice->offset();
 | 
|      string = slice->parent();
 | 
| @@ -10516,6 +10512,13 @@ String::FlatContent String::GetFlatContent() {
 | 
|      DCHECK(shape.representation_tag() != kConsStringTag &&
 | 
|             shape.representation_tag() != kSlicedStringTag);
 | 
|    }
 | 
| +  if (shape.representation_tag() == kThinStringTag) {
 | 
| +    ThinString* thin = ThinString::cast(string);
 | 
| +    string = thin->actual();
 | 
| +    shape = StringShape(string);
 | 
| +    DCHECK(!shape.IsCons());
 | 
| +    DCHECK(!shape.IsSliced());
 | 
| +  }
 | 
|    if (shape.encoding_tag() == kOneByteStringTag) {
 | 
|      const uint8_t* start;
 | 
|      if (shape.representation_tag() == kSeqStringTag) {
 | 
| @@ -10601,6 +10604,7 @@ const uc16* String::GetTwoByteData(unsigned start) {
 | 
|        return slice->parent()->GetTwoByteData(start + slice->offset());
 | 
|      }
 | 
|      case kConsStringTag:
 | 
| +    case kThinStringTag:
 | 
|        UNREACHABLE();
 | 
|        return NULL;
 | 
|    }
 | 
| @@ -10867,6 +10871,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);
 | 
| @@ -10961,6 +10966,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;
 | 
|      }
 | 
|    }
 | 
|  }
 | 
| @@ -11182,6 +11191,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()) {
 | 
| @@ -11223,6 +11243,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()) {
 | 
| @@ -16273,6 +16301,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());
 | 
| @@ -16282,6 +16318,7 @@ class InternalizedStringKey : public HashTableKey {
 | 
|      return String::cast(obj)->Hash();
 | 
|    }
 | 
|  
 | 
| + private:
 | 
|    Handle<String> string_;
 | 
|  };
 | 
|  
 | 
| @@ -17216,6 +17253,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);
 | 
|  }
 | 
|  
 | 
| @@ -17262,31 +17302,77 @@ 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);
 | 
| +    } else {
 | 
| +      // If the external string is duped into an existing non-external
 | 
| +      // internalized string, free its resource (it's about to be rewritten
 | 
| +      // into a ThinString below).
 | 
| +      isolate->heap()->FinalizeExternalString(*string);
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  // 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;
 | 
| +    if (size_delta != 0) {
 | 
| +      Heap* heap = isolate->heap();
 | 
| +      heap->CreateFillerObjectAt(thin_end, size_delta, ClearRecordedSlots::kNo);
 | 
| +      heap->AdjustLiveBytes(*thin, -size_delta);
 | 
| +    }
 | 
|    }
 | 
|    return result;
 | 
|  }
 | 
| 
 |