diff options
Diffstat (limited to 'js/src/vm/Interpreter.h')
-rw-r--r-- | js/src/vm/Interpreter.h | 582 |
1 files changed, 582 insertions, 0 deletions
diff --git a/js/src/vm/Interpreter.h b/js/src/vm/Interpreter.h new file mode 100644 index 000000000..1ffe1fdca --- /dev/null +++ b/js/src/vm/Interpreter.h @@ -0,0 +1,582 @@ +/* -*- 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 "jspubtd.h" + +#include "frontend/ParseNode.h" + +#include "vm/Stack.h" + +namespace js { + +class EnvironmentIter; + +/* + * Convert null/undefined |thisv| into the current global object for the + * compartment, and replace other primitives with boxed versions. + */ +extern bool +BoxNonStrictThis(JSContext* cx, HandleValue thisv, MutableHandleValue vp); + +extern bool +GetFunctionThis(JSContext* cx, AbstractFramePtr frame, MutableHandleValue res); + +extern bool +GetNonSyntacticGlobalThis(JSContext* cx, HandleObject envChain, MutableHandleValue res); + +/* + * 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, HandleValue v, int numToSkip, + MaybeConstruct construct = NO_CONSTRUCT); + +/* See ReportIsNotFunction comment for the meaning of numToSkip. */ +extern JSObject* +ValueToCallable(JSContext* cx, HandleValue v, int numToSkip = -1, + MaybeConstruct construct = NO_CONSTRUCT); + +/* + * Call or construct arguments that are stored in rooted memory. + * + * NOTE: Any necessary |GetThisValue| computation must have been performed on + * |args.thisv()|, likely by the interpreter when pushing |this| onto the + * stack. If you're not sure whether |GetThisValue| processing has been + * performed, use |Invoke|. + */ +extern bool +InternalCallOrConstruct(JSContext* cx, const CallArgs& args, + MaybeConstruct construct); + +/* + * These helpers take care of the infinite-recursion check necessary for + * getter/setter calls. + */ +extern bool +CallGetter(JSContext* cx, HandleValue thisv, HandleValue getter, MutableHandleValue rval); + +extern bool +CallSetter(JSContext* cx, HandleValue thisv, HandleValue setter, HandleValue rval); + +// ES7 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 7.3.12 Call(F, V, argumentsList). +// All parameters are required, hopefully forcing callers to be careful not to +// (say) blindly pass callee as |newTarget| when a different value should have +// been passed. Behavior is unspecified if any element of |args| isn't initialized. +// +// |rval| is written to *only* after |fval| and |thisv| have been consumed, so +// |rval| *may* alias either argument. +extern bool +Call(JSContext* cx, HandleValue fval, HandleValue thisv, const AnyInvokeArgs& args, + MutableHandleValue rval); + +inline bool +Call(JSContext* cx, HandleValue fval, HandleValue thisv, MutableHandleValue rval) +{ + FixedInvokeArgs<0> args(cx); + return Call(cx, fval, thisv, args, rval); +} + +inline bool +Call(JSContext* cx, HandleValue fval, JSObject* thisObj, MutableHandleValue rval) +{ + RootedValue thisv(cx, ObjectOrNullValue(thisObj)); + FixedInvokeArgs<0> args(cx); + return Call(cx, fval, thisv, args, rval); +} + +inline bool +Call(JSContext* cx, HandleValue fval, HandleValue thisv, HandleValue arg0, MutableHandleValue rval) +{ + FixedInvokeArgs<1> args(cx); + args[0].set(arg0); + return Call(cx, fval, thisv, args, rval); +} + +inline bool +Call(JSContext* cx, HandleValue fval, JSObject* thisObj, HandleValue arg0, + MutableHandleValue rval) +{ + RootedValue thisv(cx, ObjectOrNullValue(thisObj)); + FixedInvokeArgs<1> args(cx); + args[0].set(arg0); + return Call(cx, fval, thisv, args, rval); +} + +inline bool +Call(JSContext* cx, HandleValue fval, HandleValue thisv, + HandleValue arg0, HandleValue arg1, MutableHandleValue rval) +{ + FixedInvokeArgs<2> args(cx); + args[0].set(arg0); + args[1].set(arg1); + return Call(cx, fval, thisv, args, rval); +} + +inline bool +Call(JSContext* cx, HandleValue fval, JSObject* thisObj, + HandleValue arg0, HandleValue arg1, MutableHandleValue rval) +{ + RootedValue thisv(cx, ObjectOrNullValue(thisObj)); + FixedInvokeArgs<2> args(cx); + args[0].set(arg0); + args[1].set(arg1); + return Call(cx, fval, thisv, args, rval); +} + +// Perform the above Call() operation using the given arguments. Similar to +// ConstructFromStack() below, this handles |!IsCallable(args.calleev())|. +// +// This internal operation is intended only for use with arguments known to be +// on the JS stack, or at least in carefully-rooted memory. The vast majority of +// potential users should instead use InvokeArgs in concert with Call(). +extern bool +CallFromStack(JSContext* cx, const CallArgs& args); + +// ES6 7.3.13 Construct(F, argumentsList, newTarget). All parameters are +// required, hopefully forcing callers to be careful not to (say) blindly pass +// callee as |newTarget| when a different value should have been passed. +// Behavior is unspecified if any element of |args| isn't initialized. +// +// |rval| is written to *only* after |fval| and |newTarget| have been consumed, +// so |rval| *may* alias either argument. +// +// NOTE: As with the ES6 spec operation, it's the caller's responsibility to +// ensure |fval| and |newTarget| are both |IsConstructor|. +extern bool +Construct(JSContext* cx, HandleValue fval, const AnyConstructArgs& args, HandleValue newTarget, + MutableHandleObject objp); + +// Check that in the given |args|, which must be |args.isConstructing()|, that +// |IsConstructor(args.callee())|. If this is not the case, throw a TypeError. +// Otherwise, the user must ensure that, additionally, |IsConstructor(args.newTarget())|. +// (If |args| comes directly from the interpreter stack, as set up by JSOP_NEW, +// this comes for free.) Then perform a Construct() operation using |args|. +// +// This internal operation is intended only for use with arguments known to be +// on the JS stack, or at least in carefully-rooted memory. The vast majority of +// potential users should instead use ConstructArgs in concert with Construct(). +extern bool +ConstructFromStack(JSContext* cx, const CallArgs& args); + +// Call Construct(fval, args, newTarget), but use the given |thisv| as |this| +// during construction of |fval|. +// +// |rval| is written to *only* after |fval|, |thisv|, and |newTarget| have been +// consumed, so |rval| *may* alias any of these arguments. +// +// This method exists only for very rare cases where a |this| was created +// caller-side for construction of |fval|: basically only for JITs using +// |CreateThis|. If that's not you, use Construct()! +extern bool +InternalConstructWithProvidedThis(JSContext* cx, HandleValue fval, HandleValue thisv, + const AnyConstructArgs& args, HandleValue newTarget, + MutableHandleValue 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& newTargetVal, 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; + +// RunState is passed to RunScript and RunScript then either 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 }; + 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; } + + ExecuteState* asExecute() const { + MOZ_ASSERT(isExecute()); + return (ExecuteState*)this; + } + InvokeState* asInvoke() const { + MOZ_ASSERT(isInvoke()); + return (InvokeState*)this; + } + + JS::HandleScript script() const { return script_; } + + virtual InterpreterFrame* pushInterpreterFrame(JSContext* cx) = 0; + virtual void setReturnValue(const Value& v) = 0; + + bool maybeCreateThisForConstructor(JSContext* cx); + + private: + RunState(const RunState& other) = delete; + RunState(const ExecuteState& other) = delete; + RunState(const InvokeState& other) = delete; + void operator=(const RunState& other) = delete; +}; + +// Eval or global script. +class ExecuteState : public RunState +{ + RootedValue newTargetValue_; + RootedObject envChain_; + + AbstractFramePtr evalInFrame_; + Value* result_; + + public: + ExecuteState(JSContext* cx, JSScript* script, const Value& newTargetValue, + JSObject& envChain, AbstractFramePtr evalInFrame, Value* result) + : RunState(cx, Execute, script), + newTargetValue_(cx, newTargetValue), + envChain_(cx, &envChain), + evalInFrame_(evalInFrame), + result_(result) + { } + + Value newTarget() { return newTargetValue_; } + JSObject* environmentChain() const { return envChain_; } + bool isDebuggerEval() const { return !!evalInFrame_; } + + virtual InterpreterFrame* pushInterpreterFrame(JSContext* cx); + + virtual void setReturnValue(const Value& v) { + if (result_) + *result_ = v; + } +}; + +// Data to invoke a function. +class InvokeState final : public RunState +{ + const CallArgs& args_; + MaybeConstruct construct_; + bool createSingleton_; + + public: + InvokeState(JSContext* cx, const CallArgs& args, MaybeConstruct construct) + : RunState(cx, Invoke, args.callee().as<JSFunction>().nonLazyScript()), + args_(args), + construct_(construct), + createSingleton_(false) + { } + + bool createSingleton() const { return createSingleton_; } + void setCreateSingleton() { createSingleton_ = true; } + + bool constructing() const { return construct_; } + const CallArgs& args() const { return args_; } + + virtual InterpreterFrame* pushInterpreterFrame(JSContext* cx); + + virtual void setReturnValue(const Value& v) { + args_.rval().set(v); + } +}; + +extern bool +RunScript(JSContext* cx, RunState& state); + +extern bool +StrictlyEqual(JSContext* cx, HandleValue lval, HandleValue rval, bool* equal); + +extern bool +LooselyEqual(JSContext* cx, HandleValue lval, HandleValue rval, bool* equal); + +/* === except that NaN is the same as NaN and -0 is not the same as +0. */ +extern bool +SameValue(JSContext* cx, HandleValue v1, HandleValue v2, bool* same); + +extern JSType +TypeOfObject(JSObject* obj); + +extern JSType +TypeOfValue(const Value& v); + +extern bool +InstanceOfOperator(JSContext* cx, HandleObject obj, HandleValue v, bool* bp); + +extern bool +HasInstance(JSContext* cx, HandleObject obj, HandleValue v, bool* bp); + +// Unwind environment chain and iterator to match the scope corresponding to +// the given bytecode position. +extern void +UnwindEnvironment(JSContext* cx, EnvironmentIter& ei, jsbytecode* pc); + +// Unwind all environments. +extern void +UnwindAllEnvironmentsInFrame(JSContext* cx, EnvironmentIter& ei); + +// Compute the pc needed to unwind the scope to the beginning of the block +// pointed to by the try note. +extern jsbytecode* +UnwindEnvironmentToTryPc(JSScript* script, JSTryNote* tn); + +template <class StackDepthOp> +class MOZ_STACK_CLASS TryNoteIter +{ + RootedScript script_; + uint32_t pcOffset_; + JSTryNote* tn_; + JSTryNote* tnEnd_; + StackDepthOp getStackDepth_; + + void settle() { + for (; tn_ != tnEnd_; ++tn_) { + /* If pc is out of range, try the next one. */ + if (pcOffset_ - tn_->start >= tn_->length) + continue; + + /* + * We have a note that covers the exception pc but we must check + * whether the interpreter has already executed the corresponding + * handler. This is possible when the executed bytecode implements + * break or return from inside a for-in loop. + * + * In this case the emitter generates additional [enditer] and [gosub] + * opcodes to close all outstanding iterators and execute the finally + * blocks. If such an [enditer] throws an exception, its pc can still + * be inside several nested for-in loops and try-finally statements + * even if we have already closed the corresponding iterators and + * invoked the finally blocks. + * + * To address this, we make [enditer] always decrease the stack even + * when its implementation throws an exception. Thus already executed + * [enditer] and [gosub] opcodes will have try notes with the stack + * depth exceeding the current one and this condition is what we use to + * filter them out. + */ + if (tn_->stackDepth <= getStackDepth_()) + break; + } + } + + public: + TryNoteIter(JSContext* cx, JSScript* script, jsbytecode* pc, + StackDepthOp getStackDepth) + : script_(cx, script), + pcOffset_(pc - script->main()), + getStackDepth_(getStackDepth) + { + if (script->hasTrynotes()) { + tn_ = script->trynotes()->vector; + tnEnd_ = tn_ + script->trynotes()->length; + } else { + tn_ = tnEnd_ = nullptr; + } + settle(); + } + + void operator++() { + ++tn_; + settle(); + } + + bool done() const { return tn_ == tnEnd_; } + JSTryNote* operator*() const { return tn_; } +}; + +bool +HandleClosingGeneratorReturn(JSContext* cx, AbstractFramePtr frame, bool ok); + +/************************************************************************/ + +bool +Throw(JSContext* cx, HandleValue v); + +bool +ThrowingOperation(JSContext* cx, HandleValue v); + +bool +GetProperty(JSContext* cx, HandleValue value, HandlePropertyName name, MutableHandleValue vp); + +bool +GetEnvironmentName(JSContext* cx, HandleObject obj, HandlePropertyName name, + MutableHandleValue vp); + +bool +GetEnvironmentNameForTypeOf(JSContext* cx, HandleObject obj, HandlePropertyName name, + MutableHandleValue vp); + +JSObject* +Lambda(JSContext* cx, HandleFunction fun, HandleObject parent); + +JSObject* +LambdaArrow(JSContext* cx, HandleFunction fun, HandleObject parent, HandleValue newTargetv); + +bool +GetElement(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, + bool strict); +bool +SetObjectElement(JSContext* cx, HandleObject obj, HandleValue index, HandleValue value, + bool strict, HandleScript script, jsbytecode* pc); + +bool +SetObjectElement(JSContext* cx, HandleObject obj, HandleValue index, HandleValue value, + HandleValue receiver, bool strict, HandleScript script, jsbytecode* pc); + +bool +InitElementArray(JSContext* cx, jsbytecode* pc, + HandleObject obj, uint32_t index, HandleValue value); + +bool +AddValues(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res); + +bool +SubValues(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res); + +bool +MulValues(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res); + +bool +DivValues(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res); + +bool +ModValues(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res); + +bool +UrshValues(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res); + +bool +AtomicIsLockFree(JSContext* cx, HandleValue in, int* out); + +template <bool strict> +bool +DeletePropertyJit(JSContext* ctx, HandleValue val, HandlePropertyName name, bool* bv); + +template <bool strict> +bool +DeleteElementJit(JSContext* cx, HandleValue val, HandleValue index, bool* bv); + +bool +DefFunOperation(JSContext* cx, HandleScript script, HandleObject envChain, HandleFunction funArg); + +bool +ThrowMsgOperation(JSContext* cx, const unsigned errorNum); + +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 +RunOnceScriptPrologue(JSContext* cx, HandleScript script); + +bool +InitGetterSetterOperation(JSContext* cx, jsbytecode* pc, HandleObject obj, HandleId id, + HandleObject val); + +bool +InitGetterSetterOperation(JSContext* cx, jsbytecode* pc, HandleObject obj, HandlePropertyName name, + HandleObject val); + +unsigned +GetInitDataPropAttrs(JSOp op); + +bool +EnterWithOperation(JSContext* cx, AbstractFramePtr frame, HandleValue val, + Handle<WithScope*> scope); + + +bool +InitGetterSetterOperation(JSContext* cx, jsbytecode* pc, HandleObject obj, HandleValue idval, + HandleObject val); + +bool +SpreadCallOperation(JSContext* cx, HandleScript script, jsbytecode* pc, HandleValue thisv, + HandleValue callee, HandleValue arr, HandleValue newTarget, MutableHandleValue res); + +bool +OptimizeSpreadCall(JSContext* cx, HandleValue arg, bool* optimized); + +JSObject* +NewObjectOperation(JSContext* cx, HandleScript script, jsbytecode* pc, + NewObjectKind newKind = GenericObject); + +JSObject* +NewObjectOperationWithTemplate(JSContext* cx, HandleObject templateObject); + +JSObject* +NewArrayOperation(JSContext* cx, HandleScript script, jsbytecode* pc, uint32_t length, + NewObjectKind newKind = GenericObject); + +JSObject* +NewArrayOperationWithTemplate(JSContext* cx, HandleObject templateObject); + +void +ReportRuntimeLexicalError(JSContext* cx, unsigned errorNumber, HandleId id); + +void +ReportRuntimeLexicalError(JSContext* cx, unsigned errorNumber, HandlePropertyName name); + +void +ReportRuntimeLexicalError(JSContext* cx, unsigned errorNumber, HandleScript script, jsbytecode* pc); + +// The parser only reports redeclarations that occurs within a single +// script. Due to the extensibility of the global lexical scope, we also check +// for redeclarations during runtime in JSOP_DEF{VAR,LET,CONST}. +void +ReportRuntimeRedeclaration(JSContext* cx, HandlePropertyName name, const char* redeclKind); + +enum class CheckIsObjectKind : uint8_t { + IteratorNext, + GetIterator +}; + +bool +ThrowCheckIsObject(JSContext* cx, CheckIsObjectKind kind); + +bool +ThrowUninitializedThis(JSContext* cx, AbstractFramePtr frame); + +bool +DefaultClassConstructor(JSContext* cx, unsigned argc, Value* vp); + +bool +Debug_CheckSelfHosted(JSContext* cx, HandleValue v); + +} /* namespace js */ + +#endif /* vm_Interpreter_h */ |