1 /*
2 * SessionErrors.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 #include <algorithm>
17
18 #include <shared_core/Error.hpp>
19 #include <core/Exec.hpp>
20 #include <core/json/JsonRpc.hpp>
21
22 #include <r/RExec.hpp>
23 #include <r/ROptions.hpp>
24
25 #include <boost/bind/bind.hpp>
26 #include <session/SessionModuleContext.hpp>
27 #include <session/prefs/UserPrefs.hpp>
28 #include <session/prefs/UserState.hpp>
29 #include "SessionErrors.hpp"
30 #include "SessionBreakpoints.hpp"
31
32 using namespace rstudio::core;
33 using namespace boost::placeholders;
34
35 namespace rstudio {
36 namespace session {
37 namespace modules {
38 namespace errors {
39 namespace {
40
enqueErrorHandlerChanged(const std::string & type)41 void enqueErrorHandlerChanged(const std::string& type)
42 {
43 json::Object errorHandlerType;
44 errorHandlerType["type"] = type;
45 ClientEvent errorHandlerChanged(
46 client_events::kErrorHandlerChanged, errorHandlerType);
47 module_context::enqueClientEvent(errorHandlerChanged);
48 }
49
setErrHandler(const std::string & type,bool inMyCode,boost::shared_ptr<SEXP> pErrorHandler)50 Error setErrHandler(const std::string& type, bool inMyCode,
51 boost::shared_ptr<SEXP> pErrorHandler)
52 {
53 // when setting the error handler to "custom", just leave it as it was
54 if (type == kErrorHandlerTypeCustom)
55 return Success();
56
57 Error error = r::exec::RFunction(
58 ".rs.setErrorManagementType", type, inMyCode)
59 .callUnsafe();
60 if (error)
61 return error;
62
63 *pErrorHandler = r::options::getOption("error");
64 return Success();
65 }
66
setErrHandlerType(const std::string & type,boost::shared_ptr<SEXP> pErrorHandler)67 Error setErrHandlerType(const std::string& type,
68 boost::shared_ptr<SEXP> pErrorHandler)
69 {
70 Error error = setErrHandler(type,
71 prefs::userPrefs().handleErrorsInUserCodeOnly(),
72 pErrorHandler);
73 if (error)
74 return error;
75
76 prefs::userState().setErrorHandlerType(type);
77 enqueErrorHandlerChanged(type);
78 return Success();
79 }
80
setErrHandlerType(boost::shared_ptr<SEXP> pErrorHandler,const json::JsonRpcRequest & request,json::JsonRpcResponse * pResponse)81 Error setErrHandlerType(boost::shared_ptr<SEXP> pErrorHandler,
82 const json::JsonRpcRequest& request,
83 json::JsonRpcResponse* pResponse)
84 {
85 std::string type;
86 Error error = json::readParams(request.params, &type);
87 if (error)
88 return error;
89
90 return setErrHandlerType(type, pErrorHandler);
91 }
92
initializeErrManagement(boost::shared_ptr<SEXP> pErrorHandler,boost::shared_ptr<bool> pHandleUserErrorsOnly)93 Error initializeErrManagement(boost::shared_ptr<SEXP> pErrorHandler,
94 boost::shared_ptr<bool> pHandleUserErrorsOnly)
95 {
96 SEXP currentHandler = r::options::getOption("error");
97 *pHandleUserErrorsOnly = prefs::userPrefs().handleErrorsInUserCodeOnly();
98 // This runs after ~/.RProfile, so don't change the error handler if
99 // there's already one assigned, or if we're aware of a custom error
100 // handler.
101 if (currentHandler == R_NilValue &&
102 prefs::userState().errorHandlerType() != kErrorHandlerTypeCustom)
103 setErrHandlerType(prefs::userState().errorHandlerType(), pErrorHandler);
104 else
105 prefs::userState().setErrorHandlerType(kErrorHandlerTypeCustom);
106 return Success();
107 }
108
109 // Detect whether the error handler has changed, and optionally record the
110 // change as a permanent. (We don't make changes permanent when they're
111 // detected during a session.)
detectHandlerChange(boost::shared_ptr<SEXP> pErrorHandler,bool recordSetting)112 void detectHandlerChange(boost::shared_ptr<SEXP> pErrorHandler,
113 bool recordSetting)
114 {
115 // check to see if the error option has been changed from beneath us; if
116 // it has, emit a client event so the client doesn't show an incorrect
117 // error handler
118 SEXP currentHandler = r::options::getOption("error");
119 if (currentHandler != *pErrorHandler)
120 {
121 std::string handlerType;
122 if (currentHandler != R_NilValue &&
123 r::sexp::isLanguage(currentHandler))
124 {
125 // it's possible for the SEXP to change (it's a pointer) even though
126 // the handler is correct; check the attribute of the function invoked
127 // by the handler and compare to the user preference.
128 SEXP fun = CAR(currentHandler);
129 SEXP typeSEXP = r::sexp::getAttrib(fun, "errorHandlerType");
130 if (typeSEXP != nullptr && !r::sexp::isNull(typeSEXP))
131 {
132 Error error = r::sexp::extract(typeSEXP, &handlerType);
133 if (!error && handlerType == prefs::userState().errorHandlerType())
134 {
135 // the SEXP is different but the attribute matches; update our
136 // SEXP so we don't keep detecting a change
137 *pErrorHandler = currentHandler;
138 return;
139 }
140 }
141 }
142 *pErrorHandler = currentHandler;
143
144 // attempt to figure out what error handler type is in use, if any
145 if (handlerType.empty())
146 {
147 handlerType = (currentHandler == R_NilValue) ?
148 kErrorHandlerTypeMessage :
149 kErrorHandlerTypeCustom;
150 }
151 if (recordSetting)
152 prefs::userState().setErrorHandlerType(handlerType);
153
154 // the notebook error handler is transient, so don't send it to the
155 // client
156 if (handlerType != kErrorHandlerTypeNotebook)
157 enqueErrorHandlerChanged(handlerType);
158 }
159 }
160
onUserSettingsChanged(const std::string & pref,boost::shared_ptr<SEXP> pErrorHandler,boost::shared_ptr<bool> pHandleUserErrorsOnly)161 void onUserSettingsChanged(const std::string& pref,
162 boost::shared_ptr<SEXP> pErrorHandler,
163 boost::shared_ptr<bool> pHandleUserErrorsOnly)
164 {
165 // check to see if the setting for 'handle errors in user code only'
166 // has been changed since we last looked at it; if it has, switch the
167 // error handler in a corresponding way
168 if (pref != kErrorHandlerType)
169 return;
170
171 bool handleUserErrorsOnly = prefs::userPrefs().handleErrorsInUserCodeOnly();
172 Error error = setErrHandler(prefs::userState().errorHandlerType(),
173 handleUserErrorsOnly,
174 pErrorHandler);
175 if (error)
176 LOG_ERROR(error);
177
178 *pHandleUserErrorsOnly = handleUserErrorsOnly;
179 }
180
181 } // anonymous namespace
182
errorStateAsJson()183 json::Value errorStateAsJson()
184 {
185 json::Object state;
186 state["error_handler_type"] = prefs::userState().errorHandlerType();
187 return std::move(state);
188 }
189
initialize()190 Error initialize()
191 {
192 boost::shared_ptr<SEXP> pErrorHandler =
193 boost::make_shared<SEXP>(R_NilValue);
194 boost::shared_ptr<bool> pHandleUserErrorsOnly =
195 boost::make_shared<bool>(true);
196
197 using boost::bind;
198 using namespace module_context;
199
200 // Check to see whether the error handler has changed immediately after init
201 // (to find changes from e.g. .Rprofile) and after every console prompt.
202 events().onConsolePrompt.connect(bind(detectHandlerChange,
203 pErrorHandler, false));
204 events().onDeferredInit.connect(bind(detectHandlerChange,
205 pErrorHandler, true));
206 prefs::userState().onChanged.connect(bind(onUserSettingsChanged,
207 _2,
208 pErrorHandler,
209 pHandleUserErrorsOnly));
210
211 json::JsonRpcFunction setErrMgmt =
212 bind(setErrHandlerType, pErrorHandler, _1, _2);
213
214 ExecBlock initBlock;
215 initBlock.addFunctions()
216 (bind(registerRpcMethod, "set_error_management_type", setErrMgmt))
217 (bind(sourceModuleRFile, "SessionErrors.R"))
218 (bind(initializeErrManagement, pErrorHandler, pHandleUserErrorsOnly));
219
220 return initBlock.execute();
221 }
222
223 } // namespace errors
224 } // namespace modules
225 } // namespace session
226 } // namespace rstudio
227
228
229