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::{ 7 prelude::*, 8 store::{AutoAssertNoGc, StoreContextMut, StoreOpaque}, 9 ArrayType, AsContext, AsContextMut, EqRef, GcHeapOutOfMemory, GcRefImpl, GcRootIndex, HeapType, 10 ManuallyRooted, RefType, Rooted, Val, ValRaw, ValType, WasmTy, 11 }; 12 use crate::{AnyRef, FieldType}; 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 /// # foo().unwrap(); 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 /// 146 /// // If the heap is out of memory, then do a GC to free up some space 147 /// // and try again. 148 /// Err(e) if e.is::<GcHeapOutOfMemory<()>>() => { 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(); 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 /// 159 /// // Propagate any other kind of error. 160 /// Err(e) => return Err(e), 161 /// }; 162 /// 163 /// // That instance's elements should have the initial value. 164 /// for i in 0..len { 165 /// let val = my_array.get(&mut scope, i)?.unwrap_i32(); 166 /// assert_eq!(val, 42); 167 /// } 168 /// 169 /// // We can set an element to a new value because the type was defined with 170 /// // mutable elements (as opposed to const). 171 /// my_array.set(&mut scope, 3, Val::I32(1234))?; 172 /// let new_val = my_array.get(&mut scope, 3)?.unwrap_i32(); 173 /// assert_eq!(new_val, 1234); 174 /// } 175 /// # Ok(()) 176 /// # } 177 /// # foo().unwrap(); 178 /// ``` 179 #[derive(Debug)] 180 #[repr(transparent)] 181 pub struct ArrayRef { 182 pub(super) inner: GcRootIndex, 183 } 184 185 unsafe impl GcRefImpl for ArrayRef { 186 #[allow(private_interfaces)] 187 fn transmute_ref(index: &GcRootIndex) -> &Self { 188 // Safety: `ArrayRef` is a newtype of a `GcRootIndex`. 189 let me: &Self = unsafe { mem::transmute(index) }; 190 191 // Assert we really are just a newtype of a `GcRootIndex`. 192 assert!(matches!( 193 me, 194 Self { 195 inner: GcRootIndex { .. }, 196 } 197 )); 198 199 me 200 } 201 } 202 203 impl Rooted<ArrayRef> { 204 /// Upcast this `arrayref` into an `anyref`. 205 #[inline] 206 pub fn to_anyref(self) -> Rooted<AnyRef> { 207 self.unchecked_cast() 208 } 209 210 /// Upcast this `arrayref` into an `eqref`. 211 #[inline] 212 pub fn to_eqref(self) -> Rooted<EqRef> { 213 self.unchecked_cast() 214 } 215 } 216 217 impl ManuallyRooted<ArrayRef> { 218 /// Upcast this `arrayref` into an `anyref`. 219 #[inline] 220 pub fn to_anyref(self) -> ManuallyRooted<AnyRef> { 221 self.unchecked_cast() 222 } 223 224 /// Upcast this `arrayref` into an `eqref`. 225 #[inline] 226 pub fn to_eqref(self) -> ManuallyRooted<EqRef> { 227 self.unchecked_cast() 228 } 229 } 230 231 impl ArrayRef { 232 /// Allocate a new `array` of the given length, with every element 233 /// initialized to `elem`. 234 /// 235 /// For example, `ArrayRef::new(ctx, pre, &Val::I64(9), 3)` allocates the 236 /// array `[9, 9, 9]`. 237 /// 238 /// This is similar to the `array.new` instruction. 239 /// 240 /// # Errors 241 /// 242 /// If the given `elem` value's type does not match the `allocator`'s array 243 /// type's element type, an error is returned. 244 /// 245 /// If the allocation cannot be satisfied because the GC heap is currently 246 /// out of memory, but performing a garbage collection might free up space 247 /// such that retrying the allocation afterwards might succeed, then a 248 /// [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory] error is returned. 249 /// 250 /// # Panics 251 /// 252 /// Panics if either the allocator or the `elem` value is not associated 253 /// with the given store. 254 pub fn new( 255 mut store: impl AsContextMut, 256 allocator: &ArrayRefPre, 257 elem: &Val, 258 len: u32, 259 ) -> Result<Rooted<ArrayRef>> { 260 Self::_new(store.as_context_mut().0, allocator, elem, len) 261 } 262 263 pub(crate) fn _new( 264 store: &mut StoreOpaque, 265 allocator: &ArrayRefPre, 266 elem: &Val, 267 len: u32, 268 ) -> Result<Rooted<ArrayRef>> { 269 assert_eq!( 270 store.id(), 271 allocator.store_id, 272 "attempted to use a `ArrayRefPre` with the wrong store" 273 ); 274 275 // Type check the initial element value against the element type. 276 elem.ensure_matches_ty(store, allocator.ty.element_type().unpack()) 277 .context("element type mismatch")?; 278 279 return Self::_new_unchecked(store, allocator, RepeatN(elem, len)); 280 281 // NB: Can't use `iter::repeat(elem).take(len)` above because that 282 // doesn't implement `ExactSizeIterator`. 283 struct RepeatN<'a>(&'a Val, u32); 284 285 impl<'a> Iterator for RepeatN<'a> { 286 type Item = &'a Val; 287 288 fn next(&mut self) -> Option<Self::Item> { 289 if self.1 == 0 { 290 None 291 } else { 292 self.1 -= 1; 293 Some(self.0) 294 } 295 } 296 297 fn size_hint(&self) -> (usize, Option<usize>) { 298 let len = self.len(); 299 (len, Some(len)) 300 } 301 } 302 303 impl ExactSizeIterator for RepeatN<'_> { 304 fn len(&self) -> usize { 305 usize::try_from(self.1).unwrap() 306 } 307 } 308 } 309 310 /// Allocate a new array of the given elements, without checking that the 311 /// elements' types match the array's element type. 312 fn _new_unchecked<'a>( 313 store: &mut StoreOpaque, 314 allocator: &ArrayRefPre, 315 elems: impl ExactSizeIterator<Item = &'a Val>, 316 ) -> Result<Rooted<ArrayRef>> { 317 let len = u32::try_from(elems.len()).unwrap(); 318 319 // Allocate the array and write each field value into the appropriate 320 // offset. 321 let arrayref = store 322 .gc_store_mut()? 323 .alloc_uninit_array(allocator.type_index(), len, allocator.layout()) 324 .err2anyhow() 325 .context("unrecoverable error when allocating new `arrayref`")? 326 .ok_or_else(|| GcHeapOutOfMemory::new(())) 327 .err2anyhow()?; 328 329 // From this point on, if we get any errors, then the array is not 330 // fully initialized, so we need to eagerly deallocate it before the 331 // next GC where the collector might try to interpret one of the 332 // uninitialized fields as a GC reference. 333 let mut store = AutoAssertNoGc::new(store); 334 match (|| { 335 let elem_ty = allocator.ty.element_type(); 336 for (i, elem) in elems.enumerate() { 337 let i = u32::try_from(i).unwrap(); 338 debug_assert!(i < len); 339 arrayref.initialize_elem(&mut store, allocator.layout(), &elem_ty, i, *elem)?; 340 } 341 Ok(()) 342 })() { 343 Ok(()) => Ok(Rooted::new(&mut store, arrayref.into())), 344 Err(e) => { 345 store.gc_store_mut()?.dealloc_uninit_array(arrayref); 346 Err(e) 347 } 348 } 349 } 350 351 /// Allocate a new `array` containing the given elements. 352 /// 353 /// For example, `ArrayRef::new_fixed(ctx, pre, &[Val::I64(4), Val::I64(5), 354 /// Val::I64(6)])` allocates the array `[4, 5, 6]`. 355 /// 356 /// This is similar to the `array.new_fixed` instruction. 357 /// 358 /// # Errors 359 /// 360 /// If any of the `elems` values' type does not match the `allocator`'s 361 /// array type's element type, an error is returned. 362 /// 363 /// If the allocation cannot be satisfied because the GC heap is currently 364 /// out of memory, but performing a garbage collection might free up space 365 /// such that retrying the allocation afterwards might succeed, then a 366 /// [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory] error is returned. 367 /// 368 /// # Panics 369 /// 370 /// Panics if the allocator or any of the `elems` values are not associated 371 /// with the given store. 372 pub fn new_fixed( 373 mut store: impl AsContextMut, 374 allocator: &ArrayRefPre, 375 elems: &[Val], 376 ) -> Result<Rooted<ArrayRef>> { 377 Self::_new_fixed(store.as_context_mut().0, allocator, elems) 378 } 379 380 pub(crate) fn _new_fixed( 381 store: &mut StoreOpaque, 382 allocator: &ArrayRefPre, 383 elems: &[Val], 384 ) -> Result<Rooted<ArrayRef>> { 385 assert_eq!( 386 store.id(), 387 allocator.store_id, 388 "attempted to use a `ArrayRefPre` with the wrong store" 389 ); 390 391 // Type check the elements against the element type. 392 for elem in elems { 393 elem.ensure_matches_ty(store, allocator.ty.element_type().unpack()) 394 .context("element type mismatch")?; 395 } 396 397 return Self::_new_unchecked(store, allocator, elems.iter()); 398 } 399 400 #[inline] 401 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool { 402 self.inner.comes_from_same_store(store) 403 } 404 405 /// Get this `arrayref`'s type. 406 /// 407 /// # Errors 408 /// 409 /// Return an error if this reference has been unrooted. 410 /// 411 /// # Panics 412 /// 413 /// Panics if this reference is associated with a different store. 414 pub fn ty(&self, store: impl AsContext) -> Result<ArrayType> { 415 self._ty(store.as_context().0) 416 } 417 418 pub(crate) fn _ty(&self, store: &StoreOpaque) -> Result<ArrayType> { 419 assert!(self.comes_from_same_store(store)); 420 let index = self.type_index(store)?; 421 Ok(ArrayType::from_shared_type_index(store.engine(), index)) 422 } 423 424 /// Does this `arrayref` match the given type? 425 /// 426 /// That is, is this array's type a subtype of the given type? 427 /// 428 /// # Errors 429 /// 430 /// Return an error if this reference has been unrooted. 431 /// 432 /// # Panics 433 /// 434 /// Panics if this reference is associated with a different store or if the 435 /// type is not associated with the store's engine. 436 pub fn matches_ty(&self, store: impl AsContext, ty: &ArrayType) -> Result<bool> { 437 self._matches_ty(store.as_context().0, ty) 438 } 439 440 pub(crate) fn _matches_ty(&self, store: &StoreOpaque, ty: &ArrayType) -> Result<bool> { 441 assert!(self.comes_from_same_store(store)); 442 Ok(self._ty(store)?.matches(ty)) 443 } 444 445 pub(crate) fn ensure_matches_ty(&self, store: &StoreOpaque, ty: &ArrayType) -> Result<()> { 446 if !self.comes_from_same_store(store) { 447 bail!("function used with wrong store"); 448 } 449 if self._matches_ty(store, ty)? { 450 Ok(()) 451 } else { 452 let actual_ty = self._ty(store)?; 453 bail!("type mismatch: expected `(ref {ty})`, found `(ref {actual_ty})`") 454 } 455 } 456 457 /// Get the length of this array. 458 /// 459 /// # Errors 460 /// 461 /// Return an error if this reference has been unrooted. 462 /// 463 /// # Panics 464 /// 465 /// Panics if this reference is associated with a different store. 466 pub fn len(&self, store: impl AsContext) -> Result<u32> { 467 self._len(store.as_context().0) 468 } 469 470 pub(crate) fn _len(&self, store: &StoreOpaque) -> Result<u32> { 471 assert!(self.comes_from_same_store(store)); 472 let gc_ref = self.inner.try_gc_ref(store)?; 473 debug_assert!({ 474 let header = store.gc_store()?.header(gc_ref); 475 header.kind().matches(VMGcKind::ArrayRef) 476 }); 477 let arrayref = gc_ref.as_arrayref_unchecked(); 478 Ok(arrayref.len(store)) 479 } 480 481 /// Get the values of this array's elements. 482 /// 483 /// Note that `i8` and `i16` field values are zero-extended into 484 /// `Val::I32(_)`s. 485 /// 486 /// # Errors 487 /// 488 /// Return an error if this reference has been unrooted. 489 /// 490 /// # Panics 491 /// 492 /// Panics if this reference is associated with a different store. 493 pub fn elems<'a, T: 'a>( 494 &'a self, 495 store: impl Into<StoreContextMut<'a, T>>, 496 ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> { 497 self._elems(store.into().0) 498 } 499 500 pub(crate) fn _elems<'a>( 501 &'a self, 502 store: &'a mut StoreOpaque, 503 ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> { 504 assert!(self.comes_from_same_store(store)); 505 let store = AutoAssertNoGc::new(store); 506 507 let gc_ref = self.inner.try_gc_ref(&store)?; 508 let header = store.gc_store()?.header(gc_ref); 509 debug_assert!(header.kind().matches(VMGcKind::ArrayRef)); 510 511 let len = self._len(&store)?; 512 513 return Ok(Elems { 514 arrayref: self, 515 store, 516 index: 0, 517 len, 518 }); 519 520 struct Elems<'a, 'b> { 521 arrayref: &'a ArrayRef, 522 store: AutoAssertNoGc<'b>, 523 index: u32, 524 len: u32, 525 } 526 527 impl Iterator for Elems<'_, '_> { 528 type Item = Val; 529 530 #[inline] 531 fn next(&mut self) -> Option<Self::Item> { 532 let i = self.index; 533 debug_assert!(i <= self.len); 534 if i >= self.len { 535 return None; 536 } 537 self.index += 1; 538 Some(self.arrayref._get(&mut self.store, i).unwrap()) 539 } 540 541 #[inline] 542 fn size_hint(&self) -> (usize, Option<usize>) { 543 let len = self.len - self.index; 544 let len = usize::try_from(len).unwrap(); 545 (len, Some(len)) 546 } 547 } 548 549 impl ExactSizeIterator for Elems<'_, '_> { 550 #[inline] 551 fn len(&self) -> usize { 552 let len = self.len - self.index; 553 usize::try_from(len).unwrap() 554 } 555 } 556 } 557 558 fn header<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMGcHeader> { 559 assert!(self.comes_from_same_store(&store)); 560 let gc_ref = self.inner.try_gc_ref(store)?; 561 Ok(store.gc_store()?.header(gc_ref)) 562 } 563 564 fn arrayref<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMArrayRef> { 565 assert!(self.comes_from_same_store(&store)); 566 let gc_ref = self.inner.try_gc_ref(store)?; 567 debug_assert!(self.header(store)?.kind().matches(VMGcKind::ArrayRef)); 568 Ok(gc_ref.as_arrayref_unchecked()) 569 } 570 571 fn layout(&self, store: &AutoAssertNoGc<'_>) -> Result<GcArrayLayout> { 572 assert!(self.comes_from_same_store(&store)); 573 let type_index = self.type_index(store)?; 574 let layout = store 575 .engine() 576 .signatures() 577 .layout(type_index) 578 .expect("array types should have GC layouts"); 579 match layout { 580 GcLayout::Array(a) => Ok(a), 581 GcLayout::Struct(_) => unreachable!(), 582 } 583 } 584 585 fn field_ty(&self, store: &StoreOpaque) -> Result<FieldType> { 586 let ty = self._ty(store)?; 587 Ok(ty.field_type()) 588 } 589 590 /// Get this array's `index`th element. 591 /// 592 /// Note that `i8` and `i16` field values are zero-extended into 593 /// `Val::I32(_)`s. 594 /// 595 /// # Errors 596 /// 597 /// Returns an `Err(_)` if the index is out of bounds or this reference has 598 /// been unrooted. 599 /// 600 /// # Panics 601 /// 602 /// Panics if this reference is associated with a different store. 603 pub fn get(&self, mut store: impl AsContextMut, index: u32) -> Result<Val> { 604 let mut store = AutoAssertNoGc::new(store.as_context_mut().0); 605 self._get(&mut store, index) 606 } 607 608 pub(crate) fn _get(&self, store: &mut AutoAssertNoGc<'_>, index: u32) -> Result<Val> { 609 assert!( 610 self.comes_from_same_store(store), 611 "attempted to use an array with the wrong store", 612 ); 613 let arrayref = self.arrayref(store)?.unchecked_copy(); 614 let field_ty = self.field_ty(store)?; 615 let layout = self.layout(store)?; 616 let len = arrayref.len(store); 617 ensure!( 618 index < len, 619 "index out of bounds: the length is {len} but the index is {index}" 620 ); 621 Ok(arrayref.read_elem(store, &layout, field_ty.element_type(), index)) 622 } 623 624 /// Set this array's `index`th element. 625 /// 626 /// # Errors 627 /// 628 /// Returns an error in the following scenarios: 629 /// 630 /// * When given a value of the wrong type, such as trying to write an `f32` 631 /// value into an array of `i64` elements. 632 /// 633 /// * When the array elements are not mutable. 634 /// 635 /// * When `index` is not within the range `0..self.len(ctx)`. 636 /// 637 /// * When `value` is a GC reference that has since been unrooted. 638 /// 639 /// # Panics 640 /// 641 /// Panics if either this reference or the given `value` is associated with 642 /// a different store. 643 pub fn set(&self, mut store: impl AsContextMut, index: u32, value: Val) -> Result<()> { 644 self._set(store.as_context_mut().0, index, value) 645 } 646 647 pub(crate) fn _set(&self, store: &mut StoreOpaque, index: u32, value: Val) -> Result<()> { 648 assert!( 649 self.comes_from_same_store(store), 650 "attempted to use an array with the wrong store", 651 ); 652 assert!( 653 value.comes_from_same_store(store), 654 "attempted to use a value with the wrong store", 655 ); 656 657 let mut store = AutoAssertNoGc::new(store); 658 659 let field_ty = self.field_ty(&store)?; 660 ensure!( 661 field_ty.mutability().is_var(), 662 "cannot set element {index}: array elements are not mutable" 663 ); 664 665 value 666 .ensure_matches_ty(&store, &field_ty.element_type().unpack()) 667 .with_context(|| format!("cannot set element {index}: type mismatch"))?; 668 669 let layout = self.layout(&store)?; 670 let arrayref = self.arrayref(&store)?.unchecked_copy(); 671 672 let len = arrayref.len(&store); 673 ensure!( 674 index < len, 675 "index out of bounds: the length is {len} but the index is {index}" 676 ); 677 678 arrayref.write_elem(&mut store, &layout, field_ty.element_type(), index, value) 679 } 680 681 pub(crate) fn type_index(&self, store: &StoreOpaque) -> Result<VMSharedTypeIndex> { 682 let gc_ref = self.inner.try_gc_ref(store)?; 683 let header = store.gc_store()?.header(gc_ref); 684 debug_assert!(header.kind().matches(VMGcKind::ArrayRef)); 685 Ok(header.ty().expect("arrayrefs should have concrete types")) 686 } 687 688 /// Create a new `Rooted<ArrayRef>` from the given GC reference. 689 /// 690 /// `gc_ref` should point to a valid `arrayref` and should belong to the 691 /// store's GC heap. Failure to uphold these invariants is memory safe but 692 /// will lead to general incorrectness such as panics or wrong results. 693 pub(crate) fn from_cloned_gc_ref( 694 store: &mut AutoAssertNoGc<'_>, 695 gc_ref: VMGcRef, 696 ) -> Rooted<Self> { 697 debug_assert!(gc_ref.is_arrayref(&*store.unwrap_gc_store().gc_heap)); 698 Rooted::new(store, gc_ref) 699 } 700 } 701 702 unsafe impl WasmTy for Rooted<ArrayRef> { 703 #[inline] 704 fn valtype() -> ValType { 705 ValType::Ref(RefType::new(false, HeapType::Array)) 706 } 707 708 #[inline] 709 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 710 self.comes_from_same_store(store) 711 } 712 713 #[inline] 714 fn dynamic_concrete_type_check( 715 &self, 716 store: &StoreOpaque, 717 _nullable: bool, 718 ty: &HeapType, 719 ) -> Result<()> { 720 match ty { 721 HeapType::Any | HeapType::Eq | HeapType::Array => Ok(()), 722 HeapType::ConcreteArray(ty) => self.ensure_matches_ty(store, ty), 723 724 HeapType::Extern 725 | HeapType::NoExtern 726 | HeapType::Func 727 | HeapType::ConcreteFunc(_) 728 | HeapType::NoFunc 729 | HeapType::I31 730 | HeapType::Struct 731 | HeapType::ConcreteStruct(_) 732 | HeapType::None => bail!( 733 "type mismatch: expected `(ref {ty})`, got `(ref {})`", 734 self._ty(store)?, 735 ), 736 } 737 } 738 739 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 740 self.wasm_ty_store(store, ptr, ValRaw::anyref) 741 } 742 743 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 744 Self::wasm_ty_load(store, ptr.get_anyref(), ArrayRef::from_cloned_gc_ref) 745 } 746 } 747 748 unsafe impl WasmTy for Option<Rooted<ArrayRef>> { 749 #[inline] 750 fn valtype() -> ValType { 751 ValType::ARRAYREF 752 } 753 754 #[inline] 755 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 756 self.map_or(true, |x| x.comes_from_same_store(store)) 757 } 758 759 #[inline] 760 fn dynamic_concrete_type_check( 761 &self, 762 store: &StoreOpaque, 763 nullable: bool, 764 ty: &HeapType, 765 ) -> Result<()> { 766 match self { 767 Some(s) => Rooted::<ArrayRef>::dynamic_concrete_type_check(s, store, nullable, ty), 768 None => { 769 ensure!( 770 nullable, 771 "expected a non-null reference, but found a null reference" 772 ); 773 Ok(()) 774 } 775 } 776 } 777 778 #[inline] 779 fn is_vmgcref_and_points_to_object(&self) -> bool { 780 self.is_some() 781 } 782 783 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 784 <Rooted<ArrayRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref) 785 } 786 787 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 788 <Rooted<ArrayRef>>::wasm_ty_option_load( 789 store, 790 ptr.get_anyref(), 791 ArrayRef::from_cloned_gc_ref, 792 ) 793 } 794 } 795 796 unsafe impl WasmTy for ManuallyRooted<ArrayRef> { 797 #[inline] 798 fn valtype() -> ValType { 799 ValType::Ref(RefType::new(false, HeapType::Array)) 800 } 801 802 #[inline] 803 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 804 self.comes_from_same_store(store) 805 } 806 807 #[inline] 808 fn dynamic_concrete_type_check( 809 &self, 810 store: &StoreOpaque, 811 _: bool, 812 ty: &HeapType, 813 ) -> Result<()> { 814 match ty { 815 HeapType::Any | HeapType::Eq | HeapType::Array => Ok(()), 816 HeapType::ConcreteArray(ty) => self.ensure_matches_ty(store, ty), 817 818 HeapType::Extern 819 | HeapType::NoExtern 820 | HeapType::Func 821 | HeapType::ConcreteFunc(_) 822 | HeapType::NoFunc 823 | HeapType::I31 824 | HeapType::Struct 825 | HeapType::ConcreteStruct(_) 826 | HeapType::None => bail!( 827 "type mismatch: expected `(ref {ty})`, got `(ref {})`", 828 self._ty(store)?, 829 ), 830 } 831 } 832 833 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 834 self.wasm_ty_store(store, ptr, ValRaw::anyref) 835 } 836 837 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 838 Self::wasm_ty_load(store, ptr.get_anyref(), ArrayRef::from_cloned_gc_ref) 839 } 840 } 841 842 unsafe impl WasmTy for Option<ManuallyRooted<ArrayRef>> { 843 #[inline] 844 fn valtype() -> ValType { 845 ValType::ARRAYREF 846 } 847 848 #[inline] 849 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 850 self.as_ref() 851 .map_or(true, |x| x.comes_from_same_store(store)) 852 } 853 854 #[inline] 855 fn dynamic_concrete_type_check( 856 &self, 857 store: &StoreOpaque, 858 nullable: bool, 859 ty: &HeapType, 860 ) -> Result<()> { 861 match self { 862 Some(s) => { 863 ManuallyRooted::<ArrayRef>::dynamic_concrete_type_check(s, store, nullable, ty) 864 } 865 None => { 866 ensure!( 867 nullable, 868 "expected a non-null reference, but found a null reference" 869 ); 870 Ok(()) 871 } 872 } 873 } 874 875 #[inline] 876 fn is_vmgcref_and_points_to_object(&self) -> bool { 877 self.is_some() 878 } 879 880 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 881 <ManuallyRooted<ArrayRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref) 882 } 883 884 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 885 <ManuallyRooted<ArrayRef>>::wasm_ty_option_load( 886 store, 887 ptr.get_anyref(), 888 ArrayRef::from_cloned_gc_ref, 889 ) 890 } 891 } 892