Skip to main content

tinywasm/reference/
managed.rs

1use tinywasm_types::Shared;
2
3use crate::interpreter::ValueRef;
4use crate::shared::{AtomicU32, Ordering};
5use crate::{Result, Store, Trap};
6
7#[derive(Clone, Copy, PartialEq, Eq, Hash)]
8pub(crate) struct StoreId(u32);
9
10impl StoreId {
11    pub(crate) fn fresh() -> Self {
12        static NEXT: AtomicU32 = AtomicU32::new(0);
13        let id = NEXT.fetch_add(1, Ordering::Relaxed);
14        assert_ne!(id, u32::MAX, "Store identity space exhausted");
15        StoreId(id)
16    }
17}
18
19#[cfg(feature = "debug")]
20impl core::fmt::Debug for StoreId {
21    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
22        f.write_str("StoreId(..)")
23    }
24}
25
26#[derive(Clone, Copy, PartialEq, Eq)]
27#[cfg_attr(feature = "debug", derive(Debug))]
28pub(crate) enum ReferentKind {
29    I31,
30    HostExtern,
31    Struct,
32    Array,
33    Exception,
34}
35
36#[derive(Clone)]
37pub(crate) struct RootedItem {
38    pub(crate) store: StoreId,
39    pub(crate) value: ValueRef,
40    pub(crate) kind: ReferentKind,
41    pub(crate) _token: Option<Shared<()>>,
42}
43
44impl PartialEq for RootedItem {
45    fn eq(&self, other: &Self) -> bool {
46        self.kind == other.kind
47            && self.value == other.value
48            && (self.kind == ReferentKind::I31 || self.store == other.store)
49    }
50}
51
52impl Eq for RootedItem {}
53
54#[cfg(feature = "debug")]
55impl core::fmt::Debug for RootedItem {
56    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
57        f.debug_struct("Reference").field("kind", &self.kind).finish_non_exhaustive()
58    }
59}
60
61pub(crate) trait StoredRef: Clone + Sized {
62    fn from_rooted_item(item: RootedItem) -> Self;
63    fn rooted_item(&self) -> &RootedItem;
64}
65
66macro_rules! reference_types {
67    ($($(#[$meta:meta])* $name:ident),* $(,)?) => {$($(
68        #[$meta]
69    )*
70        #[derive(Clone, PartialEq, Eq)]
71        #[cfg_attr(feature = "debug", derive(Debug))]
72        pub struct $name(RootedItem);
73
74        impl StoredRef for $name {
75            fn from_rooted_item(item: RootedItem) -> Self { Self(item) }
76            fn rooted_item(&self) -> &RootedItem { &self.0 }
77        }
78    )*};
79}
80
81reference_types! {
82    /// An owned WebAssembly `anyref`.
83    AnyRef,
84    /// An owned WebAssembly `eqref`.
85    EqRef,
86    /// An owned WebAssembly `i31ref`.
87    I31Ref,
88    /// An owned WebAssembly `structref`.
89    StructRef,
90    /// An owned WebAssembly `arrayref`.
91    ArrayRef,
92    /// An owned WebAssembly `externref`.
93    ExternRef,
94    /// An owned WebAssembly `exnref`.
95    ExnRef,
96}
97
98impl AnyRef {
99    /// Returns this reference as an `eqref` when its referent is comparable.
100    pub fn as_eq(&self) -> Option<EqRef> {
101        matches!(self.0.kind, ReferentKind::I31 | ReferentKind::Struct | ReferentKind::Array)
102            .then(|| EqRef(self.0.clone()))
103    }
104
105    /// Returns this reference as an `i31ref` when it contains an i31.
106    pub fn as_i31(&self) -> Option<I31Ref> {
107        (self.0.kind == ReferentKind::I31).then(|| I31Ref(self.0.clone()))
108    }
109
110    /// Returns this reference as a `structref` when it refers to a struct.
111    pub fn as_struct(&self) -> Option<StructRef> {
112        (self.0.kind == ReferentKind::Struct).then(|| StructRef(self.0.clone()))
113    }
114
115    /// Returns this reference as an `arrayref` when it refers to an array.
116    pub fn as_array(&self) -> Option<ArrayRef> {
117        (self.0.kind == ReferentKind::Array).then(|| ArrayRef(self.0.clone()))
118    }
119
120    /// Returns this reference with the `externref` view.
121    pub fn to_extern(&self) -> ExternRef {
122        ExternRef(self.0.clone())
123    }
124}
125
126macro_rules! impl_to_any {
127    ($($ty:ty),* $(,)?) => {$(
128        impl $ty {
129            /// Returns this reference with the `anyref` view.
130            pub fn to_any(&self) -> AnyRef { AnyRef(self.0.clone()) }
131        }
132    )*};
133}
134
135impl_to_any!(EqRef, I31Ref, StructRef, ArrayRef, ExternRef);
136
137/// A Store-aware WebAssembly function reference.
138#[derive(Clone, Copy, PartialEq, Eq, Hash)]
139pub struct FuncRef {
140    store: StoreId,
141    addr: u32,
142}
143
144impl FuncRef {
145    pub(crate) const fn new(store: StoreId, addr: u32) -> Self {
146        Self { store, addr }
147    }
148
149    pub(crate) fn addr(self, store: StoreId) -> Option<u32> {
150        (self.store == store).then_some(self.addr)
151    }
152}
153
154#[cfg(feature = "debug")]
155impl core::fmt::Debug for FuncRef {
156    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
157        f.write_str("FuncRef(..)")
158    }
159}
160
161/// A host-facing WebAssembly reference value.
162#[derive(Clone, PartialEq, Eq)]
163#[cfg_attr(feature = "debug", derive(Debug))]
164pub enum RefValue {
165    /// A null reference.
166    Null,
167    /// A function reference.
168    Func(FuncRef),
169    /// An external reference.
170    Extern(ExternRef),
171    /// A reference in the `any` hierarchy.
172    Any(AnyRef),
173    /// An exception reference.
174    Exn(ExnRef),
175}
176
177impl ExternRef {
178    /// Creates an external reference containing a host-defined key.
179    ///
180    /// Keys must be in `0..=(1 << 30) - 2`.
181    pub fn try_new(store: &mut Store, key: u32) -> Result<Self> {
182        let value = ValueRef::try_from_host_any(key).ok_or(Trap::InvalidReference)?;
183        store.root_reference(value, ReferentKind::HostExtern)
184    }
185
186    /// Returns the host-defined key stored in this external reference.
187    ///
188    /// Returns an error for a reference produced by `extern.convert_any` from a
189    /// guest GC object or when `store` does not own the reference.
190    pub fn key(&self, store: &Store) -> Result<u32> {
191        let value = store.resolve_ref(self)?;
192        let addr = value.addr().filter(|_| value.is_host_any()).ok_or(Trap::InvalidReference)?;
193        Ok(addr & !(1 << 30))
194    }
195}
196
197impl I31Ref {
198    /// Creates a signed WebAssembly i31 reference.
199    ///
200    /// Values must be in `-2^30..=2^30 - 1`.
201    pub fn try_new(store: &mut Store, value: i32) -> Result<Self> {
202        if !(-(1 << 30)..(1 << 30)).contains(&value) {
203            return Err(Trap::InvalidReference.into());
204        }
205        store.root_reference(ValueRef::from_i31(value), ReferentKind::I31)
206    }
207}
208
209#[cfg(test)]
210mod tests {
211    extern crate std;
212    use super::*;
213
214    #[test]
215    fn host_reference_key_fits_internal_encoding() {
216        assert!(ValueRef::try_from_host_any((1 << 30) - 2).is_some());
217        assert!(ValueRef::try_from_host_any((1 << 30) - 1).is_none());
218    }
219}