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_inl_h michael@0: #define vm_Interpreter_inl_h michael@0: michael@0: #include "vm/Interpreter.h" michael@0: michael@0: #include "jscompartment.h" michael@0: #include "jsinfer.h" michael@0: #include "jsnum.h" michael@0: #include "jsstr.h" michael@0: michael@0: #include "jit/Ion.h" michael@0: #include "vm/ArgumentsObject.h" michael@0: #include "vm/ForkJoin.h" michael@0: michael@0: #include "jsatominlines.h" michael@0: #include "jsinferinlines.h" michael@0: #include "jsobjinlines.h" michael@0: michael@0: #include "vm/Stack-inl.h" michael@0: #include "vm/String-inl.h" michael@0: michael@0: namespace js { michael@0: michael@0: inline bool michael@0: ComputeThis(JSContext *cx, AbstractFramePtr frame) michael@0: { michael@0: JS_ASSERT_IF(frame.isInterpreterFrame(), !frame.asInterpreterFrame()->runningInJit()); michael@0: michael@0: if (frame.isFunctionFrame() && frame.fun()->isArrow()) { michael@0: /* michael@0: * Arrow functions store their (lexical) |this| value in an michael@0: * extended slot. michael@0: */ michael@0: frame.thisValue() = frame.fun()->getExtendedSlot(0); michael@0: return true; michael@0: } michael@0: michael@0: if (frame.thisValue().isObject()) michael@0: return true; michael@0: RootedValue thisv(cx, frame.thisValue()); michael@0: if (frame.isFunctionFrame()) { michael@0: if (frame.fun()->strict() || frame.fun()->isSelfHostedBuiltin()) michael@0: return true; michael@0: /* michael@0: * Eval function frames have their own |this| slot, which is a copy of the function's michael@0: * |this| slot. If we lazily wrap a primitive |this| in an eval function frame, the michael@0: * eval's frame will get the wrapper, but the function's frame will not. To prevent michael@0: * this, we always wrap a function's |this| before pushing an eval frame, and should michael@0: * thus never see an unwrapped primitive in a non-strict eval function frame. Null michael@0: * and undefined |this| values will unwrap to the same object in the function and michael@0: * eval frames, so are not required to be wrapped. michael@0: */ michael@0: JS_ASSERT_IF(frame.isEvalFrame(), thisv.isUndefined() || thisv.isNull()); michael@0: } michael@0: michael@0: JSObject *thisObj = BoxNonStrictThis(cx, thisv); michael@0: if (!thisObj) michael@0: return false; michael@0: michael@0: frame.thisValue().setObject(*thisObj); michael@0: return true; michael@0: } michael@0: michael@0: /* michael@0: * Every possible consumer of MagicValue(JS_OPTIMIZED_ARGUMENTS) (as determined michael@0: * by ScriptAnalysis::needsArgsObj) must check for these magic values and, when michael@0: * one is received, act as if the value were the function's ArgumentsObject. michael@0: * Additionally, it is possible that, after 'arguments' was copied into a michael@0: * temporary, the arguments object has been created a some other failed guard michael@0: * that called JSScript::argumentsOptimizationFailed. In this case, it is michael@0: * always valid (and necessary) to replace JS_OPTIMIZED_ARGUMENTS with the real michael@0: * arguments object. michael@0: */ michael@0: static inline bool michael@0: IsOptimizedArguments(AbstractFramePtr frame, Value *vp) michael@0: { michael@0: if (vp->isMagic(JS_OPTIMIZED_ARGUMENTS) && frame.script()->needsArgsObj()) michael@0: *vp = ObjectValue(frame.argsObj()); michael@0: return vp->isMagic(JS_OPTIMIZED_ARGUMENTS); michael@0: } michael@0: michael@0: /* michael@0: * One optimized consumer of MagicValue(JS_OPTIMIZED_ARGUMENTS) is f.apply. michael@0: * However, this speculation must be guarded before calling 'apply' in case it michael@0: * is not the builtin Function.prototype.apply. michael@0: */ michael@0: static inline bool michael@0: GuardFunApplyArgumentsOptimization(JSContext *cx, AbstractFramePtr frame, HandleValue callee, michael@0: Value *args, uint32_t argc) michael@0: { michael@0: if (argc == 2 && IsOptimizedArguments(frame, &args[1])) { michael@0: if (!IsNativeFunction(callee, js_fun_apply)) { michael@0: RootedScript script(cx, frame.script()); michael@0: if (!JSScript::argumentsOptimizationFailed(cx, script)) michael@0: return false; michael@0: args[1] = ObjectValue(frame.argsObj()); michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: /* michael@0: * Return an object on which we should look for the properties of |value|. michael@0: * This helps us implement the custom [[Get]] method that ES5's GetValue michael@0: * algorithm uses for primitive values, without actually constructing the michael@0: * temporary object that the specification does. michael@0: * michael@0: * For objects, return the object itself. For string, boolean, and number michael@0: * primitive values, return the appropriate constructor's prototype. For michael@0: * undefined and null, throw an error and return nullptr, attributing the michael@0: * problem to the value at |spindex| on the stack. michael@0: */ michael@0: MOZ_ALWAYS_INLINE JSObject * michael@0: ValuePropertyBearer(JSContext *cx, InterpreterFrame *fp, HandleValue v, int spindex) michael@0: { michael@0: if (v.isObject()) michael@0: return &v.toObject(); michael@0: michael@0: Rooted global(cx, &fp->global()); michael@0: michael@0: if (v.isString()) michael@0: return GlobalObject::getOrCreateStringPrototype(cx, global); michael@0: if (v.isNumber()) michael@0: return GlobalObject::getOrCreateNumberPrototype(cx, global); michael@0: if (v.isBoolean()) michael@0: return GlobalObject::getOrCreateBooleanPrototype(cx, global); michael@0: michael@0: JS_ASSERT(v.isNull() || v.isUndefined()); michael@0: js_ReportIsNullOrUndefined(cx, spindex, v, NullPtr()); michael@0: return nullptr; michael@0: } michael@0: michael@0: inline bool michael@0: GetLengthProperty(const Value &lval, MutableHandleValue vp) michael@0: { michael@0: /* Optimize length accesses on strings, arrays, and arguments. */ michael@0: if (lval.isString()) { michael@0: vp.setInt32(lval.toString()->length()); michael@0: return true; michael@0: } michael@0: if (lval.isObject()) { michael@0: JSObject *obj = &lval.toObject(); michael@0: if (obj->is()) { michael@0: vp.setNumber(obj->as().length()); michael@0: return true; michael@0: } michael@0: michael@0: if (obj->is()) { michael@0: ArgumentsObject *argsobj = &obj->as(); michael@0: if (!argsobj->hasOverriddenLength()) { michael@0: uint32_t length = argsobj->initialLength(); michael@0: JS_ASSERT(length < INT32_MAX); michael@0: vp.setInt32(int32_t(length)); michael@0: return true; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: template inline bool michael@0: FetchName(JSContext *cx, HandleObject obj, HandleObject obj2, HandlePropertyName name, michael@0: HandleShape shape, MutableHandleValue vp) michael@0: { michael@0: if (!shape) { michael@0: if (TypeOf) { michael@0: vp.setUndefined(); michael@0: return true; michael@0: } michael@0: JSAutoByteString printable; michael@0: if (AtomToPrintableString(cx, name, &printable)) michael@0: js_ReportIsNotDefined(cx, printable.ptr()); michael@0: return false; michael@0: } michael@0: michael@0: /* Take the slow path if shape was not found in a native object. */ michael@0: if (!obj->isNative() || !obj2->isNative()) { michael@0: Rooted id(cx, NameToId(name)); michael@0: if (!JSObject::getGeneric(cx, obj, obj, id, vp)) michael@0: return false; michael@0: } else { michael@0: Rooted normalized(cx, obj); michael@0: if (normalized->is() && !shape->hasDefaultGetter()) michael@0: normalized = &normalized->as().object(); michael@0: if (shape->isDataDescriptor() && shape->hasDefaultGetter()) { michael@0: /* Fast path for Object instance properties. */ michael@0: JS_ASSERT(shape->hasSlot()); michael@0: vp.set(obj2->nativeGetSlot(shape->slot())); michael@0: } else if (!NativeGet(cx, normalized, obj2, shape, vp)) { michael@0: return false; michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: inline bool michael@0: FetchNameNoGC(JSObject *pobj, Shape *shape, MutableHandleValue vp) michael@0: { michael@0: if (!shape || !pobj->isNative() || !shape->isDataDescriptor() || !shape->hasDefaultGetter()) michael@0: return false; michael@0: michael@0: vp.set(pobj->nativeGetSlot(shape->slot())); michael@0: return true; michael@0: } michael@0: michael@0: inline bool michael@0: GetIntrinsicOperation(JSContext *cx, jsbytecode *pc, MutableHandleValue vp) michael@0: { michael@0: RootedPropertyName name(cx, cx->currentScript()->getName(pc)); michael@0: return GlobalObject::getIntrinsicValue(cx, cx->global(), name, vp); michael@0: } michael@0: michael@0: inline bool michael@0: SetIntrinsicOperation(JSContext *cx, JSScript *script, jsbytecode *pc, HandleValue val) michael@0: { michael@0: RootedPropertyName name(cx, script->getName(pc)); michael@0: return cx->global()->setIntrinsicValue(cx, name, val); michael@0: } michael@0: michael@0: inline bool michael@0: SetNameOperation(JSContext *cx, JSScript *script, jsbytecode *pc, HandleObject scope, michael@0: HandleValue val) michael@0: { michael@0: JS_ASSERT(*pc == JSOP_SETNAME || *pc == JSOP_SETGNAME); michael@0: JS_ASSERT_IF(*pc == JSOP_SETGNAME, scope == cx->global()); michael@0: michael@0: bool strict = script->strict(); michael@0: RootedPropertyName name(cx, script->getName(pc)); michael@0: RootedValue valCopy(cx, val); michael@0: michael@0: /* michael@0: * In strict-mode, we need to trigger an error when trying to assign to an michael@0: * undeclared global variable. To do this, we call SetPropertyHelper michael@0: * directly and pass Unqualified. michael@0: */ michael@0: if (scope->is()) { michael@0: JS_ASSERT(!scope->getOps()->setProperty); michael@0: RootedId id(cx, NameToId(name)); michael@0: return baseops::SetPropertyHelper(cx, scope, scope, id, michael@0: baseops::Unqualified, &valCopy, michael@0: strict); michael@0: } michael@0: michael@0: return JSObject::setProperty(cx, scope, scope, name, &valCopy, strict); michael@0: } michael@0: michael@0: inline bool michael@0: DefVarOrConstOperation(JSContext *cx, HandleObject varobj, HandlePropertyName dn, unsigned attrs) michael@0: { michael@0: JS_ASSERT(varobj->isVarObj()); michael@0: michael@0: RootedShape prop(cx); michael@0: RootedObject obj2(cx); michael@0: if (!JSObject::lookupProperty(cx, varobj, dn, &obj2, &prop)) michael@0: return false; michael@0: michael@0: /* Steps 8c, 8d. */ michael@0: if (!prop || (obj2 != varobj && varobj->is())) { michael@0: if (!JSObject::defineProperty(cx, varobj, dn, UndefinedHandleValue, JS_PropertyStub, michael@0: JS_StrictPropertyStub, attrs)) { michael@0: return false; michael@0: } michael@0: } else if (attrs & JSPROP_READONLY) { michael@0: /* michael@0: * Extension: ordinarily we'd be done here -- but for |const|. If we michael@0: * see a redeclaration that's |const|, we consider it a conflict. michael@0: */ michael@0: unsigned oldAttrs; michael@0: RootedId id(cx, NameToId(dn)); michael@0: if (!JSObject::getGenericAttributes(cx, varobj, id, &oldAttrs)) michael@0: return false; michael@0: michael@0: JSAutoByteString bytes; michael@0: if (AtomToPrintableString(cx, dn, &bytes)) { michael@0: JS_ALWAYS_FALSE(JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, michael@0: js_GetErrorMessage, michael@0: nullptr, JSMSG_REDECLARED_VAR, michael@0: (oldAttrs & JSPROP_READONLY) michael@0: ? "const" michael@0: : "var", michael@0: bytes.ptr())); michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE bool michael@0: NegOperation(JSContext *cx, HandleScript script, jsbytecode *pc, HandleValue val, michael@0: MutableHandleValue res) michael@0: { michael@0: /* michael@0: * When the operand is int jsval, INT32_FITS_IN_JSVAL(i) implies michael@0: * INT32_FITS_IN_JSVAL(-i) unless i is 0 or INT32_MIN when the michael@0: * results, -0.0 or INT32_MAX + 1, are double values. michael@0: */ michael@0: int32_t i; michael@0: if (val.isInt32() && (i = val.toInt32()) != 0 && i != INT32_MIN) { michael@0: res.setInt32(-i); michael@0: } else { michael@0: double d; michael@0: if (!ToNumber(cx, val, &d)) michael@0: return false; michael@0: res.setNumber(-d); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE bool michael@0: ToIdOperation(JSContext *cx, HandleScript script, jsbytecode *pc, HandleValue objval, michael@0: HandleValue idval, MutableHandleValue res) michael@0: { michael@0: if (idval.isInt32()) { michael@0: res.set(idval); michael@0: return true; michael@0: } michael@0: michael@0: JSObject *obj = ToObjectFromStack(cx, objval); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: RootedId id(cx); michael@0: if (!ValueToId(cx, idval, &id)) michael@0: return false; michael@0: michael@0: res.set(IdToValue(id)); michael@0: return true; michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE bool michael@0: GetObjectElementOperation(JSContext *cx, JSOp op, JSObject *objArg, bool wasObject, michael@0: HandleValue rref, MutableHandleValue res) michael@0: { michael@0: JS_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM); michael@0: michael@0: do { michael@0: uint32_t index; michael@0: if (IsDefinitelyIndex(rref, &index)) { michael@0: if (JSObject::getElementNoGC(cx, objArg, objArg, index, res.address())) michael@0: break; michael@0: michael@0: RootedObject obj(cx, objArg); michael@0: if (!JSObject::getElement(cx, obj, obj, index, res)) michael@0: return false; michael@0: objArg = obj; michael@0: break; michael@0: } michael@0: michael@0: JSAtom *name = ToAtom(cx, rref); michael@0: if (name) { michael@0: if (name->isIndex(&index)) { michael@0: if (JSObject::getElementNoGC(cx, objArg, objArg, index, res.address())) michael@0: break; michael@0: } else { michael@0: if (JSObject::getPropertyNoGC(cx, objArg, objArg, name->asPropertyName(), res.address())) michael@0: break; michael@0: } michael@0: } michael@0: michael@0: RootedObject obj(cx, objArg); michael@0: michael@0: name = ToAtom(cx, rref); michael@0: if (!name) michael@0: return false; michael@0: michael@0: if (name->isIndex(&index)) { michael@0: if (!JSObject::getElement(cx, obj, obj, index, res)) michael@0: return false; michael@0: } else { michael@0: if (!JSObject::getProperty(cx, obj, obj, name->asPropertyName(), res)) michael@0: return false; michael@0: } michael@0: michael@0: objArg = obj; michael@0: } while (0); michael@0: michael@0: #if JS_HAS_NO_SUCH_METHOD michael@0: if (op == JSOP_CALLELEM && MOZ_UNLIKELY(res.isUndefined()) && wasObject) { michael@0: RootedObject obj(cx, objArg); michael@0: if (!OnUnknownMethod(cx, obj, rref, res)) michael@0: return false; michael@0: } michael@0: #endif michael@0: michael@0: assertSameCompartmentDebugOnly(cx, res); michael@0: return true; michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE bool michael@0: GetElemOptimizedArguments(JSContext *cx, AbstractFramePtr frame, MutableHandleValue lref, michael@0: HandleValue rref, MutableHandleValue res, bool *done) michael@0: { michael@0: JS_ASSERT(!*done); michael@0: michael@0: if (IsOptimizedArguments(frame, lref.address())) { michael@0: if (rref.isInt32()) { michael@0: int32_t i = rref.toInt32(); michael@0: if (i >= 0 && uint32_t(i) < frame.numActualArgs()) { michael@0: res.set(frame.unaliasedActual(i)); michael@0: *done = true; michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: RootedScript script(cx, frame.script()); michael@0: if (!JSScript::argumentsOptimizationFailed(cx, script)) michael@0: return false; michael@0: michael@0: lref.set(ObjectValue(frame.argsObj())); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE bool michael@0: GetElementOperation(JSContext *cx, JSOp op, MutableHandleValue lref, HandleValue rref, michael@0: MutableHandleValue res) michael@0: { michael@0: JS_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM); michael@0: michael@0: uint32_t index; michael@0: if (lref.isString() && IsDefinitelyIndex(rref, &index)) { michael@0: JSString *str = lref.toString(); michael@0: if (index < str->length()) { michael@0: str = cx->staticStrings().getUnitStringForElement(cx, str, index); michael@0: if (!str) michael@0: return false; michael@0: res.setString(str); michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: bool isObject = lref.isObject(); michael@0: JSObject *obj = ToObjectFromStack(cx, lref); michael@0: if (!obj) michael@0: return false; michael@0: return GetObjectElementOperation(cx, op, obj, isObject, rref, res); michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE JSString * michael@0: TypeOfOperation(const Value &v, JSRuntime *rt) michael@0: { michael@0: JSType type = js::TypeOfValue(v); michael@0: return TypeName(type, *rt->commonNames); michael@0: } michael@0: michael@0: static inline JSString * michael@0: TypeOfObjectOperation(JSObject *obj, JSRuntime *rt) michael@0: { michael@0: JSType type = js::TypeOfObject(obj); michael@0: return TypeName(type, *rt->commonNames); michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE bool michael@0: InitElemOperation(JSContext *cx, HandleObject obj, HandleValue idval, HandleValue val) michael@0: { michael@0: JS_ASSERT(!val.isMagic(JS_ELEMENTS_HOLE)); michael@0: michael@0: RootedId id(cx); michael@0: if (!ValueToId(cx, idval, &id)) michael@0: return false; michael@0: michael@0: return JSObject::defineGeneric(cx, obj, id, val, nullptr, nullptr, JSPROP_ENUMERATE); michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE bool michael@0: InitArrayElemOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, uint32_t index, HandleValue val) michael@0: { michael@0: JSOp op = JSOp(*pc); michael@0: JS_ASSERT(op == JSOP_INITELEM_ARRAY || op == JSOP_INITELEM_INC); michael@0: michael@0: JS_ASSERT(obj->is()); michael@0: michael@0: /* michael@0: * If val is a hole, do not call JSObject::defineElement. In this case, michael@0: * if the current op is the last element initialiser, set the array length michael@0: * to one greater than id. michael@0: */ michael@0: if (val.isMagic(JS_ELEMENTS_HOLE)) { michael@0: JSOp next = JSOp(*GetNextPc(pc)); michael@0: michael@0: if ((op == JSOP_INITELEM_ARRAY && next == JSOP_ENDINIT) || michael@0: (op == JSOP_INITELEM_INC && next == JSOP_POP)) michael@0: { michael@0: if (!SetLengthProperty(cx, obj, index + 1)) michael@0: return false; michael@0: } michael@0: } else { michael@0: if (!JSObject::defineElement(cx, obj, index, val, nullptr, nullptr, JSPROP_ENUMERATE)) michael@0: return false; michael@0: } michael@0: michael@0: if (op == JSOP_INITELEM_INC && index == INT32_MAX) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SPREAD_TOO_LARGE); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: #define RELATIONAL_OP(OP) \ michael@0: JS_BEGIN_MACRO \ michael@0: /* Optimize for two int-tagged operands (typical loop control). */ \ michael@0: if (lhs.isInt32() && rhs.isInt32()) { \ michael@0: *res = lhs.toInt32() OP rhs.toInt32(); \ michael@0: } else { \ michael@0: if (!ToPrimitive(cx, JSTYPE_NUMBER, lhs)) \ michael@0: return false; \ michael@0: if (!ToPrimitive(cx, JSTYPE_NUMBER, rhs)) \ michael@0: return false; \ michael@0: if (lhs.isString() && rhs.isString()) { \ michael@0: JSString *l = lhs.toString(), *r = rhs.toString(); \ michael@0: int32_t result; \ michael@0: if (!CompareStrings(cx, l, r, &result)) \ michael@0: return false; \ michael@0: *res = result OP 0; \ michael@0: } else { \ michael@0: double l, r; \ michael@0: if (!ToNumber(cx, lhs, &l) || !ToNumber(cx, rhs, &r)) \ michael@0: return false;; \ michael@0: *res = (l OP r); \ michael@0: } \ michael@0: } \ michael@0: return true; \ michael@0: JS_END_MACRO michael@0: michael@0: static MOZ_ALWAYS_INLINE bool michael@0: LessThanOperation(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) { michael@0: RELATIONAL_OP(<); michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE bool michael@0: LessThanOrEqualOperation(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) { michael@0: RELATIONAL_OP(<=); michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE bool michael@0: GreaterThanOperation(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) { michael@0: RELATIONAL_OP(>); michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE bool michael@0: GreaterThanOrEqualOperation(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) { michael@0: RELATIONAL_OP(>=); michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE bool michael@0: BitNot(JSContext *cx, HandleValue in, int *out) michael@0: { michael@0: int i; michael@0: if (!ToInt32(cx, in, &i)) michael@0: return false; michael@0: *out = ~i; michael@0: return true; michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE bool michael@0: BitXor(JSContext *cx, HandleValue lhs, HandleValue rhs, int *out) michael@0: { michael@0: int left, right; michael@0: if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right)) michael@0: return false; michael@0: *out = left ^ right; michael@0: return true; michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE bool michael@0: BitOr(JSContext *cx, HandleValue lhs, HandleValue rhs, int *out) michael@0: { michael@0: int left, right; michael@0: if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right)) michael@0: return false; michael@0: *out = left | right; michael@0: return true; michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE bool michael@0: BitAnd(JSContext *cx, HandleValue lhs, HandleValue rhs, int *out) michael@0: { michael@0: int left, right; michael@0: if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right)) michael@0: return false; michael@0: *out = left & right; michael@0: return true; michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE bool michael@0: BitLsh(JSContext *cx, HandleValue lhs, HandleValue rhs, int *out) michael@0: { michael@0: int32_t left, right; michael@0: if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right)) michael@0: return false; michael@0: *out = uint32_t(left) << (right & 31); michael@0: return true; michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE bool michael@0: BitRsh(JSContext *cx, HandleValue lhs, HandleValue rhs, int *out) michael@0: { michael@0: int32_t left, right; michael@0: if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right)) michael@0: return false; michael@0: *out = left >> (right & 31); michael@0: return true; michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE bool michael@0: UrshOperation(JSContext *cx, HandleValue lhs, HandleValue rhs, MutableHandleValue out) michael@0: { michael@0: uint32_t left; michael@0: int32_t right; michael@0: if (!ToUint32(cx, lhs, &left) || !ToInt32(cx, rhs, &right)) michael@0: return false; michael@0: left >>= right & 31; michael@0: out.setNumber(uint32_t(left)); michael@0: return true; michael@0: } michael@0: michael@0: #undef RELATIONAL_OP michael@0: michael@0: inline JSFunction * michael@0: ReportIfNotFunction(JSContext *cx, HandleValue v, MaybeConstruct construct = NO_CONSTRUCT) michael@0: { michael@0: if (v.isObject() && v.toObject().is()) michael@0: return &v.toObject().as(); michael@0: michael@0: ReportIsNotFunction(cx, v, -1, construct); michael@0: return nullptr; michael@0: } michael@0: michael@0: /* michael@0: * FastInvokeGuard is used to optimize calls to JS functions from natives written michael@0: * in C++, for instance Array.map. If the callee is not Ion-compiled, this will michael@0: * just call Invoke. If the callee has a valid IonScript, however, it will enter michael@0: * Ion directly. michael@0: */ michael@0: class FastInvokeGuard michael@0: { michael@0: InvokeArgs args_; michael@0: RootedFunction fun_; michael@0: RootedScript script_; michael@0: #ifdef JS_ION michael@0: // Constructing an IonContext is pretty expensive due to the TLS access, michael@0: // so only do this if we have to. michael@0: bool useIon_; michael@0: #endif michael@0: michael@0: public: michael@0: FastInvokeGuard(JSContext *cx, const Value &fval) michael@0: : args_(cx) michael@0: , fun_(cx) michael@0: , script_(cx) michael@0: #ifdef JS_ION michael@0: , useIon_(jit::IsIonEnabled(cx)) michael@0: #endif michael@0: { michael@0: JS_ASSERT(!InParallelSection()); michael@0: initFunction(fval); michael@0: } michael@0: michael@0: void initFunction(const Value &fval) { michael@0: if (fval.isObject() && fval.toObject().is()) { michael@0: JSFunction *fun = &fval.toObject().as(); michael@0: if (fun->isInterpreted()) michael@0: fun_ = fun; michael@0: } michael@0: } michael@0: michael@0: InvokeArgs &args() { michael@0: return args_; michael@0: } michael@0: michael@0: bool invoke(JSContext *cx) { michael@0: #ifdef JS_ION michael@0: if (useIon_ && fun_) { michael@0: if (!script_) { michael@0: script_ = fun_->getOrCreateScript(cx); michael@0: if (!script_) michael@0: return false; michael@0: } michael@0: JS_ASSERT(fun_->nonLazyScript() == script_); michael@0: michael@0: jit::MethodStatus status = jit::CanEnterUsingFastInvoke(cx, script_, args_.length()); michael@0: if (status == jit::Method_Error) michael@0: return false; michael@0: if (status == jit::Method_Compiled) { michael@0: jit::IonExecStatus result = jit::FastInvoke(cx, fun_, args_); michael@0: if (IsErrorStatus(result)) michael@0: return false; michael@0: michael@0: JS_ASSERT(result == jit::IonExec_Ok); michael@0: return true; michael@0: } michael@0: michael@0: JS_ASSERT(status == jit::Method_Skipped); michael@0: michael@0: if (script_->canIonCompile()) { michael@0: // This script is not yet hot. Since calling into Ion is much michael@0: // faster here, bump the use count a bit to account for this. michael@0: script_->incUseCount(5); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: return Invoke(cx, args_); michael@0: } michael@0: michael@0: private: michael@0: FastInvokeGuard(const FastInvokeGuard& other) MOZ_DELETE; michael@0: const FastInvokeGuard& operator=(const FastInvokeGuard& other) MOZ_DELETE; michael@0: }; michael@0: michael@0: } /* namespace js */ michael@0: michael@0: #endif /* vm_Interpreter_inl_h */