mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-28 06:53:42 +08:00
The backend converted size_t lengths and indexes to numbers with sitofp. On wasm32 size_t is 32 bits, and an array length reaches 2^32 - 1, so a sparse array of length 4294967295 read -1. sizeToDouble picks uitofp for a 32-bit size_t and keeps sitofp for 64 bits, whose sign bit a length never reaches, so native output is unchanged. An unsettled top-level await always exited 13. Node exits with `process.exitCode || 13` and runs the exit listeners first, which can set process.exitCode again. scr_promise_finish_top_level now notes 13 only when the program set no nonzero exit code, and the pending exit returns the exit code after the listeners, as the other exits do. Three WASI tests still exercised the embedded-JS fallback that native-only compilation removed: an embedded-source fetch scan and two npm fixtures that every target now refuses. They now check the same behavior with static programs: fetch named only in text compiles, a global fetch call reports SC3002, and CommonJS and ESM packages compile and match Node.
36 lines
1.5 KiB
TypeScript
36 lines
1.5 KiB
TypeScript
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { expect, test } from "vitest";
|
|
import { compile, deserializeModule } from "../src/index.js";
|
|
import { emitLlvmModule } from "../src/backend/llvm/emitter.js";
|
|
import type { IrModule } from "../src/ir/ir.js";
|
|
|
|
async function lowerSource(source: string): Promise<IrModule> {
|
|
const dir = await mkdtemp(join(tmpdir(), "scriptc-size-conversions-"));
|
|
try {
|
|
const entry = join(dir, "main.ts");
|
|
await writeFile(entry, source);
|
|
const outPath = join(dir, "main.ir.json");
|
|
const result = await compile(entry, { outPath, outDir: dir, outputKind: "ir" });
|
|
if (!result.ok) throw new Error(result.diagnostics.map((d) => d.message).join("\n"));
|
|
return deserializeModule(await readFile(outPath, "utf8"));
|
|
} finally {
|
|
await rm(dir, { recursive: true, force: true });
|
|
}
|
|
}
|
|
|
|
test("array lengths past 2^31 convert unsigned on 32-bit targets", async () => {
|
|
// A sparse write at index 2^32 - 2 leaves the length at 2^32 - 1, past a
|
|
// 32-bit size_t's sign bit.
|
|
const mod = await lowerSource(
|
|
"const numbers = [1];\nnumbers[4294967294] = 2;\nconsole.log(numbers.length);\n",
|
|
);
|
|
const wasm = emitLlvmModule(mod, { wasi: true, pointerBits: 32 });
|
|
expect(wasm).toMatch(/uitofp i32 %\w+ to double/);
|
|
expect(wasm).not.toMatch(/sitofp i32 %\w+ to double/);
|
|
const native = emitLlvmModule(mod, { pointerBits: 64 });
|
|
expect(native).toMatch(/sitofp i64 %\w+ to double/);
|
|
expect(native).not.toMatch(/uitofp i64/);
|
|
});
|