diff --git a/common.gypi b/common.gypi index a982da03f78b..9915a6d0d26e 100644 --- a/common.gypi +++ b/common.gypi @@ -42,7 +42,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.29', + '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 a8c87dbd0242..6aa94999ac28 100644 --- a/deps/v8/src/deoptimizer/translated-state.cc +++ b/deps/v8/src/deoptimizer/translated-state.cc @@ -1889,6 +1889,31 @@ Address TranslatedState::DecompressIfNeeded(intptr_t value) { } } +// static +std::optional> TranslatedState::TryResolveTaggedValue( + DeoptTranslationIterator* it, Address fp, + Tagged literals) { + TranslationOpcode opcode = it->NextOpcode(); + switch (opcode) { + case TranslationOpcode::LITERAL: { + int literal_index = it->NextOperand(); + return literals->get(literal_index); + } + case TranslationOpcode::TAGGED_STACK_SLOT: { + int slot_offset = + OptimizedJSFrame::StackSlotOffsetRelativeToFp(it->NextOperand()); + intptr_t value = *reinterpret_cast(fp + slot_offset); + return Tagged(DecompressIfNeeded(value)); + } + default: + // 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; + } +} + TranslatedState::TranslatedState(const JavaScriptFrame* frame) : purpose_(kFrameInspection) { int deopt_index = SafepointEntry::kNoDeoptIndex; diff --git a/deps/v8/src/deoptimizer/translated-state.h b/deps/v8/src/deoptimizer/translated-state.h index dfa5f11e8209..07daaf4be395 100644 --- a/deps/v8/src/deoptimizer/translated-state.h +++ b/deps/v8/src/deoptimizer/translated-state.h @@ -511,6 +511,17 @@ class TranslatedState { void VerifyMaterializedObjects(); bool DoUpdateFeedback(DeoptimizeReason reason); + // Resolves one deopt translation value opcode to a raw Tagged, + // reading from the live frame if needed. Only LITERAL and + // 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); + + static Address DecompressIfNeeded(intptr_t value); + private: friend TranslatedValue; @@ -529,7 +540,6 @@ class TranslatedState { int frame_index, DeoptTranslationIterator* iterator, const DeoptimizationLiteralProvider& literal_array, Address fp, RegisterValues* registers, FILE* trace_file); - Address DecompressIfNeeded(intptr_t value); void CreateArgumentsElementsTranslatedValues(int frame_index, Address input_frame_pointer, CreateArgumentsType type, diff --git a/deps/v8/src/execution/frames.cc b/deps/v8/src/execution/frames.cc index 110abb96f250..f587dd49e36d 100644 --- a/deps/v8/src/execution/frames.cc +++ b/deps/v8/src/execution/frames.cc @@ -3179,7 +3179,118 @@ FrameSummaries OptimizedJSFrame::Summarize(bool never_allocate) const { "Missing deoptimization information for OptimizedJSFrame::Summarize."); } - // Prepare iteration over translation. We must not materialize values here + // Lightweight walk: iterate frame headers only, resolving just the + // function and receiver from the live frame via ResolveTaggedValue. + // This avoids the expensive TranslatedState::Init + Prepare path that + // would parse every value in every inlined frame. + + Tagged literal_array = data->LiteralArray(); + + // Lightweight walk: resolve function and receiver from live frame headers + // and build JavaScriptFrameSummary objects directly. + bool needs_full_walk = false; + { + DisallowGarbageCollection no_gc; + DeoptimizationFrameTranslation::Iterator it( + data->FrameTranslation(), data->TranslationIndex(deopt_index).value()); + bool is_constructor = IsConstructor(); + int remaining = it.EnterBeginOpcode().total_frame_count; + + while (remaining > 0) { + TranslationOpcode opcode = it.SeekNextFrame(); + remaining--; + + if (opcode == TranslationOpcode::CONSTRUCT_CREATE_STUB_FRAME || + opcode == TranslationOpcode::CONSTRUCT_INVOKE_STUB_FRAME) { + is_constructor = true; + it.SkipOperands(TranslationOpcodeOperandCount(opcode)); + continue; + } + + if (!IsTranslationJsFrameOpcode(opcode)) { +#if V8_ENABLE_WEBASSEMBLY + // Wasm-inlined-into-JS frames need the full TranslatedState + // machinery to produce WasmFrameSummary entries. + if (opcode == TranslationOpcode::WASM_INLINED_INTO_JS_FRAME) { + needs_full_walk = true; + break; + } +#endif + it.SkipOperands(TranslationOpcodeOperandCount(opcode)); + continue; + } + + bool is_builtin_cont = + (opcode == TranslationOpcode::JAVASCRIPT_BUILTIN_CONTINUATION_FRAME || + opcode == TranslationOpcode:: + JAVASCRIPT_BUILTIN_CONTINUATION_WITH_CATCH_FRAME); + + int bytecode_offset = it.NextOperand(); + int sfi_id = it.NextOperand(); + Tagged sfi = + Cast(literal_array->get(sfi_id)); + + // Skip remaining header operands to reach the values. + it.SkipOperands(TranslationOpcodeOperandCount(opcode) - 2); + + // 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; + if (is_builtin_cont) { + code_offset = 0; + abstract_code = Cast( + isolate()->builtins()->code(Builtins::GetBuiltinFromBytecodeOffset( + BytecodeOffset(bytecode_offset)))); + } else { + code_offset = bytecode_offset; + abstract_code = Cast(sfi->GetBytecodeArray(isolate())); + } + + DirectHandle params = GetParameters(never_allocate); + FrameSummary::JavaScriptFrameSummary summary( + isolate(), *receiver_obj, Cast(*function_obj), + abstract_code, code_offset, is_constructor, *params); + summaries.frames.push_back(summary); + is_constructor = false; + } + + if (!needs_full_walk && is_constructor) { + summaries.top_frame_is_construct_call = true; + } + } // no_gc scope ends. + + if (needs_full_walk) { + return SummarizeFull(data, deopt_index, never_allocate); + } + + return summaries; +} + +FrameSummaries OptimizedJSFrame::SummarizeFull(Tagged data, + int deopt_index, + bool never_allocate) const { + FrameSummaries summaries; + + DCHECK_NE(deopt_index, SafepointEntry::kNoDeoptIndex); + DCHECK(!data.is_null()); + + // Prepare iteration over translation. We must not materialize values here // because we do not deoptimize the function. TranslatedState translated(this); translated.Prepare(fp()); @@ -3204,7 +3315,21 @@ FrameSummaries OptimizedJSFrame::Summarize(bool never_allocate) const { // Get the correct receiver in the optimized frame. static_assert(TranslatedFrame::kReceiverIsFirstParameterInJSFrames); CHECK(!translated_values->IsMaterializedObject()); - DirectHandle receiver = translated_values->GetValue(); + // Check GetRawValue() against arguments_marker() first to see whether + // calling GetValue() allocates. + Tagged receiver_obj = translated_values->GetRawValue(); + DirectHandle receiver; + if (receiver_obj == ReadOnlyRoots(isolate()).arguments_marker() && + never_allocate) { + // Calling GetValue() would definitely trigger allocation but with + // `never_allocate` allocations are not allowed. Simply pick `undefined` + // as receiver instead even though it is off. `never_allocate` is + // currently only used for OOM stacks, where we don't even emit the + // receiver but want to see as many stack frames as possible. + receiver = isolate()->factory()->undefined_value(); + } else { + receiver = translated_values->GetValue(); + } translated_values++; // Determine the underlying code object and the position within it from @@ -3311,24 +3436,20 @@ int TurbofanJSFrame::FindReturnPCForTrampoline(Tagged code, return safepoints.find_return_pc(trampoline_pc); } -Tagged OptimizedJSFrame::GetDeoptimizationData( - Tagged code, int* deopt_index) const { - DCHECK(is_optimized()); - - Address pc = maybe_unauthenticated_pc(); - - DCHECK(code->contains(isolate(), pc)); +// static +Tagged OptimizedJSFrame::GetDeoptimizationDataForPC( + Isolate* isolate, Tagged code, Address pc, int* deopt_index) { + DCHECK(code->contains(isolate, pc)); DCHECK(CodeKindCanDeoptimize(code->kind())); - if (code->is_maglevved()) { MaglevSafepointEntry safepoint_entry = - code->GetMaglevSafepointEntry(isolate(), pc); + code->GetMaglevSafepointEntry(isolate, pc); if (safepoint_entry.has_deoptimization_index()) { *deopt_index = safepoint_entry.deoptimization_index(); return code->deoptimization_data(); } } else { - SafepointEntry safepoint_entry = code->GetSafepointEntry(isolate(), pc); + SafepointEntry safepoint_entry = code->GetSafepointEntry(isolate, pc); if (safepoint_entry.has_deoptimization_index()) { *deopt_index = safepoint_entry.deoptimization_index(); return code->deoptimization_data(); @@ -3338,6 +3459,14 @@ Tagged OptimizedJSFrame::GetDeoptimizationData( return {}; } +Tagged OptimizedJSFrame::GetDeoptimizationData( + Tagged code, int* deopt_index) const { + DCHECK(is_optimized()); + Address pc = maybe_unauthenticated_pc(); + DCHECK(code->contains(isolate(), pc)); + return GetDeoptimizationDataForPC(isolate(), code, pc, deopt_index); +} + void OptimizedJSFrame::GetFunctions( std::vector>* functions) const { DCHECK(functions->empty()); diff --git a/deps/v8/src/execution/frames.h b/deps/v8/src/execution/frames.h index 017613a7305a..550fdde93671 100644 --- a/deps/v8/src/execution/frames.h +++ b/deps/v8/src/execution/frames.h @@ -1169,6 +1169,11 @@ class OptimizedJSFrame : public JavaScriptFrame { Tagged GetDeoptimizationData(Tagged code, int* deopt_index) const; + // Like GetDeoptimizationData, but takes an explicit PC instead of reading + // it from the frame. Can be used without a live frame. + static Tagged GetDeoptimizationDataForPC( + Isolate* isolate, Tagged code, Address pc, int* deopt_index); + static int StackSlotOffsetRelativeToFp(int slot_index); // Lookup exception handler for current {pc}, returns -1 if none found. @@ -1178,6 +1183,13 @@ class OptimizedJSFrame : public JavaScriptFrame { virtual int FindReturnPCForTrampoline(Tagged code, int trampoline_pc) const = 0; + private: + // Full TranslatedState-based walk, used as fallback when the lightweight + // path in Summarize() encounters frames it cannot handle (e.g. + // wasm-inlined-into-JS frames). + FrameSummaries SummarizeFull(Tagged data, int deopt_index, + bool never_allocate) const; + protected: inline explicit OptimizedJSFrame(StackFrameIteratorBase* iterator); }; diff --git a/deps/v8/src/execution/isolate.cc b/deps/v8/src/execution/isolate.cc index 06fa14bf6b10..74762de5a9aa 100644 --- a/deps/v8/src/execution/isolate.cc +++ b/deps/v8/src/execution/isolate.cc @@ -878,18 +878,21 @@ class CallSiteBuilder { // framework and library code, and stack depth tends to be more than // a dozen frames, so we over-allocate a bit here to avoid growing // the elements array in the common case. - elements_ = isolate->factory()->NewFixedArray(std::min(64, limit)); + elements_ = isolate->factory()->NewFixedArray(CallSiteInfo::Fields::kCount * + std::min(64, limit)); } void SetPrevFrameAsConstructCall() { if (skipped_prev_frame_) return; DCHECK_GT(index_, 0); - Tagged info = - Tagged::cast(elements_->get(index_ - 1)); + int base_index = (index_ - 1) * CallSiteInfo::Fields::kCount; + int flags = + Smi::ToInt(elements_->get(base_index + CallSiteInfo::Fields::kFlags)); #if V8_ENABLE_WEBASSEMBLY - if (info->IsWasm()) return; + if (flags & CallSiteInfo::kIsWasm) return; #endif - info->set_flags(info->flags() | CallSiteInfo::kIsConstructor); + elements_->set(base_index + CallSiteInfo::Fields::kFlags, + Smi::FromInt(flags | CallSiteInfo::kIsConstructor)); } bool Visit(FrameSummary const& summary) { @@ -1051,7 +1054,8 @@ class CallSiteBuilder { bool Full() { return index_ >= limit_; } Handle Build() { - return FixedArray::RightTrimOrEmpty(isolate_, elements_, index_); + return FixedArray::RightTrimOrEmpty(isolate_, elements_, + CallSiteInfo::Fields::kCount * index_); } private: @@ -1114,19 +1118,70 @@ class CallSiteBuilder { return true; } + public: + // Store a deferred entry for a baseline frame. + // Stores the Code + raw PC offset; bytecode offset resolution + // happens lazily in ExpandDeferredFrames(). + bool AppendDeferredFrame(JavaScriptFrame* frame, int deferred_flag) { + if (Full()) return false; + DirectHandle function(frame->function(), isolate_); + if (!IsVisibleInStackTrace(function)) { + skipped_prev_frame_ = true; + return true; + } + int flags = deferred_flag; + if (IsStrictFrame(function)) flags |= CallSiteInfo::kIsStrict; + if (frame->IsConstructor()) flags |= CallSiteInfo::kIsConstructor; + + Tagged code = frame->LookupCode(); + int pc_offset = static_cast(frame->pc() - code->instruction_start()); + + AppendFrame(Cast>(handle(frame->receiver(), isolate_)), + function, handle(code, isolate_), pc_offset, flags, + frame->GetParameters(/*never_allocate=*/false)); + return true; + } + void AppendFrame(DirectHandle> receiver_or_instance, DirectHandle> function, - DirectHandle code, int offset, int flags, + DirectHandle code_obj, int offset, int flags, DirectHandle parameters) { if (IsTheHole(*receiver_or_instance, isolate_)) { // TODO(jgruber): Fix all cases in which frames give us a hole value // (e.g. the receiver in RegExp constructor frames). receiver_or_instance = isolate_->factory()->undefined_value(); } - auto info = isolate_->factory()->NewCallSiteInfo( - Cast(receiver_or_instance), function, code, offset, flags, - parameters); - elements_ = FixedArray::SetAndGrow(isolate_, elements_, index_++, info); + + int base_index = index_ * CallSiteInfo::Fields::kCount; + + // Set the last field first and grow the array if needed. + static_assert(CallSiteInfo::Fields::kFlags == + CallSiteInfo::Fields::kCount - 1); + elements_ = FixedArray::SetAndGrow( + isolate_, elements_, base_index + CallSiteInfo::Fields::kFlags, + Smi::FromInt(flags)); + + elements_->set(base_index + CallSiteInfo::Fields::kReceiver, + *receiver_or_instance); + elements_->set(base_index + CallSiteInfo::Fields::kFunction, *function); + + if (DirectHandle code; TryCast(code_obj, &code)) { + elements_->set(base_index + CallSiteInfo::Fields::kCode, code->wrapper()); + } else if (DirectHandle bytecode; + TryCast(code_obj, &bytecode)) { + elements_->set(base_index + CallSiteInfo::Fields::kCode, + bytecode->wrapper()); + } else { + elements_->set(base_index + CallSiteInfo::Fields::kCode, + *isolate_->factory()->undefined_value()); + } + + elements_->set(base_index + CallSiteInfo::Fields::kOffset, + Smi::FromInt(offset)); + elements_->set(base_index + CallSiteInfo::Fields::kParameters, + *parameters); + + index_++; skipped_prev_frame_ = false; } @@ -1357,18 +1412,16 @@ void CaptureAsyncStackTrace(Isolate* isolate, CallSiteBuilder* builder) { } } +// Summarize a single frame and pass each logical frame to the visitor. template void VisitStack(Isolate* isolate, Visitor* visitor, StackTrace::StackTraceOptions options = StackTrace::kDetailed, bool never_allocate = false) { DisallowJavascriptExecution no_js(isolate); - // Keep track if we visited a stack frame, but did not visit any summarized - // frames. Either because the stack frame didn't create any summarized frames - // or due to security origin. - bool skipped_last_frame = true; for (StackFrameIterator it(isolate); !it.done(); it.Advance()) { StackFrame* frame = it.frame(); switch (frame->type()) { + case StackFrame::BASELINE: case StackFrame::API_CALLBACK_EXIT: case StackFrame::API_CONSTRUCT_EXIT: case StackFrame::BUILTIN_EXIT: @@ -1377,7 +1430,6 @@ void VisitStack(Isolate* isolate, Visitor* visitor, case StackFrame::TURBOFAN_JS: case StackFrame::MAGLEV: case StackFrame::INTERPRETED: - case StackFrame::BASELINE: case StackFrame::BUILTIN: #if V8_ENABLE_WEBASSEMBLY case StackFrame::STUB: @@ -1392,14 +1444,73 @@ void VisitStack(Isolate* isolate, Visitor* visitor, // inlining). FrameSummaries summaries = CommonFrame::cast(frame)->Summarize(never_allocate); + for (auto& summary : base::Reversed(summaries.frames)) { + // Skip frames from other origins when asked to do so. + if (!(options & StackTrace::kExposeFramesAcrossSecurityOrigins) && + !summary.native_context()->HasSameSecurityTokenAs( + isolate->context())) { + continue; + } + if (!visitor->Visit(summary)) return; + } + break; + } + + default: + break; + } + } +} + +// Specialized stack walk for CallSiteBuilder, handling deferred baseline +// frames and the construct-call flag on the previous frame. +void VisitStack_ForCallSiteBuilder(Isolate* isolate, CallSiteBuilder* visitor) { + DisallowJavascriptExecution no_js(isolate); + // Track whether the last physical frame produced any visited summarized + // frames. Used to correctly attribute construct-call flags. + bool skipped_last_frame = true; + for (StackFrameIterator it(isolate); !it.done(); it.Advance()) { + StackFrame* frame = it.frame(); + switch (frame->type()) { + case StackFrame::BASELINE: + // Baseline frames are captured as deferred entries so they can be + // expanded later without blocking the stack capture path. + if (!visitor->AppendDeferredFrame( + JavaScriptFrame::cast(frame), + CallSiteInfo::kIsDeferredBaselineFrame)) { + return; + } + skipped_last_frame = true; + break; + case StackFrame::API_CALLBACK_EXIT: + case StackFrame::API_CONSTRUCT_EXIT: + case StackFrame::BUILTIN_EXIT: + case StackFrame::JAVASCRIPT_BUILTIN_CONTINUATION: + case StackFrame::JAVASCRIPT_BUILTIN_CONTINUATION_WITH_CATCH: + case StackFrame::TURBOFAN_JS: + case StackFrame::MAGLEV: + case StackFrame::INTERPRETED: + case StackFrame::BUILTIN: +#if V8_ENABLE_WEBASSEMBLY + case StackFrame::STUB: + case StackFrame::WASM: + case StackFrame::WASM_SEGMENT_START: +#if V8_ENABLE_DRUMBRAKE + case StackFrame::WASM_INTERPRETER_ENTRY: +#endif // V8_ENABLE_DRUMBRAKE +#endif // V8_ENABLE_WEBASSEMBLY + { + // A standard frame may include many summarized frames (due to + // inlining). + FrameSummaries summaries = CommonFrame::cast(frame)->Summarize(); if (summaries.top_frame_is_construct_call && !skipped_last_frame) { visitor->SetPrevFrameAsConstructCall(); } skipped_last_frame = true; for (auto& summary : base::Reversed(summaries.frames)) { - // Skip frames from other origins when asked to do so. - if (!(options & StackTrace::kExposeFramesAcrossSecurityOrigins) && - !summary.native_context()->HasSameSecurityTokenAs( + // CaptureSimpleStackTrace uses kDetailed, which does not expose + // frames across security origins. + if (!summary.native_context()->HasSameSecurityTokenAs( isolate->context())) { continue; } @@ -1426,7 +1537,7 @@ Handle CaptureSimpleStackTrace(Isolate* isolate, int limit, #endif // V8_ENABLE_WEBASSEMBLY CallSiteBuilder builder(isolate, mode, limit, caller); - VisitStack(isolate, &builder); + VisitStack_ForCallSiteBuilder(isolate, &builder); // If --async-stack-traces are enabled and the "current microtask" is a // PromiseReactionJobTask, we try to enrich the stack trace with async @@ -1442,13 +1553,21 @@ Handle CaptureSimpleStackTrace(Isolate* isolate, int limit, } DirectHandle GetDetailedStackTraceFromCallSiteInfos( - Isolate* isolate, DirectHandle call_site_infos, int limit) { - auto frames = isolate->factory()->NewFixedArray( - std::min(limit, call_site_infos->length())); - int index = 0; - for (int i = 0; i < call_site_infos->length() && index < limit; ++i) { - DirectHandle call_site_info( - Cast(call_site_infos->get(i)), isolate); + Isolate* isolate, DirectHandle raw_data_for_call_site_infos, + uint32_t limit) { + Handle expanded = CallSiteInfo::ExpandDeferredFrames( + isolate, handle(*raw_data_for_call_site_infos, isolate)); + raw_data_for_call_site_infos = expanded; + uint32_t call_site_infos_len = + raw_data_for_call_site_infos->length() / + CallSiteInfo::Fields::kCount; + auto frames = + isolate->factory()->NewFixedArray(std::min(limit, call_site_infos_len)); + uint32_t index = 0; + for (uint32_t i = 0; i < call_site_infos_len && index < limit; ++i) { + DirectHandle call_site_info = + CallSiteInfo::ConstructFromRawData(isolate, + raw_data_for_call_site_infos, i); if (call_site_info->IsAsync()) { break; } @@ -1519,14 +1638,19 @@ MaybeDirectHandle Isolate::CaptureAndSetErrorStack( stack_trace_for_uncaught_exceptions_frame_limit_, stack_trace_for_uncaught_exceptions_options_); } else { - auto call_site_infos = + auto raw_data_for_call_site_infos = Cast(call_site_infos_or_formatted_stack); stack_trace = GetDetailedStackTraceFromCallSiteInfos( - this, call_site_infos, - stack_trace_for_uncaught_exceptions_frame_limit_); - if (stack_trace_limit < call_site_infos->length()) { + this, raw_data_for_call_site_infos, + static_cast( + stack_trace_for_uncaught_exceptions_frame_limit_)); + DCHECK_GE(stack_trace_limit, 0); + if (static_cast(stack_trace_limit) * + CallSiteInfo::Fields::kCount < + raw_data_for_call_site_infos->length()) { call_site_infos_or_formatted_stack = FixedArray::RightTrimOrEmpty( - this, call_site_infos, stack_trace_limit); + this, raw_data_for_call_site_infos, + stack_trace_limit * CallSiteInfo::Fields::kCount); } // Notify the debugger. OnStackTraceCaptured(stack_trace); @@ -1556,17 +1680,29 @@ Handle Isolate::GetSimpleStackTrace( ErrorUtils::StackPropertyLookupResult lookup = ErrorUtils::GetErrorStackProperty(this, maybe_error_object); + Handle raw_data; if (IsFixedArray(*lookup.error_stack)) { - return Cast(lookup.error_stack); - } - if (!IsErrorStackData(*lookup.error_stack)) { + raw_data = Cast(lookup.error_stack); + } else if (IsErrorStackData(*lookup.error_stack)) { + auto error_stack_data = Cast(lookup.error_stack); + if (!error_stack_data->HasRawDataForCallSiteInfos()) { + return factory()->empty_fixed_array(); + } + raw_data = handle(error_stack_data->raw_data_for_call_site_infos(), this); + } else { return factory()->empty_fixed_array(); } - auto error_stack_data = Cast(lookup.error_stack); - if (!error_stack_data->HasCallSiteInfos()) { - return factory()->empty_fixed_array(); + + raw_data = CallSiteInfo::ExpandDeferredFrames(this, raw_data); + + int frame_count = raw_data->length() / CallSiteInfo::Fields::kCount; + Handle call_site_infos = factory()->NewFixedArray(frame_count); + for (int i = 0; i < frame_count; ++i) { + DirectHandle call_site_info = + CallSiteInfo::ConstructFromRawData(this, raw_data, i); + call_site_infos->set(i, *call_site_info); } - return handle(error_stack_data->call_site_infos(), this); + return call_site_infos; } Address Isolate::GetAbstractPC(int* line, int* column) { @@ -1616,10 +1752,6 @@ class StackFrameBuilder { index_(0), limit_(limit) {} - void SetPrevFrameAsConstructCall() { - // Nothing to do. - } - bool Visit(FrameSummary& summary) { // Check if we have enough capacity left. if (index_ >= limit_) return false; @@ -1664,10 +1796,6 @@ class CurrentScriptNameStackVisitor { explicit CurrentScriptNameStackVisitor(Isolate* isolate) : isolate_(isolate) {} - void SetPrevFrameAsConstructCall() { - // Nothing to do. - } - bool Visit(FrameSummary& summary) { // Skip frames that aren't subject to debugging. Keep this in sync with // StackFrameBuilder::Visit so both visitors visit the same frames. @@ -1696,10 +1824,6 @@ class CurrentScriptNameStackVisitor { class CurrentScriptIdStackVisitor { public: - void SetPrevFrameAsConstructCall() { - // Nothing to do. - } - bool Visit(FrameSummary& summary) { // Skip frames that aren't subject to debugging. Keep this in sync with // StackFrameBuilder::Visit so both visitors visit the same frames. @@ -1729,10 +1853,6 @@ class CurrentScriptIdsAndContextsStackVisitor { DCHECK_LT(0, frame_data.size()); } - void SetPrevFrameAsConstructCall() { - // Nothing to do. - } - bool Visit(FrameSummary& summary) { if (!summary.is_subject_to_debugging()) return true; @@ -1778,10 +1898,6 @@ class CurrentScriptIdsAndContextsStackVisitor { class CurrentScriptStackVisitor { public: - void SetPrevFrameAsConstructCall() { - // Nothing to do. - } - bool Visit(FrameSummary& summary) { // Skip frames that aren't subject to debugging. Keep this in sync with // StackFrameBuilder::Visit so both visitors visit the same frames. @@ -1919,10 +2035,6 @@ class MinimalStackPrinter { explicit MinimalStackPrinter(size_t max_length) : max_length_(max_length) {} - void SetPrevFrameAsConstructCall() { - // Nothing to do. - } - bool Visit(FrameSummary& summary) { if (summary.IsJavaScript()) { const FrameSummary::JavaScriptFrameSummary& js_summary = @@ -3164,12 +3276,16 @@ Tagged Isolate::ThrowIllegalOperation() { void Isolate::PrintCurrentStackTrace( std::ostream& out, PrintCurrentStackTraceFilterCallback should_include_frame_callback) { - DirectHandle frames = CaptureSimpleStackTrace( + Handle frames = CaptureSimpleStackTrace( this, FixedArray::kMaxLength, SKIP_NONE, factory()->undefined_value()); + frames = CallSiteInfo::ExpandDeferredFrames(this, frames); IncrementalStringBuilder builder(this); - for (int i = 0; i < frames->length(); ++i) { - DirectHandle frame(Cast(frames->get(i)), this); + uint32_t frame_count = + frames->length() / CallSiteInfo::Fields::kCount; + for (uint32_t i = 0; i < frame_count; ++i) { + DirectHandle frame = + CallSiteInfo::ConstructFromRawData(this, frames, i); if (should_include_frame_callback) { Tagged raw_script_name = frame->GetScriptNameOrSourceURL(); @@ -3193,7 +3309,7 @@ void Isolate::PrintCurrentStackTrace( SerializeCallSiteInfo(this, frame, &builder); } - if (i != frames->length() - 1) builder.AppendCharacter('\n'); + if (i != frame_count - 1) builder.AppendCharacter('\n'); } DirectHandle stack_trace = builder.Finish().ToHandleChecked(); diff --git a/deps/v8/src/execution/messages.cc b/deps/v8/src/execution/messages.cc index b24aafc1be43..64800baec261 100644 --- a/deps/v8/src/execution/messages.cc +++ b/deps/v8/src/execution/messages.cc @@ -24,6 +24,7 @@ #include "src/parsing/parse-info.h" #include "src/parsing/parsing.h" #include "src/roots/roots.h" +#include "src/sandbox/indirect-pointer-inl.h" #include "src/strings/string-builder-inl.h" namespace v8 { @@ -202,15 +203,16 @@ namespace { // Convert the raw frames as written by Isolate::CaptureSimpleStackTrace into // a JSArray of JSCallSite objects. -MaybeDirectHandle GetStackFrames(Isolate* isolate, - DirectHandle frames) { - int frame_count = frames->length(); +MaybeDirectHandle GetStackFrames( + Isolate* isolate, DirectHandle raw_data_for_call_site_infos) { + uint32_t frame_count = raw_data_for_call_site_infos->length() / + CallSiteInfo::Fields::kCount; DirectHandle constructor = isolate->callsite_function(); DirectHandle sites = isolate->factory()->NewFixedArray(frame_count); - for (int i = 0; i < frame_count; ++i) { - DirectHandle frame(Cast(frames->get(i)), - isolate); + for (uint32_t i = 0; i < frame_count; ++i) { + DirectHandle frame = CallSiteInfo::ConstructFromRawData( + isolate, raw_data_for_call_site_infos, i); DirectHandle site; ASSIGN_RETURN_ON_EXCEPTION(isolate, site, JSObject::New(constructor, constructor, @@ -292,7 +294,7 @@ MaybeDirectHandle ErrorUtils::FormatStackTrace( return isolate->factory()->empty_string(); } DCHECK(IsFixedArray(*raw_stack)); - auto elems = Cast(raw_stack); + auto raw_data_for_call_site_infos = Cast(raw_stack); const bool in_recursion = isolate->formatting_stack_trace(); const bool has_overflowed = i::StackLimitCheck{isolate}.HasOverflowed(); @@ -303,8 +305,9 @@ MaybeDirectHandle ErrorUtils::FormatStackTrace( PrepareStackTraceScope scope(isolate); DirectHandle sites; - ASSIGN_RETURN_ON_EXCEPTION(isolate, sites, - GetStackFrames(isolate, elems)); + ASSIGN_RETURN_ON_EXCEPTION( + isolate, sites, + GetStackFrames(isolate, raw_data_for_call_site_infos)); DirectHandle result; ASSIGN_RETURN_ON_EXCEPTION( @@ -329,8 +332,9 @@ MaybeDirectHandle ErrorUtils::FormatStackTrace( isolate->CountUsage(v8::Isolate::kErrorPrepareStackTrace); DirectHandle sites; - ASSIGN_RETURN_ON_EXCEPTION(isolate, sites, - GetStackFrames(isolate, elems)); + ASSIGN_RETURN_ON_EXCEPTION( + isolate, sites, + GetStackFrames(isolate, raw_data_for_call_site_infos)); constexpr int argc = 2; std::array, argc> args; @@ -359,12 +363,16 @@ MaybeDirectHandle ErrorUtils::FormatStackTrace( RETURN_ON_EXCEPTION(isolate, AppendErrorString(isolate, error, &builder)); - for (int i = 0; i < elems->length(); ++i) { + int elems_len = raw_data_for_call_site_infos->length() / + CallSiteInfo::Fields::kCount; + for (int i = 0; i < elems_len; ++i) { builder.AppendCStringLiteral("\n at "); - DirectHandle frame(Cast(elems->get(i)), - isolate); + DirectHandle frame = CallSiteInfo::ConstructFromRawData( + isolate, raw_data_for_call_site_infos, i); + // TODO(marja): Avoid CallSiteInfo creation since we serialize it right + // away. v8::TryCatch try_catch(reinterpret_cast(isolate)); SerializeCallSiteInfo(isolate, frame, &builder); @@ -1160,12 +1168,13 @@ MaybeDirectHandle ErrorUtils::GetFormattedStack( DirectHandle error_object = lookup.error_stack_symbol_holder.ToHandleChecked(); + Handle expanded = CallSiteInfo::ExpandDeferredFrames( + isolate, + handle(error_stack_data->raw_data_for_call_site_infos(), isolate)); DirectHandle formatted_stack; ASSIGN_RETURN_ON_EXCEPTION( isolate, formatted_stack, - FormatStackTrace( - isolate, error_object, - direct_handle(error_stack_data->call_site_infos(), isolate))); + FormatStackTrace(isolate, error_object, expanded)); error_stack_data->set_formatted_stack(*formatted_stack); return formatted_stack; } @@ -1173,11 +1182,12 @@ MaybeDirectHandle ErrorUtils::GetFormattedStack( if (IsFixedArray(*lookup.error_stack)) { DirectHandle error_object = lookup.error_stack_symbol_holder.ToHandleChecked(); + Handle expanded = CallSiteInfo::ExpandDeferredFrames( + isolate, handle(Cast(*lookup.error_stack), isolate)); DirectHandle formatted_stack; ASSIGN_RETURN_ON_EXCEPTION( isolate, formatted_stack, - FormatStackTrace(isolate, error_object, - Cast(lookup.error_stack))); + FormatStackTrace(isolate, error_object, expanded)); RETURN_ON_EXCEPTION( isolate, Object::SetProperty(isolate, error_object, isolate->factory()->error_stack_symbol(), diff --git a/deps/v8/src/heap/factory.cc b/deps/v8/src/heap/factory.cc index 32b47dee39ff..2fc11e822292 100644 --- a/deps/v8/src/heap/factory.cc +++ b/deps/v8/src/heap/factory.cc @@ -1589,13 +1589,14 @@ DirectHandle Factory::NewInterceptorInfo( } DirectHandle Factory::NewErrorStackData( - DirectHandle> call_site_infos_or_formatted_stack, + DirectHandle> + raw_data_for_call_site_infos_or_formatted_stack, DirectHandle stack_trace) { Tagged error_stack_data = NewStructInternal( ERROR_STACK_DATA_TYPE, AllocationType::kYoung); DisallowGarbageCollection no_gc; - error_stack_data->set_call_site_infos_or_formatted_stack( - *call_site_infos_or_formatted_stack, SKIP_WRITE_BARRIER); + error_stack_data->set_raw_data_for_call_site_infos_or_formatted_stack( + *raw_data_for_call_site_infos_or_formatted_stack, SKIP_WRITE_BARRIER); error_stack_data->set_stack_trace(*stack_trace, SKIP_WRITE_BARRIER); return direct_handle(error_stack_data, isolate()); } diff --git a/deps/v8/src/heap/factory.h b/deps/v8/src/heap/factory.h index 67abd4458664..bba53c6a5872 100644 --- a/deps/v8/src/heap/factory.h +++ b/deps/v8/src/heap/factory.h @@ -472,7 +472,7 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase { DirectHandle NewErrorStackData( DirectHandle> - call_site_infos_or_formatted_stack, + raw_data_for_call_site_infos_or_formatted_stack, DirectHandle stack_trace); Handle