Skip to content

Commit 809d9a5

Browse files
committed
Don't use illegal function accessors
1 parent 0eff217 commit 809d9a5

File tree

8 files changed

+158
-24
lines changed

8 files changed

+158
-24
lines changed

crates/ark/src/modules.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,13 +326,13 @@ pub extern "C-unwind" fn ark_log_error(msg: SEXP) -> harp::error::Result<SEXP> {
326326
#[cfg(test)]
327327
mod tests {
328328
use harp::environment::Environment;
329-
use libr::CLOENV;
329+
use harp::fn_env;
330330

331331
use crate::r_task;
332332

333333
fn get_namespace(exports: Environment, fun: &str) -> Environment {
334334
let fun = exports.find(fun).unwrap();
335-
let ns = unsafe { CLOENV(fun) };
335+
let ns = fn_env(fun);
336336
Environment::view(ns)
337337
}
338338

crates/ark/src/srcref.rs

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ async fn ns_populate_srcref_without_vdoc_insertion(
7676

7777
for b in ns.iter().filter_map(Result::ok) {
7878
span.in_scope(|| {
79-
match generate_source(&b, vdoc.len(), &uri) {
79+
match generate_source(&b, ns.inner.sexp, vdoc.len(), &uri) {
8080
Ok(Some(mut lines)) => {
8181
n_ok = n_ok + 1;
8282

@@ -122,6 +122,7 @@ fn ark_ns_uri(ns_name: &str) -> String {
122122
#[tracing::instrument(level = "trace", skip_all, fields(name = %binding.name))]
123123
fn generate_source(
124124
binding: &Binding,
125+
ns_env: SEXP,
125126
line: usize,
126127
uri: &String,
127128
) -> anyhow::Result<Option<Vec<String>>> {
@@ -160,10 +161,8 @@ fn generate_source(
160161
// Inject source references in functions. This is slightly unsafe but we
161162
// couldn't think of a dire failure mode.
162163
unsafe {
163-
// First replace the body which contains expressions tagged with srcrefs
164-
// such as calls to `{`. Compiled functions are a little more tricky.
164+
let body = harp::fn_body(old.sexp);
165165

166-
let body = BODY(old.sexp);
167166
if r_typeof(body) == BCODESXP {
168167
// This is a compiled function. We could recompile the fresh
169168
// function we just created but the compiler is very slow. Instead,
@@ -179,16 +178,35 @@ fn generate_source(
179178
// Inject new body instrumented with source references
180179
SET_VECTOR_ELT(consts, 0, R_ClosureExpr(new));
181180
}
181+
182+
Rf_setAttrib(
183+
old.sexp,
184+
r_symbol!("srcref"),
185+
Rf_getAttrib(new, r_symbol!("srcref")),
186+
);
182187
} else {
183-
SET_BODY(old.sexp, BODY(new));
184-
}
188+
let new_body = harp::fn_body(new);
189+
let out = RObject::new(harp::new_function(
190+
harp::fn_formals(old.sexp),
191+
new_body,
192+
harp::fn_env(old.sexp),
193+
));
194+
195+
// TODO: Avoid `ATTRIB()`
196+
let attrib = ATTRIB(old.sexp);
197+
if attrib != R_NilValue {
198+
let attrib = RObject::new(Rf_shallow_duplicate(attrib));
199+
SET_ATTRIB(out.sexp, attrib.sexp);
200+
}
185201

186-
// Finally push the srcref attribute for the whole function
187-
Rf_setAttrib(
188-
old.sexp,
189-
r_symbol!("srcref"),
190-
Rf_getAttrib(new, r_symbol!("srcref")),
191-
);
202+
Rf_setAttrib(
203+
out.sexp,
204+
r_symbol!("srcref"),
205+
Rf_getAttrib(new, r_symbol!("srcref")),
206+
);
207+
208+
harp::env_bind_force(ns_env, binding.name.sexp, out.sexp);
209+
}
192210
}
193211

194212
let text: Vec<String> = RObject::view(text).try_into()?;

crates/harp/src/attrib.rs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
use libr::SEXP;
22

33
use crate::object::r_length;
4+
use crate::r::fn_body;
5+
use crate::r::fn_env;
6+
use crate::r::fn_formals;
7+
use crate::r::new_function;
48
use crate::r_null;
59
use crate::r_symbol;
610
use crate::RObject;
@@ -18,12 +22,26 @@ pub fn zap_srcref(x: SEXP) -> RObject {
1822

1923
fn zap_srcref_fn(x: SEXP) -> RObject {
2024
unsafe {
21-
let x = RObject::view(x).shallow_duplicate();
22-
23-
x.set_attribute("srcref", r_null());
24-
libr::SET_BODY(x.sexp, zap_srcref(libr::R_ClosureExpr(x.sexp)).sexp);
25+
let formals = fn_formals(x);
26+
let body = fn_body(x);
27+
let env = fn_env(x);
28+
29+
let new_body = zap_srcref(body);
30+
let out = RObject::new(new_function(formals, new_body.sexp, env));
31+
32+
// Copy attributes from the original, but zap `srcref`
33+
let attrib = libr::ATTRIB(x);
34+
if attrib != r_null() {
35+
if libr::Rf_getAttrib(x, r_symbol!("srcref")) == r_null() {
36+
libr::SET_ATTRIB(out.sexp, attrib);
37+
} else {
38+
let attrib = RObject::new(libr::Rf_duplicate(attrib));
39+
libr::SET_ATTRIB(out.sexp, attrib.sexp);
40+
libr::Rf_setAttrib(out.sexp, r_symbol!("srcref"), r_null());
41+
}
42+
}
2543

26-
x
44+
out
2745
}
2846
}
2947

crates/harp/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
pub mod attrib;
99
pub mod call;
10+
1011
mod column_names;
1112
pub mod command;
1213
pub mod data_frame;
@@ -30,6 +31,7 @@ pub mod parse;
3031
pub mod parser;
3132
pub mod polled_events;
3233
pub mod protect;
34+
pub mod r;
3335
pub mod raii;
3436
pub mod routines;
3537
pub mod session;
@@ -55,6 +57,7 @@ pub use matrix::*;
5557
pub use object::*;
5658
pub use parse::*;
5759
pub use parser::*;
60+
pub use r::*;
5861
pub use source::*;
5962
pub use table::*;
6063
pub use vector::character_vector::*;

crates/harp/src/r.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
use libr::SEXP;
2+
3+
pub fn fn_formals(x: SEXP) -> SEXP {
4+
unsafe { libr::FORMALS(x) }
5+
}
6+
7+
pub fn fn_body(x: SEXP) -> SEXP {
8+
unsafe {
9+
if libr::has::R_ClosureBody() {
10+
libr::R_ClosureBody(x)
11+
} else {
12+
libr::BODY(x)
13+
}
14+
}
15+
}
16+
17+
pub fn fn_env(x: SEXP) -> SEXP {
18+
unsafe {
19+
if libr::has::R_ClosureEnv() {
20+
libr::R_ClosureEnv(x)
21+
} else {
22+
libr::CLOENV(x)
23+
}
24+
}
25+
}
26+
27+
pub fn env_binding_is_locked(env: SEXP, sym: SEXP) -> bool {
28+
unsafe { libr::R_BindingIsLocked(sym, env) != 0 }
29+
}
30+
31+
pub fn env_binding_lock(env: SEXP, sym: SEXP) {
32+
unsafe {
33+
libr::R_LockBinding(sym, env);
34+
}
35+
}
36+
37+
pub fn env_binding_unlock(env: SEXP, sym: SEXP) {
38+
unsafe {
39+
libr::R_unLockBinding(sym, env);
40+
}
41+
}
42+
43+
/// Binds a value in an environment, temporarily unlocking the binding if needed.
44+
pub fn env_bind_force(env: SEXP, sym: SEXP, value: SEXP) {
45+
let locked = env_binding_is_locked(env, sym);
46+
if locked {
47+
env_binding_unlock(env, sym);
48+
}
49+
unsafe {
50+
libr::Rf_defineVar(sym, value, env);
51+
}
52+
if locked {
53+
env_binding_lock(env, sym);
54+
}
55+
}
56+
57+
/// Creates a closure.
58+
pub unsafe fn new_function(formals: SEXP, body: SEXP, env: SEXP) -> SEXP {
59+
if libr::has::R_mkClosure() {
60+
libr::R_mkClosure(formals, body, env)
61+
} else {
62+
compat::alloc_closure(formals, body, env)
63+
}
64+
}
65+
66+
mod compat {
67+
use libr::SEXP;
68+
69+
pub unsafe fn alloc_closure(formals: SEXP, body: SEXP, env: SEXP) -> SEXP {
70+
let out = libr::Rf_allocSExp(libr::CLOSXP);
71+
libr::SET_FORMALS(out, formals);
72+
libr::SET_BODY(out, body);
73+
libr::SET_CLOENV(out, env);
74+
out
75+
}
76+
}

crates/harp/src/size.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ use crate::environment::R_ENVS;
2020
use crate::list_get;
2121
use crate::object::r_chr_get;
2222
use crate::object::r_length;
23+
use crate::r::fn_body;
24+
use crate::r::fn_env;
25+
use crate::r::fn_formals;
2326
use crate::r_is_altrep;
2427
use crate::r_symbol;
2528
use crate::r_typeof;
@@ -306,24 +309,23 @@ fn obj_size_tree(
306309
// Functions
307310
CLOSXP => {
308311
size += obj_size_tree(
309-
unsafe { libr::FORMALS(x) },
312+
fn_formals(x),
310313
base_env,
311314
sizeof_node,
312315
sizeof_vector,
313316
seen,
314317
depth + 1,
315318
);
316-
// BODY is either an expression or byte code
317319
size += obj_size_tree(
318-
unsafe { libr::BODY(x) },
320+
fn_body(x),
319321
base_env,
320322
sizeof_node,
321323
sizeof_vector,
322324
seen,
323325
depth + 1,
324326
);
325327
size += obj_size_tree(
326-
unsafe { libr::CLOENV(x) },
328+
fn_env(x),
327329
base_env,
328330
sizeof_node,
329331
sizeof_vector,

crates/harp/src/utils.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use crate::object::r_str_blank;
3030
use crate::object::r_str_na;
3131
use crate::object::RObject;
3232
use crate::protect::RProtect;
33+
use crate::r::fn_formals;
3334
use crate::r_char;
3435
use crate::r_lang;
3536
use crate::r_null;
@@ -330,7 +331,7 @@ pub fn r_formals(object: SEXP) -> Result<Vec<RArgument>> {
330331
r_assert_type(*object, &[CLOSXP])?;
331332

332333
// get the formals
333-
let mut formals = unsafe { FORMALS(*object) };
334+
let mut formals = fn_formals(*object);
334335

335336
// iterate through the entries
336337
let mut arguments = Vec::new();

crates/libr/src/r.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,8 @@ functions::generate! {
226226

227227
pub fn ATTRIB(x: SEXP) -> SEXP;
228228

229+
pub fn SET_ATTRIB(x: SEXP, v: SEXP);
230+
229231
pub fn CADDDR(e: SEXP) -> SEXP;
230232

231233
pub fn CADDR(e: SEXP) -> SEXP;
@@ -334,12 +336,26 @@ functions::generate! {
334336

335337
pub fn CLOENV(x: SEXP) -> SEXP;
336338

339+
pub fn SET_CLOENV(x: SEXP, v: SEXP);
340+
337341
pub fn BODY(x: SEXP) -> SEXP;
338342

339343
pub fn SET_BODY(x: SEXP, v: SEXP);
340344

345+
pub fn SET_FORMALS(x: SEXP, v: SEXP);
346+
347+
pub fn Rf_allocSExp(t: SEXPTYPE) -> SEXP;
348+
349+
pub fn R_ClosureBody(x: SEXP) -> SEXP;
350+
351+
pub fn R_ClosureEnv(x: SEXP) -> SEXP;
352+
341353
pub fn R_ClosureExpr(x: SEXP) -> SEXP;
342354

355+
pub fn R_ClosureFormals(x: SEXP) -> SEXP;
356+
357+
pub fn R_mkClosure(formals: SEXP, body: SEXP, env: SEXP) -> SEXP;
358+
343359
pub fn Rf_PrintValue(x: SEXP);
344360

345361
pub fn R_PromiseExpr(p: SEXP) -> SEXP;

0 commit comments

Comments
 (0)