wasm/execution/checked/mod.rs
1//! Definitions for checked, safe variants of methods defined on [`Store`] and
2//! [`Linker`].
3//!
4//! This module defines extensions in the form of new types and new methods. It
5//! only relies on the fact that the [`Store`] and the [`Linker`] both store a
6//! [`StoreId`]. No other changes are required to be made to the main
7//! interpreter for this checked API.
8//!
9//!
10//! All extension methods defined in this module use special _stored_ objects.
11//! These objects are essentially normal objects like [`FuncAddr`], [`RunState`]
12//! or [`Value`]. However, they also contain an additional field of type
13//! [`StoreId`] as a tag to know to which [`Store`] they belong to.
14//!
15//! While this is easy for address types like [`FuncAddr`] or [`MemAddr`], some
16//! types are enums and their variants are visible to the user. For example,
17//! consider the [`Value`] enum, where users have full access to all of its
18//! variants. To be able to attach a tag only to the [`Value::Ref`] variant of
19//! this enum, the entire enum has to be re-defined. The result is a completely
20//! new type [`StoredValue`].
21
22use core::num::NonZeroU64;
23
24use crate::{
25 addrs::{FuncAddr, GlobalAddr, MemAddr, ModuleAddr, TableAddr},
26 config::Config,
27 core::reader::types::{FuncType, MemType, TableType},
28 interop::InteropValueList,
29 linker::Linker,
30 resumable::{ResumableRef, RunState},
31 ExternVal, GlobalType, HaltExecutionError, InstantiationOutcome, RuntimeError, Store, StoreId,
32 ValidationInfo, Value,
33};
34use alloc::{string::String, vec::Vec};
35
36mod interop;
37mod value;
38
39pub use interop::*;
40pub use value::*;
41
42// All functions in this impl block must occur in the same order as they are
43// defined in for the unchecked `Store` methods. Also all functions must follow
44// the same implementation scheme to make sure they are only light wrappers:
45//
46// 1. try unwrap [stored parameter objects]
47// 2. call [unchecked method]
48// 3. rewrap [results into stored objects]
49// 4. return [stored result objects]
50impl<'b, T: Config> Store<'b, T> {
51 // `fn new_checked` is missing, because it does not interact with any stored
52 // objects.
53
54 /// This is a safe variant of [`Store::module_instantiate_unchecked`].
55 pub fn module_instantiate(
56 &mut self,
57 validation_info: &ValidationInfo<'b>,
58 extern_vals: Vec<StoredExternVal>,
59 maybe_fuel: Option<u64>,
60 ) -> Result<StoredInstantiationOutcome, RuntimeError> {
61 // 1. try unwrap
62 let extern_vals = extern_vals
63 .into_iter()
64 .map(|extern_val| extern_val.try_unwrap_into_bare(self.id))
65 .collect::<Result<Vec<ExternVal>, RuntimeError>>()?;
66 // 2. call
67 let instantiation_outcome =
68 self.module_instantiate_unchecked(validation_info, extern_vals, maybe_fuel)?;
69 // 3. rewrap
70 // SAFETY: The `InstantiationOutcome` just came from the current store.
71 let stored_instantiation_outcome =
72 unsafe { StoredInstantiationOutcome::from_bare(instantiation_outcome, self.id) };
73 // 4. return
74 Ok(stored_instantiation_outcome)
75 }
76
77 /// This is a safe variant of [`Store::instance_export_unchecked`].
78 pub fn instance_export(
79 &self,
80 module_addr: Stored<ModuleAddr>,
81 name: &str,
82 ) -> Result<StoredExternVal, RuntimeError> {
83 // 1. try unwrap
84 let module_addr = module_addr.try_unwrap_into_bare(self.id)?;
85 // 2. call
86 let extern_val = self.instance_export_unchecked(module_addr, name)?;
87 // 3. rewrap
88 // SAFETY: The `ExternVal` just came from the current store.
89 let stored_extern_val = unsafe { StoredExternVal::from_bare(extern_val, self.id) };
90 // 4. return
91 Ok(stored_extern_val)
92 }
93
94 /// This is a safer variant of [`Store::func_alloc_unchecked`]. It is
95 /// functionally equal, with the only difference being that this function
96 /// returns a [`Stored<FuncAddr>`].
97 ///
98 /// # Safety
99 ///
100 /// The caller has to guarantee that if the [`Value`]s returned from the
101 /// given host function are references, their addresses came either from the
102 /// host function arguments or from the current [`Store`] object.
103 ///
104 /// See [`Store::func_alloc`] for more information.
105 #[allow(clippy::let_and_return)] // reason = "to follow the 1234 structure"
106 pub unsafe fn func_alloc(
107 &mut self,
108 func_type: FuncType,
109 host_func: fn(&mut T, Vec<Value>) -> Result<Vec<Value>, HaltExecutionError>,
110 ) -> Stored<FuncAddr> {
111 // 1. try unwrap
112 // no stored parameters
113 // 2. call
114 // SAFETY: The caller ensures that if the host function returns
115 // references, they originate either from the arguments or the current
116 // store.
117 let func_addr = self.func_alloc_unchecked(func_type, host_func);
118 // 3. rewrap
119 // SAFETY: The function address just came from the current store.
120 let func_addr = unsafe { Stored::from_bare(func_addr, self.id) };
121 // 4. return
122 func_addr
123 }
124
125 /// This is a safe variant of [`Store::func_type_unchecked`].
126 pub fn func_type(&self, func_addr: Stored<FuncAddr>) -> Result<FuncType, RuntimeError> {
127 // 1. try unwrap
128 let func_addr = func_addr.try_unwrap_into_bare(self.id)?;
129 // 2. call
130 let func_type = self.func_type_unchecked(func_addr);
131 // 3. rewrap
132 // `FuncType` does not have a stored variant.
133 // 4. return
134 Ok(func_type)
135 }
136
137 /// This is a safe variant of [`Store::invoke_unchecked`].
138 pub fn invoke(
139 &mut self,
140 func_addr: Stored<FuncAddr>,
141 params: Vec<StoredValue>,
142 maybe_fuel: Option<u64>,
143 ) -> Result<StoredRunState, RuntimeError> {
144 // 1. try unwrap
145 let func_addr = func_addr.try_unwrap_into_bare(self.id)?;
146 let params = try_unwrap_values(params, self.id)?;
147 // 2. call
148 let run_state = self.invoke_unchecked(func_addr, params, maybe_fuel)?;
149 // 3. rewrap
150 // SAFETY: The `RunState` just came from the current store.
151 let stored_run_state = unsafe { StoredRunState::from_bare(run_state, self.id) };
152 // 4. return
153 Ok(stored_run_state)
154 }
155
156 /// This is a safe variant of [`Store::table_alloc_unchecked`].
157 pub fn table_alloc(
158 &mut self,
159 table_type: TableType,
160 r#ref: StoredRef,
161 ) -> Result<Stored<TableAddr>, RuntimeError> {
162 // 1. try unwrap
163 let r#ref = r#ref.try_unwrap_into_bare(self.id)?;
164 // 2. call
165 let table_addr = self.table_alloc_unchecked(table_type, r#ref)?;
166 // 3. rewrap
167 // SAFETY: The `TableAddr` just came from the current store.
168 let stored_table_addr = unsafe { Stored::from_bare(table_addr, self.id) };
169 // 4. return
170 Ok(stored_table_addr)
171 }
172
173 /// This is a safe variant of [`Store::table_type_unchecked`].
174 pub fn table_type(&self, table_addr: Stored<TableAddr>) -> Result<TableType, RuntimeError> {
175 // 1. try unwrap
176 let table_addr = table_addr.try_unwrap_into_bare(self.id)?;
177 // 2. call
178 let table_type = self.table_type_unchecked(table_addr);
179 // 3. rewrap
180 // `TableType` has no stored variant.
181 // 4. return
182 Ok(table_type)
183 }
184
185 /// This is a safe variant of [`Store::table_read_unchecked`].
186 pub fn table_read(
187 &self,
188 table_addr: Stored<TableAddr>,
189 i: u32,
190 ) -> Result<StoredRef, RuntimeError> {
191 // 1. try unwrap
192 let table_addr = table_addr.try_unwrap_into_bare(self.id)?;
193 // 2. call
194 let r#ref = self.table_read_unchecked(table_addr, i)?;
195 // 3. rewrap
196 // SAFETY: The `Ref` ust came from the current store.
197 let stored_ref = unsafe { StoredRef::from_bare(r#ref, self.id) };
198 // 4. return
199 Ok(stored_ref)
200 }
201
202 /// This is a safe variant of [`Store::table_write_unchecked`].
203 pub fn table_write(
204 &mut self,
205 table_addr: Stored<TableAddr>,
206 i: u32,
207 r#ref: StoredRef,
208 ) -> Result<(), RuntimeError> {
209 // 1. try unwrap
210 let table_addr = table_addr.try_unwrap_into_bare(self.id)?;
211 let r#ref = r#ref.try_unwrap_into_bare(self.id)?;
212 // 2. call
213 self.table_write_unchecked(table_addr, i, r#ref)?;
214 // 3. rewrap
215 // result is the unit type.
216 // 4. return
217 Ok(())
218 }
219
220 /// This is a safe variant of [`Store::table_size_unchecked`].
221 pub fn table_size(&self, table_addr: Stored<TableAddr>) -> Result<u32, RuntimeError> {
222 // 1. try unwrap
223 let table_addr = table_addr.try_unwrap_into_bare(self.id)?;
224 // 2. call
225 let table_size = self.table_size_unchecked(table_addr);
226 // 3. rewrap
227 // table size has no stored variant.
228 // 4. return
229 Ok(table_size)
230 }
231
232 /// This is a safe variant of [`Store::mem_alloc_unchecked`].
233 #[allow(clippy::let_and_return)] // reason = "to follow the 1234 structure"
234 pub fn mem_alloc(&mut self, mem_type: MemType) -> Stored<MemAddr> {
235 // 1. try unwrap
236 // no stored parameters
237 // 2. call
238 let mem_addr = self.mem_alloc_unchecked(mem_type);
239 // 3. rewrap
240 // SAFETY: The `MemAddr` just came from the current store.
241 let stored_mem_addr = unsafe { Stored::from_bare(mem_addr, self.id) };
242 // 4. return
243 stored_mem_addr
244 }
245
246 /// This is a safe variant of [`Store::mem_type_unchecked`].
247 pub fn mem_type(&self, mem_addr: Stored<MemAddr>) -> Result<MemType, RuntimeError> {
248 // 1. try unwrap
249 let mem_addr = mem_addr.try_unwrap_into_bare(self.id)?;
250 // 2. call
251 let mem_type = self.mem_type_unchecked(mem_addr);
252 // 3. rewrap
253 // `MemType` does not have a stored variant.
254 // 4. return
255 Ok(mem_type)
256 }
257
258 /// This is a safe variant of [`Store::mem_read_unchecked`].
259 pub fn mem_read(&self, mem_addr: Stored<MemAddr>, i: u32) -> Result<u8, RuntimeError> {
260 // 1. try unwrap
261 let mem_addr = mem_addr.try_unwrap_into_bare(self.id)?;
262 // 2. call
263 let byte = self.mem_read_unchecked(mem_addr, i)?;
264 // 3. rewrap
265 // a single byte does not have a stored variant.
266 // 4. return
267 Ok(byte)
268 }
269
270 /// This is a safe variant of [`Store::mem_write_unchecked`].
271 pub fn mem_write(
272 &mut self,
273 mem_addr: Stored<MemAddr>,
274 i: u32,
275 byte: u8,
276 ) -> Result<(), RuntimeError> {
277 // 1. try unwrap
278 let mem_addr = mem_addr.try_unwrap_into_bare(self.id)?;
279 // 2. call
280 self.mem_write_unchecked(mem_addr, i, byte)?;
281 // 3. rewrap
282 // result is the unit type.
283 // 4. return
284 Ok(())
285 }
286
287 /// This is a safe variant of [`Store::mem_size_unchecked`].
288 pub fn mem_size(&self, mem_addr: Stored<MemAddr>) -> Result<u32, RuntimeError> {
289 // 1. try unwrap
290 let mem_addr = mem_addr.try_unwrap_into_bare(self.id)?;
291 // 2. call
292 let mem_size = self.mem_size_unchecked(mem_addr);
293 // 3. rewrap
294 // mem size does not have a stored variant.
295 // 4. return
296 Ok(mem_size)
297 }
298
299 /// This is a safe variant of [`Store::mem_grow_unchecked`].
300 pub fn mem_grow(&mut self, mem_addr: Stored<MemAddr>, n: u32) -> Result<(), RuntimeError> {
301 // 1. try unwrap
302 let mem_addr = mem_addr.try_unwrap_into_bare(self.id)?;
303 // 2. call
304 self.mem_grow_unchecked(mem_addr, n)?;
305 // 3. rewrap
306 // result is the unit type.
307 // 4. return
308 Ok(())
309 }
310
311 /// This is a safe variant of [`Store::global_alloc_unchecked`].
312 pub fn global_alloc(
313 &mut self,
314 global_type: GlobalType,
315 val: StoredValue,
316 ) -> Result<Stored<GlobalAddr>, RuntimeError> {
317 // 1. try unwrap
318 let val = val.try_unwrap_into_bare(self.id)?;
319 // 2. call
320 let global_addr = self.global_alloc_unchecked(global_type, val)?;
321 // 3. rewrap
322 // SAFETY: The `GlobalAddr` just came from the current store.
323 let stored_global_addr = unsafe { Stored::from_bare(global_addr, self.id) };
324 // 4. return
325 Ok(stored_global_addr)
326 }
327
328 /// This is a safe variant of [`Store::global_type_unchecked`].
329 pub fn global_type(&self, global_addr: Stored<GlobalAddr>) -> Result<GlobalType, RuntimeError> {
330 // 1. try unwrap
331 let global_addr = global_addr.try_unwrap_into_bare(self.id)?;
332 // 2. call
333 let global_type = self.global_type_unchecked(global_addr);
334 // 3. rewrap
335 // `GlobalType` does not have a stored variant.
336 // 4. return
337 Ok(global_type)
338 }
339
340 /// This is a safe variant of [`Store::global_read_unchecked`].
341 pub fn global_read(
342 &self,
343 global_addr: Stored<GlobalAddr>,
344 ) -> Result<StoredValue, RuntimeError> {
345 // 1. try unwrap
346 let global_addr = global_addr.try_unwrap_into_bare(self.id)?;
347 // 2. call
348 let value = self.global_read_unchecked(global_addr);
349 // 3. rewrap
350 // SAFETY: The `Value` just came from the current store.
351 let stored_value = unsafe { StoredValue::from_bare(value, self.id) };
352 // 4. return
353 Ok(stored_value)
354 }
355
356 /// This is a safe variant of [`Store::global_write_unchecked`].
357 pub fn global_write(
358 &mut self,
359 global_addr: Stored<GlobalAddr>,
360 val: StoredValue,
361 ) -> Result<(), RuntimeError> {
362 // 1. try unwrap
363 let global_addr = global_addr.try_unwrap_into_bare(self.id)?;
364 let val = val.try_unwrap_into_bare(self.id)?;
365 // 2. call
366 self.global_write_unchecked(global_addr, val)?;
367 // 3. rewrap
368 // result is the unit type.
369 // 4. return
370 Ok(())
371 }
372
373 /// This is a safe variant of [`Store::create_resumable_unchecked`].
374 pub fn create_resumable(
375 &self,
376 func_addr: Stored<FuncAddr>,
377 params: Vec<StoredValue>,
378 maybe_fuel: Option<u64>,
379 ) -> Result<Stored<ResumableRef>, RuntimeError> {
380 // 1. try unwrap
381 let func_addr = func_addr.try_unwrap_into_bare(self.id)?;
382 let params = try_unwrap_values(params, self.id)?;
383 // 2. call
384 let resumable_ref = self.create_resumable_unchecked(func_addr, params, maybe_fuel)?;
385 // 3. rewrap
386 // SAFETY: The `ResumableRef` just came from the current store.
387 let stored_resumable_ref = unsafe { Stored::from_bare(resumable_ref, self.id) };
388 // 4. return
389 Ok(stored_resumable_ref)
390 }
391
392 /// This is a safe variant of [`Store::resume_unchecked`].
393 pub fn resume(
394 &mut self,
395 resumable_ref: Stored<ResumableRef>,
396 ) -> Result<StoredRunState, RuntimeError> {
397 // 1. try unwrap
398 let resumable_ref = resumable_ref.try_unwrap_into_bare(self.id)?;
399 // 2. call
400 let run_state = self.resume_unchecked(resumable_ref)?;
401 // 3. rewrap
402 // SAFETY: The `RunState` just came from the current store.
403 let stored_run_state = unsafe { StoredRunState::from_bare(run_state, self.id) };
404 // 4. return
405 Ok(stored_run_state)
406 }
407
408 /// This is a safe variant of [`Store::access_fuel_mut_unchecked`].
409 // TODO `&mut Stored<...>` seems off as a parameter type. Instead it should
410 // be `Stored<ResumableRef>`
411 pub fn access_fuel_mut<R>(
412 &mut self,
413 resumable_ref: &mut Stored<ResumableRef>,
414 f: impl FnOnce(&mut Option<u64>) -> R,
415 ) -> Result<R, RuntimeError> {
416 // 1. try unwrap
417 let resumable_ref = resumable_ref.as_mut().try_unwrap_into_bare(self.id)?;
418 // 2. call
419 let r = self.access_fuel_mut_unchecked(resumable_ref, f)?;
420 // 3. rewrap
421 // result type `R` is generic.
422 // 4. return
423 Ok(r)
424 }
425
426 /// This is a safer variant of [`Store::func_alloc_typed_unchecked`]. It is
427 /// functionally equal, with the only difference being that this function
428 /// returns a [`Stored<FuncAddr>`].
429 ///
430 /// # Safety
431 ///
432 /// The caller has to guarantee that if the [`Value`]s returned from the
433 /// given host function are references, their addresses came either from the
434 /// host function arguments or from the current [`Store`] object.
435 ///
436 /// See: [`Store::func_alloc_typed_unchecked`] for more information.
437 #[allow(clippy::let_and_return)] // reason = "to follow the 1234 structure"
438 pub fn func_alloc_typed<Params: InteropValueList, Returns: InteropValueList>(
439 &mut self,
440 host_func: fn(&mut T, Vec<Value>) -> Result<Vec<Value>, HaltExecutionError>,
441 ) -> Stored<FuncAddr> {
442 // 1. try unwrap
443 // no stored parameters
444 // 2. call
445 // SAFETY: The caller ensures that if the host function returns
446 // references, they originate either from the arguments or the current
447 // store.
448 let func_addr = self.func_alloc_typed_unchecked::<Params, Returns>(host_func);
449 // 3. rewrap
450 // SAFETY: The function address just came from the current store.
451 let func_addr = unsafe { Stored::from_bare(func_addr, self.id) };
452 // 4. return
453 func_addr
454 }
455
456 /// This is a safe variant of [`Store::invoke_without_fuel_unchecked`].
457 pub fn invoke_without_fuel(
458 &mut self,
459 func_addr: Stored<FuncAddr>,
460 params: Vec<StoredValue>,
461 ) -> Result<Vec<StoredValue>, RuntimeError> {
462 // 1. try unwrap
463 let func_addr = func_addr.try_unwrap_into_bare(self.id)?;
464 let params = try_unwrap_values(params, self.id)?;
465 // 2. call
466 let returns = self.invoke_without_fuel_unchecked(func_addr, params)?;
467 // 3. rewrap
468 // SAFETY: All `Value`s just came from the current store.
469 let returns = unsafe { wrap_vec_elements(returns, self.id) };
470 // 4. return
471 Ok(returns)
472 }
473
474 /// This is a safe variant of [`Store::invoke_typed_without_fuel_unchecked`].
475 pub fn invoke_typed_without_fuel<
476 Params: StoredInteropValueList,
477 Returns: StoredInteropValueList,
478 >(
479 &mut self,
480 function: Stored<FuncAddr>,
481 params: Params,
482 ) -> Result<Returns, RuntimeError> {
483 // 1. try unwrap
484 let function = function.try_unwrap_into_bare(self.id)?;
485 let params = try_unwrap_values(params.into_values(), self.id)?;
486 // 2. call
487 let returns = self.invoke_without_fuel_unchecked(function, params)?;
488 // 3. rewrap
489 // SAFETY: All `Value`s just came from the current store.
490 let stored_returns = unsafe { wrap_vec_elements(returns, self.id) };
491 // 4. return
492 let stored_returns = Returns::try_from_values(stored_returns.into_iter())
493 .map_err(|_| RuntimeError::FunctionInvocationSignatureMismatch)?;
494 Ok(stored_returns)
495 }
496
497 /// This is a safe variant of [`Store::mem_access_mut_slice`].
498 pub fn mem_access_mut_slice<R>(
499 &self,
500 memory: Stored<MemAddr>,
501 accessor: impl FnOnce(&mut [u8]) -> R,
502 ) -> Result<R, RuntimeError> {
503 // 1. try unwrap
504 let memory = memory.try_unwrap_into_bare(self.id)?;
505 // 2. call
506 let returns = self.mem_access_mut_slice_unchecked(memory, accessor);
507 // 3. rewrap
508 // result is generic
509 // 4. return
510 Ok(returns)
511 }
512}
513
514// All functions in this impl block must occur in the same order as they are
515// defined in for the unchecked [`Linker`] methods. Also all functions must
516// follow the same implementation scheme to make sure they are only light
517// wrappers:
518//
519// 1. get or insert the `StoreId` [of the store associated with the current `Linker`]
520// 2. try unwrap [stored parameter objects]
521// 3. call [unchecked method]
522// 4. rewrap [results into stored objects]
523// 5. return [stored result objects]
524impl Linker {
525 /// This is a safe variant of [`Linker::define_unchecked`].
526 pub fn define(
527 &mut self,
528 module_name: String,
529 name: String,
530 extern_val: StoredExternVal,
531 ) -> Result<(), RuntimeError> {
532 // 1. get or insert the `StoreId`
533 let extern_val_store_id = extern_val
534 .id()
535 .expect("this type to always contain a StoreId");
536 let linker_store_id = *self.store_id.get_or_insert(extern_val_store_id);
537 if linker_store_id != extern_val_store_id {
538 return Err(RuntimeError::StoreIdMismatch);
539 }
540 // 2. try unwrap
541 let extern_val = extern_val.try_unwrap_into_bare(linker_store_id)?;
542 // 3. call
543 self.define_unchecked(module_name, name, extern_val)?;
544 // 4. rewrap
545 // result is the unit type.
546 // 5. return
547 Ok(())
548 }
549
550 /// This is a safe variant of [`Linker::define_module_instance_unchecked`].
551 pub fn define_module_instance<T: Config>(
552 &mut self,
553 store: &Store<T>,
554 module_name: String,
555 module: Stored<ModuleAddr>,
556 ) -> Result<(), RuntimeError> {
557 // 1. get or insert the `StoreId`
558 let module_store_id = module.id().expect("this type to always contain a StoreId");
559 let linker_store_id = *self.store_id.get_or_insert(module_store_id);
560 if linker_store_id != module_store_id {
561 return Err(RuntimeError::StoreIdMismatch);
562 }
563 // 2. try unwrap
564 let module = module.try_unwrap_into_bare(linker_store_id)?;
565 // 3. call
566 self.define_module_instance_unchecked(store, module_name, module)?;
567 // 4. rewrap
568 // result is the unit type.
569 // 5. return
570 Ok(())
571 }
572
573 /// This is a safe variant of [`Linker::get_unchecked`].
574 ///
575 /// # Interaction with unchecked API
576 ///
577 /// This method is able to find externs defined through the unchecked
578 /// `define` methods. However, for this to work, at least one of the
579 /// following methods must have been called successfully:
580 /// [`Linker::define`], [`Linker::define_module_instance`],
581 /// [`Linker::module_instantiate`]. Otherwise, this method may spuriously
582 /// return an error.
583 ///
584 /// Therefore, it is advised against to mix the unchecked and checked API
585 /// for a single [`Linker`] instance.
586 ///
587 /// # Errors
588 ///
589 /// - [`RuntimeError::LinkerNotYetAssociatedWithStoreId`]
590 /// - [`RuntimeError::UnableToResolveExternLookup`]
591 pub fn get(&self, module_name: String, name: String) -> Result<StoredExternVal, RuntimeError> {
592 // 1. get or insert the `StoreId`
593 // TODO docs are not consistent
594 let Some(linker_store_id) = self.store_id else {
595 // At this point we have no way to set the current store id, because
596 // the parameters are all non-stored types.
597
598 // We also know that nothing was defined in this linker context through
599 // the checked methods yet, because `self.store_id` has not been set
600 // yet. Therefore, a get would always return `None`.
601
602 // However, when an unchecked `define` method was used before, we
603 // also have to return `None` here, because even if the lookup for
604 // `module_name` and `name` returns something, we cannot attach a
605 // store id to it.
606
607 return Err(RuntimeError::LinkerNotYetAssociatedWithStoreId);
608 };
609 // 2. try unwrap
610 // no stored parameters
611 // 3. call
612 let extern_val = self
613 .get_unchecked(module_name, name)
614 .ok_or(RuntimeError::UnableToResolveExternLookup)?;
615 // 4. rewrap
616 // SAFETY: The `ExternVal` just came from the current `Linker`. Because
617 // a `Linker` can always be used with only one unique `Store`, this
618 // `ExternVal` must be from the current Linker's store.
619 let stored_extern_val = unsafe { StoredExternVal::from_bare(extern_val, linker_store_id) };
620 // 5. return
621 Ok(stored_extern_val)
622 }
623
624 /// This is a safe variant of [`Linker::instantiate_pre_unchecked`].
625 ///
626 /// # Interaction with unchecked API
627 ///
628 /// See [`Linker::get`]
629 ///
630 /// # Errors
631 ///
632 /// - [`RuntimeError::LinkerNotYetAssociatedWithStoreId`]
633 /// - [`RuntimeError::UnableToResolveExternLookup`]
634 pub fn instantiate_pre(
635 &self,
636 validation_info: &ValidationInfo,
637 ) -> Result<Vec<StoredExternVal>, RuntimeError> {
638 // Special case: If the module has no imports, we don't perform any
639 // linking. We need this special case, so that a `Linker`, that has not
640 // yet been associated with some `Store`, can still be used to
641 // pre-instantiate modules.
642 if validation_info.imports.is_empty() {
643 return Ok(Vec::new());
644 }
645 // 1. get or insert `StoreId`
646 let Some(linker_store_id) = self.store_id else {
647 // We are not able to perform safe linking (see this method's and
648 // `Linker::get`'s documentations).
649 return Err(RuntimeError::LinkerNotYetAssociatedWithStoreId);
650 };
651 // 2. try unwrap
652 // no stored parameters
653 // 3. call
654 let extern_vals = self.instantiate_pre_unchecked(validation_info)?;
655 // 4. rewrap
656 // SAFETY: All `ExternVal`s just came from the current `Linker`. Because
657 // a Linker can always be used with only one unique `Store`, all
658 // `ExternVal`s must be from the current Linker's store.
659 let stored_extern_vals = unsafe { wrap_vec_elements(extern_vals, linker_store_id) };
660 // 5. retur
661 Ok(stored_extern_vals)
662 }
663
664 /// This is a safe variant of [`Linker::module_instantiate_unchecked`].
665 pub fn module_instantiate<'b, T: Config>(
666 &mut self,
667 store: &mut Store<'b, T>,
668 validation_info: &ValidationInfo<'b>,
669 maybe_fuel: Option<u64>,
670 ) -> Result<StoredInstantiationOutcome, RuntimeError> {
671 // 1. get or insert `StoreId`
672 let linker_store_id = *self.store_id.get_or_insert(store.id);
673 if linker_store_id != store.id {
674 return Err(RuntimeError::StoreIdMismatch);
675 }
676 // 2. try unwrap
677 // no stored parameters
678 // 3. call
679 let instantiation_outcome =
680 self.module_instantiate_unchecked(store, validation_info, maybe_fuel)?;
681 // 4. rewrap
682 // SAFETY: The `InstantiationOutcome` just came from the current
683 // `Linker`. Because a linker can always be used with only one unique
684 // `Store`, the `InstantiationOutcome` must be from the current Linker's
685 // store.
686 let stored_instantiation_outcome = unsafe {
687 StoredInstantiationOutcome::from_bare(instantiation_outcome, linker_store_id)
688 };
689 // 5. return
690 Ok(stored_instantiation_outcome)
691 }
692}
693
694/// A trait for types that might have a [`StoreId`] attached to them, so-called
695/// _stored_ types.
696trait AbstractStored: Sized {
697 type BareTy: Sized;
698
699 /// Creates a new stored object
700 ///
701 /// # Safety
702 /// The caller has to guarantee that the bare value comes from a [`Store`]
703 /// with the given [`StoreId`].
704 unsafe fn from_bare(bare_value: Self::BareTy, id: StoreId) -> Self;
705
706 /// Gets the id of this stored object
707 ///
708 /// Not all stored objects require to have an id attached to them.
709 fn id(&self) -> Option<StoreId>;
710
711 /// Converts this stored object into its bare form that does not have any [`StoreId`] attached to it.
712 fn into_bare(self) -> Self::BareTy;
713
714 /// Checks if this stored object comes from a specific store by its
715 /// [`StoreId`]. If true, it converts self into its bare form, otherwise an
716 /// error is returned.
717 ///
718 /// # Errors
719 /// - [`RuntimeError::StoreIdMismatch`]
720 fn try_unwrap_into_bare(
721 self,
722 expected_store_id: StoreId,
723 ) -> Result<Self::BareTy, RuntimeError> {
724 if let Some(id) = self.id() {
725 if id != expected_store_id {
726 return Err(RuntimeError::StoreIdMismatch);
727 }
728 }
729
730 Ok(self.into_bare())
731 }
732}
733
734/// A generic stored wrapper. This is used to wrap `struct` types such as
735/// [`FuncAddr`], [`MemAddr`], etc.
736pub struct Stored<T> {
737 id: StoreId,
738 inner: T,
739}
740
741impl<T> AbstractStored for Stored<T> {
742 type BareTy = T;
743
744 unsafe fn from_bare(bare_value: Self::BareTy, id: StoreId) -> Self {
745 Self {
746 inner: bare_value,
747 id,
748 }
749 }
750
751 fn id(&self) -> Option<StoreId> {
752 Some(self.id)
753 }
754
755 fn into_bare(self) -> Self::BareTy {
756 self.inner
757 }
758}
759
760impl<T: Clone> Clone for Stored<T> {
761 fn clone(&self) -> Self {
762 Self {
763 id: self.id,
764 inner: self.inner.clone(),
765 }
766 }
767}
768
769impl<T: Copy> Copy for Stored<T> {}
770
771impl<T: core::fmt::Debug> core::fmt::Debug for Stored<T> {
772 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
773 f.debug_struct("Stored")
774 .field("inner", &self)
775 .field("id", &self.id)
776 .finish()
777 }
778}
779
780impl<T: PartialEq> PartialEq for Stored<T> {
781 fn eq(&self, other: &Self) -> bool {
782 self.id.eq(&other.id) && self.inner.eq(&other.inner)
783 }
784}
785
786impl<T: Eq> Eq for Stored<T> {}
787
788impl<T> Stored<T> {
789 // TODO remove this after the `ResumableRef` rework. Currently
790 // `ResumableRef` can store data, however it should merely be an addr type
791 // into the store in the future.
792 fn as_mut(&mut self) -> Stored<&mut T> {
793 Stored {
794 id: self.id,
795 inner: &mut self.inner,
796 }
797 }
798}
799
800/// A stored variant of [`ExternVal`]
801#[derive(Debug, Copy, Clone, PartialEq, Eq)]
802pub enum StoredExternVal {
803 Func(Stored<FuncAddr>),
804 Table(Stored<TableAddr>),
805 Mem(Stored<MemAddr>),
806 Global(Stored<GlobalAddr>),
807}
808
809impl AbstractStored for StoredExternVal {
810 type BareTy = ExternVal;
811
812 unsafe fn from_bare(bare_value: Self::BareTy, id: StoreId) -> Self {
813 match bare_value {
814 ExternVal::Func(func_addr) => {
815 // SAFETY: Upheld by the caller
816 let stored_func_addr = unsafe { Stored::from_bare(func_addr, id) };
817 Self::Func(stored_func_addr)
818 }
819 ExternVal::Table(table_addr) => {
820 // SAFETY: Upheld by the caller
821 let stored_table_addr = unsafe { Stored::from_bare(table_addr, id) };
822 Self::Table(stored_table_addr)
823 }
824 ExternVal::Mem(mem_addr) => {
825 // SAFETY: Upheld by the caller
826 let stored_mem_addr = unsafe { Stored::from_bare(mem_addr, id) };
827 Self::Mem(stored_mem_addr)
828 }
829 ExternVal::Global(global_addr) => {
830 // SAFETY: Upheld by the caller
831 let stored_global_addr = unsafe { Stored::from_bare(global_addr, id) };
832 Self::Global(stored_global_addr)
833 }
834 }
835 }
836
837 fn id(&self) -> Option<StoreId> {
838 match self {
839 StoredExternVal::Func(stored_func_addr) => stored_func_addr.id(),
840 StoredExternVal::Table(stored_table_addr) => stored_table_addr.id(),
841 StoredExternVal::Mem(stored_mem_addr) => stored_mem_addr.id(),
842 StoredExternVal::Global(stored_global_addr) => stored_global_addr.id(),
843 }
844 }
845
846 fn into_bare(self) -> Self::BareTy {
847 match self {
848 StoredExternVal::Func(stored_func_addr) => {
849 ExternVal::Func(stored_func_addr.into_bare())
850 }
851 StoredExternVal::Table(stored_table_addr) => {
852 ExternVal::Table(stored_table_addr.into_bare())
853 }
854 StoredExternVal::Mem(stored_mem_addr) => ExternVal::Mem(stored_mem_addr.into_bare()),
855 StoredExternVal::Global(stored_global_addr) => {
856 ExternVal::Global(stored_global_addr.into_bare())
857 }
858 }
859 }
860}
861
862impl StoredExternVal {
863 pub fn as_func(self) -> Option<Stored<FuncAddr>> {
864 match self {
865 StoredExternVal::Func(func_addr) => Some(func_addr),
866 _ => None,
867 }
868 }
869
870 pub fn as_table(self) -> Option<Stored<TableAddr>> {
871 match self {
872 StoredExternVal::Table(table_addr) => Some(table_addr),
873 _ => None,
874 }
875 }
876
877 pub fn as_mem(self) -> Option<Stored<MemAddr>> {
878 match self {
879 StoredExternVal::Mem(mem_addr) => Some(mem_addr),
880 _ => None,
881 }
882 }
883
884 pub fn as_global(self) -> Option<Stored<GlobalAddr>> {
885 match self {
886 StoredExternVal::Global(global_addr) => Some(global_addr),
887 _ => None,
888 }
889 }
890}
891
892/// A stored variant of [`RunState`]
893pub enum StoredRunState {
894 Finished {
895 values: Vec<StoredValue>,
896 maybe_remaining_fuel: Option<u64>,
897 },
898 Resumable {
899 resumable_ref: Stored<ResumableRef>,
900 required_fuel: NonZeroU64,
901 },
902}
903
904impl AbstractStored for StoredRunState {
905 type BareTy = RunState;
906
907 unsafe fn from_bare(bare_value: Self::BareTy, id: StoreId) -> Self {
908 match bare_value {
909 RunState::Finished {
910 values,
911 maybe_remaining_fuel,
912 } => Self::Finished {
913 // SAFETY: Upheld by the caller
914 values: unsafe { wrap_vec_elements(values, id) },
915 maybe_remaining_fuel,
916 },
917 RunState::Resumable {
918 resumable_ref,
919 required_fuel,
920 } => Self::Resumable {
921 // SAFETY: Upheld by the caller
922 resumable_ref: unsafe { Stored::from_bare(resumable_ref, id) },
923 required_fuel,
924 },
925 }
926 }
927
928 fn id(&self) -> Option<StoreId> {
929 todo!()
930 }
931
932 fn into_bare(self) -> Self::BareTy {
933 todo!()
934 }
935}
936
937/// A stored variant of [`InstantiationOutcome`]
938pub struct StoredInstantiationOutcome {
939 pub module_addr: Stored<ModuleAddr>,
940 pub maybe_remaining_fuel: Option<u64>,
941}
942
943impl AbstractStored for StoredInstantiationOutcome {
944 type BareTy = InstantiationOutcome;
945
946 unsafe fn from_bare(bare_value: Self::BareTy, id: StoreId) -> Self {
947 Self {
948 // SAFETY: Upheld by the caller
949 module_addr: unsafe { Stored::from_bare(bare_value.module_addr, id) },
950 maybe_remaining_fuel: bare_value.maybe_remaining_fuel,
951 }
952 }
953
954 fn id(&self) -> Option<StoreId> {
955 self.module_addr.id()
956 }
957
958 fn into_bare(self) -> Self::BareTy {
959 InstantiationOutcome {
960 module_addr: self.module_addr.into_bare(),
961 maybe_remaining_fuel: self.maybe_remaining_fuel,
962 }
963 }
964}
965
966/// Helper method for associating every element in a [`Vec`] with a [`StoreId`].
967///
968/// # Safety
969///
970/// It must be guaranteed that all given elements come from the [`Store`] with
971/// the given [`StoreId`].
972unsafe fn wrap_vec_elements<S: AbstractStored>(values: Vec<S::BareTy>, id: StoreId) -> Vec<S> {
973 values
974 .into_iter()
975 .map(|value| {
976 // SAFETY: The caller guarantees that all values in this Vec come
977 // from the store with given id. Therefore, this is also true for
978 // this specific `Value`.
979 unsafe { S::from_bare(value, id) }
980 })
981 .collect()
982}
983
984/// Helper method for checking if all [`Value`]s in a slice have the given
985/// [`StoreId`] and then, if the check was true, converting them to a
986/// [`Vec<Value>`].
987///
988/// # Errors
989/// - [`RuntimeError::StoreIdMismatch`]
990fn try_unwrap_values<S: AbstractStored>(
991 stored_values: Vec<S>,
992 expected_store_id: StoreId,
993) -> Result<Vec<S::BareTy>, RuntimeError> {
994 stored_values
995 .into_iter()
996 .map(|stored_value| stored_value.try_unwrap_into_bare(expected_store_id))
997 .collect()
998}