1 //! Working with GC `struct` objects. 2 3 use crate::runtime::vm::VMGcRef; 4 use crate::store::StoreId; 5 use crate::vm::{VMGcHeader, VMStructRef}; 6 use crate::{ 7 prelude::*, 8 store::{AutoAssertNoGc, StoreContextMut, StoreOpaque}, 9 AsContext, AsContextMut, EqRef, GcHeapOutOfMemory, GcRefImpl, GcRootIndex, HeapType, 10 ManuallyRooted, RefType, Rooted, StructType, Val, ValRaw, ValType, WasmTy, 11 }; 12 use crate::{AnyRef, FieldType}; 13 use core::mem::{self, MaybeUninit}; 14 use wasmtime_environ::{GcLayout, GcStructLayout, 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 /// Upcast this `structref` into an `eqref`. 194 #[inline] 195 pub fn to_eqref(self) -> Rooted<EqRef> { 196 self.unchecked_cast() 197 } 198 } 199 200 impl ManuallyRooted<StructRef> { 201 /// Upcast this `structref` into an `anyref`. 202 #[inline] 203 pub fn to_anyref(self) -> ManuallyRooted<AnyRef> { 204 self.unchecked_cast() 205 } 206 207 /// Upcast this `structref` into an `eqref`. 208 #[inline] 209 pub fn to_eqref(self) -> ManuallyRooted<EqRef> { 210 self.unchecked_cast() 211 } 212 } 213 214 impl StructRef { 215 /// Allocate a new `struct` and get a reference to it. 216 /// 217 /// # Errors 218 /// 219 /// If the given `fields` values' types do not match the field types of the 220 /// `allocator`'s struct type, an error is returned. 221 /// 222 /// If the allocation cannot be satisfied because the GC heap is currently 223 /// out of memory, but performing a garbage collection might free up space 224 /// such that retrying the allocation afterwards might succeed, then a 225 /// [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory] error is returned. 226 /// 227 /// # Panics 228 /// 229 /// Panics if the allocator, or any of the field values, is not associated 230 /// with the given store. 231 pub fn new( 232 mut store: impl AsContextMut, 233 allocator: &StructRefPre, 234 fields: &[Val], 235 ) -> Result<Rooted<StructRef>> { 236 Self::_new(store.as_context_mut().0, allocator, fields) 237 } 238 239 pub(crate) fn _new( 240 store: &mut StoreOpaque, 241 allocator: &StructRefPre, 242 fields: &[Val], 243 ) -> Result<Rooted<StructRef>> { 244 assert_eq!( 245 store.id(), 246 allocator.store_id, 247 "attempted to use a `StructRefPre` with the wrong store" 248 ); 249 250 // Type check the given values against the field types. 251 let expected_len = allocator.ty.fields().len(); 252 let actual_len = fields.len(); 253 ensure!( 254 actual_len == expected_len, 255 "expected {expected_len} fields, got {actual_len}" 256 ); 257 for (ty, val) in allocator.ty.fields().zip(fields) { 258 assert!( 259 val.comes_from_same_store(store), 260 "field value comes from the wrong store", 261 ); 262 let ty = ty.element_type().unpack(); 263 val.ensure_matches_ty(store, ty) 264 .context("field type mismatch")?; 265 } 266 267 // Allocate the struct and write each field value into the appropriate 268 // offset. 269 let structref = store 270 .gc_store_mut()? 271 .alloc_uninit_struct(allocator.type_index(), &allocator.layout()) 272 .context("unrecoverable error when allocating new `structref`")? 273 .ok_or_else(|| GcHeapOutOfMemory::new(()))?; 274 275 // From this point on, if we get any errors, then the struct is not 276 // fully initialized, so we need to eagerly deallocate it before the 277 // next GC where the collector might try to interpret one of the 278 // uninitialized fields as a GC reference. 279 let mut store = AutoAssertNoGc::new(store); 280 match (|| { 281 for (index, (ty, val)) in allocator.ty.fields().zip(fields).enumerate() { 282 structref.initialize_field( 283 &mut store, 284 allocator.layout(), 285 ty.element_type(), 286 index, 287 *val, 288 )?; 289 } 290 Ok(()) 291 })() { 292 Ok(()) => Ok(Rooted::new(&mut store, structref.into())), 293 Err(e) => { 294 store.gc_store_mut()?.dealloc_uninit_struct(structref); 295 Err(e) 296 } 297 } 298 } 299 300 #[inline] 301 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool { 302 self.inner.comes_from_same_store(store) 303 } 304 305 /// Get this `structref`'s type. 306 /// 307 /// # Errors 308 /// 309 /// Return an error if this reference has been unrooted. 310 /// 311 /// # Panics 312 /// 313 /// Panics if this reference is associated with a different store. 314 pub fn ty(&self, store: impl AsContext) -> Result<StructType> { 315 self._ty(store.as_context().0) 316 } 317 318 pub(crate) fn _ty(&self, store: &StoreOpaque) -> Result<StructType> { 319 assert!(self.comes_from_same_store(store)); 320 let index = self.type_index(store)?; 321 Ok(StructType::from_shared_type_index(store.engine(), index)) 322 } 323 324 /// Does this `structref` match the given type? 325 /// 326 /// That is, is this struct's type a subtype of the given type? 327 /// 328 /// # Errors 329 /// 330 /// Return an error if this reference has been unrooted. 331 /// 332 /// # Panics 333 /// 334 /// Panics if this reference is associated with a different store or if the 335 /// type is not associated with the store's engine. 336 pub fn matches_ty(&self, store: impl AsContext, ty: &StructType) -> Result<bool> { 337 self._matches_ty(store.as_context().0, ty) 338 } 339 340 pub(crate) fn _matches_ty(&self, store: &StoreOpaque, ty: &StructType) -> Result<bool> { 341 assert!(self.comes_from_same_store(store)); 342 Ok(self._ty(store)?.matches(ty)) 343 } 344 345 pub(crate) fn ensure_matches_ty(&self, store: &StoreOpaque, ty: &StructType) -> Result<()> { 346 if !self.comes_from_same_store(store) { 347 bail!("function used with wrong store"); 348 } 349 if self._matches_ty(store, ty)? { 350 Ok(()) 351 } else { 352 let actual_ty = self._ty(store)?; 353 bail!("type mismatch: expected `(ref {ty})`, found `(ref {actual_ty})`") 354 } 355 } 356 357 /// Get the values of this struct's fields. 358 /// 359 /// Note that `i8` and `i16` field values are zero-extended into 360 /// `Val::I32(_)`s. 361 /// 362 /// # Errors 363 /// 364 /// Return an error if this reference has been unrooted. 365 /// 366 /// # Panics 367 /// 368 /// Panics if this reference is associated with a different store. 369 pub fn fields<'a, T: 'a>( 370 &'a self, 371 store: impl Into<StoreContextMut<'a, T>>, 372 ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> { 373 self._fields(store.into().0) 374 } 375 376 pub(crate) fn _fields<'a>( 377 &'a self, 378 store: &'a mut StoreOpaque, 379 ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> { 380 assert!(self.comes_from_same_store(store)); 381 let store = AutoAssertNoGc::new(store); 382 383 let gc_ref = self.inner.try_gc_ref(&store)?; 384 let header = store.gc_store()?.header(gc_ref); 385 debug_assert!(header.kind().matches(VMGcKind::StructRef)); 386 387 let index = header.ty().expect("structrefs should have concrete types"); 388 let ty = StructType::from_shared_type_index(store.engine(), index); 389 let len = ty.fields().len(); 390 391 return Ok(Fields { 392 structref: self, 393 store, 394 index: 0, 395 len, 396 }); 397 398 struct Fields<'a, 'b> { 399 structref: &'a StructRef, 400 store: AutoAssertNoGc<'b>, 401 index: usize, 402 len: usize, 403 } 404 405 impl Iterator for Fields<'_, '_> { 406 type Item = Val; 407 408 #[inline] 409 fn next(&mut self) -> Option<Self::Item> { 410 let i = self.index; 411 debug_assert!(i <= self.len); 412 if i >= self.len { 413 return None; 414 } 415 self.index += 1; 416 Some(self.structref._field(&mut self.store, i).unwrap()) 417 } 418 419 #[inline] 420 fn size_hint(&self) -> (usize, Option<usize>) { 421 let len = self.len - self.index; 422 (len, Some(len)) 423 } 424 } 425 426 impl ExactSizeIterator for Fields<'_, '_> { 427 #[inline] 428 fn len(&self) -> usize { 429 self.len - self.index 430 } 431 } 432 } 433 434 fn header<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMGcHeader> { 435 assert!(self.comes_from_same_store(&store)); 436 let gc_ref = self.inner.try_gc_ref(store)?; 437 Ok(store.gc_store()?.header(gc_ref)) 438 } 439 440 fn structref<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMStructRef> { 441 assert!(self.comes_from_same_store(&store)); 442 let gc_ref = self.inner.try_gc_ref(store)?; 443 debug_assert!(self.header(store)?.kind().matches(VMGcKind::StructRef)); 444 Ok(gc_ref.as_structref_unchecked()) 445 } 446 447 fn layout(&self, store: &AutoAssertNoGc<'_>) -> Result<GcStructLayout> { 448 assert!(self.comes_from_same_store(&store)); 449 let type_index = self.type_index(store)?; 450 let layout = store 451 .engine() 452 .signatures() 453 .layout(type_index) 454 .expect("struct types should have GC layouts"); 455 match layout { 456 GcLayout::Struct(s) => Ok(s), 457 GcLayout::Array(_) => unreachable!(), 458 } 459 } 460 461 fn field_ty(&self, store: &StoreOpaque, field: usize) -> Result<FieldType> { 462 let ty = self._ty(store)?; 463 match ty.field(field) { 464 Some(f) => Ok(f), 465 None => { 466 let len = ty.fields().len(); 467 bail!("cannot access field {field}: struct only has {len} fields") 468 } 469 } 470 } 471 472 /// Get this struct's `index`th field. 473 /// 474 /// Note that `i8` and `i16` field values are zero-extended into 475 /// `Val::I32(_)`s. 476 /// 477 /// # Errors 478 /// 479 /// Returns an `Err(_)` if the index is out of bounds or this reference has 480 /// been unrooted. 481 /// 482 /// # Panics 483 /// 484 /// Panics if this reference is associated with a different store. 485 pub fn field(&self, mut store: impl AsContextMut, index: usize) -> Result<Val> { 486 let mut store = AutoAssertNoGc::new(store.as_context_mut().0); 487 self._field(&mut store, index) 488 } 489 490 pub(crate) fn _field(&self, store: &mut AutoAssertNoGc<'_>, index: usize) -> Result<Val> { 491 assert!(self.comes_from_same_store(store)); 492 let structref = self.structref(store)?.unchecked_copy(); 493 let field_ty = self.field_ty(store, index)?; 494 let layout = self.layout(store)?; 495 Ok(structref.read_field(store, &layout, field_ty.element_type(), index)) 496 } 497 498 /// Set this struct's `index`th field. 499 /// 500 /// # Errors 501 /// 502 /// Returns an error in the following scenarios: 503 /// 504 /// * When given a value of the wrong type, such as trying to set an `f32` 505 /// field to an `i64` value. 506 /// 507 /// * When the field is not mutable. 508 /// 509 /// * When this struct does not have an `index`th field, i.e. `index` is out 510 /// of bounds. 511 /// 512 /// * When `value` is a GC reference that has since been unrooted. 513 /// 514 /// # Panics 515 /// 516 /// Panics if this reference is associated with a different store. 517 pub fn set_field(&self, mut store: impl AsContextMut, index: usize, value: Val) -> Result<()> { 518 self._set_field(store.as_context_mut().0, index, value) 519 } 520 521 pub(crate) fn _set_field( 522 &self, 523 store: &mut StoreOpaque, 524 index: usize, 525 value: Val, 526 ) -> Result<()> { 527 assert!(self.comes_from_same_store(store)); 528 let mut store = AutoAssertNoGc::new(store); 529 530 let field_ty = self.field_ty(&store, index)?; 531 ensure!( 532 field_ty.mutability().is_var(), 533 "cannot set field {index}: field is not mutable" 534 ); 535 536 value 537 .ensure_matches_ty(&store, &field_ty.element_type().unpack()) 538 .with_context(|| format!("cannot set field {index}: type mismatch"))?; 539 540 let layout = self.layout(&store)?; 541 let structref = self.structref(&store)?.unchecked_copy(); 542 543 structref.write_field(&mut store, &layout, field_ty.element_type(), index, value) 544 } 545 546 pub(crate) fn type_index(&self, store: &StoreOpaque) -> Result<VMSharedTypeIndex> { 547 let gc_ref = self.inner.try_gc_ref(store)?; 548 let header = store.gc_store()?.header(gc_ref); 549 debug_assert!(header.kind().matches(VMGcKind::StructRef)); 550 Ok(header.ty().expect("structrefs should have concrete types")) 551 } 552 553 /// Create a new `Rooted<StructRef>` from the given GC reference. 554 /// 555 /// `gc_ref` should point to a valid `structref` and should belong to the 556 /// store's GC heap. Failure to uphold these invariants is memory safe but 557 /// will lead to general incorrectness such as panics or wrong results. 558 pub(crate) fn from_cloned_gc_ref( 559 store: &mut AutoAssertNoGc<'_>, 560 gc_ref: VMGcRef, 561 ) -> Rooted<Self> { 562 debug_assert!(gc_ref.is_structref(&*store.unwrap_gc_store().gc_heap)); 563 Rooted::new(store, gc_ref) 564 } 565 } 566 567 unsafe impl WasmTy for Rooted<StructRef> { 568 #[inline] 569 fn valtype() -> ValType { 570 ValType::Ref(RefType::new(false, HeapType::Struct)) 571 } 572 573 #[inline] 574 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 575 self.comes_from_same_store(store) 576 } 577 578 #[inline] 579 fn dynamic_concrete_type_check( 580 &self, 581 store: &StoreOpaque, 582 _nullable: bool, 583 ty: &HeapType, 584 ) -> Result<()> { 585 match ty { 586 HeapType::Any | HeapType::Eq | HeapType::Struct => Ok(()), 587 HeapType::ConcreteStruct(ty) => self.ensure_matches_ty(store, ty), 588 589 HeapType::Extern 590 | HeapType::NoExtern 591 | HeapType::Func 592 | HeapType::ConcreteFunc(_) 593 | HeapType::NoFunc 594 | HeapType::I31 595 | HeapType::Array 596 | HeapType::ConcreteArray(_) 597 | HeapType::None => bail!( 598 "type mismatch: expected `(ref {ty})`, got `(ref {})`", 599 self._ty(store)?, 600 ), 601 } 602 } 603 604 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 605 self.wasm_ty_store(store, ptr, ValRaw::anyref) 606 } 607 608 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 609 Self::wasm_ty_load(store, ptr.get_anyref(), StructRef::from_cloned_gc_ref) 610 } 611 } 612 613 unsafe impl WasmTy for Option<Rooted<StructRef>> { 614 #[inline] 615 fn valtype() -> ValType { 616 ValType::STRUCTREF 617 } 618 619 #[inline] 620 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 621 self.map_or(true, |x| x.comes_from_same_store(store)) 622 } 623 624 #[inline] 625 fn dynamic_concrete_type_check( 626 &self, 627 store: &StoreOpaque, 628 nullable: bool, 629 ty: &HeapType, 630 ) -> Result<()> { 631 match self { 632 Some(s) => Rooted::<StructRef>::dynamic_concrete_type_check(s, store, nullable, ty), 633 None => { 634 ensure!( 635 nullable, 636 "expected a non-null reference, but found a null reference" 637 ); 638 Ok(()) 639 } 640 } 641 } 642 643 #[inline] 644 fn is_vmgcref_and_points_to_object(&self) -> bool { 645 self.is_some() 646 } 647 648 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 649 <Rooted<StructRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref) 650 } 651 652 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 653 <Rooted<StructRef>>::wasm_ty_option_load( 654 store, 655 ptr.get_anyref(), 656 StructRef::from_cloned_gc_ref, 657 ) 658 } 659 } 660 661 unsafe impl WasmTy for ManuallyRooted<StructRef> { 662 #[inline] 663 fn valtype() -> ValType { 664 ValType::Ref(RefType::new(false, HeapType::Struct)) 665 } 666 667 #[inline] 668 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 669 self.comes_from_same_store(store) 670 } 671 672 #[inline] 673 fn dynamic_concrete_type_check( 674 &self, 675 store: &StoreOpaque, 676 _: bool, 677 ty: &HeapType, 678 ) -> Result<()> { 679 match ty { 680 HeapType::Any | HeapType::Eq | HeapType::Struct => Ok(()), 681 HeapType::ConcreteStruct(ty) => self.ensure_matches_ty(store, ty), 682 683 HeapType::Extern 684 | HeapType::NoExtern 685 | HeapType::Func 686 | HeapType::ConcreteFunc(_) 687 | HeapType::NoFunc 688 | HeapType::I31 689 | HeapType::Array 690 | HeapType::ConcreteArray(_) 691 | HeapType::None => bail!( 692 "type mismatch: expected `(ref {ty})`, got `(ref {})`", 693 self._ty(store)?, 694 ), 695 } 696 } 697 698 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 699 self.wasm_ty_store(store, ptr, ValRaw::anyref) 700 } 701 702 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 703 Self::wasm_ty_load(store, ptr.get_anyref(), StructRef::from_cloned_gc_ref) 704 } 705 } 706 707 unsafe impl WasmTy for Option<ManuallyRooted<StructRef>> { 708 #[inline] 709 fn valtype() -> ValType { 710 ValType::STRUCTREF 711 } 712 713 #[inline] 714 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 715 self.as_ref() 716 .map_or(true, |x| x.comes_from_same_store(store)) 717 } 718 719 #[inline] 720 fn dynamic_concrete_type_check( 721 &self, 722 store: &StoreOpaque, 723 nullable: bool, 724 ty: &HeapType, 725 ) -> Result<()> { 726 match self { 727 Some(s) => { 728 ManuallyRooted::<StructRef>::dynamic_concrete_type_check(s, store, nullable, ty) 729 } 730 None => { 731 ensure!( 732 nullable, 733 "expected a non-null reference, but found a null reference" 734 ); 735 Ok(()) 736 } 737 } 738 } 739 740 #[inline] 741 fn is_vmgcref_and_points_to_object(&self) -> bool { 742 self.is_some() 743 } 744 745 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 746 <ManuallyRooted<StructRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref) 747 } 748 749 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 750 <ManuallyRooted<StructRef>>::wasm_ty_option_load( 751 store, 752 ptr.get_anyref(), 753 StructRef::from_cloned_gc_ref, 754 ) 755 } 756 } 757