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/DebugOnly.h"
13 #include "mozilla/Likely.h"
14 #include "mozilla/Maybe.h"
15 #include "mozilla/MemoryReporting.h"
16 #include "mozilla/PodOperations.h"
17 
18 #include <algorithm>
19 #include <new>
20 
21 #include "jstypes.h"
22 
23 #include "builtin/Array.h"
24 #include "builtin/SelfHostingDefines.h"
25 #include "ds/Sort.h"
26 #include "gc/FreeOp.h"
27 #include "gc/Marking.h"
28 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
29 #include "js/PropertySpec.h"
30 #include "js/Proxy.h"
31 #include "util/DifferentialTesting.h"
32 #include "util/Poison.h"
33 #include "vm/BytecodeUtil.h"
34 #include "vm/GlobalObject.h"
35 #include "vm/Interpreter.h"
36 #include "vm/JSAtom.h"
37 #include "vm/JSContext.h"
38 #include "vm/JSObject.h"
39 #include "vm/JSScript.h"
40 #include "vm/NativeObject.h"  // js::PlainObject
41 #include "vm/Shape.h"
42 #include "vm/TypedArrayObject.h"
43 #include "vm/WellKnownAtom.h"  // js_*_str
44 
45 #include "vm/Compartment-inl.h"
46 #include "vm/JSScript-inl.h"
47 #include "vm/NativeObject-inl.h"
48 #include "vm/PlainObject-inl.h"  // js::PlainObject::createWithTemplate
49 #include "vm/Stack-inl.h"
50 #include "vm/StringType-inl.h"
51 
52 using namespace js;
53 
54 using mozilla::ArrayEqual;
55 using mozilla::DebugOnly;
56 using mozilla::Maybe;
57 using mozilla::PodCopy;
58 
59 using RootedPropertyIteratorObject = Rooted<PropertyIteratorObject*>;
60 
61 static const gc::AllocKind ITERATOR_FINALIZE_KIND =
62     gc::AllocKind::OBJECT2_BACKGROUND;
63 
64 // Beware!  This function may have to trace incompletely-initialized
65 // |NativeIterator| allocations if the |IdToString| in that constructor recurs
66 // into this code.
trace(JSTracer * trc)67 void NativeIterator::trace(JSTracer* trc) {
68   TraceNullableEdge(trc, &objectBeingIterated_, "objectBeingIterated_");
69   TraceNullableEdge(trc, &iterObj_, "iterObj");
70 
71   // The limits below are correct at every instant of |NativeIterator|
72   // initialization, with the end-pointer incremented as each new shape is
73   // created, so they're safe to use here.
74   std::for_each(shapesBegin(), shapesEnd(), [trc](GCPtrShape& shape) {
75     TraceEdge(trc, &shape, "iterator_shape");
76   });
77 
78   // But as properties must be created *before* shapes, |propertiesBegin()|
79   // that depends on |shapesEnd()| having its final value can't safely be
80   // used.  Until this is fully initialized, use |propertyCursor_| instead,
81   // which points at the start of properties even in partially initialized
82   // |NativeIterator|s.  (|propertiesEnd()| is safe at all times with respect
83   // to the properly-chosen beginning.)
84   //
85   // Note that we must trace all properties (not just those not yet visited,
86   // or just visited, due to |NativeIterator::previousPropertyWas|) for
87   // |NativeIterator|s to be reusable.
88   GCPtrLinearString* begin =
89       MOZ_LIKELY(isInitialized()) ? propertiesBegin() : propertyCursor_;
90   std::for_each(begin, propertiesEnd(), [trc](GCPtrLinearString& prop) {
91     // Properties begin life non-null and never *become*
92     // null.  (Deletion-suppression will shift trailing
93     // properties over a deleted property in the properties
94     // array, but it doesn't null them out.)
95     TraceEdge(trc, &prop, "prop");
96   });
97 }
98 
99 using PropertyKeySet = GCHashSet<PropertyKey, DefaultHasher<PropertyKey>>;
100 
101 template <bool CheckForDuplicates>
Enumerate(JSContext * cx,HandleObject pobj,jsid id,bool enumerable,unsigned flags,MutableHandle<PropertyKeySet> visited,MutableHandleIdVector props)102 static inline bool Enumerate(JSContext* cx, HandleObject pobj, jsid id,
103                              bool enumerable, unsigned flags,
104                              MutableHandle<PropertyKeySet> visited,
105                              MutableHandleIdVector props) {
106   if (CheckForDuplicates) {
107     // If we've already seen this, we definitely won't add it.
108     PropertyKeySet::AddPtr p = visited.lookupForAdd(id);
109     if (MOZ_UNLIKELY(!!p)) {
110       return true;
111     }
112 
113     // It's not necessary to add properties to the hash set at the end of
114     // the prototype chain, but custom enumeration behaviors might return
115     // duplicated properties, so always add in such cases.
116     if (pobj->is<ProxyObject>() || pobj->staticPrototype() ||
117         pobj->getClass()->getNewEnumerate()) {
118       if (!visited.add(p, id)) {
119         return false;
120       }
121     }
122   }
123 
124   if (!enumerable && !(flags & JSITER_HIDDEN)) {
125     return true;
126   }
127 
128   // Symbol-keyed properties and nonenumerable properties are skipped unless
129   // the caller specifically asks for them. A caller can also filter out
130   // non-symbols by asking for JSITER_SYMBOLSONLY. PrivateName symbols are
131   // skipped unless JSITER_PRIVATE is passed.
132   if (id.isSymbol()) {
133     if (!(flags & JSITER_SYMBOLS)) {
134       return true;
135     }
136     if (!(flags & JSITER_PRIVATE) && id.isPrivateName()) {
137       return true;
138     }
139   } else {
140     if ((flags & JSITER_SYMBOLSONLY)) {
141       return true;
142     }
143   }
144 
145   return props.append(id);
146 }
147 
EnumerateExtraProperties(JSContext * cx,HandleObject obj,unsigned flags,MutableHandle<PropertyKeySet> visited,MutableHandleIdVector props)148 static bool EnumerateExtraProperties(JSContext* cx, HandleObject obj,
149                                      unsigned flags,
150                                      MutableHandle<PropertyKeySet> visited,
151                                      MutableHandleIdVector props) {
152   MOZ_ASSERT(obj->getClass()->getNewEnumerate());
153 
154   RootedIdVector properties(cx);
155   bool enumerableOnly = !(flags & JSITER_HIDDEN);
156   if (!obj->getClass()->getNewEnumerate()(cx, obj, &properties,
157                                           enumerableOnly)) {
158     return false;
159   }
160 
161   RootedId id(cx);
162   for (size_t n = 0; n < properties.length(); n++) {
163     id = properties[n];
164 
165     // The enumerate hook does not indicate whether the properties
166     // it returns are enumerable or not. Since we already passed
167     // `enumerableOnly` to the hook to filter out non-enumerable
168     // properties, it doesn't really matter what we pass here.
169     bool enumerable = true;
170     if (!Enumerate<true>(cx, obj, id, enumerable, flags, visited, props)) {
171       return false;
172     }
173   }
174 
175   return true;
176 }
177 
SortComparatorIntegerIds(jsid a,jsid b,bool * lessOrEqualp)178 static bool SortComparatorIntegerIds(jsid a, jsid b, bool* lessOrEqualp) {
179   uint32_t indexA, indexB;
180   MOZ_ALWAYS_TRUE(IdIsIndex(a, &indexA));
181   MOZ_ALWAYS_TRUE(IdIsIndex(b, &indexB));
182   *lessOrEqualp = (indexA <= indexB);
183   return true;
184 }
185 
186 template <bool CheckForDuplicates>
EnumerateNativeProperties(JSContext * cx,HandleNativeObject pobj,unsigned flags,MutableHandle<PropertyKeySet> visited,MutableHandleIdVector props)187 static bool EnumerateNativeProperties(JSContext* cx, HandleNativeObject pobj,
188                                       unsigned flags,
189                                       MutableHandle<PropertyKeySet> visited,
190                                       MutableHandleIdVector props) {
191   bool enumerateSymbols;
192   if (flags & JSITER_SYMBOLSONLY) {
193     enumerateSymbols = true;
194   } else {
195     // Collect any dense elements from this object.
196     size_t firstElemIndex = props.length();
197     size_t initlen = pobj->getDenseInitializedLength();
198     const Value* vp = pobj->getDenseElements();
199     bool hasHoles = false;
200     for (size_t i = 0; i < initlen; ++i, ++vp) {
201       if (vp->isMagic(JS_ELEMENTS_HOLE)) {
202         hasHoles = true;
203       } else {
204         // Dense arrays never get so large that i would not fit into an
205         // integer id.
206         if (!Enumerate<CheckForDuplicates>(cx, pobj, INT_TO_JSID(i),
207                                            /* enumerable = */ true, flags,
208                                            visited, props)) {
209           return false;
210         }
211       }
212     }
213 
214     // Collect any typed array or shared typed array elements from this
215     // object.
216     if (pobj->is<TypedArrayObject>()) {
217       size_t len = pobj->as<TypedArrayObject>().length();
218 
219       // Fail early if the typed array is enormous, because this will be very
220       // slow and will likely report OOM. This also means we don't need to
221       // handle indices greater than JSID_INT_MAX in the loop below.
222       static_assert(JSID_INT_MAX == INT32_MAX);
223       if (len > INT32_MAX) {
224         ReportOutOfMemory(cx);
225         return false;
226       }
227 
228       for (size_t i = 0; i < len; i++) {
229         if (!Enumerate<CheckForDuplicates>(cx, pobj, INT_TO_JSID(i),
230                                            /* enumerable = */ true, flags,
231                                            visited, props)) {
232           return false;
233         }
234       }
235     }
236 
237     // Collect any sparse elements from this object.
238     bool isIndexed = pobj->isIndexed();
239     if (isIndexed) {
240       // If the dense elements didn't have holes, we don't need to include
241       // them in the sort.
242       if (!hasHoles) {
243         firstElemIndex = props.length();
244       }
245 
246       for (ShapePropertyIter<NoGC> iter(pobj->shape()); !iter.done(); iter++) {
247         jsid id = iter->key();
248         uint32_t dummy;
249         if (IdIsIndex(id, &dummy)) {
250           if (!Enumerate<CheckForDuplicates>(cx, pobj, id, iter->enumerable(),
251                                              flags, visited, props)) {
252             return false;
253           }
254         }
255       }
256 
257       MOZ_ASSERT(firstElemIndex <= props.length());
258 
259       jsid* ids = props.begin() + firstElemIndex;
260       size_t n = props.length() - firstElemIndex;
261 
262       RootedIdVector tmp(cx);
263       if (!tmp.resize(n)) {
264         return false;
265       }
266       PodCopy(tmp.begin(), ids, n);
267 
268       if (!MergeSort(ids, n, tmp.begin(), SortComparatorIntegerIds)) {
269         return false;
270       }
271     }
272 
273     size_t initialLength = props.length();
274 
275     /* Collect all unique property names from this object's shape. */
276     bool symbolsFound = false;
277     for (ShapePropertyIter<NoGC> iter(pobj->shape()); !iter.done(); iter++) {
278       jsid id = iter->key();
279 
280       if (id.isSymbol()) {
281         symbolsFound = true;
282         continue;
283       }
284 
285       uint32_t dummy;
286       if (isIndexed && IdIsIndex(id, &dummy)) {
287         continue;
288       }
289 
290       if (!Enumerate<CheckForDuplicates>(cx, pobj, id, iter->enumerable(),
291                                          flags, visited, props)) {
292         return false;
293       }
294     }
295     std::reverse(props.begin() + initialLength, props.end());
296 
297     enumerateSymbols = symbolsFound && (flags & JSITER_SYMBOLS);
298   }
299 
300   if (enumerateSymbols) {
301     // Do a second pass to collect symbols. ES6 draft rev 25 (2014 May 22)
302     // 9.1.12 requires that all symbols appear after all strings in the
303     // result.
304     size_t initialLength = props.length();
305     for (ShapePropertyIter<NoGC> iter(pobj->shape()); !iter.done(); iter++) {
306       jsid id = iter->key();
307       if (id.isSymbol()) {
308         if (!Enumerate<CheckForDuplicates>(cx, pobj, id, iter->enumerable(),
309                                            flags, visited, props)) {
310           return false;
311         }
312       }
313     }
314     std::reverse(props.begin() + initialLength, props.end());
315   }
316 
317   return true;
318 }
319 
EnumerateNativeProperties(JSContext * cx,HandleNativeObject pobj,unsigned flags,MutableHandle<PropertyKeySet> visited,MutableHandleIdVector props,bool checkForDuplicates)320 static bool EnumerateNativeProperties(JSContext* cx, HandleNativeObject pobj,
321                                       unsigned flags,
322                                       MutableHandle<PropertyKeySet> visited,
323                                       MutableHandleIdVector props,
324                                       bool checkForDuplicates) {
325   if (checkForDuplicates) {
326     return EnumerateNativeProperties<true>(cx, pobj, flags, visited, props);
327   }
328   return EnumerateNativeProperties<false>(cx, pobj, flags, visited, props);
329 }
330 
331 template <bool CheckForDuplicates>
EnumerateProxyProperties(JSContext * cx,HandleObject pobj,unsigned flags,MutableHandle<PropertyKeySet> visited,MutableHandleIdVector props)332 static bool EnumerateProxyProperties(JSContext* cx, HandleObject pobj,
333                                      unsigned flags,
334                                      MutableHandle<PropertyKeySet> visited,
335                                      MutableHandleIdVector props) {
336   MOZ_ASSERT(pobj->is<ProxyObject>());
337 
338   RootedIdVector proxyProps(cx);
339 
340   if (flags & JSITER_HIDDEN || flags & JSITER_SYMBOLS) {
341     // This gets all property keys, both strings and symbols. The call to
342     // Enumerate in the loop below will filter out unwanted keys, per the
343     // flags.
344     if (!Proxy::ownPropertyKeys(cx, pobj, &proxyProps)) {
345       return false;
346     }
347 
348     Rooted<mozilla::Maybe<PropertyDescriptor>> desc(cx);
349     for (size_t n = 0, len = proxyProps.length(); n < len; n++) {
350       bool enumerable = false;
351 
352       // We need to filter, if the caller just wants enumerable symbols.
353       if (!(flags & JSITER_HIDDEN)) {
354         if (!Proxy::getOwnPropertyDescriptor(cx, pobj, proxyProps[n], &desc)) {
355           return false;
356         }
357         enumerable = desc.isSome() && desc->enumerable();
358       }
359 
360       if (!Enumerate<CheckForDuplicates>(cx, pobj, proxyProps[n], enumerable,
361                                          flags, visited, props)) {
362         return false;
363       }
364     }
365 
366     return true;
367   }
368 
369   // Returns enumerable property names (no symbols).
370   if (!Proxy::getOwnEnumerablePropertyKeys(cx, pobj, &proxyProps)) {
371     return false;
372   }
373 
374   for (size_t n = 0, len = proxyProps.length(); n < len; n++) {
375     if (!Enumerate<CheckForDuplicates>(cx, pobj, proxyProps[n], true, flags,
376                                        visited, props)) {
377       return false;
378     }
379   }
380 
381   return true;
382 }
383 
384 #ifdef DEBUG
385 
386 struct SortComparatorIds {
387   JSContext* const cx;
388 
SortComparatorIdsSortComparatorIds389   explicit SortComparatorIds(JSContext* cx) : cx(cx) {}
390 
operator ()SortComparatorIds391   bool operator()(jsid aArg, jsid bArg, bool* lessOrEqualp) {
392     RootedId a(cx, aArg);
393     RootedId b(cx, bArg);
394 
395     // Pick an arbitrary order on jsids that is as stable as possible
396     // across executions.
397     if (a == b) {
398       *lessOrEqualp = true;
399       return true;
400     }
401 
402     size_t ta = JSID_BITS(a.get()) & JSID_TYPE_MASK;
403     size_t tb = JSID_BITS(b.get()) & JSID_TYPE_MASK;
404     if (ta != tb) {
405       *lessOrEqualp = (ta <= tb);
406       return true;
407     }
408 
409     if (JSID_IS_INT(a)) {
410       *lessOrEqualp = (JSID_TO_INT(a) <= JSID_TO_INT(b));
411       return true;
412     }
413 
414     RootedString astr(cx), bstr(cx);
415     if (a.isSymbol()) {
416       MOZ_ASSERT(b.isSymbol());
417       JS::SymbolCode ca = a.toSymbol()->code();
418       JS::SymbolCode cb = b.toSymbol()->code();
419       if (ca != cb) {
420         *lessOrEqualp = uint32_t(ca) <= uint32_t(cb);
421         return true;
422       }
423       MOZ_ASSERT(ca == JS::SymbolCode::PrivateNameSymbol ||
424                  ca == JS::SymbolCode::InSymbolRegistry ||
425                  ca == JS::SymbolCode::UniqueSymbol);
426       astr = a.toSymbol()->description();
427       bstr = b.toSymbol()->description();
428       if (!astr || !bstr) {
429         *lessOrEqualp = !astr;
430         return true;
431       }
432 
433       // Fall through to string comparison on the descriptions. The sort
434       // order is nondeterministic if two different unique symbols have
435       // the same description.
436     } else {
437       astr = IdToString(cx, a);
438       if (!astr) {
439         return false;
440       }
441       bstr = IdToString(cx, b);
442       if (!bstr) {
443         return false;
444       }
445     }
446 
447     int32_t result;
448     if (!CompareStrings(cx, astr, bstr, &result)) {
449       return false;
450     }
451 
452     *lessOrEqualp = (result <= 0);
453     return true;
454   }
455 };
456 
457 #endif /* DEBUG */
458 
Snapshot(JSContext * cx,HandleObject pobj_,unsigned flags,MutableHandleIdVector props)459 static bool Snapshot(JSContext* cx, HandleObject pobj_, unsigned flags,
460                      MutableHandleIdVector props) {
461   Rooted<PropertyKeySet> visited(cx, PropertyKeySet(cx));
462   RootedObject pobj(cx, pobj_);
463 
464   // Don't check for duplicates if we're only interested in own properties.
465   // This does the right thing for most objects: native objects don't have
466   // duplicate property ids and we allow the [[OwnPropertyKeys]] proxy trap to
467   // return duplicates.
468   //
469   // The only special case is when the object has a newEnumerate hook: it
470   // can return duplicate properties and we have to filter them. This is
471   // handled below.
472   bool checkForDuplicates = !(flags & JSITER_OWNONLY);
473 
474   do {
475     if (pobj->getClass()->getNewEnumerate()) {
476       if (!EnumerateExtraProperties(cx, pobj, flags, &visited, props)) {
477         return false;
478       }
479 
480       if (pobj->is<NativeObject>()) {
481         if (!EnumerateNativeProperties(cx, pobj.as<NativeObject>(), flags,
482                                        &visited, props, true)) {
483           return false;
484         }
485       }
486 
487     } else if (pobj->is<NativeObject>()) {
488       // Give the object a chance to resolve all lazy properties
489       if (JSEnumerateOp enumerate = pobj->getClass()->getEnumerate()) {
490         if (!enumerate(cx, pobj.as<NativeObject>())) {
491           return false;
492         }
493       }
494       if (!EnumerateNativeProperties(cx, pobj.as<NativeObject>(), flags,
495                                      &visited, props, checkForDuplicates)) {
496         return false;
497       }
498     } else if (pobj->is<ProxyObject>()) {
499       if (checkForDuplicates) {
500         if (!EnumerateProxyProperties<true>(cx, pobj, flags, &visited, props)) {
501           return false;
502         }
503       } else {
504         if (!EnumerateProxyProperties<false>(cx, pobj, flags, &visited,
505                                              props)) {
506           return false;
507         }
508       }
509     } else {
510       MOZ_CRASH("non-native objects must have an enumerate op");
511     }
512 
513     if (flags & JSITER_OWNONLY) {
514       break;
515     }
516 
517     if (!GetPrototype(cx, pobj, &pobj)) {
518       return false;
519     }
520 
521     // The [[Prototype]] chain might be cyclic.
522     if (!CheckForInterrupt(cx)) {
523       return false;
524     }
525   } while (pobj != nullptr);
526 
527 #ifdef DEBUG
528   if (js::SupportDifferentialTesting()) {
529     /*
530      * In some cases the enumeration order for an object depends on the
531      * execution mode (interpreter vs. JIT), especially for native objects
532      * with a class enumerate hook (where resolving a property changes the
533      * resulting enumeration order). These aren't really bugs, but the
534      * differences can change the generated output and confuse correctness
535      * fuzzers, so we sort the ids if such a fuzzer is running.
536      *
537      * We don't do this in the general case because (a) doing so is slow,
538      * and (b) it also breaks the web, which expects enumeration order to
539      * follow the order in which properties are added, in certain cases.
540      * Since ECMA does not specify an enumeration order for objects, both
541      * behaviors are technically correct to do.
542      */
543 
544     jsid* ids = props.begin();
545     size_t n = props.length();
546 
547     RootedIdVector tmp(cx);
548     if (!tmp.resize(n)) {
549       return false;
550     }
551     PodCopy(tmp.begin(), ids, n);
552 
553     if (!MergeSort(ids, n, tmp.begin(), SortComparatorIds(cx))) {
554       return false;
555     }
556   }
557 #endif
558 
559   return true;
560 }
561 
GetPropertyKeys(JSContext * cx,HandleObject obj,unsigned flags,MutableHandleIdVector props)562 JS_PUBLIC_API bool js::GetPropertyKeys(JSContext* cx, HandleObject obj,
563                                        unsigned flags,
564                                        MutableHandleIdVector props) {
565   return Snapshot(cx, obj,
566                   flags & (JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS |
567                            JSITER_SYMBOLSONLY | JSITER_PRIVATE),
568                   props);
569 }
570 
RegisterEnumerator(ObjectRealm & realm,NativeIterator * ni)571 static inline void RegisterEnumerator(ObjectRealm& realm, NativeIterator* ni) {
572   // Register non-escaping native enumerators (for-in) with the current
573   // context.
574   ni->link(realm.enumerators);
575 
576   MOZ_ASSERT(!ni->isActive());
577   ni->markActive();
578 }
579 
NewPropertyIteratorObject(JSContext * cx)580 static PropertyIteratorObject* NewPropertyIteratorObject(JSContext* cx) {
581   const JSClass* clasp = &PropertyIteratorObject::class_;
582   RootedShape shape(cx, SharedShape::getInitialShape(cx, clasp, cx->realm(),
583                                                      TaggedProto(nullptr),
584                                                      ITERATOR_FINALIZE_KIND));
585   if (!shape) {
586     return nullptr;
587   }
588 
589   JSObject* obj;
590   JS_TRY_VAR_OR_RETURN_NULL(
591       cx, obj,
592       NativeObject::create(cx, ITERATOR_FINALIZE_KIND,
593                            GetInitialHeap(GenericObject, clasp), shape));
594 
595   PropertyIteratorObject* res = &obj->as<PropertyIteratorObject>();
596 
597   // CodeGenerator::visitIteratorStartO assumes the iterator object is not
598   // inside the nursery when deciding whether a barrier is necessary.
599   MOZ_ASSERT(!js::gc::IsInsideNursery(res));
600 
601   MOZ_ASSERT(res->numFixedSlots() == PropertyIteratorObject::NUM_FIXED_SLOTS);
602   return res;
603 }
604 
NumTrailingWords(size_t propertyCount,size_t shapeCount)605 static inline size_t NumTrailingWords(size_t propertyCount, size_t shapeCount) {
606   static_assert(sizeof(GCPtrLinearString) == sizeof(uintptr_t));
607   static_assert(sizeof(GCPtrShape) == sizeof(uintptr_t));
608   return propertyCount + shapeCount;
609 }
610 
AllocationSize(size_t propertyCount,size_t shapeCount)611 static inline size_t AllocationSize(size_t propertyCount, size_t shapeCount) {
612   return sizeof(NativeIterator) +
613          (NumTrailingWords(propertyCount, shapeCount) * sizeof(uintptr_t));
614 }
615 
CreatePropertyIterator(JSContext * cx,Handle<JSObject * > objBeingIterated,HandleIdVector props,uint32_t numShapes,HashNumber shapesHash)616 static PropertyIteratorObject* CreatePropertyIterator(
617     JSContext* cx, Handle<JSObject*> objBeingIterated, HandleIdVector props,
618     uint32_t numShapes, HashNumber shapesHash) {
619   if (props.length() > NativeIterator::PropCountLimit) {
620     ReportAllocationOverflow(cx);
621     return nullptr;
622   }
623 
624   Rooted<PropertyIteratorObject*> propIter(cx, NewPropertyIteratorObject(cx));
625   if (!propIter) {
626     return nullptr;
627   }
628 
629   void* mem = cx->pod_malloc_with_extra<NativeIterator, uintptr_t>(
630       NumTrailingWords(props.length(), numShapes));
631   if (!mem) {
632     return nullptr;
633   }
634 
635   // This also registers |ni| with |propIter|.
636   bool hadError = false;
637   NativeIterator* ni = new (mem) NativeIterator(
638       cx, propIter, objBeingIterated, props, numShapes, shapesHash, &hadError);
639   if (hadError) {
640     return nullptr;
641   }
642 
643   ObjectRealm& realm = objBeingIterated ? ObjectRealm::get(objBeingIterated)
644                                         : ObjectRealm::get(propIter);
645   RegisterEnumerator(realm, ni);
646 
647   return propIter;
648 }
649 
650 /**
651  * Initialize a sentinel NativeIterator whose purpose is only to act as the
652  * start/end of the circular linked list of NativeIterators in
653  * ObjectRealm::enumerators.
654  */
NativeIterator()655 NativeIterator::NativeIterator() {
656   // Do our best to enforce that nothing in |this| except the two fields set
657   // below is ever observed.
658   AlwaysPoison(static_cast<void*>(this), JS_NEW_NATIVE_ITERATOR_PATTERN,
659                sizeof(*this), MemCheckKind::MakeUndefined);
660 
661   // These are the only two fields in sentinel NativeIterators that are
662   // examined, in ObjectRealm::traceWeakNativeIterators. Everything else is
663   // only examined *if* it's a NativeIterator being traced by a
664   // PropertyIteratorObject that owns it, and nothing owns this iterator.
665   prev_ = next_ = this;
666 }
667 
allocateSentinel(JSContext * cx)668 NativeIterator* NativeIterator::allocateSentinel(JSContext* cx) {
669   NativeIterator* ni = js_new<NativeIterator>();
670   if (!ni) {
671     ReportOutOfMemory(cx);
672     return nullptr;
673   }
674 
675   return ni;
676 }
677 
HashIteratorShape(Shape * shape)678 static HashNumber HashIteratorShape(Shape* shape) {
679   return DefaultHasher<Shape*>::hash(shape);
680 }
681 
682 /**
683  * Initialize a fresh NativeIterator.
684  *
685  * This definition is a bit tricky: some parts of initializing are fallible, so
686  * as we initialize, we must carefully keep this in GC-safe state (see
687  * NativeIterator::trace).
688  */
NativeIterator(JSContext * cx,Handle<PropertyIteratorObject * > propIter,Handle<JSObject * > objBeingIterated,HandleIdVector props,uint32_t numShapes,HashNumber shapesHash,bool * hadError)689 NativeIterator::NativeIterator(JSContext* cx,
690                                Handle<PropertyIteratorObject*> propIter,
691                                Handle<JSObject*> objBeingIterated,
692                                HandleIdVector props, uint32_t numShapes,
693                                HashNumber shapesHash, bool* hadError)
694     : objectBeingIterated_(objBeingIterated),
695       iterObj_(propIter),
696       // NativeIterator initially acts (before full initialization) as if it
697       // contains no shapes...
698       shapesEnd_(shapesBegin()),
699       // ...and no properties.
700       propertyCursor_(
701           reinterpret_cast<GCPtrLinearString*>(shapesBegin() + numShapes)),
702       propertiesEnd_(propertyCursor_),
703       shapesHash_(shapesHash),
704       flagsAndCount_(
705           initialFlagsAndCount(props.length()))  // note: no Flags::Initialized
706 {
707   // If there are shapes, the object and all objects on its prototype chain must
708   // be native objects. See CanCompareIterableObjectToCache.
709   MOZ_ASSERT_IF(numShapes > 0,
710                 objBeingIterated && objBeingIterated->is<NativeObject>());
711 
712   MOZ_ASSERT(!*hadError);
713 
714   // NOTE: This must be done first thing: The caller can't free `this` on error
715   //       because it has GCPtr fields whose barriers have already fired; the
716   //       store buffer has pointers to them. Only the GC can free `this` (via
717   //       PropertyIteratorObject::finalize).
718   propIter->setNativeIterator(this);
719 
720   // The GC asserts on finalization that `this->allocationSize()` matches the
721   // `nbytes` passed to `AddCellMemory`. So once these lines run, we must make
722   // `this->allocationSize()` correct. That means infallibly initializing the
723   // shapes. It's OK for the constructor to fail after that.
724   size_t nbytes = AllocationSize(props.length(), numShapes);
725   AddCellMemory(propIter, nbytes, MemoryUse::NativeIterator);
726 
727   if (numShapes > 0) {
728     // Construct shapes into the shapes array.  Also recompute the shapesHash,
729     // which incorporates Shape* addresses that could have changed during a GC
730     // triggered in (among other places) |IdToString| above.
731     JSObject* pobj = objBeingIterated;
732 #ifdef DEBUG
733     uint32_t i = 0;
734 #endif
735     HashNumber shapesHash = 0;
736     do {
737       MOZ_ASSERT(pobj->is<NativeObject>());
738       Shape* shape = pobj->shape();
739       new (shapesEnd_) GCPtrShape(shape);
740       shapesEnd_++;
741 #ifdef DEBUG
742       i++;
743 #endif
744 
745       shapesHash = mozilla::AddToHash(shapesHash, HashIteratorShape(shape));
746 
747       // The one caller of this method that passes |numShapes > 0|, does
748       // so only if the entire chain consists of cacheable objects (that
749       // necessarily have static prototypes).
750       pobj = pobj->staticPrototype();
751     } while (pobj);
752 
753     shapesHash_ = shapesHash;
754     MOZ_ASSERT(i == numShapes);
755   }
756   MOZ_ASSERT(static_cast<void*>(shapesEnd_) == propertyCursor_);
757 
758   for (size_t i = 0, len = props.length(); i < len; i++) {
759     JSLinearString* str = IdToString(cx, props[i]);
760     if (!str) {
761       *hadError = true;
762       return;
763     }
764     new (propertiesEnd_) GCPtrLinearString(str);
765     propertiesEnd_++;
766   }
767 
768   markInitialized();
769 
770   MOZ_ASSERT(!*hadError);
771 }
772 
allocationSize() const773 inline size_t NativeIterator::allocationSize() const {
774   size_t numShapes = shapesEnd() - shapesBegin();
775   return AllocationSize(initialPropertyCount(), numShapes);
776 }
777 
778 /* static */
match(PropertyIteratorObject * obj,const Lookup & lookup)779 bool IteratorHashPolicy::match(PropertyIteratorObject* obj,
780                                const Lookup& lookup) {
781   NativeIterator* ni = obj->getNativeIterator();
782   if (ni->shapesHash() != lookup.shapesHash ||
783       ni->shapeCount() != lookup.numShapes) {
784     return false;
785   }
786 
787   return ArrayEqual(reinterpret_cast<Shape**>(ni->shapesBegin()), lookup.shapes,
788                     ni->shapeCount());
789 }
790 
CanCompareIterableObjectToCache(JSObject * obj)791 static inline bool CanCompareIterableObjectToCache(JSObject* obj) {
792   if (obj->is<NativeObject>()) {
793     return obj->as<NativeObject>().getDenseInitializedLength() == 0;
794   }
795   return false;
796 }
797 
LookupInIteratorCache(JSContext * cx,JSObject * obj,uint32_t * numShapes)798 static MOZ_ALWAYS_INLINE PropertyIteratorObject* LookupInIteratorCache(
799     JSContext* cx, JSObject* obj, uint32_t* numShapes) {
800   MOZ_ASSERT(*numShapes == 0);
801 
802   Vector<Shape*, 8> shapes(cx);
803   HashNumber shapesHash = 0;
804   JSObject* pobj = obj;
805   do {
806     if (!CanCompareIterableObjectToCache(pobj)) {
807       return nullptr;
808     }
809 
810     MOZ_ASSERT(pobj->is<NativeObject>());
811     Shape* shape = pobj->shape();
812     shapesHash = mozilla::AddToHash(shapesHash, HashIteratorShape(shape));
813 
814     if (MOZ_UNLIKELY(!shapes.append(shape))) {
815       cx->recoverFromOutOfMemory();
816       return nullptr;
817     }
818 
819     pobj = pobj->staticPrototype();
820   } while (pobj);
821 
822   MOZ_ASSERT(!shapes.empty());
823   *numShapes = shapes.length();
824 
825   IteratorHashPolicy::Lookup lookup(shapes.begin(), shapes.length(),
826                                     shapesHash);
827   auto p = ObjectRealm::get(obj).iteratorCache.lookup(lookup);
828   if (!p) {
829     return nullptr;
830   }
831 
832   PropertyIteratorObject* iterobj = *p;
833   MOZ_ASSERT(iterobj->compartment() == cx->compartment());
834 
835   NativeIterator* ni = iterobj->getNativeIterator();
836   if (!ni->isReusable()) {
837     return nullptr;
838   }
839 
840   return iterobj;
841 }
842 
CanStoreInIteratorCache(JSObject * obj)843 static bool CanStoreInIteratorCache(JSObject* obj) {
844   do {
845     MOZ_ASSERT(obj->as<NativeObject>().getDenseInitializedLength() == 0);
846 
847     // Typed arrays have indexed properties not captured by the Shape guard.
848     // Enumerate hooks may add extra properties.
849     const JSClass* clasp = obj->getClass();
850     if (MOZ_UNLIKELY(IsTypedArrayClass(clasp))) {
851       return false;
852     }
853     if (MOZ_UNLIKELY(clasp->getNewEnumerate() || clasp->getEnumerate())) {
854       return false;
855     }
856 
857     obj = obj->staticPrototype();
858   } while (obj);
859 
860   return true;
861 }
862 
StoreInIteratorCache(JSContext * cx,JSObject * obj,PropertyIteratorObject * iterobj)863 [[nodiscard]] static bool StoreInIteratorCache(
864     JSContext* cx, JSObject* obj, PropertyIteratorObject* iterobj) {
865   MOZ_ASSERT(CanStoreInIteratorCache(obj));
866 
867   NativeIterator* ni = iterobj->getNativeIterator();
868   MOZ_ASSERT(ni->shapeCount() > 0);
869 
870   IteratorHashPolicy::Lookup lookup(
871       reinterpret_cast<Shape**>(ni->shapesBegin()), ni->shapeCount(),
872       ni->shapesHash());
873 
874   ObjectRealm::IteratorCache& cache = ObjectRealm::get(obj).iteratorCache;
875   bool ok;
876   auto p = cache.lookupForAdd(lookup);
877   if (MOZ_LIKELY(!p)) {
878     ok = cache.add(p, iterobj);
879   } else {
880     // If we weren't able to use an existing cached iterator, just
881     // replace it.
882     cache.remove(p);
883     ok = cache.relookupOrAdd(p, lookup, iterobj);
884   }
885   if (!ok) {
886     ReportOutOfMemory(cx);
887     return false;
888   }
889 
890   return true;
891 }
892 
EnumerateProperties(JSContext * cx,HandleObject obj,MutableHandleIdVector props)893 bool js::EnumerateProperties(JSContext* cx, HandleObject obj,
894                              MutableHandleIdVector props) {
895   MOZ_ASSERT(props.empty());
896 
897   if (MOZ_UNLIKELY(obj->is<ProxyObject>())) {
898     return Proxy::enumerate(cx, obj, props);
899   }
900 
901   return Snapshot(cx, obj, 0, props);
902 }
903 
904 #ifdef DEBUG
PrototypeMayHaveIndexedProperties(NativeObject * nobj)905 static bool PrototypeMayHaveIndexedProperties(NativeObject* nobj) {
906   JSObject* proto = nobj->staticPrototype();
907   if (!proto) {
908     return false;
909   }
910 
911   if (proto->is<NativeObject>() &&
912       proto->as<NativeObject>().getDenseInitializedLength() > 0) {
913     return true;
914   }
915 
916   return ObjectMayHaveExtraIndexedProperties(proto);
917 }
918 #endif
919 
GetIterator(JSContext * cx,HandleObject obj)920 static JSObject* GetIterator(JSContext* cx, HandleObject obj) {
921   MOZ_ASSERT(!obj->is<PropertyIteratorObject>());
922   MOZ_ASSERT(cx->compartment() == obj->compartment(),
923              "We may end up allocating shapes in the wrong zone!");
924 
925   uint32_t numShapes = 0;
926   if (PropertyIteratorObject* iterobj =
927           LookupInIteratorCache(cx, obj, &numShapes)) {
928     NativeIterator* ni = iterobj->getNativeIterator();
929     ni->changeObjectBeingIterated(*obj);
930     RegisterEnumerator(ObjectRealm::get(obj), ni);
931     return iterobj;
932   }
933 
934   if (numShapes > 0 && !CanStoreInIteratorCache(obj)) {
935     numShapes = 0;
936   }
937 
938   RootedIdVector keys(cx);
939   if (!EnumerateProperties(cx, obj, &keys)) {
940     return nullptr;
941   }
942 
943   // If the object has dense elements, mark the dense elements as
944   // maybe-in-iteration.
945   //
946   // The iterator is a snapshot so if indexed properties are added after this
947   // point we don't need to do anything. However, the object might have sparse
948   // elements now that can be densified later. To account for this, we set the
949   // maybe-in-iteration flag also in NativeObject::maybeDensifySparseElements.
950   //
951   // In debug builds, AssertDenseElementsNotIterated is used to check the flag
952   // is set correctly.
953   if (obj->is<NativeObject>() &&
954       obj->as<NativeObject>().getDenseInitializedLength() > 0) {
955     obj->as<NativeObject>().markDenseElementsMaybeInIteration();
956   }
957 
958   PropertyIteratorObject* iterobj =
959       CreatePropertyIterator(cx, obj, keys, numShapes, 0);
960   if (!iterobj) {
961     return nullptr;
962   }
963 
964   cx->check(iterobj);
965 
966 #ifdef DEBUG
967   if (obj->is<NativeObject>()) {
968     if (PrototypeMayHaveIndexedProperties(&obj->as<NativeObject>())) {
969       iterobj->getNativeIterator()->setMaybeHasIndexedPropertiesFromProto();
970     }
971   }
972 #endif
973 
974   // Cache the iterator object.
975   if (numShapes > 0) {
976     if (!StoreInIteratorCache(cx, obj, iterobj)) {
977       return nullptr;
978     }
979   }
980 
981   return iterobj;
982 }
983 
LookupInIteratorCache(JSContext * cx,HandleObject obj)984 PropertyIteratorObject* js::LookupInIteratorCache(JSContext* cx,
985                                                   HandleObject obj) {
986   uint32_t numShapes = 0;
987   return LookupInIteratorCache(cx, obj, &numShapes);
988 }
989 
990 // ES 2017 draft 7.4.7.
CreateIterResultObject(JSContext * cx,HandleValue value,bool done)991 PlainObject* js::CreateIterResultObject(JSContext* cx, HandleValue value,
992                                         bool done) {
993   // Step 1 (implicit).
994 
995   // Step 2.
996   Rooted<PlainObject*> templateObject(
997       cx, cx->realm()->getOrCreateIterResultTemplateObject(cx));
998   if (!templateObject) {
999     return nullptr;
1000   }
1001 
1002   PlainObject* resultObj;
1003   JS_TRY_VAR_OR_RETURN_NULL(
1004       cx, resultObj, PlainObject::createWithTemplate(cx, templateObject));
1005 
1006   // Step 3.
1007   resultObj->setSlot(Realm::IterResultObjectValueSlot, value);
1008 
1009   // Step 4.
1010   resultObj->setSlot(Realm::IterResultObjectDoneSlot,
1011                      done ? TrueHandleValue : FalseHandleValue);
1012 
1013   // Step 5.
1014   return resultObj;
1015 }
1016 
getOrCreateIterResultTemplateObject(JSContext * cx)1017 PlainObject* Realm::getOrCreateIterResultTemplateObject(JSContext* cx) {
1018   MOZ_ASSERT(cx->realm() == this);
1019 
1020   if (iterResultTemplate_) {
1021     return iterResultTemplate_;
1022   }
1023 
1024   PlainObject* templateObj =
1025       createIterResultTemplateObject(cx, WithObjectPrototype::Yes);
1026   iterResultTemplate_.set(templateObj);
1027   return iterResultTemplate_;
1028 }
1029 
getOrCreateIterResultWithoutPrototypeTemplateObject(JSContext * cx)1030 PlainObject* Realm::getOrCreateIterResultWithoutPrototypeTemplateObject(
1031     JSContext* cx) {
1032   MOZ_ASSERT(cx->realm() == this);
1033 
1034   if (iterResultWithoutPrototypeTemplate_) {
1035     return iterResultWithoutPrototypeTemplate_;
1036   }
1037 
1038   PlainObject* templateObj =
1039       createIterResultTemplateObject(cx, WithObjectPrototype::No);
1040   iterResultWithoutPrototypeTemplate_.set(templateObj);
1041   return iterResultWithoutPrototypeTemplate_;
1042 }
1043 
createIterResultTemplateObject(JSContext * cx,WithObjectPrototype withProto)1044 PlainObject* Realm::createIterResultTemplateObject(
1045     JSContext* cx, WithObjectPrototype withProto) {
1046   // Create template plain object
1047   Rooted<PlainObject*> templateObject(
1048       cx, withProto == WithObjectPrototype::Yes
1049               ? NewTenuredBuiltinClassInstance<PlainObject>(cx)
1050               : NewObjectWithGivenProto<PlainObject>(cx, nullptr));
1051   if (!templateObject) {
1052     return nullptr;
1053   }
1054 
1055   // Set dummy `value` property
1056   if (!NativeDefineDataProperty(cx, templateObject, cx->names().value,
1057                                 UndefinedHandleValue, JSPROP_ENUMERATE)) {
1058     return nullptr;
1059   }
1060 
1061   // Set dummy `done` property
1062   if (!NativeDefineDataProperty(cx, templateObject, cx->names().done,
1063                                 TrueHandleValue, JSPROP_ENUMERATE)) {
1064     return nullptr;
1065   }
1066 
1067 #ifdef DEBUG
1068   // Make sure that the properties are in the right slots.
1069   ShapePropertyIter<NoGC> iter(templateObject->shape());
1070   MOZ_ASSERT(iter->slot() == Realm::IterResultObjectDoneSlot &&
1071              iter->key() == NameToId(cx->names().done));
1072   iter++;
1073   MOZ_ASSERT(iter->slot() == Realm::IterResultObjectValueSlot &&
1074              iter->key() == NameToId(cx->names().value));
1075 #endif
1076 
1077   return templateObject;
1078 }
1079 
1080 /*** Iterator objects *******************************************************/
1081 
sizeOfMisc(mozilla::MallocSizeOf mallocSizeOf) const1082 size_t PropertyIteratorObject::sizeOfMisc(
1083     mozilla::MallocSizeOf mallocSizeOf) const {
1084   return mallocSizeOf(getPrivate());
1085 }
1086 
trace(JSTracer * trc,JSObject * obj)1087 void PropertyIteratorObject::trace(JSTracer* trc, JSObject* obj) {
1088   if (NativeIterator* ni =
1089           obj->as<PropertyIteratorObject>().getNativeIterator()) {
1090     ni->trace(trc);
1091   }
1092 }
1093 
finalize(JSFreeOp * fop,JSObject * obj)1094 void PropertyIteratorObject::finalize(JSFreeOp* fop, JSObject* obj) {
1095   if (NativeIterator* ni =
1096           obj->as<PropertyIteratorObject>().getNativeIterator()) {
1097     fop->free_(obj, ni, ni->allocationSize(), MemoryUse::NativeIterator);
1098   }
1099 }
1100 
1101 const JSClassOps PropertyIteratorObject::classOps_ = {
1102     nullptr,   // addProperty
1103     nullptr,   // delProperty
1104     nullptr,   // enumerate
1105     nullptr,   // newEnumerate
1106     nullptr,   // resolve
1107     nullptr,   // mayResolve
1108     finalize,  // finalize
1109     nullptr,   // call
1110     nullptr,   // hasInstance
1111     nullptr,   // construct
1112     trace,     // trace
1113 };
1114 
1115 const JSClass PropertyIteratorObject::class_ = {
1116     "Iterator", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE,
1117     &PropertyIteratorObject::classOps_};
1118 
1119 static const JSClass ArrayIteratorPrototypeClass = {"Array Iterator", 0};
1120 
1121 enum {
1122   ArrayIteratorSlotIteratedObject,
1123   ArrayIteratorSlotNextIndex,
1124   ArrayIteratorSlotItemKind,
1125   ArrayIteratorSlotCount
1126 };
1127 
1128 const JSClass ArrayIteratorObject::class_ = {
1129     "Array Iterator", JSCLASS_HAS_RESERVED_SLOTS(ArrayIteratorSlotCount)};
1130 
NewArrayIteratorTemplate(JSContext * cx)1131 ArrayIteratorObject* js::NewArrayIteratorTemplate(JSContext* cx) {
1132   RootedObject proto(
1133       cx, GlobalObject::getOrCreateArrayIteratorPrototype(cx, cx->global()));
1134   if (!proto) {
1135     return nullptr;
1136   }
1137 
1138   return NewTenuredObjectWithGivenProto<ArrayIteratorObject>(cx, proto);
1139 }
1140 
NewArrayIterator(JSContext * cx)1141 ArrayIteratorObject* js::NewArrayIterator(JSContext* cx) {
1142   RootedObject proto(
1143       cx, GlobalObject::getOrCreateArrayIteratorPrototype(cx, cx->global()));
1144   if (!proto) {
1145     return nullptr;
1146   }
1147 
1148   return NewObjectWithGivenProto<ArrayIteratorObject>(cx, proto);
1149 }
1150 
1151 static const JSFunctionSpec array_iterator_methods[] = {
1152     JS_SELF_HOSTED_FN("next", "ArrayIteratorNext", 0, 0), JS_FS_END};
1153 
1154 static const JSClass StringIteratorPrototypeClass = {"String Iterator", 0};
1155 
1156 enum {
1157   StringIteratorSlotIteratedObject,
1158   StringIteratorSlotNextIndex,
1159   StringIteratorSlotCount
1160 };
1161 
1162 const JSClass StringIteratorObject::class_ = {
1163     "String Iterator", JSCLASS_HAS_RESERVED_SLOTS(StringIteratorSlotCount)};
1164 
1165 static const JSFunctionSpec string_iterator_methods[] = {
1166     JS_SELF_HOSTED_FN("next", "StringIteratorNext", 0, 0), JS_FS_END};
1167 
NewStringIteratorTemplate(JSContext * cx)1168 StringIteratorObject* js::NewStringIteratorTemplate(JSContext* cx) {
1169   RootedObject proto(
1170       cx, GlobalObject::getOrCreateStringIteratorPrototype(cx, cx->global()));
1171   if (!proto) {
1172     return nullptr;
1173   }
1174 
1175   return NewTenuredObjectWithGivenProto<StringIteratorObject>(cx, proto);
1176 }
1177 
NewStringIterator(JSContext * cx)1178 StringIteratorObject* js::NewStringIterator(JSContext* cx) {
1179   RootedObject proto(
1180       cx, GlobalObject::getOrCreateStringIteratorPrototype(cx, cx->global()));
1181   if (!proto) {
1182     return nullptr;
1183   }
1184 
1185   return NewObjectWithGivenProto<StringIteratorObject>(cx, proto);
1186 }
1187 
1188 static const JSClass RegExpStringIteratorPrototypeClass = {
1189     "RegExp String Iterator", 0};
1190 
1191 enum {
1192   // The regular expression used for iteration. May hold the original RegExp
1193   // object when it is reused instead of a new RegExp object.
1194   RegExpStringIteratorSlotRegExp,
1195 
1196   // The String value being iterated upon.
1197   RegExpStringIteratorSlotString,
1198 
1199   // The source string of the original RegExp object. Used to validate we can
1200   // reuse the original RegExp object for matching.
1201   RegExpStringIteratorSlotSource,
1202 
1203   // The flags of the original RegExp object.
1204   RegExpStringIteratorSlotFlags,
1205 
1206   // When non-negative, this slot holds the current lastIndex position when
1207   // reusing the original RegExp object for matching. When set to |-1|, the
1208   // iterator has finished. When set to any other negative value, the
1209   // iterator is not yet exhausted and we're not on the fast path and we're
1210   // not reusing the input RegExp object.
1211   RegExpStringIteratorSlotLastIndex,
1212 
1213   RegExpStringIteratorSlotCount
1214 };
1215 
1216 static_assert(RegExpStringIteratorSlotRegExp ==
1217                   REGEXP_STRING_ITERATOR_REGEXP_SLOT,
1218               "RegExpStringIteratorSlotRegExp must match self-hosting define "
1219               "for regexp slot.");
1220 static_assert(RegExpStringIteratorSlotString ==
1221                   REGEXP_STRING_ITERATOR_STRING_SLOT,
1222               "RegExpStringIteratorSlotString must match self-hosting define "
1223               "for string slot.");
1224 static_assert(RegExpStringIteratorSlotSource ==
1225                   REGEXP_STRING_ITERATOR_SOURCE_SLOT,
1226               "RegExpStringIteratorSlotString must match self-hosting define "
1227               "for source slot.");
1228 static_assert(RegExpStringIteratorSlotFlags ==
1229                   REGEXP_STRING_ITERATOR_FLAGS_SLOT,
1230               "RegExpStringIteratorSlotFlags must match self-hosting define "
1231               "for flags slot.");
1232 static_assert(RegExpStringIteratorSlotLastIndex ==
1233                   REGEXP_STRING_ITERATOR_LASTINDEX_SLOT,
1234               "RegExpStringIteratorSlotLastIndex must match self-hosting "
1235               "define for lastIndex slot.");
1236 
1237 const JSClass RegExpStringIteratorObject::class_ = {
1238     "RegExp String Iterator",
1239     JSCLASS_HAS_RESERVED_SLOTS(RegExpStringIteratorSlotCount)};
1240 
1241 static const JSFunctionSpec regexp_string_iterator_methods[] = {
1242     JS_SELF_HOSTED_FN("next", "RegExpStringIteratorNext", 0, 0),
1243 
1244     JS_FS_END};
1245 
NewRegExpStringIteratorTemplate(JSContext * cx)1246 RegExpStringIteratorObject* js::NewRegExpStringIteratorTemplate(JSContext* cx) {
1247   RootedObject proto(cx, GlobalObject::getOrCreateRegExpStringIteratorPrototype(
1248                              cx, cx->global()));
1249   if (!proto) {
1250     return nullptr;
1251   }
1252 
1253   return NewTenuredObjectWithGivenProto<RegExpStringIteratorObject>(cx, proto);
1254 }
1255 
NewRegExpStringIterator(JSContext * cx)1256 RegExpStringIteratorObject* js::NewRegExpStringIterator(JSContext* cx) {
1257   RootedObject proto(cx, GlobalObject::getOrCreateRegExpStringIteratorPrototype(
1258                              cx, cx->global()));
1259   if (!proto) {
1260     return nullptr;
1261   }
1262 
1263   return NewObjectWithGivenProto<RegExpStringIteratorObject>(cx, proto);
1264 }
1265 
ValueToIterator(JSContext * cx,HandleValue vp)1266 JSObject* js::ValueToIterator(JSContext* cx, HandleValue vp) {
1267   RootedObject obj(cx);
1268   if (vp.isObject()) {
1269     /* Common case. */
1270     obj = &vp.toObject();
1271   } else if (vp.isNullOrUndefined()) {
1272     /*
1273      * Enumerating over null and undefined gives an empty enumerator, so
1274      * that |for (var p in <null or undefined>) <loop>;| never executes
1275      * <loop>, per ES5 12.6.4.
1276      */
1277     RootedIdVector props(cx);  // Empty
1278     return CreatePropertyIterator(cx, nullptr, props, 0, 0);
1279   } else {
1280     obj = ToObject(cx, vp);
1281     if (!obj) {
1282       return nullptr;
1283     }
1284   }
1285 
1286   return GetIterator(cx, obj);
1287 }
1288 
CloseIterator(JSObject * obj)1289 void js::CloseIterator(JSObject* obj) {
1290   if (obj->is<PropertyIteratorObject>()) {
1291     /* Remove enumerators from the active list, which is a stack. */
1292     NativeIterator* ni = obj->as<PropertyIteratorObject>().getNativeIterator();
1293 
1294     ni->unlink();
1295 
1296     MOZ_ASSERT(ni->isActive());
1297     ni->markInactive();
1298 
1299     // Reset the enumerator; it may still be in the cached iterators for
1300     // this thread and can be reused.
1301     ni->resetPropertyCursorForReuse();
1302   }
1303 }
1304 
IteratorCloseForException(JSContext * cx,HandleObject obj)1305 bool js::IteratorCloseForException(JSContext* cx, HandleObject obj) {
1306   MOZ_ASSERT(cx->isExceptionPending());
1307 
1308   bool isClosingGenerator = cx->isClosingGenerator();
1309   JS::AutoSaveExceptionState savedExc(cx);
1310 
1311   // Implements IteratorClose (ES 7.4.6) for exception unwinding. See
1312   // also the bytecode generated by BytecodeEmitter::emitIteratorClose.
1313 
1314   // Step 3.
1315   //
1316   // Get the "return" method.
1317   RootedValue returnMethod(cx);
1318   if (!GetProperty(cx, obj, obj, cx->names().return_, &returnMethod)) {
1319     return false;
1320   }
1321 
1322   // Step 4.
1323   //
1324   // Do nothing if "return" is null or undefined. Throw a TypeError if the
1325   // method is not IsCallable.
1326   if (returnMethod.isNullOrUndefined()) {
1327     return true;
1328   }
1329   if (!IsCallable(returnMethod)) {
1330     return ReportIsNotFunction(cx, returnMethod);
1331   }
1332 
1333   // Step 5, 6, 8.
1334   //
1335   // Call "return" if it is not null or undefined.
1336   RootedValue rval(cx);
1337   bool ok = Call(cx, returnMethod, obj, &rval);
1338   if (isClosingGenerator) {
1339     // Closing an iterator is implemented as an exception, but in spec
1340     // terms it is a Completion value with [[Type]] return. In this case
1341     // we *do* care if the call threw and if it returned an object.
1342     if (!ok) {
1343       return false;
1344     }
1345     if (!rval.isObject()) {
1346       return ThrowCheckIsObject(cx, CheckIsObjectKind::IteratorReturn);
1347     }
1348   } else {
1349     // We don't care if the call threw or that it returned an Object, as
1350     // Step 6 says if IteratorClose is being called during a throw, the
1351     // original throw has primacy.
1352     savedExc.restore();
1353   }
1354 
1355   return true;
1356 }
1357 
UnwindIteratorForUncatchableException(JSObject * obj)1358 void js::UnwindIteratorForUncatchableException(JSObject* obj) {
1359   if (obj->is<PropertyIteratorObject>()) {
1360     NativeIterator* ni = obj->as<PropertyIteratorObject>().getNativeIterator();
1361     ni->unlink();
1362   }
1363 }
1364 
SuppressDeletedProperty(JSContext * cx,NativeIterator * ni,HandleObject obj,Handle<JSLinearString * > str)1365 static bool SuppressDeletedProperty(JSContext* cx, NativeIterator* ni,
1366                                     HandleObject obj,
1367                                     Handle<JSLinearString*> str) {
1368   if (ni->objectBeingIterated() != obj) {
1369     return true;
1370   }
1371 
1372   // Optimization for the following common case:
1373   //
1374   //    for (var p in o) {
1375   //        delete o[p];
1376   //    }
1377   //
1378   // Note that usually both strings will be atoms so we only check for pointer
1379   // equality here.
1380   if (ni->previousPropertyWas(str)) {
1381     return true;
1382   }
1383 
1384   while (true) {
1385     bool restart = false;
1386 
1387     // Check whether id is still to come.
1388     GCPtrLinearString* const cursor = ni->nextProperty();
1389     GCPtrLinearString* const end = ni->propertiesEnd();
1390     for (GCPtrLinearString* idp = cursor; idp < end; ++idp) {
1391       // Common case: both strings are atoms.
1392       if ((*idp)->isAtom() && str->isAtom()) {
1393         if (*idp != str) {
1394           continue;
1395         }
1396       } else {
1397         if (!EqualStrings(*idp, str)) {
1398           continue;
1399         }
1400       }
1401 
1402       // Check whether another property along the prototype chain became
1403       // visible as a result of this deletion.
1404       RootedObject proto(cx);
1405       if (!GetPrototype(cx, obj, &proto)) {
1406         return false;
1407       }
1408       if (proto) {
1409         RootedId id(cx);
1410         RootedValue idv(cx, StringValue(*idp));
1411         if (!PrimitiveValueToId<CanGC>(cx, idv, &id)) {
1412           return false;
1413         }
1414 
1415         Rooted<mozilla::Maybe<PropertyDescriptor>> desc(cx);
1416         RootedObject holder(cx);
1417         if (!GetPropertyDescriptor(cx, proto, id, &desc, &holder)) {
1418           return false;
1419         }
1420 
1421         if (desc.isSome() && desc->enumerable()) {
1422           continue;
1423         }
1424       }
1425 
1426       // If GetPropertyDescriptor above removed a property from ni, start
1427       // over.
1428       if (end != ni->propertiesEnd() || cursor != ni->nextProperty()) {
1429         restart = true;
1430         break;
1431       }
1432 
1433       // No property along the prototype chain stepped in to take the
1434       // property's place, so go ahead and delete id from the list.
1435       // If it is the next property to be enumerated, just skip it.
1436       if (idp == cursor) {
1437         ni->incCursor();
1438       } else {
1439         for (GCPtrLinearString* p = idp; p + 1 != end; p++) {
1440           *p = *(p + 1);
1441         }
1442 
1443         ni->trimLastProperty();
1444       }
1445 
1446       ni->markHasUnvisitedPropertyDeletion();
1447       return true;
1448     }
1449 
1450     if (!restart) {
1451       return true;
1452     }
1453   }
1454 }
1455 
1456 /*
1457  * Suppress enumeration of deleted properties. This function must be called
1458  * when a property is deleted and there might be active enumerators.
1459  *
1460  * We maintain a list of active non-escaping for-in enumerators. To suppress
1461  * a property, we check whether each active enumerator contains the (obj, id)
1462  * pair and has not yet enumerated |id|. If so, and |id| is the next property,
1463  * we simply advance the cursor. Otherwise, we delete |id| from the list.
1464  *
1465  * We do not suppress enumeration of a property deleted along an object's
1466  * prototype chain. Only direct deletions on the object are handled.
1467  */
SuppressDeletedPropertyHelper(JSContext * cx,HandleObject obj,Handle<JSLinearString * > str)1468 static bool SuppressDeletedPropertyHelper(JSContext* cx, HandleObject obj,
1469                                           Handle<JSLinearString*> str) {
1470   NativeIterator* enumeratorList = ObjectRealm::get(obj).enumerators;
1471   NativeIterator* ni = enumeratorList->next();
1472 
1473   while (ni != enumeratorList) {
1474     if (!SuppressDeletedProperty(cx, ni, obj, str)) {
1475       return false;
1476     }
1477     ni = ni->next();
1478   }
1479 
1480   return true;
1481 }
1482 
SuppressDeletedProperty(JSContext * cx,HandleObject obj,jsid id)1483 bool js::SuppressDeletedProperty(JSContext* cx, HandleObject obj, jsid id) {
1484   if (MOZ_LIKELY(!ObjectRealm::get(obj).objectMaybeInIteration(obj))) {
1485     return true;
1486   }
1487 
1488   if (id.isSymbol()) {
1489     return true;
1490   }
1491 
1492   Rooted<JSLinearString*> str(cx, IdToString(cx, id));
1493   if (!str) {
1494     return false;
1495   }
1496   return SuppressDeletedPropertyHelper(cx, obj, str);
1497 }
1498 
SuppressDeletedElement(JSContext * cx,HandleObject obj,uint32_t index)1499 bool js::SuppressDeletedElement(JSContext* cx, HandleObject obj,
1500                                 uint32_t index) {
1501   if (MOZ_LIKELY(!ObjectRealm::get(obj).objectMaybeInIteration(obj))) {
1502     return true;
1503   }
1504 
1505   RootedId id(cx);
1506   if (!IndexToId(cx, index, &id)) {
1507     return false;
1508   }
1509 
1510   Rooted<JSLinearString*> str(cx, IdToString(cx, id));
1511   if (!str) {
1512     return false;
1513   }
1514   return SuppressDeletedPropertyHelper(cx, obj, str);
1515 }
1516 
1517 #ifdef DEBUG
AssertDenseElementsNotIterated(NativeObject * obj)1518 void js::AssertDenseElementsNotIterated(NativeObject* obj) {
1519   // Search for active iterators for |obj| and assert they don't contain any
1520   // property keys that are dense elements. This is used to check correctness
1521   // of the MAYBE_IN_ITERATION flag on ObjectElements.
1522   //
1523   // Ignore iterators that may contain indexed properties from objects on the
1524   // prototype chain, as that can result in false positives. See bug 1656744.
1525 
1526   // Limit the number of properties we check to avoid slowing down debug builds
1527   // too much.
1528   static constexpr uint32_t MaxPropsToCheck = 10;
1529   uint32_t propsChecked = 0;
1530 
1531   NativeIterator* enumeratorList = ObjectRealm::get(obj).enumerators;
1532   NativeIterator* ni = enumeratorList->next();
1533 
1534   while (ni != enumeratorList) {
1535     if (ni->objectBeingIterated() == obj &&
1536         !ni->maybeHasIndexedPropertiesFromProto()) {
1537       for (GCPtrLinearString* idp = ni->nextProperty();
1538            idp < ni->propertiesEnd(); ++idp) {
1539         uint32_t index;
1540         if (idp->get()->isIndex(&index)) {
1541           MOZ_ASSERT(!obj->containsDenseElement(index));
1542         }
1543         if (++propsChecked > MaxPropsToCheck) {
1544           return;
1545         }
1546       }
1547     }
1548     ni = ni->next();
1549   }
1550 }
1551 #endif
1552 
1553 static const JSFunctionSpec iterator_methods[] = {
1554     JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0), JS_FS_END};
1555 
1556 static const JSFunctionSpec iterator_static_methods[] = {
1557     JS_SELF_HOSTED_FN("from", "IteratorFrom", 1, 0), JS_FS_END};
1558 
1559 // These methods are only attached to Iterator.prototype when the
1560 // Iterator Helpers feature is enabled.
1561 static const JSFunctionSpec iterator_methods_with_helpers[] = {
1562     JS_SELF_HOSTED_FN("map", "IteratorMap", 1, 0),
1563     JS_SELF_HOSTED_FN("filter", "IteratorFilter", 1, 0),
1564     JS_SELF_HOSTED_FN("take", "IteratorTake", 1, 0),
1565     JS_SELF_HOSTED_FN("drop", "IteratorDrop", 1, 0),
1566     JS_SELF_HOSTED_FN("asIndexedPairs", "IteratorAsIndexedPairs", 0, 0),
1567     JS_SELF_HOSTED_FN("flatMap", "IteratorFlatMap", 1, 0),
1568     JS_SELF_HOSTED_FN("reduce", "IteratorReduce", 1, 0),
1569     JS_SELF_HOSTED_FN("toArray", "IteratorToArray", 0, 0),
1570     JS_SELF_HOSTED_FN("forEach", "IteratorForEach", 1, 0),
1571     JS_SELF_HOSTED_FN("some", "IteratorSome", 1, 0),
1572     JS_SELF_HOSTED_FN("every", "IteratorEvery", 1, 0),
1573     JS_SELF_HOSTED_FN("find", "IteratorFind", 1, 0),
1574     JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0),
1575     JS_FS_END};
1576 
1577 /* static */
initIteratorProto(JSContext * cx,Handle<GlobalObject * > global)1578 bool GlobalObject::initIteratorProto(JSContext* cx,
1579                                      Handle<GlobalObject*> global) {
1580   if (global->getReservedSlot(ITERATOR_PROTO).isObject()) {
1581     return true;
1582   }
1583 
1584   RootedObject proto(
1585       cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
1586   if (!proto) {
1587     return false;
1588   }
1589 
1590   // %IteratorPrototype%.map.[[Prototype]] is %Generator% and
1591   // %Generator%.prototype.[[Prototype]] is %IteratorPrototype%.
1592   // Populate the slot early, to prevent runaway mutual recursion.
1593   global->setReservedSlot(ITERATOR_PROTO, ObjectValue(*proto));
1594 
1595   if (!DefinePropertiesAndFunctions(cx, proto, nullptr, iterator_methods)) {
1596     // In this case, we leave a partially initialized object in the
1597     // slot. There's no obvious way to do better, since this object may already
1598     // be in the prototype chain of %GeneratorPrototype%.
1599     return false;
1600   }
1601 
1602   return true;
1603 }
1604 
1605 /* static */
1606 template <unsigned Slot, const JSClass* ProtoClass,
1607           const JSFunctionSpec* Methods>
initObjectIteratorProto(JSContext * cx,Handle<GlobalObject * > global,HandleAtom tag)1608 bool GlobalObject::initObjectIteratorProto(JSContext* cx,
1609                                            Handle<GlobalObject*> global,
1610                                            HandleAtom tag) {
1611   if (global->getReservedSlot(Slot).isObject()) {
1612     return true;
1613   }
1614 
1615   RootedObject iteratorProto(
1616       cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
1617   if (!iteratorProto) {
1618     return false;
1619   }
1620 
1621   RootedObject proto(cx, GlobalObject::createBlankPrototypeInheriting(
1622                              cx, ProtoClass, iteratorProto));
1623   if (!proto || !DefinePropertiesAndFunctions(cx, proto, nullptr, Methods) ||
1624       (tag && !DefineToStringTag(cx, proto, tag))) {
1625     return false;
1626   }
1627 
1628   global->setReservedSlot(Slot, ObjectValue(*proto));
1629   return true;
1630 }
1631 
1632 /* static */
getOrCreateArrayIteratorPrototype(JSContext * cx,Handle<GlobalObject * > global)1633 NativeObject* GlobalObject::getOrCreateArrayIteratorPrototype(
1634     JSContext* cx, Handle<GlobalObject*> global) {
1635   return MaybeNativeObject(getOrCreateObject(
1636       cx, global, ARRAY_ITERATOR_PROTO, cx->names().ArrayIterator.toHandle(),
1637       initObjectIteratorProto<ARRAY_ITERATOR_PROTO,
1638                               &ArrayIteratorPrototypeClass,
1639                               array_iterator_methods>));
1640 }
1641 
1642 /* static */
getOrCreateStringIteratorPrototype(JSContext * cx,Handle<GlobalObject * > global)1643 JSObject* GlobalObject::getOrCreateStringIteratorPrototype(
1644     JSContext* cx, Handle<GlobalObject*> global) {
1645   return getOrCreateObject(
1646       cx, global, STRING_ITERATOR_PROTO, cx->names().StringIterator.toHandle(),
1647       initObjectIteratorProto<STRING_ITERATOR_PROTO,
1648                               &StringIteratorPrototypeClass,
1649                               string_iterator_methods>);
1650 }
1651 
1652 /* static */
getOrCreateRegExpStringIteratorPrototype(JSContext * cx,Handle<GlobalObject * > global)1653 JSObject* GlobalObject::getOrCreateRegExpStringIteratorPrototype(
1654     JSContext* cx, Handle<GlobalObject*> global) {
1655   return getOrCreateObject(
1656       cx, global, REGEXP_STRING_ITERATOR_PROTO,
1657       cx->names().RegExpStringIterator.toHandle(),
1658       initObjectIteratorProto<REGEXP_STRING_ITERATOR_PROTO,
1659                               &RegExpStringIteratorPrototypeClass,
1660                               regexp_string_iterator_methods>);
1661 }
1662 
1663 // Iterator Helper Proposal 2.1.3.1 Iterator()
1664 // https://tc39.es/proposal-iterator-helpers/#sec-iterator as of revision
1665 // ed6e15a
IteratorConstructor(JSContext * cx,unsigned argc,Value * vp)1666 static bool IteratorConstructor(JSContext* cx, unsigned argc, Value* vp) {
1667   CallArgs args = CallArgsFromVp(argc, vp);
1668 
1669   // Step 1.
1670   if (!ThrowIfNotConstructing(cx, args, js_Iterator_str)) {
1671     return false;
1672   }
1673   // Throw TypeError if NewTarget is the active function object, preventing the
1674   // Iterator constructor from being used directly.
1675   if (args.callee() == args.newTarget().toObject()) {
1676     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1677                               JSMSG_BOGUS_CONSTRUCTOR, js_Iterator_str);
1678     return false;
1679   }
1680 
1681   // Step 2.
1682   RootedObject proto(cx);
1683   if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Iterator, &proto)) {
1684     return false;
1685   }
1686 
1687   JSObject* obj = NewObjectWithClassProto<IteratorObject>(cx, proto);
1688   if (!obj) {
1689     return false;
1690   }
1691 
1692   args.rval().setObject(*obj);
1693   return true;
1694 }
1695 
1696 static const ClassSpec IteratorObjectClassSpec = {
1697     GenericCreateConstructor<IteratorConstructor, 0, gc::AllocKind::FUNCTION>,
1698     GenericCreatePrototype<IteratorObject>,
1699     iterator_static_methods,
1700     nullptr,
1701     iterator_methods_with_helpers,
1702     nullptr,
1703     nullptr,
1704 };
1705 
1706 const JSClass IteratorObject::class_ = {
1707     js_Iterator_str,
1708     JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator),
1709     JS_NULL_CLASS_OPS,
1710     &IteratorObjectClassSpec,
1711 };
1712 
1713 const JSClass IteratorObject::protoClass_ = {
1714     "Iterator.prototype",
1715     JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator),
1716     JS_NULL_CLASS_OPS,
1717     &IteratorObjectClassSpec,
1718 };
1719 
1720 // Set up WrapForValidIteratorObject class and its prototype.
1721 static const JSFunctionSpec wrap_for_valid_iterator_methods[] = {
1722     JS_SELF_HOSTED_FN("next", "WrapForValidIteratorNext", 1, 0),
1723     JS_SELF_HOSTED_FN("return", "WrapForValidIteratorReturn", 1, 0),
1724     JS_SELF_HOSTED_FN("throw", "WrapForValidIteratorThrow", 1, 0),
1725     JS_FS_END,
1726 };
1727 
1728 static const JSClass WrapForValidIteratorPrototypeClass = {
1729     "Wrap For Valid Iterator", 0};
1730 
1731 const JSClass WrapForValidIteratorObject::class_ = {
1732     "Wrap For Valid Iterator",
1733     JSCLASS_HAS_RESERVED_SLOTS(WrapForValidIteratorObject::SlotCount),
1734 };
1735 
1736 /* static */
getOrCreateWrapForValidIteratorPrototype(JSContext * cx,Handle<GlobalObject * > global)1737 NativeObject* GlobalObject::getOrCreateWrapForValidIteratorPrototype(
1738     JSContext* cx, Handle<GlobalObject*> global) {
1739   return MaybeNativeObject(getOrCreateObject(
1740       cx, global, WRAP_FOR_VALID_ITERATOR_PROTO, HandleAtom(nullptr),
1741       initObjectIteratorProto<WRAP_FOR_VALID_ITERATOR_PROTO,
1742                               &WrapForValidIteratorPrototypeClass,
1743                               wrap_for_valid_iterator_methods>));
1744 }
1745 
NewWrapForValidIterator(JSContext * cx)1746 WrapForValidIteratorObject* js::NewWrapForValidIterator(JSContext* cx) {
1747   RootedObject proto(cx, GlobalObject::getOrCreateWrapForValidIteratorPrototype(
1748                              cx, cx->global()));
1749   if (!proto) {
1750     return nullptr;
1751   }
1752   return NewObjectWithGivenProto<WrapForValidIteratorObject>(cx, proto);
1753 }
1754 
1755 // Common iterator object returned by Iterator Helper methods.
1756 static const JSFunctionSpec iterator_helper_methods[] = {
1757     JS_SELF_HOSTED_FN("next", "IteratorHelperNext", 1, 0),
1758     JS_SELF_HOSTED_FN("return", "IteratorHelperReturn", 1, 0),
1759     JS_SELF_HOSTED_FN("throw", "IteratorHelperThrow", 1, 0), JS_FS_END};
1760 
1761 static const JSClass IteratorHelperPrototypeClass = {"Iterator Helper", 0};
1762 
1763 const JSClass IteratorHelperObject::class_ = {
1764     "Iterator Helper",
1765     JSCLASS_HAS_RESERVED_SLOTS(IteratorHelperObject::SlotCount),
1766 };
1767 
1768 /* static */
getOrCreateIteratorHelperPrototype(JSContext * cx,Handle<GlobalObject * > global)1769 NativeObject* GlobalObject::getOrCreateIteratorHelperPrototype(
1770     JSContext* cx, Handle<GlobalObject*> global) {
1771   return MaybeNativeObject(
1772       getOrCreateObject(cx, global, ITERATOR_HELPER_PROTO, HandleAtom(nullptr),
1773                         initObjectIteratorProto<ITERATOR_HELPER_PROTO,
1774                                                 &IteratorHelperPrototypeClass,
1775                                                 iterator_helper_methods>));
1776 }
1777 
NewIteratorHelper(JSContext * cx)1778 IteratorHelperObject* js::NewIteratorHelper(JSContext* cx) {
1779   RootedObject proto(
1780       cx, GlobalObject::getOrCreateIteratorHelperPrototype(cx, cx->global()));
1781   if (!proto) {
1782     return nullptr;
1783   }
1784   return NewObjectWithGivenProto<IteratorHelperObject>(cx, proto);
1785 }
1786