Skip to content

Commit 454de74

Browse files
committed
catch interrupts in 'tryCatch' rather than R_ToplevelExec
1 parent 1c90562 commit 454de74

File tree

1 file changed

+29
-69
lines changed

1 file changed

+29
-69
lines changed

inst/include/Rcpp/api/meat/Rcpp_eval.h

Lines changed: 29 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -22,98 +22,58 @@
2222

2323
namespace Rcpp {
2424

25-
struct EvalCall {
26-
27-
// Related to call object
28-
SEXP expr;
29-
SEXP env;
30-
SEXP result;
31-
32-
// Related to error recording / forwarding
33-
bool error_occurred;
34-
std::string error_message;
35-
};
36-
37-
inline void Rcpp_eval(void* data) {
38-
39-
EvalCall* evalCall = (EvalCall*) data;
25+
inline SEXP Rcpp_eval(SEXP expr, SEXP env) {
4026

41-
SEXP tryCatchSym = ::Rf_install("tryCatch");
42-
SEXP evalqSym = ::Rf_install("evalq");
43-
SEXP conditionMessageSym = ::Rf_install("conditionMessage");
44-
SEXP errorSym = ::Rf_install("error");
45-
46-
// get the Rcpp error recorder
47-
SEXP errorRecorder = Rf_findFun(
48-
Rf_install(".rcpp_error_recorder"),
49-
Environment::Rcpp_namespace()
27+
// 'identity' function used to capture errors, interrupts
28+
SEXP identity = Rf_findFun(
29+
::Rf_install("identity"),
30+
R_BaseNamespace
5031
);
5132

52-
if (errorRecorder == R_UnboundValue) {
53-
evalCall->error_occurred = true;
54-
evalCall->error_message =
55-
"Failed to find Rcpp error recorder: please ensure\n"
56-
"you have the latest version of Rcpp installed.";
57-
return;
33+
if (identity == R_UnboundValue) {
34+
stop("Failed to find 'base::identity()'");
5835
}
5936

6037
// define the evalq call -- the actual R evaluation we
6138
// want to execute
6239
Shield<SEXP> evalqCall(Rf_lang3(
63-
evalqSym,
64-
evalCall->expr,
65-
evalCall->env
40+
::Rf_install("evalq"),
41+
expr,
42+
env
6643
));
6744

6845
// define the call -- enclose with `tryCatch` so we can record
6946
// and later forward error messages
70-
Shield<SEXP> call(Rf_lang3(
71-
tryCatchSym,
47+
Shield<SEXP> call(Rf_lang4(
48+
::Rf_install("tryCatch"),
7249
evalqCall,
73-
errorRecorder
50+
identity,
51+
identity
7452
));
75-
SET_TAG(CDDR(call), errorSym);
53+
SET_TAG(CDDR(call), ::Rf_install("error"));
54+
SET_TAG(CDDDR(call), ::Rf_install("interrupt"));
7655

7756
// execute the call
7857
Shield<SEXP> res(::Rf_eval(call, R_GlobalEnv));
7958

8059
// check for error
81-
if (error_occured()) {
82-
Shield<SEXP> current_error (rcpp_get_current_error());
83-
Shield<SEXP> conditionMessageCall (::Rf_lang2(conditionMessageSym, current_error));
84-
Shield<SEXP> condition_message (::Rf_eval(conditionMessageCall, R_GlobalEnv));
85-
evalCall->error_occurred = true;
86-
evalCall->error_message = std::string(CHAR(::Rf_asChar(condition_message)));
87-
} else {
88-
evalCall->error_occurred = false;
89-
evalCall->result = res;
60+
if (Rf_inherits(res, "error")) {
61+
62+
Shield<SEXP> conditionMessageCall(::Rf_lang2(
63+
::Rf_install("conditionMessage"),
64+
res
65+
));
66+
67+
Shield<SEXP> conditionMessage(::Rf_eval(conditionMessageCall, R_GlobalEnv));
68+
throw eval_error(CHAR(STRING_ELT(conditionMessage, 0)));
9069
}
9170

92-
// Reset the current error for the next evaluation
93-
reset_current_error();
94-
95-
}
96-
97-
inline SEXP Rcpp_eval(SEXP expr, SEXP env) {
98-
99-
// create the call object
100-
EvalCall call;
101-
call.expr = expr;
102-
call.env = env;
103-
104-
// execute it
105-
Rboolean completed = R_ToplevelExec(Rcpp_eval, (void*) &call);
106-
107-
// handle error or result if it completed, else throw interrupt
108-
if (completed) {
109-
if (call.error_occurred) {
110-
throw eval_error(call.error_message);
111-
} else {
112-
return call.result;
113-
}
114-
} else {
71+
// check for interrupt
72+
if (Rf_inherits(res, "interrupt")) {
11573
throw internal::InterruptedException();
11674
}
75+
76+
return res;
11777
}
11878

11979
} // namespace Rcpp

0 commit comments

Comments
 (0)