https://github.com/mozilla/gecko-dev
Raw File
Tip revision: 8a844bfe26bfbc5c32e67ebb220ee0b731b1556f authored by ffxbld on 13 June 2011, 23:15:44 UTC
Added tag FIREFOX_5_0b6_BUILD1 for changeset 937762949167. CLOSED TREE a=release
Tip revision: 8a844bf
jsinterp.h
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=4 sw=4 et tw=78:
 *
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Mozilla Communicator client code, released
 * March 31, 1998.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1998
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#ifndef jsinterp_h___
#define jsinterp_h___
/*
 * JS interpreter interface.
 */
#include "jsprvtd.h"
#include "jspubtd.h"
#include "jsfun.h"
#include "jsopcode.h"
#include "jsscript.h"
#include "jsvalue.h"

struct JSFrameRegs
{
    STATIC_SKIP_INFERENCE
    js::Value       *sp;                  /* stack pointer */
    jsbytecode      *pc;                  /* program counter */
    JSStackFrame    *fp;                  /* active frame */
};

/* Flags to toggle js::Interpret() execution. */
enum JSInterpMode
{
    JSINTERP_NORMAL            =     0, /* interpreter is running normally */
    JSINTERP_RECORD            =     1, /* interpreter has been started to record/run traces */
    JSINTERP_SAFEPOINT         =     2, /* interpreter should leave on a method JIT safe point */
    JSINTERP_PROFILE           =     3  /* interpreter should profile a loop */
};

/* Flags used in JSStackFrame::flags_ */
enum JSFrameFlags
{
    /* Primary frame type */
    JSFRAME_GLOBAL             =     0x1, /* frame pushed for a global script */
    JSFRAME_FUNCTION           =     0x2, /* frame pushed for a scripted call */
    JSFRAME_DUMMY              =     0x4, /* frame pushed for bookkeeping */

    /* Frame subtypes */
    JSFRAME_EVAL               =     0x8, /* frame pushed for eval() or debugger eval */
    JSFRAME_DEBUGGER           =    0x10, /* frame pushed for debugger eval */
    JSFRAME_GENERATOR          =    0x20, /* frame is associated with a generator */
    JSFRAME_FLOATING_GENERATOR =    0x40, /* frame is is in generator obj, not on stack */
    JSFRAME_CONSTRUCTING       =    0x80, /* frame is for a constructor invocation */

    /* Temporary frame states */
    JSFRAME_ASSIGNING          =   0x100, /* not-JOF_ASSIGNING op is assigning */
    JSFRAME_YIELDING           =   0x200, /* js::Interpret dispatched JSOP_YIELD */
    JSFRAME_FINISHED_IN_INTERPRETER = 0x400, /* set if frame finished in Interpret() */

    /* Concerning function arguments */
    JSFRAME_OVERRIDE_ARGS      =  0x1000, /* overridden arguments local variable */
    JSFRAME_OVERFLOW_ARGS      =  0x2000, /* numActualArgs > numFormalArgs */
    JSFRAME_UNDERFLOW_ARGS     =  0x4000, /* numActualArgs < numFormalArgs */

    /* Lazy frame initialization */
    JSFRAME_HAS_IMACRO_PC      =   0x8000, /* frame has imacpc value available */
    JSFRAME_HAS_CALL_OBJ       =  0x10000, /* frame has a callobj reachable from scopeChain_ */
    JSFRAME_HAS_ARGS_OBJ       =  0x20000, /* frame has an argsobj in JSStackFrame::args */
    JSFRAME_HAS_HOOK_DATA      =  0x40000, /* frame has hookData_ set */
    JSFRAME_HAS_ANNOTATION     =  0x80000, /* frame has annotation_ set */
    JSFRAME_HAS_RVAL           = 0x100000, /* frame has rval_ set */
    JSFRAME_HAS_SCOPECHAIN     = 0x200000, /* frame has scopeChain_ set */
    JSFRAME_HAS_PREVPC         = 0x400000  /* frame has prevpc_ set */
};

namespace js { namespace mjit { struct JITScript; } }

/*
 * A stack frame is a part of a stack segment (see js::StackSegment) which is
 * on the per-thread VM stack (see js::StackSpace).
 */
