Skip to content

Commit

Permalink
Add GetHost to generated bindings for more flexible linking (#8448) (
Browse files Browse the repository at this point in the history
…#8620)

* Remove unused generated `add_root_to_linker` method

* WIP: bindgen GetHost

* Compile with Rust 1.78+

Use <https://users.rust-lang.org/t/generic-closure-returns-that-can-capture-arguments/76513/3>
as a guide of how to implement this by making the `GetHost` trait a bit
uglier.

* Add an option to skip `&mut T -> T` impls

Also enable this for WASI crates since they do their own thing with
`WasiView` for now. A future refactoring should be able to remove this
option entirely and switch wasi crates to a new design of `WasiView`.

* Update test expectations

* Review comments

* Undo temporary change

* Handle some TODOs

* Remove no-longer-relevant note

* Fix wasmtime-wasi-http doc link

---------

Co-authored-by: Lann <[email protected]>
  • Loading branch information
alexcrichton and lann committed May 14, 2024
1 parent 2690964 commit 37d71db
Show file tree
Hide file tree
Showing 10 changed files with 515 additions and 233 deletions.
9 changes: 9 additions & 0 deletions crates/component-macro/src/bindgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ impl Parse for Config {
.map(|p| p.into_token_stream().to_string())
.collect()
}
Opt::SkipMutForwardingImpls(val) => opts.skip_mut_forwarding_impls = val,
}
}
} else {
Expand Down Expand Up @@ -213,6 +214,7 @@ mod kw {
syn::custom_keyword!(only_imports);
syn::custom_keyword!(trappable_imports);
syn::custom_keyword!(additional_derives);
syn::custom_keyword!(skip_mut_forwarding_impls);
}

enum Opt {
Expand All @@ -227,6 +229,7 @@ enum Opt {
With(HashMap<String, String>),
TrappableImports(TrappableImports),
AdditionalDerives(Vec<syn::Path>),
SkipMutForwardingImpls(bool),
}

impl Parse for Opt {
Expand Down Expand Up @@ -367,6 +370,12 @@ impl Parse for Opt {
syn::bracketed!(contents in input);
let list = Punctuated::<_, Token![,]>::parse_terminated(&contents)?;
Ok(Opt::AdditionalDerives(list.iter().cloned().collect()))
} else if l.peek(kw::skip_mut_forwarding_impls) {
input.parse::<kw::skip_mut_forwarding_impls>()?;
input.parse::<Token![:]>()?;
Ok(Opt::SkipMutForwardingImpls(
input.parse::<syn::LitBool>()?.value,
))
} else {
Err(l.error())
}
Expand Down
3 changes: 2 additions & 1 deletion crates/wasi-http/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
//! 2. Add WASI HTTP interfaces to a [`wasmtime::component::Linker<T>`]. This is either
//! done through functions like [`proxy::add_to_linker`] (which bundles all interfaces
//! in the `wasi:http/proxy` world together) or through individual interfaces like the
//! [`bindings::http::outgoing_handler::add_to_linker`] function.
//! [`bindings::http::outgoing_handler::add_to_linker_get_host`] function.
//! 3. Use the previous [`wasmtime::component::Linker<T>::instantiate`] to instantiate
//! a [`wasmtime::component::Component`] within a [`wasmtime::Store<T>`]. If you're
//! targeting the `wasi:http/proxy` world, you can instantiate the component with
Expand Down Expand Up @@ -78,6 +78,7 @@ pub mod bindings {
trappable_error_type: {
"wasi:http/types/error-code" => crate::HttpError,
},
skip_mut_forwarding_impls: true,
});

pub use wasi::http;
Expand Down
59 changes: 37 additions & 22 deletions crates/wasi-http/src/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,26 +83,37 @@ pub fn add_to_linker<T>(l: &mut wasmtime::component::Linker<T>) -> anyhow::Resul
where
T: WasiHttpView + wasmtime_wasi::WasiView,
{
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)?;
let closure = type_annotate::<T, _>(|t| t);
wasmtime_wasi::bindings::clocks::wall_clock::add_to_linker_get_host(l, closure)?;
wasmtime_wasi::bindings::clocks::monotonic_clock::add_to_linker_get_host(l, closure)?;
wasmtime_wasi::bindings::io::poll::add_to_linker_get_host(l, closure)?;
wasmtime_wasi::bindings::io::error::add_to_linker_get_host(l, closure)?;
wasmtime_wasi::bindings::io::streams::add_to_linker_get_host(l, closure)?;
wasmtime_wasi::bindings::cli::stdin::add_to_linker_get_host(l, closure)?;
wasmtime_wasi::bindings::cli::stdout::add_to_linker_get_host(l, closure)?;
wasmtime_wasi::bindings::cli::stderr::add_to_linker_get_host(l, closure)?;
wasmtime_wasi::bindings::random::random::add_to_linker_get_host(l, closure)?;

add_only_http_to_linker(l)
}

// NB: workaround some rustc inference - a future refactoring may make this
// obsolete.
fn type_annotate<T, F>(val: F) -> F
where
F: Fn(&mut T) -> &mut T,
{
val
}

#[doc(hidden)]
pub fn add_only_http_to_linker<T>(l: &mut wasmtime::component::Linker<T>) -> anyhow::Result<()>
where
T: WasiHttpView + wasmtime_wasi::WasiView + crate::bindings::http::types::Host,
{
crate::bindings::http::outgoing_handler::add_to_linker(l, |t| t)?;
crate::bindings::http::types::add_to_linker(l, |t| t)?;
let closure = type_annotate::<T, _>(|t| t);
crate::bindings::http::outgoing_handler::add_to_linker_get_host(l, closure)?;
crate::bindings::http::types::add_to_linker_get_host(l, closure)?;

Ok(())
}
Expand Down Expand Up @@ -185,15 +196,17 @@ pub mod sync {
where
T: WasiHttpView + wasmtime_wasi::WasiView,
{
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::sync::io::poll::add_to_linker(l, |t| t)?;
wasmtime_wasi::bindings::sync::io::streams::add_to_linker(l, |t| t)?;
wasmtime_wasi::bindings::io::error::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)?;
let closure = super::type_annotate::<T, _>(|t| t);

wasmtime_wasi::bindings::clocks::wall_clock::add_to_linker_get_host(l, closure)?;
wasmtime_wasi::bindings::clocks::monotonic_clock::add_to_linker_get_host(l, closure)?;
wasmtime_wasi::bindings::sync::io::poll::add_to_linker_get_host(l, closure)?;
wasmtime_wasi::bindings::sync::io::streams::add_to_linker_get_host(l, closure)?;
wasmtime_wasi::bindings::io::error::add_to_linker_get_host(l, closure)?;
wasmtime_wasi::bindings::cli::stdin::add_to_linker_get_host(l, closure)?;
wasmtime_wasi::bindings::cli::stdout::add_to_linker_get_host(l, closure)?;
wasmtime_wasi::bindings::cli::stderr::add_to_linker_get_host(l, closure)?;
wasmtime_wasi::bindings::random::random::add_to_linker_get_host(l, closure)?;

add_only_http_to_linker(l)?;

Expand All @@ -206,8 +219,10 @@ pub mod sync {
where
T: WasiHttpView + wasmtime_wasi::WasiView + crate::bindings::http::types::Host,
{
crate::bindings::http::outgoing_handler::add_to_linker(l, |t| t)?;
crate::bindings::http::types::add_to_linker(l, |t| t)?;
let closure = super::type_annotate::<T, _>(|t| t);

crate::bindings::http::outgoing_handler::add_to_linker_get_host(l, closure)?;
crate::bindings::http::types::add_to_linker_get_host(l, closure)?;

Ok(())
}
Expand Down
38 changes: 33 additions & 5 deletions crates/wasi-http/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ impl WasiHttpCtx {
}

/// A trait which provides internal WASI HTTP state.
pub trait WasiHttpView: Send {
pub trait WasiHttpView {
/// Returns a mutable reference to the WASI HTTP context.
fn ctx(&mut self) -> &mut WasiHttpCtx;

Expand Down Expand Up @@ -71,10 +71,7 @@ pub trait WasiHttpView: Send {
&mut self,
request: hyper::Request<HyperOutgoingBody>,
config: OutgoingRequestConfig,
) -> crate::HttpResult<HostFutureIncomingResponse>
where
Self: Sized,
{
) -> crate::HttpResult<HostFutureIncomingResponse> {
Ok(default_send_request(request, config))
}

Expand All @@ -84,6 +81,37 @@ pub trait WasiHttpView: Send {
}
}

impl<T: ?Sized + WasiHttpView> WasiHttpView for &mut T {
fn ctx(&mut self) -> &mut WasiHttpCtx {
T::ctx(self)
}

fn table(&mut self) -> &mut ResourceTable {
T::table(self)
}

fn new_response_outparam(
&mut self,
result: tokio::sync::oneshot::Sender<
Result<hyper::Response<HyperOutgoingBody>, types::ErrorCode>,
>,
) -> wasmtime::Result<Resource<HostResponseOutparam>> {
T::new_response_outparam(self, result)
}

fn send_request(
&mut self,
request: hyper::Request<HyperOutgoingBody>,
config: OutgoingRequestConfig,
) -> crate::HttpResult<HostFutureIncomingResponse> {
T::send_request(self, request, config)
}

fn is_forbidden_header(&mut self, name: &HeaderName) -> bool {
T::is_forbidden_header(self, name)
}
}

/// Returns `true` when the header is forbidden according to this [`WasiHttpView`] implementation.
pub(crate) fn is_forbidden_header(view: &mut dyn WasiHttpView, name: &HeaderName) -> bool {
static FORBIDDEN_HEADERS: [HeaderName; 10] = [
Expand Down
2 changes: 1 addition & 1 deletion crates/wasi-nn/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ workspace = true

[dependencies]
# These dependencies are necessary for the WITX-generation macros to work:
anyhow = { workspace = true }
anyhow = { workspace = true, features = ['std'] }
wiggle = { workspace = true, features = ["wasmtime"] }

# This dependency is necessary for the WIT-generation macros to work:
Expand Down
11 changes: 3 additions & 8 deletions crates/wasi/src/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ pub mod sync {
"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,
}
},
skip_mut_forwarding_impls: true,
});
}
pub use self::generated::exports;
Expand All @@ -49,10 +50,6 @@ pub mod sync {
/// component through the [`Command::wasi_cli_run`] method plus
/// [`Guest::call_run`](exports::wasi::cli::run::Guest::call_run).
///
/// > **Note**: it's recommended to use
/// > [`wasmtime_wasi::add_to_linker_sync`] instead of the auto-generated
/// > [`Command::add_to_linker`] here.
///
/// [async]: wasmtime::Config::async_support
/// [`wasmtime_wasi::add_to_linker_sync`]: crate::add_to_linker_sync
///
Expand Down Expand Up @@ -202,6 +199,7 @@ mod async_io {
"wasi:cli/terminal-input/terminal-input": crate::stdio::TerminalInput,
"wasi:cli/terminal-output/terminal-output": crate::stdio::TerminalOutput,
},
skip_mut_forwarding_impls: true,
});
}

Expand All @@ -218,9 +216,6 @@ pub use self::async_io::wasi::*;
/// through the [`Command::wasi_cli_run`] method plus
/// [`Guest::call_run`](exports::wasi::cli::run::Guest::call_run).
///
/// > **Note**: it's recommended to use [`wasmtime_wasi::add_to_linker_async`]
/// > instead of the auto-generated [`Command::add_to_linker`] here.
///
/// [async]: wasmtime::Config::async_support
/// [`wasmtime_wasi::add_to_linker_async`]: crate::add_to_linker_async
///
Expand Down
9 changes: 9 additions & 0 deletions crates/wasi/src/ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,15 @@ pub trait WasiView: Send {
fn ctx(&mut self) -> &mut WasiCtx;
}

impl<T: ?Sized + WasiView> WasiView for &mut T {
fn table(&mut self) -> &mut ResourceTable {
T::table(self)
}
fn ctx(&mut self) -> &mut WasiCtx {
T::ctx(self)
}
}

/// Per-[`Store`] state which holds state necessary to implement WASI from this
/// crate.
///
Expand Down
Loading

0 comments on commit 37d71db

Please sign in to comment.