📄 tclexecute.c
字号:
/* * 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 2003/02/19 14:33:39 msofer 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_Hint 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_DEBUGstatic 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_STATSlong tclObjsAlloced = 0;long tclObjsFreed = 0;#define TCL_MAX_SHARED_OBJ_STATS 5long 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 *//* * Most of the code to support working with wide values is factored * out here because it greatly reduces the number of conditionals * through the rest of the file. Note that this needs to be * conditional because we do not want to alter Tcl's behaviour on * native-64bit platforms... */#ifndef TCL_WIDE_INT_IS_LONG#define W0 Tcl_LongAsWide(0)/* * 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); \ }#define IS_INTEGER_TYPE(typePtr) \ ((typePtr) == &tclIntType || (typePtr) == &tclWideIntType)/* * 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; \ }/* * 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); \ }/* * For tracing that uses wide values. */#define LLTRACE(a) TRACE(a)#define LLTRACE_WITH_OBJ(a,b) TRACE_WITH_OBJ(a,b)#define LLD "%" TCL_LL_MODIFIER "d"#else /* TCL_WIDE_INT_IS_LONG *//* * Versions of the above that do not use wide values. */#define REQUIRE_WIDE_OR_INT(resultVar, objPtr, longVar, wideVar) \ (resultVar) = Tcl_GetLongFromObj(interp, (objPtr), &(longVar));#define GET_WIDE_OR_INT(resultVar, objPtr, longVar, wideVar) \ (resultVar) = Tcl_GetLongFromObj((Tcl_Interp *) NULL, (objPtr), \ &(longVar));#define IS_INTEGER_TYPE(typePtr) ((typePtr) == &tclIntType)#define GET_DOUBLE_VALUE(doubleVar, objPtr, typePtr) \ if ((typePtr) == &tclIntType) { \ (doubleVar) = (double) (objPtr)->internalRep.longValue; \ } else { \ (doubleVar) = (objPtr)->internalRep.doubleValue; \ }#define FORCE_LONG(objPtr, longVar, wideVar)#define LLTRACE(a)#define LLTRACE_WITH_OBJ(a,b)#endif /* TCL_WIDE_INT_IS_LONG */#define IS_NUMERIC_TYPE(typePtr) \ (IS_INTEGER_TYPE(typePtr) || (typePtr) == &tclDoubleType)/* * 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));#ifndef TCL_WIDE_INT_IS_LONGstatic int ExprWideFunc _ANSI_ARGS_((Tcl_Interp *interp, ExecEnv *eePtr, ClientData clientData));#endif /* TCL_WIDE_INT_IS_LONG */#ifdef TCL_COMPILE_STATSstatic int EvalStatsCmd _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));#endif /* TCL_COMPILE_STATS */#ifdef TCL_COMPILE_DEBUGstatic 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_DEBUGstatic 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},
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -