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