Files
scriptc/packages/compiler/test/symbol-fields.test.ts
Chris Tate a07998f667 Expand static three.js workflows and host integration (#621)
* Expand static three.js workflows and host integration

* Preserve JavaScript class state in static three.js workflows

* Preserve inferred JavaScript behavior across static workflows

* Preserve inferred callables and native compatibility boundaries

* Retain annotated JavaScript factory exports and bootstrap-safe call spreads

* Preserve Buffer and child callback identities in static packages

* Clarify JavaScript symbol spread support
2026-10-02 19:56:34 -05:00

86 lines
4.8 KiB
TypeScript

import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { expect, test } from "vitest";
import { analyze } from "../src/index.js";
test.each([
["assignment", 'let key = Symbol.for("x"); key = Symbol.for("y"); class C { [key] = true; }'],
["shorthand assignment", 'var key = Symbol.for("x"); ({ key } = { key: Symbol.for("y") }); class C { [key] = true; }'],
["array assignment", 'var key = Symbol.for("x"); [key] = [Symbol.for("y")]; class C { [key] = true; }'],
["redeclaration", 'var key = Symbol.for("x"); var key = Symbol.for("y"); class C { [key] = true; }'],
["early class key", 'class C { [key] = true; } var key = Symbol.for("x");'],
["early hoisted call", 'read(); var key = Symbol.for("x"); function read() { return key; } class C { [key] = true; }'],
["early shorthand call", 'const callbacks = { read }; callbacks.read(); var key = Symbol.for("x"); function read() { return key; } class C { [key] = true; }'],
["for-of assignment", 'var key = Symbol.for("x"); for (key of [Symbol.for("y")]) {} class C { [key] = true; }'],
["runtime registry name", 'function text() { return "x"; } const key = Symbol.for(text()); class C { [key] = true; }'],
["local symbol", 'class C { constructor() { const key = Symbol("x"); this[key] = true; } }'],
["distinct symbol collision", 'const key = Symbol("x"); const other = Symbol.for("x"); class C { [key] = true; [other] = false; }'],
])("refuses symbol fields with %s", (_name, source) => {
const dir = mkdtempSync(join(tmpdir(), "scriptc-symbol-fields-"));
try {
const entry = join(dir, "main.cjs");
writeFileSync(entry, source + "\nnew C();\n");
const { coverage } = analyze(entry);
const diagnostics = [...coverage.diagnostics, ...(coverage.runtimeFences ?? [])];
expect(diagnostics.some((d) => d.code === "SC1090" && /symbol|computed class fields/i.test(d.message)), JSON.stringify(coverage, null, 2)).toBe(true);
expect(coverage.stats.statementsIsland).toBe(0);
} finally {
rmSync(dir, { recursive: true, force: true });
}
});
test.each([
["constant registry name", 'const text = "x"; const key = Symbol.for(text); class C { [key] = true; }'],
["uninitialized JavaScript field", 'const key = Symbol.for("x"); class C { [key]; }'],
])("accepts symbol fields with %s", (_name, source) => {
const dir = mkdtempSync(join(tmpdir(), "scriptc-symbol-fields-"));
try {
const entry = join(dir, "main.cjs");
writeFileSync(entry, source + "\nnew C();\n");
const { coverage } = analyze(entry);
expect(coverage.preflightFailed).toBe(false);
expect([...coverage.diagnostics, ...(coverage.runtimeFences ?? [])]).toEqual([]);
expect(coverage.stats.statementsIsland).toBe(0);
} finally {
rmSync(dir, { recursive: true, force: true });
}
});
test.each([
["rest declaration", "const { name, ...copy } = value; console.log(copy);"],
["rest assignment", "let copy = {}; let name; ({ name, ...copy } = value); console.log(copy);"],
["Object.assign", "const copy = Object.assign({}, value); console.log(copy);"],
["Object.assign with several sources", "const copy = Object.assign({}, value, { extra: 1 }); console.log(copy);"],
["Object.assign with spread sources", "const copy = Object.assign({}, ...[value]); console.log(copy);"],
])("refuses %s copies of symbol fields", (_name, copy) => {
const dir = mkdtempSync(join(tmpdir(), "scriptc-symbol-copy-"));
try {
const entry = join(dir, "main.cjs");
writeFileSync(entry, `const key = Symbol.for("x"); class Base { [key] = true; name = "base"; } class C extends Base {} const value = new C(); ${copy}`);
const { coverage } = analyze(entry);
const diagnostics = [...coverage.diagnostics, ...(coverage.runtimeFences ?? [])];
expect(diagnostics.some((d) => /rest bindings|copying class instances|object spread/.test(d.message)), JSON.stringify(coverage, null, 2)).toBe(true);
expect(coverage.stats.statementsIsland).toBe(0);
} finally {
rmSync(dir, { recursive: true, force: true });
}
});
test.each([
["spread", "const copy = { ...value }; console.log(copy[key]);"],
["computed-key spread", 'let name = "extra"; const copy = { [name]: 1, ...value }; console.log(copy[key]);'],
])("accepts %s copies of symbol fields", (_name, copy) => {
const dir = mkdtempSync(join(tmpdir(), "scriptc-symbol-copy-"));
try {
const entry = join(dir, "main.cjs");
writeFileSync(entry, `const key = Symbol.for("x"); class Base { [key] = true; name = "base"; } class C extends Base {} const value = new C(); ${copy}`);
const { coverage } = analyze(entry);
expect(coverage.preflightFailed).toBe(false);
expect([...coverage.diagnostics, ...(coverage.runtimeFences ?? [])]).toEqual([]);
expect(coverage.stats.statementsIsland).toBe(0);
} finally {
rmSync(dir, { recursive: true, force: true });
}
});