radix_wasmi/
linker.rs

1use super::{
2    errors::{MemoryError, TableError},
3    AsContextMut,
4    Error,
5    Extern,
6    InstancePre,
7    Module,
8};
9use crate::{
10    module::{ImportName, ImportType},
11    ExternType,
12    FuncType,
13    GlobalType,
14};
15use alloc::{
16    collections::{btree_map::Entry, BTreeMap},
17    sync::Arc,
18    vec::Vec,
19};
20use core::{
21    fmt,
22    fmt::{Debug, Display},
23    marker::PhantomData,
24    num::NonZeroUsize,
25    ops::Deref,
26};
27
28/// An error that may occur upon operating with [`Linker`] instances.
29#[derive(Debug)]
30pub enum LinkerError {
31    /// Encountered duplicate definitions for the same name.
32    DuplicateDefinition {
33        /// The duplicate import name of the definition.
34        import_name: ImportName,
35        /// The duplicated imported item.
36        ///
37        /// This refers to the second inserted item.
38        import_item: Extern,
39    },
40    /// Encountered when no definition for an import is found.
41    CannotFindDefinitionForImport {
42        /// The name of the import for which no definition was found.
43        name: ImportName,
44        // /// The module name of the import for which no definition has been found.
45        // module_name: String,
46        // /// The field name of the import for which no definition has been found.
47        // field_name: Option<String>,
48        /// The type of the import for which no definition has been found.
49        item_type: ExternType,
50    },
51    /// Encountered when a function signature does not match the expected signature.
52    FuncTypeMismatch {
53        /// The name of the import with the mismatched type.
54        name: ImportName,
55        /// The expected function type.
56        expected: FuncType,
57        /// The actual function signature found.
58        actual: FuncType,
59    },
60    /// Occurs when an imported table does not satisfy the required table type.
61    Table(TableError),
62    /// Occurs when an imported memory does not satisfy the required memory type.
63    Memory(MemoryError),
64    /// Encountered when an imported global variable has a mismatching global variable type.
65    GlobalTypeMismatch {
66        /// The name of the import with the mismatched type.
67        name: ImportName,
68        /// The expected global variable type.
69        expected: GlobalType,
70        /// The actual global variable type found.
71        actual: GlobalType,
72    },
73}
74
75impl LinkerError {
76    /// Creates a new [`LinkerError`] for when an imported definition was not found.
77    pub fn cannot_find_definition_of_import(import: &ImportType) -> Self {
78        Self::CannotFindDefinitionForImport {
79            name: import.import_name().clone(),
80            item_type: import.ty().clone(),
81        }
82    }
83}
84
85impl From<TableError> for LinkerError {
86    fn from(error: TableError) -> Self {
87        Self::Table(error)
88    }
89}
90
91impl From<MemoryError> for LinkerError {
92    fn from(error: MemoryError) -> Self {
93        Self::Memory(error)
94    }
95}
96
97#[cfg(feature = "std")]
98impl std::error::Error for LinkerError {}
99
100impl Display for LinkerError {
101    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
102        match self {
103            Self::DuplicateDefinition {
104                import_name,
105                import_item,
106            } => {
107                write!(
108                    f,
109                    "encountered duplicate definition `{import_name}` of {import_item:?}",
110                )
111            }
112            Self::CannotFindDefinitionForImport { name, item_type } => {
113                write!(f, "cannot find definition for import {name}: {item_type:?}",)
114            }
115            Self::FuncTypeMismatch {
116                name,
117                expected,
118                actual,
119            } => {
120                write!(
121                    f,
122                    "function type mismatch for import {name}: \
123                    expected {expected:?} but found {actual:?}",
124                )
125            }
126            Self::GlobalTypeMismatch {
127                name,
128                expected,
129                actual,
130            } => {
131                write!(
132                    f,
133                    "global variable type mismatch for import {name}: \
134                    expected {expected:?} but found {actual:?}",
135                )
136            }
137            Self::Table(error) => Display::fmt(error, f),
138            Self::Memory(error) => Display::fmt(error, f),
139        }
140    }
141}
142
143/// A symbol representing an interned string.
144///
145/// # Note
146///
147/// Comparing symbols for equality is equal to comparing their respective
148/// interned strings for equality given that both symbol are coming from
149/// the same string interner instance.
150///
151/// # Dev. Note
152///
153/// Internally we use [`NonZeroUsize`] so that `Option<Symbol>` can
154/// be space optimized easily by the compiler. This is important since
155/// in [`ImportKey`] we are making extensive use of `Option<Symbol>`.
156#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
157#[repr(transparent)]
158pub struct Symbol(NonZeroUsize);
159
160impl Symbol {
161    /// Creates a new symbol.
162    ///
163    /// # Panics
164    ///
165    /// If the `value` is equal to `usize::MAX`.
166    pub fn from_usize(value: usize) -> Self {
167        NonZeroUsize::new(value.wrapping_add(1))
168            .map(Symbol)
169            .expect("encountered invalid symbol value")
170    }
171
172    /// Returns the underlying `usize` value of the [`Symbol`].
173    pub fn into_usize(self) -> usize {
174        self.0.get().wrapping_sub(1)
175    }
176}
177
178/// A string interner.
179///
180/// Efficiently interns strings and distributes symbols.
181#[derive(Debug, Default, Clone)]
182pub struct StringInterner {
183    string2idx: BTreeMap<Arc<str>, Symbol>,
184    strings: Vec<Arc<str>>,
185}
186
187impl StringInterner {
188    /// Returns the next symbol.
189    fn next_symbol(&self) -> Symbol {
190        Symbol::from_usize(self.strings.len())
191    }
192
193    /// Returns the symbol of the string and interns it if necessary.
194    pub fn get_or_intern(&mut self, string: &str) -> Symbol {
195        match self.string2idx.get(string) {
196            Some(symbol) => *symbol,
197            None => {
198                let symbol = self.next_symbol();
199                let rc_string: Arc<str> = Arc::from(string);
200                self.string2idx.insert(rc_string.clone(), symbol);
201                self.strings.push(rc_string);
202                symbol
203            }
204        }
205    }
206
207    /// Returns the symbol for the string if interned.
208    pub fn get(&self, string: &str) -> Option<Symbol> {
209        self.string2idx.get(string).copied()
210    }
211
212    /// Resolves the symbol to the underlying string.
213    pub fn resolve(&self, symbol: Symbol) -> Option<&str> {
214        self.strings.get(symbol.into_usize()).map(Deref::deref)
215    }
216}
217
218/// Wasm import keys.
219#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
220struct ImportKey {
221    /// The name of the module for the definition.
222    module: Symbol,
223    /// The name of the definition within the module scope.
224    name: Symbol,
225}
226
227/// A linker used to define module imports and instantiate module instances.
228pub struct Linker<T> {
229    /// Allows to efficiently store strings and deduplicate them..
230    strings: StringInterner,
231    /// Stores the definitions given their names.
232    definitions: BTreeMap<ImportKey, Extern>,
233    marker: PhantomData<fn() -> T>,
234}
235
236impl<T> Debug for Linker<T> {
237    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
238        f.debug_struct("Linker")
239            .field("strings", &self.strings)
240            .field("definitions", &self.definitions)
241            .finish()
242    }
243}
244
245impl<T> Clone for Linker<T> {
246    fn clone(&self) -> Linker<T> {
247        Self {
248            strings: self.strings.clone(),
249            definitions: self.definitions.clone(),
250            marker: self.marker,
251        }
252    }
253}
254
255impl<T> Default for Linker<T> {
256    fn default() -> Self {
257        Self::new()
258    }
259}
260
261impl<T> Linker<T> {
262    /// Creates a new linker.
263    pub fn new() -> Self {
264        Self {
265            strings: StringInterner::default(),
266            definitions: BTreeMap::default(),
267            marker: PhantomData,
268        }
269    }
270
271    /// Define a new item in this [`Linker`].
272    ///
273    /// # Errors
274    ///
275    /// If there already is a definition under the same name for this [`Linker`].
276    pub fn define(
277        &mut self,
278        module: &str,
279        name: &str,
280        item: impl Into<Extern>,
281    ) -> Result<&mut Self, LinkerError> {
282        let key = self.import_key(module, name);
283        self.insert(key, item.into())?;
284        Ok(self)
285    }
286
287    /// Returns the import key for the module name and item name.
288    fn import_key(&mut self, module: &str, name: &str) -> ImportKey {
289        ImportKey {
290            module: self.strings.get_or_intern(module),
291            name: self.strings.get_or_intern(name),
292        }
293    }
294
295    /// Resolves the module and item name of the import key if any.
296    fn resolve_import_key(&self, key: ImportKey) -> Option<(&str, &str)> {
297        let module_name = self.strings.resolve(key.module)?;
298        let item_name = self.strings.resolve(key.name)?;
299        Some((module_name, item_name))
300    }
301
302    /// Inserts the extern item under the import key.
303    ///
304    /// # Errors
305    ///
306    /// If there already is a definition for the import key for this [`Linker`].
307    fn insert(&mut self, key: ImportKey, item: Extern) -> Result<(), LinkerError> {
308        match self.definitions.entry(key) {
309            Entry::Occupied(_) => {
310                let (module_name, field_name) = self
311                    .resolve_import_key(key)
312                    .unwrap_or_else(|| panic!("encountered missing import names for key {key:?}"));
313                let import_name = ImportName::new(module_name, field_name);
314                return Err(LinkerError::DuplicateDefinition {
315                    import_name,
316                    import_item: item,
317                });
318            }
319            Entry::Vacant(v) => {
320                v.insert(item);
321            }
322        }
323        Ok(())
324    }
325
326    /// Looks up a previously defined extern value in this [`Linker`].
327    ///
328    /// Returns `None` if this name was not previously defined in this
329    /// [`Linker`].
330    pub fn resolve(&self, module: &str, name: &str) -> Option<Extern> {
331        let key = ImportKey {
332            module: self.strings.get(module)?,
333            name: self.strings.get(name)?,
334        };
335        self.definitions.get(&key).copied()
336    }
337
338    /// Instantiates the given [`Module`] using the definitions in the [`Linker`].
339    ///
340    /// # Errors
341    ///
342    /// - If the linker does not define imports of the instantiated [`Module`].
343    /// - If any imported item does not satisfy its type requirements.
344    pub fn instantiate(
345        &self,
346        mut context: impl AsContextMut,
347        module: &Module,
348    ) -> Result<InstancePre, Error> {
349        let externals = module
350            .imports()
351            .map(|import| self.process_import(&mut context, import))
352            .collect::<Result<Vec<Extern>, Error>>()?;
353        module.instantiate(context, externals)
354    }
355
356    /// Processes a single [`Module`] import.
357    ///
358    /// # Errors
359    ///
360    /// If the imported item does not satisfy constraints set by the [`Module`].
361    fn process_import(
362        &self,
363        context: impl AsContextMut,
364        import: ImportType,
365    ) -> Result<Extern, Error> {
366        let make_err = || LinkerError::cannot_find_definition_of_import(&import);
367        let module_name = import.module();
368        let field_name = import.name();
369        let resolved = self.resolve(module_name, field_name);
370        let context = context.as_context();
371        match import.ty() {
372            ExternType::Func(expected_func_type) => {
373                let func = resolved.and_then(Extern::into_func).ok_or_else(make_err)?;
374                let actual_func_type = func.ty_dedup(&context);
375                let actual_func_type = context.store.resolve_func_type(actual_func_type);
376                if &actual_func_type != expected_func_type {
377                    return Err(LinkerError::FuncTypeMismatch {
378                        name: import.import_name().clone(),
379                        expected: expected_func_type.clone(),
380                        actual: actual_func_type,
381                    })
382                    .map_err(Into::into);
383                }
384                Ok(Extern::Func(func))
385            }
386            ExternType::Table(expected_table_type) => {
387                let table = resolved.and_then(Extern::into_table).ok_or_else(make_err)?;
388                let actual_table_type = table.ty(context);
389                actual_table_type.satisfies(expected_table_type)?;
390                Ok(Extern::Table(table))
391            }
392            ExternType::Memory(expected_memory_type) => {
393                let memory = resolved
394                    .and_then(Extern::into_memory)
395                    .ok_or_else(make_err)?;
396                let actual_memory_type = memory.ty(context);
397                actual_memory_type.satisfies(expected_memory_type)?;
398                Ok(Extern::Memory(memory))
399            }
400            ExternType::Global(expected_global_type) => {
401                let global = resolved
402                    .and_then(Extern::into_global)
403                    .ok_or_else(make_err)?;
404                let actual_global_type = global.ty(context);
405                if &actual_global_type != expected_global_type {
406                    return Err(LinkerError::GlobalTypeMismatch {
407                        name: import.import_name().clone(),
408                        expected: *expected_global_type,
409                        actual: actual_global_type,
410                    })
411                    .map_err(Into::into);
412                }
413                Ok(Extern::Global(global))
414            }
415        }
416    }
417}