`continue` to an outer label of a label set (`a: b: for (…) continue a;`) gives "break/continue label not found"
メンテナーはふだん 3 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 初心者へのやさしさ
- 48/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 活発
- 技術スタック
- c, javascript
調査の方向性
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.
索引モデルが issue の本文から書いたものです。
説明
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 (
continueinfor (let …)skips the per-iteration copy) is a separate bug in the same loop code. The fixes are independent.
- 主要言語
- C
- スター
- 11.1k
- フォーク
- 1.2k
- 平均マージ
- 11時間 20分
- マージ済み PR(30日)
- 1
環境構築
このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
bellard/quickjs のほかの issue
-
難易度 4/5 3〜5日 初心者へのやさしさ 55/100
メンテナーはふだん 3 日以内に返信
-
難易度 3/5 1〜2日 初心者へのやさしさ 55/100
メンテナーはふだん 3 日以内に返信
-
難易度 5/5 1週間以上 初心者へのやさしさ 35/100
bellard/quickjs#602 · コメント 1 件 ·
メンテナーはふだん 3 日以内に返信
-
難易度 4/5 3〜5日 初心者へのやさしさ 45/100
メンテナーはふだん 3 日以内に返信
-
難易度 4/5 3〜5日 初心者へのやさしさ 58/100
メンテナーはふだん 3 日以内に返信
bellard/quickjs の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
-
要トリアージ 辞書
難易度 1/5 1〜3時間 初心者へのやさしさ 86/100
yksr-melt/Meltype#1625 · リアクション 1 件 ·
メンテナーはふだん 1 日以内に返信
-
kind/bug priority/medium qa_automation
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
yugabyte/yugabyte-db#37324 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 65/100
tree-sitter/tree-sitter#6111 ·
メンテナーはふだん 1 日以内に返信
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
joncampbell123/dosbox-x#6797 ·
メンテナーはふだん 1 日以内に返信