mod accessors;
mod arena;
mod context;
mod entity;
mod gc;
mod gc_codec;
mod gc_collect;
mod grow;
mod inner;
mod limits;
mod managed;
mod reclaim;
pub use context::{AsContext, AsContextMut, StoreContext, StoreContextMut};
pub(crate) use entity::HostSig;
pub(crate) use entity::{
exn_charge, ExnEntity, FuncEntity, GlobalEntity, InstanceEntity, MemoryEntity, TableEntity,
TagEntity, PAGE_SIZE,
};
pub(crate) use gc::{
anyref_handle_i31, anyref_handle_slot, anyref_value, decode_anyref_handle, AnyRefHandle,
GcObject, ObjKind,
};
pub(crate) use gc_codec::{
default_for_slot, read_slot, read_slot_packed, slot_accepts, write_slot, NULL_REF,
};
pub(crate) use inner::{FuelStep, StoreInner};
#[cfg(feature = "async")]
pub use limits::ResourceLimiterAsync;
pub use limits::{ResourceLimiter, StoreLimits, StoreLimitsBuilder};
use std::sync::Arc;
use crate::engine::Engine;
use crate::exception::ThrownException;
use crate::func::Caller;
use crate::value::{ExnRef, Rooted, Val};
use crate::{Error, Result};
pub(crate) enum ResourceLimiterInner<T> {
Sync(Box<dyn FnMut(&mut T) -> &mut (dyn ResourceLimiter) + Send + Sync>),
#[cfg(feature = "async")]
Async(Box<dyn FnMut(&mut T) -> &mut (dyn ResourceLimiterAsync) + Send + Sync>),
}
pub(crate) type HostFunc<T> =
Arc<dyn Fn(Caller<'_, T>, &[Val], &mut [Val]) -> Result<()> + Send + Sync>;
#[cfg(feature = "async")]
pub(crate) type AsyncHostFunc<T> = Arc<
dyn for<'a> Fn(
Caller<'a, T>,
&'a [Val],
&'a mut [Val],
)
-> std::boxed::Box<dyn std::future::Future<Output = Result<()>> + Send + 'a>
+ Send
+ Sync,
>;
pub(crate) type EpochCallback<T> =
Box<dyn FnMut(StoreContextMut<'_, T>) -> Result<UpdateDeadline> + Send + Sync>;
pub struct Store<T: 'static> {
pub(crate) inner: StoreInner,
pub(crate) data: T,
pub(crate) host_funcs: Vec<HostFunc<T>>,
#[cfg(feature = "async")]
pub(crate) async_host_funcs: Vec<AsyncHostFunc<T>>,
pub(crate) epoch_callback: Option<EpochCallback<T>>,
pub(crate) limiter: Option<ResourceLimiterInner<T>>,
}
impl<T: 'static> core::fmt::Debug for Store<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Store").finish_non_exhaustive()
}
}
impl<T: 'static> Store<T> {
pub fn new(engine: &Engine, data: T) -> Self {
Store {
inner: StoreInner::new(engine.clone()),
data,
host_funcs: Vec::new(),
#[cfg(feature = "async")]
async_host_funcs: Vec::new(),
epoch_callback: None,
limiter: None,
}
}
pub(crate) fn push_host_func(&mut self, f: HostFunc<T>) -> u32 {
let index = self.host_funcs.len() as u32;
self.host_funcs.push(f);
index
}
#[cfg(feature = "async")]
pub(crate) fn push_async_host_func(&mut self, f: AsyncHostFunc<T>) -> u32 {
let index = self.async_host_funcs.len() as u32;
self.async_host_funcs.push(f);
index
}
pub fn data(&self) -> &T {
&self.data
}
pub fn data_mut(&mut self) -> &mut T {
&mut self.data
}
pub fn into_data(self) -> T {
self.data
}
pub fn engine(&self) -> &Engine {
self.inner.engine()
}
pub fn gc_heap_capacity(&self) -> usize {
self.inner.gc.byte_size()
}
pub fn gc(&mut self) {
let roots = self.inner.exec_roots();
self.inner.gc_collect(&roots);
}
pub fn throw<R>(
&mut self,
exception: Rooted<ExnRef>,
) -> core::result::Result<R, ThrownException> {
self.inner.set_pending_exception(exception);
Err(ThrownException)
}
pub fn take_pending_exception(&mut self) -> Option<Rooted<ExnRef>> {
self.inner.take_pending_exception()
}
pub fn set_fuel(&mut self, fuel: u64) -> Result<()> {
if !self.inner.engine().consume_fuel() {
return Err(Error::msg(
"fuel is not configured; set `Config::consume_fuel(true)`",
));
}
self.inner.set_fuel(fuel);
Ok(())
}
pub fn get_fuel(&self) -> Result<u64> {
if !self.inner.engine().consume_fuel() {
return Err(Error::msg(
"fuel is not configured; set `Config::consume_fuel(true)`",
));
}
Ok(self.inner.fuel())
}
pub fn set_epoch_deadline(&mut self, ticks_beyond_current: u64) {
let deadline = self
.inner
.engine()
.current_epoch()
.saturating_add(ticks_beyond_current);
self.inner.set_epoch_deadline(deadline);
}
pub fn epoch_deadline_trap(&mut self) {
self.epoch_callback = None;
}
pub fn epoch_deadline_callback(
&mut self,
callback: impl FnMut(StoreContextMut<'_, T>) -> Result<UpdateDeadline> + Send + Sync + 'static,
) {
self.epoch_callback = Some(Box::new(callback));
}
pub fn limiter(
&mut self,
limiter: impl (FnMut(&mut T) -> &mut dyn ResourceLimiter) + Send + Sync + 'static,
) {
self.limiter = Some(ResourceLimiterInner::Sync(Box::new(limiter)));
}
#[cfg(feature = "async")]
pub fn limiter_async(
&mut self,
limiter: impl (FnMut(&mut T) -> &mut dyn ResourceLimiterAsync) + Send + Sync + 'static,
) {
self.limiter = Some(ResourceLimiterInner::Async(Box::new(limiter)));
}
pub fn as_context(&self) -> StoreContext<'_, T> {
StoreContext(self)
}
pub fn as_context_mut(&mut self) -> StoreContextMut<'_, T> {
StoreContextMut(self)
}
#[cfg(feature = "async")]
pub fn fuel_async_yield_interval(&mut self, interval: Option<u64>) -> Result<()> {
if !self.inner.engine().consume_fuel() {
return Err(Error::msg(
"fuel is not configured; set `Config::consume_fuel(true)`",
));
}
if interval == Some(0) {
return Err(Error::msg("fuel_async_yield_interval must not be 0"));
}
if !self.inner.engine().is_async() {
return Err(Error::msg(
"fuel_async_yield_interval requires `Config::async_support(true)`",
));
}
self.inner.set_fuel_yield_interval(interval);
Ok(())
}
#[cfg(feature = "async")]
pub fn epoch_deadline_async_yield_and_update(&mut self, delta: u64) {
self.epoch_callback = Some(Box::new(move |_| Ok(UpdateDeadline::Yield(delta))));
}
}
#[non_exhaustive]
#[derive(Debug)]
pub enum UpdateDeadline {
Interrupt,
Continue(u64),
#[cfg(feature = "async")]
Yield(u64),
}
#[derive(Copy, Clone, Debug)]
pub enum CallHook {
CallingWasm,
ReturningFromWasm,
CallingHost,
ReturningFromHost,
}