Skip to main content

tinywasm_types/
reference.rs

1/// An abstract WebAssembly heap type.
2///
3/// This contains exactly the abstract heap types in core Wasm 3.0.
4///
5/// See <https://webassembly.github.io/spec/core/syntax/types.html#syntax-absheaptype>
6#[repr(u8)]
7#[derive(Clone, Copy, PartialEq, Eq, Hash)]
8#[cfg_attr(feature = "debug", derive(Debug))]
9#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
10pub enum AbstractHeapType {
11    Any,
12    Eq,
13    I31,
14    Struct,
15    Array,
16    None,
17
18    Func,
19    NoFunc,
20
21    Exn,
22    NoExn,
23
24    Extern,
25    NoExtern,
26}
27
28/// A WebAssembly reference type.
29///
30/// Packed as:
31///
32/// ```text
33/// [nullable:1 concrete:1 payload:30]
34/// ```
35///
36/// For concrete types, `payload` is a module type index before instantiation
37/// and a canonical store type address at runtime.
38/// Otherwise, it is an [`AbstractHeapType`].
39///
40/// See <https://webassembly.github.io/spec/core/syntax/types.html#syntax-reftype>
41#[derive(Clone, Copy, PartialEq, Eq, Hash)]
42#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
43pub struct RefType(u32);
44
45#[cfg(feature = "debug")]
46impl core::fmt::Debug for RefType {
47    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
48        if self.is_concrete() {
49            write!(f, "concrete({})", self.type_index().unwrap())
50        } else {
51            write!(f, "abstract({:?})", self.abstract_heap_type().unwrap())
52        }
53    }
54}
55
56impl RefType {
57    const NULLABLE: u32 = 1 << 31;
58    const CONCRETE: u32 = 1 << 30;
59    const PAYLOAD_MASK: u32 = Self::CONCRETE - 1;
60
61    pub const FUNCREF: Self = Self::new_abstract(true, AbstractHeapType::Func);
62    pub const EXTERNREF: Self = Self::new_abstract(true, AbstractHeapType::Extern);
63    pub const EXNREF: Self = Self::new_abstract(true, AbstractHeapType::Exn);
64
65    #[inline]
66    #[doc(hidden)]
67    pub const fn to_bits(self) -> u32 {
68        self.0
69    }
70
71    #[inline]
72    #[doc(hidden)]
73    pub const fn from_bits(bits: u32) -> Option<Self> {
74        let ty = Self(bits);
75        if ty.is_concrete() || ty.abstract_heap_type().is_some() { Some(ty) } else { None }
76    }
77
78    #[inline]
79    pub const fn new_abstract(nullable: bool, heap_type: AbstractHeapType) -> Self {
80        Self((nullable as u32) << 31 | heap_type as u32)
81    }
82
83    #[inline]
84    pub const fn new_concrete(nullable: bool, type_index: u32) -> Self {
85        assert!(type_index <= Self::PAYLOAD_MASK, "type index is too large for a reference type");
86        Self(((nullable as u32) << 31) | Self::CONCRETE | type_index)
87    }
88
89    #[inline]
90    pub const fn is_nullable(self) -> bool {
91        self.0 & Self::NULLABLE != 0
92    }
93
94    #[inline]
95    pub const fn is_concrete(self) -> bool {
96        self.0 & Self::CONCRETE != 0
97    }
98
99    #[inline]
100    pub const fn type_index(self) -> Option<u32> {
101        if self.is_concrete() { Some(self.0 & Self::PAYLOAD_MASK) } else { None }
102    }
103
104    #[inline]
105    pub const fn abstract_heap_type(self) -> Option<AbstractHeapType> {
106        if self.is_concrete() {
107            return None;
108        }
109
110        match self.0 & Self::PAYLOAD_MASK {
111            0 => Some(AbstractHeapType::Any),
112            1 => Some(AbstractHeapType::Eq),
113            2 => Some(AbstractHeapType::I31),
114            3 => Some(AbstractHeapType::Struct),
115            4 => Some(AbstractHeapType::Array),
116            5 => Some(AbstractHeapType::None),
117            6 => Some(AbstractHeapType::Func),
118            7 => Some(AbstractHeapType::NoFunc),
119            8 => Some(AbstractHeapType::Exn),
120            9 => Some(AbstractHeapType::NoExn),
121            10 => Some(AbstractHeapType::Extern),
122            11 => Some(AbstractHeapType::NoExtern),
123            _ => None,
124        }
125    }
126
127    #[inline]
128    pub const fn with_nullability(self, nullable: bool) -> Self {
129        Self((self.0 & !Self::NULLABLE) | ((nullable as u32) << 31))
130    }
131
132    #[inline]
133    pub const fn is_func(self) -> bool {
134        matches!(self.abstract_heap_type(), Some(AbstractHeapType::Func | AbstractHeapType::NoFunc))
135    }
136
137    #[inline]
138    pub const fn is_extern(self) -> bool {
139        matches!(self.abstract_heap_type(), Some(AbstractHeapType::Extern | AbstractHeapType::NoExtern))
140    }
141
142    #[inline]
143    pub const fn is_exn(self) -> bool {
144        matches!(self.abstract_heap_type(), Some(AbstractHeapType::Exn | AbstractHeapType::NoExn))
145    }
146}