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