use crate::runtime::vm::VMGcRef;
use crate::{
store::{AutoAssertNoGc, StoreOpaque},
AsContext, AsContextMut, GcRefImpl, GcRootIndex, HeapType, ManuallyRooted, RefType, Result,
RootSet, Rooted, ValRaw, ValType, WasmTy, I31,
};
use core::mem;
use core::mem::MaybeUninit;
#[derive(Debug)]
#[repr(transparent)]
pub struct AnyRef {
inner: GcRootIndex,
}
unsafe impl GcRefImpl for AnyRef {
#[allow(private_interfaces)]
fn transmute_ref(index: &GcRootIndex) -> &Self {
let me: &Self = unsafe { mem::transmute(index) };
assert!(matches!(
me,
Self {
inner: GcRootIndex { .. },
}
));
me
}
}
impl AnyRef {
pub fn from_i31(mut store: impl AsContextMut, value: I31) -> Rooted<Self> {
let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
Self::_from_i31(&mut store, value)
}
pub(crate) fn _from_i31(store: &mut AutoAssertNoGc<'_>, value: I31) -> Rooted<Self> {
let gc_ref = VMGcRef::from_i31(value.runtime_i31());
Rooted::new(store, gc_ref)
}
pub unsafe fn from_raw(mut store: impl AsContextMut, raw: u32) -> Option<Rooted<Self>> {
let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
let gc_ref = VMGcRef::from_raw_u32(raw)?;
Some(Self::from_cloned_gc_ref(&mut store, gc_ref))
}
pub(crate) fn from_cloned_gc_ref(
store: &mut AutoAssertNoGc<'_>,
gc_ref: VMGcRef,
) -> Rooted<Self> {
assert!(gc_ref.is_i31());
assert!(VMGcRef::ONLY_EXTERN_REF_AND_I31);
Rooted::new(store, gc_ref)
}
pub unsafe fn to_raw(&self, mut store: impl AsContextMut) -> Result<u32> {
let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
let gc_ref = self.inner.try_clone_gc_ref(&mut store)?;
let raw = gc_ref.as_raw_u32();
store.gc_store_mut()?.expose_gc_ref_to_wasm(gc_ref);
Ok(raw)
}
pub fn is_i31(&self, store: impl AsContext) -> Result<bool> {
self._is_i31(store.as_context().0)
}
pub(crate) fn _is_i31(&self, store: &StoreOpaque) -> Result<bool> {
let gc_ref = self.inner.unchecked_try_gc_ref(store)?;
Ok(gc_ref.is_i31())
}
pub fn as_i31(&self, store: impl AsContext) -> Result<Option<I31>> {
let gc_ref = self.inner.unchecked_try_gc_ref(store.as_context().0)?;
Ok(gc_ref.as_i31().map(Into::into))
}
pub fn unwrap_i31(&self, store: impl AsContext) -> Result<I31> {
Ok(self.as_i31(store)?.expect("AnyRef::unwrap_i31 on non-i31"))
}
}
unsafe impl WasmTy for Rooted<AnyRef> {
#[inline]
fn valtype() -> ValType {
ValType::Ref(RefType::new(false, HeapType::Any))
}
#[inline]
fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
self.comes_from_same_store(store)
}
#[inline]
fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
unreachable!()
}
fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
let gc_ref = self.inner.try_clone_gc_ref(store)?;
let r64 = gc_ref.as_r64();
store.gc_store_mut()?.expose_gc_ref_to_wasm(gc_ref);
debug_assert_ne!(r64, 0);
let anyref = u32::try_from(r64).unwrap();
ptr.write(ValRaw::anyref(anyref));
Ok(())
}
unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
let raw = ptr.get_anyref();
debug_assert_ne!(raw, 0);
let gc_ref = VMGcRef::from_r64(raw.into())
.expect("valid r64")
.expect("non-null");
let gc_ref = store.unwrap_gc_store_mut().clone_gc_ref(&gc_ref);
AnyRef::from_cloned_gc_ref(store, gc_ref)
}
}
unsafe impl WasmTy for Option<Rooted<AnyRef>> {
#[inline]
fn valtype() -> ValType {
ValType::ANYREF
}
#[inline]
fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
self.map_or(true, |x| x.comes_from_same_store(store))
}
#[inline]
fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
unreachable!()
}
#[inline]
fn is_vmgcref_and_points_to_object(&self) -> bool {
self.is_some()
}
fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
match self {
Some(r) => r.store(store, ptr),
None => {
ptr.write(ValRaw::anyref(0));
Ok(())
}
}
}
unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
let gc_ref = VMGcRef::from_r64(ptr.get_anyref().into()).expect("valid r64")?;
let gc_ref = store.unwrap_gc_store_mut().clone_gc_ref(&gc_ref);
Some(AnyRef::from_cloned_gc_ref(store, gc_ref))
}
}
unsafe impl WasmTy for ManuallyRooted<AnyRef> {
#[inline]
fn valtype() -> ValType {
ValType::Ref(RefType::new(false, HeapType::Any))
}
#[inline]
fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
self.comes_from_same_store(store)
}
#[inline]
fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
unreachable!()
}
fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
let gc_ref = self.inner.try_clone_gc_ref(store)?;
let r64 = gc_ref.as_r64();
store.gc_store_mut()?.expose_gc_ref_to_wasm(gc_ref);
debug_assert_ne!(r64, 0);
let anyref = u32::try_from(r64).unwrap();
ptr.write(ValRaw::anyref(anyref));
Ok(())
}
unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
let raw = ptr.get_anyref();
debug_assert_ne!(raw, 0);
let gc_ref = VMGcRef::from_r64(raw.into())
.expect("valid r64")
.expect("non-null");
let gc_ref = store.unwrap_gc_store_mut().clone_gc_ref(&gc_ref);
RootSet::with_lifo_scope(store, |store| {
let rooted = AnyRef::from_cloned_gc_ref(store, gc_ref);
rooted
._to_manually_rooted(store)
.expect("rooted is in scope")
})
}
}
unsafe impl WasmTy for Option<ManuallyRooted<AnyRef>> {
#[inline]
fn valtype() -> ValType {
ValType::ANYREF
}
#[inline]
fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
self.as_ref()
.map_or(true, |x| x.comes_from_same_store(store))
}
#[inline]
fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
unreachable!()
}
#[inline]
fn is_vmgcref_and_points_to_object(&self) -> bool {
self.is_some()
}
fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
match self {
Some(r) => r.store(store, ptr),
None => {
ptr.write(ValRaw::anyref(0));
Ok(())
}
}
}
unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
let raw = ptr.get_anyref();
debug_assert_ne!(raw, 0);
let gc_ref = VMGcRef::from_r64(raw.into()).expect("valid r64")?;
let gc_ref = store.unwrap_gc_store_mut().clone_gc_ref(&gc_ref);
RootSet::with_lifo_scope(store, |store| {
let rooted = AnyRef::from_cloned_gc_ref(store, gc_ref);
Some(
rooted
._to_manually_rooted(store)
.expect("rooted is in scope"),
)
})
}
}