diff options
Diffstat (limited to 'generic/tclCompCmds.c')
| -rw-r--r-- | generic/tclCompCmds.c | 5146 | 
1 files changed, 2701 insertions, 2445 deletions
| diff --git a/generic/tclCompCmds.c b/generic/tclCompCmds.c index 42b8448..b9bc228 100644 --- a/generic/tclCompCmds.c +++ b/generic/tclCompCmds.c @@ -1,48 +1,115 @@ -/*  +/*   * tclCompCmds.c --   * - *	This file contains compilation procedures that compile various - *	Tcl commands into a sequence of instructions ("bytecodes").  + *	This file contains compilation procedures that compile various Tcl + *	commands into a sequence of instructions ("bytecodes").   *   * Copyright (c) 1997-1998 Sun Microsystems, Inc.   * Copyright (c) 2001 by Kevin B. Kenny.  All rights reserved.   * Copyright (c) 2002 ActiveState Corporation. + * Copyright (c) 2004-2013 by Donal K. Fellows.   * - * See the file "license.terms" for information on usage and redistribution - * of this file, and for a DISCLAIMER OF ALL WARRANTIES. - * - * RCS: @(#) $Id: tclCompCmds.c,v 1.39.2.2 2003/07/15 20:51:49 dgp Exp $ + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES.   */  #include "tclInt.h"  #include "tclCompile.h" +#include <assert.h>  /*   * Prototypes for procedures defined later in this file:   */ -static ClientData	DupForeachInfo _ANSI_ARGS_((ClientData clientData)); -static void		FreeForeachInfo _ANSI_ARGS_((ClientData clientData)); -static int		TclPushVarName _ANSI_ARGS_((Tcl_Interp *interp, -	Tcl_Token *varTokenPtr, CompileEnv *envPtr, int flags, -	int *localIndexPtr, int *simpleVarNamePtr, int *isScalarPtr)); +static ClientData	DupDictUpdateInfo(ClientData clientData); +static void		FreeDictUpdateInfo(ClientData clientData); +static void		PrintDictUpdateInfo(ClientData clientData, +			    Tcl_Obj *appendObj, ByteCode *codePtr, +			    unsigned int pcOffset); +static void		DisassembleDictUpdateInfo(ClientData clientData, +			    Tcl_Obj *dictObj, ByteCode *codePtr, +			    unsigned int pcOffset); +static ClientData	DupForeachInfo(ClientData clientData); +static void		FreeForeachInfo(ClientData clientData); +static void		PrintForeachInfo(ClientData clientData, +			    Tcl_Obj *appendObj, ByteCode *codePtr, +			    unsigned int pcOffset); +static void		DisassembleForeachInfo(ClientData clientData, +			    Tcl_Obj *dictObj, ByteCode *codePtr, +			    unsigned int pcOffset); +static void		PrintNewForeachInfo(ClientData clientData, +			    Tcl_Obj *appendObj, ByteCode *codePtr, +			    unsigned int pcOffset); +static void		DisassembleNewForeachInfo(ClientData clientData, +			    Tcl_Obj *dictObj, ByteCode *codePtr, +			    unsigned int pcOffset); +static int		CompileEachloopCmd(Tcl_Interp *interp, +			    Tcl_Parse *parsePtr, Command *cmdPtr, +			    CompileEnv *envPtr, int collect); +static int		CompileDictEachCmd(Tcl_Interp *interp, +			    Tcl_Parse *parsePtr, Command *cmdPtr, +			    struct CompileEnv *envPtr, int collect);  /* - * Flags bits used by TclPushVarName. + * The structures below define the AuxData types defined in this file.   */ -#define TCL_CREATE_VAR     1 /* Create a compiled local if none is found */ -#define TCL_NO_LARGE_INDEX 2 /* Do not return localIndex value > 255 */ +static const AuxDataType foreachInfoType = { +    "ForeachInfo",		/* name */ +    DupForeachInfo,		/* dupProc */ +    FreeForeachInfo,		/* freeProc */ +    PrintForeachInfo,		/* printProc */ +    DisassembleForeachInfo	/* disassembleProc */ +}; + +static const AuxDataType newForeachInfoType = { +    "NewForeachInfo",		/* name */ +    DupForeachInfo,		/* dupProc */ +    FreeForeachInfo,		/* freeProc */ +    PrintNewForeachInfo,	/* printProc */ +    DisassembleNewForeachInfo	/* disassembleProc */ +}; +static const AuxDataType dictUpdateInfoType = { +    "DictUpdateInfo",		/* name */ +    DupDictUpdateInfo,		/* dupProc */ +    FreeDictUpdateInfo,		/* freeProc */ +    PrintDictUpdateInfo,	/* printProc */ +    DisassembleDictUpdateInfo	/* disassembleProc */ +}; +  /* - * The structures below define the AuxData types defined in this file. + *---------------------------------------------------------------------- + * + * TclGetAuxDataType -- + * + *	This procedure looks up an Auxdata type by name. + * + * Results: + *	If an AuxData type with name matching "typeName" is found, a pointer + *	to its AuxDataType structure is returned; otherwise, NULL is returned. + * + * Side effects: + *	None. + * + *----------------------------------------------------------------------   */ -AuxDataType tclForeachInfoType = { -    "ForeachInfo",				/* name */ -    DupForeachInfo,				/* dupProc */ -    FreeForeachInfo				/* freeProc */ -}; +const AuxDataType * +TclGetAuxDataType( +    const char *typeName)	/* Name of AuxData type to look up. */ +{ +    if (!strcmp(typeName, foreachInfoType.name)) { +	return &foreachInfoType; +    } else if (!strcmp(typeName, newForeachInfoType.name)) { +	return &newForeachInfoType; +    } else if (!strcmp(typeName, dictUpdateInfoType.name)) { +	return &dictUpdateInfoType; +    } else if (!strcmp(typeName, tclJumptableInfoType.name)) { +	return &tclJumptableInfoType; +    } +    return NULL; +}  /*   *---------------------------------------------------------------------- @@ -52,121 +119,376 @@ AuxDataType tclForeachInfoType = {   *	Procedure called to compile the "append" command.   *   * Results: - *	The return value is a standard Tcl result, which is normally TCL_OK - *	unless there was an error while parsing string. If an error occurs - *	then the interpreter's result contains a standard error message. If - *	complation fails because the command requires a second level of - *	substitutions, TCL_OUT_LINE_COMPILE is returned indicating that the - *	command should be compiled "out of line" by emitting code to - *	invoke its command procedure (Tcl_AppendObjCmd) at runtime. + *	Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer + *	evaluation to runtime.   *   * Side effects: - *	Instructions are added to envPtr to execute the "append" command - *	at runtime. + *	Instructions are added to envPtr to execute the "append" command at + *	runtime.   *   *----------------------------------------------------------------------   */  int -TclCompileAppendCmd(interp, parsePtr, envPtr) -    Tcl_Interp *interp;		/* Used for error reporting. */ -    Tcl_Parse *parsePtr;	/* Points to a parse structure for the -				 * command created by Tcl_ParseCommand. */ -    CompileEnv *envPtr;		/* Holds resulting instructions. */ +TclCompileAppendCmd( +    Tcl_Interp *interp,		/* Used for error reporting. */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr)		/* Holds resulting instructions. */  {      Tcl_Token *varTokenPtr, *valueTokenPtr; -    int simpleVarName, isScalar, localIndex, numWords; -    int code = TCL_OK; +    int isScalar, localIndex, numWords, i; +    DefineLineInformation;	/* TIP #280 */ +    /* TODO: Consider support for compiling expanded args. */      numWords = parsePtr->numWords;      if (numWords == 1) { -	Tcl_ResetResult(interp); -	Tcl_AppendToObj(Tcl_GetObjResult(interp), -		"wrong # args: should be \"append varName ?value value ...?\"", -		-1);  	return TCL_ERROR;      } else if (numWords == 2) {  	/* -	 * append varName === set varName +	 * append varName == set varName  	 */ -        return TclCompileSetCmd(interp, parsePtr, envPtr); + +	return TclCompileSetCmd(interp, parsePtr, cmdPtr, envPtr);      } else if (numWords > 3) {  	/* -	 * APPEND instructions currently only handle one value +	 * APPEND instructions currently only handle one value, but we can +	 * handle some multi-value cases by stringing them together.  	 */ -        return TCL_OUT_LINE_COMPILE; + +	goto appendMultiple;      }      /* -     * Decide if we can use a frame slot for the var/array name or if we -     * need to emit code to compute and push the name at runtime. We use a -     * frame slot (entry in the array of local vars) if we are compiling a -     * procedure body and if the name is simple text that does not include -     * namespace qualifiers.  +     * Decide if we can use a frame slot for the var/array name or if we need +     * to emit code to compute and push the name at runtime. We use a frame +     * slot (entry in the array of local vars) if we are compiling a procedure +     * body and if the name is simple text that does not include namespace +     * qualifiers.       */ -    varTokenPtr = parsePtr->tokenPtr -	    + (parsePtr->tokenPtr->numComponents + 1); +    varTokenPtr = TokenAfter(parsePtr->tokenPtr); -    code = TclPushVarName(interp, varTokenPtr, envPtr, TCL_CREATE_VAR, -	    &localIndex, &simpleVarName, &isScalar); -    if (code != TCL_OK) { -	goto done; -    } +    PushVarNameWord(interp, varTokenPtr, envPtr, 0, +	    &localIndex, &isScalar, 1);      /* -     * We are doing an assignment, otherwise TclCompileSetCmd was called, -     * so push the new value.  This will need to be extended to push a -     * value for each argument. +     * We are doing an assignment, otherwise TclCompileSetCmd was called, so +     * push the new value. This will need to be extended to push a value for +     * each argument.       */ -    if (numWords > 2) { -	valueTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1); -	if (valueTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) { -	    TclEmitPush(TclRegisterNewLiteral(envPtr,  -		    valueTokenPtr[1].start, valueTokenPtr[1].size), envPtr); -	} else { -	    code = TclCompileTokens(interp, valueTokenPtr+1, -	            valueTokenPtr->numComponents, envPtr); -	    if (code != TCL_OK) { -		goto done; -	    } -	} -    } +	valueTokenPtr = TokenAfter(varTokenPtr); +	CompileWord(envPtr, valueTokenPtr, interp, 2);      /*       * Emit instructions to set/get the variable.       */ -    if (simpleVarName) {  	if (isScalar) { -	    if (localIndex >= 0) { -		if (localIndex <= 255) { -		    TclEmitInstInt1(INST_APPEND_SCALAR1, localIndex, envPtr); -		} else { -		    TclEmitInstInt4(INST_APPEND_SCALAR4, localIndex, envPtr); -		} -	    } else { +	    if (localIndex < 0) {  		TclEmitOpcode(INST_APPEND_STK, envPtr); +	    } else { +		Emit14Inst(INST_APPEND_SCALAR, localIndex, envPtr);  	    }  	} else { -	    if (localIndex >= 0) { -		if (localIndex <= 255) { -		    TclEmitInstInt1(INST_APPEND_ARRAY1, localIndex, envPtr); -		} else { -		    TclEmitInstInt4(INST_APPEND_ARRAY4, localIndex, envPtr); -		} -	    } else { +	    if (localIndex < 0) {  		TclEmitOpcode(INST_APPEND_ARRAY_STK, envPtr); +	    } else { +		Emit14Inst(INST_APPEND_ARRAY, localIndex, envPtr);  	    }  	} + +    return TCL_OK; + +  appendMultiple: +    /* +     * Can only handle the case where we are appending to a local scalar when +     * there are multiple values to append.  Fortunately, this is common. +     */ + +    varTokenPtr = TokenAfter(parsePtr->tokenPtr); + +    localIndex = LocalScalarFromToken(varTokenPtr, envPtr); +    if (localIndex < 0) { +	return TCL_ERROR; +    } + +    /* +     * Definitely appending to a local scalar; generate the words and append +     * them. +     */ + +    valueTokenPtr = TokenAfter(varTokenPtr); +    for (i = 2 ; i < numWords ; i++) { +	CompileWord(envPtr, valueTokenPtr, interp, i); +	valueTokenPtr = TokenAfter(valueTokenPtr); +    } +    TclEmitInstInt4(	  INST_REVERSE, numWords-2,		envPtr); +    for (i = 2 ; i < numWords ;) { +	Emit14Inst(	  INST_APPEND_SCALAR, localIndex,	envPtr); +	if (++i < numWords) { +	    TclEmitOpcode(INST_POP,				envPtr); +	} +    } + +    return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TclCompileArray*Cmd -- + * + *	Functions called to compile "array" sucommands. + * + * Results: + *	All return TCL_OK for a successful compile, and TCL_ERROR to defer + *	evaluation to runtime. + * + * Side effects: + *	Instructions are added to envPtr to execute the "array" subcommand at + *	runtime. + * + *---------------------------------------------------------------------- + */ + +int +TclCompileArrayExistsCmd( +    Tcl_Interp *interp,		/* Used for looking up stuff. */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr)		/* Holds resulting instructions. */ +{ +    DefineLineInformation;	/* TIP #280 */ +    Tcl_Token *tokenPtr; +    int isScalar, localIndex; + +    if (parsePtr->numWords != 2) { +	return TCL_ERROR; +    } + +    tokenPtr = TokenAfter(parsePtr->tokenPtr); +    PushVarNameWord(interp, tokenPtr, envPtr, TCL_NO_ELEMENT, +	    &localIndex, &isScalar, 1); +    if (!isScalar) { +	return TCL_ERROR; +    } + +    if (localIndex >= 0) { +	TclEmitInstInt4(INST_ARRAY_EXISTS_IMM, localIndex,	envPtr);      } else { -	TclEmitOpcode(INST_APPEND_STK, envPtr); +	TclEmitOpcode(	INST_ARRAY_EXISTS_STK,			envPtr);      } +    return TCL_OK; +} + +int +TclCompileArraySetCmd( +    Tcl_Interp *interp,		/* Used for looking up stuff. */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr)		/* Holds resulting instructions. */ +{ +    DefineLineInformation;	/* TIP #280 */ +    Tcl_Token *varTokenPtr, *dataTokenPtr; +    int isScalar, localIndex, code = TCL_OK; +    int isDataLiteral, isDataValid, isDataEven, len; +    int keyVar, valVar, infoIndex; +    int fwd, offsetBack, offsetFwd; +    Tcl_Obj *literalObj; +    ForeachInfo *infoPtr; + +    if (parsePtr->numWords != 3) { +	return TCL_ERROR; +    } + +    varTokenPtr = TokenAfter(parsePtr->tokenPtr); +    dataTokenPtr = TokenAfter(varTokenPtr); +    literalObj = Tcl_NewObj(); +    isDataLiteral = TclWordKnownAtCompileTime(dataTokenPtr, literalObj); +    isDataValid = (isDataLiteral +	    && Tcl_ListObjLength(NULL, literalObj, &len) == TCL_OK); +    isDataEven = (isDataValid && (len & 1) == 0); + +    /* +     * Special case: literal odd-length argument is always an error. +     */ + +    if (isDataValid && !isDataEven) { +	PushStringLiteral(envPtr, "list must have an even number of elements"); +	PushStringLiteral(envPtr, "-errorcode {TCL ARGUMENT FORMAT}"); +	TclEmitInstInt4(INST_RETURN_IMM, TCL_ERROR,		envPtr); +	TclEmitInt4(		0,				envPtr); +	goto done; +    } + +    /* +     * Except for the special "ensure array" case below, when we're not in +     * a proc, we cannot do a better compile than generic. +     */ + +    if ((varTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) || +	    (envPtr->procPtr == NULL && !(isDataEven && len == 0))) { +	code = TclCompileBasic2ArgCmd(interp, parsePtr, cmdPtr, envPtr); +	goto done; +    } + +    PushVarNameWord(interp, varTokenPtr, envPtr, TCL_NO_ELEMENT, +	    &localIndex, &isScalar, 1); +    if (!isScalar) { +	code = TCL_ERROR; +	goto done; +    } + +    /* +     * Special case: literal empty value argument is just an "ensure array" +     * operation. +     */ + +    if (isDataEven && len == 0) { +	if (localIndex >= 0) { +	    TclEmitInstInt4(INST_ARRAY_EXISTS_IMM, localIndex,	envPtr); +	    TclEmitInstInt1(INST_JUMP_TRUE1, 7,			envPtr); +	    TclEmitInstInt4(INST_ARRAY_MAKE_IMM, localIndex,	envPtr); +	} else { +	    TclEmitOpcode(  INST_DUP,				envPtr); +	    TclEmitOpcode(  INST_ARRAY_EXISTS_STK,		envPtr); +	    TclEmitInstInt1(INST_JUMP_TRUE1, 5,			envPtr); +	    TclEmitOpcode(  INST_ARRAY_MAKE_STK,		envPtr); +	    TclEmitInstInt1(INST_JUMP1, 3,			envPtr); +	    /* Each branch decrements stack depth, but we only take one. */ +	    TclAdjustStackDepth(1, envPtr); +	    TclEmitOpcode(  INST_POP,				envPtr); +	} +	PushStringLiteral(envPtr, ""); +	goto done; +    } + +    if (localIndex < 0) { +	/* +	 * a non-local variable: upvar from a local one! This consumes the +	 * variable name that was left at stacktop. +	 */ + +	localIndex = TclFindCompiledLocal(varTokenPtr->start, +		varTokenPtr->size, 1, envPtr); +	PushStringLiteral(envPtr, "0"); +	TclEmitInstInt4(INST_REVERSE, 2,        		envPtr); +	TclEmitInstInt4(INST_UPVAR, localIndex, 		envPtr); +	TclEmitOpcode(INST_POP,          			envPtr); +    } + +    /* +     * Prepare for the internal foreach. +     */ + +    keyVar = AnonymousLocal(envPtr); +    valVar = AnonymousLocal(envPtr); + +    infoPtr = ckalloc(sizeof(ForeachInfo)); +    infoPtr->numLists = 1; +    infoPtr->varLists[0] = ckalloc(sizeof(ForeachVarList) + sizeof(int)); +    infoPtr->varLists[0]->numVars = 2; +    infoPtr->varLists[0]->varIndexes[0] = keyVar; +    infoPtr->varLists[0]->varIndexes[1] = valVar; +    infoIndex = TclCreateAuxData(infoPtr, &newForeachInfoType, envPtr); + +    /* +     * Start issuing instructions to write to the array. +     */ + +    CompileWord(envPtr, dataTokenPtr, interp, 2); +    if (!isDataLiteral || !isDataValid) { +	/* +	 * Only need this safety check if we're handling a non-literal or list +	 * containing an invalid literal; with valid list literals, we've +	 * already checked (worth it because literals are a very common +	 * use-case with [array set]). +	 */ + +	TclEmitOpcode(	INST_DUP,				envPtr); +	TclEmitOpcode(	INST_LIST_LENGTH,			envPtr); +	PushStringLiteral(envPtr, "1"); +	TclEmitOpcode(	INST_BITAND,				envPtr); +	offsetFwd = CurrentOffset(envPtr); +	TclEmitInstInt1(INST_JUMP_FALSE1, 0,			envPtr); +	PushStringLiteral(envPtr, "list must have an even number of elements"); +	PushStringLiteral(envPtr, "-errorcode {TCL ARGUMENT FORMAT}"); +	TclEmitInstInt4(INST_RETURN_IMM, TCL_ERROR,		envPtr); +	TclEmitInt4(		0,				envPtr); +	TclAdjustStackDepth(-1, envPtr); +	fwd = CurrentOffset(envPtr) - offsetFwd; +	TclStoreInt1AtPtr(fwd, envPtr->codeStart+offsetFwd+1); +    } + +    TclEmitInstInt4(INST_ARRAY_EXISTS_IMM, localIndex,	envPtr); +    TclEmitInstInt1(INST_JUMP_TRUE1, 7,			envPtr); +    TclEmitInstInt4(INST_ARRAY_MAKE_IMM, localIndex,	envPtr); +    TclEmitInstInt4(INST_FOREACH_START, infoIndex,	envPtr); +    offsetBack = CurrentOffset(envPtr); +    Emit14Inst(	INST_LOAD_SCALAR, keyVar,		envPtr); +    Emit14Inst(	INST_LOAD_SCALAR, valVar,		envPtr); +    Emit14Inst(	INST_STORE_ARRAY, localIndex,		envPtr); +    TclEmitOpcode(	INST_POP,			envPtr); +    infoPtr->loopCtTemp = offsetBack - CurrentOffset(envPtr); /*misuse */ +    TclEmitOpcode( INST_FOREACH_STEP,			envPtr); +    TclEmitOpcode( INST_FOREACH_END,			envPtr); +    TclAdjustStackDepth(-3, envPtr); +    PushStringLiteral(envPtr,	"");      done: +    Tcl_DecrRefCount(literalObj);      return code;  } + +int +TclCompileArrayUnsetCmd( +    Tcl_Interp *interp,		/* Used for looking up stuff. */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr)		/* Holds resulting instructions. */ +{ +    DefineLineInformation;	/* TIP #280 */ +    Tcl_Token *tokenPtr = TokenAfter(parsePtr->tokenPtr); +    int isScalar, localIndex; + +    if (parsePtr->numWords != 2) { +	return TclCompileBasic2ArgCmd(interp, parsePtr, cmdPtr, envPtr); +    } + +    PushVarNameWord(interp, tokenPtr, envPtr, TCL_NO_ELEMENT, +	    &localIndex, &isScalar, 1); +    if (!isScalar) { +	return TCL_ERROR; +    } + +    if (localIndex >= 0) { +	TclEmitInstInt4(INST_ARRAY_EXISTS_IMM, localIndex,	envPtr); +	TclEmitInstInt1(INST_JUMP_FALSE1, 8,			envPtr); +	TclEmitInstInt1(INST_UNSET_SCALAR, 1,			envPtr); +	TclEmitInt4(		localIndex,			envPtr); +    } else { +	TclEmitOpcode(	INST_DUP,				envPtr); +	TclEmitOpcode(	INST_ARRAY_EXISTS_STK,			envPtr); +	TclEmitInstInt1(INST_JUMP_FALSE1, 6,			envPtr); +	TclEmitInstInt1(INST_UNSET_STK, 1,			envPtr); +	TclEmitInstInt1(INST_JUMP1, 3,				envPtr); +	/* Each branch decrements stack depth, but we only take one. */ +	TclAdjustStackDepth(1, envPtr); +	TclEmitOpcode(	INST_POP,				envPtr); +    } +    PushStringLiteral(envPtr,	""); +    return TCL_OK; +}  /*   *---------------------------------------------------------------------- @@ -176,36 +498,53 @@ TclCompileAppendCmd(interp, parsePtr, envPtr)   *	Procedure called to compile the "break" command.   *   * Results: - *	The return value is a standard Tcl result, which is TCL_OK unless - *	there was an error during compilation. If an error occurs then - *	the interpreter's result contains a standard error message. + *	Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer + *	evaluation to runtime.   *   * Side effects: - *	Instructions are added to envPtr to execute the "break" command - *	at runtime. + *	Instructions are added to envPtr to execute the "break" command at + *	runtime.   *   *----------------------------------------------------------------------   */  int -TclCompileBreakCmd(interp, parsePtr, envPtr) -    Tcl_Interp *interp;		/* Used for error reporting. */ -    Tcl_Parse *parsePtr;	/* Points to a parse structure for the -				 * command created by Tcl_ParseCommand. */ -    CompileEnv *envPtr;		/* Holds resulting instructions. */ +TclCompileBreakCmd( +    Tcl_Interp *interp,		/* Used for error reporting. */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr)		/* Holds resulting instructions. */  { +    ExceptionRange *rangePtr; +    ExceptionAux *auxPtr; +      if (parsePtr->numWords != 1) { -	Tcl_ResetResult(interp); -	Tcl_AppendToObj(Tcl_GetObjResult(interp), -	        "wrong # args: should be \"break\"", -1);  	return TCL_ERROR;      }      /* -     * Emit a break instruction. +     * Find the innermost exception range that contains this command.       */ -    TclEmitOpcode(INST_BREAK, envPtr); +    rangePtr = TclGetInnermostExceptionRange(envPtr, TCL_BREAK, &auxPtr); +    if (rangePtr && rangePtr->type == LOOP_EXCEPTION_RANGE) { +	/* +	 * Found the target! No need for a nasty INST_BREAK here. +	 */ + +	TclCleanupStackForBreakContinue(envPtr, auxPtr); +	TclAddLoopBreakFixup(envPtr, auxPtr); +    } else { +	/* +	 * Emit a real break. +	 */ + +	TclEmitOpcode(INST_BREAK, envPtr); +    } +    TclAdjustStackDepth(1, envPtr); +      return TCL_OK;  } @@ -217,2883 +556,2805 @@ TclCompileBreakCmd(interp, parsePtr, envPtr)   *	Procedure called to compile the "catch" command.   *   * Results: - *	The return value is a standard Tcl result, which is TCL_OK if - *	compilation was successful. If an error occurs then the - *	interpreter's result contains a standard error message and TCL_ERROR - *	is returned. If the command is too complex for TclCompileCatchCmd, - *	TCL_OUT_LINE_COMPILE is returned indicating that the catch command - *	should be compiled "out of line" by emitting code to invoke its - *	command procedure at runtime. + *	Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer + *	evaluation to runtime.   *   * Side effects: - *	Instructions are added to envPtr to execute the "catch" command - *	at runtime. + *	Instructions are added to envPtr to execute the "catch" command at + *	runtime.   *   *----------------------------------------------------------------------   */  int -TclCompileCatchCmd(interp, parsePtr, envPtr) -    Tcl_Interp *interp;		/* Used for error reporting. */ -    Tcl_Parse *parsePtr;	/* Points to a parse structure for the -				 * command created by Tcl_ParseCommand. */ -    CompileEnv *envPtr;		/* Holds resulting instructions. */ +TclCompileCatchCmd( +    Tcl_Interp *interp,		/* Used for error reporting. */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr)		/* Holds resulting instructions. */  {      JumpFixup jumpFixup; -    Tcl_Token *cmdTokenPtr, *nameTokenPtr; -    CONST char *name; -    int localIndex, nameChars, range, startOffset, jumpDist; -    int code; -    int savedStackDepth = envPtr->currStackDepth; - -    if ((parsePtr->numWords != 2) && (parsePtr->numWords != 3)) { -	Tcl_ResetResult(interp); -	Tcl_AppendToObj(Tcl_GetObjResult(interp), -	        "wrong # args: should be \"catch command ?varName?\"", -1); +    Tcl_Token *cmdTokenPtr, *resultNameTokenPtr, *optsNameTokenPtr; +    int resultIndex, optsIndex, range, dropScript = 0; +    DefineLineInformation;	/* TIP #280 */ +    int depth = TclGetStackDepth(envPtr); + +    /* +     * If syntax does not match what we expect for [catch], do not compile. +     * Let runtime checks determine if syntax has changed. +     */ + +    if ((parsePtr->numWords < 2) || (parsePtr->numWords > 4)) {  	return TCL_ERROR;      }      /* -     * If a variable was specified and the catch command is at global level -     * (not in a procedure), don't compile it inline: the payoff is -     * too small. +     * If variables were specified and the catch command is at global level +     * (not in a procedure), don't compile it inline: the payoff is too small.       */ -    if ((parsePtr->numWords == 3) && (envPtr->procPtr == NULL)) { -	return TCL_OUT_LINE_COMPILE; +    if ((parsePtr->numWords >= 3) && !EnvHasLVT(envPtr)) { +	return TCL_ERROR;      }      /* -     * Make sure the variable name, if any, has no substitutions and just -     * refers to a local scaler. +     * Make sure the variable names, if any, have no substitutions and just +     * refer to local scalars.       */ -    localIndex = -1; -    cmdTokenPtr = parsePtr->tokenPtr -	    + (parsePtr->tokenPtr->numComponents + 1); -    if (parsePtr->numWords == 3) { -	nameTokenPtr = cmdTokenPtr + (cmdTokenPtr->numComponents + 1); -	if (nameTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) { -	    name = nameTokenPtr[1].start; -	    nameChars = nameTokenPtr[1].size; -	    if (!TclIsLocalScalar(name, nameChars)) { -		return TCL_OUT_LINE_COMPILE; +    resultIndex = optsIndex = -1; +    cmdTokenPtr = TokenAfter(parsePtr->tokenPtr); +    if (parsePtr->numWords >= 3) { +	resultNameTokenPtr = TokenAfter(cmdTokenPtr); +	/* DGP */ +	resultIndex = LocalScalarFromToken(resultNameTokenPtr, envPtr); +	if (resultIndex < 0) { +	    return TCL_ERROR; +	} + +	/* DKF */ +	if (parsePtr->numWords == 4) { +	    optsNameTokenPtr = TokenAfter(resultNameTokenPtr); +	    optsIndex = LocalScalarFromToken(optsNameTokenPtr, envPtr); +	    if (optsIndex < 0) { +		return TCL_ERROR;  	    } -	    localIndex = TclFindCompiledLocal(nameTokenPtr[1].start, -		    nameTokenPtr[1].size, /*create*/ 1,  -		    /*flags*/ VAR_SCALAR, envPtr->procPtr); -	} else { -	   return TCL_OUT_LINE_COMPILE;  	}      }      /* -     * We will compile the catch command. Emit a beginCatch instruction at -     * the start of the catch body: the subcommand it controls. +     * We will compile the catch command. Declare the exception range that it +     * uses. +     * +     * If the body is a simple word, compile a BEGIN_CATCH instruction, +     * followed by the instructions to eval the body. +     * Otherwise, compile instructions to substitute the body text before +     * starting the catch, then BEGIN_CATCH, and then EVAL_STK to evaluate the +     * substituted body. +     * Care has to be taken to make sure that substitution happens outside the +     * catch range so that errors in the substitution are not caught. +     * [Bug 219184] +     * The reason for duplicating the script is that EVAL_STK would otherwise +     * begin by undeflowing the stack below the mark set by BEGIN_CATCH4.       */ -     -    envPtr->exceptDepth++; -    envPtr->maxExceptDepth = -	TclMax(envPtr->exceptDepth, envPtr->maxExceptDepth); +      range = TclCreateExceptRange(CATCH_EXCEPTION_RANGE, envPtr); -    TclEmitInstInt4(INST_BEGIN_CATCH4, range, envPtr); +    if (cmdTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) { +	TclEmitInstInt4(	INST_BEGIN_CATCH4, range,	envPtr); +	ExceptionRangeStarts(envPtr, range); +	BODY(cmdTokenPtr, 1); +    } else { +	SetLineInformation(1); +	CompileTokens(envPtr, cmdTokenPtr, interp); +	TclEmitInstInt4(	INST_BEGIN_CATCH4, range,	envPtr); +	ExceptionRangeStarts(envPtr, range); +	TclEmitOpcode(		INST_DUP,			envPtr); +	TclEmitInvoke(envPtr,	INST_EVAL_STK); +	/* drop the script */ +	dropScript = 1; +	TclEmitInstInt4(	INST_REVERSE, 2,		envPtr); +	TclEmitOpcode(		INST_POP,			envPtr); +    } +    ExceptionRangeEnds(envPtr, range); +      /* -     * If the body is a simple word, compile the instructions to -     * eval it. Otherwise, compile instructions to substitute its -     * text without catching, a catch instruction that resets the  -     * stack to what it was before substituting the body, and then  -     * an instruction to eval the body. Care has to be taken to  -     * register the correct startOffset for the catch range so that -     * errors in the substitution are not catched [Bug 219184] +     * Emit the "no errors" epilogue: push "0" (TCL_OK) as the catch result, +     * and jump around the "error case" code.       */ -    if (cmdTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) { -	startOffset = (envPtr->codeNext - envPtr->codeStart); -	code = TclCompileCmdWord(interp, cmdTokenPtr+1, 1, envPtr); -    } else { -	code = TclCompileTokens(interp, cmdTokenPtr+1, -	        cmdTokenPtr->numComponents, envPtr); -	startOffset = (envPtr->codeNext - envPtr->codeStart); -	TclEmitOpcode(INST_EVAL_STK, envPtr); +    TclCheckStackDepth(depth+1, envPtr); +    PushStringLiteral(envPtr, "0"); +    TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &jumpFixup); + +    /* +     * Emit the "error case" epilogue. Push the interpreter result and the +     * return code. +     */ + +    ExceptionRangeTarget(envPtr, range, catchOffset); +    TclSetStackDepth(depth + dropScript, envPtr); + +    if (dropScript) { +	TclEmitOpcode(		INST_POP,			envPtr);      } -    envPtr->exceptArrayPtr[range].codeOffset = startOffset; -    if (code != TCL_OK) { -	code = TCL_OUT_LINE_COMPILE; -	goto done; + +    /* Stack at this point is empty */ +    TclEmitOpcode(		INST_PUSH_RESULT,		envPtr); +    TclEmitOpcode(		INST_PUSH_RETURN_CODE,		envPtr); + +    /* Stack at this point on both branches: result returnCode */ + +    if (TclFixupForwardJumpToHere(envPtr, &jumpFixup, 127)) { +	Tcl_Panic("TclCompileCatchCmd: bad jump distance %d", +		(int)(CurrentOffset(envPtr) - jumpFixup.codeOffset));      } -    envPtr->exceptArrayPtr[range].numCodeBytes = -	    (envPtr->codeNext - envPtr->codeStart) - startOffset; -		     +      /* -     * The "no errors" epilogue code: store the body's result into the -     * variable (if any), push "0" (TCL_OK) as the catch's "no error" -     * result, and jump around the "error case" code. +     * Push the return options if the caller wants them. This needs to happen +     * before INST_END_CATCH       */ -    if (localIndex != -1) { -	if (localIndex <= 255) { -	    TclEmitInstInt1(INST_STORE_SCALAR1, localIndex, envPtr); -	} else { -	    TclEmitInstInt4(INST_STORE_SCALAR4, localIndex, envPtr); -	} +    if (optsIndex != -1) { +	TclEmitOpcode(		INST_PUSH_RETURN_OPTIONS,	envPtr);      } -    TclEmitOpcode(INST_POP, envPtr); -    TclEmitPush(TclRegisterNewLiteral(envPtr, "0", 1), envPtr); -    TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &jumpFixup);      /* -     * The "error case" code: store the body's result into the variable (if -     * any), then push the error result code. The initial PC offset here is -     * the catch's error target. +     * End the catch       */ -    envPtr->currStackDepth = savedStackDepth; -    envPtr->exceptArrayPtr[range].catchOffset = -	    (envPtr->codeNext - envPtr->codeStart); -    if (localIndex != -1) { -	TclEmitOpcode(INST_PUSH_RESULT, envPtr); -	if (localIndex <= 255) { -	    TclEmitInstInt1(INST_STORE_SCALAR1, localIndex, envPtr); -	} else { -	    TclEmitInstInt4(INST_STORE_SCALAR4, localIndex, envPtr); -	} -	TclEmitOpcode(INST_POP, envPtr); -    } -    TclEmitOpcode(INST_PUSH_RETURN_CODE, envPtr); +    TclEmitOpcode(		INST_END_CATCH,			envPtr); + +    /* +     * Save the result and return options if the caller wants them. This needs +     * to happen after INST_END_CATCH (compile-3.6/7). +     */ +    if (optsIndex != -1) { +	Emit14Inst(		INST_STORE_SCALAR, optsIndex,	envPtr); +	TclEmitOpcode(		INST_POP,			envPtr); +    }      /* -     * Update the target of the jump after the "no errors" code, then emit -     * an endCatch instruction at the end of the catch command. +     * At this point, the top of the stack is inconveniently ordered: +     *		result returnCode +     * Reverse the stack to store the result.       */ -    jumpDist = (envPtr->codeNext - envPtr->codeStart) -	    - jumpFixup.codeOffset; -    if (TclFixupForwardJump(envPtr, &jumpFixup, jumpDist, 127)) { -	panic("TclCompileCatchCmd: bad jump distance %d\n", jumpDist); +    TclEmitInstInt4(	INST_REVERSE, 2,		envPtr); +    if (resultIndex != -1) { +	Emit14Inst(	INST_STORE_SCALAR, resultIndex,	envPtr);      } -    TclEmitOpcode(INST_END_CATCH, envPtr); +    TclEmitOpcode(	INST_POP,			envPtr); -    done: -    envPtr->currStackDepth = savedStackDepth + 1; -    envPtr->exceptDepth--; -    return code; +    TclCheckStackDepth(depth+1, envPtr); +    return TCL_OK;  } -/* - *---------------------------------------------------------------------- +/*----------------------------------------------------------------------   * - * TclCompileContinueCmd -- + * TclCompileClockClicksCmd --   * - *	Procedure called to compile the "continue" command. + *	Procedure called to compile the "tcl::clock::clicks" command.   *   * Results: - *	The return value is a standard Tcl result, which is TCL_OK unless - *	there was an error while parsing string. If an error occurs then - *	the interpreter's result contains a standard error message. + *	Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer + *	evaluation to run time.   *   * Side effects: - *	Instructions are added to envPtr to execute the "continue" command - *	at runtime. + *	Instructions are added to envPtr to execute the "clock clicks" + *	command at runtime.   *   *----------------------------------------------------------------------   */  int -TclCompileContinueCmd(interp, parsePtr, envPtr) -    Tcl_Interp *interp;		/* Used for error reporting. */ -    Tcl_Parse *parsePtr;	/* Points to a parse structure for the -				 * command created by Tcl_ParseCommand. */ -    CompileEnv *envPtr;		/* Holds resulting instructions. */ +TclCompileClockClicksCmd( +    Tcl_Interp* interp,		/* Tcl interpreter */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr)		/* Holds resulting instructions. */  { -    /* -     * There should be no argument after the "continue". -     */ +    Tcl_Token* tokenPtr; + +    switch (parsePtr->numWords) { +    case 1: +	/* +	 * No args +	 */ +	TclEmitInstInt1(INST_CLOCK_READ, 0, envPtr); +	break; +    case 2: +	/* +	 * -milliseconds or -microseconds +	 */ +	tokenPtr = TokenAfter(parsePtr->tokenPtr); +	if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD +	    || tokenPtr[1].size < 4 +	    || tokenPtr[1].size > 13) { +	    return TCL_ERROR; +	} else if (!strncmp(tokenPtr[1].start, "-microseconds", +			    tokenPtr[1].size)) { +	    TclEmitInstInt1(INST_CLOCK_READ, 1, envPtr); +	    break; +	} else if (!strncmp(tokenPtr[1].start, "-milliseconds", +			    tokenPtr[1].size)) { +	    TclEmitInstInt1(INST_CLOCK_READ, 2, envPtr); +	    break; +	} else { +	    return TCL_ERROR; +	} +    default: +	return TCL_ERROR; +    } +    return TCL_OK; +} + +/*---------------------------------------------------------------------- + * + * TclCompileClockReadingCmd -- + * + *	Procedure called to compile the "tcl::clock::microseconds", + *	"tcl::clock::milliseconds" and "tcl::clock::seconds" commands. + * + * Results: + *	Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer + *	evaluation to run time. + * + * Side effects: + *	Instructions are added to envPtr to execute the "clock clicks" + *	command at runtime. + * + * Client data is 1 for microseconds, 2 for milliseconds, 3 for seconds. + *---------------------------------------------------------------------- + */ + +int +TclCompileClockReadingCmd( +    Tcl_Interp* interp,		/* Tcl interpreter */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr)		/* Holds resulting instructions. */ +{      if (parsePtr->numWords != 1) { -	Tcl_ResetResult(interp); -	Tcl_AppendToObj(Tcl_GetObjResult(interp), -	        "wrong # args: should be \"continue\"", -1);  	return TCL_ERROR;      } -    /* -     * Emit a continue instruction. -     */ +    TclEmitInstInt1(INST_CLOCK_READ, PTR2INT(cmdPtr->objClientData), envPtr); -    TclEmitOpcode(INST_CONTINUE, envPtr);      return TCL_OK;  }  /*   *----------------------------------------------------------------------   * - * TclCompileExprCmd -- + * TclCompileConcatCmd --   * - *	Procedure called to compile the "expr" command. + *	Procedure called to compile the "concat" command.   *   * Results: - *	The return value is a standard Tcl result, which is TCL_OK - *	unless there was an error while parsing string. If an error occurs - *	then the interpreter's result contains a standard error message. + *	Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer + *	evaluation to runtime.   *   * Side effects: - *	Instructions are added to envPtr to execute the "expr" command - *	at runtime. + *	Instructions are added to envPtr to execute the "concat" command at + *	runtime.   *   *----------------------------------------------------------------------   */  int -TclCompileExprCmd(interp, parsePtr, envPtr) -    Tcl_Interp *interp;		/* Used for error reporting. */ -    Tcl_Parse *parsePtr;	/* Points to a parse structure for the -				 * command created by Tcl_ParseCommand. */ -    CompileEnv *envPtr;		/* Holds resulting instructions. */ +TclCompileConcatCmd( +    Tcl_Interp *interp,		/* Used for error reporting. */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr)		/* Holds resulting instructions. */  { -    Tcl_Token *firstWordPtr; +    DefineLineInformation;	/* TIP #280 */ +    Tcl_Obj *objPtr, *listObj; +    Tcl_Token *tokenPtr; +    int i; +    /* TODO: Consider compiling expansion case. */      if (parsePtr->numWords == 1) { -	Tcl_ResetResult(interp); -	Tcl_AppendToObj(Tcl_GetObjResult(interp), -	        "wrong # args: should be \"expr arg ?arg ...?\"", -1); -        return TCL_ERROR; +	/* +	 * [concat] without arguments just pushes an empty object. +	 */ + +	PushStringLiteral(envPtr, ""); +	return TCL_OK; +    } + +    /* +     * Test if all arguments are compile-time known. If they are, we can +     * implement with a simple push. +     */ + +    listObj = Tcl_NewObj(); +    for (i = 1, tokenPtr = parsePtr->tokenPtr; i < parsePtr->numWords; i++) { +	tokenPtr = TokenAfter(tokenPtr); +	objPtr = Tcl_NewObj(); +	if (!TclWordKnownAtCompileTime(tokenPtr, objPtr)) { +	    Tcl_DecrRefCount(objPtr); +	    Tcl_DecrRefCount(listObj); +	    listObj = NULL; +	    break; +	} +	(void) Tcl_ListObjAppendElement(NULL, listObj, objPtr); +    } +    if (listObj != NULL) { +	Tcl_Obj **objs; +	const char *bytes; +	int len; + +	Tcl_ListObjGetElements(NULL, listObj, &len, &objs); +	objPtr = Tcl_ConcatObj(len, objs); +	Tcl_DecrRefCount(listObj); +	bytes = TclGetStringFromObj(objPtr, &len); +	PushLiteral(envPtr, bytes, len); +	Tcl_DecrRefCount(objPtr); +	return TCL_OK; +    } + +    /* +     * General case: runtime concat. +     */ + +    for (i = 1, tokenPtr = parsePtr->tokenPtr; i < parsePtr->numWords; i++) { +	tokenPtr = TokenAfter(tokenPtr); +	CompileWord(envPtr, tokenPtr, interp, i);      } -    firstWordPtr = parsePtr->tokenPtr -	    + (parsePtr->tokenPtr->numComponents + 1); -    return TclCompileExprWords(interp, firstWordPtr, (parsePtr->numWords-1), -	    envPtr); +    TclEmitInstInt4(	INST_CONCAT_STK, i-1,		envPtr); + +    return TCL_OK;  }  /*   *----------------------------------------------------------------------   * - * TclCompileForCmd -- + * TclCompileContinueCmd --   * - *	Procedure called to compile the "for" command. + *	Procedure called to compile the "continue" command.   *   * Results: - *	The return value is a standard Tcl result, which is TCL_OK unless - *	there was an error while parsing string. If an error occurs then - *	the interpreter's result contains a standard error message. + *	Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer + *	evaluation to runtime.   *   * Side effects: - *	Instructions are added to envPtr to execute the "for" command - *	at runtime. + *	Instructions are added to envPtr to execute the "continue" command at + *	runtime.   *   *----------------------------------------------------------------------   */ +  int -TclCompileForCmd(interp, parsePtr, envPtr) -    Tcl_Interp *interp;		/* Used for error reporting. */ -    Tcl_Parse *parsePtr;	/* Points to a parse structure for the -				 * command created by Tcl_ParseCommand. */ -    CompileEnv *envPtr;		/* Holds resulting instructions. */ +TclCompileContinueCmd( +    Tcl_Interp *interp,		/* Used for error reporting. */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr)		/* Holds resulting instructions. */  { -    Tcl_Token *startTokenPtr, *testTokenPtr, *nextTokenPtr, *bodyTokenPtr; -    JumpFixup jumpEvalCondFixup; -    int testCodeOffset, bodyCodeOffset, nextCodeOffset, jumpDist; -    int bodyRange, nextRange, code; -    char buffer[32 + TCL_INTEGER_SPACE]; -    int savedStackDepth = envPtr->currStackDepth; - -    if (parsePtr->numWords != 5) { -	Tcl_ResetResult(interp); -	Tcl_AppendToObj(Tcl_GetObjResult(interp), -	        "wrong # args: should be \"for start test next command\"", -1); -	return TCL_ERROR; -    } +    ExceptionRange *rangePtr; +    ExceptionAux *auxPtr;      /* -     * If the test expression requires substitutions, don't compile the for -     * command inline. E.g., the expression might cause the loop to never -     * execute or execute forever, as in "for {} "$x > 5" {incr x} {}". +     * There should be no argument after the "continue".       */ -    startTokenPtr = parsePtr->tokenPtr -	    + (parsePtr->tokenPtr->numComponents + 1); -    testTokenPtr = startTokenPtr + (startTokenPtr->numComponents + 1); -    if (testTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { -	return TCL_OUT_LINE_COMPILE; +    if (parsePtr->numWords != 1) { +	return TCL_ERROR;      }      /* -     * Bail out also if the body or the next expression require substitutions -     * in order to insure correct behaviour [Bug 219166] +     * See if we can find a valid continueOffset (i.e., not -1) in the +     * innermost containing exception range.       */ -    nextTokenPtr = testTokenPtr + (testTokenPtr->numComponents + 1); -    bodyTokenPtr = nextTokenPtr + (nextTokenPtr->numComponents + 1); -    if ((nextTokenPtr->type != TCL_TOKEN_SIMPLE_WORD)  -	    || (bodyTokenPtr->type != TCL_TOKEN_SIMPLE_WORD)) { -	return TCL_OUT_LINE_COMPILE; +    rangePtr = TclGetInnermostExceptionRange(envPtr, TCL_CONTINUE, &auxPtr); +    if (rangePtr && rangePtr->type == LOOP_EXCEPTION_RANGE) { +	/* +	 * Found the target! No need for a nasty INST_CONTINUE here. +	 */ + +	TclCleanupStackForBreakContinue(envPtr, auxPtr); +	TclAddLoopContinueFixup(envPtr, auxPtr); +    } else { +	/* +	 * Emit a real continue. +	 */ + +	TclEmitOpcode(INST_CONTINUE, envPtr);      } +    TclAdjustStackDepth(1, envPtr); + +    return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TclCompileDict*Cmd -- + * + *	Functions called to compile "dict" sucommands. + * + * Results: + *	All return TCL_OK for a successful compile, and TCL_ERROR to defer + *	evaluation to runtime. + * + * Side effects: + *	Instructions are added to envPtr to execute the "dict" subcommand at + *	runtime. + * + *---------------------------------------------------------------------- + */ + +int +TclCompileDictSetCmd( +    Tcl_Interp *interp,		/* Used for looking up stuff. */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr)		/* Holds resulting instructions. */ +{ +    Tcl_Token *tokenPtr; +    int i, dictVarIndex; +    DefineLineInformation;	/* TIP #280 */ +    Tcl_Token *varTokenPtr;      /* -     * Create ExceptionRange records for the body and the "next" command. -     * The "next" command's ExceptionRange supports break but not continue -     * (and has a -1 continueOffset). +     * There must be at least one argument after the command.       */ -    envPtr->exceptDepth++; -    envPtr->maxExceptDepth = -	    TclMax(envPtr->exceptDepth, envPtr->maxExceptDepth); -    bodyRange = TclCreateExceptRange(LOOP_EXCEPTION_RANGE, envPtr); -    nextRange = TclCreateExceptRange(LOOP_EXCEPTION_RANGE, envPtr); +    if (parsePtr->numWords < 4) { +	return TCL_ERROR; +    }      /* -     * Inline compile the initial command. +     * The dictionary variable must be a local scalar that is knowable at +     * compile time; anything else exceeds the complexity of the opcode. So +     * discover what the index is.       */ -    code = TclCompileCmdWord(interp, startTokenPtr+1, -	    startTokenPtr->numComponents, envPtr); -    if (code != TCL_OK) { -	if (code == TCL_ERROR) { -            Tcl_AddObjErrorInfo(interp, -	            "\n    (\"for\" initial command)", -1); -        } -	goto done; +    varTokenPtr = TokenAfter(parsePtr->tokenPtr); +    dictVarIndex = LocalScalarFromToken(varTokenPtr, envPtr); +    if (dictVarIndex < 0) { +	return TCL_ERROR;      } -    TclEmitOpcode(INST_POP, envPtr); -    +      /* -     * Jump to the evaluation of the condition. This code uses the "loop -     * rotation" optimisation (which eliminates one branch from the loop). -     * "for start cond next body" produces then: -     *       start -     *       goto A -     *    B: body                : bodyCodeOffset -     *       next                : nextCodeOffset, continueOffset -     *    A: cond -> result      : testCodeOffset -     *       if (result) goto B +     * Remaining words (key path and value to set) can be handled normally.       */ -    TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &jumpEvalCondFixup); +    tokenPtr = TokenAfter(varTokenPtr); +    for (i=2 ; i< parsePtr->numWords ; i++) { +	CompileWord(envPtr, tokenPtr, interp, i); +	tokenPtr = TokenAfter(tokenPtr); +    }      /* -     * Compile the loop body. +     * Now emit the instruction to do the dict manipulation.       */ -    bodyCodeOffset = (envPtr->codeNext - envPtr->codeStart); - -    code = TclCompileCmdWord(interp, bodyTokenPtr+1, -	    bodyTokenPtr->numComponents, envPtr); -    envPtr->currStackDepth = savedStackDepth + 1; -    if (code != TCL_OK) { -	if (code == TCL_ERROR) { -	    sprintf(buffer, "\n    (\"for\" body line %d)", -		    interp->errorLine); -            Tcl_AddObjErrorInfo(interp, buffer, -1); -        } -	goto done; -    } -    envPtr->exceptArrayPtr[bodyRange].numCodeBytes = -	    (envPtr->codeNext - envPtr->codeStart) - bodyCodeOffset; -    TclEmitOpcode(INST_POP, envPtr); +    TclEmitInstInt4( INST_DICT_SET, parsePtr->numWords-3,	envPtr); +    TclEmitInt4(     dictVarIndex,			envPtr); +    TclAdjustStackDepth(-1, envPtr); +    return TCL_OK; +} +int +TclCompileDictIncrCmd( +    Tcl_Interp *interp,		/* Used for looking up stuff. */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr)		/* Holds resulting instructions. */ +{ +    DefineLineInformation;	/* TIP #280 */ +    Tcl_Token *varTokenPtr, *keyTokenPtr; +    int dictVarIndex, incrAmount;      /* -     * Compile the "next" subcommand. +     * There must be at least two arguments after the command.       */ -    nextCodeOffset = (envPtr->codeNext - envPtr->codeStart); - -    envPtr->currStackDepth = savedStackDepth; -    code = TclCompileCmdWord(interp, nextTokenPtr+1, -	    nextTokenPtr->numComponents, envPtr); -    envPtr->currStackDepth = savedStackDepth + 1; -    if (code != TCL_OK) { -	if (code == TCL_ERROR) { -	    Tcl_AddObjErrorInfo(interp, -	            "\n    (\"for\" loop-end command)", -1); -	} -	goto done; +    if (parsePtr->numWords < 3 || parsePtr->numWords > 4) { +	return TCL_ERROR;      } -    envPtr->exceptArrayPtr[nextRange].numCodeBytes = -	    (envPtr->codeNext - envPtr->codeStart) -	    - nextCodeOffset; -    TclEmitOpcode(INST_POP, envPtr); -    envPtr->currStackDepth = savedStackDepth; +    varTokenPtr = TokenAfter(parsePtr->tokenPtr); +    keyTokenPtr = TokenAfter(varTokenPtr);      /* -     * Compile the test expression then emit the conditional jump that -     * terminates the for. +     * Parse the increment amount, if present.       */ -    testCodeOffset = (envPtr->codeNext - envPtr->codeStart); +    if (parsePtr->numWords == 4) { +	const char *word; +	int numBytes, code; +	Tcl_Token *incrTokenPtr; +	Tcl_Obj *intObj; -    jumpDist = testCodeOffset - jumpEvalCondFixup.codeOffset; -    if (TclFixupForwardJump(envPtr, &jumpEvalCondFixup, jumpDist, 127)) { -	bodyCodeOffset += 3; -	nextCodeOffset += 3; -	testCodeOffset += 3; -    } -     -    envPtr->currStackDepth = savedStackDepth; -    code = TclCompileExprWords(interp, testTokenPtr, 1, envPtr); -    if (code != TCL_OK) { -	if (code == TCL_ERROR) { -	    Tcl_AddObjErrorInfo(interp, -				"\n    (\"for\" test expression)", -1); +	incrTokenPtr = TokenAfter(keyTokenPtr); +	if (incrTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { +	    return TclCompileBasic2Or3ArgCmd(interp, parsePtr,cmdPtr, envPtr); +	} +	word = incrTokenPtr[1].start; +	numBytes = incrTokenPtr[1].size; + +	intObj = Tcl_NewStringObj(word, numBytes); +	Tcl_IncrRefCount(intObj); +	code = TclGetIntFromObj(NULL, intObj, &incrAmount); +	TclDecrRefCount(intObj); +	if (code != TCL_OK) { +	    return TclCompileBasic2Or3ArgCmd(interp, parsePtr,cmdPtr, envPtr);  	} -	goto done; -    } -    envPtr->currStackDepth = savedStackDepth + 1; -     -    jumpDist = (envPtr->codeNext - envPtr->codeStart) - bodyCodeOffset; -    if (jumpDist > 127) { -	TclEmitInstInt4(INST_JUMP_TRUE4, -jumpDist, envPtr);      } else { -	TclEmitInstInt1(INST_JUMP_TRUE1, -jumpDist, envPtr); +	incrAmount = 1;      } -     +      /* -     * Set the loop's offsets and break target. +     * The dictionary variable must be a local scalar that is knowable at +     * compile time; anything else exceeds the complexity of the opcode. So +     * discover what the index is.       */ -    envPtr->exceptArrayPtr[bodyRange].codeOffset = bodyCodeOffset; -    envPtr->exceptArrayPtr[bodyRange].continueOffset = nextCodeOffset; - -    envPtr->exceptArrayPtr[nextRange].codeOffset = nextCodeOffset; +    dictVarIndex = LocalScalarFromToken(varTokenPtr, envPtr); +    if (dictVarIndex < 0) { +	return TclCompileBasic2Or3ArgCmd(interp, parsePtr, cmdPtr, envPtr); +    } -    envPtr->exceptArrayPtr[bodyRange].breakOffset = -            envPtr->exceptArrayPtr[nextRange].breakOffset = -	    (envPtr->codeNext - envPtr->codeStart); -          /* -     * The for command's result is an empty string. +     * Emit the key and the code to actually do the increment.       */ -    envPtr->currStackDepth = savedStackDepth; -    TclEmitPush(TclRegisterNewLiteral(envPtr, "", 0), envPtr); -    code = TCL_OK; - -    done: -    envPtr->exceptDepth--; -    return code; +    CompileWord(envPtr, keyTokenPtr, interp, 2); +    TclEmitInstInt4( INST_DICT_INCR_IMM, incrAmount,	envPtr); +    TclEmitInt4(     dictVarIndex,			envPtr); +    return TCL_OK;  } - -/* - *---------------------------------------------------------------------- - * - * TclCompileForeachCmd -- - * - *	Procedure called to compile the "foreach" command. - * - * Results: - *	The return value is a standard Tcl result, which is TCL_OK if - *	compilation was successful. If an error occurs then the - *	interpreter's result contains a standard error message and TCL_ERROR - *	is returned. If the command is too complex for TclCompileForeachCmd, - *	TCL_OUT_LINE_COMPILE is returned indicating that the foreach command - *	should be compiled "out of line" by emitting code to invoke its - *	command procedure at runtime. - * - * Side effects: - *	Instructions are added to envPtr to execute the "foreach" command - *	at runtime. - * -n*---------------------------------------------------------------------- - */  int -TclCompileForeachCmd(interp, parsePtr, envPtr) -    Tcl_Interp *interp;		/* Used for error reporting. */ -    Tcl_Parse *parsePtr;	/* Points to a parse structure for the -				 * command created by Tcl_ParseCommand. */ -    CompileEnv *envPtr;		/* Holds resulting instructions. */ +TclCompileDictGetCmd( +    Tcl_Interp *interp,		/* Used for looking up stuff. */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr)		/* Holds resulting instructions. */  { -    Proc *procPtr = envPtr->procPtr; -    ForeachInfo *infoPtr;	/* Points to the structure describing this -				 * foreach command. Stored in a AuxData -				 * record in the ByteCode. */ -    int firstValueTemp;		/* Index of the first temp var in the frame -				 * used to point to a value list. */ -    int loopCtTemp;		/* Index of temp var holding the loop's -				 * iteration count. */ -    Tcl_Token *tokenPtr, *bodyTokenPtr; -    unsigned char *jumpPc; -    JumpFixup jumpFalseFixup; -    int jumpDist, jumpBackDist, jumpBackOffset, infoIndex, range; -    int numWords, numLists, numVars, loopIndex, tempVar, i, j, code; -    char buffer[32 + TCL_INTEGER_SPACE]; -    int savedStackDepth = envPtr->currStackDepth; +    Tcl_Token *tokenPtr; +    int i; +    DefineLineInformation;	/* TIP #280 */      /* -     * We parse the variable list argument words and create two arrays: -     *    varcList[i] is number of variables in i-th var list -     *    varvList[i] points to array of var names in i-th var list +     * There must be at least two arguments after the command (the single-arg +     * case is legal, but too special and magic for us to deal with here).       */ -#define STATIC_VAR_LIST_SIZE 5 -    int varcListStaticSpace[STATIC_VAR_LIST_SIZE]; -    CONST char **varvListStaticSpace[STATIC_VAR_LIST_SIZE]; -    int *varcList = varcListStaticSpace; -    CONST char ***varvList = varvListStaticSpace; +    /* TODO: Consider support for compiling expanded args. */ +    if (parsePtr->numWords < 3) { +	return TCL_ERROR; +    } +    tokenPtr = TokenAfter(parsePtr->tokenPtr);      /* -     * If the foreach command isn't in a procedure, don't compile it inline: -     * the payoff is too small. +     * Only compile this because we need INST_DICT_GET anyway.       */ -    if (procPtr == NULL) { -	return TCL_OUT_LINE_COMPILE; +    for (i=1 ; i<parsePtr->numWords ; i++) { +	CompileWord(envPtr, tokenPtr, interp, i); +	tokenPtr = TokenAfter(tokenPtr);      } +    TclEmitInstInt4(INST_DICT_GET, parsePtr->numWords-2, envPtr); +    TclAdjustStackDepth(-1, envPtr); +    return TCL_OK; +} -    numWords = parsePtr->numWords; -    if ((numWords < 4) || (numWords%2 != 0)) { -	Tcl_ResetResult(interp); -	Tcl_AppendToObj(Tcl_GetObjResult(interp), -	        "wrong # args: should be \"foreach varList list ?varList list ...? command\"", -1); -        return TCL_ERROR; -    } +int +TclCompileDictExistsCmd( +    Tcl_Interp *interp,		/* Used for looking up stuff. */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr)		/* Holds resulting instructions. */ +{ +    Tcl_Token *tokenPtr; +    int i; +    DefineLineInformation;	/* TIP #280 */      /* -     * Bail out if the body requires substitutions -     * in order to insure correct behaviour [Bug 219166] +     * There must be at least two arguments after the command (the single-arg +     * case is legal, but too special and magic for us to deal with here).       */ -    for (i = 0, tokenPtr = parsePtr->tokenPtr; -	    i < numWords-1; -	    i++, tokenPtr += (tokenPtr->numComponents + 1)) { -    } -    bodyTokenPtr = tokenPtr; -    if (bodyTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { -	return TCL_OUT_LINE_COMPILE; + +    /* TODO: Consider support for compiling expanded args. */ +    if (parsePtr->numWords < 3) { +	return TCL_ERROR;      } +    tokenPtr = TokenAfter(parsePtr->tokenPtr);      /* -     * Allocate storage for the varcList and varvList arrays if necessary. +     * Now we do the code generation.       */ -    numLists = (numWords - 2)/2; -    if (numLists > STATIC_VAR_LIST_SIZE) { -        varcList = (int *) ckalloc(numLists * sizeof(int)); -        varvList = (CONST char ***) ckalloc(numLists * sizeof(CONST char **)); +    for (i=1 ; i<parsePtr->numWords ; i++) { +	CompileWord(envPtr, tokenPtr, interp, i); +	tokenPtr = TokenAfter(tokenPtr);      } -    for (loopIndex = 0;  loopIndex < numLists;  loopIndex++) { -        varcList[loopIndex] = 0; -        varvList[loopIndex] = NULL; -    } -     -    /* -     * Set the exception stack depth. -     */  +    TclEmitInstInt4(INST_DICT_EXISTS, parsePtr->numWords-2, envPtr); +    TclAdjustStackDepth(-1, envPtr); +    return TCL_OK; +} -    envPtr->exceptDepth++; -    envPtr->maxExceptDepth = -	TclMax(envPtr->exceptDepth, envPtr->maxExceptDepth); +int +TclCompileDictUnsetCmd( +    Tcl_Interp *interp,		/* Used for looking up stuff. */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr)		/* Holds resulting instructions. */ +{ +    Tcl_Token *tokenPtr; +    DefineLineInformation;	/* TIP #280 */ +    int i, dictVarIndex;      /* -     * Break up each var list and set the varcList and varvList arrays. -     * Don't compile the foreach inline if any var name needs substitutions -     * or isn't a scalar, or if any var list needs substitutions. +     * There must be at least one argument after the variable name for us to +     * compile to bytecode.       */ -    loopIndex = 0; -    for (i = 0, tokenPtr = parsePtr->tokenPtr; -	    i < numWords-1; -	    i++, tokenPtr += (tokenPtr->numComponents + 1)) { -	if (i%2 == 1) { -	    if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { -		code = TCL_OUT_LINE_COMPILE; -		goto done; -	    } else { -		/* Lots of copying going on here.  Need a ListObj wizard -		 * to show a better way. */ - -		Tcl_DString varList; - -		Tcl_DStringInit(&varList); -		Tcl_DStringAppend(&varList, tokenPtr[1].start, -			tokenPtr[1].size); -		code = Tcl_SplitList(interp, Tcl_DStringValue(&varList), -			&varcList[loopIndex], &varvList[loopIndex]); -		Tcl_DStringFree(&varList); -		if (code != TCL_OK) { -		    goto done; -		} -		numVars = varcList[loopIndex]; -		for (j = 0;  j < numVars;  j++) { -		    CONST char *varName = varvList[loopIndex][j]; -		    if (!TclIsLocalScalar(varName, (int) strlen(varName))) { -			code = TCL_OUT_LINE_COMPILE; -			goto done; -		    } -		} -	    } -	    loopIndex++; -	} +    /* TODO: Consider support for compiling expanded args. */ +    if (parsePtr->numWords < 3) { +	return TCL_ERROR;      }      /* -     * We will compile the foreach command. -     * Reserve (numLists + 1) temporary variables: -     *    - numLists temps to hold each value list -     *    - 1 temp for the loop counter (index of next element in each list) -     * At this time we don't try to reuse temporaries; if there are two -     * nonoverlapping foreach loops, they don't share any temps. -     */ - -    firstValueTemp = -1; -    for (loopIndex = 0;  loopIndex < numLists;  loopIndex++) { -	tempVar = TclFindCompiledLocal(NULL, /*nameChars*/ 0, -		/*create*/ 1, /*flags*/ VAR_SCALAR, procPtr); -	if (loopIndex == 0) { -	    firstValueTemp = tempVar; -	} +     * The dictionary variable must be a local scalar that is knowable at +     * compile time; anything else exceeds the complexity of the opcode. So +     * discover what the index is. +     */ + +    tokenPtr = TokenAfter(parsePtr->tokenPtr); +    dictVarIndex = LocalScalarFromToken(tokenPtr, envPtr); +    if (dictVarIndex < 0) { +	return TclCompileBasicMin2ArgCmd(interp, parsePtr, cmdPtr, envPtr);      } -    loopCtTemp = TclFindCompiledLocal(NULL, /*nameChars*/ 0, -	    /*create*/ 1, /*flags*/ VAR_SCALAR, procPtr); -     +      /* -     * Create and initialize the ForeachInfo and ForeachVarList data -     * structures describing this command. Then create a AuxData record -     * pointing to the ForeachInfo structure. +     * Remaining words (the key path) can be handled normally.       */ -    infoPtr = (ForeachInfo *) ckalloc((unsigned) -	    (sizeof(ForeachInfo) + (numLists * sizeof(ForeachVarList *)))); -    infoPtr->numLists = numLists; -    infoPtr->firstValueTemp = firstValueTemp; -    infoPtr->loopCtTemp = loopCtTemp; -    for (loopIndex = 0;  loopIndex < numLists;  loopIndex++) { -	ForeachVarList *varListPtr; -	numVars = varcList[loopIndex]; -	varListPtr = (ForeachVarList *) ckalloc((unsigned) -	        sizeof(ForeachVarList) + (numVars * sizeof(int))); -	varListPtr->numVars = numVars; -	for (j = 0;  j < numVars;  j++) { -	    CONST char *varName = varvList[loopIndex][j]; -	    int nameChars = strlen(varName); -	    varListPtr->varIndexes[j] = TclFindCompiledLocal(varName, -		    nameChars, /*create*/ 1, /*flags*/ VAR_SCALAR, procPtr); -	} -	infoPtr->varLists[loopIndex] = varListPtr; +    for (i=2 ; i<parsePtr->numWords ; i++) { +	tokenPtr = TokenAfter(tokenPtr); +	CompileWord(envPtr, tokenPtr, interp, i);      } -    infoIndex = TclCreateAuxData((ClientData) infoPtr, &tclForeachInfoType, envPtr);      /* -     * Evaluate then store each value list in the associated temporary. +     * Now emit the instruction to do the dict manipulation.       */ -    range = TclCreateExceptRange(LOOP_EXCEPTION_RANGE, envPtr); -     -    loopIndex = 0; -    for (i = 0, tokenPtr = parsePtr->tokenPtr; -	    i < numWords-1; -	    i++, tokenPtr += (tokenPtr->numComponents + 1)) { -	if ((i%2 == 0) && (i > 0)) { -	    code = TclCompileTokens(interp, tokenPtr+1, -		    tokenPtr->numComponents, envPtr); -	    if (code != TCL_OK) { -		goto done; -	    } +    TclEmitInstInt4( INST_DICT_UNSET, parsePtr->numWords-2,	envPtr); +    TclEmitInt4(	dictVarIndex,				envPtr); +    return TCL_OK; +} -	    tempVar = (firstValueTemp + loopIndex); -	    if (tempVar <= 255) { -		TclEmitInstInt1(INST_STORE_SCALAR1, tempVar, envPtr); -	    } else { -		TclEmitInstInt4(INST_STORE_SCALAR4, tempVar, envPtr); -	    } -	    TclEmitOpcode(INST_POP, envPtr); -	    loopIndex++; +int +TclCompileDictCreateCmd( +    Tcl_Interp *interp,		/* Used for looking up stuff. */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr)		/* Holds resulting instructions. */ +{ +    DefineLineInformation;	/* TIP #280 */ +    int worker;			/* Temp var for building the value in. */ +    Tcl_Token *tokenPtr; +    Tcl_Obj *keyObj, *valueObj, *dictObj; +    const char *bytes; +    int i, len; + +    if ((parsePtr->numWords & 1) == 0) { +	return TCL_ERROR; +    } + +    /* +     * See if we can build the value at compile time... +     */ + +    tokenPtr = TokenAfter(parsePtr->tokenPtr); +    dictObj = Tcl_NewObj(); +    Tcl_IncrRefCount(dictObj); +    for (i=1 ; i<parsePtr->numWords ; i+=2) { +	keyObj = Tcl_NewObj(); +	Tcl_IncrRefCount(keyObj); +	if (!TclWordKnownAtCompileTime(tokenPtr, keyObj)) { +	    Tcl_DecrRefCount(keyObj); +	    Tcl_DecrRefCount(dictObj); +	    goto nonConstant;  	} +	tokenPtr = TokenAfter(tokenPtr); +	valueObj = Tcl_NewObj(); +	Tcl_IncrRefCount(valueObj); +	if (!TclWordKnownAtCompileTime(tokenPtr, valueObj)) { +	    Tcl_DecrRefCount(keyObj); +	    Tcl_DecrRefCount(valueObj); +	    Tcl_DecrRefCount(dictObj); +	    goto nonConstant; +	} +	tokenPtr = TokenAfter(tokenPtr); +	Tcl_DictObjPut(NULL, dictObj, keyObj, valueObj); +	Tcl_DecrRefCount(keyObj); +	Tcl_DecrRefCount(valueObj);      }      /* -     * Initialize the temporary var that holds the count of loop iterations. +     * We did! Excellent. The "verifyDict" is to do type forcing.       */ -    TclEmitInstInt4(INST_FOREACH_START4, infoIndex, envPtr); -     +    bytes = TclGetStringFromObj(dictObj, &len); +    PushLiteral(envPtr, bytes, len); +    TclEmitOpcode(		INST_DUP,			envPtr); +    TclEmitOpcode(		INST_DICT_VERIFY,		envPtr); +    Tcl_DecrRefCount(dictObj); +    return TCL_OK; +      /* -     * Top of loop code: assign each loop variable and check whether -     * to terminate the loop. +     * Otherwise, we've got to issue runtime code to do the building, which we +     * do by [dict set]ting into an unnamed local variable. This requires that +     * we are in a context with an LVT.       */ -    envPtr->exceptArrayPtr[range].continueOffset = -	    (envPtr->codeNext - envPtr->codeStart); -    TclEmitInstInt4(INST_FOREACH_STEP4, infoIndex, envPtr); -    TclEmitForwardJump(envPtr, TCL_FALSE_JUMP, &jumpFalseFixup); -     +  nonConstant: +    worker = AnonymousLocal(envPtr); +    if (worker < 0) { +	return TclCompileBasicMin0ArgCmd(interp, parsePtr, cmdPtr, envPtr); +    } + +    PushStringLiteral(envPtr,		""); +    Emit14Inst(			INST_STORE_SCALAR, worker,	envPtr); +    TclEmitOpcode(		INST_POP,			envPtr); +    tokenPtr = TokenAfter(parsePtr->tokenPtr); +    for (i=1 ; i<parsePtr->numWords ; i+=2) { +	CompileWord(envPtr, tokenPtr, interp, i); +	tokenPtr = TokenAfter(tokenPtr); +	CompileWord(envPtr, tokenPtr, interp, i+1); +	tokenPtr = TokenAfter(tokenPtr); +	TclEmitInstInt4(	INST_DICT_SET, 1,		envPtr); +	TclEmitInt4(			worker,			envPtr); +	TclAdjustStackDepth(-1, envPtr); +	TclEmitOpcode(		INST_POP,			envPtr); +    } +    Emit14Inst(			INST_LOAD_SCALAR, worker,	envPtr); +    TclEmitInstInt1(		INST_UNSET_SCALAR, 0,		envPtr); +    TclEmitInt4(			worker,			envPtr); +    return TCL_OK; +} + +int +TclCompileDictMergeCmd( +    Tcl_Interp *interp,		/* Used for looking up stuff. */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr)		/* Holds resulting instructions. */ +{ +    DefineLineInformation;	/* TIP #280 */ +    Tcl_Token *tokenPtr; +    int i, workerIndex, infoIndex, outLoop; +      /* -     * Inline compile the loop body. +     * Deal with some special edge cases. Note that in the case with one +     * argument, the only thing to do is to verify the dict-ness.       */ -    envPtr->exceptArrayPtr[range].codeOffset = -	    (envPtr->codeNext - envPtr->codeStart); -    code = TclCompileCmdWord(interp, bodyTokenPtr+1, -	    bodyTokenPtr->numComponents, envPtr); -    envPtr->currStackDepth = savedStackDepth + 1; -    if (code != TCL_OK) { -	if (code == TCL_ERROR) { -	    sprintf(buffer, "\n    (\"foreach\" body line %d)", -		    interp->errorLine); -            Tcl_AddObjErrorInfo(interp, buffer, -1); -        } -	goto done; +    /* TODO: Consider support for compiling expanded args. (less likely) */ +    if (parsePtr->numWords < 2) { +	PushStringLiteral(envPtr, ""); +	return TCL_OK; +    } else if (parsePtr->numWords == 2) { +	tokenPtr = TokenAfter(parsePtr->tokenPtr); +	CompileWord(envPtr, tokenPtr, interp, 1); +	TclEmitOpcode(		INST_DUP,			envPtr); +	TclEmitOpcode(		INST_DICT_VERIFY,		envPtr); +	return TCL_OK;      } -    envPtr->exceptArrayPtr[range].numCodeBytes = -	    (envPtr->codeNext - envPtr->codeStart) -	    - envPtr->exceptArrayPtr[range].codeOffset; -    TclEmitOpcode(INST_POP, envPtr); -	 +      /* -     * Jump back to the test at the top of the loop. Generate a 4 byte jump -     * if the distance to the test is > 120 bytes. This is conservative and -     * ensures that we won't have to replace this jump if we later need to -     * replace the ifFalse jump with a 4 byte jump. +     * There's real merging work to do. +     * +     * Allocate some working space. This means we'll only ever compile this +     * command when there's an LVT present.       */ -    jumpBackOffset = (envPtr->codeNext - envPtr->codeStart); -    jumpBackDist = -	(jumpBackOffset - envPtr->exceptArrayPtr[range].continueOffset); -    if (jumpBackDist > 120) { -	TclEmitInstInt4(INST_JUMP4, -jumpBackDist, envPtr); -    } else { -	TclEmitInstInt1(INST_JUMP1, -jumpBackDist, envPtr); +    workerIndex = AnonymousLocal(envPtr); +    if (workerIndex < 0) { +	return TclCompileBasicMin2ArgCmd(interp, parsePtr, cmdPtr, envPtr);      } +    infoIndex = AnonymousLocal(envPtr);      /* -     * Fix the target of the jump after the foreach_step test. +     * Get the first dictionary and verify that it is so.       */ -    jumpDist = (envPtr->codeNext - envPtr->codeStart) -	    - jumpFalseFixup.codeOffset; -    if (TclFixupForwardJump(envPtr, &jumpFalseFixup, jumpDist, 127)) { -	/* -	 * Update the loop body's starting PC offset since it moved down. -	 */ +    tokenPtr = TokenAfter(parsePtr->tokenPtr); +    CompileWord(envPtr, tokenPtr, interp, 1); +    TclEmitOpcode(		INST_DUP,			envPtr); +    TclEmitOpcode(		INST_DICT_VERIFY,		envPtr); +    Emit14Inst(			INST_STORE_SCALAR, workerIndex,	envPtr); +    TclEmitOpcode(		INST_POP,			envPtr); -	envPtr->exceptArrayPtr[range].codeOffset += 3; +    /* +     * For each of the remaining dictionaries... +     */ +    outLoop = TclCreateExceptRange(CATCH_EXCEPTION_RANGE, envPtr); +    TclEmitInstInt4(		INST_BEGIN_CATCH4, outLoop,	envPtr); +    ExceptionRangeStarts(envPtr, outLoop); +    for (i=2 ; i<parsePtr->numWords ; i++) {  	/* -	 * Update the jump back to the test at the top of the loop since it -	 * also moved down 3 bytes. +	 * Get the dictionary, and merge its pairs into the first dict (using +	 * a small loop).  	 */ -	jumpBackOffset += 3; -	jumpPc = (envPtr->codeStart + jumpBackOffset); -	jumpBackDist += 3; -	if (jumpBackDist > 120) { -	    TclUpdateInstInt4AtPc(INST_JUMP4, -jumpBackDist, jumpPc); -	} else { -	    TclUpdateInstInt1AtPc(INST_JUMP1, -jumpBackDist, jumpPc); -	} +	tokenPtr = TokenAfter(tokenPtr); +	CompileWord(envPtr, tokenPtr, interp, i); +	TclEmitInstInt4(	INST_DICT_FIRST, infoIndex,	envPtr); +	TclEmitInstInt1(	INST_JUMP_TRUE1, 24,		envPtr); +	TclEmitInstInt4(	INST_REVERSE, 2,		envPtr); +	TclEmitInstInt4(	INST_DICT_SET, 1,		envPtr); +	TclEmitInt4(			workerIndex,		envPtr); +	TclAdjustStackDepth(-1, envPtr); +	TclEmitOpcode(		INST_POP,			envPtr); +	TclEmitInstInt4(	INST_DICT_NEXT, infoIndex,	envPtr); +	TclEmitInstInt1(	INST_JUMP_FALSE1, -20,		envPtr); +	TclEmitOpcode(		INST_POP,			envPtr); +	TclEmitOpcode(		INST_POP,			envPtr); +	TclEmitInstInt1(	INST_UNSET_SCALAR, 0,		envPtr); +	TclEmitInt4(			infoIndex,		envPtr);      } +    ExceptionRangeEnds(envPtr, outLoop); +    TclEmitOpcode(		INST_END_CATCH,			envPtr);      /* -     * Set the loop's break target. +     * Clean up any state left over.       */ -    envPtr->exceptArrayPtr[range].breakOffset = -	    (envPtr->codeNext - envPtr->codeStart); -     +    Emit14Inst(			INST_LOAD_SCALAR, workerIndex,	envPtr); +    TclEmitInstInt1(		INST_UNSET_SCALAR, 0,		envPtr); +    TclEmitInt4(			workerIndex,		envPtr); +    TclEmitInstInt1(		INST_JUMP1, 18,			envPtr); +      /* -     * The foreach command's result is an empty string. +     * If an exception happens when starting to iterate over the second (and +     * subsequent) dicts. This is strictly not necessary, but it is nice.       */ -    envPtr->currStackDepth = savedStackDepth; -    TclEmitPush(TclRegisterNewLiteral(envPtr, "", 0), envPtr); -    envPtr->currStackDepth = savedStackDepth + 1; +    TclAdjustStackDepth(-1, envPtr); +    ExceptionRangeTarget(envPtr, outLoop, catchOffset); +    TclEmitOpcode(		INST_PUSH_RETURN_OPTIONS,	envPtr); +    TclEmitOpcode(		INST_PUSH_RESULT,		envPtr); +    TclEmitOpcode(		INST_END_CATCH,			envPtr); +    TclEmitInstInt1(		INST_UNSET_SCALAR, 0,		envPtr); +    TclEmitInt4(			workerIndex,		envPtr); +    TclEmitInstInt1(		INST_UNSET_SCALAR, 0,		envPtr); +    TclEmitInt4(			infoIndex,		envPtr); +    TclEmitOpcode(		INST_RETURN_STK,		envPtr); -    done: -    for (loopIndex = 0;  loopIndex < numLists;  loopIndex++) { -	if (varvList[loopIndex] != (CONST char **) NULL) { -	    ckfree((char *) varvList[loopIndex]); -	} -    } -    if (varcList != varcListStaticSpace) { -	ckfree((char *) varcList); -        ckfree((char *) varvList); -    } -    envPtr->exceptDepth--; -    return code; +    return TCL_OK;  } - -/* - *---------------------------------------------------------------------- - * - * DupForeachInfo -- - * - *	This procedure duplicates a ForeachInfo structure created as - *	auxiliary data during the compilation of a foreach command. - * - * Results: - *	A pointer to a newly allocated copy of the existing ForeachInfo - *	structure is returned. - * - * Side effects: - *	Storage for the copied ForeachInfo record is allocated. If the - *	original ForeachInfo structure pointed to any ForeachVarList - *	records, these structures are also copied and pointers to them - *	are stored in the new ForeachInfo record. - * - *---------------------------------------------------------------------- - */ -static ClientData -DupForeachInfo(clientData) -    ClientData clientData;	/* The foreach command's compilation -				 * auxiliary data to duplicate. */ +int +TclCompileDictForCmd( +    Tcl_Interp *interp,		/* Used for looking up stuff. */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr)		/* Holds resulting instructions. */  { -    register ForeachInfo *srcPtr = (ForeachInfo *) clientData; -    ForeachInfo *dupPtr; -    register ForeachVarList *srcListPtr, *dupListPtr; -    int numLists = srcPtr->numLists; -    int numVars, i, j; -     -    dupPtr = (ForeachInfo *) ckalloc((unsigned) -	    (sizeof(ForeachInfo) + (numLists * sizeof(ForeachVarList *)))); -    dupPtr->numLists = numLists; -    dupPtr->firstValueTemp = srcPtr->firstValueTemp; -    dupPtr->loopCtTemp = srcPtr->loopCtTemp; -     -    for (i = 0;  i < numLists;  i++) { -	srcListPtr = srcPtr->varLists[i]; -	numVars = srcListPtr->numVars; -	dupListPtr = (ForeachVarList *) ckalloc((unsigned) -	        sizeof(ForeachVarList) + numVars*sizeof(int)); -	dupListPtr->numVars = numVars; -	for (j = 0;  j < numVars;  j++) { -	    dupListPtr->varIndexes[j] =	srcListPtr->varIndexes[j]; -	} -	dupPtr->varLists[i] = dupListPtr; -    } -    return (ClientData) dupPtr; +    return CompileDictEachCmd(interp, parsePtr, cmdPtr, envPtr, +	    TCL_EACH_KEEP_NONE);  } - -/* - *---------------------------------------------------------------------- - * - * FreeForeachInfo -- - * - *	Procedure to free a ForeachInfo structure created as auxiliary data - *	during the compilation of a foreach command. - * - * Results: - *	None. - * - * Side effects: - *	Storage for the ForeachInfo structure pointed to by the ClientData - *	argument is freed as is any ForeachVarList record pointed to by the - *	ForeachInfo structure. - * - *---------------------------------------------------------------------- - */ -static void -FreeForeachInfo(clientData) -    ClientData clientData;	/* The foreach command's compilation -				 * auxiliary data to free. */ +int +TclCompileDictMapCmd( +    Tcl_Interp *interp,		/* Used for looking up stuff. */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr)		/* Holds resulting instructions. */  { -    register ForeachInfo *infoPtr = (ForeachInfo *) clientData; -    register ForeachVarList *listPtr; -    int numLists = infoPtr->numLists; -    register int i; - -    for (i = 0;  i < numLists;  i++) { -	listPtr = infoPtr->varLists[i]; -	ckfree((char *) listPtr); -    } -    ckfree((char *) infoPtr); +    return CompileDictEachCmd(interp, parsePtr, cmdPtr, envPtr, +	    TCL_EACH_COLLECT);  } - -/* - *---------------------------------------------------------------------- - * - * TclCompileIfCmd -- - * - *	Procedure called to compile the "if" command. - * - * Results: - *	The return value is a standard Tcl result, which is TCL_OK if - *	compilation was successful. If an error occurs then the - *	interpreter's result contains a standard error message and TCL_ERROR - *	is returned. If the command is too complex for TclCompileIfCmd, - *	TCL_OUT_LINE_COMPILE is returned indicating that the if command - *	should be compiled "out of line" by emitting code to invoke its - *	command procedure at runtime. - * - * Side effects: - *	Instructions are added to envPtr to execute the "if" command - *	at runtime. - * - *---------------------------------------------------------------------- - */ +  int -TclCompileIfCmd(interp, parsePtr, envPtr) -    Tcl_Interp *interp;		/* Used for error reporting. */ -    Tcl_Parse *parsePtr;	/* Points to a parse structure for the -				 * command created by Tcl_ParseCommand. */ -    CompileEnv *envPtr;		/* Holds resulting instructions. */ +CompileDictEachCmd( +    Tcl_Interp *interp,		/* Used for looking up stuff. */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr,		/* Holds resulting instructions. */ +    int collect)		/* Flag == TCL_EACH_COLLECT to collect and +				 * construct a new dictionary with the loop +				 * body result. */  { -    JumpFixupArray jumpFalseFixupArray; -    				/* Used to fix the ifFalse jump after each -				 * test when its target PC is determined. */ -    JumpFixupArray jumpEndFixupArray; -				/* Used to fix the jump after each "then" -				 * body to the end of the "if" when that PC -				 * is determined. */ -    Tcl_Token *tokenPtr, *testTokenPtr; -    int jumpDist, jumpFalseDist; -    int jumpIndex = 0;          /* avoid compiler warning. */ -    int numWords, wordIdx, numBytes, j, code; -    CONST char *word; -    char buffer[100]; -    int savedStackDepth = envPtr->currStackDepth; -                                /* Saved stack depth at the start of the first -				 * test; the envPtr current depth is restored -				 * to this value at the start of each test. */ -    int realCond = 1;           /* set to 0 for static conditions: "if 0 {..}" */ -    int boolVal;                /* value of static condition */ -    int compileScripts = 1;             - -    /* -     * Only compile the "if" command if all arguments are simple -     * words, in order to insure correct substitution [Bug 219166] -     */ - -    tokenPtr = parsePtr->tokenPtr; -    wordIdx = 0; -    numWords = parsePtr->numWords; +    DefineLineInformation;	/* TIP #280 */ +    Tcl_Token *varsTokenPtr, *dictTokenPtr, *bodyTokenPtr; +    int keyVarIndex, valueVarIndex, nameChars, loopRange, catchRange; +    int infoIndex, jumpDisplacement, bodyTargetOffset, emptyTargetOffset; +    int numVars, endTargetOffset; +    int collectVar = -1;	/* Index of temp var holding the result +				 * dict. */ +    const char **argv; +    Tcl_DString buffer; -    for (wordIdx = 0; wordIdx < numWords; wordIdx++) { -	if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { -	    return TCL_OUT_LINE_COMPILE; +    /* +     * There must be three arguments after the command. +     */ + +    if (parsePtr->numWords != 4) { +	return TclCompileBasic3ArgCmd(interp, parsePtr, cmdPtr, envPtr); +    } + +    varsTokenPtr = TokenAfter(parsePtr->tokenPtr); +    dictTokenPtr = TokenAfter(varsTokenPtr); +    bodyTokenPtr = TokenAfter(dictTokenPtr); +    if (varsTokenPtr->type != TCL_TOKEN_SIMPLE_WORD || +	    bodyTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { +	return TclCompileBasic3ArgCmd(interp, parsePtr, cmdPtr, envPtr); +    } + +    /* +     * Create temporary variable to capture return values from loop body when +     * we're collecting results. +     */ + +    if (collect == TCL_EACH_COLLECT) { +	collectVar = AnonymousLocal(envPtr); +	if (collectVar < 0) { +	    return TclCompileBasic3ArgCmd(interp, parsePtr, cmdPtr, envPtr);  	} -	tokenPtr += 2;      } +    /* +     * Check we've got a pair of variables and that they are local variables. +     * Then extract their indices in the LVT. +     */ -    TclInitJumpFixupArray(&jumpFalseFixupArray); -    TclInitJumpFixupArray(&jumpEndFixupArray); -    code = TCL_OK; +    Tcl_DStringInit(&buffer); +    TclDStringAppendToken(&buffer, &varsTokenPtr[1]); +    if (Tcl_SplitList(NULL, Tcl_DStringValue(&buffer), &numVars, +	    &argv) != TCL_OK) { +	Tcl_DStringFree(&buffer); +	return TclCompileBasic3ArgCmd(interp, parsePtr, cmdPtr, envPtr); +    } +    Tcl_DStringFree(&buffer); +    if (numVars != 2) { +	ckfree(argv); +	return TclCompileBasic3ArgCmd(interp, parsePtr, cmdPtr, envPtr); +    } + +    nameChars = strlen(argv[0]); +    keyVarIndex = LocalScalar(argv[0], nameChars, envPtr); +    nameChars = strlen(argv[1]); +    valueVarIndex = LocalScalar(argv[1], nameChars, envPtr); +    ckfree(argv); + +    if ((keyVarIndex < 0) || (valueVarIndex < 0)) { +	return TclCompileBasic3ArgCmd(interp, parsePtr, cmdPtr, envPtr); +    }      /* -     * Each iteration of this loop compiles one "if expr ?then? body" -     * or "elseif expr ?then? body" clause.  +     * Allocate a temporary variable to store the iterator reference. The +     * variable will contain a Tcl_DictSearch reference which will be +     * allocated by INST_DICT_FIRST and disposed when the variable is unset +     * (at which point it should also have been finished with).       */ -    tokenPtr = parsePtr->tokenPtr; -    wordIdx = 0; -    while (wordIdx < numWords) { -	/* -	 * Stop looping if the token isn't "if" or "elseif". -	 */ +    infoIndex = AnonymousLocal(envPtr); +    if (infoIndex < 0) { +	return TclCompileBasic3ArgCmd(interp, parsePtr, cmdPtr, envPtr); +    } -	word = tokenPtr[1].start; -	numBytes = tokenPtr[1].size; -	if ((tokenPtr == parsePtr->tokenPtr) -	        || ((numBytes == 6) && (strncmp(word, "elseif", 6) == 0))) { -	    tokenPtr += (tokenPtr->numComponents + 1); -	    wordIdx++; -	} else { -	    break; -	} -	if (wordIdx >= numWords) { -	    sprintf(buffer, -	            "wrong # args: no expression after \"%.*s\" argument", -		    (numBytes > 50 ? 50 : numBytes), word); -	    Tcl_ResetResult(interp); -	    Tcl_AppendToObj(Tcl_GetObjResult(interp), buffer, -1); -	    code = TCL_ERROR; -	    goto done; -	} +    /* +     * Preparation complete; issue instructions. Note that this code issues +     * fixed-sized jumps. That simplifies things a lot! +     * +     * First up, initialize the accumulator dictionary if needed. +     */ -	/* -	 * Compile the test expression then emit the conditional jump -	 * around the "then" part.  -	 */ -	 -	envPtr->currStackDepth = savedStackDepth; -	testTokenPtr = tokenPtr; +    if (collect == TCL_EACH_COLLECT) { +	PushStringLiteral(envPtr, ""); +	Emit14Inst(	INST_STORE_SCALAR, collectVar,		envPtr); +	TclEmitOpcode(	INST_POP,				envPtr); +    } +    /* +     * Get the dictionary and start the iteration. No catching of errors at +     * this point. +     */ -	if (realCond) { -	    /* -	     * Find out if the condition is a constant.  -	     */ -	 -	    Tcl_Obj *boolObj = Tcl_NewStringObj(testTokenPtr[1].start, -		    testTokenPtr[1].size); -	    Tcl_IncrRefCount(boolObj); -	    code = Tcl_GetBooleanFromObj(NULL, boolObj, &boolVal); -	    Tcl_DecrRefCount(boolObj); -	    if (code == TCL_OK) { -		/* -		 * A static condition -		 */ -		realCond = 0; -		if (!boolVal) { -		    compileScripts = 0; -		} -	    } else { -		Tcl_ResetResult(interp); -		code = TclCompileExprWords(interp, testTokenPtr, 1, envPtr); -		if (code != TCL_OK) { -		    if (code == TCL_ERROR) { -			Tcl_AddObjErrorInfo(interp, -			        "\n    (\"if\" test expression)", -1); -		    } -		    goto done; -		} -		if (jumpFalseFixupArray.next >= jumpFalseFixupArray.end) { -		    TclExpandJumpFixupArray(&jumpFalseFixupArray); -		} -		jumpIndex = jumpFalseFixupArray.next; -		jumpFalseFixupArray.next++; -		TclEmitForwardJump(envPtr, TCL_FALSE_JUMP, -			       &(jumpFalseFixupArray.fixup[jumpIndex]));	     -	    } -	} +    CompileWord(envPtr, dictTokenPtr, interp, 2); +    /* +     * Now we catch errors from here on so that we can finalize the search +     * started by Tcl_DictObjFirst above. +     */ -	/* -	 * Skip over the optional "then" before the then clause. -	 */ +    catchRange = TclCreateExceptRange(CATCH_EXCEPTION_RANGE, envPtr); +    TclEmitInstInt4(	INST_BEGIN_CATCH4, catchRange,		envPtr); +    ExceptionRangeStarts(envPtr, catchRange); -	tokenPtr = testTokenPtr + (testTokenPtr->numComponents + 1); -	wordIdx++; -	if (wordIdx >= numWords) { -	    sprintf(buffer, -		    "wrong # args: no script following \"%.*s\" argument", -		    (testTokenPtr->size > 50 ? 50 : testTokenPtr->size), -		    testTokenPtr->start); -	    Tcl_ResetResult(interp); -	    Tcl_AppendToObj(Tcl_GetObjResult(interp), buffer, -1); -	    code = TCL_ERROR; -	    goto done; -	} -	if (tokenPtr->type == TCL_TOKEN_SIMPLE_WORD) { -	    word = tokenPtr[1].start; -	    numBytes = tokenPtr[1].size; -	    if ((numBytes == 4) && (strncmp(word, "then", 4) == 0)) { -		tokenPtr += (tokenPtr->numComponents + 1); -		wordIdx++; -		if (wordIdx >= numWords) { -		    Tcl_ResetResult(interp); -		    Tcl_AppendToObj(Tcl_GetObjResult(interp), -		            "wrong # args: no script following \"then\" argument", -1); -		    code = TCL_ERROR; -		    goto done; -		} -	    } -	} +    TclEmitInstInt4(	INST_DICT_FIRST, infoIndex,		envPtr); +    emptyTargetOffset = CurrentOffset(envPtr); +    TclEmitInstInt4(	INST_JUMP_TRUE4, 0,			envPtr); -	/* -	 * Compile the "then" command body. -	 */ +    /* +     * Inside the iteration, write the loop variables. +     */ -	if (compileScripts) { -	    envPtr->currStackDepth = savedStackDepth; -	    code = TclCompileCmdWord(interp, tokenPtr+1, -	            tokenPtr->numComponents, envPtr); -	    if (code != TCL_OK) { -		if (code == TCL_ERROR) { -		    sprintf(buffer, "\n    (\"if\" then script line %d)", -		            interp->errorLine); -		    Tcl_AddObjErrorInfo(interp, buffer, -1); -		} -		goto done; -	    }	 -	} +    bodyTargetOffset = CurrentOffset(envPtr); +    Emit14Inst(		INST_STORE_SCALAR, keyVarIndex,		envPtr); +    TclEmitOpcode(	INST_POP,				envPtr); +    Emit14Inst(		INST_STORE_SCALAR, valueVarIndex,	envPtr); +    TclEmitOpcode(	INST_POP,				envPtr); -	if (realCond) { -	    /* -	     * Jump to the end of the "if" command. Both jumpFalseFixupArray and -	     * jumpEndFixupArray are indexed by "jumpIndex". -	     */ -	     -	    if (jumpEndFixupArray.next >= jumpEndFixupArray.end) { -		TclExpandJumpFixupArray(&jumpEndFixupArray); -	    } -	    jumpEndFixupArray.next++; -	    TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, -	            &(jumpEndFixupArray.fixup[jumpIndex])); -	     -	    /* -	     * Fix the target of the jumpFalse after the test. Generate a 4 byte -	     * jump if the distance is > 120 bytes. This is conservative, and -	     * ensures that we won't have to replace this jump if we later also -	     * need to replace the proceeding jump to the end of the "if" with a -	     * 4 byte jump. -	     */ +    /* +     * Set up the loop exception targets. +     */ -	    jumpDist = (envPtr->codeNext - envPtr->codeStart) -	            - jumpFalseFixupArray.fixup[jumpIndex].codeOffset; -	    if (TclFixupForwardJump(envPtr, -	            &(jumpFalseFixupArray.fixup[jumpIndex]), jumpDist, 120)) { -		/* -		 * Adjust the code offset for the proceeding jump to the end -		 * of the "if" command. -		 */ -		 -		jumpEndFixupArray.fixup[jumpIndex].codeOffset += 3; -	    } -	} else if (boolVal) { -	    /*  -	     *We were processing an "if 1 {...}"; stop compiling -	     * scripts -	     */ +    loopRange = TclCreateExceptRange(LOOP_EXCEPTION_RANGE, envPtr); +    ExceptionRangeStarts(envPtr, loopRange); -	    compileScripts = 0; -	} else { -	    /*  -	     *We were processing an "if 0 {...}"; reset so that -	     * the rest (elseif, else) is compiled correctly -	     */ +    /* +     * Compile the loop body itself. It should be stack-neutral. +     */ + +    BODY(bodyTokenPtr, 3); +    if (collect == TCL_EACH_COLLECT) { +	Emit14Inst(	INST_LOAD_SCALAR, keyVarIndex,		envPtr); +	TclEmitInstInt4(INST_OVER, 1,				envPtr); +	TclEmitInstInt4(INST_DICT_SET, 1,			envPtr); +	TclEmitInt4(		collectVar,			envPtr); +	TclAdjustStackDepth(-1, envPtr); +	TclEmitOpcode(	INST_POP,				envPtr); +    } +    TclEmitOpcode(	INST_POP,				envPtr); + +    /* +     * Both exception target ranges (error and loop) end here. +     */ + +    ExceptionRangeEnds(envPtr, loopRange); +    ExceptionRangeEnds(envPtr, catchRange); + +    /* +     * Continue (or just normally process) by getting the next pair of items +     * from the dictionary and jumping back to the code to write them into +     * variables if there is another pair. +     */ -	    realCond = 1; -	    compileScripts = 1; -	}  +    ExceptionRangeTarget(envPtr, loopRange, continueOffset); +    TclEmitInstInt4(	INST_DICT_NEXT, infoIndex,		envPtr); +    jumpDisplacement = bodyTargetOffset - CurrentOffset(envPtr); +    TclEmitInstInt4(	INST_JUMP_FALSE4, jumpDisplacement,	envPtr); +    endTargetOffset = CurrentOffset(envPtr); +    TclEmitInstInt1(	INST_JUMP1, 0,				envPtr); -	tokenPtr += (tokenPtr->numComponents + 1); -	wordIdx++; +    /* +     * Error handler "finally" clause, which force-terminates the iteration +     * and rethrows the error. +     */ + +    TclAdjustStackDepth(-1, envPtr); +    ExceptionRangeTarget(envPtr, catchRange, catchOffset); +    TclEmitOpcode(	INST_PUSH_RETURN_OPTIONS,		envPtr); +    TclEmitOpcode(	INST_PUSH_RESULT,			envPtr); +    TclEmitOpcode(	INST_END_CATCH,				envPtr); +    TclEmitInstInt1(	INST_UNSET_SCALAR, 0,			envPtr); +    TclEmitInt4(		infoIndex,			envPtr); +    if (collect == TCL_EACH_COLLECT) { +	TclEmitInstInt1(INST_UNSET_SCALAR, 0,			envPtr); +	TclEmitInt4(		collectVar,			envPtr);      } +    TclEmitOpcode(	INST_RETURN_STK,			envPtr);      /* -     * Restore the current stack depth in the environment; the  -     * "else" clause (or its default) will add 1 to this. +     * Otherwise we're done (the jump after the DICT_FIRST points here) and we +     * need to pop the bogus key/value pair (pushed to keep stack calculations +     * easy!) Note that we skip the END_CATCH. [Bug 1382528]       */ -    envPtr->currStackDepth = savedStackDepth; +    jumpDisplacement = CurrentOffset(envPtr) - emptyTargetOffset; +    TclUpdateInstInt4AtPc(INST_JUMP_TRUE4, jumpDisplacement, +	    envPtr->codeStart + emptyTargetOffset); +    jumpDisplacement = CurrentOffset(envPtr) - endTargetOffset; +    TclUpdateInstInt1AtPc(INST_JUMP1, jumpDisplacement, +	    envPtr->codeStart + endTargetOffset); +    TclEmitOpcode(	INST_POP,				envPtr); +    TclEmitOpcode(	INST_POP,				envPtr); +    ExceptionRangeTarget(envPtr, loopRange, breakOffset); +    TclFinalizeLoopExceptionRange(envPtr, loopRange); +    TclEmitOpcode(	INST_END_CATCH,				envPtr);      /* -     * Check for the optional else clause. Do not compile -     * anything if this was an "if 1 {...}" case. +     * Final stage of the command (normal case) is that we push an empty +     * object (or push the accumulator as the result object). This is done +     * last to promote peephole optimization when it's dropped immediately.       */ -    if ((wordIdx < numWords) -	    && (tokenPtr->type == TCL_TOKEN_SIMPLE_WORD)) { -	/* -	 * There is an else clause. Skip over the optional "else" word. -	 */ +    TclEmitInstInt1(	INST_UNSET_SCALAR, 0,			envPtr); +    TclEmitInt4(		infoIndex,			envPtr); +    if (collect == TCL_EACH_COLLECT) { +	Emit14Inst(	INST_LOAD_SCALAR, collectVar,		envPtr); +	TclEmitInstInt1(INST_UNSET_SCALAR, 0,			envPtr); +	TclEmitInt4(		collectVar,			envPtr); +    } else { +	PushStringLiteral(envPtr, ""); +    } +    return TCL_OK; +} -	word = tokenPtr[1].start; -	numBytes = tokenPtr[1].size; -	if ((numBytes == 4) && (strncmp(word, "else", 4) == 0)) { -	    tokenPtr += (tokenPtr->numComponents + 1); -	    wordIdx++; -	    if (wordIdx >= numWords) { -		Tcl_ResetResult(interp); -		Tcl_AppendToObj(Tcl_GetObjResult(interp), -		        "wrong # args: no script following \"else\" argument", -1); -		code = TCL_ERROR; -		goto done; -	    } -	} +int +TclCompileDictUpdateCmd( +    Tcl_Interp *interp,		/* Used for looking up stuff. */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr)		/* Holds resulting instructions. */ +{ +    DefineLineInformation;	/* TIP #280 */ +    int i, dictIndex, numVars, range, infoIndex; +    Tcl_Token **keyTokenPtrs, *dictVarTokenPtr, *bodyTokenPtr, *tokenPtr; +    DictUpdateInfo *duiPtr; +    JumpFixup jumpFixup; -	if (compileScripts) { -	    /* -	     * Compile the else command body. -	     */ -	     -	    code = TclCompileCmdWord(interp, tokenPtr+1, -		    tokenPtr->numComponents, envPtr); -	    if (code != TCL_OK) { -		if (code == TCL_ERROR) { -		    sprintf(buffer, "\n    (\"if\" else script line %d)", -			    interp->errorLine); -		    Tcl_AddObjErrorInfo(interp, buffer, -1); -		} -		goto done; -	    } -	} +    /* +     * There must be at least one argument after the command. +     */ + +    if (parsePtr->numWords < 5) { +	return TCL_ERROR; +    } + +    /* +     * Parse the command. Expect the following: +     *   dict update <lit(eral)> <any> <lit> ?<any> <lit> ...? <lit> +     */ + +    if ((parsePtr->numWords - 1) & 1) { +	return TCL_ERROR; +    } +    numVars = (parsePtr->numWords - 3) / 2; +    /* +     * The dictionary variable must be a local scalar that is knowable at +     * compile time; anything else exceeds the complexity of the opcode. So +     * discover what the index is. +     */ + +    dictVarTokenPtr = TokenAfter(parsePtr->tokenPtr); +    dictIndex = LocalScalarFromToken(dictVarTokenPtr, envPtr); +    if (dictIndex < 0) { +	goto issueFallback; +    } + +    /* +     * Assemble the instruction metadata. This is complex enough that it is +     * represented as auxData; it holds an ordered list of variable indices +     * that are to be used. +     */ + +    duiPtr = ckalloc(sizeof(DictUpdateInfo) + sizeof(int) * (numVars - 1)); +    duiPtr->length = numVars; +    keyTokenPtrs = TclStackAlloc(interp, sizeof(Tcl_Token *) * numVars); +    tokenPtr = TokenAfter(dictVarTokenPtr); + +    for (i=0 ; i<numVars ; i++) {  	/* -	 * Make sure there are no words after the else clause. +	 * Put keys to one side for later compilation to bytecode.  	 */ -	 -	wordIdx++; -	if (wordIdx < numWords) { -	    Tcl_ResetResult(interp); -	    Tcl_AppendToObj(Tcl_GetObjResult(interp), -		    "wrong # args: extra words after \"else\" clause in \"if\" command", -1); -	    code = TCL_ERROR; -	    goto done; -	} -    } else { + +	keyTokenPtrs[i] = tokenPtr; +	tokenPtr = TokenAfter(tokenPtr); +  	/* -	 * No else clause: the "if" command's result is an empty string. +	 * Stash the index in the auxiliary data (if it is indeed a local +	 * scalar that is resolvable at compile-time).  	 */ -	if (compileScripts) { -	    TclEmitPush(TclRegisterNewLiteral(envPtr, "", 0), envPtr); +	duiPtr->varIndices[i] = LocalScalarFromToken(tokenPtr, envPtr); +	if (duiPtr->varIndices[i] < 0) { +	    goto failedUpdateInfoAssembly;  	} +	tokenPtr = TokenAfter(tokenPtr); +    } +    if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { +	goto failedUpdateInfoAssembly;      } +    bodyTokenPtr = tokenPtr;      /* -     * Fix the unconditional jumps to the end of the "if" command. +     * The list of variables to bind is stored in auxiliary data so that it +     * can't be snagged by literal sharing and forced to shimmer dangerously.       */ -     -    for (j = jumpEndFixupArray.next;  j > 0;  j--) { -	jumpIndex = (j - 1);	/* i.e. process the closest jump first */ -	jumpDist = (envPtr->codeNext - envPtr->codeStart) -	        - jumpEndFixupArray.fixup[jumpIndex].codeOffset; -	if (TclFixupForwardJump(envPtr, -	        &(jumpEndFixupArray.fixup[jumpIndex]), jumpDist, 127)) { -	    /* -	     * Adjust the immediately preceeding "ifFalse" jump. We moved -	     * it's target (just after this jump) down three bytes. -	     */ -	    unsigned char *ifFalsePc = envPtr->codeStart -	            + jumpFalseFixupArray.fixup[jumpIndex].codeOffset; -	    unsigned char opCode = *ifFalsePc; -	    if (opCode == INST_JUMP_FALSE1) { -		jumpFalseDist = TclGetInt1AtPtr(ifFalsePc + 1); -		jumpFalseDist += 3; -		TclStoreInt1AtPtr(jumpFalseDist, (ifFalsePc + 1)); -	    } else if (opCode == INST_JUMP_FALSE4) { -		jumpFalseDist = TclGetInt4AtPtr(ifFalsePc + 1); -		jumpFalseDist += 3; -		TclStoreInt4AtPtr(jumpFalseDist, (ifFalsePc + 1)); -	    } else { -		panic("TclCompileIfCmd: unexpected opcode updating ifFalse jump"); -	    } -	} +    infoIndex = TclCreateAuxData(duiPtr, &dictUpdateInfoType, envPtr); + +    for (i=0 ; i<numVars ; i++) { +	CompileWord(envPtr, keyTokenPtrs[i], interp, 2*i+2);      } +    TclEmitInstInt4(	INST_LIST, numVars,			envPtr); +    TclEmitInstInt4(	INST_DICT_UPDATE_START, dictIndex,	envPtr); +    TclEmitInt4(		infoIndex,			envPtr); + +    range = TclCreateExceptRange(CATCH_EXCEPTION_RANGE, envPtr); +    TclEmitInstInt4(	INST_BEGIN_CATCH4, range,		envPtr); + +    ExceptionRangeStarts(envPtr, range); +    BODY(bodyTokenPtr, parsePtr->numWords - 1); +    ExceptionRangeEnds(envPtr, range);      /* -     * Free the jumpFixupArray array if malloc'ed storage was used. +     * Normal termination code: the stack has the key list below the result of +     * the body evaluation: swap them and finish the update code.       */ -    done: -    envPtr->currStackDepth = savedStackDepth + 1; -    TclFreeJumpFixupArray(&jumpFalseFixupArray); -    TclFreeJumpFixupArray(&jumpEndFixupArray); -    return code; +    TclEmitOpcode(	INST_END_CATCH,				envPtr); +    TclEmitInstInt4(	INST_REVERSE, 2,			envPtr); +    TclEmitInstInt4(	INST_DICT_UPDATE_END, dictIndex,	envPtr); +    TclEmitInt4(		infoIndex,			envPtr); + +    /* +     * Jump around the exceptional termination code. +     */ + +    TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &jumpFixup); + +    /* +     * Termination code for non-ok returns: stash the result and return +     * options in the stack, bring up the key list, finish the update code, +     * and finally return with the catched return data +     */ + +    ExceptionRangeTarget(envPtr, range, catchOffset); +    TclEmitOpcode(	INST_PUSH_RESULT,			envPtr); +    TclEmitOpcode(	INST_PUSH_RETURN_OPTIONS,		envPtr); +    TclEmitOpcode(	INST_END_CATCH,				envPtr); +    TclEmitInstInt4(	INST_REVERSE, 3,			envPtr); + +    TclEmitInstInt4(	INST_DICT_UPDATE_END, dictIndex,	envPtr); +    TclEmitInt4(		infoIndex,			envPtr); +    TclEmitInvoke(envPtr,INST_RETURN_STK); + +    if (TclFixupForwardJumpToHere(envPtr, &jumpFixup, 127)) { +	Tcl_Panic("TclCompileDictCmd(update): bad jump distance %d", +		(int) (CurrentOffset(envPtr) - jumpFixup.codeOffset)); +    } +    TclStackFree(interp, keyTokenPtrs); +    return TCL_OK; + +    /* +     * Clean up after a failure to create the DictUpdateInfo structure. +     */ + +  failedUpdateInfoAssembly: +    ckfree(duiPtr); +    TclStackFree(interp, keyTokenPtrs); +  issueFallback: +    return TclCompileBasicMin2ArgCmd(interp, parsePtr, cmdPtr, envPtr);  } - -/* - *---------------------------------------------------------------------- - * - * TclCompileIncrCmd -- - * - *	Procedure called to compile the "incr" command. - * - * Results: - *	The return value is a standard Tcl result, which is TCL_OK if - *	compilation was successful. If an error occurs then the - *	interpreter's result contains a standard error message and TCL_ERROR - *	is returned. If the command is too complex for TclCompileIncrCmd, - *	TCL_OUT_LINE_COMPILE is returned indicating that the incr command - *	should be compiled "out of line" by emitting code to invoke its - *	command procedure at runtime. - * - * Side effects: - *	Instructions are added to envPtr to execute the "incr" command - *	at runtime. - * - *---------------------------------------------------------------------- - */  int -TclCompileIncrCmd(interp, parsePtr, envPtr) -    Tcl_Interp *interp;		/* Used for error reporting. */ -    Tcl_Parse *parsePtr;	/* Points to a parse structure for the -				 * command created by Tcl_ParseCommand. */ -    CompileEnv *envPtr;		/* Holds resulting instructions. */ +TclCompileDictAppendCmd( +    Tcl_Interp *interp,		/* Used for looking up stuff. */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr)		/* Holds resulting instructions. */  { -    Tcl_Token *varTokenPtr, *incrTokenPtr; -    int simpleVarName, isScalar, localIndex, haveImmValue, immValue; -    int code = TCL_OK; - -    if ((parsePtr->numWords != 2) && (parsePtr->numWords != 3)) { -	Tcl_ResetResult(interp); -	Tcl_AppendToObj(Tcl_GetObjResult(interp), -	        "wrong # args: should be \"incr varName ?increment?\"", -1); +    DefineLineInformation;	/* TIP #280 */ +    Tcl_Token *tokenPtr; +    int i, dictVarIndex; + +    /* +     * There must be at least two argument after the command. And we impose an +     * (arbirary) safe limit; anyone exceeding it should stop worrying about +     * speed quite so much. ;-) +     */ + +    /* TODO: Consider support for compiling expanded args. */ +    if (parsePtr->numWords<4 || parsePtr->numWords>100) {  	return TCL_ERROR;      } -    varTokenPtr = parsePtr->tokenPtr -	    + (parsePtr->tokenPtr->numComponents + 1); +    /* +     * Get the index of the local variable that we will be working with. +     */ -    code = TclPushVarName(interp, varTokenPtr, envPtr,  -	    (TCL_NO_LARGE_INDEX | TCL_CREATE_VAR), -	    &localIndex, &simpleVarName, &isScalar); -    if (code != TCL_OK) { -	goto done; +    tokenPtr = TokenAfter(parsePtr->tokenPtr); +    dictVarIndex = LocalScalarFromToken(tokenPtr, envPtr); +    if (dictVarIndex < 0) { +	return TclCompileBasicMin2ArgCmd(interp, parsePtr,cmdPtr, envPtr);      }      /* -     * If an increment is given, push it, but see first if it's a small -     * integer. +     * Produce the string to concatenate onto the dictionary entry.       */ -    haveImmValue = 0; -    immValue = 0; -    if (parsePtr->numWords == 3) { -	incrTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1); -	if (incrTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) { -	    CONST char *word = incrTokenPtr[1].start; -	    int numBytes = incrTokenPtr[1].size; -	    int validLength = TclParseInteger(word, numBytes); -	    long n; +    tokenPtr = TokenAfter(tokenPtr); +    for (i=2 ; i<parsePtr->numWords ; i++) { +	CompileWord(envPtr, tokenPtr, interp, i); +	tokenPtr = TokenAfter(tokenPtr); +    } +    if (parsePtr->numWords > 4) { +	TclEmitInstInt1(INST_STR_CONCAT1, parsePtr->numWords-3, envPtr); +    } -	    /* -	     * Note there is a danger that modifying the string could have -	     * undesirable side effects.  In this case, TclLooksLikeInt and -	     * TclGetLong do not have any dependencies on shared strings so we -	     * should be safe. -	     */ +    /* +     * Do the concatenation. +     */ -	    if (validLength == numBytes) { -		int code; -		Tcl_Obj *longObj = Tcl_NewStringObj(word, numBytes); -		Tcl_IncrRefCount(longObj); -		code = Tcl_GetLongFromObj(NULL, longObj, &n); -		Tcl_DecrRefCount(longObj); -		if ((code == TCL_OK) && (-127 <= n) && (n <= 127)) { -		    haveImmValue = 1; -		    immValue = n; -		} -	    } -	    if (!haveImmValue) { -		TclEmitPush( -			TclRegisterNewLiteral(envPtr, word, numBytes), envPtr); -	    } -	} else { -	    code = TclCompileTokens(interp, incrTokenPtr+1,  -	            incrTokenPtr->numComponents, envPtr); -	    if (code != TCL_OK) { -		goto done; -	    } -	} -    } else {			/* no incr amount given so use 1 */ -	haveImmValue = 1; -	immValue = 1; +    TclEmitInstInt4(INST_DICT_APPEND, dictVarIndex, envPtr); +    return TCL_OK; +} + +int +TclCompileDictLappendCmd( +    Tcl_Interp *interp,		/* Used for looking up stuff. */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr)		/* Holds resulting instructions. */ +{ +    DefineLineInformation;	/* TIP #280 */ +    Tcl_Token *varTokenPtr, *keyTokenPtr, *valueTokenPtr; +    int dictVarIndex; + +    /* +     * There must be three arguments after the command. +     */ + +    /* TODO: Consider support for compiling expanded args. */ +    /* Probably not.  Why is INST_DICT_LAPPEND limited to one value? */ +    if (parsePtr->numWords != 4) { +	return TCL_ERROR;      } -     +      /* -     * Emit the instruction to increment the variable. +     * Parse the arguments.       */ -    if (simpleVarName) { -	if (isScalar) { -	    if (localIndex >= 0) { -		if (haveImmValue) { -		    TclEmitInstInt1(INST_INCR_SCALAR1_IMM, localIndex, envPtr); -		    TclEmitInt1(immValue, envPtr); -		} else { -		    TclEmitInstInt1(INST_INCR_SCALAR1, localIndex, envPtr); -		} -	    } else { -		if (haveImmValue) { -		    TclEmitInstInt1(INST_INCR_SCALAR_STK_IMM, immValue, envPtr); -		} else { -		    TclEmitOpcode(INST_INCR_SCALAR_STK, envPtr); -		} -	    } -	} else { -	    if (localIndex >= 0) { -		if (haveImmValue) { -		    TclEmitInstInt1(INST_INCR_ARRAY1_IMM, localIndex, envPtr); -		    TclEmitInt1(immValue, envPtr); -		} else { -		    TclEmitInstInt1(INST_INCR_ARRAY1, localIndex, envPtr); -		} -	    } else { -		if (haveImmValue) { -		    TclEmitInstInt1(INST_INCR_ARRAY_STK_IMM, immValue, envPtr); -		} else { -		    TclEmitOpcode(INST_INCR_ARRAY_STK, envPtr); -		} -	    } -	} -    } else {			/* non-simple variable name */ -	if (haveImmValue) { -	    TclEmitInstInt1(INST_INCR_STK_IMM, immValue, envPtr); -	} else { -	    TclEmitOpcode(INST_INCR_STK, envPtr); -	} +    varTokenPtr = TokenAfter(parsePtr->tokenPtr); +    keyTokenPtr = TokenAfter(varTokenPtr); +    valueTokenPtr = TokenAfter(keyTokenPtr); +    dictVarIndex = LocalScalarFromToken(varTokenPtr, envPtr); +    if (dictVarIndex < 0) { +	return TclCompileBasic3ArgCmd(interp, parsePtr, cmdPtr, envPtr);      } -	 -    done: -    return code; + +    /* +     * Issue the implementation. +     */ + +    CompileWord(envPtr, keyTokenPtr, interp, 2); +    CompileWord(envPtr, valueTokenPtr, interp, 3); +    TclEmitInstInt4(	INST_DICT_LAPPEND, dictVarIndex,	envPtr); +    return TCL_OK;  } - -/* - *---------------------------------------------------------------------- - * - * TclCompileLappendCmd -- - * - *	Procedure called to compile the "lappend" command. - * - * Results: - *	The return value is a standard Tcl result, which is normally TCL_OK - *	unless there was an error while parsing string. If an error occurs - *	then the interpreter's result contains a standard error message. If - *	complation fails because the command requires a second level of - *	substitutions, TCL_OUT_LINE_COMPILE is returned indicating that the - *	command should be compiled "out of line" by emitting code to - *	invoke its command procedure (Tcl_LappendObjCmd) at runtime. - * - * Side effects: - *	Instructions are added to envPtr to execute the "lappend" command - *	at runtime. - * - *---------------------------------------------------------------------- - */  int -TclCompileLappendCmd(interp, parsePtr, envPtr) -    Tcl_Interp *interp;		/* Used for error reporting. */ -    Tcl_Parse *parsePtr;	/* Points to a parse structure for the -				 * command created by Tcl_ParseCommand. */ -    CompileEnv *envPtr;		/* Holds resulting instructions. */ +TclCompileDictWithCmd( +    Tcl_Interp *interp,		/* Used for looking up stuff. */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr)		/* Holds resulting instructions. */  { -    Tcl_Token *varTokenPtr, *valueTokenPtr; -    int simpleVarName, isScalar, localIndex, numWords; -    int code = TCL_OK; +    DefineLineInformation;	/* TIP #280 */ +    int i, range, varNameTmp = -1, pathTmp = -1, keysTmp, gotPath; +    int dictVar, bodyIsEmpty = 1; +    Tcl_Token *varTokenPtr, *tokenPtr; +    JumpFixup jumpFixup; +    const char *ptr, *end;      /* -     * If we're not in a procedure, don't compile. +     * There must be at least one argument after the command.       */ -    if (envPtr->procPtr == NULL) { -	return TCL_OUT_LINE_COMPILE; -    } -    numWords = parsePtr->numWords; -    if (numWords == 1) { -	Tcl_ResetResult(interp); -	Tcl_AppendToObj(Tcl_GetObjResult(interp), -		"wrong # args: should be \"lappend varName ?value value ...?\"", -1); +    /* TODO: Consider support for compiling expanded args. */ +    if (parsePtr->numWords < 3) {  	return TCL_ERROR;      } -    if (numWords != 3) { -	/* -	 * LAPPEND instructions currently only handle one value appends -	 */ -        return TCL_OUT_LINE_COMPILE; -    }      /* -     * Decide if we can use a frame slot for the var/array name or if we -     * need to emit code to compute and push the name at runtime. We use a -     * frame slot (entry in the array of local vars) if we are compiling a -     * procedure body and if the name is simple text that does not include -     * namespace qualifiers.  +     * Parse the command (trivially). Expect the following: +     *   dict with <any (varName)> ?<any> ...? <literal>       */ -    varTokenPtr = parsePtr->tokenPtr -	    + (parsePtr->tokenPtr->numComponents + 1); - -    code = TclPushVarName(interp, varTokenPtr, envPtr, TCL_CREATE_VAR, -	    &localIndex, &simpleVarName, &isScalar); -    if (code != TCL_OK) { -	goto done; +    varTokenPtr = TokenAfter(parsePtr->tokenPtr); +    tokenPtr = TokenAfter(varTokenPtr); +    for (i=3 ; i<parsePtr->numWords ; i++) { +	tokenPtr = TokenAfter(tokenPtr); +    } +    if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { +	return TclCompileBasicMin2ArgCmd(interp, parsePtr, cmdPtr, envPtr);      }      /* -     * If we are doing an assignment, push the new value. -     * In the no values case, create an empty object. +     * Test if the last word is an empty script; if so, we can compile it in +     * all cases, but if it is non-empty we need local variable table entries +     * to hold the temporary variables (used to keep stack usage simple).       */ -    if (numWords > 2) { -	valueTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1); -	if (valueTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) { -	    TclEmitPush(TclRegisterNewLiteral(envPtr,  -		    valueTokenPtr[1].start, valueTokenPtr[1].size), envPtr); -	} else { -	    code = TclCompileTokens(interp, valueTokenPtr+1, -	            valueTokenPtr->numComponents, envPtr); -	    if (code != TCL_OK) { -		goto done; +    for (ptr=tokenPtr[1].start,end=ptr+tokenPtr[1].size ; ptr!=end ; ptr++) { +	if (*ptr!=' ' && *ptr!='\t' && *ptr!='\n' && *ptr!='\r') { +	    if (envPtr->procPtr == NULL) { +		return TclCompileBasicMin2ArgCmd(interp, parsePtr, cmdPtr, +			envPtr);  	    } +	    bodyIsEmpty = 0; +	    break;  	}      }      /* -     * Emit instructions to set/get the variable. +     * Determine if we're manipulating a dict in a simple local variable.       */ +    gotPath = (parsePtr->numWords > 3); +    dictVar = LocalScalarFromToken(varTokenPtr, envPtr); +      /* -     * The *_STK opcodes should be refactored to make better use of existing -     * LOAD/STORE instructions. +     * Special case: an empty body means we definitely have no need to issue +     * try-finally style code or to allocate local variable table entries for +     * storing temporaries. Still need to do both INST_DICT_EXPAND and +     * INST_DICT_RECOMBINE_* though, because we can't determine if we're free +     * of traces.       */ -    if (simpleVarName) { -	if (isScalar) { -	    if (localIndex >= 0) { -		if (localIndex <= 255) { -		    TclEmitInstInt1(INST_LAPPEND_SCALAR1, localIndex, envPtr); -		} else { -		    TclEmitInstInt4(INST_LAPPEND_SCALAR4, localIndex, envPtr); + +    if (bodyIsEmpty) { +	if (dictVar >= 0) { +	    if (gotPath) { +		/* +		 * Case: Path into dict in LVT with empty body. +		 */ + +		tokenPtr = TokenAfter(varTokenPtr); +		for (i=2 ; i<parsePtr->numWords-1 ; i++) { +		    CompileWord(envPtr, tokenPtr, interp, i); +		    tokenPtr = TokenAfter(tokenPtr);  		} +		TclEmitInstInt4(INST_LIST, parsePtr->numWords-3,envPtr); +		Emit14Inst(	INST_LOAD_SCALAR, dictVar,	envPtr); +		TclEmitInstInt4(INST_OVER, 1,			envPtr); +		TclEmitOpcode(	INST_DICT_EXPAND,		envPtr); +		TclEmitInstInt4(INST_DICT_RECOMBINE_IMM, dictVar, envPtr);  	    } else { -		TclEmitOpcode(INST_LAPPEND_STK, envPtr); +		/* +		 * Case: Direct dict in LVT with empty body. +		 */ + +		PushStringLiteral(envPtr, ""); +		Emit14Inst(	INST_LOAD_SCALAR, dictVar,	envPtr); +		PushStringLiteral(envPtr, ""); +		TclEmitOpcode(	INST_DICT_EXPAND,		envPtr); +		TclEmitInstInt4(INST_DICT_RECOMBINE_IMM, dictVar, envPtr);  	    }  	} else { -	    if (localIndex >= 0) { -		if (localIndex <= 255) { -		    TclEmitInstInt1(INST_LAPPEND_ARRAY1, localIndex, envPtr); -		} else { -		    TclEmitInstInt4(INST_LAPPEND_ARRAY4, localIndex, envPtr); +	    if (gotPath) { +		/* +		 * Case: Path into dict in non-simple var with empty body. +		 */ + +		tokenPtr = varTokenPtr; +		for (i=1 ; i<parsePtr->numWords-1 ; i++) { +		    CompileWord(envPtr, tokenPtr, interp, i); +		    tokenPtr = TokenAfter(tokenPtr);  		} +		TclEmitInstInt4(INST_LIST, parsePtr->numWords-3,envPtr); +		TclEmitInstInt4(INST_OVER, 1,			envPtr); +		TclEmitOpcode(	INST_LOAD_STK,			envPtr); +		TclEmitInstInt4(INST_OVER, 1,			envPtr); +		TclEmitOpcode(	INST_DICT_EXPAND,		envPtr); +		TclEmitOpcode(	INST_DICT_RECOMBINE_STK,	envPtr);  	    } else { -		TclEmitOpcode(INST_LAPPEND_ARRAY_STK, envPtr); +		/* +		 * Case: Direct dict in non-simple var with empty body. +		 */ + +		CompileWord(envPtr, varTokenPtr, interp, 1); +		TclEmitOpcode(	INST_DUP,			envPtr); +		TclEmitOpcode(	INST_LOAD_STK,			envPtr); +		PushStringLiteral(envPtr, ""); +		TclEmitOpcode(	INST_DICT_EXPAND,		envPtr); +		PushStringLiteral(envPtr, ""); +		TclEmitInstInt4(INST_REVERSE, 2,		envPtr); +		TclEmitOpcode(	INST_DICT_RECOMBINE_STK,	envPtr);  	    }  	} +	PushStringLiteral(envPtr, ""); +	return TCL_OK; +    } + +    /* +     * OK, we have a non-trivial body. This means that the focus is on +     * generating a try-finally structure where the INST_DICT_RECOMBINE_* goes +     * in the 'finally' clause. +     * +     * Start by allocating local (unnamed, untraced) working variables. +     */ + +    if (dictVar == -1) { +	varNameTmp = AnonymousLocal(envPtr); +    } +    if (gotPath) { +	pathTmp = AnonymousLocal(envPtr); +    } +    keysTmp = AnonymousLocal(envPtr); + +    /* +     * Issue instructions. First, the part to expand the dictionary. +     */ + +    if (dictVar == -1) { +	CompileWord(envPtr, varTokenPtr, interp, 1); +	Emit14Inst(		INST_STORE_SCALAR, varNameTmp,	envPtr); +    } +    tokenPtr = TokenAfter(varTokenPtr); +    if (gotPath) { +	for (i=2 ; i<parsePtr->numWords-1 ; i++) { +	    CompileWord(envPtr, tokenPtr, interp, i); +	    tokenPtr = TokenAfter(tokenPtr); +	} +	TclEmitInstInt4(	INST_LIST, parsePtr->numWords-3,envPtr); +	Emit14Inst(		INST_STORE_SCALAR, pathTmp,	envPtr); +	TclEmitOpcode(		INST_POP,			envPtr); +    } +    if (dictVar == -1) { +	TclEmitOpcode(		INST_LOAD_STK,			envPtr); +    } else { +	Emit14Inst(		INST_LOAD_SCALAR, dictVar,	envPtr); +    } +    if (gotPath) { +	Emit14Inst(		INST_LOAD_SCALAR, pathTmp,	envPtr);      } else { -	TclEmitOpcode(INST_LAPPEND_STK, envPtr); +	PushStringLiteral(envPtr, "");      } +    TclEmitOpcode(		INST_DICT_EXPAND,		envPtr); +    Emit14Inst(			INST_STORE_SCALAR, keysTmp,	envPtr); +    TclEmitOpcode(		INST_POP,			envPtr); -    done: -    return code; +    /* +     * Now the body of the [dict with]. +     */ + +    range = TclCreateExceptRange(CATCH_EXCEPTION_RANGE, envPtr); +    TclEmitInstInt4(		INST_BEGIN_CATCH4, range,	envPtr); + +    ExceptionRangeStarts(envPtr, range); +    BODY(tokenPtr, parsePtr->numWords - 1); +    ExceptionRangeEnds(envPtr, range); + +    /* +     * Now fold the results back into the dictionary in the OK case. +     */ + +    TclEmitOpcode(		INST_END_CATCH,			envPtr); +    if (dictVar == -1) { +	Emit14Inst(		INST_LOAD_SCALAR, varNameTmp,	envPtr); +    } +    if (gotPath) { +	Emit14Inst(		INST_LOAD_SCALAR, pathTmp,	envPtr); +    } else { +	PushStringLiteral(envPtr, ""); +    } +    Emit14Inst(			INST_LOAD_SCALAR, keysTmp,	envPtr); +    if (dictVar == -1) { +	TclEmitOpcode(		INST_DICT_RECOMBINE_STK,	envPtr); +    } else { +	TclEmitInstInt4(	INST_DICT_RECOMBINE_IMM, dictVar, envPtr); +    } +    TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &jumpFixup); + +    /* +     * Now fold the results back into the dictionary in the exception case. +     */ + +    TclAdjustStackDepth(-1, envPtr); +    ExceptionRangeTarget(envPtr, range, catchOffset); +    TclEmitOpcode(		INST_PUSH_RETURN_OPTIONS,	envPtr); +    TclEmitOpcode(		INST_PUSH_RESULT,		envPtr); +    TclEmitOpcode(		INST_END_CATCH,			envPtr); +    if (dictVar == -1) { +	Emit14Inst(		INST_LOAD_SCALAR, varNameTmp,	envPtr); +    } +    if (parsePtr->numWords > 3) { +	Emit14Inst(		INST_LOAD_SCALAR, pathTmp,	envPtr); +    } else { +	PushStringLiteral(envPtr, ""); +    } +    Emit14Inst(			INST_LOAD_SCALAR, keysTmp,	envPtr); +    if (dictVar == -1) { +	TclEmitOpcode(		INST_DICT_RECOMBINE_STK,	envPtr); +    } else { +	TclEmitInstInt4(	INST_DICT_RECOMBINE_IMM, dictVar, envPtr); +    } +    TclEmitInvoke(envPtr,	INST_RETURN_STK); + +    /* +     * Prepare for the start of the next command. +     */ + +    if (TclFixupForwardJumpToHere(envPtr, &jumpFixup, 127)) { +	Tcl_Panic("TclCompileDictCmd(update): bad jump distance %d", +		(int) (CurrentOffset(envPtr) - jumpFixup.codeOffset)); +    } +    return TCL_OK;  }  /*   *----------------------------------------------------------------------   * - * TclCompileLindexCmd -- + * DupDictUpdateInfo, FreeDictUpdateInfo --   * - *	Procedure called to compile the "lindex" command. + *	Functions to duplicate, release and print the aux data created for use + *	with the INST_DICT_UPDATE_START and INST_DICT_UPDATE_END instructions.   *   * Results: - *	The return value is a standard Tcl result, which is TCL_OK if the - *	compilation was successful.  If the command cannot be byte-compiled, - *	TCL_OUT_LINE_COMPILE is returned.  If an error occurs then the - *	interpreter's result contains an error message, and TCL_ERROR is - *	returned. + *	DupDictUpdateInfo: a copy of the auxiliary data + *	FreeDictUpdateInfo: none + *	PrintDictUpdateInfo: none + *	DisassembleDictUpdateInfo: none   *   * Side effects: - *	Instructions are added to envPtr to execute the "lindex" command - *	at runtime. + *	DupDictUpdateInfo: allocates memory + *	FreeDictUpdateInfo: releases memory + *	PrintDictUpdateInfo: none + *	DisassembleDictUpdateInfo: none   *   *----------------------------------------------------------------------   */ -int -TclCompileLindexCmd(interp, parsePtr, envPtr) -    Tcl_Interp *interp;		/* Used for error reporting. */ -    Tcl_Parse *parsePtr;	/* Points to a parse structure for the -				 * command created by Tcl_ParseCommand. */ -    CompileEnv *envPtr;		/* Holds resulting instructions. */ +static ClientData +DupDictUpdateInfo( +    ClientData clientData)  { -    Tcl_Token *varTokenPtr; -    int code, i; - -    int numWords; -    numWords = parsePtr->numWords; +    DictUpdateInfo *dui1Ptr, *dui2Ptr; +    unsigned len; + +    dui1Ptr = clientData; +    len = sizeof(DictUpdateInfo) + sizeof(int) * (dui1Ptr->length - 1); +    dui2Ptr = ckalloc(len); +    memcpy(dui2Ptr, dui1Ptr, len); +    return dui2Ptr; +} -    /* -     * Quit if too few args -     */ +static void +FreeDictUpdateInfo( +    ClientData clientData) +{ +    ckfree(clientData); +} -    if ( numWords <= 1 ) { -	return TCL_OUT_LINE_COMPILE; -    } +static void +PrintDictUpdateInfo( +    ClientData clientData, +    Tcl_Obj *appendObj, +    ByteCode *codePtr, +    unsigned int pcOffset) +{ +    DictUpdateInfo *duiPtr = clientData; +    int i; -    varTokenPtr = parsePtr->tokenPtr -	+ (parsePtr->tokenPtr->numComponents + 1); -     -    /* -     * Push the operands onto the stack. -     */ -	 -    for ( i = 1 ; i < numWords ; i++ ) { -	if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) { -	    TclEmitPush( -		    TclRegisterNewLiteral( envPtr, varTokenPtr[1].start, -		    varTokenPtr[1].size), envPtr); -	} else { -	    code = TclCompileTokens(interp, varTokenPtr+1, -				    varTokenPtr->numComponents, envPtr); -	    if (code != TCL_OK) { -		return code; -	    } +    for (i=0 ; i<duiPtr->length ; i++) { +	if (i) { +	    Tcl_AppendToObj(appendObj, ", ", -1);  	} -	varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1); +	Tcl_AppendPrintfToObj(appendObj, "%%v%u", duiPtr->varIndices[i]);      } -	 -    /* -     * Emit INST_LIST_INDEX if objc==3, or INST_LIST_INDEX_MULTI -     * if there are multiple index args. -     */ +} -    if ( numWords == 3 ) { -	TclEmitOpcode( INST_LIST_INDEX, envPtr ); -    } else { - 	TclEmitInstInt4( INST_LIST_INDEX_MULTI, numWords-1, envPtr ); -    } +static void +DisassembleDictUpdateInfo( +    ClientData clientData, +    Tcl_Obj *dictObj, +    ByteCode *codePtr, +    unsigned int pcOffset) +{ +    DictUpdateInfo *duiPtr = clientData; +    int i; +    Tcl_Obj *variables = Tcl_NewObj(); -    return TCL_OK; +    for (i=0 ; i<duiPtr->length ; i++) { +	Tcl_ListObjAppendElement(NULL, variables, +		Tcl_NewIntObj(duiPtr->varIndices[i])); +    } +    Tcl_DictObjPut(NULL, dictObj, Tcl_NewStringObj("variables", -1), +	    variables);  }  /*   *----------------------------------------------------------------------   * - * TclCompileListCmd -- + * TclCompileErrorCmd --   * - *	Procedure called to compile the "list" command. + *	Procedure called to compile the "error" command.   *   * Results: - *	The return value is a standard Tcl result, which is normally TCL_OK - *	unless there was an error while parsing string. If an error occurs - *	then the interpreter's result contains a standard error message. If - *	complation fails because the command requires a second level of - *	substitutions, TCL_OUT_LINE_COMPILE is returned indicating that the - *	command should be compiled "out of line" by emitting code to - *	invoke its command procedure (Tcl_ListObjCmd) at runtime. + *	Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer + *	evaluation to runtime.   *   * Side effects: - *	Instructions are added to envPtr to execute the "list" command - *	at runtime. + *	Instructions are added to envPtr to execute the "error" command at + *	runtime.   *   *----------------------------------------------------------------------   */  int -TclCompileListCmd(interp, parsePtr, envPtr) -    Tcl_Interp *interp;		/* Used for error reporting. */ -    Tcl_Parse *parsePtr;	/* Points to a parse structure for the -				 * command created by Tcl_ParseCommand. */ -    CompileEnv *envPtr;		/* Holds resulting instructions. */ +TclCompileErrorCmd( +    Tcl_Interp *interp,		/* Used for context. */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr)		/* Holds resulting instructions. */  {      /* -     * If we're not in a procedure, don't compile. +     * General syntax: [error message ?errorInfo? ?errorCode?]       */ -    if (envPtr->procPtr == NULL) { -	return TCL_OUT_LINE_COMPILE; + +    Tcl_Token *tokenPtr; +    DefineLineInformation;	/* TIP #280 */ + +    if (parsePtr->numWords < 2 || parsePtr->numWords > 4) { +	return TCL_ERROR;      } -    if (parsePtr->numWords == 1) { -	/* -	 * Empty args case -	 */ +    /* +     * Handle the message. +     */ -	TclEmitPush(TclRegisterNewLiteral(envPtr, "", 0), envPtr); -    } else { -	/* -	 * Push the all values onto the stack. -	 */ -	Tcl_Token *valueTokenPtr; -	int i, code, numWords; +    tokenPtr = TokenAfter(parsePtr->tokenPtr); +    CompileWord(envPtr, tokenPtr, interp, 1); -	numWords = parsePtr->numWords; +    /* +     * Construct the options. Note that -code and -level are not here. +     */ -	valueTokenPtr = parsePtr->tokenPtr -	    + (parsePtr->tokenPtr->numComponents + 1); -	for (i = 1; i < numWords; i++) { -	    if (valueTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) { -		TclEmitPush(TclRegisterNewLiteral(envPtr, -			valueTokenPtr[1].start, valueTokenPtr[1].size), envPtr); -	    } else { -		code = TclCompileTokens(interp, valueTokenPtr+1, -			valueTokenPtr->numComponents, envPtr); -		if (code != TCL_OK) { -		    return code; -		} -	    } -	    valueTokenPtr = valueTokenPtr + (valueTokenPtr->numComponents + 1); +    if (parsePtr->numWords == 2) { +	PushStringLiteral(envPtr, ""); +    } else { +	PushStringLiteral(envPtr, "-errorinfo"); +	tokenPtr = TokenAfter(tokenPtr); +	CompileWord(envPtr, tokenPtr, interp, 2); +	if (parsePtr->numWords == 3) { +	    TclEmitInstInt4(	INST_LIST, 2,			envPtr); +	} else { +	    PushStringLiteral(envPtr, "-errorcode"); +	    tokenPtr = TokenAfter(tokenPtr); +	    CompileWord(envPtr, tokenPtr, interp, 3); +	    TclEmitInstInt4(	INST_LIST, 4,			envPtr);  	} -	TclEmitInstInt4(INST_LIST, numWords - 1, envPtr);      } +    /* +     * Issue the error via 'returnImm error 0'. +     */ + +    TclEmitInstInt4(		INST_RETURN_IMM, TCL_ERROR,	envPtr); +    TclEmitInt4(			0,			envPtr);      return TCL_OK;  }  /*   *----------------------------------------------------------------------   * - * TclCompileLlengthCmd -- + * TclCompileExprCmd --   * - *	Procedure called to compile the "llength" command. + *	Procedure called to compile the "expr" command.   *   * Results: - *	The return value is a standard Tcl result, which is TCL_OK if the - *	compilation was successful.  If the command cannot be byte-compiled, - *	TCL_OUT_LINE_COMPILE is returned.  If an error occurs then the - *	interpreter's result contains an error message, and TCL_ERROR is - *	returned. + *	Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer + *	evaluation to runtime.   *   * Side effects: - *	Instructions are added to envPtr to execute the "llength" command - *	at runtime. + *	Instructions are added to envPtr to execute the "expr" command at + *	runtime.   *   *----------------------------------------------------------------------   */  int -TclCompileLlengthCmd(interp, parsePtr, envPtr) -    Tcl_Interp *interp;		/* Used for error reporting. */ -    Tcl_Parse *parsePtr;	/* Points to a parse structure for the -				 * command created by Tcl_ParseCommand. */ -    CompileEnv *envPtr;		/* Holds resulting instructions. */ +TclCompileExprCmd( +    Tcl_Interp *interp,		/* Used for error reporting. */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr)		/* Holds resulting instructions. */  { -    Tcl_Token *varTokenPtr; -    int code; +    Tcl_Token *firstWordPtr; -    if (parsePtr->numWords != 2) { -	Tcl_SetResult(interp, "wrong # args: should be \"llength list\"", -		TCL_STATIC); +    if (parsePtr->numWords == 1) {  	return TCL_ERROR;      } -    varTokenPtr = parsePtr->tokenPtr -	+ (parsePtr->tokenPtr->numComponents + 1); -    if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) { -	/* -	 * We could simply count the number of elements here and push -	 * that value, but that is too rare a case to waste the code space. -	 */ -	TclEmitPush(TclRegisterNewLiteral(envPtr, varTokenPtr[1].start, -		varTokenPtr[1].size), envPtr); -    } else { -	code = TclCompileTokens(interp, varTokenPtr+1, -		varTokenPtr->numComponents, envPtr); -	if (code != TCL_OK) { -	    return code; -	} -    } -    TclEmitOpcode(INST_LIST_LENGTH, envPtr); +    /* +     * TIP #280: Use the per-word line information of the current command. +     */ + +    envPtr->line = envPtr->extCmdMapPtr->loc[ +	    envPtr->extCmdMapPtr->nuloc-1].line[1]; + +    firstWordPtr = TokenAfter(parsePtr->tokenPtr); +    TclCompileExprWords(interp, firstWordPtr, parsePtr->numWords-1, envPtr);      return TCL_OK;  }  /*   *----------------------------------------------------------------------   * - * TclCompileLsetCmd -- + * TclCompileForCmd --   * - *	Procedure called to compile the "lset" command. + *	Procedure called to compile the "for" command.   *   * Results: - *	The return value is a standard Tcl result, which is TCL_OK if - *	the compilation was successful.  If the "lset" command is too - *	complex for this function, then TCL_OUT_LINE_COMPILE is returned, - *	indicating that the command should be compiled "out of line" - *	(that is, not byte-compiled).  If an error occurs, TCL_ERROR is - *	returned, and the interpreter result contains an error message. + *	Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer + *	evaluation to runtime.   *   * Side effects: - *	Instructions are added to envPtr to execute the "lset" command - *	at runtime. - * - * The general template for execution of the "lset" command is: - *	(1) Instructions to push the variable name, unless the - *	    variable is local to the stack frame. - *	(2) If the variable is an array element, instructions - *	    to push the array element name. - *	(3) Instructions to push each of zero or more "index" arguments - *	    to the stack, followed with the "newValue" element. - *	(4) Instructions to duplicate the variable name and/or array - *	    element name onto the top of the stack, if either was - *	    pushed at steps (1) and (2). - *	(5) The appropriate INST_LOAD_* instruction to place the - *	    original value of the list variable at top of stack. - *	(6) At this point, the stack contains: - *	     varName? arrayElementName? index1 index2 ... newValue oldList - *	    The compiler emits one of INST_LSET_FLAT or INST_LSET_LIST - *	    according as whether there is exactly one index element (LIST) - *	    or either zero or else two or more (FLAT).  This instruction - *	    removes everything from the stack except for the two names - *	    and pushes the new value of the variable. - *	(7) Finally, INST_STORE_* stores the new value in the variable - *	    and cleans up the stack. + *	Instructions are added to envPtr to execute the "for" command at + *	runtime.   *   *----------------------------------------------------------------------   */  int -TclCompileLsetCmd( interp, parsePtr, envPtr ) -    Tcl_Interp* interp;		/* Tcl interpreter for error reporting */ -    Tcl_Parse* parsePtr;	/* Points to a parse structure for -				 * the command */ -    CompileEnv* envPtr;		/* Holds the resulting instructions */ +TclCompileForCmd( +    Tcl_Interp *interp,		/* Used for error reporting. */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr)		/* Holds resulting instructions. */  { +    Tcl_Token *startTokenPtr, *testTokenPtr, *nextTokenPtr, *bodyTokenPtr; +    JumpFixup jumpEvalCondFixup; +    int bodyCodeOffset, nextCodeOffset, jumpDist; +    int bodyRange, nextRange; +    DefineLineInformation;	/* TIP #280 */ -    int tempDepth;		/* Depth used for emitting one part -				 * of the code burst. */ -    Tcl_Token* varTokenPtr;	/* Pointer to the Tcl_Token representing -				 * the parse of the variable name */ - -    int result;			/* Status return from library calls */ - -    int localIndex;		/* Index of var in local var table */ -    int simpleVarName;		/* Flag == 1 if var name is simple */ -    int isScalar;		/* Flag == 1 if scalar, 0 if array */ - -    int i; +    if (parsePtr->numWords != 5) { +	return TCL_ERROR; +    } -    /* Check argument count */ +    /* +     * If the test expression requires substitutions, don't compile the for +     * command inline. E.g., the expression might cause the loop to never +     * execute or execute forever, as in "for {} "$x > 5" {incr x} {}". +     */ -    if ( parsePtr->numWords < 3 ) { -	/* Fail at run time, not in compilation */ -	return TCL_OUT_LINE_COMPILE; +    startTokenPtr = TokenAfter(parsePtr->tokenPtr); +    testTokenPtr = TokenAfter(startTokenPtr); +    if (testTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { +	return TCL_ERROR;      }      /* -     * Decide if we can use a frame slot for the var/array name or if we -     * need to emit code to compute and push the name at runtime. We use a -     * frame slot (entry in the array of local vars) if we are compiling a -     * procedure body and if the name is simple text that does not include -     * namespace qualifiers.  +     * Bail out also if the body or the next expression require substitutions +     * in order to insure correct behaviour [Bug 219166]       */ -    varTokenPtr = parsePtr->tokenPtr -	    + (parsePtr->tokenPtr->numComponents + 1); -    result = TclPushVarName( interp, varTokenPtr, envPtr,  -            TCL_CREATE_VAR, &localIndex, &simpleVarName, &isScalar ); -    if (result != TCL_OK) { -	return result; +    nextTokenPtr = TokenAfter(testTokenPtr); +    bodyTokenPtr = TokenAfter(nextTokenPtr); +    if ((nextTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) +	    || (bodyTokenPtr->type != TCL_TOKEN_SIMPLE_WORD)) { +	return TCL_ERROR;      } -    /* Push the "index" args and the new element value. */ +    /* +     * Inline compile the initial command. +     */ -    for ( i = 2; i < parsePtr->numWords; ++i ) { +    BODY(startTokenPtr, 1); +    TclEmitOpcode(INST_POP, envPtr); -	/* Advance to next arg */ +    /* +     * Jump to the evaluation of the condition. This code uses the "loop +     * rotation" optimisation (which eliminates one branch from the loop). +     * "for start cond next body" produces then: +     *       start +     *       goto A +     *    B: body                : bodyCodeOffset +     *       next                : nextCodeOffset, continueOffset +     *    A: cond -> result      : testCodeOffset +     *       if (result) goto B +     */ -	varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1); +    TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &jumpEvalCondFixup); -	/* Push an arg */ +    /* +     * Compile the loop body. +     */ -	if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) { -	    TclEmitPush(TclRegisterNewLiteral( envPtr, varTokenPtr[1].start, -		    varTokenPtr[1].size), envPtr); -	} else { -	    result = TclCompileTokens(interp, varTokenPtr+1, -				      varTokenPtr->numComponents, envPtr); -	    if ( result != TCL_OK ) { -		return result; -	    } -	} -    } +    bodyRange = TclCreateExceptRange(LOOP_EXCEPTION_RANGE, envPtr); +    bodyCodeOffset = ExceptionRangeStarts(envPtr, bodyRange); +    BODY(bodyTokenPtr, 4); +    ExceptionRangeEnds(envPtr, bodyRange); +    TclEmitOpcode(INST_POP, envPtr);      /* -     * Duplicate the variable name if it's been pushed.   +     * Compile the "next" subcommand. Note that this exception range will not +     * have a continueOffset (other than -1) connected to it; it won't trap +     * TCL_CONTINUE but rather just TCL_BREAK.       */ -    if ( !simpleVarName || localIndex < 0 ) { -	if ( !simpleVarName || isScalar ) { -	    tempDepth = parsePtr->numWords - 2; -	} else { -	    tempDepth = parsePtr->numWords - 1; -	} -	TclEmitInstInt4( INST_OVER, tempDepth, envPtr ); -    } +    nextRange = TclCreateExceptRange(LOOP_EXCEPTION_RANGE, envPtr); +    envPtr->exceptAuxArrayPtr[nextRange].supportsContinue = 0; +    nextCodeOffset = ExceptionRangeStarts(envPtr, nextRange); +    BODY(nextTokenPtr, 3); +    ExceptionRangeEnds(envPtr, nextRange); +    TclEmitOpcode(INST_POP, envPtr);      /* -     * Duplicate an array index if one's been pushed +     * Compile the test expression then emit the conditional jump that +     * terminates the for.       */ -    if ( simpleVarName && !isScalar ) { -	if ( localIndex < 0 ) { -	    tempDepth = parsePtr->numWords - 1; -	} else { -	    tempDepth = parsePtr->numWords - 2; -	} -	TclEmitInstInt4( INST_OVER, tempDepth, envPtr ); +    if (TclFixupForwardJumpToHere(envPtr, &jumpEvalCondFixup, 127)) { +	bodyCodeOffset += 3; +	nextCodeOffset += 3;      } -    /* -     * Emit code to load the variable's value. -     */ +    SetLineInformation(2); +    TclCompileExprWords(interp, testTokenPtr, 1, envPtr); -    if ( !simpleVarName ) { -	TclEmitOpcode( INST_LOAD_STK, envPtr ); -    } else if ( isScalar ) { -	if ( localIndex < 0 ) { -	    TclEmitOpcode( INST_LOAD_SCALAR_STK, envPtr ); -	} else if ( localIndex < 0x100 ) { -	    TclEmitInstInt1( INST_LOAD_SCALAR1, localIndex, envPtr ); -	} else { -	    TclEmitInstInt4( INST_LOAD_SCALAR4, localIndex, envPtr ); -	} +    jumpDist = CurrentOffset(envPtr) - bodyCodeOffset; +    if (jumpDist > 127) { +	TclEmitInstInt4(INST_JUMP_TRUE4, -jumpDist, envPtr);      } else { -	if ( localIndex < 0 ) { -	    TclEmitOpcode( INST_LOAD_ARRAY_STK, envPtr ); -	} else if ( localIndex < 0x100 ) { -	    TclEmitInstInt1( INST_LOAD_ARRAY1, localIndex, envPtr ); -	} else { -	    TclEmitInstInt4( INST_LOAD_ARRAY4, localIndex, envPtr ); -	} +	TclEmitInstInt1(INST_JUMP_TRUE1, -jumpDist, envPtr);      }      /* -     * Emit the correct variety of 'lset' instruction +     * Fix the starting points of the exception ranges (may have moved due to +     * jump type modification) and set where the exceptions target.       */ -    if ( parsePtr->numWords == 4 ) { -	TclEmitOpcode( INST_LSET_LIST, envPtr ); -    } else { -	TclEmitInstInt4( INST_LSET_FLAT, (parsePtr->numWords - 1), envPtr ); -    } +    envPtr->exceptArrayPtr[bodyRange].codeOffset = bodyCodeOffset; +    envPtr->exceptArrayPtr[bodyRange].continueOffset = nextCodeOffset; + +    envPtr->exceptArrayPtr[nextRange].codeOffset = nextCodeOffset; + +    ExceptionRangeTarget(envPtr, bodyRange, breakOffset); +    ExceptionRangeTarget(envPtr, nextRange, breakOffset); +    TclFinalizeLoopExceptionRange(envPtr, bodyRange); +    TclFinalizeLoopExceptionRange(envPtr, nextRange);      /* -     * Emit code to put the value back in the variable +     * The for command's result is an empty string.       */ -    if ( !simpleVarName ) { -	TclEmitOpcode( INST_STORE_STK, envPtr ); -    } else if ( isScalar ) { -	if ( localIndex < 0 ) { -	    TclEmitOpcode( INST_STORE_SCALAR_STK, envPtr ); -	} else if ( localIndex < 0x100 ) { -	    TclEmitInstInt1( INST_STORE_SCALAR1, localIndex, envPtr ); -	} else { -	    TclEmitInstInt4( INST_STORE_SCALAR4, localIndex, envPtr ); -	} -    } else { -	if ( localIndex < 0 ) { -	    TclEmitOpcode( INST_STORE_ARRAY_STK, envPtr ); -	} else if ( localIndex < 0x100 ) { -	    TclEmitInstInt1( INST_STORE_ARRAY1, localIndex, envPtr ); -	} else { -	    TclEmitInstInt4( INST_STORE_ARRAY4, localIndex, envPtr ); -	} -    } -     +    PushStringLiteral(envPtr, ""); +      return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TclCompileForeachCmd -- + * + *	Procedure called to compile the "foreach" command. + * + * Results: + *	Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer + *	evaluation to runtime. + * + * Side effects: + *	Instructions are added to envPtr to execute the "foreach" command at + *	runtime. + * + *---------------------------------------------------------------------- + */ +int +TclCompileForeachCmd( +    Tcl_Interp *interp,		/* Used for error reporting. */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr)		/* Holds resulting instructions. */ +{ +    return CompileEachloopCmd(interp, parsePtr, cmdPtr, envPtr, +	    TCL_EACH_KEEP_NONE);  }  /*   *----------------------------------------------------------------------   * - * TclCompileRegexpCmd -- + * TclCompileLmapCmd --   * - *	Procedure called to compile the "regexp" command. + *	Procedure called to compile the "lmap" command.   *   * Results: - *	The return value is a standard Tcl result, which is TCL_OK if - *	the compilation was successful.  If the "regexp" command is too - *	complex for this function, then TCL_OUT_LINE_COMPILE is returned, - *	indicating that the command should be compiled "out of line" - *	(that is, not byte-compiled).  If an error occurs, TCL_ERROR is - *	returned, and the interpreter result contains an error message. + *	Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer + *	evaluation to runtime.   *   * Side effects: - *	Instructions are added to envPtr to execute the "regexp" command - *	at runtime. + *	Instructions are added to envPtr to execute the "lmap" command at + *	runtime.   *   *----------------------------------------------------------------------   */  int -TclCompileRegexpCmd(interp, parsePtr, envPtr) -    Tcl_Interp* interp;		/* Tcl interpreter for error reporting */ -    Tcl_Parse* parsePtr;	/* Points to a parse structure for -				 * the command */ -    CompileEnv* envPtr;		/* Holds the resulting instructions */ +TclCompileLmapCmd( +    Tcl_Interp *interp,		/* Used for error reporting. */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr)		/* Holds resulting instructions. */  { -    Tcl_Token *varTokenPtr;	/* Pointer to the Tcl_Token representing -				 * the parse of the RE or string */ -    int i, len, code, nocase, anchorLeft, anchorRight, start; -    char *str; +    return CompileEachloopCmd(interp, parsePtr, cmdPtr, envPtr, +	    TCL_EACH_COLLECT); +} + +/* + *---------------------------------------------------------------------- + * + * CompileEachloopCmd -- + * + *	Procedure called to compile the "foreach" and "lmap" commands. + * + * Results: + *	Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer + *	evaluation to runtime. + * + * Side effects: + *	Instructions are added to envPtr to execute the "foreach" command at + *	runtime. + * + *---------------------------------------------------------------------- + */ + +static int +CompileEachloopCmd( +    Tcl_Interp *interp,		/* Used for error reporting. */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr,		/* Holds resulting instructions. */ +    int collect)		/* Select collecting or accumulating mode +				 * (TCL_EACH_*) */ +{ +    Proc *procPtr = envPtr->procPtr; +    ForeachInfo *infoPtr=NULL;	/* Points to the structure describing this +				 * foreach command. Stored in a AuxData +				 * record in the ByteCode. */ + +    Tcl_Token *tokenPtr, *bodyTokenPtr; +    int jumpBackOffset, infoIndex, range; +    int numWords, numLists, i, j, code = TCL_OK; +    Tcl_Obj *varListObj = NULL; +    DefineLineInformation;	/* TIP #280 */      /* -     * We are only interested in compiling simple regexp cases. -     * Currently supported compile cases are: -     *   regexp ?-nocase? ?--? staticString $var -     *   regexp ?-nocase? ?--? {^staticString$} $var +     * If the foreach command isn't in a procedure, don't compile it inline: +     * the payoff is too small.       */ -    if (parsePtr->numWords < 3) { -	return TCL_OUT_LINE_COMPILE; + +    if (procPtr == NULL) { +	return TCL_ERROR;      } -    nocase = 0; -    varTokenPtr = parsePtr->tokenPtr; +    numWords = parsePtr->numWords; +    if ((numWords < 4) || (numWords%2 != 0)) { +	return TCL_ERROR; +    }      /* -     * We only look for -nocase and -- as options.  Everything else -     * gets pushed to runtime execution.  This is different than regexp's -     * runtime option handling, but satisfies our stricter needs. +     * Bail out if the body requires substitutions in order to insure correct +     * behaviour. [Bug 219166]       */ -    for (i = 1; i < parsePtr->numWords - 2; i++) { -	varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1); -	if (varTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { -	    /* Not a simple string - punt to runtime. */ -	    return TCL_OUT_LINE_COMPILE; -	} -	str = (char *) varTokenPtr[1].start; -	len = varTokenPtr[1].size; -	if ((len == 2) && (str[0] == '-') && (str[1] == '-')) { -	    i++; -	    break; -	} else if ((len > 1) -		&& (strncmp(str, "-nocase", (unsigned) len) == 0)) { -	    nocase = 1; -	} else { -	    /* Not an option we recognize. */ -	    return TCL_OUT_LINE_COMPILE; -	} -    } -    if ((parsePtr->numWords - i) != 2) { -	/* We don't support capturing to variables */ -	return TCL_OUT_LINE_COMPILE; +    for (i = 0, tokenPtr = parsePtr->tokenPtr; i < numWords-1; i++) { +	tokenPtr = TokenAfter(tokenPtr); +    } +    bodyTokenPtr = tokenPtr; +    if (bodyTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { +	return TCL_ERROR;      }      /* -     * Get the regexp string.  If it is not a simple string, punt to runtime. -     * If it has a '-', it could be an incorrectly formed regexp command. +     * Create and initialize the ForeachInfo and ForeachVarList data +     * structures describing this command. Then create a AuxData record +     * pointing to the ForeachInfo structure.       */ -    varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1); -    str = (char *) varTokenPtr[1].start; -    len = varTokenPtr[1].size; -    if ((varTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) || (*str == '-')) { -	return TCL_OUT_LINE_COMPILE; -    } -    if (len == 0) { +    numLists = (numWords - 2)/2; +    infoPtr = ckalloc(sizeof(ForeachInfo) +	    + (numLists - 1) * sizeof(ForeachVarList *)); +    infoPtr->numLists = 0;	/* Count this up as we go */ + +    /* +     * Parse each var list into sequence of var names.  Don't +     * compile the foreach inline if any var name needs substitutions or isn't +     * a scalar, or if any var list needs substitutions. +     */ + +    varListObj = Tcl_NewObj(); +    for (i = 0, tokenPtr = parsePtr->tokenPtr; +	    i < numWords-1; +	    i++, tokenPtr = TokenAfter(tokenPtr)) { +	ForeachVarList *varListPtr; +	int numVars; + +	if (i%2 != 1) { +	    continue; +	} +  	/* -	 * The semantics of regexp are always match on re == "". +	 * If the variable list is empty, we can enter an infinite loop when +	 * the interpreted version would not.  Take care to ensure this does +	 * not happen.  [Bug 1671138]  	 */ -	TclEmitPush(TclRegisterNewLiteral(envPtr, "1", 1), envPtr); -	return TCL_OK; + +	if (!TclWordKnownAtCompileTime(tokenPtr, varListObj) || +		TCL_OK != Tcl_ListObjLength(NULL, varListObj, &numVars) || +		numVars == 0) { +	    code = TCL_ERROR; +	    goto done; +	} + +	varListPtr = ckalloc(sizeof(ForeachVarList) +		+ (numVars - 1) * sizeof(int)); +	varListPtr->numVars = numVars; +	infoPtr->varLists[i/2] = varListPtr; +	infoPtr->numLists++; + +	for (j = 0;  j < numVars;  j++) { +	    Tcl_Obj *varNameObj; +	    const char *bytes; +	    int numBytes, varIndex; + +	    Tcl_ListObjIndex(NULL, varListObj, j, &varNameObj); +	    bytes = TclGetStringFromObj(varNameObj, &numBytes); +	    varIndex = LocalScalar(bytes, numBytes, envPtr); +	    if (varIndex < 0) { +		code = TCL_ERROR; +		goto done; +	    } +	    varListPtr->varIndexes[j] = varIndex; +	} +	Tcl_SetObjLength(varListObj, 0);      }      /* -     * Make a copy of the string that is null-terminated for checks which -     * require such. +     * We will compile the foreach command.       */ -    str = (char *) ckalloc((unsigned) len + 1); -    strncpy(str, varTokenPtr[1].start, (size_t) len); -    str[len] = '\0'; -    start = 0; + +    infoIndex = TclCreateAuxData(infoPtr, &newForeachInfoType, envPtr);      /* -     * Check for anchored REs (ie ^foo$), so we can use string equal if -     * possible. Do not alter the start of str so we can free it correctly. +     * Create the collecting object, unshared.       */ -    if (str[0] == '^') { -	start++; -	anchorLeft = 1; -    } else { -	anchorLeft = 0; -    } -    if ((str[len-1] == '$') && ((len == 1) || (str[len-2] != '\\'))) { -	anchorRight = 1; -	str[--len] = '\0'; -    } else { -	anchorRight = 0; + +    if (collect == TCL_EACH_COLLECT) { +	TclEmitInstInt4(INST_LIST, 0, envPtr);      }      /* -     * On the first (pattern) arg, check to see if any RE special characters -     * are in the word.  If not, this is the same as 'string equal'. +     * Evaluate each value list and leave it on stack.       */ -    if ((len > (1+start)) && (str[start] == '.') && (str[start+1] == '*')) { -	start += 2; -	anchorLeft = 0; -    } -    if ((len > (2+start)) && (str[len-3] != '\\') -	    && (str[len-2] == '.') && (str[len-1] == '*')) { -	len -= 2; -	str[len] = '\0'; -	anchorRight = 0; + +    for (i = 0, tokenPtr = parsePtr->tokenPtr; +	    i < numWords-1; +	    i++, tokenPtr = TokenAfter(tokenPtr)) { +	if ((i%2 == 0) && (i > 0)) { +	    CompileWord(envPtr, tokenPtr, interp, i); +	}      } +    TclEmitInstInt4(INST_FOREACH_START, infoIndex, envPtr); +      /* -     * Don't do anything with REs with other special chars.  Also check if -     * this is a bad RE (do this at the end because it can be expensive). -     * If so, let it complain at runtime. +     * Inline compile the loop body.       */ -    if ((strpbrk(str + start, "*+?{}()[].\\|^$") != NULL) -	    || (Tcl_RegExpCompile(NULL, str) == NULL)) { -	ckfree((char *) str); -	return TCL_OUT_LINE_COMPILE; -    } -    if (anchorLeft && anchorRight) { -	TclEmitPush(TclRegisterNewLiteral(envPtr, str+start, len-start), -		envPtr); +    range = TclCreateExceptRange(LOOP_EXCEPTION_RANGE, envPtr); + +    ExceptionRangeStarts(envPtr, range); +    BODY(bodyTokenPtr, numWords - 1); +    ExceptionRangeEnds(envPtr, range); + +    if (collect == TCL_EACH_COLLECT) { +	TclEmitOpcode(INST_LMAP_COLLECT, envPtr);      } else { -	/* -	 * This needs to find the substring anywhere in the string, so -	 * use string match and *foo*, with appropriate anchoring. -	 */ -	char *newStr  = ckalloc((unsigned) len + 3); -	len -= start; -	if (anchorLeft) { -	    strncpy(newStr, str + start, (size_t) len); -	} else { -	    newStr[0] = '*'; -	    strncpy(newStr + 1, str + start, (size_t) len++); -	} -	if (!anchorRight) { -	    newStr[len++] = '*'; -	} -	newStr[len] = '\0'; -	TclEmitPush(TclRegisterNewLiteral(envPtr, newStr, len), envPtr); -	ckfree((char *) newStr); +	TclEmitOpcode(		INST_POP,			envPtr);      } -    ckfree((char *) str);      /* -     * Push the string arg +     * Bottom of loop code: assign each loop variable and check whether +     * to terminate the loop. Set the loop's break target.       */ -    varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1); -    if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) { -	TclEmitPush(TclRegisterNewLiteral(envPtr, -		varTokenPtr[1].start, varTokenPtr[1].size), envPtr); -    } else { -	code = TclCompileTokens(interp, varTokenPtr+1, -		varTokenPtr->numComponents, envPtr); -	if (code != TCL_OK) { -	    return code; -	} -    } -    if (anchorLeft && anchorRight && !nocase) { -	TclEmitOpcode(INST_STR_EQ, envPtr); -    } else { -	TclEmitInstInt1(INST_STR_MATCH, nocase, envPtr); +    ExceptionRangeTarget(envPtr, range, continueOffset); +    TclEmitOpcode(INST_FOREACH_STEP, envPtr); +    ExceptionRangeTarget(envPtr, range, breakOffset); +    TclFinalizeLoopExceptionRange(envPtr, range); +    TclEmitOpcode(INST_FOREACH_END, envPtr); +    TclAdjustStackDepth(-(numLists+2), envPtr); + +    /* +     * Set the jumpback distance from INST_FOREACH_STEP to the start of the +     * body's code. Misuse loopCtTemp for storing the jump size. +     */ + +    jumpBackOffset = envPtr->exceptArrayPtr[range].continueOffset - +	    envPtr->exceptArrayPtr[range].codeOffset; +    infoPtr->loopCtTemp = -jumpBackOffset; + +    /* +     * The command's result is an empty string if not collecting. If +     * collecting, it is automatically left on stack after FOREACH_END. +     */ + +    if (collect != TCL_EACH_COLLECT) { +	PushStringLiteral(envPtr, "");      } -    return TCL_OK; +    done: +    if (code == TCL_ERROR) { +	FreeForeachInfo(infoPtr); +    } +    Tcl_DecrRefCount(varListObj); +    return code;  }  /*   *----------------------------------------------------------------------   * - * TclCompileReturnCmd -- + * DupForeachInfo --   * - *	Procedure called to compile the "return" command. + *	This procedure duplicates a ForeachInfo structure created as auxiliary + *	data during the compilation of a foreach command.   *   * Results: - *	The return value is a standard Tcl result, which is TCL_OK if the - *	compilation was successful.  If the particular return command is - *	too complex for this function (ie, return with any flags like "-code" - *	or "-errorinfo"), TCL_OUT_LINE_COMPILE is returned, indicating that - *	the command should be compiled "out of line" (eg, not byte compiled). - *	If an error occurs then the interpreter's result contains a standard - *	error message. + *	A pointer to a newly allocated copy of the existing ForeachInfo + *	structure is returned.   *   * Side effects: - *	Instructions are added to envPtr to execute the "return" command - *	at runtime. + *	Storage for the copied ForeachInfo record is allocated. If the + *	original ForeachInfo structure pointed to any ForeachVarList records, + *	these structures are also copied and pointers to them are stored in + *	the new ForeachInfo record.   *   *----------------------------------------------------------------------   */ -int -TclCompileReturnCmd(interp, parsePtr, envPtr) -    Tcl_Interp *interp;		/* Used for error reporting. */ -    Tcl_Parse *parsePtr;	/* Points to a parse structure for the -				 * command created by Tcl_ParseCommand. */ -    CompileEnv *envPtr;		/* Holds resulting instructions. */ +static ClientData +DupForeachInfo( +    ClientData clientData)	/* The foreach command's compilation auxiliary +				 * data to duplicate. */  { -    Tcl_Token *varTokenPtr; -    int code; -    int index = envPtr->exceptArrayNext - 1; - -    /* -     * If we're not in a procedure, don't compile. -     */ - -    if (envPtr->procPtr == NULL) { -	return TCL_OUT_LINE_COMPILE; -    } - -    /* -     * Look back through the ExceptionRanges of the current CompileEnv, -     * from exceptArrayPtr[(exceptArrayNext - 1)] down to  -     * exceptArrayPtr[0] to see if any of them is an enclosing [catch]. -     * If there's an enclosing [catch], don't compile. -     */ +    register ForeachInfo *srcPtr = clientData; +    ForeachInfo *dupPtr; +    register ForeachVarList *srcListPtr, *dupListPtr; +    int numVars, i, j, numLists = srcPtr->numLists; -    while (index >= 0) { -	ExceptionRange range = envPtr->exceptArrayPtr[index]; -	if ((range.type == CATCH_EXCEPTION_RANGE)  -		&& (range.catchOffset == -1)) { -	    return TCL_OUT_LINE_COMPILE; -	} -	index--; -    } +    dupPtr = ckalloc(sizeof(ForeachInfo) +	    + numLists * sizeof(ForeachVarList *)); +    dupPtr->numLists = numLists; +    dupPtr->firstValueTemp = srcPtr->firstValueTemp; +    dupPtr->loopCtTemp = srcPtr->loopCtTemp; -    switch (parsePtr->numWords) { -	case 1: { -	    /* -	     * Simple case:  [return] -	     * Just push the literal string "". -	     */ -	    TclEmitPush(TclRegisterNewLiteral(envPtr, "", 0), envPtr); -	    break; -	} -	case 2: { -	    /* -	     * More complex cases: -	     * [return "foo"] -	     * [return $value] -	     * [return [otherCmd]] -	     */ -	    varTokenPtr = parsePtr->tokenPtr -		+ (parsePtr->tokenPtr->numComponents + 1); -	    if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) { -		/* -		 * [return "foo"] case:  the parse token is a simple word, -		 * so just push it. -		 */ -		TclEmitPush(TclRegisterNewLiteral(envPtr, varTokenPtr[1].start, -			varTokenPtr[1].size), envPtr); -	    } else { -		/* -		 * Parse token is more complex, so compile it; this handles the -		 * variable reference and nested command cases.  If the -		 * parse token can be byte-compiled, then this instance of -		 * "return" will be byte-compiled; otherwise it will be -		 * out line compiled. -		 */ -		code = TclCompileTokens(interp, varTokenPtr+1, -			varTokenPtr->numComponents, envPtr); -		if (code != TCL_OK) { -		    return code; -		} -	    } -	    break; -	} -	default: { -	    /* -	     * Most complex return cases: everything else, including -	     * [return -code error], etc. -	     */ -	    return TCL_OUT_LINE_COMPILE; +    for (i = 0;  i < numLists;  i++) { +	srcListPtr = srcPtr->varLists[i]; +	numVars = srcListPtr->numVars; +	dupListPtr = ckalloc(sizeof(ForeachVarList) +		+ numVars * sizeof(int)); +	dupListPtr->numVars = numVars; +	for (j = 0;  j < numVars;  j++) { +	    dupListPtr->varIndexes[j] =	srcListPtr->varIndexes[j];  	} +	dupPtr->varLists[i] = dupListPtr;      } - -    /* -     * The INST_DONE opcode actually causes the branching out of the -     * subroutine, and takes the top stack item as the return result -     * (which is why we pushed the value above). -     */ -    TclEmitOpcode(INST_DONE, envPtr); -    return TCL_OK; +    return dupPtr;  }  /*   *----------------------------------------------------------------------   * - * TclCompileSetCmd -- + * FreeForeachInfo --   * - *	Procedure called to compile the "set" command. + *	Procedure to free a ForeachInfo structure created as auxiliary data + *	during the compilation of a foreach command.   *   * Results: - *	The return value is a standard Tcl result, which is normally TCL_OK - *	unless there was an error while parsing string. If an error occurs - *	then the interpreter's result contains a standard error message. If - *	complation fails because the set command requires a second level of - *	substitutions, TCL_OUT_LINE_COMPILE is returned indicating that the - *	set command should be compiled "out of line" by emitting code to - *	invoke its command procedure (Tcl_SetCmd) at runtime. + *	None.   *   * Side effects: - *	Instructions are added to envPtr to execute the "set" command - *	at runtime. + *	Storage for the ForeachInfo structure pointed to by the ClientData + *	argument is freed as is any ForeachVarList record pointed to by the + *	ForeachInfo structure.   *   *----------------------------------------------------------------------   */ -int -TclCompileSetCmd(interp, parsePtr, envPtr) -    Tcl_Interp *interp;		/* Used for error reporting. */ -    Tcl_Parse *parsePtr;	/* Points to a parse structure for the -				 * command created by Tcl_ParseCommand. */ -    CompileEnv *envPtr;		/* Holds resulting instructions. */ +static void +FreeForeachInfo( +    ClientData clientData)	/* The foreach command's compilation auxiliary +				 * data to free. */  { -    Tcl_Token *varTokenPtr, *valueTokenPtr; -    int isAssignment, isScalar, simpleVarName, localIndex, numWords; -    int code = TCL_OK; - -    numWords = parsePtr->numWords; -    if ((numWords != 2) && (numWords != 3)) { -	Tcl_ResetResult(interp); -	Tcl_AppendToObj(Tcl_GetObjResult(interp), -	        "wrong # args: should be \"set varName ?newValue?\"", -1); -        return TCL_ERROR; -    } -    isAssignment = (numWords == 3); - -    /* -     * Decide if we can use a frame slot for the var/array name or if we -     * need to emit code to compute and push the name at runtime. We use a -     * frame slot (entry in the array of local vars) if we are compiling a -     * procedure body and if the name is simple text that does not include -     * namespace qualifiers.  -     */ - -    varTokenPtr = parsePtr->tokenPtr -	    + (parsePtr->tokenPtr->numComponents + 1); - -    code = TclPushVarName(interp, varTokenPtr, envPtr, TCL_CREATE_VAR, -	    &localIndex, &simpleVarName, &isScalar); -    if (code != TCL_OK) { -	goto done; -    } - -    /* -     * If we are doing an assignment, push the new value. -     */ - -    if (isAssignment) { -	valueTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1); -	if (valueTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) { -	    TclEmitPush(TclRegisterNewLiteral(envPtr, valueTokenPtr[1].start, -		    valueTokenPtr[1].size), envPtr); -	} else { -	    code = TclCompileTokens(interp, valueTokenPtr+1, -	            valueTokenPtr->numComponents, envPtr); -	    if (code != TCL_OK) { -		goto done; -	    } -	} -    } - -    /* -     * Emit instructions to set/get the variable. -     */ +    register ForeachInfo *infoPtr = clientData; +    register ForeachVarList *listPtr; +    int numLists = infoPtr->numLists; +    register int i; -    if (simpleVarName) { -	if (isScalar) { -	    if (localIndex >= 0) { -		if (localIndex <= 255) { -		    TclEmitInstInt1((isAssignment? -		            INST_STORE_SCALAR1 : INST_LOAD_SCALAR1), -			    localIndex, envPtr); -		} else { -		    TclEmitInstInt4((isAssignment? -			    INST_STORE_SCALAR4 : INST_LOAD_SCALAR4), -			    localIndex, envPtr); -		} -	    } else { -		TclEmitOpcode((isAssignment? -		        INST_STORE_SCALAR_STK : INST_LOAD_SCALAR_STK), envPtr); -	    } -	} else { -	    if (localIndex >= 0) { -		if (localIndex <= 255) { -		    TclEmitInstInt1((isAssignment? -		            INST_STORE_ARRAY1 : INST_LOAD_ARRAY1), -			    localIndex, envPtr); -		} else { -		    TclEmitInstInt4((isAssignment? -			    INST_STORE_ARRAY4 : INST_LOAD_ARRAY4), -			    localIndex, envPtr); -		} -	    } else { -		TclEmitOpcode((isAssignment? -		        INST_STORE_ARRAY_STK : INST_LOAD_ARRAY_STK), envPtr); -	    } -	} -    } else { -	TclEmitOpcode((isAssignment? INST_STORE_STK : INST_LOAD_STK), envPtr); +    for (i = 0;  i < numLists;  i++) { +	listPtr = infoPtr->varLists[i]; +	ckfree(listPtr);      } -	 -    done: -    return code; +    ckfree(infoPtr);  }  /*   *----------------------------------------------------------------------   * - * TclCompileStringCmd -- + * PrintForeachInfo, DisassembleForeachInfo --   * - *	Procedure called to compile the "string" command. + *	Functions to write a human-readable or script-readablerepresentation + *	of a ForeachInfo structure to a Tcl_Obj for debugging.   *   * Results: - *	The return value is a standard Tcl result, which is TCL_OK if the - *	compilation was successful.  If the command cannot be byte-compiled, - *	TCL_OUT_LINE_COMPILE is returned.  If an error occurs then the - *	interpreter's result contains an error message, and TCL_ERROR is - *	returned. + *	None.   *   * Side effects: - *	Instructions are added to envPtr to execute the "string" command - *	at runtime. + *	None.   *   *----------------------------------------------------------------------   */ -int -TclCompileStringCmd(interp, parsePtr, envPtr) -    Tcl_Interp *interp;		/* Used for error reporting. */ -    Tcl_Parse *parsePtr;	/* Points to a parse structure for the -				 * command created by Tcl_ParseCommand. */ -    CompileEnv *envPtr;		/* Holds resulting instructions. */ +static void +PrintForeachInfo( +    ClientData clientData, +    Tcl_Obj *appendObj, +    ByteCode *codePtr, +    unsigned int pcOffset)  { -    Tcl_Token *opTokenPtr, *varTokenPtr; -    Tcl_Obj *opObj; -    int index; -    int code; -     -    static CONST char *options[] = { -	"bytelength",	"compare",	"equal",	"first", -	"index",	"is",		"last",		"length", -	"map",		"match",	"range",	"repeat", -	"replace",	"tolower",	"toupper",	"totitle", -	"trim",		"trimleft",	"trimright", -	"wordend",	"wordstart",	(char *) NULL -    }; -    enum options { -	STR_BYTELENGTH,	STR_COMPARE,	STR_EQUAL,	STR_FIRST, -	STR_INDEX,	STR_IS,		STR_LAST,	STR_LENGTH, -	STR_MAP,	STR_MATCH,	STR_RANGE,	STR_REPEAT, -	STR_REPLACE,	STR_TOLOWER,	STR_TOUPPER,	STR_TOTITLE, -	STR_TRIM,	STR_TRIMLEFT,	STR_TRIMRIGHT, -	STR_WORDEND,	STR_WORDSTART -    };	   - -    if (parsePtr->numWords < 2) { -	/* Fail at run time, not in compilation */ -	return TCL_OUT_LINE_COMPILE; -    } -    opTokenPtr = parsePtr->tokenPtr -	+ (parsePtr->tokenPtr->numComponents + 1); - -    opObj = Tcl_NewStringObj(opTokenPtr->start, opTokenPtr->size); -    if (Tcl_GetIndexFromObj(interp, opObj, options, "option", 0, -	    &index) != TCL_OK) { -	Tcl_DecrRefCount(opObj); -	Tcl_ResetResult(interp); -	return TCL_OUT_LINE_COMPILE; -    } -    Tcl_DecrRefCount(opObj); - -    varTokenPtr = opTokenPtr + (opTokenPtr->numComponents + 1); - -    switch ((enum options) index) { -	case STR_BYTELENGTH: -	case STR_FIRST: -	case STR_IS: -	case STR_LAST: -	case STR_MAP: -	case STR_RANGE: -	case STR_REPEAT: -	case STR_REPLACE: -	case STR_TOLOWER: -	case STR_TOUPPER: -	case STR_TOTITLE: -	case STR_TRIM: -	case STR_TRIMLEFT: -	case STR_TRIMRIGHT: -	case STR_WORDEND: -	case STR_WORDSTART: -	    /* -	     * All other cases: compile out of line. -	     */ -	    return TCL_OUT_LINE_COMPILE; - -	case STR_COMPARE:  -	case STR_EQUAL: { -	    int i; -	    /* -	     * If there are any flags to the command, we can't byte compile it -	     * because the INST_STR_EQ bytecode doesn't support flags. -	     */ - -	    if (parsePtr->numWords != 4) { -		return TCL_OUT_LINE_COMPILE; -	    } - -	    /* -	     * Push the two operands onto the stack. -	     */ - -	    for (i = 0; i < 2; i++) { -		if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) { -		    TclEmitPush(TclRegisterNewLiteral(envPtr, -			    varTokenPtr[1].start, varTokenPtr[1].size), envPtr); -		} else { -		    code = TclCompileTokens(interp, varTokenPtr+1, -			    varTokenPtr->numComponents, envPtr); -		    if (code != TCL_OK) { -			return code; -		    } -		} -		varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1); +    register ForeachInfo *infoPtr = clientData; +    register ForeachVarList *varsPtr; +    int i, j; + +    Tcl_AppendToObj(appendObj, "data=[", -1); + +    for (i=0 ; i<infoPtr->numLists ; i++) { +	if (i) { +	    Tcl_AppendToObj(appendObj, ", ", -1); +	} +	Tcl_AppendPrintfToObj(appendObj, "%%v%u", +		(unsigned) (infoPtr->firstValueTemp + i)); +    } +    Tcl_AppendPrintfToObj(appendObj, "], loop=%%v%u", +	    (unsigned) infoPtr->loopCtTemp); +    for (i=0 ; i<infoPtr->numLists ; i++) { +	if (i) { +	    Tcl_AppendToObj(appendObj, ",", -1); +	} +	Tcl_AppendPrintfToObj(appendObj, "\n\t\t it%%v%u\t[", +		(unsigned) (infoPtr->firstValueTemp + i)); +	varsPtr = infoPtr->varLists[i]; +	for (j=0 ; j<varsPtr->numVars ; j++) { +	    if (j) { +		Tcl_AppendToObj(appendObj, ", ", -1);  	    } - -	    TclEmitOpcode(((((enum options) index) == STR_COMPARE) ? -		    INST_STR_CMP : INST_STR_EQ), envPtr); -	    return TCL_OK; +	    Tcl_AppendPrintfToObj(appendObj, "%%v%u", +		    (unsigned) varsPtr->varIndexes[j]);  	} -	case STR_INDEX: { -	    int i; +	Tcl_AppendToObj(appendObj, "]", -1); +    } +} -	    if (parsePtr->numWords != 4) { -		/* Fail at run time, not in compilation */ -		return TCL_OUT_LINE_COMPILE; +static void +PrintNewForeachInfo( +    ClientData clientData, +    Tcl_Obj *appendObj, +    ByteCode *codePtr, +    unsigned int pcOffset) +{ +    register ForeachInfo *infoPtr = clientData; +    register ForeachVarList *varsPtr; +    int i, j; + +    Tcl_AppendPrintfToObj(appendObj, "jumpOffset=%+d, vars=", +	    infoPtr->loopCtTemp); +    for (i=0 ; i<infoPtr->numLists ; i++) { +	if (i) { +	    Tcl_AppendToObj(appendObj, ",", -1); +	} +	Tcl_AppendToObj(appendObj, "[", -1); +	varsPtr = infoPtr->varLists[i]; +	for (j=0 ; j<varsPtr->numVars ; j++) { +	    if (j) { +		Tcl_AppendToObj(appendObj, ",", -1);  	    } +	    Tcl_AppendPrintfToObj(appendObj, "%%v%u", +		    (unsigned) varsPtr->varIndexes[j]); +	} +	Tcl_AppendToObj(appendObj, "]", -1); +    } +} -	    /* -	     * Push the two operands onto the stack. -	     */ - -	    for (i = 0; i < 2; i++) { -		if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) { -		    TclEmitPush(TclRegisterNewLiteral(envPtr, -			    varTokenPtr[1].start, varTokenPtr[1].size), envPtr); -		} else { -		    code = TclCompileTokens(interp, varTokenPtr+1, -			    varTokenPtr->numComponents, envPtr); -		    if (code != TCL_OK) { -			return code; -		    } -		} -		varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1); -	    } +static void +DisassembleForeachInfo( +    ClientData clientData, +    Tcl_Obj *dictObj, +    ByteCode *codePtr, +    unsigned int pcOffset) +{ +    register ForeachInfo *infoPtr = clientData; +    register ForeachVarList *varsPtr; +    int i, j; +    Tcl_Obj *objPtr, *innerPtr; -	    TclEmitOpcode(INST_STR_INDEX, envPtr); -	    return TCL_OK; -	} -	case STR_LENGTH: { -	    if (parsePtr->numWords != 3) { -		/* Fail at run time, not in compilation */ -		return TCL_OUT_LINE_COMPILE; -	    } +    /* +     * Data stores. +     */ -	    if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) { -		/* -		 * Here someone is asking for the length of a static string. -		 * Just push the actual character (not byte) length. -		 */ -		char buf[TCL_INTEGER_SPACE]; -		int len = Tcl_NumUtfChars(varTokenPtr[1].start, -			varTokenPtr[1].size); -		len = sprintf(buf, "%d", len); -		TclEmitPush(TclRegisterNewLiteral(envPtr, buf, len), envPtr); -		return TCL_OK; -	    } else { -		code = TclCompileTokens(interp, varTokenPtr+1, -			varTokenPtr->numComponents, envPtr); -		if (code != TCL_OK) { -		    return code; -		} -	    } -	    TclEmitOpcode(INST_STR_LEN, envPtr); -	    return TCL_OK; -	} -	case STR_MATCH: { -	    int i, length, exactMatch = 0, nocase = 0; -	    CONST char *str; +    objPtr = Tcl_NewObj(); +    for (i=0 ; i<infoPtr->numLists ; i++) { +	Tcl_ListObjAppendElement(NULL, objPtr, +		Tcl_NewIntObj(infoPtr->firstValueTemp + i)); +    } +    Tcl_DictObjPut(NULL, dictObj, Tcl_NewStringObj("data", -1), objPtr); -	    if (parsePtr->numWords < 4 || parsePtr->numWords > 5) { -		/* Fail at run time, not in compilation */ -		return TCL_OUT_LINE_COMPILE; -	    } +    /* +     * Loop counter. +     */ -	    if (parsePtr->numWords == 5) { -		if (varTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { -		    return TCL_OUT_LINE_COMPILE; -		} -		str    = varTokenPtr[1].start; -		length = varTokenPtr[1].size; -		if ((length > 1) && -			strncmp(str, "-nocase", (size_t) length) == 0) { -		    nocase = 1; -		} else { -		    /* Fail at run time, not in compilation */ -		    return TCL_OUT_LINE_COMPILE; -		} -		varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1); -	    } +    Tcl_DictObjPut(NULL, dictObj, Tcl_NewStringObj("loop", -1), +	   Tcl_NewIntObj(infoPtr->loopCtTemp)); -	    for (i = 0; i < 2; i++) { -		if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) { -		    str = varTokenPtr[1].start; -		    length = varTokenPtr[1].size; -		    if (!nocase && (i == 0)) { -			/* -			 * On the first (pattern) arg, check to see if any -			 * glob special characters are in the word '*[]?\\'. -			 * If not, this is the same as 'string equal'.  We -			 * can use strpbrk here because the glob chars are all -			 * in the ascii-7 range.  If -nocase was specified, -			 * we can't do this because INST_STR_EQ has no support -			 * for nocase. -			 */ -			Tcl_Obj *copy = Tcl_NewStringObj(str, length); -			Tcl_IncrRefCount(copy); -			exactMatch = (strpbrk(Tcl_GetString(copy), -				"*[]?\\") == NULL); -			Tcl_DecrRefCount(copy); -		    } -		    TclEmitPush( -			    TclRegisterNewLiteral(envPtr, str, length), envPtr); -		} else { -		    code = TclCompileTokens(interp, varTokenPtr+1, -			    varTokenPtr->numComponents, envPtr); -		    if (code != TCL_OK) { -			return code; -		    } -		} -		varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1); -	    } +    /* +     * Assignment targets. +     */ -	    if (exactMatch) { -		TclEmitOpcode(INST_STR_EQ, envPtr); -	    } else { -		TclEmitInstInt1(INST_STR_MATCH, nocase, envPtr); -	    } -	    return TCL_OK; +    objPtr = Tcl_NewObj(); +    for (i=0 ; i<infoPtr->numLists ; i++) { +	innerPtr = Tcl_NewObj(); +	varsPtr = infoPtr->varLists[i]; +	for (j=0 ; j<varsPtr->numVars ; j++) { +	    Tcl_ListObjAppendElement(NULL, innerPtr, +		    Tcl_NewIntObj(varsPtr->varIndexes[j]));  	} +	Tcl_ListObjAppendElement(NULL, objPtr, innerPtr);      } - -    return TCL_OK; +    Tcl_DictObjPut(NULL, dictObj, Tcl_NewStringObj("assign", -1), objPtr);  } - -/* - *---------------------------------------------------------------------- - * - * TclCompileVariableCmd -- - * - *	Procedure called to reserve the local variables for the  - *      "variable" command. The command itself is *not* compiled. - * - * Results: - *      Always returns TCL_OUT_LINE_COMPILE. - * - * Side effects: - *      Indexed local variables are added to the environment. - * - *---------------------------------------------------------------------- - */ -int -TclCompileVariableCmd(interp, parsePtr, envPtr) -    Tcl_Interp *interp;		/* Used for error reporting. */ -    Tcl_Parse *parsePtr;	/* Points to a parse structure for the -				 * command created by Tcl_ParseCommand. */ -    CompileEnv *envPtr;		/* Holds resulting instructions. */ + +static void +DisassembleNewForeachInfo( +    ClientData clientData, +    Tcl_Obj *dictObj, +    ByteCode *codePtr, +    unsigned int pcOffset)  { -    Tcl_Token *varTokenPtr; -    int i, numWords; -    CONST char *varName, *tail; -     -    if (envPtr->procPtr == NULL) { -	return TCL_OUT_LINE_COMPILE; -    } +    register ForeachInfo *infoPtr = clientData; +    register ForeachVarList *varsPtr; +    int i, j; +    Tcl_Obj *objPtr, *innerPtr; -    numWords = parsePtr->numWords; -     -    varTokenPtr = parsePtr->tokenPtr -	+ (parsePtr->tokenPtr->numComponents + 1); -    for (i = 1; i < numWords; i += 2) { -	if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) { -	    varName = varTokenPtr[1].start; -	    tail = varName + varTokenPtr[1].size - 1; -	    if ((*tail == ')') || (tail < varName)) continue; -	    while ((tail > varName) && ((*tail != ':') || (*(tail-1) != ':'))) { -		tail--; -	    } -	    if ((*tail == ':') && (tail > varName)) { -		tail++; -	    } -	    (void) TclFindCompiledLocal(tail, (tail-varName+1), -		    /*create*/ 1, /*flags*/ 0, envPtr->procPtr); -	    varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1); +    /* +     * Jump offset. +     */ + +    Tcl_DictObjPut(NULL, dictObj, Tcl_NewStringObj("jumpOffset", -1), +	   Tcl_NewIntObj(infoPtr->loopCtTemp)); + +    /* +     * Assignment targets. +     */ + +    objPtr = Tcl_NewObj(); +    for (i=0 ; i<infoPtr->numLists ; i++) { +	innerPtr = Tcl_NewObj(); +	varsPtr = infoPtr->varLists[i]; +	for (j=0 ; j<varsPtr->numVars ; j++) { +	    Tcl_ListObjAppendElement(NULL, innerPtr, +		    Tcl_NewIntObj(varsPtr->varIndexes[j]));  	} +	Tcl_ListObjAppendElement(NULL, objPtr, innerPtr);      } -    return TCL_OUT_LINE_COMPILE; +    Tcl_DictObjPut(NULL, dictObj, Tcl_NewStringObj("assign", -1), objPtr);  }  /*   *----------------------------------------------------------------------   * - * TclCompileWhileCmd -- + * TclCompileFormatCmd --   * - *	Procedure called to compile the "while" command. + *	Procedure called to compile the "format" command. Handles cases that + *	can be done as constants or simple string concatenation only.   *   * Results: - *	The return value is a standard Tcl result, which is TCL_OK if - *	compilation was successful. If an error occurs then the - *	interpreter's result contains a standard error message and TCL_ERROR - *	is returned. If compilation failed because the command is too - *	complex for TclCompileWhileCmd, TCL_OUT_LINE_COMPILE is returned - *	indicating that the while command should be compiled "out of line" - *	by emitting code to invoke its command procedure at runtime. + *	Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer + *	evaluation to runtime.   *   * Side effects: - *	Instructions are added to envPtr to execute the "while" command - *	at runtime. + *	Instructions are added to envPtr to execute the "format" command at + *	runtime.   *   *----------------------------------------------------------------------   */  int -TclCompileWhileCmd(interp, parsePtr, envPtr) -    Tcl_Interp *interp;		/* Used for error reporting. */ -    Tcl_Parse *parsePtr;	/* Points to a parse structure for the -				 * command created by Tcl_ParseCommand. */ -    CompileEnv *envPtr;		/* Holds resulting instructions. */ +TclCompileFormatCmd( +    Tcl_Interp *interp,		/* Used for error reporting. */ +    Tcl_Parse *parsePtr,	/* Points to a parse structure for the command +				 * created by Tcl_ParseCommand. */ +    Command *cmdPtr,		/* Points to defintion of command being +				 * compiled. */ +    CompileEnv *envPtr)		/* Holds resulting instructions. */  { -    Tcl_Token *testTokenPtr, *bodyTokenPtr; -    JumpFixup jumpEvalCondFixup; -    int testCodeOffset, bodyCodeOffset, jumpDist; -    int range, code; -    char buffer[32 + TCL_INTEGER_SPACE]; -    int savedStackDepth = envPtr->currStackDepth; -    int loopMayEnd = 1;         /* This is set to 0 if it is recognized as -				 * an infinite loop. */ -    Tcl_Obj *boolObj; -    int boolVal; - -    if (parsePtr->numWords != 3) { -	Tcl_ResetResult(interp); -	Tcl_AppendToObj(Tcl_GetObjResult(interp), -	        "wrong # args: should be \"while test command\"", -1); -	return TCL_ERROR; -    } +    DefineLineInformation;	/* TIP #280 */ +    Tcl_Token *tokenPtr = parsePtr->tokenPtr; +    Tcl_Obj **objv, *formatObj, *tmpObj; +    char *bytes, *start; +    int i, j, len;      /* -     * If the test expression requires substitutions, don't compile the -     * while command inline. E.g., the expression might cause the loop to -     * never execute or execute forever, as in "while "$x < 5" {}". -     * -     * Bail out also if the body expression requires substitutions -     * in order to insure correct behaviour [Bug 219166] +     * Don't handle any guaranteed-error cases.       */ -    testTokenPtr = parsePtr->tokenPtr -	    + (parsePtr->tokenPtr->numComponents + 1); -    bodyTokenPtr = testTokenPtr + (testTokenPtr->numComponents + 1); -    if ((testTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) -	    || (bodyTokenPtr->type != TCL_TOKEN_SIMPLE_WORD)) { -	return TCL_OUT_LINE_COMPILE; +    if (parsePtr->numWords < 2) { +	return TCL_ERROR;      }      /* -     * Find out if the condition is a constant.  +     * Check if the argument words are all compile-time-known literals; that's +     * a case we can handle by compiling to a constant.       */ -    boolObj = Tcl_NewStringObj(testTokenPtr[1].start, testTokenPtr[1].size); -    Tcl_IncrRefCount(boolObj); -    code = Tcl_GetBooleanFromObj(NULL, boolObj, &boolVal); -    Tcl_DecrRefCount(boolObj); -    if (code == TCL_OK) { -	if (boolVal) { -	    /* -	     * it is an infinite loop  -	     */ - -	    loopMayEnd = 0;   -	} else { -	    /* -	     * This is an empty loop: "while 0 {...}" or such. -	     * Compile no bytecodes. -	     */ +    formatObj = Tcl_NewObj(); +    Tcl_IncrRefCount(formatObj); +    tokenPtr = TokenAfter(tokenPtr); +    if (!TclWordKnownAtCompileTime(tokenPtr, formatObj)) { +	Tcl_DecrRefCount(formatObj); +	return TCL_ERROR; +    } -	    goto pushResult; +    objv = ckalloc((parsePtr->numWords-2) * sizeof(Tcl_Obj *)); +    for (i=0 ; i+2 < parsePtr->numWords ; i++) { +	tokenPtr = TokenAfter(tokenPtr); +	objv[i] = Tcl_NewObj(); +	Tcl_IncrRefCount(objv[i]); +	if (!TclWordKnownAtCompileTime(tokenPtr, objv[i])) { +	    goto checkForStringConcatCase;  	}      } -    /*  -     * Create a ExceptionRange record for the loop body. This is used to -     * implement break and continue. +    /* +     * Everything is a literal, so the result is constant too (or an error if +     * the format is broken). Do the format now.       */ -    envPtr->exceptDepth++; -    envPtr->maxExceptDepth = -	TclMax(envPtr->exceptDepth, envPtr->maxExceptDepth); -    range = TclCreateExceptRange(LOOP_EXCEPTION_RANGE, envPtr); +    tmpObj = Tcl_Format(interp, Tcl_GetString(formatObj), +	    parsePtr->numWords-2, objv); +    for (; --i>=0 ;) { +	Tcl_DecrRefCount(objv[i]); +    } +    ckfree(objv); +    Tcl_DecrRefCount(formatObj); +    if (tmpObj == NULL) { +	TclCompileSyntaxError(interp, envPtr); +	return TCL_OK; +    }      /* -     * Jump to the evaluation of the condition. This code uses the "loop -     * rotation" optimisation (which eliminates one branch from the loop). -     * "while cond body" produces then: -     *       goto A -     *    B: body                : bodyCodeOffset -     *    A: cond -> result      : testCodeOffset, continueOffset -     *       if (result) goto B -     * -     * The infinite loop "while 1 body" produces: -     *    B: body                : all three offsets here -     *       goto B +     * Not an error, always a constant result, so just push the result as a +     * literal. Job done.       */ -    if (loopMayEnd) { -	TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &jumpEvalCondFixup); -	testCodeOffset = 0; /* avoid compiler warning */ -    } else { -	testCodeOffset = (envPtr->codeNext - envPtr->codeStart); -    } -     +    bytes = TclGetStringFromObj(tmpObj, &len); +    PushLiteral(envPtr, bytes, len); +    Tcl_DecrRefCount(tmpObj); +    return TCL_OK; +  checkForStringConcatCase:      /* -     * Compile the loop body. +     * See if we can generate a sequence of things to concatenate. This +     * requires that all the % sequences be %s or %%, as everything else is +     * sufficiently complex that we don't bother. +     * +     * First, get the state of the system relatively sensible (cleaning up +     * after our attempt to spot a literal).       */ -    bodyCodeOffset = (envPtr->codeNext - envPtr->codeStart); -    code = TclCompileCmdWord(interp, bodyTokenPtr+1, -	    bodyTokenPtr->numComponents, envPtr); -    envPtr->currStackDepth = savedStackDepth + 1; -    if (code != TCL_OK) { -	if (code == TCL_ERROR) { -	    sprintf(buffer, "\n    (\"while\" body line %d)", -		    interp->errorLine); -            Tcl_AddObjErrorInfo(interp, buffer, -1); -        } -	goto error; -    } -    envPtr->exceptArrayPtr[range].numCodeBytes = -	    (envPtr->codeNext - envPtr->codeStart) - bodyCodeOffset; -    TclEmitOpcode(INST_POP, envPtr); +    for (; i>=0 ; i--) { +	Tcl_DecrRefCount(objv[i]); +    } +    ckfree(objv); +    tokenPtr = TokenAfter(parsePtr->tokenPtr); +    tokenPtr = TokenAfter(tokenPtr); +    i = 0;      /* -     * Compile the test expression then emit the conditional jump that -     * terminates the while. We already know it's a simple word. +     * Now scan through and check for non-%s and non-%% substitutions.       */ -    if (loopMayEnd) { -	testCodeOffset = (envPtr->codeNext - envPtr->codeStart); -	jumpDist = testCodeOffset - jumpEvalCondFixup.codeOffset; -	if (TclFixupForwardJump(envPtr, &jumpEvalCondFixup, jumpDist, 127)) { -	    bodyCodeOffset += 3; -	    testCodeOffset += 3; -	} -	envPtr->currStackDepth = savedStackDepth; -	code = TclCompileExprWords(interp, testTokenPtr, 1, envPtr); -	if (code != TCL_OK) { -	    if (code == TCL_ERROR) { -		Tcl_AddObjErrorInfo(interp, -				    "\n    (\"while\" test expression)", -1); +    for (bytes = Tcl_GetString(formatObj) ; *bytes ; bytes++) { +	if (*bytes == '%') { +	    bytes++; +	    if (*bytes == 's') { +		i++; +		continue; +	    } else if (*bytes == '%') { +		continue;  	    } -	    goto error; +	    Tcl_DecrRefCount(formatObj); +	    return TCL_ERROR;  	} -	envPtr->currStackDepth = savedStackDepth + 1; -     -	jumpDist = (envPtr->codeNext - envPtr->codeStart) - bodyCodeOffset; -	if (jumpDist > 127) { -	    TclEmitInstInt4(INST_JUMP_TRUE4, -jumpDist, envPtr); -	} else { -	    TclEmitInstInt1(INST_JUMP_TRUE1, -jumpDist, envPtr); -	} -    } else { -	jumpDist = (envPtr->codeNext - envPtr->codeStart) - bodyCodeOffset; -	if (jumpDist > 127) { -	    TclEmitInstInt4(INST_JUMP4, -jumpDist, envPtr); -	} else { -	    TclEmitInstInt1(INST_JUMP1, -jumpDist, envPtr); -	}	      } +    /* +     * Check if the number of things to concatenate will fit in a byte. +     */ + +    if (i+2 != parsePtr->numWords || i > 125) { +	Tcl_DecrRefCount(formatObj); +	return TCL_ERROR; +    }      /* -     * Set the loop's body, continue and break offsets. +     * Generate the pushes of the things to concatenate, a sequence of +     * literals and compiled tokens (of which at least one is non-literal or +     * we'd have the case in the first half of this function) which we will +     * concatenate.       */ -    envPtr->exceptArrayPtr[range].continueOffset = testCodeOffset; -    envPtr->exceptArrayPtr[range].codeOffset = bodyCodeOffset; -    envPtr->exceptArrayPtr[range].breakOffset = -	    (envPtr->codeNext - envPtr->codeStart); -     +    i = 0;			/* The count of things to concat. */ +    j = 2;			/* The index into the argument tokens, for +				 * TIP#280 handling. */ +    start = Tcl_GetString(formatObj); +				/* The start of the currently-scanned literal +				 * in the format string. */ +    tmpObj = Tcl_NewObj();	/* The buffer used to accumulate the literal +				 * being built. */ +    for (bytes = start ; *bytes ; bytes++) { +	if (*bytes == '%') { +	    Tcl_AppendToObj(tmpObj, start, bytes - start); +	    if (*++bytes == '%') { +		Tcl_AppendToObj(tmpObj, "%", 1); +	    } else { +		char *b = TclGetStringFromObj(tmpObj, &len); + +		/* +		 * If there is a non-empty literal from the format string, +		 * push it and reset. +		 */ + +		if (len > 0) { +		    PushLiteral(envPtr, b, len); +		    Tcl_DecrRefCount(tmpObj); +		    tmpObj = Tcl_NewObj(); +		    i++; +		} + +		/* +		 * Push the code to produce the string that would be +		 * substituted with %s, except we'll be concatenating +		 * directly. +		 */ + +		CompileWord(envPtr, tokenPtr, interp, j); +		tokenPtr = TokenAfter(tokenPtr); +		j++; +		i++; +	    } +	    start = bytes + 1; +	} +    } +      /* -     * The while command's result is an empty string. +     * Handle the case of a trailing literal.       */ -    pushResult: -    envPtr->currStackDepth = savedStackDepth; -    TclEmitPush(TclRegisterNewLiteral(envPtr, "", 0), envPtr); -    envPtr->exceptDepth--; +    Tcl_AppendToObj(tmpObj, start, bytes - start); +    bytes = TclGetStringFromObj(tmpObj, &len); +    if (len > 0) { +	PushLiteral(envPtr, bytes, len); +	i++; +    } +    Tcl_DecrRefCount(tmpObj); +    Tcl_DecrRefCount(formatObj); + +    if (i > 1) { +	/* +	 * Do the concatenation, which produces the result. +	 */ + +	TclEmitInstInt1(INST_STR_CONCAT1, i, envPtr); +    }      return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TclLocalScalarFromToken -- + * + *	Get the index into the table of compiled locals that corresponds + *	to a local scalar variable name. + * + * Results: + * 	Returns the non-negative integer index value into the table of + * 	compiled locals corresponding to a local scalar variable name. + * 	If the arguments passed in do not identify a local scalar variable + * 	then return -1. + * + * Side effects: + *	May add an entery into the table of compiled locals. + * + *---------------------------------------------------------------------- + */ -    error: -    envPtr->exceptDepth--; -    return code; +int +TclLocalScalarFromToken( +    Tcl_Token *tokenPtr, +    CompileEnv *envPtr) +{ +    int isScalar, index; + +    TclPushVarName(NULL, tokenPtr, envPtr, TCL_NO_ELEMENT, &index, &isScalar); +    if (!isScalar) { +	index = -1; +    } +    return index; +} + +int +TclLocalScalar( +    const char *bytes, +    int numBytes, +    CompileEnv *envPtr) +{ +    Tcl_Token token[2] =        {{TCL_TOKEN_SIMPLE_WORD, NULL, 0, 1}, +                                 {TCL_TOKEN_TEXT, NULL, 0, 0}}; + +    token[1].start = bytes; +    token[1].size = numBytes; +    return TclLocalScalarFromToken(token, envPtr);  }  /* @@ -3101,50 +3362,52 @@ TclCompileWhileCmd(interp, parsePtr, envPtr)   *   * TclPushVarName --   * - *	Procedure used in the compiling where pushing a variable name - *	is necessary (append, lappend, set). + *	Procedure used in the compiling where pushing a variable name is + *	necessary (append, lappend, set).   *   * Results: - *	The return value is a standard Tcl result, which is normally TCL_OK - *	unless there was an error while parsing string. If an error occurs - *	then the interpreter's result contains a standard error message. + *	The values written to *localIndexPtr and *isScalarPtr signal to + *	the caller what the instructions emitted by this routine will do: + * + *	*isScalarPtr	(*localIndexPtr < 0) + *	1		1	Push the varname on the stack. (Stack +1) + *	1		0	*localIndexPtr is the index of the compiled + *				local for this varname.  No instructions + *				emitted.	(Stack +0) + *	0		1	Push part1 and part2 names of array element + *				on the stack.	(Stack +2) + *	0		0	*localIndexPtr is the index of the compiled + *				local for this array.  Element name is pushed + *				on the stack.	(Stack +1)   *   * Side effects: - *	Instructions are added to envPtr to execute the "set" command - *	at runtime. + *	Instructions are added to envPtr.   *   *----------------------------------------------------------------------   */ -static int -TclPushVarName(interp, varTokenPtr, envPtr, flags, localIndexPtr, -	simpleVarNamePtr, isScalarPtr) -    Tcl_Interp *interp;		/* Used for error reporting. */ -    Tcl_Token *varTokenPtr;	/* Points to a variable token. */ -    CompileEnv *envPtr;		/* Holds resulting instructions. */ -    int flags;			/* takes TCL_CREATE_VAR or -				 * TCL_NO_LARGE_INDEX */ -    int *localIndexPtr;		/* must not be NULL */ -    int *simpleVarNamePtr;	/* must not be NULL */ -    int *isScalarPtr;		/* must not be NULL */ +void +TclPushVarName( +    Tcl_Interp *interp,		/* Used for error reporting. */ +    Tcl_Token *varTokenPtr,	/* Points to a variable token. */ +    CompileEnv *envPtr,		/* Holds resulting instructions. */ +    int flags,			/* TCL_NO_LARGE_INDEX | TCL_NO_ELEMENT. */ +    int *localIndexPtr,		/* Must not be NULL. */ +    int *isScalarPtr)		/* Must not be NULL. */  { -    register CONST char *p; -    CONST char *name, *elName; +    register const char *p; +    const char *name, *elName;      register int i, n; -    int nameChars, elNameChars, simpleVarName, localIndex; -    int code = TCL_OK; -      Tcl_Token *elemTokenPtr = NULL; -    int elemTokenCount = 0; -    int allocedTokens = 0; -    int removedParen = 0; +    int nameChars, elNameChars, simpleVarName, localIndex; +    int elemTokenCount = 0, allocedTokens = 0, removedParen = 0;      /* -     * Decide if we can use a frame slot for the var/array name or if we -     * need to emit code to compute and push the name at runtime. We use a -     * frame slot (entry in the array of local vars) if we are compiling a -     * procedure body and if the name is simple text that does not include -     * namespace qualifiers.  +     * Decide if we can use a frame slot for the var/array name or if we need +     * to emit code to compute and push the name at runtime. We use a frame +     * slot (entry in the array of local vars) if we are compiling a procedure +     * body and if the name is simple text that does not include namespace +     * qualifiers.       */      simpleVarName = 0; @@ -3152,45 +3415,37 @@ TclPushVarName(interp, varTokenPtr, envPtr, flags, localIndexPtr,      nameChars = elNameChars = 0;      localIndex = -1; -    /* -     * Check not only that the type is TCL_TOKEN_SIMPLE_WORD, but whether -     * curly braces surround the variable name. -     * This really matters for array elements to handle things like -     *    set {x($foo)} 5 -     * which raises an undefined var error if we are not careful here. -     */ - -    if ((varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) && -	    (varTokenPtr->start[0] != '{')) { +    if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {  	/*  	 * A simple variable name. Divide it up into "name" and "elName"  	 * strings. If it is not a local variable, look it up at runtime.  	 */ +  	simpleVarName = 1;  	name = varTokenPtr[1].start;  	nameChars = varTokenPtr[1].size; -	if ( *(name + nameChars - 1) == ')') { -	    /*  +	if (name[nameChars-1] == ')') { +	    /*  	     * last char is ')' => potential array reference.  	     */ -	    for (i = 0, p = name;  i < nameChars;  i++, p++) { +	    for (i=0,p=name ; i<nameChars ; i++,p++) {  		if (*p == '(') {  		    elName = p + 1;  		    elNameChars = nameChars - i - 2; -		    nameChars = i ; +		    nameChars = i;  		    break;  		}  	    } -	    if ((elName != NULL) && elNameChars) { +	    if (!(flags & TCL_NO_ELEMENT) && (elName != NULL) && elNameChars) {  		/* -		 * An array element, the element name is a simple -		 * string: assemble the corresponding token. +		 * An array element, the element name is a simple string: +		 * assemble the corresponding token.  		 */ -		elemTokenPtr = (Tcl_Token *) ckalloc(sizeof(Tcl_Token)); +		elemTokenPtr = TclStackAlloc(interp, sizeof(Tcl_Token));  		allocedTokens = 1;  		elemTokenPtr->type = TCL_TOKEN_TEXT;  		elemTokenPtr->start = elName; @@ -3199,72 +3454,73 @@ TclPushVarName(interp, varTokenPtr, envPtr, flags, localIndexPtr,  		elemTokenCount = 1;  	    }  	} -    } else if (((n = varTokenPtr->numComponents) > 1) +    } else if (interp && ((n = varTokenPtr->numComponents) > 1)  	    && (varTokenPtr[1].type == TCL_TOKEN_TEXT) -            && (varTokenPtr[n].type == TCL_TOKEN_TEXT) -            && (varTokenPtr[n].start[varTokenPtr[n].size - 1] == ')')) { - -        /* -	 * Check for parentheses inside first token +	    && (varTokenPtr[n].type == TCL_TOKEN_TEXT) +	    && (varTokenPtr[n].start[varTokenPtr[n].size - 1] == ')')) { +	/* +	 * Check for parentheses inside first token.  	 */ -        simpleVarName = 0; -        for (i = 0, p = varTokenPtr[1].start;  -	     i < varTokenPtr[1].size; i++, p++) { -            if (*p == '(') { -                simpleVarName = 1; -                break; -            } -        } -        if (simpleVarName) { +	simpleVarName = 0; +	for (i = 0, p = varTokenPtr[1].start; +		i < varTokenPtr[1].size; i++, p++) { +	    if (*p == '(') { +		simpleVarName = 1; +		break; +	    } +	} +	if (simpleVarName) {  	    int remainingChars;  	    /* -	     * Check the last token: if it is just ')', do not count -	     * it. Otherwise, remove the ')' and flag so that it is -	     * restored at the end. +	     * Check the last token: if it is just ')', do not count it. +	     * Otherwise, remove the ')' and flag so that it is restored at +	     * the end.  	     */  	    if (varTokenPtr[n].size == 1) { -		--n; +		n--;  	    } else { -		--varTokenPtr[n].size; +		varTokenPtr[n].size--;  		removedParen = n;  	    } -            name = varTokenPtr[1].start; -            nameChars = p - varTokenPtr[1].start; -            elName = p + 1; -            remainingChars = (varTokenPtr[2].start - p) - 1; -            elNameChars = (varTokenPtr[n].start - p) + varTokenPtr[n].size - 2; +	    name = varTokenPtr[1].start; +	    nameChars = p - varTokenPtr[1].start; +	    elName = p + 1; +	    remainingChars = (varTokenPtr[2].start - p) - 1; +	    elNameChars = (varTokenPtr[n].start-p) + varTokenPtr[n].size - 1; -	    if (remainingChars) { +	    if (!(flags & TCL_NO_ELEMENT)) { +	      if (remainingChars) {  		/* -		 * Make a first token with the extra characters in the first  +		 * Make a first token with the extra characters in the first  		 * token.  		 */ -		elemTokenPtr = (Tcl_Token *) ckalloc(n * sizeof(Tcl_Token)); +		elemTokenPtr = TclStackAlloc(interp, n * sizeof(Tcl_Token));  		allocedTokens = 1;  		elemTokenPtr->type = TCL_TOKEN_TEXT;  		elemTokenPtr->start = elName;  		elemTokenPtr->size = remainingChars;  		elemTokenPtr->numComponents = 0;  		elemTokenCount = n; -		 +  		/*  		 * Copy the remaining tokens.  		 */ -		 -		memcpy((void *) (elemTokenPtr+1), (void *) (&varTokenPtr[2]), -		       ((n-1) * sizeof(Tcl_Token))); -	    } else { + +		memcpy(elemTokenPtr+1, varTokenPtr+2, +			(n-1) * sizeof(Tcl_Token)); +	      } else {  		/*  		 * Use the already available tokens.  		 */ -		 +  		elemTokenPtr = &varTokenPtr[2]; -		elemTokenCount = n - 1;	     +		elemTokenCount = n - 1; +	      }  	    }  	}      } @@ -3275,6 +3531,7 @@ TclPushVarName(interp, varTokenPtr, envPtr, flags, localIndexPtr,  	 */  	int hasNsQualifiers = 0; +  	for (i = 0, p = name;  i < nameChars;  i++, p++) {  	    if ((*p == ':') && ((i+1) < nameChars) && (*(p+1) == ':')) {  		hasNsQualifiers = 1; @@ -3283,61 +3540,60 @@ TclPushVarName(interp, varTokenPtr, envPtr, flags, localIndexPtr,  	}  	/* -	 * Look up the var name's index in the array of local vars in the -	 * proc frame. If retrieving the var's value and it doesn't already -	 * exist, push its name and look it up at runtime. +	 * Look up the var name's index in the array of local vars in the proc +	 * frame. If retrieving the var's value and it doesn't already exist, +	 * push its name and look it up at runtime.  	 */ -	if ((envPtr->procPtr != NULL) && !hasNsQualifiers) { -	    localIndex = TclFindCompiledLocal(name, nameChars, -		    /*create*/ (flags & TCL_CREATE_VAR), -                    /*flags*/ ((elName==NULL)? VAR_SCALAR : VAR_ARRAY), -		    envPtr->procPtr); +	if (!hasNsQualifiers) { +	    localIndex = TclFindCompiledLocal(name, nameChars, 1, envPtr);  	    if ((flags & TCL_NO_LARGE_INDEX) && (localIndex > 255)) { -		/* we'll push the name */ +		/* +		 * We'll push the name. +		 */ +  		localIndex = -1;  	    }  	} -	if (localIndex < 0) { -	    TclEmitPush(TclRegisterNewLiteral(envPtr, name, nameChars), envPtr); +	if (interp && localIndex < 0) { +	    PushLiteral(envPtr, name, nameChars);  	}  	/* -	 * Compile the element script, if any. +	 * Compile the element script, if any, and only if not inhibited. [Bug +	 * 3600328]  	 */ -	if (elName != NULL) { +	if (elName != NULL && !(flags & TCL_NO_ELEMENT)) {  	    if (elNameChars) { -		code = TclCompileTokens(interp, elemTokenPtr, -                        elemTokenCount, envPtr); -		if (code != TCL_OK) { -		    goto done; -		} +		TclCompileTokens(interp, elemTokenPtr, elemTokenCount, +			envPtr);  	    } else { -		TclEmitPush(TclRegisterNewLiteral(envPtr, "", 0), envPtr); +		PushStringLiteral(envPtr, "");  	    }  	} -    } else { +    } else if (interp) {  	/*  	 * The var name isn't simple: compile and push it.  	 */ -	code = TclCompileTokens(interp, varTokenPtr+1, -		varTokenPtr->numComponents, envPtr); -	if (code != TCL_OK) { -	    goto done; -	} +	CompileTokens(envPtr, varTokenPtr, interp);      } -    done:      if (removedParen) { -	++varTokenPtr[removedParen].size; +	varTokenPtr[removedParen].size++;      }      if (allocedTokens) { -        ckfree((char *) elemTokenPtr); +	TclStackFree(interp, elemTokenPtr);      } -    *localIndexPtr	= localIndex; -    *simpleVarNamePtr	= simpleVarName; -    *isScalarPtr	= (elName == NULL); -    return code; +    *localIndexPtr = localIndex; +    *isScalarPtr = (elName == NULL);  } + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ | 
