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