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}