diff --git a/crates/lune-std-ffi/src/c/c_fn.rs b/crates/lune-std-ffi/src/c/c_fn.rs index 7f413828..dd6e2fcb 100644 --- a/crates/lune-std-ffi/src/c/c_fn.rs +++ b/crates/lune-std-ffi/src/c/c_fn.rs @@ -5,7 +5,9 @@ use mlua::prelude::*; use super::c_helper::{ get_conv, get_conv_list_from_table, libffi_type_from_userdata, libffi_type_list_from_table, }; -use crate::ffi::NativeConvert; +use crate::ffi::{ + FfiClosure, NativeArgInfo, NativeArgType, NativeConvert, NativeResultInfo, NativeResultType, +}; // cfn is a type declaration for a function. // Basically, when calling an external function, this type declaration @@ -25,21 +27,23 @@ use crate::ffi::NativeConvert; pub struct CFn { cif: *mut ffi_cif, - args_conv: Vec<*const dyn NativeConvert>, - ret_conv: *const dyn NativeConvert, + arg_info_list: Vec, + result_info: NativeResultInfo, } +// support: Cfn as function pointer + impl CFn { pub fn new( args: Vec, ret: Type, - args_conv: Vec<*const dyn NativeConvert>, - ret_conv: *const dyn NativeConvert, + arg_info_list: Vec, + result_info: NativeResultInfo, ) -> Self { Self { cif: Cif::new(args.clone(), ret.clone()).as_raw_ptr(), - args_conv, - ret_conv, + arg_info_list, + result_info, } } @@ -47,17 +51,27 @@ impl CFn { let args_types = libffi_type_list_from_table(lua, &args)?; let ret_type = libffi_type_from_userdata(lua, &ret)?; - Ok(Self::new( - args_types, - ret_type, - unsafe { get_conv_list_from_table(&args)? }, - unsafe { get_conv(&ret)? }, - )) + let len = args.raw_len(); + let mut arg_info_list = Vec::::with_capacity(len); + + for conv in unsafe { get_conv_list_from_table(&args)? } { + arg_info_list.push(NativeArgInfo { conv }) + } + + // get_conv_list_from_table(&args)?.iter().map(|conv| { + // conv.to_owned() + // }).collect() + + Ok(Self::new(args_types, ret_type, unsafe {}, unsafe { + get_conv(&ret)? + })) } } impl LuaUserData for CFn { fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { - // methods.add_method("from", | this, |) + methods.add_method("closure", |lua, this, func: LuaFunction| { + lua.create_userdata(FfiClosure::new(this.cif, userdata)) + }) } } diff --git a/crates/lune-std-ffi/src/c/c_helper.rs b/crates/lune-std-ffi/src/c/c_helper.rs index 3338c727..f4a2e186 100644 --- a/crates/lune-std-ffi/src/c/c_helper.rs +++ b/crates/lune-std-ffi/src/c/c_helper.rs @@ -9,7 +9,9 @@ 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, FFI_STATUS_NAMES}; +use crate::ffi::{ + ffi_association::get_association, NativeConvert, NativeSignedness, NativeSize, FFI_STATUS_NAMES, +}; // Get the NativeConvert handle from the type UserData // this is intended to avoid lookup userdata and lua table every time. (eg: struct) @@ -22,6 +24,7 @@ pub unsafe fn get_conv(userdata: &LuaAnyUserData) -> LuaResult<*const dyn Native unsafe { get_ctype_conv(userdata) } } } + pub unsafe fn get_conv_list_from_table( table: &LuaTable, ) -> LuaResult> { @@ -55,7 +58,7 @@ pub unsafe fn get_conv_list_from_table( // } // } -// get Vec from table(array) of c-types userdata +// get Vec from table(array) of c-type userdata pub fn libffi_type_list_from_table(lua: &Lua, table: &LuaTable) -> LuaResult> { let len: usize = table.raw_len(); let mut fields = Vec::with_capacity(len); @@ -82,9 +85,7 @@ pub fn libffi_type_from_userdata(lua: &Lua, userdata: &LuaAnyUserData) -> LuaRes Ok(userdata.borrow::()?.get_type().to_owned()) } else if let Some(t) = get_association(lua, CTYPE_STATIC, userdata)? { Ok(t.as_userdata() - .ok_or(LuaError::external( - "Failed to get static ctype from userdata", - ))? + .ok_or_else(|| LuaError::external("Failed to get static ctype from userdata"))? .borrow::()? .libffi_type .clone()) @@ -120,9 +121,7 @@ pub fn stringify_userdata(lua: &Lua, userdata: &LuaAnyUserData) -> LuaResult()? .name .unwrap_or("unnamed"), diff --git a/crates/lune-std-ffi/src/ffi/ffi_callable.rs b/crates/lune-std-ffi/src/ffi/ffi_callable.rs new file mode 100644 index 00000000..b9752e8c --- /dev/null +++ b/crates/lune-std-ffi/src/ffi/ffi_callable.rs @@ -0,0 +1,109 @@ +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, +}; +use mlua::prelude::*; + +use super::{ + bit_mask::u8_test_not, ffi_native::NativeArgInfo, FfiRef, FfiRefFlag, GetNativeData, + NativeConvert, NativeData, +}; + +// unsafe extern "C" fn callback() { +// _cif: ffi_cif, +// result: &mut +// } + +// pub type RawCallback = unsafe extern "C" fn(cif: *mut ffi_cif, result: *mut c_void, args: *mut *mut c_void, userdata: *mut c_void); +// pub unsafe extern "C" fn ffi_prep_raw_closure( +// arg1: *mut ffi_raw_closure, +// cif: *mut ffi_cif, +// fun: Option, +// user_data: *mut c_void +// ) -> u32 + +// pub fn ffi_prep_raw_closure_loc( +// arg1: *mut ffi_raw_closure, +// cif: *mut ffi_cif, +// fun: Option< +// unsafe extern "C" fn( +// arg1: *mut ffi_cif, +// arg2: *mut c_void, +// arg3: *mut ffi_raw, +// arg4: *mut c_void, +// ), +// >, +// user_data: *mut c_void, +// codeloc: *mut c_void, +// ) -> ffi_status; + +pub struct FfiCallable { + cif: *mut ffi_cif, + arg_type_list: Vec, + result_size: usize, + code: CodePtr, +} + +impl FfiCallable { + pub unsafe fn new( + cif: *mut ffi_cif, + arg_type_list: Vec, + result_size: usize, + function_ref: FfiRef, + ) -> LuaResult { + if u8_test_not(function_ref.flags, FfiRefFlag::Function.value()) { + return Err(LuaError::external("ref is not function pointer")); + } + Ok(Self { + cif, + arg_type_list, + result_size, + code: CodePtr::from_ptr(function_ref.get_pointer(0).cast::()), + }) + } + + pub unsafe fn call(&self, result: &Ref, args: LuaMultiValue) -> LuaResult<()> { + result + .check_boundary(0, self.result_size) + .then_some(()) + .ok_or_else(|| LuaError::external("result boundary check failed")) + } + // pub fn new_from_lua_table(lua: &Lua, args: LuaTable, ret: LuaAnyUserData) -> LuaResult { + // let args_types = libffi_type_list_from_table(lua, &args)?; + // let ret_type = libffi_type_from_userdata(lua, &ret)?; + + // Ok(Self::new( + // args_types, + // ret_type, + // unsafe { get_conv_list_from_table(&args)? }, + // unsafe { get_conv(&ret)? }, + // )) + // } + // pub fn call() { + + // } +} + +impl LuaUserData for FfiCallable { + fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { + methods.add_method( + "call", + |_lua, this: &FfiCallable, mut args: LuaMultiValue| { + let LuaValue::UserData(result) = args.pop_front().ok_or_else(|| { + LuaError::external("first argument must be result data handle") + })? + else { + return Err(LuaError::external("")); + }; + let call_result = unsafe { this.call(&result.get_data_handle()?, args) }; + call_result + }, + ) + } +} diff --git a/crates/lune-std-ffi/src/ffi/ffi_closure.rs b/crates/lune-std-ffi/src/ffi/ffi_closure.rs new file mode 100644 index 00000000..7afca469 --- /dev/null +++ b/crates/lune-std-ffi/src/ffi/ffi_closure.rs @@ -0,0 +1,106 @@ +use core::ffi::c_void; +use std::ptr; + +use libffi::{ + low::{closure_alloc, closure_free, ffi_cif, CodePtr}, + raw::{ffi_closure, ffi_prep_closure_loc}, +}; +use mlua::prelude::*; + +use super::{ + ffi_ref::{FfiRefBounds, FfiRefFlag}, + FfiRef, FFI_STATUS_NAMES, +}; + +pub struct FfiClosure<'a> { + closure: *mut ffi_closure, + code: CodePtr, + userdata: CallbackUserdata<'a>, +} + +impl<'a> Drop for FfiClosure<'a> { + fn drop(&mut self) { + unsafe { + closure_free(self.closure); + } + } +} + +#[allow(unused)] +pub struct CallbackUserdata<'a> { + pub func: LuaFunction<'a>, + pub lua: &'a Lua, + pub arg_ref_flags: Vec, + pub arg_ref_size: Vec, + pub result_size: usize, +} + +const RESULT_REF_FLAGS: u8 = FfiRefFlag::Leaked.value() | FfiRefFlag::Writable.value(); + +unsafe extern "C" fn callback( + cif: *mut ffi_cif, + result_pointer: *mut c_void, + arg_pointers: *mut *mut c_void, + userdata: *mut c_void, +) { + let userdata = userdata.cast::(); + let len = (*cif).nargs as usize; + let mut args = Vec::::with_capacity(len + 1); + + // Push result pointer (ref) + args.push(LuaValue::UserData( + (*userdata) + .lua + .create_userdata(FfiRef::new( + result_pointer.cast::<()>(), + RESULT_REF_FLAGS, + FfiRefBounds::new(0, (*userdata).result_size), + )) + .unwrap(), + )); + + // Push arg pointer (ref) + for i in 0..len { + args.push(LuaValue::UserData( + (*userdata) + .lua + .create_userdata(FfiRef::new( + (*arg_pointers.add(i)).cast::<()>(), + (*userdata).arg_ref_flags.get(i).unwrap().to_owned(), + FfiRefBounds::new(0, (*userdata).arg_ref_size.get(i).unwrap().to_owned()), + )) + .unwrap(), + )); + } + + (*userdata).func.call::<_, ()>(args).unwrap(); +} + +impl<'a> FfiClosure<'a> { + pub unsafe fn new( + cif: *mut ffi_cif, + userdata: CallbackUserdata<'a>, + ) -> LuaResult> { + let (closure, code) = closure_alloc(); + let prep_result = ffi_prep_closure_loc( + closure, + cif, + Some(callback), + ptr::from_ref(&userdata).cast::().cast_mut(), + code.as_mut_ptr(), + ); + + if prep_result != 0 { + Err(LuaError::external(format!( + "ffi_get_struct_offsets failed. expected result {}, got {}", + FFI_STATUS_NAMES[0], FFI_STATUS_NAMES[prep_result as usize] + ))) + } else { + Ok(FfiClosure { + closure, + code, + userdata, + }) + } + } +} diff --git a/crates/lune-std-ffi/src/ffi/ffi_native/arg.rs b/crates/lune-std-ffi/src/ffi/ffi_native/arg.rs new file mode 100644 index 00000000..c324f2dd --- /dev/null +++ b/crates/lune-std-ffi/src/ffi/ffi_native/arg.rs @@ -0,0 +1,15 @@ +use super::NativeConvert; + +pub struct FfiArgRefOption { + pub flag: u8, +} + +pub enum NativeArgType { + FfiBox, + FfiRef(FfiArgRefOption), +} + +pub struct NativeArgInfo { + pub conv: *const dyn NativeConvert, + pub kind: NativeArgType, +} diff --git a/crates/lune-std-ffi/src/ffi/ffi_native/call.rs b/crates/lune-std-ffi/src/ffi/ffi_native/call.rs deleted file mode 100644 index 164bfbc0..00000000 --- a/crates/lune-std-ffi/src/ffi/ffi_native/call.rs +++ /dev/null @@ -1,19 +0,0 @@ -use std::cell::Ref; - -use mlua::prelude::*; - -use super::NativeData; - -// Handle native data, provide type conversion between luavalue and native types -pub trait NativeCall { - // Call native function - unsafe fn call_native( - &self, - lua: &Lua, - arg: LuaMultiValue, - ret: &Ref, - ) -> LuaResult<()>; - - // Call lua closure - unsafe fn call_lua(&self, lua: &Lua, arg: LuaMultiValue, ret: *mut ()) -> LuaResult<()>; -} 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 4d493e13..2629a8a3 100644 --- a/crates/lune-std-ffi/src/ffi/ffi_native/convert.rs +++ b/crates/lune-std-ffi/src/ffi/ffi_native/convert.rs @@ -4,10 +4,13 @@ use std::cell::Ref; use mlua::prelude::*; -use super::NativeData; +use super::{NativeData, NativeSize}; // Handle native data, provide type conversion between luavalue and native types -pub trait NativeConvert { +pub trait NativeConvert +where + Self: NativeSize, +{ // 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 af7ac725..c835deb0 100644 --- a/crates/lune-std-ffi/src/ffi/ffi_native/mod.rs +++ b/crates/lune-std-ffi/src/ffi/ffi_native/mod.rs @@ -1,7 +1,8 @@ -mod call; +mod arg; mod cast; mod convert; mod data; +mod result; pub trait NativeSize { fn get_size(&self) -> usize; @@ -14,6 +15,7 @@ pub trait NativeSignedness { } pub use self::{ - call::NativeCall, cast::native_num_cast, convert::NativeConvert, data::GetNativeData, - data::NativeData, + arg::FfiArgRefOption, arg::NativeArgInfo, arg::NativeArgType, cast::native_num_cast, + convert::NativeConvert, data::GetNativeData, data::NativeData, result::NativeResultInfo, + result::NativeResultType, }; diff --git a/crates/lune-std-ffi/src/ffi/ffi_native/result.rs b/crates/lune-std-ffi/src/ffi/ffi_native/result.rs new file mode 100644 index 00000000..b25995fb --- /dev/null +++ b/crates/lune-std-ffi/src/ffi/ffi_native/result.rs @@ -0,0 +1,11 @@ +use super::NativeConvert; + +pub enum NativeResultType { + FfiBox, + FfiRef, +} + +pub struct NativeResultInfo { + conv: *const dyn NativeConvert, + kind: NativeResultType, +} diff --git a/crates/lune-std-ffi/src/ffi/ffi_raw.rs b/crates/lune-std-ffi/src/ffi/ffi_raw.rs index 2827ffc0..8e986774 100644 --- a/crates/lune-std-ffi/src/ffi/ffi_raw.rs +++ b/crates/lune-std-ffi/src/ffi/ffi_raw.rs @@ -1,5 +1,4 @@ -// use core::ffi::c_void; -// use std::{convert, mem::transmute, ptr}; +use mlua::prelude::*; // This is raw data coming from outside. // Users must convert it to a Lua value, reference, or box to use it. @@ -11,3 +10,11 @@ // data copy as possible occurs, while allowing you to do little restrictions. pub struct FfiRaw(*const ()); + +impl FfiRaw { + pub fn new(pointer: *const ()) -> Self { + Self(pointer) + } +} + +impl LuaUserData for FfiRaw {} 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 3e3bd004..1df4b2bc 100644 --- a/crates/lune-std-ffi/src/ffi/ffi_ref/bounds.rs +++ b/crates/lune-std-ffi/src/ffi/ffi_ref/bounds.rs @@ -1,3 +1,5 @@ +use std::clone; + // Memory range for ref or box data. For boundary checking pub struct FfiRefBounds { // Indicates how much data is above the pointer @@ -87,3 +89,12 @@ impl FfiRefBounds { } } } + +impl Clone for FfiRefBounds { + fn clone(&self) -> Self { + Self { + above: self.above, + below: self.below, + } + } +} diff --git a/crates/lune-std-ffi/src/ffi/ffi_ref/flag.rs b/crates/lune-std-ffi/src/ffi/ffi_ref/flag.rs index 3271671b..5dd4ec12 100644 --- a/crates/lune-std-ffi/src/ffi/ffi_ref/flag.rs +++ b/crates/lune-std-ffi/src/ffi/ffi_ref/flag.rs @@ -7,6 +7,7 @@ pub enum FfiRefFlag { Writable, Offsetable, Function, + Uninit, } impl FfiRefFlag { pub const fn value(&self) -> u8 { @@ -17,6 +18,7 @@ impl FfiRefFlag { Self::Readable => U8_MASK4, Self::Offsetable => U8_MASK5, Self::Function => U8_MASK6, + Self::Uninit => U8_MASK7, } } } diff --git a/crates/lune-std-ffi/src/ffi/ffi_ref/mod.rs b/crates/lune-std-ffi/src/ffi/ffi_ref/mod.rs index d2332b5e..d0a54042 100644 --- a/crates/lune-std-ffi/src/ffi/ffi_ref/mod.rs +++ b/crates/lune-std-ffi/src/ffi/ffi_ref/mod.rs @@ -41,6 +41,14 @@ impl FfiRef { } } + pub fn new_uninit() -> Self { + Self { + ptr: ptr::null_mut(), + flags: FfiRefFlag::Uninit.value(), + boundary: UNSIZED_BOUNDS, + } + } + // Make FfiRef from ref pub fn luaref<'lua>( lua: &'lua Lua, diff --git a/crates/lune-std-ffi/src/ffi/mod.rs b/crates/lune-std-ffi/src/ffi/mod.rs index 97687e0f..4196bcee 100644 --- a/crates/lune-std-ffi/src/ffi/mod.rs +++ b/crates/lune-std-ffi/src/ffi/mod.rs @@ -1,5 +1,7 @@ pub mod ffi_association; mod ffi_box; +mod ffi_callable; +mod ffi_closure; mod ffi_lib; mod ffi_native; mod ffi_raw; @@ -9,11 +11,16 @@ use mlua::prelude::*; pub use self::{ ffi_box::FfiBox, + ffi_callable::FfiCallable, + ffi_closure::FfiClosure, ffi_lib::FfiLib, ffi_native::{ - native_num_cast, GetNativeData, NativeConvert, NativeData, NativeSignedness, NativeSize, + native_num_cast, FfiArgRefOption, GetNativeData, NativeArgInfo, NativeArgType, + NativeConvert, NativeData, NativeResultInfo, NativeResultType, NativeSignedness, + NativeSize, }, - ffi_ref::{create_nullptr, FfiRef}, + ffi_raw::FfiRaw, + ffi_ref::{create_nullptr, FfiRef, FfiRefFlag}, }; // Named registry table names @@ -41,14 +48,17 @@ pub mod bit_mask { pub const U8_MASK7: u8 = 64; pub const U8_MASK8: u8 = 128; + #[inline] pub fn u8_test(bits: u8, mask: u8) -> bool { bits & mask != 0 } + #[inline] pub fn u8_test_not(bits: u8, mask: u8) -> bool { bits & mask == 0 } + #[inline] pub fn u8_set(bits: u8, mask: u8, val: bool) -> u8 { if val { bits | mask @@ -58,6 +68,7 @@ pub mod bit_mask { } } +#[inline] pub fn is_integer(num: LuaValue) -> bool { num.is_integer() } diff --git a/crates/lune-std-ffi/src/lib.rs b/crates/lune-std-ffi/src/lib.rs index ad1aad06..9df4bd7a 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 ffi::FfiRef; use lune_utils::TableBuilder; use mlua::prelude::*; @@ -23,20 +24,15 @@ pub fn module(lua: &Lua) -> LuaResult { .with_values(create_all_types(lua)?)? .with_values(create_all_c_types(lua)?)? .with_value("nullptr", create_nullptr(lua)?)? - .with_function("box", |_, size: usize| Ok(FfiBox::new(size)))? - // TODO: discuss about function name. matching with io.open is better? - .with_function("open", |_, name: String| { - let lib = FfiLib::new(name)?; - Ok(lib) - })? + .with_function("box", |_lua, size: usize| Ok(FfiBox::new(size)))? + .with_function("open", |_lua, name: String| FfiLib::new(name))? .with_function("struct", |lua, types: LuaTable| { - let cstruct = CStruct::new_from_lua_table(lua, types)?; - Ok(cstruct) + CStruct::new_from_lua_table(lua, types) })? + .with_function("ref", |_lua, ()| Ok(FfiRef::new_uninit()))? .with_function("isInteger", |_lua, num: LuaValue| Ok(is_integer(num)))? .with_function("fn", |lua, (args, ret): (LuaTable, LuaAnyUserData)| { - let cfn = CFn::new_from_lua_table(lua, args, ret)?; - Ok(cfn) + CFn::new_from_lua_table(lua, args, ret) })?; #[cfg(debug_assertions)]