Skip to main content

wasmtime_cli/commands/
run.rs

1//! The module that implements the `wasmtime run` command.
2
3#![cfg_attr(
4    not(feature = "component-model"),
5    allow(irrefutable_let_patterns, unreachable_patterns)
6)]
7
8use crate::common::{Profile, RunCommon, RunTarget};
9use clap::Parser;
10use std::ffi::OsString;
11use std::path::{Path, PathBuf};
12#[cfg(feature = "debug")]
13use std::pin::Pin;
14use std::thread;
15use wasmtime::{
16    Engine, Error, Func, Module, Result, Store, StoreLimits, Val, ValType, bail,
17    error::Context as _, format_err,
18};
19use wasmtime_wasi::{WasiCtxView, WasiView};
20
21#[cfg(feature = "wasi-config")]
22use wasmtime_wasi_config::{WasiConfig, WasiConfigVariables};
23#[cfg(feature = "wasi-http")]
24use wasmtime_wasi_http::WasiHttpCtx;
25#[cfg(feature = "wasi-keyvalue")]
26use wasmtime_wasi_keyvalue::{WasiKeyValue, WasiKeyValueCtx, WasiKeyValueCtxBuilder};
27#[cfg(feature = "wasi-nn")]
28use wasmtime_wasi_nn::wit::WasiNnView;
29
30fn parse_preloads(s: &str) -> Result<(String, PathBuf)> {
31    let parts: Vec<&str> = s.splitn(2, '=').collect();
32    if parts.len() != 2 {
33        bail!("must contain exactly one equals character ('=')");
34    }
35    Ok((parts[0].into(), parts[1].into()))
36}
37
38/// Runs a WebAssembly module
39#[derive(Parser)]
40pub struct RunCommand {
41    #[command(flatten)]
42    #[expect(missing_docs, reason = "don't want to mess with clap doc-strings")]
43    pub run: RunCommon,
44
45    /// The the function to run
46    ///
47    /// When used with modules, this must be the export name of a function.
48    /// Arguments to the function are parsed from trailing arguments provided
49    /// after all options.
50    ///
51    /// When used with components, this must be a wave-encoded function call,
52    /// e.g. `wasi:cli/run.run@0.2.0()` or
53    /// `your:pkg/iface.func("arguments in wave encoding")`. Bare function
54    /// names (e.g. `run()`) are accepted and searched for in all exported
55    /// instances, and must be unambigious.
56    #[arg(long, value_name = "FUNCTION")]
57    pub invoke: Option<String>,
58
59    #[command(flatten)]
60    #[expect(missing_docs, reason = "don't want to mess with clap doc-strings")]
61    pub preloads: Preloads,
62
63    /// Override the value of `argv[0]`, typically the name of the executable of
64    /// the application being run.
65    ///
66    /// This can be useful to pass in situations where a CLI tool is being
67    /// executed that dispatches its functionality on the value of `argv[0]`
68    /// without needing to rename the original wasm binary.
69    #[arg(long)]
70    pub argv0: Option<String>,
71
72    /// Override the module bytes loaded from disk. When set, the
73    /// first positional argument is ignored for loading purposes and
74    /// these bytes are used instead. This is not a CLI option; it is
75    /// used internally to inject pre-built bytes (e.g. for an
76    /// included debug adapter).
77    #[arg(skip)]
78    pub module_bytes: Option<&'static [u8]>,
79
80    /// The WebAssembly module to run and arguments to pass to it.
81    ///
82    /// Arguments passed to the wasm module will be configured as WASI CLI
83    /// arguments unless the `--invoke` CLI argument is passed in which case
84    /// arguments will be interpreted as arguments to the function specified.
85    #[arg(value_name = "WASM", trailing_var_arg = true, required = true)]
86    pub module_and_args: Vec<OsString>,
87}
88
89impl RunCommand {
90    /// Split off a sub-command representing the invocation of a
91    /// debugger component side-car to this execution.
92    ///
93    /// This is used to factor out most of the environment bringup for
94    /// the debugger component environment.
95    ///
96    /// This also adjusts the guest options as needed to enable
97    /// debugging (e.g., implicitly set `-D guest-debug=y`).
98    #[cfg(feature = "debug")]
99    pub(crate) fn debugger_run(&mut self) -> Result<Option<RunCommand>> {
100        fn set_implicit_option(
101            place: &str,
102            name: &str,
103            setting: &mut Option<bool>,
104            value: bool,
105        ) -> Result<()> {
106            if *setting == Some(!value) {
107                bail!(
108                    "Explicitly-set option on {place} {name}={} is not compatible with debugging-implied setting {value}",
109                    setting.unwrap()
110                );
111            }
112            *setting = Some(value);
113            Ok(())
114        }
115
116        // When -g is specified, set up the debugger path and args from
117        // the built-in gdbstub component.
118        #[cfg(feature = "gdbstub")]
119        let override_bytes = if let Some(addr) = self.run.gdbstub.as_deref() {
120            if self.run.common.debug.debugger.is_some() {
121                bail!("-g/--gdb cannot be combined with -Ddebugger=");
122            }
123            // Accept either a bare port number or a full address:port.
124            let addr = if addr.parse::<u16>().is_ok() {
125                format!("127.0.0.1:{addr}")
126            } else {
127                use std::net::SocketAddr;
128                addr.parse::<SocketAddr>()
129                    .with_context(|| format!("invalid gdbstub address: `{addr}`"))?;
130                addr.to_string()
131            };
132            self.run.common.debug.debugger = Some("<built-in gdbstub>".into());
133            self.run.common.debug.arg.push(addr);
134            Some(gdbstub_component_artifact::GDBSTUB_COMPONENT)
135        } else {
136            None
137        };
138        #[cfg(not(feature = "gdbstub"))]
139        let override_bytes = None;
140
141        if let Some(debugger_component_path) = self.run.common.debug.debugger.as_ref() {
142            set_implicit_option(
143                "debuggee",
144                "guest_debug",
145                &mut self.run.common.debug.guest_debug,
146                true,
147            )?;
148            set_implicit_option(
149                "debuggee",
150                "epoch_interruption",
151                &mut self.run.common.wasm.epoch_interruption,
152                true,
153            )?;
154
155            let mut debugger_run = RunCommand::try_parse_from(
156                ["run".into(), debugger_component_path.into()]
157                    .into_iter()
158                    .chain(self.run.common.debug.arg.iter().map(OsString::from)),
159            )?;
160            debugger_run.module_bytes = override_bytes;
161
162            // Explicitly permit TCP sockets for the debugger-main
163            // environment, if not already set.
164            debugger_run.run.common.wasi.tcp.get_or_insert(true);
165            debugger_run
166                .run
167                .common
168                .wasi
169                .inherit_network
170                .get_or_insert(true);
171
172            // Copy over stdin/stdout/stderr inheritance settings,
173            // except default to `false` for the debugger (so it
174            // doesn't interfere with the debuggee's CLI interface, if
175            // any). We expect most debug components will serve an
176            // interface over the network; for those that want a TUI,
177            // their setup instructions can instruct the user to set
178            // these flags as needed.
179            set_implicit_option(
180                "debugger",
181                "inherit_stdin",
182                &mut debugger_run.run.common.wasi.inherit_stdin,
183                self.run.common.debug.inherit_stdin.unwrap_or(false),
184            )?;
185            set_implicit_option(
186                "debugger",
187                "inherit_stdout",
188                &mut debugger_run.run.common.wasi.inherit_stdout,
189                self.run.common.debug.inherit_stdout.unwrap_or(false),
190            )?;
191            set_implicit_option(
192                "debugger",
193                "inherit_stderr",
194                &mut debugger_run.run.common.wasi.inherit_stderr,
195                self.run.common.debug.inherit_stderr.unwrap_or(false),
196            )?;
197            Ok(Some(debugger_run))
198        } else {
199            Ok(None)
200        }
201    }
202}
203
204#[expect(missing_docs, reason = "don't want to mess with clap doc-strings")]
205#[derive(Parser, Default, Clone)]
206pub struct Preloads {
207    /// Load the given WebAssembly module before the main module
208    #[arg(
209        long = "preload",
210        number_of_values = 1,
211        value_name = "NAME=MODULE_PATH",
212        value_parser = parse_preloads,
213    )]
214    modules: Vec<(String, PathBuf)>,
215}
216
217/// Dispatch between either a core or component linker.
218#[expect(missing_docs, reason = "self-explanatory")]
219pub enum CliLinker {
220    Core(wasmtime::Linker<Host>),
221    #[cfg(feature = "component-model")]
222    Component(wasmtime::component::Linker<Host>),
223}
224
225/// Dispatch between either a core or component instance.
226#[expect(missing_docs, reason = "self-explanatory")]
227pub enum CliInstance {
228    Core(wasmtime::Instance),
229    #[cfg(feature = "component-model")]
230    Component(wasmtime::component::Instance),
231}
232
233impl RunCommand {
234    /// Executes the command.
235    #[cfg(feature = "run")]
236    pub fn execute(mut self) -> Result<()> {
237        let runtime = tokio::runtime::Builder::new_multi_thread()
238            .enable_time()
239            .enable_io()
240            .build()?;
241
242        runtime.block_on(async {
243            self.run.common.init_logging()?;
244
245            #[cfg(feature = "debug")]
246            let debug_run = self.debugger_run()?;
247
248            let engine = self.new_engine()?;
249            let main = self.run.load_module(
250                &engine,
251                self.module_and_args[0].as_ref(),
252                self.module_bytes.as_ref().map(|v| &v[..]),
253            )?;
254            let (mut store, mut linker) = self.new_store_and_linker(&engine, &main)?;
255
256            #[cfg(feature = "debug")]
257            if let Some(mut debug_run) = debug_run {
258                let debug_engine = debug_run.new_engine()?;
259                let debug_main = debug_run.run.load_module(
260                    &debug_engine,
261                    debug_run.module_and_args[0].as_ref(),
262                    debug_run.module_bytes.as_ref().map(|v| &v[..]),
263                )?;
264                let (mut debug_store, debug_linker) =
265                    debug_run.new_store_and_linker(&debug_engine, &debug_main)?;
266
267                let debug_component = match debug_main {
268                    RunTarget::Core(_) => wasmtime::bail!(
269                        "Debugger component is a core module; only components are supported"
270                    ),
271                    RunTarget::Component(c) => c,
272                };
273                let mut debug_linker = match debug_linker {
274                    CliLinker::Core(_) => unreachable!(),
275                    CliLinker::Component(l) => l,
276                };
277                debug_run.add_debugger_api(&mut debug_linker)?;
278
279                // Pre-register the main module on the debuggee store
280                // so that `debug_all_modules()` returns it before any
281                // Wasm executes. This lets the debugger see modules
282                // and set breakpoints at the initial stop.
283                match &main {
284                    RunTarget::Core(m) => {
285                        store.debug_register_module(m)?;
286                    }
287                    #[cfg(feature = "component-model")]
288                    RunTarget::Component(c) => {
289                        store.debug_register_component(c)?;
290                    }
291                }
292
293                debug_run
294                    .invoke_debugger(
295                        &mut debug_store,
296                        &debug_component,
297                        &mut debug_linker,
298                        store,
299                        move |store| {
300                            Box::pin(async move {
301                                let engine_clone = store.engine().clone();
302                                let cancel =
303                                    std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
304                                let cancel_clone = cancel.clone();
305                                let epoch_thread = thread::spawn(move || {
306                                    while !cancel_clone.load(std::sync::atomic::Ordering::Relaxed) {
307                                        thread::sleep(std::time::Duration::from_millis(1));
308                                        engine_clone.increment_epoch();
309                                    }
310                                });
311                                self.instantiate_and_run(&engine, &mut linker, &main, store)
312                                    .await?;
313                                cancel.store(true, std::sync::atomic::Ordering::Relaxed);
314                                epoch_thread
315                                    .join()
316                                    .map_err(|_| wasmtime::Error::msg("epoch thread panicked"))?;
317                                Ok(())
318                            })
319                        },
320                    )
321                    .await?;
322                return Ok(());
323            }
324
325            self.instantiate_and_run(&engine, &mut linker, &main, &mut store)
326                .await?;
327            Ok(())
328        })
329    }
330
331    /// Creates a new `Engine` with the configuration for this command.
332    pub fn new_engine(&mut self) -> Result<Engine> {
333        let mut config = self.run.common.config(None)?;
334
335        if self.run.common.wasm.timeout.is_some() {
336            config.epoch_interruption(true);
337        }
338        match self.run.profile {
339            Some(Profile::Native(s)) => {
340                config.profiler(s);
341            }
342            Some(Profile::Guest { .. }) => {
343                // Further configured down below as well.
344                config.epoch_interruption(true);
345            }
346            None => {}
347        }
348
349        Engine::new(&config)
350    }
351
352    /// Populates a new `Store` and `CliLinker` with the configuration in this
353    /// command.
354    ///
355    /// The `engine` provided is used to for the store/linker and the `main`
356    /// provided is the module/component that is going to be run.
357    pub fn new_store_and_linker(
358        &mut self,
359        engine: &Engine,
360        main: &RunTarget,
361    ) -> Result<(Store<Host>, CliLinker)> {
362        // Validate coredump-on-trap argument
363        if let Some(path) = &self.run.common.debug.coredump {
364            if path.contains("%") {
365                bail!("the coredump-on-trap path does not support patterns yet.")
366            }
367        }
368
369        let mut linker = match &main {
370            RunTarget::Core(_) => CliLinker::Core(wasmtime::Linker::new(&engine)),
371            #[cfg(feature = "component-model")]
372            RunTarget::Component(_) => {
373                CliLinker::Component(wasmtime::component::Linker::new(&engine))
374            }
375        };
376        if let Some(enable) = self.run.common.wasm.unknown_exports_allow {
377            match &mut linker {
378                CliLinker::Core(l) => {
379                    l.allow_unknown_exports(enable);
380                }
381                #[cfg(feature = "component-model")]
382                CliLinker::Component(_) => {
383                    bail!("--allow-unknown-exports not supported with components");
384                }
385            }
386        }
387
388        let host = Host::default();
389
390        let mut store = Store::new(&engine, host);
391        self.populate_with_wasi(&mut linker, &mut store)?;
392
393        store.data_mut().limits = self.run.store_limits();
394        store.limiter(|t| &mut t.limits);
395
396        // If fuel has been configured, we want to add the configured
397        // fuel amount to this store.
398        if let Some(fuel) = self.run.common.wasm.fuel {
399            store.set_fuel(fuel)?;
400        }
401
402        Ok((store, linker))
403    }
404
405    #[cfg(feature = "debug")]
406    pub(crate) fn add_debugger_api(
407        &mut self,
408        linker: &mut wasmtime::component::Linker<Host>,
409    ) -> Result<()> {
410        wasmtime_debugger::add_to_linker(linker, |x| x.ctx().table)?;
411        Ok(())
412    }
413
414    /// Executes the `main` after instantiating it within `store`.
415    ///
416    /// This applies all configuration within `self`, such as timeouts and
417    /// profiling, and performs the execution. The resulting instance is
418    /// returned.
419    pub async fn instantiate_and_run(
420        &self,
421        engine: &Engine,
422        linker: &mut CliLinker,
423        main: &RunTarget,
424        store: &mut Store<Host>,
425    ) -> Result<CliInstance> {
426        let dur = self
427            .run
428            .common
429            .wasm
430            .timeout
431            .unwrap_or(std::time::Duration::MAX);
432        let result = tokio::time::timeout(dur, async {
433            let mut profiled_modules: Vec<(String, Module)> = Vec::new();
434            if let RunTarget::Core(m) = &main {
435                profiled_modules.push(("".to_string(), m.clone()));
436            }
437
438            // Load the preload wasm modules.
439            for (name, path) in self.preloads.modules.iter() {
440                // Read the wasm module binary either as `*.wat` or a raw binary
441                let preload_target = self.run.load_module(&engine, path, None)?;
442                let preload_module = match preload_target {
443                    RunTarget::Core(m) => m,
444                    #[cfg(feature = "component-model")]
445                    RunTarget::Component(_) => {
446                        bail!("components cannot be loaded with `--preload`")
447                    }
448                };
449                profiled_modules.push((name.to_string(), preload_module.clone()));
450
451                // Add the module's functions to the linker.
452                match linker {
453                    #[cfg(feature = "cranelift")]
454                    CliLinker::Core(linker) => {
455                        linker
456                            .module_async(&mut *store, name, &preload_module)
457                            .await
458                            .with_context(|| {
459                                format!(
460                                    "failed to process preload `{}` at `{}`",
461                                    name,
462                                    path.display()
463                                )
464                            })?;
465                    }
466                    #[cfg(not(feature = "cranelift"))]
467                    CliLinker::Core(_) => {
468                        bail!("support for --preload disabled at compile time");
469                    }
470                    #[cfg(feature = "component-model")]
471                    CliLinker::Component(_) => {
472                        bail!("--preload cannot be used with components");
473                    }
474                }
475            }
476
477            self.load_main_module(store, linker, &main, profiled_modules)
478                .await
479                .with_context(|| {
480                    format!(
481                        "failed to run main module `{}`",
482                        self.module_and_args[0].to_string_lossy()
483                    )
484                })
485        })
486        .await;
487
488        // Load the main wasm module.
489        let instance = match result.unwrap_or_else(|elapsed| {
490            Err(wasmtime::Error::from(wasmtime::Trap::Interrupt))
491                .with_context(|| format!("timed out after {elapsed}"))
492        }) {
493            Ok(instance) => instance,
494            Err(e) => {
495                // Exit the process if Wasmtime understands the error;
496                // otherwise, fall back on Rust's default error printing/return
497                // code.
498                if store.data().wasip1_ctx.is_some() {
499                    if let Some(exit) = e.downcast_ref::<wasmtime_wasi::I32Exit>() {
500                        std::process::exit(exit.0);
501                    }
502                }
503                if e.is::<wasmtime::Trap>() {
504                    eprintln!("Error: {e:?}");
505                    cfg_if::cfg_if! {
506                        if #[cfg(unix)] {
507                            std::process::exit(rustix::process::EXIT_SIGNALED_SIGABRT);
508                        } else if #[cfg(windows)] {
509                            // https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/abort?view=vs-2019
510                            std::process::exit(3);
511                        }
512                    }
513                }
514                return Err(e);
515            }
516        };
517
518        Ok(instance)
519    }
520
521    pub(crate) fn compute_argv(&self) -> Result<Vec<String>> {
522        let mut result = Vec::new();
523
524        for (i, arg) in self.module_and_args.iter().enumerate() {
525            // For argv[0], which is the program name. Only include the base
526            // name of the main wasm module, to avoid leaking path information.
527            let arg = if i == 0 {
528                match &self.argv0 {
529                    Some(s) => s.as_ref(),
530                    None => Path::new(arg).components().next_back().unwrap().as_os_str(),
531                }
532            } else {
533                arg.as_ref()
534            };
535            result.push(
536                arg.to_str()
537                    .ok_or_else(|| format_err!("failed to convert {arg:?} to utf-8"))?
538                    .to_string(),
539            );
540        }
541
542        Ok(result)
543    }
544
545    fn setup_epoch_handler(
546        &self,
547        store: &mut Store<Host>,
548        main_target: &RunTarget,
549        profiled_modules: Vec<(String, Module)>,
550    ) -> Result<Box<dyn FnOnce(&mut Store<Host>) + Send>> {
551        // If a debugger component is attached, we set up epoch
552        // interruptions in `debugger_run()` above when enabling guest
553        // instrumentation; we need to ensure that epoch interruptions
554        // cause a debug event but no trap here. This overrides other
555        // behavior below.
556        if self.run.common.debug.debugger.is_some() {
557            if self.run.profile.is_some() {
558                bail!("Cannot set profile options together with debugging; they are incompatible");
559            }
560            if self.run.common.wasm.timeout.is_some() {
561                bail!("Cannot set timeout options together with debugging; they are incompatible");
562            }
563            store.epoch_deadline_async_yield_and_update(1);
564        } else {
565            if let Some(Profile::Guest { path, interval }) = &self.run.profile {
566                #[cfg(feature = "profiling")]
567                return Ok(self.setup_guest_profiler(
568                    store,
569                    main_target,
570                    profiled_modules,
571                    path,
572                    *interval,
573                )?);
574                #[cfg(not(feature = "profiling"))]
575                {
576                    let _ = (profiled_modules, path, interval, main_target);
577                    bail!("support for profiling disabled at compile time");
578                }
579            }
580
581            if let Some(timeout) = self.run.common.wasm.timeout {
582                store.set_epoch_deadline(1);
583                let engine = store.engine().clone();
584                thread::spawn(move || {
585                    thread::sleep(timeout);
586                    engine.increment_epoch();
587                });
588            }
589        }
590
591        Ok(Box::new(|_store| {}))
592    }
593
594    #[cfg(feature = "profiling")]
595    fn setup_guest_profiler(
596        &self,
597        store: &mut Store<Host>,
598        main_target: &RunTarget,
599        profiled_modules: Vec<(String, Module)>,
600        path: &str,
601        interval: std::time::Duration,
602    ) -> Result<Box<dyn FnOnce(&mut Store<Host>) + Send>> {
603        use wasmtime::{AsContext, GuestProfiler, StoreContext, StoreContextMut, UpdateDeadline};
604
605        let module_name = self.module_and_args[0].to_str().unwrap_or("<main module>");
606        store.data_mut().guest_profiler = match main_target {
607            RunTarget::Core(_m) => Some(GuestProfiler::new(
608                store.engine(),
609                module_name,
610                interval,
611                profiled_modules,
612            )?),
613            RunTarget::Component(component) => Some(GuestProfiler::new_component(
614                store.engine(),
615                module_name,
616                interval,
617                component.clone(),
618                profiled_modules,
619            )?),
620        };
621
622        fn sample(
623            mut store: StoreContextMut<Host>,
624            f: impl FnOnce(&mut GuestProfiler, StoreContext<Host>),
625        ) {
626            let mut profiler = store.data_mut().guest_profiler.take().unwrap();
627            f(&mut profiler, store.as_context());
628            store.data_mut().guest_profiler = Some(profiler);
629        }
630
631        store.call_hook(|store, kind| {
632            sample(store, |profiler, store| profiler.call_hook(store, kind));
633            Ok(())
634        });
635
636        if let Some(timeout) = self.run.common.wasm.timeout {
637            let mut timeout = (timeout.as_secs_f64() / interval.as_secs_f64()).ceil() as u64;
638            assert!(timeout > 0);
639            store.epoch_deadline_callback(move |store| {
640                sample(store, |profiler, store| {
641                    profiler.sample(store, std::time::Duration::ZERO)
642                });
643                timeout -= 1;
644                if timeout == 0 {
645                    bail!("timeout exceeded");
646                }
647                Ok(UpdateDeadline::Continue(1))
648            });
649        } else {
650            store.epoch_deadline_callback(move |store| {
651                sample(store, |profiler, store| {
652                    profiler.sample(store, std::time::Duration::ZERO)
653                });
654                Ok(UpdateDeadline::Continue(1))
655            });
656        }
657
658        store.set_epoch_deadline(1);
659        let engine = store.engine().clone();
660        thread::spawn(move || {
661            loop {
662                thread::sleep(interval);
663                engine.increment_epoch();
664            }
665        });
666
667        let path = path.to_string();
668        Ok(Box::new(move |store| {
669            let profiler = store.data_mut().guest_profiler.take().unwrap();
670            if let Err(e) = std::fs::File::create(&path)
671                .map_err(wasmtime::Error::new)
672                .and_then(|output| profiler.finish(std::io::BufWriter::new(output)))
673            {
674                eprintln!("failed writing profile at {path}: {e:#}");
675            } else {
676                eprintln!();
677                eprintln!("Profile written to: {path}");
678                eprintln!("View this profile at https://profiler.firefox.com/.");
679            }
680        }))
681    }
682
683    async fn load_main_module(
684        &self,
685        store: &mut Store<Host>,
686        linker: &mut CliLinker,
687        main_target: &RunTarget,
688        profiled_modules: Vec<(String, Module)>,
689    ) -> Result<CliInstance> {
690        // The main module might be allowed to have unknown imports, which
691        // should be defined as traps:
692        if self.run.common.wasm.unknown_imports_trap == Some(true) {
693            match linker {
694                CliLinker::Core(linker) => {
695                    linker.define_unknown_imports_as_traps(main_target.unwrap_core())?;
696                }
697                #[cfg(feature = "component-model")]
698                CliLinker::Component(linker) => {
699                    linker.define_unknown_imports_as_traps(main_target.unwrap_component())?;
700                }
701            }
702        }
703
704        // ...or as default values.
705        if self.run.common.wasm.unknown_imports_default == Some(true) {
706            match linker {
707                CliLinker::Core(linker) => {
708                    linker.define_unknown_imports_as_default_values(
709                        &mut *store,
710                        main_target.unwrap_core(),
711                    )?;
712                }
713                _ => bail!("cannot use `--default-values-unknown-imports` with components"),
714            }
715        }
716
717        let finish_epoch_handler =
718            self.setup_epoch_handler(store, main_target, profiled_modules)?;
719
720        let result = match linker {
721            CliLinker::Core(linker) => {
722                let module = main_target.unwrap_core();
723                let instance = linker
724                    .instantiate_async(&mut *store, &module)
725                    .await
726                    .with_context(|| {
727                        format!("failed to instantiate {:?}", self.module_and_args[0])
728                    })?;
729
730                // If `_initialize` is present, meaning a reactor, then invoke
731                // the function.
732                if let Some(func) = instance.get_func(&mut *store, "_initialize") {
733                    func.typed::<(), ()>(&store)?
734                        .call_async(&mut *store, ())
735                        .await?;
736                }
737
738                // Look for the specific function provided or otherwise look for
739                // "" or "_start" exports to run as a "main" function.
740                let func = if let Some(name) = &self.invoke {
741                    Some(
742                        instance
743                            .get_func(&mut *store, name)
744                            .ok_or_else(|| format_err!("no func export named `{name}` found"))?,
745                    )
746                } else {
747                    instance
748                        .get_func(&mut *store, "")
749                        .or_else(|| instance.get_func(&mut *store, "_start"))
750                };
751
752                if let Some(func) = func {
753                    self.invoke_func(store, func).await?;
754                }
755                Ok(CliInstance::Core(instance))
756            }
757            #[cfg(feature = "component-model")]
758            CliLinker::Component(linker) => {
759                let component = main_target.unwrap_component();
760                let result = if self.invoke.is_some() {
761                    self.invoke_component(&mut *store, component, linker).await
762                } else {
763                    self.run_command_component(&mut *store, component, linker)
764                        .await
765                };
766                result
767                    .map(CliInstance::Component)
768                    .map_err(|e| self.handle_core_dump(&mut *store, e))
769            }
770        };
771        finish_epoch_handler(store);
772
773        result
774    }
775
776    #[cfg(feature = "component-model")]
777    async fn invoke_component(
778        &self,
779        store: &mut Store<Host>,
780        component: &wasmtime::component::Component,
781        linker: &mut wasmtime::component::Linker<Host>,
782    ) -> Result<wasmtime::component::Instance> {
783        use wasmtime::component::{
784            Val,
785            wasm_wave::{
786                untyped::UntypedFuncCall,
787                wasm::{DisplayFuncResults, WasmFunc},
788            },
789        };
790
791        // Check if the invoke string is present
792        let invoke: &String = self.invoke.as_ref().unwrap();
793
794        let untyped_call = UntypedFuncCall::parse(invoke).with_context(|| {
795                format!(
796                    "Failed to parse invoke '{invoke}': See https://docs.wasmtime.dev/cli-options.html#run for syntax",
797                )
798        })?;
799
800        let name = untyped_call.item_name().map_err(|e| {
801            wasmtime::Error::from_anyhow(e).context(format!(
802                "parsing `{}` as a wit item name",
803                untyped_call.name()
804            ))
805        })?;
806
807        let (export, func_type) = Self::search_component_funcs(store, &component, &name)?;
808
809        let param_types = WasmFunc::params(&func_type).collect::<Vec<_>>();
810        let params = untyped_call
811            .to_wasm_params(&param_types)
812            .with_context(|| format!("while interpreting parameters in invoke \"{invoke}\""))?;
813
814        let instance = linker.instantiate_async(&mut *store, component).await?;
815
816        let func = instance
817            .get_func(&mut *store, export)
818            .expect("found export index");
819
820        let mut results = vec![Val::Bool(false); func_type.results().len()];
821        self.call_component_func(store, &params, func, &mut results)
822            .await?;
823
824        println!("{}", DisplayFuncResults(&results));
825        Ok(instance)
826    }
827
828    #[cfg(feature = "component-model")]
829    async fn call_component_func(
830        &self,
831        store: &mut Store<Host>,
832        params: &[wasmtime::component::Val],
833        func: wasmtime::component::Func,
834        results: &mut Vec<wasmtime::component::Val>,
835    ) -> Result<(), Error> {
836        #[cfg(feature = "component-model-async")]
837        if self.run.common.wasm.concurrency_support.unwrap_or(true) {
838            store
839                .run_concurrent(async |store| func.call_concurrent(store, params, results).await)
840                .await??;
841            return Ok(());
842        }
843
844        func.call_async(&mut *store, &params, results).await?;
845        Ok(())
846    }
847
848    /// Execute the default behavior for components on the CLI, looking for
849    /// `wasi:cli`-based commands and running their exported `run` function.
850    #[cfg(feature = "component-model")]
851    async fn run_command_component(
852        &self,
853        store: &mut Store<Host>,
854        component: &wasmtime::component::Component,
855        linker: &wasmtime::component::Linker<Host>,
856    ) -> Result<wasmtime::component::Instance> {
857        let instance = linker.instantiate_async(&mut *store, component).await?;
858
859        let mut result = None;
860        let _ = &mut result;
861
862        // If WASIp3 is enabled at compile time, enabled at runtime, and found
863        // in this component then use that to generate the result.
864        #[cfg(feature = "component-model-async")]
865        if self.run.common.wasi.p3.unwrap_or(crate::common::P3_DEFAULT) {
866            if let Ok(command) = wasmtime_wasi::p3::bindings::Command::new(&mut *store, &instance) {
867                result = Some(
868                    store
869                        .run_concurrent(async |store| command.wasi_cli_run().call_run(store).await)
870                        .await?,
871                );
872            }
873        }
874
875        let result = match result {
876            Some(result) => result,
877            // If WASIp3 wasn't found then fall back to requiring WASIp2 and
878            // this'll report an error if the right export doesn't exist.
879            None => {
880                wasmtime_wasi::p2::bindings::Command::new(&mut *store, &instance)?
881                    .wasi_cli_run()
882                    .call_run(&mut *store)
883                    .await
884            }
885        };
886        let wasm_result = result.context("failed to invoke `run` function")?;
887
888        // Translate the `Result<(),()>` produced by wasm into a feigned
889        // explicit exit here with status 1 if `Err(())` is returned.
890        match wasm_result {
891            Ok(()) => Ok(instance),
892            Err(()) => Err(wasmtime_wasi::I32Exit(1).into()),
893        }
894    }
895
896    /// Invoke a debugger component with a debuggee.
897    ///
898    /// The debugger runs in `store` (using run's `Host`), while the
899    /// debuggee wraps an arbitrary store type `T` and body closure.
900    #[cfg(feature = "debug")]
901    pub(crate) async fn invoke_debugger<
902        T: Send + 'static,
903        F: for<'a> FnOnce(
904                &'a mut Store<T>,
905            ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
906            + Send
907            + 'static,
908    >(
909        &self,
910        store: &mut Store<Host>,
911        component: &wasmtime::component::Component,
912        linker: &mut wasmtime::component::Linker<Host>,
913        debuggee_host: Store<T>,
914        body: F,
915    ) -> Result<()> {
916        let instance = linker.instantiate_async(&mut *store, component).await?;
917        let command = wasmtime_debugger::DebuggerComponent::new(&mut *store, &instance)?;
918        let debuggee = wasmtime_debugger::Debuggee::new(debuggee_host, body);
919        let debuggee = wasmtime_debugger::add_debuggee(store.data_mut().ctx().table, debuggee)?;
920        {
921            // Manually construct a borrow -- wasmtime-wit-bindgen
922            // generates code that consumes the `Resource<T>` for
923            // `call_debug()` below even though the WIT type is a
924            // `borrow<debuggee>`.
925            let borrowed = wasmtime::component::Resource::new_borrow(debuggee.rep());
926            let args = self.compute_argv()?;
927            command
928                .bytecodealliance_wasmtime_debugger()
929                .call_debug(&mut *store, borrowed, &args)
930                .await?;
931        }
932        let mut debuggee = store.data_mut().ctx().table.delete(debuggee)?;
933        debuggee.finish().await?;
934        Ok(())
935    }
936
937    #[cfg(feature = "component-model")]
938    fn search_component_funcs(
939        store: &mut Store<Host>,
940        component: &wasmtime::component::Component,
941        item_name: &wasmtime::component::wit_parser::ItemName,
942    ) -> Result<(
943        wasmtime::component::ComponentExportIndex,
944        wasmtime::component::types::ComponentFunc,
945    )> {
946        use wasmtime::component::types::ComponentItem as CItem;
947        // Start by looking up the item name directly.
948        // Only match this as the search if it provides a function - it may
949        // provide an instance of the same name, in which case we want the
950        // below to search through it.
951        match component.get_export(None, item_name) {
952            Some((CItem::ComponentFunc(func), index)) => return Ok((index, func.clone())),
953            _ => {}
954        }
955        if item_name.interface.is_some() || item_name.package.is_some() {
956            // If the item name specified a package or interface, and the
957            // ItemName based lookup failed to find it, we do not consider
958            // that it may be exported under an instance and terminate the
959            // search immediately:
960            bail!("No exported func named `{item_name}` in component.")
961        }
962        // If the item name does not specify a package or interface, and it
963        // wasn't found in the root of the component above, then we search all
964        // instance exports for a function by that name.
965        let needle = item_name.to_string();
966        let mut search = component
967            .component_type()
968            .exports(store.engine())
969            .filter_map(|(instname, item)| match item.ty {
970                CItem::ComponentInstance(inst) => {
971                    inst.exports(store.engine())
972                        .find_map(|(leafname, item)| match item.ty {
973                            CItem::ComponentFunc(func) => {
974                                if leafname == needle {
975                                    // The type::Component traversal
976                                    // didn't give us an export index, get
977                                    // that now:
978                                    let (_item, inst_index) = component
979                                        .get_export(None, instname)
980                                        .expect("found exported component instance");
981                                    let (_item, index) = component
982                                        .get_export(Some(&inst_index), leafname)
983                                        .expect("found func");
984                                    Some((index, func.clone(), instname.to_string()))
985                                } else {
986                                    None
987                                }
988                            }
989                            _ => None,
990                        })
991                }
992
993                _ => None,
994            })
995            .collect::<Vec<_>>();
996
997        match search.len() {
998            0 => bail!("No exported func named `{needle}` in component."),
999            1 => {
1000                let (index, func, _instname) = search.pop().unwrap();
1001                Ok((index, func))
1002            }
1003            _ => {
1004                let candidates = search
1005                    .into_iter()
1006                    .map(|(_index, _func, instname)| {
1007                        // Manipulate as an ItemName to get package version
1008                        // correct in the output
1009                        let mut itemname: wasmtime::component::wit_parser::ItemName =
1010                            instname.parse().unwrap();
1011                        // Push the function name onto the itemname:
1012                        itemname.interface = Some(itemname.name.clone());
1013                        itemname.name = needle.to_string();
1014                        format!("`{itemname}`")
1015                    })
1016                    .collect::<Vec<_>>();
1017                bail!(
1018                    "Multiple instances contained funcs named `{needle}`, retry with a more specific name: {}",
1019                    candidates.join(", ")
1020                )
1021            }
1022        }
1023    }
1024
1025    async fn invoke_func(&self, store: &mut Store<Host>, func: Func) -> Result<()> {
1026        let ty = func.ty(&store);
1027        if ty.params().len() > 0 {
1028            eprintln!(
1029                "warning: using `--invoke` with a function that takes arguments \
1030                 is experimental and may break in the future"
1031            );
1032        }
1033        let mut args = self.module_and_args.iter().skip(1);
1034        let mut values = Vec::new();
1035        for ty in ty.params() {
1036            let val = match args.next() {
1037                Some(s) => s,
1038                None => {
1039                    if let Some(name) = &self.invoke {
1040                        bail!("not enough arguments for `{name}`")
1041                    } else {
1042                        bail!("not enough arguments for command default")
1043                    }
1044                }
1045            };
1046            let val = val
1047                .to_str()
1048                .ok_or_else(|| format_err!("argument is not valid utf-8: {val:?}"))?;
1049            values.push(match ty {
1050                // Supports both decimal and hexadecimal notation (with 0x prefix)
1051                ValType::I32 => Val::I32(if val.starts_with("0x") || val.starts_with("0X") {
1052                    i32::from_str_radix(&val[2..], 16)?
1053                } else {
1054                    val.parse::<i32>()?
1055                }),
1056                ValType::I64 => Val::I64(if val.starts_with("0x") || val.starts_with("0X") {
1057                    i64::from_str_radix(&val[2..], 16)?
1058                } else {
1059                    val.parse::<i64>()?
1060                }),
1061                ValType::F32 => Val::F32(val.parse::<f32>()?.to_bits()),
1062                ValType::F64 => Val::F64(val.parse::<f64>()?.to_bits()),
1063                t => bail!("unsupported argument type {t:?}"),
1064            });
1065        }
1066
1067        // Invoke the function and then afterwards print all the results that came
1068        // out, if there are any.
1069        let mut results = vec![Val::null_func_ref(); ty.results().len()];
1070        let invoke_res = func
1071            .call_async(&mut *store, &values, &mut results)
1072            .await
1073            .with_context(|| {
1074                if let Some(name) = &self.invoke {
1075                    format!("failed to invoke `{name}`")
1076                } else {
1077                    format!("failed to invoke command default")
1078                }
1079            });
1080
1081        if let Err(err) = invoke_res {
1082            return Err(self.handle_core_dump(&mut *store, err));
1083        }
1084
1085        if !results.is_empty() {
1086            eprintln!(
1087                "warning: using `--invoke` with a function that returns values \
1088                 is experimental and may break in the future"
1089            );
1090        }
1091
1092        for result in results {
1093            match result {
1094                Val::I32(i) => println!("{i}"),
1095                Val::I64(i) => println!("{i}"),
1096                Val::F32(f) => println!("{}", f32::from_bits(f)),
1097                Val::F64(f) => println!("{}", f64::from_bits(f)),
1098                Val::V128(i) => println!("{}", i.as_u128()),
1099                Val::ExternRef(None) => println!("<null externref>"),
1100                Val::ExternRef(Some(_)) => println!("<externref>"),
1101                Val::FuncRef(None) => println!("<null funcref>"),
1102                Val::FuncRef(Some(_)) => println!("<funcref>"),
1103                Val::AnyRef(None) => println!("<null anyref>"),
1104                Val::AnyRef(Some(_)) => println!("<anyref>"),
1105                Val::ExnRef(None) => println!("<null exnref>"),
1106                Val::ExnRef(Some(_)) => println!("<exnref>"),
1107                Val::ContRef(None) => println!("<null contref>"),
1108                Val::ContRef(Some(_)) => println!("<contref>"),
1109            }
1110        }
1111
1112        Ok(())
1113    }
1114
1115    #[cfg(feature = "coredump")]
1116    fn handle_core_dump(&self, store: &mut Store<Host>, err: Error) -> Error {
1117        let coredump_path = match &self.run.common.debug.coredump {
1118            Some(path) => path,
1119            None => return err,
1120        };
1121        if !err.is::<wasmtime::Trap>() {
1122            return err;
1123        }
1124        let source_name = self.module_and_args[0]
1125            .to_str()
1126            .unwrap_or_else(|| "unknown");
1127
1128        if let Err(coredump_err) = write_core_dump(store, &err, &source_name, coredump_path) {
1129            eprintln!("warning: coredump failed to generate: {coredump_err}");
1130            err
1131        } else {
1132            err.context(format!("core dumped at {coredump_path}"))
1133        }
1134    }
1135
1136    #[cfg(not(feature = "coredump"))]
1137    fn handle_core_dump(&self, _store: &mut Store<Host>, err: Error) -> Error {
1138        err
1139    }
1140
1141    /// Populates the given `Linker` with WASI APIs.
1142    fn populate_with_wasi(&self, linker: &mut CliLinker, store: &mut Store<Host>) -> Result<()> {
1143        self.run.validate_p3_option()?;
1144        let cli = self.run.validate_cli_enabled()?;
1145
1146        if cli != Some(false) {
1147            match linker {
1148                CliLinker::Core(linker) => {
1149                    match (self.run.common.wasi.preview2, self.run.common.wasi.threads) {
1150                        (Some(false), _) | (None, Some(true)) => {
1151                            let flag = if self.run.common.wasi.preview2 == Some(false) {
1152                                "-Spreview2=n"
1153                            } else {
1154                                "-Sthreads"
1155                            };
1156                            bail!("the `{flag}` flag is no longer supported")
1157                        }
1158                        // If preview2 was explicitly requested, always use it.
1159                        // Otherwise use it so long as threads are disabled.
1160                        //
1161                        // Note that for now `p0` is currently
1162                        // default-enabled but this may turn into
1163                        // default-disabled in the future.
1164                        (Some(true), _) | (None, Some(false) | None) => {
1165                            if self.run.common.wasi.preview0 != Some(false) {
1166                                wasmtime_wasi::p0::add_to_linker_async(linker, |t| t.wasip1_ctx())?;
1167                            }
1168                            wasmtime_wasi::p1::add_to_linker_async(linker, |t| t.wasip1_ctx())?;
1169                            self.set_wasi_ctx(store)?;
1170                        }
1171                    }
1172                }
1173                #[cfg(feature = "component-model")]
1174                CliLinker::Component(linker) => {
1175                    self.run.add_wasmtime_wasi_to_linker(linker)?;
1176                    self.set_wasi_ctx(store)?;
1177                }
1178            }
1179        }
1180
1181        if self.run.common.wasi.nn == Some(true) {
1182            #[cfg(not(feature = "wasi-nn"))]
1183            {
1184                bail!("Cannot enable wasi-nn when the binary is not compiled with this feature.");
1185            }
1186            #[cfg(all(feature = "wasi-nn", feature = "component-model"))]
1187            {
1188                let (backends, registry) = self.collect_preloaded_nn_graphs()?;
1189                match linker {
1190                    CliLinker::Core(linker) => {
1191                        wasmtime_wasi_nn::witx::add_to_linker(linker, |host| {
1192                            host.wasi_nn_witx.as_mut().unwrap()
1193                        })?;
1194                        store.data_mut().wasi_nn_witx =
1195                            Some(wasmtime_wasi_nn::witx::WasiNnCtx::new(backends, registry));
1196                    }
1197                    #[cfg(feature = "component-model")]
1198                    CliLinker::Component(linker) => {
1199                        wasmtime_wasi_nn::wit::add_to_linker(linker, |h: &mut Host| {
1200                            let ctx = h.wasip1_ctx.as_mut().expect("wasi is not configured");
1201                            let nn_ctx = h.wasi_nn_wit.as_mut().unwrap();
1202                            WasiNnView::new(ctx.ctx().table, nn_ctx)
1203                        })?;
1204                        store.data_mut().wasi_nn_wit =
1205                            Some(wasmtime_wasi_nn::wit::WasiNnCtx::new(backends, registry));
1206                    }
1207                }
1208            }
1209        }
1210
1211        if self.run.common.wasi.config == Some(true) {
1212            #[cfg(not(feature = "wasi-config"))]
1213            {
1214                bail!(
1215                    "Cannot enable wasi-config when the binary is not compiled with this feature."
1216                );
1217            }
1218            #[cfg(all(feature = "wasi-config", feature = "component-model"))]
1219            {
1220                match linker {
1221                    CliLinker::Core(_) => {
1222                        bail!("Cannot enable wasi-config for core wasm modules");
1223                    }
1224                    CliLinker::Component(linker) => {
1225                        let vars = WasiConfigVariables::from_iter(
1226                            self.run
1227                                .common
1228                                .wasi
1229                                .config_var
1230                                .iter()
1231                                .map(|v| (v.key.clone(), v.value.clone())),
1232                        );
1233
1234                        wasmtime_wasi_config::add_to_linker(linker, |h| {
1235                            WasiConfig::new(h.wasi_config.as_mut().unwrap())
1236                        })?;
1237                        store.data_mut().wasi_config = Some(vars);
1238                    }
1239                }
1240            }
1241        }
1242
1243        if self.run.common.wasi.keyvalue == Some(true) {
1244            #[cfg(not(feature = "wasi-keyvalue"))]
1245            {
1246                bail!(
1247                    "Cannot enable wasi-keyvalue when the binary is not compiled with this feature."
1248                );
1249            }
1250            #[cfg(all(feature = "wasi-keyvalue", feature = "component-model"))]
1251            {
1252                match linker {
1253                    CliLinker::Core(_) => {
1254                        bail!("Cannot enable wasi-keyvalue for core wasm modules");
1255                    }
1256                    CliLinker::Component(linker) => {
1257                        let ctx = WasiKeyValueCtxBuilder::new()
1258                            .in_memory_data(
1259                                self.run
1260                                    .common
1261                                    .wasi
1262                                    .keyvalue_in_memory_data
1263                                    .iter()
1264                                    .map(|v| (v.key.clone(), v.value.clone())),
1265                            )
1266                            .build();
1267
1268                        wasmtime_wasi_keyvalue::add_to_linker(linker, |h| {
1269                            let ctx = h.wasip1_ctx.as_mut().expect("wasip2 is not configured");
1270                            WasiKeyValue::new(h.wasi_keyvalue.as_mut().unwrap(), ctx.ctx().table)
1271                        })?;
1272                        store.data_mut().wasi_keyvalue = Some(ctx);
1273                    }
1274                }
1275            }
1276        }
1277
1278        if self.run.common.wasi.threads == Some(true) {
1279            bail!("support for wasi-threads has been removed from Wasmtime");
1280        }
1281
1282        if self.run.common.wasi.http == Some(true) {
1283            #[cfg(not(all(feature = "wasi-http", feature = "component-model")))]
1284            {
1285                bail!("Cannot enable wasi-http when the binary is not compiled with this feature.");
1286            }
1287            #[cfg(all(feature = "wasi-http", feature = "component-model"))]
1288            {
1289                match linker {
1290                    CliLinker::Core(_) => {
1291                        bail!("Cannot enable wasi-http for core wasm modules");
1292                    }
1293                    CliLinker::Component(linker) => {
1294                        wasmtime_wasi_http::p2::add_only_http_to_linker_async(linker)?;
1295                        #[cfg(feature = "component-model-async")]
1296                        if self.run.common.wasi.p3.unwrap_or(crate::common::P3_DEFAULT) {
1297                            wasmtime_wasi_http::p3::add_to_linker(linker)?;
1298                        }
1299                    }
1300                }
1301                let http = self.run.wasi_http_ctx()?;
1302                store.data_mut().wasi_http = Some(http);
1303            }
1304        }
1305
1306        if self.run.common.wasi.tls == Some(true) {
1307            #[cfg(all(not(all(feature = "wasi-tls", feature = "component-model"))))]
1308            {
1309                bail!("Cannot enable wasi-tls when the binary is not compiled with this feature.");
1310            }
1311            #[cfg(all(feature = "wasi-tls", feature = "component-model",))]
1312            {
1313                match linker {
1314                    CliLinker::Core(_) => {
1315                        bail!("Cannot enable wasi-tls for core wasm modules");
1316                    }
1317                    CliLinker::Component(linker) => {
1318                        let mut opts = wasmtime_wasi_tls::p2::LinkOptions::default();
1319                        opts.tls(true);
1320                        wasmtime_wasi_tls::p2::add_to_linker(linker, &opts)?;
1321
1322                        #[cfg(feature = "component-model-async")]
1323                        if self.run.common.wasi.p3.unwrap_or(crate::common::P3_DEFAULT) {
1324                            wasmtime_wasi_tls::p3::add_to_linker(linker)?;
1325                        }
1326
1327                        let ctx = wasmtime_wasi_tls::WasiTlsCtxBuilder::new().build();
1328                        store.data_mut().wasi_tls = Some(ctx);
1329                    }
1330                }
1331            }
1332        }
1333
1334        Ok(())
1335    }
1336
1337    /// Configure `wasmtime_wasi::WasiCtx` and store it in `Host`.
1338    fn set_wasi_ctx(&self, store: &mut Store<Host>) -> Result<()> {
1339        let mut builder = wasmtime_wasi::WasiCtxBuilder::new();
1340        builder.args(&self.compute_argv()?);
1341        if self.run.common.wasi.inherit_stdin.unwrap_or(true) {
1342            builder.inherit_stdin();
1343        }
1344        if self.run.common.wasi.inherit_stdout.unwrap_or(true) {
1345            builder.inherit_stdout();
1346        }
1347        if self.run.common.wasi.inherit_stderr.unwrap_or(true) {
1348            builder.inherit_stderr();
1349        }
1350        self.run.configure_wasip2(&mut builder)?;
1351        let mut ctx = builder.build_p1();
1352        if let Some(max) = self.run.common.wasi.max_resources {
1353            ctx.ctx().table.set_max_capacity(max);
1354            #[cfg(feature = "component-model-async")]
1355            if let Some(table) = store.concurrent_resource_table() {
1356                table.set_max_capacity(max);
1357            }
1358        }
1359        if let Some(fuel) = self.run.common.wasi.hostcall_fuel {
1360            store.set_hostcall_fuel(fuel);
1361        }
1362        store.data_mut().wasip1_ctx = Some(ctx);
1363        Ok(())
1364    }
1365
1366    #[cfg(feature = "wasi-nn")]
1367    fn collect_preloaded_nn_graphs(
1368        &self,
1369    ) -> Result<(Vec<wasmtime_wasi_nn::Backend>, wasmtime_wasi_nn::Registry)> {
1370        let graphs = self
1371            .run
1372            .common
1373            .wasi
1374            .nn_graph
1375            .iter()
1376            .map(|g| (g.format.clone(), g.dir.clone()))
1377            .collect::<Vec<_>>();
1378        wasmtime_wasi_nn::preload(&graphs)
1379    }
1380}
1381
1382/// The `T` in `Store<T>` for what the CLI is running.
1383///
1384/// This structures has a number of contexts used for various WASI proposals.
1385/// Note that all of them are optional meaning that they're `None` by default
1386/// and enabled with various CLI flags (some CLI flags are on-by-default).
1387#[derive(Default)]
1388pub struct Host {
1389    limits: StoreLimits,
1390    #[cfg(feature = "profiling")]
1391    guest_profiler: Option<wasmtime::GuestProfiler>,
1392
1393    // Context for both WASIp1 and WASIp2 (and beyond) for the `wasmtime_wasi`
1394    // crate. This has both `wasmtime_wasi::WasiCtx` as well as a
1395    // `ResourceTable` internally to be used.
1396    wasip1_ctx: Option<wasmtime_wasi::p1::WasiP1Ctx>,
1397
1398    #[cfg(feature = "wasi-nn")]
1399    wasi_nn_wit: Option<wasmtime_wasi_nn::wit::WasiNnCtx>,
1400    #[cfg(feature = "wasi-nn")]
1401    wasi_nn_witx: Option<wasmtime_wasi_nn::witx::WasiNnCtx>,
1402
1403    #[cfg(feature = "wasi-http")]
1404    wasi_http: Option<WasiHttpCtx>,
1405    #[cfg(feature = "wasi-http")]
1406    wasi_http_hooks: crate::common::HttpHooks,
1407
1408    #[cfg(feature = "wasi-config")]
1409    wasi_config: Option<WasiConfigVariables>,
1410    #[cfg(feature = "wasi-keyvalue")]
1411    wasi_keyvalue: Option<WasiKeyValueCtx>,
1412    #[cfg(feature = "wasi-tls")]
1413    wasi_tls: Option<wasmtime_wasi_tls::WasiTlsCtx>,
1414}
1415
1416impl Host {
1417    pub(crate) fn wasip1_ctx(&mut self) -> &mut wasmtime_wasi::p1::WasiP1Ctx {
1418        self.wasip1_ctx.as_mut().unwrap()
1419    }
1420}
1421
1422impl WasiView for Host {
1423    fn ctx(&mut self) -> WasiCtxView<'_> {
1424        WasiView::ctx(self.wasip1_ctx())
1425    }
1426}
1427
1428#[cfg(feature = "wasi-http")]
1429impl wasmtime_wasi_http::p2::WasiHttpView for Host {
1430    fn http(&mut self) -> wasmtime_wasi_http::p2::WasiHttpCtxView<'_> {
1431        let ctx = self.wasi_http.as_mut().unwrap();
1432        wasmtime_wasi_http::p2::WasiHttpCtxView {
1433            table: WasiView::ctx(self.wasip1_ctx.as_mut().unwrap()).table,
1434            ctx,
1435            hooks: &mut self.wasi_http_hooks,
1436        }
1437    }
1438}
1439
1440#[cfg(all(feature = "wasi-http", feature = "component-model-async"))]
1441impl wasmtime_wasi_http::p3::WasiHttpView for Host {
1442    fn http(&mut self) -> wasmtime_wasi_http::p3::WasiHttpCtxView<'_> {
1443        let ctx = self.wasi_http.as_mut().unwrap();
1444        wasmtime_wasi_http::p3::WasiHttpCtxView {
1445            table: WasiView::ctx(self.wasip1_ctx.as_mut().unwrap()).table,
1446            ctx,
1447            hooks: &mut self.wasi_http_hooks,
1448        }
1449    }
1450}
1451
1452#[cfg(all(feature = "wasi-tls"))]
1453impl wasmtime_wasi_tls::WasiTlsView for Host {
1454    fn tls(&mut self) -> wasmtime_wasi_tls::WasiTlsCtxView<'_> {
1455        wasmtime_wasi_tls::WasiTlsCtxView {
1456            table: WasiView::ctx(self.wasip1_ctx.as_mut().unwrap()).table,
1457            ctx: self.wasi_tls.as_mut().unwrap(),
1458        }
1459    }
1460}
1461
1462#[cfg(feature = "coredump")]
1463fn write_core_dump(
1464    store: &mut Store<Host>,
1465    err: &wasmtime::Error,
1466    name: &str,
1467    path: &str,
1468) -> Result<()> {
1469    use std::fs::File;
1470    use std::io::Write;
1471
1472    let core_dump = err
1473        .downcast_ref::<wasmtime::WasmCoreDump>()
1474        .expect("should have been configured to capture core dumps");
1475
1476    let core_dump = core_dump.serialize(store, name);
1477
1478    let mut core_dump_file =
1479        File::create(path).with_context(|| format!("failed to create file at `{path}`"))?;
1480    core_dump_file
1481        .write_all(&core_dump)
1482        .with_context(|| format!("failed to write core dump file at `{path}`"))?;
1483    Ok(())
1484}