wasmer_wast/
wast.rs

1use crate::error::{DirectiveError, DirectiveErrors};
2use crate::spectest::spectest_importobject;
3use anyhow::{Result, anyhow, bail};
4use std::collections::{HashMap, HashSet};
5use std::path::Path;
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/// The wast test script language allows modules to be defined and actions
14/// to be performed on them.
15#[allow(dead_code)]
16pub struct Wast {
17    /// Wast files have a concept of a "current" module, which is the most
18    /// recently defined.
19    current: Option<Instance>,
20    /// The Import Object that all wast tests will have
21    import_object: Imports,
22    /// The instances in the test
23    instances: HashMap<String, Instance>,
24    /// Allowed failures (ideally this should be empty)
25    allowed_instantiation_failures: HashSet<String>,
26    /// If the (expected from .wast, actual) message pair is in this list,
27    /// treat the strings as matching.
28    match_trap_messages: HashMap<String, String>,
29    /// If the current module was an allowed failure, we allow test to fail
30    current_is_allowed_failure: bool,
31    /// The store in which the tests are executing.
32    store: Store,
33    /// A flag indicating if Wast tests should stop as soon as one test fails.
34    pub fail_fast: bool,
35    /// A flag indicating that assert_trap and assert_exhaustion should be skipped.
36    /// See https://github.com/wasmerio/wasmer/issues/1550 for more info
37    disable_assert_trap_exhaustion: bool,
38
39    /// A flag indicating that assert_exception should be skipped.
40    disable_assert_exception: bool,
41}
42
43impl Wast {
44    /// Construct a new instance of `Wast` with a given imports.
45    pub fn new(store: Store, import_object: Imports) -> Self {
46        Self {
47            current: None,
48            store,
49            import_object,
50            allowed_instantiation_failures: HashSet::new(),
51            match_trap_messages: HashMap::new(),
52            current_is_allowed_failure: false,
53            instances: HashMap::new(),
54            fail_fast: true,
55            disable_assert_trap_exhaustion: false,
56            disable_assert_exception: false,
57        }
58    }
59
60    /// A list of instantiation failures to allow.
61    pub fn allow_instantiation_failures(&mut self, failures: &[&str]) {
62        for &failure_str in failures.iter() {
63            self.allowed_instantiation_failures
64                .insert(failure_str.to_string());
65        }
66    }
67
68    /// A list of alternative messages to permit for a trap failure.
69    pub fn allow_trap_message(&mut self, expected: &str, allowed: &str) {
70        self.match_trap_messages
71            .insert(expected.into(), allowed.into());
72    }
73
74    /// Do not run any code in assert_trap or assert_exhaustion.
75    pub fn disable_assert_and_exhaustion(&mut self) {
76        self.disable_assert_trap_exhaustion = true;
77    }
78
79    /// Do not run any code in assert_exception.
80    pub fn disable_assert_exception(&mut self) {
81        self.disable_assert_exception = true;
82    }
83
84    /// Construct a new instance of `Wast` with the spectests imports.
85    pub fn new_with_spectest(mut store: Store) -> Self {
86        let import_object = spectest_importobject(&mut store);
87        Self::new(store, import_object)
88    }
89
90    fn get_instance(&self, instance_name: Option<&str>) -> Result<Instance> {
91        match instance_name {
92            Some(name) => self
93                .instances
94                .get(name)
95                .cloned()
96                .ok_or_else(|| anyhow!("failed to find instance named `{name}`")),
97            None => self
98                .current
99                .clone()
100                .ok_or_else(|| anyhow!("no previous instance found")),
101        }
102    }
103
104    /// Perform the action portion of a command.
105    fn perform_execute(&mut self, exec: wast::WastExecute<'_>) -> Result<Vec<Value>> {
106        match exec {
107            wast::WastExecute::Invoke(invoke) => self.perform_invoke(invoke),
108            wast::WastExecute::Wat(mut module) => {
109                let binary = module.encode()?;
110                let result = self.instantiate(&binary);
111                result.map(|_| Vec::new())
112            }
113            wast::WastExecute::Get { module, global, .. } => {
114                self.get(module.map(|s| s.name()), global)
115            }
116        }
117    }
118
119    fn perform_invoke(&mut self, exec: wast::WastInvoke<'_>) -> Result<Vec<Value>> {
120        let values = exec
121            .args
122            .iter()
123            .map(|v| match v {
124                WastArg::Core(v) => self.runtime_value(v),
125                WastArg::Component(_) => bail!("expected component function, found core"),
126                _ => todo!(),
127            })
128            .collect::<Result<Vec<_>>>()?;
129        self.invoke(exec.module.map(|i| i.name()), exec.name, &values)
130    }
131
132    fn assert_return(
133        &self,
134        result: Result<Vec<Value>>,
135        results: &[wast::WastRet<'_>],
136    ) -> Result<()> {
137        let values = result?;
138        for (v, e) in values.iter().zip(results) {
139            match e {
140                wast::WastRet::Core(e) => {
141                    if self.val_matches(v, e)? {
142                        continue;
143                    }
144                }
145                wast::WastRet::Component(_) => anyhow::bail!("Components not supported yet!"),
146                _ => todo!(),
147            }
148
149            if let Value::V128(bits) = v
150                && let wast::WastRet::Core(WastRetCore::V128(pattern)) = e
151            {
152                let formatted = v128_format(*bits, pattern);
153                bail!("expected {e:?}, got {formatted:?} (v128 bits: {bits})");
154            }
155            if let Some(f) = v.f64() {
156                if let wast::WastRet::Core(WastRetCore::F64(wast::core::NanPattern::Value(f1))) = e
157                {
158                    let f = f64::from_bits(f1.bits);
159                    let f_bits = f.to_bits();
160                    bail!("expected {f:?} ({e:?}), got {v:?} ({f_bits})")
161                } else {
162                    let f_bits = f.to_bits();
163                    bail!("expected {e:?}, got {v:?} ({f_bits})")
164                }
165            } else if let Some(f) = v.f32() {
166                if let wast::WastRet::Core(WastRetCore::F32(wast::core::NanPattern::Value(f1))) = e
167                {
168                    let f = f32::from_bits(f1.bits);
169                    let f_bits = f.to_bits();
170                    bail!("expected {f:?} ({e:?}), got {v:?} ({f_bits})")
171                } else {
172                    let f_bits = f.to_bits();
173                    bail!("expected {e:?}, got {v:?} ({f_bits})")
174                }
175            } else {
176                bail!("expected {e:?}, got {v:?}")
177            }
178        }
179        Ok(())
180    }
181    /// Define a module and register it.
182    fn wat(&mut self, mut wat: QuoteWat<'_>) -> Result<()> {
183        let (is_module, name) = match &wat {
184            QuoteWat::Wat(wast::Wat::Module(m)) => (true, m.id.map(|v| v.name())),
185            QuoteWat::QuoteModule(..) => (true, None),
186            QuoteWat::Wat(wast::Wat::Component(m)) => (false, m.id.map(|v| v.name())),
187            QuoteWat::QuoteComponent(..) => (false, None),
188        };
189        let bytes = wat.encode()?;
190        if is_module {
191            self.module(name, &bytes)?;
192        } else {
193            bail!("component-model support not enabled");
194        }
195        Ok(())
196    }
197
198    fn assert_trap(&self, result: Result<Vec<Value>>, expected: &str) -> Result<()> {
199        let actual = match result {
200            Ok(values) => bail!("expected trap, got {values:?}"),
201            Err(t) => format!("{t}"),
202        };
203        if self.matches_message_assert_trap(expected, &actual) {
204            return Ok(());
205        }
206        bail!("expected '{expected}', got '{actual}'")
207    }
208
209    fn run_directive(&mut self, _test: &Path, directive: wast::WastDirective) -> Result<()> {
210        use wast::WastDirective::*;
211
212        match directive {
213            ModuleDefinition(module) => self.wat(module)?,
214            Module(module) => self.wat(module)?,
215            Register {
216                span: _,
217                name,
218                module,
219            } => {
220                self.register(module.map(|s| s.name()), name)?;
221            }
222            Invoke(i) => {
223                self.perform_invoke(i)?;
224            }
225            AssertReturn {
226                span: _,
227                exec,
228                results,
229            } => {
230                let result = self.perform_execute(exec);
231                self.assert_return(result, &results)?;
232            }
233            AssertTrap {
234                span: _,
235                exec,
236                message,
237            } => {
238                if !self.disable_assert_trap_exhaustion {
239                    let result = self.perform_execute(exec);
240                    self.assert_trap(result, message)?;
241                }
242            }
243            AssertExhaustion {
244                span: _,
245                call,
246                message,
247            } => {
248                if !self.disable_assert_trap_exhaustion {
249                    let result = self.perform_invoke(call);
250                    self.assert_trap(result, message)?;
251                }
252            }
253            AssertInvalid {
254                span: _,
255                module,
256                message,
257            } => {
258                let err = match self.wat(module) {
259                    Ok(()) => bail!("expected module to fail to build"),
260                    Err(e) => e,
261                };
262                let error_message = format!("{err:?}");
263                if !Self::matches_message_assert_invalid(message, &error_message) {
264                    bail!("assert_invalid: expected \"{message}\", got \"{error_message}\"")
265                }
266            }
267            AssertException { span: _, exec } => {
268                if !self.disable_assert_exception {
269                    let result = self.perform_execute(exec);
270                    self.assert_exception(result)?;
271                }
272            }
273            AssertMalformed {
274                module,
275                span: _,
276                message: _,
277            } => {
278                let mut module = match module {
279                    wast::QuoteWat::Wat(m) => m,
280                    // This is a `*.wat` parser test which we're not
281                    // interested in.
282                    wast::QuoteWat::QuoteModule(_, _) => return Ok(()),
283                    wast::QuoteWat::QuoteComponent(_, _) => {
284                        anyhow::bail!("Components not supported!")
285                    }
286                };
287                let bytes = module.encode()?;
288                if self.module(None, &bytes).is_ok() {
289                    bail!("expected malformed module to fail to instantiate");
290                }
291            }
292            AssertUnlinkable {
293                span: _,
294                mut module,
295                message,
296            } => {
297                let bytes = module.encode()?;
298                let err = match self.module(None, &bytes) {
299                    Ok(()) => bail!("expected module to fail to link"),
300                    Err(e) => e,
301                };
302                let error_message = format!("{err:?}");
303                if !Self::matches_message_assert_unlinkable(message, &error_message) {
304                    bail!("assert_unlinkable: expected {message}, got {error_message}")
305                }
306            }
307            Thread(_) => anyhow::bail!("`thread` directives not implemented yet!"),
308            Wait { .. } => anyhow::bail!("`wait` directives not implemented yet!"),
309            ModuleInstance { .. } => {
310                anyhow::bail!("module instance directive not implemented yet!")
311            }
312            AssertSuspension { .. } => {
313                anyhow::bail!("`assert suspension` directive not implemented yet!")
314            }
315            AssertInvalidCustom { .. } => {
316                anyhow::bail!("`assert invalid custom` directive not implemented yet!")
317            }
318            AssertMalformedCustom { .. } => {
319                anyhow::bail!("`assert malformed custom` directive not implemented yet!")
320            }
321        }
322
323        Ok(())
324    }
325
326    /// Run a wast script from a byte buffer.
327    pub fn run_buffer(&mut self, test: &Path, wast: &[u8]) -> Result<()> {
328        let wast = str::from_utf8(wast)?;
329        let filename = test.to_str().unwrap();
330        let adjust_wast = |mut err: wast::Error| {
331            err.set_path(filename.as_ref());
332            err.set_text(wast);
333            err
334        };
335
336        let mut lexer = Lexer::new(wast);
337        lexer.allow_confusing_unicode(filename.ends_with("names.wast"));
338        let buf = wast::parser::ParseBuffer::new_with_lexer(lexer).map_err(adjust_wast)?;
339        let ast = parser::parse::<WWast>(&buf).map_err(adjust_wast)?;
340
341        let mut errors = Vec::with_capacity(ast.directives.len());
342        for directive in ast.directives {
343            let sp = directive.span();
344            if let Err(e) = self.run_directive(test, directive) {
345                let message = format!("{e}");
346                // If depends on an instance that doesn't exist
347                if message.contains("no previous instance found") {
348                    continue;
349                }
350                // We don't compute it, comes from instantiating an instance
351                // that we expected to fail.
352                if self.current.is_none() && self.current_is_allowed_failure {
353                    continue;
354                }
355                let (line, col) = sp.linecol_in(wast);
356                errors.push(DirectiveError {
357                    line: line + 1,
358                    col,
359                    message,
360                });
361                if self.fail_fast {
362                    break;
363                }
364            }
365        }
366        if !errors.is_empty() {
367            return Err(DirectiveErrors {
368                filename: filename.to_string(),
369                errors,
370            }
371            .into());
372        }
373        Ok(())
374    }
375
376    //fn parse_quote_module(&self, test: &Path, source: &[&[u8]]) -> Result<Vec<u8>> {
377    //    let mut ret = String::new();
378    //    for src in source {
379    //        match str::from_utf8(src) {
380    //            Ok(s) => ret.push_str(s),
381    //            Err(_) => bail!("malformed UTF-8 encoding"),
382    //        }
383    //        ret.push(' ');
384    //    }
385    //    let buf = wast::parser::ParseBuffer::new(&ret)?;
386    //    let mut wat = wast::parser::parse::<wast::Wat>(&buf)?;
387
388    //    // TODO: when memory64 merges into the proper spec then this should be
389    //    // removed since it will presumably no longer be a text-format error but
390    //    // rather a validation error. Currently all non-memory64 proposals
391    //    // assert that this offset is a text-parser error, whereas with memory64
392    //    // support that error is deferred until later.
393    //    if ret.contains("offset=4294967296") && !test.iter().any(|t| t == "memory64") {
394    //        bail!("i32 constant out of bounds");
395    //    }
396    //    Ok(wat.encode()?)
397    //}
398
399    /// Run a wast script from a file.
400    pub fn run_file(&mut self, path: &Path) -> Result<()> {
401        let bytes = std::fs::read(path)?;
402        self.run_buffer(path, &bytes)
403    }
404}
405
406// This is the implementation specific to the Runtime
407impl Wast {
408    /// Define a module and register it.
409    fn module(&mut self, instance_name: Option<&str>, module: &[u8]) -> Result<()> {
410        let instance = match self.instantiate(module) {
411            Ok(i) => i,
412            Err(e) => {
413                // We set the current to None to allow running other
414                // spectests when `fail_fast` is `false`.
415                self.current = None;
416                let error_message = format!("{e}");
417                self.current_is_allowed_failure = false;
418                for allowed_failure in self.allowed_instantiation_failures.iter() {
419                    if error_message.contains(allowed_failure) {
420                        self.current_is_allowed_failure = true;
421                        break;
422                    }
423                }
424                bail!("instantiation failed with: {e}")
425            }
426        };
427        if let Some(name) = instance_name {
428            self.instances.insert(name.to_string(), instance.clone());
429        }
430        self.current = Some(instance);
431        self.current_is_allowed_failure = false;
432        Ok(())
433    }
434
435    fn instantiate(&mut self, module: &[u8]) -> Result<Instance> {
436        let module = Module::new(&self.store, module)?;
437        let mut imports = self.import_object.clone();
438
439        for import in module.imports() {
440            let module_name = import.module();
441            if imports.contains_namespace(module_name) {
442                continue;
443            }
444            let instance = self
445                .instances
446                .get(module_name)
447                .ok_or_else(|| anyhow!("constant expression required"))?;
448            imports.register_namespace(module_name, instance.exports.clone());
449        }
450
451        let instance = Instance::new(&mut self.store, &module, &imports)?;
452        Ok(instance)
453    }
454
455    /// Register an instance to make it available for performing actions.
456    fn register(&mut self, name: Option<&str>, as_name: &str) -> Result<()> {
457        let instance = self.get_instance(name)?;
458        self.instances.insert(as_name.to_string(), instance);
459        Ok(())
460    }
461
462    /// Invoke an exported function from an instance.
463    fn invoke(
464        &mut self,
465        instance_name: Option<&str>,
466        field: &str,
467        args: &[Value],
468    ) -> Result<Vec<Value>> {
469        let instance = self.get_instance(instance_name)?;
470        let func: &Function = instance.exports.get(field)?;
471        match func.call(&mut self.store, args) {
472            Ok(result) => Ok(result.into()),
473            Err(e) => Err(e.into()),
474        }
475    }
476
477    /// Get the value of an exported global from an instance.
478    fn get(&mut self, instance_name: Option<&str>, field: &str) -> Result<Vec<Value>> {
479        let instance = self.get_instance(instance_name)?;
480        let global: &Global = instance.exports.get(field)?;
481        Ok(vec![global.get(&mut self.store)])
482    }
483
484    /// Translate from a `script::Value` to a `Value`.
485    fn runtime_value(&mut self, v: &WastArgCore) -> Result<Value> {
486        use wast::core::WastArgCore::*;
487
488        Ok(match v {
489            I32(x) => Value::I32(*x),
490            I64(x) => Value::I64(*x),
491            F32(x) => Value::F32(f32::from_bits(x.bits)),
492            F64(x) => Value::F64(f64::from_bits(x.bits)),
493            V128(x) => Value::V128(u128::from_le_bytes(x.to_le_bytes())),
494            RefNull(HeapType::Abstract {
495                ty: AbstractHeapType::Func,
496                ..
497            }) => Value::FuncRef(None),
498            RefNull(HeapType::Abstract {
499                ty: AbstractHeapType::Extern,
500                ..
501            }) => Value::null(),
502            RefExtern(number) => Value::ExternRef(Some(ExternRef::new(&mut self.store, *number))),
503            other => bail!("couldn't convert {other:?} to a runtime value"),
504        })
505    }
506
507    // Checks if the `assert_unlinkable` message matches the expected one
508    fn matches_message_assert_unlinkable(expected: &str, actual: &str) -> bool {
509        actual.contains(expected)
510            || (expected.contains("incompatible import type")
511                && actual.contains("instantiation failed with: constant expression required"))
512            || (expected.contains("unknown import")
513                && actual.contains("instantiation failed with: constant expression required"))
514    }
515
516    // Checks if the `assert_invalid` message matches the expected one
517    fn matches_message_assert_invalid(expected: &str, actual: &str) -> bool {
518        actual.contains(expected)
519            // Waiting on https://github.com/WebAssembly/bulk-memory-operations/pull/137
520            // to propagate to WebAssembly/testsuite.
521            || (expected.contains("unknown table") && actual.contains("unknown elem"))
522            // wasmparser return the wrong message
523            || (expected.contains("unknown memory") && actual.contains("no linear memories are present"))
524            // `elem.wast` and `proposals/bulk-memory-operations/elem.wast` disagree
525            // on the expected error message for the same error.
526            || (expected.contains("out of bounds") && actual.contains("does not fit"))
527            // handle `unknown global $NUM` error messages that wasmparser doesn't return yet
528            || (expected.contains("unknown global") && actual.contains("unknown global"))
529            // handle `unknown memory $NUM` error messages that wasmparser doesn't return yet
530            || (expected.contains("unknown memory") && actual.contains("unknown memory"))
531            || (expected.contains("unknown memory") && actual.contains("Data segment extends past end of the data section"))
532            || (expected.contains("unknown elem segment") && actual.contains("unknown element segment"))
533            // The same test here is asserted to have one error message in
534            // `memory.wast` and a different error message in
535            // `memory64/memory.wast`, so we equate these two error messages to get
536            // the memory64 tests to pass.
537            || (expected.contains("memory size must be at most 65536 pages") && actual.contains("invalid u32 number"))
538            // the spec test suite asserts a different error message than we print
539            // for this scenario
540            || (expected == "unknown global" && actual.contains("global.get of locally defined global"))
541            || (expected == "immutable global" && actual.contains("global is immutable: cannot modify it with `global.set`"))
542            || (expected.contains("type mismatch: instruction requires") && actual.contains("instantiation failed with: Validation error: type mismatch: expected"))
543            || (expected.contains("alignment must not be larger than natural") && actual.contains("malformed memop alignment: alignment too large"))
544            || (expected.contains("type mismatch") && actual.contains("malformed memop alignment: alignment too large"))
545            || (expected.contains("type mismatch") && actual.contains("Validation error: gc support is not enabled"))
546    }
547
548    // Checks if the `assert_trap` message matches the expected one
549    fn matches_message_assert_trap(&self, expected: &str, actual: &str) -> bool {
550        actual.contains(expected)
551            || self
552                .match_trap_messages
553                .get(expected)
554                .is_some_and(|alternative| actual.contains(alternative))
555    }
556
557    fn assert_exception(&self, result: Result<Vec<Value>>) -> Result<()> {
558        if result.is_ok() {
559            anyhow::bail!("Expected exception to be thrown, returned {result:?} instead");
560        }
561        Ok(())
562    }
563
564    fn val_matches(&self, actual: &Value, expected: &WastRetCore) -> Result<bool> {
565        Ok(match (actual, expected) {
566            (Value::I32(a), WastRetCore::I32(b)) => a == b,
567            (Value::I64(a), WastRetCore::I64(b)) => a == b,
568            // Note that these float comparisons are comparing bits, not float
569            // values, so we're testing for bit-for-bit equivalence
570            (Value::F32(a), WastRetCore::F32(b)) => f32_matches(*a, b),
571            (Value::F64(a), WastRetCore::F64(b)) => f64_matches(*a, b),
572            (Value::V128(a), WastRetCore::V128(b)) => v128_matches(*a, b),
573            (actual, WastRetCore::Either(cases)) => cases
574                .iter()
575                .any(|case| self.val_matches(actual, case).unwrap_or(false)),
576            (
577                Value::FuncRef(None),
578                WastRetCore::RefNull(Some(wast::core::HeapType::Abstract {
579                    ty: AbstractHeapType::Func,
580                    ..
581                })),
582            ) => true,
583            (Value::FuncRef(Some(_)), WastRetCore::RefNull(_)) => false,
584            // assert_return of (ref.func $tf) and (ref.func) is a match!
585            (Value::FuncRef(Some(_)), WastRetCore::RefFunc(_)) => true,
586            (Value::FuncRef(None), WastRetCore::RefFunc(None)) => true,
587            (Value::FuncRef(None), WastRetCore::RefFunc(Some(_))) => false,
588            (Value::FuncRef(None), WastRetCore::RefNull(_)) => true,
589            (
590                Value::ExternRef(None),
591                WastRetCore::RefNull(Some(wast::core::HeapType::Abstract {
592                    ty: AbstractHeapType::Extern,
593                    ..
594                })),
595            ) => true,
596            (Value::ExternRef(None), WastRetCore::RefExtern(_)) => false,
597            (Value::ExceptionRef(None), WastRetCore::RefNull(_)) => true,
598            (Value::ExternRef(Some(_)), WastRetCore::RefNull(_)) => false,
599            (Value::ExternRef(Some(extern_ref)), WastRetCore::RefExtern(num)) => {
600                let x = extern_ref.downcast::<u32>(&self.store).cloned();
601                x == *num
602            }
603            _ => bail!("don't know how to compare {actual:?} and {expected:?} yet"),
604        })
605    }
606}
607
608fn extract_lane_as_i8(bytes: u128, lane: usize) -> i8 {
609    (bytes >> (lane * 8)) as i8
610}
611
612fn extract_lane_as_i16(bytes: u128, lane: usize) -> i16 {
613    (bytes >> (lane * 16)) as i16
614}
615
616fn extract_lane_as_i32(bytes: u128, lane: usize) -> i32 {
617    (bytes >> (lane * 32)) as i32
618}
619
620fn extract_lane_as_i64(bytes: u128, lane: usize) -> i64 {
621    (bytes >> (lane * 64)) as i64
622}
623
624fn f32_matches(actual: f32, expected: &wast::core::NanPattern<F32>) -> bool {
625    match expected {
626        wast::core::NanPattern::CanonicalNan => actual.is_canonical_nan(),
627        wast::core::NanPattern::ArithmeticNan => actual.is_arithmetic_nan(),
628        wast::core::NanPattern::Value(expected_value) => actual.to_bits() == expected_value.bits,
629    }
630}
631
632fn f64_matches(actual: f64, expected: &wast::core::NanPattern<F64>) -> bool {
633    match expected {
634        wast::core::NanPattern::CanonicalNan => actual.is_canonical_nan(),
635        wast::core::NanPattern::ArithmeticNan => actual.is_arithmetic_nan(),
636        wast::core::NanPattern::Value(expected_value) => actual.to_bits() == expected_value.bits,
637    }
638}
639
640fn v128_matches(actual: u128, expected: &wast::core::V128Pattern) -> bool {
641    match expected {
642        wast::core::V128Pattern::I8x16(b) => b
643            .iter()
644            .enumerate()
645            .all(|(i, b)| *b == extract_lane_as_i8(actual, i)),
646        wast::core::V128Pattern::I16x8(b) => b
647            .iter()
648            .enumerate()
649            .all(|(i, b)| *b == extract_lane_as_i16(actual, i)),
650        wast::core::V128Pattern::I32x4(b) => b
651            .iter()
652            .enumerate()
653            .all(|(i, b)| *b == extract_lane_as_i32(actual, i)),
654        wast::core::V128Pattern::I64x2(b) => b
655            .iter()
656            .enumerate()
657            .all(|(i, b)| *b == extract_lane_as_i64(actual, i)),
658        wast::core::V128Pattern::F32x4(b) => b.iter().enumerate().all(|(i, b)| {
659            let a = extract_lane_as_i32(actual, i) as u32;
660            f32_matches(f32::from_bits(a), b)
661        }),
662        wast::core::V128Pattern::F64x2(b) => b.iter().enumerate().all(|(i, b)| {
663            let a = extract_lane_as_i64(actual, i) as u64;
664            f64_matches(f64::from_bits(a), b)
665        }),
666    }
667}
668
669fn v128_format(actual: u128, expected: &wast::core::V128Pattern) -> wast::core::V128Pattern {
670    match expected {
671        wast::core::V128Pattern::I8x16(_) => wast::core::V128Pattern::I8x16([
672            extract_lane_as_i8(actual, 0),
673            extract_lane_as_i8(actual, 1),
674            extract_lane_as_i8(actual, 2),
675            extract_lane_as_i8(actual, 3),
676            extract_lane_as_i8(actual, 4),
677            extract_lane_as_i8(actual, 5),
678            extract_lane_as_i8(actual, 6),
679            extract_lane_as_i8(actual, 7),
680            extract_lane_as_i8(actual, 8),
681            extract_lane_as_i8(actual, 9),
682            extract_lane_as_i8(actual, 10),
683            extract_lane_as_i8(actual, 11),
684            extract_lane_as_i8(actual, 12),
685            extract_lane_as_i8(actual, 13),
686            extract_lane_as_i8(actual, 14),
687            extract_lane_as_i8(actual, 15),
688        ]),
689        wast::core::V128Pattern::I16x8(_) => wast::core::V128Pattern::I16x8([
690            extract_lane_as_i16(actual, 0),
691            extract_lane_as_i16(actual, 1),
692            extract_lane_as_i16(actual, 2),
693            extract_lane_as_i16(actual, 3),
694            extract_lane_as_i16(actual, 4),
695            extract_lane_as_i16(actual, 5),
696            extract_lane_as_i16(actual, 6),
697            extract_lane_as_i16(actual, 7),
698        ]),
699        wast::core::V128Pattern::I32x4(_) => wast::core::V128Pattern::I32x4([
700            extract_lane_as_i32(actual, 0),
701            extract_lane_as_i32(actual, 1),
702            extract_lane_as_i32(actual, 2),
703            extract_lane_as_i32(actual, 3),
704        ]),
705        wast::core::V128Pattern::I64x2(_) => wast::core::V128Pattern::I64x2([
706            extract_lane_as_i64(actual, 0),
707            extract_lane_as_i64(actual, 1),
708        ]),
709        wast::core::V128Pattern::F32x4(_) => wast::core::V128Pattern::F32x4([
710            wast::core::NanPattern::Value(F32 {
711                bits: extract_lane_as_i32(actual, 0) as _,
712            }),
713            wast::core::NanPattern::Value(F32 {
714                bits: extract_lane_as_i32(actual, 1) as _,
715            }),
716            wast::core::NanPattern::Value(F32 {
717                bits: extract_lane_as_i32(actual, 2) as _,
718            }),
719            wast::core::NanPattern::Value(F32 {
720                bits: extract_lane_as_i32(actual, 3) as _,
721            }),
722        ]),
723        wast::core::V128Pattern::F64x2(_) => wast::core::V128Pattern::F64x2([
724            wast::core::NanPattern::Value(F64 {
725                bits: extract_lane_as_i64(actual, 0) as _,
726            }),
727            wast::core::NanPattern::Value(F64 {
728                bits: extract_lane_as_i64(actual, 1) as _,
729            }),
730        ]),
731    }
732}
733
734pub trait NaNCheck {
735    fn is_arithmetic_nan(&self) -> bool;
736    fn is_canonical_nan(&self) -> bool;
737}
738
739impl NaNCheck for f32 {
740    fn is_arithmetic_nan(&self) -> bool {
741        const AF32_NAN: u32 = 0x0040_0000;
742        (self.to_bits() & AF32_NAN) == AF32_NAN
743    }
744
745    fn is_canonical_nan(&self) -> bool {
746        (self.to_bits() & 0x7fff_ffff) == 0x7fc0_0000
747    }
748}
749
750impl NaNCheck for f64 {
751    fn is_arithmetic_nan(&self) -> bool {
752        const AF64_NAN: u64 = 0x0008_0000_0000_0000;
753        (self.to_bits() & AF64_NAN) == AF64_NAN
754    }
755
756    fn is_canonical_nan(&self) -> bool {
757        (self.to_bits() & 0x7fff_ffff_ffff_ffff) == 0x7ff8_0000_0000_0000
758    }
759}