Backed out changeset de5d1b528b9a
[mozilla-central.git] / js / src / jsiter.cpp
blob33e569b508b613ca1b3b6ac373764211c71335c9
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 /* wrappedObject */
118 void
119 NativeIterator::mark(JSTracer *trc)
121 if (isKeyIter())
122 MarkIdRange(trc, beginKey(), endKey(), "props");
123 else
124 MarkValueRange(trc, beginValue(), endValue(), "props");
125 if (obj)
126 MarkObject(trc, *obj, "obj");
129 static void
130 iterator_finalize(JSContext *cx, JSObject *obj)
132 JS_ASSERT(obj->getClass() == &js_IteratorClass);
134 NativeIterator *ni = obj->getNativeIterator();
135 if (ni) {
136 cx->free(ni);
137 obj->setNativeIterator(NULL);
141 static void
142 iterator_trace(JSTracer *trc, JSObject *obj)
144 NativeIterator *ni = obj->getNativeIterator();
146 if (ni)
147 ni->mark(trc);
150 struct IdHashPolicy {
151 typedef jsid Lookup;
152 static HashNumber hash(jsid id) {
153 return JSID_BITS(id);
155 static bool match(jsid id1, jsid id2) {
156 return id1 == id2;
160 typedef HashSet<jsid, IdHashPolicy, ContextAllocPolicy> IdSet;
162 static inline bool
163 NewKeyValuePair(JSContext *cx, jsid id, const Value &val, Value *rval)
165 Value vec[2] = { IdToValue(id), val };
166 AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(vec), vec);
168 JSObject *aobj = js_NewArrayObject(cx, 2, vec);
169 if (!aobj)
170 return false;
171 rval->setObject(*aobj);
172 return true;
175 struct KeyEnumeration
177 typedef AutoIdVector ResultVector;
179 static JS_ALWAYS_INLINE bool
180 append(JSContext *, AutoIdVector &keys, JSObject *, jsid id, uintN flags)
182 JS_ASSERT((flags & JSITER_FOREACH) == 0);
183 return keys.append(id);
187 struct ValueEnumeration
189 typedef AutoValueVector ResultVector;
191 static JS_ALWAYS_INLINE bool
192 append(JSContext *cx, AutoValueVector &vals, JSObject *obj, jsid id, uintN flags)
194 JS_ASSERT(flags & JSITER_FOREACH);
196 if (!vals.growBy(1))
197 return false;
199 /* Do the lookup on the original object instead of the prototype. */
200 Value *vp = vals.end() - 1;
201 if (!obj->getProperty(cx, id, vp))
202 return false;
203 if ((flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, *vp, vp))
204 return false;
206 return true;
210 template <class EnumPolicy>
211 static inline bool
212 Enumerate(JSContext *cx, JSObject *obj, JSObject *pobj, jsid id,
213 bool enumerable, bool sharedPermanent, uintN flags, IdSet& ht,
214 typename EnumPolicy::ResultVector *props)
216 IdSet::AddPtr p = ht.lookupForAdd(id);
217 JS_ASSERT_IF(obj == pobj && !obj->isProxy(), !p);
219 /* If we've already seen this, we definitely won't add it. */
220 if (JS_UNLIKELY(!!p))
221 return true;
224 * It's not necessary to add properties to the hash table at the end of the
225 * prototype chain -- but a proxy might return duplicated properties, so
226 * always add for them.
228 if ((pobj->getProto() || pobj->isProxy()) && !ht.add(p, id))
229 return false;
231 if (JS_UNLIKELY(flags & JSITER_OWNONLY)) {
233 * Shared-permanent hack: If this property is shared permanent
234 * and pobj and obj have the same class, then treat it as an own
235 * property of obj, even if pobj != obj. (But see bug 575997.)
237 * Omit the magic __proto__ property so that JS code can use
238 * Object.getOwnPropertyNames without worrying about it.
240 if (!pobj->getProto() && id == ATOM_TO_JSID(cx->runtime->atomState.protoAtom))
241 return true;
242 if (pobj != obj && !(sharedPermanent && pobj->getClass() == obj->getClass()))
243 return true;
246 if (enumerable || (flags & JSITER_HIDDEN))
247 return EnumPolicy::append(cx, *props, obj, id, flags);
249 return true;
252 template <class EnumPolicy>
253 static bool
254 EnumerateNativeProperties(JSContext *cx, JSObject *obj, JSObject *pobj, uintN flags, IdSet &ht,
255 typename EnumPolicy::ResultVector *props)
257 JS_LOCK_OBJ(cx, pobj);
259 size_t initialLength = props->length();
261 /* Collect all unique properties from this object's scope. */
262 for (Shape::Range r = pobj->lastProperty()->all(); !r.empty(); r.popFront()) {
263 const Shape &shape = r.front();
265 if (!JSID_IS_DEFAULT_XML_NAMESPACE(shape.id) &&
266 !shape.isAlias() &&
267 !Enumerate<EnumPolicy>(cx, obj, pobj, shape.id, shape.enumerable(),
268 shape.isSharedPermanent(), flags, ht, props))
270 return false;
274 Reverse(props->begin() + initialLength, props->end());
276 JS_UNLOCK_OBJ(cx, pobj);
277 return true;
280 template <class EnumPolicy>
281 static bool
282 EnumerateDenseArrayProperties(JSContext *cx, JSObject *obj, JSObject *pobj, uintN flags,
283 IdSet &ht, typename EnumPolicy::ResultVector *props)
285 if (!Enumerate<EnumPolicy>(cx, obj, pobj, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), false, true,
286 flags, ht, props)) {
287 return false;
290 if (pobj->getArrayLength() > 0) {
291 size_t capacity = pobj->getDenseArrayCapacity();
292 Value *vp = pobj->dslots;
293 for (size_t i = 0; i < capacity; ++i, ++vp) {
294 if (!vp->isMagic(JS_ARRAY_HOLE)) {
295 /* Dense arrays never get so large that i would not fit into an integer id. */
296 if (!Enumerate<EnumPolicy>(cx, obj, pobj, INT_TO_JSID(i), true, false, flags, ht, props))
297 return false;
302 return true;
305 template <class EnumPolicy>
306 static bool
307 Snapshot(JSContext *cx, JSObject *obj, uintN flags, typename EnumPolicy::ResultVector *props)
310 * FIXME: Bug 575997 - We won't need to initialize this hash table if
311 * (flags & JSITER_OWNONLY) when we eliminate inheritance of
312 * shared-permanent properties as own properties.
314 IdSet ht(cx);
315 if (!ht.init(32))
316 return NULL;
318 JSObject *pobj = obj;
319 do {
320 Class *clasp = pobj->getClass();
321 if (pobj->isNative() &&
322 !pobj->getOps()->enumerate &&
323 !(clasp->flags & JSCLASS_NEW_ENUMERATE)) {
324 if (!clasp->enumerate(cx, pobj))
325 return false;
326 if (!EnumerateNativeProperties<EnumPolicy>(cx, obj, pobj, flags, ht, props))
327 return false;
328 } else if (pobj->isDenseArray()) {
329 if (!EnumerateDenseArrayProperties<EnumPolicy>(cx, obj, pobj, flags, ht, props))
330 return false;
331 } else {
332 if (pobj->isProxy()) {
333 AutoIdVector proxyProps(cx);
334 if (flags & JSITER_OWNONLY) {
335 if (!JSProxy::enumerateOwn(cx, pobj, proxyProps))
336 return false;
337 } else {
338 if (!JSProxy::enumerate(cx, pobj, proxyProps))
339 return false;
341 for (size_t n = 0, len = proxyProps.length(); n < len; n++) {
342 if (!Enumerate<EnumPolicy>(cx, obj, pobj, proxyProps[n], true, false, flags, ht, props))
343 return false;
345 /* Proxy objects enumerate the prototype on their own, so we are done here. */
346 break;
348 Value state;
349 JSIterateOp op = (flags & JSITER_HIDDEN) ? JSENUMERATE_INIT_ALL : JSENUMERATE_INIT;
350 if (!pobj->enumerate(cx, op, &state, NULL))
351 return false;
352 if (state.isMagic(JS_NATIVE_ENUMERATE)) {
353 if (!EnumerateNativeProperties<EnumPolicy>(cx, obj, pobj, flags, ht, props))
354 return false;
355 } else {
356 while (true) {
357 jsid id;
358 if (!pobj->enumerate(cx, JSENUMERATE_NEXT, &state, &id))
359 return false;
360 if (state.isNull())
361 break;
362 if (!Enumerate<EnumPolicy>(cx, obj, pobj, id, true, false, flags, ht, props))
363 return false;
368 if (JS_UNLIKELY(pobj->isXML()))
369 break;
370 } while ((pobj = pobj->getProto()) != NULL);
372 return true;
375 bool
376 VectorToIdArray(JSContext *cx, AutoIdVector &props, JSIdArray **idap)
378 JS_STATIC_ASSERT(sizeof(JSIdArray) > sizeof(jsid));
379 size_t len = props.length();
380 size_t idsz = len * sizeof(jsid);
381 size_t sz = (sizeof(JSIdArray) - sizeof(jsid)) + idsz;
382 JSIdArray *ida = static_cast<JSIdArray *>(cx->malloc(sz));
383 if (!ida)
384 return false;
386 ida->length = static_cast<jsint>(len);
387 memcpy(ida->vector, props.begin(), idsz);
388 *idap = ida;
389 return true;
392 bool
393 GetPropertyNames(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector *props)
395 return Snapshot<KeyEnumeration>(cx, obj, flags & (JSITER_OWNONLY | JSITER_HIDDEN), props);
398 static inline bool
399 GetCustomIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp)
401 /* Check whether we have a valid __iterator__ method. */
402 JSAtom *atom = cx->runtime->atomState.iteratorAtom;
403 if (!js_GetMethod(cx, obj, ATOM_TO_JSID(atom), JSGET_NO_METHOD_BARRIER, vp))
404 return false;
406 /* If there is no custom __iterator__ method, we are done here. */
407 if (vp->isUndefined())
408 return true;
410 /* Otherwise call it and return that object. */
411 LeaveTrace(cx);
412 Value arg = BooleanValue((flags & JSITER_FOREACH) == 0);
413 if (!ExternalInvoke(cx, obj, *vp, 1, &arg, vp))
414 return false;
415 if (vp->isPrimitive()) {
417 * We are always coming from js_ValueToIterator, and we are no longer on
418 * trace, so the object we are iterating over is on top of the stack (-1).
420 js_ReportValueError2(cx, JSMSG_BAD_TRAP_RETURN_VALUE,
421 -1, ObjectValue(*obj), NULL,
422 js_AtomToPrintableString(cx, atom));
423 return false;
425 return true;
428 template <typename T>
429 static inline bool
430 Compare(T *a, T *b, size_t c)
432 size_t n = (c + size_t(7)) / size_t(8);
433 switch (c % 8) {
434 case 0: do { if (*a++ != *b++) return false;
435 case 7: if (*a++ != *b++) return false;
436 case 6: if (*a++ != *b++) return false;
437 case 5: if (*a++ != *b++) return false;
438 case 4: if (*a++ != *b++) return false;
439 case 3: if (*a++ != *b++) return false;
440 case 2: if (*a++ != *b++) return false;
441 case 1: if (*a++ != *b++) return false;
442 } while (--n > 0);
444 return true;
447 static inline JSObject *
448 NewIteratorObject(JSContext *cx, uintN flags)
450 if (flags & JSITER_ENUMERATE) {
452 * Non-escaping native enumerator objects do not need map, proto, or
453 * parent. However, code in jstracer.cpp and elsewhere may find such a
454 * native enumerator object via the stack and (as for all objects that
455 * are not stillborn, with the exception of "NoSuchMethod" internal
456 * helper objects) expect it to have a non-null map pointer, so we
457 * share an empty Enumerator scope in the runtime.
459 JSObject *obj = js_NewGCObject(cx);
460 if (!obj)
461 return false;
462 obj->init(&js_IteratorClass, NULL, NULL, NullValue(), cx);
463 obj->setMap(cx->runtime->emptyEnumeratorShape);
464 return obj;
467 return NewBuiltinClassInstance(cx, &js_IteratorClass);
470 NativeIterator *
471 NativeIterator::allocateKeyIterator(JSContext *cx, uint32 slength, const AutoIdVector &props)
473 size_t plength = props.length();
474 NativeIterator *ni = (NativeIterator *)
475 cx->malloc(sizeof(NativeIterator) + plength * sizeof(jsid) + slength * sizeof(uint32));
476 if (!ni)
477 return NULL;
478 ni->props_array = ni->props_cursor = (jsid *) (ni + 1);
479 ni->props_end = (jsid *)ni->props_array + plength;
480 if (plength)
481 memcpy(ni->props_array, props.begin(), plength * sizeof(jsid));
482 return ni;
485 NativeIterator *
486 NativeIterator::allocateValueIterator(JSContext *cx, const AutoValueVector &props)
488 size_t plength = props.length();
489 NativeIterator *ni = (NativeIterator *)
490 cx->malloc(sizeof(NativeIterator) + plength * sizeof(Value));
491 if (!ni)
492 return NULL;
493 ni->props_array = ni->props_cursor = (Value *) (ni + 1);
494 ni->props_end = (Value *)ni->props_array + plength;
495 if (plength)
496 memcpy(ni->props_array, props.begin(), plength * sizeof(Value));
497 return ni;
500 inline void
501 NativeIterator::init(JSObject *obj, uintN flags, uint32 slength, uint32 key)
503 this->obj = obj;
504 this->flags = flags;
505 this->shapes_array = (uint32 *) this->props_end;
506 this->shapes_length = slength;
507 this->shapes_key = key;
510 static inline void
511 RegisterEnumerator(JSContext *cx, JSObject *iterobj, NativeIterator *ni)
513 /* Register non-escaping native enumerators (for-in) with the current context. */
514 if (ni->flags & JSITER_ENUMERATE) {
515 ni->next = cx->enumerators;
516 cx->enumerators = iterobj;
518 JS_ASSERT(!(ni->flags & JSITER_ACTIVE));
519 ni->flags |= JSITER_ACTIVE;
523 static inline bool
524 VectorToKeyIterator(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector &keys,
525 uint32 slength, uint32 key, Value *vp)
527 JS_ASSERT(!(flags & JSITER_FOREACH));
529 JSObject *iterobj = NewIteratorObject(cx, flags);
530 if (!iterobj)
531 return false;
533 NativeIterator *ni = NativeIterator::allocateKeyIterator(cx, slength, keys);
534 if (!ni)
535 return false;
536 ni->init(obj, flags, slength, key);
538 if (slength) {
540 * Fill in the shape array from scratch. We can't use the array that was
541 * computed for the cache lookup earlier, as constructing iterobj could
542 * have triggered a shape-regenerating GC. Don't bother with regenerating
543 * the shape key; if such a GC *does* occur, we can only get hits through
544 * the one-slot lastNativeIterator cache.
546 JSObject *pobj = obj;
547 size_t ind = 0;
548 do {
549 ni->shapes_array[ind++] = pobj->shape();
550 pobj = pobj->getProto();
551 } while (pobj);
552 JS_ASSERT(ind == slength);
555 iterobj->setNativeIterator(ni);
556 vp->setObject(*iterobj);
558 RegisterEnumerator(cx, iterobj, ni);
559 return true;
562 bool
563 VectorToKeyIterator(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector &props, Value *vp)
565 return VectorToKeyIterator(cx, obj, flags, props, 0, 0, vp);
568 bool
569 VectorToValueIterator(JSContext *cx, JSObject *obj, uintN flags, AutoValueVector &vals,
570 Value *vp)
572 JS_ASSERT(flags & JSITER_FOREACH);
574 JSObject *iterobj = NewIteratorObject(cx, flags);
575 if (!iterobj)
576 return false;
578 NativeIterator *ni = NativeIterator::allocateValueIterator(cx, vals);
579 if (!ni)
580 return false;
581 ni->init(obj, flags, 0, 0);
583 iterobj->setNativeIterator(ni);
584 vp->setObject(*iterobj);
586 RegisterEnumerator(cx, iterobj, ni);
587 return true;
590 bool
591 EnumeratedIdVectorToIterator(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector &props, Value *vp)
593 if (!(flags & JSITER_FOREACH))
594 return VectorToKeyIterator(cx, obj, flags, props, vp);
596 /* For for-each iteration, we need to look up the value of each id. */
598 size_t plength = props.length();
600 AutoValueVector vals(cx);
601 if (!vals.reserve(plength))
602 return NULL;
604 for (size_t i = 0; i < plength; ++i) {
605 if (!ValueEnumeration::append(cx, vals, obj, props[i], flags))
606 return false;
609 return VectorToValueIterator(cx, obj, flags, vals, vp);
612 typedef Vector<uint32, 8> ShapeVector;
614 bool
615 GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp)
617 uint32 hash;
618 JSObject **hp;
619 Vector<uint32, 8> shapes(cx);
620 uint32 key = 0;
622 bool keysOnly = (flags == JSITER_ENUMERATE);
624 if (obj) {
625 if (keysOnly) {
627 * Check to see if this is the same as the most recent object which
628 * was iterated over. We don't explicitly check for shapeless
629 * objects here, as they are not inserted into the cache and
630 * will result in a miss.
632 JSObject *last = JS_THREAD_DATA(cx)->lastNativeIterator;
633 JSObject *proto = obj->getProto();
634 if (last) {
635 NativeIterator *lastni = last->getNativeIterator();
636 if (!(lastni->flags & JSITER_ACTIVE) &&
637 obj->isNative() &&
638 obj->shape() == lastni->shapes_array[0] &&
639 proto && proto->isNative() &&
640 proto->shape() == lastni->shapes_array[1] &&
641 !proto->getProto()) {
642 vp->setObject(*last);
643 RegisterEnumerator(cx, last, lastni);
644 return true;
649 * The iterator object for JSITER_ENUMERATE never escapes, so we
650 * don't care for the proper parent/proto to be set. This also
651 * allows us to re-use a previous iterator object that is not
652 * currently active.
654 JSObject *pobj = obj;
655 do {
656 if (!pobj->isNative() ||
657 obj->getOps()->enumerate ||
658 pobj->getClass()->enumerate != JS_EnumerateStub) {
659 shapes.clear();
660 goto miss;
662 uint32 shape = pobj->shape();
663 key = (key + (key << 16)) ^ shape;
664 if (!shapes.append(shape))
665 return false;
666 pobj = pobj->getProto();
667 } while (pobj);
669 hash = key % JS_ARRAY_LENGTH(JS_THREAD_DATA(cx)->cachedNativeIterators);
670 hp = &JS_THREAD_DATA(cx)->cachedNativeIterators[hash];
671 JSObject *iterobj = *hp;
672 if (iterobj) {
673 NativeIterator *ni = iterobj->getNativeIterator();
674 if (!(ni->flags & JSITER_ACTIVE) &&
675 ni->shapes_key == key &&
676 ni->shapes_length == shapes.length() &&
677 Compare(ni->shapes_array, shapes.begin(), ni->shapes_length)) {
678 vp->setObject(*iterobj);
680 RegisterEnumerator(cx, iterobj, ni);
681 if (shapes.length() == 2)
682 JS_THREAD_DATA(cx)->lastNativeIterator = iterobj;
683 return true;
688 miss:
689 if (obj->isProxy())
690 return JSProxy::iterate(cx, obj, flags, vp);
691 if (!GetCustomIterator(cx, obj, flags, vp))
692 return false;
693 if (!vp->isUndefined())
694 return true;
697 /* NB: for (var p in null) succeeds by iterating over no properties. */
699 if (flags & JSITER_FOREACH) {
700 AutoValueVector vals(cx);
701 if (JS_LIKELY(obj != NULL) && !Snapshot<ValueEnumeration>(cx, obj, flags, &vals))
702 return false;
703 JS_ASSERT(shapes.empty());
704 if (!VectorToValueIterator(cx, obj, flags, vals, vp))
705 return false;
706 } else {
707 AutoIdVector keys(cx);
708 if (JS_LIKELY(obj != NULL) && !Snapshot<KeyEnumeration>(cx, obj, flags, &keys))
709 return false;
710 if (!VectorToKeyIterator(cx, obj, flags, keys, shapes.length(), key, vp))
711 return false;
714 JSObject *iterobj = &vp->toObject();
716 /* Cache the iterator object if possible. */
717 if (shapes.length()) {
718 uint32 hash = key % NATIVE_ITER_CACHE_SIZE;
719 JSObject **hp = &JS_THREAD_DATA(cx)->cachedNativeIterators[hash];
720 *hp = iterobj;
723 if (shapes.length() == 2)
724 JS_THREAD_DATA(cx)->lastNativeIterator = iterobj;
725 return true;
728 static JSObject *
729 iterator_iterator(JSContext *cx, JSObject *obj, JSBool keysonly)
731 return obj;
734 static JSBool
735 Iterator(JSContext *cx, uintN argc, Value *vp)
737 Value *argv = JS_ARGV(cx, vp);
738 bool keyonly = argc >= 2 ? js_ValueToBoolean(argv[1]) : false;
739 uintN flags = JSITER_OWNONLY | (keyonly ? 0 : (JSITER_FOREACH | JSITER_KEYVALUE));
740 *vp = argc >= 1 ? argv[0] : UndefinedValue();
741 return js_ValueToIterator(cx, flags, vp);
744 JSBool
745 js_ThrowStopIteration(JSContext *cx)
747 Value v;
749 JS_ASSERT(!JS_IsExceptionPending(cx));
750 if (js_FindClassObject(cx, NULL, JSProto_StopIteration, &v))
751 SetPendingException(cx, v);
752 return JS_FALSE;
755 static JSBool
756 iterator_next(JSContext *cx, uintN argc, Value *vp)
758 JSObject *obj;
760 obj = ComputeThisFromVp(cx, vp);
761 if (!InstanceOf(cx, obj, &js_IteratorClass, vp + 2))
762 return false;
764 if (!js_IteratorMore(cx, obj, vp))
765 return false;
766 if (!vp->toBoolean()) {
767 js_ThrowStopIteration(cx);
768 return false;
770 return js_IteratorNext(cx, obj, vp);
773 #define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT)
775 static JSFunctionSpec iterator_methods[] = {
776 JS_FN(js_next_str, iterator_next, 0,JSPROP_ROPERM),
777 JS_FS_END
781 * Call ToObject(v).__iterator__(keyonly) if ToObject(v).__iterator__ exists.
782 * Otherwise construct the default iterator.
784 JS_FRIEND_API(JSBool)
785 js_ValueToIterator(JSContext *cx, uintN flags, Value *vp)
787 /* JSITER_KEYVALUE must always come with JSITER_FOREACH */
788 JS_ASSERT_IF(flags & JSITER_KEYVALUE, flags & JSITER_FOREACH);
791 * Make sure the more/next state machine doesn't get stuck. A value might be
792 * left in iterValue when a trace is left due to an operation time-out after
793 * JSOP_MOREITER but before the value is picked up by FOR*.
795 cx->iterValue.setMagic(JS_NO_ITER_VALUE);
797 JSObject *obj;
798 if (vp->isObject()) {
799 /* Common case. */
800 obj = &vp->toObject();
801 } else {
803 * Enumerating over null and undefined gives an empty enumerator.
804 * This is contrary to ECMA-262 9.9 ToObject, invoked from step 3 of
805 * the first production in 12.6.4 and step 4 of the second production,
806 * but it's "web JS" compatible. ES5 fixed for-in to match this de-facto
807 * standard.
809 if ((flags & JSITER_ENUMERATE)) {
810 if (!js_ValueToObjectOrNull(cx, *vp, &obj))
811 return false;
812 if (!obj)
813 return GetIterator(cx, NULL, flags, vp);
814 } else {
815 obj = js_ValueToNonNullObject(cx, *vp);
816 if (!obj)
817 return false;
821 AutoObjectRooter tvr(cx, obj);
823 /* Enumerate Iterator.prototype directly. */
824 JSIteratorOp op = obj->getClass()->ext.iteratorObject;
825 if (op && (obj->getClass() != &js_IteratorClass || obj->getNativeIterator())) {
826 JSObject *iterobj = op(cx, obj, !(flags & JSITER_FOREACH));
827 if (!iterobj)
828 return false;
829 vp->setObject(*iterobj);
830 return true;
833 return GetIterator(cx, obj, flags, vp);
836 #if JS_HAS_GENERATORS
837 static JS_REQUIRES_STACK JSBool
838 CloseGenerator(JSContext *cx, JSObject *genobj);
839 #endif
841 JS_FRIEND_API(JSBool)
842 js_CloseIterator(JSContext *cx, JSObject *obj)
844 cx->iterValue.setMagic(JS_NO_ITER_VALUE);
846 Class *clasp = obj->getClass();
847 if (clasp == &js_IteratorClass) {
848 /* Remove enumerators from the active list, which is a stack. */
849 NativeIterator *ni = obj->getNativeIterator();
851 if (ni->flags & JSITER_ENUMERATE) {
852 JS_ASSERT(cx->enumerators == obj);
853 cx->enumerators = ni->next;
855 JS_ASSERT(ni->flags & JSITER_ACTIVE);
856 ni->flags &= ~JSITER_ACTIVE;
859 * Reset the enumerator; it may still be in the cached iterators
860 * for this thread, and can be reused.
862 ni->props_cursor = ni->props_array;
865 #if JS_HAS_GENERATORS
866 else if (clasp == &js_GeneratorClass) {
867 return CloseGenerator(cx, obj);
869 #endif
870 return JS_TRUE;
874 * Suppress enumeration of deleted properties. We maintain a list of all active
875 * non-escaping for-in enumerators. Whenever a property is deleted, we check
876 * whether any active enumerator contains the (obj, id) pair and has not
877 * enumerated id yet. If so, we delete the id from the list (or advance the
878 * cursor if it is the next id to be enumerated).
880 * We do not suppress enumeration of a property deleted along an object's
881 * prototype chain. Only direct deletions on the object are handled.
883 bool
884 js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id)
886 JSObject *iterobj = cx->enumerators;
887 while (iterobj) {
888 again:
889 NativeIterator *ni = iterobj->getNativeIterator();
890 /* This only works for identified surpressed keys, not values. */
891 if (ni->isKeyIter() && ni->obj == obj && ni->props_cursor < ni->props_end) {
892 /* Check whether id is still to come. */
893 jsid *props_cursor = ni->currentKey();
894 jsid *props_end = ni->endKey();
895 for (jsid *idp = props_cursor; idp < props_end; ++idp) {
896 if (*idp == id) {
898 * Check whether another property along the prototype chain
899 * became visible as a result of this deletion.
901 if (obj->getProto()) {
902 AutoObjectRooter proto(cx, obj->getProto());
903 AutoObjectRooter obj2(cx);
904 JSProperty *prop;
905 if (!proto.object()->lookupProperty(cx, id, obj2.addr(), &prop))
906 return false;
907 if (prop) {
908 uintN attrs;
909 if (obj2.object()->isNative()) {
910 attrs = ((Shape *) prop)->attributes();
911 JS_UNLOCK_OBJ(cx, obj2.object());
912 } else if (!obj2.object()->getAttributes(cx, id, &attrs)) {
913 return false;
915 if (attrs & JSPROP_ENUMERATE)
916 continue;
921 * If lookupProperty or getAttributes above removed a property from
922 * ni, start over.
924 if (props_end != ni->props_end || props_cursor != ni->props_cursor)
925 goto again;
928 * No property along the prototype chain steppeded in to take the
929 * property's place, so go ahead and delete id from the list.
930 * If it is the next property to be enumerated, just skip it.
932 if (idp == props_cursor) {
933 ni->incKeyCursor();
934 } else {
935 memmove(idp, idp + 1, (props_end - (idp + 1)) * sizeof(jsid));
936 ni->props_end = ni->endKey() - 1;
938 break;
942 iterobj = ni->next;
944 return true;
947 JSBool
948 js_IteratorMore(JSContext *cx, JSObject *iterobj, Value *rval)
950 /* Fast path for native iterators */
951 if (iterobj->getClass() == &js_IteratorClass) {
953 * Implement next directly as all the methods of native iterator are
954 * read-only and permanent.
956 NativeIterator *ni = iterobj->getNativeIterator();
957 rval->setBoolean(ni->props_cursor < ni->props_end);
958 return true;
961 /* We might still have a pending value. */
962 if (!cx->iterValue.isMagic(JS_NO_ITER_VALUE)) {
963 rval->setBoolean(true);
964 return true;
967 /* Fetch and cache the next value from the iterator. */
968 jsid id = ATOM_TO_JSID(cx->runtime->atomState.nextAtom);
969 if (!js_GetMethod(cx, iterobj, id, JSGET_METHOD_BARRIER, rval))
970 return false;
971 if (!ExternalInvoke(cx, iterobj, *rval, 0, NULL, rval)) {
972 /* Check for StopIteration. */
973 if (!cx->throwing || !js_ValueIsStopIteration(cx->exception))
974 return false;
976 /* Inline JS_ClearPendingException(cx). */
977 cx->throwing = JS_FALSE;
978 cx->exception.setUndefined();
979 cx->iterValue.setMagic(JS_NO_ITER_VALUE);
980 rval->setBoolean(false);
981 return true;
984 /* Cache the value returned by iterobj.next() so js_IteratorNext() can find it. */
985 JS_ASSERT(!rval->isMagic(JS_NO_ITER_VALUE));
986 cx->iterValue = *rval;
987 rval->setBoolean(true);
988 return true;
991 JSBool
992 js_IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval)
994 /* Fast path for native iterators */
995 if (iterobj->getClass() == &js_IteratorClass) {
997 * Implement next directly as all the methods of the native iterator are
998 * read-only and permanent.
1000 NativeIterator *ni = iterobj->getNativeIterator();
1001 JS_ASSERT(ni->props_cursor < ni->props_end);
1002 if (ni->isKeyIter()) {
1003 *rval = IdToValue(*ni->currentKey());
1004 ni->incKeyCursor();
1005 } else {
1006 *rval = *ni->currentValue();
1007 ni->incValueCursor();
1010 if (rval->isString() || !ni->isKeyIter())
1011 return true;
1013 JSString *str;
1014 jsint i;
1015 if (rval->isInt32() && (jsuint(i = rval->toInt32()) < INT_STRING_LIMIT)) {
1016 str = JSString::intString(i);
1017 } else {
1018 str = js_ValueToString(cx, *rval);
1019 if (!str)
1020 return false;
1023 rval->setString(str);
1024 return true;
1027 JS_ASSERT(!cx->iterValue.isMagic(JS_NO_ITER_VALUE));
1028 *rval = cx->iterValue;
1029 cx->iterValue.setMagic(JS_NO_ITER_VALUE);
1031 return true;
1034 static JSBool
1035 stopiter_hasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp)
1037 *bp = js_ValueIsStopIteration(*v);
1038 return JS_TRUE;
1041 Class js_StopIterationClass = {
1042 js_StopIteration_str,
1043 JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration),
1044 PropertyStub, /* addProperty */
1045 PropertyStub, /* delProperty */
1046 PropertyStub, /* getProperty */
1047 PropertyStub, /* setProperty */
1048 EnumerateStub,
1049 ResolveStub,
1050 ConvertStub,
1051 NULL, /* finalize */
1052 NULL, /* reserved0 */
1053 NULL, /* checkAccess */
1054 NULL, /* call */
1055 NULL, /* construct */
1056 NULL, /* xdrObject */
1057 stopiter_hasInstance
1060 #if JS_HAS_GENERATORS
1062 static void
1063 generator_finalize(JSContext *cx, JSObject *obj)
1065 JSGenerator *gen = (JSGenerator *) obj->getPrivate();
1066 if (!gen)
1067 return;
1070 * gen is open when a script has not called its close method while
1071 * explicitly manipulating it.
1073 JS_ASSERT(gen->state == JSGEN_NEWBORN ||
1074 gen->state == JSGEN_CLOSED ||
1075 gen->state == JSGEN_OPEN);
1076 cx->free(gen);
1079 static void
1080 generator_trace(JSTracer *trc, JSObject *obj)
1082 JSGenerator *gen = (JSGenerator *) obj->getPrivate();
1083 if (!gen)
1084 return;
1087 * Do not mark if the generator is running; the contents may be trash and
1088 * will be replaced when the generator stops.
1090 if (gen->state == JSGEN_RUNNING || gen->state == JSGEN_CLOSING)
1091 return;
1093 JSStackFrame *fp = gen->floatingFrame();
1094 JS_ASSERT(gen->liveFrame() == fp);
1095 MarkValueRange(trc, gen->floatingStack, fp->formalArgsEnd(), "generator slots");
1096 js_TraceStackFrame(trc, fp);
1097 MarkValueRange(trc, fp->slots(), gen->regs.sp, "generator slots");
1100 Class js_GeneratorClass = {
1101 js_Generator_str,
1102 JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Generator) |
1103 JSCLASS_IS_ANONYMOUS | JSCLASS_MARK_IS_TRACE,
1104 PropertyStub, /* addProperty */
1105 PropertyStub, /* delProperty */
1106 PropertyStub, /* getProperty */
1107 PropertyStub, /* setProperty */
1108 EnumerateStub,
1109 ResolveStub,
1110 ConvertStub,
1111 generator_finalize,
1112 NULL, /* reserved */
1113 NULL, /* checkAccess */
1114 NULL, /* call */
1115 NULL, /* construct */
1116 NULL, /* xdrObject */
1117 NULL, /* hasInstance */
1118 JS_CLASS_TRACE(generator_trace),
1120 NULL, /* equality */
1121 NULL, /* outerObject */
1122 NULL, /* innerObject */
1123 iterator_iterator,
1124 NULL, /* wrappedObject */
1128 static inline void
1129 RebaseRegsFromTo(JSFrameRegs *regs, JSStackFrame *from, JSStackFrame *to)
1131 regs->fp = to;
1132 regs->sp = to->slots() + (regs->sp - from->slots());
1136 * Called from the JSOP_GENERATOR case in the interpreter, with fp referring
1137 * to the frame by which the generator function was activated. Create a new
1138 * JSGenerator object, which contains its own JSStackFrame that we populate
1139 * from *fp. We know that upon return, the JSOP_GENERATOR opcode will return
1140 * from the activation in fp, so we can steal away fp->callobj and fp->argsobj
1141 * if they are non-null.
1143 JS_REQUIRES_STACK JSObject *
1144 js_NewGenerator(JSContext *cx)
1146 JSObject *obj = NewBuiltinClassInstance(cx, &js_GeneratorClass);
1147 if (!obj)
1148 return NULL;
1150 JSStackFrame *stackfp = cx->fp();
1151 JS_ASSERT(stackfp->base() == cx->regs->sp);
1152 JS_ASSERT(stackfp->actualArgs() <= stackfp->formalArgs());
1154 /* Load and compute stack slot counts. */
1155 Value *stackvp = stackfp->actualArgs() - 2;
1156 uintN vplen = stackfp->formalArgsEnd() - stackvp;
1158 /* Compute JSGenerator size. */
1159 uintN nbytes = sizeof(JSGenerator) +
1160 (-1 + /* one Value included in JSGenerator */
1161 vplen +
1162 VALUES_PER_STACK_FRAME +
1163 stackfp->numSlots()) * sizeof(Value);
1165 JSGenerator *gen = (JSGenerator *) cx->malloc(nbytes);
1166 if (!gen)
1167 return NULL;
1169 /* Cut up floatingStack space. */
1170 Value *genvp = gen->floatingStack;
1171 JSStackFrame *genfp = reinterpret_cast<JSStackFrame *>(genvp + vplen);
1173 /* Initialize JSGenerator. */
1174 gen->obj = obj;
1175 gen->state = JSGEN_NEWBORN;
1176 gen->enumerators = NULL;
1177 gen->floating = genfp;
1179 /* Initialize regs stored in generator. */
1180 gen->regs = *cx->regs;
1181 RebaseRegsFromTo(&gen->regs, stackfp, genfp);
1183 /* Copy frame off the stack. */
1184 genfp->stealFrameAndSlots(genvp, stackfp, stackvp, cx->regs->sp);
1185 genfp->initFloatingGenerator();
1187 obj->setPrivate(gen);
1188 return obj;
1191 JSGenerator *
1192 js_FloatingFrameToGenerator(JSStackFrame *fp)
1194 JS_ASSERT(fp->isGeneratorFrame() && fp->isFloatingGenerator());
1195 char *floatingStackp = (char *)(fp->actualArgs() - 2);
1196 char *p = floatingStackp - offsetof(JSGenerator, floatingStack);
1197 return reinterpret_cast<JSGenerator *>(p);
1200 typedef enum JSGeneratorOp {
1201 JSGENOP_NEXT,
1202 JSGENOP_SEND,
1203 JSGENOP_THROW,
1204 JSGENOP_CLOSE
1205 } JSGeneratorOp;
1208 * Start newborn or restart yielding generator and perform the requested
1209 * operation inside its frame.
1211 static JS_REQUIRES_STACK JSBool
1212 SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj,
1213 JSGenerator *gen, const Value &arg)
1215 if (gen->state == JSGEN_RUNNING || gen->state == JSGEN_CLOSING) {
1216 js_ReportValueError(cx, JSMSG_NESTING_GENERATOR,
1217 JSDVG_SEARCH_STACK, ObjectOrNullValue(obj),
1218 JS_GetFunctionId(gen->floatingFrame()->fun()));
1219 return JS_FALSE;
1222 /* Check for OOM errors here, where we can fail easily. */
1223 if (!cx->ensureGeneratorStackSpace())
1224 return JS_FALSE;
1226 JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN);
1227 switch (op) {
1228 case JSGENOP_NEXT:
1229 case JSGENOP_SEND:
1230 if (gen->state == JSGEN_OPEN) {
1232 * Store the argument to send as the result of the yield
1233 * expression.
1235 gen->regs.sp[-1] = arg;
1237 gen->state = JSGEN_RUNNING;
1238 break;
1240 case JSGENOP_THROW:
1241 SetPendingException(cx, arg);
1242 gen->state = JSGEN_RUNNING;
1243 break;
1245 default:
1246 JS_ASSERT(op == JSGENOP_CLOSE);
1247 SetPendingException(cx, MagicValue(JS_GENERATOR_CLOSING));
1248 gen->state = JSGEN_CLOSING;
1249 break;
1252 JSStackFrame *genfp = gen->floatingFrame();
1253 Value *genvp = gen->floatingStack;
1254 uintN vplen = genfp->formalArgsEnd() - genvp;
1256 JSStackFrame *stackfp;
1257 Value *stackvp;
1258 JSBool ok;
1261 * Get a pointer to new frame/slots. This memory is not "claimed", so
1262 * the code before pushExecuteFrame must not reenter the interpreter.
1264 GeneratorFrameGuard frame;
1265 if (!cx->stack().getGeneratorFrame(cx, vplen, genfp->numSlots(), &frame)) {
1266 gen->state = JSGEN_CLOSED;
1267 return JS_FALSE;
1269 stackfp = frame.fp();
1270 stackvp = frame.vp();
1272 /* Copy frame onto the stack. */
1273 stackfp->stealFrameAndSlots(stackvp, genfp, genvp, gen->regs.sp);
1274 stackfp->setPrev(cx->regs);
1275 stackfp->unsetFloatingGenerator();
1276 RebaseRegsFromTo(&gen->regs, genfp, stackfp);
1277 MUST_FLOW_THROUGH("restore");
1279 /* Officially push frame. frame's destructor pops. */
1280 cx->stack().pushGeneratorFrame(cx, &gen->regs, &frame);
1282 cx->enterGenerator(gen); /* OOM check above. */
1283 JSObject *enumerators = cx->enumerators;
1284 cx->enumerators = gen->enumerators;
1286 ok = RunScript(cx, stackfp->script(), stackfp->fun(), stackfp->scopeChain());
1288 gen->enumerators = cx->enumerators;
1289 cx->enumerators = enumerators;
1290 cx->leaveGenerator(gen);
1293 * Copy the stack frame and rebase the regs, but not before popping
1294 * the stack, since cx->regs == &gen->regs.
1296 genfp->stealFrameAndSlots(genvp, stackfp, stackvp, gen->regs.sp);
1297 genfp->setFloatingGenerator();
1299 MUST_FLOW_LABEL(restore)
1300 RebaseRegsFromTo(&gen->regs, stackfp, genfp);
1302 if (gen->floatingFrame()->isYielding()) {
1303 /* Yield cannot fail, throw or be called on closing. */
1304 JS_ASSERT(ok);
1305 JS_ASSERT(!cx->throwing);
1306 JS_ASSERT(gen->state == JSGEN_RUNNING);
1307 JS_ASSERT(op != JSGENOP_CLOSE);
1308 genfp->clearYielding();
1309 gen->state = JSGEN_OPEN;
1310 return JS_TRUE;
1313 genfp->clearReturnValue();
1314 gen->state = JSGEN_CLOSED;
1315 if (ok) {
1316 /* Returned, explicitly or by falling off the end. */
1317 if (op == JSGENOP_CLOSE)
1318 return JS_TRUE;
1319 return js_ThrowStopIteration(cx);
1323 * An error, silent termination by operation callback or an exception.
1324 * Propagate the condition to the caller.
1326 return JS_FALSE;
1329 static JS_REQUIRES_STACK JSBool
1330 CloseGenerator(JSContext *cx, JSObject *obj)
1332 JS_ASSERT(obj->getClass() == &js_GeneratorClass);
1334 JSGenerator *gen = (JSGenerator *) obj->getPrivate();
1335 if (!gen) {
1336 /* Generator prototype object. */
1337 return JS_TRUE;
1340 if (gen->state == JSGEN_CLOSED)
1341 return JS_TRUE;
1343 return SendToGenerator(cx, JSGENOP_CLOSE, obj, gen, UndefinedValue());
1347 * Common subroutine of generator_(next|send|throw|close) methods.
1349 static JSBool
1350 generator_op(JSContext *cx, JSGeneratorOp op, Value *vp, uintN argc)
1352 JSObject *obj;
1353 LeaveTrace(cx);
1355 obj = ComputeThisFromVp(cx, vp);
1356 if (!InstanceOf(cx, obj, &js_GeneratorClass, vp + 2))
1357 return JS_FALSE;
1359 JSGenerator *gen = (JSGenerator *) obj->getPrivate();
1360 if (!gen) {
1361 /* This happens when obj is the generator prototype. See bug 352885. */
1362 goto closed_generator;
1365 if (gen->state == JSGEN_NEWBORN) {
1366 switch (op) {
1367 case JSGENOP_NEXT:
1368 case JSGENOP_THROW:
1369 break;
1371 case JSGENOP_SEND:
1372 if (argc >= 1 && !vp[2].isUndefined()) {
1373 js_ReportValueError(cx, JSMSG_BAD_GENERATOR_SEND,
1374 JSDVG_SEARCH_STACK, vp[2], NULL);
1375 return JS_FALSE;
1377 break;
1379 default:
1380 JS_ASSERT(op == JSGENOP_CLOSE);
1381 gen->state = JSGEN_CLOSED;
1382 return JS_TRUE;
1384 } else if (gen->state == JSGEN_CLOSED) {
1385 closed_generator:
1386 switch (op) {
1387 case JSGENOP_NEXT:
1388 case JSGENOP_SEND:
1389 return js_ThrowStopIteration(cx);
1390 case JSGENOP_THROW:
1391 SetPendingException(cx, argc >= 1 ? vp[2] : UndefinedValue());
1392 return JS_FALSE;
1393 default:
1394 JS_ASSERT(op == JSGENOP_CLOSE);
1395 return JS_TRUE;
1399 bool undef = ((op == JSGENOP_SEND || op == JSGENOP_THROW) && argc != 0);
1400 if (!SendToGenerator(cx, op, obj, gen, undef ? vp[2] : UndefinedValue()))
1401 return JS_FALSE;
1402 *vp = gen->floatingFrame()->returnValue();
1403 return JS_TRUE;
1406 static JSBool
1407 generator_send(JSContext *cx, uintN argc, Value *vp)
1409 return generator_op(cx, JSGENOP_SEND, vp, argc);
1412 static JSBool
1413 generator_next(JSContext *cx, uintN argc, Value *vp)
1415 return generator_op(cx, JSGENOP_NEXT, vp, argc);
1418 static JSBool
1419 generator_throw(JSContext *cx, uintN argc, Value *vp)
1421 return generator_op(cx, JSGENOP_THROW, vp, argc);
1424 static JSBool
1425 generator_close(JSContext *cx, uintN argc, Value *vp)
1427 return generator_op(cx, JSGENOP_CLOSE, vp, argc);
1430 static JSFunctionSpec generator_methods[] = {
1431 JS_FN(js_next_str, generator_next, 0,JSPROP_ROPERM),
1432 JS_FN(js_send_str, generator_send, 1,JSPROP_ROPERM),
1433 JS_FN(js_throw_str, generator_throw, 1,JSPROP_ROPERM),
1434 JS_FN(js_close_str, generator_close, 0,JSPROP_ROPERM),
1435 JS_FS_END
1438 #endif /* JS_HAS_GENERATORS */
1440 JSObject *
1441 js_InitIteratorClasses(JSContext *cx, JSObject *obj)
1443 JSObject *proto, *stop;
1445 /* Idempotency required: we initialize several things, possibly lazily. */
1446 if (!js_GetClassObject(cx, obj, JSProto_StopIteration, &stop))
1447 return NULL;
1448 if (stop)
1449 return stop;
1451 proto = js_InitClass(cx, obj, NULL, &js_IteratorClass, Iterator, 2,
1452 NULL, iterator_methods, NULL, NULL);
1453 if (!proto)
1454 return NULL;
1456 #if JS_HAS_GENERATORS
1457 /* Initialize the generator internals if configured. */
1458 if (!js_InitClass(cx, obj, NULL, &js_GeneratorClass, NULL, 0,
1459 NULL, generator_methods, NULL, NULL)) {
1460 return NULL;
1462 #endif
1464 return js_InitClass(cx, obj, NULL, &js_StopIterationClass, NULL, 0,
1465 NULL, NULL, NULL, NULL);