michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef vm_Interpreter_h michael@0: #define vm_Interpreter_h michael@0: michael@0: /* michael@0: * JS interpreter interface. michael@0: */ michael@0: michael@0: #include "jsiter.h" michael@0: #include "jspubtd.h" michael@0: michael@0: #include "vm/Stack.h" michael@0: michael@0: namespace js { michael@0: michael@0: class ScopeIter; michael@0: michael@0: /* michael@0: * Announce to the debugger that the thread has entered a new JavaScript frame, michael@0: * |frame|. Call whatever hooks have been registered to observe new frames, and michael@0: * return a JSTrapStatus code indication how execution should proceed: michael@0: * michael@0: * - JSTRAP_CONTINUE: Continue execution normally. michael@0: * michael@0: * - JSTRAP_THROW: Throw an exception. ScriptDebugPrologue has set |cx|'s michael@0: * pending exception to the value to be thrown. michael@0: * michael@0: * - JSTRAP_ERROR: Terminate execution (as is done when a script is terminated michael@0: * for running too long). ScriptDebugPrologue has cleared |cx|'s pending michael@0: * exception. michael@0: * michael@0: * - JSTRAP_RETURN: Return from the new frame immediately. ScriptDebugPrologue michael@0: * has set |frame|'s return value appropriately. michael@0: */ michael@0: extern JSTrapStatus michael@0: ScriptDebugPrologue(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc); michael@0: michael@0: /* michael@0: * Announce to the debugger that the thread has exited a JavaScript frame, |frame|. michael@0: * If |ok| is true, the frame is returning normally; if |ok| is false, the frame michael@0: * is throwing an exception or terminating. michael@0: * michael@0: * Call whatever hooks have been registered to observe frame exits. Change cx's michael@0: * current exception and |frame|'s return value to reflect the changes in behavior michael@0: * the hooks request, if any. Return the new error/success value. michael@0: * michael@0: * This function may be called twice for the same outgoing frame; only the michael@0: * first call has any effect. (Permitting double calls simplifies some michael@0: * cases where an onPop handler's resumption value changes a return to a michael@0: * throw, or vice versa: we can redirect to a complete copy of the michael@0: * alternative path, containing its own call to ScriptDebugEpilogue.) michael@0: */ michael@0: extern bool michael@0: ScriptDebugEpilogue(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc, bool ok); michael@0: michael@0: /* michael@0: * Announce to the debugger that an exception has been thrown and propagated michael@0: * to |frame|. Call whatever hooks have been registered to observe this and michael@0: * return a JSTrapStatus code indication how execution should proceed: michael@0: * michael@0: * - JSTRAP_CONTINUE: Continue throwing the current exception. michael@0: * michael@0: * - JSTRAP_THROW: Throw another value. DebugExceptionUnwind has set |cx|'s michael@0: * pending exception to the new value. michael@0: * michael@0: * - JSTRAP_ERROR: Terminate execution. DebugExceptionUnwind has cleared |cx|'s michael@0: * pending exception. michael@0: * michael@0: * - JSTRAP_RETURN: Return from |frame|. DebugExceptionUnwind has cleared michael@0: * |cx|'s pending exception and set |frame|'s return value. michael@0: */ michael@0: extern JSTrapStatus michael@0: DebugExceptionUnwind(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc); michael@0: michael@0: /* michael@0: * For a given |call|, convert null/undefined |this| into the global object for michael@0: * the callee and replace other primitives with boxed versions. This assumes michael@0: * that call.callee() is not strict mode code. This is the special/slow case of michael@0: * ComputeThis. michael@0: */ michael@0: extern bool michael@0: BoxNonStrictThis(JSContext *cx, const CallReceiver &call); michael@0: michael@0: extern JSObject * michael@0: BoxNonStrictThis(JSContext *cx, HandleValue thisv); michael@0: michael@0: /* michael@0: * Ensure that fp->thisValue() is the correct value of |this| for the scripted michael@0: * call represented by |fp|. ComputeThis is necessary because fp->thisValue() michael@0: * may be set to 'undefined' when 'this' should really be the global object (as michael@0: * an optimization to avoid global-this computation). michael@0: */ michael@0: inline bool michael@0: ComputeThis(JSContext *cx, AbstractFramePtr frame); michael@0: michael@0: enum MaybeConstruct { michael@0: NO_CONSTRUCT = INITIAL_NONE, michael@0: CONSTRUCT = INITIAL_CONSTRUCT michael@0: }; michael@0: michael@0: /* michael@0: * numToSkip is the number of stack values the expression decompiler should skip michael@0: * before it reaches |v|. If it's -1, the decompiler will search the stack. michael@0: */ michael@0: extern bool michael@0: ReportIsNotFunction(JSContext *cx, HandleValue v, int numToSkip = -1, michael@0: MaybeConstruct construct = NO_CONSTRUCT); michael@0: michael@0: /* See ReportIsNotFunction comment for the meaning of numToSkip. */ michael@0: extern JSObject * michael@0: ValueToCallable(JSContext *cx, HandleValue v, int numToSkip = -1, michael@0: MaybeConstruct construct = NO_CONSTRUCT); michael@0: michael@0: /* michael@0: * Invoke assumes that the given args have been pushed on the top of the michael@0: * VM stack. michael@0: */ michael@0: extern bool michael@0: Invoke(JSContext *cx, CallArgs args, MaybeConstruct construct = NO_CONSTRUCT); michael@0: michael@0: /* michael@0: * This Invoke overload places the least requirements on the caller: it may be michael@0: * called at any time and it takes care of copying the given callee, this, and michael@0: * arguments onto the stack. michael@0: */ michael@0: extern bool michael@0: Invoke(JSContext *cx, const Value &thisv, const Value &fval, unsigned argc, const Value *argv, michael@0: MutableHandleValue rval); michael@0: michael@0: /* michael@0: * This helper takes care of the infinite-recursion check necessary for michael@0: * getter/setter calls. michael@0: */ michael@0: extern bool michael@0: InvokeGetterOrSetter(JSContext *cx, JSObject *obj, Value fval, unsigned argc, Value *argv, michael@0: MutableHandleValue rval); michael@0: michael@0: /* michael@0: * InvokeConstructor implement a function call from a constructor context michael@0: * (e.g. 'new') handling the the creation of the new 'this' object. michael@0: */ michael@0: extern bool michael@0: InvokeConstructor(JSContext *cx, CallArgs args); michael@0: michael@0: /* See the fval overload of Invoke. */ michael@0: extern bool michael@0: InvokeConstructor(JSContext *cx, Value fval, unsigned argc, Value *argv, Value *rval); michael@0: michael@0: /* michael@0: * Executes a script with the given scopeChain/this. The 'type' indicates michael@0: * whether this is eval code or global code. To support debugging, the michael@0: * evalFrame parameter can point to an arbitrary frame in the context's call michael@0: * stack to simulate executing an eval in that frame. michael@0: */ michael@0: extern bool michael@0: ExecuteKernel(JSContext *cx, HandleScript script, JSObject &scopeChain, const Value &thisv, michael@0: ExecuteType type, AbstractFramePtr evalInFrame, Value *result); michael@0: michael@0: /* Execute a script with the given scopeChain as global code. */ michael@0: extern bool michael@0: Execute(JSContext *cx, HandleScript script, JSObject &scopeChain, Value *rval); michael@0: michael@0: class ExecuteState; michael@0: class InvokeState; michael@0: class GeneratorState; michael@0: michael@0: // RunState is passed to RunScript and RunScript then eiter passes it to the michael@0: // interpreter or to the JITs. RunState contains all information we need to michael@0: // construct an interpreter or JIT frame. michael@0: class RunState michael@0: { michael@0: protected: michael@0: enum Kind { Execute, Invoke, Generator }; michael@0: Kind kind_; michael@0: michael@0: RootedScript script_; michael@0: michael@0: explicit RunState(JSContext *cx, Kind kind, JSScript *script) michael@0: : kind_(kind), michael@0: script_(cx, script) michael@0: { } michael@0: michael@0: public: michael@0: bool isExecute() const { return kind_ == Execute; } michael@0: bool isInvoke() const { return kind_ == Invoke; } michael@0: bool isGenerator() const { return kind_ == Generator; } michael@0: michael@0: ExecuteState *asExecute() const { michael@0: JS_ASSERT(isExecute()); michael@0: return (ExecuteState *)this; michael@0: } michael@0: InvokeState *asInvoke() const { michael@0: JS_ASSERT(isInvoke()); michael@0: return (InvokeState *)this; michael@0: } michael@0: GeneratorState *asGenerator() const { michael@0: JS_ASSERT(isGenerator()); michael@0: return (GeneratorState *)this; michael@0: } michael@0: michael@0: JSScript *script() const { return script_; } michael@0: michael@0: virtual InterpreterFrame *pushInterpreterFrame(JSContext *cx) = 0; michael@0: virtual void setReturnValue(Value v) = 0; michael@0: michael@0: private: michael@0: RunState(const RunState &other) MOZ_DELETE; michael@0: RunState(const ExecuteState &other) MOZ_DELETE; michael@0: RunState(const InvokeState &other) MOZ_DELETE; michael@0: RunState(const GeneratorState &other) MOZ_DELETE; michael@0: void operator=(const RunState &other) MOZ_DELETE; michael@0: }; michael@0: michael@0: // Eval or global script. michael@0: class ExecuteState : public RunState michael@0: { michael@0: ExecuteType type_; michael@0: michael@0: RootedValue thisv_; michael@0: RootedObject scopeChain_; michael@0: michael@0: AbstractFramePtr evalInFrame_; michael@0: Value *result_; michael@0: michael@0: public: michael@0: ExecuteState(JSContext *cx, JSScript *script, const Value &thisv, JSObject &scopeChain, michael@0: ExecuteType type, AbstractFramePtr evalInFrame, Value *result) michael@0: : RunState(cx, Execute, script), michael@0: type_(type), michael@0: thisv_(cx, thisv), michael@0: scopeChain_(cx, &scopeChain), michael@0: evalInFrame_(evalInFrame), michael@0: result_(result) michael@0: { } michael@0: michael@0: Value *addressOfThisv() { return thisv_.address(); } michael@0: JSObject *scopeChain() const { return scopeChain_; } michael@0: ExecuteType type() const { return type_; } michael@0: michael@0: virtual InterpreterFrame *pushInterpreterFrame(JSContext *cx); michael@0: michael@0: virtual void setReturnValue(Value v) { michael@0: if (result_) michael@0: *result_ = v; michael@0: } michael@0: }; michael@0: michael@0: // Data to invoke a function. michael@0: class InvokeState : public RunState michael@0: { michael@0: CallArgs &args_; michael@0: InitialFrameFlags initial_; michael@0: bool useNewType_; michael@0: michael@0: public: michael@0: InvokeState(JSContext *cx, CallArgs &args, InitialFrameFlags initial) michael@0: : RunState(cx, Invoke, args.callee().as().nonLazyScript()), michael@0: args_(args), michael@0: initial_(initial), michael@0: useNewType_(false) michael@0: { } michael@0: michael@0: bool useNewType() const { return useNewType_; } michael@0: void setUseNewType() { useNewType_ = true; } michael@0: michael@0: bool constructing() const { return InitialFrameFlagsAreConstructing(initial_); } michael@0: CallArgs &args() const { return args_; } michael@0: michael@0: virtual InterpreterFrame *pushInterpreterFrame(JSContext *cx); michael@0: michael@0: virtual void setReturnValue(Value v) { michael@0: args_.rval().set(v); michael@0: } michael@0: }; michael@0: michael@0: // Generator script. michael@0: class GeneratorState : public RunState michael@0: { michael@0: JSContext *cx_; michael@0: JSGenerator *gen_; michael@0: JSGeneratorState futureState_; michael@0: bool entered_; michael@0: michael@0: public: michael@0: GeneratorState(JSContext *cx, JSGenerator *gen, JSGeneratorState futureState); michael@0: ~GeneratorState(); michael@0: michael@0: virtual InterpreterFrame *pushInterpreterFrame(JSContext *cx); michael@0: virtual void setReturnValue(Value) { } michael@0: michael@0: JSGenerator *gen() const { return gen_; } michael@0: }; michael@0: michael@0: extern bool michael@0: RunScript(JSContext *cx, RunState &state); michael@0: michael@0: extern bool michael@0: StrictlyEqual(JSContext *cx, const Value &lval, const Value &rval, bool *equal); michael@0: michael@0: extern bool michael@0: LooselyEqual(JSContext *cx, const Value &lval, const Value &rval, bool *equal); michael@0: michael@0: /* === except that NaN is the same as NaN and -0 is not the same as +0. */ michael@0: extern bool michael@0: SameValue(JSContext *cx, const Value &v1, const Value &v2, bool *same); michael@0: michael@0: extern JSType michael@0: TypeOfObject(JSObject *obj); michael@0: michael@0: extern JSType michael@0: TypeOfValue(const Value &v); michael@0: michael@0: extern bool michael@0: HasInstance(JSContext *cx, HandleObject obj, HandleValue v, bool *bp); michael@0: michael@0: // Unwind scope chain and iterator to match the static scope corresponding to michael@0: // the given bytecode position. michael@0: extern void michael@0: UnwindScope(JSContext *cx, ScopeIter &si, jsbytecode *pc); michael@0: michael@0: /* michael@0: * Unwind for an uncatchable exception. This means not running finalizers, etc; michael@0: * just preserving the basic engine stack invariants. michael@0: */ michael@0: extern void michael@0: UnwindForUncatchableException(JSContext *cx, const InterpreterRegs ®s); michael@0: michael@0: extern bool michael@0: OnUnknownMethod(JSContext *cx, HandleObject obj, Value idval, MutableHandleValue vp); michael@0: michael@0: class TryNoteIter michael@0: { michael@0: const InterpreterRegs ®s; michael@0: RootedScript script; /* TryNotIter is always stack allocated. */ michael@0: uint32_t pcOffset; michael@0: JSTryNote *tn, *tnEnd; michael@0: michael@0: void settle(); michael@0: michael@0: public: michael@0: explicit TryNoteIter(JSContext *cx, const InterpreterRegs ®s); michael@0: bool done() const; michael@0: void operator++(); michael@0: JSTryNote *operator*() const { return tn; } michael@0: }; michael@0: michael@0: /************************************************************************/ michael@0: michael@0: bool michael@0: Throw(JSContext *cx, HandleValue v); michael@0: michael@0: bool michael@0: GetProperty(JSContext *cx, HandleValue value, HandlePropertyName name, MutableHandleValue vp); michael@0: michael@0: bool michael@0: CallProperty(JSContext *cx, HandleValue value, HandlePropertyName name, MutableHandleValue vp); michael@0: michael@0: bool michael@0: GetScopeName(JSContext *cx, HandleObject obj, HandlePropertyName name, MutableHandleValue vp); michael@0: michael@0: bool michael@0: GetScopeNameForTypeOf(JSContext *cx, HandleObject obj, HandlePropertyName name, michael@0: MutableHandleValue vp); michael@0: michael@0: JSObject * michael@0: Lambda(JSContext *cx, HandleFunction fun, HandleObject parent); michael@0: michael@0: JSObject * michael@0: LambdaArrow(JSContext *cx, HandleFunction fun, HandleObject parent, HandleValue thisv); michael@0: michael@0: bool michael@0: GetElement(JSContext *cx, MutableHandleValue lref, HandleValue rref, MutableHandleValue res); michael@0: michael@0: bool michael@0: CallElement(JSContext *cx, MutableHandleValue lref, HandleValue rref, MutableHandleValue res); michael@0: michael@0: bool michael@0: SetObjectElement(JSContext *cx, HandleObject obj, HandleValue index, HandleValue value, michael@0: bool strict); michael@0: bool michael@0: SetObjectElement(JSContext *cx, HandleObject obj, HandleValue index, HandleValue value, michael@0: bool strict, HandleScript script, jsbytecode *pc); michael@0: michael@0: bool michael@0: InitElementArray(JSContext *cx, jsbytecode *pc, michael@0: HandleObject obj, uint32_t index, HandleValue value); michael@0: michael@0: bool michael@0: AddValues(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res); michael@0: michael@0: bool michael@0: SubValues(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res); michael@0: michael@0: bool michael@0: MulValues(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res); michael@0: michael@0: bool michael@0: DivValues(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res); michael@0: michael@0: bool michael@0: ModValues(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res); michael@0: michael@0: bool michael@0: UrshValues(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res); michael@0: michael@0: template michael@0: bool michael@0: SetProperty(JSContext *cx, HandleObject obj, HandleId id, const Value &value); michael@0: michael@0: template michael@0: bool michael@0: DeleteProperty(JSContext *ctx, HandleValue val, HandlePropertyName name, bool *bv); michael@0: michael@0: template michael@0: bool michael@0: DeleteElement(JSContext *cx, HandleValue val, HandleValue index, bool *bv); michael@0: michael@0: bool michael@0: DefFunOperation(JSContext *cx, HandleScript script, HandleObject scopeChain, HandleFunction funArg); michael@0: michael@0: bool michael@0: SetCallOperation(JSContext *cx); michael@0: michael@0: bool michael@0: GetAndClearException(JSContext *cx, MutableHandleValue res); michael@0: michael@0: bool michael@0: DeleteNameOperation(JSContext *cx, HandlePropertyName name, HandleObject scopeObj, michael@0: MutableHandleValue res); michael@0: michael@0: bool michael@0: ImplicitThisOperation(JSContext *cx, HandleObject scopeObj, HandlePropertyName name, michael@0: MutableHandleValue res); michael@0: michael@0: bool michael@0: IteratorMore(JSContext *cx, JSObject *iterobj, bool *cond, MutableHandleValue rval); michael@0: michael@0: bool michael@0: IteratorNext(JSContext *cx, HandleObject iterobj, MutableHandleValue rval); michael@0: michael@0: bool michael@0: RunOnceScriptPrologue(JSContext *cx, HandleScript script); michael@0: michael@0: bool michael@0: InitGetterSetterOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, HandleId id, michael@0: HandleObject val); michael@0: michael@0: bool michael@0: InitGetterSetterOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, HandlePropertyName name, michael@0: HandleObject val); michael@0: michael@0: bool michael@0: EnterWithOperation(JSContext *cx, AbstractFramePtr frame, HandleValue val, HandleObject staticWith); michael@0: michael@0: michael@0: bool michael@0: InitGetterSetterOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, HandleValue idval, michael@0: HandleObject val); michael@0: michael@0: inline bool michael@0: SetConstOperation(JSContext *cx, HandleObject varobj, HandlePropertyName name, HandleValue rval) michael@0: { michael@0: return JSObject::defineProperty(cx, varobj, name, rval, michael@0: JS_PropertyStub, JS_StrictPropertyStub, michael@0: JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY); michael@0: } michael@0: michael@0: } /* namespace js */ michael@0: michael@0: #endif /* vm_Interpreter_h */