diff options
Diffstat (limited to 'generic/tclProc.c')
| -rw-r--r-- | generic/tclProc.c | 2439 | 
1 files changed, 1668 insertions, 771 deletions
| diff --git a/generic/tclProc.c b/generic/tclProc.c index d8a959e..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.93 2006/10/16 20:36:19 msofer 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,27 +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 skip); +			    Tcl_Obj *const objv[], int isLambda);  static void		ProcBodyDup(Tcl_Obj *srcPtr, Tcl_Obj *dupPtr);  static void		ProcBodyFree(Tcl_Obj *objPtr); -static int		ProcessProcResultCode(Tcl_Interp *interp, -			    char *procName, int nameLen, int returnCode); +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 */ @@ -56,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  }; @@ -78,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 */ @@ -109,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; @@ -135,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;      } @@ -174,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);      /* @@ -193,6 +214,101 @@ Tcl_ProcObjCmd(      procPtr->cmdPtr = (Command *) cmd;      /* +     * 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. +     * +     * This code is nearly identical to the #280 code in SetLambdaFromAny, see +     * this file. The differences are the different index of the body in the +     * line array of the context, and the lamdba code requires some special +     * processing. Find a way to factor the common elements into a single +     * function. +     */ + +    if (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. +	     */ + +	    Tcl_IncrRefCount(contextPtr->data.eval.path); +	} + +	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 (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); +	    } + +	    /* +	     * 'contextPtr' is going out of scope; account for the reference +	     * that it's holding to the path name. +	     */ + +	    Tcl_DecrRefCount(contextPtr->data.eval.path); +	    contextPtr->data.eval.path = NULL; +	} +	TclStackFree(interp, contextPtr); +    } + +    /*       * Optimize for no-op procs: if the body is not precompiled (like a TclPro       * procbody), and the argument list is just "args" and the body is empty,       * define a compileProc to compile a no-op. @@ -219,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;  	    } @@ -231,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;  	}  	/* @@ -276,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; @@ -306,7 +421,7 @@ TclCreateProc(  	 * will be holding a reference to it.  	 */ -	procPtr = (Proc *) bodyPtr->internalRep.otherValuePtr; +	procPtr = bodyPtr->internalRep.twoPtrValue.ptr1;  	procPtr->iPtr = iPtr;  	procPtr->refCount++;  	precompiled = 1; @@ -316,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 @@ -327,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);  	}  	/* @@ -339,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; @@ -358,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; @@ -366,12 +491,12 @@ TclCreateProc(      if (precompiled) {  	if (numArgs > procPtr->numArgs) { -	    Tcl_Obj *objPtr = Tcl_NewObj(); -	    TclObjPrintf(NULL, objPtr, +	    Tcl_SetObjResult(interp, Tcl_ObjPrintf(  		    "procedure \"%s\": arg list contains %d entries, "  		    "precompiled header expects %d", procName, numArgs, -		    procPtr->numArgs); -	    Tcl_SetObjResult(interp, objPtr); +		    procPtr->numArgs)); +	    Tcl_SetErrorCode(interp, "TCL", "OPERATION", "PROC", +		    "BYTECODELIES", NULL);  	    goto procError;  	}  	localPtr = procPtr->firstLocalPtr; @@ -382,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. @@ -394,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;  	} @@ -420,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++; @@ -444,46 +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_Obj *objPtr = Tcl_NewObj(); -		TclObjPrintf(NULL, objPtr, +		Tcl_SetObjResult(interp, Tcl_ObjPrintf(  			"procedure \"%s\": formal parameter %d is " -			"inconsistent with precompiled body", procName, i); -		Tcl_SetObjResult(interp, objPtr); -		ckfree((char *) fieldValues); +			"inconsistent with precompiled body", procName, i)); +		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_Obj *objPtr = Tcl_NewObj(); - -		    TclObjPrintf(NULL, objPtr, +		    Tcl_SetObjResult(interp, Tcl_ObjPrintf(  			    "procedure \"%s\": formal parameter \"%s\" has "  			    "default value inconsistent with precompiled body", -			    procName, fieldValues[0]); -		    Tcl_SetObjResult(interp, objPtr); -		    ckfree((char *) fieldValues); +			    procName, fieldValues[0])); +		    ckfree(fieldValues); +		    Tcl_SetErrorCode(interp, "TCL", "OPERATION", "PROC", +			    "BYTECODELIES", NULL);  		    goto procError;  		}  	    } @@ -501,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 { @@ -513,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) { @@ -523,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') @@ -532,11 +667,11 @@ TclCreateProc(  	    }  	} -	ckfree((char *) fieldValues); +	ckfree(fieldValues);      }      *procPtrPtr = procPtr; -    ckfree((char *) argArray); +    ckfree(argArray);      return TCL_OK;    procError: @@ -553,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;  } @@ -591,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). */  { @@ -604,7 +739,7 @@ TclGetFrame(       */      result = 1; -    curLevel = (iPtr->varFramePtr == NULL) ? 0 : iPtr->varFramePtr->level; +    curLevel = iPtr->varFramePtr->level;      if (*name== '#') {  	if (Tcl_GetInt(interp, name+1, &level) != TCL_OK || level < 0) {  	    goto levelError; @@ -623,25 +758,22 @@ TclGetFrame(       * Figure out which frame to use, and return it to the caller.       */ -    if (level == 0) { -	framePtr = NULL; -    } else { -	for (framePtr = iPtr->varFramePtr; framePtr != NULL; -		framePtr = framePtr->callerVarPtr) { -	    if (framePtr->level == level) { -		break; -	    } -	} -	if (framePtr == NULL) { -	    goto levelError; +    for (framePtr = iPtr->varFramePtr; framePtr != NULL; +	    framePtr = framePtr->callerVarPtr) { +	if (framePtr->level == level) { +	    break;  	}      } +    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;  } @@ -680,98 +812,100 @@ 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.       */      result = 1; -    curLevel = (iPtr->varFramePtr == NULL) ? 0 : iPtr->varFramePtr->level; +    curLevel = iPtr->varFramePtr->level; +    if (objPtr == NULL) { +	name = "1"; +	goto haveLevel1; +    } + +    name = TclGetString(objPtr);      if (objPtr->typePtr == &levelReferenceType) { -	if ((int) objPtr->internalRep.twoPtrValue.ptr1) { -	    level = curLevel - (int) objPtr->internalRep.twoPtrValue.ptr2; +	if (objPtr->internalRep.ptrAndLongRep.ptr != NULL) { +	    level = curLevel - objPtr->internalRep.ptrAndLongRep.value;  	} else { -	    level = (int) 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 = (void *) 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 = (void *) 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;      }      /*       * Figure out which frame to use, and return it to the caller.       */ -    if (level == 0) { -	framePtr = NULL; -    } else { -	for (framePtr = iPtr->varFramePtr; framePtr != NULL; -		framePtr = framePtr->callerVarPtr) { -	    if (framePtr->level == level) { -		break; -	    } -	} -	if (framePtr == NULL) { -	    goto levelError; +    for (framePtr = iPtr->varFramePtr; framePtr != NULL; +	    framePtr = framePtr->callerVarPtr) { +	if (framePtr->level == level) { +	    break;  	}      } +    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;  } @@ -792,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: @@ -818,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. @@ -836,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 @@ -844,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) { -	TclFormatToErrorInfo(interp, "\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);  }  /* @@ -888,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); @@ -900,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);  }  /* @@ -932,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;  }  /* @@ -1104,111 +1192,271 @@ 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 = (ByteCode *) 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 -- + * 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. */  { +    Interp *iPtr = (Interp *) interp; +    int haveResolvers = (nsPtr->compiledVarResProc || iPtr->resolverPtr); +    CompiledLocal *firstLocalPtr, *localPtr; +    int varNum; +    Tcl_ResolvedVarInfo *resVarInfo; -    return ObjInterpProcEx(clientData, interp, objc, objv, /*skip*/ 1); -} -	 -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 skip)			/* Number of initial arguments to be skipped, -				 * ie, words in the "command name" */  -{ -    Proc *procPtr = (Proc *) clientData; -    Namespace *nsPtr = procPtr->cmdPtr->nsPtr; -    CallFrame *framePtr, **framePtrPtr; -    register Var *varPtr; -    register CompiledLocal *localPtr; -    char *procName; -    int nameLen, localCt, numArgs, argCt, i, imax, result; -    Var *compiledLocals; -    Tcl_Obj *CONST *argObjs; +    /* +     * 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; +    }      /* -     * Get the procedure's name. +     * This is the first run after a recompile, or else the resolver epoch +     * has changed: update the resolver cache.       */ -    procName = Tcl_GetStringFromObj(objv[0], &nameLen); +    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; +	    } + +	    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;      /* -     * 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. +     * 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.       */ -    result = ProcCompileProc(interp, procPtr, procPtr->bodyPtr, nsPtr, -	    "body of proc", procName, &procPtr); +  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; +    for (i = 0; i < localCachePtr->numVars; i++, namePtrPtr++) { +	register Tcl_Obj *objPtr = *namePtrPtr; + +	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;      /* -     * 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. +     * 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.       */ -    framePtrPtr = &framePtr; -    result = TclPushStackFrame(interp, (Tcl_CallFrame **) framePtrPtr, -	    (Tcl_Namespace *) nsPtr, FRAME_IS_PROC); +    localCachePtr = ckalloc(sizeof(LocalCache) +	    + (localCt - 1) * sizeof(Tcl_Obj *) +	    + numArgs * sizeof(Var)); -    if (result != TCL_OK) { -	return result; +    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; +} + +/* + *---------------------------------------------------------------------- + * + * InitArgsAndLocals -- + * + *	This routine is invoked in order to initialize the arguments and other + *	compiled locals table for a new call frame. + * + * Results: + *	A standard Tcl result. + * + * Side effects: + *	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. + * + *---------------------------------------------------------------------- + */ +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.twoPtrValue.ptr1; +    register Var *varPtr, *defPtr; +    int localCt = procPtr->numCompiledLocals, numArgs, argCt, i, imax; +    Tcl_Obj *const *argObjs; -    framePtr->objc = objc; -    framePtr->objv = objv;	/* ref counts for args are incremented below */ -    framePtr->procPtr = procPtr; +    /* +     * 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 @@ -1216,10 +1464,9 @@ ObjInterpProcEx(       * 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 @@ -1229,19 +1476,18 @@ ObjInterpProcEx(       */      numArgs = procPtr->numArgs; -    argCt = objc-skip; /* set it to the number of args to the proc */ -    argObjs = &objv[skip]; -    varPtr = framePtr->compiledLocals; -    localPtr = procPtr->firstLocalPtr; +    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 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'. @@ -1249,149 +1495,284 @@ ObjInterpProcEx(  	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) { -	Tcl_Obj *listPtr = Tcl_NewListObj(argCt-i, &(argObjs[i])); +    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, *argObj; -	ByteCode *codePtr; +	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: -	codePtr = (ByteCode *) procPtr->bodyPtr->internalRep.otherValuePtr; -	InitCompiledLocals(interp, codePtr, localPtr, varPtr, 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 **) -		ckalloc(sizeof(Tcl_Obj *) * (unsigned)(numArgs+1)); +    /* +     * Initialise all compiled locals to avoid problems at DeleteLocalVars. +     */ -#ifdef AVOID_HACKS_FOR_ITCL -	desiredObjs[0] = objv[0]; -#else -	desiredObjs[0] = Tcl_NewListObj(skip, 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++) { -	    TclNewObj(argObj); -	    if (localPtr->defValuePtr != NULL) { -		Tcl_AppendStringsToObj(argObj, "?", localPtr->name, "?", NULL); -	    } else if ((i==numArgs) && !strcmp(localPtr->name, "args")) { -		Tcl_AppendStringsToObj(argObj, "...", NULL); -	    } else { -		Tcl_AppendStringsToObj(argObj, localPtr->name, NULL); -	    } -	    desiredObjs[i] = argObj; -	    localPtr = localPtr->nextPtr; -	} +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; -	Tcl_ResetResult(interp); -	Tcl_WrongNumArgs(interp, numArgs+1, desiredObjs, NULL); -	result = TCL_ERROR; +    /* +     * 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. +     */ -#ifdef AVOID_HACKS_FOR_ITCL -	for (i=1 ; i<=numArgs ; i++) { -	    TclDecrRefCount(desiredObjs[i]); +    if (procPtr->bodyPtr->typePtr == &tclByteCodeType) { +	Interp *iPtr = (Interp *) interp; + +	/* +	 * 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). +	 */ + +	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 -	for (i=0 ; i<=numArgs ; i++) { -	    TclDecrRefCount(desiredObjs[i]); +    } 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;  	} -#endif /* AVOID_HACKS_FOR_ITCL */ -	ckfree((char *) desiredObjs); -	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 = (ByteCode *) -		procPtr->bodyPtr->internalRep.otherValuePtr; +    return Tcl_NRCallObjProc(interp, TclNRInterpProc, clientData, objc, objv); +} + +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); -	InitCompiledLocals(interp, codePtr, localPtr, varPtr, nsPtr); +    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. + * + *---------------------------------------------------------------------- + */ -    /* -     * Invoke the commands in the procedure's 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; -#ifdef TCL_COMPILE_DEBUG +    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) { -	fprintf(stdout, "Calling proc "); -	for (i = 0; i < objc; i++) { -	    TclPrintObject(stdout, objv[i], 15); +	register CallFrame *framePtr = iPtr->varFramePtr; +	register int i; + +	if (framePtr->isProcCallFrame & FRAME_IS_LAMBDA) { +	    fprintf(stdout, "Calling lambda "); +	} else { +	    fprintf(stdout, "Calling proc "); +	} +	for (i = 0; i < framePtr->objc; i++) { +	    TclPrintObject(stdout, framePtr->objv[i], 15);  	    fprintf(stdout, " ");  	}  	fprintf(stdout, "\n"); @@ -1399,38 +1780,80 @@ ObjInterpProcEx(      }  #endif /*TCL_COMPILE_DEBUG*/ -    procPtr->refCount++; -    result = TclCompEvalObj(interp, procPtr->bodyPtr); -    procPtr->refCount--; -    if (procPtr->refCount <= 0) { -	TclProcCleanupProc(procPtr); +#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]; -    if (result != TCL_OK) { -	if (skip == 1) { -	    result = ProcessProcResultCode(interp, procName, nameLen, result); -	} else { -	    /* -	     * Use a 'procName' that contains the first skip elements of objv -	     * for error reporting. This insures that we do not see just -	     * 'apply', but also the lambda expression that caused the error. -	     */ -	      -	    Tcl_Obj *namePtr; +	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; -	    namePtr = Tcl_NewListObj(skip, objv); -	    procName = Tcl_GetStringFromObj(namePtr, &nameLen); -	    result = ProcessProcResultCode(interp, procName, nameLen, result); -	    TclDecrRefCount(namePtr); -	} +	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 */      /* -     * Pop and free the call frame for this procedure invocation, then free -     * the compiledLocals array if malloc'ed storage was used. +     * Invoke the commands in the procedure's body.       */ -  procDone: +    procPtr->refCount++; +    codePtr = procPtr->bodyPtr->internalRep.twoPtrValue.ptr1; + +    TclNRAddCallback(interp, InterpProcNR2, procNameObj, errorProc, +	    NULL, NULL); +    return TclNRExecuteByteCode(interp, codePtr); +} + +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 @@ -1439,11 +1862,68 @@ ObjInterpProcEx(       * allocated later on the stack.       */ -    Tcl_PopCallFrame(interp);	/* pop but do not free */ -    TclStackFree(interp);	/* free compiledLocals */ -    TclStackFree(interp);	/* free CallFrame */ +    if (result != TCL_OK) { +	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); + +	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; -#undef NUM_LOCALS + +    /* +     * Process any non-TCL_OK result code. +     */ + +    process: +    switch (result) { +    case TCL_RETURN: +	/* +	 * If it is a 'return', do the TIP#90 processing now. +	 */ + +	result = TclUpdateReturnInfo((Interp *) interp); +	break; + +    case TCL_CONTINUE: +    case TCL_BREAK: +	/* +	 * It's an error to get to this point from a 'break' or 'continue', so +	 * transform to an error now. +	 */ + +	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. Luckily, we just hand this off to the +	 * function handed to us as an argument. +	 */ + +	errorProc(interp, procNameObj); +    } +    goto done;  }  /* @@ -1471,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.) */ +				 * 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. */ +    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.) */ -    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. */ -{ -    Interp *iPtr = (Interp*)interp; -    int i, result; +    Interp *iPtr = (Interp *) interp;      Tcl_CallFrame *framePtr; -    Proc *saveProcPtr; -    ByteCode *codePtr = (ByteCode *) bodyPtr->internalRep.otherValuePtr; -    CompiledLocal *localPtr; +    ByteCode *codePtr = bodyPtr->internalRep.twoPtrValue.ptr1;      /*       * If necessary, compile the procedure's body. The compiler will allocate @@ -1516,140 +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); -	    TclAppendLimitedToObj(message, procName, -1, 50, NULL); - 	    fprintf(stdout, "%s\"\n", TclGetString(message)); +	    Tcl_AppendLimitedToObj(message, procName, -1, 50, NULL); +	    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. +	 */ -	    /* Reset the ClientData */ -	    Tcl_GetCommandInfoFromToken(token, &info); -	    if (info.objClientData == (ClientData) procPtr) { -		info.objClientData = (ClientData) new; +	iPtr->compiledProcPtr = procPtr; + +	if (procPtr->numCompiledLocals > procPtr->numArgs) { +	    CompiledLocal *clPtr = procPtr->firstLocalPtr; +	    CompiledLocal *lastPtr = NULL; +	    int i, numArgs = procPtr->numArgs; + +	    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) { -	    result = tclByteCodeType.setFromAnyProc(interp, bodyPtr); -	    TclPopStackFrame(interp); -	} +	/* +	 * TIP #280: We get the invoking context from the cmdFrame which +	 * was saved by 'Tcl_ProcObjCmd' (using linePBodyPtr). +	 */ - 	iPtr->compiledProcPtr = saveProcPtr; +	hePtr = Tcl_FindHashEntry(iPtr->linePBodyPtr, (char *) procPtr); - 	if (result != TCL_OK) { - 	    if (result == TCL_ERROR) { -		int length = strlen(procName); -		int limit = 50; -		int overflow = (length > limit); +	/* +	 * Constructed saved frame has body as word 0. See Tcl_ProcObjCmd. +	 */ -		TclFormatToErrorInfo(interp, -			"\n    (compiling %s \"%.*s%s\", line %d)", -			description, (overflow ? limit : length), procName, -			(overflow ? "..." : ""), interp->errorLine); -	    } - 	    return result; - 	} +	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; @@ -1658,56 +2097,36 @@ ProcCompileProc(  /*   *----------------------------------------------------------------------   * - * ProcessProcResultCode -- + * MakeProcError --   * - *	Function called by TclObjInterpProc to process a return code other - *	than TCL_OK returned by a Tcl procedure. + *	Function called by TclObjInterpProc to create the stack information + *	upon an error from a procedure.   *   * Results: - *	Depending on the argument return code, the result returned is another - *	return code and the interpreter's result is set to a value to - *	supplement that return code. + *	The interpreter's error info trace is set to a value that supplements + *	the error code.   *   * Side effects: - *	If the result returned is TCL_ERROR, traceback information about the - *	procedure just executed is appended to the interpreter's errorInfo - *	field. + *	none.   *   *----------------------------------------------------------------------   */ -static int -ProcessProcResultCode( +static void +MakeProcError(      Tcl_Interp *interp,		/* The interpreter in which the procedure was -				 * called and returned returnCode. */ -    char *procName,		/* Name of the procedure. Used for error +				 * called. */ +    Tcl_Obj *procNameObj)	/* Name of the procedure. Used for error  				 * messages and trace information. */ -    int nameLen,		/* Number of bytes in procedure's name. */ -    int returnCode)		/* The unexpected result code. */  { -    Interp *iPtr = (Interp *) interp; -    int overflow, limit = 60; +    int overflow, limit = 60, nameLen; +    const char *procName = Tcl_GetStringFromObj(procNameObj, &nameLen); -    if (returnCode == TCL_OK) { -	return TCL_OK; -    } -    if ((returnCode > TCL_CONTINUE) || (returnCode < TCL_OK)) { -	return returnCode; -    } -    if (returnCode == TCL_RETURN) { -	return TclUpdateReturnInfo(iPtr); -    } -    if (returnCode != TCL_ERROR) { -	Tcl_ResetResult(interp); -	Tcl_AppendResult(interp, "invoked \"", -		((returnCode == TCL_BREAK) ? "break" : "continue"), -		"\" outside of a loop", NULL); -    }      overflow = (nameLen > limit); -    TclFormatToErrorInfo(interp, "\n    (procedure \"%.*s%s\" line %d)", +    Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( +	    "\n    (procedure \"%.*s%s\" line %d)",  	    (overflow ? limit : nameLen), procName, -	    (overflow ? "..." : ""), interp->errorLine); -    return TCL_ERROR; +	    (overflow ? "..." : ""), Tcl_GetErrorLine(interp)));  }  /* @@ -1734,7 +2153,7 @@ void  TclProcDeleteProc(      ClientData clientData)	/* Procedure to be deleted. */  { -    Proc *procPtr = (Proc *) clientData; +    Proc *procPtr = clientData;      procPtr->refCount--;      if (procPtr->refCount <= 0) { @@ -1767,6 +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;      if (bodyPtr != NULL) {  	Tcl_DecrRefCount(bodyPtr); @@ -1777,9 +2199,9 @@ TclProcCleanupProc(  	resVarInfo = localPtr->resolveInfo;  	if (resVarInfo) {  	    if (resVarInfo->deleteProc) { -		(*resVarInfo->deleteProc)(resVarInfo); +		resVarInfo->deleteProc(resVarInfo);  	    } else { -		ckfree((char *) resVarInfo); +		ckfree(resVarInfo);  	    }  	} @@ -1787,10 +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 +     * structure, if any. The interpreter may not exist (For example for +     * procbody structures created by tbcload. +     */ + +    if (iPtr == NULL) { +	return; +    } + +    hePtr = Tcl_FindHashEntry(iPtr->linePBodyPtr, (char *) procPtr); +    if (!hePtr) { +	return; +    } + +    cfPtr = Tcl_GetHashValue(hePtr); + +    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); +    } +    Tcl_DeleteHashEntry(hePtr);  }  /* @@ -1826,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;  } @@ -1868,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 @@ -1888,11 +2346,10 @@ TclNewProcBodyObj(  	return NULL;      } -    objPtr = Tcl_NewStringObj("", 0); - +    TclNewObj(objPtr);      if (objPtr) {  	objPtr->typePtr = &tclProcBodyType; -	objPtr->internalRep.otherValuePtr = (void *) procPtr; +	objPtr->internalRep.twoPtrValue.ptr1 = procPtr;  	procPtr->refCount++;      } @@ -1919,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 = (Proc *) srcPtr->internalRep.otherValuePtr; +    Proc *procPtr = srcPtr->internalRep.twoPtrValue.ptr1;      dupPtr->typePtr = &tclProcBodyType; -    dupPtr->internalRep.otherValuePtr = (void *) procPtr; +    dupPtr->internalRep.twoPtrValue.ptr1 = procPtr;      procPtr->refCount++;  } @@ -1950,17 +2407,27 @@ ProcBodyDup(  static void  ProcBodyFree( -    Tcl_Obj *objPtr)		/* the object to clean up */ +    Tcl_Obj *objPtr)		/* The object to clean up. */  { -    Proc *procPtr = (Proc *) objPtr->internalRep.otherValuePtr; -    procPtr->refCount--; -    if (procPtr->refCount <= 0) { +    Proc *procPtr = objPtr->internalRep.twoPtrValue.ptr1; + +    if (procPtr->refCount-- < 2) {  	TclProcCleanupProc(procPtr);      }  }  /* - * LAMBDA and APPLY implementation. [TIP#194] + *---------------------------------------------------------------------- + * + * DupLambdaInternalRep, FreeLambdaInternalRep, SetLambdaFromAny -- + * + *	How to manage the internal representations of lambda term objects. + *	Syntactically they look like a two- or three-element list, where the + *	first element is the formal arguments, the second is the the body, and + *	the (optional) third is the namespace to execute the lambda term + *	within (the global namespace is assumed if it is absent). + * + *----------------------------------------------------------------------   */  static void @@ -1968,11 +2435,11 @@ DupLambdaInternalRep(      Tcl_Obj *srcPtr,		/* Object with internal rep to copy. */      register Tcl_Obj *copyPtr)	/* Object with internal rep to set. */  { -    Proc *procPtr = (Proc *) srcPtr->internalRep.twoPtrValue.ptr1; -    Tcl_Obj *nsObjPtr = (Tcl_Obj *) srcPtr->internalRep.twoPtrValue.ptr2; +    Proc *procPtr = srcPtr->internalRep.twoPtrValue.ptr1; +    Tcl_Obj *nsObjPtr = srcPtr->internalRep.twoPtrValue.ptr2; -    copyPtr->internalRep.twoPtrValue.ptr1 = (void *) procPtr; -    copyPtr->internalRep.twoPtrValue.ptr2 = (void *) nsObjPtr; +    copyPtr->internalRep.twoPtrValue.ptr1 = procPtr; +    copyPtr->internalRep.twoPtrValue.ptr2 = nsObjPtr;      procPtr->refCount++;      Tcl_IncrRefCount(nsObjPtr); @@ -1984,14 +2451,15 @@ FreeLambdaInternalRep(      register Tcl_Obj *objPtr)	/* CmdName object with internal representation  				 * to free. */  { -    Proc *procPtr = (Proc *) objPtr->internalRep.twoPtrValue.ptr1; -    Tcl_Obj *nsObjPtr = (Tcl_Obj *) objPtr->internalRep.twoPtrValue.ptr2; +    Proc *procPtr = objPtr->internalRep.twoPtrValue.ptr1; +    Tcl_Obj *nsObjPtr = objPtr->internalRep.twoPtrValue.ptr2;      procPtr->refCount--;      if (procPtr->refCount == 0) {  	TclProcCleanupProc(procPtr);      }      TclDecrRefCount(nsObjPtr); +    objPtr->typePtr = NULL;  }  static int @@ -1999,23 +2467,28 @@ SetLambdaFromAny(      Tcl_Interp *interp,		/* Used for error reporting if not NULL. */      register Tcl_Obj *objPtr)	/* The object to convert. */  { -    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;      } @@ -2028,32 +2501,120 @@ SetLambdaFromAny(       */      name = TclGetString(objPtr); -     -    if (TclCreateProc(interp, /*ignored nsPtr*/ NULL, name, argsPtr, -	    bodyPtr, &procPtr) != TCL_OK) { -	TclFormatToErrorInfo(interp, -		"\n    (parsing lambda expression \"%s\")", -		Tcl_GetString(objPtr), NULL); + +    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;      } -    /* CAREFUL: TclCreateProc returns refCount==1! [Bug 1578454] +    /* +     * CAREFUL: TclCreateProc returns refCount==1! [Bug 1578454]       * procPtr->refCount = 1;       */ -     +      procPtr->cmdPtr = NULL;      /* +     * 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. +     * +     * NOTE: The body is the second word in the 'objPtr'. Its location, +     * accessible through 'context.line[1]' (see below) is therefore only the +     * first approximation of the actual line the body is on. We have to use +     * the string rep of the 'objPtr' to determine the exact line. This is +     * available already through 'name'. Use 'TclListLines', see 'switch' +     * (tclCmdMZ.c). +     * +     * This code is nearly identical to the #280 code in Tcl_ProcObjCmd, see +     * this file. The differences are the different index of the body in the +     * line array of the context, and the special processing mentioned in the +     * previous paragraph to track into the list. Find a way to factor the +     * common elements into a single function. +     */ + +    if (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 (contextPtr->type == TCL_LOCATION_SOURCE) { +	    /* +	     * We can record source location within a lambda only if the body +	     * was not created by substitution. +	     */ + +	    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; +	    } + +	    /* +	     * 'contextPtr' is going out of scope. Release the reference that +	     * it's holding to the source file path +	     */ + +	    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       * global reference, or else global per default.       */ -     +      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]; @@ -2068,30 +2629,57 @@ SetLambdaFromAny(       * conversion to lambdaType.       */ -    objPtr->typePtr->freeIntRepProc(objPtr); +    TclFreeIntRep(objPtr); -    objPtr->internalRep.twoPtrValue.ptr1 = (void *) procPtr; -    objPtr->internalRep.twoPtrValue.ptr2 = (void *) nsObjPtr; +    objPtr->internalRep.twoPtrValue.ptr1 = procPtr; +    objPtr->internalRep.twoPtrValue.ptr2 = nsObjPtr;      objPtr->typePtr = &lambdaType;      return TCL_OK;  } + +/* + *---------------------------------------------------------------------- + * + * Tcl_ApplyObjCmd -- + * + *	This object-based function is invoked to process the "apply" Tcl + *	command. See the user documentation for details on what it does. + * + * Results: + *	A standard Tcl object result value. + * + * Side effects: + *	Depends on the content of the lambda term (i.e., objv[1]). + * + *---------------------------------------------------------------------- + */  int  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; +    ApplyExtraData *extraPtr;      if (objc < 2) { -	Tcl_WrongNumArgs(interp, 1, objv, "lambdaExpr ?arg1 arg2 ...?"); +	Tcl_WrongNumArgs(interp, 1, objv, "lambdaExpr ?arg ...?");  	return TCL_ERROR;      } @@ -2099,25 +2687,30 @@ Tcl_ApplyObjCmd(       * Set lambdaPtr, convert it to lambdaType in the current interp if       * necessary.       */ -  +      lambdaPtr = objv[1];      if (lambdaPtr->typePtr == &lambdaType) { -	procPtr = (Proc *) lambdaPtr->internalRep.twoPtrValue.ptr1; +	procPtr = lambdaPtr->internalRep.twoPtrValue.ptr1;      }  #define JOE_EXTENSION 0 -#if JOE_EXTENSION -    /* -     * Joe English's suggestion to allow cmdNames to function as lambdas. -     * Requires also making tclCmdNameType non-static in tclObj.c -     */ +/* + * 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. +	 */ +  	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);  	} @@ -2129,32 +2722,336 @@ Tcl_ApplyObjCmd(  	if (result != TCL_OK) {  	    return result;  	} -	procPtr = (Proc *) lambdaPtr->internalRep.twoPtrValue.ptr1; +	procPtr = lambdaPtr->internalRep.twoPtrValue.ptr1;      } -    procPtr->cmdPtr = &cmd; -     +      /*       * Find the namespace where this lambda should run, and push a call frame       * for that namespace. Note that TclObjInterpProc() will pop it.       */ -     -    nsObjPtr = (Tcl_Obj *) lambdaPtr->internalRep.twoPtrValue.ptr2; -    result  = TclGetNamespaceFromObj(interp, nsObjPtr, &nsPtr); + +    nsObjPtr = lambdaPtr->internalRep.twoPtrValue.ptr2; +    result = TclGetNamespaceFromObj(interp, nsObjPtr, &nsPtr);      if (result != TCL_OK) { -	return result; +	return TCL_ERROR;      } -    if (nsPtr == NULL) { -	errPtr = Tcl_NewStringObj("cannot find namespace \"",-1); -	Tcl_AppendObjToObj(errPtr, nsObjPtr); -	Tcl_AppendToObj(errPtr, "\"", -1); -	Tcl_SetObjResult(interp, errPtr); +    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) { +	iPtr->ensembleRewrite.sourceObjs = objv; +	iPtr->ensembleRewrite.numRemovedObjs = 1; +	iPtr->ensembleRewrite.numInsertedObjs = 0; +    } else { +	iPtr->ensembleRewrite.numInsertedObjs -= 1; +    } +    extraPtr->isRootEnsemble = isRootEnsemble; + +    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; +} + +static int +ApplyNR2( +    ClientData data[], +    Tcl_Interp *interp, +    int result) +{ +    ApplyExtraData *extraPtr = data[0]; + +    if (extraPtr->isRootEnsemble) { +	((Interp *) interp)->ensembleRewrite.sourceObjs = NULL; +    } + +    TclStackFree(interp, extraPtr); +    return result; +} + +/* + *---------------------------------------------------------------------- + * + * MakeLambdaError -- + * + *	Function called by TclObjInterpProc to create the stack information + *	upon an error from a lambda term. + * + * Results: + *	The interpreter's error info trace is set to a value that supplements + *	the error code. + * + * Side effects: + *	none. + * + *---------------------------------------------------------------------- + */ + +static void +MakeLambdaError( +    Tcl_Interp *interp,		/* The interpreter in which the procedure was +				 * called. */ +    Tcl_Obj *procNameObj)	/* Name of the procedure. Used for error +				 * messages and trace information. */ +{ +    int overflow, limit = 60, nameLen; +    const char *procName = Tcl_GetStringFromObj(procNameObj, &nameLen); + +    overflow = (nameLen > limit); +    Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( +	    "\n    (lambda term \"%.*s%s\" line %d)", +	    (overflow ? limit : nameLen), procName, +	    (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;      } -     -    cmd.nsPtr = (Namespace *) nsPtr; +    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; +	} -    return ObjInterpProcEx((ClientData) procPtr, interp, objc, objv, 2); +	/* +	 * 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;  }  /* | 
