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