soroban_wasmi/func/
funcref.rs

1use super::Func;
2use crate::{core::UntypedVal, reftype::Transposer};
3
4/// A nullable [`Func`] reference.
5#[derive(Debug, Default, Copy, Clone)]
6#[repr(transparent)]
7pub struct FuncRef {
8    inner: Option<Func>,
9}
10
11impl From<Func> for FuncRef {
12    fn from(func: Func) -> Self {
13        Self::new(func)
14    }
15}
16
17#[test]
18fn funcref_sizeof() {
19    // These assertions are important in order to convert `FuncRef`
20    // from and to 64-bit `UntypedValue` instances.
21    //
22    // The following equation must be true:
23    //     size_of(Func) == size_of(UntypedValue) == size_of(FuncRef)
24    use core::mem::size_of;
25    assert_eq!(size_of::<Func>(), size_of::<u64>());
26    assert_eq!(size_of::<Func>(), size_of::<UntypedVal>());
27    assert_eq!(size_of::<Func>(), size_of::<FuncRef>());
28}
29
30#[test]
31fn funcref_null_to_zero() {
32    assert_eq!(UntypedVal::from(FuncRef::null()), UntypedVal::from(0));
33    assert!(FuncRef::from(UntypedVal::from(0)).is_null());
34}
35
36impl From<UntypedVal> for FuncRef {
37    fn from(untyped: UntypedVal) -> Self {
38        // Safety: This union access is safe since there are no invalid
39        //         bit patterns for [`FuncRef`] instances. Therefore
40        //         this operation cannot produce invalid [`FuncRef`]
41        //         instances even though the input [`UntypedVal`]
42        //         was modified arbitrarily.
43        unsafe { <Transposer<Self>>::from(untyped).reftype }.canonicalize()
44    }
45}
46
47impl From<FuncRef> for UntypedVal {
48    fn from(funcref: FuncRef) -> Self {
49        let funcref = funcref.canonicalize();
50        // Safety: This operation is safe since there are no invalid
51        //         bit patterns for [`UntypedVal`] instances. Therefore
52        //         this operation cannot produce invalid [`UntypedVal`]
53        //         instances even if it was possible to arbitrarily modify
54        //         the input [`FuncRef`] instance.
55        Self::from(unsafe { <Transposer<FuncRef>>::new(funcref).value })
56    }
57}
58
59impl FuncRef {
60    /// Returns `true` if [`FuncRef`] is `null`.
61    pub fn is_null(&self) -> bool {
62        self.inner.is_none()
63    }
64
65    /// Canonicalize `self` so that all `null` values have the same representation.
66    ///
67    /// # Note
68    ///
69    /// The underlying issue is that `FuncRef` has many possible values for the
70    /// `null` value. However, to simplify operating on encoded `FuncRef` instances
71    /// (encoded as `UntypedValue`) we want it to encode to exactly one `null`
72    /// value. The most trivial of all possible `null` values is `0_u64`, therefore
73    /// we canonicalize all `null` values to be represented by `0_u64`.
74    fn canonicalize(self) -> Self {
75        if self.is_null() {
76            // Safety: This is safe since `0u64` can be bit
77            //         interpreted as a valid `FuncRef` value.
78            return unsafe { <Transposer<Self>>::null().reftype };
79        }
80        self
81    }
82
83    /// Creates a new [`FuncRef`].
84    ///
85    /// # Examples
86    ///
87    /// ```rust
88    /// # use wasmi::{Func, FuncRef, Store, Engine};
89    /// # let engine = Engine::default();
90    /// # let mut store = <Store<()>>::new(&engine, ());
91    /// assert!(FuncRef::new(None).is_null());
92    /// assert!(FuncRef::new(Func::wrap(&mut store, |x: i32| x)).func().is_some());
93    /// ```
94    pub fn new(nullable_func: impl Into<Option<Func>>) -> Self {
95        Self {
96            inner: nullable_func.into(),
97        }
98        .canonicalize()
99    }
100
101    /// Returns the inner [`Func`] if [`FuncRef`] is not `null`.
102    ///
103    /// Otherwise returns `None`.
104    pub fn func(&self) -> Option<&Func> {
105        self.inner.as_ref()
106    }
107
108    /// Creates a `null` [`FuncRef`].
109    pub fn null() -> Self {
110        Self::new(None).canonicalize()
111    }
112}