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
17 * The Original Code is Mozilla Communicator client code, released
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.
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.
45 #include <string.h> /* for memcpy */
68 #if JS_HAS_XML_SUPPORT
72 #define JSSLOT_ITER_STATE (JSSLOT_PRIVATE)
73 #define JSSLOT_ITER_FLAGS (JSSLOT_PRIVATE + 1)
75 #if JSSLOT_ITER_FLAGS >= JS_INITIAL_NSLOTS
76 #error JS_INITIAL_NSLOTS must be greater than JSSLOT_ITER_FLAGS.
82 CloseGenerator(JSContext
*cx
, JSObject
*genobj
);
87 * Shared code to close iterator's state either through an explicit call or
88 * when GC detects that the iterator is no longer reachable.
91 js_CloseNativeIterator(JSContext
*cx
, JSObject
*iterobj
)
96 JS_ASSERT(STOBJ_GET_CLASS(iterobj
) == &js_IteratorClass
);
98 /* Avoid double work if js_CloseNativeIterator was called on obj. */
99 state
= STOBJ_GET_SLOT(iterobj
, JSSLOT_ITER_STATE
);
100 if (JSVAL_IS_NULL(state
))
103 /* Protect against failure to fully initialize obj. */
104 iterable
= STOBJ_GET_PARENT(iterobj
);
106 #if JS_HAS_XML_SUPPORT
107 uintN flags
= JSVAL_TO_INT(STOBJ_GET_SLOT(iterobj
, JSSLOT_ITER_FLAGS
));
108 if ((flags
& JSITER_FOREACH
) && OBJECT_IS_XML(cx
, iterable
)) {
109 ((JSXMLObjectOps
*) iterable
->map
->ops
)->
110 enumerateValues(cx
, iterable
, JSENUMERATE_DESTROY
, &state
,
114 OBJ_ENUMERATE(cx
, iterable
, JSENUMERATE_DESTROY
, &state
, NULL
);
116 STOBJ_SET_SLOT(iterobj
, JSSLOT_ITER_STATE
, JSVAL_NULL
);
119 JSClass js_IteratorClass
= {
121 JSCLASS_HAS_RESERVED_SLOTS(2) | /* slots for state and flags */
122 JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator
),
123 JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
,
124 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, JS_FinalizeStub
,
125 JSCLASS_NO_OPTIONAL_MEMBERS
129 InitNativeIterator(JSContext
*cx
, JSObject
*iterobj
, JSObject
*obj
, uintN flags
)
134 JS_ASSERT(STOBJ_GET_CLASS(iterobj
) == &js_IteratorClass
);
136 /* Initialize iterobj in case of enumerate hook failure. */
137 STOBJ_SET_PARENT(iterobj
, obj
);
138 STOBJ_SET_SLOT(iterobj
, JSSLOT_ITER_STATE
, JSVAL_NULL
);
139 STOBJ_SET_SLOT(iterobj
, JSSLOT_ITER_FLAGS
, INT_TO_JSVAL(flags
));
140 if (!js_RegisterCloseableIterator(cx
, iterobj
))
146 #if JS_HAS_XML_SUPPORT
147 ((flags
& JSITER_FOREACH
) && OBJECT_IS_XML(cx
, obj
))
148 ? ((JSXMLObjectOps
*) obj
->map
->ops
)->
149 enumerateValues(cx
, obj
, JSENUMERATE_INIT
, &state
, NULL
, NULL
)
152 OBJ_ENUMERATE(cx
, obj
, JSENUMERATE_INIT
, &state
, NULL
);
156 STOBJ_SET_SLOT(iterobj
, JSSLOT_ITER_STATE
, state
);
157 if (flags
& JSITER_ENUMERATE
) {
159 * The enumerating iterator needs the original object to suppress
160 * enumeration of deleted or shadowed prototype properties. Since the
161 * enumerator never escapes to scripts, we use the prototype slot to
162 * store the original object.
164 JS_ASSERT(obj
!= iterobj
);
165 STOBJ_SET_PROTO(iterobj
, obj
);
171 Iterator(JSContext
*cx
, JSObject
*iterobj
, uintN argc
, jsval
*argv
, jsval
*rval
)
178 if (!js_ValueToBoolean(cx
, argv
[1], &keyonly
))
180 flags
= keyonly
? 0 : JSITER_FOREACH
;
182 if (cx
->fp
->flags
& JSFRAME_CONSTRUCTING
) {
183 /* XXX work around old valueOf call hidden beneath js_ValueToObject */
184 if (!JSVAL_IS_PRIMITIVE(argv
[0])) {
185 obj
= JSVAL_TO_OBJECT(argv
[0]);
187 obj
= js_ValueToNonNullObject(cx
, argv
[0]);
190 argv
[0] = OBJECT_TO_JSVAL(obj
);
192 return InitNativeIterator(cx
, iterobj
, obj
, flags
);
196 return js_ValueToIterator(cx
, flags
, rval
);
200 NewKeyValuePair(JSContext
*cx
, jsid key
, jsval val
, jsval
*rval
)
203 JSTempValueRooter tvr
;
206 vec
[0] = ID_TO_VALUE(key
);
209 JS_PUSH_TEMP_ROOT(cx
, 2, vec
, &tvr
);
210 aobj
= js_NewArrayObject(cx
, 2, vec
);
211 *rval
= OBJECT_TO_JSVAL(aobj
);
212 JS_POP_TEMP_ROOT(cx
, &tvr
);
218 IteratorNextImpl(JSContext
*cx
, JSObject
*obj
, jsval
*rval
)
226 JS_ASSERT(OBJ_GET_CLASS(cx
, obj
) == &js_IteratorClass
);
228 iterable
= OBJ_GET_PARENT(cx
, obj
);
230 state
= OBJ_GET_SLOT(cx
, obj
, JSSLOT_ITER_STATE
);
231 if (JSVAL_IS_NULL(state
))
234 flags
= JSVAL_TO_INT(OBJ_GET_SLOT(cx
, obj
, JSSLOT_ITER_FLAGS
));
235 JS_ASSERT(!(flags
& JSITER_ENUMERATE
));
236 foreach
= (flags
& JSITER_FOREACH
) != 0;
238 #if JS_HAS_XML_SUPPORT
239 (foreach
&& OBJECT_IS_XML(cx
, iterable
))
240 ? ((JSXMLObjectOps
*) iterable
->map
->ops
)->
241 enumerateValues(cx
, iterable
, JSENUMERATE_NEXT
, &state
,
245 OBJ_ENUMERATE(cx
, iterable
, JSENUMERATE_NEXT
, &state
, &id
);
249 OBJ_SET_SLOT(cx
, obj
, JSSLOT_ITER_STATE
, state
);
250 if (JSVAL_IS_NULL(state
))
254 #if JS_HAS_XML_SUPPORT
255 if (!OBJECT_IS_XML(cx
, iterable
) &&
256 !OBJ_GET_PROPERTY(cx
, iterable
, id
, rval
)) {
260 if (!NewKeyValuePair(cx
, id
, *rval
, rval
))
263 *rval
= ID_TO_VALUE(id
);
268 JS_ASSERT(OBJ_GET_SLOT(cx
, obj
, JSSLOT_ITER_STATE
) == JSVAL_NULL
);
274 js_ThrowStopIteration(JSContext
*cx
, JSObject
*obj
)
278 JS_ASSERT(!JS_IsExceptionPending(cx
));
279 if (js_FindClassObject(cx
, NULL
, INT_TO_JSID(JSProto_StopIteration
), &v
))
280 JS_SetPendingException(cx
, v
);
285 iterator_next(JSContext
*cx
, uintN argc
, jsval
*vp
)
289 obj
= JSVAL_TO_OBJECT(vp
[1]);
290 if (!JS_InstanceOf(cx
, obj
, &js_IteratorClass
, vp
+ 2))
293 if (!IteratorNextImpl(cx
, obj
, vp
))
296 if (*vp
== JSVAL_HOLE
) {
298 js_ThrowStopIteration(cx
, obj
);
305 iterator_self(JSContext
*cx
, uintN argc
, jsval
*vp
)
311 #define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT)
313 static JSFunctionSpec iterator_methods
[] = {
314 JS_FN(js_iterator_str
, iterator_self
, 0,0,JSPROP_ROPERM
,0),
315 JS_FN(js_next_str
, iterator_next
, 0,0,JSPROP_ROPERM
,0),
320 js_GetNativeIteratorFlags(JSContext
*cx
, JSObject
*iterobj
)
322 if (OBJ_GET_CLASS(cx
, iterobj
) != &js_IteratorClass
)
324 return JSVAL_TO_INT(OBJ_GET_SLOT(cx
, iterobj
, JSSLOT_ITER_FLAGS
));
328 * Call ToObject(v).__iterator__(keyonly) if ToObject(v).__iterator__ exists.
329 * Otherwise construct the defualt iterator.
332 js_ValueToIterator(JSContext
*cx
, uintN flags
, jsval
*vp
)
335 JSTempValueRooter tvr
;
341 JS_ASSERT(!(flags
& ~(JSITER_ENUMERATE
|
345 /* JSITER_KEYVALUE must always come with JSITER_FOREACH */
346 JS_ASSERT(!(flags
& JSITER_KEYVALUE
) || (flags
& JSITER_FOREACH
));
348 /* XXX work around old valueOf call hidden beneath js_ValueToObject */
349 if (!JSVAL_IS_PRIMITIVE(*vp
)) {
350 obj
= JSVAL_TO_OBJECT(*vp
);
353 * Enumerating over null and undefined gives an empty enumerator.
354 * This is contrary to ECMA-262 9.9 ToObject, invoked from step 3 of
355 * the first production in 12.6.4 and step 4 of the second production,
356 * but it's "web JS" compatible.
358 if ((flags
& JSITER_ENUMERATE
)) {
359 if (!js_ValueToObject(cx
, *vp
, &obj
))
364 obj
= js_ValueToNonNullObject(cx
, *vp
);
371 JS_PUSH_TEMP_ROOT_OBJECT(cx
, obj
, &tvr
);
373 atom
= cx
->runtime
->atomState
.iteratorAtom
;
374 #if JS_HAS_XML_SUPPORT
375 if (OBJECT_IS_XML(cx
, obj
)) {
376 if (!js_GetXMLFunction(cx
, obj
, ATOM_TO_JSID(atom
), vp
))
381 if (!OBJ_GET_PROPERTY(cx
, obj
, ATOM_TO_JSID(atom
), vp
))
385 if (JSVAL_IS_VOID(*vp
)) {
388 * Fail over to the default enumerating native iterator.
390 * Create iterobj with a NULL parent to ensure that we use the correct
391 * scope chain to lookup the iterator's constructor. Since we use the
392 * parent slot to keep track of the iterable, we must fix it up after.
394 iterobj
= js_NewObject(cx
, &js_IteratorClass
, NULL
, NULL
);
398 /* Store iterobj in *vp to protect it from GC (callers must root vp). */
399 *vp
= OBJECT_TO_JSVAL(iterobj
);
401 if (!InitNativeIterator(cx
, iterobj
, obj
, flags
))
404 arg
= BOOLEAN_TO_JSVAL((flags
& JSITER_FOREACH
) == 0);
405 if (!js_InternalInvoke(cx
, obj
, *vp
, JSINVOKE_ITERATOR
, 1, &arg
, vp
))
407 if (JSVAL_IS_PRIMITIVE(*vp
)) {
408 const char *printable
= js_AtomToPrintableString(cx
, atom
);
410 js_ReportValueError2(cx
, JSMSG_BAD_ITERATOR_RETURN
,
411 JSDVG_SEARCH_STACK
, *vp
, NULL
, printable
);
420 JS_POP_TEMP_ROOT(cx
, &tvr
);
428 js_CloseIterator(JSContext
*cx
, jsval v
)
433 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v
));
434 obj
= JSVAL_TO_OBJECT(v
);
435 clasp
= OBJ_GET_CLASS(cx
, obj
);
437 if (clasp
== &js_IteratorClass
) {
438 js_CloseNativeIterator(cx
, obj
);
440 #if JS_HAS_GENERATORS
441 else if (clasp
== &js_GeneratorClass
) {
442 if (!CloseGenerator(cx
, obj
))
450 CallEnumeratorNext(JSContext
*cx
, JSObject
*iterobj
, uintN flags
, jsval
*rval
)
452 JSObject
*obj
, *origobj
;
459 JSExtendedClass
*xclasp
;
463 JS_ASSERT(flags
& JSITER_ENUMERATE
);
464 JS_ASSERT(STOBJ_GET_CLASS(iterobj
) == &js_IteratorClass
);
466 obj
= STOBJ_GET_PARENT(iterobj
);
467 origobj
= STOBJ_GET_PROTO(iterobj
);
468 state
= STOBJ_GET_SLOT(iterobj
, JSSLOT_ITER_STATE
);
469 if (JSVAL_IS_NULL(state
))
472 foreach
= (flags
& JSITER_FOREACH
) != 0;
473 #if JS_HAS_XML_SUPPORT
475 * Treat an XML object specially only when it starts the prototype chain.
476 * Otherwise we need to do the usual deleted and shadowed property checks.
478 if (obj
== origobj
&& OBJECT_IS_XML(cx
, obj
)) {
480 JSXMLObjectOps
*xmlops
= (JSXMLObjectOps
*) obj
->map
->ops
;
482 if (!xmlops
->enumerateValues(cx
, obj
, JSENUMERATE_NEXT
, &state
,
487 if (!OBJ_ENUMERATE(cx
, obj
, JSENUMERATE_NEXT
, &state
, &id
))
490 STOBJ_SET_SLOT(iterobj
, JSSLOT_ITER_STATE
, state
);
491 if (JSVAL_IS_NULL(state
))
497 if (!OBJ_ENUMERATE(cx
, obj
, JSENUMERATE_NEXT
, &state
, &id
))
500 STOBJ_SET_SLOT(iterobj
, JSSLOT_ITER_STATE
, state
);
501 if (JSVAL_IS_NULL(state
)) {
502 #if JS_HAS_XML_SUPPORT
503 if (OBJECT_IS_XML(cx
, obj
)) {
505 * We just finished enumerating an XML obj that is present on
506 * the prototype chain of a non-XML origobj. Stop further
507 * prototype chain searches because XML objects don't
508 * enumerate prototypes.
510 JS_ASSERT(origobj
!= obj
);
511 JS_ASSERT(!OBJECT_IS_XML(cx
, origobj
));
515 obj
= OBJ_GET_PROTO(cx
, obj
);
517 STOBJ_SET_PARENT(iterobj
, obj
);
518 if (!OBJ_ENUMERATE(cx
, obj
, JSENUMERATE_INIT
, &state
, NULL
))
520 STOBJ_SET_SLOT(iterobj
, JSSLOT_ITER_STATE
, state
);
521 if (!JSVAL_IS_NULL(state
))
528 /* Skip properties not in obj when looking from origobj. */
529 if (!OBJ_LOOKUP_PROPERTY(cx
, origobj
, id
, &obj2
, &prop
))
533 OBJ_DROP_PROPERTY(cx
, obj2
, prop
);
536 * If the id was found in a prototype object or an unrelated object
537 * (specifically, not in an inner object for obj), skip it. This step
538 * means that all OBJ_LOOKUP_PROPERTY implementations must return an
539 * object further along on the prototype chain, or else possibly an
540 * object returned by the JSExtendedClass.outerObject optional hook.
544 clasp
= OBJ_GET_CLASS(cx
, obj2
);
545 if (clasp
->flags
& JSCLASS_IS_EXTENDED
) {
546 xclasp
= (JSExtendedClass
*) clasp
;
547 cond
= xclasp
->outerObject
&&
548 xclasp
->outerObject(cx
, obj2
) == obj
;
555 /* Get property querying the original object. */
556 if (!OBJ_GET_PROPERTY(cx
, origobj
, id
, rval
))
562 if (flags
& JSITER_KEYVALUE
) {
563 if (!NewKeyValuePair(cx
, id
, *rval
, rval
))
567 /* Make rval a string for uniformity and compatibility. */
568 str
= js_ValueToString(cx
, ID_TO_VALUE(id
));
571 *rval
= STRING_TO_JSVAL(str
);
576 JS_ASSERT(STOBJ_GET_SLOT(iterobj
, JSSLOT_ITER_STATE
) == JSVAL_NULL
);
582 js_CallIteratorNext(JSContext
*cx
, JSObject
*iterobj
, jsval
*rval
)
586 /* Fast path for native iterators */
587 if (OBJ_GET_CLASS(cx
, iterobj
) == &js_IteratorClass
) {
588 flags
= JSVAL_TO_INT(OBJ_GET_SLOT(cx
, iterobj
, JSSLOT_ITER_FLAGS
));
589 if (flags
& JSITER_ENUMERATE
)
590 return CallEnumeratorNext(cx
, iterobj
, flags
, rval
);
593 * Call next directly as all the methods of the native iterator are
594 * read-only and permanent.
596 if (!IteratorNextImpl(cx
, iterobj
, rval
))
599 jsid id
= ATOM_TO_JSID(cx
->runtime
->atomState
.nextAtom
);
601 if (!JS_GetMethodById(cx
, iterobj
, id
, &iterobj
, rval
))
603 if (!js_InternalCall(cx
, iterobj
, *rval
, 0, NULL
, rval
)) {
604 /* Check for StopIteration. */
606 JSVAL_IS_PRIMITIVE(cx
->exception
) ||
607 OBJ_GET_CLASS(cx
, JSVAL_TO_OBJECT(cx
->exception
))
608 != &js_StopIterationClass
) {
612 /* Inline JS_ClearPendingException(cx). */
613 cx
->throwing
= JS_FALSE
;
614 cx
->exception
= JSVAL_VOID
;
624 stopiter_hasInstance(JSContext
*cx
, JSObject
*obj
, jsval v
, JSBool
*bp
)
626 *bp
= !JSVAL_IS_PRIMITIVE(v
) &&
627 OBJ_GET_CLASS(cx
, JSVAL_TO_OBJECT(v
)) == &js_StopIterationClass
;
631 JSClass js_StopIterationClass
= {
632 js_StopIteration_str
,
633 JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration
),
634 JS_PropertyStub
, JS_PropertyStub
,
635 JS_PropertyStub
, JS_PropertyStub
,
636 JS_EnumerateStub
, JS_ResolveStub
,
637 JS_ConvertStub
, JS_FinalizeStub
,
640 NULL
, stopiter_hasInstance
,
644 #if JS_HAS_GENERATORS
647 generator_finalize(JSContext
*cx
, JSObject
*obj
)
651 gen
= (JSGenerator
*) JS_GetPrivate(cx
, obj
);
654 * gen can be open on shutdown when close hooks are ignored or when
655 * the embedding cancels scheduled close hooks.
657 JS_ASSERT(gen
->state
== JSGEN_NEWBORN
|| gen
->state
== JSGEN_CLOSED
||
658 gen
->state
== JSGEN_OPEN
);
664 generator_trace(JSTracer
*trc
, JSObject
*obj
)
668 gen
= (JSGenerator
*) JS_GetPrivate(trc
->context
, obj
);
673 * js_TraceStackFrame does not recursively trace the down-linked frame
674 * chain, so we insist that gen->frame has no parent to trace when the
675 * generator is not running.
677 JS_ASSERT_IF(gen
->state
!= JSGEN_RUNNING
&& gen
->state
!= JSGEN_CLOSING
,
681 * FIXME be 390950. Generator's frame is a part of the JS stack when the
682 * generator is running or closing. Thus tracing the frame in this case
683 * here duplicates the work done in js_TraceContext.
685 js_TraceStackFrame(trc
, &gen
->frame
);
688 JSClass js_GeneratorClass
= {
690 JSCLASS_HAS_PRIVATE
| JSCLASS_IS_ANONYMOUS
|
691 JSCLASS_MARK_IS_TRACE
| JSCLASS_HAS_CACHED_PROTO(JSProto_Generator
),
692 JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
,
693 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, generator_finalize
,
694 NULL
, NULL
, NULL
, NULL
,
695 NULL
, NULL
, JS_CLASS_TRACE(generator_trace
), NULL
699 * Called from the JSOP_GENERATOR case in the interpreter, with fp referring
700 * to the frame by which the generator function was activated. Create a new
701 * JSGenerator object, which contains its own JSStackFrame that we populate
702 * from *fp. We know that upon return, the JSOP_GENERATOR opcode will return
703 * from the activation in fp, so we can steal away fp->callobj and fp->argsobj
704 * if they are non-null.
707 js_NewGenerator(JSContext
*cx
, JSStackFrame
*fp
)
710 uintN argc
, nargs
, nvars
, depth
, nslots
;
714 /* After the following return, failing control flow must goto bad. */
715 obj
= js_NewObject(cx
, &js_GeneratorClass
, NULL
, NULL
);
719 /* Load and compute stack slot counts. */
721 nargs
= JS_MAX(argc
, fp
->fun
->nargs
);
723 depth
= fp
->script
->depth
;
724 nslots
= 2 + nargs
+ nvars
+ 2 * depth
;
726 /* Allocate obj's private data struct. */
727 gen
= (JSGenerator
*)
728 JS_malloc(cx
, sizeof(JSGenerator
) + (nslots
- 1) * sizeof(jsval
));
734 /* Steal away objects reflecting fp and point them at gen->frame. */
735 gen
->frame
.callobj
= fp
->callobj
;
737 JS_SetPrivate(cx
, fp
->callobj
, &gen
->frame
);
740 gen
->frame
.argsobj
= fp
->argsobj
;
742 JS_SetPrivate(cx
, fp
->argsobj
, &gen
->frame
);
746 /* These two references can be shared with fp until it goes away. */
747 gen
->frame
.varobj
= fp
->varobj
;
748 gen
->frame
.thisp
= fp
->thisp
;
750 /* Copy call-invariant script and function references. */
751 gen
->frame
.script
= fp
->script
;
752 gen
->frame
.callee
= fp
->callee
;
753 gen
->frame
.fun
= fp
->fun
;
755 /* Use newsp to carve space out of gen->stack. */
757 gen
->arena
.next
= NULL
;
758 gen
->arena
.base
= (jsuword
) newsp
;
759 gen
->arena
.limit
= gen
->arena
.avail
= (jsuword
) (newsp
+ nslots
);
761 #define COPY_STACK_ARRAY(vec,cnt,num) \
763 gen->frame.cnt = cnt; \
764 gen->frame.vec = newsp; \
766 memcpy(gen->frame.vec, fp->vec, (num) * sizeof(jsval)); \
769 /* Copy argv, rval, and vars. */
770 *newsp
++ = fp
->argv
[-2];
771 *newsp
++ = fp
->argv
[-1];
772 COPY_STACK_ARRAY(argv
, argc
, nargs
);
773 gen
->frame
.rval
= fp
->rval
;
774 COPY_STACK_ARRAY(vars
, nvars
, nvars
);
776 #undef COPY_STACK_ARRAY
778 /* Initialize or copy virtual machine state. */
779 gen
->frame
.down
= NULL
;
780 gen
->frame
.annotation
= NULL
;
781 gen
->frame
.scopeChain
= fp
->scopeChain
;
782 gen
->frame
.pc
= fp
->pc
;
784 /* Allocate generating pc and operand stack space. */
785 gen
->frame
.spbase
= gen
->frame
.sp
= newsp
+ depth
;
787 /* Copy remaining state (XXX sharp* and xml* should be local vars). */
788 gen
->frame
.sharpDepth
= 0;
789 gen
->frame
.sharpArray
= NULL
;
790 gen
->frame
.flags
= fp
->flags
| JSFRAME_GENERATOR
;
791 gen
->frame
.dormantNext
= NULL
;
792 gen
->frame
.xmlNamespace
= NULL
;
793 gen
->frame
.blockChain
= NULL
;
795 /* Note that gen is newborn. */
796 gen
->state
= JSGEN_NEWBORN
;
798 if (!JS_SetPrivate(cx
, obj
, gen
)) {
805 cx
->weakRoots
.newborn
[GCX_OBJECT
] = NULL
;
809 typedef enum JSGeneratorOp
{
817 * Start newborn or restart yielding generator and perform the requested
818 * operation inside its frame.
821 SendToGenerator(JSContext
*cx
, JSGeneratorOp op
, JSObject
*obj
,
822 JSGenerator
*gen
, jsval arg
, jsval
*rval
)
829 if (gen
->state
== JSGEN_RUNNING
|| gen
->state
== JSGEN_CLOSING
) {
830 js_ReportValueError(cx
, JSMSG_NESTING_GENERATOR
,
831 JSDVG_SEARCH_STACK
, OBJECT_TO_JSVAL(obj
),
832 JS_GetFunctionId(gen
->frame
.fun
));
836 JS_ASSERT(gen
->state
== JSGEN_NEWBORN
|| gen
->state
== JSGEN_OPEN
);
840 if (gen
->state
== JSGEN_OPEN
) {
842 * Store the argument to send as the result of the yield
845 gen
->frame
.sp
[-1] = arg
;
847 gen
->state
= JSGEN_RUNNING
;
851 JS_SetPendingException(cx
, arg
);
852 gen
->state
= JSGEN_RUNNING
;
856 JS_ASSERT(op
== JSGENOP_CLOSE
);
857 JS_SetPendingException(cx
, JSVAL_ARETURN
);
858 gen
->state
= JSGEN_CLOSING
;
862 /* Extend the current stack pool with gen->arena. */
863 arena
= cx
->stackPool
.current
;
864 JS_ASSERT(!arena
->next
);
865 JS_ASSERT(!gen
->arena
.next
);
866 JS_ASSERT(cx
->stackPool
.current
!= &gen
->arena
);
867 cx
->stackPool
.current
= arena
->next
= &gen
->arena
;
869 /* Push gen->frame around the interpreter activation. */
871 cx
->fp
= &gen
->frame
;
872 gen
->frame
.down
= fp
;
873 ok
= js_Interpret(cx
, gen
->frame
.pc
, &junk
);
875 gen
->frame
.down
= NULL
;
877 /* Retract the stack pool and sanitize gen->arena. */
878 JS_ASSERT(!gen
->arena
.next
);
879 JS_ASSERT(arena
->next
== &gen
->arena
);
880 JS_ASSERT(cx
->stackPool
.current
== &gen
->arena
);
881 cx
->stackPool
.current
= arena
;
884 if (gen
->frame
.flags
& JSFRAME_YIELDING
) {
885 /* Yield cannot fail, throw or be called on closing. */
887 JS_ASSERT(!cx
->throwing
);
888 JS_ASSERT(gen
->state
== JSGEN_RUNNING
);
889 JS_ASSERT(op
!= JSGENOP_CLOSE
);
890 gen
->frame
.flags
&= ~JSFRAME_YIELDING
;
891 gen
->state
= JSGEN_OPEN
;
892 *rval
= gen
->frame
.rval
;
896 gen
->state
= JSGEN_CLOSED
;
899 /* Returned, explicitly or by falling off the end. */
900 if (op
== JSGENOP_CLOSE
) {
904 return js_ThrowStopIteration(cx
, obj
);
908 * An error, silent termination by branch callback or an exception.
909 * Propagate the condition to the caller.
915 CloseGenerator(JSContext
*cx
, JSObject
*obj
)
920 JS_ASSERT(STOBJ_GET_CLASS(obj
) == &js_GeneratorClass
);
921 gen
= (JSGenerator
*) JS_GetPrivate(cx
, obj
);
923 /* Generator prototype object. */
927 if (gen
->state
== JSGEN_CLOSED
)
930 /* SendToGenerator always sets *rval to JSVAL_VOID for JSGENOP_CLOSE. */
931 return SendToGenerator(cx
, JSGENOP_CLOSE
, obj
, gen
, JSVAL_VOID
, &junk
);
935 * Common subroutine of generator_(next|send|throw|close) methods.
938 generator_op(JSContext
*cx
, JSGeneratorOp op
, jsval
*vp
)
944 obj
= JSVAL_TO_OBJECT(vp
[1]);
945 if (!JS_InstanceOf(cx
, obj
, &js_GeneratorClass
, vp
+ 2))
948 gen
= (JSGenerator
*) JS_GetPrivate(cx
, obj
);
950 /* This happens when obj is the generator prototype. See bug 352885. */
951 goto closed_generator
;
954 if (gen
->state
== JSGEN_NEWBORN
) {
961 if (!JSVAL_IS_VOID(vp
[2])) {
962 js_ReportValueError(cx
, JSMSG_BAD_GENERATOR_SEND
,
963 JSDVG_SEARCH_STACK
, vp
[2], NULL
);
969 JS_ASSERT(op
== JSGENOP_CLOSE
);
970 gen
->state
= JSGEN_CLOSED
;
973 } else if (gen
->state
== JSGEN_CLOSED
) {
978 return js_ThrowStopIteration(cx
, obj
);
980 JS_SetPendingException(cx
, vp
[2]);
983 JS_ASSERT(op
== JSGENOP_CLOSE
);
988 arg
= (op
== JSGENOP_SEND
|| op
== JSGENOP_THROW
)
991 if (!SendToGenerator(cx
, op
, obj
, gen
, arg
, vp
))
997 generator_send(JSContext
*cx
, uintN argc
, jsval
*vp
)
999 return generator_op(cx
, JSGENOP_SEND
, vp
);
1003 generator_next(JSContext
*cx
, uintN argc
, jsval
*vp
)
1005 return generator_op(cx
, JSGENOP_NEXT
, vp
);
1009 generator_throw(JSContext
*cx
, uintN argc
, jsval
*vp
)
1011 return generator_op(cx
, JSGENOP_THROW
, vp
);
1015 generator_close(JSContext
*cx
, uintN argc
, jsval
*vp
)
1017 return generator_op(cx
, JSGENOP_CLOSE
, vp
);
1020 static JSFunctionSpec generator_methods
[] = {
1021 JS_FN(js_iterator_str
, iterator_self
, 0,0,JSPROP_ROPERM
,0),
1022 JS_FN(js_next_str
, generator_next
, 0,0,JSPROP_ROPERM
,0),
1023 JS_FN(js_send_str
, generator_send
, 1,1,JSPROP_ROPERM
,0),
1024 JS_FN(js_throw_str
, generator_throw
, 1,1,JSPROP_ROPERM
,0),
1025 JS_FN(js_close_str
, generator_close
, 0,0,JSPROP_ROPERM
,0),
1029 #endif /* JS_HAS_GENERATORS */
1032 js_InitIteratorClasses(JSContext
*cx
, JSObject
*obj
)
1034 JSObject
*proto
, *stop
;
1036 /* Idempotency required: we initialize several things, possibly lazily. */
1037 if (!js_GetClassObject(cx
, obj
, JSProto_StopIteration
, &stop
))
1042 proto
= JS_InitClass(cx
, obj
, NULL
, &js_IteratorClass
, Iterator
, 2,
1043 NULL
, iterator_methods
, NULL
, NULL
);
1046 STOBJ_SET_SLOT(proto
, JSSLOT_ITER_STATE
, JSVAL_NULL
);
1048 #if JS_HAS_GENERATORS
1049 /* Initialize the generator internals if configured. */
1050 if (!JS_InitClass(cx
, obj
, NULL
, &js_GeneratorClass
, NULL
, 0,
1051 NULL
, generator_methods
, NULL
, NULL
)) {
1056 return JS_InitClass(cx
, obj
, NULL
, &js_StopIterationClass
, NULL
, 0,
1057 NULL
, NULL
, NULL
, NULL
);