Coverage Report

Created: 2026-09-14 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/r-source/src/main/inspect.c
Line
Count
Source
1
/*
2
 *  R : A Computer Language for Statistical Data Analysis
3
 *  Copyright (C) 2009-2025 The R Core Team.
4
 *
5
 *  This program is free software; you can redistribute it and/or modify
6
 *  it under the terms of the GNU General Public License as published by
7
 *  the Free Software Foundation; either version 2 of the License, or
8
 *  (at your option) any later version.
9
 *
10
 *  This program is distributed in the hope that it will be useful,
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 *  GNU General Public License for more details.
14
 *
15
 *  You should have received a copy of the GNU General Public License
16
 *  along with this program; if not, a copy is available at
17
 *  https://www.R-project.org/Licenses/
18
 */
19
20
/* This is an experimental facility for printing low-level information
21
   about R objects. It is not intended to be exposed at the top level
22
   but rather used as a debugging/inspection facility. It is not
23
   necessarily complete - feel free to add missing pieces. */
24
25
#define USE_RINTERNALS
26
27
#ifdef HAVE_CONFIG_H
28
#include <config.h>
29
#endif
30
31
#include <Defn.h>
32
#include <Internal.h>
33
#include <R_ext/Print.h>
34
35
/* FIXME: envir.c keeps this private - it should probably go to Defn.h */
36
0
#define FRAME_LOCK_MASK (1<<14)
37
0
#define FRAME_IS_LOCKED(e) (ENVFLAGS(e) & FRAME_LOCK_MASK)
38
0
#define GLOBAL_FRAME_MASK (1<<15)
39
0
#define IS_GLOBAL_FRAME(e) (ENVFLAGS(e) & GLOBAL_FRAME_MASK)
40
41
/* based on EncodeEnvironment in  printutils.c */
42
static void PrintEnvironment(SEXP x)
43
0
{
44
0
    const void *vmax = vmaxget();
45
0
    if (x == R_GlobalEnv)
46
0
  Rprintf("<R_GlobalEnv>");
47
0
    else if (x == R_BaseEnv)
48
0
  Rprintf("<base>");
49
0
    else if (x == R_EmptyEnv)
50
0
  Rprintf("<R_EmptyEnv>");
51
0
    else if (R_IsPackageEnv(x))
52
0
  Rprintf("<%s>",
53
0
    translateChar(STRING_ELT(R_PackageEnvName(x), 0)));
54
0
    else if (R_IsNamespaceEnv(x))
55
0
  Rprintf("<namespace:%s>",
56
0
    translateChar(STRING_ELT(R_NamespaceEnvSpec(x), 0)));
57
0
    else Rprintf("<%p>", (void *)x);
58
0
    vmaxset(vmax);
59
0
}
60
61
/* print prefix */
62
0
static void pp(int pre) {
63
    /* this is sort of silly, I know, but it saves at least some output
64
       calls (and we can replace \t by spaces if desired) ... */
65
0
    while (pre >= 8) { Rprintf("\t"); pre -= 8; }
66
0
    while (pre-- > 0) Rprintf(" ");
67
0
}
68
69
0
static const char *typename(SEXP v) {
70
0
    if(TYPEOF(v) == OBJSXP && IS_S4_OBJECT(v))
71
0
  return "S4SXP";
72
0
    return sexptype2char(TYPEOF(v)); // -> memory.c
73
0
}
74
75
static void inspect_tree(int, SEXP, int, int);
76
static void inspect_subtree(SEXP x, int pre, int deep, int pvec)
77
0
{
78
0
    inspect_tree(pre + 2, x, deep - 1, pvec);
79
0
}
80
81
/* pre is the prefix, v is the object to inspect, deep specifies
82
   the recursion behavior (0 = no recursion, -1 = [sort of] unlimited
83
   recursion, positive numbers define the maximum recursion depth)
84
   and pvec is the max. number of vector elements to show  */
