Skip to main content

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}