diff --git a/crates/lune-std-ffi/src/c/fn_info.rs b/crates/lune-std-ffi/src/c/fn_info.rs index 43b7f543..29944991 100644 --- a/crates/lune-std-ffi/src/c/fn_info.rs +++ b/crates/lune-std-ffi/src/c/fn_info.rs @@ -43,7 +43,6 @@ impl FfiSignedness for CFnInfo { false } } - impl FfiSize for CFnInfo { fn get_size(&self) -> usize { SIZE_OF_POINTER @@ -203,6 +202,9 @@ impl CFnInfo { } impl LuaUserData for CFnInfo { + fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { + fields.add_field_method_get("size", |_, _| Ok(SIZE_OF_POINTER)); + } fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { // Subtype method_provider::provide_ptr(methods); diff --git a/crates/lune-std-ffi/src/c/ptr_info.rs b/crates/lune-std-ffi/src/c/ptr_info.rs index f39b4251..0758ba82 100644 --- a/crates/lune-std-ffi/src/c/ptr_info.rs +++ b/crates/lune-std-ffi/src/c/ptr_info.rs @@ -124,7 +124,7 @@ impl CPtrInfo { impl LuaUserData for CPtrInfo { fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { - fields.add_field_method_get("size", |_, _| Ok(size_of::())); + fields.add_field_method_get("size", |_, _| Ok(SIZE_OF_POINTER)); fields.add_field_function_get("inner", |lua, this| { let inner = association::get(lua, CPTR_INNER, this)? .ok_or_else(|| LuaError::external("inner type not found"))?; diff --git a/crates/lune-std-ffi/src/c/void_info.rs b/crates/lune-std-ffi/src/c/void_info.rs index 05ad57d6..97a2fa28 100644 --- a/crates/lune-std-ffi/src/c/void_info.rs +++ b/crates/lune-std-ffi/src/c/void_info.rs @@ -31,6 +31,9 @@ impl CVoidInfo { } impl LuaUserData for CVoidInfo { + fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { + fields.add_field_method_get("size", |_, _| Ok(0)); + } fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { method_provider::provide_to_string(methods); method_provider::provide_ptr(methods); diff --git a/crates/lune/src/tests.rs b/crates/lune/src/tests.rs index d5f56404..acce10ec 100644 --- a/crates/lune/src/tests.rs +++ b/crates/lune/src/tests.rs @@ -99,6 +99,24 @@ create_tests! { datetime_to_universal_time: "datetime/toUniversalTime", } +#[cfg(feature = "std-ffi")] +create_tests! { + ffi_external_closure_call_closure: "ffi/external_closure/callClosure", + ffi_external_closure_call_closure_with_pointer: "ffi/external_closure/callClosureWithPointer", + ffi_external_closure_call_hello_world: "ffi/external_closure/callHelloWorld", + ffi_external_math_add_int: "ffi/external_math/addInt", + ffi_external_math_mul_int: "ffi/external_math/mulInt", + ffi_external_pointer_pointer_read: "ffi/external_pointer/pointerRead", + ffi_external_pointer_pointer_write: "ffi/external_pointer/pointerWrite", + ffi_external_print_hello_world: "ffi/external_print/helloWorld", + ffi_external_struct_ab: "ffi/external_struct/ab", + ffi_cast: "ffi/cast", + ffi_is_integer: "ffi/isInteger", + ffi_pretty_print: "ffi/prettyPrint", + ffi_read_boundary: "ffi/readBoundary", + ffi_write_boundary: "ffi/writeBoundary", +} + #[cfg(feature = "std-fs")] create_tests! { fs_files: "fs/files", diff --git a/print-ignore-me.luau b/print-ignore-me.luau deleted file mode 100644 index 3686c9ea..00000000 --- a/print-ignore-me.luau +++ /dev/null @@ -1,12 +0,0 @@ -local ffi = require("@lune/ffi") - -print(ffi.int) -print(ffi.int:ptr()) -print(ffi.int:arr(5):ptr()) -print(ffi.int:arr(5)) - -print(ffi.funcInfo({ ffi.int }, ffi.int)) -print(ffi.funcInfo({ ffi.int, ffi.double }, ffi.int:ptr())) -print(ffi.funcInfo({ ffi.int, ffi.double }, ffi.int:ptr():ptr())) - -print(ffi.structInfo({ ffi.int, ffi.char })) diff --git a/tests/ffi/benchmark/external_call/deno.ts b/tests/ffi/benchmark/external_call/deno.ts index 46b56e0c..7332ae18 100644 --- a/tests/ffi/benchmark/external_call/deno.ts +++ b/tests/ffi/benchmark/external_call/deno.ts @@ -1,5 +1,5 @@ -import { libSuffix } from "../../utility/deno.ts"; -import { get_clock, get_offset } from "../../utility/proc_clock/deno.ts"; +import { libSuffix } from "../../utils/libSuffix.ts"; +import { get_clock, get_offset } from "../../utils/proc_clock/deno.ts"; const library_file = "./tests/ffi/benchmark/external_call/lib."+libSuffix; // @ts-ignore diff --git a/tests/ffi/benchmark/external_call/init.luau b/tests/ffi/benchmark/external_call/init.luau index 3c4eef6c..6df592be 100644 --- a/tests/ffi/benchmark/external_call/init.luau +++ b/tests/ffi/benchmark/external_call/init.luau @@ -1,35 +1,27 @@ local ffi = require("@lune/ffi") +local lib = require("../../utility/compile")("./tests/ffi/benchmark/external_call/lib.c") +local process = require("@lune/process") local c = ffi.c +local BENCH_SCALE: number = tonumber(process.env.BENCH_SCALE) or 1000000 -local proc_clock = require("../../utility/proc_clock") -local before, after = proc_clock.newBox() -local get_clock = proc_clock.getClock +-- Get clock provider +local procClock = require("../../utility/proc_clock") +local before, after = procClock.newBox() +local getClock = procClock.getClock -local testdir = "./tests/ffi/benchmark/external_call" -local compile = require("../../utility/compile") -compile(`{testdir}/lib.c`, `{testdir}/lib.so`) -local lib = ffi.open(`{testdir}/lib.so`) +local add = c.fn({ c.int, c.int }, c.int):callable(lib:find("add")) -local function bench_add(bench_scale: number) - local add_info = c.fn({ c.int, c.int }, c.int) +local a = c.int:box(0) +local delta = c.int:box(1) +local a_ref = a:ref() +local delta_ref = delta:ref() - local add_callable = add_info:callable(lib:find("add")) - - local a = c.int:box(0) - local delta = c.int:box(1) - - local a_ref = a:ref() - local delta_ref = delta:ref() - - get_clock(before) - for i = 1, bench_scale do - add_callable(a, a_ref, delta_ref) - end - get_clock(after) - print(proc_clock.getOffset(before, after)) - - local result = c.int:readData(a) - assert(result == bench_scale, `bench_add failed. result expected {bench_scale}, got {result}`) +getClock(before) +for i = 1, BENCH_SCALE do + add(a, a_ref, delta_ref) end +getClock(after) -bench_add(1000000) +print(procClock.getOffset(before, after)) +local result = c.int:readData(a) +assert(result == BENCH_SCALE, `bench_add failed. result expected {BENCH_SCALE}, got {result}`) diff --git a/tests/ffi/benchmark/external_call/luajit.lua b/tests/ffi/benchmark/external_call/luajit.lua index e5dc41a8..b1eb6f43 100644 --- a/tests/ffi/benchmark/external_call/luajit.lua +++ b/tests/ffi/benchmark/external_call/luajit.lua @@ -2,26 +2,23 @@ --!nocheck local ffi = require("ffi") +local BENCH_SCALE = 1000000 -local function bench_add(bench_scale) - ffi.cdef([[ - int add(int a, int b); - ]]) - local lib = ffi.load("./tests/ffi/benchmark/external_call/lib.so") - local add = lib.add - local a = 0 +ffi.cdef([[ + int add(int a, int b); +]]) +local lib = ffi.load("./tests/ffi/benchmark/external_call/lib.so") +local add = lib.add +local a = 0 - local before = os.clock() - for i = 1, bench_scale do - a = add(a, 1) - end - local after = os.clock() - - print(after - before) - assert( - a == bench_scale, - string.format("bench_add failed. result expected %d, got %d", bench_scale, a) - ) +local before = os.clock() +for i = 1, BENCH_SCALE do + a = add(a, 1) end +local after = os.clock() -bench_add(1000000) +print(after - before) +assert( + a == BENCH_SCALE, + string.format("bench_add failed. result expected %d, got %d", BENCH_SCALE, a) +) diff --git a/tests/ffi/external_closure/callClosure.luau b/tests/ffi/external_closure/callClosure.luau new file mode 100644 index 00000000..b73fe6c7 --- /dev/null +++ b/tests/ffi/external_closure/callClosure.luau @@ -0,0 +1,14 @@ +local callableWrapper = require("../utils/callableWrapper") +local ffi = require("@lune/ffi") +local lib = require("../utils/compile")("./tests/ffi/external_closure/lib.c") +local c = ffi.c + +-- Create closure +local closureInfo = c.fn({ c.int, c.int }, c.int) +local closure = closureInfo:closure(function(ret, a, b) + c.int:writeData(ret, c.int:readData(a) + c.int:readData(b)) +end) + +local callClosure = callableWrapper(lib:find("call_closure"), { closureInfo }, c.int) +local result = callClosure(closure:ref()) +assert(result == 72, `callClosure failed. result expected 20000, got {result}`) diff --git a/tests/ffi/external_closure/callClosureWithPointer.luau b/tests/ffi/external_closure/callClosureWithPointer.luau new file mode 100644 index 00000000..5fd47b91 --- /dev/null +++ b/tests/ffi/external_closure/callClosureWithPointer.luau @@ -0,0 +1,15 @@ +local callableWrapper = require("../utils/callableWrapper") +local ffi = require("@lune/ffi") +local lib = require("../utils/compile")("./tests/ffi/external_closure/lib.c") +local c = ffi.c + +-- Create closure +local closureWithPointerInfo = c.fn({ c.int, c.int:ptr() }, c.int) +local closureWithPointer = closureWithPointerInfo:closure(function(returnRef, aRef, bRef) + c.int:writeData(returnRef, c.int:readData(aRef) + c.int:readData(bRef:deref())) +end) + +local callClosureWithPointer = + callableWrapper(lib:find("call_closure_with_pointer"), { closureWithPointerInfo }, c.int) +local result = callClosureWithPointer(closureWithPointer:ref()) +assert(result == 72, `closureWithPointer failed. result expected 20000, got {result}`) diff --git a/tests/ffi/external_closure/callHelloWorld.luau b/tests/ffi/external_closure/callHelloWorld.luau new file mode 100644 index 00000000..811601d1 --- /dev/null +++ b/tests/ffi/external_closure/callHelloWorld.luau @@ -0,0 +1,12 @@ +local ffi = require("@lune/ffi") +local lib = require("../utils/compile")("./tests/ffi/external_closure/lib.c") +local c = ffi.c + +-- Create closure +local helloWorldInfo = c.fn({}, c.void) +local helloWorld = helloWorldInfo:closure(function() + print("Hello world in lua closure!") +end) + +local callHelloWorld = c.fn({ helloWorldInfo }, c.void):callable(lib:find("call_hello_world")) +callHelloWorld(nil, helloWorld:ref()) diff --git a/tests/ffi/external_closure/init.luau b/tests/ffi/external_closure/init.luau deleted file mode 100644 index f442b9e4..00000000 --- a/tests/ffi/external_closure/init.luau +++ /dev/null @@ -1,52 +0,0 @@ -local ffi = require("@lune/ffi") -local c = ffi.c - -local testdir = "./tests/ffi/external_closure" -local compile = require("../utility/compile") -compile(`{testdir}/lib.c`, `{testdir}/lib.so`) -local lib = ffi.open(`{testdir}/lib.so`) - -local function test_callClosure() - local closureInfo = c.fn({ c.int, c.int }, c.int) - local closure = closureInfo:closure(function(ret, a, b) - c.int:writeData(ret, c.int:readData(a) + c.int:readData(b)) - end) - - local callClosure = c.fn({ closureInfo }, c.int):callable(lib:find("call_closure")) - - local resultBox = ffi.box(c.int.size) - callClosure(resultBox, closure:ref()) - local result = c.int:readData(resultBox) - assert(result == 72, `test_callClosure failed. result expected 20000, got {result}`) -end - -test_callClosure() - -local function test_helloWorld() - local helloWorldInfo = c.fn({}, c.void) - local helloWorld = helloWorldInfo:closure(function() - print("Hello world in lua closure!") - end) - - local callHelloWorld = c.fn({ helloWorldInfo }, c.void):callable(lib:find("call_hello_world")) - callHelloWorld(nil, helloWorld:ref()) -end - -test_helloWorld() - -local function test_closureWithPointer() - local closureWithPointerInfo = c.fn({ c.int, c.int:ptr() }, c.int) - local closureWithPointer = closureWithPointerInfo:closure(function(returnRef, aRef, bRef) - c.int:writeData(returnRef, c.int:readData(aRef) + c.int:readData(bRef:deref())) - end) - - local callClosureWithPointer = c.fn({ closureWithPointerInfo }, c.int) - :callable(lib:find("call_closure_with_pointer")) - - local resultBox = ffi.box(c.int.size) - callClosureWithPointer(resultBox, closureWithPointer:ref()) - local result = c.int:readData(resultBox) - assert(result == 72, `test_closureWithPointer failed. result expected 20000, got {result}`) -end - -test_closureWithPointer() diff --git a/tests/ffi/external_math/addInt.luau b/tests/ffi/external_math/addInt.luau new file mode 100644 index 00000000..d63f3132 --- /dev/null +++ b/tests/ffi/external_math/addInt.luau @@ -0,0 +1,9 @@ +local callableWrapper = require("../utils/callableWrapper") +local ffi = require("@lune/ffi") +local lib = require("../utils/compile")("./tests/ffi/external_math/lib.c") +local c = ffi.c + +local addInt = callableWrapper(lib:find("add_int"), { c.int, c.int }, c.int) +local result = addInt(100, 200) + +assert(result == 300, `test_addInt failed. result expected 300, got {result}`) diff --git a/tests/ffi/external_math/init.luau b/tests/ffi/external_math/init.luau deleted file mode 100644 index f4ce95ff..00000000 --- a/tests/ffi/external_math/init.luau +++ /dev/null @@ -1,37 +0,0 @@ -local ffi = require("@lune/ffi") -local c = ffi.c - -local testdir = "./tests/ffi/external_math" -local compile = require("../utility/compile") -compile(`{testdir}/lib.c`, `{testdir}/lib.so`) -local lib = ffi.open(`{testdir}/lib.so`) - -local function test_addInt() - local addInt = c.fn({ c.int, c.int }, c.int):callable(lib:find("add_int")) - - local resultBox = ffi.box(c.int.size) - local arg1 = c.int:box(100) - local arg2 = c.int:box(200) - - addInt(resultBox, arg1:ref(), arg2:ref()) - local result = c.int:readData(resultBox) - - assert(result == 300, `test_addInt failed. result expected 300, got {result}`) -end - -test_addInt() - -local function test_mulInt() - local mulInt = c.fn({ c.int, c.int }, c.int):callable(lib:find("mul_int")) - - local resultBox = ffi.box(c.int.size) - local arg1 = c.int:box(100) - local arg2 = c.int:box(200) - - mulInt(resultBox, arg1:ref(), arg2:ref()) - local result = c.int:readData(resultBox) - - assert(result == 20000, `test_mulInt failed. result expected 20000, got {result}`) -end - -test_mulInt() diff --git a/tests/ffi/external_math/mulInt.luau b/tests/ffi/external_math/mulInt.luau new file mode 100644 index 00000000..e14a86bb --- /dev/null +++ b/tests/ffi/external_math/mulInt.luau @@ -0,0 +1,8 @@ +local callableWrapper = require("../utils/callableWrapper") +local ffi = require("@lune/ffi") +local lib = require("../utils/compile")("./tests/ffi/external_math/lib.c") +local c = ffi.c + +local mulInt = callableWrapper(lib:find("mul_int"), { c.int, c.int }, c.int) +local result = mulInt(100, 200) +assert(result == 20000, `test_mulInt failed. result expected 20000, got {result}`) diff --git a/tests/ffi/external_pointer/init.luau b/tests/ffi/external_pointer/init.luau deleted file mode 100644 index 8e0e988f..00000000 --- a/tests/ffi/external_pointer/init.luau +++ /dev/null @@ -1,20 +0,0 @@ -local ffi = require("@lune/ffi") -local c = ffi.c - -local testdir = "./tests/ffi/external_pointer" -local compile = require("../utility/compile") -compile(`{testdir}/lib.c`, `{testdir}/lib.so`) -local lib = ffi.open(`{testdir}/lib.so`) - -local function test_pointerWrite() - local pointerWrite = c.fn({ c.int:ptr() }, c.void):callable(lib:find("pointer_write")) - - local aBox = ffi.box(c.int.size) - pointerWrite(nil, aBox:ref():ref()) - - local result = c.int:readData(aBox) - - assert(result == 123, `pointer failed. result expected 123, got {result}`) -end - -test_pointerWrite() diff --git a/tests/ffi/external_pointer/lib.c b/tests/ffi/external_pointer/lib.c index 697fd090..7d7440a8 100644 --- a/tests/ffi/external_pointer/lib.c +++ b/tests/ffi/external_pointer/lib.c @@ -1,3 +1,7 @@ void pointer_write(int *a) { *a = 123; } + +int pointer_read(int *a) { + return *a; +} diff --git a/tests/ffi/external_pointer/pointerRead.luau b/tests/ffi/external_pointer/pointerRead.luau new file mode 100644 index 00000000..e2490a3f --- /dev/null +++ b/tests/ffi/external_pointer/pointerRead.luau @@ -0,0 +1,8 @@ +local callableWrapper = require("../utils/callableWrapper") +local ffi = require("@lune/ffi") +local lib = require("../utils/compile")("./tests/ffi/external_pointer/lib.c") +local c = ffi.c + +local pointerRead = callableWrapper(lib:find("pointer_read"), { c.int:ptr() }, c.int) +local result = pointerRead(c.int:box(123):ref():ref()) +assert(result == 123, `pointerRead failed. result expected 123, got {result}`) diff --git a/tests/ffi/external_pointer/pointerWrite.luau b/tests/ffi/external_pointer/pointerWrite.luau new file mode 100644 index 00000000..72a9b40b --- /dev/null +++ b/tests/ffi/external_pointer/pointerWrite.luau @@ -0,0 +1,9 @@ +local ffi = require("@lune/ffi") +local lib = require("../utils/compile")("./tests/ffi/external_pointer/lib.c") +local c = ffi.c + +local pointerWrite = c.fn({ c.int:ptr() }, c.void):callable(lib:find("pointer_write")) +local aBox = ffi.box(c.int.size) +pointerWrite(nil, aBox:ref():ref()) +local result = c.int:readData(aBox) +assert(result == 123, `pointerWrite failed. result expected 123, got {result}`) diff --git a/tests/ffi/external_print/helloWorld.luau b/tests/ffi/external_print/helloWorld.luau new file mode 100644 index 00000000..665283b1 --- /dev/null +++ b/tests/ffi/external_print/helloWorld.luau @@ -0,0 +1,5 @@ +local ffi = require("@lune/ffi") +local lib = require("../utils/compile")("./tests/ffi/external_print/lib.c") +local c = ffi.c + +c.fn({}, c.void):callable(lib:find("hello_world"))(nil) diff --git a/tests/ffi/external_print/init.luau b/tests/ffi/external_print/init.luau deleted file mode 100644 index 174fdc2c..00000000 --- a/tests/ffi/external_print/init.luau +++ /dev/null @@ -1,17 +0,0 @@ -local ffi = require("@lune/ffi") -local c = ffi.c - -local testdir = "./tests/ffi/external_print" -local compile = require("../utility/compile") -compile(`{testdir}/lib.c`, `{testdir}/lib.so`) -local lib = ffi.open(`{testdir}/lib.so`) - -local function test_hello_world() - local hello_world_info = c.fn({}, c.void) - - local hello_world_callable = hello_world_info:callable(lib:find("hello_world")) - - hello_world_callable(nil) -end - -test_hello_world() diff --git a/tests/ffi/external_struct/ab.luau b/tests/ffi/external_struct/ab.luau new file mode 100644 index 00000000..114b74c3 --- /dev/null +++ b/tests/ffi/external_struct/ab.luau @@ -0,0 +1,12 @@ +local callableWrapper = require("../utils/callableWrapper") +local ffi = require("@lune/ffi") +local lib = require("../utils/compile")("./tests/ffi/external_struct/lib.c") +local c = ffi.c + +local argStructInfo = c.struct({ c.int, c.int:ptr() }) +local resultStructInfo = c.struct({ c.int, c.int }) + +local ab = callableWrapper(lib:find("ab"), { argStructInfo }, resultStructInfo) +local result = ab({ 100, c.int:box(200):ref() } :: { any }) +assert(result[1] == 300, `ab failed. result expected 300, got {result[1]}`) +assert(result[2] == 20000, `ab failed. result expected 300, got {result[2]}`) diff --git a/tests/ffi/external_struct/init.luau b/tests/ffi/external_struct/init.luau deleted file mode 100644 index 109ff491..00000000 --- a/tests/ffi/external_struct/init.luau +++ /dev/null @@ -1,28 +0,0 @@ -local ffi = require("@lune/ffi") -local c = ffi.c - -local testdir = "./tests/ffi/external_struct" -local compile = require("../utility/compile") -compile(`{testdir}/lib.c`, `{testdir}/lib.so`) -local lib = ffi.open(`{testdir}/lib.so`) - -local function test_AB() - local argStructInfo = c.struct({ c.int, c.int:ptr() }) - local resultStructInfo = c.struct({ c.int, c.int }) - - local AB = c.fn({ argStructInfo }, resultStructInfo) - - local AB_callable = AB:callable(lib:find("AB")) - - local resultBox = ffi.box(resultStructInfo.size) - local b = c.int:box(200) - local argStruct = argStructInfo:box({ 100, b:ref() }) - - AB_callable(resultBox, argStruct:ref()) - local result = resultStructInfo:readData(resultBox) - - assert(result[1] == 300, `AB failed. result expected 300, got {result[1]}`) - assert(result[2] == 20000, `AB failed. result expected 300, got {result[2]}`) -end - -test_AB() diff --git a/tests/ffi/external_struct/lib.c b/tests/ffi/external_struct/lib.c index b542d5d3..f68a0674 100644 --- a/tests/ffi/external_struct/lib.c +++ b/tests/ffi/external_struct/lib.c @@ -8,7 +8,7 @@ typedef struct { int mul; } ResultStruct; -ResultStruct AB(ArgStruct t) { +ResultStruct ab(ArgStruct t) { ResultStruct result = { t.a+ * t.b, t.a * (*t.b) }; return result; } diff --git a/tests/ffi/pretty_print.luau b/tests/ffi/prettyPrint.luau similarity index 100% rename from tests/ffi/pretty_print.luau rename to tests/ffi/prettyPrint.luau diff --git a/tests/ffi/read_boundary.luau b/tests/ffi/readBoundary.luau similarity index 99% rename from tests/ffi/read_boundary.luau rename to tests/ffi/readBoundary.luau index bc573579..dd8e3d25 100644 --- a/tests/ffi/read_boundary.luau +++ b/tests/ffi/readBoundary.luau @@ -1,5 +1,4 @@ local ffi = require("@lune/ffi") - local ok -- Case1: Success @@ -24,7 +23,6 @@ end) assert(ok, "assersion failed, Case3 should success") -- Case4: Success - ok = pcall(function() local box = ffi.box(ffi.u8.size * 2) ffi.u8:readData(box, ffi.u8.size) diff --git a/tests/ffi/utility/compile.luau b/tests/ffi/utility/compile.luau deleted file mode 100644 index 5cb24347..00000000 --- a/tests/ffi/utility/compile.luau +++ /dev/null @@ -1,9 +0,0 @@ -local process = require("@lune/process") -local function compile(file, out) - local gcc = process.exec("gcc", { "-shared", "-o", out, "-fPIC", file }) - if not gcc.ok then - error("Failed to execute gcc command\n" .. gcc.stdout .. gcc.stderr) - end -end - -return compile diff --git a/tests/ffi/utils/callableWrapper.luau b/tests/ffi/utils/callableWrapper.luau new file mode 100644 index 00000000..ffa9597c --- /dev/null +++ b/tests/ffi/utils/callableWrapper.luau @@ -0,0 +1,38 @@ +--!nocheck +local ffi = require("@lune/ffi") + +local function callableWrapper( + functionRef: ffi.RefData, + argTypeList: { ffi.CTypes }, + retType: ffi.CTypes +): (...any) -> any + local callable = ffi.c.fn(argTypeList, retType):callable(functionRef) + + return function(...) + local argValues = table.create(#argTypeList + 1) + + local resultBox + if retType ~= ffi.c.void then + resultBox = ffi.box(retType.size) + end + argValues[1] = resultBox + + for index, argType in argTypeList do + local arg = select(index, ...) + if type(arg) == "userdata" then + argValues[index + 1] = arg + else + argValues[index + 1] = argType:box(arg):ref() + end + end + + callable(table.unpack(argValues, 1, #argTypeList + 1)) + + if retType == ffi.c.void then + return nil + end + return retType:readData(resultBox) + end +end + +return callableWrapper diff --git a/tests/ffi/utils/compile.luau b/tests/ffi/utils/compile.luau new file mode 100644 index 00000000..624ddaf0 --- /dev/null +++ b/tests/ffi/utils/compile.luau @@ -0,0 +1,25 @@ +local ffi = require("@lune/ffi") +local process = require("@lune/process") + +local function getLibSuffix(): string + if process.os == "linux" then + return "so" + elseif process.os == "windows" then + return "dll" + elseif process.os == "macos" then + return "dylib" + end + error("Unknown OS") +end + +local function compile(file: string): ffi.LibData + local out = file:gsub("%.c$", "." .. getLibSuffix()) + local gcc = process.exec("gcc", { "-shared", "-o", out, "-fPIC", file }) + if not gcc.ok then + error("Failed to execute gcc command\n" .. gcc.stdout .. gcc.stderr) + end + + return ffi.open(out) +end + +return compile diff --git a/tests/ffi/utility/deno.ts b/tests/ffi/utils/libSuffix.ts similarity index 100% rename from tests/ffi/utility/deno.ts rename to tests/ffi/utils/libSuffix.ts diff --git a/tests/ffi/utility/proc_clock/deno.ts b/tests/ffi/utils/proc_clock/deno.ts similarity index 83% rename from tests/ffi/utility/proc_clock/deno.ts rename to tests/ffi/utils/proc_clock/deno.ts index d5c8d140..23ea8d4a 100644 --- a/tests/ffi/utility/proc_clock/deno.ts +++ b/tests/ffi/utils/proc_clock/deno.ts @@ -1,6 +1,6 @@ -import { libSuffix } from "../deno.ts"; +import { libSuffix } from "../libSuffix.ts"; -const library_file = "./tests/ffi/utility/proc_clock/lib."+libSuffix; +const library_file = "./tests/ffi/utils/proc_clock/lib."+libSuffix; // @ts-ignore let library = Deno.dlopen(library_file, { sizeof_clock: { diff --git a/tests/ffi/utility/proc_clock/init.luau b/tests/ffi/utils/proc_clock/init.luau similarity index 89% rename from tests/ffi/utility/proc_clock/init.luau rename to tests/ffi/utils/proc_clock/init.luau index 720cbd6a..8877a4c5 100644 --- a/tests/ffi/utility/proc_clock/init.luau +++ b/tests/ffi/utils/proc_clock/init.luau @@ -7,10 +7,7 @@ local c = ffi.c local procClock = {} -local libdir = "./tests/ffi/utility/proc_clock" -local compile = require("../compile") -compile(`{libdir}/lib.c`, `{libdir}/lib.so`) -local lib = ffi.open(`{libdir}/lib.so`) +local lib = require("../compile")("./tests/ffi/utils/proc_clock/lib.c") -- sizeof_clock local sizeofClock = c.fn({}, c.int):callable(lib:find("sizeof_clock")) diff --git a/tests/ffi/utility/proc_clock/lib.c b/tests/ffi/utils/proc_clock/lib.c similarity index 100% rename from tests/ffi/utility/proc_clock/lib.c rename to tests/ffi/utils/proc_clock/lib.c diff --git a/tests/ffi/write_boundary.luau b/tests/ffi/writeBoundary.luau similarity index 99% rename from tests/ffi/write_boundary.luau rename to tests/ffi/writeBoundary.luau index 53291d6e..afb2b162 100644 --- a/tests/ffi/write_boundary.luau +++ b/tests/ffi/writeBoundary.luau @@ -1,6 +1,5 @@ local ffi = require("@lune/ffi") local c = ffi.c - local ok -- Case1: Fail diff --git a/types/ffi.luau b/types/ffi.luau index f21b4bf5..a60c6ee4 100644 --- a/types/ffi.luau +++ b/types/ffi.luau @@ -42,15 +42,13 @@ local ffi = {} --[=[ @class C - @within FFI Namespace for compile time sized c types. ]=] local c = {} ffi.c = c ---#region Data -- - +--#region Data --[=[ @class RefData @@ -124,7 +122,13 @@ export type RefData = { @param dst_offset The offset in the destination where the data will be pasted @param src_offset The offset in the source data from where the data will be copied ]=] - copyFrom: (self: RefData, src: (BoxData|RefData), length: number, dst_offset: number, src_offset: number)->(); + copyFrom: ( + self: RefData, + src: BoxData | RefData, + length: number, + dst_offset: number, + src_offset: number + ) -> (), } --[=[ @@ -190,7 +194,13 @@ export type BoxData = { @param dst_offset The offset in the destination where the data will be pasted @param src_offset The offset in the source data from where the data will be copied ]=] - copyFrom: (self: BoxData, src: (BoxData|RefData), length: number, dst_offset: number, src_offset: number)->(); + copyFrom: ( + self: BoxData, + src: BoxData | RefData, + length: number, + dst_offset: number, + src_offset: number + ) -> (), } --[=[ @@ -223,7 +233,10 @@ export type LibData = { If return type is `void`, pass `nil`. ]=] -export type CallableData = (ret: (RefData|BoxData)?, ...RefData)->() & { +export type CallableData = ( + ret: (RefData | BoxData)?, + ...RefData +) -> () & { -- apply: (self: Callable, args: Args)->AppliedCallable, } @@ -246,13 +259,11 @@ export type ClosureData = { @return A reference of the closure ]=] - ref: (self: ClosureData)->RefData, + ref: (self: ClosureData) -> RefData, } +--#endregion Data ---#endregion Data -- - ---#region C ABI Type Infos -- - +--#region C ABI Type Infos -- NOTE: T is a unique identifier for the `CType` and R is the closest Lua type. export type CTypeInfo = { --[=[ @@ -278,14 +289,19 @@ export type CTypeInfo = { -- realize box: (self: CTypeInfo, val: R) -> BoxData, - readData: (self: CTypeInfo, target: (RefData|BoxData), offset: number?) -> R, - writeData: (self: CTypeInfo, target: (RefData|BoxData), value: R, offset: number?) -> (), - stringifyData: (self: CTypeInfo, target: (RefData|BoxData), offset: number?) -> string, + readData: (self: CTypeInfo, target: RefData | BoxData, offset: number?) -> R, + writeData: (self: CTypeInfo, target: RefData | BoxData, value: R, offset: number?) -> (), + stringifyData: (self: CTypeInfo, target: RefData | BoxData, offset: number?) -> string, -- FIXME: recursive types; 'intoType' should be CTypes - cast: (self: CTypeInfo, intoType: any, fromData: (RefData|BoxData), intoData: (RefData|BoxData)) -> (), + cast: ( + self: CTypeInfo, + intoType: any, + fromData: RefData | BoxData, + intoData: RefData | BoxData + ) -> (), } & { ["__phantom"]: T } -type NumCType = CTypeInfo +type NumCType = CTypeInfo export type CPtrInfo = { --[=[ @@ -313,8 +329,13 @@ export type CPtrInfo = { -- FIXME: recursive types; result 'any' should be CPtrInfo> ptr: (self: CPtrInfo) -> any, - readRef: (self: CPtrInfo, target: (RefData|BoxData), offset: number?) -> RefData, - writeRef: (self: CPtrInfo, target: (RefData|BoxData), value: (RefData|BoxData), offset: number?) -> (), + readRef: (self: CPtrInfo, target: RefData | BoxData, offset: number?) -> RefData, + writeRef: ( + self: CPtrInfo, + target: RefData | BoxData, + value: RefData | BoxData, + offset: number? + ) -> (), } --[=[ @@ -353,9 +374,20 @@ export type CArrInfo = { -- realize box: (self: CArrInfo, table: { T }) -> BoxData, - readData: (self: CArrInfo, target: (RefData|BoxData), offset: number?) -> { T }, - writeData: (self: CArrInfo, target: (RefData|BoxData), value: { R }, target_offset: number?) -> (), - copyData: (self: CArrInfo, dst: (RefData|BoxData), src: (RefData|BoxData), dst_offset: number?, src_offset: number?) -> (), + readData: (self: CArrInfo, target: RefData | BoxData, offset: number?) -> { T }, + writeData: ( + self: CArrInfo, + target: RefData | BoxData, + value: { R }, + target_offset: number? + ) -> (), + copyData: ( + self: CArrInfo, + dst: RefData | BoxData, + src: RefData | BoxData, + dst_offset: number?, + src_offset: number? + ) -> (), offset: (self: CArrInfo, index: number) -> number, } @@ -366,6 +398,16 @@ export type CArrInfo = { A c function signature type information. ]=] export type CFnInfo = { + --[=[ + @within CFnInfo + @tag Field + @field size + + The size of a function pointer. + + Equivalent to `ffi.c.usize.size`. + ]=] + size: number, --[=[ @within CFnInfo @tag Method @@ -385,7 +427,7 @@ export type CFnInfo = { @return A closure. ]=] - closure: (self: CFnInfo, (ret: RefData, ...RefData)->()) -> ClosureData, + closure: (self: CFnInfo, (ret: RefData, ...RefData) -> ()) -> ClosureData, } --[=[ @@ -394,6 +436,13 @@ export type CFnInfo = { A c struct type information. ]=] export type CStructInfo = { + --[=[ + @within CStructInfo + @tag Field + @field size + + The size of a struct, including padding. + ]=] size: number, --[=[ @@ -406,7 +455,7 @@ export type CStructInfo = { @param len The length of the array @return A struct array type ]=] - arr: (self: CStructInfo, len: number) -> CArrInfo, + arr: (self: CStructInfo, len: number) -> CArrInfo, --[=[ @within CSturctInfo @tag Method @@ -419,9 +468,20 @@ export type CStructInfo = { ptr: (self: CStructInfo) -> CPtrInfo, box: (self: CStructInfo, table: { any }) -> BoxData, - readData: (self: CStructInfo, target: (RefData|BoxData), offset: number?) -> { any }, - writeData: (self: CStructInfo, target: (RefData|BoxData), table: { any }, offset: number?) -> (), - copyData: (self: CStructInfo, dst: (RefData|BoxData), src: (RefData|BoxData), dst_offset: number?, src_offset: number?) -> (), + readData: (self: CStructInfo, target: RefData | BoxData, offset: number?) -> { any }, + writeData: ( + self: CStructInfo, + target: RefData | BoxData, + table: { any }, + offset: number? + ) -> (), + copyData: ( + self: CStructInfo, + dst: RefData | BoxData, + src: RefData | BoxData, + dst_offset: number?, + src_offset: number? + ) -> (), offset: (self: CStructInfo, index: number) -> number, field: (self: CStructInfo, index: number) -> CTypes, @@ -433,6 +493,14 @@ export type CStructInfo = { A type that represents c void. can only be used for the function return type. ]=] export type CVoidInfo = { + --[=[ + @within CVoidInfo + @tag Field + @field size + + The size of the void type. It is always 0. + ]=] + size: number, --[=[ @within CVoidInfo @tag Method @@ -445,13 +513,11 @@ export type CVoidInfo = { ptr: (self: CVoidInfo) -> CPtrInfo, } c.void = {} :: CVoidInfo +--#endregion C ABI Type Infos ---#endregion C ABI Type Infos -- - ---#region Fixed size Rust-style types -- - +--#region Fixed size Rust-style types --[=[ - @class u8 + @prop u8 NumCType @within FFI A 8-bit sized unsigned integer, Equivalent to `uint8_t` in `stdint`. @@ -459,7 +525,7 @@ c.void = {} :: CVoidInfo ffi.u8 = {} :: u8 export type u8 = NumCType<"u8"> --[=[ - @class u16 + @prop u16 NumCType @within FFI A 16-bit sized unsigned integer, Equivalent to `uint16_t` in `stdint`. @@ -467,7 +533,7 @@ export type u8 = NumCType<"u8"> ffi.u16 = {} :: u16 export type u16 = NumCType<"u16"> --[=[ - @class u32 + @prop u32 NumCType @within FFI A 32-bit sized unsigned integer, Equivalent to `uint32_t` in `stdint`. @@ -475,7 +541,7 @@ export type u16 = NumCType<"u16"> ffi.u32 = {} :: u32 export type u32 = NumCType<"u32"> --[=[ - @class u64 + @prop u64 NumCType @within FFI A 64-bit sized unsigned integer, Equivalent to `uint64_t` in `stdint`. @@ -483,7 +549,7 @@ export type u32 = NumCType<"u32"> ffi.u64 = {} :: u64 export type u64 = NumCType<"u64"> --[=[ - @class u128 + @prop u128 NumCType @within FFI A 128-bit sized unsigned integer, Equivalent to `uint128_t` in `stdint`. @@ -491,7 +557,7 @@ export type u64 = NumCType<"u64"> ffi.u128 = {} :: u128 export type u128 = NumCType<"u128"> --[=[ - @class i8 + @prop i8 NumCType @within FFI A 8-bit sized signed integer, Equivalent to `int8_t` in `stdint`. @@ -499,7 +565,7 @@ export type u128 = NumCType<"u128"> ffi.i8 = {} :: i8 export type i8 = NumCType<"i8"> --[=[ - @class i16 + @prop i16 NumCType @within FFI A 16-bit sized signed integer, Equivalent to `int16_t` in `stdint`. @@ -507,7 +573,7 @@ export type i8 = NumCType<"i8"> ffi.i16 = {} :: i16 export type i16 = NumCType<"i16"> --[=[ - @class i32 + @prop i32 NumCType @within FFI A 32-bit sized signed integer, Equivalent to `int32_t` in `stdint`. @@ -515,7 +581,7 @@ export type i16 = NumCType<"i16"> ffi.i32 = {} :: i32 export type i32 = NumCType<"i32"> --[=[ - @class i64 + @prop i64 NumCType @within FFI A 64-bit sized signed integer, Equivalent to `int64_t` in `stdint`. @@ -523,7 +589,7 @@ export type i32 = NumCType<"i32"> ffi.i64 = {} :: i64 export type i64 = NumCType<"i64"> --[=[ - @class i128 + @prop i128 NumCType @within FFI A 128-bit sized signed integer, Equivalent to `int128_t` in `stdint`. @@ -531,7 +597,7 @@ export type i64 = NumCType<"i64"> ffi.i128 = {} :: i128 export type i128 = NumCType<"i128"> --[=[ - @class f32 + @prop f32 NumCType @within FFI A single-precision 32-bit sized floating-point, Almost always equivalent to `float` in C. @@ -539,7 +605,7 @@ export type i128 = NumCType<"i128"> ffi.f32 = {} :: f32 export type f32 = NumCType<"f32"> --[=[ - @class f64 + @prop f64 NumCType @within FFI A double-precision 64-bit sized floating-point, Almost always equivalent to `double` in C. @@ -547,7 +613,7 @@ export type f32 = NumCType<"f32"> ffi.f64 = {} :: f64 export type f64 = NumCType<"f64"> --[=[ - @class usize + @prop usize NumCType @within FFI A machine specific pointer sized unsigned integer. @@ -555,19 +621,18 @@ export type f64 = NumCType<"f64"> ffi.usize = {} :: usize export type usize = NumCType<"usize"> --[=[ - @class isize + @prop isize NumCType @within FFI A machine specific pointer sized signed integer. ]=] ffi.isize = {} :: isize export type isize = NumCType<"isize"> +--#endregion Fixed size Rust-style types ---#endregion Fixed size Rust-style types -- - ---#region Variable size C-style types -- +--#region Variable size C-style types --[=[ - @class char + @prop char NumCType @within C Compiler defined C `char` type. @@ -578,12 +643,8 @@ export type isize = NumCType<"isize"> ]=] c.char = {} :: char export type char = NumCType<"char"> --- c.float = {} :: float --- export type float = NumCType<"float"> --- c.double = {} :: double --- export type double = NumCType<"double"> --[=[ - @class uchar + @prop uchar NumCType @within C Compiler defined C `unsigned char` type. @@ -593,7 +654,7 @@ export type char = NumCType<"char"> c.uchar = {} :: uchar export type uchar = NumCType<"uchar"> --[=[ - @class schar + @prop schar NumCType @within C Compiler defined C `signed char` type. @@ -601,7 +662,7 @@ export type uchar = NumCType<"uchar"> c.schar = {} :: schar export type schar = NumCType<"schar"> --[=[ - @class short + @prop short NumCType @within C Compiler defined C `short` type. @@ -609,7 +670,7 @@ export type schar = NumCType<"schar"> c.short = {} :: short export type short = NumCType<"short"> --[=[ - @class ushort + @prop ushort NumCType @within C Compiler defined C `unsigned short` type. @@ -617,7 +678,7 @@ export type short = NumCType<"short"> c.ushort = {} :: ushort export type ushort = NumCType<"ushort"> --[=[ - @class int + @prop int NumCType @within C Compiler defined C `int` type. @@ -627,7 +688,7 @@ export type ushort = NumCType<"ushort"> c.int = {} :: int export type int = NumCType<"int"> --[=[ - @class uint + @prop uint NumCType @within C Compiler defined C `unsigned int` type. @@ -637,7 +698,7 @@ export type int = NumCType<"int"> c.uint = {} :: uint export type uint = NumCType<"uint"> --[=[ - @class long + @prop long NumCType @within C Compiler defined C `long` type. @@ -647,7 +708,7 @@ export type uint = NumCType<"uint"> c.long = {} :: long export type long = NumCType<"long"> --[=[ - @class ulong + @prop ulong NumCType @within C Compiler defined C `unsigned long` type. @@ -657,7 +718,7 @@ export type long = NumCType<"long"> c.ulong = {} :: ulong export type ulong = NumCType<"ulong"> --[=[ - @class longlong + @prop longlong NumCType @within C Compiler defined C `unsigned longlong` type. @@ -665,15 +726,14 @@ export type ulong = NumCType<"ulong"> c.longlong = {} :: longlong export type longlong = NumCType<"longlong"> --[=[ - @class longlong + @prop longlong NumCType @within C Compiler defined C `unsigned longlong` type. ]=] c.ulonglong = {} :: ulonglong export type ulonglong = NumCType<"ulonglong"> - ---#endregion Variable size C-style types -- +--#endregion Variable size C-style types --[=[ @class CTypes @@ -681,7 +741,7 @@ export type ulonglong = NumCType<"ulonglong"> All possible C types. ]=] export type CTypes = - | u8 + u8 | u16 | u32 | u64 @@ -696,8 +756,6 @@ export type CTypes = | usize | isize | char - -- | float - -- | double | uchar | schar | short