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