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}