sp[-2] = JS_NewCatchOffset(ctx, 0);
sp[-1] = value;
sp[0] = JS_NewBool(ctx, done);
+ if (done) {
+ /* Iterator exhausted via {done:true}: drop the iterator object (stack
+ layout at the for-await `next` call is iter_obj,next,catch_offset,result
+ so iter_obj is sp[-4]) so the trailing OP_iterator_close skips calling
+ return(). Mirrors js_for_of_next, which nulls the iterator on done.
+ Per spec AsyncIteratorClose must NOT run on normal completion. This op
+ is emitted only by the for-await-of loop, so the layout is fixed. */
+ JS_FreeValue(ctx, sp[-4]);
+ sp[-4] = JS_UNDEFINED;
+ }
return 0;
}
int using_label_catch;
int using_label_end;
uint8_t has_iterator : 1;
+ uint8_t is_async_iterator : 1;
uint8_t is_regular_stmt : 1; /* i.e. not a loop statement */
uint8_t has_using : 1; /* the block contains a using declaration */
uint8_t is_await_using : 1; /* the block has await using declarations */
be->label_finally = -1;
be->scope_level = fd->scope_level;
be->has_iterator = FALSE;
+ be->is_async_iterator = FALSE;
be->is_regular_stmt = FALSE;
be->has_using = FALSE;
be->is_await_using = FALSE;
fd->top_break = be->prev;
}
+static void emit_iterator_close(JSParseState *s, BOOL is_async,
+ BOOL preserve_completion)
+{
+ int label_no_return, label_done;
+
+ if (!is_async) {
+ if (preserve_completion) {
+ emit_op(s, OP_rot3r);
+ emit_op(s, OP_undefined); /* dummy catch offset */
+ }
+ emit_op(s, OP_iterator_close);
+ return;
+ }
+
+ if (preserve_completion) {
+ /* iter_obj next completion -> completion iter_obj */
+ emit_op(s, OP_nip);
+ emit_op(s, OP_swap);
+ } else {
+ /* iter_obj next catch_offset -> iter_obj */
+ emit_op(s, OP_drop);
+ emit_op(s, OP_drop);
+ }
+
+ emit_op(s, OP_dup);
+ emit_op(s, OP_is_undefined);
+ label_done = emit_goto(s, OP_if_true, -1);
+
+ emit_op(s, OP_get_field2);
+ emit_atom(s, JS_ATOM_return);
+ /* completion? iter_obj return_func */
+ emit_op(s, OP_dup);
+ emit_op(s, OP_is_undefined_or_null);
+ label_no_return = emit_goto(s, OP_if_true, -1);
+ emit_op(s, OP_call_method);
+ emit_u16(s, 0);
+ emit_op(s, OP_await);
+ emit_goto(s, OP_goto, label_done);
+ emit_label(s, label_no_return);
+ emit_op(s, OP_drop);
+
+ emit_label(s, label_done);
+ emit_op(s, OP_drop);
+}
+
static __exception int emit_break(JSParseState *s, JSAtom name, int is_cont)
{
BlockEnv *top;
}
i = 0;
if (top->has_iterator) {
- emit_op(s, OP_iterator_close);
+ emit_iterator_close(s, top->is_async_iterator, FALSE);
i += 3;
}
for(; i < top->drop_count; i++)
emit_op(s, OP_nip_catch);
/* stack: iter_obj next ret_val */
if (top->has_iterator) {
- if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {
- int label_next, label_next2;
- emit_op(s, OP_nip); /* next */
- emit_op(s, OP_swap);
- emit_op(s, OP_get_field2);
- emit_atom(s, JS_ATOM_return);
- /* stack: iter_obj return_func */
- emit_op(s, OP_dup);
- emit_op(s, OP_is_undefined_or_null);
- label_next = emit_goto(s, OP_if_true, -1);
- emit_op(s, OP_call_method);
- emit_u16(s, 0);
- emit_op(s, OP_iterator_check_object);
- emit_op(s, OP_await);
- label_next2 = emit_goto(s, OP_goto, -1);
- emit_label(s, label_next);
- emit_op(s, OP_drop);
- emit_label(s, label_next2);
- emit_op(s, OP_drop);
- } else {
- emit_op(s, OP_rot3r);
- emit_op(s, OP_undefined); /* dummy catch offset */
- emit_op(s, OP_iterator_close);
- }
+ emit_iterator_close(s, top->is_async_iterator, TRUE);
} else if (top->has_using) {
/* Dispose using variables. The return value is on TOS.
stack: ret_val */
that a yield in the expression does not try to close a
not-yet-created iterator */
break_entry.has_iterator = TRUE;
+ break_entry.is_async_iterator = is_async;
break_entry.drop_count += 2;
if (is_async)
emit_op(s, OP_for_await_of_start);
emit_label(s, label_break);
if (is_for_of) {
/* close and drop enum_rec */
- emit_op(s, OP_iterator_close);
+ emit_iterator_close(s, is_async, FALSE);
} else {
emit_op(s, OP_drop);
}