Skip to main content

wasmtime/runtime/gc/enabled/
arrayref.rs

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