1 //! Working with GC `array` objects.
2 
3 use crate::runtime::vm::VMGcRef;
4 use crate::store::StoreId;
5 use crate::vm::{VMArrayRef, VMGcHeader};
6 use 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 };
12 use crate::{AnyRef, FieldType};
13 use core::mem::{self, MaybeUninit};
14 use 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 /// ```
63 pub struct ArrayRefPre {
64     store_id: StoreId,
65     ty: ArrayType,
66 }
67 
68 impl 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)]
181 pub struct ArrayRef {
182     pub(super) inner: GcRootIndex,
183 }
184 
185 unsafe 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 
203 impl 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 
217 impl 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 
231 impl 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 
700 unsafe 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 
746 unsafe 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 
794 unsafe 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 
840 unsafe 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 }
890