tor-browser

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

Iteration.h (28899B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef vm_Iteration_h
      8 #define vm_Iteration_h
      9 
     10 /*
     11 * JavaScript iterators.
     12 */
     13 
     14 #include "mozilla/MemoryReporting.h"
     15 
     16 #include "builtin/SelfHostingDefines.h"
     17 #include "gc/Barrier.h"
     18 #include "vm/NativeObject.h"
     19 #include "vm/TypedArrayObject.h"
     20 
     21 /*
     22 * [SMDOC] For-in enumeration
     23 *
     24 * A for-in loop in JS iterates over the string-valued, enumerable
     25 * property keys of an object and its prototype chain. The order in
     26 * which keys appear is specified to the extent that implementations
     27 * historically agreed, and implementation-defined beyond that. See
     28 * https://tc39.es/ecma262/#sec-enumerate-object-properties for the
     29 * gory details. Each key appears only once in the enumeration.
     30 *
     31 * We enumerate properties using PropertyEnumerator, which creates an
     32 * ordered list of PropertyKeys, using ShapePropertyIter for native
     33 * objects and calling enumerate hooks where necessary. This list is
     34 * used to create a NativeIterator, which contains (among other
     35 * things) a trailing array of strings representing the property keys
     36 * of the object, and a cursor pointing into that array. This
     37 * NativeIterator is wrapped in a PropertyIteratorObject, which is
     38 * pushed by JSOp::Iter and used by JSOp::MoreIter and JSOp::EndIter.
     39 *
     40 * While active, a NativeIterator is registered in a doubly linked
     41 * list, rooted in the compartment. When any property is deleted from
     42 * an object, this list is used to remove the deleted property from
     43 * any active enumerations. See SuppressDeletedProperty. This slows
     44 * down deletion but speeds up enumeration, which is generally a good
     45 * tradeoff.
     46 *
     47 * In many cases, objects with the same shape will have the same set
     48 * of property keys. (The most common exception is objects with dense
     49 * elements, which can be added or removed without changing the shape
     50 * of the object.) In such cases, we can reuse an existing iterator by
     51 * storing a pointer to the PropertyIteratorObject in the shape's
     52 * |cache_| pointer. Before reusing an iterator, we have to verify
     53 * that the prototype chain has not changed and no dense elements have
     54 * been added, which is done by storing a trailing array of prototype
     55 * shapes in the NativeIterator and comparing it against the shapes of
     56 * the prototype chain.
     57 *
     58 * One of the most frequent uses of for-in loops is in loops that look
     59 * like this, which iterate over each property of an object and do
     60 * something with those values:
     61 *   for (var key in obj) {
     62 *     if (obj.hasOwnProperty(key)) {
     63 *       doSomethingWith(obj[key]);
     64 *     }
     65 *   }
     66 * Most objects don't have any enumerable properties on the prototype
     67 * chain. In such cases, we can speed up property access inside the
     68 * loop by precomputing some information and storing it in the
     69 * iterator.  When we see a pattern like this in Ion, we generate a
     70 * call to GetIteratorWithIndices instead of GetIterator. In this
     71 * case, in addition to the list of property keys, PropertyEnumerator
     72 * will try to generate a list of corresponding PropertyIndex values,
     73 * which represent the location of the own property key in the object
     74 * (fixed slot/dynamic slot/dense element + offset). This list will be
     75 * stored in NativeIterator as yet another trailing array. When
     76 * present, it can be used by Ion code to speed up property access
     77 * inside for-in loops. See OptimizeIteratorIndices in
     78 * IonAnalysis.cpp.
     79 */
     80 
     81 namespace js {
     82 
     83 class ArrayObject;
     84 class PlainObject;
     85 class PropertyIteratorObject;
     86 
     87 // A PropertyIndex stores information about the location of an own data
     88 // property in a format that can be stored in a NativeIterator and consumed by
     89 // jitcode to access properties without needing to use the megamorphic cache.
     90 struct PropertyIndex {
     91 private:
     92  uint32_t asBits_;
     93 
     94 public:
     95  enum class Kind : uint32_t { DynamicSlot, FixedSlot, Element, Invalid };
     96 
     97  PropertyIndex(Kind kind, uint32_t index) : asBits_(encode(kind, index)) {}
     98 
     99  static PropertyIndex Invalid() { return PropertyIndex(Kind::Invalid, 0); }
    100 
    101  static PropertyIndex ForElement(uint32_t index) {
    102    return PropertyIndex(Kind::Element, index);
    103  }
    104 
    105  static PropertyIndex ForSlot(NativeObject* obj, uint32_t index) {
    106    if (index < obj->numFixedSlots()) {
    107      return PropertyIndex(Kind::FixedSlot, index);
    108    } else {
    109      return PropertyIndex(Kind::DynamicSlot, index - obj->numFixedSlots());
    110    }
    111  }
    112 
    113  static constexpr uint32_t KindBits = 2;
    114 
    115  static constexpr uint32_t IndexBits = 32 - KindBits;
    116  static constexpr uint32_t IndexLimit = 1 << IndexBits;
    117  static constexpr uint32_t IndexMask = (1 << IndexBits) - 1;
    118 
    119  static constexpr uint32_t KindShift = IndexBits;
    120 
    121  static_assert(NativeObject::MAX_FIXED_SLOTS < IndexLimit);
    122  static_assert(NativeObject::MAX_SLOTS_COUNT < IndexLimit);
    123  static_assert(NativeObject::MAX_DENSE_ELEMENTS_COUNT < IndexLimit);
    124 
    125 private:
    126  uint32_t encode(Kind kind, uint32_t index) {
    127    MOZ_ASSERT(index < IndexLimit);
    128    return (uint32_t(kind) << KindShift) | index;
    129  }
    130 
    131 public:
    132  Kind kind() const { return Kind(asBits_ >> KindShift); }
    133  uint32_t index() const { return asBits_ & IndexMask; }
    134 };
    135 
    136 using PropertyIndexVector = js::Vector<PropertyIndex, 8, js::TempAllocPolicy>;
    137 
    138 struct NativeIterator;
    139 
    140 class NativeIteratorListNode {
    141 protected:
    142  // While in compartment->enumerators, these form a doubly linked list.
    143  NativeIteratorListNode* prev_ = nullptr;
    144  NativeIteratorListNode* next_ = nullptr;
    145 
    146 public:
    147  NativeIteratorListNode* prev() { return prev_; }
    148  NativeIteratorListNode* next() { return next_; }
    149 
    150  void setPrev(NativeIteratorListNode* prev) { prev_ = prev; }
    151  void setNext(NativeIteratorListNode* next) { next_ = next; }
    152 
    153  static constexpr size_t offsetOfNext() {
    154    return offsetof(NativeIteratorListNode, next_);
    155  }
    156 
    157  static constexpr size_t offsetOfPrev() {
    158    return offsetof(NativeIteratorListNode, prev_);
    159  }
    160 
    161 private:
    162  NativeIterator* asNativeIterator() {
    163    return reinterpret_cast<NativeIterator*>(this);
    164  }
    165 
    166  friend class NativeIteratorListIter;
    167 };
    168 
    169 class NativeIteratorListHead : public NativeIteratorListNode {
    170 private:
    171  // Initialize a |Compartment::enumerators| sentinel.
    172  NativeIteratorListHead() { prev_ = next_ = this; }
    173  friend class JS::Compartment;
    174 };
    175 
    176 class NativeIteratorListIter {
    177 private:
    178  NativeIteratorListHead* head_;
    179  NativeIteratorListNode* curr_;
    180 
    181 public:
    182  explicit NativeIteratorListIter(NativeIteratorListHead* head)
    183      : head_(head), curr_(head->next()) {}
    184 
    185  bool done() const { return curr_ == head_; }
    186 
    187  NativeIterator* next() {
    188    MOZ_ASSERT(!done());
    189    NativeIterator* result = curr_->asNativeIterator();
    190    curr_ = curr_->next();
    191    return result;
    192  }
    193 };
    194 
    195 class IteratorProperty {
    196  uintptr_t raw_ = 0;
    197 
    198 public:
    199  static constexpr uintptr_t DeletedBit = 0x1;
    200 
    201  // The copy constructor is deleted as a safeguard against writing code which
    202  // would overwrite existing IteratorProperties on NativeIterators. They are
    203  // intended to be written once and never changed, outside of being moved in
    204  // GC callbacks.
    205  IteratorProperty(const IteratorProperty&) = delete;
    206 
    207  IteratorProperty() = default;
    208  explicit IteratorProperty(JSLinearString* str) : raw_(uintptr_t(str)) {}
    209  IteratorProperty(JSLinearString* str, bool deleted)
    210      : raw_(uintptr_t(str) | (deleted ? DeletedBit : 0)) {}
    211 
    212  JSLinearString* asString() const {
    213    return reinterpret_cast<JSLinearString*>(raw_ & ~DeletedBit);
    214  }
    215 
    216  bool deleted() const { return raw_ & DeletedBit; }
    217  void markDeleted() { raw_ |= DeletedBit; }
    218  void clearDeleted() { raw_ &= ~DeletedBit; }
    219 
    220  void traceString(JSTracer* trc);
    221 } JS_HAZ_GC_POINTER;
    222 
    223 struct NativeIterator : public NativeIteratorListNode {
    224 private:
    225  // Object being iterated.  Non-null except in NativeIterator sentinels,
    226  // the empty iterator singleton (for iterating |null| or |undefined|), and
    227  // inactive iterators.
    228  GCPtr<JSObject*> objectBeingIterated_ = {};
    229 
    230  // Internal iterator object.
    231  const GCPtr<JSObject*> iterObj_ = {};
    232  const GCPtr<Shape*> objShape_ = {};
    233  uint32_t propertyCount_ = 0;
    234  uint32_t propertyCursor_;    // initialized by constructor
    235  uint32_t ownPropertyCount_;  // initialized by constructor
    236  HashNumber shapesHash_;      // initialized by constructor
    237  uint16_t protoShapeCount_ = 0;
    238  uint8_t flags_ = 0;
    239 
    240 public:
    241  // For cacheable native iterators, whether the iterator is currently
    242  // active.  Not serialized by XDR.
    243  struct Flags {
    244    // This flag is set when all shapes and properties associated with this
    245    // NativeIterator have been initialized, such that |shapesEnd_|, in
    246    // addition to being the end of shapes, is also the beginning of
    247    // properties.
    248    //
    249    // This flag is only *not* set when a NativeIterator is in the process
    250    // of being constructed.  At such time |shapesEnd_| accounts only for
    251    // shapes that have been initialized -- potentially none of them.
    252    // Instead, |propertyCursor_| is initialized to the ultimate/actual
    253    // start of properties and must be used instead of |propertiesBegin()|,
    254    // which asserts that this flag is present to guard against misuse.
    255    static constexpr uint32_t Initialized = 0x1;
    256 
    257    // This flag indicates that this NativeIterator is currently being used
    258    // to enumerate an object's properties and has not yet been closed.
    259    static constexpr uint32_t Active = 0x2;
    260 
    261    // This flag indicates that the object being enumerated by this
    262    // |NativeIterator| had a property deleted from it before it was
    263    // visited, forcing the properties array in this to be mutated to
    264    // remove it.
    265    static constexpr uint32_t HasUnvisitedPropertyDeletion = 0x4;
    266 
    267    // Whether this is the shared empty iterator object used for iterating over
    268    // null/undefined.
    269    static constexpr uint32_t IsEmptyIteratorSingleton = 0x8;
    270 
    271    // NOTE: the three flags below pertain to iterator indices optimizations.
    272    // If an object only has own data properties, we can store a list of
    273    // PropertyIndex that can be used in Ion to more efficiently access those
    274    // properties in cases like `for (var key in obj) { ...obj[key]... }`.
    275 
    276    // Whether the object supports indices, in the event that they are
    277    // requested. Note that this is exclusive with IndicesAvailable
    278    static constexpr uint32_t IndicesSupported = 0x10;
    279 
    280    // Whether space was initially reserved for indices for this iterator.
    281    static constexpr uint32_t IndicesAllocated = 0x20;
    282 
    283    // Whether indices are actually valid in the reserved area
    284    static constexpr uint32_t IndicesAvailable = 0x40;
    285 
    286    // If this iterator was created only for own properties
    287    static constexpr uint32_t OwnPropertiesOnly = 0x80;
    288 
    289    // If any of these bits are set on a |NativeIterator|, it isn't
    290    // currently reusable.  (An active |NativeIterator| can't be stolen
    291    // *right now*; a |NativeIterator| that's had its properties mutated
    292    // can never be reused, because it would give incorrect results.)
    293    static constexpr uint32_t NotReusable =
    294        Active | HasUnvisitedPropertyDeletion | OwnPropertiesOnly;
    295  };
    296 
    297  // We have a full u32 for this, but due to the way we compute the address
    298  // of indices in the MacroAssembler, we want to have a few extra bits of
    299  // wiggle room for shifting
    300  static constexpr uint32_t PropCountLimit = 1 << 30;
    301 
    302  // If it's really important we can increase the size of protoShapeCount_,
    303  // but increasing it to 32 bits would add another word.
    304  static constexpr uint32_t ShapeCountLimit = 1 << 16;
    305 
    306 private:
    307 #ifdef DEBUG
    308  // If true, this iterator may contain indexed properties that came from
    309  // objects on the prototype chain. This is used by certain debug assertions.
    310  bool maybeHasIndexedPropertiesFromProto_ = false;
    311 #endif
    312 
    313  // END OF PROPERTIES
    314 
    315  // No further fields appear after here *in NativeIterator*, but this class is
    316  // always allocated with space tacked on immediately after |this| to store
    317  // propertyCount_ IteratorProperty values, optionally propertyCount_
    318  // PropertyIndex values, and protoShapeCount_ GCPtr<Shape*> values.
    319 public:
    320  /**
    321   * Initialize a NativeIterator properly allocated for |props.length()|
    322   * properties and |numShapes| shapes. If |indices| is non-null, also
    323   * allocates room for |indices.length()| PropertyIndex values. In this case,
    324   * |indices.length()| must equal |props.length()|.
    325   *
    326   * Despite being a constructor, THIS FUNCTION CAN REPORT ERRORS.  Users
    327   * MUST set |*hadError = false| on entry and consider |*hadError| on return
    328   * to mean this function failed.
    329   */
    330  NativeIterator(JSContext* cx, Handle<PropertyIteratorObject*> propIter,
    331                 Handle<JSObject*> objBeingIterated, HandleIdVector props,
    332                 bool supportsIndices, PropertyIndexVector* indices,
    333                 uint32_t numShapes, uint32_t ownPropertyCount,
    334                 bool forObjectKeys, bool* hadError);
    335 
    336  JSObject* objectBeingIterated() const { return objectBeingIterated_; }
    337 
    338  void initObjectBeingIterated(JSObject& obj) {
    339    MOZ_ASSERT(!objectBeingIterated_);
    340    objectBeingIterated_.init(&obj);
    341  }
    342  void clearObjectBeingIterated() {
    343    MOZ_ASSERT(objectBeingIterated_);
    344    objectBeingIterated_ = nullptr;
    345  }
    346 
    347  const GCPtr<Shape*>& objShape() const { return objShape_; }
    348 
    349  GCPtr<Shape*>* protoShapesBegin(size_t numProperties) const {
    350    uintptr_t raw = reinterpret_cast<uintptr_t>(this);
    351    uintptr_t propertiesStart = raw + offsetOfFirstProperty();
    352    uintptr_t propertiesEnd =
    353        propertiesStart + numProperties * sizeof(IteratorProperty);
    354    uintptr_t result = propertiesEnd;
    355    if (flags_ & Flags::IndicesAllocated) {
    356      result += numProperties * sizeof(PropertyIndex);
    357    }
    358    return reinterpret_cast<GCPtr<Shape*>*>(result);
    359  }
    360 
    361  GCPtr<Shape*>* protoShapesBegin() const {
    362    return protoShapesBegin(allocatedPropertyCount());
    363  }
    364 
    365  GCPtr<Shape*>* protoShapesEnd() const {
    366    return protoShapesBegin() + protoShapeCount_;
    367  }
    368 
    369  uint32_t protoShapeCount() const { return protoShapeCount_; }
    370 
    371  IteratorProperty* propertiesBegin() const {
    372    static_assert(
    373        alignof(GCPtr<Shape*>) >= alignof(IteratorProperty),
    374        "IteratorPropertys for properties must be able to appear "
    375        "directly after any GCPtr<Shape*>s after this NativeIterator, "
    376        "with no padding space required for correct alignment");
    377    static_assert(
    378        alignof(NativeIterator) >= alignof(IteratorProperty),
    379        "IteratorPropertys for properties must be able to appear "
    380        "directly after this NativeIterator when no GCPtr<Shape*>s are "
    381        "present, with no padding space required for correct "
    382        "alignment");
    383 
    384    return reinterpret_cast<IteratorProperty*>(uintptr_t(this) + sizeof(*this));
    385  }
    386 
    387  IteratorProperty* propertiesEnd() const {
    388    return propertiesBegin() + propertyCount_;
    389  }
    390 
    391  IteratorProperty* nextProperty() const {
    392    return propertiesBegin() + propertyCursor_;
    393  }
    394 
    395  PropertyIndex* indicesBegin() const {
    396    // PropertyIndex must be able to be appear directly after the properties
    397    // array, with no padding required for correct alignment.
    398    static_assert(alignof(IteratorProperty) >= alignof(PropertyIndex));
    399    return reinterpret_cast<PropertyIndex*>(propertiesEnd());
    400  }
    401 
    402  PropertyIndex* indicesEnd() const {
    403    MOZ_ASSERT(flags_ & Flags::IndicesAllocated);
    404    return indicesBegin() + propertyCount_ * sizeof(PropertyIndex);
    405  }
    406 
    407  MOZ_ALWAYS_INLINE JS::Value nextIteratedValueAndAdvance() {
    408    while (propertyCursor_ < propertyCount_) {
    409      IteratorProperty& prop = *nextProperty();
    410      incCursor();
    411      if (prop.deleted()) {
    412        continue;
    413      }
    414      return JS::StringValue(prop.asString());
    415    }
    416 
    417    return JS::MagicValue(JS_NO_ITER_VALUE);
    418  }
    419 
    420  void resetPropertyCursorForReuse() {
    421    MOZ_ASSERT(isInitialized());
    422 
    423    // This function is called unconditionally on IteratorClose, so
    424    // unvisited properties might have been deleted, so we can't assert
    425    // this NativeIterator is reusable.  (Should we not bother resetting
    426    // the cursor in that case?)
    427 
    428    // If properties were marked as deleted, unmark them.
    429    if (hasUnvisitedPropertyDeletion()) {
    430      for (IteratorProperty* prop = propertiesBegin(); prop < propertiesEnd();
    431           prop++) {
    432        prop->clearDeleted();
    433      }
    434      unmarkHasUnvisitedPropertyDeletion();
    435    }
    436 
    437    // Note: JIT code inlines |propertyCursor_| resetting when an iterator
    438    //       ends: see |MacroAssembler::iteratorClose|.
    439    propertyCursor_ = 0;
    440  }
    441 
    442  bool previousPropertyWas(JS::Handle<JSLinearString*> str) {
    443    MOZ_ASSERT(isInitialized());
    444    return propertyCursor_ > 0 &&
    445           propertiesBegin()[propertyCursor_ - 1].asString() == str;
    446  }
    447 
    448  size_t numKeys() const { return propertyCount_; }
    449 
    450  size_t ownPropertyCount() const { return ownPropertyCount_; }
    451 
    452  size_t allocatedPropertyCount() const {
    453    // propertyCursor_ holds the number of allocated properties until
    454    // the iterator is initialized. This is so we can know the proper layout
    455    // of the trailing bytes if we trigger a GC inside the constructor.
    456    if (!isInitialized()) {
    457      return propertyCursor_;
    458    }
    459    return propertyCount_;
    460  }
    461 
    462  JSObject* iterObj() const { return iterObj_; }
    463 
    464  void incCursor() {
    465    MOZ_ASSERT(isInitialized());
    466    propertyCursor_++;
    467  }
    468 
    469  HashNumber shapesHash() const { return shapesHash_; }
    470 
    471  bool isInitialized() const { return flags_ & Flags::Initialized; }
    472 
    473  size_t allocationSize() const;
    474 
    475 #ifdef DEBUG
    476  void setMaybeHasIndexedPropertiesFromProto() {
    477    maybeHasIndexedPropertiesFromProto_ = true;
    478  }
    479  bool maybeHasIndexedPropertiesFromProto() const {
    480    return maybeHasIndexedPropertiesFromProto_;
    481  }
    482 #endif
    483 
    484 private:
    485  bool indicesAllocated() const { return flags_ & Flags::IndicesAllocated; }
    486 
    487  bool isUnlinked() const { return !prev_ && !next_; }
    488 
    489 public:
    490  bool indicesAvailable() const { return flags_ & Flags::IndicesAvailable; }
    491 
    492  bool indicesSupported() const { return flags_ & Flags::IndicesSupported; }
    493 
    494  bool ownPropertiesOnly() const { return flags_ & Flags::OwnPropertiesOnly; }
    495 
    496  // Whether this is the shared empty iterator object used for iterating over
    497  // null/undefined.
    498  bool isEmptyIteratorSingleton() const {
    499    // Note: equivalent code is inlined in MacroAssembler::iteratorClose.
    500    bool res = flags_ & Flags::IsEmptyIteratorSingleton;
    501    MOZ_ASSERT_IF(
    502        res, flags_ == (Flags::Initialized | Flags::IsEmptyIteratorSingleton));
    503    MOZ_ASSERT_IF(res, !objectBeingIterated_);
    504    MOZ_ASSERT_IF(res, propertyCount_ == 0);
    505    MOZ_ASSERT_IF(res, protoShapeCount_ == 0);
    506    MOZ_ASSERT_IF(res, isUnlinked());
    507    return res;
    508  }
    509  void markEmptyIteratorSingleton() {
    510    flags_ |= Flags::IsEmptyIteratorSingleton;
    511 
    512    // isEmptyIteratorSingleton() has various debug assertions.
    513    MOZ_ASSERT(isEmptyIteratorSingleton());
    514  }
    515 
    516  bool isActive() const {
    517    MOZ_ASSERT(isInitialized());
    518 
    519    return flags_ & Flags::Active;
    520  }
    521 
    522  void markActive() {
    523    MOZ_ASSERT(isInitialized());
    524    MOZ_ASSERT(!isEmptyIteratorSingleton());
    525 
    526    flags_ |= Flags::Active;
    527  }
    528 
    529  void markInactive() {
    530    MOZ_ASSERT(isInitialized());
    531    MOZ_ASSERT(!isEmptyIteratorSingleton());
    532 
    533    flags_ &= ~Flags::Active;
    534  }
    535 
    536  bool isReusable() const {
    537    MOZ_ASSERT(isInitialized());
    538 
    539    if (!(flags_ & Flags::Initialized)) {
    540      return false;
    541    }
    542    if (flags_ & Flags::Active) {
    543      return false;
    544    }
    545    return true;
    546  }
    547 
    548  void markHasUnvisitedPropertyDeletion() {
    549    MOZ_ASSERT(isInitialized());
    550    MOZ_ASSERT(!isEmptyIteratorSingleton());
    551 
    552    flags_ |= Flags::HasUnvisitedPropertyDeletion;
    553  }
    554 
    555  void unmarkHasUnvisitedPropertyDeletion() {
    556    MOZ_ASSERT(isInitialized());
    557    MOZ_ASSERT(!isEmptyIteratorSingleton());
    558    MOZ_ASSERT(hasUnvisitedPropertyDeletion());
    559 
    560    flags_ &= ~Flags::HasUnvisitedPropertyDeletion;
    561  }
    562 
    563  bool hasUnvisitedPropertyDeletion() const {
    564    MOZ_ASSERT(isInitialized());
    565 
    566    return flags_ & Flags::HasUnvisitedPropertyDeletion;
    567  }
    568 
    569  // Indicates the native iterator may walk prototype properties.
    570  bool mayHavePrototypeProperties() {
    571    // If we can use indices for this iterator, we know it doesn't have
    572    // prototype properties, and so we use this as a check for prototype
    573    // properties.
    574    return !indicesAvailable() && !indicesSupported();
    575  }
    576 
    577  void disableIndices() {
    578    // Clear the IndicesAvailable flag so we won't use the indices on this
    579    // iterator, and ensure IndicesSupported is cleared as well, so we don't
    580    // re-request an iterator with indices. However, we leave the
    581    // IndicesAllocated flag because we need to free them later, and skip them
    582    // when looking for shapes.
    583    flags_ &= ~(Flags::IndicesAvailable | Flags::IndicesSupported);
    584  }
    585 
    586  void link(NativeIteratorListNode* other) {
    587    MOZ_ASSERT(isInitialized());
    588 
    589    // The shared iterator used for for-in with null/undefined is immutable and
    590    // shouldn't be linked.
    591    MOZ_ASSERT(!isEmptyIteratorSingleton());
    592 
    593    // A NativeIterator cannot appear in the enumerator list twice.
    594    MOZ_ASSERT(isUnlinked());
    595 
    596    setNext(other);
    597    setPrev(other->prev());
    598 
    599    other->prev()->setNext(this);
    600    other->setPrev(this);
    601  }
    602  void unlink() {
    603    MOZ_ASSERT(isInitialized());
    604    MOZ_ASSERT(!isEmptyIteratorSingleton());
    605 
    606    next()->setPrev(prev());
    607    prev()->setNext(next());
    608    setNext(nullptr);
    609    setPrev(nullptr);
    610  }
    611 
    612  void trace(JSTracer* trc);
    613 
    614  static constexpr size_t offsetOfObjectBeingIterated() {
    615    return offsetof(NativeIterator, objectBeingIterated_);
    616  }
    617 
    618  static constexpr size_t offsetOfProtoShapeCount() {
    619    return offsetof(NativeIterator, protoShapeCount_);
    620  }
    621 
    622  static constexpr size_t offsetOfPropertyCursor() {
    623    return offsetof(NativeIterator, propertyCursor_);
    624  }
    625 
    626  static constexpr size_t offsetOfPropertyCount() {
    627    return offsetof(NativeIterator, propertyCount_);
    628  }
    629 
    630  static constexpr size_t offsetOfOwnPropertyCount() {
    631    return offsetof(NativeIterator, ownPropertyCount_);
    632  }
    633 
    634  static constexpr size_t offsetOfFlags() {
    635    return offsetof(NativeIterator, flags_);
    636  }
    637 
    638  static constexpr size_t offsetOfObjectShape() {
    639    return offsetof(NativeIterator, objShape_);
    640  }
    641 
    642  static constexpr size_t offsetOfFirstProperty() {
    643    // Properties are stored directly after |this|.
    644    return sizeof(NativeIterator);
    645  }
    646 };
    647 
    648 class PropertyIteratorObject : public NativeObject {
    649  static const JSClassOps classOps_;
    650 
    651  enum { IteratorSlot, SlotCount };
    652 
    653 public:
    654  static const JSClass class_;
    655 
    656  NativeIterator* getNativeIterator() const {
    657    return maybePtrFromReservedSlot<NativeIterator>(IteratorSlot);
    658  }
    659  void initNativeIterator(js::NativeIterator* ni) {
    660    initReservedSlot(IteratorSlot, PrivateValue(ni));
    661  }
    662 
    663  size_t sizeOfMisc(mozilla::MallocSizeOf mallocSizeOf) const;
    664 
    665  static size_t offsetOfIteratorSlot() {
    666    return getFixedSlotOffset(IteratorSlot);
    667  }
    668 
    669 private:
    670  static void trace(JSTracer* trc, JSObject* obj);
    671  static void finalize(JS::GCContext* gcx, JSObject* obj);
    672 };
    673 
    674 class ArrayIteratorObject : public NativeObject {
    675 public:
    676  static const JSClass class_;
    677 };
    678 
    679 ArrayIteratorObject* NewArrayIteratorTemplate(JSContext* cx);
    680 ArrayIteratorObject* NewArrayIterator(JSContext* cx);
    681 
    682 class StringIteratorObject : public NativeObject {
    683 public:
    684  static const JSClass class_;
    685 };
    686 
    687 StringIteratorObject* NewStringIteratorTemplate(JSContext* cx);
    688 StringIteratorObject* NewStringIterator(JSContext* cx);
    689 
    690 class RegExpStringIteratorObject : public NativeObject {
    691 public:
    692  static const JSClass class_;
    693 };
    694 
    695 RegExpStringIteratorObject* NewRegExpStringIteratorTemplate(JSContext* cx);
    696 RegExpStringIteratorObject* NewRegExpStringIterator(JSContext* cx);
    697 
    698 #ifdef NIGHTLY_BUILD
    699 class IteratorRangeObject : public NativeObject {
    700 public:
    701  static const JSClass class_;
    702 };
    703 
    704 IteratorRangeObject* NewIteratorRange(JSContext* cx);
    705 #endif
    706 
    707 [[nodiscard]] bool EnumerateProperties(JSContext* cx, HandleObject obj,
    708                                       MutableHandleIdVector props);
    709 
    710 PropertyIteratorObject* LookupInIteratorCache(JSContext* cx, HandleObject obj);
    711 PropertyIteratorObject* LookupInShapeIteratorCache(JSContext* cx,
    712                                                   HandleObject obj);
    713 
    714 PropertyIteratorObject* GetIterator(JSContext* cx, HandleObject obj);
    715 PropertyIteratorObject* GetIteratorWithIndices(JSContext* cx, HandleObject obj);
    716 
    717 PropertyIteratorObject* GetIteratorForObjectKeys(JSContext* cx,
    718                                                 HandleObject obj);
    719 PropertyIteratorObject* GetIteratorWithIndicesForObjectKeys(JSContext* cx,
    720                                                            HandleObject obj);
    721 
    722 PropertyIteratorObject* ValueToIterator(JSContext* cx, HandleValue vp);
    723 
    724 void CloseIterator(JSObject* obj);
    725 
    726 bool IteratorCloseForException(JSContext* cx, HandleObject obj);
    727 
    728 void UnwindIteratorForUncatchableException(JSObject* obj);
    729 
    730 extern bool SuppressDeletedProperty(JSContext* cx, HandleObject obj, jsid id);
    731 
    732 extern bool SuppressDeletedElement(JSContext* cx, HandleObject obj,
    733                                   uint32_t index);
    734 
    735 #ifdef DEBUG
    736 extern void AssertDenseElementsNotIterated(NativeObject* obj);
    737 #else
    738 inline void AssertDenseElementsNotIterated(NativeObject* obj) {}
    739 #endif
    740 
    741 /*
    742 * IteratorMore() returns the next iteration value. If no value is available,
    743 * MagicValue(JS_NO_ITER_VALUE) is returned.
    744 */
    745 inline Value IteratorMore(JSObject* iterobj) {
    746  NativeIterator* ni =
    747      iterobj->as<PropertyIteratorObject>().getNativeIterator();
    748  return ni->nextIteratedValueAndAdvance();
    749 }
    750 
    751 /*
    752 * Create an object of the form { value: VALUE, done: DONE }.
    753 * ES 2017 draft 7.4.7.
    754 */
    755 extern PlainObject* CreateIterResultObject(JSContext* cx, HandleValue value,
    756                                           bool done);
    757 
    758 /*
    759 * Global Iterator constructor.
    760 * Iterator Helpers proposal 2.1.3.
    761 */
    762 class IteratorObject : public NativeObject {
    763 public:
    764  static const JSClass class_;
    765  static const JSClass protoClass_;
    766 
    767  static bool finishInit(JSContext* cx, HandleObject ctor, HandleObject proto);
    768 };
    769 
    770 /*
    771 * Wrapper for iterators created via Iterator.from.
    772 * Iterator Helpers proposal 2.1.3.3.1.1.
    773 */
    774 class WrapForValidIteratorObject : public NativeObject {
    775 public:
    776  static const JSClass class_;
    777 
    778  enum { IteratorSlot, NextMethodSlot, SlotCount };
    779 
    780  static_assert(
    781      IteratorSlot == WRAP_FOR_VALID_ITERATOR_ITERATOR_SLOT,
    782      "IteratedSlot must match self-hosting define for iterator object slot.");
    783 
    784  static_assert(
    785      NextMethodSlot == WRAP_FOR_VALID_ITERATOR_NEXT_METHOD_SLOT,
    786      "NextMethodSlot must match self-hosting define for next method slot.");
    787 };
    788 
    789 WrapForValidIteratorObject* NewWrapForValidIterator(JSContext* cx);
    790 
    791 /*
    792 * Generator-esque object returned by Iterator Helper methods.
    793 */
    794 class IteratorHelperObject : public NativeObject {
    795 public:
    796  static const JSClass class_;
    797 
    798  enum {
    799    // The implementation (an instance of one of the generators in
    800    // builtin/Iterator.js).
    801    // Never null.
    802    GeneratorSlot,
    803 
    804    // The [[UnderlyingIterator]] internal slot. Either an object or undefined
    805    // in the case of IteratorConcat. In the spec, the internal slot stores an
    806    // "Iterator Record", but our implementation only stores the actual iterator
    807    // object.
    808    UnderlyingIteratorSlot,
    809 
    810    SlotCount,
    811  };
    812 
    813  static_assert(GeneratorSlot == ITERATOR_HELPER_GENERATOR_SLOT,
    814                "GeneratorSlot must match self-hosting define for generator "
    815                "object slot.");
    816 
    817  static_assert(UnderlyingIteratorSlot ==
    818                    ITERATOR_HELPER_UNDERLYING_ITERATOR_SLOT,
    819                "UnderlyingIteratorSlot must match self-hosting define for "
    820                "underlying iterator slot.");
    821 };
    822 
    823 IteratorHelperObject* NewIteratorHelper(JSContext* cx);
    824 
    825 bool IterableToArray(JSContext* cx, HandleValue iterable,
    826                     MutableHandle<ArrayObject*> array);
    827 
    828 bool HasOptimizableArrayIteratorPrototype(JSContext* cx);
    829 
    830 enum class MustBePacked { No, Yes };
    831 
    832 template <MustBePacked Packed>
    833 bool IsArrayWithDefaultIterator(JSObject* obj, JSContext* cx);
    834 
    835 bool IsMapObjectWithDefaultIterator(JSObject* obj, JSContext* cx);
    836 bool IsSetObjectWithDefaultIterator(JSObject* obj, JSContext* cx);
    837 
    838 // Typed arrays and classes with an enumerate hook can have extra properties not
    839 // included in the shape's property map or the object's dense elements.
    840 static inline bool ClassCanHaveExtraEnumeratedProperties(const JSClass* clasp) {
    841  return IsTypedArrayClass(clasp) || clasp->getNewEnumerate() ||
    842         clasp->getEnumerate();
    843 }
    844 
    845 } /* namespace js */
    846 
    847 #endif /* vm_Iteration_h */