ladybird/Tests/LibJS/Runtime/functions/function-name.js
Andreas Kling 62ac304099 LibJS: Avoid naming parenthesized assignment functions
NamedEvaluation for assignment only applies when the left hand side is
an IdentifierRef. Parenthesized identifiers are still assignment
targets, but are not IdentifierRefs, so `(fn) = function () {}` must
leave the function anonymous.

Carry the parser's parenthesized-LHS bit into assignment codegen. Use
it when setting pending_lhs_name for plain and logical assignment, and
cover parenthesized assignment targets in the function-name tests.
2026-05-22 01:56:57 +02:00

145 lines
4.1 KiB
JavaScript

test("basic functionality", () => {
expect(function () {}.name).toBe("");
function bar() {}
expect(bar.name).toBe("bar");
expect((bar.name = "baz")).toBe("baz");
expect(bar.name).toBe("bar");
});
test("function assigned to variable", () => {
let foo = function () {};
expect(foo.name).toBe("foo");
expect((foo.name = "bar")).toBe("bar");
expect(foo.name).toBe("foo");
let a, b;
a = b = function () {};
expect(a.name).toBe("b");
expect(b.name).toBe("b");
});
test("functions in array assigned to variable", () => {
const arr = [function () {}, function () {}, function () {}];
expect(arr[0].name).toBe("");
expect(arr[1].name).toBe("");
expect(arr[2].name).toBe("");
});
test("functions in objects", () => {
let f;
let o = { a: function () {} };
expect(o.a.name).toBe("a");
f = o.a;
expect(f.name).toBe("a");
expect(o.a.name).toBe("a");
o = { ...o, b: f };
expect(o.a.name).toBe("a");
expect(o.b.name).toBe("a");
// Member expressions do not get named.
o.c = function () {};
expect(o.c.name).toBe("");
});
test("computed property names infer anonymous function names", () => {
const namedSymbol = Symbol("named");
const emptySymbol = Symbol("");
const unnamedSymbol = Symbol();
const getterSymbol = Symbol("getter");
const setterSymbol = Symbol("");
const object = {
[1]: function () {},
[2]: class {},
[namedSymbol]: () => {},
[emptySymbol]: function* () {},
[unnamedSymbol]: async function () {},
[/a/]: function () {},
get [getterSymbol]() {},
set [setterSymbol](value) {},
};
expect(object[1].name).toBe("1");
expect(object[2].name).toBe("2");
expect(object[namedSymbol].name).toBe("[named]");
expect(object[emptySymbol].name).toBe("[]");
expect(object[unnamedSymbol].name).toBe("");
expect(object[/a/].name).toBe("/a/");
expect(Object.getOwnPropertyDescriptor(object, getterSymbol).get.name).toBe("get [getter]");
expect(Object.getOwnPropertyDescriptor(object, setterSymbol).set.name).toBe("set []");
});
test("computed property name inference does not leak between evaluations", () => {
function objectName(key) {
return { [key]: function () {} }[key].name;
}
function classMethodName(key) {
return new (class {
[key]() {}
})()[key].name;
}
expect(objectName("first")).toBe("first");
expect(objectName("second")).toBe("second");
expect(classMethodName("first")).toBe("first");
expect(classMethodName("second")).toBe("second");
});
test("computed property name inference only applies to anonymous definitions", () => {
const key = "computed";
const value = [function () {}][0];
const object = { [key]: value };
expect(object[key].name).toBe("");
});
test("names of native functions", () => {
expect(console.debug.name).toBe("debug");
expect((console.debug.name = "warn")).toBe("warn");
expect(console.debug.name).toBe("debug");
});
describe("some anonymous functions get renamed", () => {
test("direct assignment does name new function expression", () => {
// prettier-ignore
let f1 = (function () {});
expect(f1.name).toBe("f1");
let f2 = false;
f2 ||= function () {};
expect(f2.name).toBe("f2");
});
test("assignment from variable does not name", () => {
const f1 = function () {};
let f3 = f1;
expect(f3.name).toBe("f1");
});
test("assignment via expression does not name", () => {
let f4 = false || function () {};
expect(f4.name).toBe("");
});
test("parenthesized assignment target does not name", () => {
let f5;
eval("(f5) = function () {};");
expect(f5.name).toBe("");
let f6;
eval("(f6) ??= function () {};");
expect(f6.name).toBe("");
let f7 = false;
eval("(f7) ||= function () {};");
expect(f7.name).toBe("");
let f8 = true;
eval("(f8) &&= function () {};");
expect(f8.name).toBe("");
});
});