Skip to content

Commit 66732e7

Browse files
authored
Fix break statement in presence of labels (#742)
In this snippet... for (;;) label: break ...the break statement jumped back to the start of the loop instead of *out* of the loop. Fixes: #741
1 parent ebc1a65 commit 66732e7

File tree

2 files changed

+35
-11
lines changed

2 files changed

+35
-11
lines changed

quickjs.c

+16-11
Original file line numberDiff line numberDiff line change
@@ -18626,7 +18626,8 @@ typedef struct BlockEnv {
1862618626
int drop_count; /* number of stack elements to drop */
1862718627
int label_finally; /* -1 if none */
1862818628
int scope_level;
18629-
int has_iterator;
18629+
uint8_t has_iterator : 1;
18630+
uint8_t is_regular_stmt : 1; // i.e. not a loop statement
1863018631
} BlockEnv;
1863118632

1863218633
typedef struct JSGlobalVar {
@@ -24891,6 +24892,7 @@ static void push_break_entry(JSFunctionDef *fd, BlockEnv *be,
2489124892
be->label_finally = -1;
2489224893
be->scope_level = fd->scope_level;
2489324894
be->has_iterator = FALSE;
24895+
be->is_regular_stmt = FALSE;
2489424896
}
2489524897

2489624898
static void pop_break_entry(JSFunctionDef *fd)
@@ -24917,11 +24919,12 @@ static __exception int emit_break(JSParseState *s, JSAtom name, int is_cont)
2491724919
emit_goto(s, OP_goto, top->label_cont);
2491824920
return 0;
2491924921
}
24920-
if (!is_cont &&
24921-
top->label_break != -1 &&
24922-
(name == JS_ATOM_NULL || top->label_name == name)) {
24923-
emit_goto(s, OP_goto, top->label_break);
24924-
return 0;
24922+
if (!is_cont && top->label_break != -1) {
24923+
if (top->label_name == name ||
24924+
(name == JS_ATOM_NULL && !top->is_regular_stmt)) {
24925+
emit_goto(s, OP_goto, top->label_break);
24926+
return 0;
24927+
}
2492524928
}
2492624929
i = 0;
2492724930
if (top->has_iterator) {
@@ -25335,7 +25338,8 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name,
2533525338
JS_FreeAtom(ctx, var_name);
2533625339

2533725340
if (token_is_pseudo_keyword(s, JS_ATOM_of)) {
25338-
break_entry.has_iterator = is_for_of = TRUE;
25341+
is_for_of = TRUE;
25342+
break_entry.has_iterator = TRUE;
2533925343
break_entry.drop_count += 2;
2534025344
if (has_initializer)
2534125345
goto initializer_error;
@@ -25479,13 +25483,14 @@ static __exception int js_parse_statement_or_decl(JSParseState *s,
2547925483
&& s->token.val != TOK_DO
2548025484
&& s->token.val != TOK_WHILE) {
2548125485
/* labelled regular statement */
25486+
JSFunctionDef *fd = s->cur_func;
2548225487
int label_break, mask;
2548325488
BlockEnv break_entry;
2548425489

2548525490
label_break = new_label(s);
25486-
push_break_entry(s->cur_func, &break_entry,
25487-
label_name, label_break, -1, 0);
25488-
if (!s->cur_func->is_strict_mode &&
25491+
push_break_entry(fd, &break_entry, label_name, label_break, -1, 0);
25492+
fd->top_break->is_regular_stmt = 1;
25493+
if (!fd->is_strict_mode &&
2548925494
(decl_mask & DECL_MASK_FUNC_WITH_LABEL)) {
2549025495
mask = DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL;
2549125496
} else {
@@ -25494,7 +25499,7 @@ static __exception int js_parse_statement_or_decl(JSParseState *s,
2549425499
if (js_parse_statement_or_decl(s, mask))
2549525500
goto fail;
2549625501
emit_label(s, label_break);
25497-
pop_break_entry(s->cur_func);
25502+
pop_break_entry(fd);
2549825503
goto done;
2549925504
}
2550025505
}

tests/bug741.js

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import {assert} from "./assert.js"
2+
3+
while (1) label: break
4+
5+
var i = 0
6+
while (i < 3) label: {
7+
if (i > 0)
8+
break
9+
i++
10+
}
11+
assert(i, 1)
12+
13+
for (;;) label: break
14+
15+
for (i = 0; i < 3; i++) label: {
16+
if (i > 0)
17+
break
18+
}
19+
assert(i, 1)

0 commit comments

Comments
 (0)