Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion feature/asi.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,16 @@ const MAX_ASI_DEPTH = 100;

parse.asi = (a, p, expr, b, items) => {
if (p >= lvl || asiDepth >= MAX_ASI_DEPTH) return;
// Bail if the inner expr didn't actually consume anything. Without this, a
// lookup handler that returns a non-array sentinel (e.g. switch.js's
// `reserve` flagging `case`/`default` inside a switch body) lets expr return
// a truthy token without advancing idx, and the outer ASI loop appends to its
// semicolon-list forever.
const beforeIdx = idx;
asiDepth++;
try { b = expr(lvl - .5); }
finally { asiDepth--; }
if (!b) return;
if (!b || idx === beforeIdx) return;
items = b?.[0] === ';' ? b.slice(1) : [b];
return a?.[0] === ';' ? (a.push(...items), a) : [';', a, ...items];
};
15 changes: 15 additions & 0 deletions test/feature/control.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,21 @@ test('control: switch basic', t => {
is(ast[3][0], 'case')
})

// ASI inside a switch body: when an inner expr() call hits a reserved
// keyword that signals "matched but did not consume" (e.g. case/default
// while inSwitch), the ASI loop must stop. Without the no-progress guard,
// the recursion appended to its semicolon-list until the array exceeded
// its max length and threw `Invalid array length`.
test('control: switch with multi-statement case bodies parses without ASI loop', t => {
is(parse(`switch (x) {
case 1:
a
b
case 2:
c
}`)[0], 'switch')
})

test('control: switch compile', t => {
// Basic case match
let ctx = { x: 1, y: 0 }
Expand Down
Loading