Skip to main content

wasmtime_c_api/
store.rs

1use crate::{ForeignData, wasm_engine_t, wasmtime_error_t, wasmtime_val_t};
2use std::cell::UnsafeCell;
3use std::ffi::c_void;
4use std::sync::Arc;
5use wasmtime::{
6    AsContext, AsContextMut, Caller, Store, StoreContext, StoreContextMut, StoreLimits,
7    StoreLimitsBuilder, UpdateDeadline, Val,
8};
9
10// Store-related type aliases for `wasm.h` APIs. Not for use with `wasmtime.h`
11// APIs!
12pub type WasmStoreData = ();
13pub type WasmStore = Store<WasmStoreData>;
14pub type WasmStoreContext<'a> = StoreContext<'a, WasmStoreData>;
15pub type WasmStoreContextMut<'a> = StoreContextMut<'a, WasmStoreData>;
16
17/// This representation of a `Store` is used to implement the `wasm.h` API (and
18/// *not* the `wasmtime.h` API!)
19///
20/// This is stored alongside `Func` and such for `wasm.h` so each object is
21/// independently owned. The usage of `Arc` here is mostly to just get it to be
22/// safe to drop across multiple threads, but otherwise acquiring the `context`
23/// values from this struct is considered unsafe due to it being unknown how the
24/// aliasing is working on the C side of things.
25///
26/// The aliasing requirements are documented in the C API `wasm.h` itself (at
27/// least Wasmtime's implementation).
28#[derive(Clone)]
29pub struct WasmStoreRef {
30    store: Arc<UnsafeCell<WasmStore>>,
31}
32
33impl WasmStoreRef {
34    pub unsafe fn context(&self) -> WasmStoreContext<'_> {
35        (*self.store.get()).as_context()
36    }
37
38    pub unsafe fn context_mut(&mut self) -> WasmStoreContextMut<'_> {
39        (*self.store.get()).as_context_mut()
40    }
41}
42
43#[repr(C)]
44#[derive(Clone)]
45pub struct wasm_store_t {
46    pub(crate) store: WasmStoreRef,
47}
48
49wasmtime_c_api_macros::declare_own!(wasm_store_t);
50
51#[unsafe(no_mangle)]
52pub extern "C" fn wasm_store_new(engine: &wasm_engine_t) -> Box<wasm_store_t> {
53    let engine = &engine.engine;
54    let store = Store::new(engine, ());
55    Box::new(wasm_store_t {
56        store: WasmStoreRef {
57            store: Arc::new(UnsafeCell::new(store)),
58        },
59    })
60}
61
62// Store-related type aliases for `wasmtime.h` APIs. Not for use with `wasm.h`
63// APIs!
64pub type WasmtimeStore = Store<WasmtimeStoreData>;
65pub type WasmtimeStoreContext<'a> = StoreContext<'a, WasmtimeStoreData>;
66pub type WasmtimeStoreContextMut<'a> = StoreContextMut<'a, WasmtimeStoreData>;
67pub type WasmtimeCaller<'a> = Caller<'a, WasmtimeStoreData>;
68
69/// Representation of a `Store` for `wasmtime.h` This notably tries to move more
70/// burden of aliasing on the caller rather than internally, allowing for a more
71/// raw representation of contexts and such that requires less `unsafe` in the
72/// implementation.
73///
74/// Note that this notably carries `WasmtimeStoreData` as a payload which allows
75/// storing foreign data and configuring WASI as well.
76#[repr(C)]
77pub struct wasmtime_store_t {
78    pub(crate) store: WasmtimeStore,
79}
80
81wasmtime_c_api_macros::declare_own!(wasmtime_store_t);
82
83pub struct WasmtimeStoreData {
84    foreign: crate::ForeignData,
85    #[cfg(feature = "wasi")]
86    pub(crate) wasi: Option<wasmtime_wasi::p1::WasiP1Ctx>,
87    #[cfg(feature = "wasi-http")]
88    pub(crate) wasi_http: Option<wasmtime_wasi_http::WasiHttpCtx>,
89
90    /// Temporary storage for usage during a wasm->host call to store values
91    /// in a slice we pass to the C API.
92    pub hostcall_val_storage: Vec<wasmtime_val_t>,
93
94    /// Temporary storage for usage during host->wasm calls, same as above but
95    /// for a different direction.
96    pub wasm_val_storage: Vec<Val>,
97
98    /// Limits for the store.
99    pub store_limits: StoreLimits,
100}
101
102#[cfg(all(feature = "component-model", feature = "wasi"))]
103impl wasmtime_wasi::WasiView for WasmtimeStoreData {
104    fn ctx(&mut self) -> wasmtime_wasi::WasiCtxView<'_> {
105        self.wasi.as_mut().unwrap().ctx()
106    }
107}
108
109#[cfg(all(feature = "component-model", feature = "wasi-http"))]
110impl wasmtime_wasi_http::p2::WasiHttpView for WasmtimeStoreData {
111    fn http(&mut self) -> wasmtime_wasi_http::p2::WasiHttpCtxView<'_> {
112        use wasmtime_wasi::WasiView;
113        let ctx = self.wasi_http.as_mut().unwrap();
114        let table = self.wasi.as_mut().unwrap().ctx().table;
115        wasmtime_wasi_http::p2::WasiHttpCtxView {
116            ctx,
117            table,
118            hooks: wasmtime_wasi_http::p2::default_hooks(),
119        }
120    }
121}
122
123#[unsafe(no_mangle)]
124pub extern "C" fn wasmtime_store_new(
125    engine: &wasm_engine_t,
126    data: *mut c_void,
127    finalizer: Option<extern "C" fn(*mut c_void)>,
128) -> Box<wasmtime_store_t> {
129    Box::new(wasmtime_store_t {
130        store: Store::new(
131            &engine.engine,
132            WasmtimeStoreData {
133                foreign: ForeignData { data, finalizer },
134                #[cfg(feature = "wasi")]
135                wasi: None,
136                #[cfg(feature = "wasi-http")]
137                wasi_http: None,
138                hostcall_val_storage: Vec::new(),
139                wasm_val_storage: Vec::new(),
140                store_limits: StoreLimits::default(),
141            },
142        ),
143    })
144}
145
146pub type wasmtime_update_deadline_kind_t = u8;
147pub const WASMTIME_UPDATE_DEADLINE_CONTINUE: wasmtime_update_deadline_kind_t = 0;
148pub const WASMTIME_UPDATE_DEADLINE_YIELD: wasmtime_update_deadline_kind_t = 1;
149
150#[unsafe(no_mangle)]
151pub extern "C" fn wasmtime_store_epoch_deadline_callback(
152    store: &mut wasmtime_store_t,
153    func: extern "C" fn(
154        WasmtimeStoreContextMut<'_>,
155        *mut c_void,
156        *mut u64,
157        *mut wasmtime_update_deadline_kind_t,
158    ) -> Option<Box<wasmtime_error_t>>,
159    data: *mut c_void,
160    finalizer: Option<extern "C" fn(*mut c_void)>,
161) {
162    let foreign = crate::ForeignData { data, finalizer };
163    store.store.epoch_deadline_callback(move |mut store_ctx| {
164        let _ = &foreign; // Move foreign into this closure
165        let mut delta: u64 = 0;
166        let mut kind = WASMTIME_UPDATE_DEADLINE_CONTINUE;
167        let result = (func)(
168            store_ctx.as_context_mut(),
169            foreign.data,
170            &mut delta as *mut u64,
171            &mut kind as *mut wasmtime_update_deadline_kind_t,
172        );
173        match result {
174            Some(err) => Err((*err).into()),
175            None if kind == WASMTIME_UPDATE_DEADLINE_CONTINUE => {
176                Ok(UpdateDeadline::Continue(delta))
177            }
178            #[cfg(feature = "async")]
179            None if kind == WASMTIME_UPDATE_DEADLINE_YIELD => Ok(UpdateDeadline::Yield(delta)),
180            _ => panic!("unknown wasmtime_update_deadline_kind_t: {kind}"),
181        }
182    });
183}
184
185#[unsafe(no_mangle)]
186pub extern "C" fn wasmtime_store_context(
187    store: &mut wasmtime_store_t,
188) -> WasmtimeStoreContextMut<'_> {
189    store.store.as_context_mut()
190}
191
192#[unsafe(no_mangle)]
193pub extern "C" fn wasmtime_store_limiter(
194    store: &mut wasmtime_store_t,
195    memory_size: i64,
196    table_elements: i64,
197    instances: i64,
198    tables: i64,
199    memories: i64,
200) {
201    let mut limiter = StoreLimitsBuilder::new();
202    if memory_size >= 0 {
203        limiter = limiter.memory_size(memory_size as usize);
204    }
205    if table_elements >= 0 {
206        limiter = limiter.table_elements(table_elements as usize);
207    }
208    if instances >= 0 {
209        limiter = limiter.instances(instances as usize);
210    }
211    if tables >= 0 {
212        limiter = limiter.tables(tables as usize);
213    }
214    if memories >= 0 {
215        limiter = limiter.memories(memories as usize);
216    }
217    store.store.data_mut().store_limits = limiter.build();
218    store.store.limiter(|data| &mut data.store_limits);
219}
220
221#[unsafe(no_mangle)]
222pub extern "C" fn wasmtime_context_get_data(store: WasmtimeStoreContext<'_>) -> *mut c_void {
223    store.data().foreign.data
224}
225
226#[unsafe(no_mangle)]
227pub extern "C" fn wasmtime_context_set_data(
228    mut store: WasmtimeStoreContextMut<'_>,
229    data: *mut c_void,
230) {
231    store.data_mut().foreign.data = data;
232}
233
234#[cfg(feature = "wasi")]
235#[unsafe(no_mangle)]
236pub extern "C" fn wasmtime_context_set_wasi(
237    mut context: WasmtimeStoreContextMut<'_>,
238    wasi: Box<crate::wasi_config_t>,
239) -> Option<Box<wasmtime_error_t>> {
240    crate::handle_result(wasi.into_wasi_ctx(), |wasi| {
241        context.data_mut().wasi = Some(wasi);
242    })
243}
244
245#[cfg(feature = "wasi-http")]
246#[unsafe(no_mangle)]
247pub extern "C" fn wasmtime_context_set_wasi_http(mut context: WasmtimeStoreContextMut<'_>) {
248    context.data_mut().wasi_http = Some(wasmtime_wasi_http::WasiHttpCtx::new());
249}
250
251#[unsafe(no_mangle)]
252pub extern "C" fn wasmtime_context_gc(
253    mut context: WasmtimeStoreContextMut<'_>,
254) -> Option<Box<wasmtime_error_t>> {
255    crate::handle_result(context.gc(None), |()| {})
256}
257
258#[unsafe(no_mangle)]
259pub extern "C" fn wasmtime_context_set_fuel(
260    mut store: WasmtimeStoreContextMut<'_>,
261    fuel: u64,
262) -> Option<Box<wasmtime_error_t>> {
263    crate::handle_result(store.set_fuel(fuel), |()| {})
264}
265
266#[unsafe(no_mangle)]
267pub extern "C" fn wasmtime_context_get_fuel(
268    store: WasmtimeStoreContext<'_>,
269    fuel: &mut u64,
270) -> Option<Box<wasmtime_error_t>> {
271    crate::handle_result(store.get_fuel(), |amt| {
272        *fuel = amt;
273    })
274}
275
276#[unsafe(no_mangle)]
277pub extern "C" fn wasmtime_context_set_epoch_deadline(
278    mut store: WasmtimeStoreContextMut<'_>,
279    ticks_beyond_current: u64,
280) {
281    store.set_epoch_deadline(ticks_beyond_current);
282}