1use core::fmt::Debug;
2
3use crate::{ConstInstruction, ExternRef, FuncRef};
4
5#[derive(Clone, Copy, PartialEq)]
9pub enum WasmValue {
10 I32(i32),
13 I64(i64),
15 F32(f32),
17 F64(f64),
19 V128([u8; 16]),
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
45impl WasmValue {
46 #[inline]
47 pub fn const_instr(&self) -> alloc::boxed::Box<[ConstInstruction]> {
49 alloc::boxed::Box::new([match self {
50 Self::I32(i) => ConstInstruction::I32Const(*i),
51 Self::I64(i) => ConstInstruction::I64Const(*i),
52 Self::F32(i) => ConstInstruction::F32Const(*i),
53 Self::F64(i) => ConstInstruction::F64Const(*i),
54 Self::V128(i) => ConstInstruction::V128Const(*i),
55 Self::RefFunc(i) => ConstInstruction::RefFunc(i.addr()),
56 Self::RefExtern(i) => ConstInstruction::RefExtern(i.addr()),
57 }])
58 }
59
60 #[inline]
61 pub const fn default_for(ty: WasmType) -> Self {
63 match ty {
64 WasmType::I32 => Self::I32(0),
65 WasmType::I64 => Self::I64(0),
66 WasmType::F32 => Self::F32(0.0),
67 WasmType::F64 => Self::F64(0.0),
68 WasmType::V128 => Self::V128([0; 16]),
69 WasmType::RefFunc => Self::RefFunc(FuncRef::null()),
70 WasmType::RefExtern => Self::RefExtern(ExternRef::null()),
71 }
72 }
73
74 #[inline]
75 pub fn eq_loose(&self, other: &Self) -> bool {
77 match (self, other) {
78 (Self::I32(a), Self::I32(b)) => a == b,
79 (Self::I64(a), Self::I64(b)) => a == b,
80 (Self::V128(a), Self::V128(b)) => a == b || Self::v128_nan_eq(*a, *b),
81 (Self::RefExtern(addr), Self::RefExtern(addr2)) => addr == addr2,
82 (Self::RefFunc(addr), Self::RefFunc(addr2)) => addr == addr2,
83 (Self::F32(a), Self::F32(b)) => a.is_nan() && b.is_nan() || a.to_bits() == b.to_bits(),
84 (Self::F64(a), Self::F64(b)) => a.is_nan() && b.is_nan() || a.to_bits() == b.to_bits(),
85 _ => false,
86 }
87 }
88
89 fn v128_nan_eq(a: [u8; 16], b: [u8; 16]) -> bool {
90 let a_f32x4: [f32; 4] = [
91 f32::from_le_bytes([a[0], a[1], a[2], a[3]]),
92 f32::from_le_bytes([a[4], a[5], a[6], a[7]]),
93 f32::from_le_bytes([a[8], a[9], a[10], a[11]]),
94 f32::from_le_bytes([a[12], a[13], a[14], a[15]]),
95 ];
96 let b_f32x4: [f32; 4] = [
97 f32::from_le_bytes([b[0], b[1], b[2], b[3]]),
98 f32::from_le_bytes([b[4], b[5], b[6], b[7]]),
99 f32::from_le_bytes([b[8], b[9], b[10], b[11]]),
100 f32::from_le_bytes([b[12], b[13], b[14], b[15]]),
101 ];
102
103 let all_nan_match = a_f32x4.iter().zip(b_f32x4.iter()).all(|(x, y)| {
104 if x.is_nan() && y.is_nan() {
105 true
106 } else if x.is_nan() || y.is_nan() {
107 false
108 } else {
109 x.to_bits() == y.to_bits()
110 }
111 });
112
113 if all_nan_match && a_f32x4.iter().any(|x| x.is_nan()) {
114 return true;
115 }
116
117 let a_f64x2: [f64; 2] = [
118 f64::from_le_bytes([a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]]),
119 f64::from_le_bytes([a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]]),
120 ];
121 let b_f64x2: [f64; 2] = [
122 f64::from_le_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]),
123 f64::from_le_bytes([b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]]),
124 ];
125
126 a_f64x2.iter().zip(b_f64x2.iter()).all(|(x, y)| {
127 if x.is_nan() && y.is_nan() {
128 true
129 } else if x.is_nan() || y.is_nan() {
130 false
131 } else {
132 x.to_bits() == y.to_bits()
133 }
134 }) && a_f64x2.iter().any(|x| x.is_nan())
135 }
136}
137
138impl From<&WasmValue> for WasmType {
139 #[inline]
140 fn from(value: &WasmValue) -> Self {
141 match value {
142 WasmValue::I32(_) => WasmType::I32,
143 WasmValue::I64(_) => WasmType::I64,
144 WasmValue::F32(_) => WasmType::F32,
145 WasmValue::F64(_) => WasmType::F64,
146 WasmValue::V128(_) => WasmType::V128,
147 WasmValue::RefExtern(_) => WasmType::RefExtern,
148 WasmValue::RefFunc(_) => WasmType::RefFunc,
149 }
150 }
151}
152
153impl From<WasmValue> for WasmType {
154 #[inline]
155 fn from(value: WasmValue) -> Self {
156 Self::from(&value)
157 }
158}
159
160#[derive(Clone, Copy, PartialEq, Eq, Debug)]
162#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
163pub enum WasmType {
164 I32,
166 I64,
168 F32,
170 F64,
172 V128,
174 RefFunc,
176 RefExtern,
178}
179
180impl WasmType {
181 #[inline]
182 pub const fn default_value(&self) -> WasmValue {
183 WasmValue::default_for(*self)
184 }
185
186 #[inline]
187 pub const fn is_simd(&self) -> bool {
188 matches!(self, Self::V128)
189 }
190}
191
192macro_rules! impl_conversion_for_wasmvalue {
193 ($($t:ty => $variant:ident, $accessor:ident, $doc:literal);* $(;)?) => {
194 impl WasmValue {
195 $(
196 #[doc = $doc]
197 pub const fn $accessor(&self) -> Option<$t> {
198 match self {
199 Self::$variant(value) => Some(*value),
200 _ => None,
201 }
202 }
203 )*
204 }
205
206 $(
207 impl From<$t> for WasmValue {
208 #[inline]
209 fn from(i: $t) -> Self {
210 Self::$variant(i)
211 }
212 }
213
214 impl TryFrom<WasmValue> for $t {
215 type Error = ();
216
217 #[inline]
218 fn try_from(value: WasmValue) -> Result<Self, Self::Error> {
219 if let WasmValue::$variant(i) = value { Ok(i) } else { Err(()) }
220 }
221 }
222 )*
223 }
224}
225
226impl_conversion_for_wasmvalue! {
227 i32 => I32, as_i32, "Return the `i32` from a `WasmValue`, if it is an `I32`.";
228 i64 => I64, as_i64, "Return the `i64` from a `WasmValue`, if it is an `I64`.";
229 f32 => F32, as_f32, "Return the `f32` from a `WasmValue`, if it is a `F32`.";
230 f64 => F64, as_f64, "Return the `f64` from a `WasmValue`, if it is a `F64`.";
231 [u8; 16] => V128, as_v128, "Return the raw little-endian bytes from a `WasmValue`, if it is a `V128`.";
232 ExternRef => RefExtern, as_ref_extern, "Return the [`ExternRef`] from a `WasmValue`, if it is one";
233 FuncRef => RefFunc, as_ref_func, "Return the [`FuncRef`] from a `WasmValue`, if it is one";
234}