summaryrefslogtreecommitdiff
path: root/js/src/vm/Interpreter-inl.h
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /js/src/vm/Interpreter-inl.h
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloaduxp-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
Add m-esr52 at 52.6.0
Diffstat (limited to 'js/src/vm/Interpreter-inl.h')
-rw-r--r--js/src/vm/Interpreter-inl.h880
1 files changed, 880 insertions, 0 deletions
diff --git a/js/src/vm/Interpreter-inl.h b/js/src/vm/Interpreter-inl.h
new file mode 100644
index 0000000000..5f476c4ffc
--- /dev/null
+++ b/js/src/vm/Interpreter-inl.h
@@ -0,0 +1,880 @@
+/* -*- 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_inl_h
+#define vm_Interpreter_inl_h
+
+#include "vm/Interpreter.h"
+
+#include "jscompartment.h"
+#include "jsnum.h"
+#include "jsstr.h"
+
+#include "jit/Ion.h"
+#include "vm/ArgumentsObject.h"
+
+#include "jsatominlines.h"
+#include "jsobjinlines.h"
+
+#include "vm/EnvironmentObject-inl.h"
+#include "vm/Stack-inl.h"
+#include "vm/String-inl.h"
+#include "vm/UnboxedObject-inl.h"
+
+namespace js {
+
+/*
+ * Every possible consumer of MagicValue(JS_OPTIMIZED_ARGUMENTS) (as determined
+ * by ScriptAnalysis::needsArgsObj) must check for these magic values and, when
+ * one is received, act as if the value were the function's ArgumentsObject.
+ * Additionally, it is possible that, after 'arguments' was copied into a
+ * temporary, the arguments object has been created a some other failed guard
+ * that called JSScript::argumentsOptimizationFailed. In this case, it is
+ * always valid (and necessary) to replace JS_OPTIMIZED_ARGUMENTS with the real
+ * arguments object.
+ */
+static inline bool
+IsOptimizedArguments(AbstractFramePtr frame, MutableHandleValue vp)
+{
+ if (vp.isMagic(JS_OPTIMIZED_ARGUMENTS) && frame.script()->needsArgsObj())
+ vp.setObject(frame.argsObj());
+ return vp.isMagic(JS_OPTIMIZED_ARGUMENTS);
+}
+
+/*
+ * One optimized consumer of MagicValue(JS_OPTIMIZED_ARGUMENTS) is f.apply.
+ * However, this speculation must be guarded before calling 'apply' in case it
+ * is not the builtin Function.prototype.apply.
+ */
+static inline bool
+GuardFunApplyArgumentsOptimization(JSContext* cx, AbstractFramePtr frame, CallArgs& args)
+{
+ if (args.length() == 2 && IsOptimizedArguments(frame, args[1])) {
+ if (!IsNativeFunction(args.calleev(), js::fun_apply)) {
+ RootedScript script(cx, frame.script());
+ if (!JSScript::argumentsOptimizationFailed(cx, script))
+ return false;
+ args[1].setObject(frame.argsObj());
+ }
+ }
+
+ return true;
+}
+
+/*
+ * Per ES6, lexical declarations may not be accessed in any fashion until they
+ * are initialized (i.e., until the actual declaring statement is
+ * executed). The various LEXICAL opcodes need to check if the slot is an
+ * uninitialized let declaration, represented by the magic value
+ * JS_UNINITIALIZED_LEXICAL.
+ */
+static inline bool
+IsUninitializedLexical(const Value& val)
+{
+ // Use whyMagic here because JS_OPTIMIZED_ARGUMENTS could flow into here.
+ return val.isMagic() && val.whyMagic() == JS_UNINITIALIZED_LEXICAL;
+}
+
+static inline bool
+IsUninitializedLexicalSlot(HandleObject obj, HandleShape shape)
+{
+ MOZ_ASSERT(shape);
+ if (obj->is<WithEnvironmentObject>())
+ return false;
+ // We check for IsImplicitDenseOrTypedArrayElement even though the shape
+ // is always a non-indexed property because proxy hooks may return a
+ // "non-native property found" shape, which happens to be encoded in the
+ // same way as the "dense element" shape. See MarkNonNativePropertyFound.
+ if (IsImplicitDenseOrTypedArrayElement(shape) ||
+ !shape->hasSlot() ||
+ !shape->hasDefaultGetter() ||
+ !shape->hasDefaultSetter())
+ {
+ return false;
+ }
+ MOZ_ASSERT(obj->as<NativeObject>().containsPure(shape));
+ return IsUninitializedLexical(obj->as<NativeObject>().getSlot(shape->slot()));
+}
+
+static inline void
+ReportUninitializedLexical(JSContext* cx, HandlePropertyName name)
+{
+ ReportRuntimeLexicalError(cx, JSMSG_UNINITIALIZED_LEXICAL, name);
+}
+
+static inline void
+ReportUninitializedLexical(JSContext* cx, HandleScript script, jsbytecode* pc)
+{
+ ReportRuntimeLexicalError(cx, JSMSG_UNINITIALIZED_LEXICAL, script, pc);
+}
+
+static inline bool
+CheckUninitializedLexical(JSContext* cx, PropertyName* name_, HandleValue val)
+{
+ if (IsUninitializedLexical(val)) {
+ RootedPropertyName name(cx, name_);
+ ReportUninitializedLexical(cx, name);
+ return false;
+ }
+ return true;
+}
+
+static inline bool
+CheckUninitializedLexical(JSContext* cx, HandleScript script, jsbytecode* pc, HandleValue val)
+{
+ if (IsUninitializedLexical(val)) {
+ ReportUninitializedLexical(cx, script, pc);
+ return false;
+ }
+ return true;
+}
+
+static inline void
+ReportRuntimeConstAssignment(JSContext* cx, HandlePropertyName name)
+{
+ ReportRuntimeLexicalError(cx, JSMSG_BAD_CONST_ASSIGN, name);
+}
+
+static inline void
+ReportRuntimeConstAssignment(JSContext* cx, HandleScript script, jsbytecode* pc)
+{
+ ReportRuntimeLexicalError(cx, JSMSG_BAD_CONST_ASSIGN, script, pc);
+}
+
+inline bool
+GetLengthProperty(const Value& lval, MutableHandleValue vp)
+{
+ /* Optimize length accesses on strings, arrays, and arguments. */
+ if (lval.isString()) {
+ vp.setInt32(lval.toString()->length());
+ return true;
+ }
+ if (lval.isObject()) {
+ JSObject* obj = &lval.toObject();
+ if (obj->is<ArrayObject>()) {
+ vp.setNumber(obj->as<ArrayObject>().length());
+ return true;
+ }
+
+ if (obj->is<ArgumentsObject>()) {
+ ArgumentsObject* argsobj = &obj->as<ArgumentsObject>();
+ if (!argsobj->hasOverriddenLength()) {
+ uint32_t length = argsobj->initialLength();
+ MOZ_ASSERT(length < INT32_MAX);
+ vp.setInt32(int32_t(length));
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+template <bool TypeOf> inline bool
+FetchName(JSContext* cx, HandleObject obj, HandleObject obj2, HandlePropertyName name,
+ HandleShape shape, MutableHandleValue vp)
+{
+ if (!shape) {
+ if (TypeOf) {
+ vp.setUndefined();
+ return true;
+ }
+ return ReportIsNotDefined(cx, name);
+ }
+
+ /* Take the slow path if shape was not found in a native object. */
+ if (!obj->isNative() || !obj2->isNative()) {
+ Rooted<jsid> id(cx, NameToId(name));
+ if (!GetProperty(cx, obj, obj, id, vp))
+ return false;
+ } else {
+ RootedObject normalized(cx, obj);
+ if (normalized->is<WithEnvironmentObject>() && !shape->hasDefaultGetter())
+ normalized = &normalized->as<WithEnvironmentObject>().object();
+ if (shape->isDataDescriptor() && shape->hasDefaultGetter()) {
+ /* Fast path for Object instance properties. */
+ MOZ_ASSERT(shape->hasSlot());
+ vp.set(obj2->as<NativeObject>().getSlot(shape->slot()));
+ } else {
+ if (!NativeGetExistingProperty(cx, normalized, obj2.as<NativeObject>(), shape, vp))
+ return false;
+ }
+ }
+
+ // We do our own explicit checking for |this|
+ if (name == cx->names().dotThis)
+ return true;
+
+ // NAME operations are the slow paths already, so unconditionally check
+ // for uninitialized lets.
+ return CheckUninitializedLexical(cx, name, vp);
+}
+
+inline bool
+FetchNameNoGC(JSObject* pobj, Shape* shape, MutableHandleValue vp)
+{
+ if (!shape || !pobj->isNative() || !shape->isDataDescriptor() || !shape->hasDefaultGetter())
+ return false;
+
+ vp.set(pobj->as<NativeObject>().getSlot(shape->slot()));
+ return !IsUninitializedLexical(vp);
+}
+
+inline bool
+GetIntrinsicOperation(JSContext* cx, jsbytecode* pc, MutableHandleValue vp)
+{
+ RootedPropertyName name(cx, cx->currentScript()->getName(pc));
+ return GlobalObject::getIntrinsicValue(cx, cx->global(), name, vp);
+}
+
+inline bool
+SetIntrinsicOperation(JSContext* cx, JSScript* script, jsbytecode* pc, HandleValue val)
+{
+ RootedPropertyName name(cx, script->getName(pc));
+ return GlobalObject::setIntrinsicValue(cx, cx->global(), name, val);
+}
+
+inline void
+SetAliasedVarOperation(JSContext* cx, JSScript* script, jsbytecode* pc,
+ EnvironmentObject& obj, EnvironmentCoordinate ec, const Value& val,
+ MaybeCheckTDZ checkTDZ)
+{
+ MOZ_ASSERT_IF(checkTDZ, !IsUninitializedLexical(obj.aliasedBinding(ec)));
+
+ // Avoid computing the name if no type updates are needed, as this may be
+ // expensive on scopes with large numbers of variables.
+ PropertyName* name = obj.isSingleton()
+ ? EnvironmentCoordinateName(cx->caches.envCoordinateNameCache, script, pc)
+ : nullptr;
+
+ obj.setAliasedBinding(cx, ec, name, val);
+}
+
+inline bool
+SetNameOperation(JSContext* cx, JSScript* script, jsbytecode* pc, HandleObject env,
+ HandleValue val)
+{
+ MOZ_ASSERT(*pc == JSOP_SETNAME ||
+ *pc == JSOP_STRICTSETNAME ||
+ *pc == JSOP_SETGNAME ||
+ *pc == JSOP_STRICTSETGNAME);
+ MOZ_ASSERT_IF((*pc == JSOP_SETGNAME || *pc == JSOP_STRICTSETGNAME) &&
+ !script->hasNonSyntacticScope(),
+ env == cx->global() ||
+ env == &cx->global()->lexicalEnvironment() ||
+ env->is<RuntimeLexicalErrorObject>());
+
+ bool strict = *pc == JSOP_STRICTSETNAME || *pc == JSOP_STRICTSETGNAME;
+ RootedPropertyName name(cx, script->getName(pc));
+
+ // In strict mode, assigning to an undeclared global variable is an
+ // error. To detect this, we call NativeSetProperty directly and pass
+ // Unqualified. It stores the error, if any, in |result|.
+ bool ok;
+ ObjectOpResult result;
+ RootedId id(cx, NameToId(name));
+ RootedValue receiver(cx, ObjectValue(*env));
+ if (env->isUnqualifiedVarObj()) {
+ RootedNativeObject varobj(cx);
+ if (env->is<DebugEnvironmentProxy>())
+ varobj = &env->as<DebugEnvironmentProxy>().environment().as<NativeObject>();
+ else
+ varobj = &env->as<NativeObject>();
+ MOZ_ASSERT(!varobj->getOpsSetProperty());
+ ok = NativeSetProperty(cx, varobj, id, val, receiver, Unqualified, result);
+ } else {
+ ok = SetProperty(cx, env, id, val, receiver, result);
+ }
+ return ok && result.checkStrictErrorOrWarning(cx, env, id, strict);
+}
+
+inline bool
+DefLexicalOperation(JSContext* cx, Handle<LexicalEnvironmentObject*> lexicalEnv,
+ HandleObject varObj, HandlePropertyName name, unsigned attrs)
+{
+ // Redeclaration checks should have already been done.
+ MOZ_ASSERT(CheckLexicalNameConflict(cx, lexicalEnv, varObj, name));
+ RootedId id(cx, NameToId(name));
+ RootedValue uninitialized(cx, MagicValue(JS_UNINITIALIZED_LEXICAL));
+ return NativeDefineProperty(cx, lexicalEnv, id, uninitialized, nullptr, nullptr, attrs);
+}
+
+inline bool
+DefLexicalOperation(JSContext* cx, LexicalEnvironmentObject* lexicalEnvArg,
+ JSObject* varObjArg, JSScript* script, jsbytecode* pc)
+{
+ MOZ_ASSERT(*pc == JSOP_DEFLET || *pc == JSOP_DEFCONST);
+ RootedPropertyName name(cx, script->getName(pc));
+
+ unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT;
+ if (*pc == JSOP_DEFCONST)
+ attrs |= JSPROP_READONLY;
+
+ Rooted<LexicalEnvironmentObject*> lexicalEnv(cx, lexicalEnvArg);
+ RootedObject varObj(cx, varObjArg);
+ MOZ_ASSERT_IF(!script->hasNonSyntacticScope(),
+ lexicalEnv == &cx->global()->lexicalEnvironment() && varObj == cx->global());
+
+ return DefLexicalOperation(cx, lexicalEnv, varObj, name, attrs);
+}
+
+inline void
+InitGlobalLexicalOperation(JSContext* cx, LexicalEnvironmentObject* lexicalEnvArg,
+ JSScript* script, jsbytecode* pc, HandleValue value)
+{
+ MOZ_ASSERT_IF(!script->hasNonSyntacticScope(),
+ lexicalEnvArg == &cx->global()->lexicalEnvironment());
+ MOZ_ASSERT(*pc == JSOP_INITGLEXICAL);
+ Rooted<LexicalEnvironmentObject*> lexicalEnv(cx, lexicalEnvArg);
+ RootedShape shape(cx, lexicalEnv->lookup(cx, script->getName(pc)));
+ MOZ_ASSERT(shape);
+ lexicalEnv->setSlotWithType(cx, shape, value);
+}
+
+inline bool
+InitPropertyOperation(JSContext* cx, JSOp op, HandleObject obj, HandleId id, HandleValue rhs)
+{
+ if (obj->is<PlainObject>() || obj->is<JSFunction>()) {
+ unsigned propAttrs = GetInitDataPropAttrs(op);
+ return NativeDefineProperty(cx, obj.as<NativeObject>(), id, rhs, nullptr, nullptr,
+ propAttrs);
+ }
+
+ MOZ_ASSERT(obj->as<UnboxedPlainObject>().layout().lookup(id));
+ return PutProperty(cx, obj, id, rhs, false);
+}
+
+inline bool
+DefVarOperation(JSContext* cx, HandleObject varobj, HandlePropertyName dn, unsigned attrs)
+{
+ MOZ_ASSERT(varobj->isQualifiedVarObj());
+
+#ifdef DEBUG
+ // Per spec, it is an error to redeclare a lexical binding. This should
+ // have already been checked.
+ if (JS_HasExtensibleLexicalEnvironment(varobj)) {
+ Rooted<LexicalEnvironmentObject*> lexicalEnv(cx);
+ lexicalEnv = &JS_ExtensibleLexicalEnvironment(varobj)->as<LexicalEnvironmentObject>();
+ MOZ_ASSERT(CheckVarNameConflict(cx, lexicalEnv, dn));
+ }
+#endif
+
+ RootedShape prop(cx);
+ RootedObject obj2(cx);
+ if (!LookupProperty(cx, varobj, dn, &obj2, &prop))
+ return false;
+
+ /* Steps 8c, 8d. */
+ if (!prop || (obj2 != varobj && varobj->is<GlobalObject>())) {
+ if (!DefineProperty(cx, varobj, dn, UndefinedHandleValue, nullptr, nullptr, attrs))
+ return false;
+ }
+
+ if (varobj->is<GlobalObject>()) {
+ if (!varobj->compartment()->addToVarNames(cx, dn))
+ return false;
+ }
+
+ return true;
+}
+
+static MOZ_ALWAYS_INLINE bool
+NegOperation(JSContext* cx, HandleScript script, jsbytecode* pc, HandleValue val,
+ MutableHandleValue res)
+{
+ /*
+ * When the operand is int jsval, INT32_FITS_IN_JSVAL(i) implies
+ * INT32_FITS_IN_JSVAL(-i) unless i is 0 or INT32_MIN when the
+ * results, -0.0 or INT32_MAX + 1, are double values.
+ */
+ int32_t i;
+ if (val.isInt32() && (i = val.toInt32()) != 0 && i != INT32_MIN) {
+ res.setInt32(-i);
+ } else {
+ double d;
+ if (!ToNumber(cx, val, &d))
+ return false;
+ res.setNumber(-d);
+ }
+
+ return true;
+}
+
+static MOZ_ALWAYS_INLINE bool
+ToIdOperation(JSContext* cx, HandleScript script, jsbytecode* pc, HandleValue idval,
+ MutableHandleValue res)
+{
+ if (idval.isInt32()) {
+ res.set(idval);
+ return true;
+ }
+
+ RootedId id(cx);
+ if (!ToPropertyKey(cx, idval, &id))
+ return false;
+
+ res.set(IdToValue(id));
+ return true;
+}
+
+static MOZ_ALWAYS_INLINE bool
+GetObjectElementOperation(JSContext* cx, JSOp op, JS::HandleObject obj, JS::HandleObject receiver,
+ HandleValue key, MutableHandleValue res)
+{
+ MOZ_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM || op == JSOP_GETELEM_SUPER);
+ MOZ_ASSERT_IF(op == JSOP_GETELEM || op == JSOP_CALLELEM, obj == receiver);
+
+ do {
+ uint32_t index;
+ if (IsDefinitelyIndex(key, &index)) {
+ if (GetElementNoGC(cx, obj, receiver, index, res.address()))
+ break;
+
+ if (!GetElement(cx, obj, receiver, index, res))
+ return false;
+ break;
+ }
+
+ if (key.isString()) {
+ JSString* str = key.toString();
+ JSAtom* name = str->isAtom() ? &str->asAtom() : AtomizeString(cx, str);
+ if (!name)
+ return false;
+ if (name->isIndex(&index)) {
+ if (GetElementNoGC(cx, obj, receiver, index, res.address()))
+ break;
+ } else {
+ if (GetPropertyNoGC(cx, obj, receiver, name->asPropertyName(), res.address()))
+ break;
+ }
+ }
+
+ RootedId id(cx);
+ if (!ToPropertyKey(cx, key, &id))
+ return false;
+ if (!GetProperty(cx, obj, receiver, id, res))
+ return false;
+ } while (false);
+
+ assertSameCompartmentDebugOnly(cx, res);
+ return true;
+}
+
+static MOZ_ALWAYS_INLINE bool
+GetPrimitiveElementOperation(JSContext* cx, JSOp op, JS::HandleValue receiver,
+ HandleValue key, MutableHandleValue res)
+{
+ MOZ_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM);
+
+ // FIXME: Bug 1234324 We shouldn't be boxing here.
+ RootedObject boxed(cx, ToObjectFromStack(cx, receiver));
+ if (!boxed)
+ return false;
+
+ do {
+ uint32_t index;
+ if (IsDefinitelyIndex(key, &index)) {
+ if (GetElementNoGC(cx, boxed, receiver, index, res.address()))
+ break;
+
+ if (!GetElement(cx, boxed, receiver, index, res))
+ return false;
+ break;
+ }
+
+ if (key.isString()) {
+ JSString* str = key.toString();
+ JSAtom* name = str->isAtom() ? &str->asAtom() : AtomizeString(cx, str);
+ if (!name)
+ return false;
+ if (name->isIndex(&index)) {
+ if (GetElementNoGC(cx, boxed, receiver, index, res.address()))
+ break;
+ } else {
+ if (GetPropertyNoGC(cx, boxed, receiver, name->asPropertyName(), res.address()))
+ break;
+ }
+ }
+
+ RootedId id(cx);
+ if (!ToPropertyKey(cx, key, &id))
+ return false;
+ if (!GetProperty(cx, boxed, receiver, id, res))
+ return false;
+ } while (false);
+
+ assertSameCompartmentDebugOnly(cx, res);
+ return true;
+}
+
+static MOZ_ALWAYS_INLINE bool
+GetElemOptimizedArguments(JSContext* cx, AbstractFramePtr frame, MutableHandleValue lref,
+ HandleValue rref, MutableHandleValue res, bool* done)
+{
+ MOZ_ASSERT(!*done);
+
+ if (IsOptimizedArguments(frame, lref)) {
+ if (rref.isInt32()) {
+ int32_t i = rref.toInt32();
+ if (i >= 0 && uint32_t(i) < frame.numActualArgs()) {
+ res.set(frame.unaliasedActual(i));
+ *done = true;
+ return true;
+ }
+ }
+
+ RootedScript script(cx, frame.script());
+ if (!JSScript::argumentsOptimizationFailed(cx, script))
+ return false;
+
+ lref.set(ObjectValue(frame.argsObj()));
+ }
+
+ return true;
+}
+
+static MOZ_ALWAYS_INLINE bool
+GetElementOperation(JSContext* cx, JSOp op, MutableHandleValue lref, HandleValue rref,
+ MutableHandleValue res)
+{
+ MOZ_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM);
+
+ uint32_t index;
+ if (lref.isString() && IsDefinitelyIndex(rref, &index)) {
+ JSString* str = lref.toString();
+ if (index < str->length()) {
+ str = cx->staticStrings().getUnitStringForElement(cx, str, index);
+ if (!str)
+ return false;
+ res.setString(str);
+ return true;
+ }
+ }
+
+ if (lref.isPrimitive()) {
+ RootedValue thisv(cx, lref);
+ return GetPrimitiveElementOperation(cx, op, thisv, rref, res);
+ }
+
+ RootedObject thisv(cx, &lref.toObject());
+ return GetObjectElementOperation(cx, op, thisv, thisv, rref, res);
+}
+
+static MOZ_ALWAYS_INLINE JSString*
+TypeOfOperation(const Value& v, JSRuntime* rt)
+{
+ JSType type = js::TypeOfValue(v);
+ return TypeName(type, *rt->commonNames);
+}
+
+static inline JSString*
+TypeOfObjectOperation(JSObject* obj, JSRuntime* rt)
+{
+ JSType type = js::TypeOfObject(obj);
+ return TypeName(type, *rt->commonNames);
+}
+
+static MOZ_ALWAYS_INLINE bool
+InitElemOperation(JSContext* cx, jsbytecode* pc, HandleObject obj, HandleValue idval, HandleValue val)
+{
+ MOZ_ASSERT(!val.isMagic(JS_ELEMENTS_HOLE));
+ MOZ_ASSERT(!obj->getClass()->getGetProperty());
+ MOZ_ASSERT(!obj->getClass()->getSetProperty());
+
+ RootedId id(cx);
+ if (!ToPropertyKey(cx, idval, &id))
+ return false;
+
+ unsigned flags = JSOp(*pc) == JSOP_INITHIDDENELEM ? 0 : JSPROP_ENUMERATE;
+ return DefineProperty(cx, obj, id, val, nullptr, nullptr, flags);
+}
+
+static MOZ_ALWAYS_INLINE bool
+InitArrayElemOperation(JSContext* cx, jsbytecode* pc, HandleObject obj, uint32_t index, HandleValue val)
+{
+ JSOp op = JSOp(*pc);
+ MOZ_ASSERT(op == JSOP_INITELEM_ARRAY || op == JSOP_INITELEM_INC);
+
+ MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
+
+ if (op == JSOP_INITELEM_INC && index == INT32_MAX) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SPREAD_TOO_LARGE);
+ return false;
+ }
+
+ /*
+ * If val is a hole, do not call DefineElement.
+ *
+ * Furthermore, if the current op is JSOP_INITELEM_INC, always call
+ * SetLengthProperty even if it is not the last element initialiser,
+ * because it may be followed by JSOP_SPREAD, which will not set the array
+ * length if nothing is spread.
+ *
+ * Alternatively, if the current op is JSOP_INITELEM_ARRAY, the length will
+ * have already been set by the earlier JSOP_NEWARRAY; JSOP_INITELEM_ARRAY
+ * cannot follow JSOP_SPREAD.
+ */
+ if (val.isMagic(JS_ELEMENTS_HOLE)) {
+ if (op == JSOP_INITELEM_INC) {
+ if (!SetLengthProperty(cx, obj, index + 1))
+ return false;
+ }
+ } else {
+ if (!DefineElement(cx, obj, index, val, nullptr, nullptr, JSPROP_ENUMERATE))
+ return false;
+ }
+
+ return true;
+}
+
+static MOZ_ALWAYS_INLINE bool
+ProcessCallSiteObjOperation(JSContext* cx, RootedObject& cso, RootedObject& raw,
+ RootedValue& rawValue)
+{
+ bool extensible;
+ if (!IsExtensible(cx, cso, &extensible))
+ return false;
+ if (extensible) {
+ JSAtom* name = cx->names().raw;
+ if (!DefineProperty(cx, cso, name->asPropertyName(), rawValue, nullptr, nullptr, 0))
+ return false;
+ if (!FreezeObject(cx, raw))
+ return false;
+ if (!FreezeObject(cx, cso))
+ return false;
+ }
+ return true;
+}
+
+#define RELATIONAL_OP(OP) \
+ JS_BEGIN_MACRO \
+ /* Optimize for two int-tagged operands (typical loop control). */ \
+ if (lhs.isInt32() && rhs.isInt32()) { \
+ *res = lhs.toInt32() OP rhs.toInt32(); \
+ } else { \
+ if (!ToPrimitive(cx, JSTYPE_NUMBER, lhs)) \
+ return false; \
+ if (!ToPrimitive(cx, JSTYPE_NUMBER, rhs)) \
+ return false; \
+ if (lhs.isString() && rhs.isString()) { \
+ JSString* l = lhs.toString(); \
+ JSString* r = rhs.toString(); \
+ int32_t result; \
+ if (!CompareStrings(cx, l, r, &result)) \
+ return false; \
+ *res = result OP 0; \
+ } else { \
+ double l, r; \
+ if (!ToNumber(cx, lhs, &l) || !ToNumber(cx, rhs, &r)) \
+ return false; \
+ *res = (l OP r); \
+ } \
+ } \
+ return true; \
+ JS_END_MACRO
+
+static MOZ_ALWAYS_INLINE bool
+LessThanOperation(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res) {
+ RELATIONAL_OP(<);
+}
+
+static MOZ_ALWAYS_INLINE bool
+LessThanOrEqualOperation(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res) {
+ RELATIONAL_OP(<=);
+}
+
+static MOZ_ALWAYS_INLINE bool
+GreaterThanOperation(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res) {
+ RELATIONAL_OP(>);
+}
+
+static MOZ_ALWAYS_INLINE bool
+GreaterThanOrEqualOperation(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res) {
+ RELATIONAL_OP(>=);
+}
+
+static MOZ_ALWAYS_INLINE bool
+BitNot(JSContext* cx, HandleValue in, int* out)
+{
+ int i;
+ if (!ToInt32(cx, in, &i))
+ return false;
+ *out = ~i;
+ return true;
+}
+
+static MOZ_ALWAYS_INLINE bool
+BitXor(JSContext* cx, HandleValue lhs, HandleValue rhs, int* out)
+{
+ int left, right;
+ if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
+ return false;
+ *out = left ^ right;
+ return true;
+}
+
+static MOZ_ALWAYS_INLINE bool
+BitOr(JSContext* cx, HandleValue lhs, HandleValue rhs, int* out)
+{
+ int left, right;
+ if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
+ return false;
+ *out = left | right;
+ return true;
+}
+
+static MOZ_ALWAYS_INLINE bool
+BitAnd(JSContext* cx, HandleValue lhs, HandleValue rhs, int* out)
+{
+ int left, right;
+ if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
+ return false;
+ *out = left & right;
+ return true;
+}
+
+static MOZ_ALWAYS_INLINE bool
+BitLsh(JSContext* cx, HandleValue lhs, HandleValue rhs, int* out)
+{
+ int32_t left, right;
+ if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
+ return false;
+ *out = uint32_t(left) << (right & 31);
+ return true;
+}
+
+static MOZ_ALWAYS_INLINE bool
+BitRsh(JSContext* cx, HandleValue lhs, HandleValue rhs, int* out)
+{
+ int32_t left, right;
+ if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
+ return false;
+ *out = left >> (right & 31);
+ return true;
+}
+
+static MOZ_ALWAYS_INLINE bool
+UrshOperation(JSContext* cx, HandleValue lhs, HandleValue rhs, MutableHandleValue out)
+{
+ uint32_t left;
+ int32_t right;
+ if (!ToUint32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
+ return false;
+ left >>= right & 31;
+ out.setNumber(uint32_t(left));
+ return true;
+}
+
+template <typename T>
+static MOZ_ALWAYS_INLINE bool
+SignExtendOperation(JSContext* cx, HandleValue in, int* out)
+{
+ int32_t i;
+ if (!ToInt32(cx, in, &i))
+ return false;
+ *out = (T)i;
+ return true;
+}
+
+#undef RELATIONAL_OP
+
+inline JSFunction*
+ReportIfNotFunction(JSContext* cx, HandleValue v, MaybeConstruct construct = NO_CONSTRUCT)
+{
+ if (v.isObject() && v.toObject().is<JSFunction>())
+ return &v.toObject().as<JSFunction>();
+
+ ReportIsNotFunction(cx, v, -1, construct);
+ return nullptr;
+}
+
+/*
+ * FastCallGuard is used to optimize calls to JS functions from natives written
+ * in C++, e.g. Array.prototype.map. If the callee is not Ion-compiled, this
+ * will just call js::Call. If the callee has a valid IonScript, however, it
+ * will enter Ion directly.
+ */
+class FastCallGuard
+{
+ InvokeArgs args_;
+ RootedFunction fun_;
+ RootedScript script_;
+
+ // Constructing a JitContext is pretty expensive due to the TLS access,
+ // so only do this if we have to.
+ bool useIon_;
+
+ public:
+ FastCallGuard(JSContext* cx, const Value& fval)
+ : args_(cx)
+ , fun_(cx)
+ , script_(cx)
+ , useIon_(jit::IsIonEnabled(cx))
+ {
+ initFunction(fval);
+ }
+
+ void initFunction(const Value& fval) {
+ if (fval.isObject() && fval.toObject().is<JSFunction>()) {
+ JSFunction* fun = &fval.toObject().as<JSFunction>();
+ if (fun->isInterpreted())
+ fun_ = fun;
+ }
+ }
+
+ InvokeArgs& args() {
+ return args_;
+ }
+
+ bool call(JSContext* cx, HandleValue callee, HandleValue thisv, MutableHandleValue rval) {
+ args_.CallArgs::setCallee(callee);
+ args_.CallArgs::setThis(thisv);
+
+ if (useIon_ && fun_) {
+ if (!script_) {
+ script_ = fun_->getOrCreateScript(cx);
+ if (!script_)
+ return false;
+ }
+ MOZ_ASSERT(fun_->nonLazyScript() == script_);
+
+ jit::MethodStatus status = jit::CanEnterUsingFastInvoke(cx, script_, args_.length());
+ if (status == jit::Method_Error)
+ return false;
+ if (status == jit::Method_Compiled) {
+ jit::JitExecStatus result = jit::FastInvoke(cx, fun_, args_);
+ if (IsErrorStatus(result))
+ return false;
+
+ MOZ_ASSERT(result == jit::JitExec_Ok);
+ rval.set(args_.CallArgs::rval());
+ return true;
+ }
+
+ MOZ_ASSERT(status == jit::Method_Skipped);
+
+ if (script_->canIonCompile()) {
+ // This script is not yet hot. Since calling into Ion is much
+ // faster here, bump the warm-up counter a bit to account for this.
+ script_->incWarmUpCounter(5);
+ }
+ }
+
+ if (!InternalCallOrConstruct(cx, args_, NO_CONSTRUCT))
+ return false;
+
+ rval.set(args_.CallArgs::rval());
+ return true;
+ }
+
+ private:
+ FastCallGuard(const FastCallGuard& other) = delete;
+ void operator=(const FastCallGuard& other) = delete;
+};
+
+} /* namespace js */
+
+#endif /* vm_Interpreter_inl_h */