Skip to main content

near_vm_vm/
resolver.rs

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