1 use crate::runtime::vm::{GcHeap, GcStore, VMGcRef}; 2 use core::fmt; 3 use wasmtime_environ::VMGcKind; 4 5 /// A `VMGcRef` that we know points to an `externref`. 6 /// 7 /// Create a `VMExternRef` via `VMGcRef::into_externref` and 8 /// `VMGcRef::as_externref`, or their untyped equivalents 9 /// `VMGcRef::into_externref_unchecked` and `VMGcRef::as_externref_unchecked`. 10 /// 11 /// Note: This is not a `TypedGcRef<_>` because each collector can have a 12 /// different concrete representation of `externref` that they allocate inside 13 /// their heaps. 14 #[derive(Debug, PartialEq, Eq, Hash)] 15 #[repr(transparent)] 16 pub struct VMExternRef(VMGcRef); 17 18 impl fmt::Pointer for VMExternRef { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result19 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 20 fmt::Pointer::fmt(&self.0, f) 21 } 22 } 23 24 impl From<VMExternRef> for VMGcRef { 25 #[inline] from(x: VMExternRef) -> Self26 fn from(x: VMExternRef) -> Self { 27 x.0 28 } 29 } 30 31 impl VMGcRef { 32 /// Create a new `VMExternRef` from the given `gc_ref`. 33 /// 34 /// If this is not GC reference to an `externref`, `Err(self)` is returned. into_externref(self, gc_heap: &impl GcHeap) -> Result<VMExternRef, VMGcRef>35 pub fn into_externref(self, gc_heap: &impl GcHeap) -> Result<VMExternRef, VMGcRef> { 36 if self.is_i31() { 37 return Err(self); 38 } 39 if gc_heap.header(&self).kind() == VMGcKind::ExternRef { 40 Ok(VMExternRef(self)) 41 } else { 42 Err(self) 43 } 44 } 45 46 /// Create a new `VMExternRef` from `self` without actually checking that 47 /// `self` is an `externref`. 48 /// 49 /// This method does not check that `self` is actually an `externref`, but 50 /// it should be. Failure to uphold this invariant is memory safe but will 51 /// result in general incorrectness down the line such as panics or wrong 52 /// results. 53 #[inline] into_externref_unchecked(self) -> VMExternRef54 pub fn into_externref_unchecked(self) -> VMExternRef { 55 debug_assert!(!self.is_i31()); 56 VMExternRef(self) 57 } 58 59 /// Get this GC reference as an `externref` reference, if it actually is an 60 /// `externref` reference. as_externref(&self, gc_heap: &(impl GcHeap + ?Sized)) -> Option<&VMExternRef>61 pub fn as_externref(&self, gc_heap: &(impl GcHeap + ?Sized)) -> Option<&VMExternRef> { 62 if self.is_i31() { 63 return None; 64 } 65 if gc_heap.header(&self).kind() == VMGcKind::ExternRef { 66 let ptr = self as *const VMGcRef; 67 let ret = unsafe { &*ptr.cast() }; 68 assert!(matches!(ret, VMExternRef(VMGcRef { .. }))); 69 Some(ret) 70 } else { 71 None 72 } 73 } 74 } 75 76 impl VMExternRef { 77 /// Get the underlying `VMGcRef`. as_gc_ref(&self) -> &VMGcRef78 pub fn as_gc_ref(&self) -> &VMGcRef { 79 &self.0 80 } 81 82 /// Clone this `VMExternRef`, running any GC barriers as necessary. clone(&self, gc_store: &mut GcStore) -> Self83 pub fn clone(&self, gc_store: &mut GcStore) -> Self { 84 Self(gc_store.clone_gc_ref(&self.0)) 85 } 86 87 /// Explicitly drop this `externref`, running GC drop barriers as necessary. drop(self, gc_store: &mut GcStore)88 pub fn drop(self, gc_store: &mut GcStore) { 89 gc_store.drop_gc_ref(self.0); 90 } 91 92 /// Copy this `VMExternRef` without running the GC's clone barriers. 93 /// 94 /// Prefer calling `clone(&mut GcStore)` instead! This is mostly an internal 95 /// escape hatch for collector implementations. 96 /// 97 /// Failure to run GC barriers when they would otherwise be necessary can 98 /// lead to leaks, panics, and wrong results. It cannot lead to memory 99 /// unsafety, however. unchecked_copy(&self) -> Self100 pub fn unchecked_copy(&self) -> Self { 101 Self(self.0.unchecked_copy()) 102 } 103 } 104