struct JSStackFrame
{
  private:
    mutable uint32      flags_;         /* bits described by JSFrameFlags */
    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 */
        uintN           nactual;        /*   pre GetArgumentsObject */
        JSObject        *obj;           /*   post GetArgumentsObject */
        JSScript        *script;        /* eval has no args, but needs a script */
    } args;
    mutable JSObject    *scopeChain_;   /* current scope chain */
    JSStackFrame        *prev_;         /* previous cx->regs->fp */
    void                *ncode_;        /* return address for method JIT */

    /* Lazily initialized */
    js::Value           rval_;          /* return value of the frame */
    jsbytecode          *prevpc_;       /* pc of previous frame*/
    jsbytecode          *imacropc_;     /* pc of macro caller */
    void                *hookData_;     /* closure returned by call hook */
    void                *annotation_;   /* perhaps remove with bug 546848 */

    friend class js::StackSpace;
    friend class js::FrameRegsIter;
    friend struct JSContext;

    inline void initPrev(JSContext *cx);

  public:
    /*
     * Stack frame sort (see JSStackFrame comment above)
     *
     * 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
     *  dummy frame:    bookkeeping frame (read: hack)
     *
     */

    bool isFunctionFrame() const {
        return !!(flags_ & JSFRAME_FUNCTION);
    }

    bool isGlobalFrame() const {
        return !!(flags_ & JSFRAME_GLOBAL);
    }

    bool isDummyFrame() const {
        return !!(flags_ & JSFRAME_DUMMY);
    }

    bool isScriptFrame() const {
        bool retval = !!(flags_ & (JSFRAME_FUNCTION | JSFRAME_GLOBAL));
        JS_ASSERT(retval == !isDummyFrame());
        return retval;
    }

    /*
     * 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 JSStackFrame 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 {
        JS_ASSERT_IF(flags_ & JSFRAME_EVAL, isScriptFrame());
        return flags_ & JSFRAME_EVAL;
    }

    bool isNonEvalFunctionFrame() const {
        return (flags_ & (JSFRAME_FUNCTION | JSFRAME_EVAL)) == JSFRAME_FUNCTION;
    }

    bool isStrictEvalFrame() const {
        return isEvalFrame() && script()->strictModeCode;
    }

    bool isNonStrictEvalFrame() const {
        return isEvalFrame() && !script()->strictModeCode;
    }

    /*
     * Frame initialization
     *
     * After acquiring a pointer to an uninitialized stack frame on the VM
     * stack from js::StackSpace, these members are used to initialize the
     * stack frame before officially pushing the frame into the context.
     * Collecting frame initialization into a set of inline helpers allows
     * simpler reasoning and makes call-optimization easier.
     */

    /* Used for Invoke, Interpret, trace-jit LeaveTree, and method-jit stubs. */
    inline void initCallFrame(JSContext *cx, JSObject &callee, JSFunction *fun,
                              uint32 nactual, uint32 flags);

    /* Used for SessionInvoke. */
    inline void resetInvokeCallFrame();

    /* Called by method-jit stubs and serve as a specification for jit-code. */
    inline void initCallFrameCallerHalf(JSContext *cx, uint32 flags, void *ncode);
    inline void initCallFrameEarlyPrologue(JSFunction *fun, uint32 nactual);
    inline void initCallFrameLatePrologue();

    /* Used for eval. */
    inline void initEvalFrame(JSContext *cx, JSScript *script, JSStackFrame *prev,
                              uint32 flags);
    inline void initGlobalFrame(JSScript *script, JSObject &chain, JSStackFrame *prev,
                                uint32 flags);

    /* Used when activating generators. */
    inline void stealFrameAndSlots(js::Value *vp, JSStackFrame *otherfp,
                                   js::Value *othervp, js::Value *othersp);

    /* Perhaps one fine day we will remove dummy frames. */
    inline void initDummyFrame(JSContext *cx, JSObject &chain);

    /*
     * 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.
     */

    JSStackFrame *prev() const {
        return prev_;
    }

    inline void resetGeneratorPrev(JSContext *cx);

    /*
     * Frame slots
     *
     * A frame's 'slots' are the fixed slots associated with the frame (like
     * local variables) followed by an expression stack holding temporary
     * values. A frame's 'base' is the base of the expression stack.
     */

    js::Value *slots() const {
        return (js::Value *)(this + 1);
    }

    js::Value *base() const {
        return slots() + script()->nfixed;
    }

    js::Value &varSlot(uintN i) {
        JS_ASSERT(i < script()->nfixed);
        JS_ASSERT_IF(maybeFun(), i < script()->bindings.countVars());
        return slots()[i];
    }

    /*
     * Script
     *
     * All function and global frames have an associated JSScript which holds
     * the bytecode being executed for the frame.
     */

    /*
     * Get the frame's current bytecode, assuming |this| is in |cx|.
     * next is frame whose prev == this, NULL if not known or if this == cx->fp().
     */
    jsbytecode *pc(JSContext *cx, JSStackFrame *next = NULL);

    jsbytecode *prevpc() {
        JS_ASSERT((prev_ != NULL) && (flags_ & JSFRAME_HAS_PREVPC));
        return prevpc_;
    }

    JSScript *script() const {
        JS_ASSERT(isScriptFrame());
        return isFunctionFrame()
               ? isEvalFrame() ? args.script : fun()->script()
               : exec.script;
    }

    JSScript *functionScript() const {
        JS_ASSERT(isFunctionFrame());
        return isEvalFrame() ? args.script : fun()->script();
    }

    JSScript *globalScript() const {
        JS_ASSERT(isGlobalFrame());
        return exec.script;
    }

    JSScript *maybeScript() const {
        return isScriptFrame() ? script() : NULL;
    }

    size_t numFixed() const {
        return script()->nfixed;
    }

    size_t numSlots() const {
        return script()->nslots;
    }

    size_t numGlobalVars() const {
        JS_ASSERT(isGlobalFrame());
        return exec.script->nfixed;
    }

    /*
     * Function
     *
     * All function frames have an associated interpreted JSFunction.
     */

    JSFunction* fun() const {
        JS_ASSERT(isFunctionFrame());
        return exec.fun;
    }

    JSFunction* maybeFun() const {
        return isFunctionFrame() ? fun() : NULL;
    }

    /*
     * Arguments
     *
     * Only non-eval function frames have arguments. A frame follows its
     * arguments contiguously in memory. 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 or equal actual
     * arguments, the actual and formal arguments are the same array (but with
     * different extents). When the caller passes too many arguments, the
     * formal subset of the actual arguments is copied onto the top of the
     * stack. This allows the engine to maintain a jit-time constant offset of
     * arguments from the frame pointer. Since the formal subset of the actual
     * arguments is potentially on the stack twice, it is important for all
     * reads/writes to refer to the same canonical memory location.
     *
     * An arguments object (the object returned by the 'arguments' keyword) is
     * lazily created, so a given function frame may or may not have one.
     */

    /* True if this frame has arguments. Contrast with hasArgsObj. */
    bool hasArgs() const {
        return isNonEvalFunctionFrame();
    }

    uintN numFormalArgs() const {
        JS_ASSERT(hasArgs());
        return fun()->nargs;
    }

    js::Value &formalArg(uintN i) const {
        JS_ASSERT(i < numFormalArgs());
        return formalArgs()[i];
    }

    js::Value *formalArgs() const {
        JS_ASSERT(hasArgs());
        return (js::Value *)this - numFormalArgs();
    }

    js::Value *formalArgsEnd() const {
        JS_ASSERT(hasArgs());
        return (js::Value *)this;
    }

    js::Value *maybeFormalArgs() const {
        return (flags_ & (JSFRAME_FUNCTION | JSFRAME_EVAL)) == JSFRAME_FUNCTION
               ? formalArgs()
               : NULL;
    }

    inline uintN numActualArgs() const;
    inline js::Value *actualArgs() const;
    inline js::Value *actualArgsEnd() const;

    inline js::Value &canonicalActualArg(uintN i) const;
    template <class Op> inline void forEachCanonicalActualArg(Op op);
    template <class Op> inline void forEachFormalArg(Op op);

    inline void clearMissingArgs();

    bool hasArgsObj() const {
        return !!(flags_ & JSFRAME_HAS_ARGS_OBJ);
    }

    JSObject &argsObj() const {
        JS_ASSERT(hasArgsObj());
        JS_ASSERT(!isEvalFrame());
        return *args.obj;
    }

    JSObject *maybeArgsObj() const {
        return hasArgsObj() ? &argsObj() : NULL;
    }

    inline void setArgsObj(JSObject &obj);

    /*
     * 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 or global frame, 'functionThis' and 'globalThis',
     * respectively, allow more efficient access.
     */

    js::Value &functionThis() const {
        JS_ASSERT(isFunctionFrame());
        if (isEvalFrame())
            return ((js::Value *)this)[-1];
        return formalArgs()[-1];
    }

    JSObject &constructorThis() const {
        JS_ASSERT(hasArgs());
        return formalArgs()[-1].toObject();
    }

    js::Value &globalThis() const {
        JS_ASSERT(isGlobalFrame());
        return ((js::Value *)this)[-1];
    }

    js::Value &thisValue() const {
        if (flags_ & (JSFRAME_EVAL | JSFRAME_GLOBAL))
            return ((js::Value *)this)[-1];
        return formalArgs()[-1];
    }

    inline bool computeThis(JSContext *cx);

    /*
     * Callee
     *
     * Only function frames have a callee. An eval frame in a function has the
     * same caller as its containing function frame.
     */

    js::Value &calleeValue() const {
        JS_ASSERT(isFunctionFrame());
        if (isEvalFrame())
            return ((js::Value *)this)[-2];
        return formalArgs()[-2];
    }

    JSObject &callee() const {
        JS_ASSERT(isFunctionFrame());
        return calleeValue().toObject();
    }

    JSObject *maybeCallee() const {
        return isFunctionFrame() ? &callee() : NULL;
    }

    /*
     * getValidCalleeObject is a fallible getter to compute the correct callee
     * function object, which may require deferred cloning due to the JSObject
     * methodReadBarrier. For a non-function frame, return true with *vp set
     * from calleeValue, which may not be an object (it could be undefined).
     */
    bool getValidCalleeObject(JSContext *cx, js::Value *vp);

    /*
     * Scope chain
     *
     * Every frame has a scopeChain which, when traversed via the 'parent' link
     * to the root, indicates the current global object. A 'call object' is a
     * node on a scope chain representing a function's activation record. A
     * call object is used for dynamically-scoped name lookup and lexically-
     * scoped upvar access. The call object holds the values of locals and
     * arguments when a function returns (and its stack frame is popped). For
     * performance reasons, call objects are created lazily for 'lightweight'
     * functions, i.e., functions which are not statically known to require a
     * call object. Thus, a given function frame may or may not have a call
     * object. When a function does have a call object, it is found by walking
     * up the scope chain until the first call object. Thus, it is important,
     * when setting the scope chain, to indicate whether the new scope chain
     * contains a new call object and thus changes the 'hasCallObj' state.
     *
     * NB: 'fp->hasCallObj()' implies that fp->callObj() needs to be 'put' when
     * the frame is popped. Since the scope chain of a non-strict eval frame
     * contains the call object of the parent (function) frame, it is possible
     * to have:
     *   !fp->hasCall() && fp->scopeChain().isCall()
     */

    JSObject &scopeChain() const {
        JS_ASSERT_IF(!(flags_ & JSFRAME_HAS_SCOPECHAIN), isFunctionFrame());
        if (!(flags_ & JSFRAME_HAS_SCOPECHAIN)) {
            scopeChain_ = callee().getParent();
            flags_ |= JSFRAME_HAS_SCOPECHAIN;
        }
        return *scopeChain_;
    }

    bool hasCallObj() const {
        bool ret = !!(flags_ & JSFRAME_HAS_CALL_OBJ);
        JS_ASSERT_IF(ret, !isNonStrictEvalFrame());
        return ret;
    }

    inline JSObject &callObj() const;
    inline void setScopeChainNoCallObj(JSObject &obj);
    inline void setScopeChainWithOwnCallObj(JSObject &obj);

    inline void markActivationObjectsAsPut();

    /*
     * Imacropc
     *
     * A frame's IMacro pc is the bytecode address when an imacro started
     * executing (guaranteed non-null). An imacro does not push a frame, so
     * when the imacro finishes, the frame's IMacro pc becomes the current pc.
     */

    bool hasImacropc() const {
        return flags_ & JSFRAME_HAS_IMACRO_PC;
    }

    jsbytecode *imacropc() const {
        JS_ASSERT(hasImacropc());
        return imacropc_;
    }

    jsbytecode *maybeImacropc() const {
        return hasImacropc() ? imacropc() : NULL;
    }

    void clearImacropc() {
        flags_ &= ~JSFRAME_HAS_IMACRO_PC;
    }

    void setImacropc(jsbytecode *pc) {
        JS_ASSERT(pc);
        JS_ASSERT(!(flags_ & JSFRAME_HAS_IMACRO_PC));
        imacropc_ = pc;
        flags_ |= JSFRAME_HAS_IMACRO_PC;
    }

    /* Annotation (will be removed after bug 546848) */

    void* annotation() const {
        return (flags_ & JSFRAME_HAS_ANNOTATION) ? annotation_ : NULL;
    }

    void setAnnotation(void *annot) {
        flags_ |= JSFRAME_HAS_ANNOTATION;
        annotation_ = annot;
    }

    /* Debugger hook data */

    bool hasHookData() const {
        return !!(flags_ & JSFRAME_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_ |= JSFRAME_HAS_HOOK_DATA;
    }

    /* Return value */

    const js::Value &returnValue() {
        if (!(flags_ & JSFRAME_HAS_RVAL))
            rval_.setUndefined();
        return rval_;
    }

    void markReturnValue() {
        flags_ |= JSFRAME_HAS_RVAL;
    }

    void setReturnValue(const js::Value &v) {
        rval_ = v;
        markReturnValue();
    }

    void clearReturnValue() {
        rval_.setUndefined();
        markReturnValue();
    }

    /* Native-code return address */

    void *nativeReturnAddress() const {
        return ncode_;
    }

    void setNativeReturnAddress(void *addr) {
        ncode_ = addr;
    }

    void **addressOfNativeReturnAddress() {
        return &ncode_;
    }

    /*
     * Generator-specific members
     *
     * A non-eval function frame may optionally be the activation of a
     * generator. For the most part, generator frames act like ordinary frames.
     * For exceptions, see js_FloatingFrameIfGenerator.
     */

    bool isGeneratorFrame() const {
        return !!(flags_ & JSFRAME_GENERATOR);
    }

    bool isFloatingGenerator() const {
        JS_ASSERT_IF(flags_ & JSFRAME_FLOATING_GENERATOR, isGeneratorFrame());
        return !!(flags_ & JSFRAME_FLOATING_GENERATOR);
    }

    void initFloatingGenerator() {
        JS_ASSERT(!(flags_ & JSFRAME_GENERATOR));
        flags_ |= (JSFRAME_GENERATOR | JSFRAME_FLOATING_GENERATOR);
    }

    void unsetFloatingGenerator() {
        flags_ &= ~JSFRAME_FLOATING_GENERATOR;
    }

    void setFloatingGenerator() {
        flags_ |= JSFRAME_FLOATING_GENERATOR;
    }

    /*
     * 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_ & (JSFRAME_GLOBAL | JSFRAME_EVAL));
    }

    /*
     * Other flags
     */

    bool isConstructing() const {
        return !!(flags_ & JSFRAME_CONSTRUCTING);
    }

    uint32 isConstructingFlag() const {
        JS_ASSERT(isFunctionFrame());
        JS_ASSERT((flags_ & ~(JSFRAME_CONSTRUCTING | JSFRAME_FUNCTION)) == 0);
        return flags_;
    }

    bool isDebuggerFrame() const {
        return !!(flags_ & JSFRAME_DEBUGGER);
    }

    bool isDirectEvalOrDebuggerFrame() const {
        return (flags_ & (JSFRAME_EVAL | JSFRAME_DEBUGGER)) && !(flags_ & JSFRAME_GLOBAL);
    }

    bool hasOverriddenArgs() const {
        return !!(flags_ & JSFRAME_OVERRIDE_ARGS);
    }

    bool hasOverflowArgs() const {
        return !!(flags_ & JSFRAME_OVERFLOW_ARGS);
    }

    void setOverriddenArgs() {
        flags_ |= JSFRAME_OVERRIDE_ARGS;
    }

    bool isAssigning() const {
        return !!(flags_ & JSFRAME_ASSIGNING);
    }

    void setAssigning() {
        flags_ |= JSFRAME_ASSIGNING;
    }

    void clearAssigning() {
        flags_ &= ~JSFRAME_ASSIGNING;
    }

    bool isYielding() {
        return !!(flags_ & JSFRAME_YIELDING);
    }

    void setYielding() {
        flags_ |= JSFRAME_YIELDING;
    }

    void clearYielding() {
        flags_ &= ~JSFRAME_YIELDING;
    }

    void setFinishedInInterpreter() {
        flags_ |= JSFRAME_FINISHED_IN_INTERPRETER;
    }

    bool finishedInInterpreter() const {
        return !!(flags_ & JSFRAME_FINISHED_IN_INTERPRETER);
    }

    /*
     * Variables object accessors
     *
     * A stack frame's 'varobj' refers to the 'variables object' (ES3 term)
     * associated with the Execution Context's VariableEnvironment (ES5 10.3).
     *
     * To compute the frame's varobj, the caller must supply the segment
     * containing the frame (see js::StackSegment comment). As an abbreviation,
     * the caller may pass the context if the frame is contained in that
     * context's active segment.
     */

    inline JSObject &varobj(js::StackSegment *seg) const;
    inline JSObject &varobj(JSContext *cx) const;

    /* Access to privates from the jits. */

    static size_t offsetOfFlags() {
        return offsetof(JSStackFrame, flags_);
    }

    static size_t offsetOfExec() {
        return offsetof(JSStackFrame, exec);
    }

    void *addressOfArgs() {
        return &args;
    }

    static size_t offsetOfScopeChain() {
        return offsetof(JSStackFrame, scopeChain_);
    }

    JSObject **addressOfScopeChain() {
        JS_ASSERT(flags_ & JSFRAME_HAS_SCOPECHAIN);
        return &scopeChain_;
    }

    static size_t offsetOfPrev() {
        return offsetof(JSStackFrame, prev_);
    }

    static size_t offsetOfReturnValue() {
        return offsetof(JSStackFrame, rval_);
    }

    static ptrdiff_t offsetOfncode() {
        return offsetof(JSStackFrame, ncode_);
    }

    static ptrdiff_t offsetOfCallee(JSFunction *fun) {
        JS_ASSERT(fun != NULL);
        return -(fun->nargs + 2) * sizeof(js::Value);
    }

    static ptrdiff_t offsetOfThis(JSFunction *fun) {
        return fun == NULL
               ? -1 * ptrdiff_t(sizeof(js::Value))
               : -(fun->nargs + 1) * ptrdiff_t(sizeof(js::Value));
    }

    static ptrdiff_t offsetOfFormalArg(JSFunction *fun, uintN i) {
        JS_ASSERT(i < fun->nargs);
        return (-(int)fun->nargs + i) * sizeof(js::Value);
    }

    static size_t offsetOfFixed(uintN i) {
        return sizeof(JSStackFrame) + i * sizeof(js::Value);
    }

    /* Workaround for static asserts on private members. */

    void staticAsserts() {
        JS_STATIC_ASSERT(offsetof(JSStackFrame, rval_) % sizeof(js::Value) == 0);
        JS_STATIC_ASSERT(sizeof(JSStackFrame) % sizeof(js::Value) == 0);
    }

#ifdef JS_METHODJIT
    js::mjit::JITScript *jit() {
        return script()->getJIT(isConstructing());
    }
#endif

    void methodjitStaticAsserts();

#ifdef DEBUG
    /* Poison scopeChain value set before a frame is flushed. */
    static JSObject *const sInvalidScopeChain;
#endif
};

