use tinywasm_types::ModuleInstanceId;
use crate::{Error, FromWasmValues, FuncRef, Function, FunctionTyped, IntoWasmValues, Result, WasmValue};
#[cfg_attr(feature = "debug", derive(core::fmt::Debug))]
pub struct FuncContext<'a> {
pub(crate) store: &'a mut crate::Store,
pub(crate) module_id: ModuleInstanceId,
}
impl FuncContext<'_> {
pub fn store(&self) -> &crate::Store {
self.store
}
pub fn store_mut(&mut self) -> &mut crate::Store {
self.store
}
pub fn module(&self) -> &crate::ModuleInstance {
self.store.get_module_instance(self.module_id).expect("invalid module instance id in host function context")
}
pub fn memory(&self, name: &str) -> Result<crate::Memory> {
self.module().memory(name)
}
pub fn extern_item(&self, name: &str) -> Result<crate::ExternItem> {
self.module().extern_item(name)
}
pub fn table(&self, name: &str) -> Result<crate::Table> {
self.module().table(name)
}
pub fn global_get(&mut self, name: &str) -> Result<WasmValue> {
self.module().clone().global_get(self.store, name)
}
pub fn global(&self, name: &str) -> Result<crate::Global> {
self.module().global(name)
}
pub fn global_set(&mut self, name: &str, value: WasmValue) -> Result<()> {
self.module().clone().global_set(self.store, name, value)
}
pub fn charge_fuel(&mut self, fuel: u32) {
self.store.execution_fuel = self.store.execution_fuel.saturating_sub(fuel);
}
pub fn remaining_fuel(&self) -> u32 {
self.store.execution_fuel
}
pub fn call_untyped(&mut self, func: &Function, args: &[WasmValue], results: &mut [WasmValue]) -> Result<()> {
if !self.store.execution_active {
return Err(Error::other("FuncContext::call requires an active host-function invocation"));
}
let type_addr = func.validate_call(self.store, args, results.len())?;
let call_stack_base = self.store.call_stack.len();
let value_stack_base = self.store.value_stack.base();
self.with_reentrant_call(|store| {
func.call_untyped(store, type_addr, args, results, call_stack_base, value_stack_base)
})
}
pub fn call_ref(&mut self, func: FuncRef, args: &[WasmValue], results: &mut [WasmValue]) -> Result<()> {
let addr = func.addr(self.store.id()).ok_or(crate::Trap::InvalidStore)?;
if !self.store.state.funcs.contains(addr) {
return Err(crate::Trap::InvalidReference.into());
}
let function = Function { item: crate::StoreItem::new(self.store.id(), addr), module_id: self.module_id };
self.call_untyped(&function, args, results)
}
pub fn call<P, R>(&mut self, func: &FunctionTyped<P, R>, params: P) -> Result<R>
where
P: IntoWasmValues,
R: FromWasmValues,
{
if !self.store.execution_active {
return Err(Error::other("FuncContext::call requires an active host-function invocation"));
}
func.func.item.validate_store(self.store)?;
let call_stack_base = self.store.call_stack.len();
let value_stack_base = self.store.value_stack.base();
self.with_reentrant_call(|store| {
func.func.call_typed(store, params.into_wasm_values(), call_stack_base, value_stack_base)
})
}
fn with_reentrant_call<R>(&mut self, call: impl FnOnce(&mut crate::Store) -> Result<R>) -> Result<R> {
let config = self.store.engine.config().call_stack;
let limit = if config.dynamic { config.max_size } else { config.initial_size };
if self.store.reentrant_call_depth >= limit {
return Err(crate::Trap::CallStackOverflow.into());
}
self.store.reentrant_call_depth += 1;
let result = call(self.store);
self.store.reentrant_call_depth -= 1;
result
}
}
impl core::ops::Deref for FuncContext<'_> {
type Target = crate::Store;
fn deref(&self) -> &Self::Target {
self.store
}
}
impl core::ops::DerefMut for FuncContext<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.store
}
}