85
0
static void inspect_tree(int pre, SEXP v, int deep, int pvec) {
86
0
    int a = 0;
87
0
    pp(pre);
88
    /* the use of %lx is deliberate because I hate the output of %p,
89
       but if this causes portability issues, it could be changed.
90
       SU
91
92
       It is invalid on 64-bit Windows.
93
    */
94
#ifdef _WIN64
95
    Rprintf("@%p %02d %s g%dc%d [", (void *)v, TYPEOF(v), typename(v),
96
      v->sxpinfo.gcgen, v->sxpinfo.gccls);
97
#else
98
0
    Rprintf("@%lx %02d %s g%dc%d [", (long) v, TYPEOF(v), typename(v),
99
0
      v->sxpinfo.gcgen, v->sxpinfo.gccls);
100
0
#endif
101
0
    if (OBJECT(v)) { a = 1; Rprintf("OBJ"); }
102
0
    if (MARK(v)) { if (a) Rprintf(","); Rprintf("MARK"); a = 1; }
103
#ifndef SWITCH_TO_REFCNT
104
    if (NAMED(v)) { if (a) Rprintf(","); Rprintf("NAM(%d)",NAMED(v)); a = 1; }
105
#endif
106
0
    if (REFCNT(v)) { if (a) Rprintf(","); Rprintf("REF(%d)",REFCNT(v)); a = 1; }
107
0
    if (RDEBUG(v)) { if (a) Rprintf(","); Rprintf("DBG"); a = 1; }
108
0
    if (RTRACE(v)) { if (a) Rprintf(","); Rprintf("TR"); a = 1; }
109
0
    if (RSTEP(v)) { if (a) Rprintf(","); Rprintf("STP"); a = 1; }
110
0
    if (IS_S4_OBJECT(v)) { if (a) Rprintf(","); Rprintf("S4"); a = 1; }
111
0
    if (TYPEOF(v) == SYMSXP || TYPEOF(v) == LISTSXP) {
112
0
  if (IS_ACTIVE_BINDING(v)) { if (a) Rprintf(","); Rprintf("AB"); a = 1; }
113
0
  if (BINDING_IS_LOCKED(v)) { if (a) Rprintf(","); Rprintf("LCK"); a = 1; }
114
0
    }
115
0
    if (TYPEOF(v) == ENVSXP) {
116
0
  if (FRAME_IS_LOCKED(v)) { if (a) Rprintf(","); Rprintf("LCK"); a = 1; }
117
0
  if (IS_GLOBAL_FRAME(v)) { if (a) Rprintf(","); Rprintf("GL"); a = 1; }
118
0
    }
119
0
    if (TYPEOF(v) == PROMSXP) {
120
0
  if (PROMISE_IS_EVALUATED(v)) { if (a) Rprintf(","); Rprintf("VAL"); a = 1; }
121
0
    }
122
0
    if (LEVELS(v)) { if (a) Rprintf(","); Rprintf("gp=0x%x", LEVELS(v)); a = 1; }
123
0
    if (ATTRIB(v) && ATTRIB(v) != R_NilValue) { if (a) Rprintf(","); Rprintf("ATT"); a = 1; }
124
0
    Rprintf("] ");
125
126
0
    if (ALTREP(v) && ALTREP_INSPECT(v, pre, deep, pvec, inspect_subtree)) {
127
0
  if (ATTRIB(v) && ATTRIB(v) != R_NilValue && TYPEOF(v) != CHARSXP) {
128
0
      pp(pre);
129
0
      Rprintf("ATTRIB:\n");
130
0
      inspect_tree(pre+2, ATTRIB(v), deep, pvec);
131
0
  }
132
0
  return;
133
0
    }
134
135
0
    switch (TYPEOF(v)) {
136
0
    case VECSXP: case STRSXP: case LGLSXP: case INTSXP: case RAWSXP:
137
0
    case REALSXP: case CPLXSXP: case EXPRSXP:
138
0
  if (GROWABLE_BIT_SET(v))
139
0
      Rprintf("(len=%lld, tl=%lld, gr)", (long long)XLENGTH(v), (long long)XTRUELENGTH(v));
140
0
  else
141
0
      Rprintf("(len=%lld, tl=%lld)", (long long)XLENGTH(v), (long long)XTRUELENGTH(v));
142
0
    }
143
0
    if (TYPEOF(v) == ENVSXP) /* NOTE: this is not a trivial OP since it involves looking up things
144
        in the environment, so for a low-level debugging we may want to
145
        avoid it .. */
146
0
  PrintEnvironment(v);
147
0
    if (TYPEOF(v) == CHARSXP) {
148
0
  if (IS_BYTES(v)) Rprintf("[bytes] ");
149
0
  if (IS_LATIN1(v)) Rprintf("[latin1] ");
150
0
  if (IS_UTF8(v)) Rprintf("[UTF8] ");
151
0
  if (IS_ASCII(v)) Rprintf("[ASCII] ");
152
0
  if (IS_CACHED(v)) Rprintf("[cached] ");
153
0
  Rprintf("\"%s\"", CHAR(v));
154
0
  if (v == R_NaString) Rprintf(" [NA]");
155
0
    }
156
0
    if (TYPEOF(v) == SYMSXP) {
157
0
  if (v == R_UnboundValue)
158
0
      Rprintf("[unbound value]");
159
0
  else if (v == R_MissingArg)
160
0
      Rprintf("[missing argument]");
161
0
  else if (v == R_RestartToken)
162
0
      Rprintf("[restart token]");
163
0
  else
164
0
      Rprintf("\"%s\"%s", EncodeChar(PRINTNAME(v)), (SYMVALUE(v) == R_UnboundValue) ? "" : " (has value)");
165
0
    }
166
0
    if (TYPEOF(v) == EXTPTRSXP)
167
0
  Rprintf("<%p>", R_ExternalPtrAddr(v));
168
0
    switch (TYPEOF(v)) { /* for native vectors print the first elements in-line */
169
0
    case LGLSXP:
170
0
  if (XLENGTH(v) > 0) {
171
0
    unsigned int i = 0;
172
0
    while (i < XLENGTH(v) && i < pvec) {
173
0
        Rprintf("%s%d", (i > 0) ? "," : " ",
174
0
          (int) LOGICAL_ELT(v, i));
175
0
        i++;
176
0
    }
177
0
    if (i < XLENGTH(v)) Rprintf(",...");
178
0
  }
179
0
  break;
180
0
    case INTSXP:
181
0
  if (XLENGTH(v) > 0) {
182
0
      unsigned int i = 0;
183
0
      while (i < XLENGTH(v) && i < pvec) {
184
0
    Rprintf("%s%d", (i > 0) ? "," : " ", INTEGER_ELT(v, i));
185
0
    i++;
186
0
      }
187
0
      if (i < XLENGTH(v)) Rprintf(",...");
188
0
  }
189
0
  break;
190
0
    case RAWSXP:
191
0
  if (XLENGTH(v) > 0) {
192
0
      unsigned int i = 0;
193
0
      while (i < XLENGTH(v) && i < pvec) {
194
0
    Rprintf("%s%02x", (i > 0) ? "," : " ", (int) ((unsigned char) RAW(v)[i]));
195
0
    i++;
196
0
      }
197
0
      if (i < XLENGTH(v)) Rprintf(",...");
198
0
  }
199
0
  break;
200
0
    case REALSXP:
201
0
  if (XLENGTH(v) > 0) {
202
0
      unsigned int i = 0;
203
0
      while (i < XLENGTH(v) && i < pvec) {
204
0
    Rprintf("%s%g", (i > 0) ? "," : " ", REAL_ELT(v, i));
205
0
    i++;
206
0
      }
207
0
      if (i < XLENGTH(v)) Rprintf(",...");
208
0
  }
209
0
  break;
210
0
    }
211
0
    Rprintf("\n");
212
0
    if (deep) switch (TYPEOF(v)) {
213
0
  case VECSXP: case EXPRSXP:
214
0
      {
215
0
    unsigned int i = 0;
216
0
    while (i < XLENGTH(v) && i < pvec) {
217
0
        inspect_tree(pre+2, VECTOR_ELT(v, i), deep - 1, pvec);
218
0
        i++;
219
0
    }
220
0
    if (i < XLENGTH(v)) { pp(pre+2); Rprintf("...\n"); }
221
0
      }
222
0
      break;
223
0
  case STRSXP:
224
0
      {
225
0
    unsigned int i = 0;
226
0
    while (i < XLENGTH(v) && i < pvec) {
227
0
        inspect_tree(pre+2, STRING_ELT(v, i), deep - 1, pvec);
228
0
        i++;
229
0
    }
230
0
    if (i < XLENGTH(v)) { pp(pre+2); Rprintf("...\n"); }
231
0
      }
232
0
      break;
233
0
  case LISTSXP: case LANGSXP:
234
0
      {
235
0
    SEXP lc = v;
236
0
    while (lc != R_NilValue) {
237
0
        if (TYPEOF(lc) != LISTSXP && TYPEOF(lc) != LANGSXP) {
238
      /* a dotted pair */
239
0
      pp(pre + 2);
240
0
      Rprintf(".\n");
241
0
      inspect_tree(pre + 2, lc, deep - 1, pvec);
242
0
      break;
243
0
        }
244
0
        if (TAG(lc) && TAG(lc) != R_NilValue) {
245
0
      pp(pre + 2);
246
0
      Rprintf("TAG: "); /* TAG should be a one-liner since it's a symbol so we don't put it on an extra line*/
247
0
      inspect_tree(0, TAG(lc), deep - 1, pvec);
248
0
        }
249
0
        if (BNDCELL_TAG(lc)) {
250
0
      int type = BNDCELL_TAG(lc);
251
0
      pp(pre + 2);
252
0
      Rprintf("immediate %s: ", sexptype2char(type));
253
0
      switch(type) {
254
0
      case REALSXP:
255
0
          Rprintf("%g\n", BNDCELL_DVAL(lc));
256
0
          break;
257
0
      case INTSXP:
258
0
          if (BNDCELL_IVAL(lc) == NA_INTEGER)
259
0
        Rprintf("NA\n");
260
0
          else
261
0
        Rprintf("%d\n", BNDCELL_IVAL(lc));
262
0
          break;
263
0
      case LGLSXP:
264
0
          if (BNDCELL_LVAL(lc) == NA_INTEGER)
265
0
        Rprintf("NA\n");
266
0
          else if (BNDCELL_LVAL(lc))
267
0
        Rprintf("TRUE\n");
268
0
          else
269
0
        Rprintf("FALSE\n");
270
0
          break;
271
0
      default: error("unknown immediate binding type");
272
0
      }
273
0
        }
274
0
        else
275
0
      inspect_tree(pre + 2, CAR(lc), deep - 1, pvec);
276
0
        lc = CDR(lc);
277
0
    }
278
0
      }
279
0
      break;
280
0
  case ENVSXP:
281
0
      if (FRAME(v) != R_NilValue) {
282
0
    pp(pre); Rprintf("FRAME:\n");
283
0
    inspect_tree(pre+2, FRAME(v), deep - 1, pvec);
284
0
      }
285
0
      pp(pre); Rprintf("ENCLOS:\n");
286
0
      inspect_tree(pre+2, ENCLOS(v), 0, pvec);
287
0
      if (HASHTAB(v) != R_NilValue) {
288
0
    pp(pre); Rprintf("HASHTAB:\n");
289
0
    inspect_tree(pre+2, HASHTAB(v), deep - 1, pvec);
290
0
      }
291
0
      break;
292
293
0
  case CLOSXP:
294
0
      pp(pre); Rprintf("FORMALS:\n");
295
0
      inspect_tree(pre+2, FORMALS(v), deep - 1, pvec);
296
0
      pp(pre); Rprintf("BODY:\n");
297
0
      inspect_tree(pre+2, BODY(v), deep - 1, pvec);
298
0
      pp(pre); Rprintf("CLOENV:\n");
299
0
      inspect_tree(pre+2, CLOENV(v), 0, pvec);
300
0
      break;
301
0
  case EXTPTRSXP:
302
0
      {
303
0
    SEXP prot = R_ExternalPtrProtected(v);
304
0
    SEXP tag = R_ExternalPtrTag(v);
305
0
    if (prot != R_NilValue) {
306
0
        pp(pre); Rprintf("PROTECTED:\n");
307
0
        inspect_tree(pre+2, prot, deep - 1, pvec);
308
0
    }
309
0
    if (tag != R_NilValue) {
310
0
        pp(pre); Rprintf("TAG:\n");
311
0
        inspect_tree(pre+2, tag, deep - 1, pvec);
312
0
    }
313
0
      }
314
0
      break;
315
0
  }
316
317
0
    if (ATTRIB(v) && ATTRIB(v) != R_NilValue && TYPEOF(v) != CHARSXP) {
318
0
  pp(pre); Rprintf("ATTRIB:\n"); inspect_tree(pre+2, ATTRIB(v), deep, pvec);
319
0
    }
320
0
}
321
322
/* internal API - takes one mandatory argument (object to inspect) and
323
   two optional arguments (deep and pvec - see above), positional argument
324
   matching only */