namespace js {

static const size_t VALUES_PER_STACK_FRAME = sizeof(JSStackFrame) / sizeof(Value);

extern JSObject *
GetBlockChain(JSContext *cx, JSStackFrame *fp);

extern JSObject *
GetBlockChainFast(JSContext *cx, JSStackFrame *fp, JSOp op, size_t oplen);

extern JSObject *
GetScopeChain(JSContext *cx);

/*
 * Refresh and return fp->scopeChain.  It may be stale if block scopes are
 * active but not yet reflected by objects in the scope chain.  If a block
 * scope contains a with, eval, XML filtering predicate, or similar such
 * dynamically scoped construct, then compile-time block scope at fp->blocks
 * must reflect at runtime.
 */
extern JSObject *
GetScopeChain(JSContext *cx, JSStackFrame *fp);

extern JSObject *
GetScopeChainFast(JSContext *cx, JSStackFrame *fp, JSOp op, size_t oplen);

/*
 * Report an error that the this value passed as |this| in the given arguments
 * vector is not compatible with the specified class.
 */
void
ReportIncompatibleMethod(JSContext *cx, Value *vp, Class *clasp);

/*
 * Given a context and a vector of [callee, this, args...] for a function
 * whose JSFUN_PRIMITIVE_THIS flag is set, set |*v| to the primitive value
 * of |this|. If |this| is an object, insist that it be an instance of the
 * appropriate wrapper class for T, and set |*v| to its private slot value.
 * If |this| is a primitive, unbox it into |*v| if it's of the required
 * type, and throw an error otherwise.
 */
template <typename T>
bool GetPrimitiveThis(JSContext *cx, Value *vp, T *v);

inline void
PutActivationObjects(JSContext *cx, JSStackFrame *fp)
{
    /* The order is important since js_PutCallObject does js_PutArgsObject. */
    if (fp->hasCallObj())
        js_PutCallObject(cx, fp);
    else if (fp->hasArgsObj())
        js_PutArgsObject(cx, fp);
}

/*
 * ScriptPrologue/ScriptEpilogue must be called in pairs. ScriptPrologue
 * must be called before the script executes. ScriptEpilogue must be called
 * after the script returns or exits via exception.
 */

inline bool
ScriptPrologue(JSContext *cx, JSStackFrame *fp, JSScript *script);

inline bool
ScriptEpilogue(JSContext *cx, JSStackFrame *fp, bool ok);

/*
 * It is not valid to call ScriptPrologue when a generator is resumed or to
 * call ScriptEpilogue when a generator yields. However, the debugger still
 * needs LIFO notification of generator start/stop. This pair of functions does
 * the right thing based on the state of 'fp'.
 */

inline bool
ScriptPrologueOrGeneratorResume(JSContext *cx, JSStackFrame *fp);

inline bool
ScriptEpilogueOrGeneratorYield(JSContext *cx, JSStackFrame *fp, bool ok);

/* Implemented in jsdbgapi: */

extern void
ScriptDebugPrologue(JSContext *cx, JSStackFrame *fp);

extern bool
ScriptDebugEpilogue(JSContext *cx, JSStackFrame *fp, bool ok);

/*
 * For a call's vp (which necessarily includes callee at vp[0] and the original
 * specified |this| at vp[1]), convert null/undefined |this| into the global
 * object for the callee and replace other primitives with boxed versions. The
 * callee must not be strict mode code.
 */
extern bool
BoxThisForVp(JSContext *cx, js::Value *vp);

/*
 * Abstracts the layout of the stack passed to natives from the engine and from
 * natives to js::Invoke.
 */
struct CallArgs
{
    Value *argv_;
    uintN argc_;
  protected:
    CallArgs() {}
    CallArgs(Value *argv, uintN argc) : argv_(argv), argc_(argc) {}
  public:
    Value *base() const { return argv_ - 2; }
    Value &callee() const { return argv_[-2]; }
    Value &thisv() const { return argv_[-1]; }
    Value &operator[](unsigned i) const { JS_ASSERT(i < argc_); return argv_[i]; }
    Value *argv() const { return argv_; }
    uintN argc() const { return argc_; }
    Value &rval() const { return argv_[-2]; }
};

/*
 * The js::InvokeArgumentsGuard passed to js_Invoke must come from an
 * immediately-enclosing successful call to js::StackSpace::pushInvokeArgs,
 * i.e., there must have been no un-popped pushes to cx->stack(). Furthermore,
 * |args.getvp()[0]| should be the callee, |args.getvp()[1]| should be |this|,
 * and the range [args.getvp() + 2, args.getvp() + 2 + args.getArgc()) should
 * be initialized actual arguments.
 */
extern JS_REQUIRES_STACK bool
Invoke(JSContext *cx, const CallArgs &args, uint32 flags);

/*
 * Natives like sort/forEach/replace call Invoke repeatedly with the same
 * callee, this, and number of arguments. To optimize this, such natives can
 * start an "invoke session" to factor out much of the dynamic setup logic
 * required by a normal Invoke. Usage is:
 *
 *   InvokeSessionGuard session(cx);
 *   if (!session.start(cx, callee, thisp, argc, &session))
 *     ...
 *
 *   while (...) {
 *     // write actual args (not callee, this)
 *     session[0] = ...
 *     ...
 *     session[argc - 1] = ...
 *
 *     if (!session.invoke(cx, session))
 *       ...
 *
 *     ... = session.rval();
 *   }
 *
 *   // session ended by ~InvokeSessionGuard
 */
class InvokeSessionGuard;

/*
 * Consolidated js_Invoke flags simply rename certain JSFRAME_* flags, so that
 * we can share bits stored in JSStackFrame.flags and passed to:
 *
 *   js_Invoke
 *   js_InternalInvoke
 *   js_ValueToFunction
 *   js_ValueToFunctionObject
 *   js_ValueToCallableObject
 *   js_ReportIsNotFunction
 *
 * See jsfun.h for the latter four and flag renaming macros.
 */
#define JSINVOKE_CONSTRUCT      JSFRAME_CONSTRUCTING

/*
 * Mask to isolate construct and iterator flags for use with jsfun.h functions.
 */
#define JSINVOKE_FUNFLAGS       JSINVOKE_CONSTRUCT

/*
 * "External" calls may come from C or C++ code using a JSContext on which no
 * JS is running (!cx->fp), so they may need to push a dummy JSStackFrame.
 */

extern bool
ExternalInvoke(JSContext *cx, const Value &thisv, const Value &fval,
               uintN argc, Value *argv, Value *rval);

extern bool
ExternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, const Value &fval,
                 JSAccessMode mode, uintN argc, Value *argv, Value *rval);

