diff --git a/NEWS.md b/NEWS.md index 410994f8..a586ef2c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,8 @@ # cpp11 (development version) +* `cpp11::function` now protects its underlying function, for maximum safety + (#294). + * `cpp11::writable::r_vector::proxy` now implements copy assignment. Practically this means that `x[i] = y[i]` now works when both `x` and `y` are writable vectors (#300, #339). diff --git a/inst/include/cpp11/function.hpp b/inst/include/cpp11/function.hpp index a40c79b4..066ac667 100644 --- a/inst/include/cpp11/function.hpp +++ b/inst/include/cpp11/function.hpp @@ -31,7 +31,7 @@ class function { } private: - SEXP data_; + sexp data_; template void construct_call(SEXP val, const named_arg& arg, Args&&... args) const { @@ -71,36 +71,65 @@ class package { return safe[Rf_findVarInFrame](R_NamespaceRegistry, name_sexp); } + // Either base env or in namespace registry, so no protection needed SEXP data_; }; +namespace detail { + +// Special internal way to call `base::message()` +// +// - Pure C, so call with `safe[]` +// - Holds a `static SEXP` for the `base::message` function protected with +// `R_PreserveObject()` +// +// We don't use a `static cpp11::function` because that will infinitely retain a cell in +// our preserve list, which can throw off our counts in the preserve list tests. +inline void r_message(const char* x) { + static SEXP fn = NULL; + + if (fn == NULL) { + fn = Rf_findFun(Rf_install("message"), R_BaseEnv); + R_PreserveObject(fn); + } + + SEXP x_char = PROTECT(Rf_mkCharCE(x, CE_UTF8)); + SEXP x_string = PROTECT(Rf_ScalarString(x_char)); + + SEXP call = PROTECT(Rf_lang2(fn, x_string)); + + Rf_eval(call, R_GlobalEnv); + + UNPROTECT(3); +} + +} // namespace detail + inline void message(const char* fmt_arg) { - static auto R_message = cpp11::package("base")["message"]; #ifdef CPP11_USE_FMT std::string msg = fmt::format(fmt_arg); - R_message(msg.c_str()); + safe[detail::r_message](msg.c_str()); #else char buff[1024]; int msg; msg = std::snprintf(buff, 1024, "%s", fmt_arg); if (msg >= 0 && msg < 1024) { - R_message(buff); + safe[detail::r_message](buff); } #endif } template void message(const char* fmt_arg, Args... args) { - static auto R_message = cpp11::package("base")["message"]; #ifdef CPP11_USE_FMT std::string msg = fmt::format(fmt_arg, args...); - R_message(msg.c_str()); + safe[detail::r_message](msg.c_str()); #else char buff[1024]; int msg; msg = std::snprintf(buff, 1024, fmt_arg, args...); if (msg >= 0 && msg < 1024) { - R_message(buff); + safe[detail::r_message](buff); } #endif }