Skip to main content

wasmtime_wasi/p3/
mod.rs

1//! Experimental, unstable and incomplete implementation of wasip3 version of WASI.
2//!
3//! This module is under heavy development.
4//! It is not compliant with semver and is not ready
5//! for production use.
6//!
7//! Bug and security fixes limited to wasip3 will not be given patch releases.
8//!
9//! Documentation of this module may be incorrect or out-of-sync with the implementation.
10
11pub mod bindings;
12pub mod cli;
13pub mod clocks;
14pub mod filesystem;
15pub mod random;
16pub mod sockets;
17
18use crate::p3::bindings::LinkOptions;
19use crate::{NamedId, WasiNamedView, WasiView};
20use core::pin::Pin;
21use core::task::{Context, Poll};
22use tokio::sync::oneshot;
23use wasmtime::StoreContextMut;
24use wasmtime::component::{
25    Component, Destination, Linker, StreamProducer, StreamResult, VecBuffer,
26};
27
28// Default buffer capacity to use for reads of byte-sized values.
29const DEFAULT_BUFFER_CAPACITY: usize = 8192;
30
31/// Helper structure to convert an iterator of `Result<T, E>` into a `stream<T>`
32/// plus a `future<result<_, T>>` in WIT.
33///
34/// This will drain the iterator on calls to `poll_produce` and place as many
35/// items as the input buffer has capacity for into the result. This will avoid
36/// doing anything if the async read is cancelled.
37///
38/// Note that this does not actually do anything async, it's assuming that the
39/// internal `iter` is either fast or intended to block.
40struct FallibleIteratorProducer<I, E> {
41    iter: I,
42    result: Option<oneshot::Sender<Result<(), E>>>,
43}
44
45impl<I, T, E, D> StreamProducer<D> for FallibleIteratorProducer<I, E>
46where
47    I: Iterator<Item = Result<T, E>> + Send + Unpin + 'static,
48    T: Send + Sync + 'static,
49    E: Send + 'static,
50{
51    type Item = T;
52    type Buffer = VecBuffer<T>;
53
54    fn poll_produce<'a>(
55        mut self: Pin<&mut Self>,
56        _: &mut Context<'_>,
57        mut store: StoreContextMut<'a, D>,
58        mut dst: Destination<'a, Self::Item, Self::Buffer>,
59        // Explicitly ignore `_finish` because this implementation never
60        // returns `Poll::Pending` anyway meaning that it never "blocks" in the
61        // async sense.
62        _finish: bool,
63    ) -> Poll<wasmtime::Result<StreamResult>> {
64        // Take up to `count` items as requested by the guest, or pick some
65        // reasonable-ish number for the host.
66        let count = dst.remaining(&mut store).unwrap_or(32);
67
68        // Handle 0-length reads which test for readiness as saying "we're
69        // always ready" since, in theory, this is.
70        if count == 0 {
71            return Poll::Ready(Ok(StreamResult::Completed));
72        }
73
74        // Drain `self.iter`. Successful results go into `buf`. Any errors make
75        // their way to the `oneshot` result inside this structure. Otherwise
76        // this only gets dropped if `None` is seen or an error. Also this'll
77        // terminate once `buf` grows too large.
78        let mut buf = Vec::new();
79        let result = loop {
80            match self.iter.next() {
81                Some(Ok(item)) => buf.push(item),
82                Some(Err(e)) => {
83                    self.close(Err(e));
84                    break StreamResult::Dropped;
85                }
86
87                None => {
88                    self.close(Ok(()));
89                    break StreamResult::Dropped;
90                }
91            }
92            if buf.len() >= count {
93                break StreamResult::Completed;
94            }
95        };
96
97        dst.set_buffer(buf.into());
98        return Poll::Ready(Ok(result));
99    }
100}
101
102impl<I, E> FallibleIteratorProducer<I, E> {
103    fn new(iter: I, result: oneshot::Sender<Result<(), E>>) -> Self {
104        Self {
105            iter,
106            result: Some(result),
107        }
108    }
109
110    fn close(&mut self, result: Result<(), E>) {
111        // Ignore send failures because it means the other end wasn't interested
112        // in the final error, if any.
113        let _ = self.result.take().unwrap().send(result);
114    }
115}
116
117impl<I, E> Drop for FallibleIteratorProducer<I, E> {
118    fn drop(&mut self) {
119        if self.result.is_some() {
120            self.close(Ok(()));
121        }
122    }
123}
124
125/// Add all WASI interfaces from this module into the `linker` provided.
126///
127/// This function will add all interfaces implemented by this module to the
128/// [`Linker`], which corresponds to the `wasi:cli/imports` world supported by
129/// this module.
130///
131/// # Example
132///
133/// ```
134/// use wasmtime::{Engine, Result, Store, Config};
135/// use wasmtime::component::{Linker, ResourceTable};
136/// use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView};
137///
138/// fn main() -> Result<()> {
139///     let mut config = Config::new();
140///     config.wasm_component_model_async(true);
141///     let engine = Engine::new(&config)?;
142///
143///     let mut linker = Linker::<MyState>::new(&engine);
144///     wasmtime_wasi::p3::add_to_linker(&mut linker)?;
145///     // ... add any further functionality to `linker` if desired ...
146///
147///     let mut store = Store::new(
148///         &engine,
149///         MyState::default(),
150///     );
151///
152///     // ... use `linker` to instantiate within `store` ...
153///
154///     Ok(())
155/// }
156///
157/// #[derive(Default)]
158/// struct MyState {
159///     ctx: WasiCtx,
160///     table: ResourceTable,
161/// }
162///
163/// impl WasiView for MyState {
164///     fn ctx(&mut self) -> WasiCtxView<'_> {
165///         WasiCtxView{
166///             ctx: &mut self.ctx,
167///             table: &mut self.table,
168///         }
169///     }
170/// }
171/// ```
172pub fn add_to_linker<T>(linker: &mut Linker<T>) -> wasmtime::Result<()>
173where
174    T: WasiView + 'static,
175{
176    let options = LinkOptions::default();
177    add_to_linker_with_options(linker, &options)
178}
179
180/// Similar to [`add_to_linker`], but with the ability to enable unstable features.
181pub fn add_to_linker_with_options<T>(
182    linker: &mut Linker<T>,
183    _options: &LinkOptions,
184) -> wasmtime::Result<()>
185where
186    T: WasiView + 'static,
187{
188    cli::add_to_linker(linker)?;
189    clocks::add_to_linker(linker)?;
190    filesystem::add_to_linker(linker)?;
191    random::add_to_linker(linker)?;
192    sockets::add_to_linker(linker)?;
193    Ok(())
194}
195
196/// Interfaces that are added via [`add_named_to_linker`].
197#[derive(Copy, Clone, PartialEq, Eq, Debug)]
198pub enum Interface {
199    /// `wasi:clocks/monotonic-clock`
200    ClocksMonotonicClock,
201    /// `wasi:clocks/system-clock`
202    ClocksSystemClock,
203    /// `wasi:random/random`
204    RandomRandom,
205    /// `wasi:random/insecure`
206    RandomInsecure,
207    /// `wasi:random/insecure-seed`
208    RandomInsecureSeed,
209    /// `wasi:cli/exit`
210    CliExit,
211    /// `wasi:cli/environment`
212    CliEnvironment,
213    /// `wasi:cli/stdin`
214    CliStdin,
215    /// `wasi:cli/stdout`
216    CliStdout,
217    /// `wasi:cli/stderr`
218    CliStderr,
219    /// `wasi:cli/terminal-input`
220    CliTerminalInput,
221    /// `wasi:cli/terminal-output`
222    CliTerminalOutput,
223    /// `wasi:cli/terminal-stdin`
224    CliTerminalStdin,
225    /// `wasi:cli/terminal-stdout`
226    CliTerminalStdout,
227    /// `wasi:cli/terminal-stderr`
228    CliTerminalStderr,
229    /// `wasi:filesystem/types`
230    FilesystemTypes,
231    /// `wasi:filesystem/preopens`
232    FilesystemPreopens,
233    /// `wasi:sockets/types`
234    SocketsTypes,
235    /// `wasi:sockets/ip-name-lookup`
236    SocketsIpNameLookup,
237}
238
239/// Add all WASI interfaces from this module into the `linker` provided for any
240/// named imports that a component has.
241///
242/// This function is similar to [`add_to_linker`] except that it's specifically
243/// designed to work with named imports of WASI interfaces that components may
244/// have. This requires a [`Component`] parameter to be passed in when
245/// populating the [`Linker`] provided to see what the [`Component`] actually
246/// imports.
247///
248/// All interfaces implemented by this module are added here, so the
249/// per-package functions such as [`cli::add_named_to_linker`] do not
250/// additionally need to be invoked. If this isn't low level enough you can
251/// invoke those functions, or the bindgen-generated `add_to_linker` functions
252/// within the [`named_imports`] module, directly instead.
253///
254/// [`named_imports`]: crate::p3::bindings::named_imports
255///
256/// The `lookup` function provided here is invoked for every named import found
257/// for a particular interface. The [`Interface`] given is what's being bound,
258/// and the `&str` argument is the name that the component imports it as. The
259/// embedder can then decide how it would like to allocate a [`NamedId`] for
260/// this import. If `Ok` is returned then the linker is populated with this
261/// name, and imported functions will pass the [`NamedId`] later to the
262/// implementation of [`WasiNamedView`] on `T` when invoked. If `Err` is
263/// returned then the error will cause this entire function to fail and this
264/// function call will return the same error.
265///
266/// # Example
267///
268/// ```
269/// use std::collections::HashMap;
270/// use wasmtime::component::{Component, Linker, ResourceTable};
271/// use wasmtime::{Engine, Result, Store, Config};
272/// use wasmtime_wasi::{NamedId, WasiCtx, WasiCtxView, WasiNamedView};
273///
274/// fn main() -> Result<()> {
275///     let mut config = Config::new();
276///     config.wasm_component_model_async(true);
277///     let engine = Engine::new(&config)?;
278///     let component = Component::new(&engine, "(component)")?;
279///
280///     let mut linker = Linker::<MyState>::new(&engine);
281///
282///     // ... add default functionality to `linker` as needed ...
283///
284///     // and then additionally fill in any specific named imports `component`
285///     // might have for WASI interfaces.
286///     let mut name_map = HashMap::new();
287///     wasmtime_wasi::p3::add_named_to_linker(&mut linker, &component, |_i, name| {
288///         let len = name_map.len();
289///         Ok(NamedId(*name_map.entry(name.to_string()).or_insert(len)))
290///     })?;
291///
292///     // Here a `WasiCtx` is allocated per-named-import and will then be
293///     // referred to internally by the [`NamedId`] allocated above. You could
294///     // also use `name_map` to configure each context differently.
295///     let mut my_state = MyState::default();
296///     for _ in 0..name_map.len() {
297///         my_state.contexts.push(WasiCtx::default());
298///     }
299///     let mut store = Store::new(&engine, my_state);
300///
301///     // ... use `linker` to instantiate within `store` ...
302///
303///     Ok(())
304/// }
305///
306/// #[derive(Default)]
307/// struct MyState {
308///     table: ResourceTable,
309///     contexts: Vec<WasiCtx>,
310/// }
311///
312/// impl WasiNamedView for MyState {
313///     fn ctx(&mut self, id: NamedId) -> WasiCtxView<'_> {
314///         WasiCtxView {
315///             ctx: &mut self.contexts[id.0],
316///             table: &mut self.table,
317///         }
318///     }
319/// }
320/// ```
321pub fn add_named_to_linker<T>(
322    linker: &mut Linker<T>,
323    component: &Component,
324    lookup: impl FnMut(Interface, &str) -> wasmtime::Result<NamedId>,
325) -> wasmtime::Result<()>
326where
327    T: WasiNamedView + 'static,
328{
329    let options = LinkOptions::default();
330    add_named_to_linker_with_options(linker, &options, component, lookup)
331}
332
333/// Same as [`add_named_to_linker`] except [`LinkOptions`] can be specified to
334/// configure interfaces that are added.
335pub fn add_named_to_linker_with_options<T>(
336    linker: &mut Linker<T>,
337    _options: &LinkOptions,
338    component: &Component,
339    mut lookup: impl FnMut(Interface, &str) -> wasmtime::Result<NamedId>,
340) -> wasmtime::Result<()>
341where
342    T: WasiNamedView + 'static,
343{
344    cli::add_named_to_linker(linker, component, &mut lookup)?;
345    clocks::add_named_to_linker(linker, component, &mut lookup)?;
346    filesystem::add_named_to_linker(linker, component, &mut lookup)?;
347    random::add_named_to_linker(linker, component, &mut lookup)?;
348    sockets::add_named_to_linker(linker, component, &mut lookup)?;
349    Ok(())
350}