Bug 1842773 - Part 32: Allow constructing growable SharedArrayBuffers. r=sfink
[gecko.git] / js / src / vm / Iteration.h
blob92edaccd657776504f5cc0d76a2c002e96290529
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/. */
7 #ifndef vm_Iteration_h
8 #define vm_Iteration_h
11 * JavaScript iterators.
14 #include "mozilla/ArrayUtils.h"
15 #include "mozilla/MemoryReporting.h"
17 #include "builtin/SelfHostingDefines.h"
18 #include "gc/Barrier.h"
19 #include "vm/NativeObject.h"
20 #include "vm/TypedArrayObject.h"
23 * [SMDOC] For-in enumeration
25 * A for-in loop in JS iterates over the string-valued, enumerable
26 * property keys of an object and its prototype chain. The order in
27 * which keys appear is specified to the extent that implementations
28 * historically agreed, and implementation-defined beyond that. See
29 * https://tc39.es/ecma262/#sec-enumerate-object-properties for the
30 * gory details. Each key appears only once in the enumeration.
32 * We enumerate properties using PropertyEnumerator, which creates an
33 * ordered list of PropertyKeys, using ShapePropertyIter for native
34 * objects and calling enumerate hooks where necessary. This list is
35 * used to create a NativeIterator, which contains (among other
36 * things) a trailing array of strings representing the property keys
37 * of the object, and a cursor pointing into that array. This
38 * NativeIterator is wrapped in a PropertyIteratorObject, which is
39 * pushed by JSOp::Iter and used by JSOp::MoreIter and JSOp::EndIter.
41 * While active, a NativeIterator is registered in a doubly linked
42 * list, rooted in the compartment. When any property is deleted from
43 * an object, this list is used to remove the deleted property from
44 * any active enumerations. See SuppressDeletedProperty. This slows
45 * down deletion but speeds up enumeration, which is generally a good
46 * tradeoff.
48 * In many cases, objects with the same shape will have the same set
49 * of property keys. (The most common exception is objects with dense
50 * elements, which can be added or removed without changing the shape
51 * of the object.) In such cases, we can reuse an existing iterator by
52 * storing a pointer to the PropertyIteratorObject in the shape's
53 * |cache_| pointer. Before reusing an iterator, we have to verify
54 * that the prototype chain has not changed and no dense elements have
55 * been added, which is done by storing a trailing array of prototype
56 * shapes in the NativeIterator and comparing it against the shapes of
57 * the prototype chain.
59 * One of the most frequent uses of for-in loops is in loops that look
60 * like this, which iterate over each property of an object and do
61 * something with those values:
62 * for (var key in obj) {
63 * if (obj.hasOwnProperty(key)) {
64 * doSomethingWith(obj[key]);
65 * }
66 * }
67 * Most objects don't have any enumerable properties on the prototype
68 * chain. In such cases, we can speed up property access inside the
69 * loop by precomputing some information and storing it in the
70 * iterator. When we see a pattern like this in Ion, we generate a
71 * call to GetIteratorWithIndices instead of GetIterator. In this
72 * case, in addition to the list of property keys, PropertyEnumerator
73 * will try to generate a list of corresponding PropertyIndex values,
74 * which represent the location of the own property key in the object
75 * (fixed slot/dynamic slot/dense element + offset). This list will be
76 * stored in NativeIterator as yet another trailing array. When
77 * present, it can be used by Ion code to speed up property access
78 * inside for-in loops. See OptimizeIteratorIndices in
79 * IonAnalysis.cpp.
82 namespace js {
84 class ArrayObject;
85 class PlainObject;
86 class PropertyIteratorObject;
88 // A PropertyIndex stores information about the location of an own data
89 // property in a format that can be stored in a NativeIterator and consumed by
90 // jitcode to access properties without needing to use the megamorphic cache.
91 struct PropertyIndex {
92 private:
93 uint32_t asBits_;
95 public:
96 enum class Kind : uint32_t { DynamicSlot, FixedSlot, Element, Invalid };
98 PropertyIndex(Kind kind, uint32_t index) : asBits_(encode(kind, index)) {}
100 static PropertyIndex Invalid() { return PropertyIndex(Kind::Invalid, 0); }
102 static PropertyIndex ForElement(uint32_t index) {
103 return PropertyIndex(Kind::Element, index);
106 static PropertyIndex ForSlot(NativeObject* obj, uint32_t index) {
107 if (index < obj->numFixedSlots()) {
108 return PropertyIndex(Kind::FixedSlot, index);
109 } else {
110 return PropertyIndex(Kind::DynamicSlot, index - obj->numFixedSlots());
114 static constexpr uint32_t KindBits = 2;
116 static constexpr uint32_t IndexBits = 32 - KindBits;
117 static constexpr uint32_t IndexLimit = 1 << IndexBits;
118 static constexpr uint32_t IndexMask = (1 << IndexBits) - 1;
120 static constexpr uint32_t KindShift = IndexBits;
122 static_assert(NativeObject::MAX_FIXED_SLOTS < IndexLimit);
123 static_assert(NativeObject::MAX_SLOTS_COUNT < IndexLimit);
124 static_assert(NativeObject::MAX_DENSE_ELEMENTS_COUNT < IndexLimit);
126 private:
127 uint32_t encode(Kind kind, uint32_t index) {
128 MOZ_ASSERT(index < IndexLimit);
129 return (uint32_t(kind) << KindShift) | index;
132 public:
133 Kind kind() const { return Kind(asBits_ >> KindShift); }
134 uint32_t index() const { return asBits_ & IndexMask; }
137 using PropertyIndexVector = js::Vector<PropertyIndex, 8, js::TempAllocPolicy>;
139 struct NativeIterator;
141 class NativeIteratorListNode {
142 protected:
143 // While in compartment->enumerators, these form a doubly linked list.
144 NativeIteratorListNode* prev_ = nullptr;
145 NativeIteratorListNode* next_ = nullptr;
147 public:
148 NativeIteratorListNode* prev() { return prev_; }
149 NativeIteratorListNode* next() { return next_; }
151 void setPrev(NativeIteratorListNode* prev) { prev_ = prev; }
152 void setNext(NativeIteratorListNode* next) { next_ = next; }
154 static constexpr size_t offsetOfNext() {
155 return offsetof(NativeIteratorListNode, next_);
158 static constexpr size_t offsetOfPrev() {
159 return offsetof(NativeIteratorListNode, prev_);
162 private:
163 NativeIterator* asNativeIterator() {
164 return reinterpret_cast<NativeIterator*>(this);
167 friend class NativeIteratorListIter;
170 class NativeIteratorListHead : public NativeIteratorListNode {
171 private:
172 // Initialize a |Compartment::enumerators| sentinel.
173 NativeIteratorListHead() { prev_ = next_ = this; }
174 friend class JS::Compartment;
177 class NativeIteratorListIter {
178 private:
179 NativeIteratorListHead* head_;
180 NativeIteratorListNode* curr_;
182 public:
183 explicit NativeIteratorListIter(NativeIteratorListHead* head)
184 : head_(head), curr_(head->next()) {}
186 bool done() const { return curr_ == head_; }
188 NativeIterator* next() {
189 MOZ_ASSERT(!done());
190 NativeIterator* result = curr_->asNativeIterator();
191 curr_ = curr_->next();
192 return result;
196 // If an object only has own data properties, we can store a list of
197 // PropertyIndex that can be used in Ion to more efficiently access those
198 // properties in cases like `for (var key in obj) { ...obj[key]... }`.
199 enum class NativeIteratorIndices : uint32_t {
200 // The object being iterated does not support indices.
201 Unavailable = 0,
203 // The object being iterated supports indices, but none have been
204 // allocated, because it has not yet been iterated by Ion code that
205 // can use indices-based access.
206 AvailableOnRequest = 1,
208 // The object being iterated had indices allocated, but they were
209 // disabled due to a deleted property.
210 Disabled = 2,
212 // The object being iterated had indices allocated, and they are
213 // still valid.
214 Valid = 3
217 struct NativeIterator : public NativeIteratorListNode {
218 private:
219 // Object being iterated. Non-null except in NativeIterator sentinels,
220 // the empty iterator singleton (for iterating |null| or |undefined|), and
221 // inactive iterators.
222 GCPtr<JSObject*> objectBeingIterated_ = {};
224 // Internal iterator object.
225 const GCPtr<JSObject*> iterObj_ = {};
227 // The end of GCPtr<Shape*>s that appear directly after |this|, as part of an
228 // overall allocation that stores |*this|, shapes, iterated strings, and maybe
229 // indices. Once this has been fully initialized, it also equals the start of
230 // iterated strings.
231 GCPtr<Shape*>* shapesEnd_; // initialized by constructor
233 // The next property, pointing into an array of strings directly after any
234 // GCPtr<Shape*>s that appear directly after |*this|, as part of an overall
235 // allocation that stores |*this|, shapes, iterated strings, and maybe
236 // indices.
237 GCPtr<JSLinearString*>* propertyCursor_; // initialized by constructor
239 // The limit/end of properties to iterate. Once |this| has been fully
240 // initialized, it also equals the start of indices, if indices are present,
241 // or the end of the full allocation storing |*this|, shapes, and strings, if
242 // indices are not present. Beware! This value may change as properties are
243 // deleted from the observed object.
244 GCPtr<JSLinearString*>* propertiesEnd_; // initialized by constructor
246 HashNumber shapesHash_; // initialized by constructor
248 public:
249 // For cacheable native iterators, whether the iterator is currently
250 // active. Not serialized by XDR.
251 struct Flags {
252 // This flag is set when all shapes and properties associated with this
253 // NativeIterator have been initialized, such that |shapesEnd_|, in
254 // addition to being the end of shapes, is also the beginning of
255 // properties.
257 // This flag is only *not* set when a NativeIterator is in the process
258 // of being constructed. At such time |shapesEnd_| accounts only for
259 // shapes that have been initialized -- potentially none of them.
260 // Instead, |propertyCursor_| is initialized to the ultimate/actual
261 // start of properties and must be used instead of |propertiesBegin()|,
262 // which asserts that this flag is present to guard against misuse.
263 static constexpr uint32_t Initialized = 0x1;
265 // This flag indicates that this NativeIterator is currently being used
266 // to enumerate an object's properties and has not yet been closed.
267 static constexpr uint32_t Active = 0x2;
269 // This flag indicates that the object being enumerated by this
270 // |NativeIterator| had a property deleted from it before it was
271 // visited, forcing the properties array in this to be mutated to
272 // remove it.
273 static constexpr uint32_t HasUnvisitedPropertyDeletion = 0x4;
275 // Whether this is the shared empty iterator object used for iterating over
276 // null/undefined.
277 static constexpr uint32_t IsEmptyIteratorSingleton = 0x8;
279 // If any of these bits are set on a |NativeIterator|, it isn't
280 // currently reusable. (An active |NativeIterator| can't be stolen
281 // *right now*; a |NativeIterator| that's had its properties mutated
282 // can never be reused, because it would give incorrect results.)
283 static constexpr uint32_t NotReusable =
284 Active | HasUnvisitedPropertyDeletion;
287 private:
288 static constexpr uint32_t FlagsBits = 4;
289 static constexpr uint32_t IndicesBits = 2;
291 static constexpr uint32_t FlagsMask = (1 << FlagsBits) - 1;
293 static constexpr uint32_t PropCountShift = IndicesBits + FlagsBits;
294 static constexpr uint32_t PropCountBits = 32 - PropCountShift;
296 public:
297 static constexpr uint32_t IndicesShift = FlagsBits;
298 static constexpr uint32_t IndicesMask = ((1 << IndicesBits) - 1)
299 << IndicesShift;
301 static constexpr uint32_t PropCountLimit = 1 << PropCountBits;
303 private:
304 // Stores Flags bits and indices state in the lower bits and the initial
305 // property count above them.
306 uint32_t flagsAndCount_ = 0;
308 #ifdef DEBUG
309 // If true, this iterator may contain indexed properties that came from
310 // objects on the prototype chain. This is used by certain debug assertions.
311 bool maybeHasIndexedPropertiesFromProto_ = false;
312 #endif
314 // END OF PROPERTIES
316 // No further fields appear after here *in NativeIterator*, but this class is
317 // always allocated with space tacked on immediately after |this| to store
318 // shapes p to |shapesEnd_|, iterated property names after that up to
319 // |propertiesEnd_|, and maybe PropertyIndex values up to |indices_end()|.
321 public:
323 * Initialize a NativeIterator properly allocated for |props.length()|
324 * properties and |numShapes| shapes. If |indices| is non-null, also
325 * allocates room for |indices.length()| PropertyIndex values. In this case,
326 * |indices.length()| must equal |props.length()|.
328 * Despite being a constructor, THIS FUNCTION CAN REPORT ERRORS. Users
329 * MUST set |*hadError = false| on entry and consider |*hadError| on return
330 * to mean this function failed.
332 NativeIterator(JSContext* cx, Handle<PropertyIteratorObject*> propIter,
333 Handle<JSObject*> objBeingIterated, HandleIdVector props,
334 bool supportsIndices, PropertyIndexVector* indices,
335 uint32_t numShapes, bool* hadError);
337 JSObject* objectBeingIterated() const { return objectBeingIterated_; }
339 void initObjectBeingIterated(JSObject& obj) {
340 MOZ_ASSERT(!objectBeingIterated_);
341 objectBeingIterated_.init(&obj);
343 void clearObjectBeingIterated() {
344 MOZ_ASSERT(objectBeingIterated_);
345 objectBeingIterated_ = nullptr;
348 GCPtr<Shape*>* shapesBegin() const {
349 static_assert(
350 alignof(GCPtr<Shape*>) <= alignof(NativeIterator),
351 "NativeIterator must be aligned to begin storing "
352 "GCPtr<Shape*>s immediately after it with no required padding");
353 const NativeIterator* immediatelyAfter = this + 1;
354 auto* afterNonConst = const_cast<NativeIterator*>(immediatelyAfter);
355 return reinterpret_cast<GCPtr<Shape*>*>(afterNonConst);
358 GCPtr<Shape*>* shapesEnd() const { return shapesEnd_; }
360 uint32_t shapeCount() const {
361 return mozilla::PointerRangeSize(shapesBegin(), shapesEnd());
364 GCPtr<JSLinearString*>* propertiesBegin() const {
365 static_assert(
366 alignof(GCPtr<Shape*>) >= alignof(GCPtr<JSLinearString*>),
367 "GCPtr<JSLinearString*>s for properties must be able to appear "
368 "directly after any GCPtr<Shape*>s after this NativeIterator, "
369 "with no padding space required for correct alignment");
370 static_assert(
371 alignof(NativeIterator) >= alignof(GCPtr<JSLinearString*>),
372 "GCPtr<JSLinearString*>s for properties must be able to appear "
373 "directly after this NativeIterator when no GCPtr<Shape*>s are "
374 "present, with no padding space required for correct "
375 "alignment");
377 // We *could* just check the assertion below if we wanted, but the
378 // incompletely-initialized NativeIterator case matters for so little
379 // code that we prefer not imposing the condition-check on every single
380 // user.
381 MOZ_ASSERT(isInitialized(),
382 "NativeIterator must be initialized, or else |shapesEnd_| "
383 "isn't necessarily the start of properties and instead "
384 "|propertyCursor_| is");
386 return reinterpret_cast<GCPtr<JSLinearString*>*>(shapesEnd_);
389 GCPtr<JSLinearString*>* propertiesEnd() const { return propertiesEnd_; }
391 GCPtr<JSLinearString*>* nextProperty() const { return propertyCursor_; }
393 PropertyIndex* indicesBegin() const {
394 // PropertyIndex must be able to be appear directly after the properties
395 // array, with no padding required for correct alignment.
396 static_assert(alignof(GCPtr<JSLinearString*>) >= alignof(PropertyIndex));
397 return reinterpret_cast<PropertyIndex*>(propertiesEnd_);
400 PropertyIndex* indicesEnd() const {
401 MOZ_ASSERT(indicesState() == NativeIteratorIndices::Valid);
402 return indicesBegin() + numKeys() * sizeof(PropertyIndex);
405 MOZ_ALWAYS_INLINE JS::Value nextIteratedValueAndAdvance() {
406 if (propertyCursor_ >= propertiesEnd_) {
407 MOZ_ASSERT(propertyCursor_ == propertiesEnd_);
408 return JS::MagicValue(JS_NO_ITER_VALUE);
411 JSLinearString* str = *propertyCursor_;
412 incCursor();
413 return JS::StringValue(str);
416 void resetPropertyCursorForReuse() {
417 MOZ_ASSERT(isInitialized());
419 // This function is called unconditionally on IteratorClose, so
420 // unvisited properties might have been deleted, so we can't assert
421 // this NativeIterator is reusable. (Should we not bother resetting
422 // the cursor in that case?)
424 // Note: JIT code inlines |propertyCursor_| resetting when an iterator
425 // ends: see |CodeGenerator::visitIteratorEnd|.
426 propertyCursor_ = propertiesBegin();
429 bool previousPropertyWas(JS::Handle<JSLinearString*> str) {
430 MOZ_ASSERT(isInitialized());
431 return propertyCursor_ > propertiesBegin() && propertyCursor_[-1] == str;
434 size_t numKeys() const {
435 return mozilla::PointerRangeSize(propertiesBegin(), propertiesEnd());
438 void trimLastProperty() {
439 MOZ_ASSERT(isInitialized());
440 propertiesEnd_--;
442 // This invokes the pre barrier on this property, since it's no longer
443 // going to be marked, and it ensures that any existing remembered set
444 // entry will be dropped.
445 *propertiesEnd_ = nullptr;
447 // Indices are no longer valid.
448 disableIndices();
451 JSObject* iterObj() const { return iterObj_; }
453 void incCursor() {
454 MOZ_ASSERT(isInitialized());
455 propertyCursor_++;
458 HashNumber shapesHash() const { return shapesHash_; }
460 bool isInitialized() const { return flags() & Flags::Initialized; }
462 size_t allocationSize() const;
464 #ifdef DEBUG
465 void setMaybeHasIndexedPropertiesFromProto() {
466 maybeHasIndexedPropertiesFromProto_ = true;
468 bool maybeHasIndexedPropertiesFromProto() const {
469 return maybeHasIndexedPropertiesFromProto_;
471 #endif
473 private:
474 uint32_t flags() const { return flagsAndCount_ & FlagsMask; }
476 NativeIteratorIndices indicesState() const {
477 return NativeIteratorIndices((flagsAndCount_ & IndicesMask) >>
478 IndicesShift);
481 uint32_t initialPropertyCount() const {
482 return flagsAndCount_ >> PropCountShift;
485 static uint32_t initialFlagsAndCount(uint32_t count) {
486 // No flags are initially set.
487 MOZ_ASSERT(count < PropCountLimit);
488 return count << PropCountShift;
491 void setFlags(uint32_t flags) {
492 MOZ_ASSERT((flags & ~FlagsMask) == 0);
493 flagsAndCount_ = (flagsAndCount_ & ~FlagsMask) | flags;
496 void setIndicesState(NativeIteratorIndices indices) {
497 uint32_t indicesBits = uint32_t(indices) << IndicesShift;
498 flagsAndCount_ = (flagsAndCount_ & ~IndicesMask) | indicesBits;
501 bool indicesAllocated() const {
502 return indicesState() >= NativeIteratorIndices::Disabled;
505 void markInitialized() {
506 MOZ_ASSERT(flags() == 0);
507 setFlags(Flags::Initialized);
510 bool isUnlinked() const { return !prev_ && !next_; }
512 public:
513 // Whether this is the shared empty iterator object used for iterating over
514 // null/undefined.
515 bool isEmptyIteratorSingleton() const {
516 // Note: equivalent code is inlined in MacroAssembler::iteratorClose.
517 bool res = flags() & Flags::IsEmptyIteratorSingleton;
518 MOZ_ASSERT_IF(
519 res, flags() == (Flags::Initialized | Flags::IsEmptyIteratorSingleton));
520 MOZ_ASSERT_IF(res, !objectBeingIterated_);
521 MOZ_ASSERT_IF(res, initialPropertyCount() == 0);
522 MOZ_ASSERT_IF(res, shapeCount() == 0);
523 MOZ_ASSERT_IF(res, isUnlinked());
524 return res;
526 void markEmptyIteratorSingleton() {
527 flagsAndCount_ |= Flags::IsEmptyIteratorSingleton;
529 // isEmptyIteratorSingleton() has various debug assertions.
530 MOZ_ASSERT(isEmptyIteratorSingleton());
533 bool isActive() const {
534 MOZ_ASSERT(isInitialized());
536 return flags() & Flags::Active;
539 void markActive() {
540 MOZ_ASSERT(isInitialized());
541 MOZ_ASSERT(!isEmptyIteratorSingleton());
543 flagsAndCount_ |= Flags::Active;
546 void markInactive() {
547 MOZ_ASSERT(isInitialized());
548 MOZ_ASSERT(!isEmptyIteratorSingleton());
550 flagsAndCount_ &= ~Flags::Active;
553 bool isReusable() const {
554 MOZ_ASSERT(isInitialized());
556 // Cached NativeIterators are reusable if they're not currently active
557 // and their properties array hasn't been mutated, i.e. if only
558 // |Flags::Initialized| is set. Using |Flags::NotReusable| to test
559 // would also work, but this formulation is safer against memory
560 // corruption.
561 return flags() == Flags::Initialized;
564 void markHasUnvisitedPropertyDeletion() {
565 MOZ_ASSERT(isInitialized());
566 MOZ_ASSERT(!isEmptyIteratorSingleton());
568 flagsAndCount_ |= Flags::HasUnvisitedPropertyDeletion;
571 bool hasValidIndices() const {
572 return indicesState() == NativeIteratorIndices::Valid;
575 bool indicesAvailableOnRequest() const {
576 return indicesState() == NativeIteratorIndices::AvailableOnRequest;
579 // Indicates the native iterator may walk prototype properties.
580 bool mayHavePrototypeProperties() {
581 // If we can use indices for this iterator, we know it doesn't have
582 // prototype properties, and so we use this as a check for prototype
583 // properties.
584 return !hasValidIndices() && !indicesAvailableOnRequest();
587 void disableIndices() {
588 // If we have allocated indices, set the state to Disabled.
589 // This will ensure that we don't use them, but we still
590 // free them correctly.
591 if (indicesState() == NativeIteratorIndices::Valid) {
592 setIndicesState(NativeIteratorIndices::Disabled);
596 void link(NativeIteratorListNode* other) {
597 MOZ_ASSERT(isInitialized());
599 // The shared iterator used for for-in with null/undefined is immutable and
600 // shouldn't be linked.
601 MOZ_ASSERT(!isEmptyIteratorSingleton());
603 // A NativeIterator cannot appear in the enumerator list twice.
604 MOZ_ASSERT(isUnlinked());
606 setNext(other);
607 setPrev(other->prev());
609 other->prev()->setNext(this);
610 other->setPrev(this);
612 void unlink() {
613 MOZ_ASSERT(isInitialized());
614 MOZ_ASSERT(!isEmptyIteratorSingleton());
616 next()->setPrev(prev());
617 prev()->setNext(next());
618 setNext(nullptr);
619 setPrev(nullptr);
622 void trace(JSTracer* trc);
624 static constexpr size_t offsetOfObjectBeingIterated() {
625 return offsetof(NativeIterator, objectBeingIterated_);
628 static constexpr size_t offsetOfShapesEnd() {
629 return offsetof(NativeIterator, shapesEnd_);
632 static constexpr size_t offsetOfPropertyCursor() {
633 return offsetof(NativeIterator, propertyCursor_);
636 static constexpr size_t offsetOfPropertiesEnd() {
637 return offsetof(NativeIterator, propertiesEnd_);
640 static constexpr size_t offsetOfFlagsAndCount() {
641 return offsetof(NativeIterator, flagsAndCount_);
644 static constexpr size_t offsetOfFirstShape() {
645 // Shapes are stored directly after |this|.
646 return sizeof(NativeIterator);
650 class PropertyIteratorObject : public NativeObject {
651 static const JSClassOps classOps_;
653 enum { IteratorSlot, SlotCount };
655 public:
656 static const JSClass class_;
658 NativeIterator* getNativeIterator() const {
659 return maybePtrFromReservedSlot<NativeIterator>(IteratorSlot);
661 void initNativeIterator(js::NativeIterator* ni) {
662 initReservedSlot(IteratorSlot, PrivateValue(ni));
665 size_t sizeOfMisc(mozilla::MallocSizeOf mallocSizeOf) const;
667 static size_t offsetOfIteratorSlot() {
668 return getFixedSlotOffset(IteratorSlot);
671 private:
672 static void trace(JSTracer* trc, JSObject* obj);
673 static void finalize(JS::GCContext* gcx, JSObject* obj);
676 class ArrayIteratorObject : public NativeObject {
677 public:
678 static const JSClass class_;
681 ArrayIteratorObject* NewArrayIteratorTemplate(JSContext* cx);
682 ArrayIteratorObject* NewArrayIterator(JSContext* cx);
684 class StringIteratorObject : public NativeObject {
685 public:
686 static const JSClass class_;
689 StringIteratorObject* NewStringIteratorTemplate(JSContext* cx);
690 StringIteratorObject* NewStringIterator(JSContext* cx);
692 class RegExpStringIteratorObject : public NativeObject {
693 public:
694 static const JSClass class_;
697 RegExpStringIteratorObject* NewRegExpStringIteratorTemplate(JSContext* cx);
698 RegExpStringIteratorObject* NewRegExpStringIterator(JSContext* cx);
700 [[nodiscard]] bool EnumerateProperties(JSContext* cx, HandleObject obj,
701 MutableHandleIdVector props);
703 PropertyIteratorObject* LookupInIteratorCache(JSContext* cx, HandleObject obj);
704 PropertyIteratorObject* LookupInShapeIteratorCache(JSContext* cx,
705 HandleObject obj);
707 PropertyIteratorObject* GetIterator(JSContext* cx, HandleObject obj);
708 PropertyIteratorObject* GetIteratorWithIndices(JSContext* cx, HandleObject obj);
710 PropertyIteratorObject* ValueToIterator(JSContext* cx, HandleValue vp);
712 void CloseIterator(JSObject* obj);
714 bool IteratorCloseForException(JSContext* cx, HandleObject obj);
716 void UnwindIteratorForUncatchableException(JSObject* obj);
718 extern bool SuppressDeletedProperty(JSContext* cx, HandleObject obj, jsid id);
720 extern bool SuppressDeletedElement(JSContext* cx, HandleObject obj,
721 uint32_t index);
723 #ifdef DEBUG
724 extern void AssertDenseElementsNotIterated(NativeObject* obj);
725 #else
726 inline void AssertDenseElementsNotIterated(NativeObject* obj) {}
727 #endif
730 * IteratorMore() returns the next iteration value. If no value is available,
731 * MagicValue(JS_NO_ITER_VALUE) is returned.
733 inline Value IteratorMore(JSObject* iterobj) {
734 NativeIterator* ni =
735 iterobj->as<PropertyIteratorObject>().getNativeIterator();
736 return ni->nextIteratedValueAndAdvance();
740 * Create an object of the form { value: VALUE, done: DONE }.
741 * ES 2017 draft 7.4.7.
743 extern PlainObject* CreateIterResultObject(JSContext* cx, HandleValue value,
744 bool done);
747 * Global Iterator constructor.
748 * Iterator Helpers proposal 2.1.3.
750 class IteratorObject : public NativeObject {
751 public:
752 static const JSClass class_;
753 static const JSClass protoClass_;
757 * Wrapper for iterators created via Iterator.from.
758 * Iterator Helpers proposal 2.1.3.3.1.1.
760 class WrapForValidIteratorObject : public NativeObject {
761 public:
762 static const JSClass class_;
764 enum { IteratorSlot, NextMethodSlot, SlotCount };
766 static_assert(
767 IteratorSlot == WRAP_FOR_VALID_ITERATOR_ITERATOR_SLOT,
768 "IteratedSlot must match self-hosting define for iterator object slot.");
770 static_assert(
771 NextMethodSlot == WRAP_FOR_VALID_ITERATOR_NEXT_METHOD_SLOT,
772 "NextMethodSlot must match self-hosting define for next method slot.");
775 WrapForValidIteratorObject* NewWrapForValidIterator(JSContext* cx);
778 * Generator-esque object returned by Iterator Helper methods.
780 class IteratorHelperObject : public NativeObject {
781 public:
782 static const JSClass class_;
784 enum {
785 // The implementation (an instance of one of the generators in
786 // builtin/Iterator.js).
787 // Never null.
788 GeneratorSlot,
790 SlotCount,
793 static_assert(GeneratorSlot == ITERATOR_HELPER_GENERATOR_SLOT,
794 "GeneratorSlot must match self-hosting define for generator "
795 "object slot.");
798 IteratorHelperObject* NewIteratorHelper(JSContext* cx);
800 bool IterableToArray(JSContext* cx, HandleValue iterable,
801 MutableHandle<ArrayObject*> array);
803 // Typed arrays and classes with an enumerate hook can have extra properties not
804 // included in the shape's property map or the object's dense elements.
805 static inline bool ClassCanHaveExtraEnumeratedProperties(const JSClass* clasp) {
806 return IsTypedArrayClass(clasp) || clasp->getNewEnumerate() ||
807 clasp->getEnumerate();
810 } /* namespace js */
812 #endif /* vm_Iteration_h */