wasm_link/
plugin_instance.rs1use thiserror::Error ;
2use wasmtime::component::{ Instance, Val };
3use wasmtime::Store ;
4
5use crate::{ Function, PluginContext, ReturnKind };
6use crate::resource_wrapper::{ ResourceCreationError, ResourceReceiveError };
7
8
9
10pub struct PluginInstance<Ctx: 'static> {
16 pub(crate) store: Store<Ctx>,
17 pub(crate) instance: Instance,
18 #[allow( clippy::type_complexity )]
19 pub(crate) fuel_limiter: Option<Box<dyn FnMut( &mut Store<Ctx>, &str, &str, &Function ) -> u64 + Send>>,
20 #[allow( clippy::type_complexity )]
21 pub(crate) epoch_limiter: Option<Box<dyn FnMut( &mut Store<Ctx>, &str, &str, &Function ) -> u64 + Send>>,
22}
23
24impl<Ctx: std::fmt::Debug + 'static> std::fmt::Debug for PluginInstance<Ctx> {
25 fn fmt( &self, f: &mut std::fmt::Formatter<'_> ) -> std::result::Result<(), std::fmt::Error> {
26 f.debug_struct( "PluginInstance" )
27 .field( "data", &self.store.data() )
28 .field( "store", &self.store )
29 .field( "fuel_limiter", &self.fuel_limiter.as_ref().map(| _ | "<closure>" ))
30 .field( "epoch_limiter", &self.epoch_limiter.as_ref().map(| _ | "<closure>" ))
31 .finish_non_exhaustive()
32 }
33}
34
35#[derive( Error, Debug )]
41pub enum DispatchError {
42 #[error( "Lock Rejected" )] LockRejected,
44 #[error( "Invalid Interface Path: {0}" )] InvalidInterfacePath( String ),
46 #[error( "Invalid Function: {0}" )] InvalidFunction( String ),
48 #[error( "Missing Response" )] MissingResponse,
50 #[error( "Runtime Exception" )] RuntimeException( wasmtime::Error ),
52 #[error( "Invalid Argument List" )] InvalidArgumentList,
54 #[error( "Unsupported type: {0}" )] UnsupportedType( String ),
56 #[error( "Resource Create Error: {0}" )] ResourceCreationError( #[from] ResourceCreationError ),
58 #[error( "Resource Receive Error: {0}" )] ResourceReceiveError( #[from] ResourceReceiveError ),
60}
61
62impl From<DispatchError> for Val {
63 fn from( error: DispatchError ) -> Val { match error {
64 DispatchError::LockRejected => Val::Variant( "lock-rejected".to_string(), None ),
65 DispatchError::InvalidInterfacePath( package ) => Val::Variant( "invalid-interface-path".to_string(), Some( Box::new( Val::String( package )))),
66 DispatchError::InvalidFunction( function ) => Val::Variant( "invalid-function".to_string(), Some( Box::new( Val::String( function )))),
67 DispatchError::MissingResponse => Val::Variant( "missing-response".to_string(), None ),
68 DispatchError::RuntimeException( exception ) => Val::Variant( "runtime-exception".to_string(), Some( Box::new( Val::String( exception.to_string() )))),
69 DispatchError::InvalidArgumentList => Val::Variant( "invalid-argument-list".to_string(), None ),
70 DispatchError::UnsupportedType( name ) => Val::Variant( "unsupported-type".to_string(), Some( Box::new( Val::String( name )))),
71 DispatchError::ResourceCreationError( err ) => err.into(),
72 DispatchError::ResourceReceiveError( err ) => err.into(),
73 }}
74}
75
76impl<Ctx: PluginContext + 'static> PluginInstance<Ctx> {
77
78 const PLACEHOLDER_VAL: Val = Val::Option( None );
79 const VOID_RETURN_VAL: Val = Val::Option( None );
80
81 pub(crate) fn dispatch(
82 &mut self,
83 interface_path: &str,
84 function_name: &str,
85 function: &Function,
86 data: &[Val],
87 ) -> Result<Val, DispatchError> {
88
89 let mut buffer = match function.return_kind() != ReturnKind::Void {
90 true => vec![ Self::PLACEHOLDER_VAL ],
91 false => Vec::with_capacity( 0 ),
92 };
93
94 let fuel_was_set = if let Some( mut limiter ) = self.fuel_limiter.take() {
95 let fuel = limiter( &mut self.store, interface_path, function_name, function );
96 self.fuel_limiter = Some( limiter );
97 self.store.set_fuel( fuel ).map_err( DispatchError::RuntimeException )?;
98 true
99 } else { false };
100
101 if let Some( mut limiter ) = self.epoch_limiter.take() {
102 let ticks = limiter( &mut self.store, interface_path, function_name, function );
103 self.epoch_limiter = Some( limiter );
104 self.store.set_epoch_deadline( ticks );
105 }
106
107 let interface_index = self.instance
108 .get_export_index( &mut self.store, None, interface_path )
109 .ok_or( DispatchError::InvalidInterfacePath( interface_path.to_string() ))?;
110 let func_index = self.instance
111 .get_export_index( &mut self.store, Some( &interface_index ), function_name )
112 .ok_or( DispatchError::InvalidFunction( format!( "{}:{}", interface_path, function_name )))?;
113 let func = self.instance
114 .get_func( &mut self.store, func_index )
115 .ok_or( DispatchError::InvalidFunction( format!( "{}:{}", interface_path, function_name )))?;
116 let call_result = func.call( &mut self.store, data, &mut buffer );
117
118 if fuel_was_set { let _ = self.store.set_fuel( 0 ); }
120
121 call_result.map_err( DispatchError::RuntimeException )?;
122 Ok( match function.return_kind() != ReturnKind::Void {
123 true => buffer.pop().ok_or( DispatchError::MissingResponse )?,
124 false => Self::VOID_RETURN_VAL,
125 })
126
127 }
128
129}