ladybird/Libraries/LibJS/Tests/syntax/function-hoisting.js

352 lines
9.5 KiB
JavaScript

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");
}
}
});