1 /*
2  * RCntxt.hpp
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 #ifndef R_CONTEXT_HPP
17 #define R_CONTEXT_HPP
18 
19 #include "RSexp.hpp"
20 #include "RCntxtInterface.hpp"
21 
22 #include <shared_core/Error.hpp>
23 
24 #include <boost/iterator/iterator_facade.hpp>
25 
26 namespace rstudio {
27 namespace r {
28 namespace context {
29 
30 // RCnxt is a public-facing class which wraps entries from the R context
31 // stack (RCNXT* in the R headers), which keeps track of execution contexts
32 // during the evaluation of R code. Unfortunately, the memory layout
33 // in the RCNXT* struct is not identical in all R versions, and consequently
34 // it's necessary to wrap them in a layer of abstraction, which this class
35 // supplies.
36 //
37 // It has value semantics; its only data member is a shared pointer to the class
38 // managing the RNCTXT* interface. It also implements simple iteration over
39 // entries in the context stack. RCnxt::begin() returns an iterator beginning
40 // at R_GlobalContext; RCnxt::end() returns a null context entry.
41 class RCntxt: public RCntxtInterface
42 {
43 public:
44    // copy/equality testing
45    RCntxt();
46    explicit RCntxt(void *rawCntxt);
47    bool operator==(const RCntxt& other) const;
48    bool operator!=(const RCntxt& other) const;
49 
50    // safe coercion to boolean
operator bool() const51    explicit operator bool() const
52    {
53       return pCntxt_ != nullptr;
54    }
55 
56    // utility/accessor functions
57    bool hasSourceRefs() const;
58    bool isDebugHidden() const;
59    bool isErrorHandler() const;
60 
61    // retrieve the source references associated with the context
62    // (the source reference where this context originated)
63    SEXP contextSourceRefs() const;
64 
65    // retrieve source references associated with the calling function
66    // (where that calling function was actually defined in source)
67    SEXP callFunSourceRefs() const;
68 
69    // for traced functions, the 'real' function will be replaced by
70    // a wrapper function -- use this to find the original function
71    SEXP originalFunctionCall() const;
72 
73    std::string shinyFunctionLabel() const;
74 
75    core::Error functionName(std::string* pFunctionName) const;
76    core::Error fileName(std::string* pFileName) const;
77    core::Error callSummary(std::string* pCallSummary) const;
78 
79    // implemented by R version specific internal context classes
80    //
81    // NOTE: 'srcref()' attribute may be set to <in-bc-interp>
82    // symbol for contexts associated with bytecode evaluation!
83    bool isNull() const;
84    SEXP callfun() const;
85    int callflag() const;
86    int evaldepth() const;
87    SEXP call() const;
88    SEXP srcref() const;
89    SEXP cloenv() const;
90    RCntxt nextcontext() const;
91 
92    // define an iterator for easy traversal of the context stack
93    template <class Value>
94    class cntxt_iterator: public boost::iterator_facade<
95             cntxt_iterator<Value>,
96             Value,  // value type
97             boost::forward_traversal_tag,
98             const Value&>  // reference type
99    {
100    public:
cntxt_iterator()101       cntxt_iterator():
102          context_()
103       { }
104 
cntxt_iterator(Value pCntxt)105       explicit cntxt_iterator(Value pCntxt):
106          context_(pCntxt)
107       { }
108 
109    private:
110       friend class boost::iterator_core_access;
111 
increment()112       void increment()
113       {
114          context_ = context_.nextcontext();
115       }
116 
equal(cntxt_iterator<Value> const & other) const117       bool equal(cntxt_iterator<Value> const& other) const
118       {
119          return context_ == other.context_;
120       }
121 
dereference() const122       const Value& dereference() const
123       {
124          return context_;
125       }
126 
127       Value context_;
128    };
129 
130    typedef cntxt_iterator<RCntxt> iterator;
131    typedef cntxt_iterator<RCntxt const> const_iterator;
132 
133    static iterator begin();
134    static iterator end();
135 
136 private:
137    boost::shared_ptr<RCntxtInterface> pCntxt_;
138 
139    core::Error invokeFunctionOnCall(const char* rFunction,
140                                     std::string* pResult) const;
141 };
142 
143 } // namespace context
144 } // namespace r
145 } // namespace rstudio
146 
147 #endif
148