Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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/. */
7 /* JavaScript iterators. */
9 #include "jsiter.h"
11 #include "mozilla/ArrayUtils.h"
12 #include "mozilla/MemoryReporting.h"
13 #include "mozilla/PodOperations.h"
15 #include "jsarray.h"
16 #include "jsatom.h"
17 #include "jscntxt.h"
18 #include "jsgc.h"
19 #include "jsobj.h"
20 #include "jsopcode.h"
21 #include "jsproxy.h"
22 #include "jsscript.h"
23 #include "jstypes.h"
24 #include "jsutil.h"
26 #include "ds/Sort.h"
27 #include "gc/Marking.h"
28 #include "vm/GeneratorObject.h"
29 #include "vm/GlobalObject.h"
30 #include "vm/Interpreter.h"
31 #include "vm/Shape.h"
32 #include "vm/StopIterationObject.h"
34 #include "jsinferinlines.h"
35 #include "jsobjinlines.h"
36 #include "jsscriptinlines.h"
38 #include "vm/Stack-inl.h"
39 #include "vm/String-inl.h"
41 using namespace js;
42 using namespace js::gc;
43 using JS::ForOfIterator;
45 using mozilla::ArrayLength;
46 #ifdef JS_MORE_DETERMINISTIC
47 using mozilla::PodCopy;
48 #endif
49 using mozilla::PodZero;
51 typedef Rooted<PropertyIteratorObject*> RootedPropertyIteratorObject;
53 static const gc::AllocKind ITERATOR_FINALIZE_KIND = gc::FINALIZE_OBJECT2_BACKGROUND;
55 void
56 NativeIterator::mark(JSTracer *trc)
57 {
58 for (HeapPtr<JSFlatString> *str = begin(); str < end(); str++)
59 MarkString(trc, str, "prop");
60 if (obj)
61 MarkObject(trc, &obj, "obj");
63 // The SuppressDeletedPropertyHelper loop can GC, so make sure that if the
64 // GC removes any elements from the list, it won't remove this one.
65 if (iterObj_)
66 MarkObjectUnbarriered(trc, &iterObj_, "iterObj");
67 }
69 struct IdHashPolicy {
70 typedef jsid Lookup;
71 static HashNumber hash(jsid id) {
72 return JSID_BITS(id);
73 }
74 static bool match(jsid id1, jsid id2) {
75 return id1 == id2;
76 }
77 };
79 typedef HashSet<jsid, IdHashPolicy> IdSet;
81 static inline bool
82 NewKeyValuePair(JSContext *cx, jsid id, const Value &val, MutableHandleValue rval)
83 {
84 JS::AutoValueArray<2> vec(cx);
85 vec[0].set(IdToValue(id));
86 vec[1].set(val);
88 JSObject *aobj = NewDenseCopiedArray(cx, 2, vec.begin());
89 if (!aobj)
90 return false;
91 rval.setObject(*aobj);
92 return true;
93 }
95 static inline bool
96 Enumerate(JSContext *cx, HandleObject pobj, jsid id,
97 bool enumerable, unsigned flags, IdSet& ht, AutoIdVector *props)
98 {
99 /*
100 * We implement __proto__ using a property on |Object.prototype|, but
101 * because __proto__ is highly deserving of removal, we don't want it to
102 * show up in property enumeration, even if only for |Object.prototype|
103 * (think introspection by Prototype-like frameworks that add methods to
104 * the built-in prototypes). So exclude __proto__ if the object where the
105 * property was found has no [[Prototype]] and might be |Object.prototype|.
106 */
107 if (MOZ_UNLIKELY(!pobj->getTaggedProto().isObject() && JSID_IS_ATOM(id, cx->names().proto)))
108 return true;
110 if (!(flags & JSITER_OWNONLY) || pobj->is<ProxyObject>() || pobj->getOps()->enumerate) {
111 /* If we've already seen this, we definitely won't add it. */
112 IdSet::AddPtr p = ht.lookupForAdd(id);
113 if (MOZ_UNLIKELY(!!p))
114 return true;
116 /*
117 * It's not necessary to add properties to the hash table at the end of
118 * the prototype chain, but custom enumeration behaviors might return
119 * duplicated properties, so always add in such cases.
120 */
121 if ((pobj->is<ProxyObject>() || pobj->getProto() || pobj->getOps()->enumerate) && !ht.add(p, id))
122 return false;
123 }
125 if (enumerable || (flags & JSITER_HIDDEN))
126 return props->append(id);
128 return true;
129 }
131 static bool
132 EnumerateNativeProperties(JSContext *cx, HandleObject pobj, unsigned flags, IdSet &ht,
133 AutoIdVector *props)
134 {
135 /* Collect any dense elements from this object. */
136 size_t initlen = pobj->getDenseInitializedLength();
137 const Value *vp = pobj->getDenseElements();
138 for (size_t i = 0; i < initlen; ++i, ++vp) {
139 if (!vp->isMagic(JS_ELEMENTS_HOLE)) {
140 /* Dense arrays never get so large that i would not fit into an integer id. */
141 if (!Enumerate(cx, pobj, INT_TO_JSID(i), /* enumerable = */ true, flags, ht, props))
142 return false;
143 }
144 }
146 /* Collect any typed array elements from this object. */
147 if (pobj->is<TypedArrayObject>()) {
148 size_t len = pobj->as<TypedArrayObject>().length();
149 for (size_t i = 0; i < len; i++) {
150 if (!Enumerate(cx, pobj, INT_TO_JSID(i), /* enumerable = */ true, flags, ht, props))
151 return false;
152 }
153 }
155 size_t initialLength = props->length();
157 /* Collect all unique properties from this object's scope. */
158 Shape::Range<NoGC> r(pobj->lastProperty());
159 for (; !r.empty(); r.popFront()) {
160 Shape &shape = r.front();
162 if (!Enumerate(cx, pobj, shape.propid(), shape.enumerable(), flags, ht, props))
163 return false;
164 }
166 ::Reverse(props->begin() + initialLength, props->end());
167 return true;
168 }
170 #ifdef JS_MORE_DETERMINISTIC
172 struct SortComparatorIds
173 {
174 JSContext *const cx;
176 SortComparatorIds(JSContext *cx)
177 : cx(cx) {}
179 bool operator()(jsid a, jsid b, bool *lessOrEqualp)
180 {
181 /* Pick an arbitrary total order on jsids that is stable across executions. */
182 RootedString astr(cx, IdToString(cx, a));
183 if (!astr)
184 return false;
185 RootedString bstr(cx, IdToString(cx, b));
186 if (!bstr)
187 return false;
189 int32_t result;
190 if (!CompareStrings(cx, astr, bstr, &result))
191 return false;
193 *lessOrEqualp = (result <= 0);
194 return true;
195 }
196 };
198 #endif /* JS_MORE_DETERMINISTIC */
200 static bool
201 Snapshot(JSContext *cx, JSObject *pobj_, unsigned flags, AutoIdVector *props)
202 {
203 IdSet ht(cx);
204 if (!ht.init(32))
205 return false;
207 RootedObject pobj(cx, pobj_);
209 do {
210 const Class *clasp = pobj->getClass();
211 if (pobj->isNative() &&
212 !pobj->getOps()->enumerate &&
213 !(clasp->flags & JSCLASS_NEW_ENUMERATE)) {
214 if (!clasp->enumerate(cx, pobj))
215 return false;
216 if (!EnumerateNativeProperties(cx, pobj, flags, ht, props))
217 return false;
218 } else {
219 if (pobj->is<ProxyObject>()) {
220 AutoIdVector proxyProps(cx);
221 if (flags & JSITER_OWNONLY) {
222 if (flags & JSITER_HIDDEN) {
223 if (!Proxy::getOwnPropertyNames(cx, pobj, proxyProps))
224 return false;
225 } else {
226 if (!Proxy::keys(cx, pobj, proxyProps))
227 return false;
228 }
229 } else {
230 if (!Proxy::enumerate(cx, pobj, proxyProps))
231 return false;
232 }
233 for (size_t n = 0, len = proxyProps.length(); n < len; n++) {
234 if (!Enumerate(cx, pobj, proxyProps[n], true, flags, ht, props))
235 return false;
236 }
237 /* Proxy objects enumerate the prototype on their own, so we are done here. */
238 break;
239 }
240 RootedValue state(cx);
241 RootedId id(cx);
242 JSIterateOp op = (flags & JSITER_HIDDEN) ? JSENUMERATE_INIT_ALL : JSENUMERATE_INIT;
243 if (!JSObject::enumerate(cx, pobj, op, &state, &id))
244 return false;
245 if (state.isMagic(JS_NATIVE_ENUMERATE)) {
246 if (!EnumerateNativeProperties(cx, pobj, flags, ht, props))
247 return false;
248 } else {
249 while (true) {
250 RootedId id(cx);
251 if (!JSObject::enumerate(cx, pobj, JSENUMERATE_NEXT, &state, &id))
252 return false;
253 if (state.isNull())
254 break;
255 if (!Enumerate(cx, pobj, id, true, flags, ht, props))
256 return false;
257 }
258 }
259 }
261 if (flags & JSITER_OWNONLY)
262 break;
264 } while ((pobj = pobj->getProto()) != nullptr);
266 #ifdef JS_MORE_DETERMINISTIC
268 /*
269 * In some cases the enumeration order for an object depends on the
270 * execution mode (interpreter vs. JIT), especially for native objects
271 * with a class enumerate hook (where resolving a property changes the
272 * resulting enumeration order). These aren't really bugs, but the
273 * differences can change the generated output and confuse correctness
274 * fuzzers, so we sort the ids if such a fuzzer is running.
275 *
276 * We don't do this in the general case because (a) doing so is slow,
277 * and (b) it also breaks the web, which expects enumeration order to
278 * follow the order in which properties are added, in certain cases.
279 * Since ECMA does not specify an enumeration order for objects, both
280 * behaviors are technically correct to do.
281 */
283 jsid *ids = props->begin();
284 size_t n = props->length();
286 AutoIdVector tmp(cx);
287 if (!tmp.resize(n))
288 return false;
289 PodCopy(tmp.begin(), ids, n);
291 if (!MergeSort(ids, n, tmp.begin(), SortComparatorIds(cx)))
292 return false;
294 #endif /* JS_MORE_DETERMINISTIC */
296 return true;
297 }
299 bool
300 js::VectorToIdArray(JSContext *cx, AutoIdVector &props, JSIdArray **idap)
301 {
302 JS_STATIC_ASSERT(sizeof(JSIdArray) > sizeof(jsid));
303 size_t len = props.length();
304 size_t idsz = len * sizeof(jsid);
305 size_t sz = (sizeof(JSIdArray) - sizeof(jsid)) + idsz;
306 JSIdArray *ida = static_cast<JSIdArray *>(cx->malloc_(sz));
307 if (!ida)
308 return false;
310 ida->length = static_cast<int>(len);
311 jsid *v = props.begin();
312 for (int i = 0; i < ida->length; i++)
313 ida->vector[i].init(v[i]);
314 *idap = ida;
315 return true;
316 }
318 JS_FRIEND_API(bool)
319 js::GetPropertyNames(JSContext *cx, JSObject *obj, unsigned flags, AutoIdVector *props)
320 {
321 return Snapshot(cx, obj, flags & (JSITER_OWNONLY | JSITER_HIDDEN), props);
322 }
324 size_t sCustomIteratorCount = 0;
326 static inline bool
327 GetCustomIterator(JSContext *cx, HandleObject obj, unsigned flags, MutableHandleValue vp)
328 {
329 JS_CHECK_RECURSION(cx, return false);
331 /* Check whether we have a valid __iterator__ method. */
332 HandlePropertyName name = cx->names().iteratorIntrinsic;
333 if (!JSObject::getProperty(cx, obj, obj, name, vp))
334 return false;
336 /* If there is no custom __iterator__ method, we are done here. */
337 if (!vp.isObject()) {
338 vp.setUndefined();
339 return true;
340 }
342 if (!cx->runningWithTrustedPrincipals())
343 ++sCustomIteratorCount;
345 /* Otherwise call it and return that object. */
346 Value arg = BooleanValue((flags & JSITER_FOREACH) == 0);
347 if (!Invoke(cx, ObjectValue(*obj), vp, 1, &arg, vp))
348 return false;
349 if (vp.isPrimitive()) {
350 /*
351 * We are always coming from js::ValueToIterator, and we are no longer on
352 * trace, so the object we are iterating over is on top of the stack (-1).
353 */
354 JSAutoByteString bytes;
355 if (!AtomToPrintableString(cx, name, &bytes))
356 return false;
357 RootedValue val(cx, ObjectValue(*obj));
358 js_ReportValueError2(cx, JSMSG_BAD_TRAP_RETURN_VALUE,
359 -1, val, js::NullPtr(), bytes.ptr());
360 return false;
361 }
362 return true;
363 }
365 template <typename T>
366 static inline bool
367 Compare(T *a, T *b, size_t c)
368 {
369 size_t n = (c + size_t(7)) / size_t(8);
370 switch (c % 8) {
371 case 0: do { if (*a++ != *b++) return false;
372 case 7: if (*a++ != *b++) return false;
373 case 6: if (*a++ != *b++) return false;
374 case 5: if (*a++ != *b++) return false;
375 case 4: if (*a++ != *b++) return false;
376 case 3: if (*a++ != *b++) return false;
377 case 2: if (*a++ != *b++) return false;
378 case 1: if (*a++ != *b++) return false;
379 } while (--n > 0);
380 }
381 return true;
382 }
384 static inline PropertyIteratorObject *
385 NewPropertyIteratorObject(JSContext *cx, unsigned flags)
386 {
387 if (flags & JSITER_ENUMERATE) {
388 RootedTypeObject type(cx, cx->getNewType(&PropertyIteratorObject::class_, nullptr));
389 if (!type)
390 return nullptr;
392 JSObject *metadata = nullptr;
393 if (!NewObjectMetadata(cx, &metadata))
394 return nullptr;
396 const Class *clasp = &PropertyIteratorObject::class_;
397 RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, nullptr, nullptr, metadata,
398 ITERATOR_FINALIZE_KIND));
399 if (!shape)
400 return nullptr;
402 JSObject *obj = JSObject::create(cx, ITERATOR_FINALIZE_KIND,
403 GetInitialHeap(GenericObject, clasp), shape, type);
404 if (!obj)
405 return nullptr;
407 JS_ASSERT(obj->numFixedSlots() == JSObject::ITER_CLASS_NFIXED_SLOTS);
408 return &obj->as<PropertyIteratorObject>();
409 }
411 JSObject *obj = NewBuiltinClassInstance(cx, &PropertyIteratorObject::class_);
412 if (!obj)
413 return nullptr;
415 return &obj->as<PropertyIteratorObject>();
416 }
418 NativeIterator *
419 NativeIterator::allocateIterator(JSContext *cx, uint32_t slength, const AutoIdVector &props)
420 {
421 size_t plength = props.length();
422 NativeIterator *ni = (NativeIterator *)
423 cx->malloc_(sizeof(NativeIterator)
424 + plength * sizeof(JSString *)
425 + slength * sizeof(Shape *));
426 if (!ni)
427 return nullptr;
428 AutoValueVector strings(cx);
429 ni->props_array = ni->props_cursor = (HeapPtr<JSFlatString> *) (ni + 1);
430 ni->props_end = ni->props_array + plength;
431 if (plength) {
432 for (size_t i = 0; i < plength; i++) {
433 JSFlatString *str = IdToString(cx, props[i]);
434 if (!str || !strings.append(StringValue(str)))
435 return nullptr;
436 ni->props_array[i].init(str);
437 }
438 }
439 ni->next_ = nullptr;
440 ni->prev_ = nullptr;
441 return ni;
442 }
444 NativeIterator *
445 NativeIterator::allocateSentinel(JSContext *cx)
446 {
447 NativeIterator *ni = (NativeIterator *)js_malloc(sizeof(NativeIterator));
448 if (!ni)
449 return nullptr;
451 PodZero(ni);
453 ni->next_ = ni;
454 ni->prev_ = ni;
455 return ni;
456 }
458 inline void
459 NativeIterator::init(JSObject *obj, JSObject *iterObj, unsigned flags, uint32_t slength, uint32_t key)
460 {
461 this->obj.init(obj);
462 this->iterObj_ = iterObj;
463 this->flags = flags;
464 this->shapes_array = (Shape **) this->props_end;
465 this->shapes_length = slength;
466 this->shapes_key = key;
467 }
469 static inline void
470 RegisterEnumerator(JSContext *cx, PropertyIteratorObject *iterobj, NativeIterator *ni)
471 {
472 /* Register non-escaping native enumerators (for-in) with the current context. */
473 if (ni->flags & JSITER_ENUMERATE) {
474 ni->link(cx->compartment()->enumerators);
476 JS_ASSERT(!(ni->flags & JSITER_ACTIVE));
477 ni->flags |= JSITER_ACTIVE;
478 }
479 }
481 static inline bool
482 VectorToKeyIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector &keys,
483 uint32_t slength, uint32_t key, MutableHandleValue vp)
484 {
485 JS_ASSERT(!(flags & JSITER_FOREACH));
487 if (obj) {
488 if (obj->hasSingletonType() && !obj->setIteratedSingleton(cx))
489 return false;
490 types::MarkTypeObjectFlags(cx, obj, types::OBJECT_FLAG_ITERATED);
491 }
493 Rooted<PropertyIteratorObject *> iterobj(cx, NewPropertyIteratorObject(cx, flags));
494 if (!iterobj)
495 return false;
497 NativeIterator *ni = NativeIterator::allocateIterator(cx, slength, keys);
498 if (!ni)
499 return false;
500 ni->init(obj, iterobj, flags, slength, key);
502 if (slength) {
503 /*
504 * Fill in the shape array from scratch. We can't use the array that was
505 * computed for the cache lookup earlier, as constructing iterobj could
506 * have triggered a shape-regenerating GC. Don't bother with regenerating
507 * the shape key; if such a GC *does* occur, we can only get hits through
508 * the one-slot lastNativeIterator cache.
509 */
510 JSObject *pobj = obj;
511 size_t ind = 0;
512 do {
513 ni->shapes_array[ind++] = pobj->lastProperty();
514 pobj = pobj->getProto();
515 } while (pobj);
516 JS_ASSERT(ind == slength);
517 }
519 iterobj->setNativeIterator(ni);
520 vp.setObject(*iterobj);
522 RegisterEnumerator(cx, iterobj, ni);
523 return true;
524 }
526 bool
527 js::VectorToKeyIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector &props,
528 MutableHandleValue vp)
529 {
530 return VectorToKeyIterator(cx, obj, flags, props, 0, 0, vp);
531 }
533 bool
534 js::VectorToValueIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector &keys,
535 MutableHandleValue vp)
536 {
537 JS_ASSERT(flags & JSITER_FOREACH);
539 if (obj) {
540 if (obj->hasSingletonType() && !obj->setIteratedSingleton(cx))
541 return false;
542 types::MarkTypeObjectFlags(cx, obj, types::OBJECT_FLAG_ITERATED);
543 }
545 Rooted<PropertyIteratorObject*> iterobj(cx, NewPropertyIteratorObject(cx, flags));
546 if (!iterobj)
547 return false;
549 NativeIterator *ni = NativeIterator::allocateIterator(cx, 0, keys);
550 if (!ni)
551 return false;
552 ni->init(obj, iterobj, flags, 0, 0);
554 iterobj->setNativeIterator(ni);
555 vp.setObject(*iterobj);
557 RegisterEnumerator(cx, iterobj, ni);
558 return true;
559 }
561 bool
562 js::EnumeratedIdVectorToIterator(JSContext *cx, HandleObject obj, unsigned flags,
563 AutoIdVector &props, MutableHandleValue vp)
564 {
565 if (!(flags & JSITER_FOREACH))
566 return VectorToKeyIterator(cx, obj, flags, props, vp);
568 return VectorToValueIterator(cx, obj, flags, props, vp);
569 }
571 static inline void
572 UpdateNativeIterator(NativeIterator *ni, JSObject *obj)
573 {
574 // Update the object for which the native iterator is associated, so
575 // SuppressDeletedPropertyHelper will recognize the iterator as a match.
576 ni->obj = obj;
577 }
579 bool
580 js::GetIterator(JSContext *cx, HandleObject obj, unsigned flags, MutableHandleValue vp)
581 {
582 Vector<Shape *, 8> shapes(cx);
583 uint32_t key = 0;
585 bool keysOnly = (flags == JSITER_ENUMERATE);
587 if (obj) {
588 if (JSIteratorOp op = obj->getClass()->ext.iteratorObject) {
589 JSObject *iterobj = op(cx, obj, !(flags & JSITER_FOREACH));
590 if (!iterobj)
591 return false;
592 vp.setObject(*iterobj);
593 return true;
594 }
596 if (keysOnly) {
597 /*
598 * Check to see if this is the same as the most recent object which
599 * was iterated over. We don't explicitly check for shapeless
600 * objects here, as they are not inserted into the cache and
601 * will result in a miss.
602 */
603 PropertyIteratorObject *last = cx->runtime()->nativeIterCache.last;
604 if (last) {
605 NativeIterator *lastni = last->getNativeIterator();
606 if (!(lastni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) &&
607 obj->isNative() &&
608 obj->hasEmptyElements() &&
609 obj->lastProperty() == lastni->shapes_array[0])
610 {
611 JSObject *proto = obj->getProto();
612 if (proto->isNative() &&
613 proto->hasEmptyElements() &&
614 proto->lastProperty() == lastni->shapes_array[1] &&
615 !proto->getProto())
616 {
617 vp.setObject(*last);
618 UpdateNativeIterator(lastni, obj);
619 RegisterEnumerator(cx, last, lastni);
620 return true;
621 }
622 }
623 }
625 /*
626 * The iterator object for JSITER_ENUMERATE never escapes, so we
627 * don't care for the proper parent/proto to be set. This also
628 * allows us to re-use a previous iterator object that is not
629 * currently active.
630 */
631 {
632 JSObject *pobj = obj;
633 do {
634 if (!pobj->isNative() ||
635 !pobj->hasEmptyElements() ||
636 pobj->is<TypedArrayObject>() ||
637 pobj->hasUncacheableProto() ||
638 pobj->getOps()->enumerate ||
639 pobj->getClass()->enumerate != JS_EnumerateStub ||
640 pobj->nativeContainsPure(cx->names().iteratorIntrinsic))
641 {
642 shapes.clear();
643 goto miss;
644 }
645 Shape *shape = pobj->lastProperty();
646 key = (key + (key << 16)) ^ (uintptr_t(shape) >> 3);
647 if (!shapes.append(shape))
648 return false;
649 pobj = pobj->getProto();
650 } while (pobj);
651 }
653 PropertyIteratorObject *iterobj = cx->runtime()->nativeIterCache.get(key);
654 if (iterobj) {
655 NativeIterator *ni = iterobj->getNativeIterator();
656 if (!(ni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) &&
657 ni->shapes_key == key &&
658 ni->shapes_length == shapes.length() &&
659 Compare(ni->shapes_array, shapes.begin(), ni->shapes_length)) {
660 vp.setObject(*iterobj);
662 UpdateNativeIterator(ni, obj);
663 RegisterEnumerator(cx, iterobj, ni);
664 if (shapes.length() == 2)
665 cx->runtime()->nativeIterCache.last = iterobj;
666 return true;
667 }
668 }
669 }
671 miss:
672 if (obj->is<ProxyObject>())
673 return Proxy::iterate(cx, obj, flags, vp);
675 if (!GetCustomIterator(cx, obj, flags, vp))
676 return false;
677 if (!vp.isUndefined())
678 return true;
679 }
681 /* NB: for (var p in null) succeeds by iterating over no properties. */
683 AutoIdVector keys(cx);
684 if (flags & JSITER_FOREACH) {
685 if (MOZ_LIKELY(obj != nullptr) && !Snapshot(cx, obj, flags, &keys))
686 return false;
687 JS_ASSERT(shapes.empty());
688 if (!VectorToValueIterator(cx, obj, flags, keys, vp))
689 return false;
690 } else {
691 if (MOZ_LIKELY(obj != nullptr) && !Snapshot(cx, obj, flags, &keys))
692 return false;
693 if (!VectorToKeyIterator(cx, obj, flags, keys, shapes.length(), key, vp))
694 return false;
695 }
697 PropertyIteratorObject *iterobj = &vp.toObject().as<PropertyIteratorObject>();
699 /* Cache the iterator object if possible. */
700 if (shapes.length())
701 cx->runtime()->nativeIterCache.set(key, iterobj);
703 if (shapes.length() == 2)
704 cx->runtime()->nativeIterCache.last = iterobj;
705 return true;
706 }
708 JSObject *
709 js::GetIteratorObject(JSContext *cx, HandleObject obj, uint32_t flags)
710 {
711 RootedValue value(cx);
712 if (!GetIterator(cx, obj, flags, &value))
713 return nullptr;
714 return &value.toObject();
715 }
717 JSObject *
718 js::CreateItrResultObject(JSContext *cx, HandleValue value, bool done)
719 {
720 // FIXME: We can cache the iterator result object shape somewhere.
721 AssertHeapIsIdle(cx);
723 RootedObject proto(cx, cx->global()->getOrCreateObjectPrototype(cx));
724 if (!proto)
725 return nullptr;
727 RootedObject obj(cx, NewObjectWithGivenProto(cx, &JSObject::class_, proto, cx->global()));
728 if (!obj)
729 return nullptr;
731 if (!JSObject::defineProperty(cx, obj, cx->names().value, value))
732 return nullptr;
734 RootedValue doneBool(cx, BooleanValue(done));
735 if (!JSObject::defineProperty(cx, obj, cx->names().done, doneBool))
736 return nullptr;
738 return obj;
739 }
741 bool
742 js_ThrowStopIteration(JSContext *cx)
743 {
744 JS_ASSERT(!JS_IsExceptionPending(cx));
746 // StopIteration isn't a constructor, but it's stored in GlobalObject
747 // as one, out of laziness. Hence the GetBuiltinConstructor call here.
748 RootedObject ctor(cx);
749 if (GetBuiltinConstructor(cx, JSProto_StopIteration, &ctor))
750 cx->setPendingException(ObjectValue(*ctor));
751 return false;
752 }
754 /*** Iterator objects ****************************************************************************/
756 bool
757 js::IteratorConstructor(JSContext *cx, unsigned argc, Value *vp)
758 {
759 CallArgs args = CallArgsFromVp(argc, vp);
760 if (args.length() == 0) {
761 js_ReportMissingArg(cx, args.calleev(), 0);
762 return false;
763 }
765 bool keyonly = false;
766 if (args.length() >= 2)
767 keyonly = ToBoolean(args[1]);
768 unsigned flags = JSITER_OWNONLY | (keyonly ? 0 : (JSITER_FOREACH | JSITER_KEYVALUE));
770 if (!ValueToIterator(cx, flags, args[0]))
771 return false;
772 args.rval().set(args[0]);
773 return true;
774 }
776 MOZ_ALWAYS_INLINE bool
777 IsIterator(HandleValue v)
778 {
779 return v.isObject() && v.toObject().hasClass(&PropertyIteratorObject::class_);
780 }
782 MOZ_ALWAYS_INLINE bool
783 iterator_next_impl(JSContext *cx, CallArgs args)
784 {
785 JS_ASSERT(IsIterator(args.thisv()));
787 RootedObject thisObj(cx, &args.thisv().toObject());
789 if (!js_IteratorMore(cx, thisObj, args.rval()))
790 return false;
792 if (!args.rval().toBoolean()) {
793 js_ThrowStopIteration(cx);
794 return false;
795 }
797 return js_IteratorNext(cx, thisObj, args.rval());
798 }
800 static bool
801 iterator_next(JSContext *cx, unsigned argc, Value *vp)
802 {
803 CallArgs args = CallArgsFromVp(argc, vp);
804 return CallNonGenericMethod<IsIterator, iterator_next_impl>(cx, args);
805 }
807 static const JSFunctionSpec iterator_methods[] = {
808 JS_SELF_HOSTED_FN("@@iterator", "LegacyIteratorShim", 0, 0),
809 JS_FN("next", iterator_next, 0, 0),
810 JS_FS_END
811 };
813 static JSObject *
814 iterator_iteratorObject(JSContext *cx, HandleObject obj, bool keysonly)
815 {
816 return obj;
817 }
819 size_t
820 PropertyIteratorObject::sizeOfMisc(mozilla::MallocSizeOf mallocSizeOf) const
821 {
822 return mallocSizeOf(getPrivate());
823 }
825 void
826 PropertyIteratorObject::trace(JSTracer *trc, JSObject *obj)
827 {
828 if (NativeIterator *ni = obj->as<PropertyIteratorObject>().getNativeIterator())
829 ni->mark(trc);
830 }
832 void
833 PropertyIteratorObject::finalize(FreeOp *fop, JSObject *obj)
834 {
835 if (NativeIterator *ni = obj->as<PropertyIteratorObject>().getNativeIterator())
836 fop->free_(ni);
837 }
839 const Class PropertyIteratorObject::class_ = {
840 "Iterator",
841 JSCLASS_IMPLEMENTS_BARRIERS |
842 JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator) |
843 JSCLASS_HAS_PRIVATE |
844 JSCLASS_BACKGROUND_FINALIZE,
845 JS_PropertyStub, /* addProperty */
846 JS_DeletePropertyStub, /* delProperty */
847 JS_PropertyStub, /* getProperty */
848 JS_StrictPropertyStub, /* setProperty */
849 JS_EnumerateStub,
850 JS_ResolveStub,
851 JS_ConvertStub,
852 finalize,
853 nullptr, /* call */
854 nullptr, /* hasInstance */
855 nullptr, /* construct */
856 trace,
857 JS_NULL_CLASS_SPEC,
858 {
859 nullptr, /* outerObject */
860 nullptr, /* innerObject */
861 iterator_iteratorObject,
862 }
863 };
865 enum {
866 ArrayIteratorSlotIteratedObject,
867 ArrayIteratorSlotNextIndex,
868 ArrayIteratorSlotItemKind,
869 ArrayIteratorSlotCount
870 };
872 const Class ArrayIteratorObject::class_ = {
873 "Array Iterator",
874 JSCLASS_IMPLEMENTS_BARRIERS |
875 JSCLASS_HAS_RESERVED_SLOTS(ArrayIteratorSlotCount),
876 JS_PropertyStub, /* addProperty */
877 JS_DeletePropertyStub, /* delProperty */
878 JS_PropertyStub, /* getProperty */
879 JS_StrictPropertyStub, /* setProperty */
880 JS_EnumerateStub,
881 JS_ResolveStub,
882 JS_ConvertStub,
883 nullptr /* finalize */
884 };
886 static const JSFunctionSpec array_iterator_methods[] = {
887 JS_SELF_HOSTED_FN("@@iterator", "ArrayIteratorIdentity", 0, 0),
888 JS_SELF_HOSTED_FN("next", "ArrayIteratorNext", 0, 0),
889 JS_FS_END
890 };
892 static const Class StringIteratorPrototypeClass = {
893 "String Iterator",
894 JSCLASS_IMPLEMENTS_BARRIERS,
895 JS_PropertyStub, /* addProperty */
896 JS_DeletePropertyStub, /* delProperty */
897 JS_PropertyStub, /* getProperty */
898 JS_StrictPropertyStub, /* setProperty */
899 JS_EnumerateStub,
900 JS_ResolveStub,
901 JS_ConvertStub,
902 nullptr /* finalize */
903 };
905 enum {
906 StringIteratorSlotIteratedObject,
907 StringIteratorSlotNextIndex,
908 StringIteratorSlotCount
909 };
911 const Class StringIteratorObject::class_ = {
912 "String Iterator",
913 JSCLASS_IMPLEMENTS_BARRIERS |
914 JSCLASS_HAS_RESERVED_SLOTS(StringIteratorSlotCount),
915 JS_PropertyStub, /* addProperty */
916 JS_DeletePropertyStub, /* delProperty */
917 JS_PropertyStub, /* getProperty */
918 JS_StrictPropertyStub, /* setProperty */
919 JS_EnumerateStub,
920 JS_ResolveStub,
921 JS_ConvertStub,
922 nullptr /* finalize */
923 };
925 static const JSFunctionSpec string_iterator_methods[] = {
926 JS_SELF_HOSTED_FN("@@iterator", "StringIteratorIdentity", 0, 0),
927 JS_SELF_HOSTED_FN("next", "StringIteratorNext", 0, 0),
928 JS_FS_END
929 };
931 static bool
932 CloseLegacyGenerator(JSContext *cx, HandleObject genobj);
934 bool
935 js::ValueToIterator(JSContext *cx, unsigned flags, MutableHandleValue vp)
936 {
937 /* JSITER_KEYVALUE must always come with JSITER_FOREACH */
938 JS_ASSERT_IF(flags & JSITER_KEYVALUE, flags & JSITER_FOREACH);
940 /*
941 * Make sure the more/next state machine doesn't get stuck. A value might
942 * be left in iterValue when a trace is left due to an interrupt after
943 * JSOP_MOREITER but before the value is picked up by FOR*.
944 */
945 cx->iterValue.setMagic(JS_NO_ITER_VALUE);
947 RootedObject obj(cx);
948 if (vp.isObject()) {
949 /* Common case. */
950 obj = &vp.toObject();
951 } else {
952 /*
953 * Enumerating over null and undefined gives an empty enumerator, so
954 * that |for (var p in <null or undefined>) <loop>;| never executes
955 * <loop>, per ES5 12.6.4.
956 */
957 if (!(flags & JSITER_ENUMERATE) || !vp.isNullOrUndefined()) {
958 obj = ToObject(cx, vp);
959 if (!obj)
960 return false;
961 }
962 }
964 return GetIterator(cx, obj, flags, vp);
965 }
967 bool
968 js::CloseIterator(JSContext *cx, HandleObject obj)
969 {
970 cx->iterValue.setMagic(JS_NO_ITER_VALUE);
972 if (obj->is<PropertyIteratorObject>()) {
973 /* Remove enumerators from the active list, which is a stack. */
974 NativeIterator *ni = obj->as<PropertyIteratorObject>().getNativeIterator();
976 if (ni->flags & JSITER_ENUMERATE) {
977 ni->unlink();
979 JS_ASSERT(ni->flags & JSITER_ACTIVE);
980 ni->flags &= ~JSITER_ACTIVE;
982 /*
983 * Reset the enumerator; it may still be in the cached iterators
984 * for this thread, and can be reused.
985 */
986 ni->props_cursor = ni->props_array;
987 }
988 } else if (obj->is<LegacyGeneratorObject>()) {
989 return CloseLegacyGenerator(cx, obj);
990 }
991 return true;
992 }
994 bool
995 js::UnwindIteratorForException(JSContext *cx, HandleObject obj)
996 {
997 RootedValue v(cx);
998 bool getOk = cx->getPendingException(&v);
999 cx->clearPendingException();
1000 if (!CloseIterator(cx, obj))
1001 return false;
1002 if (!getOk)
1003 return false;
1004 cx->setPendingException(v);
1005 return true;
1006 }
1008 void
1009 js::UnwindIteratorForUncatchableException(JSContext *cx, JSObject *obj)
1010 {
1011 if (obj->is<PropertyIteratorObject>()) {
1012 NativeIterator *ni = obj->as<PropertyIteratorObject>().getNativeIterator();
1013 if (ni->flags & JSITER_ENUMERATE)
1014 ni->unlink();
1015 }
1016 }
1018 /*
1019 * Suppress enumeration of deleted properties. This function must be called
1020 * when a property is deleted and there might be active enumerators.
1021 *
1022 * We maintain a list of active non-escaping for-in enumerators. To suppress
1023 * a property, we check whether each active enumerator contains the (obj, id)
1024 * pair and has not yet enumerated |id|. If so, and |id| is the next property,
1025 * we simply advance the cursor. Otherwise, we delete |id| from the list.
1026 *
1027 * We do not suppress enumeration of a property deleted along an object's
1028 * prototype chain. Only direct deletions on the object are handled.
1029 *
1030 * This function can suppress multiple properties at once. The |predicate|
1031 * argument is an object which can be called on an id and returns true or
1032 * false. It also must have a method |matchesAtMostOne| which allows us to
1033 * stop searching after the first deletion if true.
1034 */
1035 template<typename StringPredicate>
1036 static bool
1037 SuppressDeletedPropertyHelper(JSContext *cx, HandleObject obj, StringPredicate predicate)
1038 {
1039 NativeIterator *enumeratorList = cx->compartment()->enumerators;
1040 NativeIterator *ni = enumeratorList->next();
1042 while (ni != enumeratorList) {
1043 again:
1044 /* This only works for identified suppressed keys, not values. */
1045 if (ni->isKeyIter() && ni->obj == obj && ni->props_cursor < ni->props_end) {
1046 /* Check whether id is still to come. */
1047 HeapPtr<JSFlatString> *props_cursor = ni->current();
1048 HeapPtr<JSFlatString> *props_end = ni->end();
1049 for (HeapPtr<JSFlatString> *idp = props_cursor; idp < props_end; ++idp) {
1050 if (predicate(*idp)) {
1051 /*
1052 * Check whether another property along the prototype chain
1053 * became visible as a result of this deletion.
1054 */
1055 RootedObject proto(cx);
1056 if (!JSObject::getProto(cx, obj, &proto))
1057 return false;
1058 if (proto) {
1059 RootedObject obj2(cx);
1060 RootedShape prop(cx);
1061 RootedId id(cx);
1062 RootedValue idv(cx, StringValue(*idp));
1063 if (!ValueToId<CanGC>(cx, idv, &id))
1064 return false;
1065 if (!JSObject::lookupGeneric(cx, proto, id, &obj2, &prop))
1066 return false;
1067 if (prop) {
1068 unsigned attrs;
1069 if (obj2->isNative())
1070 attrs = GetShapeAttributes(obj2, prop);
1071 else if (!JSObject::getGenericAttributes(cx, obj2, id, &attrs))
1072 return false;
1074 if (attrs & JSPROP_ENUMERATE)
1075 continue;
1076 }
1077 }
1079 /*
1080 * If lookupProperty or getAttributes above removed a property from
1081 * ni, start over.
1082 */
1083 if (props_end != ni->props_end || props_cursor != ni->props_cursor)
1084 goto again;
1086 /*
1087 * No property along the prototype chain stepped in to take the
1088 * property's place, so go ahead and delete id from the list.
1089 * If it is the next property to be enumerated, just skip it.
1090 */
1091 if (idp == props_cursor) {
1092 ni->incCursor();
1093 } else {
1094 for (HeapPtr<JSFlatString> *p = idp; p + 1 != props_end; p++)
1095 *p = *(p + 1);
1096 ni->props_end = ni->end() - 1;
1098 /*
1099 * This invokes the pre barrier on this element, since
1100 * it's no longer going to be marked, and ensures that
1101 * any existing remembered set entry will be dropped.
1102 */
1103 *ni->props_end = nullptr;
1104 }
1106 /* Don't reuse modified native iterators. */
1107 ni->flags |= JSITER_UNREUSABLE;
1109 if (predicate.matchesAtMostOne())
1110 break;
1111 }
1112 }
1113 }
1114 ni = ni->next();
1115 }
1116 return true;
1117 }
1119 namespace {
1121 class SingleStringPredicate {
1122 Handle<JSFlatString*> str;
1123 public:
1124 SingleStringPredicate(Handle<JSFlatString*> str) : str(str) {}
1126 bool operator()(JSFlatString *str) { return EqualStrings(str, this->str); }
1127 bool matchesAtMostOne() { return true; }
1128 };
1130 } /* anonymous namespace */
1132 bool
1133 js_SuppressDeletedProperty(JSContext *cx, HandleObject obj, jsid id)
1134 {
1135 Rooted<JSFlatString*> str(cx, IdToString(cx, id));
1136 if (!str)
1137 return false;
1138 return SuppressDeletedPropertyHelper(cx, obj, SingleStringPredicate(str));
1139 }
1141 bool
1142 js_SuppressDeletedElement(JSContext *cx, HandleObject obj, uint32_t index)
1143 {
1144 RootedId id(cx);
1145 if (!IndexToId(cx, index, &id))
1146 return false;
1147 return js_SuppressDeletedProperty(cx, obj, id);
1148 }
1150 namespace {
1152 class IndexRangePredicate {
1153 uint32_t begin, end;
1155 public:
1156 IndexRangePredicate(uint32_t begin, uint32_t end) : begin(begin), end(end) {}
1158 bool operator()(JSFlatString *str) {
1159 uint32_t index;
1160 return str->isIndex(&index) && begin <= index && index < end;
1161 }
1163 bool matchesAtMostOne() { return false; }
1164 };
1166 } /* anonymous namespace */
1168 bool
1169 js_SuppressDeletedElements(JSContext *cx, HandleObject obj, uint32_t begin, uint32_t end)
1170 {
1171 return SuppressDeletedPropertyHelper(cx, obj, IndexRangePredicate(begin, end));
1172 }
1174 bool
1175 js_IteratorMore(JSContext *cx, HandleObject iterobj, MutableHandleValue rval)
1176 {
1177 /* Fast path for native iterators */
1178 NativeIterator *ni = nullptr;
1179 if (iterobj->is<PropertyIteratorObject>()) {
1180 /* Key iterators are handled by fast-paths. */
1181 ni = iterobj->as<PropertyIteratorObject>().getNativeIterator();
1182 bool more = ni->props_cursor < ni->props_end;
1183 if (ni->isKeyIter() || !more) {
1184 rval.setBoolean(more);
1185 return true;
1186 }
1187 }
1189 /* We might still have a pending value. */
1190 if (!cx->iterValue.isMagic(JS_NO_ITER_VALUE)) {
1191 rval.setBoolean(true);
1192 return true;
1193 }
1195 /* We're reentering below and can call anything. */
1196 JS_CHECK_RECURSION(cx, return false);
1198 /* Fetch and cache the next value from the iterator. */
1199 if (ni) {
1200 JS_ASSERT(!ni->isKeyIter());
1201 RootedId id(cx);
1202 RootedValue current(cx, StringValue(*ni->current()));
1203 if (!ValueToId<CanGC>(cx, current, &id))
1204 return false;
1205 ni->incCursor();
1206 RootedObject obj(cx, ni->obj);
1207 if (!JSObject::getGeneric(cx, obj, obj, id, rval))
1208 return false;
1209 if ((ni->flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, rval, rval))
1210 return false;
1211 } else {
1212 /* Call the iterator object's .next method. */
1213 if (!JSObject::getProperty(cx, iterobj, iterobj, cx->names().next, rval))
1214 return false;
1215 if (!Invoke(cx, ObjectValue(*iterobj), rval, 0, nullptr, rval)) {
1216 /* Check for StopIteration. */
1217 if (!cx->isExceptionPending())
1218 return false;
1219 RootedValue exception(cx);
1220 if (!cx->getPendingException(&exception))
1221 return false;
1222 if (!JS_IsStopIteration(exception))
1223 return false;
1225 cx->clearPendingException();
1226 cx->iterValue.setMagic(JS_NO_ITER_VALUE);
1227 rval.setBoolean(false);
1228 return true;
1229 }
1230 }
1232 /* Cache the value returned by iterobj.next() so js_IteratorNext() can find it. */
1233 JS_ASSERT(!rval.isMagic(JS_NO_ITER_VALUE));
1234 cx->iterValue = rval;
1235 rval.setBoolean(true);
1236 return true;
1237 }
1239 bool
1240 js_IteratorNext(JSContext *cx, HandleObject iterobj, MutableHandleValue rval)
1241 {
1242 /* Fast path for native iterators */
1243 if (iterobj->is<PropertyIteratorObject>()) {
1244 /*
1245 * Implement next directly as all the methods of the native iterator are
1246 * read-only and permanent.
1247 */
1248 NativeIterator *ni = iterobj->as<PropertyIteratorObject>().getNativeIterator();
1249 if (ni->isKeyIter()) {
1250 JS_ASSERT(ni->props_cursor < ni->props_end);
1251 rval.setString(*ni->current());
1252 ni->incCursor();
1253 return true;
1254 }
1255 }
1257 JS_ASSERT(!cx->iterValue.isMagic(JS_NO_ITER_VALUE));
1258 rval.set(cx->iterValue);
1259 cx->iterValue.setMagic(JS_NO_ITER_VALUE);
1261 return true;
1262 }
1264 static bool
1265 stopiter_hasInstance(JSContext *cx, HandleObject obj, MutableHandleValue v, bool *bp)
1266 {
1267 *bp = JS_IsStopIteration(v);
1268 return true;
1269 }
1271 const Class StopIterationObject::class_ = {
1272 "StopIteration",
1273 JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration),
1274 JS_PropertyStub, /* addProperty */
1275 JS_DeletePropertyStub, /* delProperty */
1276 JS_PropertyStub, /* getProperty */
1277 JS_StrictPropertyStub, /* setProperty */
1278 JS_EnumerateStub,
1279 JS_ResolveStub,
1280 JS_ConvertStub,
1281 nullptr, /* finalize */
1282 nullptr, /* call */
1283 stopiter_hasInstance,
1284 nullptr /* construct */
1285 };
1287 bool
1288 ForOfIterator::init(HandleValue iterable, NonIterableBehavior nonIterableBehavior)
1289 {
1290 JSContext *cx = cx_;
1291 RootedObject iterableObj(cx, ToObject(cx, iterable));
1292 if (!iterableObj)
1293 return false;
1295 JS_ASSERT(index == NOT_ARRAY);
1297 // Check the PIC first for a match.
1298 if (iterableObj->is<ArrayObject>()) {
1299 ForOfPIC::Chain *stubChain = ForOfPIC::getOrCreate(cx);
1300 if (!stubChain)
1301 return false;
1303 bool optimized;
1304 if (!stubChain->tryOptimizeArray(cx, iterableObj, &optimized))
1305 return false;
1307 if (optimized) {
1308 // Got optimized stub. Array is optimizable.
1309 index = 0;
1310 iterator = iterableObj;
1311 return true;
1312 }
1313 }
1315 JS_ASSERT(index == NOT_ARRAY);
1317 // The iterator is the result of calling obj[@@iterator]().
1318 InvokeArgs args(cx);
1319 if (!args.init(0))
1320 return false;
1321 args.setThis(ObjectValue(*iterableObj));
1323 RootedValue callee(cx);
1324 if (!JSObject::getProperty(cx, iterableObj, iterableObj, cx->names().std_iterator, &callee))
1325 return false;
1327 // Throw if obj[@@iterator] isn't callable if we were asked to do so.
1328 // js::Invoke is about to check for this kind of error anyway, but it would
1329 // throw an inscrutable error message about |method| rather than this nice
1330 // one about |obj|.
1331 if (!callee.isObject() || !callee.toObject().isCallable()) {
1332 if (nonIterableBehavior == AllowNonIterable)
1333 return true;
1334 char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, iterable, NullPtr());
1335 if (!bytes)
1336 return false;
1337 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE, bytes);
1338 js_free(bytes);
1339 return false;
1340 }
1342 args.setCallee(callee);
1343 if (!Invoke(cx, args))
1344 return false;
1346 iterator = ToObject(cx, args.rval());
1347 if (!iterator)
1348 return false;
1350 return true;
1351 }
1353 inline bool
1354 ForOfIterator::nextFromOptimizedArray(MutableHandleValue vp, bool *done)
1355 {
1356 JS_ASSERT(index != NOT_ARRAY);
1358 if (!CheckForInterrupt(cx_))
1359 return false;
1361 JS_ASSERT(iterator->isNative());
1362 JS_ASSERT(iterator->is<ArrayObject>());
1364 if (index >= iterator->as<ArrayObject>().length()) {
1365 vp.setUndefined();
1366 *done = true;
1367 return true;
1368 }
1369 *done = false;
1371 // Try to get array element via direct access.
1372 if (index < iterator->getDenseInitializedLength()) {
1373 vp.set(iterator->getDenseElement(index));
1374 if (!vp.isMagic(JS_ELEMENTS_HOLE)) {
1375 ++index;
1376 return true;
1377 }
1378 }
1380 return JSObject::getElement(cx_, iterator, iterator, index++, vp);
1381 }
1383 bool
1384 ForOfIterator::next(MutableHandleValue vp, bool *done)
1385 {
1386 JS_ASSERT(iterator);
1388 if (index != NOT_ARRAY) {
1389 ForOfPIC::Chain *stubChain = ForOfPIC::getOrCreate(cx_);
1390 if (!stubChain)
1391 return false;
1393 if (stubChain->isArrayNextStillSane())
1394 return nextFromOptimizedArray(vp, done);
1396 // ArrayIterator.prototype.next changed, materialize a proper
1397 // ArrayIterator instance and fall through to slowpath case.
1398 if (!materializeArrayIterator())
1399 return false;
1400 }
1402 RootedValue method(cx_);
1403 if (!JSObject::getProperty(cx_, iterator, iterator, cx_->names().next, &method))
1404 return false;
1406 InvokeArgs args(cx_);
1407 if (!args.init(1))
1408 return false;
1409 args.setCallee(method);
1410 args.setThis(ObjectValue(*iterator));
1411 args[0].setUndefined();
1412 if (!Invoke(cx_, args))
1413 return false;
1415 RootedObject resultObj(cx_, ToObject(cx_, args.rval()));
1416 if (!resultObj)
1417 return false;
1418 RootedValue doneVal(cx_);
1419 if (!JSObject::getProperty(cx_, resultObj, resultObj, cx_->names().done, &doneVal))
1420 return false;
1421 *done = ToBoolean(doneVal);
1422 if (*done) {
1423 vp.setUndefined();
1424 return true;
1425 }
1426 return JSObject::getProperty(cx_, resultObj, resultObj, cx_->names().value, vp);
1427 }
1429 bool
1430 ForOfIterator::materializeArrayIterator()
1431 {
1432 JS_ASSERT(index != NOT_ARRAY);
1434 const char *nameString = "ArrayValuesAt";
1436 RootedAtom name(cx_, Atomize(cx_, nameString, strlen(nameString)));
1437 if (!name)
1438 return false;
1440 RootedValue val(cx_);
1441 if (!cx_->global()->getSelfHostedFunction(cx_, name, name, 1, &val))
1442 return false;
1444 InvokeArgs args(cx_);
1445 if (!args.init(1))
1446 return false;
1447 args.setCallee(val);
1448 args.setThis(ObjectValue(*iterator));
1449 args[0].set(Int32Value(index));
1450 if (!Invoke(cx_, args))
1451 return false;
1453 index = NOT_ARRAY;
1454 // Result of call to ArrayValuesAt must be an object.
1455 iterator = &args.rval().toObject();
1456 return true;
1457 }
1459 /*** Generators **********************************************************************************/
1461 template<typename T>
1462 static void
1463 FinalizeGenerator(FreeOp *fop, JSObject *obj)
1464 {
1465 JS_ASSERT(obj->is<T>());
1466 JSGenerator *gen = obj->as<T>().getGenerator();
1467 JS_ASSERT(gen);
1468 // gen is open when a script has not called its close method while
1469 // explicitly manipulating it.
1470 JS_ASSERT(gen->state == JSGEN_NEWBORN ||
1471 gen->state == JSGEN_CLOSED ||
1472 gen->state == JSGEN_OPEN);
1473 // If gen->state is JSGEN_CLOSED, gen->fp may be nullptr.
1474 if (gen->fp)
1475 JS_POISON(gen->fp, JS_SWEPT_FRAME_PATTERN, sizeof(InterpreterFrame));
1476 JS_POISON(gen, JS_SWEPT_FRAME_PATTERN, sizeof(JSGenerator));
1477 fop->free_(gen);
1478 }
1480 static void
1481 MarkGeneratorFrame(JSTracer *trc, JSGenerator *gen)
1482 {
1483 MarkValueRange(trc,
1484 HeapValueify(gen->fp->generatorArgsSnapshotBegin()),
1485 HeapValueify(gen->fp->generatorArgsSnapshotEnd()),
1486 "Generator Floating Args");
1487 gen->fp->mark(trc);
1488 MarkValueRange(trc,
1489 HeapValueify(gen->fp->generatorSlotsSnapshotBegin()),
1490 HeapValueify(gen->regs.sp),
1491 "Generator Floating Stack");
1492 }
1494 static void
1495 GeneratorWriteBarrierPre(JSContext *cx, JSGenerator *gen)
1496 {
1497 JS::Zone *zone = cx->zone();
1498 if (zone->needsBarrier())
1499 MarkGeneratorFrame(zone->barrierTracer(), gen);
1500 }
1502 static void
1503 GeneratorWriteBarrierPost(JSContext *cx, JSGenerator *gen)
1504 {
1505 #ifdef JSGC_GENERATIONAL
1506 cx->runtime()->gcStoreBuffer.putWholeCell(gen->obj);
1507 #endif
1508 }
1510 /*
1511 * Only mark generator frames/slots when the generator is not active on the
1512 * stack or closed. Barriers when copying onto the stack or closing preserve
1513 * gc invariants.
1514 */
1515 static bool
1516 GeneratorHasMarkableFrame(JSGenerator *gen)
1517 {
1518 return gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN;
1519 }
1521 /*
1522 * When a generator is closed, the GC things reachable from the contained frame
1523 * and slots become unreachable and thus require a write barrier.
1524 */
1525 static void
1526 SetGeneratorClosed(JSContext *cx, JSGenerator *gen)
1527 {
1528 JS_ASSERT(gen->state != JSGEN_CLOSED);
1529 if (GeneratorHasMarkableFrame(gen))
1530 GeneratorWriteBarrierPre(cx, gen);
1531 gen->state = JSGEN_CLOSED;
1533 #ifdef DEBUG
1534 MakeRangeGCSafe(gen->fp->generatorArgsSnapshotBegin(),
1535 gen->fp->generatorArgsSnapshotEnd());
1536 MakeRangeGCSafe(gen->fp->generatorSlotsSnapshotBegin(),
1537 gen->regs.sp);
1538 PodZero(&gen->regs, 1);
1539 gen->fp = nullptr;
1540 #endif
1541 }
1543 template<typename T>
1544 static void
1545 TraceGenerator(JSTracer *trc, JSObject *obj)
1546 {
1547 JS_ASSERT(obj->is<T>());
1548 JSGenerator *gen = obj->as<T>().getGenerator();
1549 JS_ASSERT(gen);
1550 if (GeneratorHasMarkableFrame(gen))
1551 MarkGeneratorFrame(trc, gen);
1552 }
1554 GeneratorState::GeneratorState(JSContext *cx, JSGenerator *gen, JSGeneratorState futureState)
1555 : RunState(cx, Generator, gen->fp->script()),
1556 cx_(cx),
1557 gen_(gen),
1558 futureState_(futureState),
1559 entered_(false)
1560 { }
1562 GeneratorState::~GeneratorState()
1563 {
1564 gen_->fp->setSuspended();
1566 if (entered_)
1567 cx_->leaveGenerator(gen_);
1568 }
1570 InterpreterFrame *
1571 GeneratorState::pushInterpreterFrame(JSContext *cx)
1572 {
1573 /*
1574 * Write barrier is needed since the generator stack can be updated,
1575 * and it's not barriered in any other way. We need to do it before
1576 * gen->state changes, which can cause us to trace the generator
1577 * differently.
1578 *
1579 * We could optimize this by setting a bit on the generator to signify
1580 * that it has been marked. If this bit has already been set, there is no
1581 * need to mark again. The bit would have to be reset before the next GC,
1582 * or else some kind of epoch scheme would have to be used.
1583 */
1584 GeneratorWriteBarrierPre(cx, gen_);
1585 gen_->state = futureState_;
1587 gen_->fp->clearSuspended();
1589 cx->enterGenerator(gen_); /* OOM check above. */
1590 entered_ = true;
1591 return gen_->fp;
1592 }
1594 const Class LegacyGeneratorObject::class_ = {
1595 "Generator",
1596 JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
1597 JS_PropertyStub, /* addProperty */
1598 JS_DeletePropertyStub, /* delProperty */
1599 JS_PropertyStub, /* getProperty */
1600 JS_StrictPropertyStub, /* setProperty */
1601 JS_EnumerateStub,
1602 JS_ResolveStub,
1603 JS_ConvertStub,
1604 FinalizeGenerator<LegacyGeneratorObject>,
1605 nullptr, /* call */
1606 nullptr, /* hasInstance */
1607 nullptr, /* construct */
1608 TraceGenerator<LegacyGeneratorObject>,
1609 JS_NULL_CLASS_SPEC,
1610 {
1611 nullptr, /* outerObject */
1612 nullptr, /* innerObject */
1613 iterator_iteratorObject,
1614 }
1615 };
1617 const Class StarGeneratorObject::class_ = {
1618 "Generator",
1619 JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
1620 JS_PropertyStub, /* addProperty */
1621 JS_DeletePropertyStub, /* delProperty */
1622 JS_PropertyStub, /* getProperty */
1623 JS_StrictPropertyStub, /* setProperty */
1624 JS_EnumerateStub,
1625 JS_ResolveStub,
1626 JS_ConvertStub,
1627 FinalizeGenerator<StarGeneratorObject>,
1628 nullptr, /* call */
1629 nullptr, /* hasInstance */
1630 nullptr, /* construct */
1631 TraceGenerator<StarGeneratorObject>,
1632 JS_NULL_CLASS_SPEC,
1633 {
1634 nullptr, /* outerObject */
1635 nullptr, /* innerObject */
1636 iterator_iteratorObject,
1637 }
1638 };
1640 /*
1641 * Called from the JSOP_GENERATOR case in the interpreter, with fp referring
1642 * to the frame by which the generator function was activated. Create a new
1643 * JSGenerator object, which contains its own InterpreterFrame that we populate
1644 * from *fp. We know that upon return, the JSOP_GENERATOR opcode will return
1645 * from the activation in fp, so we can steal away fp->callobj and fp->argsobj
1646 * if they are non-null.
1647 */
1648 JSObject *
1649 js_NewGenerator(JSContext *cx, const InterpreterRegs &stackRegs)
1650 {
1651 JS_ASSERT(stackRegs.stackDepth() == 0);
1652 InterpreterFrame *stackfp = stackRegs.fp();
1654 JS_ASSERT(stackfp->script()->isGenerator());
1656 Rooted<GlobalObject*> global(cx, &stackfp->global());
1657 RootedObject obj(cx);
1658 if (stackfp->script()->isStarGenerator()) {
1659 RootedValue pval(cx);
1660 RootedObject fun(cx, stackfp->fun());
1661 // FIXME: This would be faster if we could avoid doing a lookup to get
1662 // the prototype for the instance. Bug 906600.
1663 if (!JSObject::getProperty(cx, fun, fun, cx->names().prototype, &pval))
1664 return nullptr;
1665 JSObject *proto = pval.isObject() ? &pval.toObject() : nullptr;
1666 if (!proto) {
1667 proto = GlobalObject::getOrCreateStarGeneratorObjectPrototype(cx, global);
1668 if (!proto)
1669 return nullptr;
1670 }
1671 obj = NewObjectWithGivenProto(cx, &StarGeneratorObject::class_, proto, global);
1672 } else {
1673 JS_ASSERT(stackfp->script()->isLegacyGenerator());
1674 JSObject *proto = GlobalObject::getOrCreateLegacyGeneratorObjectPrototype(cx, global);
1675 if (!proto)
1676 return nullptr;
1677 obj = NewObjectWithGivenProto(cx, &LegacyGeneratorObject::class_, proto, global);
1678 }
1679 if (!obj)
1680 return nullptr;
1682 /* Load and compute stack slot counts. */
1683 Value *stackvp = stackfp->generatorArgsSnapshotBegin();
1684 unsigned vplen = stackfp->generatorArgsSnapshotEnd() - stackvp;
1686 /* Compute JSGenerator size. */
1687 unsigned nbytes = sizeof(JSGenerator) +
1688 (-1 + /* one Value included in JSGenerator */
1689 vplen +
1690 VALUES_PER_STACK_FRAME +
1691 stackfp->script()->nslots()) * sizeof(HeapValue);
1693 JS_ASSERT(nbytes % sizeof(Value) == 0);
1694 JS_STATIC_ASSERT(sizeof(InterpreterFrame) % sizeof(HeapValue) == 0);
1696 JSGenerator *gen = (JSGenerator *) cx->calloc_(nbytes);
1697 if (!gen)
1698 return nullptr;
1700 /* Cut up floatingStack space. */
1701 HeapValue *genvp = gen->stackSnapshot;
1702 SetValueRangeToUndefined((Value *)genvp, vplen);
1704 InterpreterFrame *genfp = reinterpret_cast<InterpreterFrame *>(genvp + vplen);
1706 /* Initialize JSGenerator. */
1707 gen->obj.init(obj);
1708 gen->state = JSGEN_NEWBORN;
1709 gen->fp = genfp;
1710 gen->prevGenerator = nullptr;
1712 /* Copy from the stack to the generator's floating frame. */
1713 gen->regs.rebaseFromTo(stackRegs, *genfp);
1714 genfp->copyFrameAndValues<InterpreterFrame::DoPostBarrier>(cx, (Value *)genvp, stackfp,
1715 stackvp, stackRegs.sp);
1716 genfp->setSuspended();
1717 obj->setPrivate(gen);
1718 return obj;
1719 }
1721 static void
1722 SetGeneratorClosed(JSContext *cx, JSGenerator *gen);
1724 typedef enum JSGeneratorOp {
1725 JSGENOP_NEXT,
1726 JSGENOP_SEND,
1727 JSGENOP_THROW,
1728 JSGENOP_CLOSE
1729 } JSGeneratorOp;
1731 /*
1732 * Start newborn or restart yielding generator and perform the requested
1733 * operation inside its frame.
1734 */
1735 static bool
1736 SendToGenerator(JSContext *cx, JSGeneratorOp op, HandleObject obj,
1737 JSGenerator *gen, HandleValue arg, GeneratorKind generatorKind,
1738 MutableHandleValue rval)
1739 {
1740 JS_ASSERT(generatorKind == LegacyGenerator || generatorKind == StarGenerator);
1742 if (gen->state == JSGEN_RUNNING || gen->state == JSGEN_CLOSING) {
1743 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NESTING_GENERATOR);
1744 return false;
1745 }
1747 JSGeneratorState futureState;
1748 JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN);
1749 switch (op) {
1750 case JSGENOP_NEXT:
1751 case JSGENOP_SEND:
1752 if (gen->state == JSGEN_OPEN) {
1753 /*
1754 * Store the argument to send as the result of the yield
1755 * expression. The generator stack is not barriered, so we need
1756 * write barriers here.
1757 */
1758 HeapValue::writeBarrierPre(gen->regs.sp[-1]);
1759 gen->regs.sp[-1] = arg;
1760 HeapValue::writeBarrierPost(cx->runtime(), gen->regs.sp[-1], &gen->regs.sp[-1]);
1761 }
1762 futureState = JSGEN_RUNNING;
1763 break;
1765 case JSGENOP_THROW:
1766 cx->setPendingException(arg);
1767 futureState = JSGEN_RUNNING;
1768 break;
1770 default:
1771 JS_ASSERT(op == JSGENOP_CLOSE);
1772 JS_ASSERT(generatorKind == LegacyGenerator);
1773 cx->setPendingException(MagicValue(JS_GENERATOR_CLOSING));
1774 futureState = JSGEN_CLOSING;
1775 break;
1776 }
1778 bool ok;
1779 {
1780 GeneratorState state(cx, gen, futureState);
1781 ok = RunScript(cx, state);
1782 if (!ok && gen->state == JSGEN_CLOSED)
1783 return false;
1784 }
1786 if (gen->fp->isYielding()) {
1787 /*
1788 * Yield is ordinarily infallible, but ok can be false here if a
1789 * Debugger.Frame.onPop hook fails.
1790 */
1791 JS_ASSERT(gen->state == JSGEN_RUNNING);
1792 JS_ASSERT(op != JSGENOP_CLOSE);
1793 gen->fp->clearYielding();
1794 gen->state = JSGEN_OPEN;
1795 GeneratorWriteBarrierPost(cx, gen);
1796 rval.set(gen->fp->returnValue());
1797 return ok;
1798 }
1800 if (ok) {
1801 if (generatorKind == StarGenerator) {
1802 // Star generators return a {value:FOO, done:true} object.
1803 rval.set(gen->fp->returnValue());
1804 } else {
1805 JS_ASSERT(generatorKind == LegacyGenerator);
1807 // Otherwise we discard the return value and throw a StopIteration
1808 // if needed.
1809 rval.setUndefined();
1810 if (op != JSGENOP_CLOSE)
1811 ok = js_ThrowStopIteration(cx);
1812 }
1813 }
1815 SetGeneratorClosed(cx, gen);
1816 return ok;
1817 }
1819 MOZ_ALWAYS_INLINE bool
1820 star_generator_next(JSContext *cx, CallArgs args)
1821 {
1822 RootedObject thisObj(cx, &args.thisv().toObject());
1823 JSGenerator *gen = thisObj->as<StarGeneratorObject>().getGenerator();
1825 if (gen->state == JSGEN_CLOSED) {
1826 RootedObject obj(cx, CreateItrResultObject(cx, JS::UndefinedHandleValue, true));
1827 if (!obj)
1828 return false;
1829 args.rval().setObject(*obj);
1830 return true;
1831 }
1833 if (gen->state == JSGEN_NEWBORN && args.hasDefined(0)) {
1834 RootedValue val(cx, args[0]);
1835 js_ReportValueError(cx, JSMSG_BAD_GENERATOR_SEND,
1836 JSDVG_SEARCH_STACK, val, js::NullPtr());
1837 return false;
1838 }
1840 return SendToGenerator(cx, JSGENOP_SEND, thisObj, gen, args.get(0), StarGenerator,
1841 args.rval());
1842 }
1844 MOZ_ALWAYS_INLINE bool
1845 star_generator_throw(JSContext *cx, CallArgs args)
1846 {
1847 RootedObject thisObj(cx, &args.thisv().toObject());
1849 JSGenerator *gen = thisObj->as<StarGeneratorObject>().getGenerator();
1850 if (gen->state == JSGEN_CLOSED) {
1851 cx->setPendingException(args.get(0));
1852 return false;
1853 }
1855 return SendToGenerator(cx, JSGENOP_THROW, thisObj, gen, args.get(0), StarGenerator,
1856 args.rval());
1857 }
1859 MOZ_ALWAYS_INLINE bool
1860 legacy_generator_next(JSContext *cx, CallArgs args)
1861 {
1862 RootedObject thisObj(cx, &args.thisv().toObject());
1864 JSGenerator *gen = thisObj->as<LegacyGeneratorObject>().getGenerator();
1865 if (gen->state == JSGEN_CLOSED)
1866 return js_ThrowStopIteration(cx);
1868 if (gen->state == JSGEN_NEWBORN && args.hasDefined(0)) {
1869 RootedValue val(cx, args[0]);
1870 js_ReportValueError(cx, JSMSG_BAD_GENERATOR_SEND,
1871 JSDVG_SEARCH_STACK, val, js::NullPtr());
1872 return false;
1873 }
1875 return SendToGenerator(cx, JSGENOP_SEND, thisObj, gen, args.get(0), LegacyGenerator,
1876 args.rval());
1877 }
1879 MOZ_ALWAYS_INLINE bool
1880 legacy_generator_throw(JSContext *cx, CallArgs args)
1881 {
1882 RootedObject thisObj(cx, &args.thisv().toObject());
1884 JSGenerator *gen = thisObj->as<LegacyGeneratorObject>().getGenerator();
1885 if (gen->state == JSGEN_CLOSED) {
1886 cx->setPendingException(args.length() >= 1 ? args[0] : UndefinedValue());
1887 return false;
1888 }
1890 return SendToGenerator(cx, JSGENOP_THROW, thisObj, gen, args.get(0), LegacyGenerator,
1891 args.rval());
1892 }
1894 static bool
1895 CloseLegacyGenerator(JSContext *cx, HandleObject obj, MutableHandleValue rval)
1896 {
1897 JS_ASSERT(obj->is<LegacyGeneratorObject>());
1899 JSGenerator *gen = obj->as<LegacyGeneratorObject>().getGenerator();
1901 if (gen->state == JSGEN_CLOSED) {
1902 rval.setUndefined();
1903 return true;
1904 }
1906 if (gen->state == JSGEN_NEWBORN) {
1907 SetGeneratorClosed(cx, gen);
1908 rval.setUndefined();
1909 return true;
1910 }
1912 return SendToGenerator(cx, JSGENOP_CLOSE, obj, gen, JS::UndefinedHandleValue, LegacyGenerator,
1913 rval);
1914 }
1916 static bool
1917 CloseLegacyGenerator(JSContext *cx, HandleObject obj)
1918 {
1919 RootedValue rval(cx);
1920 return CloseLegacyGenerator(cx, obj, &rval);
1921 }
1923 MOZ_ALWAYS_INLINE bool
1924 legacy_generator_close(JSContext *cx, CallArgs args)
1925 {
1926 RootedObject thisObj(cx, &args.thisv().toObject());
1928 return CloseLegacyGenerator(cx, thisObj, args.rval());
1929 }
1931 template<typename T>
1932 MOZ_ALWAYS_INLINE bool
1933 IsObjectOfType(HandleValue v)
1934 {
1935 return v.isObject() && v.toObject().is<T>();
1936 }
1938 template<typename T, NativeImpl Impl>
1939 static bool
1940 NativeMethod(JSContext *cx, unsigned argc, Value *vp)
1941 {
1942 CallArgs args = CallArgsFromVp(argc, vp);
1943 return CallNonGenericMethod<IsObjectOfType<T>, Impl>(cx, args);
1944 }
1946 #define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT)
1947 #define JS_METHOD(name, T, impl, len, attrs) JS_FN(name, (NativeMethod<T,impl>), len, attrs)
1949 static const JSFunctionSpec star_generator_methods[] = {
1950 JS_SELF_HOSTED_FN("@@iterator", "IteratorIdentity", 0, 0),
1951 JS_METHOD("next", StarGeneratorObject, star_generator_next, 1, 0),
1952 JS_METHOD("throw", StarGeneratorObject, star_generator_throw, 1, 0),
1953 JS_FS_END
1954 };
1956 static const JSFunctionSpec legacy_generator_methods[] = {
1957 JS_SELF_HOSTED_FN("@@iterator", "LegacyGeneratorIteratorShim", 0, 0),
1958 // "send" is an alias for "next".
1959 JS_METHOD("next", LegacyGeneratorObject, legacy_generator_next, 1, JSPROP_ROPERM),
1960 JS_METHOD("send", LegacyGeneratorObject, legacy_generator_next, 1, JSPROP_ROPERM),
1961 JS_METHOD("throw", LegacyGeneratorObject, legacy_generator_throw, 1, JSPROP_ROPERM),
1962 JS_METHOD("close", LegacyGeneratorObject, legacy_generator_close, 0, JSPROP_ROPERM),
1963 JS_FS_END
1964 };
1966 static JSObject*
1967 NewSingletonObjectWithObjectPrototype(JSContext *cx, Handle<GlobalObject *> global)
1968 {
1969 JSObject *proto = global->getOrCreateObjectPrototype(cx);
1970 if (!proto)
1971 return nullptr;
1972 return NewObjectWithGivenProto(cx, &JSObject::class_, proto, global, SingletonObject);
1973 }
1975 static JSObject*
1976 NewSingletonObjectWithFunctionPrototype(JSContext *cx, Handle<GlobalObject *> global)
1977 {
1978 JSObject *proto = global->getOrCreateFunctionPrototype(cx);
1979 if (!proto)
1980 return nullptr;
1981 return NewObjectWithGivenProto(cx, &JSObject::class_, proto, global, SingletonObject);
1982 }
1984 /* static */ bool
1985 GlobalObject::initIteratorClasses(JSContext *cx, Handle<GlobalObject *> global)
1986 {
1987 RootedObject iteratorProto(cx);
1988 Value iteratorProtoVal = global->getPrototype(JSProto_Iterator);
1989 if (iteratorProtoVal.isObject()) {
1990 iteratorProto = &iteratorProtoVal.toObject();
1991 } else {
1992 iteratorProto = global->createBlankPrototype(cx, &PropertyIteratorObject::class_);
1993 if (!iteratorProto)
1994 return false;
1996 AutoIdVector blank(cx);
1997 NativeIterator *ni = NativeIterator::allocateIterator(cx, 0, blank);
1998 if (!ni)
1999 return false;
2000 ni->init(nullptr, nullptr, 0 /* flags */, 0, 0);
2002 iteratorProto->as<PropertyIteratorObject>().setNativeIterator(ni);
2004 Rooted<JSFunction*> ctor(cx);
2005 ctor = global->createConstructor(cx, IteratorConstructor, cx->names().Iterator, 2);
2006 if (!ctor)
2007 return false;
2008 if (!LinkConstructorAndPrototype(cx, ctor, iteratorProto))
2009 return false;
2010 if (!DefinePropertiesAndBrand(cx, iteratorProto, nullptr, iterator_methods))
2011 return false;
2012 if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_Iterator,
2013 ctor, iteratorProto))
2014 {
2015 return false;
2016 }
2017 }
2019 RootedObject proto(cx);
2020 if (global->getSlot(ARRAY_ITERATOR_PROTO).isUndefined()) {
2021 const Class *cls = &ArrayIteratorObject::class_;
2022 proto = global->createBlankPrototypeInheriting(cx, cls, *iteratorProto);
2023 if (!proto || !DefinePropertiesAndBrand(cx, proto, nullptr, array_iterator_methods))
2024 return false;
2025 global->setReservedSlot(ARRAY_ITERATOR_PROTO, ObjectValue(*proto));
2026 }
2028 if (global->getSlot(STRING_ITERATOR_PROTO).isUndefined()) {
2029 const Class *cls = &StringIteratorPrototypeClass;
2030 proto = global->createBlankPrototype(cx, cls);
2031 if (!proto || !DefinePropertiesAndBrand(cx, proto, nullptr, string_iterator_methods))
2032 return false;
2033 global->setReservedSlot(STRING_ITERATOR_PROTO, ObjectValue(*proto));
2034 }
2036 if (global->getSlot(LEGACY_GENERATOR_OBJECT_PROTO).isUndefined()) {
2037 proto = NewSingletonObjectWithObjectPrototype(cx, global);
2038 if (!proto || !DefinePropertiesAndBrand(cx, proto, nullptr, legacy_generator_methods))
2039 return false;
2040 global->setReservedSlot(LEGACY_GENERATOR_OBJECT_PROTO, ObjectValue(*proto));
2041 }
2043 if (global->getSlot(STAR_GENERATOR_OBJECT_PROTO).isUndefined()) {
2044 RootedObject genObjectProto(cx, NewSingletonObjectWithObjectPrototype(cx, global));
2045 if (!genObjectProto)
2046 return false;
2047 if (!DefinePropertiesAndBrand(cx, genObjectProto, nullptr, star_generator_methods))
2048 return false;
2050 RootedObject genFunctionProto(cx, NewSingletonObjectWithFunctionPrototype(cx, global));
2051 if (!genFunctionProto)
2052 return false;
2053 if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto))
2054 return false;
2056 RootedValue function(cx, global->getConstructor(JSProto_Function));
2057 if (!function.toObjectOrNull())
2058 return false;
2059 RootedAtom name(cx, cx->names().GeneratorFunction);
2060 RootedObject genFunction(cx, NewFunctionWithProto(cx, NullPtr(), Generator, 1,
2061 JSFunction::NATIVE_CTOR, global, name,
2062 &function.toObject()));
2063 if (!genFunction)
2064 return false;
2065 if (!LinkConstructorAndPrototype(cx, genFunction, genFunctionProto))
2066 return false;
2068 global->setSlot(STAR_GENERATOR_OBJECT_PROTO, ObjectValue(*genObjectProto));
2069 global->setConstructor(JSProto_GeneratorFunction, ObjectValue(*genFunction));
2070 global->setPrototype(JSProto_GeneratorFunction, ObjectValue(*genFunctionProto));
2071 }
2073 if (global->getPrototype(JSProto_StopIteration).isUndefined()) {
2074 proto = global->createBlankPrototype(cx, &StopIterationObject::class_);
2075 if (!proto || !JSObject::freeze(cx, proto))
2076 return false;
2078 // This should use a non-JSProtoKey'd slot, but this is easier for now.
2079 if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_StopIteration, proto, proto))
2080 return false;
2082 global->setConstructor(JSProto_StopIteration, ObjectValue(*proto));
2083 }
2085 return true;
2086 }
2088 JSObject *
2089 js_InitIteratorClasses(JSContext *cx, HandleObject obj)
2090 {
2091 Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
2092 if (!GlobalObject::initIteratorClasses(cx, global))
2093 return nullptr;
2094 return global->getIteratorPrototype();
2095 }