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        self.run.configure_store(&mut store, |t| &mut t.limits)?;
393
394        Ok((store, linker))
395    }
396
397    #[cfg(feature = "debug")]
398    pub(crate) fn add_debugger_api(
399        &mut self,
400        linker: &mut wasmtime::component::Linker<Host>,
401    ) -> Result<()> {
402        wasmtime_debugger::add_to_linker(linker, |x| x.ctx().table)?;
403        Ok(())
404    }
405
406    /// Executes the `main` after instantiating it within `store`.
407    ///
408    /// This applies all configuration within `self`, such as timeouts and
409    /// profiling, and performs the execution. The resulting instance is
410    /// returned.
411    pub async fn instantiate_and_run(
412        &self,
413        engine: &Engine,
414        linker: &mut CliLinker,
415        main: &RunTarget,
416        store: &mut Store<Host>,
417    ) -> Result<CliInstance> {
418        let dur = self
419            .run
420            .common
421            .wasm
422            .timeout
423            .unwrap_or(std::time::Duration::MAX);
424        let result = tokio::time::timeout(dur, async {
425            let mut profiled_modules: Vec<(String, Module)> = Vec::new();
426            if let RunTarget::Core(m) = &main {
427                profiled_modules.push(("".to_string(), m.clone()));
428            }
429
430            // Load the preload wasm modules.
431            for (name, path) in self.preloads.modules.iter() {
432                // Read the wasm module binary either as `*.wat` or a raw binary
433                let preload_target = self.run.load_module(&engine, path, None)?;
434                let preload_module = match preload_target {
435                    RunTarget::Core(m) => m,
436                    #[cfg(feature = "component-model")]
437                    RunTarget::Component(_) => {
438                        bail!("components cannot be loaded with `--preload`")
439                    }
440                };
441                profiled_modules.push((name.to_string(), preload_module.clone()));
442
443                // Add the module's functions to the linker.
444                match linker {
445                    #[cfg(feature = "cranelift")]
446                    CliLinker::Core(linker) => {
447                        linker
448                            .module_async(&mut *store, name, &preload_module)
449                            .await
450                            .with_context(|| {
451                                format!(
452                                    "failed to process preload `{}` at `{}`",
453                                    name,
454                                    path.display()
455                                )
456                            })?;
457                    }
458                    #[cfg(not(feature = "cranelift"))]
459                    CliLinker::Core(_) => {
460                        bail!("support for --preload disabled at compile time");
461                    }
462                    #[cfg(feature = "component-model")]
463                    CliLinker::Component(_) => {
464                        bail!("--preload cannot be used with components");
465                    }
466                }
467            }
468
469            self.load_main_module(store, linker, &main, profiled_modules)
470                .await
471                .with_context(|| {
472                    format!(
473                        "failed to run main module `{}`",
474                        self.module_and_args[0].to_string_lossy()
475                    )
476                })
477        })
478        .await;
479
480        // Load the main wasm module.
481        let instance = match result.unwrap_or_else(|elapsed| {
482            Err(wasmtime::Error::from(wasmtime::Trap::Interrupt))
483                .with_context(|| format!("timed out after {elapsed}"))
484        }) {
485            Ok(instance) => instance,
486            Err(e) => {
487                // Exit the process if Wasmtime understands the error;
488                // otherwise, fall back on Rust's default error printing/return
489                // code.
490                if store.data().wasip1_ctx.is_some() {
491                    if let Some(exit) = e.downcast_ref::<wasmtime_wasi::I32Exit>() {
492                        std::process::exit(exit.0);
493                    }
494                }
495                if e.is::<wasmtime::Trap>() {
496                    eprintln!("Error: {e:?}");
497                    cfg_select! {
498                        unix => {
499                            std::process::exit(rustix::process::EXIT_SIGNALED_SIGABRT);
500                        }
501                        windows => {
502                            // https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/abort?view=vs-2019
503                            std::process::exit(3);
504                        }
505                    }
506                }
507                return Err(e);
508            }
509        };
510
511        Ok(instance)
512    }
513
514    pub(crate) fn compute_argv(&self) -> Result<Vec<String>> {
515        let mut result = Vec::new();
516
517        for (i, arg) in self.module_and_args.iter().enumerate() {
518            // For argv[0], which is the program name. Only include the base
519            // name of the main wasm module, to avoid leaking path information.
520            let arg = if i == 0 {
521                match &self.argv0 {
522                    Some(s) => s.as_ref(),
523                    None => Path::new(arg).components().next_back().unwrap().as_os_str(),
524                }
525            } else {
526                arg.as_ref()
527            };
528            result.push(
529                arg.to_str()
530                    .ok_or_else(|| format_err!("failed to convert {arg:?} to utf-8"))?
531                    .to_string(),
532            );
533        }
534
535        Ok(result)
536    }
537
538    fn setup_epoch_handler(
539        &self,
540        store: &mut Store<Host>,
541        main_target: &RunTarget,
542        profiled_modules: Vec<(String, Module)>,
543    ) -> Result<Box<dyn FnOnce(&mut Store<Host>) + Send>> {
544        // If a debugger component is attached, we set up epoch
545        // interruptions in `debugger_run()` above when enabling guest
546        // instrumentation; we need to ensure that epoch interruptions
547        // cause a debug event but no trap here. This overrides other
548        // behavior below.
549        if self.run.common.debug.debugger.is_some() {
550            if self.run.profile.is_some() {
551                bail!("Cannot set profile options together with debugging; they are incompatible");
552            }
553            if self.run.common.wasm.timeout.is_some() {
554                bail!("Cannot set timeout options together with debugging; they are incompatible");
555            }
556            store.epoch_deadline_async_yield_and_update(1);
557        } else {
558            if let Some(Profile::Guest { path, interval }) = &self.run.profile {
559                #[cfg(feature = "profiling")]
560                return Ok(self.setup_guest_profiler(
561                    store,
562                    main_target,
563                    profiled_modules,
564                    path,
565                    *interval,
566                )?);
567                #[cfg(not(feature = "profiling"))]
568                {
569                    let _ = (profiled_modules, path, interval, main_target);
570                    bail!("support for profiling disabled at compile time");
571                }
572            }
573
574            if let Some(timeout) = self.run.common.wasm.timeout {
575                store.set_epoch_deadline(1);
576                let engine = store.engine().clone();
577                thread::spawn(move || {
578                    thread::sleep(timeout);
579                    engine.increment_epoch();
580                });
581            }
582        }
583
584        Ok(Box::new(|_store| {}))
585    }
586
587    #[cfg(feature = "profiling")]
588    fn setup_guest_profiler(
589        &self,
590        store: &mut Store<Host>,
591        main_target: &RunTarget,
592        profiled_modules: Vec<(String, Module)>,
593        path: &str,
594        interval: std::time::Duration,
595    ) -> Result<Box<dyn FnOnce(&mut Store<Host>) + Send>> {
596        use wasmtime::{AsContext, GuestProfiler, StoreContext, StoreContextMut, UpdateDeadline};
597
598        let module_name = self.module_and_args[0].to_str().unwrap_or("<main module>");
599        store.data_mut().guest_profiler = match main_target {
600            RunTarget::Core(_m) => Some(GuestProfiler::new(
601                store.engine(),
602                module_name,
603                interval,
604                profiled_modules,
605            )?),
606            RunTarget::Component(component) => Some(GuestProfiler::new_component(
607                store.engine(),
608                module_name,
609                interval,
610                component.clone(),
611                profiled_modules,
612            )?),
613        };
614
615        fn sample(
616            mut store: StoreContextMut<Host>,
617            f: impl FnOnce(&mut GuestProfiler, StoreContext<Host>),
618        ) {
619            let mut profiler = store.data_mut().guest_profiler.take().unwrap();
620            f(&mut profiler, store.as_context());
621            store.data_mut().guest_profiler = Some(profiler);
622        }
623
624        store.call_hook(|store, kind| {
625            sample(store, |profiler, store| profiler.call_hook(store, kind));
626            Ok(())
627        });
628
629        if let Some(timeout) = self.run.common.wasm.timeout {
630            let mut timeout = (timeout.as_secs_f64() / interval.as_secs_f64()).ceil() as u64;
631            assert!(timeout > 0);
632            store.epoch_deadline_callback(move |store| {
633                sample(store, |profiler, store| {
634                    profiler.sample(store, std::time::Duration::ZERO)
635                });
636                timeout -= 1;
637                if timeout == 0 {
638                    bail!("timeout exceeded");
639                }
640                Ok(UpdateDeadline::Continue(1))
641            });
642        } else {
643            store.epoch_deadline_callback(move |store| {
644                sample(store, |profiler, store| {
645                    profiler.sample(store, std::time::Duration::ZERO)
646                });
647                Ok(UpdateDeadline::Continue(1))
648            });
649        }
650
651        store.set_epoch_deadline(1);
652        let engine = store.engine().clone();
653        thread::spawn(move || {
654            loop {
655                thread::sleep(interval);
656                engine.increment_epoch();
657            }
658        });
659
660        let path = path.to_string();
661        Ok(Box::new(move |store| {
662            let profiler = store.data_mut().guest_profiler.take().unwrap();
663            if let Err(e) = std::fs::File::create(&path)
664                .map_err(wasmtime::Error::new)
665                .and_then(|output| profiler.finish(std::io::BufWriter::new(output)))
666            {
667                eprintln!("failed writing profile at {path}: {e:#}");
668            } else {
669                eprintln!();
670                eprintln!("Profile written to: {path}");
671                eprintln!("View this profile at https://profiler.firefox.com/.");
672            }
673        }))
674    }
675
676    async fn load_main_module(
677        &self,
678        store: &mut Store<Host>,
679        linker: &mut CliLinker,
680        main_target: &RunTarget,
681        profiled_modules: Vec<(String, Module)>,
682    ) -> Result<CliInstance> {
683        // The main module might be allowed to have unknown imports, which
684        // should be defined as traps:
685        if self.run.common.wasm.unknown_imports_trap == Some(true) {
686            match linker {
687                CliLinker::Core(linker) => {
688                    linker.define_unknown_imports_as_traps(main_target.unwrap_core())?;
689                }
690                #[cfg(feature = "component-model")]
691                CliLinker::Component(linker) => {
692                    linker.define_unknown_imports_as_traps(main_target.unwrap_component())?;
693                }
694            }
695        }
696
697        // ...or as default values.
698        if self.run.common.wasm.unknown_imports_default == Some(true) {
699            match linker {
700                CliLinker::Core(linker) => {
701                    linker.define_unknown_imports_as_default_values(
702                        &mut *store,
703                        main_target.unwrap_core(),
704                    )?;
705                }
706                _ => bail!("cannot use `--default-values-unknown-imports` with components"),
707            }
708        }
709
710        let finish_epoch_handler =
711            self.setup_epoch_handler(store, main_target, profiled_modules)?;
712
713        let result = match linker {
714            CliLinker::Core(linker) => {
715                let module = main_target.unwrap_core();
716                let instance = linker
717                    .instantiate_async(&mut *store, &module)
718                    .await
719                    .with_context(|| {
720                        format!("failed to instantiate {:?}", self.module_and_args[0])
721                    })?;
722
723                // If `_initialize` is present, meaning a reactor, then invoke
724                // the function.
725                if let Some(func) = instance.get_func(&mut *store, "_initialize") {
726                    func.typed::<(), ()>(&store)?
727                        .call_async(&mut *store, ())
728                        .await?;
729                }
730
731                // Look for the specific function provided or otherwise look for
732                // "" or "_start" exports to run as a "main" function.
733                let func = if let Some(name) = &self.invoke {
734                    Some(
735                        instance
736                            .get_func(&mut *store, name)
737                            .ok_or_else(|| format_err!("no func export named `{name}` found"))?,
738                    )
739                } else {
740                    instance
741                        .get_func(&mut *store, "")
742                        .or_else(|| instance.get_func(&mut *store, "_start"))
743                };
744
745                if let Some(func) = func {
746                    self.invoke_func(store, func).await?;
747                }
748                Ok(CliInstance::Core(instance))
749            }
750            #[cfg(feature = "component-model")]
751            CliLinker::Component(linker) => {
752                let component = main_target.unwrap_component();
753                let result = if self.invoke.is_some() {
754                    self.invoke_component(&mut *store, component, linker).await
755                } else {
756                    self.run_command_component(&mut *store, component, linker)
757                        .await
758                };
759                result
760                    .map(CliInstance::Component)
761                    .map_err(|e| self.handle_core_dump(&mut *store, e))
762            }
763        };
764        finish_epoch_handler(store);
765
766        result
767    }
768
769    #[cfg(feature = "component-model")]
770    async fn invoke_component(
771        &self,
772        store: &mut Store<Host>,
773        component: &wasmtime::component::Component,
774        linker: &mut wasmtime::component::Linker<Host>,
775    ) -> Result<wasmtime::component::Instance> {
776        use wasmtime::component::{
777            Val,
778            wasm_wave::{
779                untyped::UntypedFuncCall,
780                wasm::{DisplayFuncResults, WasmFunc},
781            },
782        };
783
784        // Check if the invoke string is present
785        let invoke: &String = self.invoke.as_ref().unwrap();
786
787        let untyped_call = UntypedFuncCall::parse(invoke).with_context(|| {
788                format!(
789                    "Failed to parse invoke '{invoke}': See https://docs.wasmtime.dev/cli-options.html#run for syntax",
790                )
791        })?;
792
793        let name = untyped_call.item_name().map_err(|e| {
794            wasmtime::Error::from_anyhow(e).context(format!(
795                "parsing `{}` as a wit item name",
796                untyped_call.name()
797            ))
798        })?;
799
800        let (export, func_type) = Self::search_component_funcs(store, &component, &name)?;
801
802        let param_types = WasmFunc::params(&func_type).collect::<Vec<_>>();
803        let params = untyped_call
804            .to_wasm_params(&param_types)
805            .with_context(|| format!("while interpreting parameters in invoke \"{invoke}\""))?;
806
807        let instance = linker.instantiate_async(&mut *store, component).await?;
808
809        let func = instance
810            .get_func(&mut *store, export)
811            .expect("found export index");
812
813        let mut results = vec![Val::Bool(false); func_type.results().len()];
814        self.call_component_func(store, &params, func, &mut results)
815            .await?;
816
817        println!("{}", DisplayFuncResults(&results));
818        Ok(instance)
819    }
820
821    #[cfg(feature = "component-model")]
822    async fn call_component_func(
823        &self,
824        store: &mut Store<Host>,
825        params: &[wasmtime::component::Val],
826        func: wasmtime::component::Func,
827        results: &mut Vec<wasmtime::component::Val>,
828    ) -> Result<(), Error> {
829        #[cfg(feature = "component-model-async")]
830        if self.run.common.wasm.concurrency_support.unwrap_or(true) {
831            store
832                .run_concurrent(async |store| func.call_concurrent(store, params, results).await)
833                .await??;
834            return Ok(());
835        }
836
837        func.call_async(&mut *store, &params, results).await?;
838        Ok(())
839    }
840
841    /// Execute the default behavior for components on the CLI, looking for
842    /// `wasi:cli`-based commands and running their exported `run` function.
843    #[cfg(feature = "component-model")]
844    async fn run_command_component(
845        &self,
846        store: &mut Store<Host>,
847        component: &wasmtime::component::Component,
848        linker: &wasmtime::component::Linker<Host>,
849    ) -> Result<wasmtime::component::Instance> {
850        let instance = linker.instantiate_async(&mut *store, component).await?;
851
852        let mut result = None;
853        let _ = &mut result;
854
855        // If WASIp3 is enabled at compile time, enabled at runtime, and found
856        // in this component then use that to generate the result.
857        #[cfg(feature = "component-model-async")]
858        if self.run.common.wasi.p3.unwrap_or(crate::common::P3_DEFAULT) {
859            if let Ok(command) = wasmtime_wasi::p3::bindings::Command::new(&mut *store, &instance) {
860                result = Some(
861                    store
862                        .run_concurrent(async |store| command.wasi_cli_run().call_run(store).await)
863                        .await?,
864                );
865            }
866        }
867
868        let result = match result {
869            Some(result) => result,
870            // If WASIp3 wasn't found then fall back to requiring WASIp2 and
871            // this'll report an error if the right export doesn't exist.
872            None => {
873                wasmtime_wasi::p2::bindings::Command::new(&mut *store, &instance)?
874                    .wasi_cli_run()
875                    .call_run(&mut *store)
876                    .await
877            }
878        };
879        let wasm_result = result.context("failed to invoke `run` function")?;
880
881        // Translate the `Result<(),()>` produced by wasm into a feigned
882        // explicit exit here with status 1 if `Err(())` is returned.
883        match wasm_result {
884            Ok(()) => Ok(instance),
885            Err(()) => Err(wasmtime_wasi::I32Exit(1).into()),
886        }
887    }
888
889    /// Invoke a debugger component with a debuggee.
890    ///
891    /// The debugger runs in `store` (using run's `Host`), while the
892    /// debuggee wraps an arbitrary store type `T` and body closure.
893    #[cfg(feature = "debug")]
894    pub(crate) async fn invoke_debugger<
895        T: Send + 'static,
896        F: FnOnce(&mut Store<T>) -> Pin<Box<dyn Future<Output = Result<()>> + Send + '_>>
897            + Send
898            + 'static,
899    >(
900        &self,
901        store: &mut Store<Host>,
902        component: &wasmtime::component::Component,
903        linker: &mut wasmtime::component::Linker<Host>,
904        debuggee_host: Store<T>,
905        body: F,
906    ) -> Result<()> {
907        let instance = linker.instantiate_async(&mut *store, component).await?;
908        let command = wasmtime_debugger::DebuggerComponent::new(&mut *store, &instance)?;
909        let debuggee = wasmtime_debugger::Debuggee::new(debuggee_host, body);
910        let debuggee = wasmtime_debugger::add_debuggee(store.data_mut().ctx().table, debuggee)?;
911        {
912            // Manually construct a borrow -- wasmtime-wit-bindgen
913            // generates code that consumes the `Resource<T>` for
914            // `call_debug()` below even though the WIT type is a
915            // `borrow<debuggee>`.
916            let borrowed = wasmtime::component::Resource::new_borrow(debuggee.rep());
917            let args = self.compute_argv()?;
918            command
919                .bytecodealliance_wasmtime_debugger()
920                .call_debug(&mut *store, borrowed, &args)
921                .await?;
922        }
923        let mut debuggee = store.data_mut().ctx().table.delete(debuggee)?;
924        debuggee.finish().await?;
925        Ok(())
926    }
927
928    #[cfg(feature = "component-model")]
929    fn search_component_funcs(
930        store: &mut Store<Host>,
931        component: &wasmtime::component::Component,
932        item_name: &wasmtime::component::wit_parser::ItemName,
933    ) -> Result<(
934        wasmtime::component::ComponentExportIndex,
935        wasmtime::component::types::ComponentFunc,
936    )> {
937        use wasmtime::component::types::ComponentItem as CItem;
938        // Start by looking up the item name directly.
939        // Only match this as the search if it provides a function - it may
940        // provide an instance of the same name, in which case we want the
941        // below to search through it.
942        match component.get_export(None, item_name) {
943            Some((CItem::ComponentFunc(func), index)) => return Ok((index, func.clone())),
944            _ => {}
945        }
946        if item_name.interface.is_some() || item_name.package.is_some() {
947            // If the item name specified a package or interface, and the
948            // ItemName based lookup failed to find it, we do not consider
949            // that it may be exported under an instance and terminate the
950            // search immediately:
951            bail!("No exported func named `{item_name}` in component.")
952        }
953        // If the item name does not specify a package or interface, and it
954        // wasn't found in the root of the component above, then we search all
955        // instance exports for a function by that name.
956        let needle = item_name.to_string();
957        let mut search = component
958            .component_type()
959            .exports(store.engine())
960            .filter_map(|(instname, item)| match item.ty {
961                CItem::ComponentInstance(inst) => {
962                    inst.exports(store.engine())
963                        .find_map(|(leafname, item)| match item.ty {
964                            CItem::ComponentFunc(func) => {
965                                if leafname == needle {
966                                    // The type::Component traversal
967                                    // didn't give us an export index, get
968                                    // that now:
969                                    let (_item, inst_index) = component
970                                        .get_export(None, instname)
971                                        .expect("found exported component instance");
972                                    let (_item, index) = component
973                                        .get_export(Some(&inst_index), leafname)
974                                        .expect("found func");
975                                    Some((index, func.clone(), instname.to_string()))
976                                } else {
977                                    None
978                                }
979                            }
980                            _ => None,
981                        })
982                }
983
984                _ => None,
985            })
986            .collect::<Vec<_>>();
987
988        match search.len() {
989            0 => bail!("No exported func named `{needle}` in component."),
990            1 => {
991                let (index, func, _instname) = search.pop().unwrap();
992                Ok((index, func))
993            }
994            _ => {
995                let candidates = search
996                    .into_iter()
997                    .map(|(_index, _func, instname)| {
998                        // Manipulate as an ItemName to get package version
999                        // correct in the output
1000                        let mut itemname: wasmtime::component::wit_parser::ItemName =
1001                            instname.parse().unwrap();
1002                        // Push the function name onto the itemname:
1003                        itemname.interface = Some(itemname.name.clone());
1004                        itemname.name = needle.to_string();
1005                        format!("`{itemname}`")
1006                    })
1007                    .collect::<Vec<_>>();
1008                bail!(
1009                    "Multiple instances contained funcs named `{needle}`, retry with a more specific name: {}",
1010                    candidates.join(", ")
1011                )
1012            }
1013        }
1014    }
1015
1016    async fn invoke_func(&self, store: &mut Store<Host>, func: Func) -> Result<()> {
1017        let ty = func.ty(&store);
1018        if ty.params().len() > 0 {
1019            eprintln!(
1020                "warning: using `--invoke` with a function that takes arguments \
1021                 is experimental and may break in the future"
1022            );
1023        }
1024        let mut args = self.module_and_args.iter().skip(1);
1025        let mut values = Vec::new();
1026        for ty in ty.params() {
1027            let val = match args.next() {
1028                Some(s) => s,
1029                None => {
1030                    if let Some(name) = &self.invoke {
1031                        bail!("not enough arguments for `{name}`")
1032                    } else {
1033                        bail!("not enough arguments for command default")
1034                    }
1035                }
1036            };
1037            let val = val
1038                .to_str()
1039                .ok_or_else(|| format_err!("argument is not valid utf-8: {val:?}"))?;
1040            values.push(match ty {
1041                // Supports both decimal and hexadecimal notation (with 0x prefix)
1042                ValType::I32 => Val::I32(if val.starts_with("0x") || val.starts_with("0X") {
1043                    i32::from_str_radix(&val[2..], 16)?
1044                } else {
1045                    val.parse::<i32>()?
1046                }),
1047                ValType::I64 => Val::I64(if val.starts_with("0x") || val.starts_with("0X") {
1048                    i64::from_str_radix(&val[2..], 16)?
1049                } else {
1050                    val.parse::<i64>()?
1051                }),
1052                ValType::F32 => Val::F32(val.parse::<f32>()?.to_bits()),
1053                ValType::F64 => Val::F64(val.parse::<f64>()?.to_bits()),
1054                t => bail!("unsupported argument type {t:?}"),
1055            });
1056        }
1057
1058        // Invoke the function and then afterwards print all the results that came
1059        // out, if there are any.
1060        let mut results = vec![Val::null_func_ref(); ty.results().len()];
1061        let invoke_res = func
1062            .call_async(&mut *store, &values, &mut results)
1063            .await
1064            .with_context(|| {
1065                if let Some(name) = &self.invoke {
1066                    format!("failed to invoke `{name}`")
1067                } else {
1068                    format!("failed to invoke command default")
1069                }
1070            });
1071
1072        if let Err(err) = invoke_res {
1073            return Err(self.handle_core_dump(&mut *store, err));
1074        }
1075
1076        if !results.is_empty() {
1077            eprintln!(
1078                "warning: using `--invoke` with a function that returns values \
1079                 is experimental and may break in the future"
1080            );
1081        }
1082
1083        for result in results {
1084            match result {
1085                Val::I32(i) => println!("{i}"),
1086                Val::I64(i) => println!("{i}"),
1087                Val::F32(f) => println!("{}", f32::from_bits(f)),
1088                Val::F64(f) => println!("{}", f64::from_bits(f)),
1089                Val::V128(i) => println!("{}", i.as_u128()),
1090                Val::ExternRef(None) => println!("<null externref>"),
1091                Val::ExternRef(Some(_)) => println!("<externref>"),
1092                Val::FuncRef(None) => println!("<null funcref>"),
1093                Val::FuncRef(Some(_)) => println!("<funcref>"),
1094                Val::AnyRef(None) => println!("<null anyref>"),
1095                Val::AnyRef(Some(_)) => println!("<anyref>"),
1096                Val::ExnRef(None) => println!("<null exnref>"),
1097                Val::ExnRef(Some(_)) => println!("<exnref>"),
1098                Val::ContRef(None) => println!("<null contref>"),
1099                Val::ContRef(Some(_)) => println!("<contref>"),
1100            }
1101        }
1102
1103        Ok(())
1104    }
1105
1106    #[cfg(feature = "coredump")]
1107    fn handle_core_dump(&self, store: &mut Store<Host>, err: Error) -> Error {
1108        let coredump_path = match &self.run.common.debug.coredump {
1109            Some(path) => path,
1110            None => return err,
1111        };
1112        if !err.is::<wasmtime::Trap>() {
1113            return err;
1114        }
1115        let source_name = self.module_and_args[0]
1116            .to_str()
1117            .unwrap_or_else(|| "unknown");
1118
1119        if let Err(coredump_err) = write_core_dump(store, &err, &source_name, coredump_path) {
1120            eprintln!("warning: coredump failed to generate: {coredump_err}");
1121            err
1122        } else {
1123            err.context(format!("core dumped at {coredump_path}"))
1124        }
1125    }
1126
1127    #[cfg(not(feature = "coredump"))]
1128    fn handle_core_dump(&self, _store: &mut Store<Host>, err: Error) -> Error {
1129        err
1130    }
1131
1132    /// Populates the given `Linker` with WASI APIs.
1133    fn populate_with_wasi(&self, linker: &mut CliLinker, store: &mut Store<Host>) -> Result<()> {
1134        self.run.validate_p3_option()?;
1135        let cli = self.run.validate_cli_enabled()?;
1136
1137        if cli != Some(false) {
1138            match linker {
1139                CliLinker::Core(linker) => {
1140                    match (self.run.common.wasi.preview2, self.run.common.wasi.threads) {
1141                        (Some(false), _) | (None, Some(true)) => {
1142                            let flag = if self.run.common.wasi.preview2 == Some(false) {
1143                                "-Spreview2=n"
1144                            } else {
1145                                "-Sthreads"
1146                            };
1147                            bail!("the `{flag}` flag is no longer supported")
1148                        }
1149                        // If preview2 was explicitly requested, always use it.
1150                        // Otherwise use it so long as threads are disabled.
1151                        //
1152                        // Note that for now `p0` is currently
1153                        // default-enabled but this may turn into
1154                        // default-disabled in the future.
1155                        (Some(true), _) | (None, Some(false) | None) => {
1156                            if self.run.common.wasi.preview0 != Some(false) {
1157                                wasmtime_wasi::p0::add_to_linker_async(linker, |t| t.wasip1_ctx())?;
1158                            }
1159                            wasmtime_wasi::p1::add_to_linker_async(linker, |t| t.wasip1_ctx())?;
1160                            self.set_wasi_ctx(store)?;
1161                        }
1162                    }
1163                }
1164                #[cfg(feature = "component-model")]
1165                CliLinker::Component(linker) => {
1166                    self.run.add_wasmtime_wasi_to_linker(linker)?;
1167                    self.set_wasi_ctx(store)?;
1168                }
1169            }
1170        }
1171
1172        if self.run.common.wasi.nn == Some(true) {
1173            #[cfg(not(feature = "wasi-nn"))]
1174            {
1175                bail!("Cannot enable wasi-nn when the binary is not compiled with this feature.");
1176            }
1177            #[cfg(all(feature = "wasi-nn", feature = "component-model"))]
1178            {
1179                let (backends, registry) = self.collect_preloaded_nn_graphs()?;
1180                match linker {
1181                    CliLinker::Core(linker) => {
1182                        wasmtime_wasi_nn::witx::add_to_linker(linker, |host| {
1183                            host.wasi_nn_witx.as_mut().unwrap()
1184                        })?;
1185                        store.data_mut().wasi_nn_witx =
1186                            Some(wasmtime_wasi_nn::witx::WasiNnCtx::new(backends, registry));
1187                    }
1188                    #[cfg(feature = "component-model")]
1189                    CliLinker::Component(linker) => {
1190                        wasmtime_wasi_nn::wit::add_to_linker(linker, |h: &mut Host| {
1191                            let ctx = h.wasip1_ctx.as_mut().expect("wasi is not configured");
1192                            let nn_ctx = h.wasi_nn_wit.as_mut().unwrap();
1193                            WasiNnView::new(ctx.ctx().table, nn_ctx)
1194                        })?;
1195                        store.data_mut().wasi_nn_wit =
1196                            Some(wasmtime_wasi_nn::wit::WasiNnCtx::new(backends, registry));
1197                    }
1198                }
1199            }
1200        }
1201
1202        if self.run.common.wasi.config == Some(true) {
1203            #[cfg(not(feature = "wasi-config"))]
1204            {
1205                bail!(
1206                    "Cannot enable wasi-config when the binary is not compiled with this feature."
1207                );
1208            }
1209            #[cfg(all(feature = "wasi-config", feature = "component-model"))]
1210            {
1211                match linker {
1212                    CliLinker::Core(_) => {
1213                        bail!("Cannot enable wasi-config for core wasm modules");
1214                    }
1215                    CliLinker::Component(linker) => {
1216                        let vars = WasiConfigVariables::from_iter(
1217                            self.run
1218                                .common
1219                                .wasi
1220                                .config_var
1221                                .iter()
1222                                .map(|v| (v.key.clone(), v.value.clone())),
1223                        );
1224
1225                        wasmtime_wasi_config::add_to_linker(linker, |h| {
1226                            WasiConfig::new(h.wasi_config.as_mut().unwrap())
1227                        })?;
1228                        store.data_mut().wasi_config = Some(vars);
1229                    }
1230                }
1231            }
1232        }
1233
1234        if self.run.common.wasi.keyvalue == Some(true) {
1235            #[cfg(not(feature = "wasi-keyvalue"))]
1236            {
1237                bail!(
1238                    "Cannot enable wasi-keyvalue when the binary is not compiled with this feature."
1239                );
1240            }
1241            #[cfg(all(feature = "wasi-keyvalue", feature = "component-model"))]
1242            {
1243                match linker {
1244                    CliLinker::Core(_) => {
1245                        bail!("Cannot enable wasi-keyvalue for core wasm modules");
1246                    }
1247                    CliLinker::Component(linker) => {
1248                        let ctx = WasiKeyValueCtxBuilder::new()
1249                            .in_memory_data(
1250                                self.run
1251                                    .common
1252                                    .wasi
1253                                    .keyvalue_in_memory_data
1254                                    .iter()
1255                                    .map(|v| (v.key.clone(), v.value.clone())),
1256                            )
1257                            .build();
1258
1259                        wasmtime_wasi_keyvalue::add_to_linker(linker, |h| {
1260                            let ctx = h.wasip1_ctx.as_mut().expect("wasip2 is not configured");
1261                            WasiKeyValue::new(h.wasi_keyvalue.as_mut().unwrap(), ctx.ctx().table)
1262                        })?;
1263                        store.data_mut().wasi_keyvalue = Some(ctx);
1264                    }
1265                }
1266            }
1267        }
1268
1269        if self.run.common.wasi.threads == Some(true) {
1270            bail!("support for wasi-threads has been removed from Wasmtime");
1271        }
1272
1273        if self.run.common.wasi.http == Some(true) {
1274            #[cfg(not(all(feature = "wasi-http", feature = "component-model")))]
1275            {
1276                bail!("Cannot enable wasi-http when the binary is not compiled with this feature.");
1277            }
1278            #[cfg(all(feature = "wasi-http", feature = "component-model"))]
1279            {
1280                match linker {
1281                    CliLinker::Core(_) => {
1282                        bail!("Cannot enable wasi-http for core wasm modules");
1283                    }
1284                    CliLinker::Component(linker) => {
1285                        wasmtime_wasi_http::p2::add_only_http_to_linker_async(linker)?;
1286                        #[cfg(feature = "component-model-async")]
1287                        if self.run.common.wasi.p3.unwrap_or(crate::common::P3_DEFAULT) {
1288                            wasmtime_wasi_http::p3::add_to_linker(linker)?;
1289                        }
1290                    }
1291                }
1292                let http = self.run.wasi_http_ctx()?;
1293                store.data_mut().wasi_http = Some(http);
1294            }
1295        }
1296
1297        if self.run.common.wasi.tls == Some(true) {
1298            #[cfg(all(not(all(feature = "wasi-tls", feature = "component-model"))))]
1299            {
1300                bail!("Cannot enable wasi-tls when the binary is not compiled with this feature.");
1301            }
1302            #[cfg(all(feature = "wasi-tls", feature = "component-model",))]
1303            {
1304                match linker {
1305                    CliLinker::Core(_) => {
1306                        bail!("Cannot enable wasi-tls for core wasm modules");
1307                    }
1308                    CliLinker::Component(linker) => {
1309                        let mut opts = wasmtime_wasi_tls::p2::LinkOptions::default();
1310                        opts.tls(true);
1311                        wasmtime_wasi_tls::p2::add_to_linker(linker, &opts)?;
1312
1313                        #[cfg(feature = "component-model-async")]
1314                        if self.run.common.wasi.p3.unwrap_or(crate::common::P3_DEFAULT) {
1315                            wasmtime_wasi_tls::p3::add_to_linker(linker)?;
1316                        }
1317
1318                        let ctx = wasmtime_wasi_tls::WasiTlsCtxBuilder::new().build();
1319                        store.data_mut().wasi_tls = Some(ctx);
1320                    }
1321                }
1322            }
1323        }
1324
1325        Ok(())
1326    }
1327
1328    /// Configure `wasmtime_wasi::WasiCtx` and store it in `Host`.
1329    fn set_wasi_ctx(&self, store: &mut Store<Host>) -> Result<()> {
1330        let mut builder = wasmtime_wasi::WasiCtxBuilder::new();
1331        builder.args(&self.compute_argv()?);
1332        if self.run.common.wasi.inherit_stdin.unwrap_or(true) {
1333            builder.inherit_stdin();
1334        }
1335        if self.run.common.wasi.inherit_stdout.unwrap_or(true) {
1336            builder.inherit_stdout();
1337        }
1338        if self.run.common.wasi.inherit_stderr.unwrap_or(true) {
1339            builder.inherit_stderr();
1340        }
1341        self.run.configure_wasip2(&mut builder)?;
1342        store.data_mut().wasip1_ctx = Some(builder.build_p1());
1343        Ok(())
1344    }
1345
1346    #[cfg(feature = "wasi-nn")]
1347    fn collect_preloaded_nn_graphs(
1348        &self,
1349    ) -> Result<(Vec<wasmtime_wasi_nn::Backend>, wasmtime_wasi_nn::Registry)> {
1350        let graphs = self
1351            .run
1352            .common
1353            .wasi
1354            .nn_graph
1355            .iter()
1356            .map(|g| (g.format.clone(), g.dir.clone()))
1357            .collect::<Vec<_>>();
1358        wasmtime_wasi_nn::preload(&graphs)
1359    }
1360}
1361
1362/// The `T` in `Store<T>` for what the CLI is running.
1363///
1364/// This structures has a number of contexts used for various WASI proposals.
1365/// Note that all of them are optional meaning that they're `None` by default
1366/// and enabled with various CLI flags (some CLI flags are on-by-default).
1367#[derive(Default)]
1368pub struct Host {
1369    limits: StoreLimits,
1370    #[cfg(feature = "profiling")]
1371    guest_profiler: Option<wasmtime::GuestProfiler>,
1372
1373    // Context for both WASIp1 and WASIp2 (and beyond) for the `wasmtime_wasi`
1374    // crate. This has both `wasmtime_wasi::WasiCtx` as well as a
1375    // `ResourceTable` internally to be used.
1376    wasip1_ctx: Option<wasmtime_wasi::p1::WasiP1Ctx>,
1377
1378    #[cfg(feature = "wasi-nn")]
1379    wasi_nn_wit: Option<wasmtime_wasi_nn::wit::WasiNnCtx>,
1380    #[cfg(feature = "wasi-nn")]
1381    wasi_nn_witx: Option<wasmtime_wasi_nn::witx::WasiNnCtx>,
1382
1383    #[cfg(feature = "wasi-http")]
1384    wasi_http: Option<WasiHttpCtx>,
1385    #[cfg(feature = "wasi-http")]
1386    wasi_http_hooks: crate::common::HttpHooks,
1387
1388    #[cfg(feature = "wasi-config")]
1389    wasi_config: Option<WasiConfigVariables>,
1390    #[cfg(feature = "wasi-keyvalue")]
1391    wasi_keyvalue: Option<WasiKeyValueCtx>,
1392    #[cfg(feature = "wasi-tls")]
1393    wasi_tls: Option<wasmtime_wasi_tls::WasiTlsCtx>,
1394}
1395
1396impl Host {
1397    pub(crate) fn wasip1_ctx(&mut self) -> &mut wasmtime_wasi::p1::WasiP1Ctx {
1398        self.wasip1_ctx.as_mut().unwrap()
1399    }
1400}
1401
1402impl WasiView for Host {
1403    fn ctx(&mut self) -> WasiCtxView<'_> {
1404        WasiView::ctx(self.wasip1_ctx())
1405    }
1406}
1407
1408#[cfg(feature = "wasi-http")]
1409impl wasmtime_wasi_http::WasiHttpView for Host {
1410    fn http(&mut self) -> wasmtime_wasi_http::WasiHttpCtxView<'_> {
1411        let ctx = self.wasi_http.as_mut().unwrap();
1412        wasmtime_wasi_http::WasiHttpCtxView {
1413            table: WasiView::ctx(self.wasip1_ctx.as_mut().unwrap()).table,
1414            ctx,
1415            hooks: &mut self.wasi_http_hooks,
1416        }
1417    }
1418}
1419
1420#[cfg(all(feature = "wasi-tls"))]
1421impl wasmtime_wasi_tls::WasiTlsView for Host {
1422    fn tls(&mut self) -> wasmtime_wasi_tls::WasiTlsCtxView<'_> {
1423        wasmtime_wasi_tls::WasiTlsCtxView {
1424            table: WasiView::ctx(self.wasip1_ctx.as_mut().unwrap()).table,
1425            ctx: self.wasi_tls.as_mut().unwrap(),
1426        }
1427    }
1428}
1429
1430#[cfg(feature = "coredump")]
1431fn write_core_dump(
1432    store: &mut Store<Host>,
1433    err: &wasmtime::Error,
1434    name: &str,
1435    path: &str,
1436) -> Result<()> {
1437    use std::fs::File;
1438    use std::io::Write;
1439
1440    let core_dump = err
1441        .downcast_ref::<wasmtime::WasmCoreDump>()
1442        .expect("should have been configured to capture core dumps");
1443
1444    let core_dump = core_dump.serialize(store, name);
1445
1446    let mut core_dump_file =
1447        File::create(path).with_context(|| format!("failed to create file at `{path}`"))?;
1448    core_dump_file
1449        .write_all(&core_dump)
1450        .with_context(|| format!("failed to write core dump file at `{path}`"))?;
1451    Ok(())
1452}