mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-01 07:33:12 +08:00
- scr_intl.c resolves the default locale as V8 does: ICU's POSIX locale (LC_ALL=C, C.UTF-8, POSIX) becomes en-US, so numbers keep their grouping separators, and other defaults round-trip through their BCP 47 tag. The number formatter, the date pattern generator, the date formatter and the default word segmenter use that locale. - The date formatter moves the Gregorian calendar's Julian cutover to the start of ECMAScript time, so dates before October 1582 print their proleptic Gregorian fields, as in Node. - icu.numFormatDefault, icu.dateLocaleString and icu.dateLocaleTimeString are in the may-throw set: a missing ICU data file throws an Error into the surrounding catch. - `x.toLocaleString(undefined)` reads the receiver before the argument runs. - The linux-x64-gnu runtime pack ships the ICU headers scr_intl.c includes beside its ICU archive (manifest `headers`, verified like the other artifacts). The native compiler's sanitized build compiles scr_intl.c against them and refuses with SC3003 when they are missing or changed. The C-source path (sanitized builds) links the pack's ICU archive and uses those headers, instead of building ICU with Zig's C++ driver. - The macOS pack omits scr_intl.c, scr_intl.h declares ScrDyn, and the library-mode teachings test uses a locale argument, which still refuses with SC2020. Corpus programs pin the C and C.UTF-8 locales, pre-1582 dates and the receiver order against Node. intl-missing-data.test.ts covers the throw. sanitizer.test.ts covers the header lookup.
74 lines
2.6 KiB
TypeScript
74 lines
2.6 KiB
TypeScript
/* The default-locale ICU formatters (scr_intl.c) throw an Error when the
|
|
* ICU data file is missing. Node cannot reach this state (its data is inside
|
|
* the binary), so the test runs the compiled program without the data file
|
|
* beside it and with ICU_DATA pointing at an empty directory: each throw
|
|
* enters the surrounding catch, directly and through a closure, and the
|
|
* program continues after it. */
|
|
import { execFile } from "node:child_process";
|
|
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
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 PROGRAM = `
|
|
function attempt(label: string, run: () => string): void {
|
|
try {
|
|
console.log(label, run());
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : String(error);
|
|
console.log(label, "caught", error instanceof Error, message.includes("icudt78l.dat"));
|
|
}
|
|
}
|
|
attempt("number", () => (1234.5).toLocaleString());
|
|
attempt("date", () => new Date(0).toLocaleString());
|
|
attempt("time", () => new Date(0).toLocaleTimeString());
|
|
try {
|
|
const text = (42).toLocaleString();
|
|
console.log("direct", text);
|
|
} catch {
|
|
console.log("direct caught");
|
|
}
|
|
console.log("after");
|
|
`;
|
|
|
|
test.skipIf(process.platform !== "linux" || process.arch !== "x64")(
|
|
"a missing ICU data file throws into the surrounding catch",
|
|
async () => {
|
|
const dir = mkdtempSync(join(tmpdir(), "scriptc-intl-missing-data-"));
|
|
try {
|
|
const entry = join(dir, "main.ts");
|
|
writeFileSync(entry, PROGRAM);
|
|
const result = await compile(entry, {
|
|
outPath: join(dir, "out", "main"),
|
|
outDir: join(dir, "out"),
|
|
sanitize: process.env["SCRIPTC_SAN"] === "1",
|
|
});
|
|
expect(result.ok, !result.ok ? JSON.stringify(result.diagnostics, null, 2) : "").toBe(true);
|
|
if (!result.ok) return;
|
|
rmSync(join(dirname(result.binaryPath), "icudt78l.dat"), { force: true });
|
|
const empty = join(dir, "no-data");
|
|
mkdirSync(empty);
|
|
const { stdout } = await execFileAsync(result.binaryPath, [], {
|
|
env: { ...process.env, ICU_DATA: empty },
|
|
});
|
|
expect(stdout).toBe(
|
|
[
|
|
"number caught true true",
|
|
"date caught true true",
|
|
"time caught true true",
|
|
"direct caught",
|
|
"after",
|
|
"",
|
|
].join("\n"),
|
|
);
|
|
} finally {
|
|
rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
},
|
|
300_000,
|
|
);
|