From 5eb62bbaf43c7314238a5a5362d798d092aafa04 Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Wed, 16 Jul 2025 14:40:17 +0200 Subject: [PATCH 01/14] feat(wasi): introduce common CLI context Signed-off-by: Roman Volosatovs --- crates/wasi/src/cli.rs | 99 +++++++++++++++++++ crates/wasi/src/ctx.rs | 50 +++++++--- crates/wasi/src/lib.rs | 1 + crates/wasi/src/p2/ctx.rs | 35 ++++--- crates/wasi/src/p2/stdio.rs | 64 +++++++----- .../wasi/src/p2/stdio/worker_thread_stdin.rs | 7 +- 6 files changed, 204 insertions(+), 52 deletions(-) create mode 100644 crates/wasi/src/cli.rs diff --git a/crates/wasi/src/cli.rs b/crates/wasi/src/cli.rs new file mode 100644 index 000000000000..2fe93fa201a7 --- /dev/null +++ b/crates/wasi/src/cli.rs @@ -0,0 +1,99 @@ +#[repr(transparent)] +pub struct WasiCliImpl(pub T); + +impl WasiCliView for &mut T { + type InputStream = T::InputStream; + type OutputStream = T::OutputStream; + + fn cli(&mut self) -> &WasiCliCtx { + (**self).cli() + } +} + +impl WasiCliView for WasiCliImpl { + type InputStream = T::InputStream; + type OutputStream = T::OutputStream; + + fn cli(&mut self) -> &WasiCliCtx { + self.0.cli() + } +} + +impl WasiCliView for WasiCliCtx { + type InputStream = I; + type OutputStream = O; + + fn cli(&mut self) -> &WasiCliCtx { + self + } +} + +pub trait WasiCliView: Send { + type InputStream; + type OutputStream; + + fn cli(&mut self) -> &WasiCliCtx; +} + +#[derive(Default)] +pub struct WasiCliCtx { + pub environment: Vec<(String, String)>, + pub arguments: Vec, + pub initial_cwd: Option, + pub stdin: I, + pub stdout: O, + pub stderr: O, +} + +pub trait IsTerminal { + /// Returns whether this stream is backed by a TTY. + fn is_terminal(&self) -> bool; +} + +impl IsTerminal for tokio::io::Empty { + fn is_terminal(&self) -> bool { + false + } +} + +impl IsTerminal for std::io::Empty { + fn is_terminal(&self) -> bool { + false + } +} + +impl IsTerminal for tokio::io::Stdin { + fn is_terminal(&self) -> bool { + std::io::stdin().is_terminal() + } +} + +impl IsTerminal for std::io::Stdin { + fn is_terminal(&self) -> bool { + std::io::IsTerminal::is_terminal(self) + } +} + +impl IsTerminal for tokio::io::Stdout { + fn is_terminal(&self) -> bool { + std::io::stdout().is_terminal() + } +} + +impl IsTerminal for std::io::Stdout { + fn is_terminal(&self) -> bool { + std::io::IsTerminal::is_terminal(self) + } +} + +impl IsTerminal for tokio::io::Stderr { + fn is_terminal(&self) -> bool { + std::io::stderr().is_terminal() + } +} + +impl IsTerminal for std::io::Stderr { + fn is_terminal(&self) -> bool { + std::io::IsTerminal::is_terminal(self) + } +} diff --git a/crates/wasi/src/ctx.rs b/crates/wasi/src/ctx.rs index 4e057996bf02..b06294e8530c 100644 --- a/crates/wasi/src/ctx.rs +++ b/crates/wasi/src/ctx.rs @@ -1,3 +1,4 @@ +use crate::cli::WasiCliCtx; use crate::clocks::{HostMonotonicClock, HostWallClock, WasiClocksCtx}; use crate::net::{SocketAddrCheck, SocketAddrUse}; use crate::random::WasiRandomCtx; @@ -16,17 +17,16 @@ use std::sync::Arc; /// [p2::WasiCtxBuilder](crate::p2::WasiCtxBuilder) /// /// [`Store`]: wasmtime::Store -pub(crate) struct WasiCtxBuilder { - pub(crate) env: Vec<(String, String)>, - pub(crate) args: Vec, +pub(crate) struct WasiCtxBuilder { pub(crate) random: WasiRandomCtx, pub(crate) clocks: WasiClocksCtx, + pub(crate) cli: WasiCliCtx, pub(crate) socket_addr_check: SocketAddrCheck, pub(crate) allowed_network_uses: AllowedNetworkUses, pub(crate) allow_blocking_current_thread: bool, } -impl WasiCtxBuilder { +impl WasiCtxBuilder { /// Creates a builder for a new context with default parameters set. /// /// The current defaults are: @@ -44,20 +44,45 @@ impl WasiCtxBuilder { /// /// These defaults can all be updated via the various builder configuration /// methods below. - pub(crate) fn new() -> Self { + pub(crate) fn new(stdin: I, stdout: O, stderr: O) -> Self { let random = WasiRandomCtx::default(); let clocks = WasiClocksCtx::default(); + let cli = WasiCliCtx { + environment: Vec::default(), + arguments: Vec::default(), + initial_cwd: None, + stdin, + stdout, + stderr, + }; Self { - env: Vec::new(), - args: Vec::new(), random, clocks, + cli, socket_addr_check: SocketAddrCheck::default(), allowed_network_uses: AllowedNetworkUses::default(), allow_blocking_current_thread: false, } } + /// Provides a custom implementation of stdin to use. + pub fn stdin(&mut self, stdin: I) -> &mut Self { + self.cli.stdin = stdin; + self + } + + /// Same as [`stdin`](WasiCtxBuilder::stdin), but for stdout. + pub fn stdout(&mut self, stdout: O) -> &mut Self { + self.cli.stdout = stdout; + self + } + + /// Same as [`stdin`](WasiCtxBuilder::stdin), but for stderr. + pub fn stderr(&mut self, stderr: O) -> &mut Self { + self.cli.stderr = stderr; + self + } + /// Configures whether or not blocking operations made through this /// `WasiCtx` are allowed to block the current thread. /// @@ -97,7 +122,7 @@ impl WasiCtxBuilder { /// At this time environment variables are not deduplicated and if the same /// key is set twice then the guest will see two entries for the same key. pub fn envs(&mut self, env: &[(impl AsRef, impl AsRef)]) -> &mut Self { - self.env.extend( + self.cli.environment.extend( env.iter() .map(|(k, v)| (k.as_ref().to_owned(), v.as_ref().to_owned())), ); @@ -109,7 +134,8 @@ impl WasiCtxBuilder { /// At this time environment variables are not deduplicated and if the same /// key is set twice then the guest will see two entries for the same key. pub fn env(&mut self, k: impl AsRef, v: impl AsRef) -> &mut Self { - self.env + self.cli + .environment .push((k.as_ref().to_owned(), v.as_ref().to_owned())); self } @@ -125,13 +151,15 @@ impl WasiCtxBuilder { /// Appends a list of arguments to the argument array to pass to wasm. pub fn args(&mut self, args: &[impl AsRef]) -> &mut Self { - self.args.extend(args.iter().map(|a| a.as_ref().to_owned())); + self.cli + .arguments + .extend(args.iter().map(|a| a.as_ref().to_owned())); self } /// Appends a single argument to get passed to wasm. pub fn arg(&mut self, arg: impl AsRef) -> &mut Self { - self.args.push(arg.as_ref().to_owned()); + self.cli.arguments.push(arg.as_ref().to_owned()); self } diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs index 0fc8e6580d3d..57ac8e4ac09f 100644 --- a/crates/wasi/src/lib.rs +++ b/crates/wasi/src/lib.rs @@ -12,6 +12,7 @@ //! //! For WASIp3, see [`p3`]. WASIp3 support is experimental, unstable and incomplete. +pub mod cli; pub mod clocks; mod ctx; mod error; diff --git a/crates/wasi/src/p2/ctx.rs b/crates/wasi/src/p2/ctx.rs index 11a12f57705a..6d49d4c44f4d 100644 --- a/crates/wasi/src/p2/ctx.rs +++ b/crates/wasi/src/p2/ctx.rs @@ -1,3 +1,4 @@ +use crate::cli::WasiCliCtx; use crate::clocks::{HostMonotonicClock, HostWallClock, WasiClocksCtx}; use crate::ctx::AllowedNetworkUses; use crate::net::{SocketAddrCheck, SocketAddrUse}; @@ -36,10 +37,7 @@ use std::pin::Pin; /// /// [`Store`]: wasmtime::Store pub struct WasiCtxBuilder { - common: crate::WasiCtxBuilder, - stdin: Box, - stdout: Box, - stderr: Box, + common: crate::WasiCtxBuilder, Box>, preopens: Vec<(Dir, String)>, built: bool, } @@ -64,10 +62,11 @@ impl WasiCtxBuilder { /// methods below. pub fn new() -> Self { Self { - common: crate::WasiCtxBuilder::new(), - stdin: Box::new(pipe::ClosedInputStream), - stdout: Box::new(pipe::SinkOutputStream), - stderr: Box::new(pipe::SinkOutputStream), + common: crate::WasiCtxBuilder::new( + Box::new(pipe::ClosedInputStream), + Box::new(pipe::SinkOutputStream), + Box::new(pipe::SinkOutputStream), + ), preopens: Vec::new(), built: false, } @@ -88,19 +87,19 @@ impl WasiCtxBuilder { /// Note that inheriting the process's stdin can also be done through /// [`inherit_stdin`](WasiCtxBuilder::inherit_stdin). pub fn stdin(&mut self, stdin: impl StdinStream + 'static) -> &mut Self { - self.stdin = Box::new(stdin); + self.common.stdin(Box::new(stdin)); self } /// Same as [`stdin`](WasiCtxBuilder::stdin), but for stdout. pub fn stdout(&mut self, stdout: impl StdoutStream + 'static) -> &mut Self { - self.stdout = Box::new(stdout); + self.common.stdout(Box::new(stdout)); self } /// Same as [`stdin`](WasiCtxBuilder::stdin), but for stderr. pub fn stderr(&mut self, stderr: impl StdoutStream + 'static) -> &mut Self { - self.stderr = Box::new(stderr); + self.common.stderr(Box::new(stderr)); self } @@ -436,8 +435,6 @@ impl WasiCtxBuilder { let Self { common: crate::WasiCtxBuilder { - env, - args, random: WasiRandomCtx { random, @@ -449,13 +446,19 @@ impl WasiCtxBuilder { wall_clock, monotonic_clock, }, + cli: + WasiCliCtx { + environment: env, + arguments: args, + initial_cwd: _, + stdin, + stdout, + stderr, + }, socket_addr_check, allowed_network_uses, allow_blocking_current_thread, }, - stdin, - stdout, - stderr, preopens, built: _, } = mem::replace(self, Self::new()); diff --git a/crates/wasi/src/p2/stdio.rs b/crates/wasi/src/p2/stdio.rs index 8ff1799abbe2..eeb72c56b775 100644 --- a/crates/wasi/src/p2/stdio.rs +++ b/crates/wasi/src/p2/stdio.rs @@ -1,3 +1,4 @@ +use crate::cli::IsTerminal; use crate::p2::bindings::cli::{ stderr, stdin, stdout, terminal_input, terminal_output, terminal_stderr, terminal_stdin, terminal_stdout, @@ -7,7 +8,6 @@ use crate::p2::{ InputStream, IoView, OutputStream, Pollable, StreamError, StreamResult, WasiImpl, WasiView, }; use bytes::Bytes; -use std::io::IsTerminal; use std::sync::Arc; use tokio::sync::Mutex; use wasmtime::component::Resource; @@ -20,7 +20,7 @@ use wasmtime_wasi_io::streams; /// /// Built-in implementations are provided for [`Stdin`], /// [`pipe::MemoryInputPipe`], and [`pipe::ClosedInputStream`]. -pub trait StdinStream: Send { +pub trait StdinStream: IsTerminal + Send { /// Creates a fresh stream which is reading stdin. /// /// Note that the returned stream must share state with all other streams @@ -33,17 +33,16 @@ pub trait StdinStream: Send { /// mean that all the others are no longer ready for reading. This is /// basically a consequence of the way the WIT APIs are designed today. fn stream(&self) -> Box; - - /// Returns whether this stream is backed by a TTY. - fn isatty(&self) -> bool; } impl StdinStream for pipe::MemoryInputPipe { fn stream(&self) -> Box { Box::new(self.clone()) } +} - fn isatty(&self) -> bool { +impl IsTerminal for pipe::MemoryInputPipe { + fn is_terminal(&self) -> bool { false } } @@ -52,8 +51,10 @@ impl StdinStream for pipe::ClosedInputStream { fn stream(&self) -> Box { Box::new(*self) } +} - fn isatty(&self) -> bool { +impl IsTerminal for pipe::ClosedInputStream { + fn is_terminal(&self) -> bool { false } } @@ -79,8 +80,10 @@ impl StdinStream for InputFile { file: Arc::clone(&self.file), }) } +} - fn isatty(&self) -> bool { +impl IsTerminal for InputFile { + fn is_terminal(&self) -> bool { false } } @@ -151,7 +154,10 @@ impl StdinStream for AsyncStdinStream { fn stream(&self) -> Box { Box::new(Self(self.0.clone())) } - fn isatty(&self) -> bool { +} + +impl IsTerminal for AsyncStdinStream { + fn is_terminal(&self) -> bool { false } } @@ -192,7 +198,7 @@ mod worker_thread_stdin; pub use self::worker_thread_stdin::{Stdin, stdin}; /// Similar to [`StdinStream`], except for output. -pub trait StdoutStream: Send { +pub trait StdoutStream: IsTerminal + Send { /// Returns a fresh new stream which can write to this output stream. /// /// Note that all output streams should output to the same logical source. @@ -206,17 +212,16 @@ pub trait StdoutStream: Send { /// /// Implementations must be able to handle this fn stream(&self) -> Box; - - /// Returns whether this stream is backed by a TTY. - fn isatty(&self) -> bool; } impl StdoutStream for pipe::MemoryOutputPipe { fn stream(&self) -> Box { Box::new(self.clone()) } +} - fn isatty(&self) -> bool { +impl IsTerminal for pipe::MemoryOutputPipe { + fn is_terminal(&self) -> bool { false } } @@ -225,8 +230,10 @@ impl StdoutStream for pipe::SinkOutputStream { fn stream(&self) -> Box { Box::new(*self) } +} - fn isatty(&self) -> bool { +impl IsTerminal for pipe::SinkOutputStream { + fn is_terminal(&self) -> bool { false } } @@ -235,8 +242,10 @@ impl StdoutStream for pipe::ClosedOutputStream { fn stream(&self) -> Box { Box::new(*self) } +} - fn isatty(&self) -> bool { +impl IsTerminal for pipe::ClosedOutputStream { + fn is_terminal(&self) -> bool { false } } @@ -262,8 +271,10 @@ impl StdoutStream for OutputFile { file: Arc::clone(&self.file), }) } +} - fn isatty(&self) -> bool { +impl IsTerminal for OutputFile { + fn is_terminal(&self) -> bool { false } } @@ -315,8 +326,10 @@ impl StdoutStream for Stdout { fn stream(&self) -> Box { Box::new(StdioOutputStream::Stdout) } +} - fn isatty(&self) -> bool { +impl IsTerminal for Stdout { + fn is_terminal(&self) -> bool { std::io::stdout().is_terminal() } } @@ -339,8 +352,10 @@ impl StdoutStream for Stderr { fn stream(&self) -> Box { Box::new(StdioOutputStream::Stderr) } +} - fn isatty(&self) -> bool { +impl IsTerminal for Stderr { + fn is_terminal(&self) -> bool { std::io::stderr().is_terminal() } } @@ -398,7 +413,10 @@ impl StdoutStream for AsyncStdoutStream { fn stream(&self) -> Box { Box::new(Self(self.0.clone())) } - fn isatty(&self) -> bool { +} + +impl IsTerminal for AsyncStdoutStream { + fn is_terminal(&self) -> bool { false } } @@ -520,7 +538,7 @@ where T: WasiView, { fn get_terminal_stdin(&mut self) -> anyhow::Result>> { - if self.ctx().stdin.isatty() { + if self.ctx().stdin.is_terminal() { let fd = self.table().push(TerminalInput)?; Ok(Some(fd)) } else { @@ -533,7 +551,7 @@ where T: WasiView, { fn get_terminal_stdout(&mut self) -> anyhow::Result>> { - if self.ctx().stdout.isatty() { + if self.ctx().stdout.is_terminal() { let fd = self.table().push(TerminalOutput)?; Ok(Some(fd)) } else { @@ -546,7 +564,7 @@ where T: WasiView, { fn get_terminal_stderr(&mut self) -> anyhow::Result>> { - if self.ctx().stderr.isatty() { + if self.ctx().stderr.is_terminal() { let fd = self.table().push(TerminalOutput)?; Ok(Some(fd)) } else { diff --git a/crates/wasi/src/p2/stdio/worker_thread_stdin.rs b/crates/wasi/src/p2/stdio/worker_thread_stdin.rs index bf7bef781b1e..7557e561d4d1 100644 --- a/crates/wasi/src/p2/stdio/worker_thread_stdin.rs +++ b/crates/wasi/src/p2/stdio/worker_thread_stdin.rs @@ -23,9 +23,10 @@ //! This module is one that's likely to change over time though as new systems //! are encountered along with preexisting bugs. +use crate::cli::IsTerminal; use crate::p2::stdio::StdinStream; use bytes::{Bytes, BytesMut}; -use std::io::{IsTerminal, Read}; +use std::io::Read; use std::mem; use std::sync::{Condvar, Mutex, OnceLock}; use tokio::sync::Notify; @@ -114,8 +115,10 @@ impl StdinStream for Stdin { fn stream(&self) -> Box { Box::new(Stdin) } +} - fn isatty(&self) -> bool { +impl IsTerminal for Stdin { + fn is_terminal(&self) -> bool { std::io::stdin().is_terminal() } } From b3d0f0615c6f999eeb3435e69c9289f7ddf7830e Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Wed, 16 Jul 2025 19:37:21 +0200 Subject: [PATCH 02/14] chore(wasi): implement traits for boxed values Signed-off-by: Roman Volosatovs --- crates/wasi/src/clocks.rs | 6 ++++++ crates/wasi/src/random.rs | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/crates/wasi/src/clocks.rs b/crates/wasi/src/clocks.rs index 2618655424a9..2d284a43e5af 100644 --- a/crates/wasi/src/clocks.rs +++ b/crates/wasi/src/clocks.rs @@ -11,6 +11,12 @@ impl WasiClocksView for &mut T { } } +impl WasiClocksView for Box { + fn clocks(&mut self) -> &WasiClocksCtx { + (**self).clocks() + } +} + impl WasiClocksView for WasiClocksImpl { fn clocks(&mut self) -> &WasiClocksCtx { self.0.clocks() diff --git a/crates/wasi/src/random.rs b/crates/wasi/src/random.rs index f3df9c6af695..09ad998c6d32 100644 --- a/crates/wasi/src/random.rs +++ b/crates/wasi/src/random.rs @@ -9,6 +9,12 @@ impl WasiRandomView for &mut T { } } +impl WasiRandomView for Box { + fn random(&mut self) -> &mut WasiRandomCtx { + (**self).random() + } +} + impl WasiRandomView for WasiRandomImpl { fn random(&mut self) -> &mut WasiRandomCtx { self.0.random() From cd2bb96f3794c25e14c9c4a784d05aebc92435fb Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Wed, 16 Jul 2025 19:37:51 +0200 Subject: [PATCH 03/14] chore(wasi): implement `Default` for common WASI builder Signed-off-by: Roman Volosatovs --- crates/wasi/src/ctx.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/wasi/src/ctx.rs b/crates/wasi/src/ctx.rs index b06294e8530c..e2cec900297e 100644 --- a/crates/wasi/src/ctx.rs +++ b/crates/wasi/src/ctx.rs @@ -17,6 +17,7 @@ use std::sync::Arc; /// [p2::WasiCtxBuilder](crate::p2::WasiCtxBuilder) /// /// [`Store`]: wasmtime::Store +#[derive(Default)] pub(crate) struct WasiCtxBuilder { pub(crate) random: WasiRandomCtx, pub(crate) clocks: WasiClocksCtx, From bd58d7061389859565a5c90efc0e4c1b95e054e0 Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Wed, 16 Jul 2025 19:38:29 +0200 Subject: [PATCH 04/14] feat(wasip3): implement `wasi:cli` Signed-off-by: Roman Volosatovs --- crates/test-programs/src/bin/p3_cli.rs | 40 ++++ crates/wasi/Cargo.toml | 1 + crates/wasi/src/cli.rs | 63 +++++- crates/wasi/src/lib.rs | 17 ++ crates/wasi/src/p3/bindings.rs | 30 ++- crates/wasi/src/p3/cli/host.rs | 251 +++++++++++++++++++++++ crates/wasi/src/p3/cli/mod.rs | 263 +++++++++++++++++++++++++ crates/wasi/src/p3/clocks/mod.rs | 5 +- crates/wasi/src/p3/ctx.rs | 68 ++++--- crates/wasi/src/p3/mod.rs | 13 +- crates/wasi/src/p3/random/mod.rs | 5 +- crates/wasi/src/p3/view.rs | 41 +++- crates/wasi/tests/all/p3/mod.rs | 34 +++- 13 files changed, 776 insertions(+), 55 deletions(-) create mode 100644 crates/test-programs/src/bin/p3_cli.rs create mode 100644 crates/wasi/src/p3/cli/host.rs create mode 100644 crates/wasi/src/p3/cli/mod.rs diff --git a/crates/test-programs/src/bin/p3_cli.rs b/crates/test-programs/src/bin/p3_cli.rs new file mode 100644 index 000000000000..a91844c8afea --- /dev/null +++ b/crates/test-programs/src/bin/p3_cli.rs @@ -0,0 +1,40 @@ +use test_programs::p3::wasi::cli::{ + environment, stderr, stdin, stdout, terminal_stderr, terminal_stdin, terminal_stdout, +}; +use test_programs::p3::wit_stream; +use wit_bindgen::StreamResult; + +struct Component; + +test_programs::p3::export!(Component); + +impl test_programs::p3::exports::wasi::cli::run::Guest for Component { + async fn run() -> Result<(), ()> { + assert_eq!(environment::get_arguments(), ["p3_cli.component", "."]); + assert_eq!(environment::get_environment().len(), 1); + assert_eq!(environment::initial_cwd(), None); + + assert!(terminal_stdin::get_terminal_stdin().is_none()); + assert!(terminal_stdout::get_terminal_stdout().is_none()); + assert!(terminal_stderr::get_terminal_stderr().is_none()); + + let mut stdin = stdin::get_stdin(); + assert!(stdin.next().await.is_none()); + + let (mut stdout_tx, stdout_rx) = wit_stream::new(); + stdout::set_stdout(stdout_rx); + let (res, buf) = stdout_tx.write(b"hello stdout\n".into()).await; + assert_eq!(res, StreamResult::Complete(13)); + assert_eq!(buf.into_vec(), []); + + let (mut stderr_tx, stderr_rx) = wit_stream::new(); + stderr::set_stderr(stderr_rx); + let (res, buf) = stderr_tx.write(b"hello stderr\n".into()).await; + assert_eq!(res, StreamResult::Complete(13)); + assert_eq!(buf.into_vec(), []); + + Ok(()) + } +} + +fn main() {} diff --git a/crates/wasi/Cargo.toml b/crates/wasi/Cargo.toml index 0e7bd464644e..a225646dc138 100644 --- a/crates/wasi/Cargo.toml +++ b/crates/wasi/Cargo.toml @@ -61,6 +61,7 @@ preview1 = [ ] p3 = [ "wasmtime/component-model-async", + "wasmtime/component-model-async-bytes", ] [[test]] diff --git a/crates/wasi/src/cli.rs b/crates/wasi/src/cli.rs index 2fe93fa201a7..5c058fadf5ce 100644 --- a/crates/wasi/src/cli.rs +++ b/crates/wasi/src/cli.rs @@ -1,3 +1,10 @@ +use std::rc::Rc; +use std::sync::Arc; + +use wasmtime::component::ResourceTable; + +use crate::ResourceView; + #[repr(transparent)] pub struct WasiCliImpl(pub T); @@ -10,27 +17,33 @@ impl WasiCliView for &mut T { } } -impl WasiCliView for WasiCliImpl { +impl WasiCliView for Box { type InputStream = T::InputStream; type OutputStream = T::OutputStream; fn cli(&mut self) -> &WasiCliCtx { - self.0.cli() + (**self).cli() } } -impl WasiCliView for WasiCliCtx { - type InputStream = I; - type OutputStream = O; +impl ResourceView for WasiCliImpl { + fn table(&mut self) -> &mut ResourceTable { + self.0.table() + } +} - fn cli(&mut self) -> &WasiCliCtx { - self +impl WasiCliView for WasiCliImpl { + type InputStream = T::InputStream; + type OutputStream = T::OutputStream; + + fn cli(&mut self) -> &WasiCliCtx { + self.0.cli() } } -pub trait WasiCliView: Send { - type InputStream; - type OutputStream; +pub trait WasiCliView: ResourceView + Send { + type InputStream: IsTerminal; + type OutputStream: IsTerminal; fn cli(&mut self) -> &WasiCliCtx; } @@ -50,6 +63,36 @@ pub trait IsTerminal { fn is_terminal(&self) -> bool; } +impl IsTerminal for &T { + fn is_terminal(&self) -> bool { + (**self).is_terminal() + } +} + +impl IsTerminal for &mut T { + fn is_terminal(&self) -> bool { + (**self).is_terminal() + } +} + +impl IsTerminal for Box { + fn is_terminal(&self) -> bool { + (**self).is_terminal() + } +} + +impl IsTerminal for Rc { + fn is_terminal(&self) -> bool { + (**self).is_terminal() + } +} + +impl IsTerminal for Arc { + fn is_terminal(&self) -> bool { + (**self).is_terminal() + } +} + impl IsTerminal for tokio::io::Empty { fn is_terminal(&self) -> bool { false diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs index 57ac8e4ac09f..ad8909d39d7c 100644 --- a/crates/wasi/src/lib.rs +++ b/crates/wasi/src/lib.rs @@ -42,3 +42,20 @@ pub use cap_fs_ext::SystemTimeSpec; pub use cap_rand::RngCore; #[doc(no_inline)] pub use wasmtime::component::{ResourceTable, ResourceTableError}; + +// TODO: Consolidate with `IoView` +pub trait ResourceView { + fn table(&mut self) -> &mut ResourceTable; +} + +impl ResourceView for &mut T { + fn table(&mut self) -> &mut ResourceTable { + (**self).table() + } +} + +impl ResourceView for Box { + fn table(&mut self) -> &mut ResourceTable { + (**self).table() + } +} diff --git a/crates/wasi/src/p3/bindings.rs b/crates/wasi/src/p3/bindings.rs index 4fb17e8ae15f..f435c7c762e3 100644 --- a/crates/wasi/src/p3/bindings.rs +++ b/crates/wasi/src/p3/bindings.rs @@ -12,9 +12,10 @@ //! done using the `with` option to [`bindgen!`]: //! //! ```rust +//! use wasmtime_wasi::ResourceView; //! use wasmtime_wasi::p3::{WasiCtx, WasiView}; //! use wasmtime::{Result, Engine, Config}; -//! use wasmtime::component::{Linker, HasSelf}; +//! use wasmtime::component::{Linker, HasSelf, ResourceTable}; //! //! wasmtime::component::bindgen!({ //! inline: " @@ -86,6 +87,7 @@ //! //! struct MyState { //! ctx: WasiCtx, +//! table: ResourceTable, //! } //! //! impl example::wasi::custom_host::Host for MyState { @@ -94,6 +96,10 @@ //! } //! } //! +//! impl ResourceView for MyState { +//! fn table(&mut self) -> &mut ResourceTable { &mut self.table } +//! } +//! //! impl WasiView for MyState { //! fn ctx(&mut self) -> &mut WasiCtx { &mut self.ctx } //! } @@ -165,6 +171,10 @@ mod generated { "wasi:sockets/types@0.3.0#[method]udp-socket.connect", ], }, + with: { + "wasi:cli/terminal-input/terminal-input": crate::p3::cli::TerminalInput, + "wasi:cli/terminal-output/terminal-output": crate::p3::cli::TerminalOutput, + } }); } pub use self::generated::LinkOptions; @@ -183,7 +193,8 @@ pub use self::generated::wasi::*; /// /// ```no_run /// use wasmtime::{Engine, Result, Store, Config}; -/// use wasmtime::component::{Component, Linker}; +/// use wasmtime::component::{Component, Linker, ResourceTable}; +/// use wasmtime_wasi::ResourceView; /// use wasmtime_wasi::p3::{WasiCtx, WasiView, WasiCtxBuilder}; /// use wasmtime_wasi::p3::bindings::Command; /// @@ -214,6 +225,7 @@ pub use self::generated::wasi::*; /// &engine, /// MyState { /// ctx: builder.build(), +/// table: ResourceTable::default(), /// }, /// ); /// @@ -231,6 +243,11 @@ pub use self::generated::wasi::*; /// /// struct MyState { /// ctx: WasiCtx, +/// table: ResourceTable, +/// } +/// +/// impl ResourceView for MyState { +/// fn table(&mut self) -> &mut ResourceTable { &mut self.table } /// } /// /// impl WasiView for MyState { @@ -250,7 +267,8 @@ pub use self::generated::Command; /// /// ```no_run /// use wasmtime::{Engine, Result, Store, Config}; -/// use wasmtime::component::{Linker, Component}; +/// use wasmtime::component::{Linker, Component, ResourceTable}; +/// use wasmtime_wasi::ResourceView; /// use wasmtime_wasi::p3::{WasiCtx, WasiView, WasiCtxBuilder}; /// use wasmtime_wasi::p3::bindings::CommandPre; /// @@ -282,6 +300,7 @@ pub use self::generated::Command; /// &engine, /// MyState { /// ctx: builder.build(), +/// table: ResourceTable::default(), /// }, /// ); /// @@ -299,6 +318,11 @@ pub use self::generated::Command; /// /// struct MyState { /// ctx: WasiCtx, +/// table: ResourceTable, +/// } +/// +/// impl ResourceView for MyState { +/// fn table(&mut self) -> &mut ResourceTable { &mut self.table } /// } /// /// impl WasiView for MyState { diff --git a/crates/wasi/src/p3/cli/host.rs b/crates/wasi/src/p3/cli/host.rs new file mode 100644 index 000000000000..d93e1e13794b --- /dev/null +++ b/crates/wasi/src/p3/cli/host.rs @@ -0,0 +1,251 @@ +use crate::cli::{IsTerminal, WasiCliImpl, WasiCliView}; +use crate::p3::bindings::cli::{ + environment, exit, stderr, stdin, stdout, terminal_input, terminal_output, terminal_stderr, + terminal_stdin, terminal_stdout, +}; +use crate::p3::cli::{InputStream, OutputStream, TerminalInput, TerminalOutput, WasiCli}; +use crate::{I32Exit, ResourceView as _}; +use anyhow::{Context as _, anyhow}; +use bytes::BytesMut; +use std::io::Cursor; +use tokio::io::{AsyncRead, AsyncReadExt as _, AsyncWrite, AsyncWriteExt as _}; +use wasmtime::component::{ + Accessor, AccessorTask, HostStream, Resource, StreamReader, StreamWriter, +}; + +struct InputTask { + rx: T, + tx: StreamWriter>, +} + +impl AccessorTask, wasmtime::Result<()>> for InputTask +where + U: 'static, + V: AsyncRead + Send + Sync + Unpin + 'static, +{ + async fn run(mut self, store: &Accessor>) -> wasmtime::Result<()> { + let mut buf = BytesMut::with_capacity(8192); + let mut tx = self.tx; + loop { + match self.rx.read_buf(&mut buf).await { + Ok(0) => return Ok(()), + Ok(_) => { + let (Some(tx_next), buf_next) = tx.write_all(store, Cursor::new(buf)).await + else { + break Ok(()); + }; + tx = tx_next; + buf = buf_next.into_inner(); + buf.clear(); + } + Err(_err) => { + // TODO: Report the error to the guest + return Ok(()); + } + } + } + } +} + +struct OutputTask { + rx: StreamReader, + tx: T, +} + +impl AccessorTask, wasmtime::Result<()>> for OutputTask +where + U: 'static, + V: AsyncWrite + Send + Sync + Unpin + 'static, +{ + async fn run(mut self, store: &Accessor>) -> wasmtime::Result<()> { + let mut buf = BytesMut::with_capacity(8192); + let mut rx = self.rx; + while let (Some(rx_next), buf_next) = rx.read(store, buf).await { + buf = buf_next; + rx = rx_next; + match self.tx.write_all(&buf).await { + Ok(()) => { + buf.clear(); + continue; + } + Err(_err) => { + // TODO: Report the error to the guest + return Ok(()); + } + } + } + Ok(()) + } +} + +impl terminal_input::Host for WasiCliImpl where T: WasiCliView {} +impl terminal_output::Host for WasiCliImpl where T: WasiCliView {} + +impl terminal_input::HostTerminalInput for WasiCliImpl +where + T: WasiCliView, +{ + fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + self.table() + .delete(rep) + .context("failed to delete terminal input resource from table")?; + Ok(()) + } +} + +impl terminal_output::HostTerminalOutput for WasiCliImpl +where + T: WasiCliView, +{ + fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + self.table() + .delete(rep) + .context("failed to delete terminal output resource from table")?; + Ok(()) + } +} + +impl terminal_stdin::Host for WasiCliImpl +where + T: WasiCliView, +{ + fn get_terminal_stdin(&mut self) -> wasmtime::Result>> { + if self.cli().stdin.is_terminal() { + let fd = self + .table() + .push(TerminalInput) + .context("failed to push terminal stdin resource to table")?; + Ok(Some(fd)) + } else { + Ok(None) + } + } +} + +impl terminal_stdout::Host for WasiCliImpl +where + T: WasiCliView, +{ + fn get_terminal_stdout(&mut self) -> wasmtime::Result>> { + if self.cli().stdout.is_terminal() { + let fd = self + .table() + .push(TerminalOutput) + .context("failed to push terminal stdout resource to table")?; + Ok(Some(fd)) + } else { + Ok(None) + } + } +} + +impl terminal_stderr::Host for WasiCliImpl +where + T: WasiCliView, +{ + fn get_terminal_stderr(&mut self) -> wasmtime::Result>> { + if self.cli().stderr.is_terminal() { + let fd = self + .table() + .push(TerminalOutput) + .context("failed to push terminal stderr resource to table")?; + Ok(Some(fd)) + } else { + Ok(None) + } + } +} + +impl stdin::HostConcurrent for WasiCli +where + T: WasiCliView + 'static, + T::InputStream: InputStream, +{ + async fn get_stdin(store: &Accessor) -> wasmtime::Result> { + store.with(|mut view| { + let instance = view.instance(); + let (tx, rx) = instance + .stream::<_, _, BytesMut>(&mut view) + .context("failed to create stream")?; + let stdin = view.get().cli().stdin.reader(); + view.spawn(InputTask { rx: stdin, tx }); + Ok(rx.into()) + }) + } +} + +impl stdin::Host for WasiCliImpl where T: WasiCliView {} + +impl stdout::HostConcurrent for WasiCli +where + T: WasiCliView + 'static, + T::OutputStream: OutputStream, +{ + async fn set_stdout( + store: &Accessor, + data: HostStream, + ) -> wasmtime::Result<()> { + store.with(|mut view| { + let stdout = data.into_reader(&mut view); + let tx = view.get().cli().stdout.writer(); + view.spawn(OutputTask { rx: stdout, tx }); + Ok(()) + }) + } +} + +impl stdout::Host for WasiCliImpl where T: WasiCliView {} + +impl stderr::HostConcurrent for WasiCli +where + T: WasiCliView + 'static, + T::OutputStream: OutputStream, +{ + async fn set_stderr( + store: &Accessor, + data: HostStream, + ) -> wasmtime::Result<()> { + store.with(|mut view| { + let stderr = data.into_reader(&mut view); + let tx = view.get().cli().stderr.writer(); + view.spawn(OutputTask { rx: stderr, tx }); + Ok(()) + }) + } +} + +impl stderr::Host for WasiCliImpl where T: WasiCliView {} + +impl environment::Host for WasiCliImpl +where + T: WasiCliView, +{ + fn get_environment(&mut self) -> wasmtime::Result> { + Ok(self.cli().environment.clone()) + } + + fn get_arguments(&mut self) -> wasmtime::Result> { + Ok(self.cli().arguments.clone()) + } + + fn initial_cwd(&mut self) -> wasmtime::Result> { + Ok(self.cli().initial_cwd.clone()) + } +} + +impl exit::Host for WasiCliImpl +where + T: WasiCliView, +{ + fn exit(&mut self, status: Result<(), ()>) -> wasmtime::Result<()> { + let status = match status { + Ok(()) => 0, + Err(()) => 1, + }; + Err(anyhow!(I32Exit(status))) + } + + fn exit_with_code(&mut self, status_code: u8) -> wasmtime::Result<()> { + Err(anyhow!(I32Exit(status_code.into()))) + } +} diff --git a/crates/wasi/src/p3/cli/mod.rs b/crates/wasi/src/p3/cli/mod.rs new file mode 100644 index 000000000000..0da7deb07a17 --- /dev/null +++ b/crates/wasi/src/p3/cli/mod.rs @@ -0,0 +1,263 @@ +mod host; + +use crate::cli::{IsTerminal, WasiCliCtx, WasiCliImpl, WasiCliView}; +use crate::p3::bindings::cli; +use std::rc::Rc; +use std::sync::Arc; +use tokio::io::{ + AsyncRead, AsyncWrite, Empty, Stderr, Stdin, Stdout, empty, stderr, stdin, stdout, +}; +use wasmtime::component::{HasData, Linker}; + +impl Default for WasiCliCtx, Box> { + fn default() -> Self { + Self { + environment: Vec::default(), + arguments: Vec::default(), + initial_cwd: None, + stdin: Box::new(empty()), + stdout: Box::new(empty()), + stderr: Box::new(empty()), + } + } +} + +/// Add all WASI interfaces from this module into the `linker` provided. +/// +/// This function will add all interfaces implemented by this module to the +/// [`Linker`], which corresponds to the `wasi:cli/imports` world supported by +/// this module. +/// +/// This is low-level API for advanced use cases, +/// [`wasmtime_wasi::p3::add_to_linker`](crate::p3::add_to_linker) can be used instead +/// to add *all* wasip3 interfaces (including the ones from this module) to the `linker`. +/// +/// # Example +/// +/// ``` +/// use wasmtime::{Engine, Result, Store, Config}; +/// use wasmtime::component::{Linker, ResourceTable}; +/// use wasmtime_wasi::ResourceView; +/// use wasmtime_wasi::cli::{WasiCliView, WasiCliCtx}; +/// use wasmtime_wasi::p3::cli::{InputStream, OutputStream}; +/// +/// fn main() -> Result<()> { +/// let mut config = Config::new(); +/// config.async_support(true); +/// config.wasm_component_model_async(true); +/// let engine = Engine::new(&config)?; +/// +/// let mut linker = Linker::::new(&engine); +/// wasmtime_wasi::p3::cli::add_to_linker(&mut linker)?; +/// // ... add any further functionality to `linker` if desired ... +/// +/// let mut store = Store::new( +/// &engine, +/// MyState::default(), +/// ); +/// +/// // ... use `linker` to instantiate within `store` ... +/// +/// Ok(()) +/// } +/// +/// #[derive(Default)] +/// struct MyState { +/// cli: WasiCliCtx, Box>, +/// table: ResourceTable, +/// } +/// +/// impl ResourceView for MyState { +/// fn table(&mut self) -> &mut ResourceTable { &mut self.table } +/// } +/// +/// impl WasiCliView for MyState { +/// type InputStream = Box; +/// type OutputStream = Box; +/// +/// fn cli(&mut self) -> &WasiCliCtx { &self.cli } +/// } +/// ``` +pub fn add_to_linker(linker: &mut Linker) -> wasmtime::Result<()> +where + T: WasiCliView + 'static, + T::InputStream: InputStream, + T::OutputStream: OutputStream, +{ + let exit_options = cli::exit::LinkOptions::default(); + add_to_linker_impl(linker, &exit_options, |x| WasiCliImpl(x)) +} + +/// Similar to [`add_to_linker`], but with the ability to enable unstable features. +pub fn add_to_linker_with_options( + linker: &mut Linker, + exit_options: &cli::exit::LinkOptions, +) -> anyhow::Result<()> +where + T: WasiCliView + 'static, + T::InputStream: InputStream, + T::OutputStream: OutputStream, +{ + add_to_linker_impl(linker, exit_options, |x| WasiCliImpl(x)) +} + +pub(crate) fn add_to_linker_impl( + linker: &mut Linker, + exit_options: &cli::exit::LinkOptions, + host_getter: fn(&mut T) -> WasiCliImpl<&mut U>, +) -> wasmtime::Result<()> +where + T: Send, + U: WasiCliView + 'static, + U::InputStream: InputStream, + U::OutputStream: OutputStream, +{ + cli::exit::add_to_linker::<_, WasiCli>(linker, exit_options, host_getter)?; + cli::environment::add_to_linker::<_, WasiCli>(linker, host_getter)?; + cli::stdin::add_to_linker::<_, WasiCli>(linker, host_getter)?; + cli::stdout::add_to_linker::<_, WasiCli>(linker, host_getter)?; + cli::stderr::add_to_linker::<_, WasiCli>(linker, host_getter)?; + cli::terminal_input::add_to_linker::<_, WasiCli>(linker, host_getter)?; + cli::terminal_output::add_to_linker::<_, WasiCli>(linker, host_getter)?; + cli::terminal_stdin::add_to_linker::<_, WasiCli>(linker, host_getter)?; + cli::terminal_stdout::add_to_linker::<_, WasiCli>(linker, host_getter)?; + cli::terminal_stderr::add_to_linker::<_, WasiCli>(linker, host_getter)?; + Ok(()) +} + +struct WasiCli(T); + +impl HasData for WasiCli { + type Data<'a> = WasiCliImpl<&'a mut T>; +} + +pub struct TerminalInput; +pub struct TerminalOutput; + +pub trait InputStream: IsTerminal { + fn reader(&self) -> Box; +} + +impl InputStream for &T { + fn reader(&self) -> Box { + (**self).reader() + } +} + +impl InputStream for &mut T { + fn reader(&self) -> Box { + (**self).reader() + } +} + +impl InputStream for Box { + fn reader(&self) -> Box { + (**self).reader() + } +} + +impl InputStream for Rc { + fn reader(&self) -> Box { + (**self).reader() + } +} + +impl InputStream for Arc { + fn reader(&self) -> Box { + (**self).reader() + } +} + +impl InputStream for Empty { + fn reader(&self) -> Box { + Box::new(empty()) + } +} + +impl InputStream for std::io::Empty { + fn reader(&self) -> Box { + Box::new(empty()) + } +} + +impl InputStream for Stdin { + fn reader(&self) -> Box { + Box::new(stdin()) + } +} + +impl InputStream for std::io::Stdin { + fn reader(&self) -> Box { + Box::new(stdin()) + } +} + +pub trait OutputStream: IsTerminal { + fn writer(&self) -> Box; +} + +impl OutputStream for &T { + fn writer(&self) -> Box { + (**self).writer() + } +} + +impl OutputStream for &mut T { + fn writer(&self) -> Box { + (**self).writer() + } +} + +impl OutputStream for Box { + fn writer(&self) -> Box { + (**self).writer() + } +} + +impl OutputStream for Rc { + fn writer(&self) -> Box { + (**self).writer() + } +} + +impl OutputStream for Arc { + fn writer(&self) -> Box { + (**self).writer() + } +} + +impl OutputStream for Empty { + fn writer(&self) -> Box { + Box::new(empty()) + } +} + +impl OutputStream for std::io::Empty { + fn writer(&self) -> Box { + Box::new(empty()) + } +} + +impl OutputStream for Stdout { + fn writer(&self) -> Box { + Box::new(stdout()) + } +} + +impl OutputStream for std::io::Stdout { + fn writer(&self) -> Box { + Box::new(stdout()) + } +} + +impl OutputStream for Stderr { + fn writer(&self) -> Box { + Box::new(stderr()) + } +} + +impl OutputStream for std::io::Stderr { + fn writer(&self) -> Box { + Box::new(stderr()) + } +} diff --git a/crates/wasi/src/p3/clocks/mod.rs b/crates/wasi/src/p3/clocks/mod.rs index 6d6167a5508b..66b185ae9562 100644 --- a/crates/wasi/src/p3/clocks/mod.rs +++ b/crates/wasi/src/p3/clocks/mod.rs @@ -51,7 +51,10 @@ use wasmtime::component::{HasData, Linker}; /// fn clocks(&mut self) -> &WasiClocksCtx { &self.clocks } /// } /// ``` -pub fn add_to_linker(linker: &mut Linker) -> wasmtime::Result<()> { +pub fn add_to_linker(linker: &mut Linker) -> wasmtime::Result<()> +where + T: WasiClocksView + 'static, +{ add_to_linker_impl(linker, |x| WasiClocksImpl(x)) } diff --git a/crates/wasi/src/p3/ctx.rs b/crates/wasi/src/p3/ctx.rs index 8e10e394fdfb..de7845ef5b7d 100644 --- a/crates/wasi/src/p3/ctx.rs +++ b/crates/wasi/src/p3/ctx.rs @@ -1,5 +1,7 @@ +use crate::cli::WasiCliCtx; use crate::clocks::{HostMonotonicClock, HostWallClock, WasiClocksCtx}; use crate::net::SocketAddrUse; +use crate::p3::cli::{InputStream, OutputStream}; use crate::p3::filesystem::Dir; use crate::random::WasiRandomCtx; use crate::{DirPerms, FilePerms, OpenMode}; @@ -11,6 +13,7 @@ use std::mem; use std::net::SocketAddr; use std::path::Path; use std::pin::Pin; +use tokio::io::{empty, stderr, stdin, stdout}; /// Builder-style structure used to create a [`WasiCtx`]. /// @@ -33,15 +36,18 @@ use std::pin::Pin; /// /// [`Store`]: wasmtime::Store pub struct WasiCtxBuilder { - common: crate::WasiCtxBuilder, - // TODO: implement CLI and filesystem - stdin: Box<()>, - stdout: Box<()>, - stderr: Box<()>, + common: crate::WasiCtxBuilder, Box>, + // TODO: implement filesystem preopens: Vec<(Dir, String)>, built: bool, } +impl Default for crate::WasiCtxBuilder, Box> { + fn default() -> Self { + crate::WasiCtxBuilder::new(Box::new(empty()), Box::new(empty()), Box::new(empty())) + } +} + impl WasiCtxBuilder { /// Creates a builder for a new context with default parameters set. /// @@ -62,10 +68,7 @@ impl WasiCtxBuilder { /// methods below. pub fn new() -> Self { Self { - common: crate::WasiCtxBuilder::new(), - stdin: Box::default(), - stdout: Box::default(), - stderr: Box::default(), + common: crate::WasiCtxBuilder::default(), preopens: Vec::default(), built: false, } @@ -77,23 +80,20 @@ impl WasiCtxBuilder { /// /// Note that inheriting the process's stdin can also be done through /// [`inherit_stdin`](WasiCtxBuilder::inherit_stdin). - // TODO: implement - pub fn stdin(&mut self, stdin: ()) -> &mut Self { - self.stdin = Box::new(stdin); + pub fn stdin(&mut self, stdin: impl InputStream + Send + 'static) -> &mut Self { + self.common.stdin(Box::new(stdin)); self } /// Same as [`stdin`](WasiCtxBuilder::stdin), but for stdout. - // TODO: implement - pub fn stdout(&mut self, stdout: ()) -> &mut Self { - self.stdout = Box::new(stdout); + pub fn stdout(&mut self, stdout: impl OutputStream + Send + 'static) -> &mut Self { + self.common.stdout(Box::new(stdout)); self } /// Same as [`stdin`](WasiCtxBuilder::stdin), but for stderr. - // TODO: implement - pub fn stderr(&mut self, stderr: ()) -> &mut Self { - self.stderr = Box::new(stderr); + pub fn stderr(&mut self, stderr: impl OutputStream + Send + 'static) -> &mut Self { + self.common.stderr(Box::new(stderr)); self } @@ -104,8 +104,7 @@ impl WasiCtxBuilder { /// when using this it's typically best to have a single wasm instance in /// the process using this. pub fn inherit_stdin(&mut self) -> &mut Self { - // TODO: implement - self.stdin(()) + self.stdin(stdin()) } /// Configures this context's stdout stream to write to the host process's @@ -114,8 +113,7 @@ impl WasiCtxBuilder { /// Note that unlike [`inherit_stdin`](WasiCtxBuilder::inherit_stdin) /// multiple instances printing to stdout works well. pub fn inherit_stdout(&mut self) -> &mut Self { - // TODO: implement - self.stdout(()) + self.stdout(stdout()) } /// Configures this context's stderr stream to write to the host process's @@ -124,8 +122,7 @@ impl WasiCtxBuilder { /// Note that unlike [`inherit_stdin`](WasiCtxBuilder::inherit_stdin) /// multiple instances printing to stderr works well. pub fn inherit_stderr(&mut self) -> &mut Self { - // TODO: implement - self.stderr(()) + self.stderr(stderr()) } /// Configures all of stdin, stdout, and stderr to be inherited from the @@ -430,13 +427,23 @@ impl WasiCtxBuilder { assert!(!self.built); let Self { - common: crate::WasiCtxBuilder { random, clocks, .. }, + common: + crate::WasiCtxBuilder { + random, + clocks, + cli, + .. + }, built: _, .. } = mem::replace(self, Self::new()); self.built = true; - WasiCtx { random, clocks } + WasiCtx { + random, + clocks, + cli, + } } } @@ -456,10 +463,17 @@ impl WasiCtxBuilder { /// # Example /// /// ``` +/// use wasmtime::component::ResourceTable; +/// use wasmtime_wasi::ResourceView; /// use wasmtime_wasi::p3::{WasiCtx, WasiView, WasiCtxBuilder}; /// /// struct MyState { /// ctx: WasiCtx, +/// table: ResourceTable, +/// } +/// +/// impl ResourceView for MyState { +/// fn table(&mut self) -> &mut ResourceTable { &mut self.table } /// } /// /// impl WasiView for MyState { @@ -475,6 +489,7 @@ impl WasiCtxBuilder { /// /// MyState { /// ctx: wasi.build(), +/// table: ResourceTable::default(), /// } /// } /// } @@ -483,6 +498,7 @@ impl WasiCtxBuilder { pub struct WasiCtx { pub random: WasiRandomCtx, pub clocks: WasiClocksCtx, + pub cli: WasiCliCtx, Box>, } impl WasiCtx { diff --git a/crates/wasi/src/p3/mod.rs b/crates/wasi/src/p3/mod.rs index 35d84d979277..b075b898cded 100644 --- a/crates/wasi/src/p3/mod.rs +++ b/crates/wasi/src/p3/mod.rs @@ -9,6 +9,7 @@ //! Documentation of this module may be incorrect or out-of-sync with the implementation. pub mod bindings; +pub mod cli; pub mod clocks; mod ctx; pub mod filesystem; @@ -17,6 +18,7 @@ mod view; use wasmtime::component::Linker; +use crate::cli::WasiCliImpl; use crate::clocks::WasiClocksImpl; use crate::p3::bindings::LinkOptions; use crate::random::WasiRandomImpl; @@ -34,7 +36,8 @@ pub use self::view::{WasiImpl, WasiView}; /// /// ``` /// use wasmtime::{Engine, Result, Store, Config}; -/// use wasmtime::component::Linker; +/// use wasmtime::component::{ Linker, ResourceTable}; +/// use wasmtime_wasi::ResourceView; /// use wasmtime_wasi::p3::{WasiCtx, WasiView}; /// /// fn main() -> Result<()> { @@ -60,6 +63,11 @@ pub use self::view::{WasiImpl, WasiView}; /// #[derive(Default)] /// struct MyState { /// ctx: WasiCtx, +/// table: ResourceTable, +/// } +/// +/// impl ResourceView for MyState { +/// fn table(&mut self) -> &mut ResourceTable { &mut self.table } /// } /// /// impl WasiView for MyState { @@ -82,9 +90,8 @@ pub fn add_to_linker_with_options( where T: WasiView + 'static, { - // TODO: use options - _ = options; clocks::add_to_linker_impl(linker, |x| WasiClocksImpl(&mut x.ctx().clocks))?; random::add_to_linker_impl(linker, |x| WasiRandomImpl(&mut x.ctx().random))?; + cli::add_to_linker_impl(linker, &options.into(), |x| WasiCliImpl(x.as_wasi_impl()))?; Ok(()) } diff --git a/crates/wasi/src/p3/random/mod.rs b/crates/wasi/src/p3/random/mod.rs index 8f41008e2352..9a9b2e68ff14 100644 --- a/crates/wasi/src/p3/random/mod.rs +++ b/crates/wasi/src/p3/random/mod.rs @@ -51,7 +51,10 @@ use wasmtime::component::{HasData, Linker}; /// fn random(&mut self) -> &mut WasiRandomCtx { &mut self.random } /// } /// ``` -pub fn add_to_linker(linker: &mut Linker) -> wasmtime::Result<()> { +pub fn add_to_linker(linker: &mut Linker) -> wasmtime::Result<()> +where + T: WasiRandomView + 'static, +{ add_to_linker_impl(linker, |x| WasiRandomImpl(x)) } diff --git a/crates/wasi/src/p3/view.rs b/crates/wasi/src/p3/view.rs index c455eae7dd77..203eae8b831b 100644 --- a/crates/wasi/src/p3/view.rs +++ b/crates/wasi/src/p3/view.rs @@ -1,3 +1,8 @@ +use wasmtime::component::ResourceTable; + +use crate::ResourceView; +use crate::cli::{WasiCliCtx, WasiCliView}; +use crate::p3::cli::{InputStream, OutputStream}; use crate::p3::ctx::WasiCtx; /// A trait which provides access to the [`WasiCtx`] inside the embedder's `T` @@ -12,10 +17,17 @@ use crate::p3::ctx::WasiCtx; /// # Example /// /// ``` -/// use wasmtime_wasi::p3::{WasiCtx, WasiView, WasiCtxBuilder}; +/// use wasmtime_wasi::ResourceView; +/// use wasmtime_wasi::p3::{WasiCtx, WasiCtxBuilder, WasiView}; +/// use wasmtime::component::ResourceTable; /// /// struct MyState { /// ctx: WasiCtx, +/// table: ResourceTable, +/// } +/// +/// impl ResourceView for MyState { +/// fn table(&mut self) -> &mut ResourceTable { &mut self.table } /// } /// /// impl WasiView for MyState { @@ -25,10 +37,20 @@ use crate::p3::ctx::WasiCtx; /// [`Store`]: wasmtime::Store /// [`Linker`]: wasmtime::component::Linker /// -pub trait WasiView: Send { +pub trait WasiView: ResourceView + Send { /// Yields mutable access to the [`WasiCtx`] configuration used for this /// context. fn ctx(&mut self) -> &mut WasiCtx; + + #[doc(hidden)] + fn as_wasi_impl(&mut self) -> &mut WasiImpl + where + Self: Sized, + { + // TODO: Figure out how to avoid `unsafe` + // SAFETY: `WasiImpl` is `repr(transparent)` + unsafe { core::mem::transmute(self) } + } } impl WasiView for &mut T { @@ -57,8 +79,23 @@ impl WasiView for Box { #[repr(transparent)] pub struct WasiImpl(pub T); +impl ResourceView for WasiImpl { + fn table(&mut self) -> &mut ResourceTable { + T::table(&mut self.0) + } +} + impl WasiView for WasiImpl { fn ctx(&mut self) -> &mut WasiCtx { T::ctx(&mut self.0) } } + +impl WasiCliView for WasiImpl { + type InputStream = Box; + type OutputStream = Box; + + fn cli(&mut self) -> &WasiCliCtx { + &T::ctx(&mut self.0).cli + } +} diff --git a/crates/wasi/tests/all/p3/mod.rs b/crates/wasi/tests/all/p3/mod.rs index 891ee9f8d631..f3252270613a 100644 --- a/crates/wasi/tests/all/p3/mod.rs +++ b/crates/wasi/tests/all/p3/mod.rs @@ -5,7 +5,7 @@ use wasmtime::Store; use wasmtime::component::{Component, Linker, ResourceTable}; use wasmtime_wasi::p3::bindings::Command; use wasmtime_wasi::p3::{WasiCtx, WasiCtxBuilder, WasiView}; -use wasmtime_wasi::{DirPerms, FilePerms}; +use wasmtime_wasi::{DirPerms, FilePerms, ResourceView}; use test_programs_artifacts::*; @@ -28,6 +28,12 @@ impl WasiView for Ctx { } } +impl ResourceView for Ctx { + fn table(&mut self) -> &mut ResourceTable { + &mut self.table + } +} + // TODO: Remove once test components are not built for `wasm32-wasip1` impl wasmtime_wasi::p2::WasiView for Ctx { fn ctx(&mut self) -> &mut wasmtime_wasi::p2::WasiCtx { @@ -56,23 +62,28 @@ async fn run(path: &str) -> anyhow::Result<()> { .context("failed to link `wasi:cli@0.2.x`")?; wasmtime_wasi::p3::add_to_linker(&mut linker).context("failed to link `wasi:cli@0.3.x`")?; - let mut builder = WasiCtxBuilder::new(); + let table = ResourceTable::default(); + + let p2 = wasmtime_wasi::p2::WasiCtx::builder() + .inherit_stdout() + .inherit_stderr() + .build(); + + let mut p3 = WasiCtxBuilder::new(); let name = path.file_stem().unwrap().to_str().unwrap(); let tempdir = tempfile::Builder::new() .prefix(&format!("wasi_components_{name}_",)) .tempdir()?; - builder - .args(&[name, "."]) + p3.args(&[name, "."]) .inherit_network() .allow_ip_name_lookup(true); println!("preopen: {tempdir:?}"); - builder.preopened_dir(tempdir.path(), ".", DirPerms::all(), FilePerms::all())?; + p3.preopened_dir(tempdir.path(), ".", DirPerms::all(), FilePerms::all())?; for (var, val) in test_programs_artifacts::wasi_tests_environment() { - builder.env(var, val); + p3.env(var, val); } - let table = ResourceTable::default(); - let p2 = wasmtime_wasi::p2::WasiCtx::builder().build(); - let p3 = builder.build(); + let p3 = p3.build(); + let mut store = Store::new(&engine, Ctx { table, p2, p3 }); let instance = linker.instantiate_async(&mut store, &component).await?; let command = @@ -98,3 +109,8 @@ async fn p3_clocks_sleep() -> anyhow::Result<()> { async fn p3_random_imports() -> anyhow::Result<()> { run(P3_RANDOM_IMPORTS_COMPONENT).await } + +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn p3_cli() -> anyhow::Result<()> { + run(P3_CLI_COMPONENT).await +} From 248313ae7d261298119c322a1e0c03fc147dc233 Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Thu, 17 Jul 2025 12:01:56 +0200 Subject: [PATCH 05/14] refactor: require streams to be `Send` Signed-off-by: Roman Volosatovs --- crates/wasi/src/p3/cli/mod.rs | 33 ++++++++++----------------------- crates/wasi/src/p3/ctx.rs | 12 ++++++------ crates/wasi/src/p3/view.rs | 4 ++-- 3 files changed, 18 insertions(+), 31 deletions(-) diff --git a/crates/wasi/src/p3/cli/mod.rs b/crates/wasi/src/p3/cli/mod.rs index 0da7deb07a17..804c16df4b64 100644 --- a/crates/wasi/src/p3/cli/mod.rs +++ b/crates/wasi/src/p3/cli/mod.rs @@ -2,14 +2,13 @@ mod host; use crate::cli::{IsTerminal, WasiCliCtx, WasiCliImpl, WasiCliView}; use crate::p3::bindings::cli; -use std::rc::Rc; use std::sync::Arc; use tokio::io::{ AsyncRead, AsyncWrite, Empty, Stderr, Stdin, Stdout, empty, stderr, stdin, stdout, }; use wasmtime::component::{HasData, Linker}; -impl Default for WasiCliCtx, Box> { +impl Default for WasiCliCtx, Box> { fn default() -> Self { Self { environment: Vec::default(), @@ -63,7 +62,7 @@ impl Default for WasiCliCtx, Box, Box>, +/// cli: WasiCliCtx, Box>, /// table: ResourceTable, /// } /// @@ -72,8 +71,8 @@ impl Default for WasiCliCtx, Box; -/// type OutputStream = Box; +/// type InputStream = Box; +/// type OutputStream = Box; /// /// fn cli(&mut self) -> &WasiCliCtx { &self.cli } /// } @@ -134,11 +133,11 @@ impl HasData for WasiCli { pub struct TerminalInput; pub struct TerminalOutput; -pub trait InputStream: IsTerminal { +pub trait InputStream: IsTerminal + Send { fn reader(&self) -> Box; } -impl InputStream for &T { +impl InputStream for &T { fn reader(&self) -> Box { (**self).reader() } @@ -156,13 +155,7 @@ impl InputStream for Box { } } -impl InputStream for Rc { - fn reader(&self) -> Box { - (**self).reader() - } -} - -impl InputStream for Arc { +impl InputStream for Arc { fn reader(&self) -> Box { (**self).reader() } @@ -192,11 +185,11 @@ impl InputStream for std::io::Stdin { } } -pub trait OutputStream: IsTerminal { +pub trait OutputStream: IsTerminal + Send { fn writer(&self) -> Box; } -impl OutputStream for &T { +impl OutputStream for &T { fn writer(&self) -> Box { (**self).writer() } @@ -214,13 +207,7 @@ impl OutputStream for Box { } } -impl OutputStream for Rc { - fn writer(&self) -> Box { - (**self).writer() - } -} - -impl OutputStream for Arc { +impl OutputStream for Arc { fn writer(&self) -> Box { (**self).writer() } diff --git a/crates/wasi/src/p3/ctx.rs b/crates/wasi/src/p3/ctx.rs index de7845ef5b7d..59f7c6111e1e 100644 --- a/crates/wasi/src/p3/ctx.rs +++ b/crates/wasi/src/p3/ctx.rs @@ -36,13 +36,13 @@ use tokio::io::{empty, stderr, stdin, stdout}; /// /// [`Store`]: wasmtime::Store pub struct WasiCtxBuilder { - common: crate::WasiCtxBuilder, Box>, + common: crate::WasiCtxBuilder, Box>, // TODO: implement filesystem preopens: Vec<(Dir, String)>, built: bool, } -impl Default for crate::WasiCtxBuilder, Box> { +impl Default for crate::WasiCtxBuilder, Box> { fn default() -> Self { crate::WasiCtxBuilder::new(Box::new(empty()), Box::new(empty()), Box::new(empty())) } @@ -80,19 +80,19 @@ impl WasiCtxBuilder { /// /// Note that inheriting the process's stdin can also be done through /// [`inherit_stdin`](WasiCtxBuilder::inherit_stdin). - pub fn stdin(&mut self, stdin: impl InputStream + Send + 'static) -> &mut Self { + pub fn stdin(&mut self, stdin: impl InputStream + 'static) -> &mut Self { self.common.stdin(Box::new(stdin)); self } /// Same as [`stdin`](WasiCtxBuilder::stdin), but for stdout. - pub fn stdout(&mut self, stdout: impl OutputStream + Send + 'static) -> &mut Self { + pub fn stdout(&mut self, stdout: impl OutputStream + 'static) -> &mut Self { self.common.stdout(Box::new(stdout)); self } /// Same as [`stdin`](WasiCtxBuilder::stdin), but for stderr. - pub fn stderr(&mut self, stderr: impl OutputStream + Send + 'static) -> &mut Self { + pub fn stderr(&mut self, stderr: impl OutputStream + 'static) -> &mut Self { self.common.stderr(Box::new(stderr)); self } @@ -498,7 +498,7 @@ impl WasiCtxBuilder { pub struct WasiCtx { pub random: WasiRandomCtx, pub clocks: WasiClocksCtx, - pub cli: WasiCliCtx, Box>, + pub cli: WasiCliCtx, Box>, } impl WasiCtx { diff --git a/crates/wasi/src/p3/view.rs b/crates/wasi/src/p3/view.rs index 203eae8b831b..c29524785bd1 100644 --- a/crates/wasi/src/p3/view.rs +++ b/crates/wasi/src/p3/view.rs @@ -92,8 +92,8 @@ impl WasiView for WasiImpl { } impl WasiCliView for WasiImpl { - type InputStream = Box; - type OutputStream = Box; + type InputStream = Box; + type OutputStream = Box; fn cli(&mut self) -> &WasiCliCtx { &T::ctx(&mut self.0).cli From 44626a5d9a533c774e6578a9c6241e697f6f2adf Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Thu, 17 Jul 2025 12:29:42 +0200 Subject: [PATCH 06/14] refactor: avoid typing `WasiCli` in task Signed-off-by: Roman Volosatovs --- crates/wasi/src/p3/cli/host.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/wasi/src/p3/cli/host.rs b/crates/wasi/src/p3/cli/host.rs index d93e1e13794b..9e67c3736ff7 100644 --- a/crates/wasi/src/p3/cli/host.rs +++ b/crates/wasi/src/p3/cli/host.rs @@ -10,7 +10,7 @@ use bytes::BytesMut; use std::io::Cursor; use tokio::io::{AsyncRead, AsyncReadExt as _, AsyncWrite, AsyncWriteExt as _}; use wasmtime::component::{ - Accessor, AccessorTask, HostStream, Resource, StreamReader, StreamWriter, + Accessor, AccessorTask, HasData, HostStream, Resource, StreamReader, StreamWriter, }; struct InputTask { @@ -18,12 +18,12 @@ struct InputTask { tx: StreamWriter>, } -impl AccessorTask, wasmtime::Result<()>> for InputTask +impl AccessorTask> for InputTask where - U: 'static, + U: HasData, V: AsyncRead + Send + Sync + Unpin + 'static, { - async fn run(mut self, store: &Accessor>) -> wasmtime::Result<()> { + async fn run(mut self, store: &Accessor) -> wasmtime::Result<()> { let mut buf = BytesMut::with_capacity(8192); let mut tx = self.tx; loop { @@ -52,12 +52,12 @@ struct OutputTask { tx: T, } -impl AccessorTask, wasmtime::Result<()>> for OutputTask +impl AccessorTask> for OutputTask where - U: 'static, + U: HasData, V: AsyncWrite + Send + Sync + Unpin + 'static, { - async fn run(mut self, store: &Accessor>) -> wasmtime::Result<()> { + async fn run(mut self, store: &Accessor) -> wasmtime::Result<()> { let mut buf = BytesMut::with_capacity(8192); let mut rx = self.rx; while let (Some(rx_next), buf_next) = rx.read(store, buf).await { From 70274b07bd712d1d5969d612283f0d16e076eecc Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Thu, 17 Jul 2025 12:30:05 +0200 Subject: [PATCH 07/14] refactor: remove `Unpin` bound from stream I/O Signed-off-by: Roman Volosatovs --- crates/wasi/src/p3/cli/host.rs | 15 ++++++++++--- crates/wasi/src/p3/cli/mod.rs | 40 +++++++++++++++++----------------- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/crates/wasi/src/p3/cli/host.rs b/crates/wasi/src/p3/cli/host.rs index 9e67c3736ff7..a2b7696733c9 100644 --- a/crates/wasi/src/p3/cli/host.rs +++ b/crates/wasi/src/p3/cli/host.rs @@ -168,7 +168,10 @@ where .stream::<_, _, BytesMut>(&mut view) .context("failed to create stream")?; let stdin = view.get().cli().stdin.reader(); - view.spawn(InputTask { rx: stdin, tx }); + view.spawn(InputTask { + rx: Box::into_pin(stdin), + tx, + }); Ok(rx.into()) }) } @@ -188,7 +191,10 @@ where store.with(|mut view| { let stdout = data.into_reader(&mut view); let tx = view.get().cli().stdout.writer(); - view.spawn(OutputTask { rx: stdout, tx }); + view.spawn(OutputTask { + rx: stdout, + tx: Box::into_pin(tx), + }); Ok(()) }) } @@ -208,7 +214,10 @@ where store.with(|mut view| { let stderr = data.into_reader(&mut view); let tx = view.get().cli().stderr.writer(); - view.spawn(OutputTask { rx: stderr, tx }); + view.spawn(OutputTask { + rx: stderr, + tx: Box::into_pin(tx), + }); Ok(()) }) } diff --git a/crates/wasi/src/p3/cli/mod.rs b/crates/wasi/src/p3/cli/mod.rs index 804c16df4b64..348ab76b9bd2 100644 --- a/crates/wasi/src/p3/cli/mod.rs +++ b/crates/wasi/src/p3/cli/mod.rs @@ -134,117 +134,117 @@ pub struct TerminalInput; pub struct TerminalOutput; pub trait InputStream: IsTerminal + Send { - fn reader(&self) -> Box; + fn reader(&self) -> Box; } impl InputStream for &T { - fn reader(&self) -> Box { + fn reader(&self) -> Box { (**self).reader() } } impl InputStream for &mut T { - fn reader(&self) -> Box { + fn reader(&self) -> Box { (**self).reader() } } impl InputStream for Box { - fn reader(&self) -> Box { + fn reader(&self) -> Box { (**self).reader() } } impl InputStream for Arc { - fn reader(&self) -> Box { + fn reader(&self) -> Box { (**self).reader() } } impl InputStream for Empty { - fn reader(&self) -> Box { + fn reader(&self) -> Box { Box::new(empty()) } } impl InputStream for std::io::Empty { - fn reader(&self) -> Box { + fn reader(&self) -> Box { Box::new(empty()) } } impl InputStream for Stdin { - fn reader(&self) -> Box { + fn reader(&self) -> Box { Box::new(stdin()) } } impl InputStream for std::io::Stdin { - fn reader(&self) -> Box { + fn reader(&self) -> Box { Box::new(stdin()) } } pub trait OutputStream: IsTerminal + Send { - fn writer(&self) -> Box; + fn writer(&self) -> Box; } impl OutputStream for &T { - fn writer(&self) -> Box { + fn writer(&self) -> Box { (**self).writer() } } impl OutputStream for &mut T { - fn writer(&self) -> Box { + fn writer(&self) -> Box { (**self).writer() } } impl OutputStream for Box { - fn writer(&self) -> Box { + fn writer(&self) -> Box { (**self).writer() } } impl OutputStream for Arc { - fn writer(&self) -> Box { + fn writer(&self) -> Box { (**self).writer() } } impl OutputStream for Empty { - fn writer(&self) -> Box { + fn writer(&self) -> Box { Box::new(empty()) } } impl OutputStream for std::io::Empty { - fn writer(&self) -> Box { + fn writer(&self) -> Box { Box::new(empty()) } } impl OutputStream for Stdout { - fn writer(&self) -> Box { + fn writer(&self) -> Box { Box::new(stdout()) } } impl OutputStream for std::io::Stdout { - fn writer(&self) -> Box { + fn writer(&self) -> Box { Box::new(stdout()) } } impl OutputStream for Stderr { - fn writer(&self) -> Box { + fn writer(&self) -> Box { Box::new(stderr()) } } impl OutputStream for std::io::Stderr { - fn writer(&self) -> Box { + fn writer(&self) -> Box { Box::new(stderr()) } } From 0d1dc772d243a0110951bc2d3081b4d5c8c89613 Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Thu, 17 Jul 2025 13:49:30 +0200 Subject: [PATCH 08/14] refactor: remove `ResourceView` Signed-off-by: Roman Volosatovs --- crates/wasi/src/cli.rs | 47 ---------------- crates/wasi/src/lib.rs | 17 ------ crates/wasi/src/p3/bindings.rs | 42 +++++++------- crates/wasi/src/p3/cli/host.rs | 97 +++++++++++---------------------- crates/wasi/src/p3/cli/mod.rs | 90 ++++++++++++++++-------------- crates/wasi/src/p3/ctx.rs | 14 ++--- crates/wasi/src/p3/mod.rs | 32 ++++++----- crates/wasi/src/p3/view.rs | 75 ++++++------------------- crates/wasi/tests/all/p3/mod.rs | 17 +++--- 9 files changed, 151 insertions(+), 280 deletions(-) diff --git a/crates/wasi/src/cli.rs b/crates/wasi/src/cli.rs index 5c058fadf5ce..47f03bdfca52 100644 --- a/crates/wasi/src/cli.rs +++ b/crates/wasi/src/cli.rs @@ -1,53 +1,6 @@ use std::rc::Rc; use std::sync::Arc; -use wasmtime::component::ResourceTable; - -use crate::ResourceView; - -#[repr(transparent)] -pub struct WasiCliImpl(pub T); - -impl WasiCliView for &mut T { - type InputStream = T::InputStream; - type OutputStream = T::OutputStream; - - fn cli(&mut self) -> &WasiCliCtx { - (**self).cli() - } -} - -impl WasiCliView for Box { - type InputStream = T::InputStream; - type OutputStream = T::OutputStream; - - fn cli(&mut self) -> &WasiCliCtx { - (**self).cli() - } -} - -impl ResourceView for WasiCliImpl { - fn table(&mut self) -> &mut ResourceTable { - self.0.table() - } -} - -impl WasiCliView for WasiCliImpl { - type InputStream = T::InputStream; - type OutputStream = T::OutputStream; - - fn cli(&mut self) -> &WasiCliCtx { - self.0.cli() - } -} - -pub trait WasiCliView: ResourceView + Send { - type InputStream: IsTerminal; - type OutputStream: IsTerminal; - - fn cli(&mut self) -> &WasiCliCtx; -} - #[derive(Default)] pub struct WasiCliCtx { pub environment: Vec<(String, String)>, diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs index ad8909d39d7c..57ac8e4ac09f 100644 --- a/crates/wasi/src/lib.rs +++ b/crates/wasi/src/lib.rs @@ -42,20 +42,3 @@ pub use cap_fs_ext::SystemTimeSpec; pub use cap_rand::RngCore; #[doc(no_inline)] pub use wasmtime::component::{ResourceTable, ResourceTableError}; - -// TODO: Consolidate with `IoView` -pub trait ResourceView { - fn table(&mut self) -> &mut ResourceTable; -} - -impl ResourceView for &mut T { - fn table(&mut self) -> &mut ResourceTable { - (**self).table() - } -} - -impl ResourceView for Box { - fn table(&mut self) -> &mut ResourceTable { - (**self).table() - } -} diff --git a/crates/wasi/src/p3/bindings.rs b/crates/wasi/src/p3/bindings.rs index f435c7c762e3..6a71e7be5b7b 100644 --- a/crates/wasi/src/p3/bindings.rs +++ b/crates/wasi/src/p3/bindings.rs @@ -12,8 +12,7 @@ //! done using the `with` option to [`bindgen!`]: //! //! ```rust -//! use wasmtime_wasi::ResourceView; -//! use wasmtime_wasi::p3::{WasiCtx, WasiView}; +//! use wasmtime_wasi::p3::{WasiCtx, WasiCtxView, WasiView}; //! use wasmtime::{Result, Engine, Config}; //! use wasmtime::component::{Linker, HasSelf, ResourceTable}; //! @@ -96,12 +95,13 @@ //! } //! } //! -//! impl ResourceView for MyState { -//! fn table(&mut self) -> &mut ResourceTable { &mut self.table } -//! } -//! //! impl WasiView for MyState { -//! fn ctx(&mut self) -> &mut WasiCtx { &mut self.ctx } +//! fn ctx(&mut self) -> WasiCtxView<'_> { +//! WasiCtxView{ +//! ctx: &mut self.ctx, +//! table: &mut self.table, +//! } +//! } //! } //! //! fn main() -> Result<()> { @@ -194,8 +194,7 @@ pub use self::generated::wasi::*; /// ```no_run /// use wasmtime::{Engine, Result, Store, Config}; /// use wasmtime::component::{Component, Linker, ResourceTable}; -/// use wasmtime_wasi::ResourceView; -/// use wasmtime_wasi::p3::{WasiCtx, WasiView, WasiCtxBuilder}; +/// use wasmtime_wasi::p3::{WasiCtx, WasiCtxView, WasiCtxBuilder, WasiView}; /// use wasmtime_wasi::p3::bindings::Command; /// /// // This example is an example shim of executing a component based on the @@ -246,12 +245,13 @@ pub use self::generated::wasi::*; /// table: ResourceTable, /// } /// -/// impl ResourceView for MyState { -/// fn table(&mut self) -> &mut ResourceTable { &mut self.table } -/// } -/// /// impl WasiView for MyState { -/// fn ctx(&mut self) -> &mut WasiCtx { &mut self.ctx } +/// fn ctx(&mut self) -> WasiCtxView<'_> { +/// WasiCtxView{ +/// ctx: &mut self.ctx, +/// table: &mut self.table, +/// } +/// } /// } /// ``` /// @@ -268,8 +268,7 @@ pub use self::generated::Command; /// ```no_run /// use wasmtime::{Engine, Result, Store, Config}; /// use wasmtime::component::{Linker, Component, ResourceTable}; -/// use wasmtime_wasi::ResourceView; -/// use wasmtime_wasi::p3::{WasiCtx, WasiView, WasiCtxBuilder}; +/// use wasmtime_wasi::p3::{WasiCtx, WasiCtxBuilder, WasiCtxView, WasiView}; /// use wasmtime_wasi::p3::bindings::CommandPre; /// /// // This example is an example shim of executing a component based on the @@ -321,12 +320,13 @@ pub use self::generated::Command; /// table: ResourceTable, /// } /// -/// impl ResourceView for MyState { -/// fn table(&mut self) -> &mut ResourceTable { &mut self.table } -/// } -/// /// impl WasiView for MyState { -/// fn ctx(&mut self) -> &mut WasiCtx { &mut self.ctx } +/// fn ctx(&mut self) -> WasiCtxView<'_> { +/// WasiCtxView{ +/// ctx: &mut self.ctx, +/// table: &mut self.table, +/// } +/// } /// } /// ``` /// diff --git a/crates/wasi/src/p3/cli/host.rs b/crates/wasi/src/p3/cli/host.rs index a2b7696733c9..7c95bd35e5be 100644 --- a/crates/wasi/src/p3/cli/host.rs +++ b/crates/wasi/src/p3/cli/host.rs @@ -1,10 +1,10 @@ -use crate::cli::{IsTerminal, WasiCliImpl, WasiCliView}; +use crate::I32Exit; +use crate::cli::IsTerminal; use crate::p3::bindings::cli::{ environment, exit, stderr, stdin, stdout, terminal_input, terminal_output, terminal_stderr, terminal_stdin, terminal_stdout, }; -use crate::p3::cli::{InputStream, OutputStream, TerminalInput, TerminalOutput, WasiCli}; -use crate::{I32Exit, ResourceView as _}; +use crate::p3::cli::{TerminalInput, TerminalOutput, WasiCli, WasiCliCtxView}; use anyhow::{Context as _, anyhow}; use bytes::BytesMut; use std::io::Cursor; @@ -78,41 +78,32 @@ where } } -impl terminal_input::Host for WasiCliImpl where T: WasiCliView {} -impl terminal_output::Host for WasiCliImpl where T: WasiCliView {} +impl terminal_input::Host for WasiCliCtxView<'_> {} +impl terminal_output::Host for WasiCliCtxView<'_> {} -impl terminal_input::HostTerminalInput for WasiCliImpl -where - T: WasiCliView, -{ +impl terminal_input::HostTerminalInput for WasiCliCtxView<'_> { fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { - self.table() + self.table .delete(rep) .context("failed to delete terminal input resource from table")?; Ok(()) } } -impl terminal_output::HostTerminalOutput for WasiCliImpl -where - T: WasiCliView, -{ +impl terminal_output::HostTerminalOutput for WasiCliCtxView<'_> { fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { - self.table() + self.table .delete(rep) .context("failed to delete terminal output resource from table")?; Ok(()) } } -impl terminal_stdin::Host for WasiCliImpl -where - T: WasiCliView, -{ +impl terminal_stdin::Host for WasiCliCtxView<'_> { fn get_terminal_stdin(&mut self) -> wasmtime::Result>> { - if self.cli().stdin.is_terminal() { + if self.ctx.stdin.is_terminal() { let fd = self - .table() + .table .push(TerminalInput) .context("failed to push terminal stdin resource to table")?; Ok(Some(fd)) @@ -122,14 +113,11 @@ where } } -impl terminal_stdout::Host for WasiCliImpl -where - T: WasiCliView, -{ +impl terminal_stdout::Host for WasiCliCtxView<'_> { fn get_terminal_stdout(&mut self) -> wasmtime::Result>> { - if self.cli().stdout.is_terminal() { + if self.ctx.stdout.is_terminal() { let fd = self - .table() + .table .push(TerminalOutput) .context("failed to push terminal stdout resource to table")?; Ok(Some(fd)) @@ -139,14 +127,11 @@ where } } -impl terminal_stderr::Host for WasiCliImpl -where - T: WasiCliView, -{ +impl terminal_stderr::Host for WasiCliCtxView<'_> { fn get_terminal_stderr(&mut self) -> wasmtime::Result>> { - if self.cli().stderr.is_terminal() { + if self.ctx.stderr.is_terminal() { let fd = self - .table() + .table .push(TerminalOutput) .context("failed to push terminal stderr resource to table")?; Ok(Some(fd)) @@ -156,18 +141,14 @@ where } } -impl stdin::HostConcurrent for WasiCli -where - T: WasiCliView + 'static, - T::InputStream: InputStream, -{ +impl stdin::HostConcurrent for WasiCli { async fn get_stdin(store: &Accessor) -> wasmtime::Result> { store.with(|mut view| { let instance = view.instance(); let (tx, rx) = instance .stream::<_, _, BytesMut>(&mut view) .context("failed to create stream")?; - let stdin = view.get().cli().stdin.reader(); + let stdin = view.get().ctx.stdin.reader(); view.spawn(InputTask { rx: Box::into_pin(stdin), tx, @@ -177,20 +158,16 @@ where } } -impl stdin::Host for WasiCliImpl where T: WasiCliView {} +impl stdin::Host for WasiCliCtxView<'_> {} -impl stdout::HostConcurrent for WasiCli -where - T: WasiCliView + 'static, - T::OutputStream: OutputStream, -{ +impl stdout::HostConcurrent for WasiCli { async fn set_stdout( store: &Accessor, data: HostStream, ) -> wasmtime::Result<()> { store.with(|mut view| { let stdout = data.into_reader(&mut view); - let tx = view.get().cli().stdout.writer(); + let tx = view.get().ctx.stdout.writer(); view.spawn(OutputTask { rx: stdout, tx: Box::into_pin(tx), @@ -200,20 +177,16 @@ where } } -impl stdout::Host for WasiCliImpl where T: WasiCliView {} +impl stdout::Host for WasiCliCtxView<'_> {} -impl stderr::HostConcurrent for WasiCli -where - T: WasiCliView + 'static, - T::OutputStream: OutputStream, -{ +impl stderr::HostConcurrent for WasiCli { async fn set_stderr( store: &Accessor, data: HostStream, ) -> wasmtime::Result<()> { store.with(|mut view| { let stderr = data.into_reader(&mut view); - let tx = view.get().cli().stderr.writer(); + let tx = view.get().ctx.stderr.writer(); view.spawn(OutputTask { rx: stderr, tx: Box::into_pin(tx), @@ -223,29 +196,23 @@ where } } -impl stderr::Host for WasiCliImpl where T: WasiCliView {} +impl stderr::Host for WasiCliCtxView<'_> {} -impl environment::Host for WasiCliImpl -where - T: WasiCliView, -{ +impl environment::Host for WasiCliCtxView<'_> { fn get_environment(&mut self) -> wasmtime::Result> { - Ok(self.cli().environment.clone()) + Ok(self.ctx.environment.clone()) } fn get_arguments(&mut self) -> wasmtime::Result> { - Ok(self.cli().arguments.clone()) + Ok(self.ctx.arguments.clone()) } fn initial_cwd(&mut self) -> wasmtime::Result> { - Ok(self.cli().initial_cwd.clone()) + Ok(self.ctx.initial_cwd.clone()) } } -impl exit::Host for WasiCliImpl -where - T: WasiCliView, -{ +impl exit::Host for WasiCliCtxView<'_> { fn exit(&mut self, status: Result<(), ()>) -> wasmtime::Result<()> { let status = match status { Ok(()) => 0, diff --git a/crates/wasi/src/p3/cli/mod.rs b/crates/wasi/src/p3/cli/mod.rs index 348ab76b9bd2..a2ba0f45e6a4 100644 --- a/crates/wasi/src/p3/cli/mod.rs +++ b/crates/wasi/src/p3/cli/mod.rs @@ -1,12 +1,33 @@ mod host; -use crate::cli::{IsTerminal, WasiCliCtx, WasiCliImpl, WasiCliView}; +use crate::cli::{IsTerminal, WasiCliCtx}; use crate::p3::bindings::cli; use std::sync::Arc; use tokio::io::{ AsyncRead, AsyncWrite, Empty, Stderr, Stdin, Stdout, empty, stderr, stdin, stdout, }; -use wasmtime::component::{HasData, Linker}; +use wasmtime::component::{HasData, Linker, ResourceTable}; + +pub struct WasiCliCtxView<'a> { + pub ctx: &'a mut WasiCliCtx, Box>, + pub table: &'a mut ResourceTable, +} + +impl WasiCliView for &mut T { + fn cli(&mut self) -> WasiCliCtxView<'_> { + T::cli(self) + } +} + +impl WasiCliView for Box { + fn cli(&mut self) -> WasiCliCtxView<'_> { + T::cli(self) + } +} + +pub trait WasiCliView: Send { + fn cli(&mut self) -> WasiCliCtxView<'_>; +} impl Default for WasiCliCtx, Box> { fn default() -> Self { @@ -36,9 +57,8 @@ impl Default for WasiCliCtx, Box> { /// ``` /// use wasmtime::{Engine, Result, Store, Config}; /// use wasmtime::component::{Linker, ResourceTable}; -/// use wasmtime_wasi::ResourceView; -/// use wasmtime_wasi::cli::{WasiCliView, WasiCliCtx}; -/// use wasmtime_wasi::p3::cli::{InputStream, OutputStream}; +/// use wasmtime_wasi::cli::WasiCliCtx; +/// use wasmtime_wasi::p3::cli::{InputStream, OutputStream, WasiCliView, WasiCliCtxView}; /// /// fn main() -> Result<()> { /// let mut config = Config::new(); @@ -66,25 +86,21 @@ impl Default for WasiCliCtx, Box> { /// table: ResourceTable, /// } /// -/// impl ResourceView for MyState { -/// fn table(&mut self) -> &mut ResourceTable { &mut self.table } -/// } -/// /// impl WasiCliView for MyState { -/// type InputStream = Box; -/// type OutputStream = Box; -/// -/// fn cli(&mut self) -> &WasiCliCtx { &self.cli } +/// fn cli(&mut self) -> WasiCliCtxView<'_> { +/// WasiCliCtxView { +/// ctx: &mut self.cli, +/// table: &mut self.table, +/// } +/// } /// } /// ``` pub fn add_to_linker(linker: &mut Linker) -> wasmtime::Result<()> where T: WasiCliView + 'static, - T::InputStream: InputStream, - T::OutputStream: OutputStream, { let exit_options = cli::exit::LinkOptions::default(); - add_to_linker_impl(linker, &exit_options, |x| WasiCliImpl(x)) + add_to_linker_impl(linker, &exit_options, |x| x.cli()) } /// Similar to [`add_to_linker`], but with the ability to enable unstable features. @@ -94,40 +110,32 @@ pub fn add_to_linker_with_options( ) -> anyhow::Result<()> where T: WasiCliView + 'static, - T::InputStream: InputStream, - T::OutputStream: OutputStream, { - add_to_linker_impl(linker, exit_options, |x| WasiCliImpl(x)) + add_to_linker_impl(linker, exit_options, |x| x.cli()) } -pub(crate) fn add_to_linker_impl( +pub(crate) fn add_to_linker_impl( linker: &mut Linker, exit_options: &cli::exit::LinkOptions, - host_getter: fn(&mut T) -> WasiCliImpl<&mut U>, -) -> wasmtime::Result<()> -where - T: Send, - U: WasiCliView + 'static, - U::InputStream: InputStream, - U::OutputStream: OutputStream, -{ - cli::exit::add_to_linker::<_, WasiCli>(linker, exit_options, host_getter)?; - cli::environment::add_to_linker::<_, WasiCli>(linker, host_getter)?; - cli::stdin::add_to_linker::<_, WasiCli>(linker, host_getter)?; - cli::stdout::add_to_linker::<_, WasiCli>(linker, host_getter)?; - cli::stderr::add_to_linker::<_, WasiCli>(linker, host_getter)?; - cli::terminal_input::add_to_linker::<_, WasiCli>(linker, host_getter)?; - cli::terminal_output::add_to_linker::<_, WasiCli>(linker, host_getter)?; - cli::terminal_stdin::add_to_linker::<_, WasiCli>(linker, host_getter)?; - cli::terminal_stdout::add_to_linker::<_, WasiCli>(linker, host_getter)?; - cli::terminal_stderr::add_to_linker::<_, WasiCli>(linker, host_getter)?; + host_getter: fn(&mut T) -> WasiCliCtxView<'_>, +) -> wasmtime::Result<()> { + cli::exit::add_to_linker::<_, WasiCli>(linker, exit_options, host_getter)?; + cli::environment::add_to_linker::<_, WasiCli>(linker, host_getter)?; + cli::stdin::add_to_linker::<_, WasiCli>(linker, host_getter)?; + cli::stdout::add_to_linker::<_, WasiCli>(linker, host_getter)?; + cli::stderr::add_to_linker::<_, WasiCli>(linker, host_getter)?; + cli::terminal_input::add_to_linker::<_, WasiCli>(linker, host_getter)?; + cli::terminal_output::add_to_linker::<_, WasiCli>(linker, host_getter)?; + cli::terminal_stdin::add_to_linker::<_, WasiCli>(linker, host_getter)?; + cli::terminal_stdout::add_to_linker::<_, WasiCli>(linker, host_getter)?; + cli::terminal_stderr::add_to_linker::<_, WasiCli>(linker, host_getter)?; Ok(()) } -struct WasiCli(T); +struct WasiCli; -impl HasData for WasiCli { - type Data<'a> = WasiCliImpl<&'a mut T>; +impl HasData for WasiCli { + type Data<'a> = WasiCliCtxView<'a>; } pub struct TerminalInput; diff --git a/crates/wasi/src/p3/ctx.rs b/crates/wasi/src/p3/ctx.rs index 59f7c6111e1e..7a42fac4670b 100644 --- a/crates/wasi/src/p3/ctx.rs +++ b/crates/wasi/src/p3/ctx.rs @@ -464,20 +464,20 @@ impl WasiCtxBuilder { /// /// ``` /// use wasmtime::component::ResourceTable; -/// use wasmtime_wasi::ResourceView; -/// use wasmtime_wasi::p3::{WasiCtx, WasiView, WasiCtxBuilder}; +/// use wasmtime_wasi::p3::{WasiCtx, WasiCtxBuilder, WasiCtxView, WasiView}; /// /// struct MyState { /// ctx: WasiCtx, /// table: ResourceTable, /// } /// -/// impl ResourceView for MyState { -/// fn table(&mut self) -> &mut ResourceTable { &mut self.table } -/// } -/// /// impl WasiView for MyState { -/// fn ctx(&mut self) -> &mut WasiCtx { &mut self.ctx } +/// fn ctx(&mut self) -> WasiCtxView<'_> { +/// WasiCtxView{ +/// ctx: &mut self.ctx, +/// table: &mut self.table, +/// } +/// } /// } /// /// impl MyState { diff --git a/crates/wasi/src/p3/mod.rs b/crates/wasi/src/p3/mod.rs index b075b898cded..4537c21b5fa3 100644 --- a/crates/wasi/src/p3/mod.rs +++ b/crates/wasi/src/p3/mod.rs @@ -18,13 +18,13 @@ mod view; use wasmtime::component::Linker; -use crate::cli::WasiCliImpl; use crate::clocks::WasiClocksImpl; use crate::p3::bindings::LinkOptions; +use crate::p3::cli::WasiCliCtxView; use crate::random::WasiRandomImpl; pub use self::ctx::{WasiCtx, WasiCtxBuilder}; -pub use self::view::{WasiImpl, WasiView}; +pub use self::view::{WasiCtxView, WasiView}; /// Add all WASI interfaces from this module into the `linker` provided. /// @@ -36,9 +36,8 @@ pub use self::view::{WasiImpl, WasiView}; /// /// ``` /// use wasmtime::{Engine, Result, Store, Config}; -/// use wasmtime::component::{ Linker, ResourceTable}; -/// use wasmtime_wasi::ResourceView; -/// use wasmtime_wasi::p3::{WasiCtx, WasiView}; +/// use wasmtime::component::{Linker, ResourceTable}; +/// use wasmtime_wasi::p3::{WasiCtx, WasiCtxView, WasiView}; /// /// fn main() -> Result<()> { /// let mut config = Config::new(); @@ -66,12 +65,13 @@ pub use self::view::{WasiImpl, WasiView}; /// table: ResourceTable, /// } /// -/// impl ResourceView for MyState { -/// fn table(&mut self) -> &mut ResourceTable { &mut self.table } -/// } -/// /// impl WasiView for MyState { -/// fn ctx(&mut self) -> &mut WasiCtx { &mut self.ctx } +/// fn ctx(&mut self) -> WasiCtxView<'_> { +/// WasiCtxView{ +/// ctx: &mut self.ctx, +/// table: &mut self.table, +/// } +/// } /// } /// ``` pub fn add_to_linker(linker: &mut Linker) -> wasmtime::Result<()> @@ -90,8 +90,14 @@ pub fn add_to_linker_with_options( where T: WasiView + 'static, { - clocks::add_to_linker_impl(linker, |x| WasiClocksImpl(&mut x.ctx().clocks))?; - random::add_to_linker_impl(linker, |x| WasiRandomImpl(&mut x.ctx().random))?; - cli::add_to_linker_impl(linker, &options.into(), |x| WasiCliImpl(x.as_wasi_impl()))?; + clocks::add_to_linker_impl(linker, |x| WasiClocksImpl(&mut x.ctx().ctx.clocks))?; + random::add_to_linker_impl(linker, |x| WasiRandomImpl(&mut x.ctx().ctx.random))?; + cli::add_to_linker_impl(linker, &options.into(), |x| { + let WasiCtxView { ctx, table } = x.ctx(); + WasiCliCtxView { + ctx: &mut ctx.cli, + table, + } + })?; Ok(()) } diff --git a/crates/wasi/src/p3/view.rs b/crates/wasi/src/p3/view.rs index c29524785bd1..7674f22051bd 100644 --- a/crates/wasi/src/p3/view.rs +++ b/crates/wasi/src/p3/view.rs @@ -1,10 +1,12 @@ use wasmtime::component::ResourceTable; -use crate::ResourceView; -use crate::cli::{WasiCliCtx, WasiCliView}; -use crate::p3::cli::{InputStream, OutputStream}; use crate::p3::ctx::WasiCtx; +pub struct WasiCtxView<'a> { + pub ctx: &'a mut WasiCtx, + pub table: &'a mut ResourceTable, +} + /// A trait which provides access to the [`WasiCtx`] inside the embedder's `T` /// of [`Store`][`Store`]. /// @@ -17,8 +19,7 @@ use crate::p3::ctx::WasiCtx; /// # Example /// /// ``` -/// use wasmtime_wasi::ResourceView; -/// use wasmtime_wasi::p3::{WasiCtx, WasiCtxBuilder, WasiView}; +/// use wasmtime_wasi::p3::{WasiCtx, WasiCtxBuilder, WasiView, WasiCtxView}; /// use wasmtime::component::ResourceTable; /// /// struct MyState { @@ -26,76 +27,32 @@ use crate::p3::ctx::WasiCtx; /// table: ResourceTable, /// } /// -/// impl ResourceView for MyState { -/// fn table(&mut self) -> &mut ResourceTable { &mut self.table } -/// } -/// /// impl WasiView for MyState { -/// fn ctx(&mut self) -> &mut WasiCtx { &mut self.ctx } +/// fn ctx(&mut self) -> WasiCtxView<'_> { +/// WasiCtxView{ +/// ctx: &mut self.ctx, +/// table: &mut self.table, +/// } +/// } /// } /// ``` /// [`Store`]: wasmtime::Store /// [`Linker`]: wasmtime::component::Linker /// -pub trait WasiView: ResourceView + Send { +pub trait WasiView: Send { /// Yields mutable access to the [`WasiCtx`] configuration used for this /// context. - fn ctx(&mut self) -> &mut WasiCtx; - - #[doc(hidden)] - fn as_wasi_impl(&mut self) -> &mut WasiImpl - where - Self: Sized, - { - // TODO: Figure out how to avoid `unsafe` - // SAFETY: `WasiImpl` is `repr(transparent)` - unsafe { core::mem::transmute(self) } - } + fn ctx(&mut self) -> WasiCtxView<'_>; } impl WasiView for &mut T { - fn ctx(&mut self) -> &mut WasiCtx { + fn ctx(&mut self) -> WasiCtxView<'_> { T::ctx(self) } } impl WasiView for Box { - fn ctx(&mut self) -> &mut WasiCtx { + fn ctx(&mut self) -> WasiCtxView<'_> { T::ctx(self) } } - -/// A small newtype wrapper which serves as the basis for implementations of -/// `Host` WASI traits in this crate. -/// -/// This type is used as the basis for the implementation of all `Host` traits -/// generated by `bindgen!` for WASI interfaces. This is used automatically with -/// [`add_to_linker`](crate::p3::add_to_linker_sync). -/// -/// This type is otherwise provided if you're calling the `add_to_linker` -/// functions generated by `bindgen!` from the [`bindings` -/// module](crate::p3::bindings). In this situation you'll want to create a value of -/// this type in the closures added to a `Linker`. -#[repr(transparent)] -pub struct WasiImpl(pub T); - -impl ResourceView for WasiImpl { - fn table(&mut self) -> &mut ResourceTable { - T::table(&mut self.0) - } -} - -impl WasiView for WasiImpl { - fn ctx(&mut self) -> &mut WasiCtx { - T::ctx(&mut self.0) - } -} - -impl WasiCliView for WasiImpl { - type InputStream = Box; - type OutputStream = Box; - - fn cli(&mut self) -> &WasiCliCtx { - &T::ctx(&mut self.0).cli - } -} diff --git a/crates/wasi/tests/all/p3/mod.rs b/crates/wasi/tests/all/p3/mod.rs index f3252270613a..9f9d247b9c52 100644 --- a/crates/wasi/tests/all/p3/mod.rs +++ b/crates/wasi/tests/all/p3/mod.rs @@ -4,8 +4,8 @@ use anyhow::{Context as _, anyhow}; use wasmtime::Store; use wasmtime::component::{Component, Linker, ResourceTable}; use wasmtime_wasi::p3::bindings::Command; -use wasmtime_wasi::p3::{WasiCtx, WasiCtxBuilder, WasiView}; -use wasmtime_wasi::{DirPerms, FilePerms, ResourceView}; +use wasmtime_wasi::p3::{WasiCtx, WasiCtxBuilder, WasiCtxView, WasiView}; +use wasmtime_wasi::{DirPerms, FilePerms}; use test_programs_artifacts::*; @@ -23,14 +23,11 @@ struct Ctx { } impl WasiView for Ctx { - fn ctx(&mut self) -> &mut WasiCtx { - &mut self.p3 - } -} - -impl ResourceView for Ctx { - fn table(&mut self) -> &mut ResourceTable { - &mut self.table + fn ctx(&mut self) -> WasiCtxView<'_> { + WasiCtxView { + ctx: &mut self.p3, + table: &mut self.table, + } } } From 7cc19b1d72c90af443c78ecbdc20c88d2d54ab6c Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Thu, 17 Jul 2025 13:52:44 +0200 Subject: [PATCH 09/14] chore: update `serve` to new WASI `isatty` API Signed-off-by: Roman Volosatovs --- src/commands/serve.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/commands/serve.rs b/src/commands/serve.rs index 097731db89c1..ddd3283952e1 100644 --- a/src/commands/serve.rs +++ b/src/commands/serve.rs @@ -840,10 +840,10 @@ impl wasmtime_wasi::p2::StdoutStream for LogStream { fn stream(&self) -> Box { Box::new(self.clone()) } +} - fn isatty(&self) -> bool { - use std::io::IsTerminal; - +impl wasmtime_wasi::cli::IsTerminal for LogStream { + fn is_terminal(&self) -> bool { match &self.output { Output::Stdout => std::io::stdout().is_terminal(), Output::Stderr => std::io::stderr().is_terminal(), From bbb4f9dd73f3a30ab48148dffaa2d852e25b19e6 Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Thu, 17 Jul 2025 14:16:14 +0200 Subject: [PATCH 10/14] chore: adapt to stream API changes Signed-off-by: Roman Volosatovs --- crates/wasi/src/p3/cli/host.rs | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/crates/wasi/src/p3/cli/host.rs b/crates/wasi/src/p3/cli/host.rs index 7c95bd35e5be..5e0a4e345cb2 100644 --- a/crates/wasi/src/p3/cli/host.rs +++ b/crates/wasi/src/p3/cli/host.rs @@ -25,17 +25,15 @@ where { async fn run(mut self, store: &Accessor) -> wasmtime::Result<()> { let mut buf = BytesMut::with_capacity(8192); - let mut tx = self.tx; - loop { + while !self.tx.is_closed() { match self.rx.read_buf(&mut buf).await { Ok(0) => return Ok(()), Ok(_) => { - let (Some(tx_next), buf_next) = tx.write_all(store, Cursor::new(buf)).await - else { - break Ok(()); - }; - tx = tx_next; - buf = buf_next.into_inner(); + buf = self + .tx + .write_all(store, Cursor::new(buf)) + .await + .into_inner(); buf.clear(); } Err(_err) => { @@ -44,6 +42,7 @@ where } } } + Ok(()) } } @@ -59,10 +58,8 @@ where { async fn run(mut self, store: &Accessor) -> wasmtime::Result<()> { let mut buf = BytesMut::with_capacity(8192); - let mut rx = self.rx; - while let (Some(rx_next), buf_next) = rx.read(store, buf).await { - buf = buf_next; - rx = rx_next; + while !self.rx.is_closed() { + buf = self.rx.read(store, buf).await; match self.tx.write_all(&buf).await { Ok(()) => { buf.clear(); From 3775d989f3fc124f863f4a0534bea6e90722323c Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Thu, 17 Jul 2025 14:07:17 +0200 Subject: [PATCH 11/14] refactor: avoid `**` syntax Signed-off-by: Roman Volosatovs --- crates/wasi/src/cli.rs | 10 +++++----- crates/wasi/src/clocks.rs | 4 ++-- crates/wasi/src/p3/cli/mod.rs | 16 ++++++++-------- crates/wasi/src/random.rs | 4 ++-- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/crates/wasi/src/cli.rs b/crates/wasi/src/cli.rs index 47f03bdfca52..3769a11d08a2 100644 --- a/crates/wasi/src/cli.rs +++ b/crates/wasi/src/cli.rs @@ -18,31 +18,31 @@ pub trait IsTerminal { impl IsTerminal for &T { fn is_terminal(&self) -> bool { - (**self).is_terminal() + T::is_terminal(self) } } impl IsTerminal for &mut T { fn is_terminal(&self) -> bool { - (**self).is_terminal() + T::is_terminal(self) } } impl IsTerminal for Box { fn is_terminal(&self) -> bool { - (**self).is_terminal() + T::is_terminal(self) } } impl IsTerminal for Rc { fn is_terminal(&self) -> bool { - (**self).is_terminal() + T::is_terminal(self) } } impl IsTerminal for Arc { fn is_terminal(&self) -> bool { - (**self).is_terminal() + T::is_terminal(self) } } diff --git a/crates/wasi/src/clocks.rs b/crates/wasi/src/clocks.rs index 2d284a43e5af..15fbc5ac0fba 100644 --- a/crates/wasi/src/clocks.rs +++ b/crates/wasi/src/clocks.rs @@ -7,13 +7,13 @@ pub struct WasiClocksImpl(pub T); impl WasiClocksView for &mut T { fn clocks(&mut self) -> &WasiClocksCtx { - (**self).clocks() + T::clocks(self) } } impl WasiClocksView for Box { fn clocks(&mut self) -> &WasiClocksCtx { - (**self).clocks() + T::clocks(self) } } diff --git a/crates/wasi/src/p3/cli/mod.rs b/crates/wasi/src/p3/cli/mod.rs index a2ba0f45e6a4..0759f890728b 100644 --- a/crates/wasi/src/p3/cli/mod.rs +++ b/crates/wasi/src/p3/cli/mod.rs @@ -147,25 +147,25 @@ pub trait InputStream: IsTerminal + Send { impl InputStream for &T { fn reader(&self) -> Box { - (**self).reader() + T::reader(self) } } impl InputStream for &mut T { fn reader(&self) -> Box { - (**self).reader() + T::reader(self) } } impl InputStream for Box { fn reader(&self) -> Box { - (**self).reader() + T::reader(self) } } impl InputStream for Arc { fn reader(&self) -> Box { - (**self).reader() + T::reader(self) } } @@ -199,25 +199,25 @@ pub trait OutputStream: IsTerminal + Send { impl OutputStream for &T { fn writer(&self) -> Box { - (**self).writer() + T::writer(self) } } impl OutputStream for &mut T { fn writer(&self) -> Box { - (**self).writer() + T::writer(self) } } impl OutputStream for Box { fn writer(&self) -> Box { - (**self).writer() + T::writer(self) } } impl OutputStream for Arc { fn writer(&self) -> Box { - (**self).writer() + T::writer(self) } } diff --git a/crates/wasi/src/random.rs b/crates/wasi/src/random.rs index 09ad998c6d32..08a593f86869 100644 --- a/crates/wasi/src/random.rs +++ b/crates/wasi/src/random.rs @@ -5,13 +5,13 @@ pub struct WasiRandomImpl(pub T); impl WasiRandomView for &mut T { fn random(&mut self) -> &mut WasiRandomCtx { - (**self).random() + T::random(self) } } impl WasiRandomView for Box { fn random(&mut self) -> &mut WasiRandomCtx { - (**self).random() + T::random(self) } } From 46c200383666cc4b7b254443196f378bf0114da0 Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Thu, 17 Jul 2025 14:37:50 +0200 Subject: [PATCH 12/14] refactor(wasip3): remove `Impl` wrappers Signed-off-by: Roman Volosatovs --- crates/wasi/src/clocks.rs | 17 ++++------------- crates/wasi/src/p3/clocks/host.rs | 28 ++++++++++------------------ crates/wasi/src/p3/clocks/mod.rs | 26 +++++++++++--------------- crates/wasi/src/p3/mod.rs | 6 ++---- crates/wasi/src/p3/random/host.rs | 27 +++++++++------------------ crates/wasi/src/p3/random/mod.rs | 28 ++++++++++++---------------- crates/wasi/src/random.rs | 9 --------- 7 files changed, 48 insertions(+), 93 deletions(-) diff --git a/crates/wasi/src/clocks.rs b/crates/wasi/src/clocks.rs index 15fbc5ac0fba..bb030e1d1c32 100644 --- a/crates/wasi/src/clocks.rs +++ b/crates/wasi/src/clocks.rs @@ -2,35 +2,26 @@ use cap_std::time::{Duration, Instant, SystemClock}; use cap_std::{AmbientAuthority, ambient_authority}; use cap_time_ext::{MonotonicClockExt as _, SystemClockExt as _}; -#[repr(transparent)] -pub struct WasiClocksImpl(pub T); - impl WasiClocksView for &mut T { - fn clocks(&mut self) -> &WasiClocksCtx { + fn clocks(&mut self) -> &mut WasiClocksCtx { T::clocks(self) } } impl WasiClocksView for Box { - fn clocks(&mut self) -> &WasiClocksCtx { + fn clocks(&mut self) -> &mut WasiClocksCtx { T::clocks(self) } } -impl WasiClocksView for WasiClocksImpl { - fn clocks(&mut self) -> &WasiClocksCtx { - self.0.clocks() - } -} - impl WasiClocksView for WasiClocksCtx { - fn clocks(&mut self) -> &WasiClocksCtx { + fn clocks(&mut self) -> &mut WasiClocksCtx { self } } pub trait WasiClocksView: Send { - fn clocks(&mut self) -> &WasiClocksCtx; + fn clocks(&mut self) -> &mut WasiClocksCtx; } pub struct WasiClocksCtx { diff --git a/crates/wasi/src/p3/clocks/host.rs b/crates/wasi/src/p3/clocks/host.rs index 5fb024a1d521..92fc75c28901 100644 --- a/crates/wasi/src/p3/clocks/host.rs +++ b/crates/wasi/src/p3/clocks/host.rs @@ -4,8 +4,9 @@ use cap_std::time::SystemTime; use tokio::time::sleep; use wasmtime::component::Accessor; +use crate::clocks::WasiClocksCtx; use crate::p3::bindings::clocks::{monotonic_clock, wall_clock}; -use crate::p3::clocks::{WasiClocks, WasiClocksImpl, WasiClocksView}; +use crate::p3::clocks::WasiClocks; impl TryFrom for wall_clock::Datetime { type Error = wasmtime::Error; @@ -21,12 +22,9 @@ impl TryFrom for wall_clock::Datetime { } } -impl wall_clock::Host for WasiClocksImpl -where - T: WasiClocksView, -{ +impl wall_clock::Host for WasiClocksCtx { fn now(&mut self) -> wasmtime::Result { - let now = self.clocks().wall_clock.now(); + let now = self.wall_clock.now(); Ok(wall_clock::Datetime { seconds: now.as_secs(), nanoseconds: now.subsec_nanos(), @@ -34,7 +32,7 @@ where } fn resolution(&mut self) -> wasmtime::Result { - let res = self.clocks().wall_clock.resolution(); + let res = self.wall_clock.resolution(); Ok(wall_clock::Datetime { seconds: res.as_secs(), nanoseconds: res.subsec_nanos(), @@ -42,15 +40,12 @@ where } } -impl monotonic_clock::HostConcurrent for WasiClocks -where - T: WasiClocksView + 'static, -{ +impl monotonic_clock::HostConcurrent for WasiClocks { async fn wait_until( store: &Accessor, when: monotonic_clock::Instant, ) -> wasmtime::Result<()> { - let clock_now = store.with(|mut view| view.get().clocks().monotonic_clock.now()); + let clock_now = store.with(|mut view| view.get().monotonic_clock.now()); if when > clock_now { sleep(Duration::from_nanos(when - clock_now)).await; }; @@ -68,15 +63,12 @@ where } } -impl monotonic_clock::Host for WasiClocksImpl -where - T: WasiClocksView, -{ +impl monotonic_clock::Host for WasiClocksCtx { fn now(&mut self) -> wasmtime::Result { - Ok(self.clocks().monotonic_clock.now()) + Ok(self.monotonic_clock.now()) } fn resolution(&mut self) -> wasmtime::Result { - Ok(self.clocks().monotonic_clock.resolution()) + Ok(self.monotonic_clock.resolution()) } } diff --git a/crates/wasi/src/p3/clocks/mod.rs b/crates/wasi/src/p3/clocks/mod.rs index 66b185ae9562..d438090aaac2 100644 --- a/crates/wasi/src/p3/clocks/mod.rs +++ b/crates/wasi/src/p3/clocks/mod.rs @@ -1,6 +1,6 @@ mod host; -use crate::clocks::{WasiClocksImpl, WasiClocksView}; +use crate::clocks::{WasiClocksCtx, WasiClocksView}; use crate::p3::bindings::clocks; use wasmtime::component::{HasData, Linker}; @@ -48,31 +48,27 @@ use wasmtime::component::{HasData, Linker}; /// } /// /// impl WasiClocksView for MyState { -/// fn clocks(&mut self) -> &WasiClocksCtx { &self.clocks } +/// fn clocks(&mut self) -> &mut WasiClocksCtx { &mut self.clocks } /// } /// ``` pub fn add_to_linker(linker: &mut Linker) -> wasmtime::Result<()> where T: WasiClocksView + 'static, { - add_to_linker_impl(linker, |x| WasiClocksImpl(x)) + add_to_linker_impl(linker, |x| x.clocks()) } -pub(crate) fn add_to_linker_impl( +pub(crate) fn add_to_linker_impl( linker: &mut Linker, - host_getter: fn(&mut T) -> WasiClocksImpl<&mut U>, -) -> wasmtime::Result<()> -where - T: Send, - U: WasiClocksView + 'static, -{ - clocks::monotonic_clock::add_to_linker::<_, WasiClocks>(linker, host_getter)?; - clocks::wall_clock::add_to_linker::<_, WasiClocks>(linker, host_getter)?; + host_getter: fn(&mut T) -> &mut WasiClocksCtx, +) -> wasmtime::Result<()> { + clocks::monotonic_clock::add_to_linker::<_, WasiClocks>(linker, host_getter)?; + clocks::wall_clock::add_to_linker::<_, WasiClocks>(linker, host_getter)?; Ok(()) } -struct WasiClocks(T); +struct WasiClocks; -impl HasData for WasiClocks { - type Data<'a> = WasiClocksImpl<&'a mut T>; +impl HasData for WasiClocks { + type Data<'a> = &'a mut WasiClocksCtx; } diff --git a/crates/wasi/src/p3/mod.rs b/crates/wasi/src/p3/mod.rs index 4537c21b5fa3..36f2cc4e1224 100644 --- a/crates/wasi/src/p3/mod.rs +++ b/crates/wasi/src/p3/mod.rs @@ -18,10 +18,8 @@ mod view; use wasmtime::component::Linker; -use crate::clocks::WasiClocksImpl; use crate::p3::bindings::LinkOptions; use crate::p3::cli::WasiCliCtxView; -use crate::random::WasiRandomImpl; pub use self::ctx::{WasiCtx, WasiCtxBuilder}; pub use self::view::{WasiCtxView, WasiView}; @@ -90,8 +88,8 @@ pub fn add_to_linker_with_options( where T: WasiView + 'static, { - clocks::add_to_linker_impl(linker, |x| WasiClocksImpl(&mut x.ctx().ctx.clocks))?; - random::add_to_linker_impl(linker, |x| WasiRandomImpl(&mut x.ctx().ctx.random))?; + clocks::add_to_linker_impl(linker, |x| &mut x.ctx().ctx.clocks)?; + random::add_to_linker_impl(linker, |x| &mut x.ctx().ctx.random)?; cli::add_to_linker_impl(linker, &options.into(), |x| { let WasiCtxView { ctx, table } = x.ctx(); WasiCliCtxView { diff --git a/crates/wasi/src/p3/random/host.rs b/crates/wasi/src/p3/random/host.rs index c23904ca4464..7e2b71640d71 100644 --- a/crates/wasi/src/p3/random/host.rs +++ b/crates/wasi/src/p3/random/host.rs @@ -2,46 +2,37 @@ use cap_rand::Rng; use cap_rand::distributions::Standard; use crate::p3::bindings::random::{insecure, insecure_seed, random}; -use crate::p3::random::{WasiRandomImpl, WasiRandomView}; +use crate::random::WasiRandomCtx; -impl random::Host for WasiRandomImpl -where - T: WasiRandomView, -{ +impl random::Host for WasiRandomCtx { fn get_random_bytes(&mut self, len: u64) -> wasmtime::Result> { - Ok((&mut self.random().random) + Ok((&mut self.random) .sample_iter(Standard) .take(len as usize) .collect()) } fn get_random_u64(&mut self) -> wasmtime::Result { - Ok(self.random().random.sample(Standard)) + Ok(self.random.sample(Standard)) } } -impl insecure::Host for WasiRandomImpl -where - T: WasiRandomView, -{ +impl insecure::Host for WasiRandomCtx { fn get_insecure_random_bytes(&mut self, len: u64) -> wasmtime::Result> { - Ok((&mut self.random().insecure_random) + Ok((&mut self.insecure_random) .sample_iter(Standard) .take(len as usize) .collect()) } fn get_insecure_random_u64(&mut self) -> wasmtime::Result { - Ok(self.random().insecure_random.sample(Standard)) + Ok(self.insecure_random.sample(Standard)) } } -impl insecure_seed::Host for WasiRandomImpl -where - T: WasiRandomView, -{ +impl insecure_seed::Host for WasiRandomCtx { fn insecure_seed(&mut self) -> wasmtime::Result<(u64, u64)> { - let seed: u128 = self.random().insecure_random_seed; + let seed: u128 = self.insecure_random_seed; Ok((seed as u64, (seed >> 64) as u64)) } } diff --git a/crates/wasi/src/p3/random/mod.rs b/crates/wasi/src/p3/random/mod.rs index 9a9b2e68ff14..568492dc0579 100644 --- a/crates/wasi/src/p3/random/mod.rs +++ b/crates/wasi/src/p3/random/mod.rs @@ -1,7 +1,7 @@ mod host; use crate::p3::bindings::random; -use crate::random::{WasiRandomImpl, WasiRandomView}; +use crate::random::{WasiRandomCtx, WasiRandomView}; use wasmtime::component::{HasData, Linker}; /// Add all WASI interfaces from this module into the `linker` provided. @@ -19,7 +19,7 @@ use wasmtime::component::{HasData, Linker}; /// /// ``` /// use wasmtime::{Engine, Result, Store, Config}; -/// use wasmtime::component::{ResourceTable, Linker}; +/// use wasmtime::component::Linker; /// use wasmtime_wasi::random::{WasiRandomView, WasiRandomCtx}; /// /// fn main() -> Result<()> { @@ -55,25 +55,21 @@ pub fn add_to_linker(linker: &mut Linker) -> wasmtime::Result<()> where T: WasiRandomView + 'static, { - add_to_linker_impl(linker, |x| WasiRandomImpl(x)) + add_to_linker_impl(linker, |x| x.random()) } -pub(crate) fn add_to_linker_impl( +pub(crate) fn add_to_linker_impl( linker: &mut Linker, - host_getter: fn(&mut T) -> WasiRandomImpl<&mut U>, -) -> wasmtime::Result<()> -where - T: Send, - U: WasiRandomView + 'static, -{ - random::random::add_to_linker::<_, WasiRandom>(linker, host_getter)?; - random::insecure::add_to_linker::<_, WasiRandom>(linker, host_getter)?; - random::insecure_seed::add_to_linker::<_, WasiRandom>(linker, host_getter)?; + host_getter: fn(&mut T) -> &mut WasiRandomCtx, +) -> wasmtime::Result<()> { + random::random::add_to_linker::<_, WasiRandom>(linker, host_getter)?; + random::insecure::add_to_linker::<_, WasiRandom>(linker, host_getter)?; + random::insecure_seed::add_to_linker::<_, WasiRandom>(linker, host_getter)?; Ok(()) } -struct WasiRandom(T); +struct WasiRandom; -impl HasData for WasiRandom { - type Data<'a> = WasiRandomImpl<&'a mut T>; +impl HasData for WasiRandom { + type Data<'a> = &'a mut WasiRandomCtx; } diff --git a/crates/wasi/src/random.rs b/crates/wasi/src/random.rs index 08a593f86869..474f819155cd 100644 --- a/crates/wasi/src/random.rs +++ b/crates/wasi/src/random.rs @@ -1,8 +1,5 @@ use cap_rand::{Rng as _, RngCore, SeedableRng as _}; -#[repr(transparent)] -pub struct WasiRandomImpl(pub T); - impl WasiRandomView for &mut T { fn random(&mut self) -> &mut WasiRandomCtx { T::random(self) @@ -15,12 +12,6 @@ impl WasiRandomView for Box { } } -impl WasiRandomView for WasiRandomImpl { - fn random(&mut self) -> &mut WasiRandomCtx { - self.0.random() - } -} - impl WasiRandomView for WasiRandomCtx { fn random(&mut self) -> &mut WasiRandomCtx { self From b2265775a91c396957c6fdb66a0d12db22989a66 Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Thu, 17 Jul 2025 14:42:26 +0200 Subject: [PATCH 13/14] refactor(wasip3): use shorthand closure syntax Signed-off-by: Roman Volosatovs --- crates/wasi/src/p3/cli/mod.rs | 4 ++-- crates/wasi/src/p3/clocks/mod.rs | 2 +- crates/wasi/src/p3/random/mod.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/wasi/src/p3/cli/mod.rs b/crates/wasi/src/p3/cli/mod.rs index 0759f890728b..ffa03d7edf4c 100644 --- a/crates/wasi/src/p3/cli/mod.rs +++ b/crates/wasi/src/p3/cli/mod.rs @@ -100,7 +100,7 @@ where T: WasiCliView + 'static, { let exit_options = cli::exit::LinkOptions::default(); - add_to_linker_impl(linker, &exit_options, |x| x.cli()) + add_to_linker_impl(linker, &exit_options, T::cli) } /// Similar to [`add_to_linker`], but with the ability to enable unstable features. @@ -111,7 +111,7 @@ pub fn add_to_linker_with_options( where T: WasiCliView + 'static, { - add_to_linker_impl(linker, exit_options, |x| x.cli()) + add_to_linker_impl(linker, exit_options, T::cli) } pub(crate) fn add_to_linker_impl( diff --git a/crates/wasi/src/p3/clocks/mod.rs b/crates/wasi/src/p3/clocks/mod.rs index d438090aaac2..ea9a4d2aff3f 100644 --- a/crates/wasi/src/p3/clocks/mod.rs +++ b/crates/wasi/src/p3/clocks/mod.rs @@ -55,7 +55,7 @@ pub fn add_to_linker(linker: &mut Linker) -> wasmtime::Result<()> where T: WasiClocksView + 'static, { - add_to_linker_impl(linker, |x| x.clocks()) + add_to_linker_impl(linker, T::clocks) } pub(crate) fn add_to_linker_impl( diff --git a/crates/wasi/src/p3/random/mod.rs b/crates/wasi/src/p3/random/mod.rs index 568492dc0579..524bef75711c 100644 --- a/crates/wasi/src/p3/random/mod.rs +++ b/crates/wasi/src/p3/random/mod.rs @@ -55,7 +55,7 @@ pub fn add_to_linker(linker: &mut Linker) -> wasmtime::Result<()> where T: WasiRandomView + 'static, { - add_to_linker_impl(linker, |x| x.random()) + add_to_linker_impl(linker, T::random) } pub(crate) fn add_to_linker_impl( From 258847669d189125112384e6082eb76c36c0b5cb Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Fri, 18 Jul 2025 12:32:06 +0200 Subject: [PATCH 14/14] chore: account for different env on different targets prtest:full Signed-off-by: Roman Volosatovs --- crates/test-programs/src/bin/p3_cli.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/test-programs/src/bin/p3_cli.rs b/crates/test-programs/src/bin/p3_cli.rs index a91844c8afea..b0a05c0ca933 100644 --- a/crates/test-programs/src/bin/p3_cli.rs +++ b/crates/test-programs/src/bin/p3_cli.rs @@ -11,7 +11,7 @@ test_programs::p3::export!(Component); impl test_programs::p3::exports::wasi::cli::run::Guest for Component { async fn run() -> Result<(), ()> { assert_eq!(environment::get_arguments(), ["p3_cli.component", "."]); - assert_eq!(environment::get_environment().len(), 1); + assert_ne!(environment::get_environment(), []); assert_eq!(environment::initial_cwd(), None); assert!(terminal_stdin::get_terminal_stdin().is_none());