Skip to content

Commit

Permalink
Implement callable and closure (#243)
Browse files Browse the repository at this point in the history
  • Loading branch information
qwreey committed Oct 13, 2024
1 parent 11bf0b6 commit 4d0fd9d
Show file tree
Hide file tree
Showing 22 changed files with 399 additions and 185 deletions.
50 changes: 32 additions & 18 deletions crates/lune-std-ffi/src/c/c_fn.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use libffi::low::ffi_cif;
use libffi::middle::{Cif, Type};
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
Expand All @@ -23,41 +26,52 @@ use crate::ffi::NativeConvert;
// moved to a Lua function or vice versa.

pub struct CFn {
libffi_cif: Cif,
args_conv: Vec<*const dyn NativeConvert>,
ret_conv: *const dyn NativeConvert,
cif: *mut ffi_cif,
arg_info_list: Vec<NativeArgInfo>,
result_info: NativeResultInfo,
}

// support: Cfn as function pointer

impl CFn {
pub fn new(
args: Vec<Type>,
ret: Type,
args_conv: Vec<*const dyn NativeConvert>,
ret_conv: *const dyn NativeConvert,
arg_info_list: Vec<NativeArgInfo>,
result_info: NativeResultInfo,
) -> Self {
let libffi_cif: Cif = Cif::new(args.clone(), ret.clone());
Self {
libffi_cif,
args_conv,
ret_conv,
cif: Cif::new(args.clone(), ret.clone()).as_raw_ptr(),
arg_info_list,
result_info,
}
}

pub fn new_from_lua_table(lua: &Lua, args: LuaTable, ret: LuaAnyUserData) -> LuaResult<Self> {
let args_type = libffi_type_list_from_table(lua, &args)?;
let args_types = libffi_type_list_from_table(lua, &args)?;
let ret_type = libffi_type_from_userdata(lua, &ret)?;

Ok(Self::new(
args_type,
ret_type,
unsafe { get_conv_list_from_table(&args)? },
unsafe { get_conv(&ret)? },
))
let len = args.raw_len();
let mut arg_info_list = Vec::<NativeArgInfo>::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))
})
}
}
15 changes: 7 additions & 8 deletions crates/lune-std-ffi/src/c/c_helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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<Vec<*const dyn NativeConvert>> {
Expand Down Expand Up @@ -55,7 +58,7 @@ pub unsafe fn get_conv_list_from_table(
// }
// }

// get Vec<libffi_type> from table(array) of c-types userdata
// get Vec<libffi_type> from table(array) of c-type userdata
pub fn libffi_type_list_from_table(lua: &Lua, table: &LuaTable) -> LuaResult<Vec<Type>> {
let len: usize = table.raw_len();
let mut fields = Vec::with_capacity(len);
Expand All @@ -82,9 +85,7 @@ pub fn libffi_type_from_userdata(lua: &Lua, userdata: &LuaAnyUserData) -> LuaRes
Ok(userdata.borrow::<CStruct>()?.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::<CTypeStatic>()?
.libffi_type
.clone())
Expand Down Expand Up @@ -120,9 +121,7 @@ pub fn stringify_userdata(lua: &Lua, userdata: &LuaAnyUserData) -> LuaResult<Str
} else if let Some(t) = get_association(lua, CTYPE_STATIC, userdata)? {
Ok(String::from(
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::<CTypeStatic>()?
.name
.unwrap_or("unnamed"),
Expand Down
4 changes: 2 additions & 2 deletions crates/lune-std-ffi/src/c/c_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ impl LuaUserData for CStruct {
if !data_handle.check_boundary(offset, this.get_size()) {
return Err(LuaError::external("Out of bounds"));
}
if !data_handle.check_readable(offset, this.get_size()) {
if !data_handle.is_readable() {
return Err(LuaError::external("Unreadable data handle"));
}

Expand All @@ -224,7 +224,7 @@ impl LuaUserData for CStruct {
if !data_handle.check_boundary(offset, this.get_size()) {
return Err(LuaError::external("Out of bounds"));
}
if !data_handle.checek_writable(offset, this.get_size()) {
if !data_handle.is_writable() {
return Err(LuaError::external("Unwritable data handle"));
}

Expand Down
4 changes: 2 additions & 2 deletions crates/lune-std-ffi/src/c/c_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ where
if !data_handle.check_boundary(offset, ctype.get_size()) {
return Err(LuaError::external("Out of bounds"));
}
if !data_handle.check_readable(offset, ctype.get_size()) {
if !data_handle.is_readable() {
return Err(LuaError::external("Unreadable data handle"));
}

Expand All @@ -187,7 +187,7 @@ where
if !data_handle.check_boundary(offset, ctype.get_size()) {
return Err(LuaError::external("Out of bounds"));
}
if !data_handle.checek_writable(offset, ctype.get_size()) {
if !data_handle.is_writable() {
return Err(LuaError::external("Unwritable data handle"));
}

Expand Down
2 changes: 0 additions & 2 deletions crates/lune-std-ffi/src/ffi/ffi_box/flag.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
use super::super::bit_mask::*;

pub enum FfiBoxFlag {
Dropped,
Leaked,
}

impl FfiBoxFlag {
pub const fn value(&self) -> u8 {
match self {
Self::Dropped => U8_MASK1,
Self::Leaked => U8_MASK2,
}
}
Expand Down
20 changes: 10 additions & 10 deletions crates/lune-std-ffi/src/ffi/ffi_box/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,17 @@ use super::{
association_names::REF_INNER,
bit_mask::*,
ffi_association::set_association,
ffi_ref::{FfiRef, FfiRefBounds, FfiRefFlag, FfiRefFlagList},
ffi_ref::{FfiRef, FfiRefBounds, FfiRefFlag},
NativeData,
};

mod flag;

pub use self::flag::FfiBoxFlag;

const BOX_REF_FLAGS: FfiRefFlagList = FfiRefFlagList::new(
FfiRefFlag::Offsetable.value() | FfiRefFlag::Readable.value() | FfiRefFlag::Writable.value(),
);
const BOX_MUT_REF_FLAGS: FfiRefFlagList = FfiRefFlagList::new(
FfiRefFlag::Offsetable.value()
| FfiRefFlag::Readable.value()
| FfiRefFlag::Writable.value()
| FfiRefFlag::Mutable.value(),
);
// Ref which created by lua should not be dereferenceable,
const BOX_REF_FLAGS: u8 =
FfiRefFlag::Readable.value() | FfiRefFlag::Writable.value() | FfiRefFlag::Offsetable.value();

// It is an untyped, sized memory area that Lua can manage.
// This area is safe within Lua. Operations have their boundaries checked.
Expand Down Expand Up @@ -143,6 +137,12 @@ impl NativeData for FfiBox {
.cast_mut()
.cast::<()>()
}
fn is_readable(&self) -> bool {
true
}
fn is_writable(&self) -> bool {
true
}
}

impl LuaUserData for FfiBox {
Expand Down
109 changes: 109 additions & 0 deletions crates/lune-std-ffi/src/ffi/ffi_callable.rs
Original file line number Diff line number Diff line change
@@ -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<unsafe extern "C" fn(_: *mut ffi_cif, _: *mut c_void, _: *mut ffi_raw, _: *mut c_void)>,
// 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<NativeArgInfo>,
result_size: usize,
code: CodePtr,
}

impl FfiCallable {
pub unsafe fn new(
cif: *mut ffi_cif,
arg_type_list: Vec<NativeArgInfo>,
result_size: usize,
function_ref: FfiRef,
) -> LuaResult<Self> {
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::<c_void>()),
})
}

pub unsafe fn call(&self, result: &Ref<dyn NativeData>, 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<Self> {
// 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
},
)
}
}
Loading

0 comments on commit 4d0fd9d

Please sign in to comment.