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