Files
ladybird/Tests/LibJS/Runtime/functions/function-expression-scoping.js
Andreas Kling 5238841da2 LibJS: Mark named function expression identifiers at individual level
Previously, when parsing a named function expression like
`Oops = function Oops() { Oops }`, the parser set a group-level flag
`might_be_variable_in_lexical_scope_in_named_function_assignment` that
propagated to the parent scope. This incorrectly prevented ALL `Oops`
identifiers from being marked as global, including those outside the
function expression.

Fix this by marking identifiers individually using
`set_is_inside_scope_with_eval()` only for identifiers inside the
function scope. This allows identifiers outside the function expression
to correctly use GetGlobal/SetGlobal while identifiers inside still
use GetBinding (since they may refer to the function's name binding).
2026-01-27 10:58:39 +01:00

72 lines
2.2 KiB
JavaScript

describe("named function expression scoping", () => {
test("name is visible inside function body", () => {
const f = function factorial(n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
};
expect(f(5)).toBe(120);
});
test("name is not visible outside function body", () => {
const f = function inner() {
return 42;
};
expect(() => inner).toThrowWithMessage(ReferenceError, "'inner' is not defined");
});
test("name binding is immutable inside function", () => {
const f = function immutableName() {
immutableName = "changed";
return typeof immutableName;
};
expect(f()).toBe("function");
});
test("outer variable with same name is shadowed inside", () => {
let shadow = "outer";
const f = function shadow() {
return typeof shadow;
};
expect(shadow).toBe("outer");
expect(f()).toBe("function");
});
test("assignment to outer variable with same name works", () => {
let outer;
outer = function outer() {
return outer.name;
};
expect(outer()).toBe("outer");
expect(typeof outer).toBe("function");
});
test("property access on outer variable with same name works", () => {
let Foo;
Foo = function Foo() {};
Foo.bar = 42;
expect(Foo.bar).toBe(42);
});
test("nested named function expressions", () => {
const outer = function outerFn() {
const inner = function innerFn() {
return typeof outerFn + "," + typeof innerFn;
};
return inner();
};
expect(outer()).toBe("function,function");
});
test("same name in nested function expressions", () => {
const outer = function sameName() {
const inner = function sameName() {
return sameName.length;
};
return [sameName.length, inner()];
};
const result = outer();
expect(result[0]).toBe(0); // outer sameName
expect(result[1]).toBe(0); // inner sameName (shadows outer)
});
});