Skip to main content

tinywasm_types/
value.rs

1use core::fmt::Debug;
2
3use crate::{ConstInstruction, ExternAddr, FuncAddr};
4
5/// A WebAssembly value.
6///
7/// See <https://webassembly.github.io/spec/core/syntax/types.html#value-types>
8#[derive(Clone, Copy, PartialEq)]
9pub enum WasmValue {
10    // Num types
11    /// A 32-bit integer.
12    I32(i32),
13    /// A 64-bit integer.
14    I64(i64),
15    /// A 32-bit float.
16    F32(f32),
17    /// A 64-bit float.
18    F64(f64),
19    // /// A 128-bit vector
20    V128(i128),
21    RefExtern(ExternRef),
22    RefFunc(FuncRef),
23}
24
25impl Debug for WasmValue {
26    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
27        match self {
28            Self::I32(i) => write!(f, "i32({i})"),
29            Self::I64(i) => write!(f, "i64({i})"),
30            Self::F32(i) => write!(f, "f32({i})"),
31            Self::F64(i) => write!(f, "f64({i})"),
32            Self::V128(i) => write!(f, "v128({i:?})"),
33            #[cfg(feature = "debug")]
34            Self::RefExtern(i) => write!(f, "ref({i:?})"),
35            #[cfg(feature = "debug")]
36            Self::RefFunc(i) => write!(f, "func({i:?})"),
37            #[cfg(not(feature = "debug"))]
38            Self::RefExtern(_) => write!(f, "ref()"),
39            #[cfg(not(feature = "debug"))]
40            Self::RefFunc(_) => write!(f, "func()"),
41        }
42    }
43}
44
45const NULL_REF: u32 = u32::MAX;
46
47#[derive(Clone, Copy, PartialEq, Eq)]
48pub struct ExternRef(u32);
49
50#[derive(Clone, Copy, PartialEq, Eq)]
51pub struct FuncRef(u32);
52
53#[cfg(feature = "debug")]
54impl Debug for ExternRef {
55    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
56        match self.addr() {
57            Some(addr) => write!(f, "extern({addr:?})"),
58            None => write!(f, "extern(null)"),
59        }
60    }
61}
62
63#[cfg(feature = "debug")]
64impl Debug for FuncRef {
65    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
66        match self.addr() {
67            Some(addr) => write!(f, "func({addr:?})"),
68            None => write!(f, "func(null)"),
69        }
70    }
71}
72
73impl FuncRef {
74    #[inline]
75    /// Create a new [`FuncRef`] from a [`FuncAddr`].
76    pub const fn new(addr: Option<FuncAddr>) -> Self {
77        match addr {
78            Some(addr) => Self(addr),
79            None => Self::null(),
80        }
81    }
82
83    #[inline]
84    /// Create a null [`FuncRef`].
85    pub const fn null() -> Self {
86        Self(NULL_REF)
87    }
88
89    #[inline]
90    /// Check if the [`FuncRef`] is null.
91    pub const fn is_null(&self) -> bool {
92        self.0 == NULL_REF
93    }
94
95    #[inline]
96    /// Get the [`FuncAddr`] from the [`FuncRef`].
97    pub const fn addr(&self) -> Option<FuncAddr> {
98        if self.is_null() { None } else { Some(self.0) }
99    }
100
101    #[inline]
102    #[doc(hidden)]
103    pub const fn from_raw(raw: u32) -> Self {
104        Self(raw)
105    }
106
107    #[inline]
108    #[doc(hidden)]
109    pub const fn raw(&self) -> u32 {
110        self.0
111    }
112}
113
114impl ExternRef {
115    #[inline]
116    /// Create a new [`ExternRef`] from an [`ExternAddr`].
117    /// Should only be used by the runtime.
118    pub const fn new(addr: Option<ExternAddr>) -> Self {
119        match addr {
120            Some(addr) => Self(addr),
121            None => Self::null(),
122        }
123    }
124
125    /// Create a null [`ExternRef`].
126    #[inline]
127    pub const fn null() -> Self {
128        Self(NULL_REF)
129    }
130
131    /// Check if the [`ExternRef`] is null.
132    #[inline]
133    pub const fn is_null(&self) -> bool {
134        self.0 == NULL_REF
135    }
136
137    /// Get the [`ExternAddr`] from the [`ExternRef`].
138    #[inline]
139    pub const fn addr(&self) -> Option<ExternAddr> {
140        if self.is_null() { None } else { Some(self.0) }
141    }
142
143    #[inline]
144    #[doc(hidden)]
145    pub const fn from_raw(raw: u32) -> Self {
146        Self(raw)
147    }
148
149    #[inline]
150    #[doc(hidden)]
151    pub const fn raw(&self) -> u32 {
152        self.0
153    }
154}
155
156impl WasmValue {
157    #[inline]
158    /// Get the matching [`ConstInstruction`] for this value.
159    pub fn const_instr(&self) -> alloc::boxed::Box<[ConstInstruction]> {
160        alloc::boxed::Box::new([match self {
161            Self::I32(i) => ConstInstruction::I32Const(*i),
162            Self::I64(i) => ConstInstruction::I64Const(*i),
163            Self::F32(i) => ConstInstruction::F32Const(*i),
164            Self::F64(i) => ConstInstruction::F64Const(*i),
165            Self::V128(i) => ConstInstruction::V128Const(*i),
166            Self::RefFunc(i) => ConstInstruction::RefFunc(i.addr()),
167            Self::RefExtern(i) => ConstInstruction::RefExtern(i.addr()),
168        }])
169    }
170
171    #[inline]
172    /// Get the default value for a given type.
173    pub const fn default_for(ty: WasmType) -> Self {
174        match ty {
175            WasmType::I32 => Self::I32(0),
176            WasmType::I64 => Self::I64(0),
177            WasmType::F32 => Self::F32(0.0),
178            WasmType::F64 => Self::F64(0.0),
179            WasmType::V128 => Self::V128(0),
180            WasmType::RefFunc => Self::RefFunc(FuncRef::null()),
181            WasmType::RefExtern => Self::RefExtern(ExternRef::null()),
182        }
183    }
184
185    #[inline]
186    /// Check if two values are equal, ignoring differences in NaN values.
187    pub fn eq_loose(&self, other: &Self) -> bool {
188        match (self, other) {
189            (Self::I32(a), Self::I32(b)) => a == b,
190            (Self::I64(a), Self::I64(b)) => a == b,
191            (Self::V128(a), Self::V128(b)) => {
192                let a_bytes = a.to_le_bytes();
193                let b_bytes = b.to_le_bytes();
194                a_bytes == b_bytes || Self::v128_nan_eq(a_bytes, b_bytes)
195            }
196            (Self::RefExtern(addr), Self::RefExtern(addr2)) => addr == addr2,
197            (Self::RefFunc(addr), Self::RefFunc(addr2)) => addr == addr2,
198            (Self::F32(a), Self::F32(b)) => {
199                if a.is_nan() && b.is_nan() {
200                    true
201                } else {
202                    a.to_bits() == b.to_bits()
203                }
204            }
205            (Self::F64(a), Self::F64(b)) => {
206                if a.is_nan() && b.is_nan() {
207                    true
208                } else {
209                    a.to_bits() == b.to_bits()
210                }
211            }
212            _ => false,
213        }
214    }
215
216    fn v128_nan_eq(a: [u8; 16], b: [u8; 16]) -> bool {
217        let a_f32x4: [f32; 4] = [
218            f32::from_le_bytes([a[0], a[1], a[2], a[3]]),
219            f32::from_le_bytes([a[4], a[5], a[6], a[7]]),
220            f32::from_le_bytes([a[8], a[9], a[10], a[11]]),
221            f32::from_le_bytes([a[12], a[13], a[14], a[15]]),
222        ];
223        let b_f32x4: [f32; 4] = [
224            f32::from_le_bytes([b[0], b[1], b[2], b[3]]),
225            f32::from_le_bytes([b[4], b[5], b[6], b[7]]),
226            f32::from_le_bytes([b[8], b[9], b[10], b[11]]),
227            f32::from_le_bytes([b[12], b[13], b[14], b[15]]),
228        ];
229
230        let all_nan_match = a_f32x4.iter().zip(b_f32x4.iter()).all(|(x, y)| {
231            if x.is_nan() && y.is_nan() {
232                true
233            } else if x.is_nan() || y.is_nan() {
234                false
235            } else {
236                x.to_bits() == y.to_bits()
237            }
238        });
239
240        if all_nan_match && a_f32x4.iter().any(|x| x.is_nan()) {
241            return true;
242        }
243
244        let a_f64x2: [f64; 2] = [
245            f64::from_le_bytes([a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]]),
246            f64::from_le_bytes([a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]]),
247        ];
248        let b_f64x2: [f64; 2] = [
249            f64::from_le_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]),
250            f64::from_le_bytes([b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]]),
251        ];
252
253        a_f64x2.iter().zip(b_f64x2.iter()).all(|(x, y)| {
254            if x.is_nan() && y.is_nan() {
255                true
256            } else if x.is_nan() || y.is_nan() {
257                false
258            } else {
259                x.to_bits() == y.to_bits()
260            }
261        }) && a_f64x2.iter().any(|x| x.is_nan())
262    }
263
264    /// Return the `i32` from a `WasmValue`, if it is an `I32`.
265    pub const fn as_i32(&self) -> Option<i32> {
266        match self {
267            Self::I32(i) => Some(*i),
268            _ => None,
269        }
270    }
271
272    /// Return the `i64` from a `WasmValue`, if it is an `I64`.
273    pub const fn as_i64(&self) -> Option<i64> {
274        match self {
275            Self::I64(i) => Some(*i),
276            _ => None,
277        }
278    }
279
280    /// Return the `f32` from a `WasmValue`, if it is a `F32`.
281    pub const fn as_f32(&self) -> Option<f32> {
282        match self {
283            Self::F32(i) => Some(*i),
284            _ => None,
285        }
286    }
287
288    /// Return the `f64` from a `WasmValue`, if it is a `F64`.
289    pub const fn as_f64(&self) -> Option<f64> {
290        match self {
291            Self::F64(i) => Some(*i),
292            _ => None,
293        }
294    }
295
296    /// Return the `i128` from a `WasmValue`, if it is a `V128`.
297    pub const fn as_v128(&self) -> Option<i128> {
298        match self {
299            Self::V128(i) => Some(*i),
300            _ => None,
301        }
302    }
303
304    /// Return the [[`ExternRef`]] from a `WasmValue`, if it is one
305    pub const fn as_ref_extern(&self) -> Option<ExternRef> {
306        match self {
307            Self::RefExtern(ref_extern) => Some(*ref_extern),
308            _ => None,
309        }
310    }
311
312    /// Return the [`FuncRef`] from a `WasmValue`, if it is one
313    pub const fn as_ref_func(&self) -> Option<FuncRef> {
314        match self {
315            Self::RefFunc(ref_func) => Some(*ref_func),
316            _ => None,
317        }
318    }
319}
320
321impl From<&WasmValue> for WasmType {
322    #[inline]
323    fn from(value: &WasmValue) -> Self {
324        match value {
325            WasmValue::I32(_) => WasmType::I32,
326            WasmValue::I64(_) => WasmType::I64,
327            WasmValue::F32(_) => WasmType::F32,
328            WasmValue::F64(_) => WasmType::F64,
329            WasmValue::V128(_) => WasmType::V128,
330            WasmValue::RefExtern(_) => WasmType::RefExtern,
331            WasmValue::RefFunc(_) => WasmType::RefFunc,
332        }
333    }
334}
335
336impl From<WasmValue> for WasmType {
337    #[inline]
338    fn from(value: WasmValue) -> Self {
339        Self::from(&value)
340    }
341}
342
343/// Type of a WebAssembly value.
344#[derive(Clone, Copy, PartialEq, Eq)]
345#[cfg_attr(feature = "debug", derive(Debug))]
346#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
347pub enum WasmType {
348    /// A 32-bit integer.
349    I32,
350    /// A 64-bit integer.
351    I64,
352    /// A 32-bit float.
353    F32,
354    /// A 64-bit float.
355    F64,
356    /// A 128-bit vector
357    V128,
358    /// A reference to a function.
359    RefFunc,
360    /// A reference to an external value.
361    RefExtern,
362}
363
364impl WasmType {
365    #[inline]
366    pub const fn default_value(&self) -> WasmValue {
367        WasmValue::default_for(*self)
368    }
369
370    #[inline]
371    pub const fn is_simd(&self) -> bool {
372        matches!(self, Self::V128)
373    }
374}
375
376macro_rules! impl_conversion_for_wasmvalue {
377    ($($t:ty => $variant:ident),*) => {
378        $(
379            impl From<$t> for WasmValue {
380                #[inline]
381                fn from(i: $t) -> Self {
382                    Self::$variant(i)
383                }
384            }
385
386            impl TryFrom<WasmValue> for $t {
387                type Error = ();
388
389                #[inline]
390                fn try_from(value: WasmValue) -> Result<Self, Self::Error> {
391                    if let WasmValue::$variant(i) = value { Ok(i) } else { Err(()) }
392                }
393            }
394        )*
395    }
396}
397
398impl_conversion_for_wasmvalue! { i32 => I32, i64 => I64, f32 => F32, f64 => F64, i128 => V128, ExternRef => RefExtern, FuncRef => RefFunc }