michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef vm_Stack_h michael@0: #define vm_Stack_h michael@0: michael@0: #include "mozilla/MemoryReporting.h" michael@0: michael@0: #include "jsfun.h" michael@0: #include "jsscript.h" michael@0: michael@0: #include "jit/AsmJSLink.h" michael@0: #include "jit/JitFrameIterator.h" michael@0: #ifdef CHECK_OSIPOINT_REGISTERS michael@0: #include "jit/Registers.h" // for RegisterDump michael@0: #endif michael@0: #include "js/OldDebugAPI.h" michael@0: michael@0: struct JSCompartment; michael@0: struct JSGenerator; michael@0: michael@0: namespace js { michael@0: michael@0: class ArgumentsObject; michael@0: class AsmJSModule; michael@0: class InterpreterRegs; michael@0: class ScopeObject; michael@0: class ScriptFrameIter; michael@0: class SPSProfiler; michael@0: class InterpreterFrame; michael@0: class StaticBlockObject; michael@0: michael@0: struct ScopeCoordinate; michael@0: michael@0: // VM stack layout michael@0: // michael@0: // A JSRuntime's stack consists of a linked list of activations. Every activation michael@0: // contains a number of scripted frames that are either running in the interpreter michael@0: // (InterpreterActivation) or JIT code (JitActivation). The frames inside a single michael@0: // activation are contiguous: whenever C++ calls back into JS, a new activation is michael@0: // pushed. michael@0: // michael@0: // Every activation is tied to a single JSContext and JSCompartment. This means we michael@0: // can reconstruct a given context's stack by skipping activations belonging to other michael@0: // contexts. This happens whenever an embedding enters the JS engine on cx1 and michael@0: // then, from a native called by the JS engine, reenters the VM on cx2. michael@0: michael@0: // Interpreter frames (InterpreterFrame) michael@0: // michael@0: // Each interpreter script activation (global or function code) is given a michael@0: // fixed-size header (js::InterpreterFrame). The frame contains bookkeeping michael@0: // information about the activation and links to the previous frame. michael@0: // michael@0: // The values after an InterpreterFrame in memory are its locals followed by its michael@0: // expression stack. InterpreterFrame::argv_ points to the frame's arguments. michael@0: // Missing formal arguments are padded with |undefined|, so the number of michael@0: // arguments is always >= the number of formals. michael@0: // michael@0: // The top of an activation's current frame's expression stack is pointed to by michael@0: // the activation's "current regs", which contains the stack pointer 'sp'. In michael@0: // the interpreter, sp is adjusted as individual values are pushed and popped michael@0: // from the stack and the InterpreterRegs struct (pointed to by the michael@0: // InterpreterActivation) is a local var of js::Interpret. michael@0: michael@0: enum MaybeCheckAliasing { CHECK_ALIASING = true, DONT_CHECK_ALIASING = false }; michael@0: michael@0: /*****************************************************************************/ michael@0: michael@0: #ifdef DEBUG michael@0: extern void michael@0: CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script, uint32_t i); michael@0: #endif michael@0: michael@0: namespace jit { michael@0: class BaselineFrame; michael@0: class RematerializedFrame; michael@0: } michael@0: michael@0: /* michael@0: * Pointer to either a ScriptFrameIter::Data, an InterpreterFrame, or a Baseline michael@0: * JIT frame. michael@0: * michael@0: * The Debugger may cache ScriptFrameIter::Data as a bookmark to reconstruct a michael@0: * ScriptFrameIter without doing a full stack walk. michael@0: * michael@0: * There is no way to directly create such an AbstractFramePtr. To do so, the michael@0: * user must call ScriptFrameIter::copyDataAsAbstractFramePtr(). michael@0: * michael@0: * ScriptFrameIter::abstractFramePtr() will never return an AbstractFramePtr michael@0: * that is in fact a ScriptFrameIter::Data. michael@0: * michael@0: * To recover a ScriptFrameIter settled at the location pointed to by an michael@0: * AbstractFramePtr, use the THIS_FRAME_ITER macro in Debugger.cpp. As an michael@0: * aside, no asScriptFrameIterData() is provided because C++ is stupid and michael@0: * cannot forward declare inner classes. michael@0: */ michael@0: michael@0: class AbstractFramePtr michael@0: { michael@0: friend class FrameIter; michael@0: michael@0: uintptr_t ptr_; michael@0: michael@0: enum { michael@0: Tag_ScriptFrameIterData = 0x0, michael@0: Tag_InterpreterFrame = 0x1, michael@0: Tag_BaselineFrame = 0x2, michael@0: Tag_RematerializedFrame = 0x3, michael@0: TagMask = 0x3 michael@0: }; michael@0: michael@0: public: michael@0: AbstractFramePtr() michael@0: : ptr_(0) michael@0: {} michael@0: michael@0: AbstractFramePtr(InterpreterFrame *fp) michael@0: : ptr_(fp ? uintptr_t(fp) | Tag_InterpreterFrame : 0) michael@0: { michael@0: MOZ_ASSERT_IF(fp, asInterpreterFrame() == fp); michael@0: } michael@0: michael@0: AbstractFramePtr(jit::BaselineFrame *fp) michael@0: : ptr_(fp ? uintptr_t(fp) | Tag_BaselineFrame : 0) michael@0: { michael@0: MOZ_ASSERT_IF(fp, asBaselineFrame() == fp); michael@0: } michael@0: michael@0: AbstractFramePtr(jit::RematerializedFrame *fp) michael@0: : ptr_(fp ? uintptr_t(fp) | Tag_RematerializedFrame : 0) michael@0: { michael@0: MOZ_ASSERT_IF(fp, asRematerializedFrame() == fp); michael@0: } michael@0: michael@0: explicit AbstractFramePtr(JSAbstractFramePtr frame) michael@0: : ptr_(uintptr_t(frame.raw())) michael@0: { michael@0: } michael@0: michael@0: static AbstractFramePtr FromRaw(void *raw) { michael@0: AbstractFramePtr frame; michael@0: frame.ptr_ = uintptr_t(raw); michael@0: return frame; michael@0: } michael@0: michael@0: bool isScriptFrameIterData() const { michael@0: return !!ptr_ && (ptr_ & TagMask) == Tag_ScriptFrameIterData; michael@0: } michael@0: bool isInterpreterFrame() const { michael@0: return (ptr_ & TagMask) == Tag_InterpreterFrame; michael@0: } michael@0: InterpreterFrame *asInterpreterFrame() const { michael@0: JS_ASSERT(isInterpreterFrame()); michael@0: InterpreterFrame *res = (InterpreterFrame *)(ptr_ & ~TagMask); michael@0: JS_ASSERT(res); michael@0: return res; michael@0: } michael@0: bool isBaselineFrame() const { michael@0: return (ptr_ & TagMask) == Tag_BaselineFrame; michael@0: } michael@0: jit::BaselineFrame *asBaselineFrame() const { michael@0: JS_ASSERT(isBaselineFrame()); michael@0: jit::BaselineFrame *res = (jit::BaselineFrame *)(ptr_ & ~TagMask); michael@0: JS_ASSERT(res); michael@0: return res; michael@0: } michael@0: bool isRematerializedFrame() const { michael@0: return (ptr_ & TagMask) == Tag_RematerializedFrame; michael@0: } michael@0: jit::RematerializedFrame *asRematerializedFrame() const { michael@0: JS_ASSERT(isRematerializedFrame()); michael@0: jit::RematerializedFrame *res = (jit::RematerializedFrame *)(ptr_ & ~TagMask); michael@0: JS_ASSERT(res); michael@0: return res; michael@0: } michael@0: michael@0: void *raw() const { return reinterpret_cast(ptr_); } michael@0: michael@0: bool operator ==(const AbstractFramePtr &other) const { return ptr_ == other.ptr_; } michael@0: bool operator !=(const AbstractFramePtr &other) const { return ptr_ != other.ptr_; } michael@0: michael@0: operator bool() const { return !!ptr_; } michael@0: michael@0: inline JSObject *scopeChain() const; michael@0: inline CallObject &callObj() const; michael@0: inline bool initFunctionScopeObjects(JSContext *cx); michael@0: inline void pushOnScopeChain(ScopeObject &scope); michael@0: michael@0: inline JSCompartment *compartment() const; michael@0: michael@0: inline bool hasCallObj() const; michael@0: inline bool isGeneratorFrame() const; michael@0: inline bool isYielding() const; michael@0: inline bool isFunctionFrame() const; michael@0: inline bool isGlobalFrame() const; michael@0: inline bool isEvalFrame() const; michael@0: inline bool isFramePushedByExecute() const; michael@0: inline bool isDebuggerFrame() const; michael@0: michael@0: inline JSScript *script() const; michael@0: inline JSFunction *fun() const; michael@0: inline JSFunction *maybeFun() const; michael@0: inline JSFunction *callee() const; michael@0: inline Value calleev() const; michael@0: inline Value &thisValue() const; michael@0: michael@0: inline bool isNonEvalFunctionFrame() const; michael@0: inline bool isNonStrictDirectEvalFrame() const; michael@0: inline bool isStrictEvalFrame() const; michael@0: michael@0: inline unsigned numActualArgs() const; michael@0: inline unsigned numFormalArgs() const; michael@0: michael@0: inline Value *argv() const; michael@0: michael@0: inline bool hasArgs() const; michael@0: inline bool hasArgsObj() const; michael@0: inline ArgumentsObject &argsObj() const; michael@0: inline void initArgsObj(ArgumentsObject &argsobj) const; michael@0: inline bool useNewType() const; michael@0: michael@0: inline bool copyRawFrameSlots(AutoValueVector *vec) const; michael@0: michael@0: inline Value &unaliasedVar(uint32_t i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING); michael@0: inline Value &unaliasedLocal(uint32_t i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING); michael@0: inline Value &unaliasedFormal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING); michael@0: inline Value &unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING); michael@0: template inline void unaliasedForEachActual(JSContext *cx, Op op); michael@0: michael@0: inline bool prevUpToDate() const; michael@0: inline void setPrevUpToDate() const; michael@0: michael@0: JSObject *evalPrevScopeChain(JSContext *cx) const; michael@0: michael@0: inline void *maybeHookData() const; michael@0: inline void setHookData(void *data) const; michael@0: inline HandleValue returnValue() const; michael@0: inline void setReturnValue(const Value &rval) const; michael@0: michael@0: bool hasPushedSPSFrame() const; michael@0: michael@0: inline void popBlock(JSContext *cx) const; michael@0: inline void popWith(JSContext *cx) const; michael@0: }; michael@0: michael@0: class NullFramePtr : public AbstractFramePtr michael@0: { michael@0: public: michael@0: NullFramePtr() michael@0: : AbstractFramePtr() michael@0: { } michael@0: }; michael@0: michael@0: /*****************************************************************************/ michael@0: michael@0: /* Flags specified for a frame as it is constructed. */ michael@0: enum InitialFrameFlags { michael@0: INITIAL_NONE = 0, michael@0: INITIAL_CONSTRUCT = 0x20, /* == InterpreterFrame::CONSTRUCTING, asserted below */ michael@0: }; michael@0: michael@0: enum ExecuteType { michael@0: EXECUTE_GLOBAL = 0x1, /* == InterpreterFrame::GLOBAL */ michael@0: EXECUTE_DIRECT_EVAL = 0x4, /* == InterpreterFrame::EVAL */ michael@0: EXECUTE_INDIRECT_EVAL = 0x5, /* == InterpreterFrame::GLOBAL | EVAL */ michael@0: EXECUTE_DEBUG = 0xc, /* == InterpreterFrame::EVAL | DEBUGGER */ michael@0: EXECUTE_DEBUG_GLOBAL = 0xd /* == InterpreterFrame::EVAL | DEBUGGER | GLOBAL */ michael@0: }; michael@0: michael@0: /*****************************************************************************/ michael@0: michael@0: class InterpreterFrame michael@0: { michael@0: public: michael@0: enum Flags { michael@0: /* Primary frame type */ michael@0: GLOBAL = 0x1, /* frame pushed for a global script */ michael@0: FUNCTION = 0x2, /* frame pushed for a scripted call */ michael@0: michael@0: /* Frame subtypes */ michael@0: EVAL = 0x4, /* frame pushed for eval() or debugger eval */ michael@0: michael@0: michael@0: /* michael@0: * Frame pushed for debugger eval. michael@0: * - Don't bother to JIT it, because it's probably short-lived. michael@0: * - It is required to have a scope chain object outside the michael@0: * js::ScopeObject hierarchy: either a global object, or a michael@0: * DebugScopeObject (not a ScopeObject, despite the name) michael@0: * - If evalInFramePrev_ is set, then this frame was created for an michael@0: * "eval in frame" call, which can push a successor to any live michael@0: * frame; so its logical "prev" frame is not necessarily the michael@0: * previous frame in memory. Iteration should treat michael@0: * evalInFramePrev_ as this frame's previous frame. michael@0: */ michael@0: DEBUGGER = 0x8, michael@0: michael@0: GENERATOR = 0x10, /* frame is associated with a generator */ michael@0: CONSTRUCTING = 0x20, /* frame is for a constructor invocation */ michael@0: michael@0: /* michael@0: * Generator frame state michael@0: * michael@0: * YIELDING and SUSPENDED are similar, but there are differences. After michael@0: * a generator yields, SendToGenerator immediately clears the YIELDING michael@0: * flag, but the frame will still have the SUSPENDED flag. Also, when the michael@0: * generator returns but before it's GC'ed, YIELDING is not set but michael@0: * SUSPENDED is. michael@0: */ michael@0: YIELDING = 0x40, /* Interpret dispatched JSOP_YIELD */ michael@0: SUSPENDED = 0x80, /* Generator is not running. */ michael@0: michael@0: /* Function prologue state */ michael@0: HAS_CALL_OBJ = 0x100, /* CallObject created for heavyweight fun */ michael@0: HAS_ARGS_OBJ = 0x200, /* ArgumentsObject created for needsArgsObj script */ michael@0: michael@0: /* Lazy frame initialization */ michael@0: HAS_HOOK_DATA = 0x400, /* frame has hookData_ set */ michael@0: HAS_RVAL = 0x800, /* frame has rval_ set */ michael@0: HAS_SCOPECHAIN = 0x1000, /* frame has scopeChain_ set */ michael@0: michael@0: /* Debugger state */ michael@0: PREV_UP_TO_DATE = 0x4000, /* see DebugScopes::updateLiveScopes */ michael@0: michael@0: /* Used in tracking calls and profiling (see vm/SPSProfiler.cpp) */ michael@0: HAS_PUSHED_SPS_FRAME = 0x8000, /* SPS was notified of enty */ michael@0: michael@0: /* michael@0: * If set, we entered one of the JITs and ScriptFrameIter should skip michael@0: * this frame. michael@0: */ michael@0: RUNNING_IN_JIT = 0x10000, michael@0: michael@0: /* Miscellaneous state. */ michael@0: USE_NEW_TYPE = 0x20000 /* Use new type for constructed |this| object. */ michael@0: }; michael@0: michael@0: private: michael@0: mutable uint32_t flags_; /* bits described by Flags */ michael@0: union { /* describes what code is executing in a */ michael@0: JSScript *script; /* global frame */ michael@0: JSFunction *fun; /* function frame, pre GetScopeChain */ michael@0: } exec; michael@0: union { /* describes the arguments of a function */ michael@0: unsigned nactual; /* for non-eval frames */ michael@0: JSScript *evalScript; /* the script of an eval-in-function */ michael@0: } u; michael@0: mutable JSObject *scopeChain_; /* if HAS_SCOPECHAIN, current scope chain */ michael@0: Value rval_; /* if HAS_RVAL, return value of the frame */ michael@0: ArgumentsObject *argsObj_; /* if HAS_ARGS_OBJ, the call's arguments object */ michael@0: michael@0: /* michael@0: * Previous frame and its pc and sp. Always nullptr for michael@0: * InterpreterActivation's entry frame, always non-nullptr for inline michael@0: * frames. michael@0: */ michael@0: InterpreterFrame *prev_; michael@0: jsbytecode *prevpc_; michael@0: Value *prevsp_; michael@0: michael@0: void *hookData_; /* if HAS_HOOK_DATA, closure returned by call hook */ michael@0: michael@0: /* michael@0: * For an eval-in-frame DEBUGGER frame, the frame in whose scope we're michael@0: * evaluating code. Iteration treats this as our previous frame. michael@0: */ michael@0: AbstractFramePtr evalInFramePrev_; michael@0: michael@0: Value *argv_; /* If hasArgs(), points to frame's arguments. */ michael@0: LifoAlloc::Mark mark_; /* Used to release memory for this frame. */ michael@0: michael@0: static void staticAsserts() { michael@0: JS_STATIC_ASSERT(offsetof(InterpreterFrame, rval_) % sizeof(Value) == 0); michael@0: JS_STATIC_ASSERT(sizeof(InterpreterFrame) % sizeof(Value) == 0); michael@0: } michael@0: michael@0: void writeBarrierPost(); michael@0: michael@0: /* michael@0: * The utilities are private since they are not able to assert that only michael@0: * unaliased vars/formals are accessed. Normal code should prefer the michael@0: * InterpreterFrame::unaliased* members (or InterpreterRegs::stackDepth for michael@0: * the usual "depth is at least" assertions). michael@0: */ michael@0: Value *slots() const { return (Value *)(this + 1); } michael@0: Value *base() const { return slots() + script()->nfixed(); } michael@0: michael@0: friend class FrameIter; michael@0: friend class InterpreterRegs; michael@0: friend class InterpreterStack; michael@0: friend class jit::BaselineFrame; michael@0: michael@0: /* michael@0: * Frame initialization, called by InterpreterStack operations after acquiring michael@0: * the raw memory for the frame: michael@0: */ michael@0: michael@0: /* Used for Invoke and Interpret. */ michael@0: void initCallFrame(JSContext *cx, InterpreterFrame *prev, jsbytecode *prevpc, Value *prevsp, michael@0: JSFunction &callee, JSScript *script, Value *argv, uint32_t nactual, michael@0: InterpreterFrame::Flags flags); michael@0: michael@0: /* Used for global and eval frames. */ michael@0: void initExecuteFrame(JSContext *cx, JSScript *script, AbstractFramePtr prev, michael@0: const Value &thisv, JSObject &scopeChain, ExecuteType type); michael@0: michael@0: public: michael@0: /* michael@0: * Frame prologue/epilogue michael@0: * michael@0: * Every stack frame must have 'prologue' called before executing the michael@0: * first op and 'epilogue' called after executing the last op and before michael@0: * popping the frame (whether the exit is exceptional or not). michael@0: * michael@0: * For inline JS calls/returns, it is easy to call the prologue/epilogue michael@0: * exactly once. When calling JS from C++, Invoke/Execute push the stack michael@0: * frame but do *not* call the prologue/epilogue. That means Interpret michael@0: * must call the prologue/epilogue for the entry frame. This scheme michael@0: * simplifies jit compilation. michael@0: * michael@0: * An important corner case is what happens when an error occurs (OOM, michael@0: * over-recursed) after pushing the stack frame but before 'prologue' is michael@0: * called or completes fully. To simplify usage, 'epilogue' does not assume michael@0: * 'prologue' has completed and handles all the intermediate state details. michael@0: */ michael@0: michael@0: bool prologue(JSContext *cx); michael@0: void epilogue(JSContext *cx); michael@0: michael@0: bool initFunctionScopeObjects(JSContext *cx); michael@0: michael@0: /* Initialize local variables of newly-pushed frame. */ michael@0: void initVarsToUndefined(); michael@0: michael@0: /* michael@0: * Stack frame type michael@0: * michael@0: * A stack frame may have one of three types, which determines which michael@0: * members of the frame may be accessed and other invariants: michael@0: * michael@0: * global frame: execution of global code or an eval in global code michael@0: * function frame: execution of function code or an eval in a function michael@0: */ michael@0: michael@0: bool isFunctionFrame() const { michael@0: return !!(flags_ & FUNCTION); michael@0: } michael@0: michael@0: bool isGlobalFrame() const { michael@0: return !!(flags_ & GLOBAL); michael@0: } michael@0: michael@0: /* michael@0: * Eval frames michael@0: * michael@0: * As noted above, global and function frames may optionally be 'eval michael@0: * frames'. Eval code shares its parent's arguments which means that the michael@0: * arg-access members of InterpreterFrame may not be used for eval frames. michael@0: * Search for 'hasArgs' below for more details. michael@0: * michael@0: * A further sub-classification of eval frames is whether the frame was michael@0: * pushed for an ES5 strict-mode eval(). michael@0: */ michael@0: michael@0: bool isEvalFrame() const { michael@0: return flags_ & EVAL; michael@0: } michael@0: michael@0: bool isEvalInFunction() const { michael@0: return (flags_ & (EVAL | FUNCTION)) == (EVAL | FUNCTION); michael@0: } michael@0: michael@0: bool isNonEvalFunctionFrame() const { michael@0: return (flags_ & (FUNCTION | EVAL)) == FUNCTION; michael@0: } michael@0: michael@0: inline bool isStrictEvalFrame() const { michael@0: return isEvalFrame() && script()->strict(); michael@0: } michael@0: michael@0: bool isNonStrictEvalFrame() const { michael@0: return isEvalFrame() && !script()->strict(); michael@0: } michael@0: michael@0: bool isDirectEvalFrame() const { michael@0: return isEvalFrame() && script()->staticLevel() > 0; michael@0: } michael@0: michael@0: bool isNonStrictDirectEvalFrame() const { michael@0: return isNonStrictEvalFrame() && isDirectEvalFrame(); michael@0: } michael@0: michael@0: /* michael@0: * Previous frame michael@0: * michael@0: * A frame's 'prev' frame is either null or the previous frame pointed to michael@0: * by cx->regs->fp when this frame was pushed. Often, given two prev-linked michael@0: * frames, the next-frame is a function or eval that was called by the michael@0: * prev-frame, but not always: the prev-frame may have called a native that michael@0: * reentered the VM through JS_CallFunctionValue on the same context michael@0: * (without calling JS_SaveFrameChain) which pushed the next-frame. Thus, michael@0: * 'prev' has little semantic meaning and basically just tells the VM what michael@0: * to set cx->regs->fp to when this frame is popped. michael@0: */ michael@0: michael@0: InterpreterFrame *prev() const { michael@0: return prev_; michael@0: } michael@0: michael@0: AbstractFramePtr evalInFramePrev() const { michael@0: JS_ASSERT(isEvalFrame()); michael@0: return evalInFramePrev_; michael@0: } michael@0: michael@0: /* michael@0: * (Unaliased) locals and arguments michael@0: * michael@0: * Only non-eval function frames have arguments. The arguments pushed by michael@0: * the caller are the 'actual' arguments. The declared arguments of the michael@0: * callee are the 'formal' arguments. When the caller passes less actual michael@0: * arguments, missing formal arguments are padded with |undefined|. michael@0: * michael@0: * When a local/formal variable is "aliased" (accessed by nested closures, michael@0: * dynamic scope operations, or 'arguments), the canonical location for michael@0: * that value is the slot of an activation object (scope or arguments). michael@0: * Currently, all variables are given slots in *both* the stack frame and michael@0: * heap objects, even though, as just described, only one should ever be michael@0: * accessed. Thus, it is up to the code performing an access to access the michael@0: * correct value. These functions assert that accesses to stack values are michael@0: * unaliased. For more about canonical values locations. michael@0: */ michael@0: michael@0: inline Value &unaliasedVar(uint32_t i, MaybeCheckAliasing = CHECK_ALIASING); michael@0: inline Value &unaliasedLocal(uint32_t i, MaybeCheckAliasing = CHECK_ALIASING); michael@0: michael@0: bool hasArgs() const { return isNonEvalFunctionFrame(); } michael@0: inline Value &unaliasedFormal(unsigned i, MaybeCheckAliasing = CHECK_ALIASING); michael@0: inline Value &unaliasedActual(unsigned i, MaybeCheckAliasing = CHECK_ALIASING); michael@0: template inline void unaliasedForEachActual(Op op); michael@0: michael@0: bool copyRawFrameSlots(AutoValueVector *v); michael@0: michael@0: unsigned numFormalArgs() const { JS_ASSERT(hasArgs()); return fun()->nargs(); } michael@0: unsigned numActualArgs() const { JS_ASSERT(hasArgs()); return u.nactual; } michael@0: michael@0: /* Watch out, this exposes a pointer to the unaliased formal arg array. */ michael@0: Value *argv() const { return argv_; } michael@0: michael@0: /* michael@0: * Arguments object michael@0: * michael@0: * If a non-eval function has script->needsArgsObj, an arguments object is michael@0: * created in the prologue and stored in the local variable for the michael@0: * 'arguments' binding (script->argumentsLocal). Since this local is michael@0: * mutable, the arguments object can be overwritten and we can "lose" the michael@0: * arguments object. Thus, InterpreterFrame keeps an explicit argsObj_ field so michael@0: * that the original arguments object is always available. michael@0: */ michael@0: michael@0: ArgumentsObject &argsObj() const; michael@0: void initArgsObj(ArgumentsObject &argsobj); michael@0: michael@0: JSObject *createRestParameter(JSContext *cx); michael@0: michael@0: /* michael@0: * Scope chain michael@0: * michael@0: * In theory, the scope chain would contain an object for every lexical michael@0: * scope. However, only objects that are required for dynamic lookup are michael@0: * actually created. michael@0: * michael@0: * Given that an InterpreterFrame corresponds roughly to a ES5 Execution Context michael@0: * (ES5 10.3), InterpreterFrame::varObj corresponds to the VariableEnvironment michael@0: * component of a Exection Context. Intuitively, the variables object is michael@0: * where new bindings (variables and functions) are stored. One might michael@0: * expect that this is either the Call object or scopeChain.globalObj for michael@0: * function or global code, respectively, however the JSAPI allows calls of michael@0: * Execute to specify a variables object on the scope chain other than the michael@0: * call/global object. This allows embeddings to run multiple scripts under michael@0: * the same global, each time using a new variables object to collect and michael@0: * discard the script's global variables. michael@0: */ michael@0: michael@0: inline HandleObject scopeChain() const; michael@0: michael@0: inline ScopeObject &aliasedVarScope(ScopeCoordinate sc) const; michael@0: inline GlobalObject &global() const; michael@0: inline CallObject &callObj() const; michael@0: inline JSObject &varObj(); michael@0: michael@0: inline void pushOnScopeChain(ScopeObject &scope); michael@0: inline void popOffScopeChain(); michael@0: michael@0: /* michael@0: * For blocks with aliased locals, these interfaces push and pop entries on michael@0: * the scope chain. michael@0: */ michael@0: michael@0: bool pushBlock(JSContext *cx, StaticBlockObject &block); michael@0: void popBlock(JSContext *cx); michael@0: michael@0: /* michael@0: * With michael@0: * michael@0: * Entering/leaving a |with| block pushes/pops an object on the scope chain. michael@0: * Pushing uses pushOnScopeChain, popping should use popWith. michael@0: */ michael@0: michael@0: void popWith(JSContext *cx); michael@0: michael@0: /* michael@0: * Script michael@0: * michael@0: * All function and global frames have an associated JSScript which holds michael@0: * the bytecode being executed for the frame. This script/bytecode does michael@0: * not reflect any inlining that has been performed by the method JIT. michael@0: * If other frames were inlined into this one, the script/pc reflect the michael@0: * point of the outermost call. Inlined frame invariants: michael@0: * michael@0: * - Inlined frames have the same scope chain as the outer frame. michael@0: * - Inlined frames have the same strictness as the outer frame. michael@0: * - Inlined frames can only make calls to other JIT frames associated with michael@0: * the same VMFrame. Other calls force expansion of the inlined frames. michael@0: */ michael@0: michael@0: JSScript *script() const { michael@0: return isFunctionFrame() michael@0: ? isEvalFrame() michael@0: ? u.evalScript michael@0: : fun()->nonLazyScript() michael@0: : exec.script; michael@0: } michael@0: michael@0: /* Return the previous frame's pc. */ michael@0: jsbytecode *prevpc() { michael@0: JS_ASSERT(prev_); michael@0: return prevpc_; michael@0: } michael@0: michael@0: /* Return the previous frame's sp. */ michael@0: Value *prevsp() { michael@0: JS_ASSERT(prev_); michael@0: return prevsp_; michael@0: } michael@0: michael@0: /* michael@0: * Function michael@0: * michael@0: * All function frames have an associated interpreted JSFunction. The michael@0: * function returned by fun() and maybeFun() is not necessarily the michael@0: * original canonical function which the frame's script was compiled michael@0: * against. michael@0: */ michael@0: michael@0: JSFunction* fun() const { michael@0: JS_ASSERT(isFunctionFrame()); michael@0: return exec.fun; michael@0: } michael@0: michael@0: JSFunction* maybeFun() const { michael@0: return isFunctionFrame() ? fun() : nullptr; michael@0: } michael@0: michael@0: /* michael@0: * This value michael@0: * michael@0: * Every frame has a this value although, until 'this' is computed, the michael@0: * value may not be the semantically-correct 'this' value. michael@0: * michael@0: * The 'this' value is stored before the formal arguments for function michael@0: * frames and directly before the frame for global frames. The *Args michael@0: * members assert !isEvalFrame(), so we implement specialized inline michael@0: * methods for accessing 'this'. When the caller has static knowledge that michael@0: * a frame is a function, 'functionThis' allows more efficient access. michael@0: */ michael@0: michael@0: Value &functionThis() const { michael@0: JS_ASSERT(isFunctionFrame()); michael@0: if (isEvalFrame()) michael@0: return ((Value *)this)[-1]; michael@0: return argv()[-1]; michael@0: } michael@0: michael@0: JSObject &constructorThis() const { michael@0: JS_ASSERT(hasArgs()); michael@0: return argv()[-1].toObject(); michael@0: } michael@0: michael@0: Value &thisValue() const { michael@0: if (flags_ & (EVAL | GLOBAL)) michael@0: return ((Value *)this)[-1]; michael@0: return argv()[-1]; michael@0: } michael@0: michael@0: /* michael@0: * Callee michael@0: * michael@0: * Only function frames have a callee. An eval frame in a function has the michael@0: * same callee as its containing function frame. maybeCalleev can be used michael@0: * to return a value that is either the callee object (for function frames) or michael@0: * null (for global frames). michael@0: */ michael@0: michael@0: JSFunction &callee() const { michael@0: JS_ASSERT(isFunctionFrame()); michael@0: return calleev().toObject().as(); michael@0: } michael@0: michael@0: const Value &calleev() const { michael@0: JS_ASSERT(isFunctionFrame()); michael@0: return mutableCalleev(); michael@0: } michael@0: michael@0: const Value &maybeCalleev() const { michael@0: Value &calleev = flags_ & (EVAL | GLOBAL) michael@0: ? ((Value *)this)[-2] michael@0: : argv()[-2]; michael@0: JS_ASSERT(calleev.isObjectOrNull()); michael@0: return calleev; michael@0: } michael@0: michael@0: Value &mutableCalleev() const { michael@0: JS_ASSERT(isFunctionFrame()); michael@0: if (isEvalFrame()) michael@0: return ((Value *)this)[-2]; michael@0: return argv()[-2]; michael@0: } michael@0: michael@0: CallReceiver callReceiver() const { michael@0: return CallReceiverFromArgv(argv()); michael@0: } michael@0: michael@0: /* michael@0: * Frame compartment michael@0: * michael@0: * A stack frame's compartment is the frame's containing context's michael@0: * compartment when the frame was pushed. michael@0: */ michael@0: michael@0: inline JSCompartment *compartment() const; michael@0: michael@0: /* Debugger hook data */ michael@0: michael@0: bool hasHookData() const { michael@0: return !!(flags_ & HAS_HOOK_DATA); michael@0: } michael@0: michael@0: void* hookData() const { michael@0: JS_ASSERT(hasHookData()); michael@0: return hookData_; michael@0: } michael@0: michael@0: void* maybeHookData() const { michael@0: return hasHookData() ? hookData_ : nullptr; michael@0: } michael@0: michael@0: void setHookData(void *v) { michael@0: hookData_ = v; michael@0: flags_ |= HAS_HOOK_DATA; michael@0: } michael@0: michael@0: bool hasPushedSPSFrame() { michael@0: return !!(flags_ & HAS_PUSHED_SPS_FRAME); michael@0: } michael@0: michael@0: void setPushedSPSFrame() { michael@0: flags_ |= HAS_PUSHED_SPS_FRAME; michael@0: } michael@0: michael@0: void unsetPushedSPSFrame() { michael@0: flags_ &= ~HAS_PUSHED_SPS_FRAME; michael@0: } michael@0: michael@0: /* Return value */ michael@0: michael@0: bool hasReturnValue() const { michael@0: return !!(flags_ & HAS_RVAL); michael@0: } michael@0: michael@0: MutableHandleValue returnValue() { michael@0: if (!(flags_ & HAS_RVAL)) michael@0: rval_.setUndefined(); michael@0: return MutableHandleValue::fromMarkedLocation(&rval_); michael@0: } michael@0: michael@0: void markReturnValue() { michael@0: flags_ |= HAS_RVAL; michael@0: } michael@0: michael@0: void setReturnValue(const Value &v) { michael@0: rval_ = v; michael@0: markReturnValue(); michael@0: } michael@0: michael@0: void clearReturnValue() { michael@0: rval_.setUndefined(); michael@0: markReturnValue(); michael@0: } michael@0: michael@0: /* michael@0: * A "generator" frame is a function frame associated with a generator. michael@0: * Since generators are not executed LIFO, the VM copies a single abstract michael@0: * generator frame back and forth between the LIFO VM stack (when the michael@0: * generator is active) and a snapshot stored in JSGenerator (when the michael@0: * generator is inactive). A generator frame is comprised of an michael@0: * InterpreterFrame structure and the values that make up the arguments, michael@0: * locals, and expression stack. The layout in the JSGenerator snapshot michael@0: * matches the layout on the stack (see the "VM stack layout" comment michael@0: * above). michael@0: */ michael@0: michael@0: bool isGeneratorFrame() const { michael@0: bool ret = flags_ & GENERATOR; michael@0: JS_ASSERT_IF(ret, isNonEvalFunctionFrame()); michael@0: return ret; michael@0: } michael@0: michael@0: void initGeneratorFrame() const { michael@0: JS_ASSERT(!isGeneratorFrame()); michael@0: JS_ASSERT(isNonEvalFunctionFrame()); michael@0: flags_ |= GENERATOR; michael@0: } michael@0: michael@0: Value *generatorArgsSnapshotBegin() const { michael@0: JS_ASSERT(isGeneratorFrame()); michael@0: return argv() - 2; michael@0: } michael@0: michael@0: Value *generatorArgsSnapshotEnd() const { michael@0: JS_ASSERT(isGeneratorFrame()); michael@0: return argv() + js::Max(numActualArgs(), numFormalArgs()); michael@0: } michael@0: michael@0: Value *generatorSlotsSnapshotBegin() const { michael@0: JS_ASSERT(isGeneratorFrame()); michael@0: return (Value *)(this + 1); michael@0: } michael@0: michael@0: enum TriggerPostBarriers { michael@0: DoPostBarrier = true, michael@0: NoPostBarrier = false michael@0: }; michael@0: template michael@0: void copyFrameAndValues(JSContext *cx, Value *vp, InterpreterFrame *otherfp, michael@0: const Value *othervp, Value *othersp); michael@0: michael@0: /* michael@0: * js::Execute pushes both global and function frames (since eval() in a michael@0: * function pushes a frame with isFunctionFrame() && isEvalFrame()). Most michael@0: * code should not care where a frame was pushed, but if it is necessary to michael@0: * pick out frames pushed by js::Execute, this is the right query: michael@0: */ michael@0: michael@0: bool isFramePushedByExecute() const { michael@0: return !!(flags_ & (GLOBAL | EVAL)); michael@0: } michael@0: michael@0: /* michael@0: * Other flags michael@0: */ michael@0: michael@0: InitialFrameFlags initialFlags() const { michael@0: JS_STATIC_ASSERT((int)INITIAL_NONE == 0); michael@0: JS_STATIC_ASSERT((int)INITIAL_CONSTRUCT == (int)CONSTRUCTING); michael@0: uint32_t mask = CONSTRUCTING; michael@0: JS_ASSERT((flags_ & mask) != mask); michael@0: return InitialFrameFlags(flags_ & mask); michael@0: } michael@0: michael@0: void setConstructing() { michael@0: flags_ |= CONSTRUCTING; michael@0: } michael@0: michael@0: bool isConstructing() const { michael@0: return !!(flags_ & CONSTRUCTING); michael@0: } michael@0: michael@0: /* michael@0: * These two queries should not be used in general: the presence/absence of michael@0: * the call/args object is determined by the static(ish) properties of the michael@0: * JSFunction/JSScript. These queries should only be performed when probing michael@0: * a stack frame that may be in the middle of the prologue (during which michael@0: * time the call/args object are created). michael@0: */ michael@0: michael@0: inline bool hasCallObj() const; michael@0: michael@0: bool hasCallObjUnchecked() const { michael@0: return flags_ & HAS_CALL_OBJ; michael@0: } michael@0: michael@0: bool hasArgsObj() const { michael@0: JS_ASSERT(script()->needsArgsObj()); michael@0: return flags_ & HAS_ARGS_OBJ; michael@0: } michael@0: michael@0: void setUseNewType() { michael@0: JS_ASSERT(isConstructing()); michael@0: flags_ |= USE_NEW_TYPE; michael@0: } michael@0: bool useNewType() const { michael@0: JS_ASSERT(isConstructing()); michael@0: return flags_ & USE_NEW_TYPE; michael@0: } michael@0: michael@0: bool isDebuggerFrame() const { michael@0: return !!(flags_ & DEBUGGER); michael@0: } michael@0: michael@0: bool prevUpToDate() const { michael@0: return !!(flags_ & PREV_UP_TO_DATE); michael@0: } michael@0: michael@0: void setPrevUpToDate() { michael@0: flags_ |= PREV_UP_TO_DATE; michael@0: } michael@0: michael@0: bool isYielding() { michael@0: return !!(flags_ & YIELDING); michael@0: } michael@0: michael@0: void setYielding() { michael@0: flags_ |= YIELDING; michael@0: } michael@0: michael@0: void clearYielding() { michael@0: flags_ &= ~YIELDING; michael@0: } michael@0: michael@0: bool isSuspended() const { michael@0: JS_ASSERT(isGeneratorFrame()); michael@0: return flags_ & SUSPENDED; michael@0: } michael@0: michael@0: void setSuspended() { michael@0: JS_ASSERT(isGeneratorFrame()); michael@0: flags_ |= SUSPENDED; michael@0: } michael@0: michael@0: void clearSuspended() { michael@0: JS_ASSERT(isGeneratorFrame()); michael@0: flags_ &= ~SUSPENDED; michael@0: } michael@0: michael@0: public: michael@0: void mark(JSTracer *trc); michael@0: void markValues(JSTracer *trc, unsigned start, unsigned end); michael@0: void markValues(JSTracer *trc, Value *sp, jsbytecode *pc); michael@0: michael@0: // Entered Baseline/Ion from the interpreter. michael@0: bool runningInJit() const { michael@0: return !!(flags_ & RUNNING_IN_JIT); michael@0: } michael@0: void setRunningInJit() { michael@0: flags_ |= RUNNING_IN_JIT; michael@0: } michael@0: void clearRunningInJit() { michael@0: flags_ &= ~RUNNING_IN_JIT; michael@0: } michael@0: }; michael@0: michael@0: static const size_t VALUES_PER_STACK_FRAME = sizeof(InterpreterFrame) / sizeof(Value); michael@0: michael@0: static inline InterpreterFrame::Flags michael@0: ToFrameFlags(InitialFrameFlags initial) michael@0: { michael@0: return InterpreterFrame::Flags(initial); michael@0: } michael@0: michael@0: static inline InitialFrameFlags michael@0: InitialFrameFlagsFromConstructing(bool b) michael@0: { michael@0: return b ? INITIAL_CONSTRUCT : INITIAL_NONE; michael@0: } michael@0: michael@0: static inline bool michael@0: InitialFrameFlagsAreConstructing(InitialFrameFlags initial) michael@0: { michael@0: return !!(initial & INITIAL_CONSTRUCT); michael@0: } michael@0: michael@0: /*****************************************************************************/ michael@0: michael@0: class InterpreterRegs michael@0: { michael@0: public: michael@0: Value *sp; michael@0: jsbytecode *pc; michael@0: private: michael@0: InterpreterFrame *fp_; michael@0: public: michael@0: InterpreterFrame *fp() const { return fp_; } michael@0: michael@0: unsigned stackDepth() const { michael@0: JS_ASSERT(sp >= fp_->base()); michael@0: return sp - fp_->base(); michael@0: } michael@0: michael@0: Value *spForStackDepth(unsigned depth) const { michael@0: JS_ASSERT(fp_->script()->nfixed() + depth <= fp_->script()->nslots()); michael@0: return fp_->base() + depth; michael@0: } michael@0: michael@0: /* For generators. */ michael@0: void rebaseFromTo(const InterpreterRegs &from, InterpreterFrame &to) { michael@0: fp_ = &to; michael@0: sp = to.slots() + (from.sp - from.fp_->slots()); michael@0: pc = from.pc; michael@0: JS_ASSERT(fp_); michael@0: } michael@0: michael@0: void popInlineFrame() { michael@0: pc = fp_->prevpc(); michael@0: sp = fp_->prevsp() - fp_->numActualArgs() - 1; michael@0: fp_ = fp_->prev(); michael@0: JS_ASSERT(fp_); michael@0: } michael@0: void prepareToRun(InterpreterFrame &fp, JSScript *script) { michael@0: pc = script->code(); michael@0: sp = fp.slots() + script->nfixed(); michael@0: fp_ = &fp; michael@0: } michael@0: michael@0: void setToEndOfScript(); michael@0: michael@0: MutableHandleValue stackHandleAt(int i) { michael@0: return MutableHandleValue::fromMarkedLocation(&sp[i]); michael@0: } michael@0: michael@0: HandleValue stackHandleAt(int i) const { michael@0: return HandleValue::fromMarkedLocation(&sp[i]); michael@0: } michael@0: }; michael@0: michael@0: /*****************************************************************************/ michael@0: michael@0: class InterpreterStack michael@0: { michael@0: friend class InterpreterActivation; michael@0: michael@0: static const size_t DEFAULT_CHUNK_SIZE = 4 * 1024; michael@0: LifoAlloc allocator_; michael@0: michael@0: // Number of interpreter frames on the stack, for over-recursion checks. michael@0: static const size_t MAX_FRAMES = 50 * 1000; michael@0: static const size_t MAX_FRAMES_TRUSTED = MAX_FRAMES + 1000; michael@0: size_t frameCount_; michael@0: michael@0: inline uint8_t *allocateFrame(JSContext *cx, size_t size); michael@0: michael@0: inline InterpreterFrame * michael@0: getCallFrame(JSContext *cx, const CallArgs &args, HandleScript script, michael@0: InterpreterFrame::Flags *pflags, Value **pargv); michael@0: michael@0: void releaseFrame(InterpreterFrame *fp) { michael@0: frameCount_--; michael@0: allocator_.release(fp->mark_); michael@0: } michael@0: michael@0: public: michael@0: InterpreterStack() michael@0: : allocator_(DEFAULT_CHUNK_SIZE), michael@0: frameCount_(0) michael@0: { } michael@0: michael@0: ~InterpreterStack() { michael@0: JS_ASSERT(frameCount_ == 0); michael@0: } michael@0: michael@0: // For execution of eval or global code. michael@0: InterpreterFrame *pushExecuteFrame(JSContext *cx, HandleScript script, const Value &thisv, michael@0: HandleObject scopeChain, ExecuteType type, michael@0: AbstractFramePtr evalInFrame); michael@0: michael@0: // Called to invoke a function. michael@0: InterpreterFrame *pushInvokeFrame(JSContext *cx, const CallArgs &args, michael@0: InitialFrameFlags initial); michael@0: michael@0: // The interpreter can push light-weight, "inline" frames without entering a michael@0: // new InterpreterActivation or recursively calling Interpret. michael@0: bool pushInlineFrame(JSContext *cx, InterpreterRegs ®s, const CallArgs &args, michael@0: HandleScript script, InitialFrameFlags initial); michael@0: michael@0: void popInlineFrame(InterpreterRegs ®s); michael@0: michael@0: inline void purge(JSRuntime *rt); michael@0: michael@0: size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { michael@0: return allocator_.sizeOfExcludingThis(mallocSizeOf); michael@0: } michael@0: }; michael@0: michael@0: void MarkInterpreterActivations(JSRuntime *rt, JSTracer *trc); michael@0: michael@0: /*****************************************************************************/ michael@0: michael@0: class InvokeArgs : public JS::CallArgs michael@0: { michael@0: AutoValueVector v_; michael@0: michael@0: public: michael@0: InvokeArgs(JSContext *cx) : v_(cx) {} michael@0: michael@0: bool init(unsigned argc) { michael@0: if (!v_.resize(2 + argc)) michael@0: return false; michael@0: ImplicitCast(*this) = CallArgsFromVp(argc, v_.begin()); michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: template <> michael@0: struct DefaultHasher { michael@0: typedef AbstractFramePtr Lookup; michael@0: michael@0: static js::HashNumber hash(const Lookup &key) { michael@0: return size_t(key.raw()); michael@0: } michael@0: michael@0: static bool match(const AbstractFramePtr &k, const Lookup &l) { michael@0: return k == l; michael@0: } michael@0: }; michael@0: michael@0: /*****************************************************************************/ michael@0: michael@0: class InterpreterActivation; michael@0: class ForkJoinActivation; michael@0: class AsmJSActivation; michael@0: michael@0: namespace jit { michael@0: class JitActivation; michael@0: }; michael@0: michael@0: class Activation michael@0: { michael@0: protected: michael@0: JSContext *cx_; michael@0: JSCompartment *compartment_; michael@0: Activation *prev_; michael@0: michael@0: // Counter incremented by JS_SaveFrameChain on the top-most activation and michael@0: // decremented by JS_RestoreFrameChain. If > 0, ScriptFrameIter should stop michael@0: // iterating when it reaches this activation (if GO_THROUGH_SAVED is not michael@0: // set). michael@0: size_t savedFrameChain_; michael@0: michael@0: // Counter incremented by JS::HideScriptedCaller and decremented by michael@0: // JS::UnhideScriptedCaller. If > 0 for the top activation, michael@0: // DescribeScriptedCaller will return null instead of querying that michael@0: // activation, which should prompt the caller to consult embedding-specific michael@0: // data structures instead. michael@0: size_t hideScriptedCallerCount_; michael@0: michael@0: enum Kind { Interpreter, Jit, ForkJoin, AsmJS }; michael@0: Kind kind_; michael@0: michael@0: inline Activation(JSContext *cx, Kind kind_); michael@0: inline ~Activation(); michael@0: michael@0: public: michael@0: JSContext *cx() const { michael@0: return cx_; michael@0: } michael@0: JSCompartment *compartment() const { michael@0: return compartment_; michael@0: } michael@0: Activation *prev() const { michael@0: return prev_; michael@0: } michael@0: michael@0: bool isInterpreter() const { michael@0: return kind_ == Interpreter; michael@0: } michael@0: bool isJit() const { michael@0: return kind_ == Jit; michael@0: } michael@0: bool isForkJoin() const { michael@0: return kind_ == ForkJoin; michael@0: } michael@0: bool isAsmJS() const { michael@0: return kind_ == AsmJS; michael@0: } michael@0: michael@0: InterpreterActivation *asInterpreter() const { michael@0: JS_ASSERT(isInterpreter()); michael@0: return (InterpreterActivation *)this; michael@0: } michael@0: jit::JitActivation *asJit() const { michael@0: JS_ASSERT(isJit()); michael@0: return (jit::JitActivation *)this; michael@0: } michael@0: ForkJoinActivation *asForkJoin() const { michael@0: JS_ASSERT(isForkJoin()); michael@0: return (ForkJoinActivation *)this; michael@0: } michael@0: AsmJSActivation *asAsmJS() const { michael@0: JS_ASSERT(isAsmJS()); michael@0: return (AsmJSActivation *)this; michael@0: } michael@0: michael@0: void saveFrameChain() { michael@0: savedFrameChain_++; michael@0: } michael@0: void restoreFrameChain() { michael@0: JS_ASSERT(savedFrameChain_ > 0); michael@0: savedFrameChain_--; michael@0: } michael@0: bool hasSavedFrameChain() const { michael@0: return savedFrameChain_ > 0; michael@0: } michael@0: michael@0: void hideScriptedCaller() { michael@0: hideScriptedCallerCount_++; michael@0: } michael@0: void unhideScriptedCaller() { michael@0: JS_ASSERT(hideScriptedCallerCount_ > 0); michael@0: hideScriptedCallerCount_--; michael@0: } michael@0: bool scriptedCallerIsHidden() const { michael@0: return hideScriptedCallerCount_ > 0; michael@0: } michael@0: michael@0: private: michael@0: Activation(const Activation &other) MOZ_DELETE; michael@0: void operator=(const Activation &other) MOZ_DELETE; michael@0: }; michael@0: michael@0: // This variable holds a special opcode value which is greater than all normal michael@0: // opcodes, and is chosen such that the bitwise or of this value with any michael@0: // opcode is this value. michael@0: static const jsbytecode EnableInterruptsPseudoOpcode = -1; michael@0: michael@0: static_assert(EnableInterruptsPseudoOpcode >= JSOP_LIMIT, michael@0: "EnableInterruptsPseudoOpcode must be greater than any opcode"); michael@0: static_assert(EnableInterruptsPseudoOpcode == jsbytecode(-1), michael@0: "EnableInterruptsPseudoOpcode must be the maximum jsbytecode value"); michael@0: michael@0: class InterpreterFrameIterator; michael@0: class RunState; michael@0: michael@0: class InterpreterActivation : public Activation michael@0: { michael@0: friend class js::InterpreterFrameIterator; michael@0: michael@0: RunState &state_; michael@0: InterpreterRegs regs_; michael@0: InterpreterFrame *entryFrame_; michael@0: size_t opMask_; // For debugger interrupts, see js::Interpret. michael@0: michael@0: #ifdef DEBUG michael@0: size_t oldFrameCount_; michael@0: #endif michael@0: michael@0: public: michael@0: inline InterpreterActivation(RunState &state, JSContext *cx, InterpreterFrame *entryFrame); michael@0: inline ~InterpreterActivation(); michael@0: michael@0: inline bool pushInlineFrame(const CallArgs &args, HandleScript script, michael@0: InitialFrameFlags initial); michael@0: inline void popInlineFrame(InterpreterFrame *frame); michael@0: michael@0: InterpreterFrame *current() const { michael@0: return regs_.fp(); michael@0: } michael@0: InterpreterRegs ®s() { michael@0: return regs_; michael@0: } michael@0: InterpreterFrame *entryFrame() const { michael@0: return entryFrame_; michael@0: } michael@0: size_t opMask() const { michael@0: return opMask_; michael@0: } michael@0: michael@0: // If this js::Interpret frame is running |script|, enable interrupts. michael@0: void enableInterruptsIfRunning(JSScript *script) { michael@0: if (regs_.fp()->script() == script) michael@0: enableInterruptsUnconditionally(); michael@0: } michael@0: void enableInterruptsUnconditionally() { michael@0: opMask_ = EnableInterruptsPseudoOpcode; michael@0: } michael@0: void clearInterruptsMask() { michael@0: opMask_ = 0; michael@0: } michael@0: }; michael@0: michael@0: // Iterates over a runtime's activation list. michael@0: class ActivationIterator michael@0: { michael@0: uint8_t *jitTop_; michael@0: michael@0: protected: michael@0: Activation *activation_; michael@0: michael@0: private: michael@0: void settle(); michael@0: michael@0: public: michael@0: explicit ActivationIterator(JSRuntime *rt); michael@0: michael@0: ActivationIterator &operator++(); michael@0: michael@0: Activation *operator->() const { michael@0: return activation_; michael@0: } michael@0: Activation *activation() const { michael@0: return activation_; michael@0: } michael@0: uint8_t *jitTop() const { michael@0: JS_ASSERT(activation_->isJit()); michael@0: return jitTop_; michael@0: } michael@0: bool done() const { michael@0: return activation_ == nullptr; michael@0: } michael@0: }; michael@0: michael@0: namespace jit { michael@0: michael@0: // A JitActivation is used for frames running in Baseline or Ion. michael@0: class JitActivation : public Activation michael@0: { michael@0: uint8_t *prevIonTop_; michael@0: JSContext *prevJitJSContext_; michael@0: bool firstFrameIsConstructing_; michael@0: bool active_; michael@0: michael@0: #ifdef JS_ION michael@0: // Rematerialized Ion frames which has info copied out of snapshots. Maps michael@0: // frame pointers (i.e. ionTop) to a vector of rematerializations of all michael@0: // inline frames associated with that frame. michael@0: // michael@0: // This table is lazily initialized by calling getRematerializedFrame. michael@0: typedef Vector RematerializedFrameVector; michael@0: typedef HashMap RematerializedFrameTable; michael@0: RematerializedFrameTable rematerializedFrames_; michael@0: michael@0: void freeRematerializedFramesInVector(RematerializedFrameVector &frames); michael@0: void clearRematerializedFrames(); michael@0: #endif michael@0: michael@0: #ifdef CHECK_OSIPOINT_REGISTERS michael@0: protected: michael@0: // Used to verify that live registers don't change between a VM call and michael@0: // the OsiPoint that follows it. Protected to silence Clang warning. michael@0: uint32_t checkRegs_; michael@0: RegisterDump regs_; michael@0: #endif michael@0: michael@0: public: michael@0: JitActivation(JSContext *cx, bool firstFrameIsConstructing, bool active = true); michael@0: ~JitActivation(); michael@0: michael@0: bool isActive() const { michael@0: return active_; michael@0: } michael@0: void setActive(JSContext *cx, bool active = true); michael@0: michael@0: uint8_t *prevIonTop() const { michael@0: return prevIonTop_; michael@0: } michael@0: JSCompartment *compartment() const { michael@0: return compartment_; michael@0: } michael@0: bool firstFrameIsConstructing() const { michael@0: return firstFrameIsConstructing_; michael@0: } michael@0: static size_t offsetOfPrevIonTop() { michael@0: return offsetof(JitActivation, prevIonTop_); michael@0: } michael@0: static size_t offsetOfPrevJitJSContext() { michael@0: return offsetof(JitActivation, prevJitJSContext_); michael@0: } michael@0: static size_t offsetOfActiveUint8() { michael@0: JS_ASSERT(sizeof(bool) == 1); michael@0: return offsetof(JitActivation, active_); michael@0: } michael@0: michael@0: #ifdef CHECK_OSIPOINT_REGISTERS michael@0: void setCheckRegs(bool check) { michael@0: checkRegs_ = check; michael@0: } michael@0: static size_t offsetOfCheckRegs() { michael@0: return offsetof(JitActivation, checkRegs_); michael@0: } michael@0: static size_t offsetOfRegs() { michael@0: return offsetof(JitActivation, regs_); michael@0: } michael@0: #endif michael@0: michael@0: #ifdef JS_ION michael@0: // Look up a rematerialized frame keyed by the fp, rematerializing the michael@0: // frame if one doesn't already exist. A frame can only be rematerialized michael@0: // if an IonFrameIterator pointing to the nearest uninlined frame can be michael@0: // provided, as values need to be read out of snapshots. michael@0: // michael@0: // The inlineDepth must be within bounds of the frame pointed to by iter. michael@0: RematerializedFrame *getRematerializedFrame(JSContext *cx, JitFrameIterator &iter, michael@0: size_t inlineDepth = 0); michael@0: michael@0: // Look up a rematerialized frame by the fp. If inlineDepth is out of michael@0: // bounds of what has been rematerialized, nullptr is returned. michael@0: RematerializedFrame *lookupRematerializedFrame(uint8_t *top, size_t inlineDepth = 0); michael@0: michael@0: bool hasRematerializedFrame(uint8_t *top, size_t inlineDepth = 0) { michael@0: return !!lookupRematerializedFrame(top, inlineDepth); michael@0: } michael@0: michael@0: // Remove a previous rematerialization by fp. michael@0: void removeRematerializedFrame(uint8_t *top); michael@0: michael@0: void markRematerializedFrames(JSTracer *trc); michael@0: #endif michael@0: }; michael@0: michael@0: // A filtering of the ActivationIterator to only stop at JitActivations. michael@0: class JitActivationIterator : public ActivationIterator michael@0: { michael@0: void settle() { michael@0: while (!done() && !activation_->isJit()) michael@0: ActivationIterator::operator++(); michael@0: } michael@0: michael@0: public: michael@0: explicit JitActivationIterator(JSRuntime *rt) michael@0: : ActivationIterator(rt) michael@0: { michael@0: settle(); michael@0: } michael@0: michael@0: JitActivationIterator &operator++() { michael@0: ActivationIterator::operator++(); michael@0: settle(); michael@0: return *this; michael@0: } michael@0: michael@0: // Returns the bottom and top addresses of the current JitActivation. michael@0: void jitStackRange(uintptr_t *&min, uintptr_t *&end); michael@0: }; michael@0: michael@0: } // namespace jit michael@0: michael@0: // Iterates over the frames of a single InterpreterActivation. michael@0: class InterpreterFrameIterator michael@0: { michael@0: InterpreterActivation *activation_; michael@0: InterpreterFrame *fp_; michael@0: jsbytecode *pc_; michael@0: Value *sp_; michael@0: michael@0: public: michael@0: explicit InterpreterFrameIterator(InterpreterActivation *activation) michael@0: : activation_(activation), michael@0: fp_(nullptr), michael@0: pc_(nullptr), michael@0: sp_(nullptr) michael@0: { michael@0: if (activation) { michael@0: fp_ = activation->current(); michael@0: pc_ = activation->regs().pc; michael@0: sp_ = activation->regs().sp; michael@0: } michael@0: } michael@0: michael@0: InterpreterFrame *frame() const { michael@0: JS_ASSERT(!done()); michael@0: return fp_; michael@0: } michael@0: jsbytecode *pc() const { michael@0: JS_ASSERT(!done()); michael@0: return pc_; michael@0: } michael@0: Value *sp() const { michael@0: JS_ASSERT(!done()); michael@0: return sp_; michael@0: } michael@0: michael@0: InterpreterFrameIterator &operator++(); michael@0: michael@0: bool done() const { michael@0: return fp_ == nullptr; michael@0: } michael@0: }; michael@0: michael@0: // An AsmJSActivation is part of two activation linked lists: michael@0: // - the normal Activation list used by FrameIter michael@0: // - a list of only AsmJSActivations that is signal-safe since it is accessed michael@0: // from the profiler at arbitrary points michael@0: // michael@0: // An eventual goal is to remove AsmJSActivation and to run asm.js code in a michael@0: // JitActivation interleaved with Ion/Baseline jit code. This would allow michael@0: // efficient calls back and forth but requires that we can walk the stack for michael@0: // all kinds of jit code. michael@0: class AsmJSActivation : public Activation michael@0: { michael@0: AsmJSModule &module_; michael@0: AsmJSActivation *prevAsmJS_; michael@0: void *errorRejoinSP_; michael@0: SPSProfiler *profiler_; michael@0: void *resumePC_; michael@0: uint8_t *exitSP_; michael@0: michael@0: static const intptr_t InterruptedSP = -1; michael@0: michael@0: public: michael@0: AsmJSActivation(JSContext *cx, AsmJSModule &module); michael@0: ~AsmJSActivation(); michael@0: michael@0: JSContext *cx() { return cx_; } michael@0: AsmJSModule &module() const { return module_; } michael@0: AsmJSActivation *prevAsmJS() const { return prevAsmJS_; } michael@0: michael@0: // Read by JIT code: michael@0: static unsigned offsetOfContext() { return offsetof(AsmJSActivation, cx_); } michael@0: static unsigned offsetOfResumePC() { return offsetof(AsmJSActivation, resumePC_); } michael@0: michael@0: // Initialized by JIT code: michael@0: static unsigned offsetOfErrorRejoinSP() { return offsetof(AsmJSActivation, errorRejoinSP_); } michael@0: static unsigned offsetOfExitSP() { return offsetof(AsmJSActivation, exitSP_); } michael@0: michael@0: // Set from SIGSEGV handler: michael@0: void setInterrupted(void *pc) { resumePC_ = pc; exitSP_ = (uint8_t*)InterruptedSP; } michael@0: bool isInterruptedSP() const { return exitSP_ == (uint8_t*)InterruptedSP; } michael@0: michael@0: // Note: exitSP is the sp right before the call instruction. On x86, this michael@0: // means before the return address is pushed on the stack, on ARM, this michael@0: // means after. michael@0: uint8_t *exitSP() const { JS_ASSERT(!isInterruptedSP()); return exitSP_; } michael@0: }; michael@0: michael@0: // A FrameIter walks over the runtime's stack of JS script activations, michael@0: // abstracting over whether the JS scripts were running in the interpreter or michael@0: // different modes of compiled code. michael@0: // michael@0: // FrameIter is parameterized by what it includes in the stack iteration: michael@0: // - The SavedOption controls whether FrameIter stops when it finds an michael@0: // activation that was set aside via JS_SaveFrameChain (and not yet retored michael@0: // by JS_RestoreFrameChain). (Hopefully this will go away.) michael@0: // - The ContextOption determines whether the iteration will view frames from michael@0: // all JSContexts or just the given JSContext. (Hopefully this will go away.) michael@0: // - When provided, the optional JSPrincipal argument will cause FrameIter to michael@0: // only show frames in globals whose JSPrincipals are subsumed (via michael@0: // JSSecurityCallbacks::subsume) by the given JSPrincipal. michael@0: // michael@0: // Additionally, there are derived FrameIter types that automatically skip michael@0: // certain frames: michael@0: // - ScriptFrameIter only shows frames that have an associated JSScript michael@0: // (currently everything other than asm.js stack frames). When !hasScript(), michael@0: // clients must stick to the portion of the michael@0: // interface marked below. michael@0: // - NonBuiltinScriptFrameIter additionally filters out builtin (self-hosted) michael@0: // scripts. michael@0: class FrameIter michael@0: { michael@0: public: michael@0: enum SavedOption { STOP_AT_SAVED, GO_THROUGH_SAVED }; michael@0: enum ContextOption { CURRENT_CONTEXT, ALL_CONTEXTS }; michael@0: enum State { DONE, INTERP, JIT, ASMJS }; michael@0: michael@0: // Unlike ScriptFrameIter itself, ScriptFrameIter::Data can be allocated on michael@0: // the heap, so this structure should not contain any GC things. michael@0: struct Data michael@0: { michael@0: JSContext * cx_; michael@0: SavedOption savedOption_; michael@0: ContextOption contextOption_; michael@0: JSPrincipals * principals_; michael@0: michael@0: State state_; michael@0: michael@0: jsbytecode * pc_; michael@0: michael@0: InterpreterFrameIterator interpFrames_; michael@0: ActivationIterator activations_; michael@0: michael@0: #ifdef JS_ION michael@0: jit::JitFrameIterator jitFrames_; michael@0: unsigned ionInlineFrameNo_; michael@0: AsmJSFrameIterator asmJSFrames_; michael@0: #endif michael@0: michael@0: Data(JSContext *cx, SavedOption savedOption, ContextOption contextOption, michael@0: JSPrincipals *principals); michael@0: Data(const Data &other); michael@0: }; michael@0: michael@0: FrameIter(JSContext *cx, SavedOption = STOP_AT_SAVED); michael@0: FrameIter(JSContext *cx, ContextOption, SavedOption, JSPrincipals* = nullptr); michael@0: FrameIter(const FrameIter &iter); michael@0: FrameIter(const Data &data); michael@0: FrameIter(AbstractFramePtr frame); michael@0: michael@0: bool done() const { return data_.state_ == DONE; } michael@0: michael@0: // ------------------------------------------------------- michael@0: // The following functions can only be called when !done() michael@0: // ------------------------------------------------------- michael@0: michael@0: FrameIter &operator++(); michael@0: michael@0: JSCompartment *compartment() const; michael@0: Activation *activation() const { return data_.activations_.activation(); } michael@0: michael@0: bool isInterp() const { JS_ASSERT(!done()); return data_.state_ == INTERP; } michael@0: bool isJit() const { JS_ASSERT(!done()); return data_.state_ == JIT; } michael@0: bool isAsmJS() const { JS_ASSERT(!done()); return data_.state_ == ASMJS; } michael@0: inline bool isIon() const; michael@0: inline bool isBaseline() const; michael@0: michael@0: bool isFunctionFrame() const; michael@0: bool isGlobalFrame() const; michael@0: bool isEvalFrame() const; michael@0: bool isNonEvalFunctionFrame() const; michael@0: bool isGeneratorFrame() const; michael@0: bool hasArgs() const { return isNonEvalFunctionFrame(); } michael@0: michael@0: /* michael@0: * Get an abstract frame pointer dispatching to either an interpreter, michael@0: * baseline, or rematerialized optimized frame. michael@0: */ michael@0: ScriptSource *scriptSource() const; michael@0: const char *scriptFilename() const; michael@0: unsigned computeLine(uint32_t *column = nullptr) const; michael@0: JSAtom *functionDisplayAtom() const; michael@0: JSPrincipals *originPrincipals() const; michael@0: michael@0: bool hasScript() const { return !isAsmJS(); } michael@0: michael@0: // ----------------------------------------------------------- michael@0: // The following functions can only be called when hasScript() michael@0: // ----------------------------------------------------------- michael@0: michael@0: inline JSScript *script() const; michael@0: michael@0: bool isConstructing() const; michael@0: jsbytecode *pc() const { JS_ASSERT(!done()); return data_.pc_; } michael@0: void updatePcQuadratic(); michael@0: JSFunction *callee() const; michael@0: Value calleev() const; michael@0: unsigned numActualArgs() const; michael@0: unsigned numFormalArgs() const; michael@0: Value unaliasedActual(unsigned i, MaybeCheckAliasing = CHECK_ALIASING) const; michael@0: template inline void unaliasedForEachActual(JSContext *cx, Op op); michael@0: michael@0: JSObject *scopeChain() const; michael@0: CallObject &callObj() const; michael@0: michael@0: bool hasArgsObj() const; michael@0: ArgumentsObject &argsObj() const; michael@0: michael@0: // Ensure that computedThisValue is correct, see ComputeThis. michael@0: bool computeThis(JSContext *cx) const; michael@0: // thisv() may not always be correct, even after computeThis. In case when michael@0: // the frame is an Ion frame, the computed this value cannot be saved to michael@0: // the Ion frame but is instead saved in the RematerializedFrame for use michael@0: // by Debugger. michael@0: // michael@0: // Both methods exist because of speed. thisv() will never rematerialize michael@0: // an Ion frame, whereas computedThisValue() will. michael@0: Value computedThisValue() const; michael@0: Value thisv() const; michael@0: michael@0: Value returnValue() const; michael@0: void setReturnValue(const Value &v); michael@0: michael@0: JSFunction *maybeCallee() const { michael@0: return isFunctionFrame() ? callee() : nullptr; michael@0: } michael@0: michael@0: // These are only valid for the top frame. michael@0: size_t numFrameSlots() const; michael@0: Value frameSlotValue(size_t index) const; michael@0: michael@0: // Ensures that we have rematerialized the top frame and its associated michael@0: // inline frames. Can only be called when isIon(). michael@0: bool ensureHasRematerializedFrame(); michael@0: michael@0: // True when isInterp() or isBaseline(). True when isIon() if it michael@0: // has a rematerialized frame. False otherwise false otherwise. michael@0: bool hasUsableAbstractFramePtr() const; michael@0: michael@0: // ----------------------------------------------------------- michael@0: // The following functions can only be called when isInterp(), michael@0: // isBaseline(), or isIon(). Further, abstractFramePtr() can michael@0: // only be called when hasUsableAbstractFramePtr(). michael@0: // ----------------------------------------------------------- michael@0: michael@0: AbstractFramePtr abstractFramePtr() const; michael@0: AbstractFramePtr copyDataAsAbstractFramePtr() const; michael@0: Data *copyData() const; michael@0: michael@0: // This can only be called when isInterp(): michael@0: inline InterpreterFrame *interpFrame() const; michael@0: michael@0: private: michael@0: Data data_; michael@0: #ifdef JS_ION michael@0: jit::InlineFrameIterator ionInlineFrames_; michael@0: #endif michael@0: michael@0: void popActivation(); michael@0: void popInterpreterFrame(); michael@0: #ifdef JS_ION michael@0: void nextJitFrame(); michael@0: void popJitFrame(); michael@0: void popAsmJSFrame(); michael@0: #endif michael@0: void settleOnActivation(); michael@0: michael@0: friend class ::JSBrokenFrameIterator; michael@0: }; michael@0: michael@0: class ScriptFrameIter : public FrameIter michael@0: { michael@0: void settle() { michael@0: while (!done() && !hasScript()) michael@0: FrameIter::operator++(); michael@0: } michael@0: michael@0: public: michael@0: ScriptFrameIter(JSContext *cx, SavedOption savedOption = STOP_AT_SAVED) michael@0: : FrameIter(cx, savedOption) michael@0: { michael@0: settle(); michael@0: } michael@0: michael@0: ScriptFrameIter(JSContext *cx, michael@0: ContextOption cxOption, michael@0: SavedOption savedOption, michael@0: JSPrincipals *prin = nullptr) michael@0: : FrameIter(cx, cxOption, savedOption, prin) michael@0: { michael@0: settle(); michael@0: } michael@0: michael@0: ScriptFrameIter(const ScriptFrameIter &iter) : FrameIter(iter) { settle(); } michael@0: ScriptFrameIter(const FrameIter::Data &data) : FrameIter(data) { settle(); } michael@0: ScriptFrameIter(AbstractFramePtr frame) : FrameIter(frame) { settle(); } michael@0: michael@0: ScriptFrameIter &operator++() { michael@0: FrameIter::operator++(); michael@0: settle(); michael@0: return *this; michael@0: } michael@0: }; michael@0: michael@0: #ifdef DEBUG michael@0: bool SelfHostedFramesVisible(); michael@0: #else michael@0: static inline bool michael@0: SelfHostedFramesVisible() michael@0: { michael@0: return false; michael@0: } michael@0: #endif michael@0: michael@0: /* A filtering of the FrameIter to only stop at non-self-hosted scripts. */ michael@0: class NonBuiltinFrameIter : public FrameIter michael@0: { michael@0: void settle(); michael@0: michael@0: public: michael@0: NonBuiltinFrameIter(JSContext *cx, michael@0: FrameIter::SavedOption opt = FrameIter::STOP_AT_SAVED) michael@0: : FrameIter(cx, opt) michael@0: { michael@0: settle(); michael@0: } michael@0: michael@0: NonBuiltinFrameIter(JSContext *cx, michael@0: FrameIter::ContextOption contextOption, michael@0: FrameIter::SavedOption savedOption, michael@0: JSPrincipals *principals = nullptr) michael@0: : FrameIter(cx, contextOption, savedOption, principals) michael@0: { michael@0: settle(); michael@0: } michael@0: michael@0: NonBuiltinFrameIter(const FrameIter::Data &data) michael@0: : FrameIter(data) michael@0: {} michael@0: michael@0: NonBuiltinFrameIter &operator++() { michael@0: FrameIter::operator++(); michael@0: settle(); michael@0: return *this; michael@0: } michael@0: }; michael@0: michael@0: /* A filtering of the ScriptFrameIter to only stop at non-self-hosted scripts. */ michael@0: class NonBuiltinScriptFrameIter : public ScriptFrameIter michael@0: { michael@0: void settle(); michael@0: michael@0: public: michael@0: NonBuiltinScriptFrameIter(JSContext *cx, michael@0: ScriptFrameIter::SavedOption opt = ScriptFrameIter::STOP_AT_SAVED) michael@0: : ScriptFrameIter(cx, opt) michael@0: { michael@0: settle(); michael@0: } michael@0: michael@0: NonBuiltinScriptFrameIter(JSContext *cx, michael@0: ScriptFrameIter::ContextOption contextOption, michael@0: ScriptFrameIter::SavedOption savedOption, michael@0: JSPrincipals *principals = nullptr) michael@0: : ScriptFrameIter(cx, contextOption, savedOption, principals) michael@0: { michael@0: settle(); michael@0: } michael@0: michael@0: NonBuiltinScriptFrameIter(const ScriptFrameIter::Data &data) michael@0: : ScriptFrameIter(data) michael@0: {} michael@0: michael@0: NonBuiltinScriptFrameIter &operator++() { michael@0: ScriptFrameIter::operator++(); michael@0: settle(); michael@0: return *this; michael@0: } michael@0: }; michael@0: michael@0: /* michael@0: * Blindly iterate over all frames in the current thread's stack. These frames michael@0: * can be from different contexts and compartments, so beware. michael@0: */ michael@0: class AllFramesIter : public ScriptFrameIter michael@0: { michael@0: public: michael@0: AllFramesIter(JSContext *cx) michael@0: : ScriptFrameIter(cx, ScriptFrameIter::ALL_CONTEXTS, ScriptFrameIter::GO_THROUGH_SAVED) michael@0: {} michael@0: }; michael@0: michael@0: /* Popular inline definitions. */ michael@0: michael@0: inline JSScript * michael@0: FrameIter::script() const michael@0: { michael@0: JS_ASSERT(!done()); michael@0: if (data_.state_ == INTERP) michael@0: return interpFrame()->script(); michael@0: #ifdef JS_ION michael@0: JS_ASSERT(data_.state_ == JIT); michael@0: if (data_.jitFrames_.isIonJS()) michael@0: return ionInlineFrames_.script(); michael@0: return data_.jitFrames_.script(); michael@0: #else michael@0: return nullptr; michael@0: #endif michael@0: } michael@0: michael@0: inline bool michael@0: FrameIter::isIon() const michael@0: { michael@0: #ifdef JS_ION michael@0: return isJit() && data_.jitFrames_.isIonJS(); michael@0: #else michael@0: return false; michael@0: #endif michael@0: } michael@0: michael@0: inline bool michael@0: FrameIter::isBaseline() const michael@0: { michael@0: #ifdef JS_ION michael@0: return isJit() && data_.jitFrames_.isBaselineJS(); michael@0: #else michael@0: return false; michael@0: #endif michael@0: } michael@0: michael@0: inline InterpreterFrame * michael@0: FrameIter::interpFrame() const michael@0: { michael@0: JS_ASSERT(data_.state_ == INTERP); michael@0: return data_.interpFrames_.frame(); michael@0: } michael@0: michael@0: } /* namespace js */ michael@0: #endif /* vm_Stack_h */