/*
 * These two functions invoke a function called from a constructor context
 * (e.g. 'new'). InvokeConstructor handles the general case where a new object
 * needs to be created for/by the constructor. ConstructWithGivenThis directly
 * calls the constructor with the given 'this', hence the caller must
 * understand the semantics of the constructor call.
 */

extern JS_REQUIRES_STACK bool
InvokeConstructor(JSContext *cx, const CallArgs &args);

extern JS_REQUIRES_STACK bool
InvokeConstructorWithGivenThis(JSContext *cx, JSObject *thisobj, const Value &fval,
                               uintN argc, Value *argv, Value *rval);

extern bool
ExternalInvokeConstructor(JSContext *cx, const Value &fval, uintN argc, Value *argv,
                          Value *rval);

/*
 * Performs a direct eval for the given arguments, which must correspond to the
 * currently-executing stack frame, which must be a script frame. On completion
 * the result is returned in *vp and the JS stack pointer is adjusted.
 */
extern JS_REQUIRES_STACK bool
DirectEval(JSContext *cx, uint32 argc, Value *vp);

/*
 * Performs a direct eval for the given arguments, which must correspond to the
 * currently-executing stack frame, which must be a script frame.  evalfun must
 * be the built-in eval function and must correspond to the callee in vp[0].
 * When this function succeeds it returns the result in *vp, adjusts the JS
 * stack pointer, and returns true.
 */
