📄 tclexecute.c
字号:
/* * tclExecute.c -- * * This file contains procedures that execute byte-compiled Tcl * commands. * * Copyright (c) 1996-1997 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * SCCS: @(#) tclExecute.c 1.3 98/07/01 17:57:42 */#include "tclInt.h"#include "tclCompile.h"#ifdef NO_FLOAT_H# include "../compat/float.h"#else# include <float.h>#endif#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#define NO_ERRNO_H#endif#ifdef NO_ERRNO_Hint errno;#define EDOM 33#define ERANGE 34#endif/* * Boolean flag indicating whether the Tcl bytecode interpreter has been * initialized. */static int execInitialized = 0;/* * 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;/* * 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 tcl_MathInProgress = 0;/* * 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_MATHERRextern int matherr();int (*tclMatherrPtr)() = matherr;#endif/* * Array of instruction names. */static char *opName[256];/* * 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"}; /* * Mapping from Tcl result codes to strings; used for error and debugging * messages. */#ifdef TCL_COMPILE_DEBUGstatic char *resultStrings[] = { "TCL_OK", "TCL_ERROR", "TCL_RETURN", "TCL_BREAK", "TCL_CONTINUE"};#endif /* TCL_COMPILE_DEBUG *//* * The following are statistics-related variables that record information * about the bytecode compiler and interpreter's operation. This includes * an array that records for each instruction how often it is executed. */#ifdef TCL_COMPILE_STATSstatic long numExecutions = 0;static int instructionCount[256];#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))#ifdef DBL_MAX# define IS_INF(v) (((v) > DBL_MAX) || ((v) < -DBL_MAX))#else# define IS_INF(v) 0#endif/* * Macro to adjust the program counter and restart the instruction execution * loop after each instruction is executed. */#define ADJUST_PC(instBytes) \ pc += instBytes; continue/* * 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. */#define STK_ITEM(offset) (stackPtr[stackTop + (offset)])#define STK_OBJECT(offset) (STK_ITEM(offset).o)#define STK_INT(offset) (STK_ITEM(offset).i)#define STK_POINTER(offset) (STK_ITEM(offset).p)/* * 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].o = (objPtr)) #define POP_OBJECT() \ (stackPtr[stackTop--].o)/* * 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. * * NOTE THAT CLIENTS OF O2S ARE LIKELY TO FAIL IF THE OBJECT'S * STRING REP CONTAINS NULLS. */#ifdef TCL_COMPILE_DEBUG #define O2S(objPtr) \ Tcl_GetStringFromObj((objPtr), &length) #ifdef TCL_COMPILE_STATS#define TRACE(a) \ if (traceInstructions) { \ fprintf(stdout, "%d: %d,%ld (%u) ", iPtr->numLevels, \ stackTop, (tclObjsAlloced - tclObjsFreed), \ (unsigned int)(pc - codePtr->codeStart)); \ printf a; \ fflush(stdout); \ }#define TRACE_WITH_OBJ(a, objPtr) \ if (traceInstructions) { \ fprintf(stdout, "%d: %d,%ld (%u) ", iPtr->numLevels, \ stackTop, (tclObjsAlloced - tclObjsFreed), \ (unsigned int)(pc - codePtr->codeStart)); \ printf a; \ bytes = Tcl_GetStringFromObj((objPtr), &length); \ TclPrintSource(stdout, bytes, TclMin(length, 30)); \ fprintf(stdout, "\n"); \ fflush(stdout); \ }#else /* not TCL_COMPILE_STATS */#define TRACE(a) \ if (traceInstructions) { \ fprintf(stdout, "%d: %d (%u) ", iPtr->numLevels, stackTop, \ (unsigned int)(pc - codePtr->codeStart)); \ printf a; \ fflush(stdout); \ }#define TRACE_WITH_OBJ(a, objPtr) \ if (traceInstructions) { \ fprintf(stdout, "%d: %d (%u) ", iPtr->numLevels, stackTop, \ (unsigned int)(pc - codePtr->codeStart)); \ printf a; \ bytes = Tcl_GetStringFromObj((objPtr), &length); \ TclPrintSource(stdout, bytes, TclMin(length, 30)); \ fprintf(stdout, "\n"); \ fflush(stdout); \ }#endif /* TCL_COMPILE_STATS */#else /* not TCL_COMPILE_DEBUG */ #define TRACE(a)#define TRACE_WITH_OBJ(a, objPtr)#define O2S(objPtr) #endif /* TCL_COMPILE_DEBUG *//* * 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_STATSstatic int EvalStatsCmd _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, int argc, char **argv));#endif /* TCL_COMPILE_STATS */static void FreeCmdNameInternalRep _ANSI_ARGS_(( Tcl_Obj *objPtr));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 int opCode, Tcl_Obj *opndPtr));static void InitByteCodeExecution _ANSI_ARGS_(( Tcl_Interp *interp));static void PrintByteCodeInfo _ANSI_ARGS_((ByteCode *codePtr));static void RecordTracebackInfo _ANSI_ARGS_((Tcl_Interp *interp, unsigned char *pc, ByteCode *codePtr));static int SetCmdNameFromAny _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Obj *objPtr));#ifdef TCL_COMPILE_DEBUGstatic char * StringForResultCode _ANSI_ARGS_((int result));#endif /* TCL_COMPILE_DEBUG */static void UpdateStringOfCmdName _ANSI_ARGS_((Tcl_Obj *objPtr));#ifdef TCL_COMPILE_DEBUGstatic void ValidatePcAndStackTop _ANSI_ARGS_(( ByteCode *codePtr, unsigned char *pc, int stackTop, int stackLowerBound, int stackUpperBound));#endif /* TCL_COMPILE_DEBUG *//* * 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 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},};/* * 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. */Tcl_ObjType tclCmdNameType = { "cmdName", /* name */ FreeCmdNameInternalRep, /* freeIntRepProc */ DupCmdNameInternalRep, /* dupIntRepProc */ UpdateStringOfCmdName, /* updateStringProc */ SetCmdNameFromAny /* setFromAnyProc */};/* *---------------------------------------------------------------------- * * 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 registers the command name * Tcl_ObjType. It also establishes the link between the Tcl * "tcl_traceExec" and C "tclTraceExec" variables. * *---------------------------------------------------------------------- */static voidInitByteCodeExecution(interp) Tcl_Interp *interp; /* Interpreter for which the Tcl variable * "tcl_traceExec" is linked to control * instruction tracing. */{ int i; Tcl_RegisterObjType(&tclCmdNameType); (VOID *) memset(opName, 0, sizeof(opName)); for (i = 0; instructionTable[i].name != NULL; i++) { opName[i] = instructionTable[i].name; }#ifdef TCL_COMPILE_STATS (VOID *) memset(instructionCount, 0, sizeof(instructionCount)); (VOID *) memset(tclByteCodeCount, 0, sizeof(tclByteCodeCount)); (VOID *) memset(tclSourceCount, 0, sizeof(tclSourceCount)); Tcl_CreateCommand(interp, "evalstats", EvalStatsCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);#endif /* TCL_COMPILE_STATS */ if (Tcl_LinkVar(interp, "tcl_traceExec", (char *) &tclTraceExec, TCL_LINK_INT) != TCL_OK) { panic("InitByteCodeExecution: can't create link for tcl_traceExec variable"); }}/* *---------------------------------------------------------------------- * * 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.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -