Files
scriptc/packages/compiler/test/live-view-commit-emission.test.ts
T
Chris Tate 8f8e972ad8 Emit no commit adapters for class and record views without dynamic stores
A class or record instance seen as unknown is a live view: reads
materialize it, and a runtime store into the view commits the result
back into the native object. Every class and record reachable from a
value passed to unknown got a commit adapter, and each adapter a checked
conversion for every field type.

A view commits only after a runtime store into it. When the program has
no such store (dynamicFieldStores, as the int32 and cycle analyses use),
class and record views are created without a commit. Array, bytes and
collection views keep theirs: a mutating array method called on an
unknown value commits without a store helper. Library builds keep every
commit.

dyn.objCreate (Object.create) and dyn.getPrototype
(Object.getPrototypeOf), which the property bags and prototype data of
class hierarchies use, are added to DYN_READ_ONLY: they create an object
or read a prototype and reach no commit.
2026-10-16 04:45:45 -05:00

53 lines
1.8 KiB
TypeScript

import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { expect, test } from "vitest";
import { compile } from "../src/index.js";
async function emit(body: string): Promise<string> {
const dir = await mkdtemp(join(tmpdir(), "scriptc-live-view-commit-"));
try {
const entry = join(dir, "main.ts");
const outPath = join(dir, "main.ll");
await writeFile(
entry,
`
class Node {
kind: number;
next: Node | undefined;
data: { label: string; size: number };
constructor(kind: number) {
this.kind = kind;
this.next = undefined;
this.data = { label: "n" + kind, size: kind };
}
}
function debug(x: unknown): string {
if (typeof x !== "object" || x === null) return String(x);
${body}
}
const n = new Node(process.argv.length);
n.next = new Node(2);
console.log(debug(n));
`,
);
const result = await compile(entry, { outDir: dir, outPath, outputKind: "llvm" });
expect(result.ok, JSON.stringify(result.diagnostics)).toBe(true);
return await readFile(outPath, "utf8");
} finally {
await rm(dir, { recursive: true, force: true });
}
}
test("class and record views commit nothing when no dynamic store exists", async () => {
const llvm = await emit(`return Object.keys(x).join(",") + JSON.stringify(Object.values(x));`);
expect(llvm).toMatch(/define internal ptr @sc_ldr_\d+_materialize\(/);
expect(llvm).not.toMatch(/define internal void @sc_ldr_\d+_materialize_commit\(/);
});
test("a dynamic store keeps the commits of class and record views", async () => {
const llvm = await emit(`(x as Record<string, unknown>)["kind"] = 1.5;
return Object.keys(x).join(",");`);
expect(llvm).toMatch(/define internal void @sc_ldr_\d+_materialize_commit\(/);
});