Skip to content

Commit 64290a7

Browse files
lionel-eddelbuettel
authored andcommitted
Improvements for unwind-protect (#877)
* Safer unwound indicator in unit tests * Pass extra arguments from unitTestSetup() to sourceCpp() For easier debugging * Add `unwindProtect` plugin * Rename `RCPP_PROTECTED_EVAL` to `RCPP_USE_UNWIND_PROTECT` Because the new API is now more general than just evaluation of R code * Move unwind.h from Rcpp/api/meat/ to Rcpp/unwindProtect.h * Make LongjumpException public * Remove default argument for Rcpp_fast_eval()
1 parent e5591c5 commit 64290a7

21 files changed

+207
-157
lines changed

ChangeLog

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,38 @@
1+
2+
2018-07-10 Lionel Henry <[email protected]>
3+
4+
* inst/include/Rcpp/exceptions.h: Move LongjumpException from the
5+
Rcpp::internal namespace to the public Rcpp namespace. If you have a
6+
catch-all statement like `catch (...)`, be sure to catch and rethrow
7+
Rcpp::LongjumpException to prevent the R longjump from being ignored.
8+
* inst/include/Rcpp/macros/macros.h (VOID_END_RCPP): idem
9+
* src/attributes.cpp: idem
10+
11+
2018-07-05 Lionel Henry <[email protected]>
12+
13+
* inst/include/Rcpp/api/meat/Rcpp_eval.h: Rename `RCPP_PROTECTED_EVAL`
14+
to `RCPP_USE_UNWIND_PROTECT` because the new API is now more general
15+
than just evaluation of R code.
16+
* inst/NEWS.Rd: idem
17+
* inst/unitTests/runit.interface.R: idem
18+
19+
2018-07-05 Lionel Henry <[email protected]>
20+
21+
* R/unit.tests.R (unitTestSetup): Pass extra arguments to sourceCpp()
22+
for easier debugging.
23+
24+
2018-07-05 Lionel Henry <[email protected]>
25+
26+
* R/Attributes.R (.plugins[["unwindProtect"]]): You can now add
27+
`[[Rcpp::plugins(unwindProtect)]]` in one of your source file to enable
28+
the new unwind-protect mechanism easily. It appends
29+
`-DRCPP_USE_UNWIND_PROTECT` to `PKG_CPPFLAGS`.
30+
31+
This is safer than using a `#define` because it ensures unwind-protect
32+
is enabled in all compilation units, including RcppExports.cpp.
33+
34+
* inst/unitTests/cpp/stack.cpp: Use new plugin to enable unwind-protect.
35+
136
2018-06-22 Kevin Ushey <[email protected]>
237

338
* inst/include/Rcpp/api/meat/Rcpp_eval.h: Ensure R_BaseEnv is used

R/Attributes.R

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,10 @@ compileAttributes <- function(pkgdir = ".", verbose = getOption("verbose")) {
523523
PKG_LIBS="-fopenmp"))
524524
}
525525

526+
.plugins[["unwindProtect"]] <- function() {
527+
list(env = list(PKG_CPPFLAGS = "-DRCPP_USE_UNWIND_PROTECT"))
528+
}
529+
526530
# register a plugin
527531
registerPlugin <- function(name, plugin) {
528532
.plugins[[name]] <- plugin # #nocov

R/unit.tests.R

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,15 @@ test <- function(output=if(file.exists("/tmp")) "/tmp" else getwd(),
8888
}
8989

9090
unitTestSetup <- function(file, packages=NULL,
91-
pathToRcppTests=system.file("unitTests", package = "Rcpp")) {
91+
pathToRcppTests=system.file("unitTests", package = "Rcpp"),
92+
...) {
9293
function() {
9394
if (! is.null(packages)) {
9495
for (p in packages) {
9596
suppressMessages(require(p, character.only=TRUE))
9697
}
9798
}
98-
sourceCpp(file.path(pathToRcppTests, "cpp", file))
99+
sourceCpp(file.path(pathToRcppTests, "cpp", file), ...)
99100
}
100101
}
101102

inst/NEWS.Rd

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
finely (Romain in \ghpr{868}).
2929
\item The new \code{Rcpp_fast_eval} is used instead of
3030
\code{Rcpp_eval} though this still requires setting
31-
\code{RCPP_PROTECTED_EVAL} before including \code{Rcpp.h} (Qiang
31+
\code{RCPP_USE_UNWIND_PROTECT} before including \code{Rcpp.h} (Qiang
3232
Kou in \ghpr{867} closing \ghit{866}).
3333
\item The \code{Rcpp::unwindProtect()} function extracts the
3434
unwinding from the \code{Rcpp_fast_eval()} function and makes it
@@ -110,7 +110,7 @@
110110
(Kevin in \ghpr{784}).
111111
\item Use public R APIs for \code{new_env} (Kevin in \ghpr{785}).
112112
\item Evaluation of R code is now safer when compiled against R
113-
3.5 (you also need to explicitly define \code{RCPP_PROTECTED_EVAL}
113+
3.5 (you also need to explicitly define \code{RCPP_USE_UNWIND_PROTECT}
114114
before including \code{Rcpp.h}). Longjumps of all kinds (condition
115115
catching, returns, restarts, debugger exit) are appropriately
116116
detected and handled, e.g. the C++ stack unwinds correctly

inst/include/Rcpp/DataFrame.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ namespace Rcpp{
132132
obj.attr( "names") = names ;
133133
Shield<SEXP> call( Rf_lang3(as_df_symb, obj, wrap( strings_as_factors ) ) ) ;
134134
SET_TAG( CDDR(call), strings_as_factors_symb ) ;
135-
Shield<SEXP> res( Rcpp_fast_eval( call ) ) ;
135+
Shield<SEXP> res(Rcpp_fast_eval(call, R_GlobalEnv));
136136
DataFrame_Impl out( res ) ;
137137
return out ;
138138

inst/include/Rcpp/Environment.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ namespace Rcpp{
3333
if( Rf_isEnvironment(x) ) return x ;
3434
SEXP asEnvironmentSym = Rf_install("as.environment");
3535
try {
36-
Shield<SEXP> res( Rcpp_fast_eval( Rf_lang2( asEnvironmentSym, x ) ) );
36+
Shield<SEXP> res(Rcpp_fast_eval(Rf_lang2(asEnvironmentSym, x), R_GlobalEnv));
3737
return res ;
3838
} catch( const eval_error& ex) {
3939
const char* fmt = "Cannot convert object to an environment: "
@@ -374,7 +374,7 @@ namespace Rcpp{
374374
try{
375375
SEXP getNamespaceSym = Rf_install("getNamespace");
376376
Shield<SEXP> package_str( Rf_mkString(package.c_str()) );
377-
env = Rcpp_fast_eval( Rf_lang2(getNamespaceSym, package_str) ) ;
377+
env = Rcpp_fast_eval(Rf_lang2(getNamespaceSym, package_str), R_GlobalEnv);
378378
} catch( ... ){
379379
throw no_such_namespace( package ) ;
380380
}
@@ -393,7 +393,7 @@ namespace Rcpp{
393393
*/
394394
Environment_Impl new_child(bool hashed) const {
395395
SEXP newEnvSym = Rf_install("new.env");
396-
return Environment_Impl( Rcpp_fast_eval(Rf_lang3( newEnvSym, Rf_ScalarLogical(hashed), Storage::get__() )) );
396+
return Environment_Impl(Rcpp_fast_eval(Rf_lang3(newEnvSym, Rf_ScalarLogical(hashed), Storage::get__()), R_GlobalEnv));
397397
}
398398

399399

inst/include/Rcpp/Function.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ namespace Rcpp{
7979

8080
SEXP operator()() const {
8181
Shield<SEXP> call(Rf_lang1(Storage::get__()));
82-
return Rcpp_fast_eval(call);
82+
return Rcpp_fast_eval(call, R_GlobalEnv);
8383
}
8484

8585
#include <Rcpp/generated/Function__operator.h>

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

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,10 @@
2121
#include <Rcpp/Interrupt.h>
2222
#include <Rversion.h>
2323

24-
#if (defined(RCPP_PROTECTED_EVAL) && defined(R_VERSION) && R_VERSION >= R_Version(3, 5, 0))
25-
#define RCPP_USE_PROTECT_UNWIND
26-
#include <Rcpp/api/meat/unwind.h>
27-
#endif
28-
2924

3025
namespace Rcpp { namespace internal {
3126

32-
#ifdef RCPP_USE_PROTECT_UNWIND
27+
#ifdef RCPP_USING_UNWIND_PROTECT
3328

3429
struct EvalData {
3530
SEXP expr;
@@ -61,7 +56,7 @@ inline SEXP Rcpp_eval_impl(SEXP expr, SEXP env) {
6156

6257
namespace Rcpp {
6358

64-
#ifdef RCPP_USE_PROTECT_UNWIND
59+
#ifdef RCPP_USING_UNWIND_PROTECT
6560

6661
inline SEXP Rcpp_fast_eval(SEXP expr, SEXP env) {
6762
internal::EvalData data(expr, env);

inst/include/Rcpp/exceptions.h

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -114,51 +114,57 @@ namespace Rcpp {
114114
throw Rcpp::exception(message.c_str());
115115
} // #nocov end
116116

117-
namespace internal {
117+
} // namespace Rcpp
118118

119-
inline SEXP longjumpSentinel(SEXP token) {
120-
SEXP sentinel = PROTECT(Rf_allocVector(VECSXP, 1));
121-
SET_VECTOR_ELT(sentinel, 0, token);
122119

123-
SEXP sentinelClass = PROTECT(Rf_mkString("Rcpp:longjumpSentinel"));
124-
Rf_setAttrib(sentinel, R_ClassSymbol, sentinelClass) ;
120+
namespace Rcpp { namespace internal {
125121

126-
UNPROTECT(2);
127-
return sentinel;
128-
}
122+
inline SEXP longjumpSentinel(SEXP token) {
123+
SEXP sentinel = PROTECT(Rf_allocVector(VECSXP, 1));
124+
SET_VECTOR_ELT(sentinel, 0, token);
129125

130-
inline bool isLongjumpSentinel(SEXP x) {
131-
return
132-
Rf_inherits(x, "Rcpp:longjumpSentinel") &&
133-
TYPEOF(x) == VECSXP &&
134-
Rf_length(x) == 1;
135-
}
126+
SEXP sentinelClass = PROTECT(Rf_mkString("Rcpp:longjumpSentinel"));
127+
Rf_setAttrib(sentinel, R_ClassSymbol, sentinelClass) ;
136128

137-
inline SEXP getLongjumpToken(SEXP sentinel) {
138-
return VECTOR_ELT(sentinel, 0);
139-
}
129+
UNPROTECT(2);
130+
return sentinel;
131+
}
132+
133+
inline bool isLongjumpSentinel(SEXP x) {
134+
return
135+
Rf_inherits(x, "Rcpp:longjumpSentinel") &&
136+
TYPEOF(x) == VECSXP &&
137+
Rf_length(x) == 1;
138+
}
139+
140+
inline SEXP getLongjumpToken(SEXP sentinel) {
141+
return VECTOR_ELT(sentinel, 0);
142+
}
140143

141-
struct LongjumpException {
142-
SEXP token;
143-
LongjumpException(SEXP token_) : token(token_) {
144-
if (isLongjumpSentinel(token)) {
145-
token = getLongjumpToken(token);
146-
}
147-
}
148-
};
149-
150-
inline void resumeJump(SEXP token) {
151-
if (isLongjumpSentinel(token)) {
152-
token = getLongjumpToken(token);
153-
}
154-
::R_ReleaseObject(token);
144+
inline void resumeJump(SEXP token) {
145+
if (isLongjumpSentinel(token)) {
146+
token = getLongjumpToken(token);
147+
}
148+
::R_ReleaseObject(token);
155149
#if (defined(R_VERSION) && R_VERSION >= R_Version(3, 5, 0))
156-
::R_ContinueUnwind(token);
150+
::R_ContinueUnwind(token);
157151
#endif
158-
Rf_error("Internal error: Rcpp longjump failed to resume");
159-
}
152+
Rf_error("Internal error: Rcpp longjump failed to resume");
153+
}
154+
155+
}} // namespace Rcpp::internal
156+
160157

161-
} // namespace internal
158+
namespace Rcpp {
159+
160+
struct LongjumpException {
161+
SEXP token;
162+
LongjumpException(SEXP token_) : token(token_) {
163+
if (internal::isLongjumpSentinel(token)) {
164+
token = internal::getLongjumpToken(token);
165+
}
166+
}
167+
};
162168

163169
} // namespace Rcpp
164170

0 commit comments

Comments
 (0)