// 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_FRAMES_H_ #define V8_FRAMES_H_ #include "src/handles.h" #include "src/objects.h" #include "src/objects/code.h" #include "src/safepoint-table.h" namespace v8 { namespace internal { namespace wasm { class WasmCode; } // Forward declarations. class AbstractCode; class Debug; class ExternalCallbackScope; class Isolate; class ObjectVisitor; class RootVisitor; class StackFrameIteratorBase; class StringStream; class ThreadLocalTop; class WasmDebugInfo; class WasmInstanceObject; class WasmModuleObject; class InnerPointerToCodeCache { public: struct InnerPointerToCodeCacheEntry { Address inner_pointer; Code* code; SafepointEntry safepoint_entry; }; explicit InnerPointerToCodeCache(Isolate* isolate) : isolate_(isolate) { Flush(); } void Flush() { memset(static_cast(&cache_[0]), 0, sizeof(cache_)); } InnerPointerToCodeCacheEntry* GetCacheEntry(Address inner_pointer); private: InnerPointerToCodeCacheEntry* cache(int index) { return &cache_[index]; } Isolate* isolate_; static const int kInnerPointerToCodeCacheSize = 1024; InnerPointerToCodeCacheEntry cache_[kInnerPointerToCodeCacheSize]; DISALLOW_COPY_AND_ASSIGN(InnerPointerToCodeCache); }; class StackHandlerConstants : public AllStatic { public: static const int kNextOffset = 0 * kPointerSize; static const int kPaddingOffset = 1 * kPointerSize; static const int kSize = kPaddingOffset + kPointerSize; static const int kSlotCount = kSize >> kPointerSizeLog2; }; 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; // 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(OPTIMIZED, OptimizedFrame) \ V(WASM_COMPILED, WasmCompiledFrame) \ V(WASM_TO_JS, WasmToJsFrame) \ V(JS_TO_WASM, JsToWasmFrame) \ V(WASM_INTERPRETER_ENTRY, WasmInterpreterEntryFrame) \ V(C_WASM_ENTRY, CWasmEntryFrame) \ V(WASM_COMPILE_LAZY, WasmCompileLazyFrame) \ V(INTERPRETED, InterpretedFrame) \ 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(ARGUMENTS_ADAPTOR, ArgumentsAdaptorFrame) \ 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 { NONE = 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 // Opaque data type for identifying stack frames. Used extensively // by the debugger. // ID_MIN_VALUE and ID_MAX_VALUE are specified to ensure that enumeration type // has correct value range (see Issue 830 for more details). enum Id { ID_MIN_VALUE = kMinInt, ID_MAX_VALUE = kMaxInt, NO_ID = 0 }; // 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_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)); return static_cast(marker >> kSmiTagSize); } // 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) { 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_interpreted() const { return type() == INTERPRETED; } bool is_wasm_compiled() const { return type() == WASM_COMPILED; } bool is_wasm_compile_lazy() const { return type() == WASM_COMPILE_LAZY; } bool is_wasm_to_js() const { return type() == WASM_TO_JS; } bool is_js_to_wasm() const { return type() == JS_TO_WASM; } bool is_wasm_interpreter_entry() const { return type() == WASM_INTERPRETER_ENTRY; } bool is_arguments_adaptor() const { return type() == ARGUMENTS_ADAPTOR; } 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; } virtual bool is_standard() const { return false; } bool is_java_script() const { Type type = this->type(); return (type == OPTIMIZED) || (type == INTERPRETED) || (type == BUILTIN) || (type == JAVA_SCRIPT_BUILTIN_CONTINUATION) || (type == JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH); } bool is_wasm() const { Type type = this->type(); return type == WASM_COMPILED || type == WASM_INTERPRETER_ENTRY; } // Accessors. Address sp() const { return state_.sp; } Address fp() const { return state_.fp; } Address callee_pc() const { return state_.callee_pc_address ? *state_.callee_pc_address : kNullAddress; } Address caller_sp() const { return GetCallerStackPointer(); } // If this frame is optimized and was dynamically aligned return its old // unaligned frame pointer. When the frame is deoptimized its FP will shift // up one word and become unaligned. Address UnpaddedFP() const; Address pc() const { return *pc_address(); } void set_pc(Address pc) { *pc_address() = pc; } 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. Id 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. Code* LookupCode() const; virtual void Iterate(RootVisitor* v) const = 0; static void IteratePc(RootVisitor* v, Address* pc_address, Address* constant_pool_address, Code* holder); // 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); // 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 NativeFrame : public StackFrame { public: Type type() const override { return NATIVE; } Code* unchecked_code() const override { return nullptr; } // Garbage collection support. void Iterate(RootVisitor* v) const override {} protected: inline explicit NativeFrame(StackFrameIteratorBase* iterator); Address GetCallerStackPointer() const override; private: void ComputeCallerState(State* state) const override; friend class StackFrameIteratorBase; }; // Entry frames are used to enter JavaScript execution from C. class EntryFrame: public StackFrame { public: Type type() const override { return ENTRY; } Code* 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(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; } Code* unchecked_code() const override; static ConstructEntryFrame* cast(StackFrame* frame) { DCHECK(frame->is_construct_entry()); return static_cast(frame); } protected: inline explicit ConstructEntryFrame(StackFrameIteratorBase* iterator); private: friend class StackFrameIteratorBase; }; // Exit frames are used to exit JavaScript execution and go to C. class ExitFrame: public StackFrame { public: Type type() const override { return EXIT; } Code* unchecked_code() const override; Object*& code_slot() const; // Garbage collection support. void Iterate(RootVisitor* v) const override; static ExitFrame* cast(StackFrame* frame) { DCHECK(frame->is_exit()); return static_cast(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); Address GetCallerStackPointer() const override; 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; } static BuiltinExitFrame* cast(StackFrame* frame) { DCHECK(frame->is_builtin_exit()); return static_cast(frame); } JSFunction* function() const; Object* receiver() const; bool IsConstructor() const; void Print(StringStream* accumulator, PrintMode mode, int index) const override; protected: inline explicit BuiltinExitFrame(StackFrameIteratorBase* iterator); private: Object* GetParameter(int i) const; int ComputeParametersCount() const; inline Object* receiver_slot_object() const; inline Object* argc_slot_object() const; inline Object* target_slot_object() const; inline Object* new_target_slot_object() const; friend class StackFrameIteratorBase; }; class StandardFrame; class FrameSummary { public: // Subclasses for the different summary kinds: #define FRAME_SUMMARY_VARIANTS(F) \ F(JAVA_SCRIPT, JavaScriptFrameSummary, java_script_summary_, JavaScript) \ F(WASM_COMPILED, WasmCompiledFrameSummary, wasm_compiled_summary_, \ WasmCompiled) \ F(WASM_INTERPRETED, WasmInterpretedFrameSummary, wasm_interpreted_summary_, \ WasmInterpreted) #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); 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_; } bool is_subject_to_debugging() const; int SourcePosition() const; int SourceStatementPosition() const; Handle script() const; Handle FunctionName() const; Handle native_context() const; private: Handle receiver_; Handle function_; Handle abstract_code_; int code_offset_; bool is_constructor_; }; class WasmFrameSummary : public FrameSummaryBase { protected: WasmFrameSummary(Isolate*, Kind, Handle, bool at_to_number_conversion); public: Handle receiver() const; uint32_t function_index() const; 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