use crate::{
store::{AutoAssertNoGc, StoreOpaque},
AsContext, AsContextMut, FuncType, GcRefImpl, GcRootIndex, HeapType, ManuallyRooted, RefType,
Result, RootSet, Rooted, ValRaw, ValType, WasmTy, I31,
};
use std::num::NonZeroU64;
use wasmtime_runtime::VMGcRef;
#[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 { std::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> {
type Abi = NonZeroU64;
#[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, _: &FuncType) -> Result<()> {
unreachable!()
}
#[inline]
fn is_non_i31_gc_ref(&self) -> bool {
true
}
#[inline]
unsafe fn abi_from_raw(raw: *mut ValRaw) -> Self::Abi {
let raw = (*raw).get_externref();
debug_assert_ne!(raw, 0);
NonZeroU64::new_unchecked(u64::from(raw))
}
#[inline]
unsafe fn abi_into_raw(abi: Self::Abi, raw: *mut ValRaw) {
let externref = u32::try_from(abi.get()).unwrap();
*raw = ValRaw::externref(externref);
}
#[inline]
fn into_abi(self, store: &mut AutoAssertNoGc<'_>) -> Result<Self::Abi> {
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);
Ok(unsafe { NonZeroU64::new_unchecked(r64) })
}
#[inline]
unsafe fn from_abi(abi: Self::Abi, store: &mut AutoAssertNoGc<'_>) -> Self {
let gc_ref = VMGcRef::from_r64(abi.get())
.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>> {
type Abi = u64;
#[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, _: &FuncType) -> Result<()> {
unreachable!()
}
#[inline]
fn is_non_i31_gc_ref(&self) -> bool {
true
}
#[inline]
unsafe fn abi_from_raw(raw: *mut ValRaw) -> Self::Abi {
let externref = (*raw).get_externref();
u64::from(externref)
}
#[inline]
unsafe fn abi_into_raw(abi: Self::Abi, raw: *mut ValRaw) {
let externref = u32::try_from(abi).unwrap();
*raw = ValRaw::externref(externref);
}
#[inline]
fn into_abi(self, store: &mut AutoAssertNoGc<'_>) -> Result<Self::Abi> {
Ok(if let Some(x) = self {
<Rooted<AnyRef> as WasmTy>::into_abi(x, store)?.get()
} else {
0
})
}
#[inline]
unsafe fn from_abi(abi: Self::Abi, store: &mut AutoAssertNoGc<'_>) -> Self {
let gc_ref = VMGcRef::from_r64(abi).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> {
type Abi = NonZeroU64;
#[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, _: &FuncType) -> Result<()> {
unreachable!()
}
#[inline]
fn is_non_i31_gc_ref(&self) -> bool {
true
}
#[inline]
unsafe fn abi_from_raw(raw: *mut ValRaw) -> Self::Abi {
let externref = (*raw).get_externref();
debug_assert_ne!(externref, 0);
NonZeroU64::new_unchecked(u64::from(externref))
}
#[inline]
unsafe fn abi_into_raw(abi: Self::Abi, raw: *mut ValRaw) {
let externref = u32::try_from(abi.get()).unwrap();
*raw = ValRaw::externref(externref);
}
#[inline]
fn into_abi(self, store: &mut AutoAssertNoGc<'_>) -> Result<Self::Abi> {
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);
Ok(unsafe { NonZeroU64::new_unchecked(r64) })
}
#[inline]
unsafe fn from_abi(abi: Self::Abi, store: &mut AutoAssertNoGc<'_>) -> Self {
let gc_ref = VMGcRef::from_r64(abi.get())
.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>> {
type Abi = u64;
#[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, _: &FuncType) -> Result<()> {
unreachable!()
}
#[inline]
fn is_non_i31_gc_ref(&self) -> bool {
true
}
#[inline]
unsafe fn abi_from_raw(raw: *mut ValRaw) -> Self::Abi {
let externref = (*raw).get_externref();
u64::from(externref)
}
#[inline]
unsafe fn abi_into_raw(abi: Self::Abi, raw: *mut ValRaw) {
let externref = u32::try_from(abi).unwrap();
*raw = ValRaw::externref(externref);
}
#[inline]
fn into_abi(self, store: &mut AutoAssertNoGc<'_>) -> Result<Self::Abi> {
Ok(if let Some(x) = self {
<ManuallyRooted<AnyRef> as WasmTy>::into_abi(x, store)?.get()
} else {
0
})
}
#[inline]
unsafe fn from_abi(abi: Self::Abi, store: &mut AutoAssertNoGc<'_>) -> Self {
let gc_ref = VMGcRef::from_r64(abi).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"),
)
})
}
}