410
arg = BOOLEAN_TO_JSVAL((flags & JSITER_FOREACH) == 0);
412
425
JS_PUSH_SINGLE_TEMP_ROOT(cx, obj, &tvr);
413
426
if (!JS_GetMethodById(cx, obj, ATOM_TO_JSID(atom), &obj, &fval))
415
428
if (JSVAL_IS_VOID(fval)) {
416
/* Fail over to the default native iterator, called directly. */
417
if (!js_DefaultIterator(cx, obj, 1, &arg, &rval))
429
/* Fail over to the default enumerating native iterator. */
430
if (!js_NewNativeIterator(cx, obj,
431
(flags & JSITER_FOREACH) | JSITER_ENUMERATE,
436
arg = BOOLEAN_TO_JSVAL((flags & JSITER_FOREACH) == 0);
437
if (!js_InternalInvoke(cx, obj, fval, JSINVOKE_ITERATOR, 1, &arg,
419
441
if (JSVAL_IS_PRIMITIVE(rval))
420
442
goto bad_iterator;
421
iterobj = JSVAL_TO_OBJECT(rval);
422
JS_ASSERT(OBJ_GET_CLASS(cx, iterobj) == &js_IteratorClass);
423
iterobj->slots[JSSLOT_ITER_FLAGS] |= INT_TO_JSVAL(JSITER_ENUMERATE);
427
if (!js_InternalInvoke(cx, obj, fval, JSINVOKE_ITERATOR, 1, &arg, &rval))
430
if (JSVAL_IS_PRIMITIVE(rval))
433
445
iterobj = JSVAL_TO_OBJECT(rval);
436
* If __iterator__ is the default native method, the native iterator it
437
* returns can be flagged as hidden from script access. This flagging is
438
* predicated on js_ValueToIterator being called only by the for-in loop
439
* code -- the js_CloseNativeIterator early-finalization optimization
440
* based on it will break badly if script can reach iterobj.
442
if (OBJ_GET_CLASS(cx, iterobj) == &js_IteratorClass &&
443
VALUE_IS_FUNCTION(cx, fval)) {
444
fun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(fval));
445
if (!FUN_INTERPRETED(fun) && fun->u.n.native == js_DefaultIterator)
446
iterobj->slots[JSSLOT_ITER_FLAGS] |= INT_TO_JSVAL(JSITER_ENUMERATE);
450
448
JS_POP_TEMP_ROOT(cx, &tvr);
712
typedef enum JSGeneratorOp {
720
* Start newborn or restart yielding generator and perform the requested
721
* operation inside its frame.
760
generator_send(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
724
SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj,
725
JSGenerator *gen, jsval arg, jsval *rval)
765
727
JSStackFrame *fp;
770
if (!JS_InstanceOf(cx, obj, &js_GeneratorClass, argv))
773
gen = (JSGenerator *)JS_GetPrivate(cx, obj);
774
if (!gen || gen->state == JSGEN_CLOSED)
775
return !JS_IsExceptionPending(cx) && js_ThrowStopIteration(cx, obj);
777
if (gen->state == JSGEN_NEWBORN && argc != 0 && !JSVAL_IS_VOID(argv[0])) {
778
str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, argv[0], NULL);
780
JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL,
781
JSMSG_BAD_GENERATOR_SEND,
782
JSSTRING_CHARS(str));
732
JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN);
736
if (gen->state == JSGEN_OPEN) {
738
* Store the argument to send as the result of the yield
741
gen->frame.sp[-1] = arg;
743
gen->state = JSGEN_RUNNING;
747
JS_SetPendingException(cx, arg);
748
gen->state = JSGEN_RUNNING;
752
JS_ASSERT(op == JSGENOP_CLOSE);
753
JS_SetPendingException(cx, JSVAL_ARETURN);
754
gen->state = JSGEN_CLOSING;
758
/* Extend the current stack pool with gen->arena. */
759
arena = cx->stackPool.current;
760
JS_ASSERT(!arena->next);
761
JS_ASSERT(!gen->arena.next);
762
JS_ASSERT(cx->stackPool.current != &gen->arena);
763
cx->stackPool.current = arena->next = &gen->arena;
765
/* Push gen->frame around the interpreter activation. */
788
arena = cx->stackPool.current;
789
cx->stackPool.current = &gen->arena;
790
767
cx->fp = &gen->frame;
791
768
gen->frame.down = fp;
793
/* Store the argument to send as the result of the yield expression. */
794
gen->frame.sp[-1] = (argc != 0) ? argv[0] : JSVAL_VOID;
795
769
ok = js_Interpret(cx, gen->frame.pc, &junk);
771
gen->frame.down = NULL;
773
/* Retract the stack pool and sanitize gen->arena. */
774
JS_ASSERT(!gen->arena.next);
775
JS_ASSERT(arena->next == &gen->arena);
776
JS_ASSERT(cx->stackPool.current == &gen->arena);
797
777
cx->stackPool.current = arena;
801
gen->state = JSGEN_CLOSED;
780
if (gen->frame.flags & JSFRAME_YIELDING) {
781
/* Yield cannot fail, throw or be called on closing. */
783
JS_ASSERT(!cx->throwing);
784
JS_ASSERT(gen->state == JSGEN_RUNNING);
785
JS_ASSERT(op != JSGENOP_CLOSE);
786
gen->frame.flags &= ~JSFRAME_YIELDING;
787
gen->state = JSGEN_OPEN;
788
*rval = gen->frame.rval;
805
if (!(gen->frame.flags & JSFRAME_YIELDING)) {
792
gen->state = JSGEN_CLOSED;
806
795
/* Returned, explicitly or by falling off the end. */
807
gen->state = JSGEN_CLOSED;
796
if (op == JSGENOP_CLOSE)
808
798
return js_ThrowStopIteration(cx, obj);
811
gen->state = JSGEN_RUNNING;
812
gen->frame.flags &= ~JSFRAME_YIELDING;
813
*rval = gen->frame.rval;
802
* An error, silent termination by branch callback or an exception.
803
* Propagate the condition to the caller.
809
* Execute gen's close hook after the GC detects that the object has become
813
js_CloseGeneratorObject(JSContext *cx, JSGenerator *gen)
815
/* We pass null as rval since SendToGenerator never uses it with CLOSE. */
816
return SendToGenerator(cx, JSGENOP_CLOSE, gen->obj, gen, JSVAL_VOID, NULL);
820
* Common subroutine of generator_(next|send|throw|close) methods.
823
generator_op(JSContext *cx, JSGeneratorOp op,
824
JSObject *obj, uintN argc, jsval *argv, jsval *rval)
830
if (!JS_InstanceOf(cx, obj, &js_GeneratorClass, argv))
833
gen = (JSGenerator *) JS_GetPrivate(cx, obj);
835
/* This happens when obj is the generator prototype. See bug 352885. */
836
goto closed_generator;
839
switch (gen->state) {
847
if (!JSVAL_IS_VOID(argv[0])) {
848
str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK,
851
JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL,
852
JSMSG_BAD_GENERATOR_SEND,
853
JSSTRING_CHARS(str));
860
JS_ASSERT(op == JSGENOP_CLOSE);
861
gen->state = JSGEN_CLOSED;
871
str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, argv[-1],
872
JS_GetFunctionId(gen->frame.fun));
874
JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL,
875
JSMSG_NESTING_GENERATOR,
876
JSSTRING_CHARS(str));
881
JS_ASSERT(gen->state == JSGEN_CLOSED);
887
return js_ThrowStopIteration(cx, obj);
889
JS_SetPendingException(cx, argv[0]);
892
JS_ASSERT(op == JSGENOP_CLOSE);
897
arg = (op == JSGENOP_SEND || op == JSGENOP_THROW)
900
if (!SendToGenerator(cx, op, obj, gen, arg, rval))
906
generator_send(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
909
return generator_op(cx, JSGENOP_SEND, obj, argc, argv, rval);
818
913
generator_next(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
821
return generator_send(cx, obj, 0, argv, rval);
916
return generator_op(cx, JSGENOP_NEXT, obj, argc, argv, rval);
825
920
generator_throw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
828
JS_SetPendingException(cx, argv[0]);
829
return generator_send(cx, obj, 0, argv, rval);
923
return generator_op(cx, JSGENOP_THROW, obj, argc, argv, rval);
833
927
generator_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
840
if (!js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_GeneratorExit),
845
JS_SetPendingException(cx, genexit);
846
if (generator_send(cx, obj, 0, argv, rval))
851
if (!JSVAL_IS_PRIMITIVE(exn)) {
852
clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(exn));
853
if (clasp == &js_GeneratorExitClass ||
854
clasp == &js_StopIterationClass) {
855
JS_ClearPendingException(cx);
861
str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, argv[-1], NULL);
863
JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL,
864
JSMSG_BAD_GENERATOR_EXIT,
865
JSSTRING_CHARS(str));
930
return generator_op(cx, JSGENOP_CLOSE, obj, argc, argv, rval);
870
933
static JSFunctionSpec generator_methods[] = {
871
{js_iterator_str, iterator_self, 0,0,0},
872
{js_next_str, generator_next, 0,0,0},
873
{js_send_str, generator_send, 1,0,0},
874
{js_throw_str, generator_throw, 1,0,0},
875
{js_close_str, generator_close, 0,JSPROP_READONLY|JSPROP_PERMANENT,0},
934
{js_iterator_str, iterator_self, 0,JSPROP_READONLY|JSPROP_PERMANENT,0},
935
{js_next_str, generator_next, 0,JSPROP_READONLY|JSPROP_PERMANENT,0},
936
{js_send_str, generator_send, 1,JSPROP_READONLY|JSPROP_PERMANENT,0},
937
{js_throw_str, generator_throw, 1,JSPROP_READONLY|JSPROP_PERMANENT,0},
938
{js_close_str, generator_close, 0,JSPROP_READONLY|JSPROP_PERMANENT,0},