Skip to main content

mono_rt/types/
value.rs

1use std::ffi::c_void;
2use std::ptr;
3
4/// A typed Mono method argument.
5///
6/// Use this with [`crate::MonoMethod::invoke_with`] to pass arguments without manually building
7/// pointer arrays. The caller is still responsible for matching the argument types to the
8/// method's actual signature - Mono does not validate types at the call site.
9#[derive(Clone, Copy, Debug)]
10pub enum Value {
11    Bool(bool),
12    I32(i32),
13    I64(i64),
14    F32(f32),
15    F64(f64),
16    /// A managed object or boxed value type pointer.
17    Object(*mut c_void),
18}
19
20impl Value {
21    /// Returns a raw pointer to the underlying data, suitable for inclusion in a Mono args array.
22    pub(crate) fn as_arg_ptr(&self) -> *mut c_void {
23        match self {
24            Self::Bool(v) => ptr::from_ref(v).cast_mut().cast(),
25            Self::I32(v) => ptr::from_ref(v).cast_mut().cast(),
26            Self::I64(v) => ptr::from_ref(v).cast_mut().cast(),
27            Self::F32(v) => ptr::from_ref(v).cast_mut().cast(),
28            Self::F64(v) => ptr::from_ref(v).cast_mut().cast(),
29            Self::Object(p) => *p,
30        }
31    }
32}
33
34#[cfg(test)]
35mod tests {
36    use std::ffi::c_void;
37
38    use super::Value;
39
40    #[test]
41    fn value_is_copy() {
42        let a = Value::I32(42);
43        let b = a;
44        // both remain usable after a copy
45        let _ = a;
46        let _ = b;
47    }
48
49    #[test]
50    fn bool_true_arg_ptr_reads_back() {
51        let v = Value::Bool(true);
52        let read = unsafe { v.as_arg_ptr().cast::<bool>().read() };
53        assert!(read);
54    }
55
56    #[test]
57    fn bool_false_arg_ptr_reads_back() {
58        let v = Value::Bool(false);
59        let read = unsafe { v.as_arg_ptr().cast::<bool>().read() };
60        assert!(!read);
61    }
62
63    #[test]
64    fn i32_arg_ptr_reads_back() {
65        let v = Value::I32(-99_999);
66        let read = unsafe { v.as_arg_ptr().cast::<i32>().read() };
67        assert_eq!(read, -99_999);
68    }
69
70    #[test]
71    fn i64_arg_ptr_reads_back() {
72        let v = Value::I64(i64::MAX);
73        let read = unsafe { v.as_arg_ptr().cast::<i64>().read() };
74        assert_eq!(read, i64::MAX);
75    }
76
77    #[test]
78    fn f32_arg_ptr_reads_back() {
79        let v = Value::F32(1.5_f32);
80        let read = unsafe { v.as_arg_ptr().cast::<f32>().read() };
81        assert!((read - 1.5_f32).abs() < f32::EPSILON);
82    }
83
84    #[test]
85    fn f64_arg_ptr_reads_back() {
86        let v = Value::F64(std::f64::consts::PI);
87        let read = unsafe { v.as_arg_ptr().cast::<f64>().read() };
88        assert!((read - std::f64::consts::PI).abs() < f64::EPSILON);
89    }
90
91    #[test]
92    fn object_arg_ptr_returns_stored_pointer() {
93        let raw: *mut c_void = 0xDEAD_BEEF_usize as *mut _;
94        let v = Value::Object(raw);
95        assert_eq!(v.as_arg_ptr(), raw);
96    }
97
98    #[test]
99    fn object_null_arg_ptr_is_null() {
100        let v = Value::Object(std::ptr::null_mut());
101        assert!(v.as_arg_ptr().is_null());
102    }
103
104    #[test]
105    fn debug_output_includes_variant_names() {
106        assert!(format!("{:?}", Value::Bool(true)).contains("Bool"));
107        assert!(format!("{:?}", Value::I32(0)).contains("I32"));
108        assert!(format!("{:?}", Value::I64(0)).contains("I64"));
109        assert!(format!("{:?}", Value::F32(0.0)).contains("F32"));
110        assert!(format!("{:?}", Value::F64(0.0)).contains("F64"));
111        assert!(format!("{:?}", Value::Object(std::ptr::null_mut())).contains("Object"));
112    }
113}