wasmer_vm/
resolver.rs

1use std::sync::Arc;
2
3use crate::{ImportInitializerFuncPtr, VMExtern, VMFunction, VMGlobal, VMMemory, VMTable};
4
5/// The value of an export passed from one instance to another.
6#[derive(Debug, Clone)]
7pub enum Export {
8    /// A function export value.
9    Function(ExportFunction),
10
11    /// A table export value.
12    Table(VMTable),
13
14    /// A memory export value.
15    Memory(VMMemory),
16
17    /// A global export value.
18    Global(VMGlobal),
19}
20
21impl From<Export> for VMExtern {
22    fn from(other: Export) -> Self {
23        match other {
24            Export::Function(ExportFunction { vm_function, .. }) => Self::Function(vm_function),
25            Export::Memory(vm_memory) => Self::Memory(vm_memory),
26            Export::Table(vm_table) => Self::Table(vm_table),
27            Export::Global(vm_global) => Self::Global(vm_global),
28        }
29    }
30}
31
32impl From<VMExtern> for Export {
33    fn from(other: VMExtern) -> Self {
34        match other {
35            VMExtern::Function(vm_function) => Self::Function(ExportFunction {
36                vm_function,
37                metadata: None,
38            }),
39            VMExtern::Memory(vm_memory) => Self::Memory(vm_memory),
40            VMExtern::Table(vm_table) => Self::Table(vm_table),
41            VMExtern::Global(vm_global) => Self::Global(vm_global),
42        }
43    }
44}
45
46/// Extra metadata about `ExportFunction`s.
47///
48/// The metadata acts as a kind of manual virtual dispatch. We store the
49/// user-supplied `WasmerEnv` as a void pointer and have methods on it
50/// that have been adapted to accept a void pointer.
51///
52/// This struct owns the original `host_env`, thus when it gets dropped
53/// it calls the `drop` function on it.
54#[derive(Debug, PartialEq)]
55pub struct ExportFunctionMetadata {
56    /// This field is stored here to be accessible by `Drop`.
57    ///
58    /// At the time it was added, it's not accessed anywhere outside of
59    /// the `Drop` implementation. This field is the "master copy" of the env,
60    /// that is, the original env passed in by the user. Every time we create
61    /// an `Instance` we clone this with the `host_env_clone_fn` field.
62    ///
63    /// Thus, we only bother to store the master copy at all here so that
64    /// we can free it.
65    ///
66    /// See `wasmer_vm::export::VMFunction::vmctx` for the version of
67    /// this pointer that is used by the VM when creating an `Instance`.
68    pub host_env: *mut std::ffi::c_void,
69
70    /// Function pointer to `WasmerEnv::init_with_instance(&mut self, instance: &Instance)`.
71    ///
72    /// This function is called to finish setting up the environment after
73    /// we create the `api::Instance`.
74    // This one is optional for now because dynamic host envs need the rest
75    // of this without the init fn
76    pub import_init_function_ptr: Option<ImportInitializerFuncPtr>,
77
78    /// A function analogous to `Clone::clone` that returns a leaked `Box`.
79    pub host_env_clone_fn: fn(*mut std::ffi::c_void) -> *mut std::ffi::c_void,
80
81    /// The destructor to free the host environment.
82    ///
83    /// # Safety
84    /// - This function should only be called in when properly synchronized.
85    /// For example, in the `Drop` implementation of this type.
86    pub host_env_drop_fn: unsafe fn(*mut std::ffi::c_void),
87}
88
89/// This can be `Send` because `host_env` comes from `WasmerEnv` which is
90/// `Send`. Therefore all operations should work on any thread.
91unsafe impl Send for ExportFunctionMetadata {}
92/// This data may be shared across threads, `drop` is an unsafe function
93/// pointer, so care must be taken when calling it.
94unsafe impl Sync for ExportFunctionMetadata {}
95
96impl ExportFunctionMetadata {
97    /// Create an `ExportFunctionMetadata` type with information about
98    /// the exported function.
99    ///
100    /// # Safety
101    /// - the `host_env` must be `Send`.
102    /// - all function pointers must work on any thread.
103    pub unsafe fn new(
104        host_env: *mut std::ffi::c_void,
105        import_init_function_ptr: Option<ImportInitializerFuncPtr>,
106        host_env_clone_fn: fn(*mut std::ffi::c_void) -> *mut std::ffi::c_void,
107        host_env_drop_fn: fn(*mut std::ffi::c_void),
108    ) -> Self {
109        Self {
110            host_env,
111            import_init_function_ptr,
112            host_env_clone_fn,
113            host_env_drop_fn,
114        }
115    }
116}
117
118// We have to free `host_env` here because we always clone it before using it
119// so all the `host_env`s freed at the `Instance` level won't touch the original.
120impl Drop for ExportFunctionMetadata {
121    fn drop(&mut self) {
122        if !self.host_env.is_null() {
123            // # Safety
124            // - This is correct because we know no other references
125            //   to this data can exist if we're dropping it.
126            unsafe {
127                (self.host_env_drop_fn)(self.host_env);
128            }
129        }
130    }
131}
132
133/// A function export value with an extra function pointer to initialize
134/// host environments.
135#[derive(Debug, Clone, PartialEq)]
136pub struct ExportFunction {
137    /// The VM function, containing most of the data.
138    pub vm_function: VMFunction,
139    /// Contains functions necessary to create and initialize host envs
140    /// with each `Instance` as well as being responsible for the
141    /// underlying memory of the host env.
142    pub metadata: Option<Arc<ExportFunctionMetadata>>,
143}
144
145impl From<ExportFunction> for Export {
146    fn from(func: ExportFunction) -> Self {
147        Self::Function(func)
148    }
149}
150
151impl From<VMTable> for Export {
152    fn from(table: VMTable) -> Self {
153        Self::Table(table)
154    }
155}
156
157impl From<VMMemory> for Export {
158    fn from(memory: VMMemory) -> Self {
159        Self::Memory(memory)
160    }
161}
162
163impl From<VMGlobal> for Export {
164    fn from(global: VMGlobal) -> Self {
165        Self::Global(global)
166    }
167}
168
169///
170/// Import resolver connects imports with available exported values.
171pub trait Resolver {
172    /// Resolves an import a WebAssembly module to an export it's hooked up to.
173    ///
174    /// The `index` provided is the index of the import in the wasm module
175    /// that's being resolved. For example 1 means that it's the second import
176    /// listed in the wasm module.
177    ///
178    /// The `module` and `field` arguments provided are the module/field names
179    /// listed on the import itself.
180    ///
181    /// # Notes:
182    ///
183    /// The index is useful because some WebAssembly modules may rely on that
184    /// for resolving ambiguity in their imports. Such as:
185    /// ```ignore
186    /// (module
187    ///   (import "" "" (func))
188    ///   (import "" "" (func (param i32) (result i32)))
189    /// )
190    /// ```
191    fn resolve(&self, _index: u32, module: &str, field: &str) -> Option<Export>;
192}
193
194/// Import resolver connects imports with available exported values.
195///
196/// This is a specific subtrait for [`Resolver`] for those users who don't
197/// care about the `index`, but only about the `module` and `field` for
198/// the resolution.
199pub trait NamedResolver {
200    /// Resolves an import a WebAssembly module to an export it's hooked up to.
201    ///
202    /// It receives the `module` and `field` names and return the [`Export`] in
203    /// case it's found.
204    fn resolve_by_name(&self, module: &str, field: &str) -> Option<Export>;
205}
206
207// All NamedResolvers should extend `Resolver`.
208impl<T: NamedResolver> Resolver for T {
209    /// By default this method will be calling [`NamedResolver::resolve_by_name`],
210    /// dismissing the provided `index`.
211    fn resolve(&self, _index: u32, module: &str, field: &str) -> Option<Export> {
212        self.resolve_by_name(module, field)
213    }
214}
215
216impl<T: NamedResolver> NamedResolver for &T {
217    fn resolve_by_name(&self, module: &str, field: &str) -> Option<Export> {
218        (**self).resolve_by_name(module, field)
219    }
220}
221
222impl NamedResolver for Box<dyn NamedResolver + Send + Sync> {
223    fn resolve_by_name(&self, module: &str, field: &str) -> Option<Export> {
224        (**self).resolve_by_name(module, field)
225    }
226}
227
228impl NamedResolver for () {
229    /// Always returns `None`.
230    fn resolve_by_name(&self, _module: &str, _field: &str) -> Option<Export> {
231        None
232    }
233}
234
235/// `Resolver` implementation that always resolves to `None`. Equivalent to `()`.
236pub struct NullResolver {}
237
238impl Resolver for NullResolver {
239    fn resolve(&self, _idx: u32, _module: &str, _field: &str) -> Option<Export> {
240        None
241    }
242}
243
244/// A [`Resolver`] that links two resolvers together in a chain.
245pub struct NamedResolverChain<A: NamedResolver + Send + Sync, B: NamedResolver + Send + Sync> {
246    a: A,
247    b: B,
248}
249
250/// A trait for chaining resolvers together.
251///
252/// ```
253/// # use wasmer_vm::{ChainableNamedResolver, NamedResolver};
254/// # fn chainable_test<A, B>(imports1: A, imports2: B)
255/// # where A: NamedResolver + Sized + Send + Sync,
256/// #       B: NamedResolver + Sized + Send + Sync,
257/// # {
258/// // override duplicates with imports from `imports2`
259/// imports1.chain_front(imports2);
260/// # }
261/// ```
262pub trait ChainableNamedResolver: NamedResolver + Sized + Send + Sync {
263    /// Chain a resolver in front of the current resolver.
264    ///
265    /// This will cause the second resolver to override the first.
266    ///
267    /// ```
268    /// # use wasmer_vm::{ChainableNamedResolver, NamedResolver};
269    /// # fn chainable_test<A, B>(imports1: A, imports2: B)
270    /// # where A: NamedResolver + Sized + Send + Sync,
271    /// #       B: NamedResolver + Sized + Send + Sync,
272    /// # {
273    /// // override duplicates with imports from `imports2`
274    /// imports1.chain_front(imports2);
275    /// # }
276    /// ```
277    fn chain_front<U>(self, other: U) -> NamedResolverChain<U, Self>
278    where
279        U: NamedResolver + Send + Sync,
280    {
281        NamedResolverChain { a: other, b: self }
282    }
283
284    /// Chain a resolver behind the current resolver.
285    ///
286    /// This will cause the first resolver to override the second.
287    ///
288    /// ```
289    /// # use wasmer_vm::{ChainableNamedResolver, NamedResolver};
290    /// # fn chainable_test<A, B>(imports1: A, imports2: B)
291    /// # where A: NamedResolver + Sized + Send + Sync,
292    /// #       B: NamedResolver + Sized + Send + Sync,
293    /// # {
294    /// // override duplicates with imports from `imports1`
295    /// imports1.chain_back(imports2);
296    /// # }
297    /// ```
298    fn chain_back<U>(self, other: U) -> NamedResolverChain<Self, U>
299    where
300        U: NamedResolver + Send + Sync,
301    {
302        NamedResolverChain { a: self, b: other }
303    }
304}
305
306// We give these chain methods to all types implementing NamedResolver
307impl<T: NamedResolver + Send + Sync> ChainableNamedResolver for T {}
308
309impl<A, B> NamedResolver for NamedResolverChain<A, B>
310where
311    A: NamedResolver + Send + Sync,
312    B: NamedResolver + Send + Sync,
313{
314    fn resolve_by_name(&self, module: &str, field: &str) -> Option<Export> {
315        self.a
316            .resolve_by_name(module, field)
317            .or_else(|| self.b.resolve_by_name(module, field))
318    }
319}
320
321impl<A, B> Clone for NamedResolverChain<A, B>
322where
323    A: NamedResolver + Clone + Send + Sync,
324    B: NamedResolver + Clone + Send + Sync,
325{
326    fn clone(&self) -> Self {
327        Self {
328            a: self.a.clone(),
329            b: self.b.clone(),
330        }
331    }
332}