Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
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 "jsiter.h"
10 :
11 : #include "mozilla/ArrayUtils.h"
12 : #include "mozilla/Maybe.h"
13 : #include "mozilla/MemoryReporting.h"
14 : #include "mozilla/PodOperations.h"
15 : #include "mozilla/Unused.h"
16 :
17 : #include "jsarray.h"
18 : #include "jsatom.h"
19 : #include "jscntxt.h"
20 : #include "jsgc.h"
21 : #include "jsobj.h"
22 : #include "jsopcode.h"
23 : #include "jsscript.h"
24 : #include "jstypes.h"
25 : #include "jsutil.h"
26 :
27 : #include "ds/Sort.h"
28 : #include "gc/Marking.h"
29 : #include "js/Proxy.h"
30 : #include "vm/GeneratorObject.h"
31 : #include "vm/GlobalObject.h"
32 : #include "vm/Interpreter.h"
33 : #include "vm/Shape.h"
34 : #include "vm/StopIterationObject.h"
35 : #include "vm/TypedArrayObject.h"
36 :
37 : #include "jsscriptinlines.h"
38 :
39 : #include "vm/NativeObject-inl.h"
40 : #include "vm/ReceiverGuard-inl.h"
41 : #include "vm/Stack-inl.h"
42 : #include "vm/String-inl.h"
43 :
44 : using namespace js;
45 : using namespace js::gc;
46 : using JS::ForOfIterator;
47 :
48 : using mozilla::ArrayLength;
49 : using mozilla::Maybe;
50 : using mozilla::PodCopy;
51 : using mozilla::PodZero;
52 :
53 : typedef Rooted<PropertyIteratorObject*> RootedPropertyIteratorObject;
54 :
55 : static const gc::AllocKind ITERATOR_FINALIZE_KIND = gc::AllocKind::OBJECT2_BACKGROUND;
56 :
57 : void
58 0 : NativeIterator::trace(JSTracer* trc)
59 : {
60 0 : for (GCPtrFlatString* str = begin(); str < end(); str++)
61 0 : TraceNullableEdge(trc, str, "prop");
62 0 : TraceNullableEdge(trc, &obj, "obj");
63 :
64 0 : for (size_t i = 0; i < guard_length; i++)
65 0 : guard_array[i].trace(trc);
66 :
67 : // The SuppressDeletedPropertyHelper loop can GC, so make sure that if the
68 : // GC removes any elements from the list, it won't remove this one.
69 0 : if (iterObj_)
70 0 : TraceManuallyBarrieredEdge(trc, &iterObj_, "iterObj");
71 0 : }
72 :
73 : typedef HashSet<jsid, DefaultHasher<jsid>> IdSet;
74 :
75 : static inline bool
76 0 : NewKeyValuePair(JSContext* cx, jsid id, const Value& val, MutableHandleValue rval)
77 : {
78 0 : return NewValuePair(cx, IdToValue(id), val, rval);
79 : }
80 :
81 : template <bool CheckForDuplicates>
82 : static inline bool
83 6437 : Enumerate(JSContext* cx, HandleObject pobj, jsid id,
84 : bool enumerable, unsigned flags, Maybe<IdSet>& ht, AutoIdVector* props)
85 : {
86 : if (CheckForDuplicates) {
87 3981 : if (!ht) {
88 66 : ht.emplace(cx);
89 : // Most of the time there are only a handful of entries.
90 66 : if (!ht->init(5))
91 21 : return false;
92 : }
93 :
94 : // If we've already seen this, we definitely won't add it.
95 3981 : IdSet::AddPtr p = ht->lookupForAdd(id);
96 3981 : if (MOZ_UNLIKELY(!!p))
97 21 : return true;
98 :
99 : // It's not necessary to add properties to the hash table at the end of
100 : // the prototype chain, but custom enumeration behaviors might return
101 : // duplicated properties, so always add in such cases.
102 11880 : if (pobj->is<ProxyObject>() ||
103 5023 : pobj->staticPrototype() ||
104 1063 : pobj->getClass()->getNewEnumerate())
105 : {
106 2897 : if (!ht->add(p, id))
107 0 : return false;
108 : }
109 : }
110 :
111 7374 : if (!enumerable && !(flags & JSITER_HIDDEN))
112 977 : return true;
113 :
114 : // Symbol-keyed properties and nonenumerable properties are skipped unless
115 : // the caller specifically asks for them. A caller can also filter out
116 : // non-symbols by asking for JSITER_SYMBOLSONLY.
117 5439 : if (JSID_IS_SYMBOL(id) ? !(flags & JSITER_SYMBOLS) : (flags & JSITER_SYMBOLSONLY))
118 31 : return true;
119 :
120 5408 : return props->append(id);
121 : }
122 :
123 : template <bool CheckForDuplicates>
124 : static bool
125 3 : EnumerateExtraProperties(JSContext* cx, HandleObject obj, unsigned flags, Maybe<IdSet>& ht,
126 : AutoIdVector* props)
127 : {
128 3 : MOZ_ASSERT(obj->getClass()->getNewEnumerate());
129 :
130 6 : AutoIdVector properties(cx);
131 3 : bool enumerableOnly = !(flags & JSITER_HIDDEN);
132 3 : if (!obj->getClass()->getNewEnumerate()(cx, obj, properties, enumerableOnly))
133 0 : return false;
134 :
135 6 : RootedId id(cx);
136 2313 : for (size_t n = 0; n < properties.length(); n++) {
137 2310 : id = properties[n];
138 :
139 : // The enumerate hook does not indicate whether the properties
140 : // it returns are enumerable or not. Since we already passed
141 : // `enumerableOnly` to the hook to filter out non-enumerable
142 : // properties, it doesn't really matter what we pass here.
143 2310 : bool enumerable = true;
144 2310 : if (!Enumerate<CheckForDuplicates>(cx, obj, id, enumerable, flags, ht, props))
145 0 : return false;
146 : }
147 :
148 3 : return true;
149 : }
150 :
151 : static bool
152 0 : SortComparatorIntegerIds(jsid a, jsid b, bool* lessOrEqualp)
153 : {
154 : uint32_t indexA, indexB;
155 0 : MOZ_ALWAYS_TRUE(IdIsIndex(a, &indexA));
156 0 : MOZ_ALWAYS_TRUE(IdIsIndex(b, &indexB));
157 0 : *lessOrEqualp = (indexA <= indexB);
158 0 : return true;
159 : }
160 :
161 : template <bool CheckForDuplicates>
162 : static bool
163 635 : EnumerateNativeProperties(JSContext* cx, HandleNativeObject pobj, unsigned flags, Maybe<IdSet>& ht,
164 : AutoIdVector* props, Handle<UnboxedPlainObject*> unboxed = nullptr)
165 : {
166 : bool enumerateSymbols;
167 635 : if (flags & JSITER_SYMBOLSONLY) {
168 54 : enumerateSymbols = true;
169 : } else {
170 : /* Collect any dense elements from this object. */
171 581 : size_t firstElemIndex = props->length();
172 581 : size_t initlen = pobj->getDenseInitializedLength();
173 581 : const Value* vp = pobj->getDenseElements();
174 581 : bool hasHoles = false;
175 617 : for (size_t i = 0; i < initlen; ++i, ++vp) {
176 36 : if (vp->isMagic(JS_ELEMENTS_HOLE)) {
177 0 : hasHoles = true;
178 : } else {
179 : /* Dense arrays never get so large that i would not fit into an integer id. */
180 36 : if (!Enumerate<CheckForDuplicates>(cx, pobj, INT_TO_JSID(i),
181 : /* enumerable = */ true, flags, ht, props))
182 : {
183 0 : return false;
184 : }
185 : }
186 : }
187 :
188 : /* Collect any typed array or shared typed array elements from this object. */
189 581 : if (pobj->is<TypedArrayObject>()) {
190 0 : size_t len = pobj->as<TypedArrayObject>().length();
191 0 : for (size_t i = 0; i < len; i++) {
192 0 : if (!Enumerate<CheckForDuplicates>(cx, pobj, INT_TO_JSID(i),
193 : /* enumerable = */ true, flags, ht, props))
194 : {
195 0 : return false;
196 : }
197 : }
198 : }
199 :
200 : // Collect any sparse elements from this object.
201 581 : bool isIndexed = pobj->isIndexed();
202 581 : if (isIndexed) {
203 : // If the dense elements didn't have holes, we don't need to include
204 : // them in the sort.
205 0 : if (!hasHoles)
206 0 : firstElemIndex = props->length();
207 :
208 0 : for (Shape::Range<NoGC> r(pobj->lastProperty()); !r.empty(); r.popFront()) {
209 0 : Shape& shape = r.front();
210 0 : jsid id = shape.propid();
211 : uint32_t dummy;
212 0 : if (IdIsIndex(id, &dummy)) {
213 0 : if (!Enumerate<CheckForDuplicates>(cx, pobj, id, shape.enumerable(), flags, ht,
214 : props))
215 : {
216 0 : return false;
217 : }
218 : }
219 : }
220 :
221 0 : MOZ_ASSERT(firstElemIndex <= props->length());
222 :
223 0 : jsid* ids = props->begin() + firstElemIndex;
224 0 : size_t n = props->length() - firstElemIndex;
225 :
226 0 : AutoIdVector tmp(cx);
227 0 : if (!tmp.resize(n))
228 0 : return false;
229 0 : PodCopy(tmp.begin(), ids, n);
230 :
231 0 : if (!MergeSort(ids, n, tmp.begin(), SortComparatorIntegerIds))
232 0 : return false;
233 : }
234 :
235 581 : if (unboxed) {
236 : // If |unboxed| is set then |pobj| is the expando for an unboxed
237 : // plain object we are enumerating. Add the unboxed properties
238 : // themselves here since they are all property names that were
239 : // given to the object before any of the expando's properties.
240 0 : MOZ_ASSERT(pobj->is<UnboxedExpandoObject>());
241 0 : if (!EnumerateExtraProperties<CheckForDuplicates>(cx, unboxed, flags, ht, props))
242 0 : return false;
243 : }
244 :
245 581 : size_t initialLength = props->length();
246 :
247 : /* Collect all unique property names from this object's shape. */
248 581 : bool symbolsFound = false;
249 581 : Shape::Range<NoGC> r(pobj->lastProperty());
250 8145 : for (; !r.empty(); r.popFront()) {
251 3782 : Shape& shape = r.front();
252 3782 : jsid id = shape.propid();
253 :
254 3782 : if (JSID_IS_SYMBOL(id)) {
255 3 : symbolsFound = true;
256 6 : continue;
257 : }
258 :
259 : uint32_t dummy;
260 3779 : if (isIndexed && IdIsIndex(id, &dummy))
261 0 : continue;
262 :
263 3779 : if (!Enumerate<CheckForDuplicates>(cx, pobj, id, shape.enumerable(), flags, ht, props))
264 0 : return false;
265 : }
266 581 : ::Reverse(props->begin() + initialLength, props->end());
267 :
268 581 : enumerateSymbols = symbolsFound && (flags & JSITER_SYMBOLS);
269 : }
270 :
271 635 : if (enumerateSymbols) {
272 : // Do a second pass to collect symbols. ES6 draft rev 25 (2014 May 22)
273 : // 9.1.12 requires that all symbols appear after all strings in the
274 : // result.
275 55 : size_t initialLength = props->length();
276 213 : for (Shape::Range<NoGC> r(pobj->lastProperty()); !r.empty(); r.popFront()) {
277 158 : Shape& shape = r.front();
278 158 : jsid id = shape.propid();
279 158 : if (JSID_IS_SYMBOL(id)) {
280 1 : if (!Enumerate<CheckForDuplicates>(cx, pobj, id, shape.enumerable(), flags, ht,
281 : props))
282 : {
283 0 : return false;
284 : }
285 : }
286 : }
287 55 : ::Reverse(props->begin() + initialLength, props->end());
288 : }
289 :
290 635 : return true;
291 : }
292 :
293 : static bool
294 635 : EnumerateNativeProperties(JSContext* cx, HandleNativeObject pobj, unsigned flags, Maybe<IdSet>& ht,
295 : AutoIdVector* props, bool checkForDuplicates,
296 : Handle<UnboxedPlainObject*> unboxed = nullptr)
297 : {
298 635 : if (checkForDuplicates)
299 136 : return EnumerateNativeProperties<true>(cx, pobj, flags, ht, props, unboxed);
300 499 : return EnumerateNativeProperties<false>(cx, pobj, flags, ht, props, unboxed);
301 : }
302 :
303 : template <bool CheckForDuplicates>
304 : static bool
305 59 : EnumerateProxyProperties(JSContext* cx, HandleObject pobj, unsigned flags, Maybe<IdSet>& ht,
306 : AutoIdVector* props)
307 : {
308 59 : MOZ_ASSERT(pobj->is<ProxyObject>());
309 :
310 118 : AutoIdVector proxyProps(cx);
311 :
312 59 : if (flags & JSITER_HIDDEN || flags & JSITER_SYMBOLS) {
313 : // This gets all property keys, both strings and symbols. The call to
314 : // Enumerate in the loop below will filter out unwanted keys, per the
315 : // flags.
316 10 : if (!Proxy::ownPropertyKeys(cx, pobj, proxyProps))
317 0 : return false;
318 :
319 20 : Rooted<PropertyDescriptor> desc(cx);
320 145 : for (size_t n = 0, len = proxyProps.length(); n < len; n++) {
321 135 : bool enumerable = false;
322 :
323 : // We need to filter, if the caller just wants enumerable symbols.
324 135 : if (!(flags & JSITER_HIDDEN)) {
325 0 : if (!Proxy::getOwnPropertyDescriptor(cx, pobj, proxyProps[n], &desc))
326 0 : return false;
327 0 : enumerable = desc.enumerable();
328 : }
329 :
330 135 : if (!Enumerate<CheckForDuplicates>(cx, pobj, proxyProps[n], enumerable, flags, ht,
331 : props))
332 : {
333 0 : return false;
334 : }
335 : }
336 :
337 10 : return true;
338 : }
339 :
340 : // Returns enumerable property names (no symbols).
341 49 : if (!Proxy::getOwnEnumerablePropertyKeys(cx, pobj, proxyProps))
342 0 : return false;
343 :
344 225 : for (size_t n = 0, len = proxyProps.length(); n < len; n++) {
345 176 : if (!Enumerate<CheckForDuplicates>(cx, pobj, proxyProps[n], true, flags, ht, props))
346 0 : return false;
347 : }
348 :
349 49 : return true;
350 : }
351 :
352 : #ifdef JS_MORE_DETERMINISTIC
353 :
354 : struct SortComparatorIds
355 : {
356 : JSContext* const cx;
357 :
358 : SortComparatorIds(JSContext* cx)
359 : : cx(cx) {}
360 :
361 : bool operator()(jsid a, jsid b, bool* lessOrEqualp)
362 : {
363 : // Pick an arbitrary order on jsids that is as stable as possible
364 : // across executions.
365 : if (a == b) {
366 : *lessOrEqualp = true;
367 : return true;
368 : }
369 :
370 : size_t ta = JSID_BITS(a) & JSID_TYPE_MASK;
371 : size_t tb = JSID_BITS(b) & JSID_TYPE_MASK;
372 : if (ta != tb) {
373 : *lessOrEqualp = (ta <= tb);
374 : return true;
375 : }
376 :
377 : if (JSID_IS_INT(a)) {
378 : *lessOrEqualp = (JSID_TO_INT(a) <= JSID_TO_INT(b));
379 : return true;
380 : }
381 :
382 : RootedString astr(cx), bstr(cx);
383 : if (JSID_IS_SYMBOL(a)) {
384 : MOZ_ASSERT(JSID_IS_SYMBOL(b));
385 : JS::SymbolCode ca = JSID_TO_SYMBOL(a)->code();
386 : JS::SymbolCode cb = JSID_TO_SYMBOL(b)->code();
387 : if (ca != cb) {
388 : *lessOrEqualp = uint32_t(ca) <= uint32_t(cb);
389 : return true;
390 : }
391 : MOZ_ASSERT(ca == JS::SymbolCode::InSymbolRegistry || ca == JS::SymbolCode::UniqueSymbol);
392 : astr = JSID_TO_SYMBOL(a)->description();
393 : bstr = JSID_TO_SYMBOL(b)->description();
394 : if (!astr || !bstr) {
395 : *lessOrEqualp = !astr;
396 : return true;
397 : }
398 :
399 : // Fall through to string comparison on the descriptions. The sort
400 : // order is nondeterministic if two different unique symbols have
401 : // the same description.
402 : } else {
403 : astr = IdToString(cx, a);
404 : if (!astr)
405 : return false;
406 : bstr = IdToString(cx, b);
407 : if (!bstr)
408 : return false;
409 : }
410 :
411 : int32_t result;
412 : if (!CompareStrings(cx, astr, bstr, &result))
413 : return false;
414 :
415 : *lessOrEqualp = (result <= 0);
416 : return true;
417 : }
418 : };
419 :
420 : #endif /* JS_MORE_DETERMINISTIC */
421 :
422 : static bool
423 624 : Snapshot(JSContext* cx, HandleObject pobj_, unsigned flags, AutoIdVector* props)
424 : {
425 : // We initialize |ht| lazily (in Enumerate()) because it ends up unused
426 : // anywhere from 67--99.9% of the time.
427 1248 : Maybe<IdSet> ht;
428 1248 : RootedObject pobj(cx, pobj_);
429 :
430 : // Don't check for duplicates if we're only interested in own properties.
431 : // This does the right thing for most objects: native objects don't have
432 : // duplicate property ids and we allow the [[OwnPropertyKeys]] proxy trap to
433 : // return duplicates.
434 : //
435 : // The only special case is when the object has a newEnumerate hook: it
436 : // can return duplicate properties and we have to filter them. This is
437 : // handled below.
438 624 : bool checkForDuplicates = !(flags & JSITER_OWNONLY);
439 :
440 133 : do {
441 694 : if (pobj->getClass()->getNewEnumerate()) {
442 3 : if (pobj->is<UnboxedPlainObject>() && pobj->as<UnboxedPlainObject>().maybeExpando()) {
443 : // Special case unboxed objects with an expando object.
444 0 : RootedNativeObject expando(cx, pobj->as<UnboxedPlainObject>().maybeExpando());
445 0 : if (!EnumerateNativeProperties(cx, expando, flags, ht, props, checkForDuplicates,
446 : pobj.as<UnboxedPlainObject>()))
447 : {
448 0 : return false;
449 : }
450 : } else {
451 : // The newEnumerate hook may return duplicates. Whitelist the
452 : // unboxed object hooks because we know they are well-behaved.
453 3 : if (!pobj->is<UnboxedPlainObject>() && !pobj->is<UnboxedArrayObject>())
454 3 : checkForDuplicates = true;
455 :
456 3 : if (checkForDuplicates) {
457 3 : if (!EnumerateExtraProperties<true>(cx, pobj, flags, ht, props))
458 0 : return false;
459 : } else {
460 0 : if (!EnumerateExtraProperties<false>(cx, pobj, flags, ht, props))
461 0 : return false;
462 : }
463 :
464 3 : if (pobj->isNative()) {
465 3 : if (!EnumerateNativeProperties(cx, pobj.as<NativeObject>(), flags, ht, props,
466 : checkForDuplicates))
467 : {
468 0 : return false;
469 : }
470 : }
471 : }
472 691 : } else if (pobj->isNative()) {
473 : // Give the object a chance to resolve all lazy properties
474 632 : if (JSEnumerateOp enumerate = pobj->getClass()->getEnumerate()) {
475 8 : if (!enumerate(cx, pobj.as<NativeObject>()))
476 0 : return false;
477 : }
478 632 : if (!EnumerateNativeProperties(cx, pobj.as<NativeObject>(), flags, ht, props,
479 : checkForDuplicates))
480 : {
481 0 : return false;
482 : }
483 59 : } else if (pobj->is<ProxyObject>()) {
484 59 : if (checkForDuplicates) {
485 0 : if (!EnumerateProxyProperties<true>(cx, pobj, flags, ht, props))
486 0 : return false;
487 : } else {
488 59 : if (!EnumerateProxyProperties<false>(cx, pobj, flags, ht, props))
489 0 : return false;
490 : }
491 : } else {
492 0 : MOZ_CRASH("non-native objects must have an enumerate op");
493 : }
494 :
495 694 : if (flags & JSITER_OWNONLY)
496 561 : break;
497 :
498 133 : if (!GetPrototype(cx, pobj, &pobj))
499 0 : return false;
500 :
501 : } while (pobj != nullptr);
502 :
503 : #ifdef JS_MORE_DETERMINISTIC
504 :
505 : /*
506 : * In some cases the enumeration order for an object depends on the
507 : * execution mode (interpreter vs. JIT), especially for native objects
508 : * with a class enumerate hook (where resolving a property changes the
509 : * resulting enumeration order). These aren't really bugs, but the
510 : * differences can change the generated output and confuse correctness
511 : * fuzzers, so we sort the ids if such a fuzzer is running.
512 : *
513 : * We don't do this in the general case because (a) doing so is slow,
514 : * and (b) it also breaks the web, which expects enumeration order to
515 : * follow the order in which properties are added, in certain cases.
516 : * Since ECMA does not specify an enumeration order for objects, both
517 : * behaviors are technically correct to do.
518 : */
519 :
520 : jsid* ids = props->begin();
521 : size_t n = props->length();
522 :
523 : AutoIdVector tmp(cx);
524 : if (!tmp.resize(n))
525 : return false;
526 : PodCopy(tmp.begin(), ids, n);
527 :
528 : if (!MergeSort(ids, n, tmp.begin(), SortComparatorIds(cx)))
529 : return false;
530 :
531 : #endif /* JS_MORE_DETERMINISTIC */
532 :
533 624 : return true;
534 : }
535 :
536 : JS_FRIEND_API(bool)
537 561 : js::GetPropertyKeys(JSContext* cx, HandleObject obj, unsigned flags, AutoIdVector* props)
538 : {
539 561 : return Snapshot(cx, obj,
540 : flags & (JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS | JSITER_SYMBOLSONLY),
541 561 : props);
542 : }
543 :
544 : size_t sCustomIteratorCount = 0;
545 :
546 : static inline bool
547 63 : GetCustomIterator(JSContext* cx, HandleObject obj, unsigned flags, MutableHandleObject objp)
548 : {
549 63 : if (MOZ_UNLIKELY(!CheckRecursionLimit(cx)))
550 0 : return false;
551 :
552 126 : RootedValue rval(cx);
553 : /* Check whether we have a valid __iterator__ method. */
554 63 : HandlePropertyName name = cx->names().iteratorIntrinsic;
555 63 : if (!GetProperty(cx, obj, obj, name, &rval))
556 0 : return false;
557 :
558 : /* If there is no custom __iterator__ method, we are done here. */
559 63 : if (MOZ_LIKELY(!rval.isObject())) {
560 63 : objp.set(nullptr);
561 63 : return true;
562 : }
563 :
564 0 : if (!cx->runningWithTrustedPrincipals())
565 0 : ++sCustomIteratorCount;
566 :
567 : /* Otherwise call it and return that object. */
568 : {
569 0 : FixedInvokeArgs<1> args(cx);
570 :
571 0 : args[0].setBoolean((flags & JSITER_FOREACH) == 0);
572 :
573 0 : RootedValue thisv(cx, ObjectValue(*obj));
574 0 : if (!js::Call(cx, rval, thisv, args, &rval))
575 0 : return false;
576 : }
577 :
578 0 : if (rval.isPrimitive()) {
579 : // Ignore the stack when throwing. We can't tell whether we were
580 : // supposed to skip over a new.target or not.
581 0 : JSAutoByteString bytes;
582 0 : if (!AtomToPrintableString(cx, name, &bytes))
583 0 : return false;
584 0 : RootedValue val(cx, ObjectValue(*obj));
585 0 : ReportValueError2(cx, JSMSG_BAD_TRAP_RETURN_VALUE,
586 0 : JSDVG_IGNORE_STACK, val, nullptr, bytes.ptr());
587 0 : return false;
588 : }
589 0 : objp.set(&rval.toObject());
590 0 : return true;
591 : }
592 :
593 : template <typename T>
594 : static inline bool
595 30 : Compare(T* a, T* b, size_t c)
596 : {
597 30 : size_t n = (c + size_t(7)) / size_t(8);
598 30 : switch (c % 8) {
599 30 : case 0: do { if (*a++ != *b++) return false; MOZ_FALLTHROUGH;
600 0 : case 7: if (*a++ != *b++) return false; MOZ_FALLTHROUGH;
601 0 : case 6: if (*a++ != *b++) return false; MOZ_FALLTHROUGH;
602 0 : case 5: if (*a++ != *b++) return false; MOZ_FALLTHROUGH;
603 0 : case 4: if (*a++ != *b++) return false; MOZ_FALLTHROUGH;
604 4 : case 3: if (*a++ != *b++) return false; MOZ_FALLTHROUGH;
605 30 : case 2: if (*a++ != *b++) return false; MOZ_FALLTHROUGH;
606 30 : case 1: if (*a++ != *b++) return false;
607 : } while (--n > 0);
608 : }
609 30 : return true;
610 : }
611 :
612 : static bool legacy_iterator_next(JSContext* cx, unsigned argc, Value* vp);
613 :
614 : static inline PropertyIteratorObject*
615 63 : NewPropertyIteratorObject(JSContext* cx, unsigned flags)
616 : {
617 63 : if (flags & JSITER_ENUMERATE) {
618 116 : RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &PropertyIteratorObject::class_,
619 232 : TaggedProto(nullptr)));
620 58 : if (!group)
621 0 : return nullptr;
622 :
623 58 : const Class* clasp = &PropertyIteratorObject::class_;
624 116 : RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, TaggedProto(nullptr),
625 116 : ITERATOR_FINALIZE_KIND));
626 58 : if (!shape)
627 0 : return nullptr;
628 :
629 : JSObject* obj;
630 58 : JS_TRY_VAR_OR_RETURN_NULL(cx, obj, NativeObject::create(cx, ITERATOR_FINALIZE_KIND,
631 : GetInitialHeap(GenericObject, clasp),
632 : shape, group));
633 :
634 58 : PropertyIteratorObject* res = &obj->as<PropertyIteratorObject>();
635 :
636 : // CodeGenerator::visitIteratorStartO assumes the iterator object is not
637 : // inside the nursery when deciding whether a barrier is necessary.
638 58 : MOZ_ASSERT(!js::gc::IsInsideNursery(res));
639 :
640 58 : MOZ_ASSERT(res->numFixedSlots() == JSObject::ITER_CLASS_NFIXED_SLOTS);
641 58 : return res;
642 : }
643 :
644 10 : Rooted<PropertyIteratorObject*> res(cx, NewBuiltinClassInstance<PropertyIteratorObject>(cx));
645 5 : if (!res)
646 0 : return nullptr;
647 :
648 5 : if (flags == 0) {
649 : // Redefine next as an own property. This ensure that deleting the
650 : // next method on the prototype doesn't break cross-global for .. in.
651 : // We don't have to do this for JSITER_ENUMERATE because that object always
652 : // takes an optimized path.
653 10 : RootedFunction next(cx, NewNativeFunction(cx, legacy_iterator_next, 0,
654 15 : HandlePropertyName(cx->names().next)));
655 5 : if (!next)
656 0 : return nullptr;
657 :
658 10 : RootedValue value(cx, ObjectValue(*next));
659 5 : if (!DefineProperty(cx, res, cx->names().next, value))
660 0 : return nullptr;
661 : }
662 :
663 5 : return res;
664 : }
665 :
666 : NativeIterator*
667 91 : NativeIterator::allocateIterator(JSContext* cx, uint32_t numGuards, uint32_t plength)
668 : {
669 : JS_STATIC_ASSERT(sizeof(ReceiverGuard) == 2 * sizeof(void*));
670 :
671 91 : size_t extraLength = plength + numGuards * 2;
672 91 : NativeIterator* ni = cx->zone()->pod_malloc_with_extra<NativeIterator, void*>(extraLength);
673 91 : if (!ni) {
674 0 : ReportOutOfMemory(cx);
675 0 : return nullptr;
676 : }
677 :
678 91 : void** extra = reinterpret_cast<void**>(ni + 1);
679 91 : PodZero(ni);
680 91 : PodZero(extra, extraLength);
681 91 : ni->props_array = ni->props_cursor = reinterpret_cast<GCPtrFlatString*>(extra);
682 91 : ni->props_end = ni->props_array + plength;
683 91 : return ni;
684 : }
685 :
686 : NativeIterator*
687 315 : NativeIterator::allocateSentinel(JSContext* maybecx)
688 : {
689 315 : NativeIterator* ni = js_pod_malloc<NativeIterator>();
690 315 : if (!ni) {
691 0 : if (maybecx)
692 0 : ReportOutOfMemory(maybecx);
693 0 : return nullptr;
694 : }
695 :
696 315 : PodZero(ni);
697 :
698 315 : ni->next_ = ni;
699 315 : ni->prev_ = ni;
700 315 : return ni;
701 : }
702 :
703 : inline void
704 91 : NativeIterator::init(JSObject* obj, JSObject* iterObj, unsigned flags, uint32_t numGuards, uint32_t key)
705 : {
706 91 : this->obj.init(obj);
707 91 : this->iterObj_ = iterObj;
708 91 : this->flags = flags;
709 91 : this->guard_array = (HeapReceiverGuard*) this->props_end;
710 91 : this->guard_length = numGuards;
711 91 : this->guard_key = key;
712 91 : }
713 :
714 : bool
715 63 : NativeIterator::initProperties(JSContext* cx, Handle<PropertyIteratorObject*> obj,
716 : const AutoIdVector& props)
717 : {
718 : // The obj parameter is just so that we can ensure that this object will get
719 : // traced if we GC.
720 63 : MOZ_ASSERT(this == obj->getNativeIterator());
721 :
722 63 : size_t plength = props.length();
723 63 : MOZ_ASSERT(plength == size_t(end() - begin()));
724 :
725 755 : for (size_t i = 0; i < plength; i++) {
726 692 : JSFlatString* str = IdToString(cx, props[i]);
727 692 : if (!str)
728 0 : return false;
729 692 : props_array[i].init(str);
730 : }
731 :
732 63 : return true;
733 : }
734 :
735 : static inline void
736 98 : RegisterEnumerator(JSContext* cx, PropertyIteratorObject* iterobj, NativeIterator* ni)
737 : {
738 : /* Register non-escaping native enumerators (for-in) with the current context. */
739 98 : if (ni->flags & JSITER_ENUMERATE) {
740 93 : ni->link(cx->compartment()->enumerators);
741 :
742 93 : MOZ_ASSERT(!(ni->flags & JSITER_ACTIVE));
743 93 : ni->flags |= JSITER_ACTIVE;
744 : }
745 98 : }
746 :
747 : static inline PropertyIteratorObject*
748 63 : VectorToKeyIterator(JSContext* cx, HandleObject obj, unsigned flags, AutoIdVector& keys,
749 : uint32_t numGuards, uint32_t key)
750 : {
751 63 : MOZ_ASSERT(!(flags & JSITER_FOREACH));
752 :
753 63 : if (obj->isSingleton() && !JSObject::setIteratedSingleton(cx, obj))
754 0 : return nullptr;
755 63 : MarkObjectGroupFlags(cx, obj, OBJECT_FLAG_ITERATED);
756 :
757 126 : Rooted<PropertyIteratorObject*> iterobj(cx, NewPropertyIteratorObject(cx, flags));
758 63 : if (!iterobj)
759 0 : return nullptr;
760 :
761 63 : NativeIterator* ni = NativeIterator::allocateIterator(cx, numGuards, keys.length());
762 63 : if (!ni)
763 0 : return nullptr;
764 :
765 63 : iterobj->setNativeIterator(ni);
766 63 : ni->init(obj, iterobj, flags, numGuards, key);
767 63 : if (!ni->initProperties(cx, iterobj, keys))
768 0 : return nullptr;
769 :
770 63 : if (numGuards) {
771 : // Fill in the guard array from scratch.
772 56 : JSObject* pobj = obj;
773 56 : size_t ind = 0;
774 61 : do {
775 117 : ni->guard_array[ind++].init(ReceiverGuard(pobj));
776 :
777 : // The one caller of this method that passes |numGuards > 0|, does
778 : // so only if the entire chain consists of cacheable objects (that
779 : // necessarily have static prototypes).
780 117 : pobj = pobj->staticPrototype();
781 117 : } while (pobj);
782 56 : MOZ_ASSERT(ind == numGuards);
783 : }
784 :
785 63 : RegisterEnumerator(cx, iterobj, ni);
786 63 : return iterobj;
787 : }
788 :
789 : static PropertyIteratorObject*
790 0 : VectorToValueIterator(JSContext* cx, HandleObject obj, unsigned flags, AutoIdVector& keys)
791 : {
792 0 : MOZ_ASSERT(flags & JSITER_FOREACH);
793 :
794 0 : if (obj->isSingleton() && !JSObject::setIteratedSingleton(cx, obj))
795 0 : return nullptr;
796 0 : MarkObjectGroupFlags(cx, obj, OBJECT_FLAG_ITERATED);
797 :
798 0 : Rooted<PropertyIteratorObject*> iterobj(cx, NewPropertyIteratorObject(cx, flags));
799 0 : if (!iterobj)
800 0 : return nullptr;
801 :
802 0 : NativeIterator* ni = NativeIterator::allocateIterator(cx, 0, keys.length());
803 0 : if (!ni)
804 0 : return nullptr;
805 :
806 0 : iterobj->setNativeIterator(ni);
807 0 : ni->init(obj, iterobj, flags, 0, 0);
808 0 : if (!ni->initProperties(cx, iterobj, keys))
809 0 : return nullptr;
810 :
811 0 : RegisterEnumerator(cx, iterobj, ni);
812 0 : return iterobj;
813 : }
814 :
815 : JSObject*
816 0 : js::EnumeratedIdVectorToIterator(JSContext* cx, HandleObject obj, unsigned flags,
817 : AutoIdVector& props)
818 : {
819 0 : if (!(flags & JSITER_FOREACH))
820 0 : return VectorToKeyIterator(cx, obj, flags, props, 0, 0);
821 :
822 0 : return VectorToValueIterator(cx, obj, flags, props);
823 : }
824 :
825 : // Mainly used for .. in over null/undefined
826 : JSObject*
827 0 : js::NewEmptyPropertyIterator(JSContext* cx, unsigned flags)
828 : {
829 0 : Rooted<PropertyIteratorObject*> iterobj(cx, NewPropertyIteratorObject(cx, flags));
830 0 : if (!iterobj)
831 0 : return nullptr;
832 :
833 0 : AutoIdVector keys(cx); // Empty
834 0 : NativeIterator* ni = NativeIterator::allocateIterator(cx, 0, keys.length());
835 0 : if (!ni)
836 0 : return nullptr;
837 :
838 0 : iterobj->setNativeIterator(ni);
839 0 : ni->init(nullptr, iterobj, flags, 0, 0);
840 0 : if (!ni->initProperties(cx, iterobj, keys))
841 0 : return nullptr;
842 :
843 0 : RegisterEnumerator(cx, iterobj, ni);
844 0 : return iterobj;
845 : }
846 :
847 : static inline void
848 35 : UpdateNativeIterator(NativeIterator* ni, JSObject* obj)
849 : {
850 : // Update the object for which the native iterator is associated, so
851 : // SuppressDeletedPropertyHelper will recognize the iterator as a match.
852 35 : ni->obj = obj;
853 35 : }
854 :
855 : static inline bool
856 257 : CanCompareIterableObjectToCache(JSObject* obj)
857 : {
858 257 : if (obj->isNative())
859 252 : return obj->as<NativeObject>().hasEmptyElements();
860 5 : if (obj->is<UnboxedPlainObject>()) {
861 0 : if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
862 0 : return expando->hasEmptyElements();
863 0 : return true;
864 : }
865 5 : return false;
866 : }
867 :
868 : static MOZ_ALWAYS_INLINE void
869 86 : UpdateLastCachedNativeIterator(JSContext* cx, JSObject* obj, PropertyIteratorObject* iterobj)
870 : {
871 : // lastCachedNativeIterator is only used when there's a single object on
872 : // the prototype chain, to simplify JIT code.
873 86 : if (iterobj->getNativeIterator()->guard_length != 2)
874 11 : return;
875 :
876 : // Both GetIterator and JIT code assume the receiver has a non-null proto,
877 : // so we have to make sure a Shape change is triggered when the proto
878 : // changes. Note that this does not apply to the object on the proto chain
879 : // because we always check it has a null proto.
880 75 : if (obj->hasUncacheableProto())
881 0 : return;
882 :
883 75 : cx->compartment()->lastCachedNativeIterator = iterobj;
884 : }
885 :
886 : using ReceiverGuardVector = Vector<ReceiverGuard, 8>;
887 :
888 : static MOZ_ALWAYS_INLINE PropertyIteratorObject*
889 93 : LookupInIteratorCache(JSContext* cx, JSObject* obj, ReceiverGuardVector& guards, uint32_t* keyArg)
890 : {
891 93 : MOZ_ASSERT(guards.empty());
892 :
893 : // The iterator object for JSITER_ENUMERATE never escapes, so we don't
894 : // care that the "proper" prototype is set. This also lets us reuse an
895 : // old, inactive iterator object.
896 :
897 93 : uint32_t key = 0;
898 93 : JSObject* pobj = obj;
899 97 : do {
900 190 : if (!CanCompareIterableObjectToCache(pobj)) {
901 6 : guards.clear();
902 12 : return nullptr;
903 : }
904 :
905 184 : ReceiverGuard guard(pobj);
906 184 : key = (key + (key << 16)) ^ guard.hash();
907 :
908 184 : if (MOZ_UNLIKELY(!guards.append(guard))) {
909 0 : cx->recoverFromOutOfMemory();
910 0 : guards.clear();
911 0 : return nullptr;
912 : }
913 :
914 184 : pobj = pobj->staticPrototype();
915 184 : } while (pobj);
916 :
917 87 : MOZ_ASSERT(!guards.empty());
918 87 : *keyArg = key;
919 :
920 87 : PropertyIteratorObject* iterobj = cx->caches().nativeIterCache.get(key);
921 87 : if (!iterobj)
922 50 : return nullptr;
923 :
924 37 : NativeIterator* ni = iterobj->getNativeIterator();
925 109 : if ((ni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) ||
926 65 : ni->guard_key != key ||
927 60 : ni->guard_length != guards.length() ||
928 30 : !Compare(reinterpret_cast<ReceiverGuard*>(ni->guard_array),
929 97 : guards.begin(), ni->guard_length) ||
930 30 : iterobj->compartment() != cx->compartment())
931 : {
932 7 : return nullptr;
933 : }
934 :
935 30 : UpdateNativeIterator(ni, obj);
936 30 : RegisterEnumerator(cx, iterobj, ni);
937 :
938 30 : UpdateLastCachedNativeIterator(cx, obj, iterobj);
939 :
940 30 : return iterobj;
941 : }
942 :
943 : static bool
944 235 : CanStoreInIteratorCache(JSContext* cx, JSObject* obj)
945 : {
946 122 : do {
947 235 : if (obj->isNative()) {
948 235 : MOZ_ASSERT(obj->as<NativeObject>().hasEmptyElements());
949 :
950 : // Typed arrays have indexed properties not captured by the Shape guard.
951 : // Enumerate hooks may add extra properties.
952 235 : const Class* clasp = obj->getClass();
953 235 : if (MOZ_UNLIKELY(IsTypedArrayClass(clasp)))
954 0 : return false;
955 235 : if (MOZ_UNLIKELY(clasp->getNewEnumerate() || clasp->getEnumerate()))
956 1 : return false;
957 :
958 234 : if (MOZ_UNLIKELY(obj->as<NativeObject>().containsPure(cx->names().iteratorIntrinsic)))
959 0 : return false;
960 : } else {
961 0 : MOZ_ASSERT(obj->is<UnboxedPlainObject>());
962 : }
963 :
964 234 : obj = obj->staticPrototype();
965 234 : } while (obj);
966 :
967 112 : return true;
968 : }
969 :
970 : static void
971 56 : StoreInIteratorCache(JSContext* cx, JSObject* obj, uint32_t key, PropertyIteratorObject* iterobj)
972 : {
973 56 : MOZ_ASSERT(CanStoreInIteratorCache(cx, obj));
974 56 : MOZ_ASSERT(iterobj->getNativeIterator()->guard_length > 0);
975 :
976 56 : cx->caches().nativeIterCache.set(key, iterobj);
977 :
978 56 : UpdateLastCachedNativeIterator(cx, obj, iterobj);
979 56 : }
980 :
981 : JSObject*
982 103 : js::GetIterator(JSContext* cx, HandleObject obj, unsigned flags)
983 : {
984 206 : ReceiverGuardVector guards(cx);
985 103 : uint32_t key = 0;
986 103 : if (flags == JSITER_ENUMERATE) {
987 : // Check to see if this is the same as the most recent object which was
988 : // iterated over.
989 98 : if (PropertyIteratorObject* last = cx->compartment()->lastCachedNativeIterator) {
990 64 : NativeIterator* lastni = last->getNativeIterator();
991 256 : if (!(lastni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) &&
992 311 : CanCompareIterableObjectToCache(obj) &&
993 308 : ReceiverGuard(obj) == lastni->guard_array[0])
994 : {
995 6 : JSObject* proto = obj->staticPrototype();
996 36 : if (CanCompareIterableObjectToCache(proto) &&
997 41 : ReceiverGuard(proto) == lastni->guard_array[1] &&
998 5 : !proto->staticPrototype())
999 : {
1000 5 : assertSameCompartment(cx, last);
1001 5 : UpdateNativeIterator(lastni, obj);
1002 5 : RegisterEnumerator(cx, last, lastni);
1003 5 : return last;
1004 : }
1005 : }
1006 : }
1007 :
1008 93 : if (PropertyIteratorObject* iterObj = LookupInIteratorCache(cx, obj, guards, &key))
1009 30 : return iterObj;
1010 :
1011 63 : if (!guards.empty() && !CanStoreInIteratorCache(cx, obj))
1012 1 : guards.clear();
1013 : }
1014 :
1015 68 : if (MOZ_UNLIKELY(obj->is<PropertyIteratorObject>() || obj->is<LegacyGeneratorObject>()))
1016 0 : return obj;
1017 :
1018 : // We should only call the enumerate trap for "for-in".
1019 : // Or when we call GetIterator from the Proxy [[Enumerate]] hook.
1020 : // JSITER_ENUMERATE is just an optimization and the same
1021 : // as flags == 0 otherwise.
1022 68 : if (flags == 0 || flags == JSITER_ENUMERATE) {
1023 68 : if (MOZ_UNLIKELY(obj->is<ProxyObject>()))
1024 5 : return Proxy::enumerate(cx, obj);
1025 : }
1026 :
1027 126 : RootedObject res(cx);
1028 63 : if (!GetCustomIterator(cx, obj, flags, &res))
1029 0 : return nullptr;
1030 63 : if (res) {
1031 0 : assertSameCompartment(cx, res);
1032 0 : return res;
1033 : }
1034 :
1035 126 : AutoIdVector keys(cx);
1036 63 : if (!Snapshot(cx, obj, flags, &keys))
1037 0 : return nullptr;
1038 :
1039 63 : if (flags & JSITER_FOREACH) {
1040 0 : MOZ_ASSERT(guards.empty());
1041 0 : res = VectorToValueIterator(cx, obj, flags, keys);
1042 0 : if (!res)
1043 0 : return nullptr;
1044 : } else {
1045 63 : res = VectorToKeyIterator(cx, obj, flags, keys, guards.length(), key);
1046 63 : if (!res)
1047 0 : return nullptr;
1048 : }
1049 :
1050 63 : PropertyIteratorObject* iterobj = &res->as<PropertyIteratorObject>();
1051 63 : assertSameCompartment(cx, iterobj);
1052 :
1053 : // Cache the iterator object.
1054 63 : if (!guards.empty())
1055 56 : StoreInIteratorCache(cx, obj, key, iterobj);
1056 :
1057 63 : return iterobj;
1058 : }
1059 :
1060 : // ES 2017 draft 7.4.7.
1061 : JSObject*
1062 0 : js::CreateIterResultObject(JSContext* cx, HandleValue value, bool done)
1063 : {
1064 : // Step 1 (implicit).
1065 :
1066 : // Step 2.
1067 0 : RootedObject resultObj(cx, NewBuiltinClassInstance<PlainObject>(cx));
1068 0 : if (!resultObj)
1069 0 : return nullptr;
1070 :
1071 : // Step 3.
1072 0 : if (!DefineProperty(cx, resultObj, cx->names().value, value))
1073 0 : return nullptr;
1074 :
1075 : // Step 4.
1076 0 : if (!DefineProperty(cx, resultObj, cx->names().done,
1077 0 : done ? TrueHandleValue : FalseHandleValue))
1078 : {
1079 0 : return nullptr;
1080 : }
1081 :
1082 : // Step 5.
1083 0 : return resultObj;
1084 : }
1085 :
1086 : bool
1087 5 : js::ThrowStopIteration(JSContext* cx)
1088 : {
1089 5 : MOZ_ASSERT(!JS_IsExceptionPending(cx));
1090 :
1091 : // StopIteration isn't a constructor, but it's stored in GlobalObject
1092 : // as one, out of laziness. Hence the GetBuiltinConstructor call here.
1093 10 : RootedObject ctor(cx);
1094 5 : if (GetBuiltinConstructor(cx, JSProto_StopIteration, &ctor))
1095 5 : cx->setPendingException(ObjectValue(*ctor));
1096 10 : return false;
1097 : }
1098 :
1099 : /*** Iterator objects ****************************************************************************/
1100 :
1101 : bool
1102 0 : js::IteratorConstructor(JSContext* cx, unsigned argc, Value* vp)
1103 : {
1104 0 : CallArgs args = CallArgsFromVp(argc, vp);
1105 0 : if (args.length() == 0) {
1106 0 : ReportMissingArg(cx, args.calleev(), 0);
1107 0 : return false;
1108 : }
1109 :
1110 0 : bool keyonly = false;
1111 0 : if (args.length() >= 2)
1112 0 : keyonly = ToBoolean(args[1]);
1113 0 : unsigned flags = JSITER_OWNONLY | (keyonly ? 0 : (JSITER_FOREACH | JSITER_KEYVALUE));
1114 :
1115 0 : RootedObject iterobj(cx, ValueToIterator(cx, flags, args[0]));
1116 0 : if (!iterobj)
1117 0 : return false;
1118 0 : args.rval().setObject(*iterobj);
1119 0 : return true;
1120 : }
1121 :
1122 : MOZ_ALWAYS_INLINE bool
1123 185 : NativeIteratorNext(JSContext* cx, NativeIterator* ni, MutableHandleValue rval, bool* done)
1124 : {
1125 185 : *done = false;
1126 :
1127 185 : if (ni->props_cursor >= ni->props_end) {
1128 27 : *done = true;
1129 27 : return true;
1130 : }
1131 :
1132 158 : if (MOZ_LIKELY(ni->isKeyIter())) {
1133 158 : rval.setString(*ni->current());
1134 158 : ni->incCursor();
1135 158 : return true;
1136 : }
1137 :
1138 : // Non-standard Iterator for "for each"
1139 0 : RootedId id(cx);
1140 0 : RootedValue current(cx, StringValue(*ni->current()));
1141 0 : if (!ValueToId<CanGC>(cx, current, &id))
1142 0 : return false;
1143 0 : ni->incCursor();
1144 0 : RootedObject obj(cx, ni->obj);
1145 0 : if (!GetProperty(cx, obj, obj, id, rval))
1146 0 : return false;
1147 :
1148 : // JS 1.7 only: for each (let [k, v] in obj)
1149 0 : if (ni->flags & JSITER_KEYVALUE)
1150 0 : return NewKeyValuePair(cx, id, rval, rval);
1151 0 : return true;
1152 : }
1153 :
1154 : MOZ_ALWAYS_INLINE bool
1155 40 : IsIterator(HandleValue v)
1156 : {
1157 40 : return v.isObject() && v.toObject().hasClass(&PropertyIteratorObject::class_);
1158 : }
1159 :
1160 : MOZ_ALWAYS_INLINE bool
1161 20 : legacy_iterator_next_impl(JSContext* cx, const CallArgs& args)
1162 : {
1163 20 : MOZ_ASSERT(IsIterator(args.thisv()));
1164 :
1165 40 : RootedObject thisObj(cx, &args.thisv().toObject());
1166 :
1167 20 : NativeIterator* ni = thisObj.as<PropertyIteratorObject>()->getNativeIterator();
1168 40 : RootedValue value(cx);
1169 : bool done;
1170 20 : if (!NativeIteratorNext(cx, ni, &value, &done))
1171 0 : return false;
1172 :
1173 : // Use old iterator protocol for compatibility reasons.
1174 20 : if (done) {
1175 5 : ThrowStopIteration(cx);
1176 5 : return false;
1177 : }
1178 :
1179 15 : args.rval().set(value);
1180 15 : return true;
1181 : }
1182 :
1183 : static bool
1184 20 : legacy_iterator_next(JSContext* cx, unsigned argc, Value* vp)
1185 : {
1186 20 : CallArgs args = CallArgsFromVp(argc, vp);
1187 20 : return CallNonGenericMethod<IsIterator, legacy_iterator_next_impl>(cx, args);
1188 : }
1189 :
1190 : static const JSFunctionSpec legacy_iterator_methods[] = {
1191 : JS_SELF_HOSTED_SYM_FN(iterator, "LegacyIteratorShim", 0, 0),
1192 : JS_FN("next", legacy_iterator_next, 0, 0),
1193 : JS_FS_END
1194 : };
1195 :
1196 : size_t
1197 0 : PropertyIteratorObject::sizeOfMisc(mozilla::MallocSizeOf mallocSizeOf) const
1198 : {
1199 0 : return mallocSizeOf(getPrivate());
1200 : }
1201 :
1202 : void
1203 0 : PropertyIteratorObject::trace(JSTracer* trc, JSObject* obj)
1204 : {
1205 0 : if (NativeIterator* ni = obj->as<PropertyIteratorObject>().getNativeIterator())
1206 0 : ni->trace(trc);
1207 0 : }
1208 :
1209 : void
1210 0 : PropertyIteratorObject::finalize(FreeOp* fop, JSObject* obj)
1211 : {
1212 0 : if (NativeIterator* ni = obj->as<PropertyIteratorObject>().getNativeIterator())
1213 0 : fop->free_(ni);
1214 0 : }
1215 :
1216 : const ClassOps PropertyIteratorObject::classOps_ = {
1217 : nullptr, /* addProperty */
1218 : nullptr, /* delProperty */
1219 : nullptr, /* getProperty */
1220 : nullptr, /* setProperty */
1221 : nullptr, /* enumerate */
1222 : nullptr, /* newEnumerate */
1223 : nullptr, /* resolve */
1224 : nullptr, /* mayResolve */
1225 : finalize,
1226 : nullptr, /* call */
1227 : nullptr, /* hasInstance */
1228 : nullptr, /* construct */
1229 : trace
1230 : };
1231 :
1232 : const Class PropertyIteratorObject::class_ = {
1233 : "Iterator",
1234 : JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator) |
1235 : JSCLASS_HAS_PRIVATE |
1236 : JSCLASS_BACKGROUND_FINALIZE,
1237 : &PropertyIteratorObject::classOps_
1238 : };
1239 :
1240 : static const Class ArrayIteratorPrototypeClass = {
1241 : "Array Iterator",
1242 : 0
1243 : };
1244 :
1245 : enum {
1246 : ArrayIteratorSlotIteratedObject,
1247 : ArrayIteratorSlotNextIndex,
1248 : ArrayIteratorSlotItemKind,
1249 : ArrayIteratorSlotCount
1250 : };
1251 :
1252 : const Class ArrayIteratorObject::class_ = {
1253 : "Array Iterator",
1254 : JSCLASS_HAS_RESERVED_SLOTS(ArrayIteratorSlotCount)
1255 : };
1256 :
1257 :
1258 : ArrayIteratorObject*
1259 2115 : js::NewArrayIteratorObject(JSContext* cx, NewObjectKind newKind)
1260 : {
1261 4230 : RootedObject proto(cx, GlobalObject::getOrCreateArrayIteratorPrototype(cx, cx->global()));
1262 2115 : if (!proto)
1263 0 : return nullptr;
1264 :
1265 2115 : return NewObjectWithGivenProto<ArrayIteratorObject>(cx, proto, newKind);
1266 : }
1267 :
1268 : static const JSFunctionSpec array_iterator_methods[] = {
1269 : JS_SELF_HOSTED_FN("next", "ArrayIteratorNext", 0, 0),
1270 : JS_FS_END
1271 : };
1272 :
1273 : static const Class StringIteratorPrototypeClass = {
1274 : "String Iterator",
1275 : 0
1276 : };
1277 :
1278 : enum {
1279 : StringIteratorSlotIteratedObject,
1280 : StringIteratorSlotNextIndex,
1281 : StringIteratorSlotCount
1282 : };
1283 :
1284 : const Class StringIteratorObject::class_ = {
1285 : "String Iterator",
1286 : JSCLASS_HAS_RESERVED_SLOTS(StringIteratorSlotCount)
1287 : };
1288 :
1289 : static const JSFunctionSpec string_iterator_methods[] = {
1290 : JS_SELF_HOSTED_FN("next", "StringIteratorNext", 0, 0),
1291 : JS_FS_END
1292 : };
1293 :
1294 : StringIteratorObject*
1295 0 : js::NewStringIteratorObject(JSContext* cx, NewObjectKind newKind)
1296 : {
1297 0 : RootedObject proto(cx, GlobalObject::getOrCreateStringIteratorPrototype(cx, cx->global()));
1298 0 : if (!proto)
1299 0 : return nullptr;
1300 :
1301 0 : return NewObjectWithGivenProto<StringIteratorObject>(cx, proto, newKind);
1302 : }
1303 :
1304 : JSObject*
1305 98 : js::ValueToIterator(JSContext* cx, unsigned flags, HandleValue vp)
1306 : {
1307 : /* JSITER_KEYVALUE must always come with JSITER_FOREACH */
1308 98 : MOZ_ASSERT_IF(flags & JSITER_KEYVALUE, flags & JSITER_FOREACH);
1309 :
1310 196 : RootedObject obj(cx);
1311 98 : if (vp.isObject()) {
1312 : /* Common case. */
1313 98 : obj = &vp.toObject();
1314 0 : } else if ((flags & JSITER_ENUMERATE) && vp.isNullOrUndefined()) {
1315 : /*
1316 : * Enumerating over null and undefined gives an empty enumerator, so
1317 : * that |for (var p in <null or undefined>) <loop>;| never executes
1318 : * <loop>, per ES5 12.6.4.
1319 : */
1320 0 : return NewEmptyPropertyIterator(cx, flags);
1321 : } else {
1322 0 : obj = ToObject(cx, vp);
1323 0 : if (!obj)
1324 0 : return nullptr;
1325 : }
1326 :
1327 98 : return GetIterator(cx, obj, flags);
1328 : }
1329 :
1330 : bool
1331 98 : js::CloseIterator(JSContext* cx, HandleObject obj)
1332 : {
1333 98 : if (obj->is<PropertyIteratorObject>()) {
1334 : /* Remove enumerators from the active list, which is a stack. */
1335 93 : NativeIterator* ni = obj->as<PropertyIteratorObject>().getNativeIterator();
1336 :
1337 93 : if (ni->flags & JSITER_ENUMERATE) {
1338 93 : ni->unlink();
1339 :
1340 93 : MOZ_ASSERT(ni->flags & JSITER_ACTIVE);
1341 93 : ni->flags &= ~JSITER_ACTIVE;
1342 :
1343 : /*
1344 : * Reset the enumerator; it may still be in the cached iterators
1345 : * for this thread, and can be reused.
1346 : */
1347 93 : ni->props_cursor = ni->props_array;
1348 : }
1349 5 : } else if (obj->is<LegacyGeneratorObject>()) {
1350 0 : Rooted<LegacyGeneratorObject*> genObj(cx, &obj->as<LegacyGeneratorObject>());
1351 0 : if (genObj->isClosed())
1352 0 : return true;
1353 0 : if (genObj->isRunning() || genObj->isClosing()) {
1354 : // Nothing sensible to do.
1355 0 : return true;
1356 : }
1357 0 : return LegacyGeneratorObject::close(cx, obj);
1358 : }
1359 :
1360 98 : return true;
1361 : }
1362 :
1363 : bool
1364 0 : js::UnwindIteratorForException(JSContext* cx, HandleObject obj)
1365 : {
1366 0 : RootedValue v(cx);
1367 0 : bool getOk = cx->getPendingException(&v);
1368 0 : cx->clearPendingException();
1369 0 : if (!CloseIterator(cx, obj))
1370 0 : return false;
1371 0 : if (!getOk)
1372 0 : return false;
1373 0 : cx->setPendingException(v);
1374 0 : return true;
1375 : }
1376 :
1377 : bool
1378 0 : js::IteratorCloseForException(JSContext* cx, HandleObject obj)
1379 : {
1380 0 : MOZ_ASSERT(cx->isExceptionPending());
1381 :
1382 0 : bool isClosingGenerator = cx->isClosingGenerator();
1383 0 : JS::AutoSaveExceptionState savedExc(cx);
1384 :
1385 : // Implements IteratorClose (ES 7.4.6) for exception unwinding. See
1386 : // also the bytecode generated by BytecodeEmitter::emitIteratorClose.
1387 :
1388 : // Step 3.
1389 : //
1390 : // Get the "return" method.
1391 0 : RootedValue returnMethod(cx);
1392 0 : if (!GetProperty(cx, obj, obj, cx->names().return_, &returnMethod))
1393 0 : return false;
1394 :
1395 : // Step 4.
1396 : //
1397 : // Do nothing if "return" is null or undefined. Throw a TypeError if the
1398 : // method is not IsCallable.
1399 0 : if (returnMethod.isNullOrUndefined())
1400 0 : return true;
1401 0 : if (!IsCallable(returnMethod))
1402 0 : return ReportIsNotFunction(cx, returnMethod);
1403 :
1404 : // Step 5, 6, 8.
1405 : //
1406 : // Call "return" if it is not null or undefined.
1407 0 : RootedValue rval(cx);
1408 0 : bool ok = Call(cx, returnMethod, obj, &rval);
1409 0 : if (isClosingGenerator) {
1410 : // Closing an iterator is implemented as an exception, but in spec
1411 : // terms it is a Completion value with [[Type]] return. In this case
1412 : // we *do* care if the call threw and if it returned an object.
1413 0 : if (!ok)
1414 0 : return false;
1415 0 : if (!rval.isObject())
1416 0 : return ThrowCheckIsObject(cx, CheckIsObjectKind::IteratorReturn);
1417 : } else {
1418 : // We don't care if the call threw or that it returned an Object, as
1419 : // Step 6 says if IteratorClose is being called during a throw, the
1420 : // original throw has primacy.
1421 0 : savedExc.restore();
1422 : }
1423 :
1424 0 : return true;
1425 : }
1426 :
1427 : void
1428 0 : js::UnwindIteratorForUncatchableException(JSContext* cx, JSObject* obj)
1429 : {
1430 0 : if (obj->is<PropertyIteratorObject>()) {
1431 0 : NativeIterator* ni = obj->as<PropertyIteratorObject>().getNativeIterator();
1432 0 : if (ni->flags & JSITER_ENUMERATE)
1433 0 : ni->unlink();
1434 : }
1435 0 : }
1436 :
1437 : /*
1438 : * Suppress enumeration of deleted properties. This function must be called
1439 : * when a property is deleted and there might be active enumerators.
1440 : *
1441 : * We maintain a list of active non-escaping for-in enumerators. To suppress
1442 : * a property, we check whether each active enumerator contains the (obj, id)
1443 : * pair and has not yet enumerated |id|. If so, and |id| is the next property,
1444 : * we simply advance the cursor. Otherwise, we delete |id| from the list.
1445 : *
1446 : * We do not suppress enumeration of a property deleted along an object's
1447 : * prototype chain. Only direct deletions on the object are handled.
1448 : *
1449 : * This function can suppress multiple properties at once. The |predicate|
1450 : * argument is an object which can be called on an id and returns true or
1451 : * false. It also must have a method |matchesAtMostOne| which allows us to
1452 : * stop searching after the first deletion if true.
1453 : */
1454 : template<typename StringPredicate>
1455 : static bool
1456 2 : SuppressDeletedPropertyHelper(JSContext* cx, HandleObject obj, StringPredicate predicate)
1457 : {
1458 2 : NativeIterator* enumeratorList = cx->compartment()->enumerators;
1459 2 : NativeIterator* ni = enumeratorList->next();
1460 :
1461 6 : while (ni != enumeratorList) {
1462 : again:
1463 : /* This only works for identified suppressed keys, not values. */
1464 2 : if (ni->isKeyIter() && ni->obj == obj && ni->props_cursor < ni->props_end) {
1465 : /* Check whether id is still to come. */
1466 2 : GCPtrFlatString* props_cursor = ni->current();
1467 2 : GCPtrFlatString* props_end = ni->end();
1468 275 : for (GCPtrFlatString* idp = props_cursor; idp < props_end; ++idp) {
1469 273 : if (predicate(*idp)) {
1470 : /*
1471 : * Check whether another property along the prototype chain
1472 : * became visible as a result of this deletion.
1473 : */
1474 0 : RootedObject proto(cx);
1475 0 : if (!GetPrototype(cx, obj, &proto))
1476 0 : return false;
1477 0 : if (proto) {
1478 0 : RootedId id(cx);
1479 0 : RootedValue idv(cx, StringValue(*idp));
1480 0 : if (!ValueToId<CanGC>(cx, idv, &id))
1481 0 : return false;
1482 :
1483 0 : Rooted<PropertyDescriptor> desc(cx);
1484 0 : if (!GetPropertyDescriptor(cx, proto, id, &desc))
1485 0 : return false;
1486 :
1487 0 : if (desc.object()) {
1488 0 : if (desc.enumerable())
1489 0 : continue;
1490 : }
1491 : }
1492 :
1493 : /*
1494 : * If GetPropertyDescriptorById above removed a property from
1495 : * ni, start over.
1496 : */
1497 0 : if (props_end != ni->props_end || props_cursor != ni->props_cursor)
1498 : goto again;
1499 :
1500 : /*
1501 : * No property along the prototype chain stepped in to take the
1502 : * property's place, so go ahead and delete id from the list.
1503 : * If it is the next property to be enumerated, just skip it.
1504 : */
1505 0 : if (idp == props_cursor) {
1506 0 : ni->incCursor();
1507 : } else {
1508 0 : for (GCPtrFlatString* p = idp; p + 1 != props_end; p++)
1509 0 : *p = *(p + 1);
1510 0 : ni->props_end = ni->end() - 1;
1511 :
1512 : /*
1513 : * This invokes the pre barrier on this element, since
1514 : * it's no longer going to be marked, and ensures that
1515 : * any existing remembered set entry will be dropped.
1516 : */
1517 0 : *ni->props_end = nullptr;
1518 : }
1519 :
1520 : /* Don't reuse modified native iterators. */
1521 0 : ni->flags |= JSITER_UNREUSABLE;
1522 :
1523 0 : if (predicate.matchesAtMostOne())
1524 0 : break;
1525 : }
1526 : }
1527 : }
1528 2 : ni = ni->next();
1529 : }
1530 2 : return true;
1531 : }
1532 :
1533 : namespace {
1534 :
1535 : class SingleStringPredicate {
1536 : Handle<JSFlatString*> str;
1537 : public:
1538 2 : explicit SingleStringPredicate(Handle<JSFlatString*> str) : str(str) {}
1539 :
1540 273 : bool operator()(JSFlatString* str) { return EqualStrings(str, this->str); }
1541 0 : bool matchesAtMostOne() { return true; }
1542 : };
1543 :
1544 : } /* anonymous namespace */
1545 :
1546 : bool
1547 834 : js::SuppressDeletedProperty(JSContext* cx, HandleObject obj, jsid id)
1548 : {
1549 834 : if (MOZ_LIKELY(!cx->compartment()->objectMaybeInIteration(obj)))
1550 832 : return true;
1551 :
1552 2 : if (JSID_IS_SYMBOL(id))
1553 0 : return true;
1554 :
1555 4 : Rooted<JSFlatString*> str(cx, IdToString(cx, id));
1556 2 : if (!str)
1557 0 : return false;
1558 2 : return SuppressDeletedPropertyHelper(cx, obj, SingleStringPredicate(str));
1559 : }
1560 :
1561 : bool
1562 1 : js::SuppressDeletedElement(JSContext* cx, HandleObject obj, uint32_t index)
1563 : {
1564 1 : if (MOZ_LIKELY(!cx->compartment()->objectMaybeInIteration(obj)))
1565 1 : return true;
1566 :
1567 0 : RootedId id(cx);
1568 0 : if (!IndexToId(cx, index, &id))
1569 0 : return false;
1570 :
1571 0 : Rooted<JSFlatString*> str(cx, IdToString(cx, id));
1572 0 : if (!str)
1573 0 : return false;
1574 0 : return SuppressDeletedPropertyHelper(cx, obj, SingleStringPredicate(str));
1575 : }
1576 :
1577 : bool
1578 185 : js::IteratorMore(JSContext* cx, HandleObject iterobj, MutableHandleValue rval)
1579 : {
1580 : // Fast path for native iterators.
1581 185 : if (iterobj->is<PropertyIteratorObject>()) {
1582 165 : NativeIterator* ni = iterobj->as<PropertyIteratorObject>().getNativeIterator();
1583 : bool done;
1584 165 : if (!NativeIteratorNext(cx, ni, rval, &done))
1585 0 : return false;
1586 :
1587 165 : if (done)
1588 22 : rval.setMagic(JS_NO_ITER_VALUE);
1589 165 : return true;
1590 : }
1591 :
1592 : // We're reentering below and can call anything.
1593 20 : if (!CheckRecursionLimit(cx))
1594 0 : return false;
1595 :
1596 : // Call the iterator object's .next method.
1597 20 : if (!GetProperty(cx, iterobj, iterobj, cx->names().next, rval))
1598 0 : return false;
1599 :
1600 : // Call the .next method. Fall through to the error-handling cases in the
1601 : // unlikely event that either one of the fallible operations performed
1602 : // during the call process fails.
1603 40 : FixedInvokeArgs<0> args(cx);
1604 40 : RootedValue iterval(cx, ObjectValue(*iterobj));
1605 20 : if (!js::Call(cx, rval, iterval, args, rval)) {
1606 : // Check for StopIteration.
1607 5 : if (!cx->isExceptionPending())
1608 0 : return false;
1609 10 : RootedValue exception(cx);
1610 5 : if (!cx->getPendingException(&exception))
1611 0 : return false;
1612 5 : if (!JS_IsStopIteration(exception))
1613 0 : return false;
1614 :
1615 5 : cx->clearPendingException();
1616 5 : rval.setMagic(JS_NO_ITER_VALUE);
1617 : }
1618 :
1619 20 : return true;
1620 : }
1621 :
1622 : static bool
1623 0 : stopiter_hasInstance(JSContext* cx, HandleObject obj, MutableHandleValue v, bool* bp)
1624 : {
1625 0 : *bp = JS_IsStopIteration(v);
1626 0 : return true;
1627 : }
1628 :
1629 : static const ClassOps StopIterationObjectClassOps = {
1630 : nullptr, /* addProperty */
1631 : nullptr, /* delProperty */
1632 : nullptr, /* getProperty */
1633 : nullptr, /* setProperty */
1634 : nullptr, /* enumerate */
1635 : nullptr, /* enumerate */
1636 : nullptr, /* resolve */
1637 : nullptr, /* mayResolve */
1638 : nullptr, /* finalize */
1639 : nullptr, /* call */
1640 : stopiter_hasInstance
1641 : };
1642 :
1643 : const Class StopIterationObject::class_ = {
1644 : "StopIteration",
1645 : JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration),
1646 : &StopIterationObjectClassOps
1647 : };
1648 :
1649 : static const JSFunctionSpec iterator_proto_methods[] = {
1650 : JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0),
1651 : JS_FS_END
1652 : };
1653 :
1654 : /* static */ bool
1655 144 : GlobalObject::initIteratorProto(JSContext* cx, Handle<GlobalObject*> global)
1656 : {
1657 144 : if (global->getReservedSlot(ITERATOR_PROTO).isObject())
1658 0 : return true;
1659 :
1660 288 : RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
1661 144 : if (!proto || !DefinePropertiesAndFunctions(cx, proto, nullptr, iterator_proto_methods))
1662 0 : return false;
1663 :
1664 144 : global->setReservedSlot(ITERATOR_PROTO, ObjectValue(*proto));
1665 144 : return true;
1666 : }
1667 :
1668 : /* static */ bool
1669 74 : GlobalObject::initArrayIteratorProto(JSContext* cx, Handle<GlobalObject*> global)
1670 : {
1671 74 : if (global->getReservedSlot(ARRAY_ITERATOR_PROTO).isObject())
1672 0 : return true;
1673 :
1674 148 : RootedObject iteratorProto(cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
1675 74 : if (!iteratorProto)
1676 0 : return false;
1677 :
1678 74 : const Class* cls = &ArrayIteratorPrototypeClass;
1679 148 : RootedObject proto(cx, GlobalObject::createBlankPrototypeInheriting(cx, global, cls,
1680 148 : iteratorProto));
1681 370 : if (!proto ||
1682 518 : !DefinePropertiesAndFunctions(cx, proto, nullptr, array_iterator_methods) ||
1683 222 : !DefineToStringTag(cx, proto, cx->names().ArrayIterator))
1684 : {
1685 0 : return false;
1686 : }
1687 :
1688 74 : global->setReservedSlot(ARRAY_ITERATOR_PROTO, ObjectValue(*proto));
1689 74 : return true;
1690 : }
1691 :
1692 : /* static */ bool
1693 0 : GlobalObject::initStringIteratorProto(JSContext* cx, Handle<GlobalObject*> global)
1694 : {
1695 0 : if (global->getReservedSlot(STRING_ITERATOR_PROTO).isObject())
1696 0 : return true;
1697 :
1698 0 : RootedObject iteratorProto(cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
1699 0 : if (!iteratorProto)
1700 0 : return false;
1701 :
1702 0 : const Class* cls = &StringIteratorPrototypeClass;
1703 0 : RootedObject proto(cx, GlobalObject::createBlankPrototypeInheriting(cx, global, cls,
1704 0 : iteratorProto));
1705 0 : if (!proto ||
1706 0 : !DefinePropertiesAndFunctions(cx, proto, nullptr, string_iterator_methods) ||
1707 0 : !DefineToStringTag(cx, proto, cx->names().StringIterator))
1708 : {
1709 0 : return false;
1710 : }
1711 :
1712 0 : global->setReservedSlot(STRING_ITERATOR_PROTO, ObjectValue(*proto));
1713 0 : return true;
1714 : }
1715 :
1716 : JSObject*
1717 28 : js::InitLegacyIteratorClass(JSContext* cx, HandleObject obj)
1718 : {
1719 28 : Handle<GlobalObject*> global = obj.as<GlobalObject>();
1720 :
1721 28 : if (global->getPrototype(JSProto_Iterator).isObject())
1722 0 : return &global->getPrototype(JSProto_Iterator).toObject();
1723 :
1724 56 : RootedObject iteratorProto(cx);
1725 56 : iteratorProto = GlobalObject::createBlankPrototype(cx, global,
1726 28 : &PropertyIteratorObject::class_);
1727 28 : if (!iteratorProto)
1728 0 : return nullptr;
1729 :
1730 28 : NativeIterator* ni = NativeIterator::allocateIterator(cx, 0, 0);
1731 28 : if (!ni)
1732 0 : return nullptr;
1733 :
1734 28 : iteratorProto->as<PropertyIteratorObject>().setNativeIterator(ni);
1735 28 : ni->init(nullptr, nullptr, 0 /* flags */, 0, 0);
1736 :
1737 56 : Rooted<JSFunction*> ctor(cx);
1738 28 : ctor = GlobalObject::createConstructor(cx, IteratorConstructor, cx->names().Iterator, 2);
1739 28 : if (!ctor)
1740 0 : return nullptr;
1741 28 : if (!LinkConstructorAndPrototype(cx, ctor, iteratorProto))
1742 0 : return nullptr;
1743 28 : if (!DefinePropertiesAndFunctions(cx, iteratorProto, nullptr, legacy_iterator_methods))
1744 0 : return nullptr;
1745 28 : if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_Iterator,
1746 : ctor, iteratorProto))
1747 : {
1748 0 : return nullptr;
1749 : }
1750 :
1751 28 : return &global->getPrototype(JSProto_Iterator).toObject();
1752 : }
1753 :
1754 : JSObject*
1755 16 : js::InitStopIterationClass(JSContext* cx, HandleObject obj)
1756 : {
1757 16 : Handle<GlobalObject*> global = obj.as<GlobalObject>();
1758 16 : if (!global->getPrototype(JSProto_StopIteration).isObject()) {
1759 32 : RootedObject proto(cx, GlobalObject::createBlankPrototype(cx, global,
1760 32 : &StopIterationObject::class_));
1761 16 : if (!proto || !FreezeObject(cx, proto))
1762 0 : return nullptr;
1763 :
1764 : // This should use a non-JSProtoKey'd slot, but this is easier for now.
1765 16 : if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_StopIteration, proto, proto))
1766 0 : return nullptr;
1767 :
1768 16 : global->setConstructor(JSProto_StopIteration, ObjectValue(*proto));
1769 : }
1770 :
1771 16 : return &global->getPrototype(JSProto_StopIteration).toObject();
1772 : }
|