diff --git a/crates/wasmtime/src/runtime/component/func.rs b/crates/wasmtime/src/runtime/component/func.rs index 3cf236d2fa7f..b178fb4b38f1 100644 --- a/crates/wasmtime/src/runtime/component/func.rs +++ b/crates/wasmtime/src/runtime/component/func.rs @@ -20,11 +20,13 @@ use wasmtime_environ::component::{ use crate::component::concurrent::{self, AsAccessor, PreparedCall}; mod host; +mod witness; mod options; mod typed; pub use self::host::*; pub use self::options::*; pub use self::typed::*; +pub use self::witness::*; /// A WebAssembly component function which can be called. /// diff --git a/crates/wasmtime/src/runtime/component/func/host.rs b/crates/wasmtime/src/runtime/component/func/host.rs index c4b8d8bb88ba..178902e2adba 100644 --- a/crates/wasmtime/src/runtime/component/func/host.rs +++ b/crates/wasmtime/src/runtime/component/func/host.rs @@ -4,7 +4,7 @@ use crate::component::concurrent; #[cfg(feature = "component-model-async")] use crate::component::concurrent::{Accessor, Status}; -use crate::component::func::{LiftContext, LowerContext}; +use crate::component::func::{LiftContext, LowerContext, Witness}; use crate::component::matching::InstanceType; use crate::component::storage::{slice_to_storage, slice_to_storage_mut}; use crate::component::types::ComponentFunc; @@ -91,7 +91,7 @@ impl HostFunc { } /// Equivalent for `Linker::func_wrap` - pub(crate) fn func_wrap(func: F) -> Arc + pub(crate) fn func_wrap(func: F, witness: Option>) -> Arc where T: 'static, F: Fn(StoreContextMut, P) -> Result + Send + Sync + 'static, @@ -100,15 +100,16 @@ impl HostFunc { { Self::new( Asyncness::No, - StaticHostFn::<_, false>::new(move |store, params| { - HostResult::Done(func(store, params)) - }), + StaticHostFn::<_, T, false>::new( + move |store, params| HostResult::Done(func(store, params)), + witness, + ), ) } /// Equivalent for `Linker::func_wrap_async` #[cfg(feature = "async")] - pub(crate) fn func_wrap_async(func: F) -> Arc + pub(crate) fn func_wrap_async(func: F, witness: Option>) -> Arc where T: 'static, F: Fn(StoreContextMut<'_, T>, P) -> Box> + Send + '_> @@ -120,19 +121,22 @@ impl HostFunc { { Self::new( Asyncness::Yes, - StaticHostFn::<_, false>::new(move |store, params| { - HostResult::Done( - store - .block_on(|store| Pin::from(func(store, params))) - .and_then(|r| r), - ) - }), + StaticHostFn::<_, T, false>::new( + move |store, params| { + HostResult::Done( + store + .block_on(|store| Pin::from(func(store, params))) + .and_then(|r| r), + ) + }, + witness, + ), ) } /// Equivalent for `Linker::func_wrap_concurrent` #[cfg(feature = "component-model-async")] - pub(crate) fn func_wrap_concurrent(func: F) -> Arc + pub(crate) fn func_wrap_concurrent(func: F, witness: Option>) -> Arc where T: 'static, F: Fn(&Accessor, P) -> Pin> + Send + '_>> @@ -145,17 +149,20 @@ impl HostFunc { let func = Arc::new(func); Self::new( Asyncness::Yes, - StaticHostFn::<_, true>::new(move |store, params| { - let func = func.clone(); - HostResult::Future(Box::pin( - store.wrap_call(move |accessor| func(accessor, params)), - )) - }), + StaticHostFn::<_, T, true>::new( + move |store, params| { + let func = func.clone(); + HostResult::Future(Box::pin( + store.wrap_call(move |accessor| func(accessor, params)), + )) + }, + witness, + ), ) } /// Equivalent of `Linker::func_new` - pub(crate) fn func_new(func: F) -> Arc + pub(crate) fn func_new(func: F, witness: Option>) -> Arc where T: 'static, F: Fn(StoreContextMut<'_, T>, ComponentFunc, &[Val], &mut [Val]) -> Result<()> @@ -165,19 +172,20 @@ impl HostFunc { { Self::new( Asyncness::No, - DynamicHostFn::<_, false>::new( + DynamicHostFn::<_, T, false>::new( move |store, ty, mut params_and_results, result_start| { let (params, results) = params_and_results.split_at_mut(result_start); let result = func(store, ty, params, results).map(move |()| params_and_results); HostResult::Done(result) }, + witness, ), ) } /// Equivalent of `Linker::func_new_async` #[cfg(feature = "async")] - pub(crate) fn func_new_async(func: F) -> Arc + pub(crate) fn func_new_async(func: F, witness: Option>) -> Arc where T: 'static, F: for<'a> Fn( @@ -192,7 +200,7 @@ impl HostFunc { { Self::new( Asyncness::Yes, - DynamicHostFn::<_, false>::new( + DynamicHostFn::<_, T, false>::new( move |store, ty, mut params_and_results, result_start| { let (params, results) = params_and_results.split_at_mut(result_start); let result = store @@ -203,13 +211,14 @@ impl HostFunc { let result = result.map(move |()| params_and_results); HostResult::Done(result) }, + witness, ), ) } /// Equivalent of `Linker::func_new_concurrent` #[cfg(feature = "component-model-async")] - pub(crate) fn func_new_concurrent(func: F) -> Arc + pub(crate) fn func_new_concurrent(func: F, witness: Option>) -> Arc where T: 'static, F: for<'a> Fn( @@ -225,7 +234,7 @@ impl HostFunc { let func = Arc::new(func); Self::new( Asyncness::Yes, - DynamicHostFn::<_, true>::new( + DynamicHostFn::<_, T, true>::new( move |store, ty, mut params_and_results, result_start| { let func = func.clone(); HostResult::Future(Box::pin(store.wrap_call(move |accessor| { @@ -236,6 +245,7 @@ impl HostFunc { }) }))) }, + witness, ), ) } @@ -587,22 +597,21 @@ where /// Implementation of a "static" host function where the parameters and results /// of a function are known at compile time. -#[repr(transparent)] -struct StaticHostFn(F); +struct StaticHostFn(F, Option>); -impl StaticHostFn { - fn new(func: F) -> Self +impl StaticHostFn { + fn new(func: F, witness: Option>) -> Self where T: 'static, P: ComponentNamedList + Lift + 'static, R: ComponentNamedList + Lower + 'static, F: Fn(StoreContextMut<'_, T>, P) -> HostResult, { - Self(func) + Self(func, witness) } } -impl HostFn for StaticHostFn +impl HostFn for StaticHostFn where T: 'static, F: Fn(StoreContextMut<'_, T>, P) -> HostResult, @@ -671,20 +680,20 @@ where /// /// This is intended for more-dynamic use cases than `StaticHostFn` above such /// as demos, gluing things together quickly, and `wast` testing. -struct DynamicHostFn(F); +struct DynamicHostFn(F, Option>); -impl DynamicHostFn { - fn new(func: F) -> Self +impl DynamicHostFn { + fn new(func: F, witness: Option>) -> Self where T: 'static, F: Fn(StoreContextMut<'_, T>, ComponentFunc, Vec, usize) -> HostResult>, { - Self(func) + Self(func, witness) } } impl HostFn), Vec> - for DynamicHostFn + for DynamicHostFn where T: 'static, F: Fn(StoreContextMut<'_, T>, ComponentFunc, Vec, usize) -> HostResult>, diff --git a/crates/wasmtime/src/runtime/component/func/witness.rs b/crates/wasmtime/src/runtime/component/func/witness.rs new file mode 100644 index 000000000000..17176ad6c287 --- /dev/null +++ b/crates/wasmtime/src/runtime/component/func/witness.rs @@ -0,0 +1,57 @@ + +use crate::prelude::*; +use core::marker::PhantomData; +use alloc::sync::Arc; + +pub struct Witness { + param_witness: Arc) -> Result<()> + Send + Sync>, + result_witness: Arc) -> Result<()> + Send + Sync>, + position: Arc>, +} +impl Witness { + pub fn new( + param: impl Fn(&mut T, &[String], ParamWitness<'_>) -> Result<()> + Send + Sync + 'static, + result: impl Fn(&mut T, &[String], ResultWitness<'_>) -> Result<()> + Send + Sync + 'static, + ) -> Self { + Self { + param_witness: Arc::new(param), + result_witness: Arc::new(result), + position: Arc::new(Vec::new()), + } + } + pub fn in_instance(&self, name: &str) -> Self { + let mut position: Vec = (&*self.position).clone(); + position.push(name.to_owned()); + Self { + param_witness: self.param_witness.clone(), + result_witness: self.result_witness.clone(), + position: Arc::new(position), + } + } +} + +impl Clone for Witness { + fn clone(&self) -> Self { + Self { + param_witness: self.param_witness.clone(), + result_witness: self.result_witness.clone(), + position: self.position.clone(), + } + } +} + +pub struct ParamWitness<'a> { + _marker: PhantomData<&'a ()>, +} + +impl<'a> ParamWitness<'a> { + +} + +pub struct ResultWitness<'a> { + _marker: PhantomData<&'a ()>, +} + +impl<'a> ResultWitness<'a> { + +} diff --git a/crates/wasmtime/src/runtime/component/linker.rs b/crates/wasmtime/src/runtime/component/linker.rs index faf1e21f9a39..18b8fa6f1ca2 100644 --- a/crates/wasmtime/src/runtime/component/linker.rs +++ b/crates/wasmtime/src/runtime/component/linker.rs @@ -1,6 +1,6 @@ #[cfg(feature = "component-model-async")] use crate::component::concurrent::Accessor; -use crate::component::func::HostFunc; +use crate::component::func::{HostFunc, Witness}; use crate::component::instance::RuntimeImport; use crate::component::matching::{InstanceType, TypeChecker}; use crate::component::types; @@ -64,6 +64,7 @@ pub struct Linker { map: NameMap, path: Vec, allow_shadowing: bool, + witness: Option>, _marker: marker::PhantomData T>, } @@ -75,6 +76,7 @@ impl Clone for Linker { map: self.map.clone(), path: self.path.clone(), allow_shadowing: self.allow_shadowing, + witness: self.witness.as_ref().map(|w| w.clone()), _marker: self._marker, } } @@ -98,6 +100,7 @@ pub struct LinkerInstance<'a, T: 'static> { strings: &'a mut Strings, map: &'a mut NameMap, allow_shadowing: bool, + witness: Option>, _marker: marker::PhantomData T>, } @@ -109,7 +112,7 @@ pub(crate) enum Definition { Resource(ResourceType, Arc), } -impl Linker { +impl Linker { /// Creates a new linker for the [`Engine`] specified with no items defined /// within it. pub fn new(engine: &Engine) -> Linker { @@ -119,6 +122,7 @@ impl Linker { map: NameMap::default(), allow_shadowing: false, path: Vec::new(), + witness: None, _marker: marker::PhantomData, } } @@ -147,10 +151,17 @@ impl Linker { strings: &mut self.strings, map: &mut self.map, allow_shadowing: self.allow_shadowing, + witness: self.witness.clone(), _marker: self._marker, } } + /// Set a `Witness` that will be applied to all functions defined in this + /// linker. + pub fn set_instance(&mut self, witness: Option>) { + self.witness = witness; + } + /// Returns a builder for the named instance specified. /// /// # Errors @@ -314,7 +325,7 @@ impl Linker { use wasmtime_environ::component::ComponentTypes; use wasmtime_environ::component::TypeDef; // Recursively stub out all imports of the component with a function that traps. - fn stub_item( + fn stub_item( linker: &mut LinkerInstance, item_name: &str, item_def: &TypeDef, @@ -374,7 +385,7 @@ impl Linker { } } -impl LinkerInstance<'_, T> { +impl LinkerInstance<'_, T> { fn as_mut(&mut self) -> LinkerInstance<'_, T> { LinkerInstance { engine: self.engine, @@ -383,6 +394,7 @@ impl LinkerInstance<'_, T> { strings: self.strings, map: self.map, allow_shadowing: self.allow_shadowing, + witness: self.witness.as_ref().map(|w| w.clone()), _marker: self._marker, } } @@ -422,7 +434,13 @@ impl LinkerInstance<'_, T> { Params: ComponentNamedList + Lift + 'static, Return: ComponentNamedList + Lower + 'static, { - self.insert(name, Definition::Func(HostFunc::func_wrap(func)))?; + self.insert( + name, + Definition::Func(HostFunc::func_wrap( + func, + self.witness.as_ref().map(|w| w.in_instance(name)), + )), + )?; Ok(()) } @@ -469,7 +487,13 @@ impl LinkerInstance<'_, T> { Params: ComponentNamedList + Lift + 'static, Return: ComponentNamedList + Lower + 'static, { - self.insert(name, Definition::Func(HostFunc::func_wrap_async(f)))?; + self.insert( + name, + Definition::Func(HostFunc::func_wrap_async( + f, + self.witness.as_ref().map(|w| w.in_instance(name)), + )), + )?; Ok(()) } @@ -534,7 +558,13 @@ impl LinkerInstance<'_, T> { if !self.engine.tunables().concurrency_support { bail!("concurrent host functions require `Config::concurrency_support`"); } - self.insert(name, Definition::Func(HostFunc::func_wrap_concurrent(f)))?; + self.insert( + name, + Definition::Func(HostFunc::func_wrap_concurrent( + f, + self.witness.as_ref().map(|w| w.in_instance(name)), + )), + )?; Ok(()) } @@ -645,7 +675,13 @@ impl LinkerInstance<'_, T> { + Sync + 'static, ) -> Result<()> { - self.insert(name, Definition::Func(HostFunc::func_new(func)))?; + self.insert( + name, + Definition::Func(HostFunc::func_new( + func, + self.witness.as_ref().map(|w| w.in_instance(name)), + )), + )?; Ok(()) } @@ -668,7 +704,13 @@ impl LinkerInstance<'_, T> { + Sync + 'static, { - self.insert(name, Definition::Func(HostFunc::func_new_async(func)))?; + self.insert( + name, + Definition::Func(HostFunc::func_new_async( + func, + self.witness.as_ref().map(|w| w.in_instance(name)), + )), + )?; Ok(()) } @@ -696,7 +738,13 @@ impl LinkerInstance<'_, T> { if !self.engine.tunables().concurrency_support { bail!("concurrent host functions require `Config::concurrency_support`"); } - self.insert(name, Definition::Func(HostFunc::func_new_concurrent(f)))?; + self.insert( + name, + Definition::Func(HostFunc::func_new_concurrent( + f, + self.witness.as_ref().map(|w| w.in_instance(name)), + )), + )?; Ok(()) } @@ -814,6 +862,7 @@ impl LinkerInstance<'_, T> { /// Same as [`LinkerInstance::instance`] except with different lifetime /// parameters. pub fn into_instance(mut self, name: &str) -> Result { + let n = name; let name = self.insert(name, Definition::Instance(NameMap::default()))?; self.map = match self.map.raw_get_mut(&name) { Some(Definition::Instance(map)) => map, @@ -822,6 +871,7 @@ impl LinkerInstance<'_, T> { self.path.truncate(self.path_len); self.path.push(name); self.path_len += 1; + self.witness.as_mut().map(|w| w.in_instance(n)); Ok(self) }