2323
2424#if (defined(RCPP_PROTECTED_EVAL) && defined(R_VERSION) && R_VERSION >= R_Version(3, 5, 0))
2525#define RCPP_USE_PROTECT_UNWIND
26+ #include < csetjmp>
2627#endif
2728
2829
@@ -31,23 +32,25 @@ namespace internal {
3132
3233#ifdef RCPP_USE_PROTECT_UNWIND
3334
35+ // Store the jump buffer as a static variable in function scope
36+ // because inline variables are a C++17 extension.
37+ inline std::jmp_buf* get_jmpbuf_ptr () {
38+ static std::jmp_buf jmpbuf;
39+ return &jmpbuf;
40+ }
41+
3442 struct EvalData {
3543 SEXP expr;
3644 SEXP env;
3745 EvalData (SEXP expr_, SEXP env_) : expr(expr_), env(env_) { }
3846 };
3947
48+ // First jump back to the protected context with a C longjmp because
49+ // `Rcpp_protected_eval()` is called from C and we can't safely throw
50+ // exceptions across C frames.
4051 inline void Rcpp_maybe_throw (void * data, Rboolean jump) {
4152 if (jump) {
42- SEXP token = static_cast <SEXP>(data);
43-
44- // Keep the token protected while unwinding because R code might run
45- // in C++ destructors. Can't use PROTECT() for this because
46- // UNPROTECT() might be called in a destructor, for instance if a
47- // Shield<SEXP> is on the stack.
48- ::R_PreserveObject (token);
49-
50- throw LongjumpException (token);
53+ longjmp (*internal::get_jmpbuf_ptr (), 1 );
5154 }
5255 }
5356
@@ -78,6 +81,17 @@ namespace internal {
7881 inline SEXP Rcpp_fast_eval (SEXP expr, SEXP env) {
7982 internal::EvalData data (expr, env);
8083 Shield<SEXP> token (::R_MakeUnwindCont ());
84+
85+ if (setjmp (*internal::get_jmpbuf_ptr ())) {
86+ // Keep the token protected while unwinding because R code might run
87+ // in C++ destructors. Can't use PROTECT() for this because
88+ // UNPROTECT() might be called in a destructor, for instance if a
89+ // Shield<SEXP> is on the stack.
90+ ::R_PreserveObject (token);
91+
92+ throw internal::LongjumpException (token);
93+ }
94+
8195 return ::R_UnwindProtect (internal::Rcpp_protected_eval, &data,
8296 internal::Rcpp_maybe_throw, token,
8397 token);
0 commit comments