Skip to main content

wasmtime/runtime/gc/enabled/
structref.rs

1//! Working with GC `struct` objects.
2#![cfg(feature = "gc")]
3
4use crate::runtime::vm::VMGcRef;
5use crate::store::{Asyncness, StoreId};
6#[cfg(feature = "async")]
7use crate::vm::VMStore;
8use crate::vm::{self, VMGcHeader, VMStructRef};
9use crate::{AnyRef, FieldType};
10use crate::{
11    AsContext, AsContextMut, EqRef, GcHeapOutOfMemory, GcRefImpl, GcRootIndex, HeapType,
12    OwnedRooted, RefType, Rooted, StructType, Val, ValRaw, ValType, WasmTy,
13    prelude::*,
14    store::{AutoAssertNoGc, StoreContextMut, StoreOpaque, StoreResourceLimiter},
15};
16use alloc::sync::Arc;
17use core::mem::{self, MaybeUninit};
18use wasmtime_environ::{GcLayout, GcStructLayout, VMGcKind, VMSharedTypeIndex};
19
20/// An allocator for a particular Wasm GC struct type.
21///
22/// Every `StructRefPre` is associated with a particular
23/// [`Store`][crate::Store] and a particular [StructType][crate::StructType].
24///
25/// Reusing an allocator across many allocations amortizes some per-type runtime
26/// overheads inside Wasmtime. A `StructRefPre` is to `StructRef`s as an
27/// `InstancePre` is to `Instance`s.
28///
29/// # Example
30///
31/// ```
32/// use wasmtime::*;
33///
34/// # fn foo() -> Result<()> {
35/// let mut config = Config::new();
36/// config.wasm_function_references(true);
37/// config.wasm_gc(true);
38///
39/// let engine = Engine::new(&config)?;
40/// let mut store = Store::new(&engine, ());
41///
42/// // Define a struct type.
43/// let struct_ty = StructType::new(
44///    store.engine(),
45///    [FieldType::new(Mutability::Var, StorageType::I8)],
46/// )?;
47///
48/// // Create an allocator for the struct type.
49/// let allocator = StructRefPre::new(&mut store, struct_ty);
50///
51/// {
52///     let mut scope = RootScope::new(&mut store);
53///
54///     // Allocate a bunch of instances of our struct type using the same
55///     // allocator! This is faster than creating a new allocator for each
56///     // instance we want to allocate.
57///     for i in 0..10 {
58///         StructRef::new(&mut scope, &allocator, &[Val::I32(i)])?;
59///     }
60/// }
61/// # Ok(())
62/// # }
63/// # foo().unwrap();
64/// ```
65pub struct StructRefPre {
66    store_id: StoreId,
67    ty: StructType,
68}
69
70impl StructRefPre {
71    /// Create a new `StructRefPre` that is associated with the given store
72    /// and type.
73    ///
74    /// # Panics
75    ///
76    /// Panics if `ty` was not created with the same
77    /// [`Engine`](crate::Engine) as `store`.
78    pub fn new(mut store: impl AsContextMut, ty: StructType) -> Self {
79        Self::_new(store.as_context_mut().0, ty)
80    }
81
82    pub(crate) fn _new(store: &mut StoreOpaque, ty: StructType) -> Self {
83        store.insert_gc_host_alloc_type(ty.registered_type().clone());
84        let store_id = store.id();
85        StructRefPre { store_id, ty }
86    }
87
88    pub(crate) fn layout(&self) -> &GcStructLayout {
89        self.ty
90            .registered_type()
91            .layout()
92            .expect("struct types have a layout")
93            .unwrap_struct()
94    }
95
96    pub(crate) fn type_index(&self) -> VMSharedTypeIndex {
97        self.ty.registered_type().index()
98    }
99}
100
101/// A reference to a GC-managed `struct` instance.
102///
103/// WebAssembly `struct`s are static, fixed-length, ordered sequences of
104/// fields. Fields are named by index, not by identifier; in this way, they are
105/// similar to Rust's tuples. Each field is mutable or constant and stores
106/// unpacked [`Val`][crate::Val]s or packed 8-/16-bit integers.
107///
108/// Like all WebAssembly references, these are opaque and unforgeable to Wasm:
109/// they cannot be faked and Wasm cannot, for example, cast the integer
110/// `0x12345678` into a reference, pretend it is a valid `structref`, and trick
111/// the host into dereferencing it and segfaulting or worse.
112///
113/// Note that you can also use `Rooted<StructRef>` and
114/// `OwnedRooted<StructRef>` as a type parameter with
115/// [`Func::typed`][crate::Func::typed]- and
116/// [`Func::wrap`][crate::Func::wrap]-style APIs.
117///
118/// # Example
119///
120/// ```
121/// use wasmtime::*;
122///
123/// # fn foo() -> Result<()> {
124/// let mut config = Config::new();
125/// config.wasm_function_references(true);
126/// config.wasm_gc(true);
127///
128/// let engine = Engine::new(&config)?;
129/// let mut store = Store::new(&engine, ());
130///
131/// // Define a struct type.
132/// let struct_ty = StructType::new(
133///    store.engine(),
134///    [FieldType::new(Mutability::Var, StorageType::I8)],
135/// )?;
136///
137/// // Create an allocator for the struct type.
138/// let allocator = StructRefPre::new(&mut store, struct_ty);
139///
140/// {
141///     let mut scope = RootScope::new(&mut store);
142///
143///     // Allocate an instance of the struct type.
144///     let my_struct = StructRef::new(&mut scope, &allocator, &[Val::I32(42)])?;
145///
146///     // That instance's field should have the expected value.
147///     let val = my_struct.field(&mut scope, 0)?.unwrap_i32();
148///     assert_eq!(val, 42);
149///
150///     // And we can update the field's value because it is a mutable field.
151///     my_struct.set_field(&mut scope, 0, Val::I32(36))?;
152///     let new_val = my_struct.field(&mut scope, 0)?.unwrap_i32();
153///     assert_eq!(new_val, 36);
154/// }
155/// # Ok(())
156/// # }
157/// # foo().unwrap();
158/// ```
159#[derive(Debug)]
160#[repr(transparent)]
161pub struct StructRef {
162    pub(super) inner: GcRootIndex,
163}
164
165unsafe impl GcRefImpl for StructRef {
166    fn transmute_ref(index: &GcRootIndex) -> &Self {
167        // Safety: `StructRef` is a newtype of a `GcRootIndex`.
168        let me: &Self = unsafe { mem::transmute(index) };
169
170        // Assert we really are just a newtype of a `GcRootIndex`.
171        assert!(matches!(
172            me,
173            Self {
174                inner: GcRootIndex { .. },
175            }
176        ));
177
178        me
179    }
180}
181
182impl Rooted<StructRef> {
183    /// Upcast this `structref` into an `anyref`.
184    #[inline]
185    pub fn to_anyref(self) -> Rooted<AnyRef> {
186        self.unchecked_cast()
187    }
188
189    /// Upcast this `structref` into an `eqref`.
190    #[inline]
191    pub fn to_eqref(self) -> Rooted<EqRef> {
192        self.unchecked_cast()
193    }
194}
195
196impl OwnedRooted<StructRef> {
197    /// Upcast this `structref` into an `anyref`.
198    #[inline]
199    pub fn to_anyref(self) -> OwnedRooted<AnyRef> {
200        self.unchecked_cast()
201    }
202
203    /// Upcast this `structref` into an `eqref`.
204    #[inline]
205    pub fn to_eqref(self) -> OwnedRooted<EqRef> {
206        self.unchecked_cast()
207    }
208}
209
210impl StructRef {
211    /// Synchronously allocate a new `struct` and get a reference to it.
212    ///
213    /// # Automatic Garbage Collection
214    ///
215    /// If the GC heap is at capacity, and there isn't room for allocating this
216    /// new struct, then this method will automatically trigger a synchronous
217    /// collection in an attempt to free up space in the GC heap.
218    ///
219    /// # Errors
220    ///
221    /// If the given `fields` values' types do not match the field types of the
222    /// `allocator`'s struct type, an error is returned.
223    ///
224    /// If the allocation cannot be satisfied because the GC heap is currently
225    /// out of memory, then a [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory]
226    /// error is returned. The allocation might succeed on a second attempt if
227    /// you drop some rooted GC references and try again.
228    ///
229    /// If `store` is configured with a
230    /// [`ResourceLimiterAsync`](crate::ResourceLimiterAsync) then an error
231    /// will be returned because [`StructRef::new_async`] should be used
232    /// instead.
233    ///
234    /// # Panics
235    ///
236    /// Panics if the allocator, or any of the field values, is not associated
237    /// with the given store.
238    pub fn new(
239        mut store: impl AsContextMut,
240        allocator: &StructRefPre,
241        fields: &[Val],
242    ) -> Result<Rooted<StructRef>> {
243        let (mut limiter, store) = store
244            .as_context_mut()
245            .0
246            .validate_sync_resource_limiter_and_store_opaque()?;
247        vm::assert_ready(Self::_new_async(
248            store,
249            limiter.as_mut(),
250            allocator,
251            fields,
252            Asyncness::No,
253        ))
254    }
255
256    /// Asynchronously allocate a new `struct` and get a reference to it.
257    ///
258    /// # Automatic Garbage Collection
259    ///
260    /// If the GC heap is at capacity, and there isn't room for allocating this
261    /// new struct, then this method will automatically trigger a synchronous
262    /// collection in an attempt to free up space in the GC heap.
263    ///
264    /// # Errors
265    ///
266    /// If the given `fields` values' types do not match the field types of the
267    /// `allocator`'s struct type, an error is returned.
268    ///
269    /// If the allocation cannot be satisfied because the GC heap is currently
270    /// out of memory, then a [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory]
271    /// error is returned. The allocation might succeed on a second attempt if
272    /// you drop some rooted GC references and try again.
273    ///
274    /// # Panics
275    ///
276    /// Panics if the allocator, or any of the field values, is not associated
277    /// with the given store.
278    #[cfg(feature = "async")]
279    pub async fn new_async(
280        mut store: impl AsContextMut,
281        allocator: &StructRefPre,
282        fields: &[Val],
283    ) -> Result<Rooted<StructRef>> {
284        let (mut limiter, store) = store.as_context_mut().0.resource_limiter_and_store_opaque();
285        Self::_new_async(store, limiter.as_mut(), allocator, fields, Asyncness::Yes).await
286    }
287
288    pub(crate) async fn _new_async(
289        store: &mut StoreOpaque,
290        limiter: Option<&mut StoreResourceLimiter<'_>>,
291        allocator: &StructRefPre,
292        fields: &[Val],
293        asyncness: Asyncness,
294    ) -> Result<Rooted<StructRef>> {
295        Self::type_check_fields(store, allocator, fields)?;
296        store
297            .retry_after_gc_async(limiter, (), asyncness, |store, ()| {
298                Self::new_unchecked(store, allocator, fields)
299            })
300            .await
301    }
302
303    /// Type check the field values before allocating a new struct.
304    fn type_check_fields(
305        store: &mut StoreOpaque,
306        allocator: &StructRefPre,
307        fields: &[Val],
308    ) -> Result<(), Error> {
309        let expected_len = allocator.ty.fields().len();
310        let actual_len = fields.len();
311        ensure!(
312            actual_len == expected_len,
313            "expected {expected_len} fields, got {actual_len}"
314        );
315        for (ty, val) in allocator.ty.fields().zip(fields) {
316            assert!(
317                val.comes_from_same_store(store),
318                "field value comes from the wrong store",
319            );
320            let ty = ty.element_type().unpack();
321            val.ensure_matches_ty(store, ty)
322                .context("field type mismatch")?;
323        }
324        Ok(())
325    }
326
327    /// Given that the field values have already been type checked, allocate a
328    /// new struct.
329    ///
330    /// Does not attempt GC+retry on OOM, that is the caller's responsibility.
331    fn new_unchecked(
332        store: &mut StoreOpaque,
333        allocator: &StructRefPre,
334        fields: &[Val],
335    ) -> Result<Rooted<StructRef>> {
336        assert_eq!(
337            store.id(),
338            allocator.store_id,
339            "attempted to use a `StructRefPre` with the wrong store"
340        );
341
342        // Allocate the struct and write each field value into the appropriate
343        // offset.
344        let structref = store
345            .require_gc_store_mut()?
346            .alloc_uninit_struct(allocator.type_index(), &allocator.layout())
347            .context("unrecoverable error when allocating new `structref`")?
348            .map_err(|n| GcHeapOutOfMemory::new((), n))?;
349
350        // From this point on, if we get any errors, then the struct is not
351        // fully initialized, so we need to eagerly deallocate it before the
352        // next GC where the collector might try to interpret one of the
353        // uninitialized fields as a GC reference.
354        let mut store = AutoAssertNoGc::new(store);
355        match (|| {
356            for (index, (ty, val)) in allocator.ty.fields().zip(fields).enumerate() {
357                structref.initialize_field(
358                    &mut store,
359                    allocator.layout(),
360                    ty.element_type(),
361                    index,
362                    *val,
363                )?;
364            }
365            Ok(())
366        })() {
367            Ok(()) => Ok(Rooted::new(&mut store, structref.into())),
368            Err(e) => {
369                store
370                    .require_gc_store_mut()?
371                    .dealloc_uninit_struct(structref)?;
372                Err(e)
373            }
374        }
375    }
376
377    #[inline]
378    pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
379        self.inner.comes_from_same_store(store)
380    }
381
382    /// Get this `structref`'s type.
383    ///
384    /// # Errors
385    ///
386    /// Return an error if this reference has been unrooted.
387    ///
388    /// # Panics
389    ///
390    /// Panics if this reference is associated with a different store.
391    pub fn ty(&self, store: impl AsContext) -> Result<StructType> {
392        self._ty(store.as_context().0)
393    }
394
395    pub(crate) fn _ty(&self, store: &StoreOpaque) -> Result<StructType> {
396        assert!(self.comes_from_same_store(store));
397        let index = self.type_index(store)?;
398        Ok(StructType::from_shared_type_index(store.engine(), index))
399    }
400
401    /// Does this `structref` match the given type?
402    ///
403    /// That is, is this struct's type a subtype of the given type?
404    ///
405    /// # Errors
406    ///
407    /// Return an error if this reference has been unrooted.
408    ///
409    /// # Panics
410    ///
411    /// Panics if this reference is associated with a different store or if the
412    /// type is not associated with the store's engine.
413    pub fn matches_ty(&self, store: impl AsContext, ty: &StructType) -> Result<bool> {
414        self._matches_ty(store.as_context().0, ty)
415    }
416
417    pub(crate) fn _matches_ty(&self, store: &StoreOpaque, ty: &StructType) -> Result<bool> {
418        assert!(self.comes_from_same_store(store));
419        Ok(self._ty(store)?.matches(ty))
420    }
421
422    pub(crate) fn ensure_matches_ty(&self, store: &StoreOpaque, ty: &StructType) -> Result<()> {
423        if !self.comes_from_same_store(store) {
424            bail!("function used with wrong store");
425        }
426        if self._matches_ty(store, ty)? {
427            Ok(())
428        } else {
429            let actual_ty = self._ty(store)?;
430            bail!("type mismatch: expected `(ref {ty})`, found `(ref {actual_ty})`")
431        }
432    }
433
434    /// Get the values of this struct's fields.
435    ///
436    /// Note that `i8` and `i16` field values are zero-extended into
437    /// `Val::I32(_)`s.
438    ///
439    /// # Errors
440    ///
441    /// Return an error if this reference has been unrooted.
442    ///
443    /// # Panics
444    ///
445    /// Panics if this reference is associated with a different store.
446    pub fn fields<'a, T: 'static>(
447        &'a self,
448        store: impl Into<StoreContextMut<'a, T>>,
449    ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> {
450        self._fields(store.into().0)
451    }
452
453    pub(crate) fn _fields<'a>(
454        &'a self,
455        store: &'a mut StoreOpaque,
456    ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> {
457        assert!(self.comes_from_same_store(store));
458        let store = AutoAssertNoGc::new(store);
459
460        let gc_ref = self.inner.try_gc_ref(&store)?;
461        let header = store.require_gc_store()?.header(gc_ref)?;
462        debug_assert!(header.kind().matches(VMGcKind::StructRef));
463
464        let index = header.ty().expect("structrefs should have concrete types");
465        let ty = StructType::from_shared_type_index(store.engine(), index);
466        let len = ty.fields().len();
467
468        return Ok(Fields {
469            structref: self,
470            store,
471            index: 0,
472            len,
473        });
474
475        struct Fields<'a, 'b> {
476            structref: &'a StructRef,
477            store: AutoAssertNoGc<'b>,
478            index: usize,
479            len: usize,
480        }
481
482        impl Iterator for Fields<'_, '_> {
483            type Item = Val;
484
485            #[inline]
486            fn next(&mut self) -> Option<Self::Item> {
487                let i = self.index;
488                debug_assert!(i <= self.len);
489                if i >= self.len {
490                    return None;
491                }
492                self.index += 1;
493                self.structref._field(&mut self.store, i).ok()
494            }
495
496            #[inline]
497            fn size_hint(&self) -> (usize, Option<usize>) {
498                let len = self.len - self.index;
499                (len, Some(len))
500            }
501        }
502
503        impl ExactSizeIterator for Fields<'_, '_> {
504            #[inline]
505            fn len(&self) -> usize {
506                self.len - self.index
507            }
508        }
509    }
510
511    fn header<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMGcHeader> {
512        assert!(self.comes_from_same_store(&store));
513        let gc_ref = self.inner.try_gc_ref(store)?;
514        Ok(store.require_gc_store()?.header(gc_ref)?)
515    }
516
517    fn structref<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMStructRef> {
518        assert!(self.comes_from_same_store(&store));
519        let gc_ref = self.inner.try_gc_ref(store)?;
520        debug_assert!(self.header(store)?.kind().matches(VMGcKind::StructRef));
521        Ok(gc_ref.as_structref_unchecked())
522    }
523
524    fn layout(&self, store: &AutoAssertNoGc<'_>) -> Result<Arc<GcStructLayout>> {
525        assert!(self.comes_from_same_store(&store));
526        let type_index = self.type_index(store)?;
527        let layout = store
528            .engine()
529            .signatures()
530            .layout(type_index)
531            .expect("struct types should have GC layouts");
532        match layout {
533            GcLayout::Struct(s) => Ok(s),
534            GcLayout::Array(_) => unreachable!(),
535        }
536    }
537
538    fn field_ty(&self, store: &StoreOpaque, field: usize) -> Result<FieldType> {
539        let ty = self._ty(store)?;
540        match ty.field(field) {
541            Some(f) => Ok(f),
542            None => {
543                let len = ty.fields().len();
544                bail!("cannot access field {field}: struct only has {len} fields")
545            }
546        }
547    }
548
549    /// Get this struct's `index`th field.
550    ///
551    /// Note that `i8` and `i16` field values are zero-extended into
552    /// `Val::I32(_)`s.
553    ///
554    /// # Errors
555    ///
556    /// Returns an `Err(_)` if the index is out of bounds or this reference has
557    /// been unrooted.
558    ///
559    /// # Panics
560    ///
561    /// Panics if this reference is associated with a different store.
562    pub fn field(&self, mut store: impl AsContextMut, index: usize) -> Result<Val> {
563        let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
564        self._field(&mut store, index)
565    }
566
567    pub(crate) fn _field(&self, store: &mut AutoAssertNoGc<'_>, index: usize) -> Result<Val> {
568        assert!(self.comes_from_same_store(store));
569        let structref = self.structref(store)?.unchecked_copy();
570        let field_ty = self.field_ty(store, index)?;
571        let layout = self.layout(store)?;
572        structref.read_field(store, &layout, field_ty.element_type(), index)
573    }
574
575    /// Set this struct's `index`th field.
576    ///
577    /// # Errors
578    ///
579    /// Returns an error in the following scenarios:
580    ///
581    /// * When given a value of the wrong type, such as trying to set an `f32`
582    ///   field to an `i64` value.
583    ///
584    /// * When the field is not mutable.
585    ///
586    /// * When this struct does not have an `index`th field, i.e. `index` is out
587    ///   of bounds.
588    ///
589    /// * When `value` is a GC reference that has since been unrooted.
590    ///
591    /// # Panics
592    ///
593    /// Panics if this reference is associated with a different store.
594    pub fn set_field(&self, mut store: impl AsContextMut, index: usize, value: Val) -> Result<()> {
595        self._set_field(store.as_context_mut().0, index, value)
596    }
597
598    pub(crate) fn _set_field(
599        &self,
600        store: &mut StoreOpaque,
601        index: usize,
602        value: Val,
603    ) -> Result<()> {
604        assert!(self.comes_from_same_store(store));
605        let mut store = AutoAssertNoGc::new(store);
606
607        let field_ty = self.field_ty(&store, index)?;
608        ensure!(
609            field_ty.mutability().is_var(),
610            "cannot set field {index}: field is not mutable"
611        );
612
613        value
614            .ensure_matches_ty(&store, &field_ty.element_type().unpack())
615            .with_context(|| format!("cannot set field {index}: type mismatch"))?;
616
617        let layout = self.layout(&store)?;
618        let structref = self.structref(&store)?.unchecked_copy();
619
620        structref.write_field(&mut store, &layout, field_ty.element_type(), index, value)
621    }
622
623    pub(crate) fn type_index(&self, store: &StoreOpaque) -> Result<VMSharedTypeIndex> {
624        let gc_ref = self.inner.try_gc_ref(store)?;
625        let header = store.require_gc_store()?.header(gc_ref)?;
626        debug_assert!(header.kind().matches(VMGcKind::StructRef));
627        Ok(header.ty().expect("structrefs should have concrete types"))
628    }
629
630    /// Create a new `Rooted<StructRef>` from the given GC reference.
631    ///
632    /// `gc_ref` should point to a valid `structref` and should belong to the
633    /// store's GC heap. Failure to uphold these invariants is memory safe but
634    /// will lead to general incorrectness such as panics or wrong results.
635    pub(crate) fn from_cloned_gc_ref(
636        store: &mut AutoAssertNoGc<'_>,
637        gc_ref: VMGcRef,
638    ) -> Rooted<Self> {
639        debug_assert!(gc_ref.is_structref(&*store.unwrap_gc_store().gc_heap));
640        Rooted::new(store, gc_ref)
641    }
642}
643
644unsafe impl WasmTy for Rooted<StructRef> {
645    #[inline]
646    fn valtype() -> ValType {
647        ValType::Ref(RefType::new(false, HeapType::Struct))
648    }
649
650    #[inline]
651    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
652        self.comes_from_same_store(store)
653    }
654
655    #[inline]
656    fn dynamic_concrete_type_check(
657        &self,
658        store: &StoreOpaque,
659        _nullable: bool,
660        ty: &HeapType,
661    ) -> Result<()> {
662        match ty {
663            HeapType::Any | HeapType::Eq | HeapType::Struct => Ok(()),
664            HeapType::ConcreteStruct(ty) => self.ensure_matches_ty(store, ty),
665
666            HeapType::Extern
667            | HeapType::NoExtern
668            | HeapType::Func
669            | HeapType::ConcreteFunc(_)
670            | HeapType::NoFunc
671            | HeapType::I31
672            | HeapType::Array
673            | HeapType::ConcreteArray(_)
674            | HeapType::None
675            | HeapType::NoCont
676            | HeapType::Cont
677            | HeapType::ConcreteCont(_)
678            | HeapType::NoExn
679            | HeapType::Exn
680            | HeapType::ConcreteExn(_) => bail!(
681                "type mismatch: expected `(ref {ty})`, got `(ref {})`",
682                self._ty(store)?,
683            ),
684        }
685    }
686
687    fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
688        self.wasm_ty_store(store, ptr, ValRaw::anyref)
689    }
690
691    unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
692        Self::wasm_ty_load(store, ptr.get_anyref(), StructRef::from_cloned_gc_ref)
693    }
694}
695
696unsafe impl WasmTy for Option<Rooted<StructRef>> {
697    #[inline]
698    fn valtype() -> ValType {
699        ValType::STRUCTREF
700    }
701
702    #[inline]
703    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
704        self.map_or(true, |x| x.comes_from_same_store(store))
705    }
706
707    #[inline]
708    fn dynamic_concrete_type_check(
709        &self,
710        store: &StoreOpaque,
711        nullable: bool,
712        ty: &HeapType,
713    ) -> Result<()> {
714        match self {
715            Some(s) => Rooted::<StructRef>::dynamic_concrete_type_check(s, store, nullable, ty),
716            None => {
717                ensure!(
718                    nullable,
719                    "expected a non-null reference, but found a null reference"
720                );
721                Ok(())
722            }
723        }
724    }
725
726    #[inline]
727    fn is_vmgcref_and_points_to_object(&self) -> bool {
728        self.is_some()
729    }
730
731    fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
732        <Rooted<StructRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
733    }
734
735    unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
736        <Rooted<StructRef>>::wasm_ty_option_load(
737            store,
738            ptr.get_anyref(),
739            StructRef::from_cloned_gc_ref,
740        )
741    }
742}
743
744unsafe impl WasmTy for OwnedRooted<StructRef> {
745    #[inline]
746    fn valtype() -> ValType {
747        ValType::Ref(RefType::new(false, HeapType::Struct))
748    }
749
750    #[inline]
751    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
752        self.comes_from_same_store(store)
753    }
754
755    #[inline]
756    fn dynamic_concrete_type_check(
757        &self,
758        store: &StoreOpaque,
759        _: bool,
760        ty: &HeapType,
761    ) -> Result<()> {
762        match ty {
763            HeapType::Any | HeapType::Eq | HeapType::Struct => Ok(()),
764            HeapType::ConcreteStruct(ty) => self.ensure_matches_ty(store, ty),
765
766            HeapType::Extern
767            | HeapType::NoExtern
768            | HeapType::Func
769            | HeapType::ConcreteFunc(_)
770            | HeapType::NoFunc
771            | HeapType::I31
772            | HeapType::Array
773            | HeapType::ConcreteArray(_)
774            | HeapType::None
775            | HeapType::NoCont
776            | HeapType::Cont
777            | HeapType::ConcreteCont(_)
778            | HeapType::NoExn
779            | HeapType::Exn
780            | HeapType::ConcreteExn(_) => bail!(
781                "type mismatch: expected `(ref {ty})`, got `(ref {})`",
782                self._ty(store)?,
783            ),
784        }
785    }
786
787    fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
788        self.wasm_ty_store(store, ptr, ValRaw::anyref)
789    }
790
791    unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
792        Self::wasm_ty_load(store, ptr.get_anyref(), StructRef::from_cloned_gc_ref)
793    }
794}
795
796unsafe impl WasmTy for Option<OwnedRooted<StructRef>> {
797    #[inline]
798    fn valtype() -> ValType {
799        ValType::STRUCTREF
800    }
801
802    #[inline]
803    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
804        self.as_ref()
805            .map_or(true, |x| x.comes_from_same_store(store))
806    }
807
808    #[inline]
809    fn dynamic_concrete_type_check(
810        &self,
811        store: &StoreOpaque,
812        nullable: bool,
813        ty: &HeapType,
814    ) -> Result<()> {
815        match self {
816            Some(s) => {
817                OwnedRooted::<StructRef>::dynamic_concrete_type_check(s, store, nullable, ty)
818            }
819            None => {
820                ensure!(
821                    nullable,
822                    "expected a non-null reference, but found a null reference"
823                );
824                Ok(())
825            }
826        }
827    }
828
829    #[inline]
830    fn is_vmgcref_and_points_to_object(&self) -> bool {
831        self.is_some()
832    }
833
834    fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
835        <OwnedRooted<StructRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
836    }
837
838    unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
839        <OwnedRooted<StructRef>>::wasm_ty_option_load(
840            store,
841            ptr.get_anyref(),
842            StructRef::from_cloned_gc_ref,
843        )
844    }
845}