wasmi_c_api/store.rs
1use crate::{wasm_engine_t, wasmi_error_t, ForeignData};
2use alloc::{boxed::Box, sync::Arc};
3use core::{cell::UnsafeCell, ffi};
4use wasmi::{AsContext, AsContextMut, Store, StoreContext, StoreContextMut};
5
6/// This representation of a `Store` is used to implement the `wasm.h` API (and
7/// *not* the `wasmi.h` API!)
8///
9/// This is stored alongside `Func` and such for `wasm.h` so each object is
10/// independently owned. The usage of `Arc` here is mostly to just get it to be
11/// safe to drop across multiple threads, but otherwise acquiring the `context`
12/// values from this struct is considered unsafe due to it being unknown how the
13/// aliasing is working on the C side of things.
14///
15/// The aliasing requirements are documented in the C API `wasm.h` itself (at
16/// least Wasmi's implementation).
17#[derive(Clone)]
18pub struct WasmStoreRef {
19 inner: Arc<UnsafeCell<Store<()>>>,
20}
21
22impl WasmStoreRef {
23 /// Returns shared access to the store context of the [`WasmStoreRef`].
24 ///
25 /// Wraps [`wasmi::AsContext`].
26 ///
27 /// # Safety
28 ///
29 /// It is the callers responsibility to provide a valid `self`.
30 pub unsafe fn context(&self) -> StoreContext<'_, ()> {
31 (*self.inner.get()).as_context()
32 }
33
34 /// Returns mutable access to the store context of the [`WasmStoreRef`].
35 ///
36 /// Wraps [`wasmi::AsContextMut`].
37 ///
38 /// # Safety
39 ///
40 /// It is the callers responsibility to provide a valid `self`.
41 pub unsafe fn context_mut(&mut self) -> StoreContextMut<'_, ()> {
42 (*self.inner.get()).as_context_mut()
43 }
44}
45
46/// The Wasm store.
47///
48/// The returned [`wasm_engine_t`] must be freed using [`wasm_store_delete`].
49///
50/// Wraps [`wasmi::Store<()>`](wasmi::Store).
51#[repr(C)]
52#[derive(Clone)]
53pub struct wasm_store_t {
54 pub(crate) inner: WasmStoreRef,
55}
56
57wasmi_c_api_macros::declare_own!(wasm_store_t);
58
59/// Creates a new [`Store<()>`](wasmi::Store) for the given `engine`.
60///
61/// The returned [`wasm_store_t`] must be freed using [`wasm_store_delete`].
62///
63/// Wraps [`<wasmi::Store<()>>::new`](wasmi::Store::new).
64#[cfg_attr(not(feature = "prefix-symbols"), no_mangle)]
65#[allow(clippy::arc_with_non_send_sync)]
66#[cfg_attr(feature = "prefix-symbols", wasmi_c_api_macros::prefix_symbol)]
67pub extern "C" fn wasm_store_new(engine: &wasm_engine_t) -> Box<wasm_store_t> {
68 let engine = &engine.inner;
69 let store = Store::new(engine, ());
70 Box::new(wasm_store_t {
71 inner: WasmStoreRef {
72 inner: Arc::new(UnsafeCell::new(store)),
73 },
74 })
75}
76
77/// The Wasm store with foreign data and optional WASI support.
78///
79/// The returned [`wasm_engine_t`] must be freed using [`wasm_store_delete`].
80///
81/// Wraps [`wasmi::Store<WasmiStoreData>`](wasmi::Store).
82#[repr(C)]
83pub struct wasmi_store_t {
84 pub(crate) store: Store<WasmiStoreData>,
85}
86
87wasmi_c_api_macros::declare_own!(wasmi_store_t);
88
89/// Extensional data stored by [`wasmi_store_t`] to handle foreign data and optional WASI support.
90pub struct WasmiStoreData {
91 foreign: ForeignData,
92}
93
94/// Creates a new [`Store<()>`](wasmi::Store) for the given `engine`.
95///
96/// - This takes a foreign `data` with an associated `finalizer`.
97/// - The returned [`wasm_store_t`] must be freed using [`wasm_store_delete`].
98///
99/// Wraps [`<wasmi::Store<()>>::new`](wasmi::Store::new).
100#[no_mangle]
101pub extern "C" fn wasmi_store_new(
102 engine: &wasm_engine_t,
103 data: *mut ffi::c_void,
104 finalizer: Option<extern "C" fn(*mut ffi::c_void)>,
105) -> Box<wasmi_store_t> {
106 Box::new(wasmi_store_t {
107 store: Store::new(
108 &engine.inner,
109 WasmiStoreData {
110 foreign: ForeignData { data, finalizer },
111 },
112 ),
113 })
114}
115
116/// Returns mutable access to the store context of the [`wasmi_store_t`].
117///
118/// Wraps [`wasmi::AsContext`].
119///
120/// # Safety
121///
122/// It is the callers responsibility to provide a valid `self`.
123#[no_mangle]
124pub extern "C" fn wasmi_store_context(
125 store: &mut wasmi_store_t,
126) -> StoreContextMut<'_, WasmiStoreData> {
127 store.store.as_context_mut()
128}
129
130/// Returns a pointer to the foreign data of the Wasmi store context.
131#[no_mangle]
132pub extern "C" fn wasmi_context_get_data(
133 store: StoreContext<'_, WasmiStoreData>,
134) -> *mut ffi::c_void {
135 store.data().foreign.data
136}
137
138/// Sets the foreign data of the Wasmi store context.
139#[no_mangle]
140pub extern "C" fn wasmi_context_set_data(
141 mut store: StoreContextMut<'_, WasmiStoreData>,
142 data: *mut ffi::c_void,
143) {
144 store.data_mut().foreign.data = data;
145}
146
147/// Returns the current fuel of the Wasmi store context in `fuel`.
148///
149/// Wraps [`Store::get_fuel`].
150///
151/// # Errors
152///
153/// If [`Store::get_fuel`] errors.
154#[no_mangle]
155pub extern "C" fn wasmi_context_get_fuel(
156 store: StoreContext<'_, WasmiStoreData>,
157 fuel: &mut u64,
158) -> Option<Box<wasmi_error_t>> {
159 crate::handle_result(store.get_fuel(), |amt| {
160 *fuel = amt;
161 })
162}
163
164/// Sets the current fuel of the Wasmi store context to `fuel`.
165///
166/// Wraps [`Store::set_fuel`].
167///
168/// # Errors
169///
170/// If [`Store::set_fuel`] errors.
171#[no_mangle]
172pub extern "C" fn wasmi_context_set_fuel(
173 mut store: StoreContextMut<'_, WasmiStoreData>,
174 fuel: u64,
175) -> Option<Box<wasmi_error_t>> {
176 crate::handle_result(store.set_fuel(fuel), |()| {})
177}