From 46dd185c6fd49ae014e0bb06b9eb09a049e9ef1d Mon Sep 17 00:00:00 2001 From: qwreey Date: Mon, 14 Oct 2024 09:37:21 +0000 Subject: [PATCH] Implement call (#243) --- .gitignore | 5 ++ crates/lune-std-ffi/src/c/c_arr.rs | 3 +- crates/lune-std-ffi/src/c/c_fn.rs | 54 ++++++------- crates/lune-std-ffi/src/c/c_helper.rs | 75 +++++++------------ crates/lune-std-ffi/src/c/c_string.rs | 2 + crates/lune-std-ffi/src/c/c_type.rs | 5 +- crates/lune-std-ffi/src/c/types/mod.rs | 17 ++++- crates/lune-std-ffi/src/ffi/ffi_box/mod.rs | 2 +- crates/lune-std-ffi/src/ffi/ffi_callable.rs | 55 ++++++++++---- crates/lune-std-ffi/src/ffi/ffi_lib.rs | 16 ++-- crates/lune-std-ffi/src/ffi/ffi_native/arg.rs | 1 + .../src/ffi/ffi_native/convert.rs | 7 +- crates/lune-std-ffi/src/ffi/ffi_native/mod.rs | 2 - .../lune-std-ffi/src/ffi/ffi_native/result.rs | 1 + crates/lune-std-ffi/src/ffi/ffi_ref/bounds.rs | 2 - crates/lune-std-ffi/src/ffi/mod.rs | 5 +- crates/lune-std-ffi/src/lib.rs | 1 + crates/lune-std-ffi/src/libffi_helper.rs | 29 +++++++ tests/ffi/external_math/init.luau | 42 +++++++++++ tests/ffi/external_math/lib.c | 7 ++ tests/ffi/external_struct/init.luau | 30 ++++++++ tests/ffi/external_struct/lib.c | 14 ++++ tests/ffi/utility/compile.luau | 9 +++ 23 files changed, 271 insertions(+), 113 deletions(-) create mode 100644 crates/lune-std-ffi/src/libffi_helper.rs create mode 100644 tests/ffi/external_math/init.luau create mode 100644 tests/ffi/external_math/lib.c create mode 100644 tests/ffi/external_struct/init.luau create mode 100644 tests/ffi/external_struct/lib.c create mode 100644 tests/ffi/utility/compile.luau diff --git a/.gitignore b/.gitignore index 6f7b83e..ba748c2 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,11 @@ luneDocs.json luneTypes.d.luau # Files generated by runtime or build scripts + scripts/brick_color.rs scripts/font_enum_map.rs scripts/physical_properties_enum_map.rs + +# Files generated by tests + +/tests/ffi/**/*.so diff --git a/crates/lune-std-ffi/src/c/c_arr.rs b/crates/lune-std-ffi/src/c/c_arr.rs index fc7ddf8..6aa9caa 100644 --- a/crates/lune-std-ffi/src/c/c_arr.rs +++ b/crates/lune-std-ffi/src/c/c_arr.rs @@ -5,13 +5,14 @@ use mlua::prelude::*; use super::{ association_names::CARR_INNER, - c_helper::{get_conv, get_ensured_size, libffi_type_from_userdata, pretty_format_userdata}, + c_helper::{get_conv, libffi_type_from_userdata, pretty_format_userdata}, CPtr, }; use crate::ffi::{ ffi_association::{get_association, set_association}, FfiBox, GetNativeData, NativeConvert, NativeData, NativeSize, }; +use crate::libffi_helper::get_ensured_size; // This is a series of some type. // It provides the final size and the offset of the index, diff --git a/crates/lune-std-ffi/src/c/c_fn.rs b/crates/lune-std-ffi/src/c/c_fn.rs index d6cfbbf..ef1bc0d 100644 --- a/crates/lune-std-ffi/src/c/c_fn.rs +++ b/crates/lune-std-ffi/src/c/c_fn.rs @@ -1,20 +1,17 @@ use std::ptr; -use libffi::low::ffi_cif; use libffi::middle::{Cif, Type}; use mlua::prelude::*; +use super::c_helper::{get_size, get_userdata}; use super::{ association_names::{CALLABLE_CFN, CALLABLE_REF, CFN_ARGS, CFN_RESULT}, - c_helper::{ - get_conv, get_conv_list_from_table, libffi_type_from_userdata, libffi_type_list_from_table, - }, + c_helper::{get_conv, libffi_type_from_userdata, libffi_type_list_from_table}, }; -use crate::ffi::bit_mask::u8_test_not; use crate::ffi::{ - ffi_association::set_association, FfiClosure, NativeArgInfo, NativeConvert, NativeResultInfo, + bit_mask::u8_test_not, ffi_association::set_association, FfiCallable, FfiRef, FfiRefFlag, + NativeArgInfo, NativeData, NativeResultInfo, }; -use crate::ffi::{FfiCallable, FfiRef, FfiRefFlag, NativeData}; // cfn is a type declaration for a function. // Basically, when calling an external function, this type declaration @@ -33,7 +30,7 @@ use crate::ffi::{FfiCallable, FfiRef, FfiRefFlag, NativeData}; // moved to a Lua function or vice versa. pub struct CFn { - cif: *mut ffi_cif, + cif: Cif, arg_info_list: Vec, result_info: NativeResultInfo, } @@ -46,37 +43,44 @@ impl CFn { ret: Type, arg_info_list: Vec, result_info: NativeResultInfo, - ) -> Self { - Self { - cif: Cif::new(args.clone(), ret.clone()).as_raw_ptr(), + ) -> LuaResult { + // let cif = ; + + Ok(Self { + cif: Cif::new(args.clone(), ret.clone()), arg_info_list, result_info, - } + }) } pub fn new_from_lua_table<'lua>( lua: &'lua Lua, - args: LuaTable, + arg_table: LuaTable, ret: LuaAnyUserData, ) -> LuaResult> { - let args_types = libffi_type_list_from_table(lua, &args)?; + let args_types = libffi_type_list_from_table(lua, &arg_table)?; let ret_type = libffi_type_from_userdata(lua, &ret)?; - let mut arg_info_list = Vec::::with_capacity(args.raw_len()); - for conv in unsafe { get_conv_list_from_table(&args)? } { - arg_info_list.push(NativeArgInfo { conv }) + let arg_len = arg_table.raw_len(); + let mut arg_info_list = Vec::::with_capacity(arg_len); + for index in 0..arg_len { + let userdata = get_userdata(arg_table.raw_get(index + 1)?)?; + arg_info_list.push(NativeArgInfo { + conv: unsafe { get_conv(&userdata)? }, + size: get_size(&userdata)?, + }); } - let result_info = NativeResultInfo { conv: unsafe { get_conv(&ret)? }, + size: get_size(&ret)?, }; let cfn = - lua.create_userdata(Self::new(args_types, ret_type, arg_info_list, result_info))?; + lua.create_userdata(Self::new(args_types, ret_type, arg_info_list, result_info)?)?; // Create association to hold argument and result type - set_association(lua, CFN_ARGS, &cfn, args)?; - set_association(lua, CFN_ARGS, &cfn, ret)?; + set_association(lua, CFN_ARGS, &cfn, arg_table)?; + set_association(lua, CFN_RESULT, &cfn, ret)?; Ok(cfn) } @@ -88,22 +92,22 @@ impl LuaUserData for CFn { // lua.create_userdata(FfiClosure::new(this.cif, userdata)) // }) methods.add_function( - "func", + "caller", |lua, (cfn, function_ref): (LuaAnyUserData, LuaAnyUserData)| { let this = cfn.borrow::()?; if !function_ref.is::() { - return Err(LuaError::external("")); + return Err(LuaError::external("argument 0 must be ffiref")); } let ffi_ref = function_ref.borrow::()?; if u8_test_not(ffi_ref.flags, FfiRefFlag::Function.value()) { - return Err(LuaError::external("")); + return Err(LuaError::external("not a function ref")); } let callable = lua.create_userdata(unsafe { FfiCallable::new( - this.cif, + this.cif.as_raw_ptr(), ptr::from_ref(&this.arg_info_list), ptr::from_ref(&this.result_info), ffi_ref.get_pointer(0), diff --git a/crates/lune-std-ffi/src/c/c_helper.rs b/crates/lune-std-ffi/src/c/c_helper.rs index f4a2e18..b7c8478 100644 --- a/crates/lune-std-ffi/src/c/c_helper.rs +++ b/crates/lune-std-ffi/src/c/c_helper.rs @@ -1,17 +1,26 @@ #![allow(clippy::inline_always)] -use std::ptr::{self, null_mut}; - -use libffi::{low, middle::Type, raw}; +use libffi::middle::Type; use lune_utils::fmt::{pretty_format_value, ValueFormatConfig}; use mlua::prelude::*; use super::{ - association_names::CTYPE_STATIC, types::get_ctype_conv, CArr, CPtr, CStruct, CTypeStatic, -}; -use crate::ffi::{ - ffi_association::get_association, NativeConvert, NativeSignedness, NativeSize, FFI_STATUS_NAMES, + association_names::CTYPE_STATIC, + types::{get_ctype_conv, get_ctype_size}, + CArr, CPtr, CStruct, CTypeStatic, }; +use crate::ffi::{ffi_association::get_association, NativeConvert, NativeSize}; + +pub fn get_userdata(value: LuaValue) -> LuaResult { + if let LuaValue::UserData(field_type) = value { + Ok(field_type) + } else { + Err(LuaError::external(format!( + "Unexpected field. CStruct, CType or CArr is required for element but got {}", + pretty_format_value(&value, &ValueFormatConfig::new()) + ))) + } +} // Get the NativeConvert handle from the type UserData // this is intended to avoid lookup userdata and lua table every time. (eg: struct) @@ -33,30 +42,21 @@ pub unsafe fn get_conv_list_from_table( for i in 0..len { let value: LuaValue = table.raw_get(i + 1)?; - - if let LuaValue::UserData(field_type) = value { - conv_list.push(get_conv(&field_type)?); - } else { - return Err(LuaError::external(format!( - "Unexpected field. CStruct, CType or CArr is required for element but got {}", - pretty_format_value(&value, &ValueFormatConfig::new()) - ))); - } + conv_list.push(get_conv(&get_userdata(value)?)?); } Ok(conv_list) } -// #[inline(always)] -// pub fn type_size_from_userdata(this: &LuaAnyUserData) -> LuaResult { -// if this.is::() { -// Ok(this.borrow::()?.get_size()) -// } else if this.is::() { -// Ok(this.borrow::()?.get_size()) -// } else { -// ctype_size_from_userdata(this) -// } -// } +pub fn get_size(this: &LuaAnyUserData) -> LuaResult { + if this.is::() { + Ok(this.borrow::()?.get_size()) + } else if this.is::() { + Ok(this.borrow::()?.get_size()) + } else { + get_ctype_size(this) + } +} // get Vec from table(array) of c-type userdata pub fn libffi_type_list_from_table(lua: &Lua, table: &LuaTable) -> LuaResult> { @@ -162,26 +162,3 @@ pub fn pretty_format_userdata(lua: &Lua, userdata: &LuaAnyUserData) -> LuaResult )) } } - -// Ensure sizeof c-type (raw::libffi_type) -// See: http://www.chiark.greenend.org.uk/doc/libffi-dev/html/Size-and-Alignment.html -pub fn get_ensured_size(ffi_type: *mut raw::ffi_type) -> LuaResult { - let mut cif = low::ffi_cif::default(); - let result = unsafe { - raw::ffi_prep_cif( - ptr::from_mut(&mut cif), - raw::ffi_abi_FFI_DEFAULT_ABI, - 0, - ffi_type, - null_mut(), - ) - }; - - if result != raw::ffi_status_FFI_OK { - return Err(LuaError::external(format!( - "ffi_get_struct_offsets failed. expected result {}, got {}", - FFI_STATUS_NAMES[0], FFI_STATUS_NAMES[result as usize] - ))); - } - unsafe { Ok((*ffi_type).size) } -} diff --git a/crates/lune-std-ffi/src/c/c_string.rs b/crates/lune-std-ffi/src/c/c_string.rs index cf79b48..9c3d777 100644 --- a/crates/lune-std-ffi/src/c/c_string.rs +++ b/crates/lune-std-ffi/src/c/c_string.rs @@ -4,3 +4,5 @@ // but separated it for clarity. // This also allows operations such as ffi.string:intoBox(). // (Write a string to an already existing box) + +// FIXME: use buffer instead? diff --git a/crates/lune-std-ffi/src/c/c_type.rs b/crates/lune-std-ffi/src/c/c_type.rs index 98ab565..3738f04 100644 --- a/crates/lune-std-ffi/src/c/c_type.rs +++ b/crates/lune-std-ffi/src/c/c_type.rs @@ -7,11 +7,12 @@ use lune_utils::fmt::{pretty_format_value, ValueFormatConfig}; use mlua::prelude::*; use num::cast::AsPrimitive; -use super::{association_names::CTYPE_STATIC, c_helper::get_ensured_size, CArr, CPtr}; +use super::{association_names::CTYPE_STATIC, CArr, CPtr}; use crate::ffi::{ ffi_association::set_association, native_num_cast, FfiBox, GetNativeData, NativeConvert, NativeData, NativeSignedness, NativeSize, }; +use crate::libffi_helper::get_ensured_size; // We can't get a CType through mlua, something like // .is::> will fail. @@ -106,11 +107,9 @@ where libffi_type: Type, name: Option<&'static str>, ) -> LuaResult> { - // let libffi_cfi = Cif::new(vec![libffi_type.clone()], Type::void()); let size = get_ensured_size(libffi_type.as_raw_ptr())?; let ctype = Self { - // libffi_cif: libffi_cfi, libffi_type, size, name, diff --git a/crates/lune-std-ffi/src/c/types/mod.rs b/crates/lune-std-ffi/src/c/types/mod.rs index 3beb647..7e116e4 100644 --- a/crates/lune-std-ffi/src/c/types/mod.rs +++ b/crates/lune-std-ffi/src/c/types/mod.rs @@ -8,7 +8,7 @@ use mlua::prelude::*; use num::cast::AsPrimitive; use super::{CType, CTypeCast}; -use crate::ffi::{NativeConvert, NativeData}; +use crate::ffi::{NativeConvert, NativeData, NativeSize}; pub mod f32; pub mod f64; @@ -145,3 +145,18 @@ macro_rules! define_get_ctype_conv { pub unsafe fn get_ctype_conv(userdata: &LuaAnyUserData) -> LuaResult<*const dyn NativeConvert> { define_get_ctype_conv!(userdata, u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, f32, f64) } + +macro_rules! define_get_ctype_size { + ($userdata:ident, $f:ty, $( $c:ty ),*) => { + if $userdata.is::>() { + Ok($userdata.borrow::>()?.get_size()) + }$( else if $userdata.is::>() { + Ok($userdata.borrow::>()?.get_size()) + })* else { + Err(LuaError::external("Unexpected type")) + } + }; +} +pub fn get_ctype_size(userdata: &LuaAnyUserData) -> LuaResult { + define_get_ctype_size!(userdata, u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, f32, f64) +} diff --git a/crates/lune-std-ffi/src/ffi/ffi_box/mod.rs b/crates/lune-std-ffi/src/ffi/ffi_box/mod.rs index 9832455..18815c5 100644 --- a/crates/lune-std-ffi/src/ffi/ffi_box/mod.rs +++ b/crates/lune-std-ffi/src/ffi/ffi_box/mod.rs @@ -128,7 +128,7 @@ impl NativeData for FfiBox { if offset < 0 { return false; } - self.size() > ((offset as usize) + size) + self.size() - (offset as usize) >= size } unsafe fn get_pointer(&self, offset: isize) -> *mut () { self.data diff --git a/crates/lune-std-ffi/src/ffi/ffi_callable.rs b/crates/lune-std-ffi/src/ffi/ffi_callable.rs index abf94d2..829b0a2 100644 --- a/crates/lune-std-ffi/src/ffi/ffi_callable.rs +++ b/crates/lune-std-ffi/src/ffi/ffi_callable.rs @@ -1,12 +1,9 @@ use core::ffi::c_void; use std::cell::Ref; -// use std::ptr; use libffi::{ - // low::{closure_alloc, ffi_cif, CodePtr, RawCallback}, low::{ffi_cif, CodePtr}, - // middle::Cif, - // raw::ffi_prep_closure_loc, + raw::ffi_call, }; use mlua::prelude::*; @@ -14,37 +11,64 @@ use super::{GetNativeData, NativeArgInfo, NativeData, NativeResultInfo}; pub struct FfiCallable { cif: *mut ffi_cif, - arg_info: *const Vec, + arg_info_list: *const Vec, result_info: *const NativeResultInfo, code: CodePtr, - - // Caching for better performance - result_size: usize, } impl FfiCallable { pub unsafe fn new( cif: *mut ffi_cif, - arg_info: *const Vec, + arg_info_list: *const Vec, result_info: *const NativeResultInfo, function_pointer: *const (), ) -> Self { - let result_size = (*(*result_info).conv).get_size(); Self { cif, - arg_info, + arg_info_list, result_info, code: CodePtr::from_ptr(function_pointer.cast::()), - - result_size, } } pub unsafe fn call(&self, result: &Ref, args: LuaMultiValue) -> LuaResult<()> { result - .check_boundary(0, self.result_size) + .check_boundary(0, self.result_info.as_ref().unwrap().size) .then_some(()) - .ok_or_else(|| LuaError::external("result boundary check failed")) + .ok_or_else(|| LuaError::external("result boundary check failed"))?; + + // cache Vec => unable to create async call but no allocation + let arg_info_list = self.arg_info_list.as_ref().unwrap(); + let mut arg_list = Vec::<*mut c_void>::with_capacity(arg_info_list.len()); + + for index in 0..arg_info_list.len() { + let arg_info = 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 { + let data_handle = userdata.get_data_handle()?; + data_handle + .check_boundary(0, arg_info.size) + .then_some(()) + .ok_or_else(|| { + LuaError::external(format!("argument {index} boundary check failed")) + })?; + data_handle.get_pointer(0) + } else { + return Err(LuaError::external("unimpl")); + }; + arg_list.push(arg_pointer.cast::()); + } + + ffi_call( + self.cif, + Some(*self.code.as_safe_fun()), + result.get_pointer(0).cast::(), + arg_list.as_mut_ptr(), + ); + + Ok(()) } } @@ -63,5 +87,6 @@ impl LuaUserData for FfiCallable { unsafe { this.call(&result.clone().get_data_handle()?, args) } }, ); + // ref, leak ..? } } diff --git a/crates/lune-std-ffi/src/ffi/ffi_lib.rs b/crates/lune-std-ffi/src/ffi/ffi_lib.rs index 8bc0524..87d81bd 100644 --- a/crates/lune-std-ffi/src/ffi/ffi_lib.rs +++ b/crates/lune-std-ffi/src/ffi/ffi_lib.rs @@ -1,6 +1,6 @@ use core::ffi::c_void; -use dlopen2::symbor::Library; +use dlopen2::raw::Library; use mlua::prelude::*; use super::{ @@ -41,12 +41,17 @@ impl FfiLib { let lib = this.borrow::()?; let sym = unsafe { lib.0 - .symbol::<*mut c_void>(name.as_str()) + .symbol::<*const c_void>(name.as_str()) .map_err(|err| LuaError::external(format!("{err}")))? }; + let ptr = sym.cast::<()>().cast_mut(); - let luasym = - lua.create_userdata(FfiRef::new((*sym).cast(), LIB_REF_FLAGS, UNSIZED_BOUNDS))?; + // unsafe { + // let f = transmute::<*mut (), unsafe extern "C" fn(i32, i32) -> i32>(ptr); + // dbg!(f(1, 2)); + // } + + let luasym = lua.create_userdata(FfiRef::new(ptr, LIB_REF_FLAGS, UNSIZED_BOUNDS))?; set_association(lua, SYM_INNER, &luasym, &this)?; @@ -57,8 +62,7 @@ impl FfiLib { impl LuaUserData for FfiLib { fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { methods.add_function("find", |lua, (this, name): (LuaAnyUserData, String)| { - let luasym = FfiLib::get_sym(lua, this, name)?; - Ok(luasym) + FfiLib::get_sym(lua, this, name) }); } } diff --git a/crates/lune-std-ffi/src/ffi/ffi_native/arg.rs b/crates/lune-std-ffi/src/ffi/ffi_native/arg.rs index 108495d..9eb06a0 100644 --- a/crates/lune-std-ffi/src/ffi/ffi_native/arg.rs +++ b/crates/lune-std-ffi/src/ffi/ffi_native/arg.rs @@ -11,5 +11,6 @@ pub enum NativeArgType { pub struct NativeArgInfo { pub conv: *const dyn NativeConvert, + pub size: usize, // pub kind: NativeArgType, } diff --git a/crates/lune-std-ffi/src/ffi/ffi_native/convert.rs b/crates/lune-std-ffi/src/ffi/ffi_native/convert.rs index 2629a8a..4d493e1 100644 --- a/crates/lune-std-ffi/src/ffi/ffi_native/convert.rs +++ b/crates/lune-std-ffi/src/ffi/ffi_native/convert.rs @@ -4,13 +4,10 @@ use std::cell::Ref; use mlua::prelude::*; -use super::{NativeData, NativeSize}; +use super::NativeData; // Handle native data, provide type conversion between luavalue and native types -pub trait NativeConvert -where - Self: NativeSize, -{ +pub trait NativeConvert { // Convert luavalue into data, then write into ptr unsafe fn luavalue_into<'lua>( &self, diff --git a/crates/lune-std-ffi/src/ffi/ffi_native/mod.rs b/crates/lune-std-ffi/src/ffi/ffi_native/mod.rs index a089a8b..4ac89ba 100644 --- a/crates/lune-std-ffi/src/ffi/ffi_native/mod.rs +++ b/crates/lune-std-ffi/src/ffi/ffi_native/mod.rs @@ -15,9 +15,7 @@ pub trait NativeSignedness { } pub use self::{ - arg::FfiArgRefOption, arg::NativeArgInfo, - arg::NativeArgType, cast::native_num_cast, convert::NativeConvert, data::GetNativeData, diff --git a/crates/lune-std-ffi/src/ffi/ffi_native/result.rs b/crates/lune-std-ffi/src/ffi/ffi_native/result.rs index 0a34f52..588aae7 100644 --- a/crates/lune-std-ffi/src/ffi/ffi_native/result.rs +++ b/crates/lune-std-ffi/src/ffi/ffi_native/result.rs @@ -7,5 +7,6 @@ use super::NativeConvert; pub struct NativeResultInfo { pub conv: *const dyn NativeConvert, + pub size: usize, // kind: NativeResultType, } diff --git a/crates/lune-std-ffi/src/ffi/ffi_ref/bounds.rs b/crates/lune-std-ffi/src/ffi/ffi_ref/bounds.rs index 1df4b2b..9fd8566 100644 --- a/crates/lune-std-ffi/src/ffi/ffi_ref/bounds.rs +++ b/crates/lune-std-ffi/src/ffi/ffi_ref/bounds.rs @@ -1,5 +1,3 @@ -use std::clone; - // Memory range for ref or box data. For boundary checking pub struct FfiRefBounds { // Indicates how much data is above the pointer diff --git a/crates/lune-std-ffi/src/ffi/mod.rs b/crates/lune-std-ffi/src/ffi/mod.rs index f186e99..50dd8f8 100644 --- a/crates/lune-std-ffi/src/ffi/mod.rs +++ b/crates/lune-std-ffi/src/ffi/mod.rs @@ -15,10 +15,9 @@ pub use self::{ ffi_closure::FfiClosure, ffi_lib::FfiLib, ffi_native::{ - native_num_cast, FfiArgRefOption, GetNativeData, NativeArgInfo, NativeArgType, - NativeConvert, NativeData, NativeResultInfo, NativeSignedness, NativeSize, + native_num_cast, GetNativeData, NativeArgInfo, NativeConvert, NativeData, NativeResultInfo, + NativeSignedness, NativeSize, }, - ffi_raw::FfiRaw, ffi_ref::{create_nullptr, FfiRef, FfiRefFlag}, }; diff --git a/crates/lune-std-ffi/src/lib.rs b/crates/lune-std-ffi/src/lib.rs index 9df4bd7..72bbbdb 100644 --- a/crates/lune-std-ffi/src/lib.rs +++ b/crates/lune-std-ffi/src/lib.rs @@ -6,6 +6,7 @@ use mlua::prelude::*; mod c; mod ffi; +mod libffi_helper; use crate::{ c::{create_all_c_types, create_all_types, CFn, CStruct}, diff --git a/crates/lune-std-ffi/src/libffi_helper.rs b/crates/lune-std-ffi/src/libffi_helper.rs new file mode 100644 index 0000000..37feaa7 --- /dev/null +++ b/crates/lune-std-ffi/src/libffi_helper.rs @@ -0,0 +1,29 @@ +use std::ptr::{self, null_mut}; + +use libffi::{low, raw}; +use mlua::prelude::*; + +use crate::ffi::FFI_STATUS_NAMES; + +// Ensure sizeof c-type (raw::libffi_type) +// See: http://www.chiark.greenend.org.uk/doc/libffi-dev/html/Size-and-Alignment.html +pub fn get_ensured_size(ffi_type: *mut raw::ffi_type) -> LuaResult { + let mut cif = low::ffi_cif::default(); + let result = unsafe { + raw::ffi_prep_cif( + ptr::from_mut(&mut cif), + raw::ffi_abi_FFI_DEFAULT_ABI, + 0, + ffi_type, + null_mut(), + ) + }; + + if result != raw::ffi_status_FFI_OK { + return Err(LuaError::external(format!( + "ffi_prep_cif failed. expected result {}, got {}", + FFI_STATUS_NAMES[0], FFI_STATUS_NAMES[result as usize] + ))); + } + unsafe { Ok((*ffi_type).size) } +} diff --git a/tests/ffi/external_math/init.luau b/tests/ffi/external_math/init.luau new file mode 100644 index 0000000..3b76871 --- /dev/null +++ b/tests/ffi/external_math/init.luau @@ -0,0 +1,42 @@ +local ffi = require("@lune/ffi") + +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_add_int() + local add_int = ffi.fn({ ffi.int, ffi.int }, ffi.int) + + local add_int_caller = add_int:caller(lib:find("add_int")) + + local resultBox = ffi.box(ffi.int.size) + local arg1 = ffi.int:box(100) + local arg2 = ffi.int:box(200) + + add_int_caller:call(resultBox, arg1, arg2) + local result = ffi.int:from(resultBox) + + assert(result == 300, `add_int failed. result expected 300, got {result}`) +end + +test_add_int() + +local function test_mul_int() + local mul_int = ffi.fn({ ffi.int, ffi.int }, ffi.int) + + local mul_int_caller = mul_int:caller(lib:find("mul_int")) + + local resultBox = ffi.box(ffi.int.size) + local arg1 = ffi.int:box(100) + local arg2 = ffi.int:box(200) + + mul_int_caller:call(resultBox, arg1, arg2) + local result = ffi.int:from(resultBox) + + assert(result == 20000, `mul_int failed. result expected 20000, got {result}`) +end + +test_mul_int() diff --git a/tests/ffi/external_math/lib.c b/tests/ffi/external_math/lib.c new file mode 100644 index 0000000..b518230 --- /dev/null +++ b/tests/ffi/external_math/lib.c @@ -0,0 +1,7 @@ +int add_int(int a, int b) { + return a + b; +} + +int mul_int(int a, int b) { + return a * b; +} diff --git a/tests/ffi/external_struct/init.luau b/tests/ffi/external_struct/init.luau new file mode 100644 index 0000000..d07cf42 --- /dev/null +++ b/tests/ffi/external_struct/init.luau @@ -0,0 +1,30 @@ +local ffi = require("@lune/ffi") + +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 ArgStruct = ffi.struct({ ffi.int, ffi.int:ptr() }) + local ResultStruct = ffi.struct({ ffi.int, ffi.int }) + + local AB = ffi.fn({ ArgStruct }, ResultStruct) + + local AB_caller = AB:caller(lib:find("AB")) + + local resultBox = ffi.box(ffi.int.size) + local a = ffi.int:box(100) + local b = ffi.int:box(200) + local arg = ArgStruct:box({ a, b:leak() }) + + AB_caller:call(resultBox, arg) + local result = ResultStruct:from(resultBox) + + assert(result[0] == 300, `AB failed. result expected 300, got {result}`) + assert(result[1] == 20000, `AB failed. result expected 300, got {result}`) +end + +test_AB() diff --git a/tests/ffi/external_struct/lib.c b/tests/ffi/external_struct/lib.c new file mode 100644 index 0000000..a9e076b --- /dev/null +++ b/tests/ffi/external_struct/lib.c @@ -0,0 +1,14 @@ +typedef struct { + int a; + int* b; +} ArgStruct; + +typedef struct { + int sum; + int mul; +} ResultStruct; + +ResultStruct AB(ArgStruct t) { + ResultStruct result = { t.a+*t.b, t.a**t.b }; + return result; +} diff --git a/tests/ffi/utility/compile.luau b/tests/ffi/utility/compile.luau new file mode 100644 index 0000000..0b67f4c --- /dev/null +++ b/tests/ffi/utility/compile.luau @@ -0,0 +1,9 @@ +local process = require("@lune/process") +local function compile(file, out) + local gcc = process.spawn("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