/* * R : A Computer Language for Statistical Data Analysis * Copyright (C) 1995, 1996 Robert Gentleman and Ross Ihaka * Copyright (C) 1998-2020 The R Core Team. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, a copy is available at * https://www.R-project.org/Licenses/ * * * Contexts: * * A linked-list of execution contexts is kept so that control-flow * constructs like "next", "break" and "return" will work. It is also * used for error returns to top-level. * * context[k] -> context[k-1] -> ... -> context[0] * ^ ^ * R_GlobalContext R_ToplevelContext * * Contexts are allocated on the stack as the evaluator invokes itself * recursively. The memory is reclaimed naturally on return through * the recursions (the R_GlobalContext pointer needs adjustment). * * A context contains the following information (and more): * * nextcontext the next level context * cjmpbuf longjump information for non-local return * cstacktop the current level of the pointer protection stack * callflag the context "type" * call the call (name of function, or expression to * get the function) that effected this * context if a closure, otherwise often NULL. * callfun the function, if this was a closure. * cloenv for closures, the environment of the closure. * sysparent the environment the closure was called from * conexit code for on.exit calls, to be executed in cloenv * at exit from the closure (normal or abnormal). * cend a pointer to function which executes if there is * non-local return (i.e. an error) * cenddata a void pointer to data for cend to use * vmax the current setting of the R_alloc stack * srcref the srcref at the time of the call * * Context types can be one of: * * CTXT_TOPLEVEL The toplevel context * CTXT_BREAK target for "break" * CTXT_NEXT target for "next" * CTXT_LOOP target for either "break" or "next" * CTXT_RETURN target for "return" (i.e. a closure) * CTXT_BROWSER target for "return" to exit from browser * CTXT_CCODE other functions that need clean up if an error occurs * CTXT_RESTART a function call to restart was made inside the * closure. * * Code (such as the sys.xxx) that looks for CTXT_RETURN must also * look for a CTXT_RESTART and CTXT_GENERIC. * The mechanism used by restart is to change * the context type; error/errorcall then looks for a RESTART and does * a long jump there if it finds one. * * A context is created with a call to * * void begincontext(RCNTXT *cptr, int flags, * SEXP syscall, SEXP env, SEXP * sysp, SEXP promargs, SEXP callfun) * * which sets up the context pointed to by cptr in the appropriate way. * When the context goes "out-of-scope" a call to * * void endcontext(RCNTXT *cptr) * * restores the previous context (i.e. it adjusts the R_GlobalContext * pointer). * * The non-local jump to a given context takes place in a call to * * void findcontext(int mask, SEXP env, SEXP val) * * This causes "val" to be stuffed into a globally accessible place and * then a search to take place back through the context list for an * appropriate context. The kind of context sort is determined by the * value of "mask". The value of mask should be the logical OR of all * the context types desired. * * The value of "mask" is returned as the value of the setjump call at * the level longjumped to. This is used to distinguish between break * and next actions. * * Contexts can be used as a wrapper around functions that create windows * or open files. These can then be shut/closed gracefully if an error * occurs. */ #ifdef HAVE_CONFIG_H #include #endif #define R_USE_SIGNALS 1 #include #include /* R_run_onexits - runs the conexit/cend code for all contexts from R_GlobalContext down to but not including the argument context. This routine does not stop at a CTXT_TOPLEVEL--the code that determines the argument is responsible for making sure CTXT_TOPLEVEL's are not crossed unless appropriate. */ attribute_hidden void R_run_onexits(RCNTXT *cptr) { RCNTXT *c; for (c = R_GlobalContext; c != cptr; c = c->nextcontext) { // a user embedding R incorrectly triggered this (PR#15420) if (c == NULL) error("bad target context--should NEVER happen if R was called correctly"); if (c->cend != NULL) { void (*cend)(void *) = c->cend; c->cend = NULL; /* prevent recursion */ R_HandlerStack = c->handlerstack; R_RestartStack = c->restartstack; cend(c->cenddata); } if (c->cloenv != R_NilValue && c->conexit != R_NilValue) { SEXP s = c->conexit; RCNTXT* savecontext = R_ExitContext; R_ExitContext = c; c->conexit = R_NilValue; /* prevent recursion */ /* we are in intermediate jump, so returnValue is undefined */ c->returnValue = SEXP_TO_STACKVAL(NULL); R_HandlerStack = c->handlerstack; R_RestartStack = c->restartstack; PROTECT(s); /* Since these are run before any jumps rather than after jumping to the context where the exit handler was set we need to make sure there is enough room on the evaluation stack in case the jump is from handling a stack overflow. To be safe it is good to also call R_CheckStack. LT */ R_Expressions = R_Expressions_keep + 500; R_CheckStack(); for (; s != R_NilValue; s = CDR(s)) { c->conexit = CDR(s); eval(CAR(s), c->cloenv); } UNPROTECT(1); R_ExitContext = savecontext; } if (R_ExitContext == c) R_ExitContext = NULL; /* Not necessary? Better safe than sorry. */ } } /* R_restore_globals - restore global variables from a target context before a LONGJMP. The target context itself is not restored here since this is done in R_jumpctxt below. */ static void R_restore_globals(RCNTXT *cptr) { R_PPStackTop = cptr->cstacktop; R_GCEnabled = cptr->gcenabled; R_BCIntActive = cptr->bcintactive; R_BCpc = cptr->bcpc; R_BCbody = cptr->bcbody; R_BCFrame = cptr->bcframe; R_EvalDepth = cptr->evaldepth; vmaxset(cptr->vmax); R_interrupts_suspended = cptr->intsusp; R_HandlerStack = cptr->handlerstack; R_RestartStack = cptr->restartstack; while (R_PendingPromises != cptr->prstack) { /* The value 2 installed in PRSEEN 2 allows forcePromise in eval.c to signal a warning when asked to evaluate a promise whose evaluation has been interrupted by a jump. */ SET_PRSEEN(R_PendingPromises->promise, 2); R_PendingPromises = R_PendingPromises->next; } /* Need to reset R_Expressions in case we are jumping after handling a stack overflow. */ R_Expressions = R_Expressions_keep; R_BCNodeStackTop = cptr->nodestack; R_Srcref = cptr->srcref; R_BCProtReset(cptr->bcprottop); } static RCNTXT *first_jump_target(RCNTXT *cptr, int mask) { RCNTXT *c; for (c = R_GlobalContext; c && c != cptr; c = c->nextcontext) { if ((c->cloenv != R_NilValue && c->conexit != R_NilValue) || c->callflag == CTXT_UNWIND) { c->jumptarget = cptr; c->jumpmask = mask; return c; } } return cptr; } /* R_jumpctxt - jump to the named context */ attribute_hidden void NORET R_jumpctxt(RCNTXT * targetcptr, int mask, SEXP val) { Rboolean savevis = R_Visible; RCNTXT *cptr; /* find the target for the first jump -- either an intermediate context with an on.exit action to run or the final target if there are no intermediate on.exit actions */ cptr = first_jump_target(targetcptr, mask); /* run cend code for all contexts down to but not including the first jump target */ R_run_onexits(cptr); R_Visible = savevis; R_ReturnedValue = val; R_GlobalContext = cptr; R_restore_globals(R_GlobalContext); /* if we are in the process of handling a C stack overflow we need to restore the C stack limit before the jump */ if (R_OldCStackLimit != 0) { R_CStackLimit = R_OldCStackLimit; R_OldCStackLimit = 0; } LONGJMP(cptr->cjmpbuf, mask); } /* begincontext - begin an execution context */ /* begincontext and endcontext are used in dataentry.c and modules */ void begincontext(RCNTXT * cptr, int flags, SEXP syscall, SEXP env, SEXP sysp, SEXP promargs, SEXP callfun) { cptr->cstacktop = R_PPStackTop; cptr->gcenabled = R_GCEnabled; cptr->relpc = R_BCRelPC(R_BCbody, R_BCpc); cptr->bcpc = R_BCpc; cptr->bcbody = R_BCbody; cptr->bcframe = R_BCFrame; cptr->bcintactive = R_BCIntActive; cptr->evaldepth = R_EvalDepth; cptr->callflag = flags; cptr->call = syscall; cptr->cloenv = env; cptr->sysparent = sysp; cptr->conexit = R_NilValue; cptr->cend = NULL; cptr->promargs = promargs; cptr->callfun = callfun; cptr->vmax = vmaxget(); cptr->intsusp = R_interrupts_suspended; cptr->handlerstack = R_HandlerStack; cptr->restartstack = R_RestartStack; cptr->prstack = R_PendingPromises; cptr->nodestack = R_BCNodeStackTop; cptr->bcprottop = R_BCProtTop; cptr->srcref = R_Srcref; cptr->browserfinish = R_GlobalContext->browserfinish; cptr->nextcontext = R_GlobalContext; cptr->returnValue = SEXP_TO_STACKVAL(NULL); cptr->jumptarget = NULL; cptr->jumpmask = 0; R_GlobalContext = cptr; } /* endcontext - end an execution context */ void endcontext(RCNTXT * cptr) { void R_FixupExitingHandlerResult(SEXP); /* defined in error.c */ SEXP R_UnwindHandlerStack(SEXP); /* defined in error.c */ R_HandlerStack = R_UnwindHandlerStack(cptr->handlerstack); R_RestartStack = cptr->restartstack; RCNTXT *jumptarget = cptr->jumptarget; if (cptr->cloenv != R_NilValue && cptr->conexit != R_NilValue ) { SEXP s = cptr->conexit; Rboolean savevis = R_Visible; RCNTXT* savecontext = R_ExitContext; SEXP saveretval = R_ReturnedValue; R_ExitContext = cptr; cptr->conexit = R_NilValue; /* prevent recursion */ cptr->jumptarget = NULL; /* in case on.exit expr calls return() */ PROTECT(saveretval); PROTECT(s); R_FixupExitingHandlerResult(saveretval); SEXP cptr_retval = cptr->returnValue.tag == 0 ? cptr->returnValue.u.sxpval : NULL; if (cptr_retval) // why is this needed??? INCREMENT_LINKS(cptr_retval); for (; s != R_NilValue; s = CDR(s)) { cptr->conexit = CDR(s); eval(CAR(s), cptr->cloenv); } if (cptr_retval) // why is this needed??? DECREMENT_LINKS(cptr_retval); R_ReturnedValue = saveretval; UNPROTECT(2); R_ExitContext = savecontext; R_Visible = savevis; } if (R_ExitContext == cptr) R_ExitContext = NULL; /* continue jumping if this was reached as an intermediate jump */ if (jumptarget) /* cptr->returnValue is undefined */ R_jumpctxt(jumptarget, cptr->jumpmask, R_ReturnedValue); R_GlobalContext = cptr->nextcontext; } /* findcontext - find the correct context */ attribute_hidden void NORET findcontext(int mask, SEXP env, SEXP val) { RCNTXT *cptr; cptr = R_GlobalContext; if (mask & CTXT_LOOP) { /* break/next */ for (cptr = R_GlobalContext; cptr != NULL && cptr->callflag != CTXT_TOPLEVEL; cptr = cptr->nextcontext) if (cptr->callflag & CTXT_LOOP && cptr->cloenv == env ) R_jumpctxt(cptr, mask, val); error(_("no loop for break/next, jumping to top level")); } else { /* return; or browser */ for (cptr = R_GlobalContext; cptr != NULL && cptr->callflag != CTXT_TOPLEVEL; cptr = cptr->nextcontext) if ((cptr->callflag & mask) && cptr->cloenv == env) R_jumpctxt(cptr, mask, val); error(_("no function to return from, jumping to top level")); } } attribute_hidden void NORET R_JumpToContext(RCNTXT *target, int mask, SEXP val) { RCNTXT *cptr; for (cptr = R_GlobalContext; cptr != NULL && cptr->callflag != CTXT_TOPLEVEL; cptr = cptr->nextcontext) { if (cptr == target) R_jumpctxt(cptr, mask, val); if (cptr == R_ExitContext) R_ExitContext = NULL; } error(_("target context is not on the stack")); } /* R_sysframe - look back up the context stack until the */ /* nth closure context and return that cloenv. */ /* R_sysframe(0) means the R_GlobalEnv environment */ /* negative n counts back from the current frame */ /* positive n counts up from the globalEnv */ attribute_hidden SEXP R_sysframe(int n, RCNTXT *cptr) { if (n == 0) return(R_GlobalEnv); if (n == NA_INTEGER) error(_("NA argument is invalid")); if (n > 0) n = framedepth(cptr) - n; else n = -n; if(n < 0) error(_("not that many frames on the stack")); while (cptr->nextcontext != NULL) { if (cptr->callflag & CTXT_FUNCTION ) { if (n == 0) { /* we need to detach the enclosing env */ return cptr->cloenv; } else n--; } cptr = cptr->nextcontext; } if(n == 0 && cptr->nextcontext == NULL) return R_GlobalEnv; else error(_("not that many frames on the stack")); return R_NilValue; /* just for -Wall */ } /* We need to find the environment that can be returned by sys.frame */ /* (so it needs to be on the cloenv pointer of a context) that matches */ /* the environment where the closure arguments are to be evaluated. */ /* It would be much simpler if sysparent just returned cptr->sysparent */ /* but then we wouldn't be compatible with S. */ int attribute_hidden R_sysparent(int n, RCNTXT *cptr) { int j; SEXP s; if(n <= 0) errorcall(R_ToplevelContext->call, _("only positive values of 'n' are allowed")); while (cptr->nextcontext != NULL && n > 1) { if (cptr->callflag & CTXT_FUNCTION ) n--; cptr = cptr->nextcontext; } /* make sure we're looking at a return context */ while (cptr->nextcontext != NULL && !(cptr->callflag & CTXT_FUNCTION) ) cptr = cptr->nextcontext; s = cptr->sysparent; if(s == R_GlobalEnv) return 0; j = 0; while (cptr != NULL ) { if (cptr->callflag & CTXT_FUNCTION) { j++; if( cptr->cloenv == s ) n=j; } cptr = cptr->nextcontext; } n = j - n + 1; if (n < 0) n = 0; return n; } int attribute_hidden framedepth(RCNTXT *cptr) { int nframe = 0; while (cptr->nextcontext != NULL) { if (cptr->callflag & CTXT_FUNCTION ) nframe++; cptr = cptr->nextcontext; } return nframe; } static SEXP getCallWithSrcref(RCNTXT *cptr) { SEXP result; PROTECT(result = shallow_duplicate(cptr->call)); if (cptr->srcref && !isNull(cptr->srcref)) { SEXP sref; if (cptr->srcref == R_InBCInterpreter) /* FIXME: this is expensive, it might be worth changing sys.call */ /* to return srcrefs only on request (add `with.source` option) */ sref = R_findBCInterpreterSrcref(cptr); else sref = cptr->srcref; setAttrib(result, R_SrcrefSymbol, duplicate(sref)); } UNPROTECT(1); return result; } attribute_hidden SEXP R_syscall(int n, RCNTXT *cptr) { /* negative n counts back from the current frame */ /* positive n counts up from the globalEnv */ if (n > 0) n = framedepth(cptr) - n; else n = - n; if(n < 0) error(_("not that many frames on the stack")); while (cptr->nextcontext != NULL) { if (cptr->callflag & CTXT_FUNCTION ) { if (n == 0) return getCallWithSrcref(cptr); else n--; } cptr = cptr->nextcontext; } if (n == 0 && cptr->nextcontext == NULL) return getCallWithSrcref(cptr); error(_("not that many frames on the stack")); return R_NilValue; /* just for -Wall */ } attribute_hidden SEXP R_sysfunction(int n, RCNTXT *cptr) { if (n > 0) n = framedepth(cptr) - n; else n = - n; if (n < 0) error(_("not that many frames on the stack")); while (cptr->nextcontext != NULL) { if (cptr->callflag & CTXT_FUNCTION ) { if (n == 0) return duplicate(cptr->callfun); /***** do we need to DUP? */ else n--; } cptr = cptr->nextcontext; } if (n == 0 && cptr->nextcontext == NULL) return duplicate(cptr->callfun); /***** do we need to DUP? */ error(_("not that many frames on the stack")); return R_NilValue; /* just for -Wall */ } /* count how many contexts of the specified type are present on the stack */ /* browser contexts are a bit special because they are transient and for */ /* any closure context with the debug bit set one will be created; so we */ /* need to count those as well */ int countContexts(int ctxttype, int browser) { int n=0; RCNTXT *cptr; cptr = R_GlobalContext; while( cptr != R_ToplevelContext) { if( cptr->callflag == ctxttype ) n++; else if( browser ) { if(cptr->callflag & CTXT_FUNCTION && RDEBUG(cptr->cloenv) ) n++; } cptr = cptr->nextcontext; } return n; } /* functions to support looking up information about the browser */ /* contexts that are in the evaluation stack */ attribute_hidden SEXP do_sysbrowser(SEXP call, SEXP op, SEXP args, SEXP rho) { SEXP rval=R_NilValue; RCNTXT *cptr; RCNTXT *prevcptr = NULL; int n; checkArity(op, args); n = asInteger(CAR(args)); if(n < 1 ) error(_("number of contexts must be positive")); /* first find the closest browser context */ cptr = R_GlobalContext; while (cptr != R_ToplevelContext) { if (cptr->callflag == CTXT_BROWSER) { break; } cptr = cptr->nextcontext; } /* error if not a browser context */ if( !(cptr->callflag == CTXT_BROWSER) ) error(_("no browser context to query")); switch (PRIMVAL(op)) { case 1: /* text */ case 2: /* condition */ /* first rewind to the right place if needed */ /* note we want n>1, as we have already */ /* rewound to the first context */ if( n > 1 ) { while (cptr != R_ToplevelContext && n > 0 ) { if (cptr->callflag == CTXT_BROWSER) { n--; break; } cptr = cptr->nextcontext; } } if( !(cptr->callflag == CTXT_BROWSER) ) error(_("not that many calls to browser are active")); if( PRIMVAL(op) == 1 ) rval = CAR(cptr->promargs); else rval = CADR(cptr->promargs); break; case 3: /* turn on debugging n levels up */ while ( (cptr != R_ToplevelContext) && n > 0 ) { if (cptr->callflag & CTXT_FUNCTION) n--; prevcptr = cptr; cptr = cptr->nextcontext; } if( !(cptr->callflag & CTXT_FUNCTION) ) error(_("not that many functions on the call stack")); if( prevcptr && prevcptr->srcref == R_InBCInterpreter ) { if ( TYPEOF(cptr->callfun) == CLOSXP && TYPEOF(BODY(cptr->callfun)) == BCODESXP ) warning(_("debug flag in compiled function has no effect")); else warning(_("debug will apply when function leaves " "compiled code")); } SET_RDEBUG(cptr->cloenv, 1); break; } return(rval); } /* An implementation of S's frame access functions. They usually count */ /* up from the globalEnv while we like to count down from the currentEnv. */ /* So if the argument is negative count down if positive count up. */ /* We don't want to count the closure that do_sys is contained in, so the */ /* indexing is adjusted to handle this. */ /* Return first `CTXT_FUNCTION` context whose execution env matches `rho` */ attribute_hidden RCNTXT *getLexicalContext(SEXP rho) { RCNTXT *cptr = R_GlobalContext; while (cptr && cptr != R_ToplevelContext) { if (cptr->callflag & CTXT_FUNCTION && cptr->cloenv == rho) break; cptr = cptr->nextcontext; } return cptr; } /* Return call of the first `CTXT_FUNCTION` context whose execution env matches `rho` */ attribute_hidden SEXP getLexicalCall(SEXP rho) { RCNTXT *cptr = getLexicalContext(rho); if (cptr) return cptr->call; else return R_NilValue; } attribute_hidden SEXP do_sys(SEXP call, SEXP op, SEXP args, SEXP rho) { int i, n = -1, nframe; SEXP rval; checkArity(op, args); /* first find the context that sys.xxx needs to be evaluated in */ SEXP t = R_GlobalContext->sysparent; RCNTXT *cptr = getLexicalContext(t); if (length(args) == 1) n = asInteger(CAR(args)); switch (PRIMVAL(op)) { case 1: /* parent */ if(n == NA_INTEGER) error(_("invalid '%s' argument"), "n"); i = nframe = framedepth(cptr); /* This is a pretty awful kludge, but the alternative would be a major redesign of everything... -pd */ while (n-- > 0) i = R_sysparent(nframe - i + 1, cptr); return ScalarInteger(i); case 2: /* call */ if(n == NA_INTEGER) error(_("invalid '%s' argument"), "which"); return R_syscall(n, cptr); case 3: /* frame */ if(n == NA_INTEGER) error(_("invalid '%s' argument"), "which"); return R_sysframe(n, cptr); case 4: /* sys.nframe */ return ScalarInteger(framedepth(cptr)); case 5: /* sys.calls */ nframe = framedepth(cptr); PROTECT(rval = allocList(nframe)); t=rval; for(i = 1; i <= nframe; i++, t = CDR(t)) SETCAR(t, R_syscall(i, cptr)); UNPROTECT(1); return rval; case 6: /* sys.frames */ nframe = framedepth(cptr); PROTECT(rval = allocList(nframe)); t = rval; for(i = 1; i <= nframe; i++, t = CDR(t)) SETCAR(t, R_sysframe(i, cptr)); UNPROTECT(1); return rval; case 7: /* sys.on.exit */ { SEXP conexit = cptr->conexit; if (conexit == R_NilValue) return R_NilValue; else if (CDR(conexit) == R_NilValue) return CAR(conexit); else return LCONS(R_BraceSymbol, conexit); } case 8: /* sys.parents */ nframe = framedepth(cptr); rval = allocVector(INTSXP, nframe); for(i = 0; i < nframe; i++) INTEGER(rval)[i] = R_sysparent(nframe - i, cptr); return rval; case 9: /* sys.function */ if(n == NA_INTEGER) error(_("invalid '%s' value"), "which"); return(R_sysfunction(n, cptr)); default: error(_("internal error in 'do_sys'")); return R_NilValue;/* just for -Wall */ } } attribute_hidden SEXP do_parentframe(SEXP call, SEXP op, SEXP args, SEXP rho) { checkArity(op, args); int n = asInteger(CAR(args)); if(n == NA_INTEGER || n < 1 ) error(_("invalid '%s' value"), "n"); RCNTXT *cptr = R_findParentContext(R_GlobalContext, n); if (cptr) return cptr->sysparent; else return R_GlobalEnv; } /* R_findExecContext - Find a context frame older than `cptr` that has `envir` as execution environment (the `cloenv` field). */ attribute_hidden RCNTXT *R_findExecContext(RCNTXT *cptr, SEXP envir) { while (cptr->nextcontext != NULL) { if ((cptr->callflag & CTXT_FUNCTION) != 0 && cptr->cloenv == envir) return cptr; cptr = cptr->nextcontext; } return NULL; } /* R_findParentContext - Find a context frame older than `cptr` whose execution environment (`cloenv` field) is the same as cptr's calling environment (`sysparent` field). In other words, find the frame where `cptr->syscall` was (seemingly) called. This algorithm powers `parent.frame()`. */ attribute_hidden RCNTXT *R_findParentContext(RCNTXT *cptr, int n) { while ((cptr = R_findExecContext(cptr, cptr->sysparent)) != NULL) { if (n == 1) return cptr; n--; } return NULL; } /* R_ToplevelExec - call fun(data) within a top level context to insure that this function cannot be left by a LONGJMP. R errors in the call to fun will result in a jump to top level. The return value is TRUE if fun returns normally, FALSE if it results in a jump to top level. */ Rboolean R_ToplevelExec(void (*fun)(void *), void *data) { RCNTXT thiscontext; RCNTXT * volatile saveToplevelContext; volatile SEXP topExp, oldHStack, oldRStack, oldRVal; volatile Rboolean oldvis; Rboolean result; PROTECT(topExp = R_CurrentExpr); PROTECT(oldHStack = R_HandlerStack); PROTECT(oldRStack = R_RestartStack); PROTECT(oldRVal = R_ReturnedValue); oldvis = R_Visible; R_HandlerStack = R_NilValue; R_RestartStack = R_NilValue; saveToplevelContext = R_ToplevelContext; begincontext(&thiscontext, CTXT_TOPLEVEL, R_NilValue, R_GlobalEnv, R_BaseEnv, R_NilValue, R_NilValue); if (SETJMP(thiscontext.cjmpbuf)) result = FALSE; else { R_GlobalContext = R_ToplevelContext = &thiscontext; fun(data); result = TRUE; } endcontext(&thiscontext); R_ToplevelContext = saveToplevelContext; R_CurrentExpr = topExp; R_HandlerStack = oldHStack; R_RestartStack = oldRStack; R_ReturnedValue = oldRVal; R_Visible = oldvis; UNPROTECT(4); return result; } /* Return the current environment. */ SEXP R_GetCurrentEnv(void) { return R_GlobalContext->sysparent; } /* This is a simple interface for evaluating R expressions from C with a guarantee that one will return to the point in the code from which the call was made (if it does return at all). This uses R_TopleveExec to do this. It is important in applications that embed R or wish to make general callbacks to R with error handling. It is currently hidden with a data structure definition and C routine visible only here. The R_tryEval() is the only visible aspect. This can be lifted into the header files if necessary. (DTL) R_tryEval is in Rinternals.h (so public), but not in the API. */ typedef struct { SEXP expression; SEXP val; SEXP env; } ProtectedEvalData; static void protectedEval(void *d) { ProtectedEvalData *data = (ProtectedEvalData *)d; SEXP env = R_GlobalEnv; if(data->env) { env = data->env; } data->val = eval(data->expression, env); R_PreserveObject(data->val); } SEXP R_tryEval(SEXP e, SEXP env, int *ErrorOccurred) { Rboolean ok; ProtectedEvalData data; data.expression = e; data.val = NULL; data.env = env; ok = R_ToplevelExec(protectedEval, &data); if (ErrorOccurred) { *ErrorOccurred = (ok == FALSE); } if (ok == FALSE) data.val = NULL; else R_ReleaseObject(data.val); return(data.val); } /* Temporary hack to suppress error message printing around a R_tryEval call for use in methods_list_dispatch.c; should be replaced once we have a way of establishing error handlers from C code (probably would want a calling handler if we want to allow user-defined calling handlers to enter a debugger, for example). LT */ SEXP R_tryEvalSilent(SEXP e, SEXP env, int *ErrorOccurred) { SEXP val; Rboolean oldshow = R_ShowErrorMessages; R_ShowErrorMessages = FALSE; val = R_tryEval(e, env, ErrorOccurred); R_ShowErrorMessages = oldshow; return val; } SEXP R_ExecWithCleanup(SEXP (*fun)(void *), void *data, void (*cleanfun)(void *), void *cleandata) { RCNTXT cntxt; SEXP result; begincontext(&cntxt, CTXT_CCODE, R_NilValue, R_BaseEnv, R_BaseEnv, R_NilValue, R_NilValue); cntxt.cend = cleanfun; cntxt.cenddata = cleandata; PROTECT(result = fun(data)); cleanfun(cleandata); endcontext(&cntxt); UNPROTECT(1); return result; } /* Unwind-protect mechanism to support C++ stack unwinding. */ typedef struct { int jumpmask; RCNTXT *jumptarget; } unwind_cont_t; SEXP R_MakeUnwindCont(void) { return CONS(R_NilValue, allocVector(RAWSXP, sizeof(unwind_cont_t))); } #define RAWDATA(x) ((void *) RAW0(x)) NORET void R_ContinueUnwind(SEXP cont) { SEXP retval = CAR(cont); unwind_cont_t *u = RAWDATA(CDR(cont)); R_jumpctxt(u->jumptarget, u->jumpmask, retval); } SEXP R_UnwindProtect(SEXP (*fun)(void *data), void *data, void (*cleanfun)(void *data, Rboolean jump), void *cleandata, SEXP cont) { RCNTXT thiscontext; volatile SEXP result; Rboolean jump; /* Allow simple usage with a NULL continuation token. This _could_ result in a failure in allocation or exceeding the PROTECT stack limit before calling fun(), so fun() and cleanfun should be written accordingly. */ if (cont == NULL) { PROTECT(cont = R_MakeUnwindCont()); result = R_UnwindProtect(fun, data, cleanfun, cleandata, cont); UNPROTECT(1); return result; } begincontext(&thiscontext, CTXT_UNWIND, R_NilValue, R_GlobalEnv, R_BaseEnv, R_NilValue, R_NilValue); if (SETJMP(thiscontext.cjmpbuf)) { jump = TRUE; SETCAR(cont, R_ReturnedValue); unwind_cont_t *u = RAWDATA(CDR(cont)); u->jumpmask = thiscontext.jumpmask; u->jumptarget = thiscontext.jumptarget; thiscontext.jumptarget = NULL; } else { result = fun(data); SETCAR(cont, result); jump = FALSE; } endcontext(&thiscontext); cleanfun(cleandata, jump); if (jump) R_ContinueUnwind(cont); return result; }