mun_runtime/
function.rs

1//! Exposes function information using the C ABI.
2
3use 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/// Describes a `Function` accessible from a Mun [`super::runtime::Runtime`].
14///
15/// An instance of `Function` shares ownership of the underlying data. To create a copy of the
16/// `Function` object call [`mun_function_add_reference`] to make sure the number of references to
17/// the data is properly tracked. Calling [`mun_function_release`] signals the runtime that the data
18/// is no longer referenced through the specified object. When all references are released the
19/// underlying data is deallocated.
20#[repr(C)]
21#[derive(Clone, Copy)]
22pub struct Function(pub *const c_void);
23
24impl Function {
25    /// Returns a Self containing nulls.
26    pub fn null() -> Self {
27        Self(ptr::null())
28    }
29
30    /// Returns a reference to the data that this instance is referencing.
31    ///
32    /// # Safety
33    ///
34    /// The caller must ensure that the internal pointers point to a valid
35    /// [`mun_runtime::FunctionDefinition`].
36    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/// Notifies the runtime an additional references exists to the function. This ensures that the data
50/// is kept alive even if [`mun_function_release`] is called for the existing references. Only
51/// after all references have been released can the underlying data be deallocated.
52///
53/// # Safety
54///
55/// This function might be unsafe if the underlying data has already been deallocated by a previous
56/// call to [`mun_function_release`].
57#[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/// Notifies the runtime that one of the references to the function is no longer in use. The data
68/// may not immediately be destroyed. Only after all references have been released can the
69/// underlying data be deallocated.
70///
71/// # Safety
72///
73/// This function might be unsafe if the underlying data has been deallocated by a previous call
74/// to [`mun_function_release`].
75#[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/// Retrieves the function's function pointer.
86///
87/// # Safety
88///
89/// This function might be unsafe if the underlying data has been deallocated by a previous call
90/// to [`mun_function_release`].
91#[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/// Retrieves the function's name.
105///
106/// If the function is successful, the caller is responsible for calling [`mun_string_destroy`] on
107/// the return pointer.
108///
109/// # Safety
110///
111/// This function might be unsafe if the underlying data has been deallocated by a previous call
112/// to [`mun_function_release`].
113#[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/// Retrieves the function's argument types.
129///
130/// If successful, ownership of the [`Types`] is transferred to the caller. It must be deallocated
131/// with a call to [`mun_types_destroy`].
132///
133/// # Safety
134///
135///
136/// This function might be unsafe if the underlying data has been deallocated by a previous call
137/// to [`mun_function_release`].
138#[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/// Retrieves the function's return type.
159///
160/// Ownership of the [`Type`] is transferred to the called. It must be released with a call to
161/// [`mun_type_release`].
162///
163/// # Safety
164///
165/// This function might be unsafe if the underlying data has been deallocated by a previous call
166/// to [`mun_function_release`].
167#[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        // This works because the Arc is not shared between threads because it's local to the
219        // runtime created in this test
220        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        // This works because the Arc is not shared between threads because it's local to the
238        // runtime created in this test
239        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}