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