Skip to content

Commit bcac5c2

Browse files
mvanhornsaghul
authored andcommitted
fix: emit source_loc around iterator_close so for/of return() stack traces report correct line
Fixes #1266
1 parent 4b0b8da commit bcac5c2

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
@@ -23384,14 +23384,19 @@ static void emit_u32(JSParseState *s, uint32_t val)
2338423384
dbuf_put_u32(&s->cur_func->byte_code, val);
2338523385
}
2338623386

23387-
static void emit_source_loc(JSParseState *s)
23387+
static void emit_source_loc_at(JSParseState *s, int line_num, int col_num)
2338823388
{
2338923389
JSFunctionDef *fd = s->cur_func;
2339023390
DynBuf *bc = &fd->byte_code;
2339123391

2339223392
dbuf_putc(bc, OP_source_loc);
23393-
dbuf_put_u32(bc, s->token.line_num);
23394-
dbuf_put_u32(bc, s->token.col_num);
23393+
dbuf_put_u32(bc, line_num);
23394+
dbuf_put_u32(bc, col_num);
23395+
}
23396+
23397+
static void emit_source_loc(JSParseState *s)
23398+
{
23399+
emit_source_loc_at(s, s->token.line_num, s->token.col_num);
2339523400
}
2339623401

2339723402
static void emit_op(JSParseState *s, uint8_t val)
@@ -26250,8 +26255,11 @@ static int js_parse_destructuring_element(JSParseState *s, int tok,
2625026255
} else if (s->token.val == '[') {
2625126256
bool has_spread;
2625226257
int enum_depth;
26258+
int source_line_num, source_col_num;
2625326259
BlockEnv block_env;
2625426260

26261+
source_line_num = s->token.line_num;
26262+
source_col_num = s->token.col_num;
2625526263
if (next_token(s))
2625626264
return -1;
2625726265
/* the block environment is only needed in generators in case
@@ -26347,6 +26355,7 @@ static int js_parse_destructuring_element(JSParseState *s, int tok,
2634726355
}
2634826356
/* close iterator object:
2634926357
if completed, enum_obj has been replaced by undefined */
26358+
emit_source_loc_at(s, source_line_num, source_col_num);
2635026359
emit_op(s, OP_iterator_close);
2635126360
pop_break_entry(s->cur_func);
2635226361
if (next_token(s))
@@ -28369,7 +28378,9 @@ static int is_using(JSParseState *s, bool is_for_of)
2836928378
/* XXX: handle IteratorClose when exiting the loop before the
2837028379
enumeration is done */
2837128380
static __exception int js_parse_for_in_of(JSParseState *s, int label_name,
28372-
bool is_async)
28381+
bool is_async,
28382+
int source_line_num,
28383+
int source_col_num)
2837328384
{
2837428385
JSContext *ctx = s->ctx;
2837528386
JSFunctionDef *fd = s->cur_func;
@@ -28676,6 +28687,7 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name,
2867628687
emit_label(s, label_break);
2867728688
if (is_for_of) {
2867828689
/* close and drop enum_rec */
28690+
emit_source_loc_at(s, source_line_num, source_col_num);
2867928691
emit_op(s, OP_iterator_close);
2868028692
} else {
2868128693
emit_op(s, OP_drop);
@@ -28941,8 +28953,11 @@ static __exception int js_parse_statement_or_decl(JSParseState *s,
2894128953
int for_scope_level;
2894228954
BlockEnv break_entry;
2894328955
int tok, bits;
28956+
int source_line_num, source_col_num;
2894428957
bool is_async;
2894528958

28959+
source_line_num = s->token.line_num;
28960+
source_col_num = s->token.col_num;
2894628961
if (next_token(s))
2894728962
goto fail;
2894828963

@@ -28966,7 +28981,8 @@ static __exception int js_parse_statement_or_decl(JSParseState *s,
2896628981

2896728982
if (!(bits & SKIP_HAS_SEMI)) {
2896828983
/* parse for/in or for/of */
28969-
if (js_parse_for_in_of(s, label_name, is_async))
28984+
if (js_parse_for_in_of(s, label_name, is_async,
28985+
source_line_num, source_col_num))
2897028986
goto fail;
2897128987
break;
2897228988
}

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)