intuicio_ffi/
lib.rs

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    /// # Safety
149    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!(&registry => 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!(&registry => 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!(&registry => 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!(&registry => fn ensure_42(v: i32) -> ()),
288        );
289        context.stack().push(2i32);
290        context.stack().push(40i32);
291        ffi_add.invoke(&mut context, &registry);
292        ffi_ensure.invoke(&mut context, &registry);
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!(&registry => fn add(a: i32, b: i32) -> (result: i32)))
304            .unwrap();
305        let ffi_ensure = lib
306            .function(function_signature!(&registry => 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}