blob: a16cfe31fd5fe73a0afed8ddbb42b5db2cab296e [file] [log] [blame]
/* -*- 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_Interpreter_h
#define vm_Interpreter_h
/*
* JS interpreter interface.
*/
#include "jsiter.h"
#include "jsprvtd.h"
#include "jspubtd.h"
#include "vm/Stack.h"
namespace js {
/* Implemented in jsdbgapi: */
/*
* Announce to the debugger that the thread has entered a new JavaScript frame,
* |frame|. Call whatever hooks have been registered to observe new frames, and
* return a JSTrapStatus code indication how execution should proceed:
*
* - JSTRAP_CONTINUE: Continue execution normally.
*
* - JSTRAP_THROW: Throw an exception. ScriptDebugPrologue has set |cx|'s
* pending exception to the value to be thrown.
*
* - JSTRAP_ERROR: Terminate execution (as is done when a script is terminated
* for running too long). ScriptDebugPrologue has cleared |cx|'s pending
* exception.
*
* - JSTRAP_RETURN: Return from the new frame immediately. ScriptDebugPrologue
* has set |frame|'s return value appropriately.
*/
extern JSTrapStatus
ScriptDebugPrologue(JSContext *cx, AbstractFramePtr frame);
/*
* Announce to the debugger that the thread has exited a JavaScript frame, |frame|.
* If |ok| is true, the frame is returning normally; if |ok| is false, the frame
* is throwing an exception or terminating.
*
* Call whatever hooks have been registered to observe frame exits. Change cx's
* current exception and |frame|'s return value to reflect the changes in behavior
* the hooks request, if any. Return the new error/success value.
*
* This function may be called twice for the same outgoing frame; only the
* first call has any effect. (Permitting double calls simplifies some
* cases where an onPop handler's resumption value changes a return to a
* throw, or vice versa: we can redirect to a complete copy of the
* alternative path, containing its own call to ScriptDebugEpilogue.)
*/
extern bool
ScriptDebugEpilogue(JSContext *cx, AbstractFramePtr frame, bool ok);
/*
* Announce to the debugger that an exception has been thrown and propagated
* to |frame|. Call whatever hooks have been registered to observe this and
* return a JSTrapStatus code indication how execution should proceed:
*
* - JSTRAP_CONTINUE: Continue throwing the current exception.
*
* - JSTRAP_THROW: Throw another value. DebugExceptionUnwind has set |cx|'s
* pending exception to the new value.
*
* - JSTRAP_ERROR: Terminate execution. DebugExceptionUnwind has cleared |cx|'s
* pending exception.
*
* - JSTRAP_RETURN: Return from |frame|. DebugExceptionUnwind has cleared
* |cx|'s pending exception and set |frame|'s return value.
*/
extern JSTrapStatus
DebugExceptionUnwind(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc);
/*
* For a given |call|, convert null/undefined |this| into the global object for
* the callee and replace other primitives with boxed versions. This assumes
* that call.callee() is not strict mode code. This is the special/slow case of
* ComputeThis.
*/
extern bool
BoxNonStrictThis(JSContext *cx, const CallReceiver &call);
extern bool
BoxNonStrictThis(JSContext *cx, MutableHandleValue thisv, bool *modified);
/*
* Ensure that fp->thisValue() is the correct value of |this| for the scripted
* call represented by |fp|. ComputeThis is necessary because fp->thisValue()
* may be set to 'undefined' when 'this' should really be the global object (as
* an optimization to avoid global-this computation).
*/
inline bool
ComputeThis(JSContext *cx, AbstractFramePtr frame);
enum MaybeConstruct {
NO_CONSTRUCT = INITIAL_NONE,
CONSTRUCT = INITIAL_CONSTRUCT
};
/*
* numToSkip is the number of stack values the expression decompiler should skip
* before it reaches |v|. If it's -1, the decompiler will search the stack.
*/
extern bool
ReportIsNotFunction(JSContext *cx, const Value &v, int numToSkip = -1,
MaybeConstruct construct = NO_CONSTRUCT);
/* See ReportIsNotFunction comment for the meaning of numToSkip. */
extern JSObject *
ValueToCallable(JSContext *cx, const Value &vp, int numToSkip = -1,
MaybeConstruct construct = NO_CONSTRUCT);
/*
* Invoke assumes that the given args have been pushed on the top of the
* VM stack.
*/
extern bool
Invoke(JSContext *cx, CallArgs args, MaybeConstruct construct = NO_CONSTRUCT);
/*
* This Invoke overload places the least requirements on the caller: it may be
* called at any time and it takes care of copying the given callee, this, and
* arguments onto the stack.
*/
extern bool
Invoke(JSContext *cx, const Value &thisv, const Value &fval, unsigned argc, Value *argv,
Value *rval);
/*
* This helper takes care of the infinite-recursion check necessary for
* getter/setter calls.
*/
extern bool
InvokeGetterOrSetter(JSContext *cx, JSObject *obj, const Value &fval, unsigned argc, Value *argv,
Value *rval);
/*
* InvokeConstructor implement a function call from a constructor context
* (e.g. 'new') handling the the creation of the new 'this' object.
*/
extern bool
InvokeConstructor(JSContext *cx, CallArgs args);
/* See the fval overload of Invoke. */
extern bool
InvokeConstructor(JSContext *cx, const Value &fval, unsigned argc, Value *argv, Value *rval);
/*
* Executes a script with the given scopeChain/this. The 'type' indicates
* whether this is eval code or global code. To support debugging, the
* evalFrame parameter can point to an arbitrary frame in the context's call
* stack to simulate executing an eval in that frame.
*/
extern bool
ExecuteKernel(JSContext *cx, HandleScript script, JSObject &scopeChain, const Value &thisv,
ExecuteType type, AbstractFramePtr evalInFrame, Value *result);
/* Execute a script with the given scopeChain as global code. */
extern bool
Execute(JSContext *cx, HandleScript script, JSObject &scopeChain, Value *rval);
class ExecuteState;
class InvokeState;
class GeneratorState;
// RunState is passed to RunScript and RunScript then eiter passes it to the
// interpreter or to the JITs. RunState contains all information we need to
// construct an interpreter or JIT frame.
class RunState
{
protected:
enum Kind { Execute, Invoke, Generator };
Kind kind_;
RootedScript script_;
explicit RunState(JSContext *cx, Kind kind, JSScript *script)
: kind_(kind),
script_(cx, script)
{ }
public:
bool isExecute() const { return kind_ == Execute; }
bool isInvoke() const { return kind_ == Invoke; }
bool isGenerator() const { return kind_ == Generator; }
ExecuteState *asExecute() const {
JS_ASSERT(isExecute());
return (ExecuteState *)this;
}
InvokeState *asInvoke() const {
JS_ASSERT(isInvoke());
return (InvokeState *)this;
}
GeneratorState *asGenerator() const {
JS_ASSERT(isGenerator());
return (GeneratorState *)this;
}
JSScript *script() const { return script_; }
virtual StackFrame *pushInterpreterFrame(JSContext *cx, FrameGuard *fg) = 0;
virtual void setReturnValue(Value v) = 0;
private:
RunState(const RunState &other) MOZ_DELETE;
RunState(const ExecuteState &other) MOZ_DELETE;
RunState(const InvokeState &other) MOZ_DELETE;
RunState(const GeneratorState &other) MOZ_DELETE;
void operator=(const RunState &other) MOZ_DELETE;
};
// Eval or global script.
class ExecuteState : public RunState
{
ExecuteType type_;
RootedValue thisv_;
RootedObject scopeChain_;
AbstractFramePtr evalInFrame_;
Value *result_;
public:
ExecuteState(JSContext *cx, JSScript *script, const Value &thisv, JSObject &scopeChain,
ExecuteType type, AbstractFramePtr evalInFrame, Value *result)
: RunState(cx, Execute, script),
type_(type),
thisv_(cx, thisv),
scopeChain_(cx, &scopeChain),
evalInFrame_(evalInFrame),
result_(result)
{ }
Value *addressOfThisv() { return thisv_.address(); }
JSObject *scopeChain() const { return scopeChain_; }
ExecuteType type() const { return type_; }
virtual StackFrame *pushInterpreterFrame(JSContext *cx, FrameGuard *fg);
virtual void setReturnValue(Value v) {
if (result_)
*result_ = v;
}
};
// Data to invoke a function.
class InvokeState : public RunState
{
CallArgs &args_;
InitialFrameFlags initial_;
bool useNewType_;
public:
InvokeState(JSContext *cx, CallArgs &args, InitialFrameFlags initial)
: RunState(cx, Invoke, args.callee().as<JSFunction>().nonLazyScript()),
args_(args),
initial_(initial),
useNewType_(false)
{ }
bool useNewType() const { return useNewType_; }
void setUseNewType() { useNewType_ = true; }
bool constructing() const { return InitialFrameFlagsAreConstructing(initial_); }
CallArgs &args() const { return args_; }
virtual StackFrame *pushInterpreterFrame(JSContext *cx, FrameGuard *fg);
virtual void setReturnValue(Value v) {
args_.rval().set(v);
}
};
// Generator script.
class GeneratorState : public RunState
{
JSContext *cx_;
JSGenerator *gen_;
JSGeneratorState futureState_;
bool entered_;
public:
GeneratorState(JSContext *cx, JSGenerator *gen, JSGeneratorState futureState);
~GeneratorState();
virtual StackFrame *pushInterpreterFrame(JSContext *cx, FrameGuard *fg);
virtual void setReturnValue(Value) { }
JSGenerator *gen() const { return gen_; }
};
extern bool
RunScript(JSContext *cx, RunState &state);
extern bool
StrictlyEqual(JSContext *cx, const Value &lval, const Value &rval, bool *equal);
extern bool
LooselyEqual(JSContext *cx, const Value &lval, const Value &rval, bool *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, bool *same);
extern JSType
TypeOfValue(JSContext *cx, const Value &v);
extern bool
HasInstance(JSContext *cx, HandleObject obj, HandleValue v, JSBool *bp);
/*
* A linked list of the |FrameRegs regs;| variables belonging to all
* js::Interpret C++ frames on this thread's stack.
*
* Note that this is *not* a list of all JS frames running under the
* interpreter; that would include inlined frames, whose FrameRegs are
* saved in various pieces in various places. Rather, this lists each
* js::Interpret call's live 'regs'; when control returns to that call, it
* will resume execution with this 'regs' instance.
*
* When Debugger puts a script in single-step mode, all js::Interpret
* invocations that might be presently running that script must have
* interrupts enabled. It's not practical to simply check
* script->stepModeEnabled() at each point some callee could have changed
* it, because there are so many places js::Interpret could possibly cause
* JavaScript to run: each place an object might be coerced to a primitive
* or a number, for example. So instead, we simply expose a list of the
* 'regs' those frames are using, and let Debugger tweak the affected
* js::Interpret frames when an onStep handler is established.
*
* Elements of this list are allocated within the js::Interpret stack
* frames themselves; the list is headed by this thread's js::ThreadData.
*/
class InterpreterFrames {
public:
class InterruptEnablerBase {
public:
virtual void enable() const = 0;
};
InterpreterFrames(JSContext *cx, FrameRegs *regs, const InterruptEnablerBase &enabler);
~InterpreterFrames();
/* If this js::Interpret frame is running |script|, enable interrupts. */
inline void enableInterruptsIfRunning(JSScript *script);
inline void enableInterruptsUnconditionally() { enabler.enable(); }
InterpreterFrames *older;
private:
JSContext *context;
FrameRegs *regs;
const InterruptEnablerBase &enabler;
};
/* Unwind block and scope chains to match the given depth. */
extern void
UnwindScope(JSContext *cx, AbstractFramePtr frame, uint32_t stackDepth);
/*
* Unwind for an uncatchable exception. This means not running finalizers, etc;
* just preserving the basic engine stack invariants.
*/
extern void
UnwindForUncatchableException(JSContext *cx, const FrameRegs &regs);
extern bool
OnUnknownMethod(JSContext *cx, HandleObject obj, Value idval, MutableHandleValue vp);
class TryNoteIter
{
const FrameRegs &regs;
RootedScript script; /* TryNotIter is always stack allocated. */
uint32_t pcOffset;
JSTryNote *tn, *tnEnd;
void settle();
public:
explicit TryNoteIter(JSContext *cx, const FrameRegs &regs);
bool done() const;
void operator++();
JSTryNote *operator*() const { return tn; }
};
/************************************************************************/
/*
* To really poison a set of values, using 'magic' or 'undefined' isn't good
* enough since often these will just be ignored by buggy code (see bug 629974)
* in debug builds and crash in release builds. Instead, we use a safe-for-crash
* pointer.
*/
static JS_ALWAYS_INLINE void
Debug_SetValueRangeToCrashOnTouch(Value *beg, Value *end)
{
#ifdef DEBUG
for (Value *v = beg; v != end; ++v)
v->setObject(*reinterpret_cast<JSObject *>(0x42));
#endif
}
static JS_ALWAYS_INLINE void
Debug_SetValueRangeToCrashOnTouch(Value *vec, size_t len)
{
#ifdef DEBUG
Debug_SetValueRangeToCrashOnTouch(vec, vec + len);
#endif
}
static JS_ALWAYS_INLINE void
Debug_SetValueRangeToCrashOnTouch(HeapValue *vec, size_t len)
{
#ifdef DEBUG
Debug_SetValueRangeToCrashOnTouch((Value *) vec, len);
#endif
}
bool
Throw(JSContext *cx, HandleValue v);
bool
GetProperty(JSContext *cx, HandleValue value, HandlePropertyName name, MutableHandleValue vp);
bool
GetScopeName(JSContext *cx, HandleObject obj, HandlePropertyName name, MutableHandleValue vp);
bool
GetScopeNameForTypeOf(JSContext *cx, HandleObject obj, HandlePropertyName name,
MutableHandleValue vp);
JSObject *
Lambda(JSContext *cx, HandleFunction fun, HandleObject parent);
bool
GetElement(JSContext *cx, MutableHandleValue lref, HandleValue rref, MutableHandleValue res);
bool
GetElementMonitored(JSContext *cx, MutableHandleValue lref, HandleValue rref, MutableHandleValue res);
bool
CallElement(JSContext *cx, MutableHandleValue lref, HandleValue rref, MutableHandleValue res);
bool
SetObjectElement(JSContext *cx, HandleObject obj, HandleValue index, HandleValue value,
JSBool strict);
bool
SetObjectElement(JSContext *cx, HandleObject obj, HandleValue index, HandleValue value,
JSBool strict, HandleScript script, jsbytecode *pc);
bool
InitElementArray(JSContext *cx, jsbytecode *pc,
HandleObject obj, uint32_t index, HandleValue value);
bool
AddValues(JSContext *cx, HandleScript script, jsbytecode *pc,
MutableHandleValue lhs, MutableHandleValue rhs,
Value *res);
bool
SubValues(JSContext *cx, HandleScript script, jsbytecode *pc,
MutableHandleValue lhs, MutableHandleValue rhs,
Value *res);
bool
MulValues(JSContext *cx, HandleScript script, jsbytecode *pc,
MutableHandleValue lhs, MutableHandleValue rhs,
Value *res);
bool
DivValues(JSContext *cx, HandleScript script, jsbytecode *pc,
MutableHandleValue lhs, MutableHandleValue rhs,
Value *res);
bool
ModValues(JSContext *cx, HandleScript script, jsbytecode *pc,
MutableHandleValue lhs, MutableHandleValue rhs,
Value *res);
bool
UrshValues(JSContext *cx, HandleScript script, jsbytecode *pc,
MutableHandleValue lhs, MutableHandleValue rhs,
Value *res);
template <bool strict>
bool
SetProperty(JSContext *cx, HandleObject obj, HandleId id, const Value &value);
template <bool strict>
bool
DeleteProperty(JSContext *ctx, HandleValue val, HandlePropertyName name, JSBool *bv);
template <bool strict>
bool
DeleteElement(JSContext *cx, HandleValue val, HandleValue index, JSBool *bv);
bool
DefFunOperation(JSContext *cx, HandleScript script, HandleObject scopeChain, HandleFunction funArg);
bool
SetCallOperation(JSContext *cx);
bool
GetAndClearException(JSContext *cx, MutableHandleValue res);
bool
DeleteNameOperation(JSContext *cx, HandlePropertyName name, HandleObject scopeObj,
MutableHandleValue res);
bool
ImplicitThisOperation(JSContext *cx, HandleObject scopeObj, HandlePropertyName name,
MutableHandleValue res);
bool
IteratorMore(JSContext *cx, JSObject *iterobj, bool *cond, MutableHandleValue rval);
bool
IteratorNext(JSContext *cx, HandleObject iterobj, MutableHandleValue rval);
bool
RunOnceScriptPrologue(JSContext *cx, HandleScript script);
bool
InitGetterSetterOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, HandleId id,
HandleValue val);
bool
InitGetterSetterOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, HandlePropertyName name,
HandleValue val);
bool
InitGetterSetterOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, HandleValue idval,
HandleValue val);
} /* namespace js */
#endif /* vm_Interpreter_h */