Browse Source

Promote wasmtime-wasi::preview2 to root of crate (#7933)

* better top matter

* eliminate wasi-common deprecations from wasmtime-wasi

* wasmtime-wasi: preview2 feature always enabled, so just eliminate it

* rename preview1-on-preview2 feature to just preview1

* wasi-http fix

* dep fixes. change some log::debug! to tracing::debug!

* mv preview2 up to root

and `sed -i 's/crate::preview2/crate/g' **/*.rs`

* fix tests too

* fixes throughout wasmtime-wasi-http

* cli: s/wasmtime_wasi::preview2/wasmtime_wasi

* rustfmt

* fix wasi-async example

* fix docs build (needs wasi-common built to compile examples)

* c-api: use wasi-common directly

i guess we didnt build the c-api in CI with deprecation warnings denied

prtest:full

* fix example required-features

* fix examples CMakeLists build
pull/7661/head
Pat Hickey 9 months ago
committed by GitHub
parent
commit
a56bd5e469
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      .github/workflows/main.yml
  2. 4
      Cargo.lock
  3. 6
      Cargo.toml
  4. 3
      crates/c-api/Cargo.toml
  5. 2
      crates/c-api/src/linker.rs
  6. 4
      crates/wasi-http/Cargo.toml
  7. 4
      crates/wasi-http/src/body.rs
  8. 6
      crates/wasi-http/src/lib.rs
  9. 69
      crates/wasi-http/src/proxy.rs
  10. 8
      crates/wasi-http/src/types.rs
  11. 6
      crates/wasi-http/src/types_impl.rs
  12. 4
      crates/wasi-http/tests/all/async_.rs
  13. 6
      crates/wasi-http/tests/all/main.rs
  14. 2
      crates/wasi-http/tests/all/sync.rs
  15. 72
      crates/wasi/Cargo.toml
  16. 0
      crates/wasi/src/clocks.rs
  17. 0
      crates/wasi/src/clocks/host.rs
  18. 122
      crates/wasi/src/command.rs
  19. 2
      crates/wasi/src/ctx.rs
  20. 0
      crates/wasi/src/error.rs
  21. 8
      crates/wasi/src/filesystem.rs
  22. 6
      crates/wasi/src/host/clocks.rs
  23. 4
      crates/wasi/src/host/env.rs
  24. 2
      crates/wasi/src/host/exit.rs
  25. 14
      crates/wasi/src/host/filesystem.rs
  26. 8
      crates/wasi/src/host/filesystem/sync.rs
  27. 6
      crates/wasi/src/host/instance_network.rs
  28. 8
      crates/wasi/src/host/io.rs
  29. 0
      crates/wasi/src/host/mod.rs
  30. 18
      crates/wasi/src/host/network.rs
  31. 4
      crates/wasi/src/host/random.rs
  32. 14
      crates/wasi/src/host/tcp.rs
  33. 6
      crates/wasi/src/host/tcp_create_socket.rs
  34. 18
      crates/wasi/src/host/udp.rs
  35. 6
      crates/wasi/src/host/udp_create_socket.rs
  36. 22
      crates/wasi/src/ip_name_lookup.rs
  37. 383
      crates/wasi/src/lib.rs
  38. 6
      crates/wasi/src/network.rs
  39. 10
      crates/wasi/src/pipe.rs
  40. 8
      crates/wasi/src/poll.rs
  41. 10
      crates/wasi/src/preview0.rs
  42. 24
      crates/wasi/src/preview1.rs
  43. 122
      crates/wasi/src/preview2/command.rs
  44. 327
      crates/wasi/src/preview2/mod.rs
  45. 0
      crates/wasi/src/random.rs
  46. 10
      crates/wasi/src/stdio.rs
  47. 6
      crates/wasi/src/stdio/worker_thread_stdin.rs
  48. 4
      crates/wasi/src/stream.rs
  49. 6
      crates/wasi/src/tcp.rs
  50. 6
      crates/wasi/src/udp.rs
  51. 6
      crates/wasi/src/write_stream.rs
  52. 39
      crates/wasi/tests/all/api.rs
  53. 2
      crates/wasi/tests/all/async_.rs
  54. 2
      crates/wasi/tests/all/main.rs
  55. 2
      crates/wasi/tests/all/preview1.rs
  56. 2
      crates/wasi/tests/all/sync.rs
  57. 4
      crates/wasi/tests/process_stdin.rs
  58. 2
      examples/CMakeLists.txt
  59. 23
      examples/wasi-async/main.rs
  60. 47
      src/commands/run.rs
  61. 12
      src/commands/serve.rs

2
.github/workflows/main.yml

@ -223,7 +223,7 @@ jobs:
echo "${{ runner.tool_cache }}/mdbook/bin" >> $GITHUB_PATH
cargo install --root ${{ runner.tool_cache }}/mdbook --version ${{ env.CARGO_MDBOOK_VERSION }} mdbook
- run: (cd docs && mdbook build)
- run: cargo build -p wasmtime-wasi --features wasmtime/wat,wasmtime/cranelift
- run: cargo build -p wasi-common --features wasmtime/wat,wasmtime/cranelift
- run: (cd docs/rust_wasi_markdown_parser && cargo build)
- run: (cd docs && mdbook test -L ../target/debug/deps)

4
Cargo.lock

@ -3326,7 +3326,6 @@ dependencies = [
"wasi-common",
"wasmtime",
"wasmtime-c-api-macros",
"wasmtime-wasi",
"wat",
]
@ -3695,12 +3694,10 @@ dependencies = [
"cap-rand",
"cap-std",
"cap-time-ext",
"cfg-if",
"fs-set-times",
"futures",
"io-extras",
"io-lifetimes",
"log",
"once_cell",
"rustix",
"system-interface",
@ -3712,7 +3709,6 @@ dependencies = [
"tracing",
"tracing-subscriber",
"url",
"wasi-common",
"wasmtime",
"wiggle",
"windows-sys 0.52.0",

6
Cargo.toml

@ -35,9 +35,7 @@ wasmtime-wast = { workspace = true, optional = true }
wasi-common = { workspace = true, default-features = true, features = [
"exit",
] }
wasmtime-wasi = { workspace = true, default-features = true, features = [
"exit",
] }
wasmtime-wasi = { workspace = true, default-features = true }
wasmtime-wasi-nn = { workspace = true, optional = true }
wasmtime-wasi-threads = { workspace = true, optional = true }
wasmtime-wasi-http = { workspace = true, optional = true }
@ -394,7 +392,7 @@ harness = false
[[example]]
name = "tokio"
required-features = ["wasmtime-wasi/tokio"]
required-features = ["wasi-common/tokio"]
[[bench]]
name = "instantiation"

3
crates/c-api/Cargo.toml

@ -31,7 +31,6 @@ tracing = { workspace = true }
wat = { workspace = true, optional = true }
# Optional dependencies for the `wasi` feature
wasmtime-wasi = { workspace = true, default-features = true, optional = true }
cap-std = { workspace = true, optional = true }
wasi-common = { workspace = true, optional = true, features = ["sync"] }
@ -43,7 +42,7 @@ async = ['wasmtime/async', 'futures']
profiling = ["wasmtime/profiling"]
cache = ["wasmtime/cache"]
parallel-compilation = ['wasmtime/parallel-compilation']
wasi = ['wasmtime-wasi', 'cap-std', 'wasi-common']
wasi = ['cap-std', 'wasi-common']
logging = ['dep:env_logger']
disable-logging = ["log/max_level_off", "tracing/max_level_off"]
coredump = ["wasmtime/coredump"]

2
crates/c-api/src/linker.rs

@ -104,7 +104,7 @@ pub extern "C" fn wasmtime_linker_define_wasi(
linker: &mut wasmtime_linker_t,
) -> Option<Box<wasmtime_error_t>> {
handle_result(
wasmtime_wasi::add_to_linker(&mut linker.linker, |cx| {
wasi_common::sync::add_to_linker(&mut linker.linker, |cx| {
cx.wasi.as_mut().expect(
"failed to define WASI on linker; did you set a WASI configuration in the store?",
)

4
crates/wasi-http/Cargo.toml

@ -25,9 +25,7 @@ http = { workspace = true }
http-body = { workspace = true }
http-body-util = { workspace = true }
tracing = { workspace = true }
wasmtime-wasi = { workspace = true, default-features = false, features = [
"preview2",
] }
wasmtime-wasi = { workspace = true }
wasmtime = { workspace = true, features = ['component-model'] }
# The `ring` crate, used to implement TLS, does not build on riscv64 or s390x

4
crates/wasi-http/src/body.rs

@ -9,7 +9,7 @@ use std::mem;
use std::task::{Context, Poll};
use std::{pin::Pin, sync::Arc, time::Duration};
use tokio::sync::{mpsc, oneshot};
use wasmtime_wasi::preview2::{
use wasmtime_wasi::{
self, poll_noop, AbortOnDropJoinHandle, HostInputStream, HostOutputStream, StreamError,
Subscribe,
};
@ -36,7 +36,7 @@ impl BodyWithTimeout {
inner,
between_bytes_timeout,
reset_sleep: true,
timeout: Box::pin(preview2::with_ambient_tokio_runtime(|| {
timeout: Box::pin(wasmtime_wasi::with_ambient_tokio_runtime(|| {
tokio::time::sleep(Duration::new(0, 0))
})),
}

6
crates/wasi-http/src/lib.rs

@ -18,9 +18,9 @@ pub mod bindings {
tracing: true,
async: false,
with: {
"wasi:io/error": wasmtime_wasi::preview2::bindings::io::error,
"wasi:io/streams": wasmtime_wasi::preview2::bindings::io::streams,
"wasi:io/poll": wasmtime_wasi::preview2::bindings::io::poll,
"wasi:io/error": wasmtime_wasi::bindings::io::error,
"wasi:io/streams": wasmtime_wasi::bindings::io::streams,
"wasi:io/poll": wasmtime_wasi::bindings::io::poll,
"wasi:http/types/outgoing-body": super::body::HostOutgoingBody,
"wasi:http/types/future-incoming-response": super::types::HostFutureIncomingResponse,

69
crates/wasi-http/src/proxy.rs

@ -1,39 +1,38 @@
use crate::{bindings, WasiHttpView};
use wasmtime_wasi::preview2;
wasmtime::component::bindgen!({
world: "wasi:http/proxy",
tracing: true,
async: true,
with: {
"wasi:cli/stderr": preview2::bindings::cli::stderr,
"wasi:cli/stdin": preview2::bindings::cli::stdin,
"wasi:cli/stdout": preview2::bindings::cli::stdout,
"wasi:clocks/monotonic-clock": preview2::bindings::clocks::monotonic_clock,
"wasi:clocks/timezone": preview2::bindings::clocks::timezone,
"wasi:clocks/wall-clock": preview2::bindings::clocks::wall_clock,
"wasi:cli/stderr": wasmtime_wasi::bindings::cli::stderr,
"wasi:cli/stdin": wasmtime_wasi::bindings::cli::stdin,
"wasi:cli/stdout": wasmtime_wasi::bindings::cli::stdout,
"wasi:clocks/monotonic-clock": wasmtime_wasi::bindings::clocks::monotonic_clock,
"wasi:clocks/timezone": wasmtime_wasi::bindings::clocks::timezone,
"wasi:clocks/wall-clock": wasmtime_wasi::bindings::clocks::wall_clock,
"wasi:http/incoming-handler": bindings::http::incoming_handler,
"wasi:http/outgoing-handler": bindings::http::outgoing_handler,
"wasi:http/types": bindings::http::types,
"wasi:io/streams": preview2::bindings::io::streams,
"wasi:io/poll": preview2::bindings::io::poll,
"wasi:random/random": preview2::bindings::random::random,
"wasi:io/streams": wasmtime_wasi::bindings::io::streams,
"wasi:io/poll": wasmtime_wasi::bindings::io::poll,
"wasi:random/random": wasmtime_wasi::bindings::random::random,
},
});
pub fn add_to_linker<T>(l: &mut wasmtime::component::Linker<T>) -> anyhow::Result<()>
where
T: WasiHttpView + preview2::WasiView + bindings::http::types::Host,
T: WasiHttpView + wasmtime_wasi::WasiView + bindings::http::types::Host,
{
preview2::bindings::clocks::wall_clock::add_to_linker(l, |t| t)?;
preview2::bindings::clocks::monotonic_clock::add_to_linker(l, |t| t)?;
preview2::bindings::io::poll::add_to_linker(l, |t| t)?;
preview2::bindings::io::error::add_to_linker(l, |t| t)?;
preview2::bindings::io::streams::add_to_linker(l, |t| t)?;
preview2::bindings::cli::stdin::add_to_linker(l, |t| t)?;
preview2::bindings::cli::stdout::add_to_linker(l, |t| t)?;
preview2::bindings::cli::stderr::add_to_linker(l, |t| t)?;
preview2::bindings::random::random::add_to_linker(l, |t| t)?;
wasmtime_wasi::bindings::clocks::wall_clock::add_to_linker(l, |t| t)?;
wasmtime_wasi::bindings::clocks::monotonic_clock::add_to_linker(l, |t| t)?;
wasmtime_wasi::bindings::io::poll::add_to_linker(l, |t| t)?;
wasmtime_wasi::bindings::io::error::add_to_linker(l, |t| t)?;
wasmtime_wasi::bindings::io::streams::add_to_linker(l, |t| t)?;
wasmtime_wasi::bindings::cli::stdin::add_to_linker(l, |t| t)?;
wasmtime_wasi::bindings::cli::stdout::add_to_linker(l, |t| t)?;
wasmtime_wasi::bindings::cli::stderr::add_to_linker(l, |t| t)?;
wasmtime_wasi::bindings::random::random::add_to_linker(l, |t| t)?;
add_only_http_to_linker(l)
}
@ -41,7 +40,7 @@ where
#[doc(hidden)]
pub fn add_only_http_to_linker<T>(l: &mut wasmtime::component::Linker<T>) -> anyhow::Result<()>
where
T: WasiHttpView + preview2::WasiView + bindings::http::types::Host,
T: WasiHttpView + wasmtime_wasi::WasiView + bindings::http::types::Host,
{
bindings::http::outgoing_handler::add_to_linker(l, |t| t)?;
bindings::http::types::add_to_linker(l, |t| t)?;
@ -51,35 +50,35 @@ where
pub mod sync {
use crate::{bindings, WasiHttpView};
use wasmtime_wasi::preview2;
use wasmtime_wasi;
wasmtime::component::bindgen!({
world: "wasi:http/proxy",
tracing: true,
async: false,
with: {
"wasi:cli/stderr": preview2::bindings::cli::stderr,
"wasi:cli/stdin": preview2::bindings::cli::stdin,
"wasi:cli/stdout": preview2::bindings::cli::stdout,
"wasi:clocks/monotonic-clock": preview2::bindings::clocks::monotonic_clock,
"wasi:clocks/timezone": preview2::bindings::clocks::timezone,
"wasi:clocks/wall-clock": preview2::bindings::clocks::wall_clock,
"wasi:cli/stderr": wasmtime_wasi::bindings::cli::stderr,
"wasi:cli/stdin": wasmtime_wasi::bindings::cli::stdin,
"wasi:cli/stdout": wasmtime_wasi::bindings::cli::stdout,
"wasi:clocks/monotonic-clock": wasmtime_wasi::bindings::clocks::monotonic_clock,
"wasi:clocks/timezone": wasmtime_wasi::bindings::clocks::timezone,
"wasi:clocks/wall-clock": wasmtime_wasi::bindings::clocks::wall_clock,
"wasi:http/incoming-handler": bindings::http::incoming_handler,
"wasi:http/outgoing-handler": bindings::http::outgoing_handler,
"wasi:http/types": bindings::http::types,
"wasi:io/streams": preview2::bindings::io::streams,
"wasi:poll/poll": preview2::bindings::poll::poll,
"wasi:random/random": preview2::bindings::random::random,
"wasi:io/streams": wasmtime_wasi::bindings::io::streams,
"wasi:poll/poll": wasmtime_wasi::bindings::poll::poll,
"wasi:random/random": wasmtime_wasi::bindings::random::random,
},
});
pub fn add_to_linker<T>(l: &mut wasmtime::component::Linker<T>) -> anyhow::Result<()>
where
T: WasiHttpView + preview2::WasiView + bindings::http::types::Host,
T: WasiHttpView + wasmtime_wasi::WasiView + bindings::http::types::Host,
{
// TODO: this shouldn't be required, but the adapter unconditionally pulls in all of these
// dependencies.
preview2::command::sync::add_to_linker(l)?;
wasmtime_wasi::command::sync::add_to_linker(l)?;
add_only_http_to_linker(l)?;
@ -87,10 +86,10 @@ pub mod sync {
}
#[doc(hidden)]
// TODO: This is temporary solution until the preview2 command functions can be removed
// TODO: This is temporary solution until the wasmtime_wasi command functions can be removed
pub fn add_only_http_to_linker<T>(l: &mut wasmtime::component::Linker<T>) -> anyhow::Result<()>
where
T: WasiHttpView + preview2::WasiView + bindings::http::types::Host,
T: WasiHttpView + wasmtime_wasi::WasiView + bindings::http::types::Host,
{
bindings::http::outgoing_handler::add_to_linker(l, |t| t)?;
bindings::http::types::add_to_linker(l, |t| t)?;

8
crates/wasi-http/src/types.rs

@ -15,7 +15,7 @@ use std::time::Duration;
use tokio::net::TcpStream;
use tokio::time::timeout;
use wasmtime::component::{Resource, ResourceTable};
use wasmtime_wasi::preview2::{self, AbortOnDropJoinHandle, Subscribe};
use wasmtime_wasi::{self, AbortOnDropJoinHandle, Subscribe};
/// Capture the state necessary for use in the wasi-http API implementation.
pub struct WasiHttpCtx;
@ -122,7 +122,7 @@ pub fn default_send_request(
between_bytes_timeout,
}: OutgoingRequest,
) -> wasmtime::Result<Resource<HostFutureIncomingResponse>> {
let handle = preview2::spawn(async move {
let handle = wasmtime_wasi::spawn(async move {
let resp = handler(
authority,
use_tls,
@ -212,7 +212,7 @@ async fn handler(
.map_err(|_| types::ErrorCode::ConnectionTimeout)?
.map_err(hyper_request_error)?;
let worker = preview2::spawn(async move {
let worker = wasmtime_wasi::spawn(async move {
match conn.await {
Ok(()) => {}
// TODO: shouldn't throw away this error and ideally should
@ -234,7 +234,7 @@ async fn handler(
.map_err(|_| types::ErrorCode::ConnectionTimeout)?
.map_err(hyper_request_error)?;
let worker = preview2::spawn(async move {
let worker = wasmtime_wasi::spawn(async move {
match conn.await {
Ok(()) => {}
// TODO: same as above, shouldn't throw this error away.

6
crates/wasi-http/src/types_impl.rs

@ -12,7 +12,7 @@ use anyhow::Context;
use std::any::Any;
use std::str::FromStr;
use wasmtime::component::{Resource, ResourceTable};
use wasmtime_wasi::preview2::{
use wasmtime_wasi::{
bindings::io::streams::{InputStream, OutputStream},
Pollable,
};
@ -644,7 +644,7 @@ impl<T: WasiHttpView> crate::bindings::http::types::HostFutureTrailers for T {
&mut self,
index: Resource<HostFutureTrailers>,
) -> wasmtime::Result<Resource<Pollable>> {
wasmtime_wasi::preview2::subscribe(self.table(), index)
wasmtime_wasi::subscribe(self.table(), index)
}
fn get(
@ -852,7 +852,7 @@ impl<T: WasiHttpView> crate::bindings::http::types::HostFutureIncomingResponse f
&mut self,
id: Resource<HostFutureIncomingResponse>,
) -> wasmtime::Result<Resource<Pollable>> {
wasmtime_wasi::preview2::subscribe(self.table(), id)
wasmtime_wasi::subscribe(self.table(), id)
}
}

4
crates/wasi-http/tests/all/async_.rs

@ -1,6 +1,6 @@
use super::*;
use test_programs_artifacts::*;
use wasmtime_wasi::preview2::command::Command;
use wasmtime_wasi::command::Command;
foreach_http!(assert_test_exists);
@ -13,7 +13,7 @@ async fn run(path: &str, server: &Server) -> Result<()> {
let component = Component::from_file(&engine, path)?;
let mut store = store(&engine, server);
let mut linker = Linker::new(&engine);
wasmtime_wasi::preview2::command::add_to_linker(&mut linker)?;
wasmtime_wasi::command::add_to_linker(&mut linker)?;
wasmtime_wasi_http::proxy::add_only_http_to_linker(&mut linker)?;
let (command, _instance) = Command::instantiate_async(&mut store, &component, &linker).await?;
let result = command.wasi_cli_run().call_run(&mut store).await?;

6
crates/wasi-http/tests/all/main.rs

@ -11,7 +11,7 @@ use wasmtime::{
component::{Component, Linker, Resource, ResourceTable},
Config, Engine, Store,
};
use wasmtime_wasi::preview2::{self, pipe::MemoryOutputPipe, WasiCtx, WasiCtxBuilder, WasiView};
use wasmtime_wasi::{self, pipe::MemoryOutputPipe, WasiCtx, WasiCtxBuilder, WasiView};
use wasmtime_wasi_http::{
bindings::http::types::ErrorCode,
body::HyperIncomingBody,
@ -160,7 +160,7 @@ async fn run_wasi_http(
let (sender, receiver) = tokio::sync::oneshot::channel();
let out = store.data_mut().new_response_outparam(sender)?;
let handle = preview2::spawn(async move {
let handle = wasmtime_wasi::spawn(async move {
proxy
.wasi_http_incoming_handler()
.call_handle(&mut store, req, out)
@ -267,7 +267,7 @@ async fn do_wasi_http_hash_all(override_send_request: bool) -> Result<()> {
let response = handle(request.into_parts().0).map(|resp| {
Ok(IncomingResponseInternal {
resp,
worker: Arc::new(preview2::spawn(future::ready(()))),
worker: Arc::new(wasmtime_wasi::spawn(future::ready(()))),
between_bytes_timeout,
})
});

2
crates/wasi-http/tests/all/sync.rs

@ -1,6 +1,6 @@
use super::*;
use test_programs_artifacts::*;
use wasmtime_wasi::preview2::command::sync::Command;
use wasmtime_wasi::command::sync::Command;
foreach_http!(assert_test_exists);

72
crates/wasi/Cargo.toml

@ -15,30 +15,26 @@ include = ["src/**/*", "README.md", "LICENSE", "witx/*", "wit/**/*", "tests/*"]
workspace = true
[dependencies]
wasmtime = { workspace = true }
wasmtime = { workspace = true, features = ["component-model", "async", "runtime"] }
anyhow = { workspace = true }
wiggle = { workspace = true, optional = true, features = ["wasmtime"] }
once_cell = { workspace = true }
log = { workspace = true }
url = { workspace = true }
tokio = { workspace = true, optional = true, features = ["time", "sync", "io-std", "io-util", "rt", "rt-multi-thread", "net"] }
tokio = { workspace = true, features = ["time", "sync", "io-std", "io-util", "rt", "rt-multi-thread", "net"] }
bytes = { workspace = true }
thiserror = { workspace = true, optional = true }
tracing = { workspace = true, optional = true }
cap-std = { workspace = true, optional = true }
cap-rand = { workspace = true, optional = true }
cap-fs-ext = { workspace = true, optional = true }
cap-net-ext = { workspace = true, optional = true }
cap-time-ext = { workspace = true, optional = true }
io-lifetimes = { workspace = true, optional = true }
fs-set-times = { workspace = true, optional = true }
bitflags = { workspace = true, optional = true }
async-trait = { workspace = true, optional = true }
system-interface = { workspace = true, optional = true}
futures = { workspace = true, optional = true }
wasi-common = { workspace = true, optional = true, features = ["sync"] }
cfg-if = { workspace = true, optional = true }
thiserror = { workspace = true }
tracing = { workspace = true }
cap-std = { workspace = true }
cap-rand = { workspace = true }
cap-fs-ext = { workspace = true }
cap-net-ext = { workspace = true }
cap-time-ext = { workspace = true }
io-lifetimes = { workspace = true }
fs-set-times = { workspace = true }
bitflags = { workspace = true }
async-trait = { workspace = true }
system-interface = { workspace = true}
futures = { workspace = true }
url = { workspace = true }
once_cell = { workspace = true }
[dev-dependencies]
tokio = { workspace = true, features = ["time", "sync", "io-std", "io-util", "rt", "rt-multi-thread", "net", "macros"] }
@ -49,41 +45,17 @@ tempfile = { workspace = true }
wasmtime = { workspace = true, features = ['cranelift'] }
[target.'cfg(unix)'.dependencies]
rustix = { workspace = true, features = ["event", "fs", "net"], optional = true }
rustix = { workspace = true, features = ["event", "fs", "net"] }
[target.'cfg(windows)'.dependencies]
io-extras = { workspace = true }
windows-sys = { workspace = true }
rustix = { workspace = true, features = ["event", "net"], optional = true }
rustix = { workspace = true, features = ["event", "net"] }
[features]
default = ["wasi-common-deprecations", "preview2", "preview1-on-preview2"]
wasi-common-deprecations = [ "dep:wasi-common" ]
tokio = [ "wasi-common?/tokio", "wasi-common-deprecations" ]
exit = [ "wasi-common-deprecations", "dep:cfg-if" ]
preview2 = [
'wasmtime/component-model',
'wasmtime/async',
'wasmtime/runtime',
'dep:thiserror',
'dep:tracing',
'dep:cap-std',
'dep:cap-rand',
'dep:cap-fs-ext',
'dep:cap-net-ext',
'dep:cap-time-ext',
'dep:io-lifetimes',
'dep:fs-set-times',
'dep:bitflags',
'dep:async-trait',
'dep:system-interface',
'dep:rustix',
'dep:tokio',
'dep:futures',
]
preview1-on-preview2 = [
"preview2",
"wiggle",
default = [ "preview1"]
preview1 = [
"dep:wiggle",
]
[[test]]

0
crates/wasi/src/preview2/clocks.rs → crates/wasi/src/clocks.rs

0
crates/wasi/src/preview2/clocks/host.rs → crates/wasi/src/clocks/host.rs

122
crates/wasi/src/command.rs

@ -0,0 +1,122 @@
use crate::WasiView;
wasmtime::component::bindgen!({
world: "wasi:cli/command",
tracing: true,
async: true,
with: {
"wasi:filesystem/types": crate::bindings::filesystem::types,
"wasi:filesystem/preopens": crate::bindings::filesystem::preopens,
"wasi:sockets/tcp": crate::bindings::sockets::tcp,
"wasi:clocks/monotonic_clock": crate::bindings::clocks::monotonic_clock,
"wasi:io/poll": crate::bindings::io::poll,
"wasi:io/streams": crate::bindings::io::streams,
"wasi:clocks/wall_clock": crate::bindings::clocks::wall_clock,
"wasi:random/random": crate::bindings::random::random,
"wasi:cli/environment": crate::bindings::cli::environment,
"wasi:cli/exit": crate::bindings::cli::exit,
"wasi:cli/stdin": crate::bindings::cli::stdin,
"wasi:cli/stdout": crate::bindings::cli::stdout,
"wasi:cli/stderr": crate::bindings::cli::stderr,
"wasi:cli/terminal-input": crate::bindings::cli::terminal_input,
"wasi:cli/terminal-output": crate::bindings::cli::terminal_output,
"wasi:cli/terminal-stdin": crate::bindings::cli::terminal_stdin,
"wasi:cli/terminal-stdout": crate::bindings::cli::terminal_stdout,
"wasi:cli/terminal-stderr": crate::bindings::cli::terminal_stderr,
},
});
pub fn add_to_linker<T: WasiView>(l: &mut wasmtime::component::Linker<T>) -> anyhow::Result<()> {
crate::bindings::clocks::wall_clock::add_to_linker(l, |t| t)?;
crate::bindings::clocks::monotonic_clock::add_to_linker(l, |t| t)?;
crate::bindings::filesystem::types::add_to_linker(l, |t| t)?;
crate::bindings::filesystem::preopens::add_to_linker(l, |t| t)?;
crate::bindings::io::error::add_to_linker(l, |t| t)?;
crate::bindings::io::poll::add_to_linker(l, |t| t)?;
crate::bindings::io::streams::add_to_linker(l, |t| t)?;
crate::bindings::random::random::add_to_linker(l, |t| t)?;
crate::bindings::random::insecure::add_to_linker(l, |t| t)?;
crate::bindings::random::insecure_seed::add_to_linker(l, |t| t)?;
crate::bindings::cli::exit::add_to_linker(l, |t| t)?;
crate::bindings::cli::environment::add_to_linker(l, |t| t)?;
crate::bindings::cli::stdin::add_to_linker(l, |t| t)?;
crate::bindings::cli::stdout::add_to_linker(l, |t| t)?;
crate::bindings::cli::stderr::add_to_linker(l, |t| t)?;
crate::bindings::cli::terminal_input::add_to_linker(l, |t| t)?;
crate::bindings::cli::terminal_output::add_to_linker(l, |t| t)?;
crate::bindings::cli::terminal_stdin::add_to_linker(l, |t| t)?;
crate::bindings::cli::terminal_stdout::add_to_linker(l, |t| t)?;
crate::bindings::cli::terminal_stderr::add_to_linker(l, |t| t)?;
crate::bindings::sockets::tcp::add_to_linker(l, |t| t)?;
crate::bindings::sockets::tcp_create_socket::add_to_linker(l, |t| t)?;
crate::bindings::sockets::udp::add_to_linker(l, |t| t)?;
crate::bindings::sockets::udp_create_socket::add_to_linker(l, |t| t)?;
crate::bindings::sockets::instance_network::add_to_linker(l, |t| t)?;
crate::bindings::sockets::network::add_to_linker(l, |t| t)?;
crate::bindings::sockets::ip_name_lookup::add_to_linker(l, |t| t)?;
Ok(())
}
pub mod sync {
use crate::WasiView;
wasmtime::component::bindgen!({
world: "wasi:cli/command",
tracing: true,
async: false,
with: {
"wasi:filesystem/types": crate::bindings::sync_io::filesystem::types,
"wasi:filesystem/preopens": crate::bindings::filesystem::preopens,
"wasi:sockets/tcp": crate::bindings::sockets::tcp,
"wasi:sockets/udp": crate::bindings::sockets::udp,
"wasi:clocks/monotonic_clock": crate::bindings::clocks::monotonic_clock,
"wasi:io/poll": crate::bindings::sync_io::io::poll,
"wasi:io/streams": crate::bindings::sync_io::io::streams,
"wasi:clocks/wall_clock": crate::bindings::clocks::wall_clock,
"wasi:random/random": crate::bindings::random::random,
"wasi:cli/environment": crate::bindings::cli::environment,
"wasi:cli/exit": crate::bindings::cli::exit,
"wasi:cli/stdin": crate::bindings::cli::stdin,
"wasi:cli/stdout": crate::bindings::cli::stdout,
"wasi:cli/stderr": crate::bindings::cli::stderr,
"wasi:cli/terminal-input": crate::bindings::cli::terminal_input,
"wasi:cli/terminal-output": crate::bindings::cli::terminal_output,
"wasi:cli/terminal-stdin": crate::bindings::cli::terminal_stdin,
"wasi:cli/terminal-stdout": crate::bindings::cli::terminal_stdout,
"wasi:cli/terminal-stderr": crate::bindings::cli::terminal_stderr,
},
});
pub fn add_to_linker<T: WasiView>(
l: &mut wasmtime::component::Linker<T>,
) -> anyhow::Result<()> {
crate::bindings::clocks::wall_clock::add_to_linker(l, |t| t)?;
crate::bindings::clocks::monotonic_clock::add_to_linker(l, |t| t)?;
crate::bindings::sync_io::filesystem::types::add_to_linker(l, |t| t)?;
crate::bindings::filesystem::preopens::add_to_linker(l, |t| t)?;
crate::bindings::io::error::add_to_linker(l, |t| t)?;
crate::bindings::sync_io::io::poll::add_to_linker(l, |t| t)?;
crate::bindings::sync_io::io::streams::add_to_linker(l, |t| t)?;
crate::bindings::random::random::add_to_linker(l, |t| t)?;
crate::bindings::random::insecure::add_to_linker(l, |t| t)?;
crate::bindings::random::insecure_seed::add_to_linker(l, |t| t)?;
crate::bindings::cli::exit::add_to_linker(l, |t| t)?;
crate::bindings::cli::environment::add_to_linker(l, |t| t)?;
crate::bindings::cli::stdin::add_to_linker(l, |t| t)?;
crate::bindings::cli::stdout::add_to_linker(l, |t| t)?;
crate::bindings::cli::stderr::add_to_linker(l, |t| t)?;
crate::bindings::cli::terminal_input::add_to_linker(l, |t| t)?;
crate::bindings::cli::terminal_output::add_to_linker(l, |t| t)?;
crate::bindings::cli::terminal_stdin::add_to_linker(l, |t| t)?;
crate::bindings::cli::terminal_stdout::add_to_linker(l, |t| t)?;
crate::bindings::cli::terminal_stderr::add_to_linker(l, |t| t)?;
crate::bindings::sockets::tcp::add_to_linker(l, |t| t)?;
crate::bindings::sockets::tcp_create_socket::add_to_linker(l, |t| t)?;
crate::bindings::sockets::udp::add_to_linker(l, |t| t)?;
crate::bindings::sockets::udp_create_socket::add_to_linker(l, |t| t)?;
crate::bindings::sockets::instance_network::add_to_linker(l, |t| t)?;
crate::bindings::sockets::network::add_to_linker(l, |t| t)?;
crate::bindings::sockets::ip_name_lookup::add_to_linker(l, |t| t)?;
Ok(())
}
}

2
crates/wasi/src/preview2/ctx.rs → crates/wasi/src/ctx.rs

@ -1,4 +1,4 @@
use crate::preview2::{
use crate::{
clocks::{
host::{monotonic_clock, wall_clock},
HostMonotonicClock, HostWallClock,

0
crates/wasi/src/preview2/error.rs → crates/wasi/src/error.rs

8
crates/wasi/src/preview2/filesystem.rs → crates/wasi/src/filesystem.rs

@ -1,5 +1,5 @@
use crate::preview2::bindings::filesystem::types;
use crate::preview2::{
use crate::bindings::filesystem::types;
use crate::{
spawn_blocking, AbortOnDropJoinHandle, HostOutputStream, StreamError, Subscribe, TrappableError,
};
use anyhow::anyhow;
@ -85,7 +85,7 @@ pub struct File {
/// [`spawn_blocking`]: Self::spawn_blocking
pub file: Arc<cap_std::fs::File>,
/// Permissions to enforce on access to the file. These permissions are
/// specified by a user of the `crate::preview2::WasiCtxBuilder`, and are
/// specified by a user of the `crate::WasiCtxBuilder`, and are
/// enforced prior to any enforced by the underlying operating system.
pub perms: FilePerms,
/// The mode the file was opened under: bits for reading, and writing.
@ -134,7 +134,7 @@ pub struct Dir {
/// [`spawn_blocking`]: Self::spawn_blocking
pub dir: Arc<cap_std::fs::Dir>,
/// Permissions to enforce on access to this directory. These permissions
/// are specified by a user of the `crate::preview2::WasiCtxBuilder`, and
/// are specified by a user of the `crate::WasiCtxBuilder`, and
/// are enforced prior to any enforced by the underlying operating system.
///
/// These permissions are also enforced on any directories opened under

6
crates/wasi/src/preview2/host/clocks.rs → crates/wasi/src/host/clocks.rs

@ -1,11 +1,11 @@
#![allow(unused_variables)]
use crate::preview2::bindings::{
use crate::bindings::{
clocks::monotonic_clock::{self, Duration as WasiDuration, Instant},
clocks::wall_clock::{self, Datetime},
};
use crate::preview2::poll::{subscribe, Subscribe};
use crate::preview2::{Pollable, WasiView};
use crate::poll::{subscribe, Subscribe};
use crate::{Pollable, WasiView};
use cap_std::time::SystemTime;
use std::time::Duration;
use wasmtime::component::Resource;

4
crates/wasi/src/preview2/host/env.rs → crates/wasi/src/host/env.rs

@ -1,5 +1,5 @@
use crate::preview2::bindings::cli::environment;
use crate::preview2::WasiView;
use crate::bindings::cli::environment;
use crate::WasiView;
impl<T: WasiView> environment::Host for T {
fn get_environment(&mut self) -> anyhow::Result<Vec<(String, String)>> {

2
crates/wasi/src/preview2/host/exit.rs → crates/wasi/src/host/exit.rs

@ -1,4 +1,4 @@
use crate::preview2::{bindings::cli::exit, I32Exit, WasiView};
use crate::{bindings::cli::exit, I32Exit, WasiView};
impl<T: WasiView> exit::Host for T {
fn exit(&mut self, status: Result<(), ()>) -> anyhow::Result<()> {

14
crates/wasi/src/preview2/host/filesystem.rs → crates/wasi/src/host/filesystem.rs

@ -1,13 +1,13 @@
use crate::preview2::bindings::clocks::wall_clock;
use crate::preview2::bindings::filesystem::preopens;
use crate::preview2::bindings::filesystem::types::{
use crate::bindings::clocks::wall_clock;
use crate::bindings::filesystem::preopens;
use crate::bindings::filesystem::types::{
self, ErrorCode, HostDescriptor, HostDirectoryEntryStream,
};
use crate::preview2::bindings::io::streams::{InputStream, OutputStream};
use crate::preview2::filesystem::{
use crate::bindings::io::streams::{InputStream, OutputStream};
use crate::filesystem::{
Descriptor, Dir, File, FileInputStream, FileOutputStream, OpenMode, ReaddirIterator,
};
use crate::preview2::{DirPerms, FilePerms, FsError, FsResult, WasiView};
use crate::{DirPerms, FilePerms, FsError, FsResult, WasiView};
use anyhow::Context;
use wasmtime::component::Resource;
@ -982,7 +982,7 @@ impl<'a> From<&'a std::io::Error> for ErrorCode {
match from_raw_os_error(err.raw_os_error()) {
Some(errno) => errno,
None => {
log::debug!("unknown raw os error: {err}");
tracing::debug!("unknown raw os error: {err}");
match err.kind() {
std::io::ErrorKind::NotFound => ErrorCode::NoEntry,
std::io::ErrorKind::PermissionDenied => ErrorCode::NotPermitted,

8
crates/wasi/src/preview2/host/filesystem/sync.rs → crates/wasi/src/host/filesystem/sync.rs

@ -1,7 +1,7 @@
use crate::preview2::bindings::filesystem::types as async_filesystem;
use crate::preview2::bindings::sync_io::filesystem::types as sync_filesystem;
use crate::preview2::bindings::sync_io::io::streams;
use crate::preview2::{in_tokio, FsError, FsResult};
use crate::bindings::filesystem::types as async_filesystem;
use crate::bindings::sync_io::filesystem::types as sync_filesystem;
use crate::bindings::sync_io::io::streams;
use crate::{in_tokio, FsError, FsResult};
use wasmtime::component::Resource;
impl<T: async_filesystem::Host> sync_filesystem::Host for T {

6
crates/wasi/src/preview2/host/instance_network.rs → crates/wasi/src/host/instance_network.rs

@ -1,6 +1,6 @@
use crate::preview2::bindings::sockets::instance_network;
use crate::preview2::network::Network;
use crate::preview2::WasiView;
use crate::bindings::sockets::instance_network;
use crate::network::Network;
use crate::WasiView;
use wasmtime::component::Resource;
impl<T: WasiView> instance_network::Host for T {

8
crates/wasi/src/preview2/host/io.rs → crates/wasi/src/host/io.rs

@ -1,4 +1,4 @@
use crate::preview2::{
use crate::{
bindings::io::error,
bindings::io::streams::{self, InputStream, OutputStream},
poll::subscribe,
@ -161,7 +161,7 @@ impl<T: WasiView> streams::HostOutputStream for T {
src: Resource<InputStream>,
len: u64,
) -> StreamResult<u64> {
use crate::preview2::Subscribe;
use crate::Subscribe;
self.table().get_mut(&dest)?.ready().await;
@ -220,12 +220,12 @@ impl<T: WasiView> streams::HostInputStream for T {
}
fn subscribe(&mut self, stream: Resource<InputStream>) -> anyhow::Result<Resource<Pollable>> {
crate::preview2::poll::subscribe(self.table(), stream)
crate::poll::subscribe(self.table(), stream)
}
}
pub mod sync {
use crate::preview2::{
use crate::{
bindings::io::streams::{
self as async_streams, Host as AsyncHost, HostInputStream as AsyncHostInputStream,
HostOutputStream as AsyncHostOutputStream,

0
crates/wasi/src/preview2/host/mod.rs → crates/wasi/src/host/mod.rs

18
crates/wasi/src/preview2/host/network.rs → crates/wasi/src/host/network.rs

@ -1,9 +1,9 @@
use crate::preview2::bindings::sockets::network::{
use crate::bindings::sockets::network::{
self, ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, Ipv4SocketAddress,
Ipv6SocketAddress,
};
use crate::preview2::network::{from_ipv4_addr, from_ipv6_addr, to_ipv4_addr, to_ipv6_addr};
use crate::preview2::{SocketError, WasiView};
use crate::network::{from_ipv4_addr, from_ipv6_addr, to_ipv4_addr, to_ipv6_addr};
use crate::{SocketError, WasiView};
use rustix::io::Errno;
use std::io;
use wasmtime::component::Resource;
@ -14,7 +14,7 @@ impl<T: WasiView> network::Host for T {
}
}
impl<T: WasiView> crate::preview2::bindings::sockets::network::HostNetwork for T {
impl<T: WasiView> crate::bindings::sockets::network::HostNetwork for T {
fn drop(&mut self, this: Resource<network::Network>) -> Result<(), anyhow::Error> {
let table = self.table();
@ -47,7 +47,7 @@ impl From<io::Error> for ErrorCode {
std::io::ErrorKind::WouldBlock => ErrorCode::WouldBlock,
_ => {
log::debug!("unknown I/O error: {value}");
tracing::debug!("unknown I/O error: {value}");
ErrorCode::Unknown
}
}
@ -98,7 +98,7 @@ impl From<Errno> for ErrorCode {
// FYI, EINPROGRESS should have already been handled by connect.
_ => {
log::debug!("unknown I/O error: {value}");
tracing::debug!("unknown I/O error: {value}");
ErrorCode::Unknown
}
}
@ -215,9 +215,9 @@ pub(crate) mod util {
use std::net::{IpAddr, Ipv6Addr, SocketAddr};
use std::time::Duration;
use crate::preview2::bindings::sockets::network::ErrorCode;
use crate::preview2::network::SocketAddressFamily;
use crate::preview2::SocketResult;
use crate::bindings::sockets::network::ErrorCode;
use crate::network::SocketAddressFamily;
use crate::SocketResult;
use cap_net_ext::{AddressFamily, Blocking, UdpSocketExt};
use rustix::fd::{AsFd, OwnedFd};
use rustix::io::Errno;

4
crates/wasi/src/preview2/host/random.rs → crates/wasi/src/host/random.rs

@ -1,5 +1,5 @@
use crate::preview2::bindings::random::{insecure, insecure_seed, random};
use crate::preview2::WasiView;
use crate::bindings::random::{insecure, insecure_seed, random};
use crate::WasiView;
use cap_rand::{distributions::Standard, Rng};
impl<T: WasiView> random::Host for T {

14
crates/wasi/src/preview2/host/tcp.rs → crates/wasi/src/host/tcp.rs

@ -1,7 +1,7 @@
use crate::preview2::host::network::util;
use crate::preview2::network::SocketAddrUse;
use crate::preview2::tcp::{TcpReadStream, TcpSocket, TcpState, TcpWriteStream};
use crate::preview2::{
use crate::host::network::util;
use crate::network::SocketAddrUse;
use crate::tcp::{TcpReadStream, TcpSocket, TcpState, TcpWriteStream};
use crate::{
bindings::{
io::streams::{InputStream, OutputStream},
sockets::network::{ErrorCode, IpAddressFamily, IpSocketAddress, Network},
@ -9,7 +9,7 @@ use crate::preview2::{
},
network::SocketAddressFamily,
};
use crate::preview2::{with_ambient_tokio_runtime, Pollable, SocketResult, WasiView};
use crate::{with_ambient_tokio_runtime, Pollable, SocketResult, WasiView};
use io_lifetimes::AsSocketlike;
use rustix::io::Errno;
use rustix::net::sockopt;
@ -21,7 +21,7 @@ use wasmtime::component::Resource;
impl<T: WasiView> tcp::Host for T {}
impl<T: WasiView> crate::preview2::host::tcp::tcp::HostTcpSocket for T {
impl<T: WasiView> crate::host::tcp::tcp::HostTcpSocket for T {
fn start_bind(
&mut self,
this: Resource<tcp::TcpSocket>,
@ -636,7 +636,7 @@ impl<T: WasiView> crate::preview2::host::tcp::tcp::HostTcpSocket for T {
}
fn subscribe(&mut self, this: Resource<tcp::TcpSocket>) -> anyhow::Result<Resource<Pollable>> {
crate::preview2::poll::subscribe(self.table(), this)
crate::poll::subscribe(self.table(), this)
}
fn shutdown(

6
crates/wasi/src/preview2/host/tcp_create_socket.rs → crates/wasi/src/host/tcp_create_socket.rs

@ -1,6 +1,6 @@
use crate::preview2::bindings::{sockets::network::IpAddressFamily, sockets::tcp_create_socket};
use crate::preview2::tcp::TcpSocket;
use crate::preview2::{SocketResult, WasiView};
use crate::bindings::{sockets::network::IpAddressFamily, sockets::tcp_create_socket};
use crate::tcp::TcpSocket;
use crate::{SocketResult, WasiView};
use wasmtime::component::Resource;
impl<T: WasiView> tcp_create_socket::Host for T {

18
crates/wasi/src/preview2/host/udp.rs → crates/wasi/src/host/udp.rs

@ -1,6 +1,6 @@
use crate::preview2::host::network::util;
use crate::preview2::network::{SocketAddrUse, SocketAddressFamily};
use crate::preview2::{
use crate::host::network::util;
use crate::network::{SocketAddrUse, SocketAddressFamily};
use crate::{
bindings::{
sockets::network::{ErrorCode, IpAddressFamily, IpSocketAddress, Network},
sockets::udp,
@ -8,7 +8,7 @@ use crate::preview2::{
udp::{IncomingDatagramStream, OutgoingDatagramStream, SendState, UdpState},
Subscribe,
};
use crate::preview2::{Pollable, SocketError, SocketResult, WasiView};
use crate::{Pollable, SocketError, SocketResult, WasiView};
use anyhow::anyhow;
use async_trait::async_trait;
use io_lifetimes::AsSocketlike;
@ -139,7 +139,9 @@ impl<T: WasiView> udp::HostUdpSocket for T {
|error| match error {
Errno::AFNOSUPPORT => ErrorCode::InvalidArgument, // See `bind` implementation.
Errno::INPROGRESS => {
log::debug!("UDP connect returned EINPROGRESS, which should never happen");
tracing::debug!(
"UDP connect returned EINPROGRESS, which should never happen"
);
ErrorCode::Unknown
}
_ => ErrorCode::from(error),
@ -283,7 +285,7 @@ impl<T: WasiView> udp::HostUdpSocket for T {
}
fn subscribe(&mut self, this: Resource<udp::UdpSocket>) -> anyhow::Result<Resource<Pollable>> {
crate::preview2::poll::subscribe(self.table(), this)
crate::poll::subscribe(self.table(), this)
}
fn drop(&mut self, this: Resource<udp::UdpSocket>) -> Result<(), anyhow::Error> {
@ -363,7 +365,7 @@ impl<T: WasiView> udp::HostIncomingDatagramStream for T {
&mut self,
this: Resource<udp::IncomingDatagramStream>,
) -> anyhow::Result<Resource<Pollable>> {
crate::preview2::poll::subscribe(self.table(), this)
crate::poll::subscribe(self.table(), this)
}
fn drop(&mut self, this: Resource<udp::IncomingDatagramStream>) -> Result<(), anyhow::Error> {
@ -497,7 +499,7 @@ impl<T: WasiView> udp::HostOutgoingDatagramStream for T {
&mut self,
this: Resource<udp::OutgoingDatagramStream>,
) -> anyhow::Result<Resource<Pollable>> {
crate::preview2::poll::subscribe(self.table(), this)
crate::poll::subscribe(self.table(), this)
}
fn drop(&mut self, this: Resource<udp::OutgoingDatagramStream>) -> Result<(), anyhow::Error> {

6
crates/wasi/src/preview2/host/udp_create_socket.rs → crates/wasi/src/host/udp_create_socket.rs

@ -1,6 +1,6 @@
use crate::preview2::bindings::{sockets::network::IpAddressFamily, sockets::udp_create_socket};
use crate::preview2::udp::UdpSocket;
use crate::preview2::{SocketResult, WasiView};
use crate::bindings::{sockets::network::IpAddressFamily, sockets::udp_create_socket};
use crate::udp::UdpSocket;
use crate::{SocketResult, WasiView};
use wasmtime::component::Resource;
impl<T: WasiView> udp_create_socket::Host for T {

22
crates/wasi/src/preview2/ip_name_lookup.rs → crates/wasi/src/ip_name_lookup.rs

@ -1,8 +1,8 @@
use crate::preview2::bindings::sockets::ip_name_lookup::{Host, HostResolveAddressStream};
use crate::preview2::bindings::sockets::network::{ErrorCode, IpAddress, Network};
use crate::preview2::host::network::util;
use crate::preview2::poll::{subscribe, Pollable, Subscribe};
use crate::preview2::{spawn_blocking, AbortOnDropJoinHandle, SocketError, WasiView};
use crate::bindings::sockets::ip_name_lookup::{Host, HostResolveAddressStream};
use crate::bindings::sockets::network::{ErrorCode, IpAddress, Network};
use crate::host::network::util;
use crate::poll::{subscribe, Pollable, Subscribe};
use crate::{spawn_blocking, AbortOnDropJoinHandle, SocketError, WasiView};
use anyhow::Result;
use std::mem;
use std::net::{Ipv6Addr, ToSocketAddrs};
@ -48,14 +48,12 @@ impl<T: WasiView> HostResolveAddressStream for T {
let stream: &mut ResolveAddressStream = self.table().get_mut(&resource)?;
loop {
match stream {
ResolveAddressStream::Waiting(future) => {
match crate::preview2::poll_noop(Pin::new(future)) {
Some(result) => {
*stream = ResolveAddressStream::Done(result.map(|v| v.into_iter()));
}
None => return Err(ErrorCode::WouldBlock.into()),
ResolveAddressStream::Waiting(future) => match crate::poll_noop(Pin::new(future)) {
Some(result) => {
*stream = ResolveAddressStream::Done(result.map(|v| v.into_iter()));
}
}
None => return Err(ErrorCode::WouldBlock.into()),
},
ResolveAddressStream::Done(slot @ Err(_)) => {
mem::replace(slot, Ok(Vec::new().into_iter()))?;
unreachable!();

383
crates/wasi/src/lib.rs

@ -1,103 +1,318 @@
//! `wasmtime-wasi` now supports using multiple snapshots to interface to the
//! same `WasiCtx`!
//! # Wasmtime's WASI Implementation
//!
//! This crate provides a Wasmtime host implementation of WASI 0.2 (aka
//! Preview 2), and a compatibility shim that provides an implementation of
//! WASI 0.1 (aka Preview 1).
//!
//! `wasmtime_wasi::Wasi::new(&Store, WasiCtx)` is a struct which owns your
//! `WasiCtx` and provides linkage to every available snapshot.
//!
//! Individual snapshots are available through
//! `wasmtime_wasi::snapshots::preview_{0, 1}::Wasi::new(&Store, Rc<RefCell<WasiCtx>>)`.
#![warn(clippy::cast_sign_loss)]
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
#[cfg(feature = "preview2")]
pub mod preview2;
mod clocks;
pub mod command;
mod ctx;
mod error;
mod filesystem;
mod host;
mod ip_name_lookup;
mod network;
pub mod pipe;
mod poll;
#[cfg(feature = "preview1")]
pub mod preview0;
#[cfg(feature = "preview1")]
pub mod preview1;
mod random;
mod stdio;
mod stream;
mod tcp;
mod udp;
mod write_stream;
#[cfg(feature = "wasi-common-deprecations")]
#[deprecated(
since = "18.0.0",
note = "The wasmtime_wasi::sync module's functionalty has been moved to
wasi_common::sync. This re-export will be removed from wasmtime_wasi in
19.0"
)]
pub mod sync {
pub use wasi_common::sync::*;
}
pub use self::clocks::{HostMonotonicClock, HostWallClock};
pub use self::ctx::{WasiCtx, WasiCtxBuilder, WasiView};
pub use self::error::{I32Exit, TrappableError};
pub use self::filesystem::{DirPerms, FilePerms, FsError, FsResult};
pub use self::network::{Network, SocketError, SocketResult};
pub use self::poll::{subscribe, ClosureFuture, MakeFuture, Pollable, PollableFuture, Subscribe};
pub use self::random::{thread_rng, Deterministic};
pub use self::stdio::{
stderr, stdin, stdout, IsATTY, Stderr, Stdin, StdinStream, Stdout, StdoutStream,
};
pub use self::stream::{
HostInputStream, HostOutputStream, InputStream, OutputStream, StreamError, StreamResult,
};
pub use cap_fs_ext::SystemTimeSpec;
pub use cap_rand::RngCore;
pub use wasmtime::component::{ResourceTable, ResourceTableError};
pub mod bindings {
// Generate traits for synchronous bindings.
//
// Note that this is only done for interfaces which can block, or those which
// have some functions in `only_imports` below for being async.
pub mod sync_io {
pub(crate) mod _internal {
use crate::{FsError, StreamError};
wasmtime::component::bindgen!({
path: "wit",
interfaces: "
import wasi:io/poll@0.2.0;
import wasi:io/streams@0.2.0;
import wasi:filesystem/types@0.2.0;
",
tracing: true,
trappable_error_type: {
"wasi:io/streams/stream-error" => StreamError,
"wasi:filesystem/types/error-code" => FsError,
},
with: {
"wasi:clocks/wall-clock": crate::bindings::clocks::wall_clock,
"wasi:filesystem/types/descriptor": super::super::filesystem::types::Descriptor,
"wasi:filesystem/types/directory-entry-stream": super::super::filesystem::types::DirectoryEntryStream,
"wasi:io/poll/pollable": super::super::io::poll::Pollable,
"wasi:io/streams/input-stream": super::super::io::streams::InputStream,
"wasi:io/streams/output-stream": super::super::io::streams::OutputStream,
"wasi:io/error/error": super::super::io::error::Error,
}
});
}
pub use self::_internal::wasi::{filesystem, io};
}
#[cfg(feature = "wasi-common-deprecations")]
#[allow(deprecated)] // Satisfy linter locally
#[deprecated(
since = "18.0.0",
note = "The wasmtime_wasi module's root export of wasmtime_wasi::sync has
been moved to wasi_common::sync. This re-export will be removed from
wasmtime_wasi in 19.0"
)]
pub use sync::*;
wasmtime::component::bindgen!({
path: "wit",
world: "wasi:cli/imports",
tracing: true,
async: {
// Only these functions are `async` and everything else is sync
// meaning that it basically doesn't need to block. These functions
// are the only ones that need to block.
//
// Note that at this time `only_imports` works on function names
// which in theory can be shared across interfaces, so this may
// need fancier syntax in the future.
only_imports: [
"[method]descriptor.access-at",
"[method]descriptor.advise",
"[method]descriptor.change-directory-permissions-at",
"[method]descriptor.change-file-permissions-at",
"[method]descriptor.create-directory-at",
"[method]descriptor.get-flags",
"[method]descriptor.get-type",
"[method]descriptor.is-same-object",
"[method]descriptor.link-at",
"[method]descriptor.lock-exclusive",
"[method]descriptor.lock-shared",
"[method]descriptor.metadata-hash",
"[method]descriptor.metadata-hash-at",
"[method]descriptor.open-at",
"[method]descriptor.read",
"[method]descriptor.read-directory",
"[method]descriptor.readlink-at",
"[method]descriptor.remove-directory-at",
"[method]descriptor.rename-at",
"[method]descriptor.set-size",
"[method]descriptor.set-times",
"[method]descriptor.set-times-at",
"[method]descriptor.stat",
"[method]descriptor.stat-at",
"[method]descriptor.symlink-at",
"[method]descriptor.sync",
"[method]descriptor.sync-data",
"[method]descriptor.try-lock-exclusive",
"[method]descriptor.try-lock-shared",
"[method]descriptor.unlink-file-at",
"[method]descriptor.unlock",
"[method]descriptor.write",
"[method]input-stream.read",
"[method]input-stream.blocking-read",
"[method]input-stream.blocking-skip",
"[method]input-stream.skip",
"[method]output-stream.forward",
"[method]output-stream.splice",
"[method]output-stream.blocking-splice",
"[method]output-stream.blocking-flush",
"[method]output-stream.blocking-write",
"[method]output-stream.blocking-write-and-flush",
"[method]output-stream.blocking-write-zeroes-and-flush",
"[method]directory-entry-stream.read-directory-entry",
"poll",
"[method]pollable.block",
"[method]pollable.ready",
],
},
trappable_error_type: {
"wasi:io/streams/stream-error" => crate::StreamError,
"wasi:filesystem/types/error-code" => crate::FsError,
"wasi:sockets/network/error-code" => crate::SocketError,
},
with: {
"wasi:sockets/network/network": super::network::Network,
"wasi:sockets/tcp/tcp-socket": super::tcp::TcpSocket,
"wasi:sockets/udp/udp-socket": super::udp::UdpSocket,
"wasi:sockets/udp/incoming-datagram-stream": super::udp::IncomingDatagramStream,
"wasi:sockets/udp/outgoing-datagram-stream": super::udp::OutgoingDatagramStream,
"wasi:sockets/ip-name-lookup/resolve-address-stream": super::ip_name_lookup::ResolveAddressStream,
"wasi:filesystem/types/directory-entry-stream": super::filesystem::ReaddirIterator,
"wasi:filesystem/types/descriptor": super::filesystem::Descriptor,
"wasi:io/streams/input-stream": super::stream::InputStream,
"wasi:io/streams/output-stream": super::stream::OutputStream,
"wasi:io/error/error": super::stream::Error,
"wasi:io/poll/pollable": super::poll::Pollable,
"wasi:cli/terminal-input/terminal-input": super::stdio::TerminalInput,
"wasi:cli/terminal-output/terminal-output": super::stdio::TerminalOutput,
},
});
#[cfg(feature = "tokio")]
#[deprecated(
since = "18.0.0",
note = "The wasmtime_wasi::tokio module's functionalty has been moved to
wasi_common::tokio. This re-export will be removed from wasmtime_wasi in
19.0"
)]
pub mod tokio {
pub use wasi_common::tokio::*;
pub use wasi::*;
}
/// Exit the process with a conventional OS error code as long as Wasmtime
/// understands the error. If the error is not an `I32Exit` or `Trap`, return
/// the error back to the caller for it to decide what to do.
///
/// Note: this function is designed for usage where it is acceptable for
/// Wasmtime failures to terminate the parent process, such as in the Wasmtime
/// CLI; this would not be suitable for use in multi-tenant embeddings.
#[cfg(feature = "exit")]
#[deprecated(
since = "18.0.0",
note = "This legacy functionality is migrated to the wasi-common crate, and will be removed in 19.0."
)]
pub fn maybe_exit_on_error(e: anyhow::Error) -> anyhow::Error {
use std::process;
use wasmtime::Trap;
pub(crate) static RUNTIME: once_cell::sync::Lazy<tokio::runtime::Runtime> =
once_cell::sync::Lazy::new(|| {
tokio::runtime::Builder::new_current_thread()
.enable_time()
.enable_io()
.build()
.unwrap()
});
if let Some(exit) = e
.downcast_ref::<preview2::I32Exit>()
.map(|exit| exit.process_exit_code())
{
process::exit(exit)
pub struct AbortOnDropJoinHandle<T>(tokio::task::JoinHandle<T>);
impl<T> Drop for AbortOnDropJoinHandle<T> {
fn drop(&mut self) {
self.0.abort()
}
// If a specific WASI error code was requested then that's
// forwarded through to the process here without printing any
// extra error information.
let code = e.downcast_ref::<wasi_common::I32Exit>().map(|e| e.0);
if let Some(exit) = code {
// Print the error message in the usual way.
// On Windows, exit status 3 indicates an abort (see below),
// so return 1 indicating a non-zero status to avoid ambiguity.
if cfg!(windows) && exit >= 3 {
process::exit(1);
}
impl<T> std::ops::Deref for AbortOnDropJoinHandle<T> {
type Target = tokio::task::JoinHandle<T>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> std::ops::DerefMut for AbortOnDropJoinHandle<T> {
fn deref_mut(&mut self) -> &mut tokio::task::JoinHandle<T> {
&mut self.0
}
}
impl<T> From<tokio::task::JoinHandle<T>> for AbortOnDropJoinHandle<T> {
fn from(jh: tokio::task::JoinHandle<T>) -> Self {
AbortOnDropJoinHandle(jh)
}
}
impl<T> Future for AbortOnDropJoinHandle<T> {
type Output = T;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match Pin::new(&mut self.as_mut().0).poll(cx) {
Poll::Pending => Poll::Pending,
Poll::Ready(r) => Poll::Ready(r.expect("child task panicked")),
}
process::exit(exit);
}
}
pub fn spawn<F>(f: F) -> AbortOnDropJoinHandle<F::Output>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
let j = with_ambient_tokio_runtime(|| tokio::task::spawn(f));
AbortOnDropJoinHandle(j)
}
// If the program exited because of a trap, return an error code
// to the outside environment indicating a more severe problem
// than a simple failure.
if e.is::<Trap>() {
eprintln!("Error: {:?}", e);
pub fn spawn_blocking<F, R>(f: F) -> AbortOnDropJoinHandle<R>
where
F: FnOnce() -> R + Send + 'static,
R: Send + 'static,
{
let j = with_ambient_tokio_runtime(|| tokio::task::spawn_blocking(f));
AbortOnDropJoinHandle(j)
}
pub fn in_tokio<F: Future>(f: F) -> F::Output {
match tokio::runtime::Handle::try_current() {
Ok(h) => {
let _enter = h.enter();
h.block_on(f)
}
// The `yield_now` here is non-obvious and if you're reading this
// you're likely curious about why it's here. This is currently required
// to get some features of "sync mode" working correctly, such as with
// the CLI. To illustrate why this is required, consider a program
// organized as:
//
// * A program has a `pollable` that it's waiting on.
// * This `pollable` is always ready .
// * Actually making the corresponding operation ready, however,
// requires some background work on Tokio's part.
// * The program is looping on "wait for readiness" coupled with
// performing the operation.
//
// In this situation this program ends up infinitely looping in waiting
// for pollables. The reason appears to be that when we enter the tokio
// runtime here it doesn't necessary yield to background work because
// the provided future `f` is ready immediately. The future `f` will run
// through the list of pollables and determine one of them is ready.
//
// Historically this happened with UDP sockets. A test send a datagram
// from one socket to another and the other socket infinitely didn't
// receive the data. This appeared to be because the server socket was
// waiting on `READABLE | WRITABLE` (which is itself a bug but ignore
// that) and the socket was currently in the "writable" state but never
// ended up receiving a notification for the "readable" state. Moving
// the socket to "readable" would require Tokio to perform some
// background work via epoll/kqueue/handle events but if the future
// provided here is always ready, then that never happened.
//
// Thus the `yield_now()` is an attempt to force Tokio to go do some
// background work eventually and look at new interest masks for
// example. This is a bit of a kludge but everything's already a bit
// wonky in synchronous mode anyway. Note that this is hypothesized to
// not be an issue in async mode because async mode typically has the
// Tokio runtime in a separate thread or otherwise participating in a
// larger application, it's only here in synchronous mode where we
// effectively own the runtime that we need some special care.
Err(_) => {
let _enter = RUNTIME.enter();
RUNTIME.block_on(async move {
tokio::task::yield_now().await;
f.await
})
}
}
}
cfg_if::cfg_if! {
if #[cfg(unix)] {
// On Unix, return the error code of an abort.
process::exit(rustix::process::EXIT_SIGNALED_SIGABRT);
} else if #[cfg(windows)] {
// On Windows, return 3.
// https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/abort?view=vs-2019
process::exit(3);
}
/// Executes the closure `f` with an "ambient Tokio runtime" which basically
/// means that if code in `f` tries to get a runtime `Handle` it'll succeed.
///
/// If a `Handle` is already available, e.g. in async contexts, then `f` is run
/// immediately. Otherwise for synchronous contexts this crate's fallback
/// runtime is configured and then `f` is executed.
pub fn with_ambient_tokio_runtime<R>(f: impl FnOnce() -> R) -> R {
match tokio::runtime::Handle::try_current() {
Ok(_) => f(),
Err(_) => {
let _enter = RUNTIME.enter();
f()
}
}
}
e
/// Attempts to get the result of a `future`.
///
/// This function does not block and will poll the provided future once. If the
/// result is here then `Some` is returned, otherwise `None` is returned.
///
/// Note that by polling `future` this means that `future` must be re-polled
/// later if it's to wake up a task.
pub fn poll_noop<F>(future: Pin<&mut F>) -> Option<F::Output>
where
F: Future,
{
let mut task = Context::from_waker(futures::task::noop_waker_ref());
match future.poll(&mut task) {
Poll::Ready(result) => Some(result),
Poll::Pending => None,
}
}

6
crates/wasi/src/preview2/network.rs → crates/wasi/src/network.rs

@ -1,6 +1,6 @@
use crate::preview2::bindings::sockets::network::{Ipv4Address, Ipv6Address};
use crate::preview2::bindings::wasi::sockets::network::ErrorCode;
use crate::preview2::TrappableError;
use crate::bindings::sockets::network::{Ipv4Address, Ipv6Address};
use crate::bindings::wasi::sockets::network::ErrorCode;
use crate::TrappableError;
use std::net::SocketAddr;
use std::sync::Arc;

10
crates/wasi/src/preview2/pipe.rs → crates/wasi/src/pipe.rs

@ -7,14 +7,14 @@
//! Some convenience constructors are included for common backing types like `Vec<u8>` and `String`,
//! but the virtual pipes can be instantiated with any `Read` or `Write` type.
//!
use crate::preview2::poll::Subscribe;
use crate::preview2::{HostInputStream, HostOutputStream, StreamError};
use crate::poll::Subscribe;
use crate::{HostInputStream, HostOutputStream, StreamError};
use anyhow::anyhow;
use bytes::Bytes;
use std::sync::{Arc, Mutex};
use tokio::sync::mpsc;
pub use crate::preview2::write_stream::AsyncWriteStream;
pub use crate::write_stream::AsyncWriteStream;
#[derive(Debug, Clone)]
pub struct MemoryInputPipe {
@ -112,7 +112,7 @@ pub struct AsyncReadStream {
closed: bool,
buffer: Option<Result<Bytes, StreamError>>,
receiver: mpsc::Receiver<Result<Bytes, StreamError>>,
_join_handle: crate::preview2::AbortOnDropJoinHandle<()>,
_join_handle: crate::AbortOnDropJoinHandle<()>,
}
impl AsyncReadStream {
@ -120,7 +120,7 @@ impl AsyncReadStream {
/// provided by this struct, the argument must impl [`tokio::io::AsyncRead`].
pub fn new<T: tokio::io::AsyncRead + Send + Unpin + 'static>(mut reader: T) -> Self {
let (sender, receiver) = mpsc::channel(1);
let join_handle = crate::preview2::spawn(async move {
let join_handle = crate::spawn(async move {
loop {
use tokio::io::AsyncReadExt;
let mut buf = bytes::BytesMut::with_capacity(4096);

8
crates/wasi/src/preview2/poll.rs → crates/wasi/src/poll.rs

@ -1,4 +1,4 @@
use crate::preview2::{bindings::io::poll, WasiView};
use crate::{bindings::io::poll, WasiView};
use anyhow::{anyhow, Result};
use std::any::Any;
use std::collections::HashMap;
@ -123,7 +123,7 @@ impl<T: WasiView> poll::Host for T {
}
#[async_trait::async_trait]
impl<T: WasiView> crate::preview2::bindings::io::poll::HostPollable for T {
impl<T: WasiView> crate::bindings::io::poll::HostPollable for T {
async fn block(&mut self, pollable: Resource<Pollable>) -> Result<()> {
let table = self.table();
let pollable = table.get(&pollable)?;
@ -151,7 +151,7 @@ impl<T: WasiView> crate::preview2::bindings::io::poll::HostPollable for T {
}
pub mod sync {
use crate::preview2::{
use crate::{
bindings::io::poll as async_poll,
bindings::sync_io::io::poll::{self, Pollable},
in_tokio, WasiView,
@ -165,7 +165,7 @@ pub mod sync {
}
}
impl<T: WasiView> crate::preview2::bindings::sync_io::io::poll::HostPollable for T {
impl<T: WasiView> crate::bindings::sync_io::io::poll::HostPollable for T {
fn ready(&mut self, pollable: Resource<Pollable>) -> Result<bool> {
in_tokio(async { async_poll::HostPollable::ready(self, pollable).await })
}

10
crates/wasi/src/preview2/preview0.rs → crates/wasi/src/preview0.rs

@ -1,7 +1,7 @@
use crate::preview2::preview0::types::Error;
use crate::preview2::preview1::types as snapshot1_types;
use crate::preview2::preview1::wasi_snapshot_preview1::WasiSnapshotPreview1 as Snapshot1;
use crate::preview2::preview1::WasiPreview1View;
use crate::preview0::types::Error;
use crate::preview1::types as snapshot1_types;
use crate::preview1::wasi_snapshot_preview1::WasiSnapshotPreview1 as Snapshot1;
use crate::preview1::WasiPreview1View;
use wiggle::{GuestError, GuestPtr};
pub fn add_to_linker_async<T: WasiPreview1View>(
@ -52,7 +52,7 @@ mod sync {
// Small wrapper around `in_tokio` to add a `Result` layer which is always
// `Ok`
fn in_tokio<F: Future>(future: F) -> Result<F::Output> {
Ok(crate::preview2::in_tokio(future))
Ok(crate::in_tokio(future))
}
}

24
crates/wasi/src/preview2/preview1.rs → crates/wasi/src/preview1.rs

@ -1,4 +1,4 @@
use crate::preview2::bindings::{
use crate::bindings::{
self,
cli::{
stderr, stdin, stdout, terminal_input, terminal_output, terminal_stderr, terminal_stdin,
@ -8,7 +8,7 @@ use crate::preview2::bindings::{
filesystem::{preopens, types as filesystem},
io::{poll, streams},
};
use crate::preview2::{FsError, IsATTY, StreamError, StreamResult, WasiView};
use crate::{FsError, IsATTY, StreamError, StreamResult, WasiView};
use anyhow::{bail, Context};
use std::borrow::Borrow;
use std::collections::{BTreeMap, HashSet};
@ -23,7 +23,7 @@ use wiggle::{GuestError, GuestPtr, GuestStrCow, GuestType};
#[derive(Debug)]
struct File {
/// The handle to the preview2 descriptor of type [`crate::preview2::filesystem::Descriptor::File`].
/// The handle to the preview2 descriptor of type [`crate::filesystem::Descriptor::File`].
fd: Resource<filesystem::Descriptor>,
/// The current-position pointer.
@ -151,14 +151,14 @@ enum Descriptor {
stream: Resource<streams::OutputStream>,
isatty: IsATTY,
},
/// A fd of type [`crate::preview2::filesystem::Descriptor::Dir`]
/// A fd of type [`crate::filesystem::Descriptor::Dir`]
Directory {
fd: Resource<filesystem::Descriptor>,
/// The path this directory was preopened as.
/// `None` means this directory was opened using `open-at`.
preopen_path: Option<String>,
},
/// A fd of type [`crate::preview2::filesystem::Descriptor::File`]
/// A fd of type [`crate::filesystem::Descriptor::File`]
File(File),
}
@ -464,7 +464,7 @@ trait WasiPreview1ViewExt:
/// Lazily initializes [`WasiPreview1Adapter`] returned by [`WasiPreview1View::adapter_mut`]
/// and returns [`filesystem::Descriptor`] corresponding to `fd`
/// if it describes a [`Descriptor::File`] of [`crate::preview2::filesystem::File`] type
/// if it describes a [`Descriptor::File`] of [`crate::filesystem::File`] type
fn get_file_fd(
&mut self,
fd: types::Fd,
@ -477,7 +477,7 @@ trait WasiPreview1ViewExt:
/// Lazily initializes [`WasiPreview1Adapter`] returned by [`WasiPreview1View::adapter_mut`]
/// and returns [`filesystem::Descriptor`] corresponding to `fd`
/// if it describes a [`Descriptor::File`] or [`Descriptor::PreopenDirectory`]
/// of [`crate::preview2::filesystem::Dir`] type
/// of [`crate::filesystem::Dir`] type
fn get_dir_fd(
&mut self,
fd: types::Fd,
@ -542,7 +542,7 @@ mod sync {
// Small wrapper around `in_tokio` to add a `Result` layer which is always
// `Ok`
fn in_tokio<F: Future>(future: F) -> Result<F::Output> {
Ok(crate::preview2::in_tokio(future))
Ok(crate::in_tokio(future))
}
}
@ -559,7 +559,7 @@ impl From<StreamError> for types::Error {
StreamError::LastOperationFailed(e) => match e.downcast::<std::io::Error>() {
Ok(err) => filesystem::ErrorCode::from(err).into(),
Err(e) => {
log::debug!("dropping error {e:?}");
tracing::debug!("dropping error {e:?}");
types::Errno::Io.into()
}
},
@ -1918,11 +1918,11 @@ impl<
})?;
let mut t = self.transact()?;
let desc = match t.view.table().get(&fd)? {
crate::preview2::filesystem::Descriptor::Dir(_) => Descriptor::Directory {
crate::filesystem::Descriptor::Dir(_) => Descriptor::Directory {
fd,
preopen_path: None,
},
crate::preview2::filesystem::Descriptor::File(_) => Descriptor::File(File {
crate::filesystem::Descriptor::File(_) => Descriptor::File(File {
fd,
position: Default::default(),
append: fdflags.contains(types::Fdflags::APPEND),
@ -2282,7 +2282,7 @@ impl<
if status >= 126 {
return anyhow::Error::msg("exit with invalid exit status outside of [0..126)");
}
crate::preview2::I32Exit(status as i32).into()
crate::I32Exit(status as i32).into()
}
#[instrument(skip(self))]

122
crates/wasi/src/preview2/command.rs

@ -1,122 +0,0 @@
use crate::preview2::WasiView;
wasmtime::component::bindgen!({
world: "wasi:cli/command",
tracing: true,
async: true,
with: {
"wasi:filesystem/types": crate::preview2::bindings::filesystem::types,
"wasi:filesystem/preopens": crate::preview2::bindings::filesystem::preopens,
"wasi:sockets/tcp": crate::preview2::bindings::sockets::tcp,
"wasi:clocks/monotonic_clock": crate::preview2::bindings::clocks::monotonic_clock,
"wasi:io/poll": crate::preview2::bindings::io::poll,
"wasi:io/streams": crate::preview2::bindings::io::streams,
"wasi:clocks/wall_clock": crate::preview2::bindings::clocks::wall_clock,
"wasi:random/random": crate::preview2::bindings::random::random,
"wasi:cli/environment": crate::preview2::bindings::cli::environment,
"wasi:cli/exit": crate::preview2::bindings::cli::exit,
"wasi:cli/stdin": crate::preview2::bindings::cli::stdin,
"wasi:cli/stdout": crate::preview2::bindings::cli::stdout,
"wasi:cli/stderr": crate::preview2::bindings::cli::stderr,
"wasi:cli/terminal-input": crate::preview2::bindings::cli::terminal_input,
"wasi:cli/terminal-output": crate::preview2::bindings::cli::terminal_output,
"wasi:cli/terminal-stdin": crate::preview2::bindings::cli::terminal_stdin,
"wasi:cli/terminal-stdout": crate::preview2::bindings::cli::terminal_stdout,
"wasi:cli/terminal-stderr": crate::preview2::bindings::cli::terminal_stderr,
},
});
pub fn add_to_linker<T: WasiView>(l: &mut wasmtime::component::Linker<T>) -> anyhow::Result<()> {
crate::preview2::bindings::clocks::wall_clock::add_to_linker(l, |t| t)?;
crate::preview2::bindings::clocks::monotonic_clock::add_to_linker(l, |t| t)?;
crate::preview2::bindings::filesystem::types::add_to_linker(l, |t| t)?;
crate::preview2::bindings::filesystem::preopens::add_to_linker(l, |t| t)?;
crate::preview2::bindings::io::error::add_to_linker(l, |t| t)?;
crate::preview2::bindings::io::poll::add_to_linker(l, |t| t)?;
crate::preview2::bindings::io::streams::add_to_linker(l, |t| t)?;
crate::preview2::bindings::random::random::add_to_linker(l, |t| t)?;
crate::preview2::bindings::random::insecure::add_to_linker(l, |t| t)?;
crate::preview2::bindings::random::insecure_seed::add_to_linker(l, |t| t)?;
crate::preview2::bindings::cli::exit::add_to_linker(l, |t| t)?;
crate::preview2::bindings::cli::environment::add_to_linker(l, |t| t)?;
crate::preview2::bindings::cli::stdin::add_to_linker(l, |t| t)?;
crate::preview2::bindings::cli::stdout::add_to_linker(l, |t| t)?;
crate::preview2::bindings::cli::stderr::add_to_linker(l, |t| t)?;
crate::preview2::bindings::cli::terminal_input::add_to_linker(l, |t| t)?;
crate::preview2::bindings::cli::terminal_output::add_to_linker(l, |t| t)?;
crate::preview2::bindings::cli::terminal_stdin::add_to_linker(l, |t| t)?;
crate::preview2::bindings::cli::terminal_stdout::add_to_linker(l, |t| t)?;
crate::preview2::bindings::cli::terminal_stderr::add_to_linker(l, |t| t)?;
crate::preview2::bindings::sockets::tcp::add_to_linker(l, |t| t)?;
crate::preview2::bindings::sockets::tcp_create_socket::add_to_linker(l, |t| t)?;
crate::preview2::bindings::sockets::udp::add_to_linker(l, |t| t)?;
crate::preview2::bindings::sockets::udp_create_socket::add_to_linker(l, |t| t)?;
crate::preview2::bindings::sockets::instance_network::add_to_linker(l, |t| t)?;
crate::preview2::bindings::sockets::network::add_to_linker(l, |t| t)?;
crate::preview2::bindings::sockets::ip_name_lookup::add_to_linker(l, |t| t)?;
Ok(())
}
pub mod sync {
use crate::preview2::WasiView;
wasmtime::component::bindgen!({
world: "wasi:cli/command",
tracing: true,
async: false,
with: {
"wasi:filesystem/types": crate::preview2::bindings::sync_io::filesystem::types,
"wasi:filesystem/preopens": crate::preview2::bindings::filesystem::preopens,
"wasi:sockets/tcp": crate::preview2::bindings::sockets::tcp,
"wasi:sockets/udp": crate::preview2::bindings::sockets::udp,
"wasi:clocks/monotonic_clock": crate::preview2::bindings::clocks::monotonic_clock,
"wasi:io/poll": crate::preview2::bindings::sync_io::io::poll,
"wasi:io/streams": crate::preview2::bindings::sync_io::io::streams,
"wasi:clocks/wall_clock": crate::preview2::bindings::clocks::wall_clock,
"wasi:random/random": crate::preview2::bindings::random::random,
"wasi:cli/environment": crate::preview2::bindings::cli::environment,
"wasi:cli/exit": crate::preview2::bindings::cli::exit,
"wasi:cli/stdin": crate::preview2::bindings::cli::stdin,
"wasi:cli/stdout": crate::preview2::bindings::cli::stdout,
"wasi:cli/stderr": crate::preview2::bindings::cli::stderr,
"wasi:cli/terminal-input": crate::preview2::bindings::cli::terminal_input,
"wasi:cli/terminal-output": crate::preview2::bindings::cli::terminal_output,
"wasi:cli/terminal-stdin": crate::preview2::bindings::cli::terminal_stdin,
"wasi:cli/terminal-stdout": crate::preview2::bindings::cli::terminal_stdout,
"wasi:cli/terminal-stderr": crate::preview2::bindings::cli::terminal_stderr,
},
});
pub fn add_to_linker<T: WasiView>(
l: &mut wasmtime::component::Linker<T>,
) -> anyhow::Result<()> {
crate::preview2::bindings::clocks::wall_clock::add_to_linker(l, |t| t)?;
crate::preview2::bindings::clocks::monotonic_clock::add_to_linker(l, |t| t)?;
crate::preview2::bindings::sync_io::filesystem::types::add_to_linker(l, |t| t)?;
crate::preview2::bindings::filesystem::preopens::add_to_linker(l, |t| t)?;
crate::preview2::bindings::io::error::add_to_linker(l, |t| t)?;
crate::preview2::bindings::sync_io::io::poll::add_to_linker(l, |t| t)?;
crate::preview2::bindings::sync_io::io::streams::add_to_linker(l, |t| t)?;
crate::preview2::bindings::random::random::add_to_linker(l, |t| t)?;
crate::preview2::bindings::random::insecure::add_to_linker(l, |t| t)?;
crate::preview2::bindings::random::insecure_seed::add_to_linker(l, |t| t)?;
crate::preview2::bindings::cli::exit::add_to_linker(l, |t| t)?;
crate::preview2::bindings::cli::environment::add_to_linker(l, |t| t)?;
crate::preview2::bindings::cli::stdin::add_to_linker(l, |t| t)?;
crate::preview2::bindings::cli::stdout::add_to_linker(l, |t| t)?;
crate::preview2::bindings::cli::stderr::add_to_linker(l, |t| t)?;
crate::preview2::bindings::cli::terminal_input::add_to_linker(l, |t| t)?;
crate::preview2::bindings::cli::terminal_output::add_to_linker(l, |t| t)?;
crate::preview2::bindings::cli::terminal_stdin::add_to_linker(l, |t| t)?;
crate::preview2::bindings::cli::terminal_stdout::add_to_linker(l, |t| t)?;
crate::preview2::bindings::cli::terminal_stderr::add_to_linker(l, |t| t)?;
crate::preview2::bindings::sockets::tcp::add_to_linker(l, |t| t)?;
crate::preview2::bindings::sockets::tcp_create_socket::add_to_linker(l, |t| t)?;
crate::preview2::bindings::sockets::udp::add_to_linker(l, |t| t)?;
crate::preview2::bindings::sockets::udp_create_socket::add_to_linker(l, |t| t)?;
crate::preview2::bindings::sockets::instance_network::add_to_linker(l, |t| t)?;
crate::preview2::bindings::sockets::network::add_to_linker(l, |t| t)?;
crate::preview2::bindings::sockets::ip_name_lookup::add_to_linker(l, |t| t)?;
Ok(())
}
}

327
crates/wasi/src/preview2/mod.rs

@ -1,327 +0,0 @@
//! # Wasmtime's WASI Preview 2 Implementation
//!
//! Welcome to the (new!) WASI implementation from the Wasmtime team. The goal
//! of this implementation is to support WASI Preview 2 via the Component
//! Model, as well as to provide legacy Preview 1 host support with an adapter
//! that is implemented in terms of the Preview 2 interfaces.
//!
//! Presently, this crate is experimental. We don't yet recommend you use it
//! in production. Specifically:
//! * the wit files in tree describing preview 2 are not faithful to the
//! standards repos
//!
//! Once these issues are resolved, we expect to move this namespace up to the
//! root of the wasmtime-wasi crate, and move its other exports underneath a
//! `pub mod legacy` with an off-by-default feature flag, and after 2
//! releases, retire and remove that code from our tree.
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
mod clocks;
pub mod command;
mod ctx;
mod error;
mod filesystem;
mod host;
mod ip_name_lookup;
mod network;
pub mod pipe;
mod poll;
#[cfg(feature = "preview1-on-preview2")]
pub mod preview0;
#[cfg(feature = "preview1-on-preview2")]
pub mod preview1;
mod random;
mod stdio;
mod stream;
mod tcp;
mod udp;
mod write_stream;
pub use self::clocks::{HostMonotonicClock, HostWallClock};
pub use self::ctx::{WasiCtx, WasiCtxBuilder, WasiView};
pub use self::error::{I32Exit, TrappableError};
pub use self::filesystem::{DirPerms, FilePerms, FsError, FsResult};
pub use self::network::{Network, SocketError, SocketResult};
pub use self::poll::{subscribe, ClosureFuture, MakeFuture, Pollable, PollableFuture, Subscribe};
pub use self::random::{thread_rng, Deterministic};
pub use self::stdio::{
stderr, stdin, stdout, IsATTY, Stderr, Stdin, StdinStream, Stdout, StdoutStream,
};
pub use self::stream::{
HostInputStream, HostOutputStream, InputStream, OutputStream, StreamError, StreamResult,
};
pub use cap_fs_ext::SystemTimeSpec;
pub use cap_rand::RngCore;
pub use wasmtime::component::{ResourceTable, ResourceTableError};
pub mod bindings {
// Generate traits for synchronous bindings.
//
// Note that this is only done for interfaces which can block, or those which
// have some functions in `only_imports` below for being async.
pub mod sync_io {
pub(crate) mod _internal {
use crate::preview2::{FsError, StreamError};
wasmtime::component::bindgen!({
path: "wit",
interfaces: "
import wasi:io/poll@0.2.0;
import wasi:io/streams@0.2.0;
import wasi:filesystem/types@0.2.0;
",
tracing: true,
trappable_error_type: {
"wasi:io/streams/stream-error" => StreamError,
"wasi:filesystem/types/error-code" => FsError,
},
with: {
"wasi:clocks/wall-clock": crate::preview2::bindings::clocks::wall_clock,
"wasi:filesystem/types/descriptor": super::super::filesystem::types::Descriptor,
"wasi:filesystem/types/directory-entry-stream": super::super::filesystem::types::DirectoryEntryStream,
"wasi:io/poll/pollable": super::super::io::poll::Pollable,
"wasi:io/streams/input-stream": super::super::io::streams::InputStream,
"wasi:io/streams/output-stream": super::super::io::streams::OutputStream,
"wasi:io/error/error": super::super::io::error::Error,
}
});
}
pub use self::_internal::wasi::{filesystem, io};
}
wasmtime::component::bindgen!({
path: "wit",
world: "wasi:cli/imports",
tracing: true,
async: {
// Only these functions are `async` and everything else is sync
// meaning that it basically doesn't need to block. These functions
// are the only ones that need to block.
//
// Note that at this time `only_imports` works on function names
// which in theory can be shared across interfaces, so this may
// need fancier syntax in the future.
only_imports: [
"[method]descriptor.access-at",
"[method]descriptor.advise",
"[method]descriptor.change-directory-permissions-at",
"[method]descriptor.change-file-permissions-at",
"[method]descriptor.create-directory-at",
"[method]descriptor.get-flags",
"[method]descriptor.get-type",
"[method]descriptor.is-same-object",
"[method]descriptor.link-at",
"[method]descriptor.lock-exclusive",
"[method]descriptor.lock-shared",
"[method]descriptor.metadata-hash",
"[method]descriptor.metadata-hash-at",
"[method]descriptor.open-at",
"[method]descriptor.read",
"[method]descriptor.read-directory",
"[method]descriptor.readlink-at",
"[method]descriptor.remove-directory-at",
"[method]descriptor.rename-at",
"[method]descriptor.set-size",
"[method]descriptor.set-times",
"[method]descriptor.set-times-at",
"[method]descriptor.stat",
"[method]descriptor.stat-at",
"[method]descriptor.symlink-at",
"[method]descriptor.sync",
"[method]descriptor.sync-data",
"[method]descriptor.try-lock-exclusive",
"[method]descriptor.try-lock-shared",
"[method]descriptor.unlink-file-at",
"[method]descriptor.unlock",
"[method]descriptor.write",
"[method]input-stream.read",
"[method]input-stream.blocking-read",
"[method]input-stream.blocking-skip",
"[method]input-stream.skip",
"[method]output-stream.forward",
"[method]output-stream.splice",
"[method]output-stream.blocking-splice",
"[method]output-stream.blocking-flush",
"[method]output-stream.blocking-write",
"[method]output-stream.blocking-write-and-flush",
"[method]output-stream.blocking-write-zeroes-and-flush",
"[method]directory-entry-stream.read-directory-entry",
"poll",
"[method]pollable.block",
"[method]pollable.ready",
],
},
trappable_error_type: {
"wasi:io/streams/stream-error" => crate::preview2::StreamError,
"wasi:filesystem/types/error-code" => crate::preview2::FsError,
"wasi:sockets/network/error-code" => crate::preview2::SocketError,
},
with: {
"wasi:sockets/network/network": super::network::Network,
"wasi:sockets/tcp/tcp-socket": super::tcp::TcpSocket,
"wasi:sockets/udp/udp-socket": super::udp::UdpSocket,
"wasi:sockets/udp/incoming-datagram-stream": super::udp::IncomingDatagramStream,
"wasi:sockets/udp/outgoing-datagram-stream": super::udp::OutgoingDatagramStream,
"wasi:sockets/ip-name-lookup/resolve-address-stream": super::ip_name_lookup::ResolveAddressStream,
"wasi:filesystem/types/directory-entry-stream": super::filesystem::ReaddirIterator,
"wasi:filesystem/types/descriptor": super::filesystem::Descriptor,
"wasi:io/streams/input-stream": super::stream::InputStream,
"wasi:io/streams/output-stream": super::stream::OutputStream,
"wasi:io/error/error": super::stream::Error,
"wasi:io/poll/pollable": super::poll::Pollable,
"wasi:cli/terminal-input/terminal-input": super::stdio::TerminalInput,
"wasi:cli/terminal-output/terminal-output": super::stdio::TerminalOutput,
},
});
pub use wasi::*;
}
pub(crate) static RUNTIME: once_cell::sync::Lazy<tokio::runtime::Runtime> =
once_cell::sync::Lazy::new(|| {
tokio::runtime::Builder::new_current_thread()
.enable_time()
.enable_io()
.build()
.unwrap()
});
pub struct AbortOnDropJoinHandle<T>(tokio::task::JoinHandle<T>);
impl<T> Drop for AbortOnDropJoinHandle<T> {
fn drop(&mut self) {
self.0.abort()
}
}
impl<T> std::ops::Deref for AbortOnDropJoinHandle<T> {
type Target = tokio::task::JoinHandle<T>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> std::ops::DerefMut for AbortOnDropJoinHandle<T> {
fn deref_mut(&mut self) -> &mut tokio::task::JoinHandle<T> {
&mut self.0
}
}
impl<T> From<tokio::task::JoinHandle<T>> for AbortOnDropJoinHandle<T> {
fn from(jh: tokio::task::JoinHandle<T>) -> Self {
AbortOnDropJoinHandle(jh)
}
}
impl<T> Future for AbortOnDropJoinHandle<T> {
type Output = T;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match Pin::new(&mut self.as_mut().0).poll(cx) {
Poll::Pending => Poll::Pending,
Poll::Ready(r) => Poll::Ready(r.expect("child task panicked")),
}
}
}
pub fn spawn<F>(f: F) -> AbortOnDropJoinHandle<F::Output>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
let j = with_ambient_tokio_runtime(|| tokio::task::spawn(f));
AbortOnDropJoinHandle(j)
}
pub fn spawn_blocking<F, R>(f: F) -> AbortOnDropJoinHandle<R>
where
F: FnOnce() -> R + Send + 'static,
R: Send + 'static,
{
let j = with_ambient_tokio_runtime(|| tokio::task::spawn_blocking(f));
AbortOnDropJoinHandle(j)
}
pub fn in_tokio<F: Future>(f: F) -> F::Output {
match tokio::runtime::Handle::try_current() {
Ok(h) => {
let _enter = h.enter();
h.block_on(f)
}
// The `yield_now` here is non-obvious and if you're reading this
// you're likely curious about why it's here. This is currently required
// to get some features of "sync mode" working correctly, such as with
// the CLI. To illustrate why this is required, consider a program
// organized as:
//
// * A program has a `pollable` that it's waiting on.
// * This `pollable` is always ready .
// * Actually making the corresponding operation ready, however,
// requires some background work on Tokio's part.
// * The program is looping on "wait for readiness" coupled with
// performing the operation.
//
// In this situation this program ends up infinitely looping in waiting
// for pollables. The reason appears to be that when we enter the tokio
// runtime here it doesn't necessary yield to background work because
// the provided future `f` is ready immediately. The future `f` will run
// through the list of pollables and determine one of them is ready.
//
// Historically this happened with UDP sockets. A test send a datagram
// from one socket to another and the other socket infinitely didn't
// receive the data. This appeared to be because the server socket was
// waiting on `READABLE | WRITABLE` (which is itself a bug but ignore
// that) and the socket was currently in the "writable" state but never
// ended up receiving a notification for the "readable" state. Moving
// the socket to "readable" would require Tokio to perform some
// background work via epoll/kqueue/handle events but if the future
// provided here is always ready, then that never happened.
//
// Thus the `yield_now()` is an attempt to force Tokio to go do some
// background work eventually and look at new interest masks for
// example. This is a bit of a kludge but everything's already a bit
// wonky in synchronous mode anyway. Note that this is hypothesized to
// not be an issue in async mode because async mode typically has the
// Tokio runtime in a separate thread or otherwise participating in a
// larger application, it's only here in synchronous mode where we
// effectively own the runtime that we need some special care.
Err(_) => {
let _enter = RUNTIME.enter();
RUNTIME.block_on(async move {
tokio::task::yield_now().await;
f.await
})
}
}
}
/// Executes the closure `f` with an "ambient Tokio runtime" which basically
/// means that if code in `f` tries to get a runtime `Handle` it'll succeed.
///
/// If a `Handle` is already available, e.g. in async contexts, then `f` is run
/// immediately. Otherwise for synchronous contexts this crate's fallback
/// runtime is configured and then `f` is executed.
pub fn with_ambient_tokio_runtime<R>(f: impl FnOnce() -> R) -> R {
match tokio::runtime::Handle::try_current() {
Ok(_) => f(),
Err(_) => {
let _enter = RUNTIME.enter();
f()
}
}
}
/// Attempts to get the result of a `future`.
///
/// This function does not block and will poll the provided future once. If the
/// result is here then `Some` is returned, otherwise `None` is returned.
///
/// Note that by polling `future` this means that `future` must be re-polled
/// later if it's to wake up a task.
pub fn poll_noop<F>(future: Pin<&mut F>) -> Option<F::Output>
where
F: Future,
{
let mut task = Context::from_waker(futures::task::noop_waker_ref());
match future.poll(&mut task) {
Poll::Ready(result) => Some(result),
Poll::Pending => None,
}
}

0
crates/wasi/src/preview2/random.rs → crates/wasi/src/random.rs

10
crates/wasi/src/preview2/stdio.rs → crates/wasi/src/stdio.rs

@ -1,12 +1,10 @@
use crate::preview2::bindings::cli::{
use crate::bindings::cli::{
stderr, stdin, stdout, terminal_input, terminal_output, terminal_stderr, terminal_stdin,
terminal_stdout,
};
use crate::preview2::bindings::io::streams;
use crate::preview2::pipe;
use crate::preview2::{
HostInputStream, HostOutputStream, StreamError, StreamResult, Subscribe, WasiView,
};
use crate::bindings::io::streams;
use crate::pipe;
use crate::{HostInputStream, HostOutputStream, StreamError, StreamResult, Subscribe, WasiView};
use bytes::Bytes;
use std::io::IsTerminal;
use wasmtime::component::Resource;

6
crates/wasi/src/preview2/stdio/worker_thread_stdin.rs → crates/wasi/src/stdio/worker_thread_stdin.rs

@ -23,9 +23,9 @@
//! This module is one that's likely to change over time though as new systems
//! are encountered along with preexisting bugs.
use crate::preview2::poll::Subscribe;
use crate::preview2::stdio::StdinStream;
use crate::preview2::{HostInputStream, StreamError};
use crate::poll::Subscribe;
use crate::stdio::StdinStream;
use crate::{HostInputStream, StreamError};
use bytes::{Bytes, BytesMut};
use std::io::{IsTerminal, Read};
use std::mem;

4
crates/wasi/src/preview2/stream.rs → crates/wasi/src/stream.rs

@ -1,5 +1,5 @@
use crate::preview2::filesystem::FileInputStream;
use crate::preview2::poll::Subscribe;
use crate::filesystem::FileInputStream;
use crate::poll::Subscribe;
use anyhow::Result;
use bytes::Bytes;

6
crates/wasi/src/preview2/tcp.rs → crates/wasi/src/tcp.rs

@ -2,7 +2,7 @@ use super::network::SocketAddressFamily;
use super::{
with_ambient_tokio_runtime, HostInputStream, HostOutputStream, SocketResult, StreamError,
};
use crate::preview2::{AbortOnDropJoinHandle, Subscribe};
use crate::{AbortOnDropJoinHandle, Subscribe};
use anyhow::{Error, Result};
use cap_net_ext::AddressFamily;
use futures::Future;
@ -164,7 +164,7 @@ impl TcpWriteStream {
assert!(matches!(self.last_write, LastWrite::Done));
let stream = self.stream.clone();
self.last_write = LastWrite::Waiting(crate::preview2::spawn(async move {
self.last_write = LastWrite::Waiting(crate::spawn(async move {
// Note: we are not using the AsyncWrite impl here, and instead using the TcpStream
// primitive try_write, which goes directly to attempt a write with mio. This has
// two advantages: 1. this operation takes a &TcpStream instead of a &mut TcpStream
@ -296,7 +296,7 @@ impl TcpSocket {
}
pub(crate) fn as_std_view(&self) -> SocketResult<SocketlikeView<'_, std::net::TcpStream>> {
use crate::preview2::bindings::sockets::network::ErrorCode;
use crate::bindings::sockets::network::ErrorCode;
match &self.tcp_state {
TcpState::Default(socket) | TcpState::Bound(socket) => {

6
crates/wasi/src/preview2/udp.rs → crates/wasi/src/udp.rs

@ -1,6 +1,6 @@
use crate::preview2::host::network::util;
use crate::preview2::poll::Subscribe;
use crate::preview2::with_ambient_tokio_runtime;
use crate::host::network::util;
use crate::poll::Subscribe;
use crate::with_ambient_tokio_runtime;
use async_trait::async_trait;
use cap_net_ext::{AddressFamily, Blocking};
use io_lifetimes::raw::{FromRawSocketlike, IntoRawSocketlike};

6
crates/wasi/src/preview2/write_stream.rs → crates/wasi/src/write_stream.rs

@ -1,4 +1,4 @@
use crate::preview2::{HostOutputStream, StreamError, Subscribe};
use crate::{HostOutputStream, StreamError, Subscribe};
use anyhow::anyhow;
use bytes::Bytes;
use std::sync::{Arc, Mutex};
@ -139,7 +139,7 @@ impl Worker {
/// Provides a [`HostOutputStream`] impl from a [`tokio::io::AsyncWrite`] impl
pub struct AsyncWriteStream {
worker: Arc<Worker>,
_join_handle: crate::preview2::AbortOnDropJoinHandle<()>,
_join_handle: crate::AbortOnDropJoinHandle<()>,
}
impl AsyncWriteStream {
@ -152,7 +152,7 @@ impl AsyncWriteStream {
let worker = Arc::new(Worker::new(write_budget));
let w = Arc::clone(&worker);
let join_handle = crate::preview2::spawn(async move { w.work(writer).await });
let join_handle = crate::spawn(async move { w.work(writer).await });
AsyncWriteStream {
worker,

39
crates/wasi/tests/all/api.rs

@ -6,11 +6,10 @@ use std::sync::Mutex;
use std::time::Duration;
use wasmtime::component::{Component, Linker, ResourceTable};
use wasmtime::{Config, Engine, Store};
use wasmtime_wasi::preview2::bindings::wasi::clocks::wall_clock;
use wasmtime_wasi::preview2::bindings::wasi::filesystem::types as filesystem;
use wasmtime_wasi::preview2::command::{add_to_linker, Command};
use wasmtime_wasi::preview2::{
self, DirPerms, FilePerms, HostMonotonicClock, HostWallClock, WasiCtx, WasiCtxBuilder, WasiView,
use wasmtime_wasi::{
bindings::wasi::{clocks::wall_clock, filesystem::types as filesystem},
command::{add_to_linker, Command},
DirPerms, FilePerms, HostMonotonicClock, HostWallClock, WasiCtx, WasiCtxBuilder, WasiView,
};
struct CommandCtx {
@ -127,19 +126,19 @@ wasmtime::component::bindgen!({
world: "test-reactor",
async: true,
with: {
"wasi:io/streams": preview2::bindings::io::streams,
"wasi:filesystem/types": preview2::bindings::filesystem::types,
"wasi:filesystem/preopens": preview2::bindings::filesystem::preopens,
"wasi:cli/environment": preview2::bindings::cli::environment,
"wasi:cli/exit": preview2::bindings::cli::exit,
"wasi:cli/stdin": preview2::bindings::cli::stdin,
"wasi:cli/stdout": preview2::bindings::cli::stdout,
"wasi:cli/stderr": preview2::bindings::cli::stderr,
"wasi:cli/terminal_input": preview2::bindings::cli::terminal_input,
"wasi:cli/terminal_output": preview2::bindings::cli::terminal_output,
"wasi:cli/terminal_stdin": preview2::bindings::cli::terminal_stdin,
"wasi:cli/terminal_stdout": preview2::bindings::cli::terminal_stdout,
"wasi:cli/terminal_stderr": preview2::bindings::cli::terminal_stderr,
"wasi:io/streams": wasmtime_wasi::bindings::io::streams,
"wasi:filesystem/types": wasmtime_wasi::bindings::filesystem::types,
"wasi:filesystem/preopens": wasmtime_wasi::bindings::filesystem::preopens,
"wasi:cli/environment": wasmtime_wasi::bindings::cli::environment,
"wasi:cli/exit": wasmtime_wasi::bindings::cli::exit,
"wasi:cli/stdin": wasmtime_wasi::bindings::cli::stdin,
"wasi:cli/stdout": wasmtime_wasi::bindings::cli::stdout,
"wasi:cli/stderr": wasmtime_wasi::bindings::cli::stderr,
"wasi:cli/terminal_input": wasmtime_wasi::bindings::cli::terminal_input,
"wasi:cli/terminal_output": wasmtime_wasi::bindings::cli::terminal_output,
"wasi:cli/terminal_stdin": wasmtime_wasi::bindings::cli::terminal_stdin,
"wasi:cli/terminal_stdout": wasmtime_wasi::bindings::cli::terminal_stdout,
"wasi:cli/terminal_stderr": wasmtime_wasi::bindings::cli::terminal_stderr,
},
ownership: Borrowing {
duplicate_if_necessary: false
@ -176,8 +175,8 @@ async fn api_reactor() -> Result<()> {
// `host` and `wasi-common` crate.
// Note, this works because of the add_to_linker invocations using the
// `host` crate for `streams`, not because of `with` in the bindgen macro.
let writepipe = preview2::pipe::MemoryOutputPipe::new(4096);
let stream: preview2::OutputStream = Box::new(writepipe.clone());
let writepipe = wasmtime_wasi::pipe::MemoryOutputPipe::new(4096);
let stream: wasmtime_wasi::OutputStream = Box::new(writepipe.clone());
let table_ix = store.data_mut().table().push(stream)?;
let r = reactor.call_write_strings_to(&mut store, table_ix).await?;
assert_eq!(r, Ok(()));

2
crates/wasi/tests/all/async_.rs

@ -1,7 +1,7 @@
use super::*;
use std::path::Path;
use test_programs_artifacts::*;
use wasmtime_wasi::preview2::command::{add_to_linker, Command};
use wasmtime_wasi::command::{add_to_linker, Command};
async fn run(path: &str, inherit_stdio: bool) -> Result<()> {
let path = Path::new(path);

2
crates/wasi/tests/all/main.rs

@ -4,7 +4,7 @@ use wasmtime::{
component::{Component, Linker, ResourceTable},
Config, Engine, Store,
};
use wasmtime_wasi::preview2::{
use wasmtime_wasi::{
pipe::MemoryOutputPipe,
preview1::{WasiPreview1Adapter, WasiPreview1View},
DirPerms, FilePerms, WasiCtx, WasiCtxBuilder, WasiView,

2
crates/wasi/tests/all/preview1.rs

@ -2,7 +2,7 @@ use super::*;
use std::path::Path;
use test_programs_artifacts::*;
use wasmtime::{Linker, Module};
use wasmtime_wasi::preview2::preview1::add_to_linker_async;
use wasmtime_wasi::preview1::add_to_linker_async;
async fn run(path: &str, inherit_stdio: bool) -> Result<()> {
let path = Path::new(path);

2
crates/wasi/tests/all/sync.rs

@ -1,7 +1,7 @@
use super::*;
use std::path::Path;
use test_programs_artifacts::*;
use wasmtime_wasi::preview2::command::sync::{add_to_linker, Command};
use wasmtime_wasi::command::sync::{add_to_linker, Command};
fn run(path: &str, inherit_stdio: bool) -> Result<()> {
let path = Path::new(path);

4
crates/wasi/tests/process_stdin.rs

@ -1,6 +1,6 @@
use std::io::{BufRead, Write};
use std::process::Command;
use wasmtime_wasi::preview2::{HostInputStream, Subscribe};
use wasmtime_wasi::{HostInputStream, Subscribe};
const VAR_NAME: &str = "__CHILD_PROCESS";
@ -35,7 +35,7 @@ fn main() {
.block_on(async {
'task: loop {
println!("child: creating stdin");
let mut stdin = wasmtime_wasi::preview2::stdin();
let mut stdin = wasmtime_wasi::stdin();
println!("child: checking that stdin is not ready");
assert!(

2
examples/CMakeLists.txt

@ -77,5 +77,5 @@ create_rust_test(multimemory)
create_rust_test(serialize)
create_rust_test(threads)
create_rust_test(wasi)
create_rust_test(tokio wasmtime-wasi/tokio)
create_rust_test(tokio wasi-common/tokio)
create_rust_test(component)

23
examples/wasi-async/main.rs

@ -9,30 +9,29 @@ You can execute this example with:
use anyhow::Result;
use wasmtime::{Config, Engine, Linker, Module, Store};
use wasmtime_wasi::preview2;
struct WasiHostCtx {
preview2_ctx: preview2::WasiCtx,
preview2_ctx: wasmtime_wasi::WasiCtx,
preview2_table: wasmtime::component::ResourceTable,
preview1_adapter: preview2::preview1::WasiPreview1Adapter,
preview1_adapter: wasmtime_wasi::preview1::WasiPreview1Adapter,
}
impl preview2::WasiView for WasiHostCtx {
impl wasmtime_wasi::WasiView for WasiHostCtx {
fn table(&mut self) -> &mut wasmtime::component::ResourceTable {
&mut self.preview2_table
}
fn ctx(&mut self) -> &mut preview2::WasiCtx {
fn ctx(&mut self) -> &mut wasmtime_wasi::WasiCtx {
&mut self.preview2_ctx
}
}
impl preview2::preview1::WasiPreview1View for WasiHostCtx {
fn adapter(&self) -> &preview2::preview1::WasiPreview1Adapter {
impl wasmtime_wasi::preview1::WasiPreview1View for WasiHostCtx {
fn adapter(&self) -> &wasmtime_wasi::preview1::WasiPreview1Adapter {
&self.preview1_adapter
}
fn adapter_mut(&mut self) -> &mut preview2::preview1::WasiPreview1Adapter {
fn adapter_mut(&mut self) -> &mut wasmtime_wasi::preview1::WasiPreview1Adapter {
&mut self.preview1_adapter
}
}
@ -47,15 +46,15 @@ async fn main() -> Result<()> {
// Add the WASI preview1 API to the linker (will be implemented in terms of
// the preview2 API)
let mut linker: Linker<WasiHostCtx> = Linker::new(&engine);
preview2::preview1::add_to_linker_async(&mut linker)?;
wasmtime_wasi::preview1::add_to_linker_async(&mut linker)?;
// Add capabilities (e.g. filesystem access) to the WASI preview2 context here.
let wasi_ctx = preview2::WasiCtxBuilder::new().inherit_stdio().build();
let wasi_ctx = wasmtime_wasi::WasiCtxBuilder::new().inherit_stdio().build();
let host_ctx = WasiHostCtx {
preview2_ctx: wasi_ctx,
preview2_table: preview2::ResourceTable::new(),
preview1_adapter: preview2::preview1::WasiPreview1Adapter::new(),
preview2_table: wasmtime::component::ResourceTable::new(),
preview1_adapter: wasmtime_wasi::preview1::WasiPreview1Adapter::new(),
};
let mut store: Store<WasiHostCtx> = Store::new(&engine, host_ctx);

47
src/commands/run.rs

@ -15,7 +15,6 @@ use std::sync::{Arc, Mutex};
use std::thread;
use wasi_common::sync::{ambient_authority, Dir, TcpListener, WasiCtxBuilder};
use wasmtime::{Engine, Func, Module, Store, StoreLimits, Val, ValType};
use wasmtime_wasi::preview2;
#[cfg(feature = "wasi-nn")]
use wasmtime_wasi_nn::WasiNnCtx;
@ -226,7 +225,7 @@ impl RunCommand {
return Err(wasi_common::maybe_exit_on_error(e));
} else if store.data().preview2_ctx.is_some() {
if let Some(exit) = e
.downcast_ref::<preview2::I32Exit>()
.downcast_ref::<wasmtime_wasi::I32Exit>()
.map(|c| c.process_exit_code())
{
std::process::exit(exit);
@ -474,8 +473,11 @@ impl RunCommand {
let component = module.unwrap_component();
let (command, _instance) =
preview2::command::sync::Command::instantiate(&mut *store, component, linker)?;
let (command, _instance) = wasmtime_wasi::command::sync::Command::instantiate(
&mut *store,
component,
linker,
)?;
let result = command
.wasi_cli_run()
.call_run(&mut *store)
@ -486,7 +488,7 @@ impl RunCommand {
// explicit exit here with status 1 if `Err(())` is returned.
result.and_then(|wasm_result| match wasm_result {
Ok(()) => Ok(()),
Err(()) => Err(wasmtime_wasi::preview2::I32Exit(1).into()),
Err(()) => Err(wasmtime_wasi::I32Exit(1).into()),
})
}
};
@ -626,16 +628,16 @@ impl RunCommand {
// default-disabled in the future.
(Some(true), _) | (None, Some(false) | None) => {
if self.run.common.wasi.preview0 != Some(false) {
preview2::preview0::add_to_linker_sync(linker)?;
wasmtime_wasi::preview0::add_to_linker_sync(linker)?;
}
preview2::preview1::add_to_linker_sync(linker)?;
wasmtime_wasi::preview1::add_to_linker_sync(linker)?;
self.set_preview2_ctx(store)?;
}
}
}
#[cfg(feature = "component-model")]
CliLinker::Component(linker) => {
preview2::command::sync::add_to_linker(linker)?;
wasmtime_wasi::command::sync::add_to_linker(linker)?;
self.set_preview2_ctx(store)?;
}
}
@ -767,7 +769,7 @@ impl RunCommand {
}
fn set_preview2_ctx(&self, store: &mut Store<Host>) -> Result<()> {
let mut builder = preview2::WasiCtxBuilder::new();
let mut builder = wasmtime_wasi::WasiCtxBuilder::new();
builder.inherit_stdio().args(&self.compute_argv()?);
for (key, value) in self.vars.iter() {
@ -789,8 +791,8 @@ impl RunCommand {
for (name, dir) in self.compute_preopen_dirs()? {
builder.preopened_dir(
dir,
preview2::DirPerms::all(),
preview2::FilePerms::all(),
wasmtime_wasi::DirPerms::all(),
wasmtime_wasi::FilePerms::all(),
name,
);
}
@ -821,7 +823,7 @@ struct Host {
// The Mutex is only needed to satisfy the Sync constraint but we never
// actually perform any locking on it as we use Mutex::get_mut for every
// access.
preview2_ctx: Option<Arc<Mutex<preview2::WasiCtx>>>,
preview2_ctx: Option<Arc<Mutex<wasmtime_wasi::WasiCtx>>>,
// Resource table for preview2 if the `preview2_ctx` is in use, otherwise
// "just" an empty table.
@ -830,7 +832,7 @@ struct Host {
// State necessary for the preview1 implementation of WASI backed by the
// preview2 host implementation. Only used with the `--preview2` flag right
// now when running core modules.
preview2_adapter: Arc<preview2::preview1::WasiPreview1Adapter>,
preview2_adapter: Arc<wasmtime_wasi::preview1::WasiPreview1Adapter>,
#[cfg(feature = "wasi-nn")]
wasi_nn: Option<Arc<WasiNnCtx>>,
@ -843,30 +845,31 @@ struct Host {
guest_profiler: Option<Arc<wasmtime::GuestProfiler>>,
}
impl preview2::WasiView for Host {
impl wasmtime_wasi::WasiView for Host {
fn table(&mut self) -> &mut wasmtime::component::ResourceTable {
Arc::get_mut(&mut self.preview2_table)
.expect("preview2 is not compatible with threads")
.expect("wasmtime_wasi is not compatible with threads")
.get_mut()
.unwrap()
}
fn ctx(&mut self) -> &mut preview2::WasiCtx {
fn ctx(&mut self) -> &mut wasmtime_wasi::WasiCtx {
let ctx = self.preview2_ctx.as_mut().unwrap();
Arc::get_mut(ctx)
.expect("preview2 is not compatible with threads")
.expect("wasmtime_wasi is not compatible with threads")
.get_mut()
.unwrap()
}
}
impl preview2::preview1::WasiPreview1View for Host {
fn adapter(&self) -> &preview2::preview1::WasiPreview1Adapter {
impl wasmtime_wasi::preview1::WasiPreview1View for Host {
fn adapter(&self) -> &wasmtime_wasi::preview1::WasiPreview1Adapter {
&self.preview2_adapter
}
fn adapter_mut(&mut self) -> &mut preview2::preview1::WasiPreview1Adapter {
Arc::get_mut(&mut self.preview2_adapter).expect("preview2 is not compatible with threads")
fn adapter_mut(&mut self) -> &mut wasmtime_wasi::preview1::WasiPreview1Adapter {
Arc::get_mut(&mut self.preview2_adapter)
.expect("wasmtime_wasi is not compatible with threads")
}
}
@ -874,7 +877,7 @@ impl preview2::preview1::WasiPreview1View for Host {
impl wasmtime_wasi_http::types::WasiHttpView for Host {
fn ctx(&mut self) -> &mut WasiHttpCtx {
let ctx = self.wasi_http.as_mut().unwrap();
Arc::get_mut(ctx).expect("preview2 is not compatible with threads")
Arc::get_mut(ctx).expect("wasmtime_wasi is not compatible with threads")
}
fn table(&mut self) -> &mut wasmtime::component::ResourceTable {

12
src/commands/serve.rs

@ -11,7 +11,7 @@ use std::{
};
use wasmtime::component::{InstancePre, Linker};
use wasmtime::{Engine, Store, StoreLimits};
use wasmtime_wasi::preview2::{self, StreamError, StreamResult, WasiCtx, WasiCtxBuilder, WasiView};
use wasmtime_wasi::{StreamError, StreamResult, WasiCtx, WasiCtxBuilder, WasiView};
use wasmtime_wasi_http::io::TokioIo;
use wasmtime_wasi_http::{
bindings::http::types as http_types, body::HyperOutgoingBody, hyper_response_error,
@ -201,7 +201,7 @@ impl ServeCommand {
// bindings which adds just those interfaces that the proxy interface
// uses.
if self.run.common.wasi.common == Some(true) {
preview2::command::add_to_linker(linker)?;
wasmtime_wasi::command::add_to_linker(linker)?;
wasmtime_wasi_http::proxy::add_only_http_to_linker(linker)?;
} else {
wasmtime_wasi_http::proxy::add_to_linker(linker)?;
@ -478,8 +478,8 @@ struct LogStream {
output: Output,
}
impl preview2::StdoutStream for LogStream {
fn stream(&self) -> Box<dyn preview2::HostOutputStream> {
impl wasmtime_wasi::StdoutStream for LogStream {
fn stream(&self) -> Box<dyn wasmtime_wasi::HostOutputStream> {
Box::new(self.clone())
}
@ -493,7 +493,7 @@ impl preview2::StdoutStream for LogStream {
}
}
impl preview2::HostOutputStream for LogStream {
impl wasmtime_wasi::HostOutputStream for LogStream {
fn write(&mut self, bytes: bytes::Bytes) -> StreamResult<()> {
let mut msg = Vec::new();
@ -520,6 +520,6 @@ impl preview2::HostOutputStream for LogStream {
}
#[async_trait::async_trait]
impl preview2::Subscribe for LogStream {
impl wasmtime_wasi::Subscribe for LogStream {
async fn ready(&mut self) {}
}

Loading…
Cancel
Save