wasmtime/runtime/gc/enabled/
structref.rs

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