1use arbitrary::{Arbitrary, Unstructured};
4use std::hash::Hash;
5use wasmtime::HeapType;
6
7#[derive(Clone, Debug)]
10#[expect(missing_docs, reason = "self-describing fields")]
11pub enum DiffValue {
12 I32(i32),
13 I64(i64),
14 F32(u32),
15 F64(u64),
16 V128(u128),
17 FuncRef { null: bool },
18 ExternRef { null: bool },
19 AnyRef { null: bool },
20 ExnRef { null: bool },
21 ContRef { null: bool },
22}
23
24impl DiffValue {
25 fn ty(&self) -> DiffValueType {
26 match self {
27 DiffValue::I32(_) => DiffValueType::I32,
28 DiffValue::I64(_) => DiffValueType::I64,
29 DiffValue::F32(_) => DiffValueType::F32,
30 DiffValue::F64(_) => DiffValueType::F64,
31 DiffValue::V128(_) => DiffValueType::V128,
32 DiffValue::FuncRef { .. } => DiffValueType::FuncRef,
33 DiffValue::ExternRef { .. } => DiffValueType::ExternRef,
34 DiffValue::AnyRef { .. } => DiffValueType::AnyRef,
35 DiffValue::ExnRef { .. } => DiffValueType::ExnRef,
36 DiffValue::ContRef { .. } => DiffValueType::ContRef,
37 }
38 }
39
40 pub fn arbitrary_of_type(
45 u: &mut Unstructured<'_>,
46 ty: DiffValueType,
47 ) -> arbitrary::Result<Self> {
48 use DiffValueType::*;
49 let val = match ty {
50 I32 => DiffValue::I32(biased_arbitrary_value(u, KNOWN_I32_VALUES)?),
51 I64 => DiffValue::I64(biased_arbitrary_value(u, KNOWN_I64_VALUES)?),
52 F32 => {
53 let known_f32_values = &[
56 f32::NAN.to_bits(),
57 f32::INFINITY.to_bits(),
58 f32::NEG_INFINITY.to_bits(),
59 f32::MIN.to_bits(),
60 (-1.0f32).to_bits(),
61 (0.0f32).to_bits(),
62 (1.0f32).to_bits(),
63 f32::MAX.to_bits(),
64 ];
65 let bits = biased_arbitrary_value(u, known_f32_values)?;
66
67 let bits = if f32::from_bits(bits).is_nan() {
72 f32::NAN.to_bits()
73 } else {
74 bits
75 };
76 DiffValue::F32(bits)
77 }
78 F64 => {
79 let known_f64_values = &[
82 f64::NAN.to_bits(),
83 f64::INFINITY.to_bits(),
84 f64::NEG_INFINITY.to_bits(),
85 f64::MIN.to_bits(),
86 (-1.0f64).to_bits(),
87 (0.0f64).to_bits(),
88 (1.0f64).to_bits(),
89 f64::MAX.to_bits(),
90 ];
91 let bits = biased_arbitrary_value(u, known_f64_values)?;
92 let bits = if f64::from_bits(bits).is_nan() {
95 f64::NAN.to_bits()
96 } else {
97 bits
98 };
99 DiffValue::F64(bits)
100 }
101 V128 => {
102 let ty: DiffSimdTy = u.arbitrary()?;
104 match ty {
105 DiffSimdTy::I8x16 => {
106 let mut i8 = || biased_arbitrary_value(u, KNOWN_I8_VALUES).map(|b| b as u8);
107 let vector = u128::from_le_bytes([
108 i8()?,
109 i8()?,
110 i8()?,
111 i8()?,
112 i8()?,
113 i8()?,
114 i8()?,
115 i8()?,
116 i8()?,
117 i8()?,
118 i8()?,
119 i8()?,
120 i8()?,
121 i8()?,
122 i8()?,
123 i8()?,
124 ]);
125 DiffValue::V128(vector)
126 }
127 DiffSimdTy::I16x8 => {
128 let mut i16 =
129 || biased_arbitrary_value(u, KNOWN_I16_VALUES).map(i16::to_le_bytes);
130 let vector: Vec<u8> = i16()?
131 .into_iter()
132 .chain(i16()?)
133 .chain(i16()?)
134 .chain(i16()?)
135 .chain(i16()?)
136 .chain(i16()?)
137 .chain(i16()?)
138 .chain(i16()?)
139 .collect();
140 DiffValue::V128(u128::from_le_bytes(vector.try_into().unwrap()))
141 }
142 DiffSimdTy::I32x4 => {
143 let mut i32 =
144 || biased_arbitrary_value(u, KNOWN_I32_VALUES).map(i32::to_le_bytes);
145 let vector: Vec<u8> = i32()?
146 .into_iter()
147 .chain(i32()?)
148 .chain(i32()?)
149 .chain(i32()?)
150 .collect();
151 DiffValue::V128(u128::from_le_bytes(vector.try_into().unwrap()))
152 }
153 DiffSimdTy::I64x2 => {
154 let mut i64 =
155 || biased_arbitrary_value(u, KNOWN_I64_VALUES).map(i64::to_le_bytes);
156 let vector: Vec<u8> = i64()?.into_iter().chain(i64()?).collect();
157 DiffValue::V128(u128::from_le_bytes(vector.try_into().unwrap()))
158 }
159 DiffSimdTy::F32x4 => {
160 let mut f32 = || {
161 Self::arbitrary_of_type(u, DiffValueType::F32).map(|v| match v {
162 DiffValue::F32(v) => v.to_le_bytes(),
163 _ => unreachable!(),
164 })
165 };
166 let vector: Vec<u8> = f32()?
167 .into_iter()
168 .chain(f32()?)
169 .chain(f32()?)
170 .chain(f32()?)
171 .collect();
172 DiffValue::V128(u128::from_le_bytes(vector.try_into().unwrap()))
173 }
174 DiffSimdTy::F64x2 => {
175 let mut f64 = || {
176 Self::arbitrary_of_type(u, DiffValueType::F64).map(|v| match v {
177 DiffValue::F64(v) => v.to_le_bytes(),
178 _ => unreachable!(),
179 })
180 };
181 let vector: Vec<u8> = f64()?.into_iter().chain(f64()?).collect();
182 DiffValue::V128(u128::from_le_bytes(vector.try_into().unwrap()))
183 }
184 }
185 }
186
187 FuncRef => DiffValue::FuncRef { null: true },
191 ExternRef => DiffValue::ExternRef { null: true },
192 AnyRef => DiffValue::AnyRef { null: true },
193 ExnRef => DiffValue::ExnRef { null: true },
194 ContRef => DiffValue::ContRef { null: true },
195 };
196 arbitrary::Result::Ok(val)
197 }
198}
199
200const KNOWN_I8_VALUES: &[i8] = &[i8::MIN, -1, 0, 1, i8::MAX];
201const KNOWN_I16_VALUES: &[i16] = &[i16::MIN, -1, 0, 1, i16::MAX];
202const KNOWN_I32_VALUES: &[i32] = &[i32::MIN, -1, 0, 1, i32::MAX];
203const KNOWN_I64_VALUES: &[i64] = &[i64::MIN, -1, 0, 1, i64::MAX];
204
205fn biased_arbitrary_value<'a, T>(
208 u: &mut Unstructured<'a>,
209 known_values: &[T],
210) -> arbitrary::Result<T>
211where
212 T: Arbitrary<'a> + Copy,
213{
214 let pick_from_known_values: bool = u.arbitrary()?;
215 if pick_from_known_values {
216 Ok(*u.choose(known_values)?)
217 } else {
218 u.arbitrary()
219 }
220}
221
222impl<'a> Arbitrary<'a> for DiffValue {
223 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
224 let ty: DiffValueType = u.arbitrary()?;
225 DiffValue::arbitrary_of_type(u, ty)
226 }
227}
228
229impl Hash for DiffValue {
230 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
231 self.ty().hash(state);
232 match self {
233 DiffValue::I32(n) => n.hash(state),
234 DiffValue::I64(n) => n.hash(state),
235 DiffValue::F32(n) => n.hash(state),
236 DiffValue::F64(n) => n.hash(state),
237 DiffValue::V128(n) => n.hash(state),
238 DiffValue::ExternRef { null } => null.hash(state),
239 DiffValue::FuncRef { null } => null.hash(state),
240 DiffValue::AnyRef { null } => null.hash(state),
241 DiffValue::ExnRef { null } => null.hash(state),
242 DiffValue::ContRef { null } => null.hash(state),
243 }
244 }
245}
246
247impl PartialEq for DiffValue {
261 fn eq(&self, other: &Self) -> bool {
262 match (self, other) {
263 (Self::I32(l0), Self::I32(r0)) => l0 == r0,
264 (Self::I64(l0), Self::I64(r0)) => l0 == r0,
265 (Self::V128(l0), Self::V128(r0)) => l0 == r0,
266 (Self::F32(l0), Self::F32(r0)) => {
267 let l0 = f32::from_bits(*l0);
268 let r0 = f32::from_bits(*r0);
269 l0 == r0 || (l0.is_nan() && r0.is_nan())
270 }
271 (Self::F64(l0), Self::F64(r0)) => {
272 let l0 = f64::from_bits(*l0);
273 let r0 = f64::from_bits(*r0);
274 l0 == r0 || (l0.is_nan() && r0.is_nan())
275 }
276 (Self::FuncRef { null: a }, Self::FuncRef { null: b }) => a == b,
277 (Self::ExternRef { null: a }, Self::ExternRef { null: b }) => a == b,
278 (Self::AnyRef { null: a }, Self::AnyRef { null: b }) => a == b,
279 (Self::ExnRef { null: a }, Self::ExnRef { null: b }) => a == b,
280 (Self::ContRef { null: a }, Self::ContRef { null: b }) => a == b,
281 _ => false,
282 }
283 }
284}
285
286#[derive(Copy, Clone, Debug, Arbitrary, Hash)]
288#[expect(missing_docs, reason = "self-describing variants")]
289pub enum DiffValueType {
290 I32,
291 I64,
292 F32,
293 F64,
294 V128,
295 FuncRef,
296 ExternRef,
297 AnyRef,
298 ExnRef,
299 ContRef,
300}
301
302impl TryFrom<wasmtime::ValType> for DiffValueType {
303 type Error = &'static str;
304 fn try_from(ty: wasmtime::ValType) -> Result<Self, Self::Error> {
305 use wasmtime::ValType::*;
306 match ty {
307 I32 => Ok(Self::I32),
308 I64 => Ok(Self::I64),
309 F32 => Ok(Self::F32),
310 F64 => Ok(Self::F64),
311 V128 => Ok(Self::V128),
312 Ref(r) => match (r.is_nullable(), r.heap_type()) {
313 (true, HeapType::Func) => Ok(Self::FuncRef),
314 (true, HeapType::Extern) => Ok(Self::ExternRef),
315 (true, HeapType::Any) => Ok(Self::AnyRef),
316 (true, HeapType::I31) => Ok(Self::AnyRef),
317 (true, HeapType::None) => Ok(Self::AnyRef),
318 (true, HeapType::Exn) => Ok(Self::ExnRef),
319 (true, HeapType::Cont) => Ok(Self::ContRef),
320 _ => Err("non-null reference types are not supported yet"),
321 },
322 }
323 }
324}
325
326#[derive(Copy, Clone, Debug, Arbitrary, Hash)]
328pub enum DiffSimdTy {
329 I8x16,
330 I16x8,
331 I32x4,
332 I64x2,
333 F32x4,
334 F64x2,
335}