linera_wasmer/
exports.rs

1use crate::store::AsStoreRef;
2use crate::{Extern, Function, Global, Memory, Table, TypedFunction, WasmTypeList};
3use indexmap::IndexMap;
4use std::fmt;
5use std::iter::{ExactSizeIterator, FromIterator};
6use thiserror::Error;
7
8/// The `ExportError` can happen when trying to get a specific
9/// export [`Extern`] from the [`Instance`] exports.
10///
11/// [`Instance`]: crate::Instance
12///
13/// # Examples
14///
15/// ## Incompatible export type
16///
17/// ```should_panic
18/// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, Value, ExportError};
19/// # let mut store = Store::default();
20/// # let wasm_bytes = wat2wasm(r#"
21/// # (module
22/// #   (global $one (export "glob") f32 (f32.const 1)))
23/// # "#.as_bytes()).unwrap();
24/// # let module = Module::new(&store, wasm_bytes).unwrap();
25/// # let import_object = imports! {};
26/// # let instance = Instance::new(&mut store, &module, &import_object).unwrap();
27/// #
28/// // This results with an error: `ExportError::IncompatibleType`.
29/// let export = instance.exports.get_function("glob").unwrap();
30/// ```
31///
32/// ## Missing export
33///
34/// ```should_panic
35/// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, Value, ExportError};
36/// # let mut store = Store::default();
37/// # let wasm_bytes = wat2wasm("(module)".as_bytes()).unwrap();
38/// # let module = Module::new(&store, wasm_bytes).unwrap();
39/// # let import_object = imports! {};
40/// # let instance = Instance::new(&mut store, &module, &import_object).unwrap();
41/// #
42/// // This results with an error: `ExportError::Missing`.
43/// let export = instance.exports.get_function("unknown").unwrap();
44/// ```
45#[derive(Error, Debug, Clone)]
46pub enum ExportError {
47    /// An error than occurs when the exported type and the expected type
48    /// are incompatible.
49    #[error("Incompatible Export Type")]
50    IncompatibleType,
51    /// This error arises when an export is missing
52    #[error("Missing export {0}")]
53    Missing(String),
54}
55
56/// Exports is a special kind of map that allows easily unwrapping
57/// the types of instances.
58///
59/// TODO: add examples of using exports
60#[derive(Clone, Default, PartialEq, Eq)]
61#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
62pub struct Exports {
63    map: IndexMap<String, Extern>,
64}
65
66impl Exports {
67    /// Creates a new `Exports`.
68    pub fn new() -> Self {
69        Default::default()
70    }
71
72    /// Creates a new `Exports` with capacity `n`.
73    pub fn with_capacity(n: usize) -> Self {
74        Self {
75            map: IndexMap::with_capacity(n),
76        }
77    }
78
79    /// Return the number of exports in the `Exports` map.
80    pub fn len(&self) -> usize {
81        self.map.len()
82    }
83
84    /// Return whether or not there are no exports
85    pub fn is_empty(&self) -> bool {
86        self.len() == 0
87    }
88
89    /// Insert a new export into this `Exports` map.
90    pub fn insert<S, E>(&mut self, name: S, value: E)
91    where
92        S: Into<String>,
93        E: Into<Extern>,
94    {
95        self.map.insert(name.into(), value.into());
96    }
97
98    /// Get an export given a `name`.
99    ///
100    /// The `get` method is specifically made for usage inside of
101    /// Rust APIs, as we can detect what's the desired type easily.
102    ///
103    /// If you want to get an export dynamically with type checking
104    /// please use the following functions: `get_func`, `get_memory`,
105    /// `get_table` or `get_global` instead.
106    ///
107    /// If you want to get an export dynamically handling manually
108    /// type checking manually, please use `get_extern`.
109    pub fn get<'a, T: Exportable<'a>>(&'a self, name: &str) -> Result<&'a T, ExportError> {
110        match self.map.get(name) {
111            None => Err(ExportError::Missing(name.to_string())),
112            Some(extern_) => T::get_self_from_extern(extern_),
113        }
114    }
115
116    /// Get an export as a `Global`.
117    pub fn get_global(&self, name: &str) -> Result<&Global, ExportError> {
118        self.get(name)
119    }
120
121    /// Get an export as a `Memory`.
122    pub fn get_memory(&self, name: &str) -> Result<&Memory, ExportError> {
123        self.get(name)
124    }
125
126    /// Get an export as a `Table`.
127    pub fn get_table(&self, name: &str) -> Result<&Table, ExportError> {
128        self.get(name)
129    }
130
131    /// Get an export as a `Func`.
132    pub fn get_function(&self, name: &str) -> Result<&Function, ExportError> {
133        self.get(name)
134    }
135
136    /// Get an export as a `TypedFunction`.
137    pub fn get_typed_function<Args, Rets>(
138        &self,
139        store: &impl AsStoreRef,
140        name: &str,
141    ) -> Result<TypedFunction<Args, Rets>, ExportError>
142    where
143        Args: WasmTypeList,
144        Rets: WasmTypeList,
145    {
146        self.get_function(name)?
147            .typed(store)
148            .map_err(|_| ExportError::IncompatibleType)
149    }
150
151    /// Hack to get this working with nativefunc too
152    pub fn get_with_generics<'a, T, Args, Rets>(&'a self, name: &str) -> Result<T, ExportError>
153    where
154        Args: WasmTypeList,
155        Rets: WasmTypeList,
156        T: ExportableWithGenerics<'a, Args, Rets>,
157    {
158        match self.map.get(name) {
159            None => Err(ExportError::Missing(name.to_string())),
160            Some(extern_) => T::get_self_from_extern_with_generics(extern_),
161        }
162    }
163
164    /// Get an export as an `Extern`.
165    pub fn get_extern(&self, name: &str) -> Option<&Extern> {
166        self.map.get(name)
167    }
168
169    /// Returns true if the `Exports` contains the given export name.
170    pub fn contains<S>(&self, name: S) -> bool
171    where
172        S: Into<String>,
173    {
174        self.map.contains_key(&name.into())
175    }
176
177    /// Get an iterator over the exports.
178    pub fn iter(&self) -> ExportsIterator<impl Iterator<Item = (&String, &Extern)>> {
179        ExportsIterator {
180            iter: self.map.iter(),
181        }
182    }
183}
184
185impl fmt::Debug for Exports {
186    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
187        f.debug_set().entries(self.iter()).finish()
188    }
189}
190
191/// An iterator over exports.
192pub struct ExportsIterator<'a, I>
193where
194    I: Iterator<Item = (&'a String, &'a Extern)> + Sized,
195{
196    iter: I,
197}
198
199impl<'a, I> Iterator for ExportsIterator<'a, I>
200where
201    I: Iterator<Item = (&'a String, &'a Extern)> + Sized,
202{
203    type Item = (&'a String, &'a Extern);
204
205    fn next(&mut self) -> Option<Self::Item> {
206        self.iter.next()
207    }
208}
209
210impl<'a, I> ExactSizeIterator for ExportsIterator<'a, I>
211where
212    I: Iterator<Item = (&'a String, &'a Extern)> + ExactSizeIterator + Sized,
213{
214    fn len(&self) -> usize {
215        self.iter.len()
216    }
217}
218
219impl<'a, I> ExportsIterator<'a, I>
220where
221    I: Iterator<Item = (&'a String, &'a Extern)> + Sized,
222{
223    /// Get only the functions.
224    pub fn functions(self) -> impl Iterator<Item = (&'a String, &'a Function)> + Sized {
225        self.iter.filter_map(|(name, export)| match export {
226            Extern::Function(function) => Some((name, function)),
227            _ => None,
228        })
229    }
230
231    /// Get only the memories.
232    pub fn memories(self) -> impl Iterator<Item = (&'a String, &'a Memory)> + Sized {
233        self.iter.filter_map(|(name, export)| match export {
234            Extern::Memory(memory) => Some((name, memory)),
235            _ => None,
236        })
237    }
238
239    /// Get only the globals.
240    pub fn globals(self) -> impl Iterator<Item = (&'a String, &'a Global)> + Sized {
241        self.iter.filter_map(|(name, export)| match export {
242            Extern::Global(global) => Some((name, global)),
243            _ => None,
244        })
245    }
246
247    /// Get only the tables.
248    pub fn tables(self) -> impl Iterator<Item = (&'a String, &'a Table)> + Sized {
249        self.iter.filter_map(|(name, export)| match export {
250            Extern::Table(table) => Some((name, table)),
251            _ => None,
252        })
253    }
254}
255
256impl FromIterator<(String, Extern)> for Exports {
257    fn from_iter<I: IntoIterator<Item = (String, Extern)>>(iter: I) -> Self {
258        Self {
259            map: IndexMap::from_iter(iter),
260        }
261    }
262}
263
264impl IntoIterator for Exports {
265    type IntoIter = indexmap::map::IntoIter<String, Extern>;
266    type Item = (String, Extern);
267
268    fn into_iter(self) -> Self::IntoIter {
269        self.map.into_iter()
270    }
271}
272
273impl<'a> IntoIterator for &'a Exports {
274    type IntoIter = indexmap::map::Iter<'a, String, Extern>;
275    type Item = (&'a String, &'a Extern);
276
277    fn into_iter(self) -> Self::IntoIter {
278        self.map.iter()
279    }
280}
281
282/// This trait is used to mark types as gettable from an [`Instance`].
283///
284/// [`Instance`]: crate::Instance
285pub trait Exportable<'a>: Sized {
286    /// Implementation of how to get the export corresponding to the implementing type
287    /// from an [`Instance`] by name.
288    ///
289    /// [`Instance`]: crate::Instance
290    fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError>;
291}
292
293/// A trait for accessing exports (like [`Exportable`]) but it takes generic
294/// `Args` and `Rets` parameters so that `TypedFunction` can be accessed directly
295/// as well.
296pub trait ExportableWithGenerics<'a, Args: WasmTypeList, Rets: WasmTypeList>: Sized {
297    /// Get an export with the given generics.
298    fn get_self_from_extern_with_generics(_extern: &'a Extern) -> Result<Self, ExportError>;
299}
300
301/// We implement it for all concrete [`Exportable`] types (that are `Clone`)
302/// with empty `Args` and `Rets`.
303impl<'a, T: Exportable<'a> + Clone + 'static> ExportableWithGenerics<'a, (), ()> for T {
304    fn get_self_from_extern_with_generics(_extern: &'a Extern) -> Result<Self, ExportError> {
305        T::get_self_from_extern(_extern).map(|i| i.clone())
306    }
307}