use super::{CallReq, Execution, ResolvedCall, StepOutcome};
use crate::canon::{CanonicalTypeId, RefKind};
use crate::func::Func;
use crate::instance::Instance;
use crate::store::{FuncEntity, StoreInner};
use crate::trap::Trap;
use crate::value::{FuncType, Ref};
use crate::Result;
#[derive(Clone, Copy)]
pub(super) enum CallKind {
Nested(u32),
Tail,
}
#[inline]
pub(super) fn resolve(inner: &StoreInner, f: Func) -> ResolvedCall {
match inner.func(f) {
FuncEntity::Wasm {
instance,
func_index,
} => {
let def_inst = *instance;
let module = inner.instance(def_inst).module.clone();
ResolvedCall::Wasm(def_inst, *func_index, module.code(*func_index))
}
FuncEntity::Host { .. } => ResolvedCall::Host(f),
#[cfg(feature = "async")]
FuncEntity::HostAsync { .. } => ResolvedCall::HostAsync(f),
}
}
impl Execution {
#[inline]
pub(super) fn do_call_indirect(
&mut self,
inner: &StoreInner,
instance: Instance,
type_idx: u32,
table: u32,
kind: CallKind,
) -> Result<StepOutcome> {
#[allow(clippy::indexing_slicing)]
let handle = inner.instance(instance).tables[table as usize];
let idx = self.pop_index(inner.table(handle).ty.is_64())?;
let f = match inner.table(handle).get(idx) {
Some(Ref::Func(Some(f))) => f,
Some(Ref::Func(None)) => return Err(Trap::IndirectCallToNull.into()),
Some(_) => return Err(Trap::BadSignature.into()),
None => return Err(Trap::TableOutOfBounds.into()),
};
let expected_module = inner.instance(instance).module.clone();
let expected_id = expected_module.inner().canonical_type_id(type_idx);
let resolved = resolve(inner, f);
match &resolved {
ResolvedCall::Wasm(def_inst, _, code) => {
let actual_id = inner
.instance(*def_inst)
.module
.inner()
.canonical_type_id(code.type_idx());
if !inner.engine().is_subtype(actual_id, expected_id) {
return Err(Trap::BadSignature.into());
}
}
ResolvedCall::Host(func) => host_sig_ok(inner, *func, expected_id)?,
#[cfg(feature = "async")]
ResolvedCall::HostAsync(func) => host_sig_ok(inner, *func, expected_id)?,
}
Ok(call_outcome(inner, resolved, instance, kind))
}
#[inline]
pub(super) fn do_call_ref(
&mut self,
inner: &StoreInner,
instance: Instance,
kind: CallKind,
) -> Result<StepOutcome> {
let f = match self.pop_ref(RefKind::Func)?.to_ref() {
Ref::Func(Some(f)) => f,
Ref::Func(None) => return Err(Trap::NullReference.into()),
_ => return Err(Trap::BadSignature.into()),
};
Ok(call_outcome(inner, resolve(inner, f), instance, kind))
}
}
#[inline]
pub(super) fn call_outcome(
inner: &StoreInner,
resolved: ResolvedCall,
instance: Instance,
kind: CallKind,
) -> StepOutcome {
let n_params = |func| host_ty(inner, func).params().len() as u32;
match resolved {
ResolvedCall::Wasm(def_inst, func_index, code) => {
let req = |return_ip| CallReq {
return_ip,
instance: def_inst,
func_index,
code: code.clone(),
};
match kind {
CallKind::Tail => StepOutcome::DoTailCall(req(0)),
CallKind::Nested(return_ip) => StepOutcome::DoCall(req(return_ip)),
}
}
ResolvedCall::Host(func) => match kind {
CallKind::Tail => StepOutcome::DoTailHostCall {
func,
instance,
n_params: n_params(func),
},
CallKind::Nested(return_ip) => StepOutcome::DoHostCall {
func,
instance,
return_ip,
},
},
#[cfg(feature = "async")]
ResolvedCall::HostAsync(func) => match kind {
CallKind::Tail => StepOutcome::DoTailHostAsyncCall {
func,
instance,
n_params: n_params(func),
},
CallKind::Nested(return_ip) => StepOutcome::DoHostAsyncCall {
func,
instance,
return_ip,
},
},
}
}
fn host_sig_ok(inner: &StoreInner, func: Func, expected_id: CanonicalTypeId) -> Result<()> {
if inner
.engine()
.is_subtype(host_ty(inner, func).canonical_id(), expected_id)
{
Ok(())
} else {
Err(Trap::BadSignature.into())
}
}
fn host_ty(inner: &StoreInner, f: crate::func::Func) -> &FuncType {
match inner.func(f) {
FuncEntity::Host { ty, .. } => ty,
#[cfg(feature = "async")]
FuncEntity::HostAsync { ty, .. } => ty,
FuncEntity::Wasm { .. } => unreachable!("resolve returned a host func"),
}
}