Rev 2 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
/** R : A Computer Langage for Statistical Data Analysis* Copyright (C) 1995, 1996 Robert Gentleman and Ross Ihaka** 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, write to the Free Software* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*//*------------------------------------------------------------------------------Contexts:A linked-list of execution contexts is kept so that control-flow constructslike "next", "break" and "return" will work. It is also used for errorreturns to top-level.context[k] -> context[k-1] -> ... -> context[0]^ ^R_GlobalContext R_ToplevelContextContexts are allocated on the stack as the evaluator invokes itselfrecursively. The memory is reclaimed naturally on return through therecursions (the R_GlobalContext pointer needs adjustment).A context contains the following information:nextcontext the next level contextcjmpbuf longjump information for non-local returncstacktop the current level of the pointer protection stackcallflag the context "type"call the call (name of function) that effected thiscontextcloenv for closures, the environment of the closure.sysparent the environment the closure was called fromconexit code for on.exit calls, to be executed in cloenvat exit from the closure (normal or abnormal).cend a pointer to function which executes if there isnon-local return (i.e. an error)Context types can be one of:CTXT_TOPLEVEL The toplevel contextCTXT_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 browserCTXT_CCODE other functions that need clean up if an error occursA context is created with a call tovoid begincontext(RCNTXT *cptr, int flags, SEXP syscall, SEXP env, SEXPsysp, SEXP promargs)which sets up the context pointed to by cptr in the appropriate way.When the context goes "out-of-scope" a call tovoid 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 tovoid findcontext(int mask, SEXP val)This causes "val" to be stuffed into a globally accessable place andthen a search to take place back through the context list for anappropriate context. The kind of context sort is determined by thevalue of "mask". The value of mask should be the logical OR of allthe context types desired.The value of "mask" is returned as the value of the setjump call at thelevel longjumped to. This is used to distinguish between break andnext actions.Contexts can be used as a wrapper around functions that create windows oropen files. These can then be shut/closed gracefully if an error occurs.------------------------------------------------------------------------------*/#include "Defn.h"static void jumpfun(RCNTXT *, int, SEXP);/* begincontext - begin an execution context */void begincontext(RCNTXT * cptr, int flags, SEXP syscall, SEXP env, SEXP sysp,SEXP promargs){cptr->nextcontext = R_GlobalContext;cptr->cstacktop = R_PPStackTop;cptr->callflag = flags;cptr->call = syscall;cptr->cloenv = env;cptr->sysparent = sysp;cptr->conexit = R_NilValue;cptr->cend = NULL;cptr->promargs = promargs;R_GlobalContext = cptr;}/* endcontext - end an execution context */void endcontext(RCNTXT * cptr){int savevis = R_Visible;if (cptr->cloenv != R_NilValue && cptr->conexit != R_NilValue )eval(cptr->conexit, cptr->cloenv);R_Visible = savevis;R_GlobalContext = cptr->nextcontext;}/* findcontext - find the correct context */void findcontext(int mask, SEXP val){RCNTXT *cptr;cptr = R_GlobalContext;if (mask & CTXT_LOOP) { /* break/next */if (cptr->callflag & CTXT_LOOP)jumpfun(cptr, mask, val);elseerror("No loop to break from, jumping to top level\n");}else { /* return; or browser */for (cptr = R_GlobalContext; cptr; cptr = cptr->nextcontext)if (cptr->callflag == mask)jumpfun(cptr, mask, val);error("No function to return from, jumping to top level\n");}}/* jumpfun - jump to the named context */static void jumpfun(RCNTXT * cptr, int mask, SEXP val){R_PPStackTop = cptr->cstacktop;R_ReturnedValue = val;if (cptr != R_ToplevelContext)R_GlobalContext = cptr->nextcontext;elseR_GlobalContext = R_ToplevelContext;longjmp(cptr->cjmpbuf, mask);}/** sysframe - look back up the context stack until the nth closure* context and return that cloenv.* sysframe(0) means the R_GlobalEnv environment* negative n counts back from the current frame* positive n counts up from the globalEnv*/SEXP sysframe(int n, RCNTXT *cptr){if (n == 0)return(R_GlobalEnv);if (n > 0)n = framedepth(cptr) - n;elsen = -n;if(n < 0)errorcall(R_GlobalContext->call,"not that many enclosing environments\n");while (cptr->nextcontext != NULL) {if (cptr->callflag == CTXT_RETURN)if (n == 0) { /* we need to detach the enclosing env */return cptr->cloenv;}elsen--;cptr = cptr->nextcontext;}if(n == 0 && cptr->nextcontext == NULL)return R_GlobalEnv;elseerror("sys.frame: not that many enclosing functions\n");}/*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 matchesthe environment where the closure arguments are to be evaluated.It would be much simpler if sysparent just returned cptr->sysparentbut then we wouldn't be compatible with S.*/int sysparent(int n, RCNTXT *cptr){int j;SEXP s;if(n<=0)errorcall( R_ToplevelContext->call,"only positive arguments are allowed\n");while (cptr->nextcontext != NULL && n > 1) {if (cptr->callflag == CTXT_RETURN)n--;cptr = cptr->nextcontext;}/* make sure we're looking at a return context */while (cptr->nextcontext != NULL && cptr->callflag != CTXT_RETURN )cptr = cptr->nextcontext;s = cptr->sysparent;if(s == R_GlobalEnv)return 0;j=0;while (cptr != NULL ) {if (cptr->callflag == CTXT_RETURN) {j++;if( cptr->cloenv == s )n=j;}cptr = cptr->nextcontext;}n=j-n+1;if( n == 0 )n=1;if( n < 0 )error("sys.parent: not that many enclosing functions\n");return n;}int framedepth(RCNTXT *cptr){int nframe=0;while (cptr->nextcontext != NULL) {if (cptr->callflag == CTXT_RETURN)nframe++;cptr = cptr->nextcontext;}return nframe;}SEXP syscall(int n, RCNTXT *cptr){/* negative n counts back from the current framepositive n counts up from the globalEnv*/if (n > 0)n=framedepth(cptr)-n;elsen=-n;if(n < 0 )errorcall(R_GlobalContext->call,"illegal frame number\n");while (cptr->nextcontext != NULL) {if (cptr->callflag == CTXT_RETURN)if (n == 0)return (duplicate(cptr->call));elsen--;cptr = cptr->nextcontext;}if( n == 0 && cptr->nextcontext == NULL )return (duplicate(cptr->call));errorcall(R_GlobalContext->call,"not that many enclosing functions\n");}SEXP sysfunction(int n, RCNTXT *cptr){SEXP s,t;if (n > 0)n=framedepth(cptr)-n;elsen=-n;if (n < 0 )errorcall(R_GlobalContext->call,"illegal frame number\n");while (cptr->nextcontext != NULL) {if (cptr->callflag == CTXT_RETURN)if (n == 0) {s=CAR(cptr->call);if( isSymbol(s) )t=findVar(s,cptr->sysparent);else if( isLanguage(s) )t=eval(s,cptr->sysparent);elset=R_NilValue;return(t);}elsen--;cptr = cptr->nextcontext;}if( n == 0 && cptr->nextcontext == NULL )return( findVar(CAR(cptr->call),cptr->sysparent) );errorcall(R_GlobalContext->call,"not that many enclosing functions\n");}/*An implementation of S's frame access functions. They usuallycount up from the globalEnv while we like to count down fromthe currentEnv. So if the argument is negative count down ifpositive count up.We don't want to count the closure that do_sys is contained inso the indexing is adjusted to handle this.*/SEXP do_sys(SEXP call, SEXP op, SEXP args, SEXP rho){int i, n, nframe;SEXP rval,t;RCNTXT *cptr;/* first find the context that sys.xxx needs to be evaluated in */cptr=R_GlobalContext;t=cptr->sysparent;while(cptr != R_ToplevelContext) {if(cptr->callflag == CTXT_RETURN)if(cptr->cloenv==t)break;cptr = cptr->nextcontext;}if( length(args)==1 ) {t=eval(CAR(args),rho);n = asInteger(t);}elsen = -1;if(n == NA_INTEGER)errorcall(call, "invalid number of environment levels\n");switch (PRIMVAL(op)) {case 1: /* parent */rval=allocVector(INTSXP,1);INTEGER(rval)[0]=sysparent(n, cptr);return rval;case 2: /* call */return syscall(n, cptr);case 3: /* frame */return sysframe(n, cptr);case 4: /* sys.nframe */rval=allocVector(INTSXP,1);INTEGER(rval)[0]=framedepth(cptr);return rval;case 5: /* sys.calls */nframe=framedepth(cptr);PROTECT(rval=allocList(nframe));t=rval;for(i=1 ; i<=nframe; i++, t=CDR(t))CAR(t)=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))CAR(t)=sysframe(i,cptr);UNPROTECT(1);return rval;case 7: /* sys.on.exit */if( R_GlobalContext->conexit )return R_GlobalContext->conexit;elsereturn R_NilValue;case 8: /* sys.parents */nframe=framedepth(cptr);rval=allocVector(INTSXP,nframe);for(i=0; i<nframe ; i++ )INTEGER(rval)[i]= sysparent(nframe-i,cptr);return rval;case 9: /* sys.function */return(sysfunction(n, cptr));default:error("internal error in do_sys\n");}}