mod caller;
mod error;
mod into_func;
mod typed_func;
pub(crate) use self::typed_func::CallResultsTuple;
pub use self::{
caller::Caller,
error::FuncError,
into_func::{IntoFunc, WasmRet, WasmType, WasmTypeList},
typed_func::{TypedFunc, WasmParams, WasmResults},
};
use super::{
engine::{DedupFuncType, FuncBody, FuncParams, FuncResults},
AsContext,
AsContextMut,
Instance,
StoreContext,
Stored,
};
use crate::{core::Trap, engine::ResumableCall, Error, FuncType, Value};
use alloc::sync::Arc;
use core::{fmt, fmt::Debug, num::NonZeroU32};
use wasmi_arena::ArenaIndex;
use wasmi_core::UntypedValue;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct FuncIdx(NonZeroU32);
impl ArenaIndex for FuncIdx {
fn into_usize(self) -> usize {
self.0.get().wrapping_sub(1) as usize
}
fn from_usize(index: usize) -> Self {
index
.try_into()
.ok()
.map(|index: u32| index.wrapping_add(1))
.and_then(NonZeroU32::new)
.map(Self)
.unwrap_or_else(|| panic!("out of bounds func index {index}"))
}
}
#[derive(Debug)]
pub struct FuncEntity<T> {
internal: FuncEntityInternal<T>,
}
impl<T> Clone for FuncEntity<T> {
fn clone(&self) -> Self {
Self {
internal: self.internal.clone(),
}
}
}
impl<T> FuncEntity<T> {
pub(crate) fn new_wasm(signature: DedupFuncType, body: FuncBody, instance: Instance) -> Self {
Self {
internal: FuncEntityInternal::Wasm(WasmFuncEntity::new(signature, body, instance)),
}
}
pub fn wrap<Params, Results>(
ctx: impl AsContextMut<UserState = T>,
func: impl IntoFunc<T, Params, Results>,
) -> Self {
Self {
internal: FuncEntityInternal::Host(HostFuncEntity::wrap(ctx, func)),
}
}
pub(crate) fn as_internal(&self) -> &FuncEntityInternal<T> {
&self.internal
}
pub fn ty_dedup(&self) -> &DedupFuncType {
match self.as_internal() {
FuncEntityInternal::Wasm(func) => func.ty_dedup(),
FuncEntityInternal::Host(func) => func.ty_dedup(),
}
}
}
#[derive(Debug)]
pub(crate) enum FuncEntityInternal<T> {
Wasm(WasmFuncEntity),
Host(HostFuncEntity<T>),
}
impl<T> Clone for FuncEntityInternal<T> {
fn clone(&self) -> Self {
match self {
Self::Wasm(func) => Self::Wasm(func.clone()),
Self::Host(func) => Self::Host(func.clone()),
}
}
}
#[derive(Debug, Clone)]
pub(crate) struct WasmFuncEntity {
signature: DedupFuncType,
body: FuncBody,
instance: Instance,
}
impl WasmFuncEntity {
pub fn new(signature: DedupFuncType, body: FuncBody, instance: Instance) -> Self {
Self {
signature,
body,
instance,
}
}
pub fn ty_dedup(&self) -> &DedupFuncType {
&self.signature
}
pub fn instance(&self) -> Instance {
self.instance
}
pub fn func_body(&self) -> FuncBody {
self.body
}
}
pub(crate) struct HostFuncEntity<T> {
signature: DedupFuncType,
trampoline: HostFuncTrampoline<T>,
}
impl<T> Clone for HostFuncEntity<T> {
fn clone(&self) -> Self {
Self {
signature: self.signature,
trampoline: self.trampoline.clone(),
}
}
}
type HostFuncTrampolineFn<T> =
dyn Fn(Caller<T>, FuncParams) -> Result<FuncResults, Trap> + Send + Sync + 'static;
pub struct HostFuncTrampoline<T> {
closure: Arc<HostFuncTrampolineFn<T>>,
}
impl<T> HostFuncTrampoline<T> {
pub fn new<F>(trampoline: F) -> Self
where
F: Fn(Caller<T>, FuncParams) -> Result<FuncResults, Trap> + Send + Sync + 'static,
{
Self {
closure: Arc::new(trampoline),
}
}
}
impl<T> Clone for HostFuncTrampoline<T> {
fn clone(&self) -> Self {
Self {
closure: self.closure.clone(),
}
}
}
impl<T> Debug for HostFuncEntity<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(&self.signature, f)
}
}
impl<T> HostFuncEntity<T> {
pub fn wrap<Params, Results>(
mut ctx: impl AsContextMut,
func: impl IntoFunc<T, Params, Results>,
) -> Self {
let (signature, trampoline) = func.into_func();
let signature = ctx.as_context_mut().store.inner.alloc_func_type(signature);
Self {
signature,
trampoline,
}
}
pub fn ty_dedup(&self) -> &DedupFuncType {
&self.signature
}
pub fn call(
&self,
mut ctx: impl AsContextMut<UserState = T>,
instance: Option<&Instance>,
params: FuncParams,
) -> Result<FuncResults, Trap> {
let caller = <Caller<T>>::new(&mut ctx, instance);
(self.trampoline.closure)(caller, params)
}
}
#[derive(Debug, Copy, Clone)]
#[repr(transparent)]
pub struct Func(Stored<FuncIdx>);
impl Func {
pub(super) fn from_inner(stored: Stored<FuncIdx>) -> Self {
Self(stored)
}
pub(super) fn as_inner(&self) -> &Stored<FuncIdx> {
&self.0
}
pub fn wrap<T, Params, Results>(
mut ctx: impl AsContextMut<UserState = T>,
func: impl IntoFunc<T, Params, Results>,
) -> Self {
let func = FuncEntity::wrap(ctx.as_context_mut(), func);
ctx.as_context_mut().store.alloc_func(func)
}
pub(crate) fn ty_dedup<'a, T: 'a>(
&self,
ctx: impl Into<StoreContext<'a, T>>,
) -> &'a DedupFuncType {
ctx.into().store.resolve_func(self).ty_dedup()
}
pub fn ty(&self, ctx: impl AsContext) -> FuncType {
ctx.as_context()
.store
.inner
.resolve_func_type(self.ty_dedup(&ctx))
}
pub fn call<T>(
&self,
mut ctx: impl AsContextMut<UserState = T>,
inputs: &[Value],
outputs: &mut [Value],
) -> Result<(), Error> {
self.verify_and_prepare_inputs_outputs(ctx.as_context(), inputs, outputs)?;
ctx.as_context().store.engine().clone().execute_func(
ctx.as_context_mut(),
*self,
inputs,
outputs,
)?;
Ok(())
}
pub fn call_resumable<T>(
&self,
mut ctx: impl AsContextMut<UserState = T>,
inputs: &[Value],
outputs: &mut [Value],
) -> Result<ResumableCall, Error> {
self.verify_and_prepare_inputs_outputs(ctx.as_context(), inputs, outputs)?;
ctx.as_context()
.store
.engine()
.clone()
.execute_func_resumable(ctx.as_context_mut(), *self, inputs, outputs)
.map_err(Into::into)
.map(ResumableCall::new)
}
fn verify_and_prepare_inputs_outputs(
&self,
ctx: impl AsContext,
inputs: &[Value],
outputs: &mut [Value],
) -> Result<(), FuncError> {
let fn_type = self.ty_dedup(ctx.as_context());
ctx.as_context()
.store
.inner
.resolve_func_type_with(fn_type, |func_type| {
func_type.match_params(inputs)?;
func_type.match_results(outputs, false)?;
func_type.prepare_outputs(outputs);
Ok(())
})
}
pub fn typed<Params, Results>(
&self,
ctx: impl AsContext,
) -> Result<TypedFunc<Params, Results>, Error>
where
Params: WasmParams,
Results: WasmResults,
{
TypedFunc::new(ctx, *self)
}
pub(crate) fn as_internal<'a, T: 'a>(
&self,
ctx: impl Into<StoreContext<'a, T>>,
) -> &'a FuncEntityInternal<T> {
ctx.into().store.resolve_func(self).as_internal()
}
}
#[derive(Debug, Default, Copy, Clone)]
#[repr(transparent)]
pub struct FuncRef {
inner: Option<Func>,
}
impl From<Func> for FuncRef {
fn from(func: Func) -> Self {
Self::new(func)
}
}
union Transposer {
funcref: FuncRef,
untyped: UntypedValue,
}
#[test]
fn funcref_sizeof() {
use core::mem::size_of;
assert_eq!(size_of::<Func>(), size_of::<UntypedValue>());
assert_eq!(size_of::<Func>(), size_of::<FuncRef>());
}
#[test]
fn funcref_null_to_zero() {
assert_eq!(UntypedValue::from(FuncRef::null()), UntypedValue::from(0));
assert!(FuncRef::from(UntypedValue::from(0)).is_null());
}
impl From<UntypedValue> for FuncRef {
fn from(untyped: UntypedValue) -> Self {
unsafe { Transposer { untyped }.funcref }.canonicalize()
}
}
impl From<FuncRef> for UntypedValue {
fn from(funcref: FuncRef) -> Self {
let funcref = funcref.canonicalize();
unsafe { Transposer { funcref }.untyped }
}
}
impl FuncRef {
pub fn is_null(&self) -> bool {
self.inner.is_none()
}
fn canonicalize(self) -> Self {
if self.is_null() {
return unsafe {
Transposer {
untyped: UntypedValue::from(0u64),
}
.funcref
};
}
self
}
pub fn new(nullable_func: impl Into<Option<Func>>) -> Self {
Self {
inner: nullable_func.into(),
}
.canonicalize()
}
pub fn func(&self) -> Option<&Func> {
self.inner.as_ref()
}
pub fn null() -> Self {
Self::new(None).canonicalize()
}
}