diff options
Diffstat (limited to 'generic/tclExecute.c')
| -rw-r--r-- | generic/tclExecute.c | 13745 | 
1 files changed, 9330 insertions, 4415 deletions
| diff --git a/generic/tclExecute.c b/generic/tclExecute.c index bf98c8d..4ecca5b 100644 --- a/generic/tclExecute.c +++ b/generic/tclExecute.c @@ -1,47 +1,50 @@ -/*  +/*   * tclExecute.c --   * - *	This file contains procedures that execute byte-compiled Tcl - *	commands. + *	This file contains procedures that execute byte-compiled Tcl commands.   *   * Copyright (c) 1996-1997 Sun Microsystems, Inc.   * Copyright (c) 1998-2000 by Scriptics Corporation. - * - * See the file "license.terms" for information on usage and redistribution - * of this file, and for a DISCLAIMER OF ALL WARRANTIES. - * - * RCS: @(#) $Id: tclExecute.c,v 1.26 2001/07/03 23:39:10 hobbs Exp $ + * Copyright (c) 2001 by Kevin B. Kenny. All rights reserved. + * Copyright (c) 2002-2010 by Miguel Sofer. + * Copyright (c) 2005-2007 by Donal K. Fellows. + * Copyright (c) 2007 Daniel A. Steffen <das@users.sourceforge.net> + * Copyright (c) 2006-2008 by Joe Mistachkin.  All rights reserved. + * + * 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 "tclOOInt.h" +#include "tommath.h" +#include <math.h> -#ifdef NO_FLOAT_H -#   include "../compat/float.h" -#else -#   include <float.h> -#endif -#ifndef TCL_NO_MATH -#include "tclMath.h" +#if NRE_ENABLE_ASSERTS +#include <assert.h>  #endif  /* - * The stuff below is a bit of a hack so that this file can be used - * in environments that include no UNIX, i.e. no errno.  Just define - * errno here. + * Hack to determine whether we may expect IEEE floating point. The hack is + * formally incorrect in that non-IEEE platforms might have the same precision + * and range, but VAX, IBM, and Cray do not; are there any other floating + * point units that we might care about?   */ -#ifndef TCL_GENERIC_ONLY -#include "tclPort.h" -#else -#define NO_ERRNO_H +#if (FLT_RADIX == 2) && (DBL_MANT_DIG == 53) && (DBL_MAX_EXP == 1024) +#define IEEE_FLOATING_POINT  #endif -#ifdef NO_ERRNO_H -int errno; -#define EDOM 33 -#define ERANGE 34 -#endif +/* + * A mask (should be 2**n-1) that is used to work out when the bytecode engine + * should call Tcl_AsyncReady() to see whether there is a signal that needs + * handling. + */ + +#ifndef ASYNC_CHECK_COUNT_MASK +#   define ASYNC_CHECK_COUNT_MASK	63 +#endif /* !ASYNC_CHECK_COUNT_MASK */  /*   * Boolean flag indicating whether the Tcl bytecode interpreter has been @@ -51,6 +54,9 @@ int errno;  static int execInitialized = 0;  TCL_DECLARE_MUTEX(execMutex) +static int cachedInExit = 0; + +#ifdef TCL_COMPILE_DEBUG  /*   * Variable that controls whether execution tracing is enabled and, if so,   * what level of tracing is desired: @@ -62,54 +68,31 @@ TCL_DECLARE_MUTEX(execMutex)   */  int tclTraceExec = 0; - -typedef struct ThreadSpecificData { -    /* -     * The following global variable is use to signal matherr that Tcl -     * is responsible for the arithmetic, so errors can be handled in a -     * fashion appropriate for Tcl.  Zero means no Tcl math is in -     * progress;  non-zero means Tcl is doing math. -     */ -     -    int mathInProgress; - -} ThreadSpecificData; - -static Tcl_ThreadDataKey dataKey; - -/* - * The variable below serves no useful purpose except to generate - * a reference to matherr, so that the Tcl version of matherr is - * linked in rather than the system version. Without this reference - * the need for matherr won't be discovered during linking until after - * libtcl.a has been processed, so Tcl's version won't be used. - */ - -#ifdef NEED_MATHERR -extern int matherr(); -int (*tclMatherrPtr)() = matherr;  #endif  /*   * Mapping from expression instruction opcodes to strings; used for error   * messages. Note that these entries must match the order and number of the   * expression opcodes (e.g., INST_LOR) in tclCompile.h. + * + * Does not include the string for INST_EXPON (and beyond), as that is + * disjoint for backward-compatability reasons.   */ -static char *operatorStrings[] = { +static const char *const operatorStrings[] = {      "||", "&&", "|", "^", "&", "==", "!=", "<", ">", "<=", ">=", "<<", ">>",      "+", "-", "*", "/", "%", "+", "-", "~", "!",      "BUILTIN FUNCTION", "FUNCTION", -    "", "", "", "", "", "", "", "", "eq", "ne", +    "", "", "", "", "", "", "", "", "eq", "ne"  };  /*   * Mapping from Tcl result codes to strings; used for error and debugging - * messages.  + * messages.   */  #ifdef TCL_COMPILE_DEBUG -static char *resultStrings[] = { +static const char *const resultStrings[] = {      "TCL_OK", "TCL_ERROR", "TCL_RETURN", "TCL_BREAK", "TCL_CONTINUE"  };  #endif @@ -120,218 +103,773 @@ static char *resultStrings[] = {  #ifdef TCL_COMPILE_STATS  long		tclObjsAlloced = 0; -long		tclObjsFreed   = 0; -#define TCL_MAX_SHARED_OBJ_STATS 5 +long		tclObjsFreed = 0;  long		tclObjsShared[TCL_MAX_SHARED_OBJ_STATS] = { 0, 0, 0, 0, 0 };  #endif /* TCL_COMPILE_STATS */  /* - * Macros for testing floating-point values for certain special cases. Test - * for not-a-number by comparing a value against itself; test for infinity - * by comparing against the largest floating-point value. + * Support pre-8.5 bytecodes unless specifically requested otherwise.   */ -#define IS_NAN(v) ((v) != (v)) -#ifdef DBL_MAX -#   define IS_INF(v) (((v) > DBL_MAX) || ((v) < -DBL_MAX)) -#else -#   define IS_INF(v) 0 +#ifndef TCL_SUPPORT_84_BYTECODE +#define TCL_SUPPORT_84_BYTECODE 1  #endif +#if TCL_SUPPORT_84_BYTECODE  /* - * Macro to adjust the program counter and restart the instruction execution - * loop after each instruction is executed. + * We need to know the tclBuiltinFuncTable to support translation of pre-8.5 + * math functions to the namespace-based ::tcl::mathfunc::op in 8.5+.   */ -#define ADJUST_PC(instBytes) \ -    pc += (instBytes); \ -    continue +typedef struct { +    const char *name;		/* Name of function. */ +    int numArgs;		/* Number of arguments for function. */ +} BuiltinFunc; + +/* + * Table describing the built-in math functions. Entries in this table are + * indexed by the values of the INST_CALL_BUILTIN_FUNC instruction's + * operand byte. + */ + +static BuiltinFunc const tclBuiltinFuncTable[] = { +    {"acos", 1}, +    {"asin", 1}, +    {"atan", 1}, +    {"atan2", 2}, +    {"ceil", 1}, +    {"cos", 1}, +    {"cosh", 1}, +    {"exp", 1}, +    {"floor", 1}, +    {"fmod", 2}, +    {"hypot", 2}, +    {"log", 1}, +    {"log10", 1}, +    {"pow", 2}, +    {"sin", 1}, +    {"sinh", 1}, +    {"sqrt", 1}, +    {"tan", 1}, +    {"tanh", 1}, +    {"abs", 1}, +    {"double", 1}, +    {"int", 1}, +    {"rand", 0}, +    {"round", 1}, +    {"srand", 1}, +    {"wide", 1}, +    {NULL, 0}, +}; + +#define LAST_BUILTIN_FUNC	25 +#endif + +/* + * NR_TEBC + * Helpers for NR - non-recursive calls to TEBC + * Minimal data required to fully reconstruct the execution state. + */ + +typedef struct TEBCdata { +    ByteCode *codePtr;		/* Constant until the BC returns */ +				/* -----------------------------------------*/ +    ptrdiff_t *catchTop;	/* These fields are used on return TO this */ +    Tcl_Obj *auxObjList;	/* this level: they record the state when a */ +    CmdFrame cmdFrame;		/* new codePtr was received for NR */ +                                /* execution. */ +    void *stack[1];		/* Start of the actual combined catch and obj +				 * stacks; the struct will be expanded as +				 * necessary */ +} TEBCdata; + +#define TEBC_YIELD() \ +    do {						\ +	esPtr->tosPtr = tosPtr;				\ +	TclNRAddCallback(interp, TEBCresume,		\ +		TD, pc, INT2PTR(cleanup), NULL);	\ +    } while (0) + +#define TEBC_DATA_DIG() \ +    do {					\ +	tosPtr = esPtr->tosPtr;			\ +    } while (0) + +#define PUSH_TAUX_OBJ(objPtr) \ +    do {							\ +	if (auxObjList) {					\ +	    objPtr->length += auxObjList->length;		\ +	}							\ +	objPtr->internalRep.ptrAndLongRep.ptr = auxObjList;	\ +	auxObjList = objPtr;					\ +    } while (0) + +#define POP_TAUX_OBJ() \ +    do {							\ +	tmpPtr = auxObjList;					\ +	auxObjList = tmpPtr->internalRep.ptrAndLongRep.ptr;	\ +	Tcl_DecrRefCount(tmpPtr);				\ +    } while (0) + +/* + * These variable-access macros have to coincide with those in tclVar.c + */ + +#define VarHashGetValue(hPtr) \ +    ((Var *) ((char *)hPtr - TclOffset(VarInHash, entry))) + +static inline Var * +VarHashCreateVar( +    TclVarHashTable *tablePtr, +    Tcl_Obj *key, +    int *newPtr) +{ +    Tcl_HashEntry *hPtr = Tcl_CreateHashEntry(&tablePtr->table, +	    key, newPtr); + +    if (!hPtr) { +	return NULL; +    } +    return VarHashGetValue(hPtr); +} + +#define VarHashFindVar(tablePtr, key) \ +    VarHashCreateVar((tablePtr), (key), NULL) + +/* + * The new macro for ending an instruction; note that a reasonable C-optimiser + * will resolve all branches at compile time. (result) is always a constant; + * the macro NEXT_INST_F handles constant (nCleanup), NEXT_INST_V is resolved + * at runtime for variable (nCleanup). + * + * ARGUMENTS: + *    pcAdjustment: how much to increment pc + *    nCleanup: how many objects to remove from the stack + *    resultHandling: 0 indicates no object should be pushed on the stack; + *	otherwise, push objResultPtr. If (result < 0), objResultPtr already + *	has the correct reference count. + * + * We use the new compile-time assertions to check that nCleanup is constant + * and within range. + */ + +/* Verify the stack depth, only when no expansion is in progress */ + +#ifdef TCL_COMPILE_DEBUG +#define CHECK_STACK()							\ +    do {								\ +	ValidatePcAndStackTop(codePtr, pc, CURR_DEPTH,			\ +		/*checkStack*/ !(starting || auxObjList));		\ +	starting = 0;							\ +    } while (0) +#else +#define CHECK_STACK() +#endif + +#define NEXT_INST_F(pcAdjustment, nCleanup, resultHandling)	\ +    do {							\ +	TCL_CT_ASSERT((nCleanup >= 0) && (nCleanup <= 2));	\ +	CHECK_STACK();						\ +	if (nCleanup == 0) {					\ +	    if (resultHandling != 0) {				\ +		if ((resultHandling) > 0) {			\ +		    PUSH_OBJECT(objResultPtr);			\ +		} else {					\ +		    *(++tosPtr) = objResultPtr;			\ +		}						\ +	    }							\ +	    pc += (pcAdjustment);				\ +	    goto cleanup0;					\ +	} else if (resultHandling != 0) {			\ +	    if ((resultHandling) > 0) {				\ +		Tcl_IncrRefCount(objResultPtr);			\ +	    }							\ +	    pc += (pcAdjustment);				\ +	    switch (nCleanup) {					\ +	    case 1: goto cleanup1_pushObjResultPtr;		\ +	    case 2: goto cleanup2_pushObjResultPtr;		\ +	    case 0: break;					\ +	    }							\ +	} else {						\ +	    pc += (pcAdjustment);				\ +	    switch (nCleanup) {					\ +	    case 1: goto cleanup1;				\ +	    case 2: goto cleanup2;				\ +	    case 0: break;					\ +	    }							\ +	}							\ +    } while (0) + +#define NEXT_INST_V(pcAdjustment, nCleanup, resultHandling)	\ +    CHECK_STACK();						\ +    do {							\ +	pc += (pcAdjustment);					\ +	cleanup = (nCleanup);					\ +	if (resultHandling) {					\ +	    if ((resultHandling) > 0) {				\ +		Tcl_IncrRefCount(objResultPtr);			\ +	    }							\ +	    goto cleanupV_pushObjResultPtr;			\ +	} else {						\ +	    goto cleanupV;					\ +	}							\ +    } while (0) + +#ifndef TCL_COMPILE_DEBUG +#define JUMP_PEEPHOLE_F(condition, pcAdjustment, cleanup) \ +    do {								\ +	pc += (pcAdjustment);						\ +	switch (*pc) {							\ +	case INST_JUMP_FALSE1:						\ +	    NEXT_INST_F(((condition)? 2 : TclGetInt1AtPtr(pc+1)), (cleanup), 0); \ +	case INST_JUMP_TRUE1:						\ +	    NEXT_INST_F(((condition)? TclGetInt1AtPtr(pc+1) : 2), (cleanup), 0); \ +	case INST_JUMP_FALSE4:						\ +	    NEXT_INST_F(((condition)? 5 : TclGetInt4AtPtr(pc+1)), (cleanup), 0); \ +	case INST_JUMP_TRUE4:						\ +	    NEXT_INST_F(((condition)? TclGetInt4AtPtr(pc+1) : 5), (cleanup), 0); \ +	default:							\ +	    if ((condition) < 0) {					\ +		TclNewIntObj(objResultPtr, -1);				\ +	    } else {							\ +		objResultPtr = TCONST((condition) > 0);			\ +	    }								\ +	    NEXT_INST_F(0, (cleanup), 1);				\ +	}								\ +    } while (0) +#define JUMP_PEEPHOLE_V(condition, pcAdjustment, cleanup) \ +    do {								\ +	pc += (pcAdjustment);						\ +	switch (*pc) {							\ +	case INST_JUMP_FALSE1:						\ +	    NEXT_INST_V(((condition)? 2 : TclGetInt1AtPtr(pc+1)), (cleanup), 0); \ +	case INST_JUMP_TRUE1:						\ +	    NEXT_INST_V(((condition)? TclGetInt1AtPtr(pc+1) : 2), (cleanup), 0); \ +	case INST_JUMP_FALSE4:						\ +	    NEXT_INST_V(((condition)? 5 : TclGetInt4AtPtr(pc+1)), (cleanup), 0); \ +	case INST_JUMP_TRUE4:						\ +	    NEXT_INST_V(((condition)? TclGetInt4AtPtr(pc+1) : 5), (cleanup), 0); \ +	default:							\ +	    if ((condition) < 0) {					\ +		TclNewIntObj(objResultPtr, -1);				\ +	    } else {							\ +		objResultPtr = TCONST((condition) > 0);			\ +	    }								\ +	    NEXT_INST_V(0, (cleanup), 1);				\ +	}								\ +    } while (0) +#else /* TCL_COMPILE_DEBUG */ +#define JUMP_PEEPHOLE_F(condition, pcAdjustment, cleanup) \ +    do{									\ +	if ((condition) < 0) {						\ +	    TclNewIntObj(objResultPtr, -1);				\ +	} else {							\ +	    objResultPtr = TCONST((condition) > 0);			\ +	}								\ +	NEXT_INST_F((pcAdjustment), (cleanup), 1);			\ +    } while (0) +#define JUMP_PEEPHOLE_V(condition, pcAdjustment, cleanup) \ +    do{									\ +	if ((condition) < 0) {						\ +	    TclNewIntObj(objResultPtr, -1);				\ +	} else {							\ +	    objResultPtr = TCONST((condition) > 0);			\ +	}								\ +	NEXT_INST_V((pcAdjustment), (cleanup), 1);			\ +    } while (0) +#endif  /*   * Macros used to cache often-referenced Tcl evaluation stack information   * in local variables. Note that a DECACHE_STACK_INFO()-CACHE_STACK_INFO() - * pair must surround any call inside TclExecuteByteCode (and a few other + * pair must surround any call inside TclNRExecuteByteCode (and a few other   * procedures that use this scheme) that could result in a recursive call - * to TclExecuteByteCode. + * to TclNRExecuteByteCode.   */  #define CACHE_STACK_INFO() \ -    stackPtr = eePtr->stackPtr; \ -    stackTop = eePtr->stackTop +    checkInterp = 1  #define DECACHE_STACK_INFO() \ -    eePtr->stackTop = stackTop +    esPtr->tosPtr = tosPtr  /*   * Macros used to access items on the Tcl evaluation stack. PUSH_OBJECT   * increments the object's ref count since it makes the stack have another   * reference pointing to the object. However, POP_OBJECT does not decrement - * the ref count. This is because the stack may hold the only reference to - * the object, so the object would be destroyed if its ref count were - * decremented before the caller had a chance to, e.g., store it in a - * variable. It is the caller's responsibility to decrement the ref count - * when it is finished with an object. + * the ref count. This is because the stack may hold the only reference to the + * object, so the object would be destroyed if its ref count were decremented + * before the caller had a chance to, e.g., store it in a variable. It is the + * caller's responsibility to decrement the ref count when it is finished with + * an object.   *   * WARNING! It is essential that objPtr only appear once in the PUSH_OBJECT - * macro. The actual parameter might be an expression with side effects, - * and this ensures that it will be executed only once.  + * macro. The actual parameter might be an expression with side effects, and + * this ensures that it will be executed only once.   */ -     +  #define PUSH_OBJECT(objPtr) \ -    Tcl_IncrRefCount(stackPtr[++stackTop] = (objPtr)) -     -#define POP_OBJECT() \ -    (stackPtr[stackTop--]) +    Tcl_IncrRefCount(*(++tosPtr) = (objPtr)) + +#define POP_OBJECT()	*(tosPtr--) + +#define OBJ_AT_TOS	*tosPtr + +#define OBJ_UNDER_TOS	*(tosPtr-1) + +#define OBJ_AT_DEPTH(n)	*(tosPtr-(n)) + +#define CURR_DEPTH	((ptrdiff_t) (tosPtr - initTosPtr)) + +#define STACK_BASE(esPtr) ((esPtr)->stackWords - 1)  /*   * Macros used to trace instruction execution. The macros TRACE, - * TRACE_WITH_OBJ, and O2S are only used inside TclExecuteByteCode. - * O2S is only used in TRACE* calls to get a string from an object. + * TRACE_WITH_OBJ, and O2S are only used inside TclNRExecuteByteCode. O2S is + * only used in TRACE* calls to get a string from an object.   */  #ifdef TCL_COMPILE_DEBUG -#define TRACE(a) \ -    if (traceInstructions) { \ -        fprintf(stdout, "%2d: %2d (%u) %s ", iPtr->numLevels, stackTop, \ -	       (unsigned int)(pc - codePtr->codeStart), \ -	       GetOpcodeName(pc)); \ -	printf a; \ -    } -#define TRACE_WITH_OBJ(a, objPtr) \ -    if (traceInstructions) { \ -        fprintf(stdout, "%2d: %2d (%u) %s ", iPtr->numLevels, stackTop, \ -	       (unsigned int)(pc - codePtr->codeStart), \ -	       GetOpcodeName(pc)); \ -	printf a; \ -        TclPrintObject(stdout, objPtr, 30); \ -        fprintf(stdout, "\n"); \ -    } -#define O2S(objPtr) \ -    (objPtr ? Tcl_GetString(objPtr) : "") -#else -#define TRACE(a) -#define TRACE_WITH_OBJ(a, objPtr) -#define O2S(objPtr) +#   define TRACE(a) \ +    while (traceInstructions) {					\ +	fprintf(stdout, "%2d: %2d (%u) %s ", iPtr->numLevels,	\ +		(int) CURR_DEPTH,				\ +		(unsigned) (pc - codePtr->codeStart),		\ +		GetOpcodeName(pc));				\ +	printf a;						\ +	break;							\ +    } +#   define TRACE_APPEND(a) \ +    while (traceInstructions) {		\ +	printf a;			\ +	break;				\ +    } +#   define TRACE_ERROR(interp) \ +    TRACE_APPEND(("ERROR: %.30s\n", O2S(Tcl_GetObjResult(interp)))); +#   define TRACE_WITH_OBJ(a, objPtr) \ +    while (traceInstructions) {					\ +	fprintf(stdout, "%2d: %2d (%u) %s ", iPtr->numLevels,	\ +		(int) CURR_DEPTH,				\ +		(unsigned) (pc - codePtr->codeStart),		\ +		GetOpcodeName(pc));				\ +	printf a;						\ +	TclPrintObject(stdout, objPtr, 30);			\ +	fprintf(stdout, "\n");					\ +	break;							\ +    } +#   define O2S(objPtr) \ +    (objPtr ? TclGetString(objPtr) : "") +#else /* !TCL_COMPILE_DEBUG */ +#   define TRACE(a) +#   define TRACE_APPEND(a) +#   define TRACE_ERROR(interp) +#   define TRACE_WITH_OBJ(a, objPtr) +#   define O2S(objPtr)  #endif /* TCL_COMPILE_DEBUG */  /* + * DTrace instruction probe macros. + */ + +#define TCL_DTRACE_INST_NEXT() \ +    do {								\ +	if (TCL_DTRACE_INST_DONE_ENABLED()) {				\ +	    if (curInstName) {						\ +		TCL_DTRACE_INST_DONE(curInstName, (int) CURR_DEPTH,	\ +			tosPtr);					\ +	    }								\ +	    curInstName = tclInstructionTable[*pc].name;		\ +	    if (TCL_DTRACE_INST_START_ENABLED()) {			\ +		TCL_DTRACE_INST_START(curInstName, (int) CURR_DEPTH,	\ +			tosPtr);					\ +	    }								\ +	} else if (TCL_DTRACE_INST_START_ENABLED()) {			\ +	    TCL_DTRACE_INST_START(tclInstructionTable[*pc].name,	\ +			(int) CURR_DEPTH, tosPtr);			\ +	}								\ +    } while (0) +#define TCL_DTRACE_INST_LAST() \ +    do {								\ +	if (TCL_DTRACE_INST_DONE_ENABLED() && curInstName) {		\ +	    TCL_DTRACE_INST_DONE(curInstName, (int) CURR_DEPTH, tosPtr);\ +	}								\ +    } while (0) + +/* + * Macro used in this file to save a function call for common uses of + * TclGetNumberFromObj(). The ANSI C "prototype" is: + * + * MODULE_SCOPE int GetNumberFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, + *			ClientData *ptrPtr, int *tPtr); + */ + +#ifdef TCL_WIDE_INT_IS_LONG +#define GetNumberFromObj(interp, objPtr, ptrPtr, tPtr) \ +    (((objPtr)->typePtr == &tclIntType)					\ +	?	(*(tPtr) = TCL_NUMBER_LONG,				\ +		*(ptrPtr) = (ClientData)				\ +		    (&((objPtr)->internalRep.longValue)), TCL_OK) :	\ +    ((objPtr)->typePtr == &tclDoubleType)				\ +	?	(((TclIsNaN((objPtr)->internalRep.doubleValue))		\ +		    ?	(*(tPtr) = TCL_NUMBER_NAN)			\ +		    :	(*(tPtr) = TCL_NUMBER_DOUBLE)),			\ +		*(ptrPtr) = (ClientData)				\ +		    (&((objPtr)->internalRep.doubleValue)), TCL_OK) :	\ +    ((((objPtr)->typePtr == NULL) && ((objPtr)->bytes == NULL)) ||	\ +    (((objPtr)->bytes != NULL) && ((objPtr)->length == 0)))		\ +	? (*(tPtr) = TCL_NUMBER_LONG),TCL_ERROR :			\ +    TclGetNumberFromObj((interp), (objPtr), (ptrPtr), (tPtr))) +#else /* !TCL_WIDE_INT_IS_LONG */ +#define GetNumberFromObj(interp, objPtr, ptrPtr, tPtr) \ +    (((objPtr)->typePtr == &tclIntType)					\ +	?	(*(tPtr) = TCL_NUMBER_LONG,				\ +		*(ptrPtr) = (ClientData)				\ +		    (&((objPtr)->internalRep.longValue)), TCL_OK) :	\ +    ((objPtr)->typePtr == &tclWideIntType)				\ +	?	(*(tPtr) = TCL_NUMBER_WIDE,				\ +		*(ptrPtr) = (ClientData)				\ +		    (&((objPtr)->internalRep.wideValue)), TCL_OK) :	\ +    ((objPtr)->typePtr == &tclDoubleType)				\ +	?	(((TclIsNaN((objPtr)->internalRep.doubleValue))		\ +		    ?	(*(tPtr) = TCL_NUMBER_NAN)			\ +		    :	(*(tPtr) = TCL_NUMBER_DOUBLE)),			\ +		*(ptrPtr) = (ClientData)				\ +		    (&((objPtr)->internalRep.doubleValue)), TCL_OK) :	\ +    ((((objPtr)->typePtr == NULL) && ((objPtr)->bytes == NULL)) ||	\ +    (((objPtr)->bytes != NULL) && ((objPtr)->length == 0)))		\ +	? (*(tPtr) = TCL_NUMBER_LONG),TCL_ERROR :			\ +    TclGetNumberFromObj((interp), (objPtr), (ptrPtr), (tPtr))) +#endif /* TCL_WIDE_INT_IS_LONG */ + +/* + * Macro used in this file to save a function call for common uses of + * Tcl_GetBooleanFromObj(). The ANSI C "prototype" is: + * + * MODULE_SCOPE int TclGetBooleanFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, + *			int *boolPtr); + */ + +#define TclGetBooleanFromObj(interp, objPtr, boolPtr) \ +    ((((objPtr)->typePtr == &tclIntType)				\ +	|| ((objPtr)->typePtr == &tclBooleanType))			\ +	? (*(boolPtr) = ((objPtr)->internalRep.longValue!=0), TCL_OK)	\ +	: Tcl_GetBooleanFromObj((interp), (objPtr), (boolPtr))) + +/* + * Macro used in this file to save a function call for common uses of + * Tcl_GetWideIntFromObj(). The ANSI C "prototype" is: + * + * MODULE_SCOPE int TclGetWideIntFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, + *			Tcl_WideInt *wideIntPtr); + */ + +#ifdef TCL_WIDE_INT_IS_LONG +#define TclGetWideIntFromObj(interp, objPtr, wideIntPtr) \ +    (((objPtr)->typePtr == &tclIntType)					\ +	? (*(wideIntPtr) = (Tcl_WideInt)				\ +		((objPtr)->internalRep.longValue), TCL_OK) :		\ +	Tcl_GetWideIntFromObj((interp), (objPtr), (wideIntPtr))) +#else /* !TCL_WIDE_INT_IS_LONG */ +#define TclGetWideIntFromObj(interp, objPtr, wideIntPtr)		\ +    (((objPtr)->typePtr == &tclWideIntType)				\ +	? (*(wideIntPtr) = (objPtr)->internalRep.wideValue, TCL_OK) :	\ +    ((objPtr)->typePtr == &tclIntType)					\ +	? (*(wideIntPtr) = (Tcl_WideInt)				\ +		((objPtr)->internalRep.longValue), TCL_OK) :		\ +	Tcl_GetWideIntFromObj((interp), (objPtr), (wideIntPtr))) +#endif /* TCL_WIDE_INT_IS_LONG */ + +/* + * Macro used to make the check for type overflow more mnemonic. This works by + * comparing sign bits; the rest of the word is irrelevant. The ANSI C + * "prototype" (where inttype_t is any integer type) is: + * + * MODULE_SCOPE int Overflowing(inttype_t a, inttype_t b, inttype_t sum); + * + * Check first the condition most likely to fail in usual code (at least for + * usage in [incr]: do the first summand and the sum have != signs? + */ + +#define Overflowing(a,b,sum) ((((a)^(sum)) < 0) && (((a)^(b)) >= 0)) + +/* + * Macro for checking whether the type is NaN, used when we're thinking about + * throwing an error for supplying a non-number number. + */ + +#ifndef ACCEPT_NAN +#define IsErroringNaNType(type)		((type) == TCL_NUMBER_NAN) +#else +#define IsErroringNaNType(type)		0 +#endif + +/* + * Auxiliary tables used to compute powers of small integers. + */ + +#if (LONG_MAX == 0x7fffffff) + +/* + * Maximum base that, when raised to powers 2, 3, ... 8, fits in a 32-bit + * signed integer. + */ + +static const long MaxBase32[] = {46340, 1290, 215, 73, 35, 21, 14}; +static const size_t MaxBase32Size = sizeof(MaxBase32)/sizeof(long); + +/* + * Table giving 3, 4, ..., 11, raised to the powers 9, 10, ..., as far as they + * fit in a 32-bit signed integer. Exp32Index[i] gives the starting index of + * powers of i+3; Exp32Value[i] gives the corresponding powers. + */ + +static const unsigned short Exp32Index[] = { +    0, 11, 18, 23, 26, 29, 31, 32, 33 +}; +static const size_t Exp32IndexSize = +    sizeof(Exp32Index) / sizeof(unsigned short); +static const long Exp32Value[] = { +    19683, 59049, 177147, 531441, 1594323, 4782969, 14348907, 43046721, +    129140163, 387420489, 1162261467, 262144, 1048576, 4194304, +    16777216, 67108864, 268435456, 1073741824, 1953125, 9765625, +    48828125, 244140625, 1220703125, 10077696, 60466176, 362797056, +    40353607, 282475249, 1977326743, 134217728, 1073741824, 387420489, +    1000000000 +}; +static const size_t Exp32ValueSize = sizeof(Exp32Value)/sizeof(long); +#endif /* LONG_MAX == 0x7fffffff -- 32 bit machine */ + +#if (LONG_MAX > 0x7fffffff) || !defined(TCL_WIDE_INT_IS_LONG) + +/* + * Maximum base that, when raised to powers 2, 3, ..., 16, fits in a + * Tcl_WideInt. + */ + +static const Tcl_WideInt MaxBase64[] = { +    (Tcl_WideInt)46340*65536+62259,	/* 3037000499 == isqrt(2**63-1) */ +    (Tcl_WideInt)2097151, (Tcl_WideInt)55108, (Tcl_WideInt)6208, +    (Tcl_WideInt)1448, (Tcl_WideInt)511, (Tcl_WideInt)234, (Tcl_WideInt)127, +    (Tcl_WideInt)78, (Tcl_WideInt)52, (Tcl_WideInt)38, (Tcl_WideInt)28, +    (Tcl_WideInt)22, (Tcl_WideInt)18, (Tcl_WideInt)15 +}; +static const size_t MaxBase64Size = sizeof(MaxBase64)/sizeof(Tcl_WideInt); + +/* + * Table giving 3, 4, ..., 13 raised to powers greater than 16 when the + * results fit in a 64-bit signed integer. + */ + +static const unsigned short Exp64Index[] = { +    0, 23, 38, 49, 57, 63, 67, 70, 72, 74, 75, 76 +}; +static const size_t Exp64IndexSize = +    sizeof(Exp64Index) / sizeof(unsigned short); +static const Tcl_WideInt Exp64Value[] = { +    (Tcl_WideInt)243*243*243*3*3, +    (Tcl_WideInt)243*243*243*3*3*3, +    (Tcl_WideInt)243*243*243*3*3*3*3, +    (Tcl_WideInt)243*243*243*243, +    (Tcl_WideInt)243*243*243*243*3, +    (Tcl_WideInt)243*243*243*243*3*3, +    (Tcl_WideInt)243*243*243*243*3*3*3, +    (Tcl_WideInt)243*243*243*243*3*3*3*3, +    (Tcl_WideInt)243*243*243*243*243, +    (Tcl_WideInt)243*243*243*243*243*3, +    (Tcl_WideInt)243*243*243*243*243*3*3, +    (Tcl_WideInt)243*243*243*243*243*3*3*3, +    (Tcl_WideInt)243*243*243*243*243*3*3*3*3, +    (Tcl_WideInt)243*243*243*243*243*243, +    (Tcl_WideInt)243*243*243*243*243*243*3, +    (Tcl_WideInt)243*243*243*243*243*243*3*3, +    (Tcl_WideInt)243*243*243*243*243*243*3*3*3, +    (Tcl_WideInt)243*243*243*243*243*243*3*3*3*3, +    (Tcl_WideInt)243*243*243*243*243*243*243, +    (Tcl_WideInt)243*243*243*243*243*243*243*3, +    (Tcl_WideInt)243*243*243*243*243*243*243*3*3, +    (Tcl_WideInt)243*243*243*243*243*243*243*3*3*3, +    (Tcl_WideInt)243*243*243*243*243*243*243*3*3*3*3, +    (Tcl_WideInt)1024*1024*1024*4*4, +    (Tcl_WideInt)1024*1024*1024*4*4*4, +    (Tcl_WideInt)1024*1024*1024*4*4*4*4, +    (Tcl_WideInt)1024*1024*1024*1024, +    (Tcl_WideInt)1024*1024*1024*1024*4, +    (Tcl_WideInt)1024*1024*1024*1024*4*4, +    (Tcl_WideInt)1024*1024*1024*1024*4*4*4, +    (Tcl_WideInt)1024*1024*1024*1024*4*4*4*4, +    (Tcl_WideInt)1024*1024*1024*1024*1024, +    (Tcl_WideInt)1024*1024*1024*1024*1024*4, +    (Tcl_WideInt)1024*1024*1024*1024*1024*4*4, +    (Tcl_WideInt)1024*1024*1024*1024*1024*4*4*4, +    (Tcl_WideInt)1024*1024*1024*1024*1024*4*4*4*4, +    (Tcl_WideInt)1024*1024*1024*1024*1024*1024, +    (Tcl_WideInt)1024*1024*1024*1024*1024*1024*4, +    (Tcl_WideInt)3125*3125*3125*5*5, +    (Tcl_WideInt)3125*3125*3125*5*5*5, +    (Tcl_WideInt)3125*3125*3125*5*5*5*5, +    (Tcl_WideInt)3125*3125*3125*3125, +    (Tcl_WideInt)3125*3125*3125*3125*5, +    (Tcl_WideInt)3125*3125*3125*3125*5*5, +    (Tcl_WideInt)3125*3125*3125*3125*5*5*5, +    (Tcl_WideInt)3125*3125*3125*3125*5*5*5*5, +    (Tcl_WideInt)3125*3125*3125*3125*3125, +    (Tcl_WideInt)3125*3125*3125*3125*3125*5, +    (Tcl_WideInt)3125*3125*3125*3125*3125*5*5, +    (Tcl_WideInt)7776*7776*7776*6*6, +    (Tcl_WideInt)7776*7776*7776*6*6*6, +    (Tcl_WideInt)7776*7776*7776*6*6*6*6, +    (Tcl_WideInt)7776*7776*7776*7776, +    (Tcl_WideInt)7776*7776*7776*7776*6, +    (Tcl_WideInt)7776*7776*7776*7776*6*6, +    (Tcl_WideInt)7776*7776*7776*7776*6*6*6, +    (Tcl_WideInt)7776*7776*7776*7776*6*6*6*6, +    (Tcl_WideInt)16807*16807*16807*7*7, +    (Tcl_WideInt)16807*16807*16807*7*7*7, +    (Tcl_WideInt)16807*16807*16807*7*7*7*7, +    (Tcl_WideInt)16807*16807*16807*16807, +    (Tcl_WideInt)16807*16807*16807*16807*7, +    (Tcl_WideInt)16807*16807*16807*16807*7*7, +    (Tcl_WideInt)32768*32768*32768*8*8, +    (Tcl_WideInt)32768*32768*32768*8*8*8, +    (Tcl_WideInt)32768*32768*32768*8*8*8*8, +    (Tcl_WideInt)32768*32768*32768*32768, +    (Tcl_WideInt)59049*59049*59049*9*9, +    (Tcl_WideInt)59049*59049*59049*9*9*9, +    (Tcl_WideInt)59049*59049*59049*9*9*9*9, +    (Tcl_WideInt)100000*100000*100000*10*10, +    (Tcl_WideInt)100000*100000*100000*10*10*10, +    (Tcl_WideInt)161051*161051*161051*11*11, +    (Tcl_WideInt)161051*161051*161051*11*11*11, +    (Tcl_WideInt)248832*248832*248832*12*12, +    (Tcl_WideInt)371293*371293*371293*13*13 +}; +static const size_t Exp64ValueSize = sizeof(Exp64Value) / sizeof(Tcl_WideInt); +#endif /* (LONG_MAX > 0x7fffffff) || !defined(TCL_WIDE_INT_IS_LONG) */ + +/* + * Markers for ExecuteExtendedBinaryMathOp. + */ + +#define DIVIDED_BY_ZERO		((Tcl_Obj *) -1) +#define EXPONENT_OF_ZERO	((Tcl_Obj *) -2) +#define GENERAL_ARITHMETIC_ERROR ((Tcl_Obj *) -3) + +/*   * Declarations for local procedures to this file:   */ -static void		CallTraceProcedure _ANSI_ARGS_((Tcl_Interp *interp, -			    Trace *tracePtr, Command *cmdPtr, -			    char *command, int numChars, -			    int objc, Tcl_Obj *objv[])); -static void		DupCmdNameInternalRep _ANSI_ARGS_((Tcl_Obj *objPtr, -			    Tcl_Obj *copyPtr)); -static int		ExprAbsFunc _ANSI_ARGS_((Tcl_Interp *interp, -			    ExecEnv *eePtr, ClientData clientData)); -static int		ExprBinaryFunc _ANSI_ARGS_((Tcl_Interp *interp, -			    ExecEnv *eePtr, ClientData clientData)); -static int		ExprCallMathFunc _ANSI_ARGS_((Tcl_Interp *interp, -			    ExecEnv *eePtr, int objc, Tcl_Obj **objv)); -static int		ExprDoubleFunc _ANSI_ARGS_((Tcl_Interp *interp, -			    ExecEnv *eePtr, ClientData clientData)); -static int		ExprIntFunc _ANSI_ARGS_((Tcl_Interp *interp, -			    ExecEnv *eePtr, ClientData clientData)); -static int		ExprRandFunc _ANSI_ARGS_((Tcl_Interp *interp, -			    ExecEnv *eePtr, ClientData clientData)); -static int		ExprRoundFunc _ANSI_ARGS_((Tcl_Interp *interp, -			    ExecEnv *eePtr, ClientData clientData)); -static int		ExprSrandFunc _ANSI_ARGS_((Tcl_Interp *interp, -			    ExecEnv *eePtr, ClientData clientData)); -static int		ExprUnaryFunc _ANSI_ARGS_((Tcl_Interp *interp, -			    ExecEnv *eePtr, ClientData clientData));  #ifdef TCL_COMPILE_STATS -static int              EvalStatsCmd _ANSI_ARGS_((ClientData clientData, -                            Tcl_Interp *interp, int argc, char **argv)); -#endif -static void		FreeCmdNameInternalRep _ANSI_ARGS_(( -    			    Tcl_Obj *objPtr)); -#ifdef TCL_COMPILE_DEBUG -static char *		GetOpcodeName _ANSI_ARGS_((unsigned char *pc)); -#endif -static ExceptionRange *	GetExceptRangeForPc _ANSI_ARGS_((unsigned char *pc, -			    int catchOnly, ByteCode* codePtr)); -static char *		GetSrcInfoForPc _ANSI_ARGS_((unsigned char *pc, -        		    ByteCode* codePtr, int *lengthPtr)); -static void		GrowEvaluationStack _ANSI_ARGS_((ExecEnv *eePtr)); -static void		IllegalExprOperandType _ANSI_ARGS_(( -			    Tcl_Interp *interp, unsigned char *pc, -			    Tcl_Obj *opndPtr)); -static void		InitByteCodeExecution _ANSI_ARGS_(( -			    Tcl_Interp *interp)); -#ifdef TCL_COMPILE_DEBUG -static void		PrintByteCodeInfo _ANSI_ARGS_((ByteCode *codePtr)); -#endif -static int		SetCmdNameFromAny _ANSI_ARGS_((Tcl_Interp *interp, -			    Tcl_Obj *objPtr)); +static int		EvalStatsCmd(ClientData clientData, +			    Tcl_Interp *interp, int objc, +			    Tcl_Obj *const objv[]); +#endif /* TCL_COMPILE_STATS */  #ifdef TCL_COMPILE_DEBUG -static char *		StringForResultCode _ANSI_ARGS_((int result)); -static void		ValidatePcAndStackTop _ANSI_ARGS_(( -			    ByteCode *codePtr, unsigned char *pc, -			    int stackTop, int stackLowerBound, -			    int stackUpperBound)); -#endif -static int		VerifyExprObjType _ANSI_ARGS_((Tcl_Interp *interp, -			    Tcl_Obj *objPtr)); +static const char *	GetOpcodeName(const unsigned char *pc); +static void		PrintByteCodeInfo(ByteCode *codePtr); +static const char *	StringForResultCode(int result); +static void		ValidatePcAndStackTop(ByteCode *codePtr, +			    const unsigned char *pc, int stackTop, +			    int checkStack); +#endif /* TCL_COMPILE_DEBUG */ +static ByteCode *	CompileExprObj(Tcl_Interp *interp, Tcl_Obj *objPtr); +static void		DeleteExecStack(ExecStack *esPtr); +static void		DupExprCodeInternalRep(Tcl_Obj *srcPtr, +			    Tcl_Obj *copyPtr); +MODULE_SCOPE int	TclCompareTwoNumbers(Tcl_Obj *valuePtr, +			    Tcl_Obj *value2Ptr); +static Tcl_Obj *	ExecuteExtendedBinaryMathOp(Tcl_Interp *interp, +			    int opcode, Tcl_Obj **constants, +			    Tcl_Obj *valuePtr, Tcl_Obj *value2Ptr); +static Tcl_Obj *	ExecuteExtendedUnaryMathOp(int opcode, +			    Tcl_Obj *valuePtr); +static void		FreeExprCodeInternalRep(Tcl_Obj *objPtr); +static ExceptionRange *	GetExceptRangeForPc(const unsigned char *pc, +			    int catchOnly, ByteCode *codePtr); +static const char *	GetSrcInfoForPc(const unsigned char *pc, +			    ByteCode *codePtr, int *lengthPtr, +			    const unsigned char **pcBeg, int *cmdIdxPtr); +static Tcl_Obj **	GrowEvaluationStack(ExecEnv *eePtr, int growth, +			    int move); +static void		IllegalExprOperandType(Tcl_Interp *interp, +			    const unsigned char *pc, Tcl_Obj *opndPtr); +static void		InitByteCodeExecution(Tcl_Interp *interp); +static inline int	wordSkip(void *ptr); +static void		ReleaseDictIterator(Tcl_Obj *objPtr); +/* Useful elsewhere, make available in tclInt.h or stubs? */ +static Tcl_Obj **	StackAllocWords(Tcl_Interp *interp, int numWords); +static Tcl_Obj **	StackReallocWords(Tcl_Interp *interp, int numWords); +static Tcl_NRPostProc	CopyCallback; +static Tcl_NRPostProc	ExprObjCallback; +static Tcl_NRPostProc	FinalizeOONext; +static Tcl_NRPostProc	FinalizeOONextFilter; +static Tcl_NRPostProc   TEBCresume;  /* - * Table describing the built-in math functions. Entries in this table are - * indexed by the values of the INST_CALL_BUILTIN_FUNC instruction's - * operand byte. + * The structure below defines a bytecode Tcl object type to hold the + * compiled bytecode for Tcl expressions.   */ -BuiltinFunc builtinFuncTable[] = { -#ifndef TCL_NO_MATH -    {"acos", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) acos}, -    {"asin", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) asin}, -    {"atan", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) atan}, -    {"atan2", 2, {TCL_DOUBLE, TCL_DOUBLE}, ExprBinaryFunc, (ClientData) atan2}, -    {"ceil", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) ceil}, -    {"cos", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) cos}, -    {"cosh", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) cosh}, -    {"exp", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) exp}, -    {"floor", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) floor}, -    {"fmod", 2, {TCL_DOUBLE, TCL_DOUBLE}, ExprBinaryFunc, (ClientData) fmod}, -    {"hypot", 2, {TCL_DOUBLE, TCL_DOUBLE}, ExprBinaryFunc, (ClientData) hypot}, -    {"log", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) log}, -    {"log10", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) log10}, -    {"pow", 2, {TCL_DOUBLE, TCL_DOUBLE}, ExprBinaryFunc, (ClientData) pow}, -    {"sin", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) sin}, -    {"sinh", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) sinh}, -    {"sqrt", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) sqrt}, -    {"tan", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) tan}, -    {"tanh", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) tanh}, -#endif -    {"abs", 1, {TCL_EITHER}, ExprAbsFunc, 0}, -    {"double", 1, {TCL_EITHER}, ExprDoubleFunc, 0}, -    {"int", 1, {TCL_EITHER}, ExprIntFunc, 0}, -    {"rand", 0, {TCL_EITHER}, ExprRandFunc, 0},	/* NOTE: rand takes no args. */ -    {"round", 1, {TCL_EITHER}, ExprRoundFunc, 0}, -    {"srand", 1, {TCL_INT}, ExprSrandFunc, 0}, -    {0}, +static const Tcl_ObjType exprCodeType = { +    "exprcode", +    FreeExprCodeInternalRep,	/* freeIntRepProc */ +    DupExprCodeInternalRep,	/* dupIntRepProc */ +    NULL,			/* updateStringProc */ +    NULL			/* setFromAnyProc */  };  /* - * The structure below defines the command name Tcl object type by means of - * procedures that can be invoked by generic object code. Objects of this - * type cache the Command pointer that results from looking up command names - * in the command hashtable. Such objects appear as the zeroth ("command - * name") argument in a Tcl command. + * Custom object type only used in this file; values of its type should never + * be seen by user scripts.   */ -Tcl_ObjType tclCmdNameType = { -    "cmdName",				/* name */ -    FreeCmdNameInternalRep,		/* freeIntRepProc */ -    DupCmdNameInternalRep,		/* dupIntRepProc */ -    (Tcl_UpdateStringProc *) NULL,	/* updateStringProc */ -    SetCmdNameFromAny			/* setFromAnyProc */ +static const Tcl_ObjType dictIteratorType = { +    "dictIterator", +    ReleaseDictIterator, +    NULL, NULL, NULL  };  /*   *----------------------------------------------------------------------   * + * ReleaseDictIterator -- + * + *	This takes apart a dictionary iterator that is stored in the given Tcl + *	object. + * + * Results: + *	None. + * + * Side effects: + *	Deallocates memory, marks the object as being untyped. + * + *---------------------------------------------------------------------- + */ + +static void +ReleaseDictIterator( +    Tcl_Obj *objPtr) +{ +    Tcl_DictSearch *searchPtr; +    Tcl_Obj *dictPtr; + +    /* +     * First kill the search, and then release the reference to the dictionary +     * that we were holding. +     */ + +    searchPtr = objPtr->internalRep.twoPtrValue.ptr1; +    Tcl_DictObjDone(searchPtr); +    ckfree(searchPtr); + +    dictPtr = objPtr->internalRep.twoPtrValue.ptr2; +    TclDecrRefCount(dictPtr); + +    objPtr->typePtr = NULL; +} + +/* + *---------------------------------------------------------------------- + *   * InitByteCodeExecution --   *   *	This procedure is called once to initialize the Tcl bytecode @@ -342,30 +880,28 @@ Tcl_ObjType tclCmdNameType = {   *   * Side effects:   *	This procedure initializes the array of instruction names. If - *	compiling with the TCL_COMPILE_STATS flag, it initializes the - *	array that counts the executions of each instruction and it - *	creates the "evalstats" command. It also registers the command name - *	Tcl_ObjType. It also establishes the link between the Tcl + *	compiling with the TCL_COMPILE_STATS flag, it initializes the array + *	that counts the executions of each instruction and it creates the + *	"evalstats" command. It also establishes the link between the Tcl   *	"tcl_traceExec" and C "tclTraceExec" variables.   *   *----------------------------------------------------------------------   */  static void -InitByteCodeExecution(interp) -    Tcl_Interp *interp;		/* Interpreter for which the Tcl variable +InitByteCodeExecution( +    Tcl_Interp *interp)		/* Interpreter for which the Tcl variable  				 * "tcl_traceExec" is linked to control  				 * instruction tracing. */  { -    Tcl_RegisterObjType(&tclCmdNameType); +#ifdef TCL_COMPILE_DEBUG      if (Tcl_LinkVar(interp, "tcl_traceExec", (char *) &tclTraceExec, -		    TCL_LINK_INT) != TCL_OK) { -	panic("InitByteCodeExecution: can't create link for tcl_traceExec variable"); +	    TCL_LINK_INT) != TCL_OK) { +	Tcl_Panic("InitByteCodeExecution: can't create link for tcl_traceExec variable");      } - -#ifdef TCL_COMPILE_STATS     -    Tcl_CreateCommand(interp, "evalstats", EvalStatsCmd, -		      (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); +#endif +#ifdef TCL_COMPILE_STATS +    Tcl_CreateObjCommand(interp, "evalstats", EvalStatsCmd, NULL, NULL);  #endif /* TCL_COMPILE_STATS */  } @@ -375,35 +911,48 @@ InitByteCodeExecution(interp)   * TclCreateExecEnv --   *   *	This procedure creates a new execution environment for Tcl bytecode - *	execution. An ExecEnv points to a Tcl evaluation stack. An ExecEnv - *	is typically created once for each Tcl interpreter (Interp - *	structure) and recursively passed to TclExecuteByteCode to execute - *	ByteCode sequences for nested commands. + *	execution. An ExecEnv points to a Tcl evaluation stack. An ExecEnv is + *	typically created once for each Tcl interpreter (Interp structure) and + *	recursively passed to TclNRExecuteByteCode to execute ByteCode sequences + *	for nested commands.   *   * Results:   *	A newly allocated ExecEnv is returned. This points to an empty   *	evaluation stack of the standard initial size.   *   * Side effects: - *	The bytecode interpreter is also initialized here, as this - *	procedure will be called before any call to TclExecuteByteCode. + *	The bytecode interpreter is also initialized here, as this procedure + *	will be called before any call to TclNRExecuteByteCode.   *   *----------------------------------------------------------------------   */ -#define TCL_STACK_INITIAL_SIZE 2000 -  ExecEnv * -TclCreateExecEnv(interp) -    Tcl_Interp *interp;		/* Interpreter for which the execution +TclCreateExecEnv( +    Tcl_Interp *interp,		/* Interpreter for which the execution  				 * environment is being created. */ +    int size)			/* The initial stack size, in number of words +				 * [sizeof(Tcl_Obj*)] */  { -    ExecEnv *eePtr = (ExecEnv *) ckalloc(sizeof(ExecEnv)); - -    eePtr->stackPtr = (Tcl_Obj **) -	ckalloc((unsigned) (TCL_STACK_INITIAL_SIZE * sizeof(Tcl_Obj *))); -    eePtr->stackTop = -1; -    eePtr->stackEnd = (TCL_STACK_INITIAL_SIZE - 1); +    ExecEnv *eePtr = ckalloc(sizeof(ExecEnv)); +    ExecStack *esPtr = ckalloc(sizeof(ExecStack) +	    + (size_t) (size-1) * sizeof(Tcl_Obj *)); + +    eePtr->execStackPtr = esPtr; +    TclNewBooleanObj(eePtr->constants[0], 0); +    Tcl_IncrRefCount(eePtr->constants[0]); +    TclNewBooleanObj(eePtr->constants[1], 1); +    Tcl_IncrRefCount(eePtr->constants[1]); +    eePtr->interp = interp; +    eePtr->callbackPtr = NULL; +    eePtr->corPtr = NULL; +    eePtr->rewind = 0; + +    esPtr->prevPtr = NULL; +    esPtr->nextPtr = NULL; +    esPtr->markerPtr = NULL; +    esPtr->endPtr = &esPtr->stackWords[size-1]; +    esPtr->tosPtr = STACK_BASE(esPtr);      Tcl_MutexLock(&execMutex);      if (!execInitialized) { @@ -415,7 +964,6 @@ TclCreateExecEnv(interp)      return eePtr;  } -#undef TCL_STACK_INITIAL_SIZE  /*   *---------------------------------------------------------------------- @@ -428,18 +976,59 @@ TclCreateExecEnv(interp)   *	None.   *   * Side effects: - *	Storage for an ExecEnv and its contained storage (e.g. the - *	evaluation stack) is freed. + *	Storage for an ExecEnv and its contained storage (e.g. the evaluation + *	stack) is freed.   *   *----------------------------------------------------------------------   */ +static void +DeleteExecStack( +    ExecStack *esPtr) +{ +    if (esPtr->markerPtr && !cachedInExit) { +	Tcl_Panic("freeing an execStack which is still in use"); +    } + +    if (esPtr->prevPtr) { +	esPtr->prevPtr->nextPtr = esPtr->nextPtr; +    } +    if (esPtr->nextPtr) { +	esPtr->nextPtr->prevPtr = esPtr->prevPtr; +    } +    ckfree(esPtr); +} +  void -TclDeleteExecEnv(eePtr) -    ExecEnv *eePtr;		/* Execution environment to free. */ +TclDeleteExecEnv( +    ExecEnv *eePtr)		/* Execution environment to free. */  { -    Tcl_EventuallyFree((ClientData)eePtr->stackPtr, TCL_DYNAMIC); -    ckfree((char *) eePtr); +    ExecStack *esPtr = eePtr->execStackPtr, *tmpPtr; + +	cachedInExit = TclInExit(); + +    /* +     * Delete all stacks in this exec env. +     */ + +    while (esPtr->nextPtr) { +	esPtr = esPtr->nextPtr; +    } +    while (esPtr) { +	tmpPtr = esPtr; +	esPtr = tmpPtr->prevPtr; +	DeleteExecStack(tmpPtr); +    } + +    TclDecrRefCount(eePtr->constants[0]); +    TclDecrRefCount(eePtr->constants[1]); +    if (eePtr->callbackPtr && !cachedInExit) { +	Tcl_Panic("Deleting execEnv with pending TEOV callbacks!"); +    } +    if (eePtr->corPtr && !cachedInExit) { +	Tcl_Panic("Deleting execEnv with existing coroutine"); +    } +    ckfree(eePtr);  }  /* @@ -447,21 +1036,21 @@ TclDeleteExecEnv(eePtr)   *   * TclFinalizeExecution --   * - *	Finalizes the execution environment setup so that it can be - *	later reinitialized. + *	Finalizes the execution environment setup so that it can be later + *	reinitialized.   *   * Results:   *	None.   *   * Side effects: - *	After this call, the next time TclCreateExecEnv will be called - *	it will call InitByteCodeExecution. + *	After this call, the next time TclCreateExecEnv will be called it will + *	call InitByteCodeExecution.   *   *----------------------------------------------------------------------   */  void -TclFinalizeExecution() +TclFinalizeExecution(void)  {      Tcl_MutexLock(&execMutex);      execInitialized = 0; @@ -470,2997 +1059,8454 @@ TclFinalizeExecution()  }  /* + * Auxiliary code to insure that GrowEvaluationStack always returns correctly + * aligned memory. + * + * WALLOCALIGN represents the alignment reqs in words, just as TCL_ALLOCALIGN + * represents the reqs in bytes. This assumes that TCL_ALLOCALIGN is a + * multiple of the wordsize 'sizeof(Tcl_Obj *)'. + */ + +#define WALLOCALIGN \ +    (TCL_ALLOCALIGN/sizeof(Tcl_Obj *)) + +/* + * wordSkip computes how many words have to be skipped until the next aligned + * word. Note that we are only interested in the low order bits of ptr, so + * that any possible information loss in PTR2INT is of no consequence. + */ + +static inline int +wordSkip( +    void *ptr) +{ +    int mask = TCL_ALLOCALIGN-1; +    int base = PTR2INT(ptr) & mask; +    return (TCL_ALLOCALIGN - base)/sizeof(Tcl_Obj *); +} + +/* + * Given a marker, compute where the following aligned memory starts. + */ + +#define MEMSTART(markerPtr) \ +    ((markerPtr) + wordSkip(markerPtr)) + +/*   *----------------------------------------------------------------------   *   * GrowEvaluationStack --   * - *	This procedure grows a Tcl evaluation stack stored in an ExecEnv. + *	This procedure grows a Tcl evaluation stack stored in an ExecEnv, + *	copying over the words since the last mark if so requested. A mark is + *	set at the beginning of the new area when no copying is requested. + * + * Results: + *	Returns a pointer to the first usable word in the (possibly) grown + *	stack. + * + * Side effects: + *	The size of the evaluation stack may be grown, a marker is set + * + *---------------------------------------------------------------------- + */ + +static Tcl_Obj ** +GrowEvaluationStack( +    ExecEnv *eePtr,		/* Points to the ExecEnv with an evaluation +				 * stack to enlarge. */ +    int growth,			/* How much larger than the current used +				 * size. */ +    int move)			/* 1 if move words since last marker. */ +{ +    ExecStack *esPtr = eePtr->execStackPtr, *oldPtr = NULL; +    int newBytes, newElems, currElems; +    int needed = growth - (esPtr->endPtr - esPtr->tosPtr); +    Tcl_Obj **markerPtr = esPtr->markerPtr, **memStart; +    int moveWords = 0; + +    if (move) { +	if (!markerPtr) { +	    Tcl_Panic("STACK: Reallocating with no previous alloc"); +	} +	if (needed <= 0) { +	    return MEMSTART(markerPtr); +	} +    } else { +#ifndef PURIFY +	Tcl_Obj **tmpMarkerPtr = esPtr->tosPtr + 1; +	int offset = wordSkip(tmpMarkerPtr); + +	if (needed + offset < 0) { +	    /* +	     * Put a marker pointing to the previous marker in this stack, and +	     * store it in esPtr as the current marker. Return a pointer to +	     * the start of aligned memory. +	     */ + +	    esPtr->markerPtr = tmpMarkerPtr; +	    memStart = tmpMarkerPtr + offset; +	    esPtr->tosPtr = memStart - 1; +	    *esPtr->markerPtr = (Tcl_Obj *) markerPtr; +	    return memStart; +	} +#endif +    } + +    /* +     * Reset move to hold the number of words to be moved to new stack (if +     * any) and growth to hold the complete stack requirements: add one for +     * the marker, (WALLOCALIGN-1) for the maximal possible offset. +     */ + +    if (move) { +	moveWords = esPtr->tosPtr - MEMSTART(markerPtr) + 1; +    } +    needed = growth + moveWords + WALLOCALIGN; + +     +    /* +     * Check if there is enough room in the next stack (if there is one, it +     * should be both empty and the last one!) +     */ + +    if (esPtr->nextPtr) { +	oldPtr = esPtr; +	esPtr = oldPtr->nextPtr; +	currElems = esPtr->endPtr - STACK_BASE(esPtr); +	if (esPtr->markerPtr || (esPtr->tosPtr != STACK_BASE(esPtr))) { +	    Tcl_Panic("STACK: Stack after current is in use"); +	} +	if (esPtr->nextPtr) { +	    Tcl_Panic("STACK: Stack after current is not last"); +	} +	if (needed <= currElems) { +	    goto newStackReady; +	} +	DeleteExecStack(esPtr); +	esPtr = oldPtr; +    } else { +	currElems = esPtr->endPtr - STACK_BASE(esPtr); +    } + +    /* +     * We need to allocate a new stack! It needs to store 'growth' words, +     * including the elements to be copied over and the new marker. +     */ + +#ifndef PURIFY +    newElems = 2*currElems; +    while (needed > newElems) { +	newElems *= 2; +    } +#else +    newElems = needed; +#endif +     +    newBytes = sizeof(ExecStack) + (newElems-1) * sizeof(Tcl_Obj *); + +    oldPtr = esPtr; +    esPtr = ckalloc(newBytes); + +    oldPtr->nextPtr = esPtr; +    esPtr->prevPtr = oldPtr; +    esPtr->nextPtr = NULL; +    esPtr->endPtr = &esPtr->stackWords[newElems-1]; + +  newStackReady: +    eePtr->execStackPtr = esPtr; + +    /* +     * Store a NULL marker at the beginning of the stack, to indicate that +     * this is the first marker in this stack and that rewinding to here +     * should actually be a return to the previous stack. +     */ + +    esPtr->stackWords[0] = NULL; +    esPtr->markerPtr = &esPtr->stackWords[0]; +    memStart = MEMSTART(esPtr->markerPtr); +    esPtr->tosPtr = memStart - 1; + +    if (move) { +	memcpy(memStart, MEMSTART(markerPtr), moveWords*sizeof(Tcl_Obj *)); +	esPtr->tosPtr += moveWords; +	oldPtr->markerPtr = (Tcl_Obj **) *markerPtr; +	oldPtr->tosPtr = markerPtr-1; +    } + +    /* +     * Free the old stack if it is now unused. +     */ + +    if (!oldPtr->markerPtr) { +	DeleteExecStack(oldPtr); +    } + +    return memStart; +} + +/* + *-------------------------------------------------------------- + * + * TclStackAlloc, TclStackRealloc, TclStackFree -- + * + *	Allocate memory from the execution stack; it has to be returned later + *	with a call to TclStackFree. + * + * Results: + *	A pointer to the first byte allocated, or panics if the allocation did + *	not succeed. + * + * Side effects: + *	The execution stack may be grown. + * + *-------------------------------------------------------------- + */ + +static Tcl_Obj ** +StackAllocWords( +    Tcl_Interp *interp, +    int numWords) +{ +    /* +     * Note that GrowEvaluationStack sets a marker in the stack. This marker +     * is read when rewinding, e.g., by TclStackFree. +     */ + +    Interp *iPtr = (Interp *) interp; +    ExecEnv *eePtr = iPtr->execEnvPtr; +    Tcl_Obj **resPtr = GrowEvaluationStack(eePtr, numWords, 0); + +    eePtr->execStackPtr->tosPtr += numWords; +    return resPtr; +} + +static Tcl_Obj ** +StackReallocWords( +    Tcl_Interp *interp, +    int numWords) +{ +    Interp *iPtr = (Interp *) interp; +    ExecEnv *eePtr = iPtr->execEnvPtr; +    Tcl_Obj **resPtr = GrowEvaluationStack(eePtr, numWords, 1); + +    eePtr->execStackPtr->tosPtr += numWords; +    return resPtr; +} + +void +TclStackFree( +    Tcl_Interp *interp, +    void *freePtr) +{ +    Interp *iPtr = (Interp *) interp; +    ExecEnv *eePtr; +    ExecStack *esPtr; +    Tcl_Obj **markerPtr, *marker; + +    if (iPtr == NULL || iPtr->execEnvPtr == NULL) { +	ckfree((char *) freePtr); +	return; +    } + +    /* +     * Rewind the stack to the previous marker position. The current marker, +     * as set in the last call to GrowEvaluationStack, contains a pointer to +     * the previous marker. +     */ + +    eePtr = iPtr->execEnvPtr; +    esPtr = eePtr->execStackPtr; +    markerPtr = esPtr->markerPtr; +    marker = *markerPtr; + +    if ((freePtr != NULL) && (MEMSTART(markerPtr) != (Tcl_Obj **)freePtr)) { +	Tcl_Panic("TclStackFree: incorrect freePtr (%p != %p). Call out of sequence?", +		freePtr, MEMSTART(markerPtr)); +    } + +    esPtr->tosPtr = markerPtr - 1; +    esPtr->markerPtr = (Tcl_Obj **) marker; +    if (marker) { +	return; +    } + +    /* +     * Return to previous active stack. Note that repeated expansions or +     * reallocs could have generated several unused intervening stacks: free +     * them too. +     */ + +    while (esPtr->nextPtr) { +	esPtr = esPtr->nextPtr; +    } +    esPtr->tosPtr = STACK_BASE(esPtr); +    while (esPtr->prevPtr) { +	ExecStack *tmpPtr = esPtr->prevPtr; +	if (tmpPtr->tosPtr == STACK_BASE(tmpPtr)) { +	    DeleteExecStack(tmpPtr); +	} else { +	    break; +	} +    } +    if (esPtr->prevPtr) { +	eePtr->execStackPtr = esPtr->prevPtr; +#ifdef PURIFY +	eePtr->execStackPtr->nextPtr = NULL; +	DeleteExecStack(esPtr); +#endif +    } else { +	eePtr->execStackPtr = esPtr; +    } +} + +void * +TclStackAlloc( +    Tcl_Interp *interp, +    int numBytes) +{ +    Interp *iPtr = (Interp *) interp; +    int numWords = (numBytes + (sizeof(Tcl_Obj *) - 1))/sizeof(Tcl_Obj *); + +    if (iPtr == NULL || iPtr->execEnvPtr == NULL) { +	return (void *) ckalloc(numBytes); +    } + +    return (void *) StackAllocWords(interp, numWords); +} + +void * +TclStackRealloc( +    Tcl_Interp *interp, +    void *ptr, +    int numBytes) +{ +    Interp *iPtr = (Interp *) interp; +    ExecEnv *eePtr; +    ExecStack *esPtr; +    Tcl_Obj **markerPtr; +    int numWords; + +    if (iPtr == NULL || iPtr->execEnvPtr == NULL) { +	return (void *) ckrealloc((char *) ptr, numBytes); +    } + +    eePtr = iPtr->execEnvPtr; +    esPtr = eePtr->execStackPtr; +    markerPtr = esPtr->markerPtr; + +    if (MEMSTART(markerPtr) != (Tcl_Obj **)ptr) { +	Tcl_Panic("TclStackRealloc: incorrect ptr. Call out of sequence?"); +    } + +    numWords = (numBytes + (sizeof(Tcl_Obj *) - 1))/sizeof(Tcl_Obj *); +    return (void *) StackReallocWords(interp, numWords); +} + +/* + *-------------------------------------------------------------- + * + * Tcl_ExprObj -- + * + *	Evaluate an expression in a Tcl_Obj. + * + * Results: + *	A standard Tcl object result. If the result is other than TCL_OK, then + *	the interpreter's result contains an error message. If the result is + *	TCL_OK, then a pointer to the expression's result value object is + *	stored in resultPtrPtr. In that case, the object's ref count is + *	incremented to reflect the reference returned to the caller; the + *	caller is then responsible for the resulting object and must, for + *	example, decrement the ref count when it is finished with the object. + * + * Side effects: + *	Any side effects caused by subcommands in the expression, if any. The + *	interpreter result is not modified unless there is an error. + * + *-------------------------------------------------------------- + */ + +int +Tcl_ExprObj( +    Tcl_Interp *interp,		/* Context in which to evaluate the +				 * expression. */ +    register Tcl_Obj *objPtr,	/* Points to Tcl object containing expression +				 * to evaluate. */ +    Tcl_Obj **resultPtrPtr)	/* Where the Tcl_Obj* that is the expression +				 * result is stored if no errors occur. */ +{ +    NRE_callback *rootPtr = TOP_CB(interp); +    Tcl_Obj *resultPtr; + +    TclNewObj(resultPtr); +    TclNRAddCallback(interp, CopyCallback, resultPtrPtr, resultPtr, +	    NULL, NULL); +    Tcl_NRExprObj(interp, objPtr, resultPtr); +    return TclNRRunCallbacks(interp, TCL_OK, rootPtr); +} + +static int +CopyCallback( +    ClientData data[], +    Tcl_Interp *interp, +    int result) +{ +    Tcl_Obj **resultPtrPtr = data[0]; +    Tcl_Obj *resultPtr = data[1]; + +    if (result == TCL_OK) { +	*resultPtrPtr = resultPtr; +	Tcl_IncrRefCount(resultPtr); +    } else { +	Tcl_DecrRefCount(resultPtr); +    } +    return result; +} + +/* + *-------------------------------------------------------------- + * + * Tcl_NRExprObj -- + * + *	Request evaluation of the expression in a Tcl_Obj by the NR stack. + * + * Results: + *	Returns TCL_OK. + * + * Side effects: + *	Compiles objPtr as a Tcl expression and places callbacks on the + *	NR stack to execute the bytecode and store the result in resultPtr. + *	If bytecode execution raises an exception, nothing is written + *	to resultPtr, and the exceptional return code flows up the NR + *	stack.  If the exception is TCL_ERROR, an error message is left + *	in the interp result and the interp's return options dictionary + *	holds additional error information too.  Execution of the bytecode + *	may have other side effects, depending on the expression. + * + *-------------------------------------------------------------- + */ + +int +Tcl_NRExprObj( +    Tcl_Interp *interp, +    Tcl_Obj *objPtr, +    Tcl_Obj *resultPtr) +{ +    ByteCode *codePtr; +    Tcl_InterpState state = Tcl_SaveInterpState(interp, TCL_OK); + +    Tcl_ResetResult(interp); +    codePtr = CompileExprObj(interp, objPtr); + +    Tcl_NRAddCallback(interp, ExprObjCallback, state, resultPtr, +	    NULL, NULL); +    return TclNRExecuteByteCode(interp, codePtr); +} + +static int +ExprObjCallback( +    ClientData data[], +    Tcl_Interp *interp, +    int result) +{ +    Tcl_InterpState state = data[0]; +    Tcl_Obj *resultPtr = data[1]; + +    if (result == TCL_OK) { +	TclSetDuplicateObj(resultPtr, Tcl_GetObjResult(interp)); +	(void) Tcl_RestoreInterpState(interp, state); +    } else { +	Tcl_DiscardInterpState(state); +    } +    return result; +} + +/* + *---------------------------------------------------------------------- + * + * CompileExprObj -- + *	Compile a Tcl expression value into ByteCode. + * + * Results: + *	A (ByteCode *) is returned pointing to the resulting ByteCode. + *	The caller must manage its refCount and arrange for a call to + *	TclCleanupByteCode() when the last reference disappears. + * + * Side effects: + *	The Tcl_ObjType of objPtr is changed to the "bytecode" type, + *	and the ByteCode is kept in the internal rep (along with context + *	data for checking validity) for faster operations the next time + *	CompileExprObj is called on the same value. + * + *---------------------------------------------------------------------- + */ + +static ByteCode * +CompileExprObj( +    Tcl_Interp *interp, +    Tcl_Obj *objPtr) +{ +    Interp *iPtr = (Interp *) interp; +    CompileEnv compEnv;		/* Compilation environment structure allocated +				 * in frame. */ +    register ByteCode *codePtr = NULL; +				/* Tcl Internal type of bytecode. Initialized +				 * to avoid compiler warning. */ + +    /* +     * Get the expression ByteCode from the object. If it exists, make sure it +     * is valid in the current context. +     */ +    if (objPtr->typePtr == &exprCodeType) { +	Namespace *namespacePtr = iPtr->varFramePtr->nsPtr; + +	codePtr = objPtr->internalRep.twoPtrValue.ptr1; +	if (((Interp *) *codePtr->interpHandle != iPtr) +		|| (codePtr->compileEpoch != iPtr->compileEpoch) +		|| (codePtr->nsPtr != namespacePtr) +		|| (codePtr->nsEpoch != namespacePtr->resolverEpoch) +		|| (codePtr->localCachePtr != iPtr->varFramePtr->localCachePtr)) { +	    FreeExprCodeInternalRep(objPtr); +	} +    } +    if (objPtr->typePtr != &exprCodeType) { +	/* +	 * TIP #280: No invoker (yet) - Expression compilation. +	 */ + +	int length; +	const char *string = TclGetStringFromObj(objPtr, &length); + +	TclInitCompileEnv(interp, &compEnv, string, length, NULL, 0); +	TclCompileExpr(interp, string, length, &compEnv, 0); + +	/* +	 * Successful compilation. If the expression yielded no instructions, +	 * push an zero object as the expression's result. +	 */ + +	if (compEnv.codeNext == compEnv.codeStart) { +	    TclEmitPush(TclRegisterNewLiteral(&compEnv, "0", 1), +		    &compEnv); +	} + +	/* +	 * Add a "done" instruction as the last instruction and change the +	 * object into a ByteCode object. Ownership of the literal objects and +	 * aux data items is given to the ByteCode object. +	 */ + +	TclEmitOpcode(INST_DONE, &compEnv); +	TclInitByteCodeObj(objPtr, &compEnv); +	objPtr->typePtr = &exprCodeType; +	TclFreeCompileEnv(&compEnv); +	codePtr = objPtr->internalRep.twoPtrValue.ptr1; +	if (iPtr->varFramePtr->localCachePtr) { +	    codePtr->localCachePtr = iPtr->varFramePtr->localCachePtr; +	    codePtr->localCachePtr->refCount++; +	} +#ifdef TCL_COMPILE_DEBUG +	if (tclTraceCompile == 2) { +	    TclPrintByteCodeObj(interp, objPtr); +	    fflush(stdout); +	} +#endif /* TCL_COMPILE_DEBUG */ +    } +    return codePtr; +} + +/* + *---------------------------------------------------------------------- + * + * DupExprCodeInternalRep -- + * + *	Part of the Tcl object type implementation for Tcl expression + *	bytecode. We do not copy the bytecode intrep. Instead, we return + *	without setting copyPtr->typePtr, so the copy is a plain string copy + *	of the expression value, and if it is to be used as a compiled + *	expression, it will just need a recompile. + * + *	This makes sense, because with Tcl's copy-on-write practices, the + *	usual (only?) time Tcl_DuplicateObj() will be called is when the copy + *	is about to be modified, which would invalidate any copied bytecode + *	anyway. The only reason it might make sense to copy the bytecode is if + *	we had some modifying routines that operated directly on the intrep, + *	like we do for lists and dicts.   *   * Results:   *	None.   *   * Side effects: - *	The size of the evaluation stack is doubled. + *	None.   *   *----------------------------------------------------------------------   */  static void -GrowEvaluationStack(eePtr) -    register ExecEnv *eePtr; /* Points to the ExecEnv with an evaluation -			      * stack to enlarge. */ +DupExprCodeInternalRep( +    Tcl_Obj *srcPtr, +    Tcl_Obj *copyPtr)  { +    return; +} + +/* + *---------------------------------------------------------------------- + * + * FreeExprCodeInternalRep -- + * + *	Part of the Tcl object type implementation for Tcl expression + *	bytecode. Frees the storage allocated to hold the internal rep, unless + *	ref counts indicate bytecode execution is still in progress. + * + * Results: + *	None. + * + * Side effects: + *	May free allocated memory. Leaves objPtr untyped. + * + *---------------------------------------------------------------------- + */ + +static void +FreeExprCodeInternalRep( +    Tcl_Obj *objPtr) +{ +    ByteCode *codePtr = objPtr->internalRep.twoPtrValue.ptr1; + +    objPtr->typePtr = NULL; +    codePtr->refCount--; +    if (codePtr->refCount <= 0) { +	TclCleanupByteCode(codePtr); +    } +} + +/* + *---------------------------------------------------------------------- + * + * TclCompileObj -- + * + *	This procedure compiles the script contained in a Tcl_Obj. + * + * Results: + *	A pointer to the corresponding ByteCode, never NULL. + * + * Side effects: + *	The object is shimmered to bytecode type. + * + *---------------------------------------------------------------------- + */ + +ByteCode * +TclCompileObj( +    Tcl_Interp *interp, +    Tcl_Obj *objPtr, +    const CmdFrame *invoker, +    int word) +{ +    register Interp *iPtr = (Interp *) interp; +    register ByteCode *codePtr;	/* Tcl Internal type of bytecode. */ +    Namespace *namespacePtr = iPtr->varFramePtr->nsPtr; +      /* -     * The current Tcl stack elements are stored from eePtr->stackPtr[0] -     * to eePtr->stackPtr[eePtr->stackEnd] (inclusive). +     * If the object is not already of tclByteCodeType, compile it (and reset +     * the compilation flags in the interpreter; this should be done after any +     * compilation). Otherwise, check that it is "fresh" enough.       */ -    int currElems = (eePtr->stackEnd + 1); -    int newElems  = 2*currElems; -    int currBytes = currElems * sizeof(Tcl_Obj *); -    int newBytes  = 2*currBytes; -    Tcl_Obj **newStackPtr = (Tcl_Obj **) ckalloc((unsigned) newBytes); +    if (objPtr->typePtr == &tclByteCodeType) { +	/* +	 * Make sure the Bytecode hasn't been invalidated by, e.g., someone +	 * redefining a command with a compile procedure (this might make the +	 * compiled code wrong). The object needs to be recompiled if it was +	 * compiled in/for a different interpreter, or for a different +	 * namespace, or for the same namespace but with different name +	 * resolution rules. Precompiled objects, however, are immutable and +	 * therefore they are not recompiled, even if the epoch has changed. +	 * +	 * To be pedantically correct, we should also check that the +	 * originating procPtr is the same as the current context procPtr +	 * (assuming one exists at all - none for global level). This code is +	 * #def'ed out because [info body] was changed to never return a +	 * bytecode type object, which should obviate us from the extra checks +	 * here. +	 */ + +	codePtr = objPtr->internalRep.twoPtrValue.ptr1; +	if (((Interp *) *codePtr->interpHandle != iPtr) +		|| (codePtr->compileEpoch != iPtr->compileEpoch) +		|| (codePtr->nsPtr != namespacePtr) +		|| (codePtr->nsEpoch != namespacePtr->resolverEpoch)) { +	    if (!(codePtr->flags & TCL_BYTECODE_PRECOMPILED)) { +		goto recompileObj; +	    } +	    if ((Interp *) *codePtr->interpHandle != iPtr) { +		Tcl_Panic("Tcl_EvalObj: compiled script jumped interps"); +	    } +	    codePtr->compileEpoch = iPtr->compileEpoch; +	} + +	/* +	 * Check that any compiled locals do refer to the current proc +	 * environment! If not, recompile. +	 */ + +	if (!(codePtr->flags & TCL_BYTECODE_PRECOMPILED) && +		(codePtr->procPtr == NULL) && +		(codePtr->localCachePtr != iPtr->varFramePtr->localCachePtr)){ +	    goto recompileObj; +	} + +	/* +	 * #280. +	 * Literal sharing fix. This part of the fix is not required by 8.4 +	 * nor 8.5, because they eval-direct any literals, so just saving the +	 * argument locations per command in bytecode is enough, embedded +	 * 'eval' commands, etc. get the correct information. +	 * +	 * But in 8.6 all the embedded script are compiled, and the resulting +	 * bytecode stored in the literal. Now the shared literal has bytecode +	 * with location data for _one_ particular location this literal is +	 * found at. If we get executed from a different location the bytecode +	 * has to be recompiled to get the correct locations. Not doing this +	 * will execute the saved bytecode with data for a different location, +	 * causing 'info frame' to point to the wrong place in the sources. +	 * +	 * Future optimizations ... +	 * (1) Save the location data (ExtCmdLoc) keyed by start line. In that +	 *     case we recompile once per location of the literal, but not +	 *     continously, because the moment we have all locations we do not +	 *     need to recompile any longer. +	 * +	 * (2) Alternative: Do not recompile, tell the execution engine the +	 *     offset between saved starting line and actual one. Then modify +	 *     the users to adjust the locations they have by this offset. +	 * +	 * (3) Alternative 2: Do not fully recompile, adjust just the location +	 *     information. +	 */ + +	if (invoker == NULL) { +	    return codePtr; +	} else { +	    Tcl_HashEntry *hePtr = +		    Tcl_FindHashEntry(iPtr->lineBCPtr, codePtr); +	    ExtCmdLoc *eclPtr; +	    CmdFrame *ctxCopyPtr; +	    int redo; + +	    if (!hePtr) { +		return codePtr; +	    } + +	    eclPtr = Tcl_GetHashValue(hePtr); +	    redo = 0; +	    ctxCopyPtr = TclStackAlloc(interp, sizeof(CmdFrame)); +	    *ctxCopyPtr = *invoker; + +	    if (invoker->type == TCL_LOCATION_BC) { +		/* +		 * Note: Type BC => ctx.data.eval.path    is not used. +		 *		    ctx.data.tebc.codePtr used instead +		 */ + +		TclGetSrcInfoForPc(ctxCopyPtr); +		if (ctxCopyPtr->type == TCL_LOCATION_SOURCE) { +		    /* +		     * The reference made by 'TclGetSrcInfoForPc' is dead. +		     */ + +		    Tcl_DecrRefCount(ctxCopyPtr->data.eval.path); +		    ctxCopyPtr->data.eval.path = NULL; +		} +	    } + +	    if (word < ctxCopyPtr->nline) { +		/* +		 * Note: We do not care if the line[word] is -1. This is a +		 * difference and requires a recompile (location changed from +		 * absolute to relative, literal is used fixed and through +		 * variable) +		 * +		 * Example: +		 * test info-32.0 using literal of info-24.8 +		 *     (dict with ... vs           set body ...). +		 */ + +		redo = ((eclPtr->type == TCL_LOCATION_SOURCE) +			    && (eclPtr->start != ctxCopyPtr->line[word])) +			|| ((eclPtr->type == TCL_LOCATION_BC) +			    && (ctxCopyPtr->type == TCL_LOCATION_SOURCE)); +	    } + +	    TclStackFree(interp, ctxCopyPtr); +	    if (!redo) { +		return codePtr; +	    } +	} +    } + +  recompileObj: +    iPtr->errorLine = 1;      /* -     * Copy the existing stack items to the new stack space, free the old -     * storage if appropriate, and mark new space as malloc'ed. +     * TIP #280. Remember the invoker for a moment in the interpreter +     * structures so that the byte code compiler can pick it up when +     * initializing the compilation environment, i.e. the extended location +     * information.       */ -  -    memcpy((VOID *) newStackPtr, (VOID *) eePtr->stackPtr, -	   (size_t) currBytes); -    Tcl_EventuallyFree((ClientData)eePtr->stackPtr, TCL_DYNAMIC); -    eePtr->stackPtr = newStackPtr; -    eePtr->stackEnd = (newElems - 1); /* i.e. index of last usable item */ + +    iPtr->invokeCmdFramePtr = invoker; +    iPtr->invokeWord = word; +    TclSetByteCodeFromAny(interp, objPtr, NULL, NULL); +    iPtr->invokeCmdFramePtr = NULL; +    codePtr = objPtr->internalRep.twoPtrValue.ptr1; +    if (iPtr->varFramePtr->localCachePtr) { +	codePtr->localCachePtr = iPtr->varFramePtr->localCachePtr; +	codePtr->localCachePtr->refCount++; +    } +    return codePtr; +} + +/* + *---------------------------------------------------------------------- + * + * TclIncrObj -- + * + *	Increment an integeral value in a Tcl_Obj by an integeral value held + *	in another Tcl_Obj. Caller is responsible for making sure we can + *	update the first object. + * + * Results: + *	TCL_ERROR if either object is non-integer, and TCL_OK otherwise. On + *	error, an error message is left in the interpreter (if it is not NULL, + *	of course). + * + * Side effects: + *	valuePtr gets the new incrmented value. + * + *---------------------------------------------------------------------- + */ + +int +TclIncrObj( +    Tcl_Interp *interp, +    Tcl_Obj *valuePtr, +    Tcl_Obj *incrPtr) +{ +    ClientData ptr1, ptr2; +    int type1, type2; +    mp_int value, incr; + +    if (Tcl_IsShared(valuePtr)) { +	Tcl_Panic("%s called with shared object", "TclIncrObj"); +    } + +    if (GetNumberFromObj(NULL, valuePtr, &ptr1, &type1) != TCL_OK) { +	/* +	 * Produce error message (reparse?!) +	 */ + +	return TclGetIntFromObj(interp, valuePtr, &type1); +    } +    if (GetNumberFromObj(NULL, incrPtr, &ptr2, &type2) != TCL_OK) { +	/* +	 * Produce error message (reparse?!) +	 */ + +	TclGetIntFromObj(interp, incrPtr, &type1); +	Tcl_AddErrorInfo(interp, "\n    (reading increment)"); +	return TCL_ERROR; +    } + +    if ((type1 == TCL_NUMBER_LONG) && (type2 == TCL_NUMBER_LONG)) { +	long augend = *((const long *) ptr1); +	long addend = *((const long *) ptr2); +	long sum = augend + addend; + +	/* +	 * Overflow when (augend and sum have different sign) and (augend and +	 * addend have the same sign). This is encapsulated in the Overflowing +	 * macro. +	 */ + +	if (!Overflowing(augend, addend, sum)) { +	    TclSetLongObj(valuePtr, sum); +	    return TCL_OK; +	} +#ifndef TCL_WIDE_INT_IS_LONG +	{ +	    Tcl_WideInt w1 = (Tcl_WideInt) augend; +	    Tcl_WideInt w2 = (Tcl_WideInt) addend; + +	    /* +	     * We know the sum value is outside the long range, so we use the +	     * macro form that doesn't range test again. +	     */ + +	    TclSetWideIntObj(valuePtr, w1 + w2); +	    return TCL_OK; +	} +#endif +    } + +    if ((type1 == TCL_NUMBER_DOUBLE) || (type1 == TCL_NUMBER_NAN)) { +	/* +	 * Produce error message (reparse?!) +	 */ + +	return TclGetIntFromObj(interp, valuePtr, &type1); +    } +    if ((type2 == TCL_NUMBER_DOUBLE) || (type2 == TCL_NUMBER_NAN)) { +	/* +	 * Produce error message (reparse?!) +	 */ + +	TclGetIntFromObj(interp, incrPtr, &type1); +	Tcl_AddErrorInfo(interp, "\n    (reading increment)"); +	return TCL_ERROR; +    } + +#ifndef TCL_WIDE_INT_IS_LONG +    if ((type1 != TCL_NUMBER_BIG) && (type2 != TCL_NUMBER_BIG)) { +	Tcl_WideInt w1, w2, sum; + +	TclGetWideIntFromObj(NULL, valuePtr, &w1); +	TclGetWideIntFromObj(NULL, incrPtr, &w2); +	sum = w1 + w2; + +	/* +	 * Check for overflow. +	 */ + +	if (!Overflowing(w1, w2, sum)) { +	    Tcl_SetWideIntObj(valuePtr, sum); +	    return TCL_OK; +	} +    } +#endif + +    Tcl_TakeBignumFromObj(interp, valuePtr, &value); +    Tcl_GetBignumFromObj(interp, incrPtr, &incr); +    mp_add(&value, &incr, &value); +    mp_clear(&incr); +    Tcl_SetBignumObj(valuePtr, &value); +    return TCL_OK;  }  /*   *----------------------------------------------------------------------   * - * TclExecuteByteCode -- + * ArgumentBCEnter --   * - *	This procedure executes the instructions of a ByteCode structure. - *	It returns when a "done" instruction is executed or an error occurs. + *	This is a helper for TclNRExecuteByteCode/TEBCresume that encapsulates + *	a code sequence that is fairly common in the code but *not* commonly + *	called.   *   * Results: - *	The return value is one of the return codes defined in tcl.h - *	(such as TCL_OK), and interp->objResultPtr refers to a Tcl object - *	that either contains the result of executing the code or an - *	error message. + *	None + * + * Side effects: + *	May register information about the bytecode in the command frame. + * + *---------------------------------------------------------------------- + */ + +static void +ArgumentBCEnter( +    Tcl_Interp *interp, +    ByteCode *codePtr, +    TEBCdata *tdPtr, +    const unsigned char *pc, +    int objc, +    Tcl_Obj **objv) +{ +    int cmd; + +    if (GetSrcInfoForPc(pc, codePtr, NULL, NULL, &cmd)) { +	TclArgumentBCEnter(interp, objv, objc, codePtr, &tdPtr->cmdFrame, cmd, +		pc - codePtr->codeStart); +    } +} + +/* + *---------------------------------------------------------------------- + * + * TclNRExecuteByteCode -- + * + *	This procedure executes the instructions of a ByteCode structure. It + *	returns when a "done" instruction is executed or an error occurs. + * + * Results: + *	The return value is one of the return codes defined in tcl.h (such as + *	TCL_OK), and interp->objResultPtr refers to a Tcl object that either + *	contains the result of executing the code or an error message.   *   * Side effects:   *	Almost certainly, depending on the ByteCode's instructions.   *   *----------------------------------------------------------------------   */ +#define	bcFramePtr	(&TD->cmdFrame) +#define	initCatchTop	((ptrdiff_t *) (&TD->stack[-1])) +#define	initTosPtr	((Tcl_Obj **) (initCatchTop+codePtr->maxExceptDepth)) +#define esPtr		(iPtr->execEnvPtr->execStackPtr)  int -TclExecuteByteCode(interp, codePtr) -    Tcl_Interp *interp;		/* Token for command interpreter. */ -    ByteCode *codePtr;		/* The bytecode sequence to interpret. */ +TclNRExecuteByteCode( +    Tcl_Interp *interp,		/* Token for command interpreter. */ +    ByteCode *codePtr)		/* The bytecode sequence to interpret. */  {      Interp *iPtr = (Interp *) interp; -    ExecEnv *eePtr = iPtr->execEnvPtr; -    				/* Points to the execution environment. */ -    register Tcl_Obj **stackPtr = eePtr->stackPtr; -    				/* Cached evaluation stack base pointer. */ -    register int stackTop = eePtr->stackTop; -    				/* Cached top index of evaluation stack. */ -    register unsigned char *pc = codePtr->codeStart; -				/* The current program counter. */ -    int opnd;			/* Current instruction's operand byte. */ -    int pcAdjustment;		/* Hold pc adjustment after instruction. */ -    int initStackTop = stackTop;/* Stack top at start of execution. */ -    ExceptionRange *rangePtr;	/* Points to closest loop or catch exception -				 * range enclosing the pc. Used by various -				 * instructions and processCatch to -				 * process break, continue, and errors. */ -    int result = TCL_OK;	/* Return code returned after execution. */ -    int traceInstructions = (tclTraceExec == 3); -    Tcl_Obj *valuePtr, *value2Ptr, *objPtr, *elemPtr; -    char *bytes; -    int length; -    long i; +    TEBCdata *TD; +    int size = sizeof(TEBCdata) - 1 +	    + (codePtr->maxStackDepth + codePtr->maxExceptDepth) +		* sizeof(void *); +    int numWords = (size + sizeof(Tcl_Obj *) - 1) / sizeof(Tcl_Obj *); + +    codePtr->refCount++; + +    /* +     * Reserve the stack, setup the TEBCdataPtr (TD) and CallFrame +     * +     * The execution uses a unified stack: first a TEBCdata, immediately +     * above it a CmdFrame, then the catch stack, then the execution stack. +     * +     * Make sure the catch stack is large enough to hold the maximum number of +     * catch commands that could ever be executing at the same time (this will +     * be no more than the exception range array's depth). Make sure the +     * execution stack is large enough to execute this ByteCode. +     */ + +    TD = (TEBCdata *) GrowEvaluationStack(iPtr->execEnvPtr, numWords, 0); +    esPtr->tosPtr = initTosPtr; + +    TD->codePtr     = codePtr; +    TD->catchTop    = initCatchTop; +    TD->auxObjList  = NULL; + +    /* +     * TIP #280: Initialize the frame. Do not push it yet: it will be pushed +     * every time that we call out from this TD, popped when we return to it. +     */ + +    bcFramePtr->type = ((codePtr->flags & TCL_BYTECODE_PRECOMPILED) +	    ? TCL_LOCATION_PREBC : TCL_LOCATION_BC); +    bcFramePtr->level = (iPtr->cmdFramePtr ? iPtr->cmdFramePtr->level+1 : 1); +    bcFramePtr->framePtr = iPtr->framePtr; +    bcFramePtr->nextPtr = iPtr->cmdFramePtr; +    bcFramePtr->nline = 0; +    bcFramePtr->line = NULL; +    bcFramePtr->litarg = NULL; +    bcFramePtr->data.tebc.codePtr = codePtr; +    bcFramePtr->data.tebc.pc = NULL; +    bcFramePtr->cmdObj = NULL; +    bcFramePtr->cmd = NULL; +    bcFramePtr->len = 0; + +#ifdef TCL_COMPILE_STATS +    iPtr->stats.numExecutions++; +#endif + +    /* +     * Push the callback for bytecode execution +     */ + +    TclNRAddCallback(interp, TEBCresume, TD, /* pc */ NULL, +	    /* cleanup */ INT2PTR(0), NULL); +    return TCL_OK; +} + +static int +TEBCresume( +    ClientData data[], +    Tcl_Interp *interp, +    int result) +{ +    /* +     * Compiler cast directive - not a real variable. +     *	   Interp *iPtr = (Interp *) interp; +     */ +#define iPtr ((Interp *) interp) + +    /* +     * Check just the read-traced/write-traced bit of a variable. +     */ + +#define ReadTraced(varPtr) ((varPtr)->flags & VAR_TRACED_READ) +#define WriteTraced(varPtr) ((varPtr)->flags & VAR_TRACED_WRITE) +#define UnsetTraced(varPtr) ((varPtr)->flags & VAR_TRACED_UNSET) + +    /* +     * Bottom of allocated stack holds the NR data +     */      /* -     * This procedure uses a stack to hold information about catch commands. -     * This information is the current operand stack top when starting to -     * execute the code for each catch command. It starts out with stack- -     * allocated space but uses dynamically-allocated storage if needed. +     * Constants: variables that do not change during the execution, used +     * sporadically: no special need for speed.       */ -#define STATIC_CATCH_STACK_SIZE 4 -    int (catchStackStorage[STATIC_CATCH_STACK_SIZE]); -    int *catchStackPtr = catchStackStorage; -    int catchTop = -1; +    int instructionCount = 0;	/* Counter that is used to work out when to +				 * call Tcl_AsyncReady() */ +    const char *curInstName; +#ifdef TCL_COMPILE_DEBUG +    int traceInstructions;	/* Whether we are doing instruction-level +				 * tracing or not. */ +#endif + +    Var *compiledLocals = iPtr->varFramePtr->compiledLocals; +    Tcl_Obj **constants = &iPtr->execEnvPtr->constants[0]; + +#define LOCAL(i)	(&compiledLocals[(i)]) +#define TCONST(i)	(constants[(i)]) + +    /* +     * These macros are just meant to save some global variables that are not +     * used too frequently +     */ + +    TEBCdata *TD = data[0]; +#define auxObjList	(TD->auxObjList) +#define catchTop	(TD->catchTop) +#define codePtr		(TD->codePtr) + +    /* +     * Globals: variables that store state, must remain valid at all times. +     */ + +    Tcl_Obj **tosPtr;		/* Cached pointer to top of evaluation +				 * stack. */ +    const unsigned char *pc = data[1]; +                                /* The current program counter. */ +    unsigned char inst;         /* The currently running instruction */ +     +    /* +     * Transfer variables - needed only between opcodes, but not while +     * executing an instruction. +     */ + +    int cleanup = PTR2INT(data[2]); +    Tcl_Obj *objResultPtr; +    int checkInterp;            /* Indicates when a check of interp readyness +				 * is necessary. Set by CACHE_STACK_INFO() */ + +    /* +     * Locals - variables that are used within opcodes or bounded sections of +     * the file (jumps between opcodes within a family). +     * NOTE: These are now mostly defined locally where needed. +     */ + +    Tcl_Obj *objPtr, *valuePtr, *value2Ptr, *part1Ptr, *part2Ptr, *tmpPtr; +    Tcl_Obj **objv; +    int objc = 0; +    int opnd, length, pcAdjustment; +    Var *varPtr, *arrayPtr; +#ifdef TCL_COMPILE_DEBUG +    char cmdNameBuf[21]; +#endif  #ifdef TCL_COMPILE_DEBUG -    if (tclTraceExec >= 2) { +    int starting = 1; +    traceInstructions = (tclTraceExec == 3); +#endif + +    TEBC_DATA_DIG(); + +#ifdef TCL_COMPILE_DEBUG +    if (!pc && (tclTraceExec >= 2)) {  	PrintByteCodeInfo(codePtr); -	fprintf(stdout, "  Starting stack top=%d\n", eePtr->stackTop); +	fprintf(stdout, "  Starting stack top=%d\n", (int) CURR_DEPTH);  	fflush(stdout);      }  #endif -     -#ifdef TCL_COMPILE_STATS -    iPtr->stats.numExecutions++; + +    if (!pc) { +	/* bytecode is starting from scratch */ +	checkInterp = 0; +	pc = codePtr->codeStart; +	goto cleanup0; +    } else { +        /* resume from invocation */ +	CACHE_STACK_INFO(); +	if (iPtr->execEnvPtr->rewind) { +	    result = TCL_ERROR; +	    goto abnormalReturn; +	} + +	NRE_ASSERT(iPtr->cmdFramePtr == bcFramePtr); +	if (bcFramePtr->cmdObj) { +	    Tcl_DecrRefCount(bcFramePtr->cmdObj); +	    bcFramePtr->cmdObj = NULL; +	    bcFramePtr->cmd = NULL; +	} +	iPtr->cmdFramePtr = bcFramePtr->nextPtr; +	if (iPtr->flags & INTERP_DEBUG_FRAME) { +	    TclArgumentBCRelease(interp, bcFramePtr); +	} +	if (codePtr->flags & TCL_BYTECODE_RECOMPILE) { +	    iPtr->flags |= ERR_ALREADY_LOGGED; +	    codePtr->flags &= ~TCL_BYTECODE_RECOMPILE; +	} + +	if (result != TCL_OK) { +	    pc--; +	    goto processExceptionReturn; +	} + +	/* +	 * Push the call's object result and continue execution with the next +	 * instruction. +	 */ + +	TRACE_WITH_OBJ(("%u => ... after \"%.20s\": TCL_OK, result=", +		objc, cmdNameBuf), Tcl_GetObjResult(interp)); + +	/* +	 * Reset the interp's result to avoid possible duplications of large +	 * objects [Bug 781585]. We do not call Tcl_ResetResult to avoid any +	 * side effects caused by the resetting of errorInfo and errorCode +	 * [Bug 804681], which are not needed here. We chose instead to +	 * manipulate the interp's object result directly. +	 * +	 * Note that the result object is now in objResultPtr, it keeps the +	 * refCount it had in its role of iPtr->objResultPtr. +	 */ + +	objResultPtr = Tcl_GetObjResult(interp); +	TclNewObj(objPtr); +	Tcl_IncrRefCount(objPtr); +	iPtr->objResultPtr = objPtr; +#ifndef TCL_COMPILE_DEBUG +	if (*pc == INST_POP) { +	    TclDecrRefCount(objResultPtr); +	    NEXT_INST_V(1, cleanup, 0); +	}  #endif +	NEXT_INST_V(0, cleanup, -1); +    }      /* -     * Make sure the catch stack is large enough to hold the maximum number -     * of catch commands that could ever be executing at the same time. This -     * will be no more than the exception range array's depth. +     * Targets for standard instruction endings; unrolled for speed in the +     * most frequent cases (instructions that consume up to two stack +     * elements). +     * +     * This used to be a "for(;;)" loop, with each instruction doing its own +     * cleanup.       */ -    if (codePtr->maxExceptDepth > STATIC_CATCH_STACK_SIZE) { -	catchStackPtr = (int *) -	        ckalloc(codePtr->maxExceptDepth * sizeof(int)); +  cleanupV_pushObjResultPtr: +    switch (cleanup) { +    case 0: +	*(++tosPtr) = (objResultPtr); +	goto cleanup0; +    default: +	cleanup -= 2; +	while (cleanup--) { +	    objPtr = POP_OBJECT(); +	    TclDecrRefCount(objPtr); +	} +    case 2: +    cleanup2_pushObjResultPtr: +	objPtr = POP_OBJECT(); +	TclDecrRefCount(objPtr); +    case 1: +    cleanup1_pushObjResultPtr: +	objPtr = OBJ_AT_TOS; +	TclDecrRefCount(objPtr); +    } +    OBJ_AT_TOS = objResultPtr; +    goto cleanup0; + +  cleanupV: +    switch (cleanup) { +    default: +	cleanup -= 2; +	while (cleanup--) { +	    objPtr = POP_OBJECT(); +	    TclDecrRefCount(objPtr); +	} +    case 2: +    cleanup2: +	objPtr = POP_OBJECT(); +	TclDecrRefCount(objPtr); +    case 1: +    cleanup1: +	objPtr = POP_OBJECT(); +	TclDecrRefCount(objPtr); +    case 0: +	/* +	 * We really want to do nothing now, but this is needed for some +	 * compilers (SunPro CC). +	 */ + +	break;      } +  cleanup0:      /* -     * Make sure the stack has enough room to execute this ByteCode. +     * Check for asynchronous handlers [Bug 746722]; we do the check every +     * ASYNC_CHECK_COUNT_MASK instruction, of the form (2**n-1).       */ -    while ((stackTop + codePtr->maxStackDepth) > eePtr->stackEnd) { -        GrowEvaluationStack(eePtr);  -        stackPtr = eePtr->stackPtr; +    if ((instructionCount++ & ASYNC_CHECK_COUNT_MASK) == 0) { +	DECACHE_STACK_INFO(); +	if (TclAsyncReady(iPtr)) { +	    result = Tcl_AsyncInvoke(interp, result); +	    if (result == TCL_ERROR) { +		CACHE_STACK_INFO(); +		goto gotError; +	    } +	} + +	if (TclCanceled(iPtr)) { +	    if (Tcl_Canceled(interp, TCL_LEAVE_ERR_MSG) == TCL_ERROR) { +		CACHE_STACK_INFO(); +		goto gotError; +	    } +	} + +	if (TclLimitReady(iPtr->limit)) { +	    if (Tcl_LimitCheck(interp) == TCL_ERROR) { +		CACHE_STACK_INFO(); +		goto gotError; +	    } +	} +	CACHE_STACK_INFO();      }      /* -     * Loop executing instructions until a "done" instruction, a TCL_RETURN, -     * or some error. +     * These two instructions account for 26% of all instructions (according +     * to measurements on tclbench by Ben Vitale +     * [http://www.cs.toronto.edu/syslab/pubs/tcl2005-vitale-zaleski.pdf] +     * Resolving them before the switch reduces the cost of branch +     * mispredictions, seems to improve runtime by 5% to 15%, and (amazingly!) +     * reduces total obj size.       */ -    for (;;) { +    inst = *pc; +     +    peepholeStart: +#ifdef TCL_COMPILE_STATS +    iPtr->stats.instructionCount[*pc]++; +#endif +  #ifdef TCL_COMPILE_DEBUG -	ValidatePcAndStackTop(codePtr, pc, stackTop, initStackTop, -		eePtr->stackEnd); -#else /* not TCL_COMPILE_DEBUG */ -        if (traceInstructions) { -            fprintf(stdout, "%2d: %2d ", iPtr->numLevels, stackTop); -            TclPrintInstruction(codePtr, pc); -            fflush(stdout); -        } +    /* +     * Skip the stack depth check if an expansion is in progress. +     */ + +    CHECK_STACK(); +    if (traceInstructions) { +	fprintf(stdout, "%2d: %2d ", iPtr->numLevels, (int) CURR_DEPTH); +	TclPrintInstruction(codePtr, pc); +	fflush(stdout); +    }  #endif /* TCL_COMPILE_DEBUG */ + +    TCL_DTRACE_INST_NEXT(); +     +    if (inst == INST_LOAD_SCALAR1) { +	goto instLoadScalar1; +    } else if (inst == INST_PUSH1) { +	PUSH_OBJECT(codePtr->objArrayPtr[TclGetUInt1AtPtr(pc+1)]); +	TRACE_WITH_OBJ(("%u => ", TclGetUInt1AtPtr(pc+1)), OBJ_AT_TOS); +	inst = *(pc += 2); +	goto peepholeStart; +    } else if (inst == INST_START_CMD) { +	/* +	 * Peephole: do not run INST_START_CMD, just skip it +	 */ -#ifdef TCL_COMPILE_STATS     -	iPtr->stats.instructionCount[*pc]++; +	iPtr->cmdCount += TclGetUInt4AtPtr(pc+5); +	if (checkInterp) { +	    checkInterp = 0; +	    if (((codePtr->compileEpoch != iPtr->compileEpoch) || +		 (codePtr->nsEpoch != iPtr->varFramePtr->nsPtr->resolverEpoch)) && +		!(codePtr->flags & TCL_BYTECODE_PRECOMPILED)) { +		goto instStartCmdFailed; +	    } +	} +	inst = *(pc += 9); +	goto peepholeStart; +    } else if (inst == INST_NOP) { +#ifndef TCL_COMPILE_DEBUG +	while (inst == INST_NOP)  #endif -        switch (*pc) { -	case INST_DONE: +	{ +	    inst = *++pc; +	} +	goto peepholeStart; +    } +     +    switch (inst) { +    case INST_SYNTAX: +    case INST_RETURN_IMM: { +	int code = TclGetInt4AtPtr(pc+1); +	int level = TclGetUInt4AtPtr(pc+5); + +	/* +	 * OBJ_AT_TOS is returnOpts, OBJ_UNDER_TOS is resultObjPtr. +	 */ + +	TRACE(("%u %u => ", code, level)); +	result = TclProcessReturn(interp, code, level, OBJ_AT_TOS); +	if (result == TCL_OK) { +	    TRACE_APPEND(("continuing to next instruction (result=\"%.30s\")\n", +		    O2S(objResultPtr))); +	    NEXT_INST_F(9, 1, 0); +	} +	Tcl_SetObjResult(interp, OBJ_UNDER_TOS); +	if (*pc == INST_SYNTAX) { +	    iPtr->flags &= ~ERR_ALREADY_LOGGED; +	} +	cleanup = 2; +	TRACE_APPEND(("\n")); +	goto processExceptionReturn; +    } + +    case INST_RETURN_STK: +	TRACE(("=> ")); +	objResultPtr = POP_OBJECT(); +	result = Tcl_SetReturnOptions(interp, OBJ_AT_TOS); +	if (result == TCL_OK) { +	    Tcl_DecrRefCount(OBJ_AT_TOS); +	    OBJ_AT_TOS = objResultPtr; +	    TRACE_APPEND(("continuing to next instruction (result=\"%.30s\")\n", +		    O2S(objResultPtr))); +	    NEXT_INST_F(1, 0, 0); +	} else if (result == TCL_ERROR) {  	    /* -	     * Pop the topmost object from the stack, set the interpreter's -	     * object result to point to it, and return. +	     * BEWARE! Must do this in this order, because an error in the +	     * option dictionary overrides the result (and can be verified by +	     * test).  	     */ -	    valuePtr = POP_OBJECT(); -	    Tcl_SetObjResult(interp, valuePtr); -	    TclDecrRefCount(valuePtr); -	    if (stackTop != initStackTop) { -		/* -		 * if extra items in the stack, clean up the stack before return -		 */ -		if (stackTop > initStackTop) goto abnormalReturn; -		fprintf(stderr, "\nTclExecuteByteCode: done instruction at pc %u: stack top %d < entry stack top %d\n", -			(unsigned int)(pc - codePtr->codeStart), -			(unsigned int) stackTop, -			(unsigned int) initStackTop); -		panic("TclExecuteByteCode execution failure: end stack top < start stack top"); + +	    Tcl_SetObjResult(interp, objResultPtr); +	    Tcl_SetReturnOptions(interp, OBJ_AT_TOS); +	    Tcl_DecrRefCount(OBJ_AT_TOS); +	    OBJ_AT_TOS = objResultPtr; +	} else { +	    Tcl_DecrRefCount(OBJ_AT_TOS); +	    OBJ_AT_TOS = objResultPtr; +	    Tcl_SetObjResult(interp, objResultPtr); +	} +	cleanup = 1; +	TRACE_APPEND(("\n")); +	goto processExceptionReturn; + +    { +	CoroutineData *corPtr; +	int yieldParameter; + +    case INST_YIELD: +	corPtr = iPtr->execEnvPtr->corPtr; +	TRACE(("%.30s => ", O2S(OBJ_AT_TOS))); +	if (!corPtr) { +	    TRACE_APPEND(("ERROR: yield outside coroutine\n")); +	    Tcl_SetObjResult(interp, Tcl_NewStringObj( +		    "yield can only be called in a coroutine", -1)); +	    DECACHE_STACK_INFO(); +	    Tcl_SetErrorCode(interp, "TCL", "COROUTINE", "ILLEGAL_YIELD", +		    NULL); +	    CACHE_STACK_INFO(); +	    goto gotError; +	} + +#ifdef TCL_COMPILE_DEBUG +	if (tclTraceExec >= 2) { +	    if (traceInstructions) { +		TRACE_APPEND(("YIELD...\n")); +	    } else { +		fprintf(stdout, "%d: (%u) yielding value \"%.30s\"\n", +			iPtr->numLevels, (unsigned)(pc - codePtr->codeStart), +			Tcl_GetString(OBJ_AT_TOS)); +	    } +	    fflush(stdout); +	} +#endif +	yieldParameter = 0; +	Tcl_SetObjResult(interp, OBJ_AT_TOS); +	goto doYield; + +    case INST_YIELD_TO_INVOKE: +	corPtr = iPtr->execEnvPtr->corPtr; +	valuePtr = OBJ_AT_TOS; +	if (!corPtr) { +	    TRACE(("[%.30s] => ERROR: yield outside coroutine\n", +		    O2S(valuePtr))); +	    Tcl_SetObjResult(interp, Tcl_NewStringObj( +		    "yieldto can only be called in a coroutine", -1)); +	    DECACHE_STACK_INFO(); +	    Tcl_SetErrorCode(interp, "TCL", "COROUTINE", "ILLEGAL_YIELD", +		    NULL); +	    CACHE_STACK_INFO(); +	    goto gotError; +	} +	if (((Namespace *)TclGetCurrentNamespace(interp))->flags & NS_DYING) { +	    TRACE(("[%.30s] => ERROR: yield in deleted\n", +		    O2S(valuePtr))); +	    Tcl_SetObjResult(interp, Tcl_NewStringObj( +		    "yieldto called in deleted namespace", -1)); +	    DECACHE_STACK_INFO(); +	    Tcl_SetErrorCode(interp, "TCL", "COROUTINE", "YIELDTO_IN_DELETED", +		    NULL); +	    CACHE_STACK_INFO(); +	    goto gotError; +	} + +#ifdef TCL_COMPILE_DEBUG +	if (tclTraceExec >= 2) { +	    if (traceInstructions) { +		TRACE(("[%.30s] => YIELD...\n", O2S(valuePtr))); +	    } else { +		/* FIXME: What is the right thing to trace? */ +		fprintf(stdout, "%d: (%u) yielding to [%.30s]\n", +			iPtr->numLevels, (unsigned)(pc - codePtr->codeStart), +			Tcl_GetString(valuePtr));  	    } +	    fflush(stdout); +	} +#endif + +	/* +	 * Install a tailcall record in the caller and continue with the +	 * yield. The yield is switched into multi-return mode (via the +	 * 'yieldParameter'). +	 */ + +	Tcl_IncrRefCount(valuePtr); +	iPtr->execEnvPtr = corPtr->callerEEPtr; +	TclSetTailcall(interp, valuePtr); +	iPtr->execEnvPtr = corPtr->eePtr; +	yieldParameter = (PTR2INT(NULL)+1);	/*==CORO_ACTIVATE_YIELDM*/ + +    doYield: +	/* TIP #280: Record the last piece of info needed by +	 * 'TclGetSrcInfoForPc', and push the frame. +	 */ +	 +	bcFramePtr->data.tebc.pc = (char *) pc; +	iPtr->cmdFramePtr = bcFramePtr; + +	if (iPtr->flags & INTERP_DEBUG_FRAME) { +	    ArgumentBCEnter(interp, codePtr, TD, pc, objc, objv); +	} + +	pc++; +	cleanup = 1; +	TEBC_YIELD(); +	TclNRAddCallback(interp, TclNRCoroutineActivateCallback, corPtr, +		INT2PTR(yieldParameter), NULL, NULL); +	return TCL_OK; +    } + +    case INST_TAILCALL: { +	Tcl_Obj *listPtr, *nsObjPtr; + +	opnd = TclGetUInt1AtPtr(pc+1); + +	if (!(iPtr->varFramePtr->isProcCallFrame & 1)) { +	    TRACE(("%d => ERROR: tailcall in non-proc context\n", opnd)); +	    Tcl_SetObjResult(interp, Tcl_NewStringObj( +		    "tailcall can only be called from a proc or lambda", -1)); +	    DECACHE_STACK_INFO(); +	    Tcl_SetErrorCode(interp, "TCL", "TAILCALL", "ILLEGAL", NULL); +	    CACHE_STACK_INFO(); +	    goto gotError; +	} + +#ifdef TCL_COMPILE_DEBUG +	/* FIXME: What is the right thing to trace? */ +	{ +	    register int i; + +	    TRACE(("%d [", opnd)); +	    for (i=opnd-1 ; i>=0 ; i--) { +		TRACE_APPEND(("\"%.30s\"", O2S(OBJ_AT_DEPTH(i)))); +		if (i > 0) { +		    TRACE_APPEND((" ")); +		} +	    } +	    TRACE_APPEND(("] => RETURN...")); +	} +#endif + +	/* +	 * Push the evaluation of the called command into the NR callback +	 * stack. +	 */ + +	listPtr = Tcl_NewListObj(opnd, &OBJ_AT_DEPTH(opnd-1)); +	nsObjPtr = Tcl_NewStringObj(iPtr->varFramePtr->nsPtr->fullName, -1); +	TclListObjSetElement(interp, listPtr, 0, nsObjPtr); +	if (iPtr->varFramePtr->tailcallPtr) { +	    Tcl_DecrRefCount(iPtr->varFramePtr->tailcallPtr); +	} +	iPtr->varFramePtr->tailcallPtr = listPtr; + +	result = TCL_RETURN; +	cleanup = opnd; +	goto processExceptionReturn; +    } + +    case INST_DONE: +	if (tosPtr > initTosPtr) { +	    /* +	     * Set the interpreter's object result to point to the topmost +	     * object from the stack, and check for a possible [catch]. The +	     * stackTop's level and refCount will be handled by "processCatch" +	     * or "abnormalReturn". +	     */ + +	    Tcl_SetObjResult(interp, OBJ_AT_TOS); +#ifdef TCL_COMPILE_DEBUG  	    TRACE_WITH_OBJ(("=> return code=%d, result=", result),  		    iPtr->objResultPtr); -#ifdef TCL_COMPILE_DEBUG	      	    if (traceInstructions) {  		fprintf(stdout, "\n");  	    }  #endif -	    goto done; +	    goto checkForCatch; +	} +	(void) POP_OBJECT(); +	goto abnormalReturn; + +    case INST_PUSH4: +	objResultPtr = codePtr->objArrayPtr[TclGetUInt4AtPtr(pc+1)]; +	TRACE_WITH_OBJ(("%u => ", TclGetUInt4AtPtr(pc+1)), objResultPtr); +	NEXT_INST_F(5, 0, 1); + +    case INST_POP: +	TRACE_WITH_OBJ(("=> discarding "), OBJ_AT_TOS); +	objPtr = POP_OBJECT(); +	TclDecrRefCount(objPtr); +	NEXT_INST_F(1, 0, 0); + +    case INST_DUP: +	objResultPtr = OBJ_AT_TOS; +	TRACE_WITH_OBJ(("=> "), objResultPtr); +	NEXT_INST_F(1, 0, 1); + +    case INST_OVER: +	opnd = TclGetUInt4AtPtr(pc+1); +	objResultPtr = OBJ_AT_DEPTH(opnd); +	TRACE_WITH_OBJ(("%u => ", opnd), objResultPtr); +	NEXT_INST_F(5, 0, 1); + +    case INST_REVERSE: { +	Tcl_Obj **a, **b; + +	opnd = TclGetUInt4AtPtr(pc+1); +	a = tosPtr-(opnd-1); +	b = tosPtr; +	while (a<b) { +	    tmpPtr = *a; +	    *a = *b; +	    *b = tmpPtr; +	    a++; b--; +	} +	TRACE(("%u => OK\n", opnd)); +	NEXT_INST_F(5, 0, 0); +    } -	case INST_PUSH1: -#ifdef TCL_COMPILE_DEBUG -	    valuePtr = codePtr->objArrayPtr[TclGetUInt1AtPtr(pc+1)]; -	    PUSH_OBJECT(valuePtr); -	    TRACE_WITH_OBJ(("%u => ", TclGetInt1AtPtr(pc+1)), valuePtr); -#else -	    PUSH_OBJECT(codePtr->objArrayPtr[TclGetUInt1AtPtr(pc+1)]); -#endif /* TCL_COMPILE_DEBUG */ -	    ADJUST_PC(2); +    case INST_STR_CONCAT1: { +	int appendLen = 0; +	char *bytes, *p; +	Tcl_Obj **currPtr; +	int onlyb = 1; -	case INST_PUSH4: -	    valuePtr = codePtr->objArrayPtr[TclGetUInt4AtPtr(pc+1)]; -	    PUSH_OBJECT(valuePtr); -	    TRACE_WITH_OBJ(("%u => ", TclGetUInt4AtPtr(pc+1)), valuePtr); -	    ADJUST_PC(5); +	opnd = TclGetUInt1AtPtr(pc+1); -	case INST_POP: -	    valuePtr = POP_OBJECT(); -	    TRACE_WITH_OBJ(("=> discarding "), valuePtr); -	    TclDecrRefCount(valuePtr); /* finished with pop'ed object. */ -	    ADJUST_PC(1); +	/* +	 * Detect only-bytearray-or-null case. +	 */ -	case INST_DUP: -	    valuePtr = stackPtr[stackTop]; -	    PUSH_OBJECT(valuePtr); -	    TRACE_WITH_OBJ(("=> "), valuePtr); -	    ADJUST_PC(1); +	for (currPtr=&OBJ_AT_DEPTH(opnd-1); currPtr<=&OBJ_AT_TOS; currPtr++) { +	    if (((*currPtr)->typePtr != &tclByteArrayType) +		    && ((*currPtr)->bytes != tclEmptyStringRep)) { +		onlyb = 0; +		break; +	    } else if (((*currPtr)->typePtr == &tclByteArrayType) && +		    ((*currPtr)->bytes != NULL)) { +		onlyb = 0; +		break; +	    } +	} -	case INST_CONCAT1: -	    opnd = TclGetUInt1AtPtr(pc+1); +	/* +	 * Compute the length to be appended. +	 */ + +	if (onlyb) { +	    for (currPtr = &OBJ_AT_DEPTH(opnd-2); +		    appendLen >= 0 && currPtr <= &OBJ_AT_TOS; currPtr++) { +		if ((*currPtr)->bytes != tclEmptyStringRep) { +		    Tcl_GetByteArrayFromObj(*currPtr, &length); +		    appendLen += length; +		} +	    } +	} else { +	    for (currPtr = &OBJ_AT_DEPTH(opnd-2); +		    appendLen >= 0 && currPtr <= &OBJ_AT_TOS; currPtr++) { +		bytes = TclGetStringFromObj(*currPtr, &length); +		if (bytes != NULL) { +		    appendLen += length; +		} +	    } +	} + +	if (appendLen < 0) { +	    /* TODO: convert panic to error ? */ +	    Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX); +	} + +	/* +	 * If nothing is to be appended, just return the first object by +	 * dropping all the others from the stack; this saves both the +	 * computation and copy of the string rep of the first object, +	 * enabling the fast '$x[set x {}]' idiom for 'K $x [set x {}]'. +	 */ + +	if (appendLen == 0) { +	    TRACE_WITH_OBJ(("%u => ", opnd), objResultPtr); +	    NEXT_INST_V(2, (opnd-1), 0); +	} + +	/* +	 * If the first object is shared, we need a new obj for the result; +	 * otherwise, we can reuse the first object. In any case, make sure it +	 * has enough room to accomodate all the concatenated bytes. Note that +	 * if it is unshared its bytes are copied by ckrealloc, so that we set +	 * the loop parameters to avoid copying them again: p points to the +	 * end of the already copied bytes, currPtr to the second object. +	 */ + +	objResultPtr = OBJ_AT_DEPTH(opnd-1); +	if (!onlyb) { +	    bytes = TclGetStringFromObj(objResultPtr, &length); +	    if (length + appendLen < 0) { +		/* TODO: convert panic to error ? */ +		Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", +			INT_MAX); +	    } +#ifndef TCL_COMPILE_DEBUG +	    if (bytes != tclEmptyStringRep && !Tcl_IsShared(objResultPtr)) { +		TclFreeIntRep(objResultPtr); +		objResultPtr->bytes = ckrealloc(bytes, length+appendLen+1); +		objResultPtr->length = length + appendLen; +		p = TclGetString(objResultPtr) + length; +		currPtr = &OBJ_AT_DEPTH(opnd - 2); +	    } else +#endif  	    { -		Tcl_Obj *concatObjPtr; -		int totalLen = 0; +		p = ckalloc(length + appendLen + 1); +		TclNewObj(objResultPtr); +		objResultPtr->bytes = p; +		objResultPtr->length = length + appendLen; +		currPtr = &OBJ_AT_DEPTH(opnd - 1); +	    } -		/* -		 * Concatenate strings (with no separators) from the top -		 * opnd items on the stack starting with the deepest item. -		 * First, determine how many characters are needed. -		 */ +	    /* +	     * Append the remaining characters. +	     */ -		for (i = (stackTop - (opnd-1));  i <= stackTop;  i++) { -		    bytes = Tcl_GetStringFromObj(stackPtr[i], &length); -		    if (bytes != NULL) { -			totalLen += length; -		    } -                } +	    for (; currPtr <= &OBJ_AT_TOS; currPtr++) { +		bytes = TclGetStringFromObj(*currPtr, &length); +		if (bytes != NULL) { +		    memcpy(p, bytes, (size_t) length); +		    p += length; +		} +	    } +	    *p = '\0'; +	} else { +	    bytes = (char *) Tcl_GetByteArrayFromObj(objResultPtr, &length); +	    if (length + appendLen < 0) { +		/* TODO: convert panic to error ? */ +		Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", +			INT_MAX); +	    } +#ifndef TCL_COMPILE_DEBUG +	    if (!Tcl_IsShared(objResultPtr)) { +		bytes = (char *) Tcl_SetByteArrayLength(objResultPtr, +			length + appendLen); +		p = bytes + length; +		currPtr = &OBJ_AT_DEPTH(opnd - 2); +	    } else +#endif +	    { +		TclNewObj(objResultPtr); +		bytes = (char *) Tcl_SetByteArrayLength(objResultPtr, +			length + appendLen); +		p = bytes; +		currPtr = &OBJ_AT_DEPTH(opnd - 1); +	    } -		/* -		 * Initialize the new append string object by appending the -		 * strings of the opnd stack objects. Also pop the objects.  -		 */ +	    /* +	     * Append the remaining characters. +	     */ -		TclNewObj(concatObjPtr); -		if (totalLen > 0) { -		    char *p = (char *) ckalloc((unsigned) (totalLen + 1)); -		    concatObjPtr->bytes = p; -		    concatObjPtr->length = totalLen; -		    for (i = (stackTop - (opnd-1));  i <= stackTop;  i++) { -			valuePtr = stackPtr[i]; -			bytes = Tcl_GetStringFromObj(valuePtr, &length); -			if (bytes != NULL) { -			    memcpy((VOID *) p, (VOID *) bytes, -			            (size_t) length); -			    p += length; -			} -			TclDecrRefCount(valuePtr); -		    } -		    *p = '\0'; -		} else { -		    for (i = (stackTop - (opnd-1));  i <= stackTop;  i++) { -			Tcl_DecrRefCount(stackPtr[i]); -		    } +	    for (; currPtr <= &OBJ_AT_TOS; currPtr++) { +		if ((*currPtr)->bytes != tclEmptyStringRep) { +		    bytes = (char *) Tcl_GetByteArrayFromObj(*currPtr,&length); +		    memcpy(p, bytes, (size_t) length); +		    p += length;  		} -		stackTop -= opnd; -		 -		PUSH_OBJECT(concatObjPtr); -		TRACE_WITH_OBJ(("%u => ", opnd), concatObjPtr); -		ADJUST_PC(2); -            } -	     -	case INST_INVOKE_STK4: -	    opnd = TclGetUInt4AtPtr(pc+1); -	    pcAdjustment = 5; -	    goto doInvocation; +	    } +	} -	case INST_INVOKE_STK1: -	    opnd = TclGetUInt1AtPtr(pc+1); -	    pcAdjustment = 2; -	     -	    doInvocation: -	    { -		int objc = opnd; /* The number of arguments. */ -		Tcl_Obj **objv;	 /* The array of argument objects. */ -		Command *cmdPtr; /* Points to command's Command struct. */ -		int newPcOffset; /* New inst offset for break, continue. */ -		Tcl_Obj **preservedStack; -				 /* Reference to memory block containing -				  * objv array (must be kept live throughout -				  * trace and command invokations.) */ +	TRACE_WITH_OBJ(("%u => ", opnd), objResultPtr); +	NEXT_INST_V(2, opnd, 1); +    } + +    case INST_CONCAT_STK: +	/* +	 * Pop the opnd (objc) top stack elements, run through Tcl_ConcatObj, +	 * and then decrement their ref counts. +	 */ + +	opnd = TclGetUInt4AtPtr(pc+1); +	objResultPtr = Tcl_ConcatObj(opnd, &OBJ_AT_DEPTH(opnd-1)); +	TRACE_WITH_OBJ(("%u => ", opnd), objResultPtr); +	NEXT_INST_V(5, opnd, 1); + +    case INST_EXPAND_START: +	/* +	 * Push an element to the auxObjList. This records the current +	 * stack depth - i.e., the point in the stack where the expanded +	 * command starts. +	 * +	 * Use a Tcl_Obj as linked list element; slight mem waste, but faster +	 * allocation than ckalloc. This also abuses the Tcl_Obj structure, as +	 * we do not define a special tclObjType for it. It is not dangerous +	 * as the obj is never passed anywhere, so that all manipulations are +	 * performed here and in INST_INVOKE_EXPANDED (in case of an expansion +	 * error, also in INST_EXPAND_STKTOP). +	 */ + +	TclNewObj(objPtr); +	objPtr->internalRep.ptrAndLongRep.value = CURR_DEPTH; +	objPtr->length = 0; +	PUSH_TAUX_OBJ(objPtr); +	TRACE(("=> mark depth as %d\n", (int) CURR_DEPTH)); +	NEXT_INST_F(1, 0, 0); + +    case INST_EXPAND_DROP: +	/* +	 * Drops an element of the auxObjList, popping stack elements to +	 * restore the stack to the state before the point where the aux +	 * element was created. +	 */ + +	CLANG_ASSERT(auxObjList); +	objc = CURR_DEPTH - auxObjList->internalRep.ptrAndLongRep.value; +	POP_TAUX_OBJ();  #ifdef TCL_COMPILE_DEBUG -		int isUnknownCmd = 0; -		char cmdNameBuf[21]; -#endif /* TCL_COMPILE_DEBUG */ +	/* Ugly abuse! */ +	starting = 1; +#endif +	TRACE(("=> drop %d items\n", objc)); +	NEXT_INST_V(1, objc, 0); -		/* -		 * If the interpreter was deleted, return an error. -		 */ +    case INST_EXPAND_STKTOP: { +	int i; +	ptrdiff_t moved; -		if (iPtr->flags & DELETED) { -		    Tcl_ResetResult(interp); -		    Tcl_AppendToObj(Tcl_GetObjResult(interp), -		            "attempt to call eval in deleted interpreter", -1); -		    Tcl_SetErrorCode(interp, "CORE", "IDELETE", -			    "attempt to call eval in deleted interpreter", -			    (char *) NULL); -		    result = TCL_ERROR; -		    goto checkForCatch; -		} +	/* +	 * Make sure that the element at stackTop is a list; if not, just +	 * leave with an error. Note that the element from the expand list +	 * will be removed at checkForCatch. +	 */ + +	objPtr = OBJ_AT_TOS; +	TRACE(("\"%.30s\" => ", O2S(objPtr))); +	if (TclListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} +	(void) POP_OBJECT(); +	/* +	 * Make sure there is enough room in the stack to expand this list +	 * *and* process the rest of the command (at least up to the next +	 * argument expansion or command end). The operand is the current +	 * stack depth, as seen by the compiler. +	 */ + +	auxObjList->length += objc - 1; +	if ((objc > 1) && (auxObjList->length > 0)) { +	    length = auxObjList->length /* Total expansion room we need */ +		    + codePtr->maxStackDepth /* Beyond the original max */ +		    - CURR_DEPTH;	/* Relative to where we are */ +	    DECACHE_STACK_INFO(); +	    moved = GrowEvaluationStack(iPtr->execEnvPtr, length, 1) +		    - (Tcl_Obj **) TD; +	    if (moved) {  		/* -		 * Find the procedure to execute this command. If the -		 * command is not found, handle it with the "unknown" proc. +		 * Change the global data to point to the new stack: move the +		 * TEBCdataPtr TD, recompute the position of every other +		 * stack-allocated parameter, update the stack pointers.  		 */ -		objv = &(stackPtr[stackTop - (objc-1)]); -		cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, objv[0]); -		if (cmdPtr == NULL) { -		    cmdPtr = (Command *) Tcl_FindCommand(interp, "unknown", -                            (Tcl_Namespace *) NULL, TCL_GLOBAL_ONLY); -                    if (cmdPtr == NULL) { -			Tcl_ResetResult(interp); -			Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -			        "invalid command name \"", -				Tcl_GetString(objv[0]), "\"", -				(char *) NULL); -			TRACE(("%u => unknown proc not found: ", objc)); -			result = TCL_ERROR; -			goto checkForCatch; -		    } +		esPtr = iPtr->execEnvPtr->execStackPtr; +		TD = (TEBCdata *) (((Tcl_Obj **)TD) + moved); + +		catchTop += moved; +		tosPtr += moved; +	    } +	} + +	/* +	 * Expand the list at stacktop onto the stack; free the list. Knowing +	 * that it has a freeIntRepProc we use Tcl_DecrRefCount(). +	 */ + +	for (i = 0; i < objc; i++) { +	    PUSH_OBJECT(objv[i]); +	} + +	TRACE_APPEND(("OK\n")); +	Tcl_DecrRefCount(objPtr); +	NEXT_INST_F(5, 0, 0); +    } + +    case INST_EXPR_STK: { +	ByteCode *newCodePtr; + +	bcFramePtr->data.tebc.pc = (char *) pc; +	iPtr->cmdFramePtr = bcFramePtr; +	DECACHE_STACK_INFO(); +	newCodePtr = CompileExprObj(interp, OBJ_AT_TOS); +	CACHE_STACK_INFO(); +	cleanup = 1; +	pc++; +	TEBC_YIELD(); +	return TclNRExecuteByteCode(interp, newCodePtr); +    } + +	/* +	 * INVOCATION BLOCK +	 */ + +    instEvalStk: +    case INST_EVAL_STK: +	bcFramePtr->data.tebc.pc = (char *) pc; +	iPtr->cmdFramePtr = bcFramePtr; + +	cleanup = 1; +	pc += 1; +	TEBC_YIELD(); +	return TclNREvalObjEx(interp, OBJ_AT_TOS, 0, NULL, 0); + +    case INST_INVOKE_EXPANDED: +	CLANG_ASSERT(auxObjList); +	objc = CURR_DEPTH - auxObjList->internalRep.ptrAndLongRep.value; +	POP_TAUX_OBJ(); +	if (objc) { +	    pcAdjustment = 1; +	    goto doInvocation; +	} + +	/* +	 * Nothing was expanded, return {}. +	 */ + +	TclNewObj(objResultPtr); +	NEXT_INST_F(1, 0, 1); + +    case INST_INVOKE_STK4: +	objc = TclGetUInt4AtPtr(pc+1); +	pcAdjustment = 5; +	goto doInvocation; + +    case INST_INVOKE_STK1: +	objc = TclGetUInt1AtPtr(pc+1); +	pcAdjustment = 2; + +    doInvocation: +	objv = &OBJ_AT_DEPTH(objc-1); +	cleanup = objc; +  #ifdef TCL_COMPILE_DEBUG -		    isUnknownCmd = 1; -#endif /*TCL_COMPILE_DEBUG*/			 -		    stackTop++; /* need room for new inserted objv[0] */ -		    for (i = objc-1;  i >= 0;  i--) { -			objv[i+1] = objv[i]; -		    } -		    objc++; -		    objv[0] = Tcl_NewStringObj("unknown", -1); -		    Tcl_IncrRefCount(objv[0]); -		} +	if (tclTraceExec >= 2) { +	    int i; -		/* -		 * A reference to part of the stack vector itself -		 * escapes our control, so must use preserve/release -		 * to stop it from being deallocated by a recursive -		 * call to ourselves.  The extra variable is needed -		 * because all others are liable to change due to the -		 * trace procedures. -		 */ +	    if (traceInstructions) { +		strncpy(cmdNameBuf, TclGetString(objv[0]), 20); +		TRACE(("%u => call ", objc)); +	    } else { +		fprintf(stdout, "%d: (%u) invoking ", iPtr->numLevels, +			(unsigned)(pc - codePtr->codeStart)); +	    } +	    for (i = 0;  i < objc;  i++) { +		TclPrintObject(stdout, objv[i], 15); +		fprintf(stdout, " "); +	    } +	    fprintf(stdout, "\n"); +	    fflush(stdout); +	} +#endif /*TCL_COMPILE_DEBUG*/ -		Tcl_Preserve((ClientData)stackPtr); -		preservedStack = stackPtr; +	/* +	 * Finally, let TclEvalObjv handle the command. +	 * +	 * TIP #280: Record the last piece of info needed by +	 * 'TclGetSrcInfoForPc', and push the frame. +	 */ -		/* -		 * Call any trace procedures. -		 */ +	bcFramePtr->data.tebc.pc = (char *) pc; +	iPtr->cmdFramePtr = bcFramePtr; -		if (iPtr->tracePtr != NULL) { -		    Trace *tracePtr, *nextTracePtr; -		     -		    for (tracePtr = iPtr->tracePtr;  tracePtr != NULL; -		            tracePtr = nextTracePtr) { -			nextTracePtr = tracePtr->nextPtr; -			if (iPtr->numLevels <= tracePtr->level) { -			    int numChars; -			    char *cmd = GetSrcInfoForPc(pc, codePtr, -				    &numChars); -			    if (cmd != NULL) { -				DECACHE_STACK_INFO(); -				CallTraceProcedure(interp, tracePtr, cmdPtr, -				        cmd, numChars, objc, objv); -				CACHE_STACK_INFO(); -			    } -			} -		    } +	if (iPtr->flags & INTERP_DEBUG_FRAME) { +	    ArgumentBCEnter(interp, codePtr, TD, pc, objc, objv); +	} + +	DECACHE_STACK_INFO(); + +	pc += pcAdjustment; +	TEBC_YIELD(); +	return TclNREvalObjv(interp, objc, objv, +		TCL_EVAL_NOERR | TCL_EVAL_SOURCE_IN_FRAME, NULL); + +#if TCL_SUPPORT_84_BYTECODE +    case INST_CALL_BUILTIN_FUNC1: +	/* +	 * Call one of the built-in pre-8.5 Tcl math functions. This +	 * translates to INST_INVOKE_STK1 with the first argument of +	 * ::tcl::mathfunc::$objv[0]. We need to insert the named math +	 * function into the stack. +	 */ + +	opnd = TclGetUInt1AtPtr(pc+1); +	if ((opnd < 0) || (opnd > LAST_BUILTIN_FUNC)) { +	    TRACE(("UNRECOGNIZED BUILTIN FUNC CODE %d\n", opnd)); +	    Tcl_Panic("TclNRExecuteByteCode: unrecognized builtin function code %d", opnd); +	} + +	TclNewLiteralStringObj(objPtr, "::tcl::mathfunc::"); +	Tcl_AppendToObj(objPtr, tclBuiltinFuncTable[opnd].name, -1); + +	/* +	 * Only 0, 1 or 2 args. +	 */ + +	{ +	    int numArgs = tclBuiltinFuncTable[opnd].numArgs; +	    Tcl_Obj *tmpPtr1, *tmpPtr2; + +	    if (numArgs == 0) { +		PUSH_OBJECT(objPtr); +	    } else if (numArgs == 1) { +		tmpPtr1 = POP_OBJECT(); +		PUSH_OBJECT(objPtr); +		PUSH_OBJECT(tmpPtr1); +		Tcl_DecrRefCount(tmpPtr1); +	    } else { +		tmpPtr2 = POP_OBJECT(); +		tmpPtr1 = POP_OBJECT(); +		PUSH_OBJECT(objPtr); +		PUSH_OBJECT(tmpPtr1); +		PUSH_OBJECT(tmpPtr2); +		Tcl_DecrRefCount(tmpPtr1); +		Tcl_DecrRefCount(tmpPtr2); +	    } +	    objc = numArgs + 1; +	} +	pcAdjustment = 2; +	goto doInvocation; + +    case INST_CALL_FUNC1: +	/* +	 * Call a non-builtin Tcl math function previously registered by a +	 * call to Tcl_CreateMathFunc pre-8.5. This is essentially +	 * INST_INVOKE_STK1 converting the first arg to +	 * ::tcl::mathfunc::$objv[0]. +	 */ + +	objc = TclGetUInt1AtPtr(pc+1);	/* Number of arguments. The function +					 * name is the 0-th argument. */ + +	objPtr = OBJ_AT_DEPTH(objc-1); +	TclNewLiteralStringObj(tmpPtr, "::tcl::mathfunc::"); +	Tcl_AppendObjToObj(tmpPtr, objPtr); +	Tcl_DecrRefCount(objPtr); + +	/* +	 * Variation of PUSH_OBJECT. +	 */ + +	OBJ_AT_DEPTH(objc-1) = tmpPtr; +	Tcl_IncrRefCount(tmpPtr); + +	pcAdjustment = 2; +	goto doInvocation; +#else +    /* +     * INST_CALL_BUILTIN_FUNC1 and INST_CALL_FUNC1 were made obsolete by the +     * changes to add a ::tcl::mathfunc namespace in 8.5. Optional support +     * remains for existing bytecode precompiled files. +     */ + +    case INST_CALL_BUILTIN_FUNC1: +	Tcl_Panic("TclNRExecuteByteCode: obsolete INST_CALL_BUILTIN_FUNC1 found"); +    case INST_CALL_FUNC1: +	Tcl_Panic("TclNRExecuteByteCode: obsolete INST_CALL_FUNC1 found"); +#endif + +    case INST_INVOKE_REPLACE: +	objc = TclGetUInt4AtPtr(pc+1); +	opnd = TclGetUInt1AtPtr(pc+5); +	objPtr = POP_OBJECT(); +	objv = &OBJ_AT_DEPTH(objc-1); +	cleanup = objc; +#ifdef TCL_COMPILE_DEBUG +	if (tclTraceExec >= 2) { +	    int i; + +	    if (traceInstructions) { +		strncpy(cmdNameBuf, TclGetString(objv[0]), 20); +		TRACE(("%u => call (implementation %s) ", objc, O2S(objPtr))); +	    } else { +		fprintf(stdout, +			"%d: (%u) invoking (using implementation %s) ", +			iPtr->numLevels, (unsigned)(pc - codePtr->codeStart), +			O2S(objPtr)); +	    } +	    for (i = 0;  i < objc;  i++) { +		if (i < opnd) { +		    fprintf(stdout, "<"); +		    TclPrintObject(stdout, objv[i], 15); +		    fprintf(stdout, ">"); +		} else { +		    TclPrintObject(stdout, objv[i], 15);  		} +		fprintf(stdout, " "); +	    } +	    fprintf(stdout, "\n"); +	    fflush(stdout); +	} +#endif /*TCL_COMPILE_DEBUG*/ +	{ +	    Tcl_Obj *copyPtr = Tcl_NewListObj(objc - opnd + 1, NULL); +	    register List *listRepPtr = copyPtr->internalRep.twoPtrValue.ptr1; +	    Tcl_Obj **copyObjv = &listRepPtr->elements; +	    int i; + +	    listRepPtr->elemCount = objc - opnd + 1; +	    copyObjv[0] = objPtr; +	    memcpy(copyObjv+1, objv+opnd, sizeof(Tcl_Obj *) * (objc - opnd)); +	    for (i=1 ; i<objc-opnd+1 ; i++) { +		Tcl_IncrRefCount(copyObjv[i]); +	    } +	    objPtr = copyPtr; +	} +	bcFramePtr->data.tebc.pc = (char *) pc; +	iPtr->cmdFramePtr = bcFramePtr; +	if (iPtr->flags & INTERP_DEBUG_FRAME) { +	    ArgumentBCEnter(interp, codePtr, TD, pc, objc, objv); +	} +	iPtr->ensembleRewrite.sourceObjs = objv; +	iPtr->ensembleRewrite.numRemovedObjs = opnd; +	iPtr->ensembleRewrite.numInsertedObjs = 1; +	DECACHE_STACK_INFO(); +	pc += 6; +	TEBC_YIELD(); + +	TclNRAddCallback(interp, TclClearRootEnsemble, NULL,NULL,NULL,NULL); +	TclSkipTailcall(interp); +	return TclNREvalObjEx(interp, objPtr, TCL_EVAL_INVOKE, NULL, INT_MIN); + +    /* +     * ----------------------------------------------------------------- +     *	   Start of INST_LOAD instructions. +     * +     * WARNING: more 'goto' here than your doctor recommended! The different +     * instructions set the value of some variables and then jump to some +     * common execution code. +     */ + +    case INST_LOAD_SCALAR1: +    instLoadScalar1: +	opnd = TclGetUInt1AtPtr(pc+1); +	varPtr = LOCAL(opnd); +	while (TclIsVarLink(varPtr)) { +	    varPtr = varPtr->value.linkPtr; +	} +	TRACE(("%u => ", opnd)); +	if (TclIsVarDirectReadable(varPtr)) { +	    /* +	     * No errors, no traces: just get the value. +	     */ +	    objResultPtr = varPtr->value.objPtr; +	    TRACE_APPEND(("%.30s\n", O2S(objResultPtr))); +	    NEXT_INST_F(2, 0, 1); +	} +	pcAdjustment = 2; +	cleanup = 0; +	arrayPtr = NULL; +	part1Ptr = part2Ptr = NULL; +	goto doCallPtrGetVar; + +    case INST_LOAD_SCALAR4: +	opnd = TclGetUInt4AtPtr(pc+1); +	varPtr = LOCAL(opnd); +	while (TclIsVarLink(varPtr)) { +	    varPtr = varPtr->value.linkPtr; +	} +	TRACE(("%u => ", opnd)); +	if (TclIsVarDirectReadable(varPtr)) { +	    /* +	     * No errors, no traces: just get the value. +	     */ + +	    objResultPtr = varPtr->value.objPtr; +	    TRACE_APPEND(("%.30s\n", O2S(objResultPtr))); +	    NEXT_INST_F(5, 0, 1); +	} +	pcAdjustment = 5; +	cleanup = 0; +	arrayPtr = NULL; +	part1Ptr = part2Ptr = NULL; +	goto doCallPtrGetVar; + +    case INST_LOAD_ARRAY4: +	opnd = TclGetUInt4AtPtr(pc+1); +	pcAdjustment = 5; +	goto doLoadArray; + +    case INST_LOAD_ARRAY1: +	opnd = TclGetUInt1AtPtr(pc+1); +	pcAdjustment = 2; + +    doLoadArray: +	part1Ptr = NULL; +	part2Ptr = OBJ_AT_TOS; +	arrayPtr = LOCAL(opnd); +	while (TclIsVarLink(arrayPtr)) { +	    arrayPtr = arrayPtr->value.linkPtr; +	} +	TRACE(("%u \"%.30s\" => ", opnd, O2S(part2Ptr))); +	if (TclIsVarArray(arrayPtr) && !ReadTraced(arrayPtr)) { +	    varPtr = VarHashFindVar(arrayPtr->value.tablePtr, part2Ptr); +	    if (varPtr && TclIsVarDirectReadable(varPtr)) {  		/* -		 * Finally, invoke the command's Tcl_ObjCmdProc. First reset -		 * the interpreter's string and object results to their -		 * default empty values since they could have gotten changed -		 * by earlier invocations. +		 * No errors, no traces: just get the value.  		 */ -		Tcl_ResetResult(interp); -		if (tclTraceExec >= 2) { +		objResultPtr = varPtr->value.objPtr; +		TRACE_APPEND(("%.30s\n", O2S(objResultPtr))); +		NEXT_INST_F(pcAdjustment, 1, 1); +	    } +	} +	varPtr = TclLookupArrayElement(interp, part1Ptr, part2Ptr, +		TCL_LEAVE_ERR_MSG, "read", 0, 1, arrayPtr, opnd); +	if (varPtr == NULL) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} +	cleanup = 1; +	goto doCallPtrGetVar; + +    case INST_LOAD_ARRAY_STK: +	cleanup = 2; +	part2Ptr = OBJ_AT_TOS;		/* element name */ +	objPtr = OBJ_UNDER_TOS;		/* array name */ +	TRACE(("\"%.30s(%.30s)\" => ", O2S(objPtr), O2S(part2Ptr))); +	goto doLoadStk; + +    case INST_LOAD_STK: +    case INST_LOAD_SCALAR_STK: +	cleanup = 1; +	part2Ptr = NULL; +	objPtr = OBJ_AT_TOS;		/* variable name */ +	TRACE(("\"%.30s\" => ", O2S(objPtr))); + +    doLoadStk: +	part1Ptr = objPtr; +	varPtr = TclObjLookupVarEx(interp, part1Ptr, part2Ptr, +		TCL_LEAVE_ERR_MSG, "read", /*createPart1*/0, /*createPart2*/1, +		&arrayPtr); +	if (!varPtr) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} + +	if (TclIsVarDirectReadable2(varPtr, arrayPtr)) { +	    /* +	     * No errors, no traces: just get the value. +	     */ + +	    objResultPtr = varPtr->value.objPtr; +	    TRACE_APPEND(("%.30s\n", O2S(objResultPtr))); +	    NEXT_INST_V(1, cleanup, 1); +	} +	pcAdjustment = 1; +	opnd = -1; + +    doCallPtrGetVar: +	/* +	 * There are either errors or the variable is traced: call +	 * TclPtrGetVar to process fully. +	 */ + +	DECACHE_STACK_INFO(); +	objResultPtr = TclPtrGetVar(interp, varPtr, arrayPtr, +		part1Ptr, part2Ptr, TCL_LEAVE_ERR_MSG, opnd); +	CACHE_STACK_INFO(); +	if (!objResultPtr) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} +	TRACE_APPEND(("%.30s\n", O2S(objResultPtr))); +	NEXT_INST_V(pcAdjustment, cleanup, 1); + +    /* +     *	   End of INST_LOAD instructions. +     * ----------------------------------------------------------------- +     *	   Start of INST_STORE and related instructions. +     * +     * WARNING: more 'goto' here than your doctor recommended! The different +     * instructions set the value of some variables and then jump to somme +     * common execution code. +     */ + +    { +	int storeFlags; + +    case INST_STORE_ARRAY4: +	opnd = TclGetUInt4AtPtr(pc+1); +	pcAdjustment = 5; +	goto doStoreArrayDirect; + +    case INST_STORE_ARRAY1: +	opnd = TclGetUInt1AtPtr(pc+1); +	pcAdjustment = 2; + +    doStoreArrayDirect: +	valuePtr = OBJ_AT_TOS; +	part2Ptr = OBJ_UNDER_TOS; +	arrayPtr = LOCAL(opnd); +	TRACE(("%u \"%.30s\" <- \"%.30s\" => ", opnd, O2S(part2Ptr), +		O2S(valuePtr))); +	while (TclIsVarLink(arrayPtr)) { +	    arrayPtr = arrayPtr->value.linkPtr; +	} +	if (TclIsVarArray(arrayPtr) && !WriteTraced(arrayPtr)) { +	    varPtr = VarHashFindVar(arrayPtr->value.tablePtr, part2Ptr); +	    if (varPtr && TclIsVarDirectWritable(varPtr)) { +		tosPtr--; +		Tcl_DecrRefCount(OBJ_AT_TOS); +		OBJ_AT_TOS = valuePtr; +		goto doStoreVarDirect; +	    } +	} +	cleanup = 2; +	storeFlags = TCL_LEAVE_ERR_MSG; +	part1Ptr = NULL; +	goto doStoreArrayDirectFailed; + +    case INST_STORE_SCALAR4: +	opnd = TclGetUInt4AtPtr(pc+1); +	pcAdjustment = 5; +	goto doStoreScalarDirect; + +    case INST_STORE_SCALAR1: +	opnd = TclGetUInt1AtPtr(pc+1); +	pcAdjustment = 2; + +    doStoreScalarDirect: +	valuePtr = OBJ_AT_TOS; +	varPtr = LOCAL(opnd); +	TRACE(("%u <- \"%.30s\" => ", opnd, O2S(valuePtr))); +	while (TclIsVarLink(varPtr)) { +	    varPtr = varPtr->value.linkPtr; +	} +	if (!TclIsVarDirectWritable(varPtr)) { +	    storeFlags = TCL_LEAVE_ERR_MSG; +	    part1Ptr = NULL; +	    goto doStoreScalar; +	} + +	/* +	 * No traces, no errors, plain 'set': we can safely inline. The value +	 * *will* be set to what's requested, so that the stack top remains +	 * pointing to the same Tcl_Obj. +	 */ + +    doStoreVarDirect: +	valuePtr = varPtr->value.objPtr; +	if (valuePtr != NULL) { +	    TclDecrRefCount(valuePtr); +	} +	objResultPtr = OBJ_AT_TOS; +	varPtr->value.objPtr = objResultPtr; +#ifndef TCL_COMPILE_DEBUG +	if (*(pc+pcAdjustment) == INST_POP) { +	    tosPtr--; +	    NEXT_INST_F((pcAdjustment+1), 0, 0); +	} +#else +	TRACE_APPEND(("%.30s\n", O2S(objResultPtr))); +#endif +	Tcl_IncrRefCount(objResultPtr); +	NEXT_INST_F(pcAdjustment, 0, 0); + +    case INST_LAPPEND_STK: +	valuePtr = OBJ_AT_TOS; /* value to append */ +	part2Ptr = NULL; +	storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE +		| TCL_LIST_ELEMENT); +	goto doStoreStk; + +    case INST_LAPPEND_ARRAY_STK: +	valuePtr = OBJ_AT_TOS; /* value to append */ +	part2Ptr = OBJ_UNDER_TOS; +	storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE +		| TCL_LIST_ELEMENT); +	goto doStoreStk; + +    case INST_APPEND_STK: +	valuePtr = OBJ_AT_TOS; /* value to append */ +	part2Ptr = NULL; +	storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE); +	goto doStoreStk; + +    case INST_APPEND_ARRAY_STK: +	valuePtr = OBJ_AT_TOS; /* value to append */ +	part2Ptr = OBJ_UNDER_TOS; +	storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE); +	goto doStoreStk; + +    case INST_STORE_ARRAY_STK: +	valuePtr = OBJ_AT_TOS; +	part2Ptr = OBJ_UNDER_TOS; +	storeFlags = TCL_LEAVE_ERR_MSG; +	goto doStoreStk; + +    case INST_STORE_STK: +    case INST_STORE_SCALAR_STK: +	valuePtr = OBJ_AT_TOS; +	part2Ptr = NULL; +	storeFlags = TCL_LEAVE_ERR_MSG; + +    doStoreStk: +	objPtr = OBJ_AT_DEPTH(1 + (part2Ptr != NULL)); /* variable name */ +	part1Ptr = objPtr;  #ifdef TCL_COMPILE_DEBUG -		    if (traceInstructions) { -			strncpy(cmdNameBuf, Tcl_GetString(objv[0]), 20); -			TRACE(("%u => call ", (isUnknownCmd? objc-1:objc))); +	if (part2Ptr == NULL) { +	    TRACE(("\"%.30s\" <- \"%.30s\" =>", O2S(part1Ptr),O2S(valuePtr))); +	} else { +	    TRACE(("\"%.30s(%.30s)\" <- \"%.30s\" => ", +		    O2S(part1Ptr), O2S(part2Ptr), O2S(valuePtr))); +	} +#endif +	varPtr = TclObjLookupVarEx(interp, objPtr,part2Ptr, TCL_LEAVE_ERR_MSG, +		"set", /*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr); +	if (!varPtr) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} +	cleanup = ((part2Ptr == NULL)? 2 : 3); +	pcAdjustment = 1; +	opnd = -1; +	goto doCallPtrSetVar; + +    case INST_LAPPEND_ARRAY4: +	opnd = TclGetUInt4AtPtr(pc+1); +	pcAdjustment = 5; +	storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE +		| TCL_LIST_ELEMENT); +	goto doStoreArray; + +    case INST_LAPPEND_ARRAY1: +	opnd = TclGetUInt1AtPtr(pc+1); +	pcAdjustment = 2; +	storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE +		| TCL_LIST_ELEMENT); +	goto doStoreArray; + +    case INST_APPEND_ARRAY4: +	opnd = TclGetUInt4AtPtr(pc+1); +	pcAdjustment = 5; +	storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE); +	goto doStoreArray; + +    case INST_APPEND_ARRAY1: +	opnd = TclGetUInt1AtPtr(pc+1); +	pcAdjustment = 2; +	storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE); +	goto doStoreArray; + +    doStoreArray: +	valuePtr = OBJ_AT_TOS; +	part2Ptr = OBJ_UNDER_TOS; +	arrayPtr = LOCAL(opnd); +	TRACE(("%u \"%.30s\" <- \"%.30s\" => ", opnd, O2S(part2Ptr), +		O2S(valuePtr))); +	while (TclIsVarLink(arrayPtr)) { +	    arrayPtr = arrayPtr->value.linkPtr; +	} +	cleanup = 2; +	part1Ptr = NULL; + +    doStoreArrayDirectFailed: +	varPtr = TclLookupArrayElement(interp, part1Ptr, part2Ptr, +		TCL_LEAVE_ERR_MSG, "set", 1, 1, arrayPtr, opnd); +	if (!varPtr) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} +	goto doCallPtrSetVar; + +    case INST_LAPPEND_SCALAR4: +	opnd = TclGetUInt4AtPtr(pc+1); +	pcAdjustment = 5; +	storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE +		| TCL_LIST_ELEMENT); +	goto doStoreScalar; + +    case INST_LAPPEND_SCALAR1: +	opnd = TclGetUInt1AtPtr(pc+1); +	pcAdjustment = 2; +	storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE +		| TCL_LIST_ELEMENT); +	goto doStoreScalar; + +    case INST_APPEND_SCALAR4: +	opnd = TclGetUInt4AtPtr(pc+1); +	pcAdjustment = 5; +	storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE); +	goto doStoreScalar; + +    case INST_APPEND_SCALAR1: +	opnd = TclGetUInt1AtPtr(pc+1); +	pcAdjustment = 2; +	storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE); +	goto doStoreScalar; + +    doStoreScalar: +	valuePtr = OBJ_AT_TOS; +	varPtr = LOCAL(opnd); +	TRACE(("%u <- \"%.30s\" => ", opnd, O2S(valuePtr))); +	while (TclIsVarLink(varPtr)) { +	    varPtr = varPtr->value.linkPtr; +	} +	cleanup = 1; +	arrayPtr = NULL; +	part1Ptr = part2Ptr = NULL; + +    doCallPtrSetVar: +	DECACHE_STACK_INFO(); +	objResultPtr = TclPtrSetVar(interp, varPtr, arrayPtr, +		part1Ptr, part2Ptr, valuePtr, storeFlags, opnd); +	CACHE_STACK_INFO(); +	if (!objResultPtr) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} +#ifndef TCL_COMPILE_DEBUG +	if (*(pc+pcAdjustment) == INST_POP) { +	    NEXT_INST_V((pcAdjustment+1), cleanup, 0); +	} +#endif +	TRACE_APPEND(("%.30s\n", O2S(objResultPtr))); +	NEXT_INST_V(pcAdjustment, cleanup, 1); +    } + +    /* +     *	   End of INST_STORE and related instructions. +     * ----------------------------------------------------------------- +     *	   Start of INST_INCR instructions. +     * +     * WARNING: more 'goto' here than your doctor recommended! The different +     * instructions set the value of some variables and then jump to somme +     * common execution code. +     */ + +/*TODO: Consider more untangling here; merge with LOAD and STORE ? */ + +    { +	Tcl_Obj *incrPtr; +#ifndef TCL_WIDE_INT_IS_LONG +	Tcl_WideInt w; +#endif +	long increment; + +    case INST_INCR_SCALAR1: +    case INST_INCR_ARRAY1: +    case INST_INCR_ARRAY_STK: +    case INST_INCR_SCALAR_STK: +    case INST_INCR_STK: +	opnd = TclGetUInt1AtPtr(pc+1); +	incrPtr = POP_OBJECT(); +	switch (*pc) { +	case INST_INCR_SCALAR1: +	    pcAdjustment = 2; +	    goto doIncrScalar; +	case INST_INCR_ARRAY1: +	    pcAdjustment = 2; +	    goto doIncrArray; +	default: +	    pcAdjustment = 1; +	    goto doIncrStk; +	} + +    case INST_INCR_ARRAY_STK_IMM: +    case INST_INCR_SCALAR_STK_IMM: +    case INST_INCR_STK_IMM: +	increment = TclGetInt1AtPtr(pc+1); +	incrPtr = Tcl_NewIntObj(increment); +	Tcl_IncrRefCount(incrPtr); +	pcAdjustment = 2; + +    doIncrStk: +	if ((*pc == INST_INCR_ARRAY_STK_IMM) +		|| (*pc == INST_INCR_ARRAY_STK)) { +	    part2Ptr = OBJ_AT_TOS; +	    objPtr = OBJ_UNDER_TOS; +	    TRACE(("\"%.30s(%.30s)\" (by %ld) => ", +		    O2S(objPtr), O2S(part2Ptr), increment)); +	} else { +	    part2Ptr = NULL; +	    objPtr = OBJ_AT_TOS; +	    TRACE(("\"%.30s\" (by %ld) => ", O2S(objPtr), increment)); +	} +	part1Ptr = objPtr; +	opnd = -1; +	varPtr = TclObjLookupVarEx(interp, objPtr, part2Ptr, +		TCL_LEAVE_ERR_MSG, "read", 1, 1, &arrayPtr); +	if (!varPtr) { +	    DECACHE_STACK_INFO(); +	    Tcl_AddErrorInfo(interp, +		    "\n    (reading value of variable to increment)"); +	    CACHE_STACK_INFO(); +	    TRACE_ERROR(interp); +	    Tcl_DecrRefCount(incrPtr); +	    goto gotError; +	} +	cleanup = ((part2Ptr == NULL)? 1 : 2); +	goto doIncrVar; + +    case INST_INCR_ARRAY1_IMM: +	opnd = TclGetUInt1AtPtr(pc+1); +	increment = TclGetInt1AtPtr(pc+2); +	incrPtr = Tcl_NewIntObj(increment); +	Tcl_IncrRefCount(incrPtr); +	pcAdjustment = 3; + +    doIncrArray: +	part1Ptr = NULL; +	part2Ptr = OBJ_AT_TOS; +	arrayPtr = LOCAL(opnd); +	cleanup = 1; +	while (TclIsVarLink(arrayPtr)) { +	    arrayPtr = arrayPtr->value.linkPtr; +	} +	TRACE(("%u \"%.30s\" (by %ld) => ", opnd, O2S(part2Ptr), increment)); +	varPtr = TclLookupArrayElement(interp, part1Ptr, part2Ptr, +		TCL_LEAVE_ERR_MSG, "read", 1, 1, arrayPtr, opnd); +	if (!varPtr) { +	    TRACE_ERROR(interp); +	    Tcl_DecrRefCount(incrPtr); +	    goto gotError; +	} +	goto doIncrVar; + +    case INST_INCR_SCALAR1_IMM: +	opnd = TclGetUInt1AtPtr(pc+1); +	increment = TclGetInt1AtPtr(pc+2); +	pcAdjustment = 3; +	cleanup = 0; +	varPtr = LOCAL(opnd); +	while (TclIsVarLink(varPtr)) { +	    varPtr = varPtr->value.linkPtr; +	} + +	if (TclIsVarDirectModifyable(varPtr)) { +	    ClientData ptr; +	    int type; + +	    objPtr = varPtr->value.objPtr; +	    if (GetNumberFromObj(NULL, objPtr, &ptr, &type) == TCL_OK) { +		if (type == TCL_NUMBER_LONG) { +		    long augend = *((const long *)ptr); +		    long sum = augend + increment; + +		    /* +		     * Overflow when (augend and sum have different sign) and +		     * (augend and increment have the same sign). This is +		     * encapsulated in the Overflowing macro. +		     */ + +		    if (!Overflowing(augend, increment, sum)) { +			TRACE(("%u %ld => ", opnd, increment)); +			if (Tcl_IsShared(objPtr)) { +			    objPtr->refCount--;	/* We know it's shared. */ +			    TclNewLongObj(objResultPtr, sum); +			    Tcl_IncrRefCount(objResultPtr); +			    varPtr->value.objPtr = objResultPtr; +			} else { +			    objResultPtr = objPtr; +			    TclSetLongObj(objPtr, sum); +			} +			goto doneIncr; +		    } +#ifndef TCL_WIDE_INT_IS_LONG +		    w = (Tcl_WideInt)augend; + +		    TRACE(("%u %ld => ", opnd, increment)); +		    if (Tcl_IsShared(objPtr)) { +			objPtr->refCount--;	/* We know it's shared. */ +			objResultPtr = Tcl_NewWideIntObj(w+increment); +			Tcl_IncrRefCount(objResultPtr); +			varPtr->value.objPtr = objResultPtr;  		    } else { -			fprintf(stdout, "%d: (%u) invoking ", -			        iPtr->numLevels, -				(unsigned int)(pc - codePtr->codeStart)); +			objResultPtr = objPtr; + +			/* +			 * We know the sum value is outside the long range; +			 * use macro form that doesn't range test again. +			 */ + +			TclSetWideIntObj(objPtr, w+increment);  		    } -		    for (i = 0;  i < objc;  i++) { -			TclPrintObject(stdout, objv[i], 15); -			fprintf(stdout, " "); +		    goto doneIncr; +#endif +		}	/* end if (type == TCL_NUMBER_LONG) */ +#ifndef TCL_WIDE_INT_IS_LONG +		if (type == TCL_NUMBER_WIDE) { +		    Tcl_WideInt sum; + +		    w = *((const Tcl_WideInt *) ptr); +		    sum = w + increment; + +		    /* +		     * Check for overflow. +		     */ + +		    if (!Overflowing(w, increment, sum)) { +			TRACE(("%u %ld => ", opnd, increment)); +			if (Tcl_IsShared(objPtr)) { +			    objPtr->refCount--;	/* We know it's shared. */ +			    objResultPtr = Tcl_NewWideIntObj(sum); +			    Tcl_IncrRefCount(objResultPtr); +			    varPtr->value.objPtr = objResultPtr; +			} else { +			    objResultPtr = objPtr; + +			    /* +			     * We *do not* know the sum value is outside the +			     * long range (wide + long can yield long); use +			     * the function call that checks range. +			     */ + +			    Tcl_SetWideIntObj(objPtr, sum); +			} +			goto doneIncr;  		    } -		    fprintf(stdout, "\n"); -		    fflush(stdout); -#else /* TCL_COMPILE_DEBUG */ -		    fprintf(stdout, "%d: (%u) invoking %s\n", -			    iPtr->numLevels, -		            (unsigned int)(pc - codePtr->codeStart), -			    Tcl_GetString(objv[0])); -#endif /*TCL_COMPILE_DEBUG*/  		} +#endif +	    } +	    if (Tcl_IsShared(objPtr)) { +		objPtr->refCount--;	/* We know it's shared */ +		objResultPtr = Tcl_DuplicateObj(objPtr); +		Tcl_IncrRefCount(objResultPtr); +		varPtr->value.objPtr = objResultPtr; +	    } else { +		objResultPtr = objPtr; +	    } +	    TclNewLongObj(incrPtr, increment); +	    if (TclIncrObj(interp, objResultPtr, incrPtr) != TCL_OK) { +		Tcl_DecrRefCount(incrPtr); +		TRACE_ERROR(interp); +		goto gotError; +	    } +	    Tcl_DecrRefCount(incrPtr); +	    goto doneIncr; +	} + +	/* +	 * All other cases, flow through to generic handling. +	 */ + +	TclNewLongObj(incrPtr, increment); +	Tcl_IncrRefCount(incrPtr); -		iPtr->cmdCount++; +    doIncrScalar: +	varPtr = LOCAL(opnd); +	while (TclIsVarLink(varPtr)) { +	    varPtr = varPtr->value.linkPtr; +	} +	arrayPtr = NULL; +	part1Ptr = part2Ptr = NULL; +	cleanup = 0; +	TRACE(("%u %s => ", opnd, Tcl_GetString(incrPtr))); + +    doIncrVar: +	if (TclIsVarDirectModifyable2(varPtr, arrayPtr)) { +	    objPtr = varPtr->value.objPtr; +	    if (Tcl_IsShared(objPtr)) { +		objPtr->refCount--;	/* We know it's shared */ +		objResultPtr = Tcl_DuplicateObj(objPtr); +		Tcl_IncrRefCount(objResultPtr); +		varPtr->value.objPtr = objResultPtr; +	    } else { +		objResultPtr = objPtr; +	    } +	    if (TclIncrObj(interp, objResultPtr, incrPtr) != TCL_OK) { +		Tcl_DecrRefCount(incrPtr); +		TRACE_ERROR(interp); +		goto gotError; +	    } +	    Tcl_DecrRefCount(incrPtr); +	} else { +	    DECACHE_STACK_INFO(); +	    objResultPtr = TclPtrIncrObjVar(interp, varPtr, arrayPtr, +		    part1Ptr, part2Ptr, incrPtr, TCL_LEAVE_ERR_MSG, opnd); +	    CACHE_STACK_INFO(); +	    Tcl_DecrRefCount(incrPtr); +	    if (objResultPtr == NULL) { +		TRACE_ERROR(interp); +		goto gotError; +	    } +	} +    doneIncr: +	TRACE_APPEND(("%.30s\n", O2S(objResultPtr))); +#ifndef TCL_COMPILE_DEBUG +	if (*(pc+pcAdjustment) == INST_POP) { +	    NEXT_INST_V((pcAdjustment+1), cleanup, 0); +	} +#endif +	NEXT_INST_V(pcAdjustment, cleanup, 1); +    } + +    /* +     *	   End of INST_INCR instructions. +     * ----------------------------------------------------------------- +     *	   Start of INST_EXIST instructions. +     */ + +    case INST_EXIST_SCALAR: +	cleanup = 0; +	pcAdjustment = 5; +	opnd = TclGetUInt4AtPtr(pc+1); +	varPtr = LOCAL(opnd); +	while (TclIsVarLink(varPtr)) { +	    varPtr = varPtr->value.linkPtr; +	} +	TRACE(("%u => ", opnd)); +	if (ReadTraced(varPtr)) { +	    DECACHE_STACK_INFO(); +	    TclObjCallVarTraces(iPtr, NULL, varPtr, NULL, NULL, +		    TCL_TRACE_READS, 0, opnd); +	    CACHE_STACK_INFO(); +	    if (TclIsVarUndefined(varPtr)) { +		TclCleanupVar(varPtr, NULL); +		varPtr = NULL; +	    } +	} +	goto afterExistsPeephole; + +    case INST_EXIST_ARRAY: +	cleanup = 1; +	pcAdjustment = 5; +	opnd = TclGetUInt4AtPtr(pc+1); +	part2Ptr = OBJ_AT_TOS; +	arrayPtr = LOCAL(opnd); +	while (TclIsVarLink(arrayPtr)) { +	    arrayPtr = arrayPtr->value.linkPtr; +	} +	TRACE(("%u \"%.30s\" => ", opnd, O2S(part2Ptr))); +	if (TclIsVarArray(arrayPtr) && !ReadTraced(arrayPtr)) { +	    varPtr = VarHashFindVar(arrayPtr->value.tablePtr, part2Ptr); +	    if (!varPtr || !ReadTraced(varPtr)) { +		goto afterExistsPeephole; +	    } +	} +	varPtr = TclLookupArrayElement(interp, NULL, part2Ptr, 0, "access", +		0, 1, arrayPtr, opnd); +	if (varPtr) { +	    if (ReadTraced(varPtr) || (arrayPtr && ReadTraced(arrayPtr))) {  		DECACHE_STACK_INFO(); -		result = (*cmdPtr->objProc)(cmdPtr->objClientData, interp, -					    objc, objv); -		if (Tcl_AsyncReady()) { -		    result = Tcl_AsyncInvoke(interp, result); -		} +		TclObjCallVarTraces(iPtr, arrayPtr, varPtr, NULL, part2Ptr, +			TCL_TRACE_READS, 0, opnd);  		CACHE_STACK_INFO(); +	    } +	    if (TclIsVarUndefined(varPtr)) { +		TclCleanupVar(varPtr, arrayPtr); +		varPtr = NULL; +	    } +	} +	goto afterExistsPeephole; + +    case INST_EXIST_ARRAY_STK: +	cleanup = 2; +	pcAdjustment = 1; +	part2Ptr = OBJ_AT_TOS;		/* element name */ +	part1Ptr = OBJ_UNDER_TOS;	/* array name */ +	TRACE(("\"%.30s(%.30s)\" => ", O2S(part1Ptr), O2S(part2Ptr))); +	goto doExistStk; + +    case INST_EXIST_STK: +	cleanup = 1; +	pcAdjustment = 1; +	part2Ptr = NULL; +	part1Ptr = OBJ_AT_TOS;		/* variable name */ +	TRACE(("\"%.30s\" => ", O2S(part1Ptr))); + +    doExistStk: +	varPtr = TclObjLookupVarEx(interp, part1Ptr, part2Ptr, 0, "access", +		/*createPart1*/0, /*createPart2*/1, &arrayPtr); +	if (varPtr) { +	    if (ReadTraced(varPtr) || (arrayPtr && ReadTraced(arrayPtr))) { +		DECACHE_STACK_INFO(); +		TclObjCallVarTraces(iPtr, arrayPtr, varPtr, part1Ptr,part2Ptr, +			TCL_TRACE_READS, 0, -1); +		CACHE_STACK_INFO(); +	    } +	    if (TclIsVarUndefined(varPtr)) { +		TclCleanupVar(varPtr, arrayPtr); +		varPtr = NULL; +	    } +	} -		/* -		 * If the old stack is going to be released, it is -		 * safe to do so now, since no references to objv are -		 * going to be used from now on. -		 */ +	/* +	 * Peep-hole optimisation: if you're about to jump, do jump from here. +	 */ + +    afterExistsPeephole: { +	int found = (varPtr && !TclIsVarUndefined(varPtr)); + +	TRACE_APPEND(("%d\n", found ? 1 : 0)); +	JUMP_PEEPHOLE_V(found, pcAdjustment, cleanup); +    } -		Tcl_Release((ClientData)preservedStack); +    /* +     *	   End of INST_EXIST instructions. +     * ----------------------------------------------------------------- +     *	   Start of INST_UNSET instructions. +     */ + +    { +	int flags; + +    case INST_UNSET_SCALAR: +	flags = TclGetUInt1AtPtr(pc+1) ? TCL_LEAVE_ERR_MSG : 0; +	opnd = TclGetUInt4AtPtr(pc+2); +	varPtr = LOCAL(opnd); +	while (TclIsVarLink(varPtr)) { +	    varPtr = varPtr->value.linkPtr; +	} +	TRACE(("%s %u => ", (flags ? "normal" : "noerr"), opnd)); +	if (TclIsVarDirectUnsettable(varPtr) && !TclIsVarInHash(varPtr)) { +	    /* +	     * No errors, no traces, no searches: just make the variable cease +	     * to exist. +	     */ +	    if (!TclIsVarUndefined(varPtr)) { +		TclDecrRefCount(varPtr->value.objPtr); +	    } else if (flags & TCL_LEAVE_ERR_MSG) { +		goto slowUnsetScalar; +	    } +	    varPtr->value.objPtr = NULL; +	    TRACE_APPEND(("OK\n")); +	    NEXT_INST_F(6, 0, 0); +	} + +    slowUnsetScalar: +	DECACHE_STACK_INFO(); +	if (TclPtrUnsetVar(interp, varPtr, NULL, NULL, NULL, flags, +		opnd) != TCL_OK && flags) { +	    goto errorInUnset; +	} +	CACHE_STACK_INFO(); +	NEXT_INST_F(6, 0, 0); + +    case INST_UNSET_ARRAY: +	flags = TclGetUInt1AtPtr(pc+1) ? TCL_LEAVE_ERR_MSG : 0; +	opnd = TclGetUInt4AtPtr(pc+2); +	part2Ptr = OBJ_AT_TOS; +	arrayPtr = LOCAL(opnd); +	while (TclIsVarLink(arrayPtr)) { +	    arrayPtr = arrayPtr->value.linkPtr; +	} +	TRACE(("%s %u \"%.30s\" => ", +		(flags ? "normal" : "noerr"), opnd, O2S(part2Ptr))); +	if (TclIsVarArray(arrayPtr) && !UnsetTraced(arrayPtr)) { +	    varPtr = VarHashFindVar(arrayPtr->value.tablePtr, part2Ptr); +	    if (varPtr && TclIsVarDirectUnsettable(varPtr)) {  		/* -		 * If the interpreter has a non-empty string result, the -		 * result object is either empty or stale because some -		 * procedure set interp->result directly. If so, move the -		 * string result to the result object, then reset the -		 * string result. +		 * No nasty traces and element exists, so we can proceed to +		 * unset it. Might still not exist though...  		 */ -		if (*(iPtr->result) != 0) { -		    (void) Tcl_GetObjResult(interp); +		if (!TclIsVarUndefined(varPtr)) { +		    TclDecrRefCount(varPtr->value.objPtr); +		} else if (flags & TCL_LEAVE_ERR_MSG) { +		    goto slowUnsetArray;  		} -		 +		varPtr->value.objPtr = NULL; +		TRACE_APPEND(("OK\n")); +		NEXT_INST_F(6, 1, 0); +	    } else if (!varPtr && !(flags & TCL_LEAVE_ERR_MSG)) {  		/* -		 * Pop the objc top stack elements and decrement their ref -		 * counts.  +		 * Don't need to do anything here.  		 */ -		for (i = 0;  i < objc;  i++) { -		    valuePtr = stackPtr[stackTop]; -		    TclDecrRefCount(valuePtr); -		    stackTop--; -		} +		TRACE_APPEND(("OK\n")); +		NEXT_INST_F(6, 1, 0); +	    } +	} +    slowUnsetArray: +	DECACHE_STACK_INFO(); +	varPtr = TclLookupArrayElement(interp, NULL, part2Ptr, flags, "unset", +		0, 0, arrayPtr, opnd); +	if (!varPtr) { +	    if (flags & TCL_LEAVE_ERR_MSG) { +		goto errorInUnset; +	    } +	} else if (TclPtrUnsetVar(interp, varPtr, arrayPtr, NULL, part2Ptr, +		flags, opnd) != TCL_OK && (flags & TCL_LEAVE_ERR_MSG)) { +	    goto errorInUnset; +	} +	CACHE_STACK_INFO(); +	NEXT_INST_F(6, 1, 0); + +    case INST_UNSET_ARRAY_STK: +	flags = TclGetUInt1AtPtr(pc+1) ? TCL_LEAVE_ERR_MSG : 0; +	cleanup = 2; +	part2Ptr = OBJ_AT_TOS;		/* element name */ +	part1Ptr = OBJ_UNDER_TOS;	/* array name */ +	TRACE(("%s \"%.30s(%.30s)\" => ", (flags ? "normal" : "noerr"), +		O2S(part1Ptr), O2S(part2Ptr))); +	goto doUnsetStk; + +    case INST_UNSET_STK: +	flags = TclGetUInt1AtPtr(pc+1) ? TCL_LEAVE_ERR_MSG : 0; +	cleanup = 1; +	part2Ptr = NULL; +	part1Ptr = OBJ_AT_TOS;		/* variable name */ +	TRACE(("%s \"%.30s\" => ", (flags ? "normal" : "noerr"), +		O2S(part1Ptr))); + +    doUnsetStk: +	DECACHE_STACK_INFO(); +	if (TclObjUnsetVar2(interp, part1Ptr, part2Ptr, flags) != TCL_OK +		&& (flags & TCL_LEAVE_ERR_MSG)) { +	    goto errorInUnset; +	} +	CACHE_STACK_INFO(); +	TRACE_APPEND(("OK\n")); +	NEXT_INST_V(2, cleanup, 0); -		/* -		 * Process the result of the Tcl_ObjCmdProc call. -		 */ -		 -		switch (result) { -		case TCL_OK: -		    /* -		     * Push the call's object result and continue execution -		     * with the next instruction. -		     */ -		    PUSH_OBJECT(Tcl_GetObjResult(interp)); -		    TRACE_WITH_OBJ(("%u => ...after \"%.20s\", result=", -		            objc, cmdNameBuf), Tcl_GetObjResult(interp)); -		    ADJUST_PC(pcAdjustment); -		     -		case TCL_BREAK: -		case TCL_CONTINUE: -		    /* -		     * The invoked command requested a break or continue. -		     * Find the closest enclosing loop or catch exception -		     * range, if any. If a loop is found, terminate its -		     * execution or skip to its next iteration. If the -		     * closest is a catch exception range, jump to its -		     * catchOffset. If no enclosing range is found, stop -		     * execution and return the TCL_BREAK or TCL_CONTINUE. -		     */ -		    rangePtr = GetExceptRangeForPc(pc, /*catchOnly*/ 0, -			    codePtr); -		    if (rangePtr == NULL) { -		        TRACE(("%u => ... after \"%.20s\", no encl. loop or catch, returning %s\n", -		                objc, cmdNameBuf, -			        StringForResultCode(result))); -			goto abnormalReturn; /* no catch exists to check */ -		    } -		    newPcOffset = 0; -		    switch (rangePtr->type) { -		    case LOOP_EXCEPTION_RANGE: -			if (result == TCL_BREAK) { -			    newPcOffset = rangePtr->breakOffset; -			} else if (rangePtr->continueOffset == -1) { -			    TRACE(("%u => ... after \"%.20s\", %s, loop w/o continue, checking for catch\n", -				   objc, cmdNameBuf, -				   StringForResultCode(result))); -			    goto checkForCatch; -			} else { -			    newPcOffset = rangePtr->continueOffset; -			} -			TRACE(("%u => ... after \"%.20s\", %s, range at %d, new pc %d\n", -			       objc, cmdNameBuf, -			       StringForResultCode(result), -			       rangePtr->codeOffset, newPcOffset)); -			break; -		    case CATCH_EXCEPTION_RANGE: -			TRACE(("%u => ... after \"%.20s\", %s...\n", -			       objc, cmdNameBuf, -			       StringForResultCode(result))); -			goto processCatch; /* it will use rangePtr */ -		    default: -			panic("TclExecuteByteCode: bad ExceptionRange type\n"); -		    } -		    result = TCL_OK; -		    pc = (codePtr->codeStart + newPcOffset); -		    continue;	/* restart outer instruction loop at pc */ -		     -		case TCL_ERROR: -		    /* -		     * The invoked command returned an error. Look for an -		     * enclosing catch exception range, if any. -		     */ -		    TRACE_WITH_OBJ(("%u => ... after \"%.20s\", TCL_ERROR ", -		            objc, cmdNameBuf), Tcl_GetObjResult(interp)); -		    goto checkForCatch; +    errorInUnset: +	CACHE_STACK_INFO(); +	TRACE_ERROR(interp); +	goto gotError; -		case TCL_RETURN: -		    /* -		     * The invoked command requested that the current -		     * procedure stop execution and return. First check -		     * for an enclosing catch exception range, if any. -		     */ -		    TRACE(("%u => ... after \"%.20s\", TCL_RETURN\n", -		            objc, cmdNameBuf)); -		    goto checkForCatch; +	/* +	 * This is really an unset operation these days. Do not issue. +	 */ -		default: -		    TRACE_WITH_OBJ(("%u => ... after \"%.20s\", OTHER RETURN CODE %d ", -		            objc, cmdNameBuf, result), -			    Tcl_GetObjResult(interp)); -		    goto checkForCatch; -		} +    case INST_DICT_DONE: +	opnd = TclGetUInt4AtPtr(pc+1); +	TRACE(("%u => OK\n", opnd)); +	varPtr = LOCAL(opnd); +	while (TclIsVarLink(varPtr)) { +	    varPtr = varPtr->value.linkPtr; +	} +	if (TclIsVarDirectUnsettable(varPtr) && !TclIsVarInHash(varPtr)) { +	    if (!TclIsVarUndefined(varPtr)) { +		TclDecrRefCount(varPtr->value.objPtr);  	    } -	     -	case INST_EVAL_STK: -	    objPtr = POP_OBJECT(); +	    varPtr->value.objPtr = NULL; +	} else {  	    DECACHE_STACK_INFO(); -	    result = Tcl_EvalObjEx(interp, objPtr, 0); +	    TclPtrUnsetVar(interp, varPtr, NULL, NULL, NULL, 0, opnd);  	    CACHE_STACK_INFO(); -	    if (result == TCL_OK) { +	} +	NEXT_INST_F(5, 0, 0); +    } + +    /* +     *	   End of INST_UNSET instructions. +     * ----------------------------------------------------------------- +     *	   Start of INST_ARRAY instructions. +     */ + +    case INST_ARRAY_EXISTS_IMM: +	opnd = TclGetUInt4AtPtr(pc+1); +	pcAdjustment = 5; +	cleanup = 0; +	part1Ptr = NULL; +	arrayPtr = NULL; +	TRACE(("%u => ", opnd)); +	varPtr = LOCAL(opnd); +	while (TclIsVarLink(varPtr)) { +	    varPtr = varPtr->value.linkPtr; +	} +	goto doArrayExists; +    case INST_ARRAY_EXISTS_STK: +	opnd = -1; +	pcAdjustment = 1; +	cleanup = 1; +	part1Ptr = OBJ_AT_TOS; +	TRACE(("\"%.30s\" => ", O2S(part1Ptr))); +	varPtr = TclObjLookupVarEx(interp, part1Ptr, NULL, 0, NULL, +		/*createPart1*/0, /*createPart2*/0, &arrayPtr); +    doArrayExists: +	if (varPtr && (varPtr->flags & VAR_TRACED_ARRAY) +		&& (TclIsVarArray(varPtr) || TclIsVarUndefined(varPtr))) { +	    DECACHE_STACK_INFO(); +	    result = TclObjCallVarTraces(iPtr, arrayPtr, varPtr, part1Ptr, +		    NULL, (TCL_LEAVE_ERR_MSG|TCL_NAMESPACE_ONLY| +		    TCL_GLOBAL_ONLY|TCL_TRACE_ARRAY), 1, opnd); +	    CACHE_STACK_INFO(); +	    if (result == TCL_ERROR) { +		TRACE_ERROR(interp); +		goto gotError; +	    } +	} +	if (varPtr && TclIsVarArray(varPtr) && !TclIsVarUndefined(varPtr)) { +	    objResultPtr = TCONST(1); +	} else { +	    objResultPtr = TCONST(0); +	} +	TRACE_APPEND(("%.30s\n", O2S(objResultPtr))); +	NEXT_INST_V(pcAdjustment, cleanup, 1); + +    case INST_ARRAY_MAKE_IMM: +	opnd = TclGetUInt4AtPtr(pc+1); +	pcAdjustment = 5; +	cleanup = 0; +	part1Ptr = NULL; +	arrayPtr = NULL; +	TRACE(("%u => ", opnd)); +	varPtr = LOCAL(opnd); +	while (TclIsVarLink(varPtr)) { +	    varPtr = varPtr->value.linkPtr; +	} +	goto doArrayMake; +    case INST_ARRAY_MAKE_STK: +	opnd = -1; +	pcAdjustment = 1; +	cleanup = 1; +	part1Ptr = OBJ_AT_TOS; +	TRACE(("\"%.30s\" => ", O2S(part1Ptr))); +	varPtr = TclObjLookupVarEx(interp, part1Ptr, NULL, TCL_LEAVE_ERR_MSG, +		"set", /*createPart1*/1, /*createPart2*/0, &arrayPtr); +	if (varPtr == NULL) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} +    doArrayMake: +	if (varPtr && !TclIsVarArray(varPtr)) { +	    if (TclIsVarArrayElement(varPtr) || !TclIsVarUndefined(varPtr)) {  		/* -		 * Normal return; push the eval's object result. +		 * Either an array element, or a scalar: lose!  		 */ -		PUSH_OBJECT(Tcl_GetObjResult(interp)); -		TRACE_WITH_OBJ(("\"%.30s\" => ", O2S(objPtr)), -			Tcl_GetObjResult(interp)); -		TclDecrRefCount(objPtr); -		ADJUST_PC(1); -	    } else if ((result == TCL_BREAK) || (result == TCL_CONTINUE)) { + +		TclObjVarErrMsg(interp, part1Ptr, NULL, "array set", +			"variable isn't array", opnd); +		DECACHE_STACK_INFO(); +		Tcl_SetErrorCode(interp, "TCL", "WRITE", "ARRAY", NULL); +		CACHE_STACK_INFO(); +		TRACE_ERROR(interp); +		goto gotError; +	    } +	    TclSetVarArray(varPtr); +	    varPtr->value.tablePtr = ckalloc(sizeof(TclVarHashTable)); +	    TclInitVarHashTable(varPtr->value.tablePtr, +		    TclGetVarNsPtr(varPtr)); +#ifdef TCL_COMPILE_DEBUG +	    TRACE_APPEND(("done\n")); +	} else { +	    TRACE_APPEND(("nothing to do\n")); +#endif +	} +	NEXT_INST_V(pcAdjustment, cleanup, 0); + +    /* +     *	   End of INST_ARRAY instructions. +     * ----------------------------------------------------------------- +     *	   Start of variable linking instructions. +     */ + +    { +	Var *otherPtr; +	CallFrame *framePtr, *savedFramePtr; +	Tcl_Namespace *nsPtr; +	Namespace *savedNsPtr; + +    case INST_UPVAR: +	TRACE(("%d %.30s %.30s => ", TclGetInt4AtPtr(pc+1), +		O2S(OBJ_UNDER_TOS), O2S(OBJ_AT_TOS))); + +	if (TclObjGetFrame(interp, OBJ_UNDER_TOS, &framePtr) == -1) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} + +	/* +	 * Locate the other variable. +	 */ + +	savedFramePtr = iPtr->varFramePtr; +	iPtr->varFramePtr = framePtr; +	otherPtr = TclObjLookupVarEx(interp, OBJ_AT_TOS, NULL, +		TCL_LEAVE_ERR_MSG, "access", /*createPart1*/ 1, +		/*createPart2*/ 1, &varPtr); +	iPtr->varFramePtr = savedFramePtr; +	if (!otherPtr) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} +	goto doLinkVars; + +    case INST_NSUPVAR: +	TRACE(("%d %.30s %.30s => ", TclGetInt4AtPtr(pc+1), +		O2S(OBJ_UNDER_TOS), O2S(OBJ_AT_TOS))); +	if (TclGetNamespaceFromObj(interp, OBJ_UNDER_TOS, &nsPtr) != TCL_OK) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} + +	/* +	 * Locate the other variable. +	 */ + +	savedNsPtr = iPtr->varFramePtr->nsPtr; +	iPtr->varFramePtr->nsPtr = (Namespace *) nsPtr; +	otherPtr = TclObjLookupVarEx(interp, OBJ_AT_TOS, NULL, +		(TCL_NAMESPACE_ONLY | TCL_LEAVE_ERR_MSG), "access", +		/*createPart1*/ 1, /*createPart2*/ 1, &varPtr); +	iPtr->varFramePtr->nsPtr = savedNsPtr; +	if (!otherPtr) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} +	goto doLinkVars; + +    case INST_VARIABLE: +	TRACE(("%d, %.30s => ", TclGetInt4AtPtr(pc+1), O2S(OBJ_AT_TOS))); +	otherPtr = TclObjLookupVarEx(interp, OBJ_AT_TOS, NULL, +		(TCL_NAMESPACE_ONLY | TCL_LEAVE_ERR_MSG), "access", +		/*createPart1*/ 1, /*createPart2*/ 1, &varPtr); +	if (!otherPtr) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} + +	/* +	 * Do the [variable] magic. +	 */ + +	TclSetVarNamespaceVar(otherPtr); + +    doLinkVars: + +	/* +	 * If we are here, the local variable has already been created: do the +	 * little work of TclPtrMakeUpvar that remains to be done right here +	 * if there are no errors; otherwise, let it handle the case. +	 */ + +	opnd = TclGetInt4AtPtr(pc+1); +	varPtr = LOCAL(opnd); +	if ((varPtr != otherPtr) && !TclIsVarTraced(varPtr) +		&& (TclIsVarUndefined(varPtr) || TclIsVarLink(varPtr))) { +	    if (!TclIsVarUndefined(varPtr)) {  		/* -		 * Find the closest enclosing loop or catch exception range, -		 * if any. If a loop is found, terminate its execution or -		 * skip to its next iteration. If the closest is a catch -		 * exception range, jump to its catchOffset. If no enclosing -		 * range is found, stop execution and return that same -		 * TCL_BREAK or TCL_CONTINUE. +		 * Then it is a defined link.  		 */ -		int newPcOffset = 0; /* Pc offset computed during break, -				      * continue, error processing. Init. -				      * to avoid compiler warning. */ - -		rangePtr = GetExceptRangeForPc(pc, /*catchOnly*/ 0, -			codePtr); -		if (rangePtr == NULL) { -		    TRACE(("\"%.30s\" => no encl. loop or catch, returning %s\n", -			    O2S(objPtr), StringForResultCode(result))); -		    Tcl_DecrRefCount(objPtr); -		    goto abnormalReturn;    /* no catch exists to check */ +		Var *linkPtr = varPtr->value.linkPtr; + +		if (linkPtr == otherPtr) { +		    TRACE_APPEND(("already linked\n")); +		    NEXT_INST_F(5, 1, 0);  		} -		switch (rangePtr->type) { -		case LOOP_EXCEPTION_RANGE: -		    if (result == TCL_BREAK) { -			newPcOffset = rangePtr->breakOffset; -		    } else if (rangePtr->continueOffset == -1) { -			TRACE(("\"%.30s\" => %s, loop w/o continue, checking for catch\n", -			       O2S(objPtr), StringForResultCode(result))); -			Tcl_DecrRefCount(objPtr); -			goto checkForCatch; -		    } else { -			newPcOffset = rangePtr->continueOffset; +		if (TclIsVarInHash(linkPtr)) { +		    VarHashRefCount(linkPtr)--; +		    if (TclIsVarUndefined(linkPtr)) { +			TclCleanupVar(linkPtr, NULL);  		    } -		    result = TCL_OK; -		    TRACE_WITH_OBJ(("\"%.30s\" => %s, range at %d, new pc %d ", -			    O2S(objPtr), StringForResultCode(result), -			    rangePtr->codeOffset, newPcOffset), valuePtr); -		    break; -		case CATCH_EXCEPTION_RANGE: -		    TRACE_WITH_OBJ(("\"%.30s\" => %s ", -			    O2S(objPtr), StringForResultCode(result)), -			    valuePtr); -		    Tcl_DecrRefCount(objPtr); -		    goto processCatch;  /* it will use rangePtr */ -		default: -		    panic("TclExecuteByteCode: unrecognized ExceptionRange type %d\n", rangePtr->type);  		} -		Tcl_DecrRefCount(objPtr); -		pc = (codePtr->codeStart + newPcOffset); -		continue;	/* restart outer instruction loop at pc */ -	    } else { /* eval returned TCL_ERROR, TCL_RETURN, unknown code */ -		TRACE_WITH_OBJ(("\"%.30s\" => ERROR: ", O2S(objPtr)), -		        Tcl_GetObjResult(interp)); -		Tcl_DecrRefCount(objPtr); -		goto checkForCatch;  	    } +	    TclSetVarLink(varPtr); +	    varPtr->value.linkPtr = otherPtr; +	    if (TclIsVarInHash(otherPtr)) { +		VarHashRefCount(otherPtr)++; +	    } +	} else if (TclPtrObjMakeUpvar(interp, otherPtr, NULL, 0, +		opnd) != TCL_OK) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} -	case INST_EXPR_STK: -	    objPtr = POP_OBJECT(); -	    Tcl_ResetResult(interp); -	    DECACHE_STACK_INFO(); -	    result = Tcl_ExprObj(interp, objPtr, &valuePtr); -	    CACHE_STACK_INFO(); -	    if (result != TCL_OK) { -		TRACE_WITH_OBJ(("\"%.30s\" => ERROR: ",  -		        O2S(objPtr)), Tcl_GetObjResult(interp)); -		Tcl_DecrRefCount(objPtr); -		goto checkForCatch; +	/* +	 * Do not pop the namespace or frame index, it may be needed for other +	 * variables - and [variable] did not push it at all. +	 */ + +	TRACE_APPEND(("link made\n")); +	NEXT_INST_F(5, 1, 0); +    } + +    /* +     *	   End of variable linking instructions. +     * ----------------------------------------------------------------- +     */ + +    case INST_JUMP1: +	opnd = TclGetInt1AtPtr(pc+1); +	TRACE(("%d => new pc %u\n", opnd, +		(unsigned)(pc + opnd - codePtr->codeStart))); +	NEXT_INST_F(opnd, 0, 0); + +    case INST_JUMP4: +	opnd = TclGetInt4AtPtr(pc+1); +	TRACE(("%d => new pc %u\n", opnd, +		(unsigned)(pc + opnd - codePtr->codeStart))); +	NEXT_INST_F(opnd, 0, 0); + +    { +	int jmpOffset[2], b; + +	/* TODO: consider rewrite so we don't compute the offset we're not +	 * going to take. */ +    case INST_JUMP_FALSE4: +	jmpOffset[0] = TclGetInt4AtPtr(pc+1);	/* FALSE offset */ +	jmpOffset[1] = 5;			/* TRUE offset */ +	goto doCondJump; + +    case INST_JUMP_TRUE4: +	jmpOffset[0] = 5; +	jmpOffset[1] = TclGetInt4AtPtr(pc+1); +	goto doCondJump; + +    case INST_JUMP_FALSE1: +	jmpOffset[0] = TclGetInt1AtPtr(pc+1); +	jmpOffset[1] = 2; +	goto doCondJump; + +    case INST_JUMP_TRUE1: +	jmpOffset[0] = 2; +	jmpOffset[1] = TclGetInt1AtPtr(pc+1); + +    doCondJump: +	valuePtr = OBJ_AT_TOS; +	TRACE(("%d => ", jmpOffset[ +		(*pc==INST_JUMP_FALSE1 || *pc==INST_JUMP_FALSE4) ? 0 : 1])); + +	/* TODO - check claim that taking address of b harms performance */ +	/* TODO - consider optimization search for constants */ +	if (TclGetBooleanFromObj(interp, valuePtr, &b) != TCL_OK) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} + +#ifdef TCL_COMPILE_DEBUG +	if (b) { +	    if ((*pc == INST_JUMP_TRUE1) || (*pc == INST_JUMP_TRUE4)) { +		TRACE_APPEND(("%.20s true, new pc %u\n", O2S(valuePtr), +			(unsigned)(pc + jmpOffset[1] - codePtr->codeStart))); +	    } else { +		TRACE_APPEND(("%.20s true\n", O2S(valuePtr)));  	    } -	    stackPtr[++stackTop] = valuePtr; /* already has right refct */ -	    TRACE_WITH_OBJ(("\"%.30s\" => ", O2S(objPtr)), valuePtr); -	    TclDecrRefCount(objPtr); -	    ADJUST_PC(1); +	} else { +	    if ((*pc == INST_JUMP_TRUE1) || (*pc == INST_JUMP_TRUE4)) { +		TRACE_APPEND(("%.20s false\n", O2S(valuePtr))); +	    } else { +		TRACE_APPEND(("%.20s false, new pc %u\n", O2S(valuePtr), +			(unsigned)(pc + jmpOffset[0] - codePtr->codeStart))); +	    } +	} +#endif +	NEXT_INST_F(jmpOffset[b], 1, 0); +    } -	case INST_LOAD_SCALAR1: -	    opnd = TclGetUInt1AtPtr(pc+1); -	    DECACHE_STACK_INFO(); -	    valuePtr = TclGetIndexedScalar(interp, opnd, TCL_LEAVE_ERR_MSG); -	    CACHE_STACK_INFO(); -	    if (valuePtr == NULL) { -		TRACE_WITH_OBJ(("%u => ERROR: ", opnd), -		        Tcl_GetObjResult(interp)); -		result = TCL_ERROR; -		goto checkForCatch; -            } -	    PUSH_OBJECT(valuePtr); -	    TRACE_WITH_OBJ(("%u => ", opnd), valuePtr); -	    ADJUST_PC(2); +    case INST_JUMP_TABLE: { +	Tcl_HashEntry *hPtr; +	JumptableInfo *jtPtr; -	case INST_LOAD_SCALAR4: -	    opnd = TclGetUInt4AtPtr(pc+1); +	/* +	 * Jump to location looked up in a hashtable; fall through to next +	 * instr if lookup fails. +	 */ + +	opnd = TclGetInt4AtPtr(pc+1); +	jtPtr = (JumptableInfo *) codePtr->auxDataArrayPtr[opnd].clientData; +	TRACE(("%d \"%.20s\" => ", opnd, O2S(OBJ_AT_TOS))); +	hPtr = Tcl_FindHashEntry(&jtPtr->hashTable, TclGetString(OBJ_AT_TOS)); +	if (hPtr != NULL) { +	    int jumpOffset = PTR2INT(Tcl_GetHashValue(hPtr)); + +	    TRACE_APPEND(("found in table, new pc %u\n", +		    (unsigned)(pc - codePtr->codeStart + jumpOffset))); +	    NEXT_INST_F(jumpOffset, 1, 0); +	} else { +	    TRACE_APPEND(("not found in table\n")); +	    NEXT_INST_F(5, 1, 0); +	} +    } + +    /* +     * These two instructions are now redundant: the complete logic of the LOR +     * and LAND is now handled by the expression compiler. +     */ + +    case INST_LOR: +    case INST_LAND: { +	/* +	 * Operands must be boolean or numeric. No int->double conversions are +	 * performed. +	 */ + +	int i1, i2, iResult; + +	value2Ptr = OBJ_AT_TOS; +	valuePtr = OBJ_UNDER_TOS; +	if (TclGetBooleanFromObj(NULL, valuePtr, &i1) != TCL_OK) { +	    TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n", O2S(valuePtr), +		    (valuePtr->typePtr? valuePtr->typePtr->name : "null")));  	    DECACHE_STACK_INFO(); -	    valuePtr = TclGetIndexedScalar(interp, opnd, TCL_LEAVE_ERR_MSG); +	    IllegalExprOperandType(interp, pc, valuePtr);  	    CACHE_STACK_INFO(); -	    if (valuePtr == NULL) { -		TRACE_WITH_OBJ(("%u => ERROR: ", opnd), -		        Tcl_GetObjResult(interp)); -		result = TCL_ERROR; -		goto checkForCatch; -            } -	    PUSH_OBJECT(valuePtr); -	    TRACE_WITH_OBJ(("%u => ", opnd), valuePtr); -	    ADJUST_PC(5); +	    goto gotError; +	} -	case INST_LOAD_STK: -	case INST_LOAD_SCALAR_STK: -	    objPtr = POP_OBJECT(); /* scalar / variable name */ +	if (TclGetBooleanFromObj(NULL, value2Ptr, &i2) != TCL_OK) { +	    TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n", O2S(value2Ptr), +		    (value2Ptr->typePtr? value2Ptr->typePtr->name : "null")));  	    DECACHE_STACK_INFO(); -	    valuePtr = Tcl_ObjGetVar2(interp, objPtr, NULL, TCL_LEAVE_ERR_MSG); +	    IllegalExprOperandType(interp, pc, value2Ptr);  	    CACHE_STACK_INFO(); -	    if (valuePtr == NULL) { -		TRACE_WITH_OBJ(("\"%.30s\" => ERROR: ", O2S(objPtr)), -		        Tcl_GetObjResult(interp)); -		Tcl_DecrRefCount(objPtr); -		result = TCL_ERROR; -		goto checkForCatch; -            } -	    PUSH_OBJECT(valuePtr); -	    TRACE_WITH_OBJ(("\"%.30s\" => ", O2S(objPtr)), valuePtr); -	    TclDecrRefCount(objPtr); -	    ADJUST_PC(1); +	    goto gotError; +	} -	case INST_LOAD_ARRAY4: -	    opnd = TclGetUInt4AtPtr(pc+1); -	    pcAdjustment = 5; -	    goto doLoadArray; +	if (*pc == INST_LOR) { +	    iResult = (i1 || i2); +	} else { +	    iResult = (i1 && i2); +	} +	objResultPtr = TCONST(iResult); +	TRACE(("%.20s %.20s => %d\n", O2S(valuePtr),O2S(value2Ptr),iResult)); +	NEXT_INST_F(1, 2, 1); +    } -	case INST_LOAD_ARRAY1: -	    opnd = TclGetUInt1AtPtr(pc+1); -	    pcAdjustment = 2; -	     -	    doLoadArray: -	    elemPtr = POP_OBJECT(); +    /* +     * ----------------------------------------------------------------- +     *	   Start of general introspector instructions. +     */ + +    case INST_NS_CURRENT: { +	Namespace *currNsPtr = (Namespace *) TclGetCurrentNamespace(interp); +	if (currNsPtr == (Namespace *) TclGetGlobalNamespace(interp)) { +	    TclNewLiteralStringObj(objResultPtr, "::"); +	} else { +	    TclNewStringObj(objResultPtr, currNsPtr->fullName, +		    strlen(currNsPtr->fullName)); +	} +	TRACE_WITH_OBJ(("=> "), objResultPtr); +	NEXT_INST_F(1, 0, 1); +    } +    case INST_COROUTINE_NAME: { +	CoroutineData *corPtr = iPtr->execEnvPtr->corPtr; + +	TclNewObj(objResultPtr); +	if (corPtr && !(corPtr->cmdPtr->flags & CMD_IS_DELETED)) { +	    Tcl_GetCommandFullName(interp, (Tcl_Command) corPtr->cmdPtr, +		    objResultPtr); +	} +	TRACE_WITH_OBJ(("=> "), objResultPtr); +	NEXT_INST_F(1, 0, 1); +    } +    case INST_INFO_LEVEL_NUM: +	TclNewIntObj(objResultPtr, iPtr->varFramePtr->level); +	TRACE_WITH_OBJ(("=> "), objResultPtr); +	NEXT_INST_F(1, 0, 1); +    case INST_INFO_LEVEL_ARGS: { +	int level; +	register CallFrame *framePtr = iPtr->varFramePtr; +	register CallFrame *rootFramePtr = iPtr->rootFramePtr; + +	TRACE(("\"%.30s\" => ", O2S(OBJ_AT_TOS))); +	if (TclGetIntFromObj(interp, OBJ_AT_TOS, &level) != TCL_OK) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} +	if (level <= 0) { +	    level += framePtr->level; +	} +	for (; (framePtr->level!=level) && (framePtr!=rootFramePtr) ; +		framePtr = framePtr->callerVarPtr) { +	    /* Empty loop body */ +	} +	if (framePtr == rootFramePtr) { +	    Tcl_SetObjResult(interp, Tcl_ObjPrintf( +		    "bad level \"%s\"", TclGetString(OBJ_AT_TOS))); +	    TRACE_ERROR(interp);  	    DECACHE_STACK_INFO(); -	    valuePtr = TclGetElementOfIndexedArray(interp, opnd, -		    elemPtr, TCL_LEAVE_ERR_MSG); +	    Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "STACK_LEVEL", +		    TclGetString(OBJ_AT_TOS), NULL);  	    CACHE_STACK_INFO(); -	    if (valuePtr == NULL) { -		TRACE_WITH_OBJ(("%u \"%.30s\" => ERROR: ", -			opnd, O2S(elemPtr)), Tcl_GetObjResult(interp)); -		Tcl_DecrRefCount(elemPtr); -		result = TCL_ERROR; -		goto checkForCatch; -	    } -	    PUSH_OBJECT(valuePtr); -	    TRACE_WITH_OBJ(("%u \"%.30s\" => ", -		    opnd, O2S(elemPtr)),valuePtr); -	    TclDecrRefCount(elemPtr); -	    ADJUST_PC(pcAdjustment); - -	case INST_LOAD_ARRAY_STK: -	    elemPtr = POP_OBJECT(); -	    objPtr = POP_OBJECT();	/* array name */ +	    goto gotError; +	} +	objResultPtr = Tcl_NewListObj(framePtr->objc, framePtr->objv); +	TRACE_APPEND(("%.30s\n", O2S(objResultPtr))); +	NEXT_INST_F(1, 1, 1); +    } +    { +	Tcl_Command cmd, origCmd; + +    case INST_RESOLVE_COMMAND: +	cmd = Tcl_GetCommandFromObj(interp, OBJ_AT_TOS); +	TclNewObj(objResultPtr); +	if (cmd != NULL) { +	    Tcl_GetCommandFullName(interp, cmd, objResultPtr); +	} +	TRACE_WITH_OBJ(("\"%.20s\" => ", O2S(OBJ_AT_TOS)), objResultPtr); +	NEXT_INST_F(1, 1, 1); + +    case INST_ORIGIN_COMMAND: +	TRACE(("\"%.30s\" => ", O2S(OBJ_AT_TOS))); +	cmd = Tcl_GetCommandFromObj(interp, OBJ_AT_TOS); +	if (cmd == NULL) { +	    Tcl_SetObjResult(interp, Tcl_ObjPrintf( +		    "invalid command name \"%s\"", TclGetString(OBJ_AT_TOS)));  	    DECACHE_STACK_INFO(); -	    valuePtr = Tcl_ObjGetVar2(interp, objPtr, elemPtr, -		    TCL_LEAVE_ERR_MSG); +	    Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "COMMAND", +		    TclGetString(OBJ_AT_TOS), NULL);  	    CACHE_STACK_INFO(); -	    if (valuePtr == NULL) { -		TRACE_WITH_OBJ(("\"%.30s(%.30s)\" => ERROR: ", -			O2S(objPtr), O2S(elemPtr)), -			Tcl_GetObjResult(interp)); -		Tcl_DecrRefCount(objPtr); -		Tcl_DecrRefCount(elemPtr); -		result = TCL_ERROR; -		goto checkForCatch; -	    } -	    PUSH_OBJECT(valuePtr); -	    TRACE_WITH_OBJ(("\"%.30s(%.30s)\" => ", -		    O2S(objPtr), O2S(elemPtr)), valuePtr); -	    TclDecrRefCount(objPtr); -	    TclDecrRefCount(elemPtr); -	    ADJUST_PC(1); - -	case INST_STORE_SCALAR4: -	    opnd = TclGetUInt4AtPtr(pc+1); -	    pcAdjustment = 5; -	    goto doStoreScalar; +	    TRACE_APPEND(("ERROR: not command\n")); +	    goto gotError; +	} +	origCmd = TclGetOriginalCommand(cmd); +	if (origCmd == NULL) { +	    origCmd = cmd; +	} +	TclNewObj(objResultPtr); +	Tcl_GetCommandFullName(interp, origCmd, objResultPtr); +	TRACE_APPEND(("\"%.30s\"", O2S(OBJ_AT_TOS))); +	NEXT_INST_F(1, 1, 1); +    } -	case INST_STORE_SCALAR1: -	    opnd = TclGetUInt1AtPtr(pc+1); -	    pcAdjustment = 2; +    /* +     * ----------------------------------------------------------------- +     *	   Start of TclOO support instructions. +     */ -	  doStoreScalar: -	    valuePtr = POP_OBJECT(); +    { +	Object *oPtr; +	CallFrame *framePtr; +	CallContext *contextPtr; +	int skip, newDepth; + +    case INST_TCLOO_SELF: +	framePtr = iPtr->varFramePtr; +	if (framePtr == NULL || +		!(framePtr->isProcCallFrame & FRAME_IS_METHOD)) { +	    TRACE(("=> ERROR: no TclOO call context\n")); +	    Tcl_SetObjResult(interp, Tcl_NewStringObj( +		    "self may only be called from inside a method", +		    -1));  	    DECACHE_STACK_INFO(); -	    value2Ptr = TclSetIndexedScalar(interp, opnd, valuePtr, -	            TCL_LEAVE_ERR_MSG); +	    Tcl_SetErrorCode(interp, "TCL", "OO", "CONTEXT_REQUIRED", NULL);  	    CACHE_STACK_INFO(); -	    if (value2Ptr == NULL) { -		TRACE_WITH_OBJ(("%u <- \"%.30s\" => ERROR: ", -			opnd, O2S(valuePtr)), Tcl_GetObjResult(interp)); -		Tcl_DecrRefCount(valuePtr); -		result = TCL_ERROR; -		goto checkForCatch; -	    } -	    PUSH_OBJECT(value2Ptr); -	    TRACE_WITH_OBJ(("%u <- \"%.30s\" => ", -		    opnd, O2S(valuePtr)), value2Ptr); -	    TclDecrRefCount(valuePtr); -	    ADJUST_PC(pcAdjustment); +	    goto gotError; +	} +	contextPtr = framePtr->clientData; -	case INST_STORE_STK: -	case INST_STORE_SCALAR_STK: -	    valuePtr = POP_OBJECT(); -	    objPtr = POP_OBJECT(); /* scalar / variable name */ +	/* +	 * Call out to get the name; it's expensive to compute but cached. +	 */ + +	objResultPtr = TclOOObjectName(interp, contextPtr->oPtr); +	TRACE_WITH_OBJ(("=> "), objResultPtr); +	NEXT_INST_F(1, 0, 1); + +    case INST_TCLOO_NEXT_CLASS: +	opnd = TclGetUInt1AtPtr(pc+1); +	framePtr = iPtr->varFramePtr; +	valuePtr = OBJ_AT_DEPTH(opnd - 2); +	objv = &OBJ_AT_DEPTH(opnd - 1); +	skip = 2; +	TRACE(("%d => ", opnd)); +	if (framePtr == NULL || +		!(framePtr->isProcCallFrame & FRAME_IS_METHOD)) { +	    TRACE_APPEND(("ERROR: no TclOO call context\n")); +	    Tcl_SetObjResult(interp, Tcl_NewStringObj( +		    "nextto may only be called from inside a method", +		    -1));  	    DECACHE_STACK_INFO(); -	    value2Ptr = Tcl_ObjSetVar2(interp, objPtr, NULL, valuePtr, -		    TCL_LEAVE_ERR_MSG); +	    Tcl_SetErrorCode(interp, "TCL", "OO", "CONTEXT_REQUIRED", NULL);  	    CACHE_STACK_INFO(); -	    if (value2Ptr == NULL) { -		TRACE_WITH_OBJ(("\"%.30s\" <- \"%.30s\" => ERROR: ", -		        O2S(objPtr), O2S(valuePtr)), -			Tcl_GetObjResult(interp)); -		Tcl_DecrRefCount(objPtr); -		Tcl_DecrRefCount(valuePtr); -		result = TCL_ERROR; -		goto checkForCatch; +	    goto gotError; +	} +	contextPtr = framePtr->clientData; + +	oPtr = (Object *) Tcl_GetObjectFromObj(interp, valuePtr); +	if (oPtr == NULL) { +	    TRACE_APPEND(("ERROR: \"%.30s\" not object\n", O2S(valuePtr))); +	    goto gotError; +	} else { +	    Class *classPtr = oPtr->classPtr; +	    struct MInvoke *miPtr; +	    int i; +	    const char *methodType; + +	    if (classPtr == NULL) { +		TRACE_APPEND(("ERROR: \"%.30s\" not class\n", O2S(valuePtr))); +		Tcl_SetObjResult(interp, Tcl_ObjPrintf( +			"\"%s\" is not a class", TclGetString(valuePtr))); +		DECACHE_STACK_INFO(); +		Tcl_SetErrorCode(interp, "TCL", "OO", "CLASS_REQUIRED", NULL); +		CACHE_STACK_INFO(); +		goto gotError;  	    } -	    PUSH_OBJECT(value2Ptr); -	    TRACE_WITH_OBJ(("\"%.30s\" <- \"%.30s\" => ", -		    O2S(objPtr), O2S(valuePtr)), value2Ptr); -	    TclDecrRefCount(objPtr); -	    TclDecrRefCount(valuePtr); -	    ADJUST_PC(1); -	case INST_STORE_ARRAY4: -	    opnd = TclGetUInt4AtPtr(pc+1); -	    pcAdjustment = 5; -	    goto doStoreArray; +	    for (i=contextPtr->index+1 ; i<contextPtr->callPtr->numChain ; i++) { +		miPtr = contextPtr->callPtr->chain + i; +		if (!miPtr->isFilter && +			miPtr->mPtr->declaringClassPtr == classPtr) { +		    newDepth = i; +#ifdef TCL_COMPILE_DEBUG +		    if (tclTraceExec >= 2) { +			if (traceInstructions) { +			    strncpy(cmdNameBuf, TclGetString(objv[0]), 20); +			} else { +			    fprintf(stdout, "%d: (%u) invoking ", +				    iPtr->numLevels, +				    (unsigned)(pc - codePtr->codeStart)); +			} +			for (i = 0;  i < opnd;  i++) { +			    TclPrintObject(stdout, objv[i], 15); +			    fprintf(stdout, " "); +			} +			fprintf(stdout, "\n"); +			fflush(stdout); +		    } +#endif /*TCL_COMPILE_DEBUG*/ +		    goto doInvokeNext; +		} +	    } -	case INST_STORE_ARRAY1: -	    opnd = TclGetUInt1AtPtr(pc+1); -	    pcAdjustment = 2; -	     -	    doStoreArray: -	    valuePtr = POP_OBJECT(); -	    elemPtr = POP_OBJECT(); +	    if (contextPtr->callPtr->flags & CONSTRUCTOR) { +		methodType = "constructor"; +	    } else if (contextPtr->callPtr->flags & DESTRUCTOR) { +		methodType = "destructor"; +	    } else { +		methodType = "method"; +	    } + +	    TRACE_APPEND(("ERROR: \"%.30s\" not on reachable chain\n", +		    O2S(valuePtr))); +	    for (i=contextPtr->index ; i>=0 ; i--) { +		miPtr = contextPtr->callPtr->chain + i; +		if (miPtr->isFilter +			|| miPtr->mPtr->declaringClassPtr != classPtr) { +		    continue; +		} +		Tcl_SetObjResult(interp, Tcl_ObjPrintf( +			"%s implementation by \"%s\" not reachable from here", +			methodType, TclGetString(valuePtr))); +		DECACHE_STACK_INFO(); +		Tcl_SetErrorCode(interp, "TCL", "OO", "CLASS_NOT_REACHABLE", +			NULL); +		CACHE_STACK_INFO(); +		goto gotError; +	    } +	    Tcl_SetObjResult(interp, Tcl_ObjPrintf( +		    "%s has no non-filter implementation by \"%s\"", +		    methodType, TclGetString(valuePtr)));  	    DECACHE_STACK_INFO(); -	    value2Ptr = TclSetElementOfIndexedArray(interp, opnd, -		    elemPtr, valuePtr, TCL_LEAVE_ERR_MSG); +	    Tcl_SetErrorCode(interp, "TCL", "OO", "CLASS_NOT_THERE", NULL);  	    CACHE_STACK_INFO(); -	    if (value2Ptr == NULL) { -		TRACE_WITH_OBJ(("%u \"%.30s\" <- \"%.30s\" => ERROR: ", -			opnd, O2S(elemPtr), O2S(valuePtr)), -			Tcl_GetObjResult(interp)); -		Tcl_DecrRefCount(elemPtr); -		Tcl_DecrRefCount(valuePtr); -		result = TCL_ERROR; -		goto checkForCatch; -	    } -	    PUSH_OBJECT(value2Ptr); -	    TRACE_WITH_OBJ(("%u \"%.30s\" <- \"%.30s\" => ", -		    opnd, O2S(elemPtr), O2S(valuePtr)), value2Ptr); -	    TclDecrRefCount(elemPtr); -	    TclDecrRefCount(valuePtr); -	    ADJUST_PC(pcAdjustment); +	    goto gotError; +	} -	case INST_STORE_ARRAY_STK: -	    valuePtr = POP_OBJECT(); -	    elemPtr = POP_OBJECT(); -	    objPtr = POP_OBJECT();	/* array name */ +    case INST_TCLOO_NEXT: +	opnd = TclGetUInt1AtPtr(pc+1); +	objv = &OBJ_AT_DEPTH(opnd - 1); +	framePtr = iPtr->varFramePtr; +	skip = 1; +	TRACE(("%d => ", opnd)); +	if (framePtr == NULL || +		!(framePtr->isProcCallFrame & FRAME_IS_METHOD)) { +	    TRACE_APPEND(("ERROR: no TclOO call context\n")); +	    Tcl_SetObjResult(interp, Tcl_NewStringObj( +		    "next may only be called from inside a method", +		    -1));  	    DECACHE_STACK_INFO(); -	    value2Ptr = Tcl_ObjSetVar2(interp, objPtr, elemPtr, valuePtr, -		    TCL_LEAVE_ERR_MSG); +	    Tcl_SetErrorCode(interp, "TCL", "OO", "CONTEXT_REQUIRED", NULL);  	    CACHE_STACK_INFO(); -	    if (value2Ptr == NULL) { -		TRACE_WITH_OBJ(("\"%.30s(%.30s)\" <- \"%.30s\" => ERROR: ", -			O2S(objPtr), O2S(elemPtr), O2S(valuePtr)), -			Tcl_GetObjResult(interp)); -		Tcl_DecrRefCount(objPtr); -		Tcl_DecrRefCount(elemPtr); -		Tcl_DecrRefCount(valuePtr); -		result = TCL_ERROR; -		goto checkForCatch; -	    } -	    PUSH_OBJECT(value2Ptr); -	    TRACE_WITH_OBJ(("\"%.30s(%.30s)\" <- \"%.30s\" => ", -		    O2S(objPtr), O2S(elemPtr), O2S(valuePtr)), -		    value2Ptr); -	    TclDecrRefCount(objPtr); -	    TclDecrRefCount(elemPtr); -	    TclDecrRefCount(valuePtr); -	    ADJUST_PC(1); +	    goto gotError; +	} +	contextPtr = framePtr->clientData; +	newDepth = contextPtr->index + 1; +	if (newDepth >= contextPtr->callPtr->numChain) {  	    /* -	     * START APPEND INSTRUCTIONS +	     * We're at the end of the chain; generate an error message unless +	     * the interpreter is being torn down, in which case we might be +	     * getting here because of methods/destructors doing a [next] (or +	     * equivalent) unexpectedly.  	     */ -	case INST_APPEND_SCALAR4: -	    opnd = TclGetUInt4AtPtr(pc+1); -	    pcAdjustment = 5; -	    goto doAppendScalar; +	    const char *methodType; -	case INST_APPEND_SCALAR1: -	    opnd = TclGetUInt1AtPtr(pc+1); -	    pcAdjustment = 2; +	    if (contextPtr->callPtr->flags & CONSTRUCTOR) { +		methodType = "constructor"; +	    } else if (contextPtr->callPtr->flags & DESTRUCTOR) { +		methodType = "destructor"; +	    } else { +		methodType = "method"; +	    } -	  doAppendScalar: -	    valuePtr = POP_OBJECT(); +	    TRACE_APPEND(("ERROR: no TclOO next impl\n")); +	    Tcl_SetObjResult(interp, Tcl_ObjPrintf( +		    "no next %s implementation", methodType));  	    DECACHE_STACK_INFO(); -	    value2Ptr = TclSetIndexedScalar(interp, opnd, valuePtr, -	            TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE); +	    Tcl_SetErrorCode(interp, "TCL", "OO", "NOTHING_NEXT", NULL);  	    CACHE_STACK_INFO(); -	    if (value2Ptr == NULL) { -		TRACE_WITH_OBJ(("%u <- \"%.30s\" => ERROR: ", -			opnd, O2S(valuePtr)), Tcl_GetObjResult(interp)); -		Tcl_DecrRefCount(valuePtr); -		result = TCL_ERROR; -		goto checkForCatch; -	    } -	    PUSH_OBJECT(value2Ptr); -	    TRACE_WITH_OBJ(("%u <- \"%.30s\" => ", -		    opnd, O2S(valuePtr)), value2Ptr); -	    TclDecrRefCount(valuePtr); -	    ADJUST_PC(pcAdjustment); +	    goto gotError; +#ifdef TCL_COMPILE_DEBUG +	} else if (tclTraceExec >= 2) { +	    int i; -	case INST_APPEND_STK: -	case INST_APPEND_ARRAY_STK: -	    valuePtr = POP_OBJECT(); /* value to append */ -	    if (*pc == INST_APPEND_ARRAY_STK) { -		elemPtr = POP_OBJECT(); +	    if (traceInstructions) { +		strncpy(cmdNameBuf, TclGetString(objv[0]), 20);  	    } else { -		elemPtr = NULL; +		fprintf(stdout, "%d: (%u) invoking ", +			iPtr->numLevels, (unsigned)(pc - codePtr->codeStart));  	    } -	    objPtr = POP_OBJECT(); /* scalar name */ +	    for (i = 0;  i < opnd;  i++) { +		TclPrintObject(stdout, objv[i], 15); +		fprintf(stdout, " "); +	    } +	    fprintf(stdout, "\n"); +	    fflush(stdout); +#endif /*TCL_COMPILE_DEBUG*/ +	} -	    DECACHE_STACK_INFO(); -	    value2Ptr = Tcl_ObjSetVar2(interp, objPtr, elemPtr, valuePtr, -		    TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE); -	    CACHE_STACK_INFO(); -	    if (value2Ptr == NULL) { -		if (elemPtr) { -		    TRACE_WITH_OBJ(("\"%.30s(%.30s)\" <-+ \"%.30s\" => ERROR: ", -			    O2S(objPtr), O2S(elemPtr), O2S(valuePtr)), -			    Tcl_GetObjResult(interp)); -		    TclDecrRefCount(elemPtr); -		} else { -		    TRACE_WITH_OBJ(("\"%.30s\" <-+ \"%.30s\" => ERROR: ", -			    O2S(objPtr), O2S(valuePtr)), -			    Tcl_GetObjResult(interp)); -		} -		Tcl_DecrRefCount(objPtr); -		Tcl_DecrRefCount(valuePtr); -		result = TCL_ERROR; -		goto checkForCatch; +    doInvokeNext: +	bcFramePtr->data.tebc.pc = (char *) pc; +	iPtr->cmdFramePtr = bcFramePtr; + +	if (iPtr->flags & INTERP_DEBUG_FRAME) { +	    ArgumentBCEnter(interp, codePtr, TD, pc, opnd, objv); +	} + +	pcAdjustment = 2; +	cleanup = opnd; +	DECACHE_STACK_INFO(); +	iPtr->varFramePtr = framePtr->callerVarPtr; +	pc += pcAdjustment; +	TEBC_YIELD(); + +	oPtr = contextPtr->oPtr; +	if (oPtr->flags & FILTER_HANDLING) { +	    TclNRAddCallback(interp, FinalizeOONextFilter, +		    framePtr, contextPtr, INT2PTR(contextPtr->index), +		    INT2PTR(contextPtr->skip)); +	} else { +	    TclNRAddCallback(interp, FinalizeOONext, +		    framePtr, contextPtr, INT2PTR(contextPtr->index), +		    INT2PTR(contextPtr->skip)); +	} +	contextPtr->skip = skip; +	contextPtr->index = newDepth; +	if (contextPtr->callPtr->chain[newDepth].isFilter +		|| contextPtr->callPtr->flags & FILTER_HANDLING) { +	    oPtr->flags |= FILTER_HANDLING; +	} else { +	    oPtr->flags &= ~FILTER_HANDLING; +	} + +	{ +	    register Method *const mPtr = +		    contextPtr->callPtr->chain[newDepth].mPtr; + +	    return mPtr->typePtr->callProc(mPtr->clientData, interp, +		    (Tcl_ObjectContext) contextPtr, opnd, objv); +	} + +    case INST_TCLOO_IS_OBJECT: +	oPtr = (Object *) Tcl_GetObjectFromObj(interp, OBJ_AT_TOS); +	objResultPtr = TCONST(oPtr != NULL ? 1 : 0); +	TRACE_WITH_OBJ(("%.30s => ", O2S(OBJ_AT_TOS)), objResultPtr); +	NEXT_INST_F(1, 1, 1); +    case INST_TCLOO_CLASS: +	oPtr = (Object *) Tcl_GetObjectFromObj(interp, OBJ_AT_TOS); +	if (oPtr == NULL) { +	    TRACE(("%.30s => ERROR: not object\n", O2S(OBJ_AT_TOS))); +	    goto gotError; +	} +	objResultPtr = TclOOObjectName(interp, oPtr->selfCls->thisPtr); +	TRACE_WITH_OBJ(("%.30s => ", O2S(OBJ_AT_TOS)), objResultPtr); +	NEXT_INST_F(1, 1, 1); +    case INST_TCLOO_NS: +	oPtr = (Object *) Tcl_GetObjectFromObj(interp, OBJ_AT_TOS); +	if (oPtr == NULL) { +	    TRACE(("%.30s => ERROR: not object\n", O2S(OBJ_AT_TOS))); +	    goto gotError; +	} + +	/* +	 * TclOO objects *never* have the global namespace as their NS. +	 */ + +	TclNewStringObj(objResultPtr, oPtr->namespacePtr->fullName, +		strlen(oPtr->namespacePtr->fullName)); +	TRACE_WITH_OBJ(("%.30s => ", O2S(OBJ_AT_TOS)), objResultPtr); +	NEXT_INST_F(1, 1, 1); +    } + +    /* +     *     End of TclOO support instructions. +     * ----------------------------------------------------------------- +     *	   Start of INST_LIST and related instructions. +     */ + +    { +	int index, numIndices, fromIdx, toIdx; +	int nocase, match, length2, cflags, s1len, s2len; +	const char *s1, *s2; + +    case INST_LIST: +	/* +	 * Pop the opnd (objc) top stack elements into a new list obj and then +	 * decrement their ref counts. +	 */ + +	opnd = TclGetUInt4AtPtr(pc+1); +	objResultPtr = Tcl_NewListObj(opnd, &OBJ_AT_DEPTH(opnd-1)); +	TRACE_WITH_OBJ(("%u => ", opnd), objResultPtr); +	NEXT_INST_V(5, opnd, 1); + +    case INST_LIST_LENGTH: +	TRACE(("\"%.30s\" => ", O2S(OBJ_AT_TOS))); +	if (TclListObjLength(interp, OBJ_AT_TOS, &length) != TCL_OK) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} +	TclNewIntObj(objResultPtr, length); +	TRACE_APPEND(("%d\n", length)); +	NEXT_INST_F(1, 1, 1); + +    case INST_LIST_INDEX:	/* lindex with objc == 3 */ +	value2Ptr = OBJ_AT_TOS; +	valuePtr = OBJ_UNDER_TOS; +	TRACE(("\"%.30s\" \"%.30s\" => ", O2S(valuePtr), O2S(value2Ptr))); + +	/* +	 * Extract the desired list element. +	 */ + +	if ((TclListObjGetElements(interp, valuePtr, &objc, &objv) == TCL_OK) +		&& (value2Ptr->typePtr != &tclListType) +		&& (TclGetIntForIndexM(NULL , value2Ptr, objc-1, +			&index) == TCL_OK)) { +	    TclDecrRefCount(value2Ptr); +	    tosPtr--; +	    pcAdjustment = 1; +	    goto lindexFastPath; +	} + +	objResultPtr = TclLindexList(interp, valuePtr, value2Ptr); +	if (!objResultPtr) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} + +	/* +	 * Stash the list element on the stack. +	 */ + +	TRACE_APPEND(("\"%.30s\"\n", O2S(objResultPtr))); +	NEXT_INST_F(1, 2, -1);	/* Already has the correct refCount */ + +    case INST_LIST_INDEX_IMM:	/* lindex with objc==3 and index in bytecode +				 * stream */ + +	/* +	 * Pop the list and get the index. +	 */ + +	valuePtr = OBJ_AT_TOS; +	opnd = TclGetInt4AtPtr(pc+1); +	TRACE(("\%.30s\" %d => ", O2S(valuePtr), opnd)); + +	/* +	 * Get the contents of the list, making sure that it really is a list +	 * in the process. +	 */ + +	if (TclListObjGetElements(interp, valuePtr, &objc, &objv) != TCL_OK) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} + +	/* +	 * Select the list item based on the index. Negative operand means +	 * end-based indexing. +	 */ + +	if (opnd < -1) { +	    index = opnd+1 + objc; +	} else { +	    index = opnd; +	} +	pcAdjustment = 5; + +    lindexFastPath: +	if (index >= 0 && index < objc) { +	    objResultPtr = objv[index]; +	} else { +	    TclNewObj(objResultPtr); +	} + +	TRACE_APPEND(("\"%.30s\"\n", O2S(objResultPtr))); +	NEXT_INST_F(pcAdjustment, 1, 1); + +    case INST_LIST_INDEX_MULTI:	/* 'lindex' with multiple index args */ +	/* +	 * Determine the count of index args. +	 */ + +	opnd = TclGetUInt4AtPtr(pc+1); +	numIndices = opnd-1; + +	/* +	 * Do the 'lindex' operation. +	 */ + +	TRACE(("%d => ", opnd)); +	objResultPtr = TclLindexFlat(interp, OBJ_AT_DEPTH(numIndices), +		numIndices, &OBJ_AT_DEPTH(numIndices - 1)); +	if (!objResultPtr) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} + +	/* +	 * Set result. +	 */ + +	TRACE_APPEND(("\"%.30s\"\n", O2S(objResultPtr))); +	NEXT_INST_V(5, opnd, -1); + +    case INST_LSET_FLAT: +	/* +	 * Lset with 3, 5, or more args. Get the number of index args. +	 */ + +	opnd = TclGetUInt4AtPtr(pc + 1); +	numIndices = opnd - 2; +	TRACE(("%d => ", opnd)); + +	/* +	 * Get the old value of variable, and remove the stack ref. This is +	 * safe because the variable still references the object; the ref +	 * count will never go zero here - we can use the smaller macro +	 * Tcl_DecrRefCount. +	 */ + +	valuePtr = POP_OBJECT(); +	Tcl_DecrRefCount(valuePtr); /* This one should be done here */ + +	/* +	 * Compute the new variable value. +	 */ + +	objResultPtr = TclLsetFlat(interp, valuePtr, numIndices, +		&OBJ_AT_DEPTH(numIndices), OBJ_AT_TOS); +	if (!objResultPtr) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} + +	/* +	 * Set result. +	 */ + +	TRACE_APPEND(("\"%.30s\"\n", O2S(objResultPtr))); +	NEXT_INST_V(5, numIndices+1, -1); + +    case INST_LSET_LIST:	/* 'lset' with 4 args */ +	/* +	 * Get the old value of variable, and remove the stack ref. This is +	 * safe because the variable still references the object; the ref +	 * count will never go zero here - we can use the smaller macro +	 * Tcl_DecrRefCount. +	 */ + +	objPtr = POP_OBJECT(); +	Tcl_DecrRefCount(objPtr);	/* This one should be done here. */ + +	/* +	 * Get the new element value, and the index list. +	 */ + +	valuePtr = OBJ_AT_TOS; +	value2Ptr = OBJ_UNDER_TOS; +	TRACE(("\"%.30s\" \"%.30s\" \"%.30s\" => ", +		O2S(value2Ptr), O2S(valuePtr), O2S(objPtr))); + +	/* +	 * Compute the new variable value. +	 */ + +	objResultPtr = TclLsetList(interp, objPtr, value2Ptr, valuePtr); +	if (!objResultPtr) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} + +	/* +	 * Set result. +	 */ + +	TRACE_APPEND(("\"%.30s\"\n", O2S(objResultPtr))); +	NEXT_INST_F(1, 2, -1); + +    case INST_LIST_RANGE_IMM:	/* lrange with objc==4 and both indices in +				 * bytecode stream */ + +	/* +	 * Pop the list and get the indices. +	 */ + +	valuePtr = OBJ_AT_TOS; +	fromIdx = TclGetInt4AtPtr(pc+1); +	toIdx = TclGetInt4AtPtr(pc+5); +	TRACE(("\"%.30s\" %d %d => ", O2S(valuePtr), TclGetInt4AtPtr(pc+1), +		TclGetInt4AtPtr(pc+5))); + +	/* +	 * Get the contents of the list, making sure that it really is a list +	 * in the process. +	 */ + +	if (TclListObjGetElements(interp, valuePtr, &objc, &objv) != TCL_OK) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} + +	/* +	 * Skip a lot of work if we're about to throw the result away (common +	 * with uses of [lassign]). +	 */ + +#ifndef TCL_COMPILE_DEBUG +	if (*(pc+9) == INST_POP) { +	    NEXT_INST_F(10, 1, 0); +	} +#endif + +	/* +	 * Adjust the indices for end-based handling. +	 */ + +	if (fromIdx < -1) { +	    fromIdx += 1+objc; +	    if (fromIdx < -1) { +		fromIdx = -1;  	    } -	    PUSH_OBJECT(value2Ptr); -	    if (elemPtr) { -		TRACE_WITH_OBJ(("\"%.30s(%.30s)\" <-+ \"%.30s\" => ", -			O2S(objPtr), O2S(elemPtr), O2S(valuePtr)), -			value2Ptr); -		TclDecrRefCount(elemPtr); -	    } else { -		TRACE_WITH_OBJ(("\"%.30s\" <-+ \"%.30s\" => ", -			O2S(objPtr), O2S(valuePtr)), value2Ptr); +	} else if (fromIdx > objc) { +	    fromIdx = objc; +	} +	if (toIdx < -1) { +	    toIdx += 1 + objc; +	    if (toIdx < -1) { +		toIdx = -1;  	    } -	    TclDecrRefCount(objPtr); -	    TclDecrRefCount(valuePtr); -	    ADJUST_PC(1); +	} else if (toIdx > objc) { +	    toIdx = objc; +	} -	case INST_APPEND_ARRAY4: -	    opnd = TclGetUInt4AtPtr(pc+1); -	    pcAdjustment = 5; -	    goto doAppendArray; +	/* +	 * Check if we are referring to a valid, non-empty list range, and if +	 * so, build the list of elements in that range. +	 */ -	case INST_APPEND_ARRAY1: -	    opnd = TclGetUInt1AtPtr(pc+1); -	    pcAdjustment = 2; -	     -	    doAppendArray: -	    valuePtr = POP_OBJECT(); -	    elemPtr = POP_OBJECT(); -	    DECACHE_STACK_INFO(); -	    value2Ptr = TclSetElementOfIndexedArray(interp, opnd, -		    elemPtr, valuePtr, TCL_LEAVE_ERR_MSG|TCL_APPEND_VALUE); -	    CACHE_STACK_INFO(); -	    if (value2Ptr == NULL) { -		TRACE_WITH_OBJ(("%u \"%.30s\" <-+ \"%.30s\" => ERROR: ", -			opnd, O2S(elemPtr), O2S(valuePtr)), -			Tcl_GetObjResult(interp)); -		Tcl_DecrRefCount(elemPtr); -		Tcl_DecrRefCount(valuePtr); -		result = TCL_ERROR; -		goto checkForCatch; +	if (fromIdx<=toIdx && fromIdx<objc && toIdx>=0) { +	    if (fromIdx < 0) { +		fromIdx = 0;  	    } -	    PUSH_OBJECT(value2Ptr); -	    TRACE_WITH_OBJ(("%u \"%.30s\" <-+ \"%.30s\" => ", -		    opnd, O2S(elemPtr), O2S(valuePtr)), value2Ptr); -	    TclDecrRefCount(elemPtr); -	    TclDecrRefCount(valuePtr); -	    ADJUST_PC(pcAdjustment); +	    if (toIdx >= objc) { +		toIdx = objc-1; +	    } +	    if (fromIdx == 0 && toIdx != objc-1 && !Tcl_IsShared(valuePtr)) { +		/* +		 * BEWARE! This is looking inside the implementation of the +		 * list type. +		 */ + +		List *listPtr = valuePtr->internalRep.twoPtrValue.ptr1; + +		if (listPtr->refCount == 1) { +		    for (index=toIdx+1; index<objc ; index++) { +			TclDecrRefCount(objv[index]); +		    } +		    listPtr->elemCount = toIdx+1; +		    listPtr->canonicalFlag = 1; +		    TclInvalidateStringRep(valuePtr); +		    TRACE_APPEND(("%.30s\n", O2S(valuePtr))); +		    NEXT_INST_F(9, 0, 0); +		} +	    } +	    objResultPtr = Tcl_NewListObj(toIdx-fromIdx+1, objv+fromIdx); +	} else { +	    TclNewObj(objResultPtr); +	} + +	TRACE_APPEND(("\"%.30s\"", O2S(objResultPtr))); +	NEXT_INST_F(9, 1, 1); + +    case INST_LIST_IN: +    case INST_LIST_NOT_IN:	/* Basic list containment operators. */ +	value2Ptr = OBJ_AT_TOS; +	valuePtr = OBJ_UNDER_TOS; + +	s1 = TclGetStringFromObj(valuePtr, &s1len); +	TRACE(("\"%.30s\" \"%.30s\" => ", O2S(valuePtr), O2S(value2Ptr))); +	if (TclListObjLength(interp, value2Ptr, &length) != TCL_OK) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} +	match = 0; +	if (length > 0) { +	    int i = 0; +	    Tcl_Obj *o;  	    /* -	     * END APPEND INSTRUCTIONS +	     * An empty list doesn't match anything.  	     */ + +	    do { +		Tcl_ListObjIndex(NULL, value2Ptr, i, &o); +		if (o != NULL) { +		    s2 = TclGetStringFromObj(o, &s2len); +		} else { +		    s2 = ""; +		    s2len = 0; +		} +		if (s1len == s2len) { +		    match = (memcmp(s1, s2, s1len) == 0); +		} +		i++; +	    } while (i < length && match == 0); +	} + +	if (*pc == INST_LIST_NOT_IN) { +	    match = !match; +	} + +	TRACE_APPEND(("%d\n", match)); + +	/* +	 * Peep-hole optimisation: if you're about to jump, do jump from here. +	 * We're saving the effort of pushing a boolean value only to pop it +	 * for branching. +	 */ + +	JUMP_PEEPHOLE_F(match, 1, 2); + +    case INST_LIST_CONCAT: +	value2Ptr = OBJ_AT_TOS; +	valuePtr = OBJ_UNDER_TOS; +	TRACE(("\"%.30s\" \"%.30s\" => ", O2S(valuePtr), O2S(value2Ptr))); +	if (Tcl_IsShared(valuePtr)) { +	    objResultPtr = Tcl_DuplicateObj(valuePtr); +	    if (Tcl_ListObjAppendList(interp, objResultPtr, +		    value2Ptr) != TCL_OK) { +		TRACE_ERROR(interp); +		TclDecrRefCount(objResultPtr); +		goto gotError; +	    } +	    TRACE_APPEND(("\"%.30s\"\n", O2S(objResultPtr))); +	    NEXT_INST_F(1, 2, 1); +	} else { +	    if (Tcl_ListObjAppendList(interp, valuePtr, value2Ptr) != TCL_OK){ +		TRACE_ERROR(interp); +		goto gotError; +	    } +	    TRACE_APPEND(("\"%.30s\"\n", O2S(valuePtr))); +	    NEXT_INST_F(1, 1, 0); +	} + +    /* +     *	   End of INST_LIST and related instructions. +     * ----------------------------------------------------------------- +     *	   Start of string-related instructions. +     */ + +    case INST_STR_EQ: +    case INST_STR_NEQ:		/* String (in)equality check */ +    case INST_STR_CMP:		/* String compare. */ +    stringCompare: +	value2Ptr = OBJ_AT_TOS; +	valuePtr = OBJ_UNDER_TOS; + +	if (valuePtr == value2Ptr) { +	    match = 0; +	} else {  	    /* -	     * START LAPPEND INSTRUCTIONS +	     * We only need to check (in)equality when we have equal length +	     * strings.  We can use memcmp in all (n)eq cases because we +	     * don't need to worry about lexical LE/BE variance.  	     */ -	case INST_LAPPEND_SCALAR4: -	    opnd = TclGetUInt4AtPtr(pc+1); -	    pcAdjustment = 5; -	    goto doLappendScalar; +	    typedef int (*memCmpFn_t)(const void*, const void*, size_t); +	    memCmpFn_t memCmpFn; +	    int checkEq = ((*pc == INST_EQ) || (*pc == INST_NEQ) +		    || (*pc == INST_STR_EQ) || (*pc == INST_STR_NEQ)); + +	    if (TclIsPureByteArray(valuePtr) +		    && TclIsPureByteArray(value2Ptr)) { +		s1 = (char *) Tcl_GetByteArrayFromObj(valuePtr, &s1len); +		s2 = (char *) Tcl_GetByteArrayFromObj(value2Ptr, &s2len); +		memCmpFn = memcmp; +	    } else if (((valuePtr->typePtr == &tclStringType) +		    && (value2Ptr->typePtr == &tclStringType))) { +		/* +		 * Do a unicode-specific comparison if both of the args are of +		 * String type. If the char length == byte length, we can do a +		 * memcmp. In benchmark testing this proved the most efficient +		 * check between the unicode and string comparison operations. +		 */ -	case INST_LAPPEND_SCALAR1: -	    opnd = TclGetUInt1AtPtr(pc+1); -	    pcAdjustment = 2; +		s1len = Tcl_GetCharLength(valuePtr); +		s2len = Tcl_GetCharLength(value2Ptr); +		if ((s1len == valuePtr->length) +			&& (s2len == value2Ptr->length)) { +		    s1 = valuePtr->bytes; +		    s2 = value2Ptr->bytes; +		    memCmpFn = memcmp; +		} else { +		    s1 = (char *) Tcl_GetUnicode(valuePtr); +		    s2 = (char *) Tcl_GetUnicode(value2Ptr); +		    if ( +#ifdef WORDS_BIGENDIAN +			1 +#else +			checkEq +#endif +			) { +			memCmpFn = memcmp; +			s1len *= sizeof(Tcl_UniChar); +			s2len *= sizeof(Tcl_UniChar); +		    } else { +			memCmpFn = (memCmpFn_t) Tcl_UniCharNcmp; +		    } +		} +	    } else { +		/* +		 * strcmp can't do a simple memcmp in order to handle the +		 * special Tcl \xC0\x80 null encoding for utf-8. +		 */ -	  doLappendScalar: -	    valuePtr = POP_OBJECT(); -	    DECACHE_STACK_INFO(); -	    value2Ptr = TclSetIndexedScalar(interp, opnd, valuePtr, -	            TCL_LEAVE_ERR_MSG|TCL_APPEND_VALUE|TCL_LIST_ELEMENT); -	    CACHE_STACK_INFO(); -	    if (value2Ptr == NULL) { -		TRACE_WITH_OBJ(("%u <- \"%.30s\" => ERROR: ", -			opnd, O2S(valuePtr)), Tcl_GetObjResult(interp)); -		Tcl_DecrRefCount(valuePtr); -		result = TCL_ERROR; -		goto checkForCatch; +		s1 = TclGetStringFromObj(valuePtr, &s1len); +		s2 = TclGetStringFromObj(value2Ptr, &s2len); +		if (checkEq) { +		    memCmpFn = memcmp; +		} else { +		    memCmpFn = (memCmpFn_t) TclpUtfNcmp2; +		}  	    } -	    PUSH_OBJECT(value2Ptr); -	    TRACE_WITH_OBJ(("%u <- \"%.30s\" => ", -		    opnd, O2S(valuePtr)), value2Ptr); -	    TclDecrRefCount(valuePtr); -	    ADJUST_PC(pcAdjustment); -	case INST_LAPPEND_STK: -	case INST_LAPPEND_ARRAY_STK: -	{ +	    if (checkEq && (s1len != s2len)) { +		match = 1; +	    } else { +		/* +		 * The comparison function should compare up to the minimum +		 * byte length only. +		 */ +		match = memCmpFn(s1, s2, +			(size_t) ((s1len < s2len) ? s1len : s2len)); +		if (match == 0) { +		    match = s1len - s2len; +		} +	    } +	} + +	/* +	 * Make sure only -1,0,1 is returned +	 * TODO: consider peephole opt. +	 */ + +	if (*pc != INST_STR_CMP) {  	    /* -	     * This compile function for this should be refactored -	     * to make better use of existing LOAD/STORE instructions. +	     * Take care of the opcodes that goto'ed into here.  	     */ -	    Tcl_Obj *newValuePtr; -	    int createdNewObj = 0; -	    value2Ptr = POP_OBJECT(); /* value to append */ -	    if (*pc == INST_LAPPEND_ARRAY_STK) { -		elemPtr = POP_OBJECT(); -	    } else { -		elemPtr = NULL; +	    switch (*pc) { +	    case INST_STR_EQ: +	    case INST_EQ: +		match = (match == 0); +		break; +	    case INST_STR_NEQ: +	    case INST_NEQ: +		match = (match != 0); +		break; +	    case INST_LT: +		match = (match < 0); +		break; +	    case INST_GT: +		match = (match > 0); +		break; +	    case INST_LE: +		match = (match <= 0); +		break; +	    case INST_GE: +		match = (match >= 0); +		break;  	    } -	    objPtr = POP_OBJECT(); /* scalar name */ +	} + +	TRACE(("\"%.20s\" \"%.20s\" => %d\n", O2S(valuePtr), O2S(value2Ptr), +		(match < 0 ? -1 : match > 0 ? 1 : 0))); +	JUMP_PEEPHOLE_F(match, 1, 2); + +    case INST_STR_LEN: +	valuePtr = OBJ_AT_TOS; +	length = Tcl_GetCharLength(valuePtr); +	TclNewIntObj(objResultPtr, length); +	TRACE(("\"%.20s\" => %d\n", O2S(valuePtr), length)); +	NEXT_INST_F(1, 1, 1); + +    case INST_STR_UPPER: +	valuePtr = OBJ_AT_TOS; +	TRACE(("\"%.20s\" => ", O2S(valuePtr))); +	if (Tcl_IsShared(valuePtr)) { +	    s1 = TclGetStringFromObj(valuePtr, &length); +	    TclNewStringObj(objResultPtr, s1, length); +	    length = Tcl_UtfToUpper(TclGetString(objResultPtr)); +	    Tcl_SetObjLength(objResultPtr, length); +	    TRACE_APPEND(("\"%.20s\"\n", O2S(objResultPtr))); +	    NEXT_INST_F(1, 1, 1); +	} else { +	    length = Tcl_UtfToUpper(TclGetString(valuePtr)); +	    Tcl_SetObjLength(valuePtr, length); +	    TclFreeIntRep(valuePtr); +	    TRACE_APPEND(("\"%.20s\"\n", O2S(valuePtr))); +	    NEXT_INST_F(1, 0, 0); +	} +    case INST_STR_LOWER: +	valuePtr = OBJ_AT_TOS; +	TRACE(("\"%.20s\" => ", O2S(valuePtr))); +	if (Tcl_IsShared(valuePtr)) { +	    s1 = TclGetStringFromObj(valuePtr, &length); +	    TclNewStringObj(objResultPtr, s1, length); +	    length = Tcl_UtfToLower(TclGetString(objResultPtr)); +	    Tcl_SetObjLength(objResultPtr, length); +	    TRACE_APPEND(("\"%.20s\"\n", O2S(objResultPtr))); +	    NEXT_INST_F(1, 1, 1); +	} else { +	    length = Tcl_UtfToLower(TclGetString(valuePtr)); +	    Tcl_SetObjLength(valuePtr, length); +	    TclFreeIntRep(valuePtr); +	    TRACE_APPEND(("\"%.20s\"\n", O2S(valuePtr))); +	    NEXT_INST_F(1, 0, 0); +	} +    case INST_STR_TITLE: +	valuePtr = OBJ_AT_TOS; +	TRACE(("\"%.20s\" => ", O2S(valuePtr))); +	if (Tcl_IsShared(valuePtr)) { +	    s1 = TclGetStringFromObj(valuePtr, &length); +	    TclNewStringObj(objResultPtr, s1, length); +	    length = Tcl_UtfToTitle(TclGetString(objResultPtr)); +	    Tcl_SetObjLength(objResultPtr, length); +	    TRACE_APPEND(("\"%.20s\"\n", O2S(objResultPtr))); +	    NEXT_INST_F(1, 1, 1); +	} else { +	    length = Tcl_UtfToTitle(TclGetString(valuePtr)); +	    Tcl_SetObjLength(valuePtr, length); +	    TclFreeIntRep(valuePtr); +	    TRACE_APPEND(("\"%.20s\"\n", O2S(valuePtr))); +	    NEXT_INST_F(1, 0, 0); +	} + +    case INST_STR_INDEX: +	value2Ptr = OBJ_AT_TOS; +	valuePtr = OBJ_UNDER_TOS; +	TRACE(("\"%.20s\" %.20s => ", O2S(valuePtr), O2S(value2Ptr))); + +	/* +	 * Get char length to calulate what 'end' means. +	 */ + +	length = Tcl_GetCharLength(valuePtr); +	if (TclGetIntForIndexM(interp, value2Ptr, length-1, &index)!=TCL_OK) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} + +	if ((index < 0) || (index >= length)) { +	    TclNewObj(objResultPtr); +	} else if (TclIsPureByteArray(valuePtr)) { +	    objResultPtr = Tcl_NewByteArrayObj( +		    Tcl_GetByteArrayFromObj(valuePtr, &length)+index, 1); +	} else if (valuePtr->bytes && length == valuePtr->length) { +	    objResultPtr = Tcl_NewStringObj((const char *) +		    valuePtr->bytes+index, 1); +	} else { +	    char buf[TCL_UTF_MAX]; +	    Tcl_UniChar ch = Tcl_GetUniChar(valuePtr, index); -	    DECACHE_STACK_INFO();  	    /* -	     * Currently value of the list. -	     * Use the TCL_TRACE_READS flag to ensure that if we have an -	     * array with no elements set yet, but with a read trace on it, -	     * we will create the variable and get read traces triggered. +	     * This could be: Tcl_NewUnicodeObj((const Tcl_UniChar *)&ch, 1) +	     * but creating the object as a string seems to be faster in +	     * practical use.  	     */ -	    valuePtr = Tcl_ObjGetVar2(interp, objPtr, elemPtr, -		    TCL_TRACE_READS); -	    CACHE_STACK_INFO(); -	    if (valuePtr == NULL) { -		valuePtr = Tcl_NewObj(); -		createdNewObj = 1; -	    } else if (Tcl_IsShared(valuePtr)) { -		valuePtr = Tcl_DuplicateObj(valuePtr); -		createdNewObj = 1; + +	    length = Tcl_UniCharToUtf(ch, buf); +	    objResultPtr = Tcl_NewStringObj(buf, length); +	} + +	TRACE_APPEND(("\"%s\"\n", O2S(objResultPtr))); +	NEXT_INST_F(1, 2, 1); + +    case INST_STR_RANGE: +	TRACE(("\"%.20s\" %.20s %.20s =>", +		O2S(OBJ_AT_DEPTH(2)), O2S(OBJ_UNDER_TOS), O2S(OBJ_AT_TOS))); +	length = Tcl_GetCharLength(OBJ_AT_DEPTH(2)) - 1; +	if (TclGetIntForIndexM(interp, OBJ_UNDER_TOS, length, +		    &fromIdx) != TCL_OK +	    || TclGetIntForIndexM(interp, OBJ_AT_TOS, length, +		    &toIdx) != TCL_OK) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} + +	if (fromIdx < 0) { +	    fromIdx = 0; +	} +	if (toIdx >= length) { +	    toIdx = length; +	} +	if (toIdx >= fromIdx) { +	    objResultPtr = Tcl_GetRange(OBJ_AT_DEPTH(2), fromIdx, toIdx); +	} else { +	    TclNewObj(objResultPtr); +	} +	TRACE_APPEND(("\"%.30s\"\n", O2S(objResultPtr))); +	NEXT_INST_V(1, 3, 1); + +    case INST_STR_RANGE_IMM: +	valuePtr = OBJ_AT_TOS; +	fromIdx = TclGetInt4AtPtr(pc+1); +	toIdx = TclGetInt4AtPtr(pc+5); +	length = Tcl_GetCharLength(valuePtr); +	TRACE(("\"%.20s\" %d %d => ", O2S(valuePtr), fromIdx, toIdx)); + +	/* +	 * Adjust indices for end-based indexing. +	 */ + +	if (fromIdx < -1) { +	    fromIdx += 1 + length; +	    if (fromIdx < 0) { +		fromIdx = 0;  	    } +	} else if (fromIdx >= length) { +	    fromIdx = length; +	} +	if (toIdx < -1) { +	    toIdx += 1 + length; +	} else if (toIdx >= length) { +	    toIdx = length - 1; +	} -	    DECACHE_STACK_INFO(); -	    result = Tcl_ListObjAppendElement(interp, valuePtr, value2Ptr); -	    CACHE_STACK_INFO(); -	    if (result != TCL_OK) { -		if (elemPtr) { -		    TRACE_WITH_OBJ(("\"%.30s(%.30s)\" <- \"%.30s\" => ERROR: ", -			    O2S(objPtr), O2S(elemPtr), O2S(valuePtr)), -			    Tcl_GetObjResult(interp)); -		    Tcl_DecrRefCount(elemPtr); +	/* +	 * Check if we can do a sane substring. +	 */ + +	if (fromIdx <= toIdx) { +	    objResultPtr = Tcl_GetRange(valuePtr, fromIdx, toIdx); +	} else { +	    TclNewObj(objResultPtr); +	} +	TRACE_APPEND(("%.30s\n", O2S(objResultPtr))); +	NEXT_INST_F(9, 1, 1); + +    { +	Tcl_UniChar *ustring1, *ustring2, *ustring3, *end, *p; +	int length3; +	Tcl_Obj *value3Ptr; + +    case INST_STR_REPLACE: +	value3Ptr = POP_OBJECT(); +	valuePtr = OBJ_AT_DEPTH(2); +	length = Tcl_GetCharLength(valuePtr) - 1; +	TRACE(("\"%.20s\" %s %s \"%.20s\" => ", O2S(valuePtr), +		O2S(OBJ_UNDER_TOS), O2S(OBJ_AT_TOS), O2S(value3Ptr))); +	if (TclGetIntForIndexM(interp, OBJ_UNDER_TOS, length, +		    &fromIdx) != TCL_OK +	    || TclGetIntForIndexM(interp, OBJ_AT_TOS, length, +		    &toIdx) != TCL_OK) { +	    TclDecrRefCount(value3Ptr); +	    TRACE_ERROR(interp); +	    goto gotError; +	} +	TclDecrRefCount(OBJ_AT_TOS); +	(void) POP_OBJECT(); +	TclDecrRefCount(OBJ_AT_TOS); +	(void) POP_OBJECT(); +	if (fromIdx < 0) { +	    fromIdx = 0; +	} + +	if (fromIdx > toIdx || fromIdx > length) { +	    TRACE_APPEND(("\"%.30s\"\n", O2S(valuePtr))); +	    TclDecrRefCount(value3Ptr); +	    NEXT_INST_F(1, 0, 0); +	} + +	if (toIdx > length) { +	    toIdx = length; +	} + +	if (fromIdx == 0 && toIdx == length) { +	    TclDecrRefCount(OBJ_AT_TOS); +	    OBJ_AT_TOS = value3Ptr; +	    TRACE_APPEND(("\"%.30s\"\n", O2S(value3Ptr))); +	    NEXT_INST_F(1, 0, 0); +	} + +	length3 = Tcl_GetCharLength(value3Ptr); + +	/* +	 * Remove substring. In-place. +	 */ + +	if (length3 == 0 && !Tcl_IsShared(valuePtr) && toIdx == length) { +	    TclDecrRefCount(value3Ptr); +	    Tcl_SetObjLength(valuePtr, fromIdx); +	    TRACE_APPEND(("\"%.30s\"\n", O2S(valuePtr))); +	    NEXT_INST_F(1, 0, 0); +	} + +	/* +	 * See if we can splice in place. This happens when the number of +	 * characters being replaced is the same as the number of characters +	 * in the string to be inserted. +	 */ + +	if (length3 - 1 == toIdx - fromIdx) { +	    unsigned char *bytes1, *bytes2; + +	    if (Tcl_IsShared(valuePtr)) { +		objResultPtr = Tcl_DuplicateObj(valuePtr); +		if (TclIsPureByteArray(objResultPtr) +			&& TclIsPureByteArray(value3Ptr)) { +		    bytes1 = Tcl_GetByteArrayFromObj(objResultPtr, NULL); +		    bytes2 = Tcl_GetByteArrayFromObj(value3Ptr, NULL); +		    memcpy(bytes1 + fromIdx, bytes2, length3);  		} else { -		    TRACE_WITH_OBJ(("\"%.30s\" <-+ \"%.30s\" => ERROR: ", -			    O2S(objPtr), O2S(value2Ptr)), -			    Tcl_GetObjResult(interp)); -		} -		Tcl_DecrRefCount(objPtr); -		Tcl_DecrRefCount(value2Ptr); -		if (createdNewObj) Tcl_DecrRefCount(valuePtr); -		result = TCL_ERROR; -		goto checkForCatch; -	    } +		    ustring1 = Tcl_GetUnicodeFromObj(objResultPtr, NULL); +		    ustring2 = Tcl_GetUnicodeFromObj(value3Ptr, NULL); +		    memcpy(ustring1 + fromIdx, ustring2, +			    length3 * sizeof(Tcl_UniChar)); -	    DECACHE_STACK_INFO(); -	    newValuePtr = Tcl_ObjSetVar2(interp, objPtr, elemPtr, valuePtr, -		    TCL_LEAVE_ERR_MSG); -	    CACHE_STACK_INFO(); -	    if (newValuePtr == NULL) { -		if (elemPtr) { -		    TRACE_WITH_OBJ(("\"%.30s(%.30s)\" <- \"%.30s\" => ERROR: ", -			    O2S(objPtr), O2S(elemPtr), O2S(valuePtr)), -			    Tcl_GetObjResult(interp)); -		    Tcl_DecrRefCount(elemPtr); +		    /* +		     * Magic! Flush the info in the string internal rep that +		     * refers to the about-to-be-invalidated UTF-8 rep. This +		     * sets the 'allocated' field of the String structure to 0 +		     * to indicate that a new buffer needs to be allocated. +		     * This is safe; we know we've got a tclStringTypePtr set +		     * at this point (post Tcl_GetUnicodeFromObj). +		     */ + +		    ((int *) objResultPtr->internalRep.otherValuePtr)[1] = 0; +		} +		Tcl_InvalidateStringRep(objResultPtr); +		TRACE_APPEND(("\"%.30s\"\n", O2S(objResultPtr))); +		NEXT_INST_F(1, 1, 1); +	    } else { +		if (TclIsPureByteArray(valuePtr) +			&& TclIsPureByteArray(value3Ptr)) { +		    bytes1 = Tcl_GetByteArrayFromObj(valuePtr, NULL); +		    bytes2 = Tcl_GetByteArrayFromObj(value3Ptr, NULL); +		    memcpy(bytes1 + fromIdx, bytes2, length3);  		} else { -		    TRACE_WITH_OBJ(("\"%.30s\" <-+ \"%.30s\" => ERROR: ", -			    O2S(objPtr), O2S(value2Ptr)), -			    Tcl_GetObjResult(interp)); +		    ustring1 = Tcl_GetUnicodeFromObj(valuePtr, NULL); +		    ustring2 = Tcl_GetUnicodeFromObj(value3Ptr, NULL); +		    memcpy(ustring1 + fromIdx, ustring2, +			    length3 * sizeof(Tcl_UniChar)); + +		    /* +		     * Magic! Flush the info in the string internal rep that +		     * refers to the about-to-be-invalidated UTF-8 rep. This +		     * sets the 'allocated' field of the String structure to 0 +		     * to indicate that a new buffer needs to be allocated. +		     * This is safe; we know we've got a tclStringTypePtr set +		     * at this point (post Tcl_GetUnicodeFromObj). +		     */ + +		    ((int *) objResultPtr->internalRep.otherValuePtr)[1] = 0;  		} -		Tcl_DecrRefCount(objPtr); -		Tcl_DecrRefCount(value2Ptr); -		if (createdNewObj) Tcl_DecrRefCount(valuePtr); -		result = TCL_ERROR; -		goto checkForCatch; +		Tcl_InvalidateStringRep(valuePtr); +		TRACE_APPEND(("\"%.30s\"\n", O2S(valuePtr))); +		NEXT_INST_F(1, 0, 0);  	    } -	    PUSH_OBJECT(newValuePtr); -	    if (elemPtr) { -		TRACE_WITH_OBJ(("\"%.30s(%.30s)\" <- \"%.30s\" => ", -			O2S(objPtr), O2S(elemPtr), O2S(valuePtr)), -			value2Ptr); -		TclDecrRefCount(elemPtr); +	} + +	/* +	 * Get the unicode representation; this is where we guarantee to lose +	 * bytearrays. +	 */ + +	ustring1 = Tcl_GetUnicodeFromObj(valuePtr, &length); +	length--; + +	/* +	 * Remove substring using copying. +	 */ + +	if (length3 == 0) { +	    if (fromIdx > 0) { +		objResultPtr = Tcl_NewUnicodeObj(ustring1, fromIdx); +		if (toIdx < length) { +		    Tcl_AppendUnicodeToObj(objResultPtr, ustring1 + toIdx + 1, +			    length - toIdx); +		}  	    } else { -		TRACE_WITH_OBJ(("\"%.30s\" <-+ \"%.30s\" => ", -			O2S(objPtr), O2S(valuePtr)), value2Ptr); +		objResultPtr = Tcl_NewUnicodeObj(ustring1 + toIdx + 1, +			length - toIdx);  	    } -	    TclDecrRefCount(objPtr); -	    TclDecrRefCount(value2Ptr); -	    ADJUST_PC(1); +	    TclDecrRefCount(value3Ptr); +	    TRACE_APPEND(("\"%.30s\"\n", O2S(objResultPtr))); +	    NEXT_INST_F(1, 1, 1);  	} -	case INST_LAPPEND_ARRAY4: -	    opnd = TclGetUInt4AtPtr(pc+1); -	    pcAdjustment = 5; -	    goto doLappendArray; +	/* +	 * Splice string pieces by full copying. +	 */ -	case INST_LAPPEND_ARRAY1: -	    opnd = TclGetUInt1AtPtr(pc+1); -	    pcAdjustment = 2; +	if (fromIdx > 0) { +	    objResultPtr = Tcl_NewUnicodeObj(ustring1, fromIdx); +	    Tcl_AppendObjToObj(objResultPtr, value3Ptr); +	    if (toIdx < length) { +		Tcl_AppendUnicodeToObj(objResultPtr, ustring1 + toIdx + 1, +			length - toIdx); +	    } +	} else if (Tcl_IsShared(value3Ptr)) { +	    objResultPtr = Tcl_DuplicateObj(value3Ptr); +	    if (toIdx < length) { +		Tcl_AppendUnicodeToObj(objResultPtr, ustring1 + toIdx + 1, +			length - toIdx); +	    } +	} else { +	    /* +	     * Be careful with splicing the stack in this case; we have a +	     * refCount:1 object in value3Ptr and we want to append to it and +	     * make it be the refCount:1 object at the top of the stack +	     * afterwards. [Bug 82e7f67325] +	     */ -	    doLappendArray: -	    valuePtr = POP_OBJECT(); -	    elemPtr = POP_OBJECT(); -	    DECACHE_STACK_INFO(); -	    value2Ptr = TclSetElementOfIndexedArray(interp, opnd, -		    elemPtr, valuePtr, -		    TCL_LEAVE_ERR_MSG|TCL_APPEND_VALUE|TCL_LIST_ELEMENT); -	    CACHE_STACK_INFO(); -	    if (value2Ptr == NULL) { -		TRACE_WITH_OBJ(("%u \"%.30s\" <-+ \"%.30s\" => ERROR: ", -			opnd, O2S(elemPtr), O2S(valuePtr)), -			Tcl_GetObjResult(interp)); -		Tcl_DecrRefCount(elemPtr); -		Tcl_DecrRefCount(valuePtr); -		result = TCL_ERROR; -		goto checkForCatch; +	    if (toIdx < length) { +		Tcl_AppendUnicodeToObj(value3Ptr, ustring1 + toIdx + 1, +			length - toIdx);  	    } -	    PUSH_OBJECT(value2Ptr); -	    TRACE_WITH_OBJ(("%u \"%.30s\" <-+ \"%.30s\" => ", -		    opnd, O2S(elemPtr), O2S(valuePtr)), value2Ptr); -	    TclDecrRefCount(elemPtr); +	    TRACE_APPEND(("\"%.30s\"\n", O2S(value3Ptr)));  	    TclDecrRefCount(valuePtr); -	    ADJUST_PC(pcAdjustment); +	    OBJ_AT_TOS = value3Ptr;	/* Tricky! */ +	    NEXT_INST_F(1, 0, 0); +	} +	TclDecrRefCount(value3Ptr); +	TRACE_APPEND(("\"%.30s\"\n", O2S(objResultPtr))); +	NEXT_INST_F(1, 1, 1); + +    case INST_STR_MAP: +	valuePtr = OBJ_AT_TOS;		/* "Main" string. */ +	value3Ptr = OBJ_UNDER_TOS;	/* "Target" string. */ +	value2Ptr = OBJ_AT_DEPTH(2);	/* "Source" string. */ +	if (value3Ptr == value2Ptr) { +	    objResultPtr = valuePtr; +	    goto doneStringMap; +	} else if (valuePtr == value2Ptr) { +	    objResultPtr = value3Ptr; +	    goto doneStringMap; +	} +	ustring1 = Tcl_GetUnicodeFromObj(valuePtr, &length); +	if (length == 0) { +	    objResultPtr = valuePtr; +	    goto doneStringMap; +	} +	ustring2 = Tcl_GetUnicodeFromObj(value2Ptr, &length2); +	if (length2 > length || length2 == 0) { +	    objResultPtr = valuePtr; +	    goto doneStringMap; +	} else if (length2 == length) { +	    if (memcmp(ustring1, ustring2, sizeof(Tcl_UniChar) * length)) { +		objResultPtr = valuePtr; +	    } else { +		objResultPtr = value3Ptr; +	    } +	    goto doneStringMap; +	} +	ustring3 = Tcl_GetUnicodeFromObj(value3Ptr, &length3); + +	objResultPtr = Tcl_NewUnicodeObj(ustring1, 0); +	p = ustring1; +	end = ustring1 + length; +	for (; ustring1 < end; ustring1++) { +	    if ((*ustring1 == *ustring2) && (length2==1 || +		    memcmp(ustring1, ustring2, sizeof(Tcl_UniChar) * length2) +			    == 0)) { +		if (p != ustring1) { +		    Tcl_AppendUnicodeToObj(objResultPtr, p, ustring1-p); +		    p = ustring1 + length2; +		} else { +		    p += length2; +		} +		ustring1 = p - 1; +		Tcl_AppendUnicodeToObj(objResultPtr, ustring3, length3); +	    } +	} +	if (p != ustring1) {  	    /* -	     * END (L)APPEND INSTRUCTIONS +	     * Put the rest of the unmapped chars onto result.  	     */ -	case INST_INCR_SCALAR1: -	    opnd = TclGetUInt1AtPtr(pc+1); -	    valuePtr = POP_OBJECT();  -	    if (valuePtr->typePtr != &tclIntType) { -		result = tclIntType.setFromAnyProc(interp, valuePtr); -		if (result != TCL_OK) { -		    TRACE_WITH_OBJ(("%u (by %s) => ERROR converting increment amount to int: ", -		            opnd, O2S(valuePtr)), Tcl_GetObjResult(interp)); -		    Tcl_DecrRefCount(valuePtr); -		    goto checkForCatch; +	    Tcl_AppendUnicodeToObj(objResultPtr, p, ustring1 - p); +	} +    doneStringMap: +	TRACE_WITH_OBJ(("%.20s %.20s %.20s => ", +		O2S(value2Ptr), O2S(value3Ptr), O2S(valuePtr)), objResultPtr); +	NEXT_INST_V(1, 3, 1); + +    case INST_STR_FIND: +	ustring1 = Tcl_GetUnicodeFromObj(OBJ_AT_TOS, &length);	/* Haystack */ +	ustring2 = Tcl_GetUnicodeFromObj(OBJ_UNDER_TOS, &length2);/* Needle */ + +	match = -1; +	if (length2 > 0 && length2 <= length) { +	    end = ustring1 + length - length2 + 1; +	    for (p=ustring1 ; p<end ; p++) { +		if ((*p == *ustring2) && +			memcmp(ustring2,p,sizeof(Tcl_UniChar)*length2) == 0) { +		    match = p - ustring1; +		    break;  		}  	    } -	    i = valuePtr->internalRep.longValue; -	    DECACHE_STACK_INFO(); -	    value2Ptr = TclIncrIndexedScalar(interp, opnd, i); -	    CACHE_STACK_INFO(); -	    if (value2Ptr == NULL) { -		TRACE_WITH_OBJ(("%u (by %ld) => ERROR: ", opnd, i), -			Tcl_GetObjResult(interp)); -		Tcl_DecrRefCount(valuePtr); -		result = TCL_ERROR; -		goto checkForCatch; -	    } -	    PUSH_OBJECT(value2Ptr); -	    TRACE_WITH_OBJ(("%u (by %ld) => ", opnd, i), value2Ptr); -	    TclDecrRefCount(valuePtr); -	    ADJUST_PC(2); +	} -	case INST_INCR_SCALAR_STK: -	case INST_INCR_STK: -	    valuePtr = POP_OBJECT(); -	    objPtr = POP_OBJECT(); /* scalar name */ -	    if (valuePtr->typePtr != &tclIntType) { -		result = tclIntType.setFromAnyProc(interp, valuePtr); -		if (result != TCL_OK) { -		    TRACE_WITH_OBJ(("\"%.30s\" (by %s) => ERROR converting increment amount to int: ", -		            O2S(objPtr), O2S(valuePtr)), -			    Tcl_GetObjResult(interp)); -		    Tcl_DecrRefCount(objPtr); -		    Tcl_DecrRefCount(valuePtr); -		    goto checkForCatch; +	TRACE(("%.20s %.20s => %d\n", +		O2S(OBJ_UNDER_TOS), O2S(OBJ_AT_TOS), match)); +	TclNewIntObj(objResultPtr, match); +	NEXT_INST_F(1, 2, 1); + +    case INST_STR_FIND_LAST: +	ustring1 = Tcl_GetUnicodeFromObj(OBJ_AT_TOS, &length);	/* Haystack */ +	ustring2 = Tcl_GetUnicodeFromObj(OBJ_UNDER_TOS, &length2);/* Needle */ + +	match = -1; +	if (length2 > 0 && length2 <= length) { +	    for (p=ustring1+length-length2 ; p>=ustring1 ; p--) { +		if ((*p == *ustring2) && +			memcmp(ustring2,p,sizeof(Tcl_UniChar)*length2) == 0) { +		    match = p - ustring1; +		    break;  		}  	    } -	    i = valuePtr->internalRep.longValue; -	    DECACHE_STACK_INFO(); -	    value2Ptr = TclIncrVar2(interp, objPtr, (Tcl_Obj *) NULL, i, -		    TCL_LEAVE_ERR_MSG); -	    CACHE_STACK_INFO(); -	    if (value2Ptr == NULL) { -		TRACE_WITH_OBJ(("\"%.30s\" (by %ld) => ERROR: ", -		        O2S(objPtr), i), Tcl_GetObjResult(interp)); -		Tcl_DecrRefCount(objPtr); -		Tcl_DecrRefCount(valuePtr); -		result = TCL_ERROR; -		goto checkForCatch; -	    } -	    PUSH_OBJECT(value2Ptr); -	    TRACE_WITH_OBJ(("\"%.30s\" (by %ld) => ", O2S(objPtr), i), -		    value2Ptr); -	    Tcl_DecrRefCount(objPtr); -	    Tcl_DecrRefCount(valuePtr); -	    ADJUST_PC(1); +	} -	case INST_INCR_ARRAY1: -	    opnd = TclGetUInt1AtPtr(pc+1); -	    valuePtr = POP_OBJECT(); -	    elemPtr = POP_OBJECT(); -	    if (valuePtr->typePtr != &tclIntType) { -		result = tclIntType.setFromAnyProc(interp, valuePtr); -		if (result != TCL_OK) { -		    TRACE_WITH_OBJ(("%u \"%.30s\" (by %s) => ERROR converting increment amount to int: ", -			    opnd, O2S(elemPtr), O2S(valuePtr)), -			    Tcl_GetObjResult(interp)); -		    Tcl_DecrRefCount(elemPtr); -		    Tcl_DecrRefCount(valuePtr); -		    goto checkForCatch; +	TRACE(("%.20s %.20s => %d\n", +		O2S(OBJ_UNDER_TOS), O2S(OBJ_AT_TOS), match)); + +	TclNewIntObj(objResultPtr, match); +	NEXT_INST_F(1, 2, 1); + +    case INST_STR_CLASS: +	opnd = TclGetInt1AtPtr(pc+1); +	valuePtr = OBJ_AT_TOS; +	TRACE(("%s \"%.30s\" => ", tclStringClassTable[opnd].name, +		O2S(valuePtr))); +	ustring1 = Tcl_GetUnicodeFromObj(valuePtr, &length); +	match = 1; +	if (length > 0) { +	    end = ustring1 + length; +	    for (p=ustring1 ; p<end ; p++) { +		if (!tclStringClassTable[opnd].comparator(*p)) { +		    match = 0; +		    break;  		}  	    } -	    i = valuePtr->internalRep.longValue; -	    DECACHE_STACK_INFO(); -	    value2Ptr = TclIncrElementOfIndexedArray(interp, opnd, -		    elemPtr, i); -	    CACHE_STACK_INFO(); -	    if (value2Ptr == NULL) { -		TRACE_WITH_OBJ(("%u \"%.30s\" (by %ld) => ERROR: ", -			opnd, O2S(elemPtr), i), -			Tcl_GetObjResult(interp)); -		Tcl_DecrRefCount(elemPtr); -		Tcl_DecrRefCount(valuePtr); -		result = TCL_ERROR; -		goto checkForCatch; +	} +	TRACE_APPEND(("%d\n", match)); +	JUMP_PEEPHOLE_F(match, 2, 1); +    } + +    case INST_STR_MATCH: +	nocase = TclGetInt1AtPtr(pc+1); +	valuePtr = OBJ_AT_TOS;		/* String */ +	value2Ptr = OBJ_UNDER_TOS;	/* Pattern */ + +	/* +	 * Check that at least one of the objects is Unicode before promoting +	 * both. +	 */ + +	if ((valuePtr->typePtr == &tclStringType) +		|| (value2Ptr->typePtr == &tclStringType)) { +	    Tcl_UniChar *ustring1, *ustring2; + +	    ustring1 = Tcl_GetUnicodeFromObj(valuePtr, &length); +	    ustring2 = Tcl_GetUnicodeFromObj(value2Ptr, &length2); +	    match = TclUniCharMatch(ustring1, length, ustring2, length2, +		    nocase); +	} else if (TclIsPureByteArray(valuePtr) && !nocase) { +	    unsigned char *bytes1, *bytes2; + +	    bytes1 = Tcl_GetByteArrayFromObj(valuePtr, &length); +	    bytes2 = Tcl_GetByteArrayFromObj(value2Ptr, &length2); +	    match = TclByteArrayMatch(bytes1, length, bytes2, length2, 0); +	} else { +	    match = Tcl_StringCaseMatch(TclGetString(valuePtr), +		    TclGetString(value2Ptr), nocase); +	} + +	/* +	 * Reuse value2Ptr object already on stack if possible. Adjustment is +	 * 2 due to the nocase byte +	 */ + +	TRACE(("%.20s %.20s => %d\n", O2S(valuePtr), O2S(value2Ptr), match)); + +	/* +	 * Peep-hole optimisation: if you're about to jump, do jump from here. +	 */ + +	JUMP_PEEPHOLE_F(match, 2, 2); + +    { +	const char *string1, *string2; +	int trim1, trim2; + +    case INST_STR_TRIM_LEFT: +	valuePtr = OBJ_UNDER_TOS;	/* String */ +	value2Ptr = OBJ_AT_TOS;		/* TrimSet */ +	string2 = TclGetStringFromObj(value2Ptr, &length2); +	string1 = TclGetStringFromObj(valuePtr, &length); +	trim1 = TclTrimLeft(string1, length, string2, length2); +	trim2 = 0; +	goto createTrimmedString; +    case INST_STR_TRIM_RIGHT: +	valuePtr = OBJ_UNDER_TOS;	/* String */ +	value2Ptr = OBJ_AT_TOS;		/* TrimSet */ +	string2 = TclGetStringFromObj(value2Ptr, &length2); +	string1 = TclGetStringFromObj(valuePtr, &length); +	trim2 = TclTrimRight(string1, length, string2, length2); +	trim1 = 0; +	goto createTrimmedString; +    case INST_STR_TRIM: +	valuePtr = OBJ_UNDER_TOS;	/* String */ +	value2Ptr = OBJ_AT_TOS;		/* TrimSet */ +	string2 = TclGetStringFromObj(value2Ptr, &length2); +	string1 = TclGetStringFromObj(valuePtr, &length); +	trim1 = TclTrimLeft(string1, length, string2, length2); +	if (trim1 < length) { +	    trim2 = TclTrimRight(string1, length, string2, length2); +	} else { +	    trim2 = 0; +	} +    createTrimmedString: +	/* +	 * Careful here; trim set often contains non-ASCII characters so we +	 * take care when printing. [Bug 971cb4f1db] +	 */ + +#ifdef TCL_COMPILE_DEBUG +	if (traceInstructions) { +	    TRACE(("\"%.30s\" ", O2S(valuePtr))); +	    TclPrintObject(stdout, value2Ptr, 30); +	    printf(" => "); +	} +#endif +	if (trim1 == 0 && trim2 == 0) { +#ifdef TCL_COMPILE_DEBUG +	    if (traceInstructions) { +		TclPrintObject(stdout, valuePtr, 30); +		printf("\n");  	    } -	    PUSH_OBJECT(value2Ptr); -	    TRACE_WITH_OBJ(("%u \"%.30s\" (by %ld) => ", -		    opnd, O2S(elemPtr), i), value2Ptr); -	    Tcl_DecrRefCount(elemPtr); -	    Tcl_DecrRefCount(valuePtr); -	    ADJUST_PC(2); -	     -	case INST_INCR_ARRAY_STK: -	    valuePtr = POP_OBJECT(); -	    elemPtr = POP_OBJECT(); -	    objPtr = POP_OBJECT();	/* array name */ -	    if (valuePtr->typePtr != &tclIntType) { -		result = tclIntType.setFromAnyProc(interp, valuePtr); -		if (result != TCL_OK) { -		    TRACE_WITH_OBJ(("\"%.30s(%.30s)\" (by %s) => ERROR converting increment amount to int: ", -			    O2S(objPtr), O2S(elemPtr), O2S(valuePtr)), -			    Tcl_GetObjResult(interp)); -		    Tcl_DecrRefCount(objPtr); -		    Tcl_DecrRefCount(elemPtr); -		    Tcl_DecrRefCount(valuePtr); -		    goto checkForCatch; -		} +#endif +	    NEXT_INST_F(1, 1, 0); +	} else { +	    objResultPtr = Tcl_NewStringObj(string1+trim1, length-trim1-trim2); +#ifdef TCL_COMPILE_DEBUG +	    if (traceInstructions) { +		TclPrintObject(stdout, objResultPtr, 30); +		printf("\n");  	    } -	    i = valuePtr->internalRep.longValue; -	    DECACHE_STACK_INFO(); -	    value2Ptr = TclIncrVar2(interp, objPtr, elemPtr, i, -		    TCL_LEAVE_ERR_MSG); -	    CACHE_STACK_INFO(); -	    if (value2Ptr == NULL) { -		TRACE_WITH_OBJ(("\"%.30s(%.30s)\" (by %ld) => ERROR: ", -			O2S(objPtr), O2S(elemPtr), i), -			Tcl_GetObjResult(interp)); -		Tcl_DecrRefCount(objPtr); -		Tcl_DecrRefCount(elemPtr); -		Tcl_DecrRefCount(valuePtr); -		result = TCL_ERROR; -		goto checkForCatch; +#endif +	    NEXT_INST_F(1, 2, 1); +	} +    } + +    case INST_REGEXP: +	cflags = TclGetInt1AtPtr(pc+1); /* RE compile flages like NOCASE */ +	valuePtr = OBJ_AT_TOS;		/* String */ +	value2Ptr = OBJ_UNDER_TOS;	/* Pattern */ +	TRACE(("\"%.30s\" \"%.30s\" => ", O2S(valuePtr), O2S(value2Ptr))); + +	/* +	 * Compile and match the regular expression. +	 */ + +	{ +	    Tcl_RegExp regExpr = +		    Tcl_GetRegExpFromObj(interp, value2Ptr, cflags); + +	    if (regExpr == NULL) { +		TRACE_ERROR(interp); +		goto gotError;  	    } -	    PUSH_OBJECT(value2Ptr); -	    TRACE_WITH_OBJ(("\"%.30s(%.30s)\" (by %ld) => ", -		    O2S(objPtr), O2S(elemPtr), i), value2Ptr); -	    Tcl_DecrRefCount(objPtr); -	    Tcl_DecrRefCount(elemPtr); -	    Tcl_DecrRefCount(valuePtr); -	    ADJUST_PC(1); -	     -	case INST_INCR_SCALAR1_IMM: -	    opnd = TclGetUInt1AtPtr(pc+1); -	    i = TclGetInt1AtPtr(pc+2); -	    DECACHE_STACK_INFO(); -	    value2Ptr = TclIncrIndexedScalar(interp, opnd, i); -	    CACHE_STACK_INFO(); -	    if (value2Ptr == NULL) { -		TRACE_WITH_OBJ(("%u %ld => ERROR: ", opnd, i), -			Tcl_GetObjResult(interp)); -		result = TCL_ERROR; -		goto checkForCatch; +	    match = Tcl_RegExpExecObj(interp, regExpr, valuePtr, 0, 0, 0); +	    if (match < 0) { +		TRACE_ERROR(interp); +		goto gotError;  	    } -	    PUSH_OBJECT(value2Ptr); -	    TRACE_WITH_OBJ(("%u %ld => ", opnd, i), value2Ptr); -	    ADJUST_PC(3); +	} -	case INST_INCR_SCALAR_STK_IMM: -	case INST_INCR_STK_IMM: -	    objPtr = POP_OBJECT(); /* variable name */ -	    i = TclGetInt1AtPtr(pc+1); -	    DECACHE_STACK_INFO(); -	    value2Ptr = TclIncrVar2(interp, objPtr, (Tcl_Obj *) NULL, i, -		    TCL_LEAVE_ERR_MSG); -	    CACHE_STACK_INFO(); -	    if (value2Ptr == NULL) { -		TRACE_WITH_OBJ(("\"%.30s\" %ld => ERROR: ", -		        O2S(objPtr), i), Tcl_GetObjResult(interp)); -		result = TCL_ERROR; -		Tcl_DecrRefCount(objPtr); -		goto checkForCatch; +	TRACE_APPEND(("%d\n", match)); + +	/* +	 * Peep-hole optimisation: if you're about to jump, do jump from here. +	 * Adjustment is 2 due to the nocase byte. +	 */ + +	JUMP_PEEPHOLE_F(match, 2, 2); +    } + +    /* +     *	   End of string-related instructions. +     * ----------------------------------------------------------------- +     *	   Start of numeric operator instructions. +     */ + +    { +	ClientData ptr1, ptr2; +	int type1, type2; +	long l1, l2, lResult; + +    case INST_NUM_TYPE: +	if (GetNumberFromObj(NULL, OBJ_AT_TOS, &ptr1, &type1) != TCL_OK) { +	    type1 = 0; +	} else if (type1 == TCL_NUMBER_LONG) { +	    /* value is between LONG_MIN and LONG_MAX */ +	    /* [string is integer] is -UINT_MAX to UINT_MAX range */ +	    int i; + +	    if (Tcl_GetIntFromObj(NULL, OBJ_AT_TOS, &i) != TCL_OK) { +		type1 = TCL_NUMBER_WIDE;  	    } -	    PUSH_OBJECT(value2Ptr); -	    TRACE_WITH_OBJ(("\"%.30s\" %ld => ", O2S(objPtr), i), -		    value2Ptr); -	    TclDecrRefCount(objPtr); -	    ADJUST_PC(2); +#ifndef TCL_WIDE_INT_IS_LONG +	} else if (type1 == TCL_NUMBER_WIDE) { +	    /* value is between WIDE_MIN and WIDE_MAX */ +	    /* [string is wideinteger] is -UWIDE_MAX to UWIDE_MAX range */ +	    int i; +	    if (Tcl_GetIntFromObj(NULL, OBJ_AT_TOS, &i) == TCL_OK) { +		type1 = TCL_NUMBER_LONG; +	    } +#endif +	} else if (type1 == TCL_NUMBER_BIG) { +	    /* value is an integer outside the WIDE_MIN to WIDE_MAX range */ +	    /* [string is wideinteger] is -UWIDE_MAX to UWIDE_MAX range */ +	    Tcl_WideInt w; -	case INST_INCR_ARRAY1_IMM: -	    opnd = TclGetUInt1AtPtr(pc+1); -	    i = TclGetInt1AtPtr(pc+2); -	    elemPtr = POP_OBJECT(); +	    if (Tcl_GetWideIntFromObj(NULL, OBJ_AT_TOS, &w) == TCL_OK) { +		type1 = TCL_NUMBER_WIDE; +	    } +	} +	TclNewIntObj(objResultPtr, type1); +	TRACE(("\"%.20s\" => %d\n", O2S(OBJ_AT_TOS), type1)); +	NEXT_INST_F(1, 1, 1); + +    case INST_EQ: +    case INST_NEQ: +    case INST_LT: +    case INST_GT: +    case INST_LE: +    case INST_GE: { +	int iResult = 0, compare = 0; + +	value2Ptr = OBJ_AT_TOS; +	valuePtr = OBJ_UNDER_TOS; + +	if (GetNumberFromObj(NULL, valuePtr, &ptr1, &type1) != TCL_OK) { +	    /* +	     * At least one non-numeric argument - compare as strings. +	     */ + +	    goto stringCompare; +	} +	if (type1 == TCL_NUMBER_NAN) { +	    /* +	     * NaN first arg: NaN != to everything, other compares are false. +	     */ + +	    iResult = (*pc == INST_NEQ); +	    goto foundResult; +	} +	if (valuePtr == value2Ptr) { +	    compare = MP_EQ; +	    goto convertComparison; +	} +	if (GetNumberFromObj(NULL, value2Ptr, &ptr2, &type2) != TCL_OK) { +	    /* +	     * At least one non-numeric argument - compare as strings. +	     */ + +	    goto stringCompare; +	} +	if (type2 == TCL_NUMBER_NAN) { +	    /* +	     * NaN 2nd arg: NaN != to everything, other compares are false. +	     */ + +	    iResult = (*pc == INST_NEQ); +	    goto foundResult; +	} +	if ((type1 == TCL_NUMBER_LONG) && (type2 == TCL_NUMBER_LONG)) { +	    l1 = *((const long *)ptr1); +	    l2 = *((const long *)ptr2); +	    compare = (l1 < l2) ? MP_LT : ((l1 > l2) ? MP_GT : MP_EQ); +	} else { +	    compare = TclCompareTwoNumbers(valuePtr, value2Ptr); +	} + +	/* +	 * Turn comparison outcome into appropriate result for opcode. +	 */ + +    convertComparison: +	switch (*pc) { +	case INST_EQ: +	    iResult = (compare == MP_EQ); +	    break; +	case INST_NEQ: +	    iResult = (compare != MP_EQ); +	    break; +	case INST_LT: +	    iResult = (compare == MP_LT); +	    break; +	case INST_GT: +	    iResult = (compare == MP_GT); +	    break; +	case INST_LE: +	    iResult = (compare != MP_GT); +	    break; +	case INST_GE: +	    iResult = (compare != MP_LT); +	    break; +	} + +	/* +	 * Peep-hole optimisation: if you're about to jump, do jump from here. +	 */ + +    foundResult: +	TRACE(("\"%.20s\" \"%.20s\" => %d\n", O2S(valuePtr), O2S(value2Ptr), +		iResult)); +	JUMP_PEEPHOLE_F(iResult, 1, 2); +    } + +    case INST_MOD: +    case INST_LSHIFT: +    case INST_RSHIFT: +    case INST_BITOR: +    case INST_BITXOR: +    case INST_BITAND: +	value2Ptr = OBJ_AT_TOS; +	valuePtr = OBJ_UNDER_TOS; + +	if ((GetNumberFromObj(NULL, valuePtr, &ptr1, &type1) != TCL_OK) +		|| (type1==TCL_NUMBER_DOUBLE) || (type1==TCL_NUMBER_NAN)) { +	    TRACE(("%.20s %.20s => ILLEGAL 1st TYPE %s\n", O2S(valuePtr), +		    O2S(value2Ptr), (valuePtr->typePtr? +		    valuePtr->typePtr->name : "null")));  	    DECACHE_STACK_INFO(); -	    value2Ptr = TclIncrElementOfIndexedArray(interp, opnd, -		    elemPtr, i); +	    IllegalExprOperandType(interp, pc, valuePtr);  	    CACHE_STACK_INFO(); -	    if (value2Ptr == NULL) { -		TRACE_WITH_OBJ(("%u \"%.30s\" (by %ld) => ERROR: ", -			opnd, O2S(elemPtr), i), -			Tcl_GetObjResult(interp)); -		Tcl_DecrRefCount(elemPtr); -		result = TCL_ERROR; -		goto checkForCatch; -	    } -	    PUSH_OBJECT(value2Ptr); -	    TRACE_WITH_OBJ(("%u \"%.30s\" (by %ld) => ", -		    opnd, O2S(elemPtr), i), value2Ptr); -	    Tcl_DecrRefCount(elemPtr); -	    ADJUST_PC(3); -	     -	case INST_INCR_ARRAY_STK_IMM: -	    i = TclGetInt1AtPtr(pc+1); -	    elemPtr = POP_OBJECT(); -	    objPtr = POP_OBJECT();	/* array name */ +	    goto gotError; +	} + +	if ((GetNumberFromObj(NULL, value2Ptr, &ptr2, &type2) != TCL_OK) +		|| (type2==TCL_NUMBER_DOUBLE) || (type2==TCL_NUMBER_NAN)) { +	    TRACE(("%.20s %.20s => ILLEGAL 2nd TYPE %s\n", O2S(valuePtr), +		    O2S(value2Ptr), (value2Ptr->typePtr? +		    value2Ptr->typePtr->name : "null")));  	    DECACHE_STACK_INFO(); -	    value2Ptr = TclIncrVar2(interp, objPtr, elemPtr, i, -		    TCL_LEAVE_ERR_MSG); +	    IllegalExprOperandType(interp, pc, value2Ptr);  	    CACHE_STACK_INFO(); -	    if (value2Ptr == NULL) { -		TRACE_WITH_OBJ(("\"%.30s(%.30s)\" (by %ld) => ERROR: ", -			O2S(objPtr), O2S(elemPtr), i), -			Tcl_GetObjResult(interp)); -		Tcl_DecrRefCount(objPtr); -		Tcl_DecrRefCount(elemPtr); -		result = TCL_ERROR; -		goto checkForCatch; -	    } -	    PUSH_OBJECT(value2Ptr); -	    TRACE_WITH_OBJ(("\"%.30s(%.30s)\" (by %ld) => ", -		    O2S(objPtr), O2S(elemPtr), i), value2Ptr); -	    Tcl_DecrRefCount(objPtr); -	    Tcl_DecrRefCount(elemPtr); -	    ADJUST_PC(2); +	    goto gotError; +	} -	case INST_JUMP1: -#ifdef TCL_COMPILE_DEBUG -	    opnd = TclGetInt1AtPtr(pc+1); -	    TRACE(("%d => new pc %u\n", opnd, -		   (unsigned int)(pc + opnd - codePtr->codeStart))); -	    pc += opnd; -#else -	    pc += TclGetInt1AtPtr(pc+1); -#endif /* TCL_COMPILE_DEBUG */ -	    continue; +	/* +	 * Check for common, simple case. +	 */ -	case INST_JUMP4: -	    opnd = TclGetInt4AtPtr(pc+1); -	    TRACE(("%d => new pc %u\n", opnd, -		   (unsigned int)(pc + opnd - codePtr->codeStart))); -	    ADJUST_PC(opnd); +	if ((type1 == TCL_NUMBER_LONG) && (type2 == TCL_NUMBER_LONG)) { +	    l1 = *((const long *)ptr1); +	    l2 = *((const long *)ptr2); + +	    switch (*pc) { +	    case INST_MOD: +		if (l2 == 0) { +		    TRACE(("%s %s => DIVIDE BY ZERO\n", O2S(valuePtr), +			    O2S(value2Ptr))); +		    goto divideByZero; +		} else if ((l2 == 1) || (l2 == -1)) { +		    /* +		     * Div. by |1| always yields remainder of 0. +		     */ -	case INST_JUMP_TRUE4: -	    opnd = TclGetInt4AtPtr(pc+1); -	    pcAdjustment = 5; -	    goto doJumpTrue; +		    TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); +		    objResultPtr = TCONST(0); +		    TRACE(("%s\n", O2S(objResultPtr))); +		    NEXT_INST_F(1, 2, 1); +		} else if (l1 == 0) { +		    /* +		     * 0 % (non-zero) always yields remainder of 0. +		     */ -	case INST_JUMP_TRUE1: -	    opnd = TclGetInt1AtPtr(pc+1); -	    pcAdjustment = 2; -	     -	    doJumpTrue: -	    { -		int b; -		 -		valuePtr = POP_OBJECT(); -		if (valuePtr->typePtr == &tclIntType) { -		    b = (valuePtr->internalRep.longValue != 0); -		} else if (valuePtr->typePtr == &tclDoubleType) { -		    b = (valuePtr->internalRep.doubleValue != 0.0); +		    TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); +		    objResultPtr = TCONST(0); +		    TRACE(("%s\n", O2S(objResultPtr))); +		    NEXT_INST_F(1, 2, 1);  		} else { -		    result = Tcl_GetBooleanFromObj(interp, valuePtr, &b); -		    if (result != TCL_OK) { -			TRACE_WITH_OBJ(("%d => ERROR: ", opnd), -				Tcl_GetObjResult(interp)); -			Tcl_DecrRefCount(valuePtr); -			goto checkForCatch; +		    lResult = l1 / l2; + +		    /* +		     * Force Tcl's integer division rules. +		     * TODO: examine for logic simplification +		     */ + +		    if ((lResult < 0 || (lResult == 0 && +			    ((l1 < 0 && l2 > 0) || (l1 > 0 && l2 < 0)))) && +			    (lResult * l2 != l1)) { +			lResult -= 1;  		    } +		    lResult = l1 - l2*lResult; +		    goto longResultOfArithmetic;  		} -		if (b) { -		    TRACE(("%d => %.20s true, new pc %u\n", -			    opnd, O2S(valuePtr), -		            (unsigned int)(pc+opnd - codePtr->codeStart))); -		    TclDecrRefCount(valuePtr); -		    ADJUST_PC(opnd); + +	    case INST_RSHIFT: +		if (l2 < 0) { +		    Tcl_SetObjResult(interp, Tcl_NewStringObj( +			    "negative shift argument", -1)); +#ifdef ERROR_CODE_FOR_EARLY_DETECTED_ARITH_ERROR +		    DECACHE_STACK_INFO(); +		    Tcl_SetErrorCode(interp, "ARITH", "DOMAIN", +			    "domain error: argument not in valid range", +			    NULL); +		    CACHE_STACK_INFO(); +#endif /* ERROR_CODE_FOR_EARLY_DETECTED_ARITH_ERROR */ +		    goto gotError; +		} else if (l1 == 0) { +		    TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); +		    objResultPtr = TCONST(0); +		    TRACE(("%s\n", O2S(objResultPtr))); +		    NEXT_INST_F(1, 2, 1);  		} else { -		    TRACE(("%d => %.20s false\n", opnd, O2S(valuePtr))); -		    TclDecrRefCount(valuePtr); -		    ADJUST_PC(pcAdjustment); +		    /* +		     * Quickly force large right shifts to 0 or -1. +		     */ + +		    if (l2 >= (long)(CHAR_BIT*sizeof(long))) { +			/* +			 * We assume that INT_MAX is much larger than the +			 * number of bits in a long. This is a pretty safe +			 * assumption, given that the former is usually around +			 * 4e9 and the latter 32 or 64... +			 */ + +			TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); +			if (l1 > 0L) { +			    objResultPtr = TCONST(0); +			} else { +			    TclNewIntObj(objResultPtr, -1); +			} +			TRACE(("%s\n", O2S(objResultPtr))); +			NEXT_INST_F(1, 2, 1); +		    } + +		    /* +		     * Handle shifts within the native long range. +		     */ + +		    lResult = l1 >> ((int) l2); +		    goto longResultOfArithmetic;  		} -	    } -	     -	case INST_JUMP_FALSE4: -	    opnd = TclGetInt4AtPtr(pc+1); -	    pcAdjustment = 5; -	    goto doJumpFalse; -	case INST_JUMP_FALSE1: -	    opnd = TclGetInt1AtPtr(pc+1); -	    pcAdjustment = 2; -	     -	    doJumpFalse: -	    { -		int b; -		 -		valuePtr = POP_OBJECT(); -		if (valuePtr->typePtr == &tclIntType) { -		    b = (valuePtr->internalRep.longValue != 0); -		} else if (valuePtr->typePtr == &tclDoubleType) { -		    b = (valuePtr->internalRep.doubleValue != 0.0); +	    case INST_LSHIFT: +		if (l2 < 0) { +		    Tcl_SetObjResult(interp, Tcl_NewStringObj( +			    "negative shift argument", -1)); +#ifdef ERROR_CODE_FOR_EARLY_DETECTED_ARITH_ERROR +		    DECACHE_STACK_INFO(); +		    Tcl_SetErrorCode(interp, "ARITH", "DOMAIN", +			    "domain error: argument not in valid range", +			    NULL); +		    CACHE_STACK_INFO(); +#endif /* ERROR_CODE_FOR_EARLY_DETECTED_ARITH_ERROR */ +		    goto gotError; +		} else if (l1 == 0) { +		    TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); +		    objResultPtr = TCONST(0); +		    TRACE(("%s\n", O2S(objResultPtr))); +		    NEXT_INST_F(1, 2, 1); +		} else if (l2 > (long) INT_MAX) { +		    /* +		     * Technically, we could hold the value (1 << (INT_MAX+1)) +		     * in an mp_int, but since we're using mp_mul_2d() to do +		     * the work, and it takes only an int argument, that's a +		     * good place to draw the line. +		     */ + +		    Tcl_SetObjResult(interp, Tcl_NewStringObj( +			    "integer value too large to represent", -1)); +#ifdef ERROR_CODE_FOR_EARLY_DETECTED_ARITH_ERROR +		    DECACHE_STACK_INFO(); +		    Tcl_SetErrorCode(interp, "ARITH", "IOVERFLOW", +			    "integer value too large to represent", NULL); +		    CACHE_STACK_INFO(); +#endif /* ERROR_CODE_FOR_EARLY_DETECTED_ARITH_ERROR */ +		    goto gotError;  		} else { -		    result = Tcl_GetBooleanFromObj(interp, valuePtr, &b); -		    if (result != TCL_OK) { -			TRACE_WITH_OBJ(("%d => ERROR: ", opnd), -				Tcl_GetObjResult(interp)); -			Tcl_DecrRefCount(valuePtr); -			goto checkForCatch; +		    int shift = (int) l2; + +		    /* +		     * Handle shifts within the native long range. +		     */ + +		    if ((size_t) shift < CHAR_BIT*sizeof(long) && (l1 != 0) +			    && !((l1>0 ? l1 : ~l1) & +				-(1L<<(CHAR_BIT*sizeof(long) - 1 - shift)))) { +			lResult = l1 << shift; +			goto longResultOfArithmetic;  		    }  		} -		if (b) { -		    TRACE(("%d => %.20s true\n", opnd, O2S(valuePtr))); -		    TclDecrRefCount(valuePtr); -		    ADJUST_PC(pcAdjustment); -		} else { -		    TRACE(("%d => %.20s false, new pc %u\n", -			   opnd, O2S(valuePtr), -			   (unsigned int)(pc + opnd - codePtr->codeStart))); -		    TclDecrRefCount(valuePtr); -		    ADJUST_PC(opnd); + +		/* +		 * Too large; need to use the broken-out function. +		 */ + +		TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); +		break; + +	    case INST_BITAND: +		lResult = l1 & l2; +		goto longResultOfArithmetic; +	    case INST_BITOR: +		lResult = l1 | l2; +		goto longResultOfArithmetic; +	    case INST_BITXOR: +		lResult = l1 ^ l2; +	    longResultOfArithmetic: +		TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); +		if (Tcl_IsShared(valuePtr)) { +		    TclNewLongObj(objResultPtr, lResult); +		    TRACE(("%s\n", O2S(objResultPtr))); +		    NEXT_INST_F(1, 2, 1);  		} +		TclSetLongObj(valuePtr, lResult); +		TRACE(("%s\n", O2S(valuePtr))); +		NEXT_INST_F(1, 1, 0);  	    } -	     -	case INST_LOR: -	case INST_LAND: -	    { +	} + +	/* +	 * DO NOT MERGE THIS WITH THE EQUIVALENT SECTION LATER! That would +	 * encourage the compiler to inline ExecuteExtendedBinaryMathOp, which +	 * is highly undesirable due to the overall impact on size. +	 */ + +	TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); +	objResultPtr = ExecuteExtendedBinaryMathOp(interp, *pc, &TCONST(0), +		valuePtr, value2Ptr); +	if (objResultPtr == DIVIDED_BY_ZERO) { +	    TRACE_APPEND(("DIVIDE BY ZERO\n")); +	    goto divideByZero; +	} else if (objResultPtr == GENERAL_ARITHMETIC_ERROR) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} else if (objResultPtr == NULL) { +	    TRACE_APPEND(("%s\n", O2S(valuePtr))); +	    NEXT_INST_F(1, 1, 0); +	} else { +	    TRACE_APPEND(("%s\n", O2S(objResultPtr))); +	    NEXT_INST_F(1, 2, 1); +	} + +    case INST_EXPON: +    case INST_ADD: +    case INST_SUB: +    case INST_DIV: +    case INST_MULT: +	value2Ptr = OBJ_AT_TOS; +	valuePtr = OBJ_UNDER_TOS; + +	if ((GetNumberFromObj(NULL, valuePtr, &ptr1, &type1) != TCL_OK) +		|| IsErroringNaNType(type1)) { +	    TRACE(("%.20s %.20s => ILLEGAL 1st TYPE %s\n", +		    O2S(value2Ptr), O2S(valuePtr), +		    (valuePtr->typePtr? valuePtr->typePtr->name: "null"))); +	    DECACHE_STACK_INFO(); +	    IllegalExprOperandType(interp, pc, valuePtr); +	    CACHE_STACK_INFO(); +	    goto gotError; +	} + +#ifdef ACCEPT_NAN +	if (type1 == TCL_NUMBER_NAN) { +	    /* +	     * NaN first argument -> result is also NaN. +	     */ + +	    NEXT_INST_F(1, 1, 0); +	} +#endif + +	if ((GetNumberFromObj(NULL, value2Ptr, &ptr2, &type2) != TCL_OK) +		|| IsErroringNaNType(type2)) { +	    TRACE(("%.20s %.20s => ILLEGAL 2nd TYPE %s\n", +		    O2S(value2Ptr), O2S(valuePtr), +		    (value2Ptr->typePtr? value2Ptr->typePtr->name: "null"))); +	    DECACHE_STACK_INFO(); +	    IllegalExprOperandType(interp, pc, value2Ptr); +	    CACHE_STACK_INFO(); +	    goto gotError; +	} + +#ifdef ACCEPT_NAN +	if (type2 == TCL_NUMBER_NAN) { +	    /* +	     * NaN second argument -> result is also NaN. +	     */ + +	    objResultPtr = value2Ptr; +	    NEXT_INST_F(1, 2, 1); +	} +#endif + +	/* +	 * Handle (long,long) arithmetic as best we can without going out to +	 * an external function. +	 */ + +	if ((type1 == TCL_NUMBER_LONG) && (type2 == TCL_NUMBER_LONG)) { +	    Tcl_WideInt w1, w2, wResult; + +	    l1 = *((const long *)ptr1); +	    l2 = *((const long *)ptr2); + +	    switch (*pc) { +	    case INST_ADD: +		w1 = (Tcl_WideInt) l1; +		w2 = (Tcl_WideInt) l2; +		wResult = w1 + w2; +#ifdef TCL_WIDE_INT_IS_LONG  		/* -		 * Operands must be boolean or numeric. No int->double -		 * conversions are performed. +		 * Check for overflow.  		 */ -		 -		int i1, i2; -		int iResult; -		char *s; -		Tcl_ObjType *t1Ptr, *t2Ptr; - -		value2Ptr = POP_OBJECT(); -		valuePtr  = POP_OBJECT(); -		t1Ptr = valuePtr->typePtr; -		t2Ptr = value2Ptr->typePtr; - -		if ((t1Ptr == &tclIntType) || (t1Ptr == &tclBooleanType)) { -		    i1 = (valuePtr->internalRep.longValue != 0); -		} else if (t1Ptr == &tclDoubleType) { -		    i1 = (valuePtr->internalRep.doubleValue != 0.0); -		} else { -		    s = Tcl_GetStringFromObj(valuePtr, &length); -		    if (TclLooksLikeInt(s, length)) { -			result = Tcl_GetLongFromObj((Tcl_Interp *) NULL, -				valuePtr, &i); -			i1 = (i != 0); -		    } else { -			result = Tcl_GetBooleanFromObj((Tcl_Interp *) NULL, -				valuePtr, &i1); -			i1 = (i1 != 0); -		    } -		    if (result != TCL_OK) { -			TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n", -			        O2S(valuePtr), -			        (t1Ptr? t1Ptr->name : "null"))); -			IllegalExprOperandType(interp, pc, valuePtr); -			Tcl_DecrRefCount(valuePtr); -			Tcl_DecrRefCount(value2Ptr); -			goto checkForCatch; -		    } -		} -		 -		if ((t2Ptr == &tclIntType) || (t2Ptr == &tclBooleanType)) { -		    i2 = (value2Ptr->internalRep.longValue != 0); -		} else if (t2Ptr == &tclDoubleType) { -		    i2 = (value2Ptr->internalRep.doubleValue != 0.0); -		} else { -		    s = Tcl_GetStringFromObj(value2Ptr, &length); -		    if (TclLooksLikeInt(s, length)) { -			result = Tcl_GetLongFromObj((Tcl_Interp *) NULL, -				value2Ptr, &i); -			i2 = (i != 0); -		    } else { -			result = Tcl_GetBooleanFromObj((Tcl_Interp *) NULL, -				value2Ptr, &i2); -		    } -		    if (result != TCL_OK) { -			TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n", -			        O2S(value2Ptr), -			        (t2Ptr? t2Ptr->name : "null"))); -			IllegalExprOperandType(interp, pc, value2Ptr); -			Tcl_DecrRefCount(valuePtr); -			Tcl_DecrRefCount(value2Ptr); -			goto checkForCatch; -		    } + +		if (Overflowing(w1, w2, wResult)) { +		    goto overflow;  		} +#endif +		goto wideResultOfArithmetic; +	    case INST_SUB: +		w1 = (Tcl_WideInt) l1; +		w2 = (Tcl_WideInt) l2; +		wResult = w1 - w2; +#ifdef TCL_WIDE_INT_IS_LONG  		/* -		 * Reuse the valuePtr object already on stack if possible. +		 * Must check for overflow. The macro tests for overflows in +		 * sums by looking at the sign bits. As we have a subtraction +		 * here, we are adding -w2. As -w2 could in turn overflow, we +		 * test with ~w2 instead: it has the opposite sign bit to w2 +		 * so it does the job. Note that the only "bad" case (w2==0) +		 * is irrelevant for this macro, as in that case w1 and +		 * wResult have the same sign and there is no overflow anyway.  		 */ -		if (*pc == INST_LOR) { -		    iResult = (i1 || i2); -		} else { -		    iResult = (i1 && i2); +		if (Overflowing(w1, ~w2, wResult)) { +		    goto overflow;  		} +#endif +	    wideResultOfArithmetic: +		TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr)));  		if (Tcl_IsShared(valuePtr)) { -		    PUSH_OBJECT(Tcl_NewLongObj(iResult)); -		    TRACE(("%.20s %.20s => %d\n", -			   O2S(valuePtr), O2S(value2Ptr), iResult)); -		    TclDecrRefCount(valuePtr); -		} else {	/* reuse the valuePtr object */ -		    TRACE(("%.20s %.20s => %d\n",  -			   O2S(valuePtr), O2S(value2Ptr), iResult)); -		    Tcl_SetLongObj(valuePtr, iResult); -		    ++stackTop; /* valuePtr now on stk top has right r.c. */ +		    objResultPtr = Tcl_NewWideIntObj(wResult); +		    TRACE(("%s\n", O2S(objResultPtr))); +		    NEXT_INST_F(1, 2, 1);  		} -		TclDecrRefCount(value2Ptr); -	    } -	    ADJUST_PC(1); +		Tcl_SetWideIntObj(valuePtr, wResult); +		TRACE(("%s\n", O2S(valuePtr))); +		NEXT_INST_F(1, 1, 0); + +	    case INST_DIV: +		if (l2 == 0) { +		    TRACE(("%s %s => DIVIDE BY ZERO\n", +			    O2S(valuePtr), O2S(value2Ptr))); +		    goto divideByZero; +		} else if ((l1 == LONG_MIN) && (l2 == -1)) { +		    /* +		     * Can't represent (-LONG_MIN) as a long. +		     */ -	case INST_LIST_LENGTH: -	    valuePtr = POP_OBJECT(); +		    goto overflow; +		} +		lResult = l1 / l2; -	    result = Tcl_ListObjLength(interp, valuePtr, &length); -	    if (result != TCL_OK) { -		TRACE_WITH_OBJ(("%.30s => ERROR: ", O2S(valuePtr)), -			Tcl_GetObjResult(interp)); -		TclDecrRefCount(valuePtr); -		goto checkForCatch; +		/* +		 * Force Tcl's integer division rules. +		 * TODO: examine for logic simplification +		 */ + +		if (((lResult < 0) || ((lResult == 0) && +			((l1 < 0 && l2 > 0) || (l1 > 0 && l2 < 0)))) && +			((lResult * l2) != l1)) { +		    lResult -= 1; +		} +		goto longResultOfArithmetic; + +	    case INST_MULT: +		if (((sizeof(long) >= 2*sizeof(int)) +			&& (l1 <= INT_MAX) && (l1 >= INT_MIN) +			&& (l2 <= INT_MAX) && (l2 >= INT_MIN)) +			|| ((sizeof(long) >= 2*sizeof(short)) +			&& (l1 <= SHRT_MAX) && (l1 >= SHRT_MIN) +			&& (l2 <= SHRT_MAX) && (l2 >= SHRT_MIN))) { +		    lResult = l1 * l2; +		    goto longResultOfArithmetic; +		}  	    } -	    PUSH_OBJECT(Tcl_NewIntObj(length)); -	    TRACE(("%.20s => %d\n", O2S(valuePtr), length)); -	    TclDecrRefCount(valuePtr); -	    ADJUST_PC(1); -	     -	case INST_LIST_INDEX: -	    { -		Tcl_Obj **elemPtrs; -		int index; -		value2Ptr = POP_OBJECT(); -		valuePtr  = POP_OBJECT(); +	    /* +	     * Fall through with INST_EXPON, INST_DIV and large multiplies. +	     */ +	} -		result = Tcl_ListObjGetElements(interp, valuePtr, -			&length, &elemPtrs); -		if (result != TCL_OK) { -		    TRACE_WITH_OBJ(("%.30s => ERROR: ", O2S(valuePtr)), -			    Tcl_GetObjResult(interp)); -		    TclDecrRefCount(value2Ptr); -		    TclDecrRefCount(valuePtr); -		    goto checkForCatch; -		} +    overflow: +	TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); +	objResultPtr = ExecuteExtendedBinaryMathOp(interp, *pc, &TCONST(0), +		valuePtr, value2Ptr); +	if (objResultPtr == DIVIDED_BY_ZERO) { +	    TRACE_APPEND(("DIVIDE BY ZERO\n")); +	    goto divideByZero; +	} else if (objResultPtr == EXPONENT_OF_ZERO) { +	    TRACE_APPEND(("EXPONENT OF ZERO\n")); +	    goto exponOfZero; +	} else if (objResultPtr == GENERAL_ARITHMETIC_ERROR) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} else if (objResultPtr == NULL) { +	    TRACE_APPEND(("%s\n", O2S(valuePtr))); +	    NEXT_INST_F(1, 1, 0); +	} else { +	    TRACE_APPEND(("%s\n", O2S(objResultPtr))); +	    NEXT_INST_F(1, 2, 1); +	} -		result = TclGetIntForIndex(interp, value2Ptr, length - 1, -			&index); -		if (result != TCL_OK) { -		    TRACE_WITH_OBJ(("%.20s => ERROR: ", O2S(value2Ptr)), -			    Tcl_GetObjResult(interp)); -		    Tcl_DecrRefCount(value2Ptr); -		    Tcl_DecrRefCount(valuePtr); -		    goto checkForCatch; -		} +    case INST_LNOT: { +	int b; -		if ((index < 0) || (index >= length)) { -		    objPtr = Tcl_NewObj(); -		} else { -		    /* -		     * Make sure listPtr still refers to a list object. It -		     * might have been converted to an int above if the -		     * argument objects were shared. -		     */ +	valuePtr = OBJ_AT_TOS; -		    if (valuePtr->typePtr != &tclListType) { -			result = Tcl_ListObjGetElements(interp, valuePtr, -				&length, &elemPtrs); -			if (result != TCL_OK) { -			    TRACE_WITH_OBJ(("%.30s => ERROR: ", O2S(valuePtr)), -				    Tcl_GetObjResult(interp)); -			    TclDecrRefCount(value2Ptr); -			    TclDecrRefCount(valuePtr); -			    goto checkForCatch; -			} -		    } -		    objPtr = elemPtrs[index]; +	/* TODO - check claim that taking address of b harms performance */ +	/* TODO - consider optimization search for constants */ +	if (TclGetBooleanFromObj(NULL, valuePtr, &b) != TCL_OK) { +	    TRACE(("\"%.20s\" => ERROR: illegal type %s\n", O2S(valuePtr), +		    (valuePtr->typePtr? valuePtr->typePtr->name : "null"))); +	    DECACHE_STACK_INFO(); +	    IllegalExprOperandType(interp, pc, valuePtr); +	    CACHE_STACK_INFO(); +	    goto gotError; +	} +	/* TODO: Consider peephole opt. */ +	objResultPtr = TCONST(!b); +	TRACE_WITH_OBJ(("%s => ", O2S(valuePtr)), objResultPtr); +	NEXT_INST_F(1, 1, 1); +    } + +    case INST_BITNOT: +	valuePtr = OBJ_AT_TOS; +	TRACE(("\"%.20s\" => ", O2S(valuePtr))); +	if ((GetNumberFromObj(NULL, valuePtr, &ptr1, &type1) != TCL_OK) +		|| (type1==TCL_NUMBER_NAN) || (type1==TCL_NUMBER_DOUBLE)) { +	    /* +	     * ... ~$NonInteger => raise an error. +	     */ + +	    TRACE_APPEND(("ERROR: illegal type %s\n", +		    (valuePtr->typePtr? valuePtr->typePtr->name : "null"))); +	    DECACHE_STACK_INFO(); +	    IllegalExprOperandType(interp, pc, valuePtr); +	    CACHE_STACK_INFO(); +	    goto gotError; +	} +	if (type1 == TCL_NUMBER_LONG) { +	    l1 = *((const long *) ptr1); +	    if (Tcl_IsShared(valuePtr)) { +		TclNewLongObj(objResultPtr, ~l1); +		TRACE_APPEND(("%s\n", O2S(objResultPtr))); +		NEXT_INST_F(1, 1, 1); +	    } +	    TclSetLongObj(valuePtr, ~l1); +	    TRACE_APPEND(("%s\n", O2S(valuePtr))); +	    NEXT_INST_F(1, 0, 0); +	} +	objResultPtr = ExecuteExtendedUnaryMathOp(*pc, valuePtr); +	if (objResultPtr != NULL) { +	    TRACE_APPEND(("%s\n", O2S(objResultPtr))); +	    NEXT_INST_F(1, 1, 1); +	} else { +	    TRACE_APPEND(("%s\n", O2S(valuePtr))); +	    NEXT_INST_F(1, 0, 0); +	} + +    case INST_UMINUS: +	valuePtr = OBJ_AT_TOS; +	TRACE(("\"%.20s\" => ", O2S(valuePtr))); +	if ((GetNumberFromObj(NULL, valuePtr, &ptr1, &type1) != TCL_OK) +		|| IsErroringNaNType(type1)) { +	    TRACE_APPEND(("ERROR: illegal type %s \n", +		    (valuePtr->typePtr? valuePtr->typePtr->name : "null"))); +	    DECACHE_STACK_INFO(); +	    IllegalExprOperandType(interp, pc, valuePtr); +	    CACHE_STACK_INFO(); +	    goto gotError; +	} +	switch (type1) { +	case TCL_NUMBER_NAN: +	    /* -NaN => NaN */ +	    TRACE_APPEND(("%s\n", O2S(valuePtr))); +	    NEXT_INST_F(1, 0, 0); +	case TCL_NUMBER_LONG: +	    l1 = *((const long *) ptr1); +	    if (l1 != LONG_MIN) { +		if (Tcl_IsShared(valuePtr)) { +		    TclNewLongObj(objResultPtr, -l1); +		    TRACE_APPEND(("%s\n", O2S(objResultPtr))); +		    NEXT_INST_F(1, 1, 1);  		} +		TclSetLongObj(valuePtr, -l1); +		TRACE_APPEND(("%s\n", O2S(valuePtr))); +		NEXT_INST_F(1, 0, 0); +	    } +	    /* FALLTHROUGH */ +	} +	objResultPtr = ExecuteExtendedUnaryMathOp(*pc, valuePtr); +	if (objResultPtr != NULL) { +	    TRACE_APPEND(("%s\n", O2S(objResultPtr))); +	    NEXT_INST_F(1, 1, 1); +	} else { +	    TRACE_APPEND(("%s\n", O2S(valuePtr))); +	    NEXT_INST_F(1, 0, 0); +	} -		PUSH_OBJECT(objPtr); -		TRACE(("%.20s %.20s => %s\n", -			O2S(valuePtr), O2S(value2Ptr), O2S(objPtr))); -		TclDecrRefCount(valuePtr); -		TclDecrRefCount(value2Ptr); +    case INST_UPLUS: +    case INST_TRY_CVT_TO_NUMERIC: +	/* +	 * Try to convert the topmost stack object to numeric object. This is +	 * done in order to support [expr]'s policy of interpreting operands +	 * if at all possible as numbers first, then strings. +	 */ + +	valuePtr = OBJ_AT_TOS; +	TRACE(("\"%.20s\" => ", O2S(valuePtr))); + +	if (GetNumberFromObj(NULL, valuePtr, &ptr1, &type1) != TCL_OK) { +	    if (*pc == INST_UPLUS) { +		/* +		 * ... +$NonNumeric => raise an error. +		 */ + +		TRACE_APPEND(("ERROR: illegal type %s\n", +			(valuePtr->typePtr? valuePtr->typePtr->name:"null"))); +		DECACHE_STACK_INFO(); +		IllegalExprOperandType(interp, pc, valuePtr); +		CACHE_STACK_INFO(); +		goto gotError;  	    } -	    ADJUST_PC(1); -	case INST_STR_EQ: -	case INST_STR_NEQ: -	    { +	    /* ... TryConvertToNumeric($NonNumeric) is acceptable */ +	    TRACE_APPEND(("not numeric\n")); +	    NEXT_INST_F(1, 0, 0); +	} +	if (IsErroringNaNType(type1)) { +	    if (*pc == INST_UPLUS) {  		/* -		 * String (in)equality check +		 * ... +$NonNumeric => raise an error.  		 */ -		int iResult; -		value2Ptr = POP_OBJECT(); -		valuePtr  = POP_OBJECT(); +		TRACE_APPEND(("ERROR: illegal type %s\n", +			(valuePtr->typePtr? valuePtr->typePtr->name:"null"))); +		DECACHE_STACK_INFO(); +		IllegalExprOperandType(interp, pc, valuePtr); +		CACHE_STACK_INFO(); +	    } else { +		/* +		 * Numeric conversion of NaN -> error. +		 */ -		if (valuePtr == value2Ptr) { -		    /* -		     * On the off-chance that the objects are the same, -		     * we don't really have to think hard about equality. -		     */ -		    iResult = (*pc == INST_STR_EQ); -		} else { -		    char *s1, *s2; -		    int s1len, s2len; +		TRACE_APPEND(("ERROR: IEEE floating pt error\n")); +		DECACHE_STACK_INFO(); +		TclExprFloatError(interp, *((const double *) ptr1)); +		CACHE_STACK_INFO(); +	    } +	    goto gotError; +	} -		    s1 = Tcl_GetStringFromObj(valuePtr, &s1len); -		    s2 = Tcl_GetStringFromObj(value2Ptr, &s2len); -		    if (s1len == s2len) { -			/* -			 * We only need to check (in)equality when -			 * we have equal length strings. -			 */ -			if (*pc == INST_STR_NEQ) { -			    iResult = (strcmp(s1, s2) != 0); -			} else { -			    /* INST_STR_EQ */ -			    iResult = (strcmp(s1, s2) == 0); +	/* +	 * Ensure that the numeric value has a string rep the same as the +	 * formatted version of its internal rep. This is used, e.g., to make +	 * sure that "expr {0001}" yields "1", not "0001". We implement this +	 * by _discarding_ the string rep since we know it will be +	 * regenerated, if needed later, by formatting the internal rep's +	 * value. +	 */ + +	if (valuePtr->bytes == NULL) { +	    TRACE_APPEND(("numeric, same Tcl_Obj\n")); +	    NEXT_INST_F(1, 0, 0); +	} +	if (Tcl_IsShared(valuePtr)) { +	    /* +	     * Here we do some surgery within the Tcl_Obj internals. We want +	     * to copy the intrep, but not the string, so we temporarily hide +	     * the string so we do not copy it. +	     */ + +	    char *savedString = valuePtr->bytes; + +	    valuePtr->bytes = NULL; +	    objResultPtr = Tcl_DuplicateObj(valuePtr); +	    valuePtr->bytes = savedString; +	    TRACE_APPEND(("numeric, new Tcl_Obj\n")); +	    NEXT_INST_F(1, 1, 1); +	} +	TclInvalidateStringRep(valuePtr); +	TRACE_APPEND(("numeric, same Tcl_Obj\n")); +	NEXT_INST_F(1, 0, 0); +    } + +    /* +     *	   End of numeric operator instructions. +     * ----------------------------------------------------------------- +     */ + +    case INST_TRY_CVT_TO_BOOLEAN: +	valuePtr = OBJ_AT_TOS; +	if (valuePtr->typePtr == &tclBooleanType) { +	    objResultPtr = TCONST(1); +	} else { +	    int result = (TclSetBooleanFromAny(NULL, valuePtr) == TCL_OK); +	    objResultPtr = TCONST(result); +	} +	TRACE_WITH_OBJ(("\"%.30s\" => ", O2S(valuePtr)), objResultPtr); +	NEXT_INST_F(1, 0, 1); + +    case INST_BREAK: +	/* +	DECACHE_STACK_INFO(); +	Tcl_ResetResult(interp); +	CACHE_STACK_INFO(); +	*/ +	result = TCL_BREAK; +	cleanup = 0; +	TRACE(("=> BREAK!\n")); +	goto processExceptionReturn; + +    case INST_CONTINUE: +	/* +	DECACHE_STACK_INFO(); +	Tcl_ResetResult(interp); +	CACHE_STACK_INFO(); +	*/ +	result = TCL_CONTINUE; +	cleanup = 0; +	TRACE(("=> CONTINUE!\n")); +	goto processExceptionReturn; + +    { +	ForeachInfo *infoPtr; +	Var *iterVarPtr, *listVarPtr; +	Tcl_Obj *oldValuePtr, *listPtr, **elements; +	ForeachVarList *varListPtr; +	int numLists, iterNum, listTmpIndex, listLen, numVars; +	int varIndex, valIndex, continueLoop, j, iterTmpIndex; +	long i; + +    case INST_FOREACH_START4: /* DEPRECATED */ +	/* +	 * Initialize the temporary local var that holds the count of the +	 * number of iterations of the loop body to -1. +	 */ + +	opnd = TclGetUInt4AtPtr(pc+1); +	infoPtr = codePtr->auxDataArrayPtr[opnd].clientData; +	iterTmpIndex = infoPtr->loopCtTemp; +	iterVarPtr = LOCAL(iterTmpIndex); +	oldValuePtr = iterVarPtr->value.objPtr; + +	if (oldValuePtr == NULL) { +	    TclNewLongObj(iterVarPtr->value.objPtr, -1); +	    Tcl_IncrRefCount(iterVarPtr->value.objPtr); +	} else { +	    TclSetLongObj(oldValuePtr, -1); +	} +	TRACE(("%u => loop iter count temp %d\n", opnd, iterTmpIndex)); + +#ifndef TCL_COMPILE_DEBUG +	/* +	 * Remark that the compiler ALWAYS sets INST_FOREACH_STEP4 immediately +	 * after INST_FOREACH_START4 - let us just fall through instead of +	 * jumping back to the top. +	 */ + +	pc += 5; +	TCL_DTRACE_INST_NEXT(); +#else +	NEXT_INST_F(5, 0, 0); +#endif + +    case INST_FOREACH_STEP4: /* DEPRECATED */ +	/* +	 * "Step" a foreach loop (i.e., begin its next iteration) by assigning +	 * the next value list element to each loop var. +	 */ + +	opnd = TclGetUInt4AtPtr(pc+1); +	TRACE(("%u => ", opnd)); +	infoPtr = codePtr->auxDataArrayPtr[opnd].clientData; +	numLists = infoPtr->numLists; + +	/* +	 * Increment the temp holding the loop iteration number. +	 */ + +	iterVarPtr = LOCAL(infoPtr->loopCtTemp); +	valuePtr = iterVarPtr->value.objPtr; +	iterNum = valuePtr->internalRep.longValue + 1; +	TclSetLongObj(valuePtr, iterNum); + +	/* +	 * Check whether all value lists are exhausted and we should stop the +	 * loop. +	 */ + +	continueLoop = 0; +	listTmpIndex = infoPtr->firstValueTemp; +	for (i = 0;  i < numLists;  i++) { +	    varListPtr = infoPtr->varLists[i]; +	    numVars = varListPtr->numVars; + +	    listVarPtr = LOCAL(listTmpIndex); +	    listPtr = listVarPtr->value.objPtr; +	    if (TclListObjLength(interp, listPtr, &listLen) != TCL_OK) { +		TRACE_APPEND(("ERROR converting list %ld, \"%.30s\": %s\n", +			i, O2S(listPtr), O2S(Tcl_GetObjResult(interp)))); +		goto gotError; +	    } +	    if (listLen > iterNum * numVars) { +		continueLoop = 1; +	    } +	    listTmpIndex++; +	} + +	/* +	 * If some var in some var list still has a remaining list element +	 * iterate one more time. Assign to var the next element from its +	 * value list. We already checked above that each list temp holds a +	 * valid list object (by calling Tcl_ListObjLength), but cannot rely +	 * on that check remaining valid: one list could have been shimmered +	 * as a side effect of setting a traced variable. +	 */ + +	if (continueLoop) { +	    listTmpIndex = infoPtr->firstValueTemp; +	    for (i = 0;  i < numLists;  i++) { +		varListPtr = infoPtr->varLists[i]; +		numVars = varListPtr->numVars; + +		listVarPtr = LOCAL(listTmpIndex); +		listPtr = TclListObjCopy(NULL, listVarPtr->value.objPtr); +		TclListObjGetElements(interp, listPtr, &listLen, &elements); + +		valIndex = (iterNum * numVars); +		for (j = 0;  j < numVars;  j++) { +		    if (valIndex >= listLen) { +			TclNewObj(valuePtr); +		    } else { +			valuePtr = elements[valIndex]; +		    } + +		    varIndex = varListPtr->varIndexes[j]; +		    varPtr = LOCAL(varIndex); +		    while (TclIsVarLink(varPtr)) { +			varPtr = varPtr->value.linkPtr; +		    } +		    if (TclIsVarDirectWritable(varPtr)) { +			value2Ptr = varPtr->value.objPtr; +			if (valuePtr != value2Ptr) { +			    if (value2Ptr != NULL) { +				TclDecrRefCount(value2Ptr); +			    } +			    varPtr->value.objPtr = valuePtr; +			    Tcl_IncrRefCount(valuePtr);  			}  		    } else { -			iResult = (*pc == INST_STR_NEQ); +			DECACHE_STACK_INFO(); +			if (TclPtrSetVar(interp, varPtr, NULL, NULL, NULL, +				valuePtr, TCL_LEAVE_ERR_MSG, varIndex)==NULL){ +			    CACHE_STACK_INFO(); +			    TRACE_APPEND(( +				    "ERROR init. index temp %d: %s\n", +				    varIndex, O2S(Tcl_GetObjResult(interp)))); +			    TclDecrRefCount(listPtr); +			    goto gotError; +			} +			CACHE_STACK_INFO();  		    } +		    valIndex++;  		} +		TclDecrRefCount(listPtr); +		listTmpIndex++; +	    } +	} +	TRACE_APPEND(("%d lists, iter %d, %s loop\n", +		numLists, iterNum, (continueLoop? "continue" : "exit"))); -		PUSH_OBJECT(Tcl_NewIntObj(iResult)); -		TRACE(("%.20s %.20s => %d\n", -			O2S(valuePtr), O2S(value2Ptr), iResult)); -		TclDecrRefCount(valuePtr); -		TclDecrRefCount(value2Ptr); +	/* +	 * Run-time peep-hole optimisation: the compiler ALWAYS follows +	 * INST_FOREACH_STEP4 with an INST_JUMP_FALSE. We just skip that +	 * instruction and jump direct from here. +	 */ + +	pc += 5; +	if (*pc == INST_JUMP_FALSE1) { +	    NEXT_INST_F((continueLoop? 2 : TclGetInt1AtPtr(pc+1)), 0, 0); +	} else { +	    NEXT_INST_F((continueLoop? 5 : TclGetInt4AtPtr(pc+1)), 0, 0); +	} + +    } +    { +	ForeachInfo *infoPtr; +	Tcl_Obj *listPtr, **elements, *tmpPtr; +	ForeachVarList *varListPtr; +	int numLists, iterMax, listLen, numVars; +	int iterTmp, iterNum, listTmpDepth; +	int varIndex, valIndex, j; +	long i; + +    case INST_FOREACH_START: +	/* +	 * Initialize the data for the looping construct, pushing the +	 * corresponding Tcl_Objs to the stack. +	 */ + +	opnd = TclGetUInt4AtPtr(pc+1); +	infoPtr = codePtr->auxDataArrayPtr[opnd].clientData; +	numLists = infoPtr->numLists; +	TRACE(("%u => ", opnd)); + +	/* +	 * Compute the number of iterations that will be run: iterMax +	 */ + +	iterMax = 0; +	listTmpDepth = numLists-1; +	for (i = 0;  i < numLists;  i++) { +	    varListPtr = infoPtr->varLists[i]; +	    numVars = varListPtr->numVars; +	    listPtr = OBJ_AT_DEPTH(listTmpDepth); +	    if (TclListObjLength(interp, listPtr, &listLen) != TCL_OK) { +		TRACE_APPEND(("ERROR converting list %ld, \"%s\": %s", +			i, O2S(listPtr), O2S(Tcl_GetObjResult(interp)))); +		goto gotError; +	    } +	    if (Tcl_IsShared(listPtr)) { +		objPtr = TclListObjCopy(NULL, listPtr); +		Tcl_IncrRefCount(objPtr); +		Tcl_DecrRefCount(listPtr); +		OBJ_AT_DEPTH(listTmpDepth) = objPtr;  	    } -	    ADJUST_PC(1); +	    iterTmp = (listLen + (numVars - 1))/numVars; +	    if (iterTmp > iterMax) { +		iterMax = iterTmp; +	    } +	    listTmpDepth--; +	} -	case INST_STR_CMP: -	    { -		/* -		 * String compare -		 */ -		char *s1, *s2; -		int s1len, s2len, iResult; +	/* +	 * Store the iterNum and iterMax in a single Tcl_Obj; we keep a +	 * nul-string obj with the pointer stored in the ptrValue so that the +	 * thing is properly garbage collected. THIS OBJ MAKES NO SENSE, but +	 * it will never leave this scope and is read-only. +	 */ -		value2Ptr = POP_OBJECT(); -		valuePtr  = POP_OBJECT(); +	TclNewObj(tmpPtr); +	tmpPtr->internalRep.twoPtrValue.ptr1 = INT2PTR(0); +	tmpPtr->internalRep.twoPtrValue.ptr2 = INT2PTR(iterMax); +	PUSH_OBJECT(tmpPtr); /* iterCounts object */ -		/* -		 * The comparison function should compare up to the -		 * minimum byte length only. -		 */ -		if ((valuePtr->typePtr == &tclByteArrayType) && -			(value2Ptr->typePtr == &tclByteArrayType)) { -		    s1 = (char *) Tcl_GetByteArrayFromObj(valuePtr, &s1len); -		    s2 = (char *) Tcl_GetByteArrayFromObj(value2Ptr, &s2len); -		    iResult = memcmp(s1, s2, -			    (size_t) ((s1len < s2len) ? s1len : s2len)); -		} else { -#if 0 -		    /* -		     * This solution is less mem intensive, but it is -		     * computationally expensive as the string grows.  The -		     * reason that we can't use a memcmp is that UTF-8 strings -		     * that contain a \u0000 can't be compared with memcmp.  If -		     * we knew that the string was ascii-7 or had no null byte, -		     * we could just do memcmp and save all the hassle. -		     */ -		    s1 = Tcl_GetStringFromObj(valuePtr, &s1len); -		    s2 = Tcl_GetStringFromObj(value2Ptr, &s2len); -		    iResult = Tcl_UtfNcmp(s1, s2, -			    (size_t) ((s1len < s2len) ? s1len : s2len)); +	/* +	 * Store a pointer to the ForeachInfo struct; same dirty trick +	 * as above +	 */ + +	TclNewObj(tmpPtr); +	tmpPtr->internalRep.otherValuePtr = infoPtr; +	PUSH_OBJECT(tmpPtr); /* infoPtr object */ +	TRACE_APPEND(("jump to loop step\n")); + +	/* +	 * Jump directly to the INST_FOREACH_STEP instruction; the C code just +	 * falls through. +	 */ + +	pc += 5 - infoPtr->loopCtTemp; + +    case INST_FOREACH_STEP: +	/* +	 * "Step" a foreach loop (i.e., begin its next iteration) by assigning +	 * the next value list element to each loop var. +	 */ + +	tmpPtr = OBJ_AT_TOS; +	infoPtr = tmpPtr->internalRep.otherValuePtr; +	numLists = infoPtr->numLists; +	TRACE(("=> ")); + +	tmpPtr = OBJ_AT_DEPTH(1); +	iterNum = PTR2INT(tmpPtr->internalRep.twoPtrValue.ptr1); +	iterMax = PTR2INT(tmpPtr->internalRep.twoPtrValue.ptr2); + +	/* +	 * If some list still has a remaining list element iterate one more +	 * time. Assign to var the next element from its value list. +	 */ + +	if (iterNum < iterMax) { +	    /* +	     * Set the variables and jump back to run the body +	     */ + +	    tmpPtr->internalRep.twoPtrValue.ptr1 = INT2PTR(iterNum + 1); + +	    listTmpDepth = numLists + 1; + +	    for (i = 0;  i < numLists;  i++) { +		varListPtr = infoPtr->varLists[i]; +		numVars = varListPtr->numVars; + +		listPtr = OBJ_AT_DEPTH(listTmpDepth); +		TclListObjGetElements(interp, listPtr, &listLen, &elements); + +		valIndex = (iterNum * numVars); +		for (j = 0;  j < numVars;  j++) { +		    if (valIndex >= listLen) { +			TclNewObj(valuePtr); +		    } else { +			valuePtr = elements[valIndex]; +		    } + +		    varIndex = varListPtr->varIndexes[j]; +		    varPtr = LOCAL(varIndex); +		    while (TclIsVarLink(varPtr)) { +			varPtr = varPtr->value.linkPtr; +		    } +		    if (TclIsVarDirectWritable(varPtr)) { +			value2Ptr = varPtr->value.objPtr; +			if (valuePtr != value2Ptr) { +			    if (value2Ptr != NULL) { +				TclDecrRefCount(value2Ptr); +			    } +			    varPtr->value.objPtr = valuePtr; +			    Tcl_IncrRefCount(valuePtr); +			} +		    } else { +			DECACHE_STACK_INFO(); +			if (TclPtrSetVar(interp, varPtr, NULL, NULL, NULL, +				valuePtr, TCL_LEAVE_ERR_MSG, varIndex)==NULL){ +			    CACHE_STACK_INFO(); +			    TRACE_APPEND(("ERROR init. index temp %d: %.30s", +				    varIndex, O2S(Tcl_GetObjResult(interp)))); +			    goto gotError; +			} +			CACHE_STACK_INFO(); +		    } +		    valIndex++; +		} +		listTmpDepth--; +	    } +	    TRACE_APPEND(("jump to loop start\n")); +	    /* loopCtTemp being 'misused' for storing the jump size */ +	    NEXT_INST_F(infoPtr->loopCtTemp, 0, 0); +	} + +	TRACE_APPEND(("loop has no more iterations\n")); +#ifdef TCL_COMPILE_DEBUG +	NEXT_INST_F(1, 0, 0);  #else -		    /* -		     * The alternative is to break this into more code -		     * that does type sensitive comparison, as done in -		     * Tcl_StringObjCmd. -		     */ -		    Tcl_UniChar *uni1, *uni2; -		    uni1 = Tcl_GetUnicodeFromObj(valuePtr, &s1len); -		    uni2 = Tcl_GetUnicodeFromObj(value2Ptr, &s2len); -		    iResult = Tcl_UniCharNcmp(uni1, uni2, -			    (unsigned) ((s1len < s2len) ? s1len : s2len)); +	/* +	 * FALL THROUGH +	 */ +	pc++;  #endif + +    case INST_FOREACH_END: +	/* THIS INSTRUCTION IS ONLY CALLED AS A BREAK TARGET */ +	tmpPtr = OBJ_AT_TOS; +	infoPtr = tmpPtr->internalRep.otherValuePtr; +	numLists = infoPtr->numLists; +	TRACE(("=> loop terminated\n")); +	NEXT_INST_V(1, numLists+2, 0); + +    case INST_LMAP_COLLECT: +	/* +	 * This instruction is only issued by lmap. The stack is: +	 *   - result +	 *   - infoPtr +	 *   - loop counters +	 *   - valLists +	 *   - collecting obj (unshared) +	 * The instruction lappends the result to the collecting obj. +	 */ + +	tmpPtr = OBJ_AT_DEPTH(1); +	infoPtr = tmpPtr->internalRep.otherValuePtr; +	numLists = infoPtr->numLists; +	TRACE_APPEND(("=> appending to list at depth %d\n", 3 + numLists)); +	 +	objPtr = OBJ_AT_DEPTH(3 + numLists); +	Tcl_ListObjAppendElement(NULL, objPtr, OBJ_AT_TOS); +	NEXT_INST_F(1, 1, 0); +    } + +    case INST_BEGIN_CATCH4: +	/* +	 * Record start of the catch command with exception range index equal +	 * to the operand. Push the current stack depth onto the special catch +	 * stack. +	 */ + +	*(++catchTop) = CURR_DEPTH; +	TRACE(("%u => catchTop=%d, stackTop=%d\n", +		TclGetUInt4AtPtr(pc+1), (int) (catchTop - initCatchTop - 1), +		(int) CURR_DEPTH)); +	NEXT_INST_F(5, 0, 0); + +    case INST_END_CATCH: +	catchTop--; +	DECACHE_STACK_INFO(); +	Tcl_ResetResult(interp); +	CACHE_STACK_INFO(); +	result = TCL_OK; +	TRACE(("=> catchTop=%d\n", (int) (catchTop - initCatchTop - 1))); +	NEXT_INST_F(1, 0, 0); + +    case INST_PUSH_RESULT: +	objResultPtr = Tcl_GetObjResult(interp); +	TRACE_WITH_OBJ(("=> "), objResultPtr); + +	/* +	 * See the comments at INST_INVOKE_STK +	 */ + +	TclNewObj(objPtr); +	Tcl_IncrRefCount(objPtr); +	iPtr->objResultPtr = objPtr; +	NEXT_INST_F(1, 0, -1); + +    case INST_PUSH_RETURN_CODE: +	TclNewIntObj(objResultPtr, result); +	TRACE(("=> %u\n", result)); +	NEXT_INST_F(1, 0, 1); + +    case INST_PUSH_RETURN_OPTIONS: +	DECACHE_STACK_INFO(); +	objResultPtr = Tcl_GetReturnOptions(interp, result); +	CACHE_STACK_INFO(); +	TRACE_WITH_OBJ(("=> "), objResultPtr); +	NEXT_INST_F(1, 0, 1); + +    case INST_RETURN_CODE_BRANCH: { +	int code; + +	if (TclGetIntFromObj(NULL, OBJ_AT_TOS, &code) != TCL_OK) { +	    Tcl_Panic("INST_RETURN_CODE_BRANCH: TOS not a return code!"); +	} +	if (code == TCL_OK) { +	    Tcl_Panic("INST_RETURN_CODE_BRANCH: TOS is TCL_OK!"); +	} +	if (code < TCL_ERROR || code > TCL_CONTINUE) { +	    code = TCL_CONTINUE + 1; +	} +	TRACE(("\"%s\" => jump offset %d\n", O2S(OBJ_AT_TOS), 2*code-1)); +	NEXT_INST_F(2*code-1, 1, 0); +    } + +    /* +     * ----------------------------------------------------------------- +     *	   Start of dictionary-related instructions. +     */ + +    { +	int opnd2, allocateDict, done, i, allocdict; +	Tcl_Obj *dictPtr, *statePtr, *keyPtr, *listPtr, *varNamePtr, *keysPtr; +	Tcl_Obj *emptyPtr, **keyPtrPtr; +	Tcl_DictSearch *searchPtr; +	DictUpdateInfo *duiPtr; + +    case INST_DICT_VERIFY: +	dictPtr = OBJ_AT_TOS; +	TRACE(("\"%.30s\" => ", O2S(dictPtr))); +	if (Tcl_DictObjSize(interp, dictPtr, &done) != TCL_OK) { +	    TRACE_APPEND(("ERROR verifying dictionary nature of \"%.30s\": %s\n", +		    O2S(dictPtr), O2S(Tcl_GetObjResult(interp)))); +	    goto gotError; +	} +	TRACE_APPEND(("OK\n")); +	NEXT_INST_F(1, 1, 0); + +    case INST_DICT_GET: +    case INST_DICT_EXISTS: { +	register Tcl_Interp *interp2 = interp; +	register int found; + +	opnd = TclGetUInt4AtPtr(pc+1); +	TRACE(("%u => ", opnd)); +	dictPtr = OBJ_AT_DEPTH(opnd); +	if (*pc == INST_DICT_EXISTS) { +	    interp2 = NULL; +	} +	if (opnd > 1) { +	    dictPtr = TclTraceDictPath(interp2, dictPtr, opnd-1, +		    &OBJ_AT_DEPTH(opnd-1), DICT_PATH_READ); +	    if (dictPtr == NULL) { +		if (*pc == INST_DICT_EXISTS) { +		    found = 0; +		    goto afterDictExists;  		} +		TRACE_WITH_OBJ(( +			"ERROR tracing dictionary path into \"%.30s\": ", +			O2S(OBJ_AT_DEPTH(opnd))), +			Tcl_GetObjResult(interp)); +		goto gotError; +	    } +	} +	if (Tcl_DictObjGet(interp2, dictPtr, OBJ_AT_TOS, +		&objResultPtr) == TCL_OK) { +	    if (*pc == INST_DICT_EXISTS) { +		found = (objResultPtr ? 1 : 0); +		goto afterDictExists; +	    } +	    if (!objResultPtr) { +		Tcl_SetObjResult(interp, Tcl_ObjPrintf( +			"key \"%s\" not known in dictionary", +			TclGetString(OBJ_AT_TOS))); +		DECACHE_STACK_INFO(); +		Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "DICT", +			TclGetString(OBJ_AT_TOS), NULL); +		CACHE_STACK_INFO(); +		TRACE_ERROR(interp); +		goto gotError; +	    } +	    TRACE_APPEND(("%.30s\n", O2S(objResultPtr))); +	    NEXT_INST_V(5, opnd+1, 1); +	} else if (*pc != INST_DICT_EXISTS) { +	    TRACE_APPEND(("ERROR reading leaf dictionary key \"%.30s\": %s", +		    O2S(dictPtr), O2S(Tcl_GetObjResult(interp)))); +	    goto gotError; +	} else { +	    found = 0; +	} +    afterDictExists: +	TRACE_APPEND(("%d\n", found)); -		/* -		 * Make sure only -1,0,1 is returned -		 */ -		if (iResult == 0) { -		    iResult = s1len - s2len; +	/* +	 * The INST_DICT_EXISTS instruction is usually followed by a +	 * conditional jump, so we can take advantage of this to do some +	 * peephole optimization (note that we're careful to not close out +	 * someone doing something else). +	 */ + +	JUMP_PEEPHOLE_V(found, 5, opnd+1); +    } + +    case INST_DICT_SET: +    case INST_DICT_UNSET: +    case INST_DICT_INCR_IMM: +	opnd = TclGetUInt4AtPtr(pc+1); +	opnd2 = TclGetUInt4AtPtr(pc+5); + +	varPtr = LOCAL(opnd2); +	while (TclIsVarLink(varPtr)) { +	    varPtr = varPtr->value.linkPtr; +	} +	TRACE(("%u %u => ", opnd, opnd2)); +	if (TclIsVarDirectReadable(varPtr)) { +	    dictPtr = varPtr->value.objPtr; +	} else { +	    DECACHE_STACK_INFO(); +	    dictPtr = TclPtrGetVar(interp, varPtr, NULL,NULL,NULL, 0, opnd2); +	    CACHE_STACK_INFO(); +	} +	if (dictPtr == NULL) { +	    TclNewObj(dictPtr); +	    allocateDict = 1; +	} else { +	    allocateDict = Tcl_IsShared(dictPtr); +	    if (allocateDict) { +		dictPtr = Tcl_DuplicateObj(dictPtr); +	    } +	} + +	switch (*pc) { +	case INST_DICT_SET: +	    cleanup = opnd + 1; +	    result = Tcl_DictObjPutKeyList(interp, dictPtr, opnd, +		    &OBJ_AT_DEPTH(opnd), OBJ_AT_TOS); +	    break; +	case INST_DICT_INCR_IMM: +	    cleanup = 1; +	    opnd = TclGetInt4AtPtr(pc+1); +	    result = Tcl_DictObjGet(interp, dictPtr, OBJ_AT_TOS, &valuePtr); +	    if (result != TCL_OK) { +		break; +	    } +	    if (valuePtr == NULL) { +		Tcl_DictObjPut(NULL, dictPtr, OBJ_AT_TOS,Tcl_NewIntObj(opnd)); +	    } else { +		value2Ptr = Tcl_NewIntObj(opnd); +		Tcl_IncrRefCount(value2Ptr); +		if (Tcl_IsShared(valuePtr)) { +		    valuePtr = Tcl_DuplicateObj(valuePtr); +		    Tcl_DictObjPut(NULL, dictPtr, OBJ_AT_TOS, valuePtr);  		} -		if (iResult < 0) { -		    iResult = -1; -		} else if (iResult > 0) { -		    iResult = 1; +		result = TclIncrObj(interp, valuePtr, value2Ptr); +		if (result == TCL_OK) { +		    TclInvalidateStringRep(dictPtr);  		} - -		PUSH_OBJECT(Tcl_NewIntObj(iResult)); -		TRACE(("%.20s %.20s => %d\n", -			O2S(valuePtr), O2S(value2Ptr), iResult)); -		TclDecrRefCount(valuePtr);  		TclDecrRefCount(value2Ptr);  	    } -	    ADJUST_PC(1); +	    break; +	case INST_DICT_UNSET: +	    cleanup = opnd; +	    result = Tcl_DictObjRemoveKeyList(interp, dictPtr, opnd, +		    &OBJ_AT_DEPTH(opnd-1)); +	    break; +	default: +	    cleanup = 0; /* stop compiler warning */ +	    Tcl_Panic("Should not happen!"); +	} -       case INST_STR_LEN: -	    { -		int length1; -		  -		valuePtr = POP_OBJECT(); +	if (result != TCL_OK) { +	    if (allocateDict) { +		TclDecrRefCount(dictPtr); +	    } +	    TRACE_APPEND(("ERROR updating dictionary: %s\n", +		    O2S(Tcl_GetObjResult(interp)))); +	    goto checkForCatch; +	} -		if (valuePtr->typePtr == &tclByteArrayType) { -		    (void) Tcl_GetByteArrayFromObj(valuePtr, &length1); -		} else { -		    length1 = Tcl_GetCharLength(valuePtr); +	if (TclIsVarDirectWritable(varPtr)) { +	    if (allocateDict) { +		value2Ptr = varPtr->value.objPtr; +		Tcl_IncrRefCount(dictPtr); +		if (value2Ptr != NULL) { +		    TclDecrRefCount(value2Ptr);  		} -		PUSH_OBJECT(Tcl_NewIntObj(length1)); -		TRACE(("%.20s => %d\n", O2S(valuePtr), length1)); -		TclDecrRefCount(valuePtr); +		varPtr->value.objPtr = dictPtr;  	    } -	    ADJUST_PC(1); -	     -       case INST_STR_INDEX: -	    { +	    objResultPtr = dictPtr; +	} else { +	    Tcl_IncrRefCount(dictPtr); +	    DECACHE_STACK_INFO(); +	    objResultPtr = TclPtrSetVar(interp, varPtr, NULL, NULL, NULL, +		    dictPtr, TCL_LEAVE_ERR_MSG, opnd2); +	    CACHE_STACK_INFO(); +	    TclDecrRefCount(dictPtr); +	    if (objResultPtr == NULL) { +		TRACE_ERROR(interp); +		goto gotError; +	    } +	} +#ifndef TCL_COMPILE_DEBUG +	if (*(pc+9) == INST_POP) { +	    NEXT_INST_V(10, cleanup, 0); +	} +#endif +	TRACE_APPEND(("\"%.30s\"\n", O2S(objResultPtr))); +	NEXT_INST_V(9, cleanup, 1); + +    case INST_DICT_APPEND: +    case INST_DICT_LAPPEND: +	opnd = TclGetUInt4AtPtr(pc+1); +	varPtr = LOCAL(opnd); +	while (TclIsVarLink(varPtr)) { +	    varPtr = varPtr->value.linkPtr; +	} +	TRACE(("%u => ", opnd)); +	if (TclIsVarDirectReadable(varPtr)) { +	    dictPtr = varPtr->value.objPtr; +	} else { +	    DECACHE_STACK_INFO(); +	    dictPtr = TclPtrGetVar(interp, varPtr, NULL, NULL, NULL, 0, opnd); +	    CACHE_STACK_INFO(); +	} +	if (dictPtr == NULL) { +	    TclNewObj(dictPtr); +	    allocateDict = 1; +	} else { +	    allocateDict = Tcl_IsShared(dictPtr); +	    if (allocateDict) { +		dictPtr = Tcl_DuplicateObj(dictPtr); +	    } +	} + +	if (Tcl_DictObjGet(interp, dictPtr, OBJ_UNDER_TOS, +		&valuePtr) != TCL_OK) { +	    if (allocateDict) { +		TclDecrRefCount(dictPtr); +	    } +	    TRACE_ERROR(interp); +	    goto gotError; +	} + +	/* +	 * Note that a non-existent key results in a NULL valuePtr, which is a +	 * case handled separately below. What we *can* say at this point is +	 * that the write-back will always succeed. +	 */ + +	switch (*pc) { +	case INST_DICT_APPEND: +	    if (valuePtr == NULL) { +		Tcl_DictObjPut(NULL, dictPtr, OBJ_UNDER_TOS, OBJ_AT_TOS); +	    } else if (Tcl_IsShared(valuePtr)) { +		valuePtr = Tcl_DuplicateObj(valuePtr); +		Tcl_AppendObjToObj(valuePtr, OBJ_AT_TOS); +		Tcl_DictObjPut(NULL, dictPtr, OBJ_UNDER_TOS, valuePtr); +	    } else { +		Tcl_AppendObjToObj(valuePtr, OBJ_AT_TOS); +  		/* -		 * String compare +		 * Must invalidate the string representation of dictionary +		 * here because we have directly updated the internal +		 * representation; if we don't, callers could see the wrong +		 * string rep despite the internal version of the dictionary +		 * having the correct value. [Bug 3079830]  		 */ -		int index; -		bytes = NULL; /* lint */ -		value2Ptr = POP_OBJECT(); -		valuePtr  = POP_OBJECT(); +		TclInvalidateStringRep(dictPtr); +	    } +	    break; +	case INST_DICT_LAPPEND: +	    /* +	     * More complex because list-append can fail. +	     */ + +	    if (valuePtr == NULL) { +		Tcl_DictObjPut(NULL, dictPtr, OBJ_UNDER_TOS, +			Tcl_NewListObj(1, &OBJ_AT_TOS)); +		break; +	    } else if (Tcl_IsShared(valuePtr)) { +		valuePtr = Tcl_DuplicateObj(valuePtr); +		if (Tcl_ListObjAppendElement(interp, valuePtr, +			OBJ_AT_TOS) != TCL_OK) { +		    TclDecrRefCount(valuePtr); +		    if (allocateDict) { +			TclDecrRefCount(dictPtr); +		    } +		    TRACE_ERROR(interp); +		    goto gotError; +		} +		Tcl_DictObjPut(NULL, dictPtr, OBJ_UNDER_TOS, valuePtr); +	    } else { +		if (Tcl_ListObjAppendElement(interp, valuePtr, +			OBJ_AT_TOS) != TCL_OK) { +		    if (allocateDict) { +			TclDecrRefCount(dictPtr); +		    } +		    TRACE_ERROR(interp); +		    goto gotError; +		}  		/* -		 * If we have a ByteArray object, avoid indexing in the -		 * Utf string since the byte array contains one byte per -		 * character.  Otherwise, use the Unicode string rep to -		 * get the index'th char. +		 * Must invalidate the string representation of dictionary +		 * here because we have directly updated the internal +		 * representation; if we don't, callers could see the wrong +		 * string rep despite the internal version of the dictionary +		 * having the correct value. [Bug 3079830]  		 */ -		if (valuePtr->typePtr == &tclByteArrayType) { -		    bytes = (char *)Tcl_GetByteArrayFromObj(valuePtr, &length); -		} else { -		    /* -		     * Get Unicode char length to calulate what 'end' means. -		     */ -		    length = Tcl_GetCharLength(valuePtr); -		} +		TclInvalidateStringRep(dictPtr); +	    } +	    break; +	default: +	    Tcl_Panic("Should not happen!"); +	} -		result = TclGetIntForIndex(interp, value2Ptr, length - 1, -			&index); -		if (result != TCL_OK) { -		    Tcl_DecrRefCount(value2Ptr); -		    Tcl_DecrRefCount(valuePtr); -		    goto checkForCatch; +	if (TclIsVarDirectWritable(varPtr)) { +	    if (allocateDict) { +		value2Ptr = varPtr->value.objPtr; +		Tcl_IncrRefCount(dictPtr); +		if (value2Ptr != NULL) { +		    TclDecrRefCount(value2Ptr);  		} +		varPtr->value.objPtr = dictPtr; +	    } +	    objResultPtr = dictPtr; +	} else { +	    Tcl_IncrRefCount(dictPtr); +	    DECACHE_STACK_INFO(); +	    objResultPtr = TclPtrSetVar(interp, varPtr, NULL, NULL, NULL, +		    dictPtr, TCL_LEAVE_ERR_MSG, opnd); +	    CACHE_STACK_INFO(); +	    TclDecrRefCount(dictPtr); +	    if (objResultPtr == NULL) { +		TRACE_ERROR(interp); +		goto gotError; +	    } +	} +#ifndef TCL_COMPILE_DEBUG +	if (*(pc+5) == INST_POP) { +	    NEXT_INST_F(6, 2, 0); +	} +#endif +	TRACE_APPEND(("%.30s\n", O2S(objResultPtr))); +	NEXT_INST_F(5, 2, 1); + +    case INST_DICT_FIRST: +	opnd = TclGetUInt4AtPtr(pc+1); +	TRACE(("%u => ", opnd)); +	dictPtr = POP_OBJECT(); +	searchPtr = ckalloc(sizeof(Tcl_DictSearch)); +	if (Tcl_DictObjFirst(interp, dictPtr, searchPtr, &keyPtr, +		&valuePtr, &done) != TCL_OK) { +	    ckfree(searchPtr); +	    TRACE_ERROR(interp); +	    goto gotError; +	} +	TclNewObj(statePtr); +	statePtr->typePtr = &dictIteratorType; +	statePtr->internalRep.twoPtrValue.ptr1 = searchPtr; +	statePtr->internalRep.twoPtrValue.ptr2 = dictPtr; +	varPtr = LOCAL(opnd); +	if (varPtr->value.objPtr) { +	    if (varPtr->value.objPtr->typePtr == &dictIteratorType) { +		Tcl_Panic("mis-issued dictFirst!"); +	    } +	    TclDecrRefCount(varPtr->value.objPtr); +	} +	varPtr->value.objPtr = statePtr; +	Tcl_IncrRefCount(statePtr); +	goto pushDictIteratorResult; + +    case INST_DICT_NEXT: +	opnd = TclGetUInt4AtPtr(pc+1); +	TRACE(("%u => ", opnd)); +	statePtr = (*LOCAL(opnd)).value.objPtr; +	if (statePtr == NULL || statePtr->typePtr != &dictIteratorType) { +	    Tcl_Panic("mis-issued dictNext!"); +	} +	searchPtr = statePtr->internalRep.twoPtrValue.ptr1; +	Tcl_DictObjNext(searchPtr, &keyPtr, &valuePtr, &done); +    pushDictIteratorResult: +	if (done) { +	    TclNewObj(emptyPtr); +	    PUSH_OBJECT(emptyPtr); +	    PUSH_OBJECT(emptyPtr); +	} else { +	    PUSH_OBJECT(valuePtr); +	    PUSH_OBJECT(keyPtr); +	} +	TRACE_APPEND(("\"%.30s\" \"%.30s\" %d\n", +		O2S(OBJ_UNDER_TOS), O2S(OBJ_AT_TOS), done)); -		if ((index >= 0) && (index < length)) { -		    if (valuePtr->typePtr == &tclByteArrayType) { -			objPtr = Tcl_NewByteArrayObj((unsigned char *) -				(&bytes[index]), 1); -		    } else { -			char buf[TCL_UTF_MAX]; -			Tcl_UniChar ch; +	/* +	 * The INST_DICT_FIRST and INST_DICT_NEXT instructsions are always +	 * followed by a conditional jump, so we can take advantage of this to +	 * do some peephole optimization (note that we're careful to not close +	 * out someone doing something else). +	 */ -			ch = Tcl_GetUniChar(valuePtr, index); -			/* -			 * This could be: -			 * Tcl_NewUnicodeObj((CONST Tcl_UniChar *)&ch, 1) -			 * but creating the object as a string seems to be -			 * faster in practical use. -			 */ -			length = Tcl_UniCharToUtf(ch, buf); -			objPtr = Tcl_NewStringObj(buf, length); -		    } -		} else { -		    objPtr = Tcl_NewObj(); +	JUMP_PEEPHOLE_F(done, 5, 0); + +    case INST_DICT_UPDATE_START: +	opnd = TclGetUInt4AtPtr(pc+1); +	opnd2 = TclGetUInt4AtPtr(pc+5); +	TRACE(("%u => ", opnd)); +	varPtr = LOCAL(opnd); +	duiPtr = codePtr->auxDataArrayPtr[opnd2].clientData; +	while (TclIsVarLink(varPtr)) { +	    varPtr = varPtr->value.linkPtr; +	} +	if (TclIsVarDirectReadable(varPtr)) { +	    dictPtr = varPtr->value.objPtr; +	} else { +	    DECACHE_STACK_INFO(); +	    dictPtr = TclPtrGetVar(interp, varPtr, NULL, NULL, NULL, +		    TCL_LEAVE_ERR_MSG, opnd); +	    CACHE_STACK_INFO(); +	    if (dictPtr == NULL) { +		TRACE_ERROR(interp); +		goto gotError; +	    } +	} +	if (TclListObjGetElements(interp, OBJ_AT_TOS, &length, +		&keyPtrPtr) != TCL_OK) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} +	if (length != duiPtr->length) { +	    Tcl_Panic("dictUpdateStart argument length mismatch"); +	} +	for (i=0 ; i<length ; i++) { +	    if (Tcl_DictObjGet(interp, dictPtr, keyPtrPtr[i], +		    &valuePtr) != TCL_OK) { +		TRACE_ERROR(interp); +		goto gotError; +	    } +	    varPtr = LOCAL(duiPtr->varIndices[i]); +	    while (TclIsVarLink(varPtr)) { +		varPtr = varPtr->value.linkPtr; +	    } +	    DECACHE_STACK_INFO(); +	    if (valuePtr == NULL) { +		TclObjUnsetVar2(interp, +			localName(iPtr->varFramePtr, duiPtr->varIndices[i]), +			NULL, 0); +	    } else if (TclPtrSetVar(interp, varPtr, NULL, NULL, NULL, +		    valuePtr, TCL_LEAVE_ERR_MSG, +		    duiPtr->varIndices[i]) == NULL) { +		CACHE_STACK_INFO(); +		TRACE_ERROR(interp); +		goto gotError; +	    } +	    CACHE_STACK_INFO(); +	} +	TRACE_APPEND(("OK\n")); +	NEXT_INST_F(9, 0, 0); + +    case INST_DICT_UPDATE_END: +	opnd = TclGetUInt4AtPtr(pc+1); +	opnd2 = TclGetUInt4AtPtr(pc+5); +	TRACE(("%u => ", opnd)); +	varPtr = LOCAL(opnd); +	duiPtr = codePtr->auxDataArrayPtr[opnd2].clientData; +	while (TclIsVarLink(varPtr)) { +	    varPtr = varPtr->value.linkPtr; +	} +	if (TclIsVarDirectReadable(varPtr)) { +	    dictPtr = varPtr->value.objPtr; +	} else { +	    DECACHE_STACK_INFO(); +	    dictPtr = TclPtrGetVar(interp, varPtr, NULL, NULL, NULL, 0, opnd); +	    CACHE_STACK_INFO(); +	} +	if (dictPtr == NULL) { +	    TRACE_APPEND(("storage was unset\n")); +	    NEXT_INST_F(9, 1, 0); +	} +	if (Tcl_DictObjSize(interp, dictPtr, &length) != TCL_OK +		|| TclListObjGetElements(interp, OBJ_AT_TOS, &length, +			&keyPtrPtr) != TCL_OK) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} +	allocdict = Tcl_IsShared(dictPtr); +	if (allocdict) { +	    dictPtr = Tcl_DuplicateObj(dictPtr); +	} +	if (length > 0) { +	    TclInvalidateStringRep(dictPtr); +	} +	for (i=0 ; i<length ; i++) { +	    Var *var2Ptr = LOCAL(duiPtr->varIndices[i]); + +	    while (TclIsVarLink(var2Ptr)) { +		var2Ptr = var2Ptr->value.linkPtr; +	    } +	    if (TclIsVarDirectReadable(var2Ptr)) { +		valuePtr = var2Ptr->value.objPtr; +	    } else { +		DECACHE_STACK_INFO(); +		valuePtr = TclPtrGetVar(interp, var2Ptr, NULL, NULL, NULL, 0, +			duiPtr->varIndices[i]); +		CACHE_STACK_INFO(); +	    } +	    if (valuePtr == NULL) { +		Tcl_DictObjRemove(interp, dictPtr, keyPtrPtr[i]); +	    } else if (dictPtr == valuePtr) { +		Tcl_DictObjPut(interp, dictPtr, keyPtrPtr[i], +			Tcl_DuplicateObj(valuePtr)); +	    } else { +		Tcl_DictObjPut(interp, dictPtr, keyPtrPtr[i], valuePtr); +	    } +	} +	if (TclIsVarDirectWritable(varPtr)) { +	    Tcl_IncrRefCount(dictPtr); +	    TclDecrRefCount(varPtr->value.objPtr); +	    varPtr->value.objPtr = dictPtr; +	} else { +	    DECACHE_STACK_INFO(); +	    objResultPtr = TclPtrSetVar(interp, varPtr, NULL, NULL, NULL, +		    dictPtr, TCL_LEAVE_ERR_MSG, opnd); +	    CACHE_STACK_INFO(); +	    if (objResultPtr == NULL) { +		if (allocdict) { +		    TclDecrRefCount(dictPtr);  		} +		TRACE_ERROR(interp); +		goto gotError; +	    } +	} +	TRACE_APPEND(("written back\n")); +	NEXT_INST_F(9, 1, 0); + +    case INST_DICT_EXPAND: +	dictPtr = OBJ_UNDER_TOS; +	listPtr = OBJ_AT_TOS; +	TRACE(("\"%.30s\" \"%.30s\" =>", O2S(dictPtr), O2S(listPtr))); +	if (TclListObjGetElements(interp, listPtr, &objc, &objv) != TCL_OK) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} +	objResultPtr = TclDictWithInit(interp, dictPtr, objc, objv); +	if (objResultPtr == NULL) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} +	TRACE_APPEND(("\"%.30s\"\n", O2S(objResultPtr))); +	NEXT_INST_F(1, 2, 1); + +    case INST_DICT_RECOMBINE_STK: +	keysPtr = POP_OBJECT(); +	varNamePtr = OBJ_UNDER_TOS; +	listPtr = OBJ_AT_TOS; +	TRACE(("\"%.30s\" \"%.30s\" \"%.30s\" => ", +		O2S(varNamePtr), O2S(valuePtr), O2S(keysPtr))); +	if (TclListObjGetElements(interp, listPtr, &objc, &objv) != TCL_OK) { +	    TRACE_ERROR(interp); +	    TclDecrRefCount(keysPtr); +	    goto gotError; +	} +	varPtr = TclObjLookupVarEx(interp, varNamePtr, NULL, +		TCL_LEAVE_ERR_MSG, "set", 1, 1, &arrayPtr); +	if (varPtr == NULL) { +	    TRACE_ERROR(interp); +	    TclDecrRefCount(keysPtr); +	    goto gotError; +	} +	DECACHE_STACK_INFO(); +	result = TclDictWithFinish(interp, varPtr,arrayPtr,varNamePtr,NULL,-1, +		objc, objv, keysPtr); +	CACHE_STACK_INFO(); +	TclDecrRefCount(keysPtr); +	if (result != TCL_OK) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} +	TRACE_APPEND(("OK\n")); +	NEXT_INST_F(1, 2, 0); + +    case INST_DICT_RECOMBINE_IMM: +	opnd = TclGetUInt4AtPtr(pc+1); +	listPtr = OBJ_UNDER_TOS; +	keysPtr = OBJ_AT_TOS; +	varPtr = LOCAL(opnd); +	TRACE(("%u <- \"%.30s\" \"%.30s\" => ", opnd, O2S(valuePtr), +		O2S(keysPtr))); +	if (TclListObjGetElements(interp, listPtr, &objc, &objv) != TCL_OK) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} +	while (TclIsVarLink(varPtr)) { +	    varPtr = varPtr->value.linkPtr; +	} +	DECACHE_STACK_INFO(); +	result = TclDictWithFinish(interp, varPtr, NULL, NULL, NULL, opnd, +		objc, objv, keysPtr); +	CACHE_STACK_INFO(); +	if (result != TCL_OK) { +	    TRACE_ERROR(interp); +	    goto gotError; +	} +	TRACE_APPEND(("OK\n")); +	NEXT_INST_F(5, 2, 0); +    } -		PUSH_OBJECT(objPtr); -		TRACE(("%.20s %.20s => %s\n", -			O2S(valuePtr), O2S(value2Ptr), O2S(objPtr))); +    /* +     *	   End of dictionary-related instructions. +     * ----------------------------------------------------------------- +     */ + +    default: +	Tcl_Panic("TclNRExecuteByteCode: unrecognized opCode %u", *pc); +    } /* end of switch on opCode */ + +    /* +     * Block for variables needed to process exception returns. +     */ + +    { +	ExceptionRange *rangePtr; +				/* Points to closest loop or catch exception +				 * range enclosing the pc. Used by various +				 * instructions and processCatch to process +				 * break, continue, and errors. */ +	const char *bytes; + +	/* +	 * An external evaluation (INST_INVOKE or INST_EVAL) returned +	 * something different from TCL_OK, or else INST_BREAK or +	 * INST_CONTINUE were called. +	 */ + +    processExceptionReturn: +#ifdef TCL_COMPILE_DEBUG +	switch (*pc) { +	case INST_INVOKE_STK1: +	    opnd = TclGetUInt1AtPtr(pc+1); +	    TRACE(("%u => ... after \"%.20s\": ", opnd, cmdNameBuf)); +	    break; +	case INST_INVOKE_STK4: +	    opnd = TclGetUInt4AtPtr(pc+1); +	    TRACE(("%u => ... after \"%.20s\": ", opnd, cmdNameBuf)); +	    break; +	case INST_EVAL_STK: +	    /* +	     * Note that the object at stacktop has to be used before doing +	     * the cleanup. +	     */ + +	    TRACE(("\"%.30s\" => ", O2S(OBJ_AT_TOS))); +	    break; +	default: +	    TRACE(("=> ")); +	} +#endif +	if ((result == TCL_CONTINUE) || (result == TCL_BREAK)) { +	    rangePtr = GetExceptRangeForPc(pc, /*catchOnly*/ 0, codePtr); +	    if (rangePtr == NULL) { +		TRACE_APPEND(("no encl. loop or catch, returning %s\n", +			StringForResultCode(result))); +		goto abnormalReturn; +	    } +	    if (rangePtr->type == CATCH_EXCEPTION_RANGE) { +		TRACE_APPEND(("%s ...\n", StringForResultCode(result))); +		goto processCatch; +	    } +	    while (cleanup--) { +		valuePtr = POP_OBJECT();  		TclDecrRefCount(valuePtr); -		TclDecrRefCount(value2Ptr);  	    } -	    ADJUST_PC(1); +	    if (result == TCL_BREAK) { +		result = TCL_OK; +		pc = (codePtr->codeStart + rangePtr->breakOffset); +		TRACE_APPEND(("%s, range at %d, new pc %d\n", +			StringForResultCode(result), +			rangePtr->codeOffset, rangePtr->breakOffset)); +		NEXT_INST_F(0, 0, 0); +	    } +	    if (rangePtr->continueOffset == -1) { +		TRACE_APPEND(("%s, loop w/o continue, checking for catch\n", +			StringForResultCode(result))); +		goto checkForCatch; +	    } +	    result = TCL_OK; +	    pc = (codePtr->codeStart + rangePtr->continueOffset); +	    TRACE_APPEND(("%s, range at %d, new pc %d\n", +		    StringForResultCode(result), +		    rangePtr->codeOffset, rangePtr->continueOffset)); +	    NEXT_INST_F(0, 0, 0); +	} +#ifdef TCL_COMPILE_DEBUG +	if (traceInstructions) { +	    objPtr = Tcl_GetObjResult(interp); +	    if ((result != TCL_ERROR) && (result != TCL_RETURN)) { +		TRACE_APPEND(("OTHER RETURN CODE %d, result=\"%.30s\"\n ", +			result, O2S(objPtr))); +	    } else { +		TRACE_APPEND(("%s, result=\"%.30s\"\n", +			StringForResultCode(result), O2S(objPtr))); +	    } +	} +#endif +	goto checkForCatch; -	case INST_STR_MATCH: -	    { -		int nocase, match; +	/* +	 * Division by zero in an expression. Control only reaches this point +	 * by "goto divideByZero". +	 */ + +    divideByZero: +	Tcl_SetObjResult(interp, Tcl_NewStringObj("divide by zero", -1)); +	DECACHE_STACK_INFO(); +	Tcl_SetErrorCode(interp, "ARITH", "DIVZERO", "divide by zero", NULL); +	CACHE_STACK_INFO(); +	goto gotError; + +	/* +	 * Exponentiation of zero by negative number in an expression. Control +	 * only reaches this point by "goto exponOfZero". +	 */ + +    exponOfZero: +	Tcl_SetObjResult(interp, Tcl_NewStringObj( +		"exponentiation of zero by negative power", -1)); +	DECACHE_STACK_INFO(); +	Tcl_SetErrorCode(interp, "ARITH", "DOMAIN", +		"exponentiation of zero by negative power", NULL); +	CACHE_STACK_INFO(); + +	/* +	 * Almost all error paths feed through here rather than assigning to +	 * result themselves (for a small but consistent saving). +	 */ + +    gotError: +	result = TCL_ERROR; + +	/* +	 * Execution has generated an "exception" such as TCL_ERROR. If the +	 * exception is an error, record information about what was being +	 * executed when the error occurred. Find the closest enclosing catch +	 * range, if any. If no enclosing catch range is found, stop execution +	 * and return the "exception" code. +	 */ + +    checkForCatch: +	if (iPtr->execEnvPtr->rewind) { +	    goto abnormalReturn; +	} +	if ((result == TCL_ERROR) && !(iPtr->flags & ERR_ALREADY_LOGGED)) { +	    const unsigned char *pcBeg; + +	    bytes = GetSrcInfoForPc(pc, codePtr, &length, &pcBeg, NULL); +	    DECACHE_STACK_INFO(); +	    TclLogCommandInfo(interp, codePtr->source, bytes, +		    bytes ? length : 0, pcBeg, tosPtr); +	    CACHE_STACK_INFO(); +	} +	iPtr->flags &= ~ERR_ALREADY_LOGGED; + +	/* +	 * Clear all expansions that may have started after the last +	 * INST_BEGIN_CATCH. +	 */ + +	while (auxObjList) { +	    if ((catchTop != initCatchTop) +		    && (*catchTop > (ptrdiff_t) +			auxObjList->internalRep.ptrAndLongRep.value)) { +		break; +	    } +	    POP_TAUX_OBJ(); +	} + +	/* +	 * We must not catch if the script in progress has been canceled with +	 * the TCL_CANCEL_UNWIND flag. Instead, it blows outwards until we +	 * either hit another interpreter (presumably where the script in +	 * progress has not been canceled) or we get to the top-level. We do +	 * NOT modify the interpreter result here because we know it will +	 * already be set prior to vectoring down to this point in the code. +	 */ + +	if (TclCanceled(iPtr) && (Tcl_Canceled(interp, 0) == TCL_ERROR)) { +#ifdef TCL_COMPILE_DEBUG +	    if (traceInstructions) { +		fprintf(stdout, "   ... cancel with unwind, returning %s\n", +			StringForResultCode(result)); +	    } +#endif +	    goto abnormalReturn; +	} + +	/* +	 * We must not catch an exceeded limit. Instead, it blows outwards +	 * until we either hit another interpreter (presumably where the limit +	 * is not exceeded) or we get to the top-level. +	 */ + +	if (TclLimitExceeded(iPtr->limit)) { +#ifdef TCL_COMPILE_DEBUG +	    if (traceInstructions) { +		fprintf(stdout, "   ... limit exceeded, returning %s\n", +			StringForResultCode(result)); +	    } +#endif +	    goto abnormalReturn; +	} +	if (catchTop == initCatchTop) { +#ifdef TCL_COMPILE_DEBUG +	    if (traceInstructions) { +		fprintf(stdout, "   ... no enclosing catch, returning %s\n", +			StringForResultCode(result)); +	    } +#endif +	    goto abnormalReturn; +	} +	rangePtr = GetExceptRangeForPc(pc, /*catchOnly*/ 1, codePtr); +	if (rangePtr == NULL) { +	    /* +	     * This is only possible when compiling a [catch] that sends its +	     * script to INST_EVAL. Cannot correct the compiler without +	     * breaking compat with previous .tbc compiled scripts. +	     */ + +#ifdef TCL_COMPILE_DEBUG +	    if (traceInstructions) { +		fprintf(stdout, "   ... no enclosing catch, returning %s\n", +			StringForResultCode(result)); +	    } +#endif +	    goto abnormalReturn; +	} + +	/* +	 * A catch exception range (rangePtr) was found to handle an +	 * "exception". It was found either by checkForCatch just above or by +	 * an instruction during break, continue, or error processing. Jump to +	 * its catchOffset after unwinding the operand stack to the depth it +	 * had when starting to execute the range's catch command. +	 */ + +    processCatch: +	while (CURR_DEPTH > *catchTop) { +	    valuePtr = POP_OBJECT(); +	    TclDecrRefCount(valuePtr); +	} +#ifdef TCL_COMPILE_DEBUG +	if (traceInstructions) { +	    fprintf(stdout, "  ... found catch at %d, catchTop=%d, " +		    "unwound to %ld, new pc %u\n", +		    rangePtr->codeOffset, (int) (catchTop - initCatchTop - 1), +		    (long) *catchTop, (unsigned) rangePtr->catchOffset); +	} +#endif +	pc = (codePtr->codeStart + rangePtr->catchOffset); +	NEXT_INST_F(0, 0, 0);	/* Restart the execution loop at pc. */ + +	/* +	 * end of infinite loop dispatching on instructions. +	 */ + +	/* +	 * Abnormal return code. Restore the stack to state it had when +	 * starting to execute the ByteCode. Panic if the stack is below the +	 * initial level. +	 */ + +    abnormalReturn: +	TCL_DTRACE_INST_LAST(); + +	/* +	 * Clear all expansions and same-level NR calls. +	 * +	 * Note that expansion markers have a NULL type; avoid removing other +	 * markers. +	 */ + +	while (auxObjList) { +	    POP_TAUX_OBJ(); +	} +	while (tosPtr > initTosPtr) { +	    objPtr = POP_OBJECT(); +	    Tcl_DecrRefCount(objPtr); +	} + +	if (tosPtr < initTosPtr) { +	    fprintf(stderr, +		    "\nTclNRExecuteByteCode: abnormal return at pc %u: " +		    "stack top %d < entry stack top %d\n", +		    (unsigned)(pc - codePtr->codeStart), +		    (unsigned) CURR_DEPTH, (unsigned) 0); +	    Tcl_Panic("TclNRExecuteByteCode execution failure: end stack top < start stack top"); +	} +	CLANG_ASSERT(bcFramePtr); +    } + +    iPtr->cmdFramePtr = bcFramePtr->nextPtr; +    if (--codePtr->refCount <= 0) { +	TclCleanupByteCode(codePtr); +    } +    TclStackFree(interp, TD);	/* free my stack */ + +    return result; + +    /* +     * INST_START_CMD failure case removed where it doesn't bother that much +     * +     * Remark that if the interpreter is marked for deletion its +     * compileEpoch is modified, so that the epoch check also verifies +     * that the interp is not deleted. If no outside call has been made +     * since the last check, it is safe to omit the check. + +     * case INST_START_CMD: +     */ + +	instStartCmdFailed: +	{ +	    const char *bytes; + +	    checkInterp = 1; +	    length = 0; + +	    /* +	     * We used to switch to direct eval; for NRE-awareness we now +	     * compile and eval the command so that this evaluation does not +	     * add a new TEBC instance. [Bug 2910748] +	     */ + +	    if (TclInterpReady(interp) == TCL_ERROR) { +		goto gotError; +	    } + +	    codePtr->flags |= TCL_BYTECODE_RECOMPILE; +	    bytes = GetSrcInfoForPc(pc, codePtr, &length, NULL, NULL); +	    opnd = TclGetUInt4AtPtr(pc+1); +	    pc += (opnd-1); +	    PUSH_OBJECT(Tcl_NewStringObj(bytes, length)); +	    goto instEvalStk; +	} +} + +#undef codePtr +#undef iPtr +#undef bcFramePtr +#undef initCatchTop +#undef initTosPtr +#undef auxObjList +#undef catchTop +#undef TCONST + +static int +FinalizeOONext( +    ClientData data[], +    Tcl_Interp *interp, +    int result) +{ +    Interp *iPtr = (Interp *) interp; +    CallContext *contextPtr = data[1]; + +    /* +     * Reset the variable lookup frame. +     */ + +    iPtr->varFramePtr = data[0]; + +    /* +     * Restore the call chain context index as we've finished the inner invoke +     * and want to operate in the outer context again. +     */ + +    contextPtr->index = PTR2INT(data[2]); +    contextPtr->skip = PTR2INT(data[3]); +    contextPtr->oPtr->flags &= ~FILTER_HANDLING; +    return result; +} + +static int +FinalizeOONextFilter( +    ClientData data[], +    Tcl_Interp *interp, +    int result) +{ +    Interp *iPtr = (Interp *) interp; +    CallContext *contextPtr = data[1]; -		valuePtr  = POP_OBJECT();	/* String */ -		value2Ptr = POP_OBJECT();	/* Pattern */ -		objPtr    = POP_OBJECT();	/* Case Sensitivity */ +    /* +     * Reset the variable lookup frame. +     */ -		Tcl_GetBooleanFromObj(interp, objPtr, &nocase); -		match = Tcl_UniCharCaseMatch(Tcl_GetUnicode(valuePtr), -			Tcl_GetUnicode(value2Ptr), nocase); +    iPtr->varFramePtr = data[0]; + +    /* +     * Restore the call chain context index as we've finished the inner invoke +     * and want to operate in the outer context again. +     */ +    contextPtr->index = PTR2INT(data[2]); +    contextPtr->skip = PTR2INT(data[3]); +    contextPtr->oPtr->flags |= FILTER_HANDLING; +    return result; +} + +/* + *---------------------------------------------------------------------- + * + * ExecuteExtendedBinaryMathOp, ExecuteExtendedUnaryMathOp -- + * + *	These functions do advanced math for binary and unary operators + *	respectively, so that the main TEBC code does not bear the cost of + *	them. + * + * Results: + *	A Tcl_Obj* result, or a NULL (in which case valuePtr is updated to + *	hold the result value), or one of the special flag values + *	GENERAL_ARITHMETIC_ERROR, EXPONENT_OF_ZERO or DIVIDED_BY_ZERO. The + *	latter two signify a zero value raised to a negative power or a value + *	divided by zero, respectively. With GENERAL_ARITHMETIC_ERROR, all + *	error information will have already been reported in the interpreter + *	result. + * + * Side effects: + *	May update the Tcl_Obj indicated valuePtr if it is unshared. Will + *	return a NULL when that happens. + * + *---------------------------------------------------------------------- + */ + +static Tcl_Obj * +ExecuteExtendedBinaryMathOp( +    Tcl_Interp *interp,		/* Where to report errors. */ +    int opcode,			/* What operation to perform. */ +    Tcl_Obj **constants,	/* The execution environment's constants. */ +    Tcl_Obj *valuePtr,		/* The first operand on the stack. */ +    Tcl_Obj *value2Ptr)		/* The second operand on the stack. */ +{ +#define LONG_RESULT(l) \ +    if (Tcl_IsShared(valuePtr)) {		\ +	TclNewLongObj(objResultPtr, l);		\ +	return objResultPtr;			\ +    } else {					\ +	Tcl_SetLongObj(valuePtr, l);		\ +	return NULL;				\ +    } +#define WIDE_RESULT(w) \ +    if (Tcl_IsShared(valuePtr)) {		\ +	return Tcl_NewWideIntObj(w);		\ +    } else {					\ +	Tcl_SetWideIntObj(valuePtr, w);		\ +	return NULL;				\ +    } +#define BIG_RESULT(b) \ +    if (Tcl_IsShared(valuePtr)) {		\ +	return Tcl_NewBignumObj(b);		\ +    } else {					\ +	Tcl_SetBignumObj(valuePtr, b);		\ +	return NULL;				\ +    } +#define DOUBLE_RESULT(d) \ +    if (Tcl_IsShared(valuePtr)) {		\ +	TclNewDoubleObj(objResultPtr, (d));	\ +	return objResultPtr;			\ +    } else {					\ +	Tcl_SetDoubleObj(valuePtr, (d));	\ +	return NULL;				\ +    } + +    int type1, type2; +    ClientData ptr1, ptr2; +    double d1, d2, dResult; +    long l1, l2, lResult; +    Tcl_WideInt w1, w2, wResult; +    mp_int big1, big2, bigResult, bigRemainder; +    Tcl_Obj *objResultPtr; +    int invalid, numPos, zero; +    long shift; + +    (void) GetNumberFromObj(NULL, valuePtr, &ptr1, &type1); +    (void) GetNumberFromObj(NULL, value2Ptr, &ptr2, &type2); + +    switch (opcode) { +    case INST_MOD: +	/* TODO: Attempts to re-use unshared operands on stack */ + +	l2 = 0;			/* silence gcc warning */ +	if (type2 == TCL_NUMBER_LONG) { +	    l2 = *((const long *)ptr2); +	    if (l2 == 0) { +		return DIVIDED_BY_ZERO; +	    } +	    if ((l2 == 1) || (l2 == -1)) {  		/* -		 * Reuse the casePtr object already on stack if possible. +		 * Div. by |1| always yields remainder of 0.  		 */ -		TRACE(("%.20s %.20s => %d\n", -			O2S(valuePtr), O2S(value2Ptr), match)); -		if (Tcl_IsShared(objPtr)) { -		    PUSH_OBJECT(Tcl_NewIntObj(match)); -		    TclDecrRefCount(objPtr); -		} else {	/* reuse the valuePtr object */ -		    Tcl_SetIntObj(objPtr, match); -		    ++stackTop; /* valuePtr now on stk top has right r.c. */ -		} -		TclDecrRefCount(valuePtr); -		TclDecrRefCount(value2Ptr); +		return constants[0];  	    } -	    ADJUST_PC(1); +	} +#ifndef TCL_WIDE_INT_IS_LONG +	if (type1 == TCL_NUMBER_WIDE) { +	    w1 = *((const Tcl_WideInt *)ptr1); +	    if (type2 != TCL_NUMBER_BIG) { +		Tcl_WideInt wQuotient, wRemainder; +		Tcl_GetWideIntFromObj(NULL, value2Ptr, &w2); +		wQuotient = w1 / w2; -	case INST_EQ: -	case INST_NEQ: -	case INST_LT: -	case INST_GT: -	case INST_LE: -	case INST_GE: -	    {  		/* -		 * Any type is allowed but the two operands must have the -	         * same type. We will compute value op value2. +		 * Force Tcl's integer division rules. +		 * TODO: examine for logic simplification  		 */ -		Tcl_ObjType *t1Ptr, *t2Ptr; -		char *s1 = NULL;   /* Init. avoids compiler warning. */ -		char *s2 = NULL;   /* Init. avoids compiler warning. */ -		long i2 = 0;	   /* Init. avoids compiler warning. */ -		double d1 = 0.0;   /* Init. avoids compiler warning. */ -		double d2 = 0.0;   /* Init. avoids compiler warning. */ -		long iResult = 0;  /* Init. avoids compiler warning. */ +		if (((wQuotient < (Tcl_WideInt) 0) +			|| ((wQuotient == (Tcl_WideInt) 0) +			&& ((w1 < (Tcl_WideInt)0 && w2 > (Tcl_WideInt)0) +			|| (w1 > (Tcl_WideInt)0 && w2 < (Tcl_WideInt)0)))) +			&& (wQuotient * w2 != w1)) { +		    wQuotient -= (Tcl_WideInt) 1; +		} +		wRemainder = w1 - w2*wQuotient; +		WIDE_RESULT(wRemainder); +	    } -		value2Ptr = POP_OBJECT(); -		valuePtr  = POP_OBJECT(); -		t1Ptr = valuePtr->typePtr; -		t2Ptr = value2Ptr->typePtr; +	    Tcl_TakeBignumFromObj(NULL, value2Ptr, &big2); +	    /* TODO: internals intrusion */ +	    if ((w1 > ((Tcl_WideInt) 0)) ^ (big2.sign == MP_ZPOS)) {  		/* -		 * We only want to coerce numeric validation if -		 * neither type is NULL.  A NULL type means the arg is -		 * essentially an empty object ("", {} or [list]). +		 * Arguments are opposite sign; remainder is sum.  		 */ -		if (!((((t1Ptr == NULL) && (valuePtr->bytes == NULL)) -			|| (valuePtr->bytes && (valuePtr->length == 0))) -			|| (((t2Ptr == NULL) && (value2Ptr->bytes == NULL)) -				|| (value2Ptr->bytes && (value2Ptr->length == 0))))) { -		    if ((t1Ptr != &tclIntType) && (t1Ptr != &tclDoubleType)) { -			s1 = Tcl_GetStringFromObj(valuePtr, &length); -			if (TclLooksLikeInt(s1, length)) { -			    (void) Tcl_GetLongFromObj((Tcl_Interp *) NULL, -				    valuePtr, &i); -			} else { -			    (void) Tcl_GetDoubleFromObj((Tcl_Interp *) NULL, -				    valuePtr, &d1); -			} -			t1Ptr = valuePtr->typePtr; -		    } -		    if ((t2Ptr != &tclIntType) && (t2Ptr != &tclDoubleType)) { -			s2 = Tcl_GetStringFromObj(value2Ptr, &length); -			if (TclLooksLikeInt(s2, length)) { -			    (void) Tcl_GetLongFromObj((Tcl_Interp *) NULL, -				    value2Ptr, &i2); -			} else { -			    (void) Tcl_GetDoubleFromObj((Tcl_Interp *) NULL, -				    value2Ptr, &d2); -			} -			t2Ptr = value2Ptr->typePtr; -		    } -		} -		if (((t1Ptr != &tclIntType) && (t1Ptr != &tclDoubleType)) -		        || ((t2Ptr != &tclIntType) && (t2Ptr != &tclDoubleType))) { -		    /* -		     * One operand is not numeric. Compare as strings. -		     * NOTE: strcmp is not correct for \x00 < \x01. -		     */ -		    int cmpValue; -		    s1 = Tcl_GetString(valuePtr); -		    s2 = Tcl_GetString(value2Ptr); -		    cmpValue = strcmp(s1, s2); -		    switch (*pc) { -		    case INST_EQ: -			iResult = (cmpValue == 0); -			break; -		    case INST_NEQ: -			iResult = (cmpValue != 0); -			break; -		    case INST_LT: -			iResult = (cmpValue < 0); -			break; -		    case INST_GT: -			iResult = (cmpValue > 0); -			break; -		    case INST_LE: -			iResult = (cmpValue <= 0); -			break; -		    case INST_GE: -			iResult = (cmpValue >= 0); -			break; -		    } -		} else if ((t1Ptr == &tclDoubleType) -		        || (t2Ptr == &tclDoubleType)) { -		    /* -		     * Compare as doubles. -		     */ -		    if (t1Ptr == &tclDoubleType) { -			d1 = valuePtr->internalRep.doubleValue; -			if (t2Ptr == &tclIntType) { -			    d2 = value2Ptr->internalRep.longValue; -			} else { -			    d2 = value2Ptr->internalRep.doubleValue; -			} -		    } else {	/* t1Ptr is int, t2Ptr is double */ -			d1 = valuePtr->internalRep.longValue; -			d2 = value2Ptr->internalRep.doubleValue; -		    } -		    switch (*pc) { -		    case INST_EQ: -			iResult = d1 == d2; -			break; -		    case INST_NEQ: -			iResult = d1 != d2; -			break; -		    case INST_LT: -			iResult = d1 < d2; -			break; -		    case INST_GT: -			iResult = d1 > d2; -			break; -		    case INST_LE: -			iResult = d1 <= d2; -			break; -		    case INST_GE: -			iResult = d1 >= d2; -			break; -		    } -		} else { -		    /* -		     * Compare as ints. -		     */ -		    i  = valuePtr->internalRep.longValue; -		    i2 = value2Ptr->internalRep.longValue; -		    switch (*pc) { -		    case INST_EQ: -			iResult = i == i2; -			break; -		    case INST_NEQ: -			iResult = i != i2; -			break; -		    case INST_LT: -			iResult = i < i2; -			break; -		    case INST_GT: -			iResult = i > i2; -			break; -		    case INST_LE: -			iResult = i <= i2; -			break; -		    case INST_GE: -			iResult = i >= i2; -			break; -		    } -		} +		TclBNInitBignumFromWideInt(&big1, w1); +		mp_add(&big2, &big1, &big2); +		mp_clear(&big1); +		BIG_RESULT(&big2); +	    } + +	    /* +	     * Arguments are same sign; remainder is first operand. +	     */ + +	    mp_clear(&big2); +	    return NULL; +	} +#endif +	Tcl_GetBignumFromObj(NULL, valuePtr, &big1); +	Tcl_GetBignumFromObj(NULL, value2Ptr, &big2); +	mp_init(&bigResult); +	mp_init(&bigRemainder); +	mp_div(&big1, &big2, &bigResult, &bigRemainder); +	if (!mp_iszero(&bigRemainder) && (bigRemainder.sign != big2.sign)) { +	    /* +	     * Convert to Tcl's integer division rules. +	     */ + +	    mp_sub_d(&bigResult, 1, &bigResult); +	    mp_add(&bigRemainder, &big2, &bigRemainder); +	} +	mp_copy(&bigRemainder, &bigResult); +	mp_clear(&bigRemainder); +	mp_clear(&big1); +	mp_clear(&big2); +	BIG_RESULT(&bigResult); + +    case INST_LSHIFT: +    case INST_RSHIFT: { +	/* +	 * Reject negative shift argument. +	 */ + +	switch (type2) { +	case TCL_NUMBER_LONG: +	    invalid = (*((const long *)ptr2) < 0L); +	    break; +#ifndef TCL_WIDE_INT_IS_LONG +	case TCL_NUMBER_WIDE: +	    invalid = (*((const Tcl_WideInt *)ptr2) < (Tcl_WideInt)0); +	    break; +#endif +	case TCL_NUMBER_BIG: +	    Tcl_TakeBignumFromObj(NULL, value2Ptr, &big2); +	    invalid = (mp_cmp_d(&big2, 0) == MP_LT); +	    mp_clear(&big2); +	    break; +	default: +	    /* Unused, here to silence compiler warning */ +	    invalid = 0; +	} +	if (invalid) { +	    Tcl_SetObjResult(interp, Tcl_NewStringObj( +		    "negative shift argument", -1)); +	    return GENERAL_ARITHMETIC_ERROR; +	} + +	/* +	 * Zero shifted any number of bits is still zero. +	 */ + +	if ((type1==TCL_NUMBER_LONG) && (*((const long *)ptr1) == (long)0)) { +	    return constants[0]; +	} + +	if (opcode == INST_LSHIFT) { +	    /* +	     * Large left shifts create integer overflow. +	     * +	     * BEWARE! Can't use Tcl_GetIntFromObj() here because that +	     * converts values in the (unsigned) range to their signed int +	     * counterparts, leading to incorrect results. +	     */ + +	    if ((type2 != TCL_NUMBER_LONG) +		    || (*((const long *)ptr2) > (long) INT_MAX)) {  		/* -		 * Reuse the valuePtr object already on stack if possible. +		 * Technically, we could hold the value (1 << (INT_MAX+1)) in +		 * an mp_int, but since we're using mp_mul_2d() to do the +		 * work, and it takes only an int argument, that's a good +		 * place to draw the line.  		 */ -		 -		if (Tcl_IsShared(valuePtr)) { -		    PUSH_OBJECT(Tcl_NewLongObj(iResult)); -		    TRACE(("%.20s %.20s => %ld\n", -			   O2S(valuePtr), O2S(value2Ptr), iResult)); -		    TclDecrRefCount(valuePtr); -		} else {	/* reuse the valuePtr object */ -		    TRACE(("%.20s %.20s => %ld\n", -			    O2S(valuePtr), O2S(value2Ptr), iResult)); -		    Tcl_SetLongObj(valuePtr, iResult); -		    ++stackTop; /* valuePtr now on stk top has right r.c. */ + +		Tcl_SetObjResult(interp, Tcl_NewStringObj( +			"integer value too large to represent", -1)); +		return GENERAL_ARITHMETIC_ERROR; +	    } +	    shift = (int)(*((const long *)ptr2)); + +	    /* +	     * Handle shifts within the native wide range. +	     */ + +	    if ((type1 != TCL_NUMBER_BIG) +		    && ((size_t)shift < CHAR_BIT*sizeof(Tcl_WideInt))) { +		TclGetWideIntFromObj(NULL, valuePtr, &w1); +		if (!((w1>0 ? w1 : ~w1) +			& -(((Tcl_WideInt)1) +			<< (CHAR_BIT*sizeof(Tcl_WideInt) - 1 - shift)))) { +		    WIDE_RESULT(w1 << shift);  		} -		TclDecrRefCount(value2Ptr);  	    } -	    ADJUST_PC(1); -	     -	case INST_MOD: -	case INST_LSHIFT: -	case INST_RSHIFT: -	case INST_BITOR: -	case INST_BITXOR: -	case INST_BITAND: -	    { +	} else { +	    /* +	     * Quickly force large right shifts to 0 or -1. +	     */ + +	    if ((type2 != TCL_NUMBER_LONG) +		    || (*(const long *)ptr2 > INT_MAX)) {  		/* -		 * Only integers are allowed. We compute value op value2. +		 * Again, technically, the value to be shifted could be an +		 * mp_int so huge that a right shift by (INT_MAX+1) bits could +		 * not take us to the result of 0 or -1, but since we're using +		 * mp_div_2d to do the work, and it takes only an int +		 * argument, we draw the line there.  		 */ -		long i2, rem, negative; -		long iResult = 0; /* Init. avoids compiler warning. */ -		 -		value2Ptr = POP_OBJECT(); -		valuePtr  = POP_OBJECT();  -		if (valuePtr->typePtr == &tclIntType) { -		    i = valuePtr->internalRep.longValue; -		} else {	/* try to convert to int */ -		    result = Tcl_GetLongFromObj((Tcl_Interp *) NULL, -			    valuePtr, &i); -		    if (result != TCL_OK) { -			TRACE(("%.20s %.20s => ILLEGAL 1st TYPE %s\n", -			      O2S(valuePtr), O2S(value2Ptr), -			      (valuePtr->typePtr? -				   valuePtr->typePtr->name : "null"))); -			IllegalExprOperandType(interp, pc, valuePtr); -			Tcl_DecrRefCount(valuePtr); -			Tcl_DecrRefCount(value2Ptr); -			goto checkForCatch; -		    } +		switch (type1) { +		case TCL_NUMBER_LONG: +		    zero = (*(const long *)ptr1 > 0L); +		    break; +#ifndef TCL_WIDE_INT_IS_LONG +		case TCL_NUMBER_WIDE: +		    zero = (*(const Tcl_WideInt *)ptr1 > (Tcl_WideInt)0); +		    break; +#endif +		case TCL_NUMBER_BIG: +		    Tcl_TakeBignumFromObj(NULL, valuePtr, &big1); +		    zero = (mp_cmp_d(&big1, 0) == MP_GT); +		    mp_clear(&big1); +		    break; +		default: +		    /* Unused, here to silence compiler warning. */ +		    zero = 0;  		} -		if (value2Ptr->typePtr == &tclIntType) { -		    i2 = value2Ptr->internalRep.longValue; -		} else { -		    result = Tcl_GetLongFromObj((Tcl_Interp *) NULL, -			    value2Ptr, &i2); -		    if (result != TCL_OK) { -			TRACE(("%.20s %.20s => ILLEGAL 2nd TYPE %s\n", -			      O2S(valuePtr), O2S(value2Ptr), -			      (value2Ptr->typePtr? -				   value2Ptr->typePtr->name : "null"))); -			IllegalExprOperandType(interp, pc, value2Ptr); -			Tcl_DecrRefCount(valuePtr); -			Tcl_DecrRefCount(value2Ptr); -			goto checkForCatch; +		if (zero) { +		    return constants[0]; +		} +		LONG_RESULT(-1); +	    } +	    shift = (int)(*(const long *)ptr2); + +#ifndef TCL_WIDE_INT_IS_LONG +	    /* +	     * Handle shifts within the native wide range. +	     */ + +	    if (type1 == TCL_NUMBER_WIDE) { +		w1 = *(const Tcl_WideInt *)ptr1; +		if ((size_t)shift >= CHAR_BIT*sizeof(Tcl_WideInt)) { +		    if (w1 >= (Tcl_WideInt)0) { +			return constants[0];  		    } +		    LONG_RESULT(-1);  		} +		WIDE_RESULT(w1 >> shift); +	    } +#endif +	} + +	Tcl_TakeBignumFromObj(NULL, valuePtr, &big1); + +	mp_init(&bigResult); +	if (opcode == INST_LSHIFT) { +	    mp_mul_2d(&big1, shift, &bigResult); +	} else { +	    mp_init(&bigRemainder); +	    mp_div_2d(&big1, shift, &bigResult, &bigRemainder); +	    if (mp_cmp_d(&bigRemainder, 0) == MP_LT) { +		/* +		 * Convert to Tcl's integer division rules. +		 */ -		switch (*pc) { -		case INST_MOD: +		mp_sub_d(&bigResult, 1, &bigResult); +	    } +	    mp_clear(&bigRemainder); +	} +	mp_clear(&big1); +	BIG_RESULT(&bigResult); +    } + +    case INST_BITOR: +    case INST_BITXOR: +    case INST_BITAND: +	if ((type1 == TCL_NUMBER_BIG) || (type2 == TCL_NUMBER_BIG)) { +	    mp_int *First, *Second; + +	    Tcl_TakeBignumFromObj(NULL, valuePtr, &big1); +	    Tcl_TakeBignumFromObj(NULL, value2Ptr, &big2); + +	    /* +	     * Count how many positive arguments we have. If only one of the +	     * arguments is negative, store it in 'Second'. +	     */ + +	    if (mp_cmp_d(&big1, 0) != MP_LT) { +		numPos = 1 + (mp_cmp_d(&big2, 0) != MP_LT); +		First = &big1; +		Second = &big2; +	    } else { +		First = &big2; +		Second = &big1; +		numPos = (mp_cmp_d(First, 0) != MP_LT); +	    } +	    mp_init(&bigResult); + +	    switch (opcode) { +	    case INST_BITAND: +		switch (numPos) { +		case 2:  		    /* -		     * This code is tricky: C doesn't guarantee much about -		     * the quotient or remainder, but Tcl does. The -		     * remainder always has the same sign as the divisor and -		     * a smaller absolute value. +		     * Both arguments positive, base case.  		     */ -		    if (i2 == 0) { -			TRACE(("%ld %ld => DIVIDE BY ZERO\n", i, i2)); -			Tcl_DecrRefCount(valuePtr); -			Tcl_DecrRefCount(value2Ptr); -			goto divideByZero; -		    } -		    negative = 0; -		    if (i2 < 0) { -			i2 = -i2; -			i = -i; -			negative = 1; -		    } -		    rem  = i % i2; -		    if (rem < 0) { -			rem += i2; -		    } -		    if (negative) { -			rem = -rem; -		    } -		    iResult = rem; -		    break; -		case INST_LSHIFT: -		    iResult = i << i2; + +		    mp_and(First, Second, &bigResult);  		    break; -		case INST_RSHIFT: +		case 1:  		    /* -		     * The following code is a bit tricky: it ensures that -		     * right shifts propagate the sign bit even on machines -		     * where ">>" won't do it by default. +		     * First is positive; second negative: +		     * P & N = P & ~~N = P&~(-N-1) = P & (P ^ (-N-1))  		     */ -		    if (i < 0) { -			iResult = ~((~i) >> i2); -		    } else { -			iResult = i >> i2; -		    } -		    break; -		case INST_BITOR: -		    iResult = i | i2; -		    break; -		case INST_BITXOR: -		    iResult = i ^ i2; + +		    mp_neg(Second, Second); +		    mp_sub_d(Second, 1, Second); +		    mp_xor(First, Second, &bigResult); +		    mp_and(First, &bigResult, &bigResult);  		    break; -		case INST_BITAND: -		    iResult = i & i2; +		case 0: +		    /* +		     * Both arguments negative: +		     * a & b = ~ (~a | ~b) = -(-a-1|-b-1)-1 +		     */ + +		    mp_neg(First, First); +		    mp_sub_d(First, 1, First); +		    mp_neg(Second, Second); +		    mp_sub_d(Second, 1, Second); +		    mp_or(First, Second, &bigResult); +		    mp_neg(&bigResult, &bigResult); +		    mp_sub_d(&bigResult, 1, &bigResult);  		    break;  		} +		break; -		/* -		 * Reuse the valuePtr object already on stack if possible. -		 */ -		 -		if (Tcl_IsShared(valuePtr)) { -		    PUSH_OBJECT(Tcl_NewLongObj(iResult)); -		    TRACE(("%ld %ld => %ld\n", i, i2, iResult)); -		    TclDecrRefCount(valuePtr); -		} else {	/* reuse the valuePtr object */ -		    TRACE(("%ld %ld => %ld\n", i, i2, iResult)); -		    Tcl_SetLongObj(valuePtr, iResult); -		    ++stackTop; /* valuePtr now on stk top has right r.c. */ -		} -		TclDecrRefCount(value2Ptr); -	    } -	    ADJUST_PC(1); -	     -	case INST_ADD: -	case INST_SUB: -	case INST_MULT: -	case INST_DIV: -	    { -		/* -		 * Operands must be numeric and ints get converted to floats -		 * if necessary. We compute value op value2. -		 */ +	    case INST_BITOR: +		switch (numPos) { +		case 2: +		    /* +		     * Both arguments positive, base case. +		     */ -		Tcl_ObjType *t1Ptr, *t2Ptr; -		long i2, quot, rem; -		double d1, d2; -		long iResult = 0;     /* Init. avoids compiler warning. */ -		double dResult = 0.0; /* Init. avoids compiler warning. */ -		int doDouble = 0;     /* 1 if doing floating arithmetic */ -		 -		value2Ptr = POP_OBJECT(); -		valuePtr  = POP_OBJECT(); -		t1Ptr = valuePtr->typePtr; -		t2Ptr = value2Ptr->typePtr; -		 -		if (t1Ptr == &tclIntType) { -		    i  = valuePtr->internalRep.longValue; -		} else if ((t1Ptr == &tclDoubleType) -			&& (valuePtr->bytes == NULL)) { +		    mp_or(First, Second, &bigResult); +		    break; +		case 1:  		    /* -		     * We can only use the internal rep directly if there is -		     * no string rep.  Otherwise the string rep might actually -		     * look like an integer, which is preferred. +		     * First is positive; second negative: +		     * N|P = ~(~N&~P) = ~((-N-1)&~P) = -((-N-1)&((-N-1)^P))-1  		     */ -		    d1 = valuePtr->internalRep.doubleValue; -		} else { -		    char *s = Tcl_GetStringFromObj(valuePtr, &length); -		    if (TclLooksLikeInt(s, length)) { -			result = Tcl_GetLongFromObj((Tcl_Interp *) NULL, -				valuePtr, &i); -		    } else { -			result = Tcl_GetDoubleFromObj((Tcl_Interp *) NULL, -				valuePtr, &d1); -		    } -		    if (result != TCL_OK) { -			TRACE(("%.20s %.20s => ILLEGAL 1st TYPE %s\n", -			       s, O2S(valuePtr), -			       (valuePtr->typePtr? -				    valuePtr->typePtr->name : "null"))); -			IllegalExprOperandType(interp, pc, valuePtr); -			Tcl_DecrRefCount(valuePtr); -			Tcl_DecrRefCount(value2Ptr); -			goto checkForCatch; -		    } -		    t1Ptr = valuePtr->typePtr; -		} -		 -		if (t2Ptr == &tclIntType) { -		    i2 = value2Ptr->internalRep.longValue; -		} else if ((t2Ptr == &tclDoubleType) -			&& (value2Ptr->bytes == NULL)) { +		    mp_neg(Second, Second); +		    mp_sub_d(Second, 1, Second); +		    mp_xor(First, Second, &bigResult); +		    mp_and(Second, &bigResult, &bigResult); +		    mp_neg(&bigResult, &bigResult); +		    mp_sub_d(&bigResult, 1, &bigResult); +		    break; +		case 0:  		    /* -		     * We can only use the internal rep directly if there is -		     * no string rep.  Otherwise the string rep might actually -		     * look like an integer, which is preferred. +		     * Both arguments negative: +		     * a | b = ~ (~a & ~b) = -(-a-1&-b-1)-1  		     */ -		    d2 = value2Ptr->internalRep.doubleValue; -		} else { -		    char *s = Tcl_GetStringFromObj(value2Ptr, &length); -		    if (TclLooksLikeInt(s, length)) { -			result = Tcl_GetLongFromObj((Tcl_Interp *) NULL, -				value2Ptr, &i2); -		    } else { -			result = Tcl_GetDoubleFromObj((Tcl_Interp *) NULL, -				value2Ptr, &d2); -		    } -		    if (result != TCL_OK) { -			TRACE(("%.20s %.20s => ILLEGAL 2nd TYPE %s\n", -			       O2S(value2Ptr), s, -			       (value2Ptr->typePtr? -				    value2Ptr->typePtr->name : "null"))); -			IllegalExprOperandType(interp, pc, value2Ptr); -			Tcl_DecrRefCount(valuePtr); -			Tcl_DecrRefCount(value2Ptr); -			goto checkForCatch; -		    } -		    t2Ptr = value2Ptr->typePtr; +		    mp_neg(First, First); +		    mp_sub_d(First, 1, First); +		    mp_neg(Second, Second); +		    mp_sub_d(Second, 1, Second); +		    mp_and(First, Second, &bigResult); +		    mp_neg(&bigResult, &bigResult); +		    mp_sub_d(&bigResult, 1, &bigResult); +		    break;  		} +		break; -		if ((t1Ptr == &tclDoubleType) || (t2Ptr == &tclDoubleType)) { +	    case INST_BITXOR: +		switch (numPos) { +		case 2:  		    /* -		     * Do double arithmetic. +		     * Both arguments positive, base case.  		     */ -		    doDouble = 1; -		    if (t1Ptr == &tclIntType) { -			d1 = i;       /* promote value 1 to double */ -		    } else if (t2Ptr == &tclIntType) { -			d2 = i2;      /* promote value 2 to double */ -		    } -		    switch (*pc) { -		    case INST_ADD: -			dResult = d1 + d2; -			break; -		    case INST_SUB: -			dResult = d1 - d2; -			break; -		    case INST_MULT: -			dResult = d1 * d2; -			break; -		    case INST_DIV: -			if (d2 == 0.0) { -			    TRACE(("%.6g %.6g => DIVIDE BY ZERO\n", d1, d2)); -			    Tcl_DecrRefCount(valuePtr); -			    Tcl_DecrRefCount(value2Ptr); -			    goto divideByZero; -			} -			dResult = d1 / d2; -			break; -		    } -		     + +		    mp_xor(First, Second, &bigResult); +		    break; +		case 1:  		    /* -		     * Check now for IEEE floating-point error. +		     * First is positive; second negative: +		     * P^N = ~(P^~N) = -(P^(-N-1))-1  		     */ -		     -		    if (IS_NAN(dResult) || IS_INF(dResult)) { -			TRACE(("%.20s %.20s => IEEE FLOATING PT ERROR\n", -			       O2S(valuePtr), O2S(value2Ptr))); -			TclExprFloatError(interp, dResult); -			result = TCL_ERROR; -			Tcl_DecrRefCount(valuePtr); -			Tcl_DecrRefCount(value2Ptr); -			goto checkForCatch; -		    } -		} else { + +		    mp_neg(Second, Second); +		    mp_sub_d(Second, 1, Second); +		    mp_xor(First, Second, &bigResult); +		    mp_neg(&bigResult, &bigResult); +		    mp_sub_d(&bigResult, 1, &bigResult); +		    break; +		case 0:  		    /* -		     * Do integer arithmetic. +		     * Both arguments negative: +		     * a ^ b = (~a ^ ~b) = (-a-1^-b-1)  		     */ -		    switch (*pc) { -		    case INST_ADD: -			iResult = i + i2; -			break; -		    case INST_SUB: -			iResult = i - i2; -			break; -		    case INST_MULT: -			iResult = i * i2; -			break; -		    case INST_DIV: -			/* -			 * This code is tricky: C doesn't guarantee much -			 * about the quotient or remainder, but Tcl does. -			 * The remainder always has the same sign as the -			 * divisor and a smaller absolute value. -			 */ -			if (i2 == 0) { -			    TRACE(("%ld %ld => DIVIDE BY ZERO\n", i, i2)); -			    Tcl_DecrRefCount(valuePtr); -			    Tcl_DecrRefCount(value2Ptr); -			    goto divideByZero; -			} -			if (i2 < 0) { -			    i2 = -i2; -			    i = -i; -			} -			quot = i / i2; -			rem  = i % i2; -			if (rem < 0) { -			    quot -= 1; -			} -			iResult = quot; -			break; -		    } -		} -		/* -		 * Reuse the valuePtr object already on stack if possible. -		 */ -		 -		if (Tcl_IsShared(valuePtr)) { -		    if (doDouble) { -			PUSH_OBJECT(Tcl_NewDoubleObj(dResult)); -			TRACE(("%.6g %.6g => %.6g\n", d1, d2, dResult)); -		    } else { -			PUSH_OBJECT(Tcl_NewLongObj(iResult)); -			TRACE(("%ld %ld => %ld\n", i, i2, iResult)); -		    }  -		    TclDecrRefCount(valuePtr); -		} else {	    /* reuse the valuePtr object */ -		    if (doDouble) { /* NB: stack top is off by 1 */ -			TRACE(("%.6g %.6g => %.6g\n", d1, d2, dResult)); -			Tcl_SetDoubleObj(valuePtr, dResult); -		    } else { -			TRACE(("%ld %ld => %ld\n", i, i2, iResult)); -			Tcl_SetLongObj(valuePtr, iResult); -		    } -		    ++stackTop; /* valuePtr now on stk top has right r.c. */ +		    mp_neg(First, First); +		    mp_sub_d(First, 1, First); +		    mp_neg(Second, Second); +		    mp_sub_d(Second, 1, Second); +		    mp_xor(First, Second, &bigResult); +		    break;  		} -		TclDecrRefCount(value2Ptr); +		break;  	    } -	    ADJUST_PC(1); -	     -	case INST_UPLUS: -	    { -	        /* -	         * Operand must be numeric. -	         */ - -		double d; -		Tcl_ObjType *tPtr; -		 -		valuePtr = stackPtr[stackTop]; -		tPtr = valuePtr->typePtr; -		if ((tPtr != &tclIntType) && ((tPtr != &tclDoubleType) -			|| (valuePtr->bytes != NULL))) { -		    char *s = Tcl_GetStringFromObj(valuePtr, &length); -		    if (TclLooksLikeInt(s, length)) { -			result = Tcl_GetLongFromObj((Tcl_Interp *) NULL, -				valuePtr, &i); -		    } else { -			result = Tcl_GetDoubleFromObj((Tcl_Interp *) NULL, -				valuePtr, &d); -		    } -		    if (result != TCL_OK) {  -			TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n", -			        s, (tPtr? tPtr->name : "null"))); -			IllegalExprOperandType(interp, pc, valuePtr); -			goto checkForCatch; -		    } -		    tPtr = valuePtr->typePtr; -		} +	    mp_clear(&big1); +	    mp_clear(&big2); +	    BIG_RESULT(&bigResult); +	} + +#ifndef TCL_WIDE_INT_IS_LONG +	if ((type1 == TCL_NUMBER_WIDE) || (type2 == TCL_NUMBER_WIDE)) { +	    TclGetWideIntFromObj(NULL, valuePtr, &w1); +	    TclGetWideIntFromObj(NULL, value2Ptr, &w2); + +	    switch (opcode) { +	    case INST_BITAND: +		wResult = w1 & w2; +		break; +	    case INST_BITOR: +		wResult = w1 | w2; +		break; +	    case INST_BITXOR: +		wResult = w1 ^ w2; +		break; +	    default: +		/* Unused, here to silence compiler warning. */ +		wResult = 0; +	    } +	    WIDE_RESULT(wResult); +	} +#endif +	l1 = *((const long *)ptr1); +	l2 = *((const long *)ptr2); + +	switch (opcode) { +	case INST_BITAND: +	    lResult = l1 & l2; +	    break; +	case INST_BITOR: +	    lResult = l1 | l2; +	    break; +	case INST_BITXOR: +	    lResult = l1 ^ l2; +	    break; +	default: +	    /* Unused, here to silence compiler warning. */ +	    lResult = 0; +	} +	LONG_RESULT(lResult); + +    case INST_EXPON: { +	int oddExponent = 0, negativeExponent = 0; +	unsigned short base; + +	if ((type1 == TCL_NUMBER_DOUBLE) || (type2 == TCL_NUMBER_DOUBLE)) { +	    Tcl_GetDoubleFromObj(NULL, valuePtr, &d1); +	    Tcl_GetDoubleFromObj(NULL, value2Ptr, &d2); + +	    if (d1==0.0 && d2<0.0) { +		return EXPONENT_OF_ZERO; +	    } +	    dResult = pow(d1, d2); +	    goto doubleResult; +	} +	l1 = l2 = 0; +	if (type2 == TCL_NUMBER_LONG) { +	    l2 = *((const long *) ptr2); +	    if (l2 == 0) {  		/* -		 * Ensure that the operand's string rep is the same as the -		 * formatted version of its internal rep. This makes sure -		 * that "expr +000123" yields "83", not "000123". We -		 * implement this by _discarding_ the string rep since we -		 * know it will be regenerated, if needed later, by -		 * formatting the internal rep's value. +		 * Anything to the zero power is 1.  		 */ -		if (Tcl_IsShared(valuePtr)) { -		    if (tPtr == &tclIntType) { -			i = valuePtr->internalRep.longValue; -			objPtr = Tcl_NewLongObj(i); -		    } else { -			d = valuePtr->internalRep.doubleValue; -			objPtr = Tcl_NewDoubleObj(d); -		    } -		    Tcl_IncrRefCount(objPtr); -		    Tcl_DecrRefCount(valuePtr); -		    valuePtr = objPtr; -		    stackPtr[stackTop] = valuePtr; -		} else { -		    Tcl_InvalidateStringRep(valuePtr); -		} -		TRACE_WITH_OBJ(("%s => ", O2S(valuePtr)), valuePtr); -	    } -	    ADJUST_PC(1); -	     -	case INST_UMINUS: -	case INST_LNOT: -	    { +		return constants[1]; +	    } else if (l2 == 1) {  		/* -		 * The operand must be numeric or a boolean string as -		 * accepted by Tcl_GetBooleanFromObj(). If the operand -		 * object is unshared modify it directly, otherwise -		 * create a copy to modify: this is "copy on write". -		 * Free any old string representation since it is now -		 * invalid. +		 * Anything to the first power is itself  		 */ -		double d; -		int boolvar; -		Tcl_ObjType *tPtr; +		return NULL; +	    } +	} -		valuePtr = POP_OBJECT(); -		tPtr = valuePtr->typePtr; -		if ((tPtr != &tclIntType) && ((tPtr != &tclDoubleType) -			|| (valuePtr->bytes != NULL))) { -		    if ((tPtr == &tclBooleanType)  -			    && (valuePtr->bytes == NULL)) { -			valuePtr->typePtr = &tclIntType; -		    } else { -			char *s = Tcl_GetStringFromObj(valuePtr, &length); -			if (TclLooksLikeInt(s, length)) { -			    result = Tcl_GetLongFromObj((Tcl_Interp *) NULL, -				    valuePtr, &i); -			} else { -			    result = Tcl_GetDoubleFromObj((Tcl_Interp *) NULL, -				    valuePtr, &d); -			} -			if (result == TCL_ERROR && *pc == INST_LNOT) { -			    result = Tcl_GetBooleanFromObj((Tcl_Interp *)NULL, -				    valuePtr, &boolvar); -			    i = (long)boolvar; /* i is long, not int! */ -			} -			if (result != TCL_OK) { -			    TRACE(("\"%.20s\" => ILLEGAL TYPE %s\n", -				    s, (tPtr? tPtr->name : "null"))); -			    IllegalExprOperandType(interp, pc, valuePtr); -			    Tcl_DecrRefCount(valuePtr); -			    goto checkForCatch; -			} -		    } -		    tPtr = valuePtr->typePtr; -		} +	switch (type2) { +	case TCL_NUMBER_LONG: +	    negativeExponent = (l2 < 0); +	    oddExponent = (int) (l2 & 1); +	    break; +#ifndef TCL_WIDE_INT_IS_LONG +	case TCL_NUMBER_WIDE: +	    w2 = *((const Tcl_WideInt *)ptr2); +	    negativeExponent = (w2 < 0); +	    oddExponent = (int) (w2 & (Tcl_WideInt)1); +	    break; +#endif +	case TCL_NUMBER_BIG: +	    Tcl_TakeBignumFromObj(NULL, value2Ptr, &big2); +	    negativeExponent = (mp_cmp_d(&big2, 0) == MP_LT); +	    mp_mod_2d(&big2, 1, &big2); +	    oddExponent = !mp_iszero(&big2); +	    mp_clear(&big2); +	    break; +	} -		if (Tcl_IsShared(valuePtr)) { +	if (type1 == TCL_NUMBER_LONG) { +	    l1 = *((const long *)ptr1); +	} +	if (negativeExponent) { +	    if (type1 == TCL_NUMBER_LONG) { +		switch (l1) { +		case 0:  		    /* -		     * Create a new object. +		     * Zero to a negative power is div by zero error.  		     */ -		    if ((tPtr == &tclIntType) || (tPtr == &tclBooleanType)) { -			i = valuePtr->internalRep.longValue; -			objPtr = Tcl_NewLongObj( -			        (*pc == INST_UMINUS)? -i : !i); -			TRACE_WITH_OBJ(("%ld => ", i), objPtr); -		    } else { -			d = valuePtr->internalRep.doubleValue; -			if (*pc == INST_UMINUS) { -			    objPtr = Tcl_NewDoubleObj(-d); -			} else { -			    /* -			     * Should be able to use "!d", but apparently -			     * some compilers can't handle it. -			     */ -			    objPtr = Tcl_NewLongObj((d==0.0)? 1 : 0); -			} -			TRACE_WITH_OBJ(("%.6g => ", d), objPtr); + +		    return EXPONENT_OF_ZERO; +		case -1: +		    if (oddExponent) { +			LONG_RESULT(-1);  		    } -		    PUSH_OBJECT(objPtr); -		    TclDecrRefCount(valuePtr); -		} else { +		    /* fallthrough */ +		case 1:  		    /* -		     * valuePtr is unshared. Modify it directly. +		     * 1 to any power is 1.  		     */ -		    if ((tPtr == &tclIntType) || (tPtr == &tclBooleanType)) { -			i = valuePtr->internalRep.longValue; -			Tcl_SetLongObj(valuePtr, -			        (*pc == INST_UMINUS)? -i : !i); -			TRACE_WITH_OBJ(("%ld => ", i), valuePtr); -		    } else { -			d = valuePtr->internalRep.doubleValue; -			if (*pc == INST_UMINUS) { -			    Tcl_SetDoubleObj(valuePtr, -d); -			} else { -			    /* -			     * Should be able to use "!d", but apparently -			     * some compilers can't handle it. -			     */ -			    Tcl_SetLongObj(valuePtr, (d==0.0)? 1 : 0); -			} -			TRACE_WITH_OBJ(("%.6g => ", d), valuePtr); -		    } -		    ++stackTop; /* valuePtr now on stk top has right r.c. */ + +		    return constants[1];  		}  	    } -	    ADJUST_PC(1); -	     -	case INST_BITNOT: -	    { + +	    /* +	     * Integers with magnitude greater than 1 raise to a negative +	     * power yield the answer zero (see TIP 123). +	     */ + +	    return constants[0]; +	} + +	if (type1 == TCL_NUMBER_LONG) { +	    switch (l1) { +	    case 0:  		/* -		 * The operand must be an integer. If the operand object is -		 * unshared modify it directly, otherwise modify a copy.  -		 * Free any old string representation since it is now -		 * invalid. +		 * Zero to a positive power is zero.  		 */ -		 -		Tcl_ObjType *tPtr; -		 -		valuePtr = POP_OBJECT(); -		tPtr = valuePtr->typePtr; -		if (tPtr != &tclIntType) { -		    result = Tcl_GetLongFromObj((Tcl_Interp *) NULL, -			    valuePtr, &i); -		    if (result != TCL_OK) {   /* try to convert to double */ -			TRACE(("\"%.20s\" => ILLEGAL TYPE %s\n", -			       O2S(valuePtr), (tPtr? tPtr->name : "null"))); -			IllegalExprOperandType(interp, pc, valuePtr); -			Tcl_DecrRefCount(valuePtr); -			goto checkForCatch; -		    } -		} -		 -		i = valuePtr->internalRep.longValue; -		if (Tcl_IsShared(valuePtr)) { -		    PUSH_OBJECT(Tcl_NewLongObj(~i)); -		    TRACE(("0x%lx => (%lu)\n", i, ~i)); -		    TclDecrRefCount(valuePtr); -		} else { -		    /* -		     * valuePtr is unshared. Modify it directly. -		     */ -		    Tcl_SetLongObj(valuePtr, ~i); -		    ++stackTop; /* valuePtr now on stk top has right r.c. */ -		    TRACE(("0x%lx => (%lu)\n", i, ~i)); + +		return constants[0]; +	    case 1: +		/* +		 * 1 to any power is 1. +		 */ + +		return constants[1]; +	    case -1: +		if (!oddExponent) { +		    return constants[1];  		} +		LONG_RESULT(-1);  	    } -	    ADJUST_PC(1); -	     -	case INST_CALL_BUILTIN_FUNC1: -	    opnd = TclGetUInt1AtPtr(pc+1); -	    { +	} + +	/* +	 * We refuse to accept exponent arguments that exceed one mp_digit +	 * which means the max exponent value is 2**28-1 = 0x0fffffff = +	 * 268435455, which fits into a signed 32 bit int which is within the +	 * range of the long int type. This means any numeric Tcl_Obj value +	 * not using TCL_NUMBER_LONG type must hold a value larger than we +	 * accept. +	 */ + +	if (type2 != TCL_NUMBER_LONG) { +	    Tcl_SetObjResult(interp, Tcl_NewStringObj( +		    "exponent too large", -1)); +	    return GENERAL_ARITHMETIC_ERROR; +	} + +	if (type1 == TCL_NUMBER_LONG) { +	    if (l1 == 2) {  		/* -		 * Call one of the built-in Tcl math functions. +		 * Reduce small powers of 2 to shifts.  		 */ -		BuiltinFunc *mathFuncPtr; -		ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); +		if ((unsigned long) l2 < CHAR_BIT * sizeof(long) - 1) { +		    LONG_RESULT(1L << l2); +		} +#if !defined(TCL_WIDE_INT_IS_LONG) +		if ((unsigned long)l2 < CHAR_BIT*sizeof(Tcl_WideInt) - 1) { +		    WIDE_RESULT(((Tcl_WideInt) 1) << l2); +		} +#endif +		goto overflowExpon; +	    } +	    if (l1 == -2) { +		int signum = oddExponent ? -1 : 1; -		if ((opnd < 0) || (opnd > LAST_BUILTIN_FUNC)) { -		    TRACE(("UNRECOGNIZED BUILTIN FUNC CODE %d\n", opnd)); -		    panic("TclExecuteByteCode: unrecognized builtin function code %d", opnd); +		/* +		 * Reduce small powers of 2 to shifts. +		 */ + +		if ((unsigned long) l2 < CHAR_BIT * sizeof(long) - 1) { +		    LONG_RESULT(signum * (1L << l2));  		} -		mathFuncPtr = &(builtinFuncTable[opnd]); -		DECACHE_STACK_INFO(); -		tsdPtr->mathInProgress++; -		result = (*mathFuncPtr->proc)(interp, eePtr, -		        mathFuncPtr->clientData); -		tsdPtr->mathInProgress--; -		CACHE_STACK_INFO(); -		if (result != TCL_OK) { -		    goto checkForCatch; +#if !defined(TCL_WIDE_INT_IS_LONG) +		if ((unsigned long)l2 < CHAR_BIT*sizeof(Tcl_WideInt) - 1){ +		    WIDE_RESULT(signum * (((Tcl_WideInt) 1) << l2));  		} -		TRACE_WITH_OBJ(("%d => ", opnd), stackPtr[stackTop]); +#endif +		goto overflowExpon;  	    } -	    ADJUST_PC(2); -		     -	case INST_CALL_FUNC1: -	    opnd = TclGetUInt1AtPtr(pc+1); -	    { +#if (LONG_MAX == 0x7fffffff) +	    if (l2 - 2 < (long)MaxBase32Size +		    && l1 <= MaxBase32[l2 - 2] +		    && l1 >= -MaxBase32[l2 - 2]) {  		/* -		 * Call a non-builtin Tcl math function previously -		 * registered by a call to Tcl_CreateMathFunc. +		 * Small powers of 32-bit integers.  		 */ -		 -		int objc = opnd;   /* Number of arguments. The function name -				    * is the 0-th argument. */ -		Tcl_Obj **objv;	   /* The array of arguments. The function -				    * name is objv[0]. */ -		ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - -		objv = &(stackPtr[stackTop - (objc-1)]); /* "objv[0]" */ -		DECACHE_STACK_INFO(); -		tsdPtr->mathInProgress++; -		result = ExprCallMathFunc(interp, eePtr, objc, objv); -		tsdPtr->mathInProgress--; -		CACHE_STACK_INFO(); -		if (result != TCL_OK) { -		    goto checkForCatch; + +		lResult = l1 * l1;		/* b**2 */ +		switch (l2) { +		case 2: +		    break; +		case 3: +		    lResult *= l1;		/* b**3 */ +		    break; +		case 4: +		    lResult *= lResult;		/* b**4 */ +		    break; +		case 5: +		    lResult *= lResult;		/* b**4 */ +		    lResult *= l1;		/* b**5 */ +		    break; +		case 6: +		    lResult *= l1;		/* b**3 */ +		    lResult *= lResult;		/* b**6 */ +		    break; +		case 7: +		    lResult *= l1;		/* b**3 */ +		    lResult *= lResult;		/* b**6 */ +		    lResult *= l1;		/* b**7 */ +		    break; +		case 8: +		    lResult *= lResult;		/* b**4 */ +		    lResult *= lResult;		/* b**8 */ +		    break;  		} -		TRACE_WITH_OBJ(("%d => ", objc), stackPtr[stackTop]); -		ADJUST_PC(2); +		LONG_RESULT(lResult);  	    } -	case INST_TRY_CVT_TO_NUMERIC: -	    { +	    if (l1 - 3 >= 0 && l1 -2 < (long)Exp32IndexSize +		    && l2 - 2 < (long)(Exp32ValueSize + MaxBase32Size)) { +		base = Exp32Index[l1 - 3] +			+ (unsigned short) (l2 - 2 - MaxBase32Size); +		if (base < Exp32Index[l1 - 2]) { +		    /* +		     * 32-bit number raised to intermediate power, done by +		     * table lookup. +		     */ + +		    LONG_RESULT(Exp32Value[base]); +		} +	    } +	    if (-l1 - 3 >= 0 && -l1 - 2 < (long)Exp32IndexSize +		    && l2 - 2 < (long)(Exp32ValueSize + MaxBase32Size)) { +		base = Exp32Index[-l1 - 3] +			+ (unsigned short) (l2 - 2 - MaxBase32Size); +		if (base < Exp32Index[-l1 - 2]) { +		    /* +		     * 32-bit number raised to intermediate power, done by +		     * table lookup. +		     */ + +		    lResult = (oddExponent) ? +			    -Exp32Value[base] : Exp32Value[base]; +		    LONG_RESULT(lResult); +		} +	    } +#endif +	} +#if (LONG_MAX > 0x7fffffff) || !defined(TCL_WIDE_INT_IS_LONG) +	if (type1 == TCL_NUMBER_LONG) { +	    w1 = l1; +#ifndef TCL_WIDE_INT_IS_LONG +	} else if (type1 == TCL_NUMBER_WIDE) { +	    w1 = *((const Tcl_WideInt *) ptr1); +#endif +	} else { +	    goto overflowExpon; +	} +	if (l2 - 2 < (long)MaxBase64Size +		&& w1 <=  MaxBase64[l2 - 2] +		&& w1 >= -MaxBase64[l2 - 2]) { +	    /* +	     * Small powers of integers whose result is wide. +	     */ + +	    wResult = w1 * w1;		/* b**2 */ +	    switch (l2) { +	    case 2: +		break; +	    case 3: +		wResult *= l1;		/* b**3 */ +		break; +	    case 4: +		wResult *= wResult;	/* b**4 */ +		break; +	    case 5: +		wResult *= wResult;	/* b**4 */ +		wResult *= w1;		/* b**5 */ +		break; +	    case 6: +		wResult *= w1;		/* b**3 */ +		wResult *= wResult;	/* b**6 */ +		break; +	    case 7: +		wResult *= w1;		/* b**3 */ +		wResult *= wResult;	/* b**6 */ +		wResult *= w1;		/* b**7 */ +		break; +	    case 8: +		wResult *= wResult;	/* b**4 */ +		wResult *= wResult;	/* b**8 */ +		break; +	    case 9: +		wResult *= wResult;	/* b**4 */ +		wResult *= wResult;	/* b**8 */ +		wResult *= w1;		/* b**9 */ +		break; +	    case 10: +		wResult *= wResult;	/* b**4 */ +		wResult *= w1;		/* b**5 */ +		wResult *= wResult;	/* b**10 */ +		break; +	    case 11: +		wResult *= wResult;	/* b**4 */ +		wResult *= w1;		/* b**5 */ +		wResult *= wResult;	/* b**10 */ +		wResult *= w1;		/* b**11 */ +		break; +	    case 12: +		wResult *= w1;		/* b**3 */ +		wResult *= wResult;	/* b**6 */ +		wResult *= wResult;	/* b**12 */ +		break; +	    case 13: +		wResult *= w1;		/* b**3 */ +		wResult *= wResult;	/* b**6 */ +		wResult *= wResult;	/* b**12 */ +		wResult *= w1;		/* b**13 */ +		break; +	    case 14: +		wResult *= w1;		/* b**3 */ +		wResult *= wResult;	/* b**6 */ +		wResult *= w1;		/* b**7 */ +		wResult *= wResult;	/* b**14 */ +		break; +	    case 15: +		wResult *= w1;		/* b**3 */ +		wResult *= wResult;	/* b**6 */ +		wResult *= w1;		/* b**7 */ +		wResult *= wResult;	/* b**14 */ +		wResult *= w1;		/* b**15 */ +		break; +	    case 16: +		wResult *= wResult;	/* b**4 */ +		wResult *= wResult;	/* b**8 */ +		wResult *= wResult;	/* b**16 */ +		break; +	    } +	    WIDE_RESULT(wResult); +	} + +	/* +	 * Handle cases of powers > 16 that still fit in a 64-bit word by +	 * doing table lookup. +	 */ + +	if (w1 - 3 >= 0 && w1 - 2 < (long)Exp64IndexSize +		&& l2 - 2 < (long)(Exp64ValueSize + MaxBase64Size)) { +	    base = Exp64Index[w1 - 3] +		    + (unsigned short) (l2 - 2 - MaxBase64Size); +	    if (base < Exp64Index[w1 - 2]) {  		/* -		 * Try to convert the topmost stack object to an int or -		 * double object. This is done in order to support Tcl's -		 * policy of interpreting operands if at all possible as -		 * first integers, else floating-point numbers. +		 * 64-bit number raised to intermediate power, done by +		 * table lookup.  		 */ -		 -		double d; -		char *s; -		Tcl_ObjType *tPtr; -		int converted, shared; - -		valuePtr = stackPtr[stackTop]; -		tPtr = valuePtr->typePtr; -		converted = 0; -		if ((tPtr != &tclIntType) && ((tPtr != &tclDoubleType) -			|| (valuePtr->bytes != NULL))) { -		    if ((tPtr == &tclBooleanType)  -			    && (valuePtr->bytes == NULL)) { -			valuePtr->typePtr = &tclIntType; -			converted = 1; -		    } else { -			s = Tcl_GetStringFromObj(valuePtr, &length); -			if (TclLooksLikeInt(s, length)) { -			    result = Tcl_GetLongFromObj((Tcl_Interp *) NULL, -				    valuePtr, &i); -			} else { -			    result = Tcl_GetDoubleFromObj((Tcl_Interp *) NULL, -				    valuePtr, &d); -			} -			if (result == TCL_OK) { -			    converted = 1; -                       } -			result = TCL_OK; /* reset the result variable */ -		    } -		    tPtr = valuePtr->typePtr; -		} +		WIDE_RESULT(Exp64Value[base]); +	    } +	} + +	if (-w1 - 3 >= 0 && -w1 - 2 < (long)Exp64IndexSize +		&& l2 - 2 < (long)(Exp64ValueSize + MaxBase64Size)) { +	    base = Exp64Index[-w1 - 3] +		    + (unsigned short) (l2 - 2 - MaxBase64Size); +	    if (base < Exp64Index[-w1 - 2]) {  		/* -		 * Ensure that the topmost stack object, if numeric, has a -		 * string rep the same as the formatted version of its -		 * internal rep. This is used, e.g., to make sure that "expr -		 * {0001}" yields "1", not "0001". We implement this by -		 * _discarding_ the string rep since we know it will be -		 * regenerated, if needed later, by formatting the internal -		 * rep's value. Also check if there has been an IEEE -		 * floating point error. +		 * 64-bit number raised to intermediate power, done by +		 * table lookup.  		 */ -		if ((tPtr == &tclIntType) || (tPtr == &tclDoubleType)) { -		    shared = 0; -		    if (Tcl_IsShared(valuePtr)) { -			shared = 1; -			if (valuePtr->bytes != NULL) { -			    /* -			     * We only need to make a copy of the object -			     * when it already had a string rep -			     */ -			    if (tPtr == &tclIntType) { -				i = valuePtr->internalRep.longValue; -				objPtr = Tcl_NewLongObj(i); -			    } else { -				d = valuePtr->internalRep.doubleValue; -				objPtr = Tcl_NewDoubleObj(d); -			    } -			    Tcl_IncrRefCount(objPtr); -			    TclDecrRefCount(valuePtr); -			    valuePtr = objPtr; -			    stackPtr[stackTop] = valuePtr; -			    tPtr = valuePtr->typePtr; -			} -		    } else { -			Tcl_InvalidateStringRep(valuePtr); -		    } -		 -		    if (tPtr == &tclDoubleType) { -			d = valuePtr->internalRep.doubleValue; -			if (IS_NAN(d) || IS_INF(d)) { -			    TRACE(("\"%.20s\" => IEEE FLOATING PT ERROR\n", -			           O2S(valuePtr))); -			    TclExprFloatError(interp, d); -			    result = TCL_ERROR; -			    goto checkForCatch; -			} -		    } -		    shared = shared;        /* lint, shared not used. */ -		    converted = converted;  /* lint, converted not used. */ -		    TRACE(("\"%.20s\" => numeric, %s, %s\n", O2S(valuePtr), -			   (converted? "converted" : "not converted"), -			   (shared? "shared" : "not shared"))); -		} else { -		    TRACE(("\"%.20s\" => not numeric\n", O2S(valuePtr))); -		} +		wResult = oddExponent ? -Exp64Value[base] : Exp64Value[base]; +		WIDE_RESULT(wResult);  	    } -	    ADJUST_PC(1); +	} +#endif + +    overflowExpon: +	Tcl_TakeBignumFromObj(NULL, value2Ptr, &big2); +	if (big2.used > 1) { +	    mp_clear(&big2); +	    Tcl_SetObjResult(interp, Tcl_NewStringObj( +		    "exponent too large", -1)); +	    return GENERAL_ARITHMETIC_ERROR; +	} +	Tcl_TakeBignumFromObj(NULL, valuePtr, &big1); +	mp_init(&bigResult); +	mp_expt_d(&big1, big2.dp[0], &bigResult); +	mp_clear(&big1); +	mp_clear(&big2); +	BIG_RESULT(&bigResult); +    } -	case INST_BREAK: +    case INST_ADD: +    case INST_SUB: +    case INST_MULT: +    case INST_DIV: +	if ((type1 == TCL_NUMBER_DOUBLE) || (type2 == TCL_NUMBER_DOUBLE)) {  	    /* -	     * First reset the interpreter's result. Then find the closest -	     * enclosing loop or catch exception range, if any. If a loop is -	     * found, terminate its execution. If the closest is a catch -	     * exception range, jump to its catchOffset. If no enclosing -	     * range is found, stop execution and return TCL_BREAK. +	     * At least one of the values is floating-point, so perform +	     * floating point calculations.  	     */ -	    Tcl_ResetResult(interp); -	    rangePtr = GetExceptRangeForPc(pc, /*catchOnly*/ 0, codePtr); -	    if (rangePtr == NULL) { -		TRACE(("=> no encl. loop or catch, returning TCL_BREAK\n")); -		result = TCL_BREAK; -		goto abnormalReturn; /* no catch exists to check */ -	    } -	    switch (rangePtr->type) { -	    case LOOP_EXCEPTION_RANGE: -		result = TCL_OK; -		TRACE(("=> range at %d, new pc %d\n", -		       rangePtr->codeOffset, rangePtr->breakOffset)); +	    Tcl_GetDoubleFromObj(NULL, valuePtr, &d1); +	    Tcl_GetDoubleFromObj(NULL, value2Ptr, &d2); + +	    switch (opcode) { +	    case INST_ADD: +		dResult = d1 + d2; +		break; +	    case INST_SUB: +		dResult = d1 - d2; +		break; +	    case INST_MULT: +		dResult = d1 * d2; +		break; +	    case INST_DIV: +#ifndef IEEE_FLOATING_POINT +		if (d2 == 0.0) { +		    return DIVIDED_BY_ZERO; +		} +#endif +		/* +		 * We presume that we are running with zero-divide unmasked if +		 * we're on an IEEE box. Otherwise, this statement might cause +		 * demons to fly out our noses. +		 */ + +		dResult = d1 / d2;  		break; -	    case CATCH_EXCEPTION_RANGE: -		result = TCL_BREAK; -		TRACE(("=> ...\n")); -		goto processCatch; /* it will use rangePtr */  	    default: -		panic("TclExecuteByteCode: unrecognized ExceptionRange type %d\n", rangePtr->type); -	    } -	    pc = (codePtr->codeStart + rangePtr->breakOffset); -	    continue;	/* restart outer instruction loop at pc */ - -	case INST_CONTINUE: -            /* -	     * Find the closest enclosing loop or catch exception range, -	     * if any. If a loop is found, skip to its next iteration. -	     * If the closest is a catch exception range, jump to its -	     * catchOffset. If no enclosing range is found, stop -	     * execution and return TCL_CONTINUE. +		/* Unused, here to silence compiler warning. */ +		dResult = 0; +	    } + +	doubleResult: +#ifndef ACCEPT_NAN +	    /* +	     * Check now for IEEE floating-point error.  	     */ -	    Tcl_ResetResult(interp); -	    rangePtr = GetExceptRangeForPc(pc, /*catchOnly*/ 0, codePtr); -	    if (rangePtr == NULL) { -		TRACE(("=> no encl. loop or catch, returning TCL_CONTINUE\n")); -		result = TCL_CONTINUE; -		goto abnormalReturn; +	    if (TclIsNaN(dResult)) { +		TclExprFloatError(interp, dResult); +		return GENERAL_ARITHMETIC_ERROR;  	    } -	    switch (rangePtr->type) { -	    case LOOP_EXCEPTION_RANGE: -		if (rangePtr->continueOffset == -1) { -		    TRACE(("=> loop w/o continue, checking for catch\n")); -		    goto checkForCatch; -		} else { -		    result = TCL_OK; -		    TRACE(("=> range at %d, new pc %d\n", -			   rangePtr->codeOffset, rangePtr->continueOffset)); +#endif +	    DOUBLE_RESULT(dResult); +	} +	if ((type1 != TCL_NUMBER_BIG) && (type2 != TCL_NUMBER_BIG)) { +	    TclGetWideIntFromObj(NULL, valuePtr, &w1); +	    TclGetWideIntFromObj(NULL, value2Ptr, &w2); + +	    switch (opcode) { +	    case INST_ADD: +		wResult = w1 + w2; +#ifndef TCL_WIDE_INT_IS_LONG +		if ((type1 == TCL_NUMBER_WIDE) || (type2 == TCL_NUMBER_WIDE)) +#endif +		{ +		    /* +		     * Check for overflow. +		     */ + +		    if (Overflowing(w1, w2, wResult)) { +			goto overflowBasic; +		    }  		}  		break; -	    case CATCH_EXCEPTION_RANGE: -		result = TCL_CONTINUE; -		TRACE(("=> ...\n")); -		goto processCatch; /* it will use rangePtr */ -	    default: -		panic("TclExecuteByteCode: unrecognized ExceptionRange type %d\n", rangePtr->type); -	    } -	    pc = (codePtr->codeStart + rangePtr->continueOffset); -	    continue;	/* restart outer instruction loop at pc */ -	case INST_FOREACH_START4: -	    opnd = TclGetUInt4AtPtr(pc+1); -	    { -	        /* -		 * Initialize the temporary local var that holds the count -		 * of the number of iterations of the loop body to -1. -		 */ +	    case INST_SUB: +		wResult = w1 - w2; +#ifndef TCL_WIDE_INT_IS_LONG +		if ((type1 == TCL_NUMBER_WIDE) || (type2 == TCL_NUMBER_WIDE)) +#endif +		{ +		    /* +		     * Must check for overflow. The macro tests for overflows +		     * in sums by looking at the sign bits. As we have a +		     * subtraction here, we are adding -w2. As -w2 could in +		     * turn overflow, we test with ~w2 instead: it has the +		     * opposite sign bit to w2 so it does the job. Note that +		     * the only "bad" case (w2==0) is irrelevant for this +		     * macro, as in that case w1 and wResult have the same +		     * sign and there is no overflow anyway. +		     */ -		ForeachInfo *infoPtr = (ForeachInfo *) -		    codePtr->auxDataArrayPtr[opnd].clientData; -		int iterTmpIndex = infoPtr->loopCtTemp; -		Var *compiledLocals = iPtr->varFramePtr->compiledLocals; -		Var *iterVarPtr = &(compiledLocals[iterTmpIndex]); -		Tcl_Obj *oldValuePtr = iterVarPtr->value.objPtr; +		    if (Overflowing(w1, ~w2, wResult)) { +			goto overflowBasic; +		    } +		} +		break; -		if (oldValuePtr == NULL) { -		    iterVarPtr->value.objPtr = Tcl_NewLongObj(-1); -		    Tcl_IncrRefCount(iterVarPtr->value.objPtr); -		} else { -		    Tcl_SetLongObj(oldValuePtr, -1); +	    case INST_MULT: +		if ((type1 != TCL_NUMBER_LONG) || (type2 != TCL_NUMBER_LONG) +			|| (sizeof(Tcl_WideInt) < 2*sizeof(long))) { +		    goto overflowBasic;  		} -		TclSetVarScalar(iterVarPtr); -		TclClearVarUndefined(iterVarPtr); -		TRACE(("%u => loop iter count temp %d\n",  -		        opnd, iterTmpIndex)); -	    } -	    ADJUST_PC(5); -	 -	case INST_FOREACH_STEP4: -	    opnd = TclGetUInt4AtPtr(pc+1); -	    { -	        /* -		 * "Step" a foreach loop (i.e., begin its next iteration) by -		 * assigning the next value list element to each loop var. -		 */ +		wResult = w1 * w2; +		break; -		ForeachInfo *infoPtr = (ForeachInfo *) -		        codePtr->auxDataArrayPtr[opnd].clientData; -		ForeachVarList *varListPtr; -		int numLists = infoPtr->numLists; -		Var *compiledLocals = iPtr->varFramePtr->compiledLocals; -		Tcl_Obj *listPtr; -		List *listRepPtr; -		Var *iterVarPtr, *listVarPtr; -		int iterNum, listTmpIndex, listLen, numVars; -		int varIndex, valIndex, continueLoop, j; +	    case INST_DIV: +		if (w2 == 0) { +		    return DIVIDED_BY_ZERO; +		}  		/* -		 * Increment the temp holding the loop iteration number. +		 * Need a bignum to represent (LLONG_MIN / -1)  		 */ -		iterVarPtr = &(compiledLocals[infoPtr->loopCtTemp]); -		valuePtr = iterVarPtr->value.objPtr; -		iterNum = (valuePtr->internalRep.longValue + 1); -		Tcl_SetLongObj(valuePtr, iterNum); -		 +		if ((w1 == LLONG_MIN) && (w2 == -1)) { +		    goto overflowBasic; +		} +		wResult = w1 / w2; +  		/* -		 * Check whether all value lists are exhausted and we should -		 * stop the loop. +		 * Force Tcl's integer division rules. +		 * TODO: examine for logic simplification  		 */ -		continueLoop = 0; -		listTmpIndex = infoPtr->firstValueTemp; -		for (i = 0;  i < numLists;  i++) { -		    varListPtr = infoPtr->varLists[i]; -		    numVars = varListPtr->numVars; -		     -		    listVarPtr = &(compiledLocals[listTmpIndex]); -		    listPtr = listVarPtr->value.objPtr; -		    result = Tcl_ListObjLength(interp, listPtr, &listLen); -		    if (result != TCL_OK) { -			TRACE_WITH_OBJ(("%u => ERROR converting list %ld, \"%s\": ", -			        opnd, i, O2S(listPtr)), -				Tcl_GetObjResult(interp)); -			goto checkForCatch; -		    } -		    if (listLen > (iterNum * numVars)) { -			continueLoop = 1; -		    } -		    listTmpIndex++; +		if (((wResult < 0) || ((wResult == 0) && +			((w1 < 0 && w2 > 0) || (w1 > 0 && w2 < 0)))) && +			(wResult*w2 != w1)) { +		    wResult -= 1;  		} +		break; +	    default:  		/* -		 * If some var in some var list still has a remaining list -		 * element iterate one more time. Assign to var the next -		 * element from its value list. We already checked above -		 * that each list temp holds a valid list object. +		 * Unused, here to silence compiler warning.  		 */ -		 -		if (continueLoop) { -		    listTmpIndex = infoPtr->firstValueTemp; -		    for (i = 0;  i < numLists;  i++) { -			varListPtr = infoPtr->varLists[i]; -			numVars = varListPtr->numVars; - -			listVarPtr = &(compiledLocals[listTmpIndex]); -			listPtr = listVarPtr->value.objPtr; -			listRepPtr = (List *) listPtr->internalRep.otherValuePtr; -			listLen = listRepPtr->elemCount; -			 -			valIndex = (iterNum * numVars); -			for (j = 0;  j < numVars;  j++) { -			    int setEmptyStr = 0; -			    if (valIndex >= listLen) { -				setEmptyStr = 1; -				valuePtr = Tcl_NewObj(); -			    } else { -				valuePtr = listRepPtr->elements[valIndex]; -			    } -			     -			    varIndex = varListPtr->varIndexes[j]; -			    DECACHE_STACK_INFO(); -			    value2Ptr = TclSetIndexedScalar(interp, -			           varIndex, valuePtr, TCL_LEAVE_ERR_MSG); -			    CACHE_STACK_INFO(); -			    if (value2Ptr == NULL) { -				TRACE_WITH_OBJ(("%u => ERROR init. index temp %d: ", -				       opnd, varIndex), -				       Tcl_GetObjResult(interp)); -				if (setEmptyStr) { -				    Tcl_DecrRefCount(valuePtr); -				} -				result = TCL_ERROR; -				goto checkForCatch; -			    } -			    valIndex++; -			} -			listTmpIndex++; -		    } -		} -		 + +		wResult = 0; +	    } + +	    WIDE_RESULT(wResult); +	} + +    overflowBasic: +	Tcl_TakeBignumFromObj(NULL, valuePtr, &big1); +	Tcl_TakeBignumFromObj(NULL, value2Ptr, &big2); +	mp_init(&bigResult); +	switch (opcode) { +	case INST_ADD: +	    mp_add(&big1, &big2, &bigResult); +	    break; +	case INST_SUB: +	    mp_sub(&big1, &big2, &bigResult); +	    break; +	case INST_MULT: +	    mp_mul(&big1, &big2, &bigResult); +	    break; +	case INST_DIV: +	    if (mp_iszero(&big2)) { +		mp_clear(&big1); +		mp_clear(&big2); +		mp_clear(&bigResult); +		return DIVIDED_BY_ZERO; +	    } +	    mp_init(&bigRemainder); +	    mp_div(&big1, &big2, &bigResult, &bigRemainder); +	    /* TODO: internals intrusion */ +	    if (!mp_iszero(&bigRemainder) +		    && (bigRemainder.sign != big2.sign)) {  		/* -		 * Push 1 if at least one value list had a remaining element -		 * and the loop should continue. Otherwise push 0. +		 * Convert to Tcl's integer division rules.  		 */ -		PUSH_OBJECT(Tcl_NewLongObj(continueLoop)); -		TRACE(("%u => %d lists, iter %d, %s loop\n",  -		        opnd, numLists, iterNum, -		        (continueLoop? "continue" : "exit"))); +		mp_sub_d(&bigResult, 1, &bigResult); +		mp_add(&bigRemainder, &big2, &bigRemainder);  	    } -	    ADJUST_PC(5); +	    mp_clear(&bigRemainder); +	    break; +	} +	mp_clear(&big1); +	mp_clear(&big2); +	BIG_RESULT(&bigResult); +    } + +    Tcl_Panic("unexpected opcode"); +    return NULL; +} + +static Tcl_Obj * +ExecuteExtendedUnaryMathOp( +    int opcode,			/* What operation to perform. */ +    Tcl_Obj *valuePtr)		/* The operand on the stack. */ +{ +    ClientData ptr; +    int type; +    Tcl_WideInt w; +    mp_int big; +    Tcl_Obj *objResultPtr; + +    (void) GetNumberFromObj(NULL, valuePtr, &ptr, &type); + +    switch (opcode) { +    case INST_BITNOT: +#ifndef TCL_WIDE_INT_IS_LONG +	if (type == TCL_NUMBER_WIDE) { +	    w = *((const Tcl_WideInt *) ptr); +	    WIDE_RESULT(~w); +	} +#endif +	Tcl_TakeBignumFromObj(NULL, valuePtr, &big); +	/* ~a = - a - 1 */ +	mp_neg(&big, &big); +	mp_sub_d(&big, 1, &big); +	BIG_RESULT(&big); +    case INST_UMINUS: +	switch (type) { +	case TCL_NUMBER_DOUBLE: +	    DOUBLE_RESULT(-(*((const double *) ptr))); +	case TCL_NUMBER_LONG: +	    w = (Tcl_WideInt) (*((const long *) ptr)); +	    if (w != LLONG_MIN) { +		WIDE_RESULT(-w); +	    } +	    TclBNInitBignumFromLong(&big, *(const long *) ptr); +	    break; +#ifndef TCL_WIDE_INT_IS_LONG +	case TCL_NUMBER_WIDE: +	    w = *((const Tcl_WideInt *) ptr); +	    if (w != LLONG_MIN) { +		WIDE_RESULT(-w); +	    } +	    TclBNInitBignumFromWideInt(&big, w); +	    break; +#endif +	default: +	    Tcl_TakeBignumFromObj(NULL, valuePtr, &big); +	} +	mp_neg(&big, &big); +	BIG_RESULT(&big); +    } + +    Tcl_Panic("unexpected opcode"); +    return NULL; +} +#undef LONG_RESULT +#undef WIDE_RESULT +#undef BIG_RESULT +#undef DOUBLE_RESULT + +/* + *---------------------------------------------------------------------- + * + * CompareTwoNumbers -- + * + *	This function compares a pair of numbers in Tcl_Objs. Each argument + *	must already be known to be numeric and not NaN. + * + * Results: + *	One of MP_LT, MP_EQ or MP_GT, depending on whether valuePtr is less + *	than, equal to, or greater than value2Ptr (respectively). + * + * Side effects: + *	None, provided both values are numeric. + * + *---------------------------------------------------------------------- + */ + +int +TclCompareTwoNumbers( +    Tcl_Obj *valuePtr, +    Tcl_Obj *value2Ptr) +{ +    int type1, type2, compare; +    ClientData ptr1, ptr2; +    mp_int big1, big2; +    double d1, d2, tmp; +    long l1, l2; +#ifndef TCL_WIDE_INT_IS_LONG +    Tcl_WideInt w1, w2; +#endif + +    (void) GetNumberFromObj(NULL, valuePtr, &ptr1, &type1); +    (void) GetNumberFromObj(NULL, value2Ptr, &ptr2, &type2); + +    switch (type1) { +    case TCL_NUMBER_LONG: +	l1 = *((const long *)ptr1); +	switch (type2) { +	case TCL_NUMBER_LONG: +	    l2 = *((const long *)ptr2); +	longCompare: +	    return (l1 < l2) ? MP_LT : ((l1 > l2) ? MP_GT : MP_EQ); +#ifndef TCL_WIDE_INT_IS_LONG +	case TCL_NUMBER_WIDE: +	    w2 = *((const Tcl_WideInt *)ptr2); +	    w1 = (Tcl_WideInt)l1; +	    goto wideCompare; +#endif +	case TCL_NUMBER_DOUBLE: +	    d2 = *((const double *)ptr2); +	    d1 = (double) l1; -	case INST_BEGIN_CATCH4:  	    /* -	     * Record start of the catch command with exception range index -	     * equal to the operand. Push the current stack depth onto the -	     * special catch stack. +	     * If the double has a fractional part, or if the long can be +	     * converted to double without loss of precision, then compare as +	     * doubles.  	     */ -	    catchStackPtr[++catchTop] = stackTop; -	    TRACE(("%u => catchTop=%d, stackTop=%d\n", -		    TclGetUInt4AtPtr(pc+1), catchTop, stackTop)); -	    ADJUST_PC(5); -	case INST_END_CATCH: -	    catchTop--; -	    result = TCL_OK; -	    TRACE(("=> catchTop=%d\n", catchTop)); -	    ADJUST_PC(1); +	    if (DBL_MANT_DIG > CHAR_BIT*sizeof(long) || l1 == (long) d1 +		    || modf(d2, &tmp) != 0.0) { +		goto doubleCompare; +	    } -	case INST_PUSH_RESULT: -	    PUSH_OBJECT(Tcl_GetObjResult(interp)); -	    TRACE_WITH_OBJ(("=> "), Tcl_GetObjResult(interp)); -	    ADJUST_PC(1); +	    /* +	     * Otherwise, to make comparision based on full precision, need to +	     * convert the double to a suitably sized integer. +	     * +	     * Need this to get comparsions like +	     *	  expr 20000000000000003 < 20000000000000004.0 +	     * right. Converting the first argument to double will yield two +	     * double values that are equivalent within double precision. +	     * Converting the double to an integer gets done exactly, then +	     * integer comparison can tell the difference. +	     */ -	case INST_PUSH_RETURN_CODE: -	    PUSH_OBJECT(Tcl_NewLongObj(result)); -	    TRACE(("=> %u\n", result)); -	    ADJUST_PC(1); +	    if (d2 < (double)LONG_MIN) { +		return MP_GT; +	    } +	    if (d2 > (double)LONG_MAX) { +		return MP_LT; +	    } +	    l2 = (long) d2; +	    goto longCompare; +	case TCL_NUMBER_BIG: +	    Tcl_TakeBignumFromObj(NULL, value2Ptr, &big2); +	    if (mp_cmp_d(&big2, 0) == MP_LT) { +		compare = MP_GT; +	    } else { +		compare = MP_LT; +	    } +	    mp_clear(&big2); +	    return compare; +	} -	default: -	    panic("TclExecuteByteCode: unrecognized opCode %u", *pc); -	} /* end of switch on opCode */ +#ifndef TCL_WIDE_INT_IS_LONG +    case TCL_NUMBER_WIDE: +	w1 = *((const Tcl_WideInt *)ptr1); +	switch (type2) { +	case TCL_NUMBER_WIDE: +	    w2 = *((const Tcl_WideInt *)ptr2); +	wideCompare: +	    return (w1 < w2) ? MP_LT : ((w1 > w2) ? MP_GT : MP_EQ); +	case TCL_NUMBER_LONG: +	    l2 = *((const long *)ptr2); +	    w2 = (Tcl_WideInt)l2; +	    goto wideCompare; +	case TCL_NUMBER_DOUBLE: +	    d2 = *((const double *)ptr2); +	    d1 = (double) w1; +	    if (DBL_MANT_DIG > CHAR_BIT*sizeof(Tcl_WideInt) +		    || w1 == (Tcl_WideInt) d1 || modf(d2, &tmp) != 0.0) { +		goto doubleCompare; +	    } +	    if (d2 < (double)LLONG_MIN) { +		return MP_GT; +	    } +	    if (d2 > (double)LLONG_MAX) { +		return MP_LT; +	    } +	    w2 = (Tcl_WideInt) d2; +	    goto wideCompare; +	case TCL_NUMBER_BIG: +	    Tcl_TakeBignumFromObj(NULL, value2Ptr, &big2); +	    if (mp_cmp_d(&big2, 0) == MP_LT) { +		compare = MP_GT; +	    } else { +		compare = MP_LT; +	    } +	    mp_clear(&big2); +	    return compare; +	} +#endif -	/* -	 * Division by zero in an expression. Control only reaches this -	 * point by "goto divideByZero". -	 */ -	 -        divideByZero: -	Tcl_ResetResult(interp); -	Tcl_AppendToObj(Tcl_GetObjResult(interp), "divide by zero", -1); -	Tcl_SetErrorCode(interp, "ARITH", "DIVZERO", "divide by zero", -			 (char *) NULL); -	result = TCL_ERROR; -	 -	/* -	 * Execution has generated an "exception" such as TCL_ERROR. If the -	 * exception is an error, record information about what was being -	 * executed when the error occurred. Find the closest enclosing -	 * catch range, if any. If no enclosing catch range is found, stop -	 * execution and return the "exception" code. -	 */ -	 -        checkForCatch: -	if ((result == TCL_ERROR) && !(iPtr->flags & ERR_ALREADY_LOGGED)) { -	    bytes = GetSrcInfoForPc(pc, codePtr, &length); -	    if (bytes != NULL) { -		Tcl_LogCommandInfo(interp, codePtr->source, bytes, length); -		iPtr->flags |= ERR_ALREADY_LOGGED; +    case TCL_NUMBER_DOUBLE: +	d1 = *((const double *)ptr1); +	switch (type2) { +	case TCL_NUMBER_DOUBLE: +	    d2 = *((const double *)ptr2); +	doubleCompare: +	    return (d1 < d2) ? MP_LT : ((d1 > d2) ? MP_GT : MP_EQ); +	case TCL_NUMBER_LONG: +	    l2 = *((const long *)ptr2); +	    d2 = (double) l2; +	    if (DBL_MANT_DIG > CHAR_BIT*sizeof(long) || l2 == (long) d2 +		    || modf(d1, &tmp) != 0.0) { +		goto doubleCompare;  	    } -        } -	rangePtr = GetExceptRangeForPc(pc, /*catchOnly*/ 1, codePtr); -	if (rangePtr == NULL) { -#ifdef TCL_COMPILE_DEBUG -	    if (traceInstructions) { -		fprintf(stdout, "   ... no enclosing catch, returning %s\n", -		        StringForResultCode(result)); +	    if (d1 < (double)LONG_MIN) { +		return MP_LT; +	    } +	    if (d1 > (double)LONG_MAX) { +		return MP_GT;  	    } +	    l1 = (long) d1; +	    goto longCompare; +#ifndef TCL_WIDE_INT_IS_LONG +	case TCL_NUMBER_WIDE: +	    w2 = *((const Tcl_WideInt *)ptr2); +	    d2 = (double) w2; +	    if (DBL_MANT_DIG > CHAR_BIT*sizeof(Tcl_WideInt) +		    || w2 == (Tcl_WideInt) d2 || modf(d1, &tmp) != 0.0) { +		goto doubleCompare; +	    } +	    if (d1 < (double)LLONG_MIN) { +		return MP_LT; +	    } +	    if (d1 > (double)LLONG_MAX) { +		return MP_GT; +	    } +	    w1 = (Tcl_WideInt) d1; +	    goto wideCompare;  #endif -	    goto abnormalReturn; +	case TCL_NUMBER_BIG: +	    if (TclIsInfinite(d1)) { +		return (d1 > 0.0) ? MP_GT : MP_LT; +	    } +	    Tcl_TakeBignumFromObj(NULL, value2Ptr, &big2); +	    if ((d1 < (double)LONG_MAX) && (d1 > (double)LONG_MIN)) { +		if (mp_cmp_d(&big2, 0) == MP_LT) { +		    compare = MP_GT; +		} else { +		    compare = MP_LT; +		} +		mp_clear(&big2); +		return compare; +	    } +	    if (DBL_MANT_DIG > CHAR_BIT*sizeof(long) +		    && modf(d1, &tmp) != 0.0) { +		d2 = TclBignumToDouble(&big2); +		mp_clear(&big2); +		goto doubleCompare; +	    } +	    Tcl_InitBignumFromDouble(NULL, d1, &big1); +	    goto bigCompare;  	} -	/* -	 * A catch exception range (rangePtr) was found to handle an -	 * "exception". It was found either by checkForCatch just above or -	 * by an instruction during break, continue, or error processing. -	 * Jump to its catchOffset after unwinding the operand stack to -	 * the depth it had when starting to execute the range's catch -	 * command. -	 */ - -        processCatch: -	while (stackTop > catchStackPtr[catchTop]) { -	    valuePtr = POP_OBJECT(); -	    TclDecrRefCount(valuePtr); -	} -#ifdef TCL_COMPILE_DEBUG -	if (traceInstructions) { -	    fprintf(stdout, "  ... found catch at %d, catchTop=%d, unwound to %d, new pc %u\n", -	        rangePtr->codeOffset, catchTop, catchStackPtr[catchTop], -	        (unsigned int)(rangePtr->catchOffset)); +    case TCL_NUMBER_BIG: +	Tcl_TakeBignumFromObj(NULL, valuePtr, &big1); +	switch (type2) { +#ifndef TCL_WIDE_INT_IS_LONG +	case TCL_NUMBER_WIDE: +#endif +	case TCL_NUMBER_LONG: +	    compare = mp_cmp_d(&big1, 0); +	    mp_clear(&big1); +	    return compare; +	case TCL_NUMBER_DOUBLE: +	    d2 = *((const double *)ptr2); +	    if (TclIsInfinite(d2)) { +		compare = (d2 > 0.0) ? MP_LT : MP_GT; +		mp_clear(&big1); +		return compare; +	    } +	    if ((d2 < (double)LONG_MAX) && (d2 > (double)LONG_MIN)) { +		compare = mp_cmp_d(&big1, 0); +		mp_clear(&big1); +		return compare; +	    } +	    if (DBL_MANT_DIG > CHAR_BIT*sizeof(long) +		    && modf(d2, &tmp) != 0.0) { +		d1 = TclBignumToDouble(&big1); +		mp_clear(&big1); +		goto doubleCompare; +	    } +	    Tcl_InitBignumFromDouble(NULL, d2, &big2); +	    goto bigCompare; +	case TCL_NUMBER_BIG: +	    Tcl_TakeBignumFromObj(NULL, value2Ptr, &big2); +	bigCompare: +	    compare = mp_cmp(&big1, &big2); +	    mp_clear(&big1); +	    mp_clear(&big2); +	    return compare;  	} -#endif	 -	pc = (codePtr->codeStart + rangePtr->catchOffset); -	continue;		/* restart the execution loop at pc */ -    } /* end of infinite loop dispatching on instructions */ - -    /* -     * Abnormal return code. Restore the stack to state it had when starting -     * to execute the ByteCode. -     */ - -    abnormalReturn: -    while (stackTop > initStackTop) { -	valuePtr = POP_OBJECT(); -	Tcl_DecrRefCount(valuePtr); -    } - -    /* -     * Free the catch stack array if malloc'ed storage was used. -     */ - -    done: -    if (catchStackPtr != catchStackStorage) { -	ckfree((char *) catchStackPtr); +    default: +	Tcl_Panic("unexpected number type"); +	return TCL_ERROR;      } -    eePtr->stackTop = initStackTop; -    return result; -#undef STATIC_CATCH_STACK_SIZE  }  #ifdef TCL_COMPILE_DEBUG @@ -3469,9 +9515,9 @@ TclExecuteByteCode(interp, codePtr)   *   * PrintByteCodeInfo --   * - *	This procedure prints a summary about a bytecode object to stdout. - *	It is called by TclExecuteByteCode when starting to execute the - *	bytecode object if tclTraceExec has the value 2 or more. + *	This procedure prints a summary about a bytecode object to stdout. It + *	is called by TclNRExecuteByteCode when starting to execute the bytecode + *	object if tclTraceExec has the value 2 or more.   *   * Results:   *	None. @@ -3483,46 +9529,45 @@ TclExecuteByteCode(interp, codePtr)   */  static void -PrintByteCodeInfo(codePtr) -    register ByteCode *codePtr;	/* The bytecode whose summary is printed -				 * to stdout. */ +PrintByteCodeInfo( +    register ByteCode *codePtr)	/* The bytecode whose summary is printed to +				 * stdout. */  {      Proc *procPtr = codePtr->procPtr;      Interp *iPtr = (Interp *) *codePtr->interpHandle; -    fprintf(stdout, "\nExecuting ByteCode 0x%x, refCt %u, epoch %u, interp 0x%x (epoch %u)\n", -	    (unsigned int) codePtr, codePtr->refCount, -	    codePtr->compileEpoch, (unsigned int) iPtr, +    fprintf(stdout, "\nExecuting ByteCode 0x%p, refCt %u, epoch %u, interp 0x%p (epoch %u)\n", +	    codePtr, codePtr->refCount, codePtr->compileEpoch, iPtr,  	    iPtr->compileEpoch); -     +      fprintf(stdout, "  Source: ");      TclPrintSource(stdout, codePtr->source, 60);      fprintf(stdout, "\n  Cmds %d, src %d, inst %u, litObjs %u, aux %d, stkDepth %u, code/src %.2f\n", -            codePtr->numCommands, codePtr->numSrcBytes, +	    codePtr->numCommands, codePtr->numSrcBytes,  	    codePtr->numCodeBytes, codePtr->numLitObjects,  	    codePtr->numAuxDataItems, codePtr->maxStackDepth,  #ifdef TCL_COMPILE_STATS -	    (codePtr->numSrcBytes? -	            ((float)codePtr->structureSize)/((float)codePtr->numSrcBytes) : 0.0)); -#else -	    0.0); +	    codePtr->numSrcBytes? +		    ((float)codePtr->structureSize)/codePtr->numSrcBytes :  #endif +	    0.0); +  #ifdef TCL_COMPILE_STATS -    fprintf(stdout, "  Code %d = header %d+inst %d+litObj %d+exc %d+aux %d+cmdMap %d\n", -	    codePtr->structureSize, -	    (sizeof(ByteCode) - (sizeof(size_t) + sizeof(Tcl_Time))), +    fprintf(stdout, "  Code %lu = header %lu+inst %d+litObj %lu+exc %lu+aux %lu+cmdMap %d\n", +	    (unsigned long) codePtr->structureSize, +	    (unsigned long) (sizeof(ByteCode)-sizeof(size_t)-sizeof(Tcl_Time)),  	    codePtr->numCodeBytes, -	    (codePtr->numLitObjects * sizeof(Tcl_Obj *)), -	    (codePtr->numExceptRanges * sizeof(ExceptionRange)), -	    (codePtr->numAuxDataItems * sizeof(AuxData)), +	    (unsigned long) (codePtr->numLitObjects * sizeof(Tcl_Obj *)), +	    (unsigned long) (codePtr->numExceptRanges*sizeof(ExceptionRange)), +	    (unsigned long) (codePtr->numAuxDataItems * sizeof(AuxData)),  	    codePtr->numCmdLocBytes);  #endif /* TCL_COMPILE_STATS */      if (procPtr != NULL) {  	fprintf(stdout, -		"  Proc 0x%x, refCt %d, args %d, compiled locals %d\n", -		(unsigned int) procPtr, procPtr->refCount, -		procPtr->numArgs, procPtr->numCompiledLocals); +		"  Proc 0x%p, refCt %d, args %d, compiled locals %d\n", +		procPtr, procPtr->refCount, procPtr->numArgs, +		procPtr->numCompiledLocals);      }  }  #endif /* TCL_COMPILE_DEBUG */ @@ -3532,7 +9577,7 @@ PrintByteCodeInfo(codePtr)   *   * ValidatePcAndStackTop --   * - *	This procedure is called by TclExecuteByteCode when debugging to + *	This procedure is called by TclNRExecuteByteCode when debugging to   *	verify that the program counter and stack top are valid during   *	execution.   * @@ -3540,60 +9585,62 @@ PrintByteCodeInfo(codePtr)   *	None.   *   * Side effects: - *	Prints a message to stderr and panics if either the pc or stack - *	top are invalid. + *	Prints a message to stderr and panics if either the pc or stack top + *	are invalid.   *   *----------------------------------------------------------------------   */  #ifdef TCL_COMPILE_DEBUG  static void -ValidatePcAndStackTop(codePtr, pc, stackTop, stackLowerBound, -        stackUpperBound) -    register ByteCode *codePtr; /* The bytecode whose summary is printed -				 * to stdout. */ -    unsigned char *pc;		/* Points to first byte of a bytecode +ValidatePcAndStackTop( +    register ByteCode *codePtr,	/* The bytecode whose summary is printed to +				 * stdout. */ +    const unsigned char *pc,	/* Points to first byte of a bytecode  				 * instruction. The program counter. */ -    int stackTop;		/* Current stack top. Must be between +    int stackTop,		/* Current stack top. Must be between  				 * stackLowerBound and stackUpperBound  				 * (inclusive). */ -    int stackLowerBound;	/* Smallest legal value for stackTop. */ -    int stackUpperBound;	/* Greatest legal value for stackTop. */ +    int checkStack)		/* 0 if the stack depth check should be +				 * skipped. */  { -    unsigned int relativePc = (unsigned int) (pc - codePtr->codeStart); -    unsigned int codeStart = (unsigned int) codePtr->codeStart; -    unsigned int codeEnd = (unsigned int) +    int stackUpperBound = codePtr->maxStackDepth; +				/* Greatest legal value for stackTop. */ +    unsigned relativePc = (unsigned) (pc - codePtr->codeStart); +    unsigned long codeStart = (unsigned long) codePtr->codeStart; +    unsigned long codeEnd = (unsigned long)  	    (codePtr->codeStart + codePtr->numCodeBytes);      unsigned char opCode = *pc; -    if (((unsigned int) pc < codeStart) || ((unsigned int) pc > codeEnd)) { -	fprintf(stderr, "\nBad instruction pc 0x%x in TclExecuteByteCode\n", -		(unsigned int) pc); -	panic("TclExecuteByteCode execution failure: bad pc"); +    if (((unsigned long) pc < codeStart) || ((unsigned long) pc > codeEnd)) { +	fprintf(stderr, "\nBad instruction pc 0x%p in TclNRExecuteByteCode\n", +		pc); +	Tcl_Panic("TclNRExecuteByteCode execution failure: bad pc");      } -    if ((unsigned int) opCode > LAST_INST_OPCODE) { -	fprintf(stderr, "\nBad opcode %d at pc %u in TclExecuteByteCode\n", -		(unsigned int) opCode, relativePc); -	panic("TclExecuteByteCode execution failure: bad opcode"); +    if ((unsigned) opCode > LAST_INST_OPCODE) { +	fprintf(stderr, "\nBad opcode %d at pc %u in TclNRExecuteByteCode\n", +		(unsigned) opCode, relativePc); +	Tcl_Panic("TclNRExecuteByteCode execution failure: bad opcode");      } -    if ((stackTop < stackLowerBound) || (stackTop > stackUpperBound)) { +    if (checkStack &&  +	    ((stackTop < 0) || (stackTop > stackUpperBound))) {  	int numChars; -	char *cmd = GetSrcInfoForPc(pc, codePtr, &numChars); -	char *ellipsis = ""; -	 -	fprintf(stderr, "\nBad stack top %d at pc %u in TclExecuteByteCode", -		stackTop, relativePc); +	const char *cmd = GetSrcInfoForPc(pc, codePtr, &numChars, NULL, NULL); + +	fprintf(stderr, "\nBad stack top %d at pc %u in TclNRExecuteByteCode (min 0, max %i)", +		stackTop, relativePc, stackUpperBound);  	if (cmd != NULL) { -	    if (numChars > 100) { -		numChars = 100; -		ellipsis = "..."; -	    } -	    fprintf(stderr, "\n executing %.*s%s\n", numChars, cmd, -		    ellipsis); +	    Tcl_Obj *message; + +	    TclNewLiteralStringObj(message, "\n executing "); +	    Tcl_IncrRefCount(message); +	    Tcl_AppendLimitedToObj(message, cmd, numChars, 100, NULL); +	    fprintf(stderr,"%s\n", Tcl_GetString(message)); +	    Tcl_DecrRefCount(message);  	} else {  	    fprintf(stderr, "\n");  	} -	panic("TclExecuteByteCode execution failure: bad stack top"); +	Tcl_Panic("TclNRExecuteByteCode execution failure: bad stack top");      }  }  #endif /* TCL_COMPILE_DEBUG */ @@ -3603,167 +9650,188 @@ ValidatePcAndStackTop(codePtr, pc, stackTop, stackLowerBound,   *   * IllegalExprOperandType --   * - *	Used by TclExecuteByteCode to add an error message to errorInfo - *	when an illegal operand type is detected by an expression + *	Used by TclNRExecuteByteCode to append an error message to the interp + *	result when an illegal operand type is detected by an expression   *	instruction. The argument opndPtr holds the operand object in error.   *   * Results:   *	None.   *   * Side effects: - *	An error message is appended to errorInfo. + *	An error message is appended to the interp result.   *   *----------------------------------------------------------------------   */  static void -IllegalExprOperandType(interp, pc, opndPtr) -    Tcl_Interp *interp;		/* Interpreter to which error information +IllegalExprOperandType( +    Tcl_Interp *interp,		/* Interpreter to which error information  				 * pertains. */ -    unsigned char *pc;		/* Points to the instruction being executed +    const unsigned char *pc, /* Points to the instruction being executed  				 * when the illegal type was found. */ -    Tcl_Obj *opndPtr;		/* Points to the operand holding the value +    Tcl_Obj *opndPtr)		/* Points to the operand holding the value  				 * with the illegal type. */  { -    unsigned char opCode = *pc; -     -    Tcl_ResetResult(interp); -    if ((opndPtr->bytes == NULL) || (opndPtr->length == 0)) { -	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -		"can't use empty string as operand of \"", -		operatorStrings[opCode - INST_LOR], "\"", (char *) NULL); -    } else { -	char *msg = "non-numeric string"; -	if (opndPtr->typePtr != &tclDoubleType) { -	    /* -	     * See if the operand can be interpreted as a double in order to -	     * improve the error message. -	     */ +    ClientData ptr; +    int type; +    const unsigned char opcode = *pc; +    const char *description, *operator = "unknown"; + +    if (opcode == INST_EXPON) { +	operator = "**"; +    } else if (opcode <= INST_STR_NEQ) { +	operator = operatorStrings[opcode - INST_LOR]; +    } -	    char *s = Tcl_GetString(opndPtr); -	    double d; +    if (GetNumberFromObj(NULL, opndPtr, &ptr, &type) != TCL_OK) { +	int numBytes; +	const char *bytes = Tcl_GetStringFromObj(opndPtr, &numBytes); -	    if (Tcl_GetDouble((Tcl_Interp *) NULL, s, &d) == TCL_OK) { -		/* -		 * Make sure that what appears to be a double -		 * (ie 08) isn't really a bad octal -		 */ -		if (TclCheckBadOctal(NULL, Tcl_GetString(opndPtr))) { -		    msg = "invalid octal number"; -		} else { -		    msg = "floating-point value"; -		} -	    } +	if (numBytes == 0) { +	    description = "empty string"; +	} else if (TclCheckBadOctal(NULL, bytes)) { +	    description = "invalid octal number"; +	} else { +	    description = "non-numeric string";  	} -	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "can't use ", -		msg, " as operand of \"", operatorStrings[opCode - INST_LOR], -		"\"", (char *) NULL); +    } else if (type == TCL_NUMBER_NAN) { +	description = "non-numeric floating-point value"; +    } else if (type == TCL_NUMBER_DOUBLE) { +	description = "floating-point value"; +    } else { +	/* TODO: No caller needs this. Eliminate? */ +	description = "(big) integer";      } + +    Tcl_SetObjResult(interp, Tcl_ObjPrintf( +	    "can't use %s as operand of \"%s\"", description, operator)); +    Tcl_SetErrorCode(interp, "ARITH", "DOMAIN", description, NULL);  }  /*   *----------------------------------------------------------------------   * - * CallTraceProcedure -- + * TclGetSrcInfoForPc, GetSrcInfoForPc, TclGetSourceFromFrame --   * - *	Invokes a trace procedure registered with an interpreter. These - *	procedures trace command execution. Currently this trace procedure - *	is called with the address of the string-based Tcl_CmdProc for the - *	command, not the Tcl_ObjCmdProc. + *	Given a program counter value, finds the closest command in the + *	bytecode code unit's CmdLocation array and returns information about + *	that command's source: a pointer to its first byte and the number of + *	characters.   *   * Results: - *	None. + *	If a command is found that encloses the program counter value, a + *	pointer to the command's source is returned and the length of the + *	source is stored at *lengthPtr. If multiple commands resulted in code + *	at pc, information about the closest enclosing command is returned. If + *	no matching command is found, NULL is returned and *lengthPtr is + *	unchanged.   *   * Side effects: - *	Those side effects made by the trace procedure. + *	The CmdFrame at *cfPtr is updated.   *   *----------------------------------------------------------------------   */ -static void -CallTraceProcedure(interp, tracePtr, cmdPtr, command, numChars, objc, objv) -    Tcl_Interp *interp;		/* The current interpreter. */ -    register Trace *tracePtr;	/* Describes the trace procedure to call. */ -    Command *cmdPtr;		/* Points to command's Command struct. */ -    char *command;		/* Points to the first character of the -				 * command's source before substitutions. */ -    int numChars;		/* The number of characters in the -				 * command's source. */ -    register int objc;		/* Number of arguments for the command. */ -    Tcl_Obj *objv[];		/* Pointers to Tcl_Obj of each argument. */ +Tcl_Obj * +TclGetSourceFromFrame( +    CmdFrame *cfPtr, +    int objc, +    Tcl_Obj *const objv[])  { -    Interp *iPtr = (Interp *) interp; -    register char **argv; -    register int i; -    int length; -    char *p; +    if (cfPtr == NULL) { +        return Tcl_NewListObj(objc, objv); +    } +    if (cfPtr->cmdObj == NULL) { +        if (cfPtr->cmd == NULL) { +	    ByteCode *codePtr = (ByteCode *) cfPtr->data.tebc.codePtr; -    /* -     * Get the string rep from the objv argument objects and place their -     * pointers in argv. First make sure argv is large enough to hold the -     * objc args plus 1 extra word for the zero end-of-argv word. -     */ -     -    argv = (char **) ckalloc((unsigned)(objc + 1) * sizeof(char *)); -    for (i = 0;  i < objc;  i++) { -	argv[i] = Tcl_GetStringFromObj(objv[i], &length); +            cfPtr->cmd = GetSrcInfoForPc((unsigned char *) +		    cfPtr->data.tebc.pc, codePtr, &cfPtr->len, NULL, NULL); +        } +        cfPtr->cmdObj = Tcl_NewStringObj(cfPtr->cmd, cfPtr->len); +        Tcl_IncrRefCount(cfPtr->cmdObj);      } -    argv[objc] = 0; +    return cfPtr->cmdObj; +} -    /* -     * Copy the command characters into a new string. -     */ +void +TclGetSrcInfoForPc( +    CmdFrame *cfPtr) +{ +    ByteCode *codePtr = (ByteCode *) cfPtr->data.tebc.codePtr; -    p = (char *) ckalloc((unsigned) (numChars + 1)); -    memcpy((VOID *) p, (VOID *) command, (size_t) numChars); -    p[numChars] = '\0'; -     -    /* -     * Call the trace procedure then free allocated storage. -     */ -     -    (*tracePtr->proc)(tracePtr->clientData, interp, iPtr->numLevels, -                      p, cmdPtr->proc, cmdPtr->clientData, objc, argv); +    assert(cfPtr->type == TCL_LOCATION_BC); + +    if (cfPtr->cmd == NULL) { -    ckfree((char *) argv); -    ckfree((char *) p); +	cfPtr->cmd = GetSrcInfoForPc( +		(unsigned char *) cfPtr->data.tebc.pc, codePtr, +		&cfPtr->len, NULL, NULL); +    } + +    if (cfPtr->cmd != NULL) { +	/* +	 * We now have the command. We can get the srcOffset back and from +	 * there find the list of word locations for this command. +	 */ + +	ExtCmdLoc *eclPtr; +	ECL *locPtr = NULL; +	int srcOffset, i; +	Interp *iPtr = (Interp *) *codePtr->interpHandle; +	Tcl_HashEntry *hePtr = +		Tcl_FindHashEntry(iPtr->lineBCPtr, codePtr); + +	if (!hePtr) { +	    return; +	} + +	srcOffset = cfPtr->cmd - codePtr->source; +	eclPtr = Tcl_GetHashValue(hePtr); + +	for (i=0; i < eclPtr->nuloc; i++) { +	    if (eclPtr->loc[i].srcOffset == srcOffset) { +		locPtr = eclPtr->loc+i; +		break; +	    } +	} +	if (locPtr == NULL) { +	    Tcl_Panic("LocSearch failure"); +	} + +	cfPtr->line = locPtr->line; +	cfPtr->nline = locPtr->nline; +	cfPtr->type = eclPtr->type; + +	if (eclPtr->type == TCL_LOCATION_SOURCE) { +	    cfPtr->data.eval.path = eclPtr->path; +	    Tcl_IncrRefCount(cfPtr->data.eval.path); +	} + +	/* +	 * Do not set cfPtr->data.eval.path NULL for non-SOURCE. Needed for +	 * cfPtr->data.tebc.codePtr. +	 */ +    }  } - -/* - *---------------------------------------------------------------------- - * - * GetSrcInfoForPc -- - * - *	Given a program counter value, finds the closest command in the - *	bytecode code unit's CmdLocation array and returns information about - *	that command's source: a pointer to its first byte and the number of - *	characters. - * - * Results: - *	If a command is found that encloses the program counter value, a - *	pointer to the command's source is returned and the length of the - *	source is stored at *lengthPtr. If multiple commands resulted in - *	code at pc, information about the closest enclosing command is - *	returned. If no matching command is found, NULL is returned and - *	*lengthPtr is unchanged. - * - * Side effects: - *	None. - * - *---------------------------------------------------------------------- - */ -static char * -GetSrcInfoForPc(pc, codePtr, lengthPtr) -    unsigned char *pc;		/* The program counter value for which to +static const char * +GetSrcInfoForPc( +    const unsigned char *pc,	/* The program counter value for which to  				 * return the closest command's source info. -				 * This points to a bytecode instruction +				 * This points within a bytecode instruction  				 * in codePtr's code. */ -    ByteCode *codePtr;		/* The bytecode sequence in which to look -				 * up the command source for the pc. */ -    int *lengthPtr;		/* If non-NULL, the location where the -				 * length of the command's source should be -				 * stored. If NULL, no length is stored. */ +    ByteCode *codePtr,		/* The bytecode sequence in which to look up +				 * the command source for the pc. */ +    int *lengthPtr,		/* If non-NULL, the location where the length +				 * of the command's source should be stored. +				 * If NULL, no length is stored. */ +    const unsigned char **pcBeg,/* If non-NULL, the bytecode location +				 * where the current instruction starts. +				 * If NULL; no pointer is stored. */ +    int *cmdIdxPtr)		/* If non-NULL, the location where the index +				 * of the command containing the pc should  +				 * be stored. */  {      register int pcOffset = (pc - codePtr->codeStart);      int numCmds = codePtr->numCommands; @@ -3773,8 +9841,10 @@ GetSrcInfoForPc(pc, codePtr, lengthPtr)      int bestDist = INT_MAX;	/* Distance of pc to best cmd's start pc. */      int bestSrcOffset = -1;	/* Initialized to avoid compiler warning. */      int bestSrcLength = -1;	/* Initialized to avoid compiler warning. */ +    int bestCmdIdx = -1;      if ((pcOffset < 0) || (pcOffset >= codePtr->numCodeBytes)) { +	if (pcBeg != NULL) *pcBeg = NULL;  	return NULL;      } @@ -3786,11 +9856,11 @@ GetSrcInfoForPc(pc, codePtr, lengthPtr)      codeDeltaNext = codePtr->codeDeltaStart;      codeLengthNext = codePtr->codeLengthStart; -    srcDeltaNext  = codePtr->srcDeltaStart; +    srcDeltaNext = codePtr->srcDeltaStart;      srcLengthNext = codePtr->srcLengthStart;      codeOffset = srcOffset = 0;      for (i = 0;  i < numCmds;  i++) { -	if ((unsigned int) (*codeDeltaNext) == (unsigned int) 0xFF) { +	if ((unsigned) *codeDeltaNext == (unsigned) 0xFF) {  	    codeDeltaNext++;  	    delta = TclGetInt4AtPtr(codeDeltaNext);  	    codeDeltaNext += 4; @@ -3800,7 +9870,7 @@ GetSrcInfoForPc(pc, codePtr, lengthPtr)  	}  	codeOffset += delta; -	if ((unsigned int) (*codeLengthNext) == (unsigned int) 0xFF) { +	if ((unsigned) *codeLengthNext == (unsigned) 0xFF) {  	    codeLengthNext++;  	    codeLen = TclGetInt4AtPtr(codeLengthNext);  	    codeLengthNext += 4; @@ -3810,7 +9880,7 @@ GetSrcInfoForPc(pc, codePtr, lengthPtr)  	}  	codeEnd = (codeOffset + codeLen - 1); -	if ((unsigned int) (*srcDeltaNext) == (unsigned int) 0xFF) { +	if ((unsigned) *srcDeltaNext == (unsigned) 0xFF) {  	    srcDeltaNext++;  	    delta = TclGetInt4AtPtr(srcDeltaNext);  	    srcDeltaNext += 4; @@ -3820,7 +9890,7 @@ GetSrcInfoForPc(pc, codePtr, lengthPtr)  	}  	srcOffset += delta; -	if ((unsigned int) (*srcLengthNext) == (unsigned int) 0xFF) { +	if ((unsigned) *srcLengthNext == (unsigned) 0xFF) {  	    srcLengthNext++;  	    srcLen = TclGetInt4AtPtr(srcLengthNext);  	    srcLengthNext += 4; @@ -3828,26 +9898,51 @@ GetSrcInfoForPc(pc, codePtr, lengthPtr)  	    srcLen = TclGetInt1AtPtr(srcLengthNext);  	    srcLengthNext++;  	} -	 -	if (codeOffset > pcOffset) {      /* best cmd already found */ + +	if (codeOffset > pcOffset) {	/* Best cmd already found */  	    break; -	} else if (pcOffset <= codeEnd) { /* this cmd's code encloses pc */ +	} +	if (pcOffset <= codeEnd) {	/* This cmd's code encloses pc */  	    int dist = (pcOffset - codeOffset); +  	    if (dist <= bestDist) {  		bestDist = dist;  		bestSrcOffset = srcOffset;  		bestSrcLength = srcLen; +		bestCmdIdx = i;  	    }  	}      } +    if (pcBeg != NULL) { +	const unsigned char *curr, *prev; + +	/* +	 * Walk from beginning of command or BC to pc, by complete +	 * instructions. Stop when crossing pc; keep previous. +	 */ + +	curr = ((bestDist == INT_MAX) ? codePtr->codeStart : pc - bestDist); +	prev = curr; +	while (curr <= pc) { +	    prev = curr; +	    curr += tclInstructionTable[*curr].numBytes; +	} +	*pcBeg = prev; +    } +      if (bestDist == INT_MAX) {  	return NULL;      } -     +      if (lengthPtr != NULL) {  	*lengthPtr = bestSrcLength;      } + +    if (cmdIdxPtr != NULL) { +	*cmdIdxPtr = bestCmdIdx; +    } +      return (codePtr->source + bestSrcOffset);  } @@ -3860,15 +9955,14 @@ GetSrcInfoForPc(pc, codePtr, lengthPtr)   *	ExceptionRange.   *   * Results: - *	In the normal case, catchOnly is 0 (false) and this procedure - *	returns a pointer to the most closely enclosing ExceptionRange - *	structure regardless of whether it is a loop or catch exception - *	range. This is appropriate when processing a TCL_BREAK or - *	TCL_CONTINUE, which will be "handled" either by a loop exception - *	range or a closer catch range. If catchOnly is nonzero, this - *	procedure ignores loop exception ranges and returns a pointer to the - *	closest catch range. If no matching ExceptionRange is found that - *	encloses pc, a NULL is returned. + *	In the normal case, catchOnly is 0 (false) and this procedure returns + *	a pointer to the most closely enclosing ExceptionRange structure + *	regardless of whether it is a loop or catch exception range. This is + *	appropriate when processing a TCL_BREAK or TCL_CONTINUE, which will be + *	"handled" either by a loop exception range or a closer catch range. If + *	catchOnly is nonzero, this procedure ignores loop exception ranges and + *	returns a pointer to the closest catch range. If no matching + *	ExceptionRange is found that encloses pc, a NULL is returned.   *   * Side effects:   *	None. @@ -3877,41 +9971,43 @@ GetSrcInfoForPc(pc, codePtr, lengthPtr)   */  static ExceptionRange * -GetExceptRangeForPc(pc, catchOnly, codePtr) -    unsigned char *pc;		/* The program counter value for which to +GetExceptRangeForPc( +    const unsigned char *pc, /* The program counter value for which to  				 * search for a closest enclosing exception  				 * range. This points to a bytecode  				 * instruction in codePtr's code. */ -    int catchOnly;		/* If 0, consider either loop or catch +    int catchOnly,		/* If 0, consider either loop or catch  				 * ExceptionRanges in search. If nonzero -				 * consider only catch ranges (and ignore -				 * any closer loop ranges). */ -    ByteCode* codePtr;		/* Points to the ByteCode in which to search +				 * consider only catch ranges (and ignore any +				 * closer loop ranges). */ +    ByteCode *codePtr)		/* Points to the ByteCode in which to search  				 * for the enclosing ExceptionRange. */  {      ExceptionRange *rangeArrayPtr;      int numRanges = codePtr->numExceptRanges;      register ExceptionRange *rangePtr; -    int pcOffset = (pc - codePtr->codeStart); -    register int i, level; +    int pcOffset = pc - codePtr->codeStart; +    register int start;      if (numRanges == 0) {  	return NULL;      } -    rangeArrayPtr = codePtr->exceptArrayPtr; -    for (level = codePtr->maxExceptDepth;  level >= 0;  level--) { -	for (i = 0;  i < numRanges;  i++) { -	    rangePtr = &(rangeArrayPtr[i]); -	    if (rangePtr->nestingLevel == level) { -		int start = rangePtr->codeOffset; -		int end   = (start + rangePtr->numCodeBytes); -		if ((start <= pcOffset) && (pcOffset < end)) { -		    if ((!catchOnly) -			    || (rangePtr->type == CATCH_EXCEPTION_RANGE)) { -			return rangePtr; -		    } -		} +    /* +     * This exploits peculiarities of our compiler: nested ranges are always +     * *after* their containing ranges, so that by scanning backwards we are +     * sure that the first matching range is indeed the deepest. +     */ + +    rangeArrayPtr = codePtr->exceptArrayPtr; +    rangePtr = rangeArrayPtr + numRanges; +    while (--rangePtr >= rangeArrayPtr) { +	start = rangePtr->codeOffset; +	if ((start <= pcOffset) && +		(pcOffset < (start + rangePtr->numCodeBytes))) { +	    if ((!catchOnly) +		    || (rangePtr->type == CATCH_EXCEPTION_RANGE)) { +		return rangePtr;  	    }  	}      } @@ -3923,9 +10019,9 @@ GetExceptRangeForPc(pc, catchOnly, codePtr)   *   * GetOpcodeName --   * - *	This procedure is called by the TRACE and TRACE_WITH_OBJ macros - *	used in TclExecuteByteCode when debugging. It returns the name of - *	the bytecode instruction at a specified instruction pc. + *	This procedure is called by the TRACE and TRACE_WITH_OBJ macros used + *	in TclNRExecuteByteCode when debugging. It returns the name of the + *	bytecode instruction at a specified instruction pc.   *   * Results:   *	A character string for the instruction. @@ -3937,871 +10033,24 @@ GetExceptRangeForPc(pc, catchOnly, codePtr)   */  #ifdef TCL_COMPILE_DEBUG -static char * -GetOpcodeName(pc) -    unsigned char *pc;		/* Points to the instruction whose name -				 * should be returned. */ +static const char * +GetOpcodeName( +    const unsigned char *pc)	/* Points to the instruction whose name should +				 * be returned. */  {      unsigned char opCode = *pc; -     -    return instructionTable[opCode].name; -} -#endif /* TCL_COMPILE_DEBUG */ - -/* - *---------------------------------------------------------------------- - * - * VerifyExprObjType -- - * - *	This procedure is called by the math functions to verify that - *	the object is either an int or double, coercing it if necessary. - *	If an error occurs during conversion, an error message is left - *	in the interpreter's result unless "interp" is NULL. - * - * Results: - *	TCL_OK if it was int or double, TCL_ERROR otherwise - * - * Side effects: - *	objPtr is ensured to be either tclIntType of tclDoubleType. - * - *---------------------------------------------------------------------- - */ - -static int -VerifyExprObjType(interp, objPtr) -    Tcl_Interp *interp;		/* The interpreter in which to execute the -				 * function. */ -    Tcl_Obj *objPtr;		/* Points to the object to type check. */ -{ -    if ((objPtr->typePtr == &tclIntType) || -	    (objPtr->typePtr == &tclDoubleType)) { -	return TCL_OK; -    } else { -	int length, result = TCL_OK; -	char *s = Tcl_GetStringFromObj(objPtr, &length); -	 -	if (TclLooksLikeInt(s, length)) { -	    long i; -	    result = Tcl_GetLongFromObj((Tcl_Interp *) NULL, objPtr, &i); -	} else { -	    double d; -	    result = Tcl_GetDoubleFromObj((Tcl_Interp *) NULL, objPtr, &d); -	} -	if ((result != TCL_OK) && (interp != NULL)) { -	    Tcl_ResetResult(interp); -	    if (TclCheckBadOctal((Tcl_Interp *) NULL, s)) { -		Tcl_AppendToObj(Tcl_GetObjResult(interp), -			"argument to math function was an invalid octal number", -			-1); -	    } else { -		Tcl_AppendToObj(Tcl_GetObjResult(interp), -			"argument to math function didn't have numeric value", -			-1); -	    } -	} -	return result; -    } -} - -/* - *---------------------------------------------------------------------- - * - * Math Functions -- - * - *	This page contains the procedures that implement all of the - *	built-in math functions for expressions. - * - * Results: - *	Each procedure returns TCL_OK if it succeeds and pushes an - *	Tcl object holding the result. If it fails it returns TCL_ERROR - *	and leaves an error message in the interpreter's result. - * - * Side effects: - *	None. - * - *---------------------------------------------------------------------- - */ - -static int -ExprUnaryFunc(interp, eePtr, clientData) -    Tcl_Interp *interp;		/* The interpreter in which to execute the -				 * function. */ -    ExecEnv *eePtr;		/* Points to the environment for executing -				 * the function. */ -    ClientData clientData;	/* Contains the address of a procedure that -				 * takes one double argument and returns a -				 * double result. */ -{ -    Tcl_Obj **stackPtr;		/* Cached evaluation stack base pointer. */ -    register int stackTop;	/* Cached top index of evaluation stack. */ -    register Tcl_Obj *valuePtr; -    double d, dResult; -    int result; -     -    double (*func) _ANSI_ARGS_((double)) = -	(double (*)_ANSI_ARGS_((double))) clientData; - -    /* -     * Set stackPtr and stackTop from eePtr. -     */ - -    result = TCL_OK; -    CACHE_STACK_INFO(); - -    /* -     * Pop the function's argument from the evaluation stack. Convert it -     * to a double if necessary. -     */ - -    valuePtr = POP_OBJECT(); -    if (VerifyExprObjType(interp, valuePtr) != TCL_OK) { -	result = TCL_ERROR; -	goto done; -    } -     -    if (valuePtr->typePtr == &tclIntType) { -	d = (double) valuePtr->internalRep.longValue; -    } else { -	d = valuePtr->internalRep.doubleValue; -    } - -    errno = 0; -    dResult = (*func)(d); -    if ((errno != 0) || IS_NAN(dResult) || IS_INF(dResult)) { -	TclExprFloatError(interp, dResult); -	result = TCL_ERROR; -	goto done; -    } -     -    /* -     * Push a Tcl object holding the result. -     */ - -    PUSH_OBJECT(Tcl_NewDoubleObj(dResult)); -     -    /* -     * Reflect the change to stackTop back in eePtr. -     */ - -    done: -    Tcl_DecrRefCount(valuePtr); -    DECACHE_STACK_INFO(); -    return result; -} - -static int -ExprBinaryFunc(interp, eePtr, clientData) -    Tcl_Interp *interp;		/* The interpreter in which to execute the -				 * function. */ -    ExecEnv *eePtr;		/* Points to the environment for executing -				 * the function. */ -    ClientData clientData;	/* Contains the address of a procedure that -				 * takes two double arguments and -				 * returns a double result. */ -{ -    Tcl_Obj **stackPtr;        /* Cached evaluation stack base pointer. */ -    register int stackTop;	/* Cached top index of evaluation stack. */ -    register Tcl_Obj *valuePtr, *value2Ptr; -    double d1, d2, dResult; -    int result; -     -    double (*func) _ANSI_ARGS_((double, double)) -	= (double (*)_ANSI_ARGS_((double, double))) clientData; - -    /* -     * Set stackPtr and stackTop from eePtr. -     */ - -    result = TCL_OK; -    CACHE_STACK_INFO(); - -    /* -     * Pop the function's two arguments from the evaluation stack. Convert -     * them to doubles if necessary. -     */ - -    value2Ptr = POP_OBJECT(); -    valuePtr  = POP_OBJECT(); - -    if ((VerifyExprObjType(interp, valuePtr) != TCL_OK) || -	    (VerifyExprObjType(interp, value2Ptr) != TCL_OK)) { -	result = TCL_ERROR; -	goto done; -    } - -    if (valuePtr->typePtr == &tclIntType) { -	d1 = (double) valuePtr->internalRep.longValue; -    } else { -	d1 = valuePtr->internalRep.doubleValue; -    } - -    if (value2Ptr->typePtr == &tclIntType) { -	d2 = (double) value2Ptr->internalRep.longValue; -    } else { -	d2 = value2Ptr->internalRep.doubleValue; -    } - -    errno = 0; -    dResult = (*func)(d1, d2); -    if ((errno != 0) || IS_NAN(dResult) || IS_INF(dResult)) { -	TclExprFloatError(interp, dResult); -	result = TCL_ERROR; -	goto done; -    } - -    /* -     * Push a Tcl object holding the result. -     */ - -    PUSH_OBJECT(Tcl_NewDoubleObj(dResult)); -     -    /* -     * Reflect the change to stackTop back in eePtr. -     */ - -    done: -    Tcl_DecrRefCount(valuePtr); -    Tcl_DecrRefCount(value2Ptr); -    DECACHE_STACK_INFO(); -    return result; -} - -static int -ExprAbsFunc(interp, eePtr, clientData) -    Tcl_Interp *interp;		/* The interpreter in which to execute the -				 * function. */ -    ExecEnv *eePtr;		/* Points to the environment for executing -				 * the function. */ -    ClientData clientData;	/* Ignored. */ -{ -    Tcl_Obj **stackPtr;        /* Cached evaluation stack base pointer. */ -    register int stackTop;	/* Cached top index of evaluation stack. */ -    register Tcl_Obj *valuePtr; -    long i, iResult; -    double d, dResult; -    int result; - -    /* -     * Set stackPtr and stackTop from eePtr. -     */ - -    result = TCL_OK; -    CACHE_STACK_INFO(); - -    /* -     * Pop the argument from the evaluation stack. -     */ - -    valuePtr = POP_OBJECT(); - -    if (VerifyExprObjType(interp, valuePtr) != TCL_OK) { -	result = TCL_ERROR; -	goto done; -    } - -    /* -     * Push a Tcl object with the result. -     */ -    if (valuePtr->typePtr == &tclIntType) { -	i = valuePtr->internalRep.longValue; -	if (i < 0) { -	    iResult = -i; -	    if (iResult < 0) { -		Tcl_ResetResult(interp); -		Tcl_AppendToObj(Tcl_GetObjResult(interp), -		        "integer value too large to represent", -1); -		Tcl_SetErrorCode(interp, "ARITH", "IOVERFLOW", -			"integer value too large to represent", (char *) NULL); -		result = TCL_ERROR; -		goto done; -	    } -	} else { -	    iResult = i; -	}	     -	PUSH_OBJECT(Tcl_NewLongObj(iResult)); -    } else { -	d = valuePtr->internalRep.doubleValue; -	if (d < 0.0) { -	    dResult = -d; -	} else { -	    dResult = d; -	} -	if (IS_NAN(dResult) || IS_INF(dResult)) { -	    TclExprFloatError(interp, dResult); -	    result = TCL_ERROR; -	    goto done; -	} -	PUSH_OBJECT(Tcl_NewDoubleObj(dResult)); -    } - -    /* -     * Reflect the change to stackTop back in eePtr. -     */ - -    done: -    Tcl_DecrRefCount(valuePtr); -    DECACHE_STACK_INFO(); -    return result; -} - -static int -ExprDoubleFunc(interp, eePtr, clientData) -    Tcl_Interp *interp;		/* The interpreter in which to execute the -				 * function. */ -    ExecEnv *eePtr;		/* Points to the environment for executing -				 * the function. */ -    ClientData clientData;	/* Ignored. */ -{ -    Tcl_Obj **stackPtr;        /* Cached evaluation stack base pointer. */ -    register int stackTop;	/* Cached top index of evaluation stack. */ -    register Tcl_Obj *valuePtr; -    double dResult; -    int result; - -    /* -     * Set stackPtr and stackTop from eePtr. -     */ - -    result = TCL_OK; -    CACHE_STACK_INFO(); - -    /* -     * Pop the argument from the evaluation stack. -     */ - -    valuePtr = POP_OBJECT(); - -    if (VerifyExprObjType(interp, valuePtr) != TCL_OK) { -	result = TCL_ERROR; -	goto done; -    } - -    if (valuePtr->typePtr == &tclIntType) { -	dResult = (double) valuePtr->internalRep.longValue; -    } else { -	dResult = valuePtr->internalRep.doubleValue; -    } - -    /* -     * Push a Tcl object with the result. -     */ - -    PUSH_OBJECT(Tcl_NewDoubleObj(dResult)); - -    /* -     * Reflect the change to stackTop back in eePtr. -     */ - -    done: -    Tcl_DecrRefCount(valuePtr); -    DECACHE_STACK_INFO(); -    return result; -} - -static int -ExprIntFunc(interp, eePtr, clientData) -    Tcl_Interp *interp;		/* The interpreter in which to execute the -				 * function. */ -    ExecEnv *eePtr;		/* Points to the environment for executing -				 * the function. */ -    ClientData clientData;	/* Ignored. */ -{ -    Tcl_Obj **stackPtr;        /* Cached evaluation stack base pointer. */ -    register int stackTop;	/* Cached top index of evaluation stack. */ -    register Tcl_Obj *valuePtr; -    long iResult; -    double d; -    int result; - -    /* -     * Set stackPtr and stackTop from eePtr. -     */ - -    result = TCL_OK; -    CACHE_STACK_INFO(); - -    /* -     * Pop the argument from the evaluation stack. -     */ - -    valuePtr = POP_OBJECT(); -     -    if (VerifyExprObjType(interp, valuePtr) != TCL_OK) { -	result = TCL_ERROR; -	goto done; -    } -     -    if (valuePtr->typePtr == &tclIntType) { -	iResult = valuePtr->internalRep.longValue; -    } else { -	d = valuePtr->internalRep.doubleValue; -	if (d < 0.0) { -	    if (d < (double) (long) LONG_MIN) { -		tooLarge: -		Tcl_ResetResult(interp); -		Tcl_AppendToObj(Tcl_GetObjResult(interp), -		        "integer value too large to represent", -1); -		Tcl_SetErrorCode(interp, "ARITH", "IOVERFLOW", -			"integer value too large to represent", (char *) NULL); -		result = TCL_ERROR; -		goto done; -	    } -	} else { -	    if (d > (double) LONG_MAX) { -		goto tooLarge; -	    } -	} -	if (IS_NAN(d) || IS_INF(d)) { -	    TclExprFloatError(interp, d); -	    result = TCL_ERROR; -	    goto done; -	} -	iResult = (long) d; -    } - -    /* -     * Push a Tcl object with the result. -     */ -     -    PUSH_OBJECT(Tcl_NewLongObj(iResult)); - -    /* -     * Reflect the change to stackTop back in eePtr. -     */ - -    done: -    Tcl_DecrRefCount(valuePtr); -    DECACHE_STACK_INFO(); -    return result; -} - -static int -ExprRandFunc(interp, eePtr, clientData) -    Tcl_Interp *interp;		/* The interpreter in which to execute the -				 * function. */ -    ExecEnv *eePtr;		/* Points to the environment for executing -				 * the function. */ -    ClientData clientData;	/* Ignored. */ -{ -    Tcl_Obj **stackPtr;        /* Cached evaluation stack base pointer. */ -    register int stackTop;	/* Cached top index of evaluation stack. */ -    Interp *iPtr = (Interp *) interp; -    double dResult; -    long tmp;			/* Algorithm assumes at least 32 bits. -				 * Only long guarantees that.  See below. */ - -    if (!(iPtr->flags & RAND_SEED_INITIALIZED)) { -	iPtr->flags |= RAND_SEED_INITIALIZED; -         -        /*  -	 * Take into consideration the thread this interp is running in order -	 * to insure different seeds in different threads (bug #416643) -	 */ - -	iPtr->randSeed = TclpGetClicks() + ((long) Tcl_GetCurrentThread() << 12); - -	/* -	 * Make sure 1 <= randSeed <= (2^31) - 2.  See below. -	 */ - -        iPtr->randSeed &= (unsigned long) 0x7fffffff; -	if ((iPtr->randSeed == 0) || (iPtr->randSeed == 0x7fffffff)) { -	    iPtr->randSeed ^= 123459876; -	} -    } -     -    /* -     * Set stackPtr and stackTop from eePtr. -     */ -     -    CACHE_STACK_INFO(); - -    /* -     * Generate the random number using the linear congruential -     * generator defined by the following recurrence: -     *		seed = ( IA * seed ) mod IM -     * where IA is 16807 and IM is (2^31) - 1.  The recurrence maps -     * a seed in the range [1, IM - 1] to a new seed in that same range. -     * The recurrence maps IM to 0, and maps 0 back to 0, so those two -     * values must not be allowed as initial values of seed. -     * -     * In order to avoid potential problems with integer overflow, the -     * recurrence is implemented in terms of additional constants -     * IQ and IR such that -     *		IM = IA*IQ + IR -     * None of the operations in the implementation overflows a 32-bit -     * signed integer, and the C type long is guaranteed to be at least -     * 32 bits wide. -     * -     * For more details on how this algorithm works, refer to the following -     * papers:  -     * -     *	S.K. Park & K.W. Miller, "Random number generators: good ones -     *	are hard to find," Comm ACM 31(10):1192-1201, Oct 1988 -     * -     *	W.H. Press & S.A. Teukolsky, "Portable random number -     *	generators," Computers in Physics 6(5):522-524, Sep/Oct 1992. -     */ - -#define RAND_IA		16807 -#define RAND_IM		2147483647 -#define RAND_IQ		127773 -#define RAND_IR		2836 -#define RAND_MASK	123459876 - -    tmp = iPtr->randSeed/RAND_IQ; -    iPtr->randSeed = RAND_IA*(iPtr->randSeed - tmp*RAND_IQ) - RAND_IR*tmp; -    if (iPtr->randSeed < 0) { -	iPtr->randSeed += RAND_IM; -    } - -    /* -     * Since the recurrence keeps seed values in the range [1, RAND_IM - 1], -     * dividing by RAND_IM yields a double in the range (0, 1). -     */ - -    dResult = iPtr->randSeed * (1.0/RAND_IM); - -    /* -     * Push a Tcl object with the result. -     */ - -    PUSH_OBJECT(Tcl_NewDoubleObj(dResult)); -     -    /* -     * Reflect the change to stackTop back in eePtr. -     */ - -    DECACHE_STACK_INFO(); -    return TCL_OK; -} - -static int -ExprRoundFunc(interp, eePtr, clientData) -    Tcl_Interp *interp;		/* The interpreter in which to execute the -				 * function. */ -    ExecEnv *eePtr;		/* Points to the environment for executing -				 * the function. */ -    ClientData clientData;	/* Ignored. */ -{ -    Tcl_Obj **stackPtr;        /* Cached evaluation stack base pointer. */ -    register int stackTop;	/* Cached top index of evaluation stack. */ -    Tcl_Obj *valuePtr; -    long iResult; -    double d, temp; -    int result; - -    /* -     * Set stackPtr and stackTop from eePtr. -     */ - -    result = TCL_OK; -    CACHE_STACK_INFO(); - -    /* -     * Pop the argument from the evaluation stack. -     */ - -    valuePtr = POP_OBJECT(); - -    if (VerifyExprObjType(interp, valuePtr) != TCL_OK) { -	result = TCL_ERROR; -	goto done; -    } -     -    if (valuePtr->typePtr == &tclIntType) { -	iResult = valuePtr->internalRep.longValue; -    } else { -	d = valuePtr->internalRep.doubleValue; -	if (d < 0.0) { -	    if (d <= (((double) (long) LONG_MIN) - 0.5)) { -		tooLarge: -		Tcl_ResetResult(interp); -		Tcl_AppendToObj(Tcl_GetObjResult(interp), -		        "integer value too large to represent", -1); -		Tcl_SetErrorCode(interp, "ARITH", "IOVERFLOW", -			"integer value too large to represent", -			(char *) NULL); -		result = TCL_ERROR; -		goto done; -	    } -	    temp = (long) (d - 0.5); -	} else { -	    if (d >= (((double) LONG_MAX + 0.5))) { -		goto tooLarge; -	    } -	    temp = (long) (d + 0.5); -	} -	if (IS_NAN(temp) || IS_INF(temp)) { -	    TclExprFloatError(interp, temp); -	    result = TCL_ERROR; -	    goto done; -	} -	iResult = (long) temp; -    } - -    /* -     * Push a Tcl object with the result. -     */ -     -    PUSH_OBJECT(Tcl_NewLongObj(iResult)); - -    /* -     * Reflect the change to stackTop back in eePtr. -     */ - -    done: -    Tcl_DecrRefCount(valuePtr); -    DECACHE_STACK_INFO(); -    return result; -} - -static int -ExprSrandFunc(interp, eePtr, clientData) -    Tcl_Interp *interp;		/* The interpreter in which to execute the -				 * function. */ -    ExecEnv *eePtr;		/* Points to the environment for executing -				 * the function. */ -    ClientData clientData;	/* Ignored. */ -{ -    Tcl_Obj **stackPtr;        /* Cached evaluation stack base pointer. */ -    register int stackTop;	/* Cached top index of evaluation stack. */ -    Interp *iPtr = (Interp *) interp; -    Tcl_Obj *valuePtr; -    long i = 0;			/* Initialized to avoid compiler warning. */ -    int result; - -    /* -     * Set stackPtr and stackTop from eePtr. -     */ -     -    CACHE_STACK_INFO(); - -    /* -     * Pop the argument from the evaluation stack.  Use the value -     * to reset the random number seed. -     */ - -    valuePtr = POP_OBJECT(); - -    if (VerifyExprObjType(interp, valuePtr) != TCL_OK) { -	result = TCL_ERROR; -	goto badValue; -    } - -    if (valuePtr->typePtr == &tclIntType) { -	i = valuePtr->internalRep.longValue; -    } else { -	/* -	 * At this point, the only other possible type is double -	 */ -	Tcl_ResetResult(interp); -	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -		"can't use floating-point value as argument to srand", -		(char *) NULL); -	badValue: -	Tcl_DecrRefCount(valuePtr); -	DECACHE_STACK_INFO(); -	return TCL_ERROR; -    } -     -    /* -     * Reset the seed.  Make sure 1 <= randSeed <= 2^31 - 2. -     * See comments in ExprRandFunc() for more details. -     */ - -    iPtr->flags |= RAND_SEED_INITIALIZED; -    iPtr->randSeed = i; -    iPtr->randSeed &= (unsigned long) 0x7fffffff; -    if ((iPtr->randSeed == 0) || (iPtr->randSeed == 0x7fffffff)) { -	iPtr->randSeed ^= 123459876; -    } - -    /* -     * To avoid duplicating the random number generation code we simply -     * clean up our state and call the real random number function. That -     * function will always succeed. -     */ -     -    Tcl_DecrRefCount(valuePtr); -    DECACHE_STACK_INFO(); - -    ExprRandFunc(interp, eePtr, clientData); -    return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * ExprCallMathFunc -- - * - *	This procedure is invoked to call a non-builtin math function - *	during the execution of an expression.  - * - * Results: - *	TCL_OK is returned if all went well and the function's value - *	was computed successfully. If an error occurred, TCL_ERROR - *	is returned and an error message is left in the interpreter's - *	result.	After a successful return this procedure pushes a Tcl object - *	holding the result.  - * - * Side effects: - *	None, unless the called math function has side effects. - * - *---------------------------------------------------------------------- - */ - -static int -ExprCallMathFunc(interp, eePtr, objc, objv) -    Tcl_Interp *interp;		/* The interpreter in which to execute the -				 * function. */ -    ExecEnv *eePtr;		/* Points to the environment for executing -				 * the function. */ -    int objc;			/* Number of arguments. The function name is -				 * the 0-th argument. */ -    Tcl_Obj **objv;		/* The array of arguments. The function name -				 * is objv[0]. */ -{ -    Interp *iPtr = (Interp *) interp; -    Tcl_Obj **stackPtr;		/* Cached evaluation stack base pointer. */ -    register int stackTop;	/* Cached top index of evaluation stack. */ -    char *funcName; -    Tcl_HashEntry *hPtr; -    MathFunc *mathFuncPtr;	/* Information about math function. */ -    Tcl_Value args[MAX_MATH_ARGS]; /* Arguments for function call. */ -    Tcl_Value funcResult;	/* Result of function call as Tcl_Value. */ -    register Tcl_Obj *valuePtr; -    long i; -    double d; -    int j, k, result; -    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - -    Tcl_ResetResult(interp); - -    /* -     * Set stackPtr and stackTop from eePtr. -     */ -     -    CACHE_STACK_INFO(); - -    /* -     * Look up the MathFunc record for the function. -     */ - -    funcName = Tcl_GetString(objv[0]); -    hPtr = Tcl_FindHashEntry(&iPtr->mathFuncTable, funcName); -    if (hPtr == NULL) { -	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -		"unknown math function \"", funcName, "\"", (char *) NULL); -	result = TCL_ERROR; -	goto done; -    } -    mathFuncPtr = (MathFunc *) Tcl_GetHashValue(hPtr); -    if (mathFuncPtr->numArgs != (objc-1)) { -	panic("ExprCallMathFunc: expected number of args %d != actual number %d", -	        mathFuncPtr->numArgs, objc); -	result = TCL_ERROR; -	goto done; -    } - -    /* -     * Collect the arguments for the function, if there are any, into the -     * array "args". Note that args[0] will have the Tcl_Value that -     * corresponds to objv[1]. -     */ - -    for (j = 1, k = 0;  j < objc;  j++, k++) { -	valuePtr = objv[j]; - -	if (VerifyExprObjType(interp, valuePtr) != TCL_OK) { -	    result = TCL_ERROR; -	    goto done; -	} - -	/* -	 * Copy the object's numeric value to the argument record, -	 * converting it if necessary.  -	 */ - -	if (valuePtr->typePtr == &tclIntType) { -	    i = valuePtr->internalRep.longValue; -	    if (mathFuncPtr->argTypes[k] == TCL_DOUBLE) { -		args[k].type = TCL_DOUBLE; -		args[k].doubleValue = i; -	    } else { -		args[k].type = TCL_INT; -		args[k].intValue = i; -	    } -	} else { -	    d = valuePtr->internalRep.doubleValue; -	    if (mathFuncPtr->argTypes[k] == TCL_INT) { -		args[k].type = TCL_INT; -		args[k].intValue = (long) d; -	    } else { -		args[k].type = TCL_DOUBLE; -		args[k].doubleValue = d; -	    } -	} -    } - -    /* -     * Invoke the function and copy its result back into valuePtr. -     */ - -    tsdPtr->mathInProgress++; -    result = (*mathFuncPtr->proc)(mathFuncPtr->clientData, interp, args, -	    &funcResult); -    tsdPtr->mathInProgress--; -    if (result != TCL_OK) { -	goto done; -    } - -    /* -     * Pop the objc top stack elements and decrement their ref counts. -     */ -		 -    i = (stackTop - (objc-1)); -    while (i <= stackTop) { -	valuePtr = stackPtr[i]; -	Tcl_DecrRefCount(valuePtr); -	i++; -    } -    stackTop -= objc; -     -    /* -     * Push the call's object result. -     */ -     -    if (funcResult.type == TCL_INT) { -	PUSH_OBJECT(Tcl_NewLongObj(funcResult.intValue)); -    } else { -	d = funcResult.doubleValue; -	if (IS_NAN(d) || IS_INF(d)) { -	    TclExprFloatError(interp, d); -	    result = TCL_ERROR; -	    goto done; -	} -	PUSH_OBJECT(Tcl_NewDoubleObj(d)); -    } - -    /* -     * Reflect the change to stackTop back in eePtr. -     */ - -    done: -    DECACHE_STACK_INFO(); -    return result; +    return tclInstructionTable[opCode].name;  } +#endif /* TCL_COMPILE_DEBUG */  /*   *----------------------------------------------------------------------   *   * TclExprFloatError --   * - *	This procedure is called when an error occurs during a - *	floating-point operation. It reads errno and sets - *	interp->objResultPtr accordingly. + *	This procedure is called when an error occurs during a floating-point + *	operation. It reads errno and sets interp->objResultPtr accordingly.   *   * Results:   *	interp->objResultPtr is set to hold an error message. @@ -4813,59 +10062,35 @@ ExprCallMathFunc(interp, eePtr, objc, objv)   */  void -TclExprFloatError(interp, value) -    Tcl_Interp *interp;		/* Where to store error message. */ -    double value;		/* Value returned after error;  used to +TclExprFloatError( +    Tcl_Interp *interp,		/* Where to store error message. */ +    double value)		/* Value returned after error; used to  				 * distinguish underflows from overflows. */  { -    char *s; +    const char *s; -    Tcl_ResetResult(interp); -    if ((errno == EDOM) || (value != value)) { +    if ((errno == EDOM) || TclIsNaN(value)) {  	s = "domain error: argument not in valid range"; -	Tcl_AppendToObj(Tcl_GetObjResult(interp), s, -1); -	Tcl_SetErrorCode(interp, "ARITH", "DOMAIN", s, (char *) NULL); -    } else if ((errno == ERANGE) || IS_INF(value)) { +	Tcl_SetObjResult(interp, Tcl_NewStringObj(s, -1)); +	Tcl_SetErrorCode(interp, "ARITH", "DOMAIN", s, NULL); +    } else if ((errno == ERANGE) || TclIsInfinite(value)) {  	if (value == 0.0) {  	    s = "floating-point value too small to represent"; -	    Tcl_AppendToObj(Tcl_GetObjResult(interp), s, -1); -	    Tcl_SetErrorCode(interp, "ARITH", "UNDERFLOW", s, (char *) NULL); +	    Tcl_SetObjResult(interp, Tcl_NewStringObj(s, -1)); +	    Tcl_SetErrorCode(interp, "ARITH", "UNDERFLOW", s, NULL);  	} else {  	    s = "floating-point value too large to represent"; -	    Tcl_AppendToObj(Tcl_GetObjResult(interp), s, -1); -	    Tcl_SetErrorCode(interp, "ARITH", "OVERFLOW", s, (char *) NULL); +	    Tcl_SetObjResult(interp, Tcl_NewStringObj(s, -1)); +	    Tcl_SetErrorCode(interp, "ARITH", "OVERFLOW", s, NULL);  	}      } else { -	char msg[64 + TCL_INTEGER_SPACE]; -	 -	sprintf(msg, "unknown floating-point error, errno = %d", errno); -	Tcl_AppendToObj(Tcl_GetObjResult(interp), msg, -1); -	Tcl_SetErrorCode(interp, "ARITH", "UNKNOWN", msg, (char *) NULL); -    } -} - -/* - *---------------------------------------------------------------------- - * - * TclMathInProgress -- - * - *	This procedure is called to find out if Tcl is doing math - *	in this thread. - * - * Results: - *	0 or 1. - * - * Side effects: - *	None. - * - *---------------------------------------------------------------------- - */ +	Tcl_Obj *objPtr = Tcl_ObjPrintf( +		"unknown floating-point error, errno = %d", errno); -int -TclMathInProgress() -{ -    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); -    return tsdPtr->mathInProgress; +	Tcl_SetErrorCode(interp, "ARITH", "UNKNOWN", +		Tcl_GetString(objPtr), NULL); +	Tcl_SetObjResult(interp, objPtr); +    }  }  #ifdef TCL_COMPILE_STATS @@ -4878,8 +10103,8 @@ TclMathInProgress()   *	the log base 2 of an integer.   *   * Results: - *	Returns the log base 2 of the operand. If the argument is less - *	than or equal to zero, a zero is returned. + *	Returns the log base 2 of the operand. If the argument is less than or + *	equal to zero, a zero is returned.   *   * Side effects:   *	None. @@ -4888,9 +10113,9 @@ TclMathInProgress()   */  int -TclLog2(value) -    register int value;		/* The integer for which to compute the -				 * log base 2. */ +TclLog2( +    register int value)		/* The integer for which to compute the log +				 * base 2. */  {      register int n = value;      register int result = 0; @@ -4920,15 +10145,15 @@ TclLog2(value)   */  static int -EvalStatsCmd(unused, interp, argc, argv) -    ClientData unused;		/* Unused. */ -    Tcl_Interp *interp;		/* The current interpreter. */ -    int argc;			/* The number of arguments. */ -    char **argv;		/* The argument strings. */ +EvalStatsCmd( +    ClientData unused,		/* Unused. */ +    Tcl_Interp *interp,		/* The current interpreter. */ +    int objc,			/* The number of arguments. */ +    Tcl_Obj *const objv[])	/* The argument strings. */  {      Interp *iPtr = (Interp *) interp; -    LiteralTable *globalTablePtr = &(iPtr->literalTable); -    ByteCodeStats *statsPtr = &(iPtr->stats); +    LiteralTable *globalTablePtr = &iPtr->literalTable; +    ByteCodeStats *statsPtr = &iPtr->stats;      double totalCodeBytes, currentCodeBytes;      double totalLiteralBytes, currentLiteralBytes;      double objBytesIfUnshared, strBytesIfUnshared, sharingBytesSaved; @@ -4940,12 +10165,18 @@ EvalStatsCmd(unused, interp, argc, argv)      int decadeHigh, minSizeDecade, maxSizeDecade, length, i;      char *litTableStats;      LiteralEntry *entryPtr; +    Tcl_Obj *objPtr; + +#define Percent(a,b) ((a) * 100.0 / (b)) + +    objPtr = Tcl_NewObj(); +    Tcl_IncrRefCount(objPtr);      numInstructions = 0.0;      for (i = 0;  i < 256;  i++) { -        if (statsPtr->instructionCount[i] != 0) { -            numInstructions += statsPtr->instructionCount[i]; -        } +	if (statsPtr->instructionCount[i] != 0) { +	    numInstructions += statsPtr->instructionCount[i]; +	}      }      totalLiteralBytes = sizeof(LiteralTable) @@ -4958,7 +10189,7 @@ EvalStatsCmd(unused, interp, argc, argv)      numCurrentByteCodes =  	    statsPtr->numCompilations - statsPtr->numByteCodesFreed;      currentHeaderBytes = numCurrentByteCodes -	    * (sizeof(ByteCode) - (sizeof(size_t) + sizeof(Tcl_Time))); +	    * (sizeof(ByteCode) - sizeof(size_t) - sizeof(Tcl_Time));      literalMgmtBytes = sizeof(LiteralTable)  	    + (iPtr->literalTable.numBuckets * sizeof(LiteralEntry *))  	    + (iPtr->literalTable.numEntries * sizeof(LiteralEntry)); @@ -4966,94 +10197,93 @@ EvalStatsCmd(unused, interp, argc, argv)  	    + iPtr->literalTable.numEntries * sizeof(Tcl_Obj)  	    + statsPtr->currentLitStringBytes;      currentCodeBytes = statsPtr->currentByteCodeBytes + currentLiteralBytes; -     +      /*       * Summary statistics, total and current source and ByteCode sizes.       */ -    fprintf(stdout, "\n----------------------------------------------------------------\n"); -    fprintf(stdout, -	    "Compilation and execution statistics for interpreter 0x%x\n", -	    (unsigned int) iPtr); +    Tcl_AppendPrintfToObj(objPtr, "\n----------------------------------------------------------------\n"); +    Tcl_AppendPrintfToObj(objPtr, +	    "Compilation and execution statistics for interpreter %#lx\n", +	    (long int)iPtr); -    fprintf(stdout, "\nNumber ByteCodes executed	%ld\n", +    Tcl_AppendPrintfToObj(objPtr, "\nNumber ByteCodes executed\t%ld\n",  	    statsPtr->numExecutions); -    fprintf(stdout, "Number ByteCodes compiled	%ld\n", +    Tcl_AppendPrintfToObj(objPtr, "Number ByteCodes compiled\t%ld\n",  	    statsPtr->numCompilations); -    fprintf(stdout, "  Mean executions/compile	%.1f\n", -	    ((float)statsPtr->numExecutions) / ((float)statsPtr->numCompilations)); -     -    fprintf(stdout, "\nInstructions executed		%.0f\n", +    Tcl_AppendPrintfToObj(objPtr, "  Mean executions/compile\t%.1f\n", +	    statsPtr->numExecutions / (float)statsPtr->numCompilations); + +    Tcl_AppendPrintfToObj(objPtr, "\nInstructions executed\t\t%.0f\n",  	    numInstructions); -    fprintf(stdout, "  Mean inst/compile		%.0f\n", +    Tcl_AppendPrintfToObj(objPtr, "  Mean inst/compile\t\t%.0f\n",  	    numInstructions / statsPtr->numCompilations); -    fprintf(stdout, "  Mean inst/execution		%.0f\n", +    Tcl_AppendPrintfToObj(objPtr, "  Mean inst/execution\t\t%.0f\n",  	    numInstructions / statsPtr->numExecutions); -    fprintf(stdout, "\nTotal ByteCodes			%ld\n", +    Tcl_AppendPrintfToObj(objPtr, "\nTotal ByteCodes\t\t\t%ld\n",  	    statsPtr->numCompilations); -    fprintf(stdout, "  Source bytes			%.6g\n", +    Tcl_AppendPrintfToObj(objPtr, "  Source bytes\t\t\t%.6g\n",  	    statsPtr->totalSrcBytes); -    fprintf(stdout, "  Code bytes			%.6g\n", +    Tcl_AppendPrintfToObj(objPtr, "  Code bytes\t\t\t%.6g\n",  	    totalCodeBytes); -    fprintf(stdout, "    ByteCode bytes		%.6g\n", +    Tcl_AppendPrintfToObj(objPtr, "    ByteCode bytes\t\t%.6g\n",  	    statsPtr->totalByteCodeBytes); -    fprintf(stdout, "    Literal bytes		%.6g\n", +    Tcl_AppendPrintfToObj(objPtr, "    Literal bytes\t\t%.6g\n",  	    totalLiteralBytes); -    fprintf(stdout, "      table %d + bkts %d + entries %ld + objects %ld + strings %.6g\n", -	    sizeof(LiteralTable), -	    iPtr->literalTable.numBuckets * sizeof(LiteralEntry *), -	    statsPtr->numLiteralsCreated * sizeof(LiteralEntry), -	    statsPtr->numLiteralsCreated * sizeof(Tcl_Obj), +    Tcl_AppendPrintfToObj(objPtr, "      table %lu + bkts %lu + entries %lu + objects %lu + strings %.6g\n", +	    (unsigned long) sizeof(LiteralTable), +	    (unsigned long) (iPtr->literalTable.numBuckets * sizeof(LiteralEntry *)), +	    (unsigned long) (statsPtr->numLiteralsCreated * sizeof(LiteralEntry)), +	    (unsigned long) (statsPtr->numLiteralsCreated * sizeof(Tcl_Obj)),  	    statsPtr->totalLitStringBytes); -    fprintf(stdout, "  Mean code/compile		%.1f\n", +    Tcl_AppendPrintfToObj(objPtr, "  Mean code/compile\t\t%.1f\n",  	    totalCodeBytes / statsPtr->numCompilations); -    fprintf(stdout, "  Mean code/source		%.1f\n", +    Tcl_AppendPrintfToObj(objPtr, "  Mean code/source\t\t%.1f\n",  	    totalCodeBytes / statsPtr->totalSrcBytes); -    fprintf(stdout, "\nCurrent (active) ByteCodes	%ld\n", +    Tcl_AppendPrintfToObj(objPtr, "\nCurrent (active) ByteCodes\t%ld\n",  	    numCurrentByteCodes); -    fprintf(stdout, "  Source bytes			%.6g\n", +    Tcl_AppendPrintfToObj(objPtr, "  Source bytes\t\t\t%.6g\n",  	    statsPtr->currentSrcBytes); -    fprintf(stdout, "  Code bytes			%.6g\n", +    Tcl_AppendPrintfToObj(objPtr, "  Code bytes\t\t\t%.6g\n",  	    currentCodeBytes); -    fprintf(stdout, "    ByteCode bytes		%.6g\n", +    Tcl_AppendPrintfToObj(objPtr, "    ByteCode bytes\t\t%.6g\n",  	    statsPtr->currentByteCodeBytes); -    fprintf(stdout, "    Literal bytes		%.6g\n", +    Tcl_AppendPrintfToObj(objPtr, "    Literal bytes\t\t%.6g\n",  	    currentLiteralBytes); -    fprintf(stdout, "      table %d + bkts %d + entries %d + objects %d + strings %.6g\n", -	    sizeof(LiteralTable), -	    iPtr->literalTable.numBuckets * sizeof(LiteralEntry *), -	    iPtr->literalTable.numEntries * sizeof(LiteralEntry), -	    iPtr->literalTable.numEntries * sizeof(Tcl_Obj), +    Tcl_AppendPrintfToObj(objPtr, "      table %lu + bkts %lu + entries %lu + objects %lu + strings %.6g\n", +	    (unsigned long) sizeof(LiteralTable), +	    (unsigned long) (iPtr->literalTable.numBuckets * sizeof(LiteralEntry *)), +	    (unsigned long) (iPtr->literalTable.numEntries * sizeof(LiteralEntry)), +	    (unsigned long) (iPtr->literalTable.numEntries * sizeof(Tcl_Obj)),  	    statsPtr->currentLitStringBytes); -    fprintf(stdout, "  Mean code/source		%.1f\n", +    Tcl_AppendPrintfToObj(objPtr, "  Mean code/source\t\t%.1f\n",  	    currentCodeBytes / statsPtr->currentSrcBytes); -    fprintf(stdout, "  Code + source bytes		%.6g (%0.1f mean code/src)\n", +    Tcl_AppendPrintfToObj(objPtr, "  Code + source bytes\t\t%.6g (%0.1f mean code/src)\n",  	    (currentCodeBytes + statsPtr->currentSrcBytes),  	    (currentCodeBytes / statsPtr->currentSrcBytes) + 1.0);      /*       * Tcl_IsShared statistics check       * -     * This gives the refcount of each obj as Tcl_IsShared was called -     * for it.  Shared objects must be duplicated before they can be -     * modified. +     * This gives the refcount of each obj as Tcl_IsShared was called for it. +     * Shared objects must be duplicated before they can be modified.       */      numSharedMultX = 0; -    fprintf(stdout, "\nTcl_IsShared object check (all objects):\n"); -    fprintf(stdout, "  Object had refcount <=1 (not shared)	%ld\n", +    Tcl_AppendPrintfToObj(objPtr, "\nTcl_IsShared object check (all objects):\n"); +    Tcl_AppendPrintfToObj(objPtr, "  Object had refcount <=1 (not shared)\t%ld\n",  	    tclObjsShared[1]);      for (i = 2;  i < TCL_MAX_SHARED_OBJ_STATS;  i++) { -	fprintf(stdout, "  refcount ==%d		%ld\n", +	Tcl_AppendPrintfToObj(objPtr, "  refcount ==%d\t\t%ld\n",  		i, tclObjsShared[i]);  	numSharedMultX += tclObjsShared[i];      } -    fprintf(stdout, "  refcount >=%d		%ld\n", +    Tcl_AppendPrintfToObj(objPtr, "  refcount >=%d\t\t%ld\n",  	    i, tclObjsShared[0]);      numSharedMultX += tclObjsShared[0]; -    fprintf(stdout, "  Total shared objects			%d\n", +    Tcl_AppendPrintfToObj(objPtr, "  Total shared objects\t\t\t%d\n",  	    numSharedMultX);      /* @@ -5063,14 +10293,14 @@ EvalStatsCmd(unused, interp, argc, argv)      numByteCodeLits = 0;      refCountSum = 0;      numSharedMultX = 0; -    numSharedOnce  = 0; -    objBytesIfUnshared  = 0.0; -    strBytesIfUnshared  = 0.0; +    numSharedOnce = 0; +    objBytesIfUnshared = 0.0; +    strBytesIfUnshared = 0.0;      strBytesSharedMultX = 0.0; -    strBytesSharedOnce  = 0.0; +    strBytesSharedOnce = 0.0;      for (i = 0;  i < globalTablePtr->numBuckets;  i++) {  	for (entryPtr = globalTablePtr->buckets[i];  entryPtr != NULL; -	        entryPtr = entryPtr->nextPtr) { +		entryPtr = entryPtr->nextPtr) {  	    if (entryPtr->objPtr->typePtr == &tclByteCodeType) {  		numByteCodeLits++;  	    } @@ -5090,556 +10320,233 @@ EvalStatsCmd(unused, interp, argc, argv)      sharingBytesSaved = (objBytesIfUnshared + strBytesIfUnshared)  	    - currentLiteralBytes; -    fprintf(stdout, "\nTotal objects (all interps)	%ld\n", +    Tcl_AppendPrintfToObj(objPtr, "\nTotal objects (all interps)\t%ld\n",  	    tclObjsAlloced); -    fprintf(stdout, "Current objects			%ld\n", +    Tcl_AppendPrintfToObj(objPtr, "Current objects\t\t\t%ld\n",  	    (tclObjsAlloced - tclObjsFreed)); -    fprintf(stdout, "Total literal objects		%ld\n", +    Tcl_AppendPrintfToObj(objPtr, "Total literal objects\t\t%ld\n",  	    statsPtr->numLiteralsCreated); -    fprintf(stdout, "\nCurrent literal objects		%d (%0.1f%% of current objects)\n", +    Tcl_AppendPrintfToObj(objPtr, "\nCurrent literal objects\t\t%d (%0.1f%% of current objects)\n",  	    globalTablePtr->numEntries, -	    (globalTablePtr->numEntries * 100.0) / (tclObjsAlloced-tclObjsFreed)); -    fprintf(stdout, "  ByteCode literals	 	%ld (%0.1f%% of current literals)\n", +	    Percent(globalTablePtr->numEntries, tclObjsAlloced-tclObjsFreed)); +    Tcl_AppendPrintfToObj(objPtr, "  ByteCode literals\t\t%ld (%0.1f%% of current literals)\n",  	    numByteCodeLits, -	    (numByteCodeLits * 100.0) / globalTablePtr->numEntries); -    fprintf(stdout, "  Literals reused > 1x	 	%d\n", +	    Percent(numByteCodeLits, globalTablePtr->numEntries)); +    Tcl_AppendPrintfToObj(objPtr, "  Literals reused > 1x\t\t%d\n",  	    numSharedMultX); -    fprintf(stdout, "  Mean reference count	 	%.2f\n", +    Tcl_AppendPrintfToObj(objPtr, "  Mean reference count\t\t%.2f\n",  	    ((double) refCountSum) / globalTablePtr->numEntries); -    fprintf(stdout, "  Mean len, str reused >1x 	%.2f\n", -	    (numSharedMultX? (strBytesSharedMultX/numSharedMultX) : 0.0)); -    fprintf(stdout, "  Mean len, str used 1x	 	%.2f\n", -	    (numSharedOnce? (strBytesSharedOnce/numSharedOnce) : 0.0)); -    fprintf(stdout, "  Total sharing savings	 	%.6g (%0.1f%% of bytes if no sharing)\n", +    Tcl_AppendPrintfToObj(objPtr, "  Mean len, str reused >1x \t%.2f\n", +	    (numSharedMultX ? strBytesSharedMultX/numSharedMultX : 0.0)); +    Tcl_AppendPrintfToObj(objPtr, "  Mean len, str used 1x\t\t%.2f\n", +	    (numSharedOnce ? strBytesSharedOnce/numSharedOnce : 0.0)); +    Tcl_AppendPrintfToObj(objPtr, "  Total sharing savings\t\t%.6g (%0.1f%% of bytes if no sharing)\n",  	    sharingBytesSaved, -	    (sharingBytesSaved * 100.0) / (objBytesIfUnshared + strBytesIfUnshared)); -    fprintf(stdout, "    Bytes with sharing		%.6g\n", +	    Percent(sharingBytesSaved, objBytesIfUnshared+strBytesIfUnshared)); +    Tcl_AppendPrintfToObj(objPtr, "    Bytes with sharing\t\t%.6g\n",  	    currentLiteralBytes); -    fprintf(stdout, "      table %d + bkts %d + entries %d + objects %d + strings %.6g\n", -	    sizeof(LiteralTable), -	    iPtr->literalTable.numBuckets * sizeof(LiteralEntry *), -	    iPtr->literalTable.numEntries * sizeof(LiteralEntry), -	    iPtr->literalTable.numEntries * sizeof(Tcl_Obj), +    Tcl_AppendPrintfToObj(objPtr, "      table %lu + bkts %lu + entries %lu + objects %lu + strings %.6g\n", +	    (unsigned long) sizeof(LiteralTable), +	    (unsigned long) (iPtr->literalTable.numBuckets * sizeof(LiteralEntry *)), +	    (unsigned long) (iPtr->literalTable.numEntries * sizeof(LiteralEntry)), +	    (unsigned long) (iPtr->literalTable.numEntries * sizeof(Tcl_Obj)),  	    statsPtr->currentLitStringBytes); -    fprintf(stdout, "    Bytes if no sharing		%.6g = objects %.6g + strings %.6g\n", +    Tcl_AppendPrintfToObj(objPtr, "    Bytes if no sharing\t\t%.6g = objects %.6g + strings %.6g\n",  	    (objBytesIfUnshared + strBytesIfUnshared),  	    objBytesIfUnshared, strBytesIfUnshared); -    fprintf(stdout, "  String sharing savings 	%.6g = unshared %.6g - shared %.6g\n", +    Tcl_AppendPrintfToObj(objPtr, "  String sharing savings \t%.6g = unshared %.6g - shared %.6g\n",  	    (strBytesIfUnshared - statsPtr->currentLitStringBytes),  	    strBytesIfUnshared, statsPtr->currentLitStringBytes); -    fprintf(stdout, "  Literal mgmt overhead	 	%ld (%0.1f%% of bytes with sharing)\n", +    Tcl_AppendPrintfToObj(objPtr, "  Literal mgmt overhead\t\t%ld (%0.1f%% of bytes with sharing)\n",  	    literalMgmtBytes, -	    (literalMgmtBytes * 100.0) / currentLiteralBytes); -    fprintf(stdout, "    table %d + buckets %d + entries %d\n", -	    sizeof(LiteralTable), -	    iPtr->literalTable.numBuckets * sizeof(LiteralEntry *), -	    iPtr->literalTable.numEntries * sizeof(LiteralEntry)); +	    Percent(literalMgmtBytes, currentLiteralBytes)); +    Tcl_AppendPrintfToObj(objPtr, "    table %lu + buckets %lu + entries %lu\n", +	    (unsigned long) sizeof(LiteralTable), +	    (unsigned long) (iPtr->literalTable.numBuckets * sizeof(LiteralEntry *)), +	    (unsigned long) (iPtr->literalTable.numEntries * sizeof(LiteralEntry)));      /*       * Breakdown of current ByteCode space requirements.       */ -     -    fprintf(stdout, "\nBreakdown of current ByteCode requirements:\n"); -    fprintf(stdout, "                         Bytes      Pct of    Avg per\n"); -    fprintf(stdout, "                                     total    ByteCode\n"); -    fprintf(stdout, "Total             %12.6g     100.00%%   %8.1f\n", + +    Tcl_AppendPrintfToObj(objPtr, "\nBreakdown of current ByteCode requirements:\n"); +    Tcl_AppendPrintfToObj(objPtr, "                         Bytes      Pct of    Avg per\n"); +    Tcl_AppendPrintfToObj(objPtr, "                                     total    ByteCode\n"); +    Tcl_AppendPrintfToObj(objPtr, "Total             %12.6g     100.00%%   %8.1f\n",  	    statsPtr->currentByteCodeBytes,  	    statsPtr->currentByteCodeBytes / numCurrentByteCodes); -    fprintf(stdout, "Header            %12.6g   %8.1f%%   %8.1f\n", +    Tcl_AppendPrintfToObj(objPtr, "Header            %12.6g   %8.1f%%   %8.1f\n",  	    currentHeaderBytes, -	    ((currentHeaderBytes * 100.0) / statsPtr->currentByteCodeBytes), +	    Percent(currentHeaderBytes, statsPtr->currentByteCodeBytes),  	    currentHeaderBytes / numCurrentByteCodes); -    fprintf(stdout, "Instructions      %12.6g   %8.1f%%   %8.1f\n", +    Tcl_AppendPrintfToObj(objPtr, "Instructions      %12.6g   %8.1f%%   %8.1f\n",  	    statsPtr->currentInstBytes, -	    ((statsPtr->currentInstBytes * 100.0) / statsPtr->currentByteCodeBytes), +	    Percent(statsPtr->currentInstBytes,statsPtr->currentByteCodeBytes),  	    statsPtr->currentInstBytes / numCurrentByteCodes); -    fprintf(stdout, "Literal ptr array %12.6g   %8.1f%%   %8.1f\n", +    Tcl_AppendPrintfToObj(objPtr, "Literal ptr array %12.6g   %8.1f%%   %8.1f\n",  	    statsPtr->currentLitBytes, -	    ((statsPtr->currentLitBytes * 100.0) / statsPtr->currentByteCodeBytes), +	    Percent(statsPtr->currentLitBytes,statsPtr->currentByteCodeBytes),  	    statsPtr->currentLitBytes / numCurrentByteCodes); -    fprintf(stdout, "Exception table   %12.6g   %8.1f%%   %8.1f\n", +    Tcl_AppendPrintfToObj(objPtr, "Exception table   %12.6g   %8.1f%%   %8.1f\n",  	    statsPtr->currentExceptBytes, -	    ((statsPtr->currentExceptBytes * 100.0) / statsPtr->currentByteCodeBytes), +	    Percent(statsPtr->currentExceptBytes,statsPtr->currentByteCodeBytes),  	    statsPtr->currentExceptBytes / numCurrentByteCodes); -    fprintf(stdout, "Auxiliary data    %12.6g   %8.1f%%   %8.1f\n", +    Tcl_AppendPrintfToObj(objPtr, "Auxiliary data    %12.6g   %8.1f%%   %8.1f\n",  	    statsPtr->currentAuxBytes, -	    ((statsPtr->currentAuxBytes * 100.0) / statsPtr->currentByteCodeBytes), +	    Percent(statsPtr->currentAuxBytes,statsPtr->currentByteCodeBytes),  	    statsPtr->currentAuxBytes / numCurrentByteCodes); -    fprintf(stdout, "Command map       %12.6g   %8.1f%%   %8.1f\n", +    Tcl_AppendPrintfToObj(objPtr, "Command map       %12.6g   %8.1f%%   %8.1f\n",  	    statsPtr->currentCmdMapBytes, -	    ((statsPtr->currentCmdMapBytes * 100.0) / statsPtr->currentByteCodeBytes), +	    Percent(statsPtr->currentCmdMapBytes,statsPtr->currentByteCodeBytes),  	    statsPtr->currentCmdMapBytes / numCurrentByteCodes);      /*       * Detailed literal statistics.       */ -     -    fprintf(stdout, "\nLiteral string sizes:\n"); -    fprintf(stdout, "	 Up to length		Percentage\n"); + +    Tcl_AppendPrintfToObj(objPtr, "\nLiteral string sizes:\n"); +    Tcl_AppendPrintfToObj(objPtr, "\t Up to length\t\tPercentage\n");      maxSizeDecade = 0;      for (i = 31;  i >= 0;  i--) { -        if (statsPtr->literalCount[i] > 0) { -            maxSizeDecade = i; +	if (statsPtr->literalCount[i] > 0) { +	    maxSizeDecade = i;  	    break; -        } +	}      }      sum = 0;      for (i = 0;  i <= maxSizeDecade;  i++) {  	decadeHigh = (1 << (i+1)) - 1;  	sum += statsPtr->literalCount[i]; -        fprintf(stdout,	"	%10d		%8.0f%%\n", -		decadeHigh, (sum * 100.0) / statsPtr->numLiteralsCreated); +	Tcl_AppendPrintfToObj(objPtr, "\t%10d\t\t%8.0f%%\n", +		decadeHigh, Percent(sum, statsPtr->numLiteralsCreated));      }      litTableStats = TclLiteralStats(globalTablePtr); -    fprintf(stdout, "\nCurrent literal table statistics:\n%s\n", -            litTableStats); -    ckfree((char *) litTableStats); +    Tcl_AppendPrintfToObj(objPtr, "\nCurrent literal table statistics:\n%s\n", +	    litTableStats); +    ckfree(litTableStats);      /*       * Source and ByteCode size distributions.       */ -    fprintf(stdout, "\nSource sizes:\n"); -    fprintf(stdout, "	 Up to size		Percentage\n"); +    Tcl_AppendPrintfToObj(objPtr, "\nSource sizes:\n"); +    Tcl_AppendPrintfToObj(objPtr, "\t Up to size\t\tPercentage\n");      minSizeDecade = maxSizeDecade = 0;      for (i = 0;  i < 31;  i++) { -        if (statsPtr->srcCount[i] > 0) { +	if (statsPtr->srcCount[i] > 0) {  	    minSizeDecade = i;  	    break; -        } +	}      }      for (i = 31;  i >= 0;  i--) { -        if (statsPtr->srcCount[i] > 0) { -            maxSizeDecade = i; +	if (statsPtr->srcCount[i] > 0) { +	    maxSizeDecade = i;  	    break; -        } +	}      }      sum = 0;      for (i = minSizeDecade;  i <= maxSizeDecade;  i++) {  	decadeHigh = (1 << (i+1)) - 1;  	sum += statsPtr->srcCount[i]; -        fprintf(stdout,	"	%10d		%8.0f%%\n", -		decadeHigh, (sum * 100.0) / statsPtr->numCompilations); +	Tcl_AppendPrintfToObj(objPtr, "\t%10d\t\t%8.0f%%\n", +		decadeHigh, Percent(sum, statsPtr->numCompilations));      } -    fprintf(stdout, "\nByteCode sizes:\n"); -    fprintf(stdout, "	 Up to size		Percentage\n"); +    Tcl_AppendPrintfToObj(objPtr, "\nByteCode sizes:\n"); +    Tcl_AppendPrintfToObj(objPtr, "\t Up to size\t\tPercentage\n");      minSizeDecade = maxSizeDecade = 0;      for (i = 0;  i < 31;  i++) { -        if (statsPtr->byteCodeCount[i] > 0) { +	if (statsPtr->byteCodeCount[i] > 0) {  	    minSizeDecade = i;  	    break; -        } +	}      }      for (i = 31;  i >= 0;  i--) { -        if (statsPtr->byteCodeCount[i] > 0) { -            maxSizeDecade = i; +	if (statsPtr->byteCodeCount[i] > 0) { +	    maxSizeDecade = i;  	    break; -        } +	}      }      sum = 0;      for (i = minSizeDecade;  i <= maxSizeDecade;  i++) {  	decadeHigh = (1 << (i+1)) - 1;  	sum += statsPtr->byteCodeCount[i]; -        fprintf(stdout,	"	%10d		%8.0f%%\n", -		decadeHigh, (sum * 100.0) / statsPtr->numCompilations); +	Tcl_AppendPrintfToObj(objPtr, "\t%10d\t\t%8.0f%%\n", +		decadeHigh, Percent(sum, statsPtr->numCompilations));      } -    fprintf(stdout, "\nByteCode longevity (excludes Current ByteCodes):\n"); -    fprintf(stdout, "	       Up to ms		Percentage\n"); +    Tcl_AppendPrintfToObj(objPtr, "\nByteCode longevity (excludes Current ByteCodes):\n"); +    Tcl_AppendPrintfToObj(objPtr, "\t       Up to ms\t\tPercentage\n");      minSizeDecade = maxSizeDecade = 0;      for (i = 0;  i < 31;  i++) { -        if (statsPtr->lifetimeCount[i] > 0) { +	if (statsPtr->lifetimeCount[i] > 0) {  	    minSizeDecade = i;  	    break; -        } +	}      }      for (i = 31;  i >= 0;  i--) { -        if (statsPtr->lifetimeCount[i] > 0) { -            maxSizeDecade = i; +	if (statsPtr->lifetimeCount[i] > 0) { +	    maxSizeDecade = i;  	    break; -        } +	}      }      sum = 0;      for (i = minSizeDecade;  i <= maxSizeDecade;  i++) {  	decadeHigh = (1 << (i+1)) - 1;  	sum += statsPtr->lifetimeCount[i]; -        fprintf(stdout,	"	%12.3f		%8.0f%%\n", -		decadeHigh / 1000.0, -		(sum * 100.0) / statsPtr->numByteCodesFreed); +	Tcl_AppendPrintfToObj(objPtr, "\t%12.3f\t\t%8.0f%%\n", +		decadeHigh/1000.0, Percent(sum, statsPtr->numByteCodesFreed));      }      /*       * Instruction counts.       */ -    fprintf(stdout, "\nInstruction counts:\n"); -    for (i = 0;  i <= LAST_INST_OPCODE;  i++) { -        if (statsPtr->instructionCount[i]) { -            fprintf(stdout, "%20s %8ld %6.1f%%\n", -		    instructionTable[i].name, -		    statsPtr->instructionCount[i], -		    (statsPtr->instructionCount[i]*100.0) / numInstructions); -        } -    } - -    fprintf(stdout, "\nInstructions NEVER executed:\n"); +    Tcl_AppendPrintfToObj(objPtr, "\nInstruction counts:\n");      for (i = 0;  i <= LAST_INST_OPCODE;  i++) { -        if (statsPtr->instructionCount[i] == 0) { -            fprintf(stdout, "%20s\n", -		    instructionTable[i].name); -        } +	Tcl_AppendPrintfToObj(objPtr, "%20s %8ld ", +		tclInstructionTable[i].name, statsPtr->instructionCount[i]); +	if (statsPtr->instructionCount[i]) { +	    Tcl_AppendPrintfToObj(objPtr, "%6.1f%%\n", +		    Percent(statsPtr->instructionCount[i], numInstructions)); +	} else { +	    Tcl_AppendPrintfToObj(objPtr, "0\n"); +	}      }  #ifdef TCL_MEM_DEBUG -    fprintf(stdout, "\nHeap Statistics:\n"); -    TclDumpMemoryInfo(stdout); +    Tcl_AppendPrintfToObj(objPtr, "\nHeap Statistics:\n"); +    TclDumpMemoryInfo((ClientData) objPtr, 1);  #endif -    fprintf(stdout, "\n----------------------------------------------------------------\n"); -    return TCL_OK; -} -#endif /* TCL_COMPILE_STATS */ - -/* - *---------------------------------------------------------------------- - * - * Tcl_GetCommandFromObj -- - * - *      Returns the command specified by the name in a Tcl_Obj. - * - * Results: - *	Returns a token for the command if it is found. Otherwise, if it - *	can't be found or there is an error, returns NULL. - * - * Side effects: - *      May update the internal representation for the object, caching - *      the command reference so that the next time this procedure is - *	called with the same object, the command can be found quickly. - * - *---------------------------------------------------------------------- - */ - -Tcl_Command -Tcl_GetCommandFromObj(interp, objPtr) -    Tcl_Interp *interp;		/* The interpreter in which to resolve the -				 * command and to report errors. */ -    register Tcl_Obj *objPtr;	/* The object containing the command's -				 * name. If the name starts with "::", will -				 * be looked up in global namespace. Else, -				 * looked up first in the current namespace -				 * if contextNsPtr is NULL, then in global -				 * namespace. */ -{ -    Interp *iPtr = (Interp *) interp; -    register ResolvedCmdName *resPtr; -    register Command *cmdPtr; -    Namespace *currNsPtr; -    int result; - -    /* -     * Get the internal representation, converting to a command type if -     * needed. The internal representation is a ResolvedCmdName that points -     * to the actual command. -     */ -     -    if (objPtr->typePtr != &tclCmdNameType) { -        result = tclCmdNameType.setFromAnyProc(interp, objPtr); -        if (result != TCL_OK) { -            return (Tcl_Command) NULL; -        } -    } -    resPtr = (ResolvedCmdName *) objPtr->internalRep.otherValuePtr; +    Tcl_AppendPrintfToObj(objPtr, "\n----------------------------------------------------------------\n"); -    /* -     * Get the current namespace. -     */ -     -    if (iPtr->varFramePtr != NULL) { -	currNsPtr = iPtr->varFramePtr->nsPtr; +    if (objc == 1) { +	Tcl_SetObjResult(interp, objPtr);      } else { -	currNsPtr = iPtr->globalNsPtr; -    } - -    /* -     * Check the context namespace and the namespace epoch of the resolved -     * symbol to make sure that it is fresh. If not, then force another -     * conversion to the command type, to discard the old rep and create a -     * new one. Note that we verify that the namespace id of the context -     * namespace is the same as the one we cached; this insures that the -     * namespace wasn't deleted and a new one created at the same address -     * with the same command epoch. -     */ -     -    cmdPtr = NULL; -    if ((resPtr != NULL) -	    && (resPtr->refNsPtr == currNsPtr) -	    && (resPtr->refNsId == currNsPtr->nsId) -	    && (resPtr->refNsCmdEpoch == currNsPtr->cmdRefEpoch)) { -        cmdPtr = resPtr->cmdPtr; -        if (cmdPtr->cmdEpoch != resPtr->cmdEpoch) { -            cmdPtr = NULL; -        } -    } - -    if (cmdPtr == NULL) { -        result = tclCmdNameType.setFromAnyProc(interp, objPtr); -        if (result != TCL_OK) { -            return (Tcl_Command) NULL; -        } -        resPtr = (ResolvedCmdName *) objPtr->internalRep.otherValuePtr; -        if (resPtr != NULL) { -            cmdPtr = resPtr->cmdPtr; -        } -    } -    return (Tcl_Command) cmdPtr; -} - -/* - *---------------------------------------------------------------------- - * - * TclSetCmdNameObj -- - * - *	Modify an object to be an CmdName object that refers to the argument - *	Command structure. - * - * Results: - *	None. - * - * Side effects: - *	The object's old internal rep is freed. It's string rep is not - *	changed. The refcount in the Command structure is incremented to - *	keep it from being freed if the command is later deleted until - *	TclExecuteByteCode has a chance to recognize that it was deleted. - * - *---------------------------------------------------------------------- - */ - -void -TclSetCmdNameObj(interp, objPtr, cmdPtr) -    Tcl_Interp *interp;		/* Points to interpreter containing command -				 * that should be cached in objPtr. */ -    register Tcl_Obj *objPtr;	/* Points to Tcl object to be changed to -				 * a CmdName object. */ -    Command *cmdPtr;		/* Points to Command structure that the -				 * CmdName object should refer to. */ -{ -    Interp *iPtr = (Interp *) interp; -    register ResolvedCmdName *resPtr; -    Tcl_ObjType *oldTypePtr = objPtr->typePtr; -    register Namespace *currNsPtr; - -    if (oldTypePtr == &tclCmdNameType) { -	return; -    } -     -    /* -     * Get the current namespace. -     */ -     -    if (iPtr->varFramePtr != NULL) { -	currNsPtr = iPtr->varFramePtr->nsPtr; -    } else { -	currNsPtr = iPtr->globalNsPtr; -    } -     -    cmdPtr->refCount++; -    resPtr = (ResolvedCmdName *) ckalloc(sizeof(ResolvedCmdName)); -    resPtr->cmdPtr = cmdPtr; -    resPtr->refNsPtr = currNsPtr; -    resPtr->refNsId  = currNsPtr->nsId; -    resPtr->refNsCmdEpoch = currNsPtr->cmdRefEpoch; -    resPtr->cmdEpoch = cmdPtr->cmdEpoch; -    resPtr->refCount = 1; -     -    if ((oldTypePtr != NULL) && (oldTypePtr->freeIntRepProc != NULL)) { -	oldTypePtr->freeIntRepProc(objPtr); -    } -    objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) resPtr; -    objPtr->internalRep.twoPtrValue.ptr2 = NULL; -    objPtr->typePtr = &tclCmdNameType; -} - -/* - *---------------------------------------------------------------------- - * - * FreeCmdNameInternalRep -- - * - *	Frees the resources associated with a cmdName object's internal - *	representation. - * - * Results: - *	None. - * - * Side effects: - *	Decrements the ref count of any cached ResolvedCmdName structure - *	pointed to by the cmdName's internal representation. If this is  - *	the last use of the ResolvedCmdName, it is freed. This in turn - *	decrements the ref count of the Command structure pointed to by  - *	the ResolvedSymbol, which may free the Command structure. - * - *---------------------------------------------------------------------- - */ - -static void -FreeCmdNameInternalRep(objPtr) -    register Tcl_Obj *objPtr;	/* CmdName object with internal -				 * representation to free. */ -{ -    register ResolvedCmdName *resPtr = -	(ResolvedCmdName *) objPtr->internalRep.otherValuePtr; - -    if (resPtr != NULL) { -	/* -	 * Decrement the reference count of the ResolvedCmdName structure. -	 * If there are no more uses, free the ResolvedCmdName structure. -	 */ -     -        resPtr->refCount--; -        if (resPtr->refCount == 0) { -            /* -	     * Now free the cached command, unless it is still in its -             * hash table or if there are other references to it -             * from other cmdName objects. -	     */ -	     -            Command *cmdPtr = resPtr->cmdPtr; -            TclCleanupCommand(cmdPtr); -            ckfree((char *) resPtr); -        } -    } -} - -/* - *---------------------------------------------------------------------- - * - * DupCmdNameInternalRep -- - * - *	Initialize the internal representation of an cmdName Tcl_Obj to a - *	copy of the internal representation of an existing cmdName object.  - * - * Results: - *	None. - * - * Side effects: - *	"copyPtr"s internal rep is set to point to the ResolvedCmdName - *	structure corresponding to "srcPtr"s internal rep. Increments the - *	ref count of the ResolvedCmdName structure pointed to by the - *	cmdName's internal representation. - * - *---------------------------------------------------------------------- - */ - -static void -DupCmdNameInternalRep(srcPtr, copyPtr) -    Tcl_Obj *srcPtr;		/* Object with internal rep to copy. */ -    register Tcl_Obj *copyPtr;	/* Object with internal rep to set. */ -{ -    register ResolvedCmdName *resPtr = -        (ResolvedCmdName *) srcPtr->internalRep.otherValuePtr; - -    copyPtr->internalRep.twoPtrValue.ptr1 = (VOID *) resPtr; -    copyPtr->internalRep.twoPtrValue.ptr2 = NULL; -    if (resPtr != NULL) { -        resPtr->refCount++; -    } -    copyPtr->typePtr = &tclCmdNameType; -} - -/* - *---------------------------------------------------------------------- - * - * SetCmdNameFromAny -- - * - *	Generate an cmdName internal form for the Tcl object "objPtr". - * - * Results: - *	The return value is a standard Tcl result. The conversion always - *	succeeds and TCL_OK is returned. - * - * Side effects: - *	A pointer to a ResolvedCmdName structure that holds a cached pointer - *	to the command with a name that matches objPtr's string rep is - *	stored as objPtr's internal representation. This ResolvedCmdName - *	pointer will be NULL if no matching command was found. The ref count - *	of the cached Command's structure (if any) is also incremented. - * - *---------------------------------------------------------------------- - */ - -static int -SetCmdNameFromAny(interp, objPtr) -    Tcl_Interp *interp;		/* Used for error reporting if not NULL. */ -    register Tcl_Obj *objPtr;	/* The object to convert. */ -{ -    Interp *iPtr = (Interp *) interp; -    char *name; -    Tcl_Command cmd; -    register Command *cmdPtr; -    Namespace *currNsPtr; -    register ResolvedCmdName *resPtr; - -    /* -     * Get "objPtr"s string representation. Make it up-to-date if necessary. -     */ - -    name = objPtr->bytes; -    if (name == NULL) { -	name = Tcl_GetString(objPtr); -    } - -    /* -     * Find the Command structure, if any, that describes the command called -     * "name". Build a ResolvedCmdName that holds a cached pointer to this -     * Command, and bump the reference count in the referenced Command -     * structure. A Command structure will not be deleted as long as it is -     * referenced from a CmdName object. -     */ - -    cmd = Tcl_FindCommand(interp, name, (Tcl_Namespace *) NULL, -	    /*flags*/ 0); -    cmdPtr = (Command *) cmd; -    if (cmdPtr != NULL) { -	/* -	 * Get the current namespace. -	 */ -	 -	if (iPtr->varFramePtr != NULL) { -	    currNsPtr = iPtr->varFramePtr->nsPtr; +	Tcl_Channel outChan; +	char *str = Tcl_GetStringFromObj(objv[1], &length); + +	if (length) { +	    if (strcmp(str, "stdout") == 0) { +		outChan = Tcl_GetStdChannel(TCL_STDOUT); +	    } else if (strcmp(str, "stderr") == 0) { +		outChan = Tcl_GetStdChannel(TCL_STDERR); +	    } else { +		outChan = Tcl_OpenFileChannel(NULL, str, "w", 0664); +	    }  	} else { -	    currNsPtr = iPtr->globalNsPtr; +	    outChan = Tcl_GetStdChannel(TCL_STDOUT); +	} +	if (outChan != NULL) { +	    Tcl_WriteObj(outChan, objPtr);  	} -	 -	cmdPtr->refCount++; -        resPtr = (ResolvedCmdName *) ckalloc(sizeof(ResolvedCmdName)); -        resPtr->cmdPtr        = cmdPtr; -        resPtr->refNsPtr      = currNsPtr; -        resPtr->refNsId       = currNsPtr->nsId; -        resPtr->refNsCmdEpoch = currNsPtr->cmdRefEpoch; -        resPtr->cmdEpoch      = cmdPtr->cmdEpoch; -        resPtr->refCount      = 1; -    } else { -	resPtr = NULL;	/* no command named "name" was found */ -    } - -    /* -     * Free the old internalRep before setting the new one. We do this as -     * late as possible to allow the conversion code, in particular -     * GetStringFromObj, to use that old internalRep. If no Command -     * structure was found, leave NULL as the cached value. -     */ - -    if ((objPtr->typePtr != NULL) -	    && (objPtr->typePtr->freeIntRepProc != NULL)) { -	objPtr->typePtr->freeIntRepProc(objPtr);      } -     -    objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) resPtr; -    objPtr->internalRep.twoPtrValue.ptr2 = NULL; -    objPtr->typePtr = &tclCmdNameType; +    Tcl_DecrRefCount(objPtr);      return TCL_OK;  } +#endif /* TCL_COMPILE_STATS */  #ifdef TCL_COMPILE_DEBUG  /* @@ -5647,15 +10554,15 @@ SetCmdNameFromAny(interp, objPtr)   *   * StringForResultCode --   * - *	Procedure that returns a human-readable string representing a - *	Tcl result code such as TCL_ERROR.  + *	Procedure that returns a human-readable string representing a Tcl + *	result code such as TCL_ERROR.   *   * Results: - *	If the result code is one of the standard Tcl return codes, the - *	result is a string representing that code such as "TCL_ERROR". - *	Otherwise, the result string is that code formatted as a - *	sequence of decimal digit characters. Note that the resulting - *	string must not be modified by the caller. + *	If the result code is one of the standard Tcl return codes, the result + *	is a string representing that code such as "TCL_ERROR". Otherwise, the + *	result string is that code formatted as a sequence of decimal digit + *	characters. Note that the resulting string must not be modified by the + *	caller.   *   * Side effects:   *	None. @@ -5663,13 +10570,13 @@ SetCmdNameFromAny(interp, objPtr)   *----------------------------------------------------------------------   */ -static char * -StringForResultCode(result) -    int result;			/* The Tcl result code for which to -				 * generate a string. */ +static const char * +StringForResultCode( +    int result)			/* The Tcl result code for which to generate a +				 * string. */  {      static char buf[TCL_INTEGER_SPACE]; -     +      if ((result >= TCL_OK) && (result <= TCL_CONTINUE)) {  	return resultStrings[result];      } @@ -5677,3 +10584,11 @@ StringForResultCode(result)      return buf;  }  #endif /* TCL_COMPILE_DEBUG */ + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ | 
