Skip to content

Commit e581286

Browse files
committed
Fix 'return' handling with 'yield' in 'for of' or with finally blocks
Ref: bellard/quickjs@4bb8c35
1 parent 39901e2 commit e581286

File tree

3 files changed

+141
-76
lines changed

3 files changed

+141
-76
lines changed

quickjs-opcode.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ DEF( goto, 5, 0, 0, label) /* must come after if_true */
183183
DEF( catch, 5, 0, 1, label)
184184
DEF( gosub, 5, 0, 0, label) /* used to execute the finally block */
185185
DEF( ret, 1, 1, 0, none) /* used to return from the finally block */
186+
DEF( nip_catch, 1, 2, 1, none) /* catch ... a -> a */
186187

187188
DEF( to_object, 1, 1, 1, none)
188189
//DEF( to_string, 1, 1, 1, none)
@@ -209,7 +210,6 @@ DEF( for_of_next, 2, 3, 5, u8)
209210
DEF(iterator_check_object, 1, 1, 1, none)
210211
DEF(iterator_get_value_done, 1, 1, 2, none)
211212
DEF( iterator_close, 1, 3, 0, none)
212-
DEF(iterator_close_return, 1, 4, 4, none)
213213
DEF( iterator_next, 1, 4, 4, none)
214214
DEF( iterator_call, 2, 4, 5, u8)
215215
DEF( initial_yield, 1, 0, 0, none)

quickjs.c

Lines changed: 122 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@
7070
8: dump stdlib functions
7171
16: dump bytecode in hex
7272
32: dump line number table
73-
64: dump executed bytecode
73+
64: dump compute_stack_size
74+
128: dump executed bytecode
7475
*/
7576
//#define DUMP_BYTECODE (1)
7677
/* dump the occurence of the automatic GC */
@@ -14354,7 +14355,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj,
1435414355
size_t alloca_size;
1435514356
JSInlineCache *ic;
1435614357

14357-
#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 64)
14358+
#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 128)
1435814359
#define DUMP_BYTECODE_OR_DONT(pc) dump_single_byte_code(ctx, pc, b);
1435914360
#else
1436014361
#define DUMP_BYTECODE_OR_DONT(pc)
@@ -14463,7 +14464,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj,
1446314464
ctx = b->realm; /* set the current realm */
1446414465
ic = b->ic;
1446514466

14466-
#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 64)
14467+
#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 128)
1446714468
print_func_name(b);
1446814469
#endif
1446914470

