1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/vm/Interpreter-inl.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,725 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#ifndef vm_Interpreter_inl_h 1.11 +#define vm_Interpreter_inl_h 1.12 + 1.13 +#include "vm/Interpreter.h" 1.14 + 1.15 +#include "jscompartment.h" 1.16 +#include "jsinfer.h" 1.17 +#include "jsnum.h" 1.18 +#include "jsstr.h" 1.19 + 1.20 +#include "jit/Ion.h" 1.21 +#include "vm/ArgumentsObject.h" 1.22 +#include "vm/ForkJoin.h" 1.23 + 1.24 +#include "jsatominlines.h" 1.25 +#include "jsinferinlines.h" 1.26 +#include "jsobjinlines.h" 1.27 + 1.28 +#include "vm/Stack-inl.h" 1.29 +#include "vm/String-inl.h" 1.30 + 1.31 +namespace js { 1.32 + 1.33 +inline bool 1.34 +ComputeThis(JSContext *cx, AbstractFramePtr frame) 1.35 +{ 1.36 + JS_ASSERT_IF(frame.isInterpreterFrame(), !frame.asInterpreterFrame()->runningInJit()); 1.37 + 1.38 + if (frame.isFunctionFrame() && frame.fun()->isArrow()) { 1.39 + /* 1.40 + * Arrow functions store their (lexical) |this| value in an 1.41 + * extended slot. 1.42 + */ 1.43 + frame.thisValue() = frame.fun()->getExtendedSlot(0); 1.44 + return true; 1.45 + } 1.46 + 1.47 + if (frame.thisValue().isObject()) 1.48 + return true; 1.49 + RootedValue thisv(cx, frame.thisValue()); 1.50 + if (frame.isFunctionFrame()) { 1.51 + if (frame.fun()->strict() || frame.fun()->isSelfHostedBuiltin()) 1.52 + return true; 1.53 + /* 1.54 + * Eval function frames have their own |this| slot, which is a copy of the function's 1.55 + * |this| slot. If we lazily wrap a primitive |this| in an eval function frame, the 1.56 + * eval's frame will get the wrapper, but the function's frame will not. To prevent 1.57 + * this, we always wrap a function's |this| before pushing an eval frame, and should 1.58 + * thus never see an unwrapped primitive in a non-strict eval function frame. Null 1.59 + * and undefined |this| values will unwrap to the same object in the function and 1.60 + * eval frames, so are not required to be wrapped. 1.61 + */ 1.62 + JS_ASSERT_IF(frame.isEvalFrame(), thisv.isUndefined() || thisv.isNull()); 1.63 + } 1.64 + 1.65 + JSObject *thisObj = BoxNonStrictThis(cx, thisv); 1.66 + if (!thisObj) 1.67 + return false; 1.68 + 1.69 + frame.thisValue().setObject(*thisObj); 1.70 + return true; 1.71 +} 1.72 + 1.73 +/* 1.74 + * Every possible consumer of MagicValue(JS_OPTIMIZED_ARGUMENTS) (as determined 1.75 + * by ScriptAnalysis::needsArgsObj) must check for these magic values and, when 1.76 + * one is received, act as if the value were the function's ArgumentsObject. 1.77 + * Additionally, it is possible that, after 'arguments' was copied into a 1.78 + * temporary, the arguments object has been created a some other failed guard 1.79 + * that called JSScript::argumentsOptimizationFailed. In this case, it is 1.80 + * always valid (and necessary) to replace JS_OPTIMIZED_ARGUMENTS with the real 1.81 + * arguments object. 1.82 + */ 1.83 +static inline bool 1.84 +IsOptimizedArguments(AbstractFramePtr frame, Value *vp) 1.85 +{ 1.86 + if (vp->isMagic(JS_OPTIMIZED_ARGUMENTS) && frame.script()->needsArgsObj()) 1.87 + *vp = ObjectValue(frame.argsObj()); 1.88 + return vp->isMagic(JS_OPTIMIZED_ARGUMENTS); 1.89 +} 1.90 + 1.91 +/* 1.92 + * One optimized consumer of MagicValue(JS_OPTIMIZED_ARGUMENTS) is f.apply. 1.93 + * However, this speculation must be guarded before calling 'apply' in case it 1.94 + * is not the builtin Function.prototype.apply. 1.95 + */ 1.96 +static inline bool 1.97 +GuardFunApplyArgumentsOptimization(JSContext *cx, AbstractFramePtr frame, HandleValue callee, 1.98 + Value *args, uint32_t argc) 1.99 +{ 1.100 + if (argc == 2 && IsOptimizedArguments(frame, &args[1])) { 1.101 + if (!IsNativeFunction(callee, js_fun_apply)) { 1.102 + RootedScript script(cx, frame.script()); 1.103 + if (!JSScript::argumentsOptimizationFailed(cx, script)) 1.104 + return false; 1.105 + args[1] = ObjectValue(frame.argsObj()); 1.106 + } 1.107 + } 1.108 + 1.109 + return true; 1.110 +} 1.111 + 1.112 +/* 1.113 + * Return an object on which we should look for the properties of |value|. 1.114 + * This helps us implement the custom [[Get]] method that ES5's GetValue 1.115 + * algorithm uses for primitive values, without actually constructing the 1.116 + * temporary object that the specification does. 1.117 + * 1.118 + * For objects, return the object itself. For string, boolean, and number 1.119 + * primitive values, return the appropriate constructor's prototype. For 1.120 + * undefined and null, throw an error and return nullptr, attributing the 1.121 + * problem to the value at |spindex| on the stack. 1.122 + */ 1.123 +MOZ_ALWAYS_INLINE JSObject * 1.124 +ValuePropertyBearer(JSContext *cx, InterpreterFrame *fp, HandleValue v, int spindex) 1.125 +{ 1.126 + if (v.isObject()) 1.127 + return &v.toObject(); 1.128 + 1.129 + Rooted<GlobalObject*> global(cx, &fp->global()); 1.130 + 1.131 + if (v.isString()) 1.132 + return GlobalObject::getOrCreateStringPrototype(cx, global); 1.133 + if (v.isNumber()) 1.134 + return GlobalObject::getOrCreateNumberPrototype(cx, global); 1.135 + if (v.isBoolean()) 1.136 + return GlobalObject::getOrCreateBooleanPrototype(cx, global); 1.137 + 1.138 + JS_ASSERT(v.isNull() || v.isUndefined()); 1.139 + js_ReportIsNullOrUndefined(cx, spindex, v, NullPtr()); 1.140 + return nullptr; 1.141 +} 1.142 + 1.143 +inline bool 1.144 +GetLengthProperty(const Value &lval, MutableHandleValue vp) 1.145 +{ 1.146 + /* Optimize length accesses on strings, arrays, and arguments. */ 1.147 + if (lval.isString()) { 1.148 + vp.setInt32(lval.toString()->length()); 1.149 + return true; 1.150 + } 1.151 + if (lval.isObject()) { 1.152 + JSObject *obj = &lval.toObject(); 1.153 + if (obj->is<ArrayObject>()) { 1.154 + vp.setNumber(obj->as<ArrayObject>().length()); 1.155 + return true; 1.156 + } 1.157 + 1.158 + if (obj->is<ArgumentsObject>()) { 1.159 + ArgumentsObject *argsobj = &obj->as<ArgumentsObject>(); 1.160 + if (!argsobj->hasOverriddenLength()) { 1.161 + uint32_t length = argsobj->initialLength(); 1.162 + JS_ASSERT(length < INT32_MAX); 1.163 + vp.setInt32(int32_t(length)); 1.164 + return true; 1.165 + } 1.166 + } 1.167 + } 1.168 + 1.169 + return false; 1.170 +} 1.171 + 1.172 +template <bool TypeOf> inline bool 1.173 +FetchName(JSContext *cx, HandleObject obj, HandleObject obj2, HandlePropertyName name, 1.174 + HandleShape shape, MutableHandleValue vp) 1.175 +{ 1.176 + if (!shape) { 1.177 + if (TypeOf) { 1.178 + vp.setUndefined(); 1.179 + return true; 1.180 + } 1.181 + JSAutoByteString printable; 1.182 + if (AtomToPrintableString(cx, name, &printable)) 1.183 + js_ReportIsNotDefined(cx, printable.ptr()); 1.184 + return false; 1.185 + } 1.186 + 1.187 + /* Take the slow path if shape was not found in a native object. */ 1.188 + if (!obj->isNative() || !obj2->isNative()) { 1.189 + Rooted<jsid> id(cx, NameToId(name)); 1.190 + if (!JSObject::getGeneric(cx, obj, obj, id, vp)) 1.191 + return false; 1.192 + } else { 1.193 + Rooted<JSObject*> normalized(cx, obj); 1.194 + if (normalized->is<DynamicWithObject>() && !shape->hasDefaultGetter()) 1.195 + normalized = &normalized->as<DynamicWithObject>().object(); 1.196 + if (shape->isDataDescriptor() && shape->hasDefaultGetter()) { 1.197 + /* Fast path for Object instance properties. */ 1.198 + JS_ASSERT(shape->hasSlot()); 1.199 + vp.set(obj2->nativeGetSlot(shape->slot())); 1.200 + } else if (!NativeGet(cx, normalized, obj2, shape, vp)) { 1.201 + return false; 1.202 + } 1.203 + } 1.204 + return true; 1.205 +} 1.206 + 1.207 +inline bool 1.208 +FetchNameNoGC(JSObject *pobj, Shape *shape, MutableHandleValue vp) 1.209 +{ 1.210 + if (!shape || !pobj->isNative() || !shape->isDataDescriptor() || !shape->hasDefaultGetter()) 1.211 + return false; 1.212 + 1.213 + vp.set(pobj->nativeGetSlot(shape->slot())); 1.214 + return true; 1.215 +} 1.216 + 1.217 +inline bool 1.218 +GetIntrinsicOperation(JSContext *cx, jsbytecode *pc, MutableHandleValue vp) 1.219 +{ 1.220 + RootedPropertyName name(cx, cx->currentScript()->getName(pc)); 1.221 + return GlobalObject::getIntrinsicValue(cx, cx->global(), name, vp); 1.222 +} 1.223 + 1.224 +inline bool 1.225 +SetIntrinsicOperation(JSContext *cx, JSScript *script, jsbytecode *pc, HandleValue val) 1.226 +{ 1.227 + RootedPropertyName name(cx, script->getName(pc)); 1.228 + return cx->global()->setIntrinsicValue(cx, name, val); 1.229 +} 1.230 + 1.231 +inline bool 1.232 +SetNameOperation(JSContext *cx, JSScript *script, jsbytecode *pc, HandleObject scope, 1.233 + HandleValue val) 1.234 +{ 1.235 + JS_ASSERT(*pc == JSOP_SETNAME || *pc == JSOP_SETGNAME); 1.236 + JS_ASSERT_IF(*pc == JSOP_SETGNAME, scope == cx->global()); 1.237 + 1.238 + bool strict = script->strict(); 1.239 + RootedPropertyName name(cx, script->getName(pc)); 1.240 + RootedValue valCopy(cx, val); 1.241 + 1.242 + /* 1.243 + * In strict-mode, we need to trigger an error when trying to assign to an 1.244 + * undeclared global variable. To do this, we call SetPropertyHelper 1.245 + * directly and pass Unqualified. 1.246 + */ 1.247 + if (scope->is<GlobalObject>()) { 1.248 + JS_ASSERT(!scope->getOps()->setProperty); 1.249 + RootedId id(cx, NameToId(name)); 1.250 + return baseops::SetPropertyHelper<SequentialExecution>(cx, scope, scope, id, 1.251 + baseops::Unqualified, &valCopy, 1.252 + strict); 1.253 + } 1.254 + 1.255 + return JSObject::setProperty(cx, scope, scope, name, &valCopy, strict); 1.256 +} 1.257 + 1.258 +inline bool 1.259 +DefVarOrConstOperation(JSContext *cx, HandleObject varobj, HandlePropertyName dn, unsigned attrs) 1.260 +{ 1.261 + JS_ASSERT(varobj->isVarObj()); 1.262 + 1.263 + RootedShape prop(cx); 1.264 + RootedObject obj2(cx); 1.265 + if (!JSObject::lookupProperty(cx, varobj, dn, &obj2, &prop)) 1.266 + return false; 1.267 + 1.268 + /* Steps 8c, 8d. */ 1.269 + if (!prop || (obj2 != varobj && varobj->is<GlobalObject>())) { 1.270 + if (!JSObject::defineProperty(cx, varobj, dn, UndefinedHandleValue, JS_PropertyStub, 1.271 + JS_StrictPropertyStub, attrs)) { 1.272 + return false; 1.273 + } 1.274 + } else if (attrs & JSPROP_READONLY) { 1.275 + /* 1.276 + * Extension: ordinarily we'd be done here -- but for |const|. If we 1.277 + * see a redeclaration that's |const|, we consider it a conflict. 1.278 + */ 1.279 + unsigned oldAttrs; 1.280 + RootedId id(cx, NameToId(dn)); 1.281 + if (!JSObject::getGenericAttributes(cx, varobj, id, &oldAttrs)) 1.282 + return false; 1.283 + 1.284 + JSAutoByteString bytes; 1.285 + if (AtomToPrintableString(cx, dn, &bytes)) { 1.286 + JS_ALWAYS_FALSE(JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, 1.287 + js_GetErrorMessage, 1.288 + nullptr, JSMSG_REDECLARED_VAR, 1.289 + (oldAttrs & JSPROP_READONLY) 1.290 + ? "const" 1.291 + : "var", 1.292 + bytes.ptr())); 1.293 + } 1.294 + return false; 1.295 + } 1.296 + 1.297 + return true; 1.298 +} 1.299 + 1.300 +static MOZ_ALWAYS_INLINE bool 1.301 +NegOperation(JSContext *cx, HandleScript script, jsbytecode *pc, HandleValue val, 1.302 + MutableHandleValue res) 1.303 +{ 1.304 + /* 1.305 + * When the operand is int jsval, INT32_FITS_IN_JSVAL(i) implies 1.306 + * INT32_FITS_IN_JSVAL(-i) unless i is 0 or INT32_MIN when the 1.307 + * results, -0.0 or INT32_MAX + 1, are double values. 1.308 + */ 1.309 + int32_t i; 1.310 + if (val.isInt32() && (i = val.toInt32()) != 0 && i != INT32_MIN) { 1.311 + res.setInt32(-i); 1.312 + } else { 1.313 + double d; 1.314 + if (!ToNumber(cx, val, &d)) 1.315 + return false; 1.316 + res.setNumber(-d); 1.317 + } 1.318 + 1.319 + return true; 1.320 +} 1.321 + 1.322 +static MOZ_ALWAYS_INLINE bool 1.323 +ToIdOperation(JSContext *cx, HandleScript script, jsbytecode *pc, HandleValue objval, 1.324 + HandleValue idval, MutableHandleValue res) 1.325 +{ 1.326 + if (idval.isInt32()) { 1.327 + res.set(idval); 1.328 + return true; 1.329 + } 1.330 + 1.331 + JSObject *obj = ToObjectFromStack(cx, objval); 1.332 + if (!obj) 1.333 + return false; 1.334 + 1.335 + RootedId id(cx); 1.336 + if (!ValueToId<CanGC>(cx, idval, &id)) 1.337 + return false; 1.338 + 1.339 + res.set(IdToValue(id)); 1.340 + return true; 1.341 +} 1.342 + 1.343 +static MOZ_ALWAYS_INLINE bool 1.344 +GetObjectElementOperation(JSContext *cx, JSOp op, JSObject *objArg, bool wasObject, 1.345 + HandleValue rref, MutableHandleValue res) 1.346 +{ 1.347 + JS_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM); 1.348 + 1.349 + do { 1.350 + uint32_t index; 1.351 + if (IsDefinitelyIndex(rref, &index)) { 1.352 + if (JSObject::getElementNoGC(cx, objArg, objArg, index, res.address())) 1.353 + break; 1.354 + 1.355 + RootedObject obj(cx, objArg); 1.356 + if (!JSObject::getElement(cx, obj, obj, index, res)) 1.357 + return false; 1.358 + objArg = obj; 1.359 + break; 1.360 + } 1.361 + 1.362 + JSAtom *name = ToAtom<NoGC>(cx, rref); 1.363 + if (name) { 1.364 + if (name->isIndex(&index)) { 1.365 + if (JSObject::getElementNoGC(cx, objArg, objArg, index, res.address())) 1.366 + break; 1.367 + } else { 1.368 + if (JSObject::getPropertyNoGC(cx, objArg, objArg, name->asPropertyName(), res.address())) 1.369 + break; 1.370 + } 1.371 + } 1.372 + 1.373 + RootedObject obj(cx, objArg); 1.374 + 1.375 + name = ToAtom<CanGC>(cx, rref); 1.376 + if (!name) 1.377 + return false; 1.378 + 1.379 + if (name->isIndex(&index)) { 1.380 + if (!JSObject::getElement(cx, obj, obj, index, res)) 1.381 + return false; 1.382 + } else { 1.383 + if (!JSObject::getProperty(cx, obj, obj, name->asPropertyName(), res)) 1.384 + return false; 1.385 + } 1.386 + 1.387 + objArg = obj; 1.388 + } while (0); 1.389 + 1.390 +#if JS_HAS_NO_SUCH_METHOD 1.391 + if (op == JSOP_CALLELEM && MOZ_UNLIKELY(res.isUndefined()) && wasObject) { 1.392 + RootedObject obj(cx, objArg); 1.393 + if (!OnUnknownMethod(cx, obj, rref, res)) 1.394 + return false; 1.395 + } 1.396 +#endif 1.397 + 1.398 + assertSameCompartmentDebugOnly(cx, res); 1.399 + return true; 1.400 +} 1.401 + 1.402 +static MOZ_ALWAYS_INLINE bool 1.403 +GetElemOptimizedArguments(JSContext *cx, AbstractFramePtr frame, MutableHandleValue lref, 1.404 + HandleValue rref, MutableHandleValue res, bool *done) 1.405 +{ 1.406 + JS_ASSERT(!*done); 1.407 + 1.408 + if (IsOptimizedArguments(frame, lref.address())) { 1.409 + if (rref.isInt32()) { 1.410 + int32_t i = rref.toInt32(); 1.411 + if (i >= 0 && uint32_t(i) < frame.numActualArgs()) { 1.412 + res.set(frame.unaliasedActual(i)); 1.413 + *done = true; 1.414 + return true; 1.415 + } 1.416 + } 1.417 + 1.418 + RootedScript script(cx, frame.script()); 1.419 + if (!JSScript::argumentsOptimizationFailed(cx, script)) 1.420 + return false; 1.421 + 1.422 + lref.set(ObjectValue(frame.argsObj())); 1.423 + } 1.424 + 1.425 + return true; 1.426 +} 1.427 + 1.428 +static MOZ_ALWAYS_INLINE bool 1.429 +GetElementOperation(JSContext *cx, JSOp op, MutableHandleValue lref, HandleValue rref, 1.430 + MutableHandleValue res) 1.431 +{ 1.432 + JS_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM); 1.433 + 1.434 + uint32_t index; 1.435 + if (lref.isString() && IsDefinitelyIndex(rref, &index)) { 1.436 + JSString *str = lref.toString(); 1.437 + if (index < str->length()) { 1.438 + str = cx->staticStrings().getUnitStringForElement(cx, str, index); 1.439 + if (!str) 1.440 + return false; 1.441 + res.setString(str); 1.442 + return true; 1.443 + } 1.444 + } 1.445 + 1.446 + bool isObject = lref.isObject(); 1.447 + JSObject *obj = ToObjectFromStack(cx, lref); 1.448 + if (!obj) 1.449 + return false; 1.450 + return GetObjectElementOperation(cx, op, obj, isObject, rref, res); 1.451 +} 1.452 + 1.453 +static MOZ_ALWAYS_INLINE JSString * 1.454 +TypeOfOperation(const Value &v, JSRuntime *rt) 1.455 +{ 1.456 + JSType type = js::TypeOfValue(v); 1.457 + return TypeName(type, *rt->commonNames); 1.458 +} 1.459 + 1.460 +static inline JSString * 1.461 +TypeOfObjectOperation(JSObject *obj, JSRuntime *rt) 1.462 +{ 1.463 + JSType type = js::TypeOfObject(obj); 1.464 + return TypeName(type, *rt->commonNames); 1.465 +} 1.466 + 1.467 +static MOZ_ALWAYS_INLINE bool 1.468 +InitElemOperation(JSContext *cx, HandleObject obj, HandleValue idval, HandleValue val) 1.469 +{ 1.470 + JS_ASSERT(!val.isMagic(JS_ELEMENTS_HOLE)); 1.471 + 1.472 + RootedId id(cx); 1.473 + if (!ValueToId<CanGC>(cx, idval, &id)) 1.474 + return false; 1.475 + 1.476 + return JSObject::defineGeneric(cx, obj, id, val, nullptr, nullptr, JSPROP_ENUMERATE); 1.477 +} 1.478 + 1.479 +static MOZ_ALWAYS_INLINE bool 1.480 +InitArrayElemOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, uint32_t index, HandleValue val) 1.481 +{ 1.482 + JSOp op = JSOp(*pc); 1.483 + JS_ASSERT(op == JSOP_INITELEM_ARRAY || op == JSOP_INITELEM_INC); 1.484 + 1.485 + JS_ASSERT(obj->is<ArrayObject>()); 1.486 + 1.487 + /* 1.488 + * If val is a hole, do not call JSObject::defineElement. In this case, 1.489 + * if the current op is the last element initialiser, set the array length 1.490 + * to one greater than id. 1.491 + */ 1.492 + if (val.isMagic(JS_ELEMENTS_HOLE)) { 1.493 + JSOp next = JSOp(*GetNextPc(pc)); 1.494 + 1.495 + if ((op == JSOP_INITELEM_ARRAY && next == JSOP_ENDINIT) || 1.496 + (op == JSOP_INITELEM_INC && next == JSOP_POP)) 1.497 + { 1.498 + if (!SetLengthProperty(cx, obj, index + 1)) 1.499 + return false; 1.500 + } 1.501 + } else { 1.502 + if (!JSObject::defineElement(cx, obj, index, val, nullptr, nullptr, JSPROP_ENUMERATE)) 1.503 + return false; 1.504 + } 1.505 + 1.506 + if (op == JSOP_INITELEM_INC && index == INT32_MAX) { 1.507 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SPREAD_TOO_LARGE); 1.508 + return false; 1.509 + } 1.510 + 1.511 + return true; 1.512 +} 1.513 + 1.514 +#define RELATIONAL_OP(OP) \ 1.515 + JS_BEGIN_MACRO \ 1.516 + /* Optimize for two int-tagged operands (typical loop control). */ \ 1.517 + if (lhs.isInt32() && rhs.isInt32()) { \ 1.518 + *res = lhs.toInt32() OP rhs.toInt32(); \ 1.519 + } else { \ 1.520 + if (!ToPrimitive(cx, JSTYPE_NUMBER, lhs)) \ 1.521 + return false; \ 1.522 + if (!ToPrimitive(cx, JSTYPE_NUMBER, rhs)) \ 1.523 + return false; \ 1.524 + if (lhs.isString() && rhs.isString()) { \ 1.525 + JSString *l = lhs.toString(), *r = rhs.toString(); \ 1.526 + int32_t result; \ 1.527 + if (!CompareStrings(cx, l, r, &result)) \ 1.528 + return false; \ 1.529 + *res = result OP 0; \ 1.530 + } else { \ 1.531 + double l, r; \ 1.532 + if (!ToNumber(cx, lhs, &l) || !ToNumber(cx, rhs, &r)) \ 1.533 + return false;; \ 1.534 + *res = (l OP r); \ 1.535 + } \ 1.536 + } \ 1.537 + return true; \ 1.538 + JS_END_MACRO 1.539 + 1.540 +static MOZ_ALWAYS_INLINE bool 1.541 +LessThanOperation(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) { 1.542 + RELATIONAL_OP(<); 1.543 +} 1.544 + 1.545 +static MOZ_ALWAYS_INLINE bool 1.546 +LessThanOrEqualOperation(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) { 1.547 + RELATIONAL_OP(<=); 1.548 +} 1.549 + 1.550 +static MOZ_ALWAYS_INLINE bool 1.551 +GreaterThanOperation(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) { 1.552 + RELATIONAL_OP(>); 1.553 +} 1.554 + 1.555 +static MOZ_ALWAYS_INLINE bool 1.556 +GreaterThanOrEqualOperation(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) { 1.557 + RELATIONAL_OP(>=); 1.558 +} 1.559 + 1.560 +static MOZ_ALWAYS_INLINE bool 1.561 +BitNot(JSContext *cx, HandleValue in, int *out) 1.562 +{ 1.563 + int i; 1.564 + if (!ToInt32(cx, in, &i)) 1.565 + return false; 1.566 + *out = ~i; 1.567 + return true; 1.568 +} 1.569 + 1.570 +static MOZ_ALWAYS_INLINE bool 1.571 +BitXor(JSContext *cx, HandleValue lhs, HandleValue rhs, int *out) 1.572 +{ 1.573 + int left, right; 1.574 + if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right)) 1.575 + return false; 1.576 + *out = left ^ right; 1.577 + return true; 1.578 +} 1.579 + 1.580 +static MOZ_ALWAYS_INLINE bool 1.581 +BitOr(JSContext *cx, HandleValue lhs, HandleValue rhs, int *out) 1.582 +{ 1.583 + int left, right; 1.584 + if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right)) 1.585 + return false; 1.586 + *out = left | right; 1.587 + return true; 1.588 +} 1.589 + 1.590 +static MOZ_ALWAYS_INLINE bool 1.591 +BitAnd(JSContext *cx, HandleValue lhs, HandleValue rhs, int *out) 1.592 +{ 1.593 + int left, right; 1.594 + if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right)) 1.595 + return false; 1.596 + *out = left & right; 1.597 + return true; 1.598 +} 1.599 + 1.600 +static MOZ_ALWAYS_INLINE bool 1.601 +BitLsh(JSContext *cx, HandleValue lhs, HandleValue rhs, int *out) 1.602 +{ 1.603 + int32_t left, right; 1.604 + if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right)) 1.605 + return false; 1.606 + *out = uint32_t(left) << (right & 31); 1.607 + return true; 1.608 +} 1.609 + 1.610 +static MOZ_ALWAYS_INLINE bool 1.611 +BitRsh(JSContext *cx, HandleValue lhs, HandleValue rhs, int *out) 1.612 +{ 1.613 + int32_t left, right; 1.614 + if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right)) 1.615 + return false; 1.616 + *out = left >> (right & 31); 1.617 + return true; 1.618 +} 1.619 + 1.620 +static MOZ_ALWAYS_INLINE bool 1.621 +UrshOperation(JSContext *cx, HandleValue lhs, HandleValue rhs, MutableHandleValue out) 1.622 +{ 1.623 + uint32_t left; 1.624 + int32_t right; 1.625 + if (!ToUint32(cx, lhs, &left) || !ToInt32(cx, rhs, &right)) 1.626 + return false; 1.627 + left >>= right & 31; 1.628 + out.setNumber(uint32_t(left)); 1.629 + return true; 1.630 +} 1.631 + 1.632 +#undef RELATIONAL_OP 1.633 + 1.634 +inline JSFunction * 1.635 +ReportIfNotFunction(JSContext *cx, HandleValue v, MaybeConstruct construct = NO_CONSTRUCT) 1.636 +{ 1.637 + if (v.isObject() && v.toObject().is<JSFunction>()) 1.638 + return &v.toObject().as<JSFunction>(); 1.639 + 1.640 + ReportIsNotFunction(cx, v, -1, construct); 1.641 + return nullptr; 1.642 +} 1.643 + 1.644 +/* 1.645 + * FastInvokeGuard is used to optimize calls to JS functions from natives written 1.646 + * in C++, for instance Array.map. If the callee is not Ion-compiled, this will 1.647 + * just call Invoke. If the callee has a valid IonScript, however, it will enter 1.648 + * Ion directly. 1.649 + */ 1.650 +class FastInvokeGuard 1.651 +{ 1.652 + InvokeArgs args_; 1.653 + RootedFunction fun_; 1.654 + RootedScript script_; 1.655 +#ifdef JS_ION 1.656 + // Constructing an IonContext is pretty expensive due to the TLS access, 1.657 + // so only do this if we have to. 1.658 + bool useIon_; 1.659 +#endif 1.660 + 1.661 + public: 1.662 + FastInvokeGuard(JSContext *cx, const Value &fval) 1.663 + : args_(cx) 1.664 + , fun_(cx) 1.665 + , script_(cx) 1.666 +#ifdef JS_ION 1.667 + , useIon_(jit::IsIonEnabled(cx)) 1.668 +#endif 1.669 + { 1.670 + JS_ASSERT(!InParallelSection()); 1.671 + initFunction(fval); 1.672 + } 1.673 + 1.674 + void initFunction(const Value &fval) { 1.675 + if (fval.isObject() && fval.toObject().is<JSFunction>()) { 1.676 + JSFunction *fun = &fval.toObject().as<JSFunction>(); 1.677 + if (fun->isInterpreted()) 1.678 + fun_ = fun; 1.679 + } 1.680 + } 1.681 + 1.682 + InvokeArgs &args() { 1.683 + return args_; 1.684 + } 1.685 + 1.686 + bool invoke(JSContext *cx) { 1.687 +#ifdef JS_ION 1.688 + if (useIon_ && fun_) { 1.689 + if (!script_) { 1.690 + script_ = fun_->getOrCreateScript(cx); 1.691 + if (!script_) 1.692 + return false; 1.693 + } 1.694 + JS_ASSERT(fun_->nonLazyScript() == script_); 1.695 + 1.696 + jit::MethodStatus status = jit::CanEnterUsingFastInvoke(cx, script_, args_.length()); 1.697 + if (status == jit::Method_Error) 1.698 + return false; 1.699 + if (status == jit::Method_Compiled) { 1.700 + jit::IonExecStatus result = jit::FastInvoke(cx, fun_, args_); 1.701 + if (IsErrorStatus(result)) 1.702 + return false; 1.703 + 1.704 + JS_ASSERT(result == jit::IonExec_Ok); 1.705 + return true; 1.706 + } 1.707 + 1.708 + JS_ASSERT(status == jit::Method_Skipped); 1.709 + 1.710 + if (script_->canIonCompile()) { 1.711 + // This script is not yet hot. Since calling into Ion is much 1.712 + // faster here, bump the use count a bit to account for this. 1.713 + script_->incUseCount(5); 1.714 + } 1.715 + } 1.716 +#endif 1.717 + 1.718 + return Invoke(cx, args_); 1.719 + } 1.720 + 1.721 + private: 1.722 + FastInvokeGuard(const FastInvokeGuard& other) MOZ_DELETE; 1.723 + const FastInvokeGuard& operator=(const FastInvokeGuard& other) MOZ_DELETE; 1.724 +}; 1.725 + 1.726 +} /* namespace js */ 1.727 + 1.728 +#endif /* vm_Interpreter_inl_h */