mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-02 08:35:07 +08:00
- Remove C emission, debugging options, and automatic backend fallback. - Complete LLVM lowering and native bootstrap coverage across supported targets. - Link executables and libraries with precompiled C runtime packs, including iOS and Android.
117 lines
5.2 KiB
TypeScript
117 lines
5.2 KiB
TypeScript
import { execFile } from "node:child_process";
|
|
import { cp, mkdtemp, readFile, rm, stat } from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { promisify } from "node:util";
|
|
import { expect, test } from "vitest";
|
|
import { compile } from "../src/index.js";
|
|
|
|
const exec = promisify(execFile);
|
|
const corpus = join(import.meta.dirname, "../../../tests/corpus/3066-source-debugging");
|
|
const sanitize = process.env["SCRIPTC_SAN"] === "1";
|
|
|
|
// Xcode ships this debugger. Resolve real breakpoints after temporary native
|
|
// objects have been deleted, including after the executable cache restores.
|
|
test.runIf(process.platform === "darwin").each(["llvm"] as const)(
|
|
"dev %s builds preserve source breakpoints across cache hits",
|
|
async (backend) => {
|
|
const dir = await mkdtemp(join(tmpdir(), "scriptc-debug-"));
|
|
try {
|
|
const source = join(dir, 'source space é "quoted"');
|
|
await cp(corpus, source, { recursive: true });
|
|
const entry = join(source, "main.ts");
|
|
const outPath = join(dir, "program");
|
|
const options = { outDir: dir, outPath, backend, optimization: "dev" as const, sanitize };
|
|
const oracle = await exec(process.execPath, [entry]);
|
|
for (let build = 0; build < 2; build++) {
|
|
const result = await compile(entry, options);
|
|
expect(result.ok, !result.ok ? JSON.stringify(result.diagnostics) : "").toBe(true);
|
|
if (!result.ok) return;
|
|
expect((await stat(`${outPath}.dSYM/Contents/Resources/DWARF/program`)).size).toBeGreaterThan(0);
|
|
const native = await exec(outPath);
|
|
expect(native.stdout).toBe(oracle.stdout);
|
|
expect(native.stderr).toBe(oracle.stderr);
|
|
const debug = await exec("lldb", ["--batch", outPath,
|
|
"-o", "breakpoint set --file main.ts --line 4 --move-to-nearest-code false",
|
|
"-o", "breakpoint set --file helper.ts --line 4 --move-to-nearest-code false",
|
|
"-o", "breakpoint list",
|
|
]);
|
|
expect(debug.stdout).not.toContain("pending");
|
|
expect(debug.stdout).toContain("main.ts:4");
|
|
expect(debug.stdout).toContain("helper.ts:4");
|
|
await rm(outPath);
|
|
await rm(`${outPath}.dSYM`, { recursive: true });
|
|
}
|
|
for (const mode of ["release", "dev", "strip"] as const) {
|
|
const result = await compile(entry, {
|
|
...options,
|
|
optimization: mode === "release" ? "release" : "dev",
|
|
strip: mode === "strip",
|
|
});
|
|
expect(result.ok).toBe(true);
|
|
if (mode === "dev") {
|
|
expect((await stat(`${outPath}.dSYM`)).isDirectory()).toBe(true);
|
|
} else {
|
|
await expect(stat(`${outPath}.dSYM`)).rejects.toMatchObject({ code: "ENOENT" });
|
|
}
|
|
}
|
|
} finally {
|
|
await rm(dir, { recursive: true, force: true });
|
|
}
|
|
},
|
|
);
|
|
|
|
test.runIf(process.platform === "darwin")("dev LLVM object and assembly outputs carry source locations", async () => {
|
|
const dir = await mkdtemp(join(tmpdir(), "scriptc-debug-object-"));
|
|
try {
|
|
for (const outputKind of ["obj", "asm"] as const) {
|
|
const outPath = join(dir, `program.${outputKind}`);
|
|
const result = await compile(join(corpus, "main.ts"), { outDir: dir, outPath, outputKind, optimization: "dev" });
|
|
expect(result.ok, !result.ok ? JSON.stringify(result.diagnostics) : "").toBe(true);
|
|
if (!result.ok) continue;
|
|
if (outputKind === "obj") {
|
|
const { stdout } = await exec("dwarfdump", ["--debug-line", outPath]);
|
|
expect(stdout).toContain("main.ts");
|
|
expect(stdout).toContain("helper.ts");
|
|
const info = await exec("dwarfdump", ["--debug-info", outPath]);
|
|
expect(info.stdout).toContain("DW_TAG_variable");
|
|
expect(info.stdout).toContain("DW_TAG_formal_parameter");
|
|
} else {
|
|
const assembly = await readFile(outPath, "utf8");
|
|
expect(assembly).toContain(".loc");
|
|
expect(assembly).toContain("helper.ts");
|
|
}
|
|
}
|
|
} finally {
|
|
await rm(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test.each([
|
|
"800-switch-basics.ts",
|
|
"1021-async-ordering.ts",
|
|
"1452-return-through-finally.ts",
|
|
"2013-generators-sent-values.ts",
|
|
"603-closures-rc-stress.ts",
|
|
])("dev code generation preserves control flow: %s", async (fixture) => {
|
|
const dir = await mkdtemp(join(tmpdir(), "scriptc-debug-flow-"));
|
|
try {
|
|
const entry = join(corpus, "..", fixture);
|
|
const node = await exec(process.execPath, [entry]);
|
|
for (const backend of ["llvm"] as const) {
|
|
const outPath = join(dir, process.platform === "win32" ? `${backend}.exe` : backend);
|
|
const built = await compile(entry, { outDir: dir, outPath, backend, optimization: "dev", sanitize });
|
|
expect(built.ok, !built.ok ? JSON.stringify(built.diagnostics) : "").toBe(true);
|
|
if (!built.ok) continue;
|
|
const native = await exec(outPath);
|
|
expect(native.stdout).toBe(node.stdout);
|
|
const stderr = sanitize
|
|
? native.stderr.replace(/^==\d+==WARNING: ASan doesn't fully support makecontext\/swapcontext functions and may produce false positives in some cases!\n/gm, "")
|
|
: native.stderr;
|
|
expect(stderr).toBe(node.stderr);
|
|
}
|
|
} finally {
|
|
await rm(dir, { recursive: true, force: true });
|
|
}
|
|
});
|