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, bail_bug,
12    prelude::*,
13    store::{AutoAssertNoGc, StoreContextMut, StoreOpaque},
14};
15use core::mem::{self, MaybeUninit};
16use wasmtime_environ::{GcArrayLayout, 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        // From this point on, if we get any errors, then the array is not
411        // fully initialized, so we need to eagerly deallocate it before the
412        // next GC where the collector might try to interpret one of the
413        // uninitialized fields as a GC reference.
414        let mut store = AutoAssertNoGc::new(store);
415        match (|| {
416            // Type check the elements here, rather than before allocating, so
417            // that oversized arrays fail without visiting every element.
418            for elem in elems.clone() {
419                elem.ensure_matches_ty(&store, allocator.ty.element_type().unpack())
420                    .context("element type mismatch")?;
421            }
422
423            let elem_ty = allocator.ty.element_type();
424            for (i, elem) in elems.enumerate() {
425                let i = u32::try_from(i)?;
426                debug_assert!(i < len);
427                arrayref.initialize_elem(&mut store, allocator.layout(), &elem_ty, i, *elem)?;
428            }
429            Ok(())
430        })() {
431            Ok(()) => Ok(Rooted::new(&mut store, arrayref.into())),
432            Err(e) => {
433                store
434                    .require_gc_store_mut()?
435                    .dealloc_uninit_array(arrayref)?;
436                Err(e)
437            }
438        }
439    }
440
441    /// Synchronously allocate a new `array` containing the given elements.
442    ///
443    /// For example, `ArrayRef::new_fixed(ctx, pre, &[Val::I64(4), Val::I64(5),
444    /// Val::I64(6)])` allocates the array `[4, 5, 6]`.
445    ///
446    /// This is similar to the `array.new_fixed` instruction.
447    ///
448    /// # Automatic Garbage Collection
449    ///
450    /// If the GC heap is at capacity, and there isn't room for allocating this
451    /// new array, then this method will automatically trigger a synchronous
452    /// collection in an attempt to free up space in the GC heap.
453    ///
454    /// # Errors
455    ///
456    /// If any of the `elems` values' type does not match the `allocator`'s
457    /// array type's element type, an error is returned.
458    ///
459    /// If the allocation cannot be satisfied because the GC heap is currently
460    /// out of memory, then a [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory]
461    /// error is returned. The allocation might succeed on a second attempt if
462    /// you drop some rooted GC references and try again.
463    ///
464    /// If `store` is configured with a
465    /// [`ResourceLimiterAsync`](crate::ResourceLimiterAsync) then an error
466    /// will be returned because [`ArrayRef::new_fixed_async`] should be used
467    /// instead.
468    ///
469    /// # Panics
470    ///
471    /// Panics if the allocator or any of the `elems` values are not associated
472    /// with the given store.
473    pub fn new_fixed(
474        mut store: impl AsContextMut,
475        allocator: &ArrayRefPre,
476        elems: &[Val],
477    ) -> Result<Rooted<ArrayRef>> {
478        let (mut limiter, store) = store
479            .as_context_mut()
480            .0
481            .validate_sync_resource_limiter_and_store_opaque()?;
482        vm::assert_ready(Self::_new_fixed_async(
483            store,
484            limiter.as_mut(),
485            allocator,
486            elems,
487            Asyncness::No,
488        ))
489    }
490
491    /// Asynchronously allocate a new `array` containing the given elements.
492    ///
493    /// For example, `ArrayRef::new_fixed_async(ctx, pre, &[Val::I64(4),
494    /// Val::I64(5), Val::I64(6)])` allocates the array `[4, 5, 6]`.
495    ///
496    /// This is similar to the `array.new_fixed` instruction.
497    ///
498    /// If your engine is not configured for async, use
499    /// [`ArrayRef::new_fixed`][crate::ArrayRef::new_fixed] to perform
500    /// synchronous allocation.
501    ///
502    /// # Automatic Garbage Collection
503    ///
504    /// If the GC heap is at capacity, and there isn't room for allocating this
505    /// new array, then this method will automatically trigger a synchronous
506    /// collection in an attempt to free up space in the GC heap.
507    ///
508    /// # Errors
509    ///
510    /// If any of the `elems` values' type does not match the `allocator`'s
511    /// array type's element type, an error is returned.
512    ///
513    /// If the allocation cannot be satisfied because the GC heap is currently
514    /// out of memory, then a [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory]
515    /// error is returned. The allocation might succeed on a second attempt if
516    /// you drop some rooted GC references and try again.
517    ///
518    /// # Panics
519    ///
520    /// Panics if the `store` is not configured for async; use
521    /// [`ArrayRef::new_fixed`][crate::ArrayRef::new_fixed] to perform
522    /// synchronous allocation instead.
523    ///
524    /// Panics if the allocator or any of the `elems` values are not associated
525    /// with the given store.
526    #[cfg(feature = "async")]
527    pub async fn new_fixed_async(
528        mut store: impl AsContextMut,
529        allocator: &ArrayRefPre,
530        elems: &[Val],
531    ) -> Result<Rooted<ArrayRef>> {
532        let (mut limiter, store) = store.as_context_mut().0.resource_limiter_and_store_opaque();
533        Self::_new_fixed_async(store, limiter.as_mut(), allocator, elems, Asyncness::Yes).await
534    }
535
536    pub(crate) async fn _new_fixed_async(
537        store: &mut StoreOpaque,
538        limiter: Option<&mut StoreResourceLimiter<'_>>,
539        allocator: &ArrayRefPre,
540        elems: &[Val],
541        asyncness: Asyncness,
542    ) -> Result<Rooted<ArrayRef>> {
543        store
544            .retry_after_gc_async(limiter, (), asyncness, |store, ()| {
545                Self::new_from_iter(store, allocator, elems.iter())
546            })
547            .await
548    }
549
550    /// Synchronously allocate a new `i8` array initialized from the given bytes.
551    ///
552    /// Unlike [`ArrayRef::new_fixed`], which initializes the array one [`Val`]
553    /// at a time, the element body is filled with a single `memcpy`. The bytes
554    /// are passed as `u8`; their signedness is only observed at read time (e.g.
555    /// `array.get_s` vs `array.get_u`).
556    ///
557    /// # Automatic Garbage Collection
558    ///
559    /// If the GC heap is at capacity, and there isn't room for allocating this
560    /// new array, then this method will automatically trigger a synchronous
561    /// collection in an attempt to free up space in the GC heap.
562    ///
563    /// # Errors
564    ///
565    /// If the `allocator`'s array type does not have `i8` elements, an error is
566    /// returned.
567    ///
568    /// If the allocation cannot be satisfied because the GC heap is currently
569    /// out of memory, then a [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory]
570    /// error is returned. The allocation might succeed on a second attempt if
571    /// you drop some rooted GC references and try again.
572    ///
573    /// If `store` is configured with a
574    /// [`ResourceLimiterAsync`](crate::ResourceLimiterAsync) then an error will
575    /// be returned because [`ArrayRef::new_from_i8_slice_async`] should be used
576    /// instead.
577    ///
578    /// # Panics
579    ///
580    /// Panics if the allocator is not associated with the given store.
581    pub fn new_from_i8_slice(
582        mut store: impl AsContextMut,
583        allocator: &ArrayRefPre,
584        elems: &[u8],
585    ) -> Result<Rooted<ArrayRef>> {
586        let (mut limiter, store) = store
587            .as_context_mut()
588            .0
589            .validate_sync_resource_limiter_and_store_opaque()?;
590        vm::assert_ready(Self::_new_from_i8_slice_async(
591            store,
592            limiter.as_mut(),
593            allocator,
594            elems,
595            Asyncness::No,
596        ))
597    }
598
599    /// Asynchronously allocate a new `i8` array initialized from the given
600    /// bytes.
601    ///
602    /// This is the `async` equivalent of [`ArrayRef::new_from_i8_slice`]; see
603    /// that method for details. If your engine is not configured for async, use
604    /// [`ArrayRef::new_from_i8_slice`] to perform synchronous allocation.
605    ///
606    /// # Automatic Garbage Collection
607    ///
608    /// If the GC heap is at capacity, and there isn't room for allocating this
609    /// new array, then this method will automatically trigger an asynchronous
610    /// collection in an attempt to free up space in the GC heap.
611    ///
612    /// # Errors
613    ///
614    /// If the `allocator`'s array type does not have `i8` elements, an error is
615    /// returned.
616    ///
617    /// If the allocation cannot be satisfied because the GC heap is currently
618    /// out of memory, then a [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory]
619    /// error is returned. The allocation might succeed on a second attempt if
620    /// you drop some rooted GC references and try again.
621    ///
622    /// # Panics
623    ///
624    /// Panics if the `store` is not configured for async; use
625    /// [`ArrayRef::new_from_i8_slice`] to perform synchronous allocation
626    /// instead.
627    ///
628    /// Panics if the allocator is not associated with the given store.
629    #[cfg(feature = "async")]
630    pub async fn new_from_i8_slice_async(
631        mut store: impl AsContextMut,
632        allocator: &ArrayRefPre,
633        elems: &[u8],
634    ) -> Result<Rooted<ArrayRef>> {
635        let (mut limiter, store) = store.as_context_mut().0.resource_limiter_and_store_opaque();
636        Self::_new_from_i8_slice_async(store, limiter.as_mut(), allocator, elems, Asyncness::Yes)
637            .await
638    }
639
640    pub(crate) async fn _new_from_i8_slice_async(
641        store: &mut StoreOpaque,
642        limiter: Option<&mut StoreResourceLimiter<'_>>,
643        allocator: &ArrayRefPre,
644        elems: &[u8],
645        asyncness: Asyncness,
646    ) -> Result<Rooted<ArrayRef>> {
647        store
648            .retry_after_gc_async(limiter, (), asyncness, |store, ()| {
649                Self::new_from_i8_slice_inner(store, allocator, elems)
650            })
651            .await
652    }
653
654    /// Allocate a new array initialized from a slice of `i8` bytes.
655    ///
656    /// Does not attempt a GC on OOM; leaves that to callers.
657    fn new_from_i8_slice_inner(
658        store: &mut StoreOpaque,
659        allocator: &ArrayRefPre,
660        elems: &[u8],
661    ) -> Result<Rooted<ArrayRef>> {
662        assert_eq!(
663            store.id(),
664            allocator.store_id,
665            "attempted to use a `ArrayRefPre` with the wrong store"
666        );
667
668        let elem_ty = allocator.ty.element_type();
669        ensure!(
670            elem_ty.is_i8(),
671            "element type mismatch: cannot initialize an array of `{elem_ty}` elements from a slice of `i8`s"
672        );
673
674        let len = u32::try_from(elems.len())?;
675        let layout = allocator.layout();
676
677        let arrayref = store
678            .require_gc_store_mut()?
679            .alloc_uninit_array(allocator.type_index(), len, layout)
680            .context("unrecoverable error when allocating new `arrayref`")?
681            .map_err(|n| GcHeapOutOfMemory::new((), n))?;
682
683        // If the copy failed then the array is not fully initialized, so we
684        // must eagerly deallocate it before the next GC.
685        let mut store = AutoAssertNoGc::new(store);
686        match (|| {
687            store
688                .require_gc_store_mut()?
689                .gc_object_data(arrayref.as_gc_ref())?
690                .copy_from_slice(layout.base_size, elems)
691        })() {
692            Ok(()) => Ok(Rooted::new(&mut store, arrayref.into())),
693            Err(e) => {
694                store
695                    .require_gc_store_mut()?
696                    .dealloc_uninit_array(arrayref)?;
697                Err(e)
698            }
699        }
700    }
701
702    /// Copy this `i8` array's elements into the given byte slice.
703    ///
704    /// Unlike [`ArrayRef::get`], which decodes each element through a [`Val`],
705    /// the whole element body is copied into `dst` with a single `memcpy`. The
706    /// `i8` elements are read out as raw `u8` bytes.
707    ///
708    /// # Errors
709    ///
710    /// If this array does not have `i8` elements, an error is returned.
711    ///
712    /// If `dst`'s length does not equal this array's length, an error is
713    /// returned.
714    ///
715    /// Returns an error if this reference has been unrooted.
716    ///
717    /// # Panics
718    ///
719    /// Panics if this reference is associated with a different store.
720    pub fn copy_to_i8_slice(&self, mut store: impl AsContextMut, dst: &mut [u8]) -> Result<()> {
721        let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
722        assert!(
723            self.comes_from_same_store(&store),
724            "attempted to use an array with the wrong store",
725        );
726
727        let field_ty = self.field_ty(&store)?;
728        let elem_ty = field_ty.element_type();
729        ensure!(
730            elem_ty.is_i8(),
731            "element type mismatch: cannot read an array of `{elem_ty}` elements into a slice of `i8`s"
732        );
733
734        let layout = self.layout(&store)?;
735        let arrayref = self.arrayref(&store)?.unchecked_copy();
736        let len = arrayref.len(&store)?;
737
738        let dst_len = u32::try_from(dst.len())?;
739        ensure!(
740            dst_len == len,
741            "destination slice length is {dst_len} but the array length is {len}",
742        );
743
744        let data = store
745            .require_gc_store_mut()?
746            .gc_object_data(arrayref.as_gc_ref())?;
747        let bytes = data.slice(layout.base_size, len)?;
748        dst.copy_from_slice(bytes);
749        Ok(())
750    }
751
752    #[inline]
753    pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
754        self.inner.comes_from_same_store(store)
755    }
756
757    /// Get this `arrayref`'s type.
758    ///
759    /// # Errors
760    ///
761    /// Return an error if this reference has been unrooted.
762    ///
763    /// # Panics
764    ///
765    /// Panics if this reference is associated with a different store.
766    pub fn ty(&self, store: impl AsContext) -> Result<ArrayType> {
767        self._ty(store.as_context().0)
768    }
769
770    pub(crate) fn _ty(&self, store: &StoreOpaque) -> Result<ArrayType> {
771        assert!(self.comes_from_same_store(store));
772        let index = self.type_index(store)?;
773        match ArrayType::from_shared_type_index(store.engine(), index) {
774            Some(ty) => Ok(ty),
775            None => bail_bug!("invalid array type index"),
776        }
777    }
778
779    /// Does this `arrayref` match the given type?
780    ///
781    /// That is, is this array's type a subtype of the given type?
782    ///
783    /// # Errors
784    ///
785    /// Return an error if this reference has been unrooted.
786    ///
787    /// # Panics
788    ///
789    /// Panics if this reference is associated with a different store or if the
790    /// type is not associated with the store's engine.
791    pub fn matches_ty(&self, store: impl AsContext, ty: &ArrayType) -> Result<bool> {
792        self._matches_ty(store.as_context().0, ty)
793    }
794
795    pub(crate) fn _matches_ty(&self, store: &StoreOpaque, ty: &ArrayType) -> Result<bool> {
796        assert!(self.comes_from_same_store(store));
797        Ok(self._ty(store)?.matches(ty))
798    }
799
800    pub(crate) fn ensure_matches_ty(&self, store: &StoreOpaque, ty: &ArrayType) -> Result<()> {
801        if !self.comes_from_same_store(store) {
802            bail!("function used with wrong store");
803        }
804        if self._matches_ty(store, ty)? {
805            Ok(())
806        } else {
807            let actual_ty = self._ty(store)?;
808            bail!("type mismatch: expected `(ref {ty})`, found `(ref {actual_ty})`")
809        }
810    }
811
812    /// Get the length of this array.
813    ///
814    /// # Errors
815    ///
816    /// Return an error if this reference has been unrooted.
817    ///
818    /// # Panics
819    ///
820    /// Panics if this reference is associated with a different store.
821    pub fn len(&self, store: impl AsContext) -> Result<u32> {
822        self._len(store.as_context().0)
823    }
824
825    pub(crate) fn _len(&self, store: &StoreOpaque) -> Result<u32> {
826        assert!(self.comes_from_same_store(store));
827        let gc_ref = self.inner.try_gc_ref(store)?;
828        debug_assert!({
829            let header = store.require_gc_store()?.header(gc_ref)?;
830            header.matches_kind(VMGcKind::ArrayRef)
831        });
832        let arrayref = gc_ref.as_arrayref_unchecked();
833        arrayref.len(store)
834    }
835
836    /// Get the values of this array's elements.
837    ///
838    /// Note that `i8` and `i16` element values are zero-extended into
839    /// `Val::I32(_)`s.
840    ///
841    /// # Errors
842    ///
843    /// Return an error if this reference has been unrooted.
844    ///
845    /// # Panics
846    ///
847    /// Panics if this reference is associated with a different store.
848    pub fn elems<'a, T: 'static>(
849        &'a self,
850        store: impl Into<StoreContextMut<'a, T>>,
851    ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> {
852        self._elems(store.into().0)
853    }
854
855    pub(crate) fn _elems<'a>(
856        &'a self,
857        store: &'a mut StoreOpaque,
858    ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> {
859        assert!(self.comes_from_same_store(store));
860        let store = AutoAssertNoGc::new(store);
861
862        let gc_ref = self.inner.try_gc_ref(&store)?;
863        let header = store.require_gc_store()?.header(gc_ref)?;
864        debug_assert!(header.matches_kind(VMGcKind::ArrayRef));
865
866        let len = self._len(&store)?;
867
868        return Ok(Elems {
869            arrayref: self,
870            store,
871            index: 0,
872            len,
873        });
874
875        struct Elems<'a, 'b> {
876            arrayref: &'a ArrayRef,
877            store: AutoAssertNoGc<'b>,
878            index: u32,
879            len: u32,
880        }
881
882        impl Iterator for Elems<'_, '_> {
883            type Item = Val;
884
885            #[inline]
886            fn next(&mut self) -> Option<Self::Item> {
887                let i = self.index;
888                debug_assert!(i <= self.len);
889                if i >= self.len {
890                    return None;
891                }
892                self.index += 1;
893                self.arrayref._get(&mut self.store, i).ok()
894            }
895
896            #[inline]
897            fn size_hint(&self) -> (usize, Option<usize>) {
898                let len = self.len - self.index;
899                let len = usize::try_from(len).unwrap();
900                (len, Some(len))
901            }
902        }
903
904        impl ExactSizeIterator for Elems<'_, '_> {
905            #[inline]
906            fn len(&self) -> usize {
907                let len = self.len - self.index;
908                usize::try_from(len).unwrap()
909            }
910        }
911    }
912
913    fn header<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMGcHeader> {
914        assert!(self.comes_from_same_store(&store));
915        let gc_ref = self.inner.try_gc_ref(store)?;
916        Ok(store.require_gc_store()?.header(gc_ref)?)
917    }
918
919    fn arrayref<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMArrayRef> {
920        assert!(self.comes_from_same_store(&store));
921        let gc_ref = self.inner.try_gc_ref(store)?;
922        debug_assert!(self.header(store)?.matches_kind(VMGcKind::ArrayRef));
923        Ok(gc_ref.as_arrayref_unchecked())
924    }
925
926    pub(crate) fn layout(&self, store: &AutoAssertNoGc<'_>) -> Result<GcArrayLayout> {
927        assert!(self.comes_from_same_store(&store));
928        let type_index = self.type_index(store)?;
929        super::gc_array_layout(store.engine(), type_index)
930    }
931
932    fn field_ty(&self, store: &StoreOpaque) -> Result<FieldType> {
933        let ty = self._ty(store)?;
934        Ok(ty.field_type())
935    }
936
937    /// Get this array's `index`th element.
938    ///
939    /// Note that `i8` and `i16` field values are zero-extended into
940    /// `Val::I32(_)`s.
941    ///
942    /// # Errors
943    ///
944    /// Returns an `Err(_)` if the index is out of bounds or this reference has
945    /// been unrooted.
946    ///
947    /// # Panics
948    ///
949    /// Panics if this reference is associated with a different store.
950    pub fn get(&self, mut store: impl AsContextMut, index: u32) -> Result<Val> {
951        let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
952        self._get(&mut store, index)
953    }
954
955    pub(crate) fn _get(&self, store: &mut AutoAssertNoGc<'_>, index: u32) -> Result<Val> {
956        assert!(
957            self.comes_from_same_store(store),
958            "attempted to use an array with the wrong store",
959        );
960        let arrayref = self.arrayref(store)?.unchecked_copy();
961        let field_ty = self.field_ty(store)?;
962        let layout = self.layout(store)?;
963        let len = arrayref.len(store)?;
964        ensure!(
965            index < len,
966            "index out of bounds: the length is {len} but the index is {index}"
967        );
968        arrayref.read_elem(store, &layout, field_ty.element_type(), index)
969    }
970
971    /// Set this array's `index`th element.
972    ///
973    /// # Errors
974    ///
975    /// Returns an error in the following scenarios:
976    ///
977    /// * When given a value of the wrong type, such as trying to write an `f32`
978    ///   value into an array of `i64` elements.
979    ///
980    /// * When the array elements are not mutable.
981    ///
982    /// * When `index` is not within the range `0..self.len(ctx)`.
983    ///
984    /// * When `value` is a GC reference that has since been unrooted.
985    ///
986    /// # Panics
987    ///
988    /// Panics if either this reference or the given `value` is associated with
989    /// a different store.
990    pub fn set(&self, mut store: impl AsContextMut, index: u32, value: Val) -> Result<()> {
991        self._set(store.as_context_mut().0, index, value)
992    }
993
994    pub(crate) fn _set(&self, store: &mut StoreOpaque, index: u32, value: Val) -> Result<()> {
995        assert!(
996            self.comes_from_same_store(store),
997            "attempted to use an array with the wrong store",
998        );
999        assert!(
1000            value.comes_from_same_store(store),
1001            "attempted to use a value with the wrong store",
1002        );
1003
1004        let mut store = AutoAssertNoGc::new(store);
1005
1006        let field_ty = self.field_ty(&store)?;
1007        ensure!(
1008            field_ty.mutability().is_var(),
1009            "cannot set element {index}: array elements are not mutable"
1010        );
1011
1012        value
1013            .ensure_matches_ty(&store, &field_ty.element_type().unpack())
1014            .with_context(|| format!("cannot set element {index}: type mismatch"))?;
1015
1016        let layout = self.layout(&store)?;
1017        let arrayref = self.arrayref(&store)?.unchecked_copy();
1018
1019        let len = arrayref.len(&store)?;
1020        ensure!(
1021            index < len,
1022            "index out of bounds: the length is {len} but the index is {index}"
1023        );
1024
1025        arrayref.write_elem(&mut store, &layout, field_ty.element_type(), index, value)
1026    }
1027
1028    pub(crate) fn type_index(&self, store: &StoreOpaque) -> Result<VMSharedTypeIndex> {
1029        let gc_ref = self.inner.try_gc_ref(store)?;
1030        let header = store.require_gc_store()?.header(gc_ref)?;
1031        debug_assert!(header.matches_kind(VMGcKind::ArrayRef));
1032        match header.ty() {
1033            Some(ty) => Ok(ty),
1034            None => bail_bug!("arrayrefs should have concrete types"),
1035        }
1036    }
1037
1038    /// Create a new `Rooted<ArrayRef>` from the given GC reference.
1039    ///
1040    /// `gc_ref` should point to a valid `arrayref` and should belong to the
1041    /// store's GC heap. Failure to uphold these invariants is memory safe but
1042    /// will lead to general incorrectness such as panics or wrong results.
1043    pub(crate) fn from_cloned_gc_ref(
1044        store: &mut AutoAssertNoGc<'_>,
1045        gc_ref: VMGcRef,
1046    ) -> Rooted<Self> {
1047        debug_assert!(gc_ref.is_arrayref(&*store.unwrap_gc_store().gc_heap));
1048        Rooted::new(store, gc_ref)
1049    }
1050}
1051
1052unsafe impl WasmTy for Rooted<ArrayRef> {
1053    #[inline]
1054    fn valtype() -> ValType {
1055        ValType::Ref(RefType::new(false, HeapType::Array))
1056    }
1057
1058    #[inline]
1059    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
1060        self.comes_from_same_store(store)
1061    }
1062
1063    #[inline]
1064    fn dynamic_concrete_type_check(
1065        &self,
1066        store: &StoreOpaque,
1067        _nullable: bool,
1068        ty: &HeapType,
1069    ) -> Result<()> {
1070        match ty {
1071            HeapType::Any | HeapType::Eq | HeapType::Array => Ok(()),
1072            HeapType::ConcreteArray(ty) => self.ensure_matches_ty(store, ty),
1073
1074            HeapType::Extern
1075            | HeapType::NoExtern
1076            | HeapType::Func
1077            | HeapType::ConcreteFunc(_)
1078            | HeapType::NoFunc
1079            | HeapType::I31
1080            | HeapType::Struct
1081            | HeapType::ConcreteStruct(_)
1082            | HeapType::Cont
1083            | HeapType::NoCont
1084            | HeapType::ConcreteCont(_)
1085            | HeapType::Exn
1086            | HeapType::NoExn
1087            | HeapType::ConcreteExn(_)
1088            | HeapType::None => bail!(
1089                "type mismatch: expected `(ref {ty})`, got `(ref {})`",
1090                self._ty(store)?,
1091            ),
1092        }
1093    }
1094
1095    fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
1096        self.wasm_ty_store(store, ptr, ValRaw::anyref)
1097    }
1098
1099    unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
1100        Self::wasm_ty_load(store, ptr.get_anyref(), ArrayRef::from_cloned_gc_ref)
1101    }
1102}
1103
1104unsafe impl WasmTy for Option<Rooted<ArrayRef>> {
1105    #[inline]
1106    fn valtype() -> ValType {
1107        ValType::ARRAYREF
1108    }
1109
1110    #[inline]
1111    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
1112        self.map_or(true, |x| x.comes_from_same_store(store))
1113    }
1114
1115    #[inline]
1116    fn dynamic_concrete_type_check(
1117        &self,
1118        store: &StoreOpaque,
1119        nullable: bool,
1120        ty: &HeapType,
1121    ) -> Result<()> {
1122        match self {
1123            Some(s) => Rooted::<ArrayRef>::dynamic_concrete_type_check(s, store, nullable, ty),
1124            None => {
1125                ensure!(
1126                    nullable,
1127                    "expected a non-null reference, but found a null reference"
1128                );
1129                Ok(())
1130            }
1131        }
1132    }
1133
1134    #[inline]
1135    fn is_vmgcref_and_points_to_object(&self) -> bool {
1136        self.is_some()
1137    }
1138
1139    fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
1140        <Rooted<ArrayRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
1141    }
1142
1143    unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
1144        <Rooted<ArrayRef>>::wasm_ty_option_load(
1145            store,
1146            ptr.get_anyref(),
1147            ArrayRef::from_cloned_gc_ref,
1148        )
1149    }
1150}
1151
1152unsafe impl WasmTy for OwnedRooted<ArrayRef> {
1153    #[inline]
1154    fn valtype() -> ValType {
1155        ValType::Ref(RefType::new(false, HeapType::Array))
1156    }
1157
1158    #[inline]
1159    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
1160        self.comes_from_same_store(store)
1161    }
1162
1163    #[inline]
1164    fn dynamic_concrete_type_check(
1165        &self,
1166        store: &StoreOpaque,
1167        _: bool,
1168        ty: &HeapType,
1169    ) -> Result<()> {
1170        match ty {
1171            HeapType::Any | HeapType::Eq | HeapType::Array => Ok(()),
1172            HeapType::ConcreteArray(ty) => self.ensure_matches_ty(store, ty),
1173
1174            HeapType::Extern
1175            | HeapType::NoExtern
1176            | HeapType::Func
1177            | HeapType::ConcreteFunc(_)
1178            | HeapType::NoFunc
1179            | HeapType::I31
1180            | HeapType::Struct
1181            | HeapType::ConcreteStruct(_)
1182            | HeapType::Cont
1183            | HeapType::NoCont
1184            | HeapType::ConcreteCont(_)
1185            | HeapType::Exn
1186            | HeapType::NoExn
1187            | HeapType::ConcreteExn(_)
1188            | HeapType::None => bail!(
1189                "type mismatch: expected `(ref {ty})`, got `(ref {})`",
1190                self._ty(store)?,
1191            ),
1192        }
1193    }
1194
1195    fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
1196        self.wasm_ty_store(store, ptr, ValRaw::anyref)
1197    }
1198
1199    unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
1200        Self::wasm_ty_load(store, ptr.get_anyref(), ArrayRef::from_cloned_gc_ref)
1201    }
1202}
1203
1204unsafe impl WasmTy for Option<OwnedRooted<ArrayRef>> {
1205    #[inline]
1206    fn valtype() -> ValType {
1207        ValType::ARRAYREF
1208    }
1209
1210    #[inline]
1211    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
1212        self.as_ref()
1213            .map_or(true, |x| x.comes_from_same_store(store))
1214    }
1215
1216    #[inline]
1217    fn dynamic_concrete_type_check(
1218        &self,
1219        store: &StoreOpaque,
1220        nullable: bool,
1221        ty: &HeapType,
1222    ) -> Result<()> {
1223        match self {
1224            Some(s) => OwnedRooted::<ArrayRef>::dynamic_concrete_type_check(s, store, nullable, ty),
1225            None => {
1226                ensure!(
1227                    nullable,
1228                    "expected a non-null reference, but found a null reference"
1229                );
1230                Ok(())
1231            }
1232        }
1233    }
1234
1235    #[inline]
1236    fn is_vmgcref_and_points_to_object(&self) -> bool {
1237        self.is_some()
1238    }
1239
1240    fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
1241        <OwnedRooted<ArrayRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
1242    }
1243
1244    unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
1245        <OwnedRooted<ArrayRef>>::wasm_ty_option_load(
1246            store,
1247            ptr.get_anyref(),
1248            ArrayRef::from_cloned_gc_ref,
1249        )
1250    }
1251}