diff --git a/packages/compiler/src/frontend/lowering/handle-properties.test.ts b/packages/compiler/src/frontend/lowering/handle-properties.test.ts new file mode 100644 index 000000000..3c1d83ac3 --- /dev/null +++ b/packages/compiler/src/frontend/lowering/handle-properties.test.ts @@ -0,0 +1,26 @@ +import { expect, test } from "vitest"; +import { VOID, type IrExpr, type IrFunction, type IrStmt, type SrcLoc } from "../../ir/ir.js"; +import { applyHandleProperties } from "./handle-properties.js"; +import { ENTRY_NAME, type Lowerer } from "./lowerer.js"; + +const loc: SrcLoc = { file: "main.ts", start: 0, end: 1 }; +const fn = (name: string, body: IrStmt[]): IrFunction => ({ + name, + body, + params: [], + locals: [], + returnType: VOID, + loc, +}); + +test("renames a libCall the IR reaches from several statements once", () => { + const call: IrExpr = { kind: "libCall", fn: "dyn.defineProperty", args: [], type: VOID, loc }; + const use: IrStmt = { kind: "exprStmt", expr: call, loc }; + const functions = [fn(ENTRY_NAME, [use, use]), fn("helper", [use])]; + applyHandleProperties({ writesOntoChildren: true } as unknown as Lowerer, functions); + expect(call).toMatchObject({ kind: "libCall", fn: "child.propsDefine" }); + expect(functions[0]!.body[0]).toMatchObject({ + kind: "exprStmt", + expr: { kind: "libCall", fn: "child.propsInstall" }, + }); +}); diff --git a/packages/compiler/src/frontend/lowering/handle-properties.ts b/packages/compiler/src/frontend/lowering/handle-properties.ts index 4af4ca54d..ba098aab5 100644 --- a/packages/compiler/src/frontend/lowering/handle-properties.ts +++ b/packages/compiler/src/frontend/lowering/handle-properties.ts @@ -283,10 +283,11 @@ const RENAMES: ReadonlyMap = new Map([ * property-table entries (in place) and installs the table in %main. */ export function applyHandleProperties(lowerer: Lowerer, functions: IrFunction[]): void { if (!lowerer.writesOntoChildren && !childAdditions(lowerer).any) return; - const calls: (IrExpr & { kind: "libCall" })[] = []; + // A node the IR shares at several places is renamed once. + const calls = new Set(); const visitor = { expr: (e: IrExpr): boolean => { - if (e.kind === "libCall" && RENAMES.has(e.fn)) calls.push(e); + if (e.kind === "libCall" && RENAMES.has(e.fn)) calls.add(e); return true; }, stmt: (): boolean => true,