1 use super::structref::{initialize_field_impl, read_field_impl}; 2 use crate::{ 3 StorageType, Val, 4 prelude::*, 5 runtime::vm::{GcHeap, GcStore, VMGcRef}, 6 store::{AutoAssertNoGc, InstanceId}, 7 }; 8 use core::fmt; 9 use wasmtime_environ::{DefinedTagIndex, GcStructLayout, VMGcKind}; 10 11 /// A `VMGcRef` that we know points to an `exn`. 12 /// 13 /// Create a `VMExnRef` via `VMGcRef::into_exnref` and 14 /// `VMGcRef::as_exnref`, or their untyped equivalents 15 /// `VMGcRef::into_exnref_unchecked` and `VMGcRef::as_exnref_unchecked`. 16 /// 17 /// Note: This is not a `TypedGcRef<_>` because each collector can have a 18 /// different concrete representation of `exnref` that they allocate inside 19 /// their heaps. 20 #[derive(Debug, PartialEq, Eq, Hash)] 21 #[repr(transparent)] 22 pub struct VMExnRef(VMGcRef); 23 24 impl fmt::Pointer for VMExnRef { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result25 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 26 fmt::Pointer::fmt(&self.0, f) 27 } 28 } 29 30 impl From<VMExnRef> for VMGcRef { 31 #[inline] from(x: VMExnRef) -> Self32 fn from(x: VMExnRef) -> Self { 33 x.0 34 } 35 } 36 37 impl VMGcRef { 38 /// Is this `VMGcRef` pointing to an `exn`? is_exnref(&self, gc_heap: &(impl GcHeap + ?Sized)) -> bool39 pub fn is_exnref(&self, gc_heap: &(impl GcHeap + ?Sized)) -> bool { 40 if self.is_i31() { 41 return false; 42 } 43 44 let header = gc_heap.header(&self); 45 header.kind().matches(VMGcKind::ExnRef) 46 } 47 48 /// Create a new `VMExnRef` from the given `gc_ref`. 49 /// 50 /// If this is not a GC reference to an `exnref`, `Err(self)` is 51 /// returned. into_exnref(self, gc_heap: &(impl GcHeap + ?Sized)) -> Result<VMExnRef, VMGcRef>52 pub fn into_exnref(self, gc_heap: &(impl GcHeap + ?Sized)) -> Result<VMExnRef, VMGcRef> { 53 if self.is_exnref(gc_heap) { 54 Ok(self.into_exnref_unchecked()) 55 } else { 56 Err(self) 57 } 58 } 59 60 /// Create a new `VMExnRef` from `self` without actually checking that 61 /// `self` is an `exnref`. 62 /// 63 /// This method does not check that `self` is actually an `exnref`, but 64 /// it should be. Failure to uphold this invariant is memory safe but will 65 /// result in general incorrectness down the line such as panics or wrong 66 /// results. 67 #[inline] into_exnref_unchecked(self) -> VMExnRef68 pub fn into_exnref_unchecked(self) -> VMExnRef { 69 debug_assert!(!self.is_i31()); 70 VMExnRef(self) 71 } 72 73 /// Get this GC reference as an `exnref` reference, if it actually is an 74 /// `exnref` reference. as_exnref(&self, gc_heap: &(impl GcHeap + ?Sized)) -> Option<&VMExnRef>75 pub fn as_exnref(&self, gc_heap: &(impl GcHeap + ?Sized)) -> Option<&VMExnRef> { 76 if self.is_exnref(gc_heap) { 77 Some(self.as_exnref_unchecked()) 78 } else { 79 None 80 } 81 } 82 83 /// Get this GC reference as an `exnref` reference without checking if it 84 /// actually is an `exnref` reference. 85 /// 86 /// Calling this method on a non-`exnref` reference is memory safe, but 87 /// will lead to general incorrectness like panics and wrong results. as_exnref_unchecked(&self) -> &VMExnRef88 pub fn as_exnref_unchecked(&self) -> &VMExnRef { 89 debug_assert!(!self.is_i31()); 90 let ptr = self as *const VMGcRef; 91 let ret = unsafe { &*ptr.cast() }; 92 assert!(matches!(ret, VMExnRef(VMGcRef { .. }))); 93 ret 94 } 95 } 96 97 impl VMExnRef { 98 /// Get the underlying `VMGcRef`. as_gc_ref(&self) -> &VMGcRef99 pub fn as_gc_ref(&self) -> &VMGcRef { 100 &self.0 101 } 102 103 /// Get a mutable borrow on the underlying `VMGcRef`. 104 /// 105 /// Requires that the mutation retains the reference's invariants, 106 /// namely: not null, and pointing to a valid exnref object. Doing 107 /// otherwise is memory safe, but will lead to general 108 /// incorrectness. as_gc_ref_mut(&mut self) -> &mut VMGcRef109 pub fn as_gc_ref_mut(&mut self) -> &mut VMGcRef { 110 &mut self.0 111 } 112 113 /// Clone this `VMExnRef`, running any GC barriers as necessary. clone(&self, gc_store: &mut GcStore) -> Self114 pub fn clone(&self, gc_store: &mut GcStore) -> Self { 115 Self(gc_store.clone_gc_ref(&self.0)) 116 } 117 118 /// Explicitly drop this `exnref`, running GC drop barriers as necessary. drop(self, gc_store: &mut GcStore)119 pub fn drop(self, gc_store: &mut GcStore) { 120 gc_store.drop_gc_ref(self.0); 121 } 122 123 /// Copy this `VMExnRef` without running the GC's clone barriers. 124 /// 125 /// Prefer calling `clone(&mut GcStore)` instead! This is mostly an internal 126 /// escape hatch for collector implementations. 127 /// 128 /// Failure to run GC barriers when they would otherwise be necessary can 129 /// lead to leaks, panics, and wrong results. It cannot lead to memory 130 /// unsafety, however. unchecked_copy(&self) -> Self131 pub fn unchecked_copy(&self) -> Self { 132 Self(self.0.unchecked_copy()) 133 } 134 135 /// Read a field of the given `StorageType` into a `Val`. 136 /// 137 /// `i8` and `i16` fields are zero-extended into `Val::I32(_)`s. 138 /// 139 /// Does not check that the field is actually of type `ty`. That is the 140 /// caller's responsibility. Failure to do so is memory safe, but will lead 141 /// to general incorrectness such as panics and wrong results. 142 /// 143 /// Panics on out-of-bounds accesses. read_field( &self, store: &mut AutoAssertNoGc, layout: &GcStructLayout, ty: &StorageType, field: usize, ) -> Val144 pub fn read_field( 145 &self, 146 store: &mut AutoAssertNoGc, 147 layout: &GcStructLayout, 148 ty: &StorageType, 149 field: usize, 150 ) -> Val { 151 let offset = layout.fields[field].offset; 152 read_field_impl(self.as_gc_ref(), store, ty, offset) 153 } 154 155 /// Initialize a field in this exnref that is currently uninitialized. 156 /// 157 /// Calling this method on an exnref that has already had the 158 /// associated field initialized will result in GC bugs. These are 159 /// memory safe but will lead to generally incorrect behavior such 160 /// as panics, leaks, and incorrect results. 161 /// 162 /// Does not check that `val` matches `ty`, nor that the field is actually 163 /// of type `ty`. Checking those things is the caller's responsibility. 164 /// Failure to do so is memory safe, but will lead to general incorrectness 165 /// such as panics and wrong results. 166 /// 167 /// Returns an error if `val` is a GC reference that has since been 168 /// unrooted. 169 /// 170 /// Panics on out-of-bounds accesses. initialize_field( &self, store: &mut AutoAssertNoGc, layout: &GcStructLayout, ty: &StorageType, field: usize, val: Val, ) -> Result<()>171 pub fn initialize_field( 172 &self, 173 store: &mut AutoAssertNoGc, 174 layout: &GcStructLayout, 175 ty: &StorageType, 176 field: usize, 177 val: Val, 178 ) -> Result<()> { 179 debug_assert!(val._matches_ty(&store, &ty.unpack())?); 180 let offset = layout.fields[field].offset; 181 initialize_field_impl(self.as_gc_ref(), store, ty, offset, val) 182 } 183 184 /// Initialize the tag referenced by this exception object. initialize_tag( &self, store: &mut AutoAssertNoGc, instance: InstanceId, tag: DefinedTagIndex, ) -> Result<()>185 pub fn initialize_tag( 186 &self, 187 store: &mut AutoAssertNoGc, 188 instance: InstanceId, 189 tag: DefinedTagIndex, 190 ) -> Result<()> { 191 let layouts = store.engine().gc_runtime().unwrap().layouts(); 192 let instance_offset = layouts.exception_tag_instance_offset(); 193 let tag_offset = layouts.exception_tag_defined_offset(); 194 let store = store.require_gc_store_mut()?; 195 store 196 .gc_object_data(&self.0) 197 .write_u32(instance_offset, instance.as_u32()); 198 store 199 .gc_object_data(&self.0) 200 .write_u32(tag_offset, tag.as_u32()); 201 Ok(()) 202 } 203 204 /// Get the tag referenced by this exception object. tag(&self, store: &mut AutoAssertNoGc) -> Result<(InstanceId, DefinedTagIndex)>205 pub fn tag(&self, store: &mut AutoAssertNoGc) -> Result<(InstanceId, DefinedTagIndex)> { 206 let layouts = store.engine().gc_runtime().unwrap().layouts(); 207 let instance_offset = layouts.exception_tag_instance_offset(); 208 let tag_offset = layouts.exception_tag_defined_offset(); 209 let instance = store 210 .require_gc_store_mut()? 211 .gc_object_data(&self.0) 212 .read_u32(instance_offset); 213 let instance = InstanceId::from_u32(instance); 214 let store = store.require_gc_store_mut()?; 215 let tag = store.gc_object_data(&self.0).read_u32(tag_offset); 216 let tag = DefinedTagIndex::from_u32(tag); 217 Ok((instance, tag)) 218 } 219 } 220