mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-28 06:53:42 +08:00
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.
53 lines
1.8 KiB
TypeScript
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\(/);
|
|
});
|