mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-21 05:50:55 +08:00
Complete worker ABI and validation coverage
- Align shared-buffer layouts and native worker declarations. - Complete runtime link and worker lifecycle contracts. - Check fixture baselines before running parity suites.
This commit is contained in:
@@ -46,6 +46,8 @@ SCRIPTC_TEST_WORKERS=4 SCRIPTC_SAN=1 pnpm test # sanitized lane
|
||||
|
||||
Corpus programs are differential tests against Node: every program runs under Node and as a compiled native binary, and stdout, stderr, and exit codes must match byte-for-byte. A new feature lands with corpus programs that pin its behavior both ways.
|
||||
|
||||
Before opening a PR that adds or changes corpus, diagnostic, or frontend fixtures, run `pnpm test:ts7 --baselines-only`. This fast completeness check uses the same fixture discovery as the parity suite; it does not type-check or replace behavioral parity. Generate missing preflight/order baselines, review their diagnostics and module order, and run the affected `order-parity.test.ts` batches before pushing. Keep existing baseline entries unchanged unless the corresponding behavior intentionally changes. The full `pnpm test:ts7` command also checks completeness before starting its sweep.
|
||||
|
||||
Test location follows scope:
|
||||
|
||||
- Co-locate white-box unit tests with implementation files under
|
||||
|
||||
@@ -1568,11 +1568,11 @@ export class LlEmitter {
|
||||
// scr_runtime.h's natural alignment: kind at 8, f64 at 16, b at 24,
|
||||
// payload at 32).
|
||||
`%ScrCaught = type { ${this.sizeType}, i32, double, i8, ptr, ptr, ptr, ptr }`,
|
||||
// ScrBytes { rc, len, elem(i32+pad), data, backing }. Indexed
|
||||
// ScrBytes { rc, len, elem(i32+pad), data, backing, brands, shared }. Indexed
|
||||
// typed-array access GEPs through this directly: the IR type already
|
||||
// fixes elem, so the hot path needs neither a runtime kind load nor
|
||||
// the generic scr_bytes_get/set call.
|
||||
`%ScrBytes = type { ${this.sizeType}, ${this.sizeType}, i32, ptr, ptr, i8 }`,
|
||||
`%ScrBytes = type { ${this.sizeType}, ${this.sizeType}, i32, ptr, ptr, i8, i8, i8, ptr }`,
|
||||
// The capture box { rc, kind, obj_retain, obj_release, obj_trace,
|
||||
// slot } — TDZ reads peek the payload slot (offset 40) directly.
|
||||
`%ScrBox = type { ${this.sizeType}, i32, ptr, ptr, ptr, i64 }`,
|
||||
|
||||
@@ -271,7 +271,7 @@ test("LLVM emission performs typed-array element access directly on the valid pa
|
||||
const mod = fixture();
|
||||
expect(validateModule(mod)).toEqual([]);
|
||||
const ll = emitLlvmModule(mod);
|
||||
expect(ll).toContain("%ScrBytes = type { i64, i64, i32, ptr, ptr, i8 }");
|
||||
expect(ll).toContain("%ScrBytes = type { i64, i64, i32, ptr, ptr, i8, i8, i8, ptr }");
|
||||
expect(ll).toContain("getelementptr inbounds i8");
|
||||
expect(ll).toContain("getelementptr inbounds i32");
|
||||
expect(ll).toContain("getelementptr inbounds float");
|
||||
|
||||
@@ -61,6 +61,8 @@ const EMIT_FIXTURES = [
|
||||
"996-fs-rc-stress.ts",
|
||||
"997-fs-modules/main.ts",
|
||||
"1006-json-fs-config.ts",
|
||||
"native-worker-roots/main.ts",
|
||||
"native-worker-shared-views.ts",
|
||||
];
|
||||
|
||||
interface CProto {
|
||||
@@ -158,6 +160,7 @@ function splitParams(argsText: string): string[] {
|
||||
async function parseHeader(): Promise<{ protos: Map<string, CProto>; dataSyms: Set<string> }> {
|
||||
const raw = await readFile(headerPath, "utf8");
|
||||
const src = raw
|
||||
.replace(/\\\r?\n/g, "")
|
||||
.replace(/\/\*[\s\S]*?\*\//g, " ")
|
||||
.replace(/\/\/[^\n]*/g, " ")
|
||||
.replace(/^[ \t]*#[^\n]*$/gm, " ");
|
||||
@@ -317,19 +320,22 @@ describe("LLVM backend declares match scr_runtime.h prototypes", () => {
|
||||
#include <stdint.h>
|
||||
#include "scr_runtime.h"
|
||||
|
||||
/* LLVM emits: %ScrBytes = type { i64, i64, i32, ptr, ptr, i8 }.
|
||||
/* LLVM emits: %ScrBytes = type { i64, i64, i32, ptr, ptr, i8, i8, i8, ptr }.
|
||||
* Keep every ABI fact used by its field-index GEPs explicit here. */
|
||||
_Static_assert(sizeof(size_t) == 8, "LLVM ScrBytes expects 64-bit size_t");
|
||||
_Static_assert(sizeof(void *) == 8, "LLVM ScrBytes expects 64-bit pointers");
|
||||
_Static_assert(sizeof(ScrBytesElem) == 4, "LLVM ScrBytes elem field is i32");
|
||||
_Static_assert(_Alignof(ScrBytes) == 8, "LLVM ScrBytes alignment changed");
|
||||
_Static_assert(sizeof(ScrBytes) == 48, "LLVM ScrBytes size changed");
|
||||
_Static_assert(sizeof(ScrBytes) == 56, "LLVM ScrBytes size changed");
|
||||
_Static_assert(offsetof(ScrBytes, rc) == 0, "LLVM ScrBytes.rc offset changed");
|
||||
_Static_assert(offsetof(ScrBytes, len) == 8, "LLVM ScrBytes.len offset changed");
|
||||
_Static_assert(offsetof(ScrBytes, elem) == 16, "LLVM ScrBytes.elem offset changed");
|
||||
_Static_assert(offsetof(ScrBytes, data) == 24, "LLVM ScrBytes.data offset changed");
|
||||
_Static_assert(offsetof(ScrBytes, backing) == 32, "LLVM ScrBytes.backing offset changed");
|
||||
_Static_assert(offsetof(ScrBytes, is_buffer) == 40, "LLVM ScrBytes.is_buffer offset changed");
|
||||
_Static_assert(offsetof(ScrBytes, is_data_view) == 41, "LLVM ScrBytes.is_data_view offset changed");
|
||||
_Static_assert(offsetof(ScrBytes, external) == 42, "LLVM ScrBytes.external offset changed");
|
||||
_Static_assert(offsetof(ScrBytes, shared) == 48, "LLVM ScrBytes.shared offset changed");
|
||||
`,
|
||||
);
|
||||
const driver = resolveCc();
|
||||
|
||||
@@ -14517,6 +14517,126 @@
|
||||
"<repo>/tests/corpus/scalar-string-slices.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/atomics-integer-operations.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/atomics-integer-operations.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/native-worker-buffer-consumers.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/native-worker-buffer-consumers.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/native-worker-clone-errors.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/native-worker-clone-errors.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/native-worker-commonjs.cjs": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/native-worker-commonjs.cjs"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/native-worker-delayed-messages.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/native-worker-delayed-messages.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/native-worker-environment.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/native-worker-environment.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/native-worker-error.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/native-worker-error.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/native-worker-exit-listeners.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/native-worker-exit-listeners.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/native-worker-io.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/native-worker-io.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/native-worker-loop-cancellation.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/native-worker-loop-cancellation.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/native-worker-message-graphs.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/native-worker-message-graphs.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/native-worker-messages.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/native-worker-messages.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/native-worker-port-lifetime.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/native-worker-port-lifetime.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/native-worker-process-exit.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/native-worker-process-exit.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/native-worker-references.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/native-worker-references.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/native-worker-roots/main.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/native-worker-roots/main.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/native-worker-shared-views.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/native-worker-shared-views.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/native-worker-terminate-loop.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/native-worker-terminate-loop.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/native-worker-terminate-suspended.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/native-worker-terminate-suspended.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/native-worker-terminate-wait.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/native-worker-terminate-wait.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
* sweep and the upgrade playbook's step 2. */
|
||||
|
||||
import { execFile } from "node:child_process";
|
||||
import { globSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
@@ -47,6 +47,7 @@ import { afterAll, describe, expect, test } from "vitest";
|
||||
import { checkPreflightTs7 } from "../../src/frontend/program-node.js";
|
||||
import { Ts7Host } from "../../src/frontend/ts7/program-adapter.js";
|
||||
import type { ScrDiagnostic } from "../../src/diagnostics/diagnostic.js";
|
||||
import { allOrderEntries, orderEntriesUnder } from "../../../../scripts/ts7-order-fixtures.mjs";
|
||||
|
||||
const repoRoot = join(import.meta.dirname, "../../../..");
|
||||
const baselinePath = join(import.meta.dirname, "baselines/order-parity.json");
|
||||
@@ -111,25 +112,13 @@ async function nativeAnswers(entries: string[]): Promise<BaselineEntry[]> {
|
||||
}
|
||||
|
||||
function entriesUnder(dir: string): string[] {
|
||||
const exts = ["ts", "js", "mjs", "cjs"];
|
||||
return exts
|
||||
.flatMap((ext) => [
|
||||
...globSync(join(repoRoot, dir, `*.${ext}`)),
|
||||
...globSync(join(repoRoot, dir, `*/main.${ext}`)),
|
||||
])
|
||||
.sort();
|
||||
return orderEntriesUnder(repoRoot, dir);
|
||||
}
|
||||
|
||||
const FULL = process.env["SCRIPTC_TS7_ALL"] === "1" || UPDATE;
|
||||
|
||||
function allEntries(): string[] {
|
||||
return [
|
||||
...entriesUnder("tests/corpus"),
|
||||
...entriesUnder("tests/diagnostics"),
|
||||
...entriesUnder("tests/fixtures/npm/cases"),
|
||||
...globSync(join(repoRoot, "tests/fixtures/strictness/*/main.ts")).sort(),
|
||||
...globSync(join(repoRoot, "tests/fixtures/node-types/*.ts")).sort(),
|
||||
];
|
||||
return allOrderEntries(repoRoot);
|
||||
}
|
||||
|
||||
function pickEntries(): string[] {
|
||||
|
||||
@@ -41,6 +41,7 @@ test.each([false, true])(
|
||||
"scr_cycle.c",
|
||||
"scr_lib.c",
|
||||
"scr_bytes.c",
|
||||
"scr_shared.c",
|
||||
"scr_bigint.c",
|
||||
"scr_url.c",
|
||||
"scr_url_params.c",
|
||||
|
||||
@@ -63,6 +63,7 @@ void scr_init(void);
|
||||
* Its versioned spelling makes a mismatched manual runtime link fail before
|
||||
* the program can start. */
|
||||
void scr_runtime_abi_v7(void);
|
||||
void scr_runtime_workers_v7(void);
|
||||
|
||||
/* ── the trap funnel (scr_console.c; scr_library.c under -DSCR_LIB) ──────
|
||||
* Every unrecoverable runtime trap — OOM, semantic range traps, internal-
|
||||
@@ -304,6 +305,10 @@ struct ScrArr *scr_context_env_pairs(void);
|
||||
|
||||
/* Also used by ordinary event-loop builds, where this always returns true. */
|
||||
bool scr_context_is_main(void);
|
||||
double scr_context_thread_number(void);
|
||||
double scr_worker_root(void);
|
||||
int scr_worker_argc(void);
|
||||
char **scr_worker_argv(void);
|
||||
|
||||
#ifdef SCR_WORKERS
|
||||
#define scr_getenv(name) scr_context_getenv(name)
|
||||
@@ -5914,6 +5919,9 @@ void scr_bytes_require_unshared(const ScrBytes *);
|
||||
#define SCR_BYTES_SNAPSHOT(name) ((void)0)
|
||||
#endif
|
||||
ScrDyn *scr_shared_array_buffer_new(ScrDyn *);
|
||||
ScrDyn *scr_worker_data(void);
|
||||
ScrDyn *scr_worker_parent_port(void);
|
||||
ScrDyn *scr_worker_new(double root, ScrStr *filename, ScrDyn *options);
|
||||
bool scr_shared_array_buffer_is(const ScrDyn *);
|
||||
bool scr_buffer_storage_is(const ScrDyn *);
|
||||
ScrBytes *scr_bytes_from_external(void *data, size_t length);
|
||||
|
||||
@@ -36,11 +36,6 @@ bool scr_worker_post_parent(ScrMessage *message);
|
||||
void scr_worker_port_close(void);
|
||||
/* Current-thread access: inbox is borrowed, data is a retained local value. */
|
||||
ScrMailbox *scr_worker_inbox(void);
|
||||
ScrDyn *scr_worker_data(void);
|
||||
ScrDyn *scr_worker_parent_port(void);
|
||||
ScrDyn *scr_worker_new(double root, ScrStr *filename, ScrDyn *options);
|
||||
int scr_worker_argc(void);
|
||||
char **scr_worker_argv(void);
|
||||
void scr_loop_set_workers(bool (*pending)(void), bool (*ready)(void), void (*dispatch)(void),
|
||||
int (*pollfd)(void), void (*wait)(double));
|
||||
|
||||
|
||||
@@ -33,6 +33,12 @@ beforeAll(async () => {
|
||||
join(testDir, "../src/scr_string.c"),
|
||||
join(testDir, "../src/scr_number.c"),
|
||||
join(testDir, "../src/scr_bytes.c"),
|
||||
join(testDir, "../src/scr_shared.c"),
|
||||
join(testDir, "../src/scr_lib.c"),
|
||||
join(testDir, "../src/scr_console.c"),
|
||||
join(testDir, "../src/scr_path.c"),
|
||||
join(testDir, "../src/scr_url.c"),
|
||||
join(testDir, "../src/scr_url_params.c"),
|
||||
join(testDir, "../src/scr_error.c"),
|
||||
join(testDir, "../src/scr_exception.c"),
|
||||
join(testDir, "../src/scr_object.c"),
|
||||
|
||||
@@ -32,6 +32,7 @@ beforeAll(async () => {
|
||||
join(testDir, "test_bytes.c"),
|
||||
join(testDir, "../src/scr_bytes.test.c"),
|
||||
join(testDir, "../src/scr_bytes.c"),
|
||||
join(testDir, "../src/scr_shared.c"),
|
||||
join(testDir, "../src/scr_bytes_io.c"),
|
||||
join(testDir, "../src/scr_zlib.c"),
|
||||
join(testDir, "../src/scr_lib.c"),
|
||||
|
||||
@@ -32,6 +32,12 @@ test("closure/box runtime: RC cascades clean under ASan + audit", async () => {
|
||||
join(srcDir, "scr_number.c"),
|
||||
join(srcDir, "scr_cycle.c"),
|
||||
join(srcDir, "scr_bytes.c"),
|
||||
join(srcDir, "scr_shared.c"),
|
||||
join(srcDir, "scr_lib.c"),
|
||||
join(srcDir, "scr_console.c"),
|
||||
join(srcDir, "scr_path.c"),
|
||||
join(srcDir, "scr_url.c"),
|
||||
join(srcDir, "scr_url_params.c"),
|
||||
join(srcDir, "scr_error.c"),
|
||||
join(srcDir, "scr_exception.c"),
|
||||
join(srcDir, "scr_object.c"),
|
||||
|
||||
@@ -36,6 +36,12 @@ beforeAll(async () => {
|
||||
join(testDir, "../src/scr_number.c"),
|
||||
join(testDir, "../src/scr_array.c"),
|
||||
join(testDir, "../src/scr_bytes.c"),
|
||||
join(testDir, "../src/scr_shared.c"),
|
||||
join(testDir, "../src/scr_lib.c"),
|
||||
join(testDir, "../src/scr_console.c"),
|
||||
join(testDir, "../src/scr_path.c"),
|
||||
join(testDir, "../src/scr_url.c"),
|
||||
join(testDir, "../src/scr_url_params.c"),
|
||||
join(testDir, "../src/scr_error.c"),
|
||||
join(testDir, "../src/scr_exception.c"),
|
||||
join(testDir, "../src/scr_object.c"),
|
||||
|
||||
@@ -43,6 +43,7 @@ beforeAll(async () => {
|
||||
join(testDir, "../src/scr_url_params.c"),
|
||||
join(testDir, "../src/scr_path.c"),
|
||||
join(testDir, "../src/scr_bytes.c"),
|
||||
join(testDir, "../src/scr_shared.c"),
|
||||
...(process.platform === "linux" ? ["-D_GNU_SOURCE", "-lm"] : []),
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -46,6 +46,7 @@ beforeAll(async () => {
|
||||
join(testDir, "../src/scr_json.c"),
|
||||
join(testDir, "../src/scr_bigint.c"),
|
||||
join(testDir, "../src/scr_bytes.c"),
|
||||
join(testDir, "../src/scr_shared.c"),
|
||||
...(process.platform === "linux" ? ["-lm"] : []),
|
||||
]);
|
||||
scratch = await mkdtemp(join(tmpdir(), "scriptc-lib-"));
|
||||
|
||||
@@ -38,6 +38,12 @@ beforeAll(async () => {
|
||||
join(testDir, "../src/scr_closure.c"),
|
||||
join(testDir, "../src/scr_object.c"),
|
||||
join(testDir, "../src/scr_bytes.c"),
|
||||
join(testDir, "../src/scr_shared.c"),
|
||||
join(testDir, "../src/scr_lib.c"),
|
||||
join(testDir, "../src/scr_console.c"),
|
||||
join(testDir, "../src/scr_path.c"),
|
||||
join(testDir, "../src/scr_url.c"),
|
||||
join(testDir, "../src/scr_url_params.c"),
|
||||
join(testDir, "../src/scr_error.c"),
|
||||
join(testDir, "../src/scr_exception.c"),
|
||||
...(process.platform === "linux" ? ["-D_GNU_SOURCE", "-lm"] : []),
|
||||
|
||||
@@ -36,6 +36,11 @@ beforeAll(async () => {
|
||||
join(testDir, "../src/scr_bigint.c"),
|
||||
join(testDir, "../src/scr_closure.c"),
|
||||
join(testDir, "../src/scr_bytes.c"),
|
||||
join(testDir, "../src/scr_shared.c"),
|
||||
join(testDir, "../src/scr_lib.c"),
|
||||
join(testDir, "../src/scr_console.c"),
|
||||
join(testDir, "../src/scr_url.c"),
|
||||
join(testDir, "../src/scr_url_params.c"),
|
||||
join(testDir, "../src/scr_error.c"),
|
||||
join(testDir, "../src/scr_exception.c"),
|
||||
join(testDir, "../src/scr_object.c"),
|
||||
|
||||
@@ -51,6 +51,7 @@ beforeAll(async () => {
|
||||
join(testDir, "../src/scr_url_params.c"),
|
||||
join(testDir, "../src/scr_path.c"),
|
||||
join(testDir, "../src/scr_bytes.c"),
|
||||
join(testDir, "../src/scr_shared.c"),
|
||||
...(process.platform === "linux" ? ["-D_GNU_SOURCE", "-lm"] : []),
|
||||
]);
|
||||
}, 60_000);
|
||||
|
||||
@@ -14,6 +14,7 @@ const RUNTIME_SOURCES = [
|
||||
"scr_string.c",
|
||||
"scr_array.c",
|
||||
"scr_bytes.c",
|
||||
"scr_shared.c",
|
||||
"scr_bytes_io.c",
|
||||
"scr_map.c",
|
||||
"scr_closure.c",
|
||||
|
||||
@@ -37,6 +37,12 @@ beforeAll(async () => {
|
||||
join(testDir, "../src/scr_bigint.c"),
|
||||
join(testDir, "../src/scr_closure.c"),
|
||||
join(testDir, "../src/scr_bytes.c"),
|
||||
join(testDir, "../src/scr_shared.c"),
|
||||
join(testDir, "../src/scr_lib.c"),
|
||||
join(testDir, "../src/scr_console.c"),
|
||||
join(testDir, "../src/scr_path.c"),
|
||||
join(testDir, "../src/scr_url.c"),
|
||||
join(testDir, "../src/scr_url_params.c"),
|
||||
join(testDir, "../src/scr_error.c"),
|
||||
join(testDir, "../src/scr_exception.c"),
|
||||
join(testDir, "../src/scr_object.c"),
|
||||
|
||||
@@ -44,6 +44,12 @@ test("scr_string_to_number matches Node Number(s) on committed oracle cases", as
|
||||
join(testDir, "../src/scr_bigint.c"),
|
||||
join(testDir, "../src/scr_closure.c"),
|
||||
join(testDir, "../src/scr_bytes.c"),
|
||||
join(testDir, "../src/scr_shared.c"),
|
||||
join(testDir, "../src/scr_lib.c"),
|
||||
join(testDir, "../src/scr_console.c"),
|
||||
join(testDir, "../src/scr_path.c"),
|
||||
join(testDir, "../src/scr_url.c"),
|
||||
join(testDir, "../src/scr_url_params.c"),
|
||||
join(testDir, "../src/scr_error.c"),
|
||||
join(testDir, "../src/scr_exception.c"),
|
||||
join(testDir, "../src/scr_object.c"),
|
||||
|
||||
@@ -36,6 +36,9 @@ beforeAll(async () => {
|
||||
join(testDir, "../src/scr_bigint.c"),
|
||||
join(testDir, "../src/scr_closure.c"),
|
||||
join(testDir, "../src/scr_bytes.c"),
|
||||
join(testDir, "../src/scr_shared.c"),
|
||||
join(testDir, "../src/scr_lib.c"),
|
||||
join(testDir, "../src/scr_console.c"),
|
||||
join(testDir, "../src/scr_error.c"),
|
||||
join(testDir, "../src/scr_exception.c"),
|
||||
join(testDir, "../src/scr_object.c"),
|
||||
|
||||
@@ -4,9 +4,28 @@ import { readdirSync, readFileSync } from "node:fs";
|
||||
import { dirname, relative, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { allOrderEntries } from "./ts7-order-fixtures.mjs";
|
||||
|
||||
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
|
||||
const baseline = JSON.parse(
|
||||
readFileSync(resolve(repoRoot, "packages/compiler/test/ts7/baselines/order-parity.json"), "utf8"),
|
||||
);
|
||||
const missingBaselines = allOrderEntries(repoRoot)
|
||||
.map((path) => relative(repoRoot, path).replaceAll("\\", "/"))
|
||||
.filter((path) => !Object.hasOwn(baseline.entries, `<repo>/${path}`));
|
||||
if (missingBaselines.length > 0) {
|
||||
console.error(`Missing TypeScript preflight/order baselines:\n${missingBaselines.join("\n")}`);
|
||||
console.error(
|
||||
"Record the new fixtures with SCRIPTC_UPDATE_BASELINES=1 pnpm exec vitest run packages/compiler/test/ts7/order-parity.test.ts, review the diff, then run the affected parity batches before opening a PR.",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
if (process.argv.includes("--baselines-only")) {
|
||||
console.log("Every TypeScript preflight/order fixture has a recorded baseline.");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const ALLOWED_TYPESCRIPT5_IMPORTS = new Set([
|
||||
"packages/compiler/src/frontend/comptime-node.ts",
|
||||
"packages/compiler/src/frontend/ts7/world-check.ts",
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { globSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
|
||||
export function orderEntriesUnder(repoRoot, dir) {
|
||||
return ["ts", "js", "mjs", "cjs"]
|
||||
.flatMap((ext) => [
|
||||
...globSync(join(repoRoot, dir, `*.${ext}`)),
|
||||
...globSync(join(repoRoot, dir, `*/main.${ext}`)),
|
||||
])
|
||||
.sort();
|
||||
}
|
||||
|
||||
export function allOrderEntries(repoRoot) {
|
||||
return [
|
||||
...orderEntriesUnder(repoRoot, "tests/corpus"),
|
||||
...orderEntriesUnder(repoRoot, "tests/diagnostics"),
|
||||
...orderEntriesUnder(repoRoot, "tests/fixtures/npm/cases"),
|
||||
...globSync(join(repoRoot, "tests/fixtures/strictness/*/main.ts")).sort(),
|
||||
...globSync(join(repoRoot, "tests/fixtures/node-types/*.ts")).sort(),
|
||||
];
|
||||
}
|
||||
@@ -4,9 +4,16 @@ if (isMainThread) {
|
||||
for (const mode of ["nested", "throw", "natural"]) {
|
||||
await new Promise<void>((resolve) => {
|
||||
const worker = new Worker(new URL(import.meta.url), {workerData: mode});
|
||||
worker.on("message", (value: unknown) => console.log(mode, "message", value));
|
||||
worker.on("error", (error: Error) => console.log(mode, "error", error.message));
|
||||
worker.on("exit", (code: number) => { console.log(mode, "exit", code); resolve(); });
|
||||
let message = -1;
|
||||
let messages = 0;
|
||||
let failure = "none";
|
||||
// Message and error use independent channels; both must arrive before exit.
|
||||
worker.on("message", (value: number) => { message = value; messages++; });
|
||||
worker.on("error", (error: Error) => { failure = error.message; });
|
||||
worker.on("exit", (code: number) => {
|
||||
console.log(mode, "exit", code, "messages", messages, message, "error", failure);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -14,19 +14,7 @@ import {
|
||||
} from "@scriptc/compiler";
|
||||
import { emitLlvmModule } from "../../packages/compiler/src/backend/llvm/emitter.js";
|
||||
import { type IrModule } from "../../packages/compiler/src/ir/ir.js";
|
||||
import {
|
||||
moduleUsesAssert,
|
||||
moduleUsesBigInt,
|
||||
moduleUsesCopying,
|
||||
moduleUsesDynInvoke,
|
||||
moduleUsesEmitter,
|
||||
moduleUsesInspect,
|
||||
moduleUsesLegacyTextDecoder,
|
||||
moduleUsesRegex,
|
||||
moduleUsesStream,
|
||||
moduleUsesSymbol,
|
||||
moduleUsesZlib,
|
||||
} from "../../packages/compiler/src/ir/runtime-features.js";
|
||||
import { executableLinkFeatures } from "../../packages/compiler/src/backend/executable-features.js";
|
||||
import {
|
||||
llvmEmitterCases,
|
||||
llvmEmitterOptions,
|
||||
@@ -51,22 +39,6 @@ function programStderr(stderr: Buffer): string {
|
||||
: text;
|
||||
}
|
||||
|
||||
function nativeFeatures(mod: IrModule) {
|
||||
return {
|
||||
regex: moduleUsesRegex(mod),
|
||||
copying: moduleUsesCopying(mod),
|
||||
inspect: moduleUsesInspect(mod),
|
||||
dynInvoke: moduleUsesDynInvoke(mod),
|
||||
symbol: moduleUsesSymbol(mod),
|
||||
bigint: moduleUsesBigInt(mod),
|
||||
zlib: moduleUsesZlib(mod),
|
||||
assert: moduleUsesAssert(mod),
|
||||
emitter: moduleUsesEmitter(mod),
|
||||
stream: moduleUsesStream(mod),
|
||||
textDecoderLegacy: moduleUsesLegacyTextDecoder(mod),
|
||||
};
|
||||
}
|
||||
|
||||
// Execute generated modules across expression dispatch, shared mutable
|
||||
// emitter state, exception paths, dynamic walkers and callback adapters.
|
||||
const programs = [
|
||||
@@ -253,7 +225,7 @@ for (const backend of ["llvm"] as const) {
|
||||
outPath,
|
||||
sanitize,
|
||||
optimization: "dev",
|
||||
...nativeFeatures(mod),
|
||||
...executableLinkFeatures(mod, false),
|
||||
});
|
||||
const node = spawnSync(process.execPath, [sourcePath], runOptions);
|
||||
const native = spawnSync(outPath, [], runOptions);
|
||||
@@ -278,7 +250,7 @@ for (const backend of ["llvm"] as const) {
|
||||
outPath: secondStage,
|
||||
sanitize,
|
||||
optimization: "dev",
|
||||
...nativeFeatures(ownIr),
|
||||
...executableLinkFeatures(ownIr, false),
|
||||
});
|
||||
const again = join(dir, "again.ll");
|
||||
const second = await execFileAsync(secondStage, [input, again, config], {
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { copyFileSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { dirname, join } from "node:path";
|
||||
import { expect, test } from "vitest";
|
||||
|
||||
test("baseline preflight reports every missing fixture before loading the compiler", () => {
|
||||
const root = mkdtempSync(join(tmpdir(), "scriptc-baseline-preflight-"));
|
||||
const put = (path: string, content: string) => {
|
||||
const fullPath = join(root, path);
|
||||
mkdirSync(dirname(fullPath), { recursive: true });
|
||||
writeFileSync(fullPath, content);
|
||||
};
|
||||
try {
|
||||
mkdirSync(join(root, "scripts"));
|
||||
for (const name of ["test-ts7.mjs", "ts7-order-fixtures.mjs"]) {
|
||||
copyFileSync(join(import.meta.dirname, "../../scripts", name), join(root, "scripts", name));
|
||||
}
|
||||
const fixtures = [
|
||||
"tests/corpus/plain.ts",
|
||||
"tests/corpus/common.cjs",
|
||||
"tests/corpus/modules/main.mjs",
|
||||
"tests/diagnostics/refused.ts",
|
||||
"tests/fixtures/npm/cases/example/main.js",
|
||||
"tests/fixtures/strictness/strict/main.ts",
|
||||
"tests/fixtures/node-types/api.ts",
|
||||
];
|
||||
for (const path of [...fixtures, "tests/corpus/modules/helper.ts"]) put(path, "");
|
||||
const baselinePath = "packages/compiler/test/ts7/baselines/order-parity.json";
|
||||
put(baselinePath, JSON.stringify({ entries: {} }));
|
||||
const run = () =>
|
||||
spawnSync(process.execPath, [join(root, "scripts/test-ts7.mjs"), "--baselines-only"], {
|
||||
encoding: "utf8",
|
||||
});
|
||||
const missing = run();
|
||||
expect(missing.status).toBe(1);
|
||||
for (const path of fixtures) expect(missing.stderr).toContain(path);
|
||||
expect(missing.stderr).not.toContain("helper.ts");
|
||||
expect(missing.stderr).toContain("SCRIPTC_UPDATE_BASELINES=1");
|
||||
|
||||
put(
|
||||
baselinePath,
|
||||
JSON.stringify({
|
||||
entries: Object.fromEntries(
|
||||
fixtures.map((path) => [`<repo>/${path}`, { order: [], diags: [] }]),
|
||||
),
|
||||
}),
|
||||
);
|
||||
const complete = run();
|
||||
expect(complete.status, complete.stderr).toBe(0);
|
||||
expect(complete.stdout).toContain("Every TypeScript preflight/order fixture");
|
||||
} finally {
|
||||
rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user