Skip to main content

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, 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/// 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    /// Allowed directive failures in a specific file at a specific 1-based line.
27    allowed_directive_failures: HashSet<(PathBuf, usize, String)>,
28    /// If the (expected from .wast, actual) message pair is in this list,
29    /// treat the strings as matching.
30    match_trap_messages: Vec<(String, String)>,
31    /// If the current module was an allowed failure, we allow test to fail
32    current_is_allowed_failure: bool,
33    /// The store in which the tests are executing.
34    store: Store,
35    /// A flag indicating if Wast tests should stop as soon as one test fails.
36    pub fail_fast: bool,
37    /// A flag indicating that assert_trap and assert_exhaustion should be skipped.
38    /// See https://github.com/wasmerio/wasmer/issues/1550 for more info
39    disable_assert_trap_exhaustion: bool,
40
41    /// A flag indicating that assert_exception should be skipped.
42    disable_assert_exception: bool,
43}
44
45impl Wast {
46    /// Construct a new instance of `Wast` with a given imports.
47    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    /// A list of instantiation failures to allow.
64    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    /// A directive failure to allow in a specific file at a specific 1-based line.
72    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    /// A list of alternative messages to permit for a trap failure.
81    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    /// Do not run any code in assert_trap or assert_exhaustion.
87    pub fn disable_assert_and_exhaustion(&mut self) {
88        self.disable_assert_trap_exhaustion = true;
89    }
90
91    /// Do not run any code in assert_exception.
92    pub fn disable_assert_exception(&mut self) {
93        self.disable_assert_exception = true;
94    }
95
96    /// Construct a new instance of `Wast` with the spectests imports.
97    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    /// Perform the action portion of a command.
117    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    /// Define a module and register it.
197    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                    // This is a `*.wat` parser test which we're not
296                    // interested in.
297                    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    /// Run a wast script from a byte buffer.
342    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 depends on an instance that doesn't exist
362                if message.contains("no previous instance found") {
363                    continue;
364                }
365                // We don't compute it, comes from instantiating an instance
366                // that we expected to fail.
367                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    //fn parse_quote_module(&self, test: &Path, source: &[&[u8]]) -> Result<Vec<u8>> {
398    //    let mut ret = String::new();
399    //    for src in source {
400    //        match str::from_utf8(src) {
401    //            Ok(s) => ret.push_str(s),
402    //            Err(_) => bail!("malformed UTF-8 encoding"),
403    //        }
404    //        ret.push(' ');
405    //    }
406    //    let buf = wast::parser::ParseBuffer::new(&ret)?;
407    //    let mut wat = wast::parser::parse::<wast::Wat>(&buf)?;
408
409    //    // TODO: when memory64 merges into the proper spec then this should be
410    //    // removed since it will presumably no longer be a text-format error but
411    //    // rather a validation error. Currently all non-memory64 proposals
412    //    // assert that this offset is a text-parser error, whereas with memory64
413    //    // support that error is deferred until later.
414    //    if ret.contains("offset=4294967296") && !test.iter().any(|t| t == "memory64") {
415    //        bail!("i32 constant out of bounds");
416    //    }
417    //    Ok(wat.encode()?)
418    //}
419
420    /// Run a wast script from a file.
421    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
427// This is the implementation specific to the Runtime
428impl Wast {
429    /// Define a module and register it.
430    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                // We set the current to None to allow running other
435                // spectests when `fail_fast` is `false`.
436                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    /// Register an instance to make it available for performing actions.
477    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    /// Invoke an exported function from an instance.
484    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    /// Get the value of an exported global from an instance.
499    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    /// Translate from a `script::Value` to a `Value`.
506    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    // Checks if the `assert_unlinkable` message matches the expected one
529    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    // Checks if the `assert_invalid` message matches the expected one
540    fn matches_message_assert_invalid(expected: &str, actual: &str) -> bool {
541        // V8 engine can't propagate detailed error message:
542        if actual.contains("Validation error: Failed to create V8 module") {
543            return true;
544        }
545
546        actual.contains(expected)
547            // Waiting on https://github.com/WebAssembly/bulk-memory-operations/pull/137
548            // to propagate to WebAssembly/testsuite.
549            || (expected.contains("unknown table") && actual.contains("unknown elem"))
550            // wasmparser return the wrong message
551            || (expected.contains("unknown memory") && actual.contains("no linear memories are present"))
552            // `elem.wast` and `proposals/bulk-memory-operations/elem.wast` disagree
553            // on the expected error message for the same error.
554            || (expected.contains("out of bounds") && actual.contains("does not fit"))
555            // handle `unknown global $NUM` error messages that wasmparser doesn't return yet
556            || (expected.contains("unknown global") && actual.contains("unknown global"))
557            // handle `unknown memory $NUM` error messages that wasmparser doesn't return yet
558            || (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            // The same test here is asserted to have one error message in
562            // `memory.wast` and a different error message in
563            // `memory64/memory.wast`, so we equate these two error messages to get
564            // the memory64 tests to pass.
565            || (expected.contains("memory size must be at most 65536 pages") && actual.contains("invalid u32 number"))
566            // the spec test suite asserts a different error message than we print
567            // for this scenario
568            || (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            // V8-specific
575            || (expected.contains("multiple tables") && actual.contains("constant expression required"))
576            || (expected.contains("multiple memories") && actual.contains("constant expression required"))
577    }
578
579    // Checks if the `assert_trap` message matches the expected one
580    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            // Note that these float comparisons are comparing bits, not float
600            // values, so we're testing for bit-for-bit equivalence
601            (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            // assert_return of (ref.func $tf) and (ref.func) is a match!
616            (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}