use super::{typeid, CallHooks, FuncInOut, HostFuncEntity, StoreInner};
use crate::{
core::{hint, UntypedVal},
errors::{MemoryError, TableError},
store::error::{InternalStoreError, StoreError},
CallHook,
Error,
Instance,
Memory,
Store,
Table,
};
use core::{
fmt::{self, Debug},
mem,
};
#[cfg(test)]
use crate::Engine;
#[allow(clippy::type_complexity)]
#[derive(Copy, Clone)]
pub struct PrunedStoreVTable {
call_host_func: fn(
store: &mut PrunedStore,
func: &HostFuncEntity,
instance: Option<&Instance>,
params_results: FuncInOut,
call_hooks: CallHooks,
) -> Result<(), StoreError<Error>>,
grow_memory:
fn(&mut PrunedStore, memory: &Memory, delta: u64) -> Result<u64, StoreError<MemoryError>>,
grow_table: fn(
&mut PrunedStore,
table: &Table,
delta: u64,
init: UntypedVal,
) -> Result<u64, StoreError<TableError>>,
}
impl PrunedStoreVTable {
pub fn new<T>() -> Self {
Self {
call_host_func: |pruned: &mut PrunedStore,
func: &HostFuncEntity,
instance: Option<&Instance>,
params_results: FuncInOut,
call_hooks: CallHooks|
-> Result<(), StoreError<Error>> {
let store: &mut Store<T> = pruned.restore()?;
if matches!(call_hooks, CallHooks::Call) {
store
.invoke_call_hook(CallHook::CallingHost)
.map_err(StoreError::external)?;
}
store.call_host_func(func, instance, params_results)?;
if matches!(call_hooks, CallHooks::Call) {
store
.invoke_call_hook(CallHook::ReturningFromHost)
.map_err(StoreError::external)?;
}
Ok(())
},
grow_memory: |pruned: &mut PrunedStore,
memory: &Memory,
delta: u64|
-> Result<u64, StoreError<MemoryError>> {
let store: &mut Store<T> = pruned.restore()?;
let (store, mut resource_limiter) = store.store_inner_and_resource_limiter_ref();
let (memory, fuel) = store.try_resolve_memory_and_fuel_mut(memory)?;
memory
.grow(delta, Some(fuel), &mut resource_limiter)
.map_err(StoreError::external)
},
grow_table: |pruned: &mut PrunedStore,
table: &Table,
delta: u64,
init: UntypedVal|
-> Result<u64, StoreError<TableError>> {
let store: &mut Store<T> = pruned.restore()?;
let (store, mut resource_limiter) = store.store_inner_and_resource_limiter_ref();
let (table, fuel) = store.try_resolve_table_and_fuel_mut(table)?;
table
.grow_untyped(delta, init, Some(fuel), &mut resource_limiter)
.map_err(StoreError::external)
},
}
}
}
impl PrunedStoreVTable {
#[inline]
fn call_host_func(
&self,
pruned: &mut PrunedStore,
func: &HostFuncEntity,
instance: Option<&Instance>,
params_results: FuncInOut,
call_hooks: CallHooks,
) -> Result<(), StoreError<Error>> {
(self.call_host_func)(pruned, func, instance, params_results, call_hooks)
}
#[inline]
fn grow_memory(
&self,
pruned: &mut PrunedStore,
memory: &Memory,
delta: u64,
) -> Result<u64, StoreError<MemoryError>> {
(self.grow_memory)(pruned, memory, delta)
}
#[inline]
fn grow_table(
&self,
pruned: &mut PrunedStore,
table: &Table,
delta: u64,
init: UntypedVal,
) -> Result<u64, StoreError<TableError>> {
(self.grow_table)(pruned, table, delta, init)
}
}
impl Debug for PrunedStoreVTable {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "RestorePrunedWrapper")
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct PrunedStore {
pruned: Store<Pruned>,
}
#[derive(Debug)]
pub struct Pruned;
impl<'a, T> From<&'a mut Store<T>> for &'a mut PrunedStore {
#[inline]
fn from(store: &'a mut Store<T>) -> Self {
unsafe { mem::transmute::<&'a mut Store<T>, &'a mut PrunedStore>(store) }
}
}
impl<T> Store<T> {
#[inline]
pub(crate) fn prune(&mut self) -> &mut PrunedStore {
self.into()
}
}
impl PrunedStore {
#[inline]
pub fn inner(&self) -> &StoreInner {
&self.pruned.inner
}
#[inline]
pub fn inner_mut(&mut self) -> &mut StoreInner {
&mut self.pruned.inner
}
#[inline]
pub fn call_host_func(
&mut self,
func: &HostFuncEntity,
instance: Option<&Instance>,
params_results: FuncInOut,
call_hooks: CallHooks,
) -> Result<(), StoreError<Error>> {
self.pruned.restore_pruned.clone().call_host_func(
self,
func,
instance,
params_results,
call_hooks,
)
}
#[inline]
pub fn grow_memory(
&mut self,
memory: &Memory,
delta: u64,
) -> Result<u64, StoreError<MemoryError>> {
self.pruned
.restore_pruned
.clone()
.grow_memory(self, memory, delta)
}
#[inline]
pub fn grow_table(
&mut self,
table: &Table,
delta: u64,
init: UntypedVal,
) -> Result<u64, StoreError<TableError>> {
self.pruned
.restore_pruned
.clone()
.grow_table(self, table, delta, init)
}
#[inline]
fn restore<T>(&mut self) -> Result<&mut Store<T>, InternalStoreError> {
if hint::unlikely(typeid::of::<T>() != self.pruned.id) {
return Err(InternalStoreError::restore_type_mismatch::<T>());
}
let store = {
unsafe { mem::transmute::<&mut PrunedStore, &mut Store<T>>(self) }
};
Ok(store)
}
}
#[test]
fn pruning_works() {
let engine = Engine::default();
let mut store = Store::new(&engine, ());
let pruned = store.prune();
assert!(pruned.restore::<()>().is_ok());
}
#[test]
fn pruning_errors() {
let engine = Engine::default();
let mut store = Store::new(&engine, ());
let pruned = store.prune();
assert!(pruned.restore::<i32>().is_err());
}
#[test]
fn pruned_store_deref() {
let mut config = crate::Config::default();
config.consume_fuel(true);
let engine = Engine::new(&config);
let mut store = Store::new(&engine, ());
let fuel_amount = 100;
store.set_fuel(fuel_amount).unwrap();
let pruned = store.prune();
assert_eq!(
PrunedStore::inner(pruned).fuel.get_fuel().unwrap(),
fuel_amount
);
PrunedStore::inner_mut(pruned)
.fuel
.set_fuel(fuel_amount * 2)
.unwrap();
assert_eq!(
PrunedStore::inner(pruned).fuel.get_fuel().unwrap(),
fuel_amount * 2
);
}
#[test]
fn equal_size() {
use super::TypedStoreInner;
type SmallType = ();
type BigType = [i64; 16];
use core::mem::size_of;
assert_eq!(size_of::<Store<SmallType>>(), size_of::<Store<BigType>>(),);
assert_eq!(
size_of::<TypedStoreInner<SmallType>>(),
size_of::<TypedStoreInner<BigType>>(),
);
}