diff options
Diffstat (limited to 'generic/tclObj.c')
-rw-r--r-- | generic/tclObj.c | 3355 |
1 files changed, 1962 insertions, 1393 deletions
diff --git a/generic/tclObj.c b/generic/tclObj.c index 87e66a0..930e1fd 100644 --- a/generic/tclObj.c +++ b/generic/tclObj.c @@ -1,33 +1,22 @@ /* * tclObj.c -- * - * This file contains Tcl object-related procedures that are used by many + * 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> * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. - * - * RCS: @(#) $Id: tclObj.c,v 1.93 2005/08/04 19:56:31 dgp Exp $ */ #include "tclInt.h" #include "tommath.h" -#include <float.h> - -/* - * Define test for NaN - */ - -#ifdef _MSC_VER -#define IS_NAN(f) _isnan((f)) -#else -#define IS_NAN(f) ((f) != (f)) -#endif +#include <math.h> /* * Table of all object types. @@ -44,11 +33,12 @@ TCL_DECLARE_MUTEX(tableMutex) Tcl_Obj *tclFreeObjList = NULL; /* - * The object allocator is single threaded. This mutex is referenced by the + * The object allocator is single threaded. This mutex is referenced by the * TclNewObj macro, however, so must be visible. */ #ifdef TCL_THREADS +MODULE_SCOPE Tcl_Mutex tclObjMutex; Tcl_Mutex tclObjMutex; #endif @@ -63,22 +53,58 @@ char *tclEmptyStringRep = &tclEmptyString; #if defined(TCL_MEM_DEBUG) && defined(TCL_THREADS) /* - * Thread local table that is used to check that a Tcl_Obj was not allocated - * by some other thread. + * 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, + * for sanity checking purposes. + */ + +typedef struct ObjData { + Tcl_Obj *objPtr; /* The pointer to the allocated Tcl_Obj. */ + 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. */ +} ObjData; +#endif /* TCL_MEM_DEBUG && TCL_THREADS */ + +/* + * All static variables used in this file are collected into a single instance + * of the following structure. For multi-threaded implementations, there is + * one instance of this structure for each thread. + * + * Notice that different structures with the same name appear in other files. + * The structure defined below is used in this file only. */ + typedef struct ThreadSpecificData { - Tcl_HashTable *objThreadMap; + Tcl_HashTable *lineCLPtr; /* This table remembers for each Tcl_Obj + * generated by a call to the function + * TclSubstTokens() from a literal text + * where bs+nl sequences occured in it, if + * any. I.e. this table keeps track of + * invisible and stripped continuation lines. + * Its keys are Tcl_Obj pointers, the values + * are ContLineLoc pointers. See the file + * 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) + Tcl_HashTable *objThreadMap;/* Thread local table that is used to check + * that a Tcl_Obj was not allocated by some + * other thread. */ +#endif /* TCL_MEM_DEBUG && TCL_THREADS */ } ThreadSpecificData; static Tcl_ThreadDataKey dataKey; -#endif /* TCL_MEM_DEBUG && TCL_THREADS */ +static void TclThreadFinalizeContLines(ClientData clientData); +static ThreadSpecificData *TclGetContLineTable(void); /* * Nested Tcl_Obj deletion management support * * All context references used in the object freeing code are pointers to this - * structure; every thread will have its own structure instance. The purpose + * structure; every thread will have its own structure instance. The purpose * of this structure is to allow deeply nested collections of Tcl_Objs to be * freed without taking a vast depth of C stack (which could cause all sorts * of breakage.) @@ -120,174 +146,164 @@ typedef struct PendingObjData { #define ObjDeletePending(contextPtr) ((contextPtr)->deletionCount > 0) #define ObjOnStack(contextPtr) ((contextPtr)->deletionStack != NULL) #define PushObjToDelete(contextPtr,objPtr) \ - /* Invalidate the string rep first so we can use the bytes value \ - * for our pointer chain. */ \ - if (((objPtr)->bytes != NULL) \ - && ((objPtr)->bytes != tclEmptyStringRep)) { \ - ckfree((char *) (objPtr)->bytes); \ - } \ - /* Now push onto the head of the stack. */ \ - (objPtr)->bytes = (char *) ((contextPtr)->deletionStack); \ + /* The string rep is already invalidated so we can use the bytes value \ + * for our pointer chain: push onto the head of the stack. */ \ + (objPtr)->bytes = (char *) ((contextPtr)->deletionStack); \ (contextPtr)->deletionStack = (objPtr) #define PopObjToDelete(contextPtr,objPtrVar) \ - (objPtrVar) = (contextPtr)->deletionStack; \ + (objPtrVar) = (contextPtr)->deletionStack; \ (contextPtr)->deletionStack = (Tcl_Obj *) (objPtrVar)->bytes /* * Macro to set up the local reference to the deletion context. */ #ifndef TCL_THREADS -PendingObjData pendingObjData; +static PendingObjData pendingObjData; +#define ObjInitDeletionContext(contextPtr) \ + PendingObjData *const contextPtr = &pendingObjData +#elif HAVE_FAST_TSD +static __thread PendingObjData pendingObjData; #define ObjInitDeletionContext(contextPtr) \ - PendingObjData *CONST contextPtr = &pendingObjData + PendingObjData *const contextPtr = &pendingObjData #else -Tcl_ThreadDataKey pendingObjDataKey; +static Tcl_ThreadDataKey pendingObjDataKey; #define ObjInitDeletionContext(contextPtr) \ - PendingObjData *CONST contextPtr = (PendingObjData *) \ + PendingObjData *const contextPtr = \ Tcl_GetThreadData(&pendingObjDataKey, sizeof(PendingObjData)) #endif - /* * Macros to pack/unpack a bignum's fields in a Tcl_Obj internal rep */ #define PACK_BIGNUM(bignum, objPtr) \ - do { \ - (objPtr)->internalRep.bignumValue.digits = (void*) (bignum).dp; \ - (objPtr)->internalRep.bignumValue.misc = ( \ - ((bignum).sign << 30) \ - | ((bignum).alloc << 15) \ - | ((bignum).used)); \ - } while (0) + if ((bignum).used > 0x7fff) { \ + mp_int *temp = (void *) ckalloc((unsigned) sizeof(mp_int)); \ + *temp = bignum; \ + (objPtr)->internalRep.ptrAndLongRep.ptr = temp; \ + (objPtr)->internalRep.ptrAndLongRep.value = (unsigned long)(-1); \ + } else { \ + if ((bignum).alloc > 0x7fff) { \ + mp_shrink(&(bignum)); \ + } \ + (objPtr)->internalRep.ptrAndLongRep.ptr = (void *) (bignum).dp; \ + (objPtr)->internalRep.ptrAndLongRep.value = ( ((bignum).sign << 30) \ + | ((bignum).alloc << 15) | ((bignum).used)); \ + } #define UNPACK_BIGNUM(objPtr, bignum) \ - do { \ - (bignum).dp = (mp_digit*) (objPtr)->internalRep.bignumValue.digits; \ - (bignum).sign = (objPtr)->internalRep.bignumValue.misc >> 30; \ - (bignum).alloc = \ - ((objPtr)->internalRep.bignumValue.misc >> 15) & 0x7fff; \ - (bignum).used = (objPtr)->internalRep.bignumValue.misc & 0x7fff; \ - } while (0) - -/* - * Prototypes for procedures defined later in this file: - */ - -static int SetBooleanFromAny _ANSI_ARGS_((Tcl_Interp *interp, - Tcl_Obj *objPtr)); -static int SetDoubleFromAny _ANSI_ARGS_((Tcl_Interp *interp, - Tcl_Obj *objPtr)); -static int SetIntFromAny _ANSI_ARGS_((Tcl_Interp *interp, - Tcl_Obj *objPtr)); -static int SetIntOrWideFromAny _ANSI_ARGS_((Tcl_Interp* interp, - Tcl_Obj *objPtr)); -static void UpdateStringOfDouble _ANSI_ARGS_((Tcl_Obj *objPtr)); -static void UpdateStringOfInt _ANSI_ARGS_((Tcl_Obj *objPtr)); -static int SetWideIntFromAny _ANSI_ARGS_((Tcl_Interp *interp, - Tcl_Obj *objPtr)); + if ((objPtr)->internalRep.ptrAndLongRep.value == (unsigned long)(-1)) { \ + (bignum) = *((mp_int *) ((objPtr)->internalRep.ptrAndLongRep.ptr)); \ + } else { \ + (bignum).dp = (objPtr)->internalRep.ptrAndLongRep.ptr; \ + (bignum).sign = (objPtr)->internalRep.ptrAndLongRep.value >> 30; \ + (bignum).alloc = \ + ((objPtr)->internalRep.ptrAndLongRep.value >> 15) & 0x7fff; \ + (bignum).used = (objPtr)->internalRep.ptrAndLongRep.value & 0x7fff; \ + } +/* + * Prototypes for functions defined later in this file: + */ + +static int ParseBoolean(Tcl_Obj *objPtr); +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 _ANSI_ARGS_((Tcl_Obj *objPtr)); +static void UpdateStringOfWideInt(Tcl_Obj *objPtr); +static int SetWideIntFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr); #endif - -static void FreeBignum _ANSI_ARGS_((Tcl_Obj *objPtr)); -static void DupBignum _ANSI_ARGS_((Tcl_Obj *objPtr, - Tcl_Obj *copyPtr)); -static void UpdateStringOfBignum _ANSI_ARGS_((Tcl_Obj *objPtr)); -static int SetBignumFromAny _ANSI_ARGS_((Tcl_Interp* interp, - Tcl_Obj* objPtr)); +static void FreeBignum(Tcl_Obj *objPtr); +static void DupBignum(Tcl_Obj *objPtr, Tcl_Obj *copyPtr); +static void UpdateStringOfBignum(Tcl_Obj *objPtr); +static int GetBignumFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, + int copy, mp_int *bignumValue); /* * Prototypes for the array hash key methods. */ -static Tcl_HashEntry * AllocObjEntry _ANSI_ARGS_(( - Tcl_HashTable *tablePtr, VOID *keyPtr)); -static int CompareObjKeys _ANSI_ARGS_(( - VOID *keyPtr, Tcl_HashEntry *hPtr)); -static void FreeObjEntry _ANSI_ARGS_(( - Tcl_HashEntry *hPtr)); -static unsigned int HashObjKey _ANSI_ARGS_(( - Tcl_HashTable *tablePtr, VOID *keyPtr)); +static Tcl_HashEntry * AllocObjEntry(Tcl_HashTable *tablePtr, void *keyPtr); /* * Prototypes for the CommandName object type. */ -static void DupCmdNameInternalRep _ANSI_ARGS_((Tcl_Obj *objPtr, - Tcl_Obj *copyPtr)); -static void FreeCmdNameInternalRep _ANSI_ARGS_((Tcl_Obj *objPtr)); -static int SetCmdNameFromAny _ANSI_ARGS_((Tcl_Interp *interp, - Tcl_Obj *objPtr)); - +static void DupCmdNameInternalRep(Tcl_Obj *objPtr, + Tcl_Obj *copyPtr); +static void FreeCmdNameInternalRep(Tcl_Obj *objPtr); +static int SetCmdNameFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr); /* * The structures below defines the Tcl object types defined in this file by - * means of procedures that can be invoked by generic object code. See also + * means of functions that can be invoked by generic object code. See also * tclStringObj.c, tclListObj.c, tclByteCode.c for other type manager * implementations. */ -Tcl_ObjType tclBooleanType = { - "boolean", /* name */ - (Tcl_FreeInternalRepProc *) NULL, /* freeIntRepProc */ - (Tcl_DupInternalRepProc *) NULL, /* dupIntRepProc */ - (Tcl_UpdateStringProc *) NULL, /* updateStringProc */ - SetBooleanFromAny /* setFromAnyProc */ +static const Tcl_ObjType oldBooleanType = { + "boolean", /* name */ + NULL, /* freeIntRepProc */ + NULL, /* dupIntRepProc */ + NULL, /* updateStringProc */ + TclSetBooleanFromAny /* setFromAnyProc */ }; - -Tcl_ObjType tclDoubleType = { - "double", /* name */ - (Tcl_FreeInternalRepProc *) NULL, /* freeIntRepProc */ - (Tcl_DupInternalRepProc *) NULL, /* dupIntRepProc */ - UpdateStringOfDouble, /* updateStringProc */ - SetDoubleFromAny /* setFromAnyProc */ +const Tcl_ObjType tclBooleanType = { + "booleanString", /* name */ + NULL, /* freeIntRepProc */ + NULL, /* dupIntRepProc */ + NULL, /* updateStringProc */ + TclSetBooleanFromAny /* setFromAnyProc */ }; - -Tcl_ObjType tclIntType = { - "int", /* name */ - (Tcl_FreeInternalRepProc *) NULL, /* freeIntRepProc */ - (Tcl_DupInternalRepProc *) NULL, /* dupIntRepProc */ - UpdateStringOfInt, /* updateStringProc */ - SetIntFromAny /* setFromAnyProc */ +const Tcl_ObjType tclDoubleType = { + "double", /* name */ + NULL, /* freeIntRepProc */ + NULL, /* dupIntRepProc */ + UpdateStringOfDouble, /* updateStringProc */ + SetDoubleFromAny /* setFromAnyProc */ }; - -Tcl_ObjType tclWideIntType = { - "wideInt", /* name */ - (Tcl_FreeInternalRepProc *) NULL, /* freeIntRepProc */ - (Tcl_DupInternalRepProc *) NULL, /* dupIntRepProc */ -#ifdef TCL_WIDE_INT_IS_LONG - UpdateStringOfInt, /* updateStringProc */ -#else /* !TCL_WIDE_INT_IS_LONG */ - UpdateStringOfWideInt, /* updateStringProc */ -#endif /* TCL_WIDE_INT_IS_LONG */ - SetWideIntFromAny /* setFromAnyProc */ +const Tcl_ObjType tclIntType = { + "int", /* name */ + NULL, /* freeIntRepProc */ + NULL, /* dupIntRepProc */ + UpdateStringOfInt, /* updateStringProc */ + SetIntFromAny /* setFromAnyProc */ }; - -Tcl_ObjType tclBignumType = { - "bignum", /* name */ - FreeBignum, /* freeIntRepProc */ - DupBignum, /* dupIntRepProc */ - UpdateStringOfBignum, /* updateStringProc */ - SetBignumFromAny /* setFromAnyProc */ +#ifndef TCL_WIDE_INT_IS_LONG +const Tcl_ObjType tclWideIntType = { + "wideInt", /* name */ + NULL, /* freeIntRepProc */ + NULL, /* dupIntRepProc */ + UpdateStringOfWideInt, /* updateStringProc */ + SetWideIntFromAny /* setFromAnyProc */ +}; +#endif +const Tcl_ObjType tclBignumType = { + "bignum", /* name */ + FreeBignum, /* freeIntRepProc */ + DupBignum, /* dupIntRepProc */ + UpdateStringOfBignum, /* updateStringProc */ + NULL /* setFromAnyProc */ }; /* * The structure below defines the Tcl obj hash key type. */ -Tcl_HashKeyType tclObjHashKeyType = { - TCL_HASH_KEY_TYPE_VERSION, /* version */ - 0, /* flags */ - HashObjKey, /* hashKeyProc */ - CompareObjKeys, /* compareKeysProc */ - AllocObjEntry, /* allocEntryProc */ - FreeObjEntry /* freeEntryProc */ + +const Tcl_HashKeyType tclObjHashKeyType = { + TCL_HASH_KEY_TYPE_VERSION, /* version */ + 0, /* flags */ + TclHashObjKey, /* hashKeyProc */ + TclCompareObjKeys, /* compareKeysProc */ + AllocObjEntry, /* allocEntryProc */ + TclFreeObjEntry /* freeEntryProc */ }; /* * The structure below defines the command name Tcl object type by means of - * procedures that can be invoked by generic object code. Objects of this type + * functions that can be invoked by generic object code. Objects of this type * cache the Command pointer that results from looking up command names in the * command hashtable. Such objects appear as the zeroth ("command name") * argument in a Tcl command. @@ -298,17 +314,24 @@ Tcl_HashKeyType tclObjHashKeyType = { * ResolvedCmdName pointer, but DO NOT DO THIS. It seems that some extensions * use the second internal pointer field of the twoPtrValue field for their * own purposes. + * + * TRICKY POINT! Some extensions update this structure! (Notably, these + * include TclBlend and TCom). This is highly ill-advised on their part, but + * does allow them to delete a command when references to it are gone, which + * is fragile but useful given their somewhat-OO style. Because of this, this + * structure MUST NOT be const so that the C compiler puts the data in + * writable memory. [Bug 2558422] + * TODO: Provide a better API for those extensions so that they can coexist... */ -static Tcl_ObjType tclCmdNameType = { - "cmdName", /* name */ - FreeCmdNameInternalRep, /* freeIntRepProc */ - DupCmdNameInternalRep, /* dupIntRepProc */ - (Tcl_UpdateStringProc *) NULL, /* updateStringProc */ - SetCmdNameFromAny /* setFromAnyProc */ +Tcl_ObjType tclCmdNameType = { + "cmdName", /* name */ + FreeCmdNameInternalRep, /* freeIntRepProc */ + DupCmdNameInternalRep, /* dupIntRepProc */ + NULL, /* updateStringProc */ + SetCmdNameFromAny /* setFromAnyProc */ }; - /* * Structure containing a cached pointer to a command that is the result of * resolving the command's name in some namespace. It is the internal @@ -320,7 +343,8 @@ typedef struct ResolvedCmdName { Command *cmdPtr; /* A cached Command pointer. */ Namespace *refNsPtr; /* Points to the namespace containing the * reference (not the namespace that contains - * the referenced command). */ + * the referenced command). NULL if the name + * is fully qualified.*/ 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 @@ -343,14 +367,13 @@ typedef struct ResolvedCmdName { * structure can be freed when refCount * becomes zero. */ } ResolvedCmdName; - /* *------------------------------------------------------------------------- * * TclInitObjectSubsystem -- * - * This procedure is invoked to perform once-only initialization of the + * This function is invoked to perform once-only initialization of the * type table. It also registers the object types defined in this file. * * Results: @@ -364,7 +387,7 @@ typedef struct ResolvedCmdName { */ void -TclInitObjSubsystem() +TclInitObjSubsystem(void) { Tcl_MutexLock(&tableMutex); typeTableInitialized = 1; @@ -375,23 +398,28 @@ TclInitObjSubsystem() Tcl_RegisterObjType(&tclDoubleType); Tcl_RegisterObjType(&tclEndOffsetType); Tcl_RegisterObjType(&tclIntType); - Tcl_RegisterObjType(&tclWideIntType); - Tcl_RegisterObjType(&tclBignumType); Tcl_RegisterObjType(&tclStringType); + Tcl_RegisterObjType(&tclListType); Tcl_RegisterObjType(&tclDictType); Tcl_RegisterObjType(&tclByteCodeType); Tcl_RegisterObjType(&tclArraySearchType); - Tcl_RegisterObjType(&tclNsNameType); Tcl_RegisterObjType(&tclCmdNameType); Tcl_RegisterObjType(&tclRegexpType); Tcl_RegisterObjType(&tclProcBodyType); + /* For backward compatibility only ... */ + Tcl_RegisterObjType(&oldBooleanType); +#ifndef TCL_WIDE_INT_IS_LONG + Tcl_RegisterObjType(&tclWideIntType); +#endif + #ifdef TCL_COMPILE_STATS Tcl_MutexLock(&tclObjMutex); tclObjsAlloced = 0; tclObjsFreed = 0; { int i; + for (i=0 ; i<TCL_MAX_SHARED_OBJ_STATS ; i++) { tclObjsShared[i] = 0; } @@ -403,10 +431,53 @@ TclInitObjSubsystem() /* *---------------------------------------------------------------------- * + * TclFinalizeThreadObjects -- + * + * This function is called by Tcl_FinalizeThread to clean up thread + * specific Tcl_Obj information. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TclFinalizeThreadObjects(void) +{ +#if defined(TCL_MEM_DEBUG) && defined(TCL_THREADS) + Tcl_HashEntry *hPtr; + Tcl_HashSearch hSearch; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + Tcl_HashTable *tablePtr = tsdPtr->objThreadMap; + + if (tablePtr != NULL) { + for (hPtr = Tcl_FirstHashEntry(tablePtr, &hSearch); + hPtr != NULL; hPtr = Tcl_NextHashEntry(&hSearch)) { + ObjData *objData = Tcl_GetHashValue(hPtr); + + if (objData != NULL) { + ckfree(objData); + } + } + + Tcl_DeleteHashTable(tablePtr); + ckfree(tablePtr); + tsdPtr->objThreadMap = NULL; + } +#endif +} + +/* + *---------------------------------------------------------------------- + * * TclFinalizeObjects -- * - * This procedure is called by Tcl_Finalize to clean up all - * registered Tcl_ObjType's and to reset the tclFreeObjList. + * This function is called by Tcl_Finalize to clean up all registered + * Tcl_ObjType's and to reset the tclFreeObjList. * * Results: * None. @@ -418,7 +489,7 @@ TclInitObjSubsystem() */ void -TclFinalizeObjects() +TclFinalizeObjects(void) { Tcl_MutexLock(&tableMutex); if (typeTableInitialized) { @@ -428,9 +499,9 @@ TclFinalizeObjects() Tcl_MutexUnlock(&tableMutex); /* - * All we do here is reset the head pointer of the linked list of - * free Tcl_Obj's to NULL; the memory finalization will take care - * of releasing memory for us. + * All we do here is reset the head pointer of the linked list of free + * Tcl_Obj's to NULL; the memory finalization will take care of releasing + * memory for us. */ Tcl_MutexLock(&tclObjMutex); tclFreeObjList = NULL; @@ -438,12 +509,316 @@ TclFinalizeObjects() } /* + *---------------------------------------------------------------------- + * + * TclGetContLineTable -- + * + * This procedure is a helper which returns the thread-specific + * hash-table used to track continuation line information associated with + * Tcl_Obj*, and the objThreadMap, etc. + * + * Results: + * A reference to the thread-data. + * + * Side effects: + * May allocate memory for the thread-data. + * + * TIP #280 + *---------------------------------------------------------------------- + */ + +static ThreadSpecificData * +TclGetContLineTable(void) +{ + /* + * Initialize the hashtable tracking invisible continuation lines. For + * the release we use a thread exit handler to ensure that this is done + * before TSD blocks are made invalid. The TclFinalizeObjects() which + * would be the natural place for this is invoked afterwards, meaning that + * we try to operate on a data structure already gone. + */ + + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + + if (!tsdPtr->lineCLPtr) { + tsdPtr->lineCLPtr = ckalloc(sizeof(Tcl_HashTable)); + Tcl_InitHashTable(tsdPtr->lineCLPtr, TCL_ONE_WORD_KEYS); + Tcl_CreateThreadExitHandler(TclThreadFinalizeContLines,NULL); + } + return tsdPtr; +} + +/* + *---------------------------------------------------------------------- + * + * TclContinuationsEnter -- + * + * This procedure is a helper which saves the continuation line + * information associated with a Tcl_Obj*. + * + * Results: + * A reference to the newly created continuation line location table. + * + * Side effects: + * Allocates memory for the table of continuation line locations. + * + * TIP #280 + *---------------------------------------------------------------------- + */ + +ContLineLoc * +TclContinuationsEnter( + Tcl_Obj *objPtr, + int num, + int *loc) +{ + int newEntry; + ThreadSpecificData *tsdPtr = TclGetContLineTable(); + Tcl_HashEntry *hPtr = + Tcl_CreateHashEntry(tsdPtr->lineCLPtr, objPtr, &newEntry); + ContLineLoc *clLocPtr = ckalloc(sizeof(ContLineLoc) + num*sizeof(int)); + + if (!newEntry) { + /* + * We're entering ContLineLoc data for the same value more than one + * time. Taking care not to leak the old entry. + * + * This can happen when literals in a proc body are shared. See for + * example test info-30.19 where the action (code) for all branches of + * the switch command is identical, mapping them all to the same + * literal. An interesting result of this is that the number and + * locations (offset) of invisible continuation lines in the literal + * are the same for all occurences. + * + * Note that while reusing the existing entry is possible it requires + * the same actions as for a new entry because we have to copy the + * incoming num/loc data even so. Because we are called from + * TclContinuationsEnterDerived for this case, which modified the + * stored locations (Rebased to the proper relative offset). Just + * returning the stored entry would rebase them a second time, or + * more, hosing the data. It is easier to simply replace, as we are + * doing. + */ + + ckfree(Tcl_GetHashValue(hPtr)); + } + + clLocPtr->num = num; + memcpy(&clLocPtr->loc, loc, num*sizeof(int)); + clLocPtr->loc[num] = CLL_END; /* Sentinel */ + Tcl_SetHashValue(hPtr, clLocPtr); + + return clLocPtr; +} + +/* + *---------------------------------------------------------------------- + * + * TclContinuationsEnterDerived -- + * + * This procedure is a helper which computes the continuation line + * information associated with a Tcl_Obj* cut from the middle of a + * script. + * + * Results: + * None. + * + * Side effects: + * Allocates memory for the table of continuation line locations. + * + * TIP #280 + *---------------------------------------------------------------------- + */ + +void +TclContinuationsEnterDerived( + Tcl_Obj *objPtr, + int start, + int *clNext) +{ + int length, end, num; + int *wordCLLast = clNext; + + /* + * We have to handle invisible continuations lines here as well, despite + * the code we have in TclSubstTokens (TST) for that. Why ? Nesting. If + * our script is the sole argument to an 'eval' command, for example, the + * scriptCLLocPtr we are using was generated by a previous call to TST, + * and while the words we have here may contain continuation lines they + * are invisible already, and the inner call to TST had no bs+nl sequences + * to trigger its code. + * + * Luckily for us, the table we have to create here for the current word + * has to be a slice of the table currently in use, with the locations + * suitably modified to be relative to the start of the word instead of + * relative to the script. + * + * That is what we are doing now. Determine the slice we need, and if not + * empty, wrap it into a new table, and save the result into our + * thread-global hashtable, as usual. + */ + + /* + * First compute the range of the word within the script. (Is there a + * better way which doesn't shimmer?) + */ + + Tcl_GetStringFromObj(objPtr, &length); + end = start + length; /* First char after the word */ + + /* + * Then compute the table slice covering the range of the word. + */ + + while (*wordCLLast >= 0 && *wordCLLast < end) { + wordCLLast++; + } + + /* + * And generate the table from the slice, if it was not empty. + */ + + num = wordCLLast - clNext; + if (num) { + int i; + ContLineLoc *clLocPtr = TclContinuationsEnter(objPtr, num, clNext); + + /* + * Re-base the locations. + */ + + for (i=0 ; i<num ; i++) { + clLocPtr->loc[i] -= start; + + /* + * Continuation lines coming before the string and affecting us + * should not happen, due to the proper maintenance of clNext + * during compilation. + */ + + if (clLocPtr->loc[i] < 0) { + Tcl_Panic("Derived ICL data for object using offsets from before the script"); + } + } + } +} + +/* + *---------------------------------------------------------------------- + * + * TclContinuationsCopy -- + * + * This procedure is a helper which copies the continuation line + * information associated with a Tcl_Obj* to another Tcl_Obj*. It is + * assumed that both contain the same string/script. Use this when a + * script is duplicated because it was shared. + * + * Results: + * None. + * + * Side effects: + * Allocates memory for the table of continuation line locations. + * + * TIP #280 + *---------------------------------------------------------------------- + */ + +void +TclContinuationsCopy( + Tcl_Obj *objPtr, + Tcl_Obj *originObjPtr) +{ + ThreadSpecificData *tsdPtr = TclGetContLineTable(); + Tcl_HashEntry *hPtr = + Tcl_FindHashEntry(tsdPtr->lineCLPtr, originObjPtr); + + if (hPtr) { + ContLineLoc *clLocPtr = Tcl_GetHashValue(hPtr); + + TclContinuationsEnter(objPtr, clLocPtr->num, clLocPtr->loc); + } +} + +/* + *---------------------------------------------------------------------- + * + * TclContinuationsGet -- + * + * This procedure is a helper which retrieves the continuation line + * information associated with a Tcl_Obj*, if it has any. + * + * Results: + * A reference to the continuation line location table, or NULL if the + * Tcl_Obj* has no such information associated with it. + * + * Side effects: + * None. + * + * TIP #280 + *---------------------------------------------------------------------- + */ + +ContLineLoc * +TclContinuationsGet( + Tcl_Obj *objPtr) +{ + ThreadSpecificData *tsdPtr = TclGetContLineTable(); + Tcl_HashEntry *hPtr = + Tcl_FindHashEntry(tsdPtr->lineCLPtr, objPtr); + + if (!hPtr) { + return NULL; + } + return Tcl_GetHashValue(hPtr); +} + +/* + *---------------------------------------------------------------------- + * + * TclThreadFinalizeContLines -- + * + * This procedure is a helper which releases all continuation line + * information currently known. It is run as a thread exit handler. + * + * Results: + * None. + * + * Side effects: + * Releases memory. + * + * TIP #280 + *---------------------------------------------------------------------- + */ + +static void +TclThreadFinalizeContLines( + ClientData clientData) +{ + /* + * Release the hashtable tracking invisible continuation lines. + */ + + ThreadSpecificData *tsdPtr = TclGetContLineTable(); + Tcl_HashEntry *hPtr; + Tcl_HashSearch hSearch; + + for (hPtr = Tcl_FirstHashEntry(tsdPtr->lineCLPtr, &hSearch); + hPtr != NULL; hPtr = Tcl_NextHashEntry(&hSearch)) { + ckfree(Tcl_GetHashValue(hPtr)); + Tcl_DeleteHashEntry(hPtr); + } + Tcl_DeleteHashTable(tsdPtr->lineCLPtr); + ckfree(tsdPtr->lineCLPtr); + tsdPtr->lineCLPtr = NULL; +} + +/* *-------------------------------------------------------------- * * Tcl_RegisterObjType -- * - * This procedure is called to register a new Tcl object type in the - * table of all object types supported by Tcl. + * This function is called to register a new Tcl object type in the table + * of all object types supported by Tcl. * * Results: * None. @@ -457,15 +832,16 @@ TclFinalizeObjects() */ void -Tcl_RegisterObjType(typePtr) - Tcl_ObjType *typePtr; /* Information about object type; storage must +Tcl_RegisterObjType( + const Tcl_ObjType *typePtr) /* Information about object type; storage must * be statically allocated (must live * forever). */ { - int new; + int isNew; + Tcl_MutexLock(&tableMutex); Tcl_SetHashValue( - Tcl_CreateHashEntry(&typeTable, typePtr->name, &new), typePtr); + Tcl_CreateHashEntry(&typeTable, typePtr->name, &isNew), typePtr); Tcl_MutexUnlock(&tableMutex); } @@ -474,10 +850,10 @@ Tcl_RegisterObjType(typePtr) * * Tcl_AppendAllObjTypes -- * - * This procedure appends onto the argument object the name of each - * object type as a list element. This includes the builtin object types - * (e.g. int, list) as well as those added using Tcl_NewObj. These names - * can be used, for example, with Tcl_GetObjType to get pointers to the + * This function appends onto the argument object the name of each object + * type as a list element. This includes the builtin object types (e.g. + * int, list) as well as those added using Tcl_NewObj. These names can be + * used, for example, with Tcl_GetObjType to get pointers to the * corresponding Tcl_ObjType structures. * * Results: @@ -494,33 +870,32 @@ Tcl_RegisterObjType(typePtr) */ int -Tcl_AppendAllObjTypes(interp, objPtr) - Tcl_Interp *interp; /* Interpreter used for error reporting. */ - Tcl_Obj *objPtr; /* Points to the Tcl object onto which the +Tcl_AppendAllObjTypes( + Tcl_Interp *interp, /* Interpreter used for error reporting. */ + Tcl_Obj *objPtr) /* Points to the Tcl object onto which the * name of each registered type is appended as * a list element. */ { register Tcl_HashEntry *hPtr; Tcl_HashSearch search; - int objc; - Tcl_Obj **objv; + int numElems; - /* + /* * Get the test for a valid list out of the way first. */ - if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) { + if (TclListObjLength(interp, objPtr, &numElems) != TCL_OK) { return TCL_ERROR; } /* - * Type names are NUL-terminated, not counted strings. - * This code relies on that. + * Type names are NUL-terminated, not counted strings. This code relies on + * that. */ Tcl_MutexLock(&tableMutex); for (hPtr = Tcl_FirstHashEntry(&typeTable, &search); - hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { + hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { Tcl_ListObjAppendElement(NULL, objPtr, Tcl_NewStringObj(Tcl_GetHashKey(&typeTable, hPtr), -1)); } @@ -533,7 +908,7 @@ Tcl_AppendAllObjTypes(interp, objPtr) * * Tcl_GetObjType -- * - * This procedure looks up an object type by name. + * This function looks up an object type by name. * * Results: * If an object type with name matching "typeName" is found, a pointer to @@ -545,17 +920,17 @@ Tcl_AppendAllObjTypes(interp, objPtr) *---------------------------------------------------------------------- */ -Tcl_ObjType * -Tcl_GetObjType(typeName) - CONST char *typeName; /* Name of Tcl object type to look up. */ +const Tcl_ObjType * +Tcl_GetObjType( + const char *typeName) /* Name of Tcl object type to look up. */ { register Tcl_HashEntry *hPtr; - Tcl_ObjType *typePtr = NULL; + const Tcl_ObjType *typePtr = NULL; Tcl_MutexLock(&tableMutex); hPtr = Tcl_FindHashEntry(&typeTable, typeName); - if (hPtr != (Tcl_HashEntry *) NULL) { - typePtr = (Tcl_ObjType *) Tcl_GetHashValue(hPtr); + if (hPtr != NULL) { + typePtr = Tcl_GetHashValue(hPtr); } Tcl_MutexUnlock(&tableMutex); return typePtr; @@ -572,7 +947,7 @@ Tcl_GetObjType(typeName) * The return value is TCL_OK on success and TCL_ERROR on failure. If * TCL_ERROR is returned, then the interpreter's result contains an error * message unless "interp" is NULL. Passing a NULL "interp" allows this - * procedure to be used as a test whether the conversion could be done + * function to be used as a test whether the conversion could be done * (and in fact was done). * * Side effects: @@ -582,10 +957,10 @@ Tcl_GetObjType(typeName) */ int -Tcl_ConvertToType(interp, objPtr, typePtr) - Tcl_Interp *interp; /* Used for error reporting if not NULL. */ - Tcl_Obj *objPtr; /* The object to convert. */ - Tcl_ObjType *typePtr; /* The target type. */ +Tcl_ConvertToType( + Tcl_Interp *interp, /* Used for error reporting if not NULL. */ + Tcl_Obj *objPtr, /* The object to convert. */ + const Tcl_ObjType *typePtr) /* The target type. */ { if (objPtr->typePtr == typePtr) { return TCL_OK; @@ -598,13 +973,67 @@ Tcl_ConvertToType(interp, objPtr, typePtr) */ if (typePtr->setFromAnyProc == NULL) { - Tcl_Panic("may not convert object to type %s", typePtr->name); + if (interp) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "can't convert value to type %s", typePtr->name)); + Tcl_SetErrorCode(interp, "TCL", "API_ABUSE", NULL); + } + return TCL_ERROR; } return typePtr->setFromAnyProc(interp, objPtr); } /* + *-------------------------------------------------------------- + * + * TclDbDumpActiveObjects -- + * + * This function is called to dump all of the active Tcl_Obj structs this + * allocator knows about. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +void +TclDbDumpActiveObjects( + FILE *outFile) +{ +#if defined(TCL_MEM_DEBUG) && defined(TCL_THREADS) + Tcl_HashSearch hSearch; + Tcl_HashEntry *hPtr; + Tcl_HashTable *tablePtr; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + + tablePtr = tsdPtr->objThreadMap; + + if (tablePtr != NULL) { + 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); + + if (objData != NULL) { + fprintf(outFile, + "key = 0x%p, objPtr = 0x%p, file = %s, line = %d\n", + Tcl_GetHashKey(tablePtr, hPtr), objData->objPtr, + objData->file, objData->line); + } else { + fprintf(outFile, "key = 0x%p\n", + Tcl_GetHashKey(tablePtr, hPtr)); + } + } + } +#endif +} + +/* *---------------------------------------------------------------------- * * TclDbInitNewObj -- @@ -623,8 +1052,13 @@ Tcl_ConvertToType(interp, objPtr, typePtr) */ #ifdef TCL_MEM_DEBUG -void TclDbInitNewObj(objPtr) - register Tcl_Obj *objPtr; +void +TclDbInitNewObj( + register Tcl_Obj *objPtr, + register const char *file, /* The name of the source file calling this + * function; used for debugging. */ + register int line) /* Line number in the source file; used for + * debugging. */ { objPtr->refCount = 0; objPtr->bytes = tclEmptyStringRep; @@ -640,20 +1074,29 @@ void TclDbInitNewObj(objPtr) if (!TclInExit()) { Tcl_HashEntry *hPtr; Tcl_HashTable *tablePtr; - int new; + int isNew; + ObjData *objData; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); if (tsdPtr->objThreadMap == NULL) { - tsdPtr->objThreadMap = (Tcl_HashTable *) - ckalloc(sizeof(Tcl_HashTable)); + tsdPtr->objThreadMap = ckalloc(sizeof(Tcl_HashTable)); Tcl_InitHashTable(tsdPtr->objThreadMap, TCL_ONE_WORD_KEYS); } tablePtr = tsdPtr->objThreadMap; - hPtr = Tcl_CreateHashEntry(tablePtr, (char *) objPtr, &new); - if (!new) { + hPtr = Tcl_CreateHashEntry(tablePtr, objPtr, &isNew); + if (!isNew) { Tcl_Panic("expected to create new entry for object map"); } - Tcl_SetHashValue(hPtr, NULL); + + /* + * Record the debugging information. + */ + + objData = ckalloc(sizeof(ObjData)); + objData->objPtr = objPtr; + objData->file = file; + objData->line = line; + Tcl_SetHashValue(hPtr, objData); } #endif /* TCL_THREADS */ } @@ -664,13 +1107,13 @@ void TclDbInitNewObj(objPtr) * * Tcl_NewObj -- * - * This procedure is normally called when not debugging: i.e., when + * This function is normally called when not debugging: i.e., when * TCL_MEM_DEBUG is not defined. It creates new Tcl objects that denote * the empty string. These objects have a NULL object type and NULL * string representation byte pointer. Type managers call this routine to * allocate new objects that they further initialize. * - * When TCL_MEM_DEBUG is defined, this procedure just returns the result + * When TCL_MEM_DEBUG is defined, this function just returns the result * of calling the debugging version Tcl_DbNewObj. * * Results: @@ -679,7 +1122,7 @@ void TclDbInitNewObj(objPtr) * to 0. * * Side effects: - * If compiling with TCL_COMPILE_STATS, this procedure increments the + * If compiling with TCL_COMPILE_STATS, this function increments the * global count of allocated objects (tclObjsAlloced). * *---------------------------------------------------------------------- @@ -689,7 +1132,7 @@ void TclDbInitNewObj(objPtr) #undef Tcl_NewObj Tcl_Obj * -Tcl_NewObj() +Tcl_NewObj(void) { return Tcl_DbNewObj("unknown", 0); } @@ -697,7 +1140,7 @@ Tcl_NewObj() #else /* if not TCL_MEM_DEBUG */ Tcl_Obj * -Tcl_NewObj() +Tcl_NewObj(void) { register Tcl_Obj *objPtr; @@ -715,15 +1158,15 @@ Tcl_NewObj() * * Tcl_DbNewObj -- * - * This procedure is normally called when debugging: i.e., when + * This function is normally called when debugging: i.e., when * TCL_MEM_DEBUG is defined. It creates new Tcl objects that denote the - * empty string. It is the same as the Tcl_NewObj procedure above except + * empty string. It is the same as the Tcl_NewObj function above except * that it calls Tcl_DbCkalloc directly with the file name and line * number from its caller. This simplifies debugging since then the * [memory active] command will report the correct file name and line * number when reporting objects that haven't been freed. * - * When TCL_MEM_DEBUG is not defined, this procedure just returns the + * When TCL_MEM_DEBUG is not defined, this function just returns the * result of calling Tcl_NewObj. * * Results: @@ -731,7 +1174,7 @@ Tcl_NewObj() * new object's typePtr is set NULL and its ref count is set to 0. * * Side effects: - * If compiling with TCL_COMPILE_STATS, this procedure increments the + * If compiling with TCL_COMPILE_STATS, this function increments the * global count of allocated objects (tclObjsAlloced). * *---------------------------------------------------------------------- @@ -740,10 +1183,10 @@ Tcl_NewObj() #ifdef TCL_MEM_DEBUG Tcl_Obj * -Tcl_DbNewObj(file, line) - register CONST char *file; /* The name of the source file calling this - * procedure; used for debugging. */ - register int line; /* Line number in the source file; used for +Tcl_DbNewObj( + register const char *file, /* The name of the source file calling this + * function; used for debugging. */ + register int line) /* Line number in the source file; used for * debugging. */ { register Tcl_Obj *objPtr; @@ -758,10 +1201,10 @@ Tcl_DbNewObj(file, line) #else /* if not TCL_MEM_DEBUG */ Tcl_Obj * -Tcl_DbNewObj(file, line) - CONST char *file; /* The name of the source file calling this - * procedure; used for debugging. */ - int line; /* Line number in the source file; used for +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. */ { return Tcl_NewObj(); @@ -773,7 +1216,7 @@ Tcl_DbNewObj(file, line) * * TclAllocateFreeObjects -- * - * Procedure to allocate a number of free Tcl_Objs. This is done using a + * Function to allocate a number of free Tcl_Objs. This is done using a * single ckalloc to reduce the overhead for Tcl_Obj allocation. * * Assumes mutex is held. @@ -784,7 +1227,7 @@ Tcl_DbNewObj(file, line) * Side effects: * tclFreeObjList, the head of the list of free Tcl_Objs, is set to the * first of a number of free Tcl_Obj's linked together by their - * internalRep.otherValuePtrs. + * internalRep.twoPtrValue.ptr1's. * *---------------------------------------------------------------------- */ @@ -792,7 +1235,7 @@ Tcl_DbNewObj(file, line) #define OBJS_TO_ALLOC_EACH_TIME 100 void -TclAllocateFreeObjects() +TclAllocateFreeObjects(void) { size_t bytesToAlloc = (OBJS_TO_ALLOC_EACH_TIME * sizeof(Tcl_Obj)); char *basePtr; @@ -800,21 +1243,20 @@ TclAllocateFreeObjects() register int i; /* - * This has been noted by Purify to be a potential leak. The problem is + * This has been noted by Purify to be a potential leak. The problem is * that Tcl, when not TCL_MEM_DEBUG compiled, keeps around all allocated * Tcl_Obj's, pointed to by tclFreeObjList, when freed instead of actually - * freeing the memory. TclFinalizeObjects() does not ckfree() this memory, + * freeing the memory. TclFinalizeObjects() does not ckfree() this memory, * but leaves it to Tcl's memory subsystem finalization to release it. * Purify apparently can't figure that out, and fires a false alarm. */ - basePtr = (char *) ckalloc(bytesToAlloc); - memset(basePtr, 0, bytesToAlloc); + basePtr = ckalloc(bytesToAlloc); prevPtr = NULL; objPtr = (Tcl_Obj *) basePtr; for (i = 0; i < OBJS_TO_ALLOC_EACH_TIME; i++) { - objPtr->internalRep.otherValuePtr = (VOID *) prevPtr; + objPtr->internalRep.twoPtrValue.ptr1 = prevPtr; prevPtr = objPtr; objPtr++; } @@ -827,7 +1269,7 @@ TclAllocateFreeObjects() * * TclFreeObj -- * - * This procedure frees the memory associated with the argument object. + * This function frees the memory associated with the argument object. * It is called by the tcl.h macro Tcl_DecrRefCount when an object's ref * count is zero. It is only "public" since it must be callable by that * macro wherever the macro is used. It should not be directly called by @@ -840,7 +1282,7 @@ TclAllocateFreeObjects() * Deallocates the storage for the object's Tcl_Obj structure after * deallocating the string representation and calling the type-specific * Tcl_FreeInternalRepProc to deallocate the object's internal - * representation. If compiling with TCL_COMPILE_STATS, this procedure + * representation. If compiling with TCL_COMPILE_STATS, this function * increments the global count of freed objects (tclObjsFreed). * *---------------------------------------------------------------------- @@ -848,10 +1290,10 @@ TclAllocateFreeObjects() #ifdef TCL_MEM_DEBUG void -TclFreeObj(objPtr) - register Tcl_Obj *objPtr; /* The object to be freed. */ +TclFreeObj( + register Tcl_Obj *objPtr) /* The object to be freed. */ { - register Tcl_ObjType *typePtr = objPtr->typePtr; + register const Tcl_ObjType *typePtr = objPtr->typePtr; /* * This macro declares a variable, so must come here... @@ -859,58 +1301,106 @@ TclFreeObj(objPtr) ObjInitDeletionContext(context); + /* + * Check for a double free of the same value. This is slightly tricky + * because it is customary to free a Tcl_Obj when its refcount falls + * either from 1 to 0, or from 0 to -1. Falling from -1 to -2, though, + * and so on, is always a sign of a botch in the caller. + */ if (objPtr->refCount < -1) { - Tcl_Panic("Reference count for %lx was negative", objPtr); + Tcl_Panic("Reference count for %p was negative", objPtr); } + /* + * Now, in case we just approved drop from 1 to 0 as acceptable, make + * sure we do not accept a second free when falling from 0 to -1. + * Skip that possibility so any double free will trigger the panic. + */ + objPtr->refCount = -1; + + /* + * Invalidate the string rep first so we can use the bytes value for our + * pointer chain, and signal an obj deletion (as opposed to shimmering) + * with 'length == -1'. + */ + + TclInvalidateStringRep(objPtr); + objPtr->length = -1; if (ObjDeletePending(context)) { PushObjToDelete(context, objPtr); } else { + TCL_DTRACE_OBJ_FREE(objPtr); if ((typePtr != NULL) && (typePtr->freeIntRepProc != NULL)) { ObjDeletionLock(context); typePtr->freeIntRepProc(objPtr); ObjDeletionUnlock(context); } - Tcl_InvalidateStringRep(objPtr); Tcl_MutexLock(&tclObjMutex); - ckfree((char *) objPtr); + ckfree(objPtr); Tcl_MutexUnlock(&tclObjMutex); -#ifdef TCL_COMPILE_STATS - tclObjsFreed++; -#endif /* TCL_COMPILE_STATS */ + TclIncrObjsFreed(); ObjDeletionLock(context); while (ObjOnStack(context)) { Tcl_Obj *objToFree; - PopObjToDelete(context,objToFree); + PopObjToDelete(context, objToFree); + TCL_DTRACE_OBJ_FREE(objToFree); TclFreeIntRep(objToFree); Tcl_MutexLock(&tclObjMutex); - ckfree((char *) objToFree); + ckfree(objToFree); Tcl_MutexUnlock(&tclObjMutex); -#ifdef TCL_COMPILE_STATS - tclObjsFreed++; -#endif /* TCL_COMPILE_STATS */ + TclIncrObjsFreed(); } ObjDeletionUnlock(context); } + + /* + * We cannot use TclGetContinuationTable() here, because that may + * re-initialize the thread-data for calls coming after the finalization. + * We have to access it using the low-level call and then check for + * validity. This function can be called after TclFinalizeThreadData() has + * already killed the thread-global data structures. Performing + * TCL_TSD_INIT will leave us with an un-initialized memory block upon + * which we crash (if we where to access the uninitialized hashtable). + */ + + { + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + Tcl_HashEntry *hPtr; + + if (tsdPtr->lineCLPtr) { + hPtr = Tcl_FindHashEntry(tsdPtr->lineCLPtr, objPtr); + if (hPtr) { + ckfree(Tcl_GetHashValue(hPtr)); + Tcl_DeleteHashEntry(hPtr); + } + } + } } #else /* TCL_MEM_DEBUG */ void -TclFreeObj(objPtr) - register Tcl_Obj *objPtr; /* The object to be freed. */ +TclFreeObj( + register Tcl_Obj *objPtr) /* The object to be freed. */ { + /* + * Invalidate the string rep first so we can use the bytes value for our + * pointer chain, and signal an obj deletion (as opposed to shimmering) + * with 'length == -1'. + */ + + TclInvalidateStringRep(objPtr); + objPtr->length = -1; + if (!objPtr->typePtr || !objPtr->typePtr->freeIntRepProc) { /* * objPtr can be freed safely, as it will not attempt to free any * other objects: it will not cause recursive calls to this function. */ - if (objPtr->bytes && (objPtr->bytes != tclEmptyStringRep)) { - ckfree((char *) objPtr->bytes); - } + TCL_DTRACE_OBJ_FREE(objPtr); TclFreeObjStorage(objPtr); TclIncrObjsFreed(); } else { @@ -933,19 +1423,19 @@ TclFreeObj(objPtr) * satisfy this. */ + TCL_DTRACE_OBJ_FREE(objPtr); ObjDeletionLock(context); objPtr->typePtr->freeIntRepProc(objPtr); ObjDeletionUnlock(context); - if (objPtr->bytes && (objPtr->bytes != tclEmptyStringRep)) { - ckfree((char *) objPtr->bytes); - } TclFreeObjStorage(objPtr); TclIncrObjsFreed(); ObjDeletionLock(context); while (ObjOnStack(context)) { Tcl_Obj *objToFree; - PopObjToDelete(context,objToFree); + + PopObjToDelete(context, objToFree); + TCL_DTRACE_OBJ_FREE(objToFree); if ((objToFree->typePtr != NULL) && (objToFree->typePtr->freeIntRepProc != NULL)) { objToFree->typePtr->freeIntRepProc(objToFree); @@ -956,8 +1446,56 @@ TclFreeObj(objPtr) ObjDeletionUnlock(context); } } + + /* + * We cannot use TclGetContinuationTable() here, because that may + * re-initialize the thread-data for calls coming after the finalization. + * We have to access it using the low-level call and then check for + * validity. This function can be called after TclFinalizeThreadData() has + * already killed the thread-global data structures. Performing + * TCL_TSD_INIT will leave us with an un-initialized memory block upon + * which we crash (if we where to access the uninitialized hashtable). + */ + + { + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + Tcl_HashEntry *hPtr; + + if (tsdPtr->lineCLPtr) { + hPtr = Tcl_FindHashEntry(tsdPtr->lineCLPtr, objPtr); + if (hPtr) { + ckfree(Tcl_GetHashValue(hPtr)); + Tcl_DeleteHashEntry(hPtr); + } + } + } +} +#endif /* TCL_MEM_DEBUG */ + +/* + *---------------------------------------------------------------------- + * + * TclObjBeingDeleted -- + * + * This function returns 1 when the Tcl_Obj is being deleted. It is + * provided for the rare cases where the reason for the loss of an + * internal rep might be relevant. [FR 1512138] + * + * Results: + * 1 if being deleted, 0 otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TclObjBeingDeleted( + Tcl_Obj *objPtr) +{ + return (objPtr->length == -1); } -#endif /* *---------------------------------------------------------------------- @@ -988,30 +1526,47 @@ TclFreeObj(objPtr) *---------------------------------------------------------------------- */ +#define SetDuplicateObj(dupPtr, objPtr) \ + { \ + const Tcl_ObjType *typePtr = (objPtr)->typePtr; \ + const char *bytes = (objPtr)->bytes; \ + if (bytes) { \ + TclInitStringRep((dupPtr), bytes, (objPtr)->length); \ + } else { \ + (dupPtr)->bytes = NULL; \ + } \ + if (typePtr) { \ + if (typePtr->dupIntRepProc) { \ + typePtr->dupIntRepProc((objPtr), (dupPtr)); \ + } else { \ + (dupPtr)->internalRep = (objPtr)->internalRep; \ + (dupPtr)->typePtr = typePtr; \ + } \ + } \ + } + Tcl_Obj * -Tcl_DuplicateObj(objPtr) - register Tcl_Obj *objPtr; /* The object to duplicate. */ +Tcl_DuplicateObj( + Tcl_Obj *objPtr) /* The object to duplicate. */ { - register Tcl_ObjType *typePtr = objPtr->typePtr; - register Tcl_Obj *dupPtr; + Tcl_Obj *dupPtr; TclNewObj(dupPtr); + SetDuplicateObj(dupPtr, objPtr); + return dupPtr; +} - if (objPtr->bytes == NULL) { - dupPtr->bytes = NULL; - } else if (objPtr->bytes != tclEmptyStringRep) { - TclInitStringRep(dupPtr, objPtr->bytes, objPtr->length); - } - - if (typePtr != NULL) { - if (typePtr->dupIntRepProc == NULL) { - dupPtr->internalRep = objPtr->internalRep; - dupPtr->typePtr = typePtr; - } else { - (*typePtr->dupIntRepProc)(objPtr, dupPtr); - } +void +TclSetDuplicateObj( + Tcl_Obj *dupPtr, + Tcl_Obj *objPtr) +{ + if (Tcl_IsShared(dupPtr)) { + Tcl_Panic("%s called with shared object", "TclSetDuplicateObj"); } - return dupPtr; + TclInvalidateStringRep(dupPtr); + TclFreeIntRep(dupPtr); + SetDuplicateObj(dupPtr, objPtr); } /* @@ -1036,19 +1591,37 @@ Tcl_DuplicateObj(objPtr) */ char * -Tcl_GetString(objPtr) - register Tcl_Obj *objPtr; /* Object whose string rep byte pointer should +Tcl_GetString( + register 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) { + /* + * 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. + */ + Tcl_Panic("UpdateStringProc should not be invoked for type %s", objPtr->typePtr->name); } - (*objPtr->typePtr->updateStringProc)(objPtr); + 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; } @@ -1076,20 +1649,14 @@ Tcl_GetString(objPtr) */ char * -Tcl_GetStringFromObj(objPtr, lengthPtr) - register Tcl_Obj *objPtr; /* Object whose string rep byte pointer should +Tcl_GetStringFromObj( + register Tcl_Obj *objPtr, /* Object whose string rep byte pointer should * be returned. */ - register int *lengthPtr; /* If non-NULL, the location where the string + register int *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) { - if (objPtr->typePtr->updateStringProc == NULL) { - Tcl_Panic("UpdateStringProc should not be invoked for type %s", - objPtr->typePtr->name); - } - (*objPtr->typePtr->updateStringProc)(objPtr); - } + (void) TclGetString(objPtr); if (lengthPtr != NULL) { *lengthPtr = objPtr->length; @@ -1102,7 +1669,7 @@ Tcl_GetStringFromObj(objPtr, lengthPtr) * * Tcl_InvalidateStringRep -- * - * This procedure is called to invalidate an object's string + * This function is called to invalidate an object's string * representation. * * Results: @@ -1116,25 +1683,24 @@ Tcl_GetStringFromObj(objPtr, lengthPtr) */ void -Tcl_InvalidateStringRep(objPtr) - register Tcl_Obj *objPtr; /* Object whose string rep byte pointer should +Tcl_InvalidateStringRep( + register Tcl_Obj *objPtr) /* Object whose string rep byte pointer should * be freed. */ { TclInvalidateStringRep(objPtr); } - /* *---------------------------------------------------------------------- * * Tcl_NewBooleanObj -- * - * This procedure is normally called when not debugging: i.e., when + * This function is normally called when not debugging: i.e., when * TCL_MEM_DEBUG is not defined. It creates a new Tcl_Obj and * initializes it from the argument boolean value. A nonzero "boolValue" * is coerced to 1. * - * When TCL_MEM_DEBUG is defined, this procedure just returns the result + * When TCL_MEM_DEBUG is defined, this function just returns the result * of calling the debugging version Tcl_DbNewBooleanObj. * * Results: @@ -1147,12 +1713,12 @@ Tcl_InvalidateStringRep(objPtr) *---------------------------------------------------------------------- */ -#ifdef TCL_MEM_DEBUG #undef Tcl_NewBooleanObj +#ifdef TCL_MEM_DEBUG Tcl_Obj * -Tcl_NewBooleanObj(boolValue) - register int boolValue; /* Boolean used to initialize new object. */ +Tcl_NewBooleanObj( + register int boolValue) /* Boolean used to initialize new object. */ { return Tcl_DbNewBooleanObj(boolValue, "unknown", 0); } @@ -1160,8 +1726,8 @@ Tcl_NewBooleanObj(boolValue) #else /* if not TCL_MEM_DEBUG */ Tcl_Obj * -Tcl_NewBooleanObj(boolValue) - register int boolValue; /* Boolean used to initialize new object. */ +Tcl_NewBooleanObj( + register int boolValue) /* Boolean used to initialize new object. */ { register Tcl_Obj *objPtr; @@ -1175,15 +1741,15 @@ Tcl_NewBooleanObj(boolValue) * * Tcl_DbNewBooleanObj -- * - * This procedure is normally called when debugging: i.e., when + * This function is normally called when debugging: i.e., when * TCL_MEM_DEBUG is defined. It creates new boolean objects. It is the - * same as the Tcl_NewBooleanObj procedure above except that it calls + * same as the Tcl_NewBooleanObj function above except that it calls * Tcl_DbCkalloc directly with the file name and line number from its * caller. This simplifies debugging since then the [memory active] * command will report the correct file name and line number when * reporting objects that haven't been freed. * - * When TCL_MEM_DEBUG is not defined, this procedure just returns the + * When TCL_MEM_DEBUG is not defined, this function just returns the * result of calling Tcl_NewBooleanObj. * * Results: @@ -1196,14 +1762,15 @@ Tcl_NewBooleanObj(boolValue) *---------------------------------------------------------------------- */ +#undef Tcl_DbNewBooleanObj #ifdef TCL_MEM_DEBUG Tcl_Obj * -Tcl_DbNewBooleanObj(boolValue, file, line) - register int boolValue; /* Boolean used to initialize new object. */ - CONST char *file; /* The name of the source file calling this - * procedure; used for debugging. */ - int line; /* Line number in the source file; used for +Tcl_DbNewBooleanObj( + register int boolValue, /* 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. */ { register Tcl_Obj *objPtr; @@ -1219,11 +1786,11 @@ Tcl_DbNewBooleanObj(boolValue, file, line) #else /* if not TCL_MEM_DEBUG */ Tcl_Obj * -Tcl_DbNewBooleanObj(boolValue, file, line) - register int boolValue; /* Boolean used to initialize new object. */ - CONST char *file; /* The name of the source file calling this - * procedure; used for debugging. */ - int line; /* Line number in the source file; used for +Tcl_DbNewBooleanObj( + register int boolValue, /* 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. */ { return Tcl_NewBooleanObj(boolValue); @@ -1248,13 +1815,14 @@ Tcl_DbNewBooleanObj(boolValue, file, line) *---------------------------------------------------------------------- */ +#undef Tcl_SetBooleanObj void -Tcl_SetBooleanObj(objPtr, boolValue) - register Tcl_Obj *objPtr; /* Object whose internal rep to init. */ - register int boolValue; /* Boolean used to set object's value. */ +Tcl_SetBooleanObj( + register Tcl_Obj *objPtr, /* Object whose internal rep to init. */ + register int boolValue) /* Boolean used to set object's value. */ { if (Tcl_IsShared(objPtr)) { - Tcl_Panic("Tcl_SetBooleanObj called with shared object"); + Tcl_Panic("%s called with shared object", "Tcl_SetBooleanObj"); } TclSetBooleanObj(objPtr, boolValue); @@ -1280,104 +1848,56 @@ Tcl_SetBooleanObj(objPtr, boolValue) */ int -Tcl_GetBooleanFromObj(interp, objPtr, boolPtr) - Tcl_Interp *interp; /* Used for error reporting if not NULL. */ - register Tcl_Obj *objPtr; /* The object from which to get boolean. */ - register int *boolPtr; /* Place to store resulting boolean. */ +Tcl_GetBooleanFromObj( + Tcl_Interp *interp, /* Used for error reporting if not NULL. */ + register Tcl_Obj *objPtr, /* The object from which to get boolean. */ + register int *boolPtr) /* Place to store resulting boolean. */ { - double d; - long l; - - /* - * The flow through this routine is "optimized" to avoid the generation of - * string rep. for "pure" numeric values. However, once the string rep is - * generated it's fairly inefficient at determining a string is *not* a - * valid boolean. It has to scan the string as many as four times (ruling - * out "double", "long", "wideint", and "boolean" in turn) to figure out - * that an invalid boolean value is stored in objPtr->bytes. - */ - - if (objPtr->typePtr == &tclIntType) { - *boolPtr = (int) (objPtr->internalRep.longValue != 0); - return TCL_OK; - } - if (objPtr->typePtr == &tclBooleanType) { - *boolPtr = (int) objPtr->internalRep.longValue; - return TCL_OK; - } - if (objPtr->typePtr == &tclWideIntType) { - *boolPtr = (int) (objPtr->internalRep.wideValue != 0); - return TCL_OK; - } - - /* - * Caution: Don't be tempted to check directly for the "double" - * Tcl_ObjType and then compare the intrep to 0.0. This isn't reliable - * because a "double" Tcl_ObjType can hold the NaN value. Use the API - * Tcl_GetDoubleFromObj, which does the checking for us. - */ - - /* - * The following call retrieves a numeric value without generating the - * string rep of a double. - */ - - if (Tcl_GetDoubleFromObj(NULL, objPtr, &d) == TCL_OK) { - *boolPtr = (d != 0.0); - - /* - * Tcl_GetDoubleFromObj() will succeed on the strings "0" and "1", but - * we'd rather keep those values around as a better objType for - * boolean value. Following call will shimmer appropriately. - */ - - if (objPtr->bytes != NULL) { - SetBooleanFromAny(NULL, objPtr); + do { + if (objPtr->typePtr == &tclIntType) { + *boolPtr = (objPtr->internalRep.longValue != 0); + return TCL_OK; } - return TCL_OK; - } + if (objPtr->typePtr == &tclBooleanType) { + *boolPtr = (int) objPtr->internalRep.longValue; + return TCL_OK; + } + if (objPtr->typePtr == &tclDoubleType) { + /* + * Caution: Don't be tempted to check directly for the "double" + * Tcl_ObjType and then compare the intrep to 0.0. This isn't + * reliable because a "double" Tcl_ObjType can hold the NaN value. + * Use the API Tcl_GetDoubleFromObj, which does the checking and + * sets the proper error message for us. + */ - /* - * Value didn't already have a numeric intrep, but perhaps we can generate - * one. Try a long value first... - */ + double d; - if (Tcl_GetLongFromObj(NULL, objPtr, &l) == TCL_OK) { - *boolPtr = (l != 0); - return TCL_OK; - } + if (Tcl_GetDoubleFromObj(interp, objPtr, &d) != TCL_OK) { + return TCL_ERROR; + } + *boolPtr = (d != 0.0); + return TCL_OK; + } + if (objPtr->typePtr == &tclBignumType) { + *boolPtr = 1; + return TCL_OK; + } #ifndef TCL_WIDE_INT_IS_LONG - else { - Tcl_WideInt w; - - /* - * ...then a wide. Check in that order so that we don't promote - * anything to wide unnecessarily. - */ - - if (Tcl_GetWideIntFromObj(NULL, objPtr, &w) == TCL_OK) { - *boolPtr = (w != 0); + if (objPtr->typePtr == &tclWideIntType) { + *boolPtr = (objPtr->internalRep.wideValue != 0); return TCL_OK; } - } #endif - - /* - * Finally, check for the string values like "yes" and generate error - * message for non-boolean values. - */ - - if (SetBooleanFromAny(interp, objPtr) == TCL_OK) { - *boolPtr = (int) objPtr->internalRep.longValue; - return TCL_OK; - } + } while ((ParseBoolean(objPtr) == TCL_OK) || (TCL_OK == + TclParseNumber(interp, objPtr, "boolean value", NULL,-1,NULL,0))); return TCL_ERROR; } /* *---------------------------------------------------------------------- * - * SetBooleanFromAny -- + * TclSetBooleanFromAny -- * * Attempt to generate a boolean internal form for the Tcl object * "objPtr". @@ -1394,14 +1914,11 @@ Tcl_GetBooleanFromObj(interp, objPtr, boolPtr) *---------------------------------------------------------------------- */ -static int -SetBooleanFromAny(interp, objPtr) - Tcl_Interp *interp; /* Used for error reporting if not NULL. */ - register Tcl_Obj *objPtr; /* The object to convert. */ +int +TclSetBooleanFromAny( + Tcl_Interp *interp, /* Used for error reporting if not NULL. */ + register Tcl_Obj *objPtr) /* The object to convert. */ { - char *str, lowerCase[6]; - int i, newBool, length; - /* * For some "pure" numeric Tcl_ObjTypes (no string rep), we can determine * whether a boolean conversion is possible without generating the string @@ -1409,9 +1926,6 @@ SetBooleanFromAny(interp, objPtr) */ if (objPtr->bytes == NULL) { - if (objPtr->typePtr == &tclDoubleType) { - goto badBoolean; - } if (objPtr->typePtr == &tclIntType) { switch (objPtr->internalRep.longValue) { case 0L: case 1L: @@ -1419,26 +1933,55 @@ SetBooleanFromAny(interp, objPtr) } goto badBoolean; } + + if (objPtr->typePtr == &tclBignumType) { + goto badBoolean; + } + +#ifndef TCL_WIDE_INT_IS_LONG if (objPtr->typePtr == &tclWideIntType) { - Tcl_WideInt w = objPtr->internalRep.wideValue; - if (w == 0 || w == 1) { - newBool = (int) w; - goto numericBoolean; - } else { - goto badBoolean; - } + goto badBoolean; + } +#endif + + if (objPtr->typePtr == &tclDoubleType) { + goto badBoolean; } } - /* - * Parse the string as a boolean. We use an implementation here that - * doesn't report errors in interp if interp is NULL. - */ + if (ParseBoolean(objPtr) == TCL_OK) { + return TCL_OK; + } + + badBoolean: + if (interp != NULL) { + int length; + const char *str = Tcl_GetStringFromObj(objPtr, &length); + Tcl_Obj *msg; + + TclNewLiteralStringObj(msg, "expected boolean value but got \""); + Tcl_AppendLimitedToObj(msg, str, length, 50, ""); + Tcl_AppendToObj(msg, "\"", -1); + Tcl_SetObjResult(interp, msg); + Tcl_SetErrorCode(interp, "TCL", "VALUE", "BOOLEAN", NULL); + } + return TCL_ERROR; +} + +static int +ParseBoolean( + register Tcl_Obj *objPtr) /* The object to parse/convert. */ +{ + int i, length, newBool; + char lowerCase[6]; + const char *str = TclGetStringFromObj(objPtr, &length); - str = Tcl_GetStringFromObj(objPtr, &length); if ((length == 0) || (length > 5)) { - /* longest valid boolean string rep. is "false" */ - goto badBoolean; + /* + * Longest valid boolean string rep. is "false". + */ + + return TCL_ERROR; } switch (str[0]) { @@ -1447,22 +1990,23 @@ SetBooleanFromAny(interp, objPtr) newBool = 0; goto numericBoolean; } - goto badBoolean; + return TCL_ERROR; case '1': if (length == 1) { newBool = 1; goto numericBoolean; } - goto badBoolean; + return TCL_ERROR; } /* - * Force to lower case for case-insensitive detection. Filter out known + * Force to lower case for case-insensitive detection. Filter out known * invalid characters at the same time. */ for (i=0; i < length; i++) { char c = str[i]; + switch (c) { case 'A': case 'E': case 'F': case 'L': case 'N': case 'O': case 'R': case 'S': case 'T': case 'U': case 'Y': @@ -1473,7 +2017,7 @@ SetBooleanFromAny(interp, objPtr) lowerCase[i] = c; break; default: - goto badBoolean; + return TCL_ERROR; } } lowerCase[length] = 0; @@ -1486,28 +2030,28 @@ SetBooleanFromAny(interp, objPtr) newBool = 1; goto goodBoolean; } - goto badBoolean; + return TCL_ERROR; case 'n': if (strncmp(lowerCase, "no", (size_t) length) == 0) { newBool = 0; goto goodBoolean; } - goto badBoolean; + return TCL_ERROR; case 't': if (strncmp(lowerCase, "true", (size_t) length) == 0) { newBool = 1; goto goodBoolean; } - goto badBoolean; + return TCL_ERROR; case 'f': if (strncmp(lowerCase, "false", (size_t) length) == 0) { newBool = 0; goto goodBoolean; } - goto badBoolean; + return TCL_ERROR; case 'o': if (length < 2) { - goto badBoolean; + return TCL_ERROR; } if (strncmp(lowerCase, "on", (size_t) length) == 0) { newBool = 1; @@ -1516,9 +2060,9 @@ SetBooleanFromAny(interp, objPtr) newBool = 0; goto goodBoolean; } - goto badBoolean; + return TCL_ERROR; default: - goto badBoolean; + return TCL_ERROR; } /* @@ -1533,17 +2077,6 @@ SetBooleanFromAny(interp, objPtr) objPtr->typePtr = &tclBooleanType; return TCL_OK; - badBoolean: - if (interp != NULL) { - Tcl_Obj *msg = - Tcl_NewStringObj("expected boolean value but got \"", -1); - str = Tcl_GetStringFromObj(objPtr, &length); - TclAppendLimitedToObj(msg, str, length, 50, ""); - Tcl_AppendToObj(msg, "\"", -1); - Tcl_SetObjResult(interp, msg); - } - return TCL_ERROR; - numericBoolean: TclFreeIntRep(objPtr); objPtr->internalRep.longValue = newBool; @@ -1556,11 +2089,11 @@ SetBooleanFromAny(interp, objPtr) * * Tcl_NewDoubleObj -- * - * This procedure is normally called when not debugging: i.e., when + * This function is normally called when not debugging: i.e., when * TCL_MEM_DEBUG is not defined. It creates a new double object and * initializes it from the argument double value. * - * When TCL_MEM_DEBUG is defined, this procedure just returns the result + * When TCL_MEM_DEBUG is defined, this function just returns the result * of calling the debugging version Tcl_DbNewDoubleObj. * * Results: @@ -1577,8 +2110,8 @@ SetBooleanFromAny(interp, objPtr) #undef Tcl_NewDoubleObj Tcl_Obj * -Tcl_NewDoubleObj(dblValue) - register double dblValue; /* Double used to initialize the object. */ +Tcl_NewDoubleObj( + register double dblValue) /* Double used to initialize the object. */ { return Tcl_DbNewDoubleObj(dblValue, "unknown", 0); } @@ -1586,8 +2119,8 @@ Tcl_NewDoubleObj(dblValue) #else /* if not TCL_MEM_DEBUG */ Tcl_Obj * -Tcl_NewDoubleObj(dblValue) - register double dblValue; /* Double used to initialize the object. */ +Tcl_NewDoubleObj( + register double dblValue) /* Double used to initialize the object. */ { register Tcl_Obj *objPtr; @@ -1601,15 +2134,15 @@ Tcl_NewDoubleObj(dblValue) * * Tcl_DbNewDoubleObj -- * - * This procedure is normally called when debugging: i.e., when + * This function is normally called when debugging: i.e., when * TCL_MEM_DEBUG is defined. It creates new double objects. It is the - * same as the Tcl_NewDoubleObj procedure above except that it calls + * same as the Tcl_NewDoubleObj function above except that it calls * Tcl_DbCkalloc directly with the file name and line number from its * caller. This simplifies debugging since then the [memory active] * command will report the correct file name and line number when * reporting objects that haven't been freed. * - * When TCL_MEM_DEBUG is not defined, this procedure just returns the + * When TCL_MEM_DEBUG is not defined, this function just returns the * result of calling Tcl_NewDoubleObj. * * Results: @@ -1625,11 +2158,11 @@ Tcl_NewDoubleObj(dblValue) #ifdef TCL_MEM_DEBUG Tcl_Obj * -Tcl_DbNewDoubleObj(dblValue, file, line) - register double dblValue; /* Double used to initialize the object. */ - CONST char *file; /* The name of the source file calling this - * procedure; used for debugging. */ - int line; /* Line number in the source file; used for +Tcl_DbNewDoubleObj( + register 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. */ { register Tcl_Obj *objPtr; @@ -1645,11 +2178,11 @@ Tcl_DbNewDoubleObj(dblValue, file, line) #else /* if not TCL_MEM_DEBUG */ Tcl_Obj * -Tcl_DbNewDoubleObj(dblValue, file, line) - register double dblValue; /* Double used to initialize the object. */ - CONST char *file; /* The name of the source file calling this - * procedure; used for debugging. */ - int line; /* Line number in the source file; used for +Tcl_DbNewDoubleObj( + register 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. */ { return Tcl_NewDoubleObj(dblValue); @@ -1675,12 +2208,12 @@ Tcl_DbNewDoubleObj(dblValue, file, line) */ void -Tcl_SetDoubleObj(objPtr, dblValue) - register Tcl_Obj *objPtr; /* Object whose internal rep to init. */ - register double dblValue; /* Double used to set the object's value. */ +Tcl_SetDoubleObj( + register Tcl_Obj *objPtr, /* Object whose internal rep to init. */ + register double dblValue) /* Double used to set the object's value. */ { if (Tcl_IsShared(objPtr)) { - Tcl_Panic("Tcl_SetDoubleObj called with shared object"); + Tcl_Panic("%s called with shared object", "Tcl_SetDoubleObj"); } TclSetDoubleObj(objPtr, dblValue); @@ -1707,34 +2240,44 @@ Tcl_SetDoubleObj(objPtr, dblValue) */ int -Tcl_GetDoubleFromObj(interp, objPtr, dblPtr) - Tcl_Interp *interp; /* Used for error reporting if not NULL. */ - register Tcl_Obj *objPtr; /* The object from which to get a double. */ - register double *dblPtr; /* Place to store resulting double. */ +Tcl_GetDoubleFromObj( + Tcl_Interp *interp, /* Used for error reporting if not NULL. */ + register Tcl_Obj *objPtr, /* The object from which to get a double. */ + register double *dblPtr) /* Place to store resulting double. */ { - register int result; + do { + if (objPtr->typePtr == &tclDoubleType) { + if (TclIsNaN(objPtr->internalRep.doubleValue)) { + if (interp != NULL) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "floating point value is Not a Number", -1)); + Tcl_SetErrorCode(interp, "TCL", "VALUE", "DOUBLE", "NAN", + NULL); + } + return TCL_ERROR; + } + *dblPtr = (double) objPtr->internalRep.doubleValue; + return TCL_OK; + } + if (objPtr->typePtr == &tclIntType) { + *dblPtr = objPtr->internalRep.longValue; + return TCL_OK; + } + if (objPtr->typePtr == &tclBignumType) { + mp_int big; - if (objPtr->typePtr == &tclIntType) { - *dblPtr = objPtr->internalRep.longValue; - return TCL_OK; - } else if (objPtr->typePtr == &tclWideIntType) { - *dblPtr = (double) objPtr->internalRep.wideValue; - return TCL_OK; - } else if (objPtr->typePtr != &tclDoubleType) { - result = SetDoubleFromAny(interp, objPtr); - if (result != TCL_OK) { - return TCL_ERROR; + UNPACK_BIGNUM(objPtr, big); + *dblPtr = TclBignumToDouble(&big); + return TCL_OK; } - } - if (IS_NAN(objPtr->internalRep.doubleValue)) { - if (interp != NULL) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "floating point value is Not a Number", -1)); +#ifndef TCL_WIDE_INT_IS_LONG + if (objPtr->typePtr == &tclWideIntType) { + *dblPtr = (double) objPtr->internalRep.wideValue; + return TCL_OK; } - return TCL_ERROR; - } - *dblPtr = objPtr->internalRep.doubleValue; - return TCL_OK; +#endif + } while (SetDoubleFromAny(interp, objPtr) == TCL_OK); + return TCL_ERROR; } /* @@ -1758,70 +2301,12 @@ Tcl_GetDoubleFromObj(interp, objPtr, dblPtr) */ static int -SetDoubleFromAny(interp, objPtr) - Tcl_Interp *interp; /* Used for error reporting if not NULL. */ - register Tcl_Obj *objPtr; /* The object to convert. */ +SetDoubleFromAny( + Tcl_Interp *interp, /* Used for error reporting if not NULL. */ + register Tcl_Obj *objPtr) /* The object to convert. */ { - CONST char *string, *end; - double newDouble; - int length; - - /* - * Get the string representation. Make it up-to-date if necessary. - */ - - string = Tcl_GetStringFromObj(objPtr, &length); - - /* - * Now parse "objPtr"s string as an double. Numbers can't have embedded - * NULLs. We use an implementation here that doesn't report errors in - * interp if interp is NULL. - */ - - errno = 0; - newDouble = TclStrToD(string, &end); - if (end == string) { - badDouble: - if (interp != NULL) { - Tcl_Obj *msg = Tcl_NewStringObj( - "expected floating-point number but got \"", -1); - TclAppendLimitedToObj(msg, string, length, 50, ""); - Tcl_AppendToObj(msg, "\"", -1); - Tcl_SetObjResult(interp, msg); - } - return TCL_ERROR; - } - - /* - * Make sure that the string has no garbage after the end of the double. - */ - - while ((end < (string+length)) - && isspace(UCHAR(*end))) { /* INTL: ISO space. */ - end++; - } - if (end != (string+length)) { - goto badDouble; - } - - if (errno != 0 && errno != ERANGE) { - if (interp != NULL) { - TclExprFloatError(interp, newDouble); - } - return TCL_ERROR; - } - - /* - * The conversion to double succeeded. Free the old internalRep before - * setting the new one. We do this as late as possible to allow the - * conversion code, in particular Tcl_GetStringFromObj, to use that old - * internalRep. - */ - - TclFreeIntRep(objPtr); - objPtr->internalRep.doubleValue = newDouble; - objPtr->typePtr = &tclDoubleType; - return TCL_OK; + return TclParseNumber(interp, objPtr, "floating-point number", NULL, -1, + NULL, 0); } /* @@ -1831,7 +2316,7 @@ SetDoubleFromAny(interp, objPtr) * * Update the string representation for a double-precision floating point * object. This must obey the current tcl_precision value for - * double-to-string conversions. Note: This procedure does not free an + * double-to-string conversions. Note: This function does not free an * existing old string rep so storage will be lost if this has not * already been done. * @@ -1846,18 +2331,17 @@ SetDoubleFromAny(interp, objPtr) */ static void -UpdateStringOfDouble(objPtr) - register Tcl_Obj *objPtr; /* Double obj with string rep to update. */ +UpdateStringOfDouble( + register Tcl_Obj *objPtr) /* Double obj with string rep to update. */ { char buffer[TCL_DOUBLE_SPACE]; register int len; - Tcl_PrintDouble((Tcl_Interp *) NULL, objPtr->internalRep.doubleValue, - buffer); + Tcl_PrintDouble(NULL, objPtr->internalRep.doubleValue, buffer); len = strlen(buffer); - objPtr->bytes = (char *) ckalloc((unsigned) len + 1); - strcpy(objPtr->bytes, buffer); + objPtr->bytes = ckalloc(len + 1); + memcpy(objPtr->bytes, buffer, (unsigned) len + 1); objPtr->length = len; } @@ -1868,7 +2352,7 @@ UpdateStringOfDouble(objPtr) * * If a client is compiled with TCL_MEM_DEBUG defined, calls to * Tcl_NewIntObj to create a new integer object end up calling the - * debugging procedure Tcl_DbNewLongObj instead. + * debugging function Tcl_DbNewLongObj instead. * * Otherwise, if the client is compiled without TCL_MEM_DEBUG defined, * calls to Tcl_NewIntObj result in a call to one of the two @@ -1891,12 +2375,12 @@ UpdateStringOfDouble(objPtr) *---------------------------------------------------------------------- */ -#ifdef TCL_MEM_DEBUG #undef Tcl_NewIntObj +#ifdef TCL_MEM_DEBUG Tcl_Obj * -Tcl_NewIntObj(intValue) - register int intValue; /* Int used to initialize the new object. */ +Tcl_NewIntObj( + register int intValue) /* Int used to initialize the new object. */ { return Tcl_DbNewLongObj((long)intValue, "unknown", 0); } @@ -1904,8 +2388,8 @@ Tcl_NewIntObj(intValue) #else /* if not TCL_MEM_DEBUG */ Tcl_Obj * -Tcl_NewIntObj(intValue) - register int intValue; /* Int used to initialize the new object. */ +Tcl_NewIntObj( + register int intValue) /* Int used to initialize the new object. */ { register Tcl_Obj *objPtr; @@ -1932,13 +2416,14 @@ Tcl_NewIntObj(intValue) *---------------------------------------------------------------------- */ +#undef Tcl_SetIntObj void -Tcl_SetIntObj(objPtr, intValue) - register Tcl_Obj *objPtr; /* Object whose internal rep to init. */ - register int intValue; /* Integer used to set object's value. */ +Tcl_SetIntObj( + register Tcl_Obj *objPtr, /* Object whose internal rep to init. */ + register int intValue) /* Integer used to set object's value. */ { if (Tcl_IsShared(objPtr)) { - Tcl_Panic("Tcl_SetIntObj called with shared object"); + Tcl_Panic("%s called with shared object", "Tcl_SetIntObj"); } TclSetIntObj(objPtr, intValue); @@ -1971,50 +2456,31 @@ Tcl_SetIntObj(objPtr, intValue) */ int -Tcl_GetIntFromObj(interp, objPtr, intPtr) - Tcl_Interp *interp; /* Used for error reporting if not NULL. */ - register Tcl_Obj *objPtr; /* The object from which to get a int. */ - register int *intPtr; /* Place to store resulting int. */ +Tcl_GetIntFromObj( + Tcl_Interp *interp, /* Used for error reporting if not NULL. */ + register Tcl_Obj *objPtr, /* The object from which to get a int. */ + register int *intPtr) /* Place to store resulting int. */ { - int result; - Tcl_WideInt w = 0; - - /* - * If the object isn't already an integer of any width, try to convert it - * to one. - */ - - if (objPtr->typePtr != &tclIntType && objPtr->typePtr != &tclWideIntType) { - result = SetIntOrWideFromAny(interp, objPtr); - if (result != TCL_OK) { - return result; - } - } - - /* - * Object should now be either int or wide. Get its value. - */ +#if (LONG_MAX == INT_MAX) + return TclGetLongFromObj(interp, objPtr, (long *) intPtr); +#else + long l; -#ifndef TCL_WIDE_INT_IS_LONG - if (objPtr->typePtr == &tclWideIntType) { - w = objPtr->internalRep.wideValue; - } else -#endif - { - w = Tcl_LongAsWide(objPtr->internalRep.longValue); + if (TclGetLongFromObj(interp, objPtr, &l) != TCL_OK) { + return TCL_ERROR; } - - if ((LLONG_MAX > UINT_MAX) - && ((w > UINT_MAX) || (w < -(Tcl_WideInt)UINT_MAX))) { + if ((ULONG_MAX > UINT_MAX) && ((l > UINT_MAX) || (l < -(long)UINT_MAX))) { if (interp != NULL) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "integer value too large to represent as non-long integer", - -1)); + const char *s = + "integer value too large to represent as non-long integer"; + Tcl_SetObjResult(interp, Tcl_NewStringObj(s, -1)); + Tcl_SetErrorCode(interp, "ARITH", "IOVERFLOW", s, NULL); } return TCL_ERROR; } - *intPtr = (int)w; + *intPtr = (int) l; return TCL_OK; +#endif } /* @@ -2026,156 +2492,21 @@ Tcl_GetIntFromObj(interp, objPtr, intPtr) * tclIntType, specifically. * * Results: - * The return value is a standard object Tcl result. If an error occurs - * during conversion, an error message is left in the interpreter's - * result unless "interp" is NULL. - * - *---------------------------------------------------------------------- - */ - -static int -SetIntFromAny(interp, objPtr) - Tcl_Interp* interp; /* Tcl interpreter */ - Tcl_Obj* objPtr; /* Pointer to the object to convert */ -{ - int result; - - result = SetIntOrWideFromAny(interp, objPtr); - if (result != TCL_OK) { - return result; - } - if (objPtr->typePtr != &tclIntType) { - if (interp != NULL) { - CONST char *s = "integer value too large to represent"; - Tcl_SetObjResult(interp, Tcl_NewStringObj(s, -1)); - Tcl_SetErrorCode(interp, "ARITH", "IOVERFLOW", s, (char *) NULL); - } - return TCL_ERROR; - } - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * SetIntOrWideFromAny -- - * - * Attempt to generate an integer internal form for the Tcl object - * "objPtr". - * - * Results: * The return value is a standard object Tcl result. If an error occurs * during conversion, an error message is left in the interpreter's * result unless "interp" is NULL. * - * Side effects: - * If no error occurs, an int is stored as "objPtr"s internal - * representation. - * *---------------------------------------------------------------------- */ static int -SetIntOrWideFromAny(interp, objPtr) - Tcl_Interp *interp; /* Used for error reporting if not NULL. */ - register Tcl_Obj *objPtr; /* The object to convert. */ +SetIntFromAny( + Tcl_Interp *interp, /* Tcl interpreter */ + Tcl_Obj *objPtr) /* Pointer to the object to convert */ { - char *string, *end; - int length; - register char *p; - unsigned long newLong; - int isNegative = 0; - - /* - * Get the string representation. Make it up-to-date if necessary. - */ - - p = string = Tcl_GetStringFromObj(objPtr, &length); - - /* - * Now parse "objPtr"s string as an int. We use an implementation here - * that doesn't report errors in interp if interp is NULL. Note: use - * strtoul instead of strtol for integer conversions to allow full-size - * unsigned numbers. We parse the leading space and sign ourselves so we - * can tell the difference between apparently positive and negative - * values. - */ - - errno = 0; - for (; isspace(UCHAR(*p)) ; p++) { /* INTL: ISO space. */ - /* Empty loop body. */ - } - if (*p == '-') { - isNegative = 1; - p++; - } else if (*p == '+') { - p++; - } - if (!isdigit(UCHAR(*p))) { - badInteger: - if (interp != NULL) { - Tcl_Obj *msg = - Tcl_NewStringObj("expected integer but got \"", -1); - TclAppendLimitedToObj(msg, string, length, 50, ""); - Tcl_AppendToObj(msg, "\"", -1); - Tcl_SetObjResult(interp, msg); - TclCheckBadOctal(interp, string); - } - return TCL_ERROR; - } - newLong = strtoul(p, &end, 0); - if (end == p) { - goto badInteger; - } - - /* - * Make sure that the string has no garbage after the end of the int. - */ - - while ((end < (string+length)) - && isspace(UCHAR(*end))) { /* INTL: ISO space. */ - end++; - } - if (end != (string+length)) { - goto badInteger; - } - - if (errno == ERANGE) { - if (interp != NULL) { - CONST char *s = "integer value too large to represent"; - Tcl_SetObjResult(interp, Tcl_NewStringObj(s, -1)); - Tcl_SetErrorCode(interp, "ARITH", "IOVERFLOW", s, (char *) NULL); - } - return TCL_ERROR; - } - - /* - * The conversion to int succeeded. Free the old internalRep before - * setting the new one. We do this as late as possible to allow the - * conversion code, in particular Tcl_GetStringFromObj, to use that old - * internalRep. - */ - - TclFreeIntRep(objPtr); -#ifndef TCL_WIDE_INT_IS_LONG - /* - * If the resulting integer will exceed the range of a long, put it into a - * wide instead. (Tcl Bug #868489) - */ + long l; - if ((isNegative && newLong > (unsigned long) (LONG_MAX) + 1) - || (!isNegative && newLong > LONG_MAX)) { - objPtr->internalRep.wideValue = - (isNegative ? -(Tcl_WideInt)newLong : (Tcl_WideInt)newLong); - objPtr->typePtr = &tclWideIntType; - } else -#endif - { - objPtr->internalRep.longValue = - (isNegative ? -(long)newLong : (long)newLong); - objPtr->typePtr = &tclIntType; - } - return TCL_OK; + return TclGetLongFromObj(interp, objPtr, &l); } /* @@ -2183,8 +2514,8 @@ SetIntOrWideFromAny(interp, objPtr) * * UpdateStringOfInt -- * - * Update the string representation for an integer object. Note: This - * procedure does not free an existing old string rep so storage will be + * Update the string representation for an 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: @@ -2198,16 +2529,16 @@ SetIntOrWideFromAny(interp, objPtr) */ static void -UpdateStringOfInt(objPtr) - register Tcl_Obj *objPtr; /* Int object whose string rep to update. */ +UpdateStringOfInt( + register Tcl_Obj *objPtr) /* Int object whose string rep to update. */ { char buffer[TCL_INTEGER_SPACE]; register int len; len = TclFormatInt(buffer, objPtr->internalRep.longValue); - objPtr->bytes = ckalloc((unsigned) len + 1); - strcpy(objPtr->bytes, buffer); + objPtr->bytes = ckalloc(len + 1); + memcpy(objPtr->bytes, buffer, (unsigned) len + 1); objPtr->length = len; } @@ -2218,7 +2549,7 @@ UpdateStringOfInt(objPtr) * * If a client is compiled with TCL_MEM_DEBUG defined, calls to * Tcl_NewLongObj to create a new long integer object end up calling the - * debugging procedure Tcl_DbNewLongObj instead. + * debugging function Tcl_DbNewLongObj instead. * * Otherwise, if the client is compiled without TCL_MEM_DEBUG defined, * calls to Tcl_NewLongObj result in a call to one of the two @@ -2245,8 +2576,8 @@ UpdateStringOfInt(objPtr) #undef Tcl_NewLongObj Tcl_Obj * -Tcl_NewLongObj(longValue) - register long longValue; /* Long integer used to initialize the +Tcl_NewLongObj( + register long longValue) /* Long integer used to initialize the * new object. */ { return Tcl_DbNewLongObj(longValue, "unknown", 0); @@ -2255,8 +2586,8 @@ Tcl_NewLongObj(longValue) #else /* if not TCL_MEM_DEBUG */ Tcl_Obj * -Tcl_NewLongObj(longValue) - register long longValue; /* Long integer used to initialize the +Tcl_NewLongObj( + register long longValue) /* Long integer used to initialize the * new object. */ { register Tcl_Obj *objPtr; @@ -2273,7 +2604,7 @@ Tcl_NewLongObj(longValue) * * If a client is compiled with TCL_MEM_DEBUG defined, calls to * Tcl_NewIntObj and Tcl_NewLongObj to create new integer or long integer - * objects end up calling the debugging procedure Tcl_DbNewLongObj + * objects end up calling the debugging function Tcl_DbNewLongObj * instead. We provide two implementations of Tcl_DbNewLongObj so that * whether the Tcl core is compiled to do memory debugging of the core is * independent of whether a client requests debugging for itself. @@ -2285,7 +2616,7 @@ Tcl_NewLongObj(longValue) * reporting objects that haven't been freed. * * Otherwise, when the core is compiled without TCL_MEM_DEBUG defined, - * this procedure just returns the result of calling Tcl_NewLongObj. + * this function just returns the result of calling Tcl_NewLongObj. * * Results: * The newly created long integer object is returned. This object will @@ -2301,12 +2632,12 @@ Tcl_NewLongObj(longValue) #ifdef TCL_MEM_DEBUG Tcl_Obj * -Tcl_DbNewLongObj(longValue, file, line) - register long longValue; /* Long integer used to initialize the new +Tcl_DbNewLongObj( + register long longValue, /* Long integer used to initialize the new * object. */ - CONST char *file; /* The name of the source file calling this - * procedure; used for debugging. */ - int line; /* Line number in the source file; used for + 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. */ { register Tcl_Obj *objPtr; @@ -2322,12 +2653,12 @@ Tcl_DbNewLongObj(longValue, file, line) #else /* if not TCL_MEM_DEBUG */ Tcl_Obj * -Tcl_DbNewLongObj(longValue, file, line) - register long longValue; /* Long integer used to initialize the new +Tcl_DbNewLongObj( + register long longValue, /* Long integer used to initialize the new * object. */ - CONST char *file; /* The name of the source file calling this - * procedure; used for debugging. */ - int line; /* Line number in the source file; used for + 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. */ { return Tcl_NewLongObj(longValue); @@ -2353,13 +2684,13 @@ Tcl_DbNewLongObj(longValue, file, line) */ void -Tcl_SetLongObj(objPtr, longValue) - register Tcl_Obj *objPtr; /* Object whose internal rep to init. */ - register long longValue; /* Long integer used to initialize the +Tcl_SetLongObj( + register Tcl_Obj *objPtr, /* Object whose internal rep to init. */ + register long longValue) /* Long integer used to initialize the * object's value. */ { if (Tcl_IsShared(objPtr)) { - Tcl_Panic("Tcl_SetLongObj called with shared object"); + Tcl_Panic("%s called with shared object", "Tcl_SetLongObj"); } TclSetLongObj(objPtr, longValue); @@ -2387,156 +2718,100 @@ Tcl_SetLongObj(objPtr, longValue) */ int -Tcl_GetLongFromObj(interp, objPtr, longPtr) - Tcl_Interp *interp; /* Used for error reporting if not NULL. */ - register Tcl_Obj *objPtr; /* The object from which to get a long. */ - register long *longPtr; /* Place to store resulting long. */ +Tcl_GetLongFromObj( + Tcl_Interp *interp, /* Used for error reporting if not NULL. */ + register Tcl_Obj *objPtr, /* The object from which to get a long. */ + register long *longPtr) /* Place to store resulting long. */ { - register int result; - - if (objPtr->typePtr != &tclIntType - && objPtr->typePtr != &tclWideIntType) { - result = SetIntOrWideFromAny(interp, objPtr); - if (result != TCL_OK) { - return result; + do { + if (objPtr->typePtr == &tclIntType) { + *longPtr = objPtr->internalRep.longValue; + return TCL_OK; } - } - #ifndef TCL_WIDE_INT_IS_LONG - if (objPtr->typePtr == &tclWideIntType) { - /* - * If the object is already a wide integer, don't convert it. This - * code allows for any integer in the range -ULONG_MAX to ULONG_MAX to - * be 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 the - * internal rep. - */ + if (objPtr->typePtr == &tclWideIntType) { + /* + * We return any integer in the range -ULONG_MAX 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 + * the internal rep. + */ - Tcl_WideInt w = objPtr->internalRep.wideValue; - if (w >= -(Tcl_WideInt)(ULONG_MAX) - && w <= (Tcl_WideInt)(ULONG_MAX)) { - *longPtr = Tcl_WideAsLong(w); - return TCL_OK; - } else { + Tcl_WideInt w = objPtr->internalRep.wideValue; + + if (w >= -(Tcl_WideInt)(ULONG_MAX) + && w <= (Tcl_WideInt)(ULONG_MAX)) { + *longPtr = Tcl_WideAsLong(w); + return TCL_OK; + } + goto tooLarge; + } +#endif + if (objPtr->typePtr == &tclDoubleType) { if (interp != NULL) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "integer value too large to represent", -1)); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "expected integer but got \"%s\"", + Tcl_GetString(objPtr))); + Tcl_SetErrorCode(interp, "TCL", "VALUE", "INTEGER", NULL); } return TCL_ERROR; } - } -#endif - - *longPtr = objPtr->internalRep.longValue; - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * SetWideIntFromAny -- - * - * Attempt to generate an integer internal form for the Tcl object - * "objPtr". - * - * Results: - * The return value is a standard object Tcl result. If an error occurs - * during conversion, an error message is left in the interpreter's - * result unless "interp" is NULL. - * - * Side effects: - * If no error occurs, an int is stored as "objPtr"s internal - * representation. - * - *---------------------------------------------------------------------- - */ + if (objPtr->typePtr == &tclBignumType) { + /* + * Must check for those bignum values that can fit in a long, even + * when auto-narrowing is enabled. Only those values in the signed + * long range get auto-narrowed to tclIntType, while all the + * values in the unsigned long range will fit in a long. + */ -static int -SetWideIntFromAny(interp, objPtr) - Tcl_Interp *interp; /* Used for error reporting if not NULL. */ - register Tcl_Obj *objPtr; /* The object to convert. */ -{ + mp_int big; + + UNPACK_BIGNUM(objPtr, big); + if ((size_t) big.used <= (CHAR_BIT * sizeof(long) + DIGIT_BIT - 1) + / DIGIT_BIT) { + unsigned long value = 0, numBytes = sizeof(long); + long scratch; + unsigned char *bytes = (unsigned char *) &scratch; + + if (mp_to_unsigned_bin_n(&big, bytes, &numBytes) == MP_OKAY) { + while (numBytes-- > 0) { + value = (value << CHAR_BIT) | *bytes++; + } + if (big.sign) { + *longPtr = - (long) value; + } else { + *longPtr = (long) value; + } + return TCL_OK; + } + } #ifndef TCL_WIDE_INT_IS_LONG - char *string, *end; - int length; - register char *p; - Tcl_WideInt newWide; - - /* - * Get the string representation. Make it up-to-date if necessary. - */ - - p = string = Tcl_GetStringFromObj(objPtr, &length); - - /* - * Now parse "objPtr"s string as an int. We use an implementation here - * that doesn't report errors in interp if interp is NULL. Note: use - * strtoull instead of strtoll for integer conversions to allow full-size - * unsigned numbers. - */ - - errno = 0; - newWide = strtoull(p, &end, 0); - if (end == p) { - badInteger: - if (interp != NULL) { - Tcl_Obj *msg = - Tcl_NewStringObj("expected integer but got \"", -1); - TclAppendLimitedToObj(msg, string, length, 50, ""); - Tcl_AppendToObj(msg, "\"", -1); - Tcl_SetObjResult(interp, msg); - TclCheckBadOctal(interp, string); - } - return TCL_ERROR; - } - - /* - * Make sure that the string has no garbage after the end of the int. - */ - - while ((end < (string+length)) - && isspace(UCHAR(*end))) { /* INTL: ISO space. */ - end++; - } - if (end != (string+length)) { - goto badInteger; - } + tooLarge: +#endif + if (interp != NULL) { + const char *s = "integer value too large to represent"; + Tcl_Obj *msg = Tcl_NewStringObj(s, -1); - if (errno == ERANGE) { - if (interp != NULL) { - CONST char *s = "integer value too large to represent"; - Tcl_SetObjResult(interp, Tcl_NewStringObj(s, -1)); - Tcl_SetErrorCode(interp, "ARITH", "IOVERFLOW", s, (char *) NULL); + Tcl_SetObjResult(interp, msg); + Tcl_SetErrorCode(interp, "ARITH", "IOVERFLOW", s, NULL); + } + return TCL_ERROR; } - return TCL_ERROR; - } - /* - * The conversion to int succeeded. Free the old internalRep before - * setting the new one. We do this as late as possible to allow the - * conversion code, in particular Tcl_GetStringFromObj, to use that old - * internalRep. - */ - - TclFreeIntRep(objPtr); - objPtr->internalRep.wideValue = newWide; -#else - if (TCL_ERROR == SetIntFromAny(interp, objPtr)) { - return TCL_ERROR; - } -#endif - objPtr->typePtr = &tclWideIntType; - return TCL_OK; + } while (TclParseNumber(interp, objPtr, "integer", NULL, -1, NULL, + 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 procedure does not free an existing old string rep so storage - * will be lost if this has not already been done. + * 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. @@ -2548,10 +2823,9 @@ SetWideIntFromAny(interp, objPtr) *---------------------------------------------------------------------- */ -#ifndef TCL_WIDE_INT_IS_LONG static void -UpdateStringOfWideInt(objPtr) - register Tcl_Obj *objPtr; /* Int object whose string rep to update. */ +UpdateStringOfWideInt( + register Tcl_Obj *objPtr) /* Int object whose string rep to update. */ { char buffer[TCL_INTEGER_SPACE+2]; register unsigned len; @@ -2566,11 +2840,11 @@ UpdateStringOfWideInt(objPtr) sprintf(buffer, "%" TCL_LL_MODIFIER "d", wideVal); len = strlen(buffer); - objPtr->bytes = ckalloc((unsigned) len + 1); + objPtr->bytes = ckalloc(len + 1); memcpy(objPtr->bytes, buffer, len + 1); objPtr->length = len; } -#endif /* TCL_WIDE_INT_IS_LONG */ +#endif /* !TCL_WIDE_INT_IS_LONG */ /* *---------------------------------------------------------------------- @@ -2579,7 +2853,7 @@ UpdateStringOfWideInt(objPtr) * * If a client is compiled with TCL_MEM_DEBUG defined, calls to * Tcl_NewWideIntObj to create a new 64-bit integer object end up calling - * the debugging procedure Tcl_DbNewWideIntObj instead. + * the debugging function Tcl_DbNewWideIntObj instead. * * Otherwise, if the client is compiled without TCL_MEM_DEBUG defined, * calls to Tcl_NewWideIntObj result in a call to one of the two @@ -2601,9 +2875,10 @@ UpdateStringOfWideInt(objPtr) #undef Tcl_NewWideIntObj Tcl_Obj * -Tcl_NewWideIntObj(wideValue) - register Tcl_WideInt wideValue; /* Wide integer used to initialize - * the new object. */ +Tcl_NewWideIntObj( + register Tcl_WideInt wideValue) + /* Wide integer used to initialize the new + * object. */ { return Tcl_DbNewWideIntObj(wideValue, "unknown", 0); } @@ -2611,13 +2886,15 @@ Tcl_NewWideIntObj(wideValue) #else /* if not TCL_MEM_DEBUG */ Tcl_Obj * -Tcl_NewWideIntObj(wideValue) - register Tcl_WideInt wideValue; /* Wide integer used to initialize - * the new object. */ +Tcl_NewWideIntObj( + register Tcl_WideInt wideValue) + /* Wide integer used to initialize the new + * object. */ { register Tcl_Obj *objPtr; - TclNewWideIntObj(objPtr, wideValue); + TclNewObj(objPtr); + Tcl_SetWideIntObj(objPtr, wideValue); return objPtr; } #endif /* if TCL_MEM_DEBUG */ @@ -2629,7 +2906,7 @@ Tcl_NewWideIntObj(wideValue) * * If a client is compiled with TCL_MEM_DEBUG defined, calls to * Tcl_NewWideIntObj to create new wide integer end up calling the - * debugging procedure Tcl_DbNewWideIntObj instead. We provide two + * debugging function Tcl_DbNewWideIntObj instead. We provide two * implementations of Tcl_DbNewWideIntObj so that whether the Tcl core is * compiled to do memory debugging of the core is independent of whether * a client requests debugging for itself. @@ -2641,7 +2918,7 @@ Tcl_NewWideIntObj(wideValue) * number when reporting objects that haven't been freed. * * Otherwise, when the core is compiled without TCL_MEM_DEBUG defined, - * this procedure just returns the result of calling Tcl_NewWideIntObj. + * this function just returns the result of calling Tcl_NewWideIntObj. * * Results: * The newly created wide integer object is returned. This object will @@ -2657,36 +2934,33 @@ Tcl_NewWideIntObj(wideValue) #ifdef TCL_MEM_DEBUG Tcl_Obj * -Tcl_DbNewWideIntObj(wideValue, file, line) - register Tcl_WideInt wideValue; /* Wide integer used to initialize the - * new object. */ - CONST char *file; /* The name of the source file calling - * this procedure; used for - * debugging. */ - int line; /* Line number in the source file; - * used for debugging. */ +Tcl_DbNewWideIntObj( + register Tcl_WideInt wideValue, + /* Wide 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. */ { register Tcl_Obj *objPtr; TclDbNewObj(objPtr, file, line); - objPtr->bytes = NULL; - - objPtr->internalRep.wideValue = wideValue; - objPtr->typePtr = &tclWideIntType; + Tcl_SetWideIntObj(objPtr, wideValue); return objPtr; } #else /* if not TCL_MEM_DEBUG */ Tcl_Obj * -Tcl_DbNewWideIntObj(wideValue, file, line) - register Tcl_WideInt wideValue; /* Long integer used to initialize the - * new object. */ - CONST char *file; /* The name of the source file calling - * this procedure; used for - * debugging. */ - int line; /* Line number in the source file; - * used for debugging. */ +Tcl_DbNewWideIntObj( + register 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. */ { return Tcl_NewWideIntObj(wideValue); } @@ -2711,16 +2985,29 @@ Tcl_DbNewWideIntObj(wideValue, file, line) */ void -Tcl_SetWideIntObj(objPtr, wideValue) - register Tcl_Obj *objPtr; /* Object w. internal rep to init. */ - register Tcl_WideInt wideValue; /* Wide integer used to initialize - * the object's value. */ +Tcl_SetWideIntObj( + register Tcl_Obj *objPtr, /* Object w. internal rep to init. */ + register Tcl_WideInt wideValue) + /* Wide integer used to initialize the + * object's value. */ { if (Tcl_IsShared(objPtr)) { - Tcl_Panic("Tcl_SetWideIntObj called with shared object"); + Tcl_Panic("%s called with shared object", "Tcl_SetWideIntObj"); } - TclSetWideIntObj(objPtr, wideValue); + 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 + } } /* @@ -2745,198 +3032,158 @@ Tcl_SetWideIntObj(objPtr, wideValue) */ int -Tcl_GetWideIntFromObj(interp, objPtr, wideIntPtr) - Tcl_Interp *interp; /* Used for error reporting if not NULL. */ - register Tcl_Obj *objPtr; /* Object from which to get a wide int. */ - register Tcl_WideInt *wideIntPtr; /* Place to store resulting long. */ +Tcl_GetWideIntFromObj( + Tcl_Interp *interp, /* Used for error reporting if not NULL. */ + register Tcl_Obj *objPtr, /* Object from which to get a wide int. */ + register Tcl_WideInt *wideIntPtr) + /* Place to store resulting long. */ { - register int result; + 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; + return TCL_OK; + } + if (objPtr->typePtr == &tclDoubleType) { + if (interp != NULL) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "expected integer but got \"%s\"", + Tcl_GetString(objPtr))); + Tcl_SetErrorCode(interp, "TCL", "VALUE", "INTEGER", NULL); + } + return TCL_ERROR; + } + if (objPtr->typePtr == &tclBignumType) { + /* + * Must check for those bignum values that can fit in a + * Tcl_WideInt, even when auto-narrowing is enabled. + */ - if (objPtr->typePtr == &tclWideIntType) { - *wideIntPtr = objPtr->internalRep.wideValue; - return TCL_OK; - } - result = SetWideIntFromAny(interp, objPtr); - if (result == TCL_OK) { - *wideIntPtr = objPtr->internalRep.wideValue; - } - return result; + mp_int big; + + UNPACK_BIGNUM(objPtr, big); + if ((size_t) big.used <= (CHAR_BIT * sizeof(Tcl_WideInt) + + DIGIT_BIT - 1) / DIGIT_BIT) { + Tcl_WideUInt value = 0; + unsigned long numBytes = sizeof(Tcl_WideInt); + Tcl_WideInt scratch; + unsigned char *bytes = (unsigned char *) &scratch; + + if (mp_to_unsigned_bin_n(&big, bytes, &numBytes) == MP_OKAY) { + while (numBytes-- > 0) { + value = (value << CHAR_BIT) | *bytes++; + } + if (big.sign) { + *wideIntPtr = - (Tcl_WideInt) value; + } else { + *wideIntPtr = (Tcl_WideInt) 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; } +#ifndef TCL_WIDE_INT_IS_LONG /* *---------------------------------------------------------------------- * - * FreeBignum -- + * SetWideIntFromAny -- * - * This procedure frees the internal rep of a bignum. + * Attempts to force the internal representation for a Tcl object to + * tclWideIntType, specifically. * * Results: - * None. + * The return value is a standard object Tcl result. If an error occurs + * during conversion, an error message is left in the interpreter's + * result unless "interp" is NULL. * *---------------------------------------------------------------------- */ -static void -FreeBignum(Tcl_Obj *objPtr) +static int +SetWideIntFromAny( + Tcl_Interp *interp, /* Tcl interpreter */ + Tcl_Obj *objPtr) /* Pointer to the object to convert */ { - mp_int toFree; /* Bignum to free */ - - UNPACK_BIGNUM(objPtr, toFree); - mp_clear(&toFree); + Tcl_WideInt w; + return Tcl_GetWideIntFromObj(interp, objPtr, &w); } +#endif /* !TCL_WIDE_INT_IS_LONG */ /* *---------------------------------------------------------------------- * - * DupBignum -- + * FreeBignum -- * - * This procedure duplicates the internal rep of a bignum. + * This function frees the internal rep of a bignum. * * Results: * None. * - * Side effects: - * The destination object receies a copy of the source object - * *---------------------------------------------------------------------- */ static void -DupBignum(srcPtr, copyPtr) - Tcl_Obj* srcPtr; - Tcl_Obj* copyPtr; +FreeBignum( + Tcl_Obj *objPtr) { - mp_int bignumVal; - mp_int bignumCopy; + mp_int toFree; /* Bignum to free */ - copyPtr->typePtr = &tclBignumType; - UNPACK_BIGNUM(srcPtr, bignumVal); - if (mp_init_copy(&bignumCopy, &bignumVal) != MP_OKAY) { - Tcl_Panic("initialization failure in DupBignum"); + UNPACK_BIGNUM(objPtr, toFree); + mp_clear(&toFree); + if ((long) objPtr->internalRep.ptrAndLongRep.value < 0) { + ckfree(objPtr->internalRep.ptrAndLongRep.ptr); } - PACK_BIGNUM(bignumVal, copyPtr); + objPtr->typePtr = NULL; } /* *---------------------------------------------------------------------- * - * SetBignumFromAny -- + * DupBignum -- * - * This procedure interprets a Tcl_Obj as a bignum and sets the internal - * representation accordingly. + * This function duplicates the internal rep of a bignum. * * Results: - * Returns a standard Tcl status. If conversion fails, an error message - * is left in the interpreter result. + * None. * * Side effects: - * The bignum internal representation is packed into the object. + * The destination object receies a copy of the source object * *---------------------------------------------------------------------- */ -static int -SetBignumFromAny(interp, objPtr) - Tcl_Interp* interp; - Tcl_Obj* objPtr; +static void +DupBignum( + Tcl_Obj *srcPtr, + Tcl_Obj *copyPtr) { - CONST char* stringVal; - CONST char* p; - int length; - int signum = MP_ZPOS; - int radix = 10; - int status; mp_int bignumVal; + mp_int bignumCopy; - if (objPtr->typePtr == &tclIntType) { - - /* - * If the number already contains an integer, simply widen it to a - * bignum. - */ - - TclBNInitBignumFromLong(&bignumVal, objPtr->internalRep.longValue); - } else { - - /* - * The number doesn't contain an integer. Convert its string rep to a - * bignum, handling 0XXX and 0xXXX notation - */ - - stringVal = Tcl_GetStringFromObj(objPtr, &length); - p = stringVal; - - /* - * Pull off the signum - */ - - if (*p == '+') { - ++p; - } else if (*p == '-') { - ++p; - signum = MP_NEG; - } - - /* - * Handle octal and hexadecimal - */ - - if (*p == '0') { - ++p; - if (*p == 'x' || *p == 'X') { - ++p; - radix = 16; - } else { - --p; - radix = 8; - } - } - - /* Convert the value */ - - if (mp_init(&bignumVal) != MP_OKAY) { - Tcl_Panic("initialization failure in SetBignumFromAny"); - } - status = mp_read_radix(&bignumVal, p, radix); - switch (status) { - case MP_MEM: - Tcl_Panic("out of memory in SetBignumFromAny"); - case MP_OKAY: - break; - default: - if (interp != NULL) { - Tcl_Obj* msg = Tcl_NewStringObj( - "expected integer but got \"", -1); - TclAppendLimitedToObj(msg, stringVal, length, 50, ""); - Tcl_AppendToObj(msg, "\"", -1); - Tcl_SetObjResult(interp, msg); - TclCheckBadOctal(interp, stringVal); - } - mp_clear(&bignumVal); - return TCL_ERROR; - } - - /* Conversion to bignum succeeded. Make sure that everything fits. */ - - if (bignumVal.alloc > 0x7fff) { - Tcl_Obj* msg = - Tcl_NewStringObj("integer value too large to represent",-1); - Tcl_SetObjResult(interp, msg); - mp_clear(&bignumVal); - return TCL_ERROR; - } + copyPtr->typePtr = &tclBignumType; + UNPACK_BIGNUM(srcPtr, bignumVal); + if (mp_init_copy(&bignumCopy, &bignumVal) != MP_OKAY) { + Tcl_Panic("initialization failure in DupBignum"); } - - /* - * Conversion succeeded. Clean up the old internal rep and store the new - * one. - */ - - TclFreeIntRep(objPtr); - bignumVal.sign = signum; - PACK_BIGNUM(bignumVal, objPtr); - objPtr->typePtr = &tclBignumType; - return TCL_OK; + PACK_BIGNUM(bignumCopy, copyPtr); } /* @@ -2944,7 +3191,7 @@ SetBignumFromAny(interp, objPtr) * * UpdateStringOfBignum -- * - * This procedure updates the string representation of a bignum object. + * This function updates the string representation of a bignum object. * * Results: * None. @@ -2954,29 +3201,46 @@ SetBignumFromAny(interp, objPtr) * to-string conversion. * * The object's existing string representation is NOT freed; memory will leak - * if the string rep is still valid at the time this procedure is called. + * if the string rep is still valid at the time this function is called. + * + *---------------------------------------------------------------------- */ static void -UpdateStringOfBignum(Tcl_Obj* objPtr) +UpdateStringOfBignum( + Tcl_Obj *objPtr) { mp_int bignumVal; int size; int status; - char* stringVal; + char *stringVal; UNPACK_BIGNUM(objPtr, bignumVal); status = mp_radix_size(&bignumVal, 10, &size); if (status != MP_OKAY) { Tcl_Panic("radix size failure in UpdateStringOfBignum"); } - stringVal = Tcl_Alloc((size_t) size); + if (size == 3) { + /* + * mp_radix_size() returns 3 when more than INT_MAX bytes would be + * needed to hold the string rep (because mp_radix_size ignores + * integer overflow issues). When we know the string rep will be more + * than 3, we can conclude the string rep would overflow our string + * length limits. + * + * Note that so long as we enforce our bignums to the size that fits + * in a packed bignum, this branch will never be taken. + */ + + Tcl_Panic("UpdateStringOfBignum: string length limit exceeded"); + } + stringVal = ckalloc(size); status = mp_toradix_n(&bignumVal, stringVal, 10, size); if (status != MP_OKAY) { Tcl_Panic("conversion failure in UpdateStringOfBignum"); } objPtr->bytes = stringVal; - objPtr->length = size - 1; /* size includes a trailing null byte */ + objPtr->length = size - 1; /* size includes a trailing NUL byte. */ } /* @@ -2997,26 +3261,22 @@ UpdateStringOfBignum(Tcl_Obj* objPtr) #ifdef TCL_MEM_DEBUG #undef Tcl_NewBignumObj -Tcl_Obj* -Tcl_NewBignumObj(mp_int* bignumValue) + +Tcl_Obj * +Tcl_NewBignumObj( + mp_int *bignumValue) { return Tcl_DbNewBignumObj(bignumValue, "unknown", 0); } #else Tcl_Obj * -Tcl_NewBignumObj(mp_int* bignumValue) +Tcl_NewBignumObj( + mp_int *bignumValue) { - Tcl_Obj* objPtr; + Tcl_Obj *objPtr; TclNewObj(objPtr); - PACK_BIGNUM(*bignumValue, objPtr); - objPtr->typePtr=&tclBignumType; - objPtr->bytes = NULL; - - /* Clear with mp_init; mp_clear would overwrite the digit array. */ - - mp_init(bignumValue); - + Tcl_SetBignumObj(objPtr, bignumValue); return objPtr; } #endif @@ -3026,9 +3286,9 @@ Tcl_NewBignumObj(mp_int* bignumValue) * * Tcl_DbNewBignumObj -- * - * This procedure is normally called when debugging: that is, when - * TCL_MEM_DEBUG is defined. It constructs a bignum object, recording - * the creation point so that [memory active] can report it. + * This function is normally called when debugging: that is, when + * TCL_MEM_DEBUG is defined. It constructs a bignum object, recording the + * creation point so that [memory active] can report it. * * Results: * Returns the newly created object. @@ -3040,26 +3300,24 @@ Tcl_NewBignumObj(mp_int* bignumValue) */ #ifdef TCL_MEM_DEBUG -Tcl_Obj* -Tcl_DbNewBignumObj(mp_int* bignumValue, CONST char* file, int line) +Tcl_Obj * +Tcl_DbNewBignumObj( + mp_int *bignumValue, + const char *file, + int line) { - Tcl_Obj* objPtr; + Tcl_Obj *objPtr; TclDbNewObj(objPtr, file, line); - objPtr->bytes = NULL; - PACK_BIGNUM(*bignumValue, objPtr); - objPtr->typePtr = &tclBignumType; - objPtr->bytes = NULL; - - /* Clear with mp_init; mp_clear would overwrite the digit array. */ - - mp_init(bignumValue); - + Tcl_SetBignumObj(objPtr, bignumValue); return objPtr; } #else -Tcl_Obj* -Tcl_DbNewBignumObj(mp_int* bignumValue, CONST char* file, int line) +Tcl_Obj * +Tcl_DbNewBignumObj( + mp_int *bignumValue, + const char *file, + int line) { return Tcl_NewBignumObj(bignumValue); } @@ -3068,44 +3326,141 @@ Tcl_DbNewBignumObj(mp_int* bignumValue, CONST char* file, int line) /* *---------------------------------------------------------------------- * + * GetBignumFromObj -- + * + * This function retrieves a 'bignum' value from a Tcl object, converting + * the object if necessary. Either copies or transfers the mp_int value + * depending on the copy flag value passed in. + * + * Results: + * Returns TCL_OK if the conversion is successful, TCL_ERROR otherwise. + * + * Side effects: + * A copy of bignum is stored in *bignumValue, which is expected to be + * uninitialized or cleared. If conversion fails, and the 'interp' + * argument is not NULL, an error message is stored in the interpreter + * result. + * + *---------------------------------------------------------------------- + */ + +static int +GetBignumFromObj( + Tcl_Interp *interp, /* Tcl interpreter for error reporting */ + Tcl_Obj *objPtr, /* Object to read */ + int copy, /* Whether to copy the returned bignum value */ + mp_int *bignumValue) /* Returned bignum value. */ +{ + do { + if (objPtr->typePtr == &tclBignumType) { + if (copy || Tcl_IsShared(objPtr)) { + mp_int temp; + + UNPACK_BIGNUM(objPtr, temp); + mp_init_copy(bignumValue, &temp); + } else { + UNPACK_BIGNUM(objPtr, *bignumValue); + objPtr->internalRep.ptrAndLongRep.ptr = NULL; + objPtr->internalRep.ptrAndLongRep.value = 0; + objPtr->typePtr = NULL; + if (objPtr->bytes == NULL) { + TclInitStringRep(objPtr, tclEmptyStringRep, 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); + return TCL_OK; + } +#endif + if (objPtr->typePtr == &tclDoubleType) { + if (interp != NULL) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "expected integer but got \"%s\"", + Tcl_GetString(objPtr))); + Tcl_SetErrorCode(interp, "TCL", "VALUE", "INTEGER", NULL); + } + return TCL_ERROR; + } + } while (TclParseNumber(interp, objPtr, "integer", NULL, -1, NULL, + TCL_PARSE_INTEGER_ONLY)==TCL_OK); + return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * * Tcl_GetBignumFromObj -- * - * This procedure retrieves a 'bignum' value from a Tcl object, - * converting the object if necessary. + * This function retrieves a 'bignum' value from a Tcl object, converting + * the object if necessary. * * Results: * Returns TCL_OK if the conversion is successful, TCL_ERROR otherwise. * * Side effects: * A copy of bignum is stored in *bignumValue, which is expected to be - * uninitialized or cleared. If conversion fails, an the 'interp' + * uninitialized or cleared. If conversion fails, an the 'interp' * argument is not NULL, an error message is stored in the interpreter * result. * * It is expected that the caller will NOT have invoked mp_init on the - * bignum value before passing it in. The raw value of the object is - * returned, and Tcl owns that memory, so the caller should NOT invoke - * mp_clear afterwards. + * bignum value before passing it in. Tcl will initialize the mp_int as + * it sets the value. The value is a copy of the value in objPtr, so it + * becomes the responsibility of the caller to call mp_clear on it. * *---------------------------------------------------------------------- */ int Tcl_GetBignumFromObj( - Tcl_Interp* interp, /* Tcl interpreter for error reporting */ - Tcl_Obj* objPtr, /* Object to read */ - mp_int* bignumValue) /* Returned bignum value. */ + Tcl_Interp *interp, /* Tcl interpreter for error reporting */ + Tcl_Obj *objPtr, /* Object to read */ + mp_int *bignumValue) /* Returned bignum value. */ { - mp_int temp; + return GetBignumFromObj(interp, objPtr, 1, bignumValue); +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_TakeBignumFromObj -- + * + * This function retrieves a 'bignum' value from a Tcl object, converting + * the object if necessary. + * + * Results: + * Returns TCL_OK if the conversion is successful, TCL_ERROR otherwise. + * + * Side effects: + * A copy of bignum is stored in *bignumValue, which is expected to be + * uninitialized or cleared. If conversion fails, an the 'interp' + * argument is not NULL, an error message is stored in the interpreter + * result. + * + * It is expected that the caller will NOT have invoked mp_init on the + * bignum value before passing it in. Tcl will initialize the mp_int as + * it sets the value. The value is transferred from the internals of + * objPtr to the caller, passing responsibility of the caller to call + * mp_clear on it. The objPtr is cleared to hold an empty value. + * + *---------------------------------------------------------------------- + */ - if (objPtr->typePtr != &tclBignumType) { - if (SetBignumFromAny(interp, objPtr) != TCL_OK) { - return TCL_ERROR; - } - } - UNPACK_BIGNUM(objPtr, temp); - mp_init_copy(bignumValue, &temp); - return TCL_OK; +int +Tcl_TakeBignumFromObj( + Tcl_Interp *interp, /* Tcl interpreter for error reporting */ + Tcl_Obj *objPtr, /* Object to read */ + mp_int *bignumValue) /* Returned bignum value. */ +{ + return GetBignumFromObj(interp, objPtr, 0, bignumValue); } /* @@ -3113,13 +3468,13 @@ Tcl_GetBignumFromObj( * * Tcl_SetBignumObj -- * - * This procedure sets the value of a Tcl_Obj to a large integer. + * This function sets the value of a Tcl_Obj to a large integer. * * Results: * None. * * Side effects: - * Object value is stored. The bignum value is cleared, since ownership + * Object value is stored. The bignum value is cleared, since ownership * has transferred to Tcl. * *---------------------------------------------------------------------- @@ -3127,20 +3482,168 @@ Tcl_GetBignumFromObj( void Tcl_SetBignumObj( - Tcl_Obj* objPtr, /* Object to set */ - mp_int* bignumValue) /* Value to store */ + Tcl_Obj *objPtr, /* Object to set */ + mp_int *bignumValue) /* Value to store */ { if (Tcl_IsShared(objPtr)) { - Tcl_Panic("Tcl_SetBignumObj called with shared object"); + Tcl_Panic("%s called with shared object", "Tcl_SetBignumObj"); } + if ((size_t) bignumValue->used + <= (CHAR_BIT * sizeof(long) + DIGIT_BIT - 1) / DIGIT_BIT) { + unsigned long value = 0, numBytes = sizeof(long); + long scratch; + unsigned char *bytes = (unsigned char *) &scratch; + + if (mp_to_unsigned_bin_n(bignumValue, bytes, &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; + } + tooLargeForLong: +#ifndef TCL_WIDE_INT_IS_LONG + if ((size_t) bignumValue->used + <= (CHAR_BIT * sizeof(Tcl_WideInt) + DIGIT_BIT - 1) / DIGIT_BIT) { + Tcl_WideUInt value = 0; + unsigned long numBytes = sizeof(Tcl_WideInt); + Tcl_WideInt scratch; + unsigned char *bytes = (unsigned char *)&scratch; + + if (mp_to_unsigned_bin_n(bignumValue, bytes, &numBytes) != MP_OKAY) { + goto tooLargeForWide; + } + while (numBytes-- > 0) { + value = (value << CHAR_BIT) | *bytes++; + } + if (value > (((~(Tcl_WideUInt)0) >> 1) + bignumValue->sign)) { + goto tooLargeForWide; + } + if (bignumValue->sign) { + TclSetWideIntObj(objPtr, -(Tcl_WideInt)value); + } else { + TclSetWideIntObj(objPtr, (Tcl_WideInt)value); + } + mp_clear(bignumValue); + return; + } + tooLargeForWide: +#endif + TclInvalidateStringRep(objPtr); TclFreeIntRep(objPtr); + TclSetBignumIntRep(objPtr, bignumValue); +} + +/* + *---------------------------------------------------------------------- + * + * TclSetBignumIntRep -- + * + * Install a bignum into the internal representation of an object. + * + * Results: + * None. + * + * Side effects: + * Object internal representation is updated and object type is set. The + * bignum value is cleared, since ownership has transferred to the + * object. + * + *---------------------------------------------------------------------- + */ + +void +TclSetBignumIntRep( + Tcl_Obj *objPtr, + mp_int *bignumValue) +{ objPtr->typePtr = &tclBignumType; PACK_BIGNUM(*bignumValue, objPtr); - Tcl_InvalidateStringRep(objPtr); - /* Clear the value with mp_init; mp_clear overwrites the digit array. */ + /* + * Clear the mp_int value. + * + * Don't call mp_clear() because it would free the digit array we just + * packed into the Tcl_Obj. + */ + + bignumValue->dp = NULL; + bignumValue->alloc = bignumValue->used = 0; + bignumValue->sign = MP_NEG; +} + +/* + *---------------------------------------------------------------------- + * + * TclGetNumberFromObj -- + * + * Extracts a number (of any possible numeric type) from an object. + * + * Results: + * Whether the extraction worked. The type is stored in the variable + * referred to by the typePtr argument, and a pointer to the + * representation is stored in the variable referred to by the + * clientDataPtr. + * + * Side effects: + * Can allocate thread-specific data for handling the copy-out space for + * bignums; this space is shared within a thread. + * + *---------------------------------------------------------------------- + */ - mp_init(bignumValue); +int +TclGetNumberFromObj( + Tcl_Interp *interp, + Tcl_Obj *objPtr, + ClientData *clientDataPtr, + int *typePtr) +{ + do { + if (objPtr->typePtr == &tclDoubleType) { + if (TclIsNaN(objPtr->internalRep.doubleValue)) { + *typePtr = TCL_NUMBER_NAN; + } else { + *typePtr = TCL_NUMBER_DOUBLE; + } + *clientDataPtr = &objPtr->internalRep.doubleValue; + 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; + *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)); + + UNPACK_BIGNUM(objPtr, *bigPtr); + *typePtr = TCL_NUMBER_BIG; + *clientDataPtr = bigPtr; + return TCL_OK; + } + } while (TCL_OK == + TclParseNumber(interp, objPtr, "number", NULL, -1, NULL, 0)); + return TCL_ERROR; } /* @@ -3148,11 +3651,11 @@ Tcl_SetBignumObj( * * Tcl_DbIncrRefCount -- * - * This procedure is normally called when debugging: i.e., when + * This function is normally called when debugging: i.e., when * TCL_MEM_DEBUG is defined. This checks to see whether or not the memory * has been freed before incrementing the ref count. * - * When TCL_MEM_DEBUG is not defined, this procedure just increments the + * When TCL_MEM_DEBUG is not defined, this function just increments the * reference count of the object. * * Results: @@ -3165,19 +3668,19 @@ Tcl_SetBignumObj( */ void -Tcl_DbIncrRefCount(objPtr, file, line) - register Tcl_Obj *objPtr; /* The object we are registering a reference +Tcl_DbIncrRefCount( + register Tcl_Obj *objPtr, /* The object we are registering a reference * to. */ - CONST char *file; /* The name of the source file calling this - * procedure; used for debugging. */ - int line; /* Line number in the source file; used for + 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. */ { #ifdef TCL_MEM_DEBUG if (objPtr->refCount == 0x61616161) { fprintf(stderr, "file = %s, line = %d\n", file, line); fflush(stderr); - Tcl_Panic("Trying to increment refCount of previously disposed object."); + Tcl_Panic("incrementing refCount of previously disposed object"); } # ifdef TCL_THREADS @@ -3188,23 +3691,21 @@ Tcl_DbIncrRefCount(objPtr, file, line) */ if (!TclInExit()) { - Tcl_HashTable *tablePtr; - Tcl_HashEntry *hPtr; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + Tcl_HashTable *tablePtr = tsdPtr->objThreadMap; + Tcl_HashEntry *hPtr; - tablePtr = tsdPtr->objThreadMap; if (!tablePtr) { Tcl_Panic("object table not initialized"); } - hPtr = Tcl_FindHashEntry(tablePtr, (char *) objPtr); + hPtr = Tcl_FindHashEntry(tablePtr, objPtr); if (!hPtr) { - Tcl_Panic("%s%s", - "Trying to incr ref count of ", - "Tcl_Obj allocated in another thread"); + Tcl_Panic("Trying to %s of Tcl_Obj allocated in another thread", + "incr ref count"); } } -# endif -#endif +# endif /* TCL_THREADS */ +#endif /* TCL_MEM_DEBUG */ ++(objPtr)->refCount; } @@ -3213,11 +3714,11 @@ Tcl_DbIncrRefCount(objPtr, file, line) * * Tcl_DbDecrRefCount -- * - * This procedure is normally called when debugging: i.e., when + * This function is normally called when debugging: i.e., when * TCL_MEM_DEBUG is defined. This checks to see whether or not the memory * has been freed before decrementing the ref count. * - * When TCL_MEM_DEBUG is not defined, this procedure just decrements the + * When TCL_MEM_DEBUG is not defined, this function just decrements the * reference count of the object. * * Results: @@ -3230,19 +3731,19 @@ Tcl_DbIncrRefCount(objPtr, file, line) */ void -Tcl_DbDecrRefCount(objPtr, file, line) - register Tcl_Obj *objPtr; /* The object we are releasing a reference +Tcl_DbDecrRefCount( + register Tcl_Obj *objPtr, /* The object we are releasing a reference * to. */ - CONST char *file; /* The name of the source file calling this - * procedure; used for debugging. */ - int line; /* Line number in the source file; used for + 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. */ { #ifdef TCL_MEM_DEBUG if (objPtr->refCount == 0x61616161) { fprintf(stderr, "file = %s, line = %d\n", file, line); fflush(stderr); - Tcl_Panic("Trying to decrement refCount of previously disposed object."); + Tcl_Panic("decrementing refCount of previously disposed object"); } # ifdef TCL_THREADS @@ -3253,28 +3754,36 @@ Tcl_DbDecrRefCount(objPtr, file, line) */ if (!TclInExit()) { - Tcl_HashTable *tablePtr; - Tcl_HashEntry *hPtr; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + Tcl_HashTable *tablePtr = tsdPtr->objThreadMap; + Tcl_HashEntry *hPtr; - tablePtr = tsdPtr->objThreadMap; if (!tablePtr) { Tcl_Panic("object table not initialized"); } - hPtr = Tcl_FindHashEntry(tablePtr, (char *) objPtr); + hPtr = Tcl_FindHashEntry(tablePtr, objPtr); if (!hPtr) { - Tcl_Panic("%s%s", - "Trying to decr ref count of ", - "Tcl_Obj allocated in another thread"); + Tcl_Panic("Trying to %s of Tcl_Obj allocated in another thread", + "decr ref count"); } - /* If the Tcl_Obj is going to be deleted, remove the entry */ - if ((((objPtr)->refCount) - 1) <= 0) { + /* + * If the Tcl_Obj is going to be deleted, remove the entry. + */ + + if ((objPtr->refCount - 1) <= 0) { + ObjData *objData = Tcl_GetHashValue(hPtr); + + if (objData != NULL) { + ckfree(objData); + } + Tcl_DeleteHashEntry(hPtr); } } -# endif -#endif +# endif /* TCL_THREADS */ +#endif /* TCL_MEM_DEBUG */ + if (--(objPtr)->refCount <= 0) { TclFreeObj(objPtr); } @@ -3285,11 +3794,11 @@ Tcl_DbDecrRefCount(objPtr, file, line) * * Tcl_DbIsShared -- * - * This procedure is normally called when debugging: i.e., when + * This function is normally called when debugging: i.e., when * TCL_MEM_DEBUG is defined. It tests whether the object has a ref count * greater than one. * - * When TCL_MEM_DEBUG is not defined, this procedure just tests if the + * When TCL_MEM_DEBUG is not defined, this function just tests if the * object has a ref count greater than one. * * Results: @@ -3302,18 +3811,18 @@ Tcl_DbDecrRefCount(objPtr, file, line) */ int -Tcl_DbIsShared(objPtr, file, line) - register Tcl_Obj *objPtr; /* The object to test for being shared. */ - CONST char *file; /* The name of the source file calling this - * procedure; used for debugging. */ - int line; /* Line number in the source file; used for +Tcl_DbIsShared( + register Tcl_Obj *objPtr, /* The object to test for being shared. */ + 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. */ { #ifdef TCL_MEM_DEBUG if (objPtr->refCount == 0x61616161) { fprintf(stderr, "file = %s, line = %d\n", file, line); fflush(stderr); - Tcl_Panic("Trying to check whether previously disposed object is shared."); + Tcl_Panic("checking whether previously disposed object is shared"); } # ifdef TCL_THREADS @@ -3324,22 +3833,21 @@ Tcl_DbIsShared(objPtr, file, line) */ if (!TclInExit()) { - Tcl_HashTable *tablePtr; - Tcl_HashEntry *hPtr; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - tablePtr = tsdPtr->objThreadMap; + Tcl_HashTable *tablePtr = tsdPtr->objThreadMap; + Tcl_HashEntry *hPtr; + if (!tablePtr) { Tcl_Panic("object table not initialized"); } - hPtr = Tcl_FindHashEntry(tablePtr, (char *) objPtr); + hPtr = Tcl_FindHashEntry(tablePtr, objPtr); if (!hPtr) { - Tcl_Panic("%s%s", - "Trying to check shared status of", - "Tcl_Obj allocated in another thread"); + Tcl_Panic("Trying to %s of Tcl_Obj allocated in another thread", + "check shared status"); } } -# endif -#endif +# endif /* TCL_THREADS */ +#endif /* TCL_MEM_DEBUG */ #ifdef TCL_COMPILE_STATS Tcl_MutexLock(&tclObjMutex); @@ -3351,7 +3859,7 @@ Tcl_DbIsShared(objPtr, file, line) tclObjsShared[0]++; } Tcl_MutexUnlock(&tclObjMutex); -#endif +#endif /* TCL_COMPILE_STATS */ return ((objPtr)->refCount > 1); } @@ -3375,8 +3883,8 @@ Tcl_DbIsShared(objPtr, file, line) */ void -Tcl_InitObjHashTable(tablePtr) - register Tcl_HashTable *tablePtr; +Tcl_InitObjHashTable( + register Tcl_HashTable *tablePtr) /* Pointer to table record, which is supplied * by the caller. */ { @@ -3401,16 +3909,16 @@ Tcl_InitObjHashTable(tablePtr) */ static Tcl_HashEntry * -AllocObjEntry(tablePtr, keyPtr) - Tcl_HashTable *tablePtr; /* Hash table. */ - VOID *keyPtr; /* Key to store in the hash table entry. */ +AllocObjEntry( + Tcl_HashTable *tablePtr, /* Hash table. */ + void *keyPtr) /* Key to store in the hash table entry. */ { - Tcl_Obj *objPtr = (Tcl_Obj *) keyPtr; - Tcl_HashEntry *hPtr; + Tcl_Obj *objPtr = keyPtr; + Tcl_HashEntry *hPtr = ckalloc(sizeof(Tcl_HashEntry)); - hPtr = (Tcl_HashEntry *) ckalloc((unsigned) (sizeof(Tcl_HashEntry))); - hPtr->key.oneWordValue = (char *) objPtr; + hPtr->key.objPtr = objPtr; Tcl_IncrRefCount(objPtr); + hPtr->clientData = NULL; return hPtr; } @@ -3418,7 +3926,7 @@ AllocObjEntry(tablePtr, keyPtr) /* *---------------------------------------------------------------------- * - * CompareObjKeys -- + * TclCompareObjKeys -- * * Compares two Tcl_Obj * keys. * @@ -3432,14 +3940,14 @@ AllocObjEntry(tablePtr, keyPtr) *---------------------------------------------------------------------- */ -static int -CompareObjKeys(keyPtr, hPtr) - VOID *keyPtr; /* New key to compare. */ - Tcl_HashEntry *hPtr; /* Existing key to compare. */ +int +TclCompareObjKeys( + void *keyPtr, /* New key to compare. */ + Tcl_HashEntry *hPtr) /* Existing key to compare. */ { - Tcl_Obj *objPtr1 = (Tcl_Obj *) keyPtr; + Tcl_Obj *objPtr1 = keyPtr; Tcl_Obj *objPtr2 = (Tcl_Obj *) hPtr->key.oneWordValue; - register CONST char *p1, *p2; + register const char *p1, *p2; register int l1, l2; /* @@ -3481,7 +3989,7 @@ CompareObjKeys(keyPtr, hPtr) /* *---------------------------------------------------------------------- * - * FreeObjEntry -- + * TclFreeObjEntry -- * * Frees space for a Tcl_HashEntry containing the Tcl_Obj * key. * @@ -3494,20 +4002,20 @@ CompareObjKeys(keyPtr, hPtr) *---------------------------------------------------------------------- */ -static void -FreeObjEntry(hPtr) - Tcl_HashEntry *hPtr; /* Hash entry to free. */ +void +TclFreeObjEntry( + Tcl_HashEntry *hPtr) /* Hash entry to free. */ { Tcl_Obj *objPtr = (Tcl_Obj *) hPtr->key.oneWordValue; Tcl_DecrRefCount(objPtr); - ckfree((char *) hPtr); + ckfree(hPtr); } /* *---------------------------------------------------------------------- * - * HashObjKey -- + * TclHashObjKey -- * * Compute a one-word summary of the string representation of the * Tcl_Obj, which can be used to generate a hash index. @@ -3522,35 +4030,55 @@ FreeObjEntry(hPtr) *---------------------------------------------------------------------- */ -static unsigned int -HashObjKey(tablePtr, keyPtr) - Tcl_HashTable *tablePtr; /* Hash table. */ - VOID *keyPtr; /* Key from which to compute hash value. */ +unsigned int +TclHashObjKey( + Tcl_HashTable *tablePtr, /* Hash table. */ + void *keyPtr) /* Key from which to compute hash value. */ { - Tcl_Obj *objPtr = (Tcl_Obj *) keyPtr; - CONST char *string = TclGetString(objPtr); - int length = objPtr->length; + Tcl_Obj *objPtr = keyPtr; + int length; + const char *string = TclGetStringFromObj(objPtr, &length); unsigned int result = 0; - int i; /* * I tried a zillion different hash functions and asked many other people - * for advice. Many people had their own favorite functions, all - * different, but no-one had much idea why they were good ones. I chose + * for advice. Many people had their own favorite functions, all + * different, but no-one had much idea why they were good ones. I chose * the one below (multiply by 9 and add new character) because of the * following reasons: * * 1. Multiplying by 10 is perfect for keys that are decimal strings, and - * multiplying by 9 is just about as good. - * 2. Times-9 is (shift-left-3) plus (old). This means that each - * character's bits hang around in the low-order bits of the hash value - * for ever, plus they spread fairly rapidly up to the high-order bits - * to fill out the hash value. This seems works well both for decimal - * and *non-decimal strings. + * multiplying by 9 is just about as good. + * 2. Times-9 is (shift-left-3) plus (old). This means that each + * character's bits hang around in the low-order bits of the hash value + * for ever, plus they spread fairly rapidly up to the high-order bits + * to fill out the hash value. This seems works well both for decimal + * and non-decimal strings. + * + * Note that this function is very weak against malicious strings; it's + * very easy to generate multiple keys that have the same hashcode. On the + * other hand, that hardly ever actually occurs and this function *is* + * very cheap, even by comparison with industry-standard hashes like FNV. + * If real strength of hash is required though, use a custom hash based on + * Bob Jenkins's lookup3(), but be aware that it's significantly slower. + * Tcl does not use that level of strength because it typically does not + * need it (and some of the aspects of that strength are genuinely + * unnecessary given the rest of Tcl's hash machinery, and the fact that + * we do not either transfer hashes to another machine, use them as a true + * substitute for equality, or attempt to minimize work in rebuilding the + * hash table). + * + * See also HashStringKey in tclHash.c. + * See also HashString in tclLiteral.c. + * + * See [tcl-Feature Request #2958832] */ - for (i=0 ; i<length ; i++) { - result += (result << 3) + string[i]; + if (length > 0) { + result = UCHAR(*string); + while (--length) { + result += (result << 3) + UCHAR(*++string); + } } return result; } @@ -3568,103 +4096,73 @@ HashObjKey(tablePtr, keyPtr) * * Side effects: * May update the internal representation for the object, caching the - * command reference so that the next time this procedure is called with + * command reference so that the next time this function is called with * the same object, the command can be found quickly. * *---------------------------------------------------------------------- */ Tcl_Command -Tcl_GetCommandFromObj(interp, objPtr) - Tcl_Interp *interp; /* The interpreter in which to resolve the +Tcl_GetCommandFromObj( + Tcl_Interp *interp, /* The interpreter in which to resolve the * command and to report errors. */ - register Tcl_Obj *objPtr; /* The object containing the command's name. + register Tcl_Obj *objPtr) /* The object containing the command's name. * If the name starts with "::", will be * looked up in global namespace. Else, looked * up first in the current namespace, then in * global namespace. */ { - Interp *iPtr = (Interp *) interp; register ResolvedCmdName *resPtr; - register Command *cmdPtr; - Namespace *currNsPtr; - int result; - CallFrame *savedFramePtr; - char *name; - - /* - * If the variable name is fully qualified, do as if the lookup were done - * from the global namespace; this helps avoid repeated lookups of fully - * qualified names. It costs close to nothing, and may be very helpful for - * OO applications which pass along a command name ("this"), [Patch - * 456668] - */ - - savedFramePtr = iPtr->varFramePtr; - name = Tcl_GetString(objPtr); - if ((*name++ == ':') && (*name == ':')) { - iPtr->varFramePtr = NULL; - } /* * Get the internal representation, converting to a command type if * needed. The internal representation is a ResolvedCmdName that points to * the actual command. + * + * Check the context namespace and the namespace epoch of the resolved + * symbol to make sure that it is fresh. Note that we verify that the + * namespace id of the context namespace is the same as the one we cached; + * this insures that the namespace wasn't deleted and a new one created at + * the same address with the same command epoch. Note that fully qualified + * names have a NULL refNsPtr, these checks needn't be made. + * + * Check also that the command's epoch is up to date, and that the command + * is not deleted. + * + * If any check fails, then force another conversion to the command type, + * to discard the old rep and create a new one. */ - if (objPtr->typePtr != &tclCmdNameType) { - result = tclCmdNameType.setFromAnyProc(interp, objPtr); - if (result != TCL_OK) { - iPtr->varFramePtr = savedFramePtr; - return (Tcl_Command) NULL; - } - } - resPtr = (ResolvedCmdName *) objPtr->internalRep.twoPtrValue.ptr1; - - /* - * Get the current namespace. - */ - - if (iPtr->varFramePtr != NULL) { - currNsPtr = iPtr->varFramePtr->nsPtr; - } else { - currNsPtr = iPtr->globalNsPtr; + resPtr = objPtr->internalRep.twoPtrValue.ptr1; + if ((objPtr->typePtr == &tclCmdNameType) && (resPtr != NULL)) { + register Command *cmdPtr = resPtr->cmdPtr; + + if ((cmdPtr->cmdEpoch == resPtr->cmdEpoch) + && !(cmdPtr->flags & CMD_IS_DELETED) + && (interp == cmdPtr->nsPtr->interp) + && !(cmdPtr->nsPtr->flags & NS_DYING)) { + register Namespace *refNsPtr = (Namespace *) + TclGetCurrentNamespace(interp); + + if ((resPtr->refNsPtr == NULL) + || ((refNsPtr == resPtr->refNsPtr) + && (resPtr->refNsId == refNsPtr->nsId) + && (resPtr->refNsCmdEpoch == refNsPtr->cmdRefEpoch))) { + return (Tcl_Command) cmdPtr; + } + } } /* - * Check the context namespace and the namespace epoch of the resolved - * symbol to make sure that it is fresh. If not, then force another - * conversion to the command type, to discard the old rep and create a new - * one. Note that we verify that the namespace id of the context namespace - * is the same as the one we cached; this insures that the namespace - * wasn't deleted and a new one created at the same address with the same - * command epoch. + * OK, must create a new internal representation (or fail) as any cache we + * had is invalid one way or another. */ - cmdPtr = NULL; - if ((resPtr != NULL) - && (resPtr->refNsPtr == currNsPtr) - && (resPtr->refNsId == currNsPtr->nsId) - && (resPtr->refNsCmdEpoch == currNsPtr->cmdRefEpoch)) { - cmdPtr = resPtr->cmdPtr; - if (cmdPtr->cmdEpoch != resPtr->cmdEpoch) { - cmdPtr = NULL; - } + if (SetCmdNameFromAny(interp, objPtr) != TCL_OK) { + return NULL; } - - if (cmdPtr == NULL) { - result = tclCmdNameType.setFromAnyProc(interp, objPtr); - if (result != TCL_OK) { - iPtr->varFramePtr = savedFramePtr; - return (Tcl_Command) NULL; - } - resPtr = (ResolvedCmdName *) objPtr->internalRep.twoPtrValue.ptr1; - if (resPtr != NULL) { - cmdPtr = resPtr->cmdPtr; - } - } - iPtr->varFramePtr = savedFramePtr; - return (Tcl_Command) cmdPtr; + resPtr = objPtr->internalRep.twoPtrValue.ptr1; + return (Tcl_Command) (resPtr ? resPtr->cmdPtr : NULL); } /* @@ -3682,69 +4180,59 @@ Tcl_GetCommandFromObj(interp, objPtr) * The object's old internal rep is freed. It's string rep is not * changed. The refcount in the Command structure is incremented to keep * it from being freed if the command is later deleted until - * TclExecuteByteCode has a chance to recognize that it was deleted. + * TclNRExecuteByteCode has a chance to recognize that it was deleted. * *---------------------------------------------------------------------- */ void -TclSetCmdNameObj(interp, objPtr, cmdPtr) - Tcl_Interp *interp; /* Points to interpreter containing command +TclSetCmdNameObj( + Tcl_Interp *interp, /* Points to interpreter containing command * that should be cached in objPtr. */ - register Tcl_Obj *objPtr; /* Points to Tcl object to be changed to a + register Tcl_Obj *objPtr, /* Points to Tcl object to be changed to a * CmdName object. */ - Command *cmdPtr; /* Points to Command structure that the + Command *cmdPtr) /* Points to Command structure that the * CmdName object should refer to. */ { Interp *iPtr = (Interp *) interp; register ResolvedCmdName *resPtr; register Namespace *currNsPtr; - CallFrame *savedFramePtr; - char *name; + const char *name; if (objPtr->typePtr == &tclCmdNameType) { return; } - /* - * If the variable name is fully qualified, do as if the lookup were done - * from the global namespace; this helps avoid repeated lookups of fully - * qualified names. It costs close to nothing, and may be very helpful for - * OO applications which pass along a command name ("this"), [Patch - * 456668] (Copied over from Tcl_GetCommandFromObj) - */ + cmdPtr->refCount++; + resPtr = ckalloc(sizeof(ResolvedCmdName)); + resPtr->cmdPtr = cmdPtr; + resPtr->cmdEpoch = cmdPtr->cmdEpoch; + resPtr->refCount = 1; - savedFramePtr = iPtr->varFramePtr; - name = Tcl_GetString(objPtr); + name = TclGetString(objPtr); if ((*name++ == ':') && (*name == ':')) { - iPtr->varFramePtr = NULL; - } + /* + * The name is fully qualified: set the referring namespace to + * NULL. + */ - /* - * Get the current namespace. - */ + resPtr->refNsPtr = NULL; + } else { + /* + * Get the current namespace. + */ - if (iPtr->varFramePtr != NULL) { currNsPtr = iPtr->varFramePtr->nsPtr; - } else { - currNsPtr = iPtr->globalNsPtr; - } - cmdPtr->refCount++; - resPtr = (ResolvedCmdName *) ckalloc(sizeof(ResolvedCmdName)); - resPtr->cmdPtr = cmdPtr; - resPtr->refNsPtr = currNsPtr; - resPtr->refNsId = currNsPtr->nsId; - resPtr->refNsCmdEpoch = currNsPtr->cmdRefEpoch; - resPtr->cmdEpoch = cmdPtr->cmdEpoch; - resPtr->refCount = 1; + resPtr->refNsPtr = currNsPtr; + resPtr->refNsId = currNsPtr->nsId; + resPtr->refNsCmdEpoch = currNsPtr->cmdRefEpoch; + } TclFreeIntRep(objPtr); - objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) resPtr; + objPtr->internalRep.twoPtrValue.ptr1 = resPtr; objPtr->internalRep.twoPtrValue.ptr2 = NULL; objPtr->typePtr = &tclCmdNameType; - - iPtr->varFramePtr = savedFramePtr; } /* @@ -3769,16 +4257,15 @@ TclSetCmdNameObj(interp, objPtr, cmdPtr) */ static void -FreeCmdNameInternalRep(objPtr) - register Tcl_Obj *objPtr; /* CmdName object with internal +FreeCmdNameInternalRep( + register Tcl_Obj *objPtr) /* CmdName object with internal * representation to free. */ { - register ResolvedCmdName *resPtr = - (ResolvedCmdName *) objPtr->internalRep.twoPtrValue.ptr1; + register ResolvedCmdName *resPtr = objPtr->internalRep.twoPtrValue.ptr1; if (resPtr != NULL) { /* - * Decrement the reference count of the ResolvedCmdName structure. If + * Decrement the reference count of the ResolvedCmdName structure. If * there are no more uses, free the ResolvedCmdName structure. */ @@ -3791,10 +4278,12 @@ FreeCmdNameInternalRep(objPtr) */ Command *cmdPtr = resPtr->cmdPtr; - TclCleanupCommand(cmdPtr); - ckfree((char *) resPtr); + + TclCleanupCommandMacro(cmdPtr); + ckfree(resPtr); } } + objPtr->typePtr = NULL; } /* @@ -3818,14 +4307,13 @@ FreeCmdNameInternalRep(objPtr) */ static void -DupCmdNameInternalRep(srcPtr, copyPtr) - Tcl_Obj *srcPtr; /* Object with internal rep to copy. */ - register Tcl_Obj *copyPtr; /* Object with internal rep to set. */ +DupCmdNameInternalRep( + Tcl_Obj *srcPtr, /* Object with internal rep to copy. */ + register Tcl_Obj *copyPtr) /* Object with internal rep to set. */ { - register ResolvedCmdName *resPtr = (ResolvedCmdName *) - srcPtr->internalRep.twoPtrValue.ptr1; + register ResolvedCmdName *resPtr = srcPtr->internalRep.twoPtrValue.ptr1; - copyPtr->internalRep.twoPtrValue.ptr1 = (VOID *) resPtr; + copyPtr->internalRep.twoPtrValue.ptr1 = resPtr; copyPtr->internalRep.twoPtrValue.ptr2 = NULL; if (resPtr != NULL) { resPtr->refCount++; @@ -3855,24 +4343,18 @@ DupCmdNameInternalRep(srcPtr, copyPtr) */ static int -SetCmdNameFromAny(interp, objPtr) - Tcl_Interp *interp; /* Used for error reporting if not NULL. */ - register Tcl_Obj *objPtr; /* The object to convert. */ +SetCmdNameFromAny( + Tcl_Interp *interp, /* Used for error reporting if not NULL. */ + register Tcl_Obj *objPtr) /* The object to convert. */ { Interp *iPtr = (Interp *) interp; - char *name; - Tcl_Command cmd; + const char *name; register Command *cmdPtr; Namespace *currNsPtr; register ResolvedCmdName *resPtr; - /* - * Get "objPtr"s string representation. Make it up-to-date if necessary. - */ - - name = objPtr->bytes; - if (name == NULL) { - name = Tcl_GetString(objPtr); + if (interp == NULL) { + return TCL_ERROR; } /* @@ -3883,43 +4365,128 @@ SetCmdNameFromAny(interp, objPtr) * referenced from a CmdName object. */ - cmd = Tcl_FindCommand(interp, name, (Tcl_Namespace *) NULL, - /*flags*/ 0); - cmdPtr = (Command *) cmd; - if (cmdPtr != NULL) { - /* - * Get the current namespace. - */ + name = TclGetString(objPtr); + cmdPtr = (Command *) + Tcl_FindCommand(interp, name, /*ns*/ NULL, /*flags*/ 0); - if (iPtr->varFramePtr != NULL) { - currNsPtr = iPtr->varFramePtr->nsPtr; + /* + * 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. + */ + + 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 { - currNsPtr = iPtr->globalNsPtr; + TclFreeIntRep(objPtr); + resPtr = 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. + */ - cmdPtr->refCount++; - resPtr = (ResolvedCmdName *) ckalloc(sizeof(ResolvedCmdName)); - resPtr->cmdPtr = cmdPtr; - resPtr->refNsPtr = currNsPtr; - resPtr->refNsId = currNsPtr->nsId; - resPtr->refNsCmdEpoch = currNsPtr->cmdRefEpoch; - resPtr->cmdEpoch = cmdPtr->cmdEpoch; - resPtr->refCount = 1; + resPtr->refNsPtr = NULL; + } else { + /* + * Get the current namespace. + */ + + currNsPtr = iPtr->varFramePtr->nsPtr; + + resPtr->refNsPtr = currNsPtr; + resPtr->refNsId = currNsPtr->nsId; + resPtr->refNsCmdEpoch = currNsPtr->cmdRefEpoch; + } } else { - resPtr = NULL; /* no command named "name" was found */ + TclFreeIntRep(objPtr); + objPtr->internalRep.twoPtrValue.ptr1 = NULL; + objPtr->internalRep.twoPtrValue.ptr2 = NULL; + objPtr->typePtr = &tclCmdNameType; + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_RepresentationCmd -- + * + * Implementation of the "tcl::unsupported::representation" command. + * + * Results: + * Reports the current representation (Tcl_Obj type) of its argument. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_RepresentationCmd( + ClientData clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const objv[]) +{ + char ptrBuffer[2*TCL_INTEGER_SPACE+6]; + Tcl_Obj *descObj; + + if (objc != 2) { + Tcl_WrongNumArgs(interp, 1, objv, "value"); + return TCL_ERROR; } /* - * Free the old internalRep before setting the new one. We do this as late - * as possible to allow the conversion code, in particular - * GetStringFromObj, to use that old internalRep. If no Command structure - * was found, leave NULL as the cached value. + * Value is a bignum with a refcount of 14, object pointer at 0x12345678, + * internal representation 0x45671234:0x98765432, string representation + * "1872361827361287" */ - TclFreeIntRep(objPtr); - objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) resPtr; - objPtr->internalRep.twoPtrValue.ptr2 = NULL; - objPtr->typePtr = &tclCmdNameType; + 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); + + 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]->bytes) { + Tcl_AppendToObj(descObj, ", string representation \"", -1); + Tcl_AppendLimitedToObj(descObj, objv[1]->bytes, objv[1]->length, + 16, "..."); + Tcl_AppendToObj(descObj, "\"", -1); + } else { + Tcl_AppendToObj(descObj, ", no string representation", -1); + } + + Tcl_SetObjResult(interp, descObj); return TCL_OK; } @@ -3928,5 +4495,7 @@ SetCmdNameFromAny(interp, objPtr) * mode: c * c-basic-offset: 4 * fill-column: 78 + * tab-width: 8 + * indent-tabs-mode: nil * End: */ |