Skip to main content

extendr_ffi/
backports.rs

1//! Backport functions
2//!
3//! R's C API is changing and is stabilizing. As such some functions
4//! that were available in previous versions of R are not available
5//! in later versions of R, or they cause a warning in `R CMD check`.
6//!
7//! Use the functions in this module to ensure backwards compatibility.
8
9// R 4.5 backports notes saved in wayback machine here:
10// https://web.archive.org/web/20250325171443/https://rstudio.github.io/r-manuals/r-exts/The-R-API.html#moving-into-c-api-compliance
11
12use crate::{Rboolean, SEXP};
13
14#[cfg(not(r_4_5))]
15use crate::R_UnboundValue;
16
17extern "C" {
18    #[cfg(not(r_4_5))]
19    fn ENCLOS(x: SEXP) -> SEXP;
20
21    #[cfg(r_4_5)]
22    fn R_ParentEnv(x: SEXP) -> SEXP;
23
24    #[cfg(not(r_4_5))]
25    fn Rf_findVar(arg1: SEXP, arg2: SEXP) -> SEXP;
26
27    #[cfg(r_4_5)]
28    fn R_getVar(arg1: SEXP, arg2: SEXP) -> SEXP;
29
30    #[cfg(not(r_4_5))]
31    fn Rf_findVarInFrame(arg1: SEXP, arg2: SEXP) -> SEXP;
32
33    #[cfg(r_4_5)]
34    fn R_getVarEx(arg1: SEXP, arg2: SEXP) -> SEXP;
35
36    #[cfg(not(r_4_5))]
37    fn CLOENV(x: SEXP) -> SEXP;
38
39    #[cfg(r_4_5)]
40    fn R_ClosureEnv(x: SEXP) -> SEXP;
41
42    #[cfg(not(r_4_5))]
43    fn BODY(x: SEXP) -> SEXP;
44
45    #[cfg(r_4_5)]
46    fn R_ClosureBody(x: SEXP) -> SEXP;
47
48    #[cfg(not(r_4_5))]
49    fn FORMALS(x: SEXP) -> SEXP;
50
51    #[cfg(r_4_5)]
52    fn R_ClosureFormals(x: SEXP) -> SEXP;
53
54    #[cfg(not(r_4_5))]
55    fn DATAPTR(x: SEXP) -> *mut ::std::os::raw::c_void;
56
57    #[cfg(r_4_5)]
58    fn DATAPTR_RO(x: SEXP) -> *const ::std::os::raw::c_void;
59
60    #[cfg(not(r_4_5))]
61    fn Rf_isFrame(x: SEXP) -> Rboolean;
62
63    #[cfg(r_4_5)]
64    fn Rf_isDataFrame(x: SEXP) -> Rboolean;
65}
66
67/// Returns the enclosing environment of env, which will usually be of type ENVSXP, except for the special environment R_EmptyEnv, which terminates the environment chain; its enclosing environment is R_NilValue.
68///
69/// # Safety
70///
71/// This function dereferences a raw SEXP pointer.
72/// The caller must ensure that `x` is a valid SEXP pointer to an environment.
73#[inline]
74pub unsafe fn get_parent_env(x: SEXP) -> SEXP {
75    #[cfg(not(r_4_5))]
76    {
77        ENCLOS(x)
78    }
79    #[cfg(r_4_5)]
80    {
81        R_ParentEnv(x)
82    }
83}
84
85/// Returns a variable from an environment
86///
87/// # Safety
88///
89/// This function dereferences raw SEXP pointers.
90/// The caller must ensure that `symbol` and `env` are valid SEXP pointers.
91#[inline]
92pub unsafe fn get_var(symbol: SEXP, env: SEXP) -> SEXP {
93    #[cfg(not(r_4_5))]
94    {
95        Rf_findVar(symbol, env)
96    }
97    #[cfg(r_4_5)]
98    {
99        R_getVar(symbol, env)
100    }
101}
102
103/// Returns a variable from an environment, or `None` if unbound.
104///
105/// On R < 4.5, uses `Rf_findVar` and checks against `R_UnboundValue`.
106/// On R >= 4.5, uses `R_getVar` which signals an error if not found;
107/// callers should wrap this with `catch_r_error` if needed.
108///
109/// # Safety
110///
111/// This function dereferences raw SEXP pointers.
112/// The caller must ensure that `symbol` and `env` are valid SEXP pointers.
113#[inline]
114pub unsafe fn get_var_safe(symbol: SEXP, env: SEXP) -> Option<SEXP> {
115    #[cfg(not(r_4_5))]
116    {
117        let var = Rf_findVar(symbol, env);
118        if var == R_UnboundValue {
119            None
120        } else {
121            Some(var)
122        }
123    }
124    #[cfg(r_4_5)]
125    {
126        Some(R_getVar(symbol, env))
127    }
128}
129
130/// Returns the value of the requested variable in an environment
131///
132/// # Safety
133///
134/// This function dereferences raw SEXP pointers.
135/// The caller must ensure that `symbol` and `env` are valid SEXP pointers.
136#[inline]
137pub unsafe fn get_var_in_frame(symbol: SEXP, env: SEXP) -> SEXP {
138    #[cfg(not(r_4_5))]
139    {
140        Rf_findVarInFrame(symbol, env)
141    }
142    #[cfg(r_4_5)]
143    {
144        R_getVarEx(env, symbol)
145    }
146}
147
148/// Return the environment of a closure
149///
150/// # Safety
151///
152/// This function dereferences a raw SEXP pointer.
153/// The caller must ensure that `x` is a valid SEXP pointer to a closure.
154#[inline]
155pub unsafe fn get_closure_env(x: SEXP) -> SEXP {
156    #[cfg(not(r_4_5))]
157    {
158        CLOENV(x)
159    }
160    #[cfg(r_4_5)]
161    {
162        R_ClosureEnv(x)
163    }
164}
165
166/// Return the body of a closure
167///
168/// # Safety
169///
170/// This function dereferences a raw SEXP pointer.
171/// The caller must ensure that `x` is a valid SEXP pointer to a closure.
172#[inline]
173pub unsafe fn get_closure_body(x: SEXP) -> SEXP {
174    #[cfg(not(r_4_5))]
175    {
176        BODY(x)
177    }
178    #[cfg(r_4_5)]
179    {
180        R_ClosureBody(x)
181    }
182}
183
184/// Access a closure's arguments
185///
186/// # Safety
187///
188/// This function dereferences a raw SEXP pointer.
189/// The caller must ensure that `x` is a valid SEXP pointer to a closure.
190#[inline]
191pub unsafe fn get_closure_formals(x: SEXP) -> SEXP {
192    #[cfg(not(r_4_5))]
193    {
194        FORMALS(x)
195    }
196    #[cfg(r_4_5)]
197    {
198        R_ClosureFormals(x)
199    }
200}
201
202/// Access a DATAPTR
203///
204/// # Safety
205///
206/// This function dereferences a raw SEXP pointer.
207/// The caller must ensure that `x` is a valid SEXP pointer.
208#[inline]
209pub unsafe fn dataptr(x: SEXP) -> *const ::std::os::raw::c_void {
210    #[cfg(not(r_4_5))]
211    {
212        DATAPTR(x) as *const _
213    }
214    #[cfg(r_4_5)]
215    {
216        DATAPTR_RO(x)
217    }
218}
219
220/// Check is data.frame
221///
222/// # Safety
223///
224/// This function dereferences a raw SEXP pointer.
225/// The caller must ensure that `x` is a valid SEXP pointer.
226#[inline]
227pub unsafe fn is_data_frame(x: SEXP) -> Rboolean {
228    #[cfg(not(r_4_5))]
229    {
230        Rf_isFrame(x)
231    }
232    #[cfg(r_4_5)]
233    {
234        Rf_isDataFrame(x)
235    }
236}