`continue` to an outer label of a label set (`a: b: for (…) continue a;`) gives "break/continue label not found"

Aperta
#595 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 3 giorni

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
4/5
Tempo stimato
3-5 giorni
Idoneità per principianti
48/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
c, javascript
Ambito
compilers, testing

Direzione di ricerca

Start in quickjs.c at js_parse_statement_or_decl, push_break_entry, and emit_break; the issue identifies the current limitation and includes a proposed change. Add or inspect regression coverage in tests/test_language.js for chained labels, including valid continue targets and invalid targets through non-loop statements. Run make test and compare test262 results with test262_errors.txt; done means the reproduction prints 2 and the invalid cases remain SyntaxErrors.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

Environment
  • QuickJS master 712e8c0 (2026-11-02) and release 2026-06-04, make qjs, Debian 12 x86-64, gcc 12.2
  • Reference: node v26.13.0
  • Checked on 2026-11-03
Reproduction
var n = 0;
a: b: for (var i = 0; i < 2; i++) { n++; continue a; }
console.log(n);

Expected 2. QuickJS: SyntaxError: break/continue label not found. continue b and break a work. The same
happens with while, do … while, for-in/for-of and longer label chains.

Expected (spec)

ContainsUndefinedContinueTarget:
each LabelledStatement appends its label to labelSet, and BreakableStatement : IterationStatement adds the whole
labelSet to iterationSet. The loop's label set is « a, b », so continue a is valid and continues the loop
(LoopContinues). a: { b: for (;;) continue a; } and
a: b: if (1) for (;;) continue a; must remain SyntaxErrors.

Cause and patch

js_parse_statement_or_decl notes /* XXX: support multiple labels on loop statements */. Only the last label becomes
the loop's BlockEnv.label_name; the outer labels are labelled regular statements with label_cont == -1. The patch
below (with a test in tests/test_language.js; make test passes, and test262 shows no new failures against
test262_errors.txt) lets those entries point at the loop they directly label and maps continue <outer> to the loop's
label in emit_break():

diff --git a/quickjs.c b/quickjs.c
--- a/quickjs.c
+++ b/quickjs.c
@@ -22471,6 +22471,9 @@ enum {
 
 typedef struct BlockEnv {
     struct BlockEnv *prev;
+    /* loop directly labelled by this label statement (a: b: for ...), so
+       that `continue a` targets that loop */
+    struct BlockEnv *cont_alias;
     JSAtom label_name; /* JS_ATOM_NULL if none */
     int label_break; /* -1 if none */
     int label_cont; /* -1 if none */
@@ -22482,6 +22485,7 @@ typedef struct BlockEnv {
     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 labels_next : 1; /* label statement whose body is another label */
     uint8_t has_using : 1; /* the block contains a using declaration */
     uint8_t is_await_using : 1; /* the block has await using declarations */
 } BlockEnv;
@@ -28958,8 +28962,11 @@ static void push_break_entry(JSFunctionDef *fd, BlockEnv *be,
                              int label_break, int label_cont,
                              int drop_count)
 {
+    BlockEnv *p;
     be->prev = fd->top_break;
     fd->top_break = be;
+    be->cont_alias = NULL;
+    be->labels_next = 0;
     be->label_name = label_name;
     be->label_break = label_break;
     be->label_cont = label_cont;
@@ -28973,6 +28980,10 @@ static void push_break_entry(JSFunctionDef *fd, BlockEnv *be,
     be->is_await_using = FALSE;
     be->using_label_catch = -1;
     be->using_label_end = -1;
+    if (label_cont != -1) {
+        for (p = be->prev; p && p->is_regular_stmt && p->labels_next; p = p->prev)
+            p->cont_alias = be;
+    }
 }
 
 static void js_emit_dispose_scope(JSParseState *s, int scope,
@@ -29089,6 +29100,15 @@ static __exception int emit_break(JSParseState *s, JSAtom name, int is_cont)
     int i, scope_level;
 
     scope_level = s->cur_func->scope_level;
+    if (is_cont && name != JS_ATOM_NULL) {
+        for (top = s->cur_func->top_break; top; top = top->prev) {
+            if (top->label_name == name) {
+                if (top->cont_alias)
+                    name = top->cont_alias->label_name;
+                break;
+            }
+        }
+    }
     top = s->cur_func->top_break;
     while (top != NULL) {
         close_scopes(s, scope_level, top->scope_level);
@@ -29807,7 +29827,6 @@ static __exception int js_parse_statement_or_decl(JSParseState *s,
     int tok;
 
     /* specific label handling */
-    /* XXX: support multiple labels on loop statements */
     label_name = JS_ATOM_NULL;
     if (is_label(s)) {
         BlockEnv *be;
@@ -29836,6 +29855,7 @@ static __exception int js_parse_statement_or_decl(JSParseState *s,
             push_break_entry(s->cur_func, &break_entry,
                              label_name, label_break, -1, 0);
             break_entry.is_regular_stmt = TRUE;
+            s->cur_func->top_break->labels_next = is_label(s);
             if (!(s->cur_func->js_mode & JS_MODE_STRICT) &&
                 (decl_mask & DECL_MASK_FUNC_WITH_LABEL)) {
                 mask = DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL;
Related
  • quickjs-ng has the same code: quickjs-ng/quickjs#1884
  • #592 (continue in for (let …) skips the per-iteration copy) is a separate bug in the same loop code. The fixes are independent.
Lingua principale
C
Stelle
11.1k
Fork
1.2k
Merge medio
11h 20m
PR unite (30g)
1

Preparare l'ambiente

Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di bellard/quickjs

Tutte le issue di bellard/quickjs

Issue simili

Altre issue su C

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.