diff --git a/common.gypi b/common.gypi index ffb06d54ffe..ce602beccf6 100644 --- a/common.gypi +++ b/common.gypi @@ -43,7 +43,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.31', + 'v8_embedder_string': '-node.32', ##### V8 defaults for Node.js ##### diff --git a/deps/v8/src/deoptimizer/translated-state.cc b/deps/v8/src/deoptimizer/translated-state.cc index 06a9136e13f..6aa94999ac2 100644 --- a/deps/v8/src/deoptimizer/translated-state.cc +++ b/deps/v8/src/deoptimizer/translated-state.cc @@ -1890,7 +1890,7 @@ Address TranslatedState::DecompressIfNeeded(intptr_t value) { } // static -Tagged TranslatedState::ResolveTaggedValue( +std::optional> TranslatedState::TryResolveTaggedValue( DeoptTranslationIterator* it, Address fp, Tagged literals) { TranslationOpcode opcode = it->NextOpcode(); @@ -1906,7 +1906,11 @@ Tagged TranslatedState::ResolveTaggedValue( return Tagged(DecompressIfNeeded(value)); } default: - UNREACHABLE(); + // Any other encoding (unboxed numerics, register-resident values, + // captured objects, etc.) requires the full TranslatedState path to + // materialize. Caller should fall back. + it->SkipOperands(TranslationOpcodeOperandCount(opcode)); + return std::nullopt; } } diff --git a/deps/v8/src/deoptimizer/translated-state.h b/deps/v8/src/deoptimizer/translated-state.h index 5029b2b7422..07daaf4be39 100644 --- a/deps/v8/src/deoptimizer/translated-state.h +++ b/deps/v8/src/deoptimizer/translated-state.h @@ -513,8 +513,10 @@ class TranslatedState { // Resolves one deopt translation value opcode to a raw Tagged, // reading from the live frame if needed. Only LITERAL and - // TAGGED_STACK_SLOT are expected; other opcodes are UNREACHABLE. - static Tagged ResolveTaggedValue( + // TAGGED_STACK_SLOT can be resolved cheaply; for any other opcode the + // iterator is left positioned just past the opcode and std::nullopt is + // returned so the caller can fall back to the full materialization path. + static std::optional> TryResolveTaggedValue( DeoptTranslationIterator* it, Address fp, Tagged literals); diff --git a/deps/v8/src/execution/frames.cc b/deps/v8/src/execution/frames.cc index be13c1d682a..f587dd49e36 100644 --- a/deps/v8/src/execution/frames.cc +++ b/deps/v8/src/execution/frames.cc @@ -3233,13 +3233,22 @@ FrameSummaries OptimizedJSFrame::Summarize(bool never_allocate) const { // Skip remaining header operands to reach the values. it.SkipOperands(TranslationOpcodeOperandCount(opcode) - 2); - // Resolve closure and receiver from the live frame. Both are always - // encoded as LITERAL or TAGGED_STACK_SLOT in the deopt translation. - Tagged function_obj = - TranslatedState::ResolveTaggedValue(&it, fp(), literal_array); - DCHECK(IsJSFunction(function_obj)); - Tagged receiver_obj = - TranslatedState::ResolveTaggedValue(&it, fp(), literal_array); + // Resolve closure and receiver from the live frame. The closure is + // always tagged (LITERAL or TAGGED_STACK_SLOT), but the receiver is + // just parameter 0 of the (possibly inlined) frame and may be encoded + // in any representation the optimizer chose (e.g. DOUBLE_STACK_SLOT + // for an unboxed Float64). Fall back to the full materialization path + // in that case. + std::optional> function_obj = + TranslatedState::TryResolveTaggedValue(&it, fp(), literal_array); + DCHECK(function_obj.has_value()); + DCHECK(IsJSFunction(*function_obj)); + std::optional> receiver_obj = + TranslatedState::TryResolveTaggedValue(&it, fp(), literal_array); + if (!receiver_obj.has_value()) { + needs_full_walk = true; + break; + } Tagged abstract_code; int code_offset; @@ -3255,7 +3264,7 @@ FrameSummaries OptimizedJSFrame::Summarize(bool never_allocate) const { DirectHandle params = GetParameters(never_allocate); FrameSummary::JavaScriptFrameSummary summary( - isolate(), receiver_obj, Cast(function_obj), + isolate(), *receiver_obj, Cast(*function_obj), abstract_code, code_offset, is_constructor, *params); summaries.frames.push_back(summary); is_constructor = false; diff --git a/deps/v8/test/mjsunit/regress/regress-499260582.js b/deps/v8/test/mjsunit/regress/regress-499260582.js new file mode 100644 index 00000000000..08e4a2daf9b --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-499260582.js @@ -0,0 +1,20 @@ +// Copyright 2026 the V8 project authors. All rights reserved. +// Use of this source code governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --allow-natives-syntax --jit-fuzzing --single-threaded + +function f9(a10, a11) { + 'use strict'; + for (let v12 = 0; v12 < 5; v12++) { + const v15 = ({ maxByteLength: 1073741824 }).maxByteLength; + %OptimizeOsr(); + function F16(a18, a19, a20) { + if (!new.target) { throw 'must be called with new'; } + } + try { F16.call(v15); } catch (e) {} + } + return f9; +} +%PrepareFunctionForOptimization(f9); +f9(f9, f9);