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