tor-browser

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

Iteration.cpp (79070B)


      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 /* JavaScript iterators. */
      8 
      9 #include "vm/Iteration.h"
     10 
     11 #include "mozilla/ArrayUtils.h"
     12 #include "mozilla/Likely.h"
     13 #include "mozilla/Maybe.h"
     14 #include "mozilla/MemoryReporting.h"
     15 #include "mozilla/PodOperations.h"
     16 
     17 #include <algorithm>
     18 #include <new>
     19 
     20 #include "jsapi.h"
     21 #include "jstypes.h"
     22 
     23 #include "builtin/Array.h"
     24 #include "builtin/MapObject.h"
     25 #include "builtin/SelfHostingDefines.h"
     26 #include "ds/Sort.h"
     27 #include "gc/GC.h"
     28 #include "gc/GCContext.h"
     29 #include "js/ForOfIterator.h"         // JS::ForOfIterator
     30 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
     31 #include "js/PropertySpec.h"
     32 #include "util/DifferentialTesting.h"
     33 #include "util/Poison.h"
     34 #include "vm/GlobalObject.h"
     35 #include "vm/Interpreter.h"
     36 #include "vm/JSContext.h"
     37 #include "vm/JSObject.h"
     38 #include "vm/NativeObject.h"  // js::PlainObject
     39 #include "vm/Shape.h"
     40 #include "vm/StringType.h"
     41 #include "vm/TypedArrayObject.h"
     42 #include "vm/Watchtower.h"
     43 
     44 #include "gc/StoreBuffer-inl.h"
     45 #include "vm/NativeObject-inl.h"
     46 #include "vm/PlainObject-inl.h"  // js::PlainObject::createWithTemplate
     47 
     48 using namespace js;
     49 
     50 using mozilla::ArrayEqual;
     51 using mozilla::Maybe;
     52 using mozilla::PodCopy;
     53 
     54 using RootedPropertyIteratorObject = Rooted<PropertyIteratorObject*>;
     55 
     56 static const gc::AllocKind ITERATOR_FINALIZE_KIND =
     57    gc::AllocKind::OBJECT2_BACKGROUND;
     58 
     59 // Beware!  This function may have to trace incompletely-initialized
     60 // |NativeIterator| allocations if the |IdToString| in that constructor recurs
     61 // into this code.
     62 void NativeIterator::trace(JSTracer* trc) {
     63  TraceNullableEdge(trc, &objectBeingIterated_, "objectBeingIterated_");
     64  TraceNullableEdge(trc, &iterObj_, "iterObj_");
     65  TraceNullableEdge(trc, &objShape_, "objShape_");
     66 
     67  // The limits below are correct at every instant of |NativeIterator|
     68  // initialization, with the end-pointer incremented as each new shape is
     69  // created, so they're safe to use here.
     70  std::for_each(protoShapesBegin(allocatedPropertyCount()), protoShapesEnd(),
     71                [trc](GCPtr<Shape*>& shape) {
     72                  TraceEdge(trc, &shape, "iterator_proto_shape");
     73                });
     74 
     75  std::for_each(propertiesBegin(), propertiesEnd(),
     76                [trc](IteratorProperty& prop) { prop.traceString(trc); });
     77 }
     78 
     79 void IteratorProperty::traceString(JSTracer* trc) {
     80  // Properties begin life non-null and never *become* null.
     81  // Trace the underlying string while preserving the deleted bit.
     82  JSLinearString* str = asString();
     83  TraceManuallyBarrieredEdge(trc, &str, "iterator-property-string");
     84  raw_ = uintptr_t(str) | (deleted() ? DeletedBit : 0);
     85 }
     86 
     87 using PropertyKeySet = GCHashSet<PropertyKey, DefaultHasher<PropertyKey>>;
     88 
     89 class PropertyEnumerator {
     90  RootedObject obj_;
     91  MutableHandleIdVector props_;
     92  PropertyIndexVector* indices_;
     93 
     94  uint32_t flags_;
     95  Rooted<PropertyKeySet> visited_;
     96 
     97  uint32_t ownPropertyCount_;
     98 
     99  bool enumeratingProtoChain_ = false;
    100 
    101  enum class IndicesState {
    102    // Every property that has been enumerated so far can be represented as a
    103    // PropertyIndex, but we are not currently producing a list of indices. If
    104    // the state is Valid when we are done enumerating, then the resulting
    105    // iterator can be marked with NativeIterator::Flags::IndicesSupported.
    106    Valid,
    107 
    108    // Every property that has been enumerated so far can be represented as a
    109    // PropertyIndex, and |indices_| points to a PropertyIndexVector containing
    110    // those indices. This is used when we want to create a NativeIterator with
    111    // valid indices.
    112    Allocating,
    113 
    114    // It is not possible to represent every property of the object being
    115    // enumerated as a PropertyIndex. For example, enumerated properties on the
    116    // prototype chain are unsupported. We can transition to this state from
    117    // either of the other two.
    118    Unsupported
    119  };
    120  IndicesState indicesState_;
    121 
    122 public:
    123  PropertyEnumerator(JSContext* cx, JSObject* obj, uint32_t flags,
    124                     MutableHandleIdVector props,
    125                     PropertyIndexVector* indices = nullptr)
    126      : obj_(cx, obj),
    127        props_(props),
    128        indices_(indices),
    129        flags_(flags),
    130        visited_(cx, PropertyKeySet(cx)),
    131        indicesState_(indices ? IndicesState::Allocating
    132                              : IndicesState::Valid) {}
    133 
    134  bool snapshot(JSContext* cx);
    135 
    136  void markIndicesUnsupported() { indicesState_ = IndicesState::Unsupported; }
    137  bool supportsIndices() const {
    138    return indicesState_ != IndicesState::Unsupported;
    139  }
    140  bool allocatingIndices() const {
    141    return indicesState_ == IndicesState::Allocating;
    142  }
    143  uint32_t ownPropertyCount() const { return ownPropertyCount_; }
    144 
    145 private:
    146  template <bool CheckForDuplicates>
    147  bool enumerate(JSContext* cx, jsid id, bool enumerable,
    148                 PropertyIndex index = PropertyIndex::Invalid());
    149 
    150  bool enumerateExtraProperties(JSContext* cx);
    151 
    152  template <bool CheckForDuplicates>
    153  bool enumerateNativeProperties(JSContext* cx);
    154 
    155  bool enumerateNativeProperties(JSContext* cx, bool checkForDuplicates) {
    156    if (checkForDuplicates) {
    157      return enumerateNativeProperties<true>(cx);
    158    }
    159    return enumerateNativeProperties<false>(cx);
    160  }
    161 
    162  template <bool CheckForDuplicates>
    163  bool enumerateProxyProperties(JSContext* cx);
    164 
    165  void reversePropsAndIndicesAfter(size_t initialLength) {
    166    // We iterate through prop maps in descending order of property creation,
    167    // but we need our return value to be in ascending order. If we are tracking
    168    // property indices, make sure to keep them in sync.
    169    MOZ_ASSERT(props_.begin() + initialLength <= props_.end());
    170    MOZ_ASSERT_IF(allocatingIndices(), props_.length() == indices_->length());
    171 
    172    std::reverse(props_.begin() + initialLength, props_.end());
    173    if (allocatingIndices()) {
    174      std::reverse(indices_->begin() + initialLength, indices_->end());
    175    }
    176  }
    177 };
    178 
    179 template <bool CheckForDuplicates>
    180 bool PropertyEnumerator::enumerate(JSContext* cx, jsid id, bool enumerable,
    181                                   PropertyIndex index) {
    182  if (CheckForDuplicates) {
    183    // If we've already seen this, we definitely won't add it.
    184    PropertyKeySet::AddPtr p = visited_.lookupForAdd(id);
    185    if (MOZ_UNLIKELY(!!p)) {
    186      return true;
    187    }
    188 
    189    // It's not necessary to add properties to the hash set at the end of
    190    // the prototype chain, but custom enumeration behaviors might return
    191    // duplicated properties, so always add in such cases.
    192    if (obj_->is<ProxyObject>() || obj_->staticPrototype() ||
    193        obj_->getClass()->getNewEnumerate()) {
    194      if (!visited_.add(p, id)) {
    195        return false;
    196      }
    197    }
    198  }
    199 
    200  if (!enumerable && !(flags_ & JSITER_HIDDEN)) {
    201    return true;
    202  }
    203 
    204  // Symbol-keyed properties and nonenumerable properties are skipped unless
    205  // the caller specifically asks for them. A caller can also filter out
    206  // non-symbols by asking for JSITER_SYMBOLSONLY. PrivateName symbols are
    207  // skipped unless JSITER_PRIVATE is passed.
    208  if (id.isSymbol()) {
    209    if (!(flags_ & JSITER_SYMBOLS)) {
    210      return true;
    211    }
    212    if (!(flags_ & JSITER_PRIVATE) && id.isPrivateName()) {
    213      return true;
    214    }
    215  } else {
    216    if ((flags_ & JSITER_SYMBOLSONLY)) {
    217      return true;
    218    }
    219  }
    220 
    221  MOZ_ASSERT_IF(allocatingIndices(), indices_->length() == props_.length());
    222  if (!props_.append(id)) {
    223    return false;
    224  }
    225 
    226  if (!supportsIndices()) {
    227    return true;
    228  }
    229  if (index.kind() == PropertyIndex::Kind::Invalid || enumeratingProtoChain_) {
    230    markIndicesUnsupported();
    231    return true;
    232  }
    233 
    234  if (allocatingIndices() && !indices_->append(index)) {
    235    return false;
    236  }
    237 
    238  return true;
    239 }
    240 
    241 bool PropertyEnumerator::enumerateExtraProperties(JSContext* cx) {
    242  MOZ_ASSERT(obj_->getClass()->getNewEnumerate());
    243 
    244  RootedIdVector properties(cx);
    245  bool enumerableOnly = !(flags_ & JSITER_HIDDEN);
    246  if (!obj_->getClass()->getNewEnumerate()(cx, obj_, &properties,
    247                                           enumerableOnly)) {
    248    return false;
    249  }
    250 
    251  RootedId id(cx);
    252  for (size_t n = 0; n < properties.length(); n++) {
    253    id = properties[n];
    254 
    255    // The enumerate hook does not indicate whether the properties
    256    // it returns are enumerable or not. Since we already passed
    257    // `enumerableOnly` to the hook to filter out non-enumerable
    258    // properties, it doesn't really matter what we pass here.
    259    bool enumerable = true;
    260    if (!enumerate<true>(cx, id, enumerable)) {
    261      return false;
    262    }
    263  }
    264 
    265  return true;
    266 }
    267 
    268 static bool SortComparatorIntegerIds(jsid a, jsid b, bool* lessOrEqualp) {
    269  uint32_t indexA, indexB;
    270  MOZ_ALWAYS_TRUE(IdIsIndex(a, &indexA));
    271  MOZ_ALWAYS_TRUE(IdIsIndex(b, &indexB));
    272  *lessOrEqualp = (indexA <= indexB);
    273  return true;
    274 }
    275 
    276 template <bool CheckForDuplicates>
    277 bool PropertyEnumerator::enumerateNativeProperties(JSContext* cx) {
    278  Handle<NativeObject*> pobj = obj_.as<NativeObject>();
    279 
    280  if (Watchtower::watchesPropertyValueChange(pobj)) {
    281    markIndicesUnsupported();
    282  }
    283 
    284  // We don't need to iterate over the shape's properties if we're only
    285  // interested in enumerable properties and the object is known to have no
    286  // enumerable properties.
    287  //
    288  // Don't optimize if CheckForDuplicates is true, because non-enumerable
    289  // properties still have to participate in duplicate-property checking.
    290  const bool iterShapeProperties = CheckForDuplicates ||
    291                                   (flags_ & JSITER_HIDDEN) ||
    292                                   pobj->hasEnumerableProperty();
    293 
    294  bool enumerateSymbols;
    295  if (flags_ & JSITER_SYMBOLSONLY) {
    296    if (!iterShapeProperties) {
    297      return true;
    298    }
    299    enumerateSymbols = true;
    300  } else {
    301    // Collect any dense elements from this object.
    302    size_t firstElemIndex = props_.length();
    303    size_t initlen = pobj->getDenseInitializedLength();
    304    const Value* elements = pobj->getDenseElements();
    305    bool hasHoles = false;
    306    for (uint32_t i = 0; i < initlen; ++i) {
    307      if (elements[i].isMagic(JS_ELEMENTS_HOLE)) {
    308        hasHoles = true;
    309      } else {
    310        // Dense arrays never get so large that i would not fit into an
    311        // integer id.
    312        if (!enumerate<CheckForDuplicates>(cx, PropertyKey::Int(i),
    313                                           /* enumerable = */ true,
    314                                           PropertyIndex::ForElement(i))) {
    315          return false;
    316        }
    317      }
    318    }
    319 
    320    // Collect any typed array or shared typed array elements from this
    321    // object.
    322    if (pobj->is<TypedArrayObject>()) {
    323      size_t len = pobj->as<TypedArrayObject>().length().valueOr(0);
    324 
    325      // Fail early if the typed array is enormous, because this will be very
    326      // slow and will likely report OOM. This also means we don't need to
    327      // handle indices greater than PropertyKey::IntMax in the loop below.
    328      static_assert(PropertyKey::IntMax == INT32_MAX);
    329      if (len > INT32_MAX) {
    330        ReportOutOfMemory(cx);
    331        return false;
    332      }
    333 
    334      for (uint32_t i = 0; i < len; i++) {
    335        if (!enumerate<CheckForDuplicates>(cx, PropertyKey::Int(i),
    336                                           /* enumerable = */ true)) {
    337          return false;
    338        }
    339      }
    340    }
    341 
    342    // The code below enumerates shape properties (including sparse elements) so
    343    // if we can ignore those we're done.
    344    if (!iterShapeProperties) {
    345      return true;
    346    }
    347 
    348    // Collect any sparse elements from this object.
    349    bool isIndexed = pobj->isIndexed();
    350    if (isIndexed) {
    351      // If the dense elements didn't have holes, we don't need to include
    352      // them in the sort.
    353      if (!hasHoles) {
    354        firstElemIndex = props_.length();
    355      }
    356 
    357      for (ShapePropertyIter<NoGC> iter(pobj->shape()); !iter.done(); iter++) {
    358        jsid id = iter->key();
    359        uint32_t dummy;
    360        if (IdIsIndex(id, &dummy)) {
    361          if (!enumerate<CheckForDuplicates>(cx, id, iter->enumerable())) {
    362            return false;
    363          }
    364        }
    365      }
    366 
    367      MOZ_ASSERT(firstElemIndex <= props_.length());
    368 
    369      jsid* ids = props_.begin() + firstElemIndex;
    370      size_t n = props_.length() - firstElemIndex;
    371 
    372      RootedIdVector tmp(cx);
    373      if (!tmp.resize(n)) {
    374        return false;
    375      }
    376      PodCopy(tmp.begin(), ids, n);
    377 
    378      if (!MergeSort(ids, n, tmp.begin(), SortComparatorIntegerIds)) {
    379        return false;
    380      }
    381    }
    382 
    383    size_t initialLength = props_.length();
    384 
    385    /* Collect all unique property names from this object's shape. */
    386    bool symbolsFound = false;
    387    for (ShapePropertyIter<NoGC> iter(pobj->shape()); !iter.done(); iter++) {
    388      jsid id = iter->key();
    389 
    390      if (id.isSymbol()) {
    391        symbolsFound = true;
    392        continue;
    393      }
    394 
    395      uint32_t dummy;
    396      if (isIndexed && IdIsIndex(id, &dummy)) {
    397        continue;
    398      }
    399 
    400      PropertyIndex index = iter->isDataProperty() && iter->writable()
    401                                ? PropertyIndex::ForSlot(pobj, iter->slot())
    402                                : PropertyIndex::Invalid();
    403      if (!enumerate<CheckForDuplicates>(cx, id, iter->enumerable(), index)) {
    404        return false;
    405      }
    406    }
    407    reversePropsAndIndicesAfter(initialLength);
    408 
    409    enumerateSymbols = symbolsFound && (flags_ & JSITER_SYMBOLS);
    410  }
    411 
    412  if (enumerateSymbols) {
    413    MOZ_ASSERT(iterShapeProperties);
    414    MOZ_ASSERT(!allocatingIndices());
    415 
    416    // Do a second pass to collect symbols. The spec requires that all symbols
    417    // appear after all strings in [[OwnPropertyKeys]] for ordinary objects:
    418    // https://tc39.es/ecma262/#sec-ordinaryownpropertykeys
    419    size_t initialLength = props_.length();
    420    for (ShapePropertyIter<NoGC> iter(pobj->shape()); !iter.done(); iter++) {
    421      jsid id = iter->key();
    422      if (id.isSymbol()) {
    423        if (!enumerate<CheckForDuplicates>(cx, id, iter->enumerable())) {
    424          return false;
    425        }
    426      }
    427    }
    428    reversePropsAndIndicesAfter(initialLength);
    429  }
    430 
    431  return true;
    432 }
    433 
    434 template <bool CheckForDuplicates>
    435 bool PropertyEnumerator::enumerateProxyProperties(JSContext* cx) {
    436  MOZ_ASSERT(obj_->is<ProxyObject>());
    437 
    438  RootedIdVector proxyProps(cx);
    439 
    440  if (flags_ & JSITER_HIDDEN || flags_ & JSITER_SYMBOLS) {
    441    // This gets all property keys, both strings and symbols. The call to
    442    // enumerate in the loop below will filter out unwanted keys, per the
    443    // flags.
    444    if (!Proxy::ownPropertyKeys(cx, obj_, &proxyProps)) {
    445      return false;
    446    }
    447 
    448    Rooted<mozilla::Maybe<PropertyDescriptor>> desc(cx);
    449    for (size_t n = 0, len = proxyProps.length(); n < len; n++) {
    450      bool enumerable = false;
    451 
    452      // We need to filter, if the caller just wants enumerable symbols.
    453      if (!(flags_ & JSITER_HIDDEN)) {
    454        if (!Proxy::getOwnPropertyDescriptor(cx, obj_, proxyProps[n], &desc)) {
    455          return false;
    456        }
    457        enumerable = desc.isSome() && desc->enumerable();
    458      }
    459 
    460      if (!enumerate<CheckForDuplicates>(cx, proxyProps[n], enumerable)) {
    461        return false;
    462      }
    463    }
    464 
    465    return true;
    466  }
    467 
    468  // Returns enumerable property names (no symbols).
    469  if (!Proxy::getOwnEnumerablePropertyKeys(cx, obj_, &proxyProps)) {
    470    return false;
    471  }
    472 
    473  for (size_t n = 0, len = proxyProps.length(); n < len; n++) {
    474    if (!enumerate<CheckForDuplicates>(cx, proxyProps[n], true)) {
    475      return false;
    476    }
    477  }
    478 
    479  return true;
    480 }
    481 
    482 #ifdef DEBUG
    483 
    484 struct SortComparatorIds {
    485  JSContext* const cx;
    486 
    487  explicit SortComparatorIds(JSContext* cx) : cx(cx) {}
    488 
    489  bool operator()(jsid aArg, jsid bArg, bool* lessOrEqualp) {
    490    RootedId a(cx, aArg);
    491    RootedId b(cx, bArg);
    492 
    493    // Pick an arbitrary order on jsids that is as stable as possible
    494    // across executions.
    495    if (a == b) {
    496      *lessOrEqualp = true;
    497      return true;
    498    }
    499 
    500    enum class KeyType { Void, Int, String, Symbol };
    501 
    502    auto keyType = [](PropertyKey key) {
    503      if (key.isString()) {
    504        return KeyType::String;
    505      }
    506      if (key.isInt()) {
    507        return KeyType::Int;
    508      }
    509      if (key.isSymbol()) {
    510        return KeyType::Symbol;
    511      }
    512      MOZ_ASSERT(key.isVoid());
    513      return KeyType::Void;
    514    };
    515 
    516    if (keyType(a) != keyType(b)) {
    517      *lessOrEqualp = (keyType(a) <= keyType(b));
    518      return true;
    519    }
    520 
    521    if (a.isInt()) {
    522      *lessOrEqualp = (a.toInt() <= b.toInt());
    523      return true;
    524    }
    525 
    526    RootedString astr(cx), bstr(cx);
    527    if (a.isSymbol()) {
    528      MOZ_ASSERT(b.isSymbol());
    529      JS::SymbolCode ca = a.toSymbol()->code();
    530      JS::SymbolCode cb = b.toSymbol()->code();
    531      if (ca != cb) {
    532        *lessOrEqualp = uint32_t(ca) <= uint32_t(cb);
    533        return true;
    534      }
    535      MOZ_ASSERT(ca == JS::SymbolCode::PrivateNameSymbol ||
    536                 ca == JS::SymbolCode::InSymbolRegistry ||
    537                 ca == JS::SymbolCode::UniqueSymbol);
    538      astr = a.toSymbol()->description();
    539      bstr = b.toSymbol()->description();
    540      if (!astr || !bstr) {
    541        *lessOrEqualp = !astr;
    542        return true;
    543      }
    544 
    545      // Fall through to string comparison on the descriptions. The sort
    546      // order is nondeterministic if two different unique symbols have
    547      // the same description.
    548    } else {
    549      astr = IdToString(cx, a);
    550      if (!astr) {
    551        return false;
    552      }
    553      bstr = IdToString(cx, b);
    554      if (!bstr) {
    555        return false;
    556      }
    557    }
    558 
    559    int32_t result;
    560    if (!CompareStrings(cx, astr, bstr, &result)) {
    561      return false;
    562    }
    563 
    564    *lessOrEqualp = (result <= 0);
    565    return true;
    566  }
    567 };
    568 
    569 #endif /* DEBUG */
    570 
    571 static void AssertNoEnumerableProperties(NativeObject* obj) {
    572 #ifdef DEBUG
    573  // Verify the object has no enumerable properties if the HasEnumerable
    574  // ObjectFlag is not set.
    575 
    576  MOZ_ASSERT(!obj->hasEnumerableProperty());
    577 
    578  static constexpr size_t MaxPropsToCheck = 5;
    579 
    580  size_t count = 0;
    581  for (ShapePropertyIter<NoGC> iter(obj->shape()); !iter.done(); iter++) {
    582    MOZ_ASSERT(!iter->enumerable());
    583    if (++count > MaxPropsToCheck) {
    584      break;
    585    }
    586  }
    587 #endif  // DEBUG
    588 }
    589 
    590 static bool ProtoMayHaveEnumerableProperties(JSObject* obj) {
    591  if (!obj->is<NativeObject>()) {
    592    return true;
    593  }
    594 
    595  JSObject* proto = obj->as<NativeObject>().staticPrototype();
    596  while (proto) {
    597    if (!proto->is<NativeObject>()) {
    598      return true;
    599    }
    600    NativeObject* nproto = &proto->as<NativeObject>();
    601    if (nproto->hasEnumerableProperty() ||
    602        nproto->getDenseInitializedLength() > 0 ||
    603        ClassCanHaveExtraEnumeratedProperties(nproto->getClass())) {
    604      return true;
    605    }
    606    AssertNoEnumerableProperties(nproto);
    607    proto = nproto->staticPrototype();
    608  }
    609 
    610  return false;
    611 }
    612 
    613 bool PropertyEnumerator::snapshot(JSContext* cx) {
    614  // If we're only interested in enumerable properties and the proto chain has
    615  // no enumerable properties (the common case), we can optimize this to ignore
    616  // the proto chain. This also lets us take advantage of the no-duplicate-check
    617  // optimization below.
    618  if (!(flags_ & JSITER_HIDDEN) && !(flags_ & JSITER_OWNONLY) &&
    619      !ProtoMayHaveEnumerableProperties(obj_)) {
    620    flags_ |= JSITER_OWNONLY;
    621  }
    622 
    623  // Don't check for duplicates if we're only interested in own properties.
    624  // This does the right thing for most objects: native objects don't have
    625  // duplicate property ids and we allow the [[OwnPropertyKeys]] proxy trap to
    626  // return duplicates.
    627  //
    628  // The only special case is when the object has a newEnumerate hook: it
    629  // can return duplicate properties and we have to filter them. This is
    630  // handled below.
    631  bool checkForDuplicates = !(flags_ & JSITER_OWNONLY);
    632 
    633  do {
    634    if (obj_->getClass()->getNewEnumerate()) {
    635      markIndicesUnsupported();
    636 
    637      if (!enumerateExtraProperties(cx)) {
    638        return false;
    639      }
    640 
    641      if (obj_->is<NativeObject>()) {
    642        if (!enumerateNativeProperties(cx, /*checkForDuplicates*/ true)) {
    643          return false;
    644        }
    645      }
    646 
    647    } else if (obj_->is<NativeObject>()) {
    648      // Give the object a chance to resolve all lazy properties
    649      if (JSEnumerateOp enumerateOp = obj_->getClass()->getEnumerate()) {
    650        markIndicesUnsupported();
    651        if (!enumerateOp(cx, obj_.as<NativeObject>())) {
    652          return false;
    653        }
    654      }
    655      if (!enumerateNativeProperties(cx, checkForDuplicates)) {
    656        return false;
    657      }
    658    } else if (obj_->is<ProxyObject>()) {
    659      markIndicesUnsupported();
    660      if (checkForDuplicates) {
    661        if (!enumerateProxyProperties<true>(cx)) {
    662          return false;
    663        }
    664      } else {
    665        if (!enumerateProxyProperties<false>(cx)) {
    666          return false;
    667        }
    668      }
    669    } else {
    670      MOZ_CRASH("non-native objects must have an enumerate op");
    671    }
    672 
    673    if (!enumeratingProtoChain_) {
    674      ownPropertyCount_ = props_.length();
    675    }
    676 
    677    if (flags_ & JSITER_OWNONLY) {
    678      break;
    679    }
    680 
    681    if (!GetPrototype(cx, obj_, &obj_)) {
    682      return false;
    683    }
    684    enumeratingProtoChain_ = true;
    685 
    686    // The [[Prototype]] chain might be cyclic.
    687    if (!CheckForInterrupt(cx)) {
    688      return false;
    689    }
    690  } while (obj_ != nullptr);
    691 
    692 #ifdef DEBUG
    693  if (js::SupportDifferentialTesting() && !supportsIndices()) {
    694    /*
    695     * In some cases the enumeration order for an object depends on the
    696     * execution mode (interpreter vs. JIT), especially for native objects
    697     * with a class enumerate hook (where resolving a property changes the
    698     * resulting enumeration order). These aren't really bugs, but the
    699     * differences can change the generated output and confuse correctness
    700     * fuzzers, so we sort the ids if such a fuzzer is running.
    701     *
    702     * We don't do this in the general case because (a) doing so is slow,
    703     * and (b) it also breaks the web, which expects enumeration order to
    704     * follow the order in which properties are added, in certain cases.
    705     * Since ECMA does not specify an enumeration order for objects, both
    706     * behaviors are technically correct to do.
    707     */
    708 
    709    jsid* ids = props_.begin();
    710    size_t n = props_.length();
    711 
    712    RootedIdVector tmp(cx);
    713    if (!tmp.resize(n)) {
    714      return false;
    715    }
    716    PodCopy(tmp.begin(), ids, n);
    717 
    718    if (!MergeSort(ids, n, tmp.begin(), SortComparatorIds(cx))) {
    719      return false;
    720    }
    721  }
    722 #endif
    723 
    724  return true;
    725 }
    726 
    727 JS_PUBLIC_API bool js::GetPropertyKeys(JSContext* cx, HandleObject obj,
    728                                       unsigned flags,
    729                                       MutableHandleIdVector props) {
    730  uint32_t validFlags =
    731      flags & (JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS |
    732               JSITER_SYMBOLSONLY | JSITER_PRIVATE);
    733 
    734  PropertyEnumerator enumerator(cx, obj, validFlags, props);
    735  return enumerator.snapshot(cx);
    736 }
    737 
    738 static inline void RegisterEnumerator(JSContext* cx, NativeIterator* ni,
    739                                      HandleObject obj) {
    740  ni->initObjectBeingIterated(*obj);
    741 
    742  // Register non-escaping native enumerators (for-in) with the current
    743  // context.
    744  ni->link(cx->compartment()->enumeratorsAddr());
    745 
    746  MOZ_ASSERT(!ni->isActive());
    747  ni->markActive();
    748 }
    749 
    750 static PropertyIteratorObject* NewPropertyIteratorObject(JSContext* cx) {
    751  const JSClass* clasp = &PropertyIteratorObject::class_;
    752  Rooted<SharedShape*> shape(
    753      cx,
    754      SharedShape::getInitialShape(cx, clasp, cx->realm(), TaggedProto(nullptr),
    755                                   ITERATOR_FINALIZE_KIND));
    756  if (!shape) {
    757    return nullptr;
    758  }
    759 
    760  auto* res = NativeObject::create<PropertyIteratorObject>(
    761      cx, ITERATOR_FINALIZE_KIND, GetInitialHeap(GenericObject, clasp), shape);
    762  if (!res) {
    763    return nullptr;
    764  }
    765 
    766  // CodeGenerator::visitIteratorStartO assumes the iterator object is not
    767  // inside the nursery when deciding whether a barrier is necessary.
    768  MOZ_ASSERT(!js::gc::IsInsideNursery(res));
    769  return res;
    770 }
    771 
    772 static inline size_t NumTrailingBytes(size_t propertyCount,
    773                                      size_t protoShapeCount, bool hasIndices) {
    774  static_assert(alignof(IteratorProperty) <= alignof(NativeIterator));
    775  static_assert(alignof(GCPtr<Shape*>) <= alignof(IteratorProperty));
    776  static_assert(alignof(PropertyIndex) <= alignof(GCPtr<Shape*>));
    777  size_t result = propertyCount * sizeof(IteratorProperty) +
    778                  protoShapeCount * sizeof(GCPtr<Shape*>);
    779  if (hasIndices) {
    780    result += propertyCount * sizeof(PropertyIndex);
    781  }
    782  return result;
    783 }
    784 
    785 static inline size_t AllocationSize(size_t propertyCount,
    786                                    size_t protoShapeCount, bool hasIndices) {
    787  return sizeof(NativeIterator) +
    788         NumTrailingBytes(propertyCount, protoShapeCount, hasIndices);
    789 }
    790 
    791 static PropertyIteratorObject* CreatePropertyIterator(
    792    JSContext* cx, Handle<JSObject*> objBeingIterated, HandleIdVector props,
    793    bool supportsIndices, PropertyIndexVector* indices,
    794    uint32_t cacheableProtoChainLength, uint32_t ownPropertyCount,
    795    bool forObjectKeys) {
    796  MOZ_ASSERT_IF(indices, supportsIndices);
    797  if (props.length() >= NativeIterator::PropCountLimit) {
    798    ReportAllocationOverflow(cx);
    799    return nullptr;
    800  }
    801 
    802  bool hasIndices = !!indices;
    803 
    804  // If the iterator is cacheable, we store the shape of each object
    805  // along the proto chain in the iterator. If the iterator is not
    806  // cacheable, but has indices, then we store one shape (the shape of
    807  // the object being iterated.)
    808  uint32_t numShapes = cacheableProtoChainLength;
    809  if (numShapes == 0 && hasIndices) {
    810    numShapes = 1;
    811  }
    812  if (numShapes > NativeIterator::ShapeCountLimit) {
    813    ReportAllocationOverflow(cx);
    814    return nullptr;
    815  }
    816  uint32_t numProtoShapes = numShapes > 0 ? numShapes - 1 : 0;
    817 
    818  Rooted<PropertyIteratorObject*> propIter(cx, NewPropertyIteratorObject(cx));
    819  if (!propIter) {
    820    return nullptr;
    821  }
    822 
    823  void* mem = cx->pod_malloc_with_extra<NativeIterator, uint8_t>(
    824      NumTrailingBytes(props.length(), numProtoShapes, hasIndices));
    825  if (!mem) {
    826    return nullptr;
    827  }
    828 
    829  // This also registers |ni| with |propIter|.
    830  bool hadError = false;
    831  new (mem) NativeIterator(cx, propIter, objBeingIterated, props,
    832                           supportsIndices, indices, numShapes,
    833                           ownPropertyCount, forObjectKeys, &hadError);
    834  if (hadError) {
    835    return nullptr;
    836  }
    837 
    838  return propIter;
    839 }
    840 
    841 static HashNumber HashIteratorShape(Shape* shape) {
    842  return DefaultHasher<Shape*>::hash(shape);
    843 }
    844 
    845 /**
    846 * Initialize a fresh NativeIterator.
    847 *
    848 * This definition is a bit tricky: some parts of initializing are fallible, so
    849 * as we initialize, we must carefully keep this in GC-safe state (see
    850 * NativeIterator::trace).
    851 */
    852 NativeIterator::NativeIterator(JSContext* cx,
    853                               Handle<PropertyIteratorObject*> propIter,
    854                               Handle<JSObject*> objBeingIterated,
    855                               HandleIdVector props, bool supportsIndices,
    856                               PropertyIndexVector* indices, uint32_t numShapes,
    857                               uint32_t ownPropertyCount, bool forObjectKeys,
    858                               bool* hadError)
    859    : objectBeingIterated_(nullptr),
    860      iterObj_(propIter),
    861      objShape_(numShapes > 0 ? objBeingIterated->shape() : nullptr),
    862      // This holds the allocated property count until we're done with
    863      // initialization
    864      propertyCursor_(props.length()),
    865      ownPropertyCount_(ownPropertyCount),
    866      shapesHash_(0) {
    867  // If there are shapes, the object and all objects on its prototype chain must
    868  // be native objects. See CanCompareIterableObjectToCache.
    869  MOZ_ASSERT_IF(numShapes > 0,
    870                objBeingIterated && objBeingIterated->is<NativeObject>());
    871 
    872  MOZ_ASSERT(!*hadError);
    873 
    874  bool hasActualIndices = !!indices;
    875  MOZ_ASSERT_IF(hasActualIndices, indices->length() == props.length());
    876 
    877  if (hasActualIndices) {
    878    flags_ |= Flags::IndicesAllocated;
    879  } else if (supportsIndices) {
    880    flags_ |= Flags::IndicesSupported;
    881  }
    882 
    883  if (forObjectKeys) {
    884    flags_ |= Flags::OwnPropertiesOnly;
    885  }
    886 
    887  // NOTE: This must be done first thing: The caller can't free `this` on error
    888  //       because it has GCPtr fields whose barriers have already fired; the
    889  //       store buffer has pointers to them. Only the GC can free `this` (via
    890  //       PropertyIteratorObject::finalize).
    891  propIter->initNativeIterator(this);
    892 
    893  // The GC asserts on finalization that `this->allocationSize()` matches the
    894  // `nbytes` passed to `AddCellMemory`. So once these lines run, we must make
    895  // `this->allocationSize()` correct. That means infallibly initializing the
    896  // shapes, and ensuring that indicesState_.allocated() is true if we've
    897  // allocated space for indices. It's OK for the constructor to fail after
    898  // that.
    899  size_t nbytes = AllocationSize(
    900      props.length(), numShapes > 0 ? numShapes - 1 : 0, hasActualIndices);
    901  AddCellMemory(propIter, nbytes, MemoryUse::NativeIterator);
    902 
    903  if (numShapes > 0) {
    904    // Construct shapes into the shapes array.  Also compute the shapesHash,
    905    // which incorporates Shape* addresses that could have changed during a GC
    906    // triggered in (among other places) |IdToString| above.
    907    JSObject* pobj = objBeingIterated;
    908    HashNumber shapesHash = 0;
    909    for (uint32_t i = 0; i < numShapes; i++) {
    910      MOZ_ASSERT(pobj->is<NativeObject>());
    911      Shape* shape = pobj->shape();
    912      if (i > 0) {
    913        new (protoShapesEnd()) GCPtr<Shape*>(shape);
    914        protoShapeCount_++;
    915      }
    916      shapesHash = mozilla::AddToHash(shapesHash, HashIteratorShape(shape));
    917      pobj = pobj->staticPrototype();
    918    }
    919    shapesHash_ = shapesHash;
    920 
    921    // There are two cases in which we need to store shapes. If this
    922    // iterator is cacheable, we store the shapes for the entire proto
    923    // chain so we can check that the cached iterator is still valid
    924    // (see MacroAssembler::maybeLoadIteratorFromShape). If this iterator
    925    // has indices, then even if it isn't cacheable we need to store the
    926    // shape of the iterated object itself (see IteratorHasIndicesAndBranch).
    927    // In the former case, assert that we're storing the entire proto chain.
    928    MOZ_ASSERT_IF(numShapes > 1, pobj == nullptr);
    929    MOZ_ASSERT(uintptr_t(protoShapesEnd()) == uintptr_t(this) + nbytes);
    930  }
    931 
    932  // Allocate any strings in the nursery until the first minor GC. After this
    933  // point they will end up getting tenured anyway because they are reachable
    934  // from |propIter| which will be tenured.
    935  AutoSelectGCHeap gcHeap(cx);
    936 
    937  bool maybeNeedGC = !gc::IsInsideNursery(propIter);
    938  uint64_t gcNumber = cx->runtime()->gc.gcNumber();
    939  size_t numProps = props.length();
    940  for (size_t i = 0; i < numProps; i++) {
    941    JSLinearString* str = IdToString(cx, props[i], gcHeap);
    942    if (!str) {
    943      *hadError = true;
    944      return;
    945    }
    946    uint64_t newGcNumber = cx->runtime()->gc.gcNumber();
    947    if (newGcNumber != gcNumber) {
    948      gcNumber = newGcNumber;
    949      maybeNeedGC = true;
    950    }
    951    // We write to our IteratorProperty children only here and in
    952    // PropertyIteratorObject::trace. Here we do not need a pre-barrier
    953    // because we are not overwriting a previous value.
    954    new (propertiesEnd()) IteratorProperty(str);
    955    propertyCount_++;
    956    if (maybeNeedGC && gc::IsInsideNursery(str)) {
    957      maybeNeedGC = false;
    958      cx->runtime()->gc.storeBuffer().putWholeCell(propIter);
    959    }
    960  }
    961 
    962  if (hasActualIndices) {
    963    PropertyIndex* cursor = indicesBegin();
    964    for (size_t i = 0; i < numProps; i++) {
    965      *cursor++ = (*indices)[i];
    966    }
    967    flags_ |= Flags::IndicesAvailable;
    968  }
    969 
    970  propertyCursor_ = 0;
    971  flags_ |= Flags::Initialized;
    972 
    973  MOZ_ASSERT(!*hadError);
    974 }
    975 
    976 inline size_t NativeIterator::allocationSize() const {
    977  return AllocationSize(allocatedPropertyCount(), protoShapeCount_,
    978                        indicesAllocated());
    979 }
    980 
    981 /* static */
    982 bool IteratorHashPolicy::match(PropertyIteratorObject* obj,
    983                               const Lookup& lookup) {
    984  NativeIterator* ni = obj->getNativeIterator();
    985  if (ni->shapesHash() != lookup.shapesHash ||
    986      ni->protoShapeCount() != lookup.numProtoShapes ||
    987      ni->objShape() != lookup.objShape) {
    988    return false;
    989  }
    990 
    991  return ArrayEqual(reinterpret_cast<Shape**>(ni->protoShapesBegin()),
    992                    lookup.protoShapes, ni->protoShapeCount());
    993 }
    994 
    995 static inline bool CanCompareIterableObjectToCache(JSObject* obj) {
    996  if (obj->is<NativeObject>()) {
    997    return obj->as<NativeObject>().getDenseInitializedLength() == 0;
    998  }
    999  return false;
   1000 }
   1001 
   1002 static bool CanStoreInIteratorCache(JSObject* obj) {
   1003  do {
   1004    MOZ_ASSERT(obj->as<NativeObject>().getDenseInitializedLength() == 0);
   1005 
   1006    // Typed arrays have indexed properties not captured by the Shape guard.
   1007    // Enumerate hooks may add extra properties.
   1008    if (MOZ_UNLIKELY(ClassCanHaveExtraEnumeratedProperties(obj->getClass()))) {
   1009      return false;
   1010    }
   1011 
   1012    obj = obj->staticPrototype();
   1013  } while (obj);
   1014 
   1015  return true;
   1016 }
   1017 
   1018 static MOZ_ALWAYS_INLINE PropertyIteratorObject* LookupInShapeIteratorCache(
   1019    JSContext* cx, JSObject* obj, uint32_t* cacheableProtoChainLength,
   1020    bool exclusive) {
   1021  if (!obj->shape()->cache().isIterator() ||
   1022      !CanCompareIterableObjectToCache(obj)) {
   1023    return nullptr;
   1024  }
   1025  PropertyIteratorObject* iterobj = obj->shape()->cache().toIterator();
   1026  NativeIterator* ni = iterobj->getNativeIterator();
   1027  MOZ_ASSERT(ni->objShape() == obj->shape());
   1028  if (exclusive && !ni->isReusable()) {
   1029    return nullptr;
   1030  }
   1031 
   1032  // Verify shapes of proto chain.
   1033  JSObject* pobj = obj;
   1034  for (GCPtr<Shape*>* s = ni->protoShapesBegin(); s != ni->protoShapesEnd();
   1035       s++) {
   1036    Shape* shape = *s;
   1037    pobj = pobj->staticPrototype();
   1038    if (pobj->shape() != shape) {
   1039      return nullptr;
   1040    }
   1041    if (!CanCompareIterableObjectToCache(pobj)) {
   1042      return nullptr;
   1043    }
   1044  }
   1045  MOZ_ASSERT(CanStoreInIteratorCache(obj));
   1046  *cacheableProtoChainLength = ni->objShape() ? ni->protoShapeCount() + 1 : 0;
   1047  return iterobj;
   1048 }
   1049 
   1050 static MOZ_ALWAYS_INLINE PropertyIteratorObject* LookupInIteratorCache(
   1051    JSContext* cx, JSObject* obj, uint32_t* cacheableProtoChainLength,
   1052    bool exclusive) {
   1053  MOZ_ASSERT(*cacheableProtoChainLength == 0);
   1054 
   1055  if (PropertyIteratorObject* shapeCached = LookupInShapeIteratorCache(
   1056          cx, obj, cacheableProtoChainLength, exclusive)) {
   1057    return shapeCached;
   1058  }
   1059 
   1060  Vector<Shape*, 8> shapes(cx);
   1061  HashNumber shapesHash = 0;
   1062  JSObject* pobj = obj;
   1063  do {
   1064    if (!CanCompareIterableObjectToCache(pobj)) {
   1065      return nullptr;
   1066    }
   1067 
   1068    MOZ_ASSERT(pobj->is<NativeObject>());
   1069    Shape* shape = pobj->shape();
   1070    shapesHash = mozilla::AddToHash(shapesHash, HashIteratorShape(shape));
   1071 
   1072    if (MOZ_UNLIKELY(!shapes.append(shape))) {
   1073      cx->recoverFromOutOfMemory();
   1074      return nullptr;
   1075    }
   1076 
   1077    pobj = pobj->staticPrototype();
   1078  } while (pobj);
   1079 
   1080  MOZ_ASSERT(!shapes.empty());
   1081  *cacheableProtoChainLength = shapes.length();
   1082 
   1083  IteratorHashPolicy::Lookup lookup(shapes[0], shapes.begin() + 1,
   1084                                    shapes.length() - 1, shapesHash);
   1085  auto p = ObjectRealm::get(obj).iteratorCache.lookup(lookup);
   1086  if (!p) {
   1087    return nullptr;
   1088  }
   1089 
   1090  PropertyIteratorObject* iterobj = *p;
   1091  MOZ_ASSERT(iterobj->compartment() == cx->compartment());
   1092 
   1093  NativeIterator* ni = iterobj->getNativeIterator();
   1094  if (exclusive && !ni->isReusable()) {
   1095    return nullptr;
   1096  }
   1097 
   1098  return iterobj;
   1099 }
   1100 
   1101 [[nodiscard]] static bool StoreInIteratorCache(
   1102    JSContext* cx, JSObject* obj, PropertyIteratorObject* iterobj) {
   1103  MOZ_ASSERT(CanStoreInIteratorCache(obj));
   1104 
   1105  NativeIterator* ni = iterobj->getNativeIterator();
   1106  MOZ_ASSERT(ni->objShape());
   1107 
   1108  obj->shape()->maybeCacheIterator(cx, iterobj);
   1109 
   1110  IteratorHashPolicy::Lookup lookup(
   1111      ni->objShape(), reinterpret_cast<Shape**>(ni->protoShapesBegin()),
   1112      ni->protoShapeCount(), ni->shapesHash());
   1113 
   1114  ObjectRealm::IteratorCache& cache = ObjectRealm::get(obj).iteratorCache;
   1115  bool ok;
   1116  auto p = cache.lookupForAdd(lookup);
   1117  if (MOZ_LIKELY(!p)) {
   1118    ok = cache.add(p, iterobj);
   1119  } else {
   1120    // If we weren't able to use an existing cached iterator, just
   1121    // replace it.
   1122    cache.remove(p);
   1123    ok = cache.relookupOrAdd(p, lookup, iterobj);
   1124  }
   1125  if (!ok) {
   1126    ReportOutOfMemory(cx);
   1127    return false;
   1128  }
   1129 
   1130  return true;
   1131 }
   1132 
   1133 bool js::EnumerateProperties(JSContext* cx, HandleObject obj,
   1134                             MutableHandleIdVector props) {
   1135  MOZ_ASSERT(props.empty());
   1136 
   1137  if (MOZ_UNLIKELY(obj->is<ProxyObject>())) {
   1138    return Proxy::enumerate(cx, obj, props);
   1139  }
   1140 
   1141  uint32_t flags = 0;
   1142  PropertyEnumerator enumerator(cx, obj, flags, props);
   1143  return enumerator.snapshot(cx);
   1144 }
   1145 
   1146 #ifdef DEBUG
   1147 static bool IndicesAreValid(NativeObject* obj, NativeIterator* ni) {
   1148  MOZ_ASSERT(ni->indicesAvailable());
   1149  size_t numDenseElements = obj->getDenseInitializedLength();
   1150  size_t numFixedSlots = obj->numFixedSlots();
   1151  const Value* elements = obj->getDenseElements();
   1152 
   1153  IteratorProperty* keys = ni->propertiesBegin();
   1154  PropertyIndex* indices = ni->indicesBegin();
   1155 
   1156  for (uint32_t i = 0; i < ni->numKeys(); i++) {
   1157    PropertyIndex index = indices[i];
   1158    switch (index.kind()) {
   1159      case PropertyIndex::Kind::Element:
   1160        // Verify that the dense element exists and is not a hole.
   1161        if (index.index() >= numDenseElements ||
   1162            elements[index.index()].isMagic(JS_ELEMENTS_HOLE)) {
   1163          return false;
   1164        }
   1165        break;
   1166      case PropertyIndex::Kind::FixedSlot: {
   1167        // Verify that the slot exists and is an enumerable data property with
   1168        // the expected key.
   1169        Maybe<PropertyInfo> prop =
   1170            obj->lookupPure(AtomToId(&keys[i].asString()->asAtom()));
   1171        if (!prop.isSome() || !prop->hasSlot() || !prop->enumerable() ||
   1172            !prop->isDataProperty() || prop->slot() != index.index()) {
   1173          return false;
   1174        }
   1175        break;
   1176      }
   1177      case PropertyIndex::Kind::DynamicSlot: {
   1178        // Verify that the slot exists and is an enumerable data property with
   1179        // the expected key.
   1180        Maybe<PropertyInfo> prop =
   1181            obj->lookupPure(AtomToId(&keys[i].asString()->asAtom()));
   1182        if (!prop.isSome() || !prop->hasSlot() || !prop->enumerable() ||
   1183            !prop->isDataProperty() ||
   1184            prop->slot() - numFixedSlots != index.index()) {
   1185          return false;
   1186        }
   1187        break;
   1188      }
   1189      case PropertyIndex::Kind::Invalid:
   1190        return false;
   1191    }
   1192  }
   1193  return true;
   1194 }
   1195 #endif
   1196 
   1197 static PropertyIteratorObject* GetIteratorImpl(JSContext* cx, HandleObject obj,
   1198                                               bool wantIndices,
   1199                                               bool forObjectKeys) {
   1200  MOZ_ASSERT(!obj->is<PropertyIteratorObject>());
   1201  MOZ_ASSERT(cx->compartment() == obj->compartment(),
   1202             "We may end up allocating shapes in the wrong zone!");
   1203 
   1204  uint32_t cacheableProtoChainLength = 0;
   1205  if (PropertyIteratorObject* iterobj = LookupInIteratorCache(
   1206          cx, obj, &cacheableProtoChainLength, !forObjectKeys)) {
   1207    NativeIterator* ni = iterobj->getNativeIterator();
   1208    bool recreateWithIndices = wantIndices && ni->indicesSupported();
   1209    bool recreateWithProtoProperties =
   1210        !forObjectKeys && ni->ownPropertiesOnly();
   1211    if (!recreateWithIndices && !recreateWithProtoProperties) {
   1212      MOZ_ASSERT_IF(wantIndices && ni->indicesAvailable(),
   1213                    IndicesAreValid(&obj->as<NativeObject>(), ni));
   1214      if (!forObjectKeys) {
   1215        RegisterEnumerator(cx, ni, obj);
   1216      }
   1217      return iterobj;
   1218    }
   1219    // We don't want to get in a pattern where an Object.keys + indices
   1220    // use case clobbers us because it wants indices, and then we clobber
   1221    // it because we want prototype keys. This prevents that.
   1222    if (!recreateWithIndices && ni->indicesAvailable()) {
   1223      wantIndices = true;
   1224    }
   1225  }
   1226 
   1227  if (cacheableProtoChainLength > 0 && !CanStoreInIteratorCache(obj)) {
   1228    cacheableProtoChainLength = 0;
   1229  }
   1230  if (cacheableProtoChainLength > NativeIterator::ShapeCountLimit) {
   1231    cacheableProtoChainLength = 0;
   1232  }
   1233 
   1234  RootedIdVector keys(cx);
   1235  PropertyIndexVector indices(cx);
   1236  bool supportsIndices = false;
   1237  uint32_t ownPropertyCount = 0;
   1238 
   1239  if (MOZ_UNLIKELY(obj->is<ProxyObject>())) {
   1240    if (!Proxy::enumerate(cx, obj, &keys)) {
   1241      return nullptr;
   1242    }
   1243  } else {
   1244    uint32_t flags = 0;
   1245    if (forObjectKeys) {
   1246      flags |= JSITER_OWNONLY;
   1247    }
   1248    PropertyEnumerator enumerator(cx, obj, flags, &keys, &indices);
   1249    if (!enumerator.snapshot(cx)) {
   1250      return nullptr;
   1251    }
   1252    supportsIndices = enumerator.supportsIndices();
   1253    ownPropertyCount = enumerator.ownPropertyCount();
   1254    MOZ_ASSERT_IF(wantIndices && supportsIndices,
   1255                  keys.length() == indices.length());
   1256  }
   1257 
   1258  // If the object has dense elements, mark the dense elements as
   1259  // maybe-in-iteration. However if this is for Object.keys, we're not able to
   1260  // do the appropriate invalidations on deletion etc. anyway. Accordingly,
   1261  // we're forced to just disable the indices optimization for this iterator
   1262  // entirely.
   1263  //
   1264  // The iterator is a snapshot so if indexed properties are added after this
   1265  // point we don't need to do anything. However, the object might have sparse
   1266  // elements now that can be densified later. To account for this, we set the
   1267  // maybe-in-iteration flag also in NativeObject::maybeDensifySparseElements.
   1268  //
   1269  // In debug builds, AssertDenseElementsNotIterated is used to check the flag
   1270  // is set correctly.
   1271  if (obj->is<NativeObject>() &&
   1272      obj->as<NativeObject>().getDenseInitializedLength() > 0) {
   1273    if (forObjectKeys) {
   1274      supportsIndices = false;
   1275    } else {
   1276      obj->as<NativeObject>().markDenseElementsMaybeInIteration();
   1277    }
   1278  }
   1279 
   1280  PropertyIndexVector* indicesPtr =
   1281      wantIndices && supportsIndices ? &indices : nullptr;
   1282  PropertyIteratorObject* iterobj = CreatePropertyIterator(
   1283      cx, obj, keys, supportsIndices, indicesPtr, cacheableProtoChainLength,
   1284      ownPropertyCount, forObjectKeys);
   1285  if (!iterobj) {
   1286    return nullptr;
   1287  }
   1288  if (!forObjectKeys) {
   1289    RegisterEnumerator(cx, iterobj->getNativeIterator(), obj);
   1290  }
   1291 
   1292  cx->check(iterobj);
   1293  MOZ_ASSERT_IF(
   1294      wantIndices && supportsIndices,
   1295      IndicesAreValid(&obj->as<NativeObject>(), iterobj->getNativeIterator()));
   1296 
   1297 #ifdef DEBUG
   1298  if (obj->is<NativeObject>()) {
   1299    if (PrototypeMayHaveIndexedProperties(&obj->as<NativeObject>())) {
   1300      iterobj->getNativeIterator()->setMaybeHasIndexedPropertiesFromProto();
   1301    }
   1302  }
   1303 #endif
   1304 
   1305  // Cache the iterator object.
   1306  if (cacheableProtoChainLength > 0) {
   1307    if (!StoreInIteratorCache(cx, obj, iterobj)) {
   1308      return nullptr;
   1309    }
   1310  }
   1311 
   1312  return iterobj;
   1313 }
   1314 
   1315 PropertyIteratorObject* js::GetIterator(JSContext* cx, HandleObject obj) {
   1316  return GetIteratorImpl(cx, obj, false, false);
   1317 }
   1318 
   1319 PropertyIteratorObject* js::GetIteratorWithIndices(JSContext* cx,
   1320                                                   HandleObject obj) {
   1321  return GetIteratorImpl(cx, obj, true, false);
   1322 }
   1323 
   1324 PropertyIteratorObject* js::GetIteratorForObjectKeys(JSContext* cx,
   1325                                                     HandleObject obj) {
   1326  return GetIteratorImpl(cx, obj, false, true);
   1327 }
   1328 
   1329 PropertyIteratorObject* js::GetIteratorWithIndicesForObjectKeys(
   1330    JSContext* cx, HandleObject obj) {
   1331  return GetIteratorImpl(cx, obj, true, true);
   1332 }
   1333 
   1334 PropertyIteratorObject* js::LookupInIteratorCache(JSContext* cx,
   1335                                                  HandleObject obj) {
   1336  uint32_t dummy = 0;
   1337  return LookupInIteratorCache(cx, obj, &dummy, true);
   1338 }
   1339 
   1340 PropertyIteratorObject* js::LookupInShapeIteratorCache(JSContext* cx,
   1341                                                       HandleObject obj) {
   1342  uint32_t dummy = 0;
   1343  return LookupInShapeIteratorCache(cx, obj, &dummy, true);
   1344 }
   1345 
   1346 // ES 2017 draft 7.4.7.
   1347 PlainObject* js::CreateIterResultObject(JSContext* cx, HandleValue value,
   1348                                        bool done) {
   1349  // Step 1 (implicit).
   1350 
   1351  // Step 2.
   1352  Rooted<PlainObject*> templateObject(
   1353      cx, GlobalObject::getOrCreateIterResultTemplateObject(cx));
   1354  if (!templateObject) {
   1355    return nullptr;
   1356  }
   1357 
   1358  PlainObject* resultObj = PlainObject::createWithTemplate(cx, templateObject);
   1359  if (!resultObj) {
   1360    return nullptr;
   1361  }
   1362 
   1363  // Step 3.
   1364  resultObj->setSlot(GlobalObject::IterResultObjectValueSlot, value);
   1365 
   1366  // Step 4.
   1367  resultObj->setSlot(GlobalObject::IterResultObjectDoneSlot,
   1368                     done ? TrueHandleValue : FalseHandleValue);
   1369 
   1370  // Step 5.
   1371  return resultObj;
   1372 }
   1373 
   1374 PlainObject* GlobalObject::getOrCreateIterResultTemplateObject(JSContext* cx) {
   1375  GCPtr<PlainObject*>& obj = cx->global()->data().iterResultTemplate;
   1376  if (obj) {
   1377    return obj;
   1378  }
   1379 
   1380  PlainObject* templateObj =
   1381      createIterResultTemplateObject(cx, WithObjectPrototype::Yes);
   1382  obj.init(templateObj);
   1383  return obj;
   1384 }
   1385 
   1386 /* static */
   1387 PlainObject* GlobalObject::getOrCreateIterResultWithoutPrototypeTemplateObject(
   1388    JSContext* cx) {
   1389  GCPtr<PlainObject*>& obj =
   1390      cx->global()->data().iterResultWithoutPrototypeTemplate;
   1391  if (obj) {
   1392    return obj;
   1393  }
   1394 
   1395  PlainObject* templateObj =
   1396      createIterResultTemplateObject(cx, WithObjectPrototype::No);
   1397  obj.init(templateObj);
   1398  return obj;
   1399 }
   1400 
   1401 /* static */
   1402 PlainObject* GlobalObject::createIterResultTemplateObject(
   1403    JSContext* cx, WithObjectPrototype withProto) {
   1404  // Create template plain object
   1405  Rooted<PlainObject*> templateObject(
   1406      cx, withProto == WithObjectPrototype::Yes
   1407              ? NewPlainObject(cx, TenuredObject)
   1408              : NewPlainObjectWithProto(cx, nullptr));
   1409  if (!templateObject) {
   1410    return nullptr;
   1411  }
   1412 
   1413  // Set dummy `value` property
   1414  if (!NativeDefineDataProperty(cx, templateObject, cx->names().value,
   1415                                UndefinedHandleValue, JSPROP_ENUMERATE)) {
   1416    return nullptr;
   1417  }
   1418 
   1419  // Set dummy `done` property
   1420  if (!NativeDefineDataProperty(cx, templateObject, cx->names().done,
   1421                                TrueHandleValue, JSPROP_ENUMERATE)) {
   1422    return nullptr;
   1423  }
   1424 
   1425 #ifdef DEBUG
   1426  // Make sure that the properties are in the right slots.
   1427  ShapePropertyIter<NoGC> iter(templateObject->shape());
   1428  MOZ_ASSERT(iter->slot() == GlobalObject::IterResultObjectDoneSlot &&
   1429             iter->key() == NameToId(cx->names().done));
   1430  iter++;
   1431  MOZ_ASSERT(iter->slot() == GlobalObject::IterResultObjectValueSlot &&
   1432             iter->key() == NameToId(cx->names().value));
   1433 #endif
   1434 
   1435  return templateObject;
   1436 }
   1437 
   1438 /*** Iterator objects *******************************************************/
   1439 
   1440 size_t PropertyIteratorObject::sizeOfMisc(
   1441    mozilla::MallocSizeOf mallocSizeOf) const {
   1442  return mallocSizeOf(getNativeIterator());
   1443 }
   1444 
   1445 void PropertyIteratorObject::trace(JSTracer* trc, JSObject* obj) {
   1446  if (NativeIterator* ni =
   1447          obj->as<PropertyIteratorObject>().getNativeIterator()) {
   1448    ni->trace(trc);
   1449  }
   1450 }
   1451 
   1452 void PropertyIteratorObject::finalize(JS::GCContext* gcx, JSObject* obj) {
   1453  if (NativeIterator* ni =
   1454          obj->as<PropertyIteratorObject>().getNativeIterator()) {
   1455    gcx->free_(obj, ni, ni->allocationSize(), MemoryUse::NativeIterator);
   1456  }
   1457 }
   1458 
   1459 const JSClassOps PropertyIteratorObject::classOps_ = {
   1460    nullptr,   // addProperty
   1461    nullptr,   // delProperty
   1462    nullptr,   // enumerate
   1463    nullptr,   // newEnumerate
   1464    nullptr,   // resolve
   1465    nullptr,   // mayResolve
   1466    finalize,  // finalize
   1467    nullptr,   // call
   1468    nullptr,   // construct
   1469    trace,     // trace
   1470 };
   1471 
   1472 const JSClass PropertyIteratorObject::class_ = {
   1473    "Iterator",
   1474    JSCLASS_HAS_RESERVED_SLOTS(SlotCount) | JSCLASS_BACKGROUND_FINALIZE,
   1475    &PropertyIteratorObject::classOps_,
   1476 };
   1477 
   1478 static const JSClass ArrayIteratorPrototypeClass = {
   1479    "Array Iterator",
   1480    0,
   1481 };
   1482 
   1483 enum {
   1484  ArrayIteratorSlotIteratedObject,
   1485  ArrayIteratorSlotNextIndex,
   1486  ArrayIteratorSlotItemKind,
   1487  ArrayIteratorSlotCount
   1488 };
   1489 // Slot numbers must match constants used in self-hosted code.
   1490 static_assert(ArrayIteratorSlotIteratedObject == ITERATOR_SLOT_TARGET);
   1491 static_assert(ArrayIteratorSlotNextIndex == ITERATOR_SLOT_NEXT_INDEX);
   1492 static_assert(ArrayIteratorSlotItemKind == ARRAY_ITERATOR_SLOT_ITEM_KIND);
   1493 
   1494 const JSClass ArrayIteratorObject::class_ = {
   1495    "Array Iterator",
   1496    JSCLASS_HAS_RESERVED_SLOTS(ArrayIteratorSlotCount),
   1497 };
   1498 
   1499 ArrayIteratorObject* js::NewArrayIteratorTemplate(JSContext* cx) {
   1500  RootedObject proto(
   1501      cx, GlobalObject::getOrCreateArrayIteratorPrototype(cx, cx->global()));
   1502  if (!proto) {
   1503    return nullptr;
   1504  }
   1505 
   1506  return NewTenuredObjectWithGivenProto<ArrayIteratorObject>(cx, proto);
   1507 }
   1508 
   1509 ArrayIteratorObject* js::NewArrayIterator(JSContext* cx) {
   1510  RootedObject proto(
   1511      cx, GlobalObject::getOrCreateArrayIteratorPrototype(cx, cx->global()));
   1512  if (!proto) {
   1513    return nullptr;
   1514  }
   1515 
   1516  return NewObjectWithGivenProto<ArrayIteratorObject>(cx, proto);
   1517 }
   1518 
   1519 static const JSFunctionSpec array_iterator_methods[] = {
   1520    JS_SELF_HOSTED_FN("next", "ArrayIteratorNext", 0, 0),
   1521    JS_FS_END,
   1522 };
   1523 
   1524 static const JSClass StringIteratorPrototypeClass = {
   1525    "String Iterator",
   1526    0,
   1527 };
   1528 
   1529 enum {
   1530  StringIteratorSlotIteratedObject,
   1531  StringIteratorSlotNextIndex,
   1532  StringIteratorSlotCount
   1533 };
   1534 // Slot numbers must match constants used in self-hosted code.
   1535 static_assert(StringIteratorSlotIteratedObject == ITERATOR_SLOT_TARGET);
   1536 static_assert(StringIteratorSlotNextIndex == ITERATOR_SLOT_NEXT_INDEX);
   1537 
   1538 const JSClass StringIteratorObject::class_ = {
   1539    "String Iterator",
   1540    JSCLASS_HAS_RESERVED_SLOTS(StringIteratorSlotCount),
   1541 };
   1542 
   1543 static const JSFunctionSpec string_iterator_methods[] = {
   1544    JS_SELF_HOSTED_FN("next", "StringIteratorNext", 0, 0),
   1545    JS_FS_END,
   1546 };
   1547 
   1548 StringIteratorObject* js::NewStringIteratorTemplate(JSContext* cx) {
   1549  RootedObject proto(
   1550      cx, GlobalObject::getOrCreateStringIteratorPrototype(cx, cx->global()));
   1551  if (!proto) {
   1552    return nullptr;
   1553  }
   1554 
   1555  return NewTenuredObjectWithGivenProto<StringIteratorObject>(cx, proto);
   1556 }
   1557 
   1558 StringIteratorObject* js::NewStringIterator(JSContext* cx) {
   1559  RootedObject proto(
   1560      cx, GlobalObject::getOrCreateStringIteratorPrototype(cx, cx->global()));
   1561  if (!proto) {
   1562    return nullptr;
   1563  }
   1564 
   1565  return NewObjectWithGivenProto<StringIteratorObject>(cx, proto);
   1566 }
   1567 
   1568 static const JSClass RegExpStringIteratorPrototypeClass = {
   1569    "RegExp String Iterator",
   1570    0,
   1571 };
   1572 
   1573 enum {
   1574  // The regular expression used for iteration. May hold the original RegExp
   1575  // object when it is reused instead of a new RegExp object.
   1576  RegExpStringIteratorSlotRegExp,
   1577 
   1578  // The String value being iterated upon.
   1579  RegExpStringIteratorSlotString,
   1580 
   1581  // The source string of the original RegExp object. Used to validate we can
   1582  // reuse the original RegExp object for matching.
   1583  RegExpStringIteratorSlotSource,
   1584 
   1585  // The flags of the original RegExp object.
   1586  RegExpStringIteratorSlotFlags,
   1587 
   1588  // When non-negative, this slot holds the current lastIndex position when
   1589  // reusing the original RegExp object for matching. When set to |-1|, the
   1590  // iterator has finished. When set to any other negative value, the
   1591  // iterator is not yet exhausted and we're not on the fast path and we're
   1592  // not reusing the input RegExp object.
   1593  RegExpStringIteratorSlotLastIndex,
   1594 
   1595  RegExpStringIteratorSlotCount
   1596 };
   1597 
   1598 static_assert(RegExpStringIteratorSlotRegExp ==
   1599                  REGEXP_STRING_ITERATOR_REGEXP_SLOT,
   1600              "RegExpStringIteratorSlotRegExp must match self-hosting define "
   1601              "for regexp slot.");
   1602 static_assert(RegExpStringIteratorSlotString ==
   1603                  REGEXP_STRING_ITERATOR_STRING_SLOT,
   1604              "RegExpStringIteratorSlotString must match self-hosting define "
   1605              "for string slot.");
   1606 static_assert(RegExpStringIteratorSlotSource ==
   1607                  REGEXP_STRING_ITERATOR_SOURCE_SLOT,
   1608              "RegExpStringIteratorSlotString must match self-hosting define "
   1609              "for source slot.");
   1610 static_assert(RegExpStringIteratorSlotFlags ==
   1611                  REGEXP_STRING_ITERATOR_FLAGS_SLOT,
   1612              "RegExpStringIteratorSlotFlags must match self-hosting define "
   1613              "for flags slot.");
   1614 static_assert(RegExpStringIteratorSlotLastIndex ==
   1615                  REGEXP_STRING_ITERATOR_LASTINDEX_SLOT,
   1616              "RegExpStringIteratorSlotLastIndex must match self-hosting "
   1617              "define for lastIndex slot.");
   1618 
   1619 const JSClass RegExpStringIteratorObject::class_ = {
   1620    "RegExp String Iterator",
   1621    JSCLASS_HAS_RESERVED_SLOTS(RegExpStringIteratorSlotCount),
   1622 };
   1623 
   1624 static const JSFunctionSpec regexp_string_iterator_methods[] = {
   1625    JS_SELF_HOSTED_FN("next", "RegExpStringIteratorNext", 0, 0),
   1626 
   1627    JS_FS_END,
   1628 };
   1629 
   1630 RegExpStringIteratorObject* js::NewRegExpStringIteratorTemplate(JSContext* cx) {
   1631  RootedObject proto(cx, GlobalObject::getOrCreateRegExpStringIteratorPrototype(
   1632                             cx, cx->global()));
   1633  if (!proto) {
   1634    return nullptr;
   1635  }
   1636 
   1637  return NewTenuredObjectWithGivenProto<RegExpStringIteratorObject>(cx, proto);
   1638 }
   1639 
   1640 RegExpStringIteratorObject* js::NewRegExpStringIterator(JSContext* cx) {
   1641  RootedObject proto(cx, GlobalObject::getOrCreateRegExpStringIteratorPrototype(
   1642                             cx, cx->global()));
   1643  if (!proto) {
   1644    return nullptr;
   1645  }
   1646 
   1647  return NewObjectWithGivenProto<RegExpStringIteratorObject>(cx, proto);
   1648 }
   1649 
   1650 #ifdef NIGHTLY_BUILD
   1651 static const JSClass IteratorRangePrototypeClass = {
   1652    "Numeric Range Iterator",
   1653    0,
   1654 };
   1655 
   1656 enum {
   1657  IteratorRangeSlotStart,
   1658  IteratorRangeSlotEnd,
   1659  IteratorRangeSlotStep,
   1660  IteratorRangeSlotInclusiveEnd,
   1661  IteratorRangeSlotZero,
   1662  IteratorRangeSlotOne,
   1663  IteratorRangeSlotCurrentCount,
   1664  IteratorRangeSlotCount
   1665 };
   1666 
   1667 // slot numbers must match constants used in self-hosted code
   1668 static_assert(IteratorRangeSlotStart == ITERATOR_RANGE_SLOT_START);
   1669 static_assert(IteratorRangeSlotEnd == ITERATOR_RANGE_SLOT_END);
   1670 static_assert(IteratorRangeSlotStep == ITERATOR_RANGE_SLOT_STEP);
   1671 static_assert(IteratorRangeSlotInclusiveEnd ==
   1672              ITERATOR_RANGE_SLOT_INCLUSIVE_END);
   1673 static_assert(IteratorRangeSlotZero == ITERATOR_RANGE_SLOT_ZERO);
   1674 static_assert(IteratorRangeSlotOne == ITERATOR_RANGE_SLOT_ONE);
   1675 static_assert(IteratorRangeSlotCurrentCount ==
   1676              ITERATOR_RANGE_SLOT_CURRENT_COUNT);
   1677 
   1678 static const JSFunctionSpec iterator_range_methods[] = {
   1679    JS_SELF_HOSTED_FN("next", "IteratorRangeNext", 0, 0),
   1680    JS_FS_END,
   1681 };
   1682 
   1683 IteratorRangeObject* js::NewIteratorRange(JSContext* cx) {
   1684  RootedObject proto(
   1685      cx, GlobalObject::getOrCreateIteratorRangePrototype(cx, cx->global()));
   1686  if (!proto) {
   1687    return nullptr;
   1688  }
   1689 
   1690  return NewObjectWithGivenProto<IteratorRangeObject>(cx, proto);
   1691 }
   1692 #endif
   1693 
   1694 // static
   1695 PropertyIteratorObject* GlobalObject::getOrCreateEmptyIterator(JSContext* cx) {
   1696  if (!cx->global()->data().emptyIterator) {
   1697    RootedIdVector props(cx);  // Empty
   1698    PropertyIteratorObject* iter =
   1699        CreatePropertyIterator(cx, nullptr, props, false, nullptr, 0, 0, false);
   1700    if (!iter) {
   1701      return nullptr;
   1702    }
   1703    iter->getNativeIterator()->markEmptyIteratorSingleton();
   1704    cx->global()->data().emptyIterator.init(iter);
   1705  }
   1706  return cx->global()->data().emptyIterator;
   1707 }
   1708 
   1709 PropertyIteratorObject* js::ValueToIterator(JSContext* cx, HandleValue vp) {
   1710  RootedObject obj(cx);
   1711  if (vp.isObject()) {
   1712    /* Common case. */
   1713    obj = &vp.toObject();
   1714  } else if (vp.isNullOrUndefined()) {
   1715    /*
   1716     * Enumerating over null and undefined gives an empty enumerator, so
   1717     * that |for (var p in <null or undefined>) <loop>;| never executes
   1718     * <loop>, per ES5 12.6.4.
   1719     */
   1720    return GlobalObject::getOrCreateEmptyIterator(cx);
   1721  } else {
   1722    obj = ToObject(cx, vp);
   1723    if (!obj) {
   1724      return nullptr;
   1725    }
   1726  }
   1727 
   1728  return GetIterator(cx, obj);
   1729 }
   1730 
   1731 void js::CloseIterator(JSObject* obj) {
   1732  if (!obj->is<PropertyIteratorObject>()) {
   1733    return;
   1734  }
   1735 
   1736  // Remove iterator from the active list, which is a stack. The shared iterator
   1737  // used for for-in with null/undefined is immutable and unlinked.
   1738 
   1739  NativeIterator* ni = obj->as<PropertyIteratorObject>().getNativeIterator();
   1740  if (ni->isEmptyIteratorSingleton()) {
   1741    return;
   1742  }
   1743 
   1744  ni->unlink();
   1745 
   1746  MOZ_ASSERT(ni->isActive());
   1747  ni->markInactive();
   1748 
   1749  ni->clearObjectBeingIterated();
   1750 
   1751  // Reset the enumerator; it may still be in the cached iterators for
   1752  // this thread and can be reused.
   1753  ni->resetPropertyCursorForReuse();
   1754 }
   1755 
   1756 bool js::IteratorCloseForException(JSContext* cx, HandleObject obj) {
   1757  MOZ_ASSERT(cx->isExceptionPending());
   1758 
   1759  // Closing an iterator is implemented as an exception.
   1760  bool isClosingGenerator = cx->isClosingGenerator();
   1761 
   1762  // Save the current exception state. This implicitly clears any pending
   1763  // exception, so it needs to happen after calling |cx->isClosingGenerator()|.
   1764  // The destructor restores the saved exception state, unless there's a new
   1765  // pending exception.
   1766  JS::AutoSaveExceptionState savedExc(cx);
   1767 
   1768  // CloseIterOperation when called with |CompletionKind::Throw| clears any
   1769  // pending exception, so the previously stored exception in |savedExc| is
   1770  // correctly restored.
   1771  // When called with |CompletionKind::Return|, pending exceptions aren't
   1772  // cleared, so the "generator closing" exception state in |savedExc| is only
   1773  // restored if there isn't a new pending exception.
   1774  auto completionKind =
   1775      isClosingGenerator ? CompletionKind::Return : CompletionKind::Throw;
   1776  return CloseIterOperation(cx, obj, completionKind);
   1777 }
   1778 
   1779 void js::UnwindIteratorForUncatchableException(JSObject* obj) {
   1780  if (obj->is<PropertyIteratorObject>()) {
   1781    NativeIterator* ni = obj->as<PropertyIteratorObject>().getNativeIterator();
   1782    if (ni->isEmptyIteratorSingleton()) {
   1783      return;
   1784    }
   1785    ni->unlink();
   1786  }
   1787 }
   1788 
   1789 static bool SuppressDeletedProperty(JSContext* cx, NativeIterator* ni,
   1790                                    HandleObject obj,
   1791                                    Handle<JSLinearString*> str) {
   1792  if (ni->objectBeingIterated() != obj) {
   1793    return true;
   1794  }
   1795 
   1796  ni->disableIndices();
   1797 
   1798  // Optimization for the following common case:
   1799  //
   1800  //    for (var p in o) {
   1801  //        delete o[p];
   1802  //    }
   1803  //
   1804  // Note that usually both strings will be atoms so we only check for pointer
   1805  // equality here.
   1806  if (ni->previousPropertyWas(str)) {
   1807    return true;
   1808  }
   1809 
   1810  // Check whether id is still to come.
   1811  Rooted<JSLinearString*> idStr(cx);
   1812  IteratorProperty* cursor = ni->nextProperty();
   1813  for (; cursor < ni->propertiesEnd(); ++cursor) {
   1814    idStr = cursor->asString();
   1815    // Common case: both strings are atoms.
   1816    if (idStr->isAtom() && str->isAtom()) {
   1817      if (idStr != str) {
   1818        continue;
   1819      }
   1820    } else {
   1821      if (!EqualStrings(idStr, str)) {
   1822        continue;
   1823      }
   1824    }
   1825 
   1826    // Check whether another property along the prototype chain became
   1827    // visible as a result of this deletion.
   1828    RootedObject proto(cx);
   1829    if (!GetPrototype(cx, obj, &proto)) {
   1830      return false;
   1831    }
   1832    if (proto) {
   1833      RootedId id(cx);
   1834      RootedValue idv(cx, StringValue(idStr));
   1835      if (!PrimitiveValueToId<CanGC>(cx, idv, &id)) {
   1836        return false;
   1837      }
   1838 
   1839      Rooted<mozilla::Maybe<PropertyDescriptor>> desc(cx);
   1840      RootedObject holder(cx);
   1841      if (!GetPropertyDescriptor(cx, proto, id, &desc, &holder)) {
   1842        return false;
   1843      }
   1844 
   1845      // If deletion just made something up the chain visible, no need to
   1846      // do anything.
   1847      if (desc.isSome() && desc->enumerable()) {
   1848        return true;
   1849      }
   1850    }
   1851 
   1852    cursor->markDeleted();
   1853    ni->markHasUnvisitedPropertyDeletion();
   1854    return true;
   1855  }
   1856 
   1857  return true;
   1858 }
   1859 
   1860 /*
   1861 * Suppress enumeration of deleted properties. This function must be called
   1862 * when a property is deleted and there might be active enumerators.
   1863 *
   1864 * We maintain a list of active non-escaping for-in enumerators. To suppress
   1865 * a property, we check whether each active enumerator contains the (obj, id)
   1866 * pair and has not yet enumerated |id|. If so, and |id| is the next property,
   1867 * we simply advance the cursor. Otherwise, we delete |id| from the list.
   1868 *
   1869 * We do not suppress enumeration of a property deleted along an object's
   1870 * prototype chain. Only direct deletions on the object are handled.
   1871 */
   1872 static bool SuppressDeletedPropertyHelper(JSContext* cx, HandleObject obj,
   1873                                          Handle<JSLinearString*> str) {
   1874  NativeIteratorListIter iter(obj->compartment()->enumeratorsAddr());
   1875  while (!iter.done()) {
   1876    NativeIterator* ni = iter.next();
   1877    if (!SuppressDeletedProperty(cx, ni, obj, str)) {
   1878      return false;
   1879    }
   1880  }
   1881 
   1882  return true;
   1883 }
   1884 
   1885 bool js::SuppressDeletedProperty(JSContext* cx, HandleObject obj, jsid id) {
   1886  if (MOZ_LIKELY(!obj->compartment()->objectMaybeInIteration(obj))) {
   1887    return true;
   1888  }
   1889 
   1890  if (id.isSymbol()) {
   1891    return true;
   1892  }
   1893 
   1894  Rooted<JSLinearString*> str(cx, IdToString(cx, id));
   1895  if (!str) {
   1896    return false;
   1897  }
   1898  return SuppressDeletedPropertyHelper(cx, obj, str);
   1899 }
   1900 
   1901 bool js::SuppressDeletedElement(JSContext* cx, HandleObject obj,
   1902                                uint32_t index) {
   1903  if (MOZ_LIKELY(!obj->compartment()->objectMaybeInIteration(obj))) {
   1904    return true;
   1905  }
   1906 
   1907  RootedId id(cx);
   1908  if (!IndexToId(cx, index, &id)) {
   1909    return false;
   1910  }
   1911 
   1912  Rooted<JSLinearString*> str(cx, IdToString(cx, id));
   1913  if (!str) {
   1914    return false;
   1915  }
   1916  return SuppressDeletedPropertyHelper(cx, obj, str);
   1917 }
   1918 
   1919 #ifdef DEBUG
   1920 void js::AssertDenseElementsNotIterated(NativeObject* obj) {
   1921  // Search for active iterators for |obj| and assert they don't contain any
   1922  // property keys that are dense elements. This is used to check correctness
   1923  // of the MAYBE_IN_ITERATION flag on ObjectElements.
   1924  //
   1925  // Ignore iterators that may contain indexed properties from objects on the
   1926  // prototype chain, as that can result in false positives. See bug 1656744.
   1927 
   1928  // Limit the number of properties we check to avoid slowing down debug builds
   1929  // too much.
   1930  static constexpr uint32_t MaxPropsToCheck = 10;
   1931  uint32_t propsChecked = 0;
   1932 
   1933  NativeIteratorListIter iter(obj->compartment()->enumeratorsAddr());
   1934  while (!iter.done()) {
   1935    NativeIterator* ni = iter.next();
   1936    if (ni->objectBeingIterated() == obj &&
   1937        !ni->maybeHasIndexedPropertiesFromProto()) {
   1938      for (IteratorProperty* idp = ni->nextProperty();
   1939           idp < ni->propertiesEnd(); ++idp) {
   1940        uint32_t index;
   1941        if (idp->asString()->isIndex(&index)) {
   1942          MOZ_ASSERT(!obj->containsDenseElement(index));
   1943        }
   1944        if (++propsChecked > MaxPropsToCheck) {
   1945          return;
   1946        }
   1947      }
   1948    }
   1949  }
   1950 }
   1951 #endif
   1952 
   1953 static const JSFunctionSpec iterator_static_methods[] = {
   1954    JS_SELF_HOSTED_FN("from", "IteratorFrom", 1, 0),
   1955    JS_SELF_HOSTED_FN("concat", "IteratorConcat", 0, 0),
   1956 #ifdef NIGHTLY_BUILD
   1957    JS_SELF_HOSTED_FN("range", "IteratorRange", 3, 0),
   1958 #endif
   1959    JS_SELF_HOSTED_FN("zip", "IteratorZip", 2, 0),
   1960    JS_SELF_HOSTED_FN("zipKeyed", "IteratorZipKeyed", 2, 0),
   1961    JS_FS_END,
   1962 };
   1963 
   1964 // These methods are only attached to Iterator.prototype when the
   1965 // Iterator Helpers feature is enabled.
   1966 static const JSFunctionSpec iterator_methods[] = {
   1967    JS_SELF_HOSTED_FN("map", "IteratorMap", 1, 0),
   1968    JS_SELF_HOSTED_FN("filter", "IteratorFilter", 1, 0),
   1969    JS_SELF_HOSTED_FN("take", "IteratorTake", 1, 0),
   1970    JS_SELF_HOSTED_FN("drop", "IteratorDrop", 1, 0),
   1971    JS_SELF_HOSTED_FN("flatMap", "IteratorFlatMap", 1, 0),
   1972    JS_SELF_HOSTED_FN("reduce", "IteratorReduce", 1, 0),
   1973    JS_SELF_HOSTED_FN("toArray", "IteratorToArray", 0, 0),
   1974    JS_SELF_HOSTED_FN("forEach", "IteratorForEach", 1, 0),
   1975    JS_SELF_HOSTED_FN("some", "IteratorSome", 1, 0),
   1976    JS_SELF_HOSTED_FN("every", "IteratorEvery", 1, 0),
   1977    JS_SELF_HOSTED_FN("find", "IteratorFind", 1, 0),
   1978    JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0),
   1979 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
   1980    JS_SELF_HOSTED_SYM_FN(dispose, "IteratorDispose", 0, 0),
   1981 #endif
   1982 #ifdef NIGHTLY_BUILD
   1983    JS_SELF_HOSTED_FN("chunks", "IteratorChunks", 1, 0),
   1984    JS_SELF_HOSTED_FN("windows", "IteratorWindows", 2, 0),
   1985    JS_SELF_HOSTED_FN("join", "IteratorJoin", 1, 0),
   1986 #endif
   1987    JS_FS_END,
   1988 };
   1989 
   1990 // https://tc39.es/proposal-iterator-helpers/#sec-SetterThatIgnoresPrototypeProperties
   1991 static bool SetterThatIgnoresPrototypeProperties(JSContext* cx,
   1992                                                 Handle<Value> thisv,
   1993                                                 Handle<PropertyKey> prop,
   1994                                                 Handle<Value> value) {
   1995  // Step 1.
   1996  Rooted<JSObject*> thisObj(cx,
   1997                            RequireObject(cx, JSMSG_OBJECT_REQUIRED, thisv));
   1998  if (!thisObj) {
   1999    return false;
   2000  }
   2001 
   2002  // Step 2.
   2003  JSObject* home = GlobalObject::getOrCreateIteratorPrototype(cx, cx->global());
   2004  if (!home) {
   2005    return false;
   2006  }
   2007  if (thisObj == home) {
   2008    UniqueChars propName =
   2009        IdToPrintableUTF8(cx, prop, IdToPrintableBehavior::IdIsPropertyKey);
   2010    if (!propName) {
   2011      return false;
   2012    }
   2013 
   2014    // Step 2.b.
   2015    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_READ_ONLY,
   2016                              propName.get());
   2017    return false;
   2018  }
   2019 
   2020  // Step 3.
   2021  Rooted<Maybe<PropertyDescriptor>> desc(cx);
   2022  if (!GetOwnPropertyDescriptor(cx, thisObj, prop, &desc)) {
   2023    return false;
   2024  }
   2025 
   2026  // Step 4.
   2027  if (desc.isNothing()) {
   2028    // Step 4.a.
   2029    return DefineDataProperty(cx, thisObj, prop, value, JSPROP_ENUMERATE);
   2030  }
   2031 
   2032  // Step 5.
   2033  return SetProperty(cx, thisObj, prop, value);
   2034 }
   2035 
   2036 // https://tc39.es/proposal-iterator-helpers/#sec-get-iteratorprototype-@@tostringtag
   2037 static bool toStringTagGetter(JSContext* cx, unsigned argc, Value* vp) {
   2038  CallArgs args = CallArgsFromVp(argc, vp);
   2039 
   2040  // Step 1.
   2041  args.rval().setString(cx->names().Iterator);
   2042  return true;
   2043 }
   2044 
   2045 // https://tc39.es/proposal-iterator-helpers/#sec-set-iteratorprototype-@@tostringtag
   2046 static bool toStringTagSetter(JSContext* cx, unsigned argc, Value* vp) {
   2047  CallArgs args = CallArgsFromVp(argc, vp);
   2048 
   2049  // Step 1.
   2050  Rooted<PropertyKey> prop(
   2051      cx, PropertyKey::Symbol(cx->wellKnownSymbols().toStringTag));
   2052  if (!SetterThatIgnoresPrototypeProperties(cx, args.thisv(), prop,
   2053                                            args.get(0))) {
   2054    return false;
   2055  }
   2056 
   2057  // Step 2.
   2058  args.rval().setUndefined();
   2059  return true;
   2060 }
   2061 
   2062 // https://tc39.es/proposal-iterator-helpers/#sec-get-iteratorprototype-constructor
   2063 static bool constructorGetter(JSContext* cx, unsigned argc, Value* vp) {
   2064  CallArgs args = CallArgsFromVp(argc, vp);
   2065 
   2066  // Step 1.
   2067  JSObject* constructor =
   2068      GlobalObject::getOrCreateConstructor(cx, JSProto_Iterator);
   2069  if (!constructor) {
   2070    return false;
   2071  }
   2072  args.rval().setObject(*constructor);
   2073  return true;
   2074 }
   2075 
   2076 // https://tc39.es/proposal-iterator-helpers/#sec-set-iteratorprototype-constructor
   2077 static bool constructorSetter(JSContext* cx, unsigned argc, Value* vp) {
   2078  CallArgs args = CallArgsFromVp(argc, vp);
   2079 
   2080  // Step 1.
   2081  Rooted<PropertyKey> prop(cx, NameToId(cx->names().constructor));
   2082  if (!SetterThatIgnoresPrototypeProperties(cx, args.thisv(), prop,
   2083                                            args.get(0))) {
   2084    return false;
   2085  }
   2086 
   2087  // Step 2.
   2088  args.rval().setUndefined();
   2089  return true;
   2090 }
   2091 
   2092 static const JSPropertySpec iterator_properties[] = {
   2093    // NOTE: Contrary to most other @@toStringTag properties, this property
   2094    // has a special setter (and a getter).
   2095    JS_SYM_GETSET(toStringTag, toStringTagGetter, toStringTagSetter, 0),
   2096    JS_PS_END,
   2097 };
   2098 
   2099 /* static */
   2100 template <GlobalObject::ProtoKind Kind, const JSClass* ProtoClass,
   2101          const JSFunctionSpec* Methods, const bool needsFuseProperty>
   2102 bool GlobalObject::initObjectIteratorProto(JSContext* cx,
   2103                                           Handle<GlobalObject*> global,
   2104                                           Handle<JSAtom*> tag) {
   2105  if (global->hasBuiltinProto(Kind)) {
   2106    return true;
   2107  }
   2108 
   2109  RootedObject iteratorProto(
   2110      cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
   2111  if (!iteratorProto) {
   2112    return false;
   2113  }
   2114 
   2115  RootedObject proto(cx, GlobalObject::createBlankPrototypeInheriting(
   2116                             cx, ProtoClass, iteratorProto));
   2117  if (!proto || !DefinePropertiesAndFunctions(cx, proto, nullptr, Methods) ||
   2118      (tag && !DefineToStringTag(cx, proto, tag))) {
   2119    return false;
   2120  }
   2121 
   2122  if constexpr (needsFuseProperty) {
   2123    if (!JSObject::setHasRealmFuseProperty(cx, proto)) {
   2124      return false;
   2125    }
   2126  }
   2127 
   2128  global->initBuiltinProto(Kind, proto);
   2129  return true;
   2130 }
   2131 
   2132 /* static */
   2133 NativeObject* GlobalObject::getOrCreateArrayIteratorPrototype(
   2134    JSContext* cx, Handle<GlobalObject*> global) {
   2135  return MaybeNativeObject(getOrCreateBuiltinProto(
   2136      cx, global, ProtoKind::ArrayIteratorProto,
   2137      cx->names().Array_Iterator_.toHandle(),
   2138      initObjectIteratorProto<
   2139          ProtoKind::ArrayIteratorProto, &ArrayIteratorPrototypeClass,
   2140          array_iterator_methods, /* hasFuseProperty= */ true>));
   2141 }
   2142 
   2143 /* static */
   2144 JSObject* GlobalObject::getOrCreateStringIteratorPrototype(
   2145    JSContext* cx, Handle<GlobalObject*> global) {
   2146  return getOrCreateBuiltinProto(
   2147      cx, global, ProtoKind::StringIteratorProto,
   2148      cx->names().String_Iterator_.toHandle(),
   2149      initObjectIteratorProto<ProtoKind::StringIteratorProto,
   2150                              &StringIteratorPrototypeClass,
   2151                              string_iterator_methods>);
   2152 }
   2153 
   2154 /* static */
   2155 JSObject* GlobalObject::getOrCreateRegExpStringIteratorPrototype(
   2156    JSContext* cx, Handle<GlobalObject*> global) {
   2157  return getOrCreateBuiltinProto(
   2158      cx, global, ProtoKind::RegExpStringIteratorProto,
   2159      cx->names().RegExp_String_Iterator_.toHandle(),
   2160      initObjectIteratorProto<ProtoKind::RegExpStringIteratorProto,
   2161                              &RegExpStringIteratorPrototypeClass,
   2162                              regexp_string_iterator_methods>);
   2163 }
   2164 
   2165 #ifdef NIGHTLY_BUILD
   2166 /* static */
   2167 JSObject* GlobalObject::getOrCreateIteratorRangePrototype(
   2168    JSContext* cx, Handle<GlobalObject*> global) {
   2169  return getOrCreateBuiltinProto(
   2170      cx, global, ProtoKind::IteratorRangeProto,
   2171      cx->names().RegExp_String_Iterator_.toHandle(),
   2172      initObjectIteratorProto<ProtoKind::IteratorRangeProto,
   2173                              &IteratorRangePrototypeClass,
   2174                              iterator_range_methods>);
   2175 }
   2176 #endif
   2177 
   2178 // Iterator Helper Proposal 2.1.3.1 Iterator()
   2179 // https://tc39.es/proposal-iterator-helpers/#sec-iterator as of revision
   2180 // ed6e15a
   2181 static bool IteratorConstructor(JSContext* cx, unsigned argc, Value* vp) {
   2182  CallArgs args = CallArgsFromVp(argc, vp);
   2183 
   2184  // Step 1.
   2185  if (!ThrowIfNotConstructing(cx, args, "Iterator")) {
   2186    return false;
   2187  }
   2188  // Throw TypeError if NewTarget is the active function object, preventing the
   2189  // Iterator constructor from being used directly.
   2190  if (args.callee() == args.newTarget().toObject()) {
   2191    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   2192                              JSMSG_BOGUS_CONSTRUCTOR, "Iterator");
   2193    return false;
   2194  }
   2195 
   2196  // Step 2.
   2197  RootedObject proto(cx);
   2198  if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Iterator, &proto)) {
   2199    return false;
   2200  }
   2201 
   2202  JSObject* obj = NewObjectWithClassProto<IteratorObject>(cx, proto);
   2203  if (!obj) {
   2204    return false;
   2205  }
   2206 
   2207  args.rval().setObject(*obj);
   2208  return true;
   2209 }
   2210 
   2211 static const ClassSpec IteratorObjectClassSpec = {
   2212    GenericCreateConstructor<IteratorConstructor, 0, gc::AllocKind::FUNCTION>,
   2213    GenericCreatePrototype<IteratorObject>,
   2214    iterator_static_methods,
   2215    nullptr,
   2216    iterator_methods,
   2217    iterator_properties,
   2218    IteratorObject::finishInit,
   2219 };
   2220 
   2221 const JSClass IteratorObject::class_ = {
   2222    "Iterator",
   2223    JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator),
   2224    JS_NULL_CLASS_OPS,
   2225    &IteratorObjectClassSpec,
   2226 };
   2227 
   2228 const JSClass IteratorObject::protoClass_ = {
   2229    "Iterator.prototype",
   2230    JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator),
   2231    JS_NULL_CLASS_OPS,
   2232    &IteratorObjectClassSpec,
   2233 };
   2234 
   2235 /* static */ bool IteratorObject::finishInit(JSContext* cx, HandleObject ctor,
   2236                                             HandleObject proto) {
   2237  Rooted<PropertyKey> id(cx, NameToId(cx->names().constructor));
   2238  return JS_DefinePropertyById(cx, proto, id, constructorGetter,
   2239                               constructorSetter, 0);
   2240 }
   2241 
   2242 // Set up WrapForValidIteratorObject class and its prototype.
   2243 static const JSFunctionSpec wrap_for_valid_iterator_methods[] = {
   2244    JS_SELF_HOSTED_FN("next", "WrapForValidIteratorNext", 0, 0),
   2245    JS_SELF_HOSTED_FN("return", "WrapForValidIteratorReturn", 0, 0),
   2246    JS_FS_END,
   2247 };
   2248 
   2249 static const JSClass WrapForValidIteratorPrototypeClass = {
   2250    "Wrap For Valid Iterator",
   2251    0,
   2252 };
   2253 
   2254 const JSClass WrapForValidIteratorObject::class_ = {
   2255    "Wrap For Valid Iterator",
   2256    JSCLASS_HAS_RESERVED_SLOTS(WrapForValidIteratorObject::SlotCount),
   2257 };
   2258 
   2259 /* static */
   2260 NativeObject* GlobalObject::getOrCreateWrapForValidIteratorPrototype(
   2261    JSContext* cx, Handle<GlobalObject*> global) {
   2262  return MaybeNativeObject(getOrCreateBuiltinProto(
   2263      cx, global, ProtoKind::WrapForValidIteratorProto,
   2264      Handle<JSAtom*>(nullptr),
   2265      initObjectIteratorProto<ProtoKind::WrapForValidIteratorProto,
   2266                              &WrapForValidIteratorPrototypeClass,
   2267                              wrap_for_valid_iterator_methods>));
   2268 }
   2269 
   2270 WrapForValidIteratorObject* js::NewWrapForValidIterator(JSContext* cx) {
   2271  RootedObject proto(cx, GlobalObject::getOrCreateWrapForValidIteratorPrototype(
   2272                             cx, cx->global()));
   2273  if (!proto) {
   2274    return nullptr;
   2275  }
   2276  return NewObjectWithGivenProto<WrapForValidIteratorObject>(cx, proto);
   2277 }
   2278 
   2279 // Common iterator object returned by Iterator Helper methods.
   2280 static const JSFunctionSpec iterator_helper_methods[] = {
   2281    JS_SELF_HOSTED_FN("next", "IteratorHelperNext", 0, 0),
   2282    JS_SELF_HOSTED_FN("return", "IteratorHelperReturn", 0, 0),
   2283    JS_FS_END,
   2284 };
   2285 
   2286 static const JSClass IteratorHelperPrototypeClass = {
   2287    "Iterator Helper",
   2288    0,
   2289 };
   2290 
   2291 const JSClass IteratorHelperObject::class_ = {
   2292    "Iterator Helper",
   2293    JSCLASS_HAS_RESERVED_SLOTS(IteratorHelperObject::SlotCount),
   2294 };
   2295 
   2296 #ifdef NIGHTLY_BUILD
   2297 const JSClass IteratorRangeObject::class_ = {
   2298    "IteratorRange",
   2299    JSCLASS_HAS_RESERVED_SLOTS(IteratorRangeSlotCount),
   2300 };
   2301 #endif
   2302 
   2303 /* static */
   2304 NativeObject* GlobalObject::getOrCreateIteratorHelperPrototype(
   2305    JSContext* cx, Handle<GlobalObject*> global) {
   2306  return MaybeNativeObject(getOrCreateBuiltinProto(
   2307      cx, global, ProtoKind::IteratorHelperProto,
   2308      cx->names().Iterator_Helper_.toHandle(),
   2309      initObjectIteratorProto<ProtoKind::IteratorHelperProto,
   2310                              &IteratorHelperPrototypeClass,
   2311                              iterator_helper_methods>));
   2312 }
   2313 
   2314 IteratorHelperObject* js::NewIteratorHelper(JSContext* cx) {
   2315  RootedObject proto(
   2316      cx, GlobalObject::getOrCreateIteratorHelperPrototype(cx, cx->global()));
   2317  if (!proto) {
   2318    return nullptr;
   2319  }
   2320  return NewObjectWithGivenProto<IteratorHelperObject>(cx, proto);
   2321 }
   2322 
   2323 bool js::IterableToArray(JSContext* cx, HandleValue iterable,
   2324                         MutableHandle<ArrayObject*> array) {
   2325  JS::ForOfIterator iterator(cx);
   2326  if (!iterator.init(iterable, JS::ForOfIterator::ThrowOnNonIterable)) {
   2327    return false;
   2328  }
   2329 
   2330  array.set(NewDenseEmptyArray(cx));
   2331  if (!array) {
   2332    return false;
   2333  }
   2334 
   2335  RootedValue nextValue(cx);
   2336  while (true) {
   2337    bool done;
   2338    if (!iterator.next(&nextValue, &done)) {
   2339      return false;
   2340    }
   2341    if (done) {
   2342      break;
   2343    }
   2344 
   2345    if (!NewbornArrayPush(cx, array, nextValue)) {
   2346      return false;
   2347    }
   2348  }
   2349  return true;
   2350 }
   2351 
   2352 bool js::HasOptimizableArrayIteratorPrototype(JSContext* cx) {
   2353  // Return true if %ArrayIteratorPrototype% does not have (re)defined `next`
   2354  // and `return` properties.
   2355  return cx->realm()->realmFuses.optimizeArrayIteratorPrototypeFuse.intact();
   2356 }
   2357 
   2358 template <MustBePacked Packed>
   2359 bool js::IsArrayWithDefaultIterator(JSObject* obj, JSContext* cx) {
   2360  if constexpr (Packed == MustBePacked::Yes) {
   2361    if (!IsPackedArray(obj)) {
   2362      return false;
   2363    }
   2364  } else {
   2365    if (!obj->is<ArrayObject>()) {
   2366      return false;
   2367    }
   2368  }
   2369  ArrayObject* arr = &obj->as<ArrayObject>();
   2370 
   2371  // Ensure Array.prototype[@@iterator] and %ArrayIteratorPrototype% haven't
   2372  // been mutated in a way that affects the iterator protocol.
   2373  if (!arr->realm()->realmFuses.optimizeGetIteratorFuse.intact()) {
   2374    return false;
   2375  }
   2376 
   2377  // Ensure the array has Array.prototype as prototype and doesn't have an own
   2378  // @@iterator property.
   2379  //
   2380  // Most arrays have the default array shape so we have a fast path for this
   2381  // case.
   2382  GlobalObject& global = arr->global();
   2383  if (arr->shape() == global.maybeArrayShapeWithDefaultProto()) {
   2384    return true;
   2385  }
   2386 
   2387  NativeObject* arrayProto = global.maybeGetArrayPrototype();
   2388  if (!arrayProto || arr->staticPrototype() != arrayProto) {
   2389    return false;
   2390  }
   2391  if (arr->containsPure(PropertyKey::Symbol(cx->wellKnownSymbols().iterator))) {
   2392    return false;
   2393  }
   2394 
   2395  return true;
   2396 }
   2397 
   2398 template bool js::IsArrayWithDefaultIterator<MustBePacked::No>(JSObject* obj,
   2399                                                               JSContext* cx);
   2400 template bool js::IsArrayWithDefaultIterator<MustBePacked::Yes>(JSObject* obj,
   2401                                                                JSContext* cx);
   2402 
   2403 template <typename ObjectT, JSProtoKey ProtoKey>
   2404 static bool IsMapOrSetObjectWithDefaultIterator(JSObject* objArg,
   2405                                                JSContext* cx) {
   2406  if (!objArg->is<ObjectT>()) {
   2407    return false;
   2408  }
   2409  auto* obj = &objArg->as<ObjectT>();
   2410 
   2411  // Ensure {Map,Set}.prototype[@@iterator] and %{Map,Set}IteratorPrototype%
   2412  // haven't been mutated in a way that affects the iterator protocol.
   2413  //
   2414  // Note: this doesn't guard against `return` properties on the iterator
   2415  // prototype, so this should only be used in places where we don't have to
   2416  // call `IteratorClose`.
   2417  if constexpr (std::is_same_v<ObjectT, MapObject>) {
   2418    if (!obj->realm()->realmFuses.optimizeMapObjectIteratorFuse.intact()) {
   2419      return false;
   2420    }
   2421  } else {
   2422    static_assert(std::is_same_v<ObjectT, SetObject>);
   2423    if (!obj->realm()->realmFuses.optimizeSetObjectIteratorFuse.intact()) {
   2424      return false;
   2425    }
   2426  }
   2427 
   2428  // Ensure the object has the builtin prototype as proto.
   2429  GlobalObject& global = obj->global();
   2430  JSObject* proto = global.maybeGetPrototype(ProtoKey);
   2431  if (!proto || obj->staticPrototype() != proto) {
   2432    return false;
   2433  }
   2434 
   2435  // Ensure the Map or Set object doesn't have an own @@iterator property.
   2436  // Most Maps and Sets have no own properties so we have a fast path for this
   2437  // case.
   2438  if (obj->empty()) {
   2439    return true;
   2440  }
   2441  if (obj->containsPure(PropertyKey::Symbol(cx->wellKnownSymbols().iterator))) {
   2442    return false;
   2443  }
   2444  return true;
   2445 }
   2446 bool js::IsMapObjectWithDefaultIterator(JSObject* obj, JSContext* cx) {
   2447  return IsMapOrSetObjectWithDefaultIterator<MapObject, JSProto_Map>(obj, cx);
   2448 }
   2449 
   2450 bool js::IsSetObjectWithDefaultIterator(JSObject* obj, JSContext* cx) {
   2451  return IsMapOrSetObjectWithDefaultIterator<SetObject, JSProto_Set>(obj, cx);
   2452 }