js/src/vm/Interpreter-inl.h

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #ifndef vm_Interpreter_inl_h
     8 #define vm_Interpreter_inl_h
    10 #include "vm/Interpreter.h"
    12 #include "jscompartment.h"
    13 #include "jsinfer.h"
    14 #include "jsnum.h"
    15 #include "jsstr.h"
    17 #include "jit/Ion.h"
    18 #include "vm/ArgumentsObject.h"
    19 #include "vm/ForkJoin.h"
    21 #include "jsatominlines.h"
    22 #include "jsinferinlines.h"
    23 #include "jsobjinlines.h"
    25 #include "vm/Stack-inl.h"
    26 #include "vm/String-inl.h"
    28 namespace js {
    30 inline bool
    31 ComputeThis(JSContext *cx, AbstractFramePtr frame)
    32 {
    33     JS_ASSERT_IF(frame.isInterpreterFrame(), !frame.asInterpreterFrame()->runningInJit());
    35     if (frame.isFunctionFrame() && frame.fun()->isArrow()) {
    36         /*
    37          * Arrow functions store their (lexical) |this| value in an
    38          * extended slot.
    39          */
    40         frame.thisValue() = frame.fun()->getExtendedSlot(0);
    41         return true;
    42     }
    44     if (frame.thisValue().isObject())
    45         return true;
    46     RootedValue thisv(cx, frame.thisValue());
    47     if (frame.isFunctionFrame()) {
    48         if (frame.fun()->strict() || frame.fun()->isSelfHostedBuiltin())
    49             return true;
    50         /*
    51          * Eval function frames have their own |this| slot, which is a copy of the function's
    52          * |this| slot. If we lazily wrap a primitive |this| in an eval function frame, the
    53          * eval's frame will get the wrapper, but the function's frame will not. To prevent
    54          * this, we always wrap a function's |this| before pushing an eval frame, and should
    55          * thus never see an unwrapped primitive in a non-strict eval function frame. Null
    56          * and undefined |this| values will unwrap to the same object in the function and
    57          * eval frames, so are not required to be wrapped.
    58          */
    59         JS_ASSERT_IF(frame.isEvalFrame(), thisv.isUndefined() || thisv.isNull());
    60     }
    62     JSObject *thisObj = BoxNonStrictThis(cx, thisv);
    63     if (!thisObj)
    64         return false;
    66     frame.thisValue().setObject(*thisObj);
    67     return true;
    68 }
    70 /*
    71  * Every possible consumer of MagicValue(JS_OPTIMIZED_ARGUMENTS) (as determined
    72  * by ScriptAnalysis::needsArgsObj) must check for these magic values and, when
    73  * one is received, act as if the value were the function's ArgumentsObject.
    74  * Additionally, it is possible that, after 'arguments' was copied into a
    75  * temporary, the arguments object has been created a some other failed guard
    76  * that called JSScript::argumentsOptimizationFailed. In this case, it is
    77  * always valid (and necessary) to replace JS_OPTIMIZED_ARGUMENTS with the real
    78  * arguments object.
    79  */
    80 static inline bool
    81 IsOptimizedArguments(AbstractFramePtr frame, Value *vp)
    82 {
    83     if (vp->isMagic(JS_OPTIMIZED_ARGUMENTS) && frame.script()->needsArgsObj())
    84         *vp = ObjectValue(frame.argsObj());
    85     return vp->isMagic(JS_OPTIMIZED_ARGUMENTS);
    86 }
    88 /*
    89  * One optimized consumer of MagicValue(JS_OPTIMIZED_ARGUMENTS) is f.apply.
    90  * However, this speculation must be guarded before calling 'apply' in case it
    91  * is not the builtin Function.prototype.apply.
    92  */
    93 static inline bool
    94 GuardFunApplyArgumentsOptimization(JSContext *cx, AbstractFramePtr frame, HandleValue callee,
    95                                    Value *args, uint32_t argc)
    96 {
    97     if (argc == 2 && IsOptimizedArguments(frame, &args[1])) {
    98         if (!IsNativeFunction(callee, js_fun_apply)) {
    99             RootedScript script(cx, frame.script());
   100             if (!JSScript::argumentsOptimizationFailed(cx, script))
   101                 return false;
   102             args[1] = ObjectValue(frame.argsObj());
   103         }
   104     }
   106     return true;
   107 }
   109 /*
   110  * Return an object on which we should look for the properties of |value|.
   111  * This helps us implement the custom [[Get]] method that ES5's GetValue
   112  * algorithm uses for primitive values, without actually constructing the
   113  * temporary object that the specification does.
   114  *
   115  * For objects, return the object itself. For string, boolean, and number
   116  * primitive values, return the appropriate constructor's prototype. For
   117  * undefined and null, throw an error and return nullptr, attributing the
   118  * problem to the value at |spindex| on the stack.
   119  */
   120 MOZ_ALWAYS_INLINE JSObject *
   121 ValuePropertyBearer(JSContext *cx, InterpreterFrame *fp, HandleValue v, int spindex)
   122 {
   123     if (v.isObject())
   124         return &v.toObject();
   126     Rooted<GlobalObject*> global(cx, &fp->global());
   128     if (v.isString())
   129         return GlobalObject::getOrCreateStringPrototype(cx, global);
   130     if (v.isNumber())
   131         return GlobalObject::getOrCreateNumberPrototype(cx, global);
   132     if (v.isBoolean())
   133         return GlobalObject::getOrCreateBooleanPrototype(cx, global);
   135     JS_ASSERT(v.isNull() || v.isUndefined());
   136     js_ReportIsNullOrUndefined(cx, spindex, v, NullPtr());
   137     return nullptr;
   138 }
   140 inline bool
   141 GetLengthProperty(const Value &lval, MutableHandleValue vp)
   142 {
   143     /* Optimize length accesses on strings, arrays, and arguments. */
   144     if (lval.isString()) {
   145         vp.setInt32(lval.toString()->length());
   146         return true;
   147     }
   148     if (lval.isObject()) {
   149         JSObject *obj = &lval.toObject();
   150         if (obj->is<ArrayObject>()) {
   151             vp.setNumber(obj->as<ArrayObject>().length());
   152             return true;
   153         }
   155         if (obj->is<ArgumentsObject>()) {
   156             ArgumentsObject *argsobj = &obj->as<ArgumentsObject>();
   157             if (!argsobj->hasOverriddenLength()) {
   158                 uint32_t length = argsobj->initialLength();
   159                 JS_ASSERT(length < INT32_MAX);
   160                 vp.setInt32(int32_t(length));
   161                 return true;
   162             }
   163         }
   164     }
   166     return false;
   167 }
   169 template <bool TypeOf> inline bool
   170 FetchName(JSContext *cx, HandleObject obj, HandleObject obj2, HandlePropertyName name,
   171           HandleShape shape, MutableHandleValue vp)
   172 {
   173     if (!shape) {
   174         if (TypeOf) {
   175             vp.setUndefined();
   176             return true;
   177         }
   178         JSAutoByteString printable;
   179         if (AtomToPrintableString(cx, name, &printable))
   180             js_ReportIsNotDefined(cx, printable.ptr());
   181         return false;
   182     }
   184     /* Take the slow path if shape was not found in a native object. */
   185     if (!obj->isNative() || !obj2->isNative()) {
   186         Rooted<jsid> id(cx, NameToId(name));
   187         if (!JSObject::getGeneric(cx, obj, obj, id, vp))
   188             return false;
   189     } else {
   190         Rooted<JSObject*> normalized(cx, obj);
   191         if (normalized->is<DynamicWithObject>() && !shape->hasDefaultGetter())
   192             normalized = &normalized->as<DynamicWithObject>().object();
   193         if (shape->isDataDescriptor() && shape->hasDefaultGetter()) {
   194             /* Fast path for Object instance properties. */
   195             JS_ASSERT(shape->hasSlot());
   196             vp.set(obj2->nativeGetSlot(shape->slot()));
   197         } else if (!NativeGet(cx, normalized, obj2, shape, vp)) {
   198             return false;
   199         }
   200     }
   201     return true;
   202 }
   204 inline bool
   205 FetchNameNoGC(JSObject *pobj, Shape *shape, MutableHandleValue vp)
   206 {
   207     if (!shape || !pobj->isNative() || !shape->isDataDescriptor() || !shape->hasDefaultGetter())
   208         return false;
   210     vp.set(pobj->nativeGetSlot(shape->slot()));
   211     return true;
   212 }
   214 inline bool
   215 GetIntrinsicOperation(JSContext *cx, jsbytecode *pc, MutableHandleValue vp)
   216 {
   217     RootedPropertyName name(cx, cx->currentScript()->getName(pc));
   218     return GlobalObject::getIntrinsicValue(cx, cx->global(), name, vp);
   219 }
   221 inline bool
   222 SetIntrinsicOperation(JSContext *cx, JSScript *script, jsbytecode *pc, HandleValue val)
   223 {
   224     RootedPropertyName name(cx, script->getName(pc));
   225     return cx->global()->setIntrinsicValue(cx, name, val);
   226 }
   228 inline bool
   229 SetNameOperation(JSContext *cx, JSScript *script, jsbytecode *pc, HandleObject scope,
   230                  HandleValue val)
   231 {
   232     JS_ASSERT(*pc == JSOP_SETNAME || *pc == JSOP_SETGNAME);
   233     JS_ASSERT_IF(*pc == JSOP_SETGNAME, scope == cx->global());
   235     bool strict = script->strict();
   236     RootedPropertyName name(cx, script->getName(pc));
   237     RootedValue valCopy(cx, val);
   239     /*
   240      * In strict-mode, we need to trigger an error when trying to assign to an
   241      * undeclared global variable. To do this, we call SetPropertyHelper
   242      * directly and pass Unqualified.
   243      */
   244     if (scope->is<GlobalObject>()) {
   245         JS_ASSERT(!scope->getOps()->setProperty);
   246         RootedId id(cx, NameToId(name));
   247         return baseops::SetPropertyHelper<SequentialExecution>(cx, scope, scope, id,
   248                                                                baseops::Unqualified, &valCopy,
   249                                                                strict);
   250     }
   252     return JSObject::setProperty(cx, scope, scope, name, &valCopy, strict);
   253 }
   255 inline bool
   256 DefVarOrConstOperation(JSContext *cx, HandleObject varobj, HandlePropertyName dn, unsigned attrs)
   257 {
   258     JS_ASSERT(varobj->isVarObj());
   260     RootedShape prop(cx);
   261     RootedObject obj2(cx);
   262     if (!JSObject::lookupProperty(cx, varobj, dn, &obj2, &prop))
   263         return false;
   265     /* Steps 8c, 8d. */
   266     if (!prop || (obj2 != varobj && varobj->is<GlobalObject>())) {
   267         if (!JSObject::defineProperty(cx, varobj, dn, UndefinedHandleValue, JS_PropertyStub,
   268                                       JS_StrictPropertyStub, attrs)) {
   269             return false;
   270         }
   271     } else if (attrs & JSPROP_READONLY) {
   272         /*
   273          * Extension: ordinarily we'd be done here -- but for |const|.  If we
   274          * see a redeclaration that's |const|, we consider it a conflict.
   275          */
   276         unsigned oldAttrs;
   277         RootedId id(cx, NameToId(dn));
   278         if (!JSObject::getGenericAttributes(cx, varobj, id, &oldAttrs))
   279             return false;
   281         JSAutoByteString bytes;
   282         if (AtomToPrintableString(cx, dn, &bytes)) {
   283             JS_ALWAYS_FALSE(JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
   284                                                          js_GetErrorMessage,
   285                                                          nullptr, JSMSG_REDECLARED_VAR,
   286                                                          (oldAttrs & JSPROP_READONLY)
   287                                                          ? "const"
   288                                                          : "var",
   289                                                          bytes.ptr()));
   290         }
   291         return false;
   292     }
   294     return true;
   295 }
   297 static MOZ_ALWAYS_INLINE bool
   298 NegOperation(JSContext *cx, HandleScript script, jsbytecode *pc, HandleValue val,
   299              MutableHandleValue res)
   300 {
   301     /*
   302      * When the operand is int jsval, INT32_FITS_IN_JSVAL(i) implies
   303      * INT32_FITS_IN_JSVAL(-i) unless i is 0 or INT32_MIN when the
   304      * results, -0.0 or INT32_MAX + 1, are double values.
   305      */
   306     int32_t i;
   307     if (val.isInt32() && (i = val.toInt32()) != 0 && i != INT32_MIN) {
   308         res.setInt32(-i);
   309     } else {
   310         double d;
   311         if (!ToNumber(cx, val, &d))
   312             return false;
   313         res.setNumber(-d);
   314     }
   316     return true;
   317 }
   319 static MOZ_ALWAYS_INLINE bool
   320 ToIdOperation(JSContext *cx, HandleScript script, jsbytecode *pc, HandleValue objval,
   321               HandleValue idval, MutableHandleValue res)
   322 {
   323     if (idval.isInt32()) {
   324         res.set(idval);
   325         return true;
   326     }
   328     JSObject *obj = ToObjectFromStack(cx, objval);
   329     if (!obj)
   330         return false;
   332     RootedId id(cx);
   333     if (!ValueToId<CanGC>(cx, idval, &id))
   334         return false;
   336     res.set(IdToValue(id));
   337     return true;
   338 }
   340 static MOZ_ALWAYS_INLINE bool
   341 GetObjectElementOperation(JSContext *cx, JSOp op, JSObject *objArg, bool wasObject,
   342                           HandleValue rref, MutableHandleValue res)
   343 {
   344     JS_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM);
   346     do {
   347         uint32_t index;
   348         if (IsDefinitelyIndex(rref, &index)) {
   349             if (JSObject::getElementNoGC(cx, objArg, objArg, index, res.address()))
   350                 break;
   352             RootedObject obj(cx, objArg);
   353             if (!JSObject::getElement(cx, obj, obj, index, res))
   354                 return false;
   355             objArg = obj;
   356             break;
   357         }
   359         JSAtom *name = ToAtom<NoGC>(cx, rref);
   360         if (name) {
   361             if (name->isIndex(&index)) {
   362                 if (JSObject::getElementNoGC(cx, objArg, objArg, index, res.address()))
   363                     break;
   364             } else {
   365                 if (JSObject::getPropertyNoGC(cx, objArg, objArg, name->asPropertyName(), res.address()))
   366                     break;
   367             }
   368         }
   370         RootedObject obj(cx, objArg);
   372         name = ToAtom<CanGC>(cx, rref);
   373         if (!name)
   374             return false;
   376         if (name->isIndex(&index)) {
   377             if (!JSObject::getElement(cx, obj, obj, index, res))
   378                 return false;
   379         } else {
   380             if (!JSObject::getProperty(cx, obj, obj, name->asPropertyName(), res))
   381                 return false;
   382         }
   384         objArg = obj;
   385     } while (0);
   387 #if JS_HAS_NO_SUCH_METHOD
   388     if (op == JSOP_CALLELEM && MOZ_UNLIKELY(res.isUndefined()) && wasObject) {
   389         RootedObject obj(cx, objArg);
   390         if (!OnUnknownMethod(cx, obj, rref, res))
   391             return false;
   392     }
   393 #endif
   395     assertSameCompartmentDebugOnly(cx, res);
   396     return true;
   397 }
   399 static MOZ_ALWAYS_INLINE bool
   400 GetElemOptimizedArguments(JSContext *cx, AbstractFramePtr frame, MutableHandleValue lref,
   401                           HandleValue rref, MutableHandleValue res, bool *done)
   402 {
   403     JS_ASSERT(!*done);
   405     if (IsOptimizedArguments(frame, lref.address())) {
   406         if (rref.isInt32()) {
   407             int32_t i = rref.toInt32();
   408             if (i >= 0 && uint32_t(i) < frame.numActualArgs()) {
   409                 res.set(frame.unaliasedActual(i));
   410                 *done = true;
   411                 return true;
   412             }
   413         }
   415         RootedScript script(cx, frame.script());
   416         if (!JSScript::argumentsOptimizationFailed(cx, script))
   417             return false;
   419         lref.set(ObjectValue(frame.argsObj()));
   420     }
   422     return true;
   423 }
   425 static MOZ_ALWAYS_INLINE bool
   426 GetElementOperation(JSContext *cx, JSOp op, MutableHandleValue lref, HandleValue rref,
   427                     MutableHandleValue res)
   428 {
   429     JS_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM);
   431     uint32_t index;
   432     if (lref.isString() && IsDefinitelyIndex(rref, &index)) {
   433         JSString *str = lref.toString();
   434         if (index < str->length()) {
   435             str = cx->staticStrings().getUnitStringForElement(cx, str, index);
   436             if (!str)
   437                 return false;
   438             res.setString(str);
   439             return true;
   440         }
   441     }
   443     bool isObject = lref.isObject();
   444     JSObject *obj = ToObjectFromStack(cx, lref);
   445     if (!obj)
   446         return false;
   447     return GetObjectElementOperation(cx, op, obj, isObject, rref, res);
   448 }
   450 static MOZ_ALWAYS_INLINE JSString *
   451 TypeOfOperation(const Value &v, JSRuntime *rt)
   452 {
   453     JSType type = js::TypeOfValue(v);
   454     return TypeName(type, *rt->commonNames);
   455 }
   457 static inline JSString *
   458 TypeOfObjectOperation(JSObject *obj, JSRuntime *rt)
   459 {
   460     JSType type = js::TypeOfObject(obj);
   461     return TypeName(type, *rt->commonNames);
   462 }
   464 static MOZ_ALWAYS_INLINE bool
   465 InitElemOperation(JSContext *cx, HandleObject obj, HandleValue idval, HandleValue val)
   466 {
   467     JS_ASSERT(!val.isMagic(JS_ELEMENTS_HOLE));
   469     RootedId id(cx);
   470     if (!ValueToId<CanGC>(cx, idval, &id))
   471         return false;
   473     return JSObject::defineGeneric(cx, obj, id, val, nullptr, nullptr, JSPROP_ENUMERATE);
   474 }
   476 static MOZ_ALWAYS_INLINE bool
   477 InitArrayElemOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, uint32_t index, HandleValue val)
   478 {
   479     JSOp op = JSOp(*pc);
   480     JS_ASSERT(op == JSOP_INITELEM_ARRAY || op == JSOP_INITELEM_INC);
   482     JS_ASSERT(obj->is<ArrayObject>());
   484     /*
   485      * If val is a hole, do not call JSObject::defineElement. In this case,
   486      * if the current op is the last element initialiser, set the array length
   487      * to one greater than id.
   488      */
   489     if (val.isMagic(JS_ELEMENTS_HOLE)) {
   490         JSOp next = JSOp(*GetNextPc(pc));
   492         if ((op == JSOP_INITELEM_ARRAY && next == JSOP_ENDINIT) ||
   493             (op == JSOP_INITELEM_INC && next == JSOP_POP))
   494         {
   495             if (!SetLengthProperty(cx, obj, index + 1))
   496                 return false;
   497         }
   498     } else {
   499         if (!JSObject::defineElement(cx, obj, index, val, nullptr, nullptr, JSPROP_ENUMERATE))
   500             return false;
   501     }
   503     if (op == JSOP_INITELEM_INC && index == INT32_MAX) {
   504         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SPREAD_TOO_LARGE);
   505         return false;
   506     }
   508     return true;
   509 }
   511 #define RELATIONAL_OP(OP)                                                     \
   512     JS_BEGIN_MACRO                                                            \
   513         /* Optimize for two int-tagged operands (typical loop control). */    \
   514         if (lhs.isInt32() && rhs.isInt32()) {                                 \
   515             *res = lhs.toInt32() OP rhs.toInt32();                            \
   516         } else {                                                              \
   517             if (!ToPrimitive(cx, JSTYPE_NUMBER, lhs))                         \
   518                 return false;                                                 \
   519             if (!ToPrimitive(cx, JSTYPE_NUMBER, rhs))                         \
   520                 return false;                                                 \
   521             if (lhs.isString() && rhs.isString()) {                           \
   522                 JSString *l = lhs.toString(), *r = rhs.toString();            \
   523                 int32_t result;                                               \
   524                 if (!CompareStrings(cx, l, r, &result))                       \
   525                     return false;                                             \
   526                 *res = result OP 0;                                           \
   527             } else {                                                          \
   528                 double l, r;                                                  \
   529                 if (!ToNumber(cx, lhs, &l) || !ToNumber(cx, rhs, &r))         \
   530                     return false;;                                            \
   531                 *res = (l OP r);                                              \
   532             }                                                                 \
   533         }                                                                     \
   534         return true;                                                          \
   535     JS_END_MACRO
   537 static MOZ_ALWAYS_INLINE bool
   538 LessThanOperation(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) {
   539     RELATIONAL_OP(<);
   540 }
   542 static MOZ_ALWAYS_INLINE bool
   543 LessThanOrEqualOperation(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) {
   544     RELATIONAL_OP(<=);
   545 }
   547 static MOZ_ALWAYS_INLINE bool
   548 GreaterThanOperation(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) {
   549     RELATIONAL_OP(>);
   550 }
   552 static MOZ_ALWAYS_INLINE bool
   553 GreaterThanOrEqualOperation(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) {
   554     RELATIONAL_OP(>=);
   555 }
   557 static MOZ_ALWAYS_INLINE bool
   558 BitNot(JSContext *cx, HandleValue in, int *out)
   559 {
   560     int i;
   561     if (!ToInt32(cx, in, &i))
   562         return false;
   563     *out = ~i;
   564     return true;
   565 }
   567 static MOZ_ALWAYS_INLINE bool
   568 BitXor(JSContext *cx, HandleValue lhs, HandleValue rhs, int *out)
   569 {
   570     int left, right;
   571     if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
   572         return false;
   573     *out = left ^ right;
   574     return true;
   575 }
   577 static MOZ_ALWAYS_INLINE bool
   578 BitOr(JSContext *cx, HandleValue lhs, HandleValue rhs, int *out)
   579 {
   580     int left, right;
   581     if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
   582         return false;
   583     *out = left | right;
   584     return true;
   585 }
   587 static MOZ_ALWAYS_INLINE bool
   588 BitAnd(JSContext *cx, HandleValue lhs, HandleValue rhs, int *out)
   589 {
   590     int left, right;
   591     if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
   592         return false;
   593     *out = left & right;
   594     return true;
   595 }
   597 static MOZ_ALWAYS_INLINE bool
   598 BitLsh(JSContext *cx, HandleValue lhs, HandleValue rhs, int *out)
   599 {
   600     int32_t left, right;
   601     if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
   602         return false;
   603     *out = uint32_t(left) << (right & 31);
   604     return true;
   605 }
   607 static MOZ_ALWAYS_INLINE bool
   608 BitRsh(JSContext *cx, HandleValue lhs, HandleValue rhs, int *out)
   609 {
   610     int32_t left, right;
   611     if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
   612         return false;
   613     *out = left >> (right & 31);
   614     return true;
   615 }
   617 static MOZ_ALWAYS_INLINE bool
   618 UrshOperation(JSContext *cx, HandleValue lhs, HandleValue rhs, MutableHandleValue out)
   619 {
   620     uint32_t left;
   621     int32_t  right;
   622     if (!ToUint32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
   623         return false;
   624     left >>= right & 31;
   625     out.setNumber(uint32_t(left));
   626     return true;
   627 }
   629 #undef RELATIONAL_OP
   631 inline JSFunction *
   632 ReportIfNotFunction(JSContext *cx, HandleValue v, MaybeConstruct construct = NO_CONSTRUCT)
   633 {
   634     if (v.isObject() && v.toObject().is<JSFunction>())
   635         return &v.toObject().as<JSFunction>();
   637     ReportIsNotFunction(cx, v, -1, construct);
   638     return nullptr;
   639 }
   641 /*
   642  * FastInvokeGuard is used to optimize calls to JS functions from natives written
   643  * in C++, for instance Array.map. If the callee is not Ion-compiled, this will
   644  * just call Invoke. If the callee has a valid IonScript, however, it will enter
   645  * Ion directly.
   646  */
   647 class FastInvokeGuard
   648 {
   649     InvokeArgs args_;
   650     RootedFunction fun_;
   651     RootedScript script_;
   652 #ifdef JS_ION
   653     // Constructing an IonContext is pretty expensive due to the TLS access,
   654     // so only do this if we have to.
   655     bool useIon_;
   656 #endif
   658   public:
   659     FastInvokeGuard(JSContext *cx, const Value &fval)
   660       : args_(cx)
   661       , fun_(cx)
   662       , script_(cx)
   663 #ifdef JS_ION
   664       , useIon_(jit::IsIonEnabled(cx))
   665 #endif
   666     {
   667         JS_ASSERT(!InParallelSection());
   668         initFunction(fval);
   669     }
   671     void initFunction(const Value &fval) {
   672         if (fval.isObject() && fval.toObject().is<JSFunction>()) {
   673             JSFunction *fun = &fval.toObject().as<JSFunction>();
   674             if (fun->isInterpreted())
   675                 fun_ = fun;
   676         }
   677     }
   679     InvokeArgs &args() {
   680         return args_;
   681     }
   683     bool invoke(JSContext *cx) {
   684 #ifdef JS_ION
   685         if (useIon_ && fun_) {
   686             if (!script_) {
   687                 script_ = fun_->getOrCreateScript(cx);
   688                 if (!script_)
   689                     return false;
   690             }
   691             JS_ASSERT(fun_->nonLazyScript() == script_);
   693             jit::MethodStatus status = jit::CanEnterUsingFastInvoke(cx, script_, args_.length());
   694             if (status == jit::Method_Error)
   695                 return false;
   696             if (status == jit::Method_Compiled) {
   697                 jit::IonExecStatus result = jit::FastInvoke(cx, fun_, args_);
   698                 if (IsErrorStatus(result))
   699                     return false;
   701                 JS_ASSERT(result == jit::IonExec_Ok);
   702                 return true;
   703             }
   705             JS_ASSERT(status == jit::Method_Skipped);
   707             if (script_->canIonCompile()) {
   708                 // This script is not yet hot. Since calling into Ion is much
   709                 // faster here, bump the use count a bit to account for this.
   710                 script_->incUseCount(5);
   711             }
   712         }
   713 #endif
   715         return Invoke(cx, args_);
   716     }
   718   private:
   719     FastInvokeGuard(const FastInvokeGuard& other) MOZ_DELETE;
   720     const FastInvokeGuard& operator=(const FastInvokeGuard& other) MOZ_DELETE;
   721 };
   723 }  /* namespace js */
   725 #endif /* vm_Interpreter_inl_h */

mercurial