1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 07:17:35 +00:00

LibJS: Allow multiple labels on the same statement

Since there are only a number of statements where labels can actually be
used we now also only store labels when necessary.
Also now tracks the first continue usage of a label since this might not
be valid but that can only be determined after we have parsed the
statement.
Also ensures the correct error does not get wiped by load_state.
This commit is contained in:
davidot 2021-09-18 23:01:54 +02:00 committed by Linus Groh
parent bfc1b4ba61
commit 79caca8ca2
7 changed files with 194 additions and 47 deletions

View file

@ -1,16 +1,16 @@
test("labeled plain scope", () => {
test: {
notused: test: alsonotused: {
let o = 1;
expect(o).toBe(1);
break test;
unused: break test;
expect().fail();
}
});
test("break on plain scope from inner scope", () => {
outer: {
notused: outer: alsonotused: {
{
break outer;
unused: break outer;
}
expect().fail();
}
@ -18,7 +18,7 @@ test("break on plain scope from inner scope", () => {
test("labeled for loop with break", () => {
let counter = 0;
outer: for (a of [1, 2, 3]) {
notused: outer: alsonotused: for (a of [1, 2, 3]) {
for (b of [4, 5, 6]) {
if (a === 2 && b === 5) break outer;
counter++;
@ -29,7 +29,7 @@ test("labeled for loop with break", () => {
test("labeled for loop with continue", () => {
let counter = 0;
outer: for (a of [1, 2, 3]) {
notused: outer: alsonotused: for (a of [1, 2, 3]) {
for (b of [4, 5, 6]) {
if (b === 6) continue outer;
counter++;
@ -38,10 +38,107 @@ test("labeled for loop with continue", () => {
expect(counter).toBe(6);
});
test("invalid label across scope", () => {
expect(`
label: {
(() => { break label; });
}
`).not.toEval();
test("break on try catch statement", () => {
let entered = false;
label1: label2: label3: try {
entered = true;
break label2;
expect().fail();
} catch (e) {
expect().fail();
}
expect(entered).toBeTrue();
});
test("can break on every label", () => {
let i = 0;
label0: label1: label2: for (; i < 3; i++) {
block: {
break block;
expect().fail();
}
if (i === 0) continue label0;
if (i === 1) continue label1;
if (i === 2) continue label2;
expect().fail();
}
expect(i).toBe(3);
});
test("can use certain 'keywords' as labels", () => {
let i = 0;
yield: {
i++;
break yield;
expect().fail();
}
await: {
i++;
break await;
expect().fail();
}
async: {
i++;
break async;
expect().fail();
}
let: {
i++;
break let;
expect().fail();
}
// prettier-ignore
l\u0065t: {
i++;
break let;
expect().fail();
}
private: {
i++;
break private;
expect().fail();
}
expect(i).toBe(6);
expect(`const: { break const; }`).not.toEval();
});
test("invalid label usage", () => {
expect(() =>
eval(`
label: {
(() => {
break label;
});
}
`)
).toThrowWithMessage(SyntaxError, "Label 'label' not found");
expect(() =>
eval(`
label: {
while (false) {
continue label;
}
}
`)
).toThrowWithMessage(
SyntaxError,
"labelled continue statement cannot use non iterating statement"
);
expect(() =>
eval(`
label: label: {
break label;
}
`)
).toThrowWithMessage(SyntaxError, "Label 'label' has already been declared");
});