11use crate :: memory:: MemoryCreator ;
22use crate :: trampoline:: MemoryCreatorProxy ;
3+ use crate :: { func:: HostFunc , Caller , FuncType , IntoFunc , Trap , Val , WasmRet , WasmTy } ;
34use anyhow:: { bail, Result } ;
45use std:: cmp;
6+ use std:: collections:: HashMap ;
57use std:: convert:: TryFrom ;
68use std:: fmt;
9+ use std:: future:: Future ;
710#[ cfg( feature = "cache" ) ]
811use std:: path:: Path ;
12+ use std:: pin:: Pin ;
913use std:: sync:: Arc ;
1014use wasmparser:: WasmFeatures ;
1115#[ cfg( feature = "cache" ) ]
@@ -266,6 +270,88 @@ impl Default for InstanceAllocationStrategy {
266270 }
267271}
268272
273+ /// This type is used for storing host functions in a `Config`.
274+ ///
275+ /// The module and function names are interned for more compact storage.
276+ #[ derive( Clone ) ]
277+ struct HostFuncMap {
278+ index_map : HashMap < Arc < str > , usize > ,
279+ strings : Vec < Arc < str > > ,
280+ funcs : HashMap < ( usize , usize ) , Arc < HostFunc > > ,
281+ }
282+
283+ impl HostFuncMap {
284+ fn new ( ) -> Self {
285+ Self {
286+ index_map : HashMap :: new ( ) ,
287+ strings : Vec :: new ( ) ,
288+ funcs : HashMap :: new ( ) ,
289+ }
290+ }
291+
292+ fn insert ( & mut self , module : & str , name : & str , func : HostFunc ) {
293+ let key = ( self . intern_str ( module) , self . intern_str ( name) ) ;
294+ self . funcs . insert ( key, Arc :: new ( func) ) ;
295+ }
296+
297+ fn get ( & self , module : & str , name : & str ) -> Option < & HostFunc > {
298+ let key = (
299+ self . index_map . get ( module) . cloned ( ) ?,
300+ self . index_map . get ( name) . cloned ( ) ?,
301+ ) ;
302+ self . funcs . get ( & key) . map ( AsRef :: as_ref)
303+ }
304+
305+ fn intern_str ( & mut self , string : & str ) -> usize {
306+ if let Some ( idx) = self . index_map . get ( string) {
307+ return * idx;
308+ }
309+ let string: Arc < str > = string. into ( ) ;
310+ let idx = self . strings . len ( ) ;
311+ self . strings . push ( string. clone ( ) ) ;
312+ self . index_map . insert ( string, idx) ;
313+ idx
314+ }
315+ }
316+
317+ macro_rules! generate_wrap_async_host_func {
318+ ( $num: tt $( $args: ident) * ) => ( paste:: paste!{
319+ /// Same as [`Config::wrap_host_func`], except the closure asynchronously produces
320+ /// its result. For more information see the [`Func`](crate::Func) documentation.
321+ ///
322+ /// # Panics
323+ ///
324+ /// A panic will occur if the store associated with the instance that calls this host
325+ /// function is not asynchronous (see [`Store::new_async`](crate::Store::new_async)).
326+ #[ allow( non_snake_case) ]
327+ #[ cfg( feature = "async" ) ]
328+ #[ cfg_attr( nightlydoc, doc( cfg( feature = "async" ) ) ) ]
329+ pub fn [ <wrap_host_func $num _async>] <$( $args, ) * R >(
330+ & mut self ,
331+ module: & str ,
332+ name: & str ,
333+ func: impl Fn ( Caller , $( $args) ,* ) -> Box <dyn Future <Output = R >> + Send + Sync + ' static ,
334+ )
335+ where
336+ $( $args: WasmTy , ) *
337+ R : WasmRet ,
338+ {
339+ self . host_funcs. insert(
340+ module,
341+ name,
342+ HostFunc :: wrap( & self . default_instance_allocator, move |caller: Caller , $( $args: $args) ,* | {
343+ let store = caller. store( ) . clone( ) ;
344+ let mut future = Pin :: from( func( caller, $( $args) ,* ) ) ;
345+ match store. block_on( future. as_mut( ) ) {
346+ Ok ( ret) => ret. into_result( ) ,
347+ Err ( e) => Err ( e) ,
348+ }
349+ } )
350+ ) ;
351+ }
352+ } )
353+ }
354+
269355/// Global configuration options used to create an [`Engine`](crate::Engine)
270356/// and customize its behavior.
271357///
@@ -292,6 +378,7 @@ pub struct Config {
292378 pub ( crate ) max_memories : usize ,
293379 #[ cfg( feature = "async" ) ]
294380 pub ( crate ) async_stack_size : usize ,
381+ host_funcs : HostFuncMap ,
295382}
296383
297384impl Config {
@@ -344,6 +431,7 @@ impl Config {
344431 max_memories : 10_000 ,
345432 #[ cfg( feature = "async" ) ]
346433 async_stack_size : 2 << 20 ,
434+ host_funcs : HostFuncMap :: new ( ) ,
347435 } ;
348436 ret. wasm_backtrace_details ( WasmBacktraceDetails :: Environment ) ;
349437 return ret;
@@ -1062,6 +1150,59 @@ impl Config {
10621150 self
10631151 }
10641152
1153+ /// Defines a host function for the [`Config`] for the given callback.
1154+ ///
1155+ /// Use [`Store::get_host_func`](crate::Store::get_host_func) to get a [`Func`](crate::Func) representing the function.
1156+ ///
1157+ /// Note that the implementation of `func` must adhere to the `ty`
1158+ /// signature given, error or traps may occur if it does not respect the
1159+ /// `ty` signature.
1160+ ///
1161+ /// Additionally note that this is quite a dynamic function since signatures
1162+ /// are not statically known. For performance reasons, it's recommended
1163+ /// to use [`Config::wrap_host_func`] if you can because with statically known
1164+ /// signatures the engine can optimize the implementation much more.
1165+ ///
1166+ /// The callback must be `Send` and `Sync` as it is shared between all engines created
1167+ /// from the `Config`. For more relaxed bounds, use [`Func::new`](crate::Func::new) to define the function.
1168+ pub fn define_host_func (
1169+ & mut self ,
1170+ module : & str ,
1171+ name : & str ,
1172+ ty : FuncType ,
1173+ func : impl Fn ( Caller , & [ Val ] , & mut [ Val ] ) -> Result < ( ) , Trap > + Send + Sync + ' static ,
1174+ ) {
1175+ self . host_funcs
1176+ . insert ( module, name, HostFunc :: new ( self , ty, func) ) ;
1177+ }
1178+
1179+ /// Defines a host function for the [`Config`] from the given Rust closure.
1180+ ///
1181+ /// Use [`Store::get_host_func`](crate::Store::get_host_func) to get a [`Func`](crate::Func) representing the function.
1182+ ///
1183+ /// See [`Func::wrap`](crate::Func::wrap) for information about accepted parameter and result types for the closure.
1184+ ///
1185+ /// The closure must be `Send` and `Sync` as it is shared between all engines created
1186+ /// from the `Config`. For more relaxed bounds, use [`Func::wrap`](crate::Func::wrap) to wrap the closure.
1187+ pub fn wrap_host_func < Params , Results > (
1188+ & mut self ,
1189+ module : & str ,
1190+ name : & str ,
1191+ func : impl IntoFunc < Params , Results > + Send + Sync ,
1192+ ) {
1193+ self . host_funcs . insert (
1194+ module,
1195+ name,
1196+ HostFunc :: wrap ( & self . default_instance_allocator , func) ,
1197+ ) ;
1198+ }
1199+
1200+ for_each_function_signature ! ( generate_wrap_async_host_func) ;
1201+
1202+ pub ( crate ) fn get_host_func ( & self , module : & str , name : & str ) -> Option < & HostFunc > {
1203+ self . host_funcs . get ( module, name)
1204+ }
1205+
10651206 pub ( crate ) fn target_isa ( & self ) -> Box < dyn TargetIsa > {
10661207 self . isa_flags
10671208 . clone ( )
0 commit comments