wasmtime_wasi/p2/mod.rs
1//! # Wasmtime's WASIp2 Implementation
2//!
3//!
4//! This module provides a Wasmtime host implementation of WASI 0.2 (aka WASIp2
5//! aka Preview 2) and WASI 0.1 (aka WASIp1 aka Preview 1). WASI is implemented
6//! with the Rust crates [`tokio`] and [`cap-primitives`] primarily, meaning that
7//! operations are implemented in terms of their native platform equivalents by
8//! default.
9//!
10//! # WASIp2 interfaces
11//!
12//! This module contains implementations of the following interfaces:
13//!
14//! * [`wasi:cli/environment`]
15//! * [`wasi:cli/exit`]
16//! * [`wasi:cli/stderr`]
17//! * [`wasi:cli/stdin`]
18//! * [`wasi:cli/stdout`]
19//! * [`wasi:cli/terminal-input`]
20//! * [`wasi:cli/terminal-output`]
21//! * [`wasi:cli/terminal-stderr`]
22//! * [`wasi:cli/terminal-stdin`]
23//! * [`wasi:cli/terminal-stdout`]
24//! * [`wasi:clocks/monotonic-clock`]
25//! * [`wasi:clocks/wall-clock`]
26//! * [`wasi:filesystem/preopens`]
27//! * [`wasi:filesystem/types`]
28//! * [`wasi:random/insecure-seed`]
29//! * [`wasi:random/insecure`]
30//! * [`wasi:random/random`]
31//! * [`wasi:sockets/instance-network`]
32//! * [`wasi:sockets/ip-name-lookup`]
33//! * [`wasi:sockets/network`]
34//! * [`wasi:sockets/tcp-create-socket`]
35//! * [`wasi:sockets/tcp`]
36//! * [`wasi:sockets/udp-create-socket`]
37//! * [`wasi:sockets/udp`]
38//!
39//! Most traits are implemented for [`WasiCtxView`] trait which provides
40//! access to [`WasiCtx`] and [`ResourceTable`], which defines the configuration
41//! for WASI and handle state. The [`WasiView`] trait is used to acquire and
42//! construct a [`WasiCtxView`].
43//!
44//! The [`wasmtime-wasi-io`] crate contains implementations of the
45//! following interfaces, and this module reuses those implementations:
46//!
47//! * [`wasi:io/error`]
48//! * [`wasi:io/poll`]
49//! * [`wasi:io/streams`]
50//!
51//! These traits are implemented directly for [`ResourceTable`]. All aspects of
52//! `wasmtime-wasi-io` that are used by this module are re-exported. Unless you
53//! are implementing other host functionality that needs to interact with the
54//! WASI scheduler and don't want to use other functionality provided by
55//! `wasmtime-wasi`, you don't need to take a direct dependency on
56//! `wasmtime-wasi-io`.
57//!
58//! # Generated Bindings
59//!
60//! This module uses [`wasmtime::component::bindgen!`] to generate bindings for
61//! all WASI interfaces. Raw bindings are available in the [`bindings`] submodule
62//! of this module. Downstream users can either implement these traits themselves
63//! or you can use the built-in implementations in this module for
64//! `WasiImpl<T: WasiView>`.
65//!
66//! # The `WasiView` trait
67//!
68//! This module's implementation of WASI is done in terms of an implementation of
69//! [`WasiView`]. This trait provides a "view" into WASI-related state that is
70//! contained within a [`Store<T>`](wasmtime::Store).
71//!
72//! For all of the generated bindings in this module (Host traits),
73//! implementations are provided looking like:
74//!
75//! ```
76//! # use wasmtime_wasi::WasiCtxView;
77//! # trait WasiView {}
78//! # mod bindings { pub mod wasi { pub trait Host {} } }
79//! impl bindings::wasi::Host for WasiCtxView<'_> {
80//! // ...
81//! }
82//! ```
83//!
84//! where the [`WasiCtxView`] type comes from [`WasiView::ctx`] for the type
85//! contained within the `Store<T>`. The [`add_to_linker_sync`] and
86//! [`add_to_linker_async`] function then require that `T: WasiView` with
87//! [`Linker<T>`](wasmtime::component::Linker).
88//!
89//! To implement the [`WasiView`] trait you will first select a
90//! `T` to put in `Store<T>` (typically, by defining your own struct).
91//! Somewhere within `T` you'll store:
92//!
93//! * [`ResourceTable`] - created through default constructors.
94//! * [`WasiCtx`] - created through [`WasiCtxBuilder`].
95//!
96//! You'll then write an implementation of the [`WasiView`]
97//! trait to access those items in your `T`. For example:
98//! ```
99//! use wasmtime::component::ResourceTable;
100//! use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView};
101//!
102//! struct MyCtx {
103//! table: ResourceTable,
104//! wasi: WasiCtx,
105//! }
106//!
107//! impl WasiView for MyCtx {
108//! fn ctx(&mut self) -> WasiCtxView<'_> {
109//! WasiCtxView { ctx: &mut self.wasi, table: &mut self.table }
110//! }
111//! }
112//! ```
113//!
114//! # Async and Sync
115//!
116//! All WASIp2 functions are blocking from WebAssembly's point of view: a
117//! WebAssembly call into these functions returns only when they are complete.
118//!
119//! This module provides an implementation of those functions in the host, where
120//! for some functions, it is appropriate to implement them using async Rust and
121//! the Tokio executor. The host implementation still blocks WebAssembly, but it
122//! does not block the host's thread. Synchronous wrappers are also provided for
123//! all async implementations, which create a private Tokio executor.
124//!
125//! Users can choose between these modes of implementation using variants
126//! of the add_to_linker functions:
127//!
128//! * For non-async users, use [`add_to_linker_sync`].
129//! * For async users, use [`add_to_linker_async`].
130//!
131//! Note that bindings are generated once for async and once for sync. Most
132//! interfaces do not change, however, so only interfaces with blocking
133//! functions have bindings generated twice. Bindings are organized as:
134//!
135//! * [`bindings`] - default location of all bindings, blocking functions are
136//! `async`
137//! * [`bindings::sync`] - blocking interfaces have synchronous versions here.
138//!
139//! # Module-specific traits
140//!
141//! This module's default implementation of WASI bindings to native primitives
142//! for the platform that it is compiled for. For example opening a TCP socket
143//! uses the native platform to open a TCP socket (so long as [`WasiCtxBuilder`]
144//! allows it). There are a few important traits, however, that are specific to
145//! this module.
146//!
147//! * [`InputStream`] and [`OutputStream`] - these are the host traits
148//! behind the WASI `input-stream` and `output-stream` types in the
149//! `wasi:io/streams` interface. These enable embedders to build their own
150//! custom stream and insert them into a [`ResourceTable`] (as a boxed trait
151//! object, see [`DynInputStream`] and [`DynOutputStream`]) to be used from
152//! wasm.
153//!
154//! * [`Pollable`] - this trait enables building arbitrary logic to get hooked
155//! into a `pollable` resource from `wasi:io/poll`. A pollable resource is
156//! created through the [`subscribe`] function.
157//!
158//! * [`HostWallClock`](crate::HostWallClock) and [`HostMonotonicClock`](crate::HostMonotonicClock) are used in conjunction with
159//! [`WasiCtxBuilder::wall_clock`] and [`WasiCtxBuilder::monotonic_clock`] if
160//! the defaults host's clock should not be used.
161//!
162//! * [`StdinStream`] and [`StdoutStream`] are used to provide custom
163//! stdin/stdout streams if they're not inherited (or null, which is the
164//! default).
165//!
166//! These traits enable embedders to customize small portions of WASI interfaces
167//! provided while still providing all other interfaces.
168//!
169//! # Examples
170//!
171//! Usage of this module is done through a few steps to get everything hooked up:
172//!
173//! 1. First implement [`WasiView`] for your type which is the
174//! `T` in `Store<T>`.
175//! 2. Add WASI interfaces to a `wasmtime::component::Linker<T>`. This is either
176//! done through top-level functions like [`add_to_linker_sync`] or through
177//! individual `add_to_linker` functions in generated bindings throughout
178//! this module.
179//! 3. Create a [`WasiCtx`] for each `Store<T>` through [`WasiCtxBuilder`]. Each
180//! WASI context is "null" or "empty" by default, so items must be explicitly
181//! added to get accessed by wasm (such as env vars or program arguments).
182//! 4. Use the previous `Linker<T>` to instantiate a `Component` within a
183//! `Store<T>`.
184//!
185//! For examples see each of [`WasiView`], [`WasiCtx`], [`WasiCtxBuilder`],
186//! [`add_to_linker_sync`], and [`bindings::Command`].
187//!
188//! [`wasmtime::component::bindgen!`]: https://docs.rs/wasmtime/latest/wasmtime/component/macro.bindgen.html
189//! [`tokio`]: https://crates.io/crates/tokio
190//! [`cap-primitives`]: https://crates.io/crates/cap-primitives
191//! [`wasmtime-wasi-io`]: https://crates.io/crates/wasmtime-wasi-io
192//! [`wasi:cli/environment`]: bindings::cli::environment::Host
193//! [`wasi:cli/exit`]: bindings::cli::exit::Host
194//! [`wasi:cli/stderr`]: bindings::cli::stderr::Host
195//! [`wasi:cli/stdin`]: bindings::cli::stdin::Host
196//! [`wasi:cli/stdout`]: bindings::cli::stdout::Host
197//! [`wasi:cli/terminal-input`]: bindings::cli::terminal_input::Host
198//! [`wasi:cli/terminal-output`]: bindings::cli::terminal_output::Host
199//! [`wasi:cli/terminal-stdin`]: bindings::cli::terminal_stdin::Host
200//! [`wasi:cli/terminal-stdout`]: bindings::cli::terminal_stdout::Host
201//! [`wasi:cli/terminal-stderr`]: bindings::cli::terminal_stderr::Host
202//! [`wasi:clocks/monotonic-clock`]: bindings::clocks::monotonic_clock::Host
203//! [`wasi:clocks/wall-clock`]: bindings::clocks::wall_clock::Host
204//! [`wasi:filesystem/preopens`]: bindings::filesystem::preopens::Host
205//! [`wasi:filesystem/types`]: bindings::filesystem::types::Host
206//! [`wasi:io/error`]: wasmtime_wasi_io::bindings::wasi::io::error::Host
207//! [`wasi:io/poll`]: wasmtime_wasi_io::bindings::wasi::io::poll::Host
208//! [`wasi:io/streams`]: wasmtime_wasi_io::bindings::wasi::io::streams::Host
209//! [`wasi:random/insecure-seed`]: bindings::random::insecure_seed::Host
210//! [`wasi:random/insecure`]: bindings::random::insecure::Host
211//! [`wasi:random/random`]: bindings::random::random::Host
212//! [`wasi:sockets/instance-network`]: bindings::sockets::instance_network::Host
213//! [`wasi:sockets/ip-name-lookup`]: bindings::sockets::ip_name_lookup::Host
214//! [`wasi:sockets/network`]: bindings::sockets::network::Host
215//! [`wasi:sockets/tcp-create-socket`]: bindings::sockets::tcp_create_socket::Host
216//! [`wasi:sockets/tcp`]: bindings::sockets::tcp::Host
217//! [`wasi:sockets/udp-create-socket`]: bindings::sockets::udp_create_socket::Host
218//! [`wasi:sockets/udp`]: bindings::sockets::udp::Host
219//! [`ResourceTable`]: wasmtime::component::ResourceTable
220//! [`WasiCtx`]: crate::WasiCtx
221//! [`WasiCtxView`]: crate::WasiCtxView
222//! [`WasiCtxBuilder`]: crate::WasiCtxBuilder
223//! [`WasiCtxBuilder::wall_clock`]: crate::WasiCtxBuilder::wall_clock
224//! [`WasiCtxBuilder::monotonic_clock`]: crate::WasiCtxBuilder::monotonic_clock
225//! [`StdinStream`]: crate::cli::StdinStream
226//! [`StdoutStream`]: crate::cli::StdoutStream
227
228use crate::cli::{WasiCli, WasiCliNamed, WasiCliView as _};
229use crate::clocks::{WasiClocks, WasiClocksNamed, WasiClocksView as _};
230use crate::filesystem::{WasiFilesystem, WasiFilesystemNamed, WasiFilesystemView as _};
231use crate::random::{WasiRandom, WasiRandomNamed};
232use crate::sockets::{WasiSockets, WasiSocketsNamed, WasiSocketsView as _};
233use crate::{NamedId, WasiCtxNamedView, WasiNamedView, WasiView};
234use wasmtime::component::{Component, HasData, Linker, ResourceTable};
235
236pub mod bindings;
237pub(crate) mod filesystem;
238mod host;
239mod ip_name_lookup;
240mod network;
241pub mod pipe;
242mod poll;
243mod stdio;
244mod tcp;
245mod udp;
246mod write_stream;
247
248pub use self::filesystem::{FsError, FsResult, ReaddirIterator};
249pub use self::network::{Network, SocketError, SocketResult};
250pub use self::stdio::IsATTY;
251pub use tcp::TcpSocket;
252pub use udp::UdpSocket;
253// These contents of wasmtime-wasi-io are re-exported by this module for compatibility:
254// they were originally defined in this module before being factored out, and many
255// users of this module depend on them at these names.
256pub use wasmtime_wasi_io::poll::{DynFuture, DynPollable, MakeFuture, Pollable, subscribe};
257pub use wasmtime_wasi_io::streams::{
258 DynInputStream, DynOutputStream, Error as IoError, InputStream, OutputStream, StreamError,
259 StreamResult,
260};
261
262/// Add all WASI interfaces from this crate into the `linker` provided.
263///
264/// This function will add the `async` variant of all interfaces into the
265/// [`Linker`] provided. For embeddings with async support disabled see
266/// [`add_to_linker_sync`] instead.
267///
268/// This function will add all interfaces implemented by this crate to the
269/// [`Linker`], which corresponds to the `wasi:cli/imports` world supported by
270/// this crate.
271///
272/// # Example
273///
274/// ```
275/// use wasmtime::{Engine, Result, Store, Config};
276/// use wasmtime::component::{ResourceTable, Linker};
277/// use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView};
278///
279/// fn main() -> Result<()> {
280/// let engine = Engine::default();
281///
282/// let mut linker = Linker::<MyState>::new(&engine);
283/// wasmtime_wasi::p2::add_to_linker_async(&mut linker)?;
284/// // ... add any further functionality to `linker` if desired ...
285///
286/// let mut builder = WasiCtx::builder();
287///
288/// // ... configure `builder` more to add env vars, args, etc ...
289///
290/// let mut store = Store::new(
291/// &engine,
292/// MyState {
293/// ctx: builder.build(),
294/// table: ResourceTable::new(),
295/// },
296/// );
297///
298/// // ... use `linker` to instantiate within `store` ...
299///
300/// Ok(())
301/// }
302///
303/// struct MyState {
304/// ctx: WasiCtx,
305/// table: ResourceTable,
306/// }
307///
308/// impl WasiView for MyState {
309/// fn ctx(&mut self) -> WasiCtxView<'_> {
310/// WasiCtxView { ctx: &mut self.ctx, table: &mut self.table }
311/// }
312/// }
313/// ```
314pub fn add_to_linker_async<T: WasiView>(linker: &mut Linker<T>) -> wasmtime::Result<()> {
315 let options = bindings::LinkOptions::default();
316 add_to_linker_with_options_async(linker, &options)
317}
318
319/// Similar to [`add_to_linker_async`], but with the ability to enable unstable features.
320pub fn add_to_linker_with_options_async<T: WasiView>(
321 linker: &mut Linker<T>,
322 options: &bindings::LinkOptions,
323) -> wasmtime::Result<()> {
324 add_async_io_to_linker(linker)?;
325 add_nonblocking_to_linker(linker, options)?;
326
327 let l = linker;
328 bindings::filesystem::types::add_to_linker::<T, WasiFilesystem>(l, T::filesystem)?;
329 bindings::sockets::tcp::add_to_linker::<T, WasiSockets>(l, T::sockets)?;
330 bindings::sockets::udp::add_to_linker::<T, WasiSockets>(l, T::sockets)?;
331 bindings::sockets::udp_create_socket::add_to_linker::<T, WasiSockets>(l, T::sockets)?;
332 bindings::sockets::ip_name_lookup::add_to_linker::<T, WasiSockets>(l, T::sockets)?;
333 Ok(())
334}
335
336/// Shared functionality for [`add_to_linker_async`] and [`add_to_linker_sync`].
337fn add_nonblocking_to_linker<'a, T: WasiView, O>(
338 linker: &mut Linker<T>,
339 options: &'a O,
340) -> wasmtime::Result<()>
341where
342 bindings::sockets::network::LinkOptions: From<&'a O>,
343{
344 use crate::p2::bindings::{cli, clocks, filesystem, random, sockets};
345
346 let l = linker;
347 clocks::wall_clock::add_to_linker::<T, WasiClocks>(l, T::clocks)?;
348 clocks::monotonic_clock::add_to_linker::<T, WasiClocks>(l, T::clocks)?;
349 filesystem::preopens::add_to_linker::<T, WasiFilesystem>(l, T::filesystem)?;
350 random::random::add_to_linker::<T, WasiRandom>(l, |t| &mut t.ctx().ctx.random)?;
351 random::insecure::add_to_linker::<T, WasiRandom>(l, |t| &mut t.ctx().ctx.random)?;
352 random::insecure_seed::add_to_linker::<T, WasiRandom>(l, |t| &mut t.ctx().ctx.random)?;
353 cli::exit::add_to_linker::<T, WasiCli>(l, T::cli)?;
354 cli::environment::add_to_linker::<T, WasiCli>(l, T::cli)?;
355 cli::stdin::add_to_linker::<T, WasiCli>(l, T::cli)?;
356 cli::stdout::add_to_linker::<T, WasiCli>(l, T::cli)?;
357 cli::stderr::add_to_linker::<T, WasiCli>(l, T::cli)?;
358 cli::terminal_input::add_to_linker::<T, WasiCli>(l, T::cli)?;
359 cli::terminal_output::add_to_linker::<T, WasiCli>(l, T::cli)?;
360 cli::terminal_stdin::add_to_linker::<T, WasiCli>(l, T::cli)?;
361 cli::terminal_stdout::add_to_linker::<T, WasiCli>(l, T::cli)?;
362 cli::terminal_stderr::add_to_linker::<T, WasiCli>(l, T::cli)?;
363 sockets::tcp_create_socket::add_to_linker::<T, WasiSockets>(l, T::sockets)?;
364 sockets::instance_network::add_to_linker::<T, WasiSockets>(l, T::sockets)?;
365 sockets::network::add_to_linker::<T, WasiSockets>(l, &options.into(), T::sockets)?;
366 Ok(())
367}
368
369/// Same as [`add_to_linker_async`] except that this only adds interfaces
370/// present in the `wasi:http/proxy` world.
371pub fn add_to_linker_proxy_interfaces_async<T: WasiView>(
372 linker: &mut Linker<T>,
373) -> wasmtime::Result<()> {
374 add_async_io_to_linker(linker)?;
375 add_proxy_interfaces_nonblocking(linker)
376}
377
378/// Same as [`add_to_linker_sync`] except that this only adds interfaces
379/// present in the `wasi:http/proxy` world.
380#[doc(hidden)]
381pub fn add_to_linker_proxy_interfaces_sync<T: WasiView>(
382 linker: &mut Linker<T>,
383) -> wasmtime::Result<()> {
384 add_sync_wasi_io(linker)?;
385 add_proxy_interfaces_nonblocking(linker)
386}
387
388fn add_proxy_interfaces_nonblocking<T: WasiView>(linker: &mut Linker<T>) -> wasmtime::Result<()> {
389 use crate::p2::bindings::{cli, clocks, random};
390
391 let l = linker;
392 clocks::wall_clock::add_to_linker::<T, WasiClocks>(l, T::clocks)?;
393 clocks::monotonic_clock::add_to_linker::<T, WasiClocks>(l, T::clocks)?;
394 random::random::add_to_linker::<T, WasiRandom>(l, |t| &mut t.ctx().ctx.random)?;
395 cli::stdin::add_to_linker::<T, WasiCli>(l, T::cli)?;
396 cli::stdout::add_to_linker::<T, WasiCli>(l, T::cli)?;
397 cli::stderr::add_to_linker::<T, WasiCli>(l, T::cli)?;
398 Ok(())
399}
400
401/// Add all WASI interfaces from this crate into the `linker` provided.
402///
403/// This function will add the synchronous variant of all interfaces into the
404/// [`Linker`] provided. For embeddings with async support enabled see
405/// [`add_to_linker_async`] instead.
406///
407/// This function will add all interfaces implemented by this crate to the
408/// [`Linker`], which corresponds to the `wasi:cli/imports` world supported by
409/// this crate.
410///
411/// # Example
412///
413/// ```
414/// use wasmtime::{Engine, Result, Store, Config};
415/// use wasmtime::component::{ResourceTable, Linker};
416/// use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView};
417///
418/// fn main() -> Result<()> {
419/// let engine = Engine::default();
420///
421/// let mut linker = Linker::<MyState>::new(&engine);
422/// wasmtime_wasi::p2::add_to_linker_sync(&mut linker)?;
423/// // ... add any further functionality to `linker` if desired ...
424///
425/// let mut builder = WasiCtx::builder();
426///
427/// // ... configure `builder` more to add env vars, args, etc ...
428///
429/// let mut store = Store::new(
430/// &engine,
431/// MyState {
432/// ctx: builder.build(),
433/// table: ResourceTable::new(),
434/// },
435/// );
436///
437/// // ... use `linker` to instantiate within `store` ...
438///
439/// Ok(())
440/// }
441///
442/// struct MyState {
443/// ctx: WasiCtx,
444/// table: ResourceTable,
445/// }
446/// impl WasiView for MyState {
447/// fn ctx(&mut self) -> WasiCtxView<'_> {
448/// WasiCtxView { ctx: &mut self.ctx, table: &mut self.table }
449/// }
450/// }
451/// ```
452pub fn add_to_linker_sync<T: WasiView>(
453 linker: &mut wasmtime::component::Linker<T>,
454) -> wasmtime::Result<()> {
455 let options = bindings::sync::LinkOptions::default();
456 add_to_linker_with_options_sync(linker, &options)
457}
458
459/// Similar to [`add_to_linker_sync`], but with the ability to enable unstable features.
460pub fn add_to_linker_with_options_sync<T: WasiView>(
461 linker: &mut wasmtime::component::Linker<T>,
462 options: &bindings::sync::LinkOptions,
463) -> wasmtime::Result<()> {
464 add_nonblocking_to_linker(linker, options)?;
465 add_sync_wasi_io(linker)?;
466
467 let l = linker;
468 bindings::sync::filesystem::types::add_to_linker::<T, WasiFilesystem>(l, T::filesystem)?;
469 bindings::sync::sockets::tcp::add_to_linker::<T, WasiSockets>(l, T::sockets)?;
470 bindings::sync::sockets::udp::add_to_linker::<T, WasiSockets>(l, T::sockets)?;
471 bindings::sync::sockets::udp_create_socket::add_to_linker::<T, WasiSockets>(l, T::sockets)?;
472 bindings::sync::sockets::ip_name_lookup::add_to_linker::<T, WasiSockets>(l, T::sockets)?;
473 Ok(())
474}
475
476/// Shared functionality of [`add_to_linker_sync`]` and
477/// [`add_to_linker_proxy_interfaces_sync`].
478fn add_sync_wasi_io<T: WasiView>(
479 linker: &mut wasmtime::component::Linker<T>,
480) -> wasmtime::Result<()> {
481 let l = linker;
482 wasmtime_wasi_io::bindings::wasi::io::error::add_to_linker::<T, HasIo>(l, |t| t.ctx().table)?;
483 bindings::sync::io::poll::add_to_linker::<T, HasIo>(l, |t| t.ctx().table)?;
484 bindings::sync::io::streams::add_to_linker::<T, HasIo>(l, |t| t.ctx().table)?;
485 Ok(())
486}
487
488struct HasIo;
489
490impl HasData for HasIo {
491 type Data<'a> = &'a mut ResourceTable;
492}
493
494// FIXME: it's a bit unfortunate that this can't use
495// `wasmtime_wasi_io::add_to_linker` and that's because `T: WasiView`, here,
496// not `T: IoView`. Ideally we'd have `impl<T: WasiView> IoView for T` but
497// that's not possible with these two traits in separate crates. For now this
498// is some small duplication but if this gets worse over time then we'll want
499// to massage this.
500fn add_async_io_to_linker<T: WasiView>(l: &mut Linker<T>) -> wasmtime::Result<()> {
501 wasmtime_wasi_io::bindings::wasi::io::error::add_to_linker::<T, HasIo>(l, |t| t.ctx().table)?;
502 wasmtime_wasi_io::bindings::wasi::io::poll::add_to_linker::<T, HasIo>(l, |t| t.ctx().table)?;
503 wasmtime_wasi_io::bindings::wasi::io::streams::add_to_linker::<T, HasIo>(l, |t| t.ctx().table)?;
504 Ok(())
505}
506
507/// Interfaces that are added via [`add_named_to_linker_async`].
508#[derive(Copy, Clone, PartialEq, Eq, Debug)]
509pub enum Interface {
510 /// `wasi:clocks/monotonic-clock`
511 ClocksMonotonicClock,
512 /// `wasi:clocks/wall-clock`
513 ClocksWallClock,
514 /// `wasi:random/random`
515 RandomRandom,
516 /// `wasi:random/insecure`
517 RandomInsecure,
518 /// `wasi:random/insecure-seed`
519 RandomInsecureSeed,
520 /// `wasi:cli/exit`
521 CliExit,
522 /// `wasi:cli/environment`
523 CliEnvironment,
524 /// `wasi:cli/stdin`
525 CliStdin,
526 /// `wasi:cli/stdout`
527 CliStdout,
528 /// `wasi:cli/stderr`
529 CliStderr,
530 /// `wasi:cli/terminal-input`
531 CliTerminalInput,
532 /// `wasi:cli/terminal-output`
533 CliTerminalOutput,
534 /// `wasi:cli/terminal-stdin`
535 CliTerminalStdin,
536 /// `wasi:cli/terminal-stdout`
537 CliTerminalStdout,
538 /// `wasi:cli/terminal-stderr`
539 CliTerminalStderr,
540 /// `wasi:filesystem/types`
541 FilesystemTypes,
542 /// `wasi:filesystem/preopens`
543 FilesystemPreopens,
544 /// `wasi:sockets/instance-network`
545 SocketsInstanceNetwork,
546 /// `wasi:sockets/network`
547 SocketsNetwork,
548 /// `wasi:sockets/ip-name-lookup`
549 SocketsIpNameLookup,
550 /// `wasi:sockets/tcp-create-socket`
551 SocketsTcpCreateSocket,
552 /// `wasi:sockets/tcp`
553 SocketsTcp,
554 /// `wasi:sockets/udp-create-socket`
555 SocketsUdpCreateSocket,
556 /// `wasi:sockets/udp`
557 SocketsUdp,
558}
559
560/// Add all WASI interfaces from this crate into the `linker` provided for any
561/// named imports that a component has.
562///
563/// This function is similar to [`add_to_linker_async`] except that it's specifically
564/// designed to work with named imports of WASI interfaces that components may
565/// have. This requires a [`Component`] parameter to be passed in when
566/// populating the [`Linker`] provided to see what the [`Component`] actually
567/// imports.
568///
569/// Like [`add_to_linker_async`] this adds the `async` variant of all
570/// interfaces. If this isn't low level enough you can invoke the
571/// bindgen-generated `add_to_linker` functions within the [`named_imports`]
572/// module directly instead.
573///
574/// [`named_imports`]: crate::p2::bindings::named_imports
575///
576/// The `lookup` function provided here is invoked for every named import found
577/// for a particular interface. The [`Interface`] given is what's being bound,
578/// and the `&str` argument is the name that the component imports it as. The
579/// embedder can then decide how it would like to allocate a [`NamedId`] for
580/// this import. If `Ok` is returned then the linker is populated with this
581/// name, and imported functions will pass the [`NamedId`] later to the
582/// implementation of [`WasiNamedView`] on `T` when invoked. If `Err` is
583/// returned then the error will cause this entire function to fail and this
584/// function call will return the same error.
585///
586/// # Example
587///
588/// ```
589/// use std::collections::HashMap;
590/// use wasmtime::component::{Component, Linker, ResourceTable};
591/// use wasmtime::{Engine, Result, Store, Config};
592/// use wasmtime_wasi::{NamedId, WasiCtx, WasiCtxView, WasiNamedView};
593///
594/// fn main() -> Result<()> {
595/// let engine = Engine::default();
596/// let component = Component::new(&engine, "(component)")?;
597///
598/// let mut linker = Linker::<MyState>::new(&engine);
599///
600/// // ... add default functionality to `linker` as needed ...
601///
602/// // and then additionally fill in any specific named imports `component`
603/// // might have for WASI interfaces.
604/// let mut name_map = HashMap::new();
605/// wasmtime_wasi::p2::add_named_to_linker_async(&mut linker, &component, |_i, name| {
606/// let len = name_map.len();
607/// Ok(NamedId(*name_map.entry(name.to_string()).or_insert(len)))
608/// })?;
609///
610/// // Here a `WasiCtx` is allocated per-named-import and will then be
611/// // referred to internally by the [`NamedId`] allocated above. You could
612/// // also use `name_map` to configure each context differently.
613/// let mut my_state = MyState::default();
614/// for _ in 0..name_map.len() {
615/// my_state.contexts.push(WasiCtx::default());
616/// }
617/// let mut store = Store::new(&engine, my_state);
618///
619/// // ... use `linker` to instantiate within `store` ...
620///
621/// Ok(())
622/// }
623///
624/// #[derive(Default)]
625/// struct MyState {
626/// table: ResourceTable,
627/// contexts: Vec<WasiCtx>,
628/// }
629///
630/// impl WasiNamedView for MyState {
631/// fn ctx(&mut self, id: NamedId) -> WasiCtxView<'_> {
632/// WasiCtxView {
633/// ctx: &mut self.contexts[id.0],
634/// table: &mut self.table,
635/// }
636/// }
637/// }
638/// ```
639pub fn add_named_to_linker_async<T>(
640 linker: &mut Linker<T>,
641 component: &Component,
642 lookup: impl FnMut(Interface, &str) -> wasmtime::Result<NamedId>,
643) -> wasmtime::Result<()>
644where
645 T: WasiNamedView,
646{
647 let options = bindings::LinkOptions::default();
648 add_named_to_linker_with_options_async(linker, &options, component, lookup)
649}
650
651/// Same as [`add_named_to_linker_async`] except [`bindings::LinkOptions`]
652/// can be specified to configure interfaces that are added.
653pub fn add_named_to_linker_with_options_async<T>(
654 linker: &mut Linker<T>,
655 options: &bindings::LinkOptions,
656 component: &Component,
657 mut lookup: impl FnMut(Interface, &str) -> wasmtime::Result<NamedId>,
658) -> wasmtime::Result<()>
659where
660 T: WasiNamedView,
661{
662 use crate::p2::bindings::named_imports::wasi::{cli, clocks, filesystem, random, sockets};
663
664 let l = linker;
665 clocks::wall_clock::add_to_linker::<T, WasiClocksNamed<T>>(
666 l,
667 component,
668 |name| lookup(Interface::ClocksWallClock, name),
669 |x| WasiCtxNamedView(x),
670 )?;
671 clocks::monotonic_clock::add_to_linker::<T, WasiClocksNamed<T>>(
672 l,
673 component,
674 |name| lookup(Interface::ClocksMonotonicClock, name),
675 |x| WasiCtxNamedView(x),
676 )?;
677 filesystem::types::add_to_linker::<T, WasiFilesystemNamed<T>>(
678 l,
679 component,
680 |name| lookup(Interface::FilesystemTypes, name),
681 |x| WasiCtxNamedView(x),
682 )?;
683 filesystem::preopens::add_to_linker::<T, WasiFilesystemNamed<T>>(
684 l,
685 component,
686 |name| lookup(Interface::FilesystemPreopens, name),
687 |x| WasiCtxNamedView(x),
688 )?;
689 random::random::add_to_linker::<T, WasiRandomNamed<T>>(
690 l,
691 component,
692 |name| lookup(Interface::RandomRandom, name),
693 |x| WasiCtxNamedView(x),
694 )?;
695 random::insecure::add_to_linker::<T, WasiRandomNamed<T>>(
696 l,
697 component,
698 |name| lookup(Interface::RandomInsecure, name),
699 |x| WasiCtxNamedView(x),
700 )?;
701 random::insecure_seed::add_to_linker::<T, WasiRandomNamed<T>>(
702 l,
703 component,
704 |name| lookup(Interface::RandomInsecureSeed, name),
705 |x| WasiCtxNamedView(x),
706 )?;
707 cli::exit::add_to_linker::<T, WasiCliNamed<T>>(
708 l,
709 component,
710 |name| lookup(Interface::CliExit, name),
711 |x| WasiCtxNamedView(x),
712 )?;
713 cli::environment::add_to_linker::<T, WasiCliNamed<T>>(
714 l,
715 component,
716 |name| lookup(Interface::CliEnvironment, name),
717 |x| WasiCtxNamedView(x),
718 )?;
719 cli::stdin::add_to_linker::<T, WasiCliNamed<T>>(
720 l,
721 component,
722 |name| lookup(Interface::CliStdin, name),
723 |x| WasiCtxNamedView(x),
724 )?;
725 cli::stdout::add_to_linker::<T, WasiCliNamed<T>>(
726 l,
727 component,
728 |name| lookup(Interface::CliStdout, name),
729 |x| WasiCtxNamedView(x),
730 )?;
731 cli::stderr::add_to_linker::<T, WasiCliNamed<T>>(
732 l,
733 component,
734 |name| lookup(Interface::CliStderr, name),
735 |x| WasiCtxNamedView(x),
736 )?;
737 cli::terminal_input::add_to_linker::<T, WasiCliNamed<T>>(
738 l,
739 component,
740 |name| lookup(Interface::CliTerminalInput, name),
741 |x| WasiCtxNamedView(x),
742 )?;
743 cli::terminal_output::add_to_linker::<T, WasiCliNamed<T>>(
744 l,
745 component,
746 |name| lookup(Interface::CliTerminalOutput, name),
747 |x| WasiCtxNamedView(x),
748 )?;
749 cli::terminal_stdin::add_to_linker::<T, WasiCliNamed<T>>(
750 l,
751 component,
752 |name| lookup(Interface::CliTerminalStdin, name),
753 |x| WasiCtxNamedView(x),
754 )?;
755 cli::terminal_stdout::add_to_linker::<T, WasiCliNamed<T>>(
756 l,
757 component,
758 |name| lookup(Interface::CliTerminalStdout, name),
759 |x| WasiCtxNamedView(x),
760 )?;
761 cli::terminal_stderr::add_to_linker::<T, WasiCliNamed<T>>(
762 l,
763 component,
764 |name| lookup(Interface::CliTerminalStderr, name),
765 |x| WasiCtxNamedView(x),
766 )?;
767 sockets::instance_network::add_to_linker::<T, WasiSocketsNamed<T>>(
768 l,
769 component,
770 |name| lookup(Interface::SocketsInstanceNetwork, name),
771 |x| WasiCtxNamedView(x),
772 )?;
773 sockets::network::add_to_linker::<T, WasiSocketsNamed<T>>(
774 l,
775 component,
776 |name| lookup(Interface::SocketsNetwork, name),
777 &options.into(),
778 |x| WasiCtxNamedView(x),
779 )?;
780 sockets::ip_name_lookup::add_to_linker::<T, WasiSocketsNamed<T>>(
781 l,
782 component,
783 |name| lookup(Interface::SocketsIpNameLookup, name),
784 |x| WasiCtxNamedView(x),
785 )?;
786 sockets::tcp_create_socket::add_to_linker::<T, WasiSocketsNamed<T>>(
787 l,
788 component,
789 |name| lookup(Interface::SocketsTcpCreateSocket, name),
790 |x| WasiCtxNamedView(x),
791 )?;
792 sockets::tcp::add_to_linker::<T, WasiSocketsNamed<T>>(
793 l,
794 component,
795 |name| lookup(Interface::SocketsTcp, name),
796 |x| WasiCtxNamedView(x),
797 )?;
798 sockets::udp_create_socket::add_to_linker::<T, WasiSocketsNamed<T>>(
799 l,
800 component,
801 |name| lookup(Interface::SocketsUdpCreateSocket, name),
802 |x| WasiCtxNamedView(x),
803 )?;
804 sockets::udp::add_to_linker::<T, WasiSocketsNamed<T>>(
805 l,
806 component,
807 |name| lookup(Interface::SocketsUdp, name),
808 |x| WasiCtxNamedView(x),
809 )?;
810 Ok(())
811}