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