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             .err2anyhow()
325             .context("unrecoverable error when allocating new `arrayref`")?
326             .ok_or_else(|| GcHeapOutOfMemory::new(()))
327             .err2anyhow()?;
328 
329         // From this point on, if we get any errors, then the array is not
330         // fully initialized, so we need to eagerly deallocate it before the
331         // next GC where the collector might try to interpret one of the
332         // uninitialized fields as a GC reference.
333         let mut store = AutoAssertNoGc::new(store);
334         match (|| {
335             let elem_ty = allocator.ty.element_type();
336             for (i, elem) in elems.enumerate() {
337                 let i = u32::try_from(i).unwrap();
338                 debug_assert!(i < len);
339                 arrayref.initialize_elem(&mut store, allocator.layout(), &elem_ty, i, *elem)?;
340             }
341             Ok(())
342         })() {
343             Ok(()) => Ok(Rooted::new(&mut store, arrayref.into())),
344             Err(e) => {
345                 store.gc_store_mut()?.dealloc_uninit_array(arrayref);
346                 Err(e)
347             }
348         }
349     }
350 
351     /// Allocate a new `array` containing the given elements.
352     ///
353     /// For example, `ArrayRef::new_fixed(ctx, pre, &[Val::I64(4), Val::I64(5),
354     /// Val::I64(6)])` allocates the array `[4, 5, 6]`.
355     ///
356     /// This is similar to the `array.new_fixed` instruction.
357     ///
358     /// # Errors
359     ///
360     /// If any of the `elems` values' type does not match the `allocator`'s
361     /// array type's element type, an error is returned.
362     ///
363     /// If the allocation cannot be satisfied because the GC heap is currently
364     /// out of memory, but performing a garbage collection might free up space
365     /// such that retrying the allocation afterwards might succeed, then a
366     /// [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory] error is returned.
367     ///
368     /// # Panics
369     ///
370     /// Panics if the allocator or any of the `elems` values are not associated
371     /// with the given store.
372     pub fn new_fixed(
373         mut store: impl AsContextMut,
374         allocator: &ArrayRefPre,
375         elems: &[Val],
376     ) -> Result<Rooted<ArrayRef>> {
377         Self::_new_fixed(store.as_context_mut().0, allocator, elems)
378     }
379 
380     pub(crate) fn _new_fixed(
381         store: &mut StoreOpaque,
382         allocator: &ArrayRefPre,
383         elems: &[Val],
384     ) -> Result<Rooted<ArrayRef>> {
385         assert_eq!(
386             store.id(),
387             allocator.store_id,
388             "attempted to use a `ArrayRefPre` with the wrong store"
389         );
390 
391         // Type check the elements against the element type.
392         for elem in elems {
393             elem.ensure_matches_ty(store, allocator.ty.element_type().unpack())
394                 .context("element type mismatch")?;
395         }
396 
397         return Self::_new_unchecked(store, allocator, elems.iter());
398     }
399 
400     #[inline]
401     pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
402         self.inner.comes_from_same_store(store)
403     }
404 
405     /// Get this `arrayref`'s type.
406     ///
407     /// # Errors
408     ///
409     /// Return an error if this reference has been unrooted.
410     ///
411     /// # Panics
412     ///
413     /// Panics if this reference is associated with a different store.
414     pub fn ty(&self, store: impl AsContext) -> Result<ArrayType> {
415         self._ty(store.as_context().0)
416     }
417 
418     pub(crate) fn _ty(&self, store: &StoreOpaque) -> Result<ArrayType> {
419         assert!(self.comes_from_same_store(store));
420         let index = self.type_index(store)?;
421         Ok(ArrayType::from_shared_type_index(store.engine(), index))
422     }
423 
424     /// Does this `arrayref` match the given type?
425     ///
426     /// That is, is this array's type a subtype of the given type?
427     ///
428     /// # Errors
429     ///
430     /// Return an error if this reference has been unrooted.
431     ///
432     /// # Panics
433     ///
434     /// Panics if this reference is associated with a different store or if the
435     /// type is not associated with the store's engine.
436     pub fn matches_ty(&self, store: impl AsContext, ty: &ArrayType) -> Result<bool> {
437         self._matches_ty(store.as_context().0, ty)
438     }
439 
440     pub(crate) fn _matches_ty(&self, store: &StoreOpaque, ty: &ArrayType) -> Result<bool> {
441         assert!(self.comes_from_same_store(store));
442         Ok(self._ty(store)?.matches(ty))
443     }
444 
445     pub(crate) fn ensure_matches_ty(&self, store: &StoreOpaque, ty: &ArrayType) -> Result<()> {
446         if !self.comes_from_same_store(store) {
447             bail!("function used with wrong store");
448         }
449         if self._matches_ty(store, ty)? {
450             Ok(())
451         } else {
452             let actual_ty = self._ty(store)?;
453             bail!("type mismatch: expected `(ref {ty})`, found `(ref {actual_ty})`")
454         }
455     }
456 
457     /// Get the length of this array.
458     ///
459     /// # Errors
460     ///
461     /// Return an error if this reference has been unrooted.
462     ///
463     /// # Panics
464     ///
465     /// Panics if this reference is associated with a different store.
466     pub fn len(&self, store: impl AsContext) -> Result<u32> {
467         self._len(store.as_context().0)
468     }
469 
470     pub(crate) fn _len(&self, store: &StoreOpaque) -> Result<u32> {
471         assert!(self.comes_from_same_store(store));
472         let gc_ref = self.inner.try_gc_ref(store)?;
473         debug_assert!({
474             let header = store.gc_store()?.header(gc_ref);
475             header.kind().matches(VMGcKind::ArrayRef)
476         });
477         let arrayref = gc_ref.as_arrayref_unchecked();
478         Ok(arrayref.len(store))
479     }
480 
481     /// Get the values of this array's elements.
482     ///
483     /// Note that `i8` and `i16` field values are zero-extended into
484     /// `Val::I32(_)`s.
485     ///
486     /// # Errors
487     ///
488     /// Return an error if this reference has been unrooted.
489     ///
490     /// # Panics
491     ///
492     /// Panics if this reference is associated with a different store.
493     pub fn elems<'a, T: 'a>(
494         &'a self,
495         store: impl Into<StoreContextMut<'a, T>>,
496     ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> {
497         self._elems(store.into().0)
498     }
499 
500     pub(crate) fn _elems<'a>(
501         &'a self,
502         store: &'a mut StoreOpaque,
503     ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> {
504         assert!(self.comes_from_same_store(store));
505         let store = AutoAssertNoGc::new(store);
506 
507         let gc_ref = self.inner.try_gc_ref(&store)?;
508         let header = store.gc_store()?.header(gc_ref);
509         debug_assert!(header.kind().matches(VMGcKind::ArrayRef));
510 
511         let len = self._len(&store)?;
512 
513         return Ok(Elems {
514             arrayref: self,
515             store,
516             index: 0,
517             len,
518         });
519 
520         struct Elems<'a, 'b> {
521             arrayref: &'a ArrayRef,
522             store: AutoAssertNoGc<'b>,
523             index: u32,
524             len: u32,
525         }
526 
527         impl Iterator for Elems<'_, '_> {
528             type Item = Val;
529 
530             #[inline]
531             fn next(&mut self) -> Option<Self::Item> {
532                 let i = self.index;
533                 debug_assert!(i <= self.len);
534                 if i >= self.len {
535                     return None;
536                 }
537                 self.index += 1;
538                 Some(self.arrayref._get(&mut self.store, i).unwrap())
539             }
540 
541             #[inline]
542             fn size_hint(&self) -> (usize, Option<usize>) {
543                 let len = self.len - self.index;
544                 let len = usize::try_from(len).unwrap();
545                 (len, Some(len))
546             }
547         }
548 
549         impl ExactSizeIterator for Elems<'_, '_> {
550             #[inline]
551             fn len(&self) -> usize {
552                 let len = self.len - self.index;
553                 usize::try_from(len).unwrap()
554             }
555         }
556     }
557 
558     fn header<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMGcHeader> {
559         assert!(self.comes_from_same_store(&store));
560         let gc_ref = self.inner.try_gc_ref(store)?;
561         Ok(store.gc_store()?.header(gc_ref))
562     }
563 
564     fn arrayref<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMArrayRef> {
565         assert!(self.comes_from_same_store(&store));
566         let gc_ref = self.inner.try_gc_ref(store)?;
567         debug_assert!(self.header(store)?.kind().matches(VMGcKind::ArrayRef));
568         Ok(gc_ref.as_arrayref_unchecked())
569     }
570 
571     pub(crate) fn layout(&self, store: &AutoAssertNoGc<'_>) -> Result<GcArrayLayout> {
572         assert!(self.comes_from_same_store(&store));
573         let type_index = self.type_index(store)?;
574         let layout = store
575             .engine()
576             .signatures()
577             .layout(type_index)
578             .expect("array types should have GC layouts");
579         match layout {
580             GcLayout::Array(a) => Ok(a),
581             GcLayout::Struct(_) => unreachable!(),
582         }
583     }
584 
585     fn field_ty(&self, store: &StoreOpaque) -> Result<FieldType> {
586         let ty = self._ty(store)?;
587         Ok(ty.field_type())
588     }
589 
590     /// Get this array's `index`th element.
591     ///
592     /// Note that `i8` and `i16` field values are zero-extended into
593     /// `Val::I32(_)`s.
594     ///
595     /// # Errors
596     ///
597     /// Returns an `Err(_)` if the index is out of bounds or this reference has
598     /// been unrooted.
599     ///
600     /// # Panics
601     ///
602     /// Panics if this reference is associated with a different store.
603     pub fn get(&self, mut store: impl AsContextMut, index: u32) -> Result<Val> {
604         let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
605         self._get(&mut store, index)
606     }
607 
608     pub(crate) fn _get(&self, store: &mut AutoAssertNoGc<'_>, index: u32) -> Result<Val> {
609         assert!(
610             self.comes_from_same_store(store),
611             "attempted to use an array with the wrong store",
612         );
613         let arrayref = self.arrayref(store)?.unchecked_copy();
614         let field_ty = self.field_ty(store)?;
615         let layout = self.layout(store)?;
616         let len = arrayref.len(store);
617         ensure!(
618             index < len,
619             "index out of bounds: the length is {len} but the index is {index}"
620         );
621         Ok(arrayref.read_elem(store, &layout, field_ty.element_type(), index))
622     }
623 
624     /// Set this array's `index`th element.
625     ///
626     /// # Errors
627     ///
628     /// Returns an error in the following scenarios:
629     ///
630     /// * When given a value of the wrong type, such as trying to write an `f32`
631     ///   value into an array of `i64` elements.
632     ///
633     /// * When the array elements are not mutable.
634     ///
635     /// * When `index` is not within the range `0..self.len(ctx)`.
636     ///
637     /// * When `value` is a GC reference that has since been unrooted.
638     ///
639     /// # Panics
640     ///
641     /// Panics if either this reference or the given `value` is associated with
642     /// a different store.
643     pub fn set(&self, mut store: impl AsContextMut, index: u32, value: Val) -> Result<()> {
644         self._set(store.as_context_mut().0, index, value)
645     }
646 
647     pub(crate) fn _set(&self, store: &mut StoreOpaque, index: u32, value: Val) -> Result<()> {
648         assert!(
649             self.comes_from_same_store(store),
650             "attempted to use an array with the wrong store",
651         );
652         assert!(
653             value.comes_from_same_store(store),
654             "attempted to use a value with the wrong store",
655         );
656 
657         let mut store = AutoAssertNoGc::new(store);
658 
659         let field_ty = self.field_ty(&store)?;
660         ensure!(
661             field_ty.mutability().is_var(),
662             "cannot set element {index}: array elements are not mutable"
663         );
664 
665         value
666             .ensure_matches_ty(&store, &field_ty.element_type().unpack())
667             .with_context(|| format!("cannot set element {index}: type mismatch"))?;
668 
669         let layout = self.layout(&store)?;
670         let arrayref = self.arrayref(&store)?.unchecked_copy();
671 
672         let len = arrayref.len(&store);
673         ensure!(
674             index < len,
675             "index out of bounds: the length is {len} but the index is {index}"
676         );
677 
678         arrayref.write_elem(&mut store, &layout, field_ty.element_type(), index, value)
679     }
680 
681     pub(crate) fn type_index(&self, store: &StoreOpaque) -> Result<VMSharedTypeIndex> {
682         let gc_ref = self.inner.try_gc_ref(store)?;
683         let header = store.gc_store()?.header(gc_ref);
684         debug_assert!(header.kind().matches(VMGcKind::ArrayRef));
685         Ok(header.ty().expect("arrayrefs should have concrete types"))
686     }
687 
688     /// Create a new `Rooted<ArrayRef>` from the given GC reference.
689     ///
690     /// `gc_ref` should point to a valid `arrayref` and should belong to the
691     /// store's GC heap. Failure to uphold these invariants is memory safe but
692     /// will lead to general incorrectness such as panics or wrong results.
693     pub(crate) fn from_cloned_gc_ref(
694         store: &mut AutoAssertNoGc<'_>,
695         gc_ref: VMGcRef,
696     ) -> Rooted<Self> {
697         debug_assert!(gc_ref.is_arrayref(&*store.unwrap_gc_store().gc_heap));
698         Rooted::new(store, gc_ref)
699     }
700 }
701 
702 unsafe impl WasmTy for Rooted<ArrayRef> {
703     #[inline]
704     fn valtype() -> ValType {
705         ValType::Ref(RefType::new(false, HeapType::Array))
706     }
707 
708     #[inline]
709     fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
710         self.comes_from_same_store(store)
711     }
712 
713     #[inline]
714     fn dynamic_concrete_type_check(
715         &self,
716         store: &StoreOpaque,
717         _nullable: bool,
718         ty: &HeapType,
719     ) -> Result<()> {
720         match ty {
721             HeapType::Any | HeapType::Eq | HeapType::Array => Ok(()),
722             HeapType::ConcreteArray(ty) => self.ensure_matches_ty(store, ty),
723 
724             HeapType::Extern
725             | HeapType::NoExtern
726             | HeapType::Func
727             | HeapType::ConcreteFunc(_)
728             | HeapType::NoFunc
729             | HeapType::I31
730             | HeapType::Struct
731             | HeapType::ConcreteStruct(_)
732             | HeapType::None => bail!(
733                 "type mismatch: expected `(ref {ty})`, got `(ref {})`",
734                 self._ty(store)?,
735             ),
736         }
737     }
738 
739     fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
740         self.wasm_ty_store(store, ptr, ValRaw::anyref)
741     }
742 
743     unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
744         Self::wasm_ty_load(store, ptr.get_anyref(), ArrayRef::from_cloned_gc_ref)
745     }
746 }
747 
748 unsafe impl WasmTy for Option<Rooted<ArrayRef>> {
749     #[inline]
750     fn valtype() -> ValType {
751         ValType::ARRAYREF
752     }
753 
754     #[inline]
755     fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
756         self.map_or(true, |x| x.comes_from_same_store(store))
757     }
758 
759     #[inline]
760     fn dynamic_concrete_type_check(
761         &self,
762         store: &StoreOpaque,
763         nullable: bool,
764         ty: &HeapType,
765     ) -> Result<()> {
766         match self {
767             Some(s) => Rooted::<ArrayRef>::dynamic_concrete_type_check(s, store, nullable, ty),
768             None => {
769                 ensure!(
770                     nullable,
771                     "expected a non-null reference, but found a null reference"
772                 );
773                 Ok(())
774             }
775         }
776     }
777 
778     #[inline]
779     fn is_vmgcref_and_points_to_object(&self) -> bool {
780         self.is_some()
781     }
782 
783     fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
784         <Rooted<ArrayRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
785     }
786 
787     unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
788         <Rooted<ArrayRef>>::wasm_ty_option_load(
789             store,
790             ptr.get_anyref(),
791             ArrayRef::from_cloned_gc_ref,
792         )
793     }
794 }
795 
796 unsafe impl WasmTy for ManuallyRooted<ArrayRef> {
797     #[inline]
798     fn valtype() -> ValType {
799         ValType::Ref(RefType::new(false, HeapType::Array))
800     }
801 
802     #[inline]
803     fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
804         self.comes_from_same_store(store)
805     }
806 
807     #[inline]
808     fn dynamic_concrete_type_check(
809         &self,
810         store: &StoreOpaque,
811         _: bool,
812         ty: &HeapType,
813     ) -> Result<()> {
814         match ty {
815             HeapType::Any | HeapType::Eq | HeapType::Array => Ok(()),
816             HeapType::ConcreteArray(ty) => self.ensure_matches_ty(store, ty),
817 
818             HeapType::Extern
819             | HeapType::NoExtern
820             | HeapType::Func
821             | HeapType::ConcreteFunc(_)
822             | HeapType::NoFunc
823             | HeapType::I31
824             | HeapType::Struct
825             | HeapType::ConcreteStruct(_)
826             | HeapType::None => bail!(
827                 "type mismatch: expected `(ref {ty})`, got `(ref {})`",
828                 self._ty(store)?,
829             ),
830         }
831     }
832 
833     fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
834         self.wasm_ty_store(store, ptr, ValRaw::anyref)
835     }
836 
837     unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
838         Self::wasm_ty_load(store, ptr.get_anyref(), ArrayRef::from_cloned_gc_ref)
839     }
840 }
841 
842 unsafe impl WasmTy for Option<ManuallyRooted<ArrayRef>> {
843     #[inline]
844     fn valtype() -> ValType {
845         ValType::ARRAYREF
846     }
847 
848     #[inline]
849     fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
850         self.as_ref()
851             .map_or(true, |x| x.comes_from_same_store(store))
852     }
853 
854     #[inline]
855     fn dynamic_concrete_type_check(
856         &self,
857         store: &StoreOpaque,
858         nullable: bool,
859         ty: &HeapType,
860     ) -> Result<()> {
861         match self {
862             Some(s) => {
863                 ManuallyRooted::<ArrayRef>::dynamic_concrete_type_check(s, store, nullable, ty)
864             }
865             None => {
866                 ensure!(
867                     nullable,
868                     "expected a non-null reference, but found a null reference"
869                 );
870                 Ok(())
871             }
872         }
873     }
874 
875     #[inline]
876     fn is_vmgcref_and_points_to_object(&self) -> bool {
877         self.is_some()
878     }
879 
880     fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
881         <ManuallyRooted<ArrayRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
882     }
883 
884     unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
885         <ManuallyRooted<ArrayRef>>::wasm_ty_option_load(
886             store,
887             ptr.get_anyref(),
888             ArrayRef::from_cloned_gc_ref,
889         )
890     }
891 }
892