diff --git a/js/src/vm/Interpreter-inl.h b/js/src/vm/Interpreter-inl.h index a9378506e182..1b275d9bdfac 100644 --- a/js/src/vm/Interpreter-inl.h +++ b/js/src/vm/Interpreter-inl.h @@ -28,50 +28,6 @@ namespace js { -/* - * Compute the implicit |this| parameter for a call expression where the callee - * funval was resolved from an unqualified name reference to a property on obj - * (an object on the scope chain). - * - * We can avoid computing |this| eagerly and push the implicit callee-coerced - * |this| value, undefined, if any of these conditions hold: - * - * 1. The nominal |this|, obj, is a global object. - * - * 2. The nominal |this|, obj, has one of Block, Call, or DeclEnv class (this - * is what IsCacheableNonGlobalScope tests). Such objects-as-scopes must be - * censored with undefined. - * - * Otherwise, we bind |this| to obj->thisObject(). Only names inside |with| - * statements and embedding-specific scope objects fall into this category. - * - * If the callee is a strict mode function, then code implementing JSOP_THIS - * in the interpreter and JITs will leave undefined as |this|. If funval is a - * function not in strict mode, JSOP_THIS code replaces undefined with funval's - * global. - * - * We set *vp to undefined early to reduce code size and bias this code for the - * common and future-friendly cases. - */ -inline bool -ComputeImplicitThis(JSContext *cx, HandleObject obj, MutableHandleValue vp) -{ - vp.setUndefined(); - - if (obj->is()) - return true; - - if (IsCacheableNonGlobalScope(obj)) - return true; - - JSObject *nobj = JSObject::thisObject(cx, obj); - if (!nobj) - return false; - - vp.setObject(*nobj); - return true; -} - inline bool ComputeThis(JSContext *cx, AbstractFramePtr frame) { @@ -345,154 +301,6 @@ DefVarOrConstOperation(JSContext *cx, HandleObject varobj, HandlePropertyName dn return true; } -inline bool -SetConstOperation(JSContext *cx, HandleObject varobj, HandlePropertyName name, HandleValue rval) -{ - return JSObject::defineProperty(cx, varobj, name, rval, - JS_PropertyStub, JS_StrictPropertyStub, - JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY); -} - -inline void -InterpreterFrames::enableInterruptsIfRunning(JSScript *script) -{ - if (regs->fp()->script() == script) - enabler.enable(); -} - -static JS_ALWAYS_INLINE bool -AddOperation(JSContext *cx, HandleScript script, jsbytecode *pc, - MutableHandleValue lhs, MutableHandleValue rhs, Value *res) -{ - if (lhs.isInt32() && rhs.isInt32()) { - int32_t l = lhs.toInt32(), r = rhs.toInt32(); - int32_t sum = l + r; - if (JS_UNLIKELY(bool((l ^ sum) & (r ^ sum) & 0x80000000))) { - res->setDouble(double(l) + double(r)); - types::TypeScript::MonitorOverflow(cx, script, pc); - } else { - res->setInt32(sum); - } - return true; - } - - /* - * If either operand is an object, any non-integer result must be - * reported to inference. - */ - bool lIsObject = lhs.isObject(), rIsObject = rhs.isObject(); - - if (!ToPrimitive(cx, lhs)) - return false; - if (!ToPrimitive(cx, rhs)) - return false; - bool lIsString, rIsString; - if ((lIsString = lhs.isString()) | (rIsString = rhs.isString())) { - JSString *lstr, *rstr; - if (lIsString) { - lstr = lhs.toString(); - } else { - lstr = ToString(cx, lhs); - if (!lstr) - return false; - } - if (rIsString) { - rstr = rhs.toString(); - } else { - // Save/restore lstr in case of GC activity under ToString. - lhs.setString(lstr); - rstr = ToString(cx, rhs); - if (!rstr) - return false; - lstr = lhs.toString(); - } - JSString *str = ConcatStrings(cx, lstr, rstr); - if (!str) { - RootedString nlstr(cx, lstr), nrstr(cx, rstr); - str = ConcatStrings(cx, nlstr, nrstr); - if (!str) - return false; - } - if (lIsObject || rIsObject) - types::TypeScript::MonitorString(cx, script, pc); - res->setString(str); - } else { - double l, r; - if (!ToNumber(cx, lhs, &l) || !ToNumber(cx, rhs, &r)) - return false; - l += r; - Value nres = NumberValue(l); - if (nres.isDouble() && - (lIsObject || rIsObject || (!lhs.isDouble() && !rhs.isDouble()))) { - types::TypeScript::MonitorOverflow(cx, script, pc); - } - *res = nres; - } - - return true; -} - -static JS_ALWAYS_INLINE bool -SubOperation(JSContext *cx, HandleScript script, jsbytecode *pc, HandleValue lhs, HandleValue rhs, - Value *res) -{ - double d1, d2; - if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2)) - return false; - double d = d1 - d2; - if (!res->setNumber(d) && !(lhs.isDouble() || rhs.isDouble())) - types::TypeScript::MonitorOverflow(cx, script, pc); - return true; -} - -static JS_ALWAYS_INLINE bool -MulOperation(JSContext *cx, HandleScript script, jsbytecode *pc, HandleValue lhs, HandleValue rhs, - Value *res) -{ - double d1, d2; - if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2)) - return false; - double d = d1 * d2; - if (!res->setNumber(d) && !(lhs.isDouble() || rhs.isDouble())) - types::TypeScript::MonitorOverflow(cx, script, pc); - return true; -} - -static JS_ALWAYS_INLINE bool -DivOperation(JSContext *cx, HandleScript script, jsbytecode *pc, HandleValue lhs, HandleValue rhs, - Value *res) -{ - double d1, d2; - if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2)) - return false; - res->setNumber(NumberDiv(d1, d2)); - - if (d2 == 0 || (res->isDouble() && !(lhs.isDouble() || rhs.isDouble()))) - types::TypeScript::MonitorOverflow(cx, script, pc); - return true; -} - -static JS_ALWAYS_INLINE bool -ModOperation(JSContext *cx, HandleScript script, jsbytecode *pc, HandleValue lhs, HandleValue rhs, - Value *res) -{ - int32_t l, r; - if (lhs.isInt32() && rhs.isInt32() && - (l = lhs.toInt32()) >= 0 && (r = rhs.toInt32()) > 0) { - int32_t mod = l % r; - res->setInt32(mod); - return true; - } - - double d1, d2; - if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2)) - return false; - - res->setNumber(NumberMod(d1, d2)); - types::TypeScript::MonitorOverflow(cx, script, pc); - return true; -} - static JS_ALWAYS_INLINE bool NegOperation(JSContext *cx, HandleScript script, jsbytecode *pc, HandleValue val, MutableHandleValue res) @@ -690,39 +498,6 @@ GetElementOperation(JSContext *cx, JSOp op, MutableHandleValue lref, HandleValue return GetObjectElementOperation(cx, op, obj, isObject, rref, res); } -static JS_ALWAYS_INLINE bool -SetObjectElementOperation(JSContext *cx, Handle obj, HandleId id, const Value &value, - bool strict, JSScript *maybeScript = NULL, jsbytecode *pc = NULL) -{ - RootedScript script(cx, maybeScript); - types::TypeScript::MonitorAssign(cx, obj, id); - - if (obj->isNative() && JSID_IS_INT(id)) { - uint32_t length = obj->getDenseInitializedLength(); - int32_t i = JSID_TO_INT(id); - if ((uint32_t)i >= length) { - // In an Ion activation, GetPcScript won't work. For non-baseline activations, - // that's ok, because optimized ion doesn't generate analysis info. However, - // baseline must generate this information, so it passes the script and pc in - // as arguments. - if (script || cx->currentlyRunningInInterpreter()) { - JS_ASSERT(!!script == !!pc); - if (!script) - types::TypeScript::GetPcScript(cx, script.address(), &pc); - - if (script->hasAnalysis()) - script->analysis()->getCode(pc).arrayWriteHole = true; - } - } - } - - if (obj->isNative() && !obj->setHadElementsAccess(cx)) - return false; - - RootedValue tmp(cx, value); - return JSObject::setGeneric(cx, obj, obj, id, &tmp, strict); -} - static JS_ALWAYS_INLINE JSString * TypeOfOperation(JSContext *cx, HandleValue v) { diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index f45a1a3907cb..9e01dc62bb66 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -1063,6 +1063,216 @@ FrameGuard::~FrameGuard() stack_->releaseFrame(fp_); } +/* + * Compute the implicit |this| parameter for a call expression where the callee + * funval was resolved from an unqualified name reference to a property on obj + * (an object on the scope chain). + * + * We can avoid computing |this| eagerly and push the implicit callee-coerced + * |this| value, undefined, if any of these conditions hold: + * + * 1. The nominal |this|, obj, is a global object. + * + * 2. The nominal |this|, obj, has one of Block, Call, or DeclEnv class (this + * is what IsCacheableNonGlobalScope tests). Such objects-as-scopes must be + * censored with undefined. + * + * Otherwise, we bind |this| to obj->thisObject(). Only names inside |with| + * statements and embedding-specific scope objects fall into this category. + * + * If the callee is a strict mode function, then code implementing JSOP_THIS + * in the interpreter and JITs will leave undefined as |this|. If funval is a + * function not in strict mode, JSOP_THIS code replaces undefined with funval's + * global. + * + * We set *vp to undefined early to reduce code size and bias this code for the + * common and future-friendly cases. + */ +inline bool +ComputeImplicitThis(JSContext *cx, HandleObject obj, MutableHandleValue vp) +{ + vp.setUndefined(); + + if (obj->is()) + return true; + + if (IsCacheableNonGlobalScope(obj)) + return true; + + JSObject *nobj = JSObject::thisObject(cx, obj); + if (!nobj) + return false; + + vp.setObject(*nobj); + return true; +} + +static JS_ALWAYS_INLINE bool +AddOperation(JSContext *cx, HandleScript script, jsbytecode *pc, + MutableHandleValue lhs, MutableHandleValue rhs, Value *res) +{ + if (lhs.isInt32() && rhs.isInt32()) { + int32_t l = lhs.toInt32(), r = rhs.toInt32(); + int32_t sum = l + r; + if (JS_UNLIKELY(bool((l ^ sum) & (r ^ sum) & 0x80000000))) { + res->setDouble(double(l) + double(r)); + types::TypeScript::MonitorOverflow(cx, script, pc); + } else { + res->setInt32(sum); + } + return true; + } + + /* + * If either operand is an object, any non-integer result must be + * reported to inference. + */ + bool lIsObject = lhs.isObject(), rIsObject = rhs.isObject(); + + if (!ToPrimitive(cx, lhs)) + return false; + if (!ToPrimitive(cx, rhs)) + return false; + bool lIsString, rIsString; + if ((lIsString = lhs.isString()) | (rIsString = rhs.isString())) { + JSString *lstr, *rstr; + if (lIsString) { + lstr = lhs.toString(); + } else { + lstr = ToString(cx, lhs); + if (!lstr) + return false; + } + if (rIsString) { + rstr = rhs.toString(); + } else { + // Save/restore lstr in case of GC activity under ToString. + lhs.setString(lstr); + rstr = ToString(cx, rhs); + if (!rstr) + return false; + lstr = lhs.toString(); + } + JSString *str = ConcatStrings(cx, lstr, rstr); + if (!str) { + RootedString nlstr(cx, lstr), nrstr(cx, rstr); + str = ConcatStrings(cx, nlstr, nrstr); + if (!str) + return false; + } + if (lIsObject || rIsObject) + types::TypeScript::MonitorString(cx, script, pc); + res->setString(str); + } else { + double l, r; + if (!ToNumber(cx, lhs, &l) || !ToNumber(cx, rhs, &r)) + return false; + l += r; + Value nres = NumberValue(l); + if (nres.isDouble() && + (lIsObject || rIsObject || (!lhs.isDouble() && !rhs.isDouble()))) { + types::TypeScript::MonitorOverflow(cx, script, pc); + } + *res = nres; + } + + return true; +} + +static JS_ALWAYS_INLINE bool +SubOperation(JSContext *cx, HandleScript script, jsbytecode *pc, HandleValue lhs, HandleValue rhs, + Value *res) +{ + double d1, d2; + if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2)) + return false; + double d = d1 - d2; + if (!res->setNumber(d) && !(lhs.isDouble() || rhs.isDouble())) + types::TypeScript::MonitorOverflow(cx, script, pc); + return true; +} + +static JS_ALWAYS_INLINE bool +MulOperation(JSContext *cx, HandleScript script, jsbytecode *pc, HandleValue lhs, HandleValue rhs, + Value *res) +{ + double d1, d2; + if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2)) + return false; + double d = d1 * d2; + if (!res->setNumber(d) && !(lhs.isDouble() || rhs.isDouble())) + types::TypeScript::MonitorOverflow(cx, script, pc); + return true; +} + +static JS_ALWAYS_INLINE bool +DivOperation(JSContext *cx, HandleScript script, jsbytecode *pc, HandleValue lhs, HandleValue rhs, + Value *res) +{ + double d1, d2; + if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2)) + return false; + res->setNumber(NumberDiv(d1, d2)); + + if (d2 == 0 || (res->isDouble() && !(lhs.isDouble() || rhs.isDouble()))) + types::TypeScript::MonitorOverflow(cx, script, pc); + return true; +} + +static JS_ALWAYS_INLINE bool +ModOperation(JSContext *cx, HandleScript script, jsbytecode *pc, HandleValue lhs, HandleValue rhs, + Value *res) +{ + int32_t l, r; + if (lhs.isInt32() && rhs.isInt32() && + (l = lhs.toInt32()) >= 0 && (r = rhs.toInt32()) > 0) { + int32_t mod = l % r; + res->setInt32(mod); + return true; + } + + double d1, d2; + if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2)) + return false; + + res->setNumber(NumberMod(d1, d2)); + types::TypeScript::MonitorOverflow(cx, script, pc); + return true; +} + +static JS_ALWAYS_INLINE bool +SetObjectElementOperation(JSContext *cx, Handle obj, HandleId id, const Value &value, + bool strict, JSScript *maybeScript = NULL, jsbytecode *pc = NULL) +{ + RootedScript script(cx, maybeScript); + types::TypeScript::MonitorAssign(cx, obj, id); + + if (obj->isNative() && JSID_IS_INT(id)) { + uint32_t length = obj->getDenseInitializedLength(); + int32_t i = JSID_TO_INT(id); + if ((uint32_t)i >= length) { + // In an Ion activation, GetPcScript won't work. For non-baseline activations, + // that's ok, because optimized ion doesn't generate analysis info. However, + // baseline must generate this information, so it passes the script and pc in + // as arguments. + if (script || cx->currentlyRunningInInterpreter()) { + JS_ASSERT(!!script == !!pc); + if (!script) + types::TypeScript::GetPcScript(cx, script.address(), &pc); + + if (script->hasAnalysis()) + script->analysis()->getCode(pc).arrayWriteHole = true; + } + } + } + + if (obj->isNative() && !obj->setHadElementsAccess(cx)) + return false; + + RootedValue tmp(cx, value); + return JSObject::setGeneric(cx, obj, obj, id, &tmp, strict); +} + static JS_NEVER_INLINE bool Interpret(JSContext *cx, RunState &state) { diff --git a/js/src/vm/Interpreter.h b/js/src/vm/Interpreter.h index 2497660c7cb6..e86251080534 100644 --- a/js/src/vm/Interpreter.h +++ b/js/src/vm/Interpreter.h @@ -350,8 +350,11 @@ class InterpreterFrames { ~InterpreterFrames(); /* If this js::Interpret frame is running |script|, enable interrupts. */ - inline void enableInterruptsIfRunning(JSScript *script); - inline void enableInterruptsUnconditionally() { enabler.enable(); } + void enableInterruptsIfRunning(JSScript *script) { + if (regs->fp()->script() == script) + enabler.enable(); + } + void enableInterruptsUnconditionally() { enabler.enable(); } InterpreterFrames *older; @@ -509,6 +512,14 @@ bool InitGetterSetterOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, HandleValue idval, HandleValue val); +inline bool +SetConstOperation(JSContext *cx, HandleObject varobj, HandlePropertyName name, HandleValue rval) +{ + return JSObject::defineProperty(cx, varobj, name, rval, + JS_PropertyStub, JS_StrictPropertyStub, + JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY); +} + } /* namespace js */ #endif /* vm_Interpreter_h */