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