From e2b306c831761e583f862c8799af150943ba0efa Mon Sep 17 00:00:00 2001 From: Yang Guo Date: Tue, 18 Jul 2017 14:38:43 +0200 Subject: [PATCH] deps: backport rehash strings after deserialization Original commit messages: https://github.com/v8/v8/commit/a2ab1353f6708b44d305fdd9fe65a6d29b95c6d6 [snapshot] Rehash strings after deserialization. See https://goo.gl/6aN8xA Bug: v8:6593 Change-Id: Ic8b0b57195d01d41591397d5d45de3f0f3ebc3d9 Reviewed-on: https://chromium-review.googlesource.com/574527 Reviewed-by: Camillo Bruni Reviewed-by: Jakob Gruber Reviewed-by: Ulan Degenbaev Commit-Queue: Yang Guo Cr-Commit-Position: refs/heads/master@{#46732} https://github.com/v8/v8/commit/182caaf4a9b94024e47007d426831c024345cb97 Do not track transitions for built-in objects. Objects created during bootstrapping do not need a transition tree except for elements kind transitions. Bug: v8:6596 Change-Id: I237b8b2792f201336e1c9731c815095dd06bc182 Reviewed-on: https://chromium-review.googlesource.com/571750 Reviewed-by: Igor Sheludko Commit-Queue: Yang Guo Cr-Commit-Position: refs/heads/master@{#46693} Fixes: https://github.com/nodejs/node/issues/14171 Refs: https://github.com/nodejs/node/pull/14345 Backport-PR-URL: https://github.com/nodejs/node/pull/14574 Backport-Reviewed-By: Anna Henningsen Backport-Reviewed-By: Refael Ackermann PR-URL: https://github.com/nodejs/node/pull/14004 Reviewed-By: Anna Henningsen Reviewed-By: Anna Henningsen Reviewed-By: Franziska Hinkelmann Reviewed-By: James M Snell Reviewed-By: Matteo Collina Reviewed-By: Colin Ihrig --- deps/v8/src/api.cc | 13 +++- deps/v8/src/bootstrapper.cc | 6 ++ deps/v8/src/flag-definitions.h | 2 + deps/v8/src/heap/heap.cc | 17 ++--- deps/v8/src/heap/heap.h | 3 + deps/v8/src/js/array.js | 2 + deps/v8/src/objects.cc | 11 ++- deps/v8/src/snapshot/deserializer.cc | 71 ++++++++++++++++++++ deps/v8/src/snapshot/deserializer.h | 18 ++++- deps/v8/src/snapshot/partial-serializer.cc | 37 +++++++--- deps/v8/src/snapshot/partial-serializer.h | 8 +++ deps/v8/src/snapshot/snapshot-common.cc | 13 +++- deps/v8/src/snapshot/snapshot.h | 12 ++-- deps/v8/src/snapshot/startup-serializer.cc | 17 ++++- deps/v8/src/snapshot/startup-serializer.h | 8 +++ deps/v8/src/transitions-inl.h | 2 - deps/v8/src/transitions.cc | 42 ++++++++++++ deps/v8/src/transitions.h | 4 +- deps/v8/test/cctest/heap/test-heap.cc | 21 ++++++ deps/v8/test/cctest/test-serialize.cc | 45 +++++++++++++ deps/v8/test/mjsunit/regress/regress-5902.js | 1 - 21 files changed, 321 insertions(+), 32 deletions(-) diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc index 79008219a7c679..818dfa1e22e69a 100644 --- a/deps/v8/src/api.cc +++ b/deps/v8/src/api.cc @@ -660,6 +660,9 @@ StartupData SnapshotCreator::CreateBlob( isolate->heap()->SetSerializedGlobalProxySizes(*global_proxy_sizes); } + // We might rehash strings and re-sort descriptors. Clear the lookup cache. + isolate->descriptor_lookup_cache()->Clear(); + // If we don't do this then we end up with a stray root pointing at the // context even after we have disposed of the context. isolate->heap()->CollectAllAvailableGarbage( @@ -701,11 +704,15 @@ StartupData SnapshotCreator::CreateBlob( // Serialize each context with a new partial serializer. i::List context_snapshots(num_additional_contexts + 1); + // TODO(6593): generalize rehashing, and remove this flag. + bool can_be_rehashed = true; + { // The default snapshot does not support embedder fields. i::PartialSerializer partial_serializer( isolate, &startup_serializer, v8::SerializeInternalFieldsCallback()); partial_serializer.Serialize(&default_context, false); + can_be_rehashed = can_be_rehashed && partial_serializer.can_be_rehashed(); context_snapshots.Add(new i::SnapshotData(&partial_serializer)); } @@ -713,10 +720,12 @@ StartupData SnapshotCreator::CreateBlob( i::PartialSerializer partial_serializer( isolate, &startup_serializer, data->embedder_fields_serializers_[i]); partial_serializer.Serialize(&contexts[i], true); + can_be_rehashed = can_be_rehashed && partial_serializer.can_be_rehashed(); context_snapshots.Add(new i::SnapshotData(&partial_serializer)); } startup_serializer.SerializeWeakReferencesAndDeferred(); + can_be_rehashed = can_be_rehashed && startup_serializer.can_be_rehashed(); #ifdef DEBUG if (i::FLAG_external_reference_stats) { @@ -725,8 +734,8 @@ StartupData SnapshotCreator::CreateBlob( #endif // DEBUG i::SnapshotData startup_snapshot(&startup_serializer); - StartupData result = - i::Snapshot::CreateSnapshotBlob(&startup_snapshot, &context_snapshots); + StartupData result = i::Snapshot::CreateSnapshotBlob( + &startup_snapshot, &context_snapshots, can_be_rehashed); // Delete heap-allocated context snapshot instances. for (const auto& context_snapshot : context_snapshots) { diff --git a/deps/v8/src/bootstrapper.cc b/deps/v8/src/bootstrapper.cc index b1fda971cfc12e..2652ab028e8b7a 100644 --- a/deps/v8/src/bootstrapper.cc +++ b/deps/v8/src/bootstrapper.cc @@ -652,6 +652,8 @@ Handle Genesis::GetThrowTypeErrorIntrinsic( DCHECK(false); } + JSObject::MigrateSlowToFast(function, 0, "Bootstrapping"); + return function; } @@ -1397,6 +1399,8 @@ void Genesis::InitializeGlobal(Handle global_object, sloppy_function_map_writable_prototype_->SetConstructor(*function_fun); strict_function_map_writable_prototype_->SetConstructor(*function_fun); class_function_map_->SetConstructor(*function_fun); + + JSObject::MigrateSlowToFast(function_fun, 0, "Bootstrapping"); } { @@ -2221,6 +2225,8 @@ void Genesis::InitializeGlobal(Handle global_object, info->set_length(1); native_context()->set_promise_reject_shared_fun(*info); } + + JSObject::MigrateSlowToFast(promise_fun, 0, "Bootstrapping"); } { // -- R e g E x p diff --git a/deps/v8/src/flag-definitions.h b/deps/v8/src/flag-definitions.h index 85ee27ee6ef321..f719555c5ff911 100644 --- a/deps/v8/src/flag-definitions.h +++ b/deps/v8/src/flag-definitions.h @@ -1002,6 +1002,8 @@ DEFINE_BOOL(abort_on_stack_overflow, false, DEFINE_BOOL(randomize_hashes, true, "randomize hashes to avoid predictable hash collisions " "(with snapshots this option cannot override the baked-in seed)") +DEFINE_BOOL(rehash_snapshot, true, + "rehash strings from the snapshot to override the baked-in seed") DEFINE_INT(hash_seed, 0, "Fixed seed to use to hash property keys (0 means random)" "(with snapshots this option cannot override the baked-in seed)") diff --git a/deps/v8/src/heap/heap.cc b/deps/v8/src/heap/heap.cc index ad3bfef5594521..2b26e0d1b9f0f7 100644 --- a/deps/v8/src/heap/heap.cc +++ b/deps/v8/src/heap/heap.cc @@ -5679,14 +5679,7 @@ bool Heap::SetUp() { // Set up the seed that is used to randomize the string hash function. DCHECK(hash_seed() == 0); - if (FLAG_randomize_hashes) { - if (FLAG_hash_seed == 0) { - int rnd = isolate()->random_number_generator()->NextInt(); - set_hash_seed(Smi::FromInt(rnd & Name::kHashBitMask)); - } else { - set_hash_seed(Smi::FromInt(FLAG_hash_seed)); - } - } + if (FLAG_randomize_hashes) InitializeHashSeed(); for (int i = 0; i < static_cast(v8::Isolate::kUseCounterFeatureCount); i++) { @@ -5731,6 +5724,14 @@ bool Heap::SetUp() { return true; } +void Heap::InitializeHashSeed() { + if (FLAG_hash_seed == 0) { + int rnd = isolate()->random_number_generator()->NextInt(); + set_hash_seed(Smi::FromInt(rnd & Name::kHashBitMask)); + } else { + set_hash_seed(Smi::FromInt(FLAG_hash_seed)); + } +} bool Heap::CreateHeapObjects() { // Create initial maps. diff --git a/deps/v8/src/heap/heap.h b/deps/v8/src/heap/heap.h index 80bc68c1723970..f18f3edd3f1231 100644 --- a/deps/v8/src/heap/heap.h +++ b/deps/v8/src/heap/heap.h @@ -1000,6 +1000,9 @@ class Heap { // without actually creating any objects. bool SetUp(); + // (Re-)Initialize hash seed from flag or RNG. + void InitializeHashSeed(); + // Bootstraps the object heap with the core set of objects required to run. // Returns whether it succeeded. bool CreateHeapObjects(); diff --git a/deps/v8/src/js/array.js b/deps/v8/src/js/array.js index 188f21c41ef250..2e22a521dcd325 100644 --- a/deps/v8/src/js/array.js +++ b/deps/v8/src/js/array.js @@ -1282,6 +1282,8 @@ var unscopables = { keys: true, }; +%ToFastProperties(unscopables); + %AddNamedProperty(GlobalArray.prototype, unscopablesSymbol, unscopables, DONT_ENUM | READ_ONLY); diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc index e2748a5216d663..a1d87342ea9fd0 100644 --- a/deps/v8/src/objects.cc +++ b/deps/v8/src/objects.cc @@ -8860,7 +8860,13 @@ void Map::TraceAllTransitions(Map* map) { void Map::ConnectTransition(Handle parent, Handle child, Handle name, SimpleTransitionFlag flag) { - if (!parent->GetBackPointer()->IsUndefined(parent->GetIsolate())) { + Isolate* isolate = parent->GetIsolate(); + // Do not track transitions during bootstrap except for element transitions. + if (isolate->bootstrapper()->IsActive() && + !name.is_identical_to(isolate->factory()->elements_transition_symbol())) { + return; + } + if (!parent->GetBackPointer()->IsUndefined(isolate)) { parent->set_owns_descriptors(false); } else { // |parent| is initial map and it must keep the ownership, there must be no @@ -16712,6 +16718,9 @@ template class Dictionary; +template void +HashTable >::Rehash(Handle key); + template Handle Dictionary::New( Isolate*, int at_least_space_for, PretenureFlag pretenure, diff --git a/deps/v8/src/snapshot/deserializer.cc b/deps/v8/src/snapshot/deserializer.cc index c76e4eca542969..b39f351a942978 100644 --- a/deps/v8/src/snapshot/deserializer.cc +++ b/deps/v8/src/snapshot/deserializer.cc @@ -125,6 +125,7 @@ void Deserializer::Deserialize(Isolate* isolate) { LOG_CODE_EVENT(isolate_, LogCompiledFunctions()); isolate_->builtins()->MarkInitialized(); + if (FLAG_rehash_snapshot && can_rehash_) Rehash(); } MaybeHandle Deserializer::DeserializePartial( @@ -155,6 +156,9 @@ MaybeHandle Deserializer::DeserializePartial( // changed and logging should be added to notify the profiler et al of the // new code, which also has to be flushed from instruction cache. CHECK_EQ(start_address, code_space->top()); + + if (FLAG_rehash_snapshot && can_rehash_) RehashContext(Context::cast(root)); + return Handle(root, isolate); } @@ -181,6 +185,63 @@ MaybeHandle Deserializer::DeserializeObject(Isolate* isolate) { } } +// We only really just need HashForObject here. +class StringRehashKey : public HashTableKey { + public: + uint32_t HashForObject(Object* other) override { + return String::cast(other)->Hash(); + } + + static uint32_t StringHash(Object* obj) { + UNREACHABLE(); + return String::cast(obj)->Hash(); + } + + bool IsMatch(Object* string) override { + UNREACHABLE(); + return false; + } + + uint32_t Hash() override { + UNREACHABLE(); + return 0; + } + + Handle AsHandle(Isolate* isolate) override { + UNREACHABLE(); + return isolate->factory()->empty_string(); + } +}; + +void Deserializer::Rehash() { + DCHECK(can_rehash_); + isolate_->heap()->InitializeHashSeed(); + if (FLAG_profile_deserialization) { + PrintF("Re-initializing hash seed to %x\n", + isolate_->heap()->hash_seed()->value()); + } + StringRehashKey string_rehash_key; + isolate_->heap()->string_table()->Rehash(&string_rehash_key); + SortMapDescriptors(); +} + +void Deserializer::RehashContext(Context* context) { + DCHECK(can_rehash_); + for (const auto& array : transition_arrays_) array->Sort(); + Handle dummy = isolate_->factory()->empty_string(); + context->global_object()->global_dictionary()->Rehash(dummy); + SortMapDescriptors(); +} + +void Deserializer::SortMapDescriptors() { + for (const auto& address : allocated_maps_) { + Map* map = Map::cast(HeapObject::FromAddress(address)); + if (map->instance_descriptors()->number_of_descriptors() > 1) { + map->instance_descriptors()->Sort(); + } + } +} + Deserializer::~Deserializer() { #ifdef DEBUG // Do not perform checks if we aborted deserialization. @@ -371,6 +432,16 @@ HeapObject* Deserializer::PostProcessNewObject(HeapObject* obj, int space) { string->resource())); isolate_->heap()->RegisterExternalString(string); } + if (FLAG_rehash_snapshot && can_rehash_ && !deserializing_user_code()) { + if (obj->IsString()) { + // Uninitialize hash field as we are going to reinitialize the hash seed. + String* string = String::cast(obj); + string->set_hash_field(String::kEmptyHashField); + } else if (obj->IsTransitionArray() && + TransitionArray::cast(obj)->number_of_entries() > 1) { + transition_arrays_.Add(TransitionArray::cast(obj)); + } + } // Check alignment. DCHECK_EQ(0, Heap::GetFillToAlign(obj->address(), obj->RequiredAlignment())); return obj; diff --git a/deps/v8/src/snapshot/deserializer.h b/deps/v8/src/snapshot/deserializer.h index a56adb67d48b16..3a650d99743fde 100644 --- a/deps/v8/src/snapshot/deserializer.h +++ b/deps/v8/src/snapshot/deserializer.h @@ -39,7 +39,8 @@ class Deserializer : public SerializerDeserializer { external_reference_table_(NULL), deserialized_large_objects_(0), deserializing_user_code_(deserializing_user_code), - next_alignment_(kWordAligned) { + next_alignment_(kWordAligned), + can_rehash_(false) { DecodeReservation(data->Reservations()); } @@ -62,6 +63,8 @@ class Deserializer : public SerializerDeserializer { attached_objects_.Add(attached_object); } + void SetRehashability(bool v) { can_rehash_ = v; } + private: void VisitRootPointers(Root root, Object** start, Object** end) override; @@ -115,6 +118,15 @@ class Deserializer : public SerializerDeserializer { // snapshot by chunk index and offset. HeapObject* GetBackReferencedObject(int space); + // Rehash after deserializing an isolate. + void Rehash(); + + // Rehash after deserializing a context. + void RehashContext(Context* context); + + // Sort descriptors of deserialized maps using new string hashes. + void SortMapDescriptors(); + // Cached current isolate. Isolate* isolate_; @@ -142,11 +154,15 @@ class Deserializer : public SerializerDeserializer { List accessor_infos_; List > new_internalized_strings_; List > new_scripts_; + List transition_arrays_; bool deserializing_user_code_; AllocationAlignment next_alignment_; + // TODO(6593): generalize rehashing, and remove this flag. + bool can_rehash_; + DISALLOW_COPY_AND_ASSIGN(Deserializer); }; diff --git a/deps/v8/src/snapshot/partial-serializer.cc b/deps/v8/src/snapshot/partial-serializer.cc index d3e60e0e4e3027..bbd829eddfe816 100644 --- a/deps/v8/src/snapshot/partial-serializer.cc +++ b/deps/v8/src/snapshot/partial-serializer.cc @@ -15,7 +15,9 @@ PartialSerializer::PartialSerializer( v8::SerializeEmbedderFieldsCallback callback) : Serializer(isolate), startup_serializer_(startup_serializer), - serialize_embedder_fields_(callback) { + serialize_embedder_fields_(callback), + rehashable_global_dictionary_(nullptr), + can_be_rehashed_(true) { InitializeCodeAddressMap(); } @@ -24,7 +26,7 @@ PartialSerializer::~PartialSerializer() { } void PartialSerializer::Serialize(Object** o, bool include_global_proxy) { - if ((*o)->IsContext()) { + if ((*o)->IsNativeContext()) { Context* context = Context::cast(*o); reference_map()->AddAttachedReference(context->global_proxy()); // The bootstrap snapshot has a code-stub context. When serializing the @@ -32,14 +34,18 @@ void PartialSerializer::Serialize(Object** o, bool include_global_proxy) { // and it's next context pointer may point to the code-stub context. Clear // it before serializing, it will get re-added to the context list // explicitly when it's loaded. - if (context->IsNativeContext()) { - context->set(Context::NEXT_CONTEXT_LINK, - isolate_->heap()->undefined_value()); - DCHECK(!context->global_object()->IsUndefined(context->GetIsolate())); - // Reset math random cache to get fresh random numbers. - context->set_math_random_index(Smi::kZero); - context->set_math_random_cache(isolate_->heap()->undefined_value()); - } + context->set(Context::NEXT_CONTEXT_LINK, + isolate_->heap()->undefined_value()); + DCHECK(!context->global_object()->IsUndefined(context->GetIsolate())); + // Reset math random cache to get fresh random numbers. + context->set_math_random_index(Smi::kZero); + context->set_math_random_cache(isolate_->heap()->undefined_value()); + DCHECK_NULL(rehashable_global_dictionary_); + rehashable_global_dictionary_ = + context->global_object()->global_dictionary(); + } else { + // We only do rehashing for native contexts. + can_be_rehashed_ = false; } VisitRootPointer(Root::kPartialSnapshotCache, o); SerializeDeferredObjects(); @@ -104,6 +110,8 @@ void PartialSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code, } } + if (obj->IsHashTable()) CheckRehashability(obj); + // Object has not yet been serialized. Serialize it here. ObjectSerializer serializer(this, obj, &sink_, how_to_code, where_to_point); serializer.Serialize(); @@ -152,5 +160,14 @@ void PartialSerializer::SerializeEmbedderFields() { sink_.Put(kSynchronize, "Finished with embedder fields data"); } +void PartialSerializer::CheckRehashability(HeapObject* table) { + DCHECK(table->IsHashTable()); + if (!can_be_rehashed_) return; + // We can only correctly rehash if the global dictionary is the only hash + // table that we deserialize. + if (table == rehashable_global_dictionary_) return; + can_be_rehashed_ = false; +} + } // namespace internal } // namespace v8 diff --git a/deps/v8/src/snapshot/partial-serializer.h b/deps/v8/src/snapshot/partial-serializer.h index 313a8000425c27..4b3035f9e544b0 100644 --- a/deps/v8/src/snapshot/partial-serializer.h +++ b/deps/v8/src/snapshot/partial-serializer.h @@ -23,6 +23,8 @@ class PartialSerializer : public Serializer { // Serialize the objects reachable from a single object pointer. void Serialize(Object** o, bool include_global_proxy); + bool can_be_rehashed() const { return can_be_rehashed_; } + private: void SerializeObject(HeapObject* o, HowToCode how_to_code, WhereToPoint where_to_point, int skip) override; @@ -31,9 +33,15 @@ class PartialSerializer : public Serializer { void SerializeEmbedderFields(); + void CheckRehashability(HeapObject* table); + StartupSerializer* startup_serializer_; List embedder_field_holders_; v8::SerializeEmbedderFieldsCallback serialize_embedder_fields_; + GlobalDictionary* rehashable_global_dictionary_; + // Indicates whether we only serialized hash tables that we can rehash. + // TODO(yangguo): generalize rehashing, and remove this flag. + bool can_be_rehashed_; DISALLOW_COPY_AND_ASSIGN(PartialSerializer); }; diff --git a/deps/v8/src/snapshot/snapshot-common.cc b/deps/v8/src/snapshot/snapshot-common.cc index 9350ec6b54010b..9f299e697ee8ad 100644 --- a/deps/v8/src/snapshot/snapshot-common.cc +++ b/deps/v8/src/snapshot/snapshot-common.cc @@ -41,6 +41,7 @@ bool Snapshot::Initialize(Isolate* isolate) { Vector startup_data = ExtractStartupData(blob); SnapshotData snapshot_data(startup_data); Deserializer deserializer(&snapshot_data); + deserializer.SetRehashability(ExtractRehashability(blob)); bool success = isolate->Init(&deserializer); if (FLAG_profile_deserialization) { double ms = timer.Elapsed().InMillisecondsF(); @@ -62,6 +63,7 @@ MaybeHandle Snapshot::NewContextFromSnapshot( ExtractContextData(blob, static_cast(context_index)); SnapshotData snapshot_data(context_data); Deserializer deserializer(&snapshot_data); + deserializer.SetRehashability(ExtractRehashability(blob)); MaybeHandle maybe_context = deserializer.DeserializePartial( isolate, global_proxy, embedder_fields_deserializer); @@ -98,7 +100,7 @@ void ProfileDeserialization(const SnapshotData* startup_snapshot, v8::StartupData Snapshot::CreateSnapshotBlob( const SnapshotData* startup_snapshot, - const List* context_snapshots) { + const List* context_snapshots, bool can_be_rehashed) { int num_contexts = context_snapshots->length(); int startup_snapshot_offset = StartupSnapshotOffset(num_contexts); int total_length = startup_snapshot_offset; @@ -111,6 +113,8 @@ v8::StartupData Snapshot::CreateSnapshotBlob( char* data = new char[total_length]; memcpy(data + kNumberOfContextsOffset, &num_contexts, kInt32Size); + int rehashability = can_be_rehashed ? 1 : 0; + memcpy(data + kRehashabilityOffset, &rehashability, kInt32Size); int payload_offset = StartupSnapshotOffset(num_contexts); int payload_length = startup_snapshot->RawData().length(); memcpy(data + payload_offset, startup_snapshot->RawData().start(), @@ -143,6 +147,13 @@ int Snapshot::ExtractNumContexts(const v8::StartupData* data) { return num_contexts; } +bool Snapshot::ExtractRehashability(const v8::StartupData* data) { + CHECK_LT(kRehashabilityOffset, data->raw_size); + int rehashability; + memcpy(&rehashability, data->data + kRehashabilityOffset, kInt32Size); + return rehashability != 0; +} + Vector Snapshot::ExtractStartupData(const v8::StartupData* data) { int num_contexts = ExtractNumContexts(data); int startup_offset = StartupSnapshotOffset(num_contexts); diff --git a/deps/v8/src/snapshot/snapshot.h b/deps/v8/src/snapshot/snapshot.h index 7d9082e6d21080..91d60cfa89d277 100644 --- a/deps/v8/src/snapshot/snapshot.h +++ b/deps/v8/src/snapshot/snapshot.h @@ -71,7 +71,7 @@ class Snapshot : public AllStatic { static v8::StartupData CreateSnapshotBlob( const SnapshotData* startup_snapshot, - const List* context_snapshots); + const List* context_snapshots, bool can_be_rehashed); #ifdef DEBUG static bool SnapshotIsValid(v8::StartupData* snapshot_blob); @@ -79,14 +79,16 @@ class Snapshot : public AllStatic { private: static int ExtractNumContexts(const v8::StartupData* data); + static bool ExtractRehashability(const v8::StartupData* data); static Vector ExtractStartupData(const v8::StartupData* data); static Vector ExtractContextData(const v8::StartupData* data, int index); // Snapshot blob layout: // [0] number of contexts N - // [1] offset to context 0 - // [2] offset to context 1 + // [1] rehashability + // [2] offset to context 0 + // [3] offset to context 1 // ... // ... offset to context N - 1 // ... startup snapshot data @@ -94,8 +96,10 @@ class Snapshot : public AllStatic { // ... context 1 snapshot data static const int kNumberOfContextsOffset = 0; + // TODO(yangguo): generalize rehashing, and remove this flag. + static const int kRehashabilityOffset = kNumberOfContextsOffset + kInt32Size; static const int kFirstContextOffsetOffset = - kNumberOfContextsOffset + kInt32Size; + kRehashabilityOffset + kInt32Size; static int StartupSnapshotOffset(int num_contexts) { return kFirstContextOffsetOffset + num_contexts * kInt32Size; diff --git a/deps/v8/src/snapshot/startup-serializer.cc b/deps/v8/src/snapshot/startup-serializer.cc index dfc02036d80587..89eaaf55e481da 100644 --- a/deps/v8/src/snapshot/startup-serializer.cc +++ b/deps/v8/src/snapshot/startup-serializer.cc @@ -16,7 +16,8 @@ StartupSerializer::StartupSerializer( : Serializer(isolate), clear_function_code_(function_code_handling == v8::SnapshotCreator::FunctionCodeHandling::kClear), - serializing_builtins_(false) { + serializing_builtins_(false), + can_be_rehashed_(true) { InitializeCodeAddressMap(); } @@ -78,6 +79,8 @@ void StartupSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code, isolate_->heap()->uninitialized_symbol()); } + if (obj->IsHashTable()) CheckRehashability(obj); + // Object has not yet been serialized. Serialize it here. ObjectSerializer object_serializer(this, obj, &sink_, how_to_code, where_to_point); @@ -189,5 +192,17 @@ bool StartupSerializer::RootShouldBeSkipped(int root_index) { serializing_immortal_immovables_roots_; } +void StartupSerializer::CheckRehashability(HeapObject* table) { + DCHECK(table->IsHashTable()); + if (!can_be_rehashed_) return; + // We can only correctly rehash if the four hash tables below are the only + // ones that we deserialize. + if (table == isolate_->heap()->empty_slow_element_dictionary()) return; + if (table == isolate_->heap()->empty_properties_dictionary()) return; + if (table == isolate_->heap()->weak_object_to_code_table()) return; + if (table == isolate_->heap()->string_table()) return; + can_be_rehashed_ = false; +} + } // namespace internal } // namespace v8 diff --git a/deps/v8/src/snapshot/startup-serializer.h b/deps/v8/src/snapshot/startup-serializer.h index 223e1c7bff3b34..c58454d0667008 100644 --- a/deps/v8/src/snapshot/startup-serializer.h +++ b/deps/v8/src/snapshot/startup-serializer.h @@ -29,6 +29,8 @@ class StartupSerializer : public Serializer { int PartialSnapshotCacheIndex(HeapObject* o); + bool can_be_rehashed() const { return can_be_rehashed_; } + private: class PartialCacheIndexMap { public: @@ -68,12 +70,18 @@ class StartupSerializer : public Serializer { // roots. In the second pass, we serialize the rest. bool RootShouldBeSkipped(int root_index); + void CheckRehashability(HeapObject* hashtable); + bool clear_function_code_; bool serializing_builtins_; bool serializing_immortal_immovables_roots_; std::bitset root_has_been_serialized_; PartialCacheIndexMap partial_cache_index_map_; List accessor_infos_; + // Indicates whether we only serialized hash tables that we can rehash. + // TODO(yangguo): generalize rehashing, and remove this flag. + bool can_be_rehashed_; + DISALLOW_COPY_AND_ASSIGN(StartupSerializer); }; diff --git a/deps/v8/src/transitions-inl.h b/deps/v8/src/transitions-inl.h index df28c2c9913d5a..694674dc0f2f97 100644 --- a/deps/v8/src/transitions-inl.h +++ b/deps/v8/src/transitions-inl.h @@ -106,7 +106,6 @@ int TransitionArray::SearchName(Name* name, int* out_insertion_index) { } -#ifdef DEBUG bool TransitionArray::IsSpecialTransition(Name* name) { if (!name->IsSymbol()) return false; Heap* heap = name->GetHeap(); @@ -115,7 +114,6 @@ bool TransitionArray::IsSpecialTransition(Name* name) { name == heap->elements_transition_symbol() || name == heap->strict_function_transition_symbol(); } -#endif int TransitionArray::CompareKeys(Name* key1, uint32_t hash1, PropertyKind kind1, diff --git a/deps/v8/src/transitions.cc b/deps/v8/src/transitions.cc index 5333fa6e25d28b..42d1c895079a7a 100644 --- a/deps/v8/src/transitions.cc +++ b/deps/v8/src/transitions.cc @@ -551,5 +551,47 @@ int TransitionArray::Search(PropertyKind kind, Name* name, if (transition == kNotFound) return kNotFound; return SearchDetails(transition, kind, attributes, out_insertion_index); } + +void TransitionArray::Sort() { + DisallowHeapAllocation no_gc; + // In-place insertion sort. + int length = number_of_transitions(); + for (int i = 1; i < length; i++) { + Name* key = GetKey(i); + Map* target = GetTarget(i); + PropertyKind kind = kData; + PropertyAttributes attributes = NONE; + if (!IsSpecialTransition(key)) { + PropertyDetails details = GetTargetDetails(key, target); + kind = details.kind(); + attributes = details.attributes(); + } + int j; + for (j = i - 1; j >= 0; j--) { + Name* temp_key = GetKey(j); + Map* temp_target = GetTarget(j); + PropertyKind temp_kind = kData; + PropertyAttributes temp_attributes = NONE; + if (!IsSpecialTransition(temp_key)) { + PropertyDetails details = GetTargetDetails(temp_key, temp_target); + temp_kind = details.kind(); + temp_attributes = details.attributes(); + } + int cmp = + CompareKeys(temp_key, temp_key->Hash(), temp_kind, temp_attributes, + key, key->Hash(), kind, attributes); + if (cmp > 0) { + SetKey(j + 1, temp_key); + SetTarget(j + 1, temp_target); + } else { + break; + } + } + SetKey(j + 1, key); + SetTarget(j + 1, target); + } + DCHECK(IsSortedNoDuplicates()); +} + } // namespace internal } // namespace v8 diff --git a/deps/v8/src/transitions.h b/deps/v8/src/transitions.h index e553a05ce406fa..e40bbb04682d71 100644 --- a/deps/v8/src/transitions.h +++ b/deps/v8/src/transitions.h @@ -190,15 +190,17 @@ class TransitionArray: public FixedArray { void TransitionArrayVerify(); #endif + void Sort(); + #ifdef DEBUG bool IsSortedNoDuplicates(int valid_entries = -1); static bool IsSortedNoDuplicates(Map* map); static bool IsConsistentWithBackPointers(Map* map); +#endif // Returns true for a non-property transitions like elements kind, observed // or frozen transitions. static inline bool IsSpecialTransition(Name* name); -#endif // Constant for denoting key was not found. static const int kNotFound = -1; diff --git a/deps/v8/test/cctest/heap/test-heap.cc b/deps/v8/test/cctest/heap/test-heap.cc index fc2b68e809a8f8..afa62b9f5727cf 100644 --- a/deps/v8/test/cctest/heap/test-heap.cc +++ b/deps/v8/test/cctest/heap/test-heap.cc @@ -6730,5 +6730,26 @@ HEAP_TEST(Regress5831) { CHECK(chunk->NeverEvacuate()); } +UNINITIALIZED_TEST(ReinitializeStringHashSeed) { + // Enable rehashing and create an isolate and context. + i::FLAG_rehash_snapshot = true; + for (int i = 1; i < 3; i++) { + i::FLAG_hash_seed = 1337 * i; + v8::Isolate::CreateParams create_params; + create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); + v8::Isolate* isolate = v8::Isolate::New(create_params); + { + v8::Isolate::Scope isolate_scope(isolate); + CHECK_EQ(1337 * i, + reinterpret_cast(isolate)->heap()->HashSeed()); + v8::HandleScope handle_scope(isolate); + v8::Local context = v8::Context::New(isolate); + CHECK(!context.IsEmpty()); + v8::Context::Scope context_scope(context); + } + isolate->Dispose(); + } +} + } // namespace internal } // namespace v8 diff --git a/deps/v8/test/cctest/test-serialize.cc b/deps/v8/test/cctest/test-serialize.cc index 058dd55d3cceae..1ab6e7bfd85731 100644 --- a/deps/v8/test/cctest/test-serialize.cc +++ b/deps/v8/test/cctest/test-serialize.cc @@ -2547,6 +2547,51 @@ TEST(SnapshotCreatorIncludeGlobalProxy) { delete[] blob.data; } +UNINITIALIZED_TEST(ReinitializeStringHashSeedNotRehashable) { + DisableAlwaysOpt(); + i::FLAG_rehash_snapshot = true; + i::FLAG_hash_seed = 42; + i::FLAG_allow_natives_syntax = true; + v8::StartupData blob; + { + v8::SnapshotCreator creator; + v8::Isolate* isolate = creator.GetIsolate(); + { + v8::HandleScope handle_scope(isolate); + v8::Local context = v8::Context::New(isolate); + v8::Context::Scope context_scope(context); + // Create dictionary mode object. + CompileRun( + "var a = {};" + "a.b = 1;" + "a.c = 2;" + "delete a.b;"); + ExpectInt32("a.c", 2); + creator.SetDefaultContext(context); + } + blob = + creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear); + } + + i::FLAG_hash_seed = 1337; + v8::Isolate::CreateParams create_params; + create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); + create_params.snapshot_blob = &blob; + v8::Isolate* isolate = v8::Isolate::New(create_params); + { + // Check that no rehashing has been performed. + CHECK_EQ(42, reinterpret_cast(isolate)->heap()->HashSeed()); + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope handle_scope(isolate); + v8::Local context = v8::Context::New(isolate); + CHECK(!context.IsEmpty()); + v8::Context::Scope context_scope(context); + ExpectInt32("a.c", 2); + } + isolate->Dispose(); + delete[] blob.data; +} + TEST(SerializationMemoryStats) { FLAG_profile_deserialization = true; FLAG_always_opt = false; diff --git a/deps/v8/test/mjsunit/regress/regress-5902.js b/deps/v8/test/mjsunit/regress/regress-5902.js index 034b6a7951bbcc..69dde4387a5b7f 100644 --- a/deps/v8/test/mjsunit/regress/regress-5902.js +++ b/deps/v8/test/mjsunit/regress/regress-5902.js @@ -58,6 +58,5 @@ assertEquals( 'Error.prototype', 'EvalError.prototype', 'RangeError.prototype', 'ReferenceError.prototype', 'SyntaxError.prototype', 'TypeError.prototype', 'URIError.prototype', - 'Map', 'Map.prototype.constructor', 'Set', 'Set.prototype.constructor' ], log);