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