tor-browser

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

RealmFuses.cpp (22052B)


      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 #include "vm/RealmFuses.h"
      7 
      8 #include <array>
      9 
     10 #include "builtin/MapObject.h"
     11 #include "builtin/Promise.h"
     12 #include "builtin/RegExp.h"
     13 #include "builtin/WeakMapObject.h"
     14 #include "builtin/WeakSetObject.h"
     15 #include "js/experimental/TypedData.h"
     16 #include "vm/GlobalObject.h"
     17 #include "vm/NativeObject.h"
     18 #include "vm/ObjectOperations.h"
     19 #include "vm/Realm.h"
     20 #include "vm/SelfHosting.h"
     21 
     22 #include "vm/JSObject-inl.h"
     23 
     24 using namespace js;
     25 
     26 void js::InvalidatingRealmFuse::popFuse(JSContext* cx, RealmFuses& realmFuses) {
     27  // Return early if the fuse is already popped.
     28  if (!intact()) {
     29    return;
     30  }
     31 
     32  InvalidatingFuse::popFuse(cx);
     33 
     34  for (auto& fd : realmFuses.fuseDependencies) {
     35    fd.invalidateForFuse(cx, this);
     36  }
     37 }
     38 
     39 bool js::InvalidatingRealmFuse::addFuseDependency(
     40    JSContext* cx, const jit::IonScriptKey& ionScript) {
     41  MOZ_ASSERT(ionScript.script()->realm() == cx->realm());
     42  auto* scriptSet =
     43      cx->realm()->realmFuses.fuseDependencies.getOrCreateDependentScriptSet(
     44          cx, this);
     45  if (!scriptSet) {
     46    return false;
     47  }
     48 
     49  return scriptSet->addScriptForFuse(this, ionScript);
     50 }
     51 
     52 void js::PopsOptimizedGetIteratorFuse::popFuse(JSContext* cx,
     53                                               RealmFuses& realmFuses) {
     54  // Pop Self.
     55  RealmFuse::popFuse(cx);
     56 
     57  // Pop associated fuse in same realm as current object.
     58  realmFuses.optimizeGetIteratorFuse.popFuse(cx, realmFuses);
     59 }
     60 
     61 void js::PopsOptimizedArrayIteratorPrototypeFuse::popFuse(
     62    JSContext* cx, RealmFuses& realmFuses) {
     63  // Pop Self.
     64  RealmFuse::popFuse(cx);
     65 
     66  // Pop associated fuse in same realm as current object.
     67  realmFuses.optimizeArrayIteratorPrototypeFuse.popFuse(cx, realmFuses);
     68 }
     69 
     70 int32_t js::RealmFuses::fuseOffsets[uint8_t(
     71    RealmFuses::FuseIndex::LastFuseIndex)] = {
     72 #define FUSE(Name, LowerName) offsetof(RealmFuses, LowerName),
     73    FOR_EACH_REALM_FUSE(FUSE)
     74 #undef FUSE
     75 };
     76 
     77 // static
     78 int32_t js::RealmFuses::offsetOfFuseWordRelativeToRealm(
     79    RealmFuses::FuseIndex index) {
     80  int32_t base_offset = offsetof(Realm, realmFuses);
     81  int32_t fuse_offset = RealmFuses::fuseOffsets[uint8_t(index)];
     82  int32_t fuseWordOffset = GuardFuse::fuseOffset();
     83 
     84  return base_offset + fuse_offset + fuseWordOffset;
     85 }
     86 
     87 const char* js::RealmFuses::fuseNames[] = {
     88 #define FUSE(Name, LowerName) #LowerName,
     89    FOR_EACH_REALM_FUSE(FUSE)
     90 #undef FUSE
     91 };
     92 
     93 // TODO: It is not elegant that we have both this mechanism, but also
     94 // GuardFuse::name, and all the overrides for naming fuses. The issue is
     95 // that this method is static to handle consumers that don't have a
     96 // RealmFuses around but work with indexes (e.g. spew code).
     97 //
     98 // I'd love it if we had a better answer.
     99 const char* js::RealmFuses::getFuseName(RealmFuses::FuseIndex index) {
    100  uint8_t rawIndex = uint8_t(index);
    101  MOZ_ASSERT(index < RealmFuses::FuseIndex::LastFuseIndex);
    102  return fuseNames[rawIndex];
    103 }
    104 
    105 bool js::OptimizeGetIteratorFuse::checkInvariant(JSContext* cx) {
    106  // Simple invariant: this fuse merely reflects the conjunction of a group of
    107  // fuses, so if this fuse is intact, then the invariant it asserts is that
    108  // these two realm fuses are also intact.
    109  auto& realmFuses = cx->realm()->realmFuses;
    110  return realmFuses.arrayPrototypeIteratorFuse.intact() &&
    111         realmFuses.optimizeArrayIteratorPrototypeFuse.intact();
    112 }
    113 
    114 void js::OptimizeGetIteratorFuse::popFuse(JSContext* cx,
    115                                          RealmFuses& realmFuses) {
    116  InvalidatingRealmFuse::popFuse(cx, realmFuses);
    117  MOZ_ASSERT(cx->global());
    118  cx->runtime()->setUseCounter(cx->global(),
    119                               JSUseCounter::OPTIMIZE_GET_ITERATOR_FUSE);
    120 }
    121 
    122 bool js::OptimizeArrayIteratorPrototypeFuse::checkInvariant(JSContext* cx) {
    123  // Simple invariant: this fuse merely reflects the conjunction of a group of
    124  // fuses, so if this fuse is intact, then the invariant it asserts is that
    125  // these realm fuses are also intact.
    126  auto& realmFuses = cx->realm()->realmFuses;
    127  return realmFuses.arrayPrototypeIteratorNextFuse.intact() &&
    128         realmFuses.arrayIteratorPrototypeHasNoReturnProperty.intact() &&
    129         realmFuses.iteratorPrototypeHasNoReturnProperty.intact() &&
    130         realmFuses.arrayIteratorPrototypeHasIteratorProto.intact() &&
    131         realmFuses.iteratorPrototypeHasObjectProto.intact() &&
    132         realmFuses.objectPrototypeHasNoReturnProperty.intact();
    133 }
    134 
    135 static bool ObjectHasDataProperty(NativeObject* obj, PropertyKey key,
    136                                  Value* val) {
    137  mozilla::Maybe<PropertyInfo> prop = obj->lookupPure(key);
    138  if (prop.isNothing() || !prop->isDataProperty()) {
    139    return false;
    140  }
    141  *val = obj->getSlot(prop->slot());
    142  return true;
    143 }
    144 
    145 // Returns true if `obj` has a data property with the given `key` and its value
    146 // is `expectedValue`.
    147 static bool ObjectHasDataPropertyValue(NativeObject* obj, PropertyKey key,
    148                                       const Value& expectedValue) {
    149  Value v;
    150  if (!ObjectHasDataProperty(obj, key, &v)) {
    151    return false;
    152  }
    153  return v == expectedValue;
    154 }
    155 
    156 // Returns true if `obj` has a data property with the given `key` and its value
    157 // is a native function that matches `expectedFunction`.
    158 static bool ObjectHasDataPropertyFunction(NativeObject* obj, PropertyKey key,
    159                                          JSNative expectedFunction) {
    160  Value v;
    161  if (!ObjectHasDataProperty(obj, key, &v)) {
    162    return false;
    163  }
    164  if (!IsNativeFunction(v, expectedFunction)) {
    165    return false;
    166  }
    167  if (obj->realm() != v.toObject().as<JSFunction>().realm()) {
    168    return false;
    169  }
    170  return true;
    171 }
    172 
    173 // Returns true if `obj` has a data property with the given `key` and its value
    174 // is a self-hosted function with `selfHostedName`.
    175 static bool ObjectHasDataPropertyFunction(NativeObject* obj, PropertyKey key,
    176                                          PropertyName* selfHostedName) {
    177  Value v;
    178  if (!ObjectHasDataProperty(obj, key, &v)) {
    179    return false;
    180  }
    181  if (!IsSelfHostedFunctionWithName(v, selfHostedName)) {
    182    return false;
    183  }
    184  if (obj->realm() != v.toObject().as<JSFunction>().realm()) {
    185    return false;
    186  }
    187  return true;
    188 }
    189 
    190 static bool ObjectHasGetterProperty(NativeObject* obj, PropertyKey key,
    191                                    JSFunction** getter) {
    192  mozilla::Maybe<PropertyInfo> prop = obj->lookupPure(key);
    193  if (prop.isNothing() || !prop->isAccessorProperty()) {
    194    return false;
    195  }
    196  JSObject* getterObject = obj->getGetter(*prop);
    197  if (!getterObject || !getterObject->is<JSFunction>()) {
    198    return false;
    199  }
    200  if (obj->realm() != getterObject->as<JSFunction>().realm()) {
    201    return false;
    202  }
    203  *getter = &getterObject->as<JSFunction>();
    204  return true;
    205 }
    206 
    207 // Returns true if `obj` has an accessor property with the given `key` and the
    208 // getter is a native function that matches `expectedFunction`.
    209 static bool ObjectHasGetterFunction(NativeObject* obj, PropertyKey key,
    210                                    JSNative expectedGetter) {
    211  JSFunction* getter;
    212  if (!ObjectHasGetterProperty(obj, key, &getter)) {
    213    return false;
    214  }
    215  return IsNativeFunction(getter, expectedGetter);
    216 }
    217 
    218 // Returns true if `obj` has an accessor property with the given `key` and the
    219 // getter is a self-hosted function with `selfHostedName`.
    220 static bool ObjectHasGetterFunction(NativeObject* obj, PropertyKey key,
    221                                    PropertyName* selfHostedName) {
    222  JSFunction* getter;
    223  if (!ObjectHasGetterProperty(obj, key, &getter)) {
    224    return false;
    225  }
    226  return IsSelfHostedFunctionWithName(getter, selfHostedName);
    227 }
    228 
    229 bool js::ArrayPrototypeIteratorFuse::checkInvariant(JSContext* cx) {
    230  // Prototype must be Array.prototype.
    231  auto* proto = cx->global()->maybeGetArrayPrototype();
    232  if (!proto) {
    233    // No proto, invariant still holds
    234    return true;
    235  }
    236 
    237  PropertyKey iteratorKey =
    238      PropertyKey::Symbol(cx->wellKnownSymbols().iterator);
    239 
    240  // Ensure that Array.prototype's @@iterator slot is unchanged.
    241  return ObjectHasDataPropertyFunction(proto, iteratorKey,
    242                                       cx->names().dollar_ArrayValues_);
    243 }
    244 
    245 /* static */
    246 bool js::ArrayPrototypeIteratorNextFuse::checkInvariant(JSContext* cx) {
    247  auto* proto = cx->global()->maybeGetArrayIteratorPrototype();
    248 
    249  if (!proto) {
    250    // Invariant holds if there is no array iterator proto.
    251    return true;
    252  }
    253 
    254  // Ensure that %ArrayIteratorPrototype%'s "next" slot is unchanged.
    255  return ObjectHasDataPropertyFunction(proto, NameToId(cx->names().next),
    256                                       cx->names().ArrayIteratorNext);
    257 }
    258 
    259 static bool HasNoReturnName(JSContext* cx, JS::HandleObject proto) {
    260  if (!proto) {
    261    // Invariant holds if there is no array iterator proto.
    262    return true;
    263  }
    264 
    265  JS::RootedId returnName(cx, NameToId(cx->names().return_));
    266 
    267  // An alternative design here would chain together all the has-return-property
    268  // fuses such that the fuses each express a stronger invariant; for now these
    269  // fuses have only the invariant that each object -itself- has no return
    270  // property.
    271  bool found = true;
    272  if (!HasOwnProperty(cx, proto, returnName, &found)) {
    273    cx->recoverFromOutOfMemory();
    274    return true;
    275  }
    276 
    277  return !found;
    278 }
    279 
    280 /* static */
    281 bool js::ArrayIteratorPrototypeHasNoReturnProperty::checkInvariant(
    282    JSContext* cx) {
    283  RootedObject proto(cx, cx->global()->maybeGetArrayIteratorPrototype());
    284 
    285  if (!proto) {
    286    // Invariant holds if there is no array iterator proto.
    287    return true;
    288  }
    289 
    290  return HasNoReturnName(cx, proto);
    291 }
    292 
    293 /* static */
    294 bool js::IteratorPrototypeHasNoReturnProperty::checkInvariant(JSContext* cx) {
    295  RootedObject proto(cx, cx->global()->maybeGetIteratorPrototype());
    296 
    297  if (!proto) {
    298    // Invariant holds if there is no array iterator proto.
    299    return true;
    300  }
    301 
    302  return HasNoReturnName(cx, proto);
    303 }
    304 
    305 /* static */
    306 bool js::ArrayIteratorPrototypeHasIteratorProto::checkInvariant(JSContext* cx) {
    307  RootedObject proto(cx, cx->global()->maybeGetArrayIteratorPrototype());
    308  if (!proto) {
    309    // Invariant holds if there is no array iterator proto.
    310    return true;
    311  }
    312 
    313  RootedObject iterProto(cx, cx->global()->maybeGetIteratorPrototype());
    314  if (!iterProto) {
    315    MOZ_CRASH("Can we have the array iter proto without the iterator proto?");
    316    return true;
    317  }
    318 
    319  return proto->staticPrototype() == iterProto;
    320 }
    321 
    322 /* static */
    323 bool js::IteratorPrototypeHasObjectProto::checkInvariant(JSContext* cx) {
    324  RootedObject proto(cx, cx->global()->maybeGetIteratorPrototype());
    325  if (!proto) {
    326    // Invariant holds if there is no array iterator proto.
    327    return true;
    328  }
    329 
    330  return proto->staticPrototype() == &cx->global()->getObjectPrototype();
    331 }
    332 
    333 /* static */
    334 bool js::ObjectPrototypeHasNoReturnProperty::checkInvariant(JSContext* cx) {
    335  RootedObject proto(cx, &cx->global()->getObjectPrototype());
    336  return HasNoReturnName(cx, proto);
    337 }
    338 
    339 void js::OptimizeArraySpeciesFuse::popFuse(JSContext* cx,
    340                                           RealmFuses& realmFuses) {
    341  InvalidatingRealmFuse::popFuse(cx, realmFuses);
    342  MOZ_ASSERT(cx->global());
    343  cx->runtime()->setUseCounter(cx->global(),
    344                               JSUseCounter::OPTIMIZE_ARRAY_SPECIES_FUSE);
    345 }
    346 
    347 static bool SpeciesFuseCheckInvariant(JSContext* cx, JSProtoKey protoKey,
    348                                      PropertyName* selfHostedSpeciesAccessor) {
    349  // Prototype must be initialized.
    350  auto* proto = cx->global()->maybeGetPrototype<NativeObject>(protoKey);
    351  if (!proto) {
    352    // No proto, invariant still holds
    353    return true;
    354  }
    355 
    356  auto* ctor = cx->global()->maybeGetConstructor<NativeObject>(protoKey);
    357  MOZ_ASSERT(ctor);
    358 
    359  // Ensure the prototype's `constructor` slot is the original constructor.
    360  if (!ObjectHasDataPropertyValue(proto, NameToId(cx->names().constructor),
    361                                  ObjectValue(*ctor))) {
    362    return false;
    363  }
    364 
    365  // Ensure constructor's `@@species` slot is the original species getter.
    366  PropertyKey speciesKey = PropertyKey::Symbol(cx->wellKnownSymbols().species);
    367  return ObjectHasGetterFunction(ctor, speciesKey, selfHostedSpeciesAccessor);
    368 }
    369 
    370 bool js::OptimizeArraySpeciesFuse::checkInvariant(JSContext* cx) {
    371  return SpeciesFuseCheckInvariant(cx, JSProto_Array,
    372                                   cx->names().dollar_ArraySpecies_);
    373 }
    374 
    375 bool js::OptimizeArrayBufferSpeciesFuse::checkInvariant(JSContext* cx) {
    376  return SpeciesFuseCheckInvariant(cx, JSProto_ArrayBuffer,
    377                                   cx->names().dollar_ArrayBufferSpecies_);
    378 }
    379 
    380 bool js::OptimizeSharedArrayBufferSpeciesFuse::checkInvariant(JSContext* cx) {
    381  return SpeciesFuseCheckInvariant(
    382      cx, JSProto_SharedArrayBuffer,
    383      cx->names().dollar_SharedArrayBufferSpecies_);
    384 }
    385 
    386 bool js::OptimizeTypedArraySpeciesFuse::checkInvariant(JSContext* cx) {
    387  // Check `constructor` and `@@species` on %TypedArray%.
    388  if (!SpeciesFuseCheckInvariant(cx, JSProto_TypedArray,
    389                                 cx->names().dollar_TypedArraySpecies_)) {
    390    return false;
    391  }
    392 
    393  auto typedArrayProtoKeys = std::array{
    394 #define PROTO_KEY(_, T, N) JSProto_##N##Array,
    395      JS_FOR_EACH_TYPED_ARRAY(PROTO_KEY)
    396 #undef PROTO_KEY
    397  };
    398 
    399  auto* typedArrayproto =
    400      cx->global()->maybeGetPrototype<NativeObject>(JSProto_TypedArray);
    401 
    402  // Check all concrete TypedArray prototypes.
    403  for (auto protoKey : typedArrayProtoKeys) {
    404    // Prototype must be initialized.
    405    auto* proto = cx->global()->maybeGetPrototype<NativeObject>(protoKey);
    406    if (!proto) {
    407      // No proto, invariant still holds
    408      continue;
    409    }
    410    MOZ_ASSERT(typedArrayproto,
    411               "%TypedArray%.prototype must be initialized when TypedArray "
    412               "subclass is initialized");
    413 
    414    // Ensure the prototype's prototype is %TypedArray%.prototype.
    415    if (proto->staticPrototype() != typedArrayproto) {
    416      return false;
    417    }
    418 
    419    auto* ctor = cx->global()->maybeGetConstructor<NativeObject>(protoKey);
    420    MOZ_ASSERT(ctor);
    421 
    422    // Ensure the prototype's `constructor` slot is the original constructor.
    423    if (!ObjectHasDataPropertyValue(proto, NameToId(cx->names().constructor),
    424                                    ObjectValue(*ctor))) {
    425      return false;
    426    }
    427  }
    428 
    429  return true;
    430 }
    431 
    432 void js::OptimizePromiseLookupFuse::popFuse(JSContext* cx,
    433                                            RealmFuses& realmFuses) {
    434  RealmFuse::popFuse(cx, realmFuses);
    435  MOZ_ASSERT(cx->global());
    436  cx->runtime()->setUseCounter(cx->global(),
    437                               JSUseCounter::OPTIMIZE_PROMISE_LOOKUP_FUSE);
    438 }
    439 
    440 bool js::OptimizePromiseLookupFuse::checkInvariant(JSContext* cx) {
    441  // Prototype must be Promise.prototype.
    442  auto* proto = cx->global()->maybeGetPrototype<NativeObject>(JSProto_Promise);
    443  if (!proto) {
    444    // No proto, invariant still holds.
    445    return true;
    446  }
    447 
    448  auto* ctor = cx->global()->maybeGetConstructor<NativeObject>(JSProto_Promise);
    449  MOZ_ASSERT(ctor);
    450 
    451  // Ensure Promise.prototype's `constructor` slot is the `Promise` constructor.
    452  if (!ObjectHasDataPropertyValue(proto, NameToId(cx->names().constructor),
    453                                  ObjectValue(*ctor))) {
    454    return false;
    455  }
    456 
    457  // Ensure Promise.prototype's `then` slot is the original function.
    458  if (!ObjectHasDataPropertyFunction(proto, NameToId(cx->names().then),
    459                                     js::Promise_then)) {
    460    return false;
    461  }
    462 
    463  // Ensure Promise's `@@species` slot is the original getter.
    464  PropertyKey speciesKey = PropertyKey::Symbol(cx->wellKnownSymbols().species);
    465  if (!ObjectHasGetterFunction(ctor, speciesKey, js::Promise_static_species)) {
    466    return false;
    467  }
    468 
    469  // Ensure Promise's `resolve` slot is the original function.
    470  if (!ObjectHasDataPropertyFunction(ctor, NameToId(cx->names().resolve),
    471                                     js::Promise_static_resolve)) {
    472    return false;
    473  }
    474 
    475  return true;
    476 }
    477 
    478 bool js::OptimizeRegExpPrototypeFuse::checkInvariant(JSContext* cx) {
    479  auto* proto = cx->global()->maybeGetPrototype<NativeObject>(JSProto_RegExp);
    480  if (!proto) {
    481    // No proto, invariant still holds.
    482    return true;
    483  }
    484 
    485  // Check getters are unchanged.
    486  if (!ObjectHasGetterFunction(proto, NameToId(cx->names().flags),
    487                               cx->names().dollar_RegExpFlagsGetter_)) {
    488    return false;
    489  }
    490  if (!ObjectHasGetterFunction(proto, NameToId(cx->names().global),
    491                               regexp_global)) {
    492    return false;
    493  }
    494  if (!ObjectHasGetterFunction(proto, NameToId(cx->names().hasIndices),
    495                               regexp_hasIndices)) {
    496    return false;
    497  }
    498  if (!ObjectHasGetterFunction(proto, NameToId(cx->names().ignoreCase),
    499                               regexp_ignoreCase)) {
    500    return false;
    501  }
    502  if (!ObjectHasGetterFunction(proto, NameToId(cx->names().multiline),
    503                               regexp_multiline)) {
    504    return false;
    505  }
    506  if (!ObjectHasGetterFunction(proto, NameToId(cx->names().sticky),
    507                               regexp_sticky)) {
    508    return false;
    509  }
    510  if (!ObjectHasGetterFunction(proto, NameToId(cx->names().unicode),
    511                               regexp_unicode)) {
    512    return false;
    513  }
    514  if (!ObjectHasGetterFunction(proto, NameToId(cx->names().unicodeSets),
    515                               regexp_unicodeSets)) {
    516    return false;
    517  }
    518  if (!ObjectHasGetterFunction(proto, NameToId(cx->names().dotAll),
    519                               regexp_dotAll)) {
    520    return false;
    521  }
    522 
    523  // Check data properties are unchanged.
    524  if (!ObjectHasDataPropertyFunction(proto, NameToId(cx->names().exec),
    525                                     cx->names().RegExp_prototype_Exec)) {
    526    return false;
    527  }
    528  if (!ObjectHasDataPropertyFunction(
    529          proto, PropertyKey::Symbol(cx->wellKnownSymbols().match),
    530          cx->names().RegExpMatch)) {
    531    return false;
    532  }
    533  if (!ObjectHasDataPropertyFunction(
    534          proto, PropertyKey::Symbol(cx->wellKnownSymbols().matchAll),
    535          cx->names().RegExpMatchAll)) {
    536    return false;
    537  }
    538  if (!ObjectHasDataPropertyFunction(
    539          proto, PropertyKey::Symbol(cx->wellKnownSymbols().replace),
    540          cx->names().RegExpReplace)) {
    541    return false;
    542  }
    543  if (!ObjectHasDataPropertyFunction(
    544          proto, PropertyKey::Symbol(cx->wellKnownSymbols().search),
    545          cx->names().RegExpSearch)) {
    546    return false;
    547  }
    548  if (!ObjectHasDataPropertyFunction(
    549          proto, PropertyKey::Symbol(cx->wellKnownSymbols().split),
    550          cx->names().RegExpSplit)) {
    551    return false;
    552  }
    553 
    554  return true;
    555 }
    556 
    557 bool js::OptimizeMapObjectIteratorFuse::checkInvariant(JSContext* cx) {
    558  // Ensure Map.prototype's @@iterator slot is unchanged.
    559  auto* proto = cx->global()->maybeGetPrototype<NativeObject>(JSProto_Map);
    560  if (!proto) {
    561    // No proto, invariant still holds
    562    return true;
    563  }
    564  PropertyKey iteratorKey =
    565      PropertyKey::Symbol(cx->wellKnownSymbols().iterator);
    566  if (!ObjectHasDataPropertyFunction(proto, iteratorKey, MapObject::entries)) {
    567    return false;
    568  }
    569 
    570  // Ensure %MapIteratorPrototype%'s `next` slot is unchanged.
    571  auto* iterProto = cx->global()->maybeBuiltinProto(
    572      GlobalObject::ProtoKind::MapIteratorProto);
    573  if (!iterProto) {
    574    // No proto, invariant still holds
    575    return true;
    576  }
    577  return ObjectHasDataPropertyFunction(&iterProto->as<NativeObject>(),
    578                                       NameToId(cx->names().next),
    579                                       cx->names().MapIteratorNext);
    580 }
    581 
    582 bool js::OptimizeSetObjectIteratorFuse::checkInvariant(JSContext* cx) {
    583  // Ensure Set.prototype's @@iterator slot is unchanged.
    584  auto* proto = cx->global()->maybeGetPrototype<NativeObject>(JSProto_Set);
    585  if (!proto) {
    586    // No proto, invariant still holds
    587    return true;
    588  }
    589  PropertyKey iteratorKey =
    590      PropertyKey::Symbol(cx->wellKnownSymbols().iterator);
    591  if (!ObjectHasDataPropertyFunction(proto, iteratorKey, SetObject::values)) {
    592    return false;
    593  }
    594 
    595  // Ensure %SetIteratorPrototype%'s `next` slot is unchanged.
    596  auto* iterProto = cx->global()->maybeBuiltinProto(
    597      GlobalObject::ProtoKind::SetIteratorProto);
    598  if (!iterProto) {
    599    // No proto, invariant still holds
    600    return true;
    601  }
    602  return ObjectHasDataPropertyFunction(&iterProto->as<NativeObject>(),
    603                                       NameToId(cx->names().next),
    604                                       cx->names().SetIteratorNext);
    605 }
    606 
    607 bool js::OptimizeMapPrototypeSetFuse::checkInvariant(JSContext* cx) {
    608  auto* proto = cx->global()->maybeGetPrototype<NativeObject>(JSProto_Map);
    609  if (!proto) {
    610    // No proto, invariant still holds
    611    return true;
    612  }
    613  return ObjectHasDataPropertyFunction(proto, NameToId(cx->names().set),
    614                                       MapObject::set);
    615 }
    616 
    617 bool js::OptimizeSetPrototypeAddFuse::checkInvariant(JSContext* cx) {
    618  auto* proto = cx->global()->maybeGetPrototype<NativeObject>(JSProto_Set);
    619  if (!proto) {
    620    // No proto, invariant still holds
    621    return true;
    622  }
    623  return ObjectHasDataPropertyFunction(proto, NameToId(cx->names().add),
    624                                       SetObject::add);
    625 }
    626 
    627 bool js::OptimizeWeakMapPrototypeSetFuse::checkInvariant(JSContext* cx) {
    628  auto* proto = cx->global()->maybeGetPrototype<NativeObject>(JSProto_WeakMap);
    629  if (!proto) {
    630    // No proto, invariant still holds
    631    return true;
    632  }
    633  return ObjectHasDataPropertyFunction(proto, NameToId(cx->names().set),
    634                                       WeakMapObject::set);
    635 }
    636 
    637 bool js::OptimizeWeakSetPrototypeAddFuse::checkInvariant(JSContext* cx) {
    638  auto* proto = cx->global()->maybeGetPrototype<NativeObject>(JSProto_WeakSet);
    639  if (!proto) {
    640    // No proto, invariant still holds
    641    return true;
    642  }
    643  return ObjectHasDataPropertyFunction(proto, NameToId(cx->names().add),
    644                                       WeakSetObject::add);
    645 }