Skip to main content

miniextendr_api/trait_abi/
view_data.rs

1//! Shared storage for the pointers and optional R owner of a generated view.
2
3use crate::SEXP;
4use crate::externalptr::{root_owned, unroot_owned};
5use crate::protect_pool::ProtectKey;
6use std::ffi::c_void;
7
8/// The checked construction boundary of a generated trait view.
9///
10/// Pointer fields live here, rather than in the expansion's module: even
11/// code beside the generated view cannot construct or overwrite them safely.
12/// An R owner is rooted in the same pool as owning `ExternalPtr` handles,
13/// independently of the PROTECT stack, so views can outlive their creating
14/// scope and be dropped in any order.
15/// Raw pointers keep this type (and every generated view) `!Send` and `!Sync`.
16#[doc(hidden)]
17pub struct TraitViewData<V> {
18    data: *mut c_void,
19    vtable: *const V,
20    owner: Option<(SEXP, ProtectKey)>,
21}
22
23impl<V> TraitViewData<V> {
24    /// Construct a view's backing storage.
25    ///
26    /// # Safety
27    ///
28    /// `data` and `vtable` must be valid, non-null pointers matching the
29    /// generated trait view. The vtable must outlive this value. When supplied,
30    /// `owner` must own `data`; otherwise the caller must keep `data` alive.
31    /// Calls must respect Rust's aliasing rules. Construction, use and drop
32    /// must run on R's main thread, inside an R unwind boundary when allocating.
33    pub unsafe fn new(data: *mut c_void, vtable: *const c_void, owner: Option<SEXP>) -> Self {
34        let owner = owner.map(|owner| (owner, root_owned(owner)));
35        Self {
36            data,
37            vtable: vtable.cast(),
38            owner,
39        }
40    }
41
42    /// The concrete data pointer, retaining its original mutable provenance.
43    pub fn data(&self) -> *mut c_void {
44        self.data
45    }
46
47    /// The interface vtable. Dereferencing it remains an unsafe operation.
48    pub fn vtable(&self) -> *const V {
49        self.vtable
50    }
51
52    /// The rooted R receiver, which owner-receiver methods pass to their shim.
53    /// `None` for a raw view built without an owner.
54    pub fn owner(&self) -> Option<SEXP> {
55        self.owner.map(|(owner, _)| owner)
56    }
57}
58
59impl<V> Drop for TraitViewData<V> {
60    fn drop(&mut self) {
61        if let Some((_, key)) = self.owner.take() {
62            // This type cannot leave the main thread it was built on, and
63            // releasing a pool slot neither allocates nor runs finalizers.
64            unroot_owned(key);
65        }
66    }
67}