From 99c41090bf62b05f64ba96527813ea66c233a149 Mon Sep 17 00:00:00 2001 From: "Node.js GitHub Bot" Date: Wed, 21 Oct 2026 15:57:22 +0000 Subject: [PATCH] deps: patch V8 to undefined Refs: https://github.com/v8/v8/compare/15.6.75.13...15.6.75.21 --- deps/v8/include/v8-version.h | 2 +- deps/v8/src/ast/scopes.cc | 4 +- deps/v8/src/ast/scopes.h | 9 ++ deps/v8/src/codegen/compiler.cc | 9 +- deps/v8/src/codegen/compiler.h | 2 +- deps/v8/src/ic/ic.cc | 31 ++-- deps/v8/src/maglev/maglev-graph-builder.cc | 15 +- .../src/maglev/maglev-known-node-aspects.cc | 54 +++++-- .../v8/src/maglev/maglev-known-node-aspects.h | 7 +- .../maglev-phi-representation-selector.cc | 31 ++-- .../maglev-phi-representation-selector.h | 5 +- deps/v8/src/maglev/maglev-range-analysis.h | 29 ++++ deps/v8/src/objects/map-updater.cc | 5 +- .../v8/src/objects/shared-function-info-inl.h | 3 + deps/v8/src/objects/shared-function-info.cc | 4 + deps/v8/src/objects/shared-function-info.h | 4 + deps/v8/src/objects/shared-function-info.tq | 1 + deps/v8/src/objects/transitions-inl.h | 8 + deps/v8/src/objects/transitions.cc | 28 ++-- deps/v8/src/objects/transitions.h | 10 +- deps/v8/src/parsing/parse-info.cc | 2 + deps/v8/src/parsing/parse-info.h | 1 + deps/v8/src/parsing/parser-base.h | 16 +- deps/v8/src/parsing/parser.cc | 13 +- deps/v8/src/snapshot/code-serializer.cc | 89 +++++++---- deps/v8/src/snapshot/code-serializer.h | 54 ++++--- ...mber-init-reparse-await-yield-arguments.js | 82 ++++++++++ .../test/mjsunit/maglev/regress-564326894.js | 29 ++++ .../mjsunit/maglev/regress-569286602-1.js | 24 +++ .../mjsunit/maglev/regress-569286602-2.js | 24 +++ .../test/mjsunit/regress/regress-566091009.js | 69 +++++++++ .../mjsunit/regress/regress-570003644-1.js | 110 +++++++++++++ .../mjsunit/regress/regress-570003644-2.js | 111 ++++++++++++++ .../mjsunit/regress/regress-570203091-1.js | 29 ++++ .../mjsunit/regress/regress-570203091-2.js | 35 +++++ .../unittests/api/deserialize-unittest.cc | 144 ++++++++++++++++++ 36 files changed, 963 insertions(+), 130 deletions(-) create mode 100644 deps/v8/test/mjsunit/es6/regress-class-member-init-reparse-await-yield-arguments.js create mode 100644 deps/v8/test/mjsunit/maglev/regress-564326894.js create mode 100644 deps/v8/test/mjsunit/maglev/regress-569286602-1.js create mode 100644 deps/v8/test/mjsunit/maglev/regress-569286602-2.js create mode 100644 deps/v8/test/mjsunit/regress/regress-566091009.js create mode 100644 deps/v8/test/mjsunit/regress/regress-570003644-1.js create mode 100644 deps/v8/test/mjsunit/regress/regress-570003644-2.js create mode 100644 deps/v8/test/mjsunit/regress/regress-570203091-1.js create mode 100644 deps/v8/test/mjsunit/regress/regress-570203091-2.js diff --git a/deps/v8/include/v8-version.h b/deps/v8/include/v8-version.h index d47b8f7f5c8..89e01f7bf5f 100644 --- a/deps/v8/include/v8-version.h +++ b/deps/v8/include/v8-version.h @@ -11,7 +11,7 @@ #define V8_MAJOR_VERSION 15 #define V8_MINOR_VERSION 6 #define V8_BUILD_NUMBER 75 -#define V8_PATCH_LEVEL 13 +#define V8_PATCH_LEVEL 21 // Use 1 for candidates and 0 otherwise. // (Boolean macro values are not supported by all preprocessors.) diff --git a/deps/v8/src/ast/scopes.cc b/deps/v8/src/ast/scopes.cc index 7713cd8103d..725f007719d 100644 --- a/deps/v8/src/ast/scopes.cc +++ b/deps/v8/src/ast/scopes.cc @@ -345,6 +345,7 @@ void DeclarationScope::SetDefaults() { is_module_scope()); set_needs_private_name_context_chain_recalc(false); set_class_scope_has_private_brand(false); + set_class_initializer_allows_await(false); receiver_ = nullptr; new_target_ = nullptr; function_ = nullptr; @@ -1575,7 +1576,8 @@ bool Scope::NeedsScopeInfo() const { } bool Scope::ShouldBanArguments() { - return GetReceiverScope()->should_ban_arguments(); + return GetReceiverScope()->should_ban_arguments() && + !GetClosureScope()->from_scope_info(); } DeclarationScope* Scope::GetReceiverScope() { diff --git a/deps/v8/src/ast/scopes.h b/deps/v8/src/ast/scopes.h index f85e7420968..fa13cc9a176 100644 --- a/deps/v8/src/ast/scopes.h +++ b/deps/v8/src/ast/scopes.h @@ -1387,6 +1387,13 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope { return ClassScopeHasPrivateBrandField::decode(flags_); } + bool class_initializer_allows_await() const { + return ClassInitializerAllowsAwaitField::decode(flags_); + } + void set_class_initializer_allows_await(bool value) { + flags_ = ClassInitializerAllowsAwaitField::update(flags_, value); + } + private: V8_INLINE void AllocateParameter(Variable* var, int index); @@ -1430,6 +1437,8 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope { HasThisDeclarationField::Next; using ClassScopeHasPrivateBrandField = NeedsPrivateNameContextChainRecalcField::Next; + using ClassInitializerAllowsAwaitField = + ClassScopeHasPrivateBrandField::Next; #if DEBUG bool is_being_lazily_parsed_; diff --git a/deps/v8/src/codegen/compiler.cc b/deps/v8/src/codegen/compiler.cc index fbcf0d8ba77..0a3599ee41c 100644 --- a/deps/v8/src/codegen/compiler.cc +++ b/deps/v8/src/codegen/compiler.cc @@ -2852,8 +2852,8 @@ void BackgroundDeserializeTask::Run() { LocalHandleScope handle_scope(&isolate); DirectHandle inner_result; - off_thread_data_ = - CodeSerializer::StartDeserializeOffThread(&isolate, &cached_data_); + CodeSerializer::StartDeserializeOffThread(&isolate, &cached_data_, + &off_thread_data_); if (v8_flags.enable_slow_asserts && off_thread_data_.HasResult()) { #ifdef ENABLE_SLOW_DCHECKS MergeAssumptionChecker checker(&isolate, false); @@ -2866,6 +2866,11 @@ void BackgroundDeserializeTask::SourceTextAvailable( Isolate* isolate, Handle source_text, const ScriptDetails& script_details) { DCHECK_EQ(isolate, isolate_for_local_isolate_); + if (!CodeSerializer::NotifySourceTextAvailable(isolate, &off_thread_data_, + &cached_data_, source_text, + script_details)) { + return; + } LanguageMode language_mode = construct_language_mode(v8_flags.use_strict); background_merge_task_.SetUpOnMainThread(isolate, source_text, script_details, language_mode); diff --git a/deps/v8/src/codegen/compiler.h b/deps/v8/src/codegen/compiler.h index 7f8c3152e8a..cfa6af01010 100644 --- a/deps/v8/src/codegen/compiler.h +++ b/deps/v8/src/codegen/compiler.h @@ -719,7 +719,7 @@ class V8_EXPORT_PRIVATE BackgroundDeserializeTask { private: Isolate* isolate_for_local_isolate_; AlignedCachedData cached_data_; - CodeSerializer::OffThreadDeserializeData off_thread_data_; + OffThreadDeserializeData off_thread_data_; BackgroundMergeTask background_merge_task_; TimedHistogram* timer_; int64_t background_time_in_microseconds_ = 0; diff --git a/deps/v8/src/ic/ic.cc b/deps/v8/src/ic/ic.cc index 7ae7d8f99d0..681dac659f1 100644 --- a/deps/v8/src/ic/ic.cc +++ b/deps/v8/src/ic/ic.cc @@ -1760,17 +1760,6 @@ KeyedAccessLoadMode GetUpdatedLoadModeForMap(Isolate* isolate, Handle KeyedLoadIC::LoadElementHandler( DirectHandle receiver_map, KeyedAccessLoadMode new_load_mode, MaybeDirectHandle maybe_transition_target) { - // Has a getter interceptor, or is any has and has a query interceptor. - if (receiver_map->has_indexed_interceptor() && - (receiver_map->GetIndexedInterceptor()->has_getter() || - (IsAnyHas() && receiver_map->GetIndexedInterceptor()->has_query())) && - !receiver_map->GetIndexedInterceptor()->non_masking()) { - // TODO(jgruber): Update counter name. - TRACE_HANDLER_STATS(isolate(), KeyedLoadIC_LoadIndexedInterceptorStub); - return IsAnyHas() ? BUILTIN_CODE(isolate(), HasIndexedInterceptorIC) - : BUILTIN_CODE(isolate(), LoadIndexedInterceptorIC); - } - InstanceType instance_type = receiver_map->instance_type(); if (instance_type < FIRST_NONSTRING_TYPE) { TRACE_HANDLER_STATS(isolate(), KeyedLoadIC_LoadIndexedStringDH); @@ -1784,6 +1773,23 @@ Handle KeyedLoadIC::LoadElementHandler( if (instance_type == JS_PROXY_TYPE) { return LoadHandler::LoadProxy(isolate()); } + if (receiver_map->is_access_check_needed()) { + TRACE_HANDLER_STATS(isolate(), KeyedLoadIC_SlowStub); + return LoadHandler::LoadSlow(isolate()); + } + + // Has a getter interceptor, or is any has and has a query interceptor. + if (receiver_map->has_indexed_interceptor() && + (receiver_map->GetIndexedInterceptor()->has_getter() || + (IsAnyHas() && receiver_map->GetIndexedInterceptor()->has_query())) && + !receiver_map->GetIndexedInterceptor()->non_masking()) { + DCHECK(!receiver_map->is_access_check_needed()); + // TODO(jgruber): Update counter name. + TRACE_HANDLER_STATS(isolate(), KeyedLoadIC_LoadIndexedInterceptorStub); + return IsAnyHas() ? BUILTIN_CODE(isolate(), HasIndexedInterceptorIC) + : BUILTIN_CODE(isolate(), LoadIndexedInterceptorIC); + } + #if V8_ENABLE_WEBASSEMBLY if (InstanceTypeChecker::IsWasmObject(instance_type)) { // TODO(jgruber): Update counter name. @@ -2870,7 +2876,8 @@ void KeyedStoreIC::UpdateStoreElement(Handle receiver_map, Handle KeyedStoreIC::StoreElementHandler( DirectHandle receiver_map, KeyedAccessStoreMode store_mode, MaybeDirectHandle> prev_validity_cell) { - if (!IsJSObjectMap(*receiver_map)) { + if (!IsJSObjectMap(*receiver_map) || receiver_map->is_access_check_needed() || + receiver_map->IsMapInArrayPrototypeChain(isolate())) { // DefineKeyedOwnIC, which is used to define computed fields in instances, // should handled by the slow stub below instead of the proxy stub. if (IsJSProxyMap(*receiver_map) && !IsDefineKeyedOwnIC()) { diff --git a/deps/v8/src/maglev/maglev-graph-builder.cc b/deps/v8/src/maglev/maglev-graph-builder.cc index b0bca8d4128..ca199bab0fa 100644 --- a/deps/v8/src/maglev/maglev-graph-builder.cc +++ b/deps/v8/src/maglev/maglev-graph-builder.cc @@ -9110,16 +9110,21 @@ MaglevGraphBuilder::BuildJSArrayBuiltinMapSwitchOnElementsKind( sub_graph.GotoOrTrim(&*object_case); sub_graph.Bind(&*object_case); } - any_successful |= (!build_kind_specific(kind).IsDoneWithAbort()); + ReduceResult result = build_kind_specific(kind); if (IsSmiElementsKind(kind) && make_smi_fallthrough_to_object) { // After building the SMI specific parts (see build_kind_specific call // above), jump to the beginning of the case for the object elements // kind. DCHECK(object_case.has_value()); sub_graph.GotoOrTrim(&*object_case); + + // Whether the Smi case is successful depends on whether the object case + // is successful, so don't update any_successful here. } else { DCHECK(do_return.has_value()); sub_graph.GotoOrTrim(&*do_return); + + any_successful |= !result.IsDoneWithAbort(); } sub_graph.Bind(&check_next_map); } else { @@ -16971,11 +16976,11 @@ void MaglevGraphBuilder::ProcessMergePointPredecessors( ResetBuilderCachedState(); if (merge_state.is_loop()) { - DCHECK_EQ(merge_state.predecessors_so_far(), - merge_state.predecessor_count() - 1); + CHECK_EQ(merge_state.predecessors_so_far(), + merge_state.predecessor_count() - 1); } else { - DCHECK_EQ(merge_state.predecessors_so_far(), - merge_state.predecessor_count()); + CHECK_EQ(merge_state.predecessors_so_far(), + merge_state.predecessor_count()); } if (merge_state.predecessor_count() == 1) { diff --git a/deps/v8/src/maglev/maglev-known-node-aspects.cc b/deps/v8/src/maglev/maglev-known-node-aspects.cc index f4abfca2495..63895d1f5f0 100644 --- a/deps/v8/src/maglev/maglev-known-node-aspects.cc +++ b/deps/v8/src/maglev/maglev-known-node-aspects.cc @@ -652,22 +652,22 @@ void KnownNodeAspects::ClearUnstableNodeAspectsForStoreMap( StoreMap* node, bool is_tracing_enabled) { if (!node->is_transitioning()) return; - if (NodeInfo* node_info = TryGetInfoFor(node->ValueInput().node())) { - if (node_info->possible_maps_are_known() && !node_info->maps_are_stale() && - node_info->possible_maps().size() == 1) { - compiler::MapRef old_map = node_info->possible_maps().at(0); - auto MaybeAliases = [&](compiler::MapRef map) -> bool { - return map.equals(old_map); - }; - // Mark aliasing maps as stale. - if (MarkMapsStaleIfAny(MaybeAliases)) { - if (V8_UNLIKELY(v8_flags.trace_maglev_kna && is_tracing_enabled)) { - std::cout << kRed << "[KNA] StoreMap: Invalidate alias " - << Brief(*old_map.object()) << kReset << std::endl; - } - } - return; - } + if (MarkSingleMapAsStale(node->ValueInput().node(), is_tracing_enabled)) { + return; + } + + // TODO(olivf): Only invalidate nodes with the same type. + OnSideEffect(); + if (V8_UNLIKELY(v8_flags.trace_maglev_kna && is_tracing_enabled)) { + std::cout << kRed << "[KNA] StoreMap: Invalidate all unstable maps" + << kReset << std::endl; + } +} + +void KnownNodeAspects::ClearUnstableNodeAspectsForMigration( + Node* node, bool is_tracing_enabled) { + if (MarkSingleMapAsStale(node->input_node(0), is_tracing_enabled)) { + return; } // TODO(olivf): Only invalidate nodes with the same type. @@ -1009,6 +1009,28 @@ KnownNodeAspects::ContextStoreResult KnownNodeAspects::RecordContextSlotStore( return {ContextStoreResult::kSetNewValue, std::move(aliased_slots)}; } +bool KnownNodeAspects::MarkSingleMapAsStale(ValueNode* node, + bool is_tracing_enabled) { + if (NodeInfo* node_info = TryGetInfoFor(node)) { + if (node_info->possible_maps_are_known() && !node_info->maps_are_stale() && + node_info->possible_maps().size() == 1) { + compiler::MapRef old_map = node_info->possible_maps().at(0); + auto MaybeAliases = [&](compiler::MapRef map) -> bool { + return map.equals(old_map); + }; + // Mark aliasing maps as stale. + if (MarkMapsStaleIfAny(MaybeAliases)) { + if (V8_UNLIKELY(v8_flags.trace_maglev_kna && is_tracing_enabled)) { + std::cout << kRed << "[KNA]: MarkSingleMapAsStale: Invalidate alias " + << Brief(*old_map.object()) << kReset << std::endl; + } + } + return true; + } + } + return false; +} + #ifdef DEBUG bool IsStringRootIndex(RootIndex index) { if (index > RootIndex::kLastReadOnlyRoot) return false; diff --git a/deps/v8/src/maglev/maglev-known-node-aspects.h b/deps/v8/src/maglev/maglev-known-node-aspects.h index b7f03911366..dd1ce8cc385 100644 --- a/deps/v8/src/maglev/maglev-known-node-aspects.h +++ b/deps/v8/src/maglev/maglev-known-node-aspects.h @@ -407,6 +407,8 @@ class KnownNodeAspects { void ClearUnstableNodeAspectsForStoreMap(StoreMap* node, bool is_tracing_enabled); + void ClearUnstableNodeAspectsForMigration(Node* node, + bool is_tracing_enabled); void ClearUnstableNodeAspectsForElementsTransition( const ZoneVector& transition_sources, bool is_tracing_enabled); @@ -824,8 +826,7 @@ class KnownNodeAspects { std::is_same_v || std::is_same_v) { - // These instructions only migrate representations of values, not the - // values themselves, so cached values are still valid. + ClearUnstableNodeAspectsForMigration(node, is_tracing_enabled); } else if constexpr (std::is_same_v) { ClearUnstableNodeAspectsForStoreMap(node, is_tracing_enabled); } else if constexpr (std::is_same_v || @@ -910,6 +911,8 @@ class KnownNodeAspects { SmallZoneVector ClearAliasedContextSlotsFor( Graph* graph, ValueNode* context, int offset, ValueNode* value); + bool MarkSingleMapAsStale(ValueNode* node, bool is_tracing_enabled); + static constexpr uint32_t kEffectEpochForPureInstructions = std::numeric_limits::max(); static constexpr uint32_t kEffectEpochOverflow = diff --git a/deps/v8/src/maglev/maglev-phi-representation-selector.cc b/deps/v8/src/maglev/maglev-phi-representation-selector.cc index eff255ed43c..5b44744d9e9 100644 --- a/deps/v8/src/maglev/maglev-phi-representation-selector.cc +++ b/deps/v8/src/maglev/maglev-phi-representation-selector.cc @@ -1074,7 +1074,7 @@ ValueNode* MaglevPhiRepresentationSelector::GetReplacementForPhiInputConversion( } ProcessResult MaglevPhiRepresentationSelector::UpdateUntaggingOfPhi( - Phi* phi, ValueNode* old_untagging) { + Phi* phi, ValueNode* old_untagging, const ProcessingState* state) { DCHECK_EQ(old_untagging->input_count(), 1); DCHECK(old_untagging->input(0).node()->Is()); @@ -1192,24 +1192,27 @@ ProcessResult MaglevPhiRepresentationSelector::UpdateUntaggingOfPhi( if (old_untagging->Is()) { conversion_is_truncating_float64 = true; } else if (old_untagging->Is()) { - if (from_repr == ValueRepresentation::kFloat64) { - // If {from_repr} is Float64, then the "CheckedNumber" part is - // guaranteed to pass and this operation just truncates its value to - // Int32. - conversion_is_truncating_float64 = true; - } else { - DCHECK_EQ(from_repr, ValueRepresentation::kHoleyFloat64); + if (from_repr == ValueRepresentation::kHoleyFloat64) { const auto* truncate = old_untagging->Cast(); // A HoleyFloat64 hole really means Undefined rather than the_hole: // whenever it gets rematerialized, it will always be rematerialized as - // Undefined. So, we can use a truncating conversion as long as the - // original assumed input type was allowing truncating Undefined. - // Assumptions without Undefined need to deopt for Hole/Undefined - // ==> not truncating. - conversion_is_truncating_float64 = !NodeTypeIs( - truncate->assumed_input_type(), NodeType::kNumberOrBoolean); + // Undefined. Assumptions without Undefined need to deopt on + // Hole/Undefined before truncating the Float64 value to Int32. + if (!NodeTypeCanBe(truncate->assumed_input_type(), + NodeType::kUndefined)) { + eager_deopt_frame_ = &old_untagging->eager_deopt_info()->top_frame(); + AddNewNodeNoInputConversion( + reducer_.current_block(), + BasicBlockPosition::At(state->node_index()), {phi}); +#ifdef DEBUG + eager_deopt_frame_ = nullptr; +#endif // DEBUG + } + } else { + DCHECK_EQ(from_repr, ValueRepresentation::kFloat64); } + conversion_is_truncating_float64 = true; } Opcode needed_conversion = GetOpcodeForConversion( diff --git a/deps/v8/src/maglev/maglev-phi-representation-selector.h b/deps/v8/src/maglev/maglev-phi-representation-selector.h index 5ecca6b95f2..3007727a099 100644 --- a/deps/v8/src/maglev/maglev-phi-representation-selector.h +++ b/deps/v8/src/maglev/maglev-phi-representation-selector.h @@ -206,7 +206,7 @@ class MaglevPhiRepresentationSelector { // removed altogether (or rather, replaced by an identity node). return UpdateUntaggingOfPhi( node->NodeBase::input(0).node()->template Cast(), - node->template Cast()); + node->template Cast(), state); } return ProcessResult::kContinue; } @@ -273,7 +273,8 @@ class MaglevPhiRepresentationSelector { // Updates {old_untagging} to reflect that its Phi input has been untagged and // that a different conversion is now needed. - ProcessResult UpdateUntaggingOfPhi(Phi* phi, ValueNode* old_untagging); + ProcessResult UpdateUntaggingOfPhi(Phi* phi, ValueNode* old_untagging, + const ProcessingState* state); ProcessResult EmitUnconditionalDeopt(NodeBase* node, DeoptimizeReason reason); diff --git a/deps/v8/src/maglev/maglev-range-analysis.h b/deps/v8/src/maglev/maglev-range-analysis.h index 51744959af0..57d415f06ed 100644 --- a/deps/v8/src/maglev/maglev-range-analysis.h +++ b/deps/v8/src/maglev/maglev-range-analysis.h @@ -67,6 +67,18 @@ class NodeRanges { return *range; } + void InitializeRangeIfMissing(BasicBlock* block, ValueNode* node, + Range range) { + DCHECK(ranges_initialized_.Contains(block->id())); + RangeMap& map = ranges_[block->id()]; + if (!map.find(node)) { + map = map.insert(zone(), node, range); + TRACE_RANGE("Initializing in Block b" << block->id() << ": " + << PrintNodeBrief(node) << ": " + << range); + } + } + void UnionUpdate(BasicBlock* block, ValueNode* node, Range range) { DCHECK(ranges_initialized_.Contains(block->id())); RangeMap& map = ranges_[block->id()]; @@ -210,6 +222,15 @@ class RangeProcessor { BlockProcessResult PreProcessBasicBlock(BasicBlock* block) { current_block_ = block; ranges_.EnsureMapExistsFor(block); + if (block->has_state() && block->state()->is_resumable_loop() && + block->predecessor_count() == 1) { + // Resumable loop headers might not have a live forward predecessor: that + // part of the code might be dead even though the loop is still reachable + // via a resume point. Make sure we initialize phis in such case. If the + // loop has a live predecessor, ProcessPhis is called when processing that + // live predecessor, and this code is a no-op. + InitializeLoopPhisIfMissing(block); + } return BlockProcessResult::kContinue; } BlockProcessResult PostProcessBasicBlock(BasicBlock* block) { @@ -543,6 +564,14 @@ class RangeProcessor { } } + void InitializeLoopPhisIfMissing(BasicBlock* block) { + if (!block->has_phi()) return; + for (Phi* phi : *block->phis()) { + ranges_.InitializeRangeIfMissing( + block, phi, phi->is_int32() ? Range::Int32() : Range::All()); + } + } + // Returns true if the loop reach a fixpoint. bool ProcessLoopPhisBackedge(BasicBlock* block, BasicBlock* backedge_pred) { if (!block->has_phi()) return true; diff --git a/deps/v8/src/objects/map-updater.cc b/deps/v8/src/objects/map-updater.cc index d62c94772f6..f1437356d69 100644 --- a/deps/v8/src/objects/map-updater.cc +++ b/deps/v8/src/objects/map-updater.cc @@ -385,8 +385,9 @@ std::optional> MapUpdater::TryUpdateNoLock(Isolate* isolate, } if (root_map->prototype() != old_map->prototype()) { - auto maybe_transition = TransitionsAccessor::GetPrototypeTransition( - isolate, root_map, old_map->prototype()); + auto maybe_transition = + TransitionsAccessor(isolate, root_map, IsConcurrent(cmode)) + .GetPrototypeTransition(old_map->prototype()); if (!maybe_transition) { return {}; } diff --git a/deps/v8/src/objects/shared-function-info-inl.h b/deps/v8/src/objects/shared-function-info-inl.h index 93de8a242af..c4504c0f58b 100644 --- a/deps/v8/src/objects/shared-function-info-inl.h +++ b/deps/v8/src/objects/shared-function-info-inl.h @@ -511,6 +511,9 @@ BIT_FIELD_ACCESSORS(SharedFunctionInfo, relaxed_flags, SharedFunctionInfo::PrivateNameLookupSkipsOuterClassBit) BIT_FIELD_ACCESSORS(SharedFunctionInfo, relaxed_flags, is_hoisted_in_context, SharedFunctionInfo::IsHoistedInContextBit) +BIT_FIELD_ACCESSORS(SharedFunctionInfo, relaxed_flags, + class_initializer_allows_await, + SharedFunctionInfo::ClassInitializerAllowsAwaitBit) bool SharedFunctionInfo::optimization_disabled(CodeKind kind) const { switch (kind) { diff --git a/deps/v8/src/objects/shared-function-info.cc b/deps/v8/src/objects/shared-function-info.cc index 5151351c3ac..0f4078ec21b 100644 --- a/deps/v8/src/objects/shared-function-info.cc +++ b/deps/v8/src/objects/shared-function-info.cc @@ -600,6 +600,10 @@ void SharedFunctionInfo::InitFromFunctionLiteral(IsolateT* isolate, lit->has_static_private_methods_or_accessors()); raw_sfi->set_is_hoisted_in_context(lit->scope()->is_hoisted_in_context()); + DCHECK_IMPLIES(lit->scope()->class_initializer_allows_await(), + IsClassInitializerFunction(lit->kind())); + raw_sfi->set_class_initializer_allows_await( + lit->scope()->class_initializer_allows_await()); raw_sfi->set_is_toplevel(is_toplevel); DCHECK(IsTheHole(raw_sfi->outer_scope_info())); diff --git a/deps/v8/src/objects/shared-function-info.h b/deps/v8/src/objects/shared-function-info.h index d5f8a8fc2a9..dd0e0bcb5a4 100644 --- a/deps/v8/src/objects/shared-function-info.h +++ b/deps/v8/src/objects/shared-function-info.h @@ -289,6 +289,7 @@ V8_OBJECT class SharedFunctionInfo : public HeapObject { PropertiesAreFinalBit::Next; using IsHoistedInContextBit = PrivateNameLookupSkipsOuterClassBit::Next; + using ClassInitializerAllowsAwaitBit = IsHoistedInContextBit::Next; // Bit positions in |flags2|. using ClassScopeHasPrivateBrandBit = base::BitField; using HasStaticPrivateMethodsOrAccessorsBit = @@ -708,6 +709,9 @@ V8_OBJECT class SharedFunctionInfo : public HeapObject { // Indicates that the function is a hoisted-in-context declaration. DECL_BOOLEAN_ACCESSORS(is_hoisted_in_context) + // Indicates that a class initializer's enclosing scope allowed await. + DECL_BOOLEAN_ACCESSORS(class_initializer_allows_await) + inline FunctionKind kind() const; int UniqueIdInScript() const; diff --git a/deps/v8/src/objects/shared-function-info.tq b/deps/v8/src/objects/shared-function-info.tq index f4949b0bc77..b46945f2c38 100644 --- a/deps/v8/src/objects/shared-function-info.tq +++ b/deps/v8/src/objects/shared-function-info.tq @@ -42,6 +42,7 @@ bitfield struct SharedFunctionInfoFlags extends uint32 { properties_are_final: bool: 1 bit; private_name_lookup_skips_outer_class: bool: 1 bit; is_hoisted_in_context: bool: 1 bit; + class_initializer_allows_await: bool: 1 bit; } @cppScope('SharedFunctionInfo') diff --git a/deps/v8/src/objects/transitions-inl.h b/deps/v8/src/objects/transitions-inl.h index af509675f54..b694790a4cd 100644 --- a/deps/v8/src/objects/transitions-inl.h +++ b/deps/v8/src/objects/transitions-inl.h @@ -400,6 +400,14 @@ MaybeHandle TransitionsAccessor::SearchSpecial(Isolate* isolate, return MaybeHandle(result, isolate); } +// static +std::optional> TransitionsAccessor::GetPrototypeTransition( + Isolate* isolate, Tagged map, Tagged prototype) { + // This helper is supposed to be used only from the main thread. + DCHECK_EQ(ThreadId::Current(), isolate->thread_id()); + return TransitionsAccessor(isolate, map).GetPrototypeTransition(prototype); +} + // TODO(375937549): Convert to uint32_t. int TransitionArray::number_of_transitions() const { if (ulength().value() < kFirstIndex) return 0; diff --git a/deps/v8/src/objects/transitions.cc b/deps/v8/src/objects/transitions.cc index 907e8e695b5..0c0ac419f30 100644 --- a/deps/v8/src/objects/transitions.cc +++ b/deps/v8/src/objects/transitions.cc @@ -435,8 +435,8 @@ bool TransitionsAccessor::PutPrototypeTransition(Isolate* isolate, const uint32_t header = TransitionArray::kProtoTransitionHeaderSize; - DirectHandle cache(GetPrototypeTransitions(isolate, *map), - isolate); + DirectHandle cache( + TransitionsAccessor(isolate, *map).GetPrototypeTransitions(), isolate); const uint32_t cache_len = cache->ulength().value(); const uint32_t capacity = cache_len >= header ? cache_len - header : 0; const uint32_t transitions = @@ -477,11 +477,13 @@ bool TransitionsAccessor::PutPrototypeTransition(Isolate* isolate, return true; } -// static std::optional> TransitionsAccessor::GetPrototypeTransition( - Isolate* isolate, Tagged map, Tagged prototype) { + Tagged prototype) { DisallowGarbageCollection no_gc; - Tagged cache = GetPrototypeTransitions(isolate, map); + if (encoding() != kFullTransitionArray) return {}; + base::MutexGuardIf guard(isolate_->full_transition_array_access(), + concurrent_access_); + Tagged cache = GetPrototypeTransitions(); const uint32_t length = TransitionArray::NumberOfPrototypeTransitions(cache); for (uint32_t i = 0; i < length; i++) { Tagged target = @@ -498,19 +500,11 @@ std::optional> TransitionsAccessor::GetPrototypeTransition( return {}; } -// static -Tagged TransitionsAccessor::GetPrototypeTransitions( - Isolate* isolate, Tagged map) { - Tagged raw_transitions = map->raw_transitions(kAcquireLoad); - if (GetEncoding(isolate, raw_transitions) != kFullTransitionArray) { - return ReadOnlyRoots(isolate).empty_weak_fixed_array(); - } - Tagged transition_array = - GetTransitionArray(isolate, raw_transitions); - if (!transition_array->HasPrototypeTransitions()) { - return ReadOnlyRoots(isolate).empty_weak_fixed_array(); +Tagged TransitionsAccessor::GetPrototypeTransitions() { + if (!HasPrototypeTransitions()) { + return ReadOnlyRoots(isolate_).empty_weak_fixed_array(); } - return transition_array->GetPrototypeTransitions(); + return transitions()->GetPrototypeTransitions(); } // static diff --git a/deps/v8/src/objects/transitions.h b/deps/v8/src/objects/transitions.h index 408bcaf0f4e..d7e948ef958 100644 --- a/deps/v8/src/objects/transitions.h +++ b/deps/v8/src/objects/transitions.h @@ -185,8 +185,13 @@ class V8_EXPORT_PRIVATE TransitionsAccessor { static bool PutPrototypeTransition(Isolate* isolate, DirectHandle, DirectHandle prototype, DirectHandle target_map); - static std::optional> GetPrototypeTransition( + + std::optional> GetPrototypeTransition(Tagged prototype); + // Convenient helper for calling from the main thread, for other cases + // use the instance method above. + static inline std::optional> GetPrototypeTransition( Isolate* isolate, Tagged map, Tagged prototype); + bool HasPrototypeTransitions(); // During the first-time Map::Update and Map::TryUpdate, the migration target @@ -267,8 +272,7 @@ class V8_EXPORT_PRIVATE TransitionsAccessor { static void SetPrototypeTransitions( Isolate* isolate, DirectHandle map, DirectHandle proto_transitions); - static Tagged GetPrototypeTransitions(Isolate* isolate, - Tagged map); + Tagged GetPrototypeTransitions(); static void InsertHelper(Isolate* isolate, DirectHandle map, DirectHandle name, DirectHandle target, diff --git a/deps/v8/src/parsing/parse-info.cc b/deps/v8/src/parsing/parse-info.cc index f33708abdf8..aa1647ec7f4 100644 --- a/deps/v8/src/parsing/parse-info.cc +++ b/deps/v8/src/parsing/parse-info.cc @@ -55,6 +55,8 @@ UnoptimizedCompileFlags UnoptimizedCompileFlags::ForFunctionCompile( flags.set_is_lazy_compile(true); flags.set_is_repl_mode(script->is_repl_mode()); + flags.set_class_initializer_allows_await( + shared->class_initializer_allows_await()); // Do not support re-parsing top-level function of a wrapped script. DCHECK_IMPLIES(flags.is_toplevel(), !script->is_wrapped()); diff --git a/deps/v8/src/parsing/parse-info.h b/deps/v8/src/parsing/parse-info.h index ef6f4f577ee..d62979f3f62 100644 --- a/deps/v8/src/parsing/parse-info.h +++ b/deps/v8/src/parsing/parse-info.h @@ -66,6 +66,7 @@ class Zone; V(compile_hints_magic_enabled, bool, 1, _) \ V(compile_hints_per_function_magic_enabled, bool, 1, _) \ V(is_hoisted_in_context, bool, 1, _) \ + V(class_initializer_allows_await, bool, 1, _) \ V(allow_heap_allocation, bool, 1, _) class V8_EXPORT_PRIVATE UnoptimizedCompileFlags { diff --git a/deps/v8/src/parsing/parser-base.h b/deps/v8/src/parsing/parser-base.h index 5b552c58379..1df1fa026f5 100644 --- a/deps/v8/src/parsing/parser-base.h +++ b/deps/v8/src/parsing/parser-base.h @@ -450,7 +450,11 @@ class ParserBase { class FunctionState final : public BlockState { public: FunctionState(FunctionState** function_state_stack, Scope** scope_stack, - DeclarationScope* scope); + DeclarationScope* scope) + : FunctionState(function_state_stack, scope_stack, scope, + scope->function_kind()) {} + FunctionState(FunctionState** function_state_stack, Scope** scope_stack, + DeclarationScope* scope, FunctionKind kind); ~FunctionState(); DeclarationScope* scope() const { return scope_->AsDeclarationScope(); } @@ -467,7 +471,7 @@ class ParserBase { int suspend_count() const { return suspend_count_; } bool CanSuspend() const { return suspend_count_ > 0; } - FunctionKind kind() const { return scope()->function_kind(); } + FunctionKind kind() const { return kind_; } bool next_function_is_likely_called() const { return next_function_is_likely_called_; @@ -534,6 +538,7 @@ class ParserBase { FunctionState** function_state_stack_; FunctionState* outer_function_state_; DeclarationScope* scope_; + FunctionKind kind_; Target* target_stack_ = nullptr; // for break, continue statements // A reason, if any, why this function should not be optimized. @@ -639,6 +644,8 @@ class ParserBase { static_elements_scope = parser->NewFunctionScope(kind); static_elements_scope->SetLanguageMode(LanguageMode::kStrict); static_elements_scope->set_start_position(beg_pos); + static_elements_scope->set_class_initializer_allows_await( + parser->is_await_allowed()); static_elements_function_id = info_id; // Actually consume the id. The id that was passed in might be an // earlier id in case of computed property names. @@ -657,6 +664,8 @@ class ParserBase { instance_members_scope = parser->NewFunctionScope(kind); instance_members_scope->SetLanguageMode(LanguageMode::kStrict); instance_members_scope->set_start_position(beg_pos); + instance_members_scope->set_class_initializer_allows_await( + parser->is_await_allowed()); instance_members_function_id = info_id; // Actually consume the id. The id that was passed in might be an // earlier id in case of computed property names. @@ -1833,13 +1842,14 @@ class ParserBase { template ParserBase::FunctionState::FunctionState( FunctionState** function_state_stack, Scope** scope_stack, - DeclarationScope* scope) + DeclarationScope* scope, FunctionKind kind) : BlockState(scope_stack, scope), expected_property_count_(0), suspend_count_(0), function_state_stack_(function_state_stack), outer_function_state_(*function_state_stack), scope_(scope), + kind_(kind), dont_optimize_reason_(BailoutReason::kNoReason), next_function_is_likely_called_(false), previous_function_was_likely_called_(false), diff --git a/deps/v8/src/parsing/parser.cc b/deps/v8/src/parsing/parser.cc index 94f19994ccf..388e4f1c96e 100644 --- a/deps/v8/src/parsing/parser.cc +++ b/deps/v8/src/parsing/parser.cc @@ -1274,10 +1274,19 @@ FunctionLiteral* Parser::ParseClassForMemberInitialization( // this point the scanner should be rewound to the position of the class // token. DCHECK_EQ(peek_position(), initializer_pos); - // Insert a FunctionState with the closest outer Declaration scope + // Insert a FunctionState with the closest outer Declaration scope. + // The immediate enclosing function scope may have been elided from the + // ScopeInfo chain if it did not allocate a context. Since class bodies are + // always strict mode, 'yield' is never a valid identifier in computed + // property names, so we can unconditionally enable generator mode; whether + // 'await' is a keyword is recorded on the initializer SharedFunctionInfo. DeclarationScope* nearest_decl_scope = original_scope_->GetDeclarationScope(); DCHECK_NOT_NULL(nearest_decl_scope); - FunctionState function_state(&function_state_, &scope_, nearest_decl_scope); + FunctionKind enclosing_kind = flags().class_initializer_allows_await() + ? FunctionKind::kAsyncGeneratorFunction + : FunctionKind::kGeneratorFunction; + FunctionState function_state(&function_state_, &scope_, nearest_decl_scope, + enclosing_kind); // We preparse the class members that are not fields with initializers // in order to collect the function literal ids. diff --git a/deps/v8/src/snapshot/code-serializer.cc b/deps/v8/src/snapshot/code-serializer.cc index 034ef5f52ea..78ba29eeca3 100644 --- a/deps/v8/src/snapshot/code-serializer.cc +++ b/deps/v8/src/snapshot/code-serializer.cc @@ -345,8 +345,8 @@ class StressOffThreadDeserializeThread final : public base::Thread { LocalIsolate local_isolate(isolate_, ThreadKind::kBackground); UnparkedScope unparked_scope(&local_isolate); LocalHandleScope handle_scope(&local_isolate); - off_thread_data_ = - CodeSerializer::StartDeserializeOffThread(&local_isolate, cached_data_); + CodeSerializer::StartDeserializeOffThread(&local_isolate, cached_data_, + &off_thread_data_); } MaybeDirectHandle Finalize( @@ -360,7 +360,7 @@ class StressOffThreadDeserializeThread final : public base::Thread { private: Isolate* isolate_; AlignedCachedData* cached_data_; - CodeSerializer::OffThreadDeserializeData off_thread_data_; + OffThreadDeserializeData off_thread_data_; }; void FinalizeDeserialization(Isolate* isolate, @@ -554,8 +554,7 @@ MaybeDirectHandle CodeSerializer::Deserialize( return scope.CloseAndEscape(result); } -DirectHandle