1#![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#[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 #[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 #[arg(long)]
64 pub argv0: Option<String>,
65
66 #[arg(skip)]
72 pub module_bytes: Option<&'static [u8]>,
73
74 #[arg(value_name = "WASM", trailing_var_arg = true, required = true)]
80 pub module_and_args: Vec<OsString>,
81}
82
83impl RunCommand {
84 #[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 #[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 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 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 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 #[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#[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#[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 #[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 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 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 config.epoch_interruption(true);
338 }
339 None => {}
340 }
341
342 Engine::new(&config)
343 }
344
345 pub fn new_store_and_linker(
351 &mut self,
352 engine: &Engine,
353 main: &RunTarget,
354 ) -> Result<(Store<Host>, CliLinker)> {
355 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 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 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 for (name, path) in self.preloads.modules.iter() {
433 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 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 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 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 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 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 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 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 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 let Some(func) = instance.get_func(&mut *store, "_initialize") {
732 func.typed::<(), ()>(&store)?
733 .call_async(&mut *store, ())
734 .await?;
735 }
736
737 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 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(¶m_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, ¶ms, 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, ¶ms, results).await?;
853 Ok(())
854 }
855
856 #[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 #[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 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 match wasm_result {
899 Ok(()) => Ok(instance),
900 Err(()) => Err(wasmtime_wasi::I32Exit(1).into()),
901 }
902 }
903
904 #[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 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 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 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 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 (Some(false), _) | (None, Some(true)) => {
1124 let flag = if self.run.common.wasi.preview2 == Some(false) {
1125 "-Spreview2=n"
1126 } else {
1127 "-Sthreads"
1128 };
1129 eprintln!(
1130 "\
1131WARNING: the `{flag}` flag will be a hard error in Wasmtime 47.0.0 on 2026-07-20. \
1132For more information see https://github.com/bytecodealliance/rfcs/pull/47 and \
1133please reach out on Zulip with questions.
1134 "
1135 );
1136 wasi_common::tokio::add_to_linker(linker, |host| {
1137 host.legacy_p1_ctx.as_mut().unwrap()
1138 })?;
1139 self.set_legacy_p1_ctx(store)?;
1140 }
1141 (Some(true), _) | (None, Some(false) | None) => {
1148 if self.run.common.wasi.preview0 != Some(false) {
1149 wasmtime_wasi::p0::add_to_linker_async(linker, |t| t.wasip1_ctx())?;
1150 }
1151 wasmtime_wasi::p1::add_to_linker_async(linker, |t| t.wasip1_ctx())?;
1152 self.set_wasi_ctx(store)?;
1153 }
1154 }
1155 }
1156 #[cfg(feature = "component-model")]
1157 CliLinker::Component(linker) => {
1158 self.run.add_wasmtime_wasi_to_linker(linker)?;
1159 self.set_wasi_ctx(store)?;
1160 }
1161 }
1162 }
1163
1164 if self.run.common.wasi.nn == Some(true) {
1165 #[cfg(not(feature = "wasi-nn"))]
1166 {
1167 bail!("Cannot enable wasi-nn when the binary is not compiled with this feature.");
1168 }
1169 #[cfg(all(feature = "wasi-nn", feature = "component-model"))]
1170 {
1171 let (backends, registry) = self.collect_preloaded_nn_graphs()?;
1172 match linker {
1173 CliLinker::Core(linker) => {
1174 wasmtime_wasi_nn::witx::add_to_linker(linker, |host| {
1175 Arc::get_mut(host.wasi_nn_witx.as_mut().unwrap())
1176 .expect("wasi-nn is not implemented with multi-threading support")
1177 })?;
1178 store.data_mut().wasi_nn_witx = Some(Arc::new(
1179 wasmtime_wasi_nn::witx::WasiNnCtx::new(backends, registry),
1180 ));
1181 }
1182 #[cfg(feature = "component-model")]
1183 CliLinker::Component(linker) => {
1184 wasmtime_wasi_nn::wit::add_to_linker(linker, |h: &mut Host| {
1185 let ctx = h.wasip1_ctx.as_mut().expect("wasi is not configured");
1186 let ctx = Arc::get_mut(ctx)
1187 .expect("wasmtime_wasi is not compatible with threads")
1188 .get_mut()
1189 .unwrap();
1190 let nn_ctx = Arc::get_mut(h.wasi_nn_wit.as_mut().unwrap())
1191 .expect("wasi-nn is not implemented with multi-threading support");
1192 WasiNnView::new(ctx.ctx().table, nn_ctx)
1193 })?;
1194 store.data_mut().wasi_nn_wit = Some(Arc::new(
1195 wasmtime_wasi_nn::wit::WasiNnCtx::new(backends, registry),
1196 ));
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(Arc::get_mut(h.wasi_config.as_mut().unwrap()).unwrap())
1227 })?;
1228 store.data_mut().wasi_config = Some(Arc::new(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 let ctx = Arc::get_mut(ctx).unwrap().get_mut().unwrap();
1262 WasiKeyValue::new(
1263 Arc::get_mut(h.wasi_keyvalue.as_mut().unwrap()).unwrap(),
1264 ctx.ctx().table,
1265 )
1266 })?;
1267 store.data_mut().wasi_keyvalue = Some(Arc::new(ctx));
1268 }
1269 }
1270 }
1271 }
1272
1273 if self.run.common.wasi.threads == Some(true) {
1274 #[cfg(not(feature = "wasi-threads"))]
1275 {
1276 let _ = &module;
1279
1280 bail!(
1281 "Cannot enable wasi-threads when the binary is not compiled with this feature."
1282 );
1283 }
1284 #[cfg(feature = "wasi-threads")]
1285 {
1286 let linker = match linker {
1287 CliLinker::Core(linker) => linker,
1288 _ => bail!("wasi-threads does not support components yet"),
1289 };
1290 let module = module.unwrap_core();
1291 wasmtime_wasi_threads::add_to_linker(linker, store, &module, |host| {
1292 host.wasi_threads.as_ref().unwrap()
1293 })?;
1294 store.data_mut().wasi_threads = Some(Arc::new(WasiThreadsCtx::new(
1295 module.clone(),
1296 Arc::new(linker.clone()),
1297 true,
1298 )?));
1299 }
1300 }
1301
1302 if self.run.common.wasi.http == Some(true) {
1303 #[cfg(not(all(feature = "wasi-http", feature = "component-model")))]
1304 {
1305 bail!("Cannot enable wasi-http when the binary is not compiled with this feature.");
1306 }
1307 #[cfg(all(feature = "wasi-http", feature = "component-model"))]
1308 {
1309 match linker {
1310 CliLinker::Core(_) => {
1311 bail!("Cannot enable wasi-http for core wasm modules");
1312 }
1313 CliLinker::Component(linker) => {
1314 wasmtime_wasi_http::p2::add_only_http_to_linker_async(linker)?;
1315 #[cfg(feature = "component-model-async")]
1316 if self.run.common.wasi.p3.unwrap_or(crate::common::P3_DEFAULT) {
1317 wasmtime_wasi_http::p3::add_to_linker(linker)?;
1318 }
1319 }
1320 }
1321 let http = self.run.wasi_http_ctx()?;
1322 store.data_mut().wasi_http = Some(Arc::new(http));
1323 }
1324 }
1325
1326 if self.run.common.wasi.tls == Some(true) {
1327 #[cfg(all(not(all(feature = "wasi-tls", feature = "component-model"))))]
1328 {
1329 bail!("Cannot enable wasi-tls when the binary is not compiled with this feature.");
1330 }
1331 #[cfg(all(feature = "wasi-tls", feature = "component-model",))]
1332 {
1333 match linker {
1334 CliLinker::Core(_) => {
1335 bail!("Cannot enable wasi-tls for core wasm modules");
1336 }
1337 CliLinker::Component(linker) => {
1338 let mut opts = wasmtime_wasi_tls::p2::LinkOptions::default();
1339 opts.tls(true);
1340 wasmtime_wasi_tls::p2::add_to_linker(linker, &opts)?;
1341
1342 #[cfg(feature = "component-model-async")]
1343 if self.run.common.wasi.p3.unwrap_or(crate::common::P3_DEFAULT) {
1344 wasmtime_wasi_tls::p3::add_to_linker(linker)?;
1345 }
1346
1347 let ctx = wasmtime_wasi_tls::WasiTlsCtxBuilder::new().build();
1348 store.data_mut().wasi_tls = Some(Arc::new(ctx));
1349 }
1350 }
1351 }
1352 }
1353
1354 Ok(())
1355 }
1356
1357 fn set_legacy_p1_ctx(&self, store: &mut Store<Host>) -> Result<()> {
1358 let mut builder = WasiCtxBuilder::new();
1359 builder.args(&self.compute_argv()?)?;
1360
1361 if self.run.common.wasi.inherit_stdin.unwrap_or(true) {
1362 builder.inherit_stdin();
1363 }
1364 if self.run.common.wasi.inherit_stdout.unwrap_or(true) {
1365 builder.inherit_stdout();
1366 }
1367 if self.run.common.wasi.inherit_stderr.unwrap_or(true) {
1368 builder.inherit_stderr();
1369 }
1370
1371 if self.run.common.wasi.inherit_env == Some(true) {
1372 for (k, v) in std::env::vars() {
1373 builder.env(&k, &v)?;
1374 }
1375 }
1376 for (key, value) in self.run.vars.iter() {
1377 let value = match value {
1378 Some(value) => value.clone(),
1379 None => match std::env::var_os(key) {
1380 Some(val) => val
1381 .into_string()
1382 .map_err(|_| format_err!("environment variable `{key}` not valid utf-8"))?,
1383 None => {
1384 continue;
1386 }
1387 },
1388 };
1389 builder.env(key, &value)?;
1390 }
1391
1392 let mut num_fd: usize = 3;
1393
1394 if self.run.common.wasi.listenfd == Some(true) {
1395 num_fd = ctx_set_listenfd(num_fd, &mut builder)?;
1396 }
1397
1398 for listener in self.run.compute_preopen_sockets()? {
1399 let listener = TcpListener::from_std(listener);
1400 builder.preopened_socket(num_fd as _, listener)?;
1401 num_fd += 1;
1402 }
1403
1404 for (host, guest) in self.run.dirs.iter() {
1405 let dir = Dir::open_ambient_dir(host, ambient_authority())
1406 .with_context(|| format!("failed to open directory '{host}'"))?;
1407 builder.preopened_dir(dir, guest)?;
1408 }
1409
1410 store.data_mut().legacy_p1_ctx = Some(builder.build());
1411 Ok(())
1412 }
1413
1414 fn set_wasi_ctx(&self, store: &mut Store<Host>) -> Result<()> {
1422 let mut builder = wasmtime_wasi::WasiCtxBuilder::new();
1423 builder.args(&self.compute_argv()?);
1424 if self.run.common.wasi.inherit_stdin.unwrap_or(true) {
1425 builder.inherit_stdin();
1426 }
1427 if self.run.common.wasi.inherit_stdout.unwrap_or(true) {
1428 builder.inherit_stdout();
1429 }
1430 if self.run.common.wasi.inherit_stderr.unwrap_or(true) {
1431 builder.inherit_stderr();
1432 }
1433 self.run.configure_wasip2(&mut builder)?;
1434 let mut ctx = builder.build_p1();
1435 if let Some(max) = self.run.common.wasi.max_resources {
1436 ctx.ctx().table.set_max_capacity(max);
1437 #[cfg(feature = "component-model-async")]
1438 if let Some(table) = store.concurrent_resource_table() {
1439 table.set_max_capacity(max);
1440 }
1441 }
1442 if let Some(fuel) = self.run.common.wasi.hostcall_fuel {
1443 store.set_hostcall_fuel(fuel);
1444 }
1445 store.data_mut().wasip1_ctx = Some(Arc::new(Mutex::new(ctx)));
1446 Ok(())
1447 }
1448
1449 #[cfg(feature = "wasi-nn")]
1450 fn collect_preloaded_nn_graphs(
1451 &self,
1452 ) -> Result<(Vec<wasmtime_wasi_nn::Backend>, wasmtime_wasi_nn::Registry)> {
1453 let graphs = self
1454 .run
1455 .common
1456 .wasi
1457 .nn_graph
1458 .iter()
1459 .map(|g| (g.format.clone(), g.dir.clone()))
1460 .collect::<Vec<_>>();
1461 wasmtime_wasi_nn::preload(&graphs)
1462 }
1463}
1464
1465#[derive(Default, Clone)]
1481pub struct Host {
1482 limits: StoreLimits,
1483 #[cfg(feature = "profiling")]
1484 guest_profiler: Option<Arc<wasmtime::GuestProfiler>>,
1485
1486 legacy_p1_ctx: Option<wasi_common::WasiCtx>,
1489
1490 wasip1_ctx: Option<Arc<Mutex<wasmtime_wasi::p1::WasiP1Ctx>>>,
1498
1499 #[cfg(feature = "wasi-nn")]
1500 wasi_nn_wit: Option<Arc<wasmtime_wasi_nn::wit::WasiNnCtx>>,
1501 #[cfg(feature = "wasi-nn")]
1502 wasi_nn_witx: Option<Arc<wasmtime_wasi_nn::witx::WasiNnCtx>>,
1503
1504 #[cfg(feature = "wasi-threads")]
1505 wasi_threads: Option<Arc<WasiThreadsCtx<Host>>>,
1506 #[cfg(feature = "wasi-http")]
1507 wasi_http: Option<Arc<WasiHttpCtx>>,
1508 #[cfg(feature = "wasi-http")]
1509 wasi_http_hooks: crate::common::HttpHooks,
1510
1511 #[cfg(feature = "wasi-config")]
1512 wasi_config: Option<Arc<WasiConfigVariables>>,
1513 #[cfg(feature = "wasi-keyvalue")]
1514 wasi_keyvalue: Option<Arc<WasiKeyValueCtx>>,
1515 #[cfg(feature = "wasi-tls")]
1516 wasi_tls: Option<Arc<wasmtime_wasi_tls::WasiTlsCtx>>,
1517}
1518
1519impl Host {
1520 pub(crate) fn wasip1_ctx(&mut self) -> &mut wasmtime_wasi::p1::WasiP1Ctx {
1521 unwrap_singlethread_context(&mut self.wasip1_ctx)
1522 }
1523}
1524
1525fn unwrap_singlethread_context<T>(ctx: &mut Option<Arc<Mutex<T>>>) -> &mut T {
1526 let ctx = ctx.as_mut().expect("context not configured");
1527 Arc::get_mut(ctx)
1528 .expect("context is not compatible with threads")
1529 .get_mut()
1530 .unwrap()
1531}
1532
1533impl WasiView for Host {
1534 fn ctx(&mut self) -> WasiCtxView<'_> {
1535 WasiView::ctx(self.wasip1_ctx())
1536 }
1537}
1538
1539#[cfg(feature = "wasi-http")]
1540impl wasmtime_wasi_http::p2::WasiHttpView for Host {
1541 fn http(&mut self) -> wasmtime_wasi_http::p2::WasiHttpCtxView<'_> {
1542 let ctx = self.wasi_http.as_mut().unwrap();
1543 let ctx = Arc::get_mut(ctx).expect("wasmtime_wasi_http is not compatible with threads");
1544 wasmtime_wasi_http::p2::WasiHttpCtxView {
1545 table: WasiView::ctx(unwrap_singlethread_context(&mut self.wasip1_ctx)).table,
1546 ctx,
1547 hooks: &mut self.wasi_http_hooks,
1548 }
1549 }
1550}
1551
1552#[cfg(all(feature = "wasi-http", feature = "component-model-async"))]
1553impl wasmtime_wasi_http::p3::WasiHttpView for Host {
1554 fn http(&mut self) -> wasmtime_wasi_http::p3::WasiHttpCtxView<'_> {
1555 let ctx = self.wasi_http.as_mut().unwrap();
1556 let ctx = Arc::get_mut(ctx).expect("wasmtime_wasi_http is not compatible with threads");
1557 wasmtime_wasi_http::p3::WasiHttpCtxView {
1558 table: WasiView::ctx(unwrap_singlethread_context(&mut self.wasip1_ctx)).table,
1559 ctx,
1560 hooks: &mut self.wasi_http_hooks,
1561 }
1562 }
1563}
1564
1565#[cfg(all(feature = "wasi-tls"))]
1566impl wasmtime_wasi_tls::WasiTlsView for Host {
1567 fn tls(&mut self) -> wasmtime_wasi_tls::WasiTlsCtxView<'_> {
1568 wasmtime_wasi_tls::WasiTlsCtxView {
1569 table: WasiView::ctx(unwrap_singlethread_context(&mut self.wasip1_ctx)).table,
1570 ctx: Arc::get_mut(self.wasi_tls.as_mut().unwrap()).unwrap(),
1571 }
1572 }
1573}
1574
1575fn ctx_set_listenfd(mut num_fd: usize, builder: &mut WasiCtxBuilder) -> Result<usize> {
1576 let _ = &mut num_fd;
1577 let _ = &mut *builder;
1578
1579 #[cfg(all(unix, feature = "run"))]
1580 {
1581 use listenfd::ListenFd;
1582
1583 for env in ["LISTEN_FDS", "LISTEN_FDNAMES"] {
1584 if let Ok(val) = std::env::var(env) {
1585 builder.env(env, &val)?;
1586 }
1587 }
1588
1589 let mut listenfd = ListenFd::from_env();
1590
1591 for i in 0..listenfd.len() {
1592 if let Some(stdlistener) = listenfd.take_tcp_listener(i)? {
1593 let _ = stdlistener.set_nonblocking(true)?;
1594 let listener = TcpListener::from_std(stdlistener);
1595 builder.preopened_socket((3 + i) as _, listener)?;
1596 num_fd = 3 + i;
1597 }
1598 }
1599 }
1600
1601 Ok(num_fd)
1602}
1603
1604#[cfg(feature = "coredump")]
1605fn write_core_dump(
1606 store: &mut Store<Host>,
1607 err: &wasmtime::Error,
1608 name: &str,
1609 path: &str,
1610) -> Result<()> {
1611 use std::fs::File;
1612 use std::io::Write;
1613
1614 let core_dump = err
1615 .downcast_ref::<wasmtime::WasmCoreDump>()
1616 .expect("should have been configured to capture core dumps");
1617
1618 let core_dump = core_dump.serialize(store, name);
1619
1620 let mut core_dump_file =
1621 File::create(path).with_context(|| format!("failed to create file at `{path}`"))?;
1622 core_dump_file
1623 .write_all(&core_dump)
1624 .with_context(|| format!("failed to write core dump file at `{path}`"))?;
1625 Ok(())
1626}