gecko/js/src/jsinterp.h

1181 lines
37 KiB
C
Raw Normal View History

/* -*- 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"
2010-07-14 23:19:36 -07:00
#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);
}
Bug 480132: Clone lexical blocks only when needed. r=igor Terminology: A "script block" is an object of class Block allocated by the byte compiler and associated with a script. Script blocks are never modified, and may be used as a prototype for a "closure block": A "closure block" is an object of class Block that holds variables that have been closed over (although we actually leave the variables on the stack until we leave their dynamic scope). A closure block is a clone of a script block (its prototype is a script block). Adjust the meanings of fp->blockChain and fp->scopeChain: fp->blockChain is always the innermost script block in whose static scope we're executing. fp->scopeChain is the current scope chain, including 'call' objects and closure blocks for those function calls and blocks in whose static scope we are currently executing, and 'with' objects for with statements; the chain is typically terminated by a global object. However, as an optimization, the young end of the chain omits block objects we have not yet needed to clone. Closures need fully reified scope chains, so have js_GetScopeChain reify any closure blocks missing from the young end of fp->scopeChain by cloning script blocks as needed from fp->blockChain. Thus, if we never actually close over a particular block, we never place a closure block for it on fp->scopeChain. Have JSOP_ENTERBLOCK and JSOP_LEAVEBLOCK always keep fp->blockChain current. When JSOP_LEAVEBLOCK pops a block from fp->blockChain that has been cloned on fp->scopeChain, pop fp->scopeChain as well. Remove the JSFRAME_POP_BLOCKS flag, as it is no longer needed. Ensure that the JIT won't have to create closure blocks or call js_PutBlockObject; it can't handle those things yet. Note our current script block when we begin recording. Abort recording if we leave that block; we can't tell in advance whether it will need to be "put" in future trace invocations. Leave trace if we call js_GetScopeChain while in the static scope of lexical blocks. Remove JIT tests based on JSFRAME_POP_BLOCKS. Verify that generators capture the correct value for blockChain. Add a constructor to JSAutoTempValueRooter for rooting JSObject pointers.
2009-03-16 09:55:06 -07:00
bool isScriptFrame() const {
bool retval = !!(flags_ & (JSFRAME_FUNCTION | JSFRAME_GLOBAL));
JS_ASSERT(retval == !isDummyFrame());
return retval;
}
2010-05-22 17:09:52 -07:00
/*
* 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;
}
Bug 480132: Clone lexical blocks only when needed. r=igor Terminology: A "script block" is an object of class Block allocated by the byte compiler and associated with a script. Script blocks are never modified, and may be used as a prototype for a "closure block": A "closure block" is an object of class Block that holds variables that have been closed over (although we actually leave the variables on the stack until we leave their dynamic scope). A closure block is a clone of a script block (its prototype is a script block). Adjust the meanings of fp->blockChain and fp->scopeChain: fp->blockChain is always the innermost script block in whose static scope we're executing. fp->scopeChain is the current scope chain, including 'call' objects and closure blocks for those function calls and blocks in whose static scope we are currently executing, and 'with' objects for with statements; the chain is typically terminated by a global object. However, as an optimization, the young end of the chain omits block objects we have not yet needed to clone. Closures need fully reified scope chains, so have js_GetScopeChain reify any closure blocks missing from the young end of fp->scopeChain by cloning script blocks as needed from fp->blockChain. Thus, if we never actually close over a particular block, we never place a closure block for it on fp->scopeChain. Have JSOP_ENTERBLOCK and JSOP_LEAVEBLOCK always keep fp->blockChain current. When JSOP_LEAVEBLOCK pops a block from fp->blockChain that has been cloned on fp->scopeChain, pop fp->scopeChain as well. Remove the JSFRAME_POP_BLOCKS flag, as it is no longer needed. Ensure that the JIT won't have to create closure blocks or call js_PutBlockObject; it can't handle those things yet. Note our current script block when we begin recording. Abort recording if we leave that block; we can't tell in advance whether it will need to be "put" in future trace invocations. Leave trace if we call js_GetScopeChain while in the static scope of lexical blocks. Remove JIT tests based on JSFRAME_POP_BLOCKS. Verify that generators capture the correct value for blockChain. Add a constructor to JSAutoTempValueRooter for rooting JSObject pointers.
2009-03-16 09:55:06 -07:00
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, 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.
*/
2010-07-14 23:19:36 -07:00
js::Value *slots() const {
return (js::Value *)(this + 1);
}
2010-07-14 23:19:36 -07:00
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 isEvalOrDebuggerFrame() const {
return !!(flags_ & (JSFRAME_EVAL | JSFRAME_DEBUGGER));
}
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 {
2010-07-14 23:19:36 -07:00
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);
2010-07-14 23:19:36 -07:00
/*
* 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, JSScript *script, 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.
*/
2010-07-14 23:19:36 -07:00
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);
2010-07-14 23:19:36 -07:00
extern bool
ExternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, const Value &fval,
2010-07-14 23:19:36 -07:00
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.
*/
2010-07-14 23:19:36 -07:00
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);
2010-05-22 17:41:21 -07:00
extern JS_REQUIRES_STACK bool
RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp);
2010-07-14 23:19:36 -07:00
extern bool
CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs);
2010-07-14 23:19:36 -07:00
extern bool
StrictlyEqual(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. */
2010-07-14 23:19:36 -07:00
extern bool
SameValue(JSContext *cx, const Value &v1, const Value &v2, JSBool *same);
2010-07-14 23:19:36 -07:00
extern JSType
TypeOfValue(JSContext *cx, const Value &v);
inline bool
InstanceOf(JSContext *cx, JSObject *obj, Class *clasp, Value *argv)
{
if (obj && obj->getClass() == clasp)
return true;
extern bool InstanceOfSlow(JSContext *, JSObject *, Class *, Value *);
return InstanceOfSlow(cx, obj, clasp, argv);
}
extern JSBool
HasInstance(JSContext *cx, JSObject *obj, const js::Value *v, JSBool *bp);
2010-07-14 23:19:36 -07:00
inline void *
GetInstancePrivate(JSContext *cx, JSObject *obj, Class *clasp, Value *argv)
{
if (!InstanceOf(cx, obj, clasp, argv))
return NULL;
return obj->getPrivate();
}
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.
*/
2010-07-14 23:19:36 -07:00
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
2010-07-14 23:19:36 -07:00
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 */
2010-06-01 18:06:46 -07:00
/*
* 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);
2010-07-19 20:49:26 -07:00
extern JS_REQUIRES_STACK js::Class *
js_IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth);
#endif /* jsinterp_h___ */