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