1 /*
2 * RCntxtUtils.cpp
3 *
4 * Copyright (C) 2021 by RStudio, PBC
5 *
6 * Unless you have received this program directly from RStudio pursuant
7 * to the terms of a commercial license agreement with RStudio, then
8 * this program is licensed to you under the terms of version 3 of the
9 * GNU Affero General Public License. This program is distributed WITHOUT
10 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
11 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
12 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
13 *
14 */
15
16 #define R_INTERNAL_FUNCTIONS
17
18 #include <r/RCntxt.hpp>
19 #include <r/RCntxtUtils.hpp>
20 #include <r/RInterface.hpp>
21 #include <r/RExec.hpp>
22 #include <r/RUtil.hpp>
23
24 namespace rstudio {
25 namespace r {
26 namespace context {
27
contextVersion()28 RCntxtVersion contextVersion()
29 {
30 // cache the context version (we look this up constantly to figure out the
31 // appropriate offsets into the RCNXT struct)
32 static RCntxtVersion s_rCntxtVersion = RVersionUnknown;
33
34 if (s_rCntxtVersion == RVersionUnknown)
35 {
36 // use current R version to divine the memory layout
37 if (r::util::hasRequiredVersion("4.0"))
38 s_rCntxtVersion = RVersion40;
39 else if (r::util::hasRequiredVersion("3.4"))
40 s_rCntxtVersion = RVersion34;
41 else if (r::util::hasRequiredVersion("3.3"))
42 s_rCntxtVersion = RVersion33;
43 else
44 s_rCntxtVersion = RVersion32;
45 }
46 return s_rCntxtVersion;
47 }
48
globalContext()49 RCntxt globalContext()
50 {
51 return RCntxt(R_GlobalContext);
52 }
53
firstFunctionContext()54 RCntxt firstFunctionContext()
55 {
56 RCntxt::iterator firstFunContext = RCntxt::begin();
57 while ((firstFunContext->callfun() == nullptr ||
58 firstFunContext->callfun() == R_NilValue) &&
59 firstFunContext->callflag())
60 firstFunContext++;
61 return *firstFunContext;
62 }
63
getFunctionContext(const int depth,int * pFoundDepth,SEXP * pEnvironment)64 RCntxt getFunctionContext(const int depth,
65 int* pFoundDepth,
66 SEXP* pEnvironment)
67 {
68 RCntxt foundContext;
69 int currentDepth = 0;
70 int foundDepth = 0;
71 SEXP browseEnv = R_NilValue;
72 for (RCntxt::iterator ctxt = RCntxt::begin(); ctxt != RCntxt::end(); ctxt++)
73 {
74 // if looking for the actively browsed function, pick the environment
75 // evaluated by the browser on top of the stack
76 if (ctxt->callflag() & CTXT_BROWSER && browseEnv == R_NilValue)
77 {
78 browseEnv = ctxt->cloenv();
79 }
80 if (ctxt->callflag() & CTXT_FUNCTION)
81 {
82 currentDepth++;
83 if (depth == BROWSER_FUNCTION && ctxt->cloenv() == browseEnv)
84 {
85 foundDepth = currentDepth;
86 foundContext = *ctxt;
87 // continue traversing the callstack; there may be several
88 // functions eval'ing this environment and we want the "original"
89 // (here meaning oldest on the callstack)
90 }
91 else if (depth > BROWSER_FUNCTION && currentDepth >= depth)
92 {
93 foundDepth = currentDepth;
94 foundContext = *ctxt;
95 break;
96 }
97 }
98 }
99
100 // indicate the depth at which we stopped and the environment we found at
101 // that depth, if requested
102 if (pFoundDepth)
103 {
104 *pFoundDepth = foundDepth;
105 }
106 if (pEnvironment)
107 {
108 *pEnvironment = (foundDepth == 0 || foundContext.isNull()) ?
109 R_GlobalEnv :
110 foundContext.cloenv();
111 }
112 return foundContext;
113 }
114
115 // Return whether we're in browse context--meaning that there's a browser on
116 // the context stack and at least one function (i.e. we're not browsing at the
117 // top level).
inBrowseContext()118 bool inBrowseContext()
119 {
120 bool foundBrowser = false;
121 bool foundFunction = false;
122 for (RCntxt::iterator ctxt = RCntxt::begin(); ctxt != RCntxt::end(); ctxt++)
123 {
124 if ((ctxt->callflag() & CTXT_BROWSER) &&
125 !(ctxt->callflag() & CTXT_FUNCTION))
126 {
127 foundBrowser = true;
128 }
129 else if (ctxt->callflag() & CTXT_FUNCTION)
130 {
131 foundFunction = true;
132 }
133 if (foundBrowser && foundFunction)
134 {
135 return true;
136 }
137 }
138 return false;
139 }
140
141 // Return whether the current context is being evaluated inside a hidden
142 // (debugger internal) function at the top level.
inDebugHiddenContext()143 bool inDebugHiddenContext()
144 {
145 for (RCntxt::iterator ctxt = RCntxt::begin(); ctxt != RCntxt::end(); ctxt++)
146 {
147 if (ctxt->callflag() & CTXT_FUNCTION)
148 {
149 // If we find a debugger internal function before any user function,
150 // hide it from the user callstack.
151 if (ctxt->isDebugHidden())
152 return true;
153
154 // If we find a user function before we encounter a debugger internal
155 // function, don't hide the user code it invokes.
156 if (ctxt->hasSourceRefs())
157 return false;
158 }
159 }
160 return false;
161 }
162
isByteCodeContext(const RCntxt & cntxt)163 bool isByteCodeContext(const RCntxt& cntxt)
164 {
165 return isByteCodeSrcRef(cntxt.srcref());
166 }
167
isByteCodeSrcRef(SEXP srcref)168 bool isByteCodeSrcRef(SEXP srcref)
169 {
170 return srcref &&
171 TYPEOF(srcref) == SYMSXP &&
172 ::strcmp(CHAR(PRINTNAME(srcref)), "<in-bc-interp>") == 0;
173 }
174
175 } // namespace context
176 } // namespace r
177 } // namespace rstudio
178
179