tclExecute.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:182k
- /*
- * tclExecute.c --
- *
- * This file contains procedures that execute byte-compiled Tcl
- * commands.
- *
- * Copyright (c) 1996-1997 Sun Microsystems, Inc.
- * Copyright (c) 1998-2000 by Scriptics Corporation.
- * Copyright (c) 2001 by Kevin B. Kenny. All rights reserved.
- *
- * 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.94.2.22 2007/09/13 15:28:12 das Exp $
- */
- #include "tclInt.h"
- #include "tclCompile.h"
- #ifndef TCL_NO_MATH
- # include "tclMath.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.
- */
- #ifndef TCL_GENERIC_ONLY
- # include "tclPort.h"
- #else /* TCL_GENERIC_ONLY */
- # ifndef NO_FLOAT_H
- # include <float.h>
- # else /* NO_FLOAT_H */
- # ifndef NO_VALUES_H
- # include <values.h>
- # endif /* !NO_VALUES_H */
- # endif /* !NO_FLOAT_H */
- # define NO_ERRNO_H
- #endif /* !TCL_GENERIC_ONLY */
- #ifdef NO_ERRNO_H
- int errno;
- # define EDOM 33
- # define ERANGE 34
- #endif
- /*
- * Need DBL_MAX for IS_INF() macro...
- */
- #ifndef DBL_MAX
- # ifdef MAXDOUBLE
- # define DBL_MAX MAXDOUBLE
- # else /* !MAXDOUBLE */
- /*
- * This value is from the Solaris headers, but doubles seem to be the
- * same size everywhere. Long doubles aren't, but we don't use those.
- */
- # define DBL_MAX 1.79769313486231570e+308
- # endif /* MAXDOUBLE */
- #endif /* !DBL_MAX */
- /*
- * Boolean flag indicating whether the Tcl bytecode interpreter has been
- * initialized.
- */
- static int execInitialized = 0;
- TCL_DECLARE_MUTEX(execMutex)
- #ifdef TCL_COMPILE_DEBUG
- /*
- * Variable that controls whether execution tracing is enabled and, if so,
- * what level of tracing is desired:
- * 0: no execution tracing
- * 1: trace invocations of Tcl procs only
- * 2: trace invocations of all (not compiled away) commands
- * 3: display each instruction executed
- * This variable is linked to the Tcl variable "tcl_traceExec".
- */
- int tclTraceExec = 0;
- #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.
- */
- static char *operatorStrings[] = {
- "||", "&&", "|", "^", "&", "==", "!=", "<", ">", "<=", ">=", "<<", ">>",
- "+", "-", "*", "/", "%", "+", "-", "~", "!",
- "BUILTIN FUNCTION", "FUNCTION",
- "", "", "", "", "", "", "", "", "eq", "ne",
- };
- /*
- * Mapping from Tcl result codes to strings; used for error and debugging
- * messages.
- */
- #ifdef TCL_COMPILE_DEBUG
- static char *resultStrings[] = {
- "TCL_OK", "TCL_ERROR", "TCL_RETURN", "TCL_BREAK", "TCL_CONTINUE"
- };
- #endif
- /*
- * These are used by evalstats to monitor object usage in Tcl.
- */
- #ifdef TCL_COMPILE_STATS
- long tclObjsAlloced = 0;
- long tclObjsFreed = 0;
- #define TCL_MAX_SHARED_OBJ_STATS 5
- 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.
- */
- #define IS_NAN(v) ((v) != (v))
- #define IS_INF(v) (((v) > DBL_MAX) || ((v) < -DBL_MAX))
- /*
- * 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
- * result: 0 indicates no object should be pushed on the
- * stack; otherwise, push objResultPtr. If (result < 0),
- * objResultPtr already has the correct reference count.
- */
- #define NEXT_INST_F(pcAdjustment, nCleanup, result)
- if (nCleanup == 0) {
- if (result != 0) {
- if ((result) > 0) {
- PUSH_OBJECT(objResultPtr);
- } else {
- stackPtr[++stackTop] = objResultPtr;
- }
- }
- pc += (pcAdjustment);
- goto cleanup0;
- } else if (result != 0) {
- if ((result) > 0) {
- Tcl_IncrRefCount(objResultPtr);
- }
- pc += (pcAdjustment);
- switch (nCleanup) {
- case 1: goto cleanup1_pushObjResultPtr;
- case 2: goto cleanup2_pushObjResultPtr;
- default: panic("ERROR: bad usage of macro NEXT_INST_F");
- }
- } else {
- pc += (pcAdjustment);
- switch (nCleanup) {
- case 1: goto cleanup1;
- case 2: goto cleanup2;
- default: panic("ERROR: bad usage of macro NEXT_INST_F");
- }
- }
- #define NEXT_INST_V(pcAdjustment, nCleanup, result)
- pc += (pcAdjustment);
- cleanup = (nCleanup);
- if (result) {
- if ((result) > 0) {
- Tcl_IncrRefCount(objResultPtr);
- }
- goto cleanupV_pushObjResultPtr;
- } else {
- goto cleanupV;
- }
- /*
- * 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
- * procedures that use this scheme) that could result in a recursive call
- * to TclExecuteByteCode.
- */
- #define CACHE_STACK_INFO()
- stackPtr = eePtr->stackPtr;
- stackTop = eePtr->stackTop
- #define DECACHE_STACK_INFO()
- eePtr->stackTop = stackTop
- /*
- * 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.
- *
- * 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.
- */
-
- #define PUSH_OBJECT(objPtr)
- Tcl_IncrRefCount(stackPtr[++stackTop] = (objPtr))
-
- #define POP_OBJECT()
- (stackPtr[stackTop--])
- /*
- * 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.
- */
- #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_APPEND(a)
- if (traceInstructions) {
- 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 ? TclGetString(objPtr) : "")
- #else /* !TCL_COMPILE_DEBUG */
- # define TRACE(a)
- # define TRACE_APPEND(a)
- # define TRACE_WITH_OBJ(a, objPtr)
- # define O2S(objPtr)
- #endif /* TCL_COMPILE_DEBUG */
- /*
- * DTrace instruction probe macros.
- */
- #define TCL_DTRACE_INST_NEXT()
- if (TCL_DTRACE_INST_DONE_ENABLED()) {
- if (curInstName) {
- TCL_DTRACE_INST_DONE(curInstName, stackTop - initStackTop,
- stackPtr + stackTop);
- }
- curInstName = tclInstructionTable[*pc].name;
- if (TCL_DTRACE_INST_START_ENABLED()) {
- TCL_DTRACE_INST_START(curInstName, stackTop - initStackTop,
- stackPtr + stackTop);
- }
- } else if (TCL_DTRACE_INST_START_ENABLED()) {
- TCL_DTRACE_INST_START(tclInstructionTable[*pc].name,
- stackTop - initStackTop, stackPtr + stackTop);
- }
- #define TCL_DTRACE_INST_LAST()
- if (TCL_DTRACE_INST_DONE_ENABLED() && curInstName) {
- TCL_DTRACE_INST_DONE(curInstName, stackTop - initStackTop,
- stackPtr + stackTop);
- }
- /*
- * Macro to read a string containing either a wide or an int and
- * decide which it is while decoding it at the same time. This
- * enforces the policy that integer constants between LONG_MIN and
- * LONG_MAX (inclusive) are represented by normal longs, and integer
- * constants outside that range are represented by wide ints.
- *
- * GET_WIDE_OR_INT is the same as REQUIRE_WIDE_OR_INT except it never
- * generates an error message.
- */
- #define REQUIRE_WIDE_OR_INT(resultVar, objPtr, longVar, wideVar)
- (resultVar) = Tcl_GetWideIntFromObj(interp, (objPtr), &(wideVar));
- if ((resultVar) == TCL_OK && (wideVar) >= Tcl_LongAsWide(LONG_MIN)
- && (wideVar) <= Tcl_LongAsWide(LONG_MAX)) {
- (objPtr)->typePtr = &tclIntType;
- (objPtr)->internalRep.longValue = (longVar)
- = Tcl_WideAsLong(wideVar);
- }
- #define GET_WIDE_OR_INT(resultVar, objPtr, longVar, wideVar)
- (resultVar) = Tcl_GetWideIntFromObj((Tcl_Interp *) NULL, (objPtr),
- &(wideVar));
- if ((resultVar) == TCL_OK && (wideVar) >= Tcl_LongAsWide(LONG_MIN)
- && (wideVar) <= Tcl_LongAsWide(LONG_MAX)) {
- (objPtr)->typePtr = &tclIntType;
- (objPtr)->internalRep.longValue = (longVar)
- = Tcl_WideAsLong(wideVar);
- }
- /*
- * Combined with REQUIRE_WIDE_OR_INT, this gets a long value from
- * an obj.
- */
- #define FORCE_LONG(objPtr, longVar, wideVar)
- if ((objPtr)->typePtr == &tclWideIntType) {
- (longVar) = Tcl_WideAsLong(wideVar);
- }
- #define IS_INTEGER_TYPE(typePtr)
- ((typePtr) == &tclIntType || (typePtr) == &tclWideIntType)
- #define IS_NUMERIC_TYPE(typePtr)
- (IS_INTEGER_TYPE(typePtr) || (typePtr) == &tclDoubleType)
- #define W0 Tcl_LongAsWide(0)
- /*
- * For tracing that uses wide values.
- */
- #define LLD "%" TCL_LL_MODIFIER "d"
- #ifndef TCL_WIDE_INT_IS_LONG
- /*
- * Extract a double value from a general numeric object.
- */
- #define GET_DOUBLE_VALUE(doubleVar, objPtr, typePtr)
- if ((typePtr) == &tclIntType) {
- (doubleVar) = (double) (objPtr)->internalRep.longValue;
- } else if ((typePtr) == &tclWideIntType) {
- (doubleVar) = Tcl_WideAsDouble((objPtr)->internalRep.wideValue);
- } else {
- (doubleVar) = (objPtr)->internalRep.doubleValue;
- }
- #else /* TCL_WIDE_INT_IS_LONG */
- #define GET_DOUBLE_VALUE(doubleVar, objPtr, typePtr)
- if (((typePtr) == &tclIntType) || ((typePtr) == &tclWideIntType)) {
- (doubleVar) = (double) (objPtr)->internalRep.longValue;
- } else {
- (doubleVar) = (objPtr)->internalRep.doubleValue;
- }
- #endif /* TCL_WIDE_INT_IS_LONG */
- /*
- * Declarations for local procedures to this file:
- */
- static int TclExecuteByteCode _ANSI_ARGS_((Tcl_Interp *interp,
- ByteCode *codePtr));
- 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));
- static int ExprWideFunc _ANSI_ARGS_((Tcl_Interp *interp,
- ExecEnv *eePtr, ClientData clientData));
- #ifdef TCL_COMPILE_STATS
- static int EvalStatsCmd _ANSI_ARGS_((ClientData clientData,
- Tcl_Interp *interp, int objc,
- Tcl_Obj *CONST objv[]));
- #endif /* TCL_COMPILE_STATS */
- #ifdef TCL_COMPILE_DEBUG
- static char * GetOpcodeName _ANSI_ARGS_((unsigned char *pc));
- #endif /* TCL_COMPILE_DEBUG */
- 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));
- static char * StringForResultCode _ANSI_ARGS_((int result));
- static void ValidatePcAndStackTop _ANSI_ARGS_((
- ByteCode *codePtr, unsigned char *pc,
- int stackTop, int stackLowerBound));
- #endif /* TCL_COMPILE_DEBUG */
- static int VerifyExprObjType _ANSI_ARGS_((Tcl_Interp *interp,
- Tcl_Obj *objPtr));
- /*
- * 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.
- */
- BuiltinFunc tclBuiltinFuncTable[] = {
- #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},
- {"wide", 1, {TCL_EITHER}, ExprWideFunc, 0},
- {0},
- };
- /*
- *----------------------------------------------------------------------
- *
- * InitByteCodeExecution --
- *
- * This procedure is called once to initialize the Tcl bytecode
- * interpreter.
- *
- * Results:
- * None.
- *
- * 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 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
- * "tcl_traceExec" is linked to control
- * instruction tracing. */
- {
- #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");
- }
- #endif
- #ifdef TCL_COMPILE_STATS
- Tcl_CreateObjCommand(interp, "evalstats", EvalStatsCmd,
- (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
- #endif /* TCL_COMPILE_STATS */
- }
- /*
- *----------------------------------------------------------------------
- *
- * 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.
- *
- * 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.
- *
- *----------------------------------------------------------------------
- */
- #define TCL_STACK_INITIAL_SIZE 2000
- ExecEnv *
- TclCreateExecEnv(interp)
- Tcl_Interp *interp; /* Interpreter for which the execution
- * environment is being created. */
- {
- ExecEnv *eePtr = (ExecEnv *) ckalloc(sizeof(ExecEnv));
- Tcl_Obj **stackPtr;
- stackPtr = (Tcl_Obj **)
- ckalloc((size_t) (TCL_STACK_INITIAL_SIZE * sizeof(Tcl_Obj *)));
- /*
- * Use the bottom pointer to keep a reference count; the
- * execution environment holds a reference.
- */
- stackPtr++;
- eePtr->stackPtr = stackPtr;
- stackPtr[-1] = (Tcl_Obj *) ((char *) 1);
- eePtr->stackTop = -1;
- eePtr->stackEnd = (TCL_STACK_INITIAL_SIZE - 2);
- eePtr->errorInfo = Tcl_NewStringObj("::errorInfo", -1);
- Tcl_IncrRefCount(eePtr->errorInfo);
- eePtr->errorCode = Tcl_NewStringObj("::errorCode", -1);
- Tcl_IncrRefCount(eePtr->errorCode);
- Tcl_MutexLock(&execMutex);
- if (!execInitialized) {
- TclInitAuxDataTypeTable();
- InitByteCodeExecution(interp);
- execInitialized = 1;
- }
- Tcl_MutexUnlock(&execMutex);
- return eePtr;
- }
- #undef TCL_STACK_INITIAL_SIZE
- /*
- *----------------------------------------------------------------------
- *
- * TclDeleteExecEnv --
- *
- * Frees the storage for an ExecEnv.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Storage for an ExecEnv and its contained storage (e.g. the
- * evaluation stack) is freed.
- *
- *----------------------------------------------------------------------
- */
- void
- TclDeleteExecEnv(eePtr)
- ExecEnv *eePtr; /* Execution environment to free. */
- {
- if (eePtr->stackPtr[-1] == (Tcl_Obj *) ((char *) 1)) {
- ckfree((char *) (eePtr->stackPtr-1));
- } else {
- panic("ERROR: freeing an execEnv whose stack is still in use.n");
- }
- TclDecrRefCount(eePtr->errorInfo);
- TclDecrRefCount(eePtr->errorCode);
- ckfree((char *) eePtr);
- }
- /*
- *----------------------------------------------------------------------
- *
- * TclFinalizeExecution --
- *
- * 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.
- *
- *----------------------------------------------------------------------
- */
- void
- TclFinalizeExecution()
- {
- Tcl_MutexLock(&execMutex);
- execInitialized = 0;
- Tcl_MutexUnlock(&execMutex);
- TclFinalizeAuxDataTypeTable();
- }
- /*
- *----------------------------------------------------------------------
- *
- * GrowEvaluationStack --
- *
- * This procedure grows a Tcl evaluation stack stored in an ExecEnv.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The size of the evaluation stack is doubled.
- *
- *----------------------------------------------------------------------
- */
- static void
- GrowEvaluationStack(eePtr)
- register ExecEnv *eePtr; /* Points to the ExecEnv with an evaluation
- * stack to enlarge. */
- {
- /*
- * The current Tcl stack elements are stored from eePtr->stackPtr[0]
- * to eePtr->stackPtr[eePtr->stackEnd] (inclusive).
- */
- 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);
- Tcl_Obj **oldStackPtr = eePtr->stackPtr;
- /*
- * We keep the stack reference count as a (char *), as that
- * works nicely as a portable pointer-sized counter.
- */
- char *refCount = (char *) oldStackPtr[-1];
- /*
- * Copy the existing stack items to the new stack space, free the old
- * storage if appropriate, and record the refCount of the new stack
- * held by the environment.
- */
-
- newStackPtr++;
- memcpy((VOID *) newStackPtr, (VOID *) oldStackPtr,
- (size_t) currBytes);
- if (refCount == (char *) 1) {
- ckfree((VOID *) (oldStackPtr-1));
- } else {
- /*
- * Remove the reference corresponding to the
- * environment pointer.
- */
-
- oldStackPtr[-1] = (Tcl_Obj *) (refCount-1);
- }
- eePtr->stackPtr = newStackPtr;
- eePtr->stackEnd = (newElems - 2); /* index of last usable item */
- newStackPtr[-1] = (Tcl_Obj *) ((char *) 1);
- }
- /*
- *--------------------------------------------------------------
- *
- * 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(interp, objPtr, resultPtrPtr)
- 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. */
- {
- Interp *iPtr = (Interp *) interp;
- CompileEnv compEnv; /* Compilation environment structure
- * allocated in frame. */
- LiteralTable *localTablePtr = &(compEnv.localLitTable);
- register ByteCode *codePtr = NULL;
- /* Tcl Internal type of bytecode.
- * Initialized to avoid compiler warning. */
- AuxData *auxDataPtr;
- LiteralEntry *entryPtr;
- Tcl_Obj *saveObjPtr;
- char *string;
- int length, i, result;
- /*
- * First handle some common expressions specially.
- */
- string = Tcl_GetStringFromObj(objPtr, &length);
- if (length == 1) {
- if (*string == '0') {
- *resultPtrPtr = Tcl_NewLongObj(0);
- Tcl_IncrRefCount(*resultPtrPtr);
- return TCL_OK;
- } else if (*string == '1') {
- *resultPtrPtr = Tcl_NewLongObj(1);
- Tcl_IncrRefCount(*resultPtrPtr);
- return TCL_OK;
- }
- } else if ((length == 2) && (*string == '!')) {
- if (*(string+1) == '0') {
- *resultPtrPtr = Tcl_NewLongObj(1);
- Tcl_IncrRefCount(*resultPtrPtr);
- return TCL_OK;
- } else if (*(string+1) == '1') {
- *resultPtrPtr = Tcl_NewLongObj(0);
- Tcl_IncrRefCount(*resultPtrPtr);
- return TCL_OK;
- }
- }
- /*
- * Get the ByteCode from the object. If it exists, make sure it hasn't
- * been invalidated by, e.g., someone redefining a command with a
- * compile procedure (this might make the compiled code wrong). If
- * necessary, convert the object to be a ByteCode object and compile it.
- * Also, if the code was compiled in/for a different interpreter, we
- * recompile it.
- *
- * Precompiled expressions, however, are immutable and therefore
- * they are not recompiled, even if the epoch has changed.
- *
- */
- if (objPtr->typePtr == &tclByteCodeType) {
- codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr;
- if (((Interp *) *codePtr->interpHandle != iPtr)
- || (codePtr->compileEpoch != iPtr->compileEpoch)) {
- if (codePtr->flags & TCL_BYTECODE_PRECOMPILED) {
- if ((Interp *) *codePtr->interpHandle != iPtr) {
- panic("Tcl_ExprObj: compiled expression jumped interps");
- }
- codePtr->compileEpoch = iPtr->compileEpoch;
- } else {
- (*tclByteCodeType.freeIntRepProc)(objPtr);
- objPtr->typePtr = (Tcl_ObjType *) NULL;
- }
- }
- }
- if (objPtr->typePtr != &tclByteCodeType) {
- #ifndef TCL_TIP280
- TclInitCompileEnv(interp, &compEnv, string, length);
- #else
- /* TIP #280 : No invoker (yet) - Expression compilation */
- TclInitCompileEnv(interp, &compEnv, string, length, NULL, 0);
- #endif
- result = TclCompileExpr(interp, string, length, &compEnv);
- /*
- * Free the compilation environment's literal table bucket array if
- * it was dynamically allocated.
- */
- if (localTablePtr->buckets != localTablePtr->staticBuckets) {
- ckfree((char *) localTablePtr->buckets);
- }
-
- if (result != TCL_OK) {
- /*
- * Compilation errors. Free storage allocated for compilation.
- */
- #ifdef TCL_COMPILE_DEBUG
- TclVerifyLocalLiteralTable(&compEnv);
- #endif /*TCL_COMPILE_DEBUG*/
- entryPtr = compEnv.literalArrayPtr;
- for (i = 0; i < compEnv.literalArrayNext; i++) {
- TclReleaseLiteral(interp, entryPtr->objPtr);
- entryPtr++;
- }
- #ifdef TCL_COMPILE_DEBUG
- TclVerifyGlobalLiteralTable(iPtr);
- #endif /*TCL_COMPILE_DEBUG*/
-
- auxDataPtr = compEnv.auxDataArrayPtr;
- for (i = 0; i < compEnv.auxDataArrayNext; i++) {
- if (auxDataPtr->type->freeProc != NULL) {
- auxDataPtr->type->freeProc(auxDataPtr->clientData);
- }
- auxDataPtr++;
- }
- TclFreeCompileEnv(&compEnv);
- return result;
- }
- /*
- * Successful compilation. If the expression yielded no
- * instructions, push an zero object as the expression's result.
- */
-
- if (compEnv.codeNext == compEnv.codeStart) {
- TclEmitPush(TclRegisterLiteral(&compEnv, "0", 1, /*onHeap*/ 0),
- &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.
- */
- compEnv.numSrcBytes = iPtr->termOffset;
- TclEmitOpcode(INST_DONE, &compEnv);
- TclInitByteCodeObj(objPtr, &compEnv);
- TclFreeCompileEnv(&compEnv);
- codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr;
- #ifdef TCL_COMPILE_DEBUG
- if (tclTraceCompile == 2) {
- TclPrintByteCodeObj(interp, objPtr);
- }
- #endif /* TCL_COMPILE_DEBUG */
- }
- /*
- * Execute the expression after first saving the interpreter's result.
- */
-
- saveObjPtr = Tcl_GetObjResult(interp);
- Tcl_IncrRefCount(saveObjPtr);
- Tcl_ResetResult(interp);
- /*
- * Increment the code's ref count while it is being executed. If
- * afterwards no references to it remain, free the code.
- */
-
- codePtr->refCount++;
- result = TclExecuteByteCode(interp, codePtr);
- codePtr->refCount--;
- if (codePtr->refCount <= 0) {
- TclCleanupByteCode(codePtr);
- objPtr->typePtr = NULL;
- objPtr->internalRep.otherValuePtr = NULL;
- }
-
- /*
- * If the expression evaluated successfully, store a pointer to its
- * value object in resultPtrPtr then restore the old interpreter result.
- * We increment the object's ref count to reflect the reference that we
- * are returning to the caller. We also decrement the ref count of the
- * interpreter's result object after calling Tcl_SetResult since we
- * next store into that field directly.
- */
-
- if (result == TCL_OK) {
- *resultPtrPtr = iPtr->objResultPtr;
- Tcl_IncrRefCount(iPtr->objResultPtr);
-
- Tcl_SetObjResult(interp, saveObjPtr);
- }
- TclDecrRefCount(saveObjPtr);
- return result;
- }
- /*
- *----------------------------------------------------------------------
- *
- * TclCompEvalObj --
- *
- * This procedure evaluates the script contained in a Tcl_Obj by
- * first compiling it and then passing it to TclExecuteByteCode.
- *
- * 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.
- *
- *----------------------------------------------------------------------
- */
- int
- #ifndef TCL_TIP280
- TclCompEvalObj(interp, objPtr)
- #else
- TclCompEvalObj(interp, objPtr, invoker, word)
- #endif
- Tcl_Interp *interp;
- Tcl_Obj *objPtr;
- #ifdef TCL_TIP280
- CONST CmdFrame* invoker; /* Frame of the command doing the eval */
- int word; /* Index of the word which is in objPtr */
- #endif
- {
- register Interp *iPtr = (Interp *) interp;
- register ByteCode* codePtr; /* Tcl Internal type of bytecode. */
- int oldCount = iPtr->cmdCount; /* Used to tell whether any commands
- * at all were executed. */
- char *script;
- int numSrcBytes;
- int result;
- Namespace *namespacePtr;
- /*
- * Check that the interpreter is ready to execute scripts
- */
- iPtr->numLevels++;
- if (TclInterpReady(interp) == TCL_ERROR) {
- iPtr->numLevels--;
- return TCL_ERROR;
- }
- if (iPtr->varFramePtr != NULL) {
- namespacePtr = iPtr->varFramePtr->nsPtr;
- } else {
- namespacePtr = iPtr->globalNsPtr;
- }
- /*
- * 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.
- */
- if (objPtr->typePtr != &tclByteCodeType) {
- recompileObj:
- iPtr->errorLine = 1;
- #ifdef TCL_TIP280
- /* 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.
- */
- iPtr->invokeCmdFramePtr = invoker;
- iPtr->invokeWord = word;
- #endif
- result = tclByteCodeType.setFromAnyProc(interp, objPtr);
- #ifdef TCL_TIP280
- iPtr->invokeCmdFramePtr = NULL;
- #endif
- if (result != TCL_OK) {
- iPtr->numLevels--;
- return result;
- }
- codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr;
- } else {
- /*
- * 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 = (ByteCode *) objPtr->internalRep.otherValuePtr;
- if (((Interp *) *codePtr->interpHandle != iPtr)
- || (codePtr->compileEpoch != iPtr->compileEpoch)
- #ifdef CHECK_PROC_ORIGINATION /* [Bug: 3412 Pedantic] */
- || (codePtr->procPtr != NULL && !(iPtr->varFramePtr &&
- iPtr->varFramePtr->procPtr == codePtr->procPtr))
- #endif
- || (codePtr->nsPtr != namespacePtr)
- || (codePtr->nsEpoch != namespacePtr->resolverEpoch)) {
- if (codePtr->flags & TCL_BYTECODE_PRECOMPILED) {
- if ((Interp *) *codePtr->interpHandle != iPtr) {
- panic("Tcl_EvalObj: compiled script jumped interps");
- }
- codePtr->compileEpoch = iPtr->compileEpoch;
- } else {
- /*
- * This byteCode is invalid: free it and recompile
- */
- tclByteCodeType.freeIntRepProc(objPtr);
- goto recompileObj;
- }
- }
- }
- /*
- * Execute the commands. If the code was compiled from an empty string,
- * don't bother executing the code.
- */
- numSrcBytes = codePtr->numSrcBytes;
- if ((numSrcBytes > 0) || (codePtr->flags & TCL_BYTECODE_PRECOMPILED)) {
- /*
- * Increment the code's ref count while it is being executed. If
- * afterwards no references to it remain, free the code.
- */
-
- codePtr->refCount++;
- result = TclExecuteByteCode(interp, codePtr);
- codePtr->refCount--;
- if (codePtr->refCount <= 0) {
- TclCleanupByteCode(codePtr);
- }
- } else {
- result = TCL_OK;
- }
- iPtr->numLevels--;
- /*
- * If no commands at all were executed, check for asynchronous
- * handlers so that they at least get one change to execute.
- * This is needed to handle event loops written in Tcl with
- * empty bodies.
- */
- if ((oldCount == iPtr->cmdCount) && Tcl_AsyncReady()) {
- result = Tcl_AsyncInvoke(interp, result);
-
- /*
- * If an error occurred, record information about what was being
- * executed when the error occurred.
- */
-
- if ((result == TCL_ERROR) && !(iPtr->flags & ERR_ALREADY_LOGGED)) {
- script = Tcl_GetStringFromObj(objPtr, &numSrcBytes);
- Tcl_LogCommandInfo(interp, script, script, numSrcBytes);
- }
- }
- /*
- * Set the interpreter's termOffset member to the offset of the
- * character just after the last one executed. We approximate the offset
- * of the last character executed by using the number of characters
- * compiled.
- */
- iPtr->termOffset = numSrcBytes;
- iPtr->flags &= ~ERR_ALREADY_LOGGED;
- return result;
- }
- /*
- *----------------------------------------------------------------------
- *
- * TclExecuteByteCode --
- *
- * 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.
- *
- *----------------------------------------------------------------------
- */
-
- static int
- TclExecuteByteCode(interp, codePtr)
- 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(s). */
- 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 storeFlags;
- Tcl_Obj *valuePtr, *value2Ptr, *objPtr;
- char *bytes;
- int length;
- long i = 0; /* Init. avoids compiler warning. */
- Tcl_WideInt w;
- register int cleanup;
- Tcl_Obj *objResultPtr;
- char *part1, *part2;
- Var *varPtr, *arrayPtr;
- CallFrame *varFramePtr = iPtr->varFramePtr;
- #ifdef TCL_TIP280
- /* TIP #280 : Structures for tracking lines */
- CmdFrame bcFrame;
- #endif
- #ifdef TCL_COMPILE_DEBUG
- int traceInstructions = (tclTraceExec == 3);
- char cmdNameBuf[21];
- #endif
- char *curInstName = NULL;
- /*
- * 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.
- */
- #define STATIC_CATCH_STACK_SIZE 4
- int (catchStackStorage[STATIC_CATCH_STACK_SIZE]);
- int *catchStackPtr = catchStackStorage;
- int catchTop = -1;
- #ifdef TCL_TIP280
- /* TIP #280 : Initialize the frame. Do not push it yet. */
- bcFrame.type = ((codePtr->flags & TCL_BYTECODE_PRECOMPILED)
- ? TCL_LOCATION_PREBC
- : TCL_LOCATION_BC);
- bcFrame.level = (iPtr->cmdFramePtr == NULL ?
- 1 :
- iPtr->cmdFramePtr->level + 1);
- bcFrame.framePtr = iPtr->framePtr;
- bcFrame.nextPtr = iPtr->cmdFramePtr;
- bcFrame.nline = 0;
- bcFrame.line = NULL;
- bcFrame.data.tebc.codePtr = codePtr;
- bcFrame.data.tebc.pc = NULL;
- bcFrame.cmd.str.cmd = NULL;
- bcFrame.cmd.str.len = 0;
- #endif
- #ifdef TCL_COMPILE_DEBUG
- if (tclTraceExec >= 2) {
- PrintByteCodeInfo(codePtr);
- fprintf(stdout, " Starting stack top=%dn", eePtr->stackTop);
- fflush(stdout);
- }
- opnd = 0; /* Init. avoids compiler warning. */
- #endif
-
- #ifdef TCL_COMPILE_STATS
- iPtr->stats.numExecutions++;
- #endif
- /*
- * 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.
- */
- if (codePtr->maxExceptDepth > STATIC_CATCH_STACK_SIZE) {
- catchStackPtr = (int *)
- ckalloc(codePtr->maxExceptDepth * sizeof(int));
- }
- /*
- * Make sure the stack has enough room to execute this ByteCode.
- */
- while ((stackTop + codePtr->maxStackDepth) > eePtr->stackEnd) {
- GrowEvaluationStack(eePtr);
- stackPtr = eePtr->stackPtr;
- }
- /*
- * Loop executing instructions until a "done" instruction, a
- * TCL_RETURN, or some error.
- */
- goto cleanup0;
-
- /*
- * 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.
- */
-
- cleanupV_pushObjResultPtr:
- switch (cleanup) {
- case 0:
- stackPtr[++stackTop] = (objResultPtr);
- goto cleanup0;
- default:
- cleanup -= 2;
- while (cleanup--) {
- valuePtr = POP_OBJECT();
- TclDecrRefCount(valuePtr);
- }
- case 2:
- cleanup2_pushObjResultPtr:
- valuePtr = POP_OBJECT();
- TclDecrRefCount(valuePtr);
- case 1:
- cleanup1_pushObjResultPtr:
- valuePtr = stackPtr[stackTop];
- TclDecrRefCount(valuePtr);
- }
- stackPtr[stackTop] = objResultPtr;
- goto cleanup0;
-
- cleanupV:
- switch (cleanup) {
- default:
- cleanup -= 2;
- while (cleanup--) {
- valuePtr = POP_OBJECT();
- TclDecrRefCount(valuePtr);
- }
- case 2:
- cleanup2:
- valuePtr = POP_OBJECT();
- TclDecrRefCount(valuePtr);
- case 1:
- cleanup1:
- valuePtr = POP_OBJECT();
- TclDecrRefCount(valuePtr);
- case 0:
- /*
- * We really want to do nothing now, but this is needed
- * for some compilers (SunPro CC)
- */
- break;
- }
- cleanup0:
-
- #ifdef TCL_COMPILE_DEBUG
- ValidatePcAndStackTop(codePtr, pc, stackTop, initStackTop);
- if (traceInstructions) {
- fprintf(stdout, "%2d: %2d ", iPtr->numLevels, stackTop);
- TclPrintInstruction(codePtr, pc);
- fflush(stdout);
- }
- #endif /* TCL_COMPILE_DEBUG */
-
- #ifdef TCL_COMPILE_STATS
- iPtr->stats.instructionCount[*pc]++;
- #endif
- TCL_DTRACE_INST_NEXT();
- switch (*pc) {
- case INST_DONE:
- if (stackTop <= initStackTop) {
- stackTop--;
- goto abnormalReturn;
- }
-
- /*
- * 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".
- */
- valuePtr = stackPtr[stackTop];
- Tcl_SetObjResult(interp, valuePtr);
- #ifdef TCL_COMPILE_DEBUG
- TRACE_WITH_OBJ(("=> return code=%d, result=", result),
- iPtr->objResultPtr);
- if (traceInstructions) {
- fprintf(stdout, "n");
- }
- #endif
- goto checkForCatch;
-
- case INST_PUSH1:
- objResultPtr = codePtr->objArrayPtr[TclGetUInt1AtPtr(pc+1)];
- TRACE_WITH_OBJ(("%u => ", TclGetInt1AtPtr(pc+1)), objResultPtr);
- NEXT_INST_F(2, 0, 1);
- 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 "), stackPtr[stackTop]);
- valuePtr = POP_OBJECT();
- TclDecrRefCount(valuePtr);
- NEXT_INST_F(1, 0, 0);
-
- case INST_DUP:
- objResultPtr = stackPtr[stackTop];
- TRACE_WITH_OBJ(("=> "), objResultPtr);
- NEXT_INST_F(1, 0, 1);
- case INST_OVER:
- opnd = TclGetUInt4AtPtr( pc+1 );
- objResultPtr = stackPtr[ stackTop - opnd ];
- TRACE_WITH_OBJ(("=> "), objResultPtr);
- NEXT_INST_F(5, 0, 1);
- case INST_CONCAT1:
- opnd = TclGetUInt1AtPtr(pc+1);
- {
- int totalLen = 0;
-
- /*
- * Peephole optimisation for appending an empty string.
- * This enables replacing 'K $x [set x{}]' by '$x[set x{}]'
- * for fastest execution. Avoid doing the optimisation for wide
- * ints - a case where equal strings may refer to different values
- * (see [Bug 1251791]).
- */
- if ((opnd == 2) && (stackPtr[stackTop-1]->typePtr != &tclWideIntType)) {
- Tcl_GetStringFromObj(stackPtr[stackTop], &length);
- if (length == 0) {
- /* Just drop the top item from the stack */
- NEXT_INST_F(2, 1, 0);
- }
- }
- /*
- * 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.
- */
- for (i = (stackTop - (opnd-1)); i <= stackTop; i++) {
- bytes = Tcl_GetStringFromObj(stackPtr[i], &length);
- if (bytes != NULL) {
- totalLen += length;
- }
- }
- /*
- * Initialize the new append string object by appending the
- * strings of the opnd stack objects. Also pop the objects.
- */
- TclNewObj(objResultPtr);
- if (totalLen > 0) {
- char *p = (char *) ckalloc((unsigned) (totalLen + 1));
- objResultPtr->bytes = p;
- objResultPtr->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;
- }
- }
- *p = ' ';
- }
-
- TRACE_WITH_OBJ(("%u => ", opnd), objResultPtr);
- NEXT_INST_V(2, opnd, 1);
- }
-
- 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. */
- /*
- * We keep the stack reference count as a (char *), as that
- * works nicely as a portable pointer-sized counter.
- */
- char **preservedStackRefCountPtr;
-
- /*
- * Reference to memory block containing
- * objv array (must be kept live throughout
- * trace and command invokations.)
- */
- objv = &(stackPtr[stackTop - (objc-1)]);
- #ifdef TCL_COMPILE_DEBUG
- if (tclTraceExec >= 2) {
- if (traceInstructions) {
- strncpy(cmdNameBuf, TclGetString(objv[0]), 20);
- TRACE(("%u => call ", objc));
- } else {
- fprintf(stdout, "%d: (%u) invoking ",
- iPtr->numLevels,
- (unsigned int)(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*/
- /*
- * If trace procedures will be called, we need a
- * command string to pass to TclEvalObjvInternal; note
- * that a copy of the string will be made there to
- * include the ending