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

Add GetHost to generated bindings for more flexible linking #8448

Merged
merged 10 commits into from May 13, 2024
9 changes: 9 additions & 0 deletions crates/component-macro/src/bindgen.rs
Expand Up @@ -149,6 +149,7 @@ impl Parse for Config {
.collect()
}
Opt::Stringify(val) => opts.stringify = val,
Opt::SkipMutForwardingImpls(val) => opts.skip_mut_forwarding_impls = val,
}
}
} else {
Expand Down Expand Up @@ -219,6 +220,7 @@ mod kw {
syn::custom_keyword!(trappable_imports);
syn::custom_keyword!(additional_derives);
syn::custom_keyword!(stringify);
syn::custom_keyword!(skip_mut_forwarding_impls);
}

enum Opt {
Expand All @@ -234,6 +236,7 @@ enum Opt {
TrappableImports(TrappableImports),
AdditionalDerives(Vec<syn::Path>),
Stringify(bool),
SkipMutForwardingImpls(bool),
}

impl Parse for Opt {
Expand Down Expand Up @@ -378,6 +381,12 @@ impl Parse for Opt {
input.parse::<kw::stringify>()?;
input.parse::<Token![:]>()?;
Ok(Opt::Stringify(input.parse::<syn::LitBool>()?.value))
} 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
42 changes: 35 additions & 7 deletions crates/component-macro/tests/expanded/char.rs
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side note: we should pick a small subset of these tests with good feature coverage to expand. As-is these are useful for review but its hard to know how many/which files to review before stopping, and having so many changed files screws with Github's UI...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point yeah I've been a bit sad that the actual changes were all hidden by default as well...

Expand Up @@ -77,35 +77,63 @@ pub mod foo {
/// A function that returns a character
fn return_char(&mut self) -> char;
}
pub fn add_to_linker<T, U>(
linker: &mut wasmtime::component::Linker<T>,
get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static,
) -> wasmtime::Result<()>
pub trait GetHost<
T,
>: Fn(T) -> <Self as GetHost<T>>::Host + Send + Sync + Copy + 'static {
type Host: Host;
}
impl<F, T, O> GetHost<T> for F
where
U: Host,
F: Fn(T) -> O + Send + Sync + Copy + 'static,
O: Host,
{
type Host = O;
}
pub fn add_to_linker_get_host<T>(
linker: &mut wasmtime::component::Linker<T>,
host_getter: impl for<'a> GetHost<&'a mut T>,
) -> wasmtime::Result<()> {
let mut inst = linker.instance("foo:foo/chars")?;
inst.func_wrap(
"take-char",
move |
mut caller: wasmtime::StoreContextMut<'_, T>,
(arg0,): (char,)|
{
let host = get(caller.data_mut());
let host = &mut host_getter(caller.data_mut());
let r = Host::take_char(host, arg0);
Ok(r)
},
)?;
inst.func_wrap(
"return-char",
move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| {
let host = get(caller.data_mut());
let host = &mut host_getter(caller.data_mut());
let r = Host::return_char(host);
Ok((r,))
},
)?;
Ok(())
}
pub fn add_to_linker<T, U>(
linker: &mut wasmtime::component::Linker<T>,
get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static,
) -> wasmtime::Result<()>
where
U: Host,
{
add_to_linker_get_host(linker, get)
}
impl<_T: Host + ?Sized> Host for &mut _T {
/// A function that accepts a character
fn take_char(&mut self, x: char) -> () {
Host::take_char(*self, x)
}
/// A function that returns a character
fn return_char(&mut self) -> char {
Host::return_char(*self)
}
}
}
}
}
Expand Down
46 changes: 39 additions & 7 deletions crates/component-macro/tests/expanded/char_async.rs
Expand Up @@ -10,8 +10,8 @@ const _: () = {
get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static,
) -> wasmtime::Result<()>
where
U: foo::foo::chars::Host + Send,
T: Send,
U: foo::foo::chars::Host + Send,
{
foo::foo::chars::add_to_linker(linker, get)?;
Ok(())
Expand Down Expand Up @@ -73,19 +73,30 @@ pub mod foo {
#[allow(unused_imports)]
use wasmtime::component::__internal::anyhow;
#[wasmtime::component::__internal::async_trait]
pub trait Host {
pub trait Host: Send {
/// A function that accepts a character
async fn take_char(&mut self, x: char) -> ();
/// A function that returns a character
async fn return_char(&mut self) -> char;
}
pub fn add_to_linker<T, U>(
pub trait GetHost<
T,
>: Fn(T) -> <Self as GetHost<T>>::Host + Send + Sync + Copy + 'static {
type Host: Host + Send;
}
impl<F, T, O> GetHost<T> for F
where
F: Fn(T) -> O + Send + Sync + Copy + 'static,
O: Host + Send,
{
type Host = O;
}
pub fn add_to_linker_get_host<T>(
linker: &mut wasmtime::component::Linker<T>,
get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static,
host_getter: impl for<'a> GetHost<&'a mut T>,
) -> wasmtime::Result<()>
where
T: Send,
U: Host + Send,
{
let mut inst = linker.instance("foo:foo/chars")?;
inst.func_wrap_async(
Expand All @@ -94,21 +105,42 @@ pub mod foo {
mut caller: wasmtime::StoreContextMut<'_, T>,
(arg0,): (char,)|
wasmtime::component::__internal::Box::new(async move {
let host = get(caller.data_mut());
let host = &mut host_getter(caller.data_mut());
let r = Host::take_char(host, arg0).await;
Ok(r)
}),
)?;
inst.func_wrap_async(
"return-char",
move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| wasmtime::component::__internal::Box::new(async move {
let host = get(caller.data_mut());
let host = &mut host_getter(caller.data_mut());
let r = Host::return_char(host).await;
Ok((r,))
}),
)?;
Ok(())
}
pub fn add_to_linker<T, U>(
linker: &mut wasmtime::component::Linker<T>,
get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static,
) -> wasmtime::Result<()>
where
U: Host + Send,
T: Send,
{
add_to_linker_get_host(linker, get)
}
#[wasmtime::component::__internal::async_trait]
impl<_T: Host + ?Sized + Send> Host for &mut _T {
/// A function that accepts a character
async fn take_char(&mut self, x: char) -> () {
Host::take_char(*self, x).await
}
/// A function that returns a character
async fn return_char(&mut self) -> char {
Host::return_char(*self).await
}
}
}
}
}
Expand Down
96 changes: 79 additions & 17 deletions crates/component-macro/tests/expanded/conventions.rs
Expand Up @@ -125,18 +125,27 @@ pub mod foo {
/// Identifiers with the same name as keywords are quoted.
fn bool(&mut self) -> ();
}
pub fn add_to_linker<T, U>(
linker: &mut wasmtime::component::Linker<T>,
get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static,
) -> wasmtime::Result<()>
pub trait GetHost<
T,
>: Fn(T) -> <Self as GetHost<T>>::Host + Send + Sync + Copy + 'static {
type Host: Host;
}
impl<F, T, O> GetHost<T> for F
where
U: Host,
F: Fn(T) -> O + Send + Sync + Copy + 'static,
O: Host,
{
type Host = O;
}
pub fn add_to_linker_get_host<T>(
linker: &mut wasmtime::component::Linker<T>,
host_getter: impl for<'a> GetHost<&'a mut T>,
) -> wasmtime::Result<()> {
let mut inst = linker.instance("foo:foo/conventions")?;
inst.func_wrap(
"kebab-case",
move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| {
let host = get(caller.data_mut());
let host = &mut host_getter(caller.data_mut());
let r = Host::kebab_case(host);
Ok(r)
},
Expand All @@ -147,93 +156,146 @@ pub mod foo {
mut caller: wasmtime::StoreContextMut<'_, T>,
(arg0,): (LudicrousSpeed,)|
{
let host = get(caller.data_mut());
let host = &mut host_getter(caller.data_mut());
let r = Host::foo(host, arg0);
Ok(r)
},
)?;
inst.func_wrap(
"function-with-dashes",
move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| {
let host = get(caller.data_mut());
let host = &mut host_getter(caller.data_mut());
let r = Host::function_with_dashes(host);
Ok(r)
},
)?;
inst.func_wrap(
"function-with-no-weird-characters",
move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| {
let host = get(caller.data_mut());
let host = &mut host_getter(caller.data_mut());
let r = Host::function_with_no_weird_characters(host);
Ok(r)
},
)?;
inst.func_wrap(
"apple",
move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| {
let host = get(caller.data_mut());
let host = &mut host_getter(caller.data_mut());
let r = Host::apple(host);
Ok(r)
},
)?;
inst.func_wrap(
"apple-pear",
move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| {
let host = get(caller.data_mut());
let host = &mut host_getter(caller.data_mut());
let r = Host::apple_pear(host);
Ok(r)
},
)?;
inst.func_wrap(
"apple-pear-grape",
move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| {
let host = get(caller.data_mut());
let host = &mut host_getter(caller.data_mut());
let r = Host::apple_pear_grape(host);
Ok(r)
},
)?;
inst.func_wrap(
"a0",
move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| {
let host = get(caller.data_mut());
let host = &mut host_getter(caller.data_mut());
let r = Host::a0(host);
Ok(r)
},
)?;
inst.func_wrap(
"is-XML",
move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| {
let host = get(caller.data_mut());
let host = &mut host_getter(caller.data_mut());
let r = Host::is_xml(host);
Ok(r)
},
)?;
inst.func_wrap(
"explicit",
move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| {
let host = get(caller.data_mut());
let host = &mut host_getter(caller.data_mut());
let r = Host::explicit(host);
Ok(r)
},
)?;
inst.func_wrap(
"explicit-kebab",
move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| {
let host = get(caller.data_mut());
let host = &mut host_getter(caller.data_mut());
let r = Host::explicit_kebab(host);
Ok(r)
},
)?;
inst.func_wrap(
"bool",
move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| {
let host = get(caller.data_mut());
let host = &mut host_getter(caller.data_mut());
let r = Host::bool(host);
Ok(r)
},
)?;
Ok(())
}
pub fn add_to_linker<T, U>(
linker: &mut wasmtime::component::Linker<T>,
get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static,
) -> wasmtime::Result<()>
where
U: Host,
{
add_to_linker_get_host(linker, get)
}
impl<_T: Host + ?Sized> Host for &mut _T {
fn kebab_case(&mut self) -> () {
Host::kebab_case(*self)
}
fn foo(&mut self, x: LudicrousSpeed) -> () {
Host::foo(*self, x)
}
fn function_with_dashes(&mut self) -> () {
Host::function_with_dashes(*self)
}
fn function_with_no_weird_characters(&mut self) -> () {
Host::function_with_no_weird_characters(*self)
}
fn apple(&mut self) -> () {
Host::apple(*self)
}
fn apple_pear(&mut self) -> () {
Host::apple_pear(*self)
}
fn apple_pear_grape(&mut self) -> () {
Host::apple_pear_grape(*self)
}
fn a0(&mut self) -> () {
Host::a0(*self)
}
/// Comment out identifiers that collide when mapped to snake_case, for now; see
/// https://github.com/WebAssembly/component-model/issues/118
/// APPLE: func()
/// APPLE-pear-GRAPE: func()
/// apple-PEAR-grape: func()
fn is_xml(&mut self) -> () {
Host::is_xml(*self)
}
fn explicit(&mut self) -> () {
Host::explicit(*self)
}
fn explicit_kebab(&mut self) -> () {
Host::explicit_kebab(*self)
}
/// Identifiers with the same name as keywords are quoted.
fn bool(&mut self) -> () {
Host::bool(*self)
}
}
}
}
}
Expand Down