Skip to content

Commit 0fc0daa

Browse files
committed
fix: emit source_loc around iterator_close so for/of return() stack traces report correct line
Fixes #1266
1 parent a653771 commit 0fc0daa

2 files changed

Lines changed: 71 additions & 5 deletions

File tree

quickjs.c

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23180,14 +23180,19 @@ static void emit_u32(JSParseState *s, uint32_t val)
2318023180
dbuf_put_u32(&s->cur_func->byte_code, val);
2318123181
}
2318223182

23183-
static void emit_source_loc(JSParseState *s)
23183+
static void emit_source_loc_at(JSParseState *s, int line_num, int col_num)
2318423184
{
2318523185
JSFunctionDef *fd = s->cur_func;
2318623186
DynBuf *bc = &fd->byte_code;
2318723187

2318823188
dbuf_putc(bc, OP_source_loc);
23189-
dbuf_put_u32(bc, s->token.line_num);
23190-
dbuf_put_u32(bc, s->token.col_num);
23189+
dbuf_put_u32(bc, line_num);
23190+
dbuf_put_u32(bc, col_num);
23191+
}
23192+
23193+
static void emit_source_loc(JSParseState *s)
23194+
{
23195+
emit_source_loc_at(s, s->token.line_num, s->token.col_num);
2319123196
}
2319223197

2319323198
static void emit_op(JSParseState *s, uint8_t val)
@@ -26032,8 +26037,11 @@ static int js_parse_destructuring_element(JSParseState *s, int tok,
2603226037
} else if (s->token.val == '[') {
2603326038
bool has_spread;
2603426039
int enum_depth;
26040+
int source_line_num, source_col_num;
2603526041
BlockEnv block_env;
2603626042

26043+
source_line_num = s->token.line_num;
26044+
source_col_num = s->token.col_num;
2603726045
if (next_token(s))
2603826046
return -1;
2603926047
/* the block environment is only needed in generators in case
@@ -26129,6 +26137,7 @@ static int js_parse_destructuring_element(JSParseState *s, int tok,
2612926137
}
2613026138
/* close iterator object:
2613126139
if completed, enum_obj has been replaced by undefined */
26140+
emit_source_loc_at(s, source_line_num, source_col_num);
2613226141
emit_op(s, OP_iterator_close);
2613326142
pop_break_entry(s->cur_func);
2613426143
if (next_token(s))
@@ -28012,7 +28021,9 @@ static int is_let(JSParseState *s, int decl_mask)
2801228021
/* XXX: handle IteratorClose when exiting the loop before the
2801328022
enumeration is done */
2801428023
static __exception int js_parse_for_in_of(JSParseState *s, int label_name,
28015-
bool is_async)
28024+
bool is_async,
28025+
int source_line_num,
28026+
int source_col_num)
2801628027
{
2801728028
JSContext *ctx = s->ctx;
2801828029
JSFunctionDef *fd = s->cur_func;
@@ -28230,6 +28241,7 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name,
2823028241
emit_label(s, label_break);
2823128242
if (is_for_of) {
2823228243
/* close and drop enum_rec */
28244+
emit_source_loc_at(s, source_line_num, source_col_num);
2823328245
emit_op(s, OP_iterator_close);
2823428246
} else {
2823528247
emit_op(s, OP_drop);
@@ -28464,8 +28476,11 @@ static __exception int js_parse_statement_or_decl(JSParseState *s,
2846428476
int pos_cont, pos_body, block_scope_level;
2846528477
BlockEnv break_entry;
2846628478
int tok, bits;
28479+
int source_line_num, source_col_num;
2846728480
bool is_async;
2846828481

28482+
source_line_num = s->token.line_num;
28483+
source_col_num = s->token.col_num;
2846928484
if (next_token(s))
2847028485
goto fail;
2847128486

@@ -28489,7 +28504,8 @@ static __exception int js_parse_statement_or_decl(JSParseState *s,
2848928504

2849028505
if (!(bits & SKIP_HAS_SEMI)) {
2849128506
/* parse for/in or for/of */
28492-
if (js_parse_for_in_of(s, label_name, is_async))
28507+
if (js_parse_for_in_of(s, label_name, is_async,
28508+
source_line_num, source_col_num))
2849328509
goto fail;
2849428510
break;
2849528511
}

tests/test_for_of_line_numbers.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { assert } from "./assert.js";
2+
3+
function closingIterable(stacks)
4+
{
5+
return {
6+
[Symbol.iterator]() {
7+
return {
8+
next() {
9+
return { done: false, value: 1 };
10+
},
11+
return() {
12+
stacks.push(new Error().stack);
13+
return { done: true };
14+
},
15+
};
16+
},
17+
};
18+
}
19+
20+
function assertCallerLine(frames, expectedLine, message)
21+
{
22+
assert(frames.length >= 2, true, message);
23+
assert(frames[1].getFileName().endsWith("test_for_of_line_numbers.js"), true, message);
24+
assert(frames[1].getLineNumber(), expectedLine, message);
25+
}
26+
27+
function test_for_of_empty_body_line_number()
28+
{
29+
const stacks = [];
30+
const expectedLine = 31;
31+
for (const _ of closingIterable(stacks)) break;
32+
assertCallerLine(stacks[0], expectedLine, "for-of iterator close");
33+
}
34+
35+
function test_for_of_destructuring_line_number()
36+
{
37+
const stacks = [];
38+
const outer = [closingIterable(stacks)];
39+
const expectedLine = 40;
40+
for (const [_] of outer) break;
41+
assertCallerLine(stacks[0], expectedLine, "destructuring iterator close");
42+
}
43+
44+
Error.prepareStackTrace = (_, frames) => frames;
45+
try {
46+
test_for_of_empty_body_line_number();
47+
test_for_of_destructuring_line_number();
48+
} finally {
49+
Error.prepareStackTrace = undefined;
50+
}

0 commit comments

Comments
 (0)