mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-05-11 09:27:00 +02:00
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).
72 lines
2.2 KiB
JavaScript
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)
|
|
});
|
|
});
|