wasm/execution/
linker.rs

1use alloc::{
2    collections::btree_map::{BTreeMap, Entry},
3    string::String,
4    vec::Vec,
5};
6
7use crate::{
8    addrs::ModuleAddr, store::InstantiationOutcome, ExternVal, RuntimeError, Store, ValidationInfo,
9};
10
11use super::config::Config;
12
13/// A linker used to link a module's imports against extern values previously
14/// defined in this [`Linker`] context.
15///
16/// # Manual Instantiation vs. Instantiation through [`Linker`]
17///
18/// Traditionally, module instances are instantiated via the method
19/// [`Store::module_instantiate`], which is part of the official Embedder API
20/// defined by the specification. However, this method accepts a list of extern
21/// values as an argument. Therefore, if the user wants to manually perform
22/// linking they have to figure out the imports of their module, then gather the
23/// correct extern values, and finally call the instantiation method.
24///
25/// This process of manual linking is very tedious and error-prone, which is why
26/// the [`Linker`] exists. It builds on top of the original instantiation method
27/// with [`Linker::module_instantiate`]. Internally this method performs name
28/// resolution and then calls the original instantiation. Name resolution is
29/// performed on all extern values which were previously defined in the current
30/// context.
31///
32/// # Extern values
33///
34/// An extern value is represented as a [`ExternVal`]. It contains an address to
35/// some store-allocated instance. In a linker context, every external value is
36/// stored in map with a unique key `(module name, name)`. To define new extern
37/// value in some linker context, use [`Linker::define`] or
38/// [`Linker::define_module_instance`].
39///
40/// # Relationship with [`Store`]
41///
42/// There is a N-to-1 relationship between the [`Linker`] and the [`Store`].
43/// This means that multiple linkers can be used with the same store, while
44/// every linker may be used only with one specific store.
45///
46/// Due to performance reasons, this bookkeeping is not done by the [`Linker`]
47/// itself. Instead it is the user's responsibility to uphold this requirement.
48#[derive(Clone, Default)]
49pub struct Linker {
50    /// All extern values in the current linker context by their import keys.
51    ///
52    /// It is guaranteed that the addresses of all extern values belong to the
53    /// same [`Store`].
54    extern_vals: BTreeMap<ImportKey, ExternVal>,
55}
56
57impl Linker {
58    /// Creates a new [`Linker`] that is not yet associated to any specific [`Store`].
59    pub fn new() -> Self {
60        Self::default()
61    }
62
63    /// Defines a new extern value in the current [`Linker`] context.
64    ///
65    /// # Safety
66    /// It must be made sure that this [`Linker`] is only used with one specific
67    /// [`Store`] and addresses that belong to that store.
68    pub fn define(
69        &mut self,
70        module_name: String,
71        name: String,
72        extern_val: ExternVal,
73    ) -> Result<(), RuntimeError> {
74        match self.extern_vals.entry(ImportKey { module_name, name }) {
75            Entry::Vacant(vacant_entry) => {
76                vacant_entry.insert(extern_val);
77                Ok(())
78            }
79            Entry::Occupied(_occupied_entry) => Err(RuntimeError::DuplicateExternDefinition),
80        }
81    }
82
83    /// Defines all exports of some module instance as extern values in the
84    /// current [`Linker`].
85    ///
86    /// # Safety
87    /// It must be guaranteed that this [`Linker`] is only ever used with one
88    /// specific [`Store`] and that the given [`ModuleAddr`] belongs to this
89    /// store.
90    pub fn define_module_instance<T: Config>(
91        &mut self,
92        store: &Store<T>,
93        module_name: String,
94        module: ModuleAddr,
95    ) -> Result<(), RuntimeError> {
96        let module = store.modules.get(module);
97        for export in &module.exports {
98            self.define(module_name.clone(), export.0.clone(), *export.1)?;
99        }
100
101        Ok(())
102    }
103
104    /// Tries to get some extern value by its module name and name.
105    ///
106    /// It is guaranteed that the address contained by the returned
107    /// [`ExternVal`] is part of the [`Store`] used with this [`Linker`].
108    pub fn get(&self, module_name: String, name: String) -> Option<ExternVal> {
109        self.extern_vals
110            .get(&ImportKey { module_name, name })
111            .copied()
112    }
113
114    /// Performs initial linking of a [`ValidationInfo`]'s imports producing a
115    /// list of extern values usable with [`Store::module_instantiate`].
116    ///
117    /// # A note on type checking
118    ///
119    /// This method does not perform type checking on the extern values.
120    /// Therefore, using the returned list of extern values may still fail when
121    /// trying to instantiate a module with it.
122    ///
123    /// # Safety
124    /// It must be guaranteed that this [`Linker`] is only ever used with one
125    /// specific [`Store`].
126    // TODO find a better name for this method? Maybe something like `link`?
127    pub fn instantiate_pre(
128        &self,
129        validation_info: &ValidationInfo,
130    ) -> Result<Vec<ExternVal>, RuntimeError> {
131        validation_info
132            .imports
133            .iter()
134            .map(|import| {
135                self.get(import.module_name.clone(), import.name.clone())
136                    .ok_or(RuntimeError::UnableToResolveImport)
137            })
138            .collect()
139    }
140
141    /// Variant of [`Store::module_instantiate`] with automatic name resolution
142    /// in the current [`Linker`] context.
143    ///
144    /// # Safety
145    /// It must be guaranteed that this [`Linker`] is only ever used with one
146    /// specific [`Store`].
147    pub fn module_instantiate<'b, T: Config>(
148        &self,
149        store: &mut Store<'b, T>,
150        validation_info: &ValidationInfo<'b>,
151        maybe_fuel: Option<u32>,
152    ) -> Result<InstantiationOutcome, RuntimeError> {
153        store.module_instantiate(
154            validation_info,
155            self.instantiate_pre(validation_info)?,
156            maybe_fuel,
157        )
158    }
159}
160
161/// A key used by Wasm modules to identify the names of imports.
162///
163/// It consists of a module name and the name of the imported item itself.
164#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord)]
165struct ImportKey {
166    module_name: String,
167    name: String,
168}