use super::structref::{initialize_field_impl, read_field_impl};
use crate::{
StorageType, Val,
prelude::*,
runtime::vm::{GcHeap, GcStore, VMGcRef},
store::{AutoAssertNoGc, InstanceId},
};
use core::fmt;
use wasmtime_environ::{DefinedTagIndex, GcStructLayout, VMGcKind};
#[derive(Debug, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct VMExnRef(VMGcRef);
impl fmt::Pointer for VMExnRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Pointer::fmt(&self.0, f)
}
}
impl From<VMExnRef> for VMGcRef {
#[inline]
fn from(x: VMExnRef) -> Self {
x.0
}
}
impl VMGcRef {
pub fn is_exnref(&self, gc_heap: &(impl GcHeap + ?Sized)) -> bool {
if self.is_i31() {
return false;
}
let header = gc_heap.header(&self);
header.kind().matches(VMGcKind::ExnRef)
}
pub fn into_exnref(self, gc_heap: &(impl GcHeap + ?Sized)) -> Result<VMExnRef, VMGcRef> {
if self.is_exnref(gc_heap) {
Ok(self.into_exnref_unchecked())
} else {
Err(self)
}
}
#[inline]
pub fn into_exnref_unchecked(self) -> VMExnRef {
debug_assert!(!self.is_i31());
VMExnRef(self)
}
pub fn as_exnref(&self, gc_heap: &(impl GcHeap + ?Sized)) -> Option<&VMExnRef> {
if self.is_exnref(gc_heap) {
Some(self.as_exnref_unchecked())
} else {
None
}
}
pub fn as_exnref_unchecked(&self) -> &VMExnRef {
debug_assert!(!self.is_i31());
let ptr = self as *const VMGcRef;
let ret = unsafe { &*ptr.cast() };
assert!(matches!(ret, VMExnRef(VMGcRef { .. })));
ret
}
}
impl VMExnRef {
pub fn as_gc_ref(&self) -> &VMGcRef {
&self.0
}
pub fn as_gc_ref_mut(&mut self) -> &mut VMGcRef {
&mut self.0
}
pub fn clone(&self, gc_store: &mut GcStore) -> Self {
Self(gc_store.clone_gc_ref(&self.0))
}
pub fn drop(self, gc_store: &mut GcStore) {
gc_store.drop_gc_ref(self.0);
}
pub fn unchecked_copy(&self) -> Self {
Self(self.0.unchecked_copy())
}
pub fn read_field(
&self,
store: &mut AutoAssertNoGc,
layout: &GcStructLayout,
ty: &StorageType,
field: usize,
) -> Val {
let offset = layout.fields[field].offset;
read_field_impl(self.as_gc_ref(), store, ty, offset)
}
pub fn initialize_field(
&self,
store: &mut AutoAssertNoGc,
layout: &GcStructLayout,
ty: &StorageType,
field: usize,
val: Val,
) -> Result<()> {
debug_assert!(val._matches_ty(&store, &ty.unpack())?);
let offset = layout.fields[field].offset;
initialize_field_impl(self.as_gc_ref(), store, ty, offset, val)
}
pub fn initialize_tag(
&self,
store: &mut AutoAssertNoGc,
instance: InstanceId,
tag: DefinedTagIndex,
) -> Result<()> {
let layouts = store.engine().gc_runtime().unwrap().layouts();
let instance_offset = layouts.exception_tag_instance_offset();
let tag_offset = layouts.exception_tag_defined_offset();
let store = store.require_gc_store_mut()?;
store
.gc_object_data(&self.0)
.write_u32(instance_offset, instance.as_u32());
store
.gc_object_data(&self.0)
.write_u32(tag_offset, tag.as_u32());
Ok(())
}
pub fn tag(&self, store: &mut AutoAssertNoGc) -> Result<(InstanceId, DefinedTagIndex)> {
let layouts = store.engine().gc_runtime().unwrap().layouts();
let instance_offset = layouts.exception_tag_instance_offset();
let tag_offset = layouts.exception_tag_defined_offset();
let instance = store
.require_gc_store_mut()?
.gc_object_data(&self.0)
.read_u32(instance_offset);
let instance = InstanceId::from_u32(instance);
let store = store.require_gc_store_mut()?;
let tag = store.gc_object_data(&self.0).read_u32(tag_offset);
let tag = DefinedTagIndex::from_u32(tag);
Ok((instance, tag))
}
}