1
0
Fork 0
mirror of https://github.com/LadybirdBrowser/ladybird.git synced 2025-06-09 09:34:57 +09:00

Everywhere: Hoist the Libraries folder to the top-level

This commit is contained in:
Timothy Flynn 2024-11-09 12:25:08 -05:00 committed by Andreas Kling
parent 950e819ee7
commit 93712b24bf
Notes: github-actions[bot] 2024-11-10 11:51:52 +00:00
4547 changed files with 104 additions and 113 deletions

View file

@ -0,0 +1,294 @@
describe("parsing freestanding async functions", () => {
test("simple", () => {
expect(`async function foo() {}`).toEval();
// Although it does not create an async function it is valid.
expect(`async
function foo() {}`).toEval();
expect(`async function await() {}`).toEval();
expect(`async function yield() {}`).toEval();
});
test("await expression", () => {
expect(`async function foo() { await bar(); }`).toEval();
expect(`async function foo() { await; }`).not.toEval();
expect(`function foo() { await bar(); }`).not.toEval();
expect(`function foo() { await; }`).toEval();
expect(`\\u0061sync function foo() { await bar(); }`).not.toEval();
expect(`\\u0061sync function foo() { \\u0061wait bar(); }`).not.toEval();
expect(`async function foo() { \\u0061wait bar(); }`).not.toEval();
});
});
describe("parsing object literal async functions", () => {
test("simple", () => {
expect(`x = { async foo() { } }`).toEval();
expect(`x = { async
foo() { } }`).not.toEval();
});
test("property on object called async", () => {
expect(`x = { async() { } }`).toEval();
expect(`x = { async() { await 4; } }`).not.toEval();
expect(`x = { async: 3 }`).toEval();
expect(`x = { async: await 3, }`).not.toEval();
});
test("await expression", () => {
expect(`x = { foo() { await bar(); } }`).not.toEval();
expect(`x = { foo() { await; } }`).toEval();
expect(`x = { async foo() { await bar(); } }`).toEval();
expect(`x = { async foo() { await; } }`).not.toEval();
});
});
describe("parsing classes with async methods", () => {
test("simple", () => {
expect(`class Foo { async foo() {} }`).toEval();
expect(`class Foo { static async foo() {} }`).toEval();
expect(`class Foo { async foo() { await bar(); } }`).toEval();
expect(`class Foo { async foo() { await; } }`).not.toEval();
expect(`class Foo { async constructor() {} }`).not.toEval();
});
});
test("function expression names equal to 'await'", () => {
expect(`async function foo() { (function await() {}); }`).toEval();
expect(`async function foo() { function await() {} }`).not.toEval();
});
test("async function cannot use await in default parameters", () => {
expect("async function foo(x = await 3) {}").not.toEval();
expect("async function foo(x = await 3) {}").not.toEval();
// Even as a reference to some variable it is not allowed
expect(`
var await = 4;
async function foo(x = await) {}
`).not.toEval();
});
describe("async arrow functions", () => {
test("basic syntax", () => {
expect("async () => await 3;").toEval();
expect("async param => await param();").toEval();
expect("async (param) => await param();").toEval();
expect("async (a, b) => await a();").toEval();
expect("async () => { await 3; }").toEval();
expect("async param => { await param(); }").toEval();
expect("async (param) => { await param(); }").toEval();
expect("async (a, b) => { await a(); }").toEval();
expect(`async
() => await 3;`).not.toEval();
expect("async async => await async()").toEval();
expect("async => async").toEval();
expect("async => await async()").not.toEval();
expect("async (b = await) => await b;").not.toEval();
expect("async (b = await 3) => await b;").not.toEval();
// Cannot escape the async keyword and get an async arrow function.
expect("\\u0061sync () => await 3").not.toEval();
expect("for (async of => {};false;) {}").toEval();
expect("for (async of []) {}").not.toEval();
expect("for (\\u0061sync of []) {}").toEval();
expect("for (\\u0061sync of => {};false;) {}").not.toEval();
expect("for (\\u0061sync => {};false;) {}").toEval();
});
test("async within a for-loop", () => {
let called = false;
// Unfortunately we cannot really test the more horrible case above.
for (
const f = async of => {
return of;
};
;
) {
expect(f(43)).toBeInstanceOf(Promise);
called = true;
break;
}
expect(called).toBeTrue();
});
});
test("basic functionality", () => {
test("simple", () => {
let executionValue = null;
let resultValue = null;
async function foo() {
executionValue = "someValue";
return "otherValue";
}
const returnValue = foo();
expect(returnValue).toBeInstanceOf(Promise);
returnValue.then(result => {
resultValue = result;
});
runQueuedPromiseJobs();
expect(executionValue).toBe("someValue");
expect(resultValue).toBe("otherValue");
});
test("await", () => {
let resultValue = null;
async function foo() {
return "someValue";
}
async function bar() {
resultValue = await foo();
}
bar();
runQueuedPromiseJobs();
expect(resultValue).toBe("someValue");
});
});
describe("non async function declaration usage of async still works", () => {
test("async as a function", () => {
function async(value = 4) {
return value;
}
expect(async(0)).toBe(0);
// We use eval here since it otherwise cannot find the async function.
const evalResult = eval("async(1)");
expect(evalResult).toBe(1);
});
test("async as a variable", () => {
let async = 3;
const evalResult = eval("async >= 2");
expect(evalResult).toBeTrue();
});
test("async with line ending does not create a function", () => {
expect(() => {
// The ignore is needed otherwise prettier puts a ';' after async.
// prettier-ignore
async
function f() {}
}).toThrowWithMessage(ReferenceError, "'async' is not defined");
expect(`async
function f() { await 3; }`).not.toEval();
});
});
describe("await cannot be used in class static init blocks", () => {
test("directly", () => {
expect("class A{ static { await; } }").not.toEval();
expect("class A{ static { let await = 3; } }").not.toEval();
expect("class A{ static { call(await); } }").not.toEval();
expect("class A{ static { for(const await = 1; false ;) {} } }").not.toEval();
});
test("via declaration", () => {
expect("class A{ static { class await {} } }").not.toEval();
expect("class A{ static { function await() {} } }").not.toEval();
expect("class A{ static { function* await() {} } }").not.toEval();
expect("class A{ static { async function* await() {} } }").not.toEval();
});
});
describe("await thenables", () => {
test("async returning a thanable variable without fulfilling", () => {
let isCalled = false;
const obj = {
then() {
isCalled = true;
},
};
const f = async () => await obj;
f();
runQueuedPromiseJobs();
expect(isCalled).toBe(true);
});
test("async returning a thanable variable that fulfills", () => {
let isCalled = false;
const obj = {
then(fulfill) {
isCalled = true;
fulfill(isCalled);
},
};
const f = async () => await obj;
f();
runQueuedPromiseJobs();
expect(isCalled).toBe(true);
});
test("async returning a thenable directly without fulfilling", () => {
let isCalled = false;
const f = async () => ({
then() {
isCalled = true;
},
});
f();
runQueuedPromiseJobs();
expect(isCalled).toBe(true);
});
test("async returning a thenable directly that fulfills", () => {
let isCalled = false;
const f = async () => ({
then(fulfill) {
isCalled = true;
fulfill(isCalled);
},
});
f();
runQueuedPromiseJobs();
expect(isCalled).toBe(true);
});
});
describe("await observably looks up constructor of Promise objects", () => {
let calls = 0;
function makeConstructorObservable(promise) {
Object.defineProperty(promise, "constructor", {
get() {
calls++;
return Promise;
},
});
return promise;
}
async function test() {
await makeConstructorObservable(Promise.resolve(1));
await makeConstructorObservable(
new Promise(resolve => {
resolve();
})
);
await makeConstructorObservable(new Boolean(true));
await makeConstructorObservable({});
await makeConstructorObservable(new Number(2));
try {
await makeConstructorObservable(Promise.reject(3));
} catch {}
try {
return makeConstructorObservable(Promise.reject(1));
} catch {
return 2;
}
}
test();
runQueuedPromiseJobs();
expect(calls).toBe(4);
});

View file

@ -0,0 +1,58 @@
describe("parsing freestanding generators", () => {
test("simple", () => {
expect(`async function* foo() {}`).toEval();
expect(`async function *foo() {}`).toEval();
expect(`async function
*foo() {}`).toEval();
expect(`async function *await() {}`).not.toEval();
expect(`async function *yield() {}`).not.toEval();
});
test("yield & await expression", () => {
expect(`async function* foo() { yield; await 1; }`).toEval();
expect(`async function* foo() { yield (yield); await (yield); }`).toEval();
expect(`async function* foo() { yield (yield foo); yield (await foo); }`).toEval();
expect(`async function foo() { yield; }`).toEval();
expect(`async function foo() { yield 3; }`).not.toEval();
expect(`function* foo() { await 3; }`).not.toEval();
});
test("yield-from expression", () => {
expect(`async function* foo() { yield *bar; }`).toEval();
expect(`async function* foo() { yield *(yield); }`).toEval();
expect(`async function* foo() { yield
*bar; }`).not.toEval();
expect(`async function foo() { yield
*bar; }`).toEval();
});
});
describe("parsing object literal generator functions", () => {
test("simple", () => {
expect(`x = { async *foo() { } }`).toEval();
expect(`x = { async * foo() { } }`).toEval();
expect(`x = { async *
foo() { } }`).toEval();
});
test("yield & await", () => {
expect(`x = { async foo() { yield; await 3;} }`).toEval();
expect(`x = { async *foo() { yield; await 3; } }`).toEval();
expect(`x = { async *foo() { yield 42; await 3; } }`).toEval();
expect(`x = { async *foo() { yield (yield); await (yield); } }`).toEval();
expect(`x = { async *
foo() { yield (yield); await 4; } }`).toEval();
expect(`x = { async foo() { yield 42; } }`).not.toEval();
expect(`x = { *foo() { await 42; } }`).not.toEval();
});
});
describe("parsing classes with generator methods", () => {
test("simple", () => {
expect(`class Foo { async *foo() {} }`).toEval();
expect(`class Foo { static async *foo() {} }`).toEval();
expect(`class Foo { async *foo() { yield; } }`).toEval();
expect(`class Foo { async *foo() { yield 42; } }`).toEval();
expect(`class Foo { async *constructor() { yield 42; } }`).not.toEval();
});
});

View file

@ -0,0 +1,41 @@
test("mixing coalescing and logical operators isn't allowed", () => {
expect("if (0) a ?? b || c").not.toEval();
expect("if (0) a ?? b && c").not.toEval();
expect("if (0) a ?? b * c || d").not.toEval();
expect("if (0) a ?? b * c && d").not.toEval();
expect("if (0) a && b ?? c").not.toEval();
expect("if (0) a || b ?? c").not.toEval();
expect("if (0) a && b * c ?? d").not.toEval();
expect("if (0) a || b * c ?? d").not.toEval();
});
test("mixing coalescing and logical operators with parens", () => {
expect("if (0) a ?? (b || c)").toEval();
expect("if (0) (a ?? b) && c").toEval();
expect("if (0) a ?? (b * c || d)").toEval();
expect("if (0) (a ?? b * c) && d").toEval();
expect("if (0) a && (b ?? c)").toEval();
expect("if (0) (a || b) ?? c").toEval();
expect("if (0) a && (b * c) ?? d").not.toEval();
expect("if (0) a || (b * c) ?? d").not.toEval();
});
test("mixing coalescing and logical operators in ternary expressions", () => {
expect("0 || 0 ? 0 : 0 ?? 0").toEval();
expect("0 ?? 0 ? 0 : 0 || 0").toEval();
expect("0 ? 0 || 0 : 0 ?? 0").toEval();
expect("0 ? 0 ?? 0 : 0 || 0").toEval();
expect("0 && 0 ? 0 ?? 0 : 0 || 0").toEval();
expect("0 ?? 0 ? 0 && 0 : 0 || 0").toEval();
expect("0 ?? 0 ? 0 || 0 : 0 && 0").toEval();
expect("0 || 0 ? 0 ?? 0 : 0 && 0").toEval();
expect("0 && 0 ? 0 || 0 : 0 ?? 0").toEval();
expect("0 || 0 ? 0 && 0 : 0 ?? 0").toEval();
});
test("mixing coalescing and logical operators when 'in' isn't allowed", () => {
expect("for (a ?? b || c in a; false;);").not.toEval();
expect("for (a ?? b && c in a; false;);").not.toEval();
expect("for (a || b ?? c in a; false;);").not.toEval();
expect("for (a && b ?? c in a; false;);").not.toEval();
});

View file

@ -0,0 +1,233 @@
describe("parsing", () => {
test("single name", () => {
expect(`var { a } = {};`).toEval();
expect(`const { a } = {};`).toEval();
expect(`let { a } = {};`).toEval();
});
test("single name with rest values", () => {
expect(`var { a, ...rest } = {};`).toEval();
expect(`const { a, ...rest } = {};`).toEval();
expect(`let { a, ...rest } = {};`).toEval();
});
test("single aliased name", () => {
expect(`var { a: b } = {};`).toEval();
expect(`const { a: b } = {};`).toEval();
expect(`let { a: b } = {};`).toEval();
});
test("single aliased name with rest values", () => {
expect(`var { a: b, ...rest } = {};`).toEval();
expect(`const { a: b, ...rest } = {};`).toEval();
expect(`let { a: b, ...rest } = {};`).toEval();
});
test("array destructuring patterns", () => {
expect(`var [ a ] = [];`).toEval();
expect(`const [ a ] = [];`).toEval();
expect(`let [ a ] = [];`).toEval();
});
test("destructuring assignment with rest assignments, array patterns", () => {
expect(`var [ a, ...rest ] = [];`).toEval();
expect(`const [ a, ...rest ] = [];`).toEval();
expect(`let [ a, ...rest ] = [];`).toEval();
});
test("destructuring assignment with rest names, array patterns with recursive patterns", () => {
expect(`var [ a, [ ...rest ] ] = [];`).toEval();
expect(`const [ a, [ ...rest ] ] = [];`).toEval();
expect(`let [ a, [ ...rest ] ] = [];`).toEval();
});
test("destructuring assignment with rest names, array patterns with recursive patterns 2", () => {
expect(`var [ a, [ ...{length} ] ] = [];`).toEval();
expect(`const [ a, [ ...{length} ] ] = [];`).toEval();
expect(`let [ a, [ ...{length} ] ] = [];`).toEval();
});
test("function parameters cannot use member expressions", () => {
expect("function f([a.b]) {}").not.toEval();
expect("function f([b[0]]) {}").not.toEval();
expect("function f({c:a.b}) {}").not.toEval();
expect("function f({a:b[0]}) {}").not.toEval();
expect("([a.b]) => 1").not.toEval();
expect("([b[0]]) => 2").not.toEval();
expect("({c:a.b}) => 3").not.toEval();
expect("({a:b[0]}) => 4").not.toEval();
});
});
describe("evaluating", () => {
test("single name", () => {
let o = { a: 1 };
{
var { a } = o;
expect(a).toBe(o.a);
}
{
const { a } = o;
expect(a).toBe(o.a);
}
{
let { a } = o;
expect(a).toBe(o.a);
}
});
test("single name with rest values", () => {
let o = { a: 1, b: 2 };
{
var { a, ...b } = o;
expect(a).toBe(o.a);
expect(b).toEqual({ b: 2 });
}
{
const { a, ...b } = o;
expect(a).toBe(o.a);
expect(b).toEqual({ b: 2 });
}
{
let { a, ...b } = o;
expect(a).toBe(o.a);
expect(b).toEqual({ b: 2 });
}
});
test("single aliased name", () => {
let o = { a: 1 };
{
var { a: x } = o;
expect(x).toBe(o.a);
}
{
const { a: x } = o;
expect(x).toBe(o.a);
}
{
let { a: x } = o;
expect(x).toBe(o.a);
}
});
test("single aliased name with rest values", () => {
let o = { a: 1, b: 2 };
{
var { a: x, ...b } = o;
expect(x).toBe(o.a);
expect(b).toEqual({ b: 2 });
}
{
const { a: x, ...b } = o;
expect(x).toBe(o.a);
expect(b).toEqual({ b: 2 });
}
{
let { a: x, ...b } = o;
expect(x).toBe(o.a);
expect(b).toEqual({ b: 2 });
}
});
test("array patterns", () => {
let o = [1, 2, 3, 4];
{
var [a, b] = o;
expect(a).toBe(o[0]);
expect(b).toBe(o[1]);
}
{
const [a, b] = o;
expect(a).toBe(o[0]);
expect(b).toBe(o[1]);
}
{
let [a, b] = o;
expect(a).toBe(o[0]);
expect(b).toBe(o[1]);
}
});
test("destructuring assignment with rest names, array patterns", () => {
let o = [1, 2, 3, 4];
{
var [a, ...b] = o;
expect(a).toBe(o[0]);
expect(b).toEqual(o.slice(1));
}
{
const [a, ...b] = o;
expect(a).toBe(o[0]);
expect(b).toEqual(o.slice(1));
}
{
let [a, ...b] = o;
expect(a).toBe(o[0]);
expect(b).toEqual(o.slice(1));
}
});
test("destructuring assignment with rest names, array patterns with recursive patterns", () => {
let o = [1, [2, 3, 4]];
{
var [a, [b, ...c]] = o;
expect(a).toBe(o[0]);
expect(b).toBe(o[1][0]);
expect(c).toEqual(o[1].slice(1));
}
{
const [a, [b, ...c]] = o;
expect(a).toBe(o[0]);
expect(b).toBe(o[1][0]);
expect(c).toEqual(o[1].slice(1));
}
{
let [a, [b, ...c]] = o;
expect(a).toBe(o[0]);
expect(b).toBe(o[1][0]);
expect(c).toEqual(o[1].slice(1));
}
});
test("destructuring assignment with rest names, array patterns with recursive patterns 2", () => {
let o = [1, [2, 3, 4]];
{
var [a, [...{ length }]] = o;
expect(a).toBe(o[0]);
expect(length).toBe(o[1].length);
}
{
const [a, [...{ length }]] = o;
expect(a).toBe(o[0]);
expect(length).toBe(o[1].length);
}
{
let [a, [...{ length }]] = o;
expect(a).toBe(o[0]);
expect(length).toBe(o[1].length);
}
{
expect(() => {
let [a, b, [...{ length }]] = o;
}).toThrowWithMessage(TypeError, "ToObject on null or undefined");
}
});
test("patterns with default", () => {
let o = { a: 1 };
let { x = "foo", a = "bar" } = o;
expect(x).toBe("foo");
expect(a).toBe(o.a);
});
test("can use big int values as number-like properties", () => {
let o = { "99999999999999999": 1 };
let { 123n: a = "foo", 99999999999999999n: b = "bar" } = o;
expect(a).toBe("foo");
expect(b).toBe(1);
});
});

View file

@ -0,0 +1,18 @@
describe("parsing", () => {
test("can parse call import call", () => {
expect("import('a')").toEval();
expect("import('a', )").toEval();
expect("import('a', {options: true})").toEval();
});
test("does not crash on unexpected tokens after import", () => {
expect("f = import('a')").toEval();
expect("f= import").not.toEval();
expect("f= import;").not.toEval();
expect("f= import?").not.toEval();
expect("f= import'").not.toEval();
expect("f= import 'a'").not.toEval();
expect("f= import['a']").not.toEval();
});
});

View file

@ -0,0 +1,19 @@
test("The use of the 'in' keyword is restricted in for loop headers", () => {
expect("for (a ? a : a in a; false; ) ;").not.toEval();
expect("for (a + a in b);").not.toEval();
expect("for (a in a of a);").not.toEval();
expect("for(a = a in a; false;);").not.toEval();
expect("for(a += a in a; false;);").not.toEval();
expect("for(a -= a in a; false;);").not.toEval();
expect("for(a *= a in a; false;);").not.toEval();
expect("for(a /= a in a; false;);").not.toEval();
expect("for(a &= a in a; false;);").not.toEval();
expect("for(a |= a in a; false;);").not.toEval();
expect("for(a ^= a in a; false;);").not.toEval();
expect("for(a ^^= a in a; false;);").not.toEval();
expect("for(a == a in a; false;);").not.toEval();
expect("for(a != a in a; false;);").not.toEval();
expect("for(a === a in a; false;);").not.toEval();
expect("for(a !== a in a; false;);").not.toEval();
expect("for (a ?? b in c;;);").not.toEval();
});

View file

@ -0,0 +1,352 @@
const noHoistLexTopLevel = false;
let canCallNonHoisted = 0;
expect(basicHoistTopLevel()).toEqual("basicHoistTopLevel");
function basicHoistTopLevel() {
return "basicHoistTopLevel";
}
expect(typeof noHoistLexTopLevel).toBe("boolean");
expect(typeof hoistInBlockTopLevel).toBe("undefined");
{
expect(noHoistLexTopLevel()).toEqual("noHoistLexTopLevel");
++canCallNonHoisted;
expect(basicHoistTopLevel()).toEqual("basicHoistTopLevelInBlock");
++canCallNonHoisted;
expect(hoistInBlockTopLevel()).toEqual("hoistInBlockTopLevel");
function hoistInBlockTopLevel() {
return "hoistInBlockTopLevel";
}
function noHoistLexTopLevel() {
return "noHoistLexTopLevel";
}
function basicHoistTopLevel() {
return "basicHoistTopLevelInBlock";
}
}
expect(canCallNonHoisted).toBe(2);
expect(hoistInBlockTopLevel()).toEqual("hoistInBlockTopLevel");
{
{
expect(nestedBlocksTopLevel()).toEqual("nestedBlocksTopLevel");
function nestedBlocksTopLevel() {
return "nestedBlocksTopLevel";
}
}
expect(nestedBlocksTopLevel()).toEqual("nestedBlocksTopLevel");
}
expect(nestedBlocksTopLevel()).toEqual("nestedBlocksTopLevel");
expect(hoistInBlockTopLevel()).toEqual("hoistInBlockTopLevel");
expect(typeof hoistSecondOneTopLevel).toBe("undefined");
{
expect(typeof hoistSecondOneTopLevel).toBe("undefined");
{
expect(hoistSecondOneTopLevel()).toEqual("hoistSecondOneTopLevel");
function hoistSecondOneTopLevel() {
return "hoistFirstOneTopLevel";
}
expect(hoistSecondOneTopLevel()).toEqual("hoistSecondOneTopLevel");
function hoistSecondOneTopLevel() {
return "hoistSecondOneTopLevel";
}
expect(hoistSecondOneTopLevel()).toEqual("hoistSecondOneTopLevel");
{
expect(hoistSecondOneTopLevel()).toEqual("hoistThirdOneTopLevel");
function hoistSecondOneTopLevel() {
return "hoistThirdOneTopLevel";
}
expect(hoistSecondOneTopLevel()).toEqual("hoistThirdOneTopLevel");
}
expect(hoistSecondOneTopLevel()).toEqual("hoistSecondOneTopLevel");
}
expect(hoistSecondOneTopLevel()).toEqual("hoistSecondOneTopLevel");
}
expect(hoistSecondOneTopLevel()).toEqual("hoistSecondOneTopLevel");
test("Non-strict function does hoist", () => {
const noHoistLexFunction = false;
let canCallNonHoisted = 0;
expect(basicHoistFunction()).toEqual("basicHoistFunction");
function basicHoistFunction() {
return "basicHoistFunction";
}
expect(typeof noHoistLexFunction).toBe("boolean");
expect(typeof hoistInBlockFunction).toBe("undefined");
{
expect(noHoistLexFunction()).toEqual("noHoistLexFunction");
++canCallNonHoisted;
expect(basicHoistFunction()).toEqual("basicHoistFunctionInBlock");
++canCallNonHoisted;
expect(hoistInBlockFunction()).toEqual("hoistInBlockFunction");
function hoistInBlockFunction() {
return "hoistInBlockFunction";
}
function noHoistLexFunction() {
return "noHoistLexFunction";
}
function basicHoistFunction() {
return "basicHoistFunctionInBlock";
}
}
expect(canCallNonHoisted).toBe(2);
expect(hoistInBlockFunction()).toEqual("hoistInBlockFunction");
{
{
expect(nestedBlocksFunction()).toEqual("nestedBlocksFunction");
function nestedBlocksFunction() {
return "nestedBlocksFunction";
}
}
expect(nestedBlocksFunction()).toEqual("nestedBlocksFunction");
}
expect(nestedBlocksFunction()).toEqual("nestedBlocksFunction");
expect(hoistInBlockFunction()).toEqual("hoistInBlockFunction");
expect(typeof hoistSecondOneFunction).toBe("undefined");
{
expect(typeof hoistSecondOneFunction).toBe("undefined");
{
expect(hoistSecondOneFunction()).toEqual("hoistSecondOneFunction");
function hoistSecondOneFunction() {
return "hoistFirstOneFunction";
}
expect(hoistSecondOneFunction()).toEqual("hoistSecondOneFunction");
function hoistSecondOneFunction() {
return "hoistSecondOneFunction";
}
expect(hoistSecondOneFunction()).toEqual("hoistSecondOneFunction");
{
expect(hoistSecondOneFunction()).toEqual("hoistThirdOneFunction");
function hoistSecondOneFunction() {
return "hoistThirdOneFunction";
}
expect(hoistSecondOneFunction()).toEqual("hoistThirdOneFunction");
}
expect(hoistSecondOneFunction()).toEqual("hoistSecondOneFunction");
}
expect(hoistSecondOneFunction()).toEqual("hoistSecondOneFunction");
}
expect(hoistSecondOneFunction()).toEqual("hoistSecondOneFunction");
expect(notBlockFunctionTopLevel()).toBe("second");
function notBlockFunctionTopLevel() {
return "first";
}
expect(notBlockFunctionTopLevel()).toBe("second");
function notBlockFunctionTopLevel() {
return "second";
}
expect(notBlockFunctionTopLevel()).toBe("second");
});
test("Strict function does not hoist", () => {
"use strict";
const noHoistLexStrictFunction = false;
let canCallNonHoisted = 0;
expect(basicHoistStrictFunction()).toEqual("basicHoistStrictFunction");
function basicHoistStrictFunction() {
return "basicHoistStrictFunction";
}
expect(typeof noHoistLexStrictFunction).toBe("boolean");
// We cannot use expect(() => ).toThrow because that introduces extra scoping
try {
hoistInBlockStrictFunction;
expect().fail();
} catch (e) {
expect(e).toBeInstanceOf(ReferenceError);
expect(e.message).toEqual("'hoistInBlockStrictFunction' is not defined");
}
{
expect(noHoistLexStrictFunction()).toEqual("noHoistLexStrictFunction");
++canCallNonHoisted;
expect(basicHoistStrictFunction()).toEqual("basicHoistStrictFunctionInBlock");
++canCallNonHoisted;
expect(hoistInBlockStrictFunction()).toEqual("hoistInBlockStrictFunction");
function hoistInBlockStrictFunction() {
return "hoistInBlockStrictFunction";
}
function noHoistLexStrictFunction() {
return "noHoistLexStrictFunction";
}
function basicHoistStrictFunction() {
return "basicHoistStrictFunctionInBlock";
}
}
expect(canCallNonHoisted).toBe(2);
try {
hoistInBlockStrictFunction;
expect().fail();
} catch (e) {
expect(e).toBeInstanceOf(ReferenceError);
expect(e.message).toEqual("'hoistInBlockStrictFunction' is not defined");
}
{
try {
nestedBlocksStrictFunction;
expect().fail();
} catch (e) {
expect(e).toBeInstanceOf(ReferenceError);
expect(e.message).toEqual("'nestedBlocksStrictFunction' is not defined");
}
{
expect(nestedBlocksStrictFunction()).toEqual("nestedBlocksStrictFunction");
function nestedBlocksStrictFunction() {
return "nestedBlocksStrictFunction";
}
}
try {
nestedBlocksStrictFunction;
expect().fail();
} catch (e) {
expect(e).toBeInstanceOf(ReferenceError);
expect(e.message).toEqual("'nestedBlocksStrictFunction' is not defined");
}
}
try {
nestedBlocksStrictFunction;
expect().fail();
} catch (e) {
expect(e).toBeInstanceOf(ReferenceError);
expect(e.message).toEqual("'nestedBlocksStrictFunction' is not defined");
}
expect(notBlockStrictFunctionTopLevel()).toBe("second");
function notBlockStrictFunctionTopLevel() {
return "first";
}
expect(notBlockStrictFunctionTopLevel()).toBe("second");
function notBlockStrictFunctionTopLevel() {
return "second";
}
{
expect(notBlockStrictFunctionTopLevel()).toBe("third");
function notBlockStrictFunctionTopLevel() {
return "third";
}
expect(notBlockStrictFunctionTopLevel()).toBe("third");
}
expect(notBlockStrictFunctionTopLevel()).toBe("second");
// Inside a block inside a strict function gives a syntax error
let didNotRunEval = true;
expect(`
didNotRunEval = false;
() => {
"use strict";
{
function f() {
return "first";
}
function f() {
return "second";
}
}
};
`).not.toEval();
expect(didNotRunEval).toBeTrue();
// However, in eval it's fine but the function does not escape the eval
{
let ranEval = false;
eval(`
expect(hoistSecondOneStrictFunction()).toBe("hoistSecondOneStrictFunction");
function hoistSecondOneStrictFunction() {
return "hoistFirstOneStrictFunction";
}
function hoistSecondOneStrictFunction() {
return "hoistSecondOneStrictFunction";
}
ranEval = true;
`);
expect(ranEval).toBeTrue();
try {
hoistSecondOneStrictFunction;
expect().fail();
} catch (e) {
expect(e).toBeInstanceOf(ReferenceError);
expect(e.message).toEqual("'hoistSecondOneStrictFunction' is not defined");
}
}
});

View file

@ -0,0 +1,143 @@
// Note: This must be checked at script level because that is the only place
// where function order is visible. We introduce some test(...) with
// names to make sure the file does have some tests and a suite.
const evalViaArrow = x => eval(x);
const evalRef = eval;
const expectedBeforeEval = "1256MNQR3478CDHIOP";
const expectedAfterEval = "1256MNQR3478CDHIOPA9B";
const expectedAfterEvalRef = "1256MNQR3478CDHIOPA9BKJL";
const expectOrderToBe = expectedOrder => {
const currentOrder = Object.getOwnPropertyNames(this)
.filter(s => s.length === 2 && s[0] === "f")
.map(s => s[1])
.join("");
expect(currentOrder).toBe(expectedOrder);
};
test("function order should be in tree order and nothing in eval should be included", () => {
expectOrderToBe(expectedBeforeEval);
});
{
function f1() {}
expectOrderToBe(expectedBeforeEval);
function f2() {}
}
expectOrderToBe(expectedBeforeEval);
function f3() {}
expectOrderToBe(expectedBeforeEval);
function f4() {}
expectOrderToBe(expectedBeforeEval);
{
function f5() {}
function f6() {}
}
function f7() {}
function f8() {}
expectOrderToBe(expectedBeforeEval);
eval(`
expectOrderToBe(expectedAfterEval);
function f9() {}
{
function fA() {}
}
function fB() {}
expectOrderToBe(expectedAfterEval);
`);
expectOrderToBe(expectedAfterEval);
function fC() {}
function fD() {}
expectOrderToBe(expectedAfterEval);
// This eval does not do anything because it goes via a function, this means
// its parent environment is not the global environment so it does not have
// a global var environment and does not put the functions on `this`.
evalViaArrow(`
expectOrderToBe(expectedAfterEval);
function fE() {}
{
expectOrderToBe(expectedAfterEval);
function fF() {}
}
function fG() {}
expectOrderToBe(expectedAfterEval);
`);
test("function order should be in tree order, functions in eval should be in order but at the back", () => {
expectOrderToBe(expectedAfterEval);
});
function fH() {}
function fI() {}
expectOrderToBe(expectedAfterEval);
// This is an indirect eval, but still has the global scope as immediate
// parent so it does influence the global `this`.
evalRef(`
expectOrderToBe(expectedAfterEvalRef);
console.log(2, JSON.stringify(Object.getOwnPropertyNames(this).filter(s => s.length === 2)));
function fJ() {}
{
expectOrderToBe(expectedAfterEvalRef);
function fK() {}
}
function fL() {}
expectOrderToBe(expectedAfterEvalRef);
`);
{
function fM() {}
function fN() {}
}
test("function order should be in tree order, functions in evalRef should be in order but at the back", () => {
expectOrderToBe(expectedAfterEvalRef);
});
function fO() {}
function fP() {}
{
function fQ() {}
{
expectOrderToBe(expectedAfterEvalRef);
}
function fR() {}
}
expectOrderToBe(expectedAfterEvalRef);

View file

@ -0,0 +1,148 @@
"use strict";
// Note: This must be checked at script level because that is the only place
// where function order is visible. We introduce some test(...) with
// names to make sure the file does have some tests and a suite.
// Note: In strict mode we expect almost the same result except that the
// functions in blocks do not get hoisted up.
// Only the indirect eval copy gets the global var environment.
const evalViaArrow = x => eval(x);
const evalRef = eval;
const expectedBeforeEval = "3478CDHIOP";
const expectedAfterEval = "3478CDHIOP";
const expectedAfterEvalRef = "3478CDHIOPKJL";
const expectOrderToBe = expectedOrder => {
const currentOrder = Object.getOwnPropertyNames(this)
.filter(s => s.length === 2 && s[0] === "f")
.map(s => s[1])
.join("");
expect(currentOrder).toBe(expectedOrder);
};
test("in strict mode: function order should be in tree order and nothing in eval should be included, in strict mode functions should not be hoisted", () => {
expectOrderToBe(expectedBeforeEval);
});
{
function f1() {}
expectOrderToBe(expectedBeforeEval);
function f2() {}
}
expectOrderToBe(expectedBeforeEval);
function f3() {}
expectOrderToBe(expectedBeforeEval);
function f4() {}
expectOrderToBe(expectedBeforeEval);
{
function f5() {}
function f6() {}
}
function f7() {}
function f8() {}
expectOrderToBe(expectedBeforeEval);
eval(`
expectOrderToBe(expectedAfterEval);
function f9() {}
{
function fA() {}
}
function fB() {}
expectOrderToBe(expectedAfterEval);
`);
expectOrderToBe(expectedAfterEval);
function fC() {}
function fD() {}
expectOrderToBe(expectedAfterEval);
// This eval does not do anything because it goes via a function, this means
// its parent environment is not the global environment so it does not have
// a global var environment and does not put the functions on `this`.
evalViaArrow(`
expectOrderToBe(expectedAfterEval);
function fE() {}
{
expectOrderToBe(expectedAfterEval);
function fF() {}
}
function fG() {}
expectOrderToBe(expectedAfterEval);
`);
test("in strict mode: function order should be in tree order, functions in eval should be in order but at the back, in strict mode functions should not be hoisted", () => {
expectOrderToBe(expectedAfterEval);
});
function fH() {}
function fI() {}
expectOrderToBe(expectedAfterEval);
// This is an indirect eval, but still has the global scope as immediate
// parent so it does influence the global `this`.
evalRef(`
expectOrderToBe(expectedAfterEvalRef);
console.log(2, JSON.stringify(Object.getOwnPropertyNames(this).filter(s => s.length === 2)));
function fJ() {}
{
expectOrderToBe(expectedAfterEvalRef);
function fK() {}
}
function fL() {}
expectOrderToBe(expectedAfterEvalRef);
`);
{
function fM() {}
function fN() {}
}
test("in strict mode: function order should be in tree order, functions in evalRef should be in order but at the back, in strict mode functions should not be hoisted", () => {
expectOrderToBe(expectedAfterEvalRef);
});
function fO() {}
function fP() {}
{
function fQ() {}
{
expectOrderToBe(expectedAfterEvalRef);
}
function fR() {}
}
expectOrderToBe(expectedAfterEvalRef);

View file

@ -0,0 +1,65 @@
describe("parsing freestanding generators", () => {
test("simple", () => {
expect(`function* foo() {}`).toEval();
expect(`function *foo() {}`).toEval();
expect(`function
*foo() {}`).toEval();
expect(`function *await() {}`).toEval();
expect(`function *yield() {}`).toEval();
});
test("yield expression", () => {
expect(`function* foo() { yield; }`).toEval();
expect(`function* foo() { yield (yield); }`).toEval();
expect(`function* foo() { yield (yield foo); }`).toEval();
expect(`function foo() { yield; }`).toEval();
expect(`function foo() { yield 3; }`).not.toEval();
});
test("yield expression only gets the first expression", () => {
expect("function* foo() { yield 2,3 }").toEval();
expect("function* foo() { ({...yield yield, }) }").toEval();
});
test("yield-from expression", () => {
expect(`function* foo() { yield *bar; }`).toEval();
expect(`function* foo() { yield *(yield); }`).toEval();
expect(`function* foo() { yield
*bar; }`).not.toEval();
expect(`function foo() { yield
*bar; }`).toEval();
});
});
describe("parsing object literal generator functions", () => {
test("simple", () => {
expect(`x = { *foo() { } }`).toEval();
expect(`x = { * foo() { } }`).toEval();
expect(`x = { *
foo() { } }`).toEval();
});
test("yield", () => {
expect(`x = { foo() { yield; } }`).toEval();
expect(`x = { *foo() { yield; } }`).toEval();
expect(`x = { *foo() { yield 42; } }`).toEval();
expect(`x = { foo() { yield 42; } }`).not.toEval();
expect(`x = { *foo() { yield (yield); } }`).toEval();
expect(`x = { *
foo() { yield (yield); } }`).toEval();
});
});
describe("parsing classes with generator methods", () => {
test("simple", () => {
expect(`class Foo { *foo() {} }`).toEval();
expect(`class Foo { static *foo() {} }`).toEval();
expect(`class Foo { *foo() { yield; } }`).toEval();
expect(`class Foo { *foo() { yield 42; } }`).toEval();
expect(`class Foo { *constructor() { yield 42; } }`).not.toEval();
});
});
test("function expression names equal to 'yield'", () => {
expect(`function *foo() { (function yield() {}); }`).toEval();
expect(`function *foo() { function yield() {} }`).not.toEval();
});

View file

@ -0,0 +1,4 @@
test("if statement consequent/alternate expression returns empty completion", () => {
expect(eval("1; if (true) {}")).toBeUndefined();
expect(eval("1; if (false) {} else {}")).toBeUndefined();
});

View file

@ -0,0 +1,5 @@
describe("parsing new expressions with optional chaining", () => {
expect("new Object()?.foo").toEval();
expect("new Object?.foo").not.toEval();
expect("(new Object)?.foo").toEval();
});

View file

@ -0,0 +1,26 @@
describe("numeric separators", () => {
test("numeric separator works for 'normal' number", () => {
expect("1_2").toEvalTo(12);
expect("4_2.4_2").toEvalTo(42.42);
expect("1_2e0_2").toEvalTo(1200);
expect("1_2E+_1").not.toEval();
expect("1_2E+0_1").toEvalTo(120);
});
test("cannot use numeric separator after .", () => {
expect("4._3").not.toEval();
expect("0._3").not.toEval();
expect("1_.3._3").not.toEval();
// Actually a valid attempt to get property '_3' on 1.3 which fails but does parse.
expect("1.3._3").toEval();
});
test("cannot use numeric separator in octal escaped number", () => {
expect("00_1").not.toEval();
expect("01_1").not.toEval();
expect("07_3").not.toEval();
expect("00_1").not.toEval();
});
});

View file

@ -0,0 +1,39 @@
test("parse optional-chaining", () => {
expect(`a?.b`).toEval();
expect(`a?.4:.5`).toEval();
expect(`a?.[b]`).toEval();
expect(`a?.b[b]`).toEval();
expect(`a?.b(c)`).toEval();
expect(`a?.b?.(c, d)`).toEval();
expect(`a?.b?.()`).toEval();
expect("a?.b``").not.toEval();
expect("a?.b?.``").not.toEval();
expect("new Foo?.bar").not.toEval();
expect("new (Foo?.bar)").toEval();
expect("(new Foo)?.bar").toEval();
});
test("evaluate optional-chaining", () => {
for (let nullishObject of [null, undefined]) {
expect((() => nullishObject?.b)()).toBeUndefined();
}
expect(
(() => {
let a = {};
return a?.foo?.bar?.baz;
})()
).toBeUndefined();
expect(
(() => {
let a = { foo: { bar: () => 42 } };
return `${a?.foo?.bar?.()}-${a?.foo?.baz?.()}`;
})()
).toBe("42-undefined");
expect(() => {
let a = { foo: { bar: () => 42 } };
return a.foo?.baz.nonExistentProperty;
}).toThrow();
});

View file

@ -0,0 +1,52 @@
test("slash token resolution in lexer", () => {
expect(`{ blah.blah; }\n/foo/`).toEval();
expect("``/foo/").not.toEval();
expect("1/foo/").not.toEval();
expect("1/foo").toEval();
expect("{} /foo/").toEval();
expect("{} /=/").toEval();
expect("{} /=a/").toEval();
expect("{} /* */ /=a/").toEval();
expect("{} /* /a/ */ /=a/").toEval();
expect("(function () {} / 1)").toEval();
expect("(function () {} / 1)").toEval();
expect("+a++ / 1").toEval();
expect("+a-- / 1").toEval();
expect("a.in / b").toEval();
expect("a.instanceof / b").toEval();
expect("class A { #name; d = a.#name / b; }").toEval();
expect("async / b").toEval();
expect("a.delete / b").toEval();
expect("delete / b/").toEval();
expect("a.in / b").toEval();
expect("for (a in / b/) {}").toEval();
expect("a.instanceof / b").toEval();
expect("a instanceof / b/").toEval();
expect("new / b/").toEval();
expect("null / b").toEval();
expect("for (a of / b/) {}").toEval();
expect("a.return / b").toEval();
expect("function foo() { return / b/ }").toEval();
expect("throw / b/").toEval();
expect("a.typeof / b").toEval();
expect("a.void / b").toEval();
expect("void / b/").toEval();
expect("await / b").toEval();
expect("await / b/").not.toEval();
expect("async function foo() { await / b }").not.toEval();
expect("async function foo() { await / b/ }").toEval();
expect("yield / b").toEval();
expect("yield / b/").not.toEval();
expect("function* foo() { yield / b }").not.toEval();
expect("function* foo() { yield / b/ }").toEval();
expect("this / 1").toEval();
expect("this / 1 /").not.toEval();
expect("this / 1 / 1").toEval();
});

View file

@ -0,0 +1,69 @@
describe("switch statement is a valid statement and gets executed", () => {
test("switch statement in a block", () => {
let hit = false;
{
switch (true) {
case true:
hit = true;
}
expect(hit).toBeTrue();
}
});
test("switch statement in an if statement when true", () => {
let hit = false;
var a = true;
if (a)
switch (true) {
case true:
hit = true;
}
else
switch (true) {
case true:
expect().fail();
}
expect(hit).toBeTrue();
});
test("switch statement in an if statement when false", () => {
let hit = false;
var a = false;
if (a)
switch (a) {
default:
expect().fail();
}
else
switch (a) {
default:
hit = true;
}
expect(hit).toBeTrue();
});
test("switch statement in an while statement", () => {
var a = 0;
var loops = 0;
while (a < 1 && loops++ < 5)
switch (a) {
case 0:
a = 1;
}
expect(a).toBe(1);
});
test("switch statement in an for statement", () => {
var loops = 0;
for (let a = 0; a < 1 && loops++ < 5; )
switch (a) {
case 0:
a = 1;
}
expect(loops).toBe(1);
});
});

View file

@ -0,0 +1,9 @@
test("syntax error for an unary expression before exponentiation", () => {
expect(`!5 ** 2`).not.toEval();
expect(`~5 ** 2`).not.toEval();
expect(`+5 ** 2`).not.toEval();
expect(`-5 ** 2`).not.toEval();
expect(`typeof 5 ** 2`).not.toEval();
expect(`void 5 ** 2`).not.toEval();
expect(`delete 5 ** 2`).not.toEval();
});