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 */
54 #include "jsversion.h"
68 #if JS_HAS_XML_SUPPORT
72 #if JSSLOT_ITER_FLAGS >= JS_INITIAL_NSLOTS
73 #error JS_INITIAL_NSLOTS must be greater than JSSLOT_ITER_FLAGS.
79 CloseGenerator(JSContext
*cx
, JSObject
*genobj
);
84 * Shared code to close iterator's state either through an explicit call or
85 * when GC detects that the iterator is no longer reachable.
88 js_CloseNativeIterator(JSContext
*cx
, JSObject
*iterobj
)
93 JS_ASSERT(STOBJ_GET_CLASS(iterobj
) == &js_IteratorClass
);
95 /* Avoid double work if js_CloseNativeIterator was called on obj. */
96 state
= STOBJ_GET_SLOT(iterobj
, JSSLOT_ITER_STATE
);
97 if (JSVAL_IS_NULL(state
))
100 /* Protect against failure to fully initialize obj. */
101 iterable
= STOBJ_GET_PARENT(iterobj
);
103 #if JS_HAS_XML_SUPPORT
104 uintN flags
= JSVAL_TO_INT(STOBJ_GET_SLOT(iterobj
, JSSLOT_ITER_FLAGS
));
105 if ((flags
& JSITER_FOREACH
) && OBJECT_IS_XML(cx
, iterable
)) {
106 ((JSXMLObjectOps
*) iterable
->map
->ops
)->
107 enumerateValues(cx
, iterable
, JSENUMERATE_DESTROY
, &state
,
111 OBJ_ENUMERATE(cx
, iterable
, JSENUMERATE_DESTROY
, &state
, NULL
);
113 STOBJ_SET_SLOT(iterobj
, JSSLOT_ITER_STATE
, JSVAL_NULL
);
116 JSClass js_IteratorClass
= {
118 JSCLASS_HAS_RESERVED_SLOTS(2) | /* slots for state and flags */
119 JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator
),
120 JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
,
121 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, JS_FinalizeStub
,
122 JSCLASS_NO_OPTIONAL_MEMBERS
126 InitNativeIterator(JSContext
*cx
, JSObject
*iterobj
, JSObject
*obj
, uintN flags
)
131 JS_ASSERT(STOBJ_GET_CLASS(iterobj
) == &js_IteratorClass
);
133 /* Initialize iterobj in case of enumerate hook failure. */
134 STOBJ_SET_PARENT(iterobj
, obj
);
135 STOBJ_SET_SLOT(iterobj
, JSSLOT_ITER_STATE
, JSVAL_NULL
);
136 STOBJ_SET_SLOT(iterobj
, JSSLOT_ITER_FLAGS
, INT_TO_JSVAL(flags
));
137 if (!js_RegisterCloseableIterator(cx
, iterobj
))
143 #if JS_HAS_XML_SUPPORT
144 ((flags
& JSITER_FOREACH
) && OBJECT_IS_XML(cx
, obj
))
145 ? ((JSXMLObjectOps
*) obj
->map
->ops
)->
146 enumerateValues(cx
, obj
, JSENUMERATE_INIT
, &state
, NULL
, NULL
)
149 OBJ_ENUMERATE(cx
, obj
, JSENUMERATE_INIT
, &state
, NULL
);
153 STOBJ_SET_SLOT(iterobj
, JSSLOT_ITER_STATE
, state
);
154 if (flags
& JSITER_ENUMERATE
) {
156 * The enumerating iterator needs the original object to suppress
157 * enumeration of deleted or shadowed prototype properties. Since the
158 * enumerator never escapes to scripts, we use the prototype slot to
159 * store the original object.
161 JS_ASSERT(obj
!= iterobj
);
162 STOBJ_SET_PROTO(iterobj
, obj
);
168 Iterator(JSContext
*cx
, JSObject
*iterobj
, uintN argc
, jsval
*argv
, jsval
*rval
)
174 keyonly
= js_ValueToBoolean(argv
[1]);
175 flags
= keyonly
? 0 : JSITER_FOREACH
;
177 if (JS_IsConstructing(cx
)) {
178 /* XXX work around old valueOf call hidden beneath js_ValueToObject */
179 if (!JSVAL_IS_PRIMITIVE(argv
[0])) {
180 obj
= JSVAL_TO_OBJECT(argv
[0]);
182 obj
= js_ValueToNonNullObject(cx
, argv
[0]);
185 argv
[0] = OBJECT_TO_JSVAL(obj
);
187 return InitNativeIterator(cx
, iterobj
, obj
, flags
);
191 return js_ValueToIterator(cx
, flags
, rval
);
195 NewKeyValuePair(JSContext
*cx
, jsid key
, jsval val
, jsval
*rval
)
198 JSTempValueRooter tvr
;
201 vec
[0] = ID_TO_VALUE(key
);
204 JS_PUSH_TEMP_ROOT(cx
, 2, vec
, &tvr
);
205 aobj
= js_NewArrayObject(cx
, 2, vec
);
206 *rval
= OBJECT_TO_JSVAL(aobj
);
207 JS_POP_TEMP_ROOT(cx
, &tvr
);
213 IteratorNextImpl(JSContext
*cx
, JSObject
*obj
, jsval
*rval
)
221 JS_ASSERT(OBJ_GET_CLASS(cx
, obj
) == &js_IteratorClass
);
223 iterable
= OBJ_GET_PARENT(cx
, obj
);
225 state
= STOBJ_GET_SLOT(obj
, JSSLOT_ITER_STATE
);
226 if (JSVAL_IS_NULL(state
))
229 flags
= JSVAL_TO_INT(STOBJ_GET_SLOT(obj
, JSSLOT_ITER_FLAGS
));
230 JS_ASSERT(!(flags
& JSITER_ENUMERATE
));
231 foreach
= (flags
& JSITER_FOREACH
) != 0;
233 #if JS_HAS_XML_SUPPORT
234 (foreach
&& OBJECT_IS_XML(cx
, iterable
))
235 ? ((JSXMLObjectOps
*) iterable
->map
->ops
)->
236 enumerateValues(cx
, iterable
, JSENUMERATE_NEXT
, &state
,
240 OBJ_ENUMERATE(cx
, iterable
, JSENUMERATE_NEXT
, &state
, &id
);
244 STOBJ_SET_SLOT(obj
, JSSLOT_ITER_STATE
, state
);
245 if (JSVAL_IS_NULL(state
))
249 #if JS_HAS_XML_SUPPORT
250 if (!OBJECT_IS_XML(cx
, iterable
) &&
251 !OBJ_GET_PROPERTY(cx
, iterable
, id
, rval
)) {
255 if (!NewKeyValuePair(cx
, id
, *rval
, rval
))
258 *rval
= ID_TO_VALUE(id
);
263 JS_ASSERT(STOBJ_GET_SLOT(obj
, JSSLOT_ITER_STATE
) == JSVAL_NULL
);
269 js_ThrowStopIteration(JSContext
*cx
)
273 JS_ASSERT(!JS_IsExceptionPending(cx
));
274 if (js_FindClassObject(cx
, NULL
, INT_TO_JSID(JSProto_StopIteration
), &v
))
275 JS_SetPendingException(cx
, v
);
280 iterator_next(JSContext
*cx
, uintN argc
, jsval
*vp
)
284 obj
= JS_THIS_OBJECT(cx
, vp
);
285 if (!JS_InstanceOf(cx
, obj
, &js_IteratorClass
, vp
+ 2))
288 if (!IteratorNextImpl(cx
, obj
, vp
))
291 if (*vp
== JSVAL_HOLE
) {
293 js_ThrowStopIteration(cx
);
300 iterator_self(JSContext
*cx
, uintN argc
, jsval
*vp
)
302 *vp
= JS_THIS(cx
, vp
);
303 return !JSVAL_IS_NULL(*vp
);
306 #define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT)
308 static JSFunctionSpec iterator_methods
[] = {
309 JS_FN(js_iterator_str
, iterator_self
, 0,JSPROP_ROPERM
),
310 JS_FN(js_next_str
, iterator_next
, 0,JSPROP_ROPERM
),
315 js_GetNativeIteratorFlags(JSContext
*cx
, JSObject
*iterobj
)
317 if (OBJ_GET_CLASS(cx
, iterobj
) != &js_IteratorClass
)
319 return JSVAL_TO_INT(STOBJ_GET_SLOT(iterobj
, JSSLOT_ITER_FLAGS
));
323 * Call ToObject(v).__iterator__(keyonly) if ToObject(v).__iterator__ exists.
324 * Otherwise construct the default iterator.
326 JS_FRIEND_API(JSBool
)
327 js_ValueToIterator(JSContext
*cx
, uintN flags
, jsval
*vp
)
330 JSTempValueRooter tvr
;
333 JSExtendedClass
*xclasp
;
338 JS_ASSERT(!(flags
& ~(JSITER_ENUMERATE
|
342 /* JSITER_KEYVALUE must always come with JSITER_FOREACH */
343 JS_ASSERT(!(flags
& JSITER_KEYVALUE
) || (flags
& JSITER_FOREACH
));
345 /* XXX work around old valueOf call hidden beneath js_ValueToObject */
346 if (!JSVAL_IS_PRIMITIVE(*vp
)) {
347 obj
= JSVAL_TO_OBJECT(*vp
);
350 * Enumerating over null and undefined gives an empty enumerator.
351 * This is contrary to ECMA-262 9.9 ToObject, invoked from step 3 of
352 * the first production in 12.6.4 and step 4 of the second production,
353 * but it's "web JS" compatible.
355 if ((flags
& JSITER_ENUMERATE
)) {
356 if (!js_ValueToObject(cx
, *vp
, &obj
))
361 obj
= js_ValueToNonNullObject(cx
, *vp
);
368 JS_PUSH_TEMP_ROOT_OBJECT(cx
, obj
, &tvr
);
370 clasp
= OBJ_GET_CLASS(cx
, obj
);
371 if ((clasp
->flags
& JSCLASS_IS_EXTENDED
) &&
372 (xclasp
= (JSExtendedClass
*) clasp
)->iteratorObject
) {
373 iterobj
= xclasp
->iteratorObject(cx
, obj
, !(flags
& JSITER_FOREACH
));
376 *vp
= OBJECT_TO_JSVAL(iterobj
);
378 atom
= cx
->runtime
->atomState
.iteratorAtom
;
379 #if JS_HAS_XML_SUPPORT
380 if (OBJECT_IS_XML(cx
, obj
)) {
381 if (!js_GetXMLFunction(cx
, obj
, ATOM_TO_JSID(atom
), vp
))
386 if (!OBJ_GET_PROPERTY(cx
, obj
, ATOM_TO_JSID(atom
), vp
))
390 if (JSVAL_IS_VOID(*vp
)) {
393 * Fail over to the default enumerating native iterator.
395 * Create iterobj with a NULL parent to ensure that we use the
396 * correct scope chain to lookup the iterator's constructor. Since
397 * we use the parent slot to keep track of the iterable, we must
400 iterobj
= js_NewObject(cx
, &js_IteratorClass
, NULL
, NULL
, 0);
404 /* Store in *vp to protect it from GC (callers must root vp). */
405 *vp
= OBJECT_TO_JSVAL(iterobj
);
407 if (!InitNativeIterator(cx
, iterobj
, obj
, flags
))
410 arg
= BOOLEAN_TO_JSVAL((flags
& JSITER_FOREACH
) == 0);
411 if (!js_InternalInvoke(cx
, obj
, *vp
, JSINVOKE_ITERATOR
, 1, &arg
,
415 if (JSVAL_IS_PRIMITIVE(*vp
)) {
416 const char *printable
= js_AtomToPrintableString(cx
, atom
);
418 js_ReportValueError2(cx
, JSMSG_BAD_ITERATOR_RETURN
,
419 JSDVG_SEARCH_STACK
, *vp
, NULL
,
430 JS_POP_TEMP_ROOT(cx
, &tvr
);
437 JS_FRIEND_API(JSBool
) JS_FASTCALL
438 js_CloseIterator(JSContext
*cx
, jsval v
)
443 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v
));
444 obj
= JSVAL_TO_OBJECT(v
);
445 clasp
= OBJ_GET_CLASS(cx
, obj
);
447 if (clasp
== &js_IteratorClass
) {
448 js_CloseNativeIterator(cx
, obj
);
450 #if JS_HAS_GENERATORS
451 else if (clasp
== &js_GeneratorClass
) {
452 if (!CloseGenerator(cx
, obj
))
460 CallEnumeratorNext(JSContext
*cx
, JSObject
*iterobj
, uintN flags
, jsval
*rval
)
462 JSObject
*obj
, *origobj
;
469 JSExtendedClass
*xclasp
;
473 JS_ASSERT(flags
& JSITER_ENUMERATE
);
474 JS_ASSERT(STOBJ_GET_CLASS(iterobj
) == &js_IteratorClass
);
476 obj
= STOBJ_GET_PARENT(iterobj
);
477 origobj
= STOBJ_GET_PROTO(iterobj
);
478 state
= STOBJ_GET_SLOT(iterobj
, JSSLOT_ITER_STATE
);
479 if (JSVAL_IS_NULL(state
))
482 foreach
= (flags
& JSITER_FOREACH
) != 0;
483 #if JS_HAS_XML_SUPPORT
485 * Treat an XML object specially only when it starts the prototype chain.
486 * Otherwise we need to do the usual deleted and shadowed property checks.
488 if (obj
== origobj
&& OBJECT_IS_XML(cx
, obj
)) {
490 JSXMLObjectOps
*xmlops
= (JSXMLObjectOps
*) obj
->map
->ops
;
492 if (!xmlops
->enumerateValues(cx
, obj
, JSENUMERATE_NEXT
, &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
))
507 if (!OBJ_ENUMERATE(cx
, obj
, JSENUMERATE_NEXT
, &state
, &id
))
510 STOBJ_SET_SLOT(iterobj
, JSSLOT_ITER_STATE
, state
);
511 if (JSVAL_IS_NULL(state
)) {
512 #if JS_HAS_XML_SUPPORT
513 if (OBJECT_IS_XML(cx
, obj
)) {
515 * We just finished enumerating an XML obj that is present on
516 * the prototype chain of a non-XML origobj. Stop further
517 * prototype chain searches because XML objects don't
518 * enumerate prototypes.
520 JS_ASSERT(origobj
!= obj
);
521 JS_ASSERT(!OBJECT_IS_XML(cx
, origobj
));
525 obj
= OBJ_GET_PROTO(cx
, obj
);
527 STOBJ_SET_PARENT(iterobj
, obj
);
528 if (!OBJ_ENUMERATE(cx
, obj
, JSENUMERATE_INIT
, &state
, NULL
))
530 STOBJ_SET_SLOT(iterobj
, JSSLOT_ITER_STATE
, state
);
531 if (!JSVAL_IS_NULL(state
))
538 /* Skip properties not in obj when looking from origobj. */
539 if (!OBJ_LOOKUP_PROPERTY(cx
, origobj
, id
, &obj2
, &prop
))
543 OBJ_DROP_PROPERTY(cx
, obj2
, prop
);
546 * If the id was found in a prototype object or an unrelated object
547 * (specifically, not in an inner object for obj), skip it. This step
548 * means that all OBJ_LOOKUP_PROPERTY implementations must return an
549 * object further along on the prototype chain, or else possibly an
550 * object returned by the JSExtendedClass.outerObject optional hook.
554 clasp
= OBJ_GET_CLASS(cx
, obj2
);
555 if (clasp
->flags
& JSCLASS_IS_EXTENDED
) {
556 xclasp
= (JSExtendedClass
*) clasp
;
557 cond
= xclasp
->outerObject
&&
558 xclasp
->outerObject(cx
, obj2
) == obj
;
565 /* Get property querying the original object. */
566 if (!OBJ_GET_PROPERTY(cx
, origobj
, id
, rval
))
572 if (flags
& JSITER_KEYVALUE
) {
573 if (!NewKeyValuePair(cx
, id
, *rval
, rval
))
577 /* Make rval a string for uniformity and compatibility. */
578 str
= js_ValueToString(cx
, ID_TO_VALUE(id
));
581 *rval
= STRING_TO_JSVAL(str
);
586 JS_ASSERT(STOBJ_GET_SLOT(iterobj
, JSSLOT_ITER_STATE
) == JSVAL_NULL
);
591 JS_FRIEND_API(JSBool
)
592 js_CallIteratorNext(JSContext
*cx
, JSObject
*iterobj
, jsval
*rval
)
596 /* Fast path for native iterators */
597 if (OBJ_GET_CLASS(cx
, iterobj
) == &js_IteratorClass
) {
598 flags
= JSVAL_TO_INT(STOBJ_GET_SLOT(iterobj
, JSSLOT_ITER_FLAGS
));
599 if (flags
& JSITER_ENUMERATE
)
600 return CallEnumeratorNext(cx
, iterobj
, flags
, rval
);
603 * Call next directly as all the methods of the native iterator are
604 * read-only and permanent.
606 if (!IteratorNextImpl(cx
, iterobj
, rval
))
609 jsid id
= ATOM_TO_JSID(cx
->runtime
->atomState
.nextAtom
);
611 if (!JS_GetMethodById(cx
, iterobj
, id
, &iterobj
, rval
))
613 if (!js_InternalCall(cx
, iterobj
, *rval
, 0, NULL
, rval
)) {
614 /* Check for StopIteration. */
615 if (!cx
->throwing
|| !js_ValueIsStopIteration(cx
->exception
))
618 /* Inline JS_ClearPendingException(cx). */
619 cx
->throwing
= JS_FALSE
;
620 cx
->exception
= JSVAL_VOID
;
630 stopiter_hasInstance(JSContext
*cx
, JSObject
*obj
, jsval v
, JSBool
*bp
)
632 *bp
= js_ValueIsStopIteration(v
);
636 JSClass js_StopIterationClass
= {
637 js_StopIteration_str
,
638 JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration
),
639 JS_PropertyStub
, JS_PropertyStub
,
640 JS_PropertyStub
, JS_PropertyStub
,
641 JS_EnumerateStub
, JS_ResolveStub
,
642 JS_ConvertStub
, JS_FinalizeStub
,
645 NULL
, stopiter_hasInstance
,
649 #if JS_HAS_GENERATORS
652 generator_finalize(JSContext
*cx
, JSObject
*obj
)
656 gen
= (JSGenerator
*) JS_GetPrivate(cx
, obj
);
659 * gen can be open on shutdown when close hooks are ignored or when
660 * the embedding cancels scheduled close hooks.
662 JS_ASSERT(gen
->state
== JSGEN_NEWBORN
|| gen
->state
== JSGEN_CLOSED
||
663 gen
->state
== JSGEN_OPEN
);
669 generator_trace(JSTracer
*trc
, JSObject
*obj
)
673 gen
= (JSGenerator
*) JS_GetPrivate(trc
->context
, obj
);
678 * js_TraceStackFrame does not recursively trace the down-linked frame
679 * chain, so we insist that gen->frame has no parent to trace when the
680 * generator is not running.
682 JS_ASSERT_IF(gen
->state
!= JSGEN_RUNNING
&& gen
->state
!= JSGEN_CLOSING
,
686 * FIXME be 390950. Generator's frame is a part of the JS stack when the
687 * generator is running or closing. Thus tracing the frame in this case
688 * here duplicates the work done in js_TraceContext.
690 js_TraceStackFrame(trc
, &gen
->frame
);
693 JSClass js_GeneratorClass
= {
695 JSCLASS_HAS_PRIVATE
| JSCLASS_IS_ANONYMOUS
|
696 JSCLASS_MARK_IS_TRACE
| JSCLASS_HAS_CACHED_PROTO(JSProto_Generator
),
697 JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
,
698 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, generator_finalize
,
699 NULL
, NULL
, NULL
, NULL
,
700 NULL
, NULL
, JS_CLASS_TRACE(generator_trace
), NULL
704 * Called from the JSOP_GENERATOR case in the interpreter, with fp referring
705 * to the frame by which the generator function was activated. Create a new
706 * JSGenerator object, which contains its own JSStackFrame that we populate
707 * from *fp. We know that upon return, the JSOP_GENERATOR opcode will return
708 * from the activation in fp, so we can steal away fp->callobj and fp->argsobj
709 * if they are non-null.
712 js_NewGenerator(JSContext
*cx
, JSStackFrame
*fp
)
715 uintN argc
, nargs
, nslots
;
719 /* After the following return, failing control flow must goto bad. */
720 obj
= js_NewObject(cx
, &js_GeneratorClass
, NULL
, NULL
, 0);
724 /* Load and compute stack slot counts. */
726 nargs
= JS_MAX(argc
, fp
->fun
->nargs
);
727 nslots
= 2 + nargs
+ fp
->script
->nslots
;
729 /* Allocate obj's private data struct. */
730 gen
= (JSGenerator
*)
731 JS_malloc(cx
, sizeof(JSGenerator
) + (nslots
- 1) * sizeof(jsval
));
737 /* Steal away objects reflecting fp and point them at gen->frame. */
738 gen
->frame
.callobj
= fp
->callobj
;
740 JS_SetPrivate(cx
, fp
->callobj
, &gen
->frame
);
743 gen
->frame
.argsobj
= fp
->argsobj
;
745 JS_SetPrivate(cx
, fp
->argsobj
, &gen
->frame
);
749 /* These two references can be shared with fp until it goes away. */
750 gen
->frame
.varobj
= fp
->varobj
;
751 gen
->frame
.thisp
= fp
->thisp
;
753 /* Copy call-invariant script and function references. */
754 gen
->frame
.script
= fp
->script
;
755 gen
->frame
.callee
= fp
->callee
;
756 gen
->frame
.fun
= fp
->fun
;
758 /* Use slots to carve space out of gen->slots. */
760 gen
->arena
.next
= NULL
;
761 gen
->arena
.base
= (jsuword
) slots
;
762 gen
->arena
.limit
= gen
->arena
.avail
= (jsuword
) (slots
+ nslots
);
764 /* Copy rval, argv and vars. */
765 gen
->frame
.rval
= fp
->rval
;
766 memcpy(slots
, fp
->argv
- 2, (2 + nargs
) * sizeof(jsval
));
767 gen
->frame
.argc
= nargs
;
768 gen
->frame
.argv
= slots
+ 2;
770 memcpy(slots
, fp
->slots
, fp
->script
->nfixed
* sizeof(jsval
));
772 /* Initialize or copy virtual machine state. */
773 gen
->frame
.down
= NULL
;
774 gen
->frame
.annotation
= NULL
;
775 gen
->frame
.scopeChain
= fp
->scopeChain
;
777 gen
->frame
.imacpc
= NULL
;
778 gen
->frame
.slots
= slots
;
779 JS_ASSERT(StackBase(fp
) == fp
->regs
->sp
);
780 gen
->savedRegs
.sp
= slots
+ fp
->script
->nfixed
;
781 gen
->savedRegs
.pc
= fp
->regs
->pc
;
782 gen
->frame
.regs
= &gen
->savedRegs
;
784 /* Copy remaining state (XXX sharp* and xml* should be local vars). */
785 gen
->frame
.sharpDepth
= 0;
786 gen
->frame
.sharpArray
= NULL
;
787 gen
->frame
.flags
= (fp
->flags
& ~JSFRAME_ROOTED_ARGV
) | JSFRAME_GENERATOR
;
788 gen
->frame
.dormantNext
= NULL
;
789 gen
->frame
.xmlNamespace
= NULL
;
790 gen
->frame
.blockChain
= NULL
;
792 /* Note that gen is newborn. */
793 gen
->state
= JSGEN_NEWBORN
;
795 if (!JS_SetPrivate(cx
, obj
, gen
)) {
802 cx
->weakRoots
.newborn
[GCX_OBJECT
] = NULL
;
806 typedef enum JSGeneratorOp
{
814 * Start newborn or restart yielding generator and perform the requested
815 * operation inside its frame.
818 SendToGenerator(JSContext
*cx
, JSGeneratorOp op
, JSObject
*obj
,
819 JSGenerator
*gen
, jsval arg
)
825 if (gen
->state
== JSGEN_RUNNING
|| gen
->state
== JSGEN_CLOSING
) {
826 js_ReportValueError(cx
, JSMSG_NESTING_GENERATOR
,
827 JSDVG_SEARCH_STACK
, OBJECT_TO_JSVAL(obj
),
828 JS_GetFunctionId(gen
->frame
.fun
));
832 JS_ASSERT(gen
->state
== JSGEN_NEWBORN
|| gen
->state
== JSGEN_OPEN
);
836 if (gen
->state
== JSGEN_OPEN
) {
838 * Store the argument to send as the result of the yield
841 gen
->savedRegs
.sp
[-1] = arg
;
843 gen
->state
= JSGEN_RUNNING
;
847 JS_SetPendingException(cx
, arg
);
848 gen
->state
= JSGEN_RUNNING
;
852 JS_ASSERT(op
== JSGENOP_CLOSE
);
853 JS_SetPendingException(cx
, JSVAL_ARETURN
);
854 gen
->state
= JSGEN_CLOSING
;
858 /* Extend the current stack pool with gen->arena. */
859 arena
= cx
->stackPool
.current
;
860 JS_ASSERT(!arena
->next
);
861 JS_ASSERT(!gen
->arena
.next
);
862 JS_ASSERT(cx
->stackPool
.current
!= &gen
->arena
);
863 cx
->stackPool
.current
= arena
->next
= &gen
->arena
;
865 /* Push gen->frame around the interpreter activation. */
866 fp
= js_GetTopStackFrame(cx
);
867 cx
->fp
= &gen
->frame
;
868 gen
->frame
.down
= fp
;
869 ok
= js_Interpret(cx
);
871 gen
->frame
.down
= NULL
;
873 /* Retract the stack pool and sanitize gen->arena. */
874 JS_ASSERT(!gen
->arena
.next
);
875 JS_ASSERT(arena
->next
== &gen
->arena
);
876 JS_ASSERT(cx
->stackPool
.current
== &gen
->arena
);
877 cx
->stackPool
.current
= arena
;
880 if (gen
->frame
.flags
& JSFRAME_YIELDING
) {
881 /* Yield cannot fail, throw or be called on closing. */
883 JS_ASSERT(!cx
->throwing
);
884 JS_ASSERT(gen
->state
== JSGEN_RUNNING
);
885 JS_ASSERT(op
!= JSGENOP_CLOSE
);
886 gen
->frame
.flags
&= ~JSFRAME_YIELDING
;
887 gen
->state
= JSGEN_OPEN
;
891 gen
->frame
.rval
= JSVAL_VOID
;
892 gen
->state
= JSGEN_CLOSED
;
894 /* Returned, explicitly or by falling off the end. */
895 if (op
== JSGENOP_CLOSE
)
897 return js_ThrowStopIteration(cx
);
901 * An error, silent termination by operation callback or an exception.
902 * Propagate the condition to the caller.
908 CloseGenerator(JSContext
*cx
, JSObject
*obj
)
912 JS_ASSERT(STOBJ_GET_CLASS(obj
) == &js_GeneratorClass
);
913 gen
= (JSGenerator
*) JS_GetPrivate(cx
, obj
);
915 /* Generator prototype object. */
919 if (gen
->state
== JSGEN_CLOSED
)
922 return SendToGenerator(cx
, JSGENOP_CLOSE
, obj
, gen
, JSVAL_VOID
);
926 * Common subroutine of generator_(next|send|throw|close) methods.
929 generator_op(JSContext
*cx
, JSGeneratorOp op
, jsval
*vp
, uintN argc
)
935 obj
= JS_THIS_OBJECT(cx
, vp
);
936 if (!JS_InstanceOf(cx
, obj
, &js_GeneratorClass
, vp
+ 2))
939 gen
= (JSGenerator
*) JS_GetPrivate(cx
, obj
);
941 /* This happens when obj is the generator prototype. See bug 352885. */
942 goto closed_generator
;
945 if (gen
->state
== JSGEN_NEWBORN
) {
952 if (argc
>= 1 && !JSVAL_IS_VOID(vp
[2])) {
953 js_ReportValueError(cx
, JSMSG_BAD_GENERATOR_SEND
,
954 JSDVG_SEARCH_STACK
, vp
[2], NULL
);
960 JS_ASSERT(op
== JSGENOP_CLOSE
);
961 gen
->state
= JSGEN_CLOSED
;
964 } else if (gen
->state
== JSGEN_CLOSED
) {
969 return js_ThrowStopIteration(cx
);
971 JS_SetPendingException(cx
, argc
>= 1 ? vp
[2] : JSVAL_VOID
);
974 JS_ASSERT(op
== JSGENOP_CLOSE
);
979 arg
= ((op
== JSGENOP_SEND
|| op
== JSGENOP_THROW
) && argc
!= 0)
982 if (!SendToGenerator(cx
, op
, obj
, gen
, arg
))
984 *vp
= gen
->frame
.rval
;
989 generator_send(JSContext
*cx
, uintN argc
, jsval
*vp
)
991 return generator_op(cx
, JSGENOP_SEND
, vp
, argc
);
995 generator_next(JSContext
*cx
, uintN argc
, jsval
*vp
)
997 return generator_op(cx
, JSGENOP_NEXT
, vp
, argc
);
1001 generator_throw(JSContext
*cx
, uintN argc
, jsval
*vp
)
1003 return generator_op(cx
, JSGENOP_THROW
, vp
, argc
);
1007 generator_close(JSContext
*cx
, uintN argc
, jsval
*vp
)
1009 return generator_op(cx
, JSGENOP_CLOSE
, vp
, argc
);
1012 static JSFunctionSpec generator_methods
[] = {
1013 JS_FN(js_iterator_str
, iterator_self
, 0,JSPROP_ROPERM
),
1014 JS_FN(js_next_str
, generator_next
, 0,JSPROP_ROPERM
),
1015 JS_FN(js_send_str
, generator_send
, 1,JSPROP_ROPERM
),
1016 JS_FN(js_throw_str
, generator_throw
, 1,JSPROP_ROPERM
),
1017 JS_FN(js_close_str
, generator_close
, 0,JSPROP_ROPERM
),
1021 #endif /* JS_HAS_GENERATORS */
1024 js_InitIteratorClasses(JSContext
*cx
, JSObject
*obj
)
1026 JSObject
*proto
, *stop
;
1028 /* Idempotency required: we initialize several things, possibly lazily. */
1029 if (!js_GetClassObject(cx
, obj
, JSProto_StopIteration
, &stop
))
1034 proto
= JS_InitClass(cx
, obj
, NULL
, &js_IteratorClass
, Iterator
, 2,
1035 NULL
, iterator_methods
, NULL
, NULL
);
1038 STOBJ_SET_SLOT(proto
, JSSLOT_ITER_STATE
, JSVAL_NULL
);
1040 #if JS_HAS_GENERATORS
1041 /* Initialize the generator internals if configured. */
1042 if (!JS_InitClass(cx
, obj
, NULL
, &js_GeneratorClass
, NULL
, 0,
1043 NULL
, generator_methods
, NULL
, NULL
)) {
1048 return JS_InitClass(cx
, obj
, NULL
, &js_StopIterationClass
, NULL
, 0,
1049 NULL
, NULL
, NULL
, NULL
);