|
1 | 1 | #include <cpp11/environment.hpp> |
2 | 2 | #include <cpp11/list.hpp> |
| 3 | +#include <cpp11/sexp.hpp> |
3 | 4 | #include <cpp11/strings.hpp> |
4 | 5 | #include <Rversion.h> |
5 | 6 | #include <map> |
6 | 7 | #include "utils.h" |
7 | 8 |
|
| 9 | +extern "C" { |
| 10 | +#include <rlang.h> |
| 11 | +} |
| 12 | + |
8 | 13 | struct Expand { |
9 | 14 | bool alrep; |
10 | 15 | bool charsxp; |
@@ -50,6 +55,30 @@ class GrowableList { |
50 | 55 | SEXP obj_children_(SEXP x, std::map<SEXP, int>& seen, double max_depth, Expand expand); |
51 | 56 | bool is_namespace(cpp11::environment env); |
52 | 57 |
|
| 58 | +// Create a placeholder inspector node for synthetic entries (e.g. promise bindings) |
| 59 | +SEXP new_placeholder_inspector(int type, std::map<SEXP, int>& seen) { |
| 60 | + SEXP out = PROTECT(Rf_allocVector(VECSXP, 0)); |
| 61 | + |
| 62 | + int id = seen.size() + 1; |
| 63 | + |
| 64 | + // Placeholder address. Causes a more bare bones display in the tree. |
| 65 | + Rf_setAttrib(out, Rf_install("addr"), PROTECT(Rf_mkString(""))); |
| 66 | + |
| 67 | + // Placeholder properties |
| 68 | + Rf_setAttrib(out, Rf_install("has_seen"), PROTECT(Rf_ScalarLogical(false))); |
| 69 | + Rf_setAttrib(out, Rf_install("id"), PROTECT(Rf_ScalarInteger(id))); |
| 70 | + Rf_setAttrib(out, Rf_install("type"), PROTECT(Rf_ScalarInteger(type))); |
| 71 | + Rf_setAttrib(out, Rf_install("length"), PROTECT(Rf_ScalarReal(0))); |
| 72 | + Rf_setAttrib(out, Rf_install("altrep"), PROTECT(Rf_ScalarLogical(false))); |
| 73 | + Rf_setAttrib(out, Rf_install("maybe_shared"), PROTECT(Rf_ScalarInteger(0))); |
| 74 | + Rf_setAttrib(out, Rf_install("no_references"), PROTECT(Rf_ScalarInteger(0))); |
| 75 | + Rf_setAttrib(out, Rf_install("object"), PROTECT(Rf_ScalarInteger(0))); |
| 76 | + Rf_setAttrib(out, Rf_install("class"), PROTECT(Rf_mkString("lobstr_inspector"))); |
| 77 | + |
| 78 | + UNPROTECT(11); |
| 79 | + return out; |
| 80 | +} |
| 81 | + |
53 | 82 | bool is_altrep(SEXP x) { |
54 | 83 | #if defined(R_VERSION) && R_VERSION >= R_Version(3, 5, 0) |
55 | 84 | return ALTREP(x); |
@@ -251,38 +280,75 @@ SEXP obj_children_( |
251 | 280 | break; |
252 | 281 |
|
253 | 282 | // Environments |
254 | | - case ENVSXP: |
| 283 | + case ENVSXP: { |
255 | 284 | if (x == R_BaseEnv || x == R_GlobalEnv || x == R_EmptyEnv || is_namespace(x)) |
256 | 285 | break; |
257 | 286 |
|
258 | | - if (expand.env) { |
259 | | - // Using node-based object accessors: CAR for FRAME, and TAG for HASHTAB. |
260 | | - // TODO: Iterate manually over the environment using environment accessors. |
261 | | - recurse(&children, seen, "_frame", CAR(x), max_depth, expand); |
262 | | - recurse(&children, seen, "_hashtab", TAG(x), max_depth, expand); |
263 | | - } else { |
264 | | - SEXP names = PROTECT(R_lsInternal3(x, /* all= */ TRUE, /* sorted= */ FALSE)); |
265 | | - for (R_xlen_t i = 0; i < XLENGTH(names); ++i) { |
266 | | - const char* name = CHAR(STRING_ELT(names, i)); |
267 | | - SEXP sym = PROTECT(Rf_install(name)); |
268 | | - |
269 | | - if (R_BindingIsActive(sym, x)) { |
270 | | - SEXP sym = PROTECT(Rf_install("_active_binding")); |
271 | | - SEXP active = PROTECT(obj_inspect_(sym, seen, max_depth, expand)); |
272 | | - children.push_back(name, active); |
273 | | - UNPROTECT(2); |
274 | | - } else { |
275 | | - SEXP obj = PROTECT(Rf_findVarInFrame(x, sym)); |
276 | | - recurse(&children, seen, name, obj, max_depth, expand); |
277 | | - UNPROTECT(1); |
| 287 | + cpp11::sexp syms(r_env_syms(x)); |
| 288 | + R_xlen_t n_bindings = Rf_xlength(syms); |
| 289 | + |
| 290 | + for (R_xlen_t i = 0; i < n_bindings; ++i) { |
| 291 | + SEXP sym = VECTOR_ELT(syms, i); |
| 292 | + const char* name = CHAR(PRINTNAME(sym)); |
| 293 | + enum r_env_binding_type type = r_env_binding_type(x, sym); |
| 294 | + |
| 295 | + switch (type) { |
| 296 | + case R_ENV_BINDING_TYPE_value: |
| 297 | + recurse(&children, seen, name, r_env_find(x, sym), max_depth, expand); |
| 298 | + break; |
| 299 | + |
| 300 | + case R_ENV_BINDING_TYPE_missing: { |
| 301 | + SEXP missing = PROTECT(new_placeholder_inspector(SYMSXP, seen)); |
| 302 | + Rf_setAttrib(missing, Rf_install("value"), PROTECT(Rf_mkString("<missing>"))); |
| 303 | + children.push_back(name, missing); |
| 304 | + UNPROTECT(2); |
| 305 | + break; |
| 306 | + } |
| 307 | + |
| 308 | + case R_ENV_BINDING_TYPE_delayed: { |
| 309 | + SEXP promise = PROTECT(new_placeholder_inspector(PROMSXP, seen)); |
| 310 | + children.push_back(name, promise); |
| 311 | + UNPROTECT(1); |
| 312 | + |
| 313 | + if (expand.env) { |
| 314 | + recurse(&children, seen, "_code", r_env_binding_delayed_expr(x, sym), max_depth, expand); |
| 315 | + recurse(&children, seen, "_env", r_env_binding_delayed_env(x, sym), max_depth, expand); |
278 | 316 | } |
| 317 | + break; |
| 318 | + } |
| 319 | + |
| 320 | + case R_ENV_BINDING_TYPE_forced: { |
| 321 | + SEXP promise = PROTECT(new_placeholder_inspector(PROMSXP, seen)); |
| 322 | + children.push_back(name, promise); |
279 | 323 | UNPROTECT(1); |
| 324 | + |
| 325 | + if (expand.env) { |
| 326 | + recurse(&children, seen, "_value", r_env_binding_forced_value(x, sym), max_depth, expand); |
| 327 | + recurse(&children, seen, "_code", r_env_binding_forced_expr(x, sym), max_depth, expand); |
| 328 | + } |
| 329 | + break; |
| 330 | + } |
| 331 | + |
| 332 | + case R_ENV_BINDING_TYPE_active: { |
| 333 | + SEXP active = PROTECT(new_placeholder_inspector(CLOSXP, seen)); |
| 334 | + Rf_setAttrib(active, Rf_install("value"), PROTECT(Rf_mkString("active"))); |
| 335 | + children.push_back(name, active); |
| 336 | + UNPROTECT(2); |
| 337 | + |
| 338 | + if (expand.env) { |
| 339 | + recurse(&children, seen, "_fn", r_env_binding_active_fn(x, sym), max_depth, expand); |
| 340 | + } |
| 341 | + break; |
| 342 | + } |
| 343 | + |
| 344 | + case R_ENV_BINDING_TYPE_unbound: |
| 345 | + break; |
280 | 346 | } |
281 | | - UNPROTECT(1); |
282 | 347 | } |
283 | 348 |
|
284 | | - recurse(&children, seen, "_enclos", R_ParentEnv(x), max_depth, expand); |
| 349 | + recurse(&children, seen, "_enclos", r_env_parent(x), max_depth, expand); |
285 | 350 | break; |
| 351 | + } |
286 | 352 |
|
287 | 353 | // Functions |
288 | 354 | case CLOSXP: |
|
0 commit comments