mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-02 07:43:42 +08:00
macOS binds libnode's own calls to v8::base::Stack inside libnode, so the bridge's definitions cannot give V8 Node's stack as they do on Linux; V8 took the main thread's stack start and its conservative scan faulted in the gap above Node's stack. While Node starts on its stack, the main thread's pthread record now names that stack, which V8 reads once and keeps. macOS's makecontext clears the whole stack it is given, so it gets only the top slice of Node's stack (no 64 MiB pass per context). CI: dev-pack.json pins mirrored darwin-arm64 and darwin-x64 libnode builds of the pinned Node 24.22.0 from the libnode-packs workflow, the dev-pack action serves either platform, and the macOS lanes provision it, so island tests fail rather than skip on macOS CI. The arm64 LLVM job runs the island harness; the Intel job runs it and the island corpus. bridge-smoke loads the .dylib bridge on macOS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
60 lines
2.2 KiB
TypeScript
60 lines
2.2 KiB
TypeScript
/* The libnode bridge against a real pack, without the compiler: the C host
|
|
* in bridge-smoke.c loads the pack the way scr_node_island.c does and makes
|
|
* calls through the bridge with the runtime's embedded bootstrap. Needs a
|
|
* pack in SCRIPTC_LIBNODE; required in the Linux x64 and macOS CI lanes
|
|
* (tests/harness/libnode.ts). */
|
|
import { execFile } from "node:child_process";
|
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import { promisify } from "node:util";
|
|
import { expect, test } from "vitest";
|
|
import { libnodePack } from "../../../tests/harness/libnode.js";
|
|
|
|
const execFileAsync = promisify(execFile);
|
|
const here = import.meta.dirname;
|
|
const outDir = join(here, "../../../node_modules/.cache/scriptc-tests/libnode-bridge");
|
|
|
|
test("a libnode pack runs calls through the bridge", async ({ skip }) => {
|
|
const pack = libnodePack();
|
|
if (pack === null) return skip();
|
|
mkdirSync(outDir, { recursive: true });
|
|
const module = join(outDir, "smoke.cjs");
|
|
writeFileSync(
|
|
module,
|
|
`"use strict";
|
|
exports.add = (a, b) => a + b;
|
|
exports.fails = () => { const e = new RangeError("out of range"); e.code = "ERR_X"; throw e; };
|
|
exports.later = (ms) => new Promise((resolve) => setTimeout(() => resolve({ waited: ms }), ms));
|
|
exports.quit = (code) => process.exit(code);
|
|
`,
|
|
);
|
|
const binary = join(outDir, "bridge-smoke");
|
|
await execFileAsync("cc", [
|
|
"-O1",
|
|
"-I",
|
|
join(here, "../../runtime/src"),
|
|
join(here, "bridge-smoke.c"),
|
|
"-ldl",
|
|
"-o",
|
|
binary,
|
|
]);
|
|
const result = await execFileAsync(binary, [pack, module], { encoding: "utf8" }).then(
|
|
({ stdout, stderr }) => ({ stdout, stderr, code: 0 }),
|
|
(err: { stdout: string; stderr: string; code: number }) => err,
|
|
);
|
|
expect(result.stderr).toBe("");
|
|
expect(result.stdout).toBe(
|
|
[
|
|
"abi 3 ok 1",
|
|
"sync add -> R5",
|
|
'sync fails -> T{"error":{"kind":2,"message":"out of range","code":"ERR_X"}}',
|
|
`sync missing -> T{"error":{"kind":1,"message":"'missing' exported by './smoke.cjs' is not a function"}}`,
|
|
"async later -> pending",
|
|
'settle 7 R{"waited":5}',
|
|
"exit 3",
|
|
"",
|
|
].join("\n"),
|
|
);
|
|
expect(result.code).toBe(3);
|
|
});
|