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 variant of [`Store::mem_alloc_unchecked`] that returns a
233    /// stored object.
234    #[allow(clippy::let_and_return)] // reason = "to follow the 1234 structure"
235    pub fn mem_alloc(&mut self, mem_type: MemType) -> Stored<MemAddr> {
236        // 1. try unwrap
237        // no stored parameters
238        // 2. call
239        let mem_addr = self.mem_alloc_unchecked(mem_type);
240        // 3. rewrap
241        // SAFETY: The `MemAddr` just came from the current store.
242        let stored_mem_addr = unsafe { Stored::from_bare(mem_addr, self.id) };
243        // 4. return
244        stored_mem_addr
245    }
246
247    /// This is a safe variant of [`Store::mem_type_unchecked`].
248    pub fn mem_type(&self, mem_addr: Stored<MemAddr>) -> Result<MemType, RuntimeError> {
249        // 1. try unwrap
250        let mem_addr = mem_addr.try_unwrap_into_bare(self.id)?;
251        // 2. call
252        let mem_type = self.mem_type_unchecked(mem_addr);
253        // 3. rewrap
254        // `MemType` does not have a stored variant.
255        // 4. return
256        Ok(mem_type)
257    }
258
259    /// This is a safe variant of [`Store::mem_read_unchecked`].
260    pub fn mem_read(&self, mem_addr: Stored<MemAddr>, i: u32) -> Result<u8, RuntimeError> {
261        // 1. try unwrap
262        let mem_addr = mem_addr.try_unwrap_into_bare(self.id)?;
263        // 2. call
264        let byte = self.mem_read_unchecked(mem_addr, i)?;
265        // 3. rewrap
266        // a single byte does not have a stored variant.
267        // 4. return
268        Ok(byte)
269    }
270
271    /// This is a safe variant of [`Store::mem_write_unchecked`].
272    pub fn mem_write(
273        &mut self,
274        mem_addr: Stored<MemAddr>,
275        i: u32,
276        byte: u8,
277    ) -> Result<(), RuntimeError> {
278        // 1. try unwrap
279        let mem_addr = mem_addr.try_unwrap_into_bare(self.id)?;
280        // 2. call
281        self.mem_write_unchecked(mem_addr, i, byte)?;
282        // 3. rewrap
283        // result is the unit type.
284        // 4. return
285        Ok(())
286    }
287
288    /// This is a safe variant of [`Store::mem_size_unchecked`].
289    pub fn mem_size(&self, mem_addr: Stored<MemAddr>) -> Result<u32, RuntimeError> {
290        // 1. try unwrap
291        let mem_addr = mem_addr.try_unwrap_into_bare(self.id)?;
292        // 2. call
293        let mem_size = self.mem_size_unchecked(mem_addr);
294        // 3. rewrap
295        // mem size does not have a stored variant.
296        // 4. return
297        Ok(mem_size)
298    }
299
300    /// This is a safe variant of [`Store::mem_grow_unchecked`].
301    pub fn mem_grow(&mut self, mem_addr: Stored<MemAddr>, n: u32) -> Result<(), RuntimeError> {
302        // 1. try unwrap
303        let mem_addr = mem_addr.try_unwrap_into_bare(self.id)?;
304        // 2. call
305        self.mem_grow_unchecked(mem_addr, n)?;
306        // 3. rewrap
307        // result is the unit type.
308        // 4. return
309        Ok(())
310    }
311
312    /// This is a safe variant of [`Store::global_alloc_unchecked`].
313    pub fn global_alloc(
314        &mut self,
315        global_type: GlobalType,
316        val: StoredValue,
317    ) -> Result<Stored<GlobalAddr>, RuntimeError> {
318        // 1. try unwrap
319        let val = val.try_unwrap_into_bare(self.id)?;
320        // 2. call
321        let global_addr = self.global_alloc_unchecked(global_type, val)?;
322        // 3. rewrap
323        // SAFETY: The `GlobalAddr` just came from the current store.
324        let stored_global_addr = unsafe { Stored::from_bare(global_addr, self.id) };
325        // 4. return
326        Ok(stored_global_addr)
327    }
328
329    /// This is a safe variant of [`Store::global_type_unchecked`].
330    pub fn global_type(&self, global_addr: Stored<GlobalAddr>) -> Result<GlobalType, RuntimeError> {
331        // 1. try unwrap
332        let global_addr = global_addr.try_unwrap_into_bare(self.id)?;
333        // 2. call
334        let global_type = self.global_type_unchecked(global_addr);
335        // 3. rewrap
336        // `GlobalType` does not have a stored variant.
337        // 4. return
338        Ok(global_type)
339    }
340
341    /// This is a safe variant of [`Store::global_read_unchecked`].
342    pub fn global_read(
343        &self,
344        global_addr: Stored<GlobalAddr>,
345    ) -> Result<StoredValue, RuntimeError> {
346        // 1. try unwrap
347        let global_addr = global_addr.try_unwrap_into_bare(self.id)?;
348        // 2. call
349        let value = self.global_read_unchecked(global_addr);
350        // 3. rewrap
351        // SAFETY: The `Value` just came from the current store.
352        let stored_value = unsafe { StoredValue::from_bare(value, self.id) };
353        // 4. return
354        Ok(stored_value)
355    }
356
357    /// This is a safe variant of [`Store::global_write_unchecked`].
358    pub fn global_write(
359        &mut self,
360        global_addr: Stored<GlobalAddr>,
361        val: StoredValue,
362    ) -> Result<(), RuntimeError> {
363        // 1. try unwrap
364        let global_addr = global_addr.try_unwrap_into_bare(self.id)?;
365        let val = val.try_unwrap_into_bare(self.id)?;
366        // 2. call
367        self.global_write_unchecked(global_addr, val)?;
368        // 3. rewrap
369        // result is the unit type.
370        // 4. return
371        Ok(())
372    }
373
374    /// This is a safe variant of [`Store::create_resumable_unchecked`].
375    pub fn create_resumable(
376        &self,
377        func_addr: Stored<FuncAddr>,
378        params: Vec<StoredValue>,
379        maybe_fuel: Option<u64>,
380    ) -> Result<Stored<ResumableRef>, RuntimeError> {
381        // 1. try unwrap
382        let func_addr = func_addr.try_unwrap_into_bare(self.id)?;
383        let params = try_unwrap_values(params, self.id)?;
384        // 2. call
385        let resumable_ref = self.create_resumable_unchecked(func_addr, params, maybe_fuel)?;
386        // 3. rewrap
387        // SAFETY: The `ResumableRef` just came from the current store.
388        let stored_resumable_ref = unsafe { Stored::from_bare(resumable_ref, self.id) };
389        // 4. return
390        Ok(stored_resumable_ref)
391    }
392
393    /// This is a safe variant of [`Store::resume_unchecked`].
394    pub fn resume(
395        &mut self,
396        resumable_ref: Stored<ResumableRef>,
397    ) -> Result<StoredRunState, RuntimeError> {
398        // 1. try unwrap
399        let resumable_ref = resumable_ref.try_unwrap_into_bare(self.id)?;
400        // 2. call
401        let run_state = self.resume_unchecked(resumable_ref)?;
402        // 3. rewrap
403        // SAFETY: The `RunState` just came from the current store.
404        let stored_run_state = unsafe { StoredRunState::from_bare(run_state, self.id) };
405        // 4. return
406        Ok(stored_run_state)
407    }
408
409    /// This is a safe variant of [`Store::access_fuel_mut_unchecked`].
410    // TODO `&mut Stored<...>` seems off as a parameter type. Instead it should
411    // be `Stored<ResumableRef>`
412    pub fn access_fuel_mut<R>(
413        &mut self,
414        resumable_ref: &mut Stored<ResumableRef>,
415        f: impl FnOnce(&mut Option<u64>) -> R,
416    ) -> Result<R, RuntimeError> {
417        // 1. try unwrap
418        let resumable_ref = resumable_ref.as_mut().try_unwrap_into_bare(self.id)?;
419        // 2. call
420        let r = self.access_fuel_mut_unchecked(resumable_ref, f)?;
421        // 3. rewrap
422        // result type `R` is generic.
423        // 4. return
424        Ok(r)
425    }
426
427    /// This is a safer variant of [`Store::func_alloc_typed_unchecked`]. It is
428    /// functionally equal, with the only difference being that this function
429    /// returns a [`Stored<FuncAddr>`].
430    ///
431    /// # Safety
432    ///
433    /// The caller has to guarantee that if the [`Value`]s returned from the
434    /// given host function are references, their addresses came either from the
435    /// host function arguments or from the current [`Store`] object.
436    ///
437    /// See: [`Store::func_alloc_typed_unchecked`] for more information.
438    #[allow(clippy::let_and_return)] // reason = "to follow the 1234 structure"
439    pub fn func_alloc_typed<Params: InteropValueList, Returns: InteropValueList>(
440        &mut self,
441        host_func: fn(&mut T, Vec<Value>) -> Result<Vec<Value>, HaltExecutionError>,
442    ) -> Stored<FuncAddr> {
443        // 1. try unwrap
444        // no stored parameters
445        // 2. call
446        // SAFETY: The caller ensures that if the host function returns
447        // references, they originate either from the arguments or the current
448        // store.
449        let func_addr = self.func_alloc_typed_unchecked::<Params, Returns>(host_func);
450        // 3. rewrap
451        // SAFETY: The function address just came from the current store.
452        let func_addr = unsafe { Stored::from_bare(func_addr, self.id) };
453        // 4. return
454        func_addr
455    }
456
457    /// This is a safe variant of [`Store::invoke_without_fuel_unchecked`].
458    pub fn invoke_without_fuel(
459        &mut self,
460        func_addr: Stored<FuncAddr>,
461        params: Vec<StoredValue>,
462    ) -> Result<Vec<StoredValue>, RuntimeError> {
463        // 1. try unwrap
464        let func_addr = func_addr.try_unwrap_into_bare(self.id)?;
465        let params = try_unwrap_values(params, self.id)?;
466        // 2. call
467        let returns = self.invoke_without_fuel_unchecked(func_addr, params)?;
468        // 3. rewrap
469        // SAFETY: All `Value`s just came from the current store.
470        let returns = unsafe { wrap_vec_elements(returns, self.id) };
471        // 4. return
472        Ok(returns)
473    }
474
475    /// This is a safe variant of [`Store::invoke_typed_without_fuel_unchecked`].
476    pub fn invoke_typed_without_fuel<
477        Params: StoredInteropValueList,
478        Returns: StoredInteropValueList,
479    >(
480        &mut self,
481        function: Stored<FuncAddr>,
482        params: Params,
483    ) -> Result<Returns, RuntimeError> {
484        // 1. try unwrap
485        let function = function.try_unwrap_into_bare(self.id)?;
486        let params = try_unwrap_values(params.into_values(), self.id)?;
487        // 2. call
488        let returns = self.invoke_without_fuel_unchecked(function, params)?;
489        // 3. rewrap
490        // SAFETY: All `Value`s just came from the current store.
491        let stored_returns = unsafe { wrap_vec_elements(returns, self.id) };
492        // 4. return
493        let stored_returns = Returns::try_from_values(stored_returns.into_iter())
494            .map_err(|_| RuntimeError::FunctionInvocationSignatureMismatch)?;
495        Ok(stored_returns)
496    }
497
498    /// This is a safe variant of [`Store::mem_access_mut_slice`].
499    pub fn mem_access_mut_slice<R>(
500        &self,
501        memory: Stored<MemAddr>,
502        accessor: impl FnOnce(&mut [u8]) -> R,
503    ) -> Result<R, RuntimeError> {
504        // 1. try unwrap
505        let memory = memory.try_unwrap_into_bare(self.id)?;
506        // 2. call
507        let returns = self.mem_access_mut_slice_unchecked(memory, accessor);
508        // 3. rewrap
509        // result is generic
510        // 4. return
511        Ok(returns)
512    }
513
514    /// This is a safe variant of [`Store::drop_resumable_unchecked`]
515    pub fn drop_resumable(
516        &mut self,
517        resumable_ref: Stored<ResumableRef>,
518    ) -> Result<(), RuntimeError> {
519        // 1. try unwrap
520        let resumable_ref = resumable_ref.try_unwrap_into_bare(self.id)?;
521        // 2. call
522        // SAFETY: It was just checked that this resumable ref comes from the
523        // current store.
524        unsafe { self.drop_resumable_unchecked(resumable_ref) };
525        // 3. rewrap
526        // result is unit type
527        // 4. return
528        Ok(())
529    }
530}
531
532// All functions in this impl block must occur in the same order as they are
533// defined in for the unchecked [`Linker`] methods. Also all functions must
534// follow the same implementation scheme to make sure they are only light
535// wrappers:
536//
537// 1. get or insert the `StoreId` [of the store associated with the current `Linker`]
538// 2. try unwrap [stored parameter objects]
539// 3. call [unchecked method]
540// 4. rewrap [results into stored objects]
541// 5. return [stored result objects]
542impl Linker {
543    /// This is a safe variant of [`Linker::define_unchecked`].
544    pub fn define(
545        &mut self,
546        module_name: String,
547        name: String,
548        extern_val: StoredExternVal,
549    ) -> Result<(), RuntimeError> {
550        // 1. get or insert the `StoreId`
551        let extern_val_store_id = extern_val
552            .id()
553            .expect("this type to always contain a StoreId");
554        let linker_store_id = *self.store_id.get_or_insert(extern_val_store_id);
555        if linker_store_id != extern_val_store_id {
556            return Err(RuntimeError::StoreIdMismatch);
557        }
558        // 2. try unwrap
559        let extern_val = extern_val.try_unwrap_into_bare(linker_store_id)?;
560        // 3. call
561        self.define_unchecked(module_name, name, extern_val)?;
562        // 4. rewrap
563        // result is the unit type.
564        // 5. return
565        Ok(())
566    }
567
568    /// This is a safe variant of [`Linker::define_module_instance_unchecked`].
569    pub fn define_module_instance<T: Config>(
570        &mut self,
571        store: &Store<T>,
572        module_name: String,
573        module: Stored<ModuleAddr>,
574    ) -> Result<(), RuntimeError> {
575        // 1. get or insert the `StoreId`
576        let module_store_id = module.id().expect("this type to always contain a StoreId");
577        let linker_store_id = *self.store_id.get_or_insert(module_store_id);
578        if linker_store_id != module_store_id {
579            return Err(RuntimeError::StoreIdMismatch);
580        }
581        // 2. try unwrap
582        let module = module.try_unwrap_into_bare(linker_store_id)?;
583        // 3. call
584        self.define_module_instance_unchecked(store, module_name, module)?;
585        // 4. rewrap
586        // result is the unit type.
587        // 5. return
588        Ok(())
589    }
590
591    /// This is a safe variant of [`Linker::get_unchecked`].
592    ///
593    /// # Interaction with unchecked API
594    ///
595    /// This method is able to find externs defined through the unchecked
596    /// `define` methods.  However, for this to work, at least one of the
597    /// following methods must have been called successfully:
598    /// [`Linker::define`], [`Linker::define_module_instance`],
599    /// [`Linker::module_instantiate`]. Otherwise, this method may spuriously
600    /// return an error.
601    ///
602    /// Therefore, it is advised against to mix the unchecked and checked API
603    /// for a single [`Linker`] instance.
604    ///
605    /// # Errors
606    ///
607    /// - [`RuntimeError::LinkerNotYetAssociatedWithStoreId`]
608    /// - [`RuntimeError::UnableToResolveExternLookup`]
609    pub fn get(&self, module_name: String, name: String) -> Result<StoredExternVal, RuntimeError> {
610        // 1. get or insert the `StoreId`
611        // TODO docs are not consistent
612        let Some(linker_store_id) = self.store_id else {
613            // At this point we have no way to set the current store id, because
614            // the parameters are all non-stored types.
615
616            // We also know that nothing was defined in this linker context through
617            // the checked methods yet, because `self.store_id` has not been set
618            // yet. Therefore, a get would always return `None`.
619
620            // However, when an unchecked `define` method was used before, we
621            // also have to return `None` here, because even if the lookup for
622            // `module_name` and `name` returns something, we cannot attach a
623            // store id to it.
624
625            return Err(RuntimeError::LinkerNotYetAssociatedWithStoreId);
626        };
627        // 2. try unwrap
628        // no stored parameters
629        // 3. call
630        let extern_val = self
631            .get_unchecked(module_name, name)
632            .ok_or(RuntimeError::UnableToResolveExternLookup)?;
633        // 4. rewrap
634        // SAFETY: The `ExternVal` just came from the current `Linker`. Because
635        // a `Linker` can always be used with only one unique `Store`, this
636        // `ExternVal` must be from the current Linker's store.
637        let stored_extern_val = unsafe { StoredExternVal::from_bare(extern_val, linker_store_id) };
638        // 5. return
639        Ok(stored_extern_val)
640    }
641
642    /// This is a safe variant of [`Linker::instantiate_pre_unchecked`].
643    ///
644    /// # Interaction with unchecked API
645    ///
646    /// See [`Linker::get`]
647    ///
648    /// # Errors
649    ///
650    /// - [`RuntimeError::LinkerNotYetAssociatedWithStoreId`]
651    /// - [`RuntimeError::UnableToResolveExternLookup`]
652    pub fn instantiate_pre(
653        &self,
654        validation_info: &ValidationInfo,
655    ) -> Result<Vec<StoredExternVal>, RuntimeError> {
656        // Special case: If the module has no imports, we don't perform any
657        // linking. We need this special case, so that a `Linker`, that has not
658        // yet been associated with some `Store`, can still be used to
659        // pre-instantiate modules.
660        if validation_info.imports.is_empty() {
661            return Ok(Vec::new());
662        }
663        // 1. get or insert `StoreId`
664        let Some(linker_store_id) = self.store_id else {
665            // We are not able to perform safe linking (see this method's and
666            // `Linker::get`'s documentations).
667            return Err(RuntimeError::LinkerNotYetAssociatedWithStoreId);
668        };
669        // 2. try unwrap
670        // no stored parameters
671        // 3. call
672        let extern_vals = self.instantiate_pre_unchecked(validation_info)?;
673        // 4. rewrap
674        // SAFETY: All `ExternVal`s just came from the current `Linker`. Because
675        // a Linker can always be used with only one unique `Store`, all
676        // `ExternVal`s must be from the current Linker's store.
677        let stored_extern_vals = unsafe { wrap_vec_elements(extern_vals, linker_store_id) };
678        // 5. retur
679        Ok(stored_extern_vals)
680    }
681
682    /// This is a safe variant of [`Linker::module_instantiate_unchecked`].
683    pub fn module_instantiate<'b, T: Config>(
684        &mut self,
685        store: &mut Store<'b, T>,
686        validation_info: &ValidationInfo<'b>,
687        maybe_fuel: Option<u64>,
688    ) -> Result<StoredInstantiationOutcome, RuntimeError> {
689        // 1. get or insert `StoreId`
690        let linker_store_id = *self.store_id.get_or_insert(store.id);
691        if linker_store_id != store.id {
692            return Err(RuntimeError::StoreIdMismatch);
693        }
694        // 2. try unwrap
695        // no stored parameters
696        // 3. call
697        let instantiation_outcome =
698            self.module_instantiate_unchecked(store, validation_info, maybe_fuel)?;
699        // 4. rewrap
700        // SAFETY: The `InstantiationOutcome` just came from the current
701        // `Linker`. Because a linker can always be used with only one unique
702        // `Store`, the `InstantiationOutcome` must be from the current Linker's
703        // store.
704        let stored_instantiation_outcome = unsafe {
705            StoredInstantiationOutcome::from_bare(instantiation_outcome, linker_store_id)
706        };
707        // 5. return
708        Ok(stored_instantiation_outcome)
709    }
710}
711
712/// A trait for types that might have a [`StoreId`] attached to them, so-called
713/// _stored_ types.
714trait AbstractStored: Sized {
715    type BareTy: Sized;
716
717    /// Creates a new stored object
718    ///
719    /// # Safety
720    /// The caller has to guarantee that the bare value comes from a [`Store`]
721    /// with the given [`StoreId`].
722    unsafe fn from_bare(bare_value: Self::BareTy, id: StoreId) -> Self;
723
724    /// Gets the id of this stored object
725    ///
726    /// Not all stored objects require to have an id attached to them.
727    fn id(&self) -> Option<StoreId>;
728
729    /// Converts this stored object into its bare form that does not have any [`StoreId`] attached to it.
730    fn into_bare(self) -> Self::BareTy;
731
732    /// Checks if this stored object comes from a specific store by its
733    /// [`StoreId`]. If true, it converts self into its bare form, otherwise an
734    /// error is returned.
735    ///
736    /// # Errors
737    /// - [`RuntimeError::StoreIdMismatch`]
738    fn try_unwrap_into_bare(
739        self,
740        expected_store_id: StoreId,
741    ) -> Result<Self::BareTy, RuntimeError> {
742        if let Some(id) = self.id() {
743            if id != expected_store_id {
744                return Err(RuntimeError::StoreIdMismatch);
745            }
746        }
747
748        Ok(self.into_bare())
749    }
750}
751
752/// A generic stored wrapper. This is used to wrap `struct` types such as
753/// [`FuncAddr`], [`MemAddr`], etc.
754pub struct Stored<T> {
755    id: StoreId,
756    inner: T,
757}
758
759impl<T> AbstractStored for Stored<T> {
760    type BareTy = T;
761
762    unsafe fn from_bare(bare_value: Self::BareTy, id: StoreId) -> Self {
763        Self {
764            inner: bare_value,
765            id,
766        }
767    }
768
769    fn id(&self) -> Option<StoreId> {
770        Some(self.id)
771    }
772
773    fn into_bare(self) -> Self::BareTy {
774        self.inner
775    }
776}
777
778impl<T: Clone> Clone for Stored<T> {
779    fn clone(&self) -> Self {
780        Self {
781            id: self.id,
782            inner: self.inner.clone(),
783        }
784    }
785}
786
787impl<T: Copy> Copy for Stored<T> {}
788
789impl<T: core::fmt::Debug> core::fmt::Debug for Stored<T> {
790    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
791        f.debug_struct("Stored")
792            .field("inner", &self)
793            .field("id", &self.id)
794            .finish()
795    }
796}
797
798impl<T: PartialEq> PartialEq for Stored<T> {
799    fn eq(&self, other: &Self) -> bool {
800        self.id.eq(&other.id) && self.inner.eq(&other.inner)
801    }
802}
803
804impl<T: Eq> Eq for Stored<T> {}
805
806impl<T> Stored<T> {
807    // TODO remove this after the `ResumableRef` rework. Currently
808    // `ResumableRef` can store data, however it should merely be an addr type
809    // into the store in the future.
810    fn as_mut(&mut self) -> Stored<&mut T> {
811        Stored {
812            id: self.id,
813            inner: &mut self.inner,
814        }
815    }
816}
817
818/// A stored variant of [`ExternVal`]
819#[derive(Debug, Copy, Clone, PartialEq, Eq)]
820pub enum StoredExternVal {
821    Func(Stored<FuncAddr>),
822    Table(Stored<TableAddr>),
823    Mem(Stored<MemAddr>),
824    Global(Stored<GlobalAddr>),
825}
826
827impl AbstractStored for StoredExternVal {
828    type BareTy = ExternVal;
829
830    unsafe fn from_bare(bare_value: Self::BareTy, id: StoreId) -> Self {
831        match bare_value {
832            ExternVal::Func(func_addr) => {
833                // SAFETY: Upheld by the caller
834                let stored_func_addr = unsafe { Stored::from_bare(func_addr, id) };
835                Self::Func(stored_func_addr)
836            }
837            ExternVal::Table(table_addr) => {
838                // SAFETY: Upheld by the caller
839                let stored_table_addr = unsafe { Stored::from_bare(table_addr, id) };
840                Self::Table(stored_table_addr)
841            }
842            ExternVal::Mem(mem_addr) => {
843                // SAFETY: Upheld by the caller
844                let stored_mem_addr = unsafe { Stored::from_bare(mem_addr, id) };
845                Self::Mem(stored_mem_addr)
846            }
847            ExternVal::Global(global_addr) => {
848                // SAFETY: Upheld by the caller
849                let stored_global_addr = unsafe { Stored::from_bare(global_addr, id) };
850                Self::Global(stored_global_addr)
851            }
852        }
853    }
854
855    fn id(&self) -> Option<StoreId> {
856        match self {
857            StoredExternVal::Func(stored_func_addr) => stored_func_addr.id(),
858            StoredExternVal::Table(stored_table_addr) => stored_table_addr.id(),
859            StoredExternVal::Mem(stored_mem_addr) => stored_mem_addr.id(),
860            StoredExternVal::Global(stored_global_addr) => stored_global_addr.id(),
861        }
862    }
863
864    fn into_bare(self) -> Self::BareTy {
865        match self {
866            StoredExternVal::Func(stored_func_addr) => {
867                ExternVal::Func(stored_func_addr.into_bare())
868            }
869            StoredExternVal::Table(stored_table_addr) => {
870                ExternVal::Table(stored_table_addr.into_bare())
871            }
872            StoredExternVal::Mem(stored_mem_addr) => ExternVal::Mem(stored_mem_addr.into_bare()),
873            StoredExternVal::Global(stored_global_addr) => {
874                ExternVal::Global(stored_global_addr.into_bare())
875            }
876        }
877    }
878}
879
880impl StoredExternVal {
881    pub fn as_func(self) -> Option<Stored<FuncAddr>> {
882        match self {
883            StoredExternVal::Func(func_addr) => Some(func_addr),
884            _ => None,
885        }
886    }
887
888    pub fn as_table(self) -> Option<Stored<TableAddr>> {
889        match self {
890            StoredExternVal::Table(table_addr) => Some(table_addr),
891            _ => None,
892        }
893    }
894
895    pub fn as_mem(self) -> Option<Stored<MemAddr>> {
896        match self {
897            StoredExternVal::Mem(mem_addr) => Some(mem_addr),
898            _ => None,
899        }
900    }
901
902    pub fn as_global(self) -> Option<Stored<GlobalAddr>> {
903        match self {
904            StoredExternVal::Global(global_addr) => Some(global_addr),
905            _ => None,
906        }
907    }
908}
909
910/// A stored variant of [`RunState`]
911pub enum StoredRunState {
912    Finished {
913        values: Vec<StoredValue>,
914        maybe_remaining_fuel: Option<u64>,
915    },
916    Resumable {
917        resumable_ref: Stored<ResumableRef>,
918        required_fuel: NonZeroU64,
919    },
920}
921
922impl AbstractStored for StoredRunState {
923    type BareTy = RunState;
924
925    unsafe fn from_bare(bare_value: Self::BareTy, id: StoreId) -> Self {
926        match bare_value {
927            RunState::Finished {
928                values,
929                maybe_remaining_fuel,
930            } => Self::Finished {
931                // SAFETY: Upheld by the caller
932                values: unsafe { wrap_vec_elements(values, id) },
933                maybe_remaining_fuel,
934            },
935            RunState::Resumable {
936                resumable_ref,
937                required_fuel,
938            } => Self::Resumable {
939                // SAFETY: Upheld by the caller
940                resumable_ref: unsafe { Stored::from_bare(resumable_ref, id) },
941                required_fuel,
942            },
943        }
944    }
945
946    fn id(&self) -> Option<StoreId> {
947        todo!()
948    }
949
950    fn into_bare(self) -> Self::BareTy {
951        todo!()
952    }
953}
954
955/// A stored variant of [`InstantiationOutcome`]
956pub struct StoredInstantiationOutcome {
957    pub module_addr: Stored<ModuleAddr>,
958    pub maybe_remaining_fuel: Option<u64>,
959}
960
961impl AbstractStored for StoredInstantiationOutcome {
962    type BareTy = InstantiationOutcome;
963
964    unsafe fn from_bare(bare_value: Self::BareTy, id: StoreId) -> Self {
965        Self {
966            // SAFETY: Upheld by the caller
967            module_addr: unsafe { Stored::from_bare(bare_value.module_addr, id) },
968            maybe_remaining_fuel: bare_value.maybe_remaining_fuel,
969        }
970    }
971
972    fn id(&self) -> Option<StoreId> {
973        self.module_addr.id()
974    }
975
976    fn into_bare(self) -> Self::BareTy {
977        InstantiationOutcome {
978            module_addr: self.module_addr.into_bare(),
979            maybe_remaining_fuel: self.maybe_remaining_fuel,
980        }
981    }
982}
983
984/// Helper method for associating every element in a [`Vec`] with a [`StoreId`].
985///
986/// # Safety
987///
988/// It must be guaranteed that all given elements come from the [`Store`] with
989/// the given [`StoreId`].
990unsafe fn wrap_vec_elements<S: AbstractStored>(values: Vec<S::BareTy>, id: StoreId) -> Vec<S> {
991    values
992        .into_iter()
993        .map(|value| {
994            // SAFETY: The caller guarantees that all values in this Vec come
995            // from the store with given id. Therefore, this is also true for
996            // this specific `Value`.
997            unsafe { S::from_bare(value, id) }
998        })
999        .collect()
1000}
1001
1002/// Helper method for checking if all [`Value`]s in a slice have the given
1003/// [`StoreId`] and then, if the check was true, converting them to a
1004/// [`Vec<Value>`].
1005///
1006/// # Errors
1007/// - [`RuntimeError::StoreIdMismatch`]
1008fn try_unwrap_values<S: AbstractStored>(
1009    stored_values: Vec<S>,
1010    expected_store_id: StoreId,
1011) -> Result<Vec<S::BareTy>, RuntimeError> {
1012    stored_values
1013        .into_iter()
1014        .map(|stored_value| stored_value.try_unwrap_into_bare(expected_store_id))
1015        .collect()
1016}