diff --git a/common.gypi b/common.gypi index 3afe3c133f8d0f..6fe46076ff8def 100644 --- a/common.gypi +++ b/common.gypi @@ -36,7 +36,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.14', + 'v8_embedder_string': '-node.17', ##### V8 defaults for Node.js ##### diff --git a/deps/v8/include/v8.h b/deps/v8/include/v8.h index 5dcb6765608aac..7c6fdb748bf368 100644 --- a/deps/v8/include/v8.h +++ b/deps/v8/include/v8.h @@ -9788,6 +9788,11 @@ class V8_EXPORT V8 { /** * Helper class to create a snapshot data blob. + * + * The Isolate used by a SnapshotCreator is owned by it, and will be entered + * and exited by the constructor and destructor, respectively; The destructor + * will also destroy the Isolate. Experimental language features, including + * those available by default, are not available while creating a snapshot. */ class V8_EXPORT SnapshotCreator { public: @@ -9816,6 +9821,10 @@ class V8_EXPORT SnapshotCreator { SnapshotCreator(const intptr_t* external_references = nullptr, StartupData* existing_blob = nullptr); + /** + * Destroy the snapshot creator, and exit and dispose of the Isolate + * associated with it. + */ ~SnapshotCreator(); /** diff --git a/deps/v8/src/heap/factory.cc b/deps/v8/src/heap/factory.cc index 933a51425f9376..85760a31c91511 100644 --- a/deps/v8/src/heap/factory.cc +++ b/deps/v8/src/heap/factory.cc @@ -2761,8 +2761,12 @@ Handle Factory::NewUninitializedJSGlobalProxy(int size) { map->set_is_access_check_needed(true); map->set_may_have_interesting_symbols(true); LOG(isolate(), MapDetails(*map)); - return Handle::cast( + Handle proxy = Handle::cast( NewJSObjectFromMap(map, AllocationType::kYoung)); + // Create identity hash early in case there is any JS collection containing + // a global proxy key and needs to be rehashed after deserialization. + proxy->GetOrCreateIdentityHash(isolate()); + return proxy; } void Factory::ReinitializeJSGlobalProxy(Handle object, diff --git a/deps/v8/src/objects/heap-object.h b/deps/v8/src/objects/heap-object.h index b19d429320b5be..74fe664ca35d6d 100644 --- a/deps/v8/src/objects/heap-object.h +++ b/deps/v8/src/objects/heap-object.h @@ -191,7 +191,7 @@ class HeapObject : public Object { bool CanBeRehashed() const; // Rehash the object based on the layout inferred from its map. - void RehashBasedOnMap(ReadOnlyRoots root); + void RehashBasedOnMap(Isolate* isolate); // Layout description. #define HEAP_OBJECT_FIELDS(V) \ diff --git a/deps/v8/src/objects/js-collection.h b/deps/v8/src/objects/js-collection.h index 17f9c3e198ba8b..a0350726c02db7 100644 --- a/deps/v8/src/objects/js-collection.h +++ b/deps/v8/src/objects/js-collection.h @@ -30,6 +30,7 @@ class JSSet : public TorqueGeneratedJSSet { public: static void Initialize(Handle set, Isolate* isolate); static void Clear(Isolate* isolate, Handle set); + void Rehash(Isolate* isolate); // Dispatched behavior. DECL_PRINTER(JSSet) @@ -56,6 +57,7 @@ class JSMap : public TorqueGeneratedJSMap { public: static void Initialize(Handle map, Isolate* isolate); static void Clear(Isolate* isolate, Handle map); + void Rehash(Isolate* isolate); // Dispatched behavior. DECL_PRINTER(JSMap) diff --git a/deps/v8/src/objects/objects.cc b/deps/v8/src/objects/objects.cc index c45562965124cb..e457d6270617f3 100644 --- a/deps/v8/src/objects/objects.cc +++ b/deps/v8/src/objects/objects.cc @@ -2305,9 +2305,8 @@ bool HeapObject::NeedsRehashing() const { case TRANSITION_ARRAY_TYPE: return TransitionArray::cast(*this).number_of_entries() > 1; case ORDERED_HASH_MAP_TYPE: - return OrderedHashMap::cast(*this).NumberOfElements() > 0; case ORDERED_HASH_SET_TYPE: - return OrderedHashSet::cast(*this).NumberOfElements() > 0; + return false; // We'll rehash from the JSMap or JSSet referencing them. case NAME_DICTIONARY_TYPE: case GLOBAL_DICTIONARY_TYPE: case NUMBER_DICTIONARY_TYPE: @@ -2317,6 +2316,8 @@ bool HeapObject::NeedsRehashing() const { case SMALL_ORDERED_HASH_MAP_TYPE: case SMALL_ORDERED_HASH_SET_TYPE: case SMALL_ORDERED_NAME_DICTIONARY_TYPE: + case JS_MAP_TYPE: + case JS_SET_TYPE: return true; default: return false; @@ -2326,10 +2327,13 @@ bool HeapObject::NeedsRehashing() const { bool HeapObject::CanBeRehashed() const { DCHECK(NeedsRehashing()); switch (map().instance_type()) { + case JS_MAP_TYPE: + case JS_SET_TYPE: + return true; case ORDERED_HASH_MAP_TYPE: case ORDERED_HASH_SET_TYPE: + UNREACHABLE(); // We'll rehash from the JSMap or JSSet referencing them. case ORDERED_NAME_DICTIONARY_TYPE: - // TODO(yangguo): actually support rehashing OrderedHash{Map,Set}. return false; case NAME_DICTIONARY_TYPE: case GLOBAL_DICTIONARY_TYPE: @@ -2353,7 +2357,8 @@ bool HeapObject::CanBeRehashed() const { return false; } -void HeapObject::RehashBasedOnMap(ReadOnlyRoots roots) { +void HeapObject::RehashBasedOnMap(Isolate* isolate) { + ReadOnlyRoots roots = ReadOnlyRoots(isolate); switch (map().instance_type()) { case HASH_TABLE_TYPE: UNREACHABLE(); @@ -2385,6 +2390,17 @@ void HeapObject::RehashBasedOnMap(ReadOnlyRoots roots) { case SMALL_ORDERED_HASH_SET_TYPE: DCHECK_EQ(0, SmallOrderedHashSet::cast(*this).NumberOfElements()); break; + case ORDERED_HASH_MAP_TYPE: + case ORDERED_HASH_SET_TYPE: + UNREACHABLE(); // We'll rehash from the JSMap or JSSet referencing them. + case JS_MAP_TYPE: { + JSMap::cast(*this).Rehash(isolate); + break; + } + case JS_SET_TYPE: { + JSSet::cast(*this).Rehash(isolate); + break; + } case SMALL_ORDERED_NAME_DICTIONARY_TYPE: DCHECK_EQ(0, SmallOrderedNameDictionary::cast(*this).NumberOfElements()); break; @@ -7864,6 +7880,13 @@ void JSSet::Clear(Isolate* isolate, Handle set) { set->set_table(*table); } +void JSSet::Rehash(Isolate* isolate) { + Handle table_handle(OrderedHashSet::cast(table()), isolate); + Handle new_table = + OrderedHashSet::Rehash(isolate, table_handle).ToHandleChecked(); + set_table(*new_table); +} + void JSMap::Initialize(Handle map, Isolate* isolate) { Handle table = isolate->factory()->NewOrderedHashMap(); map->set_table(*table); @@ -7875,6 +7898,13 @@ void JSMap::Clear(Isolate* isolate, Handle map) { map->set_table(*table); } +void JSMap::Rehash(Isolate* isolate) { + Handle table_handle(OrderedHashMap::cast(table()), isolate); + Handle new_table = + OrderedHashMap::Rehash(isolate, table_handle).ToHandleChecked(); + set_table(*new_table); +} + void JSWeakCollection::Initialize(Handle weak_collection, Isolate* isolate) { Handle table = EphemeronHashTable::New(isolate, 0); diff --git a/deps/v8/src/objects/ordered-hash-table.cc b/deps/v8/src/objects/ordered-hash-table.cc index cbf3ba373b9c24..d3250bd92db8d2 100644 --- a/deps/v8/src/objects/ordered-hash-table.cc +++ b/deps/v8/src/objects/ordered-hash-table.cc @@ -194,6 +194,13 @@ HeapObject OrderedHashMap::GetEmpty(ReadOnlyRoots ro_roots) { return ro_roots.empty_ordered_hash_map(); } +template +MaybeHandle OrderedHashTable::Rehash( + Isolate* isolate, Handle table) { + return OrderedHashTable::Rehash(isolate, table, + table->Capacity()); +} + template MaybeHandle OrderedHashTable::Rehash( Isolate* isolate, Handle table, int new_capacity) { @@ -250,6 +257,20 @@ MaybeHandle OrderedHashSet::Rehash(Isolate* isolate, new_capacity); } +MaybeHandle OrderedHashSet::Rehash( + Isolate* isolate, Handle table) { + return OrderedHashTable< + OrderedHashSet, OrderedHashSet::kEntrySizeWithoutChain>::Rehash(isolate, + table); +} + +MaybeHandle OrderedHashMap::Rehash( + Isolate* isolate, Handle table) { + return OrderedHashTable< + OrderedHashMap, OrderedHashMap::kEntrySizeWithoutChain>::Rehash(isolate, + table); +} + MaybeHandle OrderedHashMap::Rehash(Isolate* isolate, Handle table, int new_capacity) { diff --git a/deps/v8/src/objects/ordered-hash-table.h b/deps/v8/src/objects/ordered-hash-table.h index b587960432caf5..5f3c45a110aa48 100644 --- a/deps/v8/src/objects/ordered-hash-table.h +++ b/deps/v8/src/objects/ordered-hash-table.h @@ -138,6 +138,7 @@ class OrderedHashTable : public FixedArray { // The extra +1 is for linking the bucket chains together. static const int kEntrySize = entrysize + 1; + static const int kEntrySizeWithoutChain = entrysize; static const int kChainOffset = entrysize; static const int kNotFound = -1; @@ -200,6 +201,8 @@ class OrderedHashTable : public FixedArray { static MaybeHandle Allocate( Isolate* isolate, int capacity, AllocationType allocation = AllocationType::kYoung); + + static MaybeHandle Rehash(Isolate* isolate, Handle table); static MaybeHandle Rehash(Isolate* isolate, Handle table, int new_capacity); @@ -244,6 +247,8 @@ class V8_EXPORT_PRIVATE OrderedHashSet static MaybeHandle Rehash(Isolate* isolate, Handle table, int new_capacity); + static MaybeHandle Rehash(Isolate* isolate, + Handle table); static MaybeHandle Allocate( Isolate* isolate, int capacity, AllocationType allocation = AllocationType::kYoung); @@ -273,6 +278,8 @@ class V8_EXPORT_PRIVATE OrderedHashMap static MaybeHandle Rehash(Isolate* isolate, Handle table, int new_capacity); + static MaybeHandle Rehash(Isolate* isolate, + Handle table); Object ValueAt(int entry); // This takes and returns raw Address values containing tagged Object diff --git a/deps/v8/src/snapshot/deserializer.cc b/deps/v8/src/snapshot/deserializer.cc index fdb4babe0712b3..761ece803745f1 100644 --- a/deps/v8/src/snapshot/deserializer.cc +++ b/deps/v8/src/snapshot/deserializer.cc @@ -70,7 +70,7 @@ void Deserializer::Initialize(Isolate* isolate) { void Deserializer::Rehash() { DCHECK(can_rehash() || deserializing_user_code()); for (HeapObject item : to_rehash_) { - item.RehashBasedOnMap(ReadOnlyRoots(isolate_)); + item.RehashBasedOnMap(isolate_); } } @@ -130,6 +130,14 @@ void Deserializer::DeserializeDeferredObjects() { } } } + + // When the deserialization of maps are deferred, they will be created + // as filler maps, and we postpone the post processing until the maps + // are also deserialized. + for (const auto& pair : fillers_to_post_process_) { + DCHECK(!pair.first.IsFiller()); + PostProcessNewObject(pair.first, pair.second); + } } void Deserializer::LogNewObjectEvents() { @@ -201,7 +209,11 @@ HeapObject Deserializer::PostProcessNewObject(HeapObject obj, DisallowHeapAllocation no_gc; if ((FLAG_rehash_snapshot && can_rehash_) || deserializing_user_code()) { - if (obj.IsString()) { + if (obj.IsFiller()) { + DCHECK_EQ(fillers_to_post_process_.find(obj), + fillers_to_post_process_.end()); + fillers_to_post_process_.insert({obj, space}); + } else if (obj.IsString()) { // Uninitialize hash field as we need to recompute the hash. String string = String::cast(obj); string.set_hash_field(String::kEmptyHashField); diff --git a/deps/v8/src/snapshot/deserializer.h b/deps/v8/src/snapshot/deserializer.h index 62814a881ae9e3..153fda4a902cf2 100644 --- a/deps/v8/src/snapshot/deserializer.h +++ b/deps/v8/src/snapshot/deserializer.h @@ -107,6 +107,7 @@ class V8_EXPORT_PRIVATE Deserializer : public SerializerDeserializer { } std::shared_ptr backing_store(size_t i) { + DCHECK_LT(i, backing_stores_.size()); return backing_stores_[i]; } @@ -193,6 +194,11 @@ class V8_EXPORT_PRIVATE Deserializer : public SerializerDeserializer { // TODO(6593): generalize rehashing, and remove this flag. bool can_rehash_; std::vector to_rehash_; + // Store the objects whose maps are deferred and thus initialized as filler + // maps during deserialization, so that they can be processed later when the + // maps become available. + std::unordered_map + fillers_to_post_process_; #ifdef DEBUG uint32_t num_api_references_; diff --git a/deps/v8/src/snapshot/object-deserializer.cc b/deps/v8/src/snapshot/object-deserializer.cc index cca8d4224099f8..6b02f22e6582f8 100644 --- a/deps/v8/src/snapshot/object-deserializer.cc +++ b/deps/v8/src/snapshot/object-deserializer.cc @@ -48,9 +48,9 @@ MaybeHandle ObjectDeserializer::Deserialize(Isolate* isolate) { LinkAllocationSites(); LogNewMapEvents(); result = handle(HeapObject::cast(root), isolate); - Rehash(); allocator()->RegisterDeserializedObjectsForBlackAllocation(); } + Rehash(); CommitPostProcessedObjects(); return scope.CloseAndEscape(result); } diff --git a/deps/v8/src/snapshot/partial-deserializer.cc b/deps/v8/src/snapshot/partial-deserializer.cc index 8a215b6b871e94..e15cf6c678fbb8 100644 --- a/deps/v8/src/snapshot/partial-deserializer.cc +++ b/deps/v8/src/snapshot/partial-deserializer.cc @@ -57,12 +57,12 @@ MaybeHandle PartialDeserializer::Deserialize( // new code, which also has to be flushed from instruction cache. CHECK_EQ(start_address, code_space->top()); - if (FLAG_rehash_snapshot && can_rehash()) Rehash(); LogNewMapEvents(); result = handle(root, isolate); } + if (FLAG_rehash_snapshot && can_rehash()) Rehash(); SetupOffHeapArrayBufferBackingStores(); return result; diff --git a/deps/v8/src/snapshot/serializer-common.cc b/deps/v8/src/snapshot/serializer-common.cc index 9218d4eaa92969..a843771fdbb37e 100644 --- a/deps/v8/src/snapshot/serializer-common.cc +++ b/deps/v8/src/snapshot/serializer-common.cc @@ -76,7 +76,8 @@ ExternalReferenceEncoder::Value ExternalReferenceEncoder::Encode( if (maybe_index.IsNothing()) { void* addr = reinterpret_cast(address); v8::base::OS::PrintError("Unknown external reference %p.\n", addr); - v8::base::OS::PrintError("%s", ExternalReferenceTable::ResolveSymbol(addr)); + v8::base::OS::PrintError("%s\n", + ExternalReferenceTable::ResolveSymbol(addr)); v8::base::OS::Abort(); } Value result(maybe_index.FromJust()); @@ -125,7 +126,14 @@ void SerializerDeserializer::Iterate(Isolate* isolate, RootVisitor* visitor) { } bool SerializerDeserializer::CanBeDeferred(HeapObject o) { - return !o.IsString() && !o.IsScript() && !o.IsJSTypedArray(); + // ArrayBuffer instances are serialized by first re-assigning a index + // to the backing store field, then serializing the object, and then + // storing the actual backing store address again (and the same for the + // ArrayBufferExtension). If serialization of the object itself is deferred, + // the real backing store address is written into the snapshot, which cannot + // be processed when deserializing. + return !o.IsString() && !o.IsScript() && !o.IsJSTypedArray() && + !o.IsJSArrayBuffer(); } void SerializerDeserializer::RestoreExternalReferenceRedirectors( diff --git a/deps/v8/src/snapshot/startup-serializer.cc b/deps/v8/src/snapshot/startup-serializer.cc index 8b95ca6b6ac9a1..141fcfb23f1746 100644 --- a/deps/v8/src/snapshot/startup-serializer.cc +++ b/deps/v8/src/snapshot/startup-serializer.cc @@ -193,6 +193,7 @@ void SerializedHandleChecker::VisitRootPointers(Root root, PrintF("%s handle not serialized: ", root == Root::kGlobalHandles ? "global" : "eternal"); (*p).Print(); + PrintF("\n"); ok_ = false; } } diff --git a/deps/v8/test/cctest/test-serialize.cc b/deps/v8/test/cctest/test-serialize.cc index 7ce8ef71529658..b7590cd0217cff 100644 --- a/deps/v8/test/cctest/test-serialize.cc +++ b/deps/v8/test/cctest/test-serialize.cc @@ -3778,7 +3778,7 @@ UNINITIALIZED_TEST(SnapshotCreatorIncludeGlobalProxy) { FreeCurrentEmbeddedBlob(); } -UNINITIALIZED_TEST(ReinitializeHashSeedNotRehashable) { +UNINITIALIZED_TEST(ReinitializeHashSeedJSCollectionRehashable) { DisableAlwaysOpt(); i::FLAG_rehash_snapshot = true; i::FLAG_hash_seed = 42; @@ -3796,13 +3796,18 @@ UNINITIALIZED_TEST(ReinitializeHashSeedNotRehashable) { CompileRun( "var m = new Map();" "m.set('a', 1);" - "m.set('b', 2);"); + "m.set('b', 2);" + "var s = new Set();" + "s.add(1);" + "s.add(globalThis);"); ExpectInt32("m.get('b')", 2); + ExpectTrue("s.has(1)"); + ExpectTrue("s.has(globalThis)"); creator.SetDefaultContext(context); } blob = creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear); - CHECK(!blob.CanBeRehashed()); + CHECK(blob.CanBeRehashed()); } ReadOnlyHeap::ClearSharedHeapForTest(); @@ -3812,8 +3817,8 @@ UNINITIALIZED_TEST(ReinitializeHashSeedNotRehashable) { create_params.snapshot_blob = &blob; v8::Isolate* isolate = v8::Isolate::New(create_params); { - // Check that no rehashing has been performed. - CHECK_EQ(static_cast(42), + // Check that rehashing has been performed. + CHECK_EQ(static_cast(1337), HashSeed(reinterpret_cast(isolate))); v8::Isolate::Scope isolate_scope(isolate); v8::HandleScope handle_scope(isolate); @@ -3821,6 +3826,8 @@ UNINITIALIZED_TEST(ReinitializeHashSeedNotRehashable) { CHECK(!context.IsEmpty()); v8::Context::Scope context_scope(context); ExpectInt32("m.get('b')", 2); + ExpectTrue("s.has(1)"); + ExpectTrue("s.has(globalThis)"); } isolate->Dispose(); delete[] blob.data;