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