casper_wasmi/
imports.rs

1use crate::{
2    func::FuncRef,
3    global::GlobalRef,
4    memory::MemoryRef,
5    module::ModuleRef,
6    table::TableRef,
7    types::{GlobalDescriptor, MemoryDescriptor, TableDescriptor},
8    Error,
9    Signature,
10};
11use alloc::{collections::BTreeMap, string::String};
12
13/// Resolver of a module's dependencies.
14///
15/// A module have dependencies in a form of a list of imports (i.e.
16/// tuple of a (`module_name`, `field_name`, `descriptor`)).
17///
18/// The job of implementations of this trait is to provide on each
19/// import a corresponding concrete reference.
20///
21/// For simple use-cases you can use [`ImportsBuilder`].
22///
23/// [`ImportsBuilder`]: struct.ImportsBuilder.html
24pub trait ImportResolver {
25    /// Resolve a function.
26    ///
27    /// Returned function should match given `signature`, i.e. all parameter types and return value should have exact match.
28    /// Otherwise, link-time error will occur.
29    fn resolve_func(
30        &self,
31        _module_name: &str,
32        field_name: &str,
33        _signature: &Signature,
34    ) -> Result<FuncRef, Error>;
35
36    /// Resolve a global variable.
37    ///
38    /// Returned global should match given `descriptor`, i.e. type and mutability
39    /// should match. Otherwise, link-time error will occur.
40    fn resolve_global(
41        &self,
42        module_name: &str,
43        field_name: &str,
44        descriptor: &GlobalDescriptor,
45    ) -> Result<GlobalRef, Error>;
46
47    /// Resolve a memory.
48    ///
49    /// Returned memory should match requested memory (described by the `descriptor`),
50    /// i.e. initial size of a returned memory should be equal or larger than requested memory.
51    /// Furthermore, if requested memory have maximum size, returned memory either should have
52    /// equal or larger maximum size or have no maximum size at all.
53    /// If returned memory doesn't match the requested then link-time error will occur.
54    fn resolve_memory(
55        &self,
56        module_name: &str,
57        field_name: &str,
58        descriptor: &MemoryDescriptor,
59    ) -> Result<MemoryRef, Error>;
60
61    /// Resolve a table.
62    ///
63    /// Returned table should match requested table (described by the `descriptor`),
64    /// i.e. initial size of a returned table should be equal or larger than requested table.
65    /// Furthermore, if requested memory have maximum size, returned memory either should have
66    /// equal or larger maximum size or have no maximum size at all.
67    /// If returned table doesn't match the requested then link-time error will occur.
68    fn resolve_table(
69        &self,
70        module_name: &str,
71        field_name: &str,
72        descriptor: &TableDescriptor,
73    ) -> Result<TableRef, Error>;
74}
75
76/// Convenience builder of [`ImportResolver`].
77///
78/// With help of this builder, you can easily create [`ImportResolver`], just by
79/// adding needed [resolvers][`ModuleImportResolver`] by names.
80///
81/// # Examples
82///
83/// ```rust
84/// use casper_wasmi::{ModuleInstance, ImportsBuilder};
85/// #
86/// # struct EnvModuleResolver;
87/// # impl ::casper_wasmi::ModuleImportResolver for EnvModuleResolver { }
88/// # fn func() -> Result<(), ::casper_wasmi::Error> {
89/// # let module = casper_wasmi::Module::from_buffer(&[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]).unwrap();
90/// # let other_instance = ModuleInstance::new(&module, &ImportsBuilder::default())?.assert_no_start();
91///
92/// let imports = ImportsBuilder::new()
93///     .with_resolver("env", &EnvModuleResolver)
94///     // Note, that ModuleInstance can be a resolver too.
95///     .with_resolver("other_instance", &other_instance);
96/// let instance = ModuleInstance::new(&module, &imports)?.assert_no_start();
97///
98/// # Ok(())
99/// # }
100/// ```
101///
102/// [`ImportResolver`]: trait.ImportResolver.html
103/// [`ModuleImportResolver`]: trait.ModuleImportResolver.html
104pub struct ImportsBuilder<'a> {
105    modules: BTreeMap<String, &'a dyn ModuleImportResolver>,
106}
107
108impl Default for ImportsBuilder<'_> {
109    fn default() -> Self {
110        Self::new()
111    }
112}
113
114impl<'a> ImportsBuilder<'a> {
115    /// Create an empty `ImportsBuilder`.
116    pub fn new() -> ImportsBuilder<'a> {
117        ImportsBuilder {
118            modules: BTreeMap::new(),
119        }
120    }
121
122    /// Register an resolver by a name.
123    #[must_use]
124    pub fn with_resolver<N: Into<String>>(
125        mut self,
126        name: N,
127        resolver: &'a dyn ModuleImportResolver,
128    ) -> Self {
129        self.modules.insert(name.into(), resolver);
130        self
131    }
132
133    /// Register an resolver by a name.
134    ///
135    /// Mutable borrowed version.
136    pub fn push_resolver<N: Into<String>>(
137        &mut self,
138        name: N,
139        resolver: &'a dyn ModuleImportResolver,
140    ) {
141        self.modules.insert(name.into(), resolver);
142    }
143
144    fn resolver(&self, name: &str) -> Option<&dyn ModuleImportResolver> {
145        self.modules.get(name).cloned()
146    }
147}
148
149impl ImportResolver for ImportsBuilder<'_> {
150    fn resolve_func(
151        &self,
152        module_name: &str,
153        field_name: &str,
154        signature: &Signature,
155    ) -> Result<FuncRef, Error> {
156        self.resolver(module_name)
157            .ok_or_else(|| Error::Instantiation(format!("Module {} not found", module_name)))?
158            .resolve_func(field_name, signature)
159    }
160
161    fn resolve_global(
162        &self,
163        module_name: &str,
164        field_name: &str,
165        global_type: &GlobalDescriptor,
166    ) -> Result<GlobalRef, Error> {
167        self.resolver(module_name)
168            .ok_or_else(|| Error::Instantiation(format!("Module {} not found", module_name)))?
169            .resolve_global(field_name, global_type)
170    }
171
172    fn resolve_memory(
173        &self,
174        module_name: &str,
175        field_name: &str,
176        memory_type: &MemoryDescriptor,
177    ) -> Result<MemoryRef, Error> {
178        self.resolver(module_name)
179            .ok_or_else(|| Error::Instantiation(format!("Module {} not found", module_name)))?
180            .resolve_memory(field_name, memory_type)
181    }
182
183    fn resolve_table(
184        &self,
185        module_name: &str,
186        field_name: &str,
187        table_type: &TableDescriptor,
188    ) -> Result<TableRef, Error> {
189        self.resolver(module_name)
190            .ok_or_else(|| Error::Instantiation(format!("Module {} not found", module_name)))?
191            .resolve_table(field_name, table_type)
192    }
193}
194
195/// Version of [`ImportResolver`] specialized for a single module.
196///
197/// [`ImportResolver`]: trait.ImportResolver.html
198pub trait ModuleImportResolver {
199    /// Resolve a function.
200    ///
201    /// See [`ImportResolver::resolve_func`] for details.
202    ///
203    /// [`ImportResolver::resolve_func`]: trait.ImportResolver.html#tymethod.resolve_func
204    fn resolve_func(&self, field_name: &str, _signature: &Signature) -> Result<FuncRef, Error> {
205        Err(Error::Instantiation(format!(
206            "Export {} not found",
207            field_name
208        )))
209    }
210
211    /// Resolve a global variable.
212    ///
213    /// See [`ImportResolver::resolve_global`] for details.
214    ///
215    /// [`ImportResolver::resolve_global`]: trait.ImportResolver.html#tymethod.resolve_global
216    fn resolve_global(
217        &self,
218        field_name: &str,
219        _global_type: &GlobalDescriptor,
220    ) -> Result<GlobalRef, Error> {
221        Err(Error::Instantiation(format!(
222            "Export {} not found",
223            field_name
224        )))
225    }
226
227    /// Resolve a memory.
228    ///
229    /// See [`ImportResolver::resolve_memory`] for details.
230    ///
231    /// [`ImportResolver::resolve_memory`]: trait.ImportResolver.html#tymethod.resolve_memory
232    fn resolve_memory(
233        &self,
234        field_name: &str,
235        _memory_type: &MemoryDescriptor,
236    ) -> Result<MemoryRef, Error> {
237        Err(Error::Instantiation(format!(
238            "Export {} not found",
239            field_name
240        )))
241    }
242
243    /// Resolve a table.
244    ///
245    /// See [`ImportResolver::resolve_table`] for details.
246    ///
247    /// [`ImportResolver::resolve_table`]: trait.ImportResolver.html#tymethod.resolve_table
248    fn resolve_table(
249        &self,
250        field_name: &str,
251        _table_type: &TableDescriptor,
252    ) -> Result<TableRef, Error> {
253        Err(Error::Instantiation(format!(
254            "Export {} not found",
255            field_name
256        )))
257    }
258}
259
260impl ModuleImportResolver for ModuleRef {
261    fn resolve_func(&self, field_name: &str, _signature: &Signature) -> Result<FuncRef, Error> {
262        self.export_by_name(field_name)
263            .ok_or_else(|| Error::Instantiation(format!("Export {} not found", field_name)))?
264            .as_func()
265            .cloned()
266            .ok_or_else(|| Error::Instantiation(format!("Export {} is not a function", field_name)))
267    }
268
269    fn resolve_global(
270        &self,
271        field_name: &str,
272        _global_type: &GlobalDescriptor,
273    ) -> Result<GlobalRef, Error> {
274        self.export_by_name(field_name)
275            .ok_or_else(|| Error::Instantiation(format!("Export {} not found", field_name)))?
276            .as_global()
277            .cloned()
278            .ok_or_else(|| Error::Instantiation(format!("Export {} is not a global", field_name)))
279    }
280
281    fn resolve_memory(
282        &self,
283        field_name: &str,
284        _memory_type: &MemoryDescriptor,
285    ) -> Result<MemoryRef, Error> {
286        self.export_by_name(field_name)
287            .ok_or_else(|| Error::Instantiation(format!("Export {} not found", field_name)))?
288            .as_memory()
289            .cloned()
290            .ok_or_else(|| Error::Instantiation(format!("Export {} is not a memory", field_name)))
291    }
292
293    fn resolve_table(
294        &self,
295        field_name: &str,
296        _table_type: &TableDescriptor,
297    ) -> Result<TableRef, Error> {
298        self.export_by_name(field_name)
299            .ok_or_else(|| Error::Instantiation(format!("Export {} not found", field_name)))?
300            .as_table()
301            .cloned()
302            .ok_or_else(|| Error::Instantiation(format!("Export {} is not a table", field_name)))
303    }
304}