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 #[inline]
545 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
546 self.inner.comes_from_same_store(store)
547 }
548
549 /// Get this `arrayref`'s type.
550 ///
551 /// # Errors
552 ///
553 /// Return an error if this reference has been unrooted.
554 ///
555 /// # Panics
556 ///
557 /// Panics if this reference is associated with a different store.
558 pub fn ty(&self, store: impl AsContext) -> Result<ArrayType> {
559 self._ty(store.as_context().0)
560 }
561
562 pub(crate) fn _ty(&self, store: &StoreOpaque) -> Result<ArrayType> {
563 assert!(self.comes_from_same_store(store));
564 let index = self.type_index(store)?;
565 Ok(ArrayType::from_shared_type_index(store.engine(), index))
566 }
567
568 /// Does this `arrayref` match the given type?
569 ///
570 /// That is, is this array's type a subtype of the given type?
571 ///
572 /// # Errors
573 ///
574 /// Return an error if this reference has been unrooted.
575 ///
576 /// # Panics
577 ///
578 /// Panics if this reference is associated with a different store or if the
579 /// type is not associated with the store's engine.
580 pub fn matches_ty(&self, store: impl AsContext, ty: &ArrayType) -> Result<bool> {
581 self._matches_ty(store.as_context().0, ty)
582 }
583
584 pub(crate) fn _matches_ty(&self, store: &StoreOpaque, ty: &ArrayType) -> Result<bool> {
585 assert!(self.comes_from_same_store(store));
586 Ok(self._ty(store)?.matches(ty))
587 }
588
589 pub(crate) fn ensure_matches_ty(&self, store: &StoreOpaque, ty: &ArrayType) -> Result<()> {
590 if !self.comes_from_same_store(store) {
591 bail!("function used with wrong store");
592 }
593 if self._matches_ty(store, ty)? {
594 Ok(())
595 } else {
596 let actual_ty = self._ty(store)?;
597 bail!("type mismatch: expected `(ref {ty})`, found `(ref {actual_ty})`")
598 }
599 }
600
601 /// Get the length of this array.
602 ///
603 /// # Errors
604 ///
605 /// Return an error if this reference has been unrooted.
606 ///
607 /// # Panics
608 ///
609 /// Panics if this reference is associated with a different store.
610 pub fn len(&self, store: impl AsContext) -> Result<u32> {
611 self._len(store.as_context().0)
612 }
613
614 pub(crate) fn _len(&self, store: &StoreOpaque) -> Result<u32> {
615 assert!(self.comes_from_same_store(store));
616 let gc_ref = self.inner.try_gc_ref(store)?;
617 debug_assert!({
618 let header = store.require_gc_store()?.header(gc_ref)?;
619 header.kind().matches(VMGcKind::ArrayRef)
620 });
621 let arrayref = gc_ref.as_arrayref_unchecked();
622 arrayref.len(store)
623 }
624
625 /// Get the values of this array's elements.
626 ///
627 /// Note that `i8` and `i16` element values are zero-extended into
628 /// `Val::I32(_)`s.
629 ///
630 /// # Errors
631 ///
632 /// Return an error if this reference has been unrooted.
633 ///
634 /// # Panics
635 ///
636 /// Panics if this reference is associated with a different store.
637 pub fn elems<'a, T: 'static>(
638 &'a self,
639 store: impl Into<StoreContextMut<'a, T>>,
640 ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> {
641 self._elems(store.into().0)
642 }
643
644 pub(crate) fn _elems<'a>(
645 &'a self,
646 store: &'a mut StoreOpaque,
647 ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> {
648 assert!(self.comes_from_same_store(store));
649 let store = AutoAssertNoGc::new(store);
650
651 let gc_ref = self.inner.try_gc_ref(&store)?;
652 let header = store.require_gc_store()?.header(gc_ref)?;
653 debug_assert!(header.kind().matches(VMGcKind::ArrayRef));
654
655 let len = self._len(&store)?;
656
657 return Ok(Elems {
658 arrayref: self,
659 store,
660 index: 0,
661 len,
662 });
663
664 struct Elems<'a, 'b> {
665 arrayref: &'a ArrayRef,
666 store: AutoAssertNoGc<'b>,
667 index: u32,
668 len: u32,
669 }
670
671 impl Iterator for Elems<'_, '_> {
672 type Item = Val;
673
674 #[inline]
675 fn next(&mut self) -> Option<Self::Item> {
676 let i = self.index;
677 debug_assert!(i <= self.len);
678 if i >= self.len {
679 return None;
680 }
681 self.index += 1;
682 self.arrayref._get(&mut self.store, i).ok()
683 }
684
685 #[inline]
686 fn size_hint(&self) -> (usize, Option<usize>) {
687 let len = self.len - self.index;
688 let len = usize::try_from(len).unwrap();
689 (len, Some(len))
690 }
691 }
692
693 impl ExactSizeIterator for Elems<'_, '_> {
694 #[inline]
695 fn len(&self) -> usize {
696 let len = self.len - self.index;
697 usize::try_from(len).unwrap()
698 }
699 }
700 }
701
702 fn header<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMGcHeader> {
703 assert!(self.comes_from_same_store(&store));
704 let gc_ref = self.inner.try_gc_ref(store)?;
705 Ok(store.require_gc_store()?.header(gc_ref)?)
706 }
707
708 fn arrayref<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMArrayRef> {
709 assert!(self.comes_from_same_store(&store));
710 let gc_ref = self.inner.try_gc_ref(store)?;
711 debug_assert!(self.header(store)?.kind().matches(VMGcKind::ArrayRef));
712 Ok(gc_ref.as_arrayref_unchecked())
713 }
714
715 pub(crate) fn layout(&self, store: &AutoAssertNoGc<'_>) -> Result<GcArrayLayout> {
716 assert!(self.comes_from_same_store(&store));
717 let type_index = self.type_index(store)?;
718 let layout = store
719 .engine()
720 .signatures()
721 .layout(type_index)
722 .expect("array types should have GC layouts");
723 match layout {
724 GcLayout::Array(a) => Ok(a),
725 GcLayout::Struct(_) => unreachable!(),
726 }
727 }
728
729 fn field_ty(&self, store: &StoreOpaque) -> Result<FieldType> {
730 let ty = self._ty(store)?;
731 Ok(ty.field_type())
732 }
733
734 /// Get this array's `index`th element.
735 ///
736 /// Note that `i8` and `i16` field values are zero-extended into
737 /// `Val::I32(_)`s.
738 ///
739 /// # Errors
740 ///
741 /// Returns an `Err(_)` if the index is out of bounds or this reference has
742 /// been unrooted.
743 ///
744 /// # Panics
745 ///
746 /// Panics if this reference is associated with a different store.
747 pub fn get(&self, mut store: impl AsContextMut, index: u32) -> Result<Val> {
748 let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
749 self._get(&mut store, index)
750 }
751
752 pub(crate) fn _get(&self, store: &mut AutoAssertNoGc<'_>, index: u32) -> Result<Val> {
753 assert!(
754 self.comes_from_same_store(store),
755 "attempted to use an array with the wrong store",
756 );
757 let arrayref = self.arrayref(store)?.unchecked_copy();
758 let field_ty = self.field_ty(store)?;
759 let layout = self.layout(store)?;
760 let len = arrayref.len(store)?;
761 ensure!(
762 index < len,
763 "index out of bounds: the length is {len} but the index is {index}"
764 );
765 arrayref.read_elem(store, &layout, field_ty.element_type(), index)
766 }
767
768 /// Set this array's `index`th element.
769 ///
770 /// # Errors
771 ///
772 /// Returns an error in the following scenarios:
773 ///
774 /// * When given a value of the wrong type, such as trying to write an `f32`
775 /// value into an array of `i64` elements.
776 ///
777 /// * When the array elements are not mutable.
778 ///
779 /// * When `index` is not within the range `0..self.len(ctx)`.
780 ///
781 /// * When `value` is a GC reference that has since been unrooted.
782 ///
783 /// # Panics
784 ///
785 /// Panics if either this reference or the given `value` is associated with
786 /// a different store.
787 pub fn set(&self, mut store: impl AsContextMut, index: u32, value: Val) -> Result<()> {
788 self._set(store.as_context_mut().0, index, value)
789 }
790
791 pub(crate) fn _set(&self, store: &mut StoreOpaque, index: u32, value: Val) -> Result<()> {
792 assert!(
793 self.comes_from_same_store(store),
794 "attempted to use an array with the wrong store",
795 );
796 assert!(
797 value.comes_from_same_store(store),
798 "attempted to use a value with the wrong store",
799 );
800
801 let mut store = AutoAssertNoGc::new(store);
802
803 let field_ty = self.field_ty(&store)?;
804 ensure!(
805 field_ty.mutability().is_var(),
806 "cannot set element {index}: array elements are not mutable"
807 );
808
809 value
810 .ensure_matches_ty(&store, &field_ty.element_type().unpack())
811 .with_context(|| format!("cannot set element {index}: type mismatch"))?;
812
813 let layout = self.layout(&store)?;
814 let arrayref = self.arrayref(&store)?.unchecked_copy();
815
816 let len = arrayref.len(&store)?;
817 ensure!(
818 index < len,
819 "index out of bounds: the length is {len} but the index is {index}"
820 );
821
822 arrayref.write_elem(&mut store, &layout, field_ty.element_type(), index, value)
823 }
824
825 pub(crate) fn type_index(&self, store: &StoreOpaque) -> Result<VMSharedTypeIndex> {
826 let gc_ref = self.inner.try_gc_ref(store)?;
827 let header = store.require_gc_store()?.header(gc_ref)?;
828 debug_assert!(header.kind().matches(VMGcKind::ArrayRef));
829 Ok(header.ty().expect("arrayrefs should have concrete types"))
830 }
831
832 /// Create a new `Rooted<ArrayRef>` from the given GC reference.
833 ///
834 /// `gc_ref` should point to a valid `arrayref` and should belong to the
835 /// store's GC heap. Failure to uphold these invariants is memory safe but
836 /// will lead to general incorrectness such as panics or wrong results.
837 pub(crate) fn from_cloned_gc_ref(
838 store: &mut AutoAssertNoGc<'_>,
839 gc_ref: VMGcRef,
840 ) -> Rooted<Self> {
841 debug_assert!(gc_ref.is_arrayref(&*store.unwrap_gc_store().gc_heap));
842 Rooted::new(store, gc_ref)
843 }
844}
845
846unsafe impl WasmTy for Rooted<ArrayRef> {
847 #[inline]
848 fn valtype() -> ValType {
849 ValType::Ref(RefType::new(false, HeapType::Array))
850 }
851
852 #[inline]
853 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
854 self.comes_from_same_store(store)
855 }
856
857 #[inline]
858 fn dynamic_concrete_type_check(
859 &self,
860 store: &StoreOpaque,
861 _nullable: bool,
862 ty: &HeapType,
863 ) -> Result<()> {
864 match ty {
865 HeapType::Any | HeapType::Eq | HeapType::Array => Ok(()),
866 HeapType::ConcreteArray(ty) => self.ensure_matches_ty(store, ty),
867
868 HeapType::Extern
869 | HeapType::NoExtern
870 | HeapType::Func
871 | HeapType::ConcreteFunc(_)
872 | HeapType::NoFunc
873 | HeapType::I31
874 | HeapType::Struct
875 | HeapType::ConcreteStruct(_)
876 | HeapType::Cont
877 | HeapType::NoCont
878 | HeapType::ConcreteCont(_)
879 | HeapType::Exn
880 | HeapType::NoExn
881 | HeapType::ConcreteExn(_)
882 | HeapType::None => bail!(
883 "type mismatch: expected `(ref {ty})`, got `(ref {})`",
884 self._ty(store)?,
885 ),
886 }
887 }
888
889 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
890 self.wasm_ty_store(store, ptr, ValRaw::anyref)
891 }
892
893 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
894 Self::wasm_ty_load(store, ptr.get_anyref(), ArrayRef::from_cloned_gc_ref)
895 }
896}
897
898unsafe impl WasmTy for Option<Rooted<ArrayRef>> {
899 #[inline]
900 fn valtype() -> ValType {
901 ValType::ARRAYREF
902 }
903
904 #[inline]
905 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
906 self.map_or(true, |x| x.comes_from_same_store(store))
907 }
908
909 #[inline]
910 fn dynamic_concrete_type_check(
911 &self,
912 store: &StoreOpaque,
913 nullable: bool,
914 ty: &HeapType,
915 ) -> Result<()> {
916 match self {
917 Some(s) => Rooted::<ArrayRef>::dynamic_concrete_type_check(s, store, nullable, ty),
918 None => {
919 ensure!(
920 nullable,
921 "expected a non-null reference, but found a null reference"
922 );
923 Ok(())
924 }
925 }
926 }
927
928 #[inline]
929 fn is_vmgcref_and_points_to_object(&self) -> bool {
930 self.is_some()
931 }
932
933 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
934 <Rooted<ArrayRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
935 }
936
937 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
938 <Rooted<ArrayRef>>::wasm_ty_option_load(
939 store,
940 ptr.get_anyref(),
941 ArrayRef::from_cloned_gc_ref,
942 )
943 }
944}
945
946unsafe impl WasmTy for OwnedRooted<ArrayRef> {
947 #[inline]
948 fn valtype() -> ValType {
949 ValType::Ref(RefType::new(false, HeapType::Array))
950 }
951
952 #[inline]
953 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
954 self.comes_from_same_store(store)
955 }
956
957 #[inline]
958 fn dynamic_concrete_type_check(
959 &self,
960 store: &StoreOpaque,
961 _: bool,
962 ty: &HeapType,
963 ) -> Result<()> {
964 match ty {
965 HeapType::Any | HeapType::Eq | HeapType::Array => Ok(()),
966 HeapType::ConcreteArray(ty) => self.ensure_matches_ty(store, ty),
967
968 HeapType::Extern
969 | HeapType::NoExtern
970 | HeapType::Func
971 | HeapType::ConcreteFunc(_)
972 | HeapType::NoFunc
973 | HeapType::I31
974 | HeapType::Struct
975 | HeapType::ConcreteStruct(_)
976 | HeapType::Cont
977 | HeapType::NoCont
978 | HeapType::ConcreteCont(_)
979 | HeapType::Exn
980 | HeapType::NoExn
981 | HeapType::ConcreteExn(_)
982 | HeapType::None => bail!(
983 "type mismatch: expected `(ref {ty})`, got `(ref {})`",
984 self._ty(store)?,
985 ),
986 }
987 }
988
989 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
990 self.wasm_ty_store(store, ptr, ValRaw::anyref)
991 }
992
993 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
994 Self::wasm_ty_load(store, ptr.get_anyref(), ArrayRef::from_cloned_gc_ref)
995 }
996}
997
998unsafe impl WasmTy for Option<OwnedRooted<ArrayRef>> {
999 #[inline]
1000 fn valtype() -> ValType {
1001 ValType::ARRAYREF
1002 }
1003
1004 #[inline]
1005 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
1006 self.as_ref()
1007 .map_or(true, |x| x.comes_from_same_store(store))
1008 }
1009
1010 #[inline]
1011 fn dynamic_concrete_type_check(
1012 &self,
1013 store: &StoreOpaque,
1014 nullable: bool,
1015 ty: &HeapType,
1016 ) -> Result<()> {
1017 match self {
1018 Some(s) => OwnedRooted::<ArrayRef>::dynamic_concrete_type_check(s, store, nullable, ty),
1019 None => {
1020 ensure!(
1021 nullable,
1022 "expected a non-null reference, but found a null reference"
1023 );
1024 Ok(())
1025 }
1026 }
1027 }
1028
1029 #[inline]
1030 fn is_vmgcref_and_points_to_object(&self) -> bool {
1031 self.is_some()
1032 }
1033
1034 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
1035 <OwnedRooted<ArrayRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
1036 }
1037
1038 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
1039 <OwnedRooted<ArrayRef>>::wasm_ty_option_load(
1040 store,
1041 ptr.get_anyref(),
1042 ArrayRef::from_cloned_gc_ref,
1043 )
1044 }
1045}