import { spawnSync } from "bun"; import { cc, dlopen } from "bun:ffi"; import { beforeAll, describe, expect, it } from "bun:test"; import { bunEnv, bunExe, isWindows } from "harness"; import { join } from "path"; import source from "./napi-app/ffi_addon_1.c" with { type: "file" }; const symbols = { set_instance_data: { args: ["napi_env", "int"], returns: "void", }, get_instance_data: { args: ["napi_env"], returns: "int", }, get_type: { args: ["napi_env", "napi_value"], returns: "cstring", }, }; let addon1, addon2, cc1, cc2; beforeAll(() => { // build gyp const install = spawnSync({ cmd: [bunExe(), "install", "--verbose"], cwd: join(__dirname, "napi-app"), stderr: "inherit", env: bunEnv, stdout: "inherit", stdin: "inherit", }); if (!install.success) { throw new Error("build failed"); } addon1 = dlopen(join(__dirname, `napi-app/build/Debug/ffi_addon_1.node`), symbols).symbols; addon2 = dlopen(join(__dirname, `napi-app/build/Debug/ffi_addon_2.node`), symbols).symbols; try { cc1 = cc({ source, symbols, flags: `-I${join(__dirname, "napi-app/node_modules/node-api-headers/include")}`, }).symbols; cc2 = cc({ source, symbols, flags: `-I${join(__dirname, "napi-app/node_modules/node-api-headers/include")}`, }).symbols; } catch (e) { // ignore compilation failure on Windows if (!isWindows) throw e; } }); describe("ffi napi integration", () => { it("has a different napi_env for each ffi library", () => { addon1.set_instance_data(undefined, 5); addon2.set_instance_data(undefined, 6); expect(addon1.get_instance_data()).toBe(5); expect(addon2.get_instance_data()).toBe(6); }); // broken it.todo("passes values correctly", () => { expect(addon1.get_type(undefined, 123).toString()).toBe("number"); expect(addon1.get_type(undefined, "hello").toString()).toBe("string"); expect(addon1.get_type(undefined, 190n).toString()).toBe("bigint"); }); }); describe("cc napi integration", () => { // fails on windows as TCC can't link the napi_ functions it.todoIf(isWindows)("has a different napi_env for each cc invocation", () => { cc1.set_instance_data(undefined, 5); cc2.set_instance_data(undefined, 6); expect(cc1.get_instance_data()).toBe(5); expect(cc2.get_instance_data()).toBe(6); }); // broken it.todo("passes values correctly", () => { expect(cc1.get_type(undefined, 123).toString()).toBe("number"); expect(cc1.get_type(undefined, "hello").toString()).toBe("string"); expect(cc1.get_type(undefined, 190n).toString()).toBe("bigint"); }); });