mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-08 08:44:37 +08:00
With the shape overlay on, a first round of structural rewrites runs before the typed overlay (shape-inference/static-objects.ts, stage 2b of DESIGN-constant-objects.md), proven by the constant object graphs: - An ES5 constructor stored by a top-level statement whose prototype is a constant object of plain methods becomes a class declaration with those methods, when the program cannot tell: the function is only ever constructed and never escapes, its prototype is never replaced, nothing enumerates instances with for-in or reflects on the prototype or the function, nothing reads their names or sources, the bodies are strict-mode safe, and only definitions run between the constructor and its last method. - A constant namespace member holding a `this`-free function expression becomes a function declaration, and reads of the member in the same file whose receiver is exactly that constant object refer to it directly. The rounds rerun the analysis on the rebuilt program, the overlay now also covers the program's own JavaScript files, and the build report counts the rewrites. A JavaScript switch whose case tests lower to another type than the discriminant compares with === instead of being refused. Namespace benchmark (n=20000): 1.55 s -> 0.42-0.47 s, output identical to Node (typed TypeScript twin 85 ms, Node 21 ms). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
50 lines
2.2 KiB
TypeScript
50 lines
2.2 KiB
TypeScript
/* Static-object specialization, stage 2b (shape-inference/static-objects.ts):
|
|
* with the shape overlay on, the npm case static-objects (through its JavaScript consumer, js-main.cjs) compiles its one
|
|
* indistinguishable constructor function as a class and its `this`-free
|
|
* namespace function as a direct function, keeps every observably
|
|
* different form as written, and prints what Node prints. npm.test.ts runs
|
|
* the same case without the overlay. */
|
|
import { execFile } from "node:child_process";
|
|
import { mkdirSync } from "node:fs";
|
|
import { dirname, join } from "node:path";
|
|
import { promisify } from "node:util";
|
|
import { expect, test } from "vitest";
|
|
import { compile } from "@scriptc/compiler";
|
|
|
|
const execFileAsync = promisify(execFile);
|
|
const repoRoot = join(import.meta.dirname, "../..");
|
|
const entry = join(repoRoot, "tests/fixtures/npm/cases/static-objects/js-main.cjs");
|
|
const outDir = join(repoRoot, "node_modules/.cache/scriptc-tests/static-objects");
|
|
const sanitize = process.env["SCRIPTC_SAN"] === "1";
|
|
|
|
test(
|
|
"constructor functions and namespace functions compile as classes and direct functions",
|
|
{ timeout: 600_000 },
|
|
async () => {
|
|
mkdirSync(outDir, { recursive: true });
|
|
const saved = process.env["SCRIPTC_SHAPE_OVERLAY"];
|
|
process.env["SCRIPTC_SHAPE_OVERLAY"] = "1";
|
|
let result;
|
|
try {
|
|
result = await compile(entry, { outPath: join(outDir, "main"), outDir, sanitize });
|
|
} finally {
|
|
if (saved === undefined) delete process.env["SCRIPTC_SHAPE_OVERLAY"];
|
|
else process.env["SCRIPTC_SHAPE_OVERLAY"] = saved;
|
|
}
|
|
if (!result.ok)
|
|
throw new Error(result.diagnostics.map((d) => `${d.code}: ${d.message}`).join("\n"));
|
|
const report = (result.warnings ?? []).find((w) => w.code === "SC6103");
|
|
expect(report?.message).toMatch(
|
|
/compiled 1 constructor function as classes and 2 namespace functions as direct functions/,
|
|
);
|
|
const pending = execFileAsync(result.binaryPath, [], { cwd: dirname(entry), encoding: "utf8" });
|
|
pending.child.stdin?.end();
|
|
const { stdout } = await pending;
|
|
const node = await execFileAsync(process.execPath, [entry], {
|
|
cwd: dirname(entry),
|
|
encoding: "utf8",
|
|
});
|
|
expect(stdout).toBe(node.stdout);
|
|
},
|
|
);
|