aboutsummaryrefslogtreecommitdiff
path: root/test/bun.js/ffi.test.js
diff options
context:
space:
mode:
Diffstat (limited to 'test/bun.js/ffi.test.js')
-rw-r--r--test/bun.js/ffi.test.js613
1 files changed, 0 insertions, 613 deletions
diff --git a/test/bun.js/ffi.test.js b/test/bun.js/ffi.test.js
deleted file mode 100644
index 102737782..000000000
--- a/test/bun.js/ffi.test.js
+++ /dev/null
@@ -1,613 +0,0 @@
-import { afterAll, describe, expect, it } from "bun:test";
-//
-import {
- CFunction,
- CString,
- dlopen as _dlopen,
- JSCallback,
- ptr,
- read,
- toArrayBuffer,
- toBuffer,
- viewSource,
-} from "bun:ffi";
-
-const dlopen = (...args) => {
- try {
- return _dlopen(...args);
- } catch (err) {
- console.error("To enable this test, run `make compile-ffi-test`.");
- throw err;
- }
-};
-
-it("ffi print", async () => {
- await Bun.write(
- import.meta.dir + "/ffi.test.fixture.callback.c",
- viewSource(
- {
- returns: "bool",
- args: ["ptr"],
- },
- true,
- ),
- );
- await Bun.write(
- import.meta.dir + "/ffi.test.fixture.receiver.c",
- viewSource(
- {
- not_a_callback: {
- returns: "float",
- args: ["float"],
- },
- },
- false,
- )[0],
- );
- expect(
- viewSource(
- {
- returns: "int8_t",
- args: [],
- },
- true,
- ).length > 0,
- ).toBe(true);
- expect(
- viewSource(
- {
- a: {
- returns: "int8_t",
- args: [],
- },
- },
- false,
- ).length > 0,
- ).toBe(true);
-});
-
-function getTypes(fast) {
- const int64_t = fast ? "i64_fast" : "int64_t";
- const uint64_t = fast ? "u64_fast" : "uint64_t";
- return {
- returns_true: {
- returns: "bool",
- args: [],
- },
- returns_false: {
- returns: "bool",
- args: [],
- },
- returns_42_char: {
- returns: "char",
- args: [],
- },
- returns_42_float: {
- returns: "float",
- args: [],
- },
- returns_42_double: {
- returns: "double",
- args: [],
- },
- returns_42_uint8_t: {
- returns: "uint8_t",
- args: [],
- },
- returns_neg_42_int8_t: {
- returns: "int8_t",
- args: [],
- },
- returns_42_uint16_t: {
- returns: "uint16_t",
- args: [],
- },
- returns_42_uint32_t: {
- returns: "uint32_t",
- args: [],
- },
- returns_42_uint64_t: {
- returns: uint64_t,
- args: [],
- },
- returns_neg_42_int16_t: {
- returns: "int16_t",
- args: [],
- },
- returns_neg_42_int32_t: {
- returns: "int32_t",
- args: [],
- },
- returns_neg_42_int64_t: {
- returns: int64_t,
- args: [],
- },
-
- identity_char: {
- returns: "char",
- args: ["char"],
- },
- identity_float: {
- returns: "float",
- args: ["float"],
- },
- identity_bool: {
- returns: "bool",
- args: ["bool"],
- },
- identity_double: {
- returns: "double",
- args: ["double"],
- },
- identity_int8_t: {
- returns: "int8_t",
- args: ["int8_t"],
- },
- identity_int16_t: {
- returns: "int16_t",
- args: ["int16_t"],
- },
- identity_int32_t: {
- returns: "int32_t",
- args: ["int32_t"],
- },
- identity_int64_t: {
- returns: int64_t,
- args: [int64_t],
- },
- identity_uint8_t: {
- returns: "uint8_t",
- args: ["uint8_t"],
- },
- identity_uint16_t: {
- returns: "uint16_t",
- args: ["uint16_t"],
- },
- identity_uint32_t: {
- returns: "uint32_t",
- args: ["uint32_t"],
- },
- identity_uint64_t: {
- returns: uint64_t,
- args: [uint64_t],
- },
-
- add_char: {
- returns: "char",
- args: ["char", "char"],
- },
- add_float: {
- returns: "float",
- args: ["float", "float"],
- },
- add_double: {
- returns: "double",
- args: ["double", "double"],
- },
- add_int8_t: {
- returns: "int8_t",
- args: ["int8_t", "int8_t"],
- },
- add_int16_t: {
- returns: "int16_t",
- args: ["int16_t", "int16_t"],
- },
- add_int32_t: {
- returns: "int32_t",
- args: ["int32_t", "int32_t"],
- },
- add_int64_t: {
- returns: int64_t,
- args: [int64_t, int64_t],
- },
- add_uint8_t: {
- returns: "uint8_t",
- args: ["uint8_t", "uint8_t"],
- },
- add_uint16_t: {
- returns: "uint16_t",
- args: ["uint16_t", "uint16_t"],
- },
- add_uint32_t: {
- returns: "uint32_t",
- args: ["uint32_t", "uint32_t"],
- },
-
- is_null: {
- returns: "bool",
- args: ["ptr"],
- },
-
- does_pointer_equal_42_as_int32_t: {
- returns: "bool",
- args: ["ptr"],
- },
-
- ptr_should_point_to_42_as_int32_t: {
- returns: "ptr",
- args: [],
- },
- identity_ptr: {
- returns: "ptr",
- args: ["ptr"],
- },
- add_uint64_t: {
- returns: uint64_t,
- args: [uint64_t, uint64_t],
- },
-
- cb_identity_true: {
- returns: "bool",
- args: ["ptr"],
- },
- cb_identity_false: {
- returns: "bool",
- args: ["ptr"],
- },
- cb_identity_42_char: {
- returns: "char",
- args: ["ptr"],
- },
- cb_identity_42_float: {
- returns: "float",
- args: ["ptr"],
- },
- cb_identity_42_double: {
- returns: "double",
- args: ["ptr"],
- },
- cb_identity_42_uint8_t: {
- returns: "uint8_t",
- args: ["ptr"],
- },
- cb_identity_neg_42_int8_t: {
- returns: "int8_t",
- args: ["ptr"],
- },
- cb_identity_42_uint16_t: {
- returns: "uint16_t",
- args: ["ptr"],
- },
- cb_identity_42_uint32_t: {
- returns: "uint32_t",
- args: ["ptr"],
- },
- cb_identity_42_uint64_t: {
- returns: uint64_t,
- args: ["ptr"],
- },
- cb_identity_neg_42_int16_t: {
- returns: "int16_t",
- args: ["ptr"],
- },
- cb_identity_neg_42_int32_t: {
- returns: "int32_t",
- args: ["ptr"],
- },
- cb_identity_neg_42_int64_t: {
- returns: int64_t,
- args: ["ptr"],
- },
-
- return_a_function_ptr_to_function_that_returns_true: {
- returns: "ptr",
- args: [],
- },
-
- getDeallocatorCalledCount: {
- returns: "int32_t",
- args: [],
- },
- getDeallocatorCallback: {
- returns: "ptr",
- args: [],
- },
- getDeallocatorBuffer: {
- returns: "ptr",
- args: [],
- },
- };
-}
-
-function ffiRunner(fast) {
- describe("FFI runner" + (fast ? " (fast int)" : ""), () => {
- const types = getTypes(fast);
- const {
- symbols: {
- returns_true,
- returns_false,
- return_a_function_ptr_to_function_that_returns_true,
- returns_42_char,
- returns_42_float,
- returns_42_double,
- returns_42_uint8_t,
- returns_neg_42_int8_t,
- returns_42_uint16_t,
- returns_42_uint32_t,
- returns_42_uint64_t,
- returns_neg_42_int16_t,
- returns_neg_42_int32_t,
- returns_neg_42_int64_t,
- identity_char,
- identity_float,
- identity_bool,
- identity_double,
- identity_int8_t,
- identity_int16_t,
- identity_int32_t,
- identity_int64_t,
- identity_uint8_t,
- identity_uint16_t,
- identity_uint32_t,
- identity_uint64_t,
- add_char,
- add_float,
- add_double,
- add_int8_t,
- add_int16_t,
- add_int32_t,
- add_int64_t,
- add_uint8_t,
- add_uint16_t,
- identity_ptr,
- add_uint32_t,
- add_uint64_t,
- is_null,
- does_pointer_equal_42_as_int32_t,
- ptr_should_point_to_42_as_int32_t,
- cb_identity_true,
- cb_identity_false,
- cb_identity_42_char,
- cb_identity_42_float,
- cb_identity_42_double,
- cb_identity_42_uint8_t,
- cb_identity_neg_42_int8_t,
- cb_identity_42_uint16_t,
- cb_identity_42_uint32_t,
- cb_identity_42_uint64_t,
- cb_identity_neg_42_int16_t,
- cb_identity_neg_42_int32_t,
- cb_identity_neg_42_int64_t,
- getDeallocatorCalledCount,
- getDeallocatorCallback,
- getDeallocatorBuffer,
- },
- close,
- } = dlopen("/tmp/bun-ffi-test.dylib", types);
- it("primitives", () => {
- Bun.gc(true);
- expect(returns_true()).toBe(true);
- Bun.gc(true);
- expect(returns_false()).toBe(false);
-
- expect(returns_42_char()).toBe(42);
- if (fast) expect(returns_42_uint64_t().valueOf()).toBe(42);
- else expect(returns_42_uint64_t().valueOf()).toBe(42n);
- Bun.gc(true);
- expect(Math.fround(returns_42_float())).toBe(Math.fround(42.41999804973602));
- expect(returns_42_double()).toBe(42.42);
- expect(returns_42_uint8_t()).toBe(42);
- expect(returns_neg_42_int8_t()).toBe(-42);
- expect(returns_42_uint16_t()).toBe(42);
- expect(returns_42_uint32_t()).toBe(42);
- if (fast) expect(returns_42_uint64_t()).toBe(42);
- else expect(returns_42_uint64_t()).toBe(42n);
- expect(returns_neg_42_int16_t()).toBe(-42);
- expect(returns_neg_42_int32_t()).toBe(-42);
- expect(identity_int32_t(10)).toBe(10);
- Bun.gc(true);
- if (fast) expect(returns_neg_42_int64_t()).toBe(-42);
- else expect(returns_neg_42_int64_t()).toBe(-42n);
-
- expect(identity_char(10)).toBe(10);
-
- expect(identity_float(10.199999809265137)).toBe(10.199999809265137);
-
- expect(identity_bool(true)).toBe(true);
-
- expect(identity_bool(false)).toBe(false);
- expect(identity_double(10.100000000000364)).toBe(10.100000000000364);
-
- expect(identity_int8_t(10)).toBe(10);
- expect(identity_int16_t(10)).toBe(10);
-
- if (fast) expect(identity_int64_t(10)).toBe(10);
- else expect(identity_int64_t(10)).toBe(10n);
- expect(identity_uint8_t(10)).toBe(10);
- expect(identity_uint16_t(10)).toBe(10);
- expect(identity_uint32_t(10)).toBe(10);
- if (fast) expect(identity_uint64_t(10)).toBe(10);
- else expect(identity_uint64_t(10)).toBe(10n);
- Bun.gc(true);
- var bigArray = new BigUint64Array(8);
- new Uint8Array(bigArray.buffer).fill(255);
- var bigIntArray = new BigInt64Array(bigArray.buffer);
- expect(identity_uint64_t(bigArray[0])).toBe(bigArray[0]);
- expect(identity_uint64_t(bigArray[0] - BigInt(1))).toBe(bigArray[0] - BigInt(1));
- if (fast) {
- expect(add_uint64_t(BigInt(-1) * bigArray[0], bigArray[0])).toBe(0);
- expect(add_uint64_t(BigInt(-1) * bigArray[0] + BigInt(10), bigArray[0])).toBe(10);
- } else {
- expect(add_uint64_t(BigInt(-1) * bigArray[0], bigArray[0])).toBe(0n);
- expect(add_uint64_t(BigInt(-1) * bigArray[0] + BigInt(10), bigArray[0])).toBe(10n);
- }
- if (fast) {
- expect(identity_uint64_t(0)).toBe(0);
- expect(identity_uint64_t(100)).toBe(100);
- expect(identity_uint64_t(BigInt(100))).toBe(100);
-
- expect(identity_int64_t(bigIntArray[0])).toBe(-1);
- expect(identity_int64_t(bigIntArray[0] - BigInt(1))).toBe(-2);
- } else {
- expect(identity_uint64_t(0)).toBe(0n);
- expect(identity_uint64_t(100)).toBe(100n);
- expect(identity_uint64_t(BigInt(100))).toBe(100n);
-
- expect(identity_int64_t(bigIntArray[0])).toBe(bigIntArray[0]);
- expect(identity_int64_t(bigIntArray[0] - BigInt(1))).toBe(bigIntArray[0] - BigInt(1));
- }
- Bun.gc(true);
- expect(add_char.native(1, 1)).toBe(2);
-
- expect(add_float(2.4, 2.8)).toBe(Math.fround(5.2));
- expect(add_double(4.2, 0.1)).toBe(4.3);
- expect(add_int8_t(1, 1)).toBe(2);
- expect(add_int16_t(1, 1)).toBe(2);
- expect(add_int32_t(1, 1)).toBe(2);
- if (fast) expect(add_int64_t(1, 1)).toBe(2);
- else expect(add_int64_t(1n, 1n)).toBe(2n);
- expect(add_uint8_t(1, 1)).toBe(2);
- expect(add_uint16_t(1, 1)).toBe(2);
- expect(add_uint32_t(1, 1)).toBe(2);
- Bun.gc(true);
- expect(is_null(null)).toBe(true);
- const cptr = ptr_should_point_to_42_as_int32_t();
- expect(cptr != 0).toBe(true);
- expect(typeof cptr === "number").toBe(true);
- expect(does_pointer_equal_42_as_int32_t(cptr)).toBe(true);
- const buffer = toBuffer(cptr, 0, 4);
- expect(buffer.readInt32(0)).toBe(42);
- expect(new DataView(toArrayBuffer(cptr, 0, 4), 0, 4).getInt32(0, true)).toBe(42);
- expect(ptr(buffer)).toBe(cptr);
- expect(new CString(cptr, 0, 1).toString()).toBe("*");
- expect(identity_ptr(cptr)).toBe(cptr);
- const second_ptr = ptr(new Buffer(8));
- expect(identity_ptr(second_ptr)).toBe(second_ptr);
- });
-
- it("CFunction", () => {
- var myCFunction = new CFunction({
- ptr: return_a_function_ptr_to_function_that_returns_true(),
- returns: "bool",
- });
- expect(myCFunction()).toBe(true);
- });
-
- const typeMap = {
- int8_t: -8,
- int16_t: -16,
- int32_t: -32,
- int64_t: -64n,
- uint8_t: 8,
- uint16_t: 16,
- uint32_t: 32,
- uint64_t: 64n,
- float: 32.5,
- double: 64.5,
- ptr: 0xdeadbeef,
- "void*": null,
- };
-
- it("JSCallback", () => {
- var toClose = new JSCallback(
- input => {
- return input;
- },
- {
- returns: "bool",
- args: ["bool"],
- },
- );
- expect(toClose.ptr > 0).toBe(true);
- toClose.close();
- expect(toClose.ptr === null).toBe(true);
- });
-
- describe("callbacks", () => {
- // Return types, 1 argument
- for (let [returnName, returnValue] of Object.entries(typeMap)) {
- it("fn(" + returnName + ") " + returnName, () => {
- var roundtripFunction = new CFunction({
- ptr: new JSCallback(
- input => {
- return input;
- },
- {
- returns: returnName,
- args: [returnName],
- },
- ).ptr,
- returns: returnName,
- args: [returnName],
- });
- expect(roundtripFunction(returnValue)).toBe(returnValue);
- });
- }
- // Return types, no args
- for (let [name, value] of Object.entries(typeMap)) {
- it("fn() " + name, () => {
- var roundtripFunction = new CFunction({
- ptr: new JSCallback(() => value, {
- returns: name,
- }).ptr,
- returns: name,
- });
- expect(roundtripFunction()).toBe(value);
- });
- }
- });
-
- describe("threadsafe callback", done => {
- // 1 arg, threadsafe
- for (let [name, value] of Object.entries(typeMap)) {
- it("fn(" + name + ") " + name, async () => {
- const cb = new JSCallback(
- arg1 => {
- expect(arg1).toBe(value);
- },
- {
- args: [name],
- threadsafe: true,
- },
- );
- var roundtripFunction = new CFunction({
- ptr: cb.ptr,
- returns: "void",
- args: [name],
- });
- roundtripFunction(value);
- await 1;
- });
- }
- });
-
- afterAll(() => {
- close();
- });
- });
-}
-
-it("read", () => {
- const buffer = new BigInt64Array(16);
- const dataView = new DataView(buffer.buffer);
- const addr = ptr(buffer);
-
- for (let i = 0; i < buffer.length; i++) {
- buffer[i] = BigInt(i);
- expect(read.intptr(addr, i * 8)).toBe(Number(dataView.getBigInt64(i * 8, true)));
- expect(read.ptr(addr, i * 8)).toBe(Number(dataView.getBigUint64(i * 8, true)));
- expect(read.f64(addr, i + 8)).toBe(dataView.getFloat64(i + 8, true));
- expect(read.i64(addr, i * 8)).toBe(dataView.getBigInt64(i * 8, true));
- expect(read.u64(addr, i * 8)).toBe(dataView.getBigUint64(i * 8, true));
- }
-
- for (let i = 0; i < buffer.byteLength - 4; i++) {
- // read is intended to behave like DataView
- // but instead of doing
- // new DataView(toArrayBuffer(myPtr)).getInt8(0, true)
- // you can do
- // read.i8(myPtr, 0)
- expect(read.i8(addr, i)).toBe(dataView.getInt8(i, true));
- expect(read.i16(addr, i)).toBe(dataView.getInt16(i, true));
- expect(read.i32(addr, i)).toBe(dataView.getInt32(i, true));
- expect(read.u8(addr, i)).toBe(dataView.getUint8(i, true));
- expect(read.u16(addr, i)).toBe(dataView.getUint16(i, true));
- expect(read.u32(addr, i)).toBe(dataView.getUint32(i, true));
- expect(read.f32(addr, i)).toBe(dataView.getFloat32(i, true));
- }
-});
-
-describe("run ffi", () => {
- ffiRunner(false);
- ffiRunner(true);
-});