150 lines
4.8 KiB
TypeScript
150 lines
4.8 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import { bunEnv, bunExe, isWindows, tempDir } from "harness";
|
|
import { join } from "path";
|
|
|
|
// Regression test for https://github.com/oven-sh/bun/issues/26298
|
|
// Windows segfault when running standalone executables with bytecode cache.
|
|
// The crash occurred because bytecode offsets were not properly aligned
|
|
// when embedded in PE sections, causing deserialization failures.
|
|
|
|
describe.concurrent("issue #26298: bytecode cache in standalone executables", () => {
|
|
const ext = isWindows ? ".exe" : "";
|
|
|
|
test("standalone executable with --bytecode runs correctly", async () => {
|
|
using dir = tempDir("bytecode-standalone", {
|
|
"index.js": `
|
|
const add = (a, b) => a + b;
|
|
const multiply = (x, y) => x * y;
|
|
console.log("sum:", add(2, 3));
|
|
console.log("product:", multiply(4, 5));
|
|
`,
|
|
});
|
|
|
|
const outfile = join(String(dir), `app${ext}`);
|
|
|
|
// Build with bytecode
|
|
await using build = Bun.spawn({
|
|
cmd: [bunExe(), "build", "--compile", "--bytecode", join(String(dir), "index.js"), "--outfile", outfile],
|
|
env: bunEnv,
|
|
cwd: String(dir),
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
const [, buildStderr, buildExitCode] = await Promise.all([build.stdout.text(), build.stderr.text(), build.exited]);
|
|
|
|
expect(buildStderr).toBe("");
|
|
expect(buildExitCode).toBe(0);
|
|
|
|
// Run the compiled executable
|
|
await using exe = Bun.spawn({
|
|
cmd: [outfile],
|
|
env: bunEnv,
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
const [exeStdout, , exeExitCode] = await Promise.all([exe.stdout.text(), exe.stderr.text(), exe.exited]);
|
|
|
|
expect(exeStdout).toContain("sum: 5");
|
|
expect(exeStdout).toContain("product: 20");
|
|
// Should not crash with segfault
|
|
expect(exeExitCode).toBe(0);
|
|
});
|
|
|
|
test("standalone executable with --bytecode and multiple modules", async () => {
|
|
using dir = tempDir("bytecode-multi-module", {
|
|
"index.js": `
|
|
import { greet } from "./greet.js";
|
|
import { calculate } from "./math.js";
|
|
console.log(greet("World"));
|
|
console.log("result:", calculate(10, 5));
|
|
`,
|
|
"greet.js": `
|
|
export function greet(name) {
|
|
return "Hello, " + name + "!";
|
|
}
|
|
`,
|
|
"math.js": `
|
|
export function calculate(a, b) {
|
|
return a * b + (a - b);
|
|
}
|
|
`,
|
|
});
|
|
|
|
const outfile = join(String(dir), `multi${ext}`);
|
|
|
|
// Build with bytecode
|
|
await using build = Bun.spawn({
|
|
cmd: [bunExe(), "build", "--compile", "--bytecode", join(String(dir), "index.js"), "--outfile", outfile],
|
|
env: bunEnv,
|
|
cwd: String(dir),
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
const [, buildStderr, buildExitCode] = await Promise.all([build.stdout.text(), build.stderr.text(), build.exited]);
|
|
|
|
expect(buildStderr).toBe("");
|
|
expect(buildExitCode).toBe(0);
|
|
|
|
// Run the compiled executable
|
|
await using exe = Bun.spawn({
|
|
cmd: [outfile],
|
|
env: bunEnv,
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
const [exeStdout, , exeExitCode] = await Promise.all([exe.stdout.text(), exe.stderr.text(), exe.exited]);
|
|
|
|
expect(exeStdout).toContain("Hello, World!");
|
|
expect(exeStdout).toContain("result: 55");
|
|
// Should not crash with segfault
|
|
expect(exeExitCode).toBe(0);
|
|
});
|
|
|
|
test("standalone executable with --bytecode uses bytecode cache", async () => {
|
|
using dir = tempDir("bytecode-cache-hit", {
|
|
"app.js": `console.log("bytecode cache test");`,
|
|
});
|
|
|
|
const outfile = join(String(dir), `cached${ext}`);
|
|
|
|
// Build with bytecode
|
|
await using build = Bun.spawn({
|
|
cmd: [bunExe(), "build", "--compile", "--bytecode", join(String(dir), "app.js"), "--outfile", outfile],
|
|
env: bunEnv,
|
|
cwd: String(dir),
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
const [, buildStderr, buildExitCode] = await Promise.all([build.stdout.text(), build.stderr.text(), build.exited]);
|
|
|
|
expect(buildStderr).toBe("");
|
|
expect(buildExitCode).toBe(0);
|
|
|
|
// Run with verbose disk cache to verify bytecode is being used
|
|
await using exe = Bun.spawn({
|
|
cmd: [outfile],
|
|
env: {
|
|
...bunEnv,
|
|
BUN_JSC_verboseDiskCache: "1",
|
|
},
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
const [exeStdout, exeStderr, exeExitCode] = await Promise.all([exe.stdout.text(), exe.stderr.text(), exe.exited]);
|
|
|
|
expect(exeStdout).toContain("bytecode cache test");
|
|
// Check for cache hit message which confirms bytecode is being loaded.
|
|
// This relies on JSC's internal disk cache diagnostic output when
|
|
// BUN_JSC_verboseDiskCache=1 is set. The pattern is kept flexible to
|
|
// accommodate potential future changes in JSC's diagnostic format.
|
|
expect(exeStderr).toMatch(/\[Disk Cache\].*Cache hit/i);
|
|
expect(exeExitCode).toBe(0);
|
|
});
|
|
});
|