/*
* RExec.cpp
*
* Copyright (C) 2009-19 by RStudio, PBC
*
* Unless you have received this program directly from RStudio pursuant
* to the terms of a commercial license agreement with RStudio, then
* this program is licensed to you under the terms of version 3 of the
* GNU Affero General Public License. This program is distributed WITHOUT
* ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
* AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
*
*/
#define R_INTERNAL_FUNCTIONS
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
extern "C" {
LibExtern Rboolean R_interrupts_suspended;
LibExtern int R_interrupts_pending;
#ifdef _WIN32
LibExtern int UserBreak;
#endif
}
using namespace rstudio::core ;
namespace rstudio {
namespace r {
namespace exec {
namespace {
// create a scope for disabling any installed error handlers (e.g. recover)
// we need to do this so that recover isn't invoked while we are running
// R code within an r::exec scope -- when the user presses 0 to exit
// from recover and jump_to_top it gets eaten by the R_ToplevelExecute
// context so the console becomes unresponsive
class DisableErrorHandlerScope : boost::noncopyable
{
public:
DisableErrorHandlerScope()
: didDisable_(false)
{
// allow users to enable / disable suppression of error handlers
// (primarily for debugging when behind-the-scenes R code emits an error
// that we'd like to learn a bit more about)
bool suppressed = r::options::getOption("rstudio.errors.suppressed", true, false);
if (!suppressed)
return;
SEXP handlerSEXP = r::options::setErrorOption(R_NilValue);
if (handlerSEXP != R_NilValue)
{
preservedSEXP_.set(handlerSEXP);
didDisable_ = true;
}
}
virtual ~DisableErrorHandlerScope()
{
try
{
if (didDisable_)
r::options::setErrorOption(preservedSEXP_.get());
}
catch(...)
{
}
}
private:
bool didDisable_;
r::sexp::PreservedSEXP preservedSEXP_;
};
Error parseString(const std::string& str, SEXP* pSEXP, sexp::Protect* pProtect)
{
// string to parse
SEXP cv = sexp::create(str, pProtect);
// do the parse and protect the result
ParseStatus ps;
*pSEXP=R_ParseVector(cv, 1, &ps, R_NilValue);
pProtect->add(*pSEXP);
// check error/success
if (ps != PARSE_OK)
{
Error error(errc::ExpressionParsingError, ERROR_LOCATION);
error.addProperty("code", str);
return error;
}
else
{
return Success();
}
}
// evaluate expressions without altering the error handler (use with caution--
// a user-supplied error handler may be invoked if the expression raises
// an error!)
enum EvalType {
EvalTry, // use R_tryEval
EvalDirect // use Rf_eval directly
};
Error evaluateExpressionsUnsafe(SEXP expr,
SEXP env,
SEXP* pSEXP,
sexp::Protect* pProtect,
EvalType evalType)
{
int er=0;
int i=0,l;
// if we have an entire expression list, evaluate its contents one-by-one
// and return only the last one
if (TYPEOF(expr)==EXPRSXP)
{
DisableDebugScope disableStepInto(env);
l = LENGTH(expr);
while (iadd(*pSEXP);
if (er)
{
// get error message -- note this results in a recursive call to
// evaluate expressions during the fetching of the error. if this
// call yielded an error then this could infinitely recurse. it doesn't
// appears as if geterrmessage will ever return an error state so
// this is likely not an issue. still, if we were concerned about it
// then we could simply read the error buffer directly from the module
// where do_geterrmessage is defined (errors.c)
return rCodeExecutionError(getErrorMessage(), ERROR_LOCATION);
}
else
{
return Success();
}
}
Error evaluateExpressions(SEXP expr,
SEXP env,
SEXP* pSEXP,
sexp::Protect* pProtect)
{
// disable custom error handlers while we execute code
DisableErrorHandlerScope disableErrorHandler;
return evaluateExpressionsUnsafe(expr, env, pSEXP, pProtect, EvalTry);
}
Error evaluateExpressions(SEXP expr, SEXP* pSEXP, sexp::Protect* pProtect)
{
return evaluateExpressions(expr, R_GlobalEnv, pSEXP, pProtect);
}
void topLevelExec(void *data)
{
boost::function* pFunction = (boost::function*)data;
pFunction->operator()();
}
struct SEXPTopLevelExecContext
{
boost::function function;
SEXP* pReturnSEXP ;
};
void SEXPTopLevelExec(void *data)
{
SEXPTopLevelExecContext* pContext = (SEXPTopLevelExecContext*)data;
*(pContext->pReturnSEXP) = pContext->function();
}
} // anonymous namespace
Error executeSafely(boost::function function)
{
// disable custom error handlers while we execute code
DisableErrorHandlerScope disableErrorHandler;
DisableDebugScope disableStepInto(R_GlobalEnv);
Rboolean success = R_ToplevelExec(topLevelExec, (void*)&function);
if (!success)
{
return rCodeExecutionError(getErrorMessage(), ERROR_LOCATION);
}
else
{
return Success();
}
}
core::Error executeSafely(boost::function function, SEXP* pSEXP)
{
// disable custom error handlers while we execute code
DisableErrorHandlerScope disableErrorHandler;
DisableDebugScope disableStepInto(R_GlobalEnv);
SEXPTopLevelExecContext context ;
context.function = function ;
context.pReturnSEXP = pSEXP ;
Rboolean success = R_ToplevelExec(SEXPTopLevelExec, (void*)&context);
if (!success)
{
return rCodeExecutionError(getErrorMessage(), ERROR_LOCATION);
}
else
{
return Success();
}
}
Error executeStringUnsafe(const std::string& str,
SEXP envirSEXP,
SEXP* pSEXP,
sexp::Protect* pProtect)
{
SEXP parsedSEXP = R_NilValue;
Error error = r::exec::parseString(str, &parsedSEXP, pProtect);
if (error)
return error;
return evaluateExpressionsUnsafe(parsedSEXP, envirSEXP, pSEXP, pProtect, EvalDirect);
}
Error executeStringUnsafe(const std::string& str,
SEXP* pSEXP,
sexp::Protect* pProtect)
{
return executeStringUnsafe(str, R_GlobalEnv, pSEXP, pProtect);
}
Error executeString(const std::string& str)
{
sexp::Protect rProtect;
SEXP ignoredSEXP ;
return evaluateString(str, &ignoredSEXP, &rProtect);
}
Error evaluateString(const std::string& str,
SEXP* pSEXP,
sexp::Protect* pProtect)
{
// refresh source if necessary (no-op in production)
r::sourceManager().reloadIfNecessary();
// surrond the string with try in silent mode so we can capture error text
std::string rCode = "base::try(" + str + ", TRUE)";
// parse expression
SEXP ps;
Error parseError = parseString(rCode, &ps, pProtect);
if (parseError)
return parseError;
// evaluate the expression
Error evalError = evaluateExpressions(ps, pSEXP, pProtect);
if (evalError)
{
evalError.addProperty("code", str);
return evalError;
}
// check for try-error
if (Rf_inherits(*pSEXP, "try-error"))
{
// get error message (merely log on failure so we can continue
// and return the real error)
std::string errorMsg ;
Error extractError = sexp::extract(*pSEXP, &errorMsg);
if (extractError)
LOG_ERROR(extractError);
// add it to the error
return rCodeExecutionError(errorMsg, ERROR_LOCATION);
}
return Success();
}
bool atTopLevelContext()
{
return context::RCntxt::begin()->callflag() == CTXT_TOPLEVEL;
}
RFunction::RFunction(SEXP functionSEXP)
{
functionSEXP_ = functionSEXP;
preserver_.add(functionSEXP_);
}
RFunction::~RFunction()
{
}
void RFunction::commonInit(const std::string& functionName)
{
// refresh source if necessary (no-op in production)
r::sourceManager().reloadIfNecessary();
// record functionName (used later for diagnostics)
functionName_ = functionName;
// get name & ns
std::string name, ns;
// check for namespace qualifier
std::string nsQual(":::");
size_t pos = functionName_.find(nsQual);
if (pos != std::string::npos)
{
ns = functionName_.substr(0, pos);
name = functionName_.substr(pos + nsQual.size());
}
else
{
name = functionName_;
}
// lookup function
functionSEXP_ = sexp::findFunction(name, ns);
if (functionSEXP_ != R_UnboundValue)
preserver_.add(functionSEXP_);
}
Error RFunction::callUnsafe()
{
return call(R_GlobalEnv, false);
}
Error RFunction::call(SEXP evalNS, bool safely)
{
sexp::Protect rProtect;
SEXP ignoredResultSEXP ;
return call(evalNS, safely, &ignoredResultSEXP, &rProtect);
}
Error RFunction::call(SEXP* pResultSEXP, sexp::Protect* pProtect)
{
return call(R_GlobalEnv, pResultSEXP, pProtect);
}
Error RFunction::call(SEXP evalNS, SEXP* pResultSEXP, sexp::Protect* pProtect)
{
return call(evalNS, true, pResultSEXP, pProtect);
}
Error RFunction::call(SEXP evalNS, bool safely, SEXP* pResultSEXP,
sexp::Protect* pProtect)
{
// verify the function
if (functionSEXP_ == R_UnboundValue)
{
LOG_ERROR_MESSAGE("Failed to find function: '" + functionName_ + "'");
Error error(errc::SymbolNotFoundError, ERROR_LOCATION);
if (!functionName_.empty())
error.addProperty("symbol", functionName_);
return error;
}
// create the call object (LANGSXP) with the correct number of elements
SEXP callSEXP ;
pProtect->add(callSEXP = Rf_allocVector(LANGSXP, 1 + params_.size()));
SET_TAG(callSEXP, R_NilValue); // just like do_ascall() does
// assign the function to the first element of the call
SETCAR(callSEXP, functionSEXP_);
// assign parameters to the subseqent elements of the call
SEXP nextSlotSEXP = CDR(callSEXP);
for (std::vector::const_iterator
it = params_.begin(); it != params_.end(); ++it)
{
SETCAR(nextSlotSEXP, it->valueSEXP);
// parameters can optionally be named
if (!(it->name.empty()))
SET_TAG(nextSlotSEXP, Rf_install(it->name.c_str()));
nextSlotSEXP = CDR(nextSlotSEXP);
}
// call the function
Error error = safely ?
evaluateExpressions(callSEXP, evalNS, pResultSEXP, pProtect) :
evaluateExpressionsUnsafe(callSEXP, evalNS, pResultSEXP, pProtect,
EvalTry);
if (error)
return error;
// return success
return Success();
}
FilePath rBinaryPath()
{
FilePath binPath = FilePath(R_HomeDir()).completePath("bin");
#ifdef _WIN32
return binPath.completePath("Rterm.exe");
#else
return binPath.completePath("R");
#endif
}
Error system(const std::string& command, std::string* pOutput)
{
r::exec::RFunction system("system", command);
system.addParam("intern", true);
system.addParam("ignore.stderr", true);
// call it
Error error = system.call(pOutput);
if (error)
{
// if it is NoDataAvailable this means empty output
if (error == r::errc::NoDataAvailableError)
{
pOutput->clear();
return Success();
}
else
{
return error;
}
}
else
{
return Success();
}
}
void error(const std::string& message)
{
Rf_error(message.c_str());
}
void errorCall(SEXP call, const std::string& message)
{
Rf_errorcall(call, message.c_str());
}
std::string getErrorMessage()
{
std::string errMessage ;
Error callError = RFunction("geterrmessage").call(&errMessage);
if (callError)
LOG_ERROR(callError);
return errMessage;
}
void warning(const std::string& warning)
{
Rf_warning(warning.c_str());
}
void message(const std::string& message)
{
Error error = r::exec::RFunction("message", message).call();
if (error)
LOG_ERROR(error);
}
bool interruptsPending()
{
#ifdef _WIN32
return UserBreak == 1 ? true : false;
#else
return R_interrupts_pending == 1 ? true : false;
#endif
}
void setInterruptsPending(bool pending)
{
#ifdef _WIN32
UserBreak = pending ? 1 : 0;
#else
R_interrupts_pending = pending ? 1 : 0;
#endif
}
void checkUserInterrupt()
{
R_CheckUserInterrupt();
}
IgnoreInterruptsScope::IgnoreInterruptsScope()
: pSignalBlocker_(new core::system::SignalBlocker())
{
// save suspend state and set suspend flag
previousInterruptsSuspended_ = (R_interrupts_suspended == TRUE);
R_interrupts_suspended = TRUE;
// clear existing
setInterruptsPending(false);
// enable signal blocker
Error error = pSignalBlocker_->block(core::system::SigInt);
if (error)
LOG_ERROR(error);
}
IgnoreInterruptsScope::~IgnoreInterruptsScope()
{
try
{
// delete signal blocker (may cause delivery of one of the blocked
// interrupts, but we restore the previous interrupt state below
// so this is no problem)
pSignalBlocker_.reset();
// restore suspended state
R_interrupts_suspended = previousInterruptsSuspended_ ? TRUE : FALSE;
// clear state
setInterruptsPending(false);
}
catch(...)
{
}
}
DisableDebugScope::DisableDebugScope(SEXP env):
rdebug_(0),
env_(nullptr)
{
// nothing to do if no environment
if (env == nullptr) {
return;
}
// check to see whether there's a debug flag set on this environment
rdebug_ = RDEBUG(env);
// if there is, turn it off and save the old flag for restoration
if (rdebug_ != 0)
{
SET_RDEBUG(env, 0);
env_ = env;
}
}
DisableDebugScope::~DisableDebugScope()
{
// if we disabled debugging and debugging didn't end during the command
// evaluation, restore debugging
if (env_ != nullptr && !atTopLevelContext())
{
SET_RDEBUG(env_, rdebug_);
}
}
} // namespace exec
} // namespace r
} // namespace rstudio