Files
scriptc/packages/runtime/src/scr_message.test.ts
T
Malte UblandClaude Opus 5.5 4b78531ab4 Size handle tables by a fixed capacity and let handles cross threads
Appending a ScrDynHandleTag used to grow the always-linked scr_dynh_ops
table, which shifted .bss and code addresses in every program, including
programs that never box the new handle (Blob and File paid this when
they were added). The table is now sized by SCR_DYNH_CAPACITY (128; 52
tags are in use), so a link-gated unit can add a tag without changing
any other program's executable. This change is the one layout shift that
makes later tags free.

Structured clone between threads (postMessage, workerData) rejected
every handle other than MessagePorts and buffers. A handle's ops can now
carry a ScrDynHandleTransfer (share on the sender, adopt on the
receiver, drop with the message), which scr_message.c uses for any tag,
so a gated unit such as a future WebAssembly.Module needs no reference
from the message encoder and no per-tag registry. No handle declares the
hooks yet; scr_message.test.c exercises them across threads through a
test handle in the last capacity slot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 15:19:38 -07:00

46 lines
1.6 KiB
TypeScript

import { execFile } from "node:child_process";
import { mkdtemp, rm } 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 { EXECUTABLE_RUNTIME_SOURCES } from "../../compiler/src/backend/native-toolchain.js";
const exec = promisify(execFile);
test.skipIf(process.platform === "win32")(
"message data outlives its sender and decodes into a separate runtime heap",
async () => {
const dir = await mkdtemp(join(tmpdir(), "scriptc-message-"));
try {
const bin = join(dir, "messages");
await exec("clang", [
"-std=c11",
"-O1",
"-pthread",
"-DSCR_WORKERS",
"-DSCR_RC_AUDIT",
"-fsanitize=address,undefined",
"-Wno-deprecated-declarations",
...(process.platform === "linux" ? ["-D_GNU_SOURCE"] : []),
join(import.meta.dirname, "scr_message.test.c"),
join(import.meta.dirname, "scr_message.c"),
...EXECUTABLE_RUNTIME_SOURCES.map((name) => join(import.meta.dirname, name)),
...(process.platform === "linux" ? ["-lm"] : []),
"-o",
bin,
]);
const result = await exec(bin, [], {
env: { ...process.env, UBSAN_OPTIONS: "halt_on_error=1" },
});
expect(result.stdout).toBe(
"message graphs preserve identity, sparse values, byte aliases and getter order\n" +
"native handles cross threads through their transfer hooks\n",
);
expect(result.stderr).toBe("");
} finally {
await rm(dir, { recursive: true, force: true });
}
},
);