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