1 //! Working with GC `struct` objects. 2 3 use crate::runtime::vm::VMGcRef; 4 use crate::store::StoreId; 5 use crate::vm::{GcLayout, GcStructLayout, VMGcHeader, VMStructRef}; 6 use crate::{ 7 prelude::*, 8 store::{AutoAssertNoGc, StoreContextMut, StoreOpaque}, 9 AsContext, AsContextMut, GcHeapOutOfMemory, GcRefImpl, GcRootIndex, HeapType, ManuallyRooted, 10 RefType, RootSet, Rooted, StructType, Val, ValRaw, ValType, WasmTy, 11 }; 12 use crate::{AnyRef, FieldType}; 13 use core::mem::{self, MaybeUninit}; 14 use wasmtime_environ::{VMGcKind, VMSharedTypeIndex}; 15 16 /// An allocator for a particular Wasm GC struct type. 17 /// 18 /// Every `StructRefPre` is associated with a particular 19 /// [`Store`][crate::Store] and a particular [StructType][crate::StructType]. 20 /// 21 /// Reusing an allocator across many allocations amortizes some per-type runtime 22 /// overheads inside Wasmtime. A `StructRefPre` is to `StructRef`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 a struct type. 39 /// let struct_ty = StructType::new( 40 /// store.engine(), 41 /// [FieldType::new(Mutability::Var, StorageType::I8)], 42 /// )?; 43 /// 44 /// // Create an allocator for the struct type. 45 /// let allocator = StructRefPre::new(&mut store, struct_ty); 46 /// 47 /// { 48 /// let mut scope = RootScope::new(&mut store); 49 /// 50 /// // Allocate a bunch of instances of our struct 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 /// StructRef::new(&mut scope, &allocator, &[Val::I32(i)])?; 55 /// } 56 /// } 57 /// # Ok(()) 58 /// # } 59 /// # foo().unwrap(); 60 /// ``` 61 pub struct StructRefPre { 62 store_id: StoreId, 63 ty: StructType, 64 } 65 66 impl StructRefPre { 67 /// Create a new `StructRefPre` that is associated with the given store 68 /// and type. 69 pub fn new(mut store: impl AsContextMut, ty: StructType) -> Self { 70 Self::_new(store.as_context_mut().0, ty) 71 } 72 73 pub(crate) fn _new(store: &mut StoreOpaque, ty: StructType) -> Self { 74 store.insert_gc_host_alloc_type(ty.registered_type().clone()); 75 let store_id = store.id(); 76 77 StructRefPre { store_id, ty } 78 } 79 80 pub(crate) fn layout(&self) -> &GcStructLayout { 81 self.ty 82 .registered_type() 83 .layout() 84 .expect("struct types have a layout") 85 .unwrap_struct() 86 } 87 88 pub(crate) fn type_index(&self) -> VMSharedTypeIndex { 89 self.ty.registered_type().index() 90 } 91 } 92 93 /// A reference to a GC-managed `struct` instance. 94 /// 95 /// WebAssembly `struct`s are static, fixed-length, ordered sequences of 96 /// fields. Fields are named by index, not by identifier; in this way, they are 97 /// similar to Rust's tuples. Each field is mutable or constant and stores 98 /// unpacked [`Val`][crate::Val]s or packed 8-/16-bit integers. 99 /// 100 /// Like all WebAssembly references, these are opaque and unforgeable to Wasm: 101 /// they cannot be faked and Wasm cannot, for example, cast the integer 102 /// `0x12345678` into a reference, pretend it is a valid `structref`, and trick 103 /// the host into dereferencing it and segfaulting or worse. 104 /// 105 /// Note that you can also use `Rooted<StructRef>` and 106 /// `ManuallyRooted<StructRef>` as a type parameter with 107 /// [`Func::typed`][crate::Func::typed]- and 108 /// [`Func::wrap`][crate::Func::wrap]-style APIs. 109 /// 110 /// # Example 111 /// 112 /// ``` 113 /// use wasmtime::*; 114 /// 115 /// # fn foo() -> Result<()> { 116 /// let mut config = Config::new(); 117 /// config.wasm_function_references(true); 118 /// config.wasm_gc(true); 119 /// 120 /// let engine = Engine::new(&config)?; 121 /// let mut store = Store::new(&engine, ()); 122 /// 123 /// // Define a struct type. 124 /// let struct_ty = StructType::new( 125 /// store.engine(), 126 /// [FieldType::new(Mutability::Var, StorageType::I8)], 127 /// )?; 128 /// 129 /// // Create an allocator for the struct type. 130 /// let allocator = StructRefPre::new(&mut store, struct_ty); 131 /// 132 /// { 133 /// let mut scope = RootScope::new(&mut store); 134 /// 135 /// // Allocate an instance of the struct type. 136 /// let my_struct = match StructRef::new(&mut scope, &allocator, &[Val::I32(42)]) { 137 /// Ok(s) => s, 138 /// // If the heap is out of memory, then do a GC and try again. 139 /// Err(e) if e.is::<GcHeapOutOfMemory<()>>() => { 140 /// // Do a GC! Note: in an async context, you'd want to do 141 /// // `scope.as_context_mut().gc_async().await`. 142 /// scope.as_context_mut().gc(); 143 /// 144 /// StructRef::new(&mut scope, &allocator, &[Val::I32(42)])? 145 /// } 146 /// Err(e) => return Err(e), 147 /// }; 148 /// 149 /// // That instance's field should have the expected value. 150 /// let val = my_struct.field(&mut scope, 0)?.unwrap_i32(); 151 /// assert_eq!(val, 42); 152 /// 153 /// // And we can update the field's value because it is a mutable field. 154 /// my_struct.set_field(&mut scope, 0, Val::I32(36))?; 155 /// let new_val = my_struct.field(&mut scope, 0)?.unwrap_i32(); 156 /// assert_eq!(new_val, 36); 157 /// } 158 /// # Ok(()) 159 /// # } 160 /// # foo().unwrap(); 161 /// ``` 162 #[derive(Debug)] 163 #[repr(transparent)] 164 pub struct StructRef { 165 pub(super) inner: GcRootIndex, 166 } 167 168 unsafe impl GcRefImpl for StructRef { 169 #[allow(private_interfaces)] 170 fn transmute_ref(index: &GcRootIndex) -> &Self { 171 // Safety: `StructRef` is a newtype of a `GcRootIndex`. 172 let me: &Self = unsafe { mem::transmute(index) }; 173 174 // Assert we really are just a newtype of a `GcRootIndex`. 175 assert!(matches!( 176 me, 177 Self { 178 inner: GcRootIndex { .. }, 179 } 180 )); 181 182 me 183 } 184 } 185 186 impl Rooted<StructRef> { 187 /// Upcast this `structref` into an `anyref`. 188 #[inline] 189 pub fn to_anyref(self) -> Rooted<AnyRef> { 190 self.unchecked_cast() 191 } 192 } 193 194 impl ManuallyRooted<StructRef> { 195 /// Upcast this `structref` into an `anyref`. 196 #[inline] 197 pub fn to_anyref(self) -> ManuallyRooted<AnyRef> { 198 self.unchecked_cast() 199 } 200 } 201 202 impl StructRef { 203 /// Allocate a new `struct` and get a reference to it. 204 /// 205 /// # Errors 206 /// 207 /// If the given `fields` values' types do not match the field types of the 208 /// `allocator`'s struct type, an error is returned. 209 /// 210 /// If the allocation cannot be satisfied because the GC heap is currently 211 /// out of memory, but performing a garbage collection might free up space 212 /// such that retrying the allocation afterwards might succeed, then a 213 /// [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory] error is returned. 214 /// 215 /// # Panics 216 /// 217 /// Panics if the allocator, or any of the field values, is not associated 218 /// with the given store. 219 pub fn new( 220 mut store: impl AsContextMut, 221 allocator: &StructRefPre, 222 fields: &[Val], 223 ) -> Result<Rooted<StructRef>> { 224 Self::_new(store.as_context_mut().0, allocator, fields) 225 } 226 227 pub(crate) fn _new( 228 store: &mut StoreOpaque, 229 allocator: &StructRefPre, 230 fields: &[Val], 231 ) -> Result<Rooted<StructRef>> { 232 assert_eq!( 233 store.id(), 234 allocator.store_id, 235 "attempted to use a `StructRefPre` with the wrong store" 236 ); 237 238 // Type check the given values against the field types. 239 let expected_len = allocator.ty.fields().len(); 240 let actual_len = fields.len(); 241 ensure!( 242 actual_len == expected_len, 243 "expected {expected_len} fields, got {actual_len}" 244 ); 245 for (ty, val) in allocator.ty.fields().zip(fields) { 246 assert!( 247 val.comes_from_same_store(store), 248 "field value comes from the wrong store", 249 ); 250 let ty = ty.element_type().unpack(); 251 val.ensure_matches_ty(store, ty) 252 .context("field type mismatch")?; 253 } 254 255 // Allocate the struct and write each field value into the appropriate 256 // offset. 257 let structref = store 258 .gc_store_mut()? 259 .alloc_uninit_struct(allocator.type_index(), &allocator.layout()) 260 .err2anyhow() 261 .context("unrecoverable error when allocating new `structref`")? 262 .ok_or_else(|| GcHeapOutOfMemory::new(())) 263 .err2anyhow()?; 264 265 // From this point on, if we get any errors, then the struct is not 266 // fully initialized, so we need to eagerly deallocate it before the 267 // next GC where the collector might try to interpret one of the 268 // uninitialized fields as a GC reference. 269 let mut store = AutoAssertNoGc::new(store); 270 match (|| { 271 for (index, (ty, val)) in allocator.ty.fields().zip(fields).enumerate() { 272 structref.initialize_field( 273 &mut store, 274 allocator.layout(), 275 ty.element_type(), 276 index, 277 val.clone(), 278 )?; 279 } 280 Ok(()) 281 })() { 282 Ok(()) => Ok(Rooted::new(&mut store, structref.into())), 283 Err(e) => { 284 store.gc_store_mut()?.dealloc_uninit_struct(structref); 285 Err(e) 286 } 287 } 288 } 289 290 #[inline] 291 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool { 292 self.inner.comes_from_same_store(store) 293 } 294 295 /// Get this `structref`'s type. 296 /// 297 /// # Errors 298 /// 299 /// Return an error if this reference has been unrooted. 300 /// 301 /// # Panics 302 /// 303 /// Panics if this reference is associated with a different store. 304 pub fn ty(&self, store: impl AsContext) -> Result<StructType> { 305 self._ty(store.as_context().0) 306 } 307 308 pub(crate) fn _ty(&self, store: &StoreOpaque) -> Result<StructType> { 309 assert!(self.comes_from_same_store(store)); 310 let index = self.type_index(store)?; 311 Ok(StructType::from_shared_type_index(store.engine(), index)) 312 } 313 314 /// Does this `structref` match the given type? 315 /// 316 /// That is, is this struct's type a subtype of the given type? 317 /// 318 /// # Errors 319 /// 320 /// Return an error if this reference has been unrooted. 321 /// 322 /// # Panics 323 /// 324 /// Panics if this reference is associated with a different store or if the 325 /// type is not associated with the store's engine. 326 pub fn matches_ty(&self, store: impl AsContext, ty: &StructType) -> Result<bool> { 327 self._matches_ty(store.as_context().0, ty) 328 } 329 330 pub(crate) fn _matches_ty(&self, store: &StoreOpaque, ty: &StructType) -> Result<bool> { 331 assert!(self.comes_from_same_store(store)); 332 Ok(self._ty(store)?.matches(ty)) 333 } 334 335 pub(crate) fn ensure_matches_ty(&self, store: &StoreOpaque, ty: &StructType) -> Result<()> { 336 if !self.comes_from_same_store(store) { 337 bail!("function used with wrong store"); 338 } 339 if self._matches_ty(store, ty)? { 340 Ok(()) 341 } else { 342 let actual_ty = self._ty(store)?; 343 bail!("type mismatch: expected `(ref {ty})`, found `(ref {actual_ty})`") 344 } 345 } 346 347 /// Get the values of this struct's fields. 348 /// 349 /// Note that `i8` and `i16` field values are zero-extended into 350 /// `Val::I32(_)`s. 351 /// 352 /// # Errors 353 /// 354 /// Return an error if this reference has been unrooted. 355 /// 356 /// # Panics 357 /// 358 /// Panics if this reference is associated with a different store. 359 pub fn fields<'a, T: 'a>( 360 &'a self, 361 store: impl Into<StoreContextMut<'a, T>>, 362 ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> { 363 self._fields(store.into().0) 364 } 365 366 pub(crate) fn _fields<'a>( 367 &'a self, 368 store: &'a mut StoreOpaque, 369 ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> { 370 assert!(self.comes_from_same_store(store)); 371 let store = AutoAssertNoGc::new(store); 372 373 let gc_ref = self.inner.try_gc_ref(&store)?; 374 let header = store.gc_store()?.header(gc_ref); 375 debug_assert!(header.kind().matches(VMGcKind::StructRef)); 376 377 let index = header.ty().expect("structrefs should have concrete types"); 378 let ty = StructType::from_shared_type_index(store.engine(), index); 379 let len = ty.fields().len(); 380 381 return Ok(Fields { 382 structref: self, 383 store, 384 index: 0, 385 len, 386 }); 387 388 struct Fields<'a, 'b> { 389 structref: &'a StructRef, 390 store: AutoAssertNoGc<'b>, 391 index: usize, 392 len: usize, 393 } 394 395 impl Iterator for Fields<'_, '_> { 396 type Item = Val; 397 398 #[inline] 399 fn next(&mut self) -> Option<Self::Item> { 400 let i = self.index; 401 debug_assert!(i <= self.len); 402 if i >= self.len { 403 return None; 404 } 405 self.index += 1; 406 Some(self.structref._field(&mut self.store, i).unwrap()) 407 } 408 409 #[inline] 410 fn size_hint(&self) -> (usize, Option<usize>) { 411 let len = self.len - self.index; 412 (len, Some(len)) 413 } 414 } 415 416 impl ExactSizeIterator for Fields<'_, '_> { 417 #[inline] 418 fn len(&self) -> usize { 419 self.len - self.index 420 } 421 } 422 } 423 424 fn header<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMGcHeader> { 425 assert!(self.comes_from_same_store(&store)); 426 let gc_ref = self.inner.try_gc_ref(store)?; 427 Ok(store.gc_store()?.header(gc_ref)) 428 } 429 430 fn structref<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMStructRef> { 431 assert!(self.comes_from_same_store(&store)); 432 let gc_ref = self.inner.try_gc_ref(store)?; 433 debug_assert!(self.header(store)?.kind().matches(VMGcKind::StructRef)); 434 Ok(gc_ref.as_structref_unchecked()) 435 } 436 437 fn layout(&self, store: &AutoAssertNoGc<'_>) -> Result<GcStructLayout> { 438 assert!(self.comes_from_same_store(&store)); 439 let type_index = self.type_index(store)?; 440 let layout = store 441 .engine() 442 .signatures() 443 .layout(type_index) 444 .expect("struct types should have GC layouts"); 445 match layout { 446 GcLayout::Struct(s) => Ok(s), 447 GcLayout::Array(_) => unreachable!(), 448 } 449 } 450 451 fn field_ty(&self, store: &StoreOpaque, field: usize) -> Result<FieldType> { 452 let ty = self._ty(store)?; 453 match ty.field(field) { 454 Some(f) => Ok(f), 455 None => { 456 let len = ty.fields().len(); 457 bail!("cannot access field {field}: struct only has {len} fields") 458 } 459 } 460 } 461 462 /// Get this struct's `index`th field. 463 /// 464 /// Note that `i8` and `i16` field values are zero-extended into 465 /// `Val::I32(_)`s. 466 /// 467 /// # Errors 468 /// 469 /// Returns an `Err(_)` if the index is out of bounds or this reference has 470 /// been unrooted. 471 /// 472 /// # Panics 473 /// 474 /// Panics if this reference is associated with a different store. 475 pub fn field(&self, mut store: impl AsContextMut, index: usize) -> Result<Val> { 476 let mut store = AutoAssertNoGc::new(store.as_context_mut().0); 477 self._field(&mut store, index) 478 } 479 480 pub(crate) fn _field(&self, store: &mut AutoAssertNoGc<'_>, index: usize) -> Result<Val> { 481 assert!(self.comes_from_same_store(store)); 482 let structref = self.structref(store)?.unchecked_copy(); 483 let field_ty = self.field_ty(store, index)?; 484 let layout = self.layout(store)?; 485 Ok(structref.read_field(store, &layout, field_ty.element_type(), index)) 486 } 487 488 /// Set this struct's `index`th field. 489 /// 490 /// # Errors 491 /// 492 /// Returns an error in the following scenarios: 493 /// 494 /// * When given a value of the wrong type, such as trying to set an `f32` 495 /// field to an `i64` value. 496 /// 497 /// * When the field is not mutable. 498 /// 499 /// * When this struct does not have an `index`th field, i.e. `index` is out 500 /// of bounds. 501 /// 502 /// * When `value` is a GC reference that has since been unrooted. 503 /// 504 /// # Panics 505 /// 506 /// Panics if this reference is associated with a different store. 507 pub fn set_field(&self, mut store: impl AsContextMut, index: usize, value: Val) -> Result<()> { 508 self._set_field(store.as_context_mut().0, index, value) 509 } 510 511 pub(crate) fn _set_field( 512 &self, 513 store: &mut StoreOpaque, 514 index: usize, 515 value: Val, 516 ) -> Result<()> { 517 assert!(self.comes_from_same_store(store)); 518 let mut store = AutoAssertNoGc::new(store); 519 520 let field_ty = self.field_ty(&store, index)?; 521 ensure!( 522 field_ty.mutability().is_var(), 523 "cannot set field {index}: field is not mutable" 524 ); 525 526 value 527 .ensure_matches_ty(&store, &field_ty.element_type().unpack()) 528 .with_context(|| format!("cannot set field {index}: type mismatch"))?; 529 530 let layout = self.layout(&store)?; 531 let structref = self.structref(&store)?.unchecked_copy(); 532 533 structref.write_field(&mut store, &layout, field_ty.element_type(), index, value) 534 } 535 536 pub(crate) fn type_index(&self, store: &StoreOpaque) -> Result<VMSharedTypeIndex> { 537 let gc_ref = self.inner.unchecked_try_gc_ref(store)?; 538 let header = store.gc_store()?.header(gc_ref); 539 debug_assert!(header.kind().matches(VMGcKind::StructRef)); 540 Ok(header.ty().expect("structrefs should have concrete types")) 541 } 542 543 /// Create a new `Rooted<StructRef>` from the given GC reference. 544 /// 545 /// `gc_ref` should point to a valid `structref` and should belong to the 546 /// store's GC heap. Failure to uphold these invariants is memory safe but 547 /// will lead to general incorrectness such as panics or wrong results. 548 pub(crate) fn from_cloned_gc_ref( 549 store: &mut AutoAssertNoGc<'_>, 550 gc_ref: VMGcRef, 551 ) -> Rooted<Self> { 552 debug_assert!(!gc_ref.is_i31()); 553 Rooted::new(store, gc_ref) 554 } 555 } 556 557 unsafe impl WasmTy for Rooted<StructRef> { 558 #[inline] 559 fn valtype() -> ValType { 560 ValType::Ref(RefType::new(false, HeapType::Struct)) 561 } 562 563 #[inline] 564 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 565 self.comes_from_same_store(store) 566 } 567 568 #[inline] 569 fn dynamic_concrete_type_check( 570 &self, 571 store: &StoreOpaque, 572 _nullable: bool, 573 ty: &HeapType, 574 ) -> Result<()> { 575 match ty { 576 HeapType::Any | HeapType::Eq | HeapType::Struct => Ok(()), 577 HeapType::ConcreteStruct(ty) => self.ensure_matches_ty(store, ty), 578 579 HeapType::Extern 580 | HeapType::NoExtern 581 | HeapType::Func 582 | HeapType::ConcreteFunc(_) 583 | HeapType::NoFunc 584 | HeapType::I31 585 | HeapType::Array 586 | HeapType::ConcreteArray(_) 587 | HeapType::None => bail!( 588 "type mismatch: expected `(ref {ty})`, got `(ref {})`", 589 self._ty(store)?, 590 ), 591 } 592 } 593 594 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 595 let gc_ref = self.inner.try_clone_gc_ref(store)?; 596 let r64 = gc_ref.as_r64(); 597 store.gc_store_mut()?.expose_gc_ref_to_wasm(gc_ref); 598 debug_assert_ne!(r64, 0); 599 let anyref = u32::try_from(r64).unwrap(); 600 ptr.write(ValRaw::anyref(anyref)); 601 Ok(()) 602 } 603 604 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 605 let raw = ptr.get_anyref(); 606 debug_assert_ne!(raw, 0); 607 let gc_ref = VMGcRef::from_r64(raw.into()) 608 .expect("valid r64") 609 .expect("non-null"); 610 let gc_ref = store.unwrap_gc_store_mut().clone_gc_ref(&gc_ref); 611 StructRef::from_cloned_gc_ref(store, gc_ref) 612 } 613 } 614 615 unsafe impl WasmTy for Option<Rooted<StructRef>> { 616 #[inline] 617 fn valtype() -> ValType { 618 ValType::STRUCTREF 619 } 620 621 #[inline] 622 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 623 self.map_or(true, |x| x.comes_from_same_store(store)) 624 } 625 626 #[inline] 627 fn dynamic_concrete_type_check( 628 &self, 629 store: &StoreOpaque, 630 nullable: bool, 631 ty: &HeapType, 632 ) -> Result<()> { 633 match self { 634 Some(s) => Rooted::<StructRef>::dynamic_concrete_type_check(s, store, nullable, ty), 635 None => { 636 ensure!( 637 nullable, 638 "expected a non-null reference, but found a null reference" 639 ); 640 Ok(()) 641 } 642 } 643 } 644 645 #[inline] 646 fn is_vmgcref_and_points_to_object(&self) -> bool { 647 self.is_some() 648 } 649 650 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 651 match self { 652 Some(r) => r.store(store, ptr), 653 None => { 654 ptr.write(ValRaw::anyref(0)); 655 Ok(()) 656 } 657 } 658 } 659 660 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 661 let gc_ref = VMGcRef::from_r64(ptr.get_anyref().into()).expect("valid r64")?; 662 let gc_ref = store.unwrap_gc_store_mut().clone_gc_ref(&gc_ref); 663 Some(StructRef::from_cloned_gc_ref(store, gc_ref)) 664 } 665 } 666 667 unsafe impl WasmTy for ManuallyRooted<StructRef> { 668 #[inline] 669 fn valtype() -> ValType { 670 ValType::Ref(RefType::new(false, HeapType::Struct)) 671 } 672 673 #[inline] 674 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 675 self.comes_from_same_store(store) 676 } 677 678 #[inline] 679 fn dynamic_concrete_type_check( 680 &self, 681 store: &StoreOpaque, 682 _: bool, 683 ty: &HeapType, 684 ) -> Result<()> { 685 match ty { 686 HeapType::Any | HeapType::Eq | HeapType::Struct => Ok(()), 687 HeapType::ConcreteStruct(ty) => self.ensure_matches_ty(store, ty), 688 689 HeapType::Extern 690 | HeapType::NoExtern 691 | HeapType::Func 692 | HeapType::ConcreteFunc(_) 693 | HeapType::NoFunc 694 | HeapType::I31 695 | HeapType::Array 696 | HeapType::ConcreteArray(_) 697 | HeapType::None => bail!( 698 "type mismatch: expected `(ref {ty})`, got `(ref {})`", 699 self._ty(store)?, 700 ), 701 } 702 } 703 704 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 705 let gc_ref = self.inner.try_clone_gc_ref(store)?; 706 let r64 = gc_ref.as_r64(); 707 store.gc_store_mut()?.expose_gc_ref_to_wasm(gc_ref); 708 debug_assert_ne!(r64, 0); 709 let anyref = u32::try_from(r64).unwrap(); 710 ptr.write(ValRaw::anyref(anyref)); 711 Ok(()) 712 } 713 714 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 715 let raw = ptr.get_anyref(); 716 debug_assert_ne!(raw, 0); 717 let gc_ref = VMGcRef::from_r64(raw.into()) 718 .expect("valid r64") 719 .expect("non-null"); 720 let gc_ref = store.unwrap_gc_store_mut().clone_gc_ref(&gc_ref); 721 RootSet::with_lifo_scope(store, |store| { 722 let rooted = StructRef::from_cloned_gc_ref(store, gc_ref); 723 rooted 724 ._to_manually_rooted(store) 725 .expect("rooted is in scope") 726 }) 727 } 728 } 729 730 unsafe impl WasmTy for Option<ManuallyRooted<StructRef>> { 731 #[inline] 732 fn valtype() -> ValType { 733 ValType::STRUCTREF 734 } 735 736 #[inline] 737 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 738 self.as_ref() 739 .map_or(true, |x| x.comes_from_same_store(store)) 740 } 741 742 #[inline] 743 fn dynamic_concrete_type_check( 744 &self, 745 store: &StoreOpaque, 746 nullable: bool, 747 ty: &HeapType, 748 ) -> Result<()> { 749 match self { 750 Some(s) => { 751 ManuallyRooted::<StructRef>::dynamic_concrete_type_check(s, store, nullable, ty) 752 } 753 None => { 754 ensure!( 755 nullable, 756 "expected a non-null reference, but found a null reference" 757 ); 758 Ok(()) 759 } 760 } 761 } 762 763 #[inline] 764 fn is_vmgcref_and_points_to_object(&self) -> bool { 765 self.is_some() 766 } 767 768 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 769 match self { 770 Some(r) => r.store(store, ptr), 771 None => { 772 ptr.write(ValRaw::anyref(0)); 773 Ok(()) 774 } 775 } 776 } 777 778 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 779 let raw = ptr.get_anyref(); 780 debug_assert_ne!(raw, 0); 781 let gc_ref = VMGcRef::from_r64(raw.into()).expect("valid r64")?; 782 let gc_ref = store.unwrap_gc_store_mut().clone_gc_ref(&gc_ref); 783 RootSet::with_lifo_scope(store, |store| { 784 let rooted = StructRef::from_cloned_gc_ref(store, gc_ref); 785 Some( 786 rooted 787 ._to_manually_rooted(store) 788 .expect("rooted is in scope"), 789 ) 790 }) 791 } 792 } 793