use super::{cell, Execution};
use crate::canon::{AggKind, CanonicalTypeId, IrHeap, RefKind};
use crate::instance::Instance;
use crate::module::code::Code;
use crate::module::op::{Op, NULLABLE_BIT};
use crate::store::{decode_anyref_handle, AnyRefHandle, FuncEntity, ObjKind, StoreInner};
use crate::trap::Trap;
use crate::value::Val;
use crate::Result;
impl Execution {
pub(super) fn exec_cast(
&mut self,
inner: &mut StoreInner,
op: &Op,
instance: Instance,
) -> Result<()> {
match op {
Op::RefTest { ty, nullable } => {
let r = self.pop_ref(cell::refkind_of_irheap(ty));
let hit = matches_heaptype(inner, instance, &r, ty, *nullable);
self.push(Val::I32(i32::from(hit)));
Ok(())
}
Op::RefCast { ty, nullable } => {
let r = self.pop_ref(cell::refkind_of_irheap(ty));
if matches_heaptype(inner, instance, &r, ty, *nullable) {
self.push(r);
Ok(())
} else {
Err(Trap::CastFailure.into())
}
}
Op::RefEq => {
let b = self.pop_anyref();
let a = self.pop_anyref();
self.push(Val::I32(i32::from(ref_eq(&a, &b))));
Ok(())
}
Op::AnyConvertExtern => {
let e = self.pop_ref(RefKind::Extern);
let a = inner.any_convert_extern(e)?;
self.push(a);
Ok(())
}
Op::ExternConvertAny => {
let a = self.pop_anyref();
let e = inner.extern_convert_any(a)?;
self.push(e);
Ok(())
}
other => Err(crate::Error::msg(format!(
"unexpected op in exec dispatch: {other:?}"
))),
}
}
#[inline]
pub(super) fn br_on_cast(
&mut self,
inner: &StoreInner,
code: &Code,
instance: Instance,
op: &Op,
on_fail: bool,
) -> Option<u32> {
let (ty, packed) = match op {
Op::BrOnCast { ty, target } | Op::BrOnCastFail { ty, target } => (ty, *target),
_ => unreachable!("not a br_on_cast op"),
};
let nullable = packed & NULLABLE_BIT != 0;
#[allow(clippy::indexing_slicing)]
let target = code.br_tables()[(packed & !NULLABLE_BIT) as usize];
let r = self.pop_ref(cell::refkind_of_irheap(ty));
let matched = matches_heaptype(inner, instance, &r, ty, nullable);
self.push(r);
if matched ^ on_fail {
self.take_branch(target);
Some(target.ip)
} else {
None
}
}
}
pub(super) fn matches_heaptype(
inner: &StoreInner,
instance: Instance,
value: &Val,
target: &IrHeap,
nullable: bool,
) -> bool {
use IrHeap as H;
if value.is_null_ref() {
return nullable;
}
match target {
H::Any => matches!(value, Val::AnyRef(Some(_))),
H::Eq => is_eq(inner, value),
H::I31 => matches!(decode_any(value), Some(AnyRefHandle::I31(_))),
H::Struct => gc_kind(inner, value) == Some(AggKind::Struct),
H::Array => gc_kind(inner, value) == Some(AggKind::Array),
H::Func => matches!(value, Val::FuncRef(Some(_))),
H::Extern => matches!(value, Val::ExternRef(Some(_))),
H::Exn => matches!(value, Val::ExnRef(Some(_))),
H::NoFunc | H::NoExtern | H::NoExn | H::None => false,
H::Concrete(idx, kind) => concrete_matches(inner, instance, value, *idx, *kind),
}
}
fn is_eq(inner: &StoreInner, value: &Val) -> bool {
matches!(decode_any(value), Some(AnyRefHandle::I31(_))) || gc_kind(inner, value).is_some()
}
fn concrete_matches(
inner: &StoreInner,
instance: Instance,
value: &Val,
idx: u32,
kind: AggKind,
) -> bool {
let target_id = inner
.instance(instance)
.module
.inner()
.canonical_type_id(idx);
let actual = match kind {
AggKind::Func => match value {
Val::FuncRef(Some(f)) => Some(func_type_id(inner, *f)),
_ => None,
},
AggKind::Struct | AggKind::Array => gc_type_id(inner, value),
};
actual.is_some_and(|a| inner.engine().is_subtype(a, target_id))
}
fn decode_any(value: &Val) -> Option<AnyRefHandle> {
match value {
Val::AnyRef(Some(r)) => Some(decode_anyref_handle(r.raw())),
_ => None,
}
}
fn gc_kind(inner: &StoreInner, value: &Val) -> Option<AggKind> {
let slot = match decode_any(value)? {
AnyRefHandle::Slot(i) => i,
AnyRefHandle::I31(_) => return None,
};
match inner.gc_object(slot).expect("live gc slot").header.kind {
ObjKind::Struct => Some(AggKind::Struct),
ObjKind::Array => Some(AggKind::Array),
ObjKind::Extern => None, }
}
fn gc_type_id(inner: &StoreInner, value: &Val) -> Option<CanonicalTypeId> {
let AnyRefHandle::Slot(i) = decode_any(value)? else {
return None;
};
let obj = inner.gc_object(i).expect("live gc slot");
match obj.header.kind {
ObjKind::Struct | ObjKind::Array => Some(obj.header.type_id),
ObjKind::Extern => None,
}
}
fn func_type_id(inner: &StoreInner, f: crate::func::Func) -> CanonicalTypeId {
match inner.func(f) {
FuncEntity::Wasm {
instance,
func_index,
} => {
let m = inner.instance(*instance).module.inner();
#[allow(clippy::indexing_slicing)]
let id = m.canonical_type_id(m.func_types[*func_index as usize]);
id
}
FuncEntity::Host { ty, .. } => ty.canonical_id(),
#[cfg(feature = "async")]
FuncEntity::HostAsync { ty, .. } => ty.canonical_id(),
}
}
fn ref_eq(a: &Val, b: &Val) -> bool {
match (a, b) {
(Val::AnyRef(None), Val::AnyRef(None)) => true,
(Val::AnyRef(Some(x)), Val::AnyRef(Some(y))) => x.raw() == y.raw(),
(Val::AnyRef(_), Val::AnyRef(_)) => false,
_ => unreachable!("ref.eq operands are eqref"),
}
}