Coverage Report

Created: 2026-06-07 07:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wasmtime/crates/wast/src/wast.rs
Line
Count
Source
1
#[cfg(feature = "component-model")]
2
use crate::component;
3
use crate::core;
4
use crate::spectest::*;
5
use json_from_wast::{Action, Command, Const, WasmFile, WasmFileType};
6
use std::collections::HashMap;
7
use std::path::{Path, PathBuf};
8
use std::str;
9
use std::sync::Arc;
10
use std::thread;
11
use wasmtime::{error::Context as _, *};
12
use wast::lexer::Lexer;
13
use wast::parser::{self, ParseBuffer};
14
15
/// The wast test script language allows modules to be defined and actions
16
/// to be performed on them.
17
pub struct WastContext {
18
    /// Wast files have a concept of a "current" module, which is the most
19
    /// recently defined.
20
    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
    /// The store used for core wasm tests/primitives.
27
    ///
28
    /// Note that components each get their own store so this is not used for
29
    /// component-model testing.
30
    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
    ignore_error_messages: bool,
39
}
40
41
enum Outcome<T = Results> {
42
    Ok(T),
43
    Trap(Error),
44
}
45
46
impl<T> Outcome<T> {
47
539
    fn map<U>(self, map: impl FnOnce(T) -> U) -> Outcome<U> {
48
539
        match self {
49
0
            Outcome::Ok(t) => Outcome::Ok(map(t)),
50
539
            Outcome::Trap(t) => Outcome::Trap(t),
51
        }
52
539
    }
<wasmtime_wast::wast::Outcome<wasmtime::runtime::instance::Instance>>::map::<wasmtime_wast::wast::Results, <wasmtime_wast::wast::WastContext>::run_directive::{closure#3}>
Line
Count
Source
47
275
    fn map<U>(self, map: impl FnOnce(T) -> U) -> Outcome<U> {
48
275
        match self {
49
0
            Outcome::Ok(t) => Outcome::Ok(map(t)),
50
275
            Outcome::Trap(t) => Outcome::Trap(t),
51
        }
52
275
    }
<wasmtime_wast::wast::Outcome<(wasmtime::runtime::component::component::Component, wasmtime::runtime::store::Store<()>, wasmtime::runtime::component::instance::Instance)>>::map::<wasmtime_wast::wast::Results, <wasmtime_wast::wast::WastContext>::run_directive::{closure#4}>
Line
Count
Source
47
264
    fn map<U>(self, map: impl FnOnce(T) -> U) -> Outcome<U> {
48
264
        match self {
49
0
            Outcome::Ok(t) => Outcome::Ok(map(t)),
50
264
            Outcome::Trap(t) => Outcome::Trap(t),
51
        }
52
264
    }
53
54
336k
    fn into_result(self) -> Result<T> {
55
336k
        match self {
56
336k
            Outcome::Ok(t) => Ok(t),
57
0
            Outcome::Trap(t) => Err(t),
58
        }
59
336k
    }
60
}
61
62
#[derive(Debug)]
63
enum Results {
64
    Core(Vec<Val>),
65
    #[cfg(feature = "component-model")]
66
    Component(Vec<component::Val>),
67
}
68
69
#[derive(Clone)]
70
enum ModuleKind {
71
    Core(Module),
72
    #[cfg(feature = "component-model")]
73
    Component(component::Component),
74
}
75
76
enum InstanceKind {
77
    Core(Instance),
78
    #[cfg(feature = "component-model")]
79
    Component(Store<()>, component::Instance),
80
}
81
82
enum Export<'a> {
83
    Core(Extern),
84
    #[cfg(feature = "component-model")]
85
    Component(&'a mut Store<()>, component::Func),
86
87
    /// Impossible-to-construct variant to consider `'a` used when the
88
    /// `component-model` feature is disabled.
89
    _Unused(std::convert::Infallible, &'a ()),
90
}
91
92
/// Whether or not to use async APIs when calling wasm during wast testing.
93
///
94
/// Passed to [`WastContext::new`].
95
#[derive(Debug, Copy, Clone, PartialEq)]
96
#[expect(missing_docs, reason = "self-describing variants")]
97
pub enum Async {
98
    Yes,
99
    No,
100
}
101
102
impl WastContext {
103
    /// Construct a new instance of `WastContext`.
104
    ///
105
    /// The `engine` provided is used for all store/module/component creation
106
    /// and should be appropriately configured by the caller. The `async_`
107
    /// configuration indicates whether functions are invoked either async or
108
    /// sync, and then the `configure` callback is used whenever a store is
109
    /// created to further configure its settings.
110
3.83k
    pub fn new(
111
3.83k
        engine: &Engine,
112
3.83k
        async_: Async,
113
3.83k
        configure: impl Fn(&mut Store<()>) + Send + Sync + 'static,
114
3.83k
    ) -> Self {
115
        // Spec tests will redefine the same module/name sometimes, so we need
116
        // to allow shadowing in the linker which picks the most recent
117
        // definition as what to link when linking.
118
3.83k
        let mut core_linker = Linker::new(engine);
119
3.83k
        core_linker.allow_shadowing(true);
120
        Self {
121
3.83k
            current: None,
122
3.83k
            core_linker,
123
            #[cfg(feature = "component-model")]
124
            component_linker: {
125
3.83k
                let mut linker = component::Linker::new(engine);
126
3.83k
                linker.allow_shadowing(true);
127
3.83k
                linker
128
            },
129
            core_store: {
130
3.83k
                let mut store = Store::new(engine, ());
131
3.83k
                configure(&mut store);
132
3.83k
                store
133
            },
134
3.83k
            modules: Default::default(),
135
3.83k
            async_runtime: if async_ == Async::Yes {
136
2.46k
                Some(
137
2.46k
                    tokio::runtime::Builder::new_current_thread()
138
2.46k
                        .build()
139
2.46k
                        .unwrap(),
140
2.46k
                )
141
            } else {
142
1.37k
                None
143
            },
144
            generate_dwarf: true,
145
3.83k
            precompile_save: None,
146
3.83k
            precompile_load: None,
147
3.83k
            modules_by_filename: Arc::default(),
148
3.83k
            configure_store: Arc::new(configure),
149
            ignore_error_messages: false,
150
        }
151
3.83k
    }
<wasmtime_wast::wast::WastContext>::new::<wasmtime_fuzzing::oracles::wast_test::{closure#2}>
Line
Count
Source
110
3.83k
    pub fn new(
111
3.83k
        engine: &Engine,
112
3.83k
        async_: Async,
113
3.83k
        configure: impl Fn(&mut Store<()>) + Send + Sync + 'static,
114
3.83k
    ) -> Self {
115
        // Spec tests will redefine the same module/name sometimes, so we need
116
        // to allow shadowing in the linker which picks the most recent
117
        // definition as what to link when linking.
118
3.83k
        let mut core_linker = Linker::new(engine);
119
3.83k
        core_linker.allow_shadowing(true);
120
        Self {
121
3.83k
            current: None,
122
3.83k
            core_linker,
123
            #[cfg(feature = "component-model")]
124
            component_linker: {
125
3.83k
                let mut linker = component::Linker::new(engine);
126
3.83k
                linker.allow_shadowing(true);
127
3.83k
                linker
128
            },
129
            core_store: {
130
3.83k
                let mut store = Store::new(engine, ());
131
3.83k
                configure(&mut store);
132
3.83k
                store
133
            },
134
3.83k
            modules: Default::default(),
135
3.83k
            async_runtime: if async_ == Async::Yes {
136
2.46k
                Some(
137
2.46k
                    tokio::runtime::Builder::new_current_thread()
138
2.46k
                        .build()
139
2.46k
                        .unwrap(),
140
2.46k
                )
141
            } else {
142
1.37k
                None
143
            },
144
            generate_dwarf: true,
145
3.83k
            precompile_save: None,
146
3.83k
            precompile_load: None,
147
3.83k
            modules_by_filename: Arc::default(),
148
3.83k
            configure_store: Arc::new(configure),
149
            ignore_error_messages: false,
150
        }
151
3.83k
    }
Unexecuted instantiation: <wasmtime_wast::wast::WastContext>::new::<_>
152
153
49.8k
    fn engine(&self) -> &Engine {
154
49.8k
        self.core_linker.engine()
155
49.8k
    }
156
157
    /// Configures whether or not error messages are ignored in directives like
158
    /// `assert_invalid`.
159
3.83k
    pub fn ignore_error_messages(&mut self, ignore: bool) -> &mut Self {
160
3.83k
        self.ignore_error_messages = ignore;
161
3.83k
        self
162
3.83k
    }
163
164
    /// Saves precompiled modules/components into `path` instead of executing
165
    /// test directives.
166
0
    pub fn precompile_save(&mut self, path: impl AsRef<Path>) -> &mut Self {
167
0
        self.precompile_save = Some(path.as_ref().into());
168
0
        self
169
0
    }
170
171
    /// Loads precompiled modules/components from `path` instead of compiling
172
    /// natively.
173
0
    pub fn precompile_load(&mut self, path: impl AsRef<Path>) -> &mut Self {
174
0
        self.precompile_load = Some(path.as_ref().into());
175
0
        self
176
0
    }
177
178
379k
    fn get_export(&mut self, module: Option<&str>, name: &str) -> Result<Export<'_>> {
179
379k
        if let Some(module) = module {
180
            return Ok(Export::Core(
181
1.39k
                self.core_linker
182
1.39k
                    .get(&mut self.core_store, module, name)
183
1.39k
                    .with_context(|| format_err!("no item named `{module}::{name}` found"))?,
184
            ));
185
377k
        }
186
187
377k
        let cur = self
188
377k
            .current
189
377k
            .as_mut()
190
377k
            .ok_or_else(|| format_err!("no previous instance found"))?;
191
377k
        Ok(match cur {
192
373k
            InstanceKind::Core(i) => Export::Core(
193
373k
                i.get_export(&mut self.core_store, name)
194
373k
                    .ok_or_else(|| format_err!("no item named `{name}` found"))?,
195
            ),
196
            #[cfg(feature = "component-model")]
197
4.43k
            InstanceKind::Component(store, i) => {
198
4.43k
                let export = i
199
4.43k
                    .get_func(&mut *store, name)
200
4.43k
                    .ok_or_else(|| format_err!("no func named `{name}` found"))?;
201
4.43k
                Export::Component(store, export)
202
            }
203
        })
204
379k
    }
205
206
17.5k
    fn instantiate_module(&mut self, module: &Module) -> Result<Outcome<Instance>> {
207
17.5k
        let instance = match &self.async_runtime {
208
7.57k
            Some(rt) => rt.block_on(
209
7.57k
                self.core_linker
210
7.57k
                    .instantiate_async(&mut self.core_store, &module),
211
            ),
212
10.0k
            None => self.core_linker.instantiate(&mut self.core_store, &module),
213
        };
214
17.5k
        Ok(match instance {
215
16.1k
            Ok(i) => Outcome::Ok(i),
216
1.42k
            Err(e) => Outcome::Trap(e),
217
        })
218
17.5k
    }
219
220
    #[cfg(feature = "component-model")]
221
5.97k
    fn instantiate_component(
222
5.97k
        &mut self,
223
5.97k
        component: &component::Component,
224
5.97k
    ) -> Result<Outcome<(component::Component, Store<()>, component::Instance)>> {
225
5.97k
        let mut store = Store::new(self.engine(), ());
226
5.97k
        (self.configure_store)(&mut store);
227
5.97k
        let instance = match &self.async_runtime {
228
4.47k
            Some(rt) => rt.block_on(
229
4.47k
                self.component_linker
230
4.47k
                    .instantiate_async(&mut store, &component),
231
            ),
232
1.49k
            None => self.component_linker.instantiate(&mut store, &component),
233
        };
234
5.97k
        Ok(match instance {
235
5.11k
            Ok(i) => Outcome::Ok((component.clone(), store, i)),
236
857
            Err(e) => Outcome::Trap(e),
237
        })
238
5.97k
    }
239
240
    /// Register "spectest" which is used by the spec testsuite.
241
3.83k
    pub fn register_spectest(&mut self, config: &SpectestConfig) -> Result<()> {
242
3.83k
        link_spectest(&mut self.core_linker, &mut self.core_store, config)?;
243
        #[cfg(feature = "component-model")]
244
3.83k
        link_component_spectest(&mut self.component_linker)?;
245
3.83k
        Ok(())
246
3.83k
    }
247
248
    /// Register the "wasmtime" module, which provides utilities that our misc
249
    /// tests use.
250
3.83k
    pub fn register_wasmtime(&mut self) -> Result<()> {
251
3.83k
        self.core_linker
252
3.83k
            .func_wrap("wasmtime", "gc", |mut caller: Caller<_>| {
253
2.88k
                caller.gc(None)?;
254
2.88k
                Ok(())
255
2.88k
            })?;
256
        #[cfg(feature = "component-model")]
257
        {
258
3.83k
            let mut i = self.component_linker.instance("wasmtime")?;
259
3.83k
            i.func_wrap(
260
3.83k
                "set-max-table-capacity",
261
1
                |mut store, (capacity,): (u32,)| {
262
1
                    store
263
1
                        .as_context_mut()
264
1
                        .concurrent_resource_table()
265
1
                        .expect("table must be present")
266
1
                        .set_max_capacity(capacity.try_into().unwrap());
267
1
                    Ok(())
268
1
                },
269
0
            )?;
270
        }
271
3.83k
        Ok(())
272
3.83k
    }
273
274
    /// Perform the action portion of a command.
275
379k
    fn perform_action(&mut self, action: &Action<'_>) -> Result<Outcome> {
276
        // Need to simultaneously borrow `self.async_runtime` and a `&mut
277
        // Store` from components so work around the borrow checker issues by
278
        // taking out the async runtime here and putting it back through a
279
        // destructor.
280
        struct ReplaceRuntime<'a> {
281
            ctx: &'a mut WastContext,
282
            rt: Option<tokio::runtime::Runtime>,
283
        }
284
        impl Drop for ReplaceRuntime<'_> {
285
379k
            fn drop(&mut self) {
286
379k
                self.ctx.async_runtime = self.rt.take();
287
379k
            }
288
        }
289
379k
        let replace = ReplaceRuntime {
290
379k
            rt: self.async_runtime.take(),
291
379k
            ctx: self,
292
379k
        };
293
379k
        let me = &mut *replace.ctx;
294
379k
        match action {
295
            Action::Invoke {
296
378k
                module,
297
378k
                field,
298
378k
                args,
299
378k
            } => match me.get_export(module.as_deref(), field)? {
300
374k
                Export::Core(export) => {
301
374k
                    drop(replace);
302
374k
                    let func = export
303
374k
                        .into_func()
304
374k
                        .ok_or_else(|| format_err!("no function named `{field}`"))?;
305
374k
                    let values = args
306
374k
                        .iter()
307
521k
                        .map(|v| match v {
308
521k
                            Const::Core(v) => core::val(self, v),
309
0
                            _ => bail!("expected core function, found other other argument {v:?}"),
310
521k
                        })
311
374k
                        .collect::<Result<Vec<_>>>()?;
312
313
374k
                    let mut results =
314
374k
                        vec![Val::null_func_ref(); func.ty(&self.core_store).results().len()];
315
374k
                    let result = match &self.async_runtime {
316
160k
                        Some(rt) => rt.block_on(func.call_async(
317
160k
                            &mut self.core_store,
318
160k
                            &values,
319
160k
                            &mut results,
320
                        )),
321
213k
                        None => func.call(&mut self.core_store, &values, &mut results),
322
                    };
323
324
374k
                    Ok(match result {
325
335k
                        Ok(()) => Outcome::Ok(Results::Core(results)),
326
38.9k
                        Err(e) => Outcome::Trap(e),
327
                    })
328
                }
329
                #[cfg(feature = "component-model")]
330
4.43k
                Export::Component(store, func) => {
331
4.43k
                    let values = args
332
4.43k
                        .iter()
333
4.43k
                        .map(|v| match v {
334
782
                            Const::Component(v) => component::val(v),
335
0
                            _ => bail!("expected component function, found other argument {v:?}"),
336
782
                        })
337
4.43k
                        .collect::<Result<Vec<_>>>()?;
338
339
4.43k
                    let mut results =
340
4.43k
                        vec![component::Val::Bool(false); func.ty(&store).results().len()];
341
4.43k
                    let result = match &replace.rt {
342
4.24k
                        Some(rt) => {
343
4.24k
                            rt.block_on(func.call_async(&mut *store, &values, &mut results))
344
                        }
345
194
                        None => func.call(&mut *store, &values, &mut results),
346
                    };
347
4.43k
                    Ok(match result {
348
3.17k
                        Ok(()) => Outcome::Ok(Results::Component(results)),
349
1.25k
                        Err(e) => Outcome::Trap(e),
350
                    })
351
                }
352
            },
353
128
            Action::Get { module, field, .. } => me.get(module.as_deref(), field),
354
        }
355
379k
    }
356
357
    /// Instantiates the `module` provided and registers the instance under the
358
    /// `name` provided if successful.
359
23.0k
    fn module(&mut self, name: Option<&str>, module: &ModuleKind) -> Result<()> {
360
23.0k
        match module {
361
17.3k
            ModuleKind::Core(module) => {
362
17.3k
                let instance = match self.instantiate_module(&module)? {
363
16.1k
                    Outcome::Ok(i) => i,
364
1.14k
                    Outcome::Trap(e) => return Err(e).context("instantiation failed"),
365
                };
366
16.1k
                if let Some(name) = name {
367
626
                    self.core_linker
368
626
                        .instance(&mut self.core_store, name, instance)?;
369
15.5k
                }
370
16.1k
                self.current = Some(InstanceKind::Core(instance));
371
            }
372
            #[cfg(feature = "component-model")]
373
5.70k
            ModuleKind::Component(module) => {
374
5.70k
                let (component, mut store, instance) = match self.instantiate_component(&module)? {
375
5.11k
                    Outcome::Ok(i) => i,
376
593
                    Outcome::Trap(e) => return Err(e).context("instantiation failed"),
377
                };
378
5.11k
                if let Some(name) = name {
379
1.81k
                    let ty = component.component_type();
380
1.81k
                    let engine = self.engine().clone();
381
1.81k
                    let mut linker = self.component_linker.instance(name)?;
382
21.5k
                    for (name, item) in ty.exports(&engine) {
383
21.5k
                        match item.ty {
384
                            component::types::ComponentItem::Module(_) => {
385
302
                                let module = instance.get_module(&mut store, name).unwrap();
386
302
                                linker.module(name, &module)?;
387
                            }
388
                            component::types::ComponentItem::Resource(_) => {
389
74
                                let resource = instance.get_resource(&mut store, name).unwrap();
390
74
                                linker.resource(name, resource, |_, _| Ok(()))?;
391
                            }
392
                            // TODO: should ideally reflect more than just
393
                            // modules/resources into the linker's namespace
394
                            // but that's not easily supported today for host
395
                            // functions due to the inability to take a
396
                            // function from one instance and put it into the
397
                            // linker (must go through the host right now).
398
21.2k
                            _ => {}
399
                        }
400
                    }
401
3.30k
                }
402
5.11k
                self.current = Some(InstanceKind::Component(store, instance));
403
            }
404
        }
405
21.2k
        Ok(())
406
23.0k
    }
407
408
    /// Compiles the module `wat` into binary and returns the name found within
409
    /// it, if any.
410
    ///
411
    /// This will not register the name within `self.modules`.
412
48.0k
    fn module_definition(&mut self, file: &WasmFile) -> Result<ModuleKind> {
413
48.0k
        let name = match file.module_type {
414
6.11k
            WasmFileType::Text => file
415
6.11k
                .binary_filename
416
6.11k
                .as_ref()
417
6.11k
                .ok_or_else(|| format_err!("cannot compile module that isn't a valid binary"))?,
418
41.9k
            WasmFileType::Binary => &file.filename,
419
        };
420
421
41.9k
        match &self.precompile_load {
422
0
            Some(path) => {
423
0
                let cwasm = path.join(&name[..]).with_extension("cwasm");
424
0
                match Engine::detect_precompiled_file(&cwasm)
425
0
                    .with_context(|| format!("failed to read {cwasm:?}"))?
426
                {
427
                    Some(Precompiled::Module) => {
428
0
                        let module = unsafe { Module::deserialize_file(self.engine(), &cwasm)? };
429
0
                        Ok(ModuleKind::Core(module))
430
                    }
431
                    #[cfg(feature = "component-model")]
432
                    Some(Precompiled::Component) => {
433
0
                        let component = unsafe {
434
0
                            component::Component::deserialize_file(self.engine(), &cwasm)?
435
                        };
436
0
                        Ok(ModuleKind::Component(component))
437
                    }
438
                    #[cfg(not(feature = "component-model"))]
439
                    Some(Precompiled::Component) => {
440
                        bail!("support for components disabled at compile time")
441
                    }
442
0
                    None => bail!("expected a cwasm file"),
443
                }
444
            }
445
            None => {
446
41.9k
                let bytes = &self.modules_by_filename[&name[..]];
447
448
41.9k
                if wasmparser::Parser::is_core_wasm(&bytes) {
449
36.1k
                    let module = Module::new(self.engine(), &bytes)?;
450
17.5k
                    Ok(ModuleKind::Core(module))
451
                } else {
452
                    #[cfg(feature = "component-model")]
453
                    {
454
5.86k
                        let component = component::Component::new(self.engine(), &bytes)?;
455
4.85k
                        Ok(ModuleKind::Component(component))
456
                    }
457
                    #[cfg(not(feature = "component-model"))]
458
                    bail!("component-model support not enabled");
459
                }
460
            }
461
        }
462
48.0k
    }
463
464
    /// Register an instance to make it available for performing actions.
465
492
    fn register(&mut self, name: Option<&str>, as_name: &str) -> Result<()> {
466
492
        match name {
467
251
            Some(name) => self.core_linker.alias_module(name, as_name),
468
            None => {
469
241
                let current = self
470
241
                    .current
471
241
                    .as_ref()
472
241
                    .ok_or(format_err!("no previous instance"))?;
473
241
                match current {
474
241
                    InstanceKind::Core(current) => {
475
241
                        self.core_linker
476
241
                            .instance(&mut self.core_store, as_name, *current)?;
477
                    }
478
                    #[cfg(feature = "component-model")]
479
                    InstanceKind::Component(..) => {
480
0
                        bail!("register not implemented for components");
481
                    }
482
                }
483
241
                Ok(())
484
            }
485
        }
486
492
    }
487
488
    /// Get the value of an exported global from an instance.
489
128
    fn get(&mut self, instance_name: Option<&str>, field: &str) -> Result<Outcome> {
490
128
        let global = match self.get_export(instance_name, field)? {
491
128
            Export::Core(e) => e
492
128
                .into_global()
493
128
                .ok_or_else(|| format_err!("no global named `{field}`"))?,
494
            #[cfg(feature = "component-model")]
495
0
            Export::Component(..) => bail!("no global named `{field}`"),
496
        };
497
128
        Ok(Outcome::Ok(Results::Core(vec![
498
128
            global.get(&mut self.core_store),
499
128
        ])))
500
128
    }
501
502
336k
    fn assert_return(&mut self, result: Outcome, results: &[Const]) -> Result<()> {
503
336k
        match result.into_result()? {
504
333k
            Results::Core(values) => {
505
333k
                if values.len() != results.len() {
506
0
                    bail!("expected {} results found {}", results.len(), values.len());
507
333k
                }
508
333k
                for (i, (v, e)) in values.iter().zip(results).enumerate() {
509
331k
                    let e = match e {
510
331k
                        Const::Core(core) => core,
511
0
                        _ => bail!("expected core value found other value {e:?}"),
512
                    };
513
331k
                    core::match_val(&mut self.core_store, v, e)
514
331k
                        .with_context(|| format!("result {i} didn't match"))?;
515
                }
516
            }
517
            #[cfg(feature = "component-model")]
518
3.16k
            Results::Component(values) => {
519
3.16k
                if values.len() != results.len() {
520
0
                    bail!("expected {} results found {}", results.len(), values.len());
521
3.16k
                }
522
3.16k
                for (i, (v, e)) in values.iter().zip(results).enumerate() {
523
2.73k
                    let e = match e {
524
2.73k
                        Const::Component(val) => val,
525
0
                        _ => bail!("expected component value found other value {e:?}"),
526
                    };
527
2.73k
                    component::match_val(e, v)
528
2.73k
                        .with_context(|| format!("result {i} didn't match"))?;
529
                }
530
            }
531
        }
532
336k
        Ok(())
533
336k
    }
534
535
40.6k
    fn assert_trap(&self, result: Outcome, expected: &str) -> Result<()> {
536
40.6k
        let trap = match result {
537
0
            Outcome::Ok(values) => bail!("expected trap, got {values:?}"),
538
40.6k
            Outcome::Trap(t) => t,
539
        };
540
40.6k
        let actual = format!("{trap:?}");
541
40.6k
        if actual.contains(expected)
542
            // `bulk-memory-operations/bulk.wast` checks for a message that
543
            // specifies which element is uninitialized, but our traps don't
544
            // shepherd that information out.
545
282
            || (expected.contains("uninitialized element 2") && actual.contains("uninitialized element"))
546
            // function references call_ref
547
277
            || (expected.contains("null function") && (actual.contains("uninitialized element") || actual.contains("null reference")))
548
            // GC tests say "null $kind reference" but we just say "null reference".
549
272
            || (expected.contains("null") && expected.contains("reference") && actual.contains("null reference"))
550
            // upstream component model tests expect slightly different error
551
            // messages than we generate.
552
90
            || (expected.contains("cannot write") && actual.contains("cannot write"))
553
36
            || (expected.contains("cannot read") && actual.contains("cannot read"))
554
        {
555
40.6k
            return Ok(());
556
0
        }
557
0
        bail!("expected '{expected}', got '{actual}'")
558
40.6k
    }
559
560
125
    fn assert_exception(&mut self, result: Outcome) -> Result<()> {
561
125
        match result {
562
0
            Outcome::Ok(values) => bail!("expected exception, got {values:?}"),
563
125
            Outcome::Trap(err) if err.is::<ThrownException>() => {
564
                // Discard the thrown exception.
565
125
                let _ = self
566
125
                    .core_store
567
125
                    .take_pending_exception()
568
125
                    .expect("there should be a pending exception on the store");
569
125
                Ok(())
570
            }
571
0
            Outcome::Trap(err) => bail!("expected exception, got {err:?}"),
572
        }
573
125
    }
574
575
    /// Run a wast script from a byte buffer.
576
3.83k
    pub fn run_wast(&mut self, filename: &str, wast: &[u8]) -> Result<()> {
577
3.83k
        let wast = str::from_utf8(wast)?;
578
579
3.83k
        let adjust_wast = |mut err: wast::Error| {
580
0
            err.set_path(filename.as_ref());
581
0
            err.set_text(wast);
582
0
            err
583
0
        };
584
585
3.83k
        let mut lexer = Lexer::new(wast);
586
3.83k
        lexer.allow_confusing_unicode(filename.ends_with("names.wast"));
587
3.83k
        let mut buf = ParseBuffer::new_with_lexer(lexer).map_err(adjust_wast)?;
588
3.83k
        buf.track_instr_spans(self.generate_dwarf);
589
3.83k
        let ast = parser::parse::<wast::Wast>(&buf).map_err(adjust_wast)?;
590
591
3.83k
        let mut ast = json_from_wast::Opts::default()
592
3.83k
            .dwarf(self.generate_dwarf)
593
3.83k
            .convert(filename, wast, ast)
594
3.83k
            .to_wasmtime_result()?;
595
596
        // Clear out any modules, if any, from a previous `*.wast` file being
597
        // run, if any.
598
3.83k
        if !self.modules_by_filename.is_empty() {
599
0
            self.modules_by_filename = Arc::default();
600
3.83k
        }
601
3.83k
        let modules_by_filename = Arc::get_mut(&mut self.modules_by_filename).unwrap();
602
48.0k
        for (name, bytes) in ast.wasms.drain(..) {
603
48.0k
            let prev = modules_by_filename.insert(name, bytes);
604
48.0k
            assert!(prev.is_none());
605
        }
606
607
3.83k
        match &self.precompile_save {
608
0
            Some(path) => {
609
0
                let json_path = path
610
0
                    .join(Path::new(filename).file_name().unwrap())
611
0
                    .with_extension("json");
612
0
                let json = serde_json::to_string(&ast)?;
613
0
                std::fs::write(&json_path, json)
614
0
                    .with_context(|| format!("failed to write {json_path:?}"))?;
615
0
                for (name, bytes) in self.modules_by_filename.iter() {
616
0
                    let cwasm_path = path.join(name).with_extension("cwasm");
617
0
                    let cwasm = if wasmparser::Parser::is_core_wasm(&bytes) {
618
0
                        self.engine().precompile_module(bytes)
619
                    } else {
620
                        #[cfg(feature = "component-model")]
621
                        {
622
0
                            self.engine().precompile_component(bytes)
623
                        }
624
                        #[cfg(not(feature = "component-model"))]
625
                        bail!("component-model support not enabled");
626
                    };
627
0
                    if let Ok(cwasm) = cwasm {
628
0
                        std::fs::write(&cwasm_path, cwasm)
629
0
                            .with_context(|| format!("failed to write {cwasm_path:?}"))?;
630
0
                    }
631
                }
632
0
                Ok(())
633
            }
634
3.83k
            None => self.run_directives(ast.commands, filename),
635
        }
636
3.83k
    }
637
638
3.85k
    fn run_directives(&mut self, directives: Vec<Command<'_>>, filename: &str) -> Result<()> {
639
3.85k
        thread::scope(|scope| {
640
3.85k
            let mut threads = HashMap::new();
641
429k
            for directive in directives {
642
429k
                let line = directive.line();
643
429k
                log::debug!("running directive on {filename}:{line}");
644
429k
                self.run_directive(directive, filename, &scope, &mut threads)
645
429k
                    .with_context(|| format!("failed directive on {filename}:{line}"))?;
646
            }
647
3.85k
            Ok(())
648
3.85k
        })
649
3.85k
    }
650
651
429k
    fn run_directive<'a>(
652
429k
        &mut self,
653
429k
        directive: Command<'a>,
654
429k
        filename: &'a str,
655
429k
        // wast: &'a str,
656
429k
        scope: &'a thread::Scope<'a, '_>,
657
429k
        threads: &mut HashMap<String, thread::ScopedJoinHandle<'a, Result<()>>>,
658
429k
    ) -> Result<()> {
659
        use Command::*;
660
661
429k
        match directive {
662
            Module {
663
19.8k
                name,
664
19.8k
                file,
665
                line: _,
666
            } => {
667
19.8k
                let module = self.module_definition(&file)?;
668
19.8k
                self.module(name.as_deref(), &module)?;
669
            }
670
            ModuleDefinition {
671
285
                name,
672
285
                file,
673
                line: _,
674
            } => {
675
285
                let module = self.module_definition(&file)?;
676
285
                self.modules.insert(name.map(|s| s.to_string()), module);
677
            }
678
            ModuleInstance {
679
1.40k
                instance,
680
1.40k
                module,
681
                line: _,
682
            } => {
683
1.40k
                let module = self
684
1.40k
                    .modules
685
1.40k
                    .get(&module.as_ref().map(|s| s.to_string()))
686
1.40k
                    .cloned()
687
1.40k
                    .ok_or_else(|| format_err!("no module named {module:?}"))?;
688
1.40k
                self.module(instance.as_deref(), &module)?;
689
            }
690
492
            Register { line: _, name, as_ } => {
691
492
                self.register(name.as_deref(), &as_)?;
692
            }
693
2.48k
            Action { action, line: _ } => {
694
2.48k
                self.perform_action(&action)?;
695
            }
696
            AssertReturn {
697
336k
                action,
698
336k
                expected,
699
                line: _,
700
            } => {
701
336k
                let result = self.perform_action(&action)?;
702
336k
                self.assert_return(result, &expected)?;
703
            }
704
            AssertTrap {
705
39.9k
                action,
706
39.9k
                text,
707
                line: _,
708
            } => {
709
39.9k
                let result = self.perform_action(&action)?;
710
39.9k
                self.assert_trap(result, &text)?;
711
            }
712
            AssertUninstantiable {
713
539
                file,
714
539
                text,
715
                line: _,
716
            } => {
717
539
                let result = match self.module_definition(&file)? {
718
275
                    ModuleKind::Core(module) => self
719
275
                        .instantiate_module(&module)?
720
275
                        .map(|_| Results::Core(Vec::new())),
721
                    #[cfg(feature = "component-model")]
722
264
                    ModuleKind::Component(component) => self
723
264
                        .instantiate_component(&component)?
724
264
                        .map(|_| Results::Component(Vec::new())),
725
                };
726
539
                self.assert_trap(result, &text)?;
727
            }
728
            AssertExhaustion {
729
199
                action,
730
199
                text,
731
                line: _,
732
            } => {
733
199
                let result = self.perform_action(&action)?;
734
199
                self.assert_trap(result, &text)?;
735
            }
736
            AssertInvalid {
737
16.5k
                file,
738
16.5k
                text,
739
                line: _,
740
            } => {
741
16.5k
                let err = match self.module_definition(&file) {
742
0
                    Ok(_) => bail!("expected module to fail to build"),
743
16.5k
                    Err(e) => e,
744
                };
745
16.5k
                self.match_error_message(&text, err)?;
746
            }
747
            AssertMalformed {
748
9.02k
                file,
749
                text: _,
750
                line: _,
751
            } => {
752
9.02k
                if let Ok(_) = self.module_definition(&file) {
753
0
                    bail!("expected malformed module to fail to instantiate");
754
9.02k
                }
755
            }
756
            AssertUnlinkable {
757
1.73k
                file,
758
1.73k
                text,
759
                line: _,
760
            } => {
761
1.73k
                let module = self.module_definition(&file)?;
762
1.73k
                let err = match self.module(None, &module) {
763
0
                    Ok(_) => bail!("expected module to fail to link"),
764
1.73k
                    Err(e) => e,
765
                };
766
1.73k
                self.match_error_message(&text, err)?;
767
            }
768
125
            AssertException { line: _, action } => {
769
125
                let result = self.perform_action(&action)?;
770
125
                self.assert_exception(result)?;
771
            }
772
773
            Thread {
774
17
                name,
775
17
                shared_module,
776
17
                commands,
777
                line: _,
778
            } => {
779
17
                let mut core_linker = Linker::new(self.engine());
780
17
                if let Some(id) = shared_module {
781
14
                    let items = self
782
14
                        .core_linker
783
14
                        .iter(&mut self.core_store)
784
238
                        .filter(|(module, _, _)| *module == &id[..])
785
14
                        .collect::<Vec<_>>();
786
14
                    for (module, name, item) in items {
787
14
                        core_linker.define(&mut self.core_store, module, name, item)?;
788
                    }
789
3
                }
790
17
                let mut child_cx = WastContext {
791
17
                    current: None,
792
17
                    core_linker,
793
                    #[cfg(feature = "component-model")]
794
17
                    component_linker: component::Linker::new(self.engine()),
795
                    core_store: {
796
17
                        let mut store = Store::new(self.engine(), ());
797
17
                        (self.configure_store)(&mut store);
798
17
                        store
799
                    },
800
17
                    modules: self.modules.clone(),
801
17
                    async_runtime: self.async_runtime.as_ref().map(|_| {
802
0
                        tokio::runtime::Builder::new_current_thread()
803
0
                            .build()
804
0
                            .unwrap()
805
0
                    }),
806
17
                    generate_dwarf: self.generate_dwarf,
807
17
                    modules_by_filename: self.modules_by_filename.clone(),
808
17
                    precompile_load: self.precompile_load.clone(),
809
17
                    precompile_save: self.precompile_save.clone(),
810
17
                    configure_store: self.configure_store.clone(),
811
17
                    ignore_error_messages: self.ignore_error_messages,
812
                };
813
17
                let child = scope.spawn(move || child_cx.run_directives(commands, filename));
814
17
                threads.insert(name.to_string(), child);
815
            }
816
17
            Wait { thread, .. } => {
817
17
                threads
818
17
                    .remove(&thread[..])
819
17
                    .ok_or_else(|| format_err!("no thread named `{thread}`"))?
820
17
                    .join()
821
17
                    .unwrap()?;
822
            }
823
824
            AssertSuspension { .. } => {
825
0
                bail!("unimplemented wast directive");
826
            }
827
828
            AssertMalformedCustom {
829
                file: _,
830
                text: _,
831
                line: _,
832
            }
833
            | AssertInvalidCustom {
834
                file: _,
835
                text: _,
836
                line: _,
837
0
            } => bail!("unimplemented wast directives"),
838
        }
839
840
429k
        Ok(())
841
429k
    }
842
843
    /// Run a wast script from a file.
844
0
    pub fn run_file(&mut self, path: &Path) -> Result<()> {
845
0
        match &self.precompile_load {
846
0
            Some(precompile) => {
847
0
                let file = precompile
848
0
                    .join(path.file_name().unwrap())
849
0
                    .with_extension("json");
850
0
                let json = std::fs::read_to_string(&file)
851
0
                    .with_context(|| format!("failed to read {file:?}"))?;
852
0
                let wast = serde_json::from_str::<json_from_wast::Wast<'_>>(&json)?;
853
0
                self.run_directives(wast.commands, &wast.source_filename)
854
            }
855
            None => {
856
0
                let bytes = std::fs::read(path)
857
0
                    .with_context(|| format!("failed to read `{}`", path.display()))?;
858
0
                self.run_wast(path.to_str().unwrap(), &bytes)
859
            }
860
        }
861
0
    }
862
863
    /// Whether or not to generate DWARF debugging information in custom
864
    /// sections in modules being tested.
865
0
    pub fn generate_dwarf(&mut self, enable: bool) -> &mut Self {
866
0
        self.generate_dwarf = enable;
867
0
        self
868
0
    }
869
870
18.3k
    fn match_error_message(&self, expected: &str, err: wasmtime::Error) -> Result<()> {
871
18.3k
        if self.ignore_error_messages {
872
17.8k
            return Ok(());
873
512
        }
874
512
        let actual = format!("{err:?}");
875
512
        if actual.contains(expected) {
876
512
            return Ok(());
877
0
        }
878
0
        bail!("assert_invalid: expected \"{expected}\", got \"{actual}\"",)
879
18.3k
    }
880
}