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