Skip to main content

wasmtime/runtime/gc/enabled/
arrayref.rs

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