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}