1use mun_capi_utils::error::ErrorHandle;
4use mun_capi_utils::{mun_error_try, try_deref_mut};
5use mun_memory::ffi::{Type, Types};
6use std::{
7 ffi::{c_void, CString},
8 os::raw::c_char,
9 ptr,
10 sync::Arc,
11};
12
13#[repr(C)]
21#[derive(Clone, Copy)]
22pub struct Function(pub *const c_void);
23
24impl Function {
25 pub fn null() -> Self {
27 Self(ptr::null())
28 }
29
30 pub unsafe fn inner(&self) -> Result<&mun_runtime::FunctionDefinition, &'static str> {
37 (self.0 as *const mun_runtime::FunctionDefinition)
38 .as_ref()
39 .ok_or("null pointer")
40 }
41}
42
43impl From<Arc<mun_runtime::FunctionDefinition>> for Function {
44 fn from(def: Arc<mun_runtime::FunctionDefinition>) -> Self {
45 Function(Arc::into_raw(def).cast())
46 }
47}
48
49#[no_mangle]
58pub unsafe extern "C" fn mun_function_add_reference(function: Function) -> ErrorHandle {
59 if function.0.is_null() {
60 return ErrorHandle::new("invalid argument 'function': null pointer");
61 }
62
63 Arc::increment_strong_count(function.0);
64 ErrorHandle::default()
65}
66
67#[no_mangle]
76pub unsafe extern "C" fn mun_function_release(function: Function) -> ErrorHandle {
77 if function.0.is_null() {
78 return ErrorHandle::new("invalid argument 'function': null pointer");
79 }
80
81 Arc::decrement_strong_count(function.0);
82 ErrorHandle::default()
83}
84
85#[no_mangle]
92pub unsafe extern "C" fn mun_function_fn_ptr(
93 function: Function,
94 ptr: *mut *const c_void,
95) -> ErrorHandle {
96 let function = mun_error_try!(function
97 .inner()
98 .map_err(|e| format!("invalid argument 'function': {e}")));
99 let ptr = try_deref_mut!(ptr);
100 *ptr = function.fn_ptr;
101 ErrorHandle::default()
102}
103
104#[no_mangle]
114pub unsafe extern "C" fn mun_function_name(
115 function: Function,
116 name: *mut *const c_char,
117) -> ErrorHandle {
118 let function = mun_error_try!(function
119 .inner()
120 .map_err(|e| format!("invalid argument 'function': {e}")));
121 let name = try_deref_mut!(name);
122 *name = CString::new(function.prototype.name.clone())
123 .unwrap()
124 .into_raw() as *const _;
125 ErrorHandle::default()
126}
127
128#[no_mangle]
139pub unsafe extern "C" fn mun_function_argument_types(
140 function: Function,
141 arg_types: *mut Types,
142) -> ErrorHandle {
143 let function = mun_error_try!(function
144 .inner()
145 .map_err(|e| format!("invalid argument 'function': {e}")));
146 let arg_types = try_deref_mut!(arg_types);
147 *arg_types = function
148 .prototype
149 .signature
150 .arg_types
151 .iter()
152 .map(|ty| ty.clone().into())
153 .collect::<Vec<_>>()
154 .into();
155 ErrorHandle::default()
156}
157
158#[no_mangle]
168pub unsafe extern "C" fn mun_function_return_type(
169 function: Function,
170 ty: *mut Type,
171) -> ErrorHandle {
172 let function = mun_error_try!(function
173 .inner()
174 .map_err(|e| format!("invalid argument 'function': {e}")));
175 let ty = try_deref_mut!(ty);
176 *ty = function.prototype.signature.return_type.clone().into();
177 ErrorHandle::default()
178}
179
180#[cfg(test)]
181pub(crate) mod tests {
182 use super::*;
183 use mun_capi_utils::{
184 assert_error_snapshot, assert_getter1, mun_string_destroy, try_convert_c_string,
185 };
186 use mun_memory::ffi::{mun_type_equal, mun_types_destroy};
187 use mun_memory::HasStaticType;
188 use std::mem::ManuallyDrop;
189 use std::{mem::MaybeUninit, slice, sync::Arc};
190
191 #[test]
192 fn test_function_release_invalid_fn_info() {
193 assert_error_snapshot!(
194 unsafe { mun_function_release(Function::null()) },
195 @r###""invalid argument \'function\': null pointer""###);
196 }
197
198 #[test]
199 fn test_function_add_reference_invalid_fn_info() {
200 assert_error_snapshot!(
201 unsafe { mun_function_add_reference(Function::null()) },
202 @r###""invalid argument \'function\': null pointer""###);
203 }
204
205 #[test]
206 fn test_function_release_strong_count() {
207 let function = mun_runtime::FunctionDefinition::builder("foo").finish();
208 let ffi_function: Function = function.clone().into();
209
210 let fn_def = ManuallyDrop::new(unsafe {
211 Arc::from_raw(ffi_function.0 as *const mun_runtime::FunctionDefinition)
212 });
213 let strong_count = Arc::strong_count(&fn_def);
214 assert!(strong_count > 0);
215
216 assert!(unsafe { mun_function_release(ffi_function) }.is_ok());
217
218 assert_eq!(Arc::strong_count(&fn_def), strong_count - 1);
221 }
222
223 #[test]
224 fn test_function_add_reference_strong_count() {
225 let function: Function = mun_runtime::FunctionDefinition::builder("foo")
226 .finish()
227 .into();
228
229 let fn_info_arc = ManuallyDrop::new(unsafe {
230 Arc::from_raw(function.0 as *const mun_runtime::FunctionDefinition)
231 });
232 let strong_count = Arc::strong_count(&fn_info_arc);
233 assert!(strong_count > 0);
234
235 assert!(unsafe { mun_function_add_reference(function) }.is_ok());
236
237 assert_eq!(Arc::strong_count(&fn_info_arc), strong_count + 1);
240 }
241
242 #[test]
243 fn test_function_invalid_fn_info() {
244 let function = mun_runtime::FunctionDefinition::builder("foo")
245 .finish()
246 .into();
247
248 let mut ptr = MaybeUninit::uninit();
249 assert_error_snapshot!(
250 unsafe { mun_function_fn_ptr(Function::null(), ptr.as_mut_ptr()) },
251 @r###""invalid argument \'function\': null pointer""###);
252 assert_error_snapshot!(
253 unsafe { mun_function_fn_ptr(function, ptr::null_mut()) },
254 @r###""invalid argument \'ptr\': null pointer""###);
255
256 assert!(unsafe { mun_function_release(function) }.is_ok());
257 }
258
259 #[test]
260 fn test_function_fn_ptr() {
261 let invalid_fn_ptr = 0xDEAD as *const c_void;
262 let function = mun_runtime::FunctionDefinition::builder("foo")
263 .set_ptr(invalid_fn_ptr)
264 .finish()
265 .into();
266
267 assert_getter1!(mun_function_fn_ptr(function, fn_ptr));
268 assert_eq!(fn_ptr, invalid_fn_ptr);
269
270 assert!(unsafe { mun_function_release(function) }.is_ok());
271 }
272
273 #[test]
274 fn test_function_name_invalid_fn_info() {
275 let function = mun_runtime::FunctionDefinition::builder("foo")
276 .finish()
277 .into();
278
279 let mut ptr = MaybeUninit::uninit();
280 assert_error_snapshot!(
281 unsafe { mun_function_name(Function::null(), ptr.as_mut_ptr()) },
282 @r###""invalid argument \'function\': null pointer""###);
283 assert_error_snapshot!(
284 unsafe { mun_function_name(function, ptr::null_mut()) },
285 @r###""invalid argument \'name\': null pointer""###);
286
287 assert!(unsafe { mun_function_release(function) }.is_ok());
288 }
289
290 #[test]
291 fn test_function_name() {
292 let function = mun_runtime::FunctionDefinition::builder("foo")
293 .finish()
294 .into();
295
296 assert_getter1!(mun_function_name(function, name));
297 assert_ne!(name, ptr::null());
298
299 let name_str = unsafe { try_convert_c_string(name) }.expect("invalid name");
300 assert_eq!(name_str, "foo");
301
302 unsafe { mun_string_destroy(name) };
303 assert!(unsafe { mun_function_release(function) }.is_ok());
304 }
305
306 #[test]
307 fn test_function_argument_types_invalid_fn_info() {
308 let function = mun_runtime::FunctionDefinition::builder("foo")
309 .finish()
310 .into();
311
312 let mut ptr = MaybeUninit::uninit();
313 assert_error_snapshot!(unsafe {
314 mun_function_argument_types(Function::null(), ptr.as_mut_ptr())
315 }, @r###""invalid argument \'function\': null pointer""###);
316 assert_error_snapshot!(
317 unsafe { mun_function_argument_types(function, ptr::null_mut()) },
318 @r###""invalid argument \'arg_types\': null pointer""###);
319
320 assert!(unsafe { mun_function_release(function) }.is_ok());
321 }
322
323 #[test]
324 fn test_function_argument_types_none() {
325 let function = mun_runtime::FunctionDefinition::builder("foo")
326 .finish()
327 .into();
328
329 assert_getter1!(mun_function_argument_types(function, arg_types));
330 assert_eq!(arg_types.types, ptr::null());
331 assert_eq!(arg_types.count, 0);
332
333 assert!(unsafe { mun_types_destroy(arg_types) }.is_ok());
334 assert!(unsafe { mun_function_release(function) }.is_ok());
335 }
336
337 #[test]
338 fn test_function_argument_types_some() {
339 let function = mun_runtime::FunctionDefinition::builder("foo")
340 .add_argument(i32::type_info().clone())
341 .add_argument(i32::type_info().clone())
342 .finish()
343 .into();
344
345 assert_getter1!(mun_function_argument_types(function, arg_types));
346 assert_eq!(arg_types.count, 2);
347
348 for arg_type in unsafe { slice::from_raw_parts(arg_types.types, arg_types.count) } {
349 assert!(unsafe { mun_type_equal(*arg_type, i32::type_info().clone().into()) });
350 }
351
352 assert!(unsafe { mun_types_destroy(arg_types) }.is_ok());
353 assert!(unsafe { mun_function_release(function) }.is_ok());
354 }
355
356 #[test]
357 fn test_function_return_type_invalid_fn_info() {
358 let function = mun_runtime::FunctionDefinition::builder("foo")
359 .finish()
360 .into();
361
362 let mut ptr = MaybeUninit::uninit();
363 assert_error_snapshot!(
364 unsafe { mun_function_return_type(Function::null(), ptr.as_mut_ptr()) },
365 @r###""invalid argument \'function\': null pointer""###);
366 assert_error_snapshot!(
367 unsafe { mun_function_return_type(function, ptr::null_mut()) },
368 @r###""invalid argument \'ty\': null pointer""###);
369
370 assert!(unsafe { mun_function_release(function) }.is_ok());
371 }
372
373 #[test]
374 fn test_function_return_type_none() {
375 let function = mun_runtime::FunctionDefinition::builder("foo")
376 .finish()
377 .into();
378
379 assert_getter1!(mun_function_return_type(function, return_type));
380
381 assert!(unsafe { mun_type_equal(return_type, <()>::type_info().clone().into()) });
382 }
383
384 #[test]
385 fn test_function_return_type_some() {
386 let function = mun_runtime::FunctionDefinition::builder("foo")
387 .set_return_type(i32::type_info().clone())
388 .finish()
389 .into();
390
391 assert_getter1!(mun_function_return_type(function, return_type));
392
393 assert!(unsafe { mun_type_equal(return_type, i32::type_info().clone().into()) });
394 }
395}