extern JS_REQUIRES_STACK bool
DirectEval(JSContext *cx, JSFunction *evalfun, uint32 argc, Value *vp);

/*
 * Executes a script with the given scope chain in the context of the given
 * frame.
 */
extern JS_FORCES_STACK bool
Execute(JSContext *cx, JSObject *chain, JSScript *script,
        JSStackFrame *prev, uintN flags, Value *result);

/*
 * Execute the caller-initialized frame for a user-defined script or function
 * pointed to by cx->fp until completion or error.
 */
extern JS_REQUIRES_STACK JS_NEVER_INLINE bool
Interpret(JSContext *cx, JSStackFrame *stopFp, uintN inlineCallCount = 0, JSInterpMode mode = JSINTERP_NORMAL);

extern JS_REQUIRES_STACK bool
RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp);

extern bool
CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs);

extern bool
StrictlyEqual(JSContext *cx, const Value &lval, const Value &rval, JSBool *equal);

extern bool
LooselyEqual(JSContext *cx, const Value &lval, const Value &rval, JSBool *equal);

/* === except that NaN is the same as NaN and -0 is not the same as +0. */
extern bool
SameValue(JSContext *cx, const Value &v1, const Value &v2, JSBool *same);

extern JSType
TypeOfValue(JSContext *cx, const Value &v);

