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