LCOV - code coverage report
Current view: top level - js/src/vm - Interpreter-inl.h (source / functions) Hit Total Coverage
Test: output.info Lines: 255 375 68.0 %
Date: 2017-07-14 16:53:18 Functions: 37 48 77.1 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : #ifndef vm_Interpreter_inl_h
       8             : #define vm_Interpreter_inl_h
       9             : 
      10             : #include "vm/Interpreter.h"
      11             : 
      12             : #include "jscompartment.h"
      13             : #include "jsnum.h"
      14             : #include "jsstr.h"
      15             : 
      16             : #include "jit/Ion.h"
      17             : #include "vm/ArgumentsObject.h"
      18             : 
      19             : #include "jsatominlines.h"
      20             : #include "jsobjinlines.h"
      21             : 
      22             : #include "vm/EnvironmentObject-inl.h"
      23             : #include "vm/Stack-inl.h"
      24             : #include "vm/String-inl.h"
      25             : #include "vm/UnboxedObject-inl.h"
      26             : 
      27             : namespace js {
      28             : 
      29             : /*
      30             :  * Every possible consumer of MagicValue(JS_OPTIMIZED_ARGUMENTS) (as determined
      31             :  * by ScriptAnalysis::needsArgsObj) must check for these magic values and, when
      32             :  * one is received, act as if the value were the function's ArgumentsObject.
      33             :  * Additionally, it is possible that, after 'arguments' was copied into a
      34             :  * temporary, the arguments object has been created a some other failed guard
      35             :  * that called JSScript::argumentsOptimizationFailed. In this case, it is
      36             :  * always valid (and necessary) to replace JS_OPTIMIZED_ARGUMENTS with the real
      37             :  * arguments object.
      38             :  */
      39             : static inline bool
      40        5478 : IsOptimizedArguments(AbstractFramePtr frame, MutableHandleValue vp)
      41             : {
      42        5478 :     if (vp.isMagic(JS_OPTIMIZED_ARGUMENTS) && frame.script()->needsArgsObj())
      43           0 :         vp.setObject(frame.argsObj());
      44        5478 :     return vp.isMagic(JS_OPTIMIZED_ARGUMENTS);
      45             : }
      46             : 
      47             : /*
      48             :  * One optimized consumer of MagicValue(JS_OPTIMIZED_ARGUMENTS) is f.apply.
      49             :  * However, this speculation must be guarded before calling 'apply' in case it
      50             :  * is not the builtin Function.prototype.apply.
      51             :  */
      52             : static inline bool
      53         113 : GuardFunApplyArgumentsOptimization(JSContext* cx, AbstractFramePtr frame, CallArgs& args)
      54             : {
      55         113 :     if (args.length() == 2 && IsOptimizedArguments(frame, args[1])) {
      56           0 :         if (!IsNativeFunction(args.calleev(), js::fun_apply)) {
      57           0 :             RootedScript script(cx, frame.script());
      58           0 :             if (!JSScript::argumentsOptimizationFailed(cx, script))
      59           0 :                 return false;
      60           0 :             args[1].setObject(frame.argsObj());
      61             :         }
      62             :     }
      63             : 
      64         113 :     return true;
      65             : }
      66             : 
      67             : /*
      68             :  * Per ES6, lexical declarations may not be accessed in any fashion until they
      69             :  * are initialized (i.e., until the actual declaring statement is
      70             :  * executed). The various LEXICAL opcodes need to check if the slot is an
      71             :  * uninitialized let declaration, represented by the magic value
      72             :  * JS_UNINITIALIZED_LEXICAL.
      73             :  */
      74             : static inline bool
      75       82946 : IsUninitializedLexical(const Value& val)
      76             : {
      77             :     // Use whyMagic here because JS_OPTIMIZED_ARGUMENTS could flow into here.
      78       82946 :     return val.isMagic() && val.whyMagic() == JS_UNINITIALIZED_LEXICAL;
      79             : }
      80             : 
      81             : static inline bool
      82          44 : IsUninitializedLexicalSlot(HandleObject obj, Handle<PropertyResult> prop)
      83             : {
      84          44 :     MOZ_ASSERT(prop);
      85          44 :     if (obj->is<WithEnvironmentObject>())
      86           0 :         return false;
      87             : 
      88             :     // Proxy hooks may return a non-native property.
      89          44 :     if (prop.isNonNativeProperty())
      90           0 :         return false;
      91             : 
      92          44 :     Shape* shape = prop.shape();
      93         132 :     if (!shape->hasSlot() ||
      94          88 :         !shape->hasDefaultGetter() ||
      95          44 :         !shape->hasDefaultSetter())
      96             :     {
      97           0 :         return false;
      98             :     }
      99          44 :     MOZ_ASSERT(obj->as<NativeObject>().containsPure(shape));
     100          44 :     return IsUninitializedLexical(obj->as<NativeObject>().getSlot(shape->slot()));
     101             : }
     102             : 
     103             : static inline void
     104           0 : ReportUninitializedLexical(JSContext* cx, HandlePropertyName name)
     105             : {
     106           0 :     ReportRuntimeLexicalError(cx, JSMSG_UNINITIALIZED_LEXICAL, name);
     107           0 : }
     108             : 
     109             : static inline void
     110           0 : ReportUninitializedLexical(JSContext* cx, HandleScript script, jsbytecode* pc)
     111             : {
     112           0 :     ReportRuntimeLexicalError(cx, JSMSG_UNINITIALIZED_LEXICAL, script, pc);
     113           0 : }
     114             : 
     115             : static inline bool
     116        2294 : CheckUninitializedLexical(JSContext* cx, PropertyName* name_, HandleValue val)
     117             : {
     118        2294 :     if (IsUninitializedLexical(val)) {
     119           0 :         RootedPropertyName name(cx, name_);
     120           0 :         ReportUninitializedLexical(cx, name);
     121           0 :         return false;
     122             :     }
     123        2294 :     return true;
     124             : }
     125             : 
     126             : static inline bool
     127         771 : CheckUninitializedLexical(JSContext* cx, HandleScript script, jsbytecode* pc, HandleValue val)
     128             : {
     129         771 :     if (IsUninitializedLexical(val)) {
     130           0 :         ReportUninitializedLexical(cx, script, pc);
     131           0 :         return false;
     132             :     }
     133         771 :     return true;
     134             : }
     135             : 
     136             : static inline void
     137             : ReportRuntimeConstAssignment(JSContext* cx, HandlePropertyName name)
     138             : {
     139             :     ReportRuntimeLexicalError(cx, JSMSG_BAD_CONST_ASSIGN, name);
     140             : }
     141             : 
     142             : static inline void
     143           0 : ReportRuntimeConstAssignment(JSContext* cx, HandleScript script, jsbytecode* pc)
     144             : {
     145           0 :     ReportRuntimeLexicalError(cx, JSMSG_BAD_CONST_ASSIGN, script, pc);
     146           0 : }
     147             : 
     148             : inline bool
     149        1599 : GetLengthProperty(const Value& lval, MutableHandleValue vp)
     150             : {
     151             :     /* Optimize length accesses on strings, arrays, and arguments. */
     152        1599 :     if (lval.isString()) {
     153         231 :         vp.setInt32(lval.toString()->length());
     154         231 :         return true;
     155             :     }
     156        1368 :     if (lval.isObject()) {
     157        1366 :         JSObject* obj = &lval.toObject();
     158        1366 :         if (obj->is<ArrayObject>()) {
     159        1168 :             vp.setNumber(obj->as<ArrayObject>().length());
     160        1168 :             return true;
     161             :         }
     162             : 
     163         198 :         if (obj->is<ArgumentsObject>()) {
     164          20 :             ArgumentsObject* argsobj = &obj->as<ArgumentsObject>();
     165          20 :             if (!argsobj->hasOverriddenLength()) {
     166          20 :                 uint32_t length = argsobj->initialLength();
     167          20 :                 MOZ_ASSERT(length < INT32_MAX);
     168          20 :                 vp.setInt32(int32_t(length));
     169          20 :                 return true;
     170             :             }
     171             :         }
     172             :     }
     173             : 
     174         180 :     return false;
     175             : }
     176             : 
     177             : enum class GetNameMode { Normal, TypeOf };
     178             : 
     179             : template <GetNameMode mode>
     180             : inline bool
     181        2308 : FetchName(JSContext* cx, HandleObject receiver, HandleObject holder, HandlePropertyName name,
     182             :           Handle<PropertyResult> prop, MutableHandleValue vp)
     183             : {
     184        2308 :     if (!prop) {
     185             :         switch (mode) {
     186             :           case GetNameMode::Normal:
     187           0 :             return ReportIsNotDefined(cx, name);
     188             :           case GetNameMode::TypeOf:
     189          14 :             vp.setUndefined();
     190          14 :             return true;
     191             :         }
     192             :     }
     193             : 
     194             :     /* Take the slow path if shape was not found in a native object. */
     195        2294 :     if (!receiver->isNative() || !holder->isNative()) {
     196         102 :         Rooted<jsid> id(cx, NameToId(name));
     197          51 :         if (!GetProperty(cx, receiver, receiver, id, vp))
     198           0 :             return false;
     199             :     } else {
     200        4486 :         RootedShape shape(cx, prop.shape());
     201        2243 :         if (shape->isDataDescriptor() && shape->hasDefaultGetter()) {
     202             :             /* Fast path for Object instance properties. */
     203        1491 :             MOZ_ASSERT(shape->hasSlot());
     204        1491 :             vp.set(holder->as<NativeObject>().getSlot(shape->slot()));
     205             :         } else {
     206             :             // Unwrap 'with' environments for reasons given in
     207             :             // GetNameBoundInEnvironment.
     208        1504 :             RootedObject normalized(cx, MaybeUnwrapWithEnvironment(receiver));
     209         752 :             if (!NativeGetExistingProperty(cx, normalized, holder.as<NativeObject>(), shape, vp))
     210           0 :                 return false;
     211             :         }
     212             :     }
     213             : 
     214             :     // We do our own explicit checking for |this|
     215        2294 :     if (name == cx->names().dotThis)
     216           0 :         return true;
     217             : 
     218             :     // NAME operations are the slow paths already, so unconditionally check
     219             :     // for uninitialized lets.
     220        2294 :     return CheckUninitializedLexical(cx, name, vp);
     221             : }
     222             : 
     223             : inline bool
     224       15348 : FetchNameNoGC(JSObject* pobj, PropertyResult prop, MutableHandleValue vp)
     225             : {
     226       15348 :     if (!prop || !pobj->isNative())
     227           0 :         return false;
     228             : 
     229       15348 :     Shape* shape = prop.shape();
     230       15348 :     if (!shape->isDataDescriptor() || !shape->hasDefaultGetter())
     231         582 :         return false;
     232             : 
     233       14766 :     vp.set(pobj->as<NativeObject>().getSlot(shape->slot()));
     234       14766 :     return !IsUninitializedLexical(vp);
     235             : }
     236             : 
     237             : template <js::GetNameMode mode>
     238             : inline bool
     239       17074 : GetEnvironmentName(JSContext* cx, HandleObject envChain, HandlePropertyName name,
     240             :                    MutableHandleValue vp)
     241             : {
     242             :     {
     243       17074 :         PropertyResult prop;
     244       17074 :         JSObject* obj = nullptr;
     245       17074 :         JSObject* pobj = nullptr;
     246       17074 :         if (LookupNameNoGC(cx, name, envChain, &obj, &pobj, &prop)) {
     247       15348 :             if (FetchNameNoGC(pobj, prop, vp))
     248       14766 :                 return true;
     249             :         }
     250             :     }
     251             : 
     252        4616 :     Rooted<PropertyResult> prop(cx);
     253        4616 :     RootedObject obj(cx), pobj(cx);
     254        2308 :     if (!LookupName(cx, name, envChain, &obj, &pobj, &prop))
     255           0 :         return false;
     256             : 
     257        2308 :     return FetchName<mode>(cx, obj, pobj, name, prop, vp);
     258             : }
     259             : 
     260             : inline bool
     261          28 : HasOwnProperty(JSContext* cx, HandleValue val, HandleValue idValue, bool* result)
     262             : {
     263             : 
     264             :     // As an optimization, provide a fast path when rooting is not necessary and
     265             :     // we can safely retrieve the object's shape.
     266             :     jsid id;
     267          28 :     if (val.isObject() && ValueToId<NoGC>(cx, idValue, &id)) {
     268          28 :         JSObject* obj = &val.toObject();
     269          28 :         PropertyResult prop;
     270         138 :         if (obj->isNative() &&
     271         136 :             NativeLookupOwnProperty<NoGC>(cx, &obj->as<NativeObject>(), id, &prop))
     272             :         {
     273          27 :             *result = prop.isFound();
     274          27 :             return true;
     275             :         }
     276             :     }
     277             : 
     278             :     // Step 1.
     279           2 :     RootedId key(cx);
     280           1 :     if (!ToPropertyKey(cx, idValue, &key))
     281           0 :         return false;
     282             : 
     283             :     // Step 2.
     284           2 :     RootedObject obj(cx, ToObject(cx, val));
     285           1 :     if (!obj)
     286           0 :         return false;
     287             : 
     288             :     // Step 3.
     289           1 :     return HasOwnProperty(cx, obj, key, result);
     290             : }
     291             : 
     292             : 
     293             : inline bool
     294       18155 : GetIntrinsicOperation(JSContext* cx, jsbytecode* pc, MutableHandleValue vp)
     295             : {
     296       36310 :     RootedPropertyName name(cx, cx->currentScript()->getName(pc));
     297       36310 :     return GlobalObject::getIntrinsicValue(cx, cx->global(), name, vp);
     298             : }
     299             : 
     300             : inline bool
     301          87 : SetIntrinsicOperation(JSContext* cx, JSScript* script, jsbytecode* pc, HandleValue val)
     302             : {
     303         174 :     RootedPropertyName name(cx, script->getName(pc));
     304         174 :     return GlobalObject::setIntrinsicValue(cx, cx->global(), name, val);
     305             : }
     306             : 
     307             : inline void
     308        1746 : SetAliasedVarOperation(JSContext* cx, JSScript* script, jsbytecode* pc,
     309             :                        EnvironmentObject& obj, EnvironmentCoordinate ec, const Value& val,
     310             :                        MaybeCheckTDZ checkTDZ)
     311             : {
     312        1746 :     MOZ_ASSERT_IF(checkTDZ, !IsUninitializedLexical(obj.aliasedBinding(ec)));
     313             : 
     314             :     // Avoid computing the name if no type updates are needed, as this may be
     315             :     // expensive on scopes with large numbers of variables.
     316        1746 :     PropertyName* name = obj.isSingleton()
     317        1746 :                          ? EnvironmentCoordinateName(cx->caches().envCoordinateNameCache, script, pc)
     318        1746 :                          : nullptr;
     319             : 
     320        1746 :     obj.setAliasedBinding(cx, ec, name, val);
     321        1746 : }
     322             : 
     323             : inline bool
     324         863 : SetNameOperation(JSContext* cx, JSScript* script, jsbytecode* pc, HandleObject env,
     325             :                  HandleValue val)
     326             : {
     327         863 :     MOZ_ASSERT(*pc == JSOP_SETNAME ||
     328             :                *pc == JSOP_STRICTSETNAME ||
     329             :                *pc == JSOP_SETGNAME ||
     330             :                *pc == JSOP_STRICTSETGNAME);
     331         863 :     MOZ_ASSERT_IF((*pc == JSOP_SETGNAME || *pc == JSOP_STRICTSETGNAME) &&
     332             :                   !script->hasNonSyntacticScope(),
     333             :                   env == cx->global() ||
     334             :                   env == &cx->global()->lexicalEnvironment() ||
     335             :                   env->is<RuntimeLexicalErrorObject>());
     336             : 
     337         863 :     bool strict = *pc == JSOP_STRICTSETNAME || *pc == JSOP_STRICTSETGNAME;
     338        1726 :     RootedPropertyName name(cx, script->getName(pc));
     339             : 
     340             :     // In strict mode, assigning to an undeclared global variable is an
     341             :     // error. To detect this, we call NativeSetProperty directly and pass
     342             :     // Unqualified. It stores the error, if any, in |result|.
     343             :     bool ok;
     344         863 :     ObjectOpResult result;
     345        1726 :     RootedId id(cx, NameToId(name));
     346        1726 :     RootedValue receiver(cx, ObjectValue(*env));
     347         863 :     if (env->isUnqualifiedVarObj()) {
     348        1598 :         RootedNativeObject varobj(cx);
     349         799 :         if (env->is<DebugEnvironmentProxy>())
     350           0 :             varobj = &env->as<DebugEnvironmentProxy>().environment().as<NativeObject>();
     351             :         else
     352         799 :             varobj = &env->as<NativeObject>();
     353         799 :         MOZ_ASSERT(!varobj->getOpsSetProperty());
     354         799 :         ok = NativeSetProperty(cx, varobj, id, val, receiver, Unqualified, result);
     355             :     } else {
     356          64 :         ok = SetProperty(cx, env, id, val, receiver, result);
     357             :     }
     358        1726 :     return ok && result.checkStrictErrorOrWarning(cx, env, id, strict);
     359             : }
     360             : 
     361             : inline bool
     362        2451 : DefLexicalOperation(JSContext* cx, Handle<LexicalEnvironmentObject*> lexicalEnv,
     363             :                     HandleObject varObj, HandlePropertyName name, unsigned attrs)
     364             : {
     365             :     // Redeclaration checks should have already been done.
     366        2451 :     MOZ_ASSERT(CheckLexicalNameConflict(cx, lexicalEnv, varObj, name));
     367        4902 :     RootedId id(cx, NameToId(name));
     368        4902 :     RootedValue uninitialized(cx, MagicValue(JS_UNINITIALIZED_LEXICAL));
     369        4902 :     return NativeDefineProperty(cx, lexicalEnv, id, uninitialized, nullptr, nullptr, attrs);
     370             : }
     371             : 
     372             : inline bool
     373        2451 : DefLexicalOperation(JSContext* cx, LexicalEnvironmentObject* lexicalEnvArg,
     374             :                     JSObject* varObjArg, JSScript* script, jsbytecode* pc)
     375             : {
     376        2451 :     MOZ_ASSERT(*pc == JSOP_DEFLET || *pc == JSOP_DEFCONST);
     377        4902 :     RootedPropertyName name(cx, script->getName(pc));
     378             : 
     379        2451 :     unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT;
     380        2451 :     if (*pc == JSOP_DEFCONST)
     381        2326 :         attrs |= JSPROP_READONLY;
     382             : 
     383        4902 :     Rooted<LexicalEnvironmentObject*> lexicalEnv(cx, lexicalEnvArg);
     384        4902 :     RootedObject varObj(cx, varObjArg);
     385        2451 :     MOZ_ASSERT_IF(!script->hasNonSyntacticScope(),
     386             :                   lexicalEnv == &cx->global()->lexicalEnvironment() && varObj == cx->global());
     387             : 
     388        4902 :     return DefLexicalOperation(cx, lexicalEnv, varObj, name, attrs);
     389             : }
     390             : 
     391             : inline void
     392        2450 : InitGlobalLexicalOperation(JSContext* cx, LexicalEnvironmentObject* lexicalEnvArg,
     393             :                            JSScript* script, jsbytecode* pc, HandleValue value)
     394             : {
     395        2450 :     MOZ_ASSERT_IF(!script->hasNonSyntacticScope(),
     396             :                   lexicalEnvArg == &cx->global()->lexicalEnvironment());
     397        2450 :     MOZ_ASSERT(*pc == JSOP_INITGLEXICAL);
     398        4900 :     Rooted<LexicalEnvironmentObject*> lexicalEnv(cx, lexicalEnvArg);
     399        4900 :     RootedShape shape(cx, lexicalEnv->lookup(cx, script->getName(pc)));
     400        2450 :     MOZ_ASSERT(shape);
     401        2450 :     lexicalEnv->setSlot(shape->slot(), value);
     402        2450 : }
     403             : 
     404             : inline bool
     405       14272 : InitPropertyOperation(JSContext* cx, JSOp op, HandleObject obj, HandleId id, HandleValue rhs)
     406             : {
     407       14272 :     if (obj->is<PlainObject>() || obj->is<JSFunction>()) {
     408       14189 :         unsigned propAttrs = GetInitDataPropAttrs(op);
     409       14189 :         return NativeDefineProperty(cx, obj.as<NativeObject>(), id, rhs, nullptr, nullptr,
     410       14189 :                                     propAttrs);
     411             :     }
     412             : 
     413          83 :     MOZ_ASSERT(obj->as<UnboxedPlainObject>().layout().lookup(id));
     414          83 :     return PutProperty(cx, obj, id, rhs, false);
     415             : }
     416             : 
     417             : inline bool
     418         715 : DefVarOperation(JSContext* cx, HandleObject varobj, HandlePropertyName dn, unsigned attrs)
     419             : {
     420         715 :     MOZ_ASSERT(varobj->isQualifiedVarObj());
     421             : 
     422             : #ifdef DEBUG
     423             :     // Per spec, it is an error to redeclare a lexical binding. This should
     424             :     // have already been checked.
     425         715 :     if (JS_HasExtensibleLexicalEnvironment(varobj)) {
     426        1214 :         Rooted<LexicalEnvironmentObject*> lexicalEnv(cx);
     427         607 :         lexicalEnv = &JS_ExtensibleLexicalEnvironment(varobj)->as<LexicalEnvironmentObject>();
     428         607 :         MOZ_ASSERT(CheckVarNameConflict(cx, lexicalEnv, dn));
     429             :     }
     430             : #endif
     431             : 
     432        1430 :     Rooted<PropertyResult> prop(cx);
     433        1430 :     RootedObject obj2(cx);
     434         715 :     if (!LookupProperty(cx, varobj, dn, &obj2, &prop))
     435           0 :         return false;
     436             : 
     437             :     /* Steps 8c, 8d. */
     438         715 :     if (!prop || (obj2 != varobj && varobj->is<GlobalObject>())) {
     439         702 :         if (!DefineProperty(cx, varobj, dn, UndefinedHandleValue, nullptr, nullptr, attrs))
     440           0 :             return false;
     441             :     }
     442             : 
     443         715 :     if (varobj->is<GlobalObject>()) {
     444         587 :         if (!varobj->compartment()->addToVarNames(cx, dn))
     445           0 :             return false;
     446             :     }
     447             : 
     448         715 :     return true;
     449             : }
     450             : 
     451             : static MOZ_ALWAYS_INLINE bool
     452           0 : NegOperation(JSContext* cx, HandleScript script, jsbytecode* pc, HandleValue val,
     453             :              MutableHandleValue res)
     454             : {
     455             :     /*
     456             :      * When the operand is int jsval, INT32_FITS_IN_JSVAL(i) implies
     457             :      * INT32_FITS_IN_JSVAL(-i) unless i is 0 or INT32_MIN when the
     458             :      * results, -0.0 or INT32_MAX + 1, are double values.
     459             :      */
     460             :     int32_t i;
     461           0 :     if (val.isInt32() && (i = val.toInt32()) != 0 && i != INT32_MIN) {
     462           0 :         res.setInt32(-i);
     463             :     } else {
     464             :         double d;
     465           0 :         if (!ToNumber(cx, val, &d))
     466           0 :             return false;
     467           0 :         res.setNumber(-d);
     468             :     }
     469             : 
     470           0 :     return true;
     471             : }
     472             : 
     473             : static MOZ_ALWAYS_INLINE bool
     474          27 : ToIdOperation(JSContext* cx, HandleScript script, jsbytecode* pc, HandleValue idval,
     475             :               MutableHandleValue res)
     476             : {
     477          27 :     if (idval.isInt32()) {
     478           0 :         res.set(idval);
     479           0 :         return true;
     480             :     }
     481             : 
     482          54 :     RootedId id(cx);
     483          27 :     if (!ToPropertyKey(cx, idval, &id))
     484           0 :         return false;
     485             : 
     486          27 :     res.set(IdToValue(id));
     487          27 :     return true;
     488             : }
     489             : 
     490             : static MOZ_ALWAYS_INLINE bool
     491        4921 : GetObjectElementOperation(JSContext* cx, JSOp op, JS::HandleObject obj, JS::HandleValue receiver,
     492             :                           HandleValue key, MutableHandleValue res)
     493             : {
     494        4921 :     MOZ_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM || op == JSOP_GETELEM_SUPER);
     495        4921 :     MOZ_ASSERT_IF(op == JSOP_GETELEM || op == JSOP_CALLELEM, obj == &receiver.toObject());
     496             : 
     497             :     do {
     498             :         uint32_t index;
     499        4921 :         if (IsDefinitelyIndex(key, &index)) {
     500        1504 :             if (GetElementNoGC(cx, obj, receiver, index, res.address()))
     501        4041 :                 break;
     502             : 
     503          91 :             if (!GetElement(cx, obj, receiver, index, res))
     504           4 :                 return false;
     505          91 :             break;
     506             :         }
     507             : 
     508        3417 :         if (key.isString()) {
     509        1648 :             JSString* str = key.toString();
     510        1648 :             JSAtom* name = str->isAtom() ? &str->asAtom() : AtomizeString(cx, str);
     511        1648 :             if (!name)
     512           0 :                 return false;
     513        1648 :             if (name->isIndex(&index)) {
     514           0 :                 if (GetElementNoGC(cx, obj, receiver, index, res.address()))
     515           0 :                     break;
     516             :             } else {
     517        1648 :                 if (GetPropertyNoGC(cx, obj, receiver, name->asPropertyName(), res.address()))
     518        1124 :                     break;
     519             :             }
     520             :         }
     521             : 
     522        4582 :         RootedId id(cx);
     523        2293 :         if (!ToPropertyKey(cx, key, &id))
     524           0 :             return false;
     525        2293 :         if (!GetProperty(cx, obj, receiver, id, res))
     526           4 :             return false;
     527             :     } while (false);
     528             : 
     529        4917 :     assertSameCompartmentDebugOnly(cx, res);
     530        4917 :     return true;
     531             : }
     532             : 
     533             : static MOZ_ALWAYS_INLINE bool
     534           0 : GetPrimitiveElementOperation(JSContext* cx, JSOp op, JS::HandleValue receiver,
     535             :                              HandleValue key, MutableHandleValue res)
     536             : {
     537           0 :     MOZ_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM);
     538             : 
     539             :     // FIXME: Bug 1234324 We shouldn't be boxing here.
     540           0 :     RootedObject boxed(cx, ToObjectFromStack(cx, receiver));
     541           0 :     if (!boxed)
     542           0 :         return false;
     543             : 
     544             :     do {
     545             :         uint32_t index;
     546           0 :         if (IsDefinitelyIndex(key, &index)) {
     547           0 :             if (GetElementNoGC(cx, boxed, receiver, index, res.address()))
     548           0 :                 break;
     549             : 
     550           0 :             if (!GetElement(cx, boxed, receiver, index, res))
     551           0 :                 return false;
     552           0 :             break;
     553             :         }
     554             : 
     555           0 :         if (key.isString()) {
     556           0 :             JSString* str = key.toString();
     557           0 :             JSAtom* name = str->isAtom() ? &str->asAtom() : AtomizeString(cx, str);
     558           0 :             if (!name)
     559           0 :                 return false;
     560           0 :             if (name->isIndex(&index)) {
     561           0 :                 if (GetElementNoGC(cx, boxed, receiver, index, res.address()))
     562           0 :                     break;
     563             :             } else {
     564           0 :                 if (GetPropertyNoGC(cx, boxed, receiver, name->asPropertyName(), res.address()))
     565           0 :                     break;
     566             :             }
     567             :         }
     568             : 
     569           0 :         RootedId id(cx);
     570           0 :         if (!ToPropertyKey(cx, key, &id))
     571           0 :             return false;
     572           0 :         if (!GetProperty(cx, boxed, receiver, id, res))
     573           0 :             return false;
     574             :     } while (false);
     575             : 
     576           0 :     assertSameCompartmentDebugOnly(cx, res);
     577           0 :     return true;
     578             : }
     579             : 
     580             : static MOZ_ALWAYS_INLINE bool
     581        3616 : GetElemOptimizedArguments(JSContext* cx, AbstractFramePtr frame, MutableHandleValue lref,
     582             :                           HandleValue rref, MutableHandleValue res, bool* done)
     583             : {
     584        3616 :     MOZ_ASSERT(!*done);
     585             : 
     586        3616 :     if (IsOptimizedArguments(frame, lref)) {
     587         151 :         if (rref.isInt32()) {
     588         151 :             int32_t i = rref.toInt32();
     589         151 :             if (i >= 0 && uint32_t(i) < frame.numActualArgs()) {
     590         151 :                 res.set(frame.unaliasedActual(i));
     591         151 :                 *done = true;
     592         302 :                 return true;
     593             :             }
     594             :         }
     595             : 
     596           0 :         RootedScript script(cx, frame.script());
     597           0 :         if (!JSScript::argumentsOptimizationFailed(cx, script))
     598           0 :             return false;
     599             : 
     600           0 :         lref.set(ObjectValue(frame.argsObj()));
     601             :     }
     602             : 
     603        3465 :     return true;
     604             : }
     605             : 
     606             : static MOZ_ALWAYS_INLINE bool
     607        4957 : GetElementOperation(JSContext* cx, JSOp op, HandleValue lref, HandleValue rref,
     608             :                     MutableHandleValue res)
     609             : {
     610        4957 :     MOZ_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM);
     611             : 
     612             :     uint32_t index;
     613        4957 :     if (lref.isString() && IsDefinitelyIndex(rref, &index)) {
     614          36 :         JSString* str = lref.toString();
     615          36 :         if (index < str->length()) {
     616          36 :             str = cx->staticStrings().getUnitStringForElement(cx, str, index);
     617          36 :             if (!str)
     618           0 :                 return false;
     619          36 :             res.setString(str);
     620          36 :             return true;
     621             :         }
     622             :     }
     623             : 
     624        4921 :     if (lref.isPrimitive()) {
     625           0 :         RootedValue thisv(cx, lref);
     626           0 :         return GetPrimitiveElementOperation(cx, op, thisv, rref, res);
     627             :     }
     628             : 
     629        9842 :     RootedObject obj(cx, &lref.toObject());
     630        9842 :     RootedValue thisv(cx, lref);
     631        4921 :     return GetObjectElementOperation(cx, op, obj, thisv, rref, res);
     632             : }
     633             : 
     634             : static MOZ_ALWAYS_INLINE JSString*
     635         909 : TypeOfOperation(const Value& v, JSRuntime* rt)
     636             : {
     637         909 :     JSType type = js::TypeOfValue(v);
     638         909 :     return TypeName(type, *rt->commonNames);
     639             : }
     640             : 
     641             : static MOZ_ALWAYS_INLINE bool
     642         168 : InitElemOperation(JSContext* cx, jsbytecode* pc, HandleObject obj, HandleValue idval, HandleValue val)
     643             : {
     644         168 :     MOZ_ASSERT(!val.isMagic(JS_ELEMENTS_HOLE));
     645         168 :     MOZ_ASSERT(!obj->getClass()->getGetProperty());
     646         168 :     MOZ_ASSERT(!obj->getClass()->getSetProperty());
     647             : 
     648         336 :     RootedId id(cx);
     649         168 :     if (!ToPropertyKey(cx, idval, &id))
     650           0 :         return false;
     651             : 
     652         168 :     unsigned flags = JSOp(*pc) == JSOP_INITHIDDENELEM ? 0 : JSPROP_ENUMERATE;
     653         168 :     return DefineProperty(cx, obj, id, val, nullptr, nullptr, flags);
     654             : }
     655             : 
     656             : static MOZ_ALWAYS_INLINE bool
     657        1589 : InitArrayElemOperation(JSContext* cx, jsbytecode* pc, HandleObject obj, uint32_t index, HandleValue val)
     658             : {
     659        1589 :     JSOp op = JSOp(*pc);
     660        1589 :     MOZ_ASSERT(op == JSOP_INITELEM_ARRAY || op == JSOP_INITELEM_INC);
     661             : 
     662        1589 :     MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
     663             : 
     664        1589 :     if (op == JSOP_INITELEM_INC && index == INT32_MAX) {
     665           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SPREAD_TOO_LARGE);
     666           0 :         return false;
     667             :     }
     668             : 
     669             :     /*
     670             :      * If val is a hole, do not call DefineElement.
     671             :      *
     672             :      * Furthermore, if the current op is JSOP_INITELEM_INC, always call
     673             :      * SetLengthProperty even if it is not the last element initialiser,
     674             :      * because it may be followed by JSOP_SPREAD, which will not set the array
     675             :      * length if nothing is spread.
     676             :      *
     677             :      * Alternatively, if the current op is JSOP_INITELEM_ARRAY, the length will
     678             :      * have already been set by the earlier JSOP_NEWARRAY; JSOP_INITELEM_ARRAY
     679             :      * cannot follow JSOP_SPREAD.
     680             :      */
     681        1589 :     if (val.isMagic(JS_ELEMENTS_HOLE)) {
     682           0 :         if (op == JSOP_INITELEM_INC) {
     683           0 :             if (!SetLengthProperty(cx, obj, index + 1))
     684           0 :                 return false;
     685             :         }
     686             :     } else {
     687        1589 :         if (!DefineElement(cx, obj, index, val, nullptr, nullptr, JSPROP_ENUMERATE))
     688           0 :             return false;
     689             :     }
     690             : 
     691        1589 :     return true;
     692             : }
     693             : 
     694             : #define RELATIONAL_OP(OP)                                                     \
     695             :     JS_BEGIN_MACRO                                                            \
     696             :         /* Optimize for two int-tagged operands (typical loop control). */    \
     697             :         if (lhs.isInt32() && rhs.isInt32()) {                                 \
     698             :             *res = lhs.toInt32() OP rhs.toInt32();                            \
     699             :         } else {                                                              \
     700             :             if (!ToPrimitive(cx, JSTYPE_NUMBER, lhs))                         \
     701             :                 return false;                                                 \
     702             :             if (!ToPrimitive(cx, JSTYPE_NUMBER, rhs))                         \
     703             :                 return false;                                                 \
     704             :             if (lhs.isString() && rhs.isString()) {                           \
     705             :                 JSString* l = lhs.toString();                                 \
     706             :                 JSString* r = rhs.toString();                                 \
     707             :                 int32_t result;                                               \
     708             :                 if (!CompareStrings(cx, l, r, &result))                       \
     709             :                     return false;                                             \
     710             :                 *res = result OP 0;                                           \
     711             :             } else {                                                          \
     712             :                 double l, r;                                                  \
     713             :                 if (!ToNumber(cx, lhs, &l) || !ToNumber(cx, rhs, &r))         \
     714             :                     return false;                                             \
     715             :                 *res = (l OP r);                                              \
     716             :             }                                                                 \
     717             :         }                                                                     \
     718             :         return true;                                                          \
     719             :     JS_END_MACRO
     720             : 
     721             : static MOZ_ALWAYS_INLINE bool
     722         997 : LessThanOperation(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res) {
     723         997 :     RELATIONAL_OP(<);
     724             : }
     725             : 
     726             : static MOZ_ALWAYS_INLINE bool
     727          91 : LessThanOrEqualOperation(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res) {
     728          91 :     RELATIONAL_OP(<=);
     729             : }
     730             : 
     731             : static MOZ_ALWAYS_INLINE bool
     732         330 : GreaterThanOperation(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res) {
     733         330 :     RELATIONAL_OP(>);
     734             : }
     735             : 
     736             : static MOZ_ALWAYS_INLINE bool
     737         841 : GreaterThanOrEqualOperation(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res) {
     738         841 :     RELATIONAL_OP(>=);
     739             : }
     740             : 
     741             : static MOZ_ALWAYS_INLINE bool
     742           5 : BitNot(JSContext* cx, HandleValue in, int* out)
     743             : {
     744             :     int i;
     745           5 :     if (!ToInt32(cx, in, &i))
     746           0 :         return false;
     747           5 :     *out = ~i;
     748           5 :     return true;
     749             : }
     750             : 
     751             : static MOZ_ALWAYS_INLINE bool
     752           0 : BitXor(JSContext* cx, HandleValue lhs, HandleValue rhs, int* out)
     753             : {
     754             :     int left, right;
     755           0 :     if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
     756           0 :         return false;
     757           0 :     *out = left ^ right;
     758           0 :     return true;
     759             : }
     760             : 
     761             : static MOZ_ALWAYS_INLINE bool
     762          20 : BitOr(JSContext* cx, HandleValue lhs, HandleValue rhs, int* out)
     763             : {
     764             :     int left, right;
     765          20 :     if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
     766           0 :         return false;
     767          20 :     *out = left | right;
     768          20 :     return true;
     769             : }
     770             : 
     771             : static MOZ_ALWAYS_INLINE bool
     772          11 : BitAnd(JSContext* cx, HandleValue lhs, HandleValue rhs, int* out)
     773             : {
     774             :     int left, right;
     775          11 :     if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
     776           0 :         return false;
     777          11 :     *out = left & right;
     778          11 :     return true;
     779             : }
     780             : 
     781             : static MOZ_ALWAYS_INLINE bool
     782           0 : BitLsh(JSContext* cx, HandleValue lhs, HandleValue rhs, int* out)
     783             : {
     784             :     int32_t left, right;
     785           0 :     if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
     786           0 :         return false;
     787           0 :     *out = uint32_t(left) << (right & 31);
     788           0 :     return true;
     789             : }
     790             : 
     791             : static MOZ_ALWAYS_INLINE bool
     792           0 : BitRsh(JSContext* cx, HandleValue lhs, HandleValue rhs, int* out)
     793             : {
     794             :     int32_t left, right;
     795           0 :     if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
     796           0 :         return false;
     797           0 :     *out = left >> (right & 31);
     798           0 :     return true;
     799             : }
     800             : 
     801             : static MOZ_ALWAYS_INLINE bool
     802           1 : UrshOperation(JSContext* cx, HandleValue lhs, HandleValue rhs, MutableHandleValue out)
     803             : {
     804             :     uint32_t left;
     805             :     int32_t  right;
     806           1 :     if (!ToUint32(cx, lhs, &left) || !ToInt32(cx, rhs, &right))
     807           0 :         return false;
     808           1 :     left >>= right & 31;
     809           1 :     out.setNumber(uint32_t(left));
     810           1 :     return true;
     811             : }
     812             : 
     813             : template <typename T>
     814             : static MOZ_ALWAYS_INLINE bool
     815           0 : SignExtendOperation(JSContext* cx, HandleValue in, int* out)
     816             : {
     817             :     int32_t i;
     818           0 :     if (!ToInt32(cx, in, &i))
     819           0 :         return false;
     820           0 :     *out = (T)i;
     821           0 :     return true;
     822             : }
     823             : 
     824             : #undef RELATIONAL_OP
     825             : 
     826             : inline JSFunction*
     827           0 : ReportIfNotFunction(JSContext* cx, HandleValue v, MaybeConstruct construct = NO_CONSTRUCT)
     828             : {
     829           0 :     if (v.isObject() && v.toObject().is<JSFunction>())
     830           0 :         return &v.toObject().as<JSFunction>();
     831             : 
     832           0 :     ReportIsNotFunction(cx, v, -1, construct);
     833           0 :     return nullptr;
     834             : }
     835             : 
     836             : /*
     837             :  * FastCallGuard is used to optimize calls to JS functions from natives written
     838             :  * in C++, e.g. Array.prototype.map.  If the callee is not Ion-compiled, this
     839             :  * will just call js::Call.  If the callee has a valid IonScript, however, it
     840             :  * will enter Ion directly.
     841             :  */
     842             : class FastCallGuard
     843             : {
     844             :     InvokeArgs args_;
     845             :     RootedFunction fun_;
     846             :     RootedScript script_;
     847             : 
     848             :     // Constructing a JitContext is pretty expensive due to the TLS access,
     849             :     // so only do this if we have to.
     850             :     bool useIon_;
     851             : 
     852             :   public:
     853             :     FastCallGuard(JSContext* cx, const Value& fval)
     854             :       : args_(cx)
     855             :       , fun_(cx)
     856             :       , script_(cx)
     857             :       , useIon_(jit::IsIonEnabled(cx))
     858             :     {
     859             :         initFunction(fval);
     860             :     }
     861             : 
     862             :     void initFunction(const Value& fval) {
     863             :         if (fval.isObject() && fval.toObject().is<JSFunction>()) {
     864             :             JSFunction* fun = &fval.toObject().as<JSFunction>();
     865             :             if (fun->isInterpreted())
     866             :                 fun_ = fun;
     867             :         }
     868             :     }
     869             : 
     870             :     InvokeArgs& args() {
     871             :         return args_;
     872             :     }
     873             : 
     874             :     bool call(JSContext* cx, HandleValue callee, HandleValue thisv, MutableHandleValue rval) {
     875             :         args_.CallArgs::setCallee(callee);
     876             :         args_.CallArgs::setThis(thisv);
     877             : 
     878             :         if (useIon_ && fun_) {
     879             :             if (!script_) {
     880             :                 script_ = JSFunction::getOrCreateScript(cx, fun_);
     881             :                 if (!script_)
     882             :                     return false;
     883             :             }
     884             :             MOZ_ASSERT(fun_->nonLazyScript() == script_);
     885             : 
     886             :             jit::MethodStatus status = jit::CanEnterUsingFastInvoke(cx, script_, args_.length());
     887             :             if (status == jit::Method_Error)
     888             :                 return false;
     889             :             if (status == jit::Method_Compiled) {
     890             :                 jit::JitExecStatus result = jit::FastInvoke(cx, fun_, args_);
     891             :                 if (IsErrorStatus(result))
     892             :                     return false;
     893             : 
     894             :                 MOZ_ASSERT(result == jit::JitExec_Ok);
     895             :                 rval.set(args_.CallArgs::rval());
     896             :                 return true;
     897             :             }
     898             : 
     899             :             MOZ_ASSERT(status == jit::Method_Skipped);
     900             : 
     901             :             if (script_->canIonCompile()) {
     902             :                 // This script is not yet hot. Since calling into Ion is much
     903             :                 // faster here, bump the warm-up counter a bit to account for this.
     904             :                 script_->incWarmUpCounter(5);
     905             :             }
     906             :         }
     907             : 
     908             :         if (!InternalCallOrConstruct(cx, args_, NO_CONSTRUCT))
     909             :             return false;
     910             : 
     911             :         rval.set(args_.CallArgs::rval());
     912             :         return true;
     913             :     }
     914             : 
     915             :   private:
     916             :     FastCallGuard(const FastCallGuard& other) = delete;
     917             :     void operator=(const FastCallGuard& other) = delete;
     918             : };
     919             : 
     920             : }  /* namespace js */
     921             : 
     922             : #endif /* vm_Interpreter_inl_h */

Generated by: LCOV version 1.13