@@ -15599,26 +15600,21 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj,
1559915600
}
1560015601
sp--;
1560115602
BREAK;
15602-
CASE(OP_iterator_close_return):
15603+
CASE(OP_nip_catch):
1560315604
{
1560415605
JSValue ret_val;
15605-
/* iter_obj next catch_offset ... ret_val ->
15606-
ret_eval iter_obj next catch_offset */
15606+
/* catch_offset ... ret_val -> ret_eval */
1560715607
ret_val = *--sp;
1560815608
while (sp > stack_buf &&
1560915609
JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_CATCH_OFFSET) {
1561015610
JS_FreeValue(ctx, *--sp);
1561115611
}
15612-
if (unlikely(sp < stack_buf + 3)) {
15613-
JS_ThrowInternalError(ctx, "iterator_close_return");
15612+
if (unlikely(sp == stack_buf)) {
15613+
JS_ThrowInternalError(ctx, "nip_catch");
1561415614
JS_FreeValue(ctx, ret_val);
1561515615
goto exception;
1561615616
}
15617-
sp[0] = sp[-1];
15618-
sp[-1] = sp[-2];
15619-
sp[-2] = sp[-3];
15620-
sp[-3] = ret_val;
15621-
sp++;
15617+
sp[-1] = ret_val;
1562215618
}
1562315619
BREAK;
1562415620

@@ -23876,7 +23872,6 @@ static __exception int emit_break(JSParseState *s, JSAtom name, int is_cont)
2387623872
static void emit_return(JSParseState *s, BOOL hasval)
2387723873
{
2387823874
BlockEnv *top;
23879-
int drop_count;
2388023875

2388123876
if (s->cur_func->func_kind != JS_FUNC_NORMAL) {
2388223877
if (!hasval) {
@@ -23890,60 +23885,49 @@ static void emit_return(JSParseState *s, BOOL hasval)
2389023885
}
2389123886
}
2389223887

23893-
drop_count = 0;
2389423888
top = s->cur_func->top_break;
2389523889
while (top != NULL) {
23896-
/* XXX: emit the appropriate OP_leave_scope opcodes? Probably not
23897-
required as all local variables will be closed upon returning
23898-
from JS_CallInternal, but not in the same order. */
23899-
if (top->has_iterator) {
23900-
/* with 'yield', the exact number of OP_drop to emit is
23901-
unknown, so we use a specific operation to look for
23902-
the catch offset */
23890+
if (top->has_iterator || top->label_finally != -1) {
2390323891
if (!hasval) {
2390423892
emit_op(s, OP_undefined);
2390523893
hasval = TRUE;
2390623894
}
23907-
emit_op(s, OP_iterator_close_return);
23908-
if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {
23909-
int label_next, label_next2;
23910-
23911-
emit_op(s, OP_drop); /* catch offset */
23912-
emit_op(s, OP_drop); /* next */
23913-
emit_op(s, OP_get_field2);
23914-
emit_atom(s, JS_ATOM_return);
23915-
emit_ic(s, JS_ATOM_return);
23916-
/* stack: iter_obj return_func */
23917-
emit_op(s, OP_dup);
23918-
emit_op(s, OP_is_undefined_or_null);
23919-
label_next = emit_goto(s, OP_if_true, -1);
23920-
emit_op(s, OP_call_method);
23921-
emit_u16(s, 0);
23922-
emit_op(s, OP_iterator_check_object);
23923-
emit_op(s, OP_await);
23924-
label_next2 = emit_goto(s, OP_goto, -1);
23925-
emit_label(s, label_next);
23926-
emit_op(s, OP_drop);
23927-
emit_label(s, label_next2);
23928-
emit_op(s, OP_drop);
23895+
/* Remove the stack elements up to and including the catch
23896+
offset. When 'yield' is used in an expression we have
23897+
no easy way to count them, so we use this specific
23898+
instruction instead. */
23899+
emit_op(s, OP_nip_catch);
23900+
/* stack: iter_obj next ret_val */
23901+
if (top->has_iterator) {
23902+
if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {
23903+
int label_next, label_next2;
23904+
emit_op(s, OP_nip); /* next */
23905+
emit_op(s, OP_swap);
23906+
emit_op(s, OP_get_field2);
23907+
emit_atom(s, JS_ATOM_return);
23908+
emit_ic(s, JS_ATOM_return);
23909+
/* stack: iter_obj return_func */
23910+
emit_op(s, OP_dup);
23911+
emit_op(s, OP_is_undefined_or_null);
23912+
label_next = emit_goto(s, OP_if_true, -1);
23913+
emit_op(s, OP_call_method);
23914+
emit_u16(s, 0);
23915+
emit_op(s, OP_iterator_check_object);
23916+
emit_op(s, OP_await);
23917+
label_next2 = emit_goto(s, OP_goto, -1);
23918+
emit_label(s, label_next);
23919+
emit_op(s, OP_drop);
23920+
emit_label(s, label_next2);
23921+
emit_op(s, OP_drop);
23922+
} else {
23923+
emit_op(s, OP_rot3r);
23924+
emit_op(s, OP_undefined); /* dummy catch offset */
23925+
emit_op(s, OP_iterator_close);
23926+
}
2392923927
} else {
23930-
emit_op(s, OP_iterator_close);
23931-
}
23932-
drop_count = -3;
23933-
}
23934-
drop_count += top->drop_count;
23935-
if (top->label_finally != -1) {
23936-
while(drop_count) {
23937-
/* must keep the stack top if hasval */
23938-
emit_op(s, hasval ? OP_nip : OP_drop);
23939-
drop_count--;
23928+
/* execute the "finally" block */
23929+
emit_goto(s, OP_gosub, top->label_finally);
2394023930
}
23941-
if (!hasval) {
23942-
/* must push return value to keep same stack size */
23943-
emit_op(s, OP_undefined);
23944-
hasval = TRUE;
23945-
}
23946-
emit_goto(s, OP_gosub, top->label_finally);
2394723931
}
2394823932
top = top->prev;
2394923933
}
@@ -30455,14 +30439,15 @@ typedef struct StackSizeState {
3045530439
int bc_len;
3045630440
int stack_len_max;
3045730441
uint16_t *stack_level_tab;
30442+
int32_t *catch_pos_tab;
3045830443
int *pc_stack;
3045930444
int pc_stack_len;
3046030445
int pc_stack_size;
3046130446
} StackSizeState;
3046230447

3046330448
/* 'op' is only used for error indication */
3046430449
static __exception int ss_check(JSContext *ctx, StackSizeState *s,
30465-
int pos, int op, int stack_len)
30450+
int pos, int op, int stack_len, int catch_pos)
3046630451
{
3046730452
if ((unsigned)pos >= s->bc_len) {
3046830453
JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);
@@ -30481,13 +30466,18 @@ static __exception int ss_check(JSContext *ctx, StackSizeState *s,
3048130466
JS_ThrowInternalError(ctx, "inconsistent stack size: %d %d (pc=%d)",
3048230467
s->stack_level_tab[pos], stack_len, pos);
3048330468
return -1;
30469+
} else if (s->catch_pos_tab[pos] != catch_pos) {
30470+
JS_ThrowInternalError(ctx, "inconsistent catch position: %d %d (pc=%d)",
30471+
s->catch_pos_tab[pos], catch_pos, pos);
30472+
return -1;
3048430473
} else {
3048530474
return 0;
3048630475
}
3048730476
}
3048830477

3048930478
/* mark as explored and store the stack size */
3049030479
s->stack_level_tab[pos] = stack_len;
30480+
s->catch_pos_tab[pos] = catch_pos;
3049130481

3049230482
/* queue the new PC to explore */
3049330483
if (js_resize_array(ctx, (void **)&s->pc_stack, sizeof(s->pc_stack[0]),
@@ -30502,7 +30492,7 @@ static __exception int compute_stack_size(JSContext *ctx,
3050230492
int *pstack_size)
3050330493
{
3050430494
StackSizeState s_s, *s = &s_s;
30505-
int i, diff, n_pop, pos_next, stack_len, pos, op;
30495+
int i, diff, n_pop, pos_next, stack_len, pos, op, catch_pos, catch_level;
3050630496
const JSOpCode *oi;
3050730497
const uint8_t *bc_buf;
3050830498

@@ -30515,24 +30505,32 @@ static __exception int compute_stack_size(JSContext *ctx,
3051530505
return -1;
3051630506
for(i = 0; i < s->bc_len; i++)
3051730507
s->stack_level_tab[i] = 0xffff;
30518-
s->stack_len_max = 0;
3051930508
s->pc_stack = NULL;
30509+
s->catch_pos_tab = js_malloc(ctx, sizeof(s->catch_pos_tab[0]) * s->bc_len);
30510+
if (!s->catch_pos_tab)
30511+
goto fail;
30512+
30513+
s->stack_len_max = 0;
3052030514
s->pc_stack_len = 0;
3052130515
s->pc_stack_size = 0;
3052230516

3052330517
/* breadth-first graph exploration */
30524-
if (ss_check(ctx, s, 0, OP_invalid, 0))
30518+
if (ss_check(ctx, s, 0, OP_invalid, 0, -1))
3052530519
goto fail;
3052630520

3052730521
while (s->pc_stack_len > 0) {
3052830522
pos = s->pc_stack[--s->pc_stack_len];
3052930523
stack_len = s->stack_level_tab[pos];
30524+
catch_pos = s->catch_pos_tab[pos];
3053030525
op = bc_buf[pos];
3053130526
if (op == 0 || op >= OP_COUNT) {
3053230527
JS_ThrowInternalError(ctx, "invalid opcode (op=%d, pc=%d)", op, pos);
3053330528
goto fail;
3053430529
}
3053530530
oi = &short_opcode_info(op);
30531+
#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 64)
30532+
printf("%5d: %10s %5d %5d\n", pos, oi->name, stack_len, catch_pos);
30533+
#endif
3053630534
pos_next = pos + oi->size;
3053730535
if (pos_next > s->bc_len) {
3053830536
JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);
@@ -30583,54 +30581,103 @@ static __exception int compute_stack_size(JSContext *ctx,
3058330581
case OP_if_true8:
3058430582
case OP_if_false8:
3058530583
diff = (int8_t)bc_buf[pos + 1];
30586-
if (ss_check(ctx, s, pos + 1 + diff, op, stack_len))
30584+
if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos))
3058730585
goto fail;
3058830586
break;
3058930587
case OP_if_true:
3059030588
case OP_if_false:
30591-
case OP_catch:
3059230589
diff = get_u32(bc_buf + pos + 1);
30593-
if (ss_check(ctx, s, pos + 1 + diff, op, stack_len))
30590+
if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos))
3059430591
goto fail;
3059530592
break;
3059630593
case OP_gosub:
3059730594
diff = get_u32(bc_buf + pos + 1);
30598-
if (ss_check(ctx, s, pos + 1 + diff, op, stack_len + 1))
30595+
if (ss_check(ctx, s, pos + 1 + diff, op, stack_len + 1, catch_pos))
3059930596
goto fail;
3060030597
break;
3060130598
case OP_with_get_var:
3060230599
case OP_with_delete_var:
3060330600
diff = get_u32(bc_buf + pos + 5);
30604-
if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 1))
30601+
if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 1, catch_pos))
3060530602
goto fail;
3060630603
break;
3060730604
case OP_with_make_ref:
3060830605
case OP_with_get_ref:
3060930606
case OP_with_get_ref_undef:
3061030607
diff = get_u32(bc_buf + pos + 5);
30611-
if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 2))
30608+
if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 2, catch_pos))
3061230609
goto fail;
3061330610
break;
3061430611
case OP_with_put_var:
3061530612
diff = get_u32(bc_buf + pos + 5);
30616-
if (ss_check(ctx, s, pos + 5 + diff, op, stack_len - 1))
30613+
if (ss_check(ctx, s, pos + 5 + diff, op, stack_len - 1, catch_pos))
3061730614
goto fail;
3061830615
break;
30619-
30616+
case OP_catch:
30617+
diff = get_u32(bc_buf + pos + 1);
30618+
if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos))
30619+
goto fail;
30620+
catch_pos = pos;
30621+
break;
30622+
case OP_for_of_start:
30623+
case OP_for_await_of_start:
30624+
catch_pos = pos;
30625+
break;
30626+
/* we assume the catch offset entry is only removed with
30627+
some op codes */
30628+
case OP_drop:
30629+
catch_level = stack_len;
30630+
goto check_catch;
30631+
case OP_nip:
30632+
catch_level = stack_len - 1;
30633+
goto check_catch;
30634+
case OP_nip1:
30635+
catch_level = stack_len - 1;
30636+
goto check_catch;
30637+
case OP_iterator_close:
30638+
catch_level = stack_len + 2;
30639+
check_catch:
30640+
/* Note: for for_of_start/for_await_of_start we consider
30641+
the catch offset is on the first stack entry instead of
30642+
the thirst */
30643+
if (catch_pos >= 0) {
30644+
int level;
30645+
level = s->stack_level_tab[catch_pos];
30646+
if (bc_buf[catch_pos] != OP_catch)
30647+
level++; /* for_of_start, for_wait_of_start */
30648+
/* catch_level = stack_level before op_catch is executed ? */
30649+
if (catch_level == level) {
30650+
catch_pos = s->catch_pos_tab[catch_pos];
30651+
}
30652+
}
30653+
break;
30654+
case OP_nip_catch:
30655+
if (catch_pos < 0) {
30656+
JS_ThrowInternalError(ctx, "nip_catch: no catch op (pc=%d)", pos);
30657+
goto fail;
30658+
}
30659+
stack_len = s->stack_level_tab[catch_pos];
30660+
if (bc_buf[catch_pos] != OP_catch)
30661+
stack_len++; /* for_of_start, for_wait_of_start */
30662+
stack_len++; /* no stack overflow is possible by construction */
30663+
catch_pos = s->catch_pos_tab[catch_pos];
30664+
break;
3062030665
default:
3062130666
break;
3062230667
}
30623-
if (ss_check(ctx, s, pos_next, op, stack_len))
30668+
if (ss_check(ctx, s, pos_next, op, stack_len, catch_pos))
3062430669
goto fail;
3062530670
done_insn: ;
3062630671
}
30627-
js_free(ctx, s->stack_level_tab);
3062830672
js_free(ctx, s->pc_stack);
30673+
js_free(ctx, s->catch_pos_tab);
30674+
js_free(ctx, s->stack_level_tab);
3062930675
*pstack_size = s->stack_len_max;
3063030676
return 0;
3063130677
fail:
30632-
js_free(ctx, s->stack_level_tab);
3063330678
js_free(ctx, s->pc_stack);
30679+
js_free(ctx, s->catch_pos_tab);
30680+
js_free(ctx, s->stack_level_tab);
3063430681
*pstack_size = 0;
3063530682
return -1;
3063630683
}

0 commit comments

Comments
 (0)