// 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/safepoint-table.h" #include "src/common/globals.h" #include "src/handles/handles.h" #include "src/objects/code.h" #include "src/objects/objects.h" // // Frame inheritance hierarchy (please keep in sync with frame-constants.h): // - CommonFrame // - CommonFrameWithJSLinkage // - JavaScriptFrame (aka StandardFrame) // - UnoptimizedFrame // - InterpretedFrame // - BaselineFrame // - OptimizedFrame // - TypedFrameWithJSLinkage // - BuiltinFrame // - JavaScriptBuiltinContinuationFrame // - JavaScriptBuiltinContinuationWithCatchFrame // - TypedFrame // - NativeFrame // - EntryFrame // - ConstructEntryFrame // - ExitFrame // - BuiltinExitFrame // - StubFrame // - JsToWasmFrame // - CWasmEntryFrame // - Internal // - ConstructFrame // - BuiltinContinuationFrame // - WasmFrame // - WasmExitFrame // - WasmDebugBreakFrame // - WasmCompileLazyFrame // 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; 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) \ IF_WASM(V, WASM, WasmFrame) \ IF_WASM(V, WASM_TO_JS, WasmToJsFrame) \ IF_WASM(V, JS_TO_WASM, JsToWasmFrame) \ IF_WASM(V, STACK_SWITCH, StackSwitchFrame) \ IF_WASM(V, WASM_DEBUG_BREAK, WasmDebugBreakFrame) \ IF_WASM(V, C_WASM_ENTRY, CWasmEntryFrame) \ IF_WASM(V, WASM_EXIT, WasmExitFrame) \ IF_WASM(V, WASM_COMPILE_LAZY, WasmCompileLazyFrame) \ V(INTERPRETED, InterpretedFrame) \ V(BASELINE, BaselineFrame) \ V(OPTIMIZED, OptimizedFrame) \ V(STUB, StubFrame) \ V(BUILTIN_CONTINUATION, BuiltinContinuationFrame) \ V(JAVA_SCRIPT_BUILTIN_CONTINUATION, JavaScriptBuiltinContinuationFrame) \ V(JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH, \ JavaScriptBuiltinContinuationWithCatchFrame) \ V(INTERNAL, InternalFrame) \ V(CONSTRUCT, ConstructFrame) \ V(BUILTIN, BuiltinFrame) \ V(BUILTIN_EXIT, BuiltinExitFrame) \ V(NATIVE, NativeFrame) // 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_address = nullptr; Address* constant_pool_address = nullptr; }; // 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 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 Type MarkerToType(intptr_t marker) { DCHECK(IsTypeMarker(marker)); intptr_t type = marker >> kSmiTagSize; // TODO(petermarshall): There is a bug in the arm simulators that causes // invalid frame markers. #if (defined(USE_SIMULATOR) && \ (V8_TARGET_ARCH_ARM64 || V8_TARGET_ARCH_ARM)) || \ V8_TARGET_ARCH_RISCV64 if (static_cast(type) >= Type::NUMBER_OF_TYPES) { // Appease UBSan. return Type::NUMBER_OF_TYPES; } #else DCHECK_LT(static_cast(type), Type::NUMBER_OF_TYPES); #endif return static_cast(type); } // Check if a marker is a stack frame type marker or a tagged pointer. // // 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. // Otherwise, the value is a tagged function pointer. static bool IsTypeMarker(intptr_t function_or_marker) { return (function_or_marker & kSmiTagMask) == kSmiTag; } // Copy constructor; it breaks the connection to host iterator // (as an iterator usually lives on stack). StackFrame(const StackFrame& original) V8_NOEXCEPT { this->state_ = original.state_; this->iterator_ = nullptr; this->isolate_ = original.isolate_; } // 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() const { return type() == OPTIMIZED; } bool is_unoptimized() 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; } #if V8_ENABLE_WEBASSEMBLY bool is_wasm() const { return this->type() == WASM; } bool is_c_wasm_entry() const { return type() == C_WASM_ENTRY; } bool is_wasm_compile_lazy() const { return type() == WASM_COMPILE_LAZY; } 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_java_script_builtin_continuation() const { return type() == JAVA_SCRIPT_BUILTIN_CONTINUATION; } bool is_java_script_builtin_with_catch_continuation() const { return type() == JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH; } bool is_construct() const { return type() == CONSTRUCT; } bool is_builtin_exit() const { return type() == BUILTIN_EXIT; } static bool IsJavaScript(Type t) { STATIC_ASSERT(INTERPRETED + 1 == BASELINE); STATIC_ASSERT(BASELINE + 1 == OPTIMIZED); return t >= INTERPRETED && t <= OPTIMIZED; } bool is_java_script() const { return IsJavaScript(type()); } // Accessors. Address sp() const { return state_.sp; } Address fp() const { return state_.fp; } Address callee_fp() const { return state_.callee_fp; } inline Address callee_pc() const; Address caller_sp() const { return GetCallerStackPointer(); } inline Address pc() const; // 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; 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(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. // This method could be called during marking phase of GC. virtual Code unchecked_code() const = 0; // Search for the code associated with this frame. V8_EXPORT_PRIVATE Code LookupCode() const; virtual void Iterate(RootVisitor* v) const = 0; void IteratePc(RootVisitor* v, Address* pc_address, Address* constant_pool_address, Code 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); virtual ~StackFrame() = default; // Compute the stack pointer for the calling frame. virtual Address GetCallerStackPointer() const = 0; // Compute the stack frame type for the given state. static Type ComputeType(const StackFrameIteratorBase* iterator, State* state); #ifdef DEBUG bool can_access_heap_objects() const; #endif private: const StackFrameIteratorBase* iterator_; Isolate* 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 SafeStackFrameIterator; }; class CommonFrame; class V8_EXPORT_PRIVATE FrameSummary { public: // Subclasses for the different summary kinds: #define FRAME_SUMMARY_VARIANTS(F) \ F(JAVA_SCRIPT, JavaScriptFrameSummary, java_script_summary_, JavaScript) \ IF_WASM(F, WASM, WasmFrameSummary, wasm_summary_, Wasm) #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, Object receiver, JSFunction function, AbstractCode abstract_code, int code_offset, bool is_constructor, FixedArray parameters); void EnsureSourcePositionsAvailable(); bool AreSourcePositionsAvailable() const; Handle receiver() const { return receiver_; } Handle function() const { return function_; } Handle abstract_code() const { return abstract_code_; } int code_offset() const { return code_offset_; } bool is_constructor() const { return is_constructor_; } Handle parameters() const { return parameters_; } bool is_subject_to_debugging() const; int SourcePosition() const; int SourceStatementPosition() const; Handle script() const; Handle native_context() const; Handle CreateStackFrameInfo() const; private: Handle receiver_; Handle function_; Handle abstract_code_; int code_offset_; bool is_constructor_; Handle parameters_; }; #if V8_ENABLE_WEBASSEMBLY class WasmFrameSummary : public FrameSummaryBase { public: WasmFrameSummary(Isolate*, Handle, wasm::WasmCode*, int code_offset, bool at_to_number_conversion); Handle receiver() const; uint32_t function_index() const; wasm::WasmCode* code() const { return code_; } int code_offset() const { return code_offset_; } V8_EXPORT_PRIVATE int byte_offset() const; bool is_constructor() const { return false; } bool is_subject_to_debugging() const { return true; } int SourcePosition() const; int SourceStatementPosition() const { return SourcePosition(); } Handle