1use crate::error::{DirectiveError, DirectiveErrors};
2use crate::spectest::spectest_importobject;
3use anyhow::{Result, anyhow, bail};
4use std::collections::{HashMap, HashSet};
5use std::path::{Path, PathBuf};
6use std::str;
7use wasmer::*;
8use wast::core::{AbstractHeapType, HeapType, WastArgCore, WastRetCore};
9use wast::token::{F32, F64};
10use wast::{QuoteWat, Wast as WWast, WastArg};
11use wast::{lexer::Lexer, parser};
12
13#[allow(dead_code)]
16pub struct Wast {
17 current: Option<Instance>,
20 import_object: Imports,
22 instances: HashMap<String, Instance>,
24 allowed_instantiation_failures: HashSet<String>,
26 allowed_directive_failures: HashSet<(PathBuf, usize, String)>,
28 match_trap_messages: Vec<(String, String)>,
31 current_is_allowed_failure: bool,
33 store: Store,
35 pub fail_fast: bool,
37 disable_assert_trap_exhaustion: bool,
40
41 disable_assert_exception: bool,
43}
44
45impl Wast {
46 pub fn new(store: Store, import_object: Imports) -> Self {
48 Self {
49 current: None,
50 store,
51 import_object,
52 allowed_instantiation_failures: HashSet::new(),
53 allowed_directive_failures: HashSet::new(),
54 match_trap_messages: Vec::new(),
55 current_is_allowed_failure: false,
56 instances: HashMap::new(),
57 fail_fast: true,
58 disable_assert_trap_exhaustion: false,
59 disable_assert_exception: false,
60 }
61 }
62
63 pub fn allow_instantiation_failures(&mut self, failures: &[&str]) {
65 for &failure_str in failures.iter() {
66 self.allowed_instantiation_failures
67 .insert(failure_str.to_string());
68 }
69 }
70
71 pub fn allow_directive_failures_at_line(&mut self, line: usize, filename: &str, failure: &str) {
73 self.allowed_directive_failures.insert((
74 PathBuf::from(filename),
75 line,
76 failure.to_string(),
77 ));
78 }
79
80 pub fn allow_trap_message(&mut self, expected: &str, allowed: &str) {
82 self.match_trap_messages
83 .push((expected.into(), allowed.into()));
84 }
85
86 pub fn disable_assert_and_exhaustion(&mut self) {
88 self.disable_assert_trap_exhaustion = true;
89 }
90
91 pub fn disable_assert_exception(&mut self) {
93 self.disable_assert_exception = true;
94 }
95
96 pub fn new_with_spectest(mut store: Store) -> Self {
98 let import_object = spectest_importobject(&mut store);
99 Self::new(store, import_object)
100 }
101
102 fn get_instance(&self, instance_name: Option<&str>) -> Result<Instance> {
103 match instance_name {
104 Some(name) => self
105 .instances
106 .get(name)
107 .cloned()
108 .ok_or_else(|| anyhow!("failed to find instance named `{name}`")),
109 None => self
110 .current
111 .clone()
112 .ok_or_else(|| anyhow!("no previous instance found")),
113 }
114 }
115
116 fn perform_execute(&mut self, exec: wast::WastExecute<'_>) -> Result<Vec<Value>> {
118 match exec {
119 wast::WastExecute::Invoke(invoke) => self.perform_invoke(invoke),
120 wast::WastExecute::Wat(mut module) => {
121 let binary = module.encode()?;
122 let result = self.instantiate(&binary);
123 result.map(|_| Vec::new())
124 }
125 wast::WastExecute::Get { module, global, .. } => {
126 self.get(module.map(|s| s.name()), global)
127 }
128 }
129 }
130
131 fn perform_invoke(&mut self, exec: wast::WastInvoke<'_>) -> Result<Vec<Value>> {
132 let module = exec.module.map(|i| i.name());
133 self.get_instance(module)?;
134
135 let values = exec
136 .args
137 .iter()
138 .map(|v| match v {
139 WastArg::Core(v) => self.runtime_value(v),
140 WastArg::Component(_) => bail!("expected component function, found core"),
141 _ => todo!(),
142 })
143 .collect::<Result<Vec<_>>>()?;
144 self.invoke(module, exec.name, &values)
145 }
146
147 fn assert_return(
148 &self,
149 result: Result<Vec<Value>>,
150 results: &[wast::WastRet<'_>],
151 ) -> Result<()> {
152 let values = result?;
153 for (v, e) in values.iter().zip(results) {
154 match e {
155 wast::WastRet::Core(e) => {
156 if self.val_matches(v, e)? {
157 continue;
158 }
159 }
160 wast::WastRet::Component(_) => anyhow::bail!("Components not supported yet!"),
161 _ => todo!(),
162 }
163
164 if let Value::V128(bits) = v
165 && let wast::WastRet::Core(WastRetCore::V128(pattern)) = e
166 {
167 let formatted = v128_format(*bits, pattern);
168 bail!("expected {e:?}, got {formatted:?} (v128 bits: {bits})");
169 }
170 if let Some(f) = v.f64() {
171 if let wast::WastRet::Core(WastRetCore::F64(wast::core::NanPattern::Value(f1))) = e
172 {
173 let f = f64::from_bits(f1.bits);
174 let f_bits = f.to_bits();
175 bail!("expected {f:?} ({e:?}), got {v:?} ({f_bits})")
176 } else {
177 let f_bits = f.to_bits();
178 bail!("expected {e:?}, got {v:?} ({f_bits})")
179 }
180 } else if let Some(f) = v.f32() {
181 if let wast::WastRet::Core(WastRetCore::F32(wast::core::NanPattern::Value(f1))) = e
182 {
183 let f = f32::from_bits(f1.bits);
184 let f_bits = f.to_bits();
185 bail!("expected {f:?} ({e:?}), got {v:?} ({f_bits})")
186 } else {
187 let f_bits = f.to_bits();
188 bail!("expected {e:?}, got {v:?} ({f_bits})")
189 }
190 } else {
191 bail!("expected {e:?}, got {v:?}")
192 }
193 }
194 Ok(())
195 }
196 fn wat(&mut self, mut wat: QuoteWat<'_>) -> Result<()> {
198 let (is_module, name) = match &wat {
199 QuoteWat::Wat(wast::Wat::Module(m)) => (true, m.id.map(|v| v.name())),
200 QuoteWat::QuoteModule(..) => (true, None),
201 QuoteWat::Wat(wast::Wat::Component(m)) => (false, m.id.map(|v| v.name())),
202 QuoteWat::QuoteComponent(..) => (false, None),
203 };
204 let bytes = wat.encode()?;
205 if is_module {
206 self.module(name, &bytes)?;
207 } else {
208 bail!("component-model support not enabled");
209 }
210 Ok(())
211 }
212
213 fn assert_trap(&self, result: Result<Vec<Value>>, expected: &str) -> Result<()> {
214 let actual = match result {
215 Ok(values) => bail!("expected trap, got {values:?}"),
216 Err(t) => format!("{t}"),
217 };
218 if self.matches_message_assert_trap(expected, &actual) {
219 return Ok(());
220 }
221 bail!("expected '{expected}', got '{actual}'")
222 }
223
224 fn run_directive(&mut self, _test: &Path, directive: wast::WastDirective) -> Result<()> {
225 use wast::WastDirective::*;
226
227 match directive {
228 ModuleDefinition(module) => self.wat(module)?,
229 Module(module) => self.wat(module)?,
230 Register {
231 span: _,
232 name,
233 module,
234 } => {
235 self.register(module.map(|s| s.name()), name)?;
236 }
237 Invoke(i) => {
238 self.perform_invoke(i)?;
239 }
240 AssertReturn {
241 span: _,
242 exec,
243 results,
244 } => {
245 let result = self.perform_execute(exec);
246 self.assert_return(result, &results)?;
247 }
248 AssertTrap {
249 span: _,
250 exec,
251 message,
252 } => {
253 if !self.disable_assert_trap_exhaustion {
254 let result = self.perform_execute(exec);
255 self.assert_trap(result, message)?;
256 }
257 }
258 AssertExhaustion {
259 span: _,
260 call,
261 message,
262 } => {
263 if !self.disable_assert_trap_exhaustion {
264 let result = self.perform_invoke(call);
265 self.assert_trap(result, message)?;
266 }
267 }
268 AssertInvalid {
269 span: _,
270 module,
271 message,
272 } => {
273 let err = match self.wat(module) {
274 Ok(()) => bail!("expected module to fail to build"),
275 Err(e) => e,
276 };
277 let error_message = format!("{err:?}");
278 if !Self::matches_message_assert_invalid(message, &error_message) {
279 bail!("assert_invalid: expected \"{message}\", got \"{error_message}\"")
280 }
281 }
282 AssertException { span: _, exec } => {
283 if !self.disable_assert_exception {
284 let result = self.perform_execute(exec);
285 self.assert_exception(result)?;
286 }
287 }
288 AssertMalformed {
289 module,
290 span: _,
291 message: _,
292 } => {
293 let mut module = match module {
294 wast::QuoteWat::Wat(m) => m,
295 wast::QuoteWat::QuoteModule(_, _) => return Ok(()),
298 wast::QuoteWat::QuoteComponent(_, _) => {
299 anyhow::bail!("Components not supported!")
300 }
301 };
302 let bytes = module.encode()?;
303 if self.module(None, &bytes).is_ok() {
304 bail!("expected malformed module to fail to instantiate");
305 }
306 }
307 AssertUnlinkable {
308 span: _,
309 mut module,
310 message,
311 } => {
312 let bytes = module.encode()?;
313 let err = match self.module(None, &bytes) {
314 Ok(()) => bail!("expected module to fail to link"),
315 Err(e) => e,
316 };
317 let error_message = format!("{err:?}");
318 if !Self::matches_message_assert_unlinkable(message, &error_message) {
319 bail!("assert_unlinkable: expected {message}, got {error_message}")
320 }
321 }
322 Thread(_) => anyhow::bail!("`thread` directives not implemented yet!"),
323 Wait { .. } => anyhow::bail!("`wait` directives not implemented yet!"),
324 ModuleInstance { .. } => {
325 anyhow::bail!("module instance directive not implemented yet!")
326 }
327 AssertSuspension { .. } => {
328 anyhow::bail!("`assert suspension` directive not implemented yet!")
329 }
330 AssertInvalidCustom { .. } => {
331 anyhow::bail!("`assert invalid custom` directive not implemented yet!")
332 }
333 AssertMalformedCustom { .. } => {
334 anyhow::bail!("`assert malformed custom` directive not implemented yet!")
335 }
336 }
337
338 Ok(())
339 }
340
341 pub fn run_buffer(&mut self, test: &Path, wast: &[u8]) -> Result<()> {
343 let wast = str::from_utf8(wast)?;
344 let filename = test.to_str().unwrap();
345 let adjust_wast = |mut err: wast::Error| {
346 err.set_path(filename.as_ref());
347 err.set_text(wast);
348 err
349 };
350
351 let mut lexer = Lexer::new(wast);
352 lexer.allow_confusing_unicode(filename.ends_with("names.wast"));
353 let buf = wast::parser::ParseBuffer::new_with_lexer(lexer).map_err(adjust_wast)?;
354 let ast = parser::parse::<WWast>(&buf).map_err(adjust_wast)?;
355
356 let mut errors = Vec::with_capacity(ast.directives.len());
357 for directive in ast.directives {
358 let sp = directive.span();
359 if let Err(e) = self.run_directive(test, directive) {
360 let message = format!("{e}");
361 if message.contains("no previous instance found") {
363 continue;
364 }
365 if self.current.is_none() && self.current_is_allowed_failure {
368 continue;
369 }
370 let (line, col) = sp.linecol_in(wast);
371 let line = line + 1;
372 if self.allowed_directive_failures.iter().any(
373 |(allowed_filename, allowed_line, allowed_failure)| {
374 allowed_filename == filename
375 && *allowed_line == line
376 && message.contains(allowed_failure)
377 },
378 ) {
379 continue;
380 }
381 errors.push(DirectiveError { line, col, message });
382 if self.fail_fast {
383 break;
384 }
385 }
386 }
387 if !errors.is_empty() {
388 return Err(DirectiveErrors {
389 filename: filename.to_string(),
390 errors,
391 }
392 .into());
393 }
394 Ok(())
395 }
396
397 pub fn run_file(&mut self, path: &Path) -> Result<()> {
422 let bytes = std::fs::read(path)?;
423 self.run_buffer(path, &bytes)
424 }
425}
426
427impl Wast {
429 fn module(&mut self, instance_name: Option<&str>, module: &[u8]) -> Result<()> {
431 let instance = match self.instantiate(module) {
432 Ok(i) => i,
433 Err(e) => {
434 self.current = None;
437 let error_message = format!("{e}");
438 self.current_is_allowed_failure = false;
439 for allowed_failure in self.allowed_instantiation_failures.iter() {
440 if error_message.contains(allowed_failure) {
441 self.current_is_allowed_failure = true;
442 break;
443 }
444 }
445 bail!("instantiation failed with: {e}")
446 }
447 };
448 if let Some(name) = instance_name {
449 self.instances.insert(name.to_string(), instance.clone());
450 }
451 self.current = Some(instance);
452 self.current_is_allowed_failure = false;
453 Ok(())
454 }
455
456 fn instantiate(&mut self, module: &[u8]) -> Result<Instance> {
457 let module = Module::new(&self.store, module)?;
458 let mut imports = self.import_object.clone();
459
460 for import in module.imports() {
461 let module_name = import.module();
462 if imports.contains_namespace(module_name) {
463 continue;
464 }
465 let instance = self
466 .instances
467 .get(module_name)
468 .ok_or_else(|| anyhow!("constant expression required"))?;
469 imports.register_namespace(module_name, instance.exports.clone());
470 }
471
472 let instance = Instance::new(&mut self.store, &module, &imports)?;
473 Ok(instance)
474 }
475
476 fn register(&mut self, name: Option<&str>, as_name: &str) -> Result<()> {
478 let instance = self.get_instance(name)?;
479 self.instances.insert(as_name.to_string(), instance);
480 Ok(())
481 }
482
483 fn invoke(
485 &mut self,
486 instance_name: Option<&str>,
487 field: &str,
488 args: &[Value],
489 ) -> Result<Vec<Value>> {
490 let instance = self.get_instance(instance_name)?;
491 let func: &Function = instance.exports.get(field)?;
492 match func.call(&mut self.store, args) {
493 Ok(result) => Ok(result.into()),
494 Err(e) => Err(e.into()),
495 }
496 }
497
498 fn get(&mut self, instance_name: Option<&str>, field: &str) -> Result<Vec<Value>> {
500 let instance = self.get_instance(instance_name)?;
501 let global: &Global = instance.exports.get(field)?;
502 Ok(vec![global.get(&mut self.store)])
503 }
504
505 fn runtime_value(&mut self, v: &WastArgCore) -> Result<Value> {
507 use wast::core::WastArgCore::*;
508
509 Ok(match v {
510 I32(x) => Value::I32(*x),
511 I64(x) => Value::I64(*x),
512 F32(x) => Value::F32(f32::from_bits(x.bits)),
513 F64(x) => Value::F64(f64::from_bits(x.bits)),
514 V128(x) => Value::V128(u128::from_le_bytes(x.to_le_bytes())),
515 RefNull(HeapType::Abstract {
516 ty: AbstractHeapType::Func,
517 ..
518 }) => Value::FuncRef(None),
519 RefNull(HeapType::Abstract {
520 ty: AbstractHeapType::Extern,
521 ..
522 }) => Value::null(),
523 RefExtern(number) => Value::ExternRef(Some(ExternRef::new(&mut self.store, *number))),
524 other => bail!("couldn't convert {other:?} to a runtime value"),
525 })
526 }
527
528 fn matches_message_assert_unlinkable(expected: &str, actual: &str) -> bool {
530 actual.contains(expected)
531 || (expected.contains("incompatible import type")
532 && actual.contains("instantiation failed with: constant expression required"))
533 || (expected.contains("unknown import")
534 && actual.contains("instantiation failed with: constant expression required"))
535 || (expected.contains("incompatible import type")
536 && actual.contains("Uncaught LinkError: instantiation"))
537 }
538
539 fn matches_message_assert_invalid(expected: &str, actual: &str) -> bool {
541 if actual.contains("Validation error: Failed to create V8 module") {
543 return true;
544 }
545
546 actual.contains(expected)
547 || (expected.contains("unknown table") && actual.contains("unknown elem"))
550 || (expected.contains("unknown memory") && actual.contains("no linear memories are present"))
552 || (expected.contains("out of bounds") && actual.contains("does not fit"))
555 || (expected.contains("unknown global") && actual.contains("unknown global"))
557 || (expected.contains("unknown memory") && actual.contains("unknown memory"))
559 || (expected.contains("unknown memory") && actual.contains("Data segment extends past end of the data section"))
560 || (expected.contains("unknown elem segment") && actual.contains("unknown element segment"))
561 || (expected.contains("memory size must be at most 65536 pages") && actual.contains("invalid u32 number"))
566 || (expected == "unknown global" && actual.contains("global.get of locally defined global"))
569 || (expected == "immutable global" && actual.contains("global is immutable: cannot modify it with `global.set`"))
570 || (expected.contains("type mismatch: instruction requires") && actual.contains("instantiation failed with: Validation error: type mismatch: expected"))
571 || (expected.contains("alignment must not be larger than natural") && actual.contains("malformed memop alignment: alignment too large"))
572 || (expected.contains("type mismatch") && actual.contains("malformed memop alignment: alignment too large"))
573 || (expected.contains("type mismatch") && actual.contains("Validation error: gc support is not enabled"))
574 || (expected.contains("multiple tables") && actual.contains("constant expression required"))
576 || (expected.contains("multiple memories") && actual.contains("constant expression required"))
577 }
578
579 fn matches_message_assert_trap(&self, expected: &str, actual: &str) -> bool {
581 actual.contains(expected)
582 || self
583 .match_trap_messages
584 .iter()
585 .any(|(exp, alt)| exp == expected && actual.contains(alt))
586 }
587
588 fn assert_exception(&self, result: Result<Vec<Value>>) -> Result<()> {
589 if result.is_ok() {
590 anyhow::bail!("Expected exception to be thrown, returned {result:?} instead");
591 }
592 Ok(())
593 }
594
595 fn val_matches(&self, actual: &Value, expected: &WastRetCore) -> Result<bool> {
596 Ok(match (actual, expected) {
597 (Value::I32(a), WastRetCore::I32(b)) => a == b,
598 (Value::I64(a), WastRetCore::I64(b)) => a == b,
599 (Value::F32(a), WastRetCore::F32(b)) => f32_matches(*a, b),
602 (Value::F64(a), WastRetCore::F64(b)) => f64_matches(*a, b),
603 (Value::V128(a), WastRetCore::V128(b)) => v128_matches(*a, b),
604 (actual, WastRetCore::Either(cases)) => cases
605 .iter()
606 .any(|case| self.val_matches(actual, case).unwrap_or(false)),
607 (
608 Value::FuncRef(None),
609 WastRetCore::RefNull(Some(wast::core::HeapType::Abstract {
610 ty: AbstractHeapType::Func,
611 ..
612 })),
613 ) => true,
614 (Value::FuncRef(Some(_)), WastRetCore::RefNull(_)) => false,
615 (Value::FuncRef(Some(_)), WastRetCore::RefFunc(_)) => true,
617 (Value::FuncRef(None), WastRetCore::RefFunc(None)) => true,
618 (Value::FuncRef(None), WastRetCore::RefFunc(Some(_))) => false,
619 (Value::FuncRef(None), WastRetCore::RefNull(_)) => true,
620 (
621 Value::ExternRef(None),
622 WastRetCore::RefNull(Some(wast::core::HeapType::Abstract {
623 ty: AbstractHeapType::Extern,
624 ..
625 })),
626 ) => true,
627 (Value::ExternRef(None), WastRetCore::RefExtern(_)) => false,
628 (Value::ExceptionRef(None), WastRetCore::RefNull(_)) => true,
629 (Value::ExternRef(Some(_)), WastRetCore::RefNull(_)) => false,
630 (Value::ExternRef(Some(extern_ref)), WastRetCore::RefExtern(num)) => {
631 let x = extern_ref.downcast::<u32>(&self.store).cloned();
632 x == *num
633 }
634 _ => bail!("don't know how to compare {actual:?} and {expected:?} yet"),
635 })
636 }
637}
638
639fn extract_lane_as_i8(bytes: u128, lane: usize) -> i8 {
640 (bytes >> (lane * 8)) as i8
641}
642
643fn extract_lane_as_i16(bytes: u128, lane: usize) -> i16 {
644 (bytes >> (lane * 16)) as i16
645}
646
647fn extract_lane_as_i32(bytes: u128, lane: usize) -> i32 {
648 (bytes >> (lane * 32)) as i32
649}
650
651fn extract_lane_as_i64(bytes: u128, lane: usize) -> i64 {
652 (bytes >> (lane * 64)) as i64
653}
654
655fn f32_matches(actual: f32, expected: &wast::core::NanPattern<F32>) -> bool {
656 match expected {
657 wast::core::NanPattern::CanonicalNan => actual.is_canonical_nan(),
658 wast::core::NanPattern::ArithmeticNan => actual.is_arithmetic_nan(),
659 wast::core::NanPattern::Value(expected_value) => actual.to_bits() == expected_value.bits,
660 }
661}
662
663fn f64_matches(actual: f64, expected: &wast::core::NanPattern<F64>) -> bool {
664 match expected {
665 wast::core::NanPattern::CanonicalNan => actual.is_canonical_nan(),
666 wast::core::NanPattern::ArithmeticNan => actual.is_arithmetic_nan(),
667 wast::core::NanPattern::Value(expected_value) => actual.to_bits() == expected_value.bits,
668 }
669}
670
671fn v128_matches(actual: u128, expected: &wast::core::V128Pattern) -> bool {
672 match expected {
673 wast::core::V128Pattern::I8x16(b) => b
674 .iter()
675 .enumerate()
676 .all(|(i, b)| *b == extract_lane_as_i8(actual, i)),
677 wast::core::V128Pattern::I16x8(b) => b
678 .iter()
679 .enumerate()
680 .all(|(i, b)| *b == extract_lane_as_i16(actual, i)),
681 wast::core::V128Pattern::I32x4(b) => b
682 .iter()
683 .enumerate()
684 .all(|(i, b)| *b == extract_lane_as_i32(actual, i)),
685 wast::core::V128Pattern::I64x2(b) => b
686 .iter()
687 .enumerate()
688 .all(|(i, b)| *b == extract_lane_as_i64(actual, i)),
689 wast::core::V128Pattern::F32x4(b) => b.iter().enumerate().all(|(i, b)| {
690 let a = extract_lane_as_i32(actual, i) as u32;
691 f32_matches(f32::from_bits(a), b)
692 }),
693 wast::core::V128Pattern::F64x2(b) => b.iter().enumerate().all(|(i, b)| {
694 let a = extract_lane_as_i64(actual, i) as u64;
695 f64_matches(f64::from_bits(a), b)
696 }),
697 }
698}
699
700fn v128_format(actual: u128, expected: &wast::core::V128Pattern) -> wast::core::V128Pattern {
701 match expected {
702 wast::core::V128Pattern::I8x16(_) => wast::core::V128Pattern::I8x16([
703 extract_lane_as_i8(actual, 0),
704 extract_lane_as_i8(actual, 1),
705 extract_lane_as_i8(actual, 2),
706 extract_lane_as_i8(actual, 3),
707 extract_lane_as_i8(actual, 4),
708 extract_lane_as_i8(actual, 5),
709 extract_lane_as_i8(actual, 6),
710 extract_lane_as_i8(actual, 7),
711 extract_lane_as_i8(actual, 8),
712 extract_lane_as_i8(actual, 9),
713 extract_lane_as_i8(actual, 10),
714 extract_lane_as_i8(actual, 11),
715 extract_lane_as_i8(actual, 12),
716 extract_lane_as_i8(actual, 13),
717 extract_lane_as_i8(actual, 14),
718 extract_lane_as_i8(actual, 15),
719 ]),
720 wast::core::V128Pattern::I16x8(_) => wast::core::V128Pattern::I16x8([
721 extract_lane_as_i16(actual, 0),
722 extract_lane_as_i16(actual, 1),
723 extract_lane_as_i16(actual, 2),
724 extract_lane_as_i16(actual, 3),
725 extract_lane_as_i16(actual, 4),
726 extract_lane_as_i16(actual, 5),
727 extract_lane_as_i16(actual, 6),
728 extract_lane_as_i16(actual, 7),
729 ]),
730 wast::core::V128Pattern::I32x4(_) => wast::core::V128Pattern::I32x4([
731 extract_lane_as_i32(actual, 0),
732 extract_lane_as_i32(actual, 1),
733 extract_lane_as_i32(actual, 2),
734 extract_lane_as_i32(actual, 3),
735 ]),
736 wast::core::V128Pattern::I64x2(_) => wast::core::V128Pattern::I64x2([
737 extract_lane_as_i64(actual, 0),
738 extract_lane_as_i64(actual, 1),
739 ]),
740 wast::core::V128Pattern::F32x4(_) => wast::core::V128Pattern::F32x4([
741 wast::core::NanPattern::Value(F32 {
742 bits: extract_lane_as_i32(actual, 0) as _,
743 }),
744 wast::core::NanPattern::Value(F32 {
745 bits: extract_lane_as_i32(actual, 1) as _,
746 }),
747 wast::core::NanPattern::Value(F32 {
748 bits: extract_lane_as_i32(actual, 2) as _,
749 }),
750 wast::core::NanPattern::Value(F32 {
751 bits: extract_lane_as_i32(actual, 3) as _,
752 }),
753 ]),
754 wast::core::V128Pattern::F64x2(_) => wast::core::V128Pattern::F64x2([
755 wast::core::NanPattern::Value(F64 {
756 bits: extract_lane_as_i64(actual, 0) as _,
757 }),
758 wast::core::NanPattern::Value(F64 {
759 bits: extract_lane_as_i64(actual, 1) as _,
760 }),
761 ]),
762 }
763}
764
765pub trait NaNCheck {
766 fn is_arithmetic_nan(&self) -> bool;
767 fn is_canonical_nan(&self) -> bool;
768}
769
770impl NaNCheck for f32 {
771 fn is_arithmetic_nan(&self) -> bool {
772 const AF32_NAN: u32 = 0x0040_0000;
773 (self.to_bits() & AF32_NAN) == AF32_NAN
774 }
775
776 fn is_canonical_nan(&self) -> bool {
777 (self.to_bits() & 0x7fff_ffff) == 0x7fc0_0000
778 }
779}
780
781impl NaNCheck for f64 {
782 fn is_arithmetic_nan(&self) -> bool {
783 const AF64_NAN: u64 = 0x0008_0000_0000_0000;
784 (self.to_bits() & AF64_NAN) == AF64_NAN
785 }
786
787 fn is_canonical_nan(&self) -> bool {
788 (self.to_bits() & 0x7fff_ffff_ffff_ffff) == 0x7ff8_0000_0000_0000
789 }
790}