diff options
Diffstat (limited to 'generic/tclProc.c')
-rw-r--r-- | generic/tclProc.c | 2281 |
1 files changed, 1435 insertions, 846 deletions
diff --git a/generic/tclProc.c b/generic/tclProc.c index 92e81af..ce1c767 100644 --- a/generic/tclProc.c +++ b/generic/tclProc.c @@ -7,15 +7,26 @@ * Copyright (c) 1987-1993 The Regents of the University of California. * Copyright (c) 1994-1998 Sun Microsystems, Inc. * Copyright (c) 2004-2006 Miguel Sofer + * Copyright (c) 2007 Daniel A. Steffen <das@users.sourceforge.net> * * 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.108 2006/11/28 22:20:29 andreas_kupries Exp $ */ #include "tclInt.h" #include "tclCompile.h" +#include "tclOOInt.h" + +/* + * Variables that are part of the [apply] command implementation and which + * have to be passed to the other side of the NRE call. + */ + +typedef struct { + int isRootEnsemble; + Command cmd; + ExtraFrameInfo efi; +} ApplyExtraData; /* * Prototypes for static functions in this file @@ -24,30 +35,33 @@ static void DupLambdaInternalRep(Tcl_Obj *objPtr, Tcl_Obj *copyPtr); static void FreeLambdaInternalRep(Tcl_Obj *objPtr); -static void InitCompiledLocals(Tcl_Interp *interp, - ByteCode *codePtr, CompiledLocal *localPtr, - Var *varPtr, Namespace *nsPtr); -static int ObjInterpProcEx(ClientData clientData, +static int InitArgsAndLocals(Tcl_Interp *interp, + Tcl_Obj *procNameObj, int skip); +static void InitResolvedLocals(Tcl_Interp *interp, + 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, - ProcErrorProc errorProc); + 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, Tcl_Obj *procNameObj); static int SetLambdaFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr); -static int ProcCompileProc(Tcl_Interp *interp, Proc *procPtr, - Tcl_Obj *bodyPtr, Namespace *nsPtr, - CONST char *description, CONST char *procName, - Proc **procPtrPtr); + +static Tcl_NRPostProc ApplyNR2; +static Tcl_NRPostProc InterpProcNR2; +static Tcl_NRPostProc Uplevel_Callback; /* * The ProcBodyObjType type */ -Tcl_ObjType tclProcBodyType = { +const Tcl_ObjType tclProcBodyType = { "procbody", /* name for this type */ ProcBodyFree, /* FreeInternalRep function */ ProcBodyDup, /* DupInternalRep function */ @@ -59,15 +73,15 @@ Tcl_ObjType tclProcBodyType = { }; /* - * The [upvar]/[uplevel] level reference type. Uses the twoPtrValue field, - * encoding the type of level reference in ptr1 and the actual parsed out - * offset in ptr2. + * The [upvar]/[uplevel] level reference type. Uses the ptrAndLongRep field, + * encoding the type of level reference in ptr and the actual parsed out + * offset in value. * * Uses the default behaviour throughout, and never disposes of the string * rep; it's just a cache type. */ -static Tcl_ObjType levelReferenceType = { +static const Tcl_ObjType levelReferenceType = { "levelReference", NULL, NULL, NULL, NULL }; @@ -81,7 +95,7 @@ static Tcl_ObjType levelReferenceType = { * will execute within. */ -static Tcl_ObjType lambdaType = { +static const Tcl_ObjType lambdaType = { "lambdaExpr", /* name */ FreeLambdaInternalRep, /* freeIntRepProc */ DupLambdaInternalRep, /* dupIntRepProc */ @@ -112,12 +126,12 @@ Tcl_ProcObjCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { register Interp *iPtr = (Interp *) interp; Proc *procPtr; - char *fullName; - CONST char *procName, *procArgs, *procBody; + const char *fullName; + const char *procName, *procArgs, *procBody; Namespace *nsPtr, *altNsPtr, *cxtNsPtr; Tcl_Command cmd; Tcl_DString ds; @@ -138,20 +152,25 @@ Tcl_ProcObjCmd( &nsPtr, &altNsPtr, &cxtNsPtr, &procName); if (nsPtr == NULL) { - Tcl_AppendResult(interp, "can't create procedure \"", fullName, - "\": unknown namespace", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "can't create procedure \"%s\": unknown namespace", + fullName)); + Tcl_SetErrorCode(interp, "TCL", "VALUE", "COMMAND", NULL); return TCL_ERROR; } if (procName == NULL) { - Tcl_AppendResult(interp, "can't create procedure \"", fullName, - "\": bad procedure name", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "can't create procedure \"%s\": bad procedure name", + fullName)); + Tcl_SetErrorCode(interp, "TCL", "VALUE", "COMMAND", NULL); return TCL_ERROR; } if ((nsPtr != iPtr->globalNsPtr) && (procName != NULL) && (procName[0] == ':')) { - Tcl_AppendResult(interp, "can't create procedure \"", procName, - "\" in non-global namespace with name starting with \":\"", - NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "can't create procedure \"%s\" in non-global namespace with" + " name starting with \":\"", procName)); + Tcl_SetErrorCode(interp, "TCL", "VALUE", "COMMAND", NULL); return TCL_ERROR; } @@ -177,13 +196,12 @@ Tcl_ProcObjCmd( Tcl_DStringInit(&ds); if (nsPtr != iPtr->globalNsPtr) { Tcl_DStringAppend(&ds, nsPtr->fullName, -1); - Tcl_DStringAppend(&ds, "::", 2); + TclDStringAppendLiteral(&ds, "::"); } Tcl_DStringAppend(&ds, procName, -1); - cmd = Tcl_CreateObjCommand(interp, Tcl_DStringValue(&ds), - TclObjInterpProc, (ClientData) procPtr, TclProcDeleteProc); - + cmd = Tcl_NRCreateCommand(interp, Tcl_DStringValue(&ds), TclObjInterpProc, + TclNRInterpProc, procPtr, TclProcDeleteProc); Tcl_DStringFree(&ds); /* @@ -195,8 +213,9 @@ Tcl_ProcObjCmd( procPtr->cmdPtr = (Command *) cmd; - /* TIP #280 Remember the line the procedure body is starting on. In a - * Byte code context we ask the engine to provide us with the necessary + /* + * TIP #280: Remember the line the procedure body is starting on. In a + * bytecode context we ask the engine to provide us with the necessary * information. This is for the initialization of the byte code compiler * when the body is used for the first time. * @@ -208,52 +227,85 @@ Tcl_ProcObjCmd( */ if (iPtr->cmdFramePtr) { - CmdFrame context = *iPtr->cmdFramePtr; + CmdFrame *contextPtr = TclStackAlloc(interp, sizeof(CmdFrame)); + + *contextPtr = *iPtr->cmdFramePtr; + if (contextPtr->type == TCL_LOCATION_BC) { + /* + * Retrieve source information from the bytecode, if possible. If + * the information is retrieved successfully, context.type will be + * TCL_LOCATION_SOURCE and the reference held by + * context.data.eval.path will be counted. + */ + + TclGetSrcInfoForPc(contextPtr); + } else if (contextPtr->type == TCL_LOCATION_SOURCE) { + /* + * The copy into 'context' up above has created another reference + * to 'context.data.eval.path'; account for it. + */ - if (context.type == TCL_LOCATION_BC) { - TclGetSrcInfoForPc (&context); - /* May get path in context */ - } else if (context.type == TCL_LOCATION_SOURCE) { - /* context now holds another reference */ - Tcl_IncrRefCount (context.data.eval.path); + Tcl_IncrRefCount(contextPtr->data.eval.path); } - /* type == TCL_LOCATION_PREBC implies that 'line' is NULL here! - * We cannot assume that 'line' is valid here, we have to check. - */ + if (contextPtr->type == TCL_LOCATION_SOURCE) { + /* + * We can account for source location within a proc only if the + * proc body was not created by substitution. + */ - if ((context.type == TCL_LOCATION_SOURCE) && - context.line && - (context.nline >= 4) && - (context.line [3] >= 0)) { - int new; - CmdFrame* cfPtr = (CmdFrame*) ckalloc (sizeof (CmdFrame)); - - cfPtr->level = -1; - cfPtr->type = context.type; - cfPtr->line = (int*) ckalloc (sizeof (int)); - cfPtr->line [0] = context.line [3]; - cfPtr->nline = 1; - cfPtr->framePtr = NULL; - cfPtr->nextPtr = NULL; - - if (context.type == TCL_LOCATION_SOURCE) { - cfPtr->data.eval.path = context.data.eval.path; - /* Transfer of reference. The reference going away (release of - * the context) is replaced by the reference in the - * constructed cmdframe */ - } else { - cfPtr->type = TCL_LOCATION_EVAL; - cfPtr->data.eval.path = NULL; + if (contextPtr->line + && (contextPtr->nline >= 4) && (contextPtr->line[3] >= 0)) { + int isNew; + Tcl_HashEntry *hePtr; + CmdFrame *cfPtr = ckalloc(sizeof(CmdFrame)); + + cfPtr->level = -1; + cfPtr->type = contextPtr->type; + cfPtr->line = ckalloc(sizeof(int)); + cfPtr->line[0] = contextPtr->line[3]; + cfPtr->nline = 1; + cfPtr->framePtr = NULL; + cfPtr->nextPtr = NULL; + + cfPtr->data.eval.path = contextPtr->data.eval.path; + Tcl_IncrRefCount(cfPtr->data.eval.path); + + cfPtr->cmd = NULL; + cfPtr->len = 0; + + hePtr = Tcl_CreateHashEntry(iPtr->linePBodyPtr, + procPtr, &isNew); + if (!isNew) { + /* + * Get the old command frame and release it. See also + * TclProcCleanupProc in this file. Currently it seems as + * if only the procbodytest::proc command of the testsuite + * is able to trigger this situation. + */ + + CmdFrame *cfOldPtr = Tcl_GetHashValue(hePtr); + + if (cfOldPtr->type == TCL_LOCATION_SOURCE) { + Tcl_DecrRefCount(cfOldPtr->data.eval.path); + cfOldPtr->data.eval.path = NULL; + } + ckfree(cfOldPtr->line); + cfOldPtr->line = NULL; + ckfree(cfOldPtr); + } + Tcl_SetHashValue(hePtr, cfPtr); } - cfPtr->cmd.str.cmd = NULL; - cfPtr->cmd.str.len = 0; + /* + * 'contextPtr' is going out of scope; account for the reference + * that it's holding to the path name. + */ - Tcl_SetHashValue (Tcl_CreateHashEntry (iPtr->linePBodyPtr, - (char*) procPtr, &new), - cfPtr); + Tcl_DecrRefCount(contextPtr->data.eval.path); + contextPtr->data.eval.path = NULL; } + TclStackFree(interp, contextPtr); } /* @@ -283,8 +335,10 @@ Tcl_ProcObjCmd( } if ((procArgs[0] == 'a') && (strncmp(procArgs, "args", 4) == 0)) { + int numBytes; + procArgs +=4; - while(*procArgs != '\0') { + while (*procArgs != '\0') { if (*procArgs != ' ') { goto done; } @@ -295,12 +349,9 @@ Tcl_ProcObjCmd( * The argument list is just "args"; check the body */ - procBody = TclGetString(objv[3]); - while (*procBody != '\0') { - if (!isspace(UCHAR(*procBody))) { - goto done; - } - procBody++; + procBody = Tcl_GetStringFromObj(objv[3], &numBytes); + if (TclParseAllWhiteSpace(procBody, numBytes) < numBytes) { + goto done; } /* @@ -340,19 +391,19 @@ Tcl_ProcObjCmd( int TclCreateProc( - Tcl_Interp *interp, /* interpreter containing proc */ - Namespace *nsPtr, /* namespace containing this proc */ - CONST char *procName, /* unqualified name of this proc */ - Tcl_Obj *argsPtr, /* description of arguments */ - Tcl_Obj *bodyPtr, /* command body */ - Proc **procPtrPtr) /* returns: pointer to proc data */ + Tcl_Interp *interp, /* Interpreter containing proc. */ + Namespace *nsPtr, /* Namespace containing this proc. */ + const char *procName, /* Unqualified name of this proc. */ + Tcl_Obj *argsPtr, /* Description of arguments. */ + Tcl_Obj *bodyPtr, /* Command body. */ + Proc **procPtrPtr) /* Returns: pointer to proc data. */ { - Interp *iPtr = (Interp*)interp; - CONST char **argArray = NULL; + Interp *iPtr = (Interp *) interp; + const char **argArray = NULL; register Proc *procPtr; int i, length, result, numArgs; - CONST char *args, *bytes, *p; + const char *args, *bytes, *p; register CompiledLocal *localPtr = NULL; Tcl_Obj *defPtr; int precompiled = 0; @@ -370,7 +421,7 @@ TclCreateProc( * will be holding a reference to it. */ - procPtr = bodyPtr->internalRep.otherValuePtr; + procPtr = bodyPtr->internalRep.twoPtrValue.ptr1; procPtr->iPtr = iPtr; procPtr->refCount++; precompiled = 1; @@ -380,9 +431,9 @@ TclCreateProc( * is identical to, e.g., the body of another procedure, we must * create a private copy for this procedure to use. Such sharing of * procedure bodies is rare but can cause problems. A procedure body - * is compiled in a context that includes the number of - * compiler-allocated "slots" for local variables. Each formal - * parameter is given a local variable slot (the + * is compiled in a context that includes the number of "slots" + * allocated by the compiler for local variables. There is a local + * variable slot for each formal parameter (the * "procPtr->numCompiledLocals = numArgs" assignment below). This * means that the same code can not be shared by two procedures that * have a different number of arguments, even if their bodies are @@ -391,8 +442,18 @@ TclCreateProc( */ if (Tcl_IsShared(bodyPtr)) { - bytes = Tcl_GetStringFromObj(bodyPtr, &length); + Tcl_Obj *sharedBodyPtr = bodyPtr; + + bytes = TclGetStringFromObj(bodyPtr, &length); bodyPtr = Tcl_NewStringObj(bytes, length); + + /* + * TIP #280. + * Ensure that the continuation line data for the original body is + * not lost and applies to the new body as well. + */ + + TclContinuationsCopy(bodyPtr, sharedBodyPtr); } /* @@ -403,11 +464,11 @@ TclCreateProc( Tcl_IncrRefCount(bodyPtr); - procPtr = (Proc *) ckalloc(sizeof(Proc)); + procPtr = ckalloc(sizeof(Proc)); procPtr->iPtr = iPtr; procPtr->refCount = 1; procPtr->bodyPtr = bodyPtr; - procPtr->numArgs = 0; /* actual argument count is set below. */ + procPtr->numArgs = 0; /* Actual argument count is set below. */ procPtr->numCompiledLocals = 0; procPtr->firstLocalPtr = NULL; procPtr->lastLocalPtr = NULL; @@ -422,7 +483,7 @@ TclCreateProc( * THIS FAILS IF THE ARG LIST OBJECT'S STRING REP CONTAINS NULS. */ - args = Tcl_GetStringFromObj(argsPtr, &length); + args = TclGetStringFromObj(argsPtr, &length); result = Tcl_SplitList(interp, args, &numArgs, &argArray); if (result != TCL_OK) { goto procError; @@ -434,6 +495,8 @@ TclCreateProc( "procedure \"%s\": arg list contains %d entries, " "precompiled header expects %d", procName, numArgs, procPtr->numArgs)); + Tcl_SetErrorCode(interp, "TCL", "OPERATION", "PROC", + "BYTECODELIES", NULL); goto procError; } localPtr = procPtr->firstLocalPtr; @@ -444,7 +507,7 @@ TclCreateProc( for (i = 0; i < numArgs; i++) { int fieldCount, nameLength, valueLength; - CONST char **fieldValues; + const char **fieldValues; /* * Now divide the specifier up into name and default. @@ -456,15 +519,20 @@ TclCreateProc( goto procError; } if (fieldCount > 2) { - ckfree((char *) fieldValues); - Tcl_AppendResult(interp, - "too many fields in argument specifier \"", - argArray[i], "\"", NULL); + ckfree(fieldValues); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "too many fields in argument specifier \"%s\"", + argArray[i])); + Tcl_SetErrorCode(interp, "TCL", "OPERATION", "PROC", + "FORMALARGUMENTFORMAT", NULL); goto procError; } if ((fieldCount == 0) || (*fieldValues[0] == 0)) { - ckfree((char *) fieldValues); - Tcl_AppendResult(interp, "argument with no name", NULL); + ckfree(fieldValues); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "argument with no name", -1)); + Tcl_SetErrorCode(interp, "TCL", "OPERATION", "PROC", + "FORMALARGUMENTFORMAT", NULL); goto procError; } @@ -482,23 +550,27 @@ TclCreateProc( p = fieldValues[0]; while (*p != '\0') { if (*p == '(') { - CONST char *q = p; + const char *q = p; do { q++; } while (*q != '\0'); q--; - if (*q == ')') { /* we have an array element */ - Tcl_AppendResult(interp, "formal parameter \"", - fieldValues[0], - "\" is an array element", NULL); - ckfree((char *) fieldValues); + if (*q == ')') { /* We have an array element. */ + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "formal parameter \"%s\" is an array element", + fieldValues[0])); + ckfree(fieldValues); + Tcl_SetErrorCode(interp, "TCL", "OPERATION", "PROC", + "FORMALARGUMENTFORMAT", NULL); goto procError; } } else if ((*p == ':') && (*(p+1) == ':')) { - Tcl_AppendResult(interp, "formal parameter \"", - fieldValues[0], - "\" is not a simple name", NULL); - ckfree((char *) fieldValues); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "formal parameter \"%s\" is not a simple name", + fieldValues[0])); + ckfree(fieldValues); + Tcl_SetErrorCode(interp, "TCL", "OPERATION", "PROC", + "FORMALARGUMENTFORMAT", NULL); goto procError; } p++; @@ -506,41 +578,49 @@ TclCreateProc( if (precompiled) { /* - * Compare the parsed argument with the stored one. For the flags, - * 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 + * Compare the parsed argument with the stored one. Note that the + * only flag value that makes sense at this point is VAR_ARGUMENT + * (its value was kept the same as pre VarReform to simplify + * tbcload's processing of older byetcodes). + * + * The only other flag vlaue that is important to retrieve from + * precompiled procs is VAR_TEMPORARY (also unchanged). It is + * needed later when retrieving the variable names. */ if ((localPtr->nameLength != nameLength) || (strcmp(localPtr->name, fieldValues[0])) || (localPtr->frameIndex != i) - || ((localPtr->flags & ~VAR_UNDEFINED) - != (VAR_SCALAR | VAR_ARGUMENT)) + || !(localPtr->flags & VAR_ARGUMENT) || (localPtr->defValuePtr == NULL && fieldCount == 2) || (localPtr->defValuePtr != NULL && fieldCount != 2)) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "procedure \"%s\": formal parameter %d is " "inconsistent with precompiled body", procName, i)); - ckfree((char *) fieldValues); + ckfree(fieldValues); + Tcl_SetErrorCode(interp, "TCL", "OPERATION", "PROC", + "BYTECODELIES", NULL); goto procError; } /* - * compare the default value if any + * Compare the default value if any. */ if (localPtr->defValuePtr != NULL) { int tmpLength; - char *tmpPtr = Tcl_GetStringFromObj(localPtr->defValuePtr, + const char *tmpPtr = TclGetStringFromObj(localPtr->defValuePtr, &tmpLength); + if ((valueLength != tmpLength) || strncmp(fieldValues[1], tmpPtr, (size_t) tmpLength)) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "procedure \"%s\": formal parameter \"%s\" has " "default value inconsistent with precompiled body", procName, fieldValues[0])); - ckfree((char *) fieldValues); + ckfree(fieldValues); + Tcl_SetErrorCode(interp, "TCL", "OPERATION", "PROC", + "BYTECODELIES", NULL); goto procError; } } @@ -558,9 +638,7 @@ TclCreateProc( * local variables for the argument. */ - localPtr = (CompiledLocal *) ckalloc((unsigned) - (sizeof(CompiledLocal) - sizeof(localPtr->name) - + nameLength + 1)); + localPtr = ckalloc(TclOffset(CompiledLocal, name) + nameLength+1); if (procPtr->firstLocalPtr == NULL) { procPtr->firstLocalPtr = procPtr->lastLocalPtr = localPtr; } else { @@ -570,7 +648,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) { @@ -580,7 +658,7 @@ TclCreateProc( } else { localPtr->defValuePtr = NULL; } - strcpy(localPtr->name, fieldValues[0]); + memcpy(localPtr->name, fieldValues[0], nameLength + 1); if ((i == numArgs - 1) && (localPtr->nameLength == 4) && (localPtr->name[0] == 'a') @@ -589,11 +667,11 @@ TclCreateProc( } } - ckfree((char *) fieldValues); + ckfree(fieldValues); } *procPtrPtr = procPtr; - ckfree((char *) argArray); + ckfree(argArray); return TCL_OK; procError: @@ -610,12 +688,12 @@ TclCreateProc( Tcl_DecrRefCount(defPtr); } - ckfree((char *) localPtr); + ckfree(localPtr); } - ckfree((char *) procPtr); + ckfree(procPtr); } if (argArray != NULL) { - ckfree((char *) argArray); + ckfree(argArray); } return TCL_ERROR; } @@ -648,7 +726,7 @@ TclCreateProc( int TclGetFrame( Tcl_Interp *interp, /* Interpreter in which to find frame. */ - CONST char *name, /* String describing frame. */ + const char *name, /* String describing frame. */ CallFrame **framePtrPtr) /* Store pointer to frame here (or NULL if * global frame indicated). */ { @@ -681,7 +759,7 @@ TclGetFrame( */ for (framePtr = iPtr->varFramePtr; framePtr != NULL; - framePtr = framePtr->callerVarPtr) { + framePtr = framePtr->callerVarPtr) { if (framePtr->level == level) { break; } @@ -689,13 +767,13 @@ TclGetFrame( if (framePtr == NULL) { goto levelError; } - + *framePtrPtr = framePtr; return result; levelError: - Tcl_ResetResult(interp); - Tcl_AppendResult(interp, "bad level \"", name, "\"", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf("bad level \"%s\"", name)); + Tcl_SetErrorCode(interp, "TCL", "VALUE", "STACKLEVEL", NULL); return -1; } @@ -734,7 +812,7 @@ TclObjGetFrame( register Interp *iPtr = (Interp *) interp; int curLevel, level, result; CallFrame *framePtr; - CONST char *name = TclGetString(objPtr); + const char *name; /* * Parse object to figure out which level number to go to. @@ -742,65 +820,71 @@ TclObjGetFrame( result = 1; curLevel = iPtr->varFramePtr->level; + if (objPtr == NULL) { + name = "1"; + goto haveLevel1; + } + + name = TclGetString(objPtr); if (objPtr->typePtr == &levelReferenceType) { - if (PTR2INT(objPtr->internalRep.twoPtrValue.ptr1)) { - level = curLevel - PTR2INT(objPtr->internalRep.twoPtrValue.ptr2); + if (objPtr->internalRep.ptrAndLongRep.ptr != NULL) { + level = curLevel - objPtr->internalRep.ptrAndLongRep.value; } else { - level = PTR2INT(objPtr->internalRep.twoPtrValue.ptr2); + level = objPtr->internalRep.ptrAndLongRep.value; } if (level < 0) { goto levelError; } /* TODO: Consider skipping the typePtr checks */ } else if (objPtr->typePtr == &tclIntType -#ifndef NO_WIDE_TYPE +#ifndef TCL_WIDE_INT_IS_LONG || objPtr->typePtr == &tclWideIntType #endif ) { - if (Tcl_GetIntFromObj(NULL, objPtr, &level) != TCL_OK || level < 0) { + if (TclGetIntFromObj(NULL, objPtr, &level) != TCL_OK || level < 0) { goto levelError; } level = curLevel - level; - } else { - if (*name == '#') { - if (Tcl_GetInt(interp, name+1, &level) != TCL_OK || level < 0) { - goto levelError; - } + } else if (*name == '#') { + if (Tcl_GetInt(interp, name+1, &level) != TCL_OK || level < 0) { + goto levelError; + } - /* - * Cache for future reference. - * - * TODO: Use the new ptrAndLongRep intrep - */ + /* + * Cache for future reference. + * + * TODO: Use the new ptrAndLongRep intrep + */ - TclFreeIntRep(objPtr); - objPtr->typePtr = &levelReferenceType; - objPtr->internalRep.twoPtrValue.ptr1 = (void *) 0; - objPtr->internalRep.twoPtrValue.ptr2 = INT2PTR(level); - } else if (isdigit(UCHAR(*name))) { /* INTL: digit */ - if (Tcl_GetInt(interp, name, &level) != TCL_OK) { - return -1; - } + TclFreeIntRep(objPtr); + objPtr->typePtr = &levelReferenceType; + objPtr->internalRep.ptrAndLongRep.ptr = NULL; + objPtr->internalRep.ptrAndLongRep.value = level; + } else if (isdigit(UCHAR(*name))) { /* INTL: digit */ + if (Tcl_GetInt(interp, name, &level) != TCL_OK) { + return -1; + } - /* - * Cache for future reference. - * - * TODO: Use the new ptrAndLongRep intrep - */ + /* + * Cache for future reference. + * + * TODO: Use the new ptrAndLongRep intrep + */ - TclFreeIntRep(objPtr); - objPtr->typePtr = &levelReferenceType; - objPtr->internalRep.twoPtrValue.ptr1 = (void *) 1; - objPtr->internalRep.twoPtrValue.ptr2 = INT2PTR(level); - level = curLevel - level; - } else { - /* - * Don't cache as the object *isn't* a level reference. - */ + TclFreeIntRep(objPtr); + objPtr->typePtr = &levelReferenceType; + objPtr->internalRep.ptrAndLongRep.ptr = (void *) 1; /* non-NULL */ + objPtr->internalRep.ptrAndLongRep.value = level; + level = curLevel - level; + } else { + /* + * Don't cache as the object *isn't* a level reference (might even be + * NULL...) + */ - level = curLevel - 1; - result = 0; - } + haveLevel1: + level = curLevel - 1; + result = 0; } /* @@ -808,7 +892,7 @@ TclObjGetFrame( */ for (framePtr = iPtr->varFramePtr; framePtr != NULL; - framePtr = framePtr->callerVarPtr) { + framePtr = framePtr->callerVarPtr) { if (framePtr->level == level) { break; } @@ -820,8 +904,8 @@ TclObjGetFrame( return result; levelError: - Tcl_ResetResult(interp); - Tcl_AppendResult(interp, "bad level \"", name, "\"", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf("bad level \"%s\"", name)); + Tcl_SetErrorCode(interp, "TCL", "VALUE", "STACKLEVEL", NULL); return -1; } @@ -842,17 +926,52 @@ TclObjGetFrame( *---------------------------------------------------------------------- */ +static int +Uplevel_Callback( + ClientData data[], + Tcl_Interp *interp, + int result) +{ + CallFrame *savedVarFramePtr = data[0]; + + if (result == TCL_ERROR) { + Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( + "\n (\"uplevel\" body line %d)", Tcl_GetErrorLine(interp))); + } + + /* + * Restore the variable frame, and return. + */ + + ((Interp *)interp)->varFramePtr = savedVarFramePtr; + return result; +} + /* ARGSUSED */ int Tcl_UplevelObjCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + return Tcl_NRCallObjProc(interp, TclNRUplevelObjCmd, dummy, objc, objv); +} + +int +TclNRUplevelObjCmd( + ClientData dummy, /* Not used. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { + register Interp *iPtr = (Interp *) interp; + CmdFrame *invoker = NULL; + int word = 0; int result; CallFrame *savedVarFramePtr, *framePtr; + Tcl_Obj *objPtr; if (objc < 2) { uplevelSyntax: @@ -868,11 +987,11 @@ Tcl_UplevelObjCmd( if (result == -1) { return TCL_ERROR; } - objc -= (result+1); + objc -= result + 1; if (objc == 0) { goto uplevelSyntax; } - objv += (result+1); + objv += result + 1; /* * Modify the interpreter state to execute in the given frame. @@ -886,7 +1005,13 @@ Tcl_UplevelObjCmd( */ if (objc == 1) { - result = Tcl_EvalObjEx(interp, objv[0], TCL_EVAL_DIRECT); + /* + * TIP #280. Make actual argument location available to eval'd script + */ + + TclArgumentGet(interp, objv[0], &invoker, &word); + objPtr = objv[0]; + } else { /* * More than one argument: concatenate them together with spaces @@ -894,22 +1019,12 @@ Tcl_UplevelObjCmd( * object when it decrements its refcount after eval'ing it. */ - Tcl_Obj *objPtr; - objPtr = Tcl_ConcatObj(objc, objv); - result = Tcl_EvalObjEx(interp, objPtr, TCL_EVAL_DIRECT); - } - if (result == TCL_ERROR) { - Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( - "\n (\"uplevel\" body line %d)", interp->errorLine)); } - /* - * Restore the variable frame, and return. - */ - - iPtr->varFramePtr = savedVarFramePtr; - return result; + TclNRAddCallback(interp, Uplevel_Callback, savedVarFramePtr, NULL, NULL, + NULL); + return TclNREvalObjEx(interp, objPtr, 0, invoker, word); } /* @@ -938,10 +1053,9 @@ Tcl_UplevelObjCmd( Proc * TclFindProc( Interp *iPtr, /* Interpreter in which to look. */ - CONST char *procName) /* Name of desired procedure. */ + const char *procName) /* Name of desired procedure. */ { Tcl_Command cmd; - Tcl_Command origCmd; Command *cmdPtr; cmd = Tcl_FindCommand((Tcl_Interp *) iPtr, procName, NULL, /*flags*/ 0); @@ -950,14 +1064,7 @@ TclFindProc( } cmdPtr = (Command *) cmd; - origCmd = TclGetOriginalCommand(cmd); - if (origCmd != NULL) { - cmdPtr = (Command *) origCmd; - } - if (cmdPtr->objProc != TclObjInterpProc) { - return NULL; - } - return (Proc *) cmdPtr->objClientData; + return TclIsProc(cmdPtr); } /* @@ -982,146 +1089,77 @@ Proc * TclIsProc( Command *cmdPtr) /* Command to test. */ { - Tcl_Command origCmd; + Tcl_Command origCmd = TclGetOriginalCommand((Tcl_Command) cmdPtr); - origCmd = TclGetOriginalCommand((Tcl_Command) cmdPtr); if (origCmd != NULL) { cmdPtr = (Command *) origCmd; } - if (cmdPtr->objProc == TclObjInterpProc) { - return (Proc *) cmdPtr->objClientData; + if (cmdPtr->deleteProc == TclProcDeleteProc) { + return cmdPtr->objClientData; } - return (Proc *) 0; + return NULL; } -/* - *---------------------------------------------------------------------- - * - * InitCompiledLocals -- - * - * This routine is invoked in order to initialize the compiled locals - * table for a new call frame. - * - * Results: - * None. - * - * Side effects: - * May invoke various name resolvers in order to determine which - * variables are being referenced at runtime. - * - *---------------------------------------------------------------------- - */ - -static void -InitCompiledLocals( - Tcl_Interp *interp, /* Current interpreter. */ - ByteCode *codePtr, - CompiledLocal *localPtr, - Var *varPtr, - Namespace *nsPtr) /* Pointer to current namespace. */ +static int +ProcWrongNumArgs( + Tcl_Interp *interp, + int skip) { - Interp *iPtr = (Interp*) interp; - int haveResolvers = (nsPtr->compiledVarResProc || iPtr->resolverPtr); - CompiledLocal *firstLocalPtr; + CallFrame *framePtr = ((Interp *)interp)->varFramePtr; + register Proc *procPtr = framePtr->procPtr; + register Var *defPtr; + int localCt = procPtr->numCompiledLocals, numArgs, i; + Tcl_Obj **desiredObjs; + const char *final = NULL; - if (codePtr->flags & TCL_BYTECODE_RESOLVE_VARS) { - /* - * This is the first run after a recompile, or else the resolver epoch - * has changed: update the resolver cache. - */ + /* + * Build up desired argument list for Tcl_WrongNumArgs + */ - firstLocalPtr = localPtr; - for (; localPtr != NULL; localPtr = localPtr->nextPtr) { + numArgs = framePtr->procPtr->numArgs; + desiredObjs = TclStackAlloc(interp, + (int) sizeof(Tcl_Obj *) * (numArgs+1)); - if (localPtr->resolveInfo) { - if (localPtr->resolveInfo->deleteProc) { - localPtr->resolveInfo->deleteProc(localPtr->resolveInfo); - } else { - ckfree((char*)localPtr->resolveInfo); - } - localPtr->resolveInfo = NULL; - } - localPtr->flags &= ~VAR_RESOLVED; + if (framePtr->isProcCallFrame & FRAME_IS_LAMBDA) { + desiredObjs[0] = Tcl_NewStringObj("lambdaExpr", -1); + } else { + ((Interp *) interp)->ensembleRewrite.numInsertedObjs -= skip - 1; - if (haveResolvers && - !(localPtr->flags & (VAR_ARGUMENT|VAR_TEMPORARY))) { - ResolverScheme *resPtr = iPtr->resolverPtr; - Tcl_ResolvedVarInfo *vinfo; - int result; +#ifdef AVOID_HACKS_FOR_ITCL + desiredObjs[0] = framePtr->objv[skip-1]; +#else + desiredObjs[0] = Tcl_NewListObj(skip, framePtr->objv); +#endif /* AVOID_HACKS_FOR_ITCL */ + } + Tcl_IncrRefCount(desiredObjs[0]); - if (nsPtr->compiledVarResProc) { - result = (*nsPtr->compiledVarResProc)(nsPtr->interp, - localPtr->name, localPtr->nameLength, - (Tcl_Namespace *) nsPtr, &vinfo); - } else { - result = TCL_CONTINUE; - } + defPtr = (Var *) (&framePtr->localCachePtr->varName0 + localCt); + for (i=1 ; i<=numArgs ; i++, defPtr++) { + Tcl_Obj *argObj; + Tcl_Obj *namePtr = localName(framePtr, i-1); - while ((result == TCL_CONTINUE) && resPtr) { - if (resPtr->compiledVarResProc) { - result = (*resPtr->compiledVarResProc)(nsPtr->interp, - localPtr->name, localPtr->nameLength, - (Tcl_Namespace *) nsPtr, &vinfo); - } - resPtr = resPtr->nextPtr; - } - if (result == TCL_OK) { - localPtr->resolveInfo = vinfo; - localPtr->flags |= VAR_RESOLVED; - } - } + if (defPtr->value.objPtr != NULL) { + TclNewObj(argObj); + Tcl_AppendStringsToObj(argObj, "?", TclGetString(namePtr), "?", NULL); + } else if (defPtr->flags & VAR_IS_ARGS) { + numArgs--; + final = "?arg ...?"; + break; + } else { + argObj = namePtr; + Tcl_IncrRefCount(namePtr); } - localPtr = firstLocalPtr; - codePtr->flags &= ~TCL_BYTECODE_RESOLVE_VARS; + desiredObjs[i] = argObj; } - /* - * Initialize the array of local variables stored in the call frame. Some - * variables may have special resolution rules. In that case, we call - * their "resolver" procs to get our hands on the variable, and we make - * the compiled local a link to the real variable. - */ - - if (haveResolvers) { - 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; - - /* - * Now invoke the resolvers to determine the exact variables that - * should be used. - */ + Tcl_ResetResult(interp); + Tcl_WrongNumArgs(interp, numArgs+1, desiredObjs, final); - resVarInfo = localPtr->resolveInfo; - if (resVarInfo && resVarInfo->fetchProc) { - Var *resolvedVarPtr = (Var*) (*resVarInfo->fetchProc)(interp, - resVarInfo); - if (resolvedVarPtr) { - resolvedVarPtr->refCount++; - varPtr->value.linkPtr = resolvedVarPtr; - varPtr->flags = VAR_LINK; - } - } - } - } else { - 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; - } + for (i=0 ; i<=numArgs ; i++) { + Tcl_DecrRefCount(desiredObjs[i]); } + TclStackFree(interp, desiredObjs); + return TCL_ERROR; } /* @@ -1154,155 +1192,281 @@ TclInitCompiledLocals( Var *varPtr = framePtr->compiledLocals; Tcl_Obj *bodyPtr; ByteCode *codePtr; - CompiledLocal *localPtr = framePtr->procPtr->firstLocalPtr; 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; + codePtr = bodyPtr->internalRep.twoPtrValue.ptr1; - InitCompiledLocals(interp, codePtr, localPtr, varPtr, nsPtr); + if (framePtr->numCompiledLocals) { + if (!codePtr->localCachePtr) { + InitLocalCache(framePtr->procPtr) ; + } + framePtr->localCachePtr = codePtr->localCachePtr; + framePtr->localCachePtr->refCount++; + } + + InitResolvedLocals(interp, codePtr, varPtr, nsPtr); } /* *---------------------------------------------------------------------- * - * TclObjInterpProc, ObjInterpProcEx -- + * InitResolvedLocals -- * - * When a Tcl procedure gets invoked during bytecode evaluation, this - * object-based routine gets invoked to interpret the procedure. + * This routine is invoked in order to initialize the compiled locals + * table for a new call frame. * * Results: - * A standard Tcl object result value. + * None. * * Side effects: - * Depends on the commands in the procedure. + * May invoke various name resolvers in order to determine which + * variables are being referenced at runtime. * *---------------------------------------------------------------------- */ -int -TclObjInterpProc( - ClientData clientData, /* Record describing procedure to be - * interpreted. */ - register Tcl_Interp *interp,/* Interpreter in which procedure was - * invoked. */ - int objc, /* Count of number of arguments to this - * procedure. */ - Tcl_Obj *CONST objv[]) /* Argument value objects. */ +static void +InitResolvedLocals( + Tcl_Interp *interp, /* Current interpreter. */ + ByteCode *codePtr, + Var *varPtr, + Namespace *nsPtr) /* Pointer to current namespace. */ { - return ObjInterpProcEx(clientData, interp, objc, objv, /*isLambda*/ 0, - &MakeProcError); -} + Interp *iPtr = (Interp *) interp; + int haveResolvers = (nsPtr->compiledVarResProc || iPtr->resolverPtr); + CompiledLocal *firstLocalPtr, *localPtr; + int varNum; + Tcl_ResolvedVarInfo *resVarInfo; -static int -ObjInterpProcEx( - ClientData clientData, /* Record describing procedure to be - * interpreted. */ - register Tcl_Interp *interp,/* Interpreter in which procedure was - * invoked. */ - int objc, /* Count of number of arguments to this - * procedure. */ - Tcl_Obj *CONST objv[], /* Argument value objects. */ - int isLambda, /* 1 if this is a call by ApplyObjCmd: it - * needs special rules for error msg */ - ProcErrorProc errorProc) /* How to convert results from the script into - * results of the overall procedure. */ -{ - Proc *procPtr = (Proc *) clientData; - Namespace *nsPtr = procPtr->cmdPtr->nsPtr; - CallFrame *framePtr, **framePtrPtr; - int result; + /* + * Find the localPtr corresponding to varPtr + */ + + varNum = varPtr - iPtr->framePtr->compiledLocals; + localPtr = iPtr->framePtr->procPtr->firstLocalPtr; + while (varNum--) { + localPtr = localPtr->nextPtr; + } + + if (!(haveResolvers && (codePtr->flags & TCL_BYTECODE_RESOLVE_VARS))) { + goto doInitResolvedLocals; + } /* - * If necessary, compile the procedure's body. The compiler will allocate - * frame slots for the procedure's non-argument local variables. Note that - * compiling the body might increase procPtr->numCompiledLocals if new - * local variables are found while compiling. + * This is the first run after a recompile, or else the resolver epoch + * has changed: update the resolver cache. */ - result = ProcCompileProc(interp, procPtr, procPtr->bodyPtr, nsPtr, - (isLambda ? "body of lambda term" : "body of proc"), - TclGetString(objv[isLambda]), &procPtr); + firstLocalPtr = localPtr; + for (; localPtr != NULL; localPtr = localPtr->nextPtr) { + if (localPtr->resolveInfo) { + if (localPtr->resolveInfo->deleteProc) { + localPtr->resolveInfo->deleteProc(localPtr->resolveInfo); + } else { + ckfree(localPtr->resolveInfo); + } + localPtr->resolveInfo = NULL; + } + localPtr->flags &= ~VAR_RESOLVED; + + if (haveResolvers && + !(localPtr->flags & (VAR_ARGUMENT|VAR_TEMPORARY))) { + ResolverScheme *resPtr = iPtr->resolverPtr; + Tcl_ResolvedVarInfo *vinfo; + int result; + + if (nsPtr->compiledVarResProc) { + result = nsPtr->compiledVarResProc(nsPtr->interp, + localPtr->name, localPtr->nameLength, + (Tcl_Namespace *) nsPtr, &vinfo); + } else { + result = TCL_CONTINUE; + } - if (result != TCL_OK) { - return result; + while ((result == TCL_CONTINUE) && resPtr) { + if (resPtr->compiledVarResProc) { + result = resPtr->compiledVarResProc(nsPtr->interp, + localPtr->name, localPtr->nameLength, + (Tcl_Namespace *) nsPtr, &vinfo); + } + resPtr = resPtr->nextPtr; + } + if (result == TCL_OK) { + localPtr->resolveInfo = vinfo; + localPtr->flags |= VAR_RESOLVED; + } + } } + localPtr = firstLocalPtr; + codePtr->flags &= ~TCL_BYTECODE_RESOLVE_VARS; /* - * Set up and push a new call frame for the new procedure invocation. - * This call frame will execute in the proc's namespace, which might be - * different than the current namespace. The proc's namespace is that of - * its command, which can change if the command is renamed from one - * namespace to another. + * Initialize the array of local variables stored in the call frame. Some + * variables may have special resolution rules. In that case, we call + * their "resolver" procs to get our hands on the variable, and we make + * the compiled local a link to the real variable. */ - framePtrPtr = &framePtr; - result = TclPushStackFrame(interp, (Tcl_CallFrame **) framePtrPtr, - (Tcl_Namespace *) nsPtr, FRAME_IS_PROC); + doInitResolvedLocals: + for (; localPtr != NULL; varPtr++, localPtr = localPtr->nextPtr) { + varPtr->flags = 0; + varPtr->value.objPtr = NULL; - if (result != TCL_OK) { - return result; + /* + * Now invoke the resolvers to determine the exact variables that + * should be used. + */ + + resVarInfo = localPtr->resolveInfo; + if (resVarInfo && resVarInfo->fetchProc) { + register Var *resolvedVarPtr = (Var *) + resVarInfo->fetchProc(interp, resVarInfo); + + if (resolvedVarPtr) { + if (TclIsVarInHash(resolvedVarPtr)) { + VarHashRefCount(resolvedVarPtr)++; + } + varPtr->flags = VAR_LINK; + varPtr->value.linkPtr = resolvedVarPtr; + } + } } +} + +void +TclFreeLocalCache( + Tcl_Interp *interp, + LocalCache *localCachePtr) +{ + int i; + Tcl_Obj **namePtrPtr = &localCachePtr->varName0; - framePtr->objc = objc; - framePtr->objv = objv; - framePtr->procPtr = procPtr; + for (i = 0; i < localCachePtr->numVars; i++, namePtrPtr++) { + register Tcl_Obj *objPtr = *namePtrPtr; - return TclObjInterpProcCore(interp, framePtr, objv[isLambda], isLambda, - isLambda+1, errorProc); + if (objPtr) { + /* TclReleaseLiteral calls Tcl_DecrRefCount for us */ + TclReleaseLiteral(interp, objPtr); + } + } + ckfree(localCachePtr); +} + +static void +InitLocalCache( + Proc *procPtr) +{ + Interp *iPtr = procPtr->iPtr; + ByteCode *codePtr = procPtr->bodyPtr->internalRep.twoPtrValue.ptr1; + int localCt = procPtr->numCompiledLocals; + int numArgs = procPtr->numArgs, i = 0; + + Tcl_Obj **namePtr; + Var *varPtr; + LocalCache *localCachePtr; + CompiledLocal *localPtr; + 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 = ckalloc(sizeof(LocalCache) + + (localCt - 1) * sizeof(Tcl_Obj *) + + numArgs * sizeof(Var)); + + namePtr = &localCachePtr->varName0; + varPtr = (Var *) (namePtr + localCt); + localPtr = 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; } /* *---------------------------------------------------------------------- * - * TclObjInterpProcCore -- + * InitArgsAndLocals -- * - * When a Tcl procedure, lambda term or anything else that works like a - * procedure gets invoked during bytecode evaluation, this object-based - * routine gets invoked to interpret the body. + * This routine is invoked in order to initialize the arguments and other + * compiled locals table for a new call frame. * * Results: - * A standard Tcl object result value. + * A standard Tcl result. * * Side effects: - * Nearly anything; depends on the commands in the procedure body. + * Allocates memory on the stack for the compiled local variables, the + * caller is responsible for freeing them. Initialises all variables. May + * invoke various name resolvers in order to determine which variables + * are being referenced at runtime. * *---------------------------------------------------------------------- */ -int -TclObjInterpProcCore( +static int +InitArgsAndLocals( register Tcl_Interp *interp,/* Interpreter in which procedure was * invoked. */ - CallFrame *framePtr, /* The context to execute. The procPtr field - * must be non-NULL. */ Tcl_Obj *procNameObj, /* Procedure name for error reporting. */ - int isLambda, /* 1 if this is a call by ApplyObjCmd: it - * needs special rules for error msg. */ - int skip, /* Number of initial arguments to be skipped, + int skip) /* Number of initial arguments to be skipped, * i.e., words in the "command name". */ - ProcErrorProc errorProc) /* How to convert results from the script into - * results of the overall procedure. */ { + CallFrame *framePtr = ((Interp *)interp)->varFramePtr; register Proc *procPtr = framePtr->procPtr; - register Var *varPtr; - register CompiledLocal *localPtr; - int localCt, numArgs, argCt, i, imax, result; - Var *compiledLocals; + ByteCode *codePtr = procPtr->bodyPtr->internalRep.twoPtrValue.ptr1; + 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. */ - localCt = procPtr->numCompiledLocals; - compiledLocals = (Var*) TclStackAlloc(interp, (int)(localCt*sizeof(Var))); + varPtr = TclStackAlloc(interp, (int)(localCt * sizeof(Var))); + framePtr->compiledLocals = varPtr; framePtr->numCompiledLocals = localCt; - framePtr->compiledLocals = compiledLocals; /* * Match and assign the call's actual parameters to the procedure's formal @@ -1315,17 +1479,15 @@ TclObjInterpProcCore( 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 runProc; + goto correctArgs; } } - imax = ((argCt < numArgs - 1)? argCt : (numArgs - 1)); - for (i = 0; i < imax; i++) { + imax = ((argCt < numArgs-1) ? argCt : numArgs-1); + for (i = 0; i < imax; i++, varPtr++, defPtr ? defPtr++ : defPtr) { /* * "Normal" arguments; last formal is special, depends on it being * 'args'. @@ -1333,148 +1495,278 @@ TclObjInterpProcCore( Tcl_Obj *objPtr = argObjs[i]; + varPtr->flags = 0; 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; + Tcl_IncrRefCount(objPtr); /* Local var is a reference. */ } - for (; i < (numArgs - 1); i++) { + for (; i < numArgs-1; i++, varPtr++, defPtr ? defPtr++ : defPtr) { /* * 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 { + Tcl_Obj *objPtr = defPtr ? defPtr->value.objPtr : NULL; + + if (!objPtr) { goto incorrectArgs; } + varPtr->flags = 0; + varPtr->value.objPtr = objPtr; + Tcl_IncrRefCount(objPtr); /* Local var reference. */ } /* * When we get here, the last formal argument remains to be defined: - * localPtr and varPtr point to the last argument to be initialized. + * defPtr and varPtr point to the last argument to be initialized. */ - if (localPtr->flags & VAR_IS_ARGS) { + varPtr->flags = 0; + if (defPtr && 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 */ + 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; + Tcl_IncrRefCount(objPtr); /* Local var is a reference. */ + } else if ((argCt < numArgs) && defPtr && defPtr->value.objPtr) { + Tcl_Obj *objPtr = defPtr->value.objPtr; + varPtr->value.objPtr = objPtr; - Tcl_IncrRefCount(objPtr); /* local var is a reference */ + Tcl_IncrRefCount(objPtr); /* Local var is a reference. */ } else { - Tcl_Obj **desiredObjs; - ByteCode *codePtr; - const char *final; + goto incorrectArgs; + } + varPtr++; - /* - * Do initialise all compiled locals, to avoid problems at - * DeleteLocalVars. - */ + /* + * Initialise and resolve the remaining compiledLocals. In the absence of + * resolvers, they are undefined local vars: (flags=0, value=NULL). + */ - incorrectArgs: - final = NULL; - codePtr = procPtr->bodyPtr->internalRep.otherValuePtr; - InitCompiledLocals(interp, codePtr, localPtr, varPtr, framePtr->nsPtr); + correctArgs: + if (numArgs < localCt) { + if (!framePtr->nsPtr->compiledVarResProc + && !((Interp *)interp)->resolverPtr) { + memset(varPtr, 0, (localCt - numArgs)*sizeof(Var)); + } else { + InitResolvedLocals(interp, codePtr, varPtr, framePtr->nsPtr); + } + } - /* - * Build up desired argument list for Tcl_WrongNumArgs - */ + return TCL_OK; - desiredObjs = (Tcl_Obj **) TclStackAlloc(interp, - (int) sizeof(Tcl_Obj *) * (numArgs+1)); + /* + * Initialise all compiled locals to avoid problems at DeleteLocalVars. + */ -#ifdef AVOID_HACKS_FOR_ITCL - desiredObjs[0] = framePtr->objv[skip-1]; -#else - desiredObjs[0] = (isLambda ? framePtr->objv[skip-1] : - Tcl_NewListObj(skip, framePtr->objv)); -#endif /* AVOID_HACKS_FOR_ITCL */ + incorrectArgs: + memset(varPtr, 0, + ((framePtr->compiledLocals + localCt)-varPtr) * sizeof(Var)); + return ProcWrongNumArgs(interp, skip); +} + +/* + *---------------------------------------------------------------------- + * + * PushProcCallFrame -- + * + * Compiles a proc body if necessary, then pushes a CallFrame suitable + * for executing it. + * + * Results: + * A standard Tcl object result value. + * + * Side effects: + * The proc's body may be recompiled. A CallFrame is pushed, it will have + * to be popped by the caller. + * + *---------------------------------------------------------------------- + */ - localPtr = procPtr->firstLocalPtr; - for (i=1 ; i<=numArgs ; i++) { - Tcl_Obj *argObj; +static int +PushProcCallFrame( + ClientData clientData, /* Record describing procedure to be + * interpreted. */ + register Tcl_Interp *interp,/* Interpreter in which procedure was + * invoked. */ + int objc, /* Count of number of arguments to this + * procedure. */ + Tcl_Obj *const objv[], /* Argument value objects. */ + int isLambda) /* 1 if this is a call by ApplyObjCmd: it + * needs special rules for error msg */ +{ + Proc *procPtr = clientData; + Namespace *nsPtr = procPtr->cmdPtr->nsPtr; + CallFrame *framePtr, **framePtrPtr; + int result; + ByteCode *codePtr; - TclNewObj(argObj); - if (localPtr->defValuePtr != NULL) { - Tcl_AppendStringsToObj(argObj, "?", localPtr->name, "?", NULL); - } else if ((i==numArgs) && !strcmp(localPtr->name, "args")) { - numArgs--; - final = "..."; - } else { - Tcl_AppendStringsToObj(argObj, localPtr->name, NULL); - } - desiredObjs[i] = argObj; - localPtr = localPtr->nextPtr; - } + /* + * If necessary (i.e. if we haven't got a suitable compilation already + * cached) compile the procedure's body. The compiler will allocate frame + * slots for the procedure's non-argument local variables. Note that + * compiling the body might increase procPtr->numCompiledLocals if new + * local variables are found while compiling. + */ - Tcl_ResetResult(interp); - Tcl_WrongNumArgs(interp, numArgs+1, desiredObjs, final); - result = TCL_ERROR; + if (procPtr->bodyPtr->typePtr == &tclByteCodeType) { + Interp *iPtr = (Interp *) interp; -#ifndef AVOID_HACKS_FOR_ITCL - if (!isLambda) { - TclDecrRefCount(desiredObjs[0]); - } -#endif /* AVOID_HACKS_FOR_ITCL */ + /* + * When we've got bytecode, this is the check for validity. That is, + * the bytecode must be for the right interpreter (no cross-leaks!), + * the code must be from the current epoch (so subcommand compilation + * is up-to-date), the namespace must match (so variable handling + * is right) and the resolverEpoch must match (so that new shadowed + * commands and/or resolver changes are considered). + */ - for (i=1 ; i<=numArgs ; i++) { - TclDecrRefCount(desiredObjs[i]); + codePtr = procPtr->bodyPtr->internalRep.twoPtrValue.ptr1; + if (((Interp *) *codePtr->interpHandle != iPtr) + || (codePtr->compileEpoch != iPtr->compileEpoch) + || (codePtr->nsPtr != nsPtr) + || (codePtr->nsEpoch != nsPtr->resolverEpoch)) { + goto doCompilation; + } + } else { + doCompilation: + result = TclProcCompileProc(interp, procPtr, procPtr->bodyPtr, nsPtr, + (isLambda ? "body of lambda term" : "body of proc"), + TclGetString(objv[isLambda])); + if (result != TCL_OK) { + return result; } - TclStackFree(interp); - goto procDone; } - varPtr->name = localPtr->name; - varPtr->nsPtr = NULL; - varPtr->hPtr = NULL; - varPtr->refCount = 0; - varPtr->tracePtr = NULL; - varPtr->searchPtr = NULL; - varPtr->flags = localPtr->flags; + /* + * Set up and push a new call frame for the new procedure invocation. + * This call frame will execute in the proc's namespace, which might be + * different than the current namespace. The proc's namespace is that of + * its command, which can change if the command is renamed from one + * namespace to another. + */ - localPtr = localPtr->nextPtr; - varPtr++; + framePtrPtr = &framePtr; + result = TclPushStackFrame(interp, (Tcl_CallFrame **) framePtrPtr, + (Tcl_Namespace *) nsPtr, + (isLambda? (FRAME_IS_PROC|FRAME_IS_LAMBDA) : FRAME_IS_PROC)); + if (result != TCL_OK) { + return result; + } + + framePtr->objc = objc; + framePtr->objv = objv; + framePtr->procPtr = procPtr; + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TclObjInterpProc -- + * + * When a Tcl procedure gets invoked during bytecode evaluation, this + * object-based routine gets invoked to interpret the procedure. + * + * Results: + * A standard Tcl object result value. + * + * Side effects: + * Depends on the commands in the procedure. + * + *---------------------------------------------------------------------- + */ + +int +TclObjInterpProc( + ClientData clientData, /* Record describing procedure to be + * interpreted. */ + register Tcl_Interp *interp,/* Interpreter in which procedure was + * invoked. */ + int objc, /* Count of number of arguments to this + * procedure. */ + Tcl_Obj *const objv[]) /* Argument value objects. */ +{ /* - * Initialise and resolve the remaining compiledLocals. + * Not used much in the core; external interface for iTcl */ - runProc: - if (localPtr) { - ByteCode *codePtr = procPtr->bodyPtr->internalRep.otherValuePtr; + return Tcl_NRCallObjProc(interp, TclNRInterpProc, clientData, objc, objv); +} - InitCompiledLocals(interp, codePtr, localPtr, varPtr, framePtr->nsPtr); +int +TclNRInterpProc( + ClientData clientData, /* Record describing procedure to be + * interpreted. */ + register Tcl_Interp *interp,/* Interpreter in which procedure was + * invoked. */ + int objc, /* Count of number of arguments to this + * procedure. */ + Tcl_Obj *const objv[]) /* Argument value objects. */ +{ + int result = PushProcCallFrame(clientData, interp, objc, objv, + /*isLambda*/ 0); + + if (result != TCL_OK) { + return TCL_ERROR; + } + return TclNRInterpProcCore(interp, objv[0], 1, &MakeProcError); +} + +/* + *---------------------------------------------------------------------- + * + * TclNRInterpProcCore -- + * + * When a Tcl procedure, lambda term or anything else that works like a + * procedure gets invoked during bytecode evaluation, this object-based + * routine gets invoked to interpret the body. + * + * Results: + * A standard Tcl object result value. + * + * Side effects: + * Nearly anything; depends on the commands in the procedure body. + * + *---------------------------------------------------------------------- + */ + +int +TclNRInterpProcCore( + 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". */ + ProcErrorProc *errorProc) /* How to convert results from the script into + * results of the overall procedure. */ +{ + Interp *iPtr = (Interp *) interp; + register Proc *procPtr = iPtr->varFramePtr->procPtr; + int result; + CallFrame *freePtr; + ByteCode *codePtr; + + result = InitArgsAndLocals(interp, procNameObj, skip); + if (result != TCL_OK) { + freePtr = iPtr->framePtr; + Tcl_PopCallFrame(interp); /* Pop but do not free. */ + TclStackFree(interp, freePtr->compiledLocals); + /* Free compiledLocals. */ + TclStackFree(interp, freePtr); /* Free CallFrame. */ + return TCL_ERROR; } #if defined(TCL_COMPILE_DEBUG) if (tclTraceExec >= 1) { - if (isLambda) { + register CallFrame *framePtr = iPtr->varFramePtr; + register int i; + + if (framePtr->isProcCallFrame & FRAME_IS_LAMBDA) { fprintf(stdout, "Calling lambda "); } else { fprintf(stdout, "Calling proc "); @@ -1488,83 +1780,150 @@ TclObjInterpProcCore( } #endif /*TCL_COMPILE_DEBUG*/ +#ifdef USE_DTRACE + if (TCL_DTRACE_PROC_ARGS_ENABLED()) { + int l = iPtr->varFramePtr->isProcCallFrame & FRAME_IS_LAMBDA ? 1 : 0; + const char *a[10]; + int i; + + for (i = 0 ; i < 10 ; i++) { + a[i] = (l < iPtr->varFramePtr->objc ? + TclGetString(iPtr->varFramePtr->objv[l]) : NULL); + l++; + } + TCL_DTRACE_PROC_ARGS(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], + a[8], a[9]); + } + if (TCL_DTRACE_PROC_INFO_ENABLED() && iPtr->cmdFramePtr) { + Tcl_Obj *info = TclInfoFrame(interp, iPtr->cmdFramePtr); + const char *a[6]; int i[2]; + + TclDTraceInfo(info, a, i); + TCL_DTRACE_PROC_INFO(a[0], a[1], a[2], a[3], i[0], i[1], a[4], a[5]); + TclDecrRefCount(info); + } + if (TCL_DTRACE_PROC_ENTRY_ENABLED()) { + int l = iPtr->varFramePtr->isProcCallFrame & FRAME_IS_LAMBDA ? 1 : 0; + + TCL_DTRACE_PROC_ENTRY(l < iPtr->varFramePtr->objc ? + TclGetString(iPtr->varFramePtr->objv[l]) : NULL, + iPtr->varFramePtr->objc - l - 1, + (Tcl_Obj **)(iPtr->varFramePtr->objv + l + 1)); + } + if (TCL_DTRACE_PROC_ENTRY_ENABLED()) { + int l = iPtr->varFramePtr->isProcCallFrame & FRAME_IS_LAMBDA ? 1 : 0; + + TCL_DTRACE_PROC_ENTRY(l < iPtr->varFramePtr->objc ? + TclGetString(iPtr->varFramePtr->objv[l]) : NULL, + iPtr->varFramePtr->objc - l - 1, + (Tcl_Obj **)(iPtr->varFramePtr->objv + l + 1)); + } +#endif /* USE_DTRACE */ + /* * Invoke the commands in the procedure's body. */ procPtr->refCount++; + codePtr = procPtr->bodyPtr->internalRep.twoPtrValue.ptr1; - /* TIP #280: No need to set the invoking context here. The body has - * already been compiled, so the part of CompEvalObj using it is bypassed. - */ + TclNRAddCallback(interp, InterpProcNR2, procNameObj, errorProc, + NULL, NULL); + return TclNRExecuteByteCode(interp, codePtr); +} - result = TclCompEvalObj(interp, procPtr->bodyPtr, NULL, 0); - procPtr->refCount--; - if (procPtr->refCount <= 0) { +static int +InterpProcNR2( + ClientData data[], + Tcl_Interp *interp, + int result) +{ + Interp *iPtr = (Interp *) interp; + Proc *procPtr = iPtr->varFramePtr->procPtr; + CallFrame *freePtr; + Tcl_Obj *procNameObj = data[0]; + ProcErrorProc *errorProc = (ProcErrorProc *)data[1]; + + if (TCL_DTRACE_PROC_RETURN_ENABLED()) { + int l = iPtr->varFramePtr->isProcCallFrame & FRAME_IS_LAMBDA ? 1 : 0; + + TCL_DTRACE_PROC_RETURN(l < iPtr->varFramePtr->objc ? + TclGetString(iPtr->varFramePtr->objv[l]) : NULL, result); + } + if (--procPtr->refCount <= 0) { TclProcCleanupProc(procPtr); } + /* + * Free the stack-allocated compiled locals and CallFrame. It is important + * to pop the call frame without freeing it first: the compiledLocals + * cannot be freed before the frame is popped, as the local variables must + * be deleted. But the compiledLocals must be freed first, as they were + * allocated later on the stack. + */ + if (result != TCL_OK) { - /* - * Non-standard results are processed by passing them through quickly. - * This means they all work as exceptions, unwinding the stack quickly - * and neatly. Who knows how well they are handled by third-party code - * though... - */ + goto process; + } + + done: + if (TCL_DTRACE_PROC_RESULT_ENABLED()) { + int l = iPtr->varFramePtr->isProcCallFrame & FRAME_IS_LAMBDA ? 1 : 0; + Tcl_Obj *r = Tcl_GetObjResult(interp); - if ((result > TCL_CONTINUE) || (result < TCL_OK)) { - goto procDone; - } + TCL_DTRACE_PROC_RESULT(l < iPtr->varFramePtr->objc ? + TclGetString(iPtr->varFramePtr->objv[l]) : NULL, result, + TclGetString(r), r); + } + freePtr = iPtr->framePtr; + Tcl_PopCallFrame(interp); /* Pop but do not free. */ + TclStackFree(interp, freePtr->compiledLocals); + /* Free compiledLocals. */ + TclStackFree(interp, freePtr); /* Free CallFrame. */ + return result; + + /* + * Process any non-TCL_OK result code. + */ + + process: + switch (result) { + case TCL_RETURN: /* * If it is a 'return', do the TIP#90 processing now. */ - if (result == TCL_RETURN) { - result = TclUpdateReturnInfo((Interp *) interp); - goto procDone; - } + result = TclUpdateReturnInfo((Interp *) interp); + break; + case TCL_CONTINUE: + case TCL_BREAK: /* - * Must be an error, a 'break' or a 'continue'. It's an error to get - * to this point from a 'break' or 'continue' though, so transform to - * an error now. + * It's an error to get to this point from a 'break' or 'continue', so + * transform to an error now. */ - if (result != TCL_ERROR) { - Tcl_ResetResult(interp); - Tcl_AppendResult(interp, "invoked \"", - ((result == TCL_BREAK) ? "break" : "continue"), - "\" outside of a loop", NULL); - result = TCL_ERROR; - } + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "invoked \"%s\" outside of a loop", + ((result == TCL_BREAK) ? "break" : "continue"))); + Tcl_SetErrorCode(interp, "TCL", "RESULT", "UNEXPECTED", NULL); + result = TCL_ERROR; + + /* + * Fall through to the TCL_ERROR handling code. + */ + case TCL_ERROR: /* * Now it _must_ be an error, so we need to log it as such. This means - * filling out the error trace. + * filling out the error trace. Luckily, we just hand this off to the + * function handed to us as an argument. */ - (*errorProc)(interp, procNameObj); + errorProc(interp, procNameObj); } - - /* - * Pop and free the call frame for this procedure invocation, then free - * the compiledLocals array if malloc'ed storage was used. - */ - - procDone: - /* - * Free the stack-allocated compiled locals and CallFrame. It is important - * to pop the call frame without freeing it first: the compiledLocals - * cannot be freed before the frame is popped, as the local variables must - * be deleted. But the compiledLocals must be freed first, as they were - * allocated later on the stack. - */ - - Tcl_PopCallFrame(interp); /* pop but do not free */ - TclStackFree(interp); /* free compiledLocals */ - TclStackFree(interp); /* free CallFrame */ - return result; + goto done; } /* @@ -1592,35 +1951,15 @@ TclProcCompileProc( Tcl_Interp *interp, /* Interpreter containing procedure. */ Proc *procPtr, /* Data associated with procedure. */ Tcl_Obj *bodyPtr, /* Body of proc. (Usually procPtr->bodyPtr, - * but could be any code fragment compiled in - * the context of this procedure.) */ - Namespace *nsPtr, /* Namespace containing procedure. */ - CONST char *description, /* string describing this body of code. */ - CONST char *procName) /* Name of this procedure. */ -{ - return ProcCompileProc(interp, procPtr, bodyPtr, nsPtr, description, - procName, NULL); -} - -static int -ProcCompileProc( - Tcl_Interp *interp, /* Interpreter containing procedure. */ - Proc *procPtr, /* Data associated with procedure. */ - Tcl_Obj *bodyPtr, /* Body of proc. (Usually procPtr->bodyPtr, - * but could be any code fragment compiled in - * the context of this procedure.) */ + * but could be any code fragment compiled in + * the context of this procedure.) */ Namespace *nsPtr, /* Namespace containing procedure. */ - CONST char *description, /* string describing this body of code. */ - CONST char *procName, /* Name of this procedure. */ - Proc **procPtrPtr) /* Points to storage where a replacement - * (Proc *) value may be written. */ + const char *description, /* string describing this body of code. */ + const char *procName) /* Name of this procedure. */ { - Interp *iPtr = (Interp*)interp; - int i, result; + Interp *iPtr = (Interp *) interp; Tcl_CallFrame *framePtr; - Proc *saveProcPtr; - ByteCode *codePtr = bodyPtr->internalRep.otherValuePtr; - CompiledLocal *localPtr; + ByteCode *codePtr = bodyPtr->internalRep.twoPtrValue.ptr1; /* * If necessary, compile the procedure's body. The compiler will allocate @@ -1637,153 +1976,119 @@ ProcCompileProc( */ if (bodyPtr->typePtr == &tclByteCodeType) { - if (((Interp *) *codePtr->interpHandle != iPtr) - || (codePtr->compileEpoch != iPtr->compileEpoch) - || (codePtr->nsPtr != nsPtr)) { - if (codePtr->flags & TCL_BYTECODE_PRECOMPILED) { - if ((Interp *) *codePtr->interpHandle != iPtr) { - Tcl_AppendResult(interp, - "a precompiled script jumped interps", NULL); - return TCL_ERROR; - } - codePtr->compileEpoch = iPtr->compileEpoch; - codePtr->nsPtr = nsPtr; - } else { - bodyPtr->typePtr->freeIntRepProc(bodyPtr); - bodyPtr->typePtr = NULL; + if (((Interp *) *codePtr->interpHandle == iPtr) + && (codePtr->compileEpoch == iPtr->compileEpoch) + && (codePtr->nsPtr == nsPtr) + && (codePtr->nsEpoch == nsPtr->resolverEpoch)) { + return TCL_OK; + } + + if (codePtr->flags & TCL_BYTECODE_PRECOMPILED) { + if ((Interp *) *codePtr->interpHandle != iPtr) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "a precompiled script jumped interps", -1)); + Tcl_SetErrorCode(interp, "TCL", "OPERATION", "PROC", + "CROSSINTERPBYTECODE", NULL); + return TCL_ERROR; } - } + codePtr->compileEpoch = iPtr->compileEpoch; + codePtr->nsPtr = nsPtr; + } else { + TclFreeIntRep(bodyPtr); + } } + if (bodyPtr->typePtr != &tclByteCodeType) { + Tcl_HashEntry *hePtr; + #ifdef TCL_COMPILE_DEBUG - if (tclTraceCompile >= 1) { - /* - * Display a line summarizing the top level command we are about - * to compile. - */ + if (tclTraceCompile >= 1) { + /* + * Display a line summarizing the top level command we are about + * to compile. + */ - Tcl_Obj *message = Tcl_NewStringObj("Compiling ", -1); + Tcl_Obj *message; + TclNewLiteralStringObj(message, "Compiling "); Tcl_IncrRefCount(message); Tcl_AppendStringsToObj(message, description, " \"", NULL); Tcl_AppendLimitedToObj(message, procName, -1, 50, NULL); - fprintf(stdout, "%s\"\n", TclGetString(message)); + fprintf(stdout, "%s\"\n", TclGetString(message)); Tcl_DecrRefCount(message); - } + } #endif - /* - * Plug the current procPtr into the interpreter and coerce the code - * body to byte codes. The interpreter needs to know which proc it's - * compiling so that it can access its list of compiled locals. - * - * TRICKY NOTE: Be careful to push a call frame with the proper - * namespace context, so that the byte codes are compiled in the - * appropriate class context. - */ - - saveProcPtr = iPtr->compiledProcPtr; - - if (procPtrPtr != NULL && procPtr->refCount > 1) { - Tcl_Command token; - Tcl_CmdInfo info; - Proc *new = (Proc *) ckalloc(sizeof(Proc)); - - new->iPtr = procPtr->iPtr; - new->refCount = 1; - new->cmdPtr = procPtr->cmdPtr; - token = (Tcl_Command) new->cmdPtr; - new->bodyPtr = Tcl_DuplicateObj(bodyPtr); - bodyPtr = new->bodyPtr; - Tcl_IncrRefCount(bodyPtr); - new->numArgs = procPtr->numArgs; - - new->numCompiledLocals = new->numArgs; - new->firstLocalPtr = NULL; - new->lastLocalPtr = NULL; - localPtr = procPtr->firstLocalPtr; - for (i = 0; i < new->numArgs; i++, localPtr = localPtr->nextPtr) { - CompiledLocal *copy = (CompiledLocal *) ckalloc((unsigned) - (sizeof(CompiledLocal) -sizeof(localPtr->name) - + localPtr->nameLength + 1)); - if (new->firstLocalPtr == NULL) { - new->firstLocalPtr = new->lastLocalPtr = copy; - } else { - new->lastLocalPtr->nextPtr = copy; - new->lastLocalPtr = copy; - } - copy->nextPtr = NULL; - copy->nameLength = localPtr->nameLength; - copy->frameIndex = localPtr->frameIndex; - copy->flags = localPtr->flags; - copy->defValuePtr = localPtr->defValuePtr; - if (copy->defValuePtr) { - Tcl_IncrRefCount(copy->defValuePtr); - } - copy->resolveInfo = localPtr->resolveInfo; - strcpy(copy->name, localPtr->name); - } + /* + * Plug the current procPtr into the interpreter and coerce the code + * body to byte codes. The interpreter needs to know which proc it's + * compiling so that it can access its list of compiled locals. + * + * TRICKY NOTE: Be careful to push a call frame with the proper + * namespace context, so that the byte codes are compiled in the + * appropriate class context. + */ + + iPtr->compiledProcPtr = procPtr; + + if (procPtr->numCompiledLocals > procPtr->numArgs) { + CompiledLocal *clPtr = procPtr->firstLocalPtr; + CompiledLocal *lastPtr = NULL; + int i, numArgs = procPtr->numArgs; - /* Reset the ClientData */ - Tcl_GetCommandInfoFromToken(token, &info); - if (info.objClientData == (ClientData) procPtr) { - info.objClientData = (ClientData) new; + for (i = 0; i < numArgs; i++) { + lastPtr = clPtr; + clPtr = clPtr->nextPtr; } - if (info.clientData == (ClientData) procPtr) { - info.clientData = (ClientData) new; + + if (lastPtr) { + lastPtr->nextPtr = NULL; + } else { + procPtr->firstLocalPtr = NULL; } - if (info.deleteData == (ClientData) procPtr) { - info.deleteData = (ClientData) new; + procPtr->lastLocalPtr = lastPtr; + while (clPtr) { + CompiledLocal *toFree = clPtr; + + clPtr = clPtr->nextPtr; + if (toFree->resolveInfo) { + if (toFree->resolveInfo->deleteProc) { + toFree->resolveInfo->deleteProc(toFree->resolveInfo); + } else { + ckfree(toFree->resolveInfo); + } + } + ckfree(toFree); } - Tcl_SetCommandInfoFromToken(token, &info); - - procPtr->refCount--; - *procPtrPtr = procPtr = new; + procPtr->numCompiledLocals = procPtr->numArgs; } - iPtr->compiledProcPtr = procPtr; - result = TclPushStackFrame(interp, &framePtr, - (Tcl_Namespace *) nsPtr, /* isProcCallFrame */ 0); + TclPushStackFrame(interp, &framePtr, (Tcl_Namespace *) nsPtr, + /* isProcCallFrame */ 0); - if (result == TCL_OK) { - /* TIP #280. We get the invoking context from the cmdFrame - * which was saved by 'Tcl_ProcObjCmd' (using linePBodyPtr). - */ + /* + * TIP #280: We get the invoking context from the cmdFrame which + * was saved by 'Tcl_ProcObjCmd' (using linePBodyPtr). + */ - Tcl_HashEntry* hePtr = Tcl_FindHashEntry (iPtr->linePBodyPtr, (char *) procPtr); + hePtr = Tcl_FindHashEntry(iPtr->linePBodyPtr, (char *) procPtr); - /* Constructed saved frame has body as word 0. See Tcl_ProcObjCmd. - */ - iPtr->invokeWord = 0; - iPtr->invokeCmdFramePtr = (hePtr - ? (CmdFrame*) Tcl_GetHashValue (hePtr) - : NULL); - result = tclByteCodeType.setFromAnyProc(interp, bodyPtr); - iPtr->invokeCmdFramePtr = NULL; - TclPopStackFrame(interp); - } - - iPtr->compiledProcPtr = saveProcPtr; - - if (result != TCL_OK) { - if (result == TCL_ERROR) { - int length = strlen(procName); - int limit = 50; - int overflow = (length > limit); - - Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( - "\n (compiling %s \"%.*s%s\", line %d)", - description, (overflow ? limit : length), procName, - (overflow ? "..." : ""), interp->errorLine)); - } - return result; - } + /* + * Constructed saved frame has body as word 0. See Tcl_ProcObjCmd. + */ + + iPtr->invokeWord = 0; + iPtr->invokeCmdFramePtr = (hePtr ? Tcl_GetHashValue(hePtr) : NULL); + TclSetByteCodeFromAny(interp, bodyPtr, NULL, NULL); + iPtr->invokeCmdFramePtr = NULL; + TclPopStackFrame(interp); } else if (codePtr->nsEpoch != nsPtr->resolverEpoch) { /* * The resolver epoch has changed, but we only need to invalidate the * resolver cache. */ + codePtr->nsEpoch = nsPtr->resolverEpoch; codePtr->flags |= TCL_BYTECODE_RESOLVE_VARS; } return TCL_OK; @@ -1821,7 +2126,7 @@ MakeProcError( Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( "\n (procedure \"%.*s%s\" line %d)", (overflow ? limit : nameLen), procName, - (overflow ? "..." : ""), interp->errorLine)); + (overflow ? "..." : ""), Tcl_GetErrorLine(interp))); } /* @@ -1848,7 +2153,7 @@ void TclProcDeleteProc( ClientData clientData) /* Procedure to be deleted. */ { - Proc *procPtr = (Proc *) clientData; + Proc *procPtr = clientData; procPtr->refCount--; if (procPtr->refCount <= 0) { @@ -1881,9 +2186,9 @@ TclProcCleanupProc( Tcl_Obj *bodyPtr = procPtr->bodyPtr; Tcl_Obj *defPtr; Tcl_ResolvedVarInfo *resVarInfo; - Tcl_HashEntry* hePtr = NULL; - CmdFrame* cfPtr = NULL; - Interp* iPtr = procPtr->iPtr; + Tcl_HashEntry *hePtr = NULL; + CmdFrame *cfPtr = NULL; + Interp *iPtr = procPtr->iPtr; if (bodyPtr != NULL) { Tcl_DecrRefCount(bodyPtr); @@ -1894,9 +2199,9 @@ TclProcCleanupProc( resVarInfo = localPtr->resolveInfo; if (resVarInfo) { if (resVarInfo->deleteProc) { - (*resVarInfo->deleteProc)(resVarInfo); + resVarInfo->deleteProc(resVarInfo); } else { - ckfree((char *) resVarInfo); + ckfree(resVarInfo); } } @@ -1904,30 +2209,38 @@ TclProcCleanupProc( defPtr = localPtr->defValuePtr; Tcl_DecrRefCount(defPtr); } - ckfree((char *) localPtr); + ckfree(localPtr); localPtr = nextPtr; } - ckfree((char *) procPtr); + ckfree(procPtr); - /* TIP #280. Release the location data associated with this Proc + /* + * TIP #280: Release the location data associated with this Proc * structure, if any. The interpreter may not exist (For example for - * procbody structurues created by tbcload. + * procbody structures created by tbcload. */ - if (!iPtr) return; + if (iPtr == NULL) { + return; + } - hePtr = Tcl_FindHashEntry (iPtr->linePBodyPtr, (char *) procPtr); - if (!hePtr) return; + hePtr = Tcl_FindHashEntry(iPtr->linePBodyPtr, (char *) procPtr); + if (!hePtr) { + return; + } - cfPtr = (CmdFrame*) Tcl_GetHashValue (hePtr); + cfPtr = Tcl_GetHashValue(hePtr); - if (cfPtr->type == TCL_LOCATION_SOURCE) { - Tcl_DecrRefCount (cfPtr->data.eval.path); - cfPtr->data.eval.path = NULL; + if (cfPtr) { + if (cfPtr->type == TCL_LOCATION_SOURCE) { + Tcl_DecrRefCount(cfPtr->data.eval.path); + cfPtr->data.eval.path = NULL; + } + ckfree(cfPtr->line); + cfPtr->line = NULL; + ckfree(cfPtr); } - ckfree ((char*) cfPtr->line); cfPtr->line = NULL; - ckfree ((char*) cfPtr); - Tcl_DeleteHashEntry (hePtr); + Tcl_DeleteHashEntry(hePtr); } /* @@ -1963,9 +2276,17 @@ TclUpdateReturnInfo( if (iPtr->returnLevel == 0) { /* * Now we've reached the level to return the requested -code. + * Since iPtr->returnLevel and iPtr->returnCode have completed + * their task, we now reset them to default values so that any + * bare "return TCL_RETURN" that may follow will work [Bug 2152286]. */ code = iPtr->returnCode; + iPtr->returnLevel = 1; + iPtr->returnCode = TCL_OK; + if (code == TCL_ERROR) { + iPtr->flags |= ERR_LEGACY_COPY; + } } return code; } @@ -2005,7 +2326,7 @@ TclGetObjInterpProc(void) * reference count is 0. * * Results: - * Returns a pointer to a newly allocated Tcl_Obj, 0 on error. + * Returns a pointer to a newly allocated Tcl_Obj, NULL on error. * * Side effects: * The reference count in the ByteCode attached to the Proc is bumped up @@ -2025,11 +2346,10 @@ TclNewProcBodyObj( return NULL; } - objPtr = Tcl_NewStringObj("", 0); - + TclNewObj(objPtr); if (objPtr) { objPtr->typePtr = &tclProcBodyType; - objPtr->internalRep.otherValuePtr = procPtr; + objPtr->internalRep.twoPtrValue.ptr1 = procPtr; procPtr->refCount++; } @@ -2056,13 +2376,13 @@ TclNewProcBodyObj( static void ProcBodyDup( - Tcl_Obj *srcPtr, /* object to copy */ - Tcl_Obj *dupPtr) /* target object for the duplication */ + Tcl_Obj *srcPtr, /* Object to copy. */ + Tcl_Obj *dupPtr) /* Target object for the duplication. */ { - Proc *procPtr = srcPtr->internalRep.otherValuePtr; + Proc *procPtr = srcPtr->internalRep.twoPtrValue.ptr1; dupPtr->typePtr = &tclProcBodyType; - dupPtr->internalRep.otherValuePtr = procPtr; + dupPtr->internalRep.twoPtrValue.ptr1 = procPtr; procPtr->refCount++; } @@ -2087,12 +2407,11 @@ ProcBodyDup( static void ProcBodyFree( - Tcl_Obj *objPtr) /* the object to clean up */ + Tcl_Obj *objPtr) /* The object to clean up. */ { - Proc *procPtr = objPtr->internalRep.otherValuePtr; + Proc *procPtr = objPtr->internalRep.twoPtrValue.ptr1; - procPtr->refCount--; - if (procPtr->refCount <= 0) { + if (procPtr->refCount-- < 2) { TclProcCleanupProc(procPtr); } } @@ -2140,6 +2459,7 @@ FreeLambdaInternalRep( TclProcCleanupProc(procPtr); } TclDecrRefCount(nsObjPtr); + objPtr->typePtr = NULL; } static int @@ -2147,24 +2467,28 @@ SetLambdaFromAny( Tcl_Interp *interp, /* Used for error reporting if not NULL. */ register Tcl_Obj *objPtr) /* The object to convert. */ { - Interp* iPtr = (Interp*) interp; - char *name; - Tcl_Obj *argsPtr, *bodyPtr, *nsObjPtr, **objv, *errPtr; - int objc; + Interp *iPtr = (Interp *) interp; + const char *name; + Tcl_Obj *argsPtr, *bodyPtr, *nsObjPtr, **objv; + int isNew, objc, result; + CmdFrame *cfPtr = NULL; Proc *procPtr; - int result; + + if (interp == NULL) { + return TCL_ERROR; + } /* * Convert objPtr to list type first; if it cannot be converted, or if its * length is not 2, then it cannot be converted to lambdaType. */ - result = Tcl_ListObjGetElements(interp, objPtr, &objc, &objv); + result = TclListObjGetElements(NULL, objPtr, &objc, &objv); if ((result != TCL_OK) || ((objc != 2) && (objc != 3))) { - errPtr = Tcl_NewStringObj("can't interpret \"",-1); - Tcl_AppendObjToObj(errPtr, objPtr); - Tcl_AppendToObj(errPtr, "\" as a lambda expression", -1); - Tcl_SetObjResult(interp, errPtr); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "can't interpret \"%s\" as a lambda expression", + Tcl_GetString(objPtr))); + Tcl_SetErrorCode(interp, "TCL", "VALUE", "LAMBDA", NULL); return TCL_ERROR; } @@ -2178,8 +2502,8 @@ SetLambdaFromAny( name = TclGetString(objPtr); - if (TclCreateProc(interp, /*ignored nsPtr*/ NULL, name, argsPtr, - bodyPtr, &procPtr) != TCL_OK) { + if (TclCreateProc(interp, /*ignored nsPtr*/ NULL, name, argsPtr, bodyPtr, + &procPtr) != TCL_OK) { Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( "\n (parsing lambda expression \"%s\")", name)); return TCL_ERROR; @@ -2192,7 +2516,8 @@ SetLambdaFromAny( procPtr->cmdPtr = NULL; - /* TIP #280 Remember the line the apply body is starting on. In a Byte + /* + * TIP #280: Remember the line the apply body is starting on. In a Byte * code context we ask the engine to provide us with the necessary * information. This is for the initialization of the byte code compiler * when the body is used for the first time. @@ -2212,57 +2537,71 @@ SetLambdaFromAny( */ if (iPtr->cmdFramePtr) { - CmdFrame context = *iPtr->cmdFramePtr; + CmdFrame *contextPtr = TclStackAlloc(interp, sizeof(CmdFrame)); + + *contextPtr = *iPtr->cmdFramePtr; + if (contextPtr->type == TCL_LOCATION_BC) { + /* + * Retrieve the source context from the bytecode. This call + * accounts for the reference to the source file, if any, held in + * 'context.data.eval.path'. + */ + + TclGetSrcInfoForPc(contextPtr); + } else if (contextPtr->type == TCL_LOCATION_SOURCE) { + /* + * We created a new reference to the source file path name when we + * created 'context' above. Account for the reference. + */ + + Tcl_IncrRefCount(contextPtr->data.eval.path); - if (context.type == TCL_LOCATION_BC) { - TclGetSrcInfoForPc (&context); - /* May get path in context */ - } else if (context.type == TCL_LOCATION_SOURCE) { - /* context now holds another reference */ - Tcl_IncrRefCount (context.data.eval.path); } - /* type == TCL_LOCATION_PREBC implies that 'line' is NULL here! - * We cannot assume that 'line' is valid here, we have to check. - */ + if (contextPtr->type == TCL_LOCATION_SOURCE) { + /* + * We can record source location within a lambda only if the body + * was not created by substitution. + */ - if ((context.type == TCL_LOCATION_SOURCE) && - context.line && - (context.nline >= 2) && - (context.line [1] >= 0)) { - int new, buf [2]; - CmdFrame* cfPtr = (CmdFrame*) ckalloc (sizeof (CmdFrame)); - - /* Move from approximation (line of list cmd word) to actual - * location (line of 2nd list element) */ - TclListLines (name, context.line [1], 2, buf); - - cfPtr->level = -1; - cfPtr->type = context.type; - cfPtr->line = (int*) ckalloc (sizeof (int)); - cfPtr->line [0] = buf [1]; - cfPtr->nline = 1; - cfPtr->framePtr = NULL; - cfPtr->nextPtr = NULL; - - if (context.type == TCL_LOCATION_SOURCE) { - cfPtr->data.eval.path = context.data.eval.path; - /* Transfer of reference. The reference going away (release of - * the context) is replaced by the reference in the - * constructed cmdframe */ - } else { - cfPtr->type = TCL_LOCATION_EVAL; - cfPtr->data.eval.path = NULL; + if (contextPtr->line + && (contextPtr->nline >= 2) && (contextPtr->line[1] >= 0)) { + int buf[2]; + + /* + * Move from approximation (line of list cmd word) to actual + * location (line of 2nd list element). + */ + + cfPtr = ckalloc(sizeof(CmdFrame)); + TclListLines(objPtr, contextPtr->line[1], 2, buf, NULL); + + cfPtr->level = -1; + cfPtr->type = contextPtr->type; + cfPtr->line = ckalloc(sizeof(int)); + cfPtr->line[0] = buf[1]; + cfPtr->nline = 1; + cfPtr->framePtr = NULL; + cfPtr->nextPtr = NULL; + + cfPtr->data.eval.path = contextPtr->data.eval.path; + Tcl_IncrRefCount(cfPtr->data.eval.path); + + cfPtr->cmd = NULL; + cfPtr->len = 0; } - cfPtr->cmd.str.cmd = NULL; - cfPtr->cmd.str.len = 0; + /* + * 'contextPtr' is going out of scope. Release the reference that + * it's holding to the source file path + */ - Tcl_SetHashValue (Tcl_CreateHashEntry (iPtr->linePBodyPtr, - (char*) procPtr, &new), - cfPtr); + Tcl_DecrRefCount(contextPtr->data.eval.path); } + TclStackFree(interp, contextPtr); } + Tcl_SetHashValue(Tcl_CreateHashEntry(iPtr->linePBodyPtr, procPtr, + &isNew), cfPtr); /* * Set the namespace for this lambda: given by objv[2] understood as a @@ -2270,11 +2609,12 @@ SetLambdaFromAny( */ if (objc == 2) { - nsObjPtr = Tcl_NewStringObj("::", 2); + TclNewLiteralStringObj(nsObjPtr, "::"); } else { - char *nsName = Tcl_GetString(objv[2]); + const char *nsName = TclGetString(objv[2]); + if ((*nsName != ':') || (*(nsName+1) != ':')) { - nsObjPtr = Tcl_NewStringObj("::", 2); + TclNewLiteralStringObj(nsObjPtr, "::"); Tcl_AppendObjToObj(nsObjPtr, objv[2]); } else { nsObjPtr = objv[2]; @@ -2289,7 +2629,7 @@ SetLambdaFromAny( * conversion to lambdaType. */ - objPtr->typePtr->freeIntRepProc(objPtr); + TclFreeIntRep(objPtr); objPtr->internalRep.twoPtrValue.ptr1 = procPtr; objPtr->internalRep.twoPtrValue.ptr2 = nsObjPtr; @@ -2319,18 +2659,27 @@ Tcl_ApplyObjCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + return Tcl_NRCallObjProc(interp, TclNRApplyObjCmd, dummy, objc, objv); +} + +int +TclNRApplyObjCmd( + ClientData dummy, /* Not used. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { Interp *iPtr = (Interp *) interp; Proc *procPtr = NULL; - Tcl_Obj *lambdaPtr, *nsObjPtr, *errPtr; - int result; - Command cmd; + Tcl_Obj *lambdaPtr, *nsObjPtr; + int result, isRootEnsemble; Tcl_Namespace *nsPtr; - int isRootEnsemble; + ApplyExtraData *extraPtr; if (objc < 2) { - Tcl_WrongNumArgs(interp, 1, objv, "lambdaExpr ?arg1 arg2 ...?"); + Tcl_WrongNumArgs(interp, 1, objv, "lambdaExpr ?arg ...?"); return TCL_ERROR; } @@ -2345,18 +2694,23 @@ Tcl_ApplyObjCmd( } #define JOE_EXTENSION 0 +/* + * Note: this code is NOT FUNCTIONAL due to the NR implementation; DO NOT + * ENABLE! Leaving here as reminder to (a) TIP the suggestion, and (b) adapt + * the code. (MS) + */ + #if JOE_EXTENSION else { /* * Joe English's suggestion to allow cmdNames to function as lambdas. - * Also requires making tclCmdNameType non-static in tclObj.c */ Tcl_Obj *elemPtr; int numElem; if ((lambdaPtr->typePtr == &tclCmdNameType) || - (Tcl_ListObjGetElements(interp, lambdaPtr, &numElem, + (TclListObjGetElements(interp, lambdaPtr, &numElem, &elemPtr) == TCL_OK && numElem == 1)) { return Tcl_EvalObjv(interp, objc-1, objv+1, 0); } @@ -2371,20 +2725,6 @@ Tcl_ApplyObjCmd( procPtr = lambdaPtr->internalRep.twoPtrValue.ptr1; } - memset (&cmd, 0, sizeof(Command)); - procPtr->cmdPtr = &cmd; - - /* TIP#280 HACK ! - * - * Using cmd.clientData to remember the 'lambdaPtr' for 'info frame'. The - * InfoFrameCmd will detect this case by testing cmd.hPtr for NULL. This - * condition holds here because of the 'memset' above, and nowhere - * else. Regular commands always have a valid 'hPtr', and lambda's never. - */ - - cmd.clientData = (ClientData) lambdaPtr; - Tcl_IncrRefCount (lambdaPtr); - /* * Find the namespace where this lambda should run, and push a call frame * for that namespace. Note that TclObjInterpProc() will pop it. @@ -2393,18 +2733,29 @@ Tcl_ApplyObjCmd( nsObjPtr = lambdaPtr->internalRep.twoPtrValue.ptr2; result = TclGetNamespaceFromObj(interp, nsObjPtr, &nsPtr); if (result != TCL_OK) { - return result; - } - - if (nsPtr == NULL) { - errPtr = Tcl_NewStringObj("cannot find namespace \"",-1); - Tcl_AppendObjToObj(errPtr, nsObjPtr); - Tcl_AppendToObj(errPtr, "\"", -1); - Tcl_SetObjResult(interp, errPtr); return TCL_ERROR; } - cmd.nsPtr = (Namespace *) nsPtr; + extraPtr = TclStackAlloc(interp, sizeof(ApplyExtraData)); + memset(&extraPtr->cmd, 0, sizeof(Command)); + procPtr->cmdPtr = &extraPtr->cmd; + extraPtr->cmd.nsPtr = (Namespace *) nsPtr; + + /* + * TIP#280 (semi-)HACK! + * + * Using cmd.clientData to tell [info frame] how to render the lambdaPtr. + * The InfoFrameCmd will detect this case by testing cmd.hPtr for NULL. + * This condition holds here because of the memset() above, and nowhere + * else (in the core). Regular commands always have a valid hPtr, and + * lambda's never. + */ + + extraPtr->efi.length = 1; + extraPtr->efi.fields[0].name = "lambda"; + extraPtr->efi.fields[0].proc = NULL; + extraPtr->efi.fields[0].clientData = lambdaPtr; + extraPtr->cmd.clientData = &extraPtr->efi; isRootEnsemble = (iPtr->ensembleRewrite.sourceObjs == NULL); if (isRootEnsemble) { @@ -2414,20 +2765,30 @@ Tcl_ApplyObjCmd( } else { iPtr->ensembleRewrite.numInsertedObjs -= 1; } + extraPtr->isRootEnsemble = isRootEnsemble; - result = ObjInterpProcEx((ClientData) procPtr, interp, objc, objv, 1, - &MakeLambdaError); - - if (isRootEnsemble) { - iPtr->ensembleRewrite.sourceObjs = NULL; - iPtr->ensembleRewrite.numRemovedObjs = 0; - iPtr->ensembleRewrite.numInsertedObjs = 0; + result = PushProcCallFrame(procPtr, interp, objc, objv, 1); + if (result == TCL_OK) { + TclNRAddCallback(interp, ApplyNR2, extraPtr, NULL, NULL, NULL); + result = TclNRInterpProcCore(interp, objv[1], 2, &MakeLambdaError); } + return result; +} - /* TIP #280 Undo the reference held inside of 'cmd, see HACK above. */ - Tcl_DecrRefCount (lambdaPtr); +static int +ApplyNR2( + ClientData data[], + Tcl_Interp *interp, + int result) +{ + ApplyExtraData *extraPtr = data[0]; + + if (extraPtr->isRootEnsemble) { + ((Interp *) interp)->ensembleRewrite.sourceObjs = NULL; + } - return result; + TclStackFree(interp, extraPtr); + return result; } /* @@ -2462,7 +2823,235 @@ MakeLambdaError( Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( "\n (lambda term \"%.*s%s\" line %d)", (overflow ? limit : nameLen), procName, - (overflow ? "..." : ""), interp->errorLine)); + (overflow ? "..." : ""), Tcl_GetErrorLine(interp))); +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_DisassembleObjCmd -- + * + * Implementation of the "::tcl::unsupported::disassemble" command. This + * command is not documented, but will disassemble procedures, lambda + * terms and general scripts. Note that will compile terms if necessary + * in order to disassemble them. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_DisassembleObjCmd( + ClientData dummy, /* Not used. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + static const char *const types[] = { + "lambda", "method", "objmethod", "proc", "script", NULL + }; + enum Types { + DISAS_LAMBDA, DISAS_CLASS_METHOD, DISAS_OBJECT_METHOD, DISAS_PROC, + DISAS_SCRIPT + }; + int idx, result; + Tcl_Obj *codeObjPtr = NULL; + Proc *procPtr = NULL; + Tcl_HashEntry *hPtr; + Object *oPtr; + + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "type ..."); + return TCL_ERROR; + } + if (Tcl_GetIndexFromObj(interp, objv[1], types, "type", 0, &idx)!=TCL_OK){ + return TCL_ERROR; + } + + switch ((enum Types) idx) { + case DISAS_LAMBDA: { + Command cmd; + Tcl_Obj *nsObjPtr; + Tcl_Namespace *nsPtr; + + /* + * Compile (if uncompiled) and disassemble a lambda term. + */ + + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "lambdaTerm"); + return TCL_ERROR; + } + if (objv[2]->typePtr == &lambdaType) { + procPtr = objv[2]->internalRep.twoPtrValue.ptr1; + } + if (procPtr == NULL || procPtr->iPtr != (Interp *) interp) { + result = SetLambdaFromAny(interp, objv[2]); + if (result != TCL_OK) { + return result; + } + procPtr = objv[2]->internalRep.twoPtrValue.ptr1; + } + + memset(&cmd, 0, sizeof(Command)); + nsObjPtr = objv[2]->internalRep.twoPtrValue.ptr2; + result = TclGetNamespaceFromObj(interp, nsObjPtr, &nsPtr); + if (result != TCL_OK) { + return result; + } + cmd.nsPtr = (Namespace *) nsPtr; + procPtr->cmdPtr = &cmd; + result = PushProcCallFrame(procPtr, interp, objc, objv, 1); + if (result != TCL_OK) { + return result; + } + TclPopStackFrame(interp); + codeObjPtr = procPtr->bodyPtr; + break; + } + case DISAS_PROC: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "procName"); + return TCL_ERROR; + } + + procPtr = TclFindProc((Interp *) interp, TclGetString(objv[2])); + if (procPtr == NULL) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "\"%s\" isn't a procedure", TclGetString(objv[2]))); + Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "PROC", + TclGetString(objv[2]), NULL); + return TCL_ERROR; + } + + /* + * Compile (if uncompiled) and disassemble a procedure. + */ + + result = PushProcCallFrame(procPtr, interp, 2, objv+1, 1); + if (result != TCL_OK) { + return result; + } + TclPopStackFrame(interp); + codeObjPtr = procPtr->bodyPtr; + break; + case DISAS_SCRIPT: + /* + * Compile and disassemble a script. + */ + + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "script"); + return TCL_ERROR; + } + if ((objv[2]->typePtr != &tclByteCodeType) + && (TclSetByteCodeFromAny(interp, objv[2], NULL, NULL) != TCL_OK)) { + return TCL_ERROR; + } + codeObjPtr = objv[2]; + break; + + case DISAS_CLASS_METHOD: + if (objc != 4) { + Tcl_WrongNumArgs(interp, 2, objv, "className methodName"); + return TCL_ERROR; + } + + /* + * Look up the body of a class method. + */ + + oPtr = (Object *) Tcl_GetObjectFromObj(interp, objv[2]); + if (oPtr == NULL) { + return TCL_ERROR; + } + if (oPtr->classPtr == NULL) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "\"%s\" is not a class", TclGetString(objv[2]))); + Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "CLASS", + TclGetString(objv[2]), NULL); + return TCL_ERROR; + } + hPtr = Tcl_FindHashEntry(&oPtr->classPtr->classMethods, + (char *) objv[3]); + goto methodBody; + case DISAS_OBJECT_METHOD: + if (objc != 4) { + Tcl_WrongNumArgs(interp, 2, objv, "objectName methodName"); + return TCL_ERROR; + } + + /* + * Look up the body of an instance method. + */ + + oPtr = (Object *) Tcl_GetObjectFromObj(interp, objv[2]); + if (oPtr == NULL) { + return TCL_ERROR; + } + if (oPtr->methodsPtr == NULL) { + goto unknownMethod; + } + hPtr = Tcl_FindHashEntry(oPtr->methodsPtr, (char *) objv[3]); + + /* + * Compile (if necessary) and disassemble a method body. + */ + + methodBody: + if (hPtr == NULL) { + unknownMethod: + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "unknown method \"%s\"", TclGetString(objv[3]))); + Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "METHOD", + TclGetString(objv[3]), NULL); + return TCL_ERROR; + } + procPtr = TclOOGetProcFromMethod(Tcl_GetHashValue(hPtr)); + if (procPtr == NULL) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "body not available for this kind of method", -1)); + Tcl_SetErrorCode(interp, "TCL", "OPERATION", "DISASSEMBLE", + "METHODTYPE", NULL); + return TCL_ERROR; + } + if (procPtr->bodyPtr->typePtr != &tclByteCodeType) { + Command cmd; + + /* + * Yes, this is ugly, but we need to pass the namespace in to the + * compiler in two places. + */ + + cmd.nsPtr = (Namespace *) oPtr->namespacePtr; + procPtr->cmdPtr = &cmd; + result = TclProcCompileProc(interp, procPtr, procPtr->bodyPtr, + (Namespace *) oPtr->namespacePtr, "body of method", + TclGetString(objv[3])); + procPtr->cmdPtr = NULL; + if (result != TCL_OK) { + return result; + } + } + codeObjPtr = procPtr->bodyPtr; + break; + default: + CLANG_ASSERT(0); + } + + /* + * Do the actual disassembly. + */ + + if (((ByteCode *) codeObjPtr->internalRep.twoPtrValue.ptr1)->flags + & TCL_BYTECODE_PRECOMPILED) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "may not disassemble prebuilt bytecode", -1)); + Tcl_SetErrorCode(interp, "TCL", "OPERATION", "DISASSEMBLE", + "BYTECODE", NULL); + return TCL_ERROR; + } + Tcl_SetObjResult(interp, TclDisassembleByteCodeObj(codeObjPtr)); + return TCL_OK; } /* |