1 //! Working with GC `array` objects. 2 3 use crate::runtime::vm::VMGcRef; 4 use crate::store::{Asyncness, StoreId, StoreResourceLimiter}; 5 #[cfg(feature = "async")] 6 use crate::vm::VMStore; 7 use crate::vm::{self, VMArrayRef, VMGcHeader}; 8 use crate::{AnyRef, FieldType}; 9 use 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 }; 15 use core::mem::{self, MaybeUninit}; 16 use 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 /// ``` 65 pub struct ArrayRefPre { 66 store_id: StoreId, 67 ty: ArrayType, 68 } 69 70 impl 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)] 183 pub struct ArrayRef { 184 pub(super) inner: GcRootIndex, 185 } 186 187 unsafe 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 204 impl 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 218 impl 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)] 237 struct RepeatN<'a>(&'a Val, u32); 238 239 impl<'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 257 impl ExactSizeIterator for RepeatN<'_> { 258 fn len(&self) -> usize { 259 usize::try_from(self.1).unwrap() 260 } 261 } 262 263 impl 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 // Type check the elements against the element type. 397 for elem in elems.clone() { 398 elem.ensure_matches_ty(store, allocator.ty.element_type().unpack()) 399 .context("element type mismatch")?; 400 } 401 402 let len = u32::try_from(elems.len()).unwrap(); 403 404 // Allocate the array and write each field value into the appropriate 405 // offset. 406 let arrayref = store 407 .require_gc_store_mut()? 408 .alloc_uninit_array(allocator.type_index(), len, allocator.layout()) 409 .context("unrecoverable error when allocating new `arrayref`")? 410 .map_err(|n| GcHeapOutOfMemory::new((), n))?; 411 412 // From this point on, if we get any errors, then the array is not 413 // fully initialized, so we need to eagerly deallocate it before the 414 // next GC where the collector might try to interpret one of the 415 // uninitialized fields as a GC reference. 416 let mut store = AutoAssertNoGc::new(store); 417 match (|| { 418 let elem_ty = allocator.ty.element_type(); 419 for (i, elem) in elems.enumerate() { 420 let i = u32::try_from(i).unwrap(); 421 debug_assert!(i < len); 422 arrayref.initialize_elem(&mut store, allocator.layout(), &elem_ty, i, *elem)?; 423 } 424 Ok(()) 425 })() { 426 Ok(()) => Ok(Rooted::new(&mut store, arrayref.into())), 427 Err(e) => { 428 store.require_gc_store_mut()?.dealloc_uninit_array(arrayref); 429 Err(e) 430 } 431 } 432 } 433 434 /// Synchronously allocate a new `array` containing the given elements. 435 /// 436 /// For example, `ArrayRef::new_fixed(ctx, pre, &[Val::I64(4), Val::I64(5), 437 /// Val::I64(6)])` allocates the array `[4, 5, 6]`. 438 /// 439 /// This is similar to the `array.new_fixed` instruction. 440 /// 441 /// # Automatic Garbage Collection 442 /// 443 /// If the GC heap is at capacity, and there isn't room for allocating this 444 /// new array, then this method will automatically trigger a synchronous 445 /// collection in an attempt to free up space in the GC heap. 446 /// 447 /// # Errors 448 /// 449 /// If any of the `elems` values' type does not match the `allocator`'s 450 /// array type's element type, an error is returned. 451 /// 452 /// If the allocation cannot be satisfied because the GC heap is currently 453 /// out of memory, then a [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory] 454 /// error is returned. The allocation might succeed on a second attempt if 455 /// you drop some rooted GC references and try again. 456 /// 457 /// If `store` is configured with a 458 /// [`ResourceLimiterAsync`](crate::ResourceLimiterAsync) then an error 459 /// will be returned because [`ArrayRef::new_fixed_async`] should be used 460 /// instead. 461 /// 462 /// # Panics 463 /// 464 /// Panics if the allocator or any of the `elems` values are not associated 465 /// with the given store. 466 pub fn new_fixed( 467 mut store: impl AsContextMut, 468 allocator: &ArrayRefPre, 469 elems: &[Val], 470 ) -> Result<Rooted<ArrayRef>> { 471 let (mut limiter, store) = store 472 .as_context_mut() 473 .0 474 .validate_sync_resource_limiter_and_store_opaque()?; 475 vm::assert_ready(Self::_new_fixed_async( 476 store, 477 limiter.as_mut(), 478 allocator, 479 elems, 480 Asyncness::No, 481 )) 482 } 483 484 /// Asynchronously allocate a new `array` containing the given elements. 485 /// 486 /// For example, `ArrayRef::new_fixed_async(ctx, pre, &[Val::I64(4), 487 /// Val::I64(5), Val::I64(6)])` allocates the array `[4, 5, 6]`. 488 /// 489 /// This is similar to the `array.new_fixed` instruction. 490 /// 491 /// If your engine is not configured for async, use 492 /// [`ArrayRef::new_fixed`][crate::ArrayRef::new_fixed] to perform 493 /// synchronous allocation. 494 /// 495 /// # Automatic Garbage Collection 496 /// 497 /// If the GC heap is at capacity, and there isn't room for allocating this 498 /// new array, then this method will automatically trigger a synchronous 499 /// collection in an attempt to free up space in the GC heap. 500 /// 501 /// # Errors 502 /// 503 /// If any of the `elems` values' type does not match the `allocator`'s 504 /// array type's element type, an error is returned. 505 /// 506 /// If the allocation cannot be satisfied because the GC heap is currently 507 /// out of memory, then a [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory] 508 /// error is returned. The allocation might succeed on a second attempt if 509 /// you drop some rooted GC references and try again. 510 /// 511 /// # Panics 512 /// 513 /// Panics if the `store` is not configured for async; use 514 /// [`ArrayRef::new_fixed`][crate::ArrayRef::new_fixed] to perform 515 /// synchronous allocation instead. 516 /// 517 /// Panics if the allocator or any of the `elems` values are not associated 518 /// with the given store. 519 #[cfg(feature = "async")] 520 pub async fn new_fixed_async( 521 mut store: impl AsContextMut, 522 allocator: &ArrayRefPre, 523 elems: &[Val], 524 ) -> Result<Rooted<ArrayRef>> { 525 let (mut limiter, store) = store.as_context_mut().0.resource_limiter_and_store_opaque(); 526 Self::_new_fixed_async(store, limiter.as_mut(), allocator, elems, Asyncness::Yes).await 527 } 528 529 pub(crate) async fn _new_fixed_async( 530 store: &mut StoreOpaque, 531 limiter: Option<&mut StoreResourceLimiter<'_>>, 532 allocator: &ArrayRefPre, 533 elems: &[Val], 534 asyncness: Asyncness, 535 ) -> Result<Rooted<ArrayRef>> { 536 store 537 .retry_after_gc_async(limiter, (), asyncness, |store, ()| { 538 Self::new_from_iter(store, allocator, elems.iter()) 539 }) 540 .await 541 } 542 543 #[inline] 544 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool { 545 self.inner.comes_from_same_store(store) 546 } 547 548 /// Get this `arrayref`'s type. 549 /// 550 /// # Errors 551 /// 552 /// Return an error if this reference has been unrooted. 553 /// 554 /// # Panics 555 /// 556 /// Panics if this reference is associated with a different store. 557 pub fn ty(&self, store: impl AsContext) -> Result<ArrayType> { 558 self._ty(store.as_context().0) 559 } 560 561 pub(crate) fn _ty(&self, store: &StoreOpaque) -> Result<ArrayType> { 562 assert!(self.comes_from_same_store(store)); 563 let index = self.type_index(store)?; 564 Ok(ArrayType::from_shared_type_index(store.engine(), index)) 565 } 566 567 /// Does this `arrayref` match the given type? 568 /// 569 /// That is, is this array's type a subtype of the given type? 570 /// 571 /// # Errors 572 /// 573 /// Return an error if this reference has been unrooted. 574 /// 575 /// # Panics 576 /// 577 /// Panics if this reference is associated with a different store or if the 578 /// type is not associated with the store's engine. 579 pub fn matches_ty(&self, store: impl AsContext, ty: &ArrayType) -> Result<bool> { 580 self._matches_ty(store.as_context().0, ty) 581 } 582 583 pub(crate) fn _matches_ty(&self, store: &StoreOpaque, ty: &ArrayType) -> Result<bool> { 584 assert!(self.comes_from_same_store(store)); 585 Ok(self._ty(store)?.matches(ty)) 586 } 587 588 pub(crate) fn ensure_matches_ty(&self, store: &StoreOpaque, ty: &ArrayType) -> Result<()> { 589 if !self.comes_from_same_store(store) { 590 bail!("function used with wrong store"); 591 } 592 if self._matches_ty(store, ty)? { 593 Ok(()) 594 } else { 595 let actual_ty = self._ty(store)?; 596 bail!("type mismatch: expected `(ref {ty})`, found `(ref {actual_ty})`") 597 } 598 } 599 600 /// Get the length of this array. 601 /// 602 /// # Errors 603 /// 604 /// Return an error if this reference has been unrooted. 605 /// 606 /// # Panics 607 /// 608 /// Panics if this reference is associated with a different store. 609 pub fn len(&self, store: impl AsContext) -> Result<u32> { 610 self._len(store.as_context().0) 611 } 612 613 pub(crate) fn _len(&self, store: &StoreOpaque) -> Result<u32> { 614 assert!(self.comes_from_same_store(store)); 615 let gc_ref = self.inner.try_gc_ref(store)?; 616 debug_assert!({ 617 let header = store.require_gc_store()?.header(gc_ref); 618 header.kind().matches(VMGcKind::ArrayRef) 619 }); 620 let arrayref = gc_ref.as_arrayref_unchecked(); 621 Ok(arrayref.len(store)) 622 } 623 624 /// Get the values of this array's elements. 625 /// 626 /// Note that `i8` and `i16` element values are zero-extended into 627 /// `Val::I32(_)`s. 628 /// 629 /// # Errors 630 /// 631 /// Return an error if this reference has been unrooted. 632 /// 633 /// # Panics 634 /// 635 /// Panics if this reference is associated with a different store. 636 pub fn elems<'a, T: 'static>( 637 &'a self, 638 store: impl Into<StoreContextMut<'a, T>>, 639 ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> { 640 self._elems(store.into().0) 641 } 642 643 pub(crate) fn _elems<'a>( 644 &'a self, 645 store: &'a mut StoreOpaque, 646 ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> { 647 assert!(self.comes_from_same_store(store)); 648 let store = AutoAssertNoGc::new(store); 649 650 let gc_ref = self.inner.try_gc_ref(&store)?; 651 let header = store.require_gc_store()?.header(gc_ref); 652 debug_assert!(header.kind().matches(VMGcKind::ArrayRef)); 653 654 let len = self._len(&store)?; 655 656 return Ok(Elems { 657 arrayref: self, 658 store, 659 index: 0, 660 len, 661 }); 662 663 struct Elems<'a, 'b> { 664 arrayref: &'a ArrayRef, 665 store: AutoAssertNoGc<'b>, 666 index: u32, 667 len: u32, 668 } 669 670 impl Iterator for Elems<'_, '_> { 671 type Item = Val; 672 673 #[inline] 674 fn next(&mut self) -> Option<Self::Item> { 675 let i = self.index; 676 debug_assert!(i <= self.len); 677 if i >= self.len { 678 return None; 679 } 680 self.index += 1; 681 Some(self.arrayref._get(&mut self.store, i).unwrap()) 682 } 683 684 #[inline] 685 fn size_hint(&self) -> (usize, Option<usize>) { 686 let len = self.len - self.index; 687 let len = usize::try_from(len).unwrap(); 688 (len, Some(len)) 689 } 690 } 691 692 impl ExactSizeIterator for Elems<'_, '_> { 693 #[inline] 694 fn len(&self) -> usize { 695 let len = self.len - self.index; 696 usize::try_from(len).unwrap() 697 } 698 } 699 } 700 701 fn header<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMGcHeader> { 702 assert!(self.comes_from_same_store(&store)); 703 let gc_ref = self.inner.try_gc_ref(store)?; 704 Ok(store.require_gc_store()?.header(gc_ref)) 705 } 706 707 fn arrayref<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMArrayRef> { 708 assert!(self.comes_from_same_store(&store)); 709 let gc_ref = self.inner.try_gc_ref(store)?; 710 debug_assert!(self.header(store)?.kind().matches(VMGcKind::ArrayRef)); 711 Ok(gc_ref.as_arrayref_unchecked()) 712 } 713 714 pub(crate) fn layout(&self, store: &AutoAssertNoGc<'_>) -> Result<GcArrayLayout> { 715 assert!(self.comes_from_same_store(&store)); 716 let type_index = self.type_index(store)?; 717 let layout = store 718 .engine() 719 .signatures() 720 .layout(type_index) 721 .expect("array types should have GC layouts"); 722 match layout { 723 GcLayout::Array(a) => Ok(a), 724 GcLayout::Struct(_) => unreachable!(), 725 } 726 } 727 728 fn field_ty(&self, store: &StoreOpaque) -> Result<FieldType> { 729 let ty = self._ty(store)?; 730 Ok(ty.field_type()) 731 } 732 733 /// Get this array's `index`th element. 734 /// 735 /// Note that `i8` and `i16` field values are zero-extended into 736 /// `Val::I32(_)`s. 737 /// 738 /// # Errors 739 /// 740 /// Returns an `Err(_)` if the index is out of bounds or this reference has 741 /// been unrooted. 742 /// 743 /// # Panics 744 /// 745 /// Panics if this reference is associated with a different store. 746 pub fn get(&self, mut store: impl AsContextMut, index: u32) -> Result<Val> { 747 let mut store = AutoAssertNoGc::new(store.as_context_mut().0); 748 self._get(&mut store, index) 749 } 750 751 pub(crate) fn _get(&self, store: &mut AutoAssertNoGc<'_>, index: u32) -> Result<Val> { 752 assert!( 753 self.comes_from_same_store(store), 754 "attempted to use an array with the wrong store", 755 ); 756 let arrayref = self.arrayref(store)?.unchecked_copy(); 757 let field_ty = self.field_ty(store)?; 758 let layout = self.layout(store)?; 759 let len = arrayref.len(store); 760 ensure!( 761 index < len, 762 "index out of bounds: the length is {len} but the index is {index}" 763 ); 764 Ok(arrayref.read_elem(store, &layout, field_ty.element_type(), index)) 765 } 766 767 /// Set this array's `index`th element. 768 /// 769 /// # Errors 770 /// 771 /// Returns an error in the following scenarios: 772 /// 773 /// * When given a value of the wrong type, such as trying to write an `f32` 774 /// value into an array of `i64` elements. 775 /// 776 /// * When the array elements are not mutable. 777 /// 778 /// * When `index` is not within the range `0..self.len(ctx)`. 779 /// 780 /// * When `value` is a GC reference that has since been unrooted. 781 /// 782 /// # Panics 783 /// 784 /// Panics if either this reference or the given `value` is associated with 785 /// a different store. 786 pub fn set(&self, mut store: impl AsContextMut, index: u32, value: Val) -> Result<()> { 787 self._set(store.as_context_mut().0, index, value) 788 } 789 790 pub(crate) fn _set(&self, store: &mut StoreOpaque, index: u32, value: Val) -> Result<()> { 791 assert!( 792 self.comes_from_same_store(store), 793 "attempted to use an array with the wrong store", 794 ); 795 assert!( 796 value.comes_from_same_store(store), 797 "attempted to use a value with the wrong store", 798 ); 799 800 let mut store = AutoAssertNoGc::new(store); 801 802 let field_ty = self.field_ty(&store)?; 803 ensure!( 804 field_ty.mutability().is_var(), 805 "cannot set element {index}: array elements are not mutable" 806 ); 807 808 value 809 .ensure_matches_ty(&store, &field_ty.element_type().unpack()) 810 .with_context(|| format!("cannot set element {index}: type mismatch"))?; 811 812 let layout = self.layout(&store)?; 813 let arrayref = self.arrayref(&store)?.unchecked_copy(); 814 815 let len = arrayref.len(&store); 816 ensure!( 817 index < len, 818 "index out of bounds: the length is {len} but the index is {index}" 819 ); 820 821 arrayref.write_elem(&mut store, &layout, field_ty.element_type(), index, value) 822 } 823 824 pub(crate) fn type_index(&self, store: &StoreOpaque) -> Result<VMSharedTypeIndex> { 825 let gc_ref = self.inner.try_gc_ref(store)?; 826 let header = store.require_gc_store()?.header(gc_ref); 827 debug_assert!(header.kind().matches(VMGcKind::ArrayRef)); 828 Ok(header.ty().expect("arrayrefs should have concrete types")) 829 } 830 831 /// Create a new `Rooted<ArrayRef>` from the given GC reference. 832 /// 833 /// `gc_ref` should point to a valid `arrayref` and should belong to the 834 /// store's GC heap. Failure to uphold these invariants is memory safe but 835 /// will lead to general incorrectness such as panics or wrong results. 836 pub(crate) fn from_cloned_gc_ref( 837 store: &mut AutoAssertNoGc<'_>, 838 gc_ref: VMGcRef, 839 ) -> Rooted<Self> { 840 debug_assert!(gc_ref.is_arrayref(&*store.unwrap_gc_store().gc_heap)); 841 Rooted::new(store, gc_ref) 842 } 843 } 844 845 unsafe impl WasmTy for Rooted<ArrayRef> { 846 #[inline] 847 fn valtype() -> ValType { 848 ValType::Ref(RefType::new(false, HeapType::Array)) 849 } 850 851 #[inline] 852 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 853 self.comes_from_same_store(store) 854 } 855 856 #[inline] 857 fn dynamic_concrete_type_check( 858 &self, 859 store: &StoreOpaque, 860 _nullable: bool, 861 ty: &HeapType, 862 ) -> Result<()> { 863 match ty { 864 HeapType::Any | HeapType::Eq | HeapType::Array => Ok(()), 865 HeapType::ConcreteArray(ty) => self.ensure_matches_ty(store, ty), 866 867 HeapType::Extern 868 | HeapType::NoExtern 869 | HeapType::Func 870 | HeapType::ConcreteFunc(_) 871 | HeapType::NoFunc 872 | HeapType::I31 873 | HeapType::Struct 874 | HeapType::ConcreteStruct(_) 875 | HeapType::Cont 876 | HeapType::NoCont 877 | HeapType::ConcreteCont(_) 878 | HeapType::Exn 879 | HeapType::NoExn 880 | HeapType::ConcreteExn(_) 881 | HeapType::None => bail!( 882 "type mismatch: expected `(ref {ty})`, got `(ref {})`", 883 self._ty(store)?, 884 ), 885 } 886 } 887 888 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 889 self.wasm_ty_store(store, ptr, ValRaw::anyref) 890 } 891 892 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 893 Self::wasm_ty_load(store, ptr.get_anyref(), ArrayRef::from_cloned_gc_ref) 894 } 895 } 896 897 unsafe impl WasmTy for Option<Rooted<ArrayRef>> { 898 #[inline] 899 fn valtype() -> ValType { 900 ValType::ARRAYREF 901 } 902 903 #[inline] 904 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 905 self.map_or(true, |x| x.comes_from_same_store(store)) 906 } 907 908 #[inline] 909 fn dynamic_concrete_type_check( 910 &self, 911 store: &StoreOpaque, 912 nullable: bool, 913 ty: &HeapType, 914 ) -> Result<()> { 915 match self { 916 Some(s) => Rooted::<ArrayRef>::dynamic_concrete_type_check(s, store, nullable, ty), 917 None => { 918 ensure!( 919 nullable, 920 "expected a non-null reference, but found a null reference" 921 ); 922 Ok(()) 923 } 924 } 925 } 926 927 #[inline] 928 fn is_vmgcref_and_points_to_object(&self) -> bool { 929 self.is_some() 930 } 931 932 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 933 <Rooted<ArrayRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref) 934 } 935 936 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 937 <Rooted<ArrayRef>>::wasm_ty_option_load( 938 store, 939 ptr.get_anyref(), 940 ArrayRef::from_cloned_gc_ref, 941 ) 942 } 943 } 944 945 unsafe impl WasmTy for OwnedRooted<ArrayRef> { 946 #[inline] 947 fn valtype() -> ValType { 948 ValType::Ref(RefType::new(false, HeapType::Array)) 949 } 950 951 #[inline] 952 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 953 self.comes_from_same_store(store) 954 } 955 956 #[inline] 957 fn dynamic_concrete_type_check( 958 &self, 959 store: &StoreOpaque, 960 _: bool, 961 ty: &HeapType, 962 ) -> Result<()> { 963 match ty { 964 HeapType::Any | HeapType::Eq | HeapType::Array => Ok(()), 965 HeapType::ConcreteArray(ty) => self.ensure_matches_ty(store, ty), 966 967 HeapType::Extern 968 | HeapType::NoExtern 969 | HeapType::Func 970 | HeapType::ConcreteFunc(_) 971 | HeapType::NoFunc 972 | HeapType::I31 973 | HeapType::Struct 974 | HeapType::ConcreteStruct(_) 975 | HeapType::Cont 976 | HeapType::NoCont 977 | HeapType::ConcreteCont(_) 978 | HeapType::Exn 979 | HeapType::NoExn 980 | HeapType::ConcreteExn(_) 981 | HeapType::None => bail!( 982 "type mismatch: expected `(ref {ty})`, got `(ref {})`", 983 self._ty(store)?, 984 ), 985 } 986 } 987 988 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 989 self.wasm_ty_store(store, ptr, ValRaw::anyref) 990 } 991 992 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 993 Self::wasm_ty_load(store, ptr.get_anyref(), ArrayRef::from_cloned_gc_ref) 994 } 995 } 996 997 unsafe impl WasmTy for Option<OwnedRooted<ArrayRef>> { 998 #[inline] 999 fn valtype() -> ValType { 1000 ValType::ARRAYREF 1001 } 1002 1003 #[inline] 1004 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 1005 self.as_ref() 1006 .map_or(true, |x| x.comes_from_same_store(store)) 1007 } 1008 1009 #[inline] 1010 fn dynamic_concrete_type_check( 1011 &self, 1012 store: &StoreOpaque, 1013 nullable: bool, 1014 ty: &HeapType, 1015 ) -> Result<()> { 1016 match self { 1017 Some(s) => OwnedRooted::<ArrayRef>::dynamic_concrete_type_check(s, store, nullable, ty), 1018 None => { 1019 ensure!( 1020 nullable, 1021 "expected a non-null reference, but found a null reference" 1022 ); 1023 Ok(()) 1024 } 1025 } 1026 } 1027 1028 #[inline] 1029 fn is_vmgcref_and_points_to_object(&self) -> bool { 1030 self.is_some() 1031 } 1032 1033 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 1034 <OwnedRooted<ArrayRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref) 1035 } 1036 1037 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 1038 <OwnedRooted<ArrayRef>>::wasm_ty_option_load( 1039 store, 1040 ptr.get_anyref(), 1041 ArrayRef::from_cloned_gc_ref, 1042 ) 1043 } 1044 } 1045