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::{AnyRef, FieldType};
7 use crate::{
8     ArrayType, AsContext, AsContextMut, EqRef, GcHeapOutOfMemory, GcRefImpl, GcRootIndex, HeapType,
9     ManuallyRooted, RefType, Rooted, Val, ValRaw, ValType, WasmTy,
10     prelude::*,
11     store::{AutoAssertNoGc, StoreContextMut, StoreOpaque},
12 };
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 /// # let _ = foo();
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 ///         Err(e) => match e.downcast::<GcHeapOutOfMemory<()>>() {
146 ///             // If the heap is out of memory, then do a GC to free up some
147 ///             // space and try again.
148 ///             Ok(oom) => {
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(Some(&oom));
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 ///             // Propagate any other kind of error.
159 ///             Err(e) => return Err(e),
160 ///         }
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     fn transmute_ref(index: &GcRootIndex) -> &Self {
187         // Safety: `ArrayRef` is a newtype of a `GcRootIndex`.
188         let me: &Self = unsafe { mem::transmute(index) };
189 
190         // Assert we really are just a newtype of a `GcRootIndex`.
191         assert!(matches!(
192             me,
193             Self {
194                 inner: GcRootIndex { .. },
195             }
196         ));
197 
198         me
199     }
200 }
201 
202 impl Rooted<ArrayRef> {
203     /// Upcast this `arrayref` into an `anyref`.
204     #[inline]
205     pub fn to_anyref(self) -> Rooted<AnyRef> {
206         self.unchecked_cast()
207     }
208 
209     /// Upcast this `arrayref` into an `eqref`.
210     #[inline]
211     pub fn to_eqref(self) -> Rooted<EqRef> {
212         self.unchecked_cast()
213     }
214 }
215 
216 impl ManuallyRooted<ArrayRef> {
217     /// Upcast this `arrayref` into an `anyref`.
218     #[inline]
219     pub fn to_anyref(self) -> ManuallyRooted<AnyRef> {
220         self.unchecked_cast()
221     }
222 
223     /// Upcast this `arrayref` into an `eqref`.
224     #[inline]
225     pub fn to_eqref(self) -> ManuallyRooted<EqRef> {
226         self.unchecked_cast()
227     }
228 }
229 
230 /// An iterator for elements in `ArrayRef::new[_async].
231 ///
232 /// NB: We can't use `iter::repeat(elem).take(len)` because that doesn't
233 /// implement `ExactSizeIterator`.
234 #[derive(Clone)]
235 struct RepeatN<'a>(&'a Val, u32);
236 
237 impl<'a> Iterator for RepeatN<'a> {
238     type Item = &'a Val;
239 
240     fn next(&mut self) -> Option<Self::Item> {
241         if self.1 == 0 {
242             None
243         } else {
244             self.1 -= 1;
245             Some(self.0)
246         }
247     }
248 
249     fn size_hint(&self) -> (usize, Option<usize>) {
250         let len = self.len();
251         (len, Some(len))
252     }
253 }
254 
255 impl ExactSizeIterator for RepeatN<'_> {
256     fn len(&self) -> usize {
257         usize::try_from(self.1).unwrap()
258     }
259 }
260 
261 impl ArrayRef {
262     /// Allocate a new `array` of the given length, with every element
263     /// initialized to `elem`.
264     ///
265     /// For example, `ArrayRef::new(ctx, pre, &Val::I64(9), 3)` allocates the
266     /// array `[9, 9, 9]`.
267     ///
268     /// This is similar to the `array.new` instruction.
269     ///
270     /// # Automatic Garbage Collection
271     ///
272     /// If the GC heap is at capacity, and there isn't room for allocating this
273     /// new array, then this method will automatically trigger a synchronous
274     /// collection in an attempt to free up space in the GC heap.
275     ///
276     /// # Errors
277     ///
278     /// If the given `elem` value's type does not match the `allocator`'s array
279     /// type's element type, an error is returned.
280     ///
281     /// If the allocation cannot be satisfied because the GC heap is currently
282     /// out of memory, then a [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory]
283     /// error is returned. The allocation might succeed on a second attempt if
284     /// you drop some rooted GC references and try again.
285     ///
286     /// # Panics
287     ///
288     /// Panics if the `store` is configured for async; use
289     /// [`ArrayRef::new_async`][crate::ArrayRef::new_async] to perform
290     /// asynchronous allocation instead.
291     ///
292     /// Panics if either the allocator or the `elem` value is not associated
293     /// with the given store.
294     pub fn new(
295         mut store: impl AsContextMut,
296         allocator: &ArrayRefPre,
297         elem: &Val,
298         len: u32,
299     ) -> Result<Rooted<ArrayRef>> {
300         Self::_new(store.as_context_mut().0, allocator, elem, len)
301     }
302 
303     pub(crate) fn _new(
304         store: &mut StoreOpaque,
305         allocator: &ArrayRefPre,
306         elem: &Val,
307         len: u32,
308     ) -> Result<Rooted<ArrayRef>> {
309         store.retry_after_gc((), |store, ()| {
310             Self::new_from_iter(store, allocator, RepeatN(elem, len))
311         })
312     }
313 
314     /// Asynchronously allocate a new `array` of the given length, with every
315     /// element initialized to `elem`.
316     ///
317     /// For example, `ArrayRef::new(ctx, pre, &Val::I64(9), 3)` allocates the
318     /// array `[9, 9, 9]`.
319     ///
320     /// This is similar to the `array.new` instruction.
321     ///
322     /// # Automatic Garbage Collection
323     ///
324     /// If the GC heap is at capacity, and there isn't room for allocating this
325     /// new array, then this method will automatically trigger a asynchronous
326     /// collection in an attempt to free up space in the GC heap.
327     ///
328     /// # Errors
329     ///
330     /// If the given `elem` value's type does not match the `allocator`'s array
331     /// type's element type, an error is returned.
332     ///
333     /// If the allocation cannot be satisfied because the GC heap is currently
334     /// out of memory, then a [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory]
335     /// error is returned. The allocation might succeed on a second attempt if
336     /// you drop some rooted GC references and try again.
337     ///
338     /// # Panics
339     ///
340     /// Panics if your engine is not configured for async; use
341     /// [`ArrayRef::new_async`][crate::ArrayRef::new_async] to perform
342     /// synchronous allocation instead.
343     ///
344     /// Panics if either the allocator or the `elem` value is not associated
345     /// with the given store.
346     #[cfg(feature = "async")]
347     pub async fn new_async(
348         mut store: impl AsContextMut,
349         allocator: &ArrayRefPre,
350         elem: &Val,
351         len: u32,
352     ) -> Result<Rooted<ArrayRef>> {
353         Self::_new_async(store.as_context_mut().0, allocator, elem, len).await
354     }
355 
356     #[cfg(feature = "async")]
357     pub(crate) async fn _new_async(
358         store: &mut StoreOpaque,
359         allocator: &ArrayRefPre,
360         elem: &Val,
361         len: u32,
362     ) -> Result<Rooted<ArrayRef>> {
363         store
364             .retry_after_gc_async((), |store, ()| {
365                 Self::new_from_iter(store, allocator, RepeatN(elem, len))
366             })
367             .await
368     }
369 
370     /// Like `ArrayRef::new` but when async is configured must only ever be
371     /// called from on a fiber stack.
372     pub(crate) unsafe fn new_maybe_async(
373         store: &mut StoreOpaque,
374         allocator: &ArrayRefPre,
375         elem: &Val,
376         len: u32,
377     ) -> Result<Rooted<ArrayRef>> {
378         // Type check the initial element value against the element type.
379         elem.ensure_matches_ty(store, allocator.ty.element_type().unpack())
380             .context("element type mismatch")?;
381 
382         unsafe {
383             store.retry_after_gc_maybe_async((), |store, ()| {
384                 Self::new_from_iter(store, allocator, RepeatN(elem, len))
385             })
386         }
387     }
388 
389     /// Allocate a new array of the given elements.
390     ///
391     /// Does not attempt a GC on OOM; leaves that to callers.
392     fn new_from_iter<'a>(
393         store: &mut StoreOpaque,
394         allocator: &ArrayRefPre,
395         elems: impl Clone + ExactSizeIterator<Item = &'a Val>,
396     ) -> Result<Rooted<ArrayRef>> {
397         assert_eq!(
398             store.id(),
399             allocator.store_id,
400             "attempted to use a `ArrayRefPre` with the wrong store"
401         );
402 
403         // Type check the elements against the element type.
404         for elem in elems.clone() {
405             elem.ensure_matches_ty(store, allocator.ty.element_type().unpack())
406                 .context("element type mismatch")?;
407         }
408 
409         let len = u32::try_from(elems.len()).unwrap();
410 
411         // Allocate the array and write each field value into the appropriate
412         // offset.
413         let arrayref = store
414             .gc_store_mut()?
415             .alloc_uninit_array(allocator.type_index(), len, allocator.layout())
416             .context("unrecoverable error when allocating new `arrayref`")?
417             .map_err(|n| GcHeapOutOfMemory::new((), n))?;
418 
419         // From this point on, if we get any errors, then the array is not
420         // fully initialized, so we need to eagerly deallocate it before the
421         // next GC where the collector might try to interpret one of the
422         // uninitialized fields as a GC reference.
423         let mut store = AutoAssertNoGc::new(store);
424         match (|| {
425             let elem_ty = allocator.ty.element_type();
426             for (i, elem) in elems.enumerate() {
427                 let i = u32::try_from(i).unwrap();
428                 debug_assert!(i < len);
429                 arrayref.initialize_elem(&mut store, allocator.layout(), &elem_ty, i, *elem)?;
430             }
431             Ok(())
432         })() {
433             Ok(()) => Ok(Rooted::new(&mut store, arrayref.into())),
434             Err(e) => {
435                 store.gc_store_mut()?.dealloc_uninit_array(arrayref);
436                 Err(e)
437             }
438         }
439     }
440 
441     /// Synchronously allocate a new `array` containing the given elements.
442     ///
443     /// For example, `ArrayRef::new_fixed(ctx, pre, &[Val::I64(4), Val::I64(5),
444     /// Val::I64(6)])` allocates the array `[4, 5, 6]`.
445     ///
446     /// This is similar to the `array.new_fixed` instruction.
447     ///
448     /// # Automatic Garbage Collection
449     ///
450     /// If the GC heap is at capacity, and there isn't room for allocating this
451     /// new array, then this method will automatically trigger a synchronous
452     /// collection in an attempt to free up space in the GC heap.
453     ///
454     /// # Errors
455     ///
456     /// If any of the `elems` values' type does not match the `allocator`'s
457     /// array type's element type, an error is returned.
458     ///
459     /// If the allocation cannot be satisfied because the GC heap is currently
460     /// out of memory, then a [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory]
461     /// error is returned. The allocation might succeed on a second attempt if
462     /// you drop some rooted GC references and try again.
463     ///
464     /// # Panics
465     ///
466     /// Panics if the `store` is configured for async; use
467     /// [`ArrayRef::new_fixed_async`][crate::ArrayRef::new_fixed_async] to
468     /// perform asynchronous allocation instead.
469     ///
470     /// Panics if the allocator or any of the `elems` values are not associated
471     /// with the given store.
472     pub fn new_fixed(
473         mut store: impl AsContextMut,
474         allocator: &ArrayRefPre,
475         elems: &[Val],
476     ) -> Result<Rooted<ArrayRef>> {
477         Self::_new_fixed(store.as_context_mut().0, allocator, elems)
478     }
479 
480     pub(crate) fn _new_fixed(
481         store: &mut StoreOpaque,
482         allocator: &ArrayRefPre,
483         elems: &[Val],
484     ) -> Result<Rooted<ArrayRef>> {
485         store.retry_after_gc((), |store, ()| {
486             Self::new_from_iter(store, allocator, elems.iter())
487         })
488     }
489 
490     /// Asynchronously allocate a new `array` containing the given elements.
491     ///
492     /// For example, `ArrayRef::new_fixed_async(ctx, pre, &[Val::I64(4),
493     /// Val::I64(5), Val::I64(6)])` allocates the array `[4, 5, 6]`.
494     ///
495     /// This is similar to the `array.new_fixed` instruction.
496     ///
497     /// If your engine is not configured for async, use
498     /// [`ArrayRef::new_fixed`][crate::ArrayRef::new_fixed] to perform
499     /// synchronous allocation.
500     ///
501     /// # Automatic Garbage Collection
502     ///
503     /// If the GC heap is at capacity, and there isn't room for allocating this
504     /// new array, then this method will automatically trigger a synchronous
505     /// collection in an attempt to free up space in the GC heap.
506     ///
507     /// # Errors
508     ///
509     /// If any of the `elems` values' type does not match the `allocator`'s
510     /// array type's element type, an error is returned.
511     ///
512     /// If the allocation cannot be satisfied because the GC heap is currently
513     /// out of memory, then a [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory]
514     /// error is returned. The allocation might succeed on a second attempt if
515     /// you drop some rooted GC references and try again.
516     ///
517     /// # Panics
518     ///
519     /// Panics if the `store` is not configured for async; use
520     /// [`ArrayRef::new_fixed`][crate::ArrayRef::new_fixed] to perform
521     /// synchronous allocation instead.
522     ///
523     /// Panics if the allocator or any of the `elems` values are not associated
524     /// with the given store.
525     #[cfg(feature = "async")]
526     pub async fn new_fixed_async(
527         mut store: impl AsContextMut,
528         allocator: &ArrayRefPre,
529         elems: &[Val],
530     ) -> Result<Rooted<ArrayRef>> {
531         Self::_new_fixed_async(store.as_context_mut().0, allocator, elems).await
532     }
533 
534     #[cfg(feature = "async")]
535     pub(crate) async fn _new_fixed_async(
536         store: &mut StoreOpaque,
537         allocator: &ArrayRefPre,
538         elems: &[Val],
539     ) -> Result<Rooted<ArrayRef>> {
540         store
541             .retry_after_gc_async((), |store, ()| {
542                 Self::new_from_iter(store, allocator, elems.iter())
543             })
544             .await
545     }
546 
547     /// Like `ArrayRef::new_fixed[_async]` but it is the caller's responsibility
548     /// to ensure that when async is enabled, this is only called from on a
549     /// fiber stack.
550     pub(crate) unsafe fn new_fixed_maybe_async(
551         store: &mut StoreOpaque,
552         allocator: &ArrayRefPre,
553         elems: &[Val],
554     ) -> Result<Rooted<ArrayRef>> {
555         unsafe {
556             store.retry_after_gc_maybe_async((), |store, ()| {
557                 Self::new_from_iter(store, allocator, elems.iter())
558             })
559         }
560     }
561 
562     #[inline]
563     pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
564         self.inner.comes_from_same_store(store)
565     }
566 
567     /// Get this `arrayref`'s type.
568     ///
569     /// # Errors
570     ///
571     /// Return an error if this reference has been unrooted.
572     ///
573     /// # Panics
574     ///
575     /// Panics if this reference is associated with a different store.
576     pub fn ty(&self, store: impl AsContext) -> Result<ArrayType> {
577         self._ty(store.as_context().0)
578     }
579 
580     pub(crate) fn _ty(&self, store: &StoreOpaque) -> Result<ArrayType> {
581         assert!(self.comes_from_same_store(store));
582         let index = self.type_index(store)?;
583         Ok(ArrayType::from_shared_type_index(store.engine(), index))
584     }
585 
586     /// Does this `arrayref` match the given type?
587     ///
588     /// That is, is this array's type a subtype of the given type?
589     ///
590     /// # Errors
591     ///
592     /// Return an error if this reference has been unrooted.
593     ///
594     /// # Panics
595     ///
596     /// Panics if this reference is associated with a different store or if the
597     /// type is not associated with the store's engine.
598     pub fn matches_ty(&self, store: impl AsContext, ty: &ArrayType) -> Result<bool> {
599         self._matches_ty(store.as_context().0, ty)
600     }
601 
602     pub(crate) fn _matches_ty(&self, store: &StoreOpaque, ty: &ArrayType) -> Result<bool> {
603         assert!(self.comes_from_same_store(store));
604         Ok(self._ty(store)?.matches(ty))
605     }
606 
607     pub(crate) fn ensure_matches_ty(&self, store: &StoreOpaque, ty: &ArrayType) -> Result<()> {
608         if !self.comes_from_same_store(store) {
609             bail!("function used with wrong store");
610         }
611         if self._matches_ty(store, ty)? {
612             Ok(())
613         } else {
614             let actual_ty = self._ty(store)?;
615             bail!("type mismatch: expected `(ref {ty})`, found `(ref {actual_ty})`")
616         }
617     }
618 
619     /// Get the length of this array.
620     ///
621     /// # Errors
622     ///
623     /// Return an error if this reference has been unrooted.
624     ///
625     /// # Panics
626     ///
627     /// Panics if this reference is associated with a different store.
628     pub fn len(&self, store: impl AsContext) -> Result<u32> {
629         self._len(store.as_context().0)
630     }
631 
632     pub(crate) fn _len(&self, store: &StoreOpaque) -> Result<u32> {
633         assert!(self.comes_from_same_store(store));
634         let gc_ref = self.inner.try_gc_ref(store)?;
635         debug_assert!({
636             let header = store.gc_store()?.header(gc_ref);
637             header.kind().matches(VMGcKind::ArrayRef)
638         });
639         let arrayref = gc_ref.as_arrayref_unchecked();
640         Ok(arrayref.len(store))
641     }
642 
643     /// Get the values of this array's elements.
644     ///
645     /// Note that `i8` and `i16` element values are zero-extended into
646     /// `Val::I32(_)`s.
647     ///
648     /// # Errors
649     ///
650     /// Return an error if this reference has been unrooted.
651     ///
652     /// # Panics
653     ///
654     /// Panics if this reference is associated with a different store.
655     pub fn elems<'a, T: 'static>(
656         &'a self,
657         store: impl Into<StoreContextMut<'a, T>>,
658     ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> {
659         self._elems(store.into().0)
660     }
661 
662     pub(crate) fn _elems<'a>(
663         &'a self,
664         store: &'a mut StoreOpaque,
665     ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> {
666         assert!(self.comes_from_same_store(store));
667         let store = AutoAssertNoGc::new(store);
668 
669         let gc_ref = self.inner.try_gc_ref(&store)?;
670         let header = store.gc_store()?.header(gc_ref);
671         debug_assert!(header.kind().matches(VMGcKind::ArrayRef));
672 
673         let len = self._len(&store)?;
674 
675         return Ok(Elems {
676             arrayref: self,
677             store,
678             index: 0,
679             len,
680         });
681 
682         struct Elems<'a, 'b> {
683             arrayref: &'a ArrayRef,
684             store: AutoAssertNoGc<'b>,
685             index: u32,
686             len: u32,
687         }
688 
689         impl Iterator for Elems<'_, '_> {
690             type Item = Val;
691 
692             #[inline]
693             fn next(&mut self) -> Option<Self::Item> {
694                 let i = self.index;
695                 debug_assert!(i <= self.len);
696                 if i >= self.len {
697                     return None;
698                 }
699                 self.index += 1;
700                 Some(self.arrayref._get(&mut self.store, i).unwrap())
701             }
702 
703             #[inline]
704             fn size_hint(&self) -> (usize, Option<usize>) {
705                 let len = self.len - self.index;
706                 let len = usize::try_from(len).unwrap();
707                 (len, Some(len))
708             }
709         }
710 
711         impl ExactSizeIterator for Elems<'_, '_> {
712             #[inline]
713             fn len(&self) -> usize {
714                 let len = self.len - self.index;
715                 usize::try_from(len).unwrap()
716             }
717         }
718     }
719 
720     fn header<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMGcHeader> {
721         assert!(self.comes_from_same_store(&store));
722         let gc_ref = self.inner.try_gc_ref(store)?;
723         Ok(store.gc_store()?.header(gc_ref))
724     }
725 
726     fn arrayref<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMArrayRef> {
727         assert!(self.comes_from_same_store(&store));
728         let gc_ref = self.inner.try_gc_ref(store)?;
729         debug_assert!(self.header(store)?.kind().matches(VMGcKind::ArrayRef));
730         Ok(gc_ref.as_arrayref_unchecked())
731     }
732 
733     pub(crate) fn layout(&self, store: &AutoAssertNoGc<'_>) -> Result<GcArrayLayout> {
734         assert!(self.comes_from_same_store(&store));
735         let type_index = self.type_index(store)?;
736         let layout = store
737             .engine()
738             .signatures()
739             .layout(type_index)
740             .expect("array types should have GC layouts");
741         match layout {
742             GcLayout::Array(a) => Ok(a),
743             GcLayout::Struct(_) => unreachable!(),
744         }
745     }
746 
747     fn field_ty(&self, store: &StoreOpaque) -> Result<FieldType> {
748         let ty = self._ty(store)?;
749         Ok(ty.field_type())
750     }
751 
752     /// Get this array's `index`th element.
753     ///
754     /// Note that `i8` and `i16` field values are zero-extended into
755     /// `Val::I32(_)`s.
756     ///
757     /// # Errors
758     ///
759     /// Returns an `Err(_)` if the index is out of bounds or this reference has
760     /// been unrooted.
761     ///
762     /// # Panics
763     ///
764     /// Panics if this reference is associated with a different store.
765     pub fn get(&self, mut store: impl AsContextMut, index: u32) -> Result<Val> {
766         let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
767         self._get(&mut store, index)
768     }
769 
770     pub(crate) fn _get(&self, store: &mut AutoAssertNoGc<'_>, index: u32) -> Result<Val> {
771         assert!(
772             self.comes_from_same_store(store),
773             "attempted to use an array with the wrong store",
774         );
775         let arrayref = self.arrayref(store)?.unchecked_copy();
776         let field_ty = self.field_ty(store)?;
777         let layout = self.layout(store)?;
778         let len = arrayref.len(store);
779         ensure!(
780             index < len,
781             "index out of bounds: the length is {len} but the index is {index}"
782         );
783         Ok(arrayref.read_elem(store, &layout, field_ty.element_type(), index))
784     }
785 
786     /// Set this array's `index`th element.
787     ///
788     /// # Errors
789     ///
790     /// Returns an error in the following scenarios:
791     ///
792     /// * When given a value of the wrong type, such as trying to write an `f32`
793     ///   value into an array of `i64` elements.
794     ///
795     /// * When the array elements are not mutable.
796     ///
797     /// * When `index` is not within the range `0..self.len(ctx)`.
798     ///
799     /// * When `value` is a GC reference that has since been unrooted.
800     ///
801     /// # Panics
802     ///
803     /// Panics if either this reference or the given `value` is associated with
804     /// a different store.
805     pub fn set(&self, mut store: impl AsContextMut, index: u32, value: Val) -> Result<()> {
806         self._set(store.as_context_mut().0, index, value)
807     }
808 
809     pub(crate) fn _set(&self, store: &mut StoreOpaque, index: u32, value: Val) -> Result<()> {
810         assert!(
811             self.comes_from_same_store(store),
812             "attempted to use an array with the wrong store",
813         );
814         assert!(
815             value.comes_from_same_store(store),
816             "attempted to use a value with the wrong store",
817         );
818 
819         let mut store = AutoAssertNoGc::new(store);
820 
821         let field_ty = self.field_ty(&store)?;
822         ensure!(
823             field_ty.mutability().is_var(),
824             "cannot set element {index}: array elements are not mutable"
825         );
826 
827         value
828             .ensure_matches_ty(&store, &field_ty.element_type().unpack())
829             .with_context(|| format!("cannot set element {index}: type mismatch"))?;
830 
831         let layout = self.layout(&store)?;
832         let arrayref = self.arrayref(&store)?.unchecked_copy();
833 
834         let len = arrayref.len(&store);
835         ensure!(
836             index < len,
837             "index out of bounds: the length is {len} but the index is {index}"
838         );
839 
840         arrayref.write_elem(&mut store, &layout, field_ty.element_type(), index, value)
841     }
842 
843     pub(crate) fn type_index(&self, store: &StoreOpaque) -> Result<VMSharedTypeIndex> {
844         let gc_ref = self.inner.try_gc_ref(store)?;
845         let header = store.gc_store()?.header(gc_ref);
846         debug_assert!(header.kind().matches(VMGcKind::ArrayRef));
847         Ok(header.ty().expect("arrayrefs should have concrete types"))
848     }
849 
850     /// Create a new `Rooted<ArrayRef>` from the given GC reference.
851     ///
852     /// `gc_ref` should point to a valid `arrayref` and should belong to the
853     /// store's GC heap. Failure to uphold these invariants is memory safe but
854     /// will lead to general incorrectness such as panics or wrong results.
855     pub(crate) fn from_cloned_gc_ref(
856         store: &mut AutoAssertNoGc<'_>,
857         gc_ref: VMGcRef,
858     ) -> Rooted<Self> {
859         debug_assert!(gc_ref.is_arrayref(&*store.unwrap_gc_store().gc_heap));
860         Rooted::new(store, gc_ref)
861     }
862 }
863 
864 unsafe impl WasmTy for Rooted<ArrayRef> {
865     #[inline]
866     fn valtype() -> ValType {
867         ValType::Ref(RefType::new(false, HeapType::Array))
868     }
869 
870     #[inline]
871     fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
872         self.comes_from_same_store(store)
873     }
874 
875     #[inline]
876     fn dynamic_concrete_type_check(
877         &self,
878         store: &StoreOpaque,
879         _nullable: bool,
880         ty: &HeapType,
881     ) -> Result<()> {
882         match ty {
883             HeapType::Any | HeapType::Eq | HeapType::Array => Ok(()),
884             HeapType::ConcreteArray(ty) => self.ensure_matches_ty(store, ty),
885 
886             HeapType::Extern
887             | HeapType::NoExtern
888             | HeapType::Func
889             | HeapType::ConcreteFunc(_)
890             | HeapType::NoFunc
891             | HeapType::I31
892             | HeapType::Struct
893             | HeapType::ConcreteStruct(_)
894             | HeapType::Cont
895             | HeapType::NoCont
896             | HeapType::ConcreteCont(_)
897             | HeapType::None => bail!(
898                 "type mismatch: expected `(ref {ty})`, got `(ref {})`",
899                 self._ty(store)?,
900             ),
901         }
902     }
903 
904     fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
905         self.wasm_ty_store(store, ptr, ValRaw::anyref)
906     }
907 
908     unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
909         Self::wasm_ty_load(store, ptr.get_anyref(), ArrayRef::from_cloned_gc_ref)
910     }
911 }
912 
913 unsafe impl WasmTy for Option<Rooted<ArrayRef>> {
914     #[inline]
915     fn valtype() -> ValType {
916         ValType::ARRAYREF
917     }
918 
919     #[inline]
920     fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
921         self.map_or(true, |x| x.comes_from_same_store(store))
922     }
923 
924     #[inline]
925     fn dynamic_concrete_type_check(
926         &self,
927         store: &StoreOpaque,
928         nullable: bool,
929         ty: &HeapType,
930     ) -> Result<()> {
931         match self {
932             Some(s) => Rooted::<ArrayRef>::dynamic_concrete_type_check(s, store, nullable, ty),
933             None => {
934                 ensure!(
935                     nullable,
936                     "expected a non-null reference, but found a null reference"
937                 );
938                 Ok(())
939             }
940         }
941     }
942 
943     #[inline]
944     fn is_vmgcref_and_points_to_object(&self) -> bool {
945         self.is_some()
946     }
947 
948     fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
949         <Rooted<ArrayRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
950     }
951 
952     unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
953         <Rooted<ArrayRef>>::wasm_ty_option_load(
954             store,
955             ptr.get_anyref(),
956             ArrayRef::from_cloned_gc_ref,
957         )
958     }
959 }
960 
961 unsafe impl WasmTy for ManuallyRooted<ArrayRef> {
962     #[inline]
963     fn valtype() -> ValType {
964         ValType::Ref(RefType::new(false, HeapType::Array))
965     }
966 
967     #[inline]
968     fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
969         self.comes_from_same_store(store)
970     }
971 
972     #[inline]
973     fn dynamic_concrete_type_check(
974         &self,
975         store: &StoreOpaque,
976         _: bool,
977         ty: &HeapType,
978     ) -> Result<()> {
979         match ty {
980             HeapType::Any | HeapType::Eq | HeapType::Array => Ok(()),
981             HeapType::ConcreteArray(ty) => self.ensure_matches_ty(store, ty),
982 
983             HeapType::Extern
984             | HeapType::NoExtern
985             | HeapType::Func
986             | HeapType::ConcreteFunc(_)
987             | HeapType::NoFunc
988             | HeapType::I31
989             | HeapType::Struct
990             | HeapType::ConcreteStruct(_)
991             | HeapType::Cont
992             | HeapType::NoCont
993             | HeapType::ConcreteCont(_)
994             | HeapType::None => bail!(
995                 "type mismatch: expected `(ref {ty})`, got `(ref {})`",
996                 self._ty(store)?,
997             ),
998         }
999     }
1000 
1001     fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
1002         self.wasm_ty_store(store, ptr, ValRaw::anyref)
1003     }
1004 
1005     unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
1006         Self::wasm_ty_load(store, ptr.get_anyref(), ArrayRef::from_cloned_gc_ref)
1007     }
1008 }
1009 
1010 unsafe impl WasmTy for Option<ManuallyRooted<ArrayRef>> {
1011     #[inline]
1012     fn valtype() -> ValType {
1013         ValType::ARRAYREF
1014     }
1015 
1016     #[inline]
1017     fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
1018         self.as_ref()
1019             .map_or(true, |x| x.comes_from_same_store(store))
1020     }
1021 
1022     #[inline]
1023     fn dynamic_concrete_type_check(
1024         &self,
1025         store: &StoreOpaque,
1026         nullable: bool,
1027         ty: &HeapType,
1028     ) -> Result<()> {
1029         match self {
1030             Some(s) => {
1031                 ManuallyRooted::<ArrayRef>::dynamic_concrete_type_check(s, store, nullable, ty)
1032             }
1033             None => {
1034                 ensure!(
1035                     nullable,
1036                     "expected a non-null reference, but found a null reference"
1037                 );
1038                 Ok(())
1039             }
1040         }
1041     }
1042 
1043     #[inline]
1044     fn is_vmgcref_and_points_to_object(&self) -> bool {
1045         self.is_some()
1046     }
1047 
1048     fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
1049         <ManuallyRooted<ArrayRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
1050     }
1051 
1052     unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
1053         <ManuallyRooted<ArrayRef>>::wasm_ty_option_load(
1054             store,
1055             ptr.get_anyref(),
1056             ArrayRef::from_cloned_gc_ref,
1057         )
1058     }
1059 }
1060