1use anyhow::{anyhow, bail, Context, Result};
2use std::fmt::{Display, LowerHex};
3use wasmtime::{AnyRef, ExternRef, Store, Val};
4use wast::core::{AbstractHeapType, HeapType, NanPattern, V128Pattern, WastArgCore, WastRetCore};
5use wast::token::{F32, F64};
6
7pub fn val<T>(store: &mut Store<T>, v: &WastArgCore<'_>) -> Result<Val> {
9 use wast::core::WastArgCore::*;
10
11 Ok(match v {
12 I32(x) => Val::I32(*x),
13 I64(x) => Val::I64(*x),
14 F32(x) => Val::F32(x.bits),
15 F64(x) => Val::F64(x.bits),
16 V128(x) => Val::V128(u128::from_le_bytes(x.to_le_bytes()).into()),
17 RefNull(HeapType::Abstract {
18 ty: AbstractHeapType::Extern,
19 shared: false,
20 }) => Val::ExternRef(None),
21 RefNull(HeapType::Abstract {
22 ty: AbstractHeapType::Func,
23 shared: false,
24 }) => Val::FuncRef(None),
25 RefNull(HeapType::Abstract {
26 ty: AbstractHeapType::Any,
27 shared: false,
28 }) => Val::AnyRef(None),
29 RefNull(HeapType::Abstract {
30 shared: false,
31 ty: AbstractHeapType::None,
32 }) => Val::AnyRef(None),
33 RefExtern(x) => Val::ExternRef(Some(ExternRef::new(store, *x)?)),
34 RefHost(x) => {
35 let x = ExternRef::new(&mut *store, *x)?;
36 let x = AnyRef::convert_extern(&mut *store, x)?;
37 Val::AnyRef(Some(x))
38 }
39 other => bail!("couldn't convert {:?} to a runtime value", other),
40 })
41}
42
43fn extract_lane_as_i8(bytes: u128, lane: usize) -> i8 {
44 (bytes >> (lane * 8)) as i8
45}
46
47fn extract_lane_as_i16(bytes: u128, lane: usize) -> i16 {
48 (bytes >> (lane * 16)) as i16
49}
50
51fn extract_lane_as_i32(bytes: u128, lane: usize) -> i32 {
52 (bytes >> (lane * 32)) as i32
53}
54
55fn extract_lane_as_i64(bytes: u128, lane: usize) -> i64 {
56 (bytes >> (lane * 64)) as i64
57}
58
59pub fn match_val<T>(store: &mut Store<T>, actual: &Val, expected: &WastRetCore) -> Result<()> {
60 match (actual, expected) {
61 (_, WastRetCore::Either(expected)) => {
62 for expected in expected {
63 if match_val(store, actual, expected).is_ok() {
64 return Ok(());
65 }
66 }
67 match_val(store, actual, &expected[0])
68 }
69
70 (Val::I32(a), WastRetCore::I32(b)) => match_int(a, b),
71 (Val::I64(a), WastRetCore::I64(b)) => match_int(a, b),
72
73 (Val::F32(a), WastRetCore::F32(b)) => match_f32(*a, b),
76 (Val::F64(a), WastRetCore::F64(b)) => match_f64(*a, b),
77 (Val::V128(a), WastRetCore::V128(b)) => match_v128(a.as_u128(), b),
78
79 (
81 Val::FuncRef(None) | Val::ExternRef(None) | Val::AnyRef(None),
82 WastRetCore::RefNull(_),
83 )
84 | (Val::ExternRef(None), WastRetCore::RefExtern(None)) => Ok(()),
85
86 (Val::ExternRef(None), WastRetCore::RefExtern(Some(_))) => {
88 bail!("expected non-null reference, found null")
89 }
90 (
91 Val::ExternRef(Some(x)),
92 WastRetCore::RefNull(Some(HeapType::Abstract {
93 ty: AbstractHeapType::Extern,
94 shared: false,
95 })),
96 ) => {
97 match x.data(store)?.map(|x| {
98 x.downcast_ref::<u32>()
99 .expect("only u32 externrefs created in wast test suites")
100 }) {
101 None => {
102 bail!("expected null externref, found non-null externref without host data")
103 }
104 Some(x) => bail!("expected null externref, found non-null externref of {x}"),
105 }
106 }
107 (Val::ExternRef(Some(_)) | Val::FuncRef(Some(_)), WastRetCore::RefNull(_)) => {
108 bail!("expected null, found non-null reference: {actual:?}")
109 }
110
111 (Val::FuncRef(Some(_)), WastRetCore::RefFunc(_)) => Ok(()),
113 (Val::ExternRef(Some(_)), WastRetCore::RefExtern(None)) => Ok(()),
114 (Val::ExternRef(Some(x)), WastRetCore::RefExtern(Some(y))) => {
115 let x = x
116 .data(store)?
117 .ok_or_else(|| {
118 anyhow!("expected an externref of a u32, found externref without host data")
119 })?
120 .downcast_ref::<u32>()
121 .expect("only u32 externrefs created in wast test suites");
122 if x == y {
123 Ok(())
124 } else {
125 bail!("expected {} found {}", y, x);
126 }
127 }
128
129 (Val::AnyRef(Some(_)), WastRetCore::RefAny) => Ok(()),
130 (Val::AnyRef(Some(x)), WastRetCore::RefEq) => {
131 if x.is_eqref(store)? {
132 Ok(())
133 } else {
134 bail!("expected an eqref, found {x:?}");
135 }
136 }
137 (Val::AnyRef(Some(x)), WastRetCore::RefI31) => {
138 if x.is_i31(store)? {
139 Ok(())
140 } else {
141 bail!("expected a `(ref i31)`, found {x:?}");
142 }
143 }
144 (Val::AnyRef(Some(x)), WastRetCore::RefStruct) => {
145 if x.is_struct(store)? {
146 Ok(())
147 } else {
148 bail!("expected a struct reference, found {x:?}")
149 }
150 }
151 (Val::AnyRef(Some(x)), WastRetCore::RefArray) => {
152 if x.is_array(store)? {
153 Ok(())
154 } else {
155 bail!("expected a array reference, found {x:?}")
156 }
157 }
158 (Val::AnyRef(Some(x)), WastRetCore::RefHost(y)) => {
159 let x = ExternRef::convert_any(&mut *store, *x)?;
160 let x = x
161 .data(&mut *store)?
162 .ok_or_else(|| {
163 anyhow!(
164 "expected anyref of externref of u32, found anyref that is \
165 not a converted externref"
166 )
167 })?
168 .downcast_ref::<u32>()
169 .expect("only u32 externrefs created in wast test suites");
170 if x == y {
171 Ok(())
172 } else {
173 bail!("expected anyref of externref of {y}, found anyref of externref of {x}")
174 }
175 }
176
177 _ => bail!(
178 "don't know how to compare {:?} and {:?} yet",
179 actual,
180 expected
181 ),
182 }
183}
184
185pub fn match_int<T>(actual: &T, expected: &T) -> Result<()>
186where
187 T: Eq + Display + LowerHex,
188{
189 if actual == expected {
190 Ok(())
191 } else {
192 bail!(
193 "expected {:18} / {0:#018x}\n\
194 actual {:18} / {1:#018x}",
195 expected,
196 actual
197 )
198 }
199}
200
201pub fn match_f32(actual: u32, expected: &NanPattern<F32>) -> Result<()> {
202 match expected {
203 NanPattern::CanonicalNan => {
211 let canon_nan = 0x7fc0_0000;
212 if (actual & 0x7fff_ffff) == canon_nan {
213 Ok(())
214 } else {
215 bail!(
216 "expected {:10} / {:#010x}\n\
217 actual {:10} / {:#010x}",
218 "canon-nan",
219 canon_nan,
220 f32::from_bits(actual),
221 actual,
222 )
223 }
224 }
225
226 NanPattern::ArithmeticNan => {
232 const AF32_NAN: u32 = 0x7f80_0000;
233 let is_nan = actual & AF32_NAN == AF32_NAN;
234 const AF32_PAYLOAD_MSB: u32 = 0x0040_0000;
235 let is_msb_set = actual & AF32_PAYLOAD_MSB == AF32_PAYLOAD_MSB;
236 if is_nan && is_msb_set {
237 Ok(())
238 } else {
239 bail!(
240 "expected {:>10} / {:>10}\n\
241 actual {:10} / {:#010x}",
242 "arith-nan",
243 "0x7fc*****",
244 f32::from_bits(actual),
245 actual,
246 )
247 }
248 }
249 NanPattern::Value(expected_value) => {
250 if actual == expected_value.bits {
251 Ok(())
252 } else {
253 bail!(
254 "expected {:10} / {:#010x}\n\
255 actual {:10} / {:#010x}",
256 f32::from_bits(expected_value.bits),
257 expected_value.bits,
258 f32::from_bits(actual),
259 actual,
260 )
261 }
262 }
263 }
264}
265
266pub fn match_f64(actual: u64, expected: &NanPattern<F64>) -> Result<()> {
267 match expected {
268 NanPattern::CanonicalNan => {
276 let canon_nan = 0x7ff8_0000_0000_0000;
277 if (actual & 0x7fff_ffff_ffff_ffff) == canon_nan {
278 Ok(())
279 } else {
280 bail!(
281 "expected {:18} / {:#018x}\n\
282 actual {:18} / {:#018x}",
283 "canon-nan",
284 canon_nan,
285 f64::from_bits(actual),
286 actual,
287 )
288 }
289 }
290
291 NanPattern::ArithmeticNan => {
296 const AF64_NAN: u64 = 0x7ff0_0000_0000_0000;
297 let is_nan = actual & AF64_NAN == AF64_NAN;
298 const AF64_PAYLOAD_MSB: u64 = 0x0008_0000_0000_0000;
299 let is_msb_set = actual & AF64_PAYLOAD_MSB == AF64_PAYLOAD_MSB;
300 if is_nan && is_msb_set {
301 Ok(())
302 } else {
303 bail!(
304 "expected {:>18} / {:>18}\n\
305 actual {:18} / {:#018x}",
306 "arith-nan",
307 "0x7ff8************",
308 f64::from_bits(actual),
309 actual,
310 )
311 }
312 }
313 NanPattern::Value(expected_value) => {
314 if actual == expected_value.bits {
315 Ok(())
316 } else {
317 bail!(
318 "expected {:18} / {:#018x}\n\
319 actual {:18} / {:#018x}",
320 f64::from_bits(expected_value.bits),
321 expected_value.bits,
322 f64::from_bits(actual),
323 actual,
324 )
325 }
326 }
327 }
328}
329
330fn match_v128(actual: u128, expected: &V128Pattern) -> Result<()> {
331 match expected {
332 V128Pattern::I8x16(expected) => {
333 let actual = [
334 extract_lane_as_i8(actual, 0),
335 extract_lane_as_i8(actual, 1),
336 extract_lane_as_i8(actual, 2),
337 extract_lane_as_i8(actual, 3),
338 extract_lane_as_i8(actual, 4),
339 extract_lane_as_i8(actual, 5),
340 extract_lane_as_i8(actual, 6),
341 extract_lane_as_i8(actual, 7),
342 extract_lane_as_i8(actual, 8),
343 extract_lane_as_i8(actual, 9),
344 extract_lane_as_i8(actual, 10),
345 extract_lane_as_i8(actual, 11),
346 extract_lane_as_i8(actual, 12),
347 extract_lane_as_i8(actual, 13),
348 extract_lane_as_i8(actual, 14),
349 extract_lane_as_i8(actual, 15),
350 ];
351 if actual == *expected {
352 return Ok(());
353 }
354 bail!(
355 "expected {:4?}\n\
356 actual {:4?}\n\
357 \n\
358 expected (hex) {0:02x?}\n\
359 actual (hex) {1:02x?}",
360 expected,
361 actual,
362 )
363 }
364 V128Pattern::I16x8(expected) => {
365 let actual = [
366 extract_lane_as_i16(actual, 0),
367 extract_lane_as_i16(actual, 1),
368 extract_lane_as_i16(actual, 2),
369 extract_lane_as_i16(actual, 3),
370 extract_lane_as_i16(actual, 4),
371 extract_lane_as_i16(actual, 5),
372 extract_lane_as_i16(actual, 6),
373 extract_lane_as_i16(actual, 7),
374 ];
375 if actual == *expected {
376 return Ok(());
377 }
378 bail!(
379 "expected {:6?}\n\
380 actual {:6?}\n\
381 \n\
382 expected (hex) {0:04x?}\n\
383 actual (hex) {1:04x?}",
384 expected,
385 actual,
386 )
387 }
388 V128Pattern::I32x4(expected) => {
389 let actual = [
390 extract_lane_as_i32(actual, 0),
391 extract_lane_as_i32(actual, 1),
392 extract_lane_as_i32(actual, 2),
393 extract_lane_as_i32(actual, 3),
394 ];
395 if actual == *expected {
396 return Ok(());
397 }
398 bail!(
399 "expected {:11?}\n\
400 actual {:11?}\n\
401 \n\
402 expected (hex) {0:08x?}\n\
403 actual (hex) {1:08x?}",
404 expected,
405 actual,
406 )
407 }
408 V128Pattern::I64x2(expected) => {
409 let actual = [
410 extract_lane_as_i64(actual, 0),
411 extract_lane_as_i64(actual, 1),
412 ];
413 if actual == *expected {
414 return Ok(());
415 }
416 bail!(
417 "expected {:20?}\n\
418 actual {:20?}\n\
419 \n\
420 expected (hex) {0:016x?}\n\
421 actual (hex) {1:016x?}",
422 expected,
423 actual,
424 )
425 }
426 V128Pattern::F32x4(expected) => {
427 for (i, expected) in expected.iter().enumerate() {
428 let a = extract_lane_as_i32(actual, i) as u32;
429 match_f32(a, expected).with_context(|| format!("difference in lane {i}"))?;
430 }
431 Ok(())
432 }
433 V128Pattern::F64x2(expected) => {
434 for (i, expected) in expected.iter().enumerate() {
435 let a = extract_lane_as_i64(actual, i) as u64;
436 match_f64(a, expected).with_context(|| format!("difference in lane {i}"))?;
437 }
438 Ok(())
439 }
440 }
441}