| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| * vim: set ts=8 sts=4 et sw=4 tw=99: |
| * This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| #ifndef vm_Stack_h |
| #define vm_Stack_h |
| |
| #include "jsautooplen.h" |
| #include "jsfun.h" |
| #include "jsscript.h" |
| #include "jit/IonFrameIterator.h" |
| |
| struct JSContext; |
| struct JSCompartment; |
| |
| namespace js { |
| |
| class StackFrame; |
| class FrameRegs; |
| |
| class InvokeFrameGuard; |
| class FrameGuard; |
| class ExecuteFrameGuard; |
| class GeneratorFrameGuard; |
| |
| class ScriptFrameIter; |
| class AllFramesIter; |
| |
| class ArgumentsObject; |
| class ScopeObject; |
| class StaticBlockObject; |
| |
| struct ScopeCoordinate; |
| |
| // VM stack layout |
| // |
| // A JSRuntime's stack consists of a linked list of activations. Every activation |
| // contains a number of scripted frames that are either running in the interpreter |
| // (InterpreterActivation) or JIT code (JitActivation). The frames inside a single |
| // activation are contiguous: whenever C++ calls back into JS, a new activation is |
| // pushed. |
| // |
| // Every activation is tied to a single JSContext and JSCompartment. This means we |
| // can reconstruct a given context's stack by skipping activations belonging to other |
| // contexts. This happens whenever an embedding enters the JS engine on cx1 and |
| // then, from a native called by the JS engine, reenters the VM on cx2. |
| |
| // Interpreter frames (StackFrame) |
| // |
| // Each interpreter script activation (global or function code) is given a |
| // fixed-size header (js::StackFrame). The frame contains bookkeeping information |
| // about the activation and links to the previous frame. |
| // |
| // The values after a StackFrame in memory are its locals followed by its |
| // expression stack. StackFrame::argv_ points to the frame's arguments. Missing |
| // formal arguments are padded with |undefined|, so the number of arguments is |
| // always >= the number of formals. |
| // |
| // The top of an activation's current frame's expression stack is pointed to by the |
| // activation's "current regs", which contains the stack pointer 'sp'. In the |
| // interpreter, sp is adjusted as individual values are pushed and popped from |
| // the stack and the FrameRegs struct (pointed to by the InterpreterActivation) |
| // is a local var of js::Interpret. |
| |
| enum MaybeCheckAliasing { CHECK_ALIASING = true, DONT_CHECK_ALIASING = false }; |
| |
| /*****************************************************************************/ |
| |
| #ifdef DEBUG |
| extern void |
| CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script, |
| StaticBlockObject *maybeBlock, unsigned i); |
| #endif |
| |
| namespace jit { |
| class BaselineFrame; |
| } |
| |
| /* Pointer to either a StackFrame or a baseline JIT frame. */ |
| class AbstractFramePtr |
| { |
| uintptr_t ptr_; |
| |
| public: |
| AbstractFramePtr() |
| : ptr_(0) |
| {} |
| |
| AbstractFramePtr(StackFrame *fp) |
| : ptr_(fp ? uintptr_t(fp) | 0x1 : 0) |
| { |
| JS_ASSERT((uintptr_t(fp) & 1) == 0); |
| } |
| |
| AbstractFramePtr(jit::BaselineFrame *fp) |
| : ptr_(uintptr_t(fp)) |
| { |
| JS_ASSERT((uintptr_t(fp) & 1) == 0); |
| } |
| |
| explicit AbstractFramePtr(JSAbstractFramePtr frame) |
| : ptr_(uintptr_t(frame.raw())) |
| { |
| } |
| |
| bool isStackFrame() const { |
| return ptr_ & 0x1; |
| } |
| StackFrame *asStackFrame() const { |
| JS_ASSERT(isStackFrame()); |
| StackFrame *res = (StackFrame *)(ptr_ & ~0x1); |
| JS_ASSERT(res); |
| return res; |
| } |
| bool isBaselineFrame() const { |
| return ptr_ && !isStackFrame(); |
| } |
| jit::BaselineFrame *asBaselineFrame() const { |
| JS_ASSERT(isBaselineFrame()); |
| jit::BaselineFrame *res = (jit::BaselineFrame *)ptr_; |
| JS_ASSERT(res); |
| return res; |
| } |
| |
| void *raw() const { return reinterpret_cast<void *>(ptr_); } |
| |
| bool operator ==(const AbstractFramePtr &other) const { return ptr_ == other.ptr_; } |
| bool operator !=(const AbstractFramePtr &other) const { return ptr_ != other.ptr_; } |
| |
| operator bool() const { return !!ptr_; } |
| |
| inline JSGenerator *maybeSuspendedGenerator(JSRuntime *rt) const; |
| |
| inline JSObject *scopeChain() const; |
| inline CallObject &callObj() const; |
| inline bool initFunctionScopeObjects(JSContext *cx); |
| inline void pushOnScopeChain(ScopeObject &scope); |
| |
| inline JSCompartment *compartment() const; |
| |
| inline StaticBlockObject *maybeBlockChain() const; |
| inline bool hasCallObj() const; |
| inline bool isGeneratorFrame() const; |
| inline bool isYielding() const; |
| inline bool isFunctionFrame() const; |
| inline bool isGlobalFrame() const; |
| inline bool isEvalFrame() const; |
| inline bool isFramePushedByExecute() const; |
| inline bool isDebuggerFrame() const; |
| |
| inline JSScript *script() const; |
| inline JSFunction *fun() const; |
| inline JSFunction *maybeFun() const; |
| inline JSFunction *callee() const; |
| inline Value calleev() const; |
| inline Value &thisValue() const; |
| |
| inline bool isNonEvalFunctionFrame() const; |
| inline bool isNonStrictDirectEvalFrame() const; |
| inline bool isStrictEvalFrame() const; |
| |
| inline unsigned numActualArgs() const; |
| inline unsigned numFormalArgs() const; |
| |
| inline Value *argv() const; |
| |
| inline bool hasArgsObj() const; |
| inline ArgumentsObject &argsObj() const; |
| inline void initArgsObj(ArgumentsObject &argsobj) const; |
| inline bool useNewType() const; |
| |
| inline bool copyRawFrameSlots(AutoValueVector *vec) const; |
| |
| inline Value &unaliasedVar(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING); |
| inline Value &unaliasedLocal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING); |
| inline Value &unaliasedFormal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING); |
| inline Value &unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING); |
| |
| inline bool prevUpToDate() const; |
| inline void setPrevUpToDate() const; |
| |
| JSObject *evalPrevScopeChain(JSContext *cx) const; |
| |
| inline void *maybeHookData() const; |
| inline void setHookData(void *data) const; |
| inline Value returnValue() const; |
| inline void setReturnValue(const Value &rval) const; |
| |
| bool hasPushedSPSFrame() const; |
| |
| inline void popBlock(JSContext *cx) const; |
| inline void popWith(JSContext *cx) const; |
| }; |
| |
| class NullFramePtr : public AbstractFramePtr |
| { |
| public: |
| NullFramePtr() |
| : AbstractFramePtr() |
| { } |
| }; |
| |
| /*****************************************************************************/ |
| |
| /* Flags specified for a frame as it is constructed. */ |
| enum InitialFrameFlags { |
| INITIAL_NONE = 0, |
| INITIAL_CONSTRUCT = 0x20, /* == StackFrame::CONSTRUCTING, asserted below */ |
| }; |
| |
| enum ExecuteType { |
| EXECUTE_GLOBAL = 0x1, /* == StackFrame::GLOBAL */ |
| EXECUTE_DIRECT_EVAL = 0x4, /* == StackFrame::EVAL */ |
| EXECUTE_INDIRECT_EVAL = 0x5, /* == StackFrame::GLOBAL | EVAL */ |
| EXECUTE_DEBUG = 0xc, /* == StackFrame::EVAL | DEBUGGER */ |
| EXECUTE_DEBUG_GLOBAL = 0xd /* == StackFrame::EVAL | DEBUGGER | GLOBAL */ |
| }; |
| |
| /*****************************************************************************/ |
| |
| class StackFrame |
| { |
| public: |
| enum Flags { |
| /* Primary frame type */ |
| GLOBAL = 0x1, /* frame pushed for a global script */ |
| FUNCTION = 0x2, /* frame pushed for a scripted call */ |
| |
| /* Frame subtypes */ |
| EVAL = 0x4, /* frame pushed for eval() or debugger eval */ |
| DEBUGGER = 0x8, /* frame pushed for debugger eval */ |
| GENERATOR = 0x10, /* frame is associated with a generator */ |
| CONSTRUCTING = 0x20, /* frame is for a constructor invocation */ |
| |
| /* |
| * Generator frame state |
| * |
| * YIELDING and SUSPENDED are similar, but there are differences. After |
| * a generator yields, SendToGenerator immediately clears the YIELDING |
| * flag, but the frame will still have the SUSPENDED flag. Also, when the |
| * generator returns but before it's GC'ed, YIELDING is not set but |
| * SUSPENDED is. |
| */ |
| YIELDING = 0x40, /* Interpret dispatched JSOP_YIELD */ |
| SUSPENDED = 0x80, /* Generator is not running. */ |
| |
| /* Function prologue state */ |
| HAS_CALL_OBJ = 0x100, /* CallObject created for heavyweight fun */ |
| HAS_ARGS_OBJ = 0x200, /* ArgumentsObject created for needsArgsObj script */ |
| |
| /* Lazy frame initialization */ |
| HAS_HOOK_DATA = 0x400, /* frame has hookData_ set */ |
| HAS_RVAL = 0x800, /* frame has rval_ set */ |
| HAS_SCOPECHAIN = 0x1000, /* frame has scopeChain_ set */ |
| HAS_BLOCKCHAIN = 0x2000, /* frame has blockChain_ set */ |
| |
| /* Debugger state */ |
| PREV_UP_TO_DATE = 0x4000, /* see DebugScopes::updateLiveScopes */ |
| |
| /* Used in tracking calls and profiling (see vm/SPSProfiler.cpp) */ |
| HAS_PUSHED_SPS_FRAME = 0x8000, /* SPS was notified of enty */ |
| |
| /* |
| * If set, we entered one of the JITs and ScriptFrameIter should skip |
| * this frame. |
| */ |
| RUNNING_IN_JIT = 0x10000, |
| |
| /* Miscellaneous state. */ |
| USE_NEW_TYPE = 0x20000 /* Use new type for constructed |this| object. */ |
| }; |
| |
| private: |
| mutable uint32_t flags_; /* bits described by Flags */ |
| union { /* describes what code is executing in a */ |
| JSScript *script; /* global frame */ |
| JSFunction *fun; /* function frame, pre GetScopeChain */ |
| } exec; |
| union { /* describes the arguments of a function */ |
| unsigned nactual; /* for non-eval frames */ |
| JSScript *evalScript; /* the script of an eval-in-function */ |
| } u; |
| mutable JSObject *scopeChain_; /* if HAS_SCOPECHAIN, current scope chain */ |
| Value rval_; /* if HAS_RVAL, return value of the frame */ |
| StaticBlockObject *blockChain_; /* if HAS_BLOCKCHAIN, innermost let block */ |
| ArgumentsObject *argsObj_; /* if HAS_ARGS_OBJ, the call's arguments object */ |
| |
| /* |
| * Previous frame and its pc and sp. Always NULL for InterpreterActivation's |
| * entry frame, always non-NULL for inline frames. |
| */ |
| StackFrame *prev_; |
| jsbytecode *prevpc_; |
| Value *prevsp_; |
| |
| void *hookData_; /* if HAS_HOOK_DATA, closure returned by call hook */ |
| AbstractFramePtr evalInFramePrev_; /* for an eval/debugger frame, the prev frame */ |
| Value *argv_; /* If hasArgs(), points to frame's arguments. */ |
| LifoAlloc::Mark mark_; /* Used to release memory for this frame. */ |
| |
| static void staticAsserts() { |
| JS_STATIC_ASSERT(offsetof(StackFrame, rval_) % sizeof(Value) == 0); |
| JS_STATIC_ASSERT(sizeof(StackFrame) % sizeof(Value) == 0); |
| } |
| |
| void writeBarrierPost(); |
| |
| /* |
| * These utilities provide raw access to the values associated with a |
| * StackFrame (see "VM stack layout" comment). The utilities are private |
| * since they are not able to assert that only unaliased vars/formals are |
| * accessed. Normal code should prefer the StackFrame::unaliased* members |
| * (or FrameRegs::stackDepth for the usual "depth is at least" assertions). |
| */ |
| public: |
| Value *slots() const { return (Value *)(this + 1); } |
| Value *base() const { return slots() + script()->nfixed; } |
| Value *argv() const { return argv_; } |
| |
| private: |
| friend class FrameRegs; |
| friend class InterpreterStack; |
| friend class ScriptFrameIter; |
| friend class CallObject; |
| friend class ClonedBlockObject; |
| friend class ArgumentsObject; |
| |
| /* |
| * Frame initialization, called by InterpreterStack operations after acquiring |
| * the raw memory for the frame: |
| */ |
| |
| /* Used for Invoke and Interpret. */ |
| void initCallFrame(JSContext *cx, StackFrame *prev, jsbytecode *prevpc, Value *prevsp, JSFunction &callee, |
| JSScript *script, Value *argv, uint32_t nactual, StackFrame::Flags flags); |
| |
| /* Used for global and eval frames. */ |
| void initExecuteFrame(JSContext *cx, JSScript *script, AbstractFramePtr prev, |
| const Value &thisv, JSObject &scopeChain, ExecuteType type); |
| |
| public: |
| /* |
| * Frame prologue/epilogue |
| * |
| * Every stack frame must have 'prologue' called before executing the |
| * first op and 'epilogue' called after executing the last op and before |
| * popping the frame (whether the exit is exceptional or not). |
| * |
| * For inline JS calls/returns, it is easy to call the prologue/epilogue |
| * exactly once. When calling JS from C++, Invoke/Execute push the stack |
| * frame but do *not* call the prologue/epilogue. That means Interpret |
| * must call the prologue/epilogue for the entry frame. This scheme |
| * simplifies jit compilation. |
| * |
| * An important corner case is what happens when an error occurs (OOM, |
| * over-recursed) after pushing the stack frame but before 'prologue' is |
| * called or completes fully. To simplify usage, 'epilogue' does not assume |
| * 'prologue' has completed and handles all the intermediate state details. |
| */ |
| |
| bool prologue(JSContext *cx); |
| void epilogue(JSContext *cx); |
| |
| bool initFunctionScopeObjects(JSContext *cx); |
| |
| /* Initialize local variables of newly-pushed frame. */ |
| void initVarsToUndefined(); |
| |
| /* |
| * Stack frame type |
| * |
| * A stack frame may have one of three types, which determines which |
| * members of the frame may be accessed and other invariants: |
| * |
| * global frame: execution of global code or an eval in global code |
| * function frame: execution of function code or an eval in a function |
| */ |
| |
| bool isFunctionFrame() const { |
| return !!(flags_ & FUNCTION); |
| } |
| |
| bool isGlobalFrame() const { |
| return !!(flags_ & GLOBAL); |
| } |
| |
| /* |
| * Eval frames |
| * |
| * As noted above, global and function frames may optionally be 'eval |
| * frames'. Eval code shares its parent's arguments which means that the |
| * arg-access members of StackFrame may not be used for eval frames. |
| * Search for 'hasArgs' below for more details. |
| * |
| * A further sub-classification of eval frames is whether the frame was |
| * pushed for an ES5 strict-mode eval(). |
| */ |
| |
| bool isEvalFrame() const { |
| return flags_ & EVAL; |
| } |
| |
| bool isEvalInFunction() const { |
| return (flags_ & (EVAL | FUNCTION)) == (EVAL | FUNCTION); |
| } |
| |
| bool isNonEvalFunctionFrame() const { |
| return (flags_ & (FUNCTION | EVAL)) == FUNCTION; |
| } |
| |
| inline bool isStrictEvalFrame() const { |
| return isEvalFrame() && script()->strict; |
| } |
| |
| bool isNonStrictEvalFrame() const { |
| return isEvalFrame() && !script()->strict; |
| } |
| |
| bool isDirectEvalFrame() const { |
| return isEvalFrame() && script()->staticLevel > 0; |
| } |
| |
| bool isNonStrictDirectEvalFrame() const { |
| return isNonStrictEvalFrame() && isDirectEvalFrame(); |
| } |
| |
| /* |
| * Previous frame |
| * |
| * A frame's 'prev' frame is either null or the previous frame pointed to |
| * by cx->regs->fp when this frame was pushed. Often, given two prev-linked |
| * frames, the next-frame is a function or eval that was called by the |
| * prev-frame, but not always: the prev-frame may have called a native that |
| * reentered the VM through JS_CallFunctionValue on the same context |
| * (without calling JS_SaveFrameChain) which pushed the next-frame. Thus, |
| * 'prev' has little semantic meaning and basically just tells the VM what |
| * to set cx->regs->fp to when this frame is popped. |
| */ |
| |
| StackFrame *prev() const { |
| return prev_; |
| } |
| |
| AbstractFramePtr evalInFramePrev() const { |
| JS_ASSERT(isEvalFrame()); |
| return evalInFramePrev_; |
| } |
| |
| /* |
| * (Unaliased) locals and arguments |
| * |
| * Only non-eval function frames have arguments. The arguments pushed by |
| * the caller are the 'actual' arguments. The declared arguments of the |
| * callee are the 'formal' arguments. When the caller passes less actual |
| * arguments, missing formal arguments are padded with |undefined|. |
| * |
| * When a local/formal variable is "aliased" (accessed by nested closures, |
| * dynamic scope operations, or 'arguments), the canonical location for |
| * that value is the slot of an activation object (scope or arguments). |
| * Currently, all variables are given slots in *both* the stack frame and |
| * heap objects, even though, as just described, only one should ever be |
| * accessed. Thus, it is up to the code performing an access to access the |
| * correct value. These functions assert that accesses to stack values are |
| * unaliased. For more about canonical values locations. |
| */ |
| |
| inline Value &unaliasedVar(unsigned i, MaybeCheckAliasing = CHECK_ALIASING); |
| inline Value &unaliasedLocal(unsigned i, MaybeCheckAliasing = CHECK_ALIASING); |
| |
| bool hasArgs() const { return isNonEvalFunctionFrame(); } |
| inline Value &unaliasedFormal(unsigned i, MaybeCheckAliasing = CHECK_ALIASING); |
| inline Value &unaliasedActual(unsigned i, MaybeCheckAliasing = CHECK_ALIASING); |
| template <class Op> inline void forEachUnaliasedActual(Op op); |
| |
| bool copyRawFrameSlots(AutoValueVector *v); |
| |
| unsigned numFormalArgs() const { JS_ASSERT(hasArgs()); return fun()->nargs; } |
| unsigned numActualArgs() const { JS_ASSERT(hasArgs()); return u.nactual; } |
| |
| inline Value &canonicalActualArg(unsigned i) const; |
| template <class Op> |
| inline bool forEachCanonicalActualArg(Op op, unsigned start = 0, unsigned count = unsigned(-1)); |
| template <class Op> inline bool forEachFormalArg(Op op); |
| |
| /* |
| * Arguments object |
| * |
| * If a non-eval function has script->needsArgsObj, an arguments object is |
| * created in the prologue and stored in the local variable for the |
| * 'arguments' binding (script->argumentsLocal). Since this local is |
| * mutable, the arguments object can be overwritten and we can "lose" the |
| * arguments object. Thus, StackFrame keeps an explicit argsObj_ field so |
| * that the original arguments object is always available. |
| */ |
| |
| ArgumentsObject &argsObj() const; |
| void initArgsObj(ArgumentsObject &argsobj); |
| |
| JSObject *createRestParameter(JSContext *cx); |
| |
| /* |
| * Scope chain |
| * |
| * In theory, the scope chain would contain an object for every lexical |
| * scope. However, only objects that are required for dynamic lookup are |
| * actually created. |
| * |
| * Given that a StackFrame corresponds roughly to a ES5 Execution Context |
| * (ES5 10.3), StackFrame::varObj corresponds to the VariableEnvironment |
| * component of a Exection Context. Intuitively, the variables object is |
| * where new bindings (variables and functions) are stored. One might |
| * expect that this is either the Call object or scopeChain.globalObj for |
| * function or global code, respectively, however the JSAPI allows calls of |
| * Execute to specify a variables object on the scope chain other than the |
| * call/global object. This allows embeddings to run multiple scripts under |
| * the same global, each time using a new variables object to collect and |
| * discard the script's global variables. |
| */ |
| |
| inline HandleObject scopeChain() const; |
| |
| inline ScopeObject &aliasedVarScope(ScopeCoordinate sc) const; |
| inline GlobalObject &global() const; |
| inline CallObject &callObj() const; |
| inline JSObject &varObj(); |
| |
| inline void pushOnScopeChain(ScopeObject &scope); |
| inline void popOffScopeChain(); |
| |
| /* |
| * Block chain |
| * |
| * Entering/leaving a let (or exception) block may do 1 or 2 things: First, |
| * a static block object (created at compiled time and stored in the |
| * script) is pushed on StackFrame::blockChain. Second, if the static block |
| * may be cloned to hold the dynamic values if this is needed for dynamic |
| * scope access. A clone is created for a static block iff |
| * StaticBlockObject::needsClone. |
| */ |
| |
| bool hasBlockChain() const { |
| return (flags_ & HAS_BLOCKCHAIN) && blockChain_; |
| } |
| |
| StaticBlockObject *maybeBlockChain() { |
| return (flags_ & HAS_BLOCKCHAIN) ? blockChain_ : NULL; |
| } |
| |
| StaticBlockObject &blockChain() const { |
| JS_ASSERT(hasBlockChain()); |
| return *blockChain_; |
| } |
| |
| bool pushBlock(JSContext *cx, StaticBlockObject &block); |
| void popBlock(JSContext *cx); |
| |
| /* |
| * With |
| * |
| * Entering/leaving a |with| block pushes/pops an object on the scope chain. |
| * Pushing uses pushOnScopeChain, popping should use popWith. |
| */ |
| |
| void popWith(JSContext *cx); |
| |
| /* |
| * Script |
| * |
| * All function and global frames have an associated JSScript which holds |
| * the bytecode being executed for the frame. This script/bytecode does |
| * not reflect any inlining that has been performed by the method JIT. |
| * If other frames were inlined into this one, the script/pc reflect the |
| * point of the outermost call. Inlined frame invariants: |
| * |
| * - Inlined frames have the same scope chain as the outer frame. |
| * - Inlined frames have the same strictness as the outer frame. |
| * - Inlined frames can only make calls to other JIT frames associated with |
| * the same VMFrame. Other calls force expansion of the inlined frames. |
| */ |
| |
| JSScript *script() const { |
| return isFunctionFrame() |
| ? isEvalFrame() |
| ? u.evalScript |
| : fun()->nonLazyScript() |
| : exec.script; |
| } |
| |
| /* Return the previous frame's pc. */ |
| jsbytecode *prevpc() { |
| JS_ASSERT(prev_); |
| return prevpc_; |
| } |
| |
| /* Return the previous frame's sp. */ |
| Value *prevsp() { |
| JS_ASSERT(prev_); |
| return prevsp_; |
| } |
| |
| /* |
| * Function |
| * |
| * All function frames have an associated interpreted JSFunction. The |
| * function returned by fun() and maybeFun() is not necessarily the |
| * original canonical function which the frame's script was compiled |
| * against. |
| */ |
| |
| JSFunction* fun() const { |
| JS_ASSERT(isFunctionFrame()); |
| return exec.fun; |
| } |
| |
| JSFunction* maybeFun() const { |
| return isFunctionFrame() ? fun() : NULL; |
| } |
| |
| /* |
| * This value |
| * |
| * Every frame has a this value although, until 'this' is computed, the |
| * value may not be the semantically-correct 'this' value. |
| * |
| * The 'this' value is stored before the formal arguments for function |
| * frames and directly before the frame for global frames. The *Args |
| * members assert !isEvalFrame(), so we implement specialized inline |
| * methods for accessing 'this'. When the caller has static knowledge that |
| * a frame is a function, 'functionThis' allows more efficient access. |
| */ |
| |
| Value &functionThis() const { |
| JS_ASSERT(isFunctionFrame()); |
| if (isEvalFrame()) |
| return ((Value *)this)[-1]; |
| return argv()[-1]; |
| } |
| |
| JSObject &constructorThis() const { |
| JS_ASSERT(hasArgs()); |
| return argv()[-1].toObject(); |
| } |
| |
| Value &thisValue() const { |
| if (flags_ & (EVAL | GLOBAL)) |
| return ((Value *)this)[-1]; |
| return argv()[-1]; |
| } |
| |
| /* |
| * Callee |
| * |
| * Only function frames have a callee. An eval frame in a function has the |
| * same callee as its containing function frame. maybeCalleev can be used |
| * to return a value that is either the callee object (for function frames) or |
| * null (for global frames). |
| */ |
| |
| JSFunction &callee() const { |
| JS_ASSERT(isFunctionFrame()); |
| return calleev().toObject().as<JSFunction>(); |
| } |
| |
| const Value &calleev() const { |
| JS_ASSERT(isFunctionFrame()); |
| return mutableCalleev(); |
| } |
| |
| const Value &maybeCalleev() const { |
| Value &calleev = flags_ & (EVAL | GLOBAL) |
| ? ((Value *)this)[-2] |
| : argv()[-2]; |
| JS_ASSERT(calleev.isObjectOrNull()); |
| return calleev; |
| } |
| |
| Value &mutableCalleev() const { |
| JS_ASSERT(isFunctionFrame()); |
| if (isEvalFrame()) |
| return ((Value *)this)[-2]; |
| return argv()[-2]; |
| } |
| |
| CallReceiver callReceiver() const { |
| return CallReceiverFromArgv(argv()); |
| } |
| |
| /* |
| * Frame compartment |
| * |
| * A stack frame's compartment is the frame's containing context's |
| * compartment when the frame was pushed. |
| */ |
| |
| inline JSCompartment *compartment() const; |
| |
| /* Debugger hook data */ |
| |
| bool hasHookData() const { |
| return !!(flags_ & HAS_HOOK_DATA); |
| } |
| |
| void* hookData() const { |
| JS_ASSERT(hasHookData()); |
| return hookData_; |
| } |
| |
| void* maybeHookData() const { |
| return hasHookData() ? hookData_ : NULL; |
| } |
| |
| void setHookData(void *v) { |
| hookData_ = v; |
| flags_ |= HAS_HOOK_DATA; |
| } |
| |
| bool hasPushedSPSFrame() { |
| return !!(flags_ & HAS_PUSHED_SPS_FRAME); |
| } |
| |
| void setPushedSPSFrame() { |
| flags_ |= HAS_PUSHED_SPS_FRAME; |
| } |
| |
| void unsetPushedSPSFrame() { |
| flags_ &= ~HAS_PUSHED_SPS_FRAME; |
| } |
| |
| /* Return value */ |
| |
| bool hasReturnValue() const { |
| return !!(flags_ & HAS_RVAL); |
| } |
| |
| Value &returnValue() { |
| if (!(flags_ & HAS_RVAL)) |
| rval_.setUndefined(); |
| return rval_; |
| } |
| |
| void markReturnValue() { |
| flags_ |= HAS_RVAL; |
| } |
| |
| void setReturnValue(const Value &v) { |
| rval_ = v; |
| markReturnValue(); |
| } |
| |
| void clearReturnValue() { |
| rval_.setUndefined(); |
| markReturnValue(); |
| } |
| |
| /* |
| * A "generator" frame is a function frame associated with a generator. |
| * Since generators are not executed LIFO, the VM copies a single abstract |
| * generator frame back and forth between the LIFO VM stack (when the |
| * generator is active) and a snapshot stored in JSGenerator (when the |
| * generator is inactive). A generator frame is comprised of a StackFrame |
| * structure and the values that make up the arguments, locals, and |
| * expression stack. The layout in the JSGenerator snapshot matches the |
| * layout on the stack (see the "VM stack layout" comment above). |
| */ |
| |
| bool isGeneratorFrame() const { |
| bool ret = flags_ & GENERATOR; |
| JS_ASSERT_IF(ret, isNonEvalFunctionFrame()); |
| return ret; |
| } |
| |
| void initGeneratorFrame() const { |
| JS_ASSERT(!isGeneratorFrame()); |
| JS_ASSERT(isNonEvalFunctionFrame()); |
| flags_ |= GENERATOR; |
| } |
| |
| Value *generatorArgsSnapshotBegin() const { |
| JS_ASSERT(isGeneratorFrame()); |
| return argv() - 2; |
| } |
| |
| Value *generatorArgsSnapshotEnd() const { |
| JS_ASSERT(isGeneratorFrame()); |
| return argv() + js::Max(numActualArgs(), numFormalArgs()); |
| } |
| |
| Value *generatorSlotsSnapshotBegin() const { |
| JS_ASSERT(isGeneratorFrame()); |
| return (Value *)(this + 1); |
| } |
| |
| enum TriggerPostBarriers { |
| DoPostBarrier = true, |
| NoPostBarrier = false |
| }; |
| template <TriggerPostBarriers doPostBarrier> |
| void copyFrameAndValues(JSContext *cx, Value *vp, StackFrame *otherfp, |
| const Value *othervp, Value *othersp); |
| |
| JSGenerator *maybeSuspendedGenerator(JSRuntime *rt); |
| |
| /* |
| * js::Execute pushes both global and function frames (since eval() in a |
| * function pushes a frame with isFunctionFrame() && isEvalFrame()). Most |
| * code should not care where a frame was pushed, but if it is necessary to |
| * pick out frames pushed by js::Execute, this is the right query: |
| */ |
| |
| bool isFramePushedByExecute() const { |
| return !!(flags_ & (GLOBAL | EVAL)); |
| } |
| |
| /* |
| * Other flags |
| */ |
| |
| InitialFrameFlags initialFlags() const { |
| JS_STATIC_ASSERT((int)INITIAL_NONE == 0); |
| JS_STATIC_ASSERT((int)INITIAL_CONSTRUCT == (int)CONSTRUCTING); |
| uint32_t mask = CONSTRUCTING; |
| JS_ASSERT((flags_ & mask) != mask); |
| return InitialFrameFlags(flags_ & mask); |
| } |
| |
| void setConstructing() { |
| flags_ |= CONSTRUCTING; |
| } |
| |
| bool isConstructing() const { |
| return !!(flags_ & CONSTRUCTING); |
| } |
| |
| /* |
| * These two queries should not be used in general: the presence/absence of |
| * the call/args object is determined by the static(ish) properties of the |
| * JSFunction/JSScript. These queries should only be performed when probing |
| * a stack frame that may be in the middle of the prologue (during which |
| * time the call/args object are created). |
| */ |
| |
| inline bool hasCallObj() const; |
| |
| bool hasCallObjUnchecked() const { |
| return flags_ & HAS_CALL_OBJ; |
| } |
| |
| bool hasArgsObj() const { |
| JS_ASSERT(script()->needsArgsObj()); |
| return flags_ & HAS_ARGS_OBJ; |
| } |
| |
| void setUseNewType() { |
| JS_ASSERT(isConstructing()); |
| flags_ |= USE_NEW_TYPE; |
| } |
| bool useNewType() const { |
| JS_ASSERT(isConstructing()); |
| return flags_ & USE_NEW_TYPE; |
| } |
| |
| bool isDebuggerFrame() const { |
| return !!(flags_ & DEBUGGER); |
| } |
| |
| bool prevUpToDate() const { |
| return !!(flags_ & PREV_UP_TO_DATE); |
| } |
| |
| void setPrevUpToDate() { |
| flags_ |= PREV_UP_TO_DATE; |
| } |
| |
| bool isYielding() { |
| return !!(flags_ & YIELDING); |
| } |
| |
| void setYielding() { |
| flags_ |= YIELDING; |
| } |
| |
| void clearYielding() { |
| flags_ &= ~YIELDING; |
| } |
| |
| bool isSuspended() const { |
| JS_ASSERT(isGeneratorFrame()); |
| return flags_ & SUSPENDED; |
| } |
| |
| void setSuspended() { |
| JS_ASSERT(isGeneratorFrame()); |
| flags_ |= SUSPENDED; |
| } |
| |
| void clearSuspended() { |
| JS_ASSERT(isGeneratorFrame()); |
| flags_ &= ~SUSPENDED; |
| } |
| |
| public: |
| static size_t offsetOfFlags() { |
| return offsetof(StackFrame, flags_); |
| } |
| |
| static size_t offsetOfExec() { |
| return offsetof(StackFrame, exec); |
| } |
| |
| static size_t offsetOfNumActual() { |
| return offsetof(StackFrame, u.nactual); |
| } |
| |
| static size_t offsetOfScopeChain() { |
| return offsetof(StackFrame, scopeChain_); |
| } |
| |
| static ptrdiff_t offsetOfThis(JSFunction *fun) { |
| return fun == NULL |
| ? -1 * ptrdiff_t(sizeof(Value)) |
| : -(fun->nargs + 1) * ptrdiff_t(sizeof(Value)); |
| } |
| |
| static ptrdiff_t offsetOfFormalArg(JSFunction *fun, unsigned i) { |
| JS_ASSERT(i < fun->nargs); |
| return (-(int)fun->nargs + i) * sizeof(Value); |
| } |
| |
| static size_t offsetOfFixed(unsigned i) { |
| return sizeof(StackFrame) + i * sizeof(Value); |
| } |
| |
| public: |
| void mark(JSTracer *trc); |
| void markValues(JSTracer *trc, Value *sp); |
| |
| // Entered Baseline/Ion from the interpreter. |
| bool runningInJit() const { |
| return !!(flags_ & RUNNING_IN_JIT); |
| } |
| void setRunningInJit() { |
| flags_ |= RUNNING_IN_JIT; |
| } |
| void clearRunningInJit() { |
| flags_ &= ~RUNNING_IN_JIT; |
| } |
| }; |
| |
| static const size_t VALUES_PER_STACK_FRAME = sizeof(StackFrame) / sizeof(Value); |
| |
| static inline StackFrame::Flags |
| ToFrameFlags(InitialFrameFlags initial) |
| { |
| return StackFrame::Flags(initial); |
| } |
| |
| static inline InitialFrameFlags |
| InitialFrameFlagsFromConstructing(bool b) |
| { |
| return b ? INITIAL_CONSTRUCT : INITIAL_NONE; |
| } |
| |
| static inline bool |
| InitialFrameFlagsAreConstructing(InitialFrameFlags initial) |
| { |
| return !!(initial & INITIAL_CONSTRUCT); |
| } |
| |
| inline AbstractFramePtr Valueify(JSAbstractFramePtr frame) { return AbstractFramePtr(frame); } |
| static inline JSAbstractFramePtr Jsvalify(AbstractFramePtr frame) { return JSAbstractFramePtr(frame.raw()); } |
| |
| /*****************************************************************************/ |
| |
| class FrameRegs |
| { |
| public: |
| Value *sp; |
| jsbytecode *pc; |
| private: |
| StackFrame *fp_; |
| public: |
| StackFrame *fp() const { return fp_; } |
| |
| unsigned stackDepth() const { |
| JS_ASSERT(sp >= fp_->base()); |
| return sp - fp_->base(); |
| } |
| |
| Value *spForStackDepth(unsigned depth) const { |
| JS_ASSERT(fp_->script()->nfixed + depth <= fp_->script()->nslots); |
| return fp_->base() + depth; |
| } |
| |
| /* For generators. */ |
| void rebaseFromTo(const FrameRegs &from, StackFrame &to) { |
| fp_ = &to; |
| sp = to.slots() + (from.sp - from.fp_->slots()); |
| pc = from.pc; |
| JS_ASSERT(fp_); |
| } |
| |
| void popInlineFrame() { |
| pc = fp_->prevpc(); |
| sp = fp_->prevsp() - fp_->numActualArgs() - 1; |
| fp_ = fp_->prev(); |
| JS_ASSERT(fp_); |
| } |
| void prepareToRun(StackFrame &fp, JSScript *script) { |
| pc = script->code; |
| sp = fp.slots() + script->nfixed; |
| fp_ = &fp; |
| } |
| |
| void setToEndOfScript() { |
| JSScript *script = fp()->script(); |
| sp = fp()->base(); |
| pc = script->code + script->length - JSOP_STOP_LENGTH; |
| JS_ASSERT(*pc == JSOP_STOP); |
| } |
| }; |
| |
| /*****************************************************************************/ |
| |
| class InterpreterStack |
| { |
| friend class FrameGuard; |
| friend class InterpreterActivation; |
| |
| const static size_t DEFAULT_CHUNK_SIZE = 4 * 1024; |
| LifoAlloc allocator_; |
| |
| // Number of interpreter frames on the stack, for over-recursion checks. |
| static const size_t MAX_FRAMES = 50 * 1000; |
| static const size_t MAX_FRAMES_TRUSTED = MAX_FRAMES + 1000; |
| size_t frameCount_; |
| |
| inline uint8_t *allocateFrame(JSContext *cx, size_t size); |
| |
| inline StackFrame * |
| getCallFrame(JSContext *cx, const CallArgs &args, HandleScript script, |
| StackFrame::Flags *pflags, Value **pargv); |
| |
| void releaseFrame(StackFrame *fp) { |
| frameCount_--; |
| allocator_.release(fp->mark_); |
| } |
| |
| public: |
| InterpreterStack() |
| : allocator_(DEFAULT_CHUNK_SIZE), |
| frameCount_(0) |
| { } |
| |
| ~InterpreterStack() { |
| JS_ASSERT(frameCount_ == 0); |
| } |
| |
| // For execution of eval or global code. |
| StackFrame *pushExecuteFrame(JSContext *cx, HandleScript script, const Value &thisv, |
| HandleObject scopeChain, ExecuteType type, |
| AbstractFramePtr evalInFrame, FrameGuard *fg); |
| |
| // Called to invoke a function. |
| StackFrame *pushInvokeFrame(JSContext *cx, const CallArgs &args, InitialFrameFlags initial, |
| FrameGuard *fg); |
| |
| // The interpreter can push light-weight, "inline" frames without entering a |
| // new InterpreterActivation or recursively calling Interpret. |
| bool pushInlineFrame(JSContext *cx, FrameRegs ®s, const CallArgs &args, |
| HandleScript script, InitialFrameFlags initial); |
| |
| void popInlineFrame(FrameRegs ®s); |
| |
| inline void purge(JSRuntime *rt); |
| |
| size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const { |
| return allocator_.sizeOfExcludingThis(mallocSizeOf); |
| } |
| }; |
| |
| void MarkInterpreterActivations(JSRuntime *rt, JSTracer *trc); |
| |
| /*****************************************************************************/ |
| |
| class InvokeArgs : public JS::CallArgs |
| { |
| AutoValueVector v_; |
| |
| public: |
| InvokeArgs(JSContext *cx) : v_(cx) {} |
| |
| bool init(unsigned argc) { |
| if (!v_.resize(2 + argc)) |
| return false; |
| ImplicitCast<CallArgs>(*this) = CallArgsFromVp(argc, v_.begin()); |
| return true; |
| } |
| }; |
| |
| class RunState; |
| |
| class FrameGuard |
| { |
| friend class InterpreterStack; |
| RunState &state_; |
| FrameRegs ®s_; |
| InterpreterStack *stack_; |
| StackFrame *fp_; |
| |
| void setPushed(InterpreterStack &stack, StackFrame *fp) { |
| stack_ = &stack; |
| fp_ = fp; |
| } |
| |
| public: |
| FrameGuard(RunState &state, FrameRegs ®s); |
| ~FrameGuard(); |
| |
| StackFrame *fp() const { |
| JS_ASSERT(fp_); |
| return fp_; |
| } |
| }; |
| |
| template <> |
| struct DefaultHasher<AbstractFramePtr> { |
| typedef AbstractFramePtr Lookup; |
| |
| static js::HashNumber hash(const Lookup &key) { |
| return size_t(key.raw()); |
| } |
| |
| static bool match(const AbstractFramePtr &k, const Lookup &l) { |
| return k == l; |
| } |
| }; |
| |
| /*****************************************************************************/ |
| |
| class InterpreterActivation; |
| |
| namespace jit { |
| class JitActivation; |
| }; |
| |
| class Activation |
| { |
| protected: |
| JSContext *cx_; |
| JSCompartment *compartment_; |
| Activation *prev_; |
| |
| // Counter incremented by JS_SaveFrameChain on the top-most activation and |
| // decremented by JS_RestoreFrameChain. If > 0, ScriptFrameIter should stop |
| // iterating when it reaches this activation (if GO_THROUGH_SAVED is not |
| // set). |
| size_t savedFrameChain_; |
| |
| enum Kind { Interpreter, Jit }; |
| Kind kind_; |
| |
| inline Activation(JSContext *cx, Kind kind_); |
| inline ~Activation(); |
| |
| public: |
| JSContext *cx() const { |
| return cx_; |
| } |
| JSCompartment *compartment() const { |
| return compartment_; |
| } |
| Activation *prev() const { |
| return prev_; |
| } |
| |
| bool isInterpreter() const { |
| return kind_ == Interpreter; |
| } |
| bool isJit() const { |
| return kind_ == Jit; |
| } |
| |
| InterpreterActivation *asInterpreter() const { |
| JS_ASSERT(isInterpreter()); |
| return (InterpreterActivation *)this; |
| } |
| jit::JitActivation *asJit() const { |
| JS_ASSERT(isJit()); |
| return (jit::JitActivation *)this; |
| } |
| |
| void saveFrameChain() { |
| savedFrameChain_++; |
| } |
| void restoreFrameChain() { |
| JS_ASSERT(savedFrameChain_ > 0); |
| savedFrameChain_--; |
| } |
| bool hasSavedFrameChain() const { |
| return savedFrameChain_ > 0; |
| } |
| |
| private: |
| Activation(const Activation &other) MOZ_DELETE; |
| void operator=(const Activation &other) MOZ_DELETE; |
| }; |
| |
| class InterpreterFrameIterator; |
| |
| class InterpreterActivation : public Activation |
| { |
| friend class js::InterpreterFrameIterator; |
| |
| StackFrame *const entry_; // Entry frame for this activation. |
| StackFrame *current_; // The most recent frame. |
| FrameRegs ®s_; |
| |
| #ifdef DEBUG |
| size_t oldFrameCount_; |
| #endif |
| |
| public: |
| inline InterpreterActivation(JSContext *cx, StackFrame *entry, FrameRegs ®s); |
| inline ~InterpreterActivation(); |
| |
| inline bool pushInlineFrame(const CallArgs &args, HandleScript script, |
| InitialFrameFlags initial); |
| inline void popInlineFrame(StackFrame *frame); |
| |
| StackFrame *current() const { |
| JS_ASSERT(current_); |
| return current_; |
| } |
| FrameRegs ®s() const { |
| return regs_; |
| } |
| }; |
| |
| // Iterates over a runtime's activation list. |
| class ActivationIterator |
| { |
| uint8_t *jitTop_; |
| |
| protected: |
| Activation *activation_; |
| |
| private: |
| void settle(); |
| |
| public: |
| explicit ActivationIterator(JSRuntime *rt); |
| |
| ActivationIterator &operator++(); |
| |
| Activation *activation() const { |
| return activation_; |
| } |
| uint8_t *jitTop() const { |
| JS_ASSERT(activation_->isJit()); |
| return jitTop_; |
| } |
| bool done() const { |
| return activation_ == NULL; |
| } |
| }; |
| |
| namespace jit { |
| |
| // A JitActivation is used for frames running in Baseline or Ion. |
| class JitActivation : public Activation |
| { |
| uint8_t *prevIonTop_; |
| JSContext *prevIonJSContext_; |
| bool firstFrameIsConstructing_; |
| bool active_; |
| |
| public: |
| JitActivation(JSContext *cx, bool firstFrameIsConstructing, bool active = true); |
| ~JitActivation(); |
| |
| bool isActive() const { |
| return active_; |
| } |
| void setActive(JSContext *cx, bool active = true); |
| |
| uint8_t *prevIonTop() const { |
| return prevIonTop_; |
| } |
| JSCompartment *compartment() const { |
| return compartment_; |
| } |
| bool firstFrameIsConstructing() const { |
| return firstFrameIsConstructing_; |
| } |
| }; |
| |
| // A filtering of the ActivationIterator to only stop at JitActivations. |
| class JitActivationIterator : public ActivationIterator |
| { |
| void settle() { |
| while (!done() && !activation_->isJit()) |
| ActivationIterator::operator++(); |
| } |
| |
| public: |
| explicit JitActivationIterator(JSRuntime *rt) |
| : ActivationIterator(rt) |
| { |
| settle(); |
| } |
| |
| JitActivationIterator &operator++() { |
| ActivationIterator::operator++(); |
| settle(); |
| return *this; |
| } |
| |
| // Returns the bottom and top addresses of the current JitActivation. |
| void jitStackRange(uintptr_t *&min, uintptr_t *&end); |
| }; |
| |
| } // namespace jit |
| |
| // Iterates over the frames of a single InterpreterActivation. |
| class InterpreterFrameIterator |
| { |
| InterpreterActivation *activation_; |
| StackFrame *fp_; |
| jsbytecode *pc_; |
| Value *sp_; |
| |
| public: |
| explicit InterpreterFrameIterator(InterpreterActivation *activation) |
| : activation_(activation), |
| fp_(NULL), |
| pc_(NULL), |
| sp_(NULL) |
| { |
| if (activation) { |
| fp_ = activation->current(); |
| pc_ = activation->regs_.pc; |
| sp_ = activation->regs_.sp; |
| } |
| } |
| |
| StackFrame *frame() const { |
| JS_ASSERT(!done()); |
| return fp_; |
| } |
| jsbytecode *pc() const { |
| JS_ASSERT(!done()); |
| return pc_; |
| } |
| Value *sp() const { |
| JS_ASSERT(!done()); |
| return sp_; |
| } |
| |
| InterpreterFrameIterator &operator++(); |
| |
| bool done() const { |
| return fp_ == NULL; |
| } |
| }; |
| |
| /* |
| * Iterate through the callstack (following fp->prev) of the given context. |
| * Each element of said callstack can either be the execution of a script |
| * (scripted function call, global code, eval code, debugger code) or the |
| * invocation of a (C++) native. Example usage: |
| * |
| * for (Stackiter i(cx); !i.done(); ++i) { |
| * if (i.isScript()) { |
| * ... i.fp() ... i.sp() ... i.pc() |
| * } else { |
| * JS_ASSERT(i.isNativeCall()); |
| * ... i.args(); |
| * } |
| * } |
| * |
| * The SavedOption parameter additionally lets the iterator continue through |
| * breaks in the callstack (from JS_SaveFrameChain). The default is to stop. |
| */ |
| class ScriptFrameIter |
| { |
| public: |
| enum SavedOption { STOP_AT_SAVED, GO_THROUGH_SAVED }; |
| enum ContextOption { CURRENT_CONTEXT, ALL_CONTEXTS }; |
| enum State { DONE, SCRIPTED, JIT }; |
| |
| /* |
| * Unlike ScriptFrameIter itself, ScriptFrameIter::Data can be allocated on |
| * the heap, so this structure should not contain any GC things. |
| */ |
| struct Data |
| { |
| PerThreadData *perThread_; |
| JSContext *cx_; |
| SavedOption savedOption_; |
| ContextOption contextOption_; |
| |
| State state_; |
| |
| jsbytecode *pc_; |
| |
| InterpreterFrameIterator interpFrames_; |
| ActivationIterator activations_; |
| |
| #ifdef JS_ION |
| jit::IonFrameIterator ionFrames_; |
| #endif |
| |
| Data(JSContext *cx, PerThreadData *perThread, SavedOption savedOption, |
| ContextOption contextOption); |
| Data(const Data &other); |
| }; |
| |
| friend class ::JSBrokenFrameIterator; |
| private: |
| Data data_; |
| #ifdef JS_ION |
| jit::InlineFrameIterator ionInlineFrames_; |
| #endif |
| |
| void popActivation(); |
| void popInterpreterFrame(); |
| #ifdef JS_ION |
| void nextJitFrame(); |
| void popJitFrame(); |
| #endif |
| void settleOnActivation(); |
| |
| public: |
| ScriptFrameIter(JSContext *cx, SavedOption = STOP_AT_SAVED); |
| ScriptFrameIter(JSContext *cx, ContextOption, SavedOption); |
| ScriptFrameIter(const ScriptFrameIter &iter); |
| ScriptFrameIter(const Data &data); |
| |
| bool done() const { return data_.state_ == DONE; } |
| ScriptFrameIter &operator++(); |
| |
| Data *copyData() const; |
| |
| JSCompartment *compartment() const; |
| |
| JSScript *script() const { |
| JS_ASSERT(!done()); |
| if (data_.state_ == SCRIPTED) |
| return interpFrame()->script(); |
| #ifdef JS_ION |
| JS_ASSERT(data_.state_ == JIT); |
| if (data_.ionFrames_.isOptimizedJS()) |
| return ionInlineFrames_.script(); |
| return data_.ionFrames_.script(); |
| #else |
| return NULL; |
| #endif |
| } |
| bool isJit() const { |
| JS_ASSERT(!done()); |
| return data_.state_ == JIT; |
| } |
| |
| bool isIon() const { |
| #ifdef JS_ION |
| return isJit() && data_.ionFrames_.isOptimizedJS(); |
| #else |
| return false; |
| #endif |
| } |
| |
| bool isBaseline() const { |
| #ifdef JS_ION |
| return isJit() && data_.ionFrames_.isBaselineJS(); |
| #else |
| return false; |
| #endif |
| } |
| |
| bool isFunctionFrame() const; |
| bool isGlobalFrame() const; |
| bool isEvalFrame() const; |
| bool isNonEvalFunctionFrame() const; |
| bool isGeneratorFrame() const; |
| bool isConstructing() const; |
| |
| bool hasArgs() const { return isNonEvalFunctionFrame(); } |
| |
| AbstractFramePtr abstractFramePtr() const; |
| |
| /* |
| * When entering IonMonkey, the top interpreter frame (pushed by the caller) |
| * is kept on the stack as bookkeeping (with runningInIon() set). The |
| * contents of the frame are ignored by Ion code (and GC) and thus |
| * immediately become garbage and must not be touched directly. |
| */ |
| StackFrame *interpFrame() const { |
| JS_ASSERT(data_.state_ == SCRIPTED); |
| return data_.interpFrames_.frame(); |
| } |
| |
| Activation *activation() const { return data_.activations_.activation(); } |
| |
| jsbytecode *pc() const { JS_ASSERT(!done()); return data_.pc_; } |
| void updatePcQuadratic(); |
| JSFunction *callee() const; |
| Value calleev() const; |
| unsigned numActualArgs() const; |
| unsigned numFormalArgs() const { return script()->function()->nargs; } |
| Value unaliasedActual(unsigned i, MaybeCheckAliasing = CHECK_ALIASING) const; |
| |
| JSObject *scopeChain() const; |
| CallObject &callObj() const; |
| |
| bool hasArgsObj() const; |
| ArgumentsObject &argsObj() const; |
| |
| // Ensure that thisv is correct, see ComputeThis. |
| bool computeThis(JSContext *cx) const; |
| Value thisv() const; |
| |
| Value returnValue() const; |
| void setReturnValue(const Value &v); |
| |
| JSFunction *maybeCallee() const { |
| return isFunctionFrame() ? callee() : NULL; |
| } |
| |
| // These are only valid for the top frame. |
| size_t numFrameSlots() const; |
| Value frameSlotValue(size_t index) const; |
| |
| template <class Op> |
| inline void ionForEachCanonicalActualArg(JSContext *cx, Op op); |
| }; |
| |
| /* A filtering of the ScriptFrameIter to only stop at non-self-hosted scripts. */ |
| class NonBuiltinScriptFrameIter : public ScriptFrameIter |
| { |
| void settle() { |
| while (!done() && script()->selfHosted) |
| ScriptFrameIter::operator++(); |
| } |
| |
| public: |
| NonBuiltinScriptFrameIter(JSContext *cx, ScriptFrameIter::SavedOption opt = ScriptFrameIter::STOP_AT_SAVED) |
| : ScriptFrameIter(cx, opt) { settle(); } |
| |
| NonBuiltinScriptFrameIter(const ScriptFrameIter::Data &data) |
| : ScriptFrameIter(data) |
| {} |
| |
| NonBuiltinScriptFrameIter &operator++() { ScriptFrameIter::operator++(); settle(); return *this; } |
| }; |
| |
| /* |
| * Blindly iterate over all frames in the current thread's stack. These frames |
| * can be from different contexts and compartments, so beware. |
| */ |
| class AllFramesIter : public ScriptFrameIter |
| { |
| public: |
| AllFramesIter(JSContext *cx) |
| : ScriptFrameIter(cx, ScriptFrameIter::ALL_CONTEXTS, ScriptFrameIter::GO_THROUGH_SAVED) |
| {} |
| }; |
| |
| } /* namespace js */ |
| #endif /* vm_Stack_h */ |