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