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}