Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(wasi): add support for wasi:[email protected] #10063

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 13 additions & 8 deletions ci/vendor-wit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,24 @@ make_vendor() {
mkdir -p $path

for package in $packages; do
IFS='@' read -r repo tag <<< "$package"
mkdir -p $path/$repo
IFS='@' read -r repo tag subdir <<< "$package"
mkdir -p "$path/$package"
cached_extracted_dir="$cache_dir/$repo-$tag"

if [[ ! -d $cached_extracted_dir ]]; then
mkdir -p $cached_extracted_dir
curl -sL https://github.com/WebAssembly/wasi-$repo/archive/$tag.tar.gz | \
tar xzf - --strip-components=1 -C $cached_extracted_dir
rm -rf $cached_extracted_dir/wit/deps*
rm -rf $cached_extracted_dir/${subdir:-"wit"}/deps*
fi

cp -r $cached_extracted_dir/wit/* $path/$repo
cp -r $cached_extracted_dir/${subdir:-"wit"}/* $path/$package
done
}

cache_dir=$(mktemp -d)

make_vendor "wasi" "
make_vendor "wasi/src/p2" "
[email protected]
[email protected]
[email protected]
Expand All @@ -45,7 +45,12 @@ make_vendor "wasi" "
[email protected]
"

make_vendor "wasi-http" "
make_vendor "wasi/src/p3" "
clocks@[email protected]
random@[email protected]
"

make_vendor "wasi-http/src/p2" "
[email protected]
[email protected]
[email protected]
Expand All @@ -55,9 +60,9 @@ make_vendor "wasi-http" "
[email protected]
"

make_vendor "wasi-config" "config@f4d699b"
make_vendor "wasi-config/src/p2" "config@f4d699b"

make_vendor "wasi-keyvalue" "keyvalue@219ea36"
make_vendor "wasi-keyvalue/src/p2" "keyvalue@219ea36"

rm -rf $cache_dir

Expand Down
3 changes: 3 additions & 0 deletions crates/test-programs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@ anyhow = { workspace = true, features = ['std'] }
wasi = "0.11.0"
wasi-nn = "0.6.0"
wit-bindgen = { workspace = true, features = ['default'] }
# TODO: Remove once https://github.com/bytecodealliance/wit-bindgen/pull/1136 lands
wit-bindgen-rt = "0.37"
libc = { workspace = true }
getrandom = "0.2.9"
futures = { workspace = true, default-features = false, features = ['alloc'] }
url = { workspace = true }
sha2 = "0.10.2"
base64 = "0.21.0"
wasip2 = { version = "0.14.0", package = 'wasi' }
tokio = { workspace = true, features = ["macros"] }
1 change: 1 addition & 0 deletions crates/test-programs/artifacts/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ fn build_and_generate_tests() {
s if s.starts_with("http_") => "http",
s if s.starts_with("preview1_") => "preview1",
s if s.starts_with("preview2_") => "preview2",
s if s.starts_with("preview3_") => "preview3",
s if s.starts_with("cli_") => "cli",
s if s.starts_with("api_") => "api",
s if s.starts_with("nn_") => "nn",
Expand Down
2 changes: 1 addition & 1 deletion crates/test-programs/src/bin/api_reactor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::sync::{Mutex, MutexGuard};

wit_bindgen::generate!({
world: "test-reactor",
path: "../wasi/wit",
path: "../wasi/src/p2/wit",
generate_all,
});

Expand Down
2 changes: 1 addition & 1 deletion crates/test-programs/src/bin/preview2_random.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use test_programs::wasi::random;
use test_programs::wasi::random0_2_3 as random;

fn main() {
let mut bytes = [0_u8; 256];
Expand Down
2 changes: 1 addition & 1 deletion crates/test-programs/src/bin/preview2_sleep.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use test_programs::wasi::clocks::monotonic_clock;
use test_programs::wasi::clocks0_2_3::monotonic_clock;

fn main() {
sleep_10ms();
Expand Down
40 changes: 40 additions & 0 deletions crates/test-programs/src/bin/preview3_random.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use test_programs::wasi::random0_3_0 as random;

fn main() {
let mut bytes = [0_u8; 256];
getrandom::getrandom(&mut bytes).unwrap();

assert!(bytes.iter().any(|x| *x != 0));

// Acquired random bytes should be of the expected length.
let array = random::random::get_random_bytes(100);
assert_eq!(array.len(), 100);

// It shouldn't take 100+ tries to get a nonzero random integer.
for i in 0.. {
if random::random::get_random_u64() == 0 {
continue;
}
assert!(i < 100);
break;
}

// The `insecure_seed` API should return the same result each time.
let (a1, b1) = random::insecure_seed::insecure_seed();
let (a2, b2) = random::insecure_seed::insecure_seed();
assert_eq!(a1, a2);
assert_eq!(b1, b2);

// Acquired random bytes should be of the expected length.
let array = random::insecure::get_insecure_random_bytes(100);
assert_eq!(array.len(), 100);

// It shouldn't take 100+ tries to get a nonzero random integer.
for i in 0.. {
if random::insecure::get_insecure_random_u64() == 0 {
continue;
}
assert!(i < 100);
break;
}
}
62 changes: 62 additions & 0 deletions crates/test-programs/src/bin/preview3_sleep.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use core::future::Future as _;
use core::pin::pin;
use core::ptr;
use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};

use test_programs::wasi::clocks0_3_0::monotonic_clock;

// Adapted from https://github.com/rust-lang/rust/blob/cd805f09ffbfa3896c8f50a619de9b67e1d9f3c3/library/core/src/task/wake.rs#L63-L77
// TODO: Replace by `Waker::noop` once MSRV is raised to 1.85
const NOOP_RAW_WAKER: RawWaker = {
const VTABLE: RawWakerVTable = RawWakerVTable::new(
// Cloning just returns a new no-op raw waker
|_| NOOP_RAW_WAKER,
// `wake` does nothing
|_| {},
// `wake_by_ref` does nothing
|_| {},
// Dropping does nothing as we don't allocate anything
|_| {},
);
RawWaker::new(ptr::null(), &VTABLE)
};

const NOOP_WAKER: &'static Waker = &unsafe { Waker::from_raw(NOOP_RAW_WAKER) };

#[tokio::main(flavor = "current_thread")]
async fn main() {
sleep_10ms().await;
sleep_0ms();
sleep_backwards_in_time();
}

async fn sleep_10ms() {
let dur = 10_000_000;
monotonic_clock::wait_until(monotonic_clock::now() + dur).await;
monotonic_clock::wait_for(dur).await;
}

fn sleep_0ms() {
let mut cx = Context::from_waker(NOOP_WAKER);

assert_eq!(
pin!(monotonic_clock::wait_until(monotonic_clock::now())).poll(&mut cx),
Poll::Ready(()),
"waiting until now() is ready immediately",
);
assert_eq!(
pin!(monotonic_clock::wait_for(0)).poll(&mut cx),
Poll::Ready(()),
"waiting for 0 is ready immediately",
);
}

fn sleep_backwards_in_time() {
let mut cx = Context::from_waker(NOOP_WAKER);

assert_eq!(
pin!(monotonic_clock::wait_until(monotonic_clock::now() - 1)).poll(&mut cx),
Poll::Ready(()),
"waiting until instant which has passed is ready immediately",
);
}
24 changes: 17 additions & 7 deletions crates/test-programs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,46 @@ wit_bindgen::generate!({
include wasi:http/[email protected];
include wasi:config/[email protected];
include wasi:keyvalue/[email protected];

include wasi:clocks/[email protected];
include wasi:random/[email protected];
}
",
path: [
"../wasi-http/wit",
"../wasi-config/wit",
"../wasi-keyvalue/wit",
"../wasi/src/p3/wit",
"../wasi-http/src/p2/wit",
"../wasi-config/src/p2/wit",
"../wasi-keyvalue/src/p2/wit",
],
world: "wasmtime:test/test",
features: ["cli-exit-with-code"],
async: {
imports: [
"wasi:clocks/[email protected]#wait-for",
"wasi:clocks/[email protected]#wait-until",
],
},
generate_all,
});

pub mod proxy {
wit_bindgen::generate!({
path: "../wasi-http/wit",
path: "../wasi-http/src/p2/wit",
world: "wasi:http/proxy",
default_bindings_module: "test_programs::proxy",
pub_export_macro: true,
with: {
"wasi:http/[email protected]": crate::wasi::http::types,
"wasi:http/[email protected]": crate::wasi::http::outgoing_handler,
"wasi:random/[email protected]": crate::wasi::random::random,
"wasi:random/[email protected]": crate::wasi::random0_2_3::random,
"wasi:io/[email protected]": crate::wasi::io::error,
"wasi:io/[email protected]": crate::wasi::io::poll,
"wasi:io/[email protected]": crate::wasi::io::streams,
"wasi:cli/[email protected]": crate::wasi::cli::stdout,
"wasi:cli/[email protected]": crate::wasi::cli::stderr,
"wasi:cli/[email protected]": crate::wasi::cli::stdin,
"wasi:clocks/[email protected]": crate::wasi::clocks::monotonic_clock,
"wasi:clocks/[email protected]": crate::wasi::clocks::wall_clock,
"wasi:clocks/[email protected]": crate::wasi::clocks0_2_3::monotonic_clock,
"wasi:clocks/[email protected]": crate::wasi::clocks0_2_3::wall_clock,
},
});
}
4 changes: 2 additions & 2 deletions crates/test-programs/src/sockets.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::wasi::clocks::monotonic_clock;
use crate::wasi::clocks0_2_3::monotonic_clock;
use crate::wasi::io::poll::{self, Pollable};
use crate::wasi::io::streams::{InputStream, OutputStream, StreamError};
use crate::wasi::random;
use crate::wasi::random0_2_3 as random;
use crate::wasi::sockets::instance_network;
use crate::wasi::sockets::ip_name_lookup;
use crate::wasi::sockets::network::{
Expand Down
36 changes: 3 additions & 33 deletions crates/wasi-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,11 @@

#![deny(missing_docs)]

use anyhow::Result;
use std::collections::HashMap;

mod gen_ {
wasmtime::component::bindgen!({
path: "wit",
world: "wasi:config/imports",
trappable_imports: true,
});
}
use self::gen_::wasi::config::store as generated;
pub mod p2;
#[doc(inline)]
pub use p2::*;

/// Capture the state necessary for use in the `wasi-config` API implementation.
#[derive(Default)]
Expand Down Expand Up @@ -123,27 +117,3 @@ impl<'a> WasiConfig<'a> {
Self { vars }
}
}

impl generated::Host for WasiConfig<'_> {
fn get(&mut self, key: String) -> Result<Result<Option<String>, generated::Error>> {
Ok(Ok(self.vars.0.get(&key).map(|s| s.to_owned())))
}

fn get_all(&mut self) -> Result<Result<Vec<(String, String)>, generated::Error>> {
Ok(Ok(self
.vars
.0
.iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect()))
}
}

/// Add all the `wasi-config` world's interfaces to a [`wasmtime::component::Linker`].
pub fn add_to_linker<T>(
l: &mut wasmtime::component::Linker<T>,
f: impl Fn(&mut T) -> WasiConfig<'_> + Send + Sync + Copy + 'static,
) -> Result<()> {
generated::add_to_linker_get_host(l, f)?;
Ok(())
}
36 changes: 36 additions & 0 deletions crates/wasi-config/src/p2/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//! Implementation of wasip2 version of `wasi:config` package

mod gen_ {
wasmtime::component::bindgen!({
path: "src/p2/wit",
world: "wasi:config/imports",
trappable_imports: true,
});
}
use self::gen_::wasi::config::store as generated;

use crate::WasiConfig;

impl generated::Host for WasiConfig<'_> {
fn get(&mut self, key: String) -> anyhow::Result<Result<Option<String>, generated::Error>> {
Ok(Ok(self.vars.0.get(&key).map(|s| s.to_owned())))
}

fn get_all(&mut self) -> anyhow::Result<Result<Vec<(String, String)>, generated::Error>> {
Ok(Ok(self
.vars
.0
.iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect()))
}
}

/// Add all the `wasi-config` world's interfaces to a [`wasmtime::component::Linker`].
pub fn add_to_linker<T>(
l: &mut wasmtime::component::Linker<T>,
f: impl Fn(&mut T) -> WasiConfig<'_> + Send + Sync + Copy + 'static,
) -> anyhow::Result<()> {
generated::add_to_linker_get_host(l, f)?;
Ok(())
}
2 changes: 1 addition & 1 deletion crates/wasi-http/src/body.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Implementation of the `wasi:http/types` interface's various body types.

use crate::{bindings::http::types, types::FieldMap};
use crate::{p2::bindings::http::types, types::FieldMap};
use anyhow::anyhow;
use bytes::Bytes;
use http_body::{Body, Frame};
Expand Down
4 changes: 2 additions & 2 deletions crates/wasi-http/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::bindings::http::types::ErrorCode;
use crate::p2::bindings::http::types::ErrorCode;
use std::error::Error;
use std::fmt;
use wasmtime_wasi::ResourceTableError;
Expand Down Expand Up @@ -59,7 +59,7 @@ impl fmt::Display for HttpError {
impl Error for HttpError {}

pub(crate) fn dns_error(rcode: String, info_code: u16) -> ErrorCode {
ErrorCode::DnsError(crate::bindings::http::types::DnsErrorPayload {
ErrorCode::DnsError(crate::p2::bindings::http::types::DnsErrorPayload {
rcode: Some(rcode),
info_code: Some(info_code),
})
Expand Down
Loading
Loading