76 lines
3.0 KiB
TypeScript
76 lines
3.0 KiB
TypeScript
import { describe, expect, it } from "bun:test";
|
|||
|
|
import { realpathSync } from "fs";
|
||
|
|
import { bunEnv, bunExe, tempDir } from "harness";
|
||
|
|
import { join } from "path";
|
||
|
|
|
||
|
|
describe("issue/03830", () => {
|
||
|
|
// First test stays sequential: `toMatchSnapshot()` is not supported in
|
||
|
|
// concurrent tests (Bun's test runner rejects it). The second test has no
|
||
|
|
// snapshot matcher, so it runs `.concurrent` per test/CLAUDE.md.
|
||
|
|
it("macros should not lead to seg faults under any given input", async () => {
|
||
|
|
// this test code follows the same structure as and
|
||
|
|
// is based on the code for testing issue 4893
|
||
|
|
|
||
|
|
using dir = tempDir("issue-03830", {
|
||
|
|
"macro.ts": "export function fn(str) { return str; }",
|
||
|
|
"index.ts": "import { fn } from './macro' assert { type: 'macro' };\nfn(`©${Number(0)}`);",
|
||
|
|
});
|
||
|
|
const testDir = realpathSync(dir);
|
||
|
|
|
||
|
|
await using proc = Bun.spawn({
|
||
|
|
cmd: [bunExe(), "build", "--minify", join(testDir, "index.ts")],
|
||
|
|
env: bunEnv,
|
||
|
|
stderr: "pipe",
|
||
|
|
stdout: "pipe",
|
||
|
|
});
|
||
|
|
|
||
|
|
const [stderr, exitCode] = await Promise.all([proc.stderr.text(), proc.exited]);
|
||
|
|
|
||
|
|
expect(stderr.trim().replaceAll(testDir, "[dir]").replaceAll("\\", "/")).toMatchSnapshot();
|
||
|
|
expect(exitCode).toBe(1);
|
||
|
|
});
|
||
|
|
|
||
|
|
it.concurrent.each([
|
||
|
|
// [name, where transformSync+import() runs in macro.ts]
|
||
|
|
["inside the exported macro fn", "fn-body"],
|
||
|
|
// Macro::init runs the module's top-level via wait_for_promise BEFORE the
|
||
|
|
// caller's MacroModeGuard exists — Macro::init holds its own guard so
|
||
|
|
// depth > 0 here too.
|
||
|
|
["at macro-module top level (during Macro::init)", "top-level"],
|
||
|
|
])("nested Bun.Transpiler %s then import() does not free the printer", async (_, where) => {
|
||
|
|
// Bun.Transpiler#transformSync drops a nested MacroContext via
|
||
|
|
// TranspilerStateGuard, which calls __bun_macro_context_deinit. On a
|
||
|
|
// bundler-worker thread the macro was the first SOURCE_CODE_PRINTER
|
||
|
|
// allocator; freeing it here would panic the subsequent module fetch.
|
||
|
|
const macroTs =
|
||
|
|
where === "top-level"
|
||
|
|
? `new Bun.Transpiler().transformSync("const a = 1;");
|
||
|
|
const mod = await import("./helper.ts");
|
||
|
|
export function nested() { return mod.value; }`
|
||
|
|
: `export async function nested() {
|
||
|
|
new Bun.Transpiler().transformSync("const a = 1;");
|
||
|
|
const mod = await import("./helper.ts");
|
||
|
|
return mod.value;
|
||
|
|
}`;
|
||
|
|
using dir = tempDir("issue-03830-nested", {
|
||
|
|
"helper.ts": "export const value = 42;",
|
||
|
|
"macro.ts": macroTs,
|
||
|
|
"index.ts": "import { nested } from './macro.ts' with { type: 'macro' };\nconsole.log(nested());",
|
||
|
|
});
|
||
|
|
const testDir = realpathSync(dir);
|
||
|
|
|
||
|
|
await using proc = Bun.spawn({
|
||
|
|
cmd: [bunExe(), "build", "--target=bun", join(testDir, "index.ts")],
|
||
|
|
env: bunEnv,
|
||
|
|
stderr: "pipe",
|
||
|
|
stdout: "pipe",
|
||
|
|
});
|
||
|
|
|
||
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
||
|
|
|
||
|
|
expect(stderr.trim()).toBe("");
|
||
|
|
expect(stdout).toContain("console.log(42)");
|
||
|
|
expect(exitCode).toBe(0);
|
||
|
|
});
|
||
|
|
});
|