diff options
Diffstat (limited to 'generic/tclObj.c')
-rw-r--r-- | generic/tclObj.c | 1561 |
1 files changed, 999 insertions, 562 deletions
diff --git a/generic/tclObj.c b/generic/tclObj.c index 531a256..e496b1e 100644 --- a/generic/tclObj.c +++ b/generic/tclObj.c @@ -4,19 +4,20 @@ * This file contains Tcl object-related functions that are used by many * Tcl commands. * - * Copyright (c) 1995-1997 Sun Microsystems, Inc. - * Copyright (c) 1999 by Scriptics Corporation. - * Copyright (c) 2001 by ActiveState Corporation. - * Copyright (c) 2005 by Kevin B. Kenny. All rights reserved. - * Copyright (c) 2007 Daniel A. Steffen <das@users.sourceforge.net> + * Copyright © 1995-1997 Sun Microsystems, Inc. + * Copyright © 1999 Scriptics Corporation. + * Copyright © 2001 ActiveState Corporation. + * Copyright © 2005 Kevin B. Kenny. All rights reserved. + * Copyright © 2007 Daniel A. Steffen <das@users.sourceforge.net> * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ #include "tclInt.h" -#include "tommath.h" +#include "tclTomMath.h" #include <math.h> +#include <assert.h> /* * Table of all object types. @@ -37,7 +38,7 @@ Tcl_Obj *tclFreeObjList = NULL; * TclNewObj macro, however, so must be visible. */ -#ifdef TCL_THREADS +#if TCL_THREADS MODULE_SCOPE Tcl_Mutex tclObjMutex; Tcl_Mutex tclObjMutex; #endif @@ -49,9 +50,8 @@ Tcl_Mutex tclObjMutex; */ char tclEmptyString = '\0'; -char *tclEmptyStringRep = &tclEmptyString; -#if defined(TCL_MEM_DEBUG) && defined(TCL_THREADS) +#if TCL_THREADS && defined(TCL_MEM_DEBUG) /* * Structure for tracking the source file and line number where a given * Tcl_Obj was allocated. We also track the pointer to the Tcl_Obj itself, @@ -76,7 +76,7 @@ typedef struct ObjData { * The structure defined below is used in this file only. */ -typedef struct ThreadSpecificData { +typedef struct { Tcl_HashTable *lineCLPtr; /* This table remembers for each Tcl_Obj * generated by a call to the function * TclSubstTokens() from a literal text @@ -88,7 +88,7 @@ typedef struct ThreadSpecificData { * tclCompile.h for the definition of this * structure, and for references to all * related places in the core. */ -#if defined(TCL_MEM_DEBUG) && defined(TCL_THREADS) +#if TCL_THREADS && defined(TCL_MEM_DEBUG) Tcl_HashTable *objThreadMap;/* Thread local table that is used to check * that a Tcl_Obj was not allocated by some * other thread. */ @@ -97,7 +97,7 @@ typedef struct ThreadSpecificData { static Tcl_ThreadDataKey dataKey; -static void TclThreadFinalizeContLines(ClientData clientData); +static void TclThreadFinalizeContLines(void *clientData); static ThreadSpecificData *TclGetContLineTable(void); /* @@ -157,7 +157,7 @@ typedef struct PendingObjData { /* * Macro to set up the local reference to the deletion context. */ -#ifndef TCL_THREADS +#if !TCL_THREADS static PendingObjData pendingObjData; #define ObjInitDeletionContext(contextPtr) \ PendingObjData *const contextPtr = &pendingObjData @@ -169,7 +169,7 @@ static __thread PendingObjData pendingObjData; static Tcl_ThreadDataKey pendingObjDataKey; #define ObjInitDeletionContext(contextPtr) \ PendingObjData *const contextPtr = \ - Tcl_GetThreadData(&pendingObjDataKey, sizeof(PendingObjData)) + (PendingObjData *)Tcl_GetThreadData(&pendingObjDataKey, sizeof(PendingObjData)) #endif /* @@ -182,26 +182,12 @@ static Tcl_ThreadDataKey pendingObjDataKey; *temp = bignum; \ (objPtr)->internalRep.twoPtrValue.ptr1 = temp; \ (objPtr)->internalRep.twoPtrValue.ptr2 = INT2PTR(-1); \ - } else { \ - if ((bignum).alloc > 0x7FFF) { \ - mp_shrink(&(bignum)); \ - } \ + } else if (((bignum).alloc <= 0x7FFF) || (mp_shrink(&(bignum))) == MP_OKAY) { \ (objPtr)->internalRep.twoPtrValue.ptr1 = (void *)(bignum).dp; \ (objPtr)->internalRep.twoPtrValue.ptr2 = INT2PTR(((bignum).sign << 30) \ | ((bignum).alloc << 15) | ((bignum).used)); \ } -#define UNPACK_BIGNUM(objPtr, bignum) \ - if ((objPtr)->internalRep.twoPtrValue.ptr2 == INT2PTR(-1)) { \ - (bignum) = *((mp_int *) ((objPtr)->internalRep.twoPtrValue.ptr1)); \ - } else { \ - (bignum).dp = (objPtr)->internalRep.twoPtrValue.ptr1; \ - (bignum).sign = PTR2INT((objPtr)->internalRep.twoPtrValue.ptr2) >> 30; \ - (bignum).alloc = \ - (PTR2INT((objPtr)->internalRep.twoPtrValue.ptr2) >> 15) & 0x7FFF; \ - (bignum).used = PTR2INT((objPtr)->internalRep.twoPtrValue.ptr2) & 0x7FFF; \ - } - /* * Prototypes for functions defined later in this file: */ @@ -211,9 +197,8 @@ static int SetDoubleFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr); static int SetIntFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr); static void UpdateStringOfDouble(Tcl_Obj *objPtr); static void UpdateStringOfInt(Tcl_Obj *objPtr); -#ifndef TCL_WIDE_INT_IS_LONG -static void UpdateStringOfWideInt(Tcl_Obj *objPtr); -static int SetWideIntFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr); +#if !defined(TCL_NO_DEPRECATED) && TCL_MAJOR_VERSION < 9 && !defined(TCL_WIDE_INT_IS_LONG) +static void UpdateStringOfOldInt(Tcl_Obj *objPtr); #endif static void FreeBignum(Tcl_Obj *objPtr); static void DupBignum(Tcl_Obj *objPtr, Tcl_Obj *copyPtr); @@ -243,6 +228,7 @@ static int SetCmdNameFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr); * implementations. */ +#if !defined(TCL_NO_DEPRECATED) && TCL_MAJOR_VERSION < 9 static const Tcl_ObjType oldBooleanType = { "boolean", /* name */ NULL, /* freeIntRepProc */ @@ -250,6 +236,7 @@ static const Tcl_ObjType oldBooleanType = { NULL, /* updateStringProc */ TclSetBooleanFromAny /* setFromAnyProc */ }; +#endif const Tcl_ObjType tclBooleanType = { "booleanString", /* name */ NULL, /* freeIntRepProc */ @@ -265,19 +252,23 @@ const Tcl_ObjType tclDoubleType = { SetDoubleFromAny /* setFromAnyProc */ }; const Tcl_ObjType tclIntType = { +#if defined(TCL_NO_DEPRECATED) || TCL_MAJOR_VERSION > 8 || defined(TCL_WIDE_INT_IS_LONG) "int", /* name */ +#else + "wideInt", /* name, keeping maximum compatibility with Tcl 8.6 on 32-bit platforms*/ +#endif NULL, /* freeIntRepProc */ NULL, /* dupIntRepProc */ UpdateStringOfInt, /* updateStringProc */ SetIntFromAny /* setFromAnyProc */ }; -#ifndef TCL_WIDE_INT_IS_LONG -const Tcl_ObjType tclWideIntType = { - "wideInt", /* name */ +#if !defined(TCL_NO_DEPRECATED) && TCL_MAJOR_VERSION < 9 && !defined(TCL_WIDE_INT_IS_LONG) +static const Tcl_ObjType oldIntType = { + "int", /* name */ NULL, /* freeIntRepProc */ NULL, /* dupIntRepProc */ - UpdateStringOfWideInt, /* updateStringProc */ - SetWideIntFromAny /* setFromAnyProc */ + UpdateStringOfOldInt, /* updateStringProc */ + SetIntFromAny /* setFromAnyProc */ }; #endif const Tcl_ObjType tclBignumType = { @@ -345,7 +336,7 @@ typedef struct ResolvedCmdName { * reference (not the namespace that contains * the referenced command). NULL if the name * is fully qualified.*/ - long refNsId; /* refNsPtr's unique namespace id. Used to + unsigned long refNsId; /* refNsPtr's unique namespace id. Used to * verify that refNsPtr is still valid (e.g., * it's possible that the cmd's containing * namespace was deleted and a new one created @@ -361,7 +352,7 @@ typedef struct ResolvedCmdName { * incremented; if so, the cmd was renamed, * deleted, hidden, or exposed, and so the * pointer is invalid. */ - int refCount; /* Reference count: 1 for each cmdName object + size_t refCount; /* Reference count: 1 for each cmdName object * that has a pointer to this ResolvedCmdName * structure as its internal rep. This * structure can be freed when refCount @@ -396,21 +387,23 @@ TclInitObjSubsystem(void) Tcl_RegisterObjType(&tclByteArrayType); Tcl_RegisterObjType(&tclDoubleType); - Tcl_RegisterObjType(&tclEndOffsetType); - Tcl_RegisterObjType(&tclIntType); +#if (TCL_UTF_MAX < 4) || !defined(TCL_NO_DEPRECATED) Tcl_RegisterObjType(&tclStringType); +#endif Tcl_RegisterObjType(&tclListType); Tcl_RegisterObjType(&tclDictType); Tcl_RegisterObjType(&tclByteCodeType); - Tcl_RegisterObjType(&tclArraySearchType); Tcl_RegisterObjType(&tclCmdNameType); Tcl_RegisterObjType(&tclRegexpType); Tcl_RegisterObjType(&tclProcBodyType); /* For backward compatibility only ... */ +#if !defined(TCL_NO_DEPRECATED) && TCL_MAJOR_VERSION < 9 + Tcl_RegisterObjType(&tclIntType); +#if !defined(TCL_WIDE_INT_IS_LONG) + Tcl_RegisterObjType(&oldIntType); +#endif Tcl_RegisterObjType(&oldBooleanType); -#ifndef TCL_WIDE_INT_IS_LONG - Tcl_RegisterObjType(&tclWideIntType); #endif #ifdef TCL_COMPILE_STATS @@ -448,7 +441,7 @@ TclInitObjSubsystem(void) void TclFinalizeThreadObjects(void) { -#if defined(TCL_MEM_DEBUG) && defined(TCL_THREADS) +#if TCL_THREADS && defined(TCL_MEM_DEBUG) Tcl_HashEntry *hPtr; Tcl_HashSearch hSearch; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); @@ -457,7 +450,7 @@ TclFinalizeThreadObjects(void) if (tablePtr != NULL) { for (hPtr = Tcl_FirstHashEntry(tablePtr, &hSearch); hPtr != NULL; hPtr = Tcl_NextHashEntry(&hSearch)) { - ObjData *objData = Tcl_GetHashValue(hPtr); + ObjData *objData = (ObjData *)Tcl_GetHashValue(hPtr); if (objData != NULL) { ckfree(objData); @@ -576,7 +569,7 @@ TclContinuationsEnter( ThreadSpecificData *tsdPtr = TclGetContLineTable(); Tcl_HashEntry *hPtr = Tcl_CreateHashEntry(tsdPtr->lineCLPtr, objPtr, &newEntry); - ContLineLoc *clLocPtr = (ContLineLoc *)ckalloc(TclOffset(ContLineLoc, loc) + (num + 1U) *sizeof(int)); + ContLineLoc *clLocPtr = (ContLineLoc *)ckalloc(offsetof(ContLineLoc, loc) + (num + 1U) *sizeof(int)); if (!newEntry) { /* @@ -733,7 +726,7 @@ TclContinuationsCopy( Tcl_FindHashEntry(tsdPtr->lineCLPtr, originObjPtr); if (hPtr) { - ContLineLoc *clLocPtr = Tcl_GetHashValue(hPtr); + ContLineLoc *clLocPtr = (ContLineLoc *)Tcl_GetHashValue(hPtr); TclContinuationsEnter(objPtr, clLocPtr->num, clLocPtr->loc); } @@ -769,7 +762,7 @@ TclContinuationsGet( if (!hPtr) { return NULL; } - return Tcl_GetHashValue(hPtr); + return (ContLineLoc *)Tcl_GetHashValue(hPtr); } /* @@ -792,7 +785,7 @@ TclContinuationsGet( static void TclThreadFinalizeContLines( - ClientData clientData) + TCL_UNUSED(void *)) { /* * Release the hashtable tracking invisible continuation lines. @@ -884,7 +877,7 @@ Tcl_AppendAllObjTypes( * Get the test for a valid list out of the way first. */ - if (TclListObjLength(interp, objPtr, &numElems) != TCL_OK) { + if (TclListObjLengthM(interp, objPtr, &numElems) != TCL_OK) { return TCL_ERROR; } @@ -897,7 +890,7 @@ Tcl_AppendAllObjTypes( for (hPtr = Tcl_FirstHashEntry(&typeTable, &search); hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { Tcl_ListObjAppendElement(NULL, objPtr, - Tcl_NewStringObj(Tcl_GetHashKey(&typeTable, hPtr), -1)); + Tcl_NewStringObj((char *)Tcl_GetHashKey(&typeTable, hPtr), -1)); } Tcl_MutexUnlock(&tableMutex); return TCL_OK; @@ -930,7 +923,7 @@ Tcl_GetObjType( Tcl_MutexLock(&tableMutex); hPtr = Tcl_FindHashEntry(&typeTable, typeName); if (hPtr != NULL) { - typePtr = Tcl_GetHashValue(hPtr); + typePtr = (const Tcl_ObjType *)Tcl_GetHashValue(hPtr); } Tcl_MutexUnlock(&tableMutex); return typePtr; @@ -1001,11 +994,11 @@ Tcl_ConvertToType( *-------------------------------------------------------------- */ +#if TCL_THREADS && defined(TCL_MEM_DEBUG) void TclDbDumpActiveObjects( FILE *outFile) { -#if defined(TCL_MEM_DEBUG) && defined(TCL_THREADS) Tcl_HashSearch hSearch; Tcl_HashEntry *hPtr; Tcl_HashTable *tablePtr; @@ -1017,7 +1010,7 @@ TclDbDumpActiveObjects( fprintf(outFile, "total objects: %d\n", tablePtr->numEntries); for (hPtr = Tcl_FirstHashEntry(tablePtr, &hSearch); hPtr != NULL; hPtr = Tcl_NextHashEntry(&hSearch)) { - ObjData *objData = Tcl_GetHashValue(hPtr); + ObjData *objData = (ObjData *)Tcl_GetHashValue(hPtr); if (objData != NULL) { fprintf(outFile, @@ -1030,8 +1023,14 @@ TclDbDumpActiveObjects( } } } -#endif } +#else +void +TclDbDumpActiveObjects( + TCL_UNUSED(FILE *)) +{ +} +#endif /* *---------------------------------------------------------------------- @@ -1061,11 +1060,10 @@ TclDbInitNewObj( * debugging. */ { objPtr->refCount = 0; - objPtr->bytes = tclEmptyStringRep; - objPtr->length = 0; objPtr->typePtr = NULL; + TclInitStringRep(objPtr, NULL, 0); -#ifdef TCL_THREADS +#if TCL_THREADS /* * Add entry to a thread local map used to check if a Tcl_Obj was * allocated by the currently executing thread. @@ -1202,10 +1200,8 @@ Tcl_DbNewObj( Tcl_Obj * Tcl_DbNewObj( - const char *file, /* The name of the source file calling this - * function; used for debugging. */ - int line) /* Line number in the source file; used for - * debugging. */ + TCL_UNUSED(const char *) /*file*/, + TCL_UNUSED(int) /*line*/) { return Tcl_NewObj(); } @@ -1301,7 +1297,7 @@ TclFreeObj( ObjInitDeletionContext(context); -# ifdef TCL_THREADS +#if TCL_THREADS /* * Check to make sure that the Tcl_Obj was allocated by the current * thread. Don't do this check when shutting down since thread local @@ -1323,7 +1319,7 @@ TclFreeObj( * As the Tcl_Obj is going to be deleted we remove the entry. */ - ObjData *objData = Tcl_GetHashValue(hPtr); + ObjData *objData = (ObjData *)Tcl_GetHashValue(hPtr); if (objData != NULL) { ckfree(objData); @@ -1379,7 +1375,7 @@ TclFreeObj( PopObjToDelete(context, objToFree); TCL_DTRACE_OBJ_FREE(objToFree); - TclFreeIntRep(objToFree); + TclFreeInternalRep(objToFree); Tcl_MutexLock(&tclObjMutex); ckfree(objToFree); @@ -1598,7 +1594,7 @@ TclSetDuplicateObj( Tcl_Panic("%s called with shared object", "TclSetDuplicateObj"); } TclInvalidateStringRep(dupPtr); - TclFreeIntRep(dupPtr); + TclFreeInternalRep(dupPtr); SetDuplicateObj(dupPtr, objPtr); } @@ -1623,37 +1619,36 @@ TclSetDuplicateObj( *---------------------------------------------------------------------- */ +#undef Tcl_GetString char * Tcl_GetString( Tcl_Obj *objPtr) /* Object whose string rep byte pointer should * be returned. */ { - if (objPtr->bytes != NULL) { - return objPtr->bytes; - } - - /* - * Note we do not check for objPtr->typePtr == NULL. An invariant of - * a properly maintained Tcl_Obj is that at least one of objPtr->bytes - * and objPtr->typePtr must not be NULL. If broken extensions fail to - * maintain that invariant, we can crash here. - */ - - if (objPtr->typePtr->updateStringProc == NULL) { + if (objPtr->bytes == NULL) { /* - * Those Tcl_ObjTypes which choose not to define an updateStringProc - * must be written in such a way that (objPtr->bytes) never becomes - * NULL. This panic was added in Tcl 8.1. + * Note we do not check for objPtr->typePtr == NULL. An invariant + * of a properly maintained Tcl_Obj is that at least one of + * objPtr->bytes and objPtr->typePtr must not be NULL. If broken + * extensions fail to maintain that invariant, we can crash here. */ - Tcl_Panic("UpdateStringProc should not be invoked for type %s", - objPtr->typePtr->name); - } - objPtr->typePtr->updateStringProc(objPtr); - if (objPtr->bytes == NULL || objPtr->length < 0 - || objPtr->bytes[objPtr->length] != '\0') { - Tcl_Panic("UpdateStringProc for type '%s' " - "failed to create a valid string rep", objPtr->typePtr->name); + if (objPtr->typePtr->updateStringProc == NULL) { + /* + * Those Tcl_ObjTypes which choose not to define an + * updateStringProc must be written in such a way that + * (objPtr->bytes) never becomes NULL. + */ + Tcl_Panic("UpdateStringProc should not be invoked for type %s", + objPtr->typePtr->name); + } + objPtr->typePtr->updateStringProc(objPtr); + if (objPtr->bytes == NULL || objPtr->length < 0 + || objPtr->bytes[objPtr->length] != '\0') { + Tcl_Panic("UpdateStringProc for type '%s' " + "failed to create a valid string rep", + objPtr->typePtr->name); + } } return objPtr->bytes; } @@ -1661,7 +1656,7 @@ Tcl_GetString( /* *---------------------------------------------------------------------- * - * Tcl_GetStringFromObj -- + * Tcl_GetStringFromObj/TclGetStringFromObj -- * * Returns the string representation's byte array pointer and length for * an object. @@ -1681,6 +1676,7 @@ Tcl_GetString( *---------------------------------------------------------------------- */ +#undef Tcl_GetStringFromObj char * Tcl_GetStringFromObj( Tcl_Obj *objPtr, /* Object whose string rep byte pointer should @@ -1689,11 +1685,180 @@ Tcl_GetStringFromObj( * rep's byte array length should * be stored. * If NULL, no length is stored. */ { - (void) TclGetString(objPtr); + if (objPtr->bytes == NULL) { + /* + * Note we do not check for objPtr->typePtr == NULL. An invariant + * of a properly maintained Tcl_Obj is that at least one of + * objPtr->bytes and objPtr->typePtr must not be NULL. If broken + * extensions fail to maintain that invariant, we can crash here. + */ + + if (objPtr->typePtr->updateStringProc == NULL) { + /* + * Those Tcl_ObjTypes which choose not to define an + * updateStringProc must be written in such a way that + * (objPtr->bytes) never becomes NULL. + */ + Tcl_Panic("UpdateStringProc should not be invoked for type %s", + objPtr->typePtr->name); + } + objPtr->typePtr->updateStringProc(objPtr); + if (objPtr->bytes == NULL || objPtr->length < 0 + || objPtr->bytes[objPtr->length] != '\0') { + Tcl_Panic("UpdateStringProc for type '%s' " + "failed to create a valid string rep", + objPtr->typePtr->name); + } + } + if (lengthPtr != NULL) { + *lengthPtr = objPtr->length; + } + return objPtr->bytes; +} + +#undef TclGetStringFromObj +char * +TclGetStringFromObj( + Tcl_Obj *objPtr, /* Object whose string rep byte pointer should + * be returned. */ + size_t *lengthPtr) /* If non-NULL, the location where the string + * rep's byte array length should * be stored. + * If NULL, no length is stored. */ +{ + if (objPtr->bytes == NULL) { + /* + * Note we do not check for objPtr->typePtr == NULL. An invariant + * of a properly maintained Tcl_Obj is that at least one of + * objPtr->bytes and objPtr->typePtr must not be NULL. If broken + * extensions fail to maintain that invariant, we can crash here. + */ + if (objPtr->typePtr->updateStringProc == NULL) { + /* + * Those Tcl_ObjTypes which choose not to define an + * updateStringProc must be written in such a way that + * (objPtr->bytes) never becomes NULL. + */ + Tcl_Panic("UpdateStringProc should not be invoked for type %s", + objPtr->typePtr->name); + } + objPtr->typePtr->updateStringProc(objPtr); + if (objPtr->bytes == NULL || objPtr->length < 0 + || objPtr->bytes[objPtr->length] != '\0') { + Tcl_Panic("UpdateStringProc for type '%s' " + "failed to create a valid string rep", + objPtr->typePtr->name); + } + } if (lengthPtr != NULL) { +#if TCL_MAJOR_VERSION > 8 *lengthPtr = objPtr->length; +#else + *lengthPtr = ((size_t)(unsigned)(objPtr->length + 1)) - 1; +#endif + } + return objPtr->bytes; +} + + +/* + *---------------------------------------------------------------------- + * + * Tcl_InitStringRep -- + * + * This function is called in several configurations to provide all + * the tools needed to set an object's string representation. The + * function is determined by the arguments. + * + * (objPtr->bytes != NULL && bytes != NULL) || (numBytes < 0) + * Invalid call -- panic! + * + * objPtr->bytes == NULL && bytes == NULL && numBytes >= 0 + * Allocation only - allocate space for (numBytes+1) chars. + * store in objPtr->bytes and return. Also sets + * objPtr->length to 0 and objPtr->bytes[0] to NUL. + * + * objPtr->bytes == NULL && bytes != NULL && numBytes >= 0 + * Allocate and copy. bytes is assumed to point to chars to + * copy into the string rep. objPtr->length = numBytes. Allocate + * array of (numBytes + 1) chars. store in objPtr->bytes. Copy + * numBytes chars from bytes to objPtr->bytes; Set + * objPtr->bytes[numBytes] to NUL and return objPtr->bytes. + * Caller must guarantee there are numBytes chars at bytes to + * be copied. + * + * objPtr->bytes != NULL && bytes == NULL && numBytes >= 0 + * Truncate. Set objPtr->length to numBytes and + * objPr->bytes[numBytes] to NUL. Caller has to guarantee + * that a prior allocating call allocated enough bytes for + * this to be valid. Return objPtr->bytes. + * + * Caller is expected to ascertain that the bytes copied into + * the string rep make up complete valid UTF-8 characters. + * + * Results: + * A pointer to the string rep of objPtr. + * + * Side effects: + * As described above. + * + *---------------------------------------------------------------------- + */ + +char * +Tcl_InitStringRep( + Tcl_Obj *objPtr, /* Object whose string rep is to be set */ + const char *bytes, + unsigned int numBytes) +{ + assert(objPtr->bytes == NULL || bytes == NULL); + + if (numBytes > INT_MAX) { + Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX); + } + + if (objPtr->bytes == NULL) { + /* Start with no string rep */ + if (numBytes == 0) { + TclInitStringRep(objPtr, NULL, 0); + return objPtr->bytes; + } else { + objPtr->bytes = (char *)attemptckalloc(numBytes + 1); + if (objPtr->bytes) { + objPtr->length = (int) numBytes; + if (bytes) { + memcpy(objPtr->bytes, bytes, numBytes); + } + objPtr->bytes[objPtr->length] = '\0'; + } + } + } else if (objPtr->bytes == &tclEmptyString) { + /* Start with empty string rep (not allocated) */ + if (numBytes == 0) { + return objPtr->bytes; + } else { + objPtr->bytes = (char *)attemptckalloc(numBytes + 1); + if (objPtr->bytes) { + objPtr->length = (int) numBytes; + objPtr->bytes[objPtr->length] = '\0'; + } + } + } else { + /* Start with non-empty string rep (allocated) */ + if (numBytes == 0) { + ckfree(objPtr->bytes); + TclInitStringRep(objPtr, NULL, 0); + return objPtr->bytes; + } else { + objPtr->bytes = (char *)attemptckrealloc(objPtr->bytes, + numBytes + 1); + if (objPtr->bytes) { + objPtr->length = (int) numBytes; + objPtr->bytes[objPtr->length] = '\0'; + } + } } + return objPtr->bytes; } @@ -1726,6 +1891,117 @@ Tcl_InvalidateStringRep( /* *---------------------------------------------------------------------- * + * Tcl_HasStringRep -- + * + * This function reports whether object has a string representation. + * + * Results: + * Boolean. + *---------------------------------------------------------------------- + */ + +int +Tcl_HasStringRep( + Tcl_Obj *objPtr) /* Object to test */ +{ + return TclHasStringRep(objPtr); +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_StoreInternalRep -- + * + * This function is called to set the object's internal + * representation to match a particular type. + * + * It is the caller's responsibility to guarantee that + * the value of the submitted internalrep is in agreement with + * the value of any existing string rep. + * + * Results: + * None. + * + * Side effects: + * Calls the freeIntRepProc of the current Tcl_ObjType, if any. + * Sets the internalRep and typePtr fields to the submitted values. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_StoreInternalRep( + Tcl_Obj *objPtr, /* Object whose internal rep should be set. */ + const Tcl_ObjType *typePtr, /* New type for the object */ + const Tcl_ObjInternalRep *irPtr) /* New internalrep for the object */ +{ + /* Clear out any existing internalrep ( "shimmer" ) */ + TclFreeInternalRep(objPtr); + + /* When irPtr == NULL, just leave objPtr with no internalrep for typePtr */ + if (irPtr) { + /* Copy the new internalrep into place */ + objPtr->internalRep = *irPtr; + + /* Set the type to match */ + objPtr->typePtr = typePtr; + } +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_FetchInternalRep -- + * + * This function is called to retrieve the object's internal + * representation matching a requested type, if any. + * + * Results: + * A read-only pointer to the associated Tcl_ObjInternalRep, or + * NULL if no such internal representation exists. + * + * Side effects: + * Calls the freeIntRepProc of the current Tcl_ObjType, if any. + * Sets the internalRep and typePtr fields to the submitted values. + * + *---------------------------------------------------------------------- + */ + +Tcl_ObjInternalRep * +Tcl_FetchInternalRep( + Tcl_Obj *objPtr, /* Object to fetch from. */ + const Tcl_ObjType *typePtr) /* Requested type */ +{ + return TclFetchInternalRep(objPtr, typePtr); +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_FreeInternalRep -- + * + * This function is called to free an object's internal representation. + * + * Results: + * None. + * + * Side effects: + * Calls the freeIntRepProc of the current Tcl_ObjType, if any. + * Sets typePtr field to NULL. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_FreeInternalRep( + Tcl_Obj *objPtr) /* Object whose internal rep should be freed. */ +{ + TclFreeInternalRep(objPtr); +} + +/* + *---------------------------------------------------------------------- + * * Tcl_NewBooleanObj -- * * This function is normally called when not debugging: i.e., when @@ -1734,7 +2010,7 @@ Tcl_InvalidateStringRep( * is coerced to 1. * * When TCL_MEM_DEBUG is defined, this function just returns the result - * of calling the debugging version Tcl_DbNewBooleanObj. + * of calling the debugging version Tcl_DbNewLongObj. * * Results: * The newly created object is returned. This object will have an invalid @@ -1753,7 +2029,7 @@ Tcl_Obj * Tcl_NewBooleanObj( int intValue) /* Boolean used to initialize new object. */ { - return Tcl_DbNewBooleanObj(intValue, "unknown", 0); + return Tcl_DbNewWideIntObj(intValue!=0, "unknown", 0); } #else /* if not TCL_MEM_DEBUG */ @@ -1764,7 +2040,7 @@ Tcl_NewBooleanObj( { Tcl_Obj *objPtr; - TclNewBooleanObj(objPtr, intValue); + TclNewIntObj(objPtr, intValue!=0); return objPtr; } #endif /* TCL_MEM_DEBUG */ @@ -1795,6 +2071,7 @@ Tcl_NewBooleanObj( *---------------------------------------------------------------------- */ +#ifndef TCL_NO_DEPRECATED #undef Tcl_DbNewBooleanObj #ifdef TCL_MEM_DEBUG @@ -1809,9 +2086,10 @@ Tcl_DbNewBooleanObj( Tcl_Obj *objPtr; TclDbNewObj(objPtr, file, line); + /* Optimized TclInvalidateStringRep() */ objPtr->bytes = NULL; - objPtr->internalRep.longValue = (intValue? 1 : 0); + objPtr->internalRep.wideValue = (intValue != 0); objPtr->typePtr = &tclIntType; return objPtr; } @@ -1821,10 +2099,8 @@ Tcl_DbNewBooleanObj( Tcl_Obj * Tcl_DbNewBooleanObj( int intValue, /* Boolean used to initialize new object. */ - const char *file, /* The name of the source file calling this - * function; used for debugging. */ - int line) /* Line number in the source file; used for - * debugging. */ + TCL_UNUSED(const char *) /*file*/, + TCL_UNUSED(int) /*line*/) { return Tcl_NewBooleanObj(intValue); } @@ -1858,13 +2134,14 @@ Tcl_SetBooleanObj( Tcl_Panic("%s called with shared object", "Tcl_SetBooleanObj"); } - TclSetLongObj(objPtr, (intValue)!=0); + TclSetIntObj(objPtr, intValue!=0); } +#endif /* TCL_NO_DEPRECATED */ /* *---------------------------------------------------------------------- * - * Tcl_GetBooleanFromObj -- + * Tcl_GetBoolFromObj, Tcl_GetBooleanFromObj -- * * Attempt to return a boolean from the Tcl object "objPtr". This * includes conversion from any of Tcl's numeric types. @@ -1880,20 +2157,36 @@ Tcl_SetBooleanObj( *---------------------------------------------------------------------- */ +#undef Tcl_GetBoolFromObj int -Tcl_GetBooleanFromObj( +Tcl_GetBoolFromObj( Tcl_Interp *interp, /* Used for error reporting if not NULL. */ Tcl_Obj *objPtr, /* The object from which to get boolean. */ - int *intPtr) /* Place to store resulting boolean. */ + int flags, + char *charPtr) /* Place to store resulting boolean. */ { + int result; + + if ((flags & TCL_NULL_OK) && (objPtr == NULL || Tcl_GetString(objPtr)[0] == '\0')) { + result = -1; + goto boolEnd; + } else if (objPtr == NULL) { + if (interp) { + TclNewObj(objPtr); + TclParseNumber(interp, objPtr, (flags & TCL_NULL_OK) + ? "boolean value or \"\"" : "boolean value", NULL, -1, NULL, 0); + Tcl_DecrRefCount(objPtr); + } + return TCL_ERROR; + } do { if (objPtr->typePtr == &tclIntType) { - *intPtr = (objPtr->internalRep.longValue != 0); - return TCL_OK; + result = (objPtr->internalRep.wideValue != 0); + goto boolEnd; } if (objPtr->typePtr == &tclBooleanType) { - *intPtr = (int) objPtr->internalRep.longValue; - return TCL_OK; + result = objPtr->internalRep.longValue != 0; + goto boolEnd; } if (objPtr->typePtr == &tclDoubleType) { /* @@ -1909,24 +2202,45 @@ Tcl_GetBooleanFromObj( if (Tcl_GetDoubleFromObj(interp, objPtr, &d) != TCL_OK) { return TCL_ERROR; } - *intPtr = (d != 0.0); - return TCL_OK; + result = (d != 0.0); + goto boolEnd; } if (objPtr->typePtr == &tclBignumType) { - *intPtr = 1; - return TCL_OK; - } -#ifndef TCL_WIDE_INT_IS_LONG - if (objPtr->typePtr == &tclWideIntType) { - *intPtr = (objPtr->internalRep.wideValue != 0); + result = 1; + boolEnd: + if (charPtr != NULL) { + flags &= (TCL_NULL_OK-2); + if (flags) { + if (flags == (int)sizeof(int)) { + *(int *)charPtr = result; + return TCL_OK; + } else if (flags == (int)sizeof(short)) { + *(short *)charPtr = result; + return TCL_OK; + } else { + Tcl_Panic("Wrong bool var for %s", "Tcl_GetBoolFromObj"); + } + } + *charPtr = result; + } return TCL_OK; } -#endif } while ((ParseBoolean(objPtr) == TCL_OK) || (TCL_OK == - TclParseNumber(interp, objPtr, "boolean value", NULL,-1,NULL,0))); + TclParseNumber(interp, objPtr, (flags & TCL_NULL_OK) + ? "boolean value or \"\"" : "boolean value", NULL,-1,NULL,0))); return TCL_ERROR; } +#undef Tcl_GetBooleanFromObj +int +Tcl_GetBooleanFromObj( + Tcl_Interp *interp, /* Used for error reporting if not NULL. */ + Tcl_Obj *objPtr, /* The object from which to get boolean. */ + int *intPtr) /* Place to store resulting boolean. */ +{ + return Tcl_GetBoolFromObj(interp, objPtr, (TCL_NULL_OK-2)&(int)sizeof(int), (char *)(void *)intPtr); +} + /* *---------------------------------------------------------------------- * @@ -1942,7 +2256,12 @@ Tcl_GetBooleanFromObj( * * Side effects: * If no error occurs, an integer 1 or 0 is stored as "objPtr"s internal - * representation and the type of "objPtr" is set to boolean. + * representation and the type of "objPtr" is set to boolean or int/wideInt. + * + * Warning: If the returned type is "wideInt" (32-bit platforms) and your + * platform is bigendian, you cannot use internalRep.longValue to distinguish + * between false and true. On Windows and most other platforms this still will + * work fine, but basically it is non-portable. * *---------------------------------------------------------------------- */ @@ -1960,8 +2279,7 @@ TclSetBooleanFromAny( if (objPtr->bytes == NULL) { if (objPtr->typePtr == &tclIntType) { - switch (objPtr->internalRep.longValue) { - case 0L: case 1L: + if ((Tcl_WideUInt)objPtr->internalRep.wideValue < 2) { return TCL_OK; } goto badBoolean; @@ -1971,12 +2289,6 @@ TclSetBooleanFromAny( goto badBoolean; } -#ifndef TCL_WIDE_INT_IS_LONG - if (objPtr->typePtr == &tclWideIntType) { - goto badBoolean; - } -#endif - if (objPtr->typePtr == &tclDoubleType) { goto badBoolean; } @@ -1989,7 +2301,7 @@ TclSetBooleanFromAny( badBoolean: if (interp != NULL) { int length; - const char *str = TclGetStringFromObj(objPtr, &length); + const char *str = Tcl_GetStringFromObj(objPtr, &length); Tcl_Obj *msg; TclNewLiteralStringObj(msg, "expected boolean value but got \""); @@ -2005,9 +2317,10 @@ static int ParseBoolean( Tcl_Obj *objPtr) /* The object to parse/convert. */ { - int i, length, newBool; + int newBool; char lowerCase[6]; - const char *str = TclGetStringFromObj(objPtr, &length); + const char *str = TclGetString(objPtr); + size_t i, length = objPtr->length; if ((length == 0) || (length > 5)) { /* @@ -2105,14 +2418,14 @@ ParseBoolean( */ goodBoolean: - TclFreeIntRep(objPtr); + TclFreeInternalRep(objPtr); objPtr->internalRep.longValue = newBool; objPtr->typePtr = &tclBooleanType; return TCL_OK; numericBoolean: - TclFreeIntRep(objPtr); - objPtr->internalRep.longValue = newBool; + TclFreeInternalRep(objPtr); + objPtr->internalRep.wideValue = newBool; objPtr->typePtr = &tclIntType; return TCL_OK; } @@ -2201,6 +2514,7 @@ Tcl_DbNewDoubleObj( Tcl_Obj *objPtr; TclDbNewObj(objPtr, file, line); + /* Optimized TclInvalidateStringRep() */ objPtr->bytes = NULL; objPtr->internalRep.doubleValue = dblValue; @@ -2213,10 +2527,8 @@ Tcl_DbNewDoubleObj( Tcl_Obj * Tcl_DbNewDoubleObj( double dblValue, /* Double used to initialize the object. */ - const char *file, /* The name of the source file calling this - * function; used for debugging. */ - int line) /* Line number in the source file; used for - * debugging. */ + TCL_UNUSED(const char *) /*file*/, + TCL_UNUSED(int) /*line*/) { return Tcl_NewDoubleObj(dblValue); } @@ -2280,7 +2592,7 @@ Tcl_GetDoubleFromObj( { do { if (objPtr->typePtr == &tclDoubleType) { - if (TclIsNaN(objPtr->internalRep.doubleValue)) { + if (isnan(objPtr->internalRep.doubleValue)) { if (interp != NULL) { Tcl_SetObjResult(interp, Tcl_NewStringObj( "floating point value is Not a Number", -1)); @@ -2293,22 +2605,16 @@ Tcl_GetDoubleFromObj( return TCL_OK; } if (objPtr->typePtr == &tclIntType) { - *dblPtr = objPtr->internalRep.longValue; + *dblPtr = (double) objPtr->internalRep.wideValue; return TCL_OK; } if (objPtr->typePtr == &tclBignumType) { mp_int big; - UNPACK_BIGNUM(objPtr, big); + TclUnpackBignum(objPtr, big); *dblPtr = TclBignumToDouble(&big); return TCL_OK; } -#ifndef TCL_WIDE_INT_IS_LONG - if (objPtr->typePtr == &tclWideIntType) { - *dblPtr = (double) objPtr->internalRep.wideValue; - return TCL_OK; - } -#endif } while (SetDoubleFromAny(interp, objPtr) == TCL_OK); return TCL_ERROR; } @@ -2367,15 +2673,12 @@ static void UpdateStringOfDouble( Tcl_Obj *objPtr) /* Double obj with string rep to update. */ { - char buffer[TCL_DOUBLE_SPACE]; - int len; + char *dst = Tcl_InitStringRep(objPtr, NULL, TCL_DOUBLE_SPACE); - Tcl_PrintDouble(NULL, objPtr->internalRep.doubleValue, buffer); - len = strlen(buffer); + TclOOM(dst, TCL_DOUBLE_SPACE + 1); - objPtr->bytes = (char *)ckalloc(len + 1); - memcpy(objPtr->bytes, buffer, len + 1); - objPtr->length = len; + Tcl_PrintDouble(NULL, objPtr->internalRep.doubleValue, dst); + (void) Tcl_InitStringRep(objPtr, NULL, strlen(dst)); } /* @@ -2408,6 +2711,7 @@ UpdateStringOfDouble( *---------------------------------------------------------------------- */ +#ifndef TCL_NO_DEPRECATED #undef Tcl_NewIntObj #ifdef TCL_MEM_DEBUG @@ -2415,7 +2719,7 @@ Tcl_Obj * Tcl_NewIntObj( int intValue) /* Int used to initialize the new object. */ { - return Tcl_DbNewLongObj((long)intValue, "unknown", 0); + return Tcl_DbNewWideIntObj(intValue, "unknown", 0); } #else /* if not TCL_MEM_DEBUG */ @@ -2430,6 +2734,7 @@ Tcl_NewIntObj( return objPtr; } #endif /* if TCL_MEM_DEBUG */ +#endif /* TCL_NO_DEPRECATED */ /* *---------------------------------------------------------------------- @@ -2448,7 +2753,7 @@ Tcl_NewIntObj( * *---------------------------------------------------------------------- */ - +#ifndef TCL_NO_DEPRECATED #undef Tcl_SetIntObj void Tcl_SetIntObj( @@ -2461,32 +2766,30 @@ Tcl_SetIntObj( TclSetIntObj(objPtr, intValue); } +#endif /* TCL_NO_DEPRECATED */ /* *---------------------------------------------------------------------- * * Tcl_GetIntFromObj -- * - * Retrieve the integer value of 'objPtr'. - * - * Value - * - * TCL_OK - * - * Success. + * Attempt to return an int from the Tcl object "objPtr". If the object + * is not already an int, an attempt will be made to convert it to one. * - * TCL_ERROR - * - * An error occurred during conversion or the integral value can not - * be represented as an integer (it might be too large). An error - * message is left in the interpreter's result if 'interp' is not - * NULL. + * Integer and long integer objects share the same "integer" type + * implementation. We store all integers as longs and Tcl_GetIntFromObj + * checks whether the current value of the long can be represented by an + * int. * - * Effect + * Results: + * The return value is a standard Tcl object result. If an error occurs + * during conversion or if the long integer held by the object can not be + * represented by an int, an error message is left in the interpreter's + * result unless "interp" is NULL. * - * 'objPtr' is converted to an integer if necessary if it is not one - * already. The conversion frees any previously-existing internal - * representation. + * Side effects: + * If the object is not already an int, the conversion will free any old + * internal representation. * *---------------------------------------------------------------------- */ @@ -2500,20 +2803,12 @@ Tcl_GetIntFromObj( #if (LONG_MAX == INT_MAX) return TclGetLongFromObj(interp, objPtr, (long *) intPtr); #else - void *p; - int type; + long l; - if ((TclGetNumberFromObj(NULL, objPtr, &p, &type) != TCL_OK) - || (type == TCL_NUMBER_DOUBLE)) { - if (interp != NULL) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "expected integer but got \"%s\"", Tcl_GetString(objPtr))); - Tcl_SetErrorCode(interp, "TCL", "VALUE", "INTEGER", NULL); - } + if (TclGetLongFromObj(interp, objPtr, &l) != TCL_OK) { return TCL_ERROR; } - if ((type != TCL_NUMBER_LONG) || ((ULONG_MAX > UINT_MAX) - && ((*(long *)p > UINT_MAX) || (*(long *)p < -(long)UINT_MAX)))) { + if ((ULONG_MAX > UINT_MAX) && ((l > UINT_MAX) || (l < INT_MIN))) { if (interp != NULL) { const char *s = "integer value too large to represent"; @@ -2522,10 +2817,11 @@ Tcl_GetIntFromObj( } return TCL_ERROR; } - *intPtr = (int)*(long *)p; + *intPtr = (int) l; return TCL_OK; #endif } + /* *---------------------------------------------------------------------- @@ -2548,9 +2844,8 @@ SetIntFromAny( Tcl_Interp *interp, /* Tcl interpreter */ Tcl_Obj *objPtr) /* Pointer to the object to convert */ { - long l; - - return TclGetLongFromObj(interp, objPtr, &l); + Tcl_WideInt w; + return Tcl_GetWideIntFromObj(interp, objPtr, &w); } /* @@ -2576,15 +2871,25 @@ static void UpdateStringOfInt( Tcl_Obj *objPtr) /* Int object whose string rep to update. */ { - char buffer[TCL_INTEGER_SPACE]; - int len; + char *dst = Tcl_InitStringRep( objPtr, NULL, TCL_INTEGER_SPACE); + + TclOOM(dst, TCL_INTEGER_SPACE + 1); + (void) Tcl_InitStringRep(objPtr, NULL, + TclFormatInt(dst, objPtr->internalRep.wideValue)); +} - len = TclFormatInt(buffer, objPtr->internalRep.longValue); +#if !defined(TCL_NO_DEPRECATED) && TCL_MAJOR_VERSION < 9 && !defined(TCL_WIDE_INT_IS_LONG) +static void +UpdateStringOfOldInt( + Tcl_Obj *objPtr) /* Int object whose string rep to update. */ +{ + char *dst = Tcl_InitStringRep( objPtr, NULL, TCL_INTEGER_SPACE); - objPtr->bytes = (char *)ckalloc(len + 1); - memcpy(objPtr->bytes, buffer, len + 1); - objPtr->length = len; + TclOOM(dst, TCL_INTEGER_SPACE + 1); + (void) Tcl_InitStringRep(objPtr, NULL, + TclFormatInt(dst, objPtr->internalRep.longValue)); } +#endif /* *---------------------------------------------------------------------- @@ -2616,15 +2921,16 @@ UpdateStringOfInt( *---------------------------------------------------------------------- */ -#ifdef TCL_MEM_DEBUG +#ifndef TCL_NO_DEPRECATED #undef Tcl_NewLongObj +#ifdef TCL_MEM_DEBUG Tcl_Obj * Tcl_NewLongObj( long longValue) /* Long integer used to initialize the * new object. */ { - return Tcl_DbNewLongObj(longValue, "unknown", 0); + return Tcl_DbNewWideIntObj(longValue, "unknown", 0); } #else /* if not TCL_MEM_DEBUG */ @@ -2636,10 +2942,11 @@ Tcl_NewLongObj( { Tcl_Obj *objPtr; - TclNewLongObj(objPtr, longValue); + TclNewIntObj(objPtr, longValue); return objPtr; } #endif /* if TCL_MEM_DEBUG */ +#endif /* TCL_NO_DEPRECATED */ /* *---------------------------------------------------------------------- @@ -2673,6 +2980,8 @@ Tcl_NewLongObj( *---------------------------------------------------------------------- */ +#ifndef TCL_NO_DEPRECATED +#undef Tcl_DbNewLongObj #ifdef TCL_MEM_DEBUG Tcl_Obj * @@ -2687,9 +2996,10 @@ Tcl_DbNewLongObj( Tcl_Obj *objPtr; TclDbNewObj(objPtr, file, line); + /* Optimized TclInvalidateStringRep */ objPtr->bytes = NULL; - objPtr->internalRep.longValue = longValue; + objPtr->internalRep.wideValue = longValue; objPtr->typePtr = &tclIntType; return objPtr; } @@ -2700,14 +3010,13 @@ Tcl_Obj * Tcl_DbNewLongObj( long longValue, /* Long integer used to initialize the new * object. */ - const char *file, /* The name of the source file calling this - * function; used for debugging. */ - int line) /* Line number in the source file; used for - * debugging. */ + TCL_UNUSED(const char *) /*file*/, + TCL_UNUSED(int) /*line*/) { - return Tcl_NewLongObj(longValue); + return Tcl_NewWideIntObj(longValue); } #endif /* TCL_MEM_DEBUG */ +#endif /* TCL_NO_DEPRECATED */ /* *---------------------------------------------------------------------- @@ -2727,6 +3036,8 @@ Tcl_DbNewLongObj( *---------------------------------------------------------------------- */ +#ifndef TCL_NO_DEPRECATED +#undef Tcl_SetLongObj void Tcl_SetLongObj( Tcl_Obj *objPtr, /* Object whose internal rep to init. */ @@ -2737,8 +3048,9 @@ Tcl_SetLongObj( Tcl_Panic("%s called with shared object", "Tcl_SetLongObj"); } - TclSetLongObj(objPtr, longValue); + TclSetIntObj(objPtr, longValue); } +#endif /* TCL_NO_DEPRECATED */ /* *---------------------------------------------------------------------- @@ -2768,14 +3080,15 @@ Tcl_GetLongFromObj( long *longPtr) /* Place to store resulting long. */ { do { +#ifdef TCL_WIDE_INT_IS_LONG if (objPtr->typePtr == &tclIntType) { - *longPtr = objPtr->internalRep.longValue; + *longPtr = objPtr->internalRep.wideValue; return TCL_OK; } -#ifndef TCL_WIDE_INT_IS_LONG - if (objPtr->typePtr == &tclWideIntType) { +#else + if (objPtr->typePtr == &tclIntType) { /* - * We return any integer in the range -ULONG_MAX to ULONG_MAX + * We return any integer in the range LONG_MIN to ULONG_MAX * converted to a long, ignoring overflow. The rule preserves * existing semantics for conversion of integers on input, but * avoids inadvertent demotion of wide integers to 32-bit ones in @@ -2784,9 +3097,9 @@ Tcl_GetLongFromObj( Tcl_WideInt w = objPtr->internalRep.wideValue; - if (w >= -(Tcl_WideInt)(ULONG_MAX) + if (w >= (Tcl_WideInt)(LONG_MIN) && w <= (Tcl_WideInt)(ULONG_MAX)) { - *longPtr = Tcl_WideAsLong(w); + *longPtr = (long)w; return TCL_OK; } goto tooLarge; @@ -2809,28 +3122,30 @@ Tcl_GetLongFromObj( * values in the unsigned long range will fit in a long. */ + { mp_int big; + unsigned long scratch, value = 0; + unsigned char *bytes = (unsigned char *) &scratch; + size_t numBytes; - UNPACK_BIGNUM(objPtr, big); - if ((size_t) big.used <= (CHAR_BIT * sizeof(long) + MP_DIGIT_BIT - 1) - / MP_DIGIT_BIT) { - unsigned long value = 0; - size_t numBytes; - long scratch; - unsigned char *bytes = (unsigned char *) &scratch; - - if (mp_to_ubin(&big, bytes, sizeof(long), &numBytes) == MP_OKAY) { - while (numBytes-- > 0) { + TclUnpackBignum(objPtr, big); + if (mp_to_ubin(&big, bytes, sizeof(long), &numBytes) == MP_OKAY) { + while (numBytes-- > 0) { value = (value << CHAR_BIT) | *bytes++; + } + if (big.sign) { + if (value <= 1 + (unsigned long)LONG_MAX) { + *longPtr = (long)(-value); + return TCL_OK; } - if (big.sign) { - *longPtr = (long) (-value); - } else { - *longPtr = (long) value; + } else { + if (value <= (unsigned long)ULONG_MAX) { + *longPtr = (long)value; + return TCL_OK; } - return TCL_OK; } } + } #ifndef TCL_WIDE_INT_IS_LONG tooLarge: #endif @@ -2847,49 +3162,6 @@ Tcl_GetLongFromObj( TCL_PARSE_INTEGER_ONLY)==TCL_OK); return TCL_ERROR; } -#ifndef TCL_WIDE_INT_IS_LONG - -/* - *---------------------------------------------------------------------- - * - * UpdateStringOfWideInt -- - * - * Update the string representation for a wide integer object. Note: this - * function does not free an existing old string rep so storage will be - * lost if this has not already been done. - * - * Results: - * None. - * - * Side effects: - * The object's string is set to a valid string that results from the - * wideInt-to-string conversion. - * - *---------------------------------------------------------------------- - */ - -static void -UpdateStringOfWideInt( - Tcl_Obj *objPtr) /* Int object whose string rep to update. */ -{ - char buffer[TCL_INTEGER_SPACE+2]; - unsigned len; - Tcl_WideInt wideVal = objPtr->internalRep.wideValue; - - /* - * Note that sprintf will generate a compiler warning under Mingw claiming - * %I64 is an unknown format specifier. Just ignore this warning. We can't - * use %L as the format specifier since that gets printed as a 32 bit - * value. - */ - - sprintf(buffer, "%" TCL_LL_MODIFIER "d", wideVal); - len = strlen(buffer); - objPtr->bytes = (char *)ckalloc(len + 1); - memcpy(objPtr->bytes, buffer, len + 1); - objPtr->length = len; -} -#endif /* !TCL_WIDE_INT_IS_LONG */ /* *---------------------------------------------------------------------- @@ -2939,7 +3211,7 @@ Tcl_NewWideIntObj( Tcl_Obj *objPtr; TclNewObj(objPtr); - Tcl_SetWideIntObj(objPtr, wideValue); + TclSetIntObj(objPtr, wideValue); return objPtr; } #endif /* if TCL_MEM_DEBUG */ @@ -2991,7 +3263,7 @@ Tcl_DbNewWideIntObj( Tcl_Obj *objPtr; TclDbNewObj(objPtr, file, line); - Tcl_SetWideIntObj(objPtr, wideValue); + TclSetIntObj(objPtr, wideValue); return objPtr; } @@ -3002,10 +3274,8 @@ Tcl_DbNewWideIntObj( Tcl_WideInt wideValue, /* Long integer used to initialize the new * object. */ - const char *file, /* The name of the source file calling this - * function; used for debugging. */ - int line) /* Line number in the source file; used for - * debugging. */ + TCL_UNUSED(const char *) /*file*/, + TCL_UNUSED(int) /*line*/) { return Tcl_NewWideIntObj(wideValue); } @@ -3040,19 +3310,7 @@ Tcl_SetWideIntObj( Tcl_Panic("%s called with shared object", "Tcl_SetWideIntObj"); } - if ((wideValue >= (Tcl_WideInt) LONG_MIN) - && (wideValue <= (Tcl_WideInt) LONG_MAX)) { - TclSetLongObj(objPtr, (long) wideValue); - } else { -#ifndef TCL_WIDE_INT_IS_LONG - TclSetWideIntObj(objPtr, wideValue); -#else - mp_int big; - - TclBNInitBignumFromWideInt(&big, wideValue); - Tcl_SetBignumObj(objPtr, &big); -#endif - } + TclSetIntObj(objPtr, wideValue); } /* @@ -3084,14 +3342,8 @@ Tcl_GetWideIntFromObj( /* Place to store resulting long. */ { do { -#ifndef TCL_WIDE_INT_IS_LONG - if (objPtr->typePtr == &tclWideIntType) { - *wideIntPtr = objPtr->internalRep.wideValue; - return TCL_OK; - } -#endif if (objPtr->typePtr == &tclIntType) { - *wideIntPtr = (Tcl_WideInt) objPtr->internalRep.longValue; + *wideIntPtr = objPtr->internalRep.wideValue; return TCL_OK; } if (objPtr->typePtr == &tclDoubleType) { @@ -3110,25 +3362,26 @@ Tcl_GetWideIntFromObj( */ mp_int big; - - UNPACK_BIGNUM(objPtr, big); - if ((size_t) big.used <= (CHAR_BIT * sizeof(Tcl_WideInt) - + MP_DIGIT_BIT - 1) / MP_DIGIT_BIT) { - Tcl_WideUInt value = 0; - size_t numBytes; - Tcl_WideInt scratch; - unsigned char *bytes = (unsigned char *) &scratch; - - if (mp_to_ubin(&big, bytes, sizeof(Tcl_WideInt), &numBytes) == MP_OKAY) { - while (numBytes-- > 0) { - value = (value << CHAR_BIT) | *bytes++; + Tcl_WideUInt value = 0; + size_t numBytes; + Tcl_WideInt scratch; + unsigned char *bytes = (unsigned char *) &scratch; + + TclUnpackBignum(objPtr, big); + if (mp_to_ubin(&big, bytes, sizeof(Tcl_WideInt), &numBytes) == MP_OKAY) { + while (numBytes-- > 0) { + value = (value << CHAR_BIT) | *bytes++; + } + if (big.sign) { + if (value <= 1 + ~(Tcl_WideUInt)WIDE_MIN) { + *wideIntPtr = (Tcl_WideInt)(-value); + return TCL_OK; } - if (big.sign) { - *wideIntPtr = (Tcl_WideInt) (-value); - } else { - *wideIntPtr = (Tcl_WideInt) value; + } else { + if (value <= (Tcl_WideUInt)WIDE_MAX) { + *wideIntPtr = (Tcl_WideInt)value; + return TCL_OK; } - return TCL_OK; } } if (interp != NULL) { @@ -3144,33 +3397,160 @@ Tcl_GetWideIntFromObj( TCL_PARSE_INTEGER_ONLY)==TCL_OK); return TCL_ERROR; } -#ifndef TCL_WIDE_INT_IS_LONG /* *---------------------------------------------------------------------- * - * SetWideIntFromAny -- + * Tcl_GetWideUIntFromObj -- * - * Attempts to force the internal representation for a Tcl object to - * tclWideIntType, specifically. + * Attempt to return a unsigned wide integer from the Tcl object "objPtr". If the + * object is not already a wide int object or a bignum object, an attempt will + * be made to convert it to one. * * Results: - * The return value is a standard object Tcl result. If an error occurs + * The return value is a standard Tcl object result. If an error occurs * during conversion, an error message is left in the interpreter's * result unless "interp" is NULL. * + * Side effects: + * If the object is not already an int object, the conversion will free + * any old internal representation. + * *---------------------------------------------------------------------- */ -static int -SetWideIntFromAny( - Tcl_Interp *interp, /* Tcl interpreter */ - Tcl_Obj *objPtr) /* Pointer to the object to convert */ +int +Tcl_GetWideUIntFromObj( + Tcl_Interp *interp, /* Used for error reporting if not NULL. */ + Tcl_Obj *objPtr, /* Object from which to get a wide int. */ + Tcl_WideUInt *wideUIntPtr) + /* Place to store resulting long. */ { - Tcl_WideInt w; - return Tcl_GetWideIntFromObj(interp, objPtr, &w); + do { + if (objPtr->typePtr == &tclIntType) { + if (objPtr->internalRep.wideValue < 0) { + wideUIntOutOfRange: + if (interp != NULL) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "expected unsigned integer but got \"%s\"", + TclGetString(objPtr))); + Tcl_SetErrorCode(interp, "TCL", "VALUE", "INTEGER", NULL); + } + return TCL_ERROR; + } + *wideUIntPtr = (Tcl_WideUInt)objPtr->internalRep.wideValue; + return TCL_OK; + } + if (objPtr->typePtr == &tclDoubleType) { + goto wideUIntOutOfRange; + } + if (objPtr->typePtr == &tclBignumType) { + /* + * Must check for those bignum values that can fit in a + * Tcl_WideUInt, even when auto-narrowing is enabled. + */ + + mp_int big; + Tcl_WideUInt value = 0; + size_t numBytes; + Tcl_WideUInt scratch; + unsigned char *bytes = (unsigned char *) &scratch; + + TclUnpackBignum(objPtr, big); + if (big.sign == MP_NEG) { + goto wideUIntOutOfRange; + } + if (mp_to_ubin(&big, bytes, sizeof(Tcl_WideUInt), &numBytes) == MP_OKAY) { + while (numBytes-- > 0) { + value = (value << CHAR_BIT) | *bytes++; + } + *wideUIntPtr = (Tcl_WideUInt)value; + return TCL_OK; + } + + if (interp != NULL) { + const char *s = "integer value too large to represent"; + Tcl_Obj *msg = Tcl_NewStringObj(s, -1); + + Tcl_SetObjResult(interp, msg); + Tcl_SetErrorCode(interp, "ARITH", "IOVERFLOW", s, NULL); + } + return TCL_ERROR; + } + } while (TclParseNumber(interp, objPtr, "integer", NULL, -1, NULL, + TCL_PARSE_INTEGER_ONLY)==TCL_OK); + return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * TclGetWideBitsFromObj -- + * + * Attempt to return a wide integer from the Tcl object "objPtr". If the + * object is not already a int, double or bignum, an attempt will be made + * to convert it to one of these. Out-of-range values don't result in an + * error, but only the least significant 64 bits will be returned. + * + * Results: + * The return value is a standard Tcl object result. If an error occurs + * during conversion, an error message is left in the interpreter's + * result unless "interp" is NULL. + * + * Side effects: + * If the object is not already an int, double or bignum object, the + * conversion will free any old internal representation. + * + *---------------------------------------------------------------------- + */ + +int +TclGetWideBitsFromObj( + Tcl_Interp *interp, /* Used for error reporting if not NULL. */ + Tcl_Obj *objPtr, /* Object from which to get a wide int. */ + Tcl_WideInt *wideIntPtr) /* Place to store resulting wide integer. */ +{ + do { + if (objPtr->typePtr == &tclIntType) { + *wideIntPtr = objPtr->internalRep.wideValue; + return TCL_OK; + } + if (objPtr->typePtr == &tclDoubleType) { + if (interp != NULL) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "expected integer but got \"%s\"", + TclGetString(objPtr))); + Tcl_SetErrorCode(interp, "TCL", "VALUE", "INTEGER", NULL); + } + return TCL_ERROR; + } + if (objPtr->typePtr == &tclBignumType) { + mp_int big; + mp_err err; + + Tcl_WideUInt value = 0, scratch; + size_t numBytes; + unsigned char *bytes = (unsigned char *) &scratch; + + Tcl_GetBignumFromObj(NULL, objPtr, &big); + err = mp_mod_2d(&big, (int) (CHAR_BIT * sizeof(Tcl_WideInt)), &big); + if (err == MP_OKAY) { + err = mp_to_ubin(&big, bytes, sizeof(Tcl_WideInt), &numBytes); + } + if (err != MP_OKAY) { + return TCL_ERROR; + } + while (numBytes-- > 0) { + value = (value << CHAR_BIT) | *bytes++; + } + *wideIntPtr = !big.sign ? (Tcl_WideInt)value : -(Tcl_WideInt)value; + mp_clear(&big); + return TCL_OK; + } + } while (TclParseNumber(interp, objPtr, "integer", NULL, -1, NULL, + TCL_PARSE_INTEGER_ONLY)==TCL_OK); + return TCL_ERROR; } -#endif /* !TCL_WIDE_INT_IS_LONG */ /* *---------------------------------------------------------------------- @@ -3191,7 +3571,7 @@ FreeBignum( { mp_int toFree; /* Bignum to free */ - UNPACK_BIGNUM(objPtr, toFree); + TclUnpackBignum(objPtr, toFree); mp_clear(&toFree); if (PTR2INT(objPtr->internalRep.twoPtrValue.ptr2) < 0) { ckfree(objPtr->internalRep.twoPtrValue.ptr1); @@ -3224,7 +3604,7 @@ DupBignum( mp_int bignumCopy; copyPtr->typePtr = &tclBignumType; - UNPACK_BIGNUM(srcPtr, bignumVal); + TclUnpackBignum(srcPtr, bignumVal); if (mp_init_copy(&bignumCopy, &bignumVal) != MP_OKAY) { Tcl_Panic("initialization failure in DupBignum"); } @@ -3257,12 +3637,10 @@ UpdateStringOfBignum( { mp_int bignumVal; int size; - int status; char *stringVal; - UNPACK_BIGNUM(objPtr, bignumVal); - status = mp_radix_size(&bignumVal, 10, &size); - if (status != MP_OKAY) { + TclUnpackBignum(objPtr, bignumVal); + if (MP_OKAY != mp_radix_size(&bignumVal, 10, &size)) { Tcl_Panic("radix size failure in UpdateStringOfBignum"); } if (size < 2) { @@ -3277,13 +3655,13 @@ UpdateStringOfBignum( Tcl_Panic("UpdateStringOfBignum: string length limit exceeded"); } - stringVal = (char *)ckalloc(size); - status = mp_to_radix(&bignumVal, stringVal, size, NULL, 10); - if (status != MP_OKAY) { + + stringVal = Tcl_InitStringRep(objPtr, NULL, size - 1); + + TclOOM(stringVal, size); + if (MP_OKAY != mp_to_radix(&bignumVal, stringVal, size, NULL, 10)) { Tcl_Panic("conversion failure in UpdateStringOfBignum"); } - objPtr->bytes = stringVal; - objPtr->length = size - 1; /* size includes a trailing NUL byte. */ } /* @@ -3307,14 +3685,14 @@ UpdateStringOfBignum( Tcl_Obj * Tcl_NewBignumObj( - mp_int *bignumValue) + void *bignumValue) { return Tcl_DbNewBignumObj(bignumValue, "unknown", 0); } #else Tcl_Obj * Tcl_NewBignumObj( - mp_int *bignumValue) + void *bignumValue) { Tcl_Obj *objPtr; @@ -3345,7 +3723,7 @@ Tcl_NewBignumObj( #ifdef TCL_MEM_DEBUG Tcl_Obj * Tcl_DbNewBignumObj( - mp_int *bignumValue, + void *bignumValue, const char *file, int line) { @@ -3358,9 +3736,9 @@ Tcl_DbNewBignumObj( #else Tcl_Obj * Tcl_DbNewBignumObj( - mp_int *bignumValue, - const char *file, - int line) + void *bignumValue, + TCL_UNUSED(const char *) /*file*/, + TCL_UNUSED(int) /*line*/) { return Tcl_NewBignumObj(bignumValue); } @@ -3399,37 +3777,34 @@ GetBignumFromObj( if (copy || Tcl_IsShared(objPtr)) { mp_int temp; - UNPACK_BIGNUM(objPtr, temp); + TclUnpackBignum(objPtr, temp); if (mp_init_copy(bignumValue, &temp) != MP_OKAY) { - if (interp != NULL) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "insufficient memory to unpack bignum", -1)); - Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL); - } return TCL_ERROR; } } else { - UNPACK_BIGNUM(objPtr, *bignumValue); + TclUnpackBignum(objPtr, *bignumValue); + /* Optimized TclFreeInternalRep */ objPtr->internalRep.twoPtrValue.ptr1 = NULL; objPtr->internalRep.twoPtrValue.ptr2 = NULL; objPtr->typePtr = NULL; + /* + * TODO: If objPtr has a string rep, this leaves + * it undisturbed. Not clear that's proper. Pure + * bignum values are converted to empty string. + */ if (objPtr->bytes == NULL) { - TclInitStringRep(objPtr, tclEmptyStringRep, 0); + TclInitStringRep(objPtr, NULL, 0); } } return TCL_OK; } if (objPtr->typePtr == &tclIntType) { - TclBNInitBignumFromLong(bignumValue, objPtr->internalRep.longValue); - return TCL_OK; - } -#ifndef TCL_WIDE_INT_IS_LONG - if (objPtr->typePtr == &tclWideIntType) { - TclBNInitBignumFromWideInt(bignumValue, - objPtr->internalRep.wideValue); + if (mp_init_i64(bignumValue, + objPtr->internalRep.wideValue) != MP_OKAY) { + return TCL_ERROR; + } return TCL_OK; } -#endif if (objPtr->typePtr == &tclDoubleType) { if (interp != NULL) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( @@ -3473,9 +3848,9 @@ int Tcl_GetBignumFromObj( Tcl_Interp *interp, /* Tcl interpreter for error reporting */ Tcl_Obj *objPtr, /* Object to read */ - mp_int *bignumValue) /* Returned bignum value. */ + void *bignumValue) /* Returned bignum value. */ { - return GetBignumFromObj(interp, objPtr, 1, bignumValue); + return GetBignumFromObj(interp, objPtr, 1, (mp_int *)bignumValue); } /* @@ -3508,9 +3883,9 @@ int Tcl_TakeBignumFromObj( Tcl_Interp *interp, /* Tcl interpreter for error reporting */ Tcl_Obj *objPtr, /* Object to read */ - mp_int *bignumValue) /* Returned bignum value. */ + void *bignumValue) /* Returned bignum value. */ { - return GetBignumFromObj(interp, objPtr, 0, bignumValue); + return GetBignumFromObj(interp, objPtr, 0, (mp_int *)bignumValue); } /* @@ -3533,65 +3908,36 @@ Tcl_TakeBignumFromObj( void Tcl_SetBignumObj( Tcl_Obj *objPtr, /* Object to set */ - mp_int *bignumValue) /* Value to store */ + void *big) /* Value to store */ { + Tcl_WideUInt value = 0; + size_t numBytes; + Tcl_WideUInt scratch; + unsigned char *bytes = (unsigned char *) &scratch; + mp_int *bignumValue = (mp_int *) big; + if (Tcl_IsShared(objPtr)) { Tcl_Panic("%s called with shared object", "Tcl_SetBignumObj"); } - if ((size_t) bignumValue->used - <= (CHAR_BIT * sizeof(long) + MP_DIGIT_BIT - 1) / MP_DIGIT_BIT) { - unsigned long value = 0; - size_t numBytes; - long scratch; - unsigned char *bytes = (unsigned char *) &scratch; - - if (mp_to_ubin(bignumValue, bytes, sizeof(long), &numBytes) != MP_OKAY) { - goto tooLargeForLong; - } - while (numBytes-- > 0) { - value = (value << CHAR_BIT) | *bytes++; - } - if (value > (((~(unsigned long)0) >> 1) + bignumValue->sign)) { - goto tooLargeForLong; - } - if (bignumValue->sign) { - TclSetLongObj(objPtr, (long)(-value)); - } else { - TclSetLongObj(objPtr, (long)value); - } - mp_clear(bignumValue); - return; + if (mp_to_ubin(bignumValue, bytes, sizeof(Tcl_WideUInt), &numBytes) != MP_OKAY) { + goto tooLargeForWide; } - tooLargeForLong: -#ifndef TCL_WIDE_INT_IS_LONG - if ((size_t) bignumValue->used - <= (CHAR_BIT * sizeof(Tcl_WideInt) + MP_DIGIT_BIT - 1) / MP_DIGIT_BIT) { - Tcl_WideUInt value = 0; - size_t numBytes; - Tcl_WideInt scratch; - unsigned char *bytes = (unsigned char *)&scratch; - - if (mp_to_ubin(bignumValue, bytes, sizeof(Tcl_WideInt), &numBytes) != MP_OKAY) { - goto tooLargeForWide; - } - while (numBytes-- > 0) { - value = (value << CHAR_BIT) | *bytes++; - } - if (value > ((UWIDE_MAX >> 1) + bignumValue->sign)) { - goto tooLargeForWide; - } - if (bignumValue->sign) { - TclSetWideIntObj(objPtr, (Tcl_WideInt)(-value)); - } else { - TclSetWideIntObj(objPtr, (Tcl_WideInt)value); - } - mp_clear(bignumValue); - return; + while (numBytes-- > 0) { + value = (value << CHAR_BIT) | *bytes++; + } + if (value > ((Tcl_WideUInt)WIDE_MAX + bignumValue->sign)) { + goto tooLargeForWide; + } + if (bignumValue->sign) { + TclSetIntObj(objPtr, (Tcl_WideInt)(-value)); + } else { + TclSetIntObj(objPtr, (Tcl_WideInt)value); } + mp_clear(bignumValue); + return; tooLargeForWide: -#endif TclInvalidateStringRep(objPtr); - TclFreeIntRep(objPtr); + TclFreeInternalRep(objPtr); TclSetBignumInternalRep(objPtr, bignumValue); } @@ -3616,8 +3962,9 @@ Tcl_SetBignumObj( void TclSetBignumInternalRep( Tcl_Obj *objPtr, - mp_int *bignumValue) + void *big) { + mp_int *bignumValue = (mp_int *)big; objPtr->typePtr = &tclBignumType; PACK_BIGNUM(*bignumValue, objPtr); @@ -3636,7 +3983,7 @@ TclSetBignumInternalRep( /* *---------------------------------------------------------------------- * - * TclGetNumberFromObj -- + * Tcl_GetNumberFromObj -- * * Extracts a number (of any possible numeric type) from an object. * @@ -3654,15 +4001,15 @@ TclSetBignumInternalRep( */ int -TclGetNumberFromObj( +Tcl_GetNumberFromObj( Tcl_Interp *interp, Tcl_Obj *objPtr, - ClientData *clientDataPtr, + void **clientDataPtr, int *typePtr) { do { if (objPtr->typePtr == &tclDoubleType) { - if (TclIsNaN(objPtr->internalRep.doubleValue)) { + if (isnan(objPtr->internalRep.doubleValue)) { *typePtr = TCL_NUMBER_NAN; } else { *typePtr = TCL_NUMBER_DOUBLE; @@ -3671,23 +4018,16 @@ TclGetNumberFromObj( return TCL_OK; } if (objPtr->typePtr == &tclIntType) { - *typePtr = TCL_NUMBER_LONG; - *clientDataPtr = &objPtr->internalRep.longValue; - return TCL_OK; - } -#ifndef TCL_WIDE_INT_IS_LONG - if (objPtr->typePtr == &tclWideIntType) { - *typePtr = TCL_NUMBER_WIDE; + *typePtr = TCL_NUMBER_INT; *clientDataPtr = &objPtr->internalRep.wideValue; return TCL_OK; } -#endif if (objPtr->typePtr == &tclBignumType) { static Tcl_ThreadDataKey bignumKey; - mp_int *bigPtr = Tcl_GetThreadData(&bignumKey, - (int) sizeof(mp_int)); + mp_int *bigPtr = (mp_int *)Tcl_GetThreadData(&bignumKey, + sizeof(mp_int)); - UNPACK_BIGNUM(objPtr, *bigPtr); + TclUnpackBignum(objPtr, *bigPtr); *typePtr = TCL_NUMBER_BIG; *clientDataPtr = bigPtr; return TCL_OK; @@ -3696,6 +4036,107 @@ TclGetNumberFromObj( TclParseNumber(interp, objPtr, "number", NULL, -1, NULL, 0)); return TCL_ERROR; } + +int +Tcl_GetNumber( + Tcl_Interp *interp, + const char *bytes, + size_t numBytes, + void **clientDataPtr, + int *typePtr) +{ + static Tcl_ThreadDataKey numberCacheKey; + Tcl_Obj *objPtr = (Tcl_Obj *)Tcl_GetThreadData(&numberCacheKey, + sizeof(Tcl_Obj)); + + Tcl_FreeInternalRep(objPtr); + + if (bytes == NULL) { + bytes = &tclEmptyString; + numBytes = 0; + } + if (numBytes == (size_t)TCL_INDEX_NONE) { + numBytes = strlen(bytes); + } + if (numBytes > INT_MAX) { + if (interp) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "max size for a Tcl value (%d bytes) exceeded", INT_MAX)); + Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL); + } + return TCL_ERROR; + } + + objPtr->bytes = (char *) bytes; + objPtr->length = numBytes; + + return Tcl_GetNumberFromObj(interp, objPtr, clientDataPtr, typePtr); +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_IncrRefCount -- + * + * Increments the reference count of the object. + * + * Results: + * None. + * + *---------------------------------------------------------------------- + */ + +#undef Tcl_IncrRefCount +void +Tcl_IncrRefCount( + Tcl_Obj *objPtr) /* The object we are registering a reference to. */ +{ + ++(objPtr)->refCount; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_DecrRefCount -- + * + * Decrements the reference count of the object. + * + * Results: + * None. + * + *---------------------------------------------------------------------- + */ + +#undef Tcl_DecrRefCount +void +Tcl_DecrRefCount( + Tcl_Obj *objPtr) /* The object we are releasing a reference to. */ +{ + if (objPtr->refCount-- <= 1) { + TclFreeObj(objPtr); + } +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_IsShared -- + * + * Tests if the object has a ref count greater than one. + * + * Results: + * Boolean value that is the result of the test. + * + *---------------------------------------------------------------------- + */ + +#undef Tcl_IsShared +int +Tcl_IsShared( + Tcl_Obj *objPtr) /* The object to test for being shared. */ +{ + return ((objPtr)->refCount > 1); +} /* *---------------------------------------------------------------------- @@ -3718,6 +4159,7 @@ TclGetNumberFromObj( *---------------------------------------------------------------------- */ +#ifdef TCL_MEM_DEBUG void Tcl_DbIncrRefCount( Tcl_Obj *objPtr, /* The object we are registering a reference @@ -3727,14 +4169,13 @@ Tcl_DbIncrRefCount( int line) /* Line number in the source file; used for * debugging. */ { -#ifdef TCL_MEM_DEBUG if (objPtr->refCount == 0x61616161) { fprintf(stderr, "file = %s, line = %d\n", file, line); fflush(stderr); Tcl_Panic("incrementing refCount of previously disposed object"); } -# ifdef TCL_THREADS +#if TCL_THREADS /* * Check to make sure that the Tcl_Obj was allocated by the current * thread. Don't do this check when shutting down since thread local @@ -3756,9 +4197,19 @@ Tcl_DbIncrRefCount( } } # endif /* TCL_THREADS */ -#endif /* TCL_MEM_DEBUG */ ++(objPtr)->refCount; } +#else /* !TCL_MEM_DEBUG */ +void +Tcl_DbIncrRefCount( + Tcl_Obj *objPtr, /* The object we are registering a reference + * to. */ + TCL_UNUSED(const char *) /*file*/, + TCL_UNUSED(int) /*line*/) +{ + ++(objPtr)->refCount; +} +#endif /* TCL_MEM_DEBUG */ /* *---------------------------------------------------------------------- @@ -3781,6 +4232,7 @@ Tcl_DbIncrRefCount( *---------------------------------------------------------------------- */ +#ifdef TCL_MEM_DEBUG void Tcl_DbDecrRefCount( Tcl_Obj *objPtr, /* The object we are releasing a reference @@ -3790,14 +4242,13 @@ Tcl_DbDecrRefCount( int line) /* Line number in the source file; used for * debugging. */ { -#ifdef TCL_MEM_DEBUG if (objPtr->refCount == 0x61616161) { fprintf(stderr, "file = %s, line = %d\n", file, line); fflush(stderr); Tcl_Panic("decrementing refCount of previously disposed object"); } -# ifdef TCL_THREADS +#if TCL_THREADS /* * Check to make sure that the Tcl_Obj was allocated by the current * thread. Don't do this check when shutting down since thread local @@ -3819,12 +4270,24 @@ Tcl_DbDecrRefCount( } } # endif /* TCL_THREADS */ -#endif /* TCL_MEM_DEBUG */ if (objPtr->refCount-- <= 1) { TclFreeObj(objPtr); } } +#else /* !TCL_MEM_DEBUG */ +void +Tcl_DbDecrRefCount( + Tcl_Obj *objPtr, /* The object we are releasing a reference + * to. */ + TCL_UNUSED(const char *) /*file*/, + TCL_UNUSED(int) /*line*/) +{ + if (objPtr->refCount-- <= 1) { + TclFreeObj(objPtr); + } +} +#endif /* TCL_MEM_DEBUG */ /* *---------------------------------------------------------------------- @@ -3850,10 +4313,15 @@ Tcl_DbDecrRefCount( int Tcl_DbIsShared( Tcl_Obj *objPtr, /* The object to test for being shared. */ +#ifdef TCL_MEM_DEBUG const char *file, /* The name of the source file calling this * function; used for debugging. */ int line) /* Line number in the source file; used for * debugging. */ +#else + TCL_UNUSED(const char *) /*file*/, + TCL_UNUSED(int) /*line*/) +#endif { #ifdef TCL_MEM_DEBUG if (objPtr->refCount == 0x61616161) { @@ -3862,7 +4330,7 @@ Tcl_DbIsShared( Tcl_Panic("checking whether previously disposed object is shared"); } -# ifdef TCL_THREADS +#if TCL_THREADS /* * Check to make sure that the Tcl_Obj was allocated by the current * thread. Don't do this check when shutting down since thread local @@ -3947,7 +4415,7 @@ Tcl_InitObjHashTable( static Tcl_HashEntry * AllocObjEntry( - Tcl_HashTable *tablePtr, /* Hash table. */ + TCL_UNUSED(Tcl_HashTable *), void *keyPtr) /* Key to store in the hash table entry. */ { Tcl_Obj *objPtr = (Tcl_Obj *)keyPtr; @@ -3982,7 +4450,7 @@ TclCompareObjKeys( void *keyPtr, /* New key to compare. */ Tcl_HashEntry *hPtr) /* Existing key to compare. */ { - Tcl_Obj *objPtr1 = keyPtr; + Tcl_Obj *objPtr1 = (Tcl_Obj *)keyPtr; Tcl_Obj *objPtr2 = (Tcl_Obj *) hPtr->key.oneWordValue; const char *p1, *p2; size_t l1, l2; @@ -4066,15 +4534,15 @@ TclFreeObjEntry( *---------------------------------------------------------------------- */ -unsigned int +TCL_HASH_TYPE TclHashObjKey( - Tcl_HashTable *tablePtr, /* Hash table. */ + TCL_UNUSED(Tcl_HashTable *), void *keyPtr) /* Key from which to compute hash value. */ { - Tcl_Obj *objPtr = keyPtr; + Tcl_Obj *objPtr = (Tcl_Obj *)keyPtr; int length; - const char *string = TclGetStringFromObj(objPtr, &length); - unsigned int result = 0; + const char *string = Tcl_GetStringFromObj(objPtr, &length); + TCL_HASH_TYPE result = 0; /* * I tried a zillion different hash functions and asked many other people @@ -4169,12 +4637,11 @@ Tcl_GetCommandFromObj( * to discard the old rep and create a new one. */ - resPtr = objPtr->internalRep.twoPtrValue.ptr1; - if ((objPtr->typePtr == &tclCmdNameType) && (resPtr != NULL)) { + resPtr = (ResolvedCmdName *)objPtr->internalRep.twoPtrValue.ptr1; + if (objPtr->typePtr == &tclCmdNameType) { Command *cmdPtr = resPtr->cmdPtr; if ((cmdPtr->cmdEpoch == resPtr->cmdEpoch) - && !(cmdPtr->flags & CMD_IS_DELETED) && (interp == cmdPtr->nsPtr->interp) && !(cmdPtr->nsPtr->flags & NS_DYING)) { Namespace *refNsPtr = (Namespace *) @@ -4194,11 +4661,11 @@ Tcl_GetCommandFromObj( * had is invalid one way or another. */ - /* See [] why we cannot call SetCmdNameFromAny() directly here. */ + /* See [07d13d99b0a9] why we cannot call SetCmdNameFromAny() directly here. */ if (tclCmdNameType.setFromAnyProc(interp, objPtr) != TCL_OK) { return NULL; } - resPtr = objPtr->internalRep.twoPtrValue.ptr1; + resPtr = (ResolvedCmdName *)objPtr->internalRep.twoPtrValue.ptr1; return (Tcl_Command) (resPtr ? resPtr->cmdPtr : NULL); } @@ -4222,57 +4689,78 @@ Tcl_GetCommandFromObj( *---------------------------------------------------------------------- */ -void -TclSetCmdNameObj( - Tcl_Interp *interp, /* Points to interpreter containing command - * that should be cached in objPtr. */ - Tcl_Obj *objPtr, /* Points to Tcl object to be changed to a - * CmdName object. */ - Command *cmdPtr) /* Points to Command structure that the - * CmdName object should refer to. */ +static void +SetCmdNameObj( + Tcl_Interp *interp, + Tcl_Obj *objPtr, + Command *cmdPtr, + ResolvedCmdName *resPtr) { Interp *iPtr = (Interp *) interp; - ResolvedCmdName *resPtr; - Namespace *currNsPtr; - const char *name; + ResolvedCmdName *fillPtr; + const char *name = TclGetString(objPtr); - if (objPtr->typePtr == &tclCmdNameType) { - resPtr = objPtr->internalRep.twoPtrValue.ptr1; - if (resPtr != NULL && resPtr->cmdPtr == cmdPtr) { - return; - } + if (resPtr) { + fillPtr = resPtr; + } else { + fillPtr = (ResolvedCmdName *)ckalloc(sizeof(ResolvedCmdName)); + fillPtr->refCount = 1; } + fillPtr->cmdPtr = cmdPtr; cmdPtr->refCount++; - resPtr = (ResolvedCmdName *)ckalloc(sizeof(ResolvedCmdName)); - resPtr->cmdPtr = cmdPtr; - resPtr->cmdEpoch = cmdPtr->cmdEpoch; - resPtr->refCount = 1; + fillPtr->cmdEpoch = cmdPtr->cmdEpoch; - name = TclGetString(objPtr); - if ((*name++ == ':') && (*name == ':')) { + /* NOTE: relying on NULL termination here. */ + if ((name[0] == ':') && (name[1] == ':')) { /* - * The name is fully qualified: set the referring namespace to - * NULL. + * Fully qualified names always resolve to same thing. No need + * to record resolution context information. */ - resPtr->refNsPtr = NULL; + fillPtr->refNsPtr = NULL; + fillPtr->refNsId = 0; /* Will not be read */ + fillPtr->refNsCmdEpoch = 0; /* Will not be read */ } else { /* - * Get the current namespace. + * Record current state of current namespace as the resolution + * context of this command name lookup. */ + Namespace *currNsPtr = iPtr->varFramePtr->nsPtr; - currNsPtr = iPtr->varFramePtr->nsPtr; + fillPtr->refNsPtr = currNsPtr; + fillPtr->refNsId = currNsPtr->nsId; + fillPtr->refNsCmdEpoch = currNsPtr->cmdRefEpoch; + } + + if (resPtr == NULL) { + TclFreeInternalRep(objPtr); - resPtr->refNsPtr = currNsPtr; - resPtr->refNsId = currNsPtr->nsId; - resPtr->refNsCmdEpoch = currNsPtr->cmdRefEpoch; + objPtr->internalRep.twoPtrValue.ptr1 = fillPtr; + objPtr->internalRep.twoPtrValue.ptr2 = NULL; + objPtr->typePtr = &tclCmdNameType; } +} - TclFreeIntRep(objPtr); - objPtr->internalRep.twoPtrValue.ptr1 = resPtr; - objPtr->internalRep.twoPtrValue.ptr2 = NULL; - objPtr->typePtr = &tclCmdNameType; +void +TclSetCmdNameObj( + Tcl_Interp *interp, /* Points to interpreter containing command + * that should be cached in objPtr. */ + Tcl_Obj *objPtr, /* Points to Tcl object to be changed to a + * CmdName object. */ + Command *cmdPtr) /* Points to Command structure that the + * CmdName object should refer to. */ +{ + ResolvedCmdName *resPtr; + + if (objPtr->typePtr == &tclCmdNameType) { + resPtr = (ResolvedCmdName *)objPtr->internalRep.twoPtrValue.ptr1; + if (resPtr != NULL && resPtr->cmdPtr == cmdPtr) { + return; + } + } + + SetCmdNameObj(interp, objPtr, cmdPtr, NULL); } /* @@ -4301,15 +4789,14 @@ FreeCmdNameInternalRep( Tcl_Obj *objPtr) /* CmdName object with internal * representation to free. */ { - ResolvedCmdName *resPtr = objPtr->internalRep.twoPtrValue.ptr1; + ResolvedCmdName *resPtr = (ResolvedCmdName *)objPtr->internalRep.twoPtrValue.ptr1; - if (resPtr != NULL) { /* * Decrement the reference count of the ResolvedCmdName structure. If * there are no more uses, free the ResolvedCmdName structure. */ - if (resPtr->refCount-- == 1) { + if (resPtr->refCount-- <= 1) { /* * Now free the cached command, unless it is still in its hash * table or if there are other references to it from other cmdName @@ -4321,7 +4808,6 @@ FreeCmdNameInternalRep( TclCleanupCommandMacro(cmdPtr); ckfree(resPtr); } - } objPtr->typePtr = NULL; } @@ -4350,13 +4836,11 @@ DupCmdNameInternalRep( Tcl_Obj *srcPtr, /* Object with internal rep to copy. */ Tcl_Obj *copyPtr) /* Object with internal rep to set. */ { - ResolvedCmdName *resPtr = srcPtr->internalRep.twoPtrValue.ptr1; + ResolvedCmdName *resPtr = (ResolvedCmdName *)srcPtr->internalRep.twoPtrValue.ptr1; copyPtr->internalRep.twoPtrValue.ptr1 = resPtr; copyPtr->internalRep.twoPtrValue.ptr2 = NULL; - if (resPtr != NULL) { resPtr->refCount++; - } copyPtr->typePtr = &tclCmdNameType; } @@ -4386,10 +4870,8 @@ SetCmdNameFromAny( Tcl_Interp *interp, /* Used for error reporting if not NULL. */ Tcl_Obj *objPtr) /* The object to convert. */ { - Interp *iPtr = (Interp *) interp; const char *name; Command *cmdPtr; - Namespace *currNsPtr; ResolvedCmdName *resPtr; if (interp == NULL) { @@ -4409,59 +4891,31 @@ SetCmdNameFromAny( Tcl_FindCommand(interp, name, /*ns*/ NULL, /*flags*/ 0); /* - * Free the old internalRep before setting the new one. Do this after - * getting the string rep to allow the conversion code (in particular, - * Tcl_GetStringFromObj) to use that old internalRep. + * Stop shimmering and caching nothing when we found nothing. Just + * report the failure to find the command as an error. */ - if (cmdPtr) { - cmdPtr->refCount++; - resPtr = objPtr->internalRep.twoPtrValue.ptr1; - if ((objPtr->typePtr == &tclCmdNameType) - && resPtr && (resPtr->refCount == 1)) { - /* - * Reuse the old ResolvedCmdName struct instead of freeing it - */ - - Command *oldCmdPtr = resPtr->cmdPtr; - - if (--oldCmdPtr->refCount == 0) { - TclCleanupCommandMacro(oldCmdPtr); - } - } else { - TclFreeIntRep(objPtr); - resPtr = (ResolvedCmdName *)ckalloc(sizeof(ResolvedCmdName)); - resPtr->refCount = 1; - objPtr->internalRep.twoPtrValue.ptr1 = resPtr; - objPtr->internalRep.twoPtrValue.ptr2 = NULL; - objPtr->typePtr = &tclCmdNameType; - } - resPtr->cmdPtr = cmdPtr; - resPtr->cmdEpoch = cmdPtr->cmdEpoch; - if ((*name++ == ':') && (*name == ':')) { - /* - * The name is fully qualified: set the referring namespace to - * NULL. - */ + if (cmdPtr == NULL || !TclRoutineHasName(cmdPtr)) { + return TCL_ERROR; + } - resPtr->refNsPtr = NULL; - } else { - /* - * Get the current namespace. - */ + resPtr = (ResolvedCmdName *)objPtr->internalRep.twoPtrValue.ptr1; + if ((objPtr->typePtr == &tclCmdNameType) && (resPtr->refCount == 1)) { + /* + * Re-use existing ResolvedCmdName struct when possible. + * Cleanup the old fields that need it. + */ - currNsPtr = iPtr->varFramePtr->nsPtr; + Command *oldCmdPtr = resPtr->cmdPtr; - resPtr->refNsPtr = currNsPtr; - resPtr->refNsId = currNsPtr->nsId; - resPtr->refNsCmdEpoch = currNsPtr->cmdRefEpoch; + if (oldCmdPtr->refCount-- <= 1) { + TclCleanupCommandMacro(oldCmdPtr); } } else { - TclFreeIntRep(objPtr); - objPtr->internalRep.twoPtrValue.ptr1 = NULL; - objPtr->internalRep.twoPtrValue.ptr2 = NULL; - objPtr->typePtr = &tclCmdNameType; + resPtr = NULL; } + + SetCmdNameObj(interp, objPtr, cmdPtr, resPtr); return TCL_OK; } @@ -4483,12 +4937,11 @@ SetCmdNameFromAny( int Tcl_RepresentationCmd( - ClientData clientData, + TCL_UNUSED(void *), Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { - char ptrBuffer[2*TCL_INTEGER_SPACE+6]; Tcl_Obj *descObj; if (objc != 2) { @@ -4502,36 +4955,20 @@ Tcl_RepresentationCmd( * "1872361827361287" */ - sprintf(ptrBuffer, "%p", (void *) objv[1]); descObj = Tcl_ObjPrintf("value is a %s with a refcount of %d," - " object pointer at %s", - objv[1]->typePtr ? objv[1]->typePtr->name : "pure string", - objv[1]->refCount, ptrBuffer); + " object pointer at %p", + objv[1]->typePtr ? objv[1]->typePtr->name : "pure string", + objv[1]->refCount, objv[1]); - /* - * This is a workaround to silence reports from `make valgrind` - * on 64-bit systems. The problem is that the test suite - * includes calling the [represenation] command on values of - * &tclDoubleType. When these values are created, the "doubleValue" - * is set, but when the "twoPtrValue" is examined, its "ptr2" - * field has never been initialized. Since [representation] - * presents the value of the ptr2 value in its output, valgrind - * alerts about the read of uninitialized memory. - * - * The general problem with [representation], that it can read - * and report uninitialized fields, is still present. This is - * just the minimal workaround to silence one particular test. - */ - - if ((sizeof(void *) > 4) && objv[1]->typePtr == &tclDoubleType) { - objv[1]->internalRep.twoPtrValue.ptr2 = NULL; - } if (objv[1]->typePtr) { - sprintf(ptrBuffer, "%p:%p", - (void *) objv[1]->internalRep.twoPtrValue.ptr1, - (void *) objv[1]->internalRep.twoPtrValue.ptr2); - Tcl_AppendPrintfToObj(descObj, ", internal representation %s", - ptrBuffer); + if (objv[1]->typePtr == &tclDoubleType) { + Tcl_AppendPrintfToObj(descObj, ", internal representation %g", + objv[1]->internalRep.doubleValue); + } else { + Tcl_AppendPrintfToObj(descObj, ", internal representation %p:%p", + (void *) objv[1]->internalRep.twoPtrValue.ptr1, + (void *) objv[1]->internalRep.twoPtrValue.ptr2); + } } if (objv[1]->bytes) { |