tinywasm/reference/
managed.rs1use 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 AnyRef,
84 EqRef,
86 I31Ref,
88 StructRef,
90 ArrayRef,
92 ExternRef,
94 ExnRef,
96}
97
98impl AnyRef {
99 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 pub fn as_i31(&self) -> Option<I31Ref> {
107 (self.0.kind == ReferentKind::I31).then(|| I31Ref(self.0.clone()))
108 }
109
110 pub fn as_struct(&self) -> Option<StructRef> {
112 (self.0.kind == ReferentKind::Struct).then(|| StructRef(self.0.clone()))
113 }
114
115 pub fn as_array(&self) -> Option<ArrayRef> {
117 (self.0.kind == ReferentKind::Array).then(|| ArrayRef(self.0.clone()))
118 }
119
120 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 pub fn to_any(&self) -> AnyRef { AnyRef(self.0.clone()) }
131 }
132 )*};
133}
134
135impl_to_any!(EqRef, I31Ref, StructRef, ArrayRef, ExternRef);
136
137#[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#[derive(Clone, PartialEq, Eq)]
163#[cfg_attr(feature = "debug", derive(Debug))]
164pub enum RefValue {
165 Null,
167 Func(FuncRef),
169 Extern(ExternRef),
171 Any(AnyRef),
173 Exn(ExnRef),
175}
176
177impl ExternRef {
178 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 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 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}