Bug 629709 - White line of highlight pixels appears above navigation toolbar if the...
[mozilla-central.git] / js / src / jsiter.cpp
blob4599a356bc087bb556c06ab6afcc017ca8fcd87d
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=78:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
23 * the Initial Developer. All Rights Reserved.
25 * Contributor(s):
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
42 * JavaScript iterators.
44 #include <string.h> /* for memcpy */
45 #include "jstypes.h"
46 #include "jsstdint.h"
47 #include "jsutil.h"
48 #include "jsarena.h"
49 #include "jsapi.h"
50 #include "jsarray.h"
51 #include "jsatom.h"
52 #include "jsbool.h"
53 #include "jsbuiltins.h"
54 #include "jscntxt.h"
55 #include "jsversion.h"
56 #include "jsexn.h"
57 #include "jsfun.h"
58 #include "jsgc.h"
59 #include "jshashtable.h"
60 #include "jsinterp.h"
61 #include "jsiter.h"
62 #include "jslock.h"
63 #include "jsnum.h"
64 #include "jsobj.h"
65 #include "jsopcode.h"
66 #include "jsproxy.h"
67 #include "jsscan.h"
68 #include "jsscope.h"
69 #include "jsscript.h"
70 #include "jsstaticcheck.h"
71 #include "jstracer.h"
72 #include "jsvector.h"
74 #if JS_HAS_XML_SUPPORT
75 #include "jsxml.h"
76 #endif
78 #include "jscntxtinlines.h"
79 #include "jsinterpinlines.h"
80 #include "jsobjinlines.h"
81 #include "jsstrinlines.h"
83 using namespace js;
84 using namespace js::gc;
86 static void iterator_finalize(JSContext *cx, JSObject *obj);
87 static void iterator_trace(JSTracer *trc, JSObject *obj);
88 static JSObject *iterator_iterator(JSContext *cx, JSObject *obj, JSBool keysonly);
90 Class js_IteratorClass = {
91 "Iterator",
92 JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator) |
93 JSCLASS_MARK_IS_TRACE,
94 PropertyStub, /* addProperty */
95 PropertyStub, /* delProperty */
96 PropertyStub, /* getProperty */
97 PropertyStub, /* setProperty */
98 EnumerateStub,
99 ResolveStub,
100 ConvertStub,
101 iterator_finalize,
102 NULL, /* reserved */
103 NULL, /* checkAccess */
104 NULL, /* call */
105 NULL, /* construct */
106 NULL, /* xdrObject */
107 NULL, /* hasInstance */
108 JS_CLASS_TRACE(iterator_trace),
110 NULL, /* equality */
111 NULL, /* outerObject */
112 NULL, /* innerObject */
113 iterator_iterator,
114 NULL /* unused */
118 void
119 NativeIterator::mark(JSTracer *trc)
121 MarkIdRange(trc, begin(), end(), "props");
122 if (obj)
123 MarkObject(trc, *obj, "obj");
126 static void
127 iterator_finalize(JSContext *cx, JSObject *obj)
129 JS_ASSERT(obj->getClass() == &js_IteratorClass);
131 NativeIterator *ni = obj->getNativeIterator();
132 if (ni) {
133 cx->free(ni);
134 obj->setNativeIterator(NULL);
138 static void
139 iterator_trace(JSTracer *trc, JSObject *obj)
141 NativeIterator *ni = obj->getNativeIterator();
143 if (ni)
144 ni->mark(trc);
147 struct IdHashPolicy {
148 typedef jsid Lookup;
149 static HashNumber hash(jsid id) {
150 return JSID_BITS(id);
152 static bool match(jsid id1, jsid id2) {
153 return id1 == id2;
157 typedef HashSet<jsid, IdHashPolicy, ContextAllocPolicy> IdSet;
159 static inline bool
160 NewKeyValuePair(JSContext *cx, jsid id, const Value &val, Value *rval)
162 Value vec[2] = { IdToValue(id), val };
163 AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(vec), vec);
165 JSObject *aobj = NewDenseCopiedArray(cx, 2, vec);
166 if (!aobj)
167 return false;
168 rval->setObject(*aobj);
169 return true;
172 static inline bool
173 Enumerate(JSContext *cx, JSObject *obj, JSObject *pobj, jsid id,
174 bool enumerable, bool sharedPermanent, uintN flags, IdSet& ht,
175 AutoIdVector *props)
177 IdSet::AddPtr p = ht.lookupForAdd(id);
178 JS_ASSERT_IF(obj == pobj && !obj->isProxy(), !p);
180 /* If we've already seen this, we definitely won't add it. */
181 if (JS_UNLIKELY(!!p))
182 return true;
185 * It's not necessary to add properties to the hash table at the end of the
186 * prototype chain -- but a proxy might return duplicated properties, so
187 * always add for them.
189 if ((pobj->getProto() || pobj->isProxy()) && !ht.add(p, id))
190 return false;
192 if (JS_UNLIKELY(flags & JSITER_OWNONLY)) {
194 * Shared-permanent hack: If this property is shared permanent
195 * and pobj and obj have the same class, then treat it as an own
196 * property of obj, even if pobj != obj. (But see bug 575997.)
198 * Omit the magic __proto__ property so that JS code can use
199 * Object.getOwnPropertyNames without worrying about it.
201 if (!pobj->getProto() && id == ATOM_TO_JSID(cx->runtime->atomState.protoAtom))
202 return true;
203 if (pobj != obj && !(sharedPermanent && pobj->getClass() == obj->getClass()))
204 return true;
207 if (enumerable || (flags & JSITER_HIDDEN))
208 return props->append(id);
210 return true;
213 static bool
214 EnumerateNativeProperties(JSContext *cx, JSObject *obj, JSObject *pobj, uintN flags, IdSet &ht,
215 AutoIdVector *props)
217 size_t initialLength = props->length();
219 /* Collect all unique properties from this object's scope. */
220 for (Shape::Range r = pobj->lastProperty()->all(); !r.empty(); r.popFront()) {
221 const Shape &shape = r.front();
223 if (!JSID_IS_DEFAULT_XML_NAMESPACE(shape.id) &&
224 !shape.isAlias() &&
225 !Enumerate(cx, obj, pobj, shape.id, shape.enumerable(),
226 shape.isSharedPermanent(), flags, ht, props))
228 return false;
232 Reverse(props->begin() + initialLength, props->end());
233 return true;
236 static bool
237 EnumerateDenseArrayProperties(JSContext *cx, JSObject *obj, JSObject *pobj, uintN flags,
238 IdSet &ht, AutoIdVector *props)
240 if (!Enumerate(cx, obj, pobj, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), false, true,
241 flags, ht, props)) {
242 return false;
245 if (pobj->getArrayLength() > 0) {
246 size_t capacity = pobj->getDenseArrayCapacity();
247 Value *vp = pobj->getDenseArrayElements();
248 for (size_t i = 0; i < capacity; ++i, ++vp) {
249 if (!vp->isMagic(JS_ARRAY_HOLE)) {
250 /* Dense arrays never get so large that i would not fit into an integer id. */
251 if (!Enumerate(cx, obj, pobj, INT_TO_JSID(i), true, false, flags, ht, props))
252 return false;
257 return true;
260 static bool
261 Snapshot(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector *props)
264 * FIXME: Bug 575997 - We won't need to initialize this hash table if
265 * (flags & JSITER_OWNONLY) when we eliminate inheritance of
266 * shared-permanent properties as own properties.
268 IdSet ht(cx);
269 if (!ht.init(32))
270 return NULL;
272 JSObject *pobj = obj;
273 do {
274 Class *clasp = pobj->getClass();
275 if (pobj->isNative() &&
276 !pobj->getOps()->enumerate &&
277 !(clasp->flags & JSCLASS_NEW_ENUMERATE)) {
278 if (!clasp->enumerate(cx, pobj))
279 return false;
280 if (!EnumerateNativeProperties(cx, obj, pobj, flags, ht, props))
281 return false;
282 } else if (pobj->isDenseArray()) {
283 if (!EnumerateDenseArrayProperties(cx, obj, pobj, flags, ht, props))
284 return false;
285 } else {
286 if (pobj->isProxy()) {
287 AutoIdVector proxyProps(cx);
288 if (flags & JSITER_OWNONLY) {
289 if (flags & JSITER_HIDDEN) {
290 if (!JSProxy::getOwnPropertyNames(cx, pobj, proxyProps))
291 return false;
292 } else {
293 if (!JSProxy::keys(cx, pobj, proxyProps))
294 return false;
296 } else {
297 if (!JSProxy::enumerate(cx, pobj, proxyProps))
298 return false;
300 for (size_t n = 0, len = proxyProps.length(); n < len; n++) {
301 if (!Enumerate(cx, obj, pobj, proxyProps[n], true, false, flags, ht, props))
302 return false;
304 /* Proxy objects enumerate the prototype on their own, so we are done here. */
305 break;
307 Value state;
308 JSIterateOp op = (flags & JSITER_HIDDEN) ? JSENUMERATE_INIT_ALL : JSENUMERATE_INIT;
309 if (!pobj->enumerate(cx, op, &state, NULL))
310 return false;
311 if (state.isMagic(JS_NATIVE_ENUMERATE)) {
312 if (!EnumerateNativeProperties(cx, obj, pobj, flags, ht, props))
313 return false;
314 } else {
315 while (true) {
316 jsid id;
317 if (!pobj->enumerate(cx, JSENUMERATE_NEXT, &state, &id))
318 return false;
319 if (state.isNull())
320 break;
321 if (!Enumerate(cx, obj, pobj, id, true, false, flags, ht, props))
322 return false;
327 if (JS_UNLIKELY(pobj->isXML()))
328 break;
329 } while ((pobj = pobj->getProto()) != NULL);
331 return true;
334 namespace js {
336 bool
337 VectorToIdArray(JSContext *cx, AutoIdVector &props, JSIdArray **idap)
339 JS_STATIC_ASSERT(sizeof(JSIdArray) > sizeof(jsid));
340 size_t len = props.length();
341 size_t idsz = len * sizeof(jsid);
342 size_t sz = (sizeof(JSIdArray) - sizeof(jsid)) + idsz;
343 JSIdArray *ida = static_cast<JSIdArray *>(cx->malloc(sz));
344 if (!ida)
345 return false;
347 ida->length = static_cast<jsint>(len);
348 memcpy(ida->vector, props.begin(), idsz);
349 *idap = ida;
350 return true;
353 JS_FRIEND_API(bool)
354 GetPropertyNames(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector *props)
356 return Snapshot(cx, obj, flags & (JSITER_OWNONLY | JSITER_HIDDEN), props);
361 static inline bool
362 GetCustomIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp)
364 /* Check whether we have a valid __iterator__ method. */
365 JSAtom *atom = cx->runtime->atomState.iteratorAtom;
366 if (!js_GetMethod(cx, obj, ATOM_TO_JSID(atom), JSGET_NO_METHOD_BARRIER, vp))
367 return false;
369 /* If there is no custom __iterator__ method, we are done here. */
370 if (!vp->isObject()) {
371 vp->setUndefined();
372 return true;
375 /* Otherwise call it and return that object. */
376 LeaveTrace(cx);
377 Value arg = BooleanValue((flags & JSITER_FOREACH) == 0);
378 if (!ExternalInvoke(cx, ObjectValue(*obj), *vp, 1, &arg, vp))
379 return false;
380 if (vp->isPrimitive()) {
382 * We are always coming from js_ValueToIterator, and we are no longer on
383 * trace, so the object we are iterating over is on top of the stack (-1).
385 JSAutoByteString bytes;
386 if (!js_AtomToPrintableString(cx, atom, &bytes))
387 return false;
388 js_ReportValueError2(cx, JSMSG_BAD_TRAP_RETURN_VALUE,
389 -1, ObjectValue(*obj), NULL, bytes.ptr());
390 return false;
392 return true;
395 template <typename T>
396 static inline bool
397 Compare(T *a, T *b, size_t c)
399 size_t n = (c + size_t(7)) / size_t(8);
400 switch (c % 8) {
401 case 0: do { if (*a++ != *b++) return false;
402 case 7: if (*a++ != *b++) return false;
403 case 6: if (*a++ != *b++) return false;
404 case 5: if (*a++ != *b++) return false;
405 case 4: if (*a++ != *b++) return false;
406 case 3: if (*a++ != *b++) return false;
407 case 2: if (*a++ != *b++) return false;
408 case 1: if (*a++ != *b++) return false;
409 } while (--n > 0);
411 return true;
414 static inline JSObject *
415 NewIteratorObject(JSContext *cx, uintN flags)
417 if (flags & JSITER_ENUMERATE) {
419 * Non-escaping native enumerator objects do not need map, proto, or
420 * parent. However, code in jstracer.cpp and elsewhere may find such a
421 * native enumerator object via the stack and (as for all objects that
422 * are not stillborn, with the exception of "NoSuchMethod" internal
423 * helper objects) expect it to have a non-null map pointer, so we
424 * share an empty Enumerator scope in the runtime.
426 JSObject *obj = js_NewGCObject(cx, FINALIZE_OBJECT0);
427 if (!obj)
428 return false;
429 obj->init(cx, &js_IteratorClass, NULL, NULL, NULL, false);
430 obj->setMap(cx->runtime->emptyEnumeratorShape);
431 return obj;
434 return NewBuiltinClassInstance(cx, &js_IteratorClass);
437 NativeIterator *
438 NativeIterator::allocateIterator(JSContext *cx, uint32 slength, const AutoIdVector &props)
440 size_t plength = props.length();
441 NativeIterator *ni = (NativeIterator *)
442 cx->malloc(sizeof(NativeIterator) + plength * sizeof(jsid) + slength * sizeof(uint32));
443 if (!ni)
444 return NULL;
445 ni->props_array = ni->props_cursor = (jsid *) (ni + 1);
446 ni->props_end = (jsid *)ni->props_array + plength;
447 if (plength)
448 memcpy(ni->props_array, props.begin(), plength * sizeof(jsid));
449 return ni;
452 inline void
453 NativeIterator::init(JSObject *obj, uintN flags, uint32 slength, uint32 key)
455 this->obj = obj;
456 this->flags = flags;
457 this->shapes_array = (uint32 *) this->props_end;
458 this->shapes_length = slength;
459 this->shapes_key = key;
462 static inline void
463 RegisterEnumerator(JSContext *cx, JSObject *iterobj, NativeIterator *ni)
465 /* Register non-escaping native enumerators (for-in) with the current context. */
466 if (ni->flags & JSITER_ENUMERATE) {
467 ni->next = cx->enumerators;
468 cx->enumerators = iterobj;
470 JS_ASSERT(!(ni->flags & JSITER_ACTIVE));
471 ni->flags |= JSITER_ACTIVE;
475 static inline bool
476 VectorToKeyIterator(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector &keys,
477 uint32 slength, uint32 key, Value *vp)
479 JS_ASSERT(!(flags & JSITER_FOREACH));
481 JSObject *iterobj = NewIteratorObject(cx, flags);
482 if (!iterobj)
483 return false;
485 NativeIterator *ni = NativeIterator::allocateIterator(cx, slength, keys);
486 if (!ni)
487 return false;
488 ni->init(obj, flags, slength, key);
490 if (slength) {
492 * Fill in the shape array from scratch. We can't use the array that was
493 * computed for the cache lookup earlier, as constructing iterobj could
494 * have triggered a shape-regenerating GC. Don't bother with regenerating
495 * the shape key; if such a GC *does* occur, we can only get hits through
496 * the one-slot lastNativeIterator cache.
498 JSObject *pobj = obj;
499 size_t ind = 0;
500 do {
501 ni->shapes_array[ind++] = pobj->shape();
502 pobj = pobj->getProto();
503 } while (pobj);
504 JS_ASSERT(ind == slength);
507 iterobj->setNativeIterator(ni);
508 vp->setObject(*iterobj);
510 RegisterEnumerator(cx, iterobj, ni);
511 return true;
514 namespace js {
516 bool
517 VectorToKeyIterator(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector &props, Value *vp)
519 return VectorToKeyIterator(cx, obj, flags, props, 0, 0, vp);
522 bool
523 VectorToValueIterator(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector &keys,
524 Value *vp)
526 JS_ASSERT(flags & JSITER_FOREACH);
528 JSObject *iterobj = NewIteratorObject(cx, flags);
529 if (!iterobj)
530 return false;
532 NativeIterator *ni = NativeIterator::allocateIterator(cx, 0, keys);
533 if (!ni)
534 return false;
535 ni->init(obj, flags, 0, 0);
537 iterobj->setNativeIterator(ni);
538 vp->setObject(*iterobj);
540 RegisterEnumerator(cx, iterobj, ni);
541 return true;
544 bool
545 EnumeratedIdVectorToIterator(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector &props, Value *vp)
547 if (!(flags & JSITER_FOREACH))
548 return VectorToKeyIterator(cx, obj, flags, props, vp);
550 return VectorToValueIterator(cx, obj, flags, props, vp);
553 typedef Vector<uint32, 8> ShapeVector;
555 static inline void
556 UpdateNativeIterator(NativeIterator *ni, JSObject *obj)
558 // Update the object for which the native iterator is associated, so
559 // SuppressDeletedPropertyHelper will recognize the iterator as a match.
560 ni->obj = obj;
563 bool
564 GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp)
566 Vector<uint32, 8> shapes(cx);
567 uint32 key = 0;
569 bool keysOnly = (flags == JSITER_ENUMERATE);
571 if (obj) {
572 /* Enumerate Iterator.prototype directly. */
573 JSIteratorOp op = obj->getClass()->ext.iteratorObject;
574 if (op && (obj->getClass() != &js_IteratorClass || obj->getNativeIterator())) {
575 JSObject *iterobj = op(cx, obj, !(flags & JSITER_FOREACH));
576 if (!iterobj)
577 return false;
578 vp->setObject(*iterobj);
579 return true;
582 if (keysOnly) {
584 * Check to see if this is the same as the most recent object which
585 * was iterated over. We don't explicitly check for shapeless
586 * objects here, as they are not inserted into the cache and
587 * will result in a miss.
589 JSObject *last = cx->compartment->nativeIterCache.last;
590 JSObject *proto = obj->getProto();
591 if (last) {
592 NativeIterator *lastni = last->getNativeIterator();
593 if (!(lastni->flags & JSITER_ACTIVE) &&
594 obj->isNative() &&
595 obj->shape() == lastni->shapes_array[0] &&
596 proto && proto->isNative() &&
597 proto->shape() == lastni->shapes_array[1] &&
598 !proto->getProto()) {
599 vp->setObject(*last);
600 UpdateNativeIterator(lastni, obj);
601 RegisterEnumerator(cx, last, lastni);
602 return true;
607 * The iterator object for JSITER_ENUMERATE never escapes, so we
608 * don't care for the proper parent/proto to be set. This also
609 * allows us to re-use a previous iterator object that is not
610 * currently active.
612 JSObject *pobj = obj;
613 do {
614 if (!pobj->isNative() ||
615 obj->getOps()->enumerate ||
616 pobj->getClass()->enumerate != JS_EnumerateStub) {
617 shapes.clear();
618 goto miss;
620 uint32 shape = pobj->shape();
621 key = (key + (key << 16)) ^ shape;
622 if (!shapes.append(shape))
623 return false;
624 pobj = pobj->getProto();
625 } while (pobj);
627 JSObject *iterobj = cx->compartment->nativeIterCache.get(key);
628 if (iterobj) {
629 NativeIterator *ni = iterobj->getNativeIterator();
630 if (!(ni->flags & JSITER_ACTIVE) &&
631 ni->shapes_key == key &&
632 ni->shapes_length == shapes.length() &&
633 Compare(ni->shapes_array, shapes.begin(), ni->shapes_length)) {
634 vp->setObject(*iterobj);
636 UpdateNativeIterator(ni, obj);
637 RegisterEnumerator(cx, iterobj, ni);
638 if (shapes.length() == 2)
639 cx->compartment->nativeIterCache.last = iterobj;
640 return true;
645 miss:
646 if (obj->isProxy())
647 return JSProxy::iterate(cx, obj, flags, vp);
648 if (!GetCustomIterator(cx, obj, flags, vp))
649 return false;
650 if (!vp->isUndefined())
651 return true;
654 /* NB: for (var p in null) succeeds by iterating over no properties. */
656 AutoIdVector keys(cx);
657 if (flags & JSITER_FOREACH) {
658 if (JS_LIKELY(obj != NULL) && !Snapshot(cx, obj, flags, &keys))
659 return false;
660 JS_ASSERT(shapes.empty());
661 if (!VectorToValueIterator(cx, obj, flags, keys, vp))
662 return false;
663 } else {
664 if (JS_LIKELY(obj != NULL) && !Snapshot(cx, obj, flags, &keys))
665 return false;
666 if (!VectorToKeyIterator(cx, obj, flags, keys, shapes.length(), key, vp))
667 return false;
670 JSObject *iterobj = &vp->toObject();
672 /* Cache the iterator object if possible. */
673 if (shapes.length())
674 cx->compartment->nativeIterCache.set(key, iterobj);
676 if (shapes.length() == 2)
677 cx->compartment->nativeIterCache.last = iterobj;
678 return true;
683 static JSObject *
684 iterator_iterator(JSContext *cx, JSObject *obj, JSBool keysonly)
686 return obj;
689 static JSBool
690 Iterator(JSContext *cx, uintN argc, Value *vp)
692 Value *argv = JS_ARGV(cx, vp);
693 bool keyonly = argc >= 2 ? js_ValueToBoolean(argv[1]) : false;
694 uintN flags = JSITER_OWNONLY | (keyonly ? 0 : (JSITER_FOREACH | JSITER_KEYVALUE));
695 *vp = argc >= 1 ? argv[0] : UndefinedValue();
696 return js_ValueToIterator(cx, flags, vp);
699 JSBool
700 js_ThrowStopIteration(JSContext *cx)
702 Value v;
704 JS_ASSERT(!JS_IsExceptionPending(cx));
705 if (js_FindClassObject(cx, NULL, JSProto_StopIteration, &v))
706 cx->setPendingException(v);
707 return JS_FALSE;
710 static JSBool
711 iterator_next(JSContext *cx, uintN argc, Value *vp)
713 JSObject *obj = ToObject(cx, &vp[1]);
714 if (!obj || !InstanceOf(cx, obj, &js_IteratorClass, vp + 2))
715 return false;
717 if (!js_IteratorMore(cx, obj, vp))
718 return false;
719 if (!vp->toBoolean()) {
720 js_ThrowStopIteration(cx);
721 return false;
723 return js_IteratorNext(cx, obj, vp);
726 #define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT)
728 static JSFunctionSpec iterator_methods[] = {
729 JS_FN(js_next_str, iterator_next, 0,JSPROP_ROPERM),
730 JS_FS_END
734 * Call ToObject(v).__iterator__(keyonly) if ToObject(v).__iterator__ exists.
735 * Otherwise construct the default iterator.
737 JS_FRIEND_API(JSBool)
738 js_ValueToIterator(JSContext *cx, uintN flags, Value *vp)
740 /* JSITER_KEYVALUE must always come with JSITER_FOREACH */
741 JS_ASSERT_IF(flags & JSITER_KEYVALUE, flags & JSITER_FOREACH);
744 * Make sure the more/next state machine doesn't get stuck. A value might be
745 * left in iterValue when a trace is left due to an operation time-out after
746 * JSOP_MOREITER but before the value is picked up by FOR*.
748 cx->iterValue.setMagic(JS_NO_ITER_VALUE);
750 JSObject *obj;
751 if (vp->isObject()) {
752 /* Common case. */
753 obj = &vp->toObject();
754 } else {
756 * Enumerating over null and undefined gives an empty enumerator.
757 * This is contrary to ECMA-262 9.9 ToObject, invoked from step 3 of
758 * the first production in 12.6.4 and step 4 of the second production,
759 * but it's "web JS" compatible. ES5 fixed for-in to match this de-facto
760 * standard.
762 if ((flags & JSITER_ENUMERATE)) {
763 if (!js_ValueToObjectOrNull(cx, *vp, &obj))
764 return false;
765 /* fall through */
766 } else {
767 obj = js_ValueToNonNullObject(cx, *vp);
768 if (!obj)
769 return false;
773 return GetIterator(cx, obj, flags, vp);
776 #if JS_HAS_GENERATORS
777 static JS_REQUIRES_STACK JSBool
778 CloseGenerator(JSContext *cx, JSObject *genobj);
779 #endif
781 JS_FRIEND_API(JSBool)
782 js_CloseIterator(JSContext *cx, JSObject *obj)
784 cx->iterValue.setMagic(JS_NO_ITER_VALUE);
786 Class *clasp = obj->getClass();
787 if (clasp == &js_IteratorClass) {
788 /* Remove enumerators from the active list, which is a stack. */
789 NativeIterator *ni = obj->getNativeIterator();
791 if (ni->flags & JSITER_ENUMERATE) {
792 JS_ASSERT(cx->enumerators == obj);
793 cx->enumerators = ni->next;
795 JS_ASSERT(ni->flags & JSITER_ACTIVE);
796 ni->flags &= ~JSITER_ACTIVE;
799 * Reset the enumerator; it may still be in the cached iterators
800 * for this thread, and can be reused.
802 ni->props_cursor = ni->props_array;
805 #if JS_HAS_GENERATORS
806 else if (clasp == &js_GeneratorClass) {
807 return CloseGenerator(cx, obj);
809 #endif
810 return JS_TRUE;
814 * Suppress enumeration of deleted properties. This function must be called
815 * when a property is deleted and there might be active enumerators.
817 * We maintain a list of active non-escaping for-in enumerators. To suppress
818 * a property, we check whether each active enumerator contains the (obj, id)
819 * pair and has not yet enumerated |id|. If so, and |id| is the next property,
820 * we simply advance the cursor. Otherwise, we delete |id| from the list.
822 * We do not suppress enumeration of a property deleted along an object's
823 * prototype chain. Only direct deletions on the object are handled.
825 * This function can suppress multiple properties at once. The |predicate|
826 * argument is an object which can be called on an id and returns true or
827 * false. It also must have a method |matchesAtMostOne| which allows us to
828 * stop searching after the first deletion if true.
830 template<typename IdPredicate>
831 static bool
832 SuppressDeletedPropertyHelper(JSContext *cx, JSObject *obj, IdPredicate predicate)
834 JSObject *iterobj = cx->enumerators;
835 while (iterobj) {
836 again:
837 NativeIterator *ni = iterobj->getNativeIterator();
838 /* This only works for identified surpressed keys, not values. */
839 if (ni->isKeyIter() && ni->obj == obj && ni->props_cursor < ni->props_end) {
840 /* Check whether id is still to come. */
841 jsid *props_cursor = ni->current();
842 jsid *props_end = ni->end();
843 for (jsid *idp = props_cursor; idp < props_end; ++idp) {
844 if (predicate(*idp)) {
846 * Check whether another property along the prototype chain
847 * became visible as a result of this deletion.
849 if (obj->getProto()) {
850 AutoObjectRooter proto(cx, obj->getProto());
851 AutoObjectRooter obj2(cx);
852 JSProperty *prop;
853 if (!proto.object()->lookupProperty(cx, *idp, obj2.addr(), &prop))
854 return false;
855 if (prop) {
856 uintN attrs;
857 if (obj2.object()->isNative())
858 attrs = ((Shape *) prop)->attributes();
859 else if (!obj2.object()->getAttributes(cx, *idp, &attrs))
860 return false;
862 if (attrs & JSPROP_ENUMERATE)
863 continue;
868 * If lookupProperty or getAttributes above removed a property from
869 * ni, start over.
871 if (props_end != ni->props_end || props_cursor != ni->props_cursor)
872 goto again;
875 * No property along the prototype chain stepped in to take the
876 * property's place, so go ahead and delete id from the list.
877 * If it is the next property to be enumerated, just skip it.
879 if (idp == props_cursor) {
880 ni->incCursor();
881 } else {
882 memmove(idp, idp + 1, (props_end - (idp + 1)) * sizeof(jsid));
883 ni->props_end = ni->end() - 1;
885 if (predicate.matchesAtMostOne())
886 break;
890 iterobj = ni->next;
892 return true;
895 class SingleIdPredicate {
896 jsid id;
897 public:
898 SingleIdPredicate(jsid id) : id(id) {}
900 bool operator()(jsid id) { return id == this->id; }
901 bool matchesAtMostOne() { return true; }
904 bool
905 js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id)
907 id = js_CheckForStringIndex(id);
908 return SuppressDeletedPropertyHelper(cx, obj, SingleIdPredicate(id));
911 class IndexRangePredicate {
912 jsint begin, end;
913 public:
914 IndexRangePredicate(jsint begin, jsint end) : begin(begin), end(end) {}
916 bool operator()(jsid id) {
917 return JSID_IS_INT(id) && begin <= JSID_TO_INT(id) && JSID_TO_INT(id) < end;
919 bool matchesAtMostOne() { return false; }
922 bool
923 js_SuppressDeletedIndexProperties(JSContext *cx, JSObject *obj, jsint begin, jsint end)
925 return SuppressDeletedPropertyHelper(cx, obj, IndexRangePredicate(begin, end));
928 JSBool
929 js_IteratorMore(JSContext *cx, JSObject *iterobj, Value *rval)
931 /* Fast path for native iterators */
932 NativeIterator *ni = NULL;
933 if (iterobj->getClass() == &js_IteratorClass) {
934 /* Key iterators are handled by fast-paths. */
935 ni = iterobj->getNativeIterator();
936 bool more = ni->props_cursor < ni->props_end;
937 if (ni->isKeyIter() || !more) {
938 rval->setBoolean(more);
939 return true;
943 /* We might still have a pending value. */
944 if (!cx->iterValue.isMagic(JS_NO_ITER_VALUE)) {
945 rval->setBoolean(true);
946 return true;
949 /* Fetch and cache the next value from the iterator. */
950 if (!ni) {
951 jsid id = ATOM_TO_JSID(cx->runtime->atomState.nextAtom);
952 if (!js_GetMethod(cx, iterobj, id, JSGET_METHOD_BARRIER, rval))
953 return false;
954 if (!ExternalInvoke(cx, ObjectValue(*iterobj), *rval, 0, NULL, rval)) {
955 /* Check for StopIteration. */
956 if (!cx->isExceptionPending() || !js_ValueIsStopIteration(cx->getPendingException()))
957 return false;
959 cx->clearPendingException();
960 cx->iterValue.setMagic(JS_NO_ITER_VALUE);
961 rval->setBoolean(false);
962 return true;
964 } else {
965 JS_ASSERT(!ni->isKeyIter());
966 jsid id = *ni->current();
967 ni->incCursor();
968 if (!ni->obj->getProperty(cx, id, rval))
969 return false;
970 if ((ni->flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, *rval, rval))
971 return false;
974 /* Cache the value returned by iterobj.next() so js_IteratorNext() can find it. */
975 JS_ASSERT(!rval->isMagic(JS_NO_ITER_VALUE));
976 cx->iterValue = *rval;
977 rval->setBoolean(true);
978 return true;
981 JSBool
982 js_IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval)
984 /* Fast path for native iterators */
985 if (iterobj->getClass() == &js_IteratorClass) {
987 * Implement next directly as all the methods of the native iterator are
988 * read-only and permanent.
990 NativeIterator *ni = iterobj->getNativeIterator();
991 if (ni->isKeyIter()) {
992 JS_ASSERT(ni->props_cursor < ni->props_end);
993 *rval = IdToValue(*ni->current());
994 ni->incCursor();
996 if (rval->isString())
997 return true;
999 JSString *str;
1000 jsint i;
1001 if (rval->isInt32() && (jsuint(i = rval->toInt32()) < INT_STRING_LIMIT)) {
1002 str = JSString::intString(i);
1003 } else {
1004 str = js_ValueToString(cx, *rval);
1005 if (!str)
1006 return false;
1009 rval->setString(str);
1010 return true;
1014 JS_ASSERT(!cx->iterValue.isMagic(JS_NO_ITER_VALUE));
1015 *rval = cx->iterValue;
1016 cx->iterValue.setMagic(JS_NO_ITER_VALUE);
1018 return true;
1021 static JSBool
1022 stopiter_hasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp)
1024 *bp = js_ValueIsStopIteration(*v);
1025 return JS_TRUE;
1028 Class js_StopIterationClass = {
1029 js_StopIteration_str,
1030 JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration) |
1031 JSCLASS_FREEZE_PROTO,
1032 PropertyStub, /* addProperty */
1033 PropertyStub, /* delProperty */
1034 PropertyStub, /* getProperty */
1035 PropertyStub, /* setProperty */
1036 EnumerateStub,
1037 ResolveStub,
1038 ConvertStub,
1039 NULL, /* finalize */
1040 NULL, /* reserved0 */
1041 NULL, /* checkAccess */
1042 NULL, /* call */
1043 NULL, /* construct */
1044 NULL, /* xdrObject */
1045 stopiter_hasInstance
1048 #if JS_HAS_GENERATORS
1050 static void
1051 generator_finalize(JSContext *cx, JSObject *obj)
1053 JSGenerator *gen = (JSGenerator *) obj->getPrivate();
1054 if (!gen)
1055 return;
1058 * gen is open when a script has not called its close method while
1059 * explicitly manipulating it.
1061 JS_ASSERT(gen->state == JSGEN_NEWBORN ||
1062 gen->state == JSGEN_CLOSED ||
1063 gen->state == JSGEN_OPEN);
1064 cx->free(gen);
1067 static void
1068 generator_trace(JSTracer *trc, JSObject *obj)
1070 JSGenerator *gen = (JSGenerator *) obj->getPrivate();
1071 if (!gen)
1072 return;
1075 * Do not mark if the generator is running; the contents may be trash and
1076 * will be replaced when the generator stops.
1078 if (gen->state == JSGEN_RUNNING || gen->state == JSGEN_CLOSING)
1079 return;
1081 JSStackFrame *fp = gen->floatingFrame();
1082 JS_ASSERT(gen->liveFrame() == fp);
1083 MarkValueRange(trc, gen->floatingStack, fp->formalArgsEnd(), "generator slots");
1084 js_TraceStackFrame(trc, fp);
1085 MarkValueRange(trc, fp->slots(), gen->regs.sp, "generator slots");
1088 Class js_GeneratorClass = {
1089 js_Generator_str,
1090 JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Generator) |
1091 JSCLASS_IS_ANONYMOUS | JSCLASS_MARK_IS_TRACE,
1092 PropertyStub, /* addProperty */
1093 PropertyStub, /* delProperty */
1094 PropertyStub, /* getProperty */
1095 PropertyStub, /* setProperty */
1096 EnumerateStub,
1097 ResolveStub,
1098 ConvertStub,
1099 generator_finalize,
1100 NULL, /* reserved */
1101 NULL, /* checkAccess */
1102 NULL, /* call */
1103 NULL, /* construct */
1104 NULL, /* xdrObject */
1105 NULL, /* hasInstance */
1106 JS_CLASS_TRACE(generator_trace),
1108 NULL, /* equality */
1109 NULL, /* outerObject */
1110 NULL, /* innerObject */
1111 iterator_iterator,
1112 NULL /* unused */
1116 static inline void
1117 RebaseRegsFromTo(JSFrameRegs *regs, JSStackFrame *from, JSStackFrame *to)
1119 regs->fp = to;
1120 regs->sp = to->slots() + (regs->sp - from->slots());
1124 * Called from the JSOP_GENERATOR case in the interpreter, with fp referring
1125 * to the frame by which the generator function was activated. Create a new
1126 * JSGenerator object, which contains its own JSStackFrame that we populate
1127 * from *fp. We know that upon return, the JSOP_GENERATOR opcode will return
1128 * from the activation in fp, so we can steal away fp->callobj and fp->argsobj
1129 * if they are non-null.
1131 JS_REQUIRES_STACK JSObject *
1132 js_NewGenerator(JSContext *cx)
1134 JSObject *obj = NewBuiltinClassInstance(cx, &js_GeneratorClass);
1135 if (!obj)
1136 return NULL;
1138 JSStackFrame *stackfp = cx->fp();
1139 JS_ASSERT(stackfp->base() == cx->regs->sp);
1140 JS_ASSERT(stackfp->actualArgs() <= stackfp->formalArgs());
1142 /* Load and compute stack slot counts. */
1143 Value *stackvp = stackfp->actualArgs() - 2;
1144 uintN vplen = stackfp->formalArgsEnd() - stackvp;
1146 /* Compute JSGenerator size. */
1147 uintN nbytes = sizeof(JSGenerator) +
1148 (-1 + /* one Value included in JSGenerator */
1149 vplen +
1150 VALUES_PER_STACK_FRAME +
1151 stackfp->numSlots()) * sizeof(Value);
1153 JSGenerator *gen = (JSGenerator *) cx->malloc(nbytes);
1154 if (!gen)
1155 return NULL;
1157 /* Cut up floatingStack space. */
1158 Value *genvp = gen->floatingStack;
1159 JSStackFrame *genfp = reinterpret_cast<JSStackFrame *>(genvp + vplen);
1161 /* Initialize JSGenerator. */
1162 gen->obj = obj;
1163 gen->state = JSGEN_NEWBORN;
1164 gen->enumerators = NULL;
1165 gen->floating = genfp;
1167 /* Initialize regs stored in generator. */
1168 gen->regs = *cx->regs;
1169 RebaseRegsFromTo(&gen->regs, stackfp, genfp);
1171 /* Copy frame off the stack. */
1172 genfp->stealFrameAndSlots(genvp, stackfp, stackvp, cx->regs->sp);
1173 genfp->initFloatingGenerator();
1175 obj->setPrivate(gen);
1176 return obj;
1179 JSGenerator *
1180 js_FloatingFrameToGenerator(JSStackFrame *fp)
1182 JS_ASSERT(fp->isGeneratorFrame() && fp->isFloatingGenerator());
1183 char *floatingStackp = (char *)(fp->actualArgs() - 2);
1184 char *p = floatingStackp - offsetof(JSGenerator, floatingStack);
1185 return reinterpret_cast<JSGenerator *>(p);
1188 typedef enum JSGeneratorOp {
1189 JSGENOP_NEXT,
1190 JSGENOP_SEND,
1191 JSGENOP_THROW,
1192 JSGENOP_CLOSE
1193 } JSGeneratorOp;
1196 * Start newborn or restart yielding generator and perform the requested
1197 * operation inside its frame.
1199 static JS_REQUIRES_STACK JSBool
1200 SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj,
1201 JSGenerator *gen, const Value &arg)
1203 if (gen->state == JSGEN_RUNNING || gen->state == JSGEN_CLOSING) {
1204 js_ReportValueError(cx, JSMSG_NESTING_GENERATOR,
1205 JSDVG_SEARCH_STACK, ObjectOrNullValue(obj),
1206 JS_GetFunctionId(gen->floatingFrame()->fun()));
1207 return JS_FALSE;
1210 /* Check for OOM errors here, where we can fail easily. */
1211 if (!cx->ensureGeneratorStackSpace())
1212 return JS_FALSE;
1214 JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN);
1215 switch (op) {
1216 case JSGENOP_NEXT:
1217 case JSGENOP_SEND:
1218 if (gen->state == JSGEN_OPEN) {
1220 * Store the argument to send as the result of the yield
1221 * expression.
1223 gen->regs.sp[-1] = arg;
1225 gen->state = JSGEN_RUNNING;
1226 break;
1228 case JSGENOP_THROW:
1229 cx->setPendingException(arg);
1230 gen->state = JSGEN_RUNNING;
1231 break;
1233 default:
1234 JS_ASSERT(op == JSGENOP_CLOSE);
1235 cx->setPendingException(MagicValue(JS_GENERATOR_CLOSING));
1236 gen->state = JSGEN_CLOSING;
1237 break;
1240 JSStackFrame *genfp = gen->floatingFrame();
1241 Value *genvp = gen->floatingStack;
1242 uintN vplen = genfp->formalArgsEnd() - genvp;
1244 JSStackFrame *stackfp;
1245 Value *stackvp;
1246 JSBool ok;
1249 * Get a pointer to new frame/slots. This memory is not "claimed", so
1250 * the code before pushExecuteFrame must not reenter the interpreter.
1252 GeneratorFrameGuard frame;
1253 if (!cx->stack().getGeneratorFrame(cx, vplen, genfp->numSlots(), &frame)) {
1254 gen->state = JSGEN_CLOSED;
1255 return JS_FALSE;
1257 stackfp = frame.fp();
1258 stackvp = frame.vp();
1260 /* Copy frame onto the stack. */
1261 stackfp->stealFrameAndSlots(stackvp, genfp, genvp, gen->regs.sp);
1262 stackfp->resetGeneratorPrev(cx);
1263 stackfp->unsetFloatingGenerator();
1264 RebaseRegsFromTo(&gen->regs, genfp, stackfp);
1265 MUST_FLOW_THROUGH("restore");
1267 /* Officially push frame. frame's destructor pops. */
1268 cx->stack().pushGeneratorFrame(cx, &gen->regs, &frame);
1270 cx->enterGenerator(gen); /* OOM check above. */
1271 JSObject *enumerators = cx->enumerators;
1272 cx->enumerators = gen->enumerators;
1274 ok = RunScript(cx, stackfp->script(), stackfp);
1276 gen->enumerators = cx->enumerators;
1277 cx->enumerators = enumerators;
1278 cx->leaveGenerator(gen);
1281 * Copy the stack frame and rebase the regs, but not before popping
1282 * the stack, since cx->regs == &gen->regs.
1284 genfp->stealFrameAndSlots(genvp, stackfp, stackvp, gen->regs.sp);
1285 genfp->setFloatingGenerator();
1287 MUST_FLOW_LABEL(restore)
1288 RebaseRegsFromTo(&gen->regs, stackfp, genfp);
1290 if (gen->floatingFrame()->isYielding()) {
1291 /* Yield cannot fail, throw or be called on closing. */
1292 JS_ASSERT(ok);
1293 JS_ASSERT(!cx->isExceptionPending());
1294 JS_ASSERT(gen->state == JSGEN_RUNNING);
1295 JS_ASSERT(op != JSGENOP_CLOSE);
1296 genfp->clearYielding();
1297 gen->state = JSGEN_OPEN;
1298 return JS_TRUE;
1301 genfp->clearReturnValue();
1302 gen->state = JSGEN_CLOSED;
1303 if (ok) {
1304 /* Returned, explicitly or by falling off the end. */
1305 if (op == JSGENOP_CLOSE)
1306 return JS_TRUE;
1307 return js_ThrowStopIteration(cx);
1311 * An error, silent termination by operation callback or an exception.
1312 * Propagate the condition to the caller.
1314 return JS_FALSE;
1317 static JS_REQUIRES_STACK JSBool
1318 CloseGenerator(JSContext *cx, JSObject *obj)
1320 JS_ASSERT(obj->getClass() == &js_GeneratorClass);
1322 JSGenerator *gen = (JSGenerator *) obj->getPrivate();
1323 if (!gen) {
1324 /* Generator prototype object. */
1325 return JS_TRUE;
1328 if (gen->state == JSGEN_CLOSED)
1329 return JS_TRUE;
1331 return SendToGenerator(cx, JSGENOP_CLOSE, obj, gen, UndefinedValue());
1335 * Common subroutine of generator_(next|send|throw|close) methods.
1337 static JSBool
1338 generator_op(JSContext *cx, JSGeneratorOp op, Value *vp, uintN argc)
1340 LeaveTrace(cx);
1342 JSObject *obj = ToObject(cx, &vp[1]);
1343 if (!obj || !InstanceOf(cx, obj, &js_GeneratorClass, vp + 2))
1344 return JS_FALSE;
1346 JSGenerator *gen = (JSGenerator *) obj->getPrivate();
1347 if (!gen) {
1348 /* This happens when obj is the generator prototype. See bug 352885. */
1349 goto closed_generator;
1352 if (gen->state == JSGEN_NEWBORN) {
1353 switch (op) {
1354 case JSGENOP_NEXT:
1355 case JSGENOP_THROW:
1356 break;
1358 case JSGENOP_SEND:
1359 if (argc >= 1 && !vp[2].isUndefined()) {
1360 js_ReportValueError(cx, JSMSG_BAD_GENERATOR_SEND,
1361 JSDVG_SEARCH_STACK, vp[2], NULL);
1362 return JS_FALSE;
1364 break;
1366 default:
1367 JS_ASSERT(op == JSGENOP_CLOSE);
1368 gen->state = JSGEN_CLOSED;
1369 return JS_TRUE;
1371 } else if (gen->state == JSGEN_CLOSED) {
1372 closed_generator:
1373 switch (op) {
1374 case JSGENOP_NEXT:
1375 case JSGENOP_SEND:
1376 return js_ThrowStopIteration(cx);
1377 case JSGENOP_THROW:
1378 cx->setPendingException(argc >= 1 ? vp[2] : UndefinedValue());
1379 return JS_FALSE;
1380 default:
1381 JS_ASSERT(op == JSGENOP_CLOSE);
1382 return JS_TRUE;
1386 bool undef = ((op == JSGENOP_SEND || op == JSGENOP_THROW) && argc != 0);
1387 if (!SendToGenerator(cx, op, obj, gen, undef ? vp[2] : UndefinedValue()))
1388 return JS_FALSE;
1389 *vp = gen->floatingFrame()->returnValue();
1390 return JS_TRUE;
1393 static JSBool
1394 generator_send(JSContext *cx, uintN argc, Value *vp)
1396 return generator_op(cx, JSGENOP_SEND, vp, argc);
1399 static JSBool
1400 generator_next(JSContext *cx, uintN argc, Value *vp)
1402 return generator_op(cx, JSGENOP_NEXT, vp, argc);
1405 static JSBool
1406 generator_throw(JSContext *cx, uintN argc, Value *vp)
1408 return generator_op(cx, JSGENOP_THROW, vp, argc);
1411 static JSBool
1412 generator_close(JSContext *cx, uintN argc, Value *vp)
1414 return generator_op(cx, JSGENOP_CLOSE, vp, argc);
1417 static JSFunctionSpec generator_methods[] = {
1418 JS_FN(js_next_str, generator_next, 0,JSPROP_ROPERM),
1419 JS_FN(js_send_str, generator_send, 1,JSPROP_ROPERM),
1420 JS_FN(js_throw_str, generator_throw, 1,JSPROP_ROPERM),
1421 JS_FN(js_close_str, generator_close, 0,JSPROP_ROPERM),
1422 JS_FS_END
1425 #endif /* JS_HAS_GENERATORS */
1427 JSObject *
1428 js_InitIteratorClasses(JSContext *cx, JSObject *obj)
1430 JSObject *proto, *stop;
1432 /* Idempotency required: we initialize several things, possibly lazily. */
1433 if (!js_GetClassObject(cx, obj, JSProto_StopIteration, &stop))
1434 return NULL;
1435 if (stop)
1436 return stop;
1438 proto = js_InitClass(cx, obj, NULL, &js_IteratorClass, Iterator, 2,
1439 NULL, iterator_methods, NULL, NULL);
1440 if (!proto)
1441 return NULL;
1443 #if JS_HAS_GENERATORS
1444 /* Initialize the generator internals if configured. */
1445 if (!js_InitClass(cx, obj, NULL, &js_GeneratorClass, NULL, 0,
1446 NULL, generator_methods, NULL, NULL)) {
1447 return NULL;
1449 #endif
1451 return js_InitClass(cx, obj, NULL, &js_StopIterationClass, NULL, 0,
1452 NULL, NULL, NULL, NULL);