substrate_wasmtime/instance.rs
1use crate::trampoline::StoreInstanceHandle;
2use crate::{Engine, Export, Extern, Func, Global, Memory, Module, Store, Table, Trap};
3use anyhow::{bail, Error, Result};
4use std::any::Any;
5use std::mem;
6use wasmtime_environ::EntityIndex;
7use wasmtime_jit::{CompiledModule, Resolver};
8use wasmtime_runtime::{
9 InstantiationError, StackMapRegistry, VMContext, VMExternRefActivationsTable, VMFunctionBody,
10};
11
12struct SimpleResolver<'a> {
13 imports: &'a [Extern],
14}
15
16impl Resolver for SimpleResolver<'_> {
17 fn resolve(&mut self, idx: u32, _name: &str, _field: &str) -> Option<wasmtime_runtime::Export> {
18 self.imports
19 .get(idx as usize)
20 .map(|i| i.get_wasmtime_export())
21 }
22}
23
24fn instantiate(
25 store: &Store,
26 compiled_module: &CompiledModule,
27 imports: &[Extern],
28 host: Box<dyn Any>,
29) -> Result<StoreInstanceHandle, Error> {
30 // For now we have a restriction that the `Store` that we're working
31 // with is the same for everything involved here.
32 for import in imports {
33 if !import.comes_from_same_store(store) {
34 bail!("cross-`Store` instantiation is not currently supported");
35 }
36 }
37
38 if imports.len() != compiled_module.module().imports.len() {
39 bail!(
40 "wrong number of imports provided, {} != {}",
41 imports.len(),
42 compiled_module.module().imports.len()
43 );
44 }
45
46 let mut resolver = SimpleResolver { imports };
47 let config = store.engine().config();
48 let instance = unsafe {
49 let instance = compiled_module.instantiate(
50 &mut resolver,
51 &mut store.signatures_mut(),
52 config.memory_creator.as_ref().map(|a| a as _),
53 store.interrupts().clone(),
54 host,
55 store.externref_activations_table() as *const VMExternRefActivationsTable as *mut _,
56 store.stack_map_registry() as *const StackMapRegistry as *mut _,
57 )?;
58
59 // After we've created the `InstanceHandle` we still need to run
60 // initialization to set up data/elements/etc. We do this after adding
61 // the `InstanceHandle` to the store though. This is required for safety
62 // because the start function (for example) may trap, but element
63 // initializers may have run which placed elements into other instance's
64 // tables. This means that from this point on, regardless of whether
65 // initialization is successful, we need to keep the instance alive.
66 let instance = store.add_instance(instance);
67 instance
68 .initialize(
69 config.wasm_bulk_memory,
70 &compiled_module.data_initializers(),
71 )
72 .map_err(|e| -> Error {
73 match e {
74 InstantiationError::Trap(trap) => Trap::from_runtime(trap).into(),
75 other => other.into(),
76 }
77 })?;
78
79 instance
80 };
81
82 let start_func = instance.handle.module().start_func;
83
84 // If a start function is present, invoke it. Make sure we use all the
85 // trap-handling configuration in `store` as well.
86 if let Some(start) = start_func {
87 let f = match instance
88 .handle
89 .lookup_by_declaration(&EntityIndex::Function(start))
90 {
91 wasmtime_runtime::Export::Function(f) => f,
92 _ => unreachable!(), // valid modules shouldn't hit this
93 };
94 let vmctx_ptr = instance.handle.vmctx_ptr();
95 unsafe {
96 super::func::invoke_wasm_and_catch_traps(vmctx_ptr, store, || {
97 mem::transmute::<
98 *const VMFunctionBody,
99 unsafe extern "C" fn(*mut VMContext, *mut VMContext),
100 >(f.anyfunc.as_ref().func_ptr.as_ptr())(
101 f.anyfunc.as_ref().vmctx, vmctx_ptr
102 )
103 })?;
104 }
105 }
106
107 Ok(instance)
108}
109
110/// An instantiated WebAssembly module.
111///
112/// This type represents the instantiation of a [`Module`]. Once instantiated
113/// you can access the [`exports`](Instance::exports) which are of type
114/// [`Extern`] and provide the ability to call functions, set globals, read
115/// memory, etc. This is where all the fun stuff happens!
116///
117/// An [`Instance`] is created from two inputs, a [`Module`] and a list of
118/// imports, provided as a list of [`Extern`] values. The [`Module`] is the wasm
119/// code that was compiled and we're instantiating, and the [`Extern`] imports
120/// are how we're satisfying the imports of the module provided. On successful
121/// instantiation an [`Instance`] will automatically invoke the wasm `start`
122/// function.
123///
124/// When interacting with any wasm code you'll want to make an [`Instance`] to
125/// call any code or execute anything!
126#[derive(Clone)]
127pub struct Instance {
128 pub(crate) handle: StoreInstanceHandle,
129 store: Store,
130 module: Module,
131}
132
133impl Instance {
134 /// Creates a new [`Instance`] from the previously compiled [`Module`] and
135 /// list of `imports` specified.
136 ///
137 /// This method instantiates the `module` provided with the `imports`,
138 /// following the procedure in the [core specification][inst] to
139 /// instantiate. Instantiation can fail for a number of reasons (many
140 /// specified below), but if successful the `start` function will be
141 /// automatically run (if provided) and then the [`Instance`] will be
142 /// returned.
143 ///
144 /// Per the WebAssembly spec, instantiation includes running the module's
145 /// start function, if it has one (not to be confused with the `_start`
146 /// function, which is not run).
147 ///
148 /// Note that this is a low-level function that just performance an
149 /// instantiation. See the `Linker` struct for an API which provides a
150 /// convenient way to link imports and provides automatic Command and Reactor
151 /// behavior.
152 ///
153 /// ## Providing Imports
154 ///
155 /// The `imports` array here is a bit tricky. The entries in the list of
156 /// `imports` are intended to correspond 1:1 with the list of imports
157 /// returned by [`Module::imports`]. Before calling [`Instance::new`] you'll
158 /// want to inspect the return value of [`Module::imports`] and, for each
159 /// import type, create an [`Extern`] which corresponds to that type.
160 /// These [`Extern`] values are all then collected into a list and passed to
161 /// this function.
162 ///
163 /// Note that this function is intentionally relatively low level. It is the
164 /// intention that we'll soon provide a [higher level API][issue] which will
165 /// be much more ergonomic for instantiating modules. If you need the full
166 /// power of customization of imports, though, this is the method for you!
167 ///
168 /// ## Errors
169 ///
170 /// This function can fail for a number of reasons, including, but not
171 /// limited to:
172 ///
173 /// * The number of `imports` provided doesn't match the number of imports
174 /// returned by the `module`'s [`Module::imports`] method.
175 /// * The type of any [`Extern`] doesn't match the corresponding
176 /// [`ExternType`] entry that it maps to.
177 /// * The `start` function in the instance, if present, traps.
178 /// * Module/instance resource limits are exceeded.
179 ///
180 /// When instantiation fails it's recommended to inspect the return value to
181 /// see why it failed, or bubble it upwards. If you'd like to specifically
182 /// check for trap errors, you can use `error.downcast::<Trap>()`.
183 ///
184 /// [inst]: https://webassembly.github.io/spec/core/exec/modules.html#exec-instantiation
185 /// [issue]: https://github.com/bytecodealliance/wasmtime/issues/727
186 /// [`ExternType`]: crate::ExternType
187 pub fn new(store: &Store, module: &Module, imports: &[Extern]) -> Result<Instance, Error> {
188 if !Engine::same(store.engine(), module.engine()) {
189 bail!("cross-`Engine` instantiation is not currently supported");
190 }
191
192 let host_info = Box::new({
193 let frame_info_registration = module.register_frame_info();
194 store.register_jit_code(module.compiled_module().jit_code_ranges());
195 store.register_stack_maps(&module);
196 frame_info_registration
197 });
198
199 let handle = instantiate(store, module.compiled_module(), imports, host_info)?;
200
201 Ok(Instance {
202 handle,
203 store: store.clone(),
204 module: module.clone(),
205 })
206 }
207
208 /// Returns the associated [`Store`] that this `Instance` is compiled into.
209 ///
210 /// This is the [`Store`] that generally serves as a sort of global cache
211 /// for various instance-related things.
212 pub fn store(&self) -> &Store {
213 &self.store
214 }
215
216 /// Returns the list of exported items from this [`Instance`].
217 pub fn exports<'instance>(
218 &'instance self,
219 ) -> impl ExactSizeIterator<Item = Export<'instance>> + 'instance {
220 self.handle.exports().map(move |(name, entity_index)| {
221 let export = self.handle.lookup_by_declaration(entity_index);
222 let extern_ = Extern::from_wasmtime_export(export, self.handle.clone());
223 Export::new(name, extern_)
224 })
225 }
226
227 /// Looks up an exported [`Extern`] value by name.
228 ///
229 /// This method will search the module for an export named `name` and return
230 /// the value, if found.
231 ///
232 /// Returns `None` if there was no export named `name`.
233 pub fn get_export(&self, name: &str) -> Option<Extern> {
234 let export = self.handle.lookup(&name)?;
235 Some(Extern::from_wasmtime_export(export, self.handle.clone()))
236 }
237
238 /// Looks up an exported [`Func`] value by name.
239 ///
240 /// Returns `None` if there was no export named `name`, or if there was but
241 /// it wasn't a function.
242 pub fn get_func(&self, name: &str) -> Option<Func> {
243 self.get_export(name)?.into_func()
244 }
245
246 /// Looks up an exported [`Table`] value by name.
247 ///
248 /// Returns `None` if there was no export named `name`, or if there was but
249 /// it wasn't a table.
250 pub fn get_table(&self, name: &str) -> Option<Table> {
251 self.get_export(name)?.into_table()
252 }
253
254 /// Looks up an exported [`Memory`] value by name.
255 ///
256 /// Returns `None` if there was no export named `name`, or if there was but
257 /// it wasn't a memory.
258 pub fn get_memory(&self, name: &str) -> Option<Memory> {
259 self.get_export(name)?.into_memory()
260 }
261
262 /// Looks up an exported [`Global`] value by name.
263 ///
264 /// Returns `None` if there was no export named `name`, or if there was but
265 /// it wasn't a global.
266 pub fn get_global(&self, name: &str) -> Option<Global> {
267 self.get_export(name)?.into_global()
268 }
269
270 #[doc(hidden)]
271 pub fn handle(&self) -> &StoreInstanceHandle {
272 &self.handle
273 }
274}