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 fn transmute_ref(index: &GcRootIndex) -> &Self { 187 // Safety: `ArrayRef` is a newtype of a `GcRootIndex`. 188 let me: &Self = unsafe { mem::transmute(index) }; 189 190 // Assert we really are just a newtype of a `GcRootIndex`. 191 assert!(matches!( 192 me, 193 Self { 194 inner: GcRootIndex { .. }, 195 } 196 )); 197 198 me 199 } 200 } 201 202 impl Rooted<ArrayRef> { 203 /// Upcast this `arrayref` into an `anyref`. 204 #[inline] 205 pub fn to_anyref(self) -> Rooted<AnyRef> { 206 self.unchecked_cast() 207 } 208 209 /// Upcast this `arrayref` into an `eqref`. 210 #[inline] 211 pub fn to_eqref(self) -> Rooted<EqRef> { 212 self.unchecked_cast() 213 } 214 } 215 216 impl ManuallyRooted<ArrayRef> { 217 /// Upcast this `arrayref` into an `anyref`. 218 #[inline] 219 pub fn to_anyref(self) -> ManuallyRooted<AnyRef> { 220 self.unchecked_cast() 221 } 222 223 /// Upcast this `arrayref` into an `eqref`. 224 #[inline] 225 pub fn to_eqref(self) -> ManuallyRooted<EqRef> { 226 self.unchecked_cast() 227 } 228 } 229 230 /// An iterator for elements in `ArrayRef::new[_async]. 231 /// 232 /// NB: We can't use `iter::repeat(elem).take(len)` because that doesn't 233 /// implement `ExactSizeIterator`. 234 #[derive(Clone)] 235 struct RepeatN<'a>(&'a Val, u32); 236 237 impl<'a> Iterator for RepeatN<'a> { 238 type Item = &'a Val; 239 240 fn next(&mut self) -> Option<Self::Item> { 241 if self.1 == 0 { 242 None 243 } else { 244 self.1 -= 1; 245 Some(self.0) 246 } 247 } 248 249 fn size_hint(&self) -> (usize, Option<usize>) { 250 let len = self.len(); 251 (len, Some(len)) 252 } 253 } 254 255 impl ExactSizeIterator for RepeatN<'_> { 256 fn len(&self) -> usize { 257 usize::try_from(self.1).unwrap() 258 } 259 } 260 261 impl ArrayRef { 262 /// Allocate a new `array` of the given length, with every element 263 /// initialized to `elem`. 264 /// 265 /// For example, `ArrayRef::new(ctx, pre, &Val::I64(9), 3)` allocates the 266 /// array `[9, 9, 9]`. 267 /// 268 /// This is similar to the `array.new` instruction. 269 /// 270 /// # Automatic Garbage Collection 271 /// 272 /// If the GC heap is at capacity, and there isn't room for allocating this 273 /// new array, then this method will automatically trigger a synchronous 274 /// collection in an attempt to free up space in the GC heap. 275 /// 276 /// # Errors 277 /// 278 /// If the given `elem` value's type does not match the `allocator`'s array 279 /// type's element type, an error is returned. 280 /// 281 /// If the allocation cannot be satisfied because the GC heap is currently 282 /// out of memory, then a [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory] 283 /// error is returned. The allocation might succeed on a second attempt if 284 /// you drop some rooted GC references and try again. 285 /// 286 /// # Panics 287 /// 288 /// Panics if the `store` is configured for async; use 289 /// [`ArrayRef::new_async`][crate::ArrayRef::new_async] to perform 290 /// asynchronous allocation instead. 291 /// 292 /// Panics if either the allocator or the `elem` value is not associated 293 /// with the given store. 294 pub fn new( 295 mut store: impl AsContextMut, 296 allocator: &ArrayRefPre, 297 elem: &Val, 298 len: u32, 299 ) -> Result<Rooted<ArrayRef>> { 300 Self::_new(store.as_context_mut().0, allocator, elem, len) 301 } 302 303 pub(crate) fn _new( 304 store: &mut StoreOpaque, 305 allocator: &ArrayRefPre, 306 elem: &Val, 307 len: u32, 308 ) -> Result<Rooted<ArrayRef>> { 309 store.retry_after_gc((), |store, ()| { 310 Self::new_from_iter(store, allocator, RepeatN(elem, len)) 311 }) 312 } 313 314 /// Asynchronously allocate a new `array` of the given length, with every 315 /// element initialized to `elem`. 316 /// 317 /// For example, `ArrayRef::new(ctx, pre, &Val::I64(9), 3)` allocates the 318 /// array `[9, 9, 9]`. 319 /// 320 /// This is similar to the `array.new` instruction. 321 /// 322 /// # Automatic Garbage Collection 323 /// 324 /// If the GC heap is at capacity, and there isn't room for allocating this 325 /// new array, then this method will automatically trigger a asynchronous 326 /// collection in an attempt to free up space in the GC heap. 327 /// 328 /// # Errors 329 /// 330 /// If the given `elem` value's type does not match the `allocator`'s array 331 /// type's element type, an error is returned. 332 /// 333 /// If the allocation cannot be satisfied because the GC heap is currently 334 /// out of memory, then a [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory] 335 /// error is returned. The allocation might succeed on a second attempt if 336 /// you drop some rooted GC references and try again. 337 /// 338 /// # Panics 339 /// 340 /// Panics if your engine is not configured for async; use 341 /// [`ArrayRef::new_async`][crate::ArrayRef::new_async] to perform 342 /// synchronous allocation instead. 343 /// 344 /// Panics if either the allocator or the `elem` value is not associated 345 /// with the given store. 346 #[cfg(feature = "async")] 347 pub async fn new_async( 348 mut store: impl AsContextMut, 349 allocator: &ArrayRefPre, 350 elem: &Val, 351 len: u32, 352 ) -> Result<Rooted<ArrayRef>> { 353 Self::_new_async(store.as_context_mut().0, allocator, elem, len).await 354 } 355 356 #[cfg(feature = "async")] 357 pub(crate) async fn _new_async( 358 store: &mut StoreOpaque, 359 allocator: &ArrayRefPre, 360 elem: &Val, 361 len: u32, 362 ) -> Result<Rooted<ArrayRef>> { 363 store 364 .retry_after_gc_async((), |store, ()| { 365 Self::new_from_iter(store, allocator, RepeatN(elem, len)) 366 }) 367 .await 368 } 369 370 /// Like `ArrayRef::new` but when async is configured must only ever be 371 /// called from on a fiber stack. 372 pub(crate) unsafe fn new_maybe_async( 373 store: &mut StoreOpaque, 374 allocator: &ArrayRefPre, 375 elem: &Val, 376 len: u32, 377 ) -> Result<Rooted<ArrayRef>> { 378 // Type check the initial element value against the element type. 379 elem.ensure_matches_ty(store, allocator.ty.element_type().unpack()) 380 .context("element type mismatch")?; 381 382 unsafe { 383 store.retry_after_gc_maybe_async((), |store, ()| { 384 Self::new_from_iter(store, allocator, RepeatN(elem, len)) 385 }) 386 } 387 } 388 389 /// Allocate a new array of the given elements. 390 /// 391 /// Does not attempt a GC on OOM; leaves that to callers. 392 fn new_from_iter<'a>( 393 store: &mut StoreOpaque, 394 allocator: &ArrayRefPre, 395 elems: impl Clone + ExactSizeIterator<Item = &'a Val>, 396 ) -> Result<Rooted<ArrayRef>> { 397 assert_eq!( 398 store.id(), 399 allocator.store_id, 400 "attempted to use a `ArrayRefPre` with the wrong store" 401 ); 402 403 // Type check the elements against the element type. 404 for elem in elems.clone() { 405 elem.ensure_matches_ty(store, allocator.ty.element_type().unpack()) 406 .context("element type mismatch")?; 407 } 408 409 let len = u32::try_from(elems.len()).unwrap(); 410 411 // Allocate the array and write each field value into the appropriate 412 // offset. 413 let arrayref = store 414 .gc_store_mut()? 415 .alloc_uninit_array(allocator.type_index(), len, allocator.layout()) 416 .context("unrecoverable error when allocating new `arrayref`")? 417 .map_err(|n| GcHeapOutOfMemory::new((), n))?; 418 419 // From this point on, if we get any errors, then the array is not 420 // fully initialized, so we need to eagerly deallocate it before the 421 // next GC where the collector might try to interpret one of the 422 // uninitialized fields as a GC reference. 423 let mut store = AutoAssertNoGc::new(store); 424 match (|| { 425 let elem_ty = allocator.ty.element_type(); 426 for (i, elem) in elems.enumerate() { 427 let i = u32::try_from(i).unwrap(); 428 debug_assert!(i < len); 429 arrayref.initialize_elem(&mut store, allocator.layout(), &elem_ty, i, *elem)?; 430 } 431 Ok(()) 432 })() { 433 Ok(()) => Ok(Rooted::new(&mut store, arrayref.into())), 434 Err(e) => { 435 store.gc_store_mut()?.dealloc_uninit_array(arrayref); 436 Err(e) 437 } 438 } 439 } 440 441 /// Synchronously allocate a new `array` containing the given elements. 442 /// 443 /// For example, `ArrayRef::new_fixed(ctx, pre, &[Val::I64(4), Val::I64(5), 444 /// Val::I64(6)])` allocates the array `[4, 5, 6]`. 445 /// 446 /// This is similar to the `array.new_fixed` instruction. 447 /// 448 /// # Automatic Garbage Collection 449 /// 450 /// If the GC heap is at capacity, and there isn't room for allocating this 451 /// new array, then this method will automatically trigger a synchronous 452 /// collection in an attempt to free up space in the GC heap. 453 /// 454 /// # Errors 455 /// 456 /// If any of the `elems` values' type does not match the `allocator`'s 457 /// array type's element type, an error is returned. 458 /// 459 /// If the allocation cannot be satisfied because the GC heap is currently 460 /// out of memory, then a [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory] 461 /// error is returned. The allocation might succeed on a second attempt if 462 /// you drop some rooted GC references and try again. 463 /// 464 /// # Panics 465 /// 466 /// Panics if the `store` is configured for async; use 467 /// [`ArrayRef::new_fixed_async`][crate::ArrayRef::new_fixed_async] to 468 /// perform asynchronous allocation instead. 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 Self::_new_fixed(store.as_context_mut().0, allocator, elems) 478 } 479 480 pub(crate) fn _new_fixed( 481 store: &mut StoreOpaque, 482 allocator: &ArrayRefPre, 483 elems: &[Val], 484 ) -> Result<Rooted<ArrayRef>> { 485 store.retry_after_gc((), |store, ()| { 486 Self::new_from_iter(store, allocator, elems.iter()) 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 Self::_new_fixed_async(store.as_context_mut().0, allocator, elems).await 532 } 533 534 #[cfg(feature = "async")] 535 pub(crate) async fn _new_fixed_async( 536 store: &mut StoreOpaque, 537 allocator: &ArrayRefPre, 538 elems: &[Val], 539 ) -> Result<Rooted<ArrayRef>> { 540 store 541 .retry_after_gc_async((), |store, ()| { 542 Self::new_from_iter(store, allocator, elems.iter()) 543 }) 544 .await 545 } 546 547 /// Like `ArrayRef::new_fixed[_async]` but it is the caller's responsibility 548 /// to ensure that when async is enabled, this is only called from on a 549 /// fiber stack. 550 pub(crate) unsafe fn new_fixed_maybe_async( 551 store: &mut StoreOpaque, 552 allocator: &ArrayRefPre, 553 elems: &[Val], 554 ) -> Result<Rooted<ArrayRef>> { 555 unsafe { 556 store.retry_after_gc_maybe_async((), |store, ()| { 557 Self::new_from_iter(store, allocator, elems.iter()) 558 }) 559 } 560 } 561 562 #[inline] 563 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool { 564 self.inner.comes_from_same_store(store) 565 } 566 567 /// Get this `arrayref`'s type. 568 /// 569 /// # Errors 570 /// 571 /// Return an error if this reference has been unrooted. 572 /// 573 /// # Panics 574 /// 575 /// Panics if this reference is associated with a different store. 576 pub fn ty(&self, store: impl AsContext) -> Result<ArrayType> { 577 self._ty(store.as_context().0) 578 } 579 580 pub(crate) fn _ty(&self, store: &StoreOpaque) -> Result<ArrayType> { 581 assert!(self.comes_from_same_store(store)); 582 let index = self.type_index(store)?; 583 Ok(ArrayType::from_shared_type_index(store.engine(), index)) 584 } 585 586 /// Does this `arrayref` match the given type? 587 /// 588 /// That is, is this array's type a subtype of the given type? 589 /// 590 /// # Errors 591 /// 592 /// Return an error if this reference has been unrooted. 593 /// 594 /// # Panics 595 /// 596 /// Panics if this reference is associated with a different store or if the 597 /// type is not associated with the store's engine. 598 pub fn matches_ty(&self, store: impl AsContext, ty: &ArrayType) -> Result<bool> { 599 self._matches_ty(store.as_context().0, ty) 600 } 601 602 pub(crate) fn _matches_ty(&self, store: &StoreOpaque, ty: &ArrayType) -> Result<bool> { 603 assert!(self.comes_from_same_store(store)); 604 Ok(self._ty(store)?.matches(ty)) 605 } 606 607 pub(crate) fn ensure_matches_ty(&self, store: &StoreOpaque, ty: &ArrayType) -> Result<()> { 608 if !self.comes_from_same_store(store) { 609 bail!("function used with wrong store"); 610 } 611 if self._matches_ty(store, ty)? { 612 Ok(()) 613 } else { 614 let actual_ty = self._ty(store)?; 615 bail!("type mismatch: expected `(ref {ty})`, found `(ref {actual_ty})`") 616 } 617 } 618 619 /// Get the length of this array. 620 /// 621 /// # Errors 622 /// 623 /// Return an error if this reference has been unrooted. 624 /// 625 /// # Panics 626 /// 627 /// Panics if this reference is associated with a different store. 628 pub fn len(&self, store: impl AsContext) -> Result<u32> { 629 self._len(store.as_context().0) 630 } 631 632 pub(crate) fn _len(&self, store: &StoreOpaque) -> Result<u32> { 633 assert!(self.comes_from_same_store(store)); 634 let gc_ref = self.inner.try_gc_ref(store)?; 635 debug_assert!({ 636 let header = store.gc_store()?.header(gc_ref); 637 header.kind().matches(VMGcKind::ArrayRef) 638 }); 639 let arrayref = gc_ref.as_arrayref_unchecked(); 640 Ok(arrayref.len(store)) 641 } 642 643 /// Get the values of this array's elements. 644 /// 645 /// Note that `i8` and `i16` element values are zero-extended into 646 /// `Val::I32(_)`s. 647 /// 648 /// # Errors 649 /// 650 /// Return an error if this reference has been unrooted. 651 /// 652 /// # Panics 653 /// 654 /// Panics if this reference is associated with a different store. 655 pub fn elems<'a, T: 'static>( 656 &'a self, 657 store: impl Into<StoreContextMut<'a, T>>, 658 ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> { 659 self._elems(store.into().0) 660 } 661 662 pub(crate) fn _elems<'a>( 663 &'a self, 664 store: &'a mut StoreOpaque, 665 ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> { 666 assert!(self.comes_from_same_store(store)); 667 let store = AutoAssertNoGc::new(store); 668 669 let gc_ref = self.inner.try_gc_ref(&store)?; 670 let header = store.gc_store()?.header(gc_ref); 671 debug_assert!(header.kind().matches(VMGcKind::ArrayRef)); 672 673 let len = self._len(&store)?; 674 675 return Ok(Elems { 676 arrayref: self, 677 store, 678 index: 0, 679 len, 680 }); 681 682 struct Elems<'a, 'b> { 683 arrayref: &'a ArrayRef, 684 store: AutoAssertNoGc<'b>, 685 index: u32, 686 len: u32, 687 } 688 689 impl Iterator for Elems<'_, '_> { 690 type Item = Val; 691 692 #[inline] 693 fn next(&mut self) -> Option<Self::Item> { 694 let i = self.index; 695 debug_assert!(i <= self.len); 696 if i >= self.len { 697 return None; 698 } 699 self.index += 1; 700 Some(self.arrayref._get(&mut self.store, i).unwrap()) 701 } 702 703 #[inline] 704 fn size_hint(&self) -> (usize, Option<usize>) { 705 let len = self.len - self.index; 706 let len = usize::try_from(len).unwrap(); 707 (len, Some(len)) 708 } 709 } 710 711 impl ExactSizeIterator for Elems<'_, '_> { 712 #[inline] 713 fn len(&self) -> usize { 714 let len = self.len - self.index; 715 usize::try_from(len).unwrap() 716 } 717 } 718 } 719 720 fn header<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMGcHeader> { 721 assert!(self.comes_from_same_store(&store)); 722 let gc_ref = self.inner.try_gc_ref(store)?; 723 Ok(store.gc_store()?.header(gc_ref)) 724 } 725 726 fn arrayref<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMArrayRef> { 727 assert!(self.comes_from_same_store(&store)); 728 let gc_ref = self.inner.try_gc_ref(store)?; 729 debug_assert!(self.header(store)?.kind().matches(VMGcKind::ArrayRef)); 730 Ok(gc_ref.as_arrayref_unchecked()) 731 } 732 733 pub(crate) fn layout(&self, store: &AutoAssertNoGc<'_>) -> Result<GcArrayLayout> { 734 assert!(self.comes_from_same_store(&store)); 735 let type_index = self.type_index(store)?; 736 let layout = store 737 .engine() 738 .signatures() 739 .layout(type_index) 740 .expect("array types should have GC layouts"); 741 match layout { 742 GcLayout::Array(a) => Ok(a), 743 GcLayout::Struct(_) => unreachable!(), 744 } 745 } 746 747 fn field_ty(&self, store: &StoreOpaque) -> Result<FieldType> { 748 let ty = self._ty(store)?; 749 Ok(ty.field_type()) 750 } 751 752 /// Get this array's `index`th element. 753 /// 754 /// Note that `i8` and `i16` field values are zero-extended into 755 /// `Val::I32(_)`s. 756 /// 757 /// # Errors 758 /// 759 /// Returns an `Err(_)` if the index is out of bounds or this reference has 760 /// been unrooted. 761 /// 762 /// # Panics 763 /// 764 /// Panics if this reference is associated with a different store. 765 pub fn get(&self, mut store: impl AsContextMut, index: u32) -> Result<Val> { 766 let mut store = AutoAssertNoGc::new(store.as_context_mut().0); 767 self._get(&mut store, index) 768 } 769 770 pub(crate) fn _get(&self, store: &mut AutoAssertNoGc<'_>, index: u32) -> Result<Val> { 771 assert!( 772 self.comes_from_same_store(store), 773 "attempted to use an array with the wrong store", 774 ); 775 let arrayref = self.arrayref(store)?.unchecked_copy(); 776 let field_ty = self.field_ty(store)?; 777 let layout = self.layout(store)?; 778 let len = arrayref.len(store); 779 ensure!( 780 index < len, 781 "index out of bounds: the length is {len} but the index is {index}" 782 ); 783 Ok(arrayref.read_elem(store, &layout, field_ty.element_type(), index)) 784 } 785 786 /// Set this array's `index`th element. 787 /// 788 /// # Errors 789 /// 790 /// Returns an error in the following scenarios: 791 /// 792 /// * When given a value of the wrong type, such as trying to write an `f32` 793 /// value into an array of `i64` elements. 794 /// 795 /// * When the array elements are not mutable. 796 /// 797 /// * When `index` is not within the range `0..self.len(ctx)`. 798 /// 799 /// * When `value` is a GC reference that has since been unrooted. 800 /// 801 /// # Panics 802 /// 803 /// Panics if either this reference or the given `value` is associated with 804 /// a different store. 805 pub fn set(&self, mut store: impl AsContextMut, index: u32, value: Val) -> Result<()> { 806 self._set(store.as_context_mut().0, index, value) 807 } 808 809 pub(crate) fn _set(&self, store: &mut StoreOpaque, index: u32, value: Val) -> Result<()> { 810 assert!( 811 self.comes_from_same_store(store), 812 "attempted to use an array with the wrong store", 813 ); 814 assert!( 815 value.comes_from_same_store(store), 816 "attempted to use a value with the wrong store", 817 ); 818 819 let mut store = AutoAssertNoGc::new(store); 820 821 let field_ty = self.field_ty(&store)?; 822 ensure!( 823 field_ty.mutability().is_var(), 824 "cannot set element {index}: array elements are not mutable" 825 ); 826 827 value 828 .ensure_matches_ty(&store, &field_ty.element_type().unpack()) 829 .with_context(|| format!("cannot set element {index}: type mismatch"))?; 830 831 let layout = self.layout(&store)?; 832 let arrayref = self.arrayref(&store)?.unchecked_copy(); 833 834 let len = arrayref.len(&store); 835 ensure!( 836 index < len, 837 "index out of bounds: the length is {len} but the index is {index}" 838 ); 839 840 arrayref.write_elem(&mut store, &layout, field_ty.element_type(), index, value) 841 } 842 843 pub(crate) fn type_index(&self, store: &StoreOpaque) -> Result<VMSharedTypeIndex> { 844 let gc_ref = self.inner.try_gc_ref(store)?; 845 let header = store.gc_store()?.header(gc_ref); 846 debug_assert!(header.kind().matches(VMGcKind::ArrayRef)); 847 Ok(header.ty().expect("arrayrefs should have concrete types")) 848 } 849 850 /// Create a new `Rooted<ArrayRef>` from the given GC reference. 851 /// 852 /// `gc_ref` should point to a valid `arrayref` and should belong to the 853 /// store's GC heap. Failure to uphold these invariants is memory safe but 854 /// will lead to general incorrectness such as panics or wrong results. 855 pub(crate) fn from_cloned_gc_ref( 856 store: &mut AutoAssertNoGc<'_>, 857 gc_ref: VMGcRef, 858 ) -> Rooted<Self> { 859 debug_assert!(gc_ref.is_arrayref(&*store.unwrap_gc_store().gc_heap)); 860 Rooted::new(store, gc_ref) 861 } 862 } 863 864 unsafe impl WasmTy for Rooted<ArrayRef> { 865 #[inline] 866 fn valtype() -> ValType { 867 ValType::Ref(RefType::new(false, HeapType::Array)) 868 } 869 870 #[inline] 871 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 872 self.comes_from_same_store(store) 873 } 874 875 #[inline] 876 fn dynamic_concrete_type_check( 877 &self, 878 store: &StoreOpaque, 879 _nullable: bool, 880 ty: &HeapType, 881 ) -> Result<()> { 882 match ty { 883 HeapType::Any | HeapType::Eq | HeapType::Array => Ok(()), 884 HeapType::ConcreteArray(ty) => self.ensure_matches_ty(store, ty), 885 886 HeapType::Extern 887 | HeapType::NoExtern 888 | HeapType::Func 889 | HeapType::ConcreteFunc(_) 890 | HeapType::NoFunc 891 | HeapType::I31 892 | HeapType::Struct 893 | HeapType::ConcreteStruct(_) 894 | HeapType::Cont 895 | HeapType::NoCont 896 | HeapType::ConcreteCont(_) 897 | HeapType::None => bail!( 898 "type mismatch: expected `(ref {ty})`, got `(ref {})`", 899 self._ty(store)?, 900 ), 901 } 902 } 903 904 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 905 self.wasm_ty_store(store, ptr, ValRaw::anyref) 906 } 907 908 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 909 Self::wasm_ty_load(store, ptr.get_anyref(), ArrayRef::from_cloned_gc_ref) 910 } 911 } 912 913 unsafe impl WasmTy for Option<Rooted<ArrayRef>> { 914 #[inline] 915 fn valtype() -> ValType { 916 ValType::ARRAYREF 917 } 918 919 #[inline] 920 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 921 self.map_or(true, |x| x.comes_from_same_store(store)) 922 } 923 924 #[inline] 925 fn dynamic_concrete_type_check( 926 &self, 927 store: &StoreOpaque, 928 nullable: bool, 929 ty: &HeapType, 930 ) -> Result<()> { 931 match self { 932 Some(s) => Rooted::<ArrayRef>::dynamic_concrete_type_check(s, store, nullable, ty), 933 None => { 934 ensure!( 935 nullable, 936 "expected a non-null reference, but found a null reference" 937 ); 938 Ok(()) 939 } 940 } 941 } 942 943 #[inline] 944 fn is_vmgcref_and_points_to_object(&self) -> bool { 945 self.is_some() 946 } 947 948 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 949 <Rooted<ArrayRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref) 950 } 951 952 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 953 <Rooted<ArrayRef>>::wasm_ty_option_load( 954 store, 955 ptr.get_anyref(), 956 ArrayRef::from_cloned_gc_ref, 957 ) 958 } 959 } 960 961 unsafe impl WasmTy for ManuallyRooted<ArrayRef> { 962 #[inline] 963 fn valtype() -> ValType { 964 ValType::Ref(RefType::new(false, HeapType::Array)) 965 } 966 967 #[inline] 968 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 969 self.comes_from_same_store(store) 970 } 971 972 #[inline] 973 fn dynamic_concrete_type_check( 974 &self, 975 store: &StoreOpaque, 976 _: bool, 977 ty: &HeapType, 978 ) -> Result<()> { 979 match ty { 980 HeapType::Any | HeapType::Eq | HeapType::Array => Ok(()), 981 HeapType::ConcreteArray(ty) => self.ensure_matches_ty(store, ty), 982 983 HeapType::Extern 984 | HeapType::NoExtern 985 | HeapType::Func 986 | HeapType::ConcreteFunc(_) 987 | HeapType::NoFunc 988 | HeapType::I31 989 | HeapType::Struct 990 | HeapType::ConcreteStruct(_) 991 | HeapType::Cont 992 | HeapType::NoCont 993 | HeapType::ConcreteCont(_) 994 | HeapType::None => bail!( 995 "type mismatch: expected `(ref {ty})`, got `(ref {})`", 996 self._ty(store)?, 997 ), 998 } 999 } 1000 1001 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 1002 self.wasm_ty_store(store, ptr, ValRaw::anyref) 1003 } 1004 1005 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 1006 Self::wasm_ty_load(store, ptr.get_anyref(), ArrayRef::from_cloned_gc_ref) 1007 } 1008 } 1009 1010 unsafe impl WasmTy for Option<ManuallyRooted<ArrayRef>> { 1011 #[inline] 1012 fn valtype() -> ValType { 1013 ValType::ARRAYREF 1014 } 1015 1016 #[inline] 1017 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 1018 self.as_ref() 1019 .map_or(true, |x| x.comes_from_same_store(store)) 1020 } 1021 1022 #[inline] 1023 fn dynamic_concrete_type_check( 1024 &self, 1025 store: &StoreOpaque, 1026 nullable: bool, 1027 ty: &HeapType, 1028 ) -> Result<()> { 1029 match self { 1030 Some(s) => { 1031 ManuallyRooted::<ArrayRef>::dynamic_concrete_type_check(s, store, nullable, ty) 1032 } 1033 None => { 1034 ensure!( 1035 nullable, 1036 "expected a non-null reference, but found a null reference" 1037 ); 1038 Ok(()) 1039 } 1040 } 1041 } 1042 1043 #[inline] 1044 fn is_vmgcref_and_points_to_object(&self) -> bool { 1045 self.is_some() 1046 } 1047 1048 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 1049 <ManuallyRooted<ArrayRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref) 1050 } 1051 1052 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 1053 <ManuallyRooted<ArrayRef>>::wasm_ty_option_load( 1054 store, 1055 ptr.get_anyref(), 1056 ArrayRef::from_cloned_gc_ref, 1057 ) 1058 } 1059 } 1060