Skip to content

Commit 4d0fd9d

Browse files
committed
Implement callable and closure (#243)
1 parent 11bf0b6 commit 4d0fd9d

22 files changed

+399
-185
lines changed

crates/lune-std-ffi/src/c/c_fn.rs

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
use libffi::low::ffi_cif;
12
use libffi::middle::{Cif, Type};
23
use mlua::prelude::*;
34

45
use super::c_helper::{
56
get_conv, get_conv_list_from_table, libffi_type_from_userdata, libffi_type_list_from_table,
67
};
7-
use crate::ffi::NativeConvert;
8+
use crate::ffi::{
9+
FfiClosure, NativeArgInfo, NativeArgType, NativeConvert, NativeResultInfo, NativeResultType,
10+
};
811

912
// cfn is a type declaration for a function.
1013
// Basically, when calling an external function, this type declaration
@@ -23,41 +26,52 @@ use crate::ffi::NativeConvert;
2326
// moved to a Lua function or vice versa.
2427

2528
pub struct CFn {
26-
libffi_cif: Cif,
27-
args_conv: Vec<*const dyn NativeConvert>,
28-
ret_conv: *const dyn NativeConvert,
29+
cif: *mut ffi_cif,
30+
arg_info_list: Vec<NativeArgInfo>,
31+
result_info: NativeResultInfo,
2932
}
3033

34+
// support: Cfn as function pointer
35+
3136
impl CFn {
3237
pub fn new(
3338
args: Vec<Type>,
3439
ret: Type,
35-
args_conv: Vec<*const dyn NativeConvert>,
36-
ret_conv: *const dyn NativeConvert,
40+
arg_info_list: Vec<NativeArgInfo>,
41+
result_info: NativeResultInfo,
3742
) -> Self {
38-
let libffi_cif: Cif = Cif::new(args.clone(), ret.clone());
3943
Self {
40-
libffi_cif,
41-
args_conv,
42-
ret_conv,
44+
cif: Cif::new(args.clone(), ret.clone()).as_raw_ptr(),
45+
arg_info_list,
46+
result_info,
4347
}
4448
}
4549

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

50-
Ok(Self::new(
51-
args_type,
52-
ret_type,
53-
unsafe { get_conv_list_from_table(&args)? },
54-
unsafe { get_conv(&ret)? },
55-
))
54+
let len = args.raw_len();
55+
let mut arg_info_list = Vec::<NativeArgInfo>::with_capacity(len);
56+
57+
for conv in unsafe { get_conv_list_from_table(&args)? } {
58+
arg_info_list.push(NativeArgInfo { conv })
59+
}
60+
61+
// get_conv_list_from_table(&args)?.iter().map(|conv| {
62+
// conv.to_owned()
63+
// }).collect()
64+
65+
Ok(Self::new(args_types, ret_type, unsafe {}, unsafe {
66+
get_conv(&ret)?
67+
}))
5668
}
5769
}
5870

5971
impl LuaUserData for CFn {
6072
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
61-
// methods.add_method("from", | this, |)
73+
methods.add_method("closure", |lua, this, func: LuaFunction| {
74+
lua.create_userdata(FfiClosure::new(this.cif, userdata))
75+
})
6276
}
6377
}

crates/lune-std-ffi/src/c/c_helper.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ use mlua::prelude::*;
99
use super::{
1010
association_names::CTYPE_STATIC, types::get_ctype_conv, CArr, CPtr, CStruct, CTypeStatic,
1111
};
12-
use crate::ffi::{ffi_association::get_association, NativeConvert, FFI_STATUS_NAMES};
12+
use crate::ffi::{
13+
ffi_association::get_association, NativeConvert, NativeSignedness, NativeSize, FFI_STATUS_NAMES,
14+
};
1315

1416
// Get the NativeConvert handle from the type UserData
1517
// 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
2224
unsafe { get_ctype_conv(userdata) }
2325
}
2426
}
27+
2528
pub unsafe fn get_conv_list_from_table(
2629
table: &LuaTable,
2730
) -> LuaResult<Vec<*const dyn NativeConvert>> {
@@ -55,7 +58,7 @@ pub unsafe fn get_conv_list_from_table(
5558
// }
5659
// }
5760

58-
// get Vec<libffi_type> from table(array) of c-types userdata
61+
// get Vec<libffi_type> from table(array) of c-type userdata
5962
pub fn libffi_type_list_from_table(lua: &Lua, table: &LuaTable) -> LuaResult<Vec<Type>> {
6063
let len: usize = table.raw_len();
6164
let mut fields = Vec::with_capacity(len);
@@ -82,9 +85,7 @@ pub fn libffi_type_from_userdata(lua: &Lua, userdata: &LuaAnyUserData) -> LuaRes
8285
Ok(userdata.borrow::<CStruct>()?.get_type().to_owned())
8386
} else if let Some(t) = get_association(lua, CTYPE_STATIC, userdata)? {
8487
Ok(t.as_userdata()
85-
.ok_or(LuaError::external(
86-
"Failed to get static ctype from userdata",
87-
))?
88+
.ok_or_else(|| LuaError::external("Failed to get static ctype from userdata"))?
8889
.borrow::<CTypeStatic>()?
8990
.libffi_type
9091
.clone())
@@ -120,9 +121,7 @@ pub fn stringify_userdata(lua: &Lua, userdata: &LuaAnyUserData) -> LuaResult<Str
120121
} else if let Some(t) = get_association(lua, CTYPE_STATIC, userdata)? {
121122
Ok(String::from(
122123
t.as_userdata()
123-
.ok_or(LuaError::external(
124-
"Failed to get static ctype from userdata",
125-
))?
124+
.ok_or_else(|| LuaError::external("Failed to get static ctype from userdata"))?
126125
.borrow::<CTypeStatic>()?
127126
.name
128127
.unwrap_or("unnamed"),

crates/lune-std-ffi/src/c/c_struct.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ impl LuaUserData for CStruct {
208208
if !data_handle.check_boundary(offset, this.get_size()) {
209209
return Err(LuaError::external("Out of bounds"));
210210
}
211-
if !data_handle.check_readable(offset, this.get_size()) {
211+
if !data_handle.is_readable() {
212212
return Err(LuaError::external("Unreadable data handle"));
213213
}
214214

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

crates/lune-std-ffi/src/c/c_type.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ where
164164
if !data_handle.check_boundary(offset, ctype.get_size()) {
165165
return Err(LuaError::external("Out of bounds"));
166166
}
167-
if !data_handle.check_readable(offset, ctype.get_size()) {
167+
if !data_handle.is_readable() {
168168
return Err(LuaError::external("Unreadable data handle"));
169169
}
170170

@@ -187,7 +187,7 @@ where
187187
if !data_handle.check_boundary(offset, ctype.get_size()) {
188188
return Err(LuaError::external("Out of bounds"));
189189
}
190-
if !data_handle.checek_writable(offset, ctype.get_size()) {
190+
if !data_handle.is_writable() {
191191
return Err(LuaError::external("Unwritable data handle"));
192192
}
193193

crates/lune-std-ffi/src/ffi/ffi_box/flag.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
use super::super::bit_mask::*;
22

33
pub enum FfiBoxFlag {
4-
Dropped,
54
Leaked,
65
}
76

87
impl FfiBoxFlag {
98
pub const fn value(&self) -> u8 {
109
match self {
11-
Self::Dropped => U8_MASK1,
1210
Self::Leaked => U8_MASK2,
1311
}
1412
}

crates/lune-std-ffi/src/ffi/ffi_box/mod.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,17 @@ use super::{
66
association_names::REF_INNER,
77
bit_mask::*,
88
ffi_association::set_association,
9-
ffi_ref::{FfiRef, FfiRefBounds, FfiRefFlag, FfiRefFlagList},
9+
ffi_ref::{FfiRef, FfiRefBounds, FfiRefFlag},
1010
NativeData,
1111
};
1212

1313
mod flag;
1414

1515
pub use self::flag::FfiBoxFlag;
1616

17-
const BOX_REF_FLAGS: FfiRefFlagList = FfiRefFlagList::new(
18-
FfiRefFlag::Offsetable.value() | FfiRefFlag::Readable.value() | FfiRefFlag::Writable.value(),
19-
);
20-
const BOX_MUT_REF_FLAGS: FfiRefFlagList = FfiRefFlagList::new(
21-
FfiRefFlag::Offsetable.value()
22-
| FfiRefFlag::Readable.value()
23-
| FfiRefFlag::Writable.value()
24-
| FfiRefFlag::Mutable.value(),
25-
);
17+
// Ref which created by lua should not be dereferenceable,
18+
const BOX_REF_FLAGS: u8 =
19+
FfiRefFlag::Readable.value() | FfiRefFlag::Writable.value() | FfiRefFlag::Offsetable.value();
2620

2721
// It is an untyped, sized memory area that Lua can manage.
2822
// This area is safe within Lua. Operations have their boundaries checked.
@@ -143,6 +137,12 @@ impl NativeData for FfiBox {
143137
.cast_mut()
144138
.cast::<()>()
145139
}
140+
fn is_readable(&self) -> bool {
141+
true
142+
}
143+
fn is_writable(&self) -> bool {
144+
true
145+
}
146146
}
147147

148148
impl LuaUserData for FfiBox {
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
use core::ffi::c_void;
2+
use std::cell::Ref;
3+
// use std::ptr;
4+
5+
use libffi::{
6+
// low::{closure_alloc, ffi_cif, CodePtr, RawCallback},
7+
low::{ffi_cif, CodePtr},
8+
// middle::Cif,
9+
// raw::ffi_prep_closure_loc,
10+
};
11+
use mlua::prelude::*;
12+
13+
use super::{
14+
bit_mask::u8_test_not, ffi_native::NativeArgInfo, FfiRef, FfiRefFlag, GetNativeData,
15+
NativeConvert, NativeData,
16+
};
17+
18+
// unsafe extern "C" fn callback() {
19+
// _cif: ffi_cif,
20+
// result: &mut
21+
// }
22+
23+
// pub type RawCallback = unsafe extern "C" fn(cif: *mut ffi_cif, result: *mut c_void, args: *mut *mut c_void, userdata: *mut c_void);
24+
// pub unsafe extern "C" fn ffi_prep_raw_closure(
25+
// arg1: *mut ffi_raw_closure,
26+
// cif: *mut ffi_cif,
27+
// fun: Option<unsafe extern "C" fn(_: *mut ffi_cif, _: *mut c_void, _: *mut ffi_raw, _: *mut c_void)>,
28+
// user_data: *mut c_void
29+
// ) -> u32
30+
31+
// pub fn ffi_prep_raw_closure_loc(
32+
// arg1: *mut ffi_raw_closure,
33+
// cif: *mut ffi_cif,
34+
// fun: Option<
35+
// unsafe extern "C" fn(
36+
// arg1: *mut ffi_cif,
37+
// arg2: *mut c_void,
38+
// arg3: *mut ffi_raw,
39+
// arg4: *mut c_void,
40+
// ),
41+
// >,
42+
// user_data: *mut c_void,
43+
// codeloc: *mut c_void,
44+
// ) -> ffi_status;
45+
46+
pub struct FfiCallable {
47+
cif: *mut ffi_cif,
48+
arg_type_list: Vec<NativeArgInfo>,
49+
result_size: usize,
50+
code: CodePtr,
51+
}
52+
53+
impl FfiCallable {
54+
pub unsafe fn new(
55+
cif: *mut ffi_cif,
56+
arg_type_list: Vec<NativeArgInfo>,
57+
result_size: usize,
58+
function_ref: FfiRef,
59+
) -> LuaResult<Self> {
60+
if u8_test_not(function_ref.flags, FfiRefFlag::Function.value()) {
61+
return Err(LuaError::external("ref is not function pointer"));
62+
}
63+
Ok(Self {
64+
cif,
65+
arg_type_list,
66+
result_size,
67+
code: CodePtr::from_ptr(function_ref.get_pointer(0).cast::<c_void>()),
68+
})
69+
}
70+
71+
pub unsafe fn call(&self, result: &Ref<dyn NativeData>, args: LuaMultiValue) -> LuaResult<()> {
72+
result
73+
.check_boundary(0, self.result_size)
74+
.then_some(())
75+
.ok_or_else(|| LuaError::external("result boundary check failed"))
76+
}
77+
// pub fn new_from_lua_table(lua: &Lua, args: LuaTable, ret: LuaAnyUserData) -> LuaResult<Self> {
78+
// let args_types = libffi_type_list_from_table(lua, &args)?;
79+
// let ret_type = libffi_type_from_userdata(lua, &ret)?;
80+
81+
// Ok(Self::new(
82+
// args_types,
83+
// ret_type,
84+
// unsafe { get_conv_list_from_table(&args)? },
85+
// unsafe { get_conv(&ret)? },
86+
// ))
87+
// }
88+
// pub fn call() {
89+
90+
// }
91+
}
92+
93+
impl LuaUserData for FfiCallable {
94+
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
95+
methods.add_method(
96+
"call",
97+
|_lua, this: &FfiCallable, mut args: LuaMultiValue| {
98+
let LuaValue::UserData(result) = args.pop_front().ok_or_else(|| {
99+
LuaError::external("first argument must be result data handle")
100+
})?
101+
else {
102+
return Err(LuaError::external(""));
103+
};
104+
let call_result = unsafe { this.call(&result.get_data_handle()?, args) };
105+
call_result
106+
},
107+
)
108+
}
109+
}

0 commit comments

Comments
 (0)