/
githubmirror
/
node
Обзор
Документация
Войти
/
githubmirror
/
node
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
deps/v8/src/execution/frames.h
2 155 строк
72 KB
Michaël Zasso
deps: update V8 to 14.6.202.33
24 апр 2026, 19:01
Не верифицирован
24 апр 2026, 19:01
f1e0b83
Код
Авторство
О чём код?
// Copyright 2012 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef V8_EXECUTION_FRAMES_H_ #define V8_EXECUTION_FRAMES_H_ #include "include/v8-initialization.h" #include "src/base/bounds.h" #include "src/codegen/handler-table.h" #include "src/codegen/safepoint-table.h" #include "src/common/assert-scope.h" #include "src/common/globals.h" #include "src/objects/code.h" #include "src/objects/deoptimization-data.h" #include "src/objects/objects.h" #if V8_ENABLE_WEBASSEMBLY #include "src/wasm/stacks.h" #include "src/wasm/wasm-code-manager.h" #endif // V8_ENABLE_WEBASSEMBLY // // Frame inheritance hierarchy (please keep in sync with frame-constants.h): // - CommonFrame // - CommonFrameWithJSLinkage // - JavaScriptFrame (aka StandardFrame) // - UnoptimizedJSFrame // - InterpretedFrame // - BaselineFrame // - OptimizedJSFrame // - MaglevFrame // - TurbofanJSFrame // - TypedFrameWithJSLinkage // - BuiltinFrame // - JavaScriptBuiltinContinuationFrame // - JavaScriptBuiltinContinuationWithCatchFrame // - TurbofanStubWithContextFrame // - TypedFrame // - NativeFrame // - EntryFrame // - ConstructEntryFrame // - ExitFrame // - BuiltinExitFrame // - StubFrame // - JsToWasmFrame // - CWasmEntryFrame // - Internal // - ConstructFrame // - FastConstructFrame // - BuiltinContinuationFrame // - WasmFrame // - WasmExitFrame // - WasmToJsFrame // - WasmInterpreterEntryFrame (#if V8_ENABLE_DRUMBRAKE) // - WasmDebugBreakFrame // - WasmLiftoffSetupFrame // - IrregexpFrame namespace v8 { namespace internal { namespace wasm { class WasmCode; struct JumpBuffer; class StackMemory; } // namespace wasm class AbstractCode; class Debug; class ExternalCallbackScope; class InnerPointerToCodeCache; class Isolate; class ObjectVisitor; class Register; class RootVisitor; class StackFrameInfo; class StackFrameIteratorBase; class StringStream; class ThreadLocalTop; class WasmInstanceObject; class WasmModuleObject; #if V8_ENABLE_DRUMBRAKE class Tuple2; #endif // V8_ENABLE_DRUMBRAKE class StackHandlerConstants : public AllStatic { public: static const int kNextOffset = 0 * kSystemPointerSize; static const int kPaddingOffset = 1 * kSystemPointerSize; static const int kSize = kPaddingOffset + kSystemPointerSize; static const int kSlotCount = kSize >> kSystemPointerSizeLog2; }; class StackHandler { public: // Get the address of this stack handler. inline Address address() const; // Get the next stack handler in the chain. inline StackHandler* next() const; // Get the next stack handler, as an Address. This is safe to use even // when the next handler is null. inline Address next_address() const; // Conversion support. static inline StackHandler* FromAddress(Address address); private: DISALLOW_IMPLICIT_CONSTRUCTORS(StackHandler); }; #define STACK_FRAME_TYPE_LIST(V) \ V(ENTRY, EntryFrame) \ V(CONSTRUCT_ENTRY, ConstructEntryFrame) \ V(EXIT, ExitFrame) \ V(INTERPRETED, InterpretedFrame) \ V(BASELINE, BaselineFrame) \ V(MAGLEV, MaglevFrame) \ V(TURBOFAN_JS, TurbofanJSFrame) \ V(STUB, StubFrame) \ V(TURBOFAN_STUB_WITH_CONTEXT, TurbofanStubWithContextFrame) \ V(BUILTIN_CONTINUATION, BuiltinContinuationFrame) \ V(JAVASCRIPT_BUILTIN_CONTINUATION, JavaScriptBuiltinContinuationFrame) \ V(JAVASCRIPT_BUILTIN_CONTINUATION_WITH_CATCH, \ JavaScriptBuiltinContinuationWithCatchFrame) \ V(INTERNAL, InternalFrame) \ V(CONSTRUCT, ConstructFrame) \ V(FAST_CONSTRUCT, FastConstructFrame) \ V(BUILTIN, BuiltinFrame) \ V(BUILTIN_EXIT, BuiltinExitFrame) \ V(API_CALLBACK_EXIT, ApiCallbackExitFrame) \ V(API_CONSTRUCT_EXIT, ApiConstructExitFrame) \ V(API_NAMED_ACCESSOR_EXIT, ApiNamedAccessorExitFrame) \ V(API_INDEXED_ACCESSOR_EXIT, ApiIndexedAccessorExitFrame) \ V(NATIVE, NativeFrame) \ V(IRREGEXP, IrregexpFrame) \ IF_WASM(V, WASM, WasmFrame) \ IF_WASM(V, WASM_TO_JS, WasmToJsFrame) \ IF_WASM(V, JS_TO_WASM, JsToWasmFrame) \ IF_WASM(V, WASM_JSPI, WasmJspiFrame) \ IF_WASM_DRUMBRAKE(V, WASM_INTERPRETER_ENTRY, WasmInterpreterEntryFrame) \ IF_WASM(V, WASM_DEBUG_BREAK, WasmDebugBreakFrame) \ IF_WASM(V, C_WASM_ENTRY, CWasmEntryFrame) \ /* Can only appear as the first frame of a wasm stack: */ \ IF_WASM(V, WASM_STACK_ENTRY, WasmStackEntryFrame) \ IF_WASM(V, WASM_STACK_EXIT, WasmStackExitFrame) \ IF_WASM(V, WASM_EXIT, WasmExitFrame) \ IF_WASM(V, WASM_LIFTOFF_SETUP, WasmLiftoffSetupFrame) \ IF_WASM(V, WASM_SEGMENT_START, WasmSegmentStartFrame) // Abstract base class for all stack frames. class StackFrame { public: #define DECLARE_TYPE(type, ignore) type, enum Type { NO_FRAME_TYPE = 0, STACK_FRAME_TYPE_LIST(DECLARE_TYPE) NUMBER_OF_TYPES, // Used by FrameScope to indicate that the stack frame is constructed // manually and the FrameScope does not need to emit code. MANUAL }; #undef DECLARE_TYPE // Used to mark the outermost JS entry frame. // // The mark is an opaque value that should be pushed onto the stack directly, // carefully crafted to not be interpreted as a tagged pointer. enum JsFrameMarker { INNER_JSENTRY_FRAME = (0 << kSmiTagSize) | kSmiTag, OUTERMOST_JSENTRY_FRAME = (1 << kSmiTagSize) | kSmiTag }; static_assert((INNER_JSENTRY_FRAME & kHeapObjectTagMask) != kHeapObjectTag); static_assert((OUTERMOST_JSENTRY_FRAME & kHeapObjectTagMask) != kHeapObjectTag); struct State { Address sp = kNullAddress; Address fp = kNullAddress; Address* pc_address = nullptr; Address callee_fp = kNullAddress; Address callee_pc = kNullAddress; Address* constant_pool_address = nullptr; bool is_profiler_entry_frame = false; bool is_stack_exit_frame = false; // The iteration depth of StackFrameIterator, // see StackFrameIterator::Advance. uint32_t iteration_depth = 0; }; // Convert a stack frame type to a marker that can be stored on the stack. // // The marker is an opaque value, not intended to be interpreted in any way // except being checked by IsTypeMarker or converted by MarkerToType. // It has the same tagging as Smis, so any marker value that does not pass // IsTypeMarker can instead be interpreted as a tagged pointer. // // Note that the marker is not a Smi: Smis on 64-bit architectures are stored // in the top 32 bits of a 64-bit value, which in turn makes them expensive // (in terms of code/instruction size) to push as immediates onto the stack. static constexpr int32_t TypeToMarker(Type type) { DCHECK_GE(type, 0); return (type << kSmiTagSize) | kSmiTag; } // Convert a marker back to a stack frame type. // // Unlike the return value of TypeToMarker, this takes an intptr_t, as that is // the type of the value on the stack. static constexpr Type MarkerToType(intptr_t marker) { DCHECK(IsTypeMarker(marker)); return static_cast<Type>(marker >> kSmiTagSize); } // Check if a marker is a stack frame type marker. // // Returns true if the given marker is tagged as a stack frame type marker, // and should be converted back to a stack frame type using MarkerToType. static constexpr bool IsTypeMarker(uintptr_t function_or_marker) { static_assert(kSmiTag == 0); static_assert((std::numeric_limits<uintptr_t>::max() >> kSmiTagSize) > Type::NUMBER_OF_TYPES); return (function_or_marker & kSmiTagMask) == kSmiTag && function_or_marker < (Type::NUMBER_OF_TYPES << kSmiTagSize); } // Copy constructor; it breaks the connection to host iterator // (as an iterator usually lives on stack). StackFrame(const StackFrame& original) V8_NOEXCEPT : iterator_(nullptr), isolate_(original.isolate_), state_(original.state_) {} // Type testers. bool is_entry() const { return type() == ENTRY; } bool is_construct_entry() const { return type() == CONSTRUCT_ENTRY; } bool is_exit() const { return type() == EXIT; } bool is_optimized_js() const { static_assert(TURBOFAN_JS == MAGLEV + 1); return base::IsInRange(type(), MAGLEV, TURBOFAN_JS); } bool is_unoptimized_js() const { static_assert(BASELINE == INTERPRETED + 1); return base::IsInRange(type(), INTERPRETED, BASELINE); } bool is_interpreted() const { return type() == INTERPRETED; } bool is_baseline() const { return type() == BASELINE; } bool is_maglev() const { return type() == MAGLEV; } bool is_turbofan_js() const { return type() == TURBOFAN_JS; } #if V8_ENABLE_WEBASSEMBLY bool is_wasm() const { return this->type() == WASM || this->type() == WASM_SEGMENT_START #ifdef V8_ENABLE_DRUMBRAKE || this->type() == WASM_INTERPRETER_ENTRY #endif // V8_ENABLE_DRUMBRAKE ; } bool is_c_wasm_entry() const { return type() == C_WASM_ENTRY; } bool is_wasm_liftoff_setup() const { return type() == WASM_LIFTOFF_SETUP; } #if V8_ENABLE_DRUMBRAKE bool is_wasm_interpreter_entry() const { return type() == WASM_INTERPRETER_ENTRY; } #endif // V8_ENABLE_DRUMBRAKE bool is_wasm_debug_break() const { return type() == WASM_DEBUG_BREAK; } bool is_wasm_to_js() const { return type() == WASM_TO_JS; } bool is_js_to_wasm() const { return type() == JS_TO_WASM; } #endif // V8_ENABLE_WEBASSEMBLY bool is_builtin() const { return type() == BUILTIN; } bool is_internal() const { return type() == INTERNAL; } bool is_builtin_continuation() const { return type() == BUILTIN_CONTINUATION; } bool is_javascript_builtin_continuation() const { return type() == JAVASCRIPT_BUILTIN_CONTINUATION; } bool is_javascript_builtin_with_catch_continuation() const { return type() == JAVASCRIPT_BUILTIN_CONTINUATION_WITH_CATCH; } bool is_construct() const { return type() == CONSTRUCT; } bool is_fast_construct() const { return type() == FAST_CONSTRUCT; } bool is_builtin_exit() const { return type() == BUILTIN_EXIT; } bool is_api_named_accessor_exit() const { return type() == API_NAMED_ACCESSOR_EXIT; } bool is_api_indexed_accessor_exit() const { return type() == API_INDEXED_ACCESSOR_EXIT; } bool is_api_callback_exit() const { return type() == API_CALLBACK_EXIT; } bool is_api_construct_exit() const { return type() == API_CONSTRUCT_EXIT; } bool is_irregexp() const { return type() == IRREGEXP; } static bool IsJavaScript(Type t) { static_assert(INTERPRETED + 1 == BASELINE); static_assert(BASELINE + 1 == MAGLEV); static_assert(MAGLEV + 1 == TURBOFAN_JS); return t >= INTERPRETED && t <= TURBOFAN_JS; } bool is_javascript() const { return IsJavaScript(type()); } // Accessors. Address sp() const { DCHECK(!InFastCCall()); return state_.sp; } Address fp() const { return state_.fp; } Address callee_fp() const { return state_.callee_fp; } Address callee_pc() const { return state_.callee_pc; } Address caller_sp() const { return GetCallerStackPointer(); } uint32_t iteration_depth() const { return state_.iteration_depth; } inline Address pc() const; bool is_profiler_entry_frame() const { return state_.is_profiler_entry_frame; } bool is_stack_exit_frame() const { return state_.is_stack_exit_frame; } // Skip authentication of the PC, when using CFI. Used in the profiler, where // in certain corner-cases we do not use an address on the stack, which would // be signed, as the PC of the frame. inline Address unauthenticated_pc() const; static inline Address unauthenticated_pc(Address* pc_address); // Conditionally calls either pc() or unauthenticated_pc() based on whether // this is fast C call stack frame. inline Address maybe_unauthenticated_pc() const; static inline Address maybe_unauthenticated_pc(Address* pc_address); // If the stack pointer is missing, this is a fast C call frame. For such // frames we cannot compute a stack pointer because of the missing ExitFrame. bool InFastCCall() const { return state_.sp == kNullAddress; } Address constant_pool() const { return *constant_pool_address(); } void set_constant_pool(Address constant_pool) { *constant_pool_address() = constant_pool; } Address* pc_address() const { return state_.pc_address; } Address* constant_pool_address() const { return state_.constant_pool_address; } // Get the id of this stack frame. StackFrameId id() const { return static_cast<StackFrameId>(caller_sp()); } // Get the top handler from the current stack iterator. inline StackHandler* top_handler() const; // Get the type of this frame. virtual Type type() const = 0; // Get the code associated with this frame. The result might be a Code object // or an empty value. // This method is used by Isolate::PushStackTraceAndDie() for collecting a // stack trace on fatal error and thus it might be called in the middle of GC // and should be as safe as possible. virtual Tagged<HeapObject> unchecked_code() const = 0; // Search for the code associated with this frame. V8_EXPORT_PRIVATE Tagged<Code> LookupCode() const; V8_EXPORT_PRIVATE std::pair<Tagged<Code>, int> LookupCodeAndOffset() const; V8_EXPORT_PRIVATE Tagged<GcSafeCode> GcSafeLookupCode() const; V8_EXPORT_PRIVATE std::pair<Tagged<GcSafeCode>, int> GcSafeLookupCodeAndOffset() const; virtual void Iterate(RootVisitor* v) const = 0; void IteratePc(RootVisitor* v, Address* constant_pool_address, Tagged<GcSafeCode> holder) const; // Sets a callback function for return-address rewriting profilers // to resolve the location of a return address to the location of the // profiler's stashed return address. static void SetReturnAddressLocationResolver( ReturnAddressLocationResolver resolver); static inline Address ReadPC(Address* pc_address); // Resolves pc_address through the resolution address function if one is set. static inline Address* ResolveReturnAddressLocation(Address* pc_address); // Printing support. enum PrintMode { OVERVIEW, DETAILS }; virtual void Print(StringStream* accumulator, PrintMode mode, int index) const; Isolate* isolate() const { return isolate_; } void operator=(const StackFrame& original) = delete; protected: inline explicit StackFrame(StackFrameIteratorBase* iterator); // Compute the stack pointer for the calling frame. virtual Address GetCallerStackPointer() const = 0; const StackFrameIteratorBase* const iterator_; private: Isolate* const isolate_; State state_; static ReturnAddressLocationResolver return_address_location_resolver_; // Fill in the state of the calling frame. virtual void ComputeCallerState(State* state) const = 0; // Get the type and the state of the calling frame. virtual Type GetCallerState(State* state) const; static const intptr_t kIsolateTag = 1; friend class StackFrameIterator; friend class StackFrameIteratorBase; friend class StackHandlerIterator; friend class StackFrameIteratorForProfiler; }; class CommonFrame; class V8_EXPORT_PRIVATE FrameSummary { public: // Subclasses for the different summary kinds: #define FRAME_SUMMARY_VARIANTS(F) \ F(JAVASCRIPT, JavaScriptFrameSummary, javascript_summary_, JavaScript) \ IF_WASM(F, BUILTIN, BuiltinFrameSummary, builtin_summary_, Builtin) \ IF_WASM(F, WASM, WasmFrameSummary, wasm_summary_, Wasm) \ IF_WASM_DRUMBRAKE(F, WASM_INTERPRETED, WasmInterpretedFrameSummary, \ wasm_interpreted_summary_, WasmInterpreted) \ IF_WASM(F, WASM_INLINED, WasmInlinedFrameSummary, wasm_inlined_summary_, \ WasmInlined) #define FRAME_SUMMARY_KIND(kind, type, field, desc) kind, enum Kind { FRAME_SUMMARY_VARIANTS(FRAME_SUMMARY_KIND) }; #undef FRAME_SUMMARY_KIND class FrameSummaryBase { public: FrameSummaryBase(Isolate* isolate, Kind kind) : isolate_(isolate), kind_(kind) {} Isolate* isolate() const { return isolate_; } Kind kind() const { return kind_; } private: Isolate* isolate_; Kind kind_; }; class JavaScriptFrameSummary : public FrameSummaryBase { public: JavaScriptFrameSummary(Isolate* isolate, Tagged<Object> receiver, Tagged<JSFunction> function, Tagged<AbstractCode> abstract_code, int code_offset, bool is_constructor, Tagged<FixedArray> parameters); void EnsureSourcePositionsAvailable(); bool AreSourcePositionsAvailable() const; Handle<Object> receiver() const { return receiver_; } Handle<JSFunction> function() const { return function_; } Handle<AbstractCode> abstract_code() const { return abstract_code_; } int code_offset() const { return code_offset_; } bool is_constructor() const { return is_constructor_; } DirectHandle<FixedArray> parameters() const { return parameters_; } bool is_subject_to_debugging() const; int SourcePosition() const; int SourceStatementPosition() const; Handle<Object> script() const; DirectHandle<Context> native_context() const; DirectHandle<StackFrameInfo> CreateStackFrameInfo() const; private: Handle<Object> receiver_; Handle<JSFunction> function_; Handle<AbstractCode> abstract_code_; int code_offset_; bool is_constructor_; Handle<FixedArray> parameters_; }; #if V8_ENABLE_WEBASSEMBLY class WasmFrameSummary : public FrameSummaryBase { public: WasmFrameSummary(Isolate* isolate, Handle<WasmTrustedInstanceData> instance_data, wasm::WasmCode* code, int byte_offset, int function_index, bool at_to_number_conversion); Handle<Object> receiver() const; uint32_t function_index() const; wasm::WasmCode* code() const { return code_; } // Returns the wire bytes offset relative to the function entry. int code_offset() const { return byte_offset_; } bool is_constructor() const { return false; } bool is_subject_to_debugging() const { return true; } int SourcePosition() const; int SourceStatementPosition() const { return SourcePosition(); } Handle<Script> script() const; DirectHandle<WasmInstanceObject> wasm_instance() const; DirectHandle<WasmTrustedInstanceData> wasm_trusted_instance_data() const { return instance_data_; } DirectHandle<Context> native_context() const; bool at_to_number_conversion() const { return at_to_number_conversion_; } DirectHandle<StackFrameInfo> CreateStackFrameInfo() const; private: Handle<WasmTrustedInstanceData> instance_data_; bool at_to_number_conversion_; wasm::WasmCode* code_; int byte_offset_; int function_index_; }; // Summary of a wasm frame inlined into JavaScript. (Wasm frames inlined into // wasm are expressed by a WasmFrameSummary.) class WasmInlinedFrameSummary : public FrameSummaryBase { public: WasmInlinedFrameSummary(Isolate* isolate, Handle<WasmTrustedInstanceData> instance_data, int function_index, int op_wire_bytes_offset); DirectHandle<WasmInstanceObject> wasm_instance() const; DirectHandle<WasmTrustedInstanceData> wasm_trusted_instance_data() const { return instance_data_; } Handle<Object> receiver() const; uint32_t function_index() const; int code_offset() const { return op_wire_bytes_offset_; } bool is_constructor() const { return false; } bool is_subject_to_debugging() const { return true; } Handle<Script> script() const; int SourcePosition() const; int SourceStatementPosition() const { return SourcePosition(); } DirectHandle<Context> native_context() const; DirectHandle<StackFrameInfo> CreateStackFrameInfo() const; private: Handle<WasmTrustedInstanceData> instance_data_; int function_index_; int op_wire_bytes_offset_; // relative to function offset. }; class BuiltinFrameSummary : public FrameSummaryBase { public: BuiltinFrameSummary(Isolate*, Builtin); Builtin builtin() const { return builtin_; } Handle<Object> receiver() const; int code_offset() const { return 0; } bool is_constructor() const { return false; } bool is_subject_to_debugging() const { return false; } Handle<Object> script() const; int SourcePosition() const { return kNoSourcePosition; } int SourceStatementPosition() const { return 0; } DirectHandle<Context> native_context() const; DirectHandle<StackFrameInfo> CreateStackFrameInfo() const; private: Builtin builtin_; }; #if V8_ENABLE_DRUMBRAKE class WasmInterpretedFrameSummary : public FrameSummaryBase { public: WasmInterpretedFrameSummary(Isolate*, Handle<WasmInstanceObject>, uint32_t function_index, int byte_offset); Handle<WasmInstanceObject> wasm_instance() const { return wasm_instance_; } Handle<WasmTrustedInstanceData> instance_data() const; uint32_t function_index() const { return function_index_; } int byte_offset() const { return byte_offset_; } Handle<Object> receiver() const; int code_offset() const { return byte_offset_; } bool is_constructor() const { return false; } bool is_subject_to_debugging() const { return true; } int SourcePosition() const; int SourceStatementPosition() const { return SourcePosition(); } Handle<Script> script() const; DirectHandle<Context> native_context() const; DirectHandle<StackFrameInfo> CreateStackFrameInfo() const; private: Handle<WasmInstanceObject> wasm_instance_; uint32_t function_index_; int byte_offset_; }; #endif // V8_ENABLE_DRUMBRAKE #endif // V8_ENABLE_WEBASSEMBLY #define FRAME_SUMMARY_CONS(kind, type, field, desc) \ FrameSummary(type summ) : field(summ) {} // NOLINT FRAME_SUMMARY_VARIANTS(FRAME_SUMMARY_CONS) #undef FRAME_SUMMARY_CONS ~FrameSummary(); static FrameSummary GetTop(const CommonFrame* frame); static FrameSummary GetBottom(const CommonFrame* frame); static FrameSummary GetSingle(const CommonFrame* frame); static FrameSummary Get(const CommonFrame* frame, int index); void EnsureSourcePositionsAvailable(); bool AreSourcePositionsAvailable() const; // Dispatched accessors. Handle<Object> receiver() const; int code_offset() const; bool is_constructor() const; bool is_subject_to_debugging() const; Handle<Object> script() const; int SourcePosition() const; int SourceStatementPosition() const; DirectHandle<Context> native_context() const; DirectHandle<StackFrameInfo> CreateStackFrameInfo() const; #define FRAME_SUMMARY_CAST(kind_, type, field, desc) \ bool Is##desc() const { return base_.kind() == kind_; } \ const type& As##desc() const { \ DCHECK_EQ(base_.kind(), kind_); \ return field; \ } FRAME_SUMMARY_VARIANTS(FRAME_SUMMARY_CAST) #undef FRAME_SUMMARY_CAST private: #define FRAME_SUMMARY_FIELD(kind, type, field, desc) type field; union { FrameSummaryBase base_; FRAME_SUMMARY_VARIANTS(FRAME_SUMMARY_FIELD) }; #undef FRAME_SUMMARY_FIELD }; // FrameSummaries represents a collection of summarized frames // within a standard frame. In unoptimized code, there is only one frame, // while in optimized code, multiple frames may exist due to inlining. // The functions are ordered bottom-to-top (i.e. summaries.last() is the // top-most activation; caller comes before callee). struct FrameSummaries { std::vector<FrameSummary> frames; bool top_frame_is_construct_call = false; FrameSummaries() = default; explicit FrameSummaries(FrameSummary summary) : frames() { frames.push_back(summary); } int size() const { return static_cast<int>(frames.size()); } }; class CommonFrame : public StackFrame { public: // Accessors. virtual Tagged<Object> context() const; // TODO(victorgomes): CommonFrames don't have context. virtual int position() const; // Access the expressions in the stack frame including locals. inline Tagged<Object> GetExpression(int index) const; inline void SetExpression(int index, Tagged<Object> value); int ComputeExpressionsCount() const; Address GetCallerStackPointer() const override; // Build a list with summaries for this frame including all inlined frames. // The functions are ordered bottom-to-top (i.e. summaries.last() is the // top-most activation; caller comes before callee). virtual FrameSummaries Summarize(bool never_allocate = false) const; static CommonFrame* cast(StackFrame* frame) { // It is always safe to cast to common. return static_cast<CommonFrame*>(frame); } protected: inline explicit CommonFrame(StackFrameIteratorBase* iterator); bool HasTaggedOutgoingParams(Tagged<GcSafeCode> code_lookup) const; void ComputeCallerState(State* state) const override; // Accessors. inline Address caller_fp() const; inline Address caller_pc() const; // Iterate over expression stack including stack handlers, locals, // and parts of the fixed part including context and code fields. void IterateExpressions(RootVisitor* v) const; void IterateTurbofanJSOptimizedFrame(RootVisitor* v) const; // Returns the address of the n'th expression stack element. virtual Address GetExpressionAddress(int n) const; }; // This frame is used for TF-optimized code without JS linkage, but // contains the context instead of a type marker. class TurbofanStubWithContextFrame : public CommonFrame { public: Type type() const override { return TURBOFAN_STUB_WITH_CONTEXT; } Tagged<HeapObject> unchecked_code() const override; void Iterate(RootVisitor* v) const override; protected: inline explicit TurbofanStubWithContextFrame( StackFrameIteratorBase* iterator); private: friend class StackFrameIteratorBase; }; class TypedFrame : public CommonFrame { public: Tagged<HeapObject> unchecked_code() const override { return {}; } void Iterate(RootVisitor* v) const override; void IterateParamsOfGenericWasmToJSWrapper(RootVisitor* v) const; protected: inline explicit TypedFrame(StackFrameIteratorBase* iterator); }; class CommonFrameWithJSLinkage : public CommonFrame { public: // Accessors. virtual Tagged<JSFunction> function() const = 0; // Access the parameters. virtual Tagged<Object> receiver() const; virtual Tagged<Object> GetParameter(int index) const; virtual int ComputeParametersCount() const; DirectHandle<FixedArray> GetParameters(bool never_allocate) const; virtual int GetActualArgumentCount() const; Tagged<HeapObject> unchecked_code() const override; // Lookup exception handler for current {pc}, returns -1 if none found. Also // returns data associated with the handler site specific to the frame type: // - OptimizedJSFrame : Data is not used and will not return a value. // - UnoptimizedJSFrame: Data is the register index holding the context. virtual int LookupExceptionHandlerInTable( int* data, HandlerTable::CatchPrediction* prediction); // Check if this frame is a constructor frame invoked through 'new'. virtual bool IsConstructor() const; // Summarize Frame FrameSummaries Summarize(bool never_allocate = false) const override; protected: inline explicit CommonFrameWithJSLinkage(StackFrameIteratorBase* iterator); // Determines if the standard frame for the given frame pointer is a // construct frame. static inline bool IsConstructFrame(Address fp); inline Address GetParameterSlot(int index) const; }; class TypedFrameWithJSLinkage : public CommonFrameWithJSLinkage { public: void Iterate(RootVisitor* v) const override; protected: inline explicit TypedFrameWithJSLinkage(StackFrameIteratorBase* iterator); }; class JavaScriptFrame : public CommonFrameWithJSLinkage { public: Type type() const override = 0; // Accessors. Tagged<JSFunction> function() const override; Tagged<Object> unchecked_function() const; Tagged<Script> script() const; Tagged<Object> context() const override; int GetActualArgumentCount() const override; inline void set_receiver(Tagged<Object> value); // Debugger access. void SetParameterValue(int index, Tagged<Object> value) const; // Check if this frame is a constructor frame invoked through 'new'. bool IsConstructor() const override; // Garbage collection support. void Iterate(RootVisitor* v) const override; // Printing support. void Print(StringStream* accumulator, PrintMode mode, int index) const override; // Return a list with {SharedFunctionInfo} objects of this frame. virtual void GetFunctions( std::vector<Tagged<SharedFunctionInfo>>* functions) const; void GetFunctions(std::vector<Handle<SharedFunctionInfo>>* functions) const; // Returns {AbstractCode, code offset} pair for this frame's PC value. std::tuple<Tagged<AbstractCode>, int> GetActiveCodeAndOffset() const; // Architecture-specific register description. static Register fp_register(); static Register context_register(); static Register constant_pool_pointer_register(); bool is_unoptimized() const { return is_unoptimized_js(); } bool is_optimized() const { return is_optimized_js(); } bool is_turbofan() const { return is_turbofan_js(); } static JavaScriptFrame* cast(StackFrame* frame) { DCHECK(frame->is_javascript()); return static_cast<JavaScriptFrame*>(frame); } static void PrintFunctionAndOffset(Isolate* isolate, Tagged<JSFunction> function, Tagged<AbstractCode> code, int code_offset, FILE* file, bool print_line_number); static void PrintTop(Isolate* isolate, FILE* file, bool print_args, bool print_line_number); static void CollectFunctionAndOffsetForICStats(Isolate* isolate, Tagged<JSFunction> function, Tagged<AbstractCode> code, int code_offset); protected: inline explicit JavaScriptFrame(StackFrameIteratorBase* iterator); Address GetCallerStackPointer() const override; virtual void PrintFrameKind(StringStream* accumulator) const {} private: inline Tagged<Object> function_slot_object() const; friend class StackFrameIteratorBase; }; class NativeFrame : public TypedFrame { public: Type type() const override { return NATIVE; } // Garbage collection support. void Iterate(RootVisitor* v) const override {} protected: inline explicit NativeFrame(StackFrameIteratorBase* iterator); private: void ComputeCallerState(State* state) const override; friend class StackFrameIteratorBase; }; // Entry frames are used to enter JavaScript execution from C. class EntryFrame : public TypedFrame { public: Type type() const override { return ENTRY; } Tagged<HeapObject> unchecked_code() const override; // Garbage collection support. void Iterate(RootVisitor* v) const override; static EntryFrame* cast(StackFrame* frame) { DCHECK(frame->is_entry()); return static_cast<EntryFrame*>(frame); } protected: inline explicit EntryFrame(StackFrameIteratorBase* iterator); // The caller stack pointer for entry frames is always zero. The // real information about the caller frame is available through the // link to the top exit frame. Address GetCallerStackPointer() const override { return 0; } private: void ComputeCallerState(State* state) const override; Type GetCallerState(State* state) const override; friend class StackFrameIteratorBase; }; class ConstructEntryFrame : public EntryFrame { public: Type type() const override { return CONSTRUCT_ENTRY; } Tagged<HeapObject> unchecked_code() const override; static ConstructEntryFrame* cast(StackFrame* frame) { DCHECK(frame->is_construct_entry()); return static_cast<ConstructEntryFrame*>(frame); } protected: inline explicit ConstructEntryFrame(StackFrameIteratorBase* iterator); private: friend class StackFrameIteratorBase; }; // Exit frames are used to exit JavaScript execution and go to C, or to switch // out of the current stack for wasm stack-switching. class ExitFrame : public TypedFrame { public: Type type() const override { return EXIT; } // Garbage collection support. void Iterate(RootVisitor* v) const override; static ExitFrame* cast(StackFrame* frame) { DCHECK(frame->is_exit()); return static_cast<ExitFrame*>(frame); } // Compute the state and type of an exit frame given a frame // pointer. Used when constructing the first stack frame seen by an // iterator and the frames following entry frames. static Type GetStateForFramePointer(Address fp, State* state); static Address ComputeStackPointer(Address fp); static StackFrame::Type ComputeFrameType(Address fp); static void FillState(Address fp, Address sp, State* state); protected: inline explicit ExitFrame(StackFrameIteratorBase* iterator); private: void ComputeCallerState(State* state) const override; friend class StackFrameIteratorBase; }; // Builtin exit frames are a special case of exit frames, which are used // whenever C++ builtins (e.g., Math.acos) are called. Their main purpose is // to allow such builtins to appear in stack traces. class BuiltinExitFrame : public ExitFrame { public: Type type() const override { return BUILTIN_EXIT; } Tagged<JSFunction> function() const; Tagged<Object> receiver() const; Tagged<Object> GetParameter(int i) const; int ComputeParametersCount() const; DirectHandle<FixedArray> GetParameters(bool never_allocate) const; // Check if this frame is a constructor frame invoked through 'new'. bool IsConstructor() const; void Print(StringStream* accumulator, PrintMode mode, int index) const override; // Summarize Frame FrameSummaries Summarize(bool never_allocate = false) const override; protected: inline explicit BuiltinExitFrame(StackFrameIteratorBase* iterator); private: inline Tagged<Object> receiver_slot_object() const; inline Tagged<Object> argc_slot_object() const; inline Tagged<Object> target_slot_object() const; inline Tagged<Object> new_target_slot_object() const; friend class StackFrameIteratorBase; }; // Api callback exit frames are a special case of exit frames, which are used // whenever an Api functions (such as v8::Function or v8::FunctionTemplate) are // called. Their main purpose is to support preprocessing of exceptions thrown // from Api functions and as a bonus it allows these functions to appear in // stack traces (see v8_flags.experimental_stack_trace_frames). // TODO(ishell, http://crbug.com/326505377): Rename the class and frame type // to ApiCallExitFrame and API_CALL_EXIT. class ApiCallbackExitFrame : public ExitFrame { public: Type type() const override { return API_CALLBACK_EXIT; } // In case function slot contains FunctionTemplateInfo, instantiate the // function, stores it in the function slot and returns JSFunction handle. // Returns an empty handle if never_allocate is true and instantiation would // be required. DirectHandle<JSFunction> GetFunction(bool never_allocate = false) const; DirectHandle<FunctionTemplateInfo> GetFunctionTemplateInfo() const; inline Tagged<Object> receiver() const; inline Tagged<Object> GetParameter(int i) const; inline int ComputeParametersCount() const; DirectHandle<FixedArray> GetParameters(bool never_allocate) const; inline Tagged<Object> context() const override; void Print(StringStream* accumulator, PrintMode mode, int index) const override; // Summarize Frame FrameSummaries Summarize(bool never_allocate = false) const override { return SummarizeApiFrame(false, never_allocate); } static ApiCallbackExitFrame* cast(StackFrame* frame) { DCHECK(frame->is_api_callback_exit()); return static_cast<ApiCallbackExitFrame*>(frame); } protected: inline explicit ApiCallbackExitFrame(StackFrameIteratorBase* iterator); FrameSummaries SummarizeApiFrame(bool is_constructor, bool never_allocate) const; void PrintApiFrame(StringStream* accumulator, PrintMode mode, int index, bool is_constructor) const; private: // ApiCallbackExitFrame might contain either FunctionTemplateInfo or // JSFunction in the function slot. inline Tagged<HeapObject> target() const; inline void set_target(Tagged<HeapObject> function) const; inline FullObjectSlot target_slot() const; friend class StackFrameIteratorBase; }; class ApiConstructExitFrame : public ApiCallbackExitFrame { public: Type type() const override { return API_CONSTRUCT_EXIT; } // Garbage collection support. void Iterate(RootVisitor* v) const override; void Print(StringStream* accumulator, PrintMode mode, int index) const override; // Summarize Frame FrameSummaries Summarize(bool never_allocate = false) const override { return SummarizeApiFrame(true, never_allocate); } static ApiConstructExitFrame* cast(StackFrame* frame) { DCHECK(frame->is_api_construct_exit()); return static_cast<ApiConstructExitFrame*>(frame); } protected: inline explicit ApiConstructExitFrame(StackFrameIteratorBase* iterator); private: inline FullObjectSlot new_target_slot() const; friend class StackFrameIteratorBase; }; // Api accessor exit frames are a special case of exit frames, which are used // whenever an Api property accessor callbacks (v8::AccessorGetterCallback or // v8::AccessorSetterCallback) are called. Their main purpose is to support // preprocessing of exceptions thrown from these callbacks. class ApiAccessorExitFrame : public ExitFrame { public: inline Tagged<Object> holder() const; // Summarize Frame FrameSummaries Summarize(bool never_allocate = false) const override; static ApiAccessorExitFrame* cast(StackFrame* frame) { DCHECK(frame->is_api_named_accessor_exit() || frame->is_api_indexed_accessor_exit()); return static_cast<ApiAccessorExitFrame*>(frame); } protected: inline explicit ApiAccessorExitFrame(StackFrameIteratorBase* iterator); inline FullObjectSlot property_key_slot() const; inline FullObjectSlot holder_slot() const; friend class StackFrameIteratorBase; }; // Exit frame used for calling named Api accessor/interceptor callbacks. class ApiNamedAccessorExitFrame : public ApiAccessorExitFrame { public: Type type() const override { return API_NAMED_ACCESSOR_EXIT; } inline Tagged<Name> property_name() const; // GC support. void Iterate(RootVisitor* v) const override; void Print(StringStream* accumulator, PrintMode mode, int index) const override; static ApiNamedAccessorExitFrame* cast(StackFrame* frame) { DCHECK(frame->is_api_named_accessor_exit()); return static_cast<ApiNamedAccessorExitFrame*>(frame); } static const ApiNamedAccessorExitFrame* cast(const StackFrame* frame) { DCHECK(frame->is_api_named_accessor_exit()); return static_cast<const ApiNamedAccessorExitFrame*>(frame); } protected: inline explicit ApiNamedAccessorExitFrame(StackFrameIteratorBase* iterator); private: friend class StackFrameIteratorBase; }; // Exit frame used for calling indexed Api interceptor callbacks. class ApiIndexedAccessorExitFrame : public ApiAccessorExitFrame { public: Type type() const override { return API_INDEXED_ACCESSOR_EXIT; } void Print(StringStream* accumulator, PrintMode mode, int index) const override; static ApiIndexedAccessorExitFrame* cast(StackFrame* frame) { DCHECK(frame->is_api_indexed_accessor_exit()); return static_cast<ApiIndexedAccessorExitFrame*>(frame); } protected: inline explicit ApiIndexedAccessorExitFrame(StackFrameIteratorBase* iterator); private: friend class StackFrameIteratorBase; }; class StubFrame : public TypedFrame { public: Type type() const override { return STUB; } Tagged<HeapObject> unchecked_code() const override; // Lookup exception handler for current {pc}, returns -1 if none found. Only // TurboFan stub frames are supported. int LookupExceptionHandlerInTable(); FrameSummaries Summarize(bool never_allocate = false) const override; protected: inline explicit StubFrame(StackFrameIteratorBase* iterator); private: friend class StackFrameIteratorBase; }; class OptimizedJSFrame : public JavaScriptFrame { public: // Return a list with {SharedFunctionInfo} objects of this frame. // The functions are ordered bottom-to-top (i.e. functions.last() // is the top-most activation) void GetFunctions( std::vector<Tagged<SharedFunctionInfo>>* functions) const override; FrameSummaries Summarize(bool never_allocate = false) const override; Tagged<DeoptimizationData> GetDeoptimizationData(Tagged<Code> code, int* deopt_index) const; static int StackSlotOffsetRelativeToFp(int slot_index); // Lookup exception handler for current {pc}, returns -1 if none found. int LookupExceptionHandlerInTable( int* data, HandlerTable::CatchPrediction* prediction) override; virtual int FindReturnPCForTrampoline(Tagged<Code> code, int trampoline_pc) const = 0; protected: inline explicit OptimizedJSFrame(StackFrameIteratorBase* iterator); }; // An unoptimized frame is a JavaScript frame that is executing bytecode. It // may be executing it using the interpreter, or via baseline code compiled from // the bytecode. class UnoptimizedJSFrame : public JavaScriptFrame { public: // Accessors. int position() const override; // Lookup exception handler for current {pc}, returns -1 if none found. int LookupExceptionHandlerInTable( int* data, HandlerTable::CatchPrediction* prediction) override; // Returns the current offset into the bytecode stream. virtual int GetBytecodeOffset() const = 0; // Returns the frame's current bytecode array. Tagged<BytecodeArray> GetBytecodeArray() const; // Access to the interpreter register file for this frame. Tagged<Object> ReadInterpreterRegister(int register_index) const; inline void SetFeedbackVector(Tagged<FeedbackVector> feedback_vector); // Build a list with summaries for this frame including all inlined frames. FrameSummaries Summarize(bool never_allocate = false) const override; static UnoptimizedJSFrame* cast(StackFrame* frame) { DCHECK(frame->is_unoptimized_js()); return static_cast<UnoptimizedJSFrame*>(frame); } protected: inline explicit UnoptimizedJSFrame(StackFrameIteratorBase* iterator); Address GetExpressionAddress(int n) const override; private: friend class StackFrameIteratorBase; }; class InterpretedFrame : public UnoptimizedJSFrame { public: Type type() const override { return INTERPRETED; } // Returns the current offset into the bytecode stream. int GetBytecodeOffset() const override; // Updates the current offset into the bytecode stream, mainly used for stack // unwinding to continue execution at a different bytecode offset. void PatchBytecodeOffset(int new_offset); // Updates the frame's BytecodeArray with |bytecode_array|. Used by the // debugger to swap execution onto a BytecodeArray patched with breakpoints. void PatchBytecodeArray(Tagged<BytecodeArray> bytecode_array); static InterpretedFrame* cast(StackFrame* frame) { DCHECK(frame->is_interpreted()); return static_cast<InterpretedFrame*>(frame); } static const InterpretedFrame* cast(const StackFrame* frame) { DCHECK(frame->is_interpreted()); return static_cast<const InterpretedFrame*>(frame); } protected: inline explicit InterpretedFrame(StackFrameIteratorBase* iterator); private: friend class StackFrameIteratorBase; }; class BaselineFrame : public UnoptimizedJSFrame { public: Type type() const override { return BASELINE; } // Returns the current offset into the bytecode stream. int GetBytecodeOffset() const override; intptr_t GetPCForBytecodeOffset(int lookup_offset) const; void PatchContext(Tagged<Context> value); static BaselineFrame* cast(StackFrame* frame) { DCHECK(frame->is_baseline()); return static_cast<BaselineFrame*>(frame); } static const BaselineFrame* cast(const StackFrame* frame) { DCHECK(frame->is_baseline()); return static_cast<const BaselineFrame*>(frame); } protected: inline explicit BaselineFrame(StackFrameIteratorBase* iterator); private: friend class StackFrameIteratorBase; }; class MaglevFrame : public OptimizedJSFrame { public: Type type() const override { return MAGLEV; } static MaglevFrame* cast(StackFrame* frame) { DCHECK(frame->is_maglev()); return static_cast<MaglevFrame*>(frame); } void Iterate(RootVisitor* v) const override; int FindReturnPCForTrampoline(Tagged<Code> code, int trampoline_pc) const override; DirectHandle<JSFunction> GetInnermostFunction() const; BytecodeOffset GetBytecodeOffsetForOSR() const; static intptr_t StackGuardFrameSize(int register_input_count); protected: inline explicit MaglevFrame(StackFrameIteratorBase* iterator); private: friend class StackFrameIteratorBase; }; class TurbofanJSFrame : public OptimizedJSFrame { public: Type type() const override { return TURBOFAN_JS; } int ComputeParametersCount() const override; void Iterate(RootVisitor* v) const override; int FindReturnPCForTrampoline(Tagged<Code> code, int trampoline_pc) const override; protected: inline explicit TurbofanJSFrame(StackFrameIteratorBase* iterator); private: friend class StackFrameIteratorBase; Tagged<Object> StackSlotAt(int index) const; }; // Builtin frames are built for builtins with JavaScript linkage, such as // various standard library functions (i.e. Math.asin, Math.floor, etc.). class BuiltinFrame final : public TypedFrameWithJSLinkage { public: Type type() const final { return BUILTIN; } static BuiltinFrame* cast(StackFrame* frame) { DCHECK(frame->is_builtin()); return static_cast<BuiltinFrame*>(frame); } Tagged<JSFunction> function() const override; int ComputeParametersCount() const override; protected: inline explicit BuiltinFrame(StackFrameIteratorBase* iterator); private: friend class StackFrameIteratorBase; }; #if V8_ENABLE_WEBASSEMBLY class WasmFrame : public TypedFrame { public: Type type() const override { return WASM; } // Printing support. void Print(StringStream* accumulator, PrintMode mode, int index) const override; // Lookup exception handler for current {pc}, returns -1 if none found. int LookupExceptionHandlerInTable(); void Iterate(RootVisitor* v) const override; // Accessors. virtual V8_EXPORT_PRIVATE Tagged<WasmInstanceObject> wasm_instance() const; virtual Tagged<WasmTrustedInstanceData> trusted_instance_data() const; V8_EXPORT_PRIVATE wasm::NativeModule* native_module() const; virtual wasm::WasmCode* wasm_code() const; int function_index() const; Tagged<Script> script() const; // Byte position in the module, or asm.js source position. int position() const override; Tagged<Object> context() const override; bool at_to_number_conversion() const; // Generated code byte offset in the function. int generated_code_offset() const; bool is_inspectable() const; FrameSummaries Summarize(bool never_allocate = false) const override; static WasmFrame* cast(StackFrame* frame) { DCHECK(frame->is_wasm() #ifdef V8_ENABLE_DRUMBRAKE && !frame->is_wasm_interpreter_entry() #endif // V8_ENABLE_DRUMBRAKE ); return static_cast<WasmFrame*>(frame); } protected: inline explicit WasmFrame(StackFrameIteratorBase* iterator); private: friend class StackFrameIteratorBase; Tagged<WasmModuleObject> module_object() const; }; // WasmSegmentStartFrame is a regular Wasm frame moved to the // beginning of a new stack segment allocated for growable stack. // It requires special handling on return. To indicate that, the WASM frame type // is replaced by WASM_SEGMENT_START. class WasmSegmentStartFrame : public WasmFrame { public: // type() intentionally returns WASM frame type because WasmSegmentStartFrame // behaves exactly like regular WasmFrame in all scenarios. Type type() const override { return WASM; } protected: inline explicit WasmSegmentStartFrame(StackFrameIteratorBase* iterator); private: friend class StackFrameIteratorBase; }; // Wasm to C-API exit frame. class WasmExitFrame : public WasmFrame { public: Type type() const override { return WASM_EXIT; } static Address ComputeStackPointer(Address fp); protected: inline explicit WasmExitFrame(StackFrameIteratorBase* iterator); private: friend class StackFrameIteratorBase; }; #if V8_ENABLE_DRUMBRAKE class WasmInterpreterEntryFrame final : public WasmFrame { public: Type type() const override { return WASM_INTERPRETER_ENTRY; } // GC support. void Iterate(RootVisitor* v) const override; // Printing support. void Print(StringStream* accumulator, PrintMode mode, int index) const override; FrameSummaries Summarize(bool never_allocate = false) const override; // Determine the code for the frame. Tagged<HeapObject> unchecked_code() const override; // Accessors. Tagged<Tuple2> interpreter_object() const; V8_EXPORT_PRIVATE Tagged<WasmInstanceObject> wasm_instance() const override; Tagged<WasmTrustedInstanceData> trusted_instance_data() const override; wasm::WasmCode* wasm_code() const override { UNREACHABLE(); } int function_index(int inlined_function_index) const; int position() const override; Tagged<Object> context() const override; static WasmInterpreterEntryFrame* cast(StackFrame* frame) { DCHECK(frame->is_wasm_interpreter_entry()); return static_cast<WasmInterpreterEntryFrame*>(frame); } protected: inline explicit WasmInterpreterEntryFrame(StackFrameIteratorBase* iterator); Address GetCallerStackPointer() const override; private: friend class StackFrameIteratorBase; Tagged<WasmModuleObject> module_object() const; }; #endif // V8_ENABLE_DRUMBRAKE class WasmDebugBreakFrame final : public TypedFrame { public: Type type() const override { return WASM_DEBUG_BREAK; } // GC support. void Iterate(RootVisitor* v) const override; void Print(StringStream* accumulator, PrintMode mode, int index) const override; static WasmDebugBreakFrame* cast(StackFrame* frame) { DCHECK(frame->is_wasm_debug_break()); return static_cast<WasmDebugBreakFrame*>(frame); } protected: inline explicit WasmDebugBreakFrame(StackFrameIteratorBase*); private: friend class StackFrameIteratorBase; }; class WasmToJsFrame : public WasmFrame { public: Type type() const override { return WASM_TO_JS; } #if V8_ENABLE_DRUMBRAKE void Iterate(RootVisitor* v) const override; #endif // V8_ENABLE_DRUMBRAKE int position() const override { return 0; } Tagged<WasmInstanceObject> wasm_instance() const override; Tagged<WasmTrustedInstanceData> trusted_instance_data() const override; protected: inline explicit WasmToJsFrame(StackFrameIteratorBase* iterator); private: friend class StackFrameIteratorBase; }; class JsToWasmFrame : public StubFrame { public: Type type() const override { return JS_TO_WASM; } void Iterate(RootVisitor* v) const override; protected: inline explicit JsToWasmFrame(StackFrameIteratorBase* iterator); private: friend class StackFrameIteratorBase; }; class WasmJspiFrame : public ExitFrame { public: Type type() const override { return WASM_JSPI; } void Iterate(RootVisitor* v) const override; static void GetStateForJumpBuffer(wasm::JumpBuffer* jmpbuf, State* state); protected: inline explicit WasmJspiFrame(StackFrameIteratorBase* iterator); private: friend class StackFrameIteratorBase; }; class WasmStackEntryFrame : public TypedFrame { public: Type type() const override { return WASM_STACK_ENTRY; } void Iterate(RootVisitor* v) const override {} wasm::WasmCode* wasm_code(); protected: inline explicit WasmStackEntryFrame(StackFrameIteratorBase* iterator); private: friend class StackFrameIteratorBase; }; class WasmStackExitFrame : public TypedFrame { public: Type type() const override { return WASM_STACK_EXIT; } void Iterate(RootVisitor* v) const override {} protected: inline explicit WasmStackExitFrame(StackFrameIteratorBase* iterator); private: friend class StackFrameIteratorBase; }; class CWasmEntryFrame : public StubFrame { public: Type type() const override { return C_WASM_ENTRY; } #if V8_ENABLE_DRUMBRAKE void Iterate(RootVisitor* v) const override; #endif // V8_ENABLE_DRUMBRAKE protected: inline explicit CWasmEntryFrame(StackFrameIteratorBase* iterator); private: friend class StackFrameIteratorBase; Type GetCallerState(State* state) const override; }; class WasmLiftoffSetupFrame : public TypedFrame { public: Type type() const override { return WASM_LIFTOFF_SETUP; } FullObjectSlot wasm_instance_data_slot() const; int GetDeclaredFunctionIndex() const; wasm::NativeModule* GetNativeModule() const; // Garbage collection support. void Iterate(RootVisitor* v) const override; static WasmLiftoffSetupFrame* cast(StackFrame* frame) { DCHECK(frame->is_wasm_liftoff_setup()); return static_cast<WasmLiftoffSetupFrame*>(frame); } protected: inline explicit WasmLiftoffSetupFrame(StackFrameIteratorBase* iterator); private: friend class StackFrameIteratorBase; }; #endif // V8_ENABLE_WEBASSEMBLY class InternalFrame : public TypedFrame { public: Type type() const override { return INTERNAL; } // Garbage collection support. void Iterate(RootVisitor* v) const override; static InternalFrame* cast(StackFrame* frame) { DCHECK(frame->is_internal()); return static_cast<InternalFrame*>(frame); } protected: inline explicit InternalFrame(StackFrameIteratorBase* iterator); private: friend class StackFrameIteratorBase; }; // Construct frames are special trampoline frames introduced to handle // function invocations through 'new'. class ConstructFrame : public InternalFrame { public: Type type() const override { return CONSTRUCT; } static ConstructFrame* cast(StackFrame* frame) { DCHECK(frame->is_construct()); return static_cast<ConstructFrame*>(frame); } void Iterate(RootVisitor* v) const override; protected: inline explicit ConstructFrame(StackFrameIteratorBase* iterator); private: friend class StackFrameIteratorBase; }; // Fast construct frames are special construct trampoline frames that avoid // pushing arguments to the stack twice. class FastConstructFrame : public InternalFrame { public: Type type() const override { return FAST_CONSTRUCT; } static FastConstructFrame* cast(StackFrame* frame) { DCHECK(frame->is_fast_construct()); return static_cast<FastConstructFrame*>(frame); } protected: inline explicit FastConstructFrame(StackFrameIteratorBase* iterator); private: friend class StackFrameIteratorBase; }; class BuiltinContinuationFrame : public InternalFrame { public: Type type() const override { return BUILTIN_CONTINUATION; } static BuiltinContinuationFrame* cast(StackFrame* frame) { DCHECK(frame->is_builtin_continuation()); return static_cast<BuiltinContinuationFrame*>(frame); } protected: inline explicit BuiltinContinuationFrame(StackFrameIteratorBase* iterator); private: friend class StackFrameIteratorBase; }; class JavaScriptBuiltinContinuationFrame : public TypedFrameWithJSLinkage { public: Type type() const override { return JAVASCRIPT_BUILTIN_CONTINUATION; } static JavaScriptBuiltinContinuationFrame* cast(StackFrame* frame) { DCHECK(frame->is_javascript_builtin_continuation()); return static_cast<JavaScriptBuiltinContinuationFrame*>(frame); } Tagged<JSFunction> function() const override; int ComputeParametersCount() const override; intptr_t GetSPToFPDelta() const; Tagged<Object> context() const override; protected: inline explicit JavaScriptBuiltinContinuationFrame( StackFrameIteratorBase* iterator); private: friend class StackFrameIteratorBase; }; class JavaScriptBuiltinContinuationWithCatchFrame : public JavaScriptBuiltinContinuationFrame { public: Type type() const override { return JAVASCRIPT_BUILTIN_CONTINUATION_WITH_CATCH; } static JavaScriptBuiltinContinuationWithCatchFrame* cast(StackFrame* frame) { DCHECK(frame->is_javascript_builtin_with_catch_continuation()); return static_cast<JavaScriptBuiltinContinuationWithCatchFrame*>(frame); } // Patch in the exception object at the appropriate location into the stack // frame. void SetException(Tagged<Object> exception); protected: inline explicit JavaScriptBuiltinContinuationWithCatchFrame( StackFrameIteratorBase* iterator); private: friend class StackFrameIteratorBase; }; class IrregexpFrame : public TypedFrame { public: Type type() const override { return IRREGEXP; } void Iterate(RootVisitor* v) const override { // Irregexp frames should not be visited by GC because they are not visible // to any stack iterator except StackFrameIteratorForProfiler, which is not // used by GC. UNREACHABLE(); } static IrregexpFrame* cast(StackFrame* frame) { DCHECK(frame->is_irregexp()); return static_cast<IrregexpFrame*>(frame); } protected: inline explicit IrregexpFrame(StackFrameIteratorBase* iterator); private: friend class StackFrameIteratorBase; }; class StackFrameIteratorBase { public: StackFrameIteratorBase(const StackFrameIteratorBase&) = delete; StackFrameIteratorBase& operator=(const StackFrameIteratorBase&) = delete; Isolate* isolate() const { return isolate_; } #if V8_ENABLE_WEBASSEMBLY wasm::StackMemory* wasm_stack() const { return wasm_stack_; } #endif bool done() const { return frame_ == nullptr; } #ifdef DEBUG // The StackFrameIteratorForProfiler is limited in functionality because it // may run at an arbitrary point in time where stack contents are not // guaranteed to be in a consistent state and heap accesses may be limited. virtual bool IsStackFrameIteratorForProfiler() const = 0; #endif // DEBUG virtual StackFrame::Type ComputeStackFrameType( StackFrame::State* state) const = 0; protected: // An iterator that iterates over a given thread's stack. explicit StackFrameIteratorBase(Isolate* isolate); Isolate* const isolate_; union { char uninitialized_; #define DECLARE_SINGLETON(ignore, type) type type##_; STACK_FRAME_TYPE_LIST(DECLARE_SINGLETON) #undef DECLARE_SINGLETON }; StackFrame* frame_; StackHandler* handler_; #if V8_ENABLE_WEBASSEMBLY // Stop at the end of the topmost (wasm) stack. bool first_stack_only_ = false; // // Current wasm stack being iterated. wasm::StackMemory* wasm_stack_ = nullptr; #endif StackHandler* handler() const { DCHECK(!done()); return handler_; } // Update the current frame to the given state. void SetNewFrame(StackFrame::Type type, StackFrame::State* state); // A helper function, can set the frame to nullptr. void SetNewFrame(StackFrame::Type type); private: friend class StackFrame; }; class StackFrameIterator : public StackFrameIteratorBase { public: // An iterator that iterates over the isolate's current thread's stack, V8_EXPORT_PRIVATE explicit StackFrameIterator(Isolate* isolate); // An iterator that iterates over a given thread's stack. V8_EXPORT_PRIVATE StackFrameIterator(Isolate* isolate, ThreadLocalTop* t); #if V8_ENABLE_WEBASSEMBLY // Depending on the use case, users of the StackFrameIterator should either: // - Use the default constructor, which iterates the active stack and its // ancestors, but not the suspended stacks. // - Or use the constructor below to iterate the topmost stack only, and // iterate the {Isolate::wasm_stacks()} list on the side to visit all // inactive stacks. struct FirstStackOnly {}; V8_EXPORT_PRIVATE StackFrameIterator(Isolate* isolate, ThreadLocalTop* t, FirstStackOnly); // An iterator that iterates over a given wasm stack segment. V8_EXPORT_PRIVATE StackFrameIterator(Isolate* isolate, wasm::StackMemory* stack); #endif StackFrameIterator(const StackFrameIterator&) = delete; StackFrameIterator& operator=(const StackFrameIterator&) = delete; StackFrame* frame() const { DCHECK(!done()); return frame_; } V8_EXPORT_PRIVATE void Advance(); StackFrame* Reframe(); #if V8_ENABLE_WEBASSEMBLY // Go to the first frame of this stack. void Reset(ThreadLocalTop* top, wasm::StackMemory* stack); #endif #ifdef DEBUG bool IsStackFrameIteratorForProfiler() const override { return false; } #endif // DEBUG StackFrame::Type ComputeStackFrameType( StackFrame::State* state) const override; private: // Go back to the first frame. void Reset(ThreadLocalTop* top); }; // A wrapper around StackFrameIterator that skips over all non-JS frames. class JavaScriptStackFrameIterator final { public: explicit JavaScriptStackFrameIterator(Isolate* isolate) : iterator_(isolate) { if (!done()) Advance(); } JavaScriptStackFrameIterator(Isolate* isolate, ThreadLocalTop* top) : iterator_(isolate, top) { if (!done()) Advance(); } JavaScriptFrame* frame() const { return JavaScriptFrame::cast(iterator_.frame()); } JavaScriptFrame* Reframe() { return JavaScriptFrame::cast(iterator_.Reframe()); } bool done() const { return iterator_.done(); } V8_EXPORT_PRIVATE void Advance(); private: StackFrameIterator iterator_; }; // A wrapper around StackFrameIterator that skips over all non-debuggable // frames (i.e. it iterates over Wasm and debuggable JS frames). class V8_EXPORT_PRIVATE DebuggableStackFrameIterator { public: explicit DebuggableStackFrameIterator(Isolate* isolate); // Skip frames until the frame with the given id is reached. DebuggableStackFrameIterator(Isolate* isolate, StackFrameId id); bool done() const { return iterator_.done(); } void Advance(); int FrameFunctionCount() const; inline CommonFrame* frame() const; inline CommonFrame* Reframe(); inline bool is_javascript() const; #if V8_ENABLE_WEBASSEMBLY inline bool is_wasm() const; #if V8_ENABLE_DRUMBRAKE inline bool is_wasm_interpreter_entry() const; #endif // V8_ENABLE_DRUMBRAKE #endif // V8_ENABLE_WEBASSEMBLY inline JavaScriptFrame* javascript_frame() const; // Use this instead of FrameSummary::GetTop(javascript_frame) to keep // filtering behavior consistent with the rest of // DebuggableStackFrameIterator. FrameSummary GetTopValidFrame() const; private: StackFrameIterator iterator_; static bool IsValidFrame(StackFrame* frame); }; // Similar to StackFrameIterator, but can be created and used at any time and // any stack state. Currently, the only user is the profiler; if this ever // changes, find another name for this class. // IMPORTANT: Do not mark this class as V8_EXPORT_PRIVATE. The profiler creates // instances of this class from a signal handler. If we use V8_EXPORT_PRIVATE // "ld" inserts a symbol stub for the constructor call that may crash with // a stackoverflow when called from a signal handler. class StackFrameIteratorForProfiler : public StackFrameIteratorBase { public: StackFrameIteratorForProfiler(Isolate* isolate, Address pc, Address fp, Address sp, Address lr, Address js_entry_sp); inline StackFrame* frame() const; void Advance(); StackFrame::Type top_frame_type() const { return top_frame_type_; } #ifdef DEBUG bool IsStackFrameIteratorForProfiler() const override { return true; } #endif // DEBUG StackFrame::Type ComputeStackFrameType( StackFrame::State* state) const override; private: void AdvanceOneFrame(); bool IsValidStackAddress(Address addr) const { return low_bound_ <= addr && addr <= high_bound_; } bool IsValidState(const StackFrame::State& frame) const; bool HasValidExitIfEntryFrame(const StackFrame* frame) const; bool IsValidExitFrame(Address fp) const; bool IsValidTop(ThreadLocalTop* top) const; static bool IsValidFrameType(StackFrame::Type type); StackFrame::Type GetCallerIfValid(StackFrame* frame, StackFrame::State* state); // Returns true if the pc points to a bytecode handler and the frame pointer // doesn't seem to be a bytecode handler's frame, which implies that the // bytecode handler has an elided frame. This is not precise and might give // false negatives since it relies on checks to the frame's type marker, // which might be uninitialized. bool IsNoFrameBytecodeHandlerPc(Isolate* isolate, Address pc, Address fp) const; const Address low_bound_; const Address high_bound_; StackFrame::Type top_frame_type_; ExternalCallbackScope* external_callback_scope_; Address top_link_register_; #if V8_ENABLE_WEBASSEMBLY std::vector<std::unique_ptr<wasm::StackMemory>>& wasm_stacks_; #endif }; // We cannot export 'StackFrameIteratorForProfiler' for cctests since the // linker inserted symbol stub may cause a stack overflow // (https://crbug.com/1449195). // We subclass it and export the subclass instead. class V8_EXPORT_PRIVATE StackFrameIteratorForProfilerForTesting : public StackFrameIteratorForProfiler { public: StackFrameIteratorForProfilerForTesting(Isolate* isolate, Address pc, Address fp, Address sp, Address lr, Address js_entry_sp); // Redeclare methods needed by the test. Otherwise we'd have to // export individual methods on the base class (which we don't want to risk). void Advance(); }; // Frame layout helper classes. Used by the deoptimizer and instruction // selector. // ------------------------------------------------------------------------- // How to calculate the frame layout information. Precise, when all information // is available during deoptimization. Conservative, when an overapproximation // is fine. // TODO(jgruber): Investigate whether the conservative kind can be removed. It // seems possible: 1. is_topmost should be known through the outer_state chain // of FrameStateDescriptor; 2. the deopt_kind may be a property of the bailout // id; 3. for continuation_mode, we only care whether it is a mode with catch, // and that is likewise known at compile-time. // There is nothing specific blocking this, the investigation just requires time // and it is not that important to get the exact frame height at compile-time. enum class FrameInfoKind { kPrecise, kConservative, }; // Used by the deoptimizer. Corresponds to frame kinds: enum class BuiltinContinuationMode { STUB, // BuiltinContinuationFrame JAVASCRIPT, // JavaScriptBuiltinContinuationFrame JAVASCRIPT_WITH_CATCH, // JavaScriptBuiltinContinuationWithCatchFrame JAVASCRIPT_HANDLE_EXCEPTION // JavaScriptBuiltinContinuationWithCatchFrame }; class UnoptimizedFrameInfo { public: static UnoptimizedFrameInfo Precise(int parameters_count_with_receiver, int translation_height, bool is_topmost, bool pad_arguments) { return {parameters_count_with_receiver, translation_height, is_topmost, pad_arguments, FrameInfoKind::kPrecise}; } static UnoptimizedFrameInfo Conservative(int parameters_count_with_receiver, int locals_count) { return {parameters_count_with_receiver, locals_count, false, true, FrameInfoKind::kConservative}; } static uint32_t GetStackSizeForAdditionalArguments(int parameters_count); uint32_t register_stack_slot_count() const { return register_stack_slot_count_; } uint32_t frame_size_in_bytes_without_fixed() const { return frame_size_in_bytes_without_fixed_; } uint32_t frame_size_in_bytes() const { return frame_size_in_bytes_; } private: UnoptimizedFrameInfo(int parameters_count_with_receiver, int translation_height, bool is_topmost, bool pad_arguments, FrameInfoKind frame_info_kind); uint32_t register_stack_slot_count_; uint32_t frame_size_in_bytes_without_fixed_; uint32_t frame_size_in_bytes_; }; class ConstructStubFrameInfo { public: static ConstructStubFrameInfo Precise(int translation_height, bool is_topmost) { return {translation_height, is_topmost, FrameInfoKind::kPrecise}; } static ConstructStubFrameInfo Conservative(int parameters_count) { return {parameters_count, false, FrameInfoKind::kConservative}; } uint32_t frame_size_in_bytes_without_fixed() const { return frame_size_in_bytes_without_fixed_; } uint32_t frame_size_in_bytes() const { return frame_size_in_bytes_; } private: ConstructStubFrameInfo(int translation_height, bool is_topmost, FrameInfoKind frame_info_kind); uint32_t frame_size_in_bytes_without_fixed_; uint32_t frame_size_in_bytes_; }; class FastConstructStubFrameInfo { public: static FastConstructStubFrameInfo Precise(bool is_topmost) { return FastConstructStubFrameInfo(is_topmost); } static FastConstructStubFrameInfo Conservative() { // Assume it is the top most frame when conservative. return FastConstructStubFrameInfo(true); } uint32_t frame_size_in_bytes_without_fixed() const { return frame_size_in_bytes_without_fixed_; } uint32_t frame_size_in_bytes() const { return frame_size_in_bytes_; } private: explicit FastConstructStubFrameInfo(bool is_topmost); uint32_t frame_size_in_bytes_without_fixed_; uint32_t frame_size_in_bytes_; }; // Used by BuiltinContinuationFrameInfo. class CallInterfaceDescriptor; class RegisterConfiguration; class BuiltinContinuationFrameInfo { public: static BuiltinContinuationFrameInfo Precise( int translation_height, const CallInterfaceDescriptor& continuation_descriptor, const RegisterConfiguration* register_config, bool is_topmost, DeoptimizeKind deopt_kind, BuiltinContinuationMode continuation_mode) { return {translation_height, continuation_descriptor, register_config, is_topmost, deopt_kind, continuation_mode, FrameInfoKind::kPrecise}; } static BuiltinContinuationFrameInfo Conservative( int parameters_count, const CallInterfaceDescriptor& continuation_descriptor, const RegisterConfiguration* register_config) { // It doesn't matter what we pass as is_topmost, deopt_kind and // continuation_mode; these values are ignored in conservative mode. return {parameters_count, continuation_descriptor, register_config, false, DeoptimizeKind::kEager, BuiltinContinuationMode::STUB, FrameInfoKind::kConservative}; } bool frame_has_result_stack_slot() const { return frame_has_result_stack_slot_; } uint32_t translated_stack_parameter_count() const { return translated_stack_parameter_count_; } uint32_t stack_parameter_count() const { return stack_parameter_count_; } uint32_t frame_size_in_bytes() const { return frame_size_in_bytes_; } uint32_t frame_size_in_bytes_above_fp() const { return frame_size_in_bytes_above_fp_; } private: BuiltinContinuationFrameInfo( int translation_height, const CallInterfaceDescriptor& continuation_descriptor, const RegisterConfiguration* register_config, bool is_topmost, DeoptimizeKind deopt_kind, BuiltinContinuationMode continuation_mode, FrameInfoKind frame_info_kind); bool frame_has_result_stack_slot_; uint32_t translated_stack_parameter_count_; uint32_t stack_parameter_count_; uint32_t frame_size_in_bytes_; uint32_t frame_size_in_bytes_above_fp_; }; } // namespace internal } // namespace v8 #endif // V8_EXECUTION_FRAMES_H_