use crate::{wasm_engine_t, wasmtime_error_t, ForeignData}; use std::cell::UnsafeCell; use std::ffi::c_void; use std::sync::Arc; use wasmtime::{AsContext, AsContextMut, InterruptHandle, Store, StoreContext, StoreContextMut}; /// This representation of a `Store` is used to implement the `wasm.h` API. /// /// This is stored alongside `Func` and such for `wasm.h` so each object is /// independently owned. The usage of `Arc` here is mostly to just get it to be /// safe to drop across multiple threads, but otherwise acquiring the `context` /// values from this struct is considered unsafe due to it being unknown how the /// aliasing is working on the C side of things. /// /// The aliasing requirements are documented in the C API `wasm.h` itself (at /// least Wasmtime's implementation). #[derive(Clone)] pub struct StoreRef { store: Arc>>, } impl StoreRef { pub unsafe fn context(&self) -> StoreContext<'_, ()> { (*self.store.get()).as_context() } pub unsafe fn context_mut(&mut self) -> StoreContextMut<'_, ()> { (*self.store.get()).as_context_mut() } } #[repr(C)] #[derive(Clone)] pub struct wasm_store_t { pub(crate) store: StoreRef, } wasmtime_c_api_macros::declare_own!(wasm_store_t); #[no_mangle] pub extern "C" fn wasm_store_new(engine: &wasm_engine_t) -> Box { let engine = &engine.engine; let store = Store::new(engine, ()); Box::new(wasm_store_t { store: StoreRef { store: Arc::new(UnsafeCell::new(store)), }, }) } /// Representation of a `Store` for `wasmtime.h` This notably tries to move more /// burden of aliasing on the caller rather than internally, allowing for a more /// raw representation of contexts and such that requires less `unsafe` in the /// implementation. /// /// Note that this notably carries `StoreData` as a payload which allows storing /// foreign data and configuring WASI as well. #[repr(C)] pub struct wasmtime_store_t { pub(crate) store: Store, } pub type CStoreContext<'a> = StoreContext<'a, StoreData>; pub type CStoreContextMut<'a> = StoreContextMut<'a, StoreData>; pub struct StoreData { foreign: crate::ForeignData, #[cfg(feature = "wasi")] pub(crate) wasi: Option, } #[no_mangle] pub extern "C" fn wasmtime_store_delete(_: Box) {} #[no_mangle] pub extern "C" fn wasmtime_store_new( engine: &wasm_engine_t, data: *mut c_void, finalizer: Option, ) -> Box { Box::new(wasmtime_store_t { store: Store::new( &engine.engine, StoreData { foreign: ForeignData { data, finalizer }, #[cfg(feature = "wasi")] wasi: None, }, ), }) } #[no_mangle] pub extern "C" fn wasmtime_store_context(store: &mut wasmtime_store_t) -> CStoreContextMut<'_> { store.store.as_context_mut() } #[no_mangle] pub extern "C" fn wasmtime_context_get_data(store: CStoreContext<'_>) -> *mut c_void { store.data().foreign.data } #[no_mangle] pub extern "C" fn wasmtime_context_set_data(mut store: CStoreContextMut<'_>, data: *mut c_void) { store.data_mut().foreign.data = data; } #[cfg(feature = "wasi")] #[no_mangle] pub extern "C" fn wasmtime_context_set_wasi( mut context: CStoreContextMut<'_>, wasi: Box, ) -> Option> { crate::handle_result(wasi.into_wasi_ctx(), |wasi| { context.data_mut().wasi = Some(wasi); }) } #[no_mangle] pub extern "C" fn wasmtime_context_gc(mut context: CStoreContextMut<'_>) { context.gc(); } #[no_mangle] pub extern "C" fn wasmtime_context_add_fuel( mut store: CStoreContextMut<'_>, fuel: u64, ) -> Option> { crate::handle_result(store.add_fuel(fuel), |()| {}) } #[no_mangle] pub extern "C" fn wasmtime_context_fuel_consumed(store: CStoreContext<'_>, fuel: &mut u64) -> bool { match store.fuel_consumed() { Some(amt) => { *fuel = amt; true } None => false, } } #[repr(C)] pub struct wasmtime_interrupt_handle_t { handle: InterruptHandle, } #[no_mangle] pub extern "C" fn wasmtime_interrupt_handle_new( store: CStoreContext<'_>, ) -> Option> { Some(Box::new(wasmtime_interrupt_handle_t { handle: store.interrupt_handle().ok()?, })) } #[no_mangle] pub extern "C" fn wasmtime_interrupt_handle_interrupt(handle: &wasmtime_interrupt_handle_t) { handle.handle.interrupt(); } #[no_mangle] pub extern "C" fn wasmtime_interrupt_handle_delete(_: Box) {}