use super::{ExternRef, RootedGcRefImpl};
use crate::prelude::*;
use crate::runtime::vm::VMGcRef;
use crate::{
ArrayRef, ArrayType, AsContext, AsContextMut, EqRef, GcRefImpl, GcRootIndex, HeapType, I31,
OwnedRooted, RefType, Result, Rooted, StructRef, StructType, ValRaw, ValType, WasmTy,
store::{AutoAssertNoGc, StoreOpaque},
};
use core::mem;
use core::mem::MaybeUninit;
use wasmtime_environ::VMGcKind;
#[derive(Debug)]
#[repr(transparent)]
pub struct AnyRef {
pub(super) inner: GcRootIndex,
}
impl From<Rooted<EqRef>> for Rooted<AnyRef> {
#[inline]
fn from(e: Rooted<EqRef>) -> Self {
e.to_anyref()
}
}
impl From<OwnedRooted<EqRef>> for OwnedRooted<AnyRef> {
#[inline]
fn from(e: OwnedRooted<EqRef>) -> Self {
e.to_anyref()
}
}
impl From<Rooted<StructRef>> for Rooted<AnyRef> {
#[inline]
fn from(s: Rooted<StructRef>) -> Self {
s.to_anyref()
}
}
impl From<OwnedRooted<StructRef>> for OwnedRooted<AnyRef> {
#[inline]
fn from(s: OwnedRooted<StructRef>) -> Self {
s.to_anyref()
}
}
impl From<Rooted<ArrayRef>> for Rooted<AnyRef> {
#[inline]
fn from(s: Rooted<ArrayRef>) -> Self {
s.to_anyref()
}
}
impl From<OwnedRooted<ArrayRef>> for OwnedRooted<AnyRef> {
#[inline]
fn from(s: OwnedRooted<ArrayRef>) -> Self {
s.to_anyref()
}
}
unsafe impl GcRefImpl for AnyRef {
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 fn convert_extern(
mut store: impl AsContextMut,
externref: Rooted<ExternRef>,
) -> Result<Rooted<Self>> {
let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
Self::_convert_extern(&mut store, externref)
}
pub(crate) fn _convert_extern(
store: &mut AutoAssertNoGc<'_>,
externref: Rooted<ExternRef>,
) -> Result<Rooted<Self>> {
let gc_ref = externref.try_clone_gc_ref(store)?;
Ok(Self::from_cloned_gc_ref(store, gc_ref))
}
pub fn from_raw(mut store: impl AsContextMut, raw: u32) -> Option<Rooted<Self>> {
let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
Self::_from_raw(&mut store, raw)
}
pub(crate) fn _from_raw(store: &mut AutoAssertNoGc, raw: u32) -> Option<Rooted<Self>> {
let gc_ref = VMGcRef::from_raw_u32(raw)?;
let gc_ref = store.clone_gc_ref(&gc_ref);
Some(Self::from_cloned_gc_ref(store, gc_ref))
}
pub(crate) fn from_cloned_gc_ref(
store: &mut AutoAssertNoGc<'_>,
gc_ref: VMGcRef,
) -> Rooted<Self> {
debug_assert!(
gc_ref.is_i31()
|| store
.unwrap_gc_store()
.header(&gc_ref)
.kind()
.matches(VMGcKind::AnyRef)
|| store
.unwrap_gc_store()
.header(&gc_ref)
.kind()
.matches(VMGcKind::ExternRef)
);
Rooted::new(store, gc_ref)
}
#[inline]
pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
self.inner.comes_from_same_store(store)
}
pub fn to_raw(&self, mut store: impl AsContextMut) -> Result<u32> {
let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
self._to_raw(&mut store)
}
pub(crate) fn _to_raw(&self, store: &mut AutoAssertNoGc<'_>) -> Result<u32> {
let gc_ref = self.inner.try_clone_gc_ref(store)?;
let raw = if gc_ref.is_i31() {
gc_ref.as_raw_non_zero_u32()
} else {
store.require_gc_store_mut()?.expose_gc_ref_to_wasm(gc_ref)
};
Ok(raw.get())
}
pub fn ty(&self, store: impl AsContext) -> Result<HeapType> {
self._ty(store.as_context().0)
}
pub(crate) fn _ty(&self, store: &StoreOpaque) -> Result<HeapType> {
let gc_ref = self.inner.try_gc_ref(store)?;
if gc_ref.is_i31() {
return Ok(HeapType::I31);
}
let header = store.require_gc_store()?.header(gc_ref);
if header.kind().matches(VMGcKind::ExternRef) {
return Ok(HeapType::Any);
}
debug_assert!(header.kind().matches(VMGcKind::AnyRef));
debug_assert!(header.kind().matches(VMGcKind::EqRef));
if header.kind().matches(VMGcKind::StructRef) {
return Ok(HeapType::ConcreteStruct(
StructType::from_shared_type_index(store.engine(), header.ty().unwrap()),
));
}
if header.kind().matches(VMGcKind::ArrayRef) {
return Ok(HeapType::ConcreteArray(ArrayType::from_shared_type_index(
store.engine(),
header.ty().unwrap(),
)));
}
unreachable!("no other kinds of `anyref`s")
}
pub fn matches_ty(&self, store: impl AsContext, ty: &HeapType) -> Result<bool> {
self._matches_ty(store.as_context().0, ty)
}
pub(crate) fn _matches_ty(&self, store: &StoreOpaque, ty: &HeapType) -> Result<bool> {
assert!(self.comes_from_same_store(store));
Ok(self._ty(store)?.matches(ty))
}
pub(crate) fn ensure_matches_ty(&self, store: &StoreOpaque, ty: &HeapType) -> Result<()> {
if !self.comes_from_same_store(store) {
bail!("function used with wrong store");
}
if self._matches_ty(store, ty)? {
Ok(())
} else {
let actual_ty = self._ty(store)?;
bail!("type mismatch: expected `(ref {ty})`, found `(ref {actual_ty})`")
}
}
pub fn is_eqref(&self, store: impl AsContext) -> Result<bool> {
self._is_eqref(store.as_context().0)
}
pub(crate) fn _is_eqref(&self, store: &StoreOpaque) -> Result<bool> {
assert!(self.comes_from_same_store(store));
let gc_ref = self.inner.try_gc_ref(store)?;
Ok(gc_ref.is_i31()
|| store
.require_gc_store()?
.kind(gc_ref)
.matches(VMGcKind::EqRef))
}
pub fn as_eqref(&self, store: impl AsContext) -> Result<Option<Rooted<EqRef>>> {
self._as_eqref(store.as_context().0)
}
pub(crate) fn _as_eqref(&self, store: &StoreOpaque) -> Result<Option<Rooted<EqRef>>> {
if self._is_eqref(store)? {
Ok(Some(Rooted::from_gc_root_index(self.inner)))
} else {
Ok(None)
}
}
pub fn unwrap_eqref(&self, store: impl AsContext) -> Result<Rooted<EqRef>> {
self._unwrap_eqref(store.as_context().0)
}
pub(crate) fn _unwrap_eqref(&self, store: &StoreOpaque) -> Result<Rooted<EqRef>> {
Ok(self
._as_eqref(store)?
.expect("AnyRef::unwrap_eqref on non-eqref"))
}
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> {
assert!(self.comes_from_same_store(store));
let gc_ref = self.inner.try_gc_ref(store)?;
Ok(gc_ref.is_i31())
}
pub fn as_i31(&self, store: impl AsContext) -> Result<Option<I31>> {
self._as_i31(store.as_context().0)
}
pub(crate) fn _as_i31(&self, store: &StoreOpaque) -> Result<Option<I31>> {
assert!(self.comes_from_same_store(store));
let gc_ref = self.inner.try_gc_ref(store)?;
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"))
}
pub fn is_struct(&self, store: impl AsContext) -> Result<bool> {
self._is_struct(store.as_context().0)
}
pub(crate) fn _is_struct(&self, store: &StoreOpaque) -> Result<bool> {
let gc_ref = self.inner.try_gc_ref(store)?;
Ok(!gc_ref.is_i31()
&& store
.require_gc_store()?
.kind(gc_ref)
.matches(VMGcKind::StructRef))
}
pub fn as_struct(&self, store: impl AsContext) -> Result<Option<Rooted<StructRef>>> {
self._as_struct(store.as_context().0)
}
pub(crate) fn _as_struct(&self, store: &StoreOpaque) -> Result<Option<Rooted<StructRef>>> {
if self._is_struct(store)? {
Ok(Some(Rooted::from_gc_root_index(self.inner)))
} else {
Ok(None)
}
}
pub fn unwrap_struct(&self, store: impl AsContext) -> Result<Rooted<StructRef>> {
self._unwrap_struct(store.as_context().0)
}
pub(crate) fn _unwrap_struct(&self, store: &StoreOpaque) -> Result<Rooted<StructRef>> {
Ok(self
._as_struct(store)?
.expect("AnyRef::unwrap_struct on non-structref"))
}
pub fn is_array(&self, store: impl AsContext) -> Result<bool> {
self._is_array(store.as_context().0)
}
pub(crate) fn _is_array(&self, store: &StoreOpaque) -> Result<bool> {
let gc_ref = self.inner.try_gc_ref(store)?;
Ok(!gc_ref.is_i31()
&& store
.require_gc_store()?
.kind(gc_ref)
.matches(VMGcKind::ArrayRef))
}
pub fn as_array(&self, store: impl AsContext) -> Result<Option<Rooted<ArrayRef>>> {
self._as_array(store.as_context().0)
}
pub(crate) fn _as_array(&self, store: &StoreOpaque) -> Result<Option<Rooted<ArrayRef>>> {
if self._is_array(store)? {
Ok(Some(Rooted::from_gc_root_index(self.inner)))
} else {
Ok(None)
}
}
pub fn unwrap_array(&self, store: impl AsContext) -> Result<Rooted<ArrayRef>> {
self._unwrap_array(store.as_context().0)
}
pub(crate) fn _unwrap_array(&self, store: &StoreOpaque) -> Result<Rooted<ArrayRef>> {
Ok(self
._as_array(store)?
.expect("AnyRef::unwrap_array on non-arrayref"))
}
}
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,
store: &StoreOpaque,
_nullable: bool,
ty: &HeapType,
) -> Result<()> {
self.ensure_matches_ty(store, ty)
}
fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
self.wasm_ty_store(store, ptr, ValRaw::anyref)
}
unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
Self::wasm_ty_load(store, ptr.get_anyref(), AnyRef::from_cloned_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,
store: &StoreOpaque,
nullable: bool,
ty: &HeapType,
) -> Result<()> {
match self {
Some(a) => a.ensure_matches_ty(store, ty),
None => {
ensure!(
nullable,
"expected a non-null reference, but found a null reference"
);
Ok(())
}
}
}
#[inline]
fn is_vmgcref_and_points_to_object(&self) -> bool {
self.is_some()
}
fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
<Rooted<AnyRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
}
unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
<Rooted<AnyRef>>::wasm_ty_option_load(store, ptr.get_anyref(), AnyRef::from_cloned_gc_ref)
}
}
unsafe impl WasmTy for OwnedRooted<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,
store: &StoreOpaque,
_nullable: bool,
ty: &HeapType,
) -> Result<()> {
self.ensure_matches_ty(store, ty)
}
fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
self.wasm_ty_store(store, ptr, ValRaw::anyref)
}
unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
Self::wasm_ty_load(store, ptr.get_anyref(), AnyRef::from_cloned_gc_ref)
}
}
unsafe impl WasmTy for Option<OwnedRooted<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,
store: &StoreOpaque,
nullable: bool,
ty: &HeapType,
) -> Result<()> {
match self {
Some(a) => a.ensure_matches_ty(store, ty),
None => {
ensure!(
nullable,
"expected a non-null reference, but found a null reference"
);
Ok(())
}
}
}
#[inline]
fn is_vmgcref_and_points_to_object(&self) -> bool {
self.is_some()
}
fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
<OwnedRooted<AnyRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
}
unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
<OwnedRooted<AnyRef>>::wasm_ty_option_load(
store,
ptr.get_anyref(),
AnyRef::from_cloned_gc_ref,
)
}
}