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}