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(u128),
21
22    RefExtern(ExternAddr),
23    RefFunc(FuncAddr),
24    RefNull(ValType),
25}
26
27impl WasmValue {
28    #[inline]
29    pub fn const_instr(&self) -> ConstInstruction {
30        match self {
31            Self::I32(i) => ConstInstruction::I32Const(*i),
32            Self::I64(i) => ConstInstruction::I64Const(*i),
33            Self::F32(i) => ConstInstruction::F32Const(*i),
34            Self::F64(i) => ConstInstruction::F64Const(*i),
35            Self::RefFunc(i) => ConstInstruction::RefFunc(*i),
36            Self::RefNull(ty) => ConstInstruction::RefNull(*ty),
37
38            // Self::RefExtern(addr) => ConstInstruction::RefExtern(*addr),
39            _ => unimplemented!("no const_instr for {:?}", self),
40        }
41    }
42
43    /// Get the default value for a given type.
44    #[inline]
45    pub fn default_for(ty: ValType) -> Self {
46        match ty {
47            ValType::I32 => Self::I32(0),
48            ValType::I64 => Self::I64(0),
49            ValType::F32 => Self::F32(0.0),
50            ValType::F64 => Self::F64(0.0),
51            ValType::V128 => Self::V128(0),
52            ValType::RefFunc => Self::RefNull(ValType::RefFunc),
53            ValType::RefExtern => Self::RefNull(ValType::RefExtern),
54        }
55    }
56
57    #[inline]
58    pub fn eq_loose(&self, other: &Self) -> bool {
59        match (self, other) {
60            (Self::I32(a), Self::I32(b)) => a == b,
61            (Self::I64(a), Self::I64(b)) => a == b,
62            (Self::RefNull(v), Self::RefNull(v2)) => v == v2,
63            (Self::RefExtern(addr), Self::RefExtern(addr2)) => addr == addr2,
64            (Self::RefFunc(addr), Self::RefFunc(addr2)) => addr == addr2,
65            (Self::F32(a), Self::F32(b)) => {
66                if a.is_nan() && b.is_nan() {
67                    true // Both are NaN, treat them as equal
68                } else {
69                    a.to_bits() == b.to_bits()
70                }
71            }
72            (Self::F64(a), Self::F64(b)) => {
73                if a.is_nan() && b.is_nan() {
74                    true // Both are NaN, treat them as equal
75                } else {
76                    a.to_bits() == b.to_bits()
77                }
78            }
79            _ => false,
80        }
81    }
82}
83
84#[cold]
85fn cold() {}
86
87impl Debug for WasmValue {
88    fn fmt(&self, f: &mut alloc::fmt::Formatter<'_>) -> alloc::fmt::Result {
89        match self {
90            WasmValue::I32(i) => write!(f, "i32({i})"),
91            WasmValue::I64(i) => write!(f, "i64({i})"),
92            WasmValue::F32(i) => write!(f, "f32({i})"),
93            WasmValue::F64(i) => write!(f, "f64({i})"),
94            WasmValue::V128(i) => write!(f, "v128({i:?})"),
95            WasmValue::RefExtern(addr) => write!(f, "ref.extern({addr:?})"),
96            WasmValue::RefFunc(addr) => write!(f, "ref.func({addr:?})"),
97            WasmValue::RefNull(ty) => write!(f, "ref.null({ty:?})"),
98        }
99    }
100}
101
102impl WasmValue {
103    /// Get the type of a [`WasmValue`]
104    #[inline]
105    pub fn val_type(&self) -> ValType {
106        match self {
107            Self::I32(_) => ValType::I32,
108            Self::I64(_) => ValType::I64,
109            Self::F32(_) => ValType::F32,
110            Self::F64(_) => ValType::F64,
111            Self::V128(_) => ValType::V128,
112            Self::RefExtern(_) => ValType::RefExtern,
113            Self::RefFunc(_) => ValType::RefFunc,
114            Self::RefNull(ty) => *ty,
115        }
116    }
117}
118
119/// Type of a WebAssembly value.
120#[derive(Debug, Clone, Copy, PartialEq, Eq)]
121#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))]
122pub enum ValType {
123    /// A 32-bit integer.
124    I32,
125    /// A 64-bit integer.
126    I64,
127    /// A 32-bit float.
128    F32,
129    /// A 64-bit float.
130    F64,
131    /// A 128-bit vector
132    V128,
133    /// A reference to a function.
134    RefFunc,
135    /// A reference to an external value.
136    RefExtern,
137}
138
139impl ValType {
140    #[inline]
141    pub fn default_value(&self) -> WasmValue {
142        WasmValue::default_for(*self)
143    }
144
145    #[inline]
146    pub fn is_simd(&self) -> bool {
147        matches!(self, ValType::V128)
148    }
149}
150
151macro_rules! impl_conversion_for_wasmvalue {
152    ($($t:ty => $variant:ident),*) => {
153        $(
154            // Implementing From<$t> for WasmValue
155            impl From<$t> for WasmValue {
156                #[inline]
157                fn from(i: $t) -> Self {
158                    Self::$variant(i)
159                }
160            }
161
162            // Implementing TryFrom<WasmValue> for $t
163            impl TryFrom<WasmValue> for $t {
164                type Error = ();
165
166                #[inline]
167                fn try_from(value: WasmValue) -> Result<Self, Self::Error> {
168                    if let WasmValue::$variant(i) = value {
169                        Ok(i)
170                    } else {
171                        cold();
172                        Err(())
173                    }
174                }
175            }
176        )*
177    }
178}
179
180impl_conversion_for_wasmvalue! { i32 => I32, i64 => I64, f32 => F32, f64 => F64, u128 => V128 }