// 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/allocation.h" #include "src/handles.h" #include "src/safepoint-table.h" namespace v8 { namespace internal { #if V8_TARGET_ARCH_ARM64 typedef uint64_t RegList; #else typedef uint32_t RegList; #endif // Get the number of registers in a given register list. int NumRegs(RegList list); void SetUpJSCallerSavedCodeData(); // Return the code of the n-th saved register available to JavaScript. int JSCallerSavedCode(int n); // Forward declarations. class ExternalCallbackScope; class Isolate; class StackFrameIteratorBase; class ThreadLocalTop; class WasmInstanceObject; class InnerPointerToCodeCache { public: struct InnerPointerToCodeCacheEntry { Address inner_pointer; Code* code; SafepointEntry safepoint_entry; }; explicit InnerPointerToCodeCache(Isolate* isolate) : isolate_(isolate) { Flush(); } Code* GcSafeFindCodeForInnerPointer(Address inner_pointer); Code* GcSafeCastToCode(HeapObject* object, Address inner_pointer); void Flush() { memset(&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 kSize = kNextOffset + kPointerSize; static const int kSlotCount = kSize >> kPointerSizeLog2; }; class StackHandler BASE_EMBEDDED { 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(ENTRY_CONSTRUCT, EntryConstructFrame) \ V(EXIT, ExitFrame) \ V(JAVA_SCRIPT, JavaScriptFrame) \ V(OPTIMIZED, OptimizedFrame) \ V(WASM_COMPILED, WasmCompiledFrame) \ V(WASM_TO_JS, WasmToJsFrame) \ V(JS_TO_WASM, JsToWasmFrame) \ V(WASM_INTERPRETER_ENTRY, WasmInterpreterEntryFrame) \ V(INTERPRETED, InterpretedFrame) \ V(STUB, StubFrame) \ V(STUB_FAILURE_TRAMPOLINE, StubFailureTrampolineFrame) \ V(INTERNAL, InternalFrame) \ V(CONSTRUCT, ConstructFrame) \ V(ARGUMENTS_ADAPTOR, ArgumentsAdaptorFrame) \ V(BUILTIN, BuiltinFrame) \ V(BUILTIN_EXIT, BuiltinExitFrame) // Every pointer in a frame has a slot id. On 32-bit platforms, doubles consume // two slots. // // Stack slot indices >= 0 access the callee stack with slot 0 corresponding to // the callee's saved return address and 1 corresponding to the saved frame // pointer. Some frames have additional information stored in the fixed header, // for example JSFunctions store the function context and marker in the fixed // header, with slot index 2 corresponding to the current function context and 3 // corresponding to the frame marker/JSFunction. // // slot JS frame // +-----------------+-------------------------------- // -n-1 | parameter 0 | ^ // |- - - - - - - - -| | // -n | | Caller // ... | ... | frame slots // -2 | parameter n-1 | (slot < 0) // |- - - - - - - - -| | // -1 | parameter n | v // -----+-----------------+-------------------------------- // 0 | return addr | ^ ^ // |- - - - - - - - -| | | // 1 | saved frame ptr | Fixed | // |- - - - - - - - -| Header <-- frame ptr | // 2 | [Constant Pool] | | | // |- - - - - - - - -| | | // 2+cp |Context/Frm. Type| v if a constant pool | // |-----------------+---- is used, cp = 1, | // 3+cp | | ^ otherwise, cp = 0 | // |- - - - - - - - -| | | // 4+cp | | | Callee // |- - - - - - - - -| | frame slots // ... | | Frame slots (slot >= 0) // |- - - - - - - - -| | | // | | v | // -----+-----------------+----- <-- stack ptr ------------- // class CommonFrameConstants : public AllStatic { public: static const int kCallerFPOffset = 0 * kPointerSize; static const int kCallerPCOffset = kCallerFPOffset + 1 * kFPOnStackSize; static const int kCallerSPOffset = kCallerPCOffset + 1 * kPCOnStackSize; // Fixed part of the frame consists of return address, caller fp, // constant pool (if FLAG_enable_embedded_constant_pool), context, and // function. StandardFrame::IterateExpressions assumes that kLastObjectOffset // is the last object pointer. static const int kFixedFrameSizeAboveFp = kPCOnStackSize + kFPOnStackSize; static const int kFixedSlotCountAboveFp = kFixedFrameSizeAboveFp / kPointerSize; static const int kCPSlotSize = FLAG_enable_embedded_constant_pool ? kPointerSize : 0; static const int kCPSlotCount = kCPSlotSize / kPointerSize; static const int kConstantPoolOffset = kCPSlotSize ? -1 * kPointerSize : 0; static const int kContextOrFrameTypeSize = kPointerSize; static const int kContextOrFrameTypeOffset = -(kCPSlotSize + kContextOrFrameTypeSize); }; // StandardFrames are used for interpreted, full-codegen and optimized // JavaScript frames. They always have a context below the saved fp/constant // pool and below that the JSFunction of the executing function. // // slot JS frame // +-----------------+-------------------------------- // -n-1 | parameter 0 | ^ // |- - - - - - - - -| | // -n | | Caller // ... | ... | frame slots // -2 | parameter n-1 | (slot < 0) // |- - - - - - - - -| | // -1 | parameter n | v // -----+-----------------+-------------------------------- // 0 | return addr | ^ ^ // |- - - - - - - - -| | | // 1 | saved frame ptr | Fixed | // |- - - - - - - - -| Header <-- frame ptr | // 2 | [Constant Pool] | | | // |- - - - - - - - -| | | // 2+cp | Context | | if a constant pool | // |- - - - - - - - -| | is used, cp = 1, | // 3+cp | JSFunction | v otherwise, cp = 0 | // +-----------------+---- | // 4+cp | | ^ Callee // |- - - - - - - - -| | frame slots // ... | | Frame slots (slot >= 0) // |- - - - - - - - -| | | // | | v | // -----+-----------------+----- <-- stack ptr ------------- // class StandardFrameConstants : public CommonFrameConstants { public: static const int kFixedFrameSizeFromFp = 2 * kPointerSize + kCPSlotSize; static const int kFixedFrameSize = kFixedFrameSizeAboveFp + kFixedFrameSizeFromFp; static const int kFixedSlotCountFromFp = kFixedFrameSizeFromFp / kPointerSize; static const int kFixedSlotCount = kFixedFrameSize / kPointerSize; static const int kContextOffset = kContextOrFrameTypeOffset; static const int kFunctionOffset = -2 * kPointerSize - kCPSlotSize; static const int kExpressionsOffset = -3 * kPointerSize - kCPSlotSize; static const int kLastObjectOffset = kContextOffset; }; // OptimizedBuiltinFrameConstants are used for TF-generated builtins. They // always have a context below the saved fp/constant pool and below that the // JSFunction of the executing function and below that an integer (not a Smi) // containing the number of arguments passed to the builtin. // // slot JS frame // +-----------------+-------------------------------- // -n-1 | parameter 0 | ^ // |- - - - - - - - -| | // -n | | Caller // ... | ... | frame slots // -2 | parameter n-1 | (slot < 0) // |- - - - - - - - -| | // -1 | parameter n | v // -----+-----------------+-------------------------------- // 0 | return addr | ^ ^ // |- - - - - - - - -| | | // 1 | saved frame ptr | Fixed | // |- - - - - - - - -| Header <-- frame ptr | // 2 | [Constant Pool] | | | // |- - - - - - - - -| | | // 2+cp | Context | | if a constant pool | // |- - - - - - - - -| | is used, cp = 1, | // 3+cp | JSFunction | | otherwise, cp = 0 | // |- - - - - - - - -| | | // 4+cp | argc | v | // +-----------------+---- | // 5+cp | | ^ Callee // |- - - - - - - - -| | frame slots // ... | | Frame slots (slot >= 0) // |- - - - - - - - -| | | // | | v | // -----+-----------------+----- <-- stack ptr ------------- // class OptimizedBuiltinFrameConstants : public StandardFrameConstants { public: static const int kArgCSize = kPointerSize; static const int kArgCOffset = -3 * kPointerSize - kCPSlotSize; static const int kFixedFrameSize = kFixedFrameSizeAboveFp - kArgCOffset; static const int kFixedSlotCount = kFixedFrameSize / kPointerSize; }; // TypedFrames have a SMI type maker value below the saved FP/constant pool to // distinguish them from StandardFrames, which have a context in that position // instead. // // slot JS frame // +-----------------+-------------------------------- // -n-1 | parameter 0 | ^ // |- - - - - - - - -| | // -n | | Caller // ... | ... | frame slots // -2 | parameter n-1 | (slot < 0) // |- - - - - - - - -| | // -1 | parameter n | v // -----+-----------------+-------------------------------- // 0 | return addr | ^ ^ // |- - - - - - - - -| | | // 1 | saved frame ptr | Fixed | // |- - - - - - - - -| Header <-- frame ptr | // 2 | [Constant Pool] | | | // |- - - - - - - - -| | | // 2+cp |Frame Type Marker| v if a constant pool | // |-----------------+---- is used, cp = 1, | // 3+cp | | ^ otherwise, cp = 0 | // |- - - - - - - - -| | | // 4+cp | | | Callee // |- - - - - - - - -| | frame slots // ... | | Frame slots (slot >= 0) // |- - - - - - - - -| | | // | | v | // -----+-----------------+----- <-- stack ptr ------------- // class TypedFrameConstants : public CommonFrameConstants { public: static const int kFrameTypeSize = kContextOrFrameTypeSize; static const int kFrameTypeOffset = kContextOrFrameTypeOffset; static const int kFixedFrameSizeFromFp = kCPSlotSize + kFrameTypeSize; static const int kFixedSlotCountFromFp = kFixedFrameSizeFromFp / kPointerSize; static const int kFixedFrameSize = StandardFrameConstants::kFixedFrameSizeAboveFp + kFixedFrameSizeFromFp; static const int kFixedSlotCount = kFixedFrameSize / kPointerSize; static const int kFirstPushedFrameValueOffset = -StandardFrameConstants::kCPSlotSize - kFrameTypeSize - kPointerSize; }; #define TYPED_FRAME_PUSHED_VALUE_OFFSET(x) \ (TypedFrameConstants::kFirstPushedFrameValueOffset - (x)*kPointerSize) #define TYPED_FRAME_SIZE(count) \ (TypedFrameConstants::kFixedFrameSize + (count)*kPointerSize) #define TYPED_FRAME_SIZE_FROM_SP(count) \ (TypedFrameConstants::kFixedFrameSizeFromFp + (count)*kPointerSize) #define DEFINE_TYPED_FRAME_SIZES(count) \ static const int kFixedFrameSize = TYPED_FRAME_SIZE(count); \ static const int kFixedSlotCount = kFixedFrameSize / kPointerSize; \ static const int kFixedFrameSizeFromFp = TYPED_FRAME_SIZE_FROM_SP(count); \ static const int kFixedSlotCountFromFp = kFixedFrameSizeFromFp / kPointerSize class ArgumentsAdaptorFrameConstants : public TypedFrameConstants { public: // FP-relative. static const int kFunctionOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(0); static const int kLengthOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(1); DEFINE_TYPED_FRAME_SIZES(2); }; class BuiltinFrameConstants : public TypedFrameConstants { public: // FP-relative. static const int kFunctionOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(0); static const int kLengthOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(1); DEFINE_TYPED_FRAME_SIZES(2); }; class InternalFrameConstants : public TypedFrameConstants { public: // FP-relative. static const int kCodeOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(0); DEFINE_TYPED_FRAME_SIZES(1); }; class FrameDropperFrameConstants : public InternalFrameConstants { public: // FP-relative. static const int kFunctionOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(1); DEFINE_TYPED_FRAME_SIZES(2); }; class ConstructFrameConstants : public TypedFrameConstants { public: // FP-relative. static const int kContextOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(0); static const int kLengthOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(1); static const int kImplicitReceiverOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(2); DEFINE_TYPED_FRAME_SIZES(3); }; class StubFailureTrampolineFrameConstants : public InternalFrameConstants { public: static const int kArgumentsArgumentsOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(0); static const int kArgumentsLengthOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(1); static const int kArgumentsPointerOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(2); static const int kFixedHeaderBottomOffset = kArgumentsPointerOffset; DEFINE_TYPED_FRAME_SIZES(3); }; // Behaves like an exit frame but with target and new target args. class BuiltinExitFrameConstants : public CommonFrameConstants { public: static const int kNewTargetOffset = kCallerPCOffset + 1 * kPointerSize; static const int kTargetOffset = kNewTargetOffset + 1 * kPointerSize; static const int kArgcOffset = kTargetOffset + 1 * kPointerSize; }; class InterpreterFrameConstants : public AllStatic { public: // Fixed frame includes new.target, bytecode array, and bytecode offset. static const int kFixedFrameSize = StandardFrameConstants::kFixedFrameSize + 3 * kPointerSize; static const int kFixedFrameSizeFromFp = StandardFrameConstants::kFixedFrameSizeFromFp + 3 * kPointerSize; // FP-relative. static const int kLastParamFromFp = StandardFrameConstants::kCallerSPOffset; static const int kCallerPCOffsetFromFp = StandardFrameConstants::kCallerPCOffset; static const int kNewTargetFromFp = -StandardFrameConstants::kFixedFrameSizeFromFp - 1 * kPointerSize; static const int kBytecodeArrayFromFp = -StandardFrameConstants::kFixedFrameSizeFromFp - 2 * kPointerSize; static const int kBytecodeOffsetFromFp = -StandardFrameConstants::kFixedFrameSizeFromFp - 3 * kPointerSize; static const int kRegisterFileFromFp = -StandardFrameConstants::kFixedFrameSizeFromFp - 4 * kPointerSize; static const int kExpressionsOffset = kRegisterFileFromFp; // Number of fixed slots in addition to a {StandardFrame}. static const int kExtraSlotCount = InterpreterFrameConstants::kFixedFrameSize / kPointerSize - StandardFrameConstants::kFixedFrameSize / kPointerSize; // Expression index for {StandardFrame::GetExpressionAddress}. static const int kBytecodeArrayExpressionIndex = -2; static const int kBytecodeOffsetExpressionIndex = -1; static const int kRegisterFileExpressionIndex = 0; }; inline static int FPOffsetToFrameSlot(int frame_offset) { return StandardFrameConstants::kFixedSlotCountAboveFp - 1 - frame_offset / kPointerSize; } inline static int FrameSlotToFPOffset(int slot) { return (StandardFrameConstants::kFixedSlotCountAboveFp - 1 - slot) * kPointerSize; } // Abstract base class for all stack frames. class StackFrame BASE_EMBEDDED { 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 = nullptr; Address fp = nullptr; 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) { bool is_marker = ((function_or_marker & kSmiTagMask) == kSmiTag); return is_marker; } // 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_ = NULL; this->isolate_ = original.isolate_; } // Type testers. bool is_entry() const { return type() == ENTRY; } bool is_entry_construct() const { return type() == ENTRY_CONSTRUCT; } 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_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_stub_failure_trampoline() const { return type() == STUB_FAILURE_TRAMPOLINE; } 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 == JAVA_SCRIPT) || (type == OPTIMIZED) || (type == INTERPRETED) || (type == BUILTIN); } 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 : nullptr; } 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; } virtual void SetCallerFp(Address caller_fp) = 0; // Manually changes value of fp in this object. void UpdateFp(Address fp) { state_.fp = fp; } 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(OffsetFrom(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; // Get the code associated with this frame. inline Code* LookupCode() const; // Get the code object that contains the given pc. static inline Code* GetContainingCode(Isolate* isolate, Address pc); // Get the code object containing the given pc and fill in the // safepoint entry and the number of stack slots. The pc must be at // a safepoint. static Code* GetSafepointData(Isolate* isolate, Address pc, SafepointEntry* safepoint_entry, unsigned* stack_slots); virtual void Iterate(ObjectVisitor* v) const = 0; static void IteratePc(ObjectVisitor* 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() { } // Compute the stack pointer for the calling frame. virtual Address GetCallerStackPointer() const = 0; // Printing support. static void PrintIndex(StringStream* accumulator, PrintMode mode, int index); // 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; }; // 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(ObjectVisitor* v) const override; static EntryFrame* cast(StackFrame* frame) { DCHECK(frame->is_entry()); return static_cast(frame); } void SetCallerFp(Address caller_fp) override; 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 EntryConstructFrame: public EntryFrame { public: Type type() const override { return ENTRY_CONSTRUCT; } Code* unchecked_code() const override; static EntryConstructFrame* cast(StackFrame* frame) { DCHECK(frame->is_entry_construct()); return static_cast(frame); } protected: inline explicit EntryConstructFrame(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(ObjectVisitor* v) const override; void SetCallerFp(Address caller_fp) 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 BASE_EMBEDDED { public: // Mode for JavaScriptFrame::Summarize. Exact summary is required to produce // an exact stack trace. It will trigger an assertion failure if that is not // possible, e.g., because of missing deoptimization information. The // approximate mode should produce a summary even without deoptimization // information, but it might miss frames. enum Mode { kExactSummary, kApproximateSummary }; // 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, Mode mode = kExactSummary); 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