diff --git a/crates/lune-std-ffi/src/c/fn_info.rs b/crates/lune-std-ffi/src/c/fn_info.rs index 3c701a5..1fe4ec7 100644 --- a/crates/lune-std-ffi/src/c/fn_info.rs +++ b/crates/lune-std-ffi/src/c/fn_info.rs @@ -93,6 +93,10 @@ impl CFnInfo { arg_table: LuaTable, ret: LuaAnyUserData, ) -> LuaResult> { + if helper::has_void(&arg_table)? { + return Err(LuaError::external("Arguments can not include void type")); + } + let args_types = helper::get_middle_type_list(&arg_table)?; let ret_type = helper::get_middle_type(&ret)?; diff --git a/crates/lune-std-ffi/src/c/helper.rs b/crates/lune-std-ffi/src/c/helper.rs index 8a22a5b..7f452fe 100644 --- a/crates/lune-std-ffi/src/c/helper.rs +++ b/crates/lune-std-ffi/src/c/helper.rs @@ -211,6 +211,7 @@ pub unsafe fn get_conv(userdata: &LuaAnyUserData) -> LuaResult<*const dyn FfiCon } } +// Create vec from table with (userdata)->T pub fn create_list( table: &LuaTable, callback: fn(&LuaAnyUserData) -> LuaResult, @@ -226,10 +227,12 @@ pub fn create_list( Ok(list) } +//Get pub unsafe fn get_conv_list(table: &LuaTable) -> LuaResult> { create_list(table, |userdata| get_conv(userdata)) } +// Get type size from ctype userdata pub fn get_size(userdata: &LuaAnyUserData) -> LuaResult { if userdata.is::() { Ok(userdata.borrow::()?.get_size()) @@ -244,7 +247,7 @@ pub fn get_size(userdata: &LuaAnyUserData) -> LuaResult { } } -// get libffi_type from any c-type userdata +// Get libffi_type from ctype userdata pub fn get_middle_type(userdata: &LuaAnyUserData) -> LuaResult { if userdata.is::() { Ok(userdata.borrow::()?.get_middle_type()) @@ -274,6 +277,16 @@ pub fn get_middle_type_list(table: &LuaTable) -> LuaResult> { create_list(table, get_middle_type) } +pub fn has_void(table: &LuaTable) -> LuaResult { + for i in 0..table.raw_len() { + let value: LuaValue = table.raw_get(i + 1)?; + if get_userdata(value)?.is::() { + return Ok(false); + } + } + Ok(false) +} + // stringify any c-type userdata (for recursive) pub fn stringify(lua: &Lua, userdata: &LuaAnyUserData) -> LuaResult { if userdata.is::() { diff --git a/crates/lune-std-ffi/src/c/mod.rs b/crates/lune-std-ffi/src/c/mod.rs index c2cdbc3..ffd4562 100644 --- a/crates/lune-std-ffi/src/c/mod.rs +++ b/crates/lune-std-ffi/src/c/mod.rs @@ -15,6 +15,7 @@ pub use self::{ struct_info::CStructInfo, type_info::{CTypeCast, CTypeInfo}, types::{ctype_helper, export_ctypes}, + void_info::CVoidInfo, }; // Named registry table names diff --git a/crates/lune-std-ffi/src/c/struct_info.rs b/crates/lune-std-ffi/src/c/struct_info.rs index 9430a2f..1e52e36 100644 --- a/crates/lune-std-ffi/src/c/struct_info.rs +++ b/crates/lune-std-ffi/src/c/struct_info.rs @@ -65,6 +65,10 @@ impl CStructInfo { lua: &'lua Lua, table: LuaTable<'lua>, ) -> LuaResult> { + if helper::has_void(&table)? { + return Err(LuaError::external("Void field in sturct is not allowed")); + } + let cstruct = lua .create_userdata(Self::new(helper::get_middle_type_list(&table)?, unsafe { helper::get_conv_list(&table)? diff --git a/crates/lune-std-ffi/src/c/void_info.rs b/crates/lune-std-ffi/src/c/void_info.rs index 6d70081..0d36a35 100644 --- a/crates/lune-std-ffi/src/c/void_info.rs +++ b/crates/lune-std-ffi/src/c/void_info.rs @@ -16,6 +16,9 @@ impl FfiSize for CVoidInfo { } } impl CVoidInfo { + pub fn new() -> Self { + Self() + } pub fn get_middle_type() -> Type { Type::void() } diff --git a/crates/lune-std-ffi/src/data/callable_data.rs b/crates/lune-std-ffi/src/data/callable_data.rs index d5c9bd8..8a3f6ac 100644 --- a/crates/lune-std-ffi/src/data/callable_data.rs +++ b/crates/lune-std-ffi/src/data/callable_data.rs @@ -1,5 +1,5 @@ use core::ffi::c_void; -use std::cell::Ref; +use std::ptr; use libffi::{ low::{ffi_cif, CodePtr}, @@ -7,7 +7,7 @@ use libffi::{ }; use mlua::prelude::*; -use super::{FfiData, GetFfiData}; +use super::GetFfiData; use crate::ffi::{FfiArg, FfiResult}; pub struct CallableData { @@ -34,30 +34,38 @@ impl CallableData { // TODO? async call: if have no lua closure in arguments, fficallble can be called with async way - pub unsafe fn call(&self, result: &Ref, args: LuaMultiValue) -> LuaResult<()> { - result - .check_boundary(0, self.result_info.size) - .then_some(()) - .ok_or_else(|| LuaError::external("result boundary check failed"))?; - + pub unsafe fn call(&self, result: LuaValue, args: LuaMultiValue) -> LuaResult<()> { // cache Vec => unable to create async call but no allocation let mut arg_list = Vec::<*mut c_void>::with_capacity(self.arg_info_list.len()); + let result_pointer = if self.result_info.size == 0 { + ptr::null_mut() + } else { + let result_data = result.get_ffi_data()?; + if result_data.check_boundary(0, self.result_info.size) { + return Err(LuaError::external("Result boundary check failed")); + } + result_data.get_pointer() + } + .cast::(); + for index in 0..self.arg_info_list.len() { let arg_info = self.arg_info_list.get(index).unwrap(); let arg = args .get(index) .ok_or_else(|| LuaError::external(format!("argument {index} required")))?; + let arg_pointer = if let LuaValue::UserData(userdata) = arg { + // BoxData, RefData, ... let data_handle = userdata.get_ffi_data()?; - data_handle - .check_boundary(0, arg_info.size) - .then_some(()) - .ok_or_else(|| { - LuaError::external(format!("argument {index} boundary check failed")) - })?; + if !data_handle.check_boundary(0, arg_info.size) { + return Err(LuaError::external(format!( + "argument {index} boundary check failed" + ))); + } data_handle.get_pointer() } else { + // FIXME: buffer, string here return Err(LuaError::external("unimpl")); }; arg_list.push(arg_pointer.cast::()); @@ -66,7 +74,7 @@ impl CallableData { ffi_call( self.cif, Some(*self.code.as_safe_fun()), - result.get_pointer().cast::(), + result_pointer, arg_list.as_mut_ptr(), ); @@ -79,14 +87,11 @@ impl LuaUserData for CallableData { methods.add_method( "call", |_lua, this: &CallableData, mut args: LuaMultiValue| { - let result_userdata = args.pop_front().ok_or_else(|| { - LuaError::external("first argument must be result data handle") + let result = args.pop_front().ok_or_else(|| { + LuaError::external("First argument must be result data handle or nil") })?; - let LuaValue::UserData(result) = result_userdata else { - return Err(LuaError::external("")); - }; // FIXME: clone - unsafe { this.call(&result.clone().get_ffi_data()?, args) } + unsafe { this.call(result, args) } }, ); // ref, leak ..? diff --git a/crates/lune-std-ffi/src/lib.rs b/crates/lune-std-ffi/src/lib.rs index b008522..917602f 100644 --- a/crates/lune-std-ffi/src/lib.rs +++ b/crates/lune-std-ffi/src/lib.rs @@ -1,5 +1,6 @@ #![allow(clippy::cargo_common_metadata)] +use c::CVoidInfo; use data::RefData; use lune_utils::TableBuilder; use mlua::prelude::*; @@ -23,6 +24,7 @@ use crate::{ pub fn module(lua: &Lua) -> LuaResult { let result = TableBuilder::new(lua)? .with_values(export_ctypes(lua)?)? + .with_value("void", CVoidInfo::new())? .with_function("nullRef", |lua, ()| create_nullptr(lua))? .with_function("box", |_lua, size: usize| Ok(BoxData::new(size)))? .with_function("open", |_lua, name: String| LibData::new(name))? diff --git a/tests/ffi/external_print/init.luau b/tests/ffi/external_print/init.luau new file mode 100644 index 0000000..4dad523 --- /dev/null +++ b/tests/ffi/external_print/init.luau @@ -0,0 +1,18 @@ +local ffi = require("@lune/ffi") + +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 add_int = ffi.fnInfo({}, ffi.void) + + local hello_world_caller = add_int:callable(lib:find("hello_world")) + + hello_world_caller:call(nil) +end + +test_hello_world() diff --git a/tests/ffi/external_print/lib.c b/tests/ffi/external_print/lib.c new file mode 100644 index 0000000..d650492 --- /dev/null +++ b/tests/ffi/external_print/lib.c @@ -0,0 +1,5 @@ +#include + +void hello_world() { + printf("Hello world from external function!"); +} diff --git a/types/ffi.luau b/types/ffi.luau index 80927a1..2a7d767 100644 --- a/types/ffi.luau +++ b/types/ffi.luau @@ -61,6 +61,10 @@ export type CStructInfo = { writeData: (self: CStructInfo, target: (Ref|Box), table: { any }, offset: number?) -> (), } +export type CVoidInfo = { + ptrInfo: (self: CVoidInfo) -> CPtrInfo, +} + type NumCType = CTypeInfo -- Fixed size Rust-style types -- @@ -126,6 +130,7 @@ export type CTypes = | CPtrInfo | CFnInfo | CStructInfo + | CVoidInfo export type Ref = { deref: (self: Ref) -> Ref, @@ -147,12 +152,12 @@ export type Lib = { } export type Callable = { - call: (self: Callable, result: (Ref | Box), ...(Ref | Box))->(); + call: (self: Callable, result: (Ref | Box)?, ...(Ref | Box))->(); } local ffi = {} -ffi.u8 = (nil :: unknown) :: u8 +ffi.u8 = {} :: u8 ffi.u16 = {} :: u16 ffi.u32 = {} :: u32 ffi.u64 = {} :: u64 @@ -181,6 +186,8 @@ ffi.ulong = {} :: ulong ffi.longlong = {} :: longlong ffi.ulonglong = {} :: ulonglong +ffi.void = {} :: CVoidInfo + function ffi.nullRef(): Ref return nil :: any end