mun_runtime/
runtime.rs

1//! Exposes the Mun runtime using the C ABI.
2
3use crate::function::Function;
4use mun_abi as abi;
5use mun_capi_utils::{
6    error::ErrorHandle, mun_error_try, try_convert_c_string, try_deref, try_deref_mut,
7};
8use mun_memory::{ffi::Type, type_table::TypeTable, Type as RustType};
9use mun_runtime::{FunctionDefinition, FunctionPrototype, FunctionSignature};
10use std::{ffi::c_void, mem::ManuallyDrop, ops::Deref, os::raw::c_char, slice};
11
12/// A C-style handle to a runtime.
13#[repr(C)]
14#[derive(Clone, Copy)]
15pub struct Runtime(pub *mut c_void);
16
17impl Runtime {
18    /// Returns a reference to rust Runtime, or an error if this instance contains a null pointer.
19    ///
20    /// # Safety
21    ///
22    /// The caller must ensure that the internal pointers point to a valid [`mun_runtime::Runtime`].
23    pub(crate) unsafe fn inner(&self) -> Result<&mun_runtime::Runtime, &'static str> {
24        (self.0 as *mut mun_runtime::Runtime)
25            .as_ref()
26            .ok_or("null pointer")
27    }
28
29    /// Returns a mutable reference to rust Runtime, or an error if this instance contains a null
30    /// pointer.
31    ///
32    /// # Safety
33    ///
34    /// The caller must ensure that the internal pointers point to a valid [`mun_runtime::Runtime`].
35    pub unsafe fn inner_mut(&self) -> Result<&mut mun_runtime::Runtime, &'static str> {
36        (self.0 as *mut mun_runtime::Runtime)
37            .as_mut()
38            .ok_or("null pointer")
39    }
40}
41
42/// Definition of an external function that is callable from Mun.
43///
44/// The ownership of the contained TypeInfoHandles is considered to lie with this struct.
45#[repr(C)]
46#[derive(Clone)]
47pub struct ExternalFunctionDefinition {
48    /// The name of the function
49    pub name: *const c_char,
50
51    /// The number of arguments of the function
52    pub num_args: u32,
53
54    /// The types of the arguments
55    pub arg_types: *const Type,
56
57    /// The type of the return type
58    pub return_type: Type,
59
60    /// Pointer to the function
61    pub fn_ptr: *const c_void,
62}
63
64/// Options required to construct a [`RuntimeHandle`] through [`mun_runtime_create`]
65///
66/// # Safety
67///
68/// This struct contains raw pointers as parameters. Passing pointers to invalid data, will lead to
69/// undefined behavior.
70#[repr(C)]
71#[derive(Clone, Copy)]
72pub struct RuntimeOptions {
73    /// Function definitions that should be inserted in the runtime before a mun library is loaded.
74    /// This is useful to initialize `extern` functions used in a mun library.
75    ///
76    /// If the [`num_functions`] fields is non-zero this field must contain a pointer to an array
77    /// of [`abi::FunctionDefinition`]s.
78    pub functions: *const ExternalFunctionDefinition,
79
80    /// The number of functions in the [`functions`] array.
81    pub num_functions: u32,
82}
83
84impl Default for RuntimeOptions {
85    fn default() -> Self {
86        RuntimeOptions {
87            functions: std::ptr::null(),
88            num_functions: 0,
89        }
90    }
91}
92
93/// Constructs a new runtime that loads the library at `library_path` and its dependencies. If
94/// successful, the runtime `handle` is set, otherwise a non-zero error handle is returned.
95///
96/// If a non-zero error handle is returned, it must be manually destructed using
97/// [`mun_error_destroy`].
98///
99/// The runtime must be manually destructed using [`mun_runtime_destroy`].
100///
101/// # Safety
102///
103/// This function receives raw pointers as parameters. If any of the arguments is a null pointer,
104/// an error will be returned. Passing pointers to invalid data, will lead to undefined behavior.
105#[no_mangle]
106pub unsafe extern "C" fn mun_runtime_create(
107    library_path: *const c_char,
108    options: RuntimeOptions,
109    handle: *mut Runtime,
110) -> ErrorHandle {
111    let library_path = mun_error_try!(try_convert_c_string(library_path)
112        .map_err(|e| format!("invalid argument 'library_path': {e}")));
113    let handle = try_deref_mut!(handle);
114
115    if options.num_functions > 0 && options.functions.is_null() {
116        return ErrorHandle::new("invalid argument: 'functions' is null pointer.");
117    }
118
119    let type_table = TypeTable::default();
120    let user_functions = mun_error_try!(std::slice::from_raw_parts(
121        options.functions,
122        options.num_functions as usize
123    )
124    .iter()
125    .map(|def| {
126        let name =
127            try_convert_c_string(def.name).map_err(|e| format!("invalid function name: {e}"))?;
128        let return_type = ManuallyDrop::new(
129            def.return_type
130                .to_owned()
131                .map_err(|e| format!("invalid function '{name}': 'return_type': {e}"))?,
132        )
133        .deref()
134        .clone();
135
136        if def.num_args > 0 && def.arg_types.is_null() {
137            return Err(format!(
138                "invalid function '{}': 'arg_types' is null pointer.",
139                name
140            ));
141        }
142
143        let arg_types: Vec<_> = if def.num_args > 0 {
144            std::slice::from_raw_parts(def.arg_types, def.num_args as usize)
145                .iter()
146                .enumerate()
147                .map(|(i, arg)| -> Result<RustType, String> {
148                    let ty = (*arg).to_owned().map_err(|e| {
149                        format!("invalid function '{}': argument #{}: {}", name, i + 1, e)
150                    })?;
151                    Ok(ManuallyDrop::new(ty).deref().clone())
152                })
153                .collect::<Result<_, _>>()?
154        } else {
155            Vec::new()
156        };
157
158        Ok(FunctionDefinition {
159            prototype: FunctionPrototype {
160                name: name.to_owned(),
161                signature: FunctionSignature {
162                    arg_types,
163                    return_type,
164                },
165            },
166            fn_ptr: def.fn_ptr,
167        })
168    })
169    .collect::<Result<_, _>>());
170
171    let runtime_options = mun_runtime::RuntimeOptions {
172        library_path: library_path.into(),
173        user_functions,
174        type_table,
175    };
176
177    let runtime = match mun_runtime::Runtime::new(runtime_options) {
178        Ok(runtime) => runtime,
179        Err(e) => return ErrorHandle::new(format!("{:?}", e)),
180    };
181
182    handle.0 = Box::into_raw(Box::new(runtime)) as *mut _;
183    ErrorHandle::default()
184}
185
186/// Destructs the runtime corresponding to `handle`.
187#[no_mangle]
188pub extern "C" fn mun_runtime_destroy(runtime: Runtime) -> ErrorHandle {
189    if runtime.0.is_null() {
190        return ErrorHandle::new("invalid argument 'runtime': null pointer");
191    }
192    let _runtime = unsafe { Box::from_raw(runtime.0) };
193    ErrorHandle::default()
194}
195
196/// Retrieves the [`FunctionDefinition`] for `fn_name` from the `runtime`. If successful,
197/// `has_fn_info` and `fn_info` are set, otherwise a non-zero error handle is returned.
198///
199/// If a non-zero error handle is returned, it must be manually destructed using
200/// [`mun_error_destroy`].
201///
202/// # Safety
203///
204/// This function receives raw pointers as parameters. If any of the arguments is a null pointer,
205/// an error will be returned. Passing pointers to invalid data, will lead to undefined behavior.
206#[no_mangle]
207pub unsafe extern "C" fn mun_runtime_find_function_definition(
208    runtime: Runtime,
209    fn_name: *const c_char,
210    fn_name_len: usize,
211    has_fn_info: *mut bool,
212    fn_info: *mut Function,
213) -> ErrorHandle {
214    let runtime = mun_error_try!(runtime
215        .inner()
216        .map_err(|e| format!("invalid argument 'runtime': {e}")));
217    if fn_name.is_null() {
218        return ErrorHandle::new("invalid argument 'fn_name': null pointer");
219    }
220    let name = mun_error_try!(std::str::from_utf8(slice::from_raw_parts(
221        fn_name as *const u8,
222        fn_name_len
223    ))
224    .map_err(|_| String::from("invalid argument 'fn_name': invalid UTF-8 encoded")));
225    let has_fn_info = try_deref_mut!(has_fn_info);
226    let fn_info = try_deref_mut!(fn_info);
227    match runtime.get_function_definition(name) {
228        Some(info) => {
229            *has_fn_info = true;
230            *fn_info = info.into()
231        }
232        None => *has_fn_info = false,
233    }
234
235    ErrorHandle::default()
236}
237
238/// Retrieves the type information corresponding to the specified `type_name` from the runtime.
239/// If successful, `has_type_info` and `type_info` are set, otherwise a non-zero error handle is
240/// returned.
241///
242/// If a non-zero error handle is returned, it must be manually destructed using
243/// [`mun_error_destroy`].
244///
245/// # Safety
246///
247/// This function receives raw pointers as parameters. If any of the arguments is a null pointer,
248/// an error will be returned. Passing pointers to invalid data, will lead to undefined behavior.
249#[no_mangle]
250pub unsafe extern "C" fn mun_runtime_get_type_info_by_name(
251    runtime: Runtime,
252    type_name: *const c_char,
253    has_type_info: *mut bool,
254    type_info: *mut Type,
255) -> ErrorHandle {
256    let runtime = mun_error_try!(runtime
257        .inner()
258        .map_err(|e| format!("invalid argument 'runtime': {e}")));
259    let type_name =
260        mun_error_try!(try_convert_c_string(type_name)
261            .map_err(|e| format!("invalid argument 'type_name': {e}")));
262    let has_type_info = try_deref_mut!(has_type_info);
263    let type_info = try_deref_mut!(type_info);
264    match runtime.get_type_info_by_name(type_name) {
265        Some(info) => {
266            *has_type_info = true;
267            *type_info = info.into();
268        }
269        None => *has_type_info = false,
270    }
271
272    ErrorHandle::default()
273}
274
275/// Retrieves the type information corresponding to the specified `type_id` from the runtime. If
276/// successful, `has_type_info` and `type_info` are set, otherwise a non-zero error handle is
277/// returned.
278///
279/// If a non-zero error handle is returned, it must be manually destructed using
280/// [`mun_error_destroy`].
281///
282/// # Safety
283///
284/// This function receives raw pointers as parameters. If any of the arguments is a null pointer,
285/// an error will be returned. Passing pointers to invalid data, will lead to undefined behavior.
286#[no_mangle]
287pub unsafe extern "C" fn mun_runtime_get_type_info_by_id(
288    runtime: Runtime,
289    type_id: *const abi::TypeId,
290    has_type_info: *mut bool,
291    type_info: *mut Type,
292) -> ErrorHandle {
293    let runtime = mun_error_try!(runtime
294        .inner()
295        .map_err(|e| format!("invalid argument 'runtime': {e}")));
296    let type_id = try_deref!(type_id);
297    let has_type_info = try_deref_mut!(has_type_info);
298    let type_info = try_deref_mut!(type_info);
299
300    match runtime.get_type_info_by_id(type_id) {
301        Some(info) => {
302            *has_type_info = true;
303            *type_info = info.into();
304        }
305        None => *has_type_info = false,
306    }
307
308    ErrorHandle::default()
309}
310
311/// Updates the runtime corresponding to `handle`. If successful, `updated` is set, otherwise a
312/// non-zero error handle is returned.
313///
314/// If a non-zero error handle is returned, it must be manually destructed using
315/// [`mun_error_destroy`].
316///
317/// # Safety
318///
319/// This function receives raw pointers as parameters. If any of the arguments is a null pointer,
320/// an error will be returned. Passing pointers to invalid data, will lead to undefined behavior.
321#[no_mangle]
322pub unsafe extern "C" fn mun_runtime_update(runtime: Runtime, updated: *mut bool) -> ErrorHandle {
323    let runtime = mun_error_try!(runtime
324        .inner_mut()
325        .map_err(|e| format!("invalid argument 'runtime': {e}")));
326    let updated = try_deref_mut!(updated);
327    *updated = runtime.update();
328    ErrorHandle::default()
329}
330
331#[cfg(test)]
332mod tests {
333    use super::*;
334    use crate::{test_invalid_runtime, test_util::TestDriver};
335    use mun_capi_utils::error::mun_error_destroy;
336    use mun_capi_utils::{assert_error_snapshot, assert_getter1, assert_getter2, assert_getter3};
337    use mun_memory::HasStaticType;
338    use std::{ffi::CString, mem::MaybeUninit, ptr};
339
340    test_invalid_runtime!(
341        runtime_find_function_definition(ptr::null(), 0, ptr::null_mut(), ptr::null_mut()),
342        runtime_get_type_info_by_name(ptr::null(), ptr::null_mut(), ptr::null_mut()),
343        runtime_get_type_info_by_id(ptr::null(), ptr::null_mut(), ptr::null_mut()),
344        runtime_update(ptr::null_mut())
345    );
346
347    #[test]
348    fn test_runtime_create_invalid_lib_path() {
349        assert_error_snapshot!(
350            unsafe { mun_runtime_create(ptr::null(), RuntimeOptions::default(), ptr::null_mut()) },
351            @r###""invalid argument \'library_path\': null pointer""###
352        );
353    }
354
355    #[test]
356    fn test_runtime_create_invalid_lib_path_encoding() {
357        let invalid_encoding = ['�', '\0'];
358
359        assert_error_snapshot!(
360            unsafe {
361                mun_runtime_create(
362                    invalid_encoding.as_ptr() as *const _,
363                    RuntimeOptions::default(),
364                    ptr::null_mut(),
365                )
366            },
367            @r###""invalid argument \'library_path\': invalid UTF-8 encoded""###
368        );
369    }
370
371    #[test]
372    fn test_runtime_create_invalid_functions() {
373        let lib_path = CString::new("some/path").expect("Invalid library path");
374
375        let options = RuntimeOptions {
376            num_functions: 1,
377            ..Default::default()
378        };
379
380        let mut handle = MaybeUninit::uninit();
381        assert_error_snapshot!(
382            unsafe { mun_runtime_create(lib_path.into_raw(), options, handle.as_mut_ptr()) },
383            @r###""invalid argument: \'functions\' is null pointer.""###
384        );
385    }
386
387    #[test]
388    fn test_runtime_create_invalid_handle() {
389        let lib_path = CString::new("some/path").expect("Invalid library path");
390
391        assert_error_snapshot!(
392            unsafe {
393                mun_runtime_create(lib_path.into_raw(), RuntimeOptions::default(), ptr::null_mut())
394            },
395            @r###""invalid argument \'handle\': null pointer""###
396        );
397    }
398
399    #[test]
400    fn test_runtime_create_invalid_user_function_name() {
401        let lib_path = CString::new("some/path").expect("Invalid library path");
402
403        let type_id = <()>::type_info().clone().into();
404        let functions = vec![ExternalFunctionDefinition {
405            name: ptr::null(),
406            arg_types: ptr::null(),
407            return_type: type_id,
408            num_args: 0,
409            fn_ptr: ptr::null(),
410        }];
411
412        let options = RuntimeOptions {
413            functions: functions.as_ptr(),
414            num_functions: 1,
415            ..Default::default()
416        };
417
418        let mut handle = MaybeUninit::uninit();
419        assert_error_snapshot!(
420            unsafe { mun_runtime_create(lib_path.into_raw(), options, handle.as_mut_ptr()) },
421            @r###""invalid function name: null pointer""###
422        );
423    }
424
425    #[test]
426    fn test_runtime_create_invalid_user_function_name_encoding() {
427        let lib_path = CString::new("some/path").expect("Invalid library path");
428
429        let invalid_encoding = ['�', '\0'];
430        let type_id = <()>::type_info().clone().into();
431        let functions = vec![ExternalFunctionDefinition {
432            name: invalid_encoding.as_ptr() as *const _,
433            arg_types: ptr::null(),
434            return_type: type_id,
435            num_args: 0,
436            fn_ptr: ptr::null(),
437        }];
438
439        let options = RuntimeOptions {
440            functions: functions.as_ptr(),
441            num_functions: 1,
442            ..Default::default()
443        };
444
445        let mut handle = MaybeUninit::uninit();
446        assert_error_snapshot!(
447            unsafe { mun_runtime_create(lib_path.into_raw(), options, handle.as_mut_ptr()) },
448            @r###""invalid function name: invalid UTF-8 encoded""###
449        );
450    }
451
452    #[test]
453    fn test_runtime_create_invalid_user_function_return_type() {
454        let lib_path = CString::new("some/path").expect("Invalid library path");
455        let function_name = CString::new("foobar").unwrap();
456
457        let functions = vec![ExternalFunctionDefinition {
458            name: function_name.as_ptr(),
459            arg_types: ptr::null(),
460            return_type: Type::null(),
461            num_args: 0,
462            fn_ptr: ptr::null(),
463        }];
464
465        let options = RuntimeOptions {
466            functions: functions.as_ptr(),
467            num_functions: 1,
468            ..Default::default()
469        };
470
471        let mut handle = MaybeUninit::uninit();
472        assert_error_snapshot!(
473            unsafe { mun_runtime_create(lib_path.into_raw(), options, handle.as_mut_ptr()) },
474            @r###""invalid function \'foobar\': \'return_type\': null pointer""###
475        );
476    }
477
478    #[test]
479    fn test_runtime_create_invalid_user_function_arg_types_ptr() {
480        let lib_path = CString::new("some/path").expect("Invalid library path");
481        let function_name = CString::new("foobar").unwrap();
482
483        let type_id = <()>::type_info().clone().into();
484        let functions = vec![ExternalFunctionDefinition {
485            name: function_name.as_ptr(),
486            arg_types: ptr::null(),
487            return_type: type_id,
488            num_args: 1,
489            fn_ptr: ptr::null(),
490        }];
491
492        let options = RuntimeOptions {
493            functions: functions.as_ptr(),
494            num_functions: 1,
495            ..Default::default()
496        };
497
498        let mut handle = MaybeUninit::uninit();
499        assert_error_snapshot!(
500            unsafe { mun_runtime_create(lib_path.into_raw(), options, handle.as_mut_ptr()) },
501            @r###""invalid function \'foobar\': \'arg_types\' is null pointer.""###
502        );
503    }
504
505    #[test]
506    fn test_runtime_create_invalid_user_function_arg_types() {
507        let lib_path = CString::new("some/path").expect("Invalid library path");
508        let function_name = CString::new("foobar").unwrap();
509        let arg_types = [Type::null()];
510
511        let type_id = <()>::type_info().clone().into();
512        let functions = vec![ExternalFunctionDefinition {
513            name: function_name.as_ptr(),
514            arg_types: &arg_types as _,
515            return_type: type_id,
516            num_args: 1,
517            fn_ptr: ptr::null(),
518        }];
519
520        let options = RuntimeOptions {
521            functions: functions.as_ptr(),
522            num_functions: 1,
523            ..Default::default()
524        };
525
526        let mut handle = MaybeUninit::uninit();
527        assert_error_snapshot!(
528            unsafe { mun_runtime_create(lib_path.into_raw(), options, handle.as_mut_ptr()) },
529            @r###""invalid function \'foobar\': argument #1: null pointer""###
530        );
531    }
532
533    #[test]
534    fn test_runtime_get_function_info_invalid_fn_name() {
535        let driver = TestDriver::new(
536            r#"
537        pub fn main() -> i32 { 3 }
538    "#,
539        );
540
541        assert_error_snapshot!(
542            unsafe {
543                mun_runtime_find_function_definition(
544                    driver.runtime,
545                    ptr::null(),
546                    0,
547                    ptr::null_mut(),
548                    ptr::null_mut(),
549                )
550            },
551            @r###""invalid argument \'fn_name\': null pointer""###
552        );
553    }
554
555    #[test]
556    fn test_runtime_get_function_info_invalid_fn_name_encoding() {
557        let driver = TestDriver::new(
558            r#"
559        pub fn main() -> i32 { 3 }
560    "#,
561        );
562
563        let invalid_encoding = ['�', '\0'];
564        assert_error_snapshot!(
565            unsafe {
566                mun_runtime_find_function_definition(
567                    driver.runtime,
568                    invalid_encoding.as_ptr() as *const _,
569                    3,
570                    ptr::null_mut(),
571                    ptr::null_mut(),
572                )
573            },
574            @r###""invalid argument \'fn_name\': invalid UTF-8 encoded""###
575        );
576    }
577
578    #[test]
579    fn test_runtime_get_function_info_invalid_has_fn_info() {
580        let driver = TestDriver::new(
581            r#"
582        pub fn main() -> i32 { 3 }
583    "#,
584        );
585
586        let fn_name = CString::new("main").expect("Invalid function name");
587        assert_error_snapshot!(
588            unsafe {
589                mun_runtime_find_function_definition(
590                    driver.runtime,
591                    fn_name.as_ptr(),
592                    fn_name.as_bytes().len(),
593                    ptr::null_mut(),
594                    ptr::null_mut(),
595                )
596            },
597            @r###""invalid argument \'has_fn_info\': null pointer""###
598        );
599    }
600
601    #[test]
602    fn test_runtime_get_function_info_invalid_fn_info() {
603        let driver = TestDriver::new(
604            r#"
605        pub fn main() -> i32 { 3 }
606    "#,
607        );
608
609        let fn_name = CString::new("main").expect("Invalid function name");
610        let mut has_fn_info = MaybeUninit::uninit();
611        assert_error_snapshot!(
612            unsafe {
613                mun_runtime_find_function_definition(
614                    driver.runtime,
615                    fn_name.as_ptr(),
616                    fn_name.as_bytes().len(),
617                    has_fn_info.as_mut_ptr(),
618                    ptr::null_mut(),
619                )
620            },
621            @r###""invalid argument \'fn_info\': null pointer""###
622        );
623    }
624
625    #[test]
626    fn test_runtime_get_function_info_none() {
627        let driver = TestDriver::new(
628            r#"
629        pub fn main() -> i32 { 3 }
630    "#,
631        );
632
633        let fn_name = CString::new("add").expect("Invalid function name");
634        assert_getter3!(mun_runtime_find_function_definition(
635            driver.runtime,
636            fn_name.as_ptr(),
637            fn_name.as_bytes().len(),
638            has_fn_info,
639            _fn_definition,
640        ));
641        assert!(!has_fn_info);
642    }
643
644    #[test]
645    fn test_runtime_get_function_info_some() {
646        let driver = TestDriver::new(
647            r#"
648        pub fn main() -> i32 { 3 }
649    "#,
650        );
651
652        let fn_name = CString::new("main").expect("Invalid function name");
653        assert_getter3!(mun_runtime_find_function_definition(
654            driver.runtime,
655            fn_name.as_ptr(),
656            fn_name.as_bytes().len(),
657            has_fn_info,
658            _fn_definition,
659        ));
660        assert!(has_fn_info);
661    }
662
663    #[test]
664    fn test_runtime_get_type_info_by_name_invalid_type_name() {
665        let driver = TestDriver::new(
666            r#"
667            pub struct Foo;
668    "#,
669        );
670
671        assert_error_snapshot!(
672            unsafe {
673                mun_runtime_get_type_info_by_name(
674                    driver.runtime,
675                    ptr::null(),
676                    ptr::null_mut(),
677                    ptr::null_mut(),
678                )
679            },
680            @r###""invalid argument \'type_name\': null pointer""###
681        );
682    }
683
684    #[test]
685    fn test_runtime_get_type_info_by_name_invalid_type_name_encoding() {
686        let driver = TestDriver::new(
687            r#"
688            pub struct Foo;
689    "#,
690        );
691
692        let invalid_encoding = ['�', '\0'];
693        assert_error_snapshot!(
694            unsafe {
695                mun_runtime_get_type_info_by_name(
696                    driver.runtime,
697                    invalid_encoding.as_ptr() as *const _,
698                    ptr::null_mut(),
699                    ptr::null_mut(),
700                )
701            },
702            @r###""invalid argument \'type_name\': invalid UTF-8 encoded""###
703        );
704    }
705
706    #[test]
707    fn test_runtime_get_type_info_by_name_invalid_has_type_info() {
708        let driver = TestDriver::new(
709            r#"
710            pub struct Foo;
711    "#,
712        );
713
714        let type_name = CString::new("Foo").expect("Invalid type name");
715        assert_error_snapshot!(
716            unsafe {
717                mun_runtime_get_type_info_by_name(
718                    driver.runtime,
719                    type_name.as_ptr(),
720                    ptr::null_mut(),
721                    ptr::null_mut(),
722                )
723            },
724            @r###""invalid argument \'has_type_info\': null pointer""###
725        );
726    }
727
728    #[test]
729    fn test_runtime_get_type_info_by_name_invalid_type_info() {
730        let driver = TestDriver::new(
731            r#"
732            pub struct Foo;
733    "#,
734        );
735
736        let type_name = CString::new("Foo").expect("Invalid type name");
737        let mut has_type_info = false;
738        assert_error_snapshot!(
739            unsafe {
740                mun_runtime_get_type_info_by_name(
741                    driver.runtime,
742                    type_name.as_ptr(),
743                    &mut has_type_info as *mut _,
744                    ptr::null_mut(),
745                )
746            },
747            @r###""invalid argument \'type_info\': null pointer""###
748        );
749    }
750
751    #[test]
752    fn test_runtime_get_type_info_by_name_none() {
753        let driver = TestDriver::new(
754            r#"
755            pub struct Foo;
756    "#,
757        );
758
759        let type_name = CString::new("Bar").expect("Invalid type name");
760        assert_getter2!(mun_runtime_get_type_info_by_name(
761            driver.runtime,
762            type_name.as_ptr(),
763            has_type_info,
764            _type_info,
765        ));
766        assert!(!has_type_info);
767    }
768
769    #[test]
770    fn test_runtime_get_type_info_by_name_some() {
771        let driver = TestDriver::new(
772            r#"
773            pub struct Foo;
774    "#,
775        );
776
777        let type_name = CString::new("Foo").expect("Invalid type name");
778        assert_getter2!(mun_runtime_get_type_info_by_name(
779            driver.runtime,
780            type_name.as_ptr(),
781            has_type_info,
782            _type_info,
783        ));
784        assert!(has_type_info);
785    }
786
787    #[test]
788    fn test_runtime_get_type_info_by_id_invalid_type_id() {
789        let driver = TestDriver::new(
790            r#"
791            pub struct Foo;
792    "#,
793        );
794
795        assert_error_snapshot!(
796            unsafe {
797                mun_runtime_get_type_info_by_id(
798                    driver.runtime,
799                    ptr::null(),
800                    ptr::null_mut(),
801                    ptr::null_mut(),
802                )
803            },
804            @r###""invalid argument \'type_id\': null pointer""###
805        );
806    }
807
808    #[test]
809    fn test_runtime_get_type_info_by_id_invalid_has_type_info() {
810        let driver = TestDriver::new(
811            r#"
812            pub struct Foo;
813    "#,
814        );
815
816        let type_id = abi::TypeId::Concrete(abi::Guid([0; 16]));
817        assert_error_snapshot!(
818            unsafe {
819                mun_runtime_get_type_info_by_id(
820                    driver.runtime,
821                    &type_id as *const abi::TypeId,
822                    ptr::null_mut(),
823                    ptr::null_mut(),
824                )
825            },
826            @r###""invalid argument \'has_type_info\': null pointer""###
827        );
828    }
829
830    #[test]
831    fn test_runtime_get_type_info_by_id_invalid_type_info() {
832        let driver = TestDriver::new(
833            r#"
834            pub struct Foo;
835    "#,
836        );
837
838        let type_id = abi::TypeId::Concrete(abi::Guid([0; 16]));
839        let mut has_type_info = false;
840        assert_error_snapshot!(
841            unsafe {
842                mun_runtime_get_type_info_by_id(
843                    driver.runtime,
844                    &type_id as *const abi::TypeId,
845                    &mut has_type_info as *mut _,
846                    ptr::null_mut(),
847                )
848            },
849            @r###""invalid argument \'type_info\': null pointer""###
850        );
851    }
852
853    #[test]
854    fn test_runtime_get_type_info_by_id_none() {
855        let driver = TestDriver::new(
856            r#"
857            pub struct Foo;
858    "#,
859        );
860
861        let type_id = abi::TypeId::Concrete(abi::Guid([0; 16]));
862        assert_getter2!(mun_runtime_get_type_info_by_id(
863            driver.runtime,
864            &type_id as *const abi::TypeId,
865            has_type_info,
866            _type_info,
867        ));
868        assert!(!has_type_info);
869    }
870
871    #[test]
872    fn test_runtime_get_type_info_by_id_some() {
873        let driver = TestDriver::new(
874            r#"
875            pub struct Foo;
876    "#,
877        );
878
879        let type_name = CString::new("Foo").expect("Invalid type name");
880        assert_getter2!(mun_runtime_get_type_info_by_name(
881            driver.runtime,
882            type_name.as_ptr(),
883            has_type_info,
884            _type_info,
885        ));
886        assert!(has_type_info);
887    }
888
889    #[test]
890    fn test_runtime_update_invalid_updated() {
891        let driver = TestDriver::new(
892            r#"
893        pub fn main() -> i32 { 3 }
894    "#,
895        );
896
897        assert_error_snapshot!(
898            unsafe { mun_runtime_update(driver.runtime, ptr::null_mut()) },
899            @r###""invalid argument \'updated\': null pointer""###
900        );
901    }
902
903    #[test]
904    fn test_runtime_update() {
905        let driver = TestDriver::new(
906            r#"
907        pub fn main() -> i32 { 3 }
908    "#,
909        );
910
911        assert_getter1!(mun_runtime_update(driver.runtime, _updated));
912    }
913}