1use intuicio_core::{
2 context::Context,
3 function::{Function, FunctionBody, FunctionQuery, FunctionSignature},
4 types::TypeHandle,
5};
6use libffi::raw::{
7 FFI_TYPE_STRUCT, ffi_abi_FFI_DEFAULT_ABI, ffi_call, ffi_cif, ffi_prep_cif, ffi_type,
8 ffi_type_void,
9};
10use libloading::Library;
11use std::{
12 error::Error,
13 ffi::{OsString, c_void},
14 path::Path,
15 ptr::null_mut,
16 str::FromStr,
17 sync::Arc,
18};
19
20pub use libffi::low::CodePtr as FfiCodePtr;
21
22pub type FfiFunctionHandle = Arc<FfiFunction>;
23
24pub struct FfiLibrary {
25 library: Library,
26 functions: Vec<(FunctionSignature, FfiFunctionHandle)>,
27 name: String,
28}
29
30impl FfiLibrary {
31 pub fn new(path: impl AsRef<Path>) -> Result<Self, Box<dyn Error>> {
32 let mut path = path.as_ref().to_path_buf();
33 if path.extension().is_none() {
34 path.set_extension(std::env::consts::DLL_EXTENSION);
35 }
36 Ok(Self {
37 library: unsafe { Library::new(path.as_os_str())? },
38 functions: Default::default(),
39 name: path.to_string_lossy().to_string(),
40 })
41 }
42
43 pub fn name(&self) -> &str {
44 &self.name
45 }
46
47 pub fn function(
48 &mut self,
49 signature: FunctionSignature,
50 ) -> Result<FfiFunctionHandle, Box<dyn Error>> {
51 unsafe {
52 let symbol = OsString::from_str(&signature.name)?;
53 let symbol = self
54 .library
55 .get::<unsafe extern "C" fn()>(symbol.as_encoded_bytes())?;
56 let Some(function) = symbol.try_as_raw_ptr() else {
57 return Err(format!("Could not get pointer of function: `{signature}`").into());
58 };
59 let handle = Arc::new(FfiFunction::from_function_signature(
60 FfiCodePtr(function),
61 &signature,
62 ));
63 for (s, h) in &mut self.functions {
64 if s == &signature {
65 *h = handle.clone();
66 return Ok(handle);
67 }
68 }
69 self.functions.push((signature, handle.clone()));
70 Ok(handle)
71 }
72 }
73
74 pub fn find(&self, query: FunctionQuery) -> Option<FfiFunctionHandle> {
75 self.functions.iter().find_map(|(signature, handle)| {
76 if query.is_valid(signature) {
77 Some(handle.clone())
78 } else {
79 None
80 }
81 })
82 }
83}
84
85#[derive(Debug, Clone)]
86pub struct FfiFunction {
87 function: FfiCodePtr,
88 result: Option<TypeHandle>,
89 arguments: Vec<TypeHandle>,
90}
91
92unsafe impl Send for FfiFunction {}
93unsafe impl Sync for FfiFunction {}
94
95impl FfiFunction {
96 pub fn from_function_signature(function: FfiCodePtr, signature: &FunctionSignature) -> Self {
97 FfiFunction {
98 function,
99 result: signature
100 .outputs
101 .iter()
102 .find(|param| param.name == "result")
103 .map(|param| param.type_handle.clone()),
104 arguments: signature
105 .inputs
106 .iter()
107 .map(|param| param.type_handle.clone())
108 .collect(),
109 }
110 }
111
112 pub fn build_function(function: FfiCodePtr, signature: FunctionSignature) -> Function {
113 let ffi = Self::from_function_signature(function, &signature);
114 Function::new(
115 signature,
116 FunctionBody::Closure(Arc::new(move |context, _| unsafe {
117 ffi.call(context).expect("FFI call error");
118 })),
119 )
120 }
121
122 pub fn new(function: FfiCodePtr) -> Self {
123 Self {
124 function,
125 result: Default::default(),
126 arguments: Default::default(),
127 }
128 }
129
130 pub fn with_result(mut self, type_: TypeHandle) -> Self {
131 self.result(type_);
132 self
133 }
134
135 pub fn with_argument(mut self, type_: TypeHandle) -> Self {
136 self.argument(type_);
137 self
138 }
139
140 pub fn result(&mut self, type_: TypeHandle) {
141 self.result = Some(type_);
142 }
143
144 pub fn argument(&mut self, type_: TypeHandle) {
145 self.arguments.push(type_);
146 }
147
148 pub unsafe fn call(&self, context: &mut Context) -> Result<(), Box<dyn Error>> {
150 let mut arguments_data = self
151 .arguments
152 .iter()
153 .map(|type_| {
154 if let Some((_, type_hash, _, data)) = unsafe { context.stack().pop_raw() } {
155 if type_hash == type_.type_hash() {
156 Ok(data)
157 } else {
158 Err(
159 format!("Popped value from stack is not `{}` type!", type_.name())
160 .into(),
161 )
162 }
163 } else {
164 Err(format!("Could not pop `{}` type value from stack!", type_.name()).into())
165 }
166 })
167 .collect::<Result<Vec<_>, Box<dyn Error>>>()?;
168 let mut arguments = arguments_data
169 .iter_mut()
170 .map(|data| data.as_mut_ptr() as *mut c_void)
171 .collect::<Vec<_>>();
172 let mut types = Vec::with_capacity(self.arguments.len() + 1);
173 types.push(
174 self.result
175 .as_ref()
176 .map(Self::make_type)
177 .unwrap_or(unsafe { ffi_type_void }),
178 );
179 for type_ in &self.arguments {
180 types.push(Self::make_type(type_));
181 }
182 let return_type = &mut types[0] as *mut _;
183 let mut argument_types = types[1..]
184 .iter_mut()
185 .map(|type_| type_ as *mut _)
186 .collect::<Vec<_>>();
187 let mut cif = ffi_cif::default();
188 unsafe {
189 ffi_prep_cif(
190 &mut cif as *mut _,
191 ffi_abi_FFI_DEFAULT_ABI,
192 arguments_data.len() as _,
193 return_type,
194 argument_types.as_mut_ptr(),
195 )
196 };
197 let mut result = vec![0u8; unsafe { return_type.as_ref() }.unwrap().size];
198 unsafe {
199 ffi_call(
200 &mut cif as *mut _,
201 Some(*self.function.as_safe_fun()),
202 result.as_mut_ptr() as *mut _,
203 arguments.as_mut_ptr(),
204 )
205 };
206 if let Some(type_) = self.result.as_ref() {
207 unsafe {
208 context.stack().push_raw(
209 *type_.layout(),
210 type_.type_hash(),
211 type_.finalizer(),
212 &result,
213 )
214 };
215 }
216 Ok(())
217 }
218
219 fn make_type(type_: &TypeHandle) -> ffi_type {
220 let layout = type_.layout();
221 ffi_type {
222 size: layout.size(),
223 alignment: layout.align() as _,
224 type_: FFI_TYPE_STRUCT as _,
225 elements: null_mut(),
226 }
227 }
228}
229
230#[cfg(test)]
231mod tests {
232 use super::*;
233 use intuicio_core::{function_signature, registry::Registry, types::TypeQuery};
234
235 extern "C" fn add(a: i32, b: i32) -> i32 {
236 a + b
237 }
238
239 extern "C" fn ensure_42(v: i32) {
240 assert_eq!(v, 42);
241 }
242
243 fn is_async<T: Send + Sync>() {}
244
245 #[test]
246 fn test_ffi_function() {
247 is_async::<FfiFunction>();
248
249 let registry = Registry::default().with_basic_types();
250 let mut context = Context::new(10240, 10240);
251
252 let i32_type = registry.find_type(TypeQuery::of::<i32>()).unwrap();
253 let ffi_add = FfiFunction::new(FfiCodePtr(add as *mut _))
254 .with_argument(i32_type.clone())
255 .with_argument(i32_type.clone())
256 .with_result(i32_type.clone());
257 let ffi_ensure =
258 FfiFunction::new(FfiCodePtr(ensure_42 as *mut _)).with_argument(i32_type.clone());
259 context.stack().push(2i32);
260 context.stack().push(40i32);
261 unsafe {
262 ffi_add.call(&mut context).unwrap();
263 ffi_ensure.call(&mut context).unwrap();
264 }
265
266 let ffi_add = FfiFunction::from_function_signature(
267 FfiCodePtr(add as *mut _),
268 &function_signature!(®istry => fn add(a: i32, b: i32) -> (result: i32)),
269 );
270 let ffi_ensure = FfiFunction::from_function_signature(
271 FfiCodePtr(ensure_42 as *mut _),
272 &function_signature!(®istry => fn ensure_42(v: i32) -> ()),
273 );
274 context.stack().push(2i32);
275 context.stack().push(40i32);
276 unsafe {
277 ffi_add.call(&mut context).unwrap();
278 ffi_ensure.call(&mut context).unwrap();
279 }
280
281 let ffi_add = FfiFunction::build_function(
282 FfiCodePtr(add as *mut _),
283 function_signature!(®istry => fn add(a: i32, b: i32) -> (result: i32)),
284 );
285 let ffi_ensure = FfiFunction::build_function(
286 FfiCodePtr(ensure_42 as *mut _),
287 function_signature!(®istry => fn ensure_42(v: i32) -> ()),
288 );
289 context.stack().push(2i32);
290 context.stack().push(40i32);
291 ffi_add.invoke(&mut context, ®istry);
292 ffi_ensure.invoke(&mut context, ®istry);
293 }
294
295 #[test]
296 fn test_ffi_library() {
297 is_async::<FfiLibrary>();
298
299 let registry = Registry::default().with_basic_types();
300 let mut context = Context::new(10240, 10240);
301 let mut lib = FfiLibrary::new("../../target/debug/ffi").unwrap();
302 let ffi_add = lib
303 .function(function_signature!(®istry => fn add(a: i32, b: i32) -> (result: i32)))
304 .unwrap();
305 let ffi_ensure = lib
306 .function(function_signature!(®istry => fn ensure_42(v: i32) -> ()))
307 .unwrap();
308 context.stack().push(2i32);
309 context.stack().push(40i32);
310 unsafe {
311 ffi_add.call(&mut context).unwrap();
312 ffi_ensure.call(&mut context).unwrap();
313 }
314 }
315}