Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce wasmtime::StructRef and allocating Wasm GC structs #8933

Merged
merged 14 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion benches/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ fn bench_host_to_wasm<Params, Results>(
.call_unchecked(&mut *store, space.as_mut_ptr(), space.len())
.unwrap();
for (i, expected) in results.iter().enumerate() {
let ty = expected.ty(&store);
let ty = expected.ty(&store).unwrap();
let actual = Val::from_raw(&mut *store, space[i], ty);
assert_vals_eq(expected, &actual);
}
Expand Down
5 changes: 5 additions & 0 deletions crates/wasmtime/src/runtime/func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -900,6 +900,11 @@ impl Func {
/// Does this function match the given type?
///
/// That is, is this function's type a subtype of the given type?
///
/// # Panics
///
/// Panics if this function is not associated with the given store or if the
/// function type is not associated with the store's engine.
pub fn matches_ty(&self, store: impl AsContext, func_ty: &FuncType) -> bool {
self._matches_ty(store.as_context().0, func_ty)
}
Expand Down
4 changes: 4 additions & 0 deletions crates/wasmtime/src/runtime/gc/disabled.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@
//! disabled at compile time. While we implement dummy methods for these types'
//! public methods, we do not, however, create dummy constructors constructors.

#![allow(missing_docs, unreachable_code)]

mod anyref;
mod externref;
mod i31;
mod rooting;
mod structref;

pub use anyref::*;
pub use externref::*;
pub use i31::*;
pub use rooting::*;
pub use structref::*;
27 changes: 25 additions & 2 deletions crates/wasmtime/src/runtime/gc/disabled/anyref.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::runtime::vm::VMGcRef;
use crate::{
store::{AutoAssertNoGc, StoreOpaque},
AsContext, AsContextMut, GcRefImpl, HeapType, Result, Rooted, I31,
AsContext, AsContextMut, GcRefImpl, HeapType, Result, Rooted, StructRef, I31,
};

/// Support for `anyref` disabled at compile time because the `gc` cargo feature
Expand All @@ -10,7 +10,6 @@ pub enum AnyRef {}

impl GcRefImpl for AnyRef {}

#[allow(missing_docs)]
impl AnyRef {
pub(crate) fn from_cloned_gc_ref(
_store: &mut AutoAssertNoGc<'_>,
Expand All @@ -32,6 +31,10 @@ impl AnyRef {
match *self {}
}

pub(crate) fn _ty(&self, _store: &StoreOpaque) -> Result<HeapType> {
match *self {}
}

pub fn matches_ty(&self, _store: impl AsContext, _ty: &HeapType) -> Result<bool> {
match *self {}
}
Expand All @@ -51,4 +54,24 @@ impl AnyRef {
pub fn unwrap_i31(&self, _store: impl AsContext) -> Result<I31> {
match *self {}
}

pub fn is_struct(&self, _store: impl AsContext) -> Result<bool> {
match *self {}
}

pub(crate) fn _is_struct(&self, _store: &StoreOpaque) -> Result<bool> {
match *self {}
}

pub fn as_struct(&self, _store: impl AsContext) -> Result<Option<StructRef>> {
match *self {}
}

pub(crate) fn _as_struct(&self, _store: &StoreOpaque) -> Result<Option<StructRef>> {
match *self {}
}

pub fn unwrap_struct(&self, _store: impl AsContext) -> Result<StructRef> {
match *self {}
}
}
1 change: 0 additions & 1 deletion crates/wasmtime/src/runtime/gc/disabled/externref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ pub enum ExternRef {}

impl GcRefImpl for ExternRef {}

#[allow(missing_docs)]
impl ExternRef {
pub(crate) fn from_cloned_gc_ref(
_store: &mut AutoAssertNoGc<'_>,
Expand Down
1 change: 0 additions & 1 deletion crates/wasmtime/src/runtime/gc/disabled/i31.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
/// was not enabled.
pub enum I31 {}

#[allow(missing_docs)]
impl I31 {
pub fn get_u32(&self) -> u32 {
match *self {}
Expand Down
11 changes: 8 additions & 3 deletions crates/wasmtime/src/runtime/gc/disabled/rooting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,6 @@ impl<T: GcRef> Deref for Rooted<T> {
}
}

#[allow(missing_docs)]
impl<T: GcRef> Rooted<T> {
pub(crate) fn comes_from_same_store(&self, _store: &StoreOpaque) -> bool {
match self.inner {}
Expand All @@ -125,6 +124,10 @@ impl<T: GcRef> Rooted<T> {
) -> Result<bool> {
a.assert_unreachable()
}

pub(crate) fn unchecked_cast<U: GcRef>(self) -> Rooted<U> {
match self.inner {}
}
}

/// This type has been disabled because the `gc` cargo feature was not enabled
Expand All @@ -137,7 +140,6 @@ where
_phantom: marker::PhantomData<C>,
}

#[allow(missing_docs)]
impl<C> RootScope<C>
where
C: AsContextMut,
Expand Down Expand Up @@ -191,7 +193,6 @@ impl<T: GcRef> Deref for ManuallyRooted<T> {
}
}

#[allow(missing_docs)]
impl<T> ManuallyRooted<T>
where
T: GcRef,
Expand All @@ -215,6 +216,10 @@ where
pub fn into_rooted(self, _context: impl AsContextMut) -> Rooted<T> {
match self.inner {}
}

pub(crate) fn unchecked_cast<U: GcRef>(self) -> ManuallyRooted<U> {
match self.inner {}
}
}

impl<T: GcRef> RootedGcRefImpl<T> for ManuallyRooted<T> {
Expand Down
52 changes: 52 additions & 0 deletions crates/wasmtime/src/runtime/gc/disabled/structref.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use crate::runtime::vm::VMGcRef;
use crate::{
store::{AutoAssertNoGc, StoreContextMut, StoreOpaque},
AsContext, AsContextMut, GcRefImpl, Result, Rooted, StructType, Val, I31,
};

/// Support for `StructRefPre` disabled at compile time because the `gc` cargo
/// feature was not enabled.
pub enum StructRefPre {}

/// Support for `structref` disabled at compile time because the `gc` cargo feature
/// was not enabled.
pub enum StructRef {}

impl GcRefImpl for StructRef {}

impl StructRef {
pub(crate) fn from_cloned_gc_ref(
_store: &mut AutoAssertNoGc<'_>,
_gc_ref: VMGcRef,
) -> Rooted<Self> {
unreachable!()
}

pub fn ty(&self, _store: impl AsContext) -> Result<StructType> {
match *self {}
}

pub fn matches_ty(&self, _store: impl AsContext, _ty: &StructType) -> Result<bool> {
match *self {}
}

pub(crate) fn _matches_ty(&self, _store: &StoreOpaque, _ty: &StructType) -> Result<bool> {
match *self {}
}

pub fn fields<'a, T: 'a>(
&self,
_store: impl Into<StoreContextMut<'a, T>>,
) -> Result<impl ExactSizeIterator<Item = Val> + 'a> {
match *self {}
Ok([].into_iter())
}

pub fn field(&self, _store: impl AsContextMut, _index: usize) -> Result<Val> {
match *self {}
}

pub fn set_field(&self, _store: impl AsContextMut, _index: usize, _value: Val) -> Result<()> {
match *self {}
}
}
2 changes: 2 additions & 0 deletions crates/wasmtime/src/runtime/gc/enabled.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ mod anyref;
mod externref;
mod i31;
mod rooting;
mod structref;

pub use anyref::*;
pub use externref::*;
pub use i31::*;
pub use rooting::*;
pub use structref::*;
110 changes: 100 additions & 10 deletions crates/wasmtime/src/runtime/gc/enabled/anyref.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
//! Implementation of `anyref` in Wasmtime.

use wasmtime_environ::VMGcKind;

use crate::prelude::*;
use crate::runtime::vm::VMGcRef;
use crate::{prelude::*, ArrayType, StructType};
use crate::{
store::{AutoAssertNoGc, StoreOpaque},
AsContext, AsContextMut, GcRefImpl, GcRootIndex, HeapType, ManuallyRooted, RefType, Result,
RootSet, Rooted, ValRaw, ValType, WasmTy, I31,
ArrayType, AsContext, AsContextMut, GcRefImpl, GcRootIndex, HeapType, ManuallyRooted, RefType,
Result, RootSet, Rooted, StructRef, StructType, ValRaw, ValType, WasmTy, I31,
};
use core::mem;
use core::mem::MaybeUninit;
use wasmtime_environ::VMGcKind;

/// An `anyref` GC reference.
///
Expand Down Expand Up @@ -93,7 +92,21 @@ use core::mem::MaybeUninit;
#[derive(Debug)]
#[repr(transparent)]
pub struct AnyRef {
inner: GcRootIndex,
pub(super) inner: GcRootIndex,
}

impl From<Rooted<StructRef>> for Rooted<AnyRef> {
#[inline]
fn from(s: Rooted<StructRef>) -> Self {
s.to_anyref()
}
}

impl From<ManuallyRooted<StructRef>> for ManuallyRooted<AnyRef> {
#[inline]
fn from(s: ManuallyRooted<StructRef>) -> Self {
s.to_anyref()
}
}

unsafe impl GcRefImpl for AnyRef {
Expand Down Expand Up @@ -190,8 +203,14 @@ impl AnyRef {
store: &mut AutoAssertNoGc<'_>,
gc_ref: VMGcRef,
) -> Rooted<Self> {
assert!(gc_ref.is_i31());
assert!(VMGcRef::ONLY_EXTERN_REF_AND_I31);
debug_assert!(
gc_ref.is_i31()
|| store
.unwrap_gc_store()
.header(&gc_ref)
.kind()
.matches(VMGcKind::AnyRef)
);
Rooted::new(store, gc_ref)
}

Expand Down Expand Up @@ -349,6 +368,72 @@ impl AnyRef {
pub fn unwrap_i31(&self, store: impl AsContext) -> Result<I31> {
Ok(self.as_i31(store)?.expect("AnyRef::unwrap_i31 on non-i31"))
}

/// Is this `anyref` a `structref`?
///
/// # Errors
///
/// Return an error if this reference has been unrooted.
///
/// # Panics
///
/// Panics if this reference is associated with a different store.
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> {
// NB: Can't use `AutoAssertNoGc` here because we only have a shared
// context, not a mutable context.
fitzgen marked this conversation as resolved.
Show resolved Hide resolved
let gc_ref = self.inner.unchecked_try_gc_ref(store)?;
Ok(!gc_ref.is_i31() && store.gc_store()?.kind(gc_ref).matches(VMGcKind::StructRef))
}

/// Downcast this `anyref` to a `structref`.
///
/// If this `anyref` is a `structref`, then `Some(_)` is returned.
///
/// If this `anyref` is not a `structref`, then `None` is returned.
///
/// # Errors
///
/// Return an error if this reference has been unrooted.
///
/// # Panics
///
/// Panics if this reference is associated with a different store.
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)
}
}

/// Downcast this `anyref` to an `i31`, panicking if this `anyref` is not a
/// `struct`.
///
/// # Errors
///
/// Return an error if this reference has been unrooted.
///
/// # Panics
///
/// Panics if this reference is associated with a different store, or if
/// this `anyref` is not a `struct`.
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"))
}
}

unsafe impl WasmTy for Rooted<AnyRef> {
Expand Down Expand Up @@ -457,8 +542,13 @@ unsafe impl WasmTy for ManuallyRooted<AnyRef> {
}

#[inline]
fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
unreachable!()
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<()> {
Expand Down
4 changes: 2 additions & 2 deletions crates/wasmtime/src/runtime/gc/enabled/externref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ use core::mem::MaybeUninit;
#[derive(Debug, Clone)]
#[repr(transparent)]
pub struct ExternRef {
inner: GcRootIndex,
pub(crate) inner: GcRootIndex,
}

unsafe impl GcRefImpl for ExternRef {
Expand Down Expand Up @@ -280,7 +280,7 @@ impl ExternRef {
gc_ref: VMGcRef,
) -> Rooted<Self> {
assert!(
gc_ref.is_extern_ref(),
gc_ref.is_extern_ref(&*store.unwrap_gc_store().gc_heap),
"GC reference {gc_ref:#p} is not an externref"
);
Rooted::new(store, gc_ref)
Expand Down
Loading
Loading