Coverage Report

Created: 2026-03-18 17:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/build/source/crates/checked/src/store.rs
Line
Count
Source
1
use alloc::{string::String, vec::Vec};
2
use wasm::{
3
    addrs::{FuncAddr, GlobalAddr, MemAddr, ModuleAddr, TableAddr},
4
    config::Config,
5
    resumable::Resumable,
6
    FuncType, GlobalType, HaltExecutionError, MemType, RuntimeError, TableType, ValidationInfo,
7
    Value,
8
};
9
10
use crate::{
11
    stored_types::{Stored, StoredExternVal, StoredInstantiationOutcome, StoredRunState},
12
    AbstractStored, StoreId, StoredRef, StoredValue,
13
};
14
15
pub struct Store<'b, T: Config> {
16
    pub(crate) inner: wasm::Store<'b, T>,
17
18
    /// A unique identifier for this store. This is used to verify that stored
19
    /// objects belong to the current [`Store`](wasm::Store).
20
    pub(crate) id: StoreId,
21
}
22
23
impl<'b, T: Config> Store<'b, T> {
24
    /// Returns an immutable reference to the raw store.
25
27
    pub fn inner(&self) -> &wasm::Store<'b, T> {
26
27
        &self.inner
27
27
    }
28
29
    /// Deconstructs this checked store and returns its inner representation.
30
0
    pub fn into_inner(self) -> wasm::Store<'b, T> {
31
0
        self.inner
32
0
    }
33
34
    /// Returns the id of this store.
35
0
    pub fn id(&self) -> StoreId {
36
0
        self.id
37
0
    }
