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}