325
0
attribute_hidden SEXP do_inspect(SEXP call, SEXP op, SEXP args, SEXP env) {
326
0
    checkArity(op, args);
327
0
    SEXP obj = CAR(args);
328
0
    int deep = -1;
329
0
    int pvec = 5;
330
0
    if (CDR(args) != R_NilValue) {
331
0
  deep = asInteger(CADR(args));
332
0
  if (CDDR(args) != R_NilValue)
333
0
      pvec = asInteger(CADDR(args));
334
0
    }
335
336
0
    inspect_tree(0, CAR(args), deep, pvec);
337
0
    return obj;
338
0
}
339
340
attribute_hidden SEXP do_address(SEXP call, SEXP op, SEXP args, SEXP rho)
341
0
{
342
0
    checkArity(op, args);
343
0
    return R_MakeExternalPtr((void *) CAR(args), R_NilValue, R_NilValue);
344
0
}
345
346
attribute_hidden SEXP do_named(SEXP call, SEXP op, SEXP args, SEXP rho)
347
0
{
348
0
    checkArity(op, args);
349
0
    return ScalarInteger(NAMED(CAR(args)));
350
0
}
351
352
attribute_hidden SEXP do_refcnt(SEXP call, SEXP op, SEXP args, SEXP rho)
353
0
{
354
0
    checkArity(op, args);
355
0
    return ScalarInteger(REFCNT(CAR(args)));
356
0
}
357
358
/* the following functions can be use internally and for debugging purposes -
359
   so far they are not used in any actual code */
360
0
attribute_hidden SEXP R_inspect(SEXP x) {
361
0
    inspect_tree(0, x, -1, 5);
362
0
    return x;
363
0
}
364
365
0
attribute_hidden SEXP R_inspect3(SEXP x, int deep, int pvec) {
366
0
    inspect_tree(0, x, deep, pvec);
367
0
    return x;
368
0
}