1 //! Implementation of `exnref` in Wasmtime. 2 3 use crate::runtime::vm::VMGcRef; 4 use crate::store::{Asyncness, StoreId, StoreResourceLimiter}; 5 #[cfg(feature = "async")] 6 use crate::vm::VMStore; 7 use crate::vm::{self, VMExnRef, VMGcHeader}; 8 use crate::{ 9 AsContext, AsContextMut, GcRefImpl, GcRootIndex, HeapType, OwnedRooted, RefType, Result, 10 Rooted, Val, ValRaw, ValType, WasmTy, 11 store::{AutoAssertNoGc, StoreOpaque}, 12 }; 13 use crate::{ExnType, FieldType, GcHeapOutOfMemory, StoreContextMut, Tag, prelude::*}; 14 use alloc::sync::Arc; 15 use core::mem; 16 use core::mem::MaybeUninit; 17 use wasmtime_environ::{GcLayout, GcStructLayout, VMGcKind, VMSharedTypeIndex}; 18 19 /// An allocator for a particular Wasm GC exception type. 20 /// 21 /// Every `ExnRefPre` is associated with a particular 22 /// [`Store`][crate::Store] and a particular 23 /// [ExnType][crate::ExnType]. 24 /// 25 /// Reusing an allocator across many allocations amortizes some 26 /// per-type runtime overheads inside Wasmtime. An `ExnRefPre` is to 27 /// `ExnRef`s as an `InstancePre` is to `Instance`s. 28 /// 29 /// # Example 30 /// 31 /// ``` 32 /// use wasmtime::*; 33 /// 34 /// # fn foo() -> Result<()> { 35 /// let mut config = Config::new(); 36 /// config.wasm_function_references(true); 37 /// config.wasm_gc(true); 38 /// 39 /// let engine = Engine::new(&config)?; 40 /// let mut store = Store::new(&engine, ()); 41 /// 42 /// // Define a exn type. 43 /// let exn_ty = ExnType::new( 44 /// store.engine(), 45 /// [ValType::I32], 46 /// )?; 47 /// 48 /// // Create an allocator for the exn type. 49 /// let allocator = ExnRefPre::new(&mut store, exn_ty.clone()); 50 /// 51 /// // Create a tag instance to associate with our exception objects. 52 /// let tag = Tag::new(&mut store, &exn_ty.tag_type()).unwrap(); 53 /// 54 /// { 55 /// let mut scope = RootScope::new(&mut store); 56 /// 57 /// // Allocate a bunch of instances of our exception type using the same 58 /// // allocator! This is faster than creating a new allocator for each 59 /// // instance we want to allocate. 60 /// for i in 0..10 { 61 /// ExnRef::new(&mut scope, &allocator, &tag, &[Val::I32(i)])?; 62 /// } 63 /// } 64 /// # Ok(()) 65 /// # } 66 /// # foo().unwrap(); 67 /// ``` 68 pub struct ExnRefPre { 69 store_id: StoreId, 70 ty: ExnType, 71 } 72 73 impl ExnRefPre { 74 /// Create a new `ExnRefPre` that is associated with the given store 75 /// and type. new(mut store: impl AsContextMut, ty: ExnType) -> Self76 pub fn new(mut store: impl AsContextMut, ty: ExnType) -> Self { 77 Self::_new(store.as_context_mut().0, ty) 78 } 79 _new(store: &mut StoreOpaque, ty: ExnType) -> Self80 pub(crate) fn _new(store: &mut StoreOpaque, ty: ExnType) -> Self { 81 store.insert_gc_host_alloc_type(ty.registered_type().clone()); 82 let store_id = store.id(); 83 84 ExnRefPre { store_id, ty } 85 } 86 layout(&self) -> &GcStructLayout87 pub(crate) fn layout(&self) -> &GcStructLayout { 88 self.ty 89 .registered_type() 90 .layout() 91 .expect("exn types have a layout") 92 .unwrap_struct() 93 } 94 type_index(&self) -> VMSharedTypeIndex95 pub(crate) fn type_index(&self) -> VMSharedTypeIndex { 96 self.ty.registered_type().index() 97 } 98 } 99 100 /// An `exnref` GC reference. 101 /// 102 /// The `ExnRef` type represents WebAssembly `exnref` values. These 103 /// are references to exception objects created either by catching a 104 /// thrown exception in WebAssembly with a `catch_ref` clause of a 105 /// `try_table`, or by allocating via the host API. 106 /// 107 /// Note that you can also use `Rooted<ExnRef>` and `OwnedRooted<ExnRef>` as 108 /// a type parameter with [`Func::typed`][crate::Func::typed]- and 109 /// [`Func::wrap`][crate::Func::wrap]-style APIs. 110 #[derive(Debug)] 111 #[repr(transparent)] 112 pub struct ExnRef { 113 pub(super) inner: GcRootIndex, 114 } 115 116 unsafe impl GcRefImpl for ExnRef { transmute_ref(index: &GcRootIndex) -> &Self117 fn transmute_ref(index: &GcRootIndex) -> &Self { 118 // Safety: `ExnRef` is a newtype of a `GcRootIndex`. 119 let me: &Self = unsafe { mem::transmute(index) }; 120 121 // Assert we really are just a newtype of a `GcRootIndex`. 122 assert!(matches!( 123 me, 124 Self { 125 inner: GcRootIndex { .. }, 126 } 127 )); 128 129 me 130 } 131 } 132 133 impl ExnRef { 134 /// Creates a new strongly-owned [`ExnRef`] from the raw value provided. 135 /// 136 /// This is intended to be used in conjunction with [`Func::new_unchecked`], 137 /// [`Func::call_unchecked`], and [`ValRaw`] with its `anyref` field. 138 /// 139 /// This function assumes that `raw` is an `exnref` value which is currently 140 /// rooted within the [`Store`]. 141 /// 142 /// # Correctness 143 /// 144 /// This function is tricky to get right because `raw` not only must be a 145 /// valid `exnref` value produced prior by [`ExnRef::to_raw`] but it must 146 /// also be correctly rooted within the store. When arguments are provided 147 /// to a callback with [`Func::new_unchecked`], for example, or returned via 148 /// [`Func::call_unchecked`], if a GC is performed within the store then 149 /// floating `exnref` values are not rooted and will be GC'd, meaning that 150 /// this function will no longer be correct to call with the values cleaned 151 /// up. This function must be invoked *before* possible GC operations can 152 /// happen (such as calling Wasm). 153 /// 154 /// When in doubt try to not use this. Instead use the Rust APIs of 155 /// [`TypedFunc`] and friends. Note though that this function is not 156 /// `unsafe` as any value can be passed in. Incorrect values can result in 157 /// runtime panics, however, so care must still be taken with this method. 158 /// 159 /// [`Func::call_unchecked`]: crate::Func::call_unchecked 160 /// [`Func::new_unchecked`]: crate::Func::new_unchecked 161 /// [`Store`]: crate::Store 162 /// [`TypedFunc`]: crate::TypedFunc 163 /// [`ValRaw`]: crate::ValRaw from_raw(mut store: impl AsContextMut, raw: u32) -> Option<Rooted<Self>>164 pub fn from_raw(mut store: impl AsContextMut, raw: u32) -> Option<Rooted<Self>> { 165 let mut store = AutoAssertNoGc::new(store.as_context_mut().0); 166 Self::_from_raw(&mut store, raw) 167 } 168 169 // (Not actually memory unsafe since we have indexed GC heaps.) _from_raw(store: &mut AutoAssertNoGc, raw: u32) -> Option<Rooted<Self>>170 pub(crate) fn _from_raw(store: &mut AutoAssertNoGc, raw: u32) -> Option<Rooted<Self>> { 171 let gc_ref = VMGcRef::from_raw_u32(raw)?; 172 let gc_ref = store.clone_gc_ref(&gc_ref); 173 Some(Self::from_cloned_gc_ref(store, gc_ref)) 174 } 175 176 /// Synchronously allocate a new exception object and get a 177 /// reference to it. 178 /// 179 /// # Automatic Garbage Collection 180 /// 181 /// If the GC heap is at capacity, and there isn't room for 182 /// allocating this new exception object, then this method will 183 /// automatically trigger a synchronous collection in an attempt 184 /// to free up space in the GC heap. 185 /// 186 /// # Errors 187 /// 188 /// If the given `fields` values' types do not match the field 189 /// types of the `allocator`'s exception type, an error is 190 /// returned. 191 /// 192 /// If the allocation cannot be satisfied because the GC heap is currently 193 /// out of memory, then a [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory] 194 /// error is returned. The allocation might succeed on a second attempt if 195 /// you drop some rooted GC references and try again. 196 /// 197 /// If `store` is configured with a 198 /// [`ResourceLimiterAsync`](crate::ResourceLimiterAsync) then an error 199 /// will be returned because [`ExnRef::new_async`] should be used instead. 200 /// 201 /// # Panics 202 /// 203 /// Panics if the allocator, or any of the field values, is not associated 204 /// with the given store. new( mut store: impl AsContextMut, allocator: &ExnRefPre, tag: &Tag, fields: &[Val], ) -> Result<Rooted<ExnRef>>205 pub fn new( 206 mut store: impl AsContextMut, 207 allocator: &ExnRefPre, 208 tag: &Tag, 209 fields: &[Val], 210 ) -> Result<Rooted<ExnRef>> { 211 let (mut limiter, store) = store 212 .as_context_mut() 213 .0 214 .validate_sync_resource_limiter_and_store_opaque()?; 215 vm::assert_ready(Self::_new_async( 216 store, 217 limiter.as_mut(), 218 allocator, 219 tag, 220 fields, 221 Asyncness::No, 222 )) 223 } 224 225 /// Asynchronously allocate a new exception object and get a 226 /// reference to it. 227 /// 228 /// # Automatic Garbage Collection 229 /// 230 /// If the GC heap is at capacity, and there isn't room for allocating this 231 /// new exn, then this method will automatically trigger a synchronous 232 /// collection in an attempt to free up space in the GC heap. 233 /// 234 /// # Errors 235 /// 236 /// If the given `fields` values' types do not match the field 237 /// types of the `allocator`'s exception type, an error is 238 /// returned. 239 /// 240 /// If the allocation cannot be satisfied because the GC heap is currently 241 /// out of memory, then a [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory] 242 /// error is returned. The allocation might succeed on a second attempt if 243 /// you drop some rooted GC references and try again. 244 /// 245 /// # Panics 246 /// 247 /// Panics if the allocator, or any of the field values, is not associated 248 /// with the given store. 249 #[cfg(feature = "async")] new_async( mut store: impl AsContextMut, allocator: &ExnRefPre, tag: &Tag, fields: &[Val], ) -> Result<Rooted<ExnRef>>250 pub async fn new_async( 251 mut store: impl AsContextMut, 252 allocator: &ExnRefPre, 253 tag: &Tag, 254 fields: &[Val], 255 ) -> Result<Rooted<ExnRef>> { 256 let (mut limiter, store) = store.as_context_mut().0.resource_limiter_and_store_opaque(); 257 Self::_new_async( 258 store, 259 limiter.as_mut(), 260 allocator, 261 tag, 262 fields, 263 Asyncness::Yes, 264 ) 265 .await 266 } 267 _new_async( store: &mut StoreOpaque, limiter: Option<&mut StoreResourceLimiter<'_>>, allocator: &ExnRefPre, tag: &Tag, fields: &[Val], asyncness: Asyncness, ) -> Result<Rooted<ExnRef>>268 pub(crate) async fn _new_async( 269 store: &mut StoreOpaque, 270 limiter: Option<&mut StoreResourceLimiter<'_>>, 271 allocator: &ExnRefPre, 272 tag: &Tag, 273 fields: &[Val], 274 asyncness: Asyncness, 275 ) -> Result<Rooted<ExnRef>> { 276 Self::type_check_tag_and_fields(store, allocator, tag, fields)?; 277 store 278 .retry_after_gc_async(limiter, (), asyncness, |store, ()| { 279 Self::new_unchecked(store, allocator, tag, fields) 280 }) 281 .await 282 } 283 284 /// Type check the tag instance and field values before allocating 285 /// a new exception object. type_check_tag_and_fields( store: &mut StoreOpaque, allocator: &ExnRefPre, tag: &Tag, fields: &[Val], ) -> Result<(), Error>286 fn type_check_tag_and_fields( 287 store: &mut StoreOpaque, 288 allocator: &ExnRefPre, 289 tag: &Tag, 290 fields: &[Val], 291 ) -> Result<(), Error> { 292 assert!( 293 tag.comes_from_same_store(store), 294 "tag comes from the wrong store" 295 ); 296 ensure!( 297 tag.wasmtime_ty(store).signature.unwrap_engine_type_index() 298 == allocator.ty.tag_type().ty().type_index(), 299 "incorrect signature for tag when creating exception object" 300 ); 301 let expected_len = allocator.ty.fields().len(); 302 let actual_len = fields.len(); 303 ensure!( 304 actual_len == expected_len, 305 "expected {expected_len} fields, got {actual_len}" 306 ); 307 for (ty, val) in allocator.ty.fields().zip(fields) { 308 assert!( 309 val.comes_from_same_store(store), 310 "field value comes from the wrong store", 311 ); 312 let ty = ty.element_type().unpack(); 313 val.ensure_matches_ty(store, ty) 314 .context("field type mismatch")?; 315 } 316 Ok(()) 317 } 318 319 /// Given that the field values have already been type checked, allocate a 320 /// new exn. 321 /// 322 /// Does not attempt GC+retry on OOM, that is the caller's responsibility. new_unchecked( store: &mut StoreOpaque, allocator: &ExnRefPre, tag: &Tag, fields: &[Val], ) -> Result<Rooted<ExnRef>>323 fn new_unchecked( 324 store: &mut StoreOpaque, 325 allocator: &ExnRefPre, 326 tag: &Tag, 327 fields: &[Val], 328 ) -> Result<Rooted<ExnRef>> { 329 assert_eq!( 330 store.id(), 331 allocator.store_id, 332 "attempted to use a `ExnRefPre` with the wrong store" 333 ); 334 335 // Allocate the exn and write each field value into the appropriate 336 // offset. 337 let exnref = store 338 .require_gc_store_mut()? 339 .alloc_uninit_exn(allocator.type_index(), &allocator.layout()) 340 .context("unrecoverable error when allocating new `exnref`")? 341 .map_err(|n| GcHeapOutOfMemory::new((), n))?; 342 343 // From this point on, if we get any errors, then the exn is not 344 // fully initialized, so we need to eagerly deallocate it before the 345 // next GC where the collector might try to interpret one of the 346 // uninitialized fields as a GC reference. 347 let mut store = AutoAssertNoGc::new(store); 348 match (|| { 349 let (instance, index) = tag.to_raw_indices(); 350 exnref.initialize_tag(&mut store, instance, index)?; 351 for (index, (ty, val)) in allocator.ty.fields().zip(fields).enumerate() { 352 exnref.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, exnref.into())), 363 Err(e) => { 364 store.require_gc_store_mut()?.dealloc_uninit_exn(exnref); 365 Err(e) 366 } 367 } 368 } 369 type_index(&self, store: &StoreOpaque) -> Result<VMSharedTypeIndex>370 pub(crate) fn type_index(&self, store: &StoreOpaque) -> Result<VMSharedTypeIndex> { 371 let gc_ref = self.inner.try_gc_ref(store)?; 372 let header = store.require_gc_store()?.header(gc_ref); 373 debug_assert!(header.kind().matches(VMGcKind::ExnRef)); 374 Ok(header.ty().expect("exnrefs should have concrete types")) 375 } 376 377 /// Create a new `Rooted<ExnRef>` from the given GC reference. 378 /// 379 /// `gc_ref` should point to a valid `exnref` and should belong to 380 /// the store's GC heap. Failure to uphold these invariants is 381 /// memory safe but will lead to general incorrectness such as 382 /// panics or wrong results. from_cloned_gc_ref( store: &mut AutoAssertNoGc<'_>, gc_ref: VMGcRef, ) -> Rooted<Self>383 pub(crate) fn from_cloned_gc_ref( 384 store: &mut AutoAssertNoGc<'_>, 385 gc_ref: VMGcRef, 386 ) -> Rooted<Self> { 387 debug_assert!( 388 store 389 .unwrap_gc_store() 390 .header(&gc_ref) 391 .kind() 392 .matches(VMGcKind::ExnRef) 393 ); 394 Rooted::new(store, gc_ref) 395 } 396 397 #[inline] comes_from_same_store(&self, store: &StoreOpaque) -> bool398 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool { 399 self.inner.comes_from_same_store(store) 400 } 401 402 /// Converts this [`ExnRef`] to a raw value suitable to store within a 403 /// [`ValRaw`]. 404 /// 405 /// Returns an error if this `exnref` has been unrooted. 406 /// 407 /// # Correctness 408 /// 409 /// Produces a raw value which is only valid to pass into a store if a GC 410 /// doesn't happen between when the value is produce and when it's passed 411 /// into the store. 412 /// 413 /// [`ValRaw`]: crate::ValRaw to_raw(&self, mut store: impl AsContextMut) -> Result<u32>414 pub fn to_raw(&self, mut store: impl AsContextMut) -> Result<u32> { 415 let mut store = AutoAssertNoGc::new(store.as_context_mut().0); 416 self._to_raw(&mut store) 417 } 418 _to_raw(&self, store: &mut AutoAssertNoGc<'_>) -> Result<u32>419 pub(crate) fn _to_raw(&self, store: &mut AutoAssertNoGc<'_>) -> Result<u32> { 420 let gc_ref = self.inner.try_clone_gc_ref(store)?; 421 let raw = if gc_ref.is_i31() { 422 gc_ref.as_raw_non_zero_u32() 423 } else { 424 store.require_gc_store_mut()?.expose_gc_ref_to_wasm(gc_ref) 425 }; 426 Ok(raw.get()) 427 } 428 429 /// Get the type of this reference. 430 /// 431 /// # Errors 432 /// 433 /// Return an error if this reference has been unrooted. 434 /// 435 /// # Panics 436 /// 437 /// Panics if this reference is associated with a different store. ty(&self, store: impl AsContext) -> Result<ExnType>438 pub fn ty(&self, store: impl AsContext) -> Result<ExnType> { 439 self._ty(store.as_context().0) 440 } 441 _ty(&self, store: &StoreOpaque) -> Result<ExnType>442 pub(crate) fn _ty(&self, store: &StoreOpaque) -> Result<ExnType> { 443 assert!(self.comes_from_same_store(store)); 444 let index = self.type_index(store)?; 445 Ok(ExnType::from_shared_type_index(store.engine(), index)) 446 } 447 448 /// Does this `exnref` match the given type? 449 /// 450 /// That is, is this object's type a subtype of the given type? 451 /// 452 /// # Errors 453 /// 454 /// Return an error if this reference has been unrooted. 455 /// 456 /// # Panics 457 /// 458 /// Panics if this reference is associated with a different store. matches_ty(&self, store: impl AsContext, ty: &HeapType) -> Result<bool>459 pub fn matches_ty(&self, store: impl AsContext, ty: &HeapType) -> Result<bool> { 460 self._matches_ty(store.as_context().0, ty) 461 } 462 _matches_ty(&self, store: &StoreOpaque, ty: &HeapType) -> Result<bool>463 pub(crate) fn _matches_ty(&self, store: &StoreOpaque, ty: &HeapType) -> Result<bool> { 464 assert!(self.comes_from_same_store(store)); 465 Ok(HeapType::from(self._ty(store)?).matches(ty)) 466 } 467 ensure_matches_ty(&self, store: &StoreOpaque, ty: &HeapType) -> Result<()>468 pub(crate) fn ensure_matches_ty(&self, store: &StoreOpaque, ty: &HeapType) -> Result<()> { 469 if !self.comes_from_same_store(store) { 470 bail!("function used with wrong store"); 471 } 472 if self._matches_ty(store, ty)? { 473 Ok(()) 474 } else { 475 let actual_ty = self._ty(store)?; 476 bail!("type mismatch: expected `(ref {ty})`, found `(ref {actual_ty})`") 477 } 478 } 479 480 /// Get the values of this exception object's fields. 481 /// 482 /// # Errors 483 /// 484 /// Return an error if this reference has been unrooted. 485 /// 486 /// # Panics 487 /// 488 /// 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>489 pub fn fields<'a, T: 'static>( 490 &'a self, 491 store: impl Into<StoreContextMut<'a, T>>, 492 ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> { 493 self._fields(store.into().0) 494 } 495 _fields<'a>( &'a self, store: &'a mut StoreOpaque, ) -> Result<impl ExactSizeIterator<Item = Val> + 'a>496 pub(crate) fn _fields<'a>( 497 &'a self, 498 store: &'a mut StoreOpaque, 499 ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> { 500 assert!(self.comes_from_same_store(store)); 501 let store = AutoAssertNoGc::new(store); 502 503 let gc_ref = self.inner.try_gc_ref(&store)?; 504 let header = store.require_gc_store()?.header(gc_ref); 505 debug_assert!(header.kind().matches(VMGcKind::ExnRef)); 506 507 let index = header.ty().expect("exnrefs should have concrete types"); 508 let ty = ExnType::from_shared_type_index(store.engine(), index); 509 let len = ty.fields().len(); 510 511 return Ok(Fields { 512 exnref: self, 513 store, 514 index: 0, 515 len, 516 }); 517 518 struct Fields<'a, 'b> { 519 exnref: &'a ExnRef, 520 store: AutoAssertNoGc<'b>, 521 index: usize, 522 len: usize, 523 } 524 525 impl Iterator for Fields<'_, '_> { 526 type Item = Val; 527 528 #[inline] 529 fn next(&mut self) -> Option<Self::Item> { 530 let i = self.index; 531 debug_assert!(i <= self.len); 532 if i >= self.len { 533 return None; 534 } 535 self.index += 1; 536 Some(self.exnref._field(&mut self.store, i).unwrap()) 537 } 538 539 #[inline] 540 fn size_hint(&self) -> (usize, Option<usize>) { 541 let len = self.len - self.index; 542 (len, Some(len)) 543 } 544 } 545 546 impl ExactSizeIterator for Fields<'_, '_> { 547 #[inline] 548 fn len(&self) -> usize { 549 self.len - self.index 550 } 551 } 552 } 553 header<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMGcHeader>554 fn header<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMGcHeader> { 555 assert!(self.comes_from_same_store(&store)); 556 let gc_ref = self.inner.try_gc_ref(store)?; 557 Ok(store.require_gc_store()?.header(gc_ref)) 558 } 559 exnref<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMExnRef>560 fn exnref<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMExnRef> { 561 assert!(self.comes_from_same_store(&store)); 562 let gc_ref = self.inner.try_gc_ref(store)?; 563 debug_assert!(self.header(store)?.kind().matches(VMGcKind::ExnRef)); 564 Ok(gc_ref.as_exnref_unchecked()) 565 } 566 layout(&self, store: &AutoAssertNoGc<'_>) -> Result<Arc<GcStructLayout>>567 fn layout(&self, store: &AutoAssertNoGc<'_>) -> Result<Arc<GcStructLayout>> { 568 assert!(self.comes_from_same_store(&store)); 569 let type_index = self.type_index(store)?; 570 let layout = store 571 .engine() 572 .signatures() 573 .layout(type_index) 574 .expect("exn types should have GC layouts"); 575 match layout { 576 GcLayout::Struct(s) => Ok(s), 577 GcLayout::Array(_) => unreachable!(), 578 } 579 } 580 field_ty(&self, store: &StoreOpaque, field: usize) -> Result<FieldType>581 fn field_ty(&self, store: &StoreOpaque, field: usize) -> Result<FieldType> { 582 let ty = self._ty(store)?; 583 match ty.field(field) { 584 Some(f) => Ok(f), 585 None => { 586 let len = ty.fields().len(); 587 bail!("cannot access field {field}: exn only has {len} fields") 588 } 589 } 590 } 591 592 /// Get this exception object's `index`th field. 593 /// 594 /// # Errors 595 /// 596 /// Returns an `Err(_)` if the index is out of bounds or this reference has 597 /// been unrooted. 598 /// 599 /// # Panics 600 /// 601 /// Panics if this reference is associated with a different store. field(&self, mut store: impl AsContextMut, index: usize) -> Result<Val>602 pub fn field(&self, mut store: impl AsContextMut, index: usize) -> Result<Val> { 603 let mut store = AutoAssertNoGc::new(store.as_context_mut().0); 604 self._field(&mut store, index) 605 } 606 _field(&self, store: &mut AutoAssertNoGc<'_>, index: usize) -> Result<Val>607 pub(crate) fn _field(&self, store: &mut AutoAssertNoGc<'_>, index: usize) -> Result<Val> { 608 assert!(self.comes_from_same_store(store)); 609 let exnref = self.exnref(store)?.unchecked_copy(); 610 let field_ty = self.field_ty(store, index)?; 611 let layout = self.layout(store)?; 612 Ok(exnref.read_field(store, &layout, field_ty.element_type(), index)) 613 } 614 615 /// Get this exception object's associated tag. 616 /// 617 /// # Errors 618 /// 619 /// Returns an `Err(_)` if this reference has been unrooted. 620 /// 621 /// # Panics 622 /// 623 /// Panics if this reference is associated with a different store. tag(&self, mut store: impl AsContextMut) -> Result<Tag>624 pub fn tag(&self, mut store: impl AsContextMut) -> Result<Tag> { 625 let mut store = AutoAssertNoGc::new(store.as_context_mut().0); 626 assert!(self.comes_from_same_store(&store)); 627 let exnref = self.exnref(&store)?.unchecked_copy(); 628 let (instance, index) = exnref.tag(&mut store)?; 629 Ok(Tag::from_raw_indices(&*store, instance, index)) 630 } 631 } 632 633 unsafe impl WasmTy for Rooted<ExnRef> { 634 #[inline] valtype() -> ValType635 fn valtype() -> ValType { 636 ValType::Ref(RefType::new(false, HeapType::Exn)) 637 } 638 639 #[inline] compatible_with_store(&self, store: &StoreOpaque) -> bool640 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 641 self.comes_from_same_store(store) 642 } 643 644 #[inline] dynamic_concrete_type_check( &self, _store: &StoreOpaque, _nullable: bool, _ty: &HeapType, ) -> Result<()>645 fn dynamic_concrete_type_check( 646 &self, 647 _store: &StoreOpaque, 648 _nullable: bool, 649 _ty: &HeapType, 650 ) -> Result<()> { 651 // Wasm can't specify a concrete exn type, so there are no 652 // dynamic checks. 653 Ok(()) 654 } 655 store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()>656 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 657 self.wasm_ty_store(store, ptr, ValRaw::anyref) 658 } 659 load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self660 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 661 Self::wasm_ty_load(store, ptr.get_anyref(), ExnRef::from_cloned_gc_ref) 662 } 663 } 664 665 unsafe impl WasmTy for Option<Rooted<ExnRef>> { 666 #[inline] valtype() -> ValType667 fn valtype() -> ValType { 668 ValType::EXNREF 669 } 670 671 #[inline] compatible_with_store(&self, store: &StoreOpaque) -> bool672 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 673 self.map_or(true, |x| x.comes_from_same_store(store)) 674 } 675 676 #[inline] dynamic_concrete_type_check( &self, store: &StoreOpaque, nullable: bool, ty: &HeapType, ) -> Result<()>677 fn dynamic_concrete_type_check( 678 &self, 679 store: &StoreOpaque, 680 nullable: bool, 681 ty: &HeapType, 682 ) -> Result<()> { 683 match self { 684 Some(a) => a.ensure_matches_ty(store, ty), 685 None => { 686 ensure!( 687 nullable, 688 "expected a non-null reference, but found a null reference" 689 ); 690 Ok(()) 691 } 692 } 693 } 694 695 #[inline] is_vmgcref_and_points_to_object(&self) -> bool696 fn is_vmgcref_and_points_to_object(&self) -> bool { 697 self.is_some() 698 } 699 store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()>700 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 701 <Rooted<ExnRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref) 702 } 703 load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self704 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 705 <Rooted<ExnRef>>::wasm_ty_option_load(store, ptr.get_anyref(), ExnRef::from_cloned_gc_ref) 706 } 707 } 708 709 unsafe impl WasmTy for OwnedRooted<ExnRef> { 710 #[inline] valtype() -> ValType711 fn valtype() -> ValType { 712 ValType::Ref(RefType::new(false, HeapType::Exn)) 713 } 714 715 #[inline] compatible_with_store(&self, store: &StoreOpaque) -> bool716 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 717 self.comes_from_same_store(store) 718 } 719 720 #[inline] dynamic_concrete_type_check( &self, store: &StoreOpaque, _nullable: bool, ty: &HeapType, ) -> Result<()>721 fn dynamic_concrete_type_check( 722 &self, 723 store: &StoreOpaque, 724 _nullable: bool, 725 ty: &HeapType, 726 ) -> Result<()> { 727 self.ensure_matches_ty(store, ty) 728 } 729 store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()>730 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 731 self.wasm_ty_store(store, ptr, ValRaw::anyref) 732 } 733 load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self734 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 735 Self::wasm_ty_load(store, ptr.get_anyref(), ExnRef::from_cloned_gc_ref) 736 } 737 } 738 739 unsafe impl WasmTy for Option<OwnedRooted<ExnRef>> { 740 #[inline] valtype() -> ValType741 fn valtype() -> ValType { 742 ValType::EXNREF 743 } 744 745 #[inline] compatible_with_store(&self, store: &StoreOpaque) -> bool746 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 747 self.as_ref() 748 .map_or(true, |x| x.comes_from_same_store(store)) 749 } 750 751 #[inline] dynamic_concrete_type_check( &self, store: &StoreOpaque, nullable: bool, ty: &HeapType, ) -> Result<()>752 fn dynamic_concrete_type_check( 753 &self, 754 store: &StoreOpaque, 755 nullable: bool, 756 ty: &HeapType, 757 ) -> Result<()> { 758 match self { 759 Some(a) => a.ensure_matches_ty(store, ty), 760 None => { 761 ensure!( 762 nullable, 763 "expected a non-null reference, but found a null reference" 764 ); 765 Ok(()) 766 } 767 } 768 } 769 770 #[inline] is_vmgcref_and_points_to_object(&self) -> bool771 fn is_vmgcref_and_points_to_object(&self) -> bool { 772 self.is_some() 773 } 774 store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()>775 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 776 <OwnedRooted<ExnRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref) 777 } 778 load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self779 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 780 <OwnedRooted<ExnRef>>::wasm_ty_option_load( 781 store, 782 ptr.get_anyref(), 783 ExnRef::from_cloned_gc_ref, 784 ) 785 } 786 } 787