tclExecute.cGo to the documentation of this file.00001 /* 00002 * tclExecute.c -- 00003 * 00004 * This file contains procedures that execute byte-compiled Tcl commands. 00005 * 00006 * Copyright (c) 1996-1997 Sun Microsystems, Inc. 00007 * Copyright (c) 1998-2000 by Scriptics Corporation. 00008 * Copyright (c) 2001 by Kevin B. Kenny. All rights reserved. 00009 * Copyright (c) 2002-2005 by Miguel Sofer. 00010 * Copyright (c) 2005-2007 by Donal K. Fellows. 00011 * Copyright (c) 2007 Daniel A. Steffen <das@users.sourceforge.net> 00012 * 00013 * See the file "license.terms" for information on usage and redistribution of 00014 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 00015 * 00016 * RCS: @(#) $Id: tclExecute.c,v 1.363 2008/02/04 20:24:55 msofer Exp $ 00017 */ 00018 00019 #include "tclInt.h" 00020 #include "tclCompile.h" 00021 #include "tommath.h" 00022 00023 #include <math.h> 00024 #include <float.h> 00025 00026 /* 00027 * Hack to determine whether we may expect IEEE floating point. The hack is 00028 * formally incorrect in that non-IEEE platforms might have the same precision 00029 * and range, but VAX, IBM, and Cray do not; are there any other floating 00030 * point units that we might care about? 00031 */ 00032 00033 #if (FLT_RADIX == 2) && (DBL_MANT_DIG == 53) && (DBL_MAX_EXP == 1024) 00034 #define IEEE_FLOATING_POINT 00035 #endif 00036 00037 /* 00038 * A mask (should be 2**n-1) that is used to work out when the bytecode engine 00039 * should call Tcl_AsyncReady() to see whether there is a signal that needs 00040 * handling. 00041 */ 00042 00043 #ifndef ASYNC_CHECK_COUNT_MASK 00044 # define ASYNC_CHECK_COUNT_MASK 63 00045 #endif /* !ASYNC_CHECK_COUNT_MASK */ 00046 00047 /* 00048 * Boolean flag indicating whether the Tcl bytecode interpreter has been 00049 * initialized. 00050 */ 00051 00052 static int execInitialized = 0; 00053 TCL_DECLARE_MUTEX(execMutex) 00054 00055 #ifdef TCL_COMPILE_DEBUG 00056 /* 00057 * Variable that controls whether execution tracing is enabled and, if so, 00058 * what level of tracing is desired: 00059 * 0: no execution tracing 00060 * 1: trace invocations of Tcl procs only 00061 * 2: trace invocations of all (not compiled away) commands 00062 * 3: display each instruction executed 00063 * This variable is linked to the Tcl variable "tcl_traceExec". 00064 */ 00065 00066 int tclTraceExec = 0; 00067 #endif 00068 00069 /* 00070 * Mapping from expression instruction opcodes to strings; used for error 00071 * messages. Note that these entries must match the order and number of the 00072 * expression opcodes (e.g., INST_LOR) in tclCompile.h. 00073 * 00074 * Does not include the string for INST_EXPON (and beyond), as that is 00075 * disjoint for backward-compatability reasons. 00076 */ 00077 00078 static const char *operatorStrings[] = { 00079 "||", "&&", "|", "^", "&", "==", "!=", "<", ">", "<=", ">=", "<<", ">>", 00080 "+", "-", "*", "/", "%", "+", "-", "~", "!", 00081 "BUILTIN FUNCTION", "FUNCTION", 00082 "", "", "", "", "", "", "", "", "eq", "ne" 00083 }; 00084 00085 /* 00086 * Mapping from Tcl result codes to strings; used for error and debugging 00087 * messages. 00088 */ 00089 00090 #ifdef TCL_COMPILE_DEBUG 00091 static const char *resultStrings[] = { 00092 "TCL_OK", "TCL_ERROR", "TCL_RETURN", "TCL_BREAK", "TCL_CONTINUE" 00093 }; 00094 #endif 00095 00096 /* 00097 * These are used by evalstats to monitor object usage in Tcl. 00098 */ 00099 00100 #ifdef TCL_COMPILE_STATS 00101 long tclObjsAlloced = 0; 00102 long tclObjsFreed = 0; 00103 long tclObjsShared[TCL_MAX_SHARED_OBJ_STATS] = { 0, 0, 0, 0, 0 }; 00104 #endif /* TCL_COMPILE_STATS */ 00105 00106 /* 00107 * Support pre-8.5 bytecodes unless specifically requested otherwise. 00108 */ 00109 00110 #ifndef TCL_SUPPORT_84_BYTECODE 00111 #define TCL_SUPPORT_84_BYTECODE 1 00112 #endif 00113 00114 #if TCL_SUPPORT_84_BYTECODE 00115 /* 00116 * We need to know the tclBuiltinFuncTable to support translation of pre-8.5 00117 * math functions to the namespace-based ::tcl::mathfunc::op in 8.5+. 00118 */ 00119 00120 typedef struct { 00121 char *name; /* Name of function. */ 00122 int numArgs; /* Number of arguments for function. */ 00123 } BuiltinFunc; 00124 00125 /* 00126 * Table describing the built-in math functions. Entries in this table are 00127 * indexed by the values of the INST_CALL_BUILTIN_FUNC instruction's 00128 * operand byte. 00129 */ 00130 00131 static BuiltinFunc tclBuiltinFuncTable[] = { 00132 {"acos", 1}, 00133 {"asin", 1}, 00134 {"atan", 1}, 00135 {"atan2", 2}, 00136 {"ceil", 1}, 00137 {"cos", 1}, 00138 {"cosh", 1}, 00139 {"exp", 1}, 00140 {"floor", 1}, 00141 {"fmod", 2}, 00142 {"hypot", 2}, 00143 {"log", 1}, 00144 {"log10", 1}, 00145 {"pow", 2}, 00146 {"sin", 1}, 00147 {"sinh", 1}, 00148 {"sqrt", 1}, 00149 {"tan", 1}, 00150 {"tanh", 1}, 00151 {"abs", 1}, 00152 {"double", 1}, 00153 {"int", 1}, 00154 {"rand", 0}, 00155 {"round", 1}, 00156 {"srand", 1}, 00157 {"wide", 1}, 00158 {0}, 00159 }; 00160 00161 #define LAST_BUILTIN_FUNC 25 00162 #endif 00163 00164 /* 00165 * These variable-access macros have to coincide with those in tclVar.c 00166 */ 00167 00168 #define VarHashGetValue(hPtr) \ 00169 ((Var *) ((char *)hPtr - TclOffset(VarInHash, entry))) 00170 00171 static inline Var * 00172 VarHashCreateVar( 00173 TclVarHashTable *tablePtr, 00174 Tcl_Obj *key, 00175 int *newPtr) 00176 { 00177 Tcl_HashEntry *hPtr = Tcl_CreateHashEntry((Tcl_HashTable *) tablePtr, 00178 (char *) key, newPtr); 00179 00180 if (!hPtr) { 00181 return NULL; 00182 } 00183 return VarHashGetValue(hPtr); 00184 } 00185 00186 #define VarHashFindVar(tablePtr, key) \ 00187 VarHashCreateVar((tablePtr), (key), NULL) 00188 00189 /* 00190 * The new macro for ending an instruction; note that a reasonable C-optimiser 00191 * will resolve all branches at compile time. (result) is always a constant; 00192 * the macro NEXT_INST_F handles constant (nCleanup), NEXT_INST_V is resolved 00193 * at runtime for variable (nCleanup). 00194 * 00195 * ARGUMENTS: 00196 * pcAdjustment: how much to increment pc 00197 * nCleanup: how many objects to remove from the stack 00198 * resultHandling: 0 indicates no object should be pushed on the stack; 00199 * otherwise, push objResultPtr. If (result < 0), objResultPtr already 00200 * has the correct reference count. 00201 */ 00202 00203 #define NEXT_INST_F(pcAdjustment, nCleanup, resultHandling) \ 00204 if (nCleanup == 0) {\ 00205 if (resultHandling != 0) {\ 00206 if ((resultHandling) > 0) {\ 00207 PUSH_OBJECT(objResultPtr);\ 00208 } else {\ 00209 *(++tosPtr) = objResultPtr;\ 00210 }\ 00211 } \ 00212 pc += (pcAdjustment);\ 00213 goto cleanup0;\ 00214 } else if (resultHandling != 0) {\ 00215 if ((resultHandling) > 0) {\ 00216 Tcl_IncrRefCount(objResultPtr);\ 00217 }\ 00218 pc += (pcAdjustment);\ 00219 switch (nCleanup) {\ 00220 case 1: goto cleanup1_pushObjResultPtr;\ 00221 case 2: goto cleanup2_pushObjResultPtr;\ 00222 default: Tcl_Panic("bad usage of macro NEXT_INST_F");\ 00223 }\ 00224 } else {\ 00225 pc += (pcAdjustment);\ 00226 switch (nCleanup) {\ 00227 case 1: goto cleanup1;\ 00228 case 2: goto cleanup2;\ 00229 default: Tcl_Panic("bad usage of macro NEXT_INST_F");\ 00230 }\ 00231 } 00232 00233 #define NEXT_INST_V(pcAdjustment, nCleanup, resultHandling) \ 00234 pc += (pcAdjustment);\ 00235 cleanup = (nCleanup);\ 00236 if (resultHandling) {\ 00237 if ((resultHandling) > 0) {\ 00238 Tcl_IncrRefCount(objResultPtr);\ 00239 }\ 00240 goto cleanupV_pushObjResultPtr;\ 00241 } else {\ 00242 goto cleanupV;\ 00243 } 00244 00245 /* 00246 * Macros used to cache often-referenced Tcl evaluation stack information 00247 * in local variables. Note that a DECACHE_STACK_INFO()-CACHE_STACK_INFO() 00248 * pair must surround any call inside TclExecuteByteCode (and a few other 00249 * procedures that use this scheme) that could result in a recursive call 00250 * to TclExecuteByteCode. 00251 */ 00252 00253 #define CACHE_STACK_INFO() \ 00254 checkInterp = 1 00255 00256 #define DECACHE_STACK_INFO() \ 00257 esPtr->tosPtr = tosPtr 00258 00259 /* 00260 * Macros used to access items on the Tcl evaluation stack. PUSH_OBJECT 00261 * increments the object's ref count since it makes the stack have another 00262 * reference pointing to the object. However, POP_OBJECT does not decrement 00263 * the ref count. This is because the stack may hold the only reference to the 00264 * object, so the object would be destroyed if its ref count were decremented 00265 * before the caller had a chance to, e.g., store it in a variable. It is the 00266 * caller's responsibility to decrement the ref count when it is finished with 00267 * an object. 00268 * 00269 * WARNING! It is essential that objPtr only appear once in the PUSH_OBJECT 00270 * macro. The actual parameter might be an expression with side effects, and 00271 * this ensures that it will be executed only once. 00272 */ 00273 00274 #define PUSH_OBJECT(objPtr) \ 00275 Tcl_IncrRefCount(*(++tosPtr) = (objPtr)) 00276 00277 #define POP_OBJECT() *(tosPtr--) 00278 00279 #define OBJ_AT_TOS *tosPtr 00280 00281 #define OBJ_UNDER_TOS *(tosPtr-1) 00282 00283 #define OBJ_AT_DEPTH(n) *(tosPtr-(n)) 00284 00285 #define CURR_DEPTH (tosPtr - initTosPtr) 00286 00287 /* 00288 * Macros used to trace instruction execution. The macros TRACE, 00289 * TRACE_WITH_OBJ, and O2S are only used inside TclExecuteByteCode. O2S is 00290 * only used in TRACE* calls to get a string from an object. 00291 */ 00292 00293 #ifdef TCL_COMPILE_DEBUG 00294 # define TRACE(a) \ 00295 if (traceInstructions) { \ 00296 fprintf(stdout, "%2d: %2d (%u) %s ", iPtr->numLevels, \ 00297 (int) CURR_DEPTH, \ 00298 (unsigned)(pc - codePtr->codeStart), \ 00299 GetOpcodeName(pc)); \ 00300 printf a; \ 00301 } 00302 # define TRACE_APPEND(a) \ 00303 if (traceInstructions) { \ 00304 printf a; \ 00305 } 00306 # define TRACE_WITH_OBJ(a, objPtr) \ 00307 if (traceInstructions) { \ 00308 fprintf(stdout, "%2d: %2d (%u) %s ", iPtr->numLevels, \ 00309 (int) CURR_DEPTH, \ 00310 (unsigned)(pc - codePtr->codeStart), \ 00311 GetOpcodeName(pc)); \ 00312 printf a; \ 00313 TclPrintObject(stdout, objPtr, 30); \ 00314 fprintf(stdout, "\n"); \ 00315 } 00316 # define O2S(objPtr) \ 00317 (objPtr ? TclGetString(objPtr) : "") 00318 #else /* !TCL_COMPILE_DEBUG */ 00319 # define TRACE(a) 00320 # define TRACE_APPEND(a) 00321 # define TRACE_WITH_OBJ(a, objPtr) 00322 # define O2S(objPtr) 00323 #endif /* TCL_COMPILE_DEBUG */ 00324 00325 /* 00326 * DTrace instruction probe macros. 00327 */ 00328 00329 #define TCL_DTRACE_INST_NEXT() \ 00330 if (TCL_DTRACE_INST_DONE_ENABLED()) {\ 00331 if (curInstName) {\ 00332 TCL_DTRACE_INST_DONE(curInstName, (int) CURR_DEPTH, tosPtr);\ 00333 }\ 00334 curInstName = tclInstructionTable[*pc].name;\ 00335 if (TCL_DTRACE_INST_START_ENABLED()) {\ 00336 TCL_DTRACE_INST_START(curInstName, (int) CURR_DEPTH, tosPtr);\ 00337 }\ 00338 } else if (TCL_DTRACE_INST_START_ENABLED()) {\ 00339 TCL_DTRACE_INST_START(tclInstructionTable[*pc].name, (int) CURR_DEPTH,\ 00340 tosPtr);\ 00341 } 00342 #define TCL_DTRACE_INST_LAST() \ 00343 if (TCL_DTRACE_INST_DONE_ENABLED() && curInstName) {\ 00344 TCL_DTRACE_INST_DONE(curInstName, (int) CURR_DEPTH, tosPtr);\ 00345 } 00346 00347 /* 00348 * Macro used in this file to save a function call for common uses of 00349 * TclGetNumberFromObj(). The ANSI C "prototype" is: 00350 * 00351 * MODULE_SCOPE int GetNumberFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, 00352 * ClientData *ptrPtr, int *tPtr); 00353 */ 00354 00355 #ifdef NO_WIDE_TYPE 00356 00357 #define GetNumberFromObj(interp, objPtr, ptrPtr, tPtr) \ 00358 (((objPtr)->typePtr == &tclIntType) \ 00359 ? (*(tPtr) = TCL_NUMBER_LONG, \ 00360 *(ptrPtr) = (ClientData) \ 00361 (&((objPtr)->internalRep.longValue)), TCL_OK) : \ 00362 ((objPtr)->typePtr == &tclDoubleType) \ 00363 ? (((TclIsNaN((objPtr)->internalRep.doubleValue)) \ 00364 ? (*(tPtr) = TCL_NUMBER_NAN) \ 00365 : (*(tPtr) = TCL_NUMBER_DOUBLE)), \ 00366 *(ptrPtr) = (ClientData) \ 00367 (&((objPtr)->internalRep.doubleValue)), TCL_OK) : \ 00368 ((((objPtr)->typePtr == NULL) && ((objPtr)->bytes == NULL)) || \ 00369 (((objPtr)->bytes != NULL) && ((objPtr)->length == 0))) \ 00370 ? TCL_ERROR : \ 00371 TclGetNumberFromObj((interp), (objPtr), (ptrPtr), (tPtr))) 00372 00373 #else 00374 00375 #define GetNumberFromObj(interp, objPtr, ptrPtr, tPtr) \ 00376 (((objPtr)->typePtr == &tclIntType) \ 00377 ? (*(tPtr) = TCL_NUMBER_LONG, \ 00378 *(ptrPtr) = (ClientData) \ 00379 (&((objPtr)->internalRep.longValue)), TCL_OK) : \ 00380 ((objPtr)->typePtr == &tclWideIntType) \ 00381 ? (*(tPtr) = TCL_NUMBER_WIDE, \ 00382 *(ptrPtr) = (ClientData) \ 00383 (&((objPtr)->internalRep.wideValue)), TCL_OK) : \ 00384 ((objPtr)->typePtr == &tclDoubleType) \ 00385 ? (((TclIsNaN((objPtr)->internalRep.doubleValue)) \ 00386 ? (*(tPtr) = TCL_NUMBER_NAN) \ 00387 : (*(tPtr) = TCL_NUMBER_DOUBLE)), \ 00388 *(ptrPtr) = (ClientData) \ 00389 (&((objPtr)->internalRep.doubleValue)), TCL_OK) : \ 00390 ((((objPtr)->typePtr == NULL) && ((objPtr)->bytes == NULL)) || \ 00391 (((objPtr)->bytes != NULL) && ((objPtr)->length == 0))) \ 00392 ? TCL_ERROR : \ 00393 TclGetNumberFromObj((interp), (objPtr), (ptrPtr), (tPtr))) 00394 00395 #endif 00396 00397 /* 00398 * Macro used in this file to save a function call for common uses of 00399 * Tcl_GetBooleanFromObj(). The ANSI C "prototype" is: 00400 * 00401 * MODULE_SCOPE int TclGetBooleanFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, 00402 * int *boolPtr); 00403 */ 00404 00405 #define TclGetBooleanFromObj(interp, objPtr, boolPtr) \ 00406 ((((objPtr)->typePtr == &tclIntType) \ 00407 || ((objPtr)->typePtr == &tclBooleanType)) \ 00408 ? (*(boolPtr) = ((objPtr)->internalRep.longValue!=0), TCL_OK) \ 00409 : Tcl_GetBooleanFromObj((interp), (objPtr), (boolPtr))) 00410 00411 /* 00412 * Macro used in this file to save a function call for common uses of 00413 * Tcl_GetWideIntFromObj(). The ANSI C "prototype" is: 00414 * 00415 * MODULE_SCOPE int TclGetWideIntFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, 00416 * Tcl_WideInt *wideIntPtr); 00417 */ 00418 00419 #ifdef NO_WIDE_TYPE 00420 #define TclGetWideIntFromObj(interp, objPtr, wideIntPtr) \ 00421 (((objPtr)->typePtr == &tclIntType) \ 00422 ? (*(wideIntPtr) = (Tcl_WideInt) \ 00423 ((objPtr)->internalRep.longValue), TCL_OK) : \ 00424 Tcl_GetWideIntFromObj((interp), (objPtr), (wideIntPtr))) 00425 #else 00426 #define TclGetWideIntFromObj(interp, objPtr, wideIntPtr) \ 00427 (((objPtr)->typePtr == &tclWideIntType) \ 00428 ? (*(wideIntPtr) = (objPtr)->internalRep.wideValue, TCL_OK) : \ 00429 ((objPtr)->typePtr == &tclIntType) \ 00430 ? (*(wideIntPtr) = (Tcl_WideInt) \ 00431 ((objPtr)->internalRep.longValue), TCL_OK) : \ 00432 Tcl_GetWideIntFromObj((interp), (objPtr), (wideIntPtr))) 00433 #endif 00434 00435 /* 00436 * Macro used to make the check for type overflow more mnemonic. This works by 00437 * comparing sign bits; the rest of the word is irrelevant. The ANSI C 00438 * "prototype" (where inttype_t is any integer type) is: 00439 * 00440 * MODULE_SCOPE int Overflowing(inttype_t a, inttype_t b, inttype_t sum); 00441 * 00442 * Check first the condition most likely to fail in usual code (at least for 00443 * usage in [incr]: do the first summand and the sum have != signs? 00444 */ 00445 00446 #define Overflowing(a,b,sum) ((((a)^(sum)) < 0) && (((a)^(b)) >= 0)) 00447 00448 /* 00449 * Custom object type only used in this file; values of its type should never 00450 * be seen by user scripts. 00451 */ 00452 00453 static Tcl_ObjType dictIteratorType = { 00454 "dictIterator", 00455 NULL, NULL, NULL, NULL 00456 }; 00457 00458 /* 00459 * Auxiliary tables used to compute powers of small integers 00460 */ 00461 00462 #if (LONG_MAX == 0x7fffffff) 00463 00464 /* 00465 * Maximum base that, when raised to powers 2, 3, ... 8, fits in a 32-bit 00466 * signed integer 00467 */ 00468 00469 static const long MaxBase32[7] = {46340, 1290, 215, 73, 35, 21, 14}; 00470 00471 /* 00472 * Table giving 3, 4, ..., 11, raised to the powers 9, 10, ..., as far as they 00473 * fit in a 32-bit signed integer. Exp32Index[i] gives the starting index of 00474 * powers of i+3; Exp32Value[i] gives the corresponding powers. 00475 */ 00476 00477 static const unsigned short Exp32Index[] = { 00478 0, 11, 18, 23, 26, 29, 31, 32, 33 00479 }; 00480 static const long Exp32Value[] = { 00481 19683, 59049, 177147, 531441, 1594323, 4782969, 14348907, 43046721, 00482 129140163, 387420489, 1162261467, 262144, 1048576, 4194304, 00483 16777216, 67108864, 268435456, 1073741824, 1953125, 9765625, 00484 48828125, 244140625, 1220703125, 10077696, 60466176, 362797056, 00485 40353607, 282475249, 1977326743, 134217728, 1073741824, 387420489, 00486 1000000000 00487 }; 00488 00489 #endif /* LONG_MAX == 0x7fffffff -- 32 bit machine */ 00490 00491 #if (LONG_MAX > 0x7fffffff) || !defined(TCL_WIDE_INT_IS_LONG) 00492 00493 /* 00494 * Maximum base that, when raised to powers 2, 3, ..., 16, fits in a 00495 * Tcl_WideInt. 00496 */ 00497 00498 static Tcl_WideInt MaxBaseWide[15]; 00499 00500 /* 00501 *Table giving 3, 4, ..., 13 raised to powers greater than 16 when the 00502 * results fit in a 64-bit signed integer. 00503 */ 00504 00505 static const unsigned short Exp64Index[] = { 00506 0, 23, 38, 49, 57, 63, 67, 70, 72, 74, 75, 76 00507 }; 00508 static const Tcl_WideInt Exp64Value[] = { 00509 (Tcl_WideInt)243*243*243*3*3, 00510 (Tcl_WideInt)243*243*243*3*3*3, 00511 (Tcl_WideInt)243*243*243*3*3*3*3, 00512 (Tcl_WideInt)243*243*243*243, 00513 (Tcl_WideInt)243*243*243*243*3, 00514 (Tcl_WideInt)243*243*243*243*3*3, 00515 (Tcl_WideInt)243*243*243*243*3*3*3, 00516 (Tcl_WideInt)243*243*243*243*3*3*3*3, 00517 (Tcl_WideInt)243*243*243*243*243, 00518 (Tcl_WideInt)243*243*243*243*243*3, 00519 (Tcl_WideInt)243*243*243*243*243*3*3, 00520 (Tcl_WideInt)243*243*243*243*243*3*3*3, 00521 (Tcl_WideInt)243*243*243*243*243*3*3*3*3, 00522 (Tcl_WideInt)243*243*243*243*243*243, 00523 (Tcl_WideInt)243*243*243*243*243*243*3, 00524 (Tcl_WideInt)243*243*243*243*243*243*3*3, 00525 (Tcl_WideInt)243*243*243*243*243*243*3*3*3, 00526 (Tcl_WideInt)243*243*243*243*243*243*3*3*3*3, 00527 (Tcl_WideInt)243*243*243*243*243*243*243, 00528 (Tcl_WideInt)243*243*243*243*243*243*243*3, 00529 (Tcl_WideInt)243*243*243*243*243*243*243*3*3, 00530 (Tcl_WideInt)243*243*243*243*243*243*243*3*3*3, 00531 (Tcl_WideInt)243*243*243*243*243*243*243*3*3*3*3, 00532 (Tcl_WideInt)1024*1024*1024*4*4, 00533 (Tcl_WideInt)1024*1024*1024*4*4*4, 00534 (Tcl_WideInt)1024*1024*1024*4*4*4*4, 00535 (Tcl_WideInt)1024*1024*1024*1024, 00536 (Tcl_WideInt)1024*1024*1024*1024*4, 00537 (Tcl_WideInt)1024*1024*1024*1024*4*4, 00538 (Tcl_WideInt)1024*1024*1024*1024*4*4*4, 00539 (Tcl_WideInt)1024*1024*1024*1024*4*4*4*4, 00540 (Tcl_WideInt)1024*1024*1024*1024*1024, 00541 (Tcl_WideInt)1024*1024*1024*1024*1024*4, 00542 (Tcl_WideInt)1024*1024*1024*1024*1024*4*4, 00543 (Tcl_WideInt)1024*1024*1024*1024*1024*4*4*4, 00544 (Tcl_WideInt)1024*1024*1024*1024*1024*4*4*4*4, 00545 (Tcl_WideInt)1024*1024*1024*1024*1024*1024, 00546 (Tcl_WideInt)1024*1024*1024*1024*1024*1024*4, 00547 (Tcl_WideInt)3125*3125*3125*5*5, 00548 (Tcl_WideInt)3125*3125*3125*5*5*5, 00549 (Tcl_WideInt)3125*3125*3125*5*5*5*5, 00550 (Tcl_WideInt)3125*3125*3125*3125, 00551 (Tcl_WideInt)3125*3125*3125*3125*5, 00552 (Tcl_WideInt)3125*3125*3125*3125*5*5, 00553 (Tcl_WideInt)3125*3125*3125*3125*5*5*5, 00554 (Tcl_WideInt)3125*3125*3125*3125*5*5*5*5, 00555 (Tcl_WideInt)3125*3125*3125*3125*3125, 00556 (Tcl_WideInt)3125*3125*3125*3125*3125*5, 00557 (Tcl_WideInt)3125*3125*3125*3125*3125*5*5, 00558 (Tcl_WideInt)7776*7776*7776*6*6, 00559 (Tcl_WideInt)7776*7776*7776*6*6*6, 00560 (Tcl_WideInt)7776*7776*7776*6*6*6*6, 00561 (Tcl_WideInt)7776*7776*7776*7776, 00562 (Tcl_WideInt)7776*7776*7776*7776*6, 00563 (Tcl_WideInt)7776*7776*7776*7776*6*6, 00564 (Tcl_WideInt)7776*7776*7776*7776*6*6*6, 00565 (Tcl_WideInt)7776*7776*7776*7776*6*6*6*6, 00566 (Tcl_WideInt)16807*16807*16807*7*7, 00567 (Tcl_WideInt)16807*16807*16807*7*7*7, 00568 (Tcl_WideInt)16807*16807*16807*7*7*7*7, 00569 (Tcl_WideInt)16807*16807*16807*16807, 00570 (Tcl_WideInt)16807*16807*16807*16807*7, 00571 (Tcl_WideInt)16807*16807*16807*16807*7*7, 00572 (Tcl_WideInt)32768*32768*32768*8*8, 00573 (Tcl_WideInt)32768*32768*32768*8*8*8, 00574 (Tcl_WideInt)32768*32768*32768*8*8*8*8, 00575 (Tcl_WideInt)32768*32768*32768*32768, 00576 (Tcl_WideInt)59049*59049*59049*9*9, 00577 (Tcl_WideInt)59049*59049*59049*9*9*9, 00578 (Tcl_WideInt)59049*59049*59049*9*9*9*9, 00579 (Tcl_WideInt)100000*100000*100000*10*10, 00580 (Tcl_WideInt)100000*100000*100000*10*10*10, 00581 (Tcl_WideInt)161051*161051*161051*11*11, 00582 (Tcl_WideInt)161051*161051*161051*11*11*11, 00583 (Tcl_WideInt)248832*248832*248832*12*12, 00584 (Tcl_WideInt)371293*371293*371293*13*13 00585 }; 00586 00587 #endif 00588 00589 /* 00590 * Declarations for local procedures to this file: 00591 */ 00592 00593 #ifdef TCL_COMPILE_STATS 00594 static int EvalStatsCmd(ClientData clientData, 00595 Tcl_Interp *interp, int objc, 00596 Tcl_Obj *const objv[]); 00597 #endif /* TCL_COMPILE_STATS */ 00598 #ifdef TCL_COMPILE_DEBUG 00599 static char * GetOpcodeName(unsigned char *pc); 00600 #endif /* TCL_COMPILE_DEBUG */ 00601 static ExceptionRange * GetExceptRangeForPc(unsigned char *pc, int catchOnly, 00602 ByteCode *codePtr); 00603 static const char * GetSrcInfoForPc(unsigned char *pc, ByteCode *codePtr, 00604 int *lengthPtr); 00605 static Tcl_Obj ** GrowEvaluationStack(ExecEnv *eePtr, int growth, 00606 int move); 00607 static void IllegalExprOperandType(Tcl_Interp *interp, 00608 unsigned char *pc, Tcl_Obj *opndPtr); 00609 static void InitByteCodeExecution(Tcl_Interp *interp); 00610 #ifdef TCL_COMPILE_DEBUG 00611 static void PrintByteCodeInfo(ByteCode *codePtr); 00612 static const char * StringForResultCode(int result); 00613 static void ValidatePcAndStackTop(ByteCode *codePtr, 00614 unsigned char *pc, int stackTop, 00615 int stackLowerBound, int checkStack); 00616 #endif /* TCL_COMPILE_DEBUG */ 00617 static void DeleteExecStack(ExecStack *esPtr); 00618 /* Useful elsewhere, make available in tclInt.h or stubs? */ 00619 static Tcl_Obj ** StackAllocWords(Tcl_Interp *interp, int numWords); 00620 static Tcl_Obj ** StackReallocWords(Tcl_Interp *interp, int numWords); 00621 00622 /* 00623 *---------------------------------------------------------------------- 00624 * 00625 * InitByteCodeExecution -- 00626 * 00627 * This procedure is called once to initialize the Tcl bytecode 00628 * interpreter. 00629 * 00630 * Results: 00631 * None. 00632 * 00633 * Side effects: 00634 * This procedure initializes the array of instruction names. If 00635 * compiling with the TCL_COMPILE_STATS flag, it initializes the array 00636 * that counts the executions of each instruction and it creates the 00637 * "evalstats" command. It also establishes the link between the Tcl 00638 * "tcl_traceExec" and C "tclTraceExec" variables. 00639 * 00640 *---------------------------------------------------------------------- 00641 */ 00642 00643 static void 00644 InitByteCodeExecution( 00645 Tcl_Interp *interp) /* Interpreter for which the Tcl variable 00646 * "tcl_traceExec" is linked to control 00647 * instruction tracing. */ 00648 { 00649 #if (LONG_MAX > 0x7fffffff) || !defined(TCL_WIDE_INT_IS_LONG) 00650 int i, j; 00651 Tcl_WideInt w, x; 00652 #endif 00653 #ifdef TCL_COMPILE_DEBUG 00654 if (Tcl_LinkVar(interp, "tcl_traceExec", (char *) &tclTraceExec, 00655 TCL_LINK_INT) != TCL_OK) { 00656 Tcl_Panic("InitByteCodeExecution: can't create link for tcl_traceExec variable"); 00657 } 00658 #endif 00659 #ifdef TCL_COMPILE_STATS 00660 Tcl_CreateObjCommand(interp, "evalstats", EvalStatsCmd, NULL, NULL); 00661 #endif /* TCL_COMPILE_STATS */ 00662 #if (LONG_MAX > 0x7fffffff) || !defined(TCL_WIDE_INT_IS_LONG) 00663 00664 /* 00665 * Fill in a table of what base can be raised to powers 2, 3, ... 16 00666 * without overflowing a Tcl_WideInt 00667 */ 00668 00669 for (i = 2; i <= 16; ++i) { 00670 /* 00671 * Compute an initial guess in floating point. 00672 */ 00673 00674 w = (Tcl_WideInt) pow((double) LLONG_MAX, 1.0 / i) + 1; 00675 00676 /* 00677 * Correct the guess if it's too high. 00678 */ 00679 00680 for (;;) { 00681 x = LLONG_MAX; 00682 for (j = 0; j < i; ++j) { 00683 x /= w; 00684 } 00685 if (x == 1) { 00686 break; 00687 } 00688 --w; 00689 } 00690 00691 MaxBaseWide[i-2] = w; 00692 } 00693 #endif 00694 } 00695 00696 /* 00697 *---------------------------------------------------------------------- 00698 * 00699 * TclCreateExecEnv -- 00700 * 00701 * This procedure creates a new execution environment for Tcl bytecode 00702 * execution. An ExecEnv points to a Tcl evaluation stack. An ExecEnv is 00703 * typically created once for each Tcl interpreter (Interp structure) and 00704 * recursively passed to TclExecuteByteCode to execute ByteCode sequences 00705 * for nested commands. 00706 * 00707 * Results: 00708 * A newly allocated ExecEnv is returned. This points to an empty 00709 * evaluation stack of the standard initial size. 00710 * 00711 * Side effects: 00712 * The bytecode interpreter is also initialized here, as this procedure 00713 * will be called before any call to TclExecuteByteCode. 00714 * 00715 *---------------------------------------------------------------------- 00716 */ 00717 00718 #define TCL_STACK_INITIAL_SIZE 2000 00719 00720 ExecEnv * 00721 TclCreateExecEnv( 00722 Tcl_Interp *interp) /* Interpreter for which the execution 00723 * environment is being created. */ 00724 { 00725 ExecEnv *eePtr = (ExecEnv *) ckalloc(sizeof(ExecEnv)); 00726 ExecStack *esPtr = (ExecStack *) ckalloc(sizeof(ExecStack) 00727 + (size_t) (TCL_STACK_INITIAL_SIZE-1) * sizeof(Tcl_Obj *)); 00728 00729 eePtr->execStackPtr = esPtr; 00730 TclNewBooleanObj(eePtr->constants[0], 0); 00731 Tcl_IncrRefCount(eePtr->constants[0]); 00732 TclNewBooleanObj(eePtr->constants[1], 1); 00733 Tcl_IncrRefCount(eePtr->constants[1]); 00734 00735 esPtr->prevPtr = NULL; 00736 esPtr->nextPtr = NULL; 00737 esPtr->markerPtr = NULL; 00738 esPtr->endPtr = &esPtr->stackWords[TCL_STACK_INITIAL_SIZE-1]; 00739 esPtr->tosPtr = &esPtr->stackWords[-1]; 00740 00741 Tcl_MutexLock(&execMutex); 00742 if (!execInitialized) { 00743 TclInitAuxDataTypeTable(); 00744 InitByteCodeExecution(interp); 00745 execInitialized = 1; 00746 } 00747 Tcl_MutexUnlock(&execMutex); 00748 00749 return eePtr; 00750 } 00751 #undef TCL_STACK_INITIAL_SIZE 00752 00753 /* 00754 *---------------------------------------------------------------------- 00755 * 00756 * TclDeleteExecEnv -- 00757 * 00758 * Frees the storage for an ExecEnv. 00759 * 00760 * Results: 00761 * None. 00762 * 00763 * Side effects: 00764 * Storage for an ExecEnv and its contained storage (e.g. the evaluation 00765 * stack) is freed. 00766 * 00767 *---------------------------------------------------------------------- 00768 */ 00769 00770 static void 00771 DeleteExecStack( 00772 ExecStack *esPtr) 00773 { 00774 if (esPtr->markerPtr) { 00775 Tcl_Panic("freeing an execStack which is still in use"); 00776 } 00777 00778 if (esPtr->prevPtr) { 00779 esPtr->prevPtr->nextPtr = esPtr->nextPtr; 00780 } 00781 if (esPtr->nextPtr) { 00782 esPtr->nextPtr->prevPtr = esPtr->prevPtr; 00783 } 00784 ckfree((char *) esPtr); 00785 } 00786 00787 void 00788 TclDeleteExecEnv( 00789 ExecEnv *eePtr) /* Execution environment to free. */ 00790 { 00791 ExecStack *esPtr = eePtr->execStackPtr, *tmpPtr; 00792 00793 /* 00794 * Delete all stacks in this exec env. 00795 */ 00796 00797 while (esPtr->nextPtr) { 00798 esPtr = esPtr->nextPtr; 00799 } 00800 while (esPtr) { 00801 tmpPtr = esPtr; 00802 esPtr = tmpPtr->prevPtr; 00803 DeleteExecStack(tmpPtr); 00804 } 00805 00806 TclDecrRefCount(eePtr->constants[0]); 00807 TclDecrRefCount(eePtr->constants[1]); 00808 ckfree((char *) eePtr); 00809 } 00810 00811 /* 00812 *---------------------------------------------------------------------- 00813 * 00814 * TclFinalizeExecution -- 00815 * 00816 * Finalizes the execution environment setup so that it can be later 00817 * reinitialized. 00818 * 00819 * Results: 00820 * None. 00821 * 00822 * Side effects: 00823 * After this call, the next time TclCreateExecEnv will be called it will 00824 * call InitByteCodeExecution. 00825 * 00826 *---------------------------------------------------------------------- 00827 */ 00828 00829 void 00830 TclFinalizeExecution(void) 00831 { 00832 Tcl_MutexLock(&execMutex); 00833 execInitialized = 0; 00834 Tcl_MutexUnlock(&execMutex); 00835 TclFinalizeAuxDataTypeTable(); 00836 } 00837 00838 /* 00839 * Auxiliary code to insure that GrowEvaluationStack always returns correctly 00840 * aligned memory. This assumes that TCL_ALLOCALIGN is a multiple of the 00841 * wordsize 'sizeof(Tcl_Obj *)'. 00842 */ 00843 00844 #define WALLOCALIGN \ 00845 (TCL_ALLOCALIGN/sizeof(Tcl_Obj *)) 00846 00847 static inline int 00848 OFFSET( 00849 Tcl_Obj **markerPtr) 00850 { 00851 /* 00852 * Note that we are only interested in the low bits of the address, so 00853 * that the fact that PTR2INT may lose the high bits is irrelevant. 00854 */ 00855 00856 int mask, base, new; 00857 00858 mask = WALLOCALIGN-1; 00859 base = (PTR2INT(markerPtr) & mask); 00860 new = ((base + 1) + mask) & ~mask; 00861 return (new - base); 00862 } 00863 00864 #define MEMSTART(markerPtr) \ 00865 ((markerPtr) + OFFSET(markerPtr)) 00866 00867 00868 /* 00869 *---------------------------------------------------------------------- 00870 * 00871 * GrowEvaluationStack -- 00872 * 00873 * This procedure grows a Tcl evaluation stack stored in an ExecEnv, 00874 * copying over the words since the last mark if so requested. A mark is 00875 * set at the beginning of the new area when no copying is requested. 00876 * 00877 * Results: 00878 * Returns a pointer to the first usable word in the (possibly) grown 00879 * stack. 00880 * 00881 * Side effects: 00882 * The size of the evaluation stack may be grown, a marker is set 00883 * 00884 *---------------------------------------------------------------------- 00885 */ 00886 00887 static Tcl_Obj ** 00888 GrowEvaluationStack( 00889 ExecEnv *eePtr, /* Points to the ExecEnv with an evaluation 00890 * stack to enlarge. */ 00891 int growth, /* How much larger than the current used 00892 * size. */ 00893 int move) /* 1 if move words since last marker. */ 00894 { 00895 ExecStack *esPtr = eePtr->execStackPtr, *oldPtr = NULL; 00896 int newBytes, newElems, currElems; 00897 int needed = growth - (esPtr->endPtr - esPtr->tosPtr); 00898 Tcl_Obj **markerPtr = esPtr->markerPtr, **memStart; 00899 00900 if (move) { 00901 if (!markerPtr) { 00902 Tcl_Panic("STACK: Reallocating with no previous alloc"); 00903 } 00904 if (needed <= 0) { 00905 return MEMSTART(markerPtr); 00906 } 00907 } else { 00908 Tcl_Obj **tmpMarkerPtr = esPtr->tosPtr + 1; 00909 int offset = OFFSET(tmpMarkerPtr); 00910 00911 if (needed + offset < 0) { 00912 /* 00913 * Put a marker pointing to the previous marker in this stack, and 00914 * store it in esPtr as the current marker. Return a pointer to 00915 * the start of aligned memory. 00916 */ 00917 00918 esPtr->markerPtr = tmpMarkerPtr; 00919 memStart = tmpMarkerPtr + offset; 00920 esPtr->tosPtr = memStart - 1; 00921 *esPtr->markerPtr = (Tcl_Obj *) markerPtr; 00922 return memStart; 00923 } 00924 } 00925 00926 /* 00927 * Reset move to hold the number of words to be moved to new stack (if 00928 * any) and growth to hold the complete stack requirements: add the marker 00929 * and maximal possible offset. 00930 */ 00931 00932 if (move) { 00933 move = esPtr->tosPtr - MEMSTART(markerPtr) + 1; 00934 } 00935 needed = growth + move + WALLOCALIGN - 1; 00936 00937 /* 00938 * Check if there is enough room in the next stack (if there is one, it 00939 * should be both empty and the last one!) 00940 */ 00941 00942 if (esPtr->nextPtr) { 00943 oldPtr = esPtr; 00944 esPtr = oldPtr->nextPtr; 00945 currElems = esPtr->endPtr - &esPtr->stackWords[-1]; 00946 if (esPtr->markerPtr || (esPtr->tosPtr != &esPtr->stackWords[-1])) { 00947 Tcl_Panic("STACK: Stack after current is in use"); 00948 } 00949 if (esPtr->nextPtr) { 00950 Tcl_Panic("STACK: Stack after current is not last"); 00951 } 00952 if (needed <= currElems) { 00953 goto newStackReady; 00954 } 00955 DeleteExecStack(esPtr); 00956 esPtr = oldPtr; 00957 } else { 00958 currElems = esPtr->endPtr - &esPtr->stackWords[-1]; 00959 } 00960 00961 /* 00962 * We need to allocate a new stack! It needs to store 'growth' words, 00963 * including the elements to be copied over and the new marker. 00964 */ 00965 00966 newElems = 2*currElems; 00967 while (needed > newElems) { 00968 newElems *= 2; 00969 } 00970 newBytes = sizeof (ExecStack) + (newElems-1) * sizeof(Tcl_Obj *); 00971 00972 oldPtr = esPtr; 00973 esPtr = (ExecStack *) ckalloc(newBytes); 00974 00975 oldPtr->nextPtr = esPtr; 00976 esPtr->prevPtr = oldPtr; 00977 esPtr->nextPtr = NULL; 00978 esPtr->endPtr = &esPtr->stackWords[newElems-1]; 00979 00980 newStackReady: 00981 eePtr->execStackPtr = esPtr; 00982 00983 /* 00984 * Store a NULL marker at the beginning of the stack, to indicate that 00985 * this is the first marker in this stack and that rewinding to here 00986 * should actually be a return to the previous stack. 00987 */ 00988 00989 esPtr->stackWords[0] = NULL; 00990 esPtr->markerPtr = &esPtr->stackWords[0]; 00991 memStart = MEMSTART(esPtr->markerPtr); 00992 esPtr->tosPtr = memStart - 1; 00993 00994 if (move) { 00995 memcpy(memStart, MEMSTART(markerPtr), move*sizeof(Tcl_Obj *)); 00996 esPtr->tosPtr += move; 00997 oldPtr->markerPtr = (Tcl_Obj **) *markerPtr; 00998 oldPtr->tosPtr = markerPtr-1; 00999 } 01000 01001 /* 01002 * Free the old stack if it is now unused. 01003 */ 01004 01005 if (!oldPtr->markerPtr) { 01006 DeleteExecStack(oldPtr); 01007 } 01008 01009 return memStart; 01010 } 01011 01012 /* 01013 *-------------------------------------------------------------- 01014 * 01015 * TclStackAlloc, TclStackRealloc, TclStackFree -- 01016 * 01017 * Allocate memory from the execution stack; it has to be returned later 01018 * with a call to TclStackFree. 01019 * 01020 * Results: 01021 * A pointer to the first byte allocated, or panics if the allocation did 01022 * not succeed. 01023 * 01024 * Side effects: 01025 * The execution stack may be grown. 01026 * 01027 *-------------------------------------------------------------- 01028 */ 01029 01030 static Tcl_Obj ** 01031 StackAllocWords( 01032 Tcl_Interp *interp, 01033 int numWords) 01034 { 01035 /* 01036 * Note that GrowEvaluationStack sets a marker in the stack. This marker 01037 * is read when rewinding, e.g., by TclStackFree. 01038 */ 01039 01040 Interp *iPtr = (Interp *) interp; 01041 ExecEnv *eePtr = iPtr->execEnvPtr; 01042 Tcl_Obj **resPtr = GrowEvaluationStack(eePtr, numWords, 0); 01043 01044 eePtr->execStackPtr->tosPtr += numWords; 01045 return resPtr; 01046 } 01047 01048 static Tcl_Obj ** 01049 StackReallocWords( 01050 Tcl_Interp *interp, 01051 int numWords) 01052 { 01053 Interp *iPtr = (Interp *) interp; 01054 ExecEnv *eePtr = iPtr->execEnvPtr; 01055 Tcl_Obj **resPtr = GrowEvaluationStack(eePtr, numWords, 1); 01056 01057 eePtr->execStackPtr->tosPtr += numWords; 01058 return resPtr; 01059 } 01060 01061 void 01062 TclStackFree( 01063 Tcl_Interp *interp, 01064 void *freePtr) 01065 { 01066 Interp *iPtr = (Interp *) interp; 01067 ExecEnv *eePtr; 01068 ExecStack *esPtr; 01069 Tcl_Obj **markerPtr; 01070 01071 if (iPtr == NULL || iPtr->execEnvPtr == NULL) { 01072 Tcl_Free((char *) freePtr); 01073 return; 01074 } 01075 01076 /* 01077 * Rewind the stack to the previous marker position. The current marker, 01078 * as set in the last call to GrowEvaluationStack, contains a pointer to 01079 * the previous marker. 01080 */ 01081 01082 eePtr = iPtr->execEnvPtr; 01083 esPtr = eePtr->execStackPtr; 01084 markerPtr = esPtr->markerPtr; 01085 01086 if (MEMSTART(markerPtr) != (Tcl_Obj **)freePtr) { 01087 Tcl_Panic("TclStackFree: incorrect freePtr. Call out of sequence?"); 01088 } 01089 01090 esPtr->tosPtr = markerPtr-1; 01091 esPtr->markerPtr = (Tcl_Obj **) *markerPtr; 01092 if (*markerPtr) { 01093 return; 01094 } 01095 01096 /* 01097 * Return to previous stack. 01098 */ 01099 01100 esPtr->tosPtr = &esPtr->stackWords[-1]; 01101 if (esPtr->prevPtr) { 01102 eePtr->execStackPtr = esPtr->prevPtr; 01103 } 01104 if (esPtr->nextPtr) { 01105 if (!esPtr->prevPtr) { 01106 eePtr->execStackPtr = esPtr->nextPtr; 01107 } 01108 DeleteExecStack(esPtr); 01109 } 01110 } 01111 01112 void * 01113 TclStackAlloc( 01114 Tcl_Interp *interp, 01115 int numBytes) 01116 { 01117 Interp *iPtr = (Interp *) interp; 01118 int numWords = (numBytes + (sizeof(Tcl_Obj *) - 1))/sizeof(Tcl_Obj *); 01119 01120 if (iPtr == NULL || iPtr->execEnvPtr == NULL) { 01121 return (void *) Tcl_Alloc(numBytes); 01122 } 01123 01124 return (void *) StackAllocWords(interp, numWords); 01125 } 01126 01127 void * 01128 TclStackRealloc( 01129 Tcl_Interp *interp, 01130 void *ptr, 01131 int numBytes) 01132 { 01133 Interp *iPtr = (Interp *) interp; 01134 ExecEnv *eePtr; 01135 ExecStack *esPtr; 01136 Tcl_Obj **markerPtr; 01137 int numWords; 01138 01139 if (iPtr == NULL || iPtr->execEnvPtr == NULL) { 01140 return (void *) Tcl_Realloc((char *) ptr, numBytes); 01141 } 01142 01143 eePtr = iPtr->execEnvPtr; 01144 esPtr = eePtr->execStackPtr; 01145 markerPtr = esPtr->markerPtr; 01146 01147 if (MEMSTART(markerPtr) != (Tcl_Obj **)ptr) { 01148 Tcl_Panic("TclStackRealloc: incorrect ptr. Call out of sequence?"); 01149 } 01150 01151 numWords = (numBytes + (sizeof(Tcl_Obj *) - 1))/sizeof(Tcl_Obj *); 01152 return (void *) StackReallocWords(interp, numWords); 01153 } 01154 01155 /* 01156 *-------------------------------------------------------------- 01157 * 01158 * Tcl_ExprObj -- 01159 * 01160 * Evaluate an expression in a Tcl_Obj. 01161 * 01162 * Results: 01163 * A standard Tcl object result. If the result is other than TCL_OK, then 01164 * the interpreter's result contains an error message. If the result is 01165 * TCL_OK, then a pointer to the expression's result value object is 01166 * stored in resultPtrPtr. In that case, the object's ref count is 01167 * incremented to reflect the reference returned to the caller; the 01168 * caller is then responsible for the resulting object and must, for 01169 * example, decrement the ref count when it is finished with the object. 01170 * 01171 * Side effects: 01172 * Any side effects caused by subcommands in the expression, if any. The 01173 * interpreter result is not modified unless there is an error. 01174 * 01175 *-------------------------------------------------------------- 01176 */ 01177 01178 int 01179 Tcl_ExprObj( 01180 Tcl_Interp *interp, /* Context in which to evaluate the 01181 * expression. */ 01182 register Tcl_Obj *objPtr, /* Points to Tcl object containing expression 01183 * to evaluate. */ 01184 Tcl_Obj **resultPtrPtr) /* Where the Tcl_Obj* that is the expression 01185 * result is stored if no errors occur. */ 01186 { 01187 Interp *iPtr = (Interp *) interp; 01188 CompileEnv compEnv; /* Compilation environment structure allocated 01189 * in frame. */ 01190 register ByteCode *codePtr = NULL; 01191 /* Tcl Internal type of bytecode. Initialized 01192 * to avoid compiler warning. */ 01193 Tcl_Obj *saveObjPtr; 01194 int result; 01195 01196 /* 01197 * Get the ByteCode from the object. If it exists, make sure it hasn't 01198 * been invalidated by, e.g., someone redefining a command with a compile 01199 * procedure (this might make the compiled code wrong). If necessary, 01200 * convert the object to be a ByteCode object and compile it. Also, if the 01201 * code was compiled in/for a different interpreter, we recompile it. 01202 * 01203 * Precompiled expressions, however, are immutable and therefore they are 01204 * not recompiled, even if the epoch has changed. 01205 */ 01206 01207 if (objPtr->typePtr == &tclByteCodeType) { 01208 codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr; 01209 if (((Interp *) *codePtr->interpHandle != iPtr) 01210 || (codePtr->compileEpoch != iPtr->compileEpoch)) { 01211 if (codePtr->flags & TCL_BYTECODE_PRECOMPILED) { 01212 if ((Interp *) *codePtr->interpHandle != iPtr) { 01213 Tcl_Panic("Tcl_ExprObj: compiled expression jumped interps"); 01214 } 01215 codePtr->compileEpoch = iPtr->compileEpoch; 01216 } else { 01217 objPtr->typePtr->freeIntRepProc(objPtr); 01218 objPtr->typePtr = (Tcl_ObjType *) NULL; 01219 } 01220 } 01221 } 01222 if (objPtr->typePtr != &tclByteCodeType) { 01223 /* 01224 * TIP #280: No invoker (yet) - Expression compilation. 01225 */ 01226 01227 int length; 01228 const char *string = TclGetStringFromObj(objPtr, &length); 01229 01230 TclInitCompileEnv(interp, &compEnv, string, length, NULL, 0); 01231 TclCompileExpr(interp, string, length, &compEnv, 0); 01232 01233 /* 01234 * Successful compilation. If the expression yielded no instructions, 01235 * push an zero object as the expression's result. 01236 */ 01237 01238 if (compEnv.codeNext == compEnv.codeStart) { 01239 TclEmitPush(TclRegisterNewLiteral(&compEnv, "0", 1), 01240 &compEnv); 01241 } 01242 01243 /* 01244 * Add a "done" instruction as the last instruction and change the 01245 * object into a ByteCode object. Ownership of the literal objects and 01246 * aux data items is given to the ByteCode object. 01247 */ 01248 01249 TclEmitOpcode(INST_DONE, &compEnv); 01250 TclInitByteCodeObj(objPtr, &compEnv); 01251 TclFreeCompileEnv(&compEnv); 01252 codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr; 01253 #ifdef TCL_COMPILE_DEBUG 01254 if (tclTraceCompile == 2) { 01255 TclPrintByteCodeObj(interp, objPtr); 01256 fflush(stdout); 01257 } 01258 #endif /* TCL_COMPILE_DEBUG */ 01259 } 01260 01261 /* 01262 * Execute the expression after first saving the interpreter's result. 01263 */ 01264 01265 saveObjPtr = Tcl_GetObjResult(interp); 01266 Tcl_IncrRefCount(saveObjPtr); 01267 Tcl_ResetResult(interp); 01268 01269 /* 01270 * Increment the code's ref count while it is being executed. If 01271 * afterwards no references to it remain, free the code. 01272 */ 01273 01274 codePtr->refCount++; 01275 result = TclExecuteByteCode(interp, codePtr); 01276 codePtr->refCount--; 01277 if (codePtr->refCount <= 0) { 01278 TclCleanupByteCode(codePtr); 01279 objPtr->typePtr = NULL; 01280 objPtr->internalRep.otherValuePtr = NULL; 01281 } 01282 01283 /* 01284 * If the expression evaluated successfully, store a pointer to its value 01285 * object in resultPtrPtr then restore the old interpreter result. We 01286 * increment the object's ref count to reflect the reference that we are 01287 * returning to the caller. We also decrement the ref count of the 01288 * interpreter's result object after calling Tcl_SetResult since we next 01289 * store into that field directly. 01290 */ 01291 01292 if (result == TCL_OK) { 01293 *resultPtrPtr = iPtr->objResultPtr; 01294 Tcl_IncrRefCount(iPtr->objResultPtr); 01295 01296 Tcl_SetObjResult(interp, saveObjPtr); 01297 } 01298 TclDecrRefCount(saveObjPtr); 01299 return result; 01300 } 01301 01302 /* 01303 *---------------------------------------------------------------------- 01304 * 01305 * TclCompEvalObj -- 01306 * 01307 * This procedure evaluates the script contained in a Tcl_Obj by first 01308 * compiling it and then passing it to TclExecuteByteCode. 01309 * 01310 * Results: 01311 * The return value is one of the return codes defined in tcl.h (such as 01312 * TCL_OK), and interp->objResultPtr refers to a Tcl object that either 01313 * contains the result of executing the code or an error message. 01314 * 01315 * Side effects: 01316 * Almost certainly, depending on the ByteCode's instructions. 01317 * 01318 *---------------------------------------------------------------------- 01319 */ 01320 01321 int 01322 TclCompEvalObj( 01323 Tcl_Interp *interp, 01324 Tcl_Obj *objPtr, 01325 const CmdFrame *invoker, 01326 int word) 01327 { 01328 register Interp *iPtr = (Interp *) interp; 01329 register ByteCode *codePtr; /* Tcl Internal type of bytecode. */ 01330 int result; 01331 Namespace *namespacePtr; 01332 01333 /* 01334 * Check that the interpreter is ready to execute scripts. Note that we 01335 * manage the interp's runlevel here: it is a small white lie (maybe), but 01336 * saves a ++/-- pair at each invocation. Amazingly enough, the impact on 01337 * performance is noticeable. 01338 */ 01339 01340 iPtr->numLevels++; 01341 if (TclInterpReady(interp) == TCL_ERROR) { 01342 result = TCL_ERROR; 01343 goto done; 01344 } 01345 01346 namespacePtr = iPtr->varFramePtr->nsPtr; 01347 01348 /* 01349 * If the object is not already of tclByteCodeType, compile it (and reset 01350 * the compilation flags in the interpreter; this should be done after any 01351 * compilation). Otherwise, check that it is "fresh" enough. 01352 */ 01353 01354 if (objPtr->typePtr == &tclByteCodeType) { 01355 /* 01356 * Make sure the Bytecode hasn't been invalidated by, e.g., someone 01357 * redefining a command with a compile procedure (this might make the 01358 * compiled code wrong). The object needs to be recompiled if it was 01359 * compiled in/for a different interpreter, or for a different 01360 * namespace, or for the same namespace but with different name 01361 * resolution rules. Precompiled objects, however, are immutable and 01362 * therefore they are not recompiled, even if the epoch has changed. 01363 * 01364 * To be pedantically correct, we should also check that the 01365 * originating procPtr is the same as the current context procPtr 01366 * (assuming one exists at all - none for global level). This code is 01367 * #def'ed out because [info body] was changed to never return a 01368 * bytecode type object, which should obviate us from the extra checks 01369 * here. 01370 */ 01371 01372 codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr; 01373 if (((Interp *) *codePtr->interpHandle != iPtr) 01374 || (codePtr->compileEpoch != iPtr->compileEpoch) 01375 #ifdef CHECK_PROC_ORIGINATION /* [Bug: 3412 Pedantic] */ 01376 || codePtr->procPtr != iPtr->varFramePtr->procPtr 01377 #endif 01378 || (codePtr->nsPtr != namespacePtr) 01379 || (codePtr->nsEpoch != namespacePtr->resolverEpoch)) { 01380 if (codePtr->flags & TCL_BYTECODE_PRECOMPILED) { 01381 if ((Interp *) *codePtr->interpHandle != iPtr) { 01382 Tcl_Panic("Tcl_EvalObj: compiled script jumped interps"); 01383 } 01384 codePtr->compileEpoch = iPtr->compileEpoch; 01385 } else { 01386 /* 01387 * This byteCode is invalid: free it and recompile. 01388 */ 01389 01390 objPtr->typePtr->freeIntRepProc(objPtr); 01391 goto recompileObj; 01392 } 01393 } 01394 01395 /* 01396 * Increment the code's ref count while it is being executed. If 01397 * afterwards no references to it remain, free the code. 01398 */ 01399 01400 runCompiledObj: 01401 codePtr->refCount++; 01402 result = TclExecuteByteCode(interp, codePtr); 01403 codePtr->refCount--; 01404 if (codePtr->refCount <= 0) { 01405 TclCleanupByteCode(codePtr); 01406 } 01407 goto done; 01408 } 01409 01410 recompileObj: 01411 iPtr->errorLine = 1; 01412 01413 /* 01414 * TIP #280. Remember the invoker for a moment in the interpreter 01415 * structures so that the byte code compiler can pick it up when 01416 * initializing the compilation environment, i.e. the extended location 01417 * information. 01418 */ 01419 01420 iPtr->invokeCmdFramePtr = invoker; 01421 iPtr->invokeWord = word; 01422 tclByteCodeType.setFromAnyProc(interp, objPtr); 01423 iPtr->invokeCmdFramePtr = NULL; 01424 codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr; 01425 goto runCompiledObj; 01426 01427 done: 01428 iPtr->numLevels--; 01429 return result; 01430 } 01431 01432 /* 01433 *---------------------------------------------------------------------- 01434 * 01435 * TclIncrObj -- 01436 * 01437 * Increment an integeral value in a Tcl_Obj by an integeral value held 01438 * in another Tcl_Obj. Caller is responsible for making sure we can 01439 * update the first object. 01440 * 01441 * Results: 01442 * TCL_ERROR if either object is non-integer, and TCL_OK otherwise. On 01443 * error, an error message is left in the interpreter (if it is not NULL, 01444 * of course). 01445 * 01446 * Side effects: 01447 * valuePtr gets the new incrmented value. 01448 * 01449 *---------------------------------------------------------------------- 01450 */ 01451 01452 int 01453 TclIncrObj( 01454 Tcl_Interp *interp, 01455 Tcl_Obj *valuePtr, 01456 Tcl_Obj *incrPtr) 01457 { 01458 ClientData ptr1, ptr2; 01459 int type1, type2; 01460 mp_int value, incr; 01461 01462 if (Tcl_IsShared(valuePtr)) { 01463 Tcl_Panic("%s called with shared object", "TclIncrObj"); 01464 } 01465 01466 if (GetNumberFromObj(NULL, valuePtr, &ptr1, &type1) != TCL_OK) { 01467 /* 01468 * Produce error message (reparse?!) 01469 */ 01470 01471 return TclGetIntFromObj(interp, valuePtr, &type1); 01472 } 01473 if (GetNumberFromObj(NULL, incrPtr, &ptr2, &type2) != TCL_OK) { 01474 /* 01475 * Produce error message (reparse?!) 01476 */ 01477 01478 TclGetIntFromObj(interp, incrPtr, &type1); 01479 Tcl_AddErrorInfo(interp, "\n (reading increment)"); 01480 return TCL_ERROR; 01481 } 01482 01483 if ((type1 == TCL_NUMBER_LONG) && (type2 == TCL_NUMBER_LONG)) { 01484 long augend = *((const long *) ptr1); 01485 long addend = *((const long *) ptr2); 01486 long sum = augend + addend; 01487 01488 /* 01489 * Overflow when (augend and sum have different sign) and (augend and 01490 * addend have the same sign). This is encapsulated in the Overflowing 01491 * macro. 01492 */ 01493 01494 if (!Overflowing(augend, addend, sum)) { 01495 TclSetLongObj(valuePtr, sum); 01496 return TCL_OK; 01497 } 01498 #ifndef NO_WIDE_TYPE 01499 { 01500 Tcl_WideInt w1 = (Tcl_WideInt) augend; 01501 Tcl_WideInt w2 = (Tcl_WideInt) addend; 01502 01503 /* 01504 * We know the sum value is outside the long range, so we use the 01505 * macro form that doesn't range test again. 01506 */ 01507 01508 TclSetWideIntObj(valuePtr, w1 + w2); 01509 return TCL_OK; 01510 } 01511 #endif 01512 } 01513 01514 if ((type1 == TCL_NUMBER_DOUBLE) || (type1 == TCL_NUMBER_NAN)) { 01515 /* 01516 * Produce error message (reparse?!) 01517 */ 01518 01519 return TclGetIntFromObj(interp, valuePtr, &type1); 01520 } 01521 if ((type2 == TCL_NUMBER_DOUBLE) || (type2 == TCL_NUMBER_NAN)) { 01522 /* 01523 * Produce error message (reparse?!) 01524 */ 01525 01526 TclGetIntFromObj(interp, incrPtr, &type1); 01527 Tcl_AddErrorInfo(interp, "\n (reading increment)"); 01528 return TCL_ERROR; 01529 } 01530 01531 #ifndef NO_WIDE_TYPE 01532 if ((type1 != TCL_NUMBER_BIG) && (type2 != TCL_NUMBER_BIG)) { 01533 Tcl_WideInt w1, w2, sum; 01534 01535 TclGetWideIntFromObj(NULL, valuePtr, &w1); 01536 TclGetWideIntFromObj(NULL, incrPtr, &w2); 01537 sum = w1 + w2; 01538 01539 /* 01540 * Check for overflow. 01541 */ 01542 01543 if (!Overflowing(w1, w2, sum)) { 01544 Tcl_SetWideIntObj(valuePtr, sum); 01545 return TCL_OK; 01546 } 01547 } 01548 #endif 01549 01550 Tcl_TakeBignumFromObj(interp, valuePtr, &value); 01551 Tcl_GetBignumFromObj(interp, incrPtr, &incr); 01552 mp_add(&value, &incr, &value); 01553 mp_clear(&incr); 01554 Tcl_SetBignumObj(valuePtr, &value); 01555 return TCL_OK; 01556 } 01557 01558 /* 01559 *---------------------------------------------------------------------- 01560 * 01561 * TclExecuteByteCode -- 01562 * 01563 * This procedure executes the instructions of a ByteCode structure. It 01564 * returns when a "done" instruction is executed or an error occurs. 01565 * 01566 * Results: 01567 * The return value is one of the return codes defined in tcl.h (such as 01568 * TCL_OK), and interp->objResultPtr refers to a Tcl object that either 01569 * contains the result of executing the code or an error message. 01570 * 01571 * Side effects: 01572 * Almost certainly, depending on the ByteCode's instructions. 01573 * 01574 *---------------------------------------------------------------------- 01575 */ 01576 01577 int 01578 TclExecuteByteCode( 01579 Tcl_Interp *interp, /* Token for command interpreter. */ 01580 ByteCode *codePtr) /* The bytecode sequence to interpret. */ 01581 { 01582 /* 01583 * Compiler cast directive - not a real variable. 01584 * Interp *iPtr = (Interp *) interp; 01585 */ 01586 #define iPtr ((Interp *) interp) 01587 01588 /* 01589 * Check just the read-traced/write-traced bit of a variable. 01590 */ 01591 01592 #define ReadTraced(varPtr) ((varPtr)->flags & VAR_TRACED_READ) 01593 #define WriteTraced(varPtr) ((varPtr)->flags & VAR_TRACED_WRITE) 01594 01595 /* 01596 * Constants: variables that do not change during the execution, used 01597 * sporadically. 01598 */ 01599 01600 ExecStack *esPtr; 01601 Tcl_Obj **initTosPtr; /* Stack top at start of execution. */ 01602 ptrdiff_t *initCatchTop; /* Catch stack top at start of execution. */ 01603 Var *compiledLocals; 01604 Namespace *namespacePtr; 01605 CmdFrame *bcFramePtr; /* TIP #280: Structure for tracking lines. */ 01606 Tcl_Obj **constants = &iPtr->execEnvPtr->constants[0]; 01607 01608 /* 01609 * Globals: variables that store state, must remain valid at all times. 01610 */ 01611 01612 ptrdiff_t *catchTop; 01613 register Tcl_Obj **tosPtr; /* Cached pointer to top of evaluation 01614 * stack. */ 01615 register unsigned char *pc = codePtr->codeStart; 01616 /* The current program counter. */ 01617 int instructionCount = 0; /* Counter that is used to work out when to 01618 * call Tcl_AsyncReady() */ 01619 Tcl_Obj *expandNestList = NULL; 01620 int checkInterp = 0; /* Indicates when a check of interp readyness 01621 * is necessary. Set by CACHE_STACK_INFO() */ 01622 01623 /* 01624 * Transfer variables - needed only between opcodes, but not while 01625 * executing an instruction. 01626 */ 01627 01628 register int cleanup; 01629 Tcl_Obj *objResultPtr; 01630 01631 /* 01632 * Result variable - needed only when going to checkForcatch or other 01633 * error handlers; also used as local in some opcodes. 01634 */ 01635 01636 int result = TCL_OK; /* Return code returned after execution. */ 01637 01638 /* 01639 * Locals - variables that are used within opcodes or bounded sections of 01640 * the file (jumps between opcodes within a family). 01641 * NOTE: These are now defined locally where needed. 01642 */ 01643 01644 #ifdef TCL_COMPILE_DEBUG 01645 int traceInstructions = (tclTraceExec == 3); 01646 char cmdNameBuf[21]; 01647 #endif 01648 char *curInstName = NULL; 01649 01650 /* 01651 * The execution uses a unified stack: first the catch stack, immediately 01652 * above it a CmdFrame, then the execution stack. 01653 * 01654 * Make sure the catch stack is large enough to hold the maximum number of 01655 * catch commands that could ever be executing at the same time (this will 01656 * be no more than the exception range array's depth). Make sure the 01657 * execution stack is large enough to execute this ByteCode. 01658 */ 01659 01660 catchTop = initCatchTop = (ptrdiff_t *) ( 01661 GrowEvaluationStack(iPtr->execEnvPtr, 01662 codePtr->maxExceptDepth + sizeof(CmdFrame) + 01663 codePtr->maxStackDepth, 0) - 1); 01664 bcFramePtr = (CmdFrame *) (initCatchTop + codePtr->maxExceptDepth + 1); 01665 tosPtr = initTosPtr = ((Tcl_Obj **) (bcFramePtr + 1)) - 1; 01666 esPtr = iPtr->execEnvPtr->execStackPtr; 01667 01668 /* 01669 * TIP #280: Initialize the frame. Do not push it yet. 01670 */ 01671 01672 bcFramePtr->type = ((codePtr->flags & TCL_BYTECODE_PRECOMPILED) 01673 ? TCL_LOCATION_PREBC : TCL_LOCATION_BC); 01674 bcFramePtr->level = (iPtr->cmdFramePtr ? iPtr->cmdFramePtr->level+1 : 1); 01675 bcFramePtr->framePtr = iPtr->framePtr; 01676 bcFramePtr->nextPtr = iPtr->cmdFramePtr; 01677 bcFramePtr->nline = 0; 01678 bcFramePtr->line = NULL; 01679 01680 bcFramePtr->data.tebc.codePtr = codePtr; 01681 bcFramePtr->data.tebc.pc = NULL; 01682 bcFramePtr->cmd.str.cmd = NULL; 01683 bcFramePtr->cmd.str.len = 0; 01684 01685 #ifdef TCL_COMPILE_DEBUG 01686 if (tclTraceExec >= 2) { 01687 PrintByteCodeInfo(codePtr); 01688 fprintf(stdout, " Starting stack top=%d\n", CURR_DEPTH); 01689 fflush(stdout); 01690 } 01691 #endif 01692 01693 #ifdef TCL_COMPILE_STATS 01694 iPtr->stats.numExecutions++; 01695 #endif 01696 01697 namespacePtr = iPtr->varFramePtr->nsPtr; 01698 compiledLocals = iPtr->varFramePtr->compiledLocals; 01699 01700 /* 01701 * Loop executing instructions until a "done" instruction, a TCL_RETURN, 01702 * or some error. 01703 */ 01704 01705 goto cleanup0; 01706 01707 /* 01708 * Targets for standard instruction endings; unrolled for speed in the 01709 * most frequent cases (instructions that consume up to two stack 01710 * elements). 01711 * 01712 * This used to be a "for(;;)" loop, with each instruction doing its own 01713 * cleanup. 01714 */ 01715 01716 { 01717 Tcl_Obj *valuePtr; 01718 01719 cleanupV_pushObjResultPtr: 01720 switch (cleanup) { 01721 case 0: 01722 *(++tosPtr) = (objResultPtr); 01723 goto cleanup0; 01724 default: 01725 cleanup -= 2; 01726 while (cleanup--) { 01727 valuePtr = POP_OBJECT(); 01728 TclDecrRefCount(valuePtr); 01729 } 01730 case 2: 01731 cleanup2_pushObjResultPtr: 01732 valuePtr = POP_OBJECT(); 01733 TclDecrRefCount(valuePtr); 01734 case 1: 01735 cleanup1_pushObjResultPtr: 01736 valuePtr = OBJ_AT_TOS; 01737 TclDecrRefCount(valuePtr); 01738 } 01739 OBJ_AT_TOS = objResultPtr; 01740 goto cleanup0; 01741 01742 cleanupV: 01743 switch (cleanup) { 01744 default: 01745 cleanup -= 2; 01746 while (cleanup--) { 01747 valuePtr = POP_OBJECT(); 01748 TclDecrRefCount(valuePtr); 01749 } 01750 case 2: 01751 cleanup2: 01752 valuePtr = POP_OBJECT(); 01753 TclDecrRefCount(valuePtr); 01754 case 1: 01755 cleanup1: 01756 valuePtr = POP_OBJECT(); 01757 TclDecrRefCount(valuePtr); 01758 case 0: 01759 /* 01760 * We really want to do nothing now, but this is needed for some 01761 * compilers (SunPro CC). 01762 */ 01763 01764 break; 01765 } 01766 } 01767 cleanup0: 01768 01769 #ifdef TCL_COMPILE_DEBUG 01770 /* 01771 * Skip the stack depth check if an expansion is in progress. 01772 */ 01773 01774 ValidatePcAndStackTop(codePtr, pc, CURR_DEPTH, 0, 01775 /*checkStack*/ expandNestList == NULL); 01776 if (traceInstructions) { 01777 fprintf(stdout, "%2d: %2d ", iPtr->numLevels, (int) CURR_DEPTH); 01778 TclPrintInstruction(codePtr, pc); 01779 fflush(stdout); 01780 } 01781 #endif /* TCL_COMPILE_DEBUG */ 01782 01783 #ifdef TCL_COMPILE_STATS 01784 iPtr->stats.instructionCount[*pc]++; 01785 #endif 01786 01787 /* 01788 * Check for asynchronous handlers [Bug 746722]; we do the check every 01789 * ASYNC_CHECK_COUNT_MASK instruction, of the form (2**n-1). 01790 */ 01791 01792 if ((instructionCount++ & ASYNC_CHECK_COUNT_MASK) == 0) { 01793 /* 01794 * Check for asynchronous handlers [Bug 746722]; we do the check every 01795 * ASYNC_CHECK_COUNT_MASK instruction, of the form (2**n-<1). 01796 */ 01797 01798 if (TclAsyncReady(iPtr)) { 01799 int localResult; 01800 01801 DECACHE_STACK_INFO(); 01802 localResult = Tcl_AsyncInvoke(interp, result); 01803 CACHE_STACK_INFO(); 01804 if (localResult == TCL_ERROR) { 01805 result = localResult; 01806 goto checkForCatch; 01807 } 01808 } 01809 if (TclLimitReady(iPtr->limit)) { 01810 int localResult; 01811 01812 DECACHE_STACK_INFO(); 01813 localResult = Tcl_LimitCheck(interp); 01814 CACHE_STACK_INFO(); 01815 if (localResult == TCL_ERROR) { 01816 result = localResult; 01817 goto checkForCatch; 01818 } 01819 } 01820 } 01821 01822 TCL_DTRACE_INST_NEXT(); 01823 01824 /* 01825 * These two instructions account for 26% of all instructions (according 01826 * to measurements on tclbench by Ben Vitale 01827 * [http://www.cs.toronto.edu/syslab/pubs/tcl2005-vitale-zaleski.pdf] 01828 * Resolving them before the switch reduces the cost of branch 01829 * mispredictions, seems to improve runtime by 5% to 15%, and (amazingly!) 01830 * reduces total obj size. 01831 */ 01832 01833 if (*pc == INST_LOAD_SCALAR1) { 01834 goto instLoadScalar1; 01835 } else if (*pc == INST_PUSH1) { 01836 goto instPush1Peephole; 01837 } 01838 01839 switch (*pc) { 01840 case INST_SYNTAX: 01841 case INST_RETURN_IMM: { 01842 int code = TclGetInt4AtPtr(pc+1); 01843 int level = TclGetUInt4AtPtr(pc+5); 01844 01845 /* 01846 * OBJ_AT_TOS is returnOpts, OBJ_UNDER_TOS is resultObjPtr. 01847 */ 01848 01849 TRACE(("%u %u => ", code, level)); 01850 result = TclProcessReturn(interp, code, level, OBJ_AT_TOS); 01851 if (result == TCL_OK) { 01852 TRACE_APPEND(("continuing to next instruction (result=\"%.30s\")", 01853 O2S(objResultPtr))); 01854 NEXT_INST_F(9, 1, 0); 01855 } else { 01856 Tcl_SetObjResult(interp, OBJ_UNDER_TOS); 01857 if (*pc == INST_SYNTAX) { 01858 iPtr->flags &= ~ERR_ALREADY_LOGGED; 01859 } 01860 cleanup = 2; 01861 goto processExceptionReturn; 01862 } 01863 } 01864 01865 case INST_RETURN_STK: 01866 TRACE(("=> ")); 01867 objResultPtr = POP_OBJECT(); 01868 result = Tcl_SetReturnOptions(interp, OBJ_AT_TOS); 01869 OBJ_AT_TOS = objResultPtr; 01870 if (result == TCL_OK) { 01871 TRACE_APPEND(("continuing to next instruction (result=\"%.30s\")", 01872 O2S(objResultPtr))); 01873 NEXT_INST_F(1, 0, 0); 01874 } else { 01875 Tcl_SetObjResult(interp, objResultPtr); 01876 cleanup = 1; 01877 goto processExceptionReturn; 01878 } 01879 01880 case INST_DONE: 01881 if (tosPtr > initTosPtr) { 01882 /* 01883 * Set the interpreter's object result to point to the topmost 01884 * object from the stack, and check for a possible [catch]. The 01885 * stackTop's level and refCount will be handled by "processCatch" 01886 * or "abnormalReturn". 01887 */ 01888 01889 Tcl_SetObjResult(interp, OBJ_AT_TOS); 01890 #ifdef TCL_COMPILE_DEBUG 01891 TRACE_WITH_OBJ(("=> return code=%d, result=", result), 01892 iPtr->objResultPtr); 01893 if (traceInstructions) { 01894 fprintf(stdout, "\n"); 01895 } 01896 #endif 01897 goto checkForCatch; 01898 } else { 01899 (void) POP_OBJECT(); 01900 goto abnormalReturn; 01901 } 01902 01903 case INST_PUSH1: 01904 instPush1Peephole: 01905 PUSH_OBJECT(codePtr->objArrayPtr[TclGetUInt1AtPtr(pc+1)]); 01906 TRACE_WITH_OBJ(("%u => ", TclGetInt1AtPtr(pc+1)), OBJ_AT_TOS); 01907 pc += 2; 01908 #if !TCL_COMPILE_DEBUG 01909 /* 01910 * Runtime peephole optimisation: check if we are pushing again. 01911 */ 01912 01913 if (*pc == INST_PUSH1) { 01914 TCL_DTRACE_INST_NEXT(); 01915 goto instPush1Peephole; 01916 } 01917 #endif 01918 NEXT_INST_F(0, 0, 0); 01919 01920 case INST_PUSH4: 01921 objResultPtr = codePtr->objArrayPtr[TclGetUInt4AtPtr(pc+1)]; 01922 TRACE_WITH_OBJ(("%u => ", TclGetUInt4AtPtr(pc+1)), objResultPtr); 01923 NEXT_INST_F(5, 0, 1); 01924 01925 case INST_POP: { 01926 Tcl_Obj *valuePtr; 01927 01928 TRACE_WITH_OBJ(("=> discarding "), OBJ_AT_TOS); 01929 valuePtr = POP_OBJECT(); 01930 TclDecrRefCount(valuePtr); 01931 01932 /* 01933 * Runtime peephole optimisation: an INST_POP is scheduled at the end 01934 * of most commands. If the next instruction is an INST_START_CMD, 01935 * fall through to it. 01936 */ 01937 01938 pc++; 01939 #if !TCL_COMPILE_DEBUG 01940 if (*pc == INST_START_CMD) { 01941 TCL_DTRACE_INST_NEXT(); 01942 goto instStartCmdPeephole; 01943 } 01944 #endif 01945 NEXT_INST_F(0, 0, 0); 01946 } 01947 01948 case INST_START_CMD: 01949 #if !TCL_COMPILE_DEBUG 01950 instStartCmdPeephole: 01951 #endif 01952 /* 01953 * Remark that if the interpreter is marked for deletion its 01954 * compileEpoch is modified, so that the epoch check also verifies 01955 * that the interp is not deleted. If no outside call has been made 01956 * since the last check, it is safe to omit the check. 01957 */ 01958 01959 iPtr->cmdCount += TclGetUInt4AtPtr(pc+5); 01960 if (!checkInterp) { 01961 instStartCmdOK: 01962 NEXT_INST_F(9, 0, 0); 01963 } else if (((codePtr->compileEpoch == iPtr->compileEpoch) 01964 && (codePtr->nsEpoch == namespacePtr->resolverEpoch)) 01965 || (codePtr->flags & TCL_BYTECODE_PRECOMPILED)) { 01966 checkInterp = 0; 01967 goto instStartCmdOK; 01968 } else { 01969 const char *bytes; 01970 int length, opnd; 01971 Tcl_Obj *newObjResultPtr; 01972 01973 bytes = GetSrcInfoForPc(pc, codePtr, &length); 01974 DECACHE_STACK_INFO(); 01975 result = Tcl_EvalEx(interp, bytes, length, 0); 01976 CACHE_STACK_INFO(); 01977 if (result != TCL_OK) { 01978 cleanup = 0; 01979 goto processExceptionReturn; 01980 } 01981 opnd = TclGetUInt4AtPtr(pc+1); 01982 objResultPtr = Tcl_GetObjResult(interp); 01983 TclNewObj(newObjResultPtr); 01984 Tcl_IncrRefCount(newObjResultPtr); 01985 iPtr->objResultPtr = newObjResultPtr; 01986 NEXT_INST_V(opnd, 0, -1); 01987 } 01988 01989 case INST_DUP: 01990 objResultPtr = OBJ_AT_TOS; 01991 TRACE_WITH_OBJ(("=> "), objResultPtr); 01992 NEXT_INST_F(1, 0, 1); 01993 01994 case INST_OVER: { 01995 int opnd; 01996 01997 opnd = TclGetUInt4AtPtr(pc+1); 01998 objResultPtr = OBJ_AT_DEPTH(opnd); 01999 TRACE_WITH_OBJ(("=> "), objResultPtr); 02000 NEXT_INST_F(5, 0, 1); 02001 } 02002 02003 case INST_REVERSE: { 02004 int opnd; 02005 Tcl_Obj **a, **b; 02006 02007 opnd = TclGetUInt4AtPtr(pc+1); 02008 a = tosPtr-(opnd-1); 02009 b = tosPtr; 02010 while (a<b) { 02011 Tcl_Obj *temp = *a; 02012 *a = *b; 02013 *b = temp; 02014 a++; b--; 02015 } 02016 NEXT_INST_F(5, 0, 0); 02017 } 02018 02019 case INST_CONCAT1: { 02020 int opnd, length, appendLen = 0; 02021 char *bytes, *p; 02022 Tcl_Obj **currPtr; 02023 02024 opnd = TclGetUInt1AtPtr(pc+1); 02025 02026 /* 02027 * Compute the length to be appended. 02028 */ 02029 02030 for (currPtr=&OBJ_AT_DEPTH(opnd-2); currPtr<=&OBJ_AT_TOS; currPtr++) { 02031 bytes = TclGetStringFromObj(*currPtr, &length); 02032 if (bytes != NULL) { 02033 appendLen += length; 02034 } 02035 } 02036 02037 /* 02038 * If nothing is to be appended, just return the first object by 02039 * dropping all the others from the stack; this saves both the 02040 * computation and copy of the string rep of the first object, 02041 * enabling the fast '$x[set x {}]' idiom for 'K $x [set x {}]'. 02042 */ 02043 02044 if (appendLen == 0) { 02045 TRACE_WITH_OBJ(("%u => ", opnd), objResultPtr); 02046 NEXT_INST_V(2, (opnd-1), 0); 02047 } 02048 02049 /* 02050 * If the first object is shared, we need a new obj for the result; 02051 * otherwise, we can reuse the first object. In any case, make sure it 02052 * has enough room to accomodate all the concatenated bytes. Note that 02053 * if it is unshared its bytes are copied by ckrealloc, so that we set 02054 * the loop parameters to avoid copying them again: p points to the 02055 * end of the already copied bytes, currPtr to the second object. 02056 */ 02057 02058 objResultPtr = OBJ_AT_DEPTH(opnd-1); 02059 bytes = TclGetStringFromObj(objResultPtr, &length); 02060 #if !TCL_COMPILE_DEBUG 02061 if (bytes != tclEmptyStringRep && !Tcl_IsShared(objResultPtr)) { 02062 TclFreeIntRep(objResultPtr); 02063 objResultPtr->typePtr = NULL; 02064 objResultPtr->bytes = ckrealloc(bytes, (length + appendLen + 1)); 02065 objResultPtr->length = length + appendLen; 02066 p = TclGetString(objResultPtr) + length; 02067 currPtr = &OBJ_AT_DEPTH(opnd - 2); 02068 } else { 02069 #endif 02070 p = (char *) ckalloc((unsigned) (length + appendLen + 1)); 02071 TclNewObj(objResultPtr); 02072 objResultPtr->bytes = p; 02073 objResultPtr->length = length + appendLen; 02074 currPtr = &OBJ_AT_DEPTH(opnd - 1); 02075 #if !TCL_COMPILE_DEBUG 02076 } 02077 #endif 02078 02079 /* 02080 * Append the remaining characters. 02081 */ 02082 02083 for (; currPtr <= &OBJ_AT_TOS; currPtr++) { 02084 bytes = TclGetStringFromObj(*currPtr, &length); 02085 if (bytes != NULL) { 02086 memcpy(p, bytes, (size_t) length); 02087 p += length; 02088 } 02089 } 02090 *p = '\0'; 02091 02092 TRACE_WITH_OBJ(("%u => ", opnd), objResultPtr); 02093 NEXT_INST_V(2, opnd, 1); 02094 } 02095 02096 case INST_EXPAND_START: { 02097 /* 02098 * Push an element to the expandNestList. This records the current 02099 * stack depth - i.e., the point in the stack where the expanded 02100 * command starts. 02101 * 02102 * Use a Tcl_Obj as linked list element; slight mem waste, but faster 02103 * allocation than ckalloc. This also abuses the Tcl_Obj structure, as 02104 * we do not define a special tclObjType for it. It is not dangerous 02105 * as the obj is never passed anywhere, so that all manipulations are 02106 * performed here and in INST_INVOKE_EXPANDED (in case of an expansion 02107 * error, also in INST_EXPAND_STKTOP). 02108 */ 02109 02110 Tcl_Obj *objPtr; 02111 02112 TclNewObj(objPtr); 02113 objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) CURR_DEPTH; 02114 objPtr->internalRep.twoPtrValue.ptr2 = (VOID *) expandNestList; 02115 expandNestList = objPtr; 02116 NEXT_INST_F(1, 0, 0); 02117 } 02118 02119 case INST_EXPAND_STKTOP: { 02120 int objc, length, i; 02121 Tcl_Obj **objv, *valuePtr; 02122 ptrdiff_t moved; 02123 02124 /* 02125 * Make sure that the element at stackTop is a list; if not, just 02126 * leave with an error. Note that the element from the expand list 02127 * will be removed at checkForCatch. 02128 */ 02129 02130 valuePtr = OBJ_AT_TOS; 02131 if (TclListObjGetElements(interp, valuePtr, &objc, &objv) != TCL_OK){ 02132 TRACE_WITH_OBJ(("%.30s => ERROR: ", O2S(valuePtr)), 02133 Tcl_GetObjResult(interp)); 02134 result = TCL_ERROR; 02135 goto checkForCatch; 02136 } 02137 (void) POP_OBJECT(); 02138 02139 /* 02140 * Make sure there is enough room in the stack to expand this list 02141 * *and* process the rest of the command (at least up to the next 02142 * argument expansion or command end). The operand is the current 02143 * stack depth, as seen by the compiler. 02144 */ 02145 02146 length = objc + (codePtr->maxStackDepth - TclGetInt4AtPtr(pc+1)); 02147 DECACHE_STACK_INFO(); 02148 moved = (GrowEvaluationStack(iPtr->execEnvPtr, length, 1) - 1) 02149 - (Tcl_Obj **) initCatchTop; 02150 02151 if (moved) { 02152 /* 02153 * Change the global data to point to the new stack. 02154 */ 02155 02156 initCatchTop += moved; 02157 catchTop += moved; 02158 initTosPtr += moved; 02159 tosPtr += moved; 02160 esPtr = iPtr->execEnvPtr->execStackPtr; 02161 } 02162 02163 /* 02164 * Expand the list at stacktop onto the stack; free the list. Knowing 02165 * that it has a freeIntRepProc we use Tcl_DecrRefCount(). 02166 */ 02167 02168 for (i = 0; i < objc; i++) { 02169 PUSH_OBJECT(objv[i]); 02170 } 02171 02172 Tcl_DecrRefCount(valuePtr); 02173 NEXT_INST_F(5, 0, 0); 02174 } 02175 02176 { 02177 /* 02178 * INVOCATION BLOCK 02179 */ 02180 02181 int objc, pcAdjustment; 02182 02183 case INST_INVOKE_EXPANDED: 02184 { 02185 Tcl_Obj *objPtr = expandNestList; 02186 02187 expandNestList = (Tcl_Obj *) objPtr->internalRep.twoPtrValue.ptr2; 02188 objc = CURR_DEPTH 02189 - (ptrdiff_t) objPtr->internalRep.twoPtrValue.ptr1; 02190 TclDecrRefCount(objPtr); 02191 } 02192 02193 if (objc) { 02194 pcAdjustment = 1; 02195 goto doInvocation; 02196 } else { 02197 /* 02198 * Nothing was expanded, return {}. 02199 */ 02200 02201 TclNewObj(objResultPtr); 02202 NEXT_INST_F(1, 0, 1); 02203 } 02204 02205 case INST_INVOKE_STK4: 02206 objc = TclGetUInt4AtPtr(pc+1); 02207 pcAdjustment = 5; 02208 goto doInvocation; 02209 02210 case INST_INVOKE_STK1: 02211 objc = TclGetUInt1AtPtr(pc+1); 02212 pcAdjustment = 2; 02213 02214 doInvocation: 02215 { 02216 Tcl_Obj **objv = &OBJ_AT_DEPTH(objc-1); 02217 02218 #ifdef TCL_COMPILE_DEBUG 02219 if (tclTraceExec >= 2) { 02220 int i; 02221 02222 if (traceInstructions) { 02223 strncpy(cmdNameBuf, TclGetString(objv[0]), 20); 02224 TRACE(("%u => call ", objc)); 02225 } else { 02226 fprintf(stdout, "%d: (%u) invoking ", iPtr->numLevels, 02227 (unsigned)(pc - codePtr->codeStart)); 02228 } 02229 for (i = 0; i < objc; i++) { 02230 TclPrintObject(stdout, objv[i], 15); 02231 fprintf(stdout, " "); 02232 } 02233 fprintf(stdout, "\n"); 02234 fflush(stdout); 02235 } 02236 #endif /*TCL_COMPILE_DEBUG*/ 02237 02238 /* 02239 * Reset the instructionCount variable, since we're about to check 02240 * for async stuff anyway while processing TclEvalObjvInternal. 02241 */ 02242 02243 instructionCount = 1; 02244 02245 /* 02246 * Finally, let TclEvalObjvInternal handle the command. 02247 * 02248 * TIP #280: Record the last piece of info needed by 02249 * 'TclGetSrcInfoForPc', and push the frame. 02250 */ 02251 02252 bcFramePtr->data.tebc.pc = (char *) pc; 02253 iPtr->cmdFramePtr = bcFramePtr; 02254 DECACHE_STACK_INFO(); 02255 result = TclEvalObjvInternal(interp, objc, objv, 02256 /* call from TEBC */(char *) -1, -1, 0); 02257 CACHE_STACK_INFO(); 02258 iPtr->cmdFramePtr = iPtr->cmdFramePtr->nextPtr; 02259 02260 if (result == TCL_OK) { 02261 Tcl_Obj *objPtr; 02262 02263 #ifndef TCL_COMPILE_DEBUG 02264 if (*(pc+pcAdjustment) == INST_POP) { 02265 NEXT_INST_V((pcAdjustment+1), objc, 0); 02266 } 02267 #endif 02268 /* 02269 * Push the call's object result and continue execution with 02270 * the next instruction. 02271 */ 02272 02273 TRACE_WITH_OBJ(("%u => ... after \"%.20s\": TCL_OK, result=", 02274 objc, cmdNameBuf), Tcl_GetObjResult(interp)); 02275 02276 objResultPtr = Tcl_GetObjResult(interp); 02277 02278 /* 02279 * Reset the interp's result to avoid possible duplications of 02280 * large objects [Bug 781585]. We do not call Tcl_ResetResult 02281 * to avoid any side effects caused by the resetting of 02282 * errorInfo and errorCode [Bug 804681], which are not needed 02283 * here. We chose instead to manipulate the interp's object 02284 * result directly. 02285 * 02286 * Note that the result object is now in objResultPtr, it 02287 * keeps the refCount it had in its role of 02288 * iPtr->objResultPtr. 02289 */ 02290 02291 TclNewObj(objPtr); 02292 Tcl_IncrRefCount(objPtr); 02293 iPtr->objResultPtr = objPtr; 02294 NEXT_INST_V(pcAdjustment, objc, -1); 02295 } else { 02296 cleanup = objc; 02297 goto processExceptionReturn; 02298 } 02299 } 02300 02301 #if TCL_SUPPORT_84_BYTECODE 02302 case INST_CALL_BUILTIN_FUNC1: { 02303 /* 02304 * Call one of the built-in pre-8.5 Tcl math functions. This 02305 * translates to INST_INVOKE_STK1 with the first argument of 02306 * ::tcl::mathfunc::$objv[0]. We need to insert the named math 02307 * function into the stack. 02308 */ 02309 02310 int opnd, numArgs; 02311 Tcl_Obj *objPtr; 02312 02313 opnd = TclGetUInt1AtPtr(pc+1); 02314 if ((opnd < 0) || (opnd > LAST_BUILTIN_FUNC)) { 02315 TRACE(("UNRECOGNIZED BUILTIN FUNC CODE %d\n", opnd)); 02316 Tcl_Panic("TclExecuteByteCode: unrecognized builtin function code %d", opnd); 02317 } 02318 02319 objPtr = Tcl_NewStringObj("::tcl::mathfunc::", 17); 02320 Tcl_AppendToObj(objPtr, tclBuiltinFuncTable[opnd].name, -1); 02321 02322 /* 02323 * Only 0, 1 or 2 args. 02324 */ 02325 02326 numArgs = tclBuiltinFuncTable[opnd].numArgs; 02327 if (numArgs == 0) { 02328 PUSH_OBJECT(objPtr); 02329 } else if (numArgs == 1) { 02330 Tcl_Obj *tmpPtr1 = POP_OBJECT(); 02331 PUSH_OBJECT(objPtr); 02332 PUSH_OBJECT(tmpPtr1); 02333 Tcl_DecrRefCount(tmpPtr1); 02334 } else { 02335 Tcl_Obj *tmpPtr1, *tmpPtr2; 02336 tmpPtr2 = POP_OBJECT(); 02337 tmpPtr1 = POP_OBJECT(); 02338 PUSH_OBJECT(objPtr); 02339 PUSH_OBJECT(tmpPtr1); 02340 PUSH_OBJECT(tmpPtr2); 02341 Tcl_DecrRefCount(tmpPtr1); 02342 Tcl_DecrRefCount(tmpPtr2); 02343 } 02344 02345 objc = numArgs + 1; 02346 pcAdjustment = 2; 02347 goto doInvocation; 02348 } 02349 02350 case INST_CALL_FUNC1: { 02351 /* 02352 * Call a non-builtin Tcl math function previously registered by a 02353 * call to Tcl_CreateMathFunc pre-8.5. This is essentially 02354 * INST_INVOKE_STK1 converting the first arg to 02355 * ::tcl::mathfunc::$objv[0]. 02356 */ 02357 02358 Tcl_Obj *tmpPtr, *objPtr; 02359 02360 /* 02361 * Number of arguments. The function name is the 0-th argument. 02362 */ 02363 02364 objc = TclGetUInt1AtPtr(pc+1); 02365 02366 objPtr = OBJ_AT_DEPTH(objc-1); 02367 tmpPtr = Tcl_NewStringObj("::tcl::mathfunc::", 17); 02368 Tcl_AppendObjToObj(tmpPtr, objPtr); 02369 Tcl_DecrRefCount(objPtr); 02370 02371 /* 02372 * Variation of PUSH_OBJECT. 02373 */ 02374 02375 OBJ_AT_DEPTH(objc-1) = tmpPtr; 02376 Tcl_IncrRefCount(tmpPtr); 02377 02378 pcAdjustment = 2; 02379 goto doInvocation; 02380 } 02381 #else 02382 /* 02383 * INST_CALL_BUILTIN_FUNC1 and INST_CALL_FUNC1 were made obsolete by the 02384 * changes to add a ::tcl::mathfunc namespace in 8.5. Optional support 02385 * remains for existing bytecode precompiled files. 02386 */ 02387 02388 case INST_CALL_BUILTIN_FUNC1: 02389 Tcl_Panic("TclExecuteByteCode: obsolete INST_CALL_BUILTIN_FUNC1 found"); 02390 case INST_CALL_FUNC1: 02391 Tcl_Panic("TclExecuteByteCode: obsolete INST_CALL_FUNC1 found"); 02392 #endif 02393 } 02394 02395 case INST_EVAL_STK: { 02396 /* 02397 * Note to maintainers: it is important that INST_EVAL_STK pop its 02398 * argument from the stack before jumping to checkForCatch! DO NOT 02399 * OPTIMISE! 02400 */ 02401 02402 Tcl_Obj *objPtr = OBJ_AT_TOS; 02403 02404 DECACHE_STACK_INFO(); 02405 02406 /* 02407 * TIP #280: The invoking context is left NULL for a dynamically 02408 * constructed command. We cannot match its lines to the outer 02409 * context. 02410 */ 02411 02412 result = TclCompEvalObj(interp, objPtr, NULL, 0); 02413 CACHE_STACK_INFO(); 02414 if (result == TCL_OK) { 02415 /* 02416 * Normal return; push the eval's object result. 02417 */ 02418 02419 objResultPtr = Tcl_GetObjResult(interp); 02420 TRACE_WITH_OBJ(("\"%.30s\" => ", O2S(objPtr)), 02421 Tcl_GetObjResult(interp)); 02422 02423 /* 02424 * Reset the interp's result to avoid possible duplications of 02425 * large objects [Bug 781585]. We do not call Tcl_ResetResult to 02426 * avoid any side effects caused by the resetting of errorInfo and 02427 * errorCode [Bug 804681], which are not needed here. We chose 02428 * instead to manipulate the interp's object result directly. 02429 * 02430 * Note that the result object is now in objResultPtr, it keeps 02431 * the refCount it had in its role of iPtr->objResultPtr. 02432 */ 02433 02434 TclNewObj(objPtr); 02435 Tcl_IncrRefCount(objPtr); 02436 iPtr->objResultPtr = objPtr; 02437 NEXT_INST_F(1, 1, -1); 02438 } else { 02439 cleanup = 1; 02440 goto processExceptionReturn; 02441 } 02442 } 02443 02444 case INST_EXPR_STK: { 02445 Tcl_Obj *objPtr, *valuePtr; 02446 02447 objPtr = OBJ_AT_TOS; 02448 DECACHE_STACK_INFO(); 02449 /*Tcl_ResetResult(interp);*/ 02450 result = Tcl_ExprObj(interp, objPtr, &valuePtr); 02451 CACHE_STACK_INFO(); 02452 if (result == TCL_OK) { 02453 objResultPtr = valuePtr; 02454 TRACE_WITH_OBJ(("\"%.30s\" => ", O2S(objPtr)), valuePtr); 02455 NEXT_INST_F(1, 1, -1); /* Already has right refct. */ 02456 } else { 02457 TRACE_WITH_OBJ(("\"%.30s\" => ERROR: ", O2S(objPtr)), 02458 Tcl_GetObjResult(interp)); 02459 goto checkForCatch; 02460 } 02461 } 02462 02463 /* 02464 * --------------------------------------------------------- 02465 * Start of INST_LOAD instructions. 02466 * 02467 * WARNING: more 'goto' here than your doctor recommended! The different 02468 * instructions set the value of some variables and then jump to some 02469 * common execution code. 02470 */ 02471 { 02472 int opnd, pcAdjustment; 02473 Tcl_Obj *part1Ptr, *part2Ptr; 02474 Var *varPtr, *arrayPtr; 02475 Tcl_Obj *objPtr; 02476 02477 case INST_LOAD_SCALAR1: 02478 instLoadScalar1: 02479 opnd = TclGetUInt1AtPtr(pc+1); 02480 varPtr = &(compiledLocals[opnd]); 02481 while (TclIsVarLink(varPtr)) { 02482 varPtr = varPtr->value.linkPtr; 02483 } 02484 TRACE(("%u => ", opnd)); 02485 if (TclIsVarDirectReadable(varPtr)) { 02486 /* 02487 * No errors, no traces: just get the value. 02488 */ 02489 02490 objResultPtr = varPtr->value.objPtr; 02491 TRACE_APPEND(("%.30s\n", O2S(objResultPtr))); 02492 NEXT_INST_F(2, 0, 1); 02493 } 02494 pcAdjustment = 2; 02495 cleanup = 0; 02496 arrayPtr = NULL; 02497 part1Ptr = part2Ptr = NULL; 02498 goto doCallPtrGetVar; 02499 02500 case INST_LOAD_SCALAR4: 02501 opnd = TclGetUInt4AtPtr(pc+1); 02502 varPtr = &(compiledLocals[opnd]); 02503 while (TclIsVarLink(varPtr)) { 02504 varPtr = varPtr->value.linkPtr; 02505 } 02506 TRACE(("%u => ", opnd)); 02507 if (TclIsVarDirectReadable(varPtr)) { 02508 /* 02509 * No errors, no traces: just get the value. 02510 */ 02511 02512 objResultPtr = varPtr->value.objPtr; 02513 TRACE_APPEND(("%.30s\n", O2S(objResultPtr))); 02514 NEXT_INST_F(5, 0, 1); 02515 } 02516 pcAdjustment = 5; 02517 cleanup = 0; 02518 arrayPtr = NULL; 02519 part1Ptr = part2Ptr = NULL; 02520 goto doCallPtrGetVar; 02521 02522 case INST_LOAD_ARRAY4: 02523 opnd = TclGetUInt4AtPtr(pc+1); 02524 pcAdjustment = 5; 02525 goto doLoadArray; 02526 02527 case INST_LOAD_ARRAY1: 02528 opnd = TclGetUInt1AtPtr(pc+1); 02529 pcAdjustment = 2; 02530 02531 doLoadArray: 02532 part1Ptr = NULL; 02533 part2Ptr = OBJ_AT_TOS; 02534 arrayPtr = &(compiledLocals[opnd]); 02535 while (TclIsVarLink(arrayPtr)) { 02536 arrayPtr = arrayPtr->value.linkPtr; 02537 } 02538 TRACE(("%u \"%.30s\" => ", opnd, O2S(part2Ptr))); 02539 if (TclIsVarArray(arrayPtr) && !ReadTraced(arrayPtr)) { 02540 varPtr = VarHashFindVar(arrayPtr->value.tablePtr, part2Ptr); 02541 if (varPtr && TclIsVarDirectReadable(varPtr)) { 02542 /* 02543 * No errors, no traces: just get the value. 02544 */ 02545 02546 objResultPtr = varPtr->value.objPtr; 02547 TRACE_APPEND(("%.30s\n", O2S(objResultPtr))); 02548 NEXT_INST_F(pcAdjustment, 1, 1); 02549 } 02550 } 02551 varPtr = TclLookupArrayElement(interp, part1Ptr, part2Ptr, 02552 TCL_LEAVE_ERR_MSG, "read", 0, 1, arrayPtr, opnd); 02553 if (varPtr == NULL) { 02554 TRACE_APPEND(("ERROR: %.30s\n", 02555 O2S(Tcl_GetObjResult(interp)))); 02556 result = TCL_ERROR; 02557 goto checkForCatch; 02558 } 02559 cleanup = 1; 02560 goto doCallPtrGetVar; 02561 02562 case INST_LOAD_ARRAY_STK: 02563 cleanup = 2; 02564 part2Ptr = OBJ_AT_TOS; /* element name */ 02565 objPtr = OBJ_UNDER_TOS; /* array name */ 02566 TRACE(("\"%.30s(%.30s)\" => ", O2S(objPtr), O2S(part2Ptr))); 02567 goto doLoadStk; 02568 02569 case INST_LOAD_STK: 02570 case INST_LOAD_SCALAR_STK: 02571 cleanup = 1; 02572 part2Ptr = NULL; 02573 objPtr = OBJ_AT_TOS; /* variable name */ 02574 TRACE(("\"%.30s\" => ", O2S(objPtr))); 02575 02576 doLoadStk: 02577 part1Ptr = objPtr; 02578 varPtr = TclObjLookupVarEx(interp, part1Ptr, part2Ptr, 02579 TCL_LEAVE_ERR_MSG, "read", /*createPart1*/0, /*createPart2*/1, 02580 &arrayPtr); 02581 if (varPtr) { 02582 if (TclIsVarDirectReadable2(varPtr, arrayPtr)) { 02583 /* 02584 * No errors, no traces: just get the value. 02585 */ 02586 02587 objResultPtr = varPtr->value.objPtr; 02588 TRACE_APPEND(("%.30s\n", O2S(objResultPtr))); 02589 NEXT_INST_V(1, cleanup, 1); 02590 } 02591 pcAdjustment = 1; 02592 opnd = -1; 02593 goto doCallPtrGetVar; 02594 } else { 02595 TRACE_APPEND(("ERROR: %.30s\n", O2S(Tcl_GetObjResult(interp)))); 02596 result = TCL_ERROR; 02597 goto checkForCatch; 02598 } 02599 02600 doCallPtrGetVar: 02601 /* 02602 * There are either errors or the variable is traced: call 02603 * TclPtrGetVar to process fully. 02604 */ 02605 02606 DECACHE_STACK_INFO(); 02607 objResultPtr = TclPtrGetVar(interp, varPtr, arrayPtr, 02608 part1Ptr, part2Ptr, TCL_LEAVE_ERR_MSG, opnd); 02609 CACHE_STACK_INFO(); 02610 if (objResultPtr) { 02611 TRACE_APPEND(("%.30s\n", O2S(objResultPtr))); 02612 NEXT_INST_V(pcAdjustment, cleanup, 1); 02613 } else { 02614 TRACE_APPEND(("ERROR: %.30s\n", O2S(Tcl_GetObjResult(interp)))); 02615 result = TCL_ERROR; 02616 goto checkForCatch; 02617 } 02618 } 02619 02620 /* 02621 * End of INST_LOAD instructions. 02622 * --------------------------------------------------------- 02623 */ 02624 02625 /* 02626 * --------------------------------------------------------- 02627 * Start of INST_STORE and related instructions. 02628 * 02629 * WARNING: more 'goto' here than your doctor recommended! The different 02630 * instructions set the value of some variables and then jump to somme 02631 * common execution code. 02632 */ 02633 02634 { 02635 int opnd, pcAdjustment, storeFlags; 02636 Tcl_Obj *part1Ptr, *part2Ptr; 02637 Var *varPtr, *arrayPtr; 02638 Tcl_Obj *objPtr, *valuePtr; 02639 02640 case INST_STORE_ARRAY4: 02641 opnd = TclGetUInt4AtPtr(pc+1); 02642 pcAdjustment = 5; 02643 goto doStoreArrayDirect; 02644 02645 case INST_STORE_ARRAY1: 02646 opnd = TclGetUInt1AtPtr(pc+1); 02647 pcAdjustment = 2; 02648 02649 doStoreArrayDirect: 02650 valuePtr = OBJ_AT_TOS; 02651 part2Ptr = OBJ_UNDER_TOS; 02652 arrayPtr = &(compiledLocals[opnd]); 02653 TRACE(("%u \"%.30s\" <- \"%.30s\" => ", opnd, O2S(part2Ptr), 02654 O2S(valuePtr))); 02655 while (TclIsVarLink(arrayPtr)) { 02656 arrayPtr = arrayPtr->value.linkPtr; 02657 } 02658 if (TclIsVarArray(arrayPtr) && !WriteTraced(arrayPtr)) { 02659 varPtr = VarHashFindVar(arrayPtr->value.tablePtr, part2Ptr); 02660 if (varPtr && TclIsVarDirectWritable(varPtr)) { 02661 tosPtr--; 02662 Tcl_DecrRefCount(OBJ_AT_TOS); 02663 OBJ_AT_TOS = valuePtr; 02664 goto doStoreVarDirect; 02665 } 02666 } 02667 cleanup = 2; 02668 storeFlags = TCL_LEAVE_ERR_MSG; 02669 part1Ptr = NULL; 02670 goto doStoreArrayDirectFailed; 02671 02672 case INST_STORE_SCALAR4: 02673 opnd = TclGetUInt4AtPtr(pc+1); 02674 pcAdjustment = 5; 02675 goto doStoreScalarDirect; 02676 02677 case INST_STORE_SCALAR1: 02678 opnd = TclGetUInt1AtPtr(pc+1); 02679 pcAdjustment = 2; 02680 02681 doStoreScalarDirect: 02682 valuePtr = OBJ_AT_TOS; 02683 varPtr = &(compiledLocals[opnd]); 02684 TRACE(("%u <- \"%.30s\" => ", opnd, O2S(valuePtr))); 02685 while (TclIsVarLink(varPtr)) { 02686 varPtr = varPtr->value.linkPtr; 02687 } 02688 if (TclIsVarDirectWritable(varPtr)) { 02689 doStoreVarDirect: 02690 /* 02691 * No traces, no errors, plain 'set': we can safely inline. The 02692 * value *will* be set to what's requested, so that the stack top 02693 * remains pointing to the same Tcl_Obj. 02694 */ 02695 02696 valuePtr = varPtr->value.objPtr; 02697 if (valuePtr != NULL) { 02698 TclDecrRefCount(valuePtr); 02699 } 02700 objResultPtr = OBJ_AT_TOS; 02701 varPtr->value.objPtr = objResultPtr; 02702 #ifndef TCL_COMPILE_DEBUG 02703 if (*(pc+pcAdjustment) == INST_POP) { 02704 tosPtr--; 02705 NEXT_INST_F((pcAdjustment+1), 0, 0); 02706 } 02707 #else 02708 TRACE_APPEND(("%.30s\n", O2S(objResultPtr))); 02709 #endif 02710 Tcl_IncrRefCount(objResultPtr); 02711 NEXT_INST_F(pcAdjustment, 0, 0); 02712 } 02713 storeFlags = TCL_LEAVE_ERR_MSG; 02714 part1Ptr = NULL; 02715 goto doStoreScalar; 02716 02717 case INST_LAPPEND_STK: 02718 valuePtr = OBJ_AT_TOS; /* value to append */ 02719 part2Ptr = NULL; 02720 storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE 02721 | TCL_LIST_ELEMENT | TCL_TRACE_READS); 02722 goto doStoreStk; 02723 02724 case INST_LAPPEND_ARRAY_STK: 02725 valuePtr = OBJ_AT_TOS; /* value to append */ 02726 part2Ptr = OBJ_UNDER_TOS; 02727 storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE 02728 | TCL_LIST_ELEMENT | TCL_TRACE_READS); 02729 goto doStoreStk; 02730 02731 case INST_APPEND_STK: 02732 valuePtr = OBJ_AT_TOS; /* value to append */ 02733 part2Ptr = NULL; 02734 storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE); 02735 goto doStoreStk; 02736 02737 case INST_APPEND_ARRAY_STK: 02738 valuePtr = OBJ_AT_TOS; /* value to append */ 02739 part2Ptr = OBJ_UNDER_TOS; 02740 storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE); 02741 goto doStoreStk; 02742 02743 case INST_STORE_ARRAY_STK: 02744 valuePtr = OBJ_AT_TOS; 02745 part2Ptr = OBJ_UNDER_TOS; 02746 storeFlags = TCL_LEAVE_ERR_MSG; 02747 goto doStoreStk; 02748 02749 case INST_STORE_STK: 02750 case INST_STORE_SCALAR_STK: 02751 valuePtr = OBJ_AT_TOS; 02752 part2Ptr = NULL; 02753 storeFlags = TCL_LEAVE_ERR_MSG; 02754 02755 doStoreStk: 02756 objPtr = OBJ_AT_DEPTH(1 + (part2Ptr != NULL)); /* variable name */ 02757 part1Ptr = objPtr; 02758 #ifdef TCL_COMPILE_DEBUG 02759 if (part2Ptr == NULL) { 02760 TRACE(("\"%.30s\" <- \"%.30s\" =>", O2S(part1Ptr),O2S(valuePtr))); 02761 } else { 02762 TRACE(("\"%.30s(%.30s)\" <- \"%.30s\" => ", 02763 O2S(part1Ptr), O2S(part2Ptr), O2S(valuePtr))); 02764 } 02765 #endif 02766 varPtr = TclObjLookupVarEx(interp, objPtr,part2Ptr, TCL_LEAVE_ERR_MSG, 02767 "set", /*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr); 02768 if (varPtr) { 02769 cleanup = ((part2Ptr == NULL)? 2 : 3); 02770 pcAdjustment = 1; 02771 opnd = -1; 02772 goto doCallPtrSetVar; 02773 } else { 02774 TRACE_APPEND(("ERROR: %.30s\n", O2S(Tcl_GetObjResult(interp)))); 02775 result = TCL_ERROR; 02776 goto checkForCatch; 02777 } 02778 02779 case INST_LAPPEND_ARRAY4: 02780 opnd = TclGetUInt4AtPtr(pc+1); 02781 pcAdjustment = 5; 02782 storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE 02783 | TCL_LIST_ELEMENT | TCL_TRACE_READS); 02784 goto doStoreArray; 02785 02786 case INST_LAPPEND_ARRAY1: 02787 opnd = TclGetUInt1AtPtr(pc+1); 02788 pcAdjustment = 2; 02789 storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE 02790 | TCL_LIST_ELEMENT | TCL_TRACE_READS); 02791 goto doStoreArray; 02792 02793 case INST_APPEND_ARRAY4: 02794 opnd = TclGetUInt4AtPtr(pc+1); 02795 pcAdjustment = 5; 02796 storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE); 02797 goto doStoreArray; 02798 02799 case INST_APPEND_ARRAY1: 02800 opnd = TclGetUInt1AtPtr(pc+1); 02801 pcAdjustment = 2; 02802 storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE); 02803 goto doStoreArray; 02804 02805 doStoreArray: 02806 valuePtr = OBJ_AT_TOS; 02807 part2Ptr = OBJ_UNDER_TOS; 02808 arrayPtr = &(compiledLocals[opnd]); 02809 TRACE(("%u \"%.30s\" <- \"%.30s\" => ", opnd, O2S(part2Ptr), 02810 O2S(valuePtr))); 02811 while (TclIsVarLink(arrayPtr)) { 02812 arrayPtr = arrayPtr->value.linkPtr; 02813 } 02814 cleanup = 2; 02815 part1Ptr = NULL; 02816 02817 doStoreArrayDirectFailed: 02818 varPtr = TclLookupArrayElement(interp, part1Ptr, part2Ptr, 02819 TCL_LEAVE_ERR_MSG, "set", 1, 1, arrayPtr, opnd); 02820 if (varPtr) { 02821 goto doCallPtrSetVar; 02822 } else { 02823 TRACE_APPEND(("ERROR: %.30s\n", O2S(Tcl_GetObjResult(interp)))); 02824 result = TCL_ERROR; 02825 goto checkForCatch; 02826 } 02827 02828 case INST_LAPPEND_SCALAR4: 02829 opnd = TclGetUInt4AtPtr(pc+1); 02830 pcAdjustment = 5; 02831 storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE 02832 | TCL_LIST_ELEMENT | TCL_TRACE_READS); 02833 goto doStoreScalar; 02834 02835 case INST_LAPPEND_SCALAR1: 02836 opnd = TclGetUInt1AtPtr(pc+1); 02837 pcAdjustment = 2; 02838 storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE 02839 | TCL_LIST_ELEMENT | TCL_TRACE_READS); 02840 goto doStoreScalar; 02841 02842 case INST_APPEND_SCALAR4: 02843 opnd = TclGetUInt4AtPtr(pc+1); 02844 pcAdjustment = 5; 02845 storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE); 02846 goto doStoreScalar; 02847 02848 case INST_APPEND_SCALAR1: 02849 opnd = TclGetUInt1AtPtr(pc+1); 02850 pcAdjustment = 2; 02851 storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE); 02852 goto doStoreScalar; 02853 02854 doStoreScalar: 02855 valuePtr = OBJ_AT_TOS; 02856 varPtr = &(compiledLocals[opnd]); 02857 TRACE(("%u <- \"%.30s\" => ", opnd, O2S(valuePtr))); 02858 while (TclIsVarLink(varPtr)) { 02859 varPtr = varPtr->value.linkPtr; 02860 } 02861 cleanup = 1; 02862 arrayPtr = NULL; 02863 part1Ptr = part2Ptr = NULL; 02864 02865 doCallPtrSetVar: 02866 DECACHE_STACK_INFO(); 02867 objResultPtr = TclPtrSetVar(interp, varPtr, arrayPtr, 02868 part1Ptr, part2Ptr, valuePtr, storeFlags, opnd); 02869 CACHE_STACK_INFO(); 02870 if (objResultPtr) { 02871 #ifndef TCL_COMPILE_DEBUG 02872 if (*(pc+pcAdjustment) == INST_POP) { 02873 NEXT_INST_V((pcAdjustment+1), cleanup, 0); 02874 } 02875 #endif 02876 TRACE_APPEND(("%.30s\n", O2S(objResultPtr))); 02877 NEXT_INST_V(pcAdjustment, cleanup, 1); 02878 } else { 02879 TRACE_APPEND(("ERROR: %.30s\n", O2S(Tcl_GetObjResult(interp)))); 02880 result = TCL_ERROR; 02881 goto checkForCatch; 02882 } 02883 } 02884 02885 /* 02886 * End of INST_STORE and related instructions. 02887 * --------------------------------------------------------- 02888 */ 02889 02890 /* 02891 * --------------------------------------------------------- 02892 * Start of INST_INCR instructions. 02893 * 02894 * WARNING: more 'goto' here than your doctor recommended! The different 02895 * instructions set the value of some variables and then jump to somme 02896 * common execution code. 02897 */ 02898 02899 /*TODO: Consider more untangling here; merge with LOAD and STORE ? */ 02900 02901 { 02902 Tcl_Obj *objPtr, *incrPtr; 02903 int opnd, pcAdjustment; 02904 #ifndef NO_WIDE_TYPE 02905 Tcl_WideInt w; 02906 #endif 02907 long i; 02908 Tcl_Obj *part1Ptr, *part2Ptr; 02909 Var *varPtr, *arrayPtr; 02910 02911 case INST_INCR_SCALAR1: 02912 case INST_INCR_ARRAY1: 02913 case INST_INCR_ARRAY_STK: 02914 case INST_INCR_SCALAR_STK: 02915 case INST_INCR_STK: 02916 opnd = TclGetUInt1AtPtr(pc+1); 02917 incrPtr = POP_OBJECT(); 02918 switch (*pc) { 02919 case INST_INCR_SCALAR1: 02920 pcAdjustment = 2; 02921 goto doIncrScalar; 02922 case INST_INCR_ARRAY1: 02923 pcAdjustment = 2; 02924 goto doIncrArray; 02925 default: 02926 pcAdjustment = 1; 02927 goto doIncrStk; 02928 } 02929 02930 case INST_INCR_ARRAY_STK_IMM: 02931 case INST_INCR_SCALAR_STK_IMM: 02932 case INST_INCR_STK_IMM: 02933 i = TclGetInt1AtPtr(pc+1); 02934 incrPtr = Tcl_NewIntObj(i); 02935 Tcl_IncrRefCount(incrPtr); 02936 pcAdjustment = 2; 02937 02938 doIncrStk: 02939 if ((*pc == INST_INCR_ARRAY_STK_IMM) 02940 || (*pc == INST_INCR_ARRAY_STK)) { 02941 part2Ptr = OBJ_AT_TOS; 02942 objPtr = OBJ_UNDER_TOS; 02943 TRACE(("\"%.30s(%.30s)\" (by %ld) => ", 02944 O2S(objPtr), O2S(part2Ptr), i)); 02945 } else { 02946 part2Ptr = NULL; 02947 objPtr = OBJ_AT_TOS; 02948 TRACE(("\"%.30s\" (by %ld) => ", O2S(objPtr), i)); 02949 } 02950 part1Ptr = objPtr; 02951 opnd = -1; 02952 varPtr = TclObjLookupVarEx(interp, objPtr, part2Ptr, 02953 TCL_LEAVE_ERR_MSG, "read", 1, 1, &arrayPtr); 02954 if (varPtr) { 02955 cleanup = ((part2Ptr == NULL)? 1 : 2); 02956 goto doIncrVar; 02957 } else { 02958 Tcl_AddObjErrorInfo(interp, 02959 "\n (reading value of variable to increment)", -1); 02960 TRACE_APPEND(("ERROR: %.30s\n", O2S(Tcl_GetObjResult(interp)))); 02961 result = TCL_ERROR; 02962 Tcl_DecrRefCount(incrPtr); 02963 goto checkForCatch; 02964 } 02965 02966 case INST_INCR_ARRAY1_IMM: 02967 opnd = TclGetUInt1AtPtr(pc+1); 02968 i = TclGetInt1AtPtr(pc+2); 02969 incrPtr = Tcl_NewIntObj(i); 02970 Tcl_IncrRefCount(incrPtr); 02971 pcAdjustment = 3; 02972 02973 doIncrArray: 02974 part1Ptr = NULL; 02975 part2Ptr = OBJ_AT_TOS; 02976 arrayPtr = &(compiledLocals[opnd]); 02977 cleanup = 1; 02978 while (TclIsVarLink(arrayPtr)) { 02979 arrayPtr = arrayPtr->value.linkPtr; 02980 } 02981 TRACE(("%u \"%.30s\" (by %ld) => ", opnd, O2S(part2Ptr), i)); 02982 varPtr = TclLookupArrayElement(interp, part1Ptr, part2Ptr, 02983 TCL_LEAVE_ERR_MSG, "read", 1, 1, arrayPtr, opnd); 02984 if (varPtr) { 02985 goto doIncrVar; 02986 } else { 02987 TRACE_APPEND(("ERROR: %.30s\n", O2S(Tcl_GetObjResult(interp)))); 02988 result = TCL_ERROR; 02989 Tcl_DecrRefCount(incrPtr); 02990 goto checkForCatch; 02991 } 02992 02993 case INST_INCR_SCALAR1_IMM: 02994 opnd = TclGetUInt1AtPtr(pc+1); 02995 i = TclGetInt1AtPtr(pc+2); 02996 pcAdjustment = 3; 02997 cleanup = 0; 02998 varPtr = &(compiledLocals[opnd]); 02999 while (TclIsVarLink(varPtr)) { 03000 varPtr = varPtr->value.linkPtr; 03001 } 03002 03003 if (TclIsVarDirectModifyable(varPtr)) { 03004 ClientData ptr; 03005 int type; 03006 03007 objPtr = varPtr->value.objPtr; 03008 if (GetNumberFromObj(NULL, objPtr, &ptr, &type) == TCL_OK) { 03009 if (type == TCL_NUMBER_LONG) { 03010 long augend = *((const long *)ptr); 03011 long sum = augend + i; 03012 03013 /* 03014 * Overflow when (augend and sum have different sign) and 03015 * (augend and i have the same sign). This is encapsulated 03016 * in the Overflowing macro. 03017 */ 03018 03019 if (!Overflowing(augend, i, sum)) { 03020 TRACE(("%u %ld => ", opnd, i)); 03021 if (Tcl_IsShared(objPtr)) { 03022 objPtr->refCount--; /* We know it's shared. */ 03023 TclNewLongObj(objResultPtr, sum); 03024 Tcl_IncrRefCount(objResultPtr); 03025 varPtr->value.objPtr = objResultPtr; 03026 } else { 03027 objResultPtr = objPtr; 03028 TclSetLongObj(objPtr, sum); 03029 } 03030 goto doneIncr; 03031 } 03032 #ifndef NO_WIDE_TYPE 03033 { 03034 w = (Tcl_WideInt)augend; 03035 03036 TRACE(("%u %ld => ", opnd, i)); 03037 if (Tcl_IsShared(objPtr)) { 03038 objPtr->refCount--; /* We know it's shared. */ 03039 objResultPtr = Tcl_NewWideIntObj(w+i); 03040 Tcl_IncrRefCount(objResultPtr); 03041 varPtr->value.objPtr = objResultPtr; 03042 } else { 03043 objResultPtr = objPtr; 03044 03045 /* 03046 * We know the sum value is outside the long 03047 * range; use macro form that doesn't range test 03048 * again. 03049 */ 03050 03051 TclSetWideIntObj(objPtr, w+i); 03052 } 03053 goto doneIncr; 03054 } 03055 #endif 03056 } /* end if (type == TCL_NUMBER_LONG) */ 03057 #ifndef NO_WIDE_TYPE 03058 if (type == TCL_NUMBER_WIDE) { 03059 Tcl_WideInt sum; 03060 w = *((const Tcl_WideInt *)ptr); 03061 sum = w + i; 03062 03063 /* 03064 * Check for overflow. 03065 */ 03066 03067 if (!Overflowing(w, i, sum)) { 03068 TRACE(("%u %ld => ", opnd, i)); 03069 if (Tcl_IsShared(objPtr)) { 03070 objPtr->refCount--; /* We know it's shared. */ 03071 objResultPtr = Tcl_NewWideIntObj(sum); 03072 Tcl_IncrRefCount(objResultPtr); 03073 varPtr->value.objPtr = objResultPtr; 03074 } else { 03075 objResultPtr = objPtr; 03076 03077 /* 03078 * We *do not* know the sum value is outside the 03079 * long range (wide + long can yield long); use 03080 * the function call that checks range. 03081 */ 03082 03083 Tcl_SetWideIntObj(objPtr, sum); 03084 } 03085 goto doneIncr; 03086 } 03087 } 03088 #endif 03089 } 03090 if (Tcl_IsShared(objPtr)) { 03091 objPtr->refCount--; /* We know it's shared */ 03092 objResultPtr = Tcl_DuplicateObj(objPtr); 03093 Tcl_IncrRefCount(objResultPtr); 03094 varPtr->value.objPtr = objResultPtr; 03095 } else { 03096 objResultPtr = objPtr; 03097 } 03098 TclNewLongObj(incrPtr, i); 03099 result = TclIncrObj(interp, objResultPtr, incrPtr); 03100 Tcl_DecrRefCount(incrPtr); 03101 if (result == TCL_OK) { 03102 goto doneIncr; 03103 } else { 03104 TRACE_APPEND(("ERROR: %.30s\n", 03105 O2S(Tcl_GetObjResult(interp)))); 03106 goto checkForCatch; 03107 } 03108 } 03109 03110 /* 03111 * All other cases, flow through to generic handling. 03112 */ 03113 03114 TclNewLongObj(incrPtr, i); 03115 Tcl_IncrRefCount(incrPtr); 03116 03117 doIncrScalar: 03118 varPtr = &(compiledLocals[opnd]); 03119 while (TclIsVarLink(varPtr)) { 03120 varPtr = varPtr->value.linkPtr; 03121 } 03122 arrayPtr = NULL; 03123 part1Ptr = part2Ptr = NULL; 03124 cleanup = 0; 03125 TRACE(("%u %ld => ", opnd, i)); 03126 03127 doIncrVar: 03128 if (TclIsVarDirectModifyable2(varPtr, arrayPtr)) { 03129 objPtr = varPtr->value.objPtr; 03130 if (Tcl_IsShared(objPtr)) { 03131 objPtr->refCount--; /* We know it's shared */ 03132 objResultPtr = Tcl_DuplicateObj(objPtr); 03133 Tcl_IncrRefCount(objResultPtr); 03134 varPtr->value.objPtr = objResultPtr; 03135 } else { 03136 objResultPtr = objPtr; 03137 } 03138 result = TclIncrObj(interp, objResultPtr, incrPtr); 03139 Tcl_DecrRefCount(incrPtr); 03140 if (result == TCL_OK) { 03141 goto doneIncr; 03142 } else { 03143 TRACE_APPEND(("ERROR: %.30s\n", 03144 O2S(Tcl_GetObjResult(interp)))); 03145 goto checkForCatch; 03146 } 03147 } else { 03148 DECACHE_STACK_INFO(); 03149 objResultPtr = TclPtrIncrObjVar(interp, varPtr, arrayPtr, 03150 part1Ptr, part2Ptr, incrPtr, TCL_LEAVE_ERR_MSG, opnd); 03151 CACHE_STACK_INFO(); 03152 Tcl_DecrRefCount(incrPtr); 03153 if (objResultPtr == NULL) { 03154 TRACE_APPEND(("ERROR: %.30s\n", 03155 O2S(Tcl_GetObjResult(interp)))); 03156 result = TCL_ERROR; 03157 goto checkForCatch; 03158 } 03159 } 03160 doneIncr: 03161 TRACE_APPEND(("%.30s\n", O2S(objResultPtr))); 03162 #ifndef TCL_COMPILE_DEBUG 03163 if (*(pc+pcAdjustment) == INST_POP) { 03164 NEXT_INST_V((pcAdjustment+1), cleanup, 0); 03165 } 03166 #endif 03167 NEXT_INST_V(pcAdjustment, cleanup, 1); 03168 } 03169 03170 /* 03171 * End of INST_INCR instructions. 03172 * --------------------------------------------------------- 03173 */ 03174 03175 /* 03176 * --------------------------------------------------------- 03177 * Start of INST_EXIST instructions. 03178 */ 03179 { 03180 Tcl_Obj *part1Ptr, *part2Ptr; 03181 Var *varPtr, *arrayPtr; 03182 03183 case INST_EXIST_SCALAR: { 03184 int opnd = TclGetUInt4AtPtr(pc+1); 03185 03186 varPtr = &(compiledLocals[opnd]); 03187 while (TclIsVarLink(varPtr)) { 03188 varPtr = varPtr->value.linkPtr; 03189 } 03190 TRACE(("%u => ", opnd)); 03191 if (ReadTraced(varPtr)) { 03192 DECACHE_STACK_INFO(); 03193 TclObjCallVarTraces(iPtr, NULL, varPtr, NULL, NULL, 03194 TCL_TRACE_READS, 0, opnd); 03195 CACHE_STACK_INFO(); 03196 if (TclIsVarUndefined(varPtr)) { 03197 TclCleanupVar(varPtr, NULL); 03198 varPtr = NULL; 03199 } 03200 } 03201 03202 /* 03203 * Tricky! Arrays always exist. 03204 */ 03205 03206 objResultPtr = constants[!varPtr || TclIsVarUndefined(varPtr) ? 0 : 1]; 03207 TRACE_APPEND(("%.30s\n", O2S(objResultPtr))); 03208 NEXT_INST_F(5, 0, 1); 03209 } 03210 03211 case INST_EXIST_ARRAY: { 03212 int opnd = TclGetUInt4AtPtr(pc+1); 03213 03214 part2Ptr = OBJ_AT_TOS; 03215 arrayPtr = &(compiledLocals[opnd]); 03216 while (TclIsVarLink(arrayPtr)) { 03217 arrayPtr = arrayPtr->value.linkPtr; 03218 } 03219 TRACE(("%u \"%.30s\" => ", opnd, O2S(part2Ptr))); 03220 if (TclIsVarArray(arrayPtr) && !ReadTraced(arrayPtr)) { 03221 varPtr = VarHashFindVar(arrayPtr->value.tablePtr, part2Ptr); 03222 if (!varPtr || !ReadTraced(varPtr)) { 03223 goto doneExistArray; 03224 } 03225 } 03226 varPtr = TclLookupArrayElement(interp, NULL, part2Ptr, 0, "access", 03227 0, 1, arrayPtr, opnd); 03228 if (varPtr) { 03229 if (ReadTraced(varPtr) || (arrayPtr && ReadTraced(arrayPtr))) { 03230 DECACHE_STACK_INFO(); 03231 TclObjCallVarTraces(iPtr, arrayPtr, varPtr, NULL, part2Ptr, 03232 TCL_TRACE_READS, 0, opnd); 03233 CACHE_STACK_INFO(); 03234 } 03235 if (TclIsVarUndefined(varPtr)) { 03236 TclCleanupVar(varPtr, arrayPtr); 03237 varPtr = NULL; 03238 } 03239 } 03240 doneExistArray: 03241 objResultPtr = constants[!varPtr || TclIsVarUndefined(varPtr) ? 0 : 1]; 03242 TRACE_APPEND(("%.30s\n", O2S(objResultPtr))); 03243 NEXT_INST_F(5, 1, 1); 03244 } 03245 03246 case INST_EXIST_ARRAY_STK: 03247 cleanup = 2; 03248 part2Ptr = OBJ_AT_TOS; /* element name */ 03249 part1Ptr = OBJ_UNDER_TOS; /* array name */ 03250 TRACE(("\"%.30s(%.30s)\" => ", O2S(part1Ptr), O2S(part2Ptr))); 03251 goto doExistStk; 03252 03253 case INST_EXIST_STK: 03254 cleanup = 1; 03255 part2Ptr = NULL; 03256 part1Ptr = OBJ_AT_TOS; /* variable name */ 03257 TRACE(("\"%.30s\" => ", O2S(part1Ptr))); 03258 03259 doExistStk: 03260 varPtr = TclObjLookupVarEx(interp, part1Ptr, part2Ptr, 0, "access", 03261 /*createPart1*/0, /*createPart2*/1, &arrayPtr); 03262 if (varPtr) { 03263 if (ReadTraced(varPtr) || (arrayPtr && ReadTraced(arrayPtr))) { 03264 DECACHE_STACK_INFO(); 03265 TclObjCallVarTraces(iPtr, arrayPtr, varPtr, part1Ptr,part2Ptr, 03266 TCL_TRACE_READS, 0, -1); 03267 CACHE_STACK_INFO(); 03268 } 03269 if (TclIsVarUndefined(varPtr)) { 03270 TclCleanupVar(varPtr, arrayPtr); 03271 varPtr = NULL; 03272 } 03273 } 03274 objResultPtr = constants[!varPtr || TclIsVarUndefined(varPtr) ? 0 : 1]; 03275 TRACE_APPEND(("%.30s\n", O2S(objResultPtr))); 03276 NEXT_INST_V(1, cleanup, 1); 03277 } 03278 03279 /* 03280 * End of INST_EXIST instructions. 03281 * --------------------------------------------------------- 03282 */ 03283 03284 case INST_UPVAR: { 03285 int opnd; 03286 Var *varPtr, *otherPtr; 03287 03288 TRACE_WITH_OBJ(("upvar "), OBJ_UNDER_TOS); 03289 03290 { 03291 CallFrame *framePtr, *savedFramePtr; 03292 03293 result = TclObjGetFrame(interp, OBJ_UNDER_TOS, &framePtr); 03294 if (result != -1) { 03295 /* 03296 * Locate the other variable. 03297 */ 03298 03299 savedFramePtr = iPtr->varFramePtr; 03300 iPtr->varFramePtr = framePtr; 03301 otherPtr = TclObjLookupVarEx(interp, OBJ_AT_TOS, NULL, 03302 (TCL_LEAVE_ERR_MSG), "access", 03303 /*createPart1*/ 1, /*createPart2*/ 1, &varPtr); 03304 iPtr->varFramePtr = savedFramePtr; 03305 if (otherPtr) { 03306 result = TCL_OK; 03307 goto doLinkVars; 03308 } 03309 } 03310 result = TCL_ERROR; 03311 goto checkForCatch; 03312 } 03313 03314 case INST_VARIABLE: 03315 TRACE(("variable ")); 03316 otherPtr = TclObjLookupVarEx(interp, OBJ_AT_TOS, NULL, 03317 (TCL_NAMESPACE_ONLY | TCL_LEAVE_ERR_MSG), "access", 03318 /*createPart1*/ 1, /*createPart2*/ 1, &varPtr); 03319 if (otherPtr) { 03320 /* 03321 * Do the [variable] magic. 03322 */ 03323 03324 TclSetVarNamespaceVar(otherPtr); 03325 result = TCL_OK; 03326 goto doLinkVars; 03327 } 03328 result = TCL_ERROR; 03329 goto checkForCatch; 03330 03331 case INST_NSUPVAR: 03332 TRACE_WITH_OBJ(("nsupvar "), OBJ_UNDER_TOS); 03333 03334 { 03335 Tcl_Namespace *nsPtr, *savedNsPtr; 03336 03337 result = TclGetNamespaceFromObj(interp, OBJ_UNDER_TOS, &nsPtr); 03338 if (result == TCL_OK) { 03339 /* 03340 * Locate the other variable. 03341 */ 03342 03343 savedNsPtr = (Tcl_Namespace *) iPtr->varFramePtr->nsPtr; 03344 iPtr->varFramePtr->nsPtr = (Namespace *) nsPtr; 03345 otherPtr = TclObjLookupVarEx(interp, OBJ_AT_TOS, NULL, 03346 (TCL_NAMESPACE_ONLY | TCL_LEAVE_ERR_MSG), "access", 03347 /*createPart1*/ 1, /*createPart2*/ 1, &varPtr); 03348 iPtr->varFramePtr->nsPtr = (Namespace *) savedNsPtr; 03349 if (otherPtr) { 03350 goto doLinkVars; 03351 } 03352 } 03353 result = TCL_ERROR; 03354 goto checkForCatch; 03355 } 03356 03357 doLinkVars: 03358 03359 /* 03360 * If we are here, the local variable has already been created: do the 03361 * little work of TclPtrMakeUpvar that remains to be done right here 03362 * if there are no errors; otherwise, let it handle the case. 03363 */ 03364 03365 opnd = TclGetInt4AtPtr(pc+1);; 03366 varPtr = &(compiledLocals[opnd]); 03367 if ((varPtr != otherPtr) && !TclIsVarTraced(varPtr) 03368 && (TclIsVarUndefined(varPtr) || TclIsVarLink(varPtr))) { 03369 if (!TclIsVarUndefined(varPtr)) { 03370 /* 03371 * Then it is a defined link. 03372 */ 03373 03374 Var *linkPtr = varPtr->value.linkPtr; 03375 03376 if (linkPtr == otherPtr) { 03377 goto doLinkVarsDone; 03378 } 03379 if (TclIsVarInHash(linkPtr)) { 03380 VarHashRefCount(linkPtr)--; 03381 if (TclIsVarUndefined(linkPtr)) { 03382 TclCleanupVar(linkPtr, NULL); 03383 } 03384 } 03385 } 03386 TclSetVarLink(varPtr); 03387 varPtr->value.linkPtr = otherPtr; 03388 if (TclIsVarInHash(otherPtr)) { 03389 VarHashRefCount(otherPtr)++; 03390 } 03391 } else { 03392 result = TclPtrObjMakeUpvar(interp, otherPtr, NULL, 0, opnd); 03393 if (result != TCL_OK) { 03394 goto checkForCatch; 03395 } 03396 } 03397 03398 /* 03399 * Do not pop the namespace or frame index, it may be needed for other 03400 * variables - and [variable] did not push it at all. 03401 */ 03402 03403 doLinkVarsDone: 03404 NEXT_INST_F(5, 1, 0); 03405 } 03406 03407 case INST_JUMP1: { 03408 int opnd = TclGetInt1AtPtr(pc+1); 03409 03410 TRACE(("%d => new pc %u\n", opnd, 03411 (unsigned)(pc + opnd - codePtr->codeStart))); 03412 NEXT_INST_F(opnd, 0, 0); 03413 } 03414 03415 case INST_JUMP4: { 03416 int opnd = TclGetInt4AtPtr(pc+1); 03417 03418 TRACE(("%d => new pc %u\n", opnd, 03419 (unsigned)(pc + opnd - codePtr->codeStart))); 03420 NEXT_INST_F(opnd, 0, 0); 03421 } 03422 03423 { 03424 int jmpOffset[2], b; 03425 Tcl_Obj *valuePtr; 03426 03427 /* TODO: consider rewrite so we don't compute the offset we're not 03428 * going to take. */ 03429 case INST_JUMP_FALSE4: 03430 jmpOffset[0] = TclGetInt4AtPtr(pc+1); /* FALSE offset */ 03431 jmpOffset[1] = 5; /* TRUE offset*/ 03432 goto doCondJump; 03433 03434 case INST_JUMP_TRUE4: 03435 jmpOffset[0] = 5; 03436 jmpOffset[1] = TclGetInt4AtPtr(pc+1); 03437 goto doCondJump; 03438 03439 case INST_JUMP_FALSE1: 03440 jmpOffset[0] = TclGetInt1AtPtr(pc+1); 03441 jmpOffset[1] = 2; 03442 goto doCondJump; 03443 03444 case INST_JUMP_TRUE1: 03445 jmpOffset[0] = 2; 03446 jmpOffset[1] = TclGetInt1AtPtr(pc+1); 03447 03448 doCondJump: 03449 valuePtr = OBJ_AT_TOS; 03450 03451 /* TODO - check claim that taking address of b harms performance */ 03452 /* TODO - consider optimization search for constants */ 03453 result = TclGetBooleanFromObj(interp, valuePtr, &b); 03454 if (result != TCL_OK) { 03455 TRACE_WITH_OBJ(("%d => ERROR: ", jmpOffset[ 03456 ((*pc == INST_JUMP_FALSE1) || (*pc == INST_JUMP_FALSE4)) 03457 ? 0 : 1]), Tcl_GetObjResult(interp)); 03458 goto checkForCatch; 03459 } 03460 03461 #ifdef TCL_COMPILE_DEBUG 03462 if (b) { 03463 if ((*pc == INST_JUMP_TRUE1) || (*pc == INST_JUMP_TRUE4)) { 03464 TRACE(("%d => %.20s true, new pc %u\n", jmpOffset[1], 03465 O2S(valuePtr), 03466 (unsigned)(pc + jmpOffset[1] - codePtr->codeStart))); 03467 } else { 03468 TRACE(("%d => %.20s true\n", jmpOffset[0], O2S(valuePtr))); 03469 } 03470 } else { 03471 if ((*pc == INST_JUMP_TRUE1) || (*pc == INST_JUMP_TRUE4)) { 03472 TRACE(("%d => %.20s false\n", jmpOffset[0], O2S(valuePtr))); 03473 } else { 03474 TRACE(("%d => %.20s false, new pc %u\n", jmpOffset[0], 03475 O2S(valuePtr), 03476 (unsigned)(pc + jmpOffset[1] - codePtr->codeStart))); 03477 } 03478 } 03479 #endif 03480 NEXT_INST_F(jmpOffset[b], 1, 0); 03481 } 03482 03483 case INST_JUMP_TABLE: { 03484 Tcl_HashEntry *hPtr; 03485 JumptableInfo *jtPtr; 03486 int opnd; 03487 03488 /* 03489 * Jump to location looked up in a hashtable; fall through to next 03490 * instr if lookup fails. 03491 */ 03492 03493 opnd = TclGetInt4AtPtr(pc+1); 03494 jtPtr = (JumptableInfo *) codePtr->auxDataArrayPtr[opnd].clientData; 03495 TRACE(("%d => %.20s ", opnd, O2S(OBJ_AT_TOS))); 03496 hPtr = Tcl_FindHashEntry(&jtPtr->hashTable, TclGetString(OBJ_AT_TOS)); 03497 if (hPtr != NULL) { 03498 int jumpOffset = PTR2INT(Tcl_GetHashValue(hPtr)); 03499 03500 TRACE_APPEND(("found in table, new pc %u\n", 03501 (unsigned)(pc - codePtr->codeStart + jumpOffset))); 03502 NEXT_INST_F(jumpOffset, 1, 0); 03503 } else { 03504 TRACE_APPEND(("not found in table\n")); 03505 NEXT_INST_F(5, 1, 0); 03506 } 03507 } 03508 03509 /* 03510 * These two instructions are now redundant: the complete logic of the LOR 03511 * and LAND is now handled by the expression compiler. 03512 */ 03513 03514 case INST_LOR: 03515 case INST_LAND: { 03516 /* 03517 * Operands must be boolean or numeric. No int->double conversions are 03518 * performed. 03519 */ 03520 03521 int i1, i2, iResult; 03522 Tcl_Obj *value2Ptr = OBJ_AT_TOS; 03523 Tcl_Obj *valuePtr = OBJ_UNDER_TOS; 03524 03525 result = TclGetBooleanFromObj(NULL, valuePtr, &i1); 03526 if (result != TCL_OK) { 03527 TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n", O2S(valuePtr), 03528 (valuePtr->typePtr? valuePtr->typePtr->name : "null"))); 03529 IllegalExprOperandType(interp, pc, valuePtr); 03530 goto checkForCatch; 03531 } 03532 03533 result = TclGetBooleanFromObj(NULL, value2Ptr, &i2); 03534 if (result != TCL_OK) { 03535 TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n", O2S(value2Ptr), 03536 (value2Ptr->typePtr? value2Ptr->typePtr->name : "null"))); 03537 IllegalExprOperandType(interp, pc, value2Ptr); 03538 goto checkForCatch; 03539 } 03540 03541 if (*pc == INST_LOR) { 03542 iResult = (i1 || i2); 03543 } else { 03544 iResult = (i1 && i2); 03545 } 03546 objResultPtr = constants[iResult]; 03547 TRACE(("%.20s %.20s => %d\n", O2S(valuePtr),O2S(value2Ptr),iResult)); 03548 NEXT_INST_F(1, 2, 1); 03549 } 03550 03551 /* 03552 * --------------------------------------------------------- 03553 * Start of INST_LIST and related instructions. 03554 */ 03555 03556 case INST_LIST: { 03557 /* 03558 * Pop the opnd (objc) top stack elements into a new list obj and then 03559 * decrement their ref counts. 03560 */ 03561 03562 int opnd; 03563 03564 opnd = TclGetUInt4AtPtr(pc+1); 03565 objResultPtr = Tcl_NewListObj(opnd, &OBJ_AT_DEPTH(opnd-1)); 03566 TRACE_WITH_OBJ(("%u => ", opnd), objResultPtr); 03567 NEXT_INST_V(5, opnd, 1); 03568 } 03569 03570 case INST_LIST_LENGTH: { 03571 Tcl_Obj *valuePtr; 03572 int length; 03573 03574 valuePtr = OBJ_AT_TOS; 03575 03576 result = TclListObjLength(interp, valuePtr, &length); 03577 if (result == TCL_OK) { 03578 TclNewIntObj(objResultPtr, length); 03579 TRACE(("%.20s => %d\n", O2S(valuePtr), length)); 03580 NEXT_INST_F(1, 1, 1); 03581 } else { 03582 TRACE_WITH_OBJ(("%.30s => ERROR: ", O2S(valuePtr)), 03583 Tcl_GetObjResult(interp)); 03584 goto checkForCatch; 03585 } 03586 } 03587 03588 case INST_LIST_INDEX: { 03589 /*** lindex with objc == 3 ***/ 03590 03591 /* Variables also for INST_LIST_INDEX_IMM */ 03592 03593 int listc, idx, opnd, pcAdjustment; 03594 Tcl_Obj **listv; 03595 Tcl_Obj *valuePtr, *value2Ptr; 03596 03597 /* 03598 * Pop the two operands. 03599 */ 03600 03601 value2Ptr = OBJ_AT_TOS; 03602 valuePtr = OBJ_UNDER_TOS; 03603 03604 /* 03605 * Extract the desired list element. 03606 */ 03607 03608 result = TclListObjGetElements(interp, valuePtr, &listc, &listv); 03609 if ((result == TCL_OK) && (value2Ptr->typePtr != &tclListType) 03610 && (TclGetIntForIndexM(NULL , value2Ptr, listc-1, 03611 &idx) == TCL_OK)) { 03612 TclDecrRefCount(value2Ptr); 03613 tosPtr--; 03614 pcAdjustment = 1; 03615 goto lindexFastPath; 03616 } 03617 03618 objResultPtr = TclLindexList(interp, valuePtr, value2Ptr); 03619 if (objResultPtr) { 03620 /* 03621 * Stash the list element on the stack. 03622 */ 03623 03624 TRACE(("%.20s %.20s => %s\n", 03625 O2S(valuePtr), O2S(value2Ptr), O2S(objResultPtr))); 03626 NEXT_INST_F(1, 2, -1); /* Already has the correct refCount */ 03627 } else { 03628 TRACE_WITH_OBJ(("%.30s %.30s => ERROR: ", O2S(valuePtr), 03629 O2S(value2Ptr)), Tcl_GetObjResult(interp)); 03630 result = TCL_ERROR; 03631 goto checkForCatch; 03632 } 03633 03634 case INST_LIST_INDEX_IMM: 03635 /*** lindex with objc==3 and index in bytecode stream ***/ 03636 03637 pcAdjustment = 5; 03638 03639 /* 03640 * Pop the list and get the index. 03641 */ 03642 03643 valuePtr = OBJ_AT_TOS; 03644 opnd = TclGetInt4AtPtr(pc+1); 03645 03646 /* 03647 * Get the contents of the list, making sure that it really is a list 03648 * in the process. 03649 */ 03650 03651 result = TclListObjGetElements(interp, valuePtr, &listc, &listv); 03652 03653 if (result == TCL_OK) { 03654 /* 03655 * Select the list item based on the index. Negative operand means 03656 * end-based indexing. 03657 */ 03658 03659 if (opnd < -1) { 03660 idx = opnd+1 + listc; 03661 } else { 03662 idx = opnd; 03663 } 03664 03665 lindexFastPath: 03666 if (idx >= 0 && idx < listc) { 03667 objResultPtr = listv[idx]; 03668 } else { 03669 TclNewObj(objResultPtr); 03670 } 03671 03672 TRACE_WITH_OBJ(("\"%.30s\" %d => ", O2S(valuePtr), opnd), 03673 objResultPtr); 03674 NEXT_INST_F(pcAdjustment, 1, 1); 03675 } else { 03676 TRACE_WITH_OBJ(("\"%.30s\" %d => ERROR: ", O2S(valuePtr), opnd), 03677 Tcl_GetObjResult(interp)); 03678 goto checkForCatch; 03679 } 03680 } 03681 03682 case INST_LIST_INDEX_MULTI: { 03683 /* 03684 * 'lindex' with multiple index args: 03685 * 03686 * Determine the count of index args. 03687 */ 03688 03689 int numIdx, opnd; 03690 03691 opnd = TclGetUInt4AtPtr(pc+1); 03692 numIdx = opnd-1; 03693 03694 /* 03695 * Do the 'lindex' operation. 03696 */ 03697 03698 objResultPtr = TclLindexFlat(interp, OBJ_AT_DEPTH(numIdx), 03699 numIdx, &OBJ_AT_DEPTH(numIdx - 1)); 03700 03701 /* 03702 * Check for errors. 03703 */ 03704 03705 if (objResultPtr) { 03706 /* 03707 * Set result. 03708 */ 03709 03710 TRACE(("%d => %s\n", opnd, O2S(objResultPtr))); 03711 NEXT_INST_V(5, opnd, -1); 03712 } else { 03713 TRACE_WITH_OBJ(("%d => ERROR: ", opnd), Tcl_GetObjResult(interp)); 03714 result = TCL_ERROR; 03715 goto checkForCatch; 03716 } 03717 } 03718 03719 case INST_LSET_FLAT: { 03720 /* 03721 * Lset with 3, 5, or more args. Get the number of index args. 03722 */ 03723 03724 int numIdx,opnd; 03725 Tcl_Obj *valuePtr, *value2Ptr; 03726 03727 opnd = TclGetUInt4AtPtr(pc + 1); 03728 numIdx = opnd - 2; 03729 03730 /* 03731 * Get the old value of variable, and remove the stack ref. This is 03732 * safe because the variable still references the object; the ref 03733 * count will never go zero here - we can use the smaller macro 03734 * Tcl_DecrRefCount. 03735 */ 03736 03737 value2Ptr = POP_OBJECT(); 03738 Tcl_DecrRefCount(value2Ptr); /* This one should be done here */ 03739 03740 /* 03741 * Get the new element value. 03742 */ 03743 03744 valuePtr = OBJ_AT_TOS; 03745 03746 /* 03747 * Compute the new variable value. 03748 */ 03749 03750 objResultPtr = TclLsetFlat(interp, value2Ptr, numIdx, 03751 &OBJ_AT_DEPTH(numIdx), valuePtr); 03752 03753 /* 03754 * Check for errors. 03755 */ 03756 03757 if (objResultPtr) { 03758 /* 03759 * Set result. 03760 */ 03761 03762 TRACE(("%d => %s\n", opnd, O2S(objResultPtr))); 03763 NEXT_INST_V(5, (numIdx+1), -1); 03764 } else { 03765 TRACE_WITH_OBJ(("%d => ERROR: ", opnd), Tcl_GetObjResult(interp)); 03766 result = TCL_ERROR; 03767 goto checkForCatch; 03768 } 03769 } 03770 03771 case INST_LSET_LIST: { 03772 /* 03773 * 'lset' with 4 args. 03774 */ 03775 03776 Tcl_Obj *objPtr, *valuePtr, *value2Ptr; 03777 03778 /* 03779 * Get the old value of variable, and remove the stack ref. This is 03780 * safe because the variable still references the object; the ref 03781 * count will never go zero here - we can use the smaller macro 03782 * Tcl_DecrRefCount. 03783 */ 03784 03785 objPtr = POP_OBJECT(); 03786 Tcl_DecrRefCount(objPtr); /* This one should be done here. */ 03787 03788 /* 03789 * Get the new element value, and the index list. 03790 */ 03791 03792 valuePtr = OBJ_AT_TOS; 03793 value2Ptr = OBJ_UNDER_TOS; 03794 03795 /* 03796 * Compute the new variable value. 03797 */ 03798 03799 objResultPtr = TclLsetList(interp, objPtr, value2Ptr, valuePtr); 03800 03801 /* 03802 * Check for errors. 03803 */ 03804 03805 if (objResultPtr) { 03806 /* 03807 * Set result. 03808 */ 03809 03810 TRACE(("=> %s\n", O2S(objResultPtr))); 03811 NEXT_INST_F(1, 2, -1); 03812 } else { 03813 TRACE_WITH_OBJ(("\"%.30s\" => ERROR: ", O2S(value2Ptr)), 03814 Tcl_GetObjResult(interp)); 03815 result = TCL_ERROR; 03816 goto checkForCatch; 03817 } 03818 } 03819 03820 case INST_LIST_RANGE_IMM: { 03821 /*** lrange with objc==4 and both indices in bytecode stream ***/ 03822 03823 int listc, fromIdx, toIdx; 03824 Tcl_Obj **listv, *valuePtr; 03825 03826 /* 03827 * Pop the list and get the indices. 03828 */ 03829 03830 valuePtr = OBJ_AT_TOS; 03831 fromIdx = TclGetInt4AtPtr(pc+1); 03832 toIdx = TclGetInt4AtPtr(pc+5); 03833 03834 /* 03835 * Get the contents of the list, making sure that it really is a list 03836 * in the process. 03837 */ 03838 result = TclListObjGetElements(interp, valuePtr, &listc, &listv); 03839 03840 /* 03841 * Skip a lot of work if we're about to throw the result away (common 03842 * with uses of [lassign]). 03843 */ 03844 03845 if (result == TCL_OK) { 03846 #ifndef TCL_COMPILE_DEBUG 03847 if (*(pc+9) == INST_POP) { 03848 NEXT_INST_F(10, 1, 0); 03849 } 03850 #endif 03851 } else { 03852 TRACE_WITH_OBJ(("\"%.30s\" %d %d => ERROR: ", O2S(valuePtr), 03853 fromIdx, toIdx), Tcl_GetObjResult(interp)); 03854 goto checkForCatch; 03855 } 03856 03857 /* 03858 * Adjust the indices for end-based handling. 03859 */ 03860 03861 if (fromIdx < -1) { 03862 fromIdx += 1+listc; 03863 if (fromIdx < -1) { 03864 fromIdx = -1; 03865 } 03866 } else if (fromIdx > listc) { 03867 fromIdx = listc; 03868 } 03869 if (toIdx < -1) { 03870 toIdx += 1+listc; 03871 if (toIdx < -1) { 03872 toIdx = -1; 03873 } 03874 } else if (toIdx > listc) { 03875 toIdx = listc; 03876 } 03877 03878 /* 03879 * Check if we are referring to a valid, non-empty list range, and if 03880 * so, build the list of elements in that range. 03881 */ 03882 03883 if (fromIdx<=toIdx && fromIdx<listc && toIdx>=0) { 03884 if (fromIdx<0) { 03885 fromIdx = 0; 03886 } 03887 if (toIdx >= listc) { 03888 toIdx = listc-1; 03889 } 03890 objResultPtr = Tcl_NewListObj(toIdx-fromIdx+1, listv+fromIdx); 03891 } else { 03892 TclNewObj(objResultPtr); 03893 } 03894 03895 TRACE_WITH_OBJ(("\"%.30s\" %d %d => ", O2S(valuePtr), 03896 TclGetInt4AtPtr(pc+1), TclGetInt4AtPtr(pc+5)), objResultPtr); 03897 NEXT_INST_F(9, 1, 1); 03898 } 03899 03900 case INST_LIST_IN: 03901 case INST_LIST_NOT_IN: { 03902 /* 03903 * Basic list containment operators. 03904 */ 03905 03906 int found, s1len, s2len, llen, i; 03907 Tcl_Obj *valuePtr, *value2Ptr, *o; 03908 char *s1; 03909 const char *s2; 03910 03911 value2Ptr = OBJ_AT_TOS; 03912 valuePtr = OBJ_UNDER_TOS; 03913 03914 /* TODO: Consider more efficient tests than strcmp() */ 03915 s1 = TclGetStringFromObj(valuePtr, &s1len); 03916 result = TclListObjLength(interp, value2Ptr, &llen); 03917 if (result != TCL_OK) { 03918 TRACE_WITH_OBJ(("\"%.30s\" \"%.30s\" => ERROR: ", O2S(valuePtr), 03919 O2S(value2Ptr)), Tcl_GetObjResult(interp)); 03920 goto checkForCatch; 03921 } 03922 found = 0; 03923 if (llen > 0) { 03924 /* 03925 * An empty list doesn't match anything. 03926 */ 03927 03928 i = 0; 03929 do { 03930 Tcl_ListObjIndex(NULL, value2Ptr, i, &o); 03931 if (o != NULL) { 03932 s2 = TclGetStringFromObj(o, &s2len); 03933 } else { 03934 s2 = ""; 03935 } 03936 if (s1len == s2len) { 03937 found = (strcmp(s1, s2) == 0); 03938 } 03939 i++; 03940 } while (i < llen && found == 0); 03941 } 03942 03943 if (*pc == INST_LIST_NOT_IN) { 03944 found = !found; 03945 } 03946 03947 TRACE(("%.20s %.20s => %d\n", O2S(valuePtr), O2S(value2Ptr), found)); 03948 03949 /* 03950 * Peep-hole optimisation: if you're about to jump, do jump from here. 03951 * We're saving the effort of pushing a boolean value only to pop it 03952 * for branching. 03953 */ 03954 03955 pc++; 03956 #ifndef TCL_COMPILE_DEBUG 03957 switch (*pc) { 03958 case INST_JUMP_FALSE1: 03959 NEXT_INST_F((found ? 2 : TclGetInt1AtPtr(pc+1)), 2, 0); 03960 case INST_JUMP_TRUE1: 03961 NEXT_INST_F((found ? TclGetInt1AtPtr(pc+1) : 2), 2, 0); 03962 case INST_JUMP_FALSE4: 03963 NEXT_INST_F((found ? 5 : TclGetInt4AtPtr(pc+1)), 2, 0); 03964 case INST_JUMP_TRUE4: 03965 NEXT_INST_F((found ? TclGetInt4AtPtr(pc+1) : 5), 2, 0); 03966 } 03967 #endif 03968 objResultPtr = constants[found]; 03969 NEXT_INST_F(0, 2, 1); 03970 } 03971 03972 /* 03973 * End of INST_LIST and related instructions. 03974 * --------------------------------------------------------- 03975 */ 03976 03977 case INST_STR_EQ: 03978 case INST_STR_NEQ: { 03979 /* 03980 * String (in)equality check 03981 * TODO: Consider merging into INST_STR_CMP 03982 */ 03983 03984 int iResult; 03985 Tcl_Obj *valuePtr, *value2Ptr; 03986 03987 value2Ptr = OBJ_AT_TOS; 03988 valuePtr = OBJ_UNDER_TOS; 03989 03990 if (valuePtr == value2Ptr) { 03991 /* 03992 * On the off-chance that the objects are the same, we don't 03993 * really have to think hard about equality. 03994 */ 03995 03996 iResult = (*pc == INST_STR_EQ); 03997 } else { 03998 char *s1, *s2; 03999 int s1len, s2len; 04000 04001 s1 = TclGetStringFromObj(valuePtr, &s1len); 04002 s2 = TclGetStringFromObj(value2Ptr, &s2len); 04003 if (s1len == s2len) { 04004 /* 04005 * We only need to check (in)equality when we have equal 04006 * length strings. 04007 */ 04008 04009 if (*pc == INST_STR_NEQ) { 04010 iResult = (strcmp(s1, s2) != 0); 04011 } else { 04012 /* INST_STR_EQ */ 04013 iResult = (strcmp(s1, s2) == 0); 04014 } 04015 } else { 04016 iResult = (*pc == INST_STR_NEQ); 04017 } 04018 } 04019 04020 TRACE(("%.20s %.20s => %d\n", O2S(valuePtr),O2S(value2Ptr),iResult)); 04021 04022 /* 04023 * Peep-hole optimisation: if you're about to jump, do jump from here. 04024 */ 04025 04026 pc++; 04027 #ifndef TCL_COMPILE_DEBUG 04028 switch (*pc) { 04029 case INST_JUMP_FALSE1: 04030 NEXT_INST_F((iResult? 2 : TclGetInt1AtPtr(pc+1)), 2, 0); 04031 case INST_JUMP_TRUE1: 04032 NEXT_INST_F((iResult? TclGetInt1AtPtr(pc+1) : 2), 2, 0); 04033 case INST_JUMP_FALSE4: 04034 NEXT_INST_F((iResult? 5 : TclGetInt4AtPtr(pc+1)), 2, 0); 04035 case INST_JUMP_TRUE4: 04036 NEXT_INST_F((iResult? TclGetInt4AtPtr(pc+1) : 5), 2, 0); 04037 } 04038 #endif 04039 objResultPtr = constants[iResult]; 04040 NEXT_INST_F(0, 2, 1); 04041 } 04042 04043 case INST_STR_CMP: { 04044 /* 04045 * String compare. 04046 */ 04047 04048 const char *s1, *s2; 04049 int s1len, s2len, iResult; 04050 Tcl_Obj *valuePtr, *value2Ptr; 04051 04052 stringCompare: 04053 value2Ptr = OBJ_AT_TOS; 04054 valuePtr = OBJ_UNDER_TOS; 04055 04056 /* 04057 * The comparison function should compare up to the minimum byte 04058 * length only. 04059 */ 04060 04061 if (valuePtr == value2Ptr) { 04062 /* 04063 * In the pure equality case, set lengths too for the checks below 04064 * (or we could goto beyond it). 04065 */ 04066 04067 iResult = s1len = s2len = 0; 04068 } else if ((valuePtr->typePtr == &tclByteArrayType) 04069 && (value2Ptr->typePtr == &tclByteArrayType)) { 04070 s1 = (char *) Tcl_GetByteArrayFromObj(valuePtr, &s1len); 04071 s2 = (char *) Tcl_GetByteArrayFromObj(value2Ptr, &s2len); 04072 iResult = memcmp(s1, s2, 04073 (size_t) ((s1len < s2len) ? s1len : s2len)); 04074 } else if (((valuePtr->typePtr == &tclStringType) 04075 && (value2Ptr->typePtr == &tclStringType))) { 04076 /* 04077 * Do a unicode-specific comparison if both of the args are of 04078 * String type. If the char length == byte length, we can do a 04079 * memcmp. In benchmark testing this proved the most efficient 04080 * check between the unicode and string comparison operations. 04081 */ 04082 04083 s1len = Tcl_GetCharLength(valuePtr); 04084 s2len = Tcl_GetCharLength(value2Ptr); 04085 if ((s1len == valuePtr->length) && (s2len == value2Ptr->length)) { 04086 iResult = memcmp(valuePtr->bytes, value2Ptr->bytes, 04087 (unsigned) ((s1len < s2len) ? s1len : s2len)); 04088 } else { 04089 iResult = TclUniCharNcmp(Tcl_GetUnicode(valuePtr), 04090 Tcl_GetUnicode(value2Ptr), 04091 (unsigned) ((s1len < s2len) ? s1len : s2len)); 04092 } 04093 } else { 04094 /* 04095 * We can't do a simple memcmp in order to handle the special Tcl 04096 * \xC0\x80 null encoding for utf-8. 04097 */ 04098 04099 s1 = TclGetStringFromObj(valuePtr, &s1len); 04100 s2 = TclGetStringFromObj(value2Ptr, &s2len); 04101 iResult = TclpUtfNcmp2(s1, s2, 04102 (size_t) ((s1len < s2len) ? s1len : s2len)); 04103 } 04104 04105 /* 04106 * Make sure only -1,0,1 is returned 04107 * TODO: consider peephole opt. 04108 */ 04109 04110 if (iResult == 0) { 04111 iResult = s1len - s2len; 04112 } 04113 04114 if (*pc != INST_STR_CMP) { 04115 /* 04116 * Take care of the opcodes that goto'ed into here. 04117 */ 04118 04119 switch (*pc) { 04120 case INST_EQ: 04121 iResult = (iResult == 0); 04122 break; 04123 case INST_NEQ: 04124 iResult = (iResult != 0); 04125 break; 04126 case INST_LT: 04127 iResult = (iResult < 0); 04128 break; 04129 case INST_GT: 04130 iResult = (iResult > 0); 04131 break; 04132 case INST_LE: 04133 iResult = (iResult <= 0); 04134 break; 04135 case INST_GE: 04136 iResult = (iResult >= 0); 04137 break; 04138 } 04139 } 04140 if (iResult < 0) { 04141 TclNewIntObj(objResultPtr, -1); 04142 TRACE(("%.20s %.20s => %d\n", O2S(valuePtr), O2S(value2Ptr), -1)); 04143 } else { 04144 objResultPtr = constants[(iResult>0)]; 04145 TRACE(("%.20s %.20s => %d\n", O2S(valuePtr), O2S(value2Ptr), 04146 (iResult > 0))); 04147 } 04148 04149 NEXT_INST_F(1, 2, 1); 04150 } 04151 04152 case INST_STR_LEN: { 04153 int length; 04154 Tcl_Obj *valuePtr; 04155 04156 valuePtr = OBJ_AT_TOS; 04157 04158 if (valuePtr->typePtr == &tclByteArrayType) { 04159 (void) Tcl_GetByteArrayFromObj(valuePtr, &length); 04160 } else { 04161 length = Tcl_GetCharLength(valuePtr); 04162 } 04163 TclNewIntObj(objResultPtr, length); 04164 TRACE(("%.20s => %d\n", O2S(valuePtr), length)); 04165 NEXT_INST_F(1, 1, 1); 04166 } 04167 04168 case INST_STR_INDEX: { 04169 /* 04170 * String compare. 04171 */ 04172 04173 int index, length; 04174 char *bytes; 04175 Tcl_Obj *valuePtr, *value2Ptr; 04176 04177 bytes = NULL; /* lint */ 04178 value2Ptr = OBJ_AT_TOS; 04179 valuePtr = OBJ_UNDER_TOS; 04180 04181 /* 04182 * If we have a ByteArray object, avoid indexing in the Utf string 04183 * since the byte array contains one byte per character. Otherwise, 04184 * use the Unicode string rep to get the index'th char. 04185 */ 04186 04187 if (valuePtr->typePtr == &tclByteArrayType) { 04188 bytes = (char *)Tcl_GetByteArrayFromObj(valuePtr, &length); 04189 } else { 04190 /* 04191 * Get Unicode char length to calulate what 'end' means. 04192 */ 04193 04194 length = Tcl_GetCharLength(valuePtr); 04195 } 04196 04197 result = TclGetIntForIndexM(interp, value2Ptr, length - 1, &index); 04198 if (result != TCL_OK) { 04199 goto checkForCatch; 04200 } 04201 04202 if ((index >= 0) && (index < length)) { 04203 if (valuePtr->typePtr == &tclByteArrayType) { 04204 objResultPtr = Tcl_NewByteArrayObj((unsigned char *) 04205 (&bytes[index]), 1); 04206 } else if (valuePtr->bytes && length == valuePtr->length) { 04207 objResultPtr = Tcl_NewStringObj((const char *) 04208 (&valuePtr->bytes[index]), 1); 04209 } else { 04210 char buf[TCL_UTF_MAX]; 04211 Tcl_UniChar ch; 04212 04213 ch = Tcl_GetUniChar(valuePtr, index); 04214 04215 /* 04216 * This could be: Tcl_NewUnicodeObj((const Tcl_UniChar *)&ch, 04217 * 1) but creating the object as a string seems to be faster 04218 * in practical use. 04219 */ 04220 04221 length = Tcl_UniCharToUtf(ch, buf); 04222 objResultPtr = Tcl_NewStringObj(buf, length); 04223 } 04224 } else { 04225 TclNewObj(objResultPtr); 04226 } 04227 04228 TRACE(("%.20s %.20s => %s\n", O2S(valuePtr), O2S(value2Ptr), 04229 O2S(objResultPtr))); 04230 NEXT_INST_F(1, 2, 1); 04231 } 04232 04233 case INST_STR_MATCH: { 04234 int nocase, match; 04235 Tcl_Obj *valuePtr, *value2Ptr; 04236 04237 nocase = TclGetInt1AtPtr(pc+1); 04238 valuePtr = OBJ_AT_TOS; /* String */ 04239 value2Ptr = OBJ_UNDER_TOS; /* Pattern */ 04240 04241 /* 04242 * Check that at least one of the objects is Unicode before promoting 04243 * both. 04244 */ 04245 04246 if ((valuePtr->typePtr == &tclStringType) 04247 || (value2Ptr->typePtr == &tclStringType)) { 04248 Tcl_UniChar *ustring1, *ustring2; 04249 int length1, length2; 04250 04251 ustring1 = Tcl_GetUnicodeFromObj(valuePtr, &length1); 04252 ustring2 = Tcl_GetUnicodeFromObj(value2Ptr, &length2); 04253 match = TclUniCharMatch(ustring1, length1, ustring2, length2, 04254 nocase); 04255 } else if ((valuePtr->typePtr == &tclByteArrayType) && !nocase) { 04256 unsigned char *string1, *string2; 04257 int length1, length2; 04258 04259 string1 = Tcl_GetByteArrayFromObj(valuePtr, &length1); 04260 string2 = Tcl_GetByteArrayFromObj(value2Ptr, &length2); 04261 match = TclByteArrayMatch(string1, length1, string2, length2, 0); 04262 } else { 04263 match = Tcl_StringCaseMatch(TclGetString(valuePtr), 04264 TclGetString(value2Ptr), nocase); 04265 } 04266 04267 /* 04268 * Reuse value2Ptr object already on stack if possible. Adjustment is 04269 * 2 due to the nocase byte 04270 * TODO: consider peephole opt. 04271 */ 04272 04273 TRACE(("%.20s %.20s => %d\n", O2S(valuePtr), O2S(value2Ptr), match)); 04274 objResultPtr = constants[match]; 04275 NEXT_INST_F(2, 2, 1); 04276 } 04277 04278 case INST_REGEXP: { 04279 int cflags, match; 04280 Tcl_Obj *valuePtr, *value2Ptr; 04281 Tcl_RegExp regExpr; 04282 04283 cflags = TclGetInt1AtPtr(pc+1); /* RE compile flages like NOCASE */ 04284 valuePtr = OBJ_AT_TOS; /* String */ 04285 value2Ptr = OBJ_UNDER_TOS; /* Pattern */ 04286 04287 regExpr = Tcl_GetRegExpFromObj(interp, value2Ptr, cflags); 04288 if (regExpr == NULL) { 04289 match = -1; 04290 } else { 04291 match = Tcl_RegExpExecObj(interp, regExpr, valuePtr, 0, 0, 0); 04292 } 04293 04294 /* 04295 * Adjustment is 2 due to the nocase byte 04296 */ 04297 04298 if (match < 0) { 04299 objResultPtr = Tcl_GetObjResult(interp); 04300 TRACE_WITH_OBJ(("%.20s %.20s => ERROR: ", 04301 O2S(valuePtr), O2S(value2Ptr)), objResultPtr); 04302 result = TCL_ERROR; 04303 goto checkForCatch; 04304 } else { 04305 TRACE(("%.20s %.20s => %d\n", 04306 O2S(valuePtr), O2S(value2Ptr), match)); 04307 objResultPtr = constants[match]; 04308 NEXT_INST_F(2, 2, 1); 04309 } 04310 } 04311 04312 case INST_EQ: 04313 case INST_NEQ: 04314 case INST_LT: 04315 case INST_GT: 04316 case INST_LE: 04317 case INST_GE: { 04318 Tcl_Obj *valuePtr = OBJ_UNDER_TOS; 04319 Tcl_Obj *value2Ptr = OBJ_AT_TOS; 04320 ClientData ptr1, ptr2; 04321 int iResult = 0, compare = 0, type1, type2; 04322 double d1, d2, tmp; 04323 long l1, l2; 04324 mp_int big1, big2; 04325 #ifndef NO_WIDE_TYPE 04326 Tcl_WideInt w1, w2; 04327 #endif 04328 04329 if (GetNumberFromObj(NULL, valuePtr, &ptr1, &type1) != TCL_OK) { 04330 /* 04331 * At least one non-numeric argument - compare as strings. 04332 */ 04333 04334 goto stringCompare; 04335 } 04336 if (type1 == TCL_NUMBER_NAN) { 04337 /* 04338 * NaN first arg: NaN != to everything, other compares are false. 04339 */ 04340 04341 iResult = (*pc == INST_NEQ); 04342 goto foundResult; 04343 } 04344 if (valuePtr == value2Ptr) { 04345 compare = MP_EQ; 04346 goto convertComparison; 04347 } 04348 if (GetNumberFromObj(NULL, value2Ptr, &ptr2, &type2) != TCL_OK) { 04349 /* 04350 * At least one non-numeric argument - compare as strings. 04351 */ 04352 04353 goto stringCompare; 04354 } 04355 if (type2 == TCL_NUMBER_NAN) { 04356 /* 04357 * NaN 2nd arg: NaN != to everything, other compares are false. 04358 */ 04359 04360 iResult = (*pc == INST_NEQ); 04361 goto foundResult; 04362 } 04363 switch (type1) { 04364 case TCL_NUMBER_LONG: 04365 l1 = *((const long *)ptr1); 04366 switch (type2) { 04367 case TCL_NUMBER_LONG: 04368 l2 = *((const long *)ptr2); 04369 longCompare: 04370 compare = (l1 < l2) ? MP_LT : ((l1 > l2) ? MP_GT : MP_EQ); 04371 break; 04372 #ifndef NO_WIDE_TYPE 04373 case TCL_NUMBER_WIDE: 04374 w2 = *((const Tcl_WideInt *)ptr2); 04375 w1 = (Tcl_WideInt)l1; 04376 goto wideCompare; 04377 #endif 04378 case TCL_NUMBER_DOUBLE: 04379 d2 = *((const double *)ptr2); 04380 d1 = (double) l1; 04381 04382 /* 04383 * If the double has a fractional part, or if the long can be 04384 * converted to double without loss of precision, then compare 04385 * as doubles. 04386 */ 04387 04388 if (DBL_MANT_DIG > CHAR_BIT*sizeof(long) 04389 || l1 == (long) d1 04390 || modf(d2, &tmp) != 0.0) { 04391 goto doubleCompare; 04392 } 04393 04394 /* 04395 * Otherwise, to make comparision based on full precision, 04396 * need to convert the double to a suitably sized integer. 04397 * 04398 * Need this to get comparsions like 04399 * expr 20000000000000003 < 20000000000000004.0 04400 * right. Converting the first argument to double will yield 04401 * two double values that are equivalent within double 04402 * precision. Converting the double to an integer gets done 04403 * exactly, then integer comparison can tell the difference. 04404 */ 04405 04406 if (d2 < (double)LONG_MIN) { 04407 compare = MP_GT; 04408 break; 04409 } 04410 if (d2 > (double)LONG_MAX) { 04411 compare = MP_LT; 04412 break; 04413 } 04414 l2 = (long) d2; 04415 goto longCompare; 04416 case TCL_NUMBER_BIG: 04417 Tcl_TakeBignumFromObj(NULL, value2Ptr, &big2); 04418 if (mp_cmp_d(&big2, 0) == MP_LT) { 04419 compare = MP_GT; 04420 } else { 04421 compare = MP_LT; 04422 } 04423 mp_clear(&big2); 04424 } 04425 break; 04426 04427 #ifndef NO_WIDE_TYPE 04428 case TCL_NUMBER_WIDE: 04429 w1 = *((const Tcl_WideInt *)ptr1); 04430 switch (type2) { 04431 case TCL_NUMBER_WIDE: 04432 w2 = *((const Tcl_WideInt *)ptr2); 04433 wideCompare: 04434 compare = (w1 < w2) ? MP_LT : ((w1 > w2) ? MP_GT : MP_EQ); 04435 break; 04436 case TCL_NUMBER_LONG: 04437 l2 = *((const long *)ptr2); 04438 w2 = (Tcl_WideInt)l2; 04439 goto wideCompare; 04440 case TCL_NUMBER_DOUBLE: 04441 d2 = *((const double *)ptr2); 04442 d1 = (double) w1; 04443 if (DBL_MANT_DIG > CHAR_BIT*sizeof(Tcl_WideInt) 04444 || w1 == (Tcl_WideInt) d1 04445 || modf(d2, &tmp) != 0.0) { 04446 goto doubleCompare; 04447 } 04448 if (d2 < (double)LLONG_MIN) { 04449 compare = MP_GT; 04450 break; 04451 } 04452 if (d2 > (double)LLONG_MAX) { 04453 compare = MP_LT; 04454 break; 04455 } 04456 w2 = (Tcl_WideInt) d2; 04457 goto wideCompare; 04458 case TCL_NUMBER_BIG: 04459 Tcl_TakeBignumFromObj(NULL, value2Ptr, &big2); 04460 if (mp_cmp_d(&big2, 0) == MP_LT) { 04461 compare = MP_GT; 04462 } else { 04463 compare = MP_LT; 04464 } 04465 mp_clear(&big2); 04466 } 04467 break; 04468 #endif 04469 04470 case TCL_NUMBER_DOUBLE: 04471 d1 = *((const double *)ptr1); 04472 switch (type2) { 04473 case TCL_NUMBER_DOUBLE: 04474 d2 = *((const double *)ptr2); 04475 doubleCompare: 04476 compare = (d1 < d2) ? MP_LT : ((d1 > d2) ? MP_GT : MP_EQ); 04477 break; 04478 case TCL_NUMBER_LONG: 04479 l2 = *((const long *)ptr2); 04480 d2 = (double) l2; 04481 if (DBL_MANT_DIG > CHAR_BIT*sizeof(long) 04482 || l2 == (long) d2 04483 || modf(d1, &tmp) != 0.0) { 04484 goto doubleCompare; 04485 } 04486 if (d1 < (double)LONG_MIN) { 04487 compare = MP_LT; 04488 break; 04489 } 04490 if (d1 > (double)LONG_MAX) { 04491 compare = MP_GT; 04492 break; 04493 } 04494 l1 = (long) d1; 04495 goto longCompare; 04496 #ifndef NO_WIDE_TYPE 04497 case TCL_NUMBER_WIDE: 04498 w2 = *((const Tcl_WideInt *)ptr2); 04499 d2 = (double) w2; 04500 if (DBL_MANT_DIG > CHAR_BIT*sizeof(Tcl_WideInt) 04501 || w2 == (Tcl_WideInt) d2 04502 || modf(d1, &tmp) != 0.0) { 04503 goto doubleCompare; 04504 } 04505 if (d1 < (double)LLONG_MIN) { 04506 compare = MP_LT; 04507 break; 04508 } 04509 if (d1 > (double)LLONG_MAX) { 04510 compare = MP_GT; 04511 break; 04512 } 04513 w1 = (Tcl_WideInt) d1; 04514 goto wideCompare; 04515 #endif 04516 case TCL_NUMBER_BIG: 04517 if (TclIsInfinite(d1)) { 04518 compare = (d1 > 0.0) ? MP_GT : MP_LT; 04519 break; 04520 } 04521 Tcl_TakeBignumFromObj(NULL, value2Ptr, &big2); 04522 if ((d1 < (double)LONG_MAX) && (d1 > (double)LONG_MIN)) { 04523 if (mp_cmp_d(&big2, 0) == MP_LT) { 04524 compare = MP_GT; 04525 } else { 04526 compare = MP_LT; 04527 } 04528 mp_clear(&big2); 04529 break; 04530 } 04531 if (DBL_MANT_DIG > CHAR_BIT*sizeof(long) 04532 && modf(d1, &tmp) != 0.0) { 04533 d2 = TclBignumToDouble(&big2); 04534 mp_clear(&big2); 04535 goto doubleCompare; 04536 } 04537 Tcl_InitBignumFromDouble(NULL, d1, &big1); 04538 goto bigCompare; 04539 } 04540 break; 04541 04542 case TCL_NUMBER_BIG: 04543 Tcl_TakeBignumFromObj(NULL, valuePtr, &big1); 04544 switch (type2) { 04545 #ifndef NO_WIDE_TYPE 04546 case TCL_NUMBER_WIDE: 04547 #endif 04548 case TCL_NUMBER_LONG: 04549 compare = mp_cmp_d(&big1, 0); 04550 mp_clear(&big1); 04551 break; 04552 case TCL_NUMBER_DOUBLE: 04553 d2 = *((const double *)ptr2); 04554 if (TclIsInfinite(d2)) { 04555 compare = (d2 > 0.0) ? MP_LT : MP_GT; 04556 mp_clear(&big1); 04557 break; 04558 } 04559 if ((d2 < (double)LONG_MAX) && (d2 > (double)LONG_MIN)) { 04560 compare = mp_cmp_d(&big1, 0); 04561 mp_clear(&big1); 04562 break; 04563 } 04564 if (DBL_MANT_DIG > CHAR_BIT*sizeof(long) 04565 && modf(d2, &tmp) != 0.0) { 04566 d1 = TclBignumToDouble(&big1); 04567 mp_clear(&big1); 04568 goto doubleCompare; 04569 } 04570 Tcl_InitBignumFromDouble(NULL, d2, &big2); 04571 goto bigCompare; 04572 case TCL_NUMBER_BIG: 04573 Tcl_TakeBignumFromObj(NULL, value2Ptr, &big2); 04574 bigCompare: 04575 compare = mp_cmp(&big1, &big2); 04576 mp_clear(&big1); 04577 mp_clear(&big2); 04578 } 04579 } 04580 04581 /* 04582 * Turn comparison outcome into appropriate result for opcode. 04583 */ 04584 04585 convertComparison: 04586 switch (*pc) { 04587 case INST_EQ: 04588 iResult = (compare == MP_EQ); 04589 break; 04590 case INST_NEQ: 04591 iResult = (compare != MP_EQ); 04592 break; 04593 case INST_LT: 04594 iResult = (compare == MP_LT); 04595 break; 04596 case INST_GT: 04597 iResult = (compare == MP_GT); 04598 break; 04599 case INST_LE: 04600 iResult = (compare != MP_GT); 04601 break; 04602 case INST_GE: 04603 iResult = (compare != MP_LT); 04604 break; 04605 } 04606 04607 /* 04608 * Peep-hole optimisation: if you're about to jump, do jump from here. 04609 */ 04610 04611 foundResult: 04612 pc++; 04613 #ifndef TCL_COMPILE_DEBUG 04614 switch (*pc) { 04615 case INST_JUMP_FALSE1: 04616 NEXT_INST_F((iResult? 2 : TclGetInt1AtPtr(pc+1)), 2, 0); 04617 case INST_JUMP_TRUE1: 04618 NEXT_INST_F((iResult? TclGetInt1AtPtr(pc+1) : 2), 2, 0); 04619 case INST_JUMP_FALSE4: 04620 NEXT_INST_F((iResult? 5 : TclGetInt4AtPtr(pc+1)), 2, 0); 04621 case INST_JUMP_TRUE4: 04622 NEXT_INST_F((iResult? TclGetInt4AtPtr(pc+1) : 5), 2, 0); 04623 } 04624 #endif 04625 objResultPtr = constants[iResult]; 04626 NEXT_INST_F(0, 2, 1); 04627 } 04628 04629 case INST_MOD: 04630 case INST_LSHIFT: 04631 case INST_RSHIFT: { 04632 Tcl_Obj *value2Ptr = OBJ_AT_TOS; 04633 Tcl_Obj *valuePtr = OBJ_UNDER_TOS; 04634 ClientData ptr1, ptr2; 04635 int invalid, shift, type1, type2; 04636 long l1 = 0; 04637 04638 result = GetNumberFromObj(NULL, valuePtr, &ptr1, &type1); 04639 if ((result != TCL_OK) || (type1 == TCL_NUMBER_DOUBLE) 04640 || (type1 == TCL_NUMBER_NAN)) { 04641 result = TCL_ERROR; 04642 TRACE(("%.20s %.20s => ILLEGAL 1st TYPE %s\n", O2S(valuePtr), 04643 O2S(value2Ptr), (valuePtr->typePtr? 04644 valuePtr->typePtr->name : "null"))); 04645 IllegalExprOperandType(interp, pc, valuePtr); 04646 goto checkForCatch; 04647 } 04648 04649 result = GetNumberFromObj(NULL, value2Ptr, &ptr2, &type2); 04650 if ((result != TCL_OK) || (type2 == TCL_NUMBER_DOUBLE) 04651 || (type2 == TCL_NUMBER_NAN)) { 04652 result = TCL_ERROR; 04653 TRACE(("%.20s %.20s => ILLEGAL 2nd TYPE %s\n", O2S(valuePtr), 04654 O2S(value2Ptr), (value2Ptr->typePtr? 04655 value2Ptr->typePtr->name : "null"))); 04656 IllegalExprOperandType(interp, pc, value2Ptr); 04657 goto checkForCatch; 04658 } 04659 04660 if (*pc == INST_MOD) { 04661 /* TODO: Attempts to re-use unshared operands on stack */ 04662 04663 long l2 = 0; /* silence gcc warning */ 04664 04665 if (type2 == TCL_NUMBER_LONG) { 04666 l2 = *((const long *)ptr2); 04667 if (l2 == 0) { 04668 TRACE(("%s %s => DIVIDE BY ZERO\n", O2S(valuePtr), 04669 O2S(value2Ptr))); 04670 goto divideByZero; 04671 } 04672 if ((l2 == 1) || (l2 == -1)) { 04673 /* 04674 * Div. by |1| always yields remainder of 0. 04675 */ 04676 04677 objResultPtr = constants[0]; 04678 TRACE(("%s\n", O2S(objResultPtr))); 04679 NEXT_INST_F(1, 2, 1); 04680 } 04681 } 04682 if (type1 == TCL_NUMBER_LONG) { 04683 l1 = *((const long *)ptr1); 04684 if (l1 == 0) { 04685 /* 04686 * 0 % (non-zero) always yields remainder of 0. 04687 */ 04688 04689 objResultPtr = constants[0]; 04690 TRACE(("%s\n", O2S(objResultPtr))); 04691 NEXT_INST_F(1, 2, 1); 04692 } 04693 if (type2 == TCL_NUMBER_LONG) { 04694 /* 04695 * Both operands are long; do native calculation. 04696 */ 04697 04698 long lRemainder, lQuotient = l1 / l2; 04699 04700 /* 04701 * Force Tcl's integer division rules. 04702 * TODO: examine for logic simplification 04703 */ 04704 04705 if ((lQuotient < 0 || (lQuotient == 0 && 04706 ((l1 < 0 && l2 > 0) || (l1 > 0 && l2 < 0)))) && 04707 (lQuotient * l2 != l1)) { 04708 lQuotient -= 1; 04709 } 04710 lRemainder = l1 - l2*lQuotient; 04711 TclNewLongObj(objResultPtr, lRemainder); 04712 TRACE(("%s\n", O2S(objResultPtr))); 04713 NEXT_INST_F(1, 2, 1); 04714 } 04715 04716 /* 04717 * First operand fits in long; second does not, so the second 04718 * has greater magnitude than first. No need to divide to 04719 * determine the remainder. 04720 */ 04721 04722 #ifndef NO_WIDE_TYPE 04723 if (type2 == TCL_NUMBER_WIDE) { 04724 Tcl_WideInt w2 = *((const Tcl_WideInt *)ptr2); 04725 04726 if ((l1 > 0) ^ (w2 > (Tcl_WideInt)0)) { 04727 /* 04728 * Arguments are opposite sign; remainder is sum. 04729 */ 04730 04731 objResultPtr = Tcl_NewWideIntObj(w2+(Tcl_WideInt)l1); 04732 TRACE(("%s\n", O2S(objResultPtr))); 04733 NEXT_INST_F(1, 2, 1); 04734 } 04735 04736 /* 04737 * Arguments are same sign; remainder is first operand. 04738 */ 04739 04740 TRACE(("%s\n", O2S(valuePtr))); 04741 NEXT_INST_F(1, 1, 0); 04742 } 04743 #endif 04744 { 04745 mp_int big2; 04746 04747 Tcl_TakeBignumFromObj(NULL, value2Ptr, &big2); 04748 04749 /* TODO: internals intrusion */ 04750 if ((l1 > 0) ^ (big2.sign == MP_ZPOS)) { 04751 /* 04752 * Arguments are opposite sign; remainder is sum. 04753 */ 04754 04755 mp_int big1; 04756 04757 TclBNInitBignumFromLong(&big1, l1); 04758 mp_add(&big2, &big1, &big2); 04759 mp_clear(&big1); 04760 objResultPtr = Tcl_NewBignumObj(&big2); 04761 TRACE(("%s\n", O2S(objResultPtr))); 04762 NEXT_INST_F(1, 2, 1); 04763 } 04764 04765 /* 04766 * Arguments are same sign; remainder is first operand. 04767 */ 04768 04769 mp_clear(&big2); 04770 TRACE(("%s\n", O2S(valuePtr))); 04771 NEXT_INST_F(1, 1, 0); 04772 } 04773 } 04774 #ifndef NO_WIDE_TYPE 04775 if (type1 == TCL_NUMBER_WIDE) { 04776 Tcl_WideInt w1 = *((const Tcl_WideInt *)ptr1); 04777 04778 if (type2 != TCL_NUMBER_BIG) { 04779 Tcl_WideInt w2, wQuotient, wRemainder; 04780 04781 Tcl_GetWideIntFromObj(NULL, value2Ptr, &w2); 04782 wQuotient = w1 / w2; 04783 04784 /* 04785 * Force Tcl's integer division rules. 04786 * TODO: examine for logic simplification 04787 */ 04788 04789 if (((wQuotient < (Tcl_WideInt) 0) 04790 || ((wQuotient == (Tcl_WideInt) 0) 04791 && ((w1 < (Tcl_WideInt)0 && w2 > (Tcl_WideInt)0) 04792 || (w1 > (Tcl_WideInt)0 && w2 < (Tcl_WideInt)0)))) 04793 && (wQuotient * w2 != w1)) { 04794 wQuotient -= (Tcl_WideInt) 1; 04795 } 04796 wRemainder = w1 - w2*wQuotient; 04797 objResultPtr = Tcl_NewWideIntObj(wRemainder); 04798 TRACE(("%s\n", O2S(objResultPtr))); 04799 NEXT_INST_F(1, 2, 1); 04800 } 04801 { 04802 mp_int big2; 04803 Tcl_TakeBignumFromObj(NULL, value2Ptr, &big2); 04804 04805 /* TODO: internals intrusion */ 04806 if ((w1 > ((Tcl_WideInt) 0)) ^ (big2.sign == MP_ZPOS)) { 04807 /* 04808 * Arguments are opposite sign; remainder is sum. 04809 */ 04810 04811 mp_int big1; 04812 04813 TclBNInitBignumFromWideInt(&big1, w1); 04814 mp_add(&big2, &big1, &big2); 04815 mp_clear(&big1); 04816 objResultPtr = Tcl_NewBignumObj(&big2); 04817 TRACE(("%s\n", O2S(objResultPtr))); 04818 NEXT_INST_F(1, 2, 1); 04819 } 04820 04821 /* 04822 * Arguments are same sign; remainder is first operand. 04823 */ 04824 04825 mp_clear(&big2); 04826 TRACE(("%s\n", O2S(valuePtr))); 04827 NEXT_INST_F(1, 1, 0); 04828 } 04829 } 04830 #endif 04831 { 04832 mp_int big1, big2, bigResult, bigRemainder; 04833 04834 Tcl_GetBignumFromObj(NULL, valuePtr, &big1); 04835 Tcl_GetBignumFromObj(NULL, value2Ptr, &big2); 04836 mp_init(&bigResult); 04837 mp_init(&bigRemainder); 04838 mp_div(&big1, &big2, &bigResult, &bigRemainder); 04839 if (!mp_iszero(&bigRemainder) 04840 && (bigRemainder.sign != big2.sign)) { 04841 /* 04842 * Convert to Tcl's integer division rules. 04843 */ 04844 04845 mp_sub_d(&bigResult, 1, &bigResult); 04846 mp_add(&bigRemainder, &big2, &bigRemainder); 04847 } 04848 mp_copy(&bigRemainder, &bigResult); 04849 mp_clear(&bigRemainder); 04850 mp_clear(&big1); 04851 mp_clear(&big2); 04852 TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); 04853 if (Tcl_IsShared(valuePtr)) { 04854 objResultPtr = Tcl_NewBignumObj(&bigResult); 04855 TRACE(("%s\n", O2S(objResultPtr))); 04856 NEXT_INST_F(1, 2, 1); 04857 } 04858 Tcl_SetBignumObj(valuePtr, &bigResult); 04859 TRACE(("%s\n", O2S(valuePtr))); 04860 NEXT_INST_F(1, 1, 0); 04861 } 04862 } 04863 04864 /* 04865 * Reject negative shift argument. 04866 */ 04867 04868 switch (type2) { 04869 case TCL_NUMBER_LONG: 04870 invalid = (*((const long *)ptr2) < (long)0); 04871 break; 04872 #ifndef NO_WIDE_TYPE 04873 case TCL_NUMBER_WIDE: 04874 invalid = (*((const Tcl_WideInt *)ptr2) < (Tcl_WideInt)0); 04875 break; 04876 #endif 04877 case TCL_NUMBER_BIG: { 04878 mp_int big2; 04879 04880 Tcl_TakeBignumFromObj(NULL, value2Ptr, &big2); 04881 invalid = (mp_cmp_d(&big2, 0) == MP_LT); 04882 mp_clear(&big2); 04883 break; 04884 } 04885 default: 04886 /* Unused, here to silence compiler warning */ 04887 invalid = 0; 04888 } 04889 if (invalid) { 04890 Tcl_SetObjResult(interp, 04891 Tcl_NewStringObj("negative shift argument", -1)); 04892 result = TCL_ERROR; 04893 goto checkForCatch; 04894 } 04895 04896 /* 04897 * Zero shifted any number of bits is still zero. 04898 */ 04899 04900 if ((type1==TCL_NUMBER_LONG) && (*((const long *)ptr1) == (long)0)) { 04901 TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); 04902 objResultPtr = constants[0]; 04903 TRACE(("%s\n", O2S(objResultPtr))); 04904 NEXT_INST_F(1, 2, 1); 04905 } 04906 04907 if (*pc == INST_LSHIFT) { 04908 /* 04909 * Large left shifts create integer overflow. 04910 * 04911 * BEWARE! Can't use Tcl_GetIntFromObj() here because that 04912 * converts values in the (unsigned) range to their signed int 04913 * counterparts, leading to incorrect results. 04914 */ 04915 04916 if ((type2 != TCL_NUMBER_LONG) 04917 || (*((const long *)ptr2) > (long) INT_MAX)) { 04918 /* 04919 * Technically, we could hold the value (1 << (INT_MAX+1)) in 04920 * an mp_int, but since we're using mp_mul_2d() to do the 04921 * work, and it takes only an int argument, that's a good 04922 * place to draw the line. 04923 */ 04924 04925 Tcl_SetObjResult(interp, Tcl_NewStringObj( 04926 "integer value too large to represent", -1)); 04927 result = TCL_ERROR; 04928 goto checkForCatch; 04929 } 04930 shift = (int)(*((const long *)ptr2)); 04931 04932 /* 04933 * Handle shifts within the native long range. 04934 */ 04935 04936 TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); 04937 if ((type1 == TCL_NUMBER_LONG) 04938 && (size_t) shift < CHAR_BIT*sizeof(long) 04939 && ((l1 = *(const long *)ptr1) != 0) 04940 && !((l1>0 ? l1 : ~l1) 04941 & -(1L<<(CHAR_BIT*sizeof(long) - 1 - shift)))) { 04942 TclNewLongObj(objResultPtr, (l1<<shift)); 04943 TRACE(("%s\n", O2S(objResultPtr))); 04944 NEXT_INST_F(1, 2, 1); 04945 } 04946 04947 /* 04948 * Handle shifts within the native wide range. 04949 */ 04950 04951 TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); 04952 if ((type1 != TCL_NUMBER_BIG) 04953 && ((size_t)shift < CHAR_BIT*sizeof(Tcl_WideInt))) { 04954 Tcl_WideInt w; 04955 04956 TclGetWideIntFromObj(NULL, valuePtr, &w); 04957 if (!((w>0 ? w : ~w) 04958 & -(((Tcl_WideInt)1) 04959 << (CHAR_BIT*sizeof(Tcl_WideInt) - 1 - shift)))) { 04960 objResultPtr = Tcl_NewWideIntObj(w<<shift); 04961 TRACE(("%s\n", O2S(objResultPtr))); 04962 NEXT_INST_F(1, 2, 1); 04963 } 04964 } 04965 } else { 04966 /* 04967 * Quickly force large right shifts to 0 or -1. 04968 */ 04969 04970 TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); 04971 if ((type2 != TCL_NUMBER_LONG) 04972 || (*(const long *)ptr2 > INT_MAX)) { 04973 /* 04974 * Again, technically, the value to be shifted could be an 04975 * mp_int so huge that a right shift by (INT_MAX+1) bits could 04976 * not take us to the result of 0 or -1, but since we're using 04977 * mp_div_2d to do the work, and it takes only an int 04978 * argument, we draw the line there. 04979 */ 04980 04981 int zero; 04982 04983 switch (type1) { 04984 case TCL_NUMBER_LONG: 04985 zero = (*(const long *)ptr1 > 0L); 04986 break; 04987 #ifndef NO_WIDE_TYPE 04988 case TCL_NUMBER_WIDE: 04989 zero = (*(const Tcl_WideInt *)ptr1 > (Tcl_WideInt)0); 04990 break; 04991 #endif 04992 case TCL_NUMBER_BIG: { 04993 mp_int big1; 04994 Tcl_TakeBignumFromObj(NULL, valuePtr, &big1); 04995 zero = (mp_cmp_d(&big1, 0) == MP_GT); 04996 mp_clear(&big1); 04997 break; 04998 } 04999 default: 05000 /* Unused, here to silence compiler warning. */ 05001 zero = 0; 05002 } 05003 if (zero) { 05004 objResultPtr = constants[0]; 05005 } else { 05006 TclNewIntObj(objResultPtr, -1); 05007 } 05008 TRACE(("%s\n", O2S(objResultPtr))); 05009 NEXT_INST_F(1, 2, 1); 05010 } 05011 shift = (int)(*(const long *)ptr2); 05012 05013 /* 05014 * Handle shifts within the native long range. 05015 */ 05016 05017 if (type1 == TCL_NUMBER_LONG) { 05018 l1 = *((const long *)ptr1); 05019 if ((size_t)shift >= CHAR_BIT*sizeof(long)) { 05020 if (l1 >= (long)0) { 05021 objResultPtr = constants[0]; 05022 } else { 05023 TclNewIntObj(objResultPtr, -1); 05024 } 05025 } else { 05026 TclNewLongObj(objResultPtr, (l1 >> shift)); 05027 } 05028 TRACE(("%s\n", O2S(objResultPtr))); 05029 NEXT_INST_F(1, 2, 1); 05030 } 05031 05032 #ifndef NO_WIDE_TYPE 05033 /* 05034 * Handle shifts within the native wide range. 05035 */ 05036 05037 if (type1 == TCL_NUMBER_WIDE) { 05038 Tcl_WideInt w = *(const Tcl_WideInt *)ptr1; 05039 05040 if ((size_t)shift >= CHAR_BIT*sizeof(Tcl_WideInt)) { 05041 if (w >= (Tcl_WideInt)0) { 05042 objResultPtr = constants[0]; 05043 } else { 05044 TclNewIntObj(objResultPtr, -1); 05045 } 05046 } else { 05047 objResultPtr = Tcl_NewWideIntObj(w >> shift); 05048 } 05049 TRACE(("%s\n", O2S(objResultPtr))); 05050 NEXT_INST_F(1, 2, 1); 05051 } 05052 #endif 05053 } 05054 05055 { 05056 mp_int big, bigResult, bigRemainder; 05057 05058 Tcl_TakeBignumFromObj(NULL, valuePtr, &big); 05059 05060 mp_init(&bigResult); 05061 if (*pc == INST_LSHIFT) { 05062 mp_mul_2d(&big, shift, &bigResult); 05063 } else { 05064 mp_init(&bigRemainder); 05065 mp_div_2d(&big, shift, &bigResult, &bigRemainder); 05066 if (mp_cmp_d(&bigRemainder, 0) == MP_LT) { 05067 /* 05068 * Convert to Tcl's integer division rules. 05069 */ 05070 05071 mp_sub_d(&bigResult, 1, &bigResult); 05072 } 05073 mp_clear(&bigRemainder); 05074 } 05075 mp_clear(&big); 05076 05077 if (!Tcl_IsShared(valuePtr)) { 05078 Tcl_SetBignumObj(valuePtr, &bigResult); 05079 TRACE(("%s\n", O2S(valuePtr))); 05080 NEXT_INST_F(1, 1, 0); 05081 } 05082 objResultPtr = Tcl_NewBignumObj(&bigResult); 05083 } 05084 TRACE(("%s\n", O2S(objResultPtr))); 05085 NEXT_INST_F(1, 2, 1); 05086 } 05087 05088 case INST_BITOR: 05089 case INST_BITXOR: 05090 case INST_BITAND: { 05091 ClientData ptr1, ptr2; 05092 int type1, type2; 05093 Tcl_Obj *value2Ptr = OBJ_AT_TOS; 05094 Tcl_Obj *valuePtr = OBJ_UNDER_TOS; 05095 05096 result = GetNumberFromObj(NULL, valuePtr, &ptr1, &type1); 05097 if ((result != TCL_OK) 05098 || (type1 == TCL_NUMBER_NAN) 05099 || (type1 == TCL_NUMBER_DOUBLE)) { 05100 result = TCL_ERROR; 05101 TRACE(("%.20s %.20s => ILLEGAL 1st TYPE %s\n", O2S(valuePtr), 05102 O2S(value2Ptr), (valuePtr->typePtr? 05103 valuePtr->typePtr->name : "null"))); 05104 IllegalExprOperandType(interp, pc, valuePtr); 05105 goto checkForCatch; 05106 } 05107 result = GetNumberFromObj(NULL, value2Ptr, &ptr2, &type2); 05108 if ((result != TCL_OK) || (type2 == TCL_NUMBER_NAN) 05109 || (type2 == TCL_NUMBER_DOUBLE)) { 05110 result = TCL_ERROR; 05111 TRACE(("%.20s %.20s => ILLEGAL 2nd TYPE %s\n", O2S(valuePtr), 05112 O2S(value2Ptr), (value2Ptr->typePtr? 05113 value2Ptr->typePtr->name : "null"))); 05114 IllegalExprOperandType(interp, pc, value2Ptr); 05115 goto checkForCatch; 05116 } 05117 05118 if ((type1 == TCL_NUMBER_BIG) || (type2 == TCL_NUMBER_BIG)) { 05119 mp_int big1, big2, bigResult, *First, *Second; 05120 int numPos; 05121 05122 Tcl_TakeBignumFromObj(NULL, valuePtr, &big1); 05123 Tcl_TakeBignumFromObj(NULL, value2Ptr, &big2); 05124 05125 /* 05126 * Count how many positive arguments we have. If only one of the 05127 * arguments is negative, store it in 'Second'. 05128 */ 05129 05130 if (mp_cmp_d(&big1, 0) != MP_LT) { 05131 numPos = 1 + (mp_cmp_d(&big2, 0) != MP_LT); 05132 First = &big1; 05133 Second = &big2; 05134 } else { 05135 First = &big2; 05136 Second = &big1; 05137 numPos = (mp_cmp_d(First, 0) != MP_LT); 05138 } 05139 mp_init(&bigResult); 05140 05141 switch (*pc) { 05142 case INST_BITAND: 05143 switch (numPos) { 05144 case 2: 05145 /* 05146 * Both arguments positive, base case. 05147 */ 05148 05149 mp_and(First, Second, &bigResult); 05150 break; 05151 case 1: 05152 /* 05153 * First is positive; second negative: 05154 * P & N = P & ~~N = P&~(-N-1) = P & (P ^ (-N-1)) 05155 */ 05156 05157 mp_neg(Second, Second); 05158 mp_sub_d(Second, 1, Second); 05159 mp_xor(First, Second, &bigResult); 05160 mp_and(First, &bigResult, &bigResult); 05161 break; 05162 case 0: 05163 /* 05164 * Both arguments negative: 05165 * a & b = ~ (~a | ~b) = -(-a-1|-b-1)-1 05166 */ 05167 05168 mp_neg(First, First); 05169 mp_sub_d(First, 1, First); 05170 mp_neg(Second, Second); 05171 mp_sub_d(Second, 1, Second); 05172 mp_or(First, Second, &bigResult); 05173 mp_neg(&bigResult, &bigResult); 05174 mp_sub_d(&bigResult, 1, &bigResult); 05175 break; 05176 } 05177 break; 05178 05179 case INST_BITOR: 05180 switch (numPos) { 05181 case 2: 05182 /* 05183 * Both arguments positive, base case. 05184 */ 05185 05186 mp_or(First, Second, &bigResult); 05187 break; 05188 case 1: 05189 /* 05190 * First is positive; second negative: 05191 * N|P = ~(~N&~P) = ~((-N-1)&~P) = -((-N-1)&((-N-1)^P))-1 05192 */ 05193 05194 mp_neg(Second, Second); 05195 mp_sub_d(Second, 1, Second); 05196 mp_xor(First, Second, &bigResult); 05197 mp_and(Second, &bigResult, &bigResult); 05198 mp_neg(&bigResult, &bigResult); 05199 mp_sub_d(&bigResult, 1, &bigResult); 05200 break; 05201 case 0: 05202 /* 05203 * Both arguments negative: 05204 * a | b = ~ (~a & ~b) = -(-a-1&-b-1)-1 05205 */ 05206 05207 mp_neg(First, First); 05208 mp_sub_d(First, 1, First); 05209 mp_neg(Second, Second); 05210 mp_sub_d(Second, 1, Second); 05211 mp_and(First, Second, &bigResult); 05212 mp_neg(&bigResult, &bigResult); 05213 mp_sub_d(&bigResult, 1, &bigResult); 05214 break; 05215 } 05216 break; 05217 05218 case INST_BITXOR: 05219 switch (numPos) { 05220 case 2: 05221 /* 05222 * Both arguments positive, base case. 05223 */ 05224 05225 mp_xor(First, Second, &bigResult); 05226 break; 05227 case 1: 05228 /* 05229 * First is positive; second negative: 05230 * P^N = ~(P^~N) = -(P^(-N-1))-1 05231 */ 05232 05233 mp_neg(Second, Second); 05234 mp_sub_d(Second, 1, Second); 05235 mp_xor(First, Second, &bigResult); 05236 mp_neg(&bigResult, &bigResult); 05237 mp_sub_d(&bigResult, 1, &bigResult); 05238 break; 05239 case 0: 05240 /* 05241 * Both arguments negative: 05242 * a ^ b = (~a ^ ~b) = (-a-1^-b-1) 05243 */ 05244 05245 mp_neg(First, First); 05246 mp_sub_d(First, 1, First); 05247 mp_neg(Second, Second); 05248 mp_sub_d(Second, 1, Second); 05249 mp_xor(First, Second, &bigResult); 05250 break; 05251 } 05252 break; 05253 } 05254 05255 mp_clear(&big1); 05256 mp_clear(&big2); 05257 TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); 05258 if (Tcl_IsShared(valuePtr)) { 05259 objResultPtr = Tcl_NewBignumObj(&bigResult); 05260 TRACE(("%s\n", O2S(objResultPtr))); 05261 NEXT_INST_F(1, 2, 1); 05262 } 05263 Tcl_SetBignumObj(valuePtr, &bigResult); 05264 TRACE(("%s\n", O2S(valuePtr))); 05265 NEXT_INST_F(1, 1, 0); 05266 } 05267 05268 #ifndef NO_WIDE_TYPE 05269 if ((type1 == TCL_NUMBER_WIDE) || (type2 == TCL_NUMBER_WIDE)) { 05270 Tcl_WideInt wResult, w1, w2; 05271 05272 TclGetWideIntFromObj(NULL, valuePtr, &w1); 05273 TclGetWideIntFromObj(NULL, value2Ptr, &w2); 05274 05275 switch (*pc) { 05276 case INST_BITAND: 05277 wResult = w1 & w2; 05278 break; 05279 case INST_BITOR: 05280 wResult = w1 | w2; 05281 break; 05282 case INST_BITXOR: 05283 wResult = w1 ^ w2; 05284 break; 05285 default: 05286 /* Unused, here to silence compiler warning. */ 05287 wResult = 0; 05288 } 05289 05290 TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); 05291 if (Tcl_IsShared(valuePtr)) { 05292 objResultPtr = Tcl_NewWideIntObj(wResult); 05293 TRACE(("%s\n", O2S(objResultPtr))); 05294 NEXT_INST_F(1, 2, 1); 05295 } 05296 Tcl_SetWideIntObj(valuePtr, wResult); 05297 TRACE(("%s\n", O2S(valuePtr))); 05298 NEXT_INST_F(1, 1, 0); 05299 } 05300 #endif 05301 { 05302 long lResult, l1 = *((const long *)ptr1); 05303 long l2 = *((const long *)ptr2); 05304 05305 switch (*pc) { 05306 case INST_BITAND: 05307 lResult = l1 & l2; 05308 break; 05309 case INST_BITOR: 05310 lResult = l1 | l2; 05311 break; 05312 case INST_BITXOR: 05313 lResult = l1 ^ l2; 05314 break; 05315 default: 05316 /* Unused, here to silence compiler warning. */ 05317 lResult = 0; 05318 } 05319 05320 TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); 05321 if (Tcl_IsShared(valuePtr)) { 05322 TclNewLongObj(objResultPtr, lResult); 05323 TRACE(("%s\n", O2S(objResultPtr))); 05324 NEXT_INST_F(1, 2, 1); 05325 } 05326 TclSetLongObj(valuePtr, lResult); 05327 TRACE(("%s\n", O2S(valuePtr))); 05328 NEXT_INST_F(1, 1, 0); 05329 } 05330 } 05331 05332 case INST_EXPON: 05333 case INST_ADD: 05334 case INST_SUB: 05335 case INST_DIV: 05336 case INST_MULT: { 05337 ClientData ptr1, ptr2; 05338 int type1, type2; 05339 Tcl_Obj *value2Ptr = OBJ_AT_TOS; 05340 Tcl_Obj *valuePtr = OBJ_UNDER_TOS; 05341 05342 result = GetNumberFromObj(NULL, valuePtr, &ptr1, &type1); 05343 if ((result != TCL_OK) 05344 #ifndef ACCEPT_NAN 05345 || (type1 == TCL_NUMBER_NAN) 05346 #endif 05347 ) { 05348 result = TCL_ERROR; 05349 TRACE(("%.20s %.20s => ILLEGAL 1st TYPE %s\n", 05350 O2S(value2Ptr), O2S(valuePtr), 05351 (valuePtr->typePtr? valuePtr->typePtr->name: "null"))); 05352 IllegalExprOperandType(interp, pc, valuePtr); 05353 goto checkForCatch; 05354 } 05355 05356 #ifdef ACCEPT_NAN 05357 if (type1 == TCL_NUMBER_NAN) { 05358 /* 05359 * NaN first argument -> result is also NaN. 05360 */ 05361 05362 NEXT_INST_F(1, 1, 0); 05363 } 05364 #endif 05365 05366 result = GetNumberFromObj(NULL, value2Ptr, &ptr2, &type2); 05367 if ((result != TCL_OK) 05368 #ifndef ACCEPT_NAN 05369 || (type2 == TCL_NUMBER_NAN) 05370 #endif 05371 ) { 05372 result = TCL_ERROR; 05373 TRACE(("%.20s %.20s => ILLEGAL 2nd TYPE %s\n", 05374 O2S(value2Ptr), O2S(valuePtr), 05375 (value2Ptr->typePtr? value2Ptr->typePtr->name: "null"))); 05376 IllegalExprOperandType(interp, pc, value2Ptr); 05377 goto checkForCatch; 05378 } 05379 05380 #ifdef ACCEPT_NAN 05381 if (type2 == TCL_NUMBER_NAN) { 05382 /* 05383 * NaN second argument -> result is also NaN. 05384 */ 05385 05386 objResultPtr = value2Ptr; 05387 NEXT_INST_F(1, 2, 1); 05388 } 05389 #endif 05390 05391 if ((type1 == TCL_NUMBER_DOUBLE) || (type2 == TCL_NUMBER_DOUBLE)) { 05392 /* 05393 * At least one of the values is floating-point, so perform 05394 * floating point calculations. 05395 */ 05396 05397 double d1, d2, dResult; 05398 05399 Tcl_GetDoubleFromObj(NULL, valuePtr, &d1); 05400 Tcl_GetDoubleFromObj(NULL, value2Ptr, &d2); 05401 05402 switch (*pc) { 05403 case INST_ADD: 05404 dResult = d1 + d2; 05405 break; 05406 case INST_SUB: 05407 dResult = d1 - d2; 05408 break; 05409 case INST_MULT: 05410 dResult = d1 * d2; 05411 break; 05412 case INST_DIV: 05413 #ifndef IEEE_FLOATING_POINT 05414 if (d2 == 0.0) { 05415 TRACE(("%.6g %.6g => DIVIDE BY ZERO\n", d1, d2)); 05416 goto divideByZero; 05417 } 05418 #endif 05419 /* 05420 * We presume that we are running with zero-divide unmasked if 05421 * we're on an IEEE box. Otherwise, this statement might cause 05422 * demons to fly out our noses. 05423 */ 05424 05425 dResult = d1 / d2; 05426 break; 05427 case INST_EXPON: 05428 if (d1==0.0 && d2<0.0) { 05429 TRACE(("%.6g %.6g => EXPONENT OF ZERO\n", d1, d2)); 05430 goto exponOfZero; 05431 } 05432 dResult = pow(d1, d2); 05433 break; 05434 default: 05435 /* Unused, here to silence compiler warning. */ 05436 dResult = 0; 05437 } 05438 05439 #ifndef ACCEPT_NAN 05440 /* 05441 * Check now for IEEE floating-point error. 05442 */ 05443 05444 if (TclIsNaN(dResult)) { 05445 TRACE(("%.20s %.20s => IEEE FLOATING PT ERROR\n", 05446 O2S(valuePtr), O2S(value2Ptr))); 05447 TclExprFloatError(interp, dResult); 05448 result = TCL_ERROR; 05449 goto checkForCatch; 05450 } 05451 #endif 05452 TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); 05453 if (Tcl_IsShared(valuePtr)) { 05454 TclNewDoubleObj(objResultPtr, dResult); 05455 TRACE(("%s\n", O2S(objResultPtr))); 05456 NEXT_INST_F(1, 2, 1); 05457 } 05458 TclSetDoubleObj(valuePtr, dResult); 05459 TRACE(("%s\n", O2S(valuePtr))); 05460 NEXT_INST_F(1, 1, 0); 05461 } 05462 05463 if ((sizeof(long) >= 2*sizeof(int)) && (*pc == INST_MULT) 05464 && (type1 == TCL_NUMBER_LONG) && (type2 == TCL_NUMBER_LONG)) { 05465 long l1 = *((const long *)ptr1); 05466 long l2 = *((const long *)ptr2); 05467 05468 if ((l1 <= INT_MAX) && (l1 >= INT_MIN) 05469 && (l2 <= INT_MAX) && (l2 >= INT_MIN)) { 05470 long lResult = l1 * l2; 05471 05472 TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); 05473 if (Tcl_IsShared(valuePtr)) { 05474 TclNewLongObj(objResultPtr,lResult); 05475 TRACE(("%s\n", O2S(objResultPtr))); 05476 NEXT_INST_F(1, 2, 1); 05477 } 05478 TclSetLongObj(valuePtr, lResult); 05479 TRACE(("%s\n", O2S(valuePtr))); 05480 NEXT_INST_F(1, 1, 0); 05481 } 05482 } 05483 05484 if ((sizeof(Tcl_WideInt) >= 2*sizeof(long)) && (*pc == INST_MULT) 05485 && (type1 == TCL_NUMBER_LONG) && (type2 == TCL_NUMBER_LONG)) { 05486 Tcl_WideInt w1, w2, wResult; 05487 TclGetWideIntFromObj(NULL, valuePtr, &w1); 05488 TclGetWideIntFromObj(NULL, value2Ptr, &w2); 05489 05490 wResult = w1 * w2; 05491 05492 TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); 05493 if (Tcl_IsShared(valuePtr)) { 05494 objResultPtr = Tcl_NewWideIntObj(wResult); 05495 TRACE(("%s\n", O2S(objResultPtr))); 05496 NEXT_INST_F(1, 2, 1); 05497 } 05498 Tcl_SetWideIntObj(valuePtr, wResult); 05499 TRACE(("%s\n", O2S(valuePtr))); 05500 NEXT_INST_F(1, 1, 0); 05501 } 05502 05503 /* TODO: Attempts to re-use unshared operands on stack. */ 05504 if (*pc == INST_EXPON) { 05505 long l1 = 0, l2 = 0; 05506 Tcl_WideInt w1; 05507 int oddExponent = 0, negativeExponent = 0; 05508 05509 if (type2 == TCL_NUMBER_LONG) { 05510 l2 = *((const long *) ptr2); 05511 if (l2 == 0) { 05512 /* 05513 * Anything to the zero power is 1. 05514 */ 05515 05516 objResultPtr = constants[1]; 05517 NEXT_INST_F(1, 2, 1); 05518 } else if (l2 == 1) { 05519 /* 05520 * Anything to the first power is itself 05521 */ 05522 NEXT_INST_F(1, 1, 0); 05523 } 05524 } 05525 05526 switch (type2) { 05527 case TCL_NUMBER_LONG: { 05528 negativeExponent = (l2 < 0); 05529 oddExponent = (int) (l2 & 1); 05530 break; 05531 } 05532 #ifndef NO_WIDE_TYPE 05533 case TCL_NUMBER_WIDE: { 05534 Tcl_WideInt w2 = *((const Tcl_WideInt *)ptr2); 05535 05536 negativeExponent = (w2 < 0); 05537 oddExponent = (int) (w2 & (Tcl_WideInt)1); 05538 break; 05539 } 05540 #endif 05541 case TCL_NUMBER_BIG: { 05542 mp_int big2; 05543 05544 Tcl_TakeBignumFromObj(NULL, value2Ptr, &big2); 05545 negativeExponent = (mp_cmp_d(&big2, 0) == MP_LT); 05546 mp_mod_2d(&big2, 1, &big2); 05547 oddExponent = !mp_iszero(&big2); 05548 mp_clear(&big2); 05549 break; 05550 } 05551 } 05552 05553 if (negativeExponent) { 05554 if (type1 == TCL_NUMBER_LONG) { 05555 l1 = *((const long *)ptr1); 05556 switch (l1) { 05557 case 0: 05558 /* 05559 * Zero to a negative power is div by zero error. 05560 */ 05561 05562 TRACE(("%s %s => EXPONENT OF ZERO\n", O2S(valuePtr), 05563 O2S(value2Ptr))); 05564 goto exponOfZero; 05565 case -1: 05566 if (oddExponent) { 05567 TclNewIntObj(objResultPtr, -1); 05568 } else { 05569 objResultPtr = constants[1]; 05570 } 05571 NEXT_INST_F(1, 2, 1); 05572 case 1: 05573 /* 05574 * 1 to any power is 1. 05575 */ 05576 05577 objResultPtr = constants[1]; 05578 NEXT_INST_F(1, 2, 1); 05579 } 05580 } 05581 05582 /* 05583 * Integers with magnitude greater than 1 raise to a negative 05584 * power yield the answer zero (see TIP 123). 05585 */ 05586 05587 objResultPtr = constants[0]; 05588 NEXT_INST_F(1, 2, 1); 05589 } 05590 05591 if (type1 == TCL_NUMBER_LONG) { 05592 l1 = *((const long *)ptr1); 05593 switch (l1) { 05594 case 0: 05595 /* 05596 * Zero to a positive power is zero. 05597 */ 05598 05599 objResultPtr = constants[0]; 05600 NEXT_INST_F(1, 2, 1); 05601 case 1: 05602 /* 05603 * 1 to any power is 1. 05604 */ 05605 05606 objResultPtr = constants[1]; 05607 NEXT_INST_F(1, 2, 1); 05608 case -1: 05609 if (oddExponent) { 05610 TclNewIntObj(objResultPtr, -1); 05611 } else { 05612 objResultPtr = constants[1]; 05613 } 05614 NEXT_INST_F(1, 2, 1); 05615 } 05616 } 05617 if (type2 == TCL_NUMBER_BIG) { 05618 Tcl_SetObjResult(interp, 05619 Tcl_NewStringObj("exponent too large", -1)); 05620 result = TCL_ERROR; 05621 goto checkForCatch; 05622 } 05623 05624 if (type1 == TCL_NUMBER_LONG && type2 == TCL_NUMBER_LONG) { 05625 if (l1 == 2) { 05626 /* 05627 * Reduce small powers of 2 to shifts. 05628 */ 05629 05630 if ((unsigned long) l2 < CHAR_BIT * sizeof(long) - 1) { 05631 TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); 05632 TclNewLongObj(objResultPtr, (1L << l2)); 05633 TRACE(("%s\n", O2S(objResultPtr))); 05634 NEXT_INST_F(1, 2, 1); 05635 } 05636 #if !defined(TCL_WIDE_INT_IS_LONG) 05637 if ((unsigned long)l2 < CHAR_BIT*sizeof(Tcl_WideInt) - 1){ 05638 TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); 05639 objResultPtr = 05640 Tcl_NewWideIntObj(((Tcl_WideInt) 1) << l2); 05641 TRACE(("%s\n", O2S(objResultPtr))); 05642 NEXT_INST_F(1, 2, 1); 05643 } 05644 #endif 05645 } 05646 if (l1 == -2) { 05647 int signum = oddExponent ? -1 : 1; 05648 05649 /* 05650 * Reduce small powers of 2 to shifts. 05651 */ 05652 05653 if ((unsigned long) l2 < CHAR_BIT * sizeof(long) - 1) { 05654 TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); 05655 TclNewLongObj(objResultPtr, signum * (1L << l2)); 05656 TRACE(("%s\n", O2S(objResultPtr))); 05657 NEXT_INST_F(1, 2, 1); 05658 } 05659 #if !defined(TCL_WIDE_INT_IS_LONG) 05660 if ((unsigned long)l2 < CHAR_BIT*sizeof(Tcl_WideInt) - 1){ 05661 TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); 05662 objResultPtr = Tcl_NewWideIntObj( 05663 signum * (((Tcl_WideInt) 1) << l2)); 05664 TRACE(("%s\n", O2S(objResultPtr))); 05665 NEXT_INST_F(1, 2, 1); 05666 } 05667 #endif 05668 } 05669 #if (LONG_MAX == 0x7fffffff) 05670 if (l2 <= 8 && 05671 l1 <= MaxBase32[l2-2] && l1 >= -MaxBase32[l2-2]) { 05672 /* 05673 * Small powers of 32-bit integers. 05674 */ 05675 05676 long lResult = l1 * l1; /* b**2 */ 05677 switch (l2) { 05678 case 2: 05679 break; 05680 case 3: 05681 lResult *= l1; /* b**3 */ 05682 break; 05683 case 4: 05684 lResult *= lResult; /* b**4 */ 05685 break; 05686 case 5: 05687 lResult *= lResult; /* b**4 */ 05688 lResult *= l1; /* b**5 */ 05689 break; 05690 case 6: 05691 lResult *= l1; /* b**3 */ 05692 lResult *= lResult; /* b**6 */ 05693 break; 05694 case 7: 05695 lResult *= l1; /* b**3 */ 05696 lResult *= lResult; /* b**6 */ 05697 lResult *= l1; /* b**7 */ 05698 break; 05699 case 8: 05700 lResult *= lResult; /* b**4 */ 05701 lResult *= lResult; /* b**8 */ 05702 break; 05703 } 05704 TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); 05705 if (Tcl_IsShared(valuePtr)) { 05706 TclNewLongObj(objResultPtr, lResult); 05707 TRACE(("%s\n", O2S(objResultPtr))); 05708 NEXT_INST_F(1, 2, 1); 05709 } 05710 Tcl_SetLongObj(valuePtr, lResult); 05711 TRACE(("%s\n", O2S(valuePtr))); 05712 NEXT_INST_F(1, 1, 0); 05713 } 05714 if (l1 >= 3 && 05715 ((unsigned long) l1 < (sizeof(Exp32Index) 05716 / sizeof(unsigned short)) - 1)) { 05717 unsigned short base = Exp32Index[l1-3] 05718 + (unsigned short) l2 - 9; 05719 if (base < Exp32Index[l1-2]) { 05720 /* 05721 * 32-bit number raised to intermediate power, done by 05722 * table lookup. 05723 */ 05724 05725 TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); 05726 if (Tcl_IsShared(valuePtr)) { 05727 TclNewLongObj(objResultPtr, Exp32Value[base]); 05728 TRACE(("%s\n", O2S(objResultPtr))); 05729 NEXT_INST_F(1, 2, 1); 05730 } 05731 Tcl_SetLongObj(valuePtr, Exp32Value[base]); 05732 TRACE(("%s\n", O2S(valuePtr))); 05733 NEXT_INST_F(1, 1, 0); 05734 } 05735 } 05736 if (-l1 >= 3 05737 && (unsigned long)(-l1) < (sizeof(Exp32Index) 05738 / sizeof(unsigned short)) - 1) { 05739 unsigned short base 05740 = Exp32Index[-l1-3] + (unsigned short) l2 - 9; 05741 if (base < Exp32Index[-l1-2]) { 05742 long lResult = (oddExponent) ? 05743 -Exp32Value[base] : Exp32Value[base]; 05744 05745 /* 05746 * 32-bit number raised to intermediate power, done by 05747 * table lookup. 05748 */ 05749 05750 TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); 05751 if (Tcl_IsShared(valuePtr)) { 05752 TclNewLongObj(objResultPtr, lResult); 05753 TRACE(("%s\n", O2S(objResultPtr))); 05754 NEXT_INST_F(1, 2, 1); 05755 } 05756 Tcl_SetLongObj(valuePtr, lResult); 05757 TRACE(("%s\n", O2S(valuePtr))); 05758 NEXT_INST_F(1, 1, 0); 05759 } 05760 } 05761 #endif 05762 } 05763 if (type1 == TCL_NUMBER_LONG) { 05764 w1 = l1; 05765 #ifndef NO_WIDE_TYPE 05766 } else if (type1 == TCL_NUMBER_WIDE) { 05767 w1 = *((const Tcl_WideInt*) ptr1); 05768 #endif 05769 } else { 05770 w1 = 0; 05771 } 05772 #if (LONG_MAX > 0x7fffffff) || !defined(TCL_WIDE_INT_IS_LONG) 05773 if (w1 != 0 && type2 == TCL_NUMBER_LONG && l2 <= 16 05774 && w1 <= MaxBaseWide[l2-2] && w1 >= -MaxBaseWide[l2-2]) { 05775 /* 05776 * Small powers of integers whose result is wide. 05777 */ 05778 05779 Tcl_WideInt wResult = w1 * w1; /* b**2 */ 05780 05781 switch (l2) { 05782 case 2: 05783 break; 05784 case 3: 05785 wResult *= l1; /* b**3 */ 05786 break; 05787 case 4: 05788 wResult *= wResult; /* b**4 */ 05789 break; 05790 case 5: 05791 wResult *= wResult; /* b**4 */ 05792 wResult *= w1; /* b**5 */ 05793 break; 05794 case 6: 05795 wResult *= w1; /* b**3 */ 05796 wResult *= wResult; /* b**6 */ 05797 break; 05798 case 7: 05799 wResult *= w1; /* b**3 */ 05800 wResult *= wResult; /* b**6 */ 05801 wResult *= w1; /* b**7 */ 05802 break; 05803 case 8: 05804 wResult *= wResult; /* b**4 */ 05805 wResult *= wResult; /* b**8 */ 05806 break; 05807 case 9: 05808 wResult *= wResult; /* b**4 */ 05809 wResult *= wResult; /* b**8 */ 05810 wResult *= w1; /* b**9 */ 05811 break; 05812 case 10: 05813 wResult *= wResult; /* b**4 */ 05814 wResult *= w1; /* b**5 */ 05815 wResult *= wResult; /* b**10 */ 05816 break; 05817 case 11: 05818 wResult *= wResult; /* b**4 */ 05819 wResult *= w1; /* b**5 */ 05820 wResult *= wResult; /* b**10 */ 05821 wResult *= w1; /* b**11 */ 05822 break; 05823 case 12: 05824 wResult *= w1; /* b**3 */ 05825 wResult *= wResult; /* b**6 */ 05826 wResult *= wResult; /* b**12 */ 05827 break; 05828 case 13: 05829 wResult *= w1; /* b**3 */ 05830 wResult *= wResult; /* b**6 */ 05831 wResult *= wResult; /* b**12 */ 05832 wResult *= w1; /* b**13 */ 05833 break; 05834 case 14: 05835 wResult *= w1; /* b**3 */ 05836 wResult *= wResult; /* b**6 */ 05837 wResult *= w1; /* b**7 */ 05838 wResult *= wResult; /* b**14 */ 05839 break; 05840 case 15: 05841 wResult *= w1; /* b**3 */ 05842 wResult *= wResult; /* b**6 */ 05843 wResult *= w1; /* b**7 */ 05844 wResult *= wResult; /* b**14 */ 05845 wResult *= w1; /* b**15 */ 05846 break; 05847 case 16: 05848 wResult *= wResult; /* b**4 */ 05849 wResult *= wResult; /* b**8 */ 05850 wResult *= wResult; /* b**16 */ 05851 break; 05852 05853 } 05854 TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); 05855 objResultPtr = Tcl_NewWideIntObj(wResult); 05856 TRACE(("%s\n", O2S(objResultPtr))); 05857 NEXT_INST_F(1, 2, 1); 05858 } 05859 05860 /* 05861 * Handle cases of powers > 16 that still fit in a 64-bit word by 05862 * doing table lookup. 05863 */ 05864 05865 if (w1 >= 3 && 05866 (Tcl_WideUInt) w1 < (sizeof(Exp64Index) 05867 / sizeof(unsigned short)) - 1) { 05868 unsigned short base = 05869 Exp64Index[w1-3] + (unsigned short) l2 - 17; 05870 05871 if (base < Exp64Index[w1-2]) { 05872 /* 05873 * 64-bit number raised to intermediate power, done by 05874 * table lookup. 05875 */ 05876 05877 TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); 05878 if (Tcl_IsShared(valuePtr)) { 05879 objResultPtr = Tcl_NewWideIntObj(Exp64Value[base]); 05880 TRACE(("%s\n", O2S(objResultPtr))); 05881 NEXT_INST_F(1, 2, 1); 05882 } 05883 Tcl_SetWideIntObj(valuePtr, Exp64Value[base]); 05884 TRACE(("%s\n", O2S(valuePtr))); 05885 NEXT_INST_F(1, 1, 0); 05886 } 05887 } 05888 if (-w1 >= 3 && 05889 (Tcl_WideUInt) (-w1) < (sizeof(Exp64Index) 05890 / sizeof(unsigned short)) - 1) { 05891 unsigned short base = 05892 Exp64Index[-w1-3] + (unsigned short) l2 - 17; 05893 05894 if (base < Exp64Index[-w1-2]) { 05895 Tcl_WideInt wResult = (oddExponent) ? 05896 -Exp64Value[base] : Exp64Value[base]; 05897 /* 05898 * 64-bit number raised to intermediate power, done by 05899 * table lookup. 05900 */ 05901 05902 TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); 05903 if (Tcl_IsShared(valuePtr)) { 05904 objResultPtr = Tcl_NewWideIntObj(wResult); 05905 TRACE(("%s\n", O2S(objResultPtr))); 05906 NEXT_INST_F(1, 2, 1); 05907 } 05908 Tcl_SetWideIntObj(valuePtr, wResult); 05909 TRACE(("%s\n", O2S(valuePtr))); 05910 NEXT_INST_F(1, 1, 0); 05911 } 05912 } 05913 #endif 05914 05915 goto overflow; 05916 } 05917 05918 if ((*pc != INST_MULT) 05919 && (type1 != TCL_NUMBER_BIG) && (type2 != TCL_NUMBER_BIG)) { 05920 Tcl_WideInt w1, w2, wResult; 05921 05922 TclGetWideIntFromObj(NULL, valuePtr, &w1); 05923 TclGetWideIntFromObj(NULL, value2Ptr, &w2); 05924 05925 switch (*pc) { 05926 case INST_ADD: 05927 wResult = w1 + w2; 05928 #ifndef NO_WIDE_TYPE 05929 if ((type1 == TCL_NUMBER_WIDE) || (type2 == TCL_NUMBER_WIDE)) 05930 #endif 05931 { 05932 /* 05933 * Check for overflow. 05934 */ 05935 05936 if (Overflowing(w1, w2, wResult)) { 05937 goto overflow; 05938 } 05939 } 05940 break; 05941 05942 case INST_SUB: 05943 wResult = w1 - w2; 05944 #ifndef NO_WIDE_TYPE 05945 if ((type1 == TCL_NUMBER_WIDE) || (type2 == TCL_NUMBER_WIDE)) 05946 #endif 05947 { 05948 /* 05949 * Must check for overflow. The macro tests for overflows 05950 * in sums by looking at the sign bits. As we have a 05951 * subtraction here, we are adding -w2. As -w2 could in 05952 * turn overflow, we test with ~w2 instead: it has the 05953 * opposite sign bit to w2 so it does the job. Note that 05954 * the only "bad" case (w2==0) is irrelevant for this 05955 * macro, as in that case w1 and wResult have the same 05956 * sign and there is no overflow anyway. 05957 */ 05958 05959 if (Overflowing(w1, ~w2, wResult)) { 05960 goto overflow; 05961 } 05962 } 05963 break; 05964 05965 case INST_DIV: 05966 if (w2 == 0) { 05967 TRACE(("%s %s => DIVIDE BY ZERO\n", 05968 O2S(valuePtr), O2S(value2Ptr))); 05969 goto divideByZero; 05970 } 05971 05972 /* 05973 * Need a bignum to represent (LLONG_MIN / -1) 05974 */ 05975 05976 if ((w1 == LLONG_MIN) && (w2 == -1)) { 05977 goto overflow; 05978 } 05979 wResult = w1 / w2; 05980 05981 /* 05982 * Force Tcl's integer division rules. 05983 * TODO: examine for logic simplification 05984 */ 05985 05986 if (((wResult < 0) || ((wResult == 0) && 05987 ((w1 < 0 && w2 > 0) || (w1 > 0 && w2 < 0)))) && 05988 ((wResult * w2) != w1)) { 05989 wResult -= 1; 05990 } 05991 break; 05992 default: 05993 /* 05994 * Unused, here to silence compiler warning. 05995 */ 05996 05997 wResult = 0; 05998 } 05999 06000 TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); 06001 if (Tcl_IsShared(valuePtr)) { 06002 objResultPtr = Tcl_NewWideIntObj(wResult); 06003 TRACE(("%s\n", O2S(objResultPtr))); 06004 NEXT_INST_F(1, 2, 1); 06005 } 06006 Tcl_SetWideIntObj(valuePtr, wResult); 06007 TRACE(("%s\n", O2S(valuePtr))); 06008 NEXT_INST_F(1, 1, 0); 06009 } 06010 06011 overflow: 06012 { 06013 mp_int big1, big2, bigResult, bigRemainder; 06014 06015 TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); 06016 Tcl_TakeBignumFromObj(NULL, valuePtr, &big1); 06017 Tcl_TakeBignumFromObj(NULL, value2Ptr, &big2); 06018 mp_init(&bigResult); 06019 switch (*pc) { 06020 case INST_ADD: 06021 mp_add(&big1, &big2, &bigResult); 06022 break; 06023 case INST_SUB: 06024 mp_sub(&big1, &big2, &bigResult); 06025 break; 06026 case INST_MULT: 06027 mp_mul(&big1, &big2, &bigResult); 06028 break; 06029 case INST_DIV: 06030 if (mp_iszero(&big2)) { 06031 TRACE(("%s %s => DIVIDE BY ZERO\n", O2S(valuePtr), 06032 O2S(value2Ptr))); 06033 mp_clear(&big1); 06034 mp_clear(&big2); 06035 mp_clear(&bigResult); 06036 goto divideByZero; 06037 } 06038 mp_init(&bigRemainder); 06039 mp_div(&big1, &big2, &bigResult, &bigRemainder); 06040 /* TODO: internals intrusion */ 06041 if (!mp_iszero(&bigRemainder) 06042 && (bigRemainder.sign != big2.sign)) { 06043 /* 06044 * Convert to Tcl's integer division rules. 06045 */ 06046 06047 mp_sub_d(&bigResult, 1, &bigResult); 06048 mp_add(&bigRemainder, &big2, &bigRemainder); 06049 } 06050 mp_clear(&bigRemainder); 06051 break; 06052 case INST_EXPON: 06053 if (big2.used > 1) { 06054 Tcl_SetObjResult(interp, 06055 Tcl_NewStringObj("exponent too large", -1)); 06056 mp_clear(&big1); 06057 mp_clear(&big2); 06058 mp_clear(&bigResult); 06059 result = TCL_ERROR; 06060 goto checkForCatch; 06061 } 06062 mp_expt_d(&big1, big2.dp[0], &bigResult); 06063 break; 06064 } 06065 mp_clear(&big1); 06066 mp_clear(&big2); 06067 if (Tcl_IsShared(valuePtr)) { 06068 objResultPtr = Tcl_NewBignumObj(&bigResult); 06069 TRACE(("%s\n", O2S(objResultPtr))); 06070 NEXT_INST_F(1, 2, 1); 06071 } 06072 Tcl_SetBignumObj(valuePtr, &bigResult); 06073 TRACE(("%s\n", O2S(valuePtr))); 06074 NEXT_INST_F(1, 1, 0); 06075 } 06076 } 06077 06078 case INST_LNOT: { 06079 int b; 06080 Tcl_Obj *valuePtr = OBJ_AT_TOS; 06081 06082 /* TODO - check claim that taking address of b harms performance */ 06083 /* TODO - consider optimization search for constants */ 06084 result = TclGetBooleanFromObj(NULL, valuePtr, &b); 06085 if (result != TCL_OK) { 06086 TRACE(("\"%.20s\" => ILLEGAL TYPE %s\n", O2S(valuePtr), 06087 (valuePtr->typePtr? valuePtr->typePtr->name : "null"))); 06088 IllegalExprOperandType(interp, pc, valuePtr); 06089 goto checkForCatch; 06090 } 06091 /* TODO: Consider peephole opt. */ 06092 objResultPtr = constants[!b]; 06093 NEXT_INST_F(1, 1, 1); 06094 } 06095 06096 case INST_BITNOT: { 06097 mp_int big; 06098 ClientData ptr; 06099 int type; 06100 Tcl_Obj *valuePtr = OBJ_AT_TOS; 06101 06102 result = GetNumberFromObj(NULL, valuePtr, &ptr, &type); 06103 if ((result != TCL_OK) 06104 || (type == TCL_NUMBER_NAN) || (type == TCL_NUMBER_DOUBLE)) { 06105 /* 06106 * ... ~$NonInteger => raise an error. 06107 */ 06108 06109 result = TCL_ERROR; 06110 TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n", O2S(valuePtr), 06111 (valuePtr->typePtr? valuePtr->typePtr->name : "null"))); 06112 IllegalExprOperandType(interp, pc, valuePtr); 06113 goto checkForCatch; 06114 } 06115 if (type == TCL_NUMBER_LONG) { 06116 long l = *((const long *)ptr); 06117 06118 if (Tcl_IsShared(valuePtr)) { 06119 TclNewLongObj(objResultPtr, ~l); 06120 NEXT_INST_F(1, 1, 1); 06121 } 06122 TclSetLongObj(valuePtr, ~l); 06123 NEXT_INST_F(1, 0, 0); 06124 } 06125 #ifndef NO_WIDE_TYPE 06126 if (type == TCL_NUMBER_WIDE) { 06127 Tcl_WideInt w = *((const Tcl_WideInt *)ptr); 06128 06129 if (Tcl_IsShared(valuePtr)) { 06130 objResultPtr = Tcl_NewWideIntObj(~w); 06131 NEXT_INST_F(1, 1, 1); 06132 } 06133 Tcl_SetWideIntObj(valuePtr, ~w); 06134 NEXT_INST_F(1, 0, 0); 06135 } 06136 #endif 06137 Tcl_TakeBignumFromObj(NULL, valuePtr, &big); 06138 /* ~a = - a - 1 */ 06139 mp_neg(&big, &big); 06140 mp_sub_d(&big, 1, &big); 06141 if (Tcl_IsShared(valuePtr)) { 06142 objResultPtr = Tcl_NewBignumObj(&big); 06143 NEXT_INST_F(1, 1, 1); 06144 } 06145 Tcl_SetBignumObj(valuePtr, &big); 06146 NEXT_INST_F(1, 0, 0); 06147 } 06148 06149 case INST_UMINUS: { 06150 ClientData ptr; 06151 int type; 06152 Tcl_Obj *valuePtr = OBJ_AT_TOS; 06153 06154 result = GetNumberFromObj(NULL, valuePtr, &ptr, &type); 06155 if ((result != TCL_OK) 06156 #ifndef ACCEPT_NAN 06157 || (type == TCL_NUMBER_NAN) 06158 #endif 06159 ) { 06160 result = TCL_ERROR; 06161 TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n", O2S(valuePtr), 06162 (valuePtr->typePtr? valuePtr->typePtr->name : "null"))); 06163 IllegalExprOperandType(interp, pc, valuePtr); 06164 goto checkForCatch; 06165 } 06166 switch (type) { 06167 case TCL_NUMBER_DOUBLE: { 06168 double d; 06169 06170 if (Tcl_IsShared(valuePtr)) { 06171 TclNewDoubleObj(objResultPtr, -(*((const double *)ptr))); 06172 NEXT_INST_F(1, 1, 1); 06173 } 06174 d = *((const double *)ptr); 06175 TclSetDoubleObj(valuePtr, -d); 06176 NEXT_INST_F(1, 0, 0); 06177 } 06178 case TCL_NUMBER_LONG: { 06179 long l = *((const long *)ptr); 06180 06181 if (l != LONG_MIN) { 06182 if (Tcl_IsShared(valuePtr)) { 06183 TclNewLongObj(objResultPtr, -l); 06184 NEXT_INST_F(1, 1, 1); 06185 } 06186 TclSetLongObj(valuePtr, -l); 06187 NEXT_INST_F(1, 0, 0); 06188 } 06189 /* FALLTHROUGH */ 06190 } 06191 #ifndef NO_WIDE_TYPE 06192 case TCL_NUMBER_WIDE: { 06193 Tcl_WideInt w; 06194 06195 if (type == TCL_NUMBER_LONG) { 06196 w = (Tcl_WideInt)(*((const long *)ptr)); 06197 } else { 06198 w = *((const Tcl_WideInt *)ptr); 06199 } 06200 if (w != LLONG_MIN) { 06201 if (Tcl_IsShared(valuePtr)) { 06202 objResultPtr = Tcl_NewWideIntObj(-w); 06203 NEXT_INST_F(1, 1, 1); 06204 } 06205 Tcl_SetWideIntObj(valuePtr, -w); 06206 NEXT_INST_F(1, 0, 0); 06207 } 06208 /* FALLTHROUGH */ 06209 } 06210 #endif 06211 case TCL_NUMBER_BIG: { 06212 mp_int big; 06213 06214 switch (type) { 06215 #ifdef NO_WIDE_TYPE 06216 case TCL_NUMBER_LONG: 06217 TclBNInitBignumFromLong(&big, *(const long *) ptr); 06218 break; 06219 #else 06220 case TCL_NUMBER_WIDE: 06221 TclBNInitBignumFromWideInt(&big, *(const Tcl_WideInt *) ptr); 06222 break; 06223 #endif 06224 case TCL_NUMBER_BIG: 06225 Tcl_TakeBignumFromObj(NULL, valuePtr, &big); 06226 } 06227 mp_neg(&big, &big); 06228 if (Tcl_IsShared(valuePtr)) { 06229 objResultPtr = Tcl_NewBignumObj(&big); 06230 NEXT_INST_F(1, 1, 1); 06231 } 06232 Tcl_SetBignumObj(valuePtr, &big); 06233 NEXT_INST_F(1, 0, 0); 06234 } 06235 case TCL_NUMBER_NAN: 06236 /* -NaN => NaN */ 06237 NEXT_INST_F(1, 0, 0); 06238 } 06239 } 06240 06241 case INST_UPLUS: 06242 case INST_TRY_CVT_TO_NUMERIC: { 06243 /* 06244 * Try to convert the topmost stack object to numeric object. This is 06245 * done in order to support [expr]'s policy of interpreting operands 06246 * if at all possible as numbers first, then strings. 06247 */ 06248 06249 ClientData ptr; 06250 int type; 06251 Tcl_Obj *valuePtr = OBJ_AT_TOS; 06252 06253 if (GetNumberFromObj(NULL, valuePtr, &ptr, &type) != TCL_OK) { 06254 if (*pc == INST_UPLUS) { 06255 /* 06256 * ... +$NonNumeric => raise an error. 06257 */ 06258 06259 result = TCL_ERROR; 06260 TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n", O2S(valuePtr), 06261 (valuePtr->typePtr? valuePtr->typePtr->name:"null"))); 06262 IllegalExprOperandType(interp, pc, valuePtr); 06263 goto checkForCatch; 06264 } else { 06265 /* ... TryConvertToNumeric($NonNumeric) is acceptable */ 06266 TRACE(("\"%.20s\" => not numeric\n", O2S(valuePtr))); 06267 NEXT_INST_F(1, 0, 0); 06268 } 06269 } 06270 #ifndef ACCEPT_NAN 06271 if (type == TCL_NUMBER_NAN) { 06272 result = TCL_ERROR; 06273 if (*pc == INST_UPLUS) { 06274 /* 06275 * ... +$NonNumeric => raise an error. 06276 */ 06277 06278 TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n", O2S(valuePtr), 06279 (valuePtr->typePtr? valuePtr->typePtr->name:"null"))); 06280 IllegalExprOperandType(interp, pc, valuePtr); 06281 } else { 06282 /* 06283 * Numeric conversion of NaN -> error. 06284 */ 06285 06286 TRACE(("\"%.20s\" => IEEE FLOATING PT ERROR\n", 06287 O2S(objResultPtr))); 06288 TclExprFloatError(interp, *((const double *)ptr)); 06289 } 06290 goto checkForCatch; 06291 } 06292 #endif 06293 06294 /* 06295 * Ensure that the numeric value has a string rep the same as the 06296 * formatted version of its internal rep. This is used, e.g., to make 06297 * sure that "expr {0001}" yields "1", not "0001". We implement this 06298 * by _discarding_ the string rep since we know it will be 06299 * regenerated, if needed later, by formatting the internal rep's 06300 * value. 06301 */ 06302 06303 if (valuePtr->bytes == NULL) { 06304 TRACE(("\"%.20s\" => numeric, same Tcl_Obj\n", O2S(valuePtr))); 06305 NEXT_INST_F(1, 0, 0); 06306 } 06307 if (Tcl_IsShared(valuePtr)) { 06308 /* 06309 * Here we do some surgery within the Tcl_Obj internals. We want 06310 * to copy the intrep, but not the string, so we temporarily hide 06311 * the string so we do not copy it. 06312 */ 06313 06314 char *savedString = valuePtr->bytes; 06315 06316 valuePtr->bytes = NULL; 06317 objResultPtr = Tcl_DuplicateObj(valuePtr); 06318 valuePtr->bytes = savedString; 06319 TRACE(("\"%.20s\" => numeric, new Tcl_Obj\n", O2S(valuePtr))); 06320 NEXT_INST_F(1, 1, 1); 06321 } 06322 TclInvalidateStringRep(valuePtr); 06323 TRACE(("\"%.20s\" => numeric, same Tcl_Obj\n", O2S(valuePtr))); 06324 NEXT_INST_F(1, 0, 0); 06325 } 06326 06327 case INST_BREAK: 06328 /* 06329 DECACHE_STACK_INFO(); 06330 Tcl_ResetResult(interp); 06331 CACHE_STACK_INFO(); 06332 */ 06333 result = TCL_BREAK; 06334 cleanup = 0; 06335 goto processExceptionReturn; 06336 06337 case INST_CONTINUE: 06338 /* 06339 DECACHE_STACK_INFO(); 06340 Tcl_ResetResult(interp); 06341 CACHE_STACK_INFO(); 06342 */ 06343 result = TCL_CONTINUE; 06344 cleanup = 0; 06345 goto processExceptionReturn; 06346 06347 case INST_FOREACH_START4: { 06348 /* 06349 * Initialize the temporary local var that holds the count of the 06350 * number of iterations of the loop body to -1. 06351 */ 06352 06353 int opnd, iterTmpIndex; 06354 ForeachInfo *infoPtr; 06355 Var *iterVarPtr; 06356 Tcl_Obj *oldValuePtr; 06357 06358 opnd = TclGetUInt4AtPtr(pc+1); 06359 infoPtr = (ForeachInfo *) codePtr->auxDataArrayPtr[opnd].clientData; 06360 iterTmpIndex = infoPtr->loopCtTemp; 06361 iterVarPtr = &(compiledLocals[iterTmpIndex]); 06362 oldValuePtr = iterVarPtr->value.objPtr; 06363 06364 if (oldValuePtr == NULL) { 06365 TclNewLongObj(iterVarPtr->value.objPtr, -1); 06366 Tcl_IncrRefCount(iterVarPtr->value.objPtr); 06367 } else { 06368 TclSetLongObj(oldValuePtr, -1); 06369 } 06370 TRACE(("%u => loop iter count temp %d\n", opnd, iterTmpIndex)); 06371 06372 #ifndef TCL_COMPILE_DEBUG 06373 /* 06374 * Remark that the compiler ALWAYS sets INST_FOREACH_STEP4 immediately 06375 * after INST_FOREACH_START4 - let us just fall through instead of 06376 * jumping back to the top. 06377 */ 06378 06379 pc += 5; 06380 TCL_DTRACE_INST_NEXT(); 06381 #else 06382 NEXT_INST_F(5, 0, 0); 06383 #endif 06384 } 06385 06386 case INST_FOREACH_STEP4: { 06387 /* 06388 * "Step" a foreach loop (i.e., begin its next iteration) by assigning 06389 * the next value list element to each loop var. 06390 */ 06391 06392 ForeachInfo *infoPtr; 06393 ForeachVarList *varListPtr; 06394 Tcl_Obj *listPtr,*valuePtr, *value2Ptr, **elements; 06395 Var *iterVarPtr, *listVarPtr, *varPtr; 06396 int opnd, numLists, iterNum, listTmpIndex, listLen, numVars; 06397 int varIndex, valIndex, continueLoop, j; 06398 long i; 06399 06400 opnd = TclGetUInt4AtPtr(pc+1); 06401 infoPtr = (ForeachInfo *) codePtr->auxDataArrayPtr[opnd].clientData; 06402 numLists = infoPtr->numLists; 06403 06404 /* 06405 * Increment the temp holding the loop iteration number. 06406 */ 06407 06408 iterVarPtr = &(compiledLocals[infoPtr->loopCtTemp]); 06409 valuePtr = iterVarPtr->value.objPtr; 06410 iterNum = (valuePtr->internalRep.longValue + 1); 06411 TclSetLongObj(valuePtr, iterNum); 06412 06413 /* 06414 * Check whether all value lists are exhausted and we should stop the 06415 * loop. 06416 */ 06417 06418 continueLoop = 0; 06419 listTmpIndex = infoPtr->firstValueTemp; 06420 for (i = 0; i < numLists; i++) { 06421 varListPtr = infoPtr->varLists[i]; 06422 numVars = varListPtr->numVars; 06423 06424 listVarPtr = &(compiledLocals[listTmpIndex]); 06425 listPtr = listVarPtr->value.objPtr; 06426 result = TclListObjLength(interp, listPtr, &listLen); 06427 if (result == TCL_OK) { 06428 if (listLen > (iterNum * numVars)) { 06429 continueLoop = 1; 06430 } 06431 listTmpIndex++; 06432 } else { 06433 TRACE_WITH_OBJ(("%u => ERROR converting list %ld, \"%s\": ", 06434 opnd, i, O2S(listPtr)), Tcl_GetObjResult(interp)); 06435 goto checkForCatch; 06436 } 06437 } 06438 06439 /* 06440 * If some var in some var list still has a remaining list element 06441 * iterate one more time. Assign to var the next element from its 06442 * value list. We already checked above that each list temp holds a 06443 * valid list object (by calling Tcl_ListObjLength), but cannot rely 06444 * on that check remaining valid: one list could have been shimmered 06445 * as a side effect of setting a traced variable. 06446 */ 06447 06448 if (continueLoop) { 06449 listTmpIndex = infoPtr->firstValueTemp; 06450 for (i = 0; i < numLists; i++) { 06451 varListPtr = infoPtr->varLists[i]; 06452 numVars = varListPtr->numVars; 06453 06454 listVarPtr = &(compiledLocals[listTmpIndex]); 06455 listPtr = TclListObjCopy(NULL, listVarPtr->value.objPtr); 06456 TclListObjGetElements(interp, listPtr, &listLen, &elements); 06457 06458 valIndex = (iterNum * numVars); 06459 for (j = 0; j < numVars; j++) { 06460 if (valIndex >= listLen) { 06461 TclNewObj(valuePtr); 06462 } else { 06463 valuePtr = elements[valIndex]; 06464 } 06465 06466 varIndex = varListPtr->varIndexes[j]; 06467 varPtr = &(compiledLocals[varIndex]); 06468 while (TclIsVarLink(varPtr)) { 06469 varPtr = varPtr->value.linkPtr; 06470 } 06471 if (TclIsVarDirectWritable(varPtr)) { 06472 value2Ptr = varPtr->value.objPtr; 06473 if (valuePtr != value2Ptr) { 06474 if (value2Ptr != NULL) { 06475 TclDecrRefCount(value2Ptr); 06476 } 06477 varPtr->value.objPtr = valuePtr; 06478 Tcl_IncrRefCount(valuePtr); 06479 } 06480 } else { 06481 DECACHE_STACK_INFO(); 06482 value2Ptr = TclPtrSetVar(interp, varPtr, NULL, NULL, 06483 NULL, valuePtr, TCL_LEAVE_ERR_MSG, varIndex); 06484 CACHE_STACK_INFO(); 06485 if (value2Ptr == NULL) { 06486 TRACE_WITH_OBJ(( 06487 "%u => ERROR init. index temp %d: ", 06488 opnd,varIndex), Tcl_GetObjResult(interp)); 06489 result = TCL_ERROR; 06490 TclDecrRefCount(listPtr); 06491 goto checkForCatch; 06492 } 06493 } 06494 valIndex++; 06495 } 06496 TclDecrRefCount(listPtr); 06497 listTmpIndex++; 06498 } 06499 } 06500 TRACE(("%u => %d lists, iter %d, %s loop\n", opnd, numLists, 06501 iterNum, (continueLoop? "continue" : "exit"))); 06502 06503 /* 06504 * Run-time peep-hole optimisation: the compiler ALWAYS follows 06505 * INST_FOREACH_STEP4 with an INST_JUMP_FALSE. We just skip that 06506 * instruction and jump direct from here. 06507 */ 06508 06509 pc += 5; 06510 if (*pc == INST_JUMP_FALSE1) { 06511 NEXT_INST_F((continueLoop? 2 : TclGetInt1AtPtr(pc+1)), 0, 0); 06512 } else { 06513 NEXT_INST_F((continueLoop? 5 : TclGetInt4AtPtr(pc+1)), 0, 0); 06514 } 06515 } 06516 06517 case INST_BEGIN_CATCH4: 06518 /* 06519 * Record start of the catch command with exception range index equal 06520 * to the operand. Push the current stack depth onto the special catch 06521 * stack. 06522 */ 06523 06524 *(++catchTop) = CURR_DEPTH; 06525 TRACE(("%u => catchTop=%d, stackTop=%d\n", 06526 TclGetUInt4AtPtr(pc+1), (catchTop - initCatchTop - 1), 06527 (int) CURR_DEPTH)); 06528 NEXT_INST_F(5, 0, 0); 06529 06530 case INST_END_CATCH: 06531 catchTop--; 06532 Tcl_ResetResult(interp); 06533 result = TCL_OK; 06534 TRACE(("=> catchTop=%d\n", (catchTop - initCatchTop - 1))); 06535 NEXT_INST_F(1, 0, 0); 06536 06537 case INST_PUSH_RESULT: 06538 objResultPtr = Tcl_GetObjResult(interp); 06539 TRACE_WITH_OBJ(("=> "), objResultPtr); 06540 06541 /* 06542 * See the comments at INST_INVOKE_STK 06543 */ 06544 { 06545 Tcl_Obj *newObjResultPtr; 06546 06547 TclNewObj(newObjResultPtr); 06548 Tcl_IncrRefCount(newObjResultPtr); 06549 iPtr->objResultPtr = newObjResultPtr; 06550 } 06551 06552 NEXT_INST_F(1, 0, -1); 06553 06554 case INST_PUSH_RETURN_CODE: 06555 TclNewIntObj(objResultPtr, result); 06556 TRACE(("=> %u\n", result)); 06557 NEXT_INST_F(1, 0, 1); 06558 06559 case INST_PUSH_RETURN_OPTIONS: 06560 objResultPtr = Tcl_GetReturnOptions(interp, result); 06561 TRACE_WITH_OBJ(("=> "), objResultPtr); 06562 NEXT_INST_F(1, 0, 1); 06563 06564 /* TODO: normalize "valPtr" to "valuePtr" */ 06565 { 06566 int opnd, opnd2, allocateDict; 06567 Tcl_Obj *dictPtr, *valPtr; 06568 Var *varPtr; 06569 06570 case INST_DICT_GET: 06571 opnd = TclGetUInt4AtPtr(pc+1); 06572 TRACE(("%u => ", opnd)); 06573 dictPtr = OBJ_AT_DEPTH(opnd); 06574 if (opnd > 1) { 06575 dictPtr = TclTraceDictPath(interp, dictPtr, opnd-1, 06576 &OBJ_AT_DEPTH(opnd-1), DICT_PATH_READ); 06577 if (dictPtr == NULL) { 06578 TRACE_WITH_OBJ(( 06579 "%u => ERROR tracing dictionary path into \"%s\": ", 06580 opnd, O2S(OBJ_AT_DEPTH(opnd))), 06581 Tcl_GetObjResult(interp)); 06582 result = TCL_ERROR; 06583 goto checkForCatch; 06584 } 06585 } 06586 result = Tcl_DictObjGet(interp, dictPtr, OBJ_AT_TOS, &objResultPtr); 06587 if ((result == TCL_OK) && objResultPtr) { 06588 TRACE_APPEND(("%.30s\n", O2S(objResultPtr))); 06589 NEXT_INST_V(5, opnd+1, 1); 06590 } 06591 if (result != TCL_OK) { 06592 TRACE_WITH_OBJ(( 06593 "%u => ERROR reading leaf dictionary key \"%s\": ", 06594 opnd, O2S(dictPtr)), Tcl_GetObjResult(interp)); 06595 } else { 06596 /*Tcl_ResetResult(interp);*/ 06597 Tcl_AppendResult(interp, "key \"", TclGetString(OBJ_AT_TOS), 06598 "\" not known in dictionary", NULL); 06599 TRACE_WITH_OBJ(("%u => ERROR ", opnd), Tcl_GetObjResult(interp)); 06600 result = TCL_ERROR; 06601 } 06602 goto checkForCatch; 06603 06604 case INST_DICT_SET: 06605 case INST_DICT_UNSET: 06606 case INST_DICT_INCR_IMM: 06607 opnd = TclGetUInt4AtPtr(pc+1); 06608 opnd2 = TclGetUInt4AtPtr(pc+5); 06609 06610 varPtr = &(compiledLocals[opnd2]); 06611 while (TclIsVarLink(varPtr)) { 06612 varPtr = varPtr->value.linkPtr; 06613 } 06614 TRACE(("%u %u => ", opnd, opnd2)); 06615 if (TclIsVarDirectReadable(varPtr)) { 06616 dictPtr = varPtr->value.objPtr; 06617 } else { 06618 DECACHE_STACK_INFO(); 06619 dictPtr = TclPtrGetVar(interp, varPtr, NULL,NULL,NULL, 0, opnd2); 06620 CACHE_STACK_INFO(); 06621 } 06622 if (dictPtr == NULL) { 06623 TclNewObj(dictPtr); 06624 allocateDict = 1; 06625 } else { 06626 allocateDict = Tcl_IsShared(dictPtr); 06627 if (allocateDict) { 06628 dictPtr = Tcl_DuplicateObj(dictPtr); 06629 } 06630 } 06631 06632 switch (*pc) { 06633 case INST_DICT_SET: 06634 cleanup = opnd + 1; 06635 result = Tcl_DictObjPutKeyList(interp, dictPtr, opnd, 06636 &OBJ_AT_DEPTH(opnd), OBJ_AT_TOS); 06637 break; 06638 case INST_DICT_INCR_IMM: 06639 cleanup = 1; 06640 opnd = TclGetInt4AtPtr(pc+1); 06641 result = Tcl_DictObjGet(interp, dictPtr, OBJ_AT_TOS, &valPtr); 06642 if (result != TCL_OK) { 06643 break; 06644 } 06645 if (valPtr == NULL) { 06646 Tcl_DictObjPut(NULL, dictPtr, OBJ_AT_TOS,Tcl_NewIntObj(opnd)); 06647 } else { 06648 Tcl_Obj *incrPtr = Tcl_NewIntObj(opnd); 06649 06650 Tcl_IncrRefCount(incrPtr); 06651 if (Tcl_IsShared(valPtr)) { 06652 valPtr = Tcl_DuplicateObj(valPtr); 06653 Tcl_DictObjPut(NULL, dictPtr, OBJ_AT_TOS, valPtr); 06654 } 06655 result = TclIncrObj(interp, valPtr, incrPtr); 06656 if (result == TCL_OK) { 06657 Tcl_InvalidateStringRep(dictPtr); 06658 } 06659 TclDecrRefCount(incrPtr); 06660 } 06661 break; 06662 case INST_DICT_UNSET: 06663 cleanup = opnd; 06664 result = Tcl_DictObjRemoveKeyList(interp, dictPtr, opnd, 06665 &OBJ_AT_DEPTH(opnd-1)); 06666 break; 06667 default: 06668 cleanup = 0; /* stop compiler warning */ 06669 Tcl_Panic("Should not happen!"); 06670 } 06671 06672 if (result != TCL_OK) { 06673 if (allocateDict) { 06674 TclDecrRefCount(dictPtr); 06675 } 06676 TRACE_WITH_OBJ(("%u %u => ERROR updating dictionary: ", 06677 opnd, opnd2), Tcl_GetObjResult(interp)); 06678 goto checkForCatch; 06679 } 06680 06681 if (TclIsVarDirectWritable(varPtr)) { 06682 if (allocateDict) { 06683 Tcl_Obj *oldValuePtr = varPtr->value.objPtr; 06684 06685 Tcl_IncrRefCount(dictPtr); 06686 if (oldValuePtr != NULL) { 06687 TclDecrRefCount(oldValuePtr); 06688 } 06689 varPtr->value.objPtr = dictPtr; 06690 } 06691 objResultPtr = dictPtr; 06692 } else { 06693 Tcl_IncrRefCount(dictPtr); 06694 DECACHE_STACK_INFO(); 06695 objResultPtr = TclPtrSetVar(interp, varPtr, NULL, NULL, NULL, 06696 dictPtr, TCL_LEAVE_ERR_MSG, opnd2); 06697 CACHE_STACK_INFO(); 06698 TclDecrRefCount(dictPtr); 06699 if (objResultPtr == NULL) { 06700 TRACE_APPEND(("ERROR: %.30s\n", 06701 O2S(Tcl_GetObjResult(interp)))); 06702 result = TCL_ERROR; 06703 goto checkForCatch; 06704 } 06705 } 06706 #ifndef TCL_COMPILE_DEBUG 06707 if (*(pc+9) == INST_POP) { 06708 NEXT_INST_V(10, cleanup, 0); 06709 } 06710 #endif 06711 TRACE_APPEND(("%.30s\n", O2S(objResultPtr))); 06712 NEXT_INST_V(9, cleanup, 1); 06713 06714 case INST_DICT_APPEND: 06715 case INST_DICT_LAPPEND: 06716 opnd = TclGetUInt4AtPtr(pc+1); 06717 06718 varPtr = &(compiledLocals[opnd]); 06719 while (TclIsVarLink(varPtr)) { 06720 varPtr = varPtr->value.linkPtr; 06721 } 06722 TRACE(("%u => ", opnd)); 06723 if (TclIsVarDirectReadable(varPtr)) { 06724 dictPtr = varPtr->value.objPtr; 06725 } else { 06726 DECACHE_STACK_INFO(); 06727 dictPtr = TclPtrGetVar(interp, varPtr, NULL, NULL, NULL, 0, opnd); 06728 CACHE_STACK_INFO(); 06729 } 06730 if (dictPtr == NULL) { 06731 TclNewObj(dictPtr); 06732 allocateDict = 1; 06733 } else { 06734 allocateDict = Tcl_IsShared(dictPtr); 06735 if (allocateDict) { 06736 dictPtr = Tcl_DuplicateObj(dictPtr); 06737 } 06738 } 06739 06740 result = Tcl_DictObjGet(interp, dictPtr, OBJ_UNDER_TOS, &valPtr); 06741 if (result != TCL_OK) { 06742 if (allocateDict) { 06743 TclDecrRefCount(dictPtr); 06744 } 06745 goto checkForCatch; 06746 } 06747 06748 /* 06749 * Note that a non-existent key results in a NULL valPtr, which is a 06750 * case handled separately below. What we *can* say at this point is 06751 * that the write-back will always succeed. 06752 */ 06753 06754 switch (*pc) { 06755 case INST_DICT_APPEND: 06756 if (valPtr == NULL) { 06757 valPtr = OBJ_AT_TOS; 06758 } else { 06759 if (Tcl_IsShared(valPtr)) { 06760 valPtr = Tcl_DuplicateObj(valPtr); 06761 } 06762 Tcl_AppendObjToObj(valPtr, OBJ_AT_TOS); 06763 } 06764 break; 06765 case INST_DICT_LAPPEND: 06766 /* 06767 * More complex because list-append can fail. 06768 */ 06769 06770 if (valPtr == NULL) { 06771 valPtr = Tcl_NewListObj(1, &OBJ_AT_TOS); 06772 } else if (Tcl_IsShared(valPtr)) { 06773 valPtr = Tcl_DuplicateObj(valPtr); 06774 result = Tcl_ListObjAppendElement(interp, valPtr, OBJ_AT_TOS); 06775 if (result != TCL_OK) { 06776 TclDecrRefCount(valPtr); 06777 if (allocateDict) { 06778 TclDecrRefCount(dictPtr); 06779 } 06780 goto checkForCatch; 06781 } 06782 } else { 06783 result = Tcl_ListObjAppendElement(interp, valPtr, OBJ_AT_TOS); 06784 if (result != TCL_OK) { 06785 if (allocateDict) { 06786 TclDecrRefCount(dictPtr); 06787 } 06788 goto checkForCatch; 06789 } 06790 } 06791 break; 06792 default: 06793 Tcl_Panic("Should not happen!"); 06794 } 06795 06796 Tcl_DictObjPut(NULL, dictPtr, OBJ_UNDER_TOS, valPtr); 06797 06798 if (TclIsVarDirectWritable(varPtr)) { 06799 if (allocateDict) { 06800 Tcl_Obj *oldValuePtr = varPtr->value.objPtr; 06801 06802 Tcl_IncrRefCount(dictPtr); 06803 if (oldValuePtr != NULL) { 06804 TclDecrRefCount(oldValuePtr); 06805 } 06806 varPtr->value.objPtr = dictPtr; 06807 } 06808 objResultPtr = dictPtr; 06809 } else { 06810 Tcl_IncrRefCount(dictPtr); 06811 DECACHE_STACK_INFO(); 06812 objResultPtr = TclPtrSetVar(interp, varPtr, NULL, NULL, NULL, 06813 dictPtr, TCL_LEAVE_ERR_MSG, opnd); 06814 CACHE_STACK_INFO(); 06815 TclDecrRefCount(dictPtr); 06816 if (objResultPtr == NULL) { 06817 TRACE_APPEND(("ERROR: %.30s\n", 06818 O2S(Tcl_GetObjResult(interp)))); 06819 result = TCL_ERROR; 06820 goto checkForCatch; 06821 } 06822 } 06823 #ifndef TCL_COMPILE_DEBUG 06824 if (*(pc+5) == INST_POP) { 06825 NEXT_INST_F(6, 2, 0); 06826 } 06827 #endif 06828 TRACE_APPEND(("%.30s\n", O2S(objResultPtr))); 06829 NEXT_INST_F(5, 2, 1); 06830 } 06831 06832 { 06833 int opnd, done; 06834 Tcl_Obj *statePtr, *dictPtr, *keyPtr, *valuePtr, *emptyPtr; 06835 Var *varPtr; 06836 Tcl_DictSearch *searchPtr; 06837 06838 case INST_DICT_FIRST: 06839 opnd = TclGetUInt4AtPtr(pc+1); 06840 TRACE(("%u => ", opnd)); 06841 dictPtr = POP_OBJECT(); 06842 searchPtr = (Tcl_DictSearch *) ckalloc(sizeof(Tcl_DictSearch)); 06843 result = Tcl_DictObjFirst(interp, dictPtr, searchPtr, &keyPtr, 06844 &valuePtr, &done); 06845 if (result != TCL_OK) { 06846 ckfree((char *) searchPtr); 06847 goto checkForCatch; 06848 } 06849 TclNewObj(statePtr); 06850 statePtr->typePtr = &dictIteratorType; 06851 statePtr->internalRep.twoPtrValue.ptr1 = (void *) searchPtr; 06852 statePtr->internalRep.twoPtrValue.ptr2 = (void *) dictPtr; 06853 varPtr = (compiledLocals + opnd); 06854 if (varPtr->value.objPtr) { 06855 if (varPtr->value.objPtr->typePtr != &dictIteratorType) { 06856 TclDecrRefCount(varPtr->value.objPtr); 06857 } else { 06858 Tcl_Panic("mis-issued dictFirst!"); 06859 } 06860 } 06861 varPtr->value.objPtr = statePtr; 06862 Tcl_IncrRefCount(statePtr); 06863 goto pushDictIteratorResult; 06864 06865 case INST_DICT_NEXT: 06866 opnd = TclGetUInt4AtPtr(pc+1); 06867 TRACE(("%u => ", opnd)); 06868 statePtr = compiledLocals[opnd].value.objPtr; 06869 if (statePtr == NULL || statePtr->typePtr != &dictIteratorType) { 06870 Tcl_Panic("mis-issued dictNext!"); 06871 } 06872 searchPtr = (Tcl_DictSearch *) statePtr->internalRep.twoPtrValue.ptr1; 06873 Tcl_DictObjNext(searchPtr, &keyPtr, &valuePtr, &done); 06874 pushDictIteratorResult: 06875 if (done) { 06876 TclNewObj(emptyPtr); 06877 PUSH_OBJECT(emptyPtr); 06878 PUSH_OBJECT(emptyPtr); 06879 } else { 06880 PUSH_OBJECT(valuePtr); 06881 PUSH_OBJECT(keyPtr); 06882 } 06883 TRACE_APPEND(("\"%.30s\" \"%.30s\" %d", 06884 O2S(OBJ_UNDER_TOS), O2S(OBJ_AT_TOS), done)); 06885 objResultPtr = constants[done]; 06886 /* TODO: consider opt like INST_FOREACH_STEP4 */ 06887 NEXT_INST_F(5, 0, 1); 06888 06889 case INST_DICT_DONE: 06890 opnd = TclGetUInt4AtPtr(pc+1); 06891 TRACE(("%u => ", opnd)); 06892 statePtr = compiledLocals[opnd].value.objPtr; 06893 if (statePtr == NULL) { 06894 Tcl_Panic("mis-issued dictDone!"); 06895 } 06896 06897 if (statePtr->typePtr == &dictIteratorType) { 06898 /* 06899 * First kill the search, and then release the reference to the 06900 * dictionary that we were holding. 06901 */ 06902 06903 searchPtr = (Tcl_DictSearch *) 06904 statePtr->internalRep.twoPtrValue.ptr1; 06905 Tcl_DictObjDone(searchPtr); 06906 ckfree((char *) searchPtr); 06907 06908 dictPtr = (Tcl_Obj *) statePtr->internalRep.twoPtrValue.ptr2; 06909 TclDecrRefCount(dictPtr); 06910 06911 /* 06912 * Set the internal variable to an empty object to signify that we 06913 * don't hold an iterator. 06914 */ 06915 06916 TclDecrRefCount(statePtr); 06917 TclNewObj(emptyPtr); 06918 compiledLocals[opnd].value.objPtr = emptyPtr; 06919 Tcl_IncrRefCount(emptyPtr); 06920 } 06921 NEXT_INST_F(5, 0, 0); 06922 } 06923 06924 { 06925 int opnd, opnd2, i, length, allocdict; 06926 Tcl_Obj **keyPtrPtr, *dictPtr; 06927 DictUpdateInfo *duiPtr; 06928 Var *varPtr; 06929 06930 case INST_DICT_UPDATE_START: 06931 opnd = TclGetUInt4AtPtr(pc+1); 06932 opnd2 = TclGetUInt4AtPtr(pc+5); 06933 varPtr = &(compiledLocals[opnd]); 06934 duiPtr = codePtr->auxDataArrayPtr[opnd2].clientData; 06935 while (TclIsVarLink(varPtr)) { 06936 varPtr = varPtr->value.linkPtr; 06937 } 06938 TRACE(("%u => ", opnd)); 06939 if (TclIsVarDirectReadable(varPtr)) { 06940 dictPtr = varPtr->value.objPtr; 06941 } else { 06942 DECACHE_STACK_INFO(); 06943 dictPtr = TclPtrGetVar(interp, varPtr, NULL, NULL, NULL, 06944 TCL_LEAVE_ERR_MSG, opnd); 06945 CACHE_STACK_INFO(); 06946 if (dictPtr == NULL) { 06947 goto dictUpdateStartFailed; 06948 } 06949 } 06950 if (TclListObjGetElements(interp, OBJ_AT_TOS, &length, 06951 &keyPtrPtr) != TCL_OK) { 06952 goto dictUpdateStartFailed; 06953 } 06954 if (length != duiPtr->length) { 06955 Tcl_Panic("dictUpdateStart argument length mismatch"); 06956 } 06957 for (i=0 ; i<length ; i++) { 06958 Tcl_Obj *valPtr; 06959 06960 if (Tcl_DictObjGet(interp, dictPtr, keyPtrPtr[i], 06961 &valPtr) != TCL_OK) { 06962 goto dictUpdateStartFailed; 06963 } 06964 varPtr = &(compiledLocals[duiPtr->varIndices[i]]); 06965 while (TclIsVarLink(varPtr)) { 06966 varPtr = varPtr->value.linkPtr; 06967 } 06968 DECACHE_STACK_INFO(); 06969 if (valPtr == NULL) { 06970 TclObjUnsetVar2(interp, 06971 localName(iPtr->varFramePtr, duiPtr->varIndices[i]), 06972 NULL, 0); 06973 } else if (TclPtrSetVar(interp, varPtr, NULL, NULL, NULL, 06974 valPtr, TCL_LEAVE_ERR_MSG, 06975 duiPtr->varIndices[i]) == NULL) { 06976 CACHE_STACK_INFO(); 06977 dictUpdateStartFailed: 06978 result = TCL_ERROR; 06979 goto checkForCatch; 06980 } 06981 CACHE_STACK_INFO(); 06982 } 06983 NEXT_INST_F(9, 0, 0); 06984 06985 case INST_DICT_UPDATE_END: 06986 opnd = TclGetUInt4AtPtr(pc+1); 06987 opnd2 = TclGetUInt4AtPtr(pc+5); 06988 varPtr = &(compiledLocals[opnd]); 06989 duiPtr = codePtr->auxDataArrayPtr[opnd2].clientData; 06990 while (TclIsVarLink(varPtr)) { 06991 varPtr = varPtr->value.linkPtr; 06992 } 06993 TRACE(("%u => ", opnd)); 06994 if (TclIsVarDirectReadable(varPtr)) { 06995 dictPtr = varPtr->value.objPtr; 06996 } else { 06997 DECACHE_STACK_INFO(); 06998 dictPtr = TclPtrGetVar(interp, varPtr, NULL, NULL, NULL, 0, opnd); 06999 CACHE_STACK_INFO(); 07000 } 07001 if (dictPtr == NULL) { 07002 NEXT_INST_F(9, 1, 0); 07003 } 07004 if (Tcl_DictObjSize(interp, dictPtr, &length) != TCL_OK 07005 || TclListObjGetElements(interp, OBJ_AT_TOS, &length, 07006 &keyPtrPtr) != TCL_OK) { 07007 result = TCL_ERROR; 07008 goto checkForCatch; 07009 } 07010 allocdict = Tcl_IsShared(dictPtr); 07011 if (allocdict) { 07012 dictPtr = Tcl_DuplicateObj(dictPtr); 07013 } 07014 for (i=0 ; i<length ; i++) { 07015 Tcl_Obj *valPtr; 07016 Var *var2Ptr; 07017 07018 var2Ptr = &(compiledLocals[duiPtr->varIndices[i]]); 07019 while (TclIsVarLink(var2Ptr)) { 07020 var2Ptr = var2Ptr->value.linkPtr; 07021 } 07022 if (TclIsVarDirectReadable(var2Ptr)) { 07023 valPtr = var2Ptr->value.objPtr; 07024 } else { 07025 DECACHE_STACK_INFO(); 07026 valPtr = TclPtrGetVar(interp, var2Ptr, NULL, NULL, NULL, 0, 07027 duiPtr->varIndices[i]); 07028 CACHE_STACK_INFO(); 07029 } 07030 if (valPtr == NULL) { 07031 Tcl_DictObjRemove(interp, dictPtr, keyPtrPtr[i]); 07032 } else if (dictPtr == valPtr) { 07033 Tcl_DictObjPut(interp, dictPtr, keyPtrPtr[i], 07034 Tcl_DuplicateObj(valPtr)); 07035 } else { 07036 Tcl_DictObjPut(interp, dictPtr, keyPtrPtr[i], valPtr); 07037 } 07038 } 07039 if (TclIsVarDirectWritable(varPtr)) { 07040 Tcl_IncrRefCount(dictPtr); 07041 TclDecrRefCount(varPtr->value.objPtr); 07042 varPtr->value.objPtr = dictPtr; 07043 } else { 07044 DECACHE_STACK_INFO(); 07045 objResultPtr = TclPtrSetVar(interp, varPtr, NULL, NULL, NULL, 07046 dictPtr, TCL_LEAVE_ERR_MSG, opnd); 07047 CACHE_STACK_INFO(); 07048 if (objResultPtr == NULL) { 07049 if (allocdict) { 07050 TclDecrRefCount(dictPtr); 07051 } 07052 result = TCL_ERROR; 07053 goto checkForCatch; 07054 } 07055 } 07056 NEXT_INST_F(9, 1, 0); 07057 } 07058 07059 default: 07060 Tcl_Panic("TclExecuteByteCode: unrecognized opCode %u", *pc); 07061 } /* end of switch on opCode */ 07062 07063 /* 07064 * Division by zero in an expression. Control only reaches this point by 07065 * "goto divideByZero". 07066 */ 07067 07068 divideByZero: 07069 Tcl_SetObjResult(interp, Tcl_NewStringObj("divide by zero", -1)); 07070 Tcl_SetErrorCode(interp, "ARITH", "DIVZERO", "divide by zero", NULL); 07071 07072 result = TCL_ERROR; 07073 goto checkForCatch; 07074 07075 /* 07076 * Exponentiation of zero by negative number in an expression. Control 07077 * only reaches this point by "goto exponOfZero". 07078 */ 07079 07080 exponOfZero: 07081 Tcl_SetObjResult(interp, Tcl_NewStringObj( 07082 "exponentiation of zero by negative power", -1)); 07083 Tcl_SetErrorCode(interp, "ARITH", "DOMAIN", 07084 "exponentiation of zero by negative power", NULL); 07085 result = TCL_ERROR; 07086 goto checkForCatch; 07087 07088 /* 07089 * Block for variables needed to process exception returns. 07090 */ 07091 07092 { 07093 ExceptionRange *rangePtr; 07094 /* Points to closest loop or catch exception 07095 * range enclosing the pc. Used by various 07096 * instructions and processCatch to process 07097 * break, continue, and errors. */ 07098 Tcl_Obj *valuePtr; 07099 const char *bytes; 07100 int length; 07101 #if TCL_COMPILE_DEBUG 07102 int opnd; 07103 #endif 07104 07105 /* 07106 * An external evaluation (INST_INVOKE or INST_EVAL) returned 07107 * something different from TCL_OK, or else INST_BREAK or 07108 * INST_CONTINUE were called. 07109 */ 07110 07111 processExceptionReturn: 07112 #if TCL_COMPILE_DEBUG 07113 switch (*pc) { 07114 case INST_INVOKE_STK1: 07115 opnd = TclGetUInt1AtPtr(pc+1); 07116 TRACE(("%u => ... after \"%.20s\": ", opnd, cmdNameBuf)); 07117 break; 07118 case INST_INVOKE_STK4: 07119 opnd = TclGetUInt4AtPtr(pc+1); 07120 TRACE(("%u => ... after \"%.20s\": ", opnd, cmdNameBuf)); 07121 break; 07122 case INST_EVAL_STK: 07123 /* 07124 * Note that the object at stacktop has to be used before doing 07125 * the cleanup. 07126 */ 07127 07128 TRACE(("\"%.30s\" => ", O2S(OBJ_AT_TOS))); 07129 break; 07130 default: 07131 TRACE(("=> ")); 07132 } 07133 #endif 07134 if ((result == TCL_CONTINUE) || (result == TCL_BREAK)) { 07135 rangePtr = GetExceptRangeForPc(pc, /*catchOnly*/ 0, codePtr); 07136 if (rangePtr == NULL) { 07137 TRACE_APPEND(("no encl. loop or catch, returning %s\n", 07138 StringForResultCode(result))); 07139 goto abnormalReturn; 07140 } 07141 if (rangePtr->type == CATCH_EXCEPTION_RANGE) { 07142 TRACE_APPEND(("%s ...\n", StringForResultCode(result))); 07143 goto processCatch; 07144 } 07145 while (cleanup--) { 07146 valuePtr = POP_OBJECT(); 07147 TclDecrRefCount(valuePtr); 07148 } 07149 if (result == TCL_BREAK) { 07150 result = TCL_OK; 07151 pc = (codePtr->codeStart + rangePtr->breakOffset); 07152 TRACE_APPEND(("%s, range at %d, new pc %d\n", 07153 StringForResultCode(result), 07154 rangePtr->codeOffset, rangePtr->breakOffset)); 07155 NEXT_INST_F(0, 0, 0); 07156 } else { 07157 if (rangePtr->continueOffset == -1) { 07158 TRACE_APPEND(( 07159 "%s, loop w/o continue, checking for catch\n", 07160 StringForResultCode(result))); 07161 goto checkForCatch; 07162 } 07163 result = TCL_OK; 07164 pc = (codePtr->codeStart + rangePtr->continueOffset); 07165 TRACE_APPEND(("%s, range at %d, new pc %d\n", 07166 StringForResultCode(result), 07167 rangePtr->codeOffset, rangePtr->continueOffset)); 07168 NEXT_INST_F(0, 0, 0); 07169 } 07170 #if TCL_COMPILE_DEBUG 07171 } else if (traceInstructions) { 07172 if ((result != TCL_ERROR) && (result != TCL_RETURN)) { 07173 Tcl_Obj *objPtr = Tcl_GetObjResult(interp); 07174 TRACE_APPEND(("OTHER RETURN CODE %d, result= \"%s\"\n ", 07175 result, O2S(objPtr))); 07176 } else { 07177 Tcl_Obj *objPtr = Tcl_GetObjResult(interp); 07178 TRACE_APPEND(("%s, result= \"%s\"\n", 07179 StringForResultCode(result), O2S(objPtr))); 07180 } 07181 #endif 07182 } 07183 07184 /* 07185 * Execution has generated an "exception" such as TCL_ERROR. If the 07186 * exception is an error, record information about what was being 07187 * executed when the error occurred. Find the closest enclosing catch 07188 * range, if any. If no enclosing catch range is found, stop execution 07189 * and return the "exception" code. 07190 */ 07191 07192 checkForCatch: 07193 if ((result == TCL_ERROR) && !(iPtr->flags & ERR_ALREADY_LOGGED)) { 07194 bytes = GetSrcInfoForPc(pc, codePtr, &length); 07195 if (bytes != NULL) { 07196 DECACHE_STACK_INFO(); 07197 Tcl_LogCommandInfo(interp, codePtr->source, bytes, length); 07198 CACHE_STACK_INFO(); 07199 } 07200 } 07201 iPtr->flags &= ~ERR_ALREADY_LOGGED; 07202 07203 /* 07204 * Clear all expansions that may have started after the last 07205 * INST_BEGIN_CATCH. 07206 */ 07207 07208 while ((expandNestList != NULL) && ((catchTop == initCatchTop) || 07209 (*catchTop <= 07210 (ptrdiff_t) expandNestList->internalRep.twoPtrValue.ptr1))) { 07211 Tcl_Obj *objPtr = expandNestList->internalRep.twoPtrValue.ptr2; 07212 07213 TclDecrRefCount(expandNestList); 07214 expandNestList = objPtr; 07215 } 07216 07217 /* 07218 * We must not catch an exceeded limit. Instead, it blows outwards 07219 * until we either hit another interpreter (presumably where the limit 07220 * is not exceeded) or we get to the top-level. 07221 */ 07222 07223 if (TclLimitExceeded(iPtr->limit)) { 07224 #ifdef TCL_COMPILE_DEBUG 07225 if (traceInstructions) { 07226 fprintf(stdout, " ... limit exceeded, returning %s\n", 07227 StringForResultCode(result)); 07228 } 07229 #endif 07230 goto abnormalReturn; 07231 } 07232 if (catchTop == initCatchTop) { 07233 #ifdef TCL_COMPILE_DEBUG 07234 if (traceInstructions) { 07235 fprintf(stdout, " ... no enclosing catch, returning %s\n", 07236 StringForResultCode(result)); 07237 } 07238 #endif 07239 goto abnormalReturn; 07240 } 07241 rangePtr = GetExceptRangeForPc(pc, /*catchOnly*/ 1, codePtr); 07242 if (rangePtr == NULL) { 07243 /* 07244 * This is only possible when compiling a [catch] that sends its 07245 * script to INST_EVAL. Cannot correct the compiler without 07246 * breakingcompat with previous .tbc compiled scripts. 07247 */ 07248 07249 #ifdef TCL_COMPILE_DEBUG 07250 if (traceInstructions) { 07251 fprintf(stdout, " ... no enclosing catch, returning %s\n", 07252 StringForResultCode(result)); 07253 } 07254 #endif 07255 goto abnormalReturn; 07256 } 07257 07258 /* 07259 * A catch exception range (rangePtr) was found to handle an 07260 * "exception". It was found either by checkForCatch just above or by 07261 * an instruction during break, continue, or error processing. Jump to 07262 * its catchOffset after unwinding the operand stack to the depth it 07263 * had when starting to execute the range's catch command. 07264 */ 07265 07266 processCatch: 07267 while (CURR_DEPTH > *catchTop) { 07268 valuePtr = POP_OBJECT(); 07269 TclDecrRefCount(valuePtr); 07270 } 07271 #ifdef TCL_COMPILE_DEBUG 07272 if (traceInstructions) { 07273 fprintf(stdout, " ... found catch at %d, catchTop=%d, " 07274 "unwound to %ld, new pc %u\n", 07275 rangePtr->codeOffset, catchTop - initCatchTop - 1, 07276 (long) *catchTop, (unsigned) rangePtr->catchOffset); 07277 } 07278 #endif 07279 pc = (codePtr->codeStart + rangePtr->catchOffset); 07280 NEXT_INST_F(0, 0, 0); /* Restart the execution loop at pc. */ 07281 07282 /* 07283 * end of infinite loop dispatching on instructions. 07284 */ 07285 07286 /* 07287 * Abnormal return code. Restore the stack to state it had when 07288 * starting to execute the ByteCode. Panic if the stack is below the 07289 * initial level. 07290 */ 07291 07292 abnormalReturn: 07293 TCL_DTRACE_INST_LAST(); 07294 while (tosPtr > initTosPtr) { 07295 Tcl_Obj *objPtr = POP_OBJECT(); 07296 07297 Tcl_DecrRefCount(objPtr); 07298 } 07299 07300 /* 07301 * Clear all expansions. 07302 */ 07303 07304 while (expandNestList) { 07305 Tcl_Obj *objPtr = expandNestList->internalRep.twoPtrValue.ptr2; 07306 07307 TclDecrRefCount(expandNestList); 07308 expandNestList = objPtr; 07309 } 07310 if (tosPtr < initTosPtr) { 07311 fprintf(stderr, 07312 "\nTclExecuteByteCode: abnormal return at pc %u: " 07313 "stack top %d < entry stack top %d\n", 07314 (unsigned)(pc - codePtr->codeStart), 07315 (unsigned) CURR_DEPTH, (unsigned) 0); 07316 Tcl_Panic("TclExecuteByteCode execution failure: end stack top < start stack top"); 07317 } 07318 } 07319 07320 /* 07321 * Restore the stack to the state it had previous to this bytecode. 07322 */ 07323 07324 TclStackFree(interp, initCatchTop+1); 07325 return result; 07326 #undef iPtr 07327 } 07328 07329 #ifdef TCL_COMPILE_DEBUG 07330 /* 07331 *---------------------------------------------------------------------- 07332 * 07333 * PrintByteCodeInfo -- 07334 * 07335 * This procedure prints a summary about a bytecode object to stdout. It 07336 * is called by TclExecuteByteCode when starting to execute the bytecode 07337 * object if tclTraceExec has the value 2 or more. 07338 * 07339 * Results: 07340 * None. 07341 * 07342 * Side effects: 07343 * None. 07344 * 07345 *---------------------------------------------------------------------- 07346 */ 07347 07348 static void 07349 PrintByteCodeInfo( 07350 register ByteCode *codePtr) /* The bytecode whose summary is printed to 07351 * stdout. */ 07352 { 07353 Proc *procPtr = codePtr->procPtr; 07354 Interp *iPtr = (Interp *) *codePtr->interpHandle; 07355 07356 fprintf(stdout, "\nExecuting ByteCode 0x%p, refCt %u, epoch %u, interp 0x%p (epoch %u)\n", 07357 codePtr, codePtr->refCount, codePtr->compileEpoch, iPtr, 07358 iPtr->compileEpoch); 07359 07360 fprintf(stdout, " Source: "); 07361 TclPrintSource(stdout, codePtr->source, 60); 07362 07363 fprintf(stdout, "\n Cmds %d, src %d, inst %u, litObjs %u, aux %d, stkDepth %u, code/src %.2f\n", 07364 codePtr->numCommands, codePtr->numSrcBytes, 07365 codePtr->numCodeBytes, codePtr->numLitObjects, 07366 codePtr->numAuxDataItems, codePtr->maxStackDepth, 07367 #ifdef TCL_COMPILE_STATS 07368 codePtr->numSrcBytes? 07369 ((float)codePtr->structureSize)/codePtr->numSrcBytes : 07370 #endif 07371 0.0); 07372 07373 #ifdef TCL_COMPILE_STATS 07374 fprintf(stdout, " Code %lu = header %lu+inst %d+litObj %lu+exc %lu+aux %lu+cmdMap %d\n", 07375 (unsigned long) codePtr->structureSize, 07376 (unsigned long) (sizeof(ByteCode)-sizeof(size_t)-sizeof(Tcl_Time)), 07377 codePtr->numCodeBytes, 07378 (unsigned long) (codePtr->numLitObjects * sizeof(Tcl_Obj *)), 07379 (unsigned long) (codePtr->numExceptRanges*sizeof(ExceptionRange)), 07380 (unsigned long) (codePtr->numAuxDataItems * sizeof(AuxData)), 07381 codePtr->numCmdLocBytes); 07382 #endif /* TCL_COMPILE_STATS */ 07383 if (procPtr != NULL) { 07384 fprintf(stdout, 07385 " Proc 0x%p, refCt %d, args %d, compiled locals %d\n", 07386 procPtr, procPtr->refCount, procPtr->numArgs, 07387 procPtr->numCompiledLocals); 07388 } 07389 } 07390 #endif /* TCL_COMPILE_DEBUG */ 07391 07392 /* 07393 *---------------------------------------------------------------------- 07394 * 07395 * ValidatePcAndStackTop -- 07396 * 07397 * This procedure is called by TclExecuteByteCode when debugging to 07398 * verify that the program counter and stack top are valid during 07399 * execution. 07400 * 07401 * Results: 07402 * None. 07403 * 07404 * Side effects: 07405 * Prints a message to stderr and panics if either the pc or stack top 07406 * are invalid. 07407 * 07408 *---------------------------------------------------------------------- 07409 */ 07410 07411 #ifdef TCL_COMPILE_DEBUG 07412 static void 07413 ValidatePcAndStackTop( 07414 register ByteCode *codePtr, /* The bytecode whose summary is printed to 07415 * stdout. */ 07416 unsigned char *pc, /* Points to first byte of a bytecode 07417 * instruction. The program counter. */ 07418 int stackTop, /* Current stack top. Must be between 07419 * stackLowerBound and stackUpperBound 07420 * (inclusive). */ 07421 int stackLowerBound, /* Smallest legal value for stackTop. */ 07422 int checkStack) /* 0 if the stack depth check should be 07423 * skipped. */ 07424 { 07425 int stackUpperBound = stackLowerBound + codePtr->maxStackDepth; 07426 /* Greatest legal value for stackTop. */ 07427 unsigned relativePc = (unsigned) (pc - codePtr->codeStart); 07428 unsigned long codeStart = (unsigned long) codePtr->codeStart; 07429 unsigned long codeEnd = (unsigned long) 07430 (codePtr->codeStart + codePtr->numCodeBytes); 07431 unsigned char opCode = *pc; 07432 07433 if (((unsigned long) pc < codeStart) || ((unsigned long) pc > codeEnd)) { 07434 fprintf(stderr, "\nBad instruction pc 0x%p in TclExecuteByteCode\n", 07435 pc); 07436 Tcl_Panic("TclExecuteByteCode execution failure: bad pc"); 07437 } 07438 if ((unsigned) opCode > LAST_INST_OPCODE) { 07439 fprintf(stderr, "\nBad opcode %d at pc %u in TclExecuteByteCode\n", 07440 (unsigned) opCode, relativePc); 07441 Tcl_Panic("TclExecuteByteCode execution failure: bad opcode"); 07442 } 07443 if (checkStack && 07444 ((stackTop < stackLowerBound) || (stackTop > stackUpperBound))) { 07445 int numChars; 07446 const char *cmd = GetSrcInfoForPc(pc, codePtr, &numChars); 07447 07448 fprintf(stderr, "\nBad stack top %d at pc %u in TclExecuteByteCode (min %i, max %i)", 07449 stackTop, relativePc, stackLowerBound, stackUpperBound); 07450 if (cmd != NULL) { 07451 Tcl_Obj *message; 07452 07453 TclNewLiteralStringObj(message, "\n executing "); 07454 Tcl_IncrRefCount(message); 07455 Tcl_AppendLimitedToObj(message, cmd, numChars, 100, NULL); 07456 fprintf(stderr,"%s\n", Tcl_GetString(message)); 07457 Tcl_DecrRefCount(message); 07458 } else { 07459 fprintf(stderr, "\n"); 07460 } 07461 Tcl_Panic("TclExecuteByteCode execution failure: bad stack top"); 07462 } 07463 } 07464 #endif /* TCL_COMPILE_DEBUG */ 07465 07466 /* 07467 *---------------------------------------------------------------------- 07468 * 07469 * IllegalExprOperandType -- 07470 * 07471 * Used by TclExecuteByteCode to append an error message to the interp 07472 * result when an illegal operand type is detected by an expression 07473 * instruction. The argument opndPtr holds the operand object in error. 07474 * 07475 * Results: 07476 * None. 07477 * 07478 * Side effects: 07479 * An error message is appended to the interp result. 07480 * 07481 *---------------------------------------------------------------------- 07482 */ 07483 07484 static void 07485 IllegalExprOperandType( 07486 Tcl_Interp *interp, /* Interpreter to which error information 07487 * pertains. */ 07488 unsigned char *pc, /* Points to the instruction being executed 07489 * when the illegal type was found. */ 07490 Tcl_Obj *opndPtr) /* Points to the operand holding the value 07491 * with the illegal type. */ 07492 { 07493 ClientData ptr; 07494 int type; 07495 unsigned char opcode = *pc; 07496 const char *description, *operator = operatorStrings[opcode - INST_LOR]; 07497 07498 if (opcode == INST_EXPON) { 07499 operator = "**"; 07500 } 07501 07502 if (GetNumberFromObj(NULL, opndPtr, &ptr, &type) != TCL_OK) { 07503 int numBytes; 07504 const char *bytes = Tcl_GetStringFromObj(opndPtr, &numBytes); 07505 07506 if (numBytes == 0) { 07507 description = "empty string"; 07508 } else if (TclCheckBadOctal(NULL, bytes)) { 07509 description = "invalid octal number"; 07510 } else { 07511 description = "non-numeric string"; 07512 } 07513 } else if (type == TCL_NUMBER_NAN) { 07514 description = "non-numeric floating-point value"; 07515 } else if (type == TCL_NUMBER_DOUBLE) { 07516 description = "floating-point value"; 07517 } else { 07518 /* TODO: No caller needs this. Eliminate? */ 07519 description = "(big) integer"; 07520 } 07521 07522 Tcl_SetObjResult(interp, Tcl_ObjPrintf( 07523 "can't use %s as operand of \"%s\"", description, operator)); 07524 } 07525 07526 /* 07527 *---------------------------------------------------------------------- 07528 * 07529 * TclGetSrcInfoForPc, GetSrcInfoForPc, TclGetSrcInfoForCmd -- 07530 * 07531 * Given a program counter value, finds the closest command in the 07532 * bytecode code unit's CmdLocation array and returns information about 07533 * that command's source: a pointer to its first byte and the number of 07534 * characters. 07535 * 07536 * Results: 07537 * If a command is found that encloses the program counter value, a 07538 * pointer to the command's source is returned and the length of the 07539 * source is stored at *lengthPtr. If multiple commands resulted in code 07540 * at pc, information about the closest enclosing command is returned. If 07541 * no matching command is found, NULL is returned and *lengthPtr is 07542 * unchanged. 07543 * 07544 * Side effects: 07545 * The CmdFrame at *cfPtr is updated. 07546 * 07547 *---------------------------------------------------------------------- 07548 */ 07549 07550 const char * 07551 TclGetSrcInfoForCmd( 07552 Interp *iPtr, 07553 int *lenPtr) 07554 { 07555 CmdFrame *cfPtr = iPtr->cmdFramePtr; 07556 ByteCode *codePtr = (ByteCode *) cfPtr->data.tebc.codePtr; 07557 07558 return GetSrcInfoForPc((unsigned char *) cfPtr->data.tebc.pc, 07559 codePtr, lenPtr); 07560 } 07561 07562 void 07563 TclGetSrcInfoForPc( 07564 CmdFrame *cfPtr) 07565 { 07566 ByteCode *codePtr = (ByteCode *) cfPtr->data.tebc.codePtr; 07567 07568 if (cfPtr->cmd.str.cmd == NULL) { 07569 cfPtr->cmd.str.cmd = GetSrcInfoForPc( 07570 (unsigned char *) cfPtr->data.tebc.pc, codePtr, 07571 &cfPtr->cmd.str.len); 07572 } 07573 07574 if (cfPtr->cmd.str.cmd != NULL) { 07575 /* 07576 * We now have the command. We can get the srcOffset back and from 07577 * there find the list of word locations for this command. 07578 */ 07579 07580 ExtCmdLoc *eclPtr; 07581 ECL *locPtr = NULL; 07582 int srcOffset, i; 07583 Interp *iPtr = (Interp *) *codePtr->interpHandle; 07584 Tcl_HashEntry *hePtr = 07585 Tcl_FindHashEntry(iPtr->lineBCPtr, (char *) codePtr); 07586 07587 if (!hePtr) { 07588 return; 07589 } 07590 07591 srcOffset = cfPtr->cmd.str.cmd - codePtr->source; 07592 eclPtr = (ExtCmdLoc *) Tcl_GetHashValue (hePtr); 07593 07594 for (i=0; i < eclPtr->nuloc; i++) { 07595 if (eclPtr->loc[i].srcOffset == srcOffset) { 07596 locPtr = eclPtr->loc+i; 07597 break; 07598 } 07599 } 07600 if (locPtr == NULL) { 07601 Tcl_Panic("LocSearch failure"); 07602 } 07603 07604 cfPtr->line = locPtr->line; 07605 cfPtr->nline = locPtr->nline; 07606 cfPtr->type = eclPtr->type; 07607 07608 if (eclPtr->type == TCL_LOCATION_SOURCE) { 07609 cfPtr->data.eval.path = eclPtr->path; 07610 Tcl_IncrRefCount(cfPtr->data.eval.path); 07611 } 07612 07613 /* 07614 * Do not set cfPtr->data.eval.path NULL for non-SOURCE. Needed for 07615 * cfPtr->data.tebc.codePtr. 07616 */ 07617 } 07618 } 07619 07620 static const char * 07621 GetSrcInfoForPc( 07622 unsigned char *pc, /* The program counter value for which to 07623 * return the closest command's source info. 07624 * This points to a bytecode instruction in 07625 * codePtr's code. */ 07626 ByteCode *codePtr, /* The bytecode sequence in which to look up 07627 * the command source for the pc. */ 07628 int *lengthPtr) /* If non-NULL, the location where the length 07629 * of the command's source should be stored. 07630 * If NULL, no length is stored. */ 07631 { 07632 register int pcOffset = (pc - codePtr->codeStart); 07633 int numCmds = codePtr->numCommands; 07634 unsigned char *codeDeltaNext, *codeLengthNext; 07635 unsigned char *srcDeltaNext, *srcLengthNext; 07636 int codeOffset, codeLen, codeEnd, srcOffset, srcLen, delta, i; 07637 int bestDist = INT_MAX; /* Distance of pc to best cmd's start pc. */ 07638 int bestSrcOffset = -1; /* Initialized to avoid compiler warning. */ 07639 int bestSrcLength = -1; /* Initialized to avoid compiler warning. */ 07640 07641 if ((pcOffset < 0) || (pcOffset >= codePtr->numCodeBytes)) { 07642 return NULL; 07643 } 07644 07645 /* 07646 * Decode the code and source offset and length for each command. The 07647 * closest enclosing command is the last one whose code started before 07648 * pcOffset. 07649 */ 07650 07651 codeDeltaNext = codePtr->codeDeltaStart; 07652 codeLengthNext = codePtr->codeLengthStart; 07653 srcDeltaNext = codePtr->srcDeltaStart; 07654 srcLengthNext = codePtr->srcLengthStart; 07655 codeOffset = srcOffset = 0; 07656 for (i = 0; i < numCmds; i++) { 07657 if ((unsigned) *codeDeltaNext == (unsigned) 0xFF) { 07658 codeDeltaNext++; 07659 delta = TclGetInt4AtPtr(codeDeltaNext); 07660 codeDeltaNext += 4; 07661 } else { 07662 delta = TclGetInt1AtPtr(codeDeltaNext); 07663 codeDeltaNext++; 07664 } 07665 codeOffset += delta; 07666 07667 if ((unsigned) *codeLengthNext == (unsigned) 0xFF) { 07668 codeLengthNext++; 07669 codeLen = TclGetInt4AtPtr(codeLengthNext); 07670 codeLengthNext += 4; 07671 } else { 07672 codeLen = TclGetInt1AtPtr(codeLengthNext); 07673 codeLengthNext++; 07674 } 07675 codeEnd = (codeOffset + codeLen - 1); 07676 07677 if ((unsigned) *srcDeltaNext == (unsigned) 0xFF) { 07678 srcDeltaNext++; 07679 delta = TclGetInt4AtPtr(srcDeltaNext); 07680 srcDeltaNext += 4; 07681 } else { 07682 delta = TclGetInt1AtPtr(srcDeltaNext); 07683 srcDeltaNext++; 07684 } 07685 srcOffset += delta; 07686 07687 if ((unsigned) *srcLengthNext == (unsigned) 0xFF) { 07688 srcLengthNext++; 07689 srcLen = TclGetInt4AtPtr(srcLengthNext); 07690 srcLengthNext += 4; 07691 } else { 07692 srcLen = TclGetInt1AtPtr(srcLengthNext); 07693 srcLengthNext++; 07694 } 07695 07696 if (codeOffset > pcOffset) { /* Best cmd already found */ 07697 break; 07698 } 07699 if (pcOffset <= codeEnd) { /* This cmd's code encloses pc */ 07700 int dist = (pcOffset - codeOffset); 07701 07702 if (dist <= bestDist) { 07703 bestDist = dist; 07704 bestSrcOffset = srcOffset; 07705 bestSrcLength = srcLen; 07706 } 07707 } 07708 } 07709 07710 if (bestDist == INT_MAX) { 07711 return NULL; 07712 } 07713 07714 if (lengthPtr != NULL) { 07715 *lengthPtr = bestSrcLength; 07716 } 07717 return (codePtr->source + bestSrcOffset); 07718 } 07719 07720 /* 07721 *---------------------------------------------------------------------- 07722 * 07723 * GetExceptRangeForPc -- 07724 * 07725 * Given a program counter value, return the closest enclosing 07726 * ExceptionRange. 07727 * 07728 * Results: 07729 * In the normal case, catchOnly is 0 (false) and this procedure returns 07730 * a pointer to the most closely enclosing ExceptionRange structure 07731 * regardless of whether it is a loop or catch exception range. This is 07732 * appropriate when processing a TCL_BREAK or TCL_CONTINUE, which will be 07733 * "handled" either by a loop exception range or a closer catch range. If 07734 * catchOnly is nonzero, this procedure ignores loop exception ranges and 07735 * returns a pointer to the closest catch range. If no matching 07736 * ExceptionRange is found that encloses pc, a NULL is returned. 07737 * 07738 * Side effects: 07739 * None. 07740 * 07741 *---------------------------------------------------------------------- 07742 */ 07743 07744 static ExceptionRange * 07745 GetExceptRangeForPc( 07746 unsigned char *pc, /* The program counter value for which to 07747 * search for a closest enclosing exception 07748 * range. This points to a bytecode 07749 * instruction in codePtr's code. */ 07750 int catchOnly, /* If 0, consider either loop or catch 07751 * ExceptionRanges in search. If nonzero 07752 * consider only catch ranges (and ignore any 07753 * closer loop ranges). */ 07754 ByteCode *codePtr) /* Points to the ByteCode in which to search 07755 * for the enclosing ExceptionRange. */ 07756 { 07757 ExceptionRange *rangeArrayPtr; 07758 int numRanges = codePtr->numExceptRanges; 07759 register ExceptionRange *rangePtr; 07760 int pcOffset = pc - codePtr->codeStart; 07761 register int start; 07762 07763 if (numRanges == 0) { 07764 return NULL; 07765 } 07766 07767 /* 07768 * This exploits peculiarities of our compiler: nested ranges are always 07769 * *after* their containing ranges, so that by scanning backwards we are 07770 * sure that the first matching range is indeed the deepest. 07771 */ 07772 07773 rangeArrayPtr = codePtr->exceptArrayPtr; 07774 rangePtr = rangeArrayPtr + numRanges; 07775 while (--rangePtr >= rangeArrayPtr) { 07776 start = rangePtr->codeOffset; 07777 if ((start <= pcOffset) && 07778 (pcOffset < (start + rangePtr->numCodeBytes))) { 07779 if ((!catchOnly) 07780 || (rangePtr->type == CATCH_EXCEPTION_RANGE)) { 07781 return rangePtr; 07782 } 07783 } 07784 } 07785 return NULL; 07786 } 07787 07788 /* 07789 *---------------------------------------------------------------------- 07790 * 07791 * GetOpcodeName -- 07792 * 07793 * This procedure is called by the TRACE and TRACE_WITH_OBJ macros used 07794 * in TclExecuteByteCode when debugging. It returns the name of the 07795 * bytecode instruction at a specified instruction pc. 07796 * 07797 * Results: 07798 * A character string for the instruction. 07799 * 07800 * Side effects: 07801 * None. 07802 * 07803 *---------------------------------------------------------------------- 07804 */ 07805 07806 #ifdef TCL_COMPILE_DEBUG 07807 static char * 07808 GetOpcodeName( 07809 unsigned char *pc) /* Points to the instruction whose name should 07810 * be returned. */ 07811 { 07812 unsigned char opCode = *pc; 07813 07814 return tclInstructionTable[opCode].name; 07815 } 07816 #endif /* TCL_COMPILE_DEBUG */ 07817 07818 /* 07819 *---------------------------------------------------------------------- 07820 * 07821 * TclExprFloatError -- 07822 * 07823 * This procedure is called when an error occurs during a floating-point 07824 * operation. It reads errno and sets interp->objResultPtr accordingly. 07825 * 07826 * Results: 07827 * interp->objResultPtr is set to hold an error message. 07828 * 07829 * Side effects: 07830 * None. 07831 * 07832 *---------------------------------------------------------------------- 07833 */ 07834 07835 void 07836 TclExprFloatError( 07837 Tcl_Interp *interp, /* Where to store error message. */ 07838 double value) /* Value returned after error; used to 07839 * distinguish underflows from overflows. */ 07840 { 07841 const char *s; 07842 07843 if ((errno == EDOM) || TclIsNaN(value)) { 07844 s = "domain error: argument not in valid range"; 07845 Tcl_SetObjResult(interp, Tcl_NewStringObj(s, -1)); 07846 Tcl_SetErrorCode(interp, "ARITH", "DOMAIN", s, NULL); 07847 } else if ((errno == ERANGE) || TclIsInfinite(value)) { 07848 if (value == 0.0) { 07849 s = "floating-point value too small to represent"; 07850 Tcl_SetObjResult(interp, Tcl_NewStringObj(s, -1)); 07851 Tcl_SetErrorCode(interp, "ARITH", "UNDERFLOW", s, NULL); 07852 } else { 07853 s = "floating-point value too large to represent"; 07854 Tcl_SetObjResult(interp, Tcl_NewStringObj(s, -1)); 07855 Tcl_SetErrorCode(interp, "ARITH", "OVERFLOW", s, NULL); 07856 } 07857 } else { 07858 Tcl_Obj *objPtr = Tcl_ObjPrintf( 07859 "unknown floating-point error, errno = %d", errno); 07860 07861 Tcl_SetErrorCode(interp, "ARITH", "UNKNOWN", 07862 Tcl_GetString(objPtr), NULL); 07863 Tcl_SetObjResult(interp, objPtr); 07864 } 07865 } 07866 07867 #ifdef TCL_COMPILE_STATS 07868 /* 07869 *---------------------------------------------------------------------- 07870 * 07871 * TclLog2 -- 07872 * 07873 * Procedure used while collecting compilation statistics to determine 07874 * the log base 2 of an integer. 07875 * 07876 * Results: 07877 * Returns the log base 2 of the operand. If the argument is less than or 07878 * equal to zero, a zero is returned. 07879 * 07880 * Side effects: 07881 * None. 07882 * 07883 *---------------------------------------------------------------------- 07884 */ 07885 07886 int 07887 TclLog2( 07888 register int value) /* The integer for which to compute the log 07889 * base 2. */ 07890 { 07891 register int n = value; 07892 register int result = 0; 07893 07894 while (n > 1) { 07895 n = n >> 1; 07896 result++; 07897 } 07898 return result; 07899 } 07900 07901 /* 07902 *---------------------------------------------------------------------- 07903 * 07904 * EvalStatsCmd -- 07905 * 07906 * Implements the "evalstats" command that prints instruction execution 07907 * counts to stdout. 07908 * 07909 * Results: 07910 * Standard Tcl results. 07911 * 07912 * Side effects: 07913 * None. 07914 * 07915 *---------------------------------------------------------------------- 07916 */ 07917 07918 static int 07919 EvalStatsCmd( 07920 ClientData unused, /* Unused. */ 07921 Tcl_Interp *interp, /* The current interpreter. */ 07922 int objc, /* The number of arguments. */ 07923 Tcl_Obj *const objv[]) /* The argument strings. */ 07924 { 07925 Interp *iPtr = (Interp *) interp; 07926 LiteralTable *globalTablePtr = &iPtr->literalTable; 07927 ByteCodeStats *statsPtr = &iPtr->stats; 07928 double totalCodeBytes, currentCodeBytes; 07929 double totalLiteralBytes, currentLiteralBytes; 07930 double objBytesIfUnshared, strBytesIfUnshared, sharingBytesSaved; 07931 double strBytesSharedMultX, strBytesSharedOnce; 07932 double numInstructions, currentHeaderBytes; 07933 long numCurrentByteCodes, numByteCodeLits; 07934 long refCountSum, literalMgmtBytes, sum; 07935 int numSharedMultX, numSharedOnce; 07936 int decadeHigh, minSizeDecade, maxSizeDecade, length, i; 07937 char *litTableStats; 07938 LiteralEntry *entryPtr; 07939 07940 #define Percent(a,b) ((a) * 100.0 / (b)) 07941 07942 numInstructions = 0.0; 07943 for (i = 0; i < 256; i++) { 07944 if (statsPtr->instructionCount[i] != 0) { 07945 numInstructions += statsPtr->instructionCount[i]; 07946 } 07947 } 07948 07949 totalLiteralBytes = sizeof(LiteralTable) 07950 + iPtr->literalTable.numBuckets * sizeof(LiteralEntry *) 07951 + (statsPtr->numLiteralsCreated * sizeof(LiteralEntry)) 07952 + (statsPtr->numLiteralsCreated * sizeof(Tcl_Obj)) 07953 + statsPtr->totalLitStringBytes; 07954 totalCodeBytes = statsPtr->totalByteCodeBytes + totalLiteralBytes; 07955 07956 numCurrentByteCodes = 07957 statsPtr->numCompilations - statsPtr->numByteCodesFreed; 07958 currentHeaderBytes = numCurrentByteCodes 07959 * (sizeof(ByteCode) - sizeof(size_t) - sizeof(Tcl_Time)); 07960 literalMgmtBytes = sizeof(LiteralTable) 07961 + (iPtr->literalTable.numBuckets * sizeof(LiteralEntry *)) 07962 + (iPtr->literalTable.numEntries * sizeof(LiteralEntry)); 07963 currentLiteralBytes = literalMgmtBytes 07964 + iPtr->literalTable.numEntries * sizeof(Tcl_Obj) 07965 + statsPtr->currentLitStringBytes; 07966 currentCodeBytes = statsPtr->currentByteCodeBytes + currentLiteralBytes; 07967 07968 /* 07969 * Summary statistics, total and current source and ByteCode sizes. 07970 */ 07971 07972 fprintf(stdout, "\n----------------------------------------------------------------\n"); 07973 fprintf(stdout, 07974 "Compilation and execution statistics for interpreter 0x%p\n", 07975 iPtr); 07976 07977 fprintf(stdout, "\nNumber ByteCodes executed %ld\n", 07978 statsPtr->numExecutions); 07979 fprintf(stdout, "Number ByteCodes compiled %ld\n", 07980 statsPtr->numCompilations); 07981 fprintf(stdout, " Mean executions/compile %.1f\n", 07982 statsPtr->numExecutions / (float)statsPtr->numCompilations); 07983 07984 fprintf(stdout, "\nInstructions executed %.0f\n", 07985 numInstructions); 07986 fprintf(stdout, " Mean inst/compile %.0f\n", 07987 numInstructions / statsPtr->numCompilations); 07988 fprintf(stdout, " Mean inst/execution %.0f\n", 07989 numInstructions / statsPtr->numExecutions); 07990 07991 fprintf(stdout, "\nTotal ByteCodes %ld\n", 07992 statsPtr->numCompilations); 07993 fprintf(stdout, " Source bytes %.6g\n", 07994 statsPtr->totalSrcBytes); 07995 fprintf(stdout, " Code bytes %.6g\n", 07996 totalCodeBytes); 07997 fprintf(stdout, " ByteCode bytes %.6g\n", 07998 statsPtr->totalByteCodeBytes); 07999 fprintf(stdout, " Literal bytes %.6g\n", 08000 totalLiteralBytes); 08001 fprintf(stdout, " table %lu + bkts %lu + entries %lu + objects %lu + strings %.6g\n", 08002 (unsigned long) sizeof(LiteralTable), 08003 (unsigned long) (iPtr->literalTable.numBuckets * sizeof(LiteralEntry *)), 08004 (unsigned long) (statsPtr->numLiteralsCreated * sizeof(LiteralEntry)), 08005 (unsigned long) (statsPtr->numLiteralsCreated * sizeof(Tcl_Obj)), 08006 statsPtr->totalLitStringBytes); 08007 fprintf(stdout, " Mean code/compile %.1f\n", 08008 totalCodeBytes / statsPtr->numCompilations); 08009 fprintf(stdout, " Mean code/source %.1f\n", 08010 totalCodeBytes / statsPtr->totalSrcBytes); 08011 08012 fprintf(stdout, "\nCurrent (active) ByteCodes %ld\n", 08013 numCurrentByteCodes); 08014 fprintf(stdout, " Source bytes %.6g\n", 08015 statsPtr->currentSrcBytes); 08016 fprintf(stdout, " Code bytes %.6g\n", 08017 currentCodeBytes); 08018 fprintf(stdout, " ByteCode bytes %.6g\n", 08019 statsPtr->currentByteCodeBytes); 08020 fprintf(stdout, " Literal bytes %.6g\n", 08021 currentLiteralBytes); 08022 fprintf(stdout, " table %lu + bkts %lu + entries %lu + objects %lu + strings %.6g\n", 08023 (unsigned long) sizeof(LiteralTable), 08024 (unsigned long) (iPtr->literalTable.numBuckets * sizeof(LiteralEntry *)), 08025 (unsigned long) (iPtr->literalTable.numEntries * sizeof(LiteralEntry)), 08026 (unsigned long) (iPtr->literalTable.numEntries * sizeof(Tcl_Obj)), 08027 statsPtr->currentLitStringBytes); 08028 fprintf(stdout, " Mean code/source %.1f\n", 08029 currentCodeBytes / statsPtr->currentSrcBytes); 08030 fprintf(stdout, " Code + source bytes %.6g (%0.1f mean code/src)\n", 08031 (currentCodeBytes + statsPtr->currentSrcBytes), 08032 (currentCodeBytes / statsPtr->currentSrcBytes) + 1.0); 08033 08034 /* 08035 * Tcl_IsShared statistics check 08036 * 08037 * This gives the refcount of each obj as Tcl_IsShared was called for it. 08038 * Shared objects must be duplicated before they can be modified. 08039 */ 08040 08041 numSharedMultX = 0; 08042 fprintf(stdout, "\nTcl_IsShared object check (all objects):\n"); 08043 fprintf(stdout, " Object had refcount <=1 (not shared) %ld\n", 08044 tclObjsShared[1]); 08045 for (i = 2; i < TCL_MAX_SHARED_OBJ_STATS; i++) { 08046 fprintf(stdout, " refcount ==%d %ld\n", 08047 i, tclObjsShared[i]); 08048 numSharedMultX += tclObjsShared[i]; 08049 } 08050 fprintf(stdout, " refcount >=%d %ld\n", 08051 i, tclObjsShared[0]); 08052 numSharedMultX += tclObjsShared[0]; 08053 fprintf(stdout, " Total shared objects %d\n", 08054 numSharedMultX); 08055 08056 /* 08057 * Literal table statistics. 08058 */ 08059 08060 numByteCodeLits = 0; 08061 refCountSum = 0; 08062 numSharedMultX = 0; 08063 numSharedOnce = 0; 08064 objBytesIfUnshared = 0.0; 08065 strBytesIfUnshared = 0.0; 08066 strBytesSharedMultX = 0.0; 08067 strBytesSharedOnce = 0.0; 08068 for (i = 0; i < globalTablePtr->numBuckets; i++) { 08069 for (entryPtr = globalTablePtr->buckets[i]; entryPtr != NULL; 08070 entryPtr = entryPtr->nextPtr) { 08071 if (entryPtr->objPtr->typePtr == &tclByteCodeType) { 08072 numByteCodeLits++; 08073 } 08074 (void) Tcl_GetStringFromObj(entryPtr->objPtr, &length); 08075 refCountSum += entryPtr->refCount; 08076 objBytesIfUnshared += (entryPtr->refCount * sizeof(Tcl_Obj)); 08077 strBytesIfUnshared += (entryPtr->refCount * (length+1)); 08078 if (entryPtr->refCount > 1) { 08079 numSharedMultX++; 08080 strBytesSharedMultX += (length+1); 08081 } else { 08082 numSharedOnce++; 08083 strBytesSharedOnce += (length+1); 08084 } 08085 } 08086 } 08087 sharingBytesSaved = (objBytesIfUnshared + strBytesIfUnshared) 08088 - currentLiteralBytes; 08089 08090 fprintf(stdout, "\nTotal objects (all interps) %ld\n", 08091 tclObjsAlloced); 08092 fprintf(stdout, "Current objects %ld\n", 08093 (tclObjsAlloced - tclObjsFreed)); 08094 fprintf(stdout, "Total literal objects %ld\n", 08095 statsPtr->numLiteralsCreated); 08096 08097 fprintf(stdout, "\nCurrent literal objects %d (%0.1f%% of current objects)\n", 08098 globalTablePtr->numEntries, 08099 Percent(globalTablePtr->numEntries, tclObjsAlloced-tclObjsFreed)); 08100 fprintf(stdout, " ByteCode literals %ld (%0.1f%% of current literals)\n", 08101 numByteCodeLits, 08102 Percent(numByteCodeLits, globalTablePtr->numEntries)); 08103 fprintf(stdout, " Literals reused > 1x %d\n", 08104 numSharedMultX); 08105 fprintf(stdout, " Mean reference count %.2f\n", 08106 ((double) refCountSum) / globalTablePtr->numEntries); 08107 fprintf(stdout, " Mean len, str reused >1x %.2f\n", 08108 (numSharedMultX ? strBytesSharedMultX/numSharedMultX : 0.0)); 08109 fprintf(stdout, " Mean len, str used 1x %.2f\n", 08110 (numSharedOnce ? strBytesSharedOnce/numSharedOnce : 0.0)); 08111 fprintf(stdout, " Total sharing savings %.6g (%0.1f%% of bytes if no sharing)\n", 08112 sharingBytesSaved, 08113 Percent(sharingBytesSaved, objBytesIfUnshared+strBytesIfUnshared)); 08114 fprintf(stdout, " Bytes with sharing %.6g\n", 08115 currentLiteralBytes); 08116 fprintf(stdout, " table %lu + bkts %lu + entries %lu + objects %lu + strings %.6g\n", 08117 (unsigned long) sizeof(LiteralTable), 08118 (unsigned long) (iPtr->literalTable.numBuckets * sizeof(LiteralEntry *)), 08119 (unsigned long) (iPtr->literalTable.numEntries * sizeof(LiteralEntry)), 08120 (unsigned long) (iPtr->literalTable.numEntries * sizeof(Tcl_Obj)), 08121 statsPtr->currentLitStringBytes); 08122 fprintf(stdout, " Bytes if no sharing %.6g = objects %.6g + strings %.6g\n", 08123 (objBytesIfUnshared + strBytesIfUnshared), 08124 objBytesIfUnshared, strBytesIfUnshared); 08125 fprintf(stdout, " String sharing savings %.6g = unshared %.6g - shared %.6g\n", 08126 (strBytesIfUnshared - statsPtr->currentLitStringBytes), 08127 strBytesIfUnshared, statsPtr->currentLitStringBytes); 08128 fprintf(stdout, " Literal mgmt overhead %ld (%0.1f%% of bytes with sharing)\n", 08129 literalMgmtBytes, 08130 Percent(literalMgmtBytes, currentLiteralBytes)); 08131 fprintf(stdout, " table %lu + buckets %lu + entries %lu\n", 08132 (unsigned long) sizeof(LiteralTable), 08133 (unsigned long) (iPtr->literalTable.numBuckets * sizeof(LiteralEntry *)), 08134 (unsigned long) (iPtr->literalTable.numEntries * sizeof(LiteralEntry))); 08135 08136 /* 08137 * Breakdown of current ByteCode space requirements. 08138 */ 08139 08140 fprintf(stdout, "\nBreakdown of current ByteCode requirements:\n"); 08141 fprintf(stdout, " Bytes Pct of Avg per\n"); 08142 fprintf(stdout, " total ByteCode\n"); 08143 fprintf(stdout, "Total %12.6g 100.00%% %8.1f\n", 08144 statsPtr->currentByteCodeBytes, 08145 statsPtr->currentByteCodeBytes / numCurrentByteCodes); 08146 fprintf(stdout, "Header %12.6g %8.1f%% %8.1f\n", 08147 currentHeaderBytes, 08148 Percent(currentHeaderBytes, statsPtr->currentByteCodeBytes), 08149 currentHeaderBytes / numCurrentByteCodes); 08150 fprintf(stdout, "Instructions %12.6g %8.1f%% %8.1f\n", 08151 statsPtr->currentInstBytes, 08152 Percent(statsPtr->currentInstBytes,statsPtr->currentByteCodeBytes), 08153 statsPtr->currentInstBytes / numCurrentByteCodes); 08154 fprintf(stdout, "Literal ptr array %12.6g %8.1f%% %8.1f\n", 08155 statsPtr->currentLitBytes, 08156 Percent(statsPtr->currentLitBytes,statsPtr->currentByteCodeBytes), 08157 statsPtr->currentLitBytes / numCurrentByteCodes); 08158 fprintf(stdout, "Exception table %12.6g %8.1f%% %8.1f\n", 08159 statsPtr->currentExceptBytes, 08160 Percent(statsPtr->currentExceptBytes,statsPtr->currentByteCodeBytes), 08161 statsPtr->currentExceptBytes / numCurrentByteCodes); 08162 fprintf(stdout, "Auxiliary data %12.6g %8.1f%% %8.1f\n", 08163 statsPtr->currentAuxBytes, 08164 Percent(statsPtr->currentAuxBytes,statsPtr->currentByteCodeBytes), 08165 statsPtr->currentAuxBytes / numCurrentByteCodes); 08166 fprintf(stdout, "Command map %12.6g %8.1f%% %8.1f\n", 08167 statsPtr->currentCmdMapBytes, 08168 Percent(statsPtr->currentCmdMapBytes,statsPtr->currentByteCodeBytes), 08169 statsPtr->currentCmdMapBytes / numCurrentByteCodes); 08170 08171 /* 08172 * Detailed literal statistics. 08173 */ 08174 08175 fprintf(stdout, "\nLiteral string sizes:\n"); 08176 fprintf(stdout, " Up to length Percentage\n"); 08177 maxSizeDecade = 0; 08178 for (i = 31; i >= 0; i--) { 08179 if (statsPtr->literalCount[i] > 0) { 08180 maxSizeDecade = i; 08181 break; 08182 } 08183 } 08184 sum = 0; 08185 for (i = 0; i <= maxSizeDecade; i++) { 08186 decadeHigh = (1 << (i+1)) - 1; 08187 sum += statsPtr->literalCount[i]; 08188 fprintf(stdout, " %10d %8.0f%%\n", 08189 decadeHigh, Percent(sum, statsPtr->numLiteralsCreated)); 08190 } 08191 08192 litTableStats = TclLiteralStats(globalTablePtr); 08193 fprintf(stdout, "\nCurrent literal table statistics:\n%s\n", 08194 litTableStats); 08195 ckfree((char *) litTableStats); 08196 08197 /* 08198 * Source and ByteCode size distributions. 08199 */ 08200 08201 fprintf(stdout, "\nSource sizes:\n"); 08202 fprintf(stdout, " Up to size Percentage\n"); 08203 minSizeDecade = maxSizeDecade = 0; 08204 for (i = 0; i < 31; i++) { 08205 if (statsPtr->srcCount[i] > 0) { 08206 minSizeDecade = i; 08207 break; 08208 } 08209 } 08210 for (i = 31; i >= 0; i--) { 08211 if (statsPtr->srcCount[i] > 0) { 08212 maxSizeDecade = i; 08213 break; 08214 } 08215 } 08216 sum = 0; 08217 for (i = minSizeDecade; i <= maxSizeDecade; i++) { 08218 decadeHigh = (1 << (i+1)) - 1; 08219 sum += statsPtr->srcCount[i]; 08220 fprintf(stdout, " %10d %8.0f%%\n", 08221 decadeHigh, Percent(sum, statsPtr->numCompilations)); 08222 } 08223 08224 fprintf(stdout, "\nByteCode sizes:\n"); 08225 fprintf(stdout, " Up to size Percentage\n"); 08226 minSizeDecade = maxSizeDecade = 0; 08227 for (i = 0; i < 31; i++) { 08228 if (statsPtr->byteCodeCount[i] > 0) { 08229 minSizeDecade = i; 08230 break; 08231 } 08232 } 08233 for (i = 31; i >= 0; i--) { 08234 if (statsPtr->byteCodeCount[i] > 0) { 08235 maxSizeDecade = i; 08236 break; 08237 } 08238 } 08239 sum = 0; 08240 for (i = minSizeDecade; i <= maxSizeDecade; i++) { 08241 decadeHigh = (1 << (i+1)) - 1; 08242 sum += statsPtr->byteCodeCount[i]; 08243 fprintf(stdout, " %10d %8.0f%%\n", 08244 decadeHigh, Percent(sum, statsPtr->numCompilations)); 08245 } 08246 08247 fprintf(stdout, "\nByteCode longevity (excludes Current ByteCodes):\n"); 08248 fprintf(stdout, " Up to ms Percentage\n"); 08249 minSizeDecade = maxSizeDecade = 0; 08250 for (i = 0; i < 31; i++) { 08251 if (statsPtr->lifetimeCount[i] > 0) { 08252 minSizeDecade = i; 08253 break; 08254 } 08255 } 08256 for (i = 31; i >= 0; i--) { 08257 if (statsPtr->lifetimeCount[i] > 0) { 08258 maxSizeDecade = i; 08259 break; 08260 } 08261 } 08262 sum = 0; 08263 for (i = minSizeDecade; i <= maxSizeDecade; i++) { 08264 decadeHigh = (1 << (i+1)) - 1; 08265 sum += statsPtr->lifetimeCount[i]; 08266 fprintf(stdout, " %12.3f %8.0f%%\n", 08267 decadeHigh/1000.0, Percent(sum, statsPtr->numByteCodesFreed)); 08268 } 08269 08270 /* 08271 * Instruction counts. 08272 */ 08273 08274 fprintf(stdout, "\nInstruction counts:\n"); 08275 for (i = 0; i <= LAST_INST_OPCODE; i++) { 08276 if (statsPtr->instructionCount[i] == 0) { 08277 fprintf(stdout, "%20s %8ld %6.1f%%\n", 08278 tclInstructionTable[i].name, 08279 statsPtr->instructionCount[i], 08280 Percent(statsPtr->instructionCount[i], numInstructions)); 08281 } 08282 } 08283 08284 fprintf(stdout, "\nInstructions NEVER executed:\n"); 08285 for (i = 0; i <= LAST_INST_OPCODE; i++) { 08286 if (statsPtr->instructionCount[i] == 0) { 08287 fprintf(stdout, "%20s\n", tclInstructionTable[i].name); 08288 } 08289 } 08290 08291 #ifdef TCL_MEM_DEBUG 08292 fprintf(stdout, "\nHeap Statistics:\n"); 08293 TclDumpMemoryInfo(stdout); 08294 #endif 08295 fprintf(stdout, "\n----------------------------------------------------------------\n"); 08296 return TCL_OK; 08297 } 08298 #endif /* TCL_COMPILE_STATS */ 08299 08300 #ifdef TCL_COMPILE_DEBUG 08301 /* 08302 *---------------------------------------------------------------------- 08303 * 08304 * StringForResultCode -- 08305 * 08306 * Procedure that returns a human-readable string representing a Tcl 08307 * result code such as TCL_ERROR. 08308 * 08309 * Results: 08310 * If the result code is one of the standard Tcl return codes, the result 08311 * is a string representing that code such as "TCL_ERROR". Otherwise, the 08312 * result string is that code formatted as a sequence of decimal digit 08313 * characters. Note that the resulting string must not be modified by the 08314 * caller. 08315 * 08316 * Side effects: 08317 * None. 08318 * 08319 *---------------------------------------------------------------------- 08320 */ 08321 08322 static const char * 08323 StringForResultCode( 08324 int result) /* The Tcl result code for which to generate a 08325 * string. */ 08326 { 08327 static char buf[TCL_INTEGER_SPACE]; 08328 08329 if ((result >= TCL_OK) && (result <= TCL_CONTINUE)) { 08330 return resultStrings[result]; 08331 } 08332 TclFormatInt(buf, result); 08333 return buf; 08334 } 08335 #endif /* TCL_COMPILE_DEBUG */ 08336 08337 /* 08338 * Local Variables: 08339 * mode: c 08340 * c-basic-offset: 4 08341 * fill-column: 78 08342 * End: 08343 */
Generated on Wed Mar 12 12:18:16 2008 by 1.5.1 |