use crate::{wasm_engine_t, wasmtime_error_t, wasmtime_val_t, ForeignData};
use std::cell::UnsafeCell;
use std::ffi::c_void;
use std::sync::Arc;
use wasmtime::{
AsContext, AsContextMut, Caller, Store, StoreContext, StoreContextMut, StoreLimits,
StoreLimitsBuilder, UpdateDeadline, Val,
};
pub type WasmStoreData = ();
pub type WasmStore = Store<WasmStoreData>;
pub type WasmStoreContext<'a> = StoreContext<'a, WasmStoreData>;
pub type WasmStoreContextMut<'a> = StoreContextMut<'a, WasmStoreData>;
#[derive(Clone)]
pub struct WasmStoreRef {
store: Arc<UnsafeCell<WasmStore>>,
}
impl WasmStoreRef {
pub unsafe fn context(&self) -> WasmStoreContext<'_> {
(*self.store.get()).as_context()
}
pub unsafe fn context_mut(&mut self) -> WasmStoreContextMut<'_> {
(*self.store.get()).as_context_mut()
}
}
#[repr(C)]
#[derive(Clone)]
pub struct wasm_store_t {
pub(crate) store: WasmStoreRef,
}
wasmtime_c_api_macros::declare_own!(wasm_store_t);
#[no_mangle]
pub extern "C" fn wasm_store_new(engine: &wasm_engine_t) -> Box<wasm_store_t> {
let engine = &engine.engine;
let store = Store::new(engine, ());
Box::new(wasm_store_t {
store: WasmStoreRef {
store: Arc::new(UnsafeCell::new(store)),
},
})
}
pub type WasmtimeStore = Store<WasmtimeStoreData>;
pub type WasmtimeStoreContext<'a> = StoreContext<'a, WasmtimeStoreData>;
pub type WasmtimeStoreContextMut<'a> = StoreContextMut<'a, WasmtimeStoreData>;
pub type WasmtimeCaller<'a> = Caller<'a, WasmtimeStoreData>;
#[repr(C)]
pub struct wasmtime_store_t {
pub(crate) store: WasmtimeStore,
}
wasmtime_c_api_macros::declare_own!(wasmtime_store_t);
pub struct WasmtimeStoreData {
foreign: crate::ForeignData,
#[cfg(feature = "wasi")]
pub(crate) wasi: Option<wasmtime_wasi::preview1::WasiP1Ctx>,
pub hostcall_val_storage: Vec<wasmtime_val_t>,
pub wasm_val_storage: Vec<Val>,
pub store_limits: StoreLimits,
}
#[no_mangle]
pub extern "C" fn wasmtime_store_new(
engine: &wasm_engine_t,
data: *mut c_void,
finalizer: Option<extern "C" fn(*mut c_void)>,
) -> Box<wasmtime_store_t> {
Box::new(wasmtime_store_t {
store: Store::new(
&engine.engine,
WasmtimeStoreData {
foreign: ForeignData { data, finalizer },
#[cfg(feature = "wasi")]
wasi: None,
hostcall_val_storage: Vec::new(),
wasm_val_storage: Vec::new(),
store_limits: StoreLimits::default(),
},
),
})
}
pub type wasmtime_update_deadline_kind_t = u8;
pub const WASMTIME_UPDATE_DEADLINE_CONTINUE: wasmtime_update_deadline_kind_t = 0;
pub const WASMTIME_UPDATE_DEADLINE_YIELD: wasmtime_update_deadline_kind_t = 1;
#[no_mangle]
pub extern "C" fn wasmtime_store_epoch_deadline_callback(
store: &mut wasmtime_store_t,
func: extern "C" fn(
WasmtimeStoreContextMut<'_>,
*mut c_void,
*mut u64,
*mut wasmtime_update_deadline_kind_t,
) -> Option<Box<wasmtime_error_t>>,
data: *mut c_void,
finalizer: Option<extern "C" fn(*mut c_void)>,
) {
let foreign = crate::ForeignData { data, finalizer };
store.store.epoch_deadline_callback(move |mut store_ctx| {
let _ = &foreign; let mut delta: u64 = 0;
let mut kind = WASMTIME_UPDATE_DEADLINE_CONTINUE;
let result = (func)(
store_ctx.as_context_mut(),
foreign.data,
&mut delta as *mut u64,
&mut kind as *mut wasmtime_update_deadline_kind_t,
);
match result {
Some(err) => Err(wasmtime::Error::from(<wasmtime_error_t as Into<
anyhow::Error,
>>::into(*err))),
None if kind == WASMTIME_UPDATE_DEADLINE_CONTINUE => {
Ok(UpdateDeadline::Continue(delta))
}
#[cfg(feature = "async")]
None if kind == WASMTIME_UPDATE_DEADLINE_YIELD => Ok(UpdateDeadline::Yield(delta)),
_ => panic!("unknown wasmtime_update_deadline_kind_t: {kind}"),
}
});
}
#[no_mangle]
pub extern "C" fn wasmtime_store_context(
store: &mut wasmtime_store_t,
) -> WasmtimeStoreContextMut<'_> {
store.store.as_context_mut()
}
#[no_mangle]
pub extern "C" fn wasmtime_store_limiter(
store: &mut wasmtime_store_t,
memory_size: i64,
table_elements: i64,
instances: i64,
tables: i64,
memories: i64,
) {
let mut limiter = StoreLimitsBuilder::new();
if memory_size >= 0 {
limiter = limiter.memory_size(memory_size as usize);
}
if table_elements >= 0 {
limiter = limiter.table_elements(table_elements as u32);
}
if instances >= 0 {
limiter = limiter.instances(instances as usize);
}
if tables >= 0 {
limiter = limiter.tables(tables as usize);
}
if memories >= 0 {
limiter = limiter.memories(memories as usize);
}
store.store.data_mut().store_limits = limiter.build();
store.store.limiter(|data| &mut data.store_limits);
}
#[no_mangle]
pub extern "C" fn wasmtime_context_get_data(store: WasmtimeStoreContext<'_>) -> *mut c_void {
store.data().foreign.data
}
#[no_mangle]
pub extern "C" fn wasmtime_context_set_data(
mut store: WasmtimeStoreContextMut<'_>,
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: WasmtimeStoreContextMut<'_>,
wasi: Box<crate::wasi_config_t>,
) -> Option<Box<wasmtime_error_t>> {
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: WasmtimeStoreContextMut<'_>) {
context.gc();
}
#[no_mangle]
pub extern "C" fn wasmtime_context_set_fuel(
mut store: WasmtimeStoreContextMut<'_>,
fuel: u64,
) -> Option<Box<wasmtime_error_t>> {
crate::handle_result(store.set_fuel(fuel), |()| {})
}
#[no_mangle]
pub extern "C" fn wasmtime_context_get_fuel(
store: WasmtimeStoreContext<'_>,
fuel: &mut u64,
) -> Option<Box<wasmtime_error_t>> {
crate::handle_result(store.get_fuel(), |amt| {
*fuel = amt;
})
}
#[no_mangle]
pub extern "C" fn wasmtime_context_set_epoch_deadline(
mut store: WasmtimeStoreContextMut<'_>,
ticks_beyond_current: u64,
) {
store.set_epoch_deadline(ticks_beyond_current);
}