extendr_api/robj/
rinternals.rs

1use crate::*;
2use extendr_ffi::{
3    get_var, R_CFinalizer_t, R_ExternalPtrAddr, R_ExternalPtrProtected, R_ExternalPtrTag,
4    R_GetCurrentSrcref, R_GetSrcFilename, R_IsNamespaceEnv, R_IsPackageEnv, R_MakeExternalPtr,
5    R_MissingArg, R_NamespaceEnvSpec, R_PackageEnvName, R_RegisterCFinalizerEx, R_UnboundValue,
6    R_xlen_t, Rboolean, Rf_PairToVectorList, Rf_VectorToPairList, Rf_allocMatrix, Rf_allocVector,
7    Rf_asChar, Rf_asCharacterFactor, Rf_coerceVector, Rf_conformable, Rf_duplicate, Rf_findFun,
8    Rf_isArray, Rf_isComplex, Rf_isEnvironment, Rf_isExpression, Rf_isFactor, Rf_isFrame,
9    Rf_isFunction, Rf_isInteger, Rf_isLanguage, Rf_isList, Rf_isLogical, Rf_isMatrix, Rf_isNewList,
10    Rf_isNull, Rf_isNumber, Rf_isObject, Rf_isPrimitive, Rf_isReal, Rf_isS4, Rf_isString,
11    Rf_isSymbol, Rf_isTs, Rf_isUserBinop, Rf_isVector, Rf_isVectorAtomic, Rf_isVectorList,
12    Rf_isVectorizable, Rf_ncols, Rf_nrows, Rf_xlengthgets, ALTREP, TYPEOF,
13};
14///////////////////////////////////////////////////////////////
15/// The following impls wrap specific Rinternals.h functions.
16///
17pub trait Rinternals: Types + Conversions {
18    /// Return true if this is the null object.
19    fn is_null(&self) -> bool {
20        unsafe { Rf_isNull(self.get()).into() }
21    }
22
23    /// Return true if this is a symbol.
24    fn is_symbol(&self) -> bool {
25        unsafe { Rf_isSymbol(self.get()).into() }
26    }
27
28    /// Return true if this is a boolean (logical) vector
29    fn is_logical(&self) -> bool {
30        unsafe { Rf_isLogical(self.get()).into() }
31    }
32
33    /// Return true if this is a real (f64) vector.
34    fn is_real(&self) -> bool {
35        unsafe { Rf_isReal(self.get()).into() }
36    }
37
38    /// Return true if this is a complex vector.
39    fn is_complex(&self) -> bool {
40        unsafe { Rf_isComplex(self.get()).into() }
41    }
42
43    /// Return true if this is an expression.
44    fn is_expressions(&self) -> bool {
45        unsafe { Rf_isExpression(self.get()).into() }
46    }
47
48    /// Return true if this is an environment.
49    fn is_environment(&self) -> bool {
50        unsafe { Rf_isEnvironment(self.get()).into() }
51    }
52
53    /// Return true if this is an environment.
54    fn is_promise(&self) -> bool {
55        self.sexptype() == SEXPTYPE::PROMSXP
56    }
57
58    /// Return true if this is a string.
59    fn is_string(&self) -> bool {
60        unsafe { Rf_isString(self.get()).into() }
61    }
62
63    /// Return true if this is an object (ie. has a class attribute).
64    fn is_object(&self) -> bool {
65        unsafe { Rf_isObject(self.get()).into() }
66    }
67
68    /// Return true if this is a S4 object.
69    fn is_s4(&self) -> bool {
70        unsafe { Rf_isS4(self.get()).into() }
71    }
72
73    /// Return true if this is an expression.
74    fn is_external_pointer(&self) -> bool {
75        self.rtype() == Rtype::ExternalPtr
76    }
77
78    /// Get the source ref.
79    fn get_current_srcref(val: i32) -> Robj {
80        unsafe { Robj::from_sexp(R_GetCurrentSrcref(val as std::ffi::c_int)) }
81    }
82
83    /// Get the source filename.
84    fn get_src_filename(&self) -> Robj {
85        unsafe { Robj::from_sexp(R_GetSrcFilename(self.get())) }
86    }
87
88    /// Convert to a string vector.
89    fn as_character_vector(&self) -> Robj {
90        unsafe { Robj::from_sexp(Rf_asChar(self.get())) }
91    }
92
93    /// Convert to vectors of many kinds.
94    fn coerce_vector(&self, sexptype: SEXPTYPE) -> Robj {
95        single_threaded(|| unsafe { Robj::from_sexp(Rf_coerceVector(self.get(), sexptype)) })
96    }
97
98    /// Convert a pairlist (LISTSXP) to a vector list (VECSXP).
99    fn pair_to_vector_list(&self) -> Robj {
100        single_threaded(|| unsafe { Robj::from_sexp(Rf_PairToVectorList(self.get())) })
101    }
102
103    /// Convert a vector list (VECSXP) to a pair list (LISTSXP)
104    fn vector_to_pair_list(&self) -> Robj {
105        single_threaded(|| unsafe { Robj::from_sexp(Rf_VectorToPairList(self.get())) })
106    }
107
108    /// Convert a factor to a string vector.
109    fn as_character_factor(&self) -> Robj {
110        single_threaded(|| unsafe { Robj::from_sexp(Rf_asCharacterFactor(self.get())) })
111    }
112
113    /// Allocate a matrix object.
114    fn alloc_matrix(sexptype: SEXPTYPE, rows: i32, cols: i32) -> Robj {
115        single_threaded(|| unsafe { Robj::from_sexp(Rf_allocMatrix(sexptype, rows, cols)) })
116    }
117
118    /// Do a deep copy of this object.
119    /// Note that clone() only adds a reference.
120    fn duplicate(&self) -> Robj {
121        single_threaded(|| unsafe { Robj::from_sexp(Rf_duplicate(self.get())) })
122    }
123
124    /// Find a function in an environment ignoring other variables.
125    ///
126    /// This evaulates promises if they are found.
127    ///
128    /// See also [global_function()].
129    /// ```
130    /// use extendr_api::prelude::*;
131    /// test! {
132    ///    let my_fun = base_env().find_function(sym!(ls)).unwrap();
133    ///    assert_eq!(my_fun.is_function(), true);
134    ///
135    ///    // Note: this may crash on some versions of windows which don't support unwinding.
136    ///    // assert!(base_env().find_function(sym!(qwertyuiop)).is_none());
137    /// }
138    /// ```
139    fn find_function<K: TryInto<Symbol, Error = Error>>(&self, key: K) -> Result<Robj> {
140        let key: Symbol = key.try_into()?;
141        if !self.is_environment() {
142            return Err(Error::NotFound(key.into()));
143        }
144        // This may be better:
145        // let mut env: Robj = self.into();
146        // loop {
147        //     if let Some(var) = env.local(&key) {
148        //         if let Some(var) = var.eval_promise() {
149        //             if var.is_function() {
150        //                 break Some(var);
151        //             }
152        //         }
153        //     }
154        //     if let Some(parent) = env.parent() {
155        //         env = parent;
156        //     } else {
157        //         break None;
158        //     }
159        // }
160        unsafe {
161            let sexp = self.get();
162            if let Ok(var) = catch_r_error(|| Rf_findFun(key.get(), sexp)) {
163                Ok(Robj::from_sexp(var))
164            } else {
165                Err(Error::NotFound(key.into()))
166            }
167        }
168    }
169
170    /// Find a variable in an environment.
171    ///
172    // //TODO: fix me, as this variable is hidden behind non-api as of this writing
173    // See also [global_var()].
174    ///
175    /// Note that many common variables and functions are contained in promises
176    /// which must be evaluated and this function may throw an R error.
177    ///
178    fn find_var<K: TryInto<Symbol, Error = Error>>(&self, key: K) -> Result<Robj> {
179        let key: Symbol = key.try_into()?;
180        if !self.is_environment() {
181            return Err(Error::NotFound(key.into()));
182        }
183        // Alternative:
184        // let mut env: Robj = self.into();
185        // loop {
186        //     if let Some(var) = env.local(&key) {
187        //         println!("v1={:?}", var);
188        //         if let Some(var) = var.eval_promise() {
189        //             println!("v2={:?}", var);
190        //             break Some(var);
191        //         }
192        //     }
193        //     if let Some(parent) = env.parent() {
194        //         env = parent;
195        //     } else {
196        //         break None;
197        //     }
198        // }
199        unsafe {
200            let sexp = self.get();
201            if let Ok(var) = catch_r_error(|| get_var(key.get(), sexp)) {
202                if var != R_UnboundValue {
203                    Ok(Robj::from_sexp(var))
204                } else {
205                    Err(Error::NotFound(key.into()))
206                }
207            } else {
208                Err(Error::NotFound(key.into()))
209            }
210        }
211    }
212
213    #[cfg(feature = "non-api")]
214    /// If this object is a promise, evaluate it, otherwise return the object.
215    /// ```
216    /// use extendr_api::prelude::*;
217    /// test! {
218    ///    let iris_promise = global_env().find_var(sym!(iris)).unwrap();
219    ///    let iris_dataframe = iris_promise.eval_promise().unwrap();
220    ///    assert_eq!(iris_dataframe.is_frame(), true);
221    /// }
222    /// ```
223    fn eval_promise(&self) -> Result<Robj> {
224        if self.is_promise() {
225            self.as_promise().unwrap().eval()
226        } else {
227            Ok(self.as_robj().clone())
228        }
229    }
230
231    /// Number of columns of a matrix
232    fn ncols(&self) -> usize {
233        unsafe { Rf_ncols(self.get()) as usize }
234    }
235
236    /// Number of rows of a matrix
237    fn nrows(&self) -> usize {
238        unsafe { Rf_nrows(self.get()) as usize }
239    }
240
241    /// Internal function used to implement `#[extendr]` impl
242    #[doc(hidden)]
243    unsafe fn make_external_ptr<T>(p: *mut T, prot: Robj) -> Robj {
244        let type_name: Robj = std::any::type_name::<T>().into();
245        Robj::from_sexp(single_threaded(|| {
246            R_MakeExternalPtr(
247                p as *mut ::std::os::raw::c_void,
248                type_name.get(),
249                prot.get(),
250            )
251        }))
252    }
253
254    /// Internal function used to implement `#[extendr]` impl
255    #[doc(hidden)]
256    unsafe fn external_ptr_addr<T>(&self) -> *mut T {
257        R_ExternalPtrAddr(self.get()).cast()
258    }
259
260    /// Internal function used to implement `#[extendr]` impl
261    #[doc(hidden)]
262    unsafe fn external_ptr_tag(&self) -> Robj {
263        Robj::from_sexp(R_ExternalPtrTag(self.get()))
264    }
265
266    /// Internal function used to implement `#[extendr]` impl
267    #[doc(hidden)]
268    unsafe fn external_ptr_protected(&self) -> Robj {
269        Robj::from_sexp(R_ExternalPtrProtected(self.get()))
270    }
271
272    #[doc(hidden)]
273    unsafe fn register_c_finalizer(&self, func: R_CFinalizer_t) {
274        // Use R_RegisterCFinalizerEx() and set onexit to 1 (TRUE) to invoke the
275        // finalizer on a shutdown of the R session as well.
276        single_threaded(|| R_RegisterCFinalizerEx(self.get(), func, Rboolean::TRUE));
277    }
278
279    /// Copy a vector and resize it.
280    /// See. <https://github.com/hadley/r-internals/blob/master/vectors.md>
281    fn xlengthgets(&self, new_len: usize) -> Result<Robj> {
282        unsafe {
283            if self.is_vector() {
284                Ok(single_threaded(|| {
285                    Robj::from_sexp(Rf_xlengthgets(self.get(), new_len as R_xlen_t))
286                }))
287            } else {
288                Err(Error::ExpectedVector(self.as_robj().clone()))
289            }
290        }
291    }
292
293    /// Allocated an owned object of a certain type.
294    fn alloc_vector(sexptype: SEXPTYPE, len: usize) -> Robj {
295        single_threaded(|| unsafe { Robj::from_sexp(Rf_allocVector(sexptype, len as R_xlen_t)) })
296    }
297
298    /// Return true if two arrays have identical dims.
299    fn conformable(a: &Robj, b: &Robj) -> bool {
300        single_threaded(|| unsafe { Rf_conformable(a.get(), b.get()).into() })
301    }
302
303    /// Return true if this is an array.
304    fn is_array(&self) -> bool {
305        unsafe { Rf_isArray(self.get()).into() }
306    }
307
308    /// Return true if this is factor.
309    fn is_factor(&self) -> bool {
310        unsafe { Rf_isFactor(self.get()).into() }
311    }
312
313    /// Return true if this is a data frame.
314    fn is_frame(&self) -> bool {
315        unsafe { Rf_isFrame(self.get()).into() }
316    }
317
318    /// Return true if this is a function or a primitive (CLOSXP, BUILTINSXP or SPECIALSXP)
319    fn is_function(&self) -> bool {
320        unsafe { Rf_isFunction(self.get()).into() }
321    }
322
323    /// Return true if this is an integer vector (INTSXP) but not a factor.
324    fn is_integer(&self) -> bool {
325        unsafe { Rf_isInteger(self.get()).into() }
326    }
327
328    /// Return true if this is a language object (LANGSXP).
329    fn is_language(&self) -> bool {
330        unsafe { Rf_isLanguage(self.get()).into() }
331    }
332
333    /// Return true if this is NILSXP or LISTSXP.
334    fn is_pairlist(&self) -> bool {
335        unsafe { Rf_isList(self.get()).into() }
336    }
337
338    /// Return true if this is a matrix.
339    fn is_matrix(&self) -> bool {
340        unsafe { Rf_isMatrix(self.get()).into() }
341    }
342
343    /// Return true if this is NILSXP or VECSXP.
344    fn is_list(&self) -> bool {
345        unsafe { Rf_isNewList(self.get()).into() }
346    }
347
348    /// Return true if this is INTSXP, LGLSXP or REALSXP but not a factor.
349    fn is_number(&self) -> bool {
350        unsafe { Rf_isNumber(self.get()).into() }
351    }
352
353    /// Return true if this is a primitive function BUILTINSXP, SPECIALSXP.
354    fn is_primitive(&self) -> bool {
355        unsafe { Rf_isPrimitive(self.get()).into() }
356    }
357
358    /// Return true if this is a time series vector (see tsp).
359    fn is_ts(&self) -> bool {
360        unsafe { Rf_isTs(self.get()).into() }
361    }
362
363    /// Return true if this is a user defined binop.
364    fn is_user_binop(&self) -> bool {
365        unsafe { Rf_isUserBinop(self.get()).into() }
366    }
367
368    #[cfg(feature = "non-api")]
369    /// Return true if this is a valid string.
370    fn is_valid_string(&self) -> bool {
371        unsafe { extendr_ffi::Rf_isValidString(self.get()).into() }
372    }
373
374    #[cfg(feature = "non-api")]
375    /// Return true if this is a valid string.
376    fn is_valid_string_f(&self) -> bool {
377        unsafe { extendr_ffi::Rf_isValidStringF(self.get()).into() }
378    }
379
380    /// Return true if this is a vector.
381    fn is_vector(&self) -> bool {
382        unsafe { Rf_isVector(self.get()).into() }
383    }
384
385    /// Return true if this is an atomic vector.
386    fn is_vector_atomic(&self) -> bool {
387        unsafe { Rf_isVectorAtomic(self.get()).into() }
388    }
389
390    /// Return true if this is a vector list.
391    fn is_vector_list(&self) -> bool {
392        unsafe { Rf_isVectorList(self.get()).into() }
393    }
394
395    /// Return true if this is can be made into a vector.
396    fn is_vectorizable(&self) -> bool {
397        unsafe { Rf_isVectorizable(self.get()).into() }
398    }
399
400    /// Return true if this is RAWSXP.
401    fn is_raw(&self) -> bool {
402        self.rtype() == Rtype::Raw
403    }
404
405    /// Return true if this is CHARSXP.
406    fn is_char(&self) -> bool {
407        self.rtype() == Rtype::Rstr
408    }
409
410    /// Check an external pointer tag.
411    /// This is used to wrap R objects.
412    #[doc(hidden)]
413    fn check_external_ptr_type<T>(&self) -> bool {
414        if self.sexptype() == SEXPTYPE::EXTPTRSXP {
415            let tag = unsafe { self.external_ptr_tag() };
416            if tag.as_str() == Some(std::any::type_name::<T>()) {
417                return true;
418            }
419        }
420        false
421    }
422
423    fn is_missing_arg(&self) -> bool {
424        unsafe { self.get() == R_MissingArg }
425    }
426
427    fn is_unbound_value(&self) -> bool {
428        unsafe { self.get() == R_UnboundValue }
429    }
430
431    fn is_package_env(&self) -> bool {
432        unsafe { R_IsPackageEnv(self.get()).into() }
433    }
434
435    fn package_env_name(&self) -> Robj {
436        unsafe { Robj::from_sexp(R_PackageEnvName(self.get())) }
437    }
438
439    fn is_namespace_env(&self) -> bool {
440        unsafe { R_IsNamespaceEnv(self.get()).into() }
441    }
442
443    fn namespace_env_spec(&self) -> Robj {
444        unsafe { Robj::from_sexp(R_NamespaceEnvSpec(self.get())) }
445    }
446
447    /// Returns `true` if this is an ALTREP object.
448    fn is_altrep(&self) -> bool {
449        unsafe { ALTREP(self.get()) != 0 }
450    }
451
452    /// Returns `true` if this is an integer ALTREP object.
453    fn is_altinteger(&self) -> bool {
454        unsafe { ALTREP(self.get()) != 0 && TYPEOF(self.get()) == SEXPTYPE::INTSXP }
455    }
456
457    /// Returns `true` if this is an real ALTREP object.
458    fn is_altreal(&self) -> bool {
459        unsafe { ALTREP(self.get()) != 0 && TYPEOF(self.get()) == SEXPTYPE::REALSXP }
460    }
461
462    /// Returns `true` if this is an logical ALTREP object.
463    fn is_altlogical(&self) -> bool {
464        unsafe { ALTREP(self.get()) != 0 && TYPEOF(self.get()) == SEXPTYPE::LGLSXP }
465    }
466
467    /// Returns `true` if this is a raw ALTREP object.
468    fn is_altraw(&self) -> bool {
469        unsafe { ALTREP(self.get()) != 0 && TYPEOF(self.get()) == SEXPTYPE::RAWSXP }
470    }
471
472    /// Returns `true` if this is an integer ALTREP object.
473    fn is_altstring(&self) -> bool {
474        unsafe { ALTREP(self.get()) != 0 && TYPEOF(self.get()) == SEXPTYPE::STRSXP }
475    }
476
477    /// Returns `true` if this is an integer ALTREP object.
478    #[cfg(use_r_altlist)]
479    fn is_altlist(&self) -> bool {
480        unsafe { ALTREP(self.get()) != 0 && TYPEOF(self.get()) == SEXPTYPE::VECSXP }
481    }
482
483    /// Generate a text representation of this object.
484    fn deparse(&self) -> Result<String> {
485        use crate as extendr_api;
486        let strings: Strings = call!("deparse", self.as_robj())?.try_into()?;
487        if strings.len() == 1 {
488            Ok(String::from(strings.elt(0).as_str()))
489        } else {
490            Ok(strings
491                .iter()
492                .map(|s| s.as_str())
493                .collect::<Vec<_>>()
494                .join(""))
495        }
496    }
497}
498
499impl Rinternals for Robj {}