wasmtime/runtime/gc/enabled/
arrayref.rs

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