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