#![allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)]
use super::Execution;
use crate::error::InternalError;
use crate::instance::Instance;
use crate::module::op::Op;
use crate::store::{
anyref_handle_i31, anyref_handle_slot, anyref_value, decode_anyref_handle, default_for_slot,
read_slot, read_slot_packed, write_slot, AnyRefHandle, GcObject, StoreInner,
};
use crate::trap::Trap;
use crate::value::Val;
use crate::Result;
impl Execution {
pub(super) fn exec_gc(
&mut self,
inner: &mut StoreInner,
op: &Op,
instance: Instance,
) -> Result<()> {
match op {
Op::StructNew(ty) => self.struct_new(inner, instance, *ty, false),
Op::StructNewDefault(ty) => self.struct_new(inner, instance, *ty, true),
Op::StructGet { ty, field } => self.struct_get(inner, instance, *ty, *field, None),
Op::StructGetS { ty, field } => {
self.struct_get(inner, instance, *ty, *field, Some(true))
}
Op::StructGetU { ty, field } => {
self.struct_get(inner, instance, *ty, *field, Some(false))
}
Op::StructSet { ty, field } => self.struct_set(inner, instance, *ty, *field),
Op::RefI31 => {
let v = self.pop_i32()?;
self.push(anyref_value(anyref_handle_i31(v)));
Ok(())
}
Op::I31GetS => self.i31_get(true),
Op::I31GetU => self.i31_get(false),
_ => self.exec_gc_array(inner, op, instance),
}
}
fn struct_new(
&mut self,
inner: &mut StoreInner,
instance: Instance,
ty: u32,
default: bool,
) -> Result<()> {
let module = inner.instance(instance).module.clone();
let type_id = module.inner().canonical_type_id(ty);
let layout = module.inner().struct_layout(ty)?;
let fields = layout.fields();
let mut data = vec![0u8; layout.size()];
if default {
for &slot in fields {
write_slot(slot, &mut data, default_for_slot(slot));
}
} else {
for &slot in fields.iter().rev() {
let v = self.pop_val_for(slot)?;
write_slot(slot, &mut data, v);
}
}
let handle = inner.alloc_gc(GcObject::new_struct(type_id, data.into_boxed_slice()))?;
self.push(anyref_value(anyref_handle_slot(handle)));
Ok(())
}
fn struct_get(
&mut self,
inner: &StoreInner,
instance: Instance,
ty: u32,
field: u32,
ext: Option<bool>,
) -> Result<()> {
let module = inner.instance(instance).module.clone();
let slot = module.inner().struct_field(ty, field)?;
let r = self.pop_anyref()?;
let obj = anyref_slot(&r, Trap::NullStructReference)?;
let data = &inner
.gc_object(obj)
.ok_or(InternalError::GcMetadata(
"struct operand refers to a missing object",
))?
.data;
let v = match ext {
None => read_slot(slot, data),
Some(signed) => Val::I32(read_slot_packed(slot, data, signed)),
};
self.push(v);
Ok(())
}
fn struct_set(
&mut self,
inner: &mut StoreInner,
instance: Instance,
ty: u32,
field: u32,
) -> Result<()> {
let module = inner.instance(instance).module.clone();
let slot = module.inner().struct_field(ty, field)?;
let v = self.pop_val_for(slot)?;
let r = self.pop_anyref()?;
let obj = anyref_slot(&r, Trap::NullStructReference)?;
let data = &mut inner
.gc_object_mut(obj)
.ok_or(InternalError::GcMetadata(
"struct operand refers to a missing object",
))?
.data;
write_slot(slot, data, v);
Ok(())
}
fn i31_get(&mut self, signed: bool) -> Result<()> {
let handle = match self.pop_anyref()? {
Val::AnyRef(Some(r)) => r.raw(),
Val::AnyRef(None) => return Err(Trap::NullI31Reference.into()),
_ => {
return Err(InternalError::OperandStack("i31.get operand is not an anyref").into())
}
};
let v = match decode_anyref_handle(handle) {
AnyRefHandle::I31(s) => {
if signed {
s
} else {
s & 0x7FFF_FFFF
}
}
AnyRefHandle::Slot(_) => {
return Err(InternalError::OperandStack("i31.get operand is not an i31").into())
}
};
self.push(Val::I32(v));
Ok(())
}
}
pub(super) fn anyref_slot(r: &Val, null_trap: Trap) -> Result<u32> {
match r {
Val::AnyRef(Some(rooted)) => match decode_anyref_handle(rooted.raw()) {
AnyRefHandle::Slot(i) => Ok(i),
AnyRefHandle::I31(_) => {
Err(InternalError::OperandStack("aggregate operand is an i31").into())
}
},
Val::AnyRef(None) => Err(null_trap.into()),
_ => Err(InternalError::OperandStack("aggregate operand is not an anyref").into()),
}
}