38
}
39
40
// All functions in this impl block must occur in the same order as they are
41
// defined in for the unchecked `Store` methods. Also all functions must follow
42
// the same implementation scheme to make sure they are only light wrappers:
43
//
44
// 1. try unwrap [stored parameter objects]
45
// 2. call [unchecked method]
46
// 3. rewrap [results into stored objects]
47
// 4. return [stored result objects]
48
impl<'b, T: Config> Store<'b, T> {
49
433
    pub fn new(user_data: T) -> Self {
50
433
        Self {
51
433
            inner: wasm::Store::new(user_data),
52
433
            id: StoreId::new(),
53
433
        }
54
433
    }
55
56
    /// This is a safe variant of
57
    /// [`Store::module_instantiate`](wasm::Store::module_instantiate).
58
288
    pub fn module_instantiate(
59
288
        &mut self,
60
288
        validation_info: &ValidationInfo<'b>,
61
288
        extern_vals: Vec<StoredExternVal>,
62
288
        maybe_fuel: Option<u64>,
63
288
    ) -> Result<StoredInstantiationOutcome, RuntimeError> {
64
        // 1. try unwrap
65
288
        let extern_vals = extern_vals.try_unwrap_into_bare(self.id);
66
        // 2. call
67
        // SAFETY: It was just checked that the `ExternVal`s came from the
68
        // current store through their store ids.
69
286
        let instantiation_outcome = unsafe {
70
288
            self.inner
71
288
                .module_instantiate(validation_info, extern_vals, maybe_fuel)
72
2
        }?;
73
        // 3. rewrap
74
        // SAFETY: The `InstantiationOutcome` just came from the current store.
75
286
        let stored_instantiation_outcome =
76
286
            unsafe { StoredInstantiationOutcome::from_bare(instantiation_outcome, self.id) };
77
        // 4. return
78
286
        Ok(stored_instantiation_outcome)
79
288
    }
80
81
    /// This is a safe variant of
82
    /// [`Store::instance_export`](wasm::Store::instance_export).
83
48.1k
    pub fn instance_export(
84
48.1k
        &self,
85
48.1k
        module_addr: Stored<ModuleAddr>,
86
48.1k
        name: &str,
87
48.1k
    ) -> Result<StoredExternVal, RuntimeError> {
88
        // 1. try unwrap
89
48.1k
        let module_addr = module_addr.try_unwrap_into_bare(self.id);
90
        // 2. call
91
        // SAFETY: It was just checked that the `ModuleAddr` came from the
92
        // current store through its store id.
93
48.1k
        let extern_val = unsafe { self.inner.instance_export(module_addr, name) }
?0
;
94
        // 3. rewrap
95
        // SAFETY: The `ExternVal` just came from the current store.
96
48.1k
        let stored_extern_val = unsafe { StoredExternVal::from_bare(extern_val, self.id) };
97
        // 4. return
98
48.1k
        Ok(stored_extern_val)
99
48.1k
    }
100
101
    /// This is a safer variant of
102
    /// [`Store::func_alloc`](wasm::Store::func_alloc). It is functionally
103
    /// equal, with the only difference being that this function returns a
104
    /// [`Stored<FuncAddr>`].
105
    ///
106
    /// # Safety
107
    ///
108
    /// The caller has to guarantee that if the [`Value`]s returned from the
109
    /// given host function are references, their addresses came either from the
110
    /// host function arguments or from the current [`Store`] object.
111
    ///
112
    /// See [`Store::func_alloc`](wasm::Store::func_alloc) for more information.
113
    #[allow(clippy::let_and_return)] // reason = "to follow the 1234 structure"
114
0
    pub unsafe fn func_alloc(
115
0
        &mut self,
116
0
        func_type: FuncType,
117
0
        host_func: fn(&mut T, Vec<Value>) -> Result<Vec<Value>, HaltExecutionError>,
118
0
    ) -> Stored<FuncAddr> {
119
        // 1. try unwrap
120
        // no stored parameters
121
        // 2. call
122
        // SAFETY: The caller ensures that if the host function returns
123
        // references, they originate either from the arguments or the current
124
        // store.
125
0
        let func_addr = unsafe { self.inner.func_alloc(func_type, host_func) };
126
        // 3. rewrap
127
        // 4. return
128
        // SAFETY: The function address just came from the current store.
129
0
        unsafe { Stored::from_bare(func_addr, self.id) }
130
0
    }
131
132
    /// This is a safe variant of [`Store::func_type`](wasm::Store::func_type).
133
0
    pub fn func_type(&self, func_addr: Stored<FuncAddr>) -> FuncType {
134
        // 1. try unwrap
135
0
        let func_addr = func_addr.try_unwrap_into_bare(self.id);
136
        // 2. call
137
        // 3. rewrap
138
        // `FuncType` does not have a stored variant.
139
        // 4. return
140
        // SAFETY: It was just checked that the `FuncAddr` came from the current
141
        // store through its store id.
142
0
        unsafe { self.inner.func_type(func_addr) }
143
0
    }
144
145
    /// This is a safe variant of [`Store::invoke`](wasm::Store::invoke).
146
0
    pub fn invoke(
147
0
        &mut self,
148
0
        func_addr: Stored<FuncAddr>,
149
0
        params: Vec<StoredValue>,
150
0
        maybe_fuel: Option<u64>,
151
0
    ) -> Result<StoredRunState<T>, RuntimeError> {
152
        // 1. try unwrap
153
0
        let func_addr = func_addr.try_unwrap_into_bare(self.id);
154
0
        let params = params.try_unwrap_into_bare(self.id);
155
        // 2. call
156
        // SAFETY: It was just checked that the `FuncAddr` and any addresses in
157
        // the parameters came from the current store through their store ids.
158
0
        let run_state = unsafe { self.inner.invoke(func_addr, params, maybe_fuel) }?;
159
        // 3. rewrap
160
        // SAFETY: The `RunState` just came from the current store.
161
0
        let stored_run_state = unsafe { StoredRunState::from_bare(run_state, self.id) };
162
        // 4. return
163
0
        Ok(stored_run_state)
164
0
    }
165
166
    /// This is a safe variant of
167
    /// [`Store::table_alloc`](wasm::Store::table_alloc).
168
146
    pub fn table_alloc(
169
146
        &mut self,
170
146
        table_type: TableType,
171
146
        r#ref: StoredRef,
172
146
    ) -> Result<Stored<TableAddr>, RuntimeError> {
173
        // 1. try unwrap
174
146
        let r#ref = r#ref.try_unwrap_into_bare(self.id);
175
        // 2. call
176
        // SAFETY: It was just checked that any address in the reference came
177
        // from the current store through its store id.
178
146
        let table_addr = unsafe { self.inner.table_alloc(table_type, r#ref) }
?0
;
179
        // 3. rewrap
180
        // SAFETY: The `TableAddr` just came from the current store.
181
146
        let stored_table_addr = unsafe { Stored::from_bare(table_addr, self.id) };
182
        // 4. return
183
146
        Ok(stored_table_addr)
184
146
    }
185
186
    /// This is a safe variant of
187
    /// [`Store::table_type`](wasm::Store::table_type).
188
0
    pub fn table_type(&self, table_addr: Stored<TableAddr>) -> TableType {
189
        // 1. try unwrap
190
0
        let table_addr = table_addr.try_unwrap_into_bare(self.id);
191
        // 2. call
192
        // 3. rewrap
193
        // `TableType` has no stored variant.
194
        // 4. return
195
        // SAFETY: It was just checked that the `TableAddr` came from the
196
        // current store through its store id.
197
0
        unsafe { self.inner.table_type(table_addr) }
198
0
    }
199
200
    /// This is a safe variant of
201
    /// [`Store::table_read`](wasm::Store::table_read).
202
2
    pub fn table_read(
203
2
        &self,
204
2
        table_addr: Stored<TableAddr>,
205
2
        i: u32,
206
2
    ) -> Result<StoredRef, RuntimeError> {
207
        // 1. try unwrap
208
2
        let table_addr = table_addr.try_unwrap_into_bare(self.id);
209
        // 2. call
210
        // SAFETY: It was just checked that the `TableAddr` came from the
211
        // current store through its store id.
212
2
        let r#ref = unsafe { self.inner.table_read(table_addr, i) }
?0
;
213
        // 3. rewrap
214
        // SAFETY: The `Ref` ust came from the current store.
215
2
        let stored_ref = unsafe { StoredRef::from_bare(r#ref, self.id) };
216
        // 4. return
217
2
        Ok(stored_ref)
218
2
    }
219
220
    /// This is a safe variant of
221
    /// [`Store::table_write`](wasm::Store::table_write).
222
0
    pub fn table_write(
223
0
        &mut self,
224
0
        table_addr: Stored<TableAddr>,
225
0
        i: u32,
226
0
        r#ref: StoredRef,
227
0
    ) -> Result<(), RuntimeError> {
228
        // 1. try unwrap
229
0
        let table_addr = table_addr.try_unwrap_into_bare(self.id);
230
0
        let r#ref = r#ref.try_unwrap_into_bare(self.id);
231
        // 2. call
232
        // SAFETY: It was just checked that the `TableAddr` and any address in
233
        // the reference came from the current store through their store ids.
234
0
        unsafe { self.inner.table_write(table_addr, i, r#ref) }?;
235
        // 3. rewrap
236
        // result is the unit type.
237
        // 4. return
238
0
        Ok(())
239
0
    }
240
241
    /// This is a safe variant of
242
    /// [`Store::table_size`](wasm::Store::table_size).
243
0
    pub fn table_size(&self, table_addr: Stored<TableAddr>) -> u32 {
244
        // 1. try unwrap
245
0
        let table_addr = table_addr.try_unwrap_into_bare(self.id);
246
        // 2. call
247
        // 3. rewrap
248
        // table size has no stored variant.
249
        // 4. return
250
        // SAFETY: It was just checked that the `TableAddr` came from the
251
        // current store through its store id.
252
0
        unsafe { self.inner.table_size(table_addr) }
253
0
    }
254
255
    /// This is a variant of [`Store::mem_alloc`](wasm::Store::mem_alloc) that
256
    /// returns a stored object.
257
    #[allow(clippy::let_and_return)] // reason = "to follow the 1234 structure"
258
148
    pub fn mem_alloc(&mut self, mem_type: MemType) -> Stored<MemAddr> {
259
        // 1. try unwrap
260
        // no stored parameters
261
        // 2. call
262
148
        let mem_addr = self.inner.mem_alloc(mem_type);
263
        // 3. rewrap
264
        // 4. return
265
        // SAFETY: The `MemAddr` just came from the current store.
266
148
        unsafe { Stored::from_bare(mem_addr, self.id) }
267
148
    }
268
269
    /// This is a safe variant of [`Store::mem_type`](wasm::Store::mem_type).
270
0
    pub fn mem_type(&self, mem_addr: Stored<MemAddr>) -> MemType {
271
        // 1. try unwrap
272
0
        let mem_addr = mem_addr.try_unwrap_into_bare(self.id);
273
        // 2. call
274
        // 3. rewrap
275
        // `MemType` does not have a stored variant.
276
        // 4. return
277
        // SAFETY: It was just checked that the `MemAddr` came from the current
278
        // store through its store id.
279
0
        unsafe { self.inner.mem_type(mem_addr) }
280
0
    }
281
282
    /// This is a safe variant of [`Store::mem_read`](wasm::Store::mem_read).
283
105
    pub fn mem_read(&self, mem_addr: Stored<MemAddr>, i: u32) -> Result<u8, RuntimeError> {
284
        // 1. try unwrap
285
105
        let mem_addr = mem_addr.try_unwrap_into_bare(self.id);
286
        // 2. call
287
        // SAFETY: It was just checked that the `MemAddr` came from the current
288
        // store through its store id.
289
105
        let byte = unsafe { self.inner.mem_read(mem_addr, i) }
?0
;
290
        // 3. rewrap
291
        // a single byte does not have a stored variant.
292
        // 4. return
293
105
        Ok(byte)
294
105
    }
295
296
    /// This is a safe variant of [`Store::mem_write`](wasm::Store::mem_write).
297
0
    pub fn mem_write(
298
0
        &mut self,
299
0
        mem_addr: Stored<MemAddr>,
300
0
        i: u32,
301
0
        byte: u8,
302
0
    ) -> Result<(), RuntimeError> {
303
        // 1. try unwrap
304
0
        let mem_addr = mem_addr.try_unwrap_into_bare(self.id);
305
        // 2. call
306
        // SAFETY: It was just checked that the `MemAddr` came from the current
307
        // store through its store id.
308
0
        unsafe { self.inner.mem_write(mem_addr, i, byte) }?;
309
        // 3. rewrap
310
        // result is the unit type.
311
        // 4. return
312
0
        Ok(())
313
0
    }
314
315
    /// This is a safe variant of [`Store::mem_size`](wasm::Store::mem_size).
316
0
    pub fn mem_size(&self, mem_addr: Stored<MemAddr>) -> u32 {
317
        // 1. try unwrap
318
0
        let mem_addr = mem_addr.try_unwrap_into_bare(self.id);
319
        // 2. call
320
        // 3. rewrap
321
        // mem size does not have a stored variant.
322
        // 4. return
323
        // SAFETY: It was just checked that the `MemAddr` came from the current
324
        // store through its store id.
325
0
        unsafe { self.inner.mem_size(mem_addr) }
326
0
    }
327
328
    /// This is a safe variant of [`Store::mem_grow`](wasm::Store::mem_grow).
329
0
    pub fn mem_grow(&mut self, mem_addr: Stored<MemAddr>, n: u32) -> Result<(), RuntimeError> {
330
        // 1. try unwrap
331
0
        let mem_addr = mem_addr.try_unwrap_into_bare(self.id);
332
        // 2. call
333
        // SAFETY: It was just checked that the `MemAddr` came from the current
334
        // store through its store id.
335
0
        unsafe { self.inner.mem_grow(mem_addr, n) }?;
336
        // 3. rewrap
337
        // result is the unit type.
338
        // 4. return
339
0
        Ok(())
340
0
    }
341
342
    /// This is a safe variant of
343
    /// [`Store::global_alloc`](wasm::Store::global_alloc).
344
584
    pub fn global_alloc(
345
584
        &mut self,
346
584
        global_type: GlobalType,
347
584
        val: StoredValue,
348
584
    ) -> Result<Stored<GlobalAddr>, RuntimeError> {
349
        // 1. try unwrap
350
584
        let val = val.try_unwrap_into_bare(self.id);
351
        // 2. call
352
        // SAFETY: It was just checked that any address the value came from the
353
        // current store through its store id.
354
584
        let global_addr = unsafe { self.inner.global_alloc(global_type, val) }
?0
;
355
        // 3. rewrap
356
        // SAFETY: The `GlobalAddr` just came from the current store.
357
584
        let stored_global_addr = unsafe { Stored::from_bare(global_addr, self.id) };
358
        // 4. return
359
584
        Ok(stored_global_addr)
360
584
    }
361
362
    /// This is a safe variant of
363
    /// [`Store::global_type`](wasm::Store::global_type).
364
2
    pub fn global_type(&self, global_addr: Stored<GlobalAddr>) -> Result<GlobalType, RuntimeError> {
365
        // 1. try unwrap
366
2
        let global_addr = global_addr.try_unwrap_into_bare(self.id);
367
        // 2. call
368
        // SAFETY: It was just checked that the `GlobalAddr` came from the
369
        // current store through its store id.
370
2
        let global_type = unsafe { self.inner.global_type(global_addr) };
371
        // 3. rewrap
372
        // `GlobalType` does not have a stored variant.
373
        // 4. return
374
2
        Ok(global_type)
375
2
    }
376
377
    /// This is a safe variant of
378
    /// [`Store::global_read`](wasm::Store::global_read).
379
99
    pub fn global_read(&self, global_addr: Stored<GlobalAddr>) -> StoredValue {
380
        // 1. try unwrap
381
99
        let global_addr = global_addr.try_unwrap_into_bare(self.id);
382
        // 2. call
383
        // SAFETY: It was just checked that the `GlobalAddr` came from the
384
        // current store through its store id.
385
99
        let value = unsafe { self.inner.global_read(global_addr) };
386
        // 3. rewrap
387
        // 4. return
388
        // SAFETY: The `Value` just came from the current store.
389
99
        unsafe { StoredValue::from_bare(value, self.id) }
390
99
    }
391
392
    /// This is a safe variant of
393
    /// [`Store::global_write`](wasm::Store::global_write).
394
1
    pub fn global_write(
395
1
        &mut self,
396
1
        global_addr: Stored<GlobalAddr>,
397
1
        val: StoredValue,
398
1
    ) -> Result<(), RuntimeError> {
399
        // 1. try unwrap
400
1
        let global_addr = global_addr.try_unwrap_into_bare(self.id);
401
1
        let val = val.try_unwrap_into_bare(self.id);
402
        // 2. call
403
        // SAFETY: It was just checked that the `GlobalAddr` any any address
404
        // contained in the value came from the current store through their
405
        // store ids.
406
1
        unsafe { self.inner.global_write(global_addr, val) }
?0
;
407
        // 3. rewrap
408
        // result is the unit type.
409
        // 4. return
410
1
        Ok(())
411
1
    }
412
413
    /// This is a safe variant of
414
    /// [`Store::create_resumable`](wasm::Store::create_resumable).
415
6
    pub fn create_resumable(
416
6
        &self,
417
6
        func_addr: Stored<FuncAddr>,
418
6
        params: Vec<StoredValue>,
419
6
        maybe_fuel: Option<u64>,
420
6
    ) -> Result<Stored<Resumable<T>>, RuntimeError> {
421
        // 1. try unwrap
422
6
        let func_addr = func_addr.try_unwrap_into_bare(self.id);
423
6
        let params = params.try_unwrap_into_bare(self.id);
424
        // 2. call
425
        // SAFETY: It was just checked that the `FuncAddr` any any addresses
426
        // contained in the parameters came from the current store through their
427
        // store ids.
428
6
        let resumable = unsafe { self.inner.create_resumable(func_addr, params, maybe_fuel) }
?0
;
429
        // 3. rewrap
430
        // SAFETY: The `Resumable` just came from the current store.
431
6
        let stored_resumable = unsafe { Stored::from_bare(resumable, self.id) };
432
        // 4. return
433
6
        Ok(stored_resumable)
434
6
    }
435
436
    /// This is a safe variant of [`Store::resume`](wasm::Store::resume).
437
49
    pub fn resume(
438
49
        &mut self,
439
49
        resumable: Stored<Resumable<T>>,
440
49
    ) -> Result<StoredRunState<T>, RuntimeError> {
441
        // 1. try unwrap
442
49
        let resumable = resumable.try_unwrap_into_bare(self.id);
443
        // 2. call
444
        // SAFETY: It was just checked that the `Resumable` came from the
445
        // current store through its store id.
446
49
        let run_state = unsafe { self.inner.resume(resumable) }
?0
;
447
        // 3. rewrap
448
        // SAFETY: The `RunState` just came from the current store.
449
49
        let stored_run_state = unsafe { StoredRunState::from_bare(run_state, self.id) };
450
        // 4. return
451
49
        Ok(stored_run_state)
452
49
    }
453
454
    /// This is a safe variant of
455
    /// [`Store::invoke_without_fuel`](wasm::Store::invoke_without_fuel).
456
47.8k
    pub fn invoke_without_fuel(
457
47.8k
        &mut self,
458
47.8k
        func_addr: Stored<FuncAddr>,
459
47.8k
        params: Vec<StoredValue>,
460
47.8k
    ) -> Result<Vec<StoredValue>, RuntimeError> {
461
        // 1. try unwrap
462
47.8k
        let func_addr = func_addr.try_unwrap_into_bare(self.id);
463
47.8k
        let params = params.try_unwrap_into_bare(self.id);
464
        // 2. call
465
        // SAFETY: It was just checked that the `FuncAddr` and any addresses
466
        // contained in the parameters came from the current store through their
467
        // store ids.
468
47.8k
        let 
returns45.3k
= unsafe { self.inner.invoke_without_fuel(func_addr, params) }
?2.42k
;
469
        // 3. rewrap
470
        // SAFETY: All `Value`s just came from the current store.
471
45.3k
        let returns = unsafe { Vec::from_bare(returns, self.id) };
472
        // 4. return
473
45.3k
        Ok(returns)
474
47.8k
    }
475
476
    /// This is a safe variant of
477
    /// [`Store::mem_access_mut_slice`](wasm::Store::mem_access_mut_slice).
478
3
    pub fn mem_access_mut_slice<R>(
479
3
        &self,
480
3
        memory: Stored<MemAddr>,
481
3
        accessor: impl FnOnce(&mut [u8]) -> R,
482
3
    ) -> R {
483
        // 1. try unwrap
484
3
        let memory = memory.try_unwrap_into_bare(self.id);
485
        // 2. call
486
        // 3. rewrap
487
        // result is generic
488
        // 4. return
489
        // SAFETY: It was just checked that the `MemAddr` came from the current
490
        // store through its store id.
491
3
        unsafe { self.inner.mem_access_mut_slice(memory, accessor) }
492
3
    }
493
494
    /// This is a safe variant of
495
    /// [`Store::instance_exports`](wasm::Store::instance_exports)
496
0
    pub fn instance_exports(
497
0
        &self,
498
0
        module_addr: Stored<ModuleAddr>,
499
0
    ) -> Vec<(String, StoredExternVal)> {
500
        // 1. try unwrap
501
0
        let module_addr = module_addr.try_unwrap_into_bare(self.id);
502
        // 2. call
503
        // SAFETY: We just checked that this module address is valid in the
504
        // current store through its store id.
505
0
        let exports = unsafe { self.inner.instance_exports(module_addr) };
506
        // 3. rewrap
507
        // 4. return
508
0
        exports
509
0
            .into_iter()
510
0
            .map(|(name, externval)| {
511
                // SAFETY: The `ExternVal`s just came from the current store.
512
0
                let stored_externval = unsafe { StoredExternVal::from_bare(externval, self.id) };
513
0
                (name, stored_externval)
514
0
            })
515
0
            .collect()
516
0
    }
517
}