//! Experimental, unstable and incomplete implementation of wasip3 version of WASI.1//!2//! This module is under heavy development.3//! It is not compliant with semver and is not ready4//! for production use.5//!6//! Bug and security fixes limited to wasip3 will not be given patch releases.7//!8//! Documentation of this module may be incorrect or out-of-sync with the implementation.910pub mod bindings;11pub mod cli;12pub mod clocks;13pub mod filesystem;14pub mod random;15pub mod sockets;1617use crate::WasiView;18use crate::p3::bindings::LinkOptions;19use anyhow::Context as _;20use core::marker::PhantomData;21use core::pin::Pin;22use core::task::{Context, Poll};23use tokio::sync::oneshot;24use wasmtime::StoreContextMut;25use wasmtime::component::{26Accessor, Destination, FutureProducer, Linker, StreamProducer, StreamResult, VecBuffer,27};2829// Default buffer capacity to use for reads of byte-sized values.30const DEFAULT_BUFFER_CAPACITY: usize = 8192;3132struct StreamEmptyProducer<T>(PhantomData<fn(T) -> T>);3334impl<T> Default for StreamEmptyProducer<T> {35fn default() -> Self {36Self(PhantomData)37}38}3940impl<T: Send + Sync + 'static, D> StreamProducer<D> for StreamEmptyProducer<T> {41type Item = T;42type Buffer = Option<Self::Item>;4344fn poll_produce<'a>(45self: Pin<&mut Self>,46_: &mut Context<'_>,47_: StoreContextMut<'a, D>,48_: Destination<'a, Self::Item, Self::Buffer>,49_: bool,50) -> Poll<wasmtime::Result<StreamResult>> {51Poll::Ready(Ok(StreamResult::Dropped))52}53}5455struct FutureReadyProducer<T>(T);5657impl<T, D> FutureProducer<D> for FutureReadyProducer<T>58where59T: Send + 'static,60{61type Item = T;6263async fn produce(self, _: &Accessor<D>) -> wasmtime::Result<T> {64Ok(self.0)65}66}6768struct FutureOneshotProducer<T>(oneshot::Receiver<T>);6970impl<T, D> FutureProducer<D> for FutureOneshotProducer<T>71where72T: Send + 'static,73{74type Item = T;7576async fn produce(self, _: &Accessor<D>) -> wasmtime::Result<T> {77self.0.await.context("oneshot sender dropped")78}79}8081/// Helper structure to convert an iterator of `Result<T, E>` into a `stream<T>`82/// plus a `future<result<_, T>>` in WIT.83///84/// This will drain the iterator on calls to `poll_produce` and place as many85/// items as the input buffer has capacity for into the result. This will avoid86/// doing anything if the async read is cancelled.87///88/// Note that this does not actually do anything async, it's assuming that the89/// internal `iter` is either fast or intended to block.90struct FallibleIteratorProducer<I, E> {91iter: I,92result: Option<oneshot::Sender<Result<(), E>>>,93}9495impl<I, T, E, D> StreamProducer<D> for FallibleIteratorProducer<I, E>96where97I: Iterator<Item = Result<T, E>> + Send + Unpin + 'static,98T: Send + Sync + 'static,99E: Send + 'static,100{101type Item = T;102type Buffer = VecBuffer<T>;103104fn poll_produce<'a>(105mut self: Pin<&mut Self>,106_: &mut Context<'_>,107mut store: StoreContextMut<'a, D>,108mut dst: Destination<'a, Self::Item, Self::Buffer>,109// Explicitly ignore `_finish` because this implementation never110// returns `Poll::Pending` anyway meaning that it never "blocks" in the111// async sense.112_finish: bool,113) -> Poll<wasmtime::Result<StreamResult>> {114// Take up to `count` items as requested by the guest, or pick some115// reasonable-ish number for the host.116let count = dst.remaining(&mut store).unwrap_or(32);117118// Handle 0-length reads which test for readiness as saying "we're119// always ready" since, in theory, this is.120if count == 0 {121return Poll::Ready(Ok(StreamResult::Completed));122}123124// Drain `self.iter`. Successful results go into `buf`. Any errors make125// their way to the `oneshot` result inside this structure. Otherwise126// this only gets dropped if `None` is seen or an error. Also this'll127// terminate once `buf` grows too large.128let mut buf = Vec::new();129let result = loop {130match self.iter.next() {131Some(Ok(item)) => buf.push(item),132Some(Err(e)) => {133self.close(Err(e));134break StreamResult::Dropped;135}136137None => {138self.close(Ok(()));139break StreamResult::Dropped;140}141}142if buf.len() >= count {143break StreamResult::Completed;144}145};146147dst.set_buffer(buf.into());148return Poll::Ready(Ok(result));149}150}151152impl<I, E> FallibleIteratorProducer<I, E> {153fn new(iter: I, result: oneshot::Sender<Result<(), E>>) -> Self {154Self {155iter,156result: Some(result),157}158}159160fn close(&mut self, result: Result<(), E>) {161// Ignore send failures because it means the other end wasn't interested162// in the final error, if any.163let _ = self.result.take().unwrap().send(result);164}165}166167impl<I, E> Drop for FallibleIteratorProducer<I, E> {168fn drop(&mut self) {169if self.result.is_some() {170self.close(Ok(()));171}172}173}174175/// Add all WASI interfaces from this module into the `linker` provided.176///177/// This function will add all interfaces implemented by this module to the178/// [`Linker`], which corresponds to the `wasi:cli/imports` world supported by179/// this module.180///181/// # Example182///183/// ```184/// use wasmtime::{Engine, Result, Store, Config};185/// use wasmtime::component::{Linker, ResourceTable};186/// use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView};187///188/// fn main() -> Result<()> {189/// let mut config = Config::new();190/// config.async_support(true);191/// config.wasm_component_model_async(true);192/// let engine = Engine::new(&config)?;193///194/// let mut linker = Linker::<MyState>::new(&engine);195/// wasmtime_wasi::p3::add_to_linker(&mut linker)?;196/// // ... add any further functionality to `linker` if desired ...197///198/// let mut store = Store::new(199/// &engine,200/// MyState::default(),201/// );202///203/// // ... use `linker` to instantiate within `store` ...204///205/// Ok(())206/// }207///208/// #[derive(Default)]209/// struct MyState {210/// ctx: WasiCtx,211/// table: ResourceTable,212/// }213///214/// impl WasiView for MyState {215/// fn ctx(&mut self) -> WasiCtxView<'_> {216/// WasiCtxView{217/// ctx: &mut self.ctx,218/// table: &mut self.table,219/// }220/// }221/// }222/// ```223pub fn add_to_linker<T>(linker: &mut Linker<T>) -> wasmtime::Result<()>224where225T: WasiView + 'static,226{227let options = LinkOptions::default();228add_to_linker_with_options(linker, &options)229}230231/// Similar to [`add_to_linker`], but with the ability to enable unstable features.232pub fn add_to_linker_with_options<T>(233linker: &mut Linker<T>,234options: &LinkOptions,235) -> wasmtime::Result<()>236where237T: WasiView + 'static,238{239cli::add_to_linker_with_options(linker, &options.into())?;240clocks::add_to_linker(linker)?;241filesystem::add_to_linker(linker)?;242random::add_to_linker(linker)?;243sockets::add_to_linker(linker)?;244Ok(())245}246247248