extern JSBool
HasInstance(JSContext *cx, JSObject *obj, const js::Value *v, JSBool *bp);

extern bool
ValueToId(JSContext *cx, const Value &v, jsid *idp);

/*
 * @param closureLevel      The static level of the closure that the cookie
 *                          pertains to.
 * @param cookie            Level amount is a "skip" (delta) value from the
 *                          closure level.
 * @return  The value of the upvar.
 */
extern const js::Value &
GetUpvar(JSContext *cx, uintN level, js::UpvarCookie cookie);

} /* namespace js */

/*
 * JS_LONE_INTERPRET indicates that the compiler should see just the code for
 * the js_Interpret function when compiling jsinterp.cpp. The rest of the code
 * from the file should be visible only when compiling jsinvoke.cpp. It allows
 * platform builds to optimize selectively js_Interpret when the granularity
 * of the optimizations with the given compiler is a compilation unit.
 *
 * JS_STATIC_INTERPRET is the modifier for functions defined in jsinterp.cpp
 * that only js_Interpret calls. When JS_LONE_INTERPRET is true all such
 * functions are declared below.
 */
#ifndef JS_LONE_INTERPRET
# ifdef _MSC_VER
#  define JS_LONE_INTERPRET 0
# else
#  define JS_LONE_INTERPRET 1
# endif
#endif

#define JS_MAX_INLINE_CALL_COUNT 3000

#if !JS_LONE_INTERPRET
# define JS_STATIC_INTERPRET    static
#else
# define JS_STATIC_INTERPRET

extern JS_REQUIRES_STACK JSBool
js_EnterWith(JSContext *cx, jsint stackIndex, JSOp op, size_t oplen);

extern JS_REQUIRES_STACK void
js_LeaveWith(JSContext *cx);

/*
 * Find the results of incrementing or decrementing *vp. For pre-increments,
 * both *vp and *vp2 will contain the result on return. For post-increments,
 * vp will contain the original value converted to a number and vp2 will get
 * the result. Both vp and vp2 must be roots.
 */
extern JSBool
js_DoIncDec(JSContext *cx, const JSCodeSpec *cs, js::Value *vp, js::Value *vp2);

/*
 * Opcode tracing helper. When len is not 0, cx->fp->regs->pc[-len] gives the
 * previous opcode.
 */
extern JS_REQUIRES_STACK void
js_LogOpcode(JSContext *cx);

/*
 * JS_OPMETER helper functions.
 */
extern void
js_MeterOpcodePair(JSOp op1, JSOp op2);

extern void
js_MeterSlotOpcode(JSOp op, uint32 slot);

#endif /* JS_LONE_INTERPRET */
/*
 * Unwind block and scope chains to match the given depth. The function sets
 * fp->sp on return to stackDepth.
 */
extern JS_REQUIRES_STACK JSBool
js_UnwindScope(JSContext *cx, jsint stackDepth, JSBool normalUnwind);

extern JSBool
js_OnUnknownMethod(JSContext *cx, js::Value *vp);

extern JS_REQUIRES_STACK js::Class *
js_IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth);

#endif /* jsinterp_h___ */
back to top