tor-browser

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

MapObject.cpp (63176B)


      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 #include "builtin/MapObject-inl.h"
      8 #include "builtin/MapObject.h"
      9 
     10 #include "jsapi.h"
     11 
     12 #include "builtin/OrderedHashTableObject.h"
     13 #include "gc/GCContext.h"
     14 #include "jit/InlinableNatives.h"
     15 #include "js/MapAndSet.h"
     16 #include "js/PropertyAndElement.h"  // JS_DefineFunctions
     17 #include "js/PropertySpec.h"
     18 #include "js/Utility.h"
     19 #include "vm/BigIntType.h"
     20 #include "vm/EqualityOperations.h"  // js::SameValue
     21 #include "vm/GlobalObject.h"
     22 #include "vm/Interpreter.h"
     23 #include "vm/JSContext.h"
     24 #include "vm/JSObject.h"
     25 #include "vm/SelfHosting.h"
     26 #include "vm/SymbolType.h"
     27 
     28 #include "builtin/OrderedHashTableObject-inl.h"
     29 #include "gc/GCContext-inl.h"
     30 #include "gc/Marking-inl.h"
     31 #include "gc/ObjectKind-inl.h"
     32 #include "vm/GeckoProfiler-inl.h"
     33 #include "vm/NativeObject-inl.h"
     34 
     35 using namespace js;
     36 
     37 using mozilla::NumberEqualsInt32;
     38 
     39 /*** HashableValue **********************************************************/
     40 
     41 static PreBarriered<Value> NormalizeDoubleValue(double d) {
     42  int32_t i;
     43  if (NumberEqualsInt32(d, &i)) {
     44    // Normalize int32_t-valued doubles to int32_t for faster hashing and
     45    // testing. Note: we use NumberEqualsInt32 here instead of NumberIsInt32
     46    // because we want -0 and 0 to be normalized to the same thing.
     47    return Int32Value(i);
     48  }
     49 
     50  // Normalize the sign bit of a NaN.
     51  return JS::CanonicalizedDoubleValue(d);
     52 }
     53 
     54 bool HashableValue::setValue(JSContext* cx, const Value& v) {
     55  if (v.isString()) {
     56    // Atomize so that hash() and operator==() are fast and infallible.
     57    JSString* str = AtomizeString(cx, v.toString());
     58    if (!str) {
     59      return false;
     60    }
     61    value = StringValue(str);
     62  } else if (v.isDouble()) {
     63    value = NormalizeDoubleValue(v.toDouble());
     64  } else {
     65    value = v;
     66  }
     67 
     68  MOZ_ASSERT(value.isUndefined() || value.isNull() || value.isBoolean() ||
     69             value.isNumber() || value.isString() || value.isSymbol() ||
     70             value.isObject() || value.isBigInt());
     71  return true;
     72 }
     73 
     74 static HashNumber HashValue(const Value& v,
     75                            const mozilla::HashCodeScrambler& hcs) {
     76  // HashableValue::setValue normalizes values so that the SameValue relation
     77  // on HashableValues is the same as the == relationship on
     78  // value.asRawBits(). So why not just return that? Security.
     79  //
     80  // To avoid revealing GC of atoms, string-based hash codes are computed
     81  // from the string contents rather than any pointer; to avoid revealing
     82  // addresses, pointer-based hash codes are computed using the
     83  // HashCodeScrambler.
     84 
     85  if (v.isString()) {
     86    return v.toString()->asAtom().hash();
     87  }
     88  if (v.isSymbol()) {
     89    return v.toSymbol()->hash();
     90  }
     91  if (v.isBigInt()) {
     92    return MaybeForwarded(v.toBigInt())->hash();
     93  }
     94  if (v.isObject()) {
     95    return hcs.scramble(v.asRawBits());
     96  }
     97 
     98  MOZ_ASSERT(!v.isGCThing(), "do not reveal pointers via hash codes");
     99  return mozilla::HashGeneric(v.asRawBits());
    100 }
    101 
    102 HashNumber HashableValue::hash(const mozilla::HashCodeScrambler& hcs) const {
    103  return HashValue(value, hcs);
    104 }
    105 
    106 bool HashableValue::equals(const HashableValue& other) const {
    107  // Two HashableValues are equal if they have equal bits.
    108  bool b = (value.asRawBits() == other.value.asRawBits());
    109 
    110  if (!b && (value.type() == other.value.type())) {
    111    if (value.isBigInt()) {
    112      // BigInt values are considered equal if they represent the same
    113      // mathematical value.
    114      b = BigInt::equal(value.toBigInt(), other.value.toBigInt());
    115    }
    116  }
    117 
    118 #ifdef DEBUG
    119  bool same;
    120  JSContext* cx = TlsContext.get();
    121  MOZ_ASSERT(SameValueZero(cx, value, other.value, &same));
    122  MOZ_ASSERT(same == b);
    123 #endif
    124  return b;
    125 }
    126 
    127 /*** MapIterator ************************************************************/
    128 
    129 namespace {} /* anonymous namespace */
    130 
    131 static const JSClassOps MapIteratorObjectClassOps = {
    132    nullptr,                      // addProperty
    133    nullptr,                      // delProperty
    134    nullptr,                      // enumerate
    135    nullptr,                      // newEnumerate
    136    nullptr,                      // resolve
    137    nullptr,                      // mayResolve
    138    MapIteratorObject::finalize,  // finalize
    139    nullptr,                      // call
    140    nullptr,                      // construct
    141    nullptr,                      // trace
    142 };
    143 
    144 static const ClassExtension MapIteratorObjectClassExtension = {
    145    MapIteratorObject::objectMoved,  // objectMovedOp
    146 };
    147 
    148 const JSClass MapIteratorObject::class_ = {
    149    "Map Iterator",
    150    JSCLASS_HAS_RESERVED_SLOTS(MapIteratorObject::SlotCount) |
    151        JSCLASS_FOREGROUND_FINALIZE | JSCLASS_SKIP_NURSERY_FINALIZE,
    152    &MapIteratorObjectClassOps,
    153    JS_NULL_CLASS_SPEC,
    154    &MapIteratorObjectClassExtension,
    155 };
    156 
    157 const JSFunctionSpec MapIteratorObject::methods[] = {
    158    JS_SELF_HOSTED_FN("next", "MapIteratorNext", 0, 0),
    159    JS_FS_END,
    160 };
    161 
    162 /* static */
    163 bool GlobalObject::initMapIteratorProto(JSContext* cx,
    164                                        Handle<GlobalObject*> global) {
    165  Rooted<JSObject*> base(
    166      cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
    167  if (!base) {
    168    return false;
    169  }
    170  Rooted<PlainObject*> proto(
    171      cx, GlobalObject::createBlankPrototypeInheriting<PlainObject>(cx, base));
    172  if (!proto) {
    173    return false;
    174  }
    175  if (!JS_DefineFunctions(cx, proto, MapIteratorObject::methods) ||
    176      !DefineToStringTag(cx, proto, cx->names().Map_Iterator_)) {
    177    return false;
    178  }
    179  if (!JSObject::setHasRealmFuseProperty(cx, proto)) {
    180    return false;
    181  }
    182  global->initBuiltinProto(ProtoKind::MapIteratorProto, proto);
    183  return true;
    184 }
    185 
    186 template <typename TableObject>
    187 static inline bool HasRegisteredNurseryIterators(TableObject* t) {
    188  Value v = t->getReservedSlot(TableObject::RegisteredNurseryIteratorsSlot);
    189  return v.toBoolean();
    190 }
    191 
    192 template <typename TableObject>
    193 static inline void SetRegisteredNurseryIterators(TableObject* t, bool b) {
    194  t->setReservedSlot(TableObject::RegisteredNurseryIteratorsSlot,
    195                     JS::BooleanValue(b));
    196 }
    197 
    198 MapIteratorObject* MapIteratorObject::create(JSContext* cx,
    199                                             Handle<MapObject*> mapobj,
    200                                             Kind kind) {
    201  Rooted<GlobalObject*> global(cx, &mapobj->global());
    202  Rooted<JSObject*> proto(
    203      cx, GlobalObject::getOrCreateMapIteratorPrototype(cx, global));
    204  if (!proto) {
    205    return nullptr;
    206  }
    207 
    208  MapIteratorObject* iterobj =
    209      NewObjectWithGivenProto<MapIteratorObject>(cx, proto);
    210  if (!iterobj) {
    211    return nullptr;
    212  }
    213 
    214  if (IsInsideNursery(iterobj) &&
    215      !HasRegisteredNurseryIterators(mapobj.get())) {
    216    if (!cx->nursery().addMapWithNurseryIterators(mapobj)) {
    217      ReportOutOfMemory(cx);
    218      return nullptr;
    219    }
    220    SetRegisteredNurseryIterators(mapobj.get(), true);
    221  }
    222 
    223  MapObject::Table(mapobj).initIterator(iterobj, kind);
    224 
    225  return iterobj;
    226 }
    227 
    228 void MapIteratorObject::finalize(JS::GCContext* gcx, JSObject* obj) {
    229  MOZ_ASSERT(gcx->onMainThread());
    230  MOZ_ASSERT(!IsInsideNursery(obj));
    231  if (obj->as<MapIteratorObject>().isActive()) {
    232    obj->as<MapIteratorObject>().unlink();
    233  }
    234 }
    235 
    236 size_t MapIteratorObject::objectMoved(JSObject* obj, JSObject* old) {
    237  MapIteratorObject* iter = &obj->as<MapIteratorObject>();
    238  if (!iter->isActive()) {
    239    return 0;
    240  }
    241  if (IsInsideNursery(old)) {
    242    MapObject* mapObj = iter->target();
    243    MapObject::Table(mapObj).relinkNurseryIterator(iter);
    244  } else {
    245    iter->updateListAfterMove(&old->as<MapIteratorObject>());
    246  }
    247  return 0;
    248 }
    249 
    250 MapObject* MapIteratorObject::target() const {
    251  MOZ_ASSERT(isActive(), "only active iterators have a target object");
    252  Value value = getFixedSlot(TargetSlot);
    253  return &MaybeForwarded(&value.toObject())->as<MapObject>();
    254 }
    255 
    256 bool MapIteratorObject::next(MapIteratorObject* mapIterator,
    257                             ArrayObject* resultPairObj) {
    258  // IC code calls this directly.
    259  AutoUnsafeCallWithABI unsafe;
    260 
    261  // Check invariants for inlined GetNextMapEntryForIterator.
    262 
    263  // The array should be tenured, so that post-barrier can be done simply.
    264  MOZ_ASSERT(resultPairObj->isTenured());
    265 
    266  // The array elements should be fixed.
    267  MOZ_ASSERT(resultPairObj->hasFixedElements());
    268  MOZ_ASSERT(resultPairObj->getDenseInitializedLength() == 2);
    269  MOZ_ASSERT(resultPairObj->getDenseCapacity() >= 2);
    270 
    271  if (!mapIterator->isActive()) {
    272    // Already done.
    273    return true;
    274  }
    275 
    276  auto storeResult = [resultPairObj](Kind kind, const auto& element) {
    277    switch (kind) {
    278      case Kind::Keys:
    279        resultPairObj->setDenseElement(0, element.key.get());
    280        break;
    281 
    282      case Kind::Values:
    283        resultPairObj->setDenseElement(1, element.value);
    284        break;
    285 
    286      case Kind::Entries: {
    287        resultPairObj->setDenseElement(0, element.key.get());
    288        resultPairObj->setDenseElement(1, element.value);
    289        break;
    290      }
    291    }
    292  };
    293  MapObject* mapObj = mapIterator->target();
    294  return MapObject::Table(mapObj).iteratorNext(mapIterator, storeResult);
    295 }
    296 
    297 /* static */
    298 JSObject* MapIteratorObject::createResultPair(JSContext* cx) {
    299  ArrayObject* resultPairObj =
    300      NewDenseFullyAllocatedArray(cx, 2, TenuredObject);
    301  if (!resultPairObj) {
    302    return nullptr;
    303  }
    304 
    305  resultPairObj->setDenseInitializedLength(2);
    306  resultPairObj->initDenseElement(0, NullValue());
    307  resultPairObj->initDenseElement(1, NullValue());
    308 
    309  return resultPairObj;
    310 }
    311 
    312 /*** Map ********************************************************************/
    313 
    314 struct js::UnbarrieredHashPolicy {
    315  using Lookup = Value;
    316  static HashNumber hash(const Lookup& v,
    317                         const mozilla::HashCodeScrambler& hcs) {
    318    return HashValue(v, hcs);
    319  }
    320  static bool match(const Value& k, const Lookup& l) { return k == l; }
    321  static bool isEmpty(const Value& v) { return v.isMagic(JS_HASH_KEY_EMPTY); }
    322  static void makeEmpty(Value* vp) { vp->setMagic(JS_HASH_KEY_EMPTY); }
    323 };
    324 
    325 // MapObject::Table, ::UnbarrieredTable and ::PreBarrieredTable must all have
    326 // the same memory layout.
    327 static_assert(sizeof(MapObject::Table::Entry) ==
    328              sizeof(MapObject::UnbarrieredTable::Entry));
    329 static_assert(sizeof(MapObject::Table::Entry) ==
    330              sizeof(MapObject::PreBarrieredTable::Entry));
    331 
    332 const JSClassOps MapObject::classOps_ = {
    333    nullptr,  // addProperty
    334    nullptr,  // delProperty
    335    nullptr,  // enumerate
    336    nullptr,  // newEnumerate
    337    nullptr,  // resolve
    338    nullptr,  // mayResolve
    339    nullptr,  // finalize
    340    nullptr,  // call
    341    nullptr,  // construct
    342    trace,    // trace
    343 };
    344 
    345 const ClassSpec MapObject::classSpec_ = {
    346    GenericCreateConstructor<MapObject::construct, 0, gc::AllocKind::FUNCTION,
    347                             &jit::JitInfo_MapConstructor>,
    348    GenericCreatePrototype<MapObject>,
    349    MapObject::staticMethods,
    350    MapObject::staticProperties,
    351    MapObject::methods,
    352    MapObject::properties,
    353    MapObject::finishInit,
    354 };
    355 
    356 const ClassExtension MapObject::classExtension_ = {
    357    MapObject::objectMoved,  // objectMovedOp
    358 };
    359 
    360 const JSClass MapObject::class_ = {
    361    "Map",
    362    JSCLASS_DELAY_METADATA_BUILDER |
    363        JSCLASS_HAS_RESERVED_SLOTS(MapObject::SlotCount) |
    364        JSCLASS_HAS_CACHED_PROTO(JSProto_Map),
    365    &MapObject::classOps_, &MapObject::classSpec_, &MapObject::classExtension_};
    366 
    367 const JSClass MapObject::protoClass_ = {
    368    "Map.prototype",
    369    JSCLASS_HAS_CACHED_PROTO(JSProto_Map),
    370    JS_NULL_CLASS_OPS,
    371    &MapObject::classSpec_,
    372 };
    373 
    374 const JSPropertySpec MapObject::properties[] = {
    375    JS_INLINABLE_PSG("size", size, 0, MapSize),
    376    JS_STRING_SYM_PS(toStringTag, "Map", JSPROP_READONLY),
    377    JS_PS_END,
    378 };
    379 
    380 const JSFunctionSpec MapObject::methods[] = {
    381    JS_INLINABLE_FN("get", get, 1, 0, MapGet),
    382    JS_INLINABLE_FN("has", has, 1, 0, MapHas),
    383    JS_INLINABLE_FN("set", set, 2, 0, MapSet),
    384    JS_INLINABLE_FN("delete", delete_, 1, 0, MapDelete),
    385    JS_FN("keys", keys, 0, 0),
    386    JS_FN("values", values, 0, 0),
    387    JS_FN("clear", clear, 0, 0),
    388    JS_SELF_HOSTED_FN("forEach", "MapForEach", 2, 0),
    389    JS_FN("getOrInsert", getOrInsert, 2, 0),
    390    JS_SELF_HOSTED_FN("getOrInsertComputed", "MapGetOrInsertComputed", 2, 0),
    391    JS_FN("entries", entries, 0, 0),
    392    // @@iterator is re-defined in finishInit so that it has the
    393    // same identity as |entries|.
    394    JS_SYM_FN(iterator, entries, 0, 0),
    395    JS_FS_END,
    396 };
    397 
    398 const JSPropertySpec MapObject::staticProperties[] = {
    399    JS_SELF_HOSTED_SYM_GET(species, "$MapSpecies", 0),
    400    JS_PS_END,
    401 };
    402 
    403 const JSFunctionSpec MapObject::staticMethods[] = {
    404    JS_SELF_HOSTED_FN("groupBy", "MapGroupBy", 2, 0),
    405    JS_FS_END,
    406 };
    407 
    408 /* static */ bool MapObject::finishInit(JSContext* cx, HandleObject ctor,
    409                                        HandleObject proto) {
    410  Handle<NativeObject*> nativeProto = proto.as<NativeObject>();
    411 
    412  RootedValue entriesFn(cx);
    413  RootedId entriesId(cx, NameToId(cx->names().entries));
    414  if (!NativeGetProperty(cx, nativeProto, entriesId, &entriesFn)) {
    415    return false;
    416  }
    417 
    418  // 23.1.3.12 Map.prototype[@@iterator]()
    419  // The initial value of the @@iterator property is the same function object
    420  // as the initial value of the "entries" property.
    421  RootedId iteratorId(cx, PropertyKey::Symbol(cx->wellKnownSymbols().iterator));
    422  if (!NativeDefineDataProperty(cx, nativeProto, iteratorId, entriesFn, 0)) {
    423    return false;
    424  }
    425 
    426  return JSObject::setHasRealmFuseProperty(cx, nativeProto);
    427 }
    428 
    429 void MapObject::trace(JSTracer* trc, JSObject* obj) {
    430  MapObject* mapObj = &obj->as<MapObject>();
    431  Table(mapObj).trace(trc);
    432 }
    433 
    434 using NurseryKeysVector = GCVector<Value, 4, SystemAllocPolicy>;
    435 
    436 template <typename TableObject>
    437 static NurseryKeysVector* GetNurseryKeys(TableObject* t) {
    438  Value value = t->getReservedSlot(TableObject::NurseryKeysSlot);
    439  return reinterpret_cast<NurseryKeysVector*>(value.toPrivate());
    440 }
    441 
    442 template <typename TableObject>
    443 static NurseryKeysVector* AllocNurseryKeys(TableObject* t) {
    444  MOZ_ASSERT(!GetNurseryKeys(t));
    445  auto keys = js_new<NurseryKeysVector>();
    446  if (!keys) {
    447    return nullptr;
    448  }
    449 
    450  t->setReservedSlot(TableObject::NurseryKeysSlot, PrivateValue(keys));
    451  return keys;
    452 }
    453 
    454 template <typename TableObject>
    455 static void DeleteNurseryKeys(TableObject* t) {
    456  auto keys = GetNurseryKeys(t);
    457  MOZ_ASSERT(keys);
    458  js_delete(keys);
    459  t->setReservedSlot(TableObject::NurseryKeysSlot, PrivateValue(nullptr));
    460 }
    461 
    462 // A generic store buffer entry that traces all nursery keys for an ordered hash
    463 // map or set.
    464 template <typename ObjectT>
    465 class js::OrderedHashTableRef : public gc::BufferableRef {
    466  ObjectT* object;
    467 
    468 public:
    469  explicit OrderedHashTableRef(ObjectT* obj) : object(obj) {}
    470 
    471  void trace(JSTracer* trc) override {
    472    MOZ_ASSERT(!IsInsideNursery(object));
    473    NurseryKeysVector* keys = GetNurseryKeys(object);
    474    MOZ_ASSERT(keys);
    475 
    476    keys->mutableEraseIf([&](Value& key) {
    477      MOZ_ASSERT(typename ObjectT::UnbarrieredTable(object).hash(key) ==
    478                 typename ObjectT::Table(object).hash(
    479                     *reinterpret_cast<const HashableValue*>(&key)));
    480      MOZ_ASSERT(IsInsideNursery(key.toGCThing()));
    481 
    482      auto result = typename ObjectT::UnbarrieredTable(object).rekeyOneEntry(
    483          key, [trc](const Value& prior) {
    484            Value key = prior;
    485            TraceManuallyBarrieredEdge(trc, &key, "ordered hash table key");
    486            return key;
    487          });
    488 
    489      if (result.isNothing()) {
    490        return true;  // Key removed.
    491      }
    492 
    493      key = result.value();
    494      return !IsInsideNursery(key.toGCThing());
    495    });
    496 
    497    if (!keys->empty()) {
    498      trc->runtime()->gc.storeBuffer().putGeneric(
    499          OrderedHashTableRef<ObjectT>(object));
    500      return;
    501    }
    502 
    503    DeleteNurseryKeys(object);
    504  }
    505 };
    506 
    507 template <typename ObjectT>
    508 [[nodiscard]] inline static bool PostWriteBarrier(ObjectT* obj,
    509                                                  const Value& keyValue) {
    510  MOZ_ASSERT(!IsInsideNursery(obj));
    511 
    512  if (MOZ_LIKELY(!keyValue.isObject() && !keyValue.isBigInt())) {
    513    MOZ_ASSERT_IF(keyValue.isGCThing(), !IsInsideNursery(keyValue.toGCThing()));
    514    return true;
    515  }
    516 
    517  if (!IsInsideNursery(keyValue.toGCThing())) {
    518    return true;
    519  }
    520 
    521  NurseryKeysVector* keys = GetNurseryKeys(obj);
    522  if (!keys) {
    523    keys = AllocNurseryKeys(obj);
    524    if (!keys) {
    525      return false;
    526    }
    527 
    528    keyValue.toGCThing()->storeBuffer()->putGeneric(
    529        OrderedHashTableRef<ObjectT>(obj));
    530  }
    531 
    532  return keys->append(keyValue);
    533 }
    534 
    535 bool MapObject::getKeysAndValuesInterleaved(
    536    JS::MutableHandle<GCVector<JS::Value>> entries) {
    537  auto appendEntry = [&entries](auto& entry) {
    538    return entries.append(entry.key.get()) && entries.append(entry.value);
    539  };
    540  return Table(this).forEachEntry(appendEntry);
    541 }
    542 
    543 bool MapObject::set(JSContext* cx, const Value& key, const Value& val) {
    544  HashableValue k;
    545  if (!k.setValue(cx, key)) {
    546    return false;
    547  }
    548  return setWithHashableKey(cx, k, val);
    549 }
    550 
    551 bool MapObject::setWithHashableKey(JSContext* cx, const HashableValue& key,
    552                                   const Value& value) {
    553  bool needsPostBarriers = isTenured();
    554  if (needsPostBarriers) {
    555    // Use the Table representation which has post barriers.
    556    if (!PostWriteBarrier(this, key)) {
    557      ReportOutOfMemory(cx);
    558      return false;
    559    }
    560    if (!Table(this).put(cx, key, value)) {
    561      return false;
    562    }
    563  } else {
    564    // Use the PreBarrieredTable representation which does not.
    565    if (!PreBarrieredTable(this).put(cx, key, value)) {
    566      return false;
    567    }
    568  }
    569 
    570  return true;
    571 }
    572 
    573 bool MapObject::getOrInsert(JSContext* cx, const Value& key, const Value& val,
    574                            MutableHandleValue rval) {
    575  HashableValue k;
    576  if (!k.setValue(cx, key)) {
    577    return false;
    578  }
    579 
    580  bool needsPostBarriers = isTenured();
    581  if (needsPostBarriers) {
    582    if (!PostWriteBarrier(this, k)) {
    583      ReportOutOfMemory(cx);
    584      return false;
    585    }
    586    // Use the Table representation which has post barriers.
    587    if (const Table::Entry* p = Table(this).getOrAdd(cx, k, val)) {
    588      rval.set(p->value);
    589    } else {
    590      return false;
    591    }
    592  } else {
    593    // Use the PreBarrieredTable representation which does not.
    594    if (const PreBarrieredTable::Entry* p =
    595            PreBarrieredTable(this).getOrAdd(cx, k, val)) {
    596      rval.set(p->value);
    597    } else {
    598      return false;
    599    }
    600  }
    601  return true;
    602 }
    603 
    604 MapObject* MapObject::createWithProto(JSContext* cx, HandleObject proto,
    605                                      NewObjectKind newKind) {
    606  MOZ_ASSERT(proto);
    607 
    608  gc::AllocKind allocKind = gc::GetGCObjectKind(SlotCount);
    609 
    610  AutoSetNewObjectMetadata metadata(cx);
    611  auto* mapObj =
    612      NewObjectWithGivenProtoAndKinds<MapObject>(cx, proto, allocKind, newKind);
    613  if (!mapObj) {
    614    return nullptr;
    615  }
    616 
    617  UnbarrieredTable(mapObj).initSlots();
    618  mapObj->initReservedSlot(NurseryKeysSlot, PrivateValue(nullptr));
    619  mapObj->initReservedSlot(RegisteredNurseryIteratorsSlot, BooleanValue(false));
    620  return mapObj;
    621 }
    622 
    623 MapObject* MapObject::create(JSContext* cx,
    624                             HandleObject proto /* = nullptr */) {
    625  if (proto) {
    626    return createWithProto(cx, proto, GenericObject);
    627  }
    628 
    629  // This is the common case so use the template object's shape to optimize the
    630  // allocation.
    631  MapObject* templateObj = GlobalObject::getOrCreateMapTemplateObject(cx);
    632  if (!templateObj) {
    633    return nullptr;
    634  }
    635 
    636  gc::AllocKind allocKind = templateObj->asTenured().getAllocKind();
    637  MOZ_ASSERT(gc::GetGCKindSlots(allocKind) >= SlotCount);
    638  MOZ_ASSERT(!gc::IsFinalizedKind(allocKind));
    639 
    640  AutoSetNewObjectMetadata metadata(cx);
    641  Rooted<SharedShape*> shape(cx, templateObj->sharedShape());
    642  auto* mapObj =
    643      NativeObject::create<MapObject>(cx, allocKind, gc::Heap::Default, shape);
    644  if (!mapObj) {
    645    return nullptr;
    646  }
    647 
    648  UnbarrieredTable(mapObj).initSlots();
    649  mapObj->initReservedSlot(NurseryKeysSlot, PrivateValue(nullptr));
    650  mapObj->initReservedSlot(RegisteredNurseryIteratorsSlot, BooleanValue(false));
    651  return mapObj;
    652 }
    653 
    654 // static
    655 MapObject* GlobalObject::getOrCreateMapTemplateObject(JSContext* cx) {
    656  GlobalObjectData& data = cx->global()->data();
    657  if (MapObject* obj = data.mapObjectTemplate) {
    658    return obj;
    659  }
    660 
    661  Rooted<JSObject*> proto(cx,
    662                          GlobalObject::getOrCreatePrototype(cx, JSProto_Map));
    663  if (!proto) {
    664    return nullptr;
    665  }
    666  auto* mapObj = MapObject::createWithProto(cx, proto, TenuredObject);
    667  if (!mapObj) {
    668    return nullptr;
    669  }
    670 
    671  data.mapObjectTemplate.init(mapObj);
    672  return mapObj;
    673 }
    674 
    675 size_t MapObject::sizeOfData(mozilla::MallocSizeOf mallocSizeOf) {
    676  size_t size = 0;
    677  size += Table(this).sizeOfExcludingObject(mallocSizeOf);
    678  if (NurseryKeysVector* nurseryKeys = GetNurseryKeys(this)) {
    679    size += nurseryKeys->sizeOfIncludingThis(mallocSizeOf);
    680  }
    681  return size;
    682 }
    683 
    684 size_t MapObject::objectMoved(JSObject* obj, JSObject* old) {
    685  auto* mapObj = &obj->as<MapObject>();
    686 
    687  Table(mapObj).updateIteratorsAfterMove(&old->as<MapObject>());
    688 
    689  if (IsInsideNursery(old)) {
    690    Nursery& nursery = mapObj->runtimeFromMainThread()->gc.nursery();
    691    Table(mapObj).maybeMoveBufferOnPromotion(nursery);
    692  }
    693 
    694  return 0;
    695 }
    696 
    697 void MapObject::clearNurseryIteratorsBeforeMinorGC() {
    698  Table(this).clearNurseryIterators();
    699 }
    700 
    701 /* static */
    702 MapObject* MapObject::sweepAfterMinorGC(JS::GCContext* gcx, MapObject* mapobj) {
    703  Nursery& nursery = gcx->runtime()->gc.nursery();
    704  bool wasInCollectedRegion = nursery.inCollectedRegion(mapobj);
    705  if (wasInCollectedRegion && !IsForwarded(mapobj)) {
    706    // This MapObject is dead.
    707    return nullptr;
    708  }
    709 
    710  mapobj = MaybeForwarded(mapobj);
    711 
    712  // Keep |mapobj| registered with the nursery if it still has nursery
    713  // iterators.
    714  bool hasNurseryIterators = Table(mapobj).hasNurseryIterators();
    715  SetRegisteredNurseryIterators(mapobj, hasNurseryIterators);
    716  return hasNurseryIterators ? mapobj : nullptr;
    717 }
    718 
    719 bool MapObject::tryOptimizeCtorWithIterable(JSContext* cx,
    720                                            const Value& iterableVal,
    721                                            bool* optimized) {
    722  MOZ_ASSERT(!iterableVal.isNullOrUndefined());
    723  MOZ_ASSERT(!*optimized);
    724 
    725  if (!CanOptimizeMapOrSetCtorWithIterable<JSProto_Map>(MapObject::set, this,
    726                                                        cx)) {
    727    return true;
    728  }
    729 
    730  if (!iterableVal.isObject()) {
    731    return true;
    732  }
    733  JSObject* iterable = &iterableVal.toObject();
    734 
    735  // Fast path for `new Map(array)`.
    736  if (IsOptimizableArrayForMapOrSetCtor<MapOrSet::Map>(iterable, cx)) {
    737    ArrayObject* array = &iterable->as<ArrayObject>();
    738    uint32_t len = array->getDenseInitializedLength();
    739 
    740    for (uint32_t index = 0; index < len; index++) {
    741      Value element = array->getDenseElement(index);
    742      MOZ_ASSERT(IsPackedArray(&element.toObject()));
    743 
    744      auto* elementArray = &element.toObject().as<ArrayObject>();
    745      Value key = elementArray->getDenseElement(0);
    746      Value value = elementArray->getDenseElement(1);
    747 
    748      MOZ_ASSERT(!key.isMagic(JS_ELEMENTS_HOLE));
    749      MOZ_ASSERT(!value.isMagic(JS_ELEMENTS_HOLE));
    750 
    751      if (!set(cx, key, value)) {
    752        return false;
    753      }
    754    }
    755 
    756    *optimized = true;
    757    return true;
    758  }
    759 
    760  // Fast path for `new Map(map)`.
    761  if (IsMapObjectWithDefaultIterator(iterable, cx)) {
    762    auto* iterableMap = &iterable->as<MapObject>();
    763    auto addEntry = [cx, this](auto& entry) {
    764      return setWithHashableKey(cx, entry.key, entry.value);
    765    };
    766    if (!Table(iterableMap).forEachEntry(addEntry)) {
    767      return false;
    768    }
    769    *optimized = true;
    770    return true;
    771  }
    772 
    773  return true;
    774 }
    775 
    776 // static
    777 MapObject* MapObject::createFromIterable(JSContext* cx, Handle<JSObject*> proto,
    778                                         Handle<Value> iterable,
    779                                         Handle<MapObject*> allocatedFromJit) {
    780  // A null-or-undefined |iterable| is quite common and we check for this in JIT
    781  // code.
    782  MOZ_ASSERT_IF(allocatedFromJit, !iterable.isNullOrUndefined());
    783 
    784  Rooted<MapObject*> obj(cx, allocatedFromJit);
    785  if (!obj) {
    786    obj = MapObject::create(cx, proto);
    787    if (!obj) {
    788      return nullptr;
    789    }
    790  }
    791 
    792  if (!iterable.isNullOrUndefined()) {
    793    bool optimized = false;
    794    if (!obj->tryOptimizeCtorWithIterable(cx, iterable, &optimized)) {
    795      return nullptr;
    796    }
    797    if (!optimized) {
    798      FixedInvokeArgs<1> args(cx);
    799      args[0].set(iterable);
    800 
    801      RootedValue thisv(cx, ObjectValue(*obj));
    802      if (!CallSelfHostedFunction(cx, cx->names().MapConstructorInit, thisv,
    803                                  args, args.rval())) {
    804        return nullptr;
    805      }
    806    }
    807  }
    808 
    809  return obj;
    810 }
    811 
    812 bool MapObject::construct(JSContext* cx, unsigned argc, Value* vp) {
    813  AutoJSConstructorProfilerEntry pseudoFrame(cx, "Map");
    814  CallArgs args = CallArgsFromVp(argc, vp);
    815 
    816  if (!ThrowIfNotConstructing(cx, args, "Map")) {
    817    return false;
    818  }
    819 
    820  RootedObject proto(cx);
    821  if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Map, &proto)) {
    822    return false;
    823  }
    824 
    825  MapObject* obj = MapObject::createFromIterable(cx, proto, args.get(0));
    826  if (!obj) {
    827    return false;
    828  }
    829 
    830  args.rval().setObject(*obj);
    831  return true;
    832 }
    833 
    834 bool MapObject::is(HandleValue v) {
    835  return v.isObject() && v.toObject().hasClass(&class_);
    836 }
    837 
    838 bool MapObject::is(HandleObject o) { return o->hasClass(&class_); }
    839 
    840 uint32_t MapObject::size() {
    841  static_assert(sizeof(Table(this).count()) <= sizeof(uint32_t),
    842                "map count must be precisely representable as a JS number");
    843  return Table(this).count();
    844 }
    845 
    846 bool MapObject::size_impl(JSContext* cx, const CallArgs& args) {
    847  auto* mapObj = &args.thisv().toObject().as<MapObject>();
    848  args.rval().setNumber(mapObj->size());
    849  return true;
    850 }
    851 
    852 bool MapObject::size(JSContext* cx, unsigned argc, Value* vp) {
    853  AutoJSMethodProfilerEntry pseudoFrame(cx, "Map.prototype", "size");
    854  CallArgs args = CallArgsFromVp(argc, vp);
    855  return CallNonGenericMethod<MapObject::is, MapObject::size_impl>(cx, args);
    856 }
    857 
    858 bool MapObject::get(JSContext* cx, const Value& key, MutableHandleValue rval) {
    859  HashableValue k;
    860  if (!k.setValue(cx, key)) {
    861    return false;
    862  }
    863 
    864  if (const Table::Entry* p = Table(this).get(k)) {
    865    rval.set(p->value);
    866  } else {
    867    rval.setUndefined();
    868  }
    869 
    870  return true;
    871 }
    872 
    873 bool MapObject::get_impl(JSContext* cx, const CallArgs& args) {
    874  auto* mapObj = &args.thisv().toObject().as<MapObject>();
    875  return mapObj->get(cx, args.get(0), args.rval());
    876 }
    877 
    878 bool MapObject::get(JSContext* cx, unsigned argc, Value* vp) {
    879  AutoJSMethodProfilerEntry pseudoFrame(cx, "Map.prototype", "get");
    880  CallArgs args = CallArgsFromVp(argc, vp);
    881  return CallNonGenericMethod<MapObject::is, MapObject::get_impl>(cx, args);
    882 }
    883 
    884 bool MapObject::has(JSContext* cx, const Value& key, bool* rval) {
    885  HashableValue k;
    886  if (!k.setValue(cx, key)) {
    887    return false;
    888  }
    889 
    890  *rval = Table(this).has(k);
    891  return true;
    892 }
    893 
    894 bool MapObject::has_impl(JSContext* cx, const CallArgs& args) {
    895  auto* mapObj = &args.thisv().toObject().as<MapObject>();
    896  bool found;
    897  if (!mapObj->has(cx, args.get(0), &found)) {
    898    return false;
    899  }
    900  args.rval().setBoolean(found);
    901  return true;
    902 }
    903 
    904 bool MapObject::has(JSContext* cx, unsigned argc, Value* vp) {
    905  AutoJSMethodProfilerEntry pseudoFrame(cx, "Map.prototype", "has");
    906  CallArgs args = CallArgsFromVp(argc, vp);
    907  return CallNonGenericMethod<MapObject::is, MapObject::has_impl>(cx, args);
    908 }
    909 
    910 bool MapObject::set_impl(JSContext* cx, const CallArgs& args) {
    911  auto* mapObj = &args.thisv().toObject().as<MapObject>();
    912  if (!mapObj->set(cx, args.get(0), args.get(1))) {
    913    return false;
    914  }
    915  args.rval().set(args.thisv());
    916  return true;
    917 }
    918 
    919 bool MapObject::set(JSContext* cx, unsigned argc, Value* vp) {
    920  AutoJSMethodProfilerEntry pseudoFrame(cx, "Map.prototype", "set");
    921  CallArgs args = CallArgsFromVp(argc, vp);
    922  return CallNonGenericMethod<MapObject::is, MapObject::set_impl>(cx, args);
    923 }
    924 
    925 bool MapObject::getOrInsert_impl(JSContext* cx, const CallArgs& args) {
    926  auto* mapObj = &args.thisv().toObject().as<MapObject>();
    927  return mapObj->getOrInsert(cx, args.get(0), args.get(1), args.rval());
    928 }
    929 
    930 bool MapObject::getOrInsert(JSContext* cx, unsigned argc, Value* vp) {
    931  AutoJSMethodProfilerEntry pseudoFrame(cx, "Map.prototype", "getOrInsert");
    932  CallArgs args = CallArgsFromVp(argc, vp);
    933  return CallNonGenericMethod<MapObject::is, MapObject::getOrInsert_impl>(cx,
    934                                                                          args);
    935 }
    936 
    937 bool MapObject::delete_(JSContext* cx, const Value& key, bool* rval) {
    938  HashableValue k;
    939  if (!k.setValue(cx, key)) {
    940    return false;
    941  }
    942 
    943  if (isTenured()) {
    944    *rval = Table(this).remove(cx, k);
    945  } else {
    946    *rval = PreBarrieredTable(this).remove(cx, k);
    947  }
    948  return true;
    949 }
    950 
    951 bool MapObject::delete_impl(JSContext* cx, const CallArgs& args) {
    952  // MapObject::trace does not trace deleted entries. Incremental GC therefore
    953  // requires that no HeapPtr<Value> objects pointing to heap values be left
    954  // alive in the hash table.
    955  //
    956  // OrderedHashMapImpl::remove() doesn't destroy the removed entry. It merely
    957  // calls OrderedHashMapImpl::MapOps::makeEmpty. But that is sufficient,
    958  // because makeEmpty clears the value by doing e->value = Value(), and in the
    959  // case of Table, Value() means HeapPtr<Value>(), which is the same as
    960  // HeapPtr<Value>(UndefinedValue()).
    961 
    962  auto* mapObj = &args.thisv().toObject().as<MapObject>();
    963  bool found;
    964  if (!mapObj->delete_(cx, args.get(0), &found)) {
    965    return false;
    966  }
    967  args.rval().setBoolean(found);
    968  return true;
    969 }
    970 
    971 bool MapObject::delete_(JSContext* cx, unsigned argc, Value* vp) {
    972  AutoJSMethodProfilerEntry pseudoFrame(cx, "Map.prototype", "delete");
    973  CallArgs args = CallArgsFromVp(argc, vp);
    974  return CallNonGenericMethod<MapObject::is, MapObject::delete_impl>(cx, args);
    975 }
    976 
    977 bool MapObject::iterator(JSContext* cx, IteratorKind kind,
    978                         Handle<MapObject*> obj, MutableHandleValue iter) {
    979  JSObject* iterobj = MapIteratorObject::create(cx, obj, kind);
    980  if (!iterobj) {
    981    return false;
    982  }
    983  iter.setObject(*iterobj);
    984  return true;
    985 }
    986 
    987 bool MapObject::iterator_impl(JSContext* cx, const CallArgs& args,
    988                              IteratorKind kind) {
    989  Rooted<MapObject*> mapObj(cx, &args.thisv().toObject().as<MapObject>());
    990  return iterator(cx, kind, mapObj, args.rval());
    991 }
    992 
    993 bool MapObject::keys_impl(JSContext* cx, const CallArgs& args) {
    994  return iterator_impl(cx, args, IteratorKind::Keys);
    995 }
    996 
    997 bool MapObject::keys(JSContext* cx, unsigned argc, Value* vp) {
    998  AutoJSMethodProfilerEntry pseudoFrame(cx, "Map.prototype", "keys");
    999  CallArgs args = CallArgsFromVp(argc, vp);
   1000  return CallNonGenericMethod(cx, is, keys_impl, args);
   1001 }
   1002 
   1003 bool MapObject::values_impl(JSContext* cx, const CallArgs& args) {
   1004  return iterator_impl(cx, args, IteratorKind::Values);
   1005 }
   1006 
   1007 bool MapObject::values(JSContext* cx, unsigned argc, Value* vp) {
   1008  AutoJSMethodProfilerEntry pseudoFrame(cx, "Map.prototype", "values");
   1009  CallArgs args = CallArgsFromVp(argc, vp);
   1010  return CallNonGenericMethod(cx, is, values_impl, args);
   1011 }
   1012 
   1013 bool MapObject::entries_impl(JSContext* cx, const CallArgs& args) {
   1014  return iterator_impl(cx, args, IteratorKind::Entries);
   1015 }
   1016 
   1017 bool MapObject::entries(JSContext* cx, unsigned argc, Value* vp) {
   1018  AutoJSMethodProfilerEntry pseudoFrame(cx, "Map.prototype", "entries");
   1019  CallArgs args = CallArgsFromVp(argc, vp);
   1020  return CallNonGenericMethod(cx, is, entries_impl, args);
   1021 }
   1022 
   1023 bool MapObject::clear_impl(JSContext* cx, const CallArgs& args) {
   1024  auto* mapObj = &args.thisv().toObject().as<MapObject>();
   1025  mapObj->clear(cx);
   1026  args.rval().setUndefined();
   1027  return true;
   1028 }
   1029 
   1030 bool MapObject::clear(JSContext* cx, unsigned argc, Value* vp) {
   1031  AutoJSMethodProfilerEntry pseudoFrame(cx, "Map.prototype", "clear");
   1032  CallArgs args = CallArgsFromVp(argc, vp);
   1033  return CallNonGenericMethod(cx, is, clear_impl, args);
   1034 }
   1035 
   1036 void MapObject::clear(JSContext* cx) {
   1037  if (isTenured()) {
   1038    Table(this).clear(cx);
   1039  } else {
   1040    PreBarrieredTable(this).clear(cx);
   1041  }
   1042 }
   1043 
   1044 /*** SetIterator ************************************************************/
   1045 
   1046 static const JSClassOps SetIteratorObjectClassOps = {
   1047    nullptr,                      // addProperty
   1048    nullptr,                      // delProperty
   1049    nullptr,                      // enumerate
   1050    nullptr,                      // newEnumerate
   1051    nullptr,                      // resolve
   1052    nullptr,                      // mayResolve
   1053    SetIteratorObject::finalize,  // finalize
   1054    nullptr,                      // call
   1055    nullptr,                      // construct
   1056    nullptr,                      // trace
   1057 };
   1058 
   1059 static const ClassExtension SetIteratorObjectClassExtension = {
   1060    SetIteratorObject::objectMoved,  // objectMovedOp
   1061 };
   1062 
   1063 const JSClass SetIteratorObject::class_ = {
   1064    "Set Iterator",
   1065    JSCLASS_HAS_RESERVED_SLOTS(SetIteratorObject::SlotCount) |
   1066        JSCLASS_FOREGROUND_FINALIZE | JSCLASS_SKIP_NURSERY_FINALIZE,
   1067    &SetIteratorObjectClassOps,
   1068    JS_NULL_CLASS_SPEC,
   1069    &SetIteratorObjectClassExtension,
   1070 };
   1071 
   1072 const JSFunctionSpec SetIteratorObject::methods[] = {
   1073    JS_SELF_HOSTED_FN("next", "SetIteratorNext", 0, 0),
   1074    JS_FS_END,
   1075 };
   1076 
   1077 /* static */
   1078 bool GlobalObject::initSetIteratorProto(JSContext* cx,
   1079                                        Handle<GlobalObject*> global) {
   1080  Rooted<JSObject*> base(
   1081      cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
   1082  if (!base) {
   1083    return false;
   1084  }
   1085  Rooted<PlainObject*> proto(
   1086      cx, GlobalObject::createBlankPrototypeInheriting<PlainObject>(cx, base));
   1087  if (!proto) {
   1088    return false;
   1089  }
   1090  if (!JS_DefineFunctions(cx, proto, SetIteratorObject::methods) ||
   1091      !DefineToStringTag(cx, proto, cx->names().Set_Iterator_)) {
   1092    return false;
   1093  }
   1094  if (!JSObject::setHasRealmFuseProperty(cx, proto)) {
   1095    return false;
   1096  }
   1097  global->initBuiltinProto(ProtoKind::SetIteratorProto, proto);
   1098  return true;
   1099 }
   1100 
   1101 SetIteratorObject* SetIteratorObject::create(JSContext* cx,
   1102                                             Handle<SetObject*> setobj,
   1103                                             Kind kind) {
   1104  MOZ_ASSERT(kind != Kind::Keys);
   1105 
   1106  Rooted<GlobalObject*> global(cx, &setobj->global());
   1107  Rooted<JSObject*> proto(
   1108      cx, GlobalObject::getOrCreateSetIteratorPrototype(cx, global));
   1109  if (!proto) {
   1110    return nullptr;
   1111  }
   1112 
   1113  SetIteratorObject* iterobj =
   1114      NewObjectWithGivenProto<SetIteratorObject>(cx, proto);
   1115  if (!iterobj) {
   1116    return nullptr;
   1117  }
   1118 
   1119  if (IsInsideNursery(iterobj) &&
   1120      !HasRegisteredNurseryIterators(setobj.get())) {
   1121    if (!cx->nursery().addSetWithNurseryIterators(setobj)) {
   1122      ReportOutOfMemory(cx);
   1123      return nullptr;
   1124    }
   1125    SetRegisteredNurseryIterators(setobj.get(), true);
   1126  }
   1127 
   1128  SetObject::Table(setobj).initIterator(iterobj, kind);
   1129 
   1130  return iterobj;
   1131 }
   1132 
   1133 void SetIteratorObject::finalize(JS::GCContext* gcx, JSObject* obj) {
   1134  MOZ_ASSERT(gcx->onMainThread());
   1135  MOZ_ASSERT(!IsInsideNursery(obj));
   1136  if (obj->as<SetIteratorObject>().isActive()) {
   1137    obj->as<SetIteratorObject>().unlink();
   1138  }
   1139 }
   1140 
   1141 size_t SetIteratorObject::objectMoved(JSObject* obj, JSObject* old) {
   1142  SetIteratorObject* iter = &obj->as<SetIteratorObject>();
   1143  if (!iter->isActive()) {
   1144    return 0;
   1145  }
   1146  if (IsInsideNursery(old)) {
   1147    SetObject* setObj = iter->target();
   1148    SetObject::Table(setObj).relinkNurseryIterator(iter);
   1149  } else {
   1150    iter->updateListAfterMove(&old->as<SetIteratorObject>());
   1151  }
   1152  return 0;
   1153 }
   1154 
   1155 SetObject* SetIteratorObject::target() const {
   1156  MOZ_ASSERT(isActive(), "only active iterators have a target object");
   1157  Value value = getFixedSlot(TargetSlot);
   1158  return &MaybeForwarded(&value.toObject())->as<SetObject>();
   1159 }
   1160 
   1161 bool SetIteratorObject::next(SetIteratorObject* setIterator,
   1162                             ArrayObject* resultObj) {
   1163  // IC code calls this directly.
   1164  AutoUnsafeCallWithABI unsafe;
   1165 
   1166  // Check invariants for inlined _GetNextSetEntryForIterator.
   1167 
   1168  // The array should be tenured, so that post-barrier can be done simply.
   1169  MOZ_ASSERT(resultObj->isTenured());
   1170 
   1171  // The array elements should be fixed.
   1172  MOZ_ASSERT(resultObj->hasFixedElements());
   1173  MOZ_ASSERT(resultObj->getDenseInitializedLength() == 1);
   1174  MOZ_ASSERT(resultObj->getDenseCapacity() >= 1);
   1175 
   1176  if (!setIterator->isActive()) {
   1177    // Already done.
   1178    return true;
   1179  }
   1180 
   1181  auto storeResult = [resultObj](Kind kind, const auto& element) {
   1182    resultObj->setDenseElement(0, element.get());
   1183  };
   1184  SetObject* setObj = setIterator->target();
   1185  return SetObject::Table(setObj).iteratorNext(setIterator, storeResult);
   1186 }
   1187 
   1188 /* static */
   1189 JSObject* SetIteratorObject::createResult(JSContext* cx) {
   1190  ArrayObject* resultObj = NewDenseFullyAllocatedArray(cx, 1, TenuredObject);
   1191  if (!resultObj) {
   1192    return nullptr;
   1193  }
   1194 
   1195  resultObj->setDenseInitializedLength(1);
   1196  resultObj->initDenseElement(0, NullValue());
   1197 
   1198  return resultObj;
   1199 }
   1200 
   1201 /*** Set ********************************************************************/
   1202 
   1203 const JSClassOps SetObject::classOps_ = {
   1204    nullptr,  // addProperty
   1205    nullptr,  // delProperty
   1206    nullptr,  // enumerate
   1207    nullptr,  // newEnumerate
   1208    nullptr,  // resolve
   1209    nullptr,  // mayResolve
   1210    nullptr,  // finalize
   1211    nullptr,  // call
   1212    nullptr,  // construct
   1213    trace,    // trace
   1214 };
   1215 
   1216 const ClassSpec SetObject::classSpec_ = {
   1217    GenericCreateConstructor<SetObject::construct, 0, gc::AllocKind::FUNCTION,
   1218                             &jit::JitInfo_SetConstructor>,
   1219    GenericCreatePrototype<SetObject>,
   1220    nullptr,
   1221    SetObject::staticProperties,
   1222    SetObject::methods,
   1223    SetObject::properties,
   1224    SetObject::finishInit,
   1225 };
   1226 
   1227 const ClassExtension SetObject::classExtension_ = {
   1228    SetObject::objectMoved,  // objectMovedOp
   1229 };
   1230 
   1231 const JSClass SetObject::class_ = {
   1232    "Set",
   1233    JSCLASS_DELAY_METADATA_BUILDER |
   1234        JSCLASS_HAS_RESERVED_SLOTS(SetObject::SlotCount) |
   1235        JSCLASS_HAS_CACHED_PROTO(JSProto_Set),
   1236    &SetObject::classOps_, &SetObject::classSpec_, &SetObject::classExtension_};
   1237 
   1238 const JSClass SetObject::protoClass_ = {
   1239    "Set.prototype",
   1240    JSCLASS_HAS_CACHED_PROTO(JSProto_Set),
   1241    JS_NULL_CLASS_OPS,
   1242    &SetObject::classSpec_,
   1243 };
   1244 
   1245 const JSPropertySpec SetObject::properties[] = {
   1246    JS_INLINABLE_PSG("size", size, 0, SetSize),
   1247    JS_STRING_SYM_PS(toStringTag, "Set", JSPROP_READONLY),
   1248    JS_PS_END,
   1249 };
   1250 
   1251 const JSFunctionSpec SetObject::methods[] = {
   1252    JS_INLINABLE_FN("has", has, 1, 0, SetHas),
   1253    JS_INLINABLE_FN("add", add, 1, 0, SetAdd),
   1254    JS_INLINABLE_FN("delete", delete_, 1, 0, SetDelete),
   1255    JS_FN("entries", entries, 0, 0),
   1256    JS_FN("clear", clear, 0, 0),
   1257    JS_SELF_HOSTED_FN("forEach", "SetForEach", 2, 0),
   1258    JS_SELF_HOSTED_FN("union", "SetUnion", 1, 0),
   1259    JS_SELF_HOSTED_FN("difference", "SetDifference", 1, 0),
   1260    JS_SELF_HOSTED_FN("intersection", "SetIntersection", 1, 0),
   1261    JS_SELF_HOSTED_FN("symmetricDifference", "SetSymmetricDifference", 1, 0),
   1262    JS_SELF_HOSTED_FN("isSubsetOf", "SetIsSubsetOf", 1, 0),
   1263    JS_SELF_HOSTED_FN("isSupersetOf", "SetIsSupersetOf", 1, 0),
   1264    JS_SELF_HOSTED_FN("isDisjointFrom", "SetIsDisjointFrom", 1, 0),
   1265    JS_FN("values", values, 0, 0),
   1266    // @@iterator and |keys| re-defined in finishInit so that they have the
   1267    // same identity as |values|.
   1268    JS_FN("keys", values, 0, 0),
   1269    JS_SYM_FN(iterator, values, 0, 0),
   1270    JS_FS_END,
   1271 };
   1272 // clang-format on
   1273 
   1274 const JSPropertySpec SetObject::staticProperties[] = {
   1275    JS_SELF_HOSTED_SYM_GET(species, "$SetSpecies", 0),
   1276    JS_PS_END,
   1277 };
   1278 
   1279 /* static */ bool SetObject::finishInit(JSContext* cx, HandleObject ctor,
   1280                                        HandleObject proto) {
   1281  Handle<NativeObject*> nativeProto = proto.as<NativeObject>();
   1282 
   1283  RootedValue valuesFn(cx);
   1284  RootedId valuesId(cx, NameToId(cx->names().values));
   1285  if (!NativeGetProperty(cx, nativeProto, valuesId, &valuesFn)) {
   1286    return false;
   1287  }
   1288 
   1289  // 23.2.3.8 Set.prototype.keys()
   1290  // The initial value of the "keys" property is the same function object
   1291  // as the initial value of the "values" property.
   1292  RootedId keysId(cx, NameToId(cx->names().keys));
   1293  if (!NativeDefineDataProperty(cx, nativeProto, keysId, valuesFn, 0)) {
   1294    return false;
   1295  }
   1296 
   1297  // 23.2.3.11 Set.prototype[@@iterator]()
   1298  // See above.
   1299  RootedId iteratorId(cx, PropertyKey::Symbol(cx->wellKnownSymbols().iterator));
   1300  if (!NativeDefineDataProperty(cx, nativeProto, iteratorId, valuesFn, 0)) {
   1301    return false;
   1302  }
   1303 
   1304  return JSObject::setHasRealmFuseProperty(cx, nativeProto);
   1305 }
   1306 
   1307 bool SetObject::keys(JS::MutableHandle<GCVector<JS::Value>> keys) {
   1308  auto appendEntry = [&keys](auto& entry) { return keys.append(entry.get()); };
   1309  return Table(this).forEachEntry(appendEntry);
   1310 }
   1311 
   1312 bool SetObject::add(JSContext* cx, const Value& key) {
   1313  HashableValue k;
   1314  if (!k.setValue(cx, key)) {
   1315    return false;
   1316  }
   1317  return addHashableValue(cx, k);
   1318 }
   1319 
   1320 bool SetObject::addHashableValue(JSContext* cx, const HashableValue& value) {
   1321  bool needsPostBarriers = isTenured();
   1322  if (needsPostBarriers && !PostWriteBarrier(this, value)) {
   1323    ReportOutOfMemory(cx);
   1324    return false;
   1325  }
   1326  return Table(this).put(cx, value);
   1327 }
   1328 
   1329 SetObject* SetObject::createWithProto(JSContext* cx, HandleObject proto,
   1330                                      NewObjectKind newKind) {
   1331  MOZ_ASSERT(proto);
   1332 
   1333  gc::AllocKind allocKind = gc::GetGCObjectKind(SlotCount);
   1334 
   1335  AutoSetNewObjectMetadata metadata(cx);
   1336  auto* setObj =
   1337      NewObjectWithGivenProtoAndKinds<SetObject>(cx, proto, allocKind, newKind);
   1338  if (!setObj) {
   1339    return nullptr;
   1340  }
   1341 
   1342  UnbarrieredTable(setObj).initSlots();
   1343  setObj->initReservedSlot(NurseryKeysSlot, PrivateValue(nullptr));
   1344  setObj->initReservedSlot(RegisteredNurseryIteratorsSlot, BooleanValue(false));
   1345  return setObj;
   1346 }
   1347 
   1348 SetObject* SetObject::create(JSContext* cx,
   1349                             HandleObject proto /* = nullptr */) {
   1350  if (proto) {
   1351    return createWithProto(cx, proto, GenericObject);
   1352  }
   1353 
   1354  // This is the common case so use the template object's shape to optimize the
   1355  // allocation.
   1356  SetObject* templateObj = GlobalObject::getOrCreateSetTemplateObject(cx);
   1357  if (!templateObj) {
   1358    return nullptr;
   1359  }
   1360 
   1361  gc::AllocKind allocKind = templateObj->asTenured().getAllocKind();
   1362  MOZ_ASSERT(gc::GetGCKindSlots(allocKind) >= SlotCount);
   1363  MOZ_ASSERT(!gc::IsFinalizedKind(allocKind));
   1364 
   1365  AutoSetNewObjectMetadata metadata(cx);
   1366  Rooted<SharedShape*> shape(cx, templateObj->sharedShape());
   1367  auto* setObj =
   1368      NativeObject::create<SetObject>(cx, allocKind, gc::Heap::Default, shape);
   1369  if (!setObj) {
   1370    return nullptr;
   1371  }
   1372 
   1373  UnbarrieredTable(setObj).initSlots();
   1374  setObj->initReservedSlot(NurseryKeysSlot, PrivateValue(nullptr));
   1375  setObj->initReservedSlot(RegisteredNurseryIteratorsSlot, BooleanValue(false));
   1376  return setObj;
   1377 }
   1378 
   1379 // static
   1380 SetObject* GlobalObject::getOrCreateSetTemplateObject(JSContext* cx) {
   1381  GlobalObjectData& data = cx->global()->data();
   1382  if (SetObject* obj = data.setObjectTemplate) {
   1383    return obj;
   1384  }
   1385 
   1386  Rooted<JSObject*> proto(cx,
   1387                          GlobalObject::getOrCreatePrototype(cx, JSProto_Set));
   1388  if (!proto) {
   1389    return nullptr;
   1390  }
   1391  auto* setObj = SetObject::createWithProto(cx, proto, TenuredObject);
   1392  if (!setObj) {
   1393    return nullptr;
   1394  }
   1395 
   1396  data.setObjectTemplate.init(setObj);
   1397  return setObj;
   1398 }
   1399 
   1400 void SetObject::trace(JSTracer* trc, JSObject* obj) {
   1401  SetObject* setobj = static_cast<SetObject*>(obj);
   1402  Table(setobj).trace(trc);
   1403 }
   1404 
   1405 size_t SetObject::sizeOfData(mozilla::MallocSizeOf mallocSizeOf) {
   1406  size_t size = 0;
   1407  size += Table(this).sizeOfExcludingObject(mallocSizeOf);
   1408  if (NurseryKeysVector* nurseryKeys = GetNurseryKeys(this)) {
   1409    size += nurseryKeys->sizeOfIncludingThis(mallocSizeOf);
   1410  }
   1411  return size;
   1412 }
   1413 
   1414 size_t SetObject::objectMoved(JSObject* obj, JSObject* old) {
   1415  auto* setObj = &obj->as<SetObject>();
   1416 
   1417  Table(setObj).updateIteratorsAfterMove(&old->as<SetObject>());
   1418 
   1419  if (IsInsideNursery(old)) {
   1420    Nursery& nursery = setObj->runtimeFromMainThread()->gc.nursery();
   1421    Table(setObj).maybeMoveBufferOnPromotion(nursery);
   1422  }
   1423 
   1424  return 0;
   1425 }
   1426 
   1427 void SetObject::clearNurseryIteratorsBeforeMinorGC() {
   1428  Table(this).clearNurseryIterators();
   1429 }
   1430 
   1431 /* static */
   1432 SetObject* SetObject::sweepAfterMinorGC(JS::GCContext* gcx, SetObject* setobj) {
   1433  Nursery& nursery = gcx->runtime()->gc.nursery();
   1434  bool wasInCollectedRegion = nursery.inCollectedRegion(setobj);
   1435  if (wasInCollectedRegion && !IsForwarded(setobj)) {
   1436    // This SetObject is dead.
   1437    return nullptr;
   1438  }
   1439 
   1440  setobj = MaybeForwarded(setobj);
   1441 
   1442  // Keep |setobj| registered with the nursery if it still has nursery
   1443  // iterators.
   1444  bool hasNurseryIterators = Table(setobj).hasNurseryIterators();
   1445  SetRegisteredNurseryIterators(setobj, hasNurseryIterators);
   1446  return hasNurseryIterators ? setobj : nullptr;
   1447 }
   1448 
   1449 bool SetObject::tryOptimizeCtorWithIterable(JSContext* cx,
   1450                                            const Value& iterableVal,
   1451                                            bool* optimized) {
   1452  MOZ_ASSERT(!iterableVal.isNullOrUndefined());
   1453  MOZ_ASSERT(!*optimized);
   1454 
   1455  if (!CanOptimizeMapOrSetCtorWithIterable<JSProto_Set>(SetObject::add, this,
   1456                                                        cx)) {
   1457    return true;
   1458  }
   1459 
   1460  if (!iterableVal.isObject()) {
   1461    return true;
   1462  }
   1463  JSObject* iterable = &iterableVal.toObject();
   1464 
   1465  // Fast path for `new Set(array)`.
   1466  if (IsOptimizableArrayForMapOrSetCtor<MapOrSet::Set>(iterable, cx)) {
   1467    ArrayObject* array = &iterable->as<ArrayObject>();
   1468    uint32_t len = array->getDenseInitializedLength();
   1469 
   1470    for (uint32_t index = 0; index < len; index++) {
   1471      Value keyVal = array->getDenseElement(index);
   1472      MOZ_ASSERT(!keyVal.isMagic(JS_ELEMENTS_HOLE));
   1473      if (!add(cx, keyVal)) {
   1474        return false;
   1475      }
   1476    }
   1477 
   1478    *optimized = true;
   1479    return true;
   1480  }
   1481 
   1482  // Fast path for `new Set(set)`.
   1483  if (IsSetObjectWithDefaultIterator(iterable, cx)) {
   1484    auto* iterableSet = &iterable->as<SetObject>();
   1485    if (!IsSetObjectWithDefaultIterator(iterableSet, cx)) {
   1486      return true;
   1487    }
   1488    auto addEntry = [cx, this](auto& entry) {
   1489      return addHashableValue(cx, entry);
   1490    };
   1491    if (!Table(iterableSet).forEachEntry(addEntry)) {
   1492      return false;
   1493    }
   1494    *optimized = true;
   1495    return true;
   1496  }
   1497 
   1498  return true;
   1499 }
   1500 
   1501 // static
   1502 SetObject* SetObject::createFromIterable(JSContext* cx, Handle<JSObject*> proto,
   1503                                         Handle<Value> iterable,
   1504                                         Handle<SetObject*> allocatedFromJit) {
   1505  // A null-or-undefined |iterable| is quite common and we check for this in JIT
   1506  // code.
   1507  MOZ_ASSERT_IF(allocatedFromJit, !iterable.isNullOrUndefined());
   1508 
   1509  Rooted<SetObject*> obj(cx, allocatedFromJit);
   1510  if (!obj) {
   1511    obj = SetObject::create(cx, proto);
   1512    if (!obj) {
   1513      return nullptr;
   1514    }
   1515  }
   1516 
   1517  if (!iterable.isNullOrUndefined()) {
   1518    bool optimized = false;
   1519    if (!obj->tryOptimizeCtorWithIterable(cx, iterable, &optimized)) {
   1520      return nullptr;
   1521    }
   1522    if (!optimized) {
   1523      FixedInvokeArgs<1> args(cx);
   1524      args[0].set(iterable);
   1525 
   1526      RootedValue thisv(cx, ObjectValue(*obj));
   1527      if (!CallSelfHostedFunction(cx, cx->names().SetConstructorInit, thisv,
   1528                                  args, args.rval())) {
   1529        return nullptr;
   1530      }
   1531    }
   1532  }
   1533 
   1534  return obj;
   1535 }
   1536 
   1537 bool SetObject::construct(JSContext* cx, unsigned argc, Value* vp) {
   1538  AutoJSConstructorProfilerEntry pseudoFrame(cx, "Set");
   1539  CallArgs args = CallArgsFromVp(argc, vp);
   1540 
   1541  if (!ThrowIfNotConstructing(cx, args, "Set")) {
   1542    return false;
   1543  }
   1544 
   1545  RootedObject proto(cx);
   1546  if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Set, &proto)) {
   1547    return false;
   1548  }
   1549 
   1550  SetObject* obj = SetObject::createFromIterable(cx, proto, args.get(0));
   1551  if (!obj) {
   1552    return false;
   1553  }
   1554 
   1555  args.rval().setObject(*obj);
   1556  return true;
   1557 }
   1558 
   1559 bool SetObject::is(HandleValue v) {
   1560  return v.isObject() && v.toObject().hasClass(&class_);
   1561 }
   1562 
   1563 bool SetObject::is(HandleObject o) { return o->hasClass(&class_); }
   1564 
   1565 uint32_t SetObject::size() {
   1566  static_assert(sizeof(Table(this).count()) <= sizeof(uint32_t),
   1567                "set count must be precisely representable as a JS number");
   1568  return Table(this).count();
   1569 }
   1570 
   1571 bool SetObject::size_impl(JSContext* cx, const CallArgs& args) {
   1572  auto* setObj = &args.thisv().toObject().as<SetObject>();
   1573  args.rval().setNumber(setObj->size());
   1574  return true;
   1575 }
   1576 
   1577 bool SetObject::size(JSContext* cx, unsigned argc, Value* vp) {
   1578  AutoJSMethodProfilerEntry pseudoFrame(cx, "Set.prototype", "size");
   1579  CallArgs args = CallArgsFromVp(argc, vp);
   1580  return CallNonGenericMethod<SetObject::is, SetObject::size_impl>(cx, args);
   1581 }
   1582 
   1583 bool SetObject::has_impl(JSContext* cx, const CallArgs& args) {
   1584  auto* setObj = &args.thisv().toObject().as<SetObject>();
   1585  bool found;
   1586  if (!setObj->has(cx, args.get(0), &found)) {
   1587    return false;
   1588  }
   1589  args.rval().setBoolean(found);
   1590  return true;
   1591 }
   1592 
   1593 bool SetObject::has(JSContext* cx, const Value& key, bool* rval) {
   1594  HashableValue k;
   1595  if (!k.setValue(cx, key)) {
   1596    return false;
   1597  }
   1598 
   1599  *rval = Table(this).has(k);
   1600  return true;
   1601 }
   1602 
   1603 bool SetObject::has(JSContext* cx, unsigned argc, Value* vp) {
   1604  AutoJSMethodProfilerEntry pseudoFrame(cx, "Set.prototype", "has");
   1605  CallArgs args = CallArgsFromVp(argc, vp);
   1606  return CallNonGenericMethod<SetObject::is, SetObject::has_impl>(cx, args);
   1607 }
   1608 
   1609 bool SetObject::add_impl(JSContext* cx, const CallArgs& args) {
   1610  auto* setObj = &args.thisv().toObject().as<SetObject>();
   1611  if (!setObj->add(cx, args.get(0))) {
   1612    return false;
   1613  }
   1614  args.rval().set(args.thisv());
   1615  return true;
   1616 }
   1617 
   1618 bool SetObject::add(JSContext* cx, unsigned argc, Value* vp) {
   1619  AutoJSMethodProfilerEntry pseudoFrame(cx, "Set.prototype", "add");
   1620  CallArgs args = CallArgsFromVp(argc, vp);
   1621  return CallNonGenericMethod<SetObject::is, SetObject::add_impl>(cx, args);
   1622 }
   1623 
   1624 bool SetObject::delete_(JSContext* cx, const Value& key, bool* rval) {
   1625  HashableValue k;
   1626  if (!k.setValue(cx, key)) {
   1627    return false;
   1628  }
   1629 
   1630  *rval = Table(this).remove(cx, k);
   1631  return true;
   1632 }
   1633 
   1634 bool SetObject::delete_impl(JSContext* cx, const CallArgs& args) {
   1635  auto* setObj = &args.thisv().toObject().as<SetObject>();
   1636  bool found;
   1637  if (!setObj->delete_(cx, args.get(0), &found)) {
   1638    return false;
   1639  }
   1640  args.rval().setBoolean(found);
   1641  return true;
   1642 }
   1643 
   1644 bool SetObject::delete_(JSContext* cx, unsigned argc, Value* vp) {
   1645  AutoJSMethodProfilerEntry pseudoFrame(cx, "Set.prototype", "delete");
   1646  CallArgs args = CallArgsFromVp(argc, vp);
   1647  return CallNonGenericMethod<SetObject::is, SetObject::delete_impl>(cx, args);
   1648 }
   1649 
   1650 bool SetObject::iterator(JSContext* cx, IteratorKind kind,
   1651                         Handle<SetObject*> obj, MutableHandleValue iter) {
   1652  JSObject* iterobj = SetIteratorObject::create(cx, obj, kind);
   1653  if (!iterobj) {
   1654    return false;
   1655  }
   1656  iter.setObject(*iterobj);
   1657  return true;
   1658 }
   1659 
   1660 bool SetObject::iterator_impl(JSContext* cx, const CallArgs& args,
   1661                              IteratorKind kind) {
   1662  Rooted<SetObject*> setObj(cx, &args.thisv().toObject().as<SetObject>());
   1663  return iterator(cx, kind, setObj, args.rval());
   1664 }
   1665 
   1666 bool SetObject::values_impl(JSContext* cx, const CallArgs& args) {
   1667  return iterator_impl(cx, args, IteratorKind::Values);
   1668 }
   1669 
   1670 bool SetObject::values(JSContext* cx, unsigned argc, Value* vp) {
   1671  AutoJSMethodProfilerEntry pseudoFrame(cx, "Set.prototype", "values");
   1672  CallArgs args = CallArgsFromVp(argc, vp);
   1673  return CallNonGenericMethod(cx, is, values_impl, args);
   1674 }
   1675 
   1676 bool SetObject::entries_impl(JSContext* cx, const CallArgs& args) {
   1677  return iterator_impl(cx, args, IteratorKind::Entries);
   1678 }
   1679 
   1680 bool SetObject::entries(JSContext* cx, unsigned argc, Value* vp) {
   1681  AutoJSMethodProfilerEntry pseudoFrame(cx, "Set.prototype", "entries");
   1682  CallArgs args = CallArgsFromVp(argc, vp);
   1683  return CallNonGenericMethod(cx, is, entries_impl, args);
   1684 }
   1685 
   1686 void SetObject::clear(JSContext* cx) { Table(this).clear(cx); }
   1687 
   1688 bool SetObject::clear_impl(JSContext* cx, const CallArgs& args) {
   1689  auto* setObj = &args.thisv().toObject().as<SetObject>();
   1690  setObj->clear(cx);
   1691  args.rval().setUndefined();
   1692  return true;
   1693 }
   1694 
   1695 bool SetObject::clear(JSContext* cx, unsigned argc, Value* vp) {
   1696  AutoJSMethodProfilerEntry pseudoFrame(cx, "Set.prototype", "clear");
   1697  CallArgs args = CallArgsFromVp(argc, vp);
   1698  return CallNonGenericMethod(cx, is, clear_impl, args);
   1699 }
   1700 
   1701 bool SetObject::copy(JSContext* cx, unsigned argc, Value* vp) {
   1702  CallArgs args = CallArgsFromVp(argc, vp);
   1703  MOZ_ASSERT(args.length() == 1);
   1704  MOZ_ASSERT(SetObject::is(args[0]));
   1705 
   1706  auto* result = SetObject::create(cx);
   1707  if (!result) {
   1708    return false;
   1709  }
   1710 
   1711  auto* from = &args[0].toObject().as<SetObject>();
   1712 
   1713  auto addToResult = [cx, result](auto& entry) {
   1714    return result->addHashableValue(cx, entry);
   1715  };
   1716  if (!Table(from).forEachEntry(addToResult)) {
   1717    return false;
   1718  }
   1719 
   1720  args.rval().setObject(*result);
   1721  return true;
   1722 }
   1723 
   1724 /*** JS static utility functions ********************************************/
   1725 
   1726 static bool forEach(const char* funcName, JSContext* cx, HandleObject obj,
   1727                    HandleValue callbackFn, HandleValue thisArg) {
   1728  CHECK_THREAD(cx);
   1729 
   1730  RootedId forEachId(cx, NameToId(cx->names().forEach));
   1731  RootedFunction forEachFunc(
   1732      cx, JS::GetSelfHostedFunction(cx, funcName, forEachId, 2));
   1733  if (!forEachFunc) {
   1734    return false;
   1735  }
   1736 
   1737  RootedValue fval(cx, ObjectValue(*forEachFunc));
   1738  return Call(cx, fval, obj, callbackFn, thisArg, &fval);
   1739 }
   1740 
   1741 // RAII class that unwraps a wrapped Map or Set object and then enters its
   1742 // realm.
   1743 template <typename TableObject>
   1744 class MOZ_RAII AutoEnterTableRealm {
   1745  mozilla::Maybe<AutoRealm> ar_;
   1746  Rooted<TableObject*> unwrapped_;
   1747 
   1748 public:
   1749  AutoEnterTableRealm(JSContext* cx, JSObject* obj) : unwrapped_(cx) {
   1750    JSObject* unwrapped = UncheckedUnwrap(obj);
   1751    MOZ_ASSERT(unwrapped != obj);
   1752    MOZ_RELEASE_ASSERT(unwrapped->is<TableObject>());
   1753    unwrapped_ = &unwrapped->as<TableObject>();
   1754    ar_.emplace(cx, unwrapped_);
   1755  }
   1756  Handle<TableObject*> unwrapped() const { return unwrapped_; }
   1757 };
   1758 
   1759 /*** JS public APIs *********************************************************/
   1760 
   1761 JS_PUBLIC_API JSObject* JS::NewMapObject(JSContext* cx) {
   1762  return MapObject::create(cx);
   1763 }
   1764 
   1765 JS_PUBLIC_API uint32_t JS::MapSize(JSContext* cx, HandleObject obj) {
   1766  CHECK_THREAD(cx);
   1767  cx->check(obj);
   1768 
   1769  if (obj->is<MapObject>()) {
   1770    return obj.as<MapObject>()->size();
   1771  }
   1772 
   1773  AutoEnterTableRealm<MapObject> enter(cx, obj);
   1774  return enter.unwrapped()->size();
   1775 }
   1776 
   1777 JS_PUBLIC_API bool JS::MapGet(JSContext* cx, HandleObject obj, HandleValue key,
   1778                              MutableHandleValue rval) {
   1779  CHECK_THREAD(cx);
   1780  cx->check(obj, key, rval);
   1781 
   1782  if (obj->is<MapObject>()) {
   1783    return obj.as<MapObject>()->get(cx, key, rval);
   1784  }
   1785 
   1786  {
   1787    AutoEnterTableRealm<MapObject> enter(cx, obj);
   1788    Rooted<Value> wrappedKey(cx, key);
   1789    if (!JS_WrapValue(cx, &wrappedKey)) {
   1790      return false;
   1791    }
   1792    if (!enter.unwrapped()->get(cx, wrappedKey, rval)) {
   1793      return false;
   1794    }
   1795  }
   1796  return JS_WrapValue(cx, rval);
   1797 }
   1798 
   1799 JS_PUBLIC_API bool JS::MapSet(JSContext* cx, HandleObject obj, HandleValue key,
   1800                              HandleValue val) {
   1801  CHECK_THREAD(cx);
   1802  cx->check(obj, key, val);
   1803 
   1804  if (obj->is<MapObject>()) {
   1805    return obj.as<MapObject>()->set(cx, key, val);
   1806  }
   1807 
   1808  AutoEnterTableRealm<MapObject> enter(cx, obj);
   1809  Rooted<Value> wrappedKey(cx, key);
   1810  Rooted<Value> wrappedValue(cx, val);
   1811  if (!JS_WrapValue(cx, &wrappedKey) || !JS_WrapValue(cx, &wrappedValue)) {
   1812    return false;
   1813  }
   1814  return enter.unwrapped()->set(cx, wrappedKey, wrappedValue);
   1815 }
   1816 
   1817 JS_PUBLIC_API bool JS::MapHas(JSContext* cx, HandleObject obj, HandleValue key,
   1818                              bool* rval) {
   1819  CHECK_THREAD(cx);
   1820  cx->check(obj, key);
   1821 
   1822  if (obj->is<MapObject>()) {
   1823    return obj.as<MapObject>()->has(cx, key, rval);
   1824  }
   1825 
   1826  AutoEnterTableRealm<MapObject> enter(cx, obj);
   1827  Rooted<Value> wrappedKey(cx, key);
   1828  if (!JS_WrapValue(cx, &wrappedKey)) {
   1829    return false;
   1830  }
   1831  return enter.unwrapped()->has(cx, wrappedKey, rval);
   1832 }
   1833 
   1834 JS_PUBLIC_API bool JS::MapGetOrInsert(JSContext* cx, HandleObject obj,
   1835                                      HandleValue key, HandleValue val,
   1836                                      MutableHandleValue rval) {
   1837  CHECK_THREAD(cx);
   1838  cx->check(obj, key, val);
   1839 
   1840  if (obj->is<MapObject>()) {
   1841    return obj.as<MapObject>()->getOrInsert(cx, key, val, rval);
   1842  }
   1843  {
   1844    AutoEnterTableRealm<MapObject> enter(cx, obj);
   1845    Rooted<Value> wrappedKey(cx, key);
   1846    Rooted<Value> wrappedValue(cx, val);
   1847    if (!JS_WrapValue(cx, &wrappedKey) || !JS_WrapValue(cx, &wrappedValue)) {
   1848      return false;
   1849    }
   1850    if (!enter.unwrapped()->getOrInsert(cx, wrappedKey, wrappedValue, rval)) {
   1851      return false;
   1852    }
   1853  }
   1854  return JS_WrapValue(cx, rval);
   1855 }
   1856 
   1857 JS_PUBLIC_API bool JS::MapDelete(JSContext* cx, HandleObject obj,
   1858                                 HandleValue key, bool* rval) {
   1859  CHECK_THREAD(cx);
   1860  cx->check(obj, key);
   1861 
   1862  if (obj->is<MapObject>()) {
   1863    return obj.as<MapObject>()->delete_(cx, key, rval);
   1864  }
   1865 
   1866  AutoEnterTableRealm<MapObject> enter(cx, obj);
   1867  Rooted<Value> wrappedKey(cx, key);
   1868  if (!JS_WrapValue(cx, &wrappedKey)) {
   1869    return false;
   1870  }
   1871  return enter.unwrapped()->delete_(cx, wrappedKey, rval);
   1872 }
   1873 
   1874 JS_PUBLIC_API bool JS::MapClear(JSContext* cx, HandleObject obj) {
   1875  CHECK_THREAD(cx);
   1876  cx->check(obj);
   1877 
   1878  if (obj->is<MapObject>()) {
   1879    obj.as<MapObject>()->clear(cx);
   1880    return true;
   1881  }
   1882 
   1883  AutoEnterTableRealm<MapObject> enter(cx, obj);
   1884  enter.unwrapped()->clear(cx);
   1885  return true;
   1886 }
   1887 
   1888 template <typename TableObject>
   1889 [[nodiscard]] static bool CreateIterator(
   1890    JSContext* cx, typename TableObject::IteratorKind kind,
   1891    Handle<JSObject*> obj, MutableHandle<Value> rval) {
   1892  CHECK_THREAD(cx);
   1893  cx->check(obj);
   1894 
   1895  if (obj->is<TableObject>()) {
   1896    return TableObject::iterator(cx, kind, obj.as<TableObject>(), rval);
   1897  }
   1898 
   1899  {
   1900    AutoEnterTableRealm<TableObject> enter(cx, obj);
   1901    if (!TableObject::iterator(cx, kind, enter.unwrapped(), rval)) {
   1902      return false;
   1903    }
   1904  }
   1905  return JS_WrapValue(cx, rval);
   1906 }
   1907 
   1908 JS_PUBLIC_API bool JS::MapKeys(JSContext* cx, HandleObject obj,
   1909                               MutableHandleValue rval) {
   1910  return CreateIterator<MapObject>(cx, MapObject::IteratorKind::Keys, obj,
   1911                                   rval);
   1912 }
   1913 
   1914 JS_PUBLIC_API bool JS::MapValues(JSContext* cx, HandleObject obj,
   1915                                 MutableHandleValue rval) {
   1916  return CreateIterator<MapObject>(cx, MapObject::IteratorKind::Values, obj,
   1917                                   rval);
   1918 }
   1919 
   1920 JS_PUBLIC_API bool JS::MapEntries(JSContext* cx, HandleObject obj,
   1921                                  MutableHandleValue rval) {
   1922  return CreateIterator<MapObject>(cx, MapObject::IteratorKind::Entries, obj,
   1923                                   rval);
   1924 }
   1925 
   1926 JS_PUBLIC_API bool JS::MapForEach(JSContext* cx, HandleObject obj,
   1927                                  HandleValue callbackFn, HandleValue thisVal) {
   1928  return forEach("MapForEach", cx, obj, callbackFn, thisVal);
   1929 }
   1930 
   1931 JS_PUBLIC_API JSObject* JS::NewSetObject(JSContext* cx) {
   1932  return SetObject::create(cx);
   1933 }
   1934 
   1935 JS_PUBLIC_API uint32_t JS::SetSize(JSContext* cx, HandleObject obj) {
   1936  CHECK_THREAD(cx);
   1937  cx->check(obj);
   1938 
   1939  if (obj->is<SetObject>()) {
   1940    return obj.as<SetObject>()->size();
   1941  }
   1942 
   1943  AutoEnterTableRealm<SetObject> enter(cx, obj);
   1944  return enter.unwrapped()->size();
   1945 }
   1946 
   1947 JS_PUBLIC_API bool JS::SetAdd(JSContext* cx, HandleObject obj,
   1948                              HandleValue key) {
   1949  CHECK_THREAD(cx);
   1950  cx->check(obj, key);
   1951 
   1952  if (obj->is<SetObject>()) {
   1953    return obj.as<SetObject>()->add(cx, key);
   1954  }
   1955 
   1956  AutoEnterTableRealm<SetObject> enter(cx, obj);
   1957  Rooted<Value> wrappedKey(cx, key);
   1958  if (!JS_WrapValue(cx, &wrappedKey)) {
   1959    return false;
   1960  }
   1961  return enter.unwrapped()->add(cx, wrappedKey);
   1962 }
   1963 
   1964 JS_PUBLIC_API bool JS::SetHas(JSContext* cx, HandleObject obj, HandleValue key,
   1965                              bool* rval) {
   1966  CHECK_THREAD(cx);
   1967  cx->check(obj, key);
   1968 
   1969  if (obj->is<SetObject>()) {
   1970    return obj.as<SetObject>()->has(cx, key, rval);
   1971  }
   1972 
   1973  AutoEnterTableRealm<SetObject> enter(cx, obj);
   1974  Rooted<Value> wrappedKey(cx, key);
   1975  if (!JS_WrapValue(cx, &wrappedKey)) {
   1976    return false;
   1977  }
   1978  return enter.unwrapped()->has(cx, wrappedKey, rval);
   1979 }
   1980 
   1981 JS_PUBLIC_API bool JS::SetDelete(JSContext* cx, HandleObject obj,
   1982                                 HandleValue key, bool* rval) {
   1983  CHECK_THREAD(cx);
   1984  cx->check(obj, key);
   1985 
   1986  if (obj->is<SetObject>()) {
   1987    return obj.as<SetObject>()->delete_(cx, key, rval);
   1988  }
   1989 
   1990  AutoEnterTableRealm<SetObject> enter(cx, obj);
   1991  Rooted<Value> wrappedKey(cx, key);
   1992  if (!JS_WrapValue(cx, &wrappedKey)) {
   1993    return false;
   1994  }
   1995  return enter.unwrapped()->delete_(cx, wrappedKey, rval);
   1996 }
   1997 
   1998 JS_PUBLIC_API bool JS::SetClear(JSContext* cx, HandleObject obj) {
   1999  CHECK_THREAD(cx);
   2000  cx->check(obj);
   2001 
   2002  if (obj->is<SetObject>()) {
   2003    obj.as<SetObject>()->clear(cx);
   2004    return true;
   2005  }
   2006 
   2007  AutoEnterTableRealm<SetObject> enter(cx, obj);
   2008  enter.unwrapped()->clear(cx);
   2009  return true;
   2010 }
   2011 
   2012 JS_PUBLIC_API bool JS::SetKeys(JSContext* cx, HandleObject obj,
   2013                               MutableHandleValue rval) {
   2014  return SetValues(cx, obj, rval);
   2015 }
   2016 
   2017 JS_PUBLIC_API bool JS::SetValues(JSContext* cx, HandleObject obj,
   2018                                 MutableHandleValue rval) {
   2019  return CreateIterator<SetObject>(cx, SetObject::IteratorKind::Values, obj,
   2020                                   rval);
   2021 }
   2022 
   2023 JS_PUBLIC_API bool JS::SetEntries(JSContext* cx, HandleObject obj,
   2024                                  MutableHandleValue rval) {
   2025  return CreateIterator<SetObject>(cx, SetObject::IteratorKind::Entries, obj,
   2026                                   rval);
   2027 }
   2028 
   2029 JS_PUBLIC_API bool JS::SetForEach(JSContext* cx, HandleObject obj,
   2030                                  HandleValue callbackFn, HandleValue thisVal) {
   2031  return forEach("SetForEach", cx, obj, callbackFn, thisVal);
   2032 }
   2033 
   2034 JS_PUBLIC_API bool js::GetSetObjectKeys(
   2035    JSContext* cx, JS::HandleObject obj,
   2036    JS::MutableHandle<JS::GCVector<JS::Value>> keys) {
   2037  CHECK_THREAD(cx);
   2038  cx->check(obj);
   2039 
   2040  if (obj->is<SetObject>()) {
   2041    return obj->as<SetObject>().keys(keys);
   2042  }
   2043 
   2044  {
   2045    AutoEnterTableRealm<SetObject> enter(cx, obj);
   2046    if (!enter.unwrapped()->keys(keys)) {
   2047      return false;
   2048    }
   2049  }
   2050 
   2051  for (uint32_t i = 0; i < keys.length(); i++) {
   2052    if (!JS_WrapValue(cx, keys[i])) {
   2053      return false;
   2054    }
   2055  }
   2056 
   2057  return true;
   2058 }
   2059 
   2060 JS_PUBLIC_API bool js::GetMapObjectKeysAndValuesInterleaved(
   2061    JSContext* cx, JS::HandleObject obj,
   2062    JS::MutableHandle<JS::GCVector<JS::Value>> entries) {
   2063  CHECK_THREAD(cx);
   2064  cx->check(obj);
   2065 
   2066  if (obj->is<MapObject>()) {
   2067    return obj->as<MapObject>().getKeysAndValuesInterleaved(entries);
   2068  }
   2069 
   2070  {
   2071    AutoEnterTableRealm<MapObject> enter(cx, obj);
   2072    if (!enter.unwrapped()->getKeysAndValuesInterleaved(entries)) {
   2073      return false;
   2074    }
   2075  }
   2076 
   2077  for (uint32_t i = 0; i < entries.length(); i++) {
   2078    if (!JS_WrapValue(cx, entries[i])) {
   2079      return false;
   2080    }
   2081  }
   2082 
   2083  return true;
   2084 }