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