mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 18:18:12 +00:00
LibJS: No longer hoist if parent scope has a function with the same name
This commit is contained in:
parent
54b2d8c1b8
commit
5ca6e8dca8
2 changed files with 357 additions and 3 deletions
352
Userland/Libraries/LibJS/Tests/syntax/function-hoisting.js
Normal file
352
Userland/Libraries/LibJS/Tests/syntax/function-hoisting.js
Normal 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");
|
||||
}
|
||||
}
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue