1#[cfg(feature = "component-model")]
2use crate::component;
3use crate::core;
4use crate::spectest::*;
5use json_from_wast::{Action, Command, Const, WasmFile, WasmFileType};
6use std::collections::HashMap;
7use std::path::{Path, PathBuf};
8use std::str;
9use std::sync::Arc;
10use std::thread;
11use wasmtime::{error::Context as _, *};
12use wast::lexer::Lexer;
13use wast::parser::{self, ParseBuffer};
14
15pub struct WastContext {
18 current: Option<InstanceKind>,
21 core_linker: Linker<()>,
22 modules: HashMap<Option<String>, ModuleKind>,
23 #[cfg(feature = "component-model")]
24 component_linker: component::Linker<()>,
25
26 pub(crate) core_store: Store<()>,
31 pub(crate) async_runtime: Option<tokio::runtime::Runtime>,
32 generate_dwarf: bool,
33 precompile_save: Option<PathBuf>,
34 precompile_load: Option<PathBuf>,
35
36 modules_by_filename: Arc<HashMap<String, Vec<u8>>>,
37 configure_store: Arc<dyn Fn(&mut Store<()>) + Send + Sync>,
38}
39
40enum Outcome<T = Results> {
41 Ok(T),
42 Trap(Error),
43}
44
45impl<T> Outcome<T> {
46 fn map<U>(self, map: impl FnOnce(T) -> U) -> Outcome<U> {
47 match self {
48 Outcome::Ok(t) => Outcome::Ok(map(t)),
49 Outcome::Trap(t) => Outcome::Trap(t),
50 }
51 }
52
53 fn into_result(self) -> Result<T> {
54 match self {
55 Outcome::Ok(t) => Ok(t),
56 Outcome::Trap(t) => Err(t),
57 }
58 }
59}
60
61#[derive(Debug)]
62enum Results {
63 Core(Vec<Val>),
64 #[cfg(feature = "component-model")]
65 Component(Vec<component::Val>),
66}
67
68#[derive(Clone)]
69enum ModuleKind {
70 Core(Module),
71 #[cfg(feature = "component-model")]
72 Component(component::Component),
73}
74
75enum InstanceKind {
76 Core(Instance),
77 #[cfg(feature = "component-model")]
78 Component(Store<()>, component::Instance),
79}
80
81enum Export<'a> {
82 Core(Extern),
83 #[cfg(feature = "component-model")]
84 Component(&'a mut Store<()>, component::Func),
85
86 _Unused(std::convert::Infallible, &'a ()),
89}
90
91#[derive(Debug, Copy, Clone, PartialEq)]
95#[expect(missing_docs, reason = "self-describing variants")]
96pub enum Async {
97 Yes,
98 No,
99}
100
101impl WastContext {
102 pub fn new(
110 engine: &Engine,
111 async_: Async,
112 configure: impl Fn(&mut Store<()>) + Send + Sync + 'static,
113 ) -> Self {
114 let mut core_linker = Linker::new(engine);
118 core_linker.allow_shadowing(true);
119 Self {
120 current: None,
121 core_linker,
122 #[cfg(feature = "component-model")]
123 component_linker: {
124 let mut linker = component::Linker::new(engine);
125 linker.allow_shadowing(true);
126 linker
127 },
128 core_store: {
129 let mut store = Store::new(engine, ());
130 configure(&mut store);
131 store
132 },
133 modules: Default::default(),
134 async_runtime: if async_ == Async::Yes {
135 Some(
136 tokio::runtime::Builder::new_current_thread()
137 .build()
138 .unwrap(),
139 )
140 } else {
141 None
142 },
143 generate_dwarf: true,
144 precompile_save: None,
145 precompile_load: None,
146 modules_by_filename: Arc::default(),
147 configure_store: Arc::new(configure),
148 }
149 }
150
151 fn engine(&self) -> &Engine {
152 self.core_linker.engine()
153 }
154
155 pub fn precompile_save(&mut self, path: impl AsRef<Path>) -> &mut Self {
158 self.precompile_save = Some(path.as_ref().into());
159 self
160 }
161
162 pub fn precompile_load(&mut self, path: impl AsRef<Path>) -> &mut Self {
165 self.precompile_load = Some(path.as_ref().into());
166 self
167 }
168
169 fn get_export(&mut self, module: Option<&str>, name: &str) -> Result<Export<'_>> {
170 if let Some(module) = module {
171 return Ok(Export::Core(
172 self.core_linker
173 .get(&mut self.core_store, module, name)
174 .with_context(|| format_err!("no item named `{module}::{name}` found"))?,
175 ));
176 }
177
178 let cur = self
179 .current
180 .as_mut()
181 .ok_or_else(|| format_err!("no previous instance found"))?;
182 Ok(match cur {
183 InstanceKind::Core(i) => Export::Core(
184 i.get_export(&mut self.core_store, name)
185 .ok_or_else(|| format_err!("no item named `{name}` found"))?,
186 ),
187 #[cfg(feature = "component-model")]
188 InstanceKind::Component(store, i) => {
189 let export = i
190 .get_func(&mut *store, name)
191 .ok_or_else(|| format_err!("no func named `{name}` found"))?;
192 Export::Component(store, export)
193 }
194 })
195 }
196
197 fn instantiate_module(&mut self, module: &Module) -> Result<Outcome<Instance>> {
198 let instance = match &self.async_runtime {
199 Some(rt) => rt.block_on(
200 self.core_linker
201 .instantiate_async(&mut self.core_store, &module),
202 ),
203 None => self.core_linker.instantiate(&mut self.core_store, &module),
204 };
205 Ok(match instance {
206 Ok(i) => Outcome::Ok(i),
207 Err(e) => Outcome::Trap(e),
208 })
209 }
210
211 #[cfg(feature = "component-model")]
212 fn instantiate_component(
213 &mut self,
214 component: &component::Component,
215 ) -> Result<Outcome<(component::Component, Store<()>, component::Instance)>> {
216 let mut store = Store::new(self.engine(), ());
217 (self.configure_store)(&mut store);
218 let instance = match &self.async_runtime {
219 Some(rt) => rt.block_on(
220 self.component_linker
221 .instantiate_async(&mut store, &component),
222 ),
223 None => self.component_linker.instantiate(&mut store, &component),
224 };
225 Ok(match instance {
226 Ok(i) => Outcome::Ok((component.clone(), store, i)),
227 Err(e) => Outcome::Trap(e),
228 })
229 }
230
231 pub fn register_spectest(&mut self, config: &SpectestConfig) -> Result<()> {
233 link_spectest(&mut self.core_linker, &mut self.core_store, config)?;
234 #[cfg(feature = "component-model")]
235 link_component_spectest(&mut self.component_linker)?;
236 Ok(())
237 }
238
239 pub fn register_wasmtime(&mut self) -> Result<()> {
242 self.core_linker
243 .func_wrap("wasmtime", "gc", |mut caller: Caller<_>| {
244 caller.gc(None)?;
245 Ok(())
246 })?;
247 #[cfg(feature = "component-model")]
248 {
249 let mut i = self.component_linker.instance("wasmtime")?;
250 i.func_wrap(
251 "set-max-table-capacity",
252 |mut store, (capacity,): (u32,)| {
253 store
254 .as_context_mut()
255 .concurrent_resource_table()
256 .expect("table must be present")
257 .set_max_capacity(capacity.try_into().unwrap());
258 Ok(())
259 },
260 )?;
261 }
262 Ok(())
263 }
264
265 fn perform_action(&mut self, action: &Action<'_>) -> Result<Outcome> {
267 struct ReplaceRuntime<'a> {
272 ctx: &'a mut WastContext,
273 rt: Option<tokio::runtime::Runtime>,
274 }
275 impl Drop for ReplaceRuntime<'_> {
276 fn drop(&mut self) {
277 self.ctx.async_runtime = self.rt.take();
278 }
279 }
280 let replace = ReplaceRuntime {
281 rt: self.async_runtime.take(),
282 ctx: self,
283 };
284 let me = &mut *replace.ctx;
285 match action {
286 Action::Invoke {
287 module,
288 field,
289 args,
290 } => match me.get_export(module.as_deref(), field)? {
291 Export::Core(export) => {
292 drop(replace);
293 let func = export
294 .into_func()
295 .ok_or_else(|| format_err!("no function named `{field}`"))?;
296 let values = args
297 .iter()
298 .map(|v| match v {
299 Const::Core(v) => core::val(self, v),
300 _ => bail!("expected core function, found other other argument {v:?}"),
301 })
302 .collect::<Result<Vec<_>>>()?;
303
304 let mut results =
305 vec![Val::null_func_ref(); func.ty(&self.core_store).results().len()];
306 let result = match &self.async_runtime {
307 Some(rt) => rt.block_on(func.call_async(
308 &mut self.core_store,
309 &values,
310 &mut results,
311 )),
312 None => func.call(&mut self.core_store, &values, &mut results),
313 };
314
315 Ok(match result {
316 Ok(()) => Outcome::Ok(Results::Core(results)),
317 Err(e) => Outcome::Trap(e),
318 })
319 }
320 #[cfg(feature = "component-model")]
321 Export::Component(store, func) => {
322 let values = args
323 .iter()
324 .map(|v| match v {
325 Const::Component(v) => component::val(v),
326 _ => bail!("expected component function, found other argument {v:?}"),
327 })
328 .collect::<Result<Vec<_>>>()?;
329
330 let mut results =
331 vec![component::Val::Bool(false); func.ty(&store).results().len()];
332 let result = match &replace.rt {
333 Some(rt) => {
334 rt.block_on(func.call_async(&mut *store, &values, &mut results))
335 }
336 None => func.call(&mut *store, &values, &mut results),
337 };
338 Ok(match result {
339 Ok(()) => Outcome::Ok(Results::Component(results)),
340 Err(e) => Outcome::Trap(e),
341 })
342 }
343 },
344 Action::Get { module, field, .. } => me.get(module.as_deref(), field),
345 }
346 }
347
348 fn module(&mut self, name: Option<&str>, module: &ModuleKind) -> Result<()> {
351 match module {
352 ModuleKind::Core(module) => {
353 let instance = match self.instantiate_module(&module)? {
354 Outcome::Ok(i) => i,
355 Outcome::Trap(e) => return Err(e).context("instantiation failed"),
356 };
357 if let Some(name) = name {
358 self.core_linker
359 .instance(&mut self.core_store, name, instance)?;
360 }
361 self.current = Some(InstanceKind::Core(instance));
362 }
363 #[cfg(feature = "component-model")]
364 ModuleKind::Component(module) => {
365 let (component, mut store, instance) = match self.instantiate_component(&module)? {
366 Outcome::Ok(i) => i,
367 Outcome::Trap(e) => return Err(e).context("instantiation failed"),
368 };
369 if let Some(name) = name {
370 let ty = component.component_type();
371 let engine = self.engine().clone();
372 let mut linker = self.component_linker.instance(name)?;
373 for (name, item) in ty.exports(&engine) {
374 match item {
375 component::types::ComponentItem::Module(_) => {
376 let module = instance.get_module(&mut store, name).unwrap();
377 linker.module(name, &module)?;
378 }
379 component::types::ComponentItem::Resource(_) => {
380 let resource = instance.get_resource(&mut store, name).unwrap();
381 linker.resource(name, resource, |_, _| Ok(()))?;
382 }
383 _ => {}
390 }
391 }
392 }
393 self.current = Some(InstanceKind::Component(store, instance));
394 }
395 }
396 Ok(())
397 }
398
399 fn module_definition(&mut self, file: &WasmFile) -> Result<ModuleKind> {
404 let name = match file.module_type {
405 WasmFileType::Text => file
406 .binary_filename
407 .as_ref()
408 .ok_or_else(|| format_err!("cannot compile module that isn't a valid binary"))?,
409 WasmFileType::Binary => &file.filename,
410 };
411
412 match &self.precompile_load {
413 Some(path) => {
414 let cwasm = path.join(&name[..]).with_extension("cwasm");
415 match Engine::detect_precompiled_file(&cwasm)
416 .with_context(|| format!("failed to read {cwasm:?}"))?
417 {
418 Some(Precompiled::Module) => {
419 let module = unsafe { Module::deserialize_file(self.engine(), &cwasm)? };
420 Ok(ModuleKind::Core(module))
421 }
422 #[cfg(feature = "component-model")]
423 Some(Precompiled::Component) => {
424 let component = unsafe {
425 component::Component::deserialize_file(self.engine(), &cwasm)?
426 };
427 Ok(ModuleKind::Component(component))
428 }
429 #[cfg(not(feature = "component-model"))]
430 Some(Precompiled::Component) => {
431 bail!("support for components disabled at compile time")
432 }
433 None => bail!("expected a cwasm file"),
434 }
435 }
436 None => {
437 let bytes = &self.modules_by_filename[&name[..]];
438
439 if wasmparser::Parser::is_core_wasm(&bytes) {
440 let module = Module::new(self.engine(), &bytes)?;
441 Ok(ModuleKind::Core(module))
442 } else {
443 #[cfg(feature = "component-model")]
444 {
445 let component = component::Component::new(self.engine(), &bytes)?;
446 Ok(ModuleKind::Component(component))
447 }
448 #[cfg(not(feature = "component-model"))]
449 bail!("component-model support not enabled");
450 }
451 }
452 }
453 }
454
455 fn register(&mut self, name: Option<&str>, as_name: &str) -> Result<()> {
457 match name {
458 Some(name) => self.core_linker.alias_module(name, as_name),
459 None => {
460 let current = self
461 .current
462 .as_ref()
463 .ok_or(format_err!("no previous instance"))?;
464 match current {
465 InstanceKind::Core(current) => {
466 self.core_linker
467 .instance(&mut self.core_store, as_name, *current)?;
468 }
469 #[cfg(feature = "component-model")]
470 InstanceKind::Component(..) => {
471 bail!("register not implemented for components");
472 }
473 }
474 Ok(())
475 }
476 }
477 }
478
479 fn get(&mut self, instance_name: Option<&str>, field: &str) -> Result<Outcome> {
481 let global = match self.get_export(instance_name, field)? {
482 Export::Core(e) => e
483 .into_global()
484 .ok_or_else(|| format_err!("no global named `{field}`"))?,
485 #[cfg(feature = "component-model")]
486 Export::Component(..) => bail!("no global named `{field}`"),
487 };
488 Ok(Outcome::Ok(Results::Core(vec![
489 global.get(&mut self.core_store),
490 ])))
491 }
492
493 fn assert_return(&mut self, result: Outcome, results: &[Const]) -> Result<()> {
494 match result.into_result()? {
495 Results::Core(values) => {
496 if values.len() != results.len() {
497 bail!("expected {} results found {}", results.len(), values.len());
498 }
499 for (i, (v, e)) in values.iter().zip(results).enumerate() {
500 let e = match e {
501 Const::Core(core) => core,
502 _ => bail!("expected core value found other value {e:?}"),
503 };
504 core::match_val(&mut self.core_store, v, e)
505 .with_context(|| format!("result {i} didn't match"))?;
506 }
507 }
508 #[cfg(feature = "component-model")]
509 Results::Component(values) => {
510 if values.len() != results.len() {
511 bail!("expected {} results found {}", results.len(), values.len());
512 }
513 for (i, (v, e)) in values.iter().zip(results).enumerate() {
514 let e = match e {
515 Const::Component(val) => val,
516 _ => bail!("expected component value found other value {e:?}"),
517 };
518 component::match_val(e, v)
519 .with_context(|| format!("result {i} didn't match"))?;
520 }
521 }
522 }
523 Ok(())
524 }
525
526 fn assert_trap(&self, result: Outcome, expected: &str) -> Result<()> {
527 let trap = match result {
528 Outcome::Ok(values) => bail!("expected trap, got {values:?}"),
529 Outcome::Trap(t) => t,
530 };
531 let actual = format!("{trap:?}");
532 if actual.contains(expected)
533 || (expected.contains("uninitialized element 2") && actual.contains("uninitialized element"))
537 || (expected.contains("null function") && (actual.contains("uninitialized element") || actual.contains("null reference")))
539 || (expected.contains("null") && expected.contains("reference") && actual.contains("null reference"))
541 {
542 return Ok(());
543 }
544 bail!("expected '{expected}', got '{actual}'")
545 }
546
547 fn assert_exception(&mut self, result: Outcome) -> Result<()> {
548 match result {
549 Outcome::Ok(values) => bail!("expected exception, got {values:?}"),
550 Outcome::Trap(err) if err.is::<ThrownException>() => {
551 let _ = self
553 .core_store
554 .take_pending_exception()
555 .expect("there should be a pending exception on the store");
556 Ok(())
557 }
558 Outcome::Trap(err) => bail!("expected exception, got {err:?}"),
559 }
560 }
561
562 pub fn run_wast(&mut self, filename: &str, wast: &[u8]) -> Result<()> {
564 let wast = str::from_utf8(wast)?;
565
566 let adjust_wast = |mut err: wast::Error| {
567 err.set_path(filename.as_ref());
568 err.set_text(wast);
569 err
570 };
571
572 let mut lexer = Lexer::new(wast);
573 lexer.allow_confusing_unicode(filename.ends_with("names.wast"));
574 let mut buf = ParseBuffer::new_with_lexer(lexer).map_err(adjust_wast)?;
575 buf.track_instr_spans(self.generate_dwarf);
576 let ast = parser::parse::<wast::Wast>(&buf).map_err(adjust_wast)?;
577
578 let mut ast = json_from_wast::Opts::default()
579 .dwarf(self.generate_dwarf)
580 .convert(filename, wast, ast)
581 .to_wasmtime_result()?;
582
583 if !self.modules_by_filename.is_empty() {
586 self.modules_by_filename = Arc::default();
587 }
588 let modules_by_filename = Arc::get_mut(&mut self.modules_by_filename).unwrap();
589 for (name, bytes) in ast.wasms.drain(..) {
590 let prev = modules_by_filename.insert(name, bytes);
591 assert!(prev.is_none());
592 }
593
594 match &self.precompile_save {
595 Some(path) => {
596 let json_path = path
597 .join(Path::new(filename).file_name().unwrap())
598 .with_extension("json");
599 let json = serde_json::to_string(&ast)?;
600 std::fs::write(&json_path, json)
601 .with_context(|| format!("failed to write {json_path:?}"))?;
602 for (name, bytes) in self.modules_by_filename.iter() {
603 let cwasm_path = path.join(name).with_extension("cwasm");
604 let cwasm = if wasmparser::Parser::is_core_wasm(&bytes) {
605 self.engine().precompile_module(bytes)
606 } else {
607 #[cfg(feature = "component-model")]
608 {
609 self.engine().precompile_component(bytes)
610 }
611 #[cfg(not(feature = "component-model"))]
612 bail!("component-model support not enabled");
613 };
614 if let Ok(cwasm) = cwasm {
615 std::fs::write(&cwasm_path, cwasm)
616 .with_context(|| format!("failed to write {cwasm_path:?}"))?;
617 }
618 }
619 Ok(())
620 }
621 None => self.run_directives(ast.commands, filename),
622 }
623 }
624
625 fn run_directives(&mut self, directives: Vec<Command<'_>>, filename: &str) -> Result<()> {
626 thread::scope(|scope| {
627 let mut threads = HashMap::new();
628 for directive in directives {
629 let line = directive.line();
630 log::debug!("running directive on {filename}:{line}");
631 self.run_directive(directive, filename, &scope, &mut threads)
632 .with_context(|| format!("failed directive on {filename}:{line}"))?;
633 }
634 Ok(())
635 })
636 }
637
638 fn run_directive<'a>(
639 &mut self,
640 directive: Command<'a>,
641 filename: &'a str,
642 scope: &'a thread::Scope<'a, '_>,
644 threads: &mut HashMap<String, thread::ScopedJoinHandle<'a, Result<()>>>,
645 ) -> Result<()> {
646 use Command::*;
647
648 match directive {
649 Module {
650 name,
651 file,
652 line: _,
653 } => {
654 let module = self.module_definition(&file)?;
655 self.module(name.as_deref(), &module)?;
656 }
657 ModuleDefinition {
658 name,
659 file,
660 line: _,
661 } => {
662 let module = self.module_definition(&file)?;
663 self.modules.insert(name.map(|s| s.to_string()), module);
664 }
665 ModuleInstance {
666 instance,
667 module,
668 line: _,
669 } => {
670 let module = self
671 .modules
672 .get(&module.as_ref().map(|s| s.to_string()))
673 .cloned()
674 .ok_or_else(|| format_err!("no module named {module:?}"))?;
675 self.module(instance.as_deref(), &module)?;
676 }
677 Register { line: _, name, as_ } => {
678 self.register(name.as_deref(), &as_)?;
679 }
680 Action { action, line: _ } => {
681 self.perform_action(&action)?;
682 }
683 AssertReturn {
684 action,
685 expected,
686 line: _,
687 } => {
688 let result = self.perform_action(&action)?;
689 self.assert_return(result, &expected)?;
690 }
691 AssertTrap {
692 action,
693 text,
694 line: _,
695 } => {
696 let result = self.perform_action(&action)?;
697 self.assert_trap(result, &text)?;
698 }
699 AssertUninstantiable {
700 file,
701 text,
702 line: _,
703 } => {
704 let result = match self.module_definition(&file)? {
705 ModuleKind::Core(module) => self
706 .instantiate_module(&module)?
707 .map(|_| Results::Core(Vec::new())),
708 #[cfg(feature = "component-model")]
709 ModuleKind::Component(component) => self
710 .instantiate_component(&component)?
711 .map(|_| Results::Component(Vec::new())),
712 };
713 self.assert_trap(result, &text)?;
714 }
715 AssertExhaustion {
716 action,
717 text,
718 line: _,
719 } => {
720 let result = self.perform_action(&action)?;
721 self.assert_trap(result, &text)?;
722 }
723 AssertInvalid {
724 file,
725 text,
726 line: _,
727 } => {
728 let err = match self.module_definition(&file) {
729 Ok(_) => bail!("expected module to fail to build"),
730 Err(e) => e,
731 };
732 let error_message = format!("{err:?}");
733 if !is_matching_assert_invalid_error_message(filename, &text, &error_message) {
734 bail!("assert_invalid: expected \"{text}\", got \"{error_message}\"",)
735 }
736 }
737 AssertMalformed {
738 file,
739 text: _,
740 line: _,
741 } => {
742 if let Ok(_) = self.module_definition(&file) {
743 bail!("expected malformed module to fail to instantiate");
744 }
745 }
746 AssertUnlinkable {
747 file,
748 text,
749 line: _,
750 } => {
751 let module = self.module_definition(&file)?;
752 let err = match self.module(None, &module) {
753 Ok(_) => bail!("expected module to fail to link"),
754 Err(e) => e,
755 };
756 let error_message = format!("{err:?}");
757 if !is_matching_assert_invalid_error_message(filename, &text, &error_message) {
758 bail!("assert_unlinkable: expected {text}, got {error_message}",)
759 }
760 }
761 AssertException { line: _, action } => {
762 let result = self.perform_action(&action)?;
763 self.assert_exception(result)?;
764 }
765
766 Thread {
767 name,
768 shared_module,
769 commands,
770 line: _,
771 } => {
772 let mut core_linker = Linker::new(self.engine());
773 if let Some(id) = shared_module {
774 let items = self
775 .core_linker
776 .iter(&mut self.core_store)
777 .filter(|(module, _, _)| *module == &id[..])
778 .collect::<Vec<_>>();
779 for (module, name, item) in items {
780 core_linker.define(&mut self.core_store, module, name, item)?;
781 }
782 }
783 let mut child_cx = WastContext {
784 current: None,
785 core_linker,
786 #[cfg(feature = "component-model")]
787 component_linker: component::Linker::new(self.engine()),
788 core_store: {
789 let mut store = Store::new(self.engine(), ());
790 (self.configure_store)(&mut store);
791 store
792 },
793 modules: self.modules.clone(),
794 async_runtime: self.async_runtime.as_ref().map(|_| {
795 tokio::runtime::Builder::new_current_thread()
796 .build()
797 .unwrap()
798 }),
799 generate_dwarf: self.generate_dwarf,
800 modules_by_filename: self.modules_by_filename.clone(),
801 precompile_load: self.precompile_load.clone(),
802 precompile_save: self.precompile_save.clone(),
803 configure_store: self.configure_store.clone(),
804 };
805 let child = scope.spawn(move || child_cx.run_directives(commands, filename));
806 threads.insert(name.to_string(), child);
807 }
808 Wait { thread, .. } => {
809 threads
810 .remove(&thread[..])
811 .ok_or_else(|| format_err!("no thread named `{thread}`"))?
812 .join()
813 .unwrap()?;
814 }
815
816 AssertSuspension { .. } => {
817 bail!("unimplemented wast directive");
818 }
819
820 AssertMalformedCustom {
821 file: _,
822 text: _,
823 line: _,
824 }
825 | AssertInvalidCustom {
826 file: _,
827 text: _,
828 line: _,
829 } => bail!("unimplemented wast directives"),
830 }
831
832 Ok(())
833 }
834
835 pub fn run_file(&mut self, path: &Path) -> Result<()> {
837 match &self.precompile_load {
838 Some(precompile) => {
839 let file = precompile
840 .join(path.file_name().unwrap())
841 .with_extension("json");
842 let json = std::fs::read_to_string(&file)
843 .with_context(|| format!("failed to read {file:?}"))?;
844 let wast = serde_json::from_str::<json_from_wast::Wast<'_>>(&json)?;
845 self.run_directives(wast.commands, &wast.source_filename)
846 }
847 None => {
848 let bytes = std::fs::read(path)
849 .with_context(|| format!("failed to read `{}`", path.display()))?;
850 self.run_wast(path.to_str().unwrap(), &bytes)
851 }
852 }
853 }
854
855 pub fn generate_dwarf(&mut self, enable: bool) -> &mut Self {
858 self.generate_dwarf = enable;
859 self
860 }
861}
862
863fn is_matching_assert_invalid_error_message(test: &str, expected: &str, actual: &str) -> bool {
864 if actual.contains(expected) {
865 return true;
866 }
867
868 if test.contains("spec_testsuite") {
876 return true;
877 }
878
879 false
882}