Bug 586358: make imacpc flagged. (r=lw)
[mozilla-central.git] / js / src / jsiter.cpp
blobd0af795fecd809b3d0390d363a518464dcb9a1b6
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 "jsobjinlines.h"
80 #include "jsstrinlines.h"
82 using namespace js;
84 static void iterator_finalize(JSContext *cx, JSObject *obj);
85 static void iterator_trace(JSTracer *trc, JSObject *obj);
86 static JSObject *iterator_iterator(JSContext *cx, JSObject *obj, JSBool keysonly);
88 Class js_IteratorClass = {
89 "Iterator",
90 JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator) | JSCLASS_MARK_IS_TRACE,
91 PropertyStub, /* addProperty */
92 PropertyStub, /* delProperty */
93 PropertyStub, /* getProperty */
94 PropertyStub, /* setProperty */
95 EnumerateStub,
96 ResolveStub,
97 ConvertStub,
98 iterator_finalize,
99 NULL, /* reserved */
100 NULL, /* checkAccess */
101 NULL, /* call */
102 NULL, /* construct */
103 NULL, /* xdrObject */
104 NULL, /* hasInstance */
105 JS_CLASS_TRACE(iterator_trace),
107 NULL, /* equality */
108 NULL, /* outerObject */
109 NULL, /* innerObject */
110 iterator_iterator,
111 NULL /* wrappedObject */
115 void
116 NativeIterator::mark(JSTracer *trc)
118 if (isKeyIter())
119 MarkIdRange(trc, beginKey(), endKey(), "props");
120 else
121 MarkValueRange(trc, beginValue(), endValue(), "props");
122 if (obj)
123 MarkObject(trc, obj, "obj");
127 * Shared code to close iterator's state either through an explicit call or
128 * when GC detects that the iterator is no longer reachable.
130 static void
131 iterator_finalize(JSContext *cx, JSObject *obj)
133 JS_ASSERT(obj->getClass() == &js_IteratorClass);
135 /* Avoid double work if the iterator was closed by JSOP_ENDITER. */
136 NativeIterator *ni = obj->getNativeIterator();
137 if (ni) {
138 cx->free(ni);
139 obj->setNativeIterator(NULL);
143 static void
144 iterator_trace(JSTracer *trc, JSObject *obj)
146 NativeIterator *ni = obj->getNativeIterator();
148 if (ni)
149 ni->mark(trc);
152 struct IdHashPolicy {
153 typedef jsid Lookup;
154 static HashNumber hash(jsid id) {
155 return JSID_BITS(id);
157 static bool match(jsid id1, jsid id2) {
158 return id1 == id2;
162 typedef HashSet<jsid, IdHashPolicy, ContextAllocPolicy> IdSet;
164 static inline bool
165 NewKeyValuePair(JSContext *cx, jsid id, const Value &val, Value *rval)
167 Value vec[2] = { IdToValue(id), val };
168 AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(vec), vec);
170 JSObject *aobj = js_NewArrayObject(cx, 2, vec);
171 if (!aobj)
172 return false;
173 rval->setObject(*aobj);
174 return true;
177 struct KeyEnumeration
179 typedef AutoIdVector ResultVector;
181 static JS_ALWAYS_INLINE bool
182 append(JSContext *, AutoIdVector &keys, JSObject *, jsid id, uintN flags)
184 JS_ASSERT((flags & JSITER_FOREACH) == 0);
185 return keys.append(id);
189 struct ValueEnumeration
191 typedef AutoValueVector ResultVector;
193 static JS_ALWAYS_INLINE bool
194 append(JSContext *cx, AutoValueVector &vals, JSObject *obj, jsid id, uintN flags)
196 JS_ASSERT(flags & JSITER_FOREACH);
198 if (!vals.growBy(1))
199 return false;
201 /* Do the lookup on the original object instead of the prototype. */
202 Value *vp = vals.end() - 1;
203 if (!obj->getProperty(cx, id, vp))
204 return false;
205 if ((flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, *vp, vp))
206 return false;
208 return true;
212 template <class EnumPolicy>
213 static inline bool
214 Enumerate(JSContext *cx, JSObject *obj, JSObject *pobj, jsid id,
215 bool enumerable, bool sharedPermanent, uintN flags, IdSet& ht,
216 typename EnumPolicy::ResultVector &props)
218 IdSet::AddPtr p = ht.lookupForAdd(id);
219 JS_ASSERT_IF(obj == pobj && !obj->isProxy(), !p);
221 /* If we've already seen this, we definitely won't add it. */
222 if (JS_UNLIKELY(!!p))
223 return true;
226 * It's not necessary to add properties to the hash table at the end of the
227 * prototype chain -- but a proxy might return duplicated properties, so
228 * always add for them.
230 if ((pobj->getProto() || pobj->isProxy()) && !ht.add(p, id))
231 return false;
233 if (JS_UNLIKELY(flags & JSITER_OWNONLY)) {
235 * Shared-permanent hack: If this property is shared permanent
236 * and pobj and obj have the same class, then treat it as an own
237 * property of obj, even if pobj != obj. (But see bug 575997.)
239 * Omit the magic __proto__ property so that JS code can use
240 * Object.getOwnPropertyNames without worrying about it.
242 if (!pobj->getProto() && id == ATOM_TO_JSID(cx->runtime->atomState.protoAtom))
243 return true;
244 if (pobj != obj && !(sharedPermanent && pobj->getClass() == obj->getClass()))
245 return true;
248 if (enumerable || (flags & JSITER_HIDDEN))
249 return EnumPolicy::append(cx, props, obj, id, flags);
251 return true;
254 template <class EnumPolicy>
255 static bool
256 EnumerateNativeProperties(JSContext *cx, JSObject *obj, JSObject *pobj, uintN flags, IdSet &ht,
257 typename EnumPolicy::ResultVector &props)
259 JS_LOCK_OBJ(cx, pobj);
261 size_t initialLength = props.length();
263 /* Collect all unique properties from this object's scope. */
264 JSScope *scope = pobj->scope();
265 for (JSScopeProperty *sprop = scope->lastProperty(); sprop; sprop = sprop->parent) {
266 if (!JSID_IS_DEFAULT_XML_NAMESPACE(sprop->id) &&
267 !sprop->isAlias() &&
268 !Enumerate<EnumPolicy>(cx, obj, pobj, sprop->id, sprop->enumerable(), sprop->isSharedPermanent(),
269 flags, ht, props))
271 return false;
275 Reverse(props.begin() + initialLength, props.end());
277 JS_UNLOCK_SCOPE(cx, scope);
278 return true;
281 template <class EnumPolicy>
282 static bool
283 EnumerateDenseArrayProperties(JSContext *cx, JSObject *obj, JSObject *pobj, uintN flags,
284 IdSet &ht, typename EnumPolicy::ResultVector &props)
286 if (!Enumerate<EnumPolicy>(cx, obj, pobj, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), false, true,
287 flags, ht, props)) {
288 return false;
291 if (pobj->getArrayLength() > 0) {
292 size_t capacity = pobj->getDenseArrayCapacity();
293 Value *vp = pobj->dslots;
294 for (size_t i = 0; i < capacity; ++i, ++vp) {
295 if (!vp->isMagic(JS_ARRAY_HOLE)) {
296 /* Dense arrays never get so large that i would not fit into an integer id. */
297 if (!Enumerate<EnumPolicy>(cx, obj, pobj, INT_TO_JSID(i), true, false, flags, ht, props))
298 return false;
303 return true;
306 template <class EnumPolicy>
307 static bool
308 Snapshot(JSContext *cx, JSObject *obj, uintN flags, typename EnumPolicy::ResultVector &props)
311 * FIXME: Bug 575997 - We won't need to initialize this hash table if
312 * (flags & JSITER_OWNONLY) when we eliminate inheritance of
313 * shared-permanent properties as own properties.
315 IdSet ht(cx);
316 if (!ht.init(32))
317 return NULL;
319 JSObject *pobj = obj;
320 do {
321 Class *clasp = pobj->getClass();
322 if (pobj->isNative() &&
323 !pobj->getOps()->enumerate &&
324 !(clasp->flags & JSCLASS_NEW_ENUMERATE)) {
325 if (!clasp->enumerate(cx, pobj))
326 return false;
327 if (!EnumerateNativeProperties<EnumPolicy>(cx, obj, pobj, flags, ht, props))
328 return false;
329 } else if (pobj->isDenseArray()) {
330 if (!EnumerateDenseArrayProperties<EnumPolicy>(cx, obj, pobj, flags, ht, props))
331 return false;
332 } else {
333 if (pobj->isProxy()) {
334 AutoIdVector proxyProps(cx);
335 if (flags & JSITER_OWNONLY) {
336 if (!JSProxy::enumerateOwn(cx, pobj, proxyProps))
337 return false;
338 } else {
339 if (!JSProxy::enumerate(cx, pobj, proxyProps))
340 return false;
342 for (size_t n = 0, len = proxyProps.length(); n < len; n++) {
343 if (!Enumerate<EnumPolicy>(cx, obj, pobj, proxyProps[n], true, false, flags, ht, props))
344 return false;
346 /* Proxy objects enumerate the prototype on their own, so we are done here. */
347 break;
349 Value state;
350 JSIterateOp op = (flags & JSITER_HIDDEN) ? JSENUMERATE_INIT_ALL : JSENUMERATE_INIT;
351 if (!pobj->enumerate(cx, op, &state, NULL))
352 return false;
353 if (state.isMagic(JS_NATIVE_ENUMERATE)) {
354 if (!EnumerateNativeProperties<EnumPolicy>(cx, obj, pobj, flags, ht, props))
355 return false;
356 } else {
357 while (true) {
358 jsid id;
359 if (!pobj->enumerate(cx, JSENUMERATE_NEXT, &state, &id))
360 return false;
361 if (state.isNull())
362 break;
363 if (!Enumerate<EnumPolicy>(cx, obj, pobj, id, true, false, flags, ht, props))
364 return false;
369 if (JS_UNLIKELY(pobj->isXML()))
370 break;
371 } while ((pobj = pobj->getProto()) != NULL);
373 return true;
376 bool
377 VectorToIdArray(JSContext *cx, AutoIdVector &props, JSIdArray **idap)
379 JS_STATIC_ASSERT(sizeof(JSIdArray) > sizeof(jsid));
380 size_t len = props.length();
381 size_t idsz = len * sizeof(jsid);
382 size_t sz = (sizeof(JSIdArray) - sizeof(jsid)) + idsz;
383 JSIdArray *ida = static_cast<JSIdArray *>(cx->malloc(sz));
384 if (!ida)
385 return false;
387 ida->length = static_cast<jsint>(len);
388 memcpy(ida->vector, props.begin(), idsz);
389 *idap = ida;
390 return true;
393 bool
394 GetPropertyNames(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector &props)
396 return Snapshot<KeyEnumeration>(cx, obj, flags & (JSITER_OWNONLY | JSITER_HIDDEN), props);
399 static inline bool
400 GetCustomIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp)
402 /* Check whether we have a valid __iterator__ method. */
403 JSAtom *atom = cx->runtime->atomState.iteratorAtom;
404 if (!js_GetMethod(cx, obj, ATOM_TO_JSID(atom), JSGET_NO_METHOD_BARRIER, vp))
405 return false;
407 /* If there is no custom __iterator__ method, we are done here. */
408 if (vp->isUndefined())
409 return true;
411 /* Otherwise call it and return that object. */
412 LeaveTrace(cx);
413 Value arg = BooleanValue((flags & JSITER_FOREACH) == 0);
414 if (!InternalCall(cx, obj, *vp, 1, &arg, vp))
415 return false;
416 if (vp->isPrimitive()) {
418 * We are always coming from js_ValueToIterator, and we are no longer on
419 * trace, so the object we are iterating over is on top of the stack (-1).
421 js_ReportValueError2(cx, JSMSG_BAD_TRAP_RETURN_VALUE,
422 -1, ObjectValue(*obj), NULL,
423 js_AtomToPrintableString(cx, atom));
424 return false;
426 return true;
429 template <typename T>
430 static inline bool
431 Compare(T *a, T *b, size_t c)
433 size_t n = (c + size_t(7)) / size_t(8);
434 switch (c % 8) {
435 case 0: do { if (*a++ != *b++) return false;
436 case 7: if (*a++ != *b++) return false;
437 case 6: if (*a++ != *b++) return false;
438 case 5: if (*a++ != *b++) return false;
439 case 4: if (*a++ != *b++) return false;
440 case 3: if (*a++ != *b++) return false;
441 case 2: if (*a++ != *b++) return false;
442 case 1: if (*a++ != *b++) return false;
443 } while (--n > 0);
445 return true;
448 static inline JSObject *
449 NewIteratorObject(JSContext *cx, uintN flags)
451 if (flags & JSITER_ENUMERATE) {
453 * Non-escaping native enumerator objects do not need map, proto, or
454 * parent. However, code in jstracer.cpp and elsewhere may find such a
455 * native enumerator object via the stack and (as for all objects that
456 * are not stillborn, with the exception of "NoSuchMethod" internal
457 * helper objects) expect it to have a non-null map pointer, so we
458 * share an empty Enumerator scope in the runtime.
460 JSObject *obj = js_NewGCObject(cx);
461 if (!obj)
462 return false;
463 obj->map = cx->runtime->emptyEnumeratorScope->hold();
464 obj->init(&js_IteratorClass, NULL, NULL, NullValue());
465 return obj;
468 return NewBuiltinClassInstance(cx, &js_IteratorClass);
471 NativeIterator *
472 NativeIterator::allocateKeyIterator(JSContext *cx, uint32 slength, const AutoIdVector &props)
474 size_t plength = props.length();
475 NativeIterator *ni = (NativeIterator *)
476 cx->malloc(sizeof(NativeIterator) + plength * sizeof(jsid) + slength * sizeof(uint32));
477 if (!ni)
478 return NULL;
479 ni->props_array = ni->props_cursor = (jsid *) (ni + 1);
480 ni->props_end = (jsid *)ni->props_array + plength;
481 if (plength)
482 memcpy(ni->props_array, props.begin(), plength * sizeof(jsid));
483 return ni;
486 NativeIterator *
487 NativeIterator::allocateValueIterator(JSContext *cx, uint32 slength, const AutoValueVector &props)
489 size_t plength = props.length();
490 NativeIterator *ni = (NativeIterator *)
491 cx->malloc(sizeof(NativeIterator) + plength * sizeof(Value) + slength * sizeof(uint32));
492 if (!ni)
493 return NULL;
494 ni->props_array = ni->props_cursor = (Value *) (ni + 1);
495 ni->props_end = (Value *)ni->props_array + plength;
496 if (plength)
497 memcpy(ni->props_array, props.begin(), plength * sizeof(Value));
498 return ni;
501 inline void
502 NativeIterator::init(JSObject *obj, uintN flags, const uint32 *sarray, uint32 slength, uint32 key)
504 this->obj = obj;
505 this->flags = flags;
506 this->shapes_array = (uint32 *) this->props_end;
507 this->shapes_length = slength;
508 this->shapes_key = key;
509 if (slength)
510 memcpy(this->shapes_array, sarray, slength * sizeof(uint32));
513 static inline void
514 RegisterEnumerator(JSContext *cx, JSObject *iterobj, NativeIterator *ni)
516 /* Register non-escaping native enumerators (for-in) with the current context. */
517 if (ni->flags & JSITER_ENUMERATE) {
518 ni->next = cx->enumerators;
519 cx->enumerators = iterobj;
523 static inline bool
524 VectorToKeyIterator(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector &keys,
525 const uint32 *sarray, uint32 slength, uint32 key, Value *vp)
527 JS_ASSERT(!(flags & JSITER_FOREACH));
529 JSObject *iterobj = NewIteratorObject(cx, flags);
530 if (!iterobj)
531 return false;
534 NativeIterator *ni = NativeIterator::allocateKeyIterator(cx, slength, keys);
535 if (!ni)
536 return NULL;
537 ni->init(obj, flags, sarray, slength, key);
539 iterobj->setNativeIterator(ni);
540 vp->setObject(*iterobj);
542 RegisterEnumerator(cx, iterobj, ni);
543 return true;
546 bool
547 VectorToKeyIterator(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector &props, Value *vp)
549 return VectorToKeyIterator(cx, obj, flags, props, NULL, 0, 0, vp);
552 static inline bool
553 VectorToValueIterator(JSContext *cx, JSObject *obj, uintN flags, AutoValueVector &vals,
554 const uint32 *sarray, uint32 slength, uint32 key, Value *vp)
556 JS_ASSERT(flags & JSITER_FOREACH);
558 JSObject *iterobj = NewIteratorObject(cx, flags);
559 if (!iterobj)
560 return false;
562 NativeIterator *ni = NativeIterator::allocateValueIterator(cx, slength, vals);
563 if (!ni)
564 return NULL;
565 ni->init(obj, flags, sarray, slength, key);
567 iterobj->setNativeIterator(ni);
568 vp->setObject(*iterobj);
570 RegisterEnumerator(cx, iterobj, ni);
571 return true;
574 bool
575 VectorToValueIterator(JSContext *cx, JSObject *obj, uintN flags, AutoValueVector &props, Value *vp)
577 return VectorToValueIterator(cx, obj, flags, props, NULL, 0, 0, vp);
580 bool
581 EnumeratedIdVectorToIterator(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector &props, Value *vp)
583 if (!(flags & JSITER_FOREACH))
584 return VectorToKeyIterator(cx, obj, flags, props, vp);
586 /* For for-each iteration, we need to look up the value of each id. */
588 size_t plength = props.length();
590 AutoValueVector vals(cx);
591 if (!vals.reserve(plength))
592 return NULL;
594 for (size_t i = 0; i < plength; ++i) {
595 if (!ValueEnumeration::append(cx, vals, obj, props[i], flags))
596 return false;
599 return VectorToValueIterator(cx, obj, flags, vals, vp);
602 typedef Vector<uint32, 8> ShapeVector;
604 bool
605 GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp)
607 uint32 hash;
608 JSObject **hp;
609 Vector<uint32, 8> shapes(cx);
610 uint32 key = 0;
612 bool keysOnly = (flags == JSITER_ENUMERATE);
614 if (obj) {
615 if (keysOnly) {
617 * The iterator object for JSITER_ENUMERATE never escapes, so we
618 * don't care for the proper parent/proto to be set. This also
619 * allows us to re-use a previous iterator object that was freed
620 * by JSOP_ENDITER.
622 JSObject *pobj = obj;
623 do {
624 if (!pobj->isNative() ||
625 obj->getOps()->enumerate ||
626 pobj->getClass()->enumerate != JS_EnumerateStub) {
627 shapes.clear();
628 goto miss;
630 uint32 shape = pobj->shape();
631 key = (key + (key << 16)) ^ shape;
632 if (!shapes.append(shape))
633 return false;
634 pobj = pobj->getProto();
635 } while (pobj);
637 hash = key % JS_ARRAY_LENGTH(JS_THREAD_DATA(cx)->cachedNativeIterators);
638 hp = &JS_THREAD_DATA(cx)->cachedNativeIterators[hash];
639 JSObject *iterobj = *hp;
640 if (iterobj) {
641 NativeIterator *ni = iterobj->getNativeIterator();
642 if (ni->shapes_key == key &&
643 ni->shapes_length == shapes.length() &&
644 Compare(ni->shapes_array, shapes.begin(), ni->shapes_length)) {
645 vp->setObject(*iterobj);
646 *hp = ni->next;
648 RegisterEnumerator(cx, iterobj, ni);
649 return true;
654 miss:
655 if (obj->isProxy())
656 return JSProxy::iterate(cx, obj, flags, vp);
657 if (!GetCustomIterator(cx, obj, flags, vp))
658 return false;
659 if (!vp->isUndefined())
660 return true;
663 /* NB: for (var p in null) succeeds by iterating over no properties. */
665 if (flags & JSITER_FOREACH) {
666 AutoValueVector vals(cx);
667 if (JS_LIKELY(obj != NULL) && !Snapshot<ValueEnumeration>(cx, obj, flags, vals))
668 return false;
669 return VectorToValueIterator(cx, obj, flags, vals, shapes.begin(), shapes.length(), key, vp);
672 AutoIdVector keys(cx);
673 if (JS_LIKELY(obj != NULL) && !Snapshot<KeyEnumeration>(cx, obj, flags, keys))
674 return false;
675 return VectorToKeyIterator(cx, obj, flags, keys, shapes.begin(), shapes.length(), key, vp);
678 static JSObject *
679 iterator_iterator(JSContext *cx, JSObject *obj, JSBool keysonly)
681 return obj;
684 static JSBool
685 Iterator(JSContext *cx, JSObject *iterobj, uintN argc, Value *argv, Value *rval)
687 JSBool keyonly;
688 uintN flags;
690 keyonly = js_ValueToBoolean(argv[1]);
691 flags = JSITER_OWNONLY | (keyonly ? 0 : (JSITER_FOREACH | JSITER_KEYVALUE));
692 *rval = argv[0];
693 return js_ValueToIterator(cx, flags, rval);
696 JSBool
697 js_ThrowStopIteration(JSContext *cx)
699 Value v;
701 JS_ASSERT(!JS_IsExceptionPending(cx));
702 if (js_FindClassObject(cx, NULL, JSProto_StopIteration, &v))
703 SetPendingException(cx, v);
704 return JS_FALSE;
707 static JSBool
708 iterator_next(JSContext *cx, uintN argc, Value *vp)
710 JSObject *obj;
712 obj = ComputeThisFromVp(cx, vp);
713 if (!InstanceOf(cx, obj, &js_IteratorClass, vp + 2))
714 return false;
716 if (!js_IteratorMore(cx, obj, vp))
717 return false;
718 if (!vp->toBoolean()) {
719 js_ThrowStopIteration(cx);
720 return false;
722 return js_IteratorNext(cx, obj, vp);
725 #define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT)
727 static JSFunctionSpec iterator_methods[] = {
728 JS_FN(js_next_str, iterator_next, 0,JSPROP_ROPERM),
729 JS_FS_END
733 * Call ToObject(v).__iterator__(keyonly) if ToObject(v).__iterator__ exists.
734 * Otherwise construct the default iterator.
736 JS_FRIEND_API(JSBool)
737 js_ValueToIterator(JSContext *cx, uintN flags, Value *vp)
739 /* JSITER_KEYVALUE must always come with JSITER_FOREACH */
740 JS_ASSERT_IF(flags & JSITER_KEYVALUE, flags & JSITER_FOREACH);
743 * Make sure the more/next state machine doesn't get stuck. A value might be
744 * left in iterValue when a trace is left due to an operation time-out after
745 * JSOP_MOREITER but before the value is picked up by FOR*.
747 cx->iterValue.setMagic(JS_NO_ITER_VALUE);
749 JSObject *obj;
750 if (vp->isObject()) {
751 /* Common case. */
752 obj = &vp->toObject();
753 } else {
755 * Enumerating over null and undefined gives an empty enumerator.
756 * This is contrary to ECMA-262 9.9 ToObject, invoked from step 3 of
757 * the first production in 12.6.4 and step 4 of the second production,
758 * but it's "web JS" compatible. ES5 fixed for-in to match this de-facto
759 * standard.
761 if ((flags & JSITER_ENUMERATE)) {
762 if (!js_ValueToObjectOrNull(cx, *vp, &obj))
763 return false;
764 if (!obj)
765 return GetIterator(cx, NULL, flags, vp);
766 } else {
767 obj = js_ValueToNonNullObject(cx, *vp);
768 if (!obj)
769 return false;
773 AutoObjectRooter tvr(cx, obj);
775 /* Enumerate Iterator.prototype directly. */
776 JSIteratorOp op = obj->getClass()->ext.iteratorObject;
777 if (op && (obj->getClass() != &js_IteratorClass || obj->getNativeIterator())) {
778 JSObject *iterobj = op(cx, obj, !(flags & JSITER_FOREACH));
779 if (!iterobj)
780 return false;
781 vp->setObject(*iterobj);
782 return true;
785 return GetIterator(cx, obj, flags, vp);
788 #if JS_HAS_GENERATORS
789 static JS_REQUIRES_STACK JSBool
790 CloseGenerator(JSContext *cx, JSObject *genobj);
791 #endif
793 JS_FRIEND_API(JSBool)
794 js_CloseIterator(JSContext *cx, JSObject *obj)
796 cx->iterValue.setMagic(JS_NO_ITER_VALUE);
798 Class *clasp = obj->getClass();
799 if (clasp == &js_IteratorClass) {
800 /* Remove enumerators from the active list, which is a stack. */
801 NativeIterator *ni = obj->getNativeIterator();
802 if (ni->flags & JSITER_ENUMERATE) {
803 JS_ASSERT(cx->enumerators == obj);
804 cx->enumerators = ni->next;
807 /* Cache the iterator object if possible. */
808 if (ni->shapes_length) {
809 uint32 hash = ni->shapes_key % NATIVE_ITER_CACHE_SIZE;
810 JSObject **hp = &JS_THREAD_DATA(cx)->cachedNativeIterators[hash];
811 ni->props_cursor = ni->props_array;
812 ni->next = *hp;
813 *hp = obj;
814 } else {
815 iterator_finalize(cx, obj);
818 #if JS_HAS_GENERATORS
819 else if (clasp == &js_GeneratorClass) {
820 return CloseGenerator(cx, obj);
822 #endif
823 return JS_TRUE;
827 * Suppress enumeration of deleted properties. We maintain a list of all active
828 * non-escaping for-in enumerators. Whenever a property is deleted, we check
829 * whether any active enumerator contains the (obj, id) pair and has not
830 * enumerated id yet. If so, we delete the id from the list (or advance the
831 * cursor if it is the next id to be enumerated).
833 * We do not suppress enumeration of a property deleted along an object's
834 * prototype chain. Only direct deletions on the object are handled.
836 bool
837 js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id)
839 JSObject *iterobj = cx->enumerators;
840 while (iterobj) {
841 again:
842 NativeIterator *ni = iterobj->getNativeIterator();
843 /* This only works for identified surpressed keys, not values. */
844 if (ni->isKeyIter() && ni->obj == obj && ni->props_cursor < ni->props_end) {
845 /* Check whether id is still to come. */
846 jsid *props_cursor = ni->currentKey();
847 jsid *props_end = ni->endKey();
848 for (jsid *idp = props_cursor; idp < props_end; ++idp) {
849 if (*idp == id) {
851 * Check whether another property along the prototype chain
852 * became visible as a result of this deletion.
854 if (obj->getProto()) {
855 AutoObjectRooter proto(cx, obj->getProto());
856 AutoObjectRooter obj2(cx);
857 JSProperty *prop;
858 if (!proto.object()->lookupProperty(cx, id, obj2.addr(), &prop))
859 return false;
860 if (prop) {
861 uintN attrs;
862 if (obj2.object()->isNative()) {
863 attrs = ((JSScopeProperty *) prop)->attributes();
864 JS_UNLOCK_OBJ(cx, obj2.object());
865 } else if (!obj2.object()->getAttributes(cx, id, &attrs)) {
866 return false;
868 if (attrs & JSPROP_ENUMERATE)
869 continue;
874 * If lookupProperty or getAttributes above removed a property from
875 * ni, start over.
877 if (props_end != ni->props_end || props_cursor != ni->props_cursor)
878 goto again;
881 * No property along the prototype chain steppeded in to take the
882 * property's place, so go ahead and delete id from the list.
883 * If it is the next property to be enumerated, just skip it.
885 if (idp == props_cursor) {
886 ni->incKeyCursor();
887 } else {
888 memmove(idp, idp + 1, (props_end - (idp + 1)) * sizeof(jsid));
889 ni->props_end = ni->endKey() - 1;
891 break;
895 iterobj = ni->next;
897 return true;
900 JSBool
901 js_IteratorMore(JSContext *cx, JSObject *iterobj, Value *rval)
903 /* Fast path for native iterators */
904 if (iterobj->getClass() == &js_IteratorClass) {
906 * Implement next directly as all the methods of native iterator are
907 * read-only and permanent.
909 NativeIterator *ni = iterobj->getNativeIterator();
910 rval->setBoolean(ni->props_cursor < ni->props_end);
911 return true;
914 /* We might still have a pending value. */
915 if (!cx->iterValue.isMagic(JS_NO_ITER_VALUE)) {
916 rval->setBoolean(true);
917 return true;
920 /* Fetch and cache the next value from the iterator. */
921 jsid id = ATOM_TO_JSID(cx->runtime->atomState.nextAtom);
922 if (!js_GetMethod(cx, iterobj, id, JSGET_METHOD_BARRIER, rval))
923 return false;
924 if (!InternalCall(cx, iterobj, *rval, 0, NULL, rval)) {
925 /* Check for StopIteration. */
926 if (!cx->throwing || !js_ValueIsStopIteration(cx->exception))
927 return false;
929 /* Inline JS_ClearPendingException(cx). */
930 cx->throwing = JS_FALSE;
931 cx->exception.setUndefined();
932 cx->iterValue.setMagic(JS_NO_ITER_VALUE);
933 rval->setBoolean(false);
934 return true;
937 /* Cache the value returned by iterobj.next() so js_IteratorNext() can find it. */
938 JS_ASSERT(!rval->isMagic(JS_NO_ITER_VALUE));
939 cx->iterValue = *rval;
940 rval->setBoolean(true);
941 return true;
944 JSBool
945 js_IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval)
947 /* Fast path for native iterators */
948 if (iterobj->getClass() == &js_IteratorClass) {
950 * Implement next directly as all the methods of the native iterator are
951 * read-only and permanent.
953 NativeIterator *ni = iterobj->getNativeIterator();
954 JS_ASSERT(ni->props_cursor < ni->props_end);
955 if (ni->isKeyIter()) {
956 *rval = IdToValue(*ni->currentKey());
957 ni->incKeyCursor();
958 } else {
959 *rval = *ni->currentValue();
960 ni->incValueCursor();
963 if (rval->isString() || !ni->isKeyIter())
964 return true;
966 JSString *str;
967 jsint i;
968 if (rval->isInt32() && (jsuint(i = rval->toInt32()) < INT_STRING_LIMIT)) {
969 str = JSString::intString(i);
970 } else {
971 str = js_ValueToString(cx, *rval);
972 if (!str)
973 return false;
976 rval->setString(str);
977 return true;
980 JS_ASSERT(!cx->iterValue.isMagic(JS_NO_ITER_VALUE));
981 *rval = cx->iterValue;
982 cx->iterValue.setMagic(JS_NO_ITER_VALUE);
984 return true;
987 static JSBool
988 stopiter_hasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp)
990 *bp = js_ValueIsStopIteration(*v);
991 return JS_TRUE;
994 Class js_StopIterationClass = {
995 js_StopIteration_str,
996 JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration),
997 PropertyStub, /* addProperty */
998 PropertyStub, /* delProperty */
999 PropertyStub, /* getProperty */
1000 PropertyStub, /* setProperty */
1001 EnumerateStub,
1002 ResolveStub,
1003 ConvertStub,
1004 NULL, /* finalize */
1005 NULL, /* reserved0 */
1006 NULL, /* checkAccess */
1007 NULL, /* call */
1008 NULL, /* construct */
1009 NULL, /* xdrObject */
1010 stopiter_hasInstance
1013 #if JS_HAS_GENERATORS
1015 static void
1016 generator_finalize(JSContext *cx, JSObject *obj)
1018 JSGenerator *gen = (JSGenerator *) obj->getPrivate();
1019 if (!gen)
1020 return;
1023 * gen is open when a script has not called its close method while
1024 * explicitly manipulating it.
1026 JS_ASSERT(gen->state == JSGEN_NEWBORN ||
1027 gen->state == JSGEN_CLOSED ||
1028 gen->state == JSGEN_OPEN);
1029 cx->free(gen);
1032 static void
1033 generator_trace(JSTracer *trc, JSObject *obj)
1035 JSGenerator *gen = (JSGenerator *) obj->getPrivate();
1036 if (!gen)
1037 return;
1040 * Do not mark if the generator is running; the contents may be trash and
1041 * will be replaced when the generator stops.
1043 if (gen->state == JSGEN_RUNNING || gen->state == JSGEN_CLOSING)
1044 return;
1046 JSStackFrame *fp = gen->getFloatingFrame();
1047 JS_ASSERT(gen->getLiveFrame() == fp);
1048 MarkValueRange(trc, gen->floatingStack, fp->argEnd(), "generator slots");
1049 js_TraceStackFrame(trc, fp);
1050 MarkValueRange(trc, fp->slots(), gen->savedRegs.sp, "generator slots");
1053 Class js_GeneratorClass = {
1054 js_Generator_str,
1055 JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Generator) |
1056 JSCLASS_IS_ANONYMOUS | JSCLASS_MARK_IS_TRACE,
1057 PropertyStub, /* addProperty */
1058 PropertyStub, /* delProperty */
1059 PropertyStub, /* getProperty */
1060 PropertyStub, /* setProperty */
1061 EnumerateStub,
1062 ResolveStub,
1063 ConvertStub,
1064 generator_finalize,
1065 NULL, /* reserved */
1066 NULL, /* checkAccess */
1067 NULL, /* call */
1068 NULL, /* construct */
1069 NULL, /* xdrObject */
1070 NULL, /* hasInstance */
1071 JS_CLASS_TRACE(generator_trace),
1073 NULL, /* equality */
1074 NULL, /* outerObject */
1075 NULL, /* innerObject */
1076 iterator_iterator,
1077 NULL, /* wrappedObject */
1082 * Called from the JSOP_GENERATOR case in the interpreter, with fp referring
1083 * to the frame by which the generator function was activated. Create a new
1084 * JSGenerator object, which contains its own JSStackFrame that we populate
1085 * from *fp. We know that upon return, the JSOP_GENERATOR opcode will return
1086 * from the activation in fp, so we can steal away fp->callobj and fp->argsobj
1087 * if they are non-null.
1089 JS_REQUIRES_STACK JSObject *
1090 js_NewGenerator(JSContext *cx)
1092 JSObject *obj = NewBuiltinClassInstance(cx, &js_GeneratorClass);
1093 if (!obj)
1094 return NULL;
1096 /* Load and compute stack slot counts. */
1097 JSStackFrame *fp = cx->fp;
1098 uintN argc = fp->argc;
1099 uintN nargs = JS_MAX(argc, fp->fun->nargs);
1100 uintN vplen = 2 + nargs;
1102 /* Compute JSGenerator size. */
1103 uintN nbytes = sizeof(JSGenerator) +
1104 (-1 + /* one Value included in JSGenerator */
1105 vplen +
1106 VALUES_PER_STACK_FRAME +
1107 fp->script->nslots) * sizeof(Value);
1109 JSGenerator *gen = (JSGenerator *) cx->malloc(nbytes);
1110 if (!gen)
1111 return NULL;
1113 /* Cut up floatingStack space. */
1114 Value *vp = gen->floatingStack;
1115 JSStackFrame *newfp = reinterpret_cast<JSStackFrame *>(vp + vplen);
1116 Value *slots = newfp->slots();
1118 /* Initialize JSGenerator. */
1119 gen->obj = obj;
1120 gen->state = JSGEN_NEWBORN;
1121 gen->savedRegs.pc = cx->regs->pc;
1122 JS_ASSERT(cx->regs->sp == fp->slots() + fp->script->nfixed);
1123 gen->savedRegs.sp = slots + fp->script->nfixed;
1124 gen->vplen = vplen;
1125 gen->enumerators = NULL;
1126 gen->liveFrame = newfp;
1128 /* Copy generator's stack frame copy in from |cx->fp|. */
1129 newfp->setCallObj(fp->maybeCallObj());
1130 if (fp->hasCallObj()) { /* Steal call object. */
1131 fp->getCallObj()->setPrivate(newfp);
1132 fp->setCallObj(NULL);
1134 newfp->setArgsObj(fp->maybeArgsObj());
1135 if (fp->hasArgsObj()) { /* Steal args object. */
1136 fp->getArgsObj()->setPrivate(newfp);
1137 fp->setArgsObj(NULL);
1139 newfp->script = fp->script;
1140 newfp->fun = fp->fun;
1141 newfp->thisv = fp->thisv;
1142 newfp->argc = fp->argc;
1143 newfp->argv = vp + 2;
1144 newfp->rval = fp->rval;
1145 newfp->setAnnotation(NULL);
1146 newfp->setScopeChain(fp->maybeScopeChain());
1147 JS_ASSERT(!fp->hasBlockChain());
1148 newfp->setBlockChain(NULL);
1149 newfp->flags = fp->flags | JSFRAME_GENERATOR | JSFRAME_FLOATING_GENERATOR;
1150 JS_ASSERT(!newfp->hasIMacroPC());
1152 /* Copy in arguments and slots. */
1153 memcpy(vp, fp->argv - 2, vplen * sizeof(Value));
1154 memcpy(slots, fp->slots(), fp->script->nfixed * sizeof(Value));
1156 obj->setPrivate(gen);
1157 return obj;
1160 JSGenerator *
1161 js_FloatingFrameToGenerator(JSStackFrame *fp)
1163 JS_ASSERT(fp->isGenerator() && fp->isFloatingGenerator());
1164 char *floatingStackp = (char *)(fp->argv - 2);
1165 char *p = floatingStackp - offsetof(JSGenerator, floatingStack);
1166 return reinterpret_cast<JSGenerator *>(p);
1169 typedef enum JSGeneratorOp {
1170 JSGENOP_NEXT,
1171 JSGENOP_SEND,
1172 JSGENOP_THROW,
1173 JSGENOP_CLOSE
1174 } JSGeneratorOp;
1177 * Start newborn or restart yielding generator and perform the requested
1178 * operation inside its frame.
1180 static JS_REQUIRES_STACK JSBool
1181 SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj,
1182 JSGenerator *gen, const Value &arg)
1184 if (gen->state == JSGEN_RUNNING || gen->state == JSGEN_CLOSING) {
1185 js_ReportValueError(cx, JSMSG_NESTING_GENERATOR,
1186 JSDVG_SEARCH_STACK, ObjectOrNullValue(obj),
1187 JS_GetFunctionId(gen->getFloatingFrame()->fun));
1188 return JS_FALSE;
1191 /* Check for OOM errors here, where we can fail easily. */
1192 if (!cx->ensureGeneratorStackSpace())
1193 return JS_FALSE;
1195 JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN);
1196 switch (op) {
1197 case JSGENOP_NEXT:
1198 case JSGENOP_SEND:
1199 if (gen->state == JSGEN_OPEN) {
1201 * Store the argument to send as the result of the yield
1202 * expression.
1204 gen->savedRegs.sp[-1] = arg;
1206 gen->state = JSGEN_RUNNING;
1207 break;
1209 case JSGENOP_THROW:
1210 SetPendingException(cx, arg);
1211 gen->state = JSGEN_RUNNING;
1212 break;
1214 default:
1215 JS_ASSERT(op == JSGENOP_CLOSE);
1216 SetPendingException(cx, MagicValue(JS_GENERATOR_CLOSING));
1217 gen->state = JSGEN_CLOSING;
1218 break;
1221 JSStackFrame *genfp = gen->getFloatingFrame();
1222 JSBool ok;
1224 Value *genVp = gen->floatingStack;
1225 uintN vplen = gen->vplen;
1226 uintN nfixed = genfp->script->nslots;
1229 * Get a pointer to new frame/slots. This memory is not "claimed", so
1230 * the code before pushExecuteFrame must not reenter the interpreter.
1232 ExecuteFrameGuard frame;
1233 if (!cx->stack().getExecuteFrame(cx, cx->fp, vplen, nfixed, frame)) {
1234 gen->state = JSGEN_CLOSED;
1235 return JS_FALSE;
1238 Value *vp = frame.getvp();
1239 JSStackFrame *fp = frame.getFrame();
1242 * Copy and rebase stack frame/args/slots. The "floating" flag must
1243 * only be set on the generator's frame. See args_or_call_trace.
1245 uintN usedBefore = gen->savedRegs.sp - genVp;
1246 memcpy(vp, genVp, usedBefore * sizeof(Value));
1247 fp->flags &= ~JSFRAME_FLOATING_GENERATOR;
1248 fp->argv = vp + 2;
1249 gen->savedRegs.sp = fp->slots() + (gen->savedRegs.sp - genfp->slots());
1250 JS_ASSERT(uintN(gen->savedRegs.sp - fp->slots()) <= fp->script->nslots);
1252 #ifdef DEBUG
1253 JSObject *callobjBefore = fp->maybeCallObj();
1254 JSObject *argsobjBefore = fp->maybeArgsObj();
1255 #endif
1258 * Repoint Call, Arguments, Block and With objects to the new live
1259 * frame. Call and Arguments are done directly because we have
1260 * pointers to them. Block and With objects are done indirectly through
1261 * 'liveFrame'. See js_LiveFrameToFloating comment in jsiter.h.
1263 if (genfp->hasCallObj())
1264 fp->getCallObj()->setPrivate(fp);
1265 if (genfp->hasArgsObj())
1266 fp->getArgsObj()->setPrivate(fp);
1267 gen->liveFrame = fp;
1268 (void)cx->enterGenerator(gen); /* OOM check above. */
1270 /* Officially push |fp|. |frame|'s destructor pops. */
1271 cx->stack().pushExecuteFrame(cx, frame, gen->savedRegs, NULL);
1273 /* Swap the enumerators stack for the generator's stack. */
1274 JSObject *enumerators = cx->enumerators;
1275 cx->enumerators = gen->enumerators;
1277 ok = Interpret(cx);
1279 /* Restore the original enumerators stack. */
1280 gen->enumerators = cx->enumerators;
1281 cx->enumerators = enumerators;
1283 /* Restore call/args/block objects. */
1284 cx->leaveGenerator(gen);
1285 gen->liveFrame = genfp;
1286 if (fp->hasArgsObj())
1287 fp->getArgsObj()->setPrivate(genfp);
1288 if (fp->hasCallObj())
1289 fp->getCallObj()->setPrivate(genfp);
1291 JS_ASSERT_IF(argsobjBefore, argsobjBefore == fp->maybeArgsObj());
1292 JS_ASSERT_IF(callobjBefore, callobjBefore == fp->maybeCallObj());
1294 /* Copy and rebase stack frame/args/slots. Restore "floating" flag. */
1295 JS_ASSERT(uintN(gen->savedRegs.sp - fp->slots()) <= fp->script->nslots);
1296 uintN usedAfter = gen->savedRegs.sp - vp;
1297 memcpy(genVp, vp, usedAfter * sizeof(Value));
1298 genfp->flags |= JSFRAME_FLOATING_GENERATOR;
1299 genfp->argv = genVp + 2;
1300 gen->savedRegs.sp = genfp->slots() + (gen->savedRegs.sp - fp->slots());
1301 JS_ASSERT(uintN(gen->savedRegs.sp - genfp->slots()) <= genfp->script->nslots);
1304 if (gen->getFloatingFrame()->flags & JSFRAME_YIELDING) {
1305 /* Yield cannot fail, throw or be called on closing. */
1306 JS_ASSERT(ok);
1307 JS_ASSERT(!cx->throwing);
1308 JS_ASSERT(gen->state == JSGEN_RUNNING);
1309 JS_ASSERT(op != JSGENOP_CLOSE);
1310 genfp->flags &= ~JSFRAME_YIELDING;
1311 gen->state = JSGEN_OPEN;
1312 return JS_TRUE;
1315 genfp->rval.setUndefined();
1316 gen->state = JSGEN_CLOSED;
1317 if (ok) {
1318 /* Returned, explicitly or by falling off the end. */
1319 if (op == JSGENOP_CLOSE)
1320 return JS_TRUE;
1321 return js_ThrowStopIteration(cx);
1325 * An error, silent termination by operation callback or an exception.
1326 * Propagate the condition to the caller.
1328 return JS_FALSE;
1331 static JS_REQUIRES_STACK JSBool
1332 CloseGenerator(JSContext *cx, JSObject *obj)
1334 JS_ASSERT(obj->getClass() == &js_GeneratorClass);
1336 JSGenerator *gen = (JSGenerator *) obj->getPrivate();
1337 if (!gen) {
1338 /* Generator prototype object. */
1339 return JS_TRUE;
1342 if (gen->state == JSGEN_CLOSED)
1343 return JS_TRUE;
1345 return SendToGenerator(cx, JSGENOP_CLOSE, obj, gen, UndefinedValue());
1349 * Common subroutine of generator_(next|send|throw|close) methods.
1351 static JSBool
1352 generator_op(JSContext *cx, JSGeneratorOp op, Value *vp, uintN argc)
1354 JSObject *obj;
1355 LeaveTrace(cx);
1357 obj = ComputeThisFromVp(cx, vp);
1358 if (!InstanceOf(cx, obj, &js_GeneratorClass, vp + 2))
1359 return JS_FALSE;
1361 JSGenerator *gen = (JSGenerator *) obj->getPrivate();
1362 if (!gen) {
1363 /* This happens when obj is the generator prototype. See bug 352885. */
1364 goto closed_generator;
1367 if (gen->state == JSGEN_NEWBORN) {
1368 switch (op) {
1369 case JSGENOP_NEXT:
1370 case JSGENOP_THROW:
1371 break;
1373 case JSGENOP_SEND:
1374 if (argc >= 1 && !vp[2].isUndefined()) {
1375 js_ReportValueError(cx, JSMSG_BAD_GENERATOR_SEND,
1376 JSDVG_SEARCH_STACK, vp[2], NULL);
1377 return JS_FALSE;
1379 break;
1381 default:
1382 JS_ASSERT(op == JSGENOP_CLOSE);
1383 gen->state = JSGEN_CLOSED;
1384 return JS_TRUE;
1386 } else if (gen->state == JSGEN_CLOSED) {
1387 closed_generator:
1388 switch (op) {
1389 case JSGENOP_NEXT:
1390 case JSGENOP_SEND:
1391 return js_ThrowStopIteration(cx);
1392 case JSGENOP_THROW:
1393 SetPendingException(cx, argc >= 1 ? vp[2] : UndefinedValue());
1394 return JS_FALSE;
1395 default:
1396 JS_ASSERT(op == JSGENOP_CLOSE);
1397 return JS_TRUE;
1401 bool undef = ((op == JSGENOP_SEND || op == JSGENOP_THROW) && argc != 0);
1402 if (!SendToGenerator(cx, op, obj, gen, undef ? vp[2] : UndefinedValue()))
1403 return JS_FALSE;
1404 *vp = gen->getFloatingFrame()->rval;
1405 return JS_TRUE;
1408 static JSBool
1409 generator_send(JSContext *cx, uintN argc, Value *vp)
1411 return generator_op(cx, JSGENOP_SEND, vp, argc);
1414 static JSBool
1415 generator_next(JSContext *cx, uintN argc, Value *vp)
1417 return generator_op(cx, JSGENOP_NEXT, vp, argc);
1420 static JSBool
1421 generator_throw(JSContext *cx, uintN argc, Value *vp)
1423 return generator_op(cx, JSGENOP_THROW, vp, argc);
1426 static JSBool
1427 generator_close(JSContext *cx, uintN argc, Value *vp)
1429 return generator_op(cx, JSGENOP_CLOSE, vp, argc);
1432 static JSFunctionSpec generator_methods[] = {
1433 JS_FN(js_next_str, generator_next, 0,JSPROP_ROPERM),
1434 JS_FN(js_send_str, generator_send, 1,JSPROP_ROPERM),
1435 JS_FN(js_throw_str, generator_throw, 1,JSPROP_ROPERM),
1436 JS_FN(js_close_str, generator_close, 0,JSPROP_ROPERM),
1437 JS_FS_END
1440 #endif /* JS_HAS_GENERATORS */
1442 JSObject *
1443 js_InitIteratorClasses(JSContext *cx, JSObject *obj)
1445 JSObject *proto, *stop;
1447 /* Idempotency required: we initialize several things, possibly lazily. */
1448 if (!js_GetClassObject(cx, obj, JSProto_StopIteration, &stop))
1449 return NULL;
1450 if (stop)
1451 return stop;
1453 proto = js_InitClass(cx, obj, NULL, &js_IteratorClass, Iterator, 2,
1454 NULL, iterator_methods, NULL, NULL);
1455 if (!proto)
1456 return NULL;
1458 #if JS_HAS_GENERATORS
1459 /* Initialize the generator internals if configured. */
1460 if (!js_InitClass(cx, obj, NULL, &js_GeneratorClass, NULL, 0,
1461 NULL, generator_methods, NULL, NULL)) {
1462 return NULL;
1464 #endif
1466 return js_InitClass(cx, obj, NULL, &js_StopIterationClass, NULL, 0,
1467 NULL, NULL, NULL, NULL);