intuicio_frontend_simpleton/library/
ffi.rs

1use crate::Reference;
2use intuicio_core::{
3    context::Context,
4    function::{Function, FunctionBody, FunctionParameter, FunctionSignature},
5    registry::Registry,
6    types::{struct_type::NativeStructBuilder, TypeHandle},
7    utils::object_pop_from_stack,
8};
9use intuicio_data::type_hash::TypeHash;
10use intuicio_derive::intuicio_function;
11use intuicio_ffi::FfiLibrary;
12use intuicio_framework_dynamic::{Array, Integer, Real, Text, Type};
13use std::{
14    alloc::Layout,
15    ffi::{
16        c_char, c_double, c_float, c_int, c_long, c_longlong, c_short, c_uchar, c_uint, c_ulong,
17        c_ulonglong, c_ushort, c_void, CString,
18    },
19    path::Path,
20    ptr::null_mut,
21    sync::Arc,
22};
23
24#[repr(transparent)]
25#[allow(non_camel_case_types)]
26struct c_string(pub *mut c_char);
27
28impl Default for c_string {
29    fn default() -> Self {
30        Self(null_mut())
31    }
32}
33
34#[repr(transparent)]
35#[allow(non_camel_case_types)]
36struct c_pointer(pub *const c_void);
37
38impl Default for c_pointer {
39    fn default() -> Self {
40        Self(null_mut())
41    }
42}
43
44enum DataType {
45    Void,
46    CShort,
47    CInt,
48    CLong,
49    CLongLong,
50    CUChar,
51    CUShort,
52    CUInt,
53    CULong,
54    CULongLong,
55    CFloat,
56    CDouble,
57    CChar,
58    CString,
59    Pointer,
60    Value(TypeHandle),
61}
62
63impl DataType {
64    fn from_reference(reference: &Reference, allow_non_copy_owned: bool) -> Self {
65        if reference.is_null() {
66            Self::Void
67        } else if let Some(text) = reference.read::<Text>() {
68            match text.as_str() {
69                "void" => Self::Void,
70                "char" => Self::CChar,
71                "short" => Self::CShort,
72                "int" => Self::CInt,
73                "long" => Self::CLong,
74                "longlong" => Self::CLongLong,
75                "uchar" => Self::CUChar,
76                "ushort" => Self::CUShort,
77                "uint" => Self::CUInt,
78                "ulong" => Self::CULong,
79                "ulonglong" => Self::CULongLong,
80                "float" => Self::CFloat,
81                "double" => Self::CDouble,
82                "string" => Self::CString,
83                "pointer" => Self::Pointer,
84                name => panic!("Unsupported data type specifier: {}", name),
85            }
86        } else if let Some(type_) = reference.read::<Type>() {
87            if allow_non_copy_owned || type_.handle().unwrap().is_copy() {
88                Self::Value(type_.handle().unwrap().clone())
89            } else {
90                panic!(
91                    "Owned value type `{}` is not copy!",
92                    type_.handle().unwrap().name()
93                );
94            }
95        } else {
96            panic!("Data type specifier can be only Text or Type!");
97        }
98    }
99
100    fn type_handle(&self, is_result: bool) -> Option<TypeHandle> {
101        match self {
102            DataType::Void => None,
103            DataType::CShort => Some(
104                NativeStructBuilder::new::<c_short>()
105                    .build()
106                    .into_type()
107                    .into_handle(),
108            ),
109            DataType::CInt => Some(
110                NativeStructBuilder::new::<c_int>()
111                    .build()
112                    .into_type()
113                    .into_handle(),
114            ),
115            DataType::CLong => Some(
116                NativeStructBuilder::new::<c_long>()
117                    .build()
118                    .into_type()
119                    .into_handle(),
120            ),
121            DataType::CLongLong => Some(
122                NativeStructBuilder::new::<c_longlong>()
123                    .build()
124                    .into_type()
125                    .into_handle(),
126            ),
127            DataType::CUChar => Some(
128                NativeStructBuilder::new::<c_uchar>()
129                    .build()
130                    .into_type()
131                    .into_handle(),
132            ),
133            DataType::CUShort => Some(
134                NativeStructBuilder::new::<c_ushort>()
135                    .build()
136                    .into_type()
137                    .into_handle(),
138            ),
139            DataType::CUInt => Some(
140                NativeStructBuilder::new::<c_uint>()
141                    .build()
142                    .into_type()
143                    .into_handle(),
144            ),
145            DataType::CULong => Some(
146                NativeStructBuilder::new::<c_ulong>()
147                    .build()
148                    .into_type()
149                    .into_handle(),
150            ),
151            DataType::CULongLong => Some(
152                NativeStructBuilder::new::<c_ulonglong>()
153                    .build()
154                    .into_type()
155                    .into_handle(),
156            ),
157            DataType::CFloat => Some(
158                NativeStructBuilder::new::<c_float>()
159                    .build()
160                    .into_type()
161                    .into_handle(),
162            ),
163            DataType::CDouble => Some(
164                NativeStructBuilder::new::<c_double>()
165                    .build()
166                    .into_type()
167                    .into_handle(),
168            ),
169            DataType::CChar => Some(
170                NativeStructBuilder::new::<c_char>()
171                    .build()
172                    .into_type()
173                    .into_handle(),
174            ),
175            DataType::CString => Some(
176                NativeStructBuilder::new::<c_string>()
177                    .build()
178                    .into_type()
179                    .into_handle(),
180            ),
181            DataType::Pointer => {
182                if is_result {
183                    None
184                } else {
185                    Some(
186                        NativeStructBuilder::new::<c_pointer>()
187                            .build()
188                            .into_type()
189                            .into_handle(),
190                    )
191                }
192            }
193            DataType::Value(handle) => Some(handle.clone()),
194        }
195    }
196
197    fn pop_from_stack(&self, context: &mut Context, registry: &Registry) -> Reference {
198        if let Some(value) = context.stack().pop::<c_short>() {
199            Reference::new_integer(value as _, registry)
200        } else if let Some(value) = context.stack().pop::<c_int>() {
201            Reference::new_integer(value as _, registry)
202        } else if let Some(value) = context.stack().pop::<c_long>() {
203            Reference::new_integer(value as _, registry)
204        } else if let Some(value) = context.stack().pop::<c_longlong>() {
205            Reference::new_integer(value as _, registry)
206        } else if let Some(value) = context.stack().pop::<c_uchar>() {
207            Reference::new_integer(value as _, registry)
208        } else if let Some(value) = context.stack().pop::<c_ushort>() {
209            Reference::new_integer(value as _, registry)
210        } else if let Some(value) = context.stack().pop::<c_uint>() {
211            Reference::new_integer(value as _, registry)
212        } else if let Some(value) = context.stack().pop::<c_ulong>() {
213            Reference::new_integer(value as _, registry)
214        } else if let Some(value) = context.stack().pop::<c_ulonglong>() {
215            Reference::new_integer(value as _, registry)
216        } else if let Some(value) = context.stack().pop::<c_float>() {
217            Reference::new_real(value as _, registry)
218        } else if let Some(value) = context.stack().pop::<c_double>() {
219            Reference::new_real(value as _, registry)
220        } else if let Some(value) = context.stack().pop::<c_char>() {
221            Reference::new_text((value as u8 as char).to_string(), registry)
222        } else if let Some(pointer) = context.stack().pop::<c_string>() {
223            let value = unsafe { CString::from_raw(pointer.0) };
224            Reference::new_text(value.to_string_lossy().to_string(), registry)
225        } else if context.stack().pop::<c_pointer>().is_some() {
226            panic!("Cannot marshal pointers from stack!")
227        } else {
228            let object = object_pop_from_stack(context.stack(), registry).unwrap();
229            Reference::new_raw(object)
230        }
231    }
232}
233
234enum DataValue {
235    CShort(c_short),
236    CInt(c_int),
237    CLong(c_long),
238    CLongLong(c_longlong),
239    CUChar(c_uchar),
240    CUShort(c_ushort),
241    CUInt(c_uint),
242    CULong(c_ulong),
243    CULongLong(c_ulonglong),
244    CFloat(c_float),
245    CDouble(c_double),
246    CChar(c_char),
247    CString(CString),
248    Pointer(Reference),
249    Value(Layout, TypeHash, unsafe fn(*mut ()), Vec<u8>),
250}
251
252impl DataValue {
253    fn from_reference(reference: Reference, type_: &DataType) -> Self {
254        match type_ {
255            DataType::Void => unreachable!(),
256            DataType::CShort => Self::CShort(*reference.read::<Integer>().unwrap() as _),
257            DataType::CInt => Self::CInt(*reference.read::<Integer>().unwrap() as _),
258            DataType::CLong => Self::CLong(*reference.read::<Integer>().unwrap() as _),
259            DataType::CLongLong => Self::CLongLong(*reference.read::<Integer>().unwrap() as _),
260            DataType::CUChar => Self::CUChar(*reference.read::<Integer>().unwrap() as _),
261            DataType::CUShort => Self::CUShort(*reference.read::<Integer>().unwrap() as _),
262            DataType::CUInt => Self::CUInt(*reference.read::<Integer>().unwrap() as _),
263            DataType::CULong => Self::CULong(*reference.read::<Integer>().unwrap() as _),
264            DataType::CULongLong => Self::CULongLong(*reference.read::<Integer>().unwrap() as _),
265            DataType::CFloat => Self::CFloat(*reference.read::<Real>().unwrap() as _),
266            DataType::CDouble => Self::CDouble(*reference.read::<Real>().unwrap() as _),
267            DataType::CChar => {
268                let text = reference.read::<Text>().unwrap();
269                if text.is_empty() {
270                    panic!("Text cannot be empty!");
271                } else {
272                    Self::CChar(text.chars().nth(0).unwrap() as _)
273                }
274            }
275            DataType::CString => {
276                let text = reference.read::<Text>().unwrap();
277                Self::CString(CString::new(text.as_str()).unwrap())
278            }
279            DataType::Pointer => Self::Pointer(reference),
280            DataType::Value(type_) => {
281                if type_.is_copy() {
282                    unsafe {
283                        let mut data = vec![0u8; type_.layout().size()];
284                        data.as_mut_ptr()
285                            .copy_from(reference.read_object().unwrap().as_ptr(), data.len());
286                        Self::Value(*type_.layout(), type_.type_hash(), type_.finalizer(), data)
287                    }
288                } else {
289                    unreachable!()
290                }
291            }
292        }
293    }
294
295    fn push_to_stack(&self, context: &mut Context) {
296        match self {
297            Self::CShort(value) => {
298                context.stack().push(*value);
299            }
300            Self::CInt(value) => {
301                context.stack().push(*value);
302            }
303            Self::CLong(value) => {
304                context.stack().push(*value);
305            }
306            Self::CLongLong(value) => {
307                context.stack().push(*value);
308            }
309            Self::CUChar(value) => {
310                context.stack().push(*value);
311            }
312            Self::CUShort(value) => {
313                context.stack().push(*value);
314            }
315            Self::CUInt(value) => {
316                context.stack().push(*value);
317            }
318            Self::CULong(value) => {
319                context.stack().push(*value);
320            }
321            Self::CULongLong(value) => {
322                context.stack().push(*value);
323            }
324            Self::CFloat(value) => {
325                context.stack().push(*value);
326            }
327            Self::CDouble(value) => {
328                context.stack().push(*value);
329            }
330            Self::CChar(value) => {
331                context.stack().push(*value);
332            }
333            Self::CString(buffer) => {
334                context
335                    .stack()
336                    .push(c_string(buffer.as_ptr().cast_mut() as *mut _));
337            }
338            Self::Pointer(reference) => {
339                context.stack().push(unsafe {
340                    c_pointer(reference.read_object().unwrap().as_ptr().cast_mut() as *mut _)
341                });
342            }
343            Self::Value(layout, type_hash, finalizer, data) => unsafe {
344                context
345                    .stack()
346                    .push_raw(*layout, *type_hash, *finalizer, data);
347            },
348        }
349    }
350}
351
352#[intuicio_function(module_name = "ffi", use_registry)]
353pub fn load(registry: &Registry, path: Reference) -> Reference {
354    let path = path.read::<Text>().unwrap();
355    let path = Path::new(path.as_str());
356    if let Ok(lib) = FfiLibrary::new(path) {
357        Reference::new(lib, registry)
358    } else {
359        Reference::null()
360    }
361}
362
363#[intuicio_function(module_name = "ffi", use_registry)]
364pub fn function(
365    registry: &Registry,
366    mut library: Reference,
367    name: Reference,
368    result: Reference,
369    arguments: Reference,
370) -> Reference {
371    let mut library = library.write::<FfiLibrary>().unwrap();
372    let name = name.read::<Text>().unwrap();
373    let result_type = DataType::from_reference(&result, true);
374    let argument_types = arguments
375        .read::<Array>()
376        .unwrap()
377        .iter()
378        .map(|argument| DataType::from_reference(argument, false))
379        .collect::<Vec<_>>();
380    let mut signature = FunctionSignature::new(name.as_str()).with_module_name(library.name());
381    if let Some(type_) = result_type.type_handle(true) {
382        signature
383            .outputs
384            .push(FunctionParameter::new("result", type_));
385    }
386    for (index, argument) in argument_types.iter().enumerate() {
387        signature.inputs.push(FunctionParameter::new(
388            format!("arg{}", index),
389            argument.type_handle(false).unwrap(),
390        ));
391    }
392    let ffi = library.function(signature.clone()).unwrap();
393    let handle = Function::new(
394        signature,
395        FunctionBody::Closure(Arc::new(move |context, registry| unsafe {
396            let values = argument_types
397                .iter()
398                .map(|type_| {
399                    DataValue::from_reference(context.stack().pop::<Reference>().unwrap(), type_)
400                })
401                .collect::<Vec<_>>();
402            for value in values.into_iter().rev() {
403                value.push_to_stack(context);
404            }
405            ffi.call(context).expect("FFI call error");
406            let result = if !matches!(result_type, DataType::Void) {
407                result_type.pop_from_stack(context, registry)
408            } else {
409                Reference::null()
410            };
411            context.stack().push(result);
412        })),
413    )
414    .into_handle();
415    Reference::new(intuicio_framework_dynamic::Function::new(handle), registry)
416}
417
418pub fn install(registry: &mut Registry) {
419    registry.add_type(NativeStructBuilder::new_uninitialized::<FfiLibrary>().build());
420    registry.add_function(load::define_function(registry));
421    registry.add_function(function::define_function(registry));
422}