Skip to main content

wasm_link/
plugin_instance.rs

1use thiserror::Error ;
2use wasmtime::component::{ Component, Instance, Val };
3use wasmtime::Store ;
4
5use crate::plugin::PluginData ;
6use crate::interface::InterfaceData ;
7use crate::loading::{ ResourceCreationError, ResourceReceiveError };
8
9
10
11pub struct PluginInstance<P: PluginData + 'static> {
12    pub(crate) id: P::Id,
13    pub(crate) _component: Component,
14    pub(crate) store: Store<P>,
15    pub(crate) instance: Instance,
16}
17
18impl<P: PluginData + std::fmt::Debug> std::fmt::Debug for PluginInstance<P> {
19    fn fmt( &self, f: &mut std::fmt::Formatter<'_> ) -> std::result::Result<(), std::fmt::Error> {
20        f.debug_struct( "Plugin Instance" )
21            .field( "id", &self.id )
22            .field( "data", &self.store.data() )
23            .field( "store", &self.store )
24            .finish_non_exhaustive()
25    }
26}
27
28/// Errors that can occur when dispatching a function call to plugins.
29///
30/// Returned inside the [`Socket`] from [`PluginTreeHead::dispatch`] when a
31/// function call fails at runtime.
32///
33/// [`Socket`]: crate::Socket
34/// [`PluginTreeHead::dispatch`]: crate::PluginTreeHead::dispatch
35#[derive( Error, Debug )]
36pub enum DispatchError<I: InterfaceData> {
37    /// Failed to acquire lock on plugin instance (another call is in progress).
38    #[error( "Deadlock" )] Deadlock,
39    /// Failed to parse interface metadata during dispatch.
40    #[error( "Interface Error: {0}")] WitParserError( I::Error ),
41    /// The specified interface path doesn't match any known interface.
42    #[error( "Invalid Interface: {0}" )] InvalidInterface( String ),
43    /// The specified function doesn't exist on the interface.
44    #[error( "Invalid Function: {0}" )] InvalidFunction( String ),
45    /// Function was expected to return a value but didn't.
46    #[error( "Missing Response" )] MissingResponse,
47    /// The WASM function threw an exception during execution.
48    #[error( "Runtime Exception" )] RuntimeException( wasmtime::Error ),
49    /// The provided arguments don't match the function signature.
50    #[error( "Invalid Argument List" )] InvalidArgumentList,
51    /// Async types (`Future`, `Stream`, `ErrorContext`) are not yet supported for cross-plugin transfer.
52    #[error( "Unsupported type: {0}" )] UnsupportedType( String ),
53    /// Failed to create a resource handle for cross-plugin transfer.
54    #[error( "Resource Create Error: {0}" )] ResourceCreationError( #[from] ResourceCreationError ),
55    /// Failed to receive a resource handle from another plugin.
56    #[error( "Resource Receive Error: {0}" )] ResourceReceiveError( #[from] ResourceReceiveError ),
57}
58
59impl<I: InterfaceData> From<DispatchError<I>> for Val {
60    fn from( error: DispatchError<I> ) -> Val { match error {
61        DispatchError::Deadlock => Val::Variant( "deadlock".to_string(), None ),
62        DispatchError::WitParserError( err ) => Val::Variant( "wit-parser-error".to_string(), Some( Box::new( Val::String( err.to_string() )))),
63        DispatchError::InvalidInterface( package ) => Val::Variant( "invalid-interface".to_string(), Some( Box::new( Val::String( package )))),
64        DispatchError::InvalidFunction( function ) => Val::Variant( "invalid-function".to_string(), Some( Box::new( Val::String( function )))),
65        DispatchError::MissingResponse => Val::Variant( "missing-response".to_string(), None ),
66        DispatchError::RuntimeException( exception ) => Val::Variant( "runtime-exception".to_string(), Some( Box::new( Val::String( exception.to_string() )))),
67        DispatchError::InvalidArgumentList => Val::Variant( "invalid-argument-list".to_string(), None ),
68		DispatchError::UnsupportedType( name ) => Val::Variant( "unsupported-type".to_string(), Some( Box::new( Val::String( name )))),
69        DispatchError::ResourceCreationError( err ) => err.into(),
70        DispatchError::ResourceReceiveError( err ) => err.into(),
71    }}
72}
73
74impl<P: PluginData> PluginInstance<P> {
75
76    pub fn id( &self ) -> &P::Id { &self.id }
77
78    const PLACEHOLDER_VAL: Val = Val::Tuple( vec![] );
79
80    pub(crate) fn dispatch<I: InterfaceData>(
81        &mut self,
82        interface_path: &str,
83        function: &str,
84        returns: bool,
85        data: &[Val],
86    ) -> Result<Val, DispatchError<I>> {
87
88        let mut buffer = match returns {
89            true => vec![ Self::PLACEHOLDER_VAL ],
90            false => Vec::with_capacity( 0 ),
91        };
92
93        let interface_index = self.instance
94            .get_export_index( &mut self.store, None, interface_path )
95            .ok_or( DispatchError::InvalidInterface( interface_path.to_string() ))?;
96        let func_index = self.instance
97            .get_export_index( &mut self.store, Some( &interface_index ), function )
98            .ok_or( DispatchError::InvalidFunction( format!( "{}:{}", interface_path, function )))?;
99        let func = self.instance
100            .get_func( &mut self.store, func_index )
101            .ok_or( DispatchError::InvalidFunction( format!( "{}:{}", interface_path, function )))?;
102        func
103            .call( &mut self.store, data, &mut buffer )
104            .map_err( DispatchError::RuntimeException )?;
105        let _ = func.post_return( &mut self.store );
106
107        Ok( match returns {
108            true => buffer.pop().ok_or( DispatchError::MissingResponse )?,
109            false => Self::PLACEHOLDER_VAL,
110        })
111
112    }
113}