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::{Asyncness, StoreId, StoreResourceLimiter};
5#[cfg(feature = "async")]
6use crate::vm::VMStore;
7use crate::vm::{self, VMArrayRef, VMGcHeader};
8use crate::{AnyRef, FieldType};
9use 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};
15use core::mem::{self, MaybeUninit};
16use 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/// ```
65pub struct ArrayRefPre {
66    store_id: StoreId,
67    ty: ArrayType,
68}
69
70impl 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)]
183pub struct ArrayRef {
184    pub(super) inner: GcRootIndex,
185}
186
187unsafe 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
204impl 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
218impl 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)]
237struct RepeatN<'a>(&'a Val, u32);
238
239impl<'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
257impl ExactSizeIterator for RepeatN<'_> {
258    fn len(&self) -> usize {
259        usize::try_from(self.1).unwrap()
260    }
261}
262
263impl 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        let len = u32::try_from(elems.len())?;
397
398        // Allocate the array.
399        let arrayref = store
400            .require_gc_store_mut()?
401            .alloc_uninit_array(allocator.type_index(), len, allocator.layout())
402            .context("unrecoverable error when allocating new `arrayref`")?
403            .map_err(|n| GcHeapOutOfMemory::new((), n))?;
404
405        // Type check the elements against the element type.
406        for elem in elems.clone() {
407            elem.ensure_matches_ty(store, allocator.ty.element_type().unpack())
408                .context("element type mismatch")?;
409        }
410
411        // From this point on, if we get any errors, then the array is not
412        // fully initialized, so we need to eagerly deallocate it before the
413        // next GC where the collector might try to interpret one of the
414        // uninitialized fields as a GC reference.
415        let mut store = AutoAssertNoGc::new(store);
416        match (|| {
417            let elem_ty = allocator.ty.element_type();
418            for (i, elem) in elems.enumerate() {
419                let i = u32::try_from(i)?;
420                debug_assert!(i < len);
421                arrayref.initialize_elem(&mut store, allocator.layout(), &elem_ty, i, *elem)?;
422            }
423            Ok(())
424        })() {
425            Ok(()) => Ok(Rooted::new(&mut store, arrayref.into())),
426            Err(e) => {
427                store
428                    .require_gc_store_mut()?
429                    .dealloc_uninit_array(arrayref)?;
430                Err(e)
431            }
432        }
433    }
434
435    /// Synchronously allocate a new `array` containing the given elements.
436    ///
437    /// For example, `ArrayRef::new_fixed(ctx, pre, &[Val::I64(4), Val::I64(5),
438    /// Val::I64(6)])` allocates the array `[4, 5, 6]`.
439    ///
440    /// This is similar to the `array.new_fixed` instruction.
441    ///
442    /// # Automatic Garbage Collection
443    ///
444    /// If the GC heap is at capacity, and there isn't room for allocating this
445    /// new array, then this method will automatically trigger a synchronous
446    /// collection in an attempt to free up space in the GC heap.
447    ///
448    /// # Errors
449    ///
450    /// If any of the `elems` values' type does not match the `allocator`'s
451    /// array type's element type, an error is returned.
452    ///
453    /// If the allocation cannot be satisfied because the GC heap is currently
454    /// out of memory, then a [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory]
455    /// error is returned. The allocation might succeed on a second attempt if
456    /// you drop some rooted GC references and try again.
457    ///
458    /// If `store` is configured with a
459    /// [`ResourceLimiterAsync`](crate::ResourceLimiterAsync) then an error
460    /// will be returned because [`ArrayRef::new_fixed_async`] should be used
461    /// instead.
462    ///
463    /// # Panics
464    ///
465    /// Panics if the allocator or any of the `elems` values are not associated
466    /// with the given store.
467    pub fn new_fixed(
468        mut store: impl AsContextMut,
469        allocator: &ArrayRefPre,
470        elems: &[Val],
471    ) -> Result<Rooted<ArrayRef>> {
472        let (mut limiter, store) = store
473            .as_context_mut()
474            .0
475            .validate_sync_resource_limiter_and_store_opaque()?;
476        vm::assert_ready(Self::_new_fixed_async(
477            store,
478            limiter.as_mut(),
479            allocator,
480            elems,
481            Asyncness::No,
482        ))
483    }
484
485    /// Asynchronously allocate a new `array` containing the given elements.
486    ///
487    /// For example, `ArrayRef::new_fixed_async(ctx, pre, &[Val::I64(4),
488    /// Val::I64(5), Val::I64(6)])` allocates the array `[4, 5, 6]`.
489    ///
490    /// This is similar to the `array.new_fixed` instruction.
491    ///
492    /// If your engine is not configured for async, use
493    /// [`ArrayRef::new_fixed`][crate::ArrayRef::new_fixed] to perform
494    /// synchronous allocation.
495    ///
496    /// # Automatic Garbage Collection
497    ///
498    /// If the GC heap is at capacity, and there isn't room for allocating this
499    /// new array, then this method will automatically trigger a synchronous
500    /// collection in an attempt to free up space in the GC heap.
501    ///
502    /// # Errors
503    ///
504    /// If any of the `elems` values' type does not match the `allocator`'s
505    /// array type's element type, an error is returned.
506    ///
507    /// If the allocation cannot be satisfied because the GC heap is currently
508    /// out of memory, then a [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory]
509    /// error is returned. The allocation might succeed on a second attempt if
510    /// you drop some rooted GC references and try again.
511    ///
512    /// # Panics
513    ///
514    /// Panics if the `store` is not configured for async; use
515    /// [`ArrayRef::new_fixed`][crate::ArrayRef::new_fixed] to perform
516    /// synchronous allocation instead.
517    ///
518    /// Panics if the allocator or any of the `elems` values are not associated
519    /// with the given store.
520    #[cfg(feature = "async")]
521    pub async fn new_fixed_async(
522        mut store: impl AsContextMut,
523        allocator: &ArrayRefPre,
524        elems: &[Val],
525    ) -> Result<Rooted<ArrayRef>> {
526        let (mut limiter, store) = store.as_context_mut().0.resource_limiter_and_store_opaque();
527        Self::_new_fixed_async(store, limiter.as_mut(), allocator, elems, Asyncness::Yes).await
528    }
529
530    pub(crate) async fn _new_fixed_async(
531        store: &mut StoreOpaque,
532        limiter: Option<&mut StoreResourceLimiter<'_>>,
533        allocator: &ArrayRefPre,
534        elems: &[Val],
535        asyncness: Asyncness,
536    ) -> Result<Rooted<ArrayRef>> {
537        store
538            .retry_after_gc_async(limiter, (), asyncness, |store, ()| {
539                Self::new_from_iter(store, allocator, elems.iter())
540            })
541            .await
542    }
543
544    #[inline]
545    pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
546        self.inner.comes_from_same_store(store)
547    }
548
549    /// Get this `arrayref`'s type.
550    ///
551    /// # Errors
552    ///
553    /// Return an error if this reference has been unrooted.
554    ///
555    /// # Panics
556    ///
557    /// Panics if this reference is associated with a different store.
558    pub fn ty(&self, store: impl AsContext) -> Result<ArrayType> {
559        self._ty(store.as_context().0)
560    }
561
562    pub(crate) fn _ty(&self, store: &StoreOpaque) -> Result<ArrayType> {
563        assert!(self.comes_from_same_store(store));
564        let index = self.type_index(store)?;
565        Ok(ArrayType::from_shared_type_index(store.engine(), index))
566    }
567
568    /// Does this `arrayref` match the given type?
569    ///
570    /// That is, is this array's type a subtype of the given type?
571    ///
572    /// # Errors
573    ///
574    /// Return an error if this reference has been unrooted.
575    ///
576    /// # Panics
577    ///
578    /// Panics if this reference is associated with a different store or if the
579    /// type is not associated with the store's engine.
580    pub fn matches_ty(&self, store: impl AsContext, ty: &ArrayType) -> Result<bool> {
581        self._matches_ty(store.as_context().0, ty)
582    }
583
584    pub(crate) fn _matches_ty(&self, store: &StoreOpaque, ty: &ArrayType) -> Result<bool> {
585        assert!(self.comes_from_same_store(store));
586        Ok(self._ty(store)?.matches(ty))
587    }
588
589    pub(crate) fn ensure_matches_ty(&self, store: &StoreOpaque, ty: &ArrayType) -> Result<()> {
590        if !self.comes_from_same_store(store) {
591            bail!("function used with wrong store");
592        }
593        if self._matches_ty(store, ty)? {
594            Ok(())
595        } else {
596            let actual_ty = self._ty(store)?;
597            bail!("type mismatch: expected `(ref {ty})`, found `(ref {actual_ty})`")
598        }
599    }
600
601    /// Get the length of this array.
602    ///
603    /// # Errors
604    ///
605    /// Return an error if this reference has been unrooted.
606    ///
607    /// # Panics
608    ///
609    /// Panics if this reference is associated with a different store.
610    pub fn len(&self, store: impl AsContext) -> Result<u32> {
611        self._len(store.as_context().0)
612    }
613
614    pub(crate) fn _len(&self, store: &StoreOpaque) -> Result<u32> {
615        assert!(self.comes_from_same_store(store));
616        let gc_ref = self.inner.try_gc_ref(store)?;
617        debug_assert!({
618            let header = store.require_gc_store()?.header(gc_ref)?;
619            header.kind().matches(VMGcKind::ArrayRef)
620        });
621        let arrayref = gc_ref.as_arrayref_unchecked();
622        arrayref.len(store)
623    }
624
625    /// Get the values of this array's elements.
626    ///
627    /// Note that `i8` and `i16` element values are zero-extended into
628    /// `Val::I32(_)`s.
629    ///
630    /// # Errors
631    ///
632    /// Return an error if this reference has been unrooted.
633    ///
634    /// # Panics
635    ///
636    /// Panics if this reference is associated with a different store.
637    pub fn elems<'a, T: 'static>(
638        &'a self,
639        store: impl Into<StoreContextMut<'a, T>>,
640    ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> {
641        self._elems(store.into().0)
642    }
643
644    pub(crate) fn _elems<'a>(
645        &'a self,
646        store: &'a mut StoreOpaque,
647    ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> {
648        assert!(self.comes_from_same_store(store));
649        let store = AutoAssertNoGc::new(store);
650
651        let gc_ref = self.inner.try_gc_ref(&store)?;
652        let header = store.require_gc_store()?.header(gc_ref)?;
653        debug_assert!(header.kind().matches(VMGcKind::ArrayRef));
654
655        let len = self._len(&store)?;
656
657        return Ok(Elems {
658            arrayref: self,
659            store,
660            index: 0,
661            len,
662        });
663
664        struct Elems<'a, 'b> {
665            arrayref: &'a ArrayRef,
666            store: AutoAssertNoGc<'b>,
667            index: u32,
668            len: u32,
669        }
670
671        impl Iterator for Elems<'_, '_> {
672            type Item = Val;
673
674            #[inline]
675            fn next(&mut self) -> Option<Self::Item> {
676                let i = self.index;
677                debug_assert!(i <= self.len);
678                if i >= self.len {
679                    return None;
680                }
681                self.index += 1;
682                self.arrayref._get(&mut self.store, i).ok()
683            }
684
685            #[inline]
686            fn size_hint(&self) -> (usize, Option<usize>) {
687                let len = self.len - self.index;
688                let len = usize::try_from(len).unwrap();
689                (len, Some(len))
690            }
691        }
692
693        impl ExactSizeIterator for Elems<'_, '_> {
694            #[inline]
695            fn len(&self) -> usize {
696                let len = self.len - self.index;
697                usize::try_from(len).unwrap()
698            }
699        }
700    }
701
702    fn header<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMGcHeader> {
703        assert!(self.comes_from_same_store(&store));
704        let gc_ref = self.inner.try_gc_ref(store)?;
705        Ok(store.require_gc_store()?.header(gc_ref)?)
706    }
707
708    fn arrayref<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMArrayRef> {
709        assert!(self.comes_from_same_store(&store));
710        let gc_ref = self.inner.try_gc_ref(store)?;
711        debug_assert!(self.header(store)?.kind().matches(VMGcKind::ArrayRef));
712        Ok(gc_ref.as_arrayref_unchecked())
713    }
714
715    pub(crate) fn layout(&self, store: &AutoAssertNoGc<'_>) -> Result<GcArrayLayout> {
716        assert!(self.comes_from_same_store(&store));
717        let type_index = self.type_index(store)?;
718        let layout = store
719            .engine()
720            .signatures()
721            .layout(type_index)
722            .expect("array types should have GC layouts");
723        match layout {
724            GcLayout::Array(a) => Ok(a),
725            GcLayout::Struct(_) => unreachable!(),
726        }
727    }
728
729    fn field_ty(&self, store: &StoreOpaque) -> Result<FieldType> {
730        let ty = self._ty(store)?;
731        Ok(ty.field_type())
732    }
733
734    /// Get this array's `index`th element.
735    ///
736    /// Note that `i8` and `i16` field values are zero-extended into
737    /// `Val::I32(_)`s.
738    ///
739    /// # Errors
740    ///
741    /// Returns an `Err(_)` if the index is out of bounds or this reference has
742    /// been unrooted.
743    ///
744    /// # Panics
745    ///
746    /// Panics if this reference is associated with a different store.
747    pub fn get(&self, mut store: impl AsContextMut, index: u32) -> Result<Val> {
748        let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
749        self._get(&mut store, index)
750    }
751
752    pub(crate) fn _get(&self, store: &mut AutoAssertNoGc<'_>, index: u32) -> Result<Val> {
753        assert!(
754            self.comes_from_same_store(store),
755            "attempted to use an array with the wrong store",
756        );
757        let arrayref = self.arrayref(store)?.unchecked_copy();
758        let field_ty = self.field_ty(store)?;
759        let layout = self.layout(store)?;
760        let len = arrayref.len(store)?;
761        ensure!(
762            index < len,
763            "index out of bounds: the length is {len} but the index is {index}"
764        );
765        arrayref.read_elem(store, &layout, field_ty.element_type(), index)
766    }
767
768    /// Set this array's `index`th element.
769    ///
770    /// # Errors
771    ///
772    /// Returns an error in the following scenarios:
773    ///
774    /// * When given a value of the wrong type, such as trying to write an `f32`
775    ///   value into an array of `i64` elements.
776    ///
777    /// * When the array elements are not mutable.
778    ///
779    /// * When `index` is not within the range `0..self.len(ctx)`.
780    ///
781    /// * When `value` is a GC reference that has since been unrooted.
782    ///
783    /// # Panics
784    ///
785    /// Panics if either this reference or the given `value` is associated with
786    /// a different store.
787    pub fn set(&self, mut store: impl AsContextMut, index: u32, value: Val) -> Result<()> {
788        self._set(store.as_context_mut().0, index, value)
789    }
790
791    pub(crate) fn _set(&self, store: &mut StoreOpaque, index: u32, value: Val) -> Result<()> {
792        assert!(
793            self.comes_from_same_store(store),
794            "attempted to use an array with the wrong store",
795        );
796        assert!(
797            value.comes_from_same_store(store),
798            "attempted to use a value with the wrong store",
799        );
800
801        let mut store = AutoAssertNoGc::new(store);
802
803        let field_ty = self.field_ty(&store)?;
804        ensure!(
805            field_ty.mutability().is_var(),
806            "cannot set element {index}: array elements are not mutable"
807        );
808
809        value
810            .ensure_matches_ty(&store, &field_ty.element_type().unpack())
811            .with_context(|| format!("cannot set element {index}: type mismatch"))?;
812
813        let layout = self.layout(&store)?;
814        let arrayref = self.arrayref(&store)?.unchecked_copy();
815
816        let len = arrayref.len(&store)?;
817        ensure!(
818            index < len,
819            "index out of bounds: the length is {len} but the index is {index}"
820        );
821
822        arrayref.write_elem(&mut store, &layout, field_ty.element_type(), index, value)
823    }
824
825    pub(crate) fn type_index(&self, store: &StoreOpaque) -> Result<VMSharedTypeIndex> {
826        let gc_ref = self.inner.try_gc_ref(store)?;
827        let header = store.require_gc_store()?.header(gc_ref)?;
828        debug_assert!(header.kind().matches(VMGcKind::ArrayRef));
829        Ok(header.ty().expect("arrayrefs should have concrete types"))
830    }
831
832    /// Create a new `Rooted<ArrayRef>` from the given GC reference.
833    ///
834    /// `gc_ref` should point to a valid `arrayref` and should belong to the
835    /// store's GC heap. Failure to uphold these invariants is memory safe but
836    /// will lead to general incorrectness such as panics or wrong results.
837    pub(crate) fn from_cloned_gc_ref(
838        store: &mut AutoAssertNoGc<'_>,
839        gc_ref: VMGcRef,
840    ) -> Rooted<Self> {
841        debug_assert!(gc_ref.is_arrayref(&*store.unwrap_gc_store().gc_heap));
842        Rooted::new(store, gc_ref)
843    }
844}
845
846unsafe impl WasmTy for Rooted<ArrayRef> {
847    #[inline]
848    fn valtype() -> ValType {
849        ValType::Ref(RefType::new(false, HeapType::Array))
850    }
851
852    #[inline]
853    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
854        self.comes_from_same_store(store)
855    }
856
857    #[inline]
858    fn dynamic_concrete_type_check(
859        &self,
860        store: &StoreOpaque,
861        _nullable: bool,
862        ty: &HeapType,
863    ) -> Result<()> {
864        match ty {
865            HeapType::Any | HeapType::Eq | HeapType::Array => Ok(()),
866            HeapType::ConcreteArray(ty) => self.ensure_matches_ty(store, ty),
867
868            HeapType::Extern
869            | HeapType::NoExtern
870            | HeapType::Func
871            | HeapType::ConcreteFunc(_)
872            | HeapType::NoFunc
873            | HeapType::I31
874            | HeapType::Struct
875            | HeapType::ConcreteStruct(_)
876            | HeapType::Cont
877            | HeapType::NoCont
878            | HeapType::ConcreteCont(_)
879            | HeapType::Exn
880            | HeapType::NoExn
881            | HeapType::ConcreteExn(_)
882            | HeapType::None => bail!(
883                "type mismatch: expected `(ref {ty})`, got `(ref {})`",
884                self._ty(store)?,
885            ),
886        }
887    }
888
889    fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
890        self.wasm_ty_store(store, ptr, ValRaw::anyref)
891    }
892
893    unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
894        Self::wasm_ty_load(store, ptr.get_anyref(), ArrayRef::from_cloned_gc_ref)
895    }
896}
897
898unsafe impl WasmTy for Option<Rooted<ArrayRef>> {
899    #[inline]
900    fn valtype() -> ValType {
901        ValType::ARRAYREF
902    }
903
904    #[inline]
905    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
906        self.map_or(true, |x| x.comes_from_same_store(store))
907    }
908
909    #[inline]
910    fn dynamic_concrete_type_check(
911        &self,
912        store: &StoreOpaque,
913        nullable: bool,
914        ty: &HeapType,
915    ) -> Result<()> {
916        match self {
917            Some(s) => Rooted::<ArrayRef>::dynamic_concrete_type_check(s, store, nullable, ty),
918            None => {
919                ensure!(
920                    nullable,
921                    "expected a non-null reference, but found a null reference"
922                );
923                Ok(())
924            }
925        }
926    }
927
928    #[inline]
929    fn is_vmgcref_and_points_to_object(&self) -> bool {
930        self.is_some()
931    }
932
933    fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
934        <Rooted<ArrayRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
935    }
936
937    unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
938        <Rooted<ArrayRef>>::wasm_ty_option_load(
939            store,
940            ptr.get_anyref(),
941            ArrayRef::from_cloned_gc_ref,
942        )
943    }
944}
945
946unsafe impl WasmTy for OwnedRooted<ArrayRef> {
947    #[inline]
948    fn valtype() -> ValType {
949        ValType::Ref(RefType::new(false, HeapType::Array))
950    }
951
952    #[inline]
953    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
954        self.comes_from_same_store(store)
955    }
956
957    #[inline]
958    fn dynamic_concrete_type_check(
959        &self,
960        store: &StoreOpaque,
961        _: bool,
962        ty: &HeapType,
963    ) -> Result<()> {
964        match ty {
965            HeapType::Any | HeapType::Eq | HeapType::Array => Ok(()),
966            HeapType::ConcreteArray(ty) => self.ensure_matches_ty(store, ty),
967
968            HeapType::Extern
969            | HeapType::NoExtern
970            | HeapType::Func
971            | HeapType::ConcreteFunc(_)
972            | HeapType::NoFunc
973            | HeapType::I31
974            | HeapType::Struct
975            | HeapType::ConcreteStruct(_)
976            | HeapType::Cont
977            | HeapType::NoCont
978            | HeapType::ConcreteCont(_)
979            | HeapType::Exn
980            | HeapType::NoExn
981            | HeapType::ConcreteExn(_)
982            | HeapType::None => bail!(
983                "type mismatch: expected `(ref {ty})`, got `(ref {})`",
984                self._ty(store)?,
985            ),
986        }
987    }
988
989    fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
990        self.wasm_ty_store(store, ptr, ValRaw::anyref)
991    }
992
993    unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
994        Self::wasm_ty_load(store, ptr.get_anyref(), ArrayRef::from_cloned_gc_ref)
995    }
996}
997
998unsafe impl WasmTy for Option<OwnedRooted<ArrayRef>> {
999    #[inline]
1000    fn valtype() -> ValType {
1001        ValType::ARRAYREF
1002    }
1003
1004    #[inline]
1005    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
1006        self.as_ref()
1007            .map_or(true, |x| x.comes_from_same_store(store))
1008    }
1009
1010    #[inline]
1011    fn dynamic_concrete_type_check(
1012        &self,
1013        store: &StoreOpaque,
1014        nullable: bool,
1015        ty: &HeapType,
1016    ) -> Result<()> {
1017        match self {
1018            Some(s) => OwnedRooted::<ArrayRef>::dynamic_concrete_type_check(s, store, nullable, ty),
1019            None => {
1020                ensure!(
1021                    nullable,
1022                    "expected a non-null reference, but found a null reference"
1023                );
1024                Ok(())
1025            }
1026        }
1027    }
1028
1029    #[inline]
1030    fn is_vmgcref_and_points_to_object(&self) -> bool {
1031        self.is_some()
1032    }
1033
1034    fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
1035        <OwnedRooted<ArrayRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
1036    }
1037
1038    unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
1039        <OwnedRooted<ArrayRef>>::wasm_ty_option_load(
1040            store,
1041            ptr.get_anyref(),
1042            ArrayRef::from_cloned_gc_ref,
1043        )
1044    }
1045}