Files
scriptc/tests/harness/static-tables.test.ts
T
Chris Tate 2a0489a334 Keep the TypeError of receivers that may be null in static-object rewrites
The rewrites decide on a read's non-nullish receivers, since the analysis
also gives an absent key's read undefined (ns.util before ns.util = {...}
runs). A rewrite then removed the property access, so a receiver that was
in fact null or undefined no longer threw.

- A read whose receiver may also be nullish is rewritten to the read as
  written followed by the replacement in a comma expression: a direct
  reference becomes (recv.k, name) and a folded member (recv.k, literal),
  so a nullish receiver throws Node's TypeError and any other receiver
  yields the same value.
- A function table call through such a table reads table[key] first,
  before its arguments, where the call reads it.
- The static-tables case reads, calls and dispatches through receivers
  that are the constant object or undefined, and records the ts7
  baselines of the npm cases added in this change.
2026-10-25 13:23:54 -05:00

47 lines
2.0 KiB
TypeScript

/* Static-object specialization (shape-inference/static-objects.ts): with the
* shape overlay on, the npm case static-tables (through its JavaScript
* consumer, js-main.cjs) calls its constant function table directly, reads
* constant primitive members as literals, refers to namespace members of
* another module through that module's exports, keeps every form whose
* dynamic read is observable, keeps the TypeError of a receiver that may
* be undefined, and prints what Node prints. The npm suites
* run the TypeScript consumer with and 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-tables/js-main.cjs");
const outDir = join(repoRoot, "node_modules/.cache/scriptc-tests/static-tables");
const sanitize = process.env["SCRIPTC_SAN"] === "1";
test(
"constant function tables, primitive members and cross-module members compile directly",
{ timeout: 600_000 },
async () => {
mkdirSync(outDir, { recursive: true });
const result = await compile(entry, {
outPath: join(outDir, "main"),
outDir,
sanitize,
shapeOverlay: true,
});
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(/2 function table calls, 9 constant reads\)/);
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);
},
);