diff --git a/crates/fuzzing/src/generators/component_types.rs b/crates/fuzzing/src/generators/component_types.rs index 133b990fc2..0c83bf10cf 100644 --- a/crates/fuzzing/src/generators/component_types.rs +++ b/crates/fuzzing/src/generators/component_types.rs @@ -158,7 +158,7 @@ macro_rules! define_static_api_test { .func_wrap( IMPORT_FUNCTION, |cx: StoreContextMut<'_, Box>, - $($param_name: $param,)*| + ($($param_name,)*): ($($param,)*)| { log::trace!("received parameters {:?}", ($(&$param_name,)*)); let data: &($($param,)* R,) = diff --git a/crates/wasmtime/src/component/func/host.rs b/crates/wasmtime/src/component/func/host.rs index e8cc501828..023acba474 100644 --- a/crates/wasmtime/src/component/func/host.rs +++ b/crates/wasmtime/src/component/func/host.rs @@ -17,18 +17,28 @@ use wasmtime_runtime::component::{ }; use wasmtime_runtime::{VMCallerCheckedAnyfunc, VMMemoryDefinition, VMOpaqueContext}; -/// Trait representing host-defined functions that can be imported into a wasm -/// component. -/// -/// For more information see the -/// [`func_wrap`](crate::component::LinkerInstance::func_wrap) documentation. -pub trait IntoComponentFunc { - /// Host entrypoint from a cranelift-generated trampoline. - /// - /// This function has type `VMLoweringCallee` and delegates to the shared - /// `call_host` function below. - #[doc(hidden)] - extern "C" fn entrypoint( +pub struct HostFunc { + entrypoint: VMLoweringCallee, + typecheck: Box) -> Result<()>) + Send + Sync>, + func: Box, +} + +impl HostFunc { + pub(crate) fn from_closure(func: F) -> Arc + where + F: Fn(StoreContextMut, P) -> Result + Send + Sync + 'static, + P: ComponentNamedList + Lift + 'static, + R: ComponentNamedList + Lower + 'static, + { + let entrypoint = Self::entrypoint::; + Arc::new(HostFunc { + entrypoint, + typecheck: Box::new(typecheck::), + func: Box::new(func), + }) + } + + extern "C" fn entrypoint( cx: *mut VMOpaqueContext, data: *mut u8, flags: InstanceFlags, @@ -37,30 +47,25 @@ pub trait IntoComponentFunc { string_encoding: StringEncoding, storage: *mut ValRaw, storage_len: usize, - ); - - #[doc(hidden)] - fn into_host_func(self) -> Arc; -} - -pub struct HostFunc { - entrypoint: VMLoweringCallee, - typecheck: Box) -> Result<()>) + Send + Sync>, - func: Box, -} - -impl HostFunc { - fn new(func: F, entrypoint: VMLoweringCallee) -> Arc - where - F: Send + Sync + 'static, + ) where + F: Fn(StoreContextMut, P) -> Result, P: ComponentNamedList + Lift + 'static, R: ComponentNamedList + Lower + 'static, { - Arc::new(HostFunc { - entrypoint, - typecheck: Box::new(typecheck::), - func: Box::new(func), - }) + let data = data as *const F; + unsafe { + handle_result(|| { + call_host::<_, _, _, _>( + cx, + flags, + memory, + realloc, + string_encoding, + std::slice::from_raw_parts_mut(storage, storage_len), + |store, args| (*data)(store, args), + ) + }) + } } pub(crate) fn new_dynamic( @@ -122,10 +127,10 @@ where /// The "meat" of calling a host function from wasm. /// -/// This function is delegated to from implementations of `IntoComponentFunc` -/// generated in the macro below. Most of the arguments from the `entrypoint` -/// are forwarded here except for the `data` pointer which is encapsulated in -/// the `closure` argument here. +/// This function is delegated to from implementations of +/// `HostFunc::from_closure`. Most of the arguments from the `entrypoint` are +/// forwarded here except for the `data` pointer which is encapsulated in the +/// `closure` argument here. /// /// This function is parameterized over: /// @@ -270,88 +275,6 @@ unsafe fn handle_result(func: impl FnOnce() -> Result<()>) { } } -macro_rules! impl_into_component_func { - ($num:tt $($args:ident)*) => { - // Implement for functions without a leading `StoreContextMut` parameter - #[allow(non_snake_case)] - impl IntoComponentFunc for F - where - F: Fn($($args),*) -> Result + Send + Sync + 'static, - ($($args,)*): ComponentNamedList + Lift + 'static, - R: ComponentNamedList + Lower + 'static, - { - extern "C" fn entrypoint( - cx: *mut VMOpaqueContext, - data: *mut u8, - flags: InstanceFlags, - memory: *mut VMMemoryDefinition, - realloc: *mut VMCallerCheckedAnyfunc, - string_encoding: StringEncoding, - storage: *mut ValRaw, - storage_len: usize, - ) { - let data = data as *const Self; - unsafe { - handle_result(|| call_host::( - cx, - flags, - memory, - realloc, - string_encoding, - std::slice::from_raw_parts_mut(storage, storage_len), - |_, ($($args,)*)| (*data)($($args),*), - )) - } - } - - fn into_host_func(self) -> Arc { - let entrypoint = >::entrypoint; - HostFunc::new::<_, ($($args,)*), R>(self, entrypoint) - } - } - - // Implement for functions with a leading `StoreContextMut` parameter - #[allow(non_snake_case)] - impl IntoComponentFunc, $($args,)*), R> for F - where - F: Fn(StoreContextMut<'_, T>, $($args),*) -> Result + Send + Sync + 'static, - ($($args,)*): ComponentNamedList + Lift + 'static, - R: ComponentNamedList + Lower + 'static, - { - extern "C" fn entrypoint( - cx: *mut VMOpaqueContext, - data: *mut u8, - flags: InstanceFlags, - memory: *mut VMMemoryDefinition, - realloc: *mut VMCallerCheckedAnyfunc, - string_encoding: StringEncoding, - storage: *mut ValRaw, - storage_len: usize, - ) { - let data = data as *const Self; - unsafe { - handle_result(|| call_host::( - cx, - flags, - memory, - realloc, - string_encoding, - std::slice::from_raw_parts_mut(storage, storage_len), - |store, ($($args,)*)| (*data)(store, $($args),*), - )) - } - } - - fn into_host_func(self) -> Arc { - let entrypoint = , $($args,)*), R>>::entrypoint; - HostFunc::new::<_, ($($args,)*), R>(self, entrypoint) - } - } - } -} - -for_each_function_signature!(impl_into_component_func); - unsafe fn call_host_dynamic( Types { params, results }: &Types, cx: *mut VMOpaqueContext, diff --git a/crates/wasmtime/src/component/linker.rs b/crates/wasmtime/src/component/linker.rs index 8978abb0d4..c5e230c93a 100644 --- a/crates/wasmtime/src/component/linker.rs +++ b/crates/wasmtime/src/component/linker.rs @@ -1,7 +1,7 @@ use crate::component::func::HostFunc; use crate::component::instance::RuntimeImport; use crate::component::matching::TypeChecker; -use crate::component::{Component, Instance, InstancePre, IntoComponentFunc, Val}; +use crate::component::{Component, ComponentNamedList, Instance, InstancePre, Lift, Lower, Val}; use crate::{AsContextMut, Engine, Module, StoreContextMut}; use anyhow::{anyhow, bail, Context, Result}; use std::collections::hash_map::{Entry, HashMap}; @@ -209,11 +209,8 @@ impl LinkerInstance<'_, T> { /// types that will come from wasm and `Return` is a value coming from the /// host going back to wasm. /// - /// The [`IntoComponentFunc`] trait is implemented for functions whose - /// arguments and return values implement the - /// [`ComponentType`](crate::component::ComponentType) trait. Additionally - /// the `func` may take a [`StoreContextMut`](crate::StoreContextMut) as its - /// first parameter. + /// Additionally the `func` takes a + /// [`StoreContextMut`](crate::StoreContextMut) as its first parameter. /// /// Note that `func` must be an `Fn` and must also be `Send + Sync + /// 'static`. Shared state within a func is typically accessed with the `T` @@ -222,13 +219,14 @@ impl LinkerInstance<'_, T> { /// argument which can be provided to the `func` given here. // // TODO: needs more words and examples - pub fn func_wrap( - &mut self, - name: &str, - func: impl IntoComponentFunc, - ) -> Result<()> { + pub fn func_wrap(&mut self, name: &str, func: F) -> Result<()> + where + F: Fn(StoreContextMut, Params) -> Result + Send + Sync + 'static, + Params: ComponentNamedList + Lift + 'static, + Return: ComponentNamedList + Lower + 'static, + { let name = self.strings.intern(name); - self.insert(name, Definition::Func(func.into_host_func())) + self.insert(name, Definition::Func(HostFunc::from_closure(func))) } /// Define a new host-provided function using dynamic types. diff --git a/crates/wasmtime/src/component/mod.rs b/crates/wasmtime/src/component/mod.rs index 1b49ca0e3e..fef7fa53f9 100644 --- a/crates/wasmtime/src/component/mod.rs +++ b/crates/wasmtime/src/component/mod.rs @@ -14,8 +14,7 @@ pub mod types; mod values; pub use self::component::Component; pub use self::func::{ - ComponentNamedList, ComponentType, Func, IntoComponentFunc, Lift, Lower, TypedFunc, WasmList, - WasmStr, + ComponentNamedList, ComponentType, Func, Lift, Lower, TypedFunc, WasmList, WasmStr, }; pub use self::instance::{ExportInstance, Exports, Instance, InstancePre}; pub use self::linker::{Linker, LinkerInstance}; diff --git a/crates/wast/src/spectest.rs b/crates/wast/src/spectest.rs index a02d86970f..29b8300915 100644 --- a/crates/wast/src/spectest.rs +++ b/crates/wast/src/spectest.rs @@ -48,11 +48,13 @@ pub fn link_spectest(linker: &mut Linker, store: &mut Store) -> Result< #[cfg(feature = "component-model")] pub fn link_component_spectest(linker: &mut component::Linker) -> Result<()> { let engine = linker.engine().clone(); - linker.root().func_wrap("host-return-two", || Ok((2u32,)))?; + linker + .root() + .func_wrap("host-return-two", |_, _: ()| Ok((2u32,)))?; let mut i = linker.instance("host")?; - i.func_wrap("return-three", || Ok((3u32,)))?; + i.func_wrap("return-three", |_, _: ()| Ok((3u32,)))?; i.instance("nested")? - .func_wrap("return-four", || Ok((4u32,)))?; + .func_wrap("return-four", |_, _: ()| Ok((4u32,)))?; let module = Module::new( &engine, diff --git a/tests/all/component_model/func.rs b/tests/all/component_model/func.rs index 07a930bc8a..5c3027a5ef 100644 --- a/tests/all/component_model/func.rs +++ b/tests/all/component_model/func.rs @@ -2004,12 +2004,13 @@ fn drop_component_still_works() -> Result<()> { let component = Component::new(&engine, component)?; let mut store = Store::new(&engine, 0); let mut linker = Linker::new(&engine); - linker - .root() - .func_wrap("f", |mut store: StoreContextMut<'_, u32>| -> Result<()> { + linker.root().func_wrap( + "f", + |mut store: StoreContextMut<'_, u32>, _: ()| -> Result<()> { *store.data_mut() += 1; Ok(()) - })?; + }, + )?; let instance = linker.instantiate(&mut store, &component)?; (store, instance) }; @@ -2216,7 +2217,7 @@ fn lower_then_lift() -> Result<()> { let component = Component::new(&engine, component)?; let mut store = Store::new(&engine, ()); let mut linker = Linker::new(&engine); - linker.root().func_wrap("f", || Ok((2u32,)))?; + linker.root().func_wrap("f", |_, _: ()| Ok((2u32,)))?; let instance = linker.instantiate(&mut store, &component)?; let f = instance.get_typed_func::<(), (i32,), _>(&mut store, "f")?; @@ -2252,7 +2253,7 @@ fn lower_then_lift() -> Result<()> { let mut store = Store::new(&engine, ()); linker .root() - .func_wrap("s", |store: StoreContextMut<'_, ()>, x: WasmStr| { + .func_wrap("s", |store: StoreContextMut<'_, ()>, (x,): (WasmStr,)| { assert_eq!(x.to_str(&store)?, "hello"); Ok(()) })?; @@ -2292,7 +2293,7 @@ fn lower_then_lift() -> Result<()> { let mut store = Store::new(&engine, ()); linker .root() - .func_wrap("s2", |store: StoreContextMut<'_, ()>, x: WasmStr| { + .func_wrap("s2", |store: StoreContextMut<'_, ()>, (x,): (WasmStr,)| { assert_eq!(x.to_str(&store)?, "hello"); Ok((u32::MAX,)) })?; diff --git a/tests/all/component_model/import.rs b/tests/all/component_model/import.rs index e6ec69dfcf..79cabe6ba3 100644 --- a/tests/all/component_model/import.rs +++ b/tests/all/component_model/import.rs @@ -127,7 +127,7 @@ fn simple() -> Result<()> { let mut linker = Linker::new(&engine); linker.root().func_wrap( "", - |mut store: StoreContextMut<'_, Option>, arg: WasmStr| -> Result<_> { + |mut store: StoreContextMut<'_, Option>, (arg,): (WasmStr,)| -> Result<_> { let s = arg.to_str(&store)?.to_string(); assert!(store.data().is_none()); *store.data_mut() = Some(s); @@ -239,12 +239,14 @@ fn attempt_to_leave_during_malloc() -> Result<()> { let engine = super::engine(); let mut linker = Linker::new(&engine); + linker.root().func_wrap("thunk", |_, _: ()| -> Result<()> { + panic!("should not get here") + })?; linker .root() - .func_wrap("thunk", || -> Result<()> { panic!("should not get here") })?; - linker - .root() - .func_wrap("ret-string", || -> Result<_> { Ok(("hello".to_string(),)) })?; + .func_wrap("ret-string", |_, _: ()| -> Result<_> { + Ok(("hello".to_string(),)) + })?; let component = Component::new(&engine, component)?; let mut store = Store::new(&engine, ()); @@ -338,7 +340,7 @@ fn attempt_to_reenter_during_host() -> Result<()> { let mut linker = Linker::new(&engine); linker.root().func_wrap( "thunk", - |mut store: StoreContextMut<'_, StaticState>| -> Result<()> { + |mut store: StoreContextMut<'_, StaticState>, _: ()| -> Result<()> { let func = store.data_mut().func.take().unwrap(); let trap = func.call(&mut store, ()).unwrap_err(); assert!( @@ -530,14 +532,16 @@ fn stack_and_heap_args_and_rets() -> Result<()> { // First, test the static API let mut linker = Linker::new(&engine); - linker.root().func_wrap("f1", |x: u32| -> Result<(u32,)> { - assert_eq!(x, 1); - Ok((2,)) - })?; + linker + .root() + .func_wrap("f1", |_, (x,): (u32,)| -> Result<(u32,)> { + assert_eq!(x, 1); + Ok((2,)) + })?; linker.root().func_wrap( "f2", |cx: StoreContextMut<'_, ()>, - arg: ( + (arg,): (( WasmStr, WasmStr, WasmStr, @@ -547,7 +551,7 @@ fn stack_and_heap_args_and_rets() -> Result<()> { WasmStr, WasmStr, WasmStr, - )| + ),)| -> Result<(u32,)> { assert_eq!(arg.0.to_str(&cx).unwrap(), "abc"); Ok((3,)) @@ -555,14 +559,14 @@ fn stack_and_heap_args_and_rets() -> Result<()> { )?; linker .root() - .func_wrap("f3", |arg: u32| -> Result<(String,)> { + .func_wrap("f3", |_, (arg,): (u32,)| -> Result<(String,)> { assert_eq!(arg, 8); Ok(("xyz".to_string(),)) })?; linker.root().func_wrap( "f4", |cx: StoreContextMut<'_, ()>, - arg: ( + (arg,): (( WasmStr, WasmStr, WasmStr, @@ -572,7 +576,7 @@ fn stack_and_heap_args_and_rets() -> Result<()> { WasmStr, WasmStr, WasmStr, - )| + ),)| -> Result<(String,)> { assert_eq!(arg.0.to_str(&cx).unwrap(), "abc"); Ok(("xyz".to_string(),)) @@ -703,12 +707,13 @@ fn bad_import_alignment() -> Result<()> { let mut linker = Linker::new(&engine); linker .root() - .func_wrap("unaligned-retptr", || -> Result<(String,)> { + .func_wrap("unaligned-retptr", |_, _: ()| -> Result<(String,)> { Ok((String::new(),)) })?; linker.root().func_wrap( "unaligned-argptr", - |_: ( + |_, + _: (( WasmStr, WasmStr, WasmStr, @@ -718,7 +723,7 @@ fn bad_import_alignment() -> Result<()> { WasmStr, WasmStr, WasmStr, - )| + ),)| -> Result<()> { unreachable!() }, )?; let component = Component::new(&engine, component)?; @@ -775,12 +780,13 @@ fn no_actual_wasm_code() -> Result<()> { // First, test the static API let mut linker = Linker::new(&engine); - linker - .root() - .func_wrap("f", |mut store: StoreContextMut<'_, u32>| -> Result<()> { + linker.root().func_wrap( + "f", + |mut store: StoreContextMut<'_, u32>, _: ()| -> Result<()> { *store.data_mut() += 1; Ok(()) - })?; + }, + )?; let instance = linker.instantiate(&mut store, &component)?; let thunk = instance.get_typed_func::<(), (), _>(&mut store, "thunk")?; diff --git a/tests/all/component_model/nested.rs b/tests/all/component_model/nested.rs index 101dd936a8..1ca932eca0 100644 --- a/tests/all/component_model/nested.rs +++ b/tests/all/component_model/nested.rs @@ -95,7 +95,7 @@ fn nested_many_instantiations() -> Result<()> { let mut linker = Linker::new(&engine); linker .root() - .func_wrap("count", |mut store: StoreContextMut<'_, u32>| { + .func_wrap("count", |mut store: StoreContextMut<'_, u32>, _: ()| { *store.data_mut() += 1; Ok(()) })?; @@ -162,7 +162,7 @@ fn thread_options_through_inner() -> Result<()> { let mut linker = Linker::new(&engine); linker .root() - .func_wrap("hostfn", |param: u32| Ok((param.to_string(),)))?; + .func_wrap("hostfn", |_, (param,): (u32,)| Ok((param.to_string(),)))?; let instance = linker.instantiate(&mut store, &component)?; let result = instance .get_typed_func::<(u32,), (WasmStr,), _>(&mut store, "run")? diff --git a/tests/all/component_model/post_return.rs b/tests/all/component_model/post_return.rs index b301567c6f..9079cc78d2 100644 --- a/tests/all/component_model/post_return.rs +++ b/tests/all/component_model/post_return.rs @@ -120,13 +120,14 @@ fn invoke_post_return() -> Result<()> { let component = Component::new(&engine, component)?; let mut store = Store::new(&engine, false); let mut linker = Linker::new(&engine); - linker - .root() - .func_wrap("f", |mut store: StoreContextMut<'_, bool>| -> Result<()> { + linker.root().func_wrap( + "f", + |mut store: StoreContextMut<'_, bool>, _: ()| -> Result<()> { assert!(!*store.data()); *store.data_mut() = true; Ok(()) - })?; + }, + )?; let instance = linker.instantiate(&mut store, &component)?; let thunk = instance.get_typed_func::<(), (), _>(&mut store, "thunk")?; diff --git a/tests/all/component_model/strings.rs b/tests/all/component_model/strings.rs index 9974e51aef..fca18ebc49 100644 --- a/tests/all/component_model/strings.rs +++ b/tests/all/component_model/strings.rs @@ -171,12 +171,13 @@ fn test_roundtrip(engine: &Engine, src: &str, dst: &str) -> Result<()> { let component = Component::new(engine, &component)?; let mut store = Store::new(engine, String::new()); let mut linker = Linker::new(engine); - linker - .root() - .func_wrap("host", |store: StoreContextMut, arg: String| { + linker.root().func_wrap( + "host", + |store: StoreContextMut, (arg,): (String,)| { assert_eq!(*store.data(), arg); Ok((arg,)) - })?; + }, + )?; let instance = linker.instantiate(&mut store, &component)?; let func = instance.get_typed_func::<(String,), (String,), _>(&mut store, "echo")?;