diff options
author | dgp <dgp@users.sourceforge.net> | 2005-10-08 14:42:44 (GMT) |
---|---|---|
committer | dgp <dgp@users.sourceforge.net> | 2005-10-08 14:42:44 (GMT) |
commit | 76faac0f28fe9661f23ff9e35f44df1d899420e5 (patch) | |
tree | 7e3de1d0523d70328cfd81d9864b897058823d34 /generic/tclExecute.c | |
parent | 98a6fcad96289a40b501fbd2095387a245fd804d (diff) | |
download | tcl-76faac0f28fe9661f23ff9e35f44df1d899420e5.zip tcl-76faac0f28fe9661f23ff9e35f44df1d899420e5.tar.gz tcl-76faac0f28fe9661f23ff9e35f44df1d899420e5.tar.bz2 |
TIP#237 IMPLEMENTATION
[kennykb-numerics-branch] Resynchronized with the HEAD; at this
checkpoint [-rkennykb-numerics-branch-20051008], the HEAD and
kennykb-numerics-branch contain identical code.
Diffstat (limited to 'generic/tclExecute.c')
-rw-r--r-- | generic/tclExecute.c | 2665 |
1 files changed, 1656 insertions, 1009 deletions
diff --git a/generic/tclExecute.c b/generic/tclExecute.c index c7502f0..33e5ae2 100644 --- a/generic/tclExecute.c +++ b/generic/tclExecute.c @@ -12,11 +12,12 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclExecute.c,v 1.201 2005/09/15 16:40:02 dgp Exp $ + * RCS: @(#) $Id: tclExecute.c,v 1.202 2005/10/08 14:42:45 dgp Exp $ */ #include "tclInt.h" #include "tclCompile.h" +#include "tommath.h" #include <math.h> #include <float.h> @@ -48,26 +49,13 @@ # define NO_ERRNO_H #endif /* !TCL_GENERIC_ONLY */ +#if 0 #ifdef NO_ERRNO_H int errno; # define EDOM 33 # define ERANGE 34 #endif - -/* - * Need DBL_MAX for IS_INF() macro... - */ -#ifndef DBL_MAX -# ifdef MAXDOUBLE -# define DBL_MAX MAXDOUBLE -# else /* !MAXDOUBLE */ -/* - * This value is from the Solaris headers, but doubles seem to be the same - * size everywhere. Long doubles aren't, but we don't use those. - */ -# define DBL_MAX 1.79769313486231570e+308 -# endif /* MAXDOUBLE */ -#endif /* !DBL_MAX */ +#endif /* * A mask (should be 2**n-1) that is used to work out when the bytecode engine @@ -141,20 +129,6 @@ long tclObjsShared[TCL_MAX_SHARED_OBJ_STATS] = { 0, 0, 0, 0, 0 }; #endif /* TCL_COMPILE_STATS */ /* - * Macros for testing floating-point values for certain special cases. Test - * for not-a-number by comparing a value against itself; test for infinity by - * comparing against the largest floating-point value. - */ - -#ifdef _MSC_VER -#define IS_NAN(f) (_isnan((f))) -#define IS_INF(f) ( ! (_finite((f)))) -#else -#define IS_NAN(f) ((f) != (f)) -#define IS_INF(f) ( (f) > DBL_MAX || (f) < -DBL_MAX ) -#endif - -/* * The new macro for ending an instruction; note that a reasonable C-optimiser * will resolve all branches at compile time. (result) is always a constant; * the macro NEXT_INST_F handles constant (nCleanup), NEXT_INST_V is resolved @@ -286,6 +260,7 @@ long tclObjsShared[TCL_MAX_SHARED_OBJ_STATS] = { 0, 0, 0, 0, 0 }; # define O2S(objPtr) #endif /* TCL_COMPILE_DEBUG */ +#if 0 /* * Macro to read a string containing either a wide or an int and decide which * it is while decoding it at the same time. This enforces the policy that @@ -295,6 +270,7 @@ long tclObjsShared[TCL_MAX_SHARED_OBJ_STATS] = { 0, 0, 0, 0, 0 }; * * GET_WIDE_OR_INT is the same as REQUIRE_WIDE_OR_INT except it never * generates an error message. + * */ #define REQUIRE_WIDE_OR_INT(resultVar, objPtr, longVar, wideVar) \ (resultVar) = Tcl_GetWideIntFromObj(interp, (objPtr), &(wideVar)); \ @@ -313,15 +289,17 @@ long tclObjsShared[TCL_MAX_SHARED_OBJ_STATS] = { 0, 0, 0, 0, 0 }; (objPtr)->internalRep.longValue = (longVar) \ = Tcl_WideAsLong(wideVar); \ } +#endif /* * Combined with REQUIRE_WIDE_OR_INT, this gets a long value from an obj. */ +#if 0 #define FORCE_LONG(objPtr, longVar, wideVar) \ if ((objPtr)->typePtr == &tclWideIntType) { \ (longVar) = Tcl_WideAsLong(wideVar); \ } #define IS_INTEGER_TYPE(typePtr) \ - ((typePtr) == &tclIntType || (typePtr) == &tclWideIntType) + ((typePtr) == &tclIntType || (typePtr) == &tclWideIntType || (typePtr) == &tclBignumType) #define IS_NUMERIC_TYPE(typePtr) \ (IS_INTEGER_TYPE(typePtr) || (typePtr) == &tclDoubleType) @@ -351,6 +329,89 @@ long tclObjsShared[TCL_MAX_SHARED_OBJ_STATS] = { 0, 0, 0, 0, 0 }; (doubleVar) = (objPtr)->internalRep.doubleValue; \ } #endif /* TCL_WIDE_INT_IS_LONG */ +#endif + +/* + * Macro used in this file to save a function call for common uses of + * TclGetNumberFromObj(). The ANSI C "prototype" is: + * + * MODULE_SCOPE int GetNumberFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, + * ClientData *ptrPtr, int *tPtr); + */ + +#ifdef TCL_WIDE_INT_IS_LONG + +#define GetNumberFromObj(interp, objPtr, ptrPtr, tPtr) \ + (((objPtr)->typePtr == &tclIntType) \ + ? (*(tPtr) = TCL_NUMBER_LONG, \ + *(ptrPtr) = (ClientData) \ + (&((objPtr)->internalRep.longValue)), TCL_OK) : \ + ((objPtr)->typePtr == &tclDoubleType) \ + ? (((TclIsNaN((objPtr)->internalRep.doubleValue)) \ + ? (*(tPtr) = TCL_NUMBER_NAN) \ + : (*(tPtr) = TCL_NUMBER_DOUBLE)), \ + *(ptrPtr) = (ClientData) \ + (&((objPtr)->internalRep.doubleValue)), TCL_OK) : \ + TclGetNumberFromObj((interp), (objPtr), (ptrPtr), (tPtr))) + +#else + +#define GetNumberFromObj(interp, objPtr, ptrPtr, tPtr) \ + (((objPtr)->typePtr == &tclIntType) \ + ? (*(tPtr) = TCL_NUMBER_LONG, \ + *(ptrPtr) = (ClientData) \ + (&((objPtr)->internalRep.longValue)), TCL_OK) : \ + ((objPtr)->typePtr == &tclWideIntType) \ + ? (*(tPtr) = TCL_NUMBER_WIDE, \ + *(ptrPtr) = (ClientData) \ + (&((objPtr)->internalRep.wideValue)), TCL_OK) : \ + ((objPtr)->typePtr == &tclDoubleType) \ + ? (((TclIsNaN((objPtr)->internalRep.doubleValue)) \ + ? (*(tPtr) = TCL_NUMBER_NAN) \ + : (*(tPtr) = TCL_NUMBER_DOUBLE)), \ + *(ptrPtr) = (ClientData) \ + (&((objPtr)->internalRep.doubleValue)), TCL_OK) : \ + TclGetNumberFromObj((interp), (objPtr), (ptrPtr), (tPtr))) + +#endif + +/* + * Macro used in this file to save a function call for common uses of + * Tcl_GetBooleanFromObj(). The ANSI C "prototype" is: + * + * MODULE_SCOPE int TclGetBooleanFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, + * int *boolPtr); + */ + +#define TclGetBooleanFromObj(interp, objPtr, boolPtr) \ + ((((objPtr)->typePtr == &tclIntType) \ + || ((objPtr)->typePtr == &tclIntType)) \ + ? (*(boolPtr) = ((objPtr)->internalRep.longValue!=0), TCL_OK) \ + : Tcl_GetBooleanFromObj((interp), (objPtr), (boolPtr))) + +/* + * Macro used in this file to save a function call for common uses of + * Tcl_GetWideIntFromObj(). The ANSI C "prototype" is: + * + * MODULE_SCOPE int TclGetWideIntFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, + * Tcl_WideInt *wideIntPtr); + */ + +#ifdef TCL_WIDE_INT_IS_LONG +#define TclGetWideIntFromObj(interp, objPtr, wideIntPtr) \ + (((objPtr)->typePtr == &tclIntType) \ + ? (*(wideIntPtr) = (Tcl_WideInt) \ + ((objPtr)->internalRep.longValue), TCL_OK) : \ + Tcl_GetWideIntFromObj((interp), (objPtr), (wideIntPtr))) +#else +#define TclGetWideIntFromObj(interp, objPtr, wideIntPtr) \ + (((objPtr)->typePtr == &tclWideIntType) \ + ? (*(wideIntPtr) = (objPtr)->internalRep.wideValue, TCL_OK) : \ + ((objPtr)->typePtr == &tclIntType) \ + ? (*(wideIntPtr) = (Tcl_WideInt) \ + ((objPtr)->internalRep.longValue), TCL_OK) : \ + Tcl_GetWideIntFromObj((interp), (objPtr), (wideIntPtr))) +#endif static Tcl_ObjType dictIteratorType = { "dictIterator", @@ -389,10 +450,12 @@ static void ValidatePcAndStackTop _ANSI_ARGS_(( int stackTop, int stackLowerBound, int checkStack)); #endif /* TCL_COMPILE_DEBUG */ +#if 0 static Tcl_WideInt ExponWide _ANSI_ARGS_((Tcl_WideInt w, Tcl_WideInt w2, int *errExpon)); static long ExponLong _ANSI_ARGS_((long i, long i2, int *errExpon)); +#endif /* @@ -481,9 +544,9 @@ TclCreateExecEnv(interp) eePtr->tosPtr = stackPtr - 1; eePtr->endPtr = stackPtr + (TCL_STACK_INITIAL_SIZE - 2); - TclNewIntObj(eePtr->constants[0], 0); + TclNewBooleanObj(eePtr->constants[0], 0); Tcl_IncrRefCount(eePtr->constants[0]); - TclNewIntObj(eePtr->constants[1], 1); + TclNewBooleanObj(eePtr->constants[1], 1); Tcl_IncrRefCount(eePtr->constants[1]); Tcl_MutexLock(&execMutex); @@ -753,24 +816,24 @@ Tcl_ExprObj(interp, objPtr, resultPtrPtr) string = Tcl_GetStringFromObj(objPtr, &length); if (length == 1) { if (*string == '0') { - TclNewLongObj(resultPtr, 0); + TclNewBooleanObj(resultPtr, 0); Tcl_IncrRefCount(resultPtr); *resultPtrPtr = resultPtr; return TCL_OK; } else if (*string == '1') { - TclNewLongObj(resultPtr, 1); + TclNewBooleanObj(resultPtr, 1); Tcl_IncrRefCount(resultPtr); *resultPtrPtr = resultPtr; return TCL_OK; } } else if ((length == 2) && (*string == '!')) { if (*(string+1) == '0') { - TclNewLongObj(resultPtr, 1); + TclNewBooleanObj(resultPtr, 1); Tcl_IncrRefCount(resultPtr); *resultPtrPtr = resultPtr; return TCL_OK; } else if (*(string+1) == '1') { - TclNewLongObj(resultPtr, 0); + TclNewBooleanObj(resultPtr, 0); Tcl_IncrRefCount(resultPtr); *resultPtrPtr = resultPtr; return TCL_OK; @@ -1031,6 +1094,79 @@ TclCompEvalObj(interp, objPtr) /* *---------------------------------------------------------------------- * + * TclIncrObj -- + * + * Increment an integeral value in a Tcl_Obj by an integeral value + * held in another Tcl_Obj. Caller is responsible for making sure + * we can update the first object. + * + * Results: + * TCL_ERROR if either object is non-integer, and TCL_OK otherwise. On + * error, an error message is left in the interpreter (if it is not NULL, + * of course). + * + * Side effects: + * valuePtr gets the new incrmented value. + * + *---------------------------------------------------------------------- + */ + +int +TclIncrObj(interp, valuePtr, incrPtr) + Tcl_Interp *interp; + Tcl_Obj *valuePtr; + Tcl_Obj *incrPtr; +{ + ClientData ptr1, ptr2; + int type1, type2; + mp_int value, incr; + + if (Tcl_IsShared(valuePtr)) { + Tcl_Panic("shared object passed to TclIncrObj"); + } + + if ((GetNumberFromObj(NULL, valuePtr, &ptr1, &type1) != TCL_OK) + || (type1 == TCL_NUMBER_DOUBLE) || (type1 == TCL_NUMBER_NAN)) { + /* Produce error message (reparse?!) */ + return Tcl_GetIntFromObj(interp, valuePtr, &type1); + } + if ((GetNumberFromObj(NULL, incrPtr, &ptr2, &type2) != TCL_OK) + || (type1 == TCL_NUMBER_DOUBLE) || (type1 == TCL_NUMBER_NAN)) { + /* Produce error message (reparse?!) */ + Tcl_GetIntFromObj(interp, incrPtr, &type1); + Tcl_AddErrorInfo(interp, "\n (reading increment)"); + return TCL_ERROR; + } + do {if ((type1 != TCL_NUMBER_BIG) && (type2 != TCL_NUMBER_BIG)) { + Tcl_WideInt w1, w2, sum; + TclGetWideIntFromObj(NULL, valuePtr, &w1); + TclGetWideIntFromObj(NULL, incrPtr, &w2); + sum = w1 + w2; +#ifndef NO_WIDE_TYPE + if ((type1 == TCL_NUMBER_WIDE) || (type2 == TCL_NUMBER_WIDE)) +#endif + { + /* Check for overflow */ + if (((w1 < 0) && (w2 < 0) && (sum > 0)) + || ((w1 > 0) && (w2 > 0) && (sum < 0))) { + break; + } + } + Tcl_SetWideIntObj(valuePtr, sum); + return TCL_OK; + }} while (0); + + Tcl_GetBignumAndClearObj(interp, valuePtr, &value); + Tcl_GetBignumFromObj(interp, incrPtr, &incr); + mp_add(&value, &incr, &value); + mp_clear(&incr); + Tcl_SetBignumObj(valuePtr, &value); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * * TclExecuteByteCode -- * * This procedure executes the instructions of a ByteCode structure. It @@ -2215,11 +2351,16 @@ TclExecuteByteCode(interp, codePtr) * common execution code. */ +/*TODO: Consider more untangling here; merge with LOAD and STORE ? */ + { - Tcl_Obj *objPtr; - int opnd, pcAdjustment, isWide; - long i; + Tcl_Obj *objPtr, *incrPtr; + int opnd, pcAdjustment; +#if 0 + int isWide; Tcl_WideInt w; +#endif + long i; char *part1, *part2; Var *varPtr, *arrayPtr; @@ -2229,6 +2370,7 @@ TclExecuteByteCode(interp, codePtr) case INST_INCR_SCALAR_STK: case INST_INCR_STK: opnd = TclGetUInt1AtPtr(pc+1); +#if 0 objPtr = *tosPtr; if (objPtr->typePtr == &tclIntType) { i = objPtr->internalRep.longValue; @@ -2250,6 +2392,10 @@ TclExecuteByteCode(interp, codePtr) } tosPtr--; TclDecrRefCount(objPtr); +#else + incrPtr = *tosPtr; + tosPtr--; +#endif switch (*pc) { case INST_INCR_SCALAR1: pcAdjustment = 2; @@ -2266,7 +2412,12 @@ TclExecuteByteCode(interp, codePtr) case INST_INCR_SCALAR_STK_IMM: case INST_INCR_STK_IMM: i = TclGetInt1AtPtr(pc+1); +#if 0 isWide = 0; +#else + incrPtr = Tcl_NewIntObj(i); + Tcl_IncrRefCount(incrPtr); +#endif pcAdjustment = 2; doIncrStk: @@ -2290,6 +2441,7 @@ TclExecuteByteCode(interp, codePtr) "\n (reading value of variable to increment)", -1); TRACE_APPEND(("ERROR: %.30s\n", O2S(Tcl_GetObjResult(interp)))); result = TCL_ERROR; + Tcl_DecrRefCount(incrPtr); goto checkForCatch; } cleanup = ((part2 == NULL)? 1 : 2); @@ -2298,7 +2450,12 @@ TclExecuteByteCode(interp, codePtr) case INST_INCR_ARRAY1_IMM: opnd = TclGetUInt1AtPtr(pc+1); i = TclGetInt1AtPtr(pc+2); +#if 0 isWide = 0; +#else + incrPtr = Tcl_NewIntObj(i); + Tcl_IncrRefCount(incrPtr); +#endif pcAdjustment = 3; doIncrArray: @@ -2314,6 +2471,7 @@ TclExecuteByteCode(interp, codePtr) if (varPtr == NULL) { TRACE_APPEND(("ERROR: %.30s\n", O2S(Tcl_GetObjResult(interp)))); result = TCL_ERROR; + Tcl_DecrRefCount(incrPtr); goto checkForCatch; } cleanup = 1; @@ -2322,7 +2480,12 @@ TclExecuteByteCode(interp, codePtr) case INST_INCR_SCALAR1_IMM: opnd = TclGetUInt1AtPtr(pc+1); i = TclGetInt1AtPtr(pc+2); +#if 0 isWide = 0; +#else + incrPtr = Tcl_NewIntObj(i); + Tcl_IncrRefCount(incrPtr); +#endif pcAdjustment = 3; doIncrScalar: @@ -2337,6 +2500,7 @@ TclExecuteByteCode(interp, codePtr) TRACE(("%u %ld => ", opnd, i)); doIncrVar: +#if 0 objPtr = varPtr->value.objPtr; if (TclIsVarDirectReadable(varPtr) && ((arrayPtr == NULL) || TclIsVarUntraced(arrayPtr))) { @@ -2385,12 +2549,22 @@ TclExecuteByteCode(interp, codePtr) part2, i, TCL_LEAVE_ERR_MSG); } CACHE_STACK_INFO(); +#else + /* TODO: Restore no trace optimization */ + DECACHE_STACK_INFO(); + objResultPtr = TclPtrIncrObjVar(interp, varPtr, arrayPtr, part1, part2, + incrPtr, TCL_LEAVE_ERR_MSG); + CACHE_STACK_INFO(); + Tcl_DecrRefCount(incrPtr); +#endif if (objResultPtr == NULL) { TRACE_APPEND(("ERROR: %.30s\n", O2S(Tcl_GetObjResult(interp)))); result = TCL_ERROR; goto checkForCatch; } +#if 0 doneIncr: +#endif TRACE_APPEND(("%.30s\n", O2S(objResultPtr))); #ifndef TCL_COMPILE_DEBUG if (*(pc+pcAdjustment) == INST_POP) { @@ -2430,6 +2604,8 @@ TclExecuteByteCode(interp, codePtr) int b; Tcl_Obj *valuePtr; +/* TODO: consider rewrite so we don't compute the offset we're + * not going to take. */ case INST_JUMP_FALSE4: jmpOffset[0] = TclGetInt4AtPtr(pc+1); /* FALSE offset */ jmpOffset[1] = 5; /* TRUE offset*/ @@ -2452,35 +2628,17 @@ TclExecuteByteCode(interp, codePtr) doCondJump: valuePtr = *tosPtr; - if (valuePtr->typePtr == &tclIntType) { - b = (valuePtr->internalRep.longValue != 0); - } else if (valuePtr->typePtr == &tclDoubleType) { - b = (valuePtr->internalRep.doubleValue != 0.0); - } else if (valuePtr->typePtr == &tclWideIntType) { - Tcl_WideInt w; - - TclGetWide(w,valuePtr); - b = (w != W0); - } else { - /* - * Taking b's address impedes it being a register variable (in gcc - * at least), so we avoid doing it. - */ - int b1; - result = Tcl_GetBooleanFromObj(interp, valuePtr, &b1); - if (result != TCL_OK) { - if ((*pc == INST_JUMP_FALSE1) || (*pc == INST_JUMP_FALSE4)) { - jmpOffset[1] = jmpOffset[0]; - } - TRACE_WITH_OBJ(("%d => ERROR: ", jmpOffset[1]), - Tcl_GetObjResult(interp)); - goto checkForCatch; - } - b = b1; + /* TODO - check claim that taking address of b harms performance */ + /* TODO - consider optimization search for eePtr->constants */ + result = TclGetBooleanFromObj(interp, valuePtr, &b); + if (result != TCL_OK) { + TRACE_WITH_OBJ(("%d => ERROR: ", jmpOffset[ + ((*pc == INST_JUMP_FALSE1) || (*pc == INST_JUMP_FALSE4)) + ? 0 : 1]), Tcl_GetObjResult(interp)); + goto checkForCatch; } -#ifndef TCL_COMPILE_DEBUG - NEXT_INST_F(jmpOffset[b], 1, 0); -#else + +#ifdef TCL_COMPILE_DEBUG if (b) { if ((*pc == INST_JUMP_TRUE1) || (*pc == INST_JUMP_TRUE4)) { TRACE(("%d => %.20s true, new pc %u\n", jmpOffset[1], O2S(valuePtr), @@ -2488,7 +2646,6 @@ TclExecuteByteCode(interp, codePtr) } else { TRACE(("%d => %.20s true\n", jmpOffset[0], O2S(valuePtr))); } - NEXT_INST_F(jmpOffset[1], 1, 0); } else { if ((*pc == INST_JUMP_TRUE1) || (*pc == INST_JUMP_TRUE4)) { TRACE(("%d => %.20s false\n", jmpOffset[0], O2S(valuePtr))); @@ -2496,9 +2653,9 @@ TclExecuteByteCode(interp, codePtr) TRACE(("%d => %.20s false, new pc %u\n", jmpOffset[0], O2S(valuePtr), (unsigned int)(pc + jmpOffset[1] - codePtr->codeStart))); } - NEXT_INST_F(jmpOffset[0], 1, 0); } #endif + NEXT_INST_F(jmpOffset[b], 1, 0); } /* @@ -2514,94 +2671,34 @@ TclExecuteByteCode(interp, codePtr) * performed. */ - int i1, i2, length; - int iResult; - char *s; - Tcl_ObjType *t1Ptr, *t2Ptr; - Tcl_Obj *valuePtr, *value2Ptr; - Tcl_WideInt w; - - value2Ptr = *tosPtr; - valuePtr = *(tosPtr - 1); - t1Ptr = valuePtr->typePtr; - t2Ptr = value2Ptr->typePtr; - - if (t1Ptr == &tclIntType) { - i1 = (valuePtr->internalRep.longValue != 0); - } else if (t1Ptr == &tclWideIntType) { - TclGetWide(w,valuePtr); - i1 = (w != W0); - } else if (t1Ptr == &tclDoubleType) { - i1 = (valuePtr->internalRep.doubleValue != 0.0); - } else { - s = Tcl_GetStringFromObj(valuePtr, &length); - if (TclLooksLikeInt(s, length)) { - long i = 0; + int i1, i2, iResult; + Tcl_Obj *value2Ptr = *tosPtr; + Tcl_Obj *valuePtr = *(tosPtr - 1); - GET_WIDE_OR_INT(result, valuePtr, i, w); - if (valuePtr->typePtr == &tclIntType) { - i1 = (i != 0); - } else { - i1 = (w != W0); - } - } else { - result = Tcl_GetBooleanFromObj(NULL, valuePtr, &i1); - } - if (result != TCL_OK) { - TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n", O2S(valuePtr), - (t1Ptr? t1Ptr->name : "null"))); - IllegalExprOperandType(interp, pc, valuePtr); - goto checkForCatch; - } + result = TclGetBooleanFromObj(NULL, valuePtr, &i1); + if (result != TCL_OK) { + TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n", O2S(valuePtr), + (valuePtr->typePtr? valuePtr->typePtr->name : "null"))); + IllegalExprOperandType(interp, pc, valuePtr); + goto checkForCatch; } - if (t2Ptr == &tclIntType) { - i2 = (value2Ptr->internalRep.longValue != 0); - } else if (t2Ptr == &tclWideIntType) { - TclGetWide(w,value2Ptr); - i2 = (w != W0); - } else if (t2Ptr == &tclDoubleType) { - i2 = (value2Ptr->internalRep.doubleValue != 0.0); - } else { - s = Tcl_GetStringFromObj(value2Ptr, &length); - if (TclLooksLikeInt(s, length)) { - long i = 0; - - GET_WIDE_OR_INT(result, value2Ptr, i, w); - if (value2Ptr->typePtr == &tclIntType) { - i2 = (i != 0); - } else { - i2 = (w != W0); - } - } else { - result = Tcl_GetBooleanFromObj(NULL, value2Ptr, &i2); - } - if (result != TCL_OK) { - TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n", O2S(value2Ptr), - (t2Ptr? t2Ptr->name : "null"))); - IllegalExprOperandType(interp, pc, value2Ptr); - goto checkForCatch; - } + result = TclGetBooleanFromObj(NULL, value2Ptr, &i2); + if (result != TCL_OK) { + TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n", O2S(value2Ptr), + (value2Ptr->typePtr? value2Ptr->typePtr->name : "null"))); + IllegalExprOperandType(interp, pc, value2Ptr); + goto checkForCatch; } - /* - * Reuse the valuePtr object already on stack if possible. - */ - if (*pc == INST_LOR) { iResult = (i1 || i2); } else { iResult = (i1 && i2); } - if (Tcl_IsShared(valuePtr)) { - TclNewLongObj(objResultPtr, iResult); - TRACE(("%.20s %.20s => %d\n", O2S(valuePtr), O2S(value2Ptr), iResult)); - NEXT_INST_F(1, 2, 1); - } else { /* reuse the valuePtr object */ - TRACE(("%.20s %.20s => %d\n", O2S(valuePtr), O2S(value2Ptr), iResult)); - TclSetLongObj(valuePtr, iResult); - NEXT_INST_F(1, 1, 0); - } + objResultPtr = eePtr->constants[iResult]; + TRACE(("%.20s %.20s => %d\n", O2S(valuePtr), O2S(value2Ptr), iResult)); + NEXT_INST_F(1, 2, 1); } /* @@ -2930,6 +3027,7 @@ TclExecuteByteCode(interp, codePtr) value2Ptr = *tosPtr; valuePtr = *(tosPtr - 1); + /* TODO: Consider more efficient tests than strcmp() */ s1 = Tcl_GetStringFromObj(valuePtr, &s1len); result = Tcl_ListObjLength(interp, value2Ptr, &llen); if (result != TCL_OK) { @@ -2963,6 +3061,8 @@ TclExecuteByteCode(interp, codePtr) /* * Peep-hole optimisation: if you're about to jump, do jump from here. + * We're saving the effort of pushing a boolean value only to pop it + * for branching. */ pc++; @@ -2978,7 +3078,7 @@ TclExecuteByteCode(interp, codePtr) NEXT_INST_F((found ? TclGetInt4AtPtr(pc+1) : 5), 2, 0); } #endif - TclNewIntObj(objResultPtr, found); + objResultPtr = eePtr->constants[found]; NEXT_INST_F(0, 2, 1); } @@ -2991,6 +3091,7 @@ TclExecuteByteCode(interp, codePtr) case INST_STR_NEQ: { /* * String (in)equality check + * TODO: Consider merging into INST_STR_CMP */ int iResult; Tcl_Obj *valuePtr, *value2Ptr; @@ -3057,6 +3158,7 @@ TclExecuteByteCode(interp, codePtr) int s1len, s2len, iResult; Tcl_Obj *valuePtr, *value2Ptr; + stringCompare: value2Ptr = *tosPtr; valuePtr = *(tosPtr - 1); @@ -3108,18 +3210,44 @@ TclExecuteByteCode(interp, codePtr) /* * Make sure only -1,0,1 is returned + * TODO: consider peephole opt. */ if (iResult == 0) { iResult = s1len - s2len; } + + if (*pc != INST_STR_CMP) { + /* Take care of the opcodes that goto'ed into here */ + switch (*pc) { + case INST_EQ: + iResult = (iResult == 0); + break; + case INST_NEQ: + iResult = (iResult != 0); + break; + case INST_LT: + iResult = (iResult < 0); + break; + case INST_GT: + iResult = (iResult > 0); + break; + case INST_LE: + iResult = (iResult <= 0); + break; + case INST_GE: + iResult = (iResult >= 0); + break; + } + } if (iResult < 0) { - iResult = -1; - } else if (iResult > 0) { - iResult = 1; + TclNewIntObj(objResultPtr, -1); + TRACE(("%.20s %.20s => %d\n", O2S(valuePtr), O2S(value2Ptr), -1)); + } else { + objResultPtr = eePtr->constants[(iResult>0)]; + TRACE(("%.20s %.20s => %d\n", O2S(valuePtr), O2S(value2Ptr), + (iResult > 0))); } - TclNewIntObj(objResultPtr, iResult); - TRACE(("%.20s %.20s => %d\n", O2S(valuePtr), O2S(value2Ptr), iResult)); NEXT_INST_F(1, 2, 1); } @@ -3230,6 +3358,7 @@ TclExecuteByteCode(interp, codePtr) /* * Reuse value2Ptr object already on stack if possible. Adjustment is * 2 due to the nocase byte + * TODO: consider peephole opt. */ TRACE(("%.20s %.20s => %d\n", O2S(valuePtr), O2S(value2Ptr), match)); @@ -3243,251 +3372,293 @@ TclExecuteByteCode(interp, codePtr) case INST_GT: case INST_LE: case INST_GE: { - /* - * Any type is allowed but the two operands must have the same type. - * We will compute value op value2. - */ - - Tcl_ObjType *t1Ptr, *t2Ptr; - char *s1 = NULL; /* Init. avoids compiler warning. */ - char *s2 = NULL; /* Init. avoids compiler warning. */ - long i2 = 0; /* Init. avoids compiler warning. */ - double d1 = 0.0; /* Init. avoids compiler warning. */ - double d2 = 0.0; /* Init. avoids compiler warning. */ - long iResult = 0; /* Init. avoids compiler warning. */ - Tcl_Obj *valuePtr, *value2Ptr; - int length; - Tcl_WideInt w; - long i; - - value2Ptr = *tosPtr; - valuePtr = *(tosPtr - 1); - - /* - * Be careful in the equal-object case; 'NaN' isn't supposed to be - * equal to even itself. [Bug 761471] - */ - - t1Ptr = valuePtr->typePtr; + Tcl_Obj *valuePtr = *(tosPtr - 1); + Tcl_Obj *value2Ptr = *tosPtr; + ClientData ptr1, ptr2; + int iResult, compare, type1, type2; + double d1, d2, tmp; + long l1, l2; + Tcl_WideInt w1, w2; + mp_int big1, big2; + + if (GetNumberFromObj(NULL, valuePtr, &ptr1, &type1) != TCL_OK) { + /* At least one non-numeric argument - compare as strings */ + goto stringCompare; + } + if (type1 == TCL_NUMBER_NAN) { + /* NaN first arg: NaN != to everything, other compares are false */ + iResult = (*pc == INST_NEQ); + goto foundResult; + } if (valuePtr == value2Ptr) { - /* - * If we are numeric already, or a dictionary (which is never like - * a single-element list), we can proceed to the main equality - * check right now. Otherwise, we need to try to coerce to a - * numeric type so we can see if we've got a NaN but haven't - * parsed it as numeric. - */ - if (!IS_NUMERIC_TYPE(t1Ptr) && (t1Ptr != &tclDictType)) { - if (t1Ptr == &tclListType) { - int length; - /* - * Only a list of length 1 can be NaN or such things. - */ - (void) Tcl_ListObjLength(NULL, valuePtr, &length); - if (length == 1) { - goto mustConvertForNaNCheck; - } - } else { - /* - * Too bad, we'll have to compute the string and try the - * conversion - */ - - mustConvertForNaNCheck: - s1 = Tcl_GetStringFromObj(valuePtr, &length); - if (TclLooksLikeInt(s1, length)) { - GET_WIDE_OR_INT(iResult, valuePtr, i, w); - } else { - (void) Tcl_GetDoubleFromObj((Tcl_Interp *) NULL, - valuePtr, &d1); - } - t1Ptr = valuePtr->typePtr; - } - } - - switch (*pc) { - case INST_EQ: - case INST_LE: - case INST_GE: - iResult = !((t1Ptr == &tclDoubleType) - && IS_NAN(valuePtr->internalRep.doubleValue)); - break; - case INST_LT: - case INST_GT: - iResult = 0; - break; - case INST_NEQ: - iResult = ((t1Ptr == &tclDoubleType) - && IS_NAN(valuePtr->internalRep.doubleValue)); - break; - } + compare = MP_EQ; + goto convertComparison; + } + if (GetNumberFromObj(NULL, value2Ptr, &ptr2, &type2) != TCL_OK) { + /* At least one non-numeric argument - compare as strings */ + goto stringCompare; + } + if (type2 == TCL_NUMBER_NAN) { + /* NaN 2nd arg: NaN != to everything, other compares are false */ + iResult = (*pc == INST_NEQ); goto foundResult; } - - t2Ptr = value2Ptr->typePtr; - - /* - * We only want to coerce numeric validation if neither type is NULL. - * A NULL type means the arg is essentially an empty object ("", {} or - * [list]). - */ - if (!( (!t1Ptr && !valuePtr->bytes) - || (valuePtr->bytes && !valuePtr->length) - || (!t2Ptr && !value2Ptr->bytes) - || (value2Ptr->bytes && !value2Ptr->length))) { - if (!IS_NUMERIC_TYPE(t1Ptr)) { - s1 = Tcl_GetStringFromObj(valuePtr, &length); - if (TclLooksLikeInt(s1, length)) { - GET_WIDE_OR_INT(iResult, valuePtr, i, w); + switch (type1) { + case TCL_NUMBER_LONG: + l1 = *((CONST long *)ptr1); + switch (type2) { + case TCL_NUMBER_LONG: + l2 = *((CONST long *)ptr2); + longCompare: + compare = (l1 < l2) ? MP_LT : ((l1 > l2) ? MP_GT : MP_EQ); + break; +#ifndef NO_WIDE_TYPE + case TCL_NUMBER_WIDE: + w2 = *((CONST Tcl_WideInt *)ptr2); + w1 = (Tcl_WideInt)l1; + goto wideCompare; +#endif + case TCL_NUMBER_DOUBLE: + d2 = *((CONST double *)ptr2); + d1 = (double) l1; + + /* + * If the double has a fractional part, or if the + * long can be converted to double without loss of + * precision, then compare as doubles. + */ + if ((DBL_MANT_DIG > CHAR_BIT*sizeof(long)) + || (l1 == (long) d1) || (modf(d2, &tmp) != 0.0)) { + goto doubleCompare; + } + /* + * Otherwise, to make comparision based on full precision, + * need to convert the double to a suitably sized integer. + * + * Need this to get comparsions like + * expr 20000000000000003 < 20000000000000004.0 + * right. Converting the first argument to double + * will yield two double values that are equivalent + * within double precision. Converting the double to + * an integer gets done exactly, then integer comparison + * can tell the difference. + */ + if (d2 < (double)LONG_MIN) { + compare = MP_GT; + break; + } + if (d2 > (double)LONG_MAX) { + compare = MP_LT; + break; + } + l2 = (long) d2; + goto longCompare; + case TCL_NUMBER_BIG: + if (Tcl_IsShared(value2Ptr)) { + Tcl_GetBignumFromObj(NULL, value2Ptr, &big2); } else { - (void) Tcl_GetDoubleFromObj((Tcl_Interp *) NULL, - valuePtr, &d1); + Tcl_GetBignumAndClearObj(NULL, value2Ptr, &big2); } - t1Ptr = valuePtr->typePtr; - } - if (!IS_NUMERIC_TYPE(t2Ptr)) { - s2 = Tcl_GetStringFromObj(value2Ptr, &length); - if (TclLooksLikeInt(s2, length)) { - GET_WIDE_OR_INT(iResult, value2Ptr, i2, w); + if (mp_cmp_d(&big2, 0) == MP_LT) { + compare = MP_GT; } else { - (void) Tcl_GetDoubleFromObj((Tcl_Interp *) NULL, - value2Ptr, &d2); + compare = MP_LT; } - t2Ptr = value2Ptr->typePtr; + mp_clear(&big2); } - } - if (!IS_NUMERIC_TYPE(t1Ptr) || !IS_NUMERIC_TYPE(t2Ptr)) { - /* - * One operand is not numeric. Compare as strings. NOTE: strcmp - * is not correct for \x00 < \x01, but that is unlikely to occur - * here. We could use the TclUtfNCmp2 to handle this. - */ - int s1len, s2len; - s1 = Tcl_GetStringFromObj(valuePtr, &s1len); - s2 = Tcl_GetStringFromObj(value2Ptr, &s2len); - switch (*pc) { - case INST_EQ: - if (s1len == s2len) { - iResult = (strcmp(s1, s2) == 0); + break; + +#ifndef NO_WIDE_TYPE + case TCL_NUMBER_WIDE: + w1 = *((CONST Tcl_WideInt *)ptr1); + switch (type2) { + case TCL_NUMBER_WIDE: + w2 = *((CONST Tcl_WideInt *)ptr2); + wideCompare: + compare = (w1 < w2) ? MP_LT : ((w1 > w2) ? MP_GT : MP_EQ); + break; + case TCL_NUMBER_LONG: + l2 = *((CONST long *)ptr2); + w2 = (Tcl_WideInt)l2; + goto wideCompare; + case TCL_NUMBER_DOUBLE: + d2 = *((CONST double *)ptr2); + d1 = (double) w1; + if ((DBL_MANT_DIG > CHAR_BIT*sizeof(Tcl_WideInt)) + || (w1 == (Tcl_WideInt) d1) || (modf(d2, &tmp) != 0.0)) { + goto doubleCompare; + } + if (d2 < (double)LLONG_MIN) { + compare = MP_GT; + break; + } + if (d2 > (double)LLONG_MAX) { + compare = MP_LT; + break; + } + w2 = (Tcl_WideInt) d2; + goto wideCompare; + case TCL_NUMBER_BIG: + if (Tcl_IsShared(value2Ptr)) { + Tcl_GetBignumFromObj(NULL, value2Ptr, &big2); } else { - iResult = 0; + Tcl_GetBignumAndClearObj(NULL, value2Ptr, &big2); } - break; - case INST_NEQ: - if (s1len == s2len) { - iResult = (strcmp(s1, s2) != 0); + if (mp_cmp_d(&big2, 0) == MP_LT) { + compare = MP_GT; } else { - iResult = 1; + compare = MP_LT; } - break; - case INST_LT: - iResult = (strcmp(s1, s2) < 0); - break; - case INST_GT: - iResult = (strcmp(s1, s2) > 0); - break; - case INST_LE: - iResult = (strcmp(s1, s2) <= 0); - break; - case INST_GE: - iResult = (strcmp(s1, s2) >= 0); - break; + mp_clear(&big2); } - } else if ((t1Ptr == &tclDoubleType) || (t2Ptr == &tclDoubleType)) { - /* - * Compare as doubles. - */ - if (t1Ptr == &tclDoubleType) { - d1 = valuePtr->internalRep.doubleValue; - GET_DOUBLE_VALUE(d2, value2Ptr, t2Ptr); - } else { /* t1Ptr is integer, t2Ptr is double */ - GET_DOUBLE_VALUE(d1, valuePtr, t1Ptr); - d2 = value2Ptr->internalRep.doubleValue; - } - switch (*pc) { - case INST_EQ: - iResult = d1 == d2; - break; - case INST_NEQ: - iResult = d1 != d2; - break; - case INST_LT: - iResult = d1 < d2; - break; - case INST_GT: - iResult = d1 > d2; - break; - case INST_LE: - iResult = d1 <= d2; - break; - case INST_GE: - iResult = d1 >= d2; + break; +#endif + + case TCL_NUMBER_DOUBLE: + d1 = *((CONST double *)ptr1); + switch (type2) { + case TCL_NUMBER_DOUBLE: + d2 = *((CONST double *)ptr2); + doubleCompare: + compare = (d1 < d2) ? MP_LT : ((d1 > d2) ? MP_GT : MP_EQ); break; + case TCL_NUMBER_LONG: + l2 = *((CONST long *)ptr2); + d2 = (double) l2; + + if ((DBL_MANT_DIG > CHAR_BIT*sizeof(long)) + || (l2 == (long) d2) || (modf(d1, &tmp) != 0.0)) { + goto doubleCompare; + } + if (d1 < (double)LONG_MIN) { + compare = MP_LT; + break; + } + if (d1 > (double)LONG_MAX) { + compare = MP_GT; + break; + } + l1 = (long) d1; + goto longCompare; +#ifndef NO_WIDE_TYPE + case TCL_NUMBER_WIDE: + w2 = *((CONST Tcl_WideInt *)ptr2); + d2 = (double) w2; + if ((DBL_MANT_DIG > CHAR_BIT*sizeof(Tcl_WideInt)) + || (w2 == (Tcl_WideInt) d2) || (modf(d1, &tmp) != 0.0)) { + goto doubleCompare; + } + if (d1 < (double)LLONG_MIN) { + compare = MP_LT; + break; + } + if (d1 > (double)LLONG_MAX) { + compare = MP_GT; + break; + } + w1 = (Tcl_WideInt) d1; + goto wideCompare; +#endif + case TCL_NUMBER_BIG: + if (TclIsInfinite(d1)) { + compare = (d1 > 0.0) ? MP_GT : MP_LT; + break; + } + if (Tcl_IsShared(value2Ptr)) { + Tcl_GetBignumFromObj(NULL, value2Ptr, &big2); + } else { + Tcl_GetBignumAndClearObj(NULL, value2Ptr, &big2); + } + if ((d1 < (double)LONG_MAX) && (d1 > (double)LONG_MIN)) { + if (mp_cmp_d(&big2, 0) == MP_LT) { + compare = MP_GT; + } else { + compare = MP_LT; + } + mp_clear(&big2); + break; + } + if ((DBL_MANT_DIG > CHAR_BIT*sizeof(long)) + && (modf(d1, &tmp) != 0.0)) { + d2 = TclBignumToDouble( &big2); + mp_clear(&big2); + goto doubleCompare; + } + TclInitBignumFromDouble(NULL, d1, &big1); + goto bigCompare; } - } else if ((t1Ptr == &tclWideIntType) || (t2Ptr == &tclWideIntType)) { - Tcl_WideInt w2; - /* - * Compare as wide ints (neither are doubles) - */ - if (t1Ptr == &tclIntType) { - w = Tcl_LongAsWide(valuePtr->internalRep.longValue); - TclGetWide(w2,value2Ptr); - } else if (t2Ptr == &tclIntType) { - TclGetWide(w,valuePtr); - w2 = Tcl_LongAsWide(value2Ptr->internalRep.longValue); + break; + + case TCL_NUMBER_BIG: + if (Tcl_IsShared(valuePtr)) { + Tcl_GetBignumFromObj(NULL, valuePtr, &big1); } else { - TclGetWide(w,valuePtr); - TclGetWide(w2,value2Ptr); + Tcl_GetBignumAndClearObj(NULL, valuePtr, &big1); } - switch (*pc) { - case INST_EQ: - iResult = w == w2; - break; - case INST_NEQ: - iResult = w != w2; - break; - case INST_LT: - iResult = w < w2; - break; - case INST_GT: - iResult = w > w2; - break; - case INST_LE: - iResult = w <= w2; - break; - case INST_GE: - iResult = w >= w2; - break; - } - } else { - /* - * Compare as ints. - */ - i = valuePtr->internalRep.longValue; - i2 = value2Ptr->internalRep.longValue; - switch (*pc) { - case INST_EQ: - iResult = i == i2; - break; - case INST_NEQ: - iResult = i != i2; - break; - case INST_LT: - iResult = i < i2; - break; - case INST_GT: - iResult = i > i2; - break; - case INST_LE: - iResult = i <= i2; - break; - case INST_GE: - iResult = i >= i2; + switch (type2) { +#ifndef NO_WIDE_TYPE + case TCL_NUMBER_WIDE: +#endif + case TCL_NUMBER_LONG: + compare = mp_cmp_d(&big1, 0); + mp_clear(&big1); break; + case TCL_NUMBER_DOUBLE: + d2 = *((CONST double *)ptr2); + if (TclIsInfinite(d2)) { + compare = (d2 > 0.0) ? MP_LT : MP_GT; + mp_clear(&big1); + break; + } + if ((d2 < (double)LONG_MAX) && (d2 > (double)LONG_MIN)) { + compare = mp_cmp_d(&big1, 0); + mp_clear(&big1); + break; + } + if ((DBL_MANT_DIG > CHAR_BIT*sizeof(long)) + && (modf(d2, &tmp) != 0.0)) { + d1 = TclBignumToDouble( &big1); + mp_clear(&big1); + goto doubleCompare; + } + TclInitBignumFromDouble(NULL, d2, &big2); + goto bigCompare; + case TCL_NUMBER_BIG: + if (Tcl_IsShared(value2Ptr)) { + Tcl_GetBignumFromObj(NULL, value2Ptr, &big2); + } else { + Tcl_GetBignumAndClearObj(NULL, value2Ptr, &big2); + } + bigCompare: + compare = mp_cmp(&big1, &big2); + mp_clear(&big1); + mp_clear(&big2); } } - TRACE(("%.20s %.20s => %ld\n", O2S(valuePtr), O2S(value2Ptr), iResult)); + /* Turn comparison outcome into appropriate result for opcode */ + + convertComparison: + switch (*pc) { + case INST_EQ: + iResult = (compare == MP_EQ); + break; + case INST_NEQ: + iResult = (compare != MP_EQ); + break; + case INST_LT: + iResult = (compare == MP_LT); + break; + case INST_GT: + iResult = (compare == MP_GT); + break; + case INST_LE: + iResult = (compare != MP_GT); + break; + case INST_GE: + iResult = (compare != MP_LT); + break; + } /* * Peep-hole optimisation: if you're about to jump, do jump from here. @@ -3511,12 +3682,445 @@ TclExecuteByteCode(interp, codePtr) NEXT_INST_F(0, 2, 1); } - case INST_MOD: case INST_LSHIFT: - case INST_RSHIFT: + case INST_RSHIFT: { + Tcl_Obj *value2Ptr = *tosPtr; + Tcl_Obj *valuePtr = *(tosPtr - 1); + ClientData ptr1, ptr2; + int invalid, shift, type1, type2; + long l; + + result = GetNumberFromObj(NULL, valuePtr, &ptr1, &type1); + if ((result != TCL_OK) + || (type1 == TCL_NUMBER_DOUBLE) || (type1 == TCL_NUMBER_NAN)) { + result = TCL_ERROR; + TRACE(("%.20s %.20s => ILLEGAL 1st TYPE %s\n", O2S(valuePtr), + O2S(value2Ptr), (valuePtr->typePtr? + valuePtr->typePtr->name : "null"))); + IllegalExprOperandType(interp, pc, valuePtr); + goto checkForCatch; + } + + result = GetNumberFromObj(NULL, value2Ptr, &ptr2, &type2); + if ((result != TCL_OK) + || (type2 == TCL_NUMBER_DOUBLE) || (type2 == TCL_NUMBER_NAN)) { + result = TCL_ERROR; + TRACE(("%.20s %.20s => ILLEGAL 2nd TYPE %s\n", O2S(valuePtr), + O2S(value2Ptr), (value2Ptr->typePtr? + value2Ptr->typePtr->name : "null"))); + IllegalExprOperandType(interp, pc, value2Ptr); + goto checkForCatch; + } + + /* reject negative shift argument */ + switch (type2) { + case TCL_NUMBER_LONG: + invalid = (*((CONST long *)ptr2) < (long)0); + break; +#ifndef NO_WIDE_TYPE + case TCL_NUMBER_WIDE: + invalid = (*((CONST Tcl_WideInt *)ptr2) < (Tcl_WideInt)0); + break; +#endif + case TCL_NUMBER_BIG: + /* TODO: const correctness ? */ + invalid = (mp_cmp_d((mp_int *)ptr2, 0) == MP_LT); + } + if (invalid) { + Tcl_SetObjResult(interp, + Tcl_NewStringObj("negative shift argument", -1)); + result = TCL_ERROR; + goto checkForCatch; + } + + /* Zero shifted any number of bits is still zero */ + if ((type1 == TCL_NUMBER_LONG) && (*((CONST long *)ptr1) == (long)0)) { + TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); + objResultPtr = eePtr->constants[0]; + TRACE(("%s\n", O2S(objResultPtr))); + NEXT_INST_F(1, 2, 1); + } + + if (*pc == INST_LSHIFT) { + /* Large left shifts create integer overflow */ + result = Tcl_GetIntFromObj(NULL, value2Ptr, &shift); + if (result != TCL_OK) { + /* + * Technically, we could hold the value (1 << (INT_MAX+1)) + * in an mp_int, but since we're using mp_mul_2d() to do the + * work, and it takes only an int argument, that's a good + * place to draw the line. + */ + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "integer value too large to represent", -1)); + goto checkForCatch; + } + /* Handle shifts within the native long range */ + TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); + if ((type1 == TCL_NUMBER_LONG) && (shift < CHAR_BIT*sizeof(long)) + && (l = *((CONST long *)ptr1)) + && !(((l>0) ? l : ~l) + & -(1<<(CHAR_BIT*sizeof(long)-1-shift)))) { + TclNewLongObj(objResultPtr, (l<<shift)); + TRACE(("%s\n", O2S(objResultPtr))); + NEXT_INST_F(1, 2, 1); + } + + /* Handle shifts within the native wide range */ + TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); + if ((type1 != TCL_NUMBER_BIG) + && (shift < CHAR_BIT*sizeof(Tcl_WideInt))) { + Tcl_WideInt w; + TclGetWideIntFromObj(NULL, valuePtr, &w); + if (!(((w>0) ? w : ~w) + & -(((Tcl_WideInt)1) + <<(CHAR_BIT*sizeof(Tcl_WideInt)-1-shift)))) { + objResultPtr = Tcl_NewWideIntObj(w<<shift); + TRACE(("%s\n", O2S(objResultPtr))); + NEXT_INST_F(1, 2, 1); + } + } + +/* + if ((type1 == TCL_NUMBER_LONG) && (shift < CHAR_BIT*sizeof(long)) + && (l = *((CONST long *)ptr1)) + && !(((l>0) ? l : ~l) + & -(1<<(CHAR_BIT*sizeof(long)-1-shift)))) { + TclNewLongObj(objResultPtr, (l<<shift)); + TRACE(("%s\n", O2S(objResultPtr))); + NEXT_INST_F(1, 2, 1); + } +*/ + + + + } else { + /* Quickly force large right shifts to 0 or -1 */ + TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); + if ((type2 != TCL_NUMBER_LONG) + || ( *((CONST long *)ptr2) > INT_MAX)) { + /* + * Again, technically, the value to be shifted could + * be an mp_int so huge that a right shift by (INT_MAX+1) + * bits could not take us to the result of 0 or -1, but + * since we're using mp_div_2d to do the work, and it + * takes only an int argument, we draw the line there. + */ + int zero; + switch (type1) { + case TCL_NUMBER_LONG: + zero = (*((CONST long *)ptr1) > (long)0); + break; +#ifndef NO_WIDE_TYPE + case TCL_NUMBER_WIDE: + zero = (*((CONST Tcl_WideInt *)ptr1) > (Tcl_WideInt)0); + break; +#endif + case TCL_NUMBER_BIG: + /* TODO: const correctness ? */ + zero = (mp_cmp_d((mp_int *)ptr1, 0) == MP_GT); + } + if (zero) { + objResultPtr = eePtr->constants[0]; + } else { + TclNewIntObj(objResultPtr, -1); + } + TRACE(("%s\n", O2S(objResultPtr))); + NEXT_INST_F(1, 2, 1); + } + shift = (int)(*((CONST long *)ptr2)); + /* Handle shifts within the native long range */ + if (type1 == TCL_NUMBER_LONG) { + long l = *((CONST long *)ptr1); + if (shift >= CHAR_BIT*sizeof(long)) { + if (l >= (long)0) { + objResultPtr = eePtr->constants[0]; + } else { + TclNewIntObj(objResultPtr, -1); + } + } else { + TclNewLongObj(objResultPtr, (l >> shift)); + } + TRACE(("%s\n", O2S(objResultPtr))); + NEXT_INST_F(1, 2, 1); + } +#ifndef NO_WIDE_TYPE + /* Handle shifts within the native wide range */ + if (type1 == TCL_NUMBER_WIDE) { + Tcl_WideInt w = *((CONST Tcl_WideInt *)ptr1); + if (shift >= CHAR_BIT*sizeof(Tcl_WideInt)) { + if (w >= (Tcl_WideInt)0) { + objResultPtr = eePtr->constants[0]; + } else { + TclNewIntObj(objResultPtr, -1); + } + } else { + objResultPtr = Tcl_NewWideIntObj(w >> shift); + } + TRACE(("%s\n", O2S(objResultPtr))); + NEXT_INST_F(1, 2, 1); + } +#endif + } + + { + mp_int big, bigResult, bigRemainder; + + if (Tcl_IsShared(valuePtr)) { + Tcl_GetBignumFromObj(NULL, valuePtr, &big); + } else { + Tcl_GetBignumAndClearObj(NULL, valuePtr, &big); + } + + mp_init(&bigResult); + if (*pc == INST_LSHIFT) { + mp_mul_2d(&big, shift, &bigResult); + } else { + mp_init(&bigRemainder); + mp_div_2d(&big, shift, &bigResult, &bigRemainder); + if (mp_cmp_d(&bigRemainder, 0) == MP_LT) { + /* Convert to Tcl's integer division rules */ + mp_sub_d(&bigResult, 1, &bigResult); + } + mp_clear(&bigRemainder); + } + mp_clear(&big); + + if (!Tcl_IsShared(valuePtr)) { + Tcl_SetBignumObj(valuePtr, &bigResult); + TRACE(("%s\n", O2S(valuePtr))); + NEXT_INST_F(1, 1, 0); + } + objResultPtr = Tcl_NewBignumObj(&bigResult); + } + TRACE(("%s\n", O2S(objResultPtr))); + NEXT_INST_F(1, 2, 1); + } + case INST_BITOR: case INST_BITXOR: case INST_BITAND: { + ClientData ptr1, ptr2; + int type1, type2; + Tcl_Obj *value2Ptr = *tosPtr; + Tcl_Obj *valuePtr = *(tosPtr - 1); + + result = GetNumberFromObj(NULL, valuePtr, &ptr1, &type1); + if ((result != TCL_OK) + || (type1 == TCL_NUMBER_NAN) || (type1 == TCL_NUMBER_DOUBLE)) { + result = TCL_ERROR; + TRACE(("%.20s %.20s => ILLEGAL 1st TYPE %s\n", O2S(valuePtr), + O2S(value2Ptr), (valuePtr->typePtr? + valuePtr->typePtr->name : "null"))); + IllegalExprOperandType(interp, pc, valuePtr); + goto checkForCatch; + } + result = GetNumberFromObj(NULL, value2Ptr, &ptr2, &type2); + if ((result != TCL_OK) + || (type2 == TCL_NUMBER_NAN) || (type2 == TCL_NUMBER_DOUBLE)) { + result = TCL_ERROR; + TRACE(("%.20s %.20s => ILLEGAL 2nd TYPE %s\n", O2S(valuePtr), + O2S(value2Ptr), (value2Ptr->typePtr? + value2Ptr->typePtr->name : "null"))); + IllegalExprOperandType(interp, pc, value2Ptr); + goto checkForCatch; + } + + if ((type1 == TCL_NUMBER_BIG) || (type2 == TCL_NUMBER_BIG)) { + mp_int big1, big2, bigResult; + mp_int *Pos, *Neg, *Other; + int numPos = 0; + + if (Tcl_IsShared(valuePtr)) { + Tcl_GetBignumFromObj(NULL, valuePtr, &big1); + } else { + Tcl_GetBignumAndClearObj(NULL, valuePtr, &big1); + } + if (Tcl_IsShared(value2Ptr)) { + Tcl_GetBignumFromObj(NULL, value2Ptr, &big2); + } else { + Tcl_GetBignumAndClearObj(NULL, value2Ptr, &big2); + } + + if (mp_cmp_d(&big1, 0) != MP_LT) { + numPos++; + Pos = &big1; + if (mp_cmp_d(&big2, 0) != MP_LT) { + numPos++; + Other = &big2; + } else { + Neg = &big2; + } + } else { + Neg = &big1; + if (mp_cmp_d(&big2, 0) != MP_LT) { + numPos++; + Pos = &big2; + } else { + Other = &big2; + } + } + mp_init(&bigResult); + + switch (*pc) { + case INST_BITAND: + switch (numPos) { + case 2: + /* Both arguments positive, base case */ + mp_and(Pos, Other, &bigResult); + break; + case 1: + /* One arg positive; one negative + * P & N = P & ~~N = P&~(-N-1) = P & (P ^ (-N-1)) */ + mp_neg(Neg, Neg); + mp_sub_d(Neg, 1, Neg); + mp_xor(Pos, Neg, &bigResult); + mp_and(Pos, &bigResult, &bigResult); + break; + case 0: + /* Both arguments negative + * a & b = ~ (~a | ~b) = -(-a-1|-b-1)-1 */ + mp_neg(Neg, Neg); + mp_sub_d(Neg, 1, Neg); + mp_neg(Other, Other); + mp_sub_d(Other, 1, Other); + mp_or(Neg, Other, &bigResult); + mp_neg(&bigResult, &bigResult); + mp_sub_d(&bigResult, 1, &bigResult); + break; + } + break; + + case INST_BITOR: + switch (numPos) { + case 2: + /* Both arguments positive, base case */ + mp_or(Pos, Other, &bigResult); + break; + case 1: + /* One arg positive; one negative + * N|P = ~(~N&~P) = ~((-N-1)&~P) = -((-N-1)&((-N-1)^P))-1 */ + mp_neg(Neg, Neg); + mp_sub_d(Neg, 1, Neg); + mp_xor(Pos, Neg, &bigResult); + mp_and(Neg, &bigResult, &bigResult); + mp_neg(&bigResult, &bigResult); + mp_sub_d(&bigResult, 1, &bigResult); + break; + case 0: + /* Both arguments negative + * a | b = ~ (~a & ~b) = -(-a-1&-b-1)-1 */ + mp_neg(Neg, Neg); + mp_sub_d(Neg, 1, Neg); + mp_neg(Other, Other); + mp_sub_d(Other, 1, Other); + mp_and(Neg, Other, &bigResult); + mp_neg(&bigResult, &bigResult); + mp_sub_d(&bigResult, 1, &bigResult); + break; + } + break; + + case INST_BITXOR: + switch (numPos) { + case 2: + /* Both arguments positive, base case */ + mp_xor(Pos, Other, &bigResult); + break; + case 1: + /* One arg positive; one negative + * P^N = ~(P^~N) = -(P^(-N-1))-1 + */ + mp_neg(Neg, Neg); + mp_sub_d(Neg, 1, Neg); + mp_xor(Pos, Neg, &bigResult); + mp_neg(&bigResult, &bigResult); + mp_sub_d(&bigResult, 1, &bigResult); + break; + case 0: + /* Both arguments negative + * a ^ b = (~a ^ ~b) = (-a-1^-b-1) */ + mp_neg(Neg, Neg); + mp_sub_d(Neg, 1, Neg); + mp_neg(Other, Other); + mp_sub_d(Other, 1, Other); + mp_xor(Neg, Other, &bigResult); + break; + } + break; + } + + mp_clear(&big1); + mp_clear(&big2); + TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); + if (Tcl_IsShared(valuePtr)) { + objResultPtr = Tcl_NewBignumObj(&bigResult); + TRACE(("%s\n", O2S(objResultPtr))); + NEXT_INST_F(1, 2, 1); + } + Tcl_SetBignumObj(valuePtr, &bigResult); + TRACE(("%s\n", O2S(valuePtr))); + NEXT_INST_F(1, 1, 0); + } + +#ifndef NO_WIDE_TYPE + if ((type1 == TCL_NUMBER_WIDE) || (type2 == TCL_NUMBER_WIDE)) { + Tcl_WideInt wResult, w1, w2; + TclGetWideIntFromObj(NULL, valuePtr, &w1); + TclGetWideIntFromObj(NULL, value2Ptr, &w2); + + switch (*pc) { + case INST_BITAND: + wResult = w1 & w2; + break; + case INST_BITOR: + wResult = w1 | w2; + break; + case INST_BITXOR: + wResult = w1 ^ w2; + } + + TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); + if (Tcl_IsShared(valuePtr)) { + objResultPtr = Tcl_NewWideIntObj(wResult); + TRACE(("%s\n", O2S(objResultPtr))); + NEXT_INST_F(1, 2, 1); + } + Tcl_SetWideIntObj(valuePtr, wResult); + TRACE(("%s\n", O2S(valuePtr))); + NEXT_INST_F(1, 1, 0); + } +#endif + { + long lResult, l1 = *((CONST long *)ptr1); + long l2 = *((CONST long *)ptr2); + + switch (*pc) { + case INST_BITAND: + lResult = l1 & l2; + break; + case INST_BITOR: + lResult = l1 | l2; + break; + case INST_BITXOR: + lResult = l1 ^ l2; + } + + TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); + if (Tcl_IsShared(valuePtr)) { + TclNewLongObj(objResultPtr, lResult); + TRACE(("%s\n", O2S(objResultPtr))); + NEXT_INST_F(1, 2, 1); + } + TclSetLongObj(valuePtr, lResult); + TRACE(("%s\n", O2S(valuePtr))); + NEXT_INST_F(1, 1, 0); + } + } + +#if 0 + case INST_MOD: + { /* * Only integers are allowed. We compute value op value2. */ @@ -3560,8 +4164,7 @@ TclExecuteByteCode(interp, codePtr) } } - switch (*pc) { - case INST_MOD: + do { /* * This code is tricky: C doesn't guarantee much about the * quotient or remainder, and results with a negative divisor are @@ -3691,171 +4294,7 @@ TclExecuteByteCode(interp, codePtr) rem = -rem; } iResult = rem; - break; - case INST_LSHIFT: - /* - * Shifts are never usefully 64-bits wide! - */ - FORCE_LONG(value2Ptr, i2, w2); - if (valuePtr->typePtr == &tclWideIntType) { -#ifdef TCL_COMPILE_DEBUG - w2 = Tcl_LongAsWide(i2); -#endif /* TCL_COMPILE_DEBUG */ - wResult = w; - /* - * Shift in steps when the shift gets large to prevent - * annoying compiler/processor bugs. [Bug 868467] - */ - if (i2 >= 64) { - wResult = Tcl_LongAsWide(0); - } else if (i2 > 60) { - wResult = w << 30; - wResult <<= 30; - wResult <<= i2-60; - } else if (i2 > 30) { - wResult = w << 30; - wResult <<= i2-30; - } else { - wResult = w << i2; - } - doWide = 1; - break; - } - /* - * Shift in steps when the shift gets large to prevent annoying - * compiler/processor bugs. [Bug 868467] - */ - if (i2 >= 64) { - iResult = 0; - } else if (i2 > 60) { - iResult = i << 30; - iResult <<= 30; - iResult <<= i2-60; - } else if (i2 > 30) { - iResult = i << 30; - iResult <<= i2-30; - } else { - iResult = i << i2; - } - break; - case INST_RSHIFT: - /* - * The following code is a bit tricky: it ensures that right - * shifts propagate the sign bit even on machines where ">>" won't - * do it by default. - */ - /* - * Shifts are never usefully 64-bits wide! - */ - FORCE_LONG(value2Ptr, i2, w2); - if (valuePtr->typePtr == &tclWideIntType) { -#ifdef TCL_COMPILE_DEBUG - w2 = Tcl_LongAsWide(i2); -#endif /* TCL_COMPILE_DEBUG */ - if (w < 0) { - wResult = ~w; - } else { - wResult = w; - } - /* - * Shift in steps when the shift gets large to prevent - * annoying compiler/processor bugs. [Bug 868467] - */ - if (i2 >= 64) { - wResult = Tcl_LongAsWide(0); - } else if (i2 > 60) { - wResult >>= 30; - wResult >>= 30; - wResult >>= i2-60; - } else if (i2 > 30) { - wResult >>= 30; - wResult >>= i2-30; - } else { - wResult >>= i2; - } - if (w < 0) { - wResult = ~wResult; - } - doWide = 1; - break; - } - if (i < 0) { - iResult = ~i; - } else { - iResult = i; - } - /* - * Shift in steps when the shift gets large to prevent annoying - * compiler/processor bugs. [Bug 868467] - */ - if (i2 >= 64) { - iResult = 0; - } else if (i2 > 60) { - iResult >>= 30; - iResult >>= 30; - iResult >>= i2-60; - } else if (i2 > 30) { - iResult >>= 30; - iResult >>= i2-30; - } else { - iResult >>= i2; - } - if (i < 0) { - iResult = ~iResult; - } - break; - case INST_BITOR: - if (valuePtr->typePtr == &tclWideIntType - || value2Ptr->typePtr == &tclWideIntType) { - /* - * Promote to wide - */ - if (valuePtr->typePtr == &tclIntType) { - w = Tcl_LongAsWide(i); - } else if (value2Ptr->typePtr == &tclIntType) { - w2 = Tcl_LongAsWide(i2); - } - wResult = w | w2; - doWide = 1; - break; - } - iResult = i | i2; - break; - case INST_BITXOR: - if (valuePtr->typePtr == &tclWideIntType - || value2Ptr->typePtr == &tclWideIntType) { - /* - * Promote to wide - */ - if (valuePtr->typePtr == &tclIntType) { - w = Tcl_LongAsWide(i); - } else if (value2Ptr->typePtr == &tclIntType) { - w2 = Tcl_LongAsWide(i2); - } - wResult = w ^ w2; - doWide = 1; - break; - } - iResult = i ^ i2; - break; - case INST_BITAND: - if (valuePtr->typePtr == &tclWideIntType - || value2Ptr->typePtr == &tclWideIntType) { - /* - * Promote to wide - */ - if (valuePtr->typePtr == &tclIntType) { - w = Tcl_LongAsWide(i); - } else if (value2Ptr->typePtr == &tclIntType) { - w2 = Tcl_LongAsWide(i2); - } - wResult = w & w2; - doWide = 1; - break; - } - iResult = i & i2; - break; - } + } while (0); /* * Reuse the valuePtr object already on stack if possible. @@ -3881,27 +4320,284 @@ TclExecuteByteCode(interp, codePtr) NEXT_INST_F(1, 1, 0); } } +#endif case INST_ADD: case INST_SUB: - case INST_MULT: case INST_DIV: + case INST_MULT: { + ClientData ptr1, ptr2; + int type1, type2; + Tcl_Obj *value2Ptr = *tosPtr; + Tcl_Obj *valuePtr = *(tosPtr - 1); + + result = GetNumberFromObj(NULL, valuePtr, &ptr1, &type1); + if ((result != TCL_OK) +#ifndef ACCEPT_NAN + || (type1 == TCL_NUMBER_NAN) +#endif + ) { + result = TCL_ERROR; + TRACE(("%.20s %.20s => ILLEGAL 1st TYPE %s\n", + O2S(value2Ptr), O2S(valuePtr), + (valuePtr->typePtr? valuePtr->typePtr->name: "null"))); + IllegalExprOperandType(interp, pc, valuePtr); + goto checkForCatch; + } + +#ifdef ACCEPT_NAN + if (type1 == TCL_NUMBER_NAN) { + /* NaN first argument -> result is also NaN */ + NEXT_INST_F(1, 1, 0); + } +#endif + + result = GetNumberFromObj(NULL, value2Ptr, &ptr2, &type2); + if ((result != TCL_OK) +#ifndef ACCEPT_NAN + || (type2 == TCL_NUMBER_NAN) +#endif + ) { + result = TCL_ERROR; + TRACE(("%.20s %.20s => ILLEGAL 2nd TYPE %s\n", + O2S(value2Ptr), O2S(valuePtr), + (value2Ptr->typePtr? value2Ptr->typePtr->name: "null"))); + IllegalExprOperandType(interp, pc, value2Ptr); + goto checkForCatch; + } + +#ifdef ACCEPT_NAN + if (type2 == TCL_NUMBER_NAN) { + /* NaN second argument -> result is also NaN */ + objResultPtr = value2Ptr; + NEXT_INST_F(1, 2, 1); + } +#endif + + if ((type1 == TCL_NUMBER_DOUBLE) || (type2 == TCL_NUMBER_DOUBLE)) { + /* At least one of the values is floating-point, so perform + * floating point calculations */ + double d1, d2, dResult; + Tcl_GetDoubleFromObj(NULL, valuePtr, &d1); + Tcl_GetDoubleFromObj(NULL, value2Ptr, &d2); + + switch (*pc) { + case INST_ADD: + dResult = d1 + d2; + break; + case INST_SUB: + dResult = d1 - d2; + break; + case INST_MULT: + dResult = d1 * d2; + break; + case INST_DIV: +#ifndef IEEE_FLOATING_POINT + if (d2 == 0.0) { + TRACE(("%.6g %.6g => DIVIDE BY ZERO\n", d1, d2)); + goto divideByZero; + } +#endif + /* + * We presume that we are running with zero-divide unmasked if + * we're on an IEEE box. Otherwise, this statement might cause + * demons to fly out our noses. + */ + dResult = d1 / d2; + break; + } + +#ifndef ACCEPT_NAN + /* + * Check now for IEEE floating-point error. + */ + + if (TclIsNaN(dResult)) { + TRACE(("%.20s %.20s => IEEE FLOATING PT ERROR\n", + O2S(valuePtr), O2S(value2Ptr))); + TclExprFloatError(interp, dResult); + result = TCL_ERROR; + goto checkForCatch; + } +#endif + TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); + if (Tcl_IsShared(valuePtr)) { + TclNewDoubleObj(objResultPtr, dResult); + TRACE(("%s\n", O2S(objResultPtr))); + NEXT_INST_F(1, 2, 1); + } + TclSetDoubleObj(valuePtr, dResult); + TRACE(("%s\n", O2S(valuePtr))); + NEXT_INST_F(1, 1, 0); + } + + if ((*pc == INST_MULT) && (sizeof(Tcl_WideInt) >= 2*sizeof(long)) + && (type1 == TCL_NUMBER_LONG) && (type2 == TCL_NUMBER_LONG)) { + Tcl_WideInt w1, w2, wResult; + TclGetWideIntFromObj(NULL, valuePtr, &w1); + TclGetWideIntFromObj(NULL, value2Ptr, &w2); + + wResult = w1 * w2; + + TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); + if (Tcl_IsShared(valuePtr)) { + objResultPtr = Tcl_NewWideIntObj(wResult); + TRACE(("%s\n", O2S(objResultPtr))); + NEXT_INST_F(1, 2, 1); + } + Tcl_SetWideIntObj(valuePtr, wResult); + TRACE(("%s\n", O2S(valuePtr))); + NEXT_INST_F(1, 1, 0); + } + + if ((*pc != INST_MULT) + && (type1 != TCL_NUMBER_BIG) && (type2 != TCL_NUMBER_BIG)) { + Tcl_WideInt w1, w2, wResult; + TclGetWideIntFromObj(NULL, valuePtr, &w1); + TclGetWideIntFromObj(NULL, value2Ptr, &w2); + + switch (*pc) { + case INST_ADD: + wResult = w1 + w2; +#ifndef NO_WIDE_TYPE + if ((type1 == TCL_NUMBER_WIDE) || (type2 == TCL_NUMBER_WIDE)) +#endif + { + /* Check for overflow */ + if (((w1 < 0) && (w2 < 0) && (wResult > 0)) + || ((w1 > 0) && (w2 > 0) && (wResult < 0))) { + goto overflow; + } + } + break; + + case INST_SUB: + wResult = w1 - w2; +#ifndef NO_WIDE_TYPE + if ((type1 == TCL_NUMBER_WIDE) || (type2 == TCL_NUMBER_WIDE)) +#endif + { + /* Must check for overflow */ + if (((w1 < 0) && (w2 > 0) && (wResult > 0)) + || ((w1 > 0) && (w2 < 0) && (wResult < 0))) { + goto overflow; + } + } + break; + + case INST_DIV: + if (w2 == 0) { + TRACE(("%s %s => DIVIDE BY ZERO\n", + O2S(valuePtr), O2S(value2Ptr))); + goto divideByZero; + } + + /* Need a bignum to represent (LLONG_MIN / -1) */ + if ((w1 == LLONG_MIN) && (w2 == -1)) { + goto overflow; + } + wResult = w1 / w2; + + /* Force Tcl's integer division rules */ + /* TODO: examine for logic simplification */ + if (((wResult < 0) || ((wResult == 0) && + ((w1 < 0 && w2 > 0) || (w1 > 0 && w2 < 0)))) && + ((wResult * w2) != w1)) { + wResult -= 1; + } + break; + } + + TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); + if (Tcl_IsShared(valuePtr)) { + objResultPtr = Tcl_NewWideIntObj(wResult); + TRACE(("%s\n", O2S(objResultPtr))); + NEXT_INST_F(1, 2, 1); + } + Tcl_SetWideIntObj(valuePtr, wResult); + TRACE(("%s\n", O2S(valuePtr))); + NEXT_INST_F(1, 1, 0); + } + + overflow: + { + mp_int big1, big2, bigResult, bigRemainder; + TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); + if (Tcl_IsShared(valuePtr)) { + Tcl_GetBignumFromObj(NULL, valuePtr, &big1); + } else { + Tcl_GetBignumAndClearObj(NULL, valuePtr, &big1); + } + if (Tcl_IsShared(value2Ptr)) { + Tcl_GetBignumFromObj(NULL, value2Ptr, &big2); + } else { + Tcl_GetBignumAndClearObj(NULL, value2Ptr, &big2); + } + mp_init(&bigResult); + switch (*pc) { + case INST_ADD: + mp_add(&big1, &big2, &bigResult); + break; + case INST_SUB: + mp_sub(&big1, &big2, &bigResult); + break; + case INST_MULT: + mp_mul(&big1, &big2, &bigResult); + break; + case INST_DIV: + if (mp_iszero(&big2)) { + TRACE(("%s %s => DIVIDE BY ZERO\n", O2S(valuePtr), + O2S(value2Ptr))); + mp_clear(&big1); + mp_clear(&big2); + goto divideByZero; + } + mp_init(&bigRemainder); + mp_div(&big1, &big2, &bigResult, &bigRemainder); + /* TODO: internals intrusion */ + if (!mp_iszero(&bigRemainder) + && (bigRemainder.sign != big2.sign)) { + /* Convert to Tcl's integer division rules */ + mp_sub_d(&bigResult, 1, &bigResult); + mp_add(&bigRemainder, &big2, &bigRemainder); + } + if (*pc == INST_MOD) { + mp_copy(&bigRemainder, &bigResult); + } + mp_clear(&bigRemainder); + break; + } + mp_clear(&big1); + mp_clear(&big2); + if (Tcl_IsShared(valuePtr)) { + objResultPtr = Tcl_NewBignumObj(&bigResult); + TRACE(("%s\n", O2S(objResultPtr))); + NEXT_INST_F(1, 2, 1); + } + Tcl_SetBignumObj(valuePtr, &bigResult); + TRACE(("%s\n", O2S(valuePtr))); + NEXT_INST_F(1, 1, 0); + } + } + + case INST_MOD: case INST_EXPON: { /* * Operands must be numeric and ints get converted to floats if * necessary. We compute value op value2. */ + double d1, d2; + double dResult = 0.0; /* Init. avoids compiler warning. */ + Tcl_Obj *valuePtr,*value2Ptr; +#if 0 Tcl_ObjType *t1Ptr, *t2Ptr; long i = 0, i2 = 0, quot; /* Init. avoids compiler warning. */ - double d1, d2; long iResult = 0; /* Init. avoids compiler warning. */ - double dResult = 0.0; /* Init. avoids compiler warning. */ int doDouble = 0; /* 1 if doing floating arithmetic */ Tcl_WideInt w, w2, wquot; Tcl_WideInt wResult = W0; /* Init. avoids compiler warning. */ int doWide = 0; /* 1 if doing wide arithmetic. */ - Tcl_Obj *valuePtr,*value2Ptr; int length; value2Ptr = *tosPtr; @@ -3994,20 +4690,6 @@ TclExecuteByteCode(interp, codePtr) case INST_MULT: dResult = d1 * d2; break; - case INST_DIV: -#ifndef IEEE_FLOATING_POINT - if (d2 == 0.0) { - TRACE(("%.6g %.6g => DIVIDE BY ZERO\n", d1, d2)); - goto divideByZero; - } -#endif - /* - * We presume that we are running with zero-divide unmasked if - * we're on an IEEE box. Otherwise, this statement might cause - * demons to fly out our noses. - */ - dResult = d1 / d2; - break; case INST_EXPON: if (d1==0.0 && d2<0.0) { TRACE(("%.6g %.6g => EXPONENT OF ZERO\n", d1, d2)); @@ -4175,261 +4857,345 @@ TclExecuteByteCode(interp, codePtr) } NEXT_INST_F(1, 1, 0); } - } - - case INST_UPLUS: { - /* - * Operand must be numeric. - */ - - double d; - Tcl_ObjType *tPtr; - Tcl_Obj *valuePtr; - - valuePtr = *tosPtr; - tPtr = valuePtr->typePtr; - if (IS_INTEGER_TYPE(tPtr) - || ((tPtr == &tclDoubleType) && (valuePtr->bytes == NULL))) { - /* - * We already have a numeric internal rep, either some kind of - * integer, or a "pure" double. (Need "pure" so that we know the - * string rep of the double would not prefer to be interpreted as - * an integer.) - */ - } else { - /* - * Otherwise, we need to generate a numeric internal rep. from - * the string rep. - */ - int length; - long i; /* Set but never used, needed in GET_WIDE_OR_INT */ - Tcl_WideInt w; - char *s = Tcl_GetStringFromObj(valuePtr, &length); - - if (TclLooksLikeInt(s, length)) { - GET_WIDE_OR_INT(result, valuePtr, i, w); - } else { - result = Tcl_GetDoubleFromObj((Tcl_Interp *) NULL, valuePtr, &d); - } - if (result != TCL_OK) { - TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n", - s, (tPtr? tPtr->name : "null"))); - IllegalExprOperandType(interp, pc, valuePtr); - goto checkForCatch; +#else + value2Ptr = *tosPtr; + valuePtr = *(tosPtr - 1); + result = Tcl_GetDoubleFromObj(NULL, valuePtr, &d1); + if (result != TCL_OK) { +#ifdef ACCEPT_NAN + if (valuePtr->typePtr == &tclDoubleType) { + /* NaN first argument -> result is also NaN */ + result = TCL_OK; + NEXT_INST_F(1, 1, 0); } - tPtr = valuePtr->typePtr; +#endif + TRACE(("%.20s %.20s => ILLEGAL 1st TYPE %s\n", + O2S(value2Ptr), O2S(valuePtr), + (valuePtr->typePtr? valuePtr->typePtr->name: "null"))); + IllegalExprOperandType(interp, pc, valuePtr); + goto checkForCatch; } - - /* - * Ensure that the operand's string rep is the same as the formatted - * version of its internal rep. This makes sure that "expr +000123" - * yields "83", not "000123". We implement this by _discarding_ the - * string rep since we know it will be regenerated, if needed later, - * by formatting the internal rep's value. - */ - - if (Tcl_IsShared(valuePtr)) { - if (tPtr == &tclIntType) { - TclNewLongObj(objResultPtr, valuePtr->internalRep.longValue); - } else if (tPtr == &tclWideIntType) { - Tcl_WideInt w; - - TclGetWide(w,valuePtr); - TclNewWideIntObj(objResultPtr, w); - } else { - TclNewDoubleObj(objResultPtr, valuePtr->internalRep.doubleValue); + result = Tcl_GetDoubleFromObj(NULL, value2Ptr, &d2); + if (result != TCL_OK) { +#ifdef ACCEPT_NAN + if (value2Ptr->typePtr == &tclDoubleType) { + /* NaN second argument -> result is also NaN */ + objResultPtr = value2Ptr; + result = TCL_OK; + NEXT_INST_F(1, 2, 1); } - TRACE_WITH_OBJ(("%s => ", O2S(objResultPtr)), objResultPtr); - NEXT_INST_F(1, 1, 1); - } else { - TclInvalidateStringRep(valuePtr); - TRACE_WITH_OBJ(("%s => ", O2S(valuePtr)), valuePtr); - NEXT_INST_F(1, 0, 0); +#endif + TRACE(("%.20s %.20s => ILLEGAL 2nd TYPE %s\n", + O2S(value2Ptr), O2S(valuePtr), + (value2Ptr->typePtr? value2Ptr->typePtr->name: "null"))); + IllegalExprOperandType(interp, pc, value2Ptr); + goto checkForCatch; } - } - - case INST_UMINUS: - case INST_LNOT: { - /* - * The operand must be numeric or a boolean string as accepted by - * Tcl_GetBooleanFromObj(). If the operand object is unshared modify - * it directly, otherwise create a copy to modify: this is "copy on - * write". Free any old string representation since it is now - * invalid. - */ - - double d; - int boolvar; - long i; - int negate_value = 1; - Tcl_WideInt w; - Tcl_ObjType *tPtr; - Tcl_Obj *valuePtr; - - valuePtr = *tosPtr; - tPtr = valuePtr->typePtr; - if (IS_INTEGER_TYPE(tPtr) - || ((tPtr == &tclDoubleType) && (valuePtr->bytes == NULL))) { - /* - * We already have a numeric internal rep, either some kind of - * integer, or a "pure" double. (Need "pure" so that we know the - * string rep of the double would not prefer to be interpreted as - * an integer.) - */ - } else { + if (valuePtr->typePtr == &tclDoubleType + || value2Ptr->typePtr == &tclDoubleType) { + /* At least one of the values is floating-point, so perform + * floating point calculations */ + switch (*pc) { + case INST_EXPON: + if (d1==0.0 && d2<0.0) { + TRACE(("%.6g %.6g => EXPONENT OF ZERO\n", d1, d2)); + goto exponOfZero; + } + dResult = pow(d1, d2); + break; + case INST_MOD: + if (valuePtr->typePtr == &tclDoubleType) { + TRACE(("%.20s %.20s => ILLEGAL 1st TYPE %s\n", + O2S(value2Ptr), O2S(valuePtr), (valuePtr->typePtr? + valuePtr->typePtr->name: "null"))); + IllegalExprOperandType(interp, pc, valuePtr); + } else { + TRACE(("%.20s %.20s => ILLEGAL 2nd TYPE %s\n", + O2S(value2Ptr), O2S(valuePtr), (value2Ptr->typePtr? + value2Ptr->typePtr->name: "null"))); + IllegalExprOperandType(interp, pc, value2Ptr); + } + result = TCL_ERROR; + goto checkForCatch; + } +#ifndef ACCEPT_NAN /* - * Otherwise, we need to generate a numeric internal rep. from - * the string rep. + * Check now for IEEE floating-point error. */ - int length; - char *s = Tcl_GetStringFromObj(valuePtr, &length); - if (TclLooksLikeInt(s, length)) { - GET_WIDE_OR_INT(result, valuePtr, i, w); - - /* - * An integer was parsed. If parsing a literal that is the - * smallest long value, then it would have been promoted to a - * wide since it would not fit in a long type without the - * leading '-'. Convert back to the smallest possible long. - */ - if ((result == TCL_OK) && - (*pc == INST_UMINUS) && - (valuePtr->typePtr == &tclWideIntType) && - (w == -Tcl_LongAsWide(LONG_MIN))) { - valuePtr->typePtr = &tclIntType; - valuePtr->internalRep.longValue = LONG_MIN; - negate_value = 0; - } - } else { - result = Tcl_GetDoubleFromObj(NULL, valuePtr, &d); - } - if (result == TCL_ERROR && *pc == INST_LNOT) { - result = Tcl_GetBooleanFromObj(NULL, valuePtr, &boolvar); - i = (long)boolvar; /* i is long, not int! */ - } - if (result != TCL_OK) { - TRACE(("\"%.20s\" => ILLEGAL TYPE %s\n", s, - (tPtr? tPtr->name : "null"))); - IllegalExprOperandType(interp, pc, valuePtr); + if (TclIsNaN(dResult)) { + TRACE(("%.20s %.20s => IEEE FLOATING PT ERROR\n", + O2S(valuePtr), O2S(value2Ptr))); + TclExprFloatError(interp, dResult); + result = TCL_ERROR; goto checkForCatch; } - tPtr = valuePtr->typePtr; - } - - if (*pc == INST_UMINUS) { +#endif if (Tcl_IsShared(valuePtr)) { - /* - * Create a new object. - */ - if (tPtr == &tclIntType) { - i = valuePtr->internalRep.longValue; - if (negate_value) { - i = -i; + TclNewDoubleObj(objResultPtr, dResult); + NEXT_INST_F(1, 2, 1); + } + TclSetDoubleObj(valuePtr, dResult); + NEXT_INST_F(1, 1, 0); + } else { + /* Both values are some kind of integer */ + /* TODO: optimize use of narrower native integers */ + mp_int big1, big2, bigResult, bigRemainder; + Tcl_GetBignumFromObj(NULL, valuePtr, &big1); + Tcl_GetBignumFromObj(NULL, value2Ptr, &big2); + mp_init(&bigResult); + switch (*pc) { + case INST_MOD: + if (mp_iszero(&big2)) { + TRACE(("%s %s => DIVIDE BY ZERO\n", O2S(valuePtr), + O2S(value2Ptr))); + mp_clear(&big1); + mp_clear(&big2); + goto divideByZero; + } + mp_init(&bigRemainder); + mp_div(&big1, &big2, &bigResult, &bigRemainder); + if (!mp_iszero(&bigRemainder) + && (bigRemainder.sign != big2.sign)) { + /* Convert to Tcl's integer division rules */ + mp_sub_d(&bigResult, 1, &bigResult); + mp_add(&bigRemainder, &big2, &bigRemainder); + } + if (*pc == INST_MOD) { + mp_copy(&bigRemainder, &bigResult); + } + mp_clear(&bigRemainder); + break; + case INST_EXPON: + if (mp_iszero(&big2)) { + /* Anything to the zero power is 1 */ + mp_clear(&big1); + mp_clear(&big2); + objResultPtr = eePtr->constants[1]; + NEXT_INST_F(1, 2, 1); + } + if (mp_iszero(&big1)) { + if (mp_cmp_d(&big2, 0) == MP_LT) { + TRACE(("%s %s => EXPONENT OF ZERO\n", O2S(valuePtr), + O2S(value2Ptr))); + mp_clear(&big1); + mp_clear(&big2); + goto exponOfZero; } - TclNewLongObj(objResultPtr, i); - TRACE_WITH_OBJ(("%ld => ", i), objResultPtr); - } else if (tPtr == &tclWideIntType) { - TclGetWide(w,valuePtr); - TclNewWideIntObj(objResultPtr, -w); - TRACE_WITH_OBJ((LLD" => ", w), objResultPtr); - } else { - d = valuePtr->internalRep.doubleValue; - TclNewDoubleObj(objResultPtr, -d); - TRACE_WITH_OBJ(("%.6g => ", d), objResultPtr); + mp_clear(&big1); + mp_clear(&big2); + objResultPtr = eePtr->constants[0]; + NEXT_INST_F(1, 2, 1); } - NEXT_INST_F(1, 1, 1); - } else { - /* - * valuePtr is unshared. Modify it directly. - */ - if (tPtr == &tclIntType) { - i = valuePtr->internalRep.longValue; - if (negate_value) { - i = -i; + if (mp_cmp_d(&big2, 0) == MP_LT) { + switch (mp_cmp_d(&big1, 1)) { + case MP_GT: + objResultPtr = eePtr->constants[0]; + break; + case MP_EQ: + objResultPtr = eePtr->constants[1]; + break; + case MP_LT: + mp_add_d(&big1, 1, &big1); + if (mp_cmp_d(&big1, 0) == MP_LT) { + objResultPtr = eePtr->constants[0]; + break; + } + mp_mod_2d(&big2, 1, &big2); + if (mp_iszero(&big2)) { + objResultPtr = eePtr->constants[1]; + } else { + TclNewIntObj(objResultPtr, -1); + } } - TclSetLongObj(valuePtr, i); - TRACE_WITH_OBJ(("%ld => ", i), valuePtr); - } else if (tPtr == &tclWideIntType) { - TclGetWide(w,valuePtr); - TclSetWideIntObj(valuePtr, -w); - TRACE_WITH_OBJ((LLD" => ", w), valuePtr); - } else { - d = valuePtr->internalRep.doubleValue; - TclSetDoubleObj(valuePtr, -d); - TRACE_WITH_OBJ(("%.6g => ", d), valuePtr); + mp_clear(&big1); + mp_clear(&big2); + NEXT_INST_F(1, 2, 1); } - NEXT_INST_F(1, 0, 0); + if (big2.used > 1) { + Tcl_SetObjResult(interp, + Tcl_NewStringObj("exponent too large", -1)); + mp_clear(&big1); + mp_clear(&big2); + goto checkForCatch; + } + mp_expt_d(&big1, big2.dp[0], &bigResult); + break; } - } else { /* *pc == INST_UMINUS */ - if ((tPtr == &tclIntType) || (tPtr == &tclBooleanType)) { - i = !valuePtr->internalRep.longValue; - TRACE_WITH_OBJ(("%ld => ", i), objResultPtr); - } else if (tPtr == &tclWideIntType) { - TclGetWide(w,valuePtr); - i = (w == W0); - TRACE_WITH_OBJ((LLD" => ", w), objResultPtr); - } else { - i = (valuePtr->internalRep.doubleValue == 0.0); - TRACE_WITH_OBJ(("%.6g => ", d), objResultPtr); + mp_clear(&big1); + mp_clear(&big2); + TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr))); + if (Tcl_IsShared(valuePtr)) { + objResultPtr = Tcl_NewBignumObj(&bigResult); + TRACE(("%s\n", O2S(objResultPtr))); + NEXT_INST_F(1, 2, 1); } - objResultPtr = eePtr->constants[i]; - NEXT_INST_F(1, 1, 1); + Tcl_SetBignumObj(valuePtr, &bigResult); + TRACE(("%s\n", O2S(valuePtr))); + NEXT_INST_F(1, 1, 0); } +#endif } - case INST_BITNOT: { - /* - * The operand must be an integer. If the operand object is unshared - * modify it directly, otherwise modify a copy. Free any old string - * representation since it is now invalid. - */ + case INST_LNOT: { + int b; + Tcl_Obj *valuePtr = *tosPtr; - Tcl_ObjType *tPtr; - Tcl_Obj *valuePtr; - Tcl_WideInt w; - long i; + /* TODO - check claim that taking address of b harms performance */ + /* TODO - consider optimization search for eePtr->constants */ + result = TclGetBooleanFromObj(NULL, valuePtr, &b); + if (result != TCL_OK) { + TRACE(("\"%.20s\" => ILLEGAL TYPE %s\n", O2S(valuePtr), + (valuePtr->typePtr? valuePtr->typePtr->name : "null"))); + IllegalExprOperandType(interp, pc, valuePtr); + goto checkForCatch; + } + /* TODO: Consider peephole opt. */ + objResultPtr = eePtr->constants[!b]; + NEXT_INST_F(1, 1, 1); + } - valuePtr = *tosPtr; - tPtr = valuePtr->typePtr; - if (!IS_INTEGER_TYPE(tPtr)) { - REQUIRE_WIDE_OR_INT(result, valuePtr, i, w); - if (result != TCL_OK) { /* try to convert to double */ - TRACE(("\"%.20s\" => ILLEGAL TYPE %s\n", - O2S(valuePtr), (tPtr? tPtr->name : "null"))); - IllegalExprOperandType(interp, pc, valuePtr); - goto checkForCatch; + case INST_BITNOT: { + mp_int big; + ClientData ptr; + int type; + Tcl_Obj *valuePtr = *tosPtr; + + result = GetNumberFromObj(NULL, valuePtr, &ptr, &type); + if ((result != TCL_OK) + || (type == TCL_NUMBER_NAN) || (type == TCL_NUMBER_DOUBLE)) { + /* ... ~$NonInteger => raise an error */ + result = TCL_ERROR; + TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n", O2S(valuePtr), + (valuePtr->typePtr? valuePtr->typePtr->name : "null"))); + IllegalExprOperandType(interp, pc, valuePtr); + goto checkForCatch; + } + if (type == TCL_NUMBER_LONG) { + long l = *((CONST long *)ptr); + if (Tcl_IsShared(valuePtr)) { + TclNewLongObj(objResultPtr, ~l); + NEXT_INST_F(1, 1, 1); } + TclSetLongObj(valuePtr, ~l); + NEXT_INST_F(1, 0, 0); } - - if (valuePtr->typePtr == &tclWideIntType) { - TclGetWide(w,valuePtr); +#ifndef NO_WIDE_TYPE + if (type == TCL_NUMBER_LONG) { + Tcl_WideInt w = *((CONST Tcl_WideInt *)ptr); if (Tcl_IsShared(valuePtr)) { - TclNewWideIntObj(objResultPtr, ~w); - TRACE(("0x%llx => (%llu)\n", w, ~w)); + objResultPtr = Tcl_NewWideIntObj(~w); NEXT_INST_F(1, 1, 1); - } else { - /* - * valuePtr is unshared. Modify it directly. - */ - TclSetWideIntObj(valuePtr, ~w); - TRACE(("0x%llx => (%llu)\n", w, ~w)); - NEXT_INST_F(1, 0, 0); } + Tcl_SetWideIntObj(valuePtr, ~w); + NEXT_INST_F(1, 0, 0); + } +#endif + if (Tcl_IsShared(valuePtr)) { + Tcl_GetBignumFromObj(NULL, valuePtr, &big); } else { - i = valuePtr->internalRep.longValue; + Tcl_GetBignumAndClearObj(NULL, valuePtr, &big); + } + /* ~a = - a - 1 */ + mp_neg(&big, &big); + mp_sub_d(&big, 1, &big); + if (Tcl_IsShared(valuePtr)) { + objResultPtr = Tcl_NewBignumObj(&big); + NEXT_INST_F(1, 1, 1); + } + Tcl_SetBignumObj(valuePtr, &big); + NEXT_INST_F(1, 0, 0); + } + + case INST_UMINUS: { + ClientData ptr; + int type; + Tcl_Obj *valuePtr = *tosPtr; + + result = GetNumberFromObj(NULL, valuePtr, &ptr, &type); + if ((result != TCL_OK) +#ifndef ACCEPT_NAN + || (type == TCL_NUMBER_NAN) +#endif + ) { + result = TCL_ERROR; + TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n", O2S(valuePtr), + (valuePtr->typePtr? valuePtr->typePtr->name : "null"))); + IllegalExprOperandType(interp, pc, valuePtr); + goto checkForCatch; + } + switch (type) { + case TCL_NUMBER_DOUBLE: { + double d; if (Tcl_IsShared(valuePtr)) { - TclNewLongObj(objResultPtr, ~i); - TRACE(("0x%lx => (%lu)\n", i, ~i)); + TclNewDoubleObj(objResultPtr, -(*((CONST double *)ptr))); NEXT_INST_F(1, 1, 1); + } + d = *((CONST double *)ptr); + TclSetDoubleObj(valuePtr, -d); + NEXT_INST_F(1, 0, 0); + } + case TCL_NUMBER_LONG: { + long l = *((CONST long *)ptr); + if (l != LONG_MIN) { + if (Tcl_IsShared(valuePtr)) { + TclNewLongObj(objResultPtr, -l); + NEXT_INST_F(1, 1, 1); + } + TclSetLongObj(valuePtr, -l); + NEXT_INST_F(1, 0, 0); + } + /* FALLTHROUGH */ + } +#ifndef NO_WIDE_TYPE + case TCL_NUMBER_WIDE: { + Tcl_WideInt w; + if (type == TCL_NUMBER_LONG) { + w = (Tcl_WideInt)(*((CONST long *)ptr)); } else { - /* - * valuePtr is unshared. Modify it directly. - */ - TclSetLongObj(valuePtr, ~i); - TRACE(("0x%lx => (%lu)\n", i, ~i)); + w = *((CONST Tcl_WideInt *)ptr); + } + if (w != LLONG_MIN) { + if (Tcl_IsShared(valuePtr)) { + objResultPtr = Tcl_NewWideIntObj(-w); + NEXT_INST_F(1, 1, 1); + } + Tcl_SetWideIntObj(valuePtr, -w); NEXT_INST_F(1, 0, 0); } + /* FALLTHROUGH */ + } +#endif + case TCL_NUMBER_BIG: { + mp_int big; + switch (type) { +#ifdef NO_WIDE_TYPE + case TCL_NUMBER_LONG: + TclBNInitBignumFromLong(&big, *((CONST long *)ptr)); + break; +#else + case TCL_NUMBER_WIDE: + TclBNInitBignumFromWideInt(&big, *((CONST Tcl_WideInt*)ptr)); + break; +#endif + case TCL_NUMBER_BIG: + if (Tcl_IsShared(valuePtr)) { + Tcl_GetBignumFromObj(NULL, valuePtr, &big); + } else { + Tcl_GetBignumAndClearObj(NULL, valuePtr, &big); + } + } + mp_neg(&big, &big); + if (Tcl_IsShared(valuePtr)) { + objResultPtr = Tcl_NewBignumObj(&big); + NEXT_INST_F(1, 1, 1); + } + Tcl_SetBignumObj(valuePtr, &big); + NEXT_INST_F(1, 0, 0); + } + case TCL_NUMBER_NAN: + /* -NaN => NaN */ + NEXT_INST_F(1, 0, 0); } } @@ -4441,109 +5207,78 @@ TclExecuteByteCode(interp, codePtr) Tcl_Panic("TclExecuteByteCode: obsolete INST_CALL_FUNC1 found"); } + case INST_UPLUS: case INST_TRY_CVT_TO_NUMERIC: { /* - * Try to convert the topmost stack object to an int or double object. - * This is done in order to support Tcl's policy of interpreting - * operands if at all possible as first integers, else floating-point - * numbers. + * Try to convert the topmost stack object to numeric object. + * This is done in order to support [expr]'s policy of interpreting + * operands if at all possible as numbers first, then strings. */ - double d; - char *s; - Tcl_ObjType *tPtr; - int converted, needNew, length; - Tcl_Obj *valuePtr; - long i; - Tcl_WideInt w; + ClientData ptr; + int type; + Tcl_Obj *valuePtr = *tosPtr; - valuePtr = *tosPtr; - tPtr = valuePtr->typePtr; - converted = 0; - if (IS_INTEGER_TYPE(tPtr) - || ((tPtr == &tclDoubleType) && (valuePtr->bytes == NULL))) { - /* - * We already have a numeric internal rep, either some kind of - * integer, or a "pure" double. (Need "pure" so that we know the - * string rep of the double would not prefer to be interpreted as - * an integer.) - */ - } else { - /* - * Otherwise, we need to generate a numeric internal rep. from - * the string rep. - */ - s = Tcl_GetStringFromObj(valuePtr, &length); - if (TclLooksLikeInt(s, length)) { - GET_WIDE_OR_INT(result, valuePtr, i, w); + if (GetNumberFromObj(NULL, valuePtr, &ptr, &type) != TCL_OK) { + if (*pc == INST_UPLUS) { + /* ... +$NonNumeric => raise an error */ + result = TCL_ERROR; + TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n", O2S(valuePtr), + (valuePtr->typePtr? valuePtr->typePtr->name : "null"))); + IllegalExprOperandType(interp, pc, valuePtr); + goto checkForCatch; } else { - result = Tcl_GetDoubleFromObj(NULL, valuePtr, &d); + /* ... TryConvertToNumeric($NonNumeric) is acceptable */ + TRACE(("\"%.20s\" => not numeric\n", O2S(valuePtr))); + NEXT_INST_F(1, 0, 0); } - if (result == TCL_OK) { - converted = 1; + } +#ifndef ACCEPT_NAN + if (type == TCL_NUMBER_NAN) { + result = TCL_ERROR; + if (*pc == INST_UPLUS) { + /* ... +$NonNumeric => raise an error */ + TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n", O2S(valuePtr), + (valuePtr->typePtr? valuePtr->typePtr->name : "null"))); + IllegalExprOperandType(interp, pc, valuePtr); + } else { + /* Numeric conversion of NaN -> error */ + TRACE(("\"%.20s\" => IEEE FLOATING PT ERROR\n", + O2S(objResultPtr))); + TclExprFloatError(interp, *((CONST double *)ptr)); } - result = TCL_OK; /* reset the result variable */ - tPtr = valuePtr->typePtr; + goto checkForCatch; } +#endif /* - * Ensure that the topmost stack object, if numeric, has a string rep - * the same as the formatted version of its internal rep. This is - * used, e.g., to make sure that "expr {0001}" yields "1", not - * "0001". We implement this by _discarding_ the string rep since we - * know it will be regenerated, if needed later, by formatting the - * internal rep's value. Also check if there has been an IEEE floating - * point error. + * Ensure that the numeric value has a string rep the same as + * the formatted version of its internal rep. This is used, e.g., + * to make sure that "expr {0001}" yields "1", not "0001". + * We implement this by _discarding_ the string rep since we + * know it will be regenerated, if needed later, by formatting + * the internal rep's value. */ - - objResultPtr = valuePtr; - needNew = 0; - if (IS_NUMERIC_TYPE(tPtr)) { - if (Tcl_IsShared(valuePtr)) { - if (valuePtr->bytes != NULL) { - /* - * We only need to make a copy of the object when it - * already had a string rep - */ - needNew = 1; - if (tPtr == &tclIntType) { - i = valuePtr->internalRep.longValue; - TclNewLongObj(objResultPtr, i); - } else if (tPtr == &tclWideIntType) { - TclGetWide(w,valuePtr); - TclNewWideIntObj(objResultPtr, w); - } else { - d = valuePtr->internalRep.doubleValue; - TclNewDoubleObj(objResultPtr, d); - } - tPtr = objResultPtr->typePtr; - } - } else { - Tcl_InvalidateStringRep(valuePtr); - } - - if (tPtr == &tclDoubleType) { - d = objResultPtr->internalRep.doubleValue; - if (IS_NAN(d)) { - TRACE(("\"%.20s\" => IEEE FLOATING PT ERROR\n", - O2S(objResultPtr))); - TclExprFloatError(interp, d); - result = TCL_ERROR; - goto checkForCatch; - } - } - converted = converted; /* lint, converted not used. */ - TRACE(("\"%.20s\" => numeric, %s, %s\n", O2S(valuePtr), - (converted? "converted" : "not converted"), - (needNew? "new Tcl_Obj" : "same Tcl_Obj"))); - } else { - TRACE(("\"%.20s\" => not numeric\n", O2S(valuePtr))); + if (valuePtr->bytes == NULL) { + TRACE(("\"%.20s\" => numeric, same Tcl_Obj\n", O2S(valuePtr))); + NEXT_INST_F(1, 0, 0); } - if (needNew) { + if (Tcl_IsShared(valuePtr)) { + /* + * Here we do some surgery within the Tcl_Obj internals. + * We want to copy the intrep, but not the string, so we + * temporarily hide the string so we do not copy it. + */ + char *savedString = valuePtr->bytes; + valuePtr->bytes = NULL; + objResultPtr = Tcl_DuplicateObj(valuePtr); + valuePtr->bytes = savedString; + TRACE(("\"%.20s\" => numeric, new Tcl_Obj\n", O2S(valuePtr))); NEXT_INST_F(1, 1, 1); - } else { - NEXT_INST_F(1, 0, 0); } + TclInvalidateStringRep(valuePtr); + TRACE(("\"%.20s\" => numeric, same Tcl_Obj\n", O2S(valuePtr))); + NEXT_INST_F(1, 0, 0); } case INST_BREAK: @@ -4779,7 +5514,7 @@ TclExecuteByteCode(interp, codePtr) NEXT_INST_F(1, 0, -1); case INST_PUSH_RETURN_CODE: - TclNewLongObj(objResultPtr, result); + TclNewIntObj(objResultPtr, result); TRACE(("=> %u\n", result)); NEXT_INST_F(1, 0, 1); @@ -4788,6 +5523,7 @@ TclExecuteByteCode(interp, codePtr) TRACE_WITH_OBJ(("=> "), objResultPtr); NEXT_INST_F(1, 0, 1); +/* TODO: normalize "valPtr" to "valuePtr" */ { int opnd, opnd2, allocateDict; Tcl_Obj *dictPtr, *valPtr; @@ -4874,34 +5610,19 @@ TclExecuteByteCode(interp, codePtr) break; } if (valPtr == NULL) { - Tcl_DictObjPut(NULL, dictPtr, *tosPtr, Tcl_NewLongObj(opnd)); - } else if (valPtr->typePtr == &tclWideIntType) { - Tcl_WideInt wvalue; - - Tcl_GetWideIntFromObj(NULL, valPtr, &wvalue); - Tcl_DictObjPut(NULL, dictPtr, *tosPtr, - Tcl_NewWideIntObj(wvalue + opnd)); - } else if (valPtr->typePtr == &tclIntType) { - long value; - - Tcl_GetLongFromObj(NULL, valPtr, &value); - Tcl_DictObjPut(NULL, dictPtr, *tosPtr, - Tcl_NewLongObj(value + opnd)); + Tcl_DictObjPut(NULL, dictPtr, *tosPtr, Tcl_NewIntObj(opnd)); } else { - long value = 0; /* stop compiler warning */ - Tcl_WideInt wvalue; - - REQUIRE_WIDE_OR_INT(result, valPtr, value, wvalue); - if (result != TCL_OK) { - break; + Tcl_Obj *incrPtr = Tcl_NewIntObj(opnd); + Tcl_IncrRefCount(incrPtr); + if (Tcl_IsShared(valPtr)) { + valPtr = Tcl_DuplicateObj(valPtr); + Tcl_DictObjPut(NULL, dictPtr, *tosPtr, valPtr); } - if (valPtr->typePtr == &tclWideIntType) { - Tcl_DictObjPut(NULL, dictPtr, *tosPtr, - Tcl_NewWideIntObj(wvalue + opnd)); - } else { - Tcl_DictObjPut(NULL, dictPtr, *tosPtr, - Tcl_NewLongObj(value + opnd)); + result = TclIncrObj(interp, valPtr, incrPtr); + if (result == TCL_OK) { + Tcl_InvalidateStringRep(dictPtr); } + Tcl_DecrRefCount(incrPtr); } break; case INST_DICT_UNSET: @@ -5134,7 +5855,8 @@ TclExecuteByteCode(interp, codePtr) } TRACE_APPEND(("\"%.30s\" \"%.30s\" %d", O2S(*(tosPtr-1)), O2S(*tosPtr), done)); - objResultPtr = Tcl_NewBooleanObj(done); + objResultPtr = eePtr->constants[done]; + /*TODO: consider opt like INST_FOREACH_STEP4 */ NEXT_INST_F(5, 0, 1); case INST_DICT_DONE: @@ -5722,116 +6444,38 @@ IllegalExprOperandType(interp, pc, opndPtr) Tcl_Obj *opndPtr; /* Points to the operand holding the value * with the illegal type. */ { - unsigned char opCode = *pc; - CONST char *operator = operatorStrings[opCode - INST_LOR]; - if (opCode == INST_EXPON) { + ClientData ptr; + int type; + unsigned char opcode = *pc; + CONST char *description, *operator = operatorStrings[opcode - INST_LOR]; + Tcl_Obj *msg = Tcl_NewObj(); + + if (opcode == INST_EXPON) { operator = "**"; } - Tcl_SetObjResult(interp, Tcl_NewObj()); - if ((opndPtr->bytes == NULL) || (opndPtr->length == 0)) { - Tcl_AppendResult(interp, "can't use empty string as operand of \"", - operator, "\"", (char *) NULL); - } else { - char *msg = "non-numeric string"; - char *s, *p; - int length; - int looksLikeInt = 0; - - s = Tcl_GetStringFromObj(opndPtr, &length); - p = s; - /* - * strtod() isn't at all consistent about detecting Inf and NaN - * between platforms. - */ - if (length == 3) { - if ((s[0]=='n' || s[0]=='N') && (s[1]=='a' || s[1]=='A') && - (s[2]=='n' || s[2]=='N')) { - msg = "non-numeric floating-point value"; - goto makeErrorMessage; - } - if ((s[0]=='i' || s[0]=='I') && (s[1]=='n' || s[1]=='N') && - (s[2]=='f' || s[2]=='F')) { - msg = "infinite floating-point value"; - goto makeErrorMessage; - } - } - - /* - * We cannot use TclLooksLikeInt here because it passes strings like - * "10;" [Bug 587140]. We'll accept as "looking like ints" for the - * present purposes any string that looks formally like a - * (decimal|octal|hex) integer. - */ - - while (length && isspace(UCHAR(*p))) { - length--; - p++; - } - if (length && ((*p == '+') || (*p == '-'))) { - length--; - p++; - } - if (length) { - if ((*p == '0') && ((*(p+1) == 'x') || (*(p+1) == 'X'))) { - p += 2; - length -= 2; - looksLikeInt = ((length > 0) && isxdigit(UCHAR(*p))); - if (looksLikeInt) { - length--; - p++; - while (length && isxdigit(UCHAR(*p))) { - length--; - p++; - } - } - } else { - looksLikeInt = (length && isdigit(UCHAR(*p))); - if (looksLikeInt) { - length--; - p++; - while (length && isdigit(UCHAR(*p))) { - length--; - p++; - } - } - } - while (length && isspace(UCHAR(*p))) { - length--; - p++; - } - looksLikeInt = !length; - } - if (looksLikeInt) { - /* - * If something that looks like an integer could not be converted, - * then it *must* be a bad octal or too large to represent [Bug - * 542588]. - */ - - if (TclCheckBadOctal(NULL, s)) { - msg = "invalid octal number"; - } else { - msg = "integer value too large to represent"; - Tcl_SetErrorCode(interp, "ARITH", "IOVERFLOW", - "integer value too large to represent", (char *) NULL); - } + if (GetNumberFromObj(NULL, opndPtr, &ptr, &type) != TCL_OK) { + int numBytes; + CONST char *bytes = Tcl_GetStringFromObj(opndPtr, &numBytes); + if (numBytes == 0) { + description = "empty string"; + } else if (TclCheckBadOctal(NULL, bytes)) { + description = "invalid octal number"; } else { - /* - * See if the operand can be interpreted as a double in order to - * improve the error message. - */ - - double d; - - if (Tcl_GetDouble((Tcl_Interp *) NULL, s, &d) == TCL_OK) { - msg = "floating-point value"; - } + description = "non-numeric string"; } - makeErrorMessage: - Tcl_AppendResult(interp, "can't use ", msg, " as operand of \"", - operator, "\"", (char *) NULL); + } else if (type == TCL_NUMBER_NAN) { + description = "non-numeric floating-point value"; + } else if (type == TCL_NUMBER_DOUBLE) { + description = "floating-point value"; + } else { + /* TODO: No caller needs this. Eliminate? */ + description = "(big) integer"; } + + TclObjPrintf(NULL, msg, "can't use %s as operand of \"%s\"", + description, operator); + Tcl_SetObjResult(interp, msg); } /* @@ -6054,6 +6698,7 @@ GetOpcodeName(pc) } #endif /* TCL_COMPILE_DEBUG */ + /* *---------------------------------------------------------------------- * @@ -6079,11 +6724,11 @@ TclExprFloatError(interp, value) { CONST char *s; - if ((errno == EDOM) || IS_NAN(value)) { + if ((errno == EDOM) || TclIsNaN(value)) { s = "domain error: argument not in valid range"; Tcl_SetObjResult(interp, Tcl_NewStringObj(s, -1)); Tcl_SetErrorCode(interp, "ARITH", "DOMAIN", s, (char *) NULL); - } else if ((errno == ERANGE) || IS_INF(value)) { + } else if ((errno == ERANGE) || TclIsInfinite(value)) { if (value == 0.0) { s = "floating-point value too small to represent"; Tcl_SetObjResult(interp, Tcl_NewStringObj(s, -1)); @@ -6571,6 +7216,7 @@ StringForResultCode(result) return buf; } #endif /* TCL_COMPILE_DEBUG */ +#if 0 /* *---------------------------------------------------------------------- @@ -6706,3 +7352,4 @@ ExponLong(i, i2, errExpon) } return result * i; } +#endif |