tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Interpreter-inl.h (29858B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      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 "mozilla/CheckedArithmetic.h"
     13 
     14 #include "jslibmath.h"
     15 #include "jsmath.h"
     16 #include "jsnum.h"
     17 
     18 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
     19 #include "vm/BigIntType.h"
     20 #include "vm/BytecodeUtil.h"  // JSDVG_SEARCH_STACK
     21 #include "vm/JSAtomUtils.h"   // AtomizeString
     22 #include "vm/Realm.h"
     23 #include "vm/StaticStrings.h"
     24 #include "vm/ThrowMsgKind.h"
     25 
     26 #include "vm/GlobalObject-inl.h"
     27 #include "vm/JSAtomUtils-inl.h"  // PrimitiveValueToId, TypeName
     28 #include "vm/JSContext-inl.h"
     29 #include "vm/JSObject-inl.h"
     30 #include "vm/NativeObject-inl.h"
     31 #include "vm/ObjectOperations-inl.h"
     32 #include "vm/StringType-inl.h"
     33 
     34 namespace js {
     35 
     36 /*
     37 * Per ES6, lexical declarations may not be accessed in any fashion until they
     38 * are initialized (i.e., until the actual declaring statement is
     39 * executed). The various LEXICAL opcodes need to check if the slot is an
     40 * uninitialized let declaration, represented by the magic value
     41 * JS_UNINITIALIZED_LEXICAL.
     42 */
     43 static inline bool IsUninitializedLexical(Value val) {
     44  // Use whyMagic here because JS_OPTIMIZED_OUT could flow into here.
     45  return val.isMagic() && val.whyMagic() == JS_UNINITIALIZED_LEXICAL;
     46 }
     47 
     48 static inline bool IsUninitializedLexicalSlot(HandleObject obj,
     49                                              const PropertyResult& prop) {
     50  MOZ_ASSERT(prop.isFound());
     51  if (obj->is<WithEnvironmentObject>()) {
     52    return false;
     53  }
     54 
     55  // Proxy hooks may return a non-native property.
     56  if (prop.isNonNativeProperty()) {
     57    return false;
     58  }
     59 
     60  PropertyInfo propInfo = prop.propertyInfo();
     61  if (!propInfo.isDataProperty()) {
     62    return false;
     63  }
     64 
     65  return IsUninitializedLexical(
     66      obj->as<NativeObject>().getSlot(propInfo.slot()));
     67 }
     68 
     69 static inline bool CheckUninitializedLexical(JSContext* cx,
     70                                             Handle<PropertyName*> name,
     71                                             HandleValue val) {
     72  if (IsUninitializedLexical(val)) {
     73    ReportRuntimeLexicalError(cx, JSMSG_UNINITIALIZED_LEXICAL, name);
     74    return false;
     75  }
     76  return true;
     77 }
     78 
     79 enum class GetNameMode { Normal, TypeOf };
     80 
     81 template <GetNameMode mode>
     82 inline bool FetchName(JSContext* cx, HandleObject receiver, HandleObject holder,
     83                      Handle<PropertyName*> name, const PropertyResult& prop,
     84                      MutableHandleValue vp) {
     85  if (prop.isNotFound()) {
     86    switch (mode) {
     87      case GetNameMode::Normal:
     88        ReportIsNotDefined(cx, name);
     89        return false;
     90      case GetNameMode::TypeOf:
     91        vp.setUndefined();
     92        return true;
     93    }
     94  }
     95 
     96  /* Take the slow path if shape was not found in a native object. */
     97  if (!receiver->is<NativeObject>() || !holder->is<NativeObject>() ||
     98      (receiver->is<WithEnvironmentObject>() &&
     99       receiver->as<WithEnvironmentObject>().supportUnscopables())) {
    100    Rooted<jsid> id(cx, NameToId(name));
    101    if (!GetProperty(cx, receiver, receiver, id, vp)) {
    102      return false;
    103    }
    104  } else {
    105    PropertyInfo propInfo = prop.propertyInfo();
    106    if (propInfo.isDataProperty()) {
    107      /* Fast path for Object instance properties. */
    108      vp.set(holder->as<NativeObject>().getSlot(propInfo.slot()));
    109    } else {
    110      // Unwrap 'with' environments for reasons given in
    111      // GetNameBoundInEnvironment.
    112      RootedObject normalized(cx, MaybeUnwrapWithEnvironment(receiver));
    113      RootedId id(cx, NameToId(name));
    114      if (!NativeGetExistingProperty(cx, normalized, holder.as<NativeObject>(),
    115                                     id, propInfo, vp)) {
    116        return false;
    117      }
    118    }
    119  }
    120 
    121  // We do our own explicit checking for |this|
    122  if (name == cx->names().dot_this_) {
    123    return true;
    124  }
    125 
    126  // NAME operations are the slow paths already, so unconditionally check
    127  // for uninitialized lets.
    128  return CheckUninitializedLexical(cx, name, vp);
    129 }
    130 
    131 inline bool FetchNameNoGC(NativeObject* pobj, PropertyResult prop, Value* vp) {
    132  if (prop.isNotFound()) {
    133    return false;
    134  }
    135 
    136  PropertyInfo propInfo = prop.propertyInfo();
    137  if (!propInfo.isDataProperty()) {
    138    return false;
    139  }
    140 
    141  *vp = pobj->getSlot(propInfo.slot());
    142  return !IsUninitializedLexical(*vp);
    143 }
    144 
    145 template <js::GetNameMode mode>
    146 inline bool GetEnvironmentName(JSContext* cx, HandleObject envChain,
    147                               Handle<PropertyName*> name,
    148                               MutableHandleValue vp) {
    149  {
    150    PropertyResult prop;
    151    NativeObject* pobj = nullptr;
    152    if (LookupNameNoGC(cx, name, envChain, &pobj, &prop)) {
    153      if (FetchNameNoGC(pobj, prop, vp.address())) {
    154        return true;
    155      }
    156    }
    157  }
    158 
    159  PropertyResult prop;
    160  RootedObject obj(cx), pobj(cx);
    161  if (!LookupName(cx, name, envChain, &obj, &pobj, &prop)) {
    162    return false;
    163  }
    164 
    165  return FetchName<mode>(cx, obj, pobj, name, prop, vp);
    166 }
    167 
    168 inline bool HasOwnProperty(JSContext* cx, HandleValue val, HandleValue idValue,
    169                           bool* result) {
    170  // As an optimization, provide a fast path when rooting is not necessary and
    171  // we can safely retrieve the object's shape.
    172  jsid id;
    173  if (val.isObject() && idValue.isPrimitive() &&
    174      PrimitiveValueToId<NoGC>(cx, idValue, &id)) {
    175    JSObject* obj = &val.toObject();
    176    PropertyResult prop;
    177    if (obj->is<NativeObject>() &&
    178        NativeLookupOwnProperty<NoGC>(cx, &obj->as<NativeObject>(), id,
    179                                      &prop)) {
    180      *result = prop.isFound();
    181      return true;
    182    }
    183  }
    184 
    185  // Step 1.
    186  RootedId key(cx);
    187  if (!ToPropertyKey(cx, idValue, &key)) {
    188    return false;
    189  }
    190 
    191  // Step 2.
    192  RootedObject obj(cx, ToObject(cx, val));
    193  if (!obj) {
    194    return false;
    195  }
    196 
    197  // Step 3.
    198  return HasOwnProperty(cx, obj, key, result);
    199 }
    200 
    201 inline bool GetIntrinsicOperation(JSContext* cx, HandleScript script,
    202                                  jsbytecode* pc, MutableHandleValue vp) {
    203  Rooted<PropertyName*> name(cx, script->getName(pc));
    204  return GlobalObject::getIntrinsicValue(cx, cx->global(), name, vp);
    205 }
    206 
    207 inline bool SetIntrinsicOperation(JSContext* cx, JSScript* script,
    208                                  jsbytecode* pc, HandleValue val) {
    209  Rooted<PropertyName*> name(cx, script->getName(pc));
    210  return GlobalObject::setIntrinsicValue(cx, cx->global(), name, val);
    211 }
    212 
    213 inline bool SetNameOperation(JSContext* cx, JSScript* script, jsbytecode* pc,
    214                             HandleObject env, HandleValue val) {
    215  MOZ_ASSERT(JSOp(*pc) == JSOp::SetName || JSOp(*pc) == JSOp::StrictSetName ||
    216             JSOp(*pc) == JSOp::SetGName || JSOp(*pc) == JSOp::StrictSetGName);
    217  MOZ_ASSERT_IF(
    218      JSOp(*pc) == JSOp::SetGName || JSOp(*pc) == JSOp::StrictSetGName,
    219      !script->hasNonSyntacticScope());
    220  MOZ_ASSERT_IF(
    221      JSOp(*pc) == JSOp::SetGName || JSOp(*pc) == JSOp::StrictSetGName,
    222      env == cx->global() || env == &cx->global()->lexicalEnvironment() ||
    223          env->is<RuntimeLexicalErrorObject>());
    224 
    225  bool strict =
    226      JSOp(*pc) == JSOp::StrictSetName || JSOp(*pc) == JSOp::StrictSetGName;
    227  Rooted<PropertyName*> name(cx, script->getName(pc));
    228 
    229  // In strict mode, assigning to an undeclared global variable is an
    230  // error. To detect this, we call NativeSetProperty directly and pass
    231  // Unqualified. It stores the error, if any, in |result|.
    232  bool ok;
    233  ObjectOpResult result;
    234  RootedId id(cx, NameToId(name));
    235  RootedValue receiver(cx, ObjectValue(*env));
    236  if (env->isUnqualifiedVarObj()) {
    237    Rooted<NativeObject*> varobj(cx);
    238    if (env->is<DebugEnvironmentProxy>()) {
    239      varobj =
    240          &env->as<DebugEnvironmentProxy>().environment().as<NativeObject>();
    241    } else {
    242      varobj = &env->as<NativeObject>();
    243    }
    244    MOZ_ASSERT(!varobj->getOpsSetProperty());
    245    ok = NativeSetProperty<Unqualified>(cx, varobj, id, val, receiver, result);
    246  } else {
    247    ok = SetProperty(cx, env, id, val, receiver, result);
    248  }
    249  return ok && result.checkStrictModeError(cx, env, id, strict);
    250 }
    251 
    252 inline void InitGlobalLexicalOperation(
    253    JSContext* cx, ExtensibleLexicalEnvironmentObject* lexicalEnv,
    254    JSScript* script, jsbytecode* pc, HandleValue value) {
    255  MOZ_ASSERT_IF(!script->hasNonSyntacticScope(),
    256                lexicalEnv == &cx->global()->lexicalEnvironment());
    257  MOZ_ASSERT(JSOp(*pc) == JSOp::InitGLexical);
    258 
    259  mozilla::Maybe<PropertyInfo> prop =
    260      lexicalEnv->lookup(cx, script->getName(pc));
    261  MOZ_ASSERT(prop.isSome());
    262  MOZ_ASSERT(IsUninitializedLexical(lexicalEnv->getSlot(prop->slot())));
    263 
    264  // Note: we don't have to call Watchtower::watchPropertyValueChange because
    265  // this is an initialization instead of a mutation. We don't optimize loads of
    266  // uninitialized lexicals in the JIT.
    267  lexicalEnv->setSlot(prop->slot(), value);
    268 }
    269 
    270 inline bool InitPropertyOperation(JSContext* cx, jsbytecode* pc,
    271                                  HandleObject obj, Handle<PropertyName*> name,
    272                                  HandleValue rhs) {
    273  unsigned propAttrs = GetInitDataPropAttrs(JSOp(*pc));
    274  return DefineDataProperty(cx, obj, name, rhs, propAttrs);
    275 }
    276 
    277 static MOZ_ALWAYS_INLINE bool NegOperation(JSContext* cx,
    278                                           MutableHandleValue val,
    279                                           MutableHandleValue res) {
    280  /*
    281   * When the operand is int jsval, INT32_FITS_IN_JSVAL(i) implies
    282   * INT32_FITS_IN_JSVAL(-i) unless i is 0 or INT32_MIN when the
    283   * results, -0.0 or INT32_MAX + 1, are double values.
    284   */
    285  int32_t i;
    286  if (val.isInt32() && (i = val.toInt32()) != 0 && i != INT32_MIN) {
    287    res.setInt32(-i);
    288    return true;
    289  }
    290 
    291  if (!ToNumeric(cx, val)) {
    292    return false;
    293  }
    294 
    295  if (val.isBigInt()) {
    296    return BigInt::negValue(cx, val, res);
    297  }
    298 
    299  res.setNumber(-val.toNumber());
    300  return true;
    301 }
    302 
    303 static MOZ_ALWAYS_INLINE bool IncOperation(JSContext* cx, HandleValue val,
    304                                           MutableHandleValue res) {
    305  int32_t i;
    306  if (val.isInt32() && (i = val.toInt32()) != INT32_MAX) {
    307    res.setInt32(i + 1);
    308    return true;
    309  }
    310 
    311  if (val.isNumber()) {
    312    res.setNumber(val.toNumber() + 1);
    313    return true;
    314  }
    315 
    316  MOZ_ASSERT(val.isBigInt(), "+1 only callable on result of JSOp::ToNumeric");
    317  return BigInt::incValue(cx, val, res);
    318 }
    319 
    320 static MOZ_ALWAYS_INLINE bool DecOperation(JSContext* cx, HandleValue val,
    321                                           MutableHandleValue res) {
    322  int32_t i;
    323  if (val.isInt32() && (i = val.toInt32()) != INT32_MIN) {
    324    res.setInt32(i - 1);
    325    return true;
    326  }
    327 
    328  if (val.isNumber()) {
    329    res.setNumber(val.toNumber() - 1);
    330    return true;
    331  }
    332 
    333  MOZ_ASSERT(val.isBigInt(), "-1 only callable on result of JSOp::ToNumeric");
    334  return BigInt::decValue(cx, val, res);
    335 }
    336 
    337 static MOZ_ALWAYS_INLINE bool ToPropertyKeyOperation(JSContext* cx,
    338                                                     HandleValue idval,
    339                                                     MutableHandleValue res) {
    340  if (idval.isInt32()) {
    341    res.set(idval);
    342    return true;
    343  }
    344 
    345  RootedId id(cx);
    346  if (!ToPropertyKey(cx, idval, &id)) {
    347    return false;
    348  }
    349 
    350  res.set(IdToValue(id));
    351  return true;
    352 }
    353 
    354 static MOZ_ALWAYS_INLINE bool GetObjectElementOperation(
    355    JSContext* cx, JSOp op, JS::HandleObject obj, JS::HandleValue receiver,
    356    HandleValue key, MutableHandleValue res) {
    357  MOZ_ASSERT(op == JSOp::GetElem || op == JSOp::GetElemSuper);
    358  MOZ_ASSERT_IF(op == JSOp::GetElem, obj == &receiver.toObject());
    359 
    360  do {
    361    uint32_t index;
    362    if (IsDefinitelyIndex(key, &index)) {
    363      if (GetElementNoGC(cx, obj, receiver, index, res.address())) {
    364        break;
    365      }
    366 
    367      if (!GetElement(cx, obj, receiver, index, res)) {
    368        return false;
    369      }
    370      break;
    371    }
    372 
    373    if (key.isString()) {
    374      JSAtom* name = AtomizeString(cx, key.toString());
    375      if (!name) {
    376        return false;
    377      }
    378      if (name->isIndex(&index)) {
    379        if (GetElementNoGC(cx, obj, receiver, index, res.address())) {
    380          break;
    381        }
    382      } else {
    383        if (GetPropertyNoGC(cx, obj, receiver, name->asPropertyName(),
    384                            res.address())) {
    385          break;
    386        }
    387      }
    388    }
    389 
    390    RootedId id(cx);
    391    if (!ToPropertyKey(cx, key, &id)) {
    392      return false;
    393    }
    394    if (!GetProperty(cx, obj, receiver, id, res)) {
    395      return false;
    396    }
    397  } while (false);
    398 
    399  cx->debugOnlyCheck(res);
    400  return true;
    401 }
    402 
    403 static MOZ_ALWAYS_INLINE bool GetPrimitiveElementOperation(
    404    JSContext* cx, JS::HandleValue receiver, int receiverIndex, HandleValue key,
    405    MutableHandleValue res) {
    406  // FIXME: Bug 1234324 We shouldn't be boxing here.
    407  RootedObject boxed(
    408      cx, ToObjectFromStackForPropertyAccess(cx, receiver, receiverIndex, key));
    409  if (!boxed) {
    410    return false;
    411  }
    412 
    413  do {
    414    uint32_t index;
    415    if (IsDefinitelyIndex(key, &index)) {
    416      if (GetElementNoGC(cx, boxed, receiver, index, res.address())) {
    417        break;
    418      }
    419 
    420      if (!GetElement(cx, boxed, receiver, index, res)) {
    421        return false;
    422      }
    423      break;
    424    }
    425 
    426    if (key.isString()) {
    427      JSAtom* name = AtomizeString(cx, key.toString());
    428      if (!name) {
    429        return false;
    430      }
    431      if (name->isIndex(&index)) {
    432        if (GetElementNoGC(cx, boxed, receiver, index, res.address())) {
    433          break;
    434        }
    435      } else {
    436        if (GetPropertyNoGC(cx, boxed, receiver, name->asPropertyName(),
    437                            res.address())) {
    438          break;
    439        }
    440      }
    441    }
    442 
    443    RootedId id(cx);
    444    if (!ToPropertyKey(cx, key, &id)) {
    445      return false;
    446    }
    447    if (!GetProperty(cx, boxed, receiver, id, res)) {
    448      return false;
    449    }
    450  } while (false);
    451 
    452  cx->debugOnlyCheck(res);
    453  return true;
    454 }
    455 
    456 static MOZ_ALWAYS_INLINE bool GetElementOperationWithStackIndex(
    457    JSContext* cx, HandleValue lref, int lrefIndex, HandleValue rref,
    458    MutableHandleValue res) {
    459  uint32_t index;
    460  if (lref.isString() && IsDefinitelyIndex(rref, &index)) {
    461    JSString* str = lref.toString();
    462    if (index < str->length()) {
    463      str = cx->staticStrings().getUnitStringForElement(cx, str, index);
    464      if (!str) {
    465        return false;
    466      }
    467      res.setString(str);
    468      return true;
    469    }
    470  }
    471 
    472  if (lref.isPrimitive()) {
    473    return GetPrimitiveElementOperation(cx, lref, lrefIndex, rref, res);
    474  }
    475 
    476  RootedObject obj(cx, &lref.toObject());
    477  return GetObjectElementOperation(cx, JSOp::GetElem, obj, lref, rref, res);
    478 }
    479 
    480 // Wrapper for callVM from JIT.
    481 static MOZ_ALWAYS_INLINE bool GetElementOperation(JSContext* cx,
    482                                                  HandleValue lref,
    483                                                  HandleValue rref,
    484                                                  MutableHandleValue res) {
    485  return GetElementOperationWithStackIndex(cx, lref, JSDVG_SEARCH_STACK, rref,
    486                                           res);
    487 }
    488 
    489 static MOZ_ALWAYS_INLINE JSString* TypeOfOperation(const Value& v,
    490                                                   JSRuntime* rt) {
    491  JSType type = js::TypeOfValue(v);
    492  return TypeName(type, *rt->commonNames);
    493 }
    494 
    495 static MOZ_ALWAYS_INLINE bool InitElemOperation(JSContext* cx, jsbytecode* pc,
    496                                                HandleObject obj,
    497                                                HandleValue idval,
    498                                                HandleValue val) {
    499  MOZ_ASSERT(!val.isMagic(JS_ELEMENTS_HOLE));
    500 
    501  RootedId id(cx);
    502  if (!ToPropertyKey(cx, idval, &id)) {
    503    return false;
    504  }
    505 
    506  unsigned flags = GetInitDataPropAttrs(JSOp(*pc));
    507  return DefineDataProperty(cx, obj, id, val, flags);
    508 }
    509 
    510 static MOZ_ALWAYS_INLINE bool CheckPrivateFieldOperation(JSContext* cx,
    511                                                         jsbytecode* pc,
    512                                                         HandleValue val,
    513                                                         HandleValue idval,
    514                                                         bool* result) {
    515  MOZ_ASSERT(idval.isSymbol());
    516  MOZ_ASSERT(idval.toSymbol()->isPrivateName());
    517 
    518  // Result had better not be a nullptr.
    519  MOZ_ASSERT(result);
    520 
    521  ThrowCondition condition;
    522  ThrowMsgKind msgKind;
    523  GetCheckPrivateFieldOperands(pc, &condition, &msgKind);
    524 
    525  // When we are using OnlyCheckRhs, we are implementing PrivateInExpr
    526  // This requires we throw if the rhs is not an object;
    527  //
    528  // The InlineCache for CheckPrivateField already checks for a
    529  // non-object rhs and refuses to attach in that circumstance.
    530  if (condition == ThrowCondition::OnlyCheckRhs) {
    531    if (!val.isObject()) {
    532      ReportInNotObjectError(cx, idval, val);
    533      return false;
    534    }
    535  }
    536 
    537  // Invoke the HostEnsureCanAddPrivateElement ( O ) host hook here
    538  // if the code is attempting to attach a new private element (which
    539  // corresponds to the ThrowHas Throw Condition).
    540  if (condition == ThrowCondition::ThrowHas) {
    541    if (JS::EnsureCanAddPrivateElementOp op =
    542            cx->runtime()->canAddPrivateElement) {
    543      if (!op(cx, val)) {
    544        return false;
    545      }
    546    }
    547  }
    548 
    549  if (!HasOwnProperty(cx, val, idval, result)) {
    550    return false;
    551  }
    552 
    553  if (!CheckPrivateFieldWillThrow(condition, *result)) {
    554    return true;
    555  }
    556 
    557  // Throw!
    558  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    559                            ThrowMsgKindToErrNum(msgKind));
    560  return false;
    561 }
    562 
    563 static inline JS::Symbol* NewPrivateName(JSContext* cx, Handle<JSAtom*> name) {
    564  return JS::Symbol::new_(cx, JS::SymbolCode::PrivateNameSymbol, name);
    565 }
    566 
    567 inline bool InitElemIncOperation(JSContext* cx, Handle<ArrayObject*> arr,
    568                                 uint32_t index, HandleValue val) {
    569  if (index == INT32_MAX) {
    570    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    571                              JSMSG_SPREAD_TOO_LARGE);
    572    return false;
    573  }
    574 
    575  // If val is a hole, do not call DefineDataElement.
    576  if (val.isMagic(JS_ELEMENTS_HOLE)) {
    577    // Always call SetLengthProperty even if this is not the last element
    578    // initialiser, because this may be followed by a SpreadElement loop,
    579    // which will not set the array length if nothing is spread.
    580    return SetLengthProperty(cx, arr, index + 1);
    581  }
    582 
    583  return DefineDataElement(cx, arr, index, val, JSPROP_ENUMERATE);
    584 }
    585 
    586 inline JSFunction* ReportIfNotFunction(
    587    JSContext* cx, HandleValue v, MaybeConstruct construct = NO_CONSTRUCT) {
    588  if (v.isObject() && v.toObject().is<JSFunction>()) {
    589    return &v.toObject().as<JSFunction>();
    590  }
    591 
    592  ReportIsNotFunction(cx, v, -1, construct);
    593  return nullptr;
    594 }
    595 
    596 static inline JSObject* SuperFunOperation(JSObject* callee) {
    597  MOZ_ASSERT(callee->as<JSFunction>().isClassConstructor());
    598  MOZ_ASSERT(
    599      callee->as<JSFunction>().baseScript()->isDerivedClassConstructor());
    600 
    601  return callee->as<JSFunction>().staticPrototype();
    602 }
    603 
    604 static inline JSObject* HomeObjectSuperBase(JSObject* homeObj) {
    605  MOZ_ASSERT(homeObj->is<PlainObject>() || homeObj->is<JSFunction>());
    606 
    607  return homeObj->staticPrototype();
    608 }
    609 
    610 static MOZ_ALWAYS_INLINE bool AddOperation(JSContext* cx,
    611                                           MutableHandleValue lhs,
    612                                           MutableHandleValue rhs,
    613                                           MutableHandleValue res) {
    614  if (lhs.isInt32() && rhs.isInt32()) {
    615    int32_t l = lhs.toInt32(), r = rhs.toInt32();
    616    int32_t t;
    617    if (MOZ_LIKELY(mozilla::SafeAdd(l, r, &t))) {
    618      res.setInt32(t);
    619      return true;
    620    }
    621  }
    622 
    623  if (!ToPrimitive(cx, lhs)) {
    624    return false;
    625  }
    626  if (!ToPrimitive(cx, rhs)) {
    627    return false;
    628  }
    629 
    630  bool lIsString = lhs.isString();
    631  bool rIsString = rhs.isString();
    632  if (lIsString || rIsString) {
    633    JSString* lstr;
    634    if (lIsString) {
    635      lstr = lhs.toString();
    636    } else {
    637      lstr = ToString<CanGC>(cx, lhs);
    638      if (!lstr) {
    639        return false;
    640      }
    641    }
    642 
    643    JSString* rstr;
    644    if (rIsString) {
    645      rstr = rhs.toString();
    646    } else {
    647      // Save/restore lstr in case of GC activity under ToString.
    648      lhs.setString(lstr);
    649      rstr = ToString<CanGC>(cx, rhs);
    650      if (!rstr) {
    651        return false;
    652      }
    653      lstr = lhs.toString();
    654    }
    655    JSString* str = ConcatStrings<NoGC>(cx, lstr, rstr);
    656    if (!str) {
    657      RootedString nlstr(cx, lstr), nrstr(cx, rstr);
    658      str = ConcatStrings<CanGC>(cx, nlstr, nrstr);
    659      if (!str) {
    660        return false;
    661      }
    662    }
    663    res.setString(str);
    664    return true;
    665  }
    666 
    667  if (!ToNumeric(cx, lhs) || !ToNumeric(cx, rhs)) {
    668    return false;
    669  }
    670 
    671  if (lhs.isBigInt() || rhs.isBigInt()) {
    672    return BigInt::addValue(cx, lhs, rhs, res);
    673  }
    674 
    675  res.setNumber(lhs.toNumber() + rhs.toNumber());
    676  return true;
    677 }
    678 
    679 static MOZ_ALWAYS_INLINE bool SubOperation(JSContext* cx,
    680                                           MutableHandleValue lhs,
    681                                           MutableHandleValue rhs,
    682                                           MutableHandleValue res) {
    683  if (!ToNumeric(cx, lhs) || !ToNumeric(cx, rhs)) {
    684    return false;
    685  }
    686 
    687  if (lhs.isBigInt() || rhs.isBigInt()) {
    688    return BigInt::subValue(cx, lhs, rhs, res);
    689  }
    690 
    691  res.setNumber(lhs.toNumber() - rhs.toNumber());
    692  return true;
    693 }
    694 
    695 static MOZ_ALWAYS_INLINE bool MulOperation(JSContext* cx,
    696                                           MutableHandleValue lhs,
    697                                           MutableHandleValue rhs,
    698                                           MutableHandleValue res) {
    699  if (!ToNumeric(cx, lhs) || !ToNumeric(cx, rhs)) {
    700    return false;
    701  }
    702 
    703  if (lhs.isBigInt() || rhs.isBigInt()) {
    704    return BigInt::mulValue(cx, lhs, rhs, res);
    705  }
    706 
    707  res.setNumber(lhs.toNumber() * rhs.toNumber());
    708  return true;
    709 }
    710 
    711 static MOZ_ALWAYS_INLINE bool DivOperation(JSContext* cx,
    712                                           MutableHandleValue lhs,
    713                                           MutableHandleValue rhs,
    714                                           MutableHandleValue res) {
    715  if (!ToNumeric(cx, lhs) || !ToNumeric(cx, rhs)) {
    716    return false;
    717  }
    718 
    719  if (lhs.isBigInt() || rhs.isBigInt()) {
    720    return BigInt::divValue(cx, lhs, rhs, res);
    721  }
    722 
    723  res.setNumber(NumberDiv(lhs.toNumber(), rhs.toNumber()));
    724  return true;
    725 }
    726 
    727 static MOZ_ALWAYS_INLINE bool ModOperation(JSContext* cx,
    728                                           MutableHandleValue lhs,
    729                                           MutableHandleValue rhs,
    730                                           MutableHandleValue res) {
    731  int32_t l, r;
    732  if (lhs.isInt32() && rhs.isInt32() && (l = lhs.toInt32()) >= 0 &&
    733      (r = rhs.toInt32()) > 0) {
    734    int32_t mod = l % r;
    735    res.setInt32(mod);
    736    return true;
    737  }
    738 
    739  if (!ToNumeric(cx, lhs) || !ToNumeric(cx, rhs)) {
    740    return false;
    741  }
    742 
    743  if (lhs.isBigInt() || rhs.isBigInt()) {
    744    return BigInt::modValue(cx, lhs, rhs, res);
    745  }
    746 
    747  res.setNumber(NumberMod(lhs.toNumber(), rhs.toNumber()));
    748  return true;
    749 }
    750 
    751 static MOZ_ALWAYS_INLINE bool PowOperation(JSContext* cx,
    752                                           MutableHandleValue lhs,
    753                                           MutableHandleValue rhs,
    754                                           MutableHandleValue res) {
    755  if (!ToNumeric(cx, lhs) || !ToNumeric(cx, rhs)) {
    756    return false;
    757  }
    758 
    759  if (lhs.isBigInt() || rhs.isBigInt()) {
    760    return BigInt::powValue(cx, lhs, rhs, res);
    761  }
    762 
    763  res.setNumber(ecmaPow(lhs.toNumber(), rhs.toNumber()));
    764  return true;
    765 }
    766 
    767 static MOZ_ALWAYS_INLINE bool BitNotOperation(JSContext* cx,
    768                                              MutableHandleValue in,
    769                                              MutableHandleValue out) {
    770  if (!ToInt32OrBigInt(cx, in)) {
    771    return false;
    772  }
    773 
    774  if (in.isBigInt()) {
    775    return BigInt::bitNotValue(cx, in, out);
    776  }
    777 
    778  out.setInt32(~in.toInt32());
    779  return true;
    780 }
    781 
    782 static MOZ_ALWAYS_INLINE bool BitXorOperation(JSContext* cx,
    783                                              MutableHandleValue lhs,
    784                                              MutableHandleValue rhs,
    785                                              MutableHandleValue out) {
    786  if (!ToInt32OrBigInt(cx, lhs) || !ToInt32OrBigInt(cx, rhs)) {
    787    return false;
    788  }
    789 
    790  if (lhs.isBigInt() || rhs.isBigInt()) {
    791    return BigInt::bitXorValue(cx, lhs, rhs, out);
    792  }
    793 
    794  out.setInt32(lhs.toInt32() ^ rhs.toInt32());
    795  return true;
    796 }
    797 
    798 static MOZ_ALWAYS_INLINE bool BitOrOperation(JSContext* cx,
    799                                             MutableHandleValue lhs,
    800                                             MutableHandleValue rhs,
    801                                             MutableHandleValue out) {
    802  if (!ToInt32OrBigInt(cx, lhs) || !ToInt32OrBigInt(cx, rhs)) {
    803    return false;
    804  }
    805 
    806  if (lhs.isBigInt() || rhs.isBigInt()) {
    807    return BigInt::bitOrValue(cx, lhs, rhs, out);
    808  }
    809 
    810  out.setInt32(lhs.toInt32() | rhs.toInt32());
    811  return true;
    812 }
    813 
    814 static MOZ_ALWAYS_INLINE bool BitAndOperation(JSContext* cx,
    815                                              MutableHandleValue lhs,
    816                                              MutableHandleValue rhs,
    817                                              MutableHandleValue out) {
    818  if (!ToInt32OrBigInt(cx, lhs) || !ToInt32OrBigInt(cx, rhs)) {
    819    return false;
    820  }
    821 
    822  if (lhs.isBigInt() || rhs.isBigInt()) {
    823    return BigInt::bitAndValue(cx, lhs, rhs, out);
    824  }
    825 
    826  out.setInt32(lhs.toInt32() & rhs.toInt32());
    827  return true;
    828 }
    829 
    830 static MOZ_ALWAYS_INLINE bool BitLshOperation(JSContext* cx,
    831                                              MutableHandleValue lhs,
    832                                              MutableHandleValue rhs,
    833                                              MutableHandleValue out) {
    834  if (!ToInt32OrBigInt(cx, lhs) || !ToInt32OrBigInt(cx, rhs)) {
    835    return false;
    836  }
    837 
    838  if (lhs.isBigInt() || rhs.isBigInt()) {
    839    return BigInt::lshValue(cx, lhs, rhs, out);
    840  }
    841 
    842  // Signed left-shift is undefined on overflow, so |lhs << (rhs & 31)| won't
    843  // work.  Instead, convert to unsigned space (where overflow is treated
    844  // modularly), perform the operation there, then convert back.
    845  uint32_t left = static_cast<uint32_t>(lhs.toInt32());
    846  uint8_t right = rhs.toInt32() & 31;
    847  out.setInt32(mozilla::WrapToSigned(left << right));
    848  return true;
    849 }
    850 
    851 static MOZ_ALWAYS_INLINE bool BitRshOperation(JSContext* cx,
    852                                              MutableHandleValue lhs,
    853                                              MutableHandleValue rhs,
    854                                              MutableHandleValue out) {
    855  if (!ToInt32OrBigInt(cx, lhs) || !ToInt32OrBigInt(cx, rhs)) {
    856    return false;
    857  }
    858 
    859  if (lhs.isBigInt() || rhs.isBigInt()) {
    860    return BigInt::rshValue(cx, lhs, rhs, out);
    861  }
    862 
    863  out.setInt32(lhs.toInt32() >> (rhs.toInt32() & 31));
    864  return true;
    865 }
    866 
    867 static MOZ_ALWAYS_INLINE bool UrshOperation(JSContext* cx,
    868                                            MutableHandleValue lhs,
    869                                            MutableHandleValue rhs,
    870                                            MutableHandleValue out) {
    871  if (!ToNumeric(cx, lhs) || !ToNumeric(cx, rhs)) {
    872    return false;
    873  }
    874 
    875  if (lhs.isBigInt() || rhs.isBigInt()) {
    876    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    877                              JSMSG_BIGINT_TO_NUMBER);
    878    return false;
    879  }
    880 
    881  uint32_t left;
    882  int32_t right;
    883  if (!ToUint32(cx, lhs, &left) || !ToInt32(cx, rhs, &right)) {
    884    return false;
    885  }
    886  left >>= right & 31;
    887  out.setNumber(uint32_t(left));
    888  return true;
    889 }
    890 
    891 static MOZ_ALWAYS_INLINE void InitElemArrayOperation(JSContext* cx,
    892                                                     jsbytecode* pc,
    893                                                     Handle<ArrayObject*> arr,
    894                                                     HandleValue val) {
    895  MOZ_ASSERT(JSOp(*pc) == JSOp::InitElemArray);
    896 
    897  // The dense elements must have been initialized up to this index. The JIT
    898  // implementation also depends on this.
    899  uint32_t index = GET_UINT32(pc);
    900  MOZ_ASSERT(index < arr->getDenseCapacity());
    901  MOZ_ASSERT(index == arr->getDenseInitializedLength());
    902 
    903  // Bump the initialized length even for hole values to ensure the
    904  // index == initLength invariant holds for later InitElemArray ops.
    905  arr->setDenseInitializedLength(index + 1);
    906 
    907  if (val.isMagic(JS_ELEMENTS_HOLE)) {
    908    arr->initDenseElementHole(index);
    909  } else {
    910    arr->initDenseElement(index, val);
    911  }
    912 }
    913 
    914 /*
    915 * As an optimization, the interpreter creates a handful of reserved rooted
    916 * variables at the beginning, thus inserting them into the Rooted list once
    917 * upon entry. ReservedRooted "borrows" a reserved Rooted variable and uses it
    918 * within a local scope, resetting the value to nullptr (or the appropriate
    919 * equivalent for T) at scope end. This avoids inserting/removing the Rooted
    920 * from the rooter list, while preventing stale values from being kept alive
    921 * unnecessarily.
    922 */
    923 template <typename T>
    924 class ReservedRooted : public RootedOperations<T, ReservedRooted<T>> {
    925  MutableHandle<T> savedRoot;
    926 
    927 public:
    928  ReservedRooted(MutableHandle<T> root, const T& ptr) : savedRoot(root) {
    929    root.set(ptr);
    930  }
    931 
    932  explicit ReservedRooted(MutableHandle<T> root) : savedRoot(root) { clear(); }
    933 
    934  ~ReservedRooted() { clear(); }
    935 
    936  void clear() { savedRoot.set(JS::SafelyInitialized<T>::create()); }
    937  void set(const T& p) { savedRoot.set(p); }
    938  operator Handle<T>() { return savedRoot; }
    939  MutableHandle<T> operator&() { return savedRoot; }
    940 
    941  DECLARE_NONPOINTER_ACCESSOR_METHODS(savedRoot.get())
    942  DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(savedRoot.get())
    943  DECLARE_POINTER_CONSTREF_OPS(T)
    944  DECLARE_POINTER_ASSIGN_OPS(ReservedRooted, T)
    945 };
    946 
    947 } /* namespace js */
    948 
    949 #endif /* vm_Interpreter_inl_h */