From 96a4475c4aa4e7f173d328e2a6f37770ae35f497 Mon Sep 17 00:00:00 2001 From: Miguel Sofer Date: Wed, 15 Dec 2004 20:44:17 +0000 Subject: * generic/tclTest.c: Added two new functions to allocate memory from the execution stack (TclStackAlloc, TclStackFree). Added functions TclPushStackFrame and TclPopStackFrame that do the work of Tcl_PushCallFrame and Tcl_PopCallFrame, but using frames allocated in the execution stack - i.e., heap instead of C-stack. The core uses these two new functions exclusively; the old ones remain for backwards compat, as at least two popular extensions (itcl, xotcl) are known to use them. --- ChangeLog | 18 +++++++++++ generic/tclBasic.c | 8 ++--- generic/tclExecute.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++- generic/tclInt.decls | 16 +++++++++- generic/tclIntDecls.h | 46 +++++++++++++++++++++++++++- generic/tclNamesp.c | 79 ++++++++++++++++++++++++++++++++++++++++-------- generic/tclProc.c | 16 +++++----- generic/tclStubInit.c | 6 +++- generic/tclTest.c | 8 ++--- 9 files changed, 247 insertions(+), 33 deletions(-) diff --git a/ChangeLog b/ChangeLog index 8f832b9..154fdfa 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2004-12-15 Miguel Sofer + + * generic/tclBasic.c: + * generic/tclExecute.c: + * generic/tclInt.decls: + * generic/tclIntDecls.h: + * generic/tclNamesp.c: + * generic/tclProc.c: + * generic/tclStubInit.c: + * generic/tclTest.c: Added two new functions to allocate memory + from the execution stack (TclStackAlloc, TclStackFree). Added + functions TclPushStackFrame and TclPopStackFrame that do the work + of Tcl_PushCallFrame and Tcl_PopCallFrame, but using frames + allocated in the execution stack - i.e., heap instead of + C-stack. The core uses these two new functions exclusively; the + old ones remain for backwards compat, as at least two popular + extensions (itcl, xotcl) are known to use them. + 2004-12-14 Miguel Sofer * generic/tclCmdIL.c: diff --git a/generic/tclBasic.c b/generic/tclBasic.c index d4c9382..45a3c4b 100644 --- a/generic/tclBasic.c +++ b/generic/tclBasic.c @@ -13,7 +13,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclBasic.c,v 1.136 2004/11/30 19:34:46 dgp Exp $ + * RCS: @(#) $Id: tclBasic.c,v 1.137 2004/12/15 20:44:34 msofer Exp $ */ #include "tclInt.h" @@ -4145,22 +4145,22 @@ TclObjInvokeNamespace(interp, objc, objv, nsPtr, flags) * TCL_INVOKE_NO_UNKNOWN, or * TCL_INVOKE_NO_TRACEBACK. */ { - Tcl_CallFrame frame; int result; + Tcl_CallFrame *framePtr; /* * Make the specified namespace the current namespace and invoke * the command. */ - result = Tcl_PushCallFrame(interp, &frame, nsPtr, /*isProcCallFrame*/ 0); + result = TclPushStackFrame(interp, &framePtr, nsPtr, /*isProcCallFrame*/ 0); if (result != TCL_OK) { return TCL_ERROR; } result = TclObjInvoke(interp, objc, objv, flags); - Tcl_PopCallFrame(interp); + TclPopStackFrame(interp); return result; } diff --git a/generic/tclExecute.c b/generic/tclExecute.c index 99cf90a..afbb4aa 100644 --- a/generic/tclExecute.c +++ b/generic/tclExecute.c @@ -11,7 +11,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclExecute.c,v 1.167 2004/11/12 19:16:50 dgp Exp $ + * RCS: @(#) $Id: tclExecute.c,v 1.168 2004/12/15 20:44:36 msofer Exp $ */ #include "tclInt.h" @@ -659,6 +659,87 @@ GrowEvaluationStack(eePtr) /* *-------------------------------------------------------------- * + * TclStackAlloc -- + * + * Allocate memory from the execution stack; it has to be returned later + * with a call to TclStackFree + * + * Results: + * A pointer to the first byte allocated, or panics if the allocation did + * not succeed. + * + * Side effects: + * The execution stack may be grown. + * + *-------------------------------------------------------------- + */ + +char * +TclStackAlloc(interp, numBytes) + Tcl_Interp *interp; + int numBytes; +{ + Interp *iPtr = (Interp *) interp; + ExecEnv *eePtr = iPtr->execEnvPtr; + int numWords; + Tcl_Obj **tosPtr = eePtr->tosPtr; + char **stackRefCountPtr; + + /* + * Add two words to store + * - a pointer to the used execution stack + * - the number of words reserved + * These will be used later by TclStackFree. + */ + + numWords = (numBytes + 3*sizeof(void *) - 1)/sizeof(void *); + + while ((tosPtr + numWords) > eePtr->endPtr) { + GrowEvaluationStack(eePtr); + tosPtr = eePtr->tosPtr; + } + + /* + * Increase the stack's reference count, to make sure it is not freed + * prematurely. + */ + + stackRefCountPtr = (char **) (eePtr->stackPtr-1); + ++*stackRefCountPtr; + + /* + * Reserve the space in the exec stack, and store the data for freeing. + */ + + eePtr->tosPtr += numWords; + *(eePtr->tosPtr-1) = (Tcl_Obj *) stackRefCountPtr; + *(eePtr->tosPtr) = (Tcl_Obj *) numWords; + + return (char *) (tosPtr+1); +} + +void +TclStackFree(interp) + Tcl_Interp *interp; +{ + Interp *iPtr = (Interp *) interp; + ExecEnv *eePtr = iPtr->execEnvPtr; + char **stackRefCountPtr; + + + stackRefCountPtr = (char **) *(eePtr->tosPtr-1); + eePtr->tosPtr -= (int) *(eePtr->tosPtr); + + --*stackRefCountPtr; + if (*stackRefCountPtr == (char *) 0) { + ckfree((VOID *) stackRefCountPtr); + } +} + + +/* + *-------------------------------------------------------------- + * * Tcl_ExprObj -- * * Evaluate an expression in a Tcl_Obj. diff --git a/generic/tclInt.decls b/generic/tclInt.decls index e497298..f59d01e 100644 --- a/generic/tclInt.decls +++ b/generic/tclInt.decls @@ -12,7 +12,7 @@ # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # -# RCS: @(#) $Id: tclInt.decls,v 1.84 2004/12/01 23:18:50 dgp Exp $ +# RCS: @(#) $Id: tclInt.decls,v 1.85 2004/12/15 20:44:38 msofer Exp $ library tcl @@ -859,6 +859,20 @@ declare 213 generic { declare 214 generic { void TclSetObjNameOfExecutable(Tcl_Obj *name, Tcl_Encoding encoding) } +declare 215 generic { + char * TclStackAlloc(Tcl_Interp *interp, int numBytes) +} +declare 216 generic { + void TclStackFree(Tcl_Interp *interp) +} +declare 217 generic { + int TclPushStackFrame(Tcl_Interp *interp, Tcl_CallFrame **framePtrPtr, + Tcl_Namespace *namespacePtr, int isProcCallFrame ) +} +declare 218 generic { + void TclPopStackFrame(Tcl_Interp *interp) +} + ############################################################################## diff --git a/generic/tclIntDecls.h b/generic/tclIntDecls.h index ed4b443..23acb90 100644 --- a/generic/tclIntDecls.h +++ b/generic/tclIntDecls.h @@ -11,7 +11,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclIntDecls.h,v 1.75 2004/12/01 23:18:52 dgp Exp $ + * RCS: @(#) $Id: tclIntDecls.h,v 1.76 2004/12/15 20:44:39 msofer Exp $ */ #ifndef _TCLINTDECLS @@ -1118,6 +1118,30 @@ EXTERN Tcl_Obj * TclGetObjNameOfExecutable _ANSI_ARGS_((void)); EXTERN void TclSetObjNameOfExecutable _ANSI_ARGS_(( Tcl_Obj * name, Tcl_Encoding encoding)); #endif +#ifndef TclStackAlloc_TCL_DECLARED +#define TclStackAlloc_TCL_DECLARED +/* 215 */ +EXTERN char * TclStackAlloc _ANSI_ARGS_((Tcl_Interp * interp, + int numBytes)); +#endif +#ifndef TclStackFree_TCL_DECLARED +#define TclStackFree_TCL_DECLARED +/* 216 */ +EXTERN void TclStackFree _ANSI_ARGS_((Tcl_Interp * interp)); +#endif +#ifndef TclPushStackFrame_TCL_DECLARED +#define TclPushStackFrame_TCL_DECLARED +/* 217 */ +EXTERN int TclPushStackFrame _ANSI_ARGS_((Tcl_Interp * interp, + Tcl_CallFrame ** framePtrPtr, + Tcl_Namespace * namespacePtr, + int isProcCallFrame)); +#endif +#ifndef TclPopStackFrame_TCL_DECLARED +#define TclPopStackFrame_TCL_DECLARED +/* 218 */ +EXTERN void TclPopStackFrame _ANSI_ARGS_((Tcl_Interp * interp)); +#endif typedef struct TclIntStubs { int magic; @@ -1353,6 +1377,10 @@ typedef struct TclIntStubs { void (*tclpFindExecutable) _ANSI_ARGS_((CONST char * argv0)); /* 212 */ Tcl_Obj * (*tclGetObjNameOfExecutable) _ANSI_ARGS_((void)); /* 213 */ void (*tclSetObjNameOfExecutable) _ANSI_ARGS_((Tcl_Obj * name, Tcl_Encoding encoding)); /* 214 */ + char * (*tclStackAlloc) _ANSI_ARGS_((Tcl_Interp * interp, int numBytes)); /* 215 */ + void (*tclStackFree) _ANSI_ARGS_((Tcl_Interp * interp)); /* 216 */ + int (*tclPushStackFrame) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_CallFrame ** framePtrPtr, Tcl_Namespace * namespacePtr, int isProcCallFrame)); /* 217 */ + void (*tclPopStackFrame) _ANSI_ARGS_((Tcl_Interp * interp)); /* 218 */ } TclIntStubs; #ifdef __cplusplus @@ -2100,6 +2128,22 @@ extern TclIntStubs *tclIntStubsPtr; #define TclSetObjNameOfExecutable \ (tclIntStubsPtr->tclSetObjNameOfExecutable) /* 214 */ #endif +#ifndef TclStackAlloc +#define TclStackAlloc \ + (tclIntStubsPtr->tclStackAlloc) /* 215 */ +#endif +#ifndef TclStackFree +#define TclStackFree \ + (tclIntStubsPtr->tclStackFree) /* 216 */ +#endif +#ifndef TclPushStackFrame +#define TclPushStackFrame \ + (tclIntStubsPtr->tclPushStackFrame) /* 217 */ +#endif +#ifndef TclPopStackFrame +#define TclPopStackFrame \ + (tclIntStubsPtr->tclPopStackFrame) /* 218 */ +#endif #endif /* defined(USE_TCL_STUBS) && !defined(USE_TCL_STUB_PROCS) */ diff --git a/generic/tclNamesp.c b/generic/tclNamesp.c index b9818a1..48a306d 100644 --- a/generic/tclNamesp.c +++ b/generic/tclNamesp.c @@ -21,7 +21,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclNamesp.c,v 1.69 2004/12/11 14:41:47 msofer Exp $ + * RCS: @(#) $Id: tclNamesp.c,v 1.70 2004/12/15 20:44:41 msofer Exp $ */ #include "tclInt.h" @@ -533,6 +533,59 @@ Tcl_PopCallFrame(interp) /* *---------------------------------------------------------------------- * + * TclPushStackFrame -- + * + * Allocates a new call frame in the interpreter's execution stack, then + * pushes it onto the interpreter's Tcl call stack. + * Called when executing a Tcl procedure or a "namespace eval" or + * "namespace inscope" command. + * + * Results: + * Returns TCL_OK if successful, or TCL_ERROR (along with an error + * message in the interpreter's result object) if something goes wrong. + * + * Side effects: + * Modifies the interpreter's Tcl call stack. + * + *---------------------------------------------------------------------- + */ + +int +TclPushStackFrame(interp, framePtrPtr, namespacePtr, isProcCallFrame) + Tcl_Interp *interp; /* Interpreter in which the new call frame + * is to be pushed. */ + Tcl_CallFrame **framePtrPtr; /* Place to store a pointer to the stack + * allocated call frame.*/ + Tcl_Namespace *namespacePtr; /* Points to the namespace in which the + * frame will execute. If NULL, the + * interpreter's current namespace will + * be used. */ + int isProcCallFrame; /* If nonzero, the frame represents a + * called Tcl procedure and may have local + * vars. Vars will ordinarily be looked up + * in the frame. If new variables are + * created, they will be created in the + * frame. If 0, the frame is for a + * "namespace eval" or "namespace inscope" + * command and var references are treated + * as references to namespace variables. */ +{ + + *framePtrPtr = (Tcl_CallFrame *) TclStackAlloc(interp, sizeof(CallFrame)); + return Tcl_PushCallFrame(interp, *framePtrPtr, namespacePtr, isProcCallFrame); +} + +void +TclPopStackFrame(interp) + Tcl_Interp* interp; /* Interpreter with call frame to pop. */ +{ + Tcl_PopCallFrame(interp); + TclStackFree(interp); +} + +/* + *---------------------------------------------------------------------- + * * EstablishErrorCodeTraces -- * * Creates traces on the ::errorCode variable to keep its value @@ -2046,14 +2099,14 @@ TclGetNamespaceForQualName(interp, qualName, cxtNsPtr, flags, if (entryPtr != NULL) { nsPtr = (Namespace *) Tcl_GetHashValue(entryPtr); } else if (flags & TCL_CREATE_NS_IF_UNKNOWN) { - Tcl_CallFrame frame; + Tcl_CallFrame *framePtr; - (void) Tcl_PushCallFrame(interp, &frame, + (void) TclPushStackFrame(interp, &framePtr, (Tcl_Namespace *) nsPtr, /*isProcCallFrame*/ 0); nsPtr = (Namespace *) Tcl_CreateNamespace(interp, nsName, (ClientData) NULL, (Tcl_NamespaceDeleteProc *) NULL); - Tcl_PopCallFrame(interp); + TclPopStackFrame(interp); if (nsPtr == NULL) { Tcl_Panic("Could not create namespace '%s'", nsName); @@ -3183,7 +3236,7 @@ NamespaceEvalCmd(dummy, interp, objc, objv) Tcl_Obj *CONST objv[]; /* Argument objects. */ { Tcl_Namespace *namespacePtr; - CallFrame frame, *framePtr; + CallFrame *framePtr, **framePtrPtr; Tcl_Obj *objPtr; int result; @@ -3221,14 +3274,14 @@ NamespaceEvalCmd(dummy, interp, objc, objv) */ /* This is needed to satisfy GCC 3.3's strict aliasing rules */ - framePtr = &frame; - result = Tcl_PushCallFrame(interp, (Tcl_CallFrame *) framePtr, + framePtrPtr = &framePtr; + result = TclPushStackFrame(interp, (Tcl_CallFrame **) framePtrPtr, namespacePtr, /*isProcCallFrame*/ 0); if (result != TCL_OK) { return TCL_ERROR; } - frame.objc = objc; - frame.objv = objv; /* ref counts do not need to be incremented here */ + framePtr->objc = objc; + framePtr->objv = objv; /* ref counts do not need to be incremented here */ if (objc == 4) { result = Tcl_EvalObjEx(interp, objv[3], 0); @@ -3260,7 +3313,7 @@ NamespaceEvalCmd(dummy, interp, objc, objv) * Restore the previous "current" namespace. */ - Tcl_PopCallFrame(interp); + TclPopStackFrame(interp); return result; } @@ -3596,7 +3649,7 @@ NamespaceInscopeCmd(dummy, interp, objc, objv) Tcl_Obj *CONST objv[]; /* Argument objects. */ { Tcl_Namespace *namespacePtr; - Tcl_CallFrame frame; + Tcl_CallFrame *framePtr; int i, result; if (objc < 4) { @@ -3622,7 +3675,7 @@ NamespaceInscopeCmd(dummy, interp, objc, objv) * Make the specified namespace the current namespace. */ - result = Tcl_PushCallFrame(interp, &frame, namespacePtr, + result = TclPushStackFrame(interp, &framePtr, namespacePtr, /*isProcCallFrame*/ 0); if (result != TCL_OK) { return result; @@ -3675,7 +3728,7 @@ NamespaceInscopeCmd(dummy, interp, objc, objv) * Restore the previous "current" namespace. */ - Tcl_PopCallFrame(interp); + TclPopStackFrame(interp); return result; } diff --git a/generic/tclProc.c b/generic/tclProc.c index bcf82d2..feda831 100644 --- a/generic/tclProc.c +++ b/generic/tclProc.c @@ -10,7 +10,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclProc.c,v 1.68 2004/12/14 21:11:47 msofer Exp $ + * RCS: @(#) $Id: tclProc.c,v 1.69 2004/12/15 20:44:41 msofer Exp $ */ #include "tclInt.h" @@ -918,8 +918,7 @@ TclObjInterpProc(clientData, interp, objc, objv) { register Proc *procPtr = (Proc *) clientData; Namespace *nsPtr = procPtr->cmdPtr->nsPtr; - CallFrame frame; - register CallFrame *framePtr = &frame; + CallFrame *framePtr, **framePtrPtr; register Var *varPtr; register CompiledLocal *localPtr; char *procName; @@ -975,7 +974,8 @@ TclObjInterpProc(clientData, interp, objc, objv) * from one namespace to another. */ - result = Tcl_PushCallFrame(interp, (Tcl_CallFrame *) framePtr, + framePtrPtr = &framePtr; + result = TclPushStackFrame(interp, (Tcl_CallFrame **) framePtrPtr, (Tcl_Namespace *) nsPtr, FRAME_IS_PROC); if (result != TCL_OK) { @@ -1139,7 +1139,7 @@ TclObjInterpProc(clientData, interp, objc, objv) */ procDone: - Tcl_PopCallFrame(interp); + TclPopStackFrame(interp); if (compiledLocals != localStorage) { ckfree((char *) compiledLocals); } @@ -1181,7 +1181,7 @@ TclProcCompileProc(interp, procPtr, bodyPtr, nsPtr, description, procName) { Interp *iPtr = (Interp*)interp; int result; - Tcl_CallFrame frame; + Tcl_CallFrame *framePtr; Proc *saveProcPtr; ByteCode *codePtr = (ByteCode *) bodyPtr->internalRep.otherValuePtr; @@ -1247,12 +1247,12 @@ TclProcCompileProc(interp, procPtr, bodyPtr, nsPtr, description, procName) saveProcPtr = iPtr->compiledProcPtr; iPtr->compiledProcPtr = procPtr; - result = Tcl_PushCallFrame(interp, &frame, + result = TclPushStackFrame(interp, &framePtr, (Tcl_Namespace*)nsPtr, /* isProcCallFrame */ 0); if (result == TCL_OK) { result = tclByteCodeType.setFromAnyProc(interp, bodyPtr); - Tcl_PopCallFrame(interp); + TclPopStackFrame(interp); } iPtr->compiledProcPtr = saveProcPtr; diff --git a/generic/tclStubInit.c b/generic/tclStubInit.c index 83218f9..7c302bc 100644 --- a/generic/tclStubInit.c +++ b/generic/tclStubInit.c @@ -8,7 +8,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclStubInit.c,v 1.109 2004/12/01 23:18:53 dgp Exp $ + * RCS: @(#) $Id: tclStubInit.c,v 1.110 2004/12/15 20:44:42 msofer Exp $ */ #include "tclInt.h" @@ -299,6 +299,10 @@ TclIntStubs tclIntStubs = { TclpFindExecutable, /* 212 */ TclGetObjNameOfExecutable, /* 213 */ TclSetObjNameOfExecutable, /* 214 */ + TclStackAlloc, /* 215 */ + TclStackFree, /* 216 */ + TclPushStackFrame, /* 217 */ + TclPopStackFrame, /* 218 */ }; TclIntPlatStubs tclIntPlatStubs = { diff --git a/generic/tclTest.c b/generic/tclTest.c index c2f9dd0..72c8cef 100644 --- a/generic/tclTest.c +++ b/generic/tclTest.c @@ -14,7 +14,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclTest.c,v 1.86 2004/11/30 19:34:50 dgp Exp $ + * RCS: @(#) $Id: tclTest.c,v 1.87 2004/12/15 20:44:43 msofer Exp $ */ #define TCL_TEST @@ -4191,7 +4191,7 @@ TestgetvarfullnameCmd(dummy, interp, objc, objv) char *name, *arg; int flags = 0; Tcl_Namespace *namespacePtr; - Tcl_CallFrame frame; + Tcl_CallFrame *framePtr; Tcl_Var variable; int result; @@ -4222,7 +4222,7 @@ TestgetvarfullnameCmd(dummy, interp, objc, objv) if (namespacePtr == NULL) { return TCL_ERROR; } - result = Tcl_PushCallFrame(interp, &frame, namespacePtr, + result = TclPushStackFrame(interp, &framePtr, namespacePtr, /*isProcCallFrame*/ 0); if (result != TCL_OK) { return result; @@ -4233,7 +4233,7 @@ TestgetvarfullnameCmd(dummy, interp, objc, objv) (flags | TCL_LEAVE_ERR_MSG)); if (flags == TCL_NAMESPACE_ONLY) { - Tcl_PopCallFrame(interp); + TclPopStackFrame(interp); } if (variable == (Tcl_Var) NULL) { return TCL_ERROR; -- cgit v0.12