diff options
author | Miguel Sofer <miguel.sofer@gmail.com> | 2007-07-31 17:03:34 (GMT) |
---|---|---|
committer | Miguel Sofer <miguel.sofer@gmail.com> | 2007-07-31 17:03:34 (GMT) |
commit | c78aef8e3103f916ede55e36edd8f5fb876ab0f6 (patch) | |
tree | 6bef95f9839cbc6e08ab7040bd9bbd6c9925a5f8 /generic/tclProc.c | |
parent | 4de8702e9bdf3ad59efdba5918502f6b9f23c827 (diff) | |
download | tcl-c78aef8e3103f916ede55e36edd8f5fb876ab0f6.zip tcl-c78aef8e3103f916ede55e36edd8f5fb876ab0f6.tar.gz tcl-c78aef8e3103f916ede55e36edd8f5fb876ab0f6.tar.bz2 |
VarReform [Patch 1750051]
*** POTENTIAL INCOMPATIBILITY *** (tclInt.h and tclCompile.h)
Diffstat (limited to 'generic/tclProc.c')
-rw-r--r-- | generic/tclProc.c | 523 |
1 files changed, 307 insertions, 216 deletions
diff --git a/generic/tclProc.c b/generic/tclProc.c index 8cd8aa1..c6e0219 100644 --- a/generic/tclProc.c +++ b/generic/tclProc.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: tclProc.c,v 1.125 2007/06/20 18:46:14 dgp Exp $ + * RCS: @(#) $Id: tclProc.c,v 1.126 2007/07/31 17:03:39 msofer Exp $ */ #include "tclInt.h" @@ -27,13 +27,15 @@ static void FreeLambdaInternalRep(Tcl_Obj *objPtr); static int InitArgsAndLocals(Tcl_Interp *interp, Tcl_Obj *procNameObj, int skip); static void InitCompiledLocals(Tcl_Interp *interp, - ByteCode *codePtr, CompiledLocal *localPtr, - Var *varPtr, Namespace *nsPtr); + ByteCode *codePtr, Var *defPtr, + Namespace *nsPtr); +static void InitLocalCache(Proc *procPtr); static int PushProcCallFrame(ClientData clientData, register Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int isLambda); static void ProcBodyDup(Tcl_Obj *srcPtr, Tcl_Obj *dupPtr); static void ProcBodyFree(Tcl_Obj *objPtr); +static int ProcWrongNumArgs(Tcl_Interp *interp, int skip); static void MakeProcError(Tcl_Interp *interp, Tcl_Obj *procNameObj); static void MakeLambdaError(Tcl_Interp *interp, @@ -527,13 +529,17 @@ TclCreateProc( * we and out VAR_UNDEFINED to support bridging precompiled <= 8.3 * code in 8.4 where this is now used as an optimization * indicator. Yes, this is a hack. -- hobbs + * + * FIXME! Is this right? It does depend on VAR_ARGUMENT not + * changing. Note that a change of VAR_TEMPORARY would not be so + * important, as there are no variable names in precompiled + * bytecodes anyway - right? */ if ((localPtr->nameLength != nameLength) || (strcmp(localPtr->name, fieldValues[0])) || (localPtr->frameIndex != i) - || ((localPtr->flags & ~VAR_UNDEFINED) - != (VAR_SCALAR | VAR_ARGUMENT)) + || !(localPtr->flags & VAR_ARGUMENT) /* /// CHECK HERE! */ || (localPtr->defValuePtr == NULL && fieldCount == 2) || (localPtr->defValuePtr != NULL && fieldCount != 2)) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( @@ -588,7 +594,7 @@ TclCreateProc( localPtr->nextPtr = NULL; localPtr->nameLength = nameLength; localPtr->frameIndex = i; - localPtr->flags = VAR_SCALAR | VAR_ARGUMENT; + localPtr->flags = VAR_ARGUMENT; localPtr->resolveInfo = NULL; if (fieldCount == 2) { @@ -1031,161 +1037,21 @@ TclIsProc( */ static int -InitArgsAndLocals( - register Tcl_Interp *interp,/* Interpreter in which procedure was - * invoked. */ - Tcl_Obj *procNameObj, /* Procedure name for error reporting. */ - int skip) /* Number of initial arguments to be skipped, - * i.e., words in the "command name". */ +ProcWrongNumArgs( + Tcl_Interp *interp, int skip) { CallFrame *framePtr = ((Interp *)interp)->varFramePtr; register Proc *procPtr = framePtr->procPtr; - ByteCode *codePtr = procPtr->bodyPtr->internalRep.otherValuePtr; - register Var *varPtr; - register CompiledLocal *localPtr; - int localCt, numArgs, argCt, i, imax; - Var *compiledLocals; - Tcl_Obj *const *argObjs; + register Var *defPtr; + int localCt = procPtr->numCompiledLocals, numArgs, i; Tcl_Obj **desiredObjs; - const char *final; - - /* - * Create the "compiledLocals" array. Make sure it is large enough to hold - * all the procedure's compiled local variables, including its formal - * parameters. - */ - - localCt = procPtr->numCompiledLocals; - compiledLocals = (Var*) TclStackAlloc(interp, (int)(localCt*sizeof(Var))); - framePtr->numCompiledLocals = localCt; - framePtr->compiledLocals = compiledLocals; - - /* - * Match and assign the call's actual parameters to the procedure's formal - * arguments. The formal arguments are described by the first numArgs - * entries in both the Proc structure's local variable list and the call - * frame's local variable array. - */ - - numArgs = procPtr->numArgs; - argCt = framePtr->objc - skip; /* Set it to the number of args to the - * procedure. */ - argObjs = framePtr->objv + skip; - varPtr = framePtr->compiledLocals; - localPtr = procPtr->firstLocalPtr; - if (numArgs == 0) { - if (argCt) { - goto incorrectArgs; - } else { - goto correctArgs; - } - } - imax = ((argCt < numArgs-1) ? argCt : numArgs-1); - for (i = 0; i < imax; i++) { - /* - * "Normal" arguments; last formal is special, depends on it being - * 'args'. - */ - - Tcl_Obj *objPtr = argObjs[i]; - - varPtr->value.objPtr = objPtr; - Tcl_IncrRefCount(objPtr); /* Local var is a reference. */ - varPtr->name = localPtr->name; - varPtr->nsPtr = NULL; - varPtr->hPtr = NULL; - varPtr->refCount = 0; - varPtr->tracePtr = NULL; - varPtr->searchPtr = NULL; - varPtr->flags = localPtr->flags; - varPtr++; - localPtr = localPtr->nextPtr; - } - for (; i < numArgs-1; i++) { - /* - * This loop is entered if argCt < (numArgs-1). Set default values; - * last formal is special. - */ - - if (localPtr->defValuePtr != NULL) { - Tcl_Obj *objPtr = localPtr->defValuePtr; - - varPtr->value.objPtr = objPtr; - Tcl_IncrRefCount(objPtr); /* Local var is a reference. */ - varPtr->name = localPtr->name; - varPtr->nsPtr = NULL; - varPtr->hPtr = NULL; - varPtr->refCount = 0; - varPtr->tracePtr = NULL; - varPtr->searchPtr = NULL; - varPtr->flags = localPtr->flags; - varPtr++; - localPtr = localPtr->nextPtr; - } else { - goto incorrectArgs; - } - } - - /* - * When we get here, the last formal argument remains to be defined: - * localPtr and varPtr point to the last argument to be initialized. - */ - - if (localPtr->flags & VAR_IS_ARGS) { - Tcl_Obj *listPtr = Tcl_NewListObj(argCt-i, argObjs+i); - - varPtr->value.objPtr = listPtr; - Tcl_IncrRefCount(listPtr); /* Local var is a reference. */ - } else if (argCt == numArgs) { - Tcl_Obj *objPtr = argObjs[i]; - - varPtr->value.objPtr = objPtr; - Tcl_IncrRefCount(objPtr); /* Local var is a reference. */ - } else if ((argCt < numArgs) && (localPtr->defValuePtr != NULL)) { - Tcl_Obj *objPtr = localPtr->defValuePtr; - - varPtr->value.objPtr = objPtr; - Tcl_IncrRefCount(objPtr); /* Local var is a reference. */ - } else { - goto incorrectArgs; - } - - varPtr->name = localPtr->name; - varPtr->nsPtr = NULL; - varPtr->hPtr = NULL; - varPtr->refCount = 0; - varPtr->tracePtr = NULL; - varPtr->searchPtr = NULL; - varPtr->flags = localPtr->flags; - - localPtr = localPtr->nextPtr; - varPtr++; - - /* - * Initialise and resolve the remaining compiledLocals. - */ - - correctArgs: - if (localPtr) { - InitCompiledLocals(interp, codePtr, localPtr, varPtr, framePtr->nsPtr); - } - - return TCL_OK; - - - incorrectArgs: - /* - * Do initialise all compiled locals, to avoid problems at - * DeleteLocalVars. - */ - - final = NULL; - InitCompiledLocals(interp, codePtr, localPtr, varPtr, framePtr->nsPtr); - + const char *final = NULL; + /* * Build up desired argument list for Tcl_WrongNumArgs */ + numArgs = framePtr->procPtr->numArgs; desiredObjs = (Tcl_Obj **) TclStackAlloc(interp, (int) sizeof(Tcl_Obj *) * (numArgs+1)); @@ -1198,22 +1064,23 @@ InitArgsAndLocals( #endif /* AVOID_HACKS_FOR_ITCL */ Tcl_IncrRefCount(desiredObjs[0]); - localPtr = procPtr->firstLocalPtr; - for (i=1 ; i<=numArgs ; i++) { + defPtr = (Var *) (&framePtr->localCachePtr->varName0 + localCt); + for (i=1 ; i<=numArgs ; i++, defPtr++) { Tcl_Obj *argObj; + Tcl_Obj *namePtr = localName(framePtr, i-1); - if (localPtr->defValuePtr != NULL) { + if (defPtr->value.objPtr != NULL) { TclNewObj(argObj); - Tcl_AppendStringsToObj(argObj, "?", localPtr->name, "?", NULL); - } else if ((i==numArgs) && !strcmp(localPtr->name, "args")) { + Tcl_AppendStringsToObj(argObj, "?", TclGetString(namePtr), "?", NULL); + } else if (defPtr->flags & VAR_IS_ARGS) { numArgs--; final = "..."; break; } else { - argObj = Tcl_NewStringObj(localPtr->name, -1); + argObj = namePtr; + Tcl_IncrRefCount(namePtr); } desiredObjs[i] = argObj; - localPtr = localPtr->nextPtr; } Tcl_ResetResult(interp); @@ -1229,6 +1096,46 @@ InitArgsAndLocals( /* *---------------------------------------------------------------------- * + * TclInitCompiledLocals -- + * + * This routine is invoked in order to initialize the compiled locals + * table for a new call frame. + * + * DEPRECATED: functionality has been inlined elsewhere; this function + * remains to insure binary compatibility with Itcl. + * + + * Results: + * None. + * + * Side effects: + * May invoke various name resolvers in order to determine which + * variables are being referenced at runtime. + * + *---------------------------------------------------------------------- + */ +void +TclInitCompiledLocals( + Tcl_Interp *interp, /* Current interpreter. */ + CallFrame *framePtr, /* Call frame to initialize. */ + Namespace *nsPtr) /* Pointer to current namespace. */ +{ + Var *varPtr = framePtr->compiledLocals; + Tcl_Obj *bodyPtr; + ByteCode *codePtr; + + bodyPtr = framePtr->procPtr->bodyPtr; + if (bodyPtr->typePtr != &tclByteCodeType) { + Tcl_Panic("body object for proc attached to frame is not a byte code type"); + } + codePtr = bodyPtr->internalRep.otherValuePtr; + + InitCompiledLocals(interp, codePtr, varPtr, nsPtr); +} + +/* + *---------------------------------------------------------------------- + * * InitCompiledLocals -- * * This routine is invoked in order to initialize the compiled locals @@ -1248,14 +1155,29 @@ static void InitCompiledLocals( Tcl_Interp *interp, /* Current interpreter. */ ByteCode *codePtr, - CompiledLocal *localPtr, Var *varPtr, Namespace *nsPtr) /* Pointer to current namespace. */ { Interp *iPtr = (Interp *) interp; int haveResolvers = (nsPtr->compiledVarResProc || iPtr->resolverPtr); - CompiledLocal *firstLocalPtr; + CompiledLocal *firstLocalPtr, *localPtr; + int varNum; + + /* + * Find the localPtr corresponding to varPtr + */ + varNum = varPtr - iPtr->framePtr->compiledLocals; + localPtr = iPtr->framePtr->procPtr->firstLocalPtr; + while (varNum--) { + localPtr = localPtr->nextPtr; + } + + /* + //FIXME: old bytecompiled code: drop whatever flags are coming in (except + //maybe for VAR_TEMPORARY? Who cares really?) A job for tbcload, not us. + */ + if (!(haveResolvers && (codePtr->flags & TCL_BYTECODE_RESOLVE_VARS))) { /* * Initialize the array of local variables stored in the call frame. @@ -1266,31 +1188,21 @@ InitCompiledLocals( doInitCompiledLocals: if (!haveResolvers) { + /* + * Should not be called: deadwood. + */ + for (; localPtr != NULL; varPtr++, localPtr = localPtr->nextPtr) { - varPtr->value.objPtr = NULL; - varPtr->name = localPtr->name; /* Will be just '\0' if temp - * var. */ - varPtr->nsPtr = NULL; - varPtr->hPtr = NULL; - varPtr->refCount = 0; - varPtr->tracePtr = NULL; - varPtr->searchPtr = NULL; varPtr->flags = localPtr->flags; + varPtr->value.objPtr = NULL; } return; } else { Tcl_ResolvedVarInfo *resVarInfo; for (; localPtr != NULL; varPtr++, localPtr = localPtr->nextPtr) { - varPtr->value.objPtr = NULL; - varPtr->name = localPtr->name; /* Will be just '\0' if temp - * var. */ - varPtr->nsPtr = NULL; - varPtr->hPtr = NULL; - varPtr->refCount = 0; - varPtr->tracePtr = NULL; - varPtr->searchPtr = NULL; varPtr->flags = localPtr->flags; + varPtr->value.objPtr = NULL; /* * Now invoke the resolvers to determine the exact variables @@ -1302,9 +1214,9 @@ InitCompiledLocals( Var *resolvedVarPtr = (Var *) (*resVarInfo->fetchProc)(interp, resVarInfo); if (resolvedVarPtr) { - resolvedVarPtr->refCount++; - varPtr->value.linkPtr = resolvedVarPtr; + VarHashRefCount(resolvedVarPtr)++; varPtr->flags = VAR_LINK; + varPtr->value.linkPtr = resolvedVarPtr; } } } @@ -1361,46 +1273,224 @@ InitCompiledLocals( goto doInitCompiledLocals; } } - -/* - *---------------------------------------------------------------------- - * - * TclInitCompiledLocals -- - * - * This routine is invoked in order to initialize the compiled locals - * table for a new call frame. - * - * DEPRECATED: functionality has been inlined elsewhere; this function - * remains to insure binary compatibility with Itcl. - * - * Results: - * None. - * - * Side effects: - * May invoke various name resolvers in order to determine which - * variables are being referenced at runtime. - * - *---------------------------------------------------------------------- - */ void -TclInitCompiledLocals( - Tcl_Interp *interp, /* Current interpreter. */ - CallFrame *framePtr, /* Call frame to initialize. */ - Namespace *nsPtr) /* Pointer to current namespace. */ +TclFreeLocalCache( + Tcl_Interp *interp, + LocalCache *localCachePtr) { - Var *varPtr = framePtr->compiledLocals; - Tcl_Obj *bodyPtr; - ByteCode *codePtr; - CompiledLocal *localPtr = framePtr->procPtr->firstLocalPtr; + int i; + Tcl_Obj **namePtrPtr = &localCachePtr->varName0; - bodyPtr = framePtr->procPtr->bodyPtr; - if (bodyPtr->typePtr != &tclByteCodeType) { - Tcl_Panic("body object for proc attached to frame is not a byte code type"); + for (i = 0; i < localCachePtr->numVars; i++, namePtrPtr++) { + Tcl_Obj *objPtr = *namePtrPtr; + /* + * Note that this can be called with interp==NULL, on interp + * deletion. In that case, the literal table and objects go away + * on their own. + */ + if (objPtr) { + if (interp) { + TclReleaseLiteral(interp, objPtr); + } else { + Tcl_DecrRefCount(objPtr); + } + } } - codePtr = bodyPtr->internalRep.otherValuePtr; + ckfree((char *) localCachePtr); +} - InitCompiledLocals(interp, codePtr, localPtr, varPtr, nsPtr); +static void +InitLocalCache(Proc *procPtr) +{ + Interp *iPtr = procPtr->iPtr; + ByteCode *codePtr = procPtr->bodyPtr->internalRep.otherValuePtr; + CompiledLocal *localPtr = codePtr->procPtr->firstLocalPtr; + int localCt = procPtr->numCompiledLocals; + int numArgs = procPtr->numArgs, i = 0; + + Tcl_Obj **namePtr; + Var *varPtr; + LocalCache *localCachePtr; + int new; + + /* + * Cache the names and initial values of local variables; store the + * cache in both the framePtr for this execution and in the codePtr + * for future calls. + */ + + localCachePtr = (LocalCache *) ckalloc(sizeof(LocalCache) + + (localCt-1)*sizeof(Tcl_Obj *) + + numArgs*sizeof(Var)); + + namePtr = &localCachePtr->varName0; + varPtr = (Var *) (namePtr + localCt); + localPtr = codePtr->procPtr->firstLocalPtr; + while (localPtr) { + if (TclIsVarTemporary(localPtr)) { + *namePtr = NULL; + } else { + *namePtr = TclCreateLiteral(iPtr, localPtr->name, + localPtr->nameLength, /* hash */ (unsigned int) -1, + &new, /* nsPtr */ NULL, 0, NULL); + Tcl_IncrRefCount(*namePtr); + } + + if (i < numArgs) { + varPtr->flags = (localPtr->flags & VAR_IS_ARGS); + varPtr->value.objPtr = localPtr->defValuePtr; + varPtr++; + i++; + } + namePtr++; + localPtr=localPtr->nextPtr; + } + codePtr->localCachePtr = localCachePtr; + localCachePtr->refCount = 1; + localCachePtr->numVars = localCt; +} + +static int +InitArgsAndLocals( + register Tcl_Interp *interp,/* Interpreter in which procedure was + * invoked. */ + Tcl_Obj *procNameObj, /* Procedure name for error reporting. */ + int skip) /* Number of initial arguments to be skipped, + * i.e., words in the "command name". */ +{ + CallFrame *framePtr = ((Interp *)interp)->varFramePtr; + register Proc *procPtr = framePtr->procPtr; + ByteCode *codePtr = procPtr->bodyPtr->internalRep.otherValuePtr; + register Var *varPtr, *defPtr; + int localCt = procPtr->numCompiledLocals, numArgs, argCt, i, imax; + Tcl_Obj *const *argObjs; + + /* + * Make sure that the local cache of variable names and initial values has + * been initialised properly . + */ + + if (localCt) { + if (!codePtr->localCachePtr) { + InitLocalCache(procPtr) ; + } + framePtr->localCachePtr = codePtr->localCachePtr; + framePtr->localCachePtr->refCount++; + defPtr = (Var *) (&framePtr->localCachePtr->varName0 + localCt); + } else { + defPtr = NULL; + } + + /* + * Create the "compiledLocals" array. Make sure it is large enough to hold + * all the procedure's compiled local variables, including its formal + * parameters. + */ + + varPtr = (Var*) TclStackAlloc(interp, (int)(localCt*sizeof(Var))); + framePtr->compiledLocals = varPtr; + framePtr->numCompiledLocals = localCt; + + /* + * Match and assign the call's actual parameters to the procedure's formal + * arguments. The formal arguments are described by the first numArgs + * entries in both the Proc structure's local variable list and the call + * frame's local variable array. + */ + + numArgs = procPtr->numArgs; + argCt = framePtr->objc - skip; /* Set it to the number of args to the + * procedure. */ + argObjs = framePtr->objv + skip; + if (numArgs == 0) { + if (argCt) { + goto incorrectArgs; + } else { + goto correctArgs; + } + } + imax = ((argCt < numArgs-1) ? argCt : numArgs-1); + for (i = 0; i < imax; i++, varPtr++, defPtr++) { + /* + * "Normal" arguments; last formal is special, depends on it being + * 'args'. + */ + + Tcl_Obj *objPtr = argObjs[i]; + + varPtr->flags = 0; + varPtr->value.objPtr = objPtr; + Tcl_IncrRefCount(objPtr); /* Local var is a reference. */ + } + for (; i < numArgs-1; i++, varPtr++, defPtr++) { + /* + * This loop is entered if argCt < (numArgs-1). Set default values; + * last formal is special. + */ + + Tcl_Obj *objPtr = defPtr->value.objPtr; + + if (objPtr) { + varPtr->flags = 0; + varPtr->value.objPtr = objPtr; + Tcl_IncrRefCount(objPtr); /* Local var reference. */ + } else { + goto incorrectArgs; + } + } + + /* + * When we get here, the last formal argument remains to be defined: + * defPtr and varPtr point to the last argument to be initialized. + */ + + + varPtr->flags = 0; + if (defPtr->flags & VAR_IS_ARGS) { + Tcl_Obj *listPtr = Tcl_NewListObj(argCt-i, argObjs+i); + + varPtr->value.objPtr = listPtr; + Tcl_IncrRefCount(listPtr); /* Local var is a reference. */ + } else if (argCt == numArgs) { + Tcl_Obj *objPtr = argObjs[i]; + + varPtr->value.objPtr = objPtr; + Tcl_IncrRefCount(objPtr); /* Local var is a reference. */ + } else if ((argCt < numArgs) && (defPtr->value.objPtr != NULL)) { + Tcl_Obj *objPtr = defPtr->value.objPtr; + + varPtr->value.objPtr = objPtr; + Tcl_IncrRefCount(objPtr); /* Local var is a reference. */ + } else { + goto incorrectArgs; + } + varPtr++; + + /* + * Initialise and resolve the remaining compiledLocals. In the absence of + * resolvers, they are undefined local vars: (flags=0, value=NULL). + */ + + correctArgs: + if (numArgs < localCt) { + if (!framePtr->nsPtr->compiledVarResProc && !((Interp *)interp)->resolverPtr) { + memset(varPtr, 0, (localCt - numArgs)*sizeof(Var)); + } else { + InitCompiledLocals(interp, codePtr, varPtr, framePtr->nsPtr); + } + } + + return TCL_OK; + + + incorrectArgs: + /* + * Initialise all compiled locals to avoid problems at DeleteLocalVars. + */ + + memset(varPtr, 0, ((framePtr->compiledLocals + localCt)-varPtr)*sizeof(Var)); + return ProcWrongNumArgs(interp, skip); } /* @@ -1437,7 +1527,8 @@ PushProcCallFrame( Namespace *nsPtr = procPtr->cmdPtr->nsPtr; CallFrame *framePtr, **framePtrPtr; int result; - + ByteCode *codePtr; + /* * If necessary (i.e. if we haven't got a suitable compilation already * cached) compile the procedure's body. The compiler will allocate frame @@ -1448,7 +1539,6 @@ PushProcCallFrame( if (procPtr->bodyPtr->typePtr == &tclByteCodeType) { Interp *iPtr = (Interp *) interp; - ByteCode *codePtr = procPtr->bodyPtr->internalRep.otherValuePtr; /* * When we've got bytecode, this is the check for validity. That is, @@ -1459,6 +1549,7 @@ PushProcCallFrame( * commands and/or resolver changes are considered). */ + codePtr = procPtr->bodyPtr->internalRep.otherValuePtr; if (((Interp *) *codePtr->interpHandle != iPtr) || (codePtr->compileEpoch != iPtr->compileEpoch) || (codePtr->nsPtr != nsPtr) |