diff options
Diffstat (limited to 'generic/tclDictObj.c')
-rw-r--r-- | generic/tclDictObj.c | 3080 |
1 files changed, 1852 insertions, 1228 deletions
diff --git a/generic/tclDictObj.c b/generic/tclDictObj.c index 1e428a1..e31d708 100644 --- a/generic/tclDictObj.c +++ b/generic/tclDictObj.c @@ -1,18 +1,17 @@ -/* +/* * tclDictObj.c -- * - * This file contains procedures that implement the Tcl dict object - * type and its accessor command. - * - * Copyright (c) 2002 by Donal K. Fellows. + * This file contains functions that implement the Tcl dict object type + * and its accessor command. * - * See the file "license.terms" for information on usage and redistribution - * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * Copyright (c) 2002-2010 by Donal K. Fellows. * - * RCS: @(#) $Id: tclDictObj.c,v 1.33 2005/07/21 21:49:05 dkf Exp $ + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ #include "tclInt.h" +#include "tommath.h" /* * Forward declaration. @@ -20,142 +19,375 @@ struct Dict; /* - * Prototypes for procedures defined later in this file: + * Prototypes for functions defined later in this file: + */ + +static void DeleteDict(struct Dict *dict); +static int DictAppendCmd(ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *const *objv); +static int DictCreateCmd(ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *const *objv); +static int DictExistsCmd(ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *const *objv); +static int DictFilterCmd(ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *const *objv); +static int DictGetCmd(ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *const *objv); +static int DictIncrCmd(ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *const *objv); +static int DictInfoCmd(ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *const *objv); +static int DictKeysCmd(ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *const *objv); +static int DictLappendCmd(ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *const *objv); +static int DictMergeCmd(ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *const *objv); +static int DictRemoveCmd(ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *const *objv); +static int DictReplaceCmd(ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *const *objv); +static int DictSetCmd(ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *const *objv); +static int DictSizeCmd(ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *const *objv); +static int DictUnsetCmd(ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *const *objv); +static int DictUpdateCmd(ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *const *objv); +static int DictValuesCmd(ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *const *objv); +static int DictWithCmd(ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *const *objv); +static void DupDictInternalRep(Tcl_Obj *srcPtr, Tcl_Obj *copyPtr); +static void FreeDictInternalRep(Tcl_Obj *dictPtr); +static void InvalidateDictChain(Tcl_Obj *dictObj); +static int SetDictFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr); +static void UpdateStringOfDict(Tcl_Obj *dictPtr); +static Tcl_HashEntry * AllocChainEntry(Tcl_HashTable *tablePtr,void *keyPtr); +static inline void InitChainTable(struct Dict *dict); +static inline void DeleteChainTable(struct Dict *dict); +static inline Tcl_HashEntry *CreateChainEntry(struct Dict *dict, + Tcl_Obj *keyPtr, int *newPtr); +static inline int DeleteChainEntry(struct Dict *dict, Tcl_Obj *keyPtr); +static int FinalizeDictUpdate(ClientData data[], + Tcl_Interp *interp, int result); +static int FinalizeDictWith(ClientData data[], + Tcl_Interp *interp, int result); +static int DictForNRCmd(ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *const *objv); +static int DictMapNRCmd(ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *const *objv); +static int DictForLoopCallback(ClientData data[], + Tcl_Interp *interp, int result); +static int DictMapLoopCallback(ClientData data[], + Tcl_Interp *interp, int result); + +/* + * Table of dict subcommand names and implementations. */ -static void DeleteDict _ANSI_ARGS_((struct Dict *dict)); -static int DictAppendCmd _ANSI_ARGS_((Tcl_Interp *interp, - int objc, Tcl_Obj *CONST *objv)); -static int DictCreateCmd _ANSI_ARGS_((Tcl_Interp *interp, - int objc, Tcl_Obj *CONST *objv)); -static int DictExistsCmd _ANSI_ARGS_((Tcl_Interp *interp, - int objc, Tcl_Obj *CONST *objv)); -static int DictFilterCmd _ANSI_ARGS_((Tcl_Interp *interp, - int objc, Tcl_Obj *CONST *objv)); -static int DictForCmd _ANSI_ARGS_((Tcl_Interp *interp, - int objc, Tcl_Obj *CONST *objv)); -static int DictGetCmd _ANSI_ARGS_((Tcl_Interp *interp, - int objc, Tcl_Obj *CONST *objv)); -static int DictIncrCmd _ANSI_ARGS_((Tcl_Interp *interp, - int objc, Tcl_Obj *CONST *objv)); -static int DictInfoCmd _ANSI_ARGS_((Tcl_Interp *interp, - int objc, Tcl_Obj *CONST *objv)); -static int DictKeysCmd _ANSI_ARGS_((Tcl_Interp *interp, - int objc, Tcl_Obj *CONST *objv)); -static int DictLappendCmd _ANSI_ARGS_((Tcl_Interp *interp, - int objc, Tcl_Obj *CONST *objv)); -static int DictMergeCmd _ANSI_ARGS_((Tcl_Interp *interp, - int objc, Tcl_Obj *CONST *objv)); -static int DictRemoveCmd _ANSI_ARGS_((Tcl_Interp *interp, - int objc, Tcl_Obj *CONST *objv)); -static int DictReplaceCmd _ANSI_ARGS_((Tcl_Interp *interp, - int objc, Tcl_Obj *CONST *objv)); -static int DictSetCmd _ANSI_ARGS_((Tcl_Interp *interp, - int objc, Tcl_Obj *CONST *objv)); -static int DictSizeCmd _ANSI_ARGS_((Tcl_Interp *interp, - int objc, Tcl_Obj *CONST *objv)); -static int DictUnsetCmd _ANSI_ARGS_((Tcl_Interp *interp, - int objc, Tcl_Obj *CONST *objv)); -static int DictValuesCmd _ANSI_ARGS_((Tcl_Interp *interp, - int objc, Tcl_Obj *CONST *objv)); -static int DictUpdateCmd _ANSI_ARGS_((Tcl_Interp *interp, - int objc, Tcl_Obj *CONST *objv)); -static int DictWithCmd _ANSI_ARGS_((Tcl_Interp *interp, - int objc, Tcl_Obj *CONST *objv)); -static void DupDictInternalRep _ANSI_ARGS_((Tcl_Obj *srcPtr, - Tcl_Obj *copyPtr)); -static void FreeDictInternalRep _ANSI_ARGS_((Tcl_Obj *dictPtr)); -static void InvalidateDictChain _ANSI_ARGS_((Tcl_Obj *dictObj)); -static int SetDictFromAny _ANSI_ARGS_((Tcl_Interp *interp, - Tcl_Obj *objPtr)); -static void UpdateStringOfDict _ANSI_ARGS_((Tcl_Obj *dictPtr)); +static const EnsembleImplMap implementationMap[] = { + {"append", DictAppendCmd, TclCompileDictAppendCmd, NULL, NULL, 0 }, + {"create", DictCreateCmd, TclCompileDictCreateCmd, NULL, NULL, 0 }, + {"exists", DictExistsCmd, TclCompileDictExistsCmd, NULL, NULL, 0 }, + {"filter", DictFilterCmd, NULL, NULL, NULL, 0 }, + {"for", NULL, TclCompileDictForCmd, DictForNRCmd, NULL, 0 }, + {"get", DictGetCmd, TclCompileDictGetCmd, NULL, NULL, 0 }, + {"incr", DictIncrCmd, TclCompileDictIncrCmd, NULL, NULL, 0 }, + {"info", DictInfoCmd, TclCompileBasic1ArgCmd, NULL, NULL, 0 }, + {"keys", DictKeysCmd, TclCompileBasic1Or2ArgCmd, NULL, NULL, 0 }, + {"lappend", DictLappendCmd, TclCompileDictLappendCmd, NULL, NULL, 0 }, + {"map", NULL, TclCompileDictMapCmd, DictMapNRCmd, NULL, 0 }, + {"merge", DictMergeCmd, TclCompileDictMergeCmd, NULL, NULL, 0 }, + {"remove", DictRemoveCmd, TclCompileBasicMin1ArgCmd, NULL, NULL, 0 }, + {"replace", DictReplaceCmd, NULL, NULL, NULL, 0 }, + {"set", DictSetCmd, TclCompileDictSetCmd, NULL, NULL, 0 }, + {"size", DictSizeCmd, TclCompileBasic1ArgCmd, NULL, NULL, 0 }, + {"unset", DictUnsetCmd, TclCompileDictUnsetCmd, NULL, NULL, 0 }, + {"update", DictUpdateCmd, TclCompileDictUpdateCmd, NULL, NULL, 0 }, + {"values", DictValuesCmd, TclCompileBasic1Or2ArgCmd, NULL, NULL, 0 }, + {"with", DictWithCmd, TclCompileDictWithCmd, NULL, NULL, 0 }, + {NULL, NULL, NULL, NULL, NULL, 0} +}; + +/* + * Internal representation of the entries in the hash table that backs a + * dictionary. + */ + +typedef struct ChainEntry { + Tcl_HashEntry entry; + struct ChainEntry *prevPtr; + struct ChainEntry *nextPtr; +} ChainEntry; /* * Internal representation of a dictionary. * - * The internal representation of a dictionary object is a hash table - * (with Tcl_Objs for both keys and values), a reference count and - * epoch number for detecting concurrent modifications of the - * dictionary, and a pointer to the parent object (used when - * invalidating string reps of pathed dictionary trees) which is NULL - * in normal use. The fact that hash tables know (with appropriate - * initialisation) already about objects makes key management /so/ + * The internal representation of a dictionary object is a hash table (with + * Tcl_Objs for both keys and values), a reference count and epoch number for + * detecting concurrent modifications of the dictionary, and a pointer to the + * parent object (used when invalidating string reps of pathed dictionary + * trees) which is NULL in normal use. The fact that hash tables know (with + * appropriate initialisation) already about objects makes key management /so/ * much easier! * - * Reference counts are used to enable safe iteration across hashes - * while allowing the type of the containing object to be modified. + * Reference counts are used to enable safe iteration across hashes while + * allowing the type of the containing object to be modified. */ typedef struct Dict { - Tcl_HashTable table; - int epoch; - int refcount; - Tcl_Obj *chain; + Tcl_HashTable table; /* Object hash table to store mapping in. */ + ChainEntry *entryChainHead; /* Linked list of all entries in the + * dictionary. Used for doing traversal of the + * entries in the order that they are + * created. */ + ChainEntry *entryChainTail; /* Other end of linked list of all entries in + * the dictionary. Used for doing traversal of + * the entries in the order that they are + * created. */ + int epoch; /* Epoch counter */ + int refcount; /* Reference counter (see above) */ + Tcl_Obj *chain; /* Linked list used for invalidating the + * string representations of updated nested + * dictionaries. */ } Dict; /* * The structure below defines the dictionary object type by means of - * procedures that can be invoked by generic object code. + * functions that can be invoked by generic object code. */ -Tcl_ObjType tclDictType = { +const Tcl_ObjType tclDictType = { "dict", FreeDictInternalRep, /* freeIntRepProc */ - DupDictInternalRep, /* dupIntRepProc */ + DupDictInternalRep, /* dupIntRepProc */ UpdateStringOfDict, /* updateStringProc */ SetDictFromAny /* setFromAnyProc */ }; + +/* + * The type of the specially adapted version of the Tcl_Obj*-containing hash + * table defined in the tclObj.c code. This version differs in that it + * allocates a bit more space in each hash entry in order to hold the pointers + * used to keep the hash entries in a linked list. + * + * Note that this type of hash table is *only* suitable for direct use in + * *this* file. Everything else should use the dict iterator API. + */ + +static const Tcl_HashKeyType chainHashType = { + TCL_HASH_KEY_TYPE_VERSION, + 0, + TclHashObjKey, + TclCompareObjKeys, + AllocChainEntry, + TclFreeObjEntry +}; + +/* + * Structure used in implementation of 'dict map' to hold the state that gets + * passed between parts of the implementation. + */ + +typedef struct { + Tcl_Obj *keyVarObj; /* The name of the variable that will have + * keys assigned to it. */ + Tcl_Obj *valueVarObj; /* The name of the variable that will have + * values assigned to it. */ + Tcl_DictSearch search; /* The dictionary search structure. */ + Tcl_Obj *scriptObj; /* The script to evaluate each time through + * the loop. */ + Tcl_Obj *accumulatorObj; /* The dictionary used to accumulate the + * results. */ +} DictMapStorage; + +/***** START OF FUNCTIONS IMPLEMENTING DICT CORE API *****/ + +/* + *---------------------------------------------------------------------- + * + * AllocChainEntry -- + * + * Allocate space for a Tcl_HashEntry containing the Tcl_Obj * key, and + * which has a bit of extra space afterwards for storing pointers to the + * rest of the chain of entries (the extra pointers are left NULL). + * + * Results: + * The return value is a pointer to the created entry. + * + * Side effects: + * Increments the reference count on the object. + * + *---------------------------------------------------------------------- + */ + +static Tcl_HashEntry * +AllocChainEntry( + Tcl_HashTable *tablePtr, + void *keyPtr) +{ + Tcl_Obj *objPtr = keyPtr; + ChainEntry *cPtr; + + cPtr = ckalloc(sizeof(ChainEntry)); + cPtr->entry.key.objPtr = objPtr; + Tcl_IncrRefCount(objPtr); + cPtr->entry.clientData = NULL; + cPtr->prevPtr = cPtr->nextPtr = NULL; + + return &cPtr->entry; +} + +/* + * Helper functions that disguise most of the details relating to how the + * linked list of hash entries is managed. In particular, these manage the + * creation of the table and initializing of the chain, the deletion of the + * table and chain, the adding of an entry to the chain, and the removal of an + * entry from the chain. + */ + +static inline void +InitChainTable( + Dict *dict) +{ + Tcl_InitCustomHashTable(&dict->table, TCL_CUSTOM_PTR_KEYS, + &chainHashType); + dict->entryChainHead = dict->entryChainTail = NULL; +} + +static inline void +DeleteChainTable( + Dict *dict) +{ + ChainEntry *cPtr; + + for (cPtr=dict->entryChainHead ; cPtr!=NULL ; cPtr=cPtr->nextPtr) { + Tcl_Obj *valuePtr = Tcl_GetHashValue(&cPtr->entry); + + TclDecrRefCount(valuePtr); + } + Tcl_DeleteHashTable(&dict->table); +} + +static inline Tcl_HashEntry * +CreateChainEntry( + Dict *dict, + Tcl_Obj *keyPtr, + int *newPtr) +{ + ChainEntry *cPtr = (ChainEntry *) + Tcl_CreateHashEntry(&dict->table, keyPtr, newPtr); + + /* + * If this is a new entry in the hash table, stitch it into the chain. + */ + + if (*newPtr) { + cPtr->nextPtr = NULL; + if (dict->entryChainHead == NULL) { + cPtr->prevPtr = NULL; + dict->entryChainHead = cPtr; + dict->entryChainTail = cPtr; + } else { + cPtr->prevPtr = dict->entryChainTail; + dict->entryChainTail->nextPtr = cPtr; + dict->entryChainTail = cPtr; + } + } + + return &cPtr->entry; +} + +static inline int +DeleteChainEntry( + Dict *dict, + Tcl_Obj *keyPtr) +{ + ChainEntry *cPtr = (ChainEntry *) + Tcl_FindHashEntry(&dict->table, keyPtr); + + if (cPtr == NULL) { + return 0; + } else { + Tcl_Obj *valuePtr = Tcl_GetHashValue(&cPtr->entry); + TclDecrRefCount(valuePtr); + } + + /* + * Unstitch from the chain. + */ + + if (cPtr->nextPtr) { + cPtr->nextPtr->prevPtr = cPtr->prevPtr; + } else { + dict->entryChainTail = cPtr->prevPtr; + } + if (cPtr->prevPtr) { + cPtr->prevPtr->nextPtr = cPtr->nextPtr; + } else { + dict->entryChainHead = cPtr->nextPtr; + } + + Tcl_DeleteHashEntry(&cPtr->entry); + return 1; +} /* *---------------------------------------------------------------------- * * DupDictInternalRep -- * - * Initialize the internal representation of a dictionary Tcl_Obj - * to a copy of the internal representation of an existing - * dictionary object. + * Initialize the internal representation of a dictionary Tcl_Obj to a + * copy of the internal representation of an existing dictionary object. * * Results: * None. * * Side effects: - * "srcPtr"s dictionary internal rep pointer should not be NULL and - * we assume it is not NULL. We set "copyPtr"s internal rep to a - * pointer to a newly allocated dictionary rep that, in turn, points - * to "srcPtr"s key and value objects. Those objects are not - * actually copied but are shared between "srcPtr" and "copyPtr". - * The ref count of each key and value object is incremented. + * "srcPtr"s dictionary internal rep pointer should not be NULL and we + * assume it is not NULL. We set "copyPtr"s internal rep to a pointer to + * a newly allocated dictionary rep that, in turn, points to "srcPtr"s + * key and value objects. Those objects are not actually copied but are + * shared between "srcPtr" and "copyPtr". The ref count of each key and + * value object is incremented. * *---------------------------------------------------------------------- */ static void -DupDictInternalRep(srcPtr, copyPtr) - Tcl_Obj *srcPtr, *copyPtr; +DupDictInternalRep( + Tcl_Obj *srcPtr, + Tcl_Obj *copyPtr) { - Dict *oldDict = (Dict *) srcPtr->internalRep.otherValuePtr; - Dict *newDict = (Dict *) ckalloc(sizeof(Dict)); - Tcl_HashEntry *hPtr, *newHPtr; - Tcl_HashSearch search; - Tcl_Obj *keyPtr, *valuePtr; - int isNew; + Dict *oldDict = srcPtr->internalRep.twoPtrValue.ptr1; + Dict *newDict = ckalloc(sizeof(Dict)); + ChainEntry *cPtr; /* * Copy values across from the old hash table. */ - Tcl_InitObjHashTable(&newDict->table); - for (hPtr=Tcl_FirstHashEntry(&oldDict->table,&search); hPtr!=NULL; - hPtr=Tcl_NextHashEntry(&search)) { - keyPtr = (Tcl_Obj *) Tcl_GetHashKey(&oldDict->table, hPtr); - valuePtr = (Tcl_Obj *) Tcl_GetHashValue(hPtr); - newHPtr = Tcl_CreateHashEntry(&newDict->table, (char *)keyPtr, &isNew); - Tcl_SetHashValue(newHPtr, (ClientData)valuePtr); + + InitChainTable(newDict); + for (cPtr=oldDict->entryChainHead ; cPtr!=NULL ; cPtr=cPtr->nextPtr) { + Tcl_Obj *key = Tcl_GetHashKey(&oldDict->table, &cPtr->entry); + Tcl_Obj *valuePtr = Tcl_GetHashValue(&cPtr->entry); + int n; + Tcl_HashEntry *hPtr = CreateChainEntry(newDict, key, &n); + + /* + * Fill in the contents. + */ + + Tcl_SetHashValue(hPtr, valuePtr); Tcl_IncrRefCount(valuePtr); } /* * Initialise other fields. */ + newDict->epoch = 0; newDict->chain = NULL; newDict->refcount = 1; @@ -163,7 +395,8 @@ DupDictInternalRep(srcPtr, copyPtr) /* * Store in the object. */ - copyPtr->internalRep.otherValuePtr = (VOID *) newDict; + + copyPtr->internalRep.twoPtrValue.ptr1 = newDict; copyPtr->typePtr = &tclDictType; } @@ -172,32 +405,30 @@ DupDictInternalRep(srcPtr, copyPtr) * * FreeDictInternalRep -- * - * Deallocate the storage associated with a dictionary object's - * internal representation. + * Deallocate the storage associated with a dictionary object's internal + * representation. * * Results: * None * * Side effects: - * Frees the memory holding the dictionary's internal hash table - * unless it is locked by an iteration going over it. + * Frees the memory holding the dictionary's internal hash table unless + * it is locked by an iteration going over it. * *---------------------------------------------------------------------- - */ static void -FreeDictInternalRep(dictPtr) - Tcl_Obj *dictPtr; +FreeDictInternalRep( + Tcl_Obj *dictPtr) { - Dict *dict = (Dict *) dictPtr->internalRep.otherValuePtr; + Dict *dict = dictPtr->internalRep.twoPtrValue.ptr1; - --dict->refcount; + dict->refcount--; if (dict->refcount <= 0) { DeleteDict(dict); } - - dictPtr->internalRep.otherValuePtr = NULL; /* Belt and braces! */ + dictPtr->typePtr = NULL; } /* @@ -205,41 +436,27 @@ FreeDictInternalRep(dictPtr) * * DeleteDict -- * - * Delete the structure that is used to implement a dictionary's - * internal representation. Called when either the dictionary - * object loses its internal representation or when the last - * iteration over the dictionary completes. + * Delete the structure that is used to implement a dictionary's internal + * representation. Called when either the dictionary object loses its + * internal representation or when the last iteration over the dictionary + * completes. * * Results: * None * * Side effects: - * Decrements the reference count of all key and value objects in - * the dictionary, which may free them. + * Decrements the reference count of all key and value objects in the + * dictionary, which may free them. * *---------------------------------------------------------------------- */ static void -DeleteDict(dict) - Dict *dict; +DeleteDict( + Dict *dict) { - Tcl_HashEntry *hPtr; - Tcl_HashSearch search; - Tcl_Obj *valuePtr; - - /* - * Delete the values ourselves, because hashes know nothing about - * their contents (but do know about the key type, so that doesn't - * need explicit attention.) - */ - for (hPtr=Tcl_FirstHashEntry(&dict->table,&search); hPtr!=NULL; - hPtr=Tcl_NextHashEntry(&search)) { - valuePtr = (Tcl_Obj *) Tcl_GetHashValue(hPtr); - TclDecrRefCount(valuePtr); - } - Tcl_DeleteHashTable(&dict->table); - ckfree((char *) dict); + DeleteChainTable(dict); + ckfree(dict); } /* @@ -247,41 +464,50 @@ DeleteDict(dict) * * UpdateStringOfDict -- * - * Update the string representation for a dictionary object. - * Note: This procedure does not invalidate an existing old string - * rep so storage will be lost if this has not already been done. - * This code is based on UpdateStringOfList in tclListObj.c + * Update the string representation for a dictionary object. Note: This + * function does not invalidate an existing old string rep so storage + * will be lost if this has not already been done. This code is based on + * UpdateStringOfList in tclListObj.c * * Results: * None. * * Side effects: - * The object's string is set to a valid string that results from - * the dict-to-string conversion. This string will be empty if the - * dictionary has no key/value pairs. The dictionary internal - * representation should not be NULL and we assume it is not NULL. + * The object's string is set to a valid string that results from the + * dict-to-string conversion. This string will be empty if the dictionary + * has no key/value pairs. The dictionary internal representation should + * not be NULL and we assume it is not NULL. * *---------------------------------------------------------------------- */ static void -UpdateStringOfDict(dictPtr) - Tcl_Obj *dictPtr; +UpdateStringOfDict( + Tcl_Obj *dictPtr) { #define LOCAL_SIZE 20 - int localFlags[LOCAL_SIZE], *flagPtr; - Dict *dict = (Dict *) dictPtr->internalRep.otherValuePtr; - Tcl_HashEntry *hPtr; - Tcl_HashSearch search; + int localFlags[LOCAL_SIZE], *flagPtr = NULL; + Dict *dict = dictPtr->internalRep.twoPtrValue.ptr1; + ChainEntry *cPtr; Tcl_Obj *keyPtr, *valuePtr; - int numElems, i, length; - char *elem, *dst; + int i, length, bytesNeeded = 0; + const char *elem; + char *dst; + const int maxFlags = UINT_MAX / sizeof(int); /* - * This field is the most useful one in the whole hash structure, - * and it is not exposed by any API function... + * This field is the most useful one in the whole hash structure, and it + * is not exposed by any API function... */ - numElems = dict->table.numEntries * 2; + + int numElems = dict->table.numEntries * 2; + + /* Handle empty list case first, simplifies what follows */ + if (numElems == 0) { + dictPtr->bytes = tclEmptyStringRep; + dictPtr->length = 0; + return; + } /* * Pass 1: estimate space, gather flags. @@ -289,57 +515,63 @@ UpdateStringOfDict(dictPtr) if (numElems <= LOCAL_SIZE) { flagPtr = localFlags; + } else if (numElems > maxFlags) { + Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX); } else { - flagPtr = (int *) ckalloc((unsigned) numElems*sizeof(int)); + flagPtr = ckalloc(numElems * sizeof(int)); } - dictPtr->length = 1; - for (i=0,hPtr=Tcl_FirstHashEntry(&dict->table,&search) ; i<numElems ; - i+=2,hPtr=Tcl_NextHashEntry(&search)) { + for (i=0,cPtr=dict->entryChainHead; i<numElems; i+=2,cPtr=cPtr->nextPtr) { /* - * Assume that hPtr is never NULL since we know the number of - * array elements already. + * Assume that cPtr is never NULL since we know the number of array + * elements already. */ - keyPtr = (Tcl_Obj *) Tcl_GetHashKey(&dict->table, hPtr); - elem = Tcl_GetStringFromObj(keyPtr, &length); - dictPtr->length += Tcl_ScanCountedElement(elem, length, - &flagPtr[i]) + 1; + flagPtr[i] = ( i ? TCL_DONT_QUOTE_HASH : 0 ); + keyPtr = Tcl_GetHashKey(&dict->table, &cPtr->entry); + elem = TclGetStringFromObj(keyPtr, &length); + bytesNeeded += TclScanElement(elem, length, flagPtr+i); + if (bytesNeeded < 0) { + Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX); + } - valuePtr = (Tcl_Obj *) Tcl_GetHashValue(hPtr); - elem = Tcl_GetStringFromObj(valuePtr, &length); - dictPtr->length += Tcl_ScanCountedElement(elem, length, - &flagPtr[i+1]) + 1; + flagPtr[i+1] = TCL_DONT_QUOTE_HASH; + valuePtr = Tcl_GetHashValue(&cPtr->entry); + elem = TclGetStringFromObj(valuePtr, &length); + bytesNeeded += TclScanElement(elem, length, flagPtr+i+1); + if (bytesNeeded < 0) { + Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX); + } + } + if (bytesNeeded > INT_MAX - numElems + 1) { + Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX); } + bytesNeeded += numElems; /* * Pass 2: copy into string rep buffer. */ - dictPtr->bytes = ckalloc((unsigned) dictPtr->length); + dictPtr->length = bytesNeeded - 1; + dictPtr->bytes = ckalloc(bytesNeeded); dst = dictPtr->bytes; - for (i=0,hPtr=Tcl_FirstHashEntry(&dict->table,&search) ; i<numElems ; - i+=2,hPtr=Tcl_NextHashEntry(&search)) { - keyPtr = (Tcl_Obj *) Tcl_GetHashKey(&dict->table, hPtr); - elem = Tcl_GetStringFromObj(keyPtr, &length); - dst += Tcl_ConvertCountedElement(elem, length, dst, - flagPtr[i] | (i==0 ? 0 : TCL_DONT_QUOTE_HASH) ); - *(dst++) = ' '; - - valuePtr = (Tcl_Obj *) Tcl_GetHashValue(hPtr); - elem = Tcl_GetStringFromObj(valuePtr, &length); - dst += Tcl_ConvertCountedElement(elem, length, dst, - flagPtr[i+1] | TCL_DONT_QUOTE_HASH); - *(dst++) = ' '; + for (i=0,cPtr=dict->entryChainHead; i<numElems; i+=2,cPtr=cPtr->nextPtr) { + flagPtr[i] |= ( i ? TCL_DONT_QUOTE_HASH : 0 ); + keyPtr = Tcl_GetHashKey(&dict->table, &cPtr->entry); + elem = TclGetStringFromObj(keyPtr, &length); + dst += TclConvertElement(elem, length, dst, flagPtr[i]); + *dst++ = ' '; + + flagPtr[i+1] |= TCL_DONT_QUOTE_HASH; + valuePtr = Tcl_GetHashValue(&cPtr->entry); + elem = TclGetStringFromObj(valuePtr, &length); + dst += TclConvertElement(elem, length, dst, flagPtr[i+1]); + *dst++ = ' '; } + dictPtr->bytes[dictPtr->length] = '\0'; + if (flagPtr != localFlags) { - ckfree((char *) flagPtr); - } - if (dst == dictPtr->bytes) { - *dst = 0; - } else { - *(--dst) = 0; + ckfree(flagPtr); } - dictPtr->length = dst - dictPtr->bytes; } /* @@ -347,11 +579,10 @@ UpdateStringOfDict(dictPtr) * * SetDictFromAny -- * - * Convert a non-dictionary object into a dictionary object. This - * code is very closely related to SetListFromAny in tclListObj.c - * but does not actually guarantee that a dictionary object will - * have a string rep (as conversions from lists are handled with a - * special case.) + * Convert a non-dictionary object into a dictionary object. This code is + * very closely related to SetListFromAny in tclListObj.c but does not + * actually guarantee that a dictionary object will have a string rep (as + * conversions from lists are handled with a special case.) * * Results: * A standard Tcl result. @@ -364,168 +595,117 @@ UpdateStringOfDict(dictPtr) */ static int -SetDictFromAny(interp, objPtr) - Tcl_Interp *interp; - Tcl_Obj *objPtr; +SetDictFromAny( + Tcl_Interp *interp, + Tcl_Obj *objPtr) { - char *string, *s; - CONST char *elemStart, *nextElem; - int lenRemain, length, elemSize, hasBrace, result, isNew; - char *limit; /* Points just after string's last byte. */ - register CONST char *p; - register Tcl_Obj *keyPtr, *valuePtr; - Dict *dict; Tcl_HashEntry *hPtr; - Tcl_HashSearch search; + int isNew, result; + Dict *dict = ckalloc(sizeof(Dict)); + + InitChainTable(dict); /* * Since lists and dictionaries have very closely-related string - * representations (i.e. the same parsing code) we can safely - * special-case the conversion from lists to dictionaries. + * representations (i.e. the same parsing code) we can safely special-case + * the conversion from lists to dictionaries. */ if (objPtr->typePtr == &tclListType) { int objc, i; Tcl_Obj **objv; - if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) { - return TCL_ERROR; - } + /* Cannot fail, we already know the Tcl_ObjType is "list". */ + TclListObjGetElements(NULL, objPtr, &objc, &objv); if (objc & 1) { - if (interp != NULL) { - Tcl_SetObjResult(interp, - Tcl_NewStringObj("missing value to go with key", -1)); - } - return TCL_ERROR; - } - - /* - * If the list is shared its string rep must not be lost so it - * still is the same list. - */ - - if (Tcl_IsShared(objPtr)) { - (void) Tcl_GetString(objPtr); + goto missingValue; } - /* - * Build the hash of key/value pairs. - */ - dict = (Dict *) ckalloc(sizeof(Dict)); - Tcl_InitObjHashTable(&dict->table); for (i=0 ; i<objc ; i+=2) { - /* - * Store key and value in the hash table we're building. - */ - - hPtr = Tcl_CreateHashEntry(&dict->table, (char *)objv[i], &isNew); + + /* Store key and value in the hash table we're building. */ + hPtr = CreateChainEntry(dict, objv[i], &isNew); if (!isNew) { - Tcl_Obj *discardedValue = (Tcl_Obj *) Tcl_GetHashValue(hPtr); - TclDecrRefCount(discardedValue); - } - Tcl_SetHashValue(hPtr, (ClientData) objv[i+1]); - Tcl_IncrRefCount(objv[i+1]); /* since hash now holds ref to it */ - } + Tcl_Obj *discardedValue = Tcl_GetHashValue(hPtr); - /* - * Share type-setting code with the string-conversion case. - */ - goto installHash; - } - - /* - * Get the string representation. Make it up-to-date if necessary. - */ - - string = Tcl_GetStringFromObj(objPtr, &length); - limit = (string + length); + /* + * Not really a well-formed dictionary as there are duplicate + * keys, so better get the string rep here so that we can + * convert back. + */ - /* - * Allocate a new HashTable that has objects for keys and objects - * for values. - */ + (void) Tcl_GetString(objPtr); - dict = (Dict *) ckalloc(sizeof(Dict)); - Tcl_InitObjHashTable(&dict->table); - for (p = string, lenRemain = length; - lenRemain > 0; - p = nextElem, lenRemain = (limit - nextElem)) { - result = TclFindElement(interp, p, lenRemain, - &elemStart, &nextElem, &elemSize, &hasBrace); - if (result != TCL_OK) { - goto errorExit; - } - if (elemStart >= limit) { - break; + TclDecrRefCount(discardedValue); + } + Tcl_SetHashValue(hPtr, objv[i+1]); + Tcl_IncrRefCount(objv[i+1]); /* Since hash now holds ref to it */ } + } else { + int length; + const char *nextElem = TclGetStringFromObj(objPtr, &length); + const char *limit = (nextElem + length); - /* - * Allocate a Tcl object for the element and initialize it from the - * "elemSize" bytes starting at "elemStart". - */ + while (nextElem < limit) { + Tcl_Obj *keyPtr, *valuePtr; + const char *elemStart; + int elemSize, literal; - s = ckalloc((unsigned) elemSize + 1); - if (hasBrace) { - memcpy((VOID *) s, (VOID *) elemStart, (size_t) elemSize); - s[elemSize] = 0; - } else { - elemSize = TclCopyAndCollapse(elemSize, elemStart, s); - } - - TclNewObj(keyPtr); - keyPtr->bytes = s; - keyPtr->length = elemSize; - - p = nextElem; - lenRemain = (limit - nextElem); - if (lenRemain <= 0) { - goto missingKey; - } + result = TclFindElement(interp, nextElem, (limit - nextElem), + &elemStart, &nextElem, &elemSize, &literal); + if (result != TCL_OK) { + goto errorExit; + } + if (elemStart == limit) { + break; + } + if (nextElem == limit) { + goto missingValue; + } - result = TclFindElement(interp, p, lenRemain, - &elemStart, &nextElem, &elemSize, &hasBrace); - if (result != TCL_OK) { - TclDecrRefCount(keyPtr); - goto errorExit; - } - if (elemStart >= limit) { - goto missingKey; - } + if (literal) { + TclNewStringObj(keyPtr, elemStart, elemSize); + } else { + /* Avoid double copy */ + TclNewObj(keyPtr); + keyPtr->bytes = ckalloc((unsigned) elemSize + 1); + keyPtr->length = TclCopyAndCollapse(elemSize, elemStart, + keyPtr->bytes); + } - /* - * Allocate a Tcl object for the element and initialize it from the - * "elemSize" bytes starting at "elemStart". - */ + result = TclFindElement(interp, nextElem, (limit - nextElem), + &elemStart, &nextElem, &elemSize, &literal); + if (result != TCL_OK) { + TclDecrRefCount(keyPtr); + goto errorExit; + } - s = ckalloc((unsigned) elemSize + 1); - if (hasBrace) { - memcpy((VOID *) s, (VOID *) elemStart, (size_t) elemSize); - s[elemSize] = 0; - } else { - elemSize = TclCopyAndCollapse(elemSize, elemStart, s); - } + if (literal) { + TclNewStringObj(valuePtr, elemStart, elemSize); + } else { + /* Avoid double copy */ + TclNewObj(valuePtr); + valuePtr->bytes = ckalloc((unsigned) elemSize + 1); + valuePtr->length = TclCopyAndCollapse(elemSize, elemStart, + valuePtr->bytes); + } - TclNewObj(valuePtr); - valuePtr->bytes = s; - valuePtr->length = elemSize; + /* Store key and value in the hash table we're building. */ + hPtr = CreateChainEntry(dict, keyPtr, &isNew); + if (!isNew) { + Tcl_Obj *discardedValue = Tcl_GetHashValue(hPtr); - /* - * Store key and value in the hash table we're building. - */ - hPtr = Tcl_CreateHashEntry(&dict->table, (char *)keyPtr, &isNew); - if (!isNew) { - Tcl_Obj *discardedValue = (Tcl_Obj *) Tcl_GetHashValue(hPtr); - TclDecrRefCount(keyPtr); - TclDecrRefCount(discardedValue); + TclDecrRefCount(keyPtr); + TclDecrRefCount(discardedValue); + } + Tcl_SetHashValue(hPtr, valuePtr); + Tcl_IncrRefCount(valuePtr); /* since hash now holds ref to it */ } - Tcl_SetHashValue(hPtr, (ClientData) valuePtr); - Tcl_IncrRefCount(valuePtr); /* since hash now holds ref to it */ } - installHash: /* - * Free the old internalRep before setting the new one. We do this as - * late as possible to allow the conversion code, in particular + * 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. */ @@ -533,25 +713,24 @@ SetDictFromAny(interp, objPtr) dict->epoch = 0; dict->chain = NULL; dict->refcount = 1; - objPtr->internalRep.otherValuePtr = (VOID *) dict; + objPtr->internalRep.twoPtrValue.ptr1 = dict; objPtr->typePtr = &tclDictType; return TCL_OK; - missingKey: + missingValue: if (interp != NULL) { - Tcl_SetObjResult(interp, - Tcl_NewStringObj("missing value to go with key", -1)); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "missing value to go with key", -1)); + Tcl_SetErrorCode(interp, "TCL", "VALUE", "DICTIONARY", NULL); } - TclDecrRefCount(keyPtr); result = TCL_ERROR; - errorExit: - for (hPtr=Tcl_FirstHashEntry(&dict->table,&search); - hPtr!=NULL ; hPtr=Tcl_NextHashEntry(&search)) { - valuePtr = (Tcl_Obj *) Tcl_GetHashValue(hPtr); - TclDecrRefCount(valuePtr); + + errorExit: + if (interp != NULL) { + Tcl_SetErrorCode(interp, "TCL", "VALUE", "DICTIONARY", NULL); } - Tcl_DeleteHashTable(&dict->table); - ckfree((char *) dict); + DeleteChainTable(dict); + ckfree(dict); return result; } @@ -560,29 +739,27 @@ SetDictFromAny(interp, objPtr) * * TclTraceDictPath -- * - * Trace through a tree of dictionaries using the array of keys - * given. If the flags argument has the DICT_PATH_UPDATE flag is - * set, a backward-pointing chain of dictionaries is also built - * (in the Dict's chain field) and the chained dictionaries are - * made into unshared dictionaries (if they aren't already.) + * Trace through a tree of dictionaries using the array of keys given. If + * the flags argument has the DICT_PATH_UPDATE flag is set, a + * backward-pointing chain of dictionaries is also built (in the Dict's + * chain field) and the chained dictionaries are made into unshared + * dictionaries (if they aren't already.) * * Results: - * The object at the end of the path, or NULL if there was an - * error. Note that this it is an error for an intermediate - * dictionary on the path to not exist. If the flags argument - * has the DICT_PATH_EXISTS set, a non-existent path gives a - * DICT_PATH_NON_EXISTENT result. + * The object at the end of the path, or NULL if there was an error. Note + * that this it is an error for an intermediate dictionary on the path to + * not exist. If the flags argument has the DICT_PATH_EXISTS set, a + * non-existent path gives a DICT_PATH_NON_EXISTENT result. * * Side effects: - * If the flags argument is zero or DICT_PATH_EXISTS, there are - * no side effects (other than potential conversion of objects to - * dictionaries.) If the flags argument is DICT_PATH_UPDATE, the - * following additional side effects occur. Shared dictionaries - * along the path are converted into unshared objects, and a - * backward-pointing chain is built using the chain fields of the - * dictionaries (for easy invalidation of string representations - * using InvalidateDictChain.) If the flags argument has the - * DICT_PATH_CREATE bits set (and not the DICT_PATH_EXISTS bit), + * If the flags argument is zero or DICT_PATH_EXISTS, there are no side + * effects (other than potential conversion of objects to dictionaries.) + * If the flags argument is DICT_PATH_UPDATE, the following additional + * side effects occur. Shared dictionaries along the path are converted + * into unshared objects, and a backward-pointing chain is built using + * the chain fields of the dictionaries (for easy invalidation of string + * representations using InvalidateDictChain). If the flags argument has + * the DICT_PATH_CREATE bits set (and not the DICT_PATH_EXISTS bit), * non-existant keys will be inserted with a value of an empty * dictionary, resulting in the path being built. * @@ -590,10 +767,12 @@ SetDictFromAny(interp, objPtr) */ Tcl_Obj * -TclTraceDictPath(interp, dictPtr, keyc, keyv, flags) - Tcl_Interp *interp; - Tcl_Obj *dictPtr, *CONST keyv[]; - int keyc, flags; +TclTraceDictPath( + Tcl_Interp *interp, + Tcl_Obj *dictPtr, + int keyc, + Tcl_Obj *const keyv[], + int flags) { Dict *dict, *newDict; int i; @@ -603,25 +782,28 @@ TclTraceDictPath(interp, dictPtr, keyc, keyv, flags) return NULL; } } - dict = (Dict *) dictPtr->internalRep.otherValuePtr; + dict = dictPtr->internalRep.twoPtrValue.ptr1; if (flags & DICT_PATH_UPDATE) { dict->chain = NULL; } for (i=0 ; i<keyc ; i++) { - Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&dict->table, (char *)keyv[i]); + Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&dict->table, keyv[i]); Tcl_Obj *tmpObj; if (hPtr == NULL) { int isNew; /* Dummy */ + if (flags & DICT_PATH_EXISTS) { return DICT_PATH_NON_EXISTENT; } if ((flags & DICT_PATH_CREATE) != DICT_PATH_CREATE) { if (interp != NULL) { - Tcl_ResetResult(interp); - Tcl_AppendResult(interp, "key \"", TclGetString(keyv[i]), - "\" not known in dictionary", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "key \"%s\" not known in dictionary", + TclGetString(keyv[i]))); + Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "DICT", + TclGetString(keyv[i]), NULL); } return NULL; } @@ -629,12 +811,13 @@ TclTraceDictPath(interp, dictPtr, keyc, keyv, flags) /* * The next line should always set isNew to 1. */ - hPtr = Tcl_CreateHashEntry(&dict->table, (char *)keyv[i], &isNew); + + hPtr = CreateChainEntry(dict, keyv[i], &isNew); tmpObj = Tcl_NewDictObj(); Tcl_IncrRefCount(tmpObj); - Tcl_SetHashValue(hPtr, (ClientData) tmpObj); + Tcl_SetHashValue(hPtr, tmpObj); } else { - tmpObj = (Tcl_Obj *) Tcl_GetHashValue(hPtr); + tmpObj = Tcl_GetHashValue(hPtr); if (tmpObj->typePtr != &tclDictType) { if (SetDictFromAny(interp, tmpObj) != TCL_OK) { return NULL; @@ -642,15 +825,15 @@ TclTraceDictPath(interp, dictPtr, keyc, keyv, flags) } } - newDict = (Dict *) tmpObj->internalRep.otherValuePtr; + newDict = tmpObj->internalRep.twoPtrValue.ptr1; if (flags & DICT_PATH_UPDATE) { if (Tcl_IsShared(tmpObj)) { TclDecrRefCount(tmpObj); tmpObj = Tcl_DuplicateObj(tmpObj); Tcl_IncrRefCount(tmpObj); - Tcl_SetHashValue(hPtr, (ClientData) tmpObj); + Tcl_SetHashValue(hPtr, tmpObj); dict->epoch++; - newDict = (Dict *) tmpObj->internalRep.otherValuePtr; + newDict = tmpObj->internalRep.twoPtrValue.ptr1; } newDict->chain = dictPtr; @@ -666,36 +849,36 @@ TclTraceDictPath(interp, dictPtr, keyc, keyv, flags) * * InvalidateDictChain -- * - * Go through a dictionary chain (built by an updating invokation - * of TclTraceDictPath) and invalidate the string representations - * of all the dictionaries on the chain. + * Go through a dictionary chain (built by an updating invokation of + * TclTraceDictPath) and invalidate the string representations of all the + * dictionaries on the chain. * * Results: * None * * Side effects: - * String reps are invalidated and epoch counters (for detecting - * illegal concurrent modifications) are updated through the - * chain of updated dictionaries. + * String reps are invalidated and epoch counters (for detecting illegal + * concurrent modifications) are updated through the chain of updated + * dictionaries. * *---------------------------------------------------------------------- */ static void -InvalidateDictChain(dictObj) - Tcl_Obj *dictObj; +InvalidateDictChain( + Tcl_Obj *dictObj) { - Dict *dict = (Dict *) dictObj->internalRep.otherValuePtr; + Dict *dict = dictObj->internalRep.twoPtrValue.ptr1; do { - Tcl_InvalidateStringRep(dictObj); + TclInvalidateStringRep(dictObj); dict->epoch++; dictObj = dict->chain; if (dictObj == NULL) { break; } dict->chain = NULL; - dict = (Dict *) dictObj->internalRep.otherValuePtr; + dict = dictObj->internalRep.twoPtrValue.ptr1; } while (dict != NULL); } @@ -704,48 +887,52 @@ InvalidateDictChain(dictObj) * * Tcl_DictObjPut -- * - * Add a key,value pair to a dictionary, or update the value for a - * key if that key already has a mapping in the dictionary. + * Add a key,value pair to a dictionary, or update the value for a key if + * that key already has a mapping in the dictionary. * * Results: * A standard Tcl result. * * Side effects: - * The object pointed to by dictPtr is converted to a dictionary if - * it is not already one, and any string representation that it has - * is invalidated. + * The object pointed to by dictPtr is converted to a dictionary if it is + * not already one, and any string representation that it has is + * invalidated. * *---------------------------------------------------------------------- */ int -Tcl_DictObjPut(interp, dictPtr, keyPtr, valuePtr) - Tcl_Interp *interp; - Tcl_Obj *dictPtr, *keyPtr, *valuePtr; +Tcl_DictObjPut( + Tcl_Interp *interp, + Tcl_Obj *dictPtr, + Tcl_Obj *keyPtr, + Tcl_Obj *valuePtr) { Dict *dict; Tcl_HashEntry *hPtr; int isNew; if (Tcl_IsShared(dictPtr)) { - Tcl_Panic("Tcl_DictObjPut called with shared object"); + Tcl_Panic("%s called with shared object", "Tcl_DictObjPut"); } if (dictPtr->typePtr != &tclDictType) { int result = SetDictFromAny(interp, dictPtr); + if (result != TCL_OK) { return result; } } if (dictPtr->bytes != NULL) { - Tcl_InvalidateStringRep(dictPtr); + TclInvalidateStringRep(dictPtr); } - dict = (Dict *) dictPtr->internalRep.otherValuePtr; - hPtr = Tcl_CreateHashEntry(&dict->table, (char *)keyPtr, &isNew); + dict = dictPtr->internalRep.twoPtrValue.ptr1; + hPtr = CreateChainEntry(dict, keyPtr, &isNew); Tcl_IncrRefCount(valuePtr); if (!isNew) { - Tcl_Obj *oldValuePtr = (Tcl_Obj *) Tcl_GetHashValue(hPtr); + Tcl_Obj *oldValuePtr = Tcl_GetHashValue(hPtr); + TclDecrRefCount(oldValuePtr); } Tcl_SetHashValue(hPtr, valuePtr); @@ -758,25 +945,27 @@ Tcl_DictObjPut(interp, dictPtr, keyPtr, valuePtr) * * Tcl_DictObjGet -- * - * Given a key, get its value from the dictionary (or NULL if key - * is not found in dictionary.) + * Given a key, get its value from the dictionary (or NULL if key is not + * found in dictionary.) * * Results: - * A standard Tcl result. The variable pointed to by valuePtrPtr - * is updated with the value for the key. Note that it is not an - * error for the key to have no mapping in the dictionary. + * A standard Tcl result. The variable pointed to by valuePtrPtr is + * updated with the value for the key. Note that it is not an error for + * the key to have no mapping in the dictionary. * * Side effects: - * The object pointed to by dictPtr is converted to a dictionary if - * it is not already one. + * The object pointed to by dictPtr is converted to a dictionary if it is + * not already one. * *---------------------------------------------------------------------- */ int -Tcl_DictObjGet(interp, dictPtr, keyPtr, valuePtrPtr) - Tcl_Interp *interp; - Tcl_Obj *dictPtr, *keyPtr, **valuePtrPtr; +Tcl_DictObjGet( + Tcl_Interp *interp, + Tcl_Obj *dictPtr, + Tcl_Obj *keyPtr, + Tcl_Obj **valuePtrPtr) { Dict *dict; Tcl_HashEntry *hPtr; @@ -784,16 +973,17 @@ Tcl_DictObjGet(interp, dictPtr, keyPtr, valuePtrPtr) if (dictPtr->typePtr != &tclDictType) { int result = SetDictFromAny(interp, dictPtr); if (result != TCL_OK) { + *valuePtrPtr = NULL; return result; } } - dict = (Dict *) dictPtr->internalRep.otherValuePtr; - hPtr = Tcl_FindHashEntry(&dict->table, (char *)keyPtr); + dict = dictPtr->internalRep.twoPtrValue.ptr1; + hPtr = Tcl_FindHashEntry(&dict->table, keyPtr); if (hPtr == NULL) { *valuePtrPtr = NULL; } else { - *valuePtrPtr = (Tcl_Obj *) Tcl_GetHashValue(hPtr); + *valuePtrPtr = Tcl_GetHashValue(hPtr); } return TCL_OK; } @@ -803,30 +993,30 @@ Tcl_DictObjGet(interp, dictPtr, keyPtr, valuePtrPtr) * * Tcl_DictObjRemove -- * - * Remove the key,value pair with the given key from the dictionary; - * the key does not need to be present in the dictionary. + * Remove the key,value pair with the given key from the dictionary; the + * key does not need to be present in the dictionary. * * Results: * A standard Tcl result. * * Side effects: - * The object pointed to by dictPtr is converted to a dictionary if - * it is not already one, and any string representation that it has - * is invalidated. + * The object pointed to by dictPtr is converted to a dictionary if it is + * not already one, and any string representation that it has is + * invalidated. * *---------------------------------------------------------------------- */ int -Tcl_DictObjRemove(interp, dictPtr, keyPtr) - Tcl_Interp *interp; - Tcl_Obj *dictPtr, *keyPtr; +Tcl_DictObjRemove( + Tcl_Interp *interp, + Tcl_Obj *dictPtr, + Tcl_Obj *keyPtr) { Dict *dict; - Tcl_HashEntry *hPtr; if (Tcl_IsShared(dictPtr)) { - Tcl_Panic("Tcl_DictObjRemove called with shared object"); + Tcl_Panic("%s called with shared object", "Tcl_DictObjRemove"); } if (dictPtr->typePtr != &tclDictType) { @@ -837,15 +1027,10 @@ Tcl_DictObjRemove(interp, dictPtr, keyPtr) } if (dictPtr->bytes != NULL) { - Tcl_InvalidateStringRep(dictPtr); + TclInvalidateStringRep(dictPtr); } - dict = (Dict *) dictPtr->internalRep.otherValuePtr; - hPtr = Tcl_FindHashEntry(&dict->table, (char *)keyPtr); - if (hPtr != NULL) { - Tcl_Obj *valuePtr = (Tcl_Obj *) Tcl_GetHashValue(hPtr); - - TclDecrRefCount(valuePtr); - Tcl_DeleteHashEntry(hPtr); + dict = dictPtr->internalRep.twoPtrValue.ptr1; + if (DeleteChainEntry(dict, keyPtr)) { dict->epoch++; } return TCL_OK; @@ -859,21 +1044,21 @@ Tcl_DictObjRemove(interp, dictPtr, keyPtr) * How many key,value pairs are there in the dictionary? * * Results: - * A standard Tcl result. Updates the variable pointed to by - * sizePtr with the number of key,value pairs in the dictionary. + * A standard Tcl result. Updates the variable pointed to by sizePtr with + * the number of key,value pairs in the dictionary. * * Side effects: - * The dictPtr object is converted to a dictionary type if it is - * not a dictionary already. + * The dictPtr object is converted to a dictionary type if it is not a + * dictionary already. * *---------------------------------------------------------------------- */ int -Tcl_DictObjSize(interp, dictPtr, sizePtr) - Tcl_Interp *interp; - Tcl_Obj *dictPtr; - int *sizePtr; +Tcl_DictObjSize( + Tcl_Interp *interp, + Tcl_Obj *dictPtr, + int *sizePtr) { Dict *dict; @@ -884,7 +1069,7 @@ Tcl_DictObjSize(interp, dictPtr, sizePtr) } } - dict = (Dict *) dictPtr->internalRep.otherValuePtr; + dict = dictPtr->internalRep.twoPtrValue.ptr1; *sizePtr = dict->table.numEntries; return TCL_OK; } @@ -894,67 +1079,67 @@ Tcl_DictObjSize(interp, dictPtr, sizePtr) * * Tcl_DictObjFirst -- * - * Start a traversal of the dictionary. Caller must supply the - * search context, pointers for returning key and value, and a - * pointer to allow indication of whether the dictionary has been - * traversed (i.e. the dictionary is empty.) The order of traversal - * is undefined. + * Start a traversal of the dictionary. Caller must supply the search + * context, pointers for returning key and value, and a pointer to allow + * indication of whether the dictionary has been traversed (i.e. the + * dictionary is empty). The order of traversal is undefined. * * Results: - * A standard Tcl result. Updates the variables pointed to by - * keyPtrPtr, valuePtrPtr and donePtr. Either of keyPtrPtr and - * valuePtrPtr may be NULL, in which case the key/value is not made - * available to the caller. + * A standard Tcl result. Updates the variables pointed to by keyPtrPtr, + * valuePtrPtr and donePtr. Either of keyPtrPtr and valuePtrPtr may be + * NULL, in which case the key/value is not made available to the caller. * * Side effects: - * The dictPtr object is converted to a dictionary type if it is - * not a dictionary already. The search context is initialised if - * the search has not finished. The dictionary's internal rep is - * Tcl_Preserve()d if the dictionary has at least one element. + * The dictPtr object is converted to a dictionary type if it is not a + * dictionary already. The search context is initialised if the search + * has not finished. The dictionary's internal rep is Tcl_Preserve()d if + * the dictionary has at least one element. * *---------------------------------------------------------------------- */ int -Tcl_DictObjFirst(interp, dictPtr, searchPtr, keyPtrPtr, valuePtrPtr, donePtr) - Tcl_Interp *interp; /* For error messages, or NULL if no - * error messages desired. */ - Tcl_Obj *dictPtr; /* Dictionary to traverse. */ - Tcl_DictSearch *searchPtr; /* Pointer to a dict search context. */ - Tcl_Obj **keyPtrPtr; /* Pointer to a variable to have the - * first key written into, or NULL. */ - Tcl_Obj **valuePtrPtr; /* Pointer to a variable to have the - * first value written into, or NULL.*/ - int *donePtr; /* Pointer to a variable which will - * have a 1 written into when there - * are no further values in the - * dictionary, or a 0 otherwise. */ +Tcl_DictObjFirst( + Tcl_Interp *interp, /* For error messages, or NULL if no error + * messages desired. */ + Tcl_Obj *dictPtr, /* Dictionary to traverse. */ + Tcl_DictSearch *searchPtr, /* Pointer to a dict search context. */ + Tcl_Obj **keyPtrPtr, /* Pointer to a variable to have the first key + * written into, or NULL. */ + Tcl_Obj **valuePtrPtr, /* Pointer to a variable to have the first + * value written into, or NULL.*/ + int *donePtr) /* Pointer to a variable which will have a 1 + * written into when there are no further + * values in the dictionary, or a 0 + * otherwise. */ { Dict *dict; - Tcl_HashEntry *hPtr; + ChainEntry *cPtr; if (dictPtr->typePtr != &tclDictType) { int result = SetDictFromAny(interp, dictPtr); + if (result != TCL_OK) { return result; } } - dict = (Dict *) dictPtr->internalRep.otherValuePtr; - hPtr = Tcl_FirstHashEntry(&dict->table, &searchPtr->search); - if (hPtr == NULL) { + dict = dictPtr->internalRep.twoPtrValue.ptr1; + cPtr = dict->entryChainHead; + if (cPtr == NULL) { searchPtr->epoch = -1; *donePtr = 1; } else { *donePtr = 0; searchPtr->dictionaryPtr = (Tcl_Dict) dict; searchPtr->epoch = dict->epoch; + searchPtr->next = cPtr->nextPtr; dict->refcount++; if (keyPtrPtr != NULL) { - *keyPtrPtr = (Tcl_Obj *) Tcl_GetHashKey(&dict->table, hPtr); + *keyPtrPtr = Tcl_GetHashKey(&dict->table, &cPtr->entry); } if (valuePtrPtr != NULL) { - *valuePtrPtr = (Tcl_Obj *) Tcl_GetHashValue(hPtr); + *valuePtrPtr = Tcl_GetHashValue(&cPtr->entry); } } return TCL_OK; @@ -966,69 +1151,72 @@ Tcl_DictObjFirst(interp, dictPtr, searchPtr, keyPtrPtr, valuePtrPtr, donePtr) * Tcl_DictObjNext -- * * Continue a traversal of a dictionary previously started with - * Tcl_DictObjFirst. This function is safe against concurrent - * modification of the underlying object (including type - * shimmering), treating such situations as if the search has - * terminated, though it is up to the caller to ensure that the - * object itself is not disposed until the search has finished. - * It is _not_ safe against modifications from other threads. + * Tcl_DictObjFirst. This function is safe against concurrent + * modification of the underlying object (including type shimmering), + * treating such situations as if the search has terminated, though it is + * up to the caller to ensure that the object itself is not disposed + * until the search has finished. It is _not_ safe against modifications + * from other threads. * * Results: * Updates the variables pointed to by keyPtrPtr, valuePtrPtr and - * donePtr. Either of keyPtrPtr and valuePtrPtr may be NULL, in - * which case the key/value is not made available to the caller. + * donePtr. Either of keyPtrPtr and valuePtrPtr may be NULL, in which + * case the key/value is not made available to the caller. * * Side effects: - * Removes a reference to the dictionary's internal rep if the - * search terminates. + * Removes a reference to the dictionary's internal rep if the search + * terminates. * *---------------------------------------------------------------------- */ void -Tcl_DictObjNext(searchPtr, keyPtrPtr, valuePtrPtr, donePtr) - Tcl_DictSearch *searchPtr; /* Pointer to a hash search context. */ - Tcl_Obj **keyPtrPtr; /* Pointer to a variable to have the - * first key written into, or NULL. */ - Tcl_Obj **valuePtrPtr; /* Pointer to a variable to have the - * first value written into, or NULL.*/ - int *donePtr; /* Pointer to a variable which will - * have a 1 written into when there - * are no further values in the - * dictionary, or a 0 otherwise. */ +Tcl_DictObjNext( + Tcl_DictSearch *searchPtr, /* Pointer to a hash search context. */ + Tcl_Obj **keyPtrPtr, /* Pointer to a variable to have the first key + * written into, or NULL. */ + Tcl_Obj **valuePtrPtr, /* Pointer to a variable to have the first + * value written into, or NULL.*/ + int *donePtr) /* Pointer to a variable which will have a 1 + * written into when there are no further + * values in the dictionary, or a 0 + * otherwise. */ { - Tcl_HashEntry *hPtr; + ChainEntry *cPtr; /* * If the searh is done; we do no work. */ + if (searchPtr->epoch == -1) { *donePtr = 1; return; } /* - * Bail out if the dictionary has had any elements added, modified - * or removed. This *shouldn't* happen, but... + * Bail out if the dictionary has had any elements added, modified or + * removed. This *shouldn't* happen, but... */ + if (((Dict *)searchPtr->dictionaryPtr)->epoch != searchPtr->epoch) { Tcl_Panic("concurrent dictionary modification and search"); } - hPtr = Tcl_NextHashEntry(&searchPtr->search); - if (hPtr == NULL) { + cPtr = searchPtr->next; + if (cPtr == NULL) { Tcl_DictObjDone(searchPtr); *donePtr = 1; return; } + searchPtr->next = cPtr->nextPtr; *donePtr = 0; if (keyPtrPtr != NULL) { - *keyPtrPtr = (Tcl_Obj *) Tcl_GetHashKey( - &((Dict *)searchPtr->dictionaryPtr)->table, hPtr); + *keyPtrPtr = Tcl_GetHashKey( + &((Dict *)searchPtr->dictionaryPtr)->table, &cPtr->entry); } if (valuePtrPtr != NULL) { - *valuePtrPtr = (Tcl_Obj *) Tcl_GetHashValue(hPtr); + *valuePtrPtr = Tcl_GetHashValue(&cPtr->entry); } } @@ -1037,11 +1225,10 @@ Tcl_DictObjNext(searchPtr, keyPtrPtr, valuePtrPtr, donePtr) * * Tcl_DictObjDone -- * - * Call this if you want to stop a search before you reach the - * end of the dictionary (e.g. because of abnormal termination of - * the search.) It should not be used if the search reaches its - * natural end (i.e. if either Tcl_DictObjFirst or Tcl_DictObjNext - * sets its donePtr variable to 1.) + * Call this if you want to stop a search before you reach the end of the + * dictionary (e.g. because of abnormal termination of the search). It + * need not be used if the search reaches its natural end (i.e. if either + * Tcl_DictObjFirst or Tcl_DictObjNext sets its donePtr variable to 1). * * Results: * None. @@ -1053,8 +1240,8 @@ Tcl_DictObjNext(searchPtr, keyPtrPtr, valuePtrPtr, donePtr) */ void -Tcl_DictObjDone(searchPtr) - Tcl_DictSearch *searchPtr; /* Pointer to a hash search context. */ +Tcl_DictObjDone( + Tcl_DictSearch *searchPtr) /* Pointer to a hash search context. */ { Dict *dict; @@ -1073,36 +1260,38 @@ Tcl_DictObjDone(searchPtr) * * Tcl_DictObjPutKeyList -- * - * Add a key...key,value pair to a dictionary tree. The main - * dictionary value must not be shared, though sub-dictionaries may - * be. All intermediate dictionaries on the path must exist. + * Add a key...key,value pair to a dictionary tree. The main dictionary + * value must not be shared, though sub-dictionaries may be. All + * intermediate dictionaries on the path must exist. * * Results: - * A standard Tcl result. Note that in the error case, a message - * is left in interp unless that is NULL. + * A standard Tcl result. Note that in the error case, a message is left + * in interp unless that is NULL. * * Side effects: - * If the dictionary and any of its sub-dictionaries on the - * path have string representations, these are invalidated. + * If the dictionary and any of its sub-dictionaries on the path have + * string representations, these are invalidated. * *---------------------------------------------------------------------- */ int -Tcl_DictObjPutKeyList(interp, dictPtr, keyc, keyv, valuePtr) - Tcl_Interp *interp; - int keyc; - Tcl_Obj *dictPtr, *CONST keyv[], *valuePtr; +Tcl_DictObjPutKeyList( + Tcl_Interp *interp, + Tcl_Obj *dictPtr, + int keyc, + Tcl_Obj *const keyv[], + Tcl_Obj *valuePtr) { Dict *dict; Tcl_HashEntry *hPtr; int isNew; if (Tcl_IsShared(dictPtr)) { - Tcl_Panic("Tcl_DictObjPutKeyList called with shared object"); + Tcl_Panic("%s called with shared object", "Tcl_DictObjPutKeyList"); } if (keyc < 1) { - Tcl_Panic("Tcl_DictObjPutKeyList called with empty key list"); + Tcl_Panic("%s called with empty key list", "Tcl_DictObjPutKeyList"); } dictPtr = TclTraceDictPath(interp, dictPtr, keyc-1,keyv, DICT_PATH_CREATE); @@ -1110,11 +1299,11 @@ Tcl_DictObjPutKeyList(interp, dictPtr, keyc, keyv, valuePtr) return TCL_ERROR; } - dict = (Dict *) dictPtr->internalRep.otherValuePtr; - hPtr = Tcl_CreateHashEntry(&dict->table, (char *)keyv[keyc-1], &isNew); + dict = dictPtr->internalRep.twoPtrValue.ptr1; + hPtr = CreateChainEntry(dict, keyv[keyc-1], &isNew); Tcl_IncrRefCount(valuePtr); if (!isNew) { - Tcl_Obj *oldValuePtr = (Tcl_Obj *) Tcl_GetHashValue(hPtr); + Tcl_Obj *oldValuePtr = Tcl_GetHashValue(hPtr); TclDecrRefCount(oldValuePtr); } Tcl_SetHashValue(hPtr, valuePtr); @@ -1129,36 +1318,36 @@ Tcl_DictObjPutKeyList(interp, dictPtr, keyc, keyv, valuePtr) * Tcl_DictObjRemoveKeyList -- * * Remove a key...key,value pair from a dictionary tree (the value - * removed is implicit in the key path.) The main dictionary value - * must not be shared, though sub-dictionaries may be. It is not - * an error if there is no value associated with the given key list, - * but all intermediate dictionaries on the key path must exist. + * removed is implicit in the key path). The main dictionary value must + * not be shared, though sub-dictionaries may be. It is not an error if + * there is no value associated with the given key list, but all + * intermediate dictionaries on the key path must exist. * * Results: - * A standard Tcl result. Note that in the error case, a message - * is left in interp unless that is NULL. + * A standard Tcl result. Note that in the error case, a message is left + * in interp unless that is NULL. * * Side effects: - * If the dictionary and any of its sub-dictionaries on the key - * path have string representations, these are invalidated. + * If the dictionary and any of its sub-dictionaries on the key path have + * string representations, these are invalidated. * *---------------------------------------------------------------------- */ int -Tcl_DictObjRemoveKeyList(interp, dictPtr, keyc, keyv) - Tcl_Interp *interp; - int keyc; - Tcl_Obj *dictPtr, *CONST keyv[]; +Tcl_DictObjRemoveKeyList( + Tcl_Interp *interp, + Tcl_Obj *dictPtr, + int keyc, + Tcl_Obj *const keyv[]) { Dict *dict; - Tcl_HashEntry *hPtr; if (Tcl_IsShared(dictPtr)) { - Tcl_Panic("Tcl_DictObjRemoveKeyList called with shared object"); + Tcl_Panic("%s called with shared object", "Tcl_DictObjRemoveKeyList"); } if (keyc < 1) { - Tcl_Panic("Tcl_DictObjRemoveKeyList called with empty key list"); + Tcl_Panic("%s called with empty key list", "Tcl_DictObjRemoveKeyList"); } dictPtr = TclTraceDictPath(interp, dictPtr, keyc-1,keyv, DICT_PATH_UPDATE); @@ -1166,13 +1355,8 @@ Tcl_DictObjRemoveKeyList(interp, dictPtr, keyc, keyv) return TCL_ERROR; } - dict = (Dict *) dictPtr->internalRep.otherValuePtr; - hPtr = Tcl_FindHashEntry(&dict->table, (char *)keyv[keyc-1]); - if (hPtr != NULL) { - Tcl_Obj *oldValuePtr = (Tcl_Obj *) Tcl_GetHashValue(hPtr); - TclDecrRefCount(oldValuePtr); - Tcl_DeleteHashEntry(hPtr); - } + dict = dictPtr->internalRep.twoPtrValue.ptr1; + DeleteChainEntry(dict, keyv[keyc-1]); InvalidateDictChain(dictPtr); return TCL_OK; } @@ -1182,17 +1366,17 @@ Tcl_DictObjRemoveKeyList(interp, dictPtr, keyc, keyv) * * Tcl_NewDictObj -- * - * This procedure is normally called when not debugging: i.e., when - * TCL_MEM_DEBUG is not defined. It creates a new dict object - * without any content. + * This function is normally called when not debugging: i.e., when + * TCL_MEM_DEBUG is not defined. It creates a new dict object without any + * content. * - * When TCL_MEM_DEBUG is defined, this procedure just returns the - * result of calling the debugging version Tcl_DbNewDictObj. + * When TCL_MEM_DEBUG is defined, this function just returns the result + * of calling the debugging version Tcl_DbNewDictObj. * * Results: - * A new dict object is returned; it has no keys defined in it. - * The new object's string representation is left NULL, and the - * ref count of the object is 0. + * A new dict object is returned; it has no keys defined in it. The new + * object's string representation is left NULL, and the ref count of the + * object is 0. * * Side Effects: * None. @@ -1201,22 +1385,23 @@ Tcl_DictObjRemoveKeyList(interp, dictPtr, keyc, keyv) */ Tcl_Obj * -Tcl_NewDictObj() +Tcl_NewDictObj(void) { #ifdef TCL_MEM_DEBUG return Tcl_DbNewDictObj("unknown", 0); #else /* !TCL_MEM_DEBUG */ + Tcl_Obj *dictPtr; Dict *dict; TclNewObj(dictPtr); - Tcl_InvalidateStringRep(dictPtr); - dict = (Dict *) ckalloc(sizeof(Dict)); - Tcl_InitObjHashTable(&dict->table); + TclInvalidateStringRep(dictPtr); + dict = ckalloc(sizeof(Dict)); + InitChainTable(dict); dict->epoch = 0; dict->chain = NULL; dict->refcount = 1; - dictPtr->internalRep.otherValuePtr = (VOID *) dict; + dictPtr->internalRep.twoPtrValue.ptr1 = dict; dictPtr->typePtr = &tclDictType; return dictPtr; #endif @@ -1227,21 +1412,21 @@ Tcl_NewDictObj() * * Tcl_DbNewDictObj -- * - * This procedure is normally called when debugging: i.e., when - * TCL_MEM_DEBUG is defined. It creates new dict objects. It is the - * same as the Tcl_NewDictObj procedure above except that it calls + * This function is normally called when debugging: i.e., when + * TCL_MEM_DEBUG is defined. It creates new dict objects. It is the same + * as the Tcl_NewDictObj 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 + * 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_NewDictObj. * * Results: - * A new dict object is returned; it has no keys defined in it. - * The new object's string representation is left NULL, and the - * ref count of the object is 0. + * A new dict object is returned; it has no keys defined in it. The new + * object's string representation is left NULL, and the ref count of the + * object is 0. * * Side Effects: * None. @@ -1250,22 +1435,22 @@ Tcl_NewDictObj() */ Tcl_Obj * -Tcl_DbNewDictObj(file, line) - CONST char *file; - int line; +Tcl_DbNewDictObj( + const char *file, + int line) { #ifdef TCL_MEM_DEBUG Tcl_Obj *dictPtr; Dict *dict; TclDbNewObj(dictPtr, file, line); - Tcl_InvalidateStringRep(dictPtr); - dict = (Dict *) ckalloc(sizeof(Dict)); - Tcl_InitObjHashTable(&dict->table); + TclInvalidateStringRep(dictPtr); + dict = ckalloc(sizeof(Dict)); + InitChainTable(dict); dict->epoch = 0; dict->chain = NULL; dict->refcount = 1; - dictPtr->internalRep.otherValuePtr = (VOID *) dict; + dictPtr->internalRep.twoPtrValue.ptr1 = dict; dictPtr->typePtr = &tclDictType; return dictPtr; #else /* !TCL_MEM_DEBUG */ @@ -1280,9 +1465,9 @@ Tcl_DbNewDictObj(file, line) * * DictCreateCmd -- * - * This function implements the "dict create" Tcl command. - * See the user documentation for details on what it does, and - * TIP#111 for the formal specification. + * This function implements the "dict create" Tcl command. See the user + * documentation for details on what it does, and TIP#111 for the formal + * specification. * * Results: * A standard Tcl result. @@ -1294,26 +1479,28 @@ Tcl_DbNewDictObj(file, line) */ static int -DictCreateCmd(interp, objc, objv) - Tcl_Interp *interp; - int objc; - Tcl_Obj *CONST *objv; +DictCreateCmd( + ClientData dummy, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const *objv) { Tcl_Obj *dictObj; int i; /* - * Must have an even number of arguments; note that number of - * preceding arguments (i.e. "dict create" is also even, which - * makes this much easier.) + * Must have an even number of arguments; note that number of preceding + * arguments (i.e. "dict create" is also even, which makes this much + * easier.) */ - if (objc & 1) { - Tcl_WrongNumArgs(interp, 2, objv, "?key value ...?"); + + if ((objc & 1) == 0) { + Tcl_WrongNumArgs(interp, 1, objv, "?key value ...?"); return TCL_ERROR; } dictObj = Tcl_NewDictObj(); - for (i=2 ; i<objc ; i+=2) { + for (i=1 ; i<objc ; i+=2) { /* * The next command is assumed to never fail... */ @@ -1328,9 +1515,9 @@ DictCreateCmd(interp, objc, objv) * * DictGetCmd -- * - * This function implements the "dict get" Tcl command. - * See the user documentation for details on what it does, and - * TIP#111 for the formal specification. + * This function implements the "dict get" Tcl command. See the user + * documentation for details on what it does, and TIP#111 for the formal + * specification. * * Results: * A standard Tcl result. @@ -1342,31 +1529,32 @@ DictCreateCmd(interp, objc, objv) */ static int -DictGetCmd(interp, objc, objv) - Tcl_Interp *interp; - int objc; - Tcl_Obj *CONST *objv; +DictGetCmd( + ClientData dummy, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const *objv) { Tcl_Obj *dictPtr, *valuePtr = NULL; int result; - if (objc < 3) { - Tcl_WrongNumArgs(interp, 2, objv, "dictionary ?key key ...?"); + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "dictionary ?key ...?"); return TCL_ERROR; } /* - * Test for the special case of no keys, which returns a *list* of - * all key,value pairs. We produce a copy here because that makes - * subsequent list handling more efficient. + * Test for the special case of no keys, which returns a *list* of all + * key,value pairs. We produce a copy here because that makes subsequent + * list handling more efficient. */ - if (objc == 3) { - Tcl_Obj *keyPtr, *listPtr; + if (objc == 2) { + Tcl_Obj *keyPtr = NULL, *listPtr; Tcl_DictSearch search; int done; - result = Tcl_DictObjFirst(interp, objv[2], &search, + result = Tcl_DictObjFirst(interp, objv[1], &search, &keyPtr, &valuePtr, &done); if (result != TCL_OK) { return result; @@ -1374,8 +1562,8 @@ DictGetCmd(interp, objc, objv) listPtr = Tcl_NewListObj(0, NULL); while (!done) { /* - * Assume these won't fail as we have complete control - * over the types of things here. + * Assume these won't fail as we have complete control over the + * types of things here. */ Tcl_ListObjAppendElement(interp, listPtr, keyPtr); @@ -1388,15 +1576,14 @@ DictGetCmd(interp, objc, objv) } /* - * Loop through the list of keys, looking up the key at the - * current index in the current dictionary each time. Once we've - * done the lookup, we set the current dictionary to be the value - * we looked up (in case the value was not the last one and we are - * going through a chain of searches.) Note that this loop always - * executes at least once. + * Loop through the list of keys, looking up the key at the current index + * in the current dictionary each time. Once we've done the lookup, we set + * the current dictionary to be the value we looked up (in case the value + * was not the last one and we are going through a chain of searches.) + * Note that this loop always executes at least once. */ - dictPtr = TclTraceDictPath(interp, objv[2], objc-4,objv+3, DICT_PATH_READ); + dictPtr = TclTraceDictPath(interp, objv[1], objc-3,objv+2, DICT_PATH_READ); if (dictPtr == NULL) { return TCL_ERROR; } @@ -1405,9 +1592,11 @@ DictGetCmd(interp, objc, objv) return result; } if (valuePtr == NULL) { - Tcl_ResetResult(interp); - Tcl_AppendResult(interp, "key \"", TclGetString(objv[objc-1]), - "\" not known in dictionary", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "key \"%s\" not known in dictionary", + TclGetString(objv[objc-1]))); + Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "DICT", + TclGetString(objv[objc-1]), NULL); return TCL_ERROR; } Tcl_SetObjResult(interp, valuePtr); @@ -1419,9 +1608,9 @@ DictGetCmd(interp, objc, objv) * * DictReplaceCmd -- * - * This function implements the "dict replace" Tcl command. - * See the user documentation for details on what it does, and - * TIP#111 for the formal specification. + * This function implements the "dict replace" Tcl command. See the user + * documentation for details on what it does, and TIP#111 for the formal + * specification. * * Results: * A standard Tcl result. @@ -1433,26 +1622,27 @@ DictGetCmd(interp, objc, objv) */ static int -DictReplaceCmd(interp, objc, objv) - Tcl_Interp *interp; - int objc; - Tcl_Obj *CONST *objv; +DictReplaceCmd( + ClientData dummy, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const *objv) { Tcl_Obj *dictPtr; int i, result; int allocatedDict = 0; - if ((objc < 3) || !(objc & 1)) { - Tcl_WrongNumArgs(interp, 2, objv, "dictionary ?key value ...?"); + if ((objc < 2) || (objc & 1)) { + Tcl_WrongNumArgs(interp, 1, objv, "dictionary ?key value ...?"); return TCL_ERROR; } - dictPtr = objv[2]; + dictPtr = objv[1]; if (Tcl_IsShared(dictPtr)) { dictPtr = Tcl_DuplicateObj(dictPtr); allocatedDict = 1; } - for (i=3 ; i<objc ; i+=2) { + for (i=2 ; i<objc ; i+=2) { result = Tcl_DictObjPut(interp, dictPtr, objv[i], objv[i+1]); if (result != TCL_OK) { if (allocatedDict) { @@ -1470,9 +1660,9 @@ DictReplaceCmd(interp, objc, objv) * * DictRemoveCmd -- * - * This function implements the "dict remove" Tcl command. - * See the user documentation for details on what it does, and - * TIP#111 for the formal specification. + * This function implements the "dict remove" Tcl command. See the user + * documentation for details on what it does, and TIP#111 for the formal + * specification. * * Results: * A standard Tcl result. @@ -1484,26 +1674,27 @@ DictReplaceCmd(interp, objc, objv) */ static int -DictRemoveCmd(interp, objc, objv) - Tcl_Interp *interp; - int objc; - Tcl_Obj *CONST *objv; +DictRemoveCmd( + ClientData dummy, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const *objv) { Tcl_Obj *dictPtr; int i, result; int allocatedDict = 0; - if (objc < 3) { - Tcl_WrongNumArgs(interp, 2, objv, "dictionary ?key ...?"); + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "dictionary ?key ...?"); return TCL_ERROR; } - dictPtr = objv[2]; + dictPtr = objv[1]; if (Tcl_IsShared(dictPtr)) { dictPtr = Tcl_DuplicateObj(dictPtr); allocatedDict = 1; } - for (i=3 ; i<objc ; i++) { + for (i=2 ; i<objc ; i++) { result = Tcl_DictObjRemove(interp, dictPtr, objv[i]); if (result != TCL_OK) { if (allocatedDict) { @@ -1521,9 +1712,9 @@ DictRemoveCmd(interp, objc, objv) * * DictMergeCmd -- * - * This function implements the "dict merge" Tcl command. - * See the user documentation for details on what it does, and - * TIP#163 for the formal specification. + * This function implements the "dict merge" Tcl command. See the user + * documentation for details on what it does, and TIP#163 for the formal + * specification. * * Results: * A standard Tcl result. @@ -1535,34 +1726,42 @@ DictRemoveCmd(interp, objc, objv) */ static int -DictMergeCmd(interp, objc, objv) - Tcl_Interp *interp; - int objc; - Tcl_Obj *CONST *objv; +DictMergeCmd( + ClientData dummy, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const *objv) { - Tcl_Obj *targetObj, *keyObj, *valueObj; + Tcl_Obj *targetObj, *keyObj = NULL, *valueObj = NULL; int allocatedDict = 0; int i, done; Tcl_DictSearch search; - if (objc == 2) { + if (objc == 1) { /* * No dictionary arguments; return default (empty value). */ + return TCL_OK; } - if (objc == 3) { + /* + * Make sure first argument is a dictionary. + */ + + targetObj = objv[1]; + if (targetObj->typePtr != &tclDictType) { + if (SetDictFromAny(interp, targetObj) != TCL_OK) { + return TCL_ERROR; + } + } + + if (objc == 2) { /* - * Single argument, make sure it is a dictionary, but - * otherwise return it. + * Single argument, return it. */ - if (objv[2]->typePtr != &tclDictType) { - if (SetDictFromAny(interp, objv[2]) != TCL_OK) { - return TCL_ERROR; - } - } - Tcl_SetObjResult(interp, objv[2]); + + Tcl_SetObjResult(interp, objv[1]); return TCL_OK; } @@ -1570,12 +1769,11 @@ DictMergeCmd(interp, objc, objv) * Normal behaviour: combining two (or more) dictionaries. */ - targetObj = objv[2]; if (Tcl_IsShared(targetObj)) { targetObj = Tcl_DuplicateObj(targetObj); allocatedDict = 1; } - for (i=3 ; i<objc ; i++) { + for (i=2 ; i<objc ; i++) { if (Tcl_DictObjFirst(interp, objv[i], &search, &keyObj, &valueObj, &done) != TCL_OK) { if (allocatedDict) { @@ -1584,16 +1782,15 @@ DictMergeCmd(interp, objc, objv) return TCL_ERROR; } while (!done) { - if (Tcl_DictObjPut(interp, targetObj, - keyObj, valueObj) != TCL_OK) { - Tcl_DictObjDone(&search); - if (allocatedDict) { - TclDecrRefCount(targetObj); - } - return TCL_ERROR; - } + /* + * Next line can't fail; already know we have a dictionary in + * targetObj. + */ + + Tcl_DictObjPut(NULL, targetObj, keyObj, valueObj); Tcl_DictObjNext(&search, &keyObj, &valueObj, &done); } + Tcl_DictObjDone(&search); } Tcl_SetObjResult(interp, targetObj); return TCL_OK; @@ -1604,9 +1801,9 @@ DictMergeCmd(interp, objc, objv) * * DictKeysCmd -- * - * This function implements the "dict keys" Tcl command. - * See the user documentation for details on what it does, and - * TIP#111 for the formal specification. + * This function implements the "dict keys" Tcl command. See the user + * documentation for details on what it does, and TIP#111 for the formal + * specification. * * Results: * A standard Tcl result. @@ -1618,46 +1815,66 @@ DictMergeCmd(interp, objc, objv) */ static int -DictKeysCmd(interp, objc, objv) - Tcl_Interp *interp; - int objc; - Tcl_Obj *CONST *objv; +DictKeysCmd( + ClientData dummy, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const *objv) { - Tcl_Obj *keyPtr, *listPtr; - Tcl_DictSearch search; - int result, done; - char *pattern = NULL; + Tcl_Obj *listPtr; + const char *pattern = NULL; - if (objc!=3 && objc!=4) { - Tcl_WrongNumArgs(interp, 2, objv, "dictionary ?pattern?"); + if (objc!=2 && objc!=3) { + Tcl_WrongNumArgs(interp, 1, objv, "dictionary ?pattern?"); return TCL_ERROR; } - result = Tcl_DictObjFirst(interp, objv[2], &search, &keyPtr, NULL, &done); - if (result != TCL_OK) { - return TCL_ERROR; + /* + * A direct check that we have a dictionary. We don't start the iteration + * yet because that might allocate memory or set locks that we do not + * need. [Bug 1705778, leak K04] + */ + + if (objv[1]->typePtr != &tclDictType) { + int result = SetDictFromAny(interp, objv[1]); + + if (result != TCL_OK) { + return result; + } } - if (objc == 4) { - pattern = TclGetString(objv[3]); + + if (objc == 3) { + pattern = TclGetString(objv[2]); } listPtr = Tcl_NewListObj(0, NULL); if ((pattern != NULL) && TclMatchIsTrivial(pattern)) { Tcl_Obj *valuePtr = NULL; - Tcl_DictObjGet(interp, objv[2], objv[3], &valuePtr); + + Tcl_DictObjGet(interp, objv[1], objv[2], &valuePtr); if (valuePtr != NULL) { - Tcl_ListObjAppendElement(interp, listPtr, objv[3]); + Tcl_ListObjAppendElement(NULL, listPtr, objv[2]); } - goto searchDone; - } - for (; !done ; Tcl_DictObjNext(&search, &keyPtr, NULL, &done)) { - if (pattern==NULL || Tcl_StringMatch(TclGetString(keyPtr), pattern)) { - /* - * Assume this operation always succeeds. - */ - Tcl_ListObjAppendElement(interp, listPtr, keyPtr); + } else { + Tcl_DictSearch search; + Tcl_Obj *keyPtr = NULL; + int done = 0; + + /* + * At this point, we know we have a dictionary (or at least something + * that can be represented; it could theoretically have shimmered away + * when the pattern was fetched, but that shouldn't be damaging) so we + * can start the iteration process without checking for failures. + */ + + Tcl_DictObjFirst(NULL, objv[1], &search, &keyPtr, NULL, &done); + for (; !done ; Tcl_DictObjNext(&search, &keyPtr, NULL, &done)) { + if (!pattern || Tcl_StringMatch(TclGetString(keyPtr), pattern)) { + Tcl_ListObjAppendElement(NULL, listPtr, keyPtr); + } } + Tcl_DictObjDone(&search); } -searchDone: + Tcl_SetObjResult(interp, listPtr); return TCL_OK; } @@ -1667,9 +1884,9 @@ searchDone: * * DictValuesCmd -- * - * This function implements the "dict values" Tcl command. - * See the user documentation for details on what it does, and - * TIP#111 for the formal specification. + * This function implements the "dict values" Tcl command. See the user + * documentation for details on what it does, and TIP#111 for the formal + * specification. * * Results: * A standard Tcl result. @@ -1681,27 +1898,30 @@ searchDone: */ static int -DictValuesCmd(interp, objc, objv) - Tcl_Interp *interp; - int objc; - Tcl_Obj *CONST *objv; +DictValuesCmd( + ClientData dummy, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const *objv) { - Tcl_Obj *valuePtr, *listPtr; + Tcl_Obj *valuePtr = NULL, *listPtr; Tcl_DictSearch search; - int result, done; - char *pattern = NULL; + int done; + const char *pattern; - if (objc!=3 && objc!=4) { - Tcl_WrongNumArgs(interp, 2, objv, "dictionary ?pattern?"); + if (objc!=2 && objc!=3) { + Tcl_WrongNumArgs(interp, 1, objv, "dictionary ?pattern?"); return TCL_ERROR; } - result= Tcl_DictObjFirst(interp, objv[2], &search, NULL, &valuePtr, &done); - if (result != TCL_OK) { + if (Tcl_DictObjFirst(interp, objv[1], &search, NULL, &valuePtr, + &done) != TCL_OK) { return TCL_ERROR; } - if (objc == 4) { - pattern = TclGetString(objv[3]); + if (objc == 3) { + pattern = TclGetString(objv[2]); + } else { + pattern = NULL; } listPtr = Tcl_NewListObj(0, NULL); for (; !done ; Tcl_DictObjNext(&search, NULL, &valuePtr, &done)) { @@ -1709,9 +1929,12 @@ DictValuesCmd(interp, objc, objv) /* * Assume this operation always succeeds. */ + Tcl_ListObjAppendElement(interp, listPtr, valuePtr); } } + Tcl_DictObjDone(&search); + Tcl_SetObjResult(interp, listPtr); return TCL_OK; } @@ -1721,9 +1944,9 @@ DictValuesCmd(interp, objc, objv) * * DictSizeCmd -- * - * This function implements the "dict size" Tcl command. - * See the user documentation for details on what it does, and - * TIP#111 for the formal specification. + * This function implements the "dict size" Tcl command. See the user + * documentation for details on what it does, and TIP#111 for the formal + * specification. * * Results: * A standard Tcl result. @@ -1735,18 +1958,19 @@ DictValuesCmd(interp, objc, objv) */ static int -DictSizeCmd(interp, objc, objv) - Tcl_Interp *interp; - int objc; - Tcl_Obj *CONST *objv; +DictSizeCmd( + ClientData dummy, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const *objv) { int result, size; - if (objc != 3) { - Tcl_WrongNumArgs(interp, 2, objv, "dictionary"); + if (objc != 2) { + Tcl_WrongNumArgs(interp, 1, objv, "dictionary"); return TCL_ERROR; } - result = Tcl_DictObjSize(interp, objv[2], &size); + result = Tcl_DictObjSize(interp, objv[1], &size); if (result == TCL_OK) { Tcl_SetObjResult(interp, Tcl_NewIntObj(size)); } @@ -1758,9 +1982,9 @@ DictSizeCmd(interp, objc, objv) * * DictExistsCmd -- * - * This function implements the "dict exists" Tcl command. - * See the user documentation for details on what it does, and - * TIP#111 for the formal specification. + * This function implements the "dict exists" Tcl command. See the user + * documentation for details on what it does, and TIP#111 for the formal + * specification. * * Results: * A standard Tcl result. @@ -1772,33 +1996,28 @@ DictSizeCmd(interp, objc, objv) */ static int -DictExistsCmd(interp, objc, objv) - Tcl_Interp *interp; - int objc; - Tcl_Obj *CONST *objv; +DictExistsCmd( + ClientData dummy, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const *objv) { Tcl_Obj *dictPtr, *valuePtr; - int result; - if (objc < 4) { - Tcl_WrongNumArgs(interp, 2, objv, "dictionary key ?key ...?"); + if (objc < 3) { + Tcl_WrongNumArgs(interp, 1, objv, "dictionary key ?key ...?"); return TCL_ERROR; } - dictPtr = TclTraceDictPath(interp, objv[2], objc-4, objv+3, + dictPtr = TclTraceDictPath(interp, objv[1], objc-3, objv+2, DICT_PATH_EXISTS); - if (dictPtr == NULL) { - return TCL_ERROR; - } - if (dictPtr == DICT_PATH_NON_EXISTENT) { + if (dictPtr == NULL || dictPtr == DICT_PATH_NON_EXISTENT + || Tcl_DictObjGet(interp, dictPtr, objv[objc-1], + &valuePtr) != TCL_OK) { Tcl_SetObjResult(interp, Tcl_NewBooleanObj(0)); - return TCL_OK; - } - result = Tcl_DictObjGet(interp, dictPtr, objv[objc-1], &valuePtr); - if (result != TCL_OK) { - return result; + } else { + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(valuePtr != NULL)); } - Tcl_SetObjResult(interp, Tcl_NewBooleanObj(valuePtr != NULL)); return TCL_OK; } @@ -1807,9 +2026,9 @@ DictExistsCmd(interp, objc, objv) * * DictInfoCmd -- * - * This function implements the "dict info" Tcl command. - * See the user documentation for details on what it does, and - * TIP#111 for the formal specification. + * This function implements the "dict info" Tcl command. See the user + * documentation for details on what it does, and TIP#111 for the formal + * specification. * * Results: * A standard Tcl result. @@ -1821,31 +2040,33 @@ DictExistsCmd(interp, objc, objv) */ static int -DictInfoCmd(interp, objc, objv) - Tcl_Interp *interp; - int objc; - Tcl_Obj *CONST *objv; +DictInfoCmd( + ClientData dummy, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const *objv) { Tcl_Obj *dictPtr; Dict *dict; + char *statsStr; - if (objc != 3) { - Tcl_WrongNumArgs(interp, 2, objv, "dictionary"); + if (objc != 2) { + Tcl_WrongNumArgs(interp, 1, objv, "dictionary"); return TCL_ERROR; } - dictPtr = objv[2]; + dictPtr = objv[1]; if (dictPtr->typePtr != &tclDictType) { int result = SetDictFromAny(interp, dictPtr); if (result != TCL_OK) { return result; } } - dict = (Dict *)dictPtr->internalRep.otherValuePtr; - /* - * This next cast is actually OK. - */ - Tcl_SetResult(interp, (char *)Tcl_HashStats(&dict->table), TCL_DYNAMIC); + dict = dictPtr->internalRep.twoPtrValue.ptr1; + + statsStr = Tcl_HashStats(&dict->table); + Tcl_SetObjResult(interp, Tcl_NewStringObj(statsStr, -1)); + ckfree(statsStr); return TCL_OK; } @@ -1854,9 +2075,9 @@ DictInfoCmd(interp, objc, objv) * * DictIncrCmd -- * - * This function implements the "dict incr" Tcl command. - * See the user documentation for details on what it does, and - * TIP#111 for the formal specification. + * This function implements the "dict incr" Tcl command. See the user + * documentation for details on what it does, and TIP#111 for the formal + * specification. * * Results: * A standard Tcl result. @@ -1868,178 +2089,106 @@ DictInfoCmd(interp, objc, objv) */ static int -DictIncrCmd(interp, objc, objv) - Tcl_Interp *interp; - int objc; - Tcl_Obj *CONST *objv; +DictIncrCmd( + ClientData dummy, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const *objv) { - Tcl_Obj *dictPtr, *valuePtr, *resultPtr; - int result, isWide = 0; - long incrValue = 1; - Tcl_WideInt wideIncrValue = 0; - int allocatedDict = 0; + int code = TCL_OK; + Tcl_Obj *dictPtr, *valuePtr = NULL; - if (objc < 4 || objc > 5) { - Tcl_WrongNumArgs(interp, 2, objv, "varName key ?increment?"); + if (objc < 3 || objc > 4) { + Tcl_WrongNumArgs(interp, 1, objv, "varName key ?increment?"); return TCL_ERROR; } - if (objc == 5) { - if (objv[4]->typePtr == &tclIntType) { - incrValue = objv[4]->internalRep.longValue; - } else if (objv[4]->typePtr == &tclWideIntType) { - wideIncrValue = objv[4]->internalRep.wideValue; - isWide = 1; - } else { - result = Tcl_GetWideIntFromObj(interp, objv[4], &wideIncrValue); - if (result != TCL_OK) { - return result; - } - if (wideIncrValue <= Tcl_LongAsWide(LONG_MAX) - && wideIncrValue >= Tcl_LongAsWide(LONG_MIN)) { - incrValue = Tcl_WideAsLong(wideIncrValue); - objv[4]->typePtr = &tclIntType; - } else { - isWide = 1; - } - } - } - - dictPtr = Tcl_ObjGetVar2(interp, objv[2], NULL, 0); + dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, 0); if (dictPtr == NULL) { - allocatedDict = 1; + /* + * Variable didn't yet exist. Create new dictionary value. + */ + dictPtr = Tcl_NewDictObj(); - if (isWide) { - valuePtr = Tcl_NewWideIntObj(wideIncrValue); + } else if (Tcl_DictObjGet(interp, dictPtr, objv[2], &valuePtr) != TCL_OK) { + /* + * Variable contents are not a dict, report error. + */ + + return TCL_ERROR; + } + if (Tcl_IsShared(dictPtr)) { + /* + * A little internals surgery to avoid copying a string rep that will + * soon be no good. + */ + + char *saved = dictPtr->bytes; + Tcl_Obj *oldPtr = dictPtr; + + dictPtr->bytes = NULL; + dictPtr = Tcl_DuplicateObj(dictPtr); + oldPtr->bytes = saved; + } + if (valuePtr == NULL) { + /* + * Key not in dictionary. Create new key with increment as value. + */ + + if (objc == 4) { + /* + * Verify increment is an integer. + */ + + mp_int increment; + + code = Tcl_GetBignumFromObj(interp, objv[3], &increment); + if (code != TCL_OK) { + Tcl_AddErrorInfo(interp, "\n (reading increment)"); + } else { + /* + * Remember to dispose with the bignum as we're not actually + * using it directly. [Bug 2874678] + */ + + mp_clear(&increment); + Tcl_DictObjPut(interp, dictPtr, objv[2], objv[3]); + } } else { - valuePtr = Tcl_NewLongObj(incrValue); + Tcl_DictObjPut(interp, dictPtr, objv[2], Tcl_NewIntObj(1)); } - Tcl_DictObjPut(interp, dictPtr, objv[3], valuePtr); } else { - long lValue; - Tcl_WideInt wValue; + /* + * Key in dictionary. Increment its value with minimum dup. + */ - if (Tcl_IsShared(dictPtr)) { - allocatedDict = 1; - dictPtr = Tcl_DuplicateObj(dictPtr); + if (Tcl_IsShared(valuePtr)) { + valuePtr = Tcl_DuplicateObj(valuePtr); + Tcl_DictObjPut(interp, dictPtr, objv[2], valuePtr); } + if (objc == 4) { + code = TclIncrObj(interp, valuePtr, objv[3]); + } else { + Tcl_Obj *incrPtr = Tcl_NewIntObj(1); - if (Tcl_DictObjGet(interp, dictPtr, objv[3], &valuePtr) != TCL_OK) { - if (allocatedDict) { - TclDecrRefCount(dictPtr); - } - return TCL_ERROR; + Tcl_IncrRefCount(incrPtr); + code = TclIncrObj(interp, valuePtr, incrPtr); + Tcl_DecrRefCount(incrPtr); } + } + if (code == TCL_OK) { + TclInvalidateStringRep(dictPtr); + valuePtr = Tcl_ObjSetVar2(interp, objv[1], NULL, + dictPtr, TCL_LEAVE_ERR_MSG); if (valuePtr == NULL) { - if (isWide) { - valuePtr = Tcl_NewWideIntObj(wideIncrValue); - } else { - valuePtr = Tcl_NewLongObj(incrValue); - } - } else if (valuePtr->typePtr == &tclWideIntType) { - Tcl_GetWideIntFromObj(NULL, valuePtr, &wValue); - if (Tcl_IsShared(valuePtr)) { - if (isWide) { - valuePtr = Tcl_NewWideIntObj(wValue + wideIncrValue); - } else { - valuePtr = Tcl_NewWideIntObj(wValue + incrValue); - } - } else { - if (isWide) { - Tcl_SetWideIntObj(valuePtr, wValue + wideIncrValue); - } else { - Tcl_SetWideIntObj(valuePtr, wValue + incrValue); - } - if (dictPtr->bytes != NULL) { - Tcl_InvalidateStringRep(dictPtr); - } - goto valueAlreadyInDictionary; - } - } else if (valuePtr->typePtr == &tclIntType) { - Tcl_GetLongFromObj(NULL, valuePtr, &lValue); - if (Tcl_IsShared(valuePtr)) { - if (isWide) { - valuePtr = Tcl_NewWideIntObj(lValue + wideIncrValue); - } else { - valuePtr = Tcl_NewLongObj(lValue + incrValue); - } - } else { - if (isWide) { - Tcl_SetWideIntObj(valuePtr, lValue + wideIncrValue); - } else { - Tcl_SetLongObj(valuePtr, lValue + incrValue); - } - if (dictPtr->bytes != NULL) { - Tcl_InvalidateStringRep(dictPtr); - } - goto valueAlreadyInDictionary; - } + code = TCL_ERROR; } else { - /* - * Note that these operations on wide ints should work - * fine where they are the same as normal longs, though - * the compiler might complain about trivially satisifed - * tests. - */ - result = Tcl_GetWideIntFromObj(interp, valuePtr, &wValue); - if (result != TCL_OK) { - if (allocatedDict) { - TclDecrRefCount(dictPtr); - } - return result; - } - /* - * Determine if we should have got a standard long instead. - */ - if (Tcl_IsShared(valuePtr)) { - if (isWide) { - valuePtr = Tcl_NewWideIntObj(wValue + wideIncrValue); - } else if (wValue >= LONG_MIN && wValue <= LONG_MAX) { - /* - * Convert the type... - */ - Tcl_GetLongFromObj(NULL, valuePtr, &lValue); - valuePtr = Tcl_NewLongObj(lValue + incrValue); - } else { - valuePtr = Tcl_NewWideIntObj(wValue + incrValue); - } - } else { - if (isWide) { - Tcl_SetWideIntObj(valuePtr, wValue + wideIncrValue); - } else if (wValue >= LONG_MIN && wValue <= LONG_MAX) { - Tcl_SetLongObj(valuePtr, - Tcl_WideAsLong(wValue) + incrValue); - } else { - Tcl_SetWideIntObj(valuePtr, wValue + incrValue); - } - if (dictPtr->bytes != NULL) { - Tcl_InvalidateStringRep(dictPtr); - } - goto valueAlreadyInDictionary; - } - } - if (Tcl_DictObjPut(interp, dictPtr, objv[3], valuePtr) != TCL_OK) { - /* - * This shouldn't happen since dictPtr is known - * from above to be a valid dictionary. - */ - if (allocatedDict) { - TclDecrRefCount(dictPtr); - } - TclDecrRefCount(valuePtr); - return TCL_ERROR; + Tcl_SetObjResult(interp, valuePtr); } + } else if (dictPtr->refCount == 0) { + Tcl_DecrRefCount(dictPtr); } - valueAlreadyInDictionary: - Tcl_IncrRefCount(dictPtr); - resultPtr = Tcl_ObjSetVar2(interp, objv[2], NULL, dictPtr, - TCL_LEAVE_ERR_MSG); - TclDecrRefCount(dictPtr); - if (resultPtr == NULL) { - return TCL_ERROR; - } - Tcl_SetObjResult(interp, resultPtr); - return TCL_OK; + return code; } /* @@ -2047,9 +2196,9 @@ DictIncrCmd(interp, objc, objv) * * DictLappendCmd -- * - * This function implements the "dict lappend" Tcl command. - * See the user documentation for details on what it does, and - * TIP#111 for the formal specification. + * This function implements the "dict lappend" Tcl command. See the user + * documentation for details on what it does, and TIP#111 for the formal + * specification. * * Results: * A standard Tcl result. @@ -2061,20 +2210,21 @@ DictIncrCmd(interp, objc, objv) */ static int -DictLappendCmd(interp, objc, objv) - Tcl_Interp *interp; - int objc; - Tcl_Obj *CONST *objv; +DictLappendCmd( + ClientData dummy, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const *objv) { Tcl_Obj *dictPtr, *valuePtr, *resultPtr; int i, allocatedDict = 0, allocatedValue = 0; - if (objc < 4) { - Tcl_WrongNumArgs(interp, 2, objv, "varName key ?value ...?"); + if (objc < 3) { + Tcl_WrongNumArgs(interp, 1, objv, "varName key ?value ...?"); return TCL_ERROR; } - dictPtr = Tcl_ObjGetVar2(interp, objv[2], NULL, 0); + dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, 0); if (dictPtr == NULL) { allocatedDict = 1; dictPtr = Tcl_NewDictObj(); @@ -2083,7 +2233,7 @@ DictLappendCmd(interp, objc, objv) dictPtr = Tcl_DuplicateObj(dictPtr); } - if (Tcl_DictObjGet(interp, dictPtr, objv[3], &valuePtr) != TCL_OK) { + if (Tcl_DictObjGet(interp, dictPtr, objv[2], &valuePtr) != TCL_OK) { if (allocatedDict) { TclDecrRefCount(dictPtr); } @@ -2091,7 +2241,7 @@ DictLappendCmd(interp, objc, objv) } if (valuePtr == NULL) { - valuePtr = Tcl_NewListObj(objc-4, objv+4); + valuePtr = Tcl_NewListObj(objc-3, objv+3); allocatedValue = 1; } else { if (Tcl_IsShared(valuePtr)) { @@ -2099,7 +2249,7 @@ DictLappendCmd(interp, objc, objv) valuePtr = Tcl_DuplicateObj(valuePtr); } - for (i=4 ; i<objc ; i++) { + for (i=3 ; i<objc ; i++) { if (Tcl_ListObjAppendElement(interp, valuePtr, objv[i]) != TCL_OK) { if (allocatedValue) { @@ -2114,15 +2264,13 @@ DictLappendCmd(interp, objc, objv) } if (allocatedValue) { - Tcl_DictObjPut(interp, dictPtr, objv[3], valuePtr); + Tcl_DictObjPut(interp, dictPtr, objv[2], valuePtr); } else if (dictPtr->bytes != NULL) { - Tcl_InvalidateStringRep(dictPtr); + TclInvalidateStringRep(dictPtr); } - Tcl_IncrRefCount(dictPtr); - resultPtr = Tcl_ObjSetVar2(interp, objv[2], NULL, dictPtr, + resultPtr = Tcl_ObjSetVar2(interp, objv[1], NULL, dictPtr, TCL_LEAVE_ERR_MSG); - TclDecrRefCount(dictPtr); if (resultPtr == NULL) { return TCL_ERROR; } @@ -2135,9 +2283,9 @@ DictLappendCmd(interp, objc, objv) * * DictAppendCmd -- * - * This function implements the "dict append" Tcl command. - * See the user documentation for details on what it does, and - * TIP#111 for the formal specification. + * This function implements the "dict append" Tcl command. See the user + * documentation for details on what it does, and TIP#111 for the formal + * specification. * * Results: * A standard Tcl result. @@ -2149,20 +2297,21 @@ DictLappendCmd(interp, objc, objv) */ static int -DictAppendCmd(interp, objc, objv) - Tcl_Interp *interp; - int objc; - Tcl_Obj *CONST *objv; +DictAppendCmd( + ClientData dummy, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const *objv) { Tcl_Obj *dictPtr, *valuePtr, *resultPtr; int i, allocatedDict = 0; - if (objc < 4) { - Tcl_WrongNumArgs(interp, 2, objv, "varName key ?value ...?"); + if (objc < 3) { + Tcl_WrongNumArgs(interp, 1, objv, "varName key ?value ...?"); return TCL_ERROR; } - dictPtr = Tcl_ObjGetVar2(interp, objv[2], NULL, 0); + dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, 0); if (dictPtr == NULL) { allocatedDict = 1; dictPtr = Tcl_NewDictObj(); @@ -2171,7 +2320,7 @@ DictAppendCmd(interp, objc, objv) dictPtr = Tcl_DuplicateObj(dictPtr); } - if (Tcl_DictObjGet(interp, dictPtr, objv[3], &valuePtr) != TCL_OK) { + if (Tcl_DictObjGet(interp, dictPtr, objv[2], &valuePtr) != TCL_OK) { if (allocatedDict) { TclDecrRefCount(dictPtr); } @@ -2186,16 +2335,14 @@ DictAppendCmd(interp, objc, objv) } } - for (i=4 ; i<objc ; i++) { + for (i=3 ; i<objc ; i++) { Tcl_AppendObjToObj(valuePtr, objv[i]); } - Tcl_DictObjPut(interp, dictPtr, objv[3], valuePtr); + Tcl_DictObjPut(interp, dictPtr, objv[2], valuePtr); - Tcl_IncrRefCount(dictPtr); - resultPtr = Tcl_ObjSetVar2(interp, objv[2], NULL, dictPtr, + resultPtr = Tcl_ObjSetVar2(interp, objv[1], NULL, dictPtr, TCL_LEAVE_ERR_MSG); - TclDecrRefCount(dictPtr); if (resultPtr == NULL) { return TCL_ERROR; } @@ -2206,11 +2353,11 @@ DictAppendCmd(interp, objc, objv) /* *---------------------------------------------------------------------- * - * DictForCmd -- + * DictForNRCmd -- * - * This function implements the "dict for" Tcl command. - * See the user documentation for details on what it does, and - * TIP#111 for the formal specification. + * These functions implement the "dict for" Tcl command. See the user + * documentation for details on what it does, and TIP#111 for the formal + * specification. * * Results: * A standard Tcl result. @@ -2222,23 +2369,29 @@ DictAppendCmd(interp, objc, objv) */ static int -DictForCmd(interp, objc, objv) - Tcl_Interp *interp; - int objc; - Tcl_Obj *CONST *objv; +DictForNRCmd( + ClientData dummy, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const *objv) { + Interp *iPtr = (Interp *) interp; Tcl_Obj *scriptObj, *keyVarObj, *valueVarObj; Tcl_Obj **varv, *keyObj, *valueObj; - Tcl_DictSearch search; - int varc, done, result; + Tcl_DictSearch *searchPtr; + int varc, done; - if (objc != 5) { - Tcl_WrongNumArgs(interp, 2, objv, + if (objc != 4) { + Tcl_WrongNumArgs(interp, 1, objv, "{keyVar valueVar} dictionary script"); return TCL_ERROR; } - if (Tcl_ListObjGetElements(interp, objv[2], &varc, &varv) != TCL_OK) { + /* + * Parse arguments. + */ + + if (TclListObjGetElements(interp, objv[1], &varc, &varv) != TCL_OK) { return TCL_ERROR; } if (varc != 2) { @@ -2246,78 +2399,357 @@ DictForCmd(interp, objc, objv) "must have exactly two variable names", -1)); return TCL_ERROR; } + searchPtr = TclStackAlloc(interp, sizeof(Tcl_DictSearch)); + if (Tcl_DictObjFirst(interp, objv[2], searchPtr, &keyObj, &valueObj, + &done) != TCL_OK) { + TclStackFree(interp, searchPtr); + return TCL_ERROR; + } + if (done) { + TclStackFree(interp, searchPtr); + return TCL_OK; + } + TclListObjGetElements(NULL, objv[1], &varc, &varv); keyVarObj = varv[0]; valueVarObj = varv[1]; - scriptObj = objv[4]; + scriptObj = objv[3]; + /* * Make sure that these objects (which we need throughout the body of the - * loop) don't vanish. Note that the dictionary internal rep is locked + * loop) don't vanish. Note that the dictionary internal rep is locked * internally so that updates, shimmering, etc are not a problem. */ + Tcl_IncrRefCount(keyVarObj); Tcl_IncrRefCount(valueVarObj); Tcl_IncrRefCount(scriptObj); - result = Tcl_DictObjFirst(interp, objv[3], - &search, &keyObj, &valueObj, &done); - if (result != TCL_OK) { - goto doneFor; - } + /* + * Stop the value from getting hit in any way by any traces on the key + * variable. + */ - while (!done) { - /* - * Stop the value from getting hit in any way by any traces on - * the key variable. - */ - Tcl_IncrRefCount(valueObj); - if (Tcl_ObjSetVar2(interp, keyVarObj, NULL, keyObj, 0) == NULL) { - Tcl_ResetResult(interp); - Tcl_AppendResult(interp, "couldn't set key variable: \"", - TclGetString(keyVarObj), "\"", (char *) NULL); - TclDecrRefCount(valueObj); - result = TCL_ERROR; - goto doneFor; - } + Tcl_IncrRefCount(valueObj); + if (Tcl_ObjSetVar2(interp, keyVarObj, NULL, keyObj, TCL_LEAVE_ERR_MSG) == NULL) { TclDecrRefCount(valueObj); - if (Tcl_ObjSetVar2(interp, valueVarObj, NULL, valueObj, 0) == NULL) { + goto error; + } + TclDecrRefCount(valueObj); + if (Tcl_ObjSetVar2(interp, valueVarObj, NULL, valueObj, TCL_LEAVE_ERR_MSG) == NULL) { + goto error; + } + + /* + * Run the script. + */ + + TclNRAddCallback(interp, DictForLoopCallback, searchPtr, keyVarObj, + valueVarObj, scriptObj); + return TclNREvalObjEx(interp, scriptObj, 0, iPtr->cmdFramePtr, 3); + + /* + * For unwinding everything on error. + */ + + error: + TclDecrRefCount(keyVarObj); + TclDecrRefCount(valueVarObj); + TclDecrRefCount(scriptObj); + Tcl_DictObjDone(searchPtr); + TclStackFree(interp, searchPtr); + return TCL_ERROR; +} + +static int +DictForLoopCallback( + ClientData data[], + Tcl_Interp *interp, + int result) +{ + Interp *iPtr = (Interp *) interp; + Tcl_DictSearch *searchPtr = data[0]; + Tcl_Obj *keyVarObj = data[1]; + Tcl_Obj *valueVarObj = data[2]; + Tcl_Obj *scriptObj = data[3]; + Tcl_Obj *keyObj, *valueObj; + int done; + + /* + * Process the result from the previous execution of the script body. + */ + + if (result == TCL_CONTINUE) { + result = TCL_OK; + } else if (result != TCL_OK) { + if (result == TCL_BREAK) { Tcl_ResetResult(interp); - Tcl_AppendResult(interp, "couldn't set value variable: \"", - TclGetString(valueVarObj), "\"", (char *) NULL); - result = TCL_ERROR; - goto doneFor; + result = TCL_OK; + } else if (result == TCL_ERROR) { + Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( + "\n (\"dict for\" body line %d)", + Tcl_GetErrorLine(interp))); } + goto done; + } - result = Tcl_EvalObjEx(interp, scriptObj, 0); - if (result == TCL_CONTINUE) { - result = TCL_OK; - } else if (result != TCL_OK) { - if (result == TCL_BREAK) { - result = TCL_OK; - } else if (result == TCL_ERROR) { - char msg[32 + TCL_INTEGER_SPACE]; + /* + * Get the next mapping from the dictionary. + */ - sprintf(msg, "\n (\"dict for\" body line %d)", - interp->errorLine); - Tcl_AddObjErrorInfo(interp, msg, -1); - } - break; - } + Tcl_DictObjNext(searchPtr, &keyObj, &valueObj, &done); + if (done) { + Tcl_ResetResult(interp); + goto done; + } + + /* + * Stop the value from getting hit in any way by any traces on the key + * variable. + */ - Tcl_DictObjNext(&search, &keyObj, &valueObj, &done); + Tcl_IncrRefCount(valueObj); + if (Tcl_ObjSetVar2(interp, keyVarObj, NULL, keyObj, + TCL_LEAVE_ERR_MSG) == NULL) { + TclDecrRefCount(valueObj); + result = TCL_ERROR; + goto done; + } + TclDecrRefCount(valueObj); + if (Tcl_ObjSetVar2(interp, valueVarObj, NULL, valueObj, + TCL_LEAVE_ERR_MSG) == NULL) { + result = TCL_ERROR; + goto done; } - doneFor: /* - * Stop holding a reference to these objects. + * Run the script. + */ + + TclNRAddCallback(interp, DictForLoopCallback, searchPtr, keyVarObj, + valueVarObj, scriptObj); + return TclNREvalObjEx(interp, scriptObj, 0, iPtr->cmdFramePtr, 3); + + /* + * For unwinding everything once the iterating is done. */ + + done: TclDecrRefCount(keyVarObj); TclDecrRefCount(valueVarObj); TclDecrRefCount(scriptObj); + Tcl_DictObjDone(searchPtr); + TclStackFree(interp, searchPtr); + return result; +} + +/* + *---------------------------------------------------------------------- + * + * DictMapNRCmd -- + * + * These functions implement the "dict map" Tcl command. See the user + * documentation for details on what it does, and TIP#405 for the formal + * specification. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ - Tcl_DictObjDone(&search); - if (result == TCL_OK) { - Tcl_ResetResult(interp); +static int +DictMapNRCmd( + ClientData dummy, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const *objv) +{ + Interp *iPtr = (Interp *) interp; + Tcl_Obj **varv, *keyObj, *valueObj; + DictMapStorage *storagePtr; + int varc, done; + + if (objc != 4) { + Tcl_WrongNumArgs(interp, 1, objv, + "{keyVar valueVar} dictionary script"); + return TCL_ERROR; + } + + /* + * Parse arguments. + */ + + if (TclListObjGetElements(interp, objv[1], &varc, &varv) != TCL_OK) { + return TCL_ERROR; + } + if (varc != 2) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "must have exactly two variable names", -1)); + return TCL_ERROR; + } + storagePtr = TclStackAlloc(interp, sizeof(DictMapStorage)); + if (Tcl_DictObjFirst(interp, objv[2], &storagePtr->search, &keyObj, + &valueObj, &done) != TCL_OK) { + TclStackFree(interp, storagePtr); + return TCL_ERROR; + } + if (done) { + /* + * Note that this exit leaves an empty value in the result (due to + * command calling conventions) but that is OK since an empty value is + * an empty dictionary. + */ + + TclStackFree(interp, storagePtr); + return TCL_OK; + } + TclNewObj(storagePtr->accumulatorObj); + TclListObjGetElements(NULL, objv[1], &varc, &varv); + storagePtr->keyVarObj = varv[0]; + storagePtr->valueVarObj = varv[1]; + storagePtr->scriptObj = objv[3]; + + /* + * Make sure that these objects (which we need throughout the body of the + * loop) don't vanish. Note that the dictionary internal rep is locked + * internally so that updates, shimmering, etc are not a problem. + */ + + Tcl_IncrRefCount(storagePtr->accumulatorObj); + Tcl_IncrRefCount(storagePtr->keyVarObj); + Tcl_IncrRefCount(storagePtr->valueVarObj); + Tcl_IncrRefCount(storagePtr->scriptObj); + + /* + * Stop the value from getting hit in any way by any traces on the key + * variable. + */ + + Tcl_IncrRefCount(valueObj); + if (Tcl_ObjSetVar2(interp, storagePtr->keyVarObj, NULL, keyObj, + TCL_LEAVE_ERR_MSG) == NULL) { + TclDecrRefCount(valueObj); + goto error; + } + if (Tcl_ObjSetVar2(interp, storagePtr->valueVarObj, NULL, valueObj, + TCL_LEAVE_ERR_MSG) == NULL) { + TclDecrRefCount(valueObj); + goto error; + } + TclDecrRefCount(valueObj); + + /* + * Run the script. + */ + + TclNRAddCallback(interp, DictMapLoopCallback, storagePtr, NULL,NULL,NULL); + return TclNREvalObjEx(interp, storagePtr->scriptObj, 0, + iPtr->cmdFramePtr, 3); + + /* + * For unwinding everything on error. + */ + + error: + TclDecrRefCount(storagePtr->keyVarObj); + TclDecrRefCount(storagePtr->valueVarObj); + TclDecrRefCount(storagePtr->scriptObj); + TclDecrRefCount(storagePtr->accumulatorObj); + Tcl_DictObjDone(&storagePtr->search); + TclStackFree(interp, storagePtr); + return TCL_ERROR; +} + +static int +DictMapLoopCallback( + ClientData data[], + Tcl_Interp *interp, + int result) +{ + Interp *iPtr = (Interp *) interp; + DictMapStorage *storagePtr = data[0]; + Tcl_Obj *keyObj, *valueObj; + int done; + + /* + * Process the result from the previous execution of the script body. + */ + + if (result == TCL_CONTINUE) { + result = TCL_OK; + } else if (result != TCL_OK) { + if (result == TCL_BREAK) { + Tcl_ResetResult(interp); + result = TCL_OK; + } else if (result == TCL_ERROR) { + Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( + "\n (\"dict map\" body line %d)", + Tcl_GetErrorLine(interp))); + } + goto done; + } else { + keyObj = Tcl_ObjGetVar2(interp, storagePtr->keyVarObj, NULL, + TCL_LEAVE_ERR_MSG); + if (keyObj == NULL) { + result = TCL_ERROR; + goto done; + } + Tcl_DictObjPut(NULL, storagePtr->accumulatorObj, keyObj, + Tcl_GetObjResult(interp)); } + + /* + * Get the next mapping from the dictionary. + */ + + Tcl_DictObjNext(&storagePtr->search, &keyObj, &valueObj, &done); + if (done) { + Tcl_SetObjResult(interp, storagePtr->accumulatorObj); + goto done; + } + + /* + * Stop the value from getting hit in any way by any traces on the key + * variable. + */ + + Tcl_IncrRefCount(valueObj); + if (Tcl_ObjSetVar2(interp, storagePtr->keyVarObj, NULL, keyObj, + TCL_LEAVE_ERR_MSG) == NULL) { + TclDecrRefCount(valueObj); + result = TCL_ERROR; + goto done; + } + if (Tcl_ObjSetVar2(interp, storagePtr->valueVarObj, NULL, valueObj, + TCL_LEAVE_ERR_MSG) == NULL) { + TclDecrRefCount(valueObj); + result = TCL_ERROR; + goto done; + } + TclDecrRefCount(valueObj); + + /* + * Run the script. + */ + + TclNRAddCallback(interp, DictMapLoopCallback, storagePtr, NULL,NULL,NULL); + return TclNREvalObjEx(interp, storagePtr->scriptObj, 0, + iPtr->cmdFramePtr, 3); + + /* + * For unwinding everything once the iterating is done. + */ + + done: + TclDecrRefCount(storagePtr->keyVarObj); + TclDecrRefCount(storagePtr->valueVarObj); + TclDecrRefCount(storagePtr->scriptObj); + TclDecrRefCount(storagePtr->accumulatorObj); + Tcl_DictObjDone(&storagePtr->search); + TclStackFree(interp, storagePtr); return result; } @@ -2326,9 +2758,9 @@ DictForCmd(interp, objc, objv) * * DictSetCmd -- * - * This function implements the "dict set" Tcl command. - * See the user documentation for details on what it does, and - * TIP#111 for the formal specification. + * This function implements the "dict set" Tcl command. See the user + * documentation for details on what it does, and TIP#111 for the formal + * specification. * * Results: * A standard Tcl result. @@ -2340,20 +2772,21 @@ DictForCmd(interp, objc, objv) */ static int -DictSetCmd(interp, objc, objv) - Tcl_Interp *interp; - int objc; - Tcl_Obj *CONST *objv; +DictSetCmd( + ClientData dummy, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const *objv) { Tcl_Obj *dictPtr, *resultPtr; int result, allocatedDict = 0; - if (objc < 5) { - Tcl_WrongNumArgs(interp, 2, objv, "varName key ?key ...? value"); + if (objc < 4) { + Tcl_WrongNumArgs(interp, 1, objv, "varName key ?key ...? value"); return TCL_ERROR; } - dictPtr = Tcl_ObjGetVar2(interp, objv[2], NULL, 0); + dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, 0); if (dictPtr == NULL) { allocatedDict = 1; dictPtr = Tcl_NewDictObj(); @@ -2362,7 +2795,7 @@ DictSetCmd(interp, objc, objv) dictPtr = Tcl_DuplicateObj(dictPtr); } - result = Tcl_DictObjPutKeyList(interp, dictPtr, objc-4, objv+3, + result = Tcl_DictObjPutKeyList(interp, dictPtr, objc-3, objv+2, objv[objc-1]); if (result != TCL_OK) { if (allocatedDict) { @@ -2371,10 +2804,8 @@ DictSetCmd(interp, objc, objv) return TCL_ERROR; } - Tcl_IncrRefCount(dictPtr); - resultPtr = Tcl_ObjSetVar2(interp, objv[2], NULL, dictPtr, + resultPtr = Tcl_ObjSetVar2(interp, objv[1], NULL, dictPtr, TCL_LEAVE_ERR_MSG); - TclDecrRefCount(dictPtr); if (resultPtr == NULL) { return TCL_ERROR; } @@ -2387,9 +2818,9 @@ DictSetCmd(interp, objc, objv) * * DictUnsetCmd -- * - * This function implements the "dict unset" Tcl command. - * See the user documentation for details on what it does, and - * TIP#111 for the formal specification. + * This function implements the "dict unset" Tcl command. See the user + * documentation for details on what it does, and TIP#111 for the formal + * specification. * * Results: * A standard Tcl result. @@ -2401,20 +2832,21 @@ DictSetCmd(interp, objc, objv) */ static int -DictUnsetCmd(interp, objc, objv) - Tcl_Interp *interp; - int objc; - Tcl_Obj *CONST *objv; +DictUnsetCmd( + ClientData dummy, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const *objv) { Tcl_Obj *dictPtr, *resultPtr; int result, allocatedDict = 0; - if (objc < 4) { - Tcl_WrongNumArgs(interp, 2, objv, "varName key ?key ...?"); + if (objc < 3) { + Tcl_WrongNumArgs(interp, 1, objv, "varName key ?key ...?"); return TCL_ERROR; } - dictPtr = Tcl_ObjGetVar2(interp, objv[2], NULL, 0); + dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, 0); if (dictPtr == NULL) { allocatedDict = 1; dictPtr = Tcl_NewDictObj(); @@ -2423,7 +2855,7 @@ DictUnsetCmd(interp, objc, objv) dictPtr = Tcl_DuplicateObj(dictPtr); } - result = Tcl_DictObjRemoveKeyList(interp, dictPtr, objc-3, objv+3); + result = Tcl_DictObjRemoveKeyList(interp, dictPtr, objc-2, objv+2); if (result != TCL_OK) { if (allocatedDict) { TclDecrRefCount(dictPtr); @@ -2431,10 +2863,8 @@ DictUnsetCmd(interp, objc, objv) return TCL_ERROR; } - Tcl_IncrRefCount(dictPtr); - resultPtr = Tcl_ObjSetVar2(interp, objv[2], NULL, dictPtr, + resultPtr = Tcl_ObjSetVar2(interp, objv[1], NULL, dictPtr, TCL_LEAVE_ERR_MSG); - TclDecrRefCount(dictPtr); if (resultPtr == NULL) { return TCL_ERROR; } @@ -2447,9 +2877,9 @@ DictUnsetCmd(interp, objc, objv) * * DictFilterCmd -- * - * This function implements the "dict filter" Tcl command. - * See the user documentation for details on what it does, and - * TIP#111 for the formal specification. + * This function implements the "dict filter" Tcl command. See the user + * documentation for details on what it does, and TIP#111 for the formal + * specification. * * Results: * A standard Tcl result. @@ -2461,58 +2891,90 @@ DictUnsetCmd(interp, objc, objv) */ static int -DictFilterCmd(interp, objc, objv) - Tcl_Interp *interp; - int objc; - Tcl_Obj *CONST *objv; +DictFilterCmd( + ClientData dummy, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const *objv) { - static CONST char *filters[] = { + Interp *iPtr = (Interp *) interp; + static const char *const filters[] = { "key", "script", "value", NULL }; enum FilterTypes { FILTER_KEYS, FILTER_SCRIPT, FILTER_VALUES }; Tcl_Obj *scriptObj, *keyVarObj, *valueVarObj; - Tcl_Obj **varv, *keyObj, *valueObj, *resultObj, *boolObj; + Tcl_Obj **varv, *keyObj = NULL, *valueObj = NULL, *resultObj, *boolObj; Tcl_DictSearch search; int index, varc, done, result, satisfied; - char *pattern; - char msg[32 + TCL_INTEGER_SPACE]; + const char *pattern; - if (objc < 4) { - Tcl_WrongNumArgs(interp, 2, objv, "dictionary filterType ..."); + if (objc < 3) { + Tcl_WrongNumArgs(interp, 1, objv, "dictionary filterType ?arg ...?"); return TCL_ERROR; } - if (Tcl_GetIndexFromObj(interp, objv[3], filters, "filterType", + if (Tcl_GetIndexFromObj(interp, objv[2], filters, "filterType", 0, &index) != TCL_OK) { return TCL_ERROR; } switch ((enum FilterTypes) index) { case FILTER_KEYS: - if (objc != 5) { - Tcl_WrongNumArgs(interp, 2, objv, "dictionary key globPattern"); - return TCL_ERROR; - } - /* * Create a dictionary whose keys all match a certain pattern. */ - if (Tcl_DictObjFirst(interp, objv[2], &search, + + if (Tcl_DictObjFirst(interp, objv[1], &search, &keyObj, &valueObj, &done) != TCL_OK) { return TCL_ERROR; } - pattern = TclGetString(objv[4]); - resultObj = Tcl_NewDictObj(); - if (TclMatchIsTrivial(pattern)) { - Tcl_DictObjGet(interp, objv[2], objv[4], &valueObj); - if (valueObj != NULL) { - Tcl_DictObjPut(interp, resultObj, objv[4], valueObj); + if (objc == 3) { + /* + * Nothing to match, so return nothing (== empty dictionary). + */ + + Tcl_DictObjDone(&search); + return TCL_OK; + } else if (objc == 4) { + pattern = TclGetString(objv[3]); + resultObj = Tcl_NewDictObj(); + if (TclMatchIsTrivial(pattern)) { + /* + * Must release the search lock here to prevent a memory leak + * since we are not exhausing the search. [Bug 1705778, leak + * K05] + */ + + Tcl_DictObjDone(&search); + Tcl_DictObjGet(interp, objv[1], objv[3], &valueObj); + if (valueObj != NULL) { + Tcl_DictObjPut(interp, resultObj, objv[3], valueObj); + } + } else { + while (!done) { + if (Tcl_StringMatch(TclGetString(keyObj), pattern)) { + Tcl_DictObjPut(interp, resultObj, keyObj, valueObj); + } + Tcl_DictObjNext(&search, &keyObj, &valueObj, &done); + } } } else { + /* + * Can't optimize this match for trivial globbing: would disturb + * order. + */ + + resultObj = Tcl_NewDictObj(); while (!done) { - if (Tcl_StringMatch(TclGetString(keyObj), pattern)) { - Tcl_DictObjPut(interp, resultObj, keyObj, valueObj); + int i; + + for (i=3 ; i<objc ; i++) { + pattern = TclGetString(objv[i]); + if (Tcl_StringMatch(TclGetString(keyObj), pattern)) { + Tcl_DictObjPut(interp, resultObj, keyObj, valueObj); + break; /* stop inner loop */ + } } Tcl_DictObjNext(&search, &keyObj, &valueObj, &done); } @@ -2521,23 +2983,24 @@ DictFilterCmd(interp, objc, objv) return TCL_OK; case FILTER_VALUES: - if (objc != 5) { - Tcl_WrongNumArgs(interp, 2, objv, "dictionary value globPattern"); - return TCL_ERROR; - } - /* * Create a dictionary whose values all match a certain pattern. */ - if (Tcl_DictObjFirst(interp, objv[2], &search, + + if (Tcl_DictObjFirst(interp, objv[1], &search, &keyObj, &valueObj, &done) != TCL_OK) { return TCL_ERROR; } - pattern = TclGetString(objv[4]); resultObj = Tcl_NewDictObj(); while (!done) { - if (Tcl_StringMatch(TclGetString(valueObj), pattern)) { - Tcl_DictObjPut(interp, resultObj, keyObj, valueObj); + int i; + + for (i=3 ; i<objc ; i++) { + pattern = TclGetString(objv[i]); + if (Tcl_StringMatch(TclGetString(valueObj), pattern)) { + Tcl_DictObjPut(interp, resultObj, keyObj, valueObj); + break; /* stop inner loop */ + } } Tcl_DictObjNext(&search, &keyObj, &valueObj, &done); } @@ -2545,20 +3008,19 @@ DictFilterCmd(interp, objc, objv) return TCL_OK; case FILTER_SCRIPT: - if (objc != 6) { - Tcl_WrongNumArgs(interp, 2, objv, + if (objc != 5) { + Tcl_WrongNumArgs(interp, 1, objv, "dictionary script {keyVar valueVar} filterScript"); return TCL_ERROR; } /* - * Create a dictionary whose key,value pairs all satisfy a - * script (i.e. get a true boolean result from its - * evaluation.) Massive copying from the "dict for" - * implementation has occurred! + * Create a dictionary whose key,value pairs all satisfy a script + * (i.e. get a true boolean result from its evaluation). Massive + * copying from the "dict for" implementation has occurred! */ - if (Tcl_ListObjGetElements(interp, objv[4], &varc, &varv) != TCL_OK) { + if (TclListObjGetElements(interp, objv[3], &varc, &varv) != TCL_OK) { return TCL_ERROR; } if (varc != 2) { @@ -2568,18 +3030,20 @@ DictFilterCmd(interp, objc, objv) } keyVarObj = varv[0]; valueVarObj = varv[1]; - scriptObj = objv[5]; + scriptObj = objv[4]; + /* * Make sure that these objects (which we need throughout the body of - * the loop) don't vanish. Note that the dictionary internal rep is + * the loop) don't vanish. Note that the dictionary internal rep is * locked internally so that updates, shimmering, etc are not a * problem. */ + Tcl_IncrRefCount(keyVarObj); Tcl_IncrRefCount(valueVarObj); Tcl_IncrRefCount(scriptObj); - result = Tcl_DictObjFirst(interp, objv[2], + result = Tcl_DictObjFirst(interp, objv[1], &search, &keyObj, &valueObj, &done); if (result != TCL_OK) { TclDecrRefCount(keyVarObj); @@ -2592,28 +3056,36 @@ DictFilterCmd(interp, objc, objv) while (!done) { /* - * Stop the value from getting hit in any way by any - * traces on the key variable. + * Stop the value from getting hit in any way by any traces on the + * key variable. */ + Tcl_IncrRefCount(keyObj); Tcl_IncrRefCount(valueObj); if (Tcl_ObjSetVar2(interp, keyVarObj, NULL, keyObj, TCL_LEAVE_ERR_MSG) == NULL) { Tcl_ResetResult(interp); - Tcl_AppendResult(interp, "couldn't set key variable: \"", - TclGetString(keyVarObj), "\"", (char *) NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "couldn't set key variable: \"%s\"", + TclGetString(keyVarObj))); result = TCL_ERROR; goto abnormalResult; } if (Tcl_ObjSetVar2(interp, valueVarObj, NULL, valueObj, TCL_LEAVE_ERR_MSG) == NULL) { Tcl_ResetResult(interp); - Tcl_AppendResult(interp, "couldn't set value variable: \"", - TclGetString(valueVarObj), "\"", (char *) NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "couldn't set value variable: \"%s\"", + TclGetString(valueVarObj))); + result = TCL_ERROR; goto abnormalResult; } - result = Tcl_EvalObjEx(interp, scriptObj, 0); + /* + * TIP #280. Make invoking context available to loop body. + */ + + result = TclEvalObjEx(interp, scriptObj, 0, iPtr->cmdFramePtr, 4); switch (result) { case TCL_OK: boolObj = Tcl_GetObjResult(interp); @@ -2636,15 +3108,16 @@ DictFilterCmd(interp, objc, objv) * makes the next Tcl_DictObjNext say there is nothing more to * do. */ + Tcl_ResetResult(interp); Tcl_DictObjDone(&search); case TCL_CONTINUE: result = TCL_OK; break; case TCL_ERROR: - sprintf(msg, "\n (\"dict filter\" script line %d)", - interp->errorLine); - Tcl_AddObjErrorInfo(interp, msg, -1); + Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( + "\n (\"dict filter\" script line %d)", + Tcl_GetErrorLine(interp))); default: goto abnormalResult; } @@ -2658,6 +3131,7 @@ DictFilterCmd(interp, objc, objv) /* * Stop holding a reference to these objects. */ + TclDecrRefCount(keyVarObj); TclDecrRefCount(valueVarObj); TclDecrRefCount(scriptObj); @@ -2669,6 +3143,7 @@ DictFilterCmd(interp, objc, objv) TclDecrRefCount(resultObj); } return result; + abnormalResult: Tcl_DictObjDone(&search); TclDecrRefCount(keyObj); @@ -2689,9 +3164,9 @@ DictFilterCmd(interp, objc, objv) * * DictUpdateCmd -- * - * This function implements the "dict update" Tcl command. - * See the user documentation for details on what it does, and - * TIP#212 for the formal specification. + * This function implements the "dict update" Tcl command. See the user + * documentation for details on what it does, and TIP#212 for the formal + * specification. * * Results: * A standard Tcl result. @@ -2703,22 +3178,23 @@ DictFilterCmd(interp, objc, objv) */ static int -DictUpdateCmd(interp, objc, objv) - Tcl_Interp *interp; - int objc; - Tcl_Obj *CONST *objv; +DictUpdateCmd( + ClientData clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const *objv) { + Interp *iPtr = (Interp *) interp; Tcl_Obj *dictPtr, *objPtr; - int i, result, dummy, allocdict = 0; - Tcl_InterpState state; + int i, dummy; - if (objc < 6 || objc & 1) { - Tcl_WrongNumArgs(interp, 2, objv, + if (objc < 5 || !(objc & 1)) { + Tcl_WrongNumArgs(interp, 1, objv, "varName key varName ?key varName ...? script"); return TCL_ERROR; } - dictPtr = Tcl_ObjGetVar2(interp, objv[2], NULL, TCL_LEAVE_ERR_MSG); + dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, TCL_LEAVE_ERR_MSG); if (dictPtr == NULL) { return TCL_ERROR; } @@ -2726,7 +3202,7 @@ DictUpdateCmd(interp, objc, objv) return TCL_ERROR; } Tcl_IncrRefCount(dictPtr); - for (i=3 ; i+2<objc ; i+=2) { + for (i=2 ; i+2<objc ; i+=2) { if (Tcl_DictObjGet(interp, dictPtr, objv[i], &objPtr) != TCL_OK) { TclDecrRefCount(dictPtr); return TCL_ERROR; @@ -2743,21 +3219,46 @@ DictUpdateCmd(interp, objc, objv) TclDecrRefCount(dictPtr); /* - * Execute the body. + * Execute the body after setting up the NRE handler to process the + * results. + */ + + objPtr = Tcl_NewListObj(objc-3, objv+2); + Tcl_IncrRefCount(objPtr); + Tcl_IncrRefCount(objv[1]); + TclNRAddCallback(interp, FinalizeDictUpdate, objv[1], objPtr, NULL,NULL); + + return TclNREvalObjEx(interp, objv[objc-1], 0, iPtr->cmdFramePtr, objc-1); +} + +static int +FinalizeDictUpdate( + ClientData data[], + Tcl_Interp *interp, + int result) +{ + Tcl_Obj *dictPtr, *objPtr, **objv; + Tcl_InterpState state; + int i, objc; + Tcl_Obj *varName = data[0]; + Tcl_Obj *argsObj = data[1]; + + /* + * ErrorInfo handling. */ - result = Tcl_EvalObj(interp, objv[objc-1]); if (result == TCL_ERROR) { Tcl_AddErrorInfo(interp, "\n (body of \"dict update\")"); } /* - * If the dictionary variable doesn't exist, drop everything - * silently. + * If the dictionary variable doesn't exist, drop everything silently. */ - dictPtr = Tcl_ObjGetVar2(interp, objv[2], NULL, 0); + dictPtr = Tcl_ObjGetVar2(interp, varName, NULL, 0); if (dictPtr == NULL) { + TclDecrRefCount(varName); + TclDecrRefCount(argsObj); return result; } @@ -2766,44 +3267,54 @@ DictUpdateCmd(interp, objc, objv) */ state = Tcl_SaveInterpState(interp, result); - if (Tcl_DictObjSize(interp, dictPtr, &dummy) != TCL_OK) { + if (Tcl_DictObjSize(interp, dictPtr, &objc) != TCL_OK) { Tcl_DiscardInterpState(state); + TclDecrRefCount(varName); + TclDecrRefCount(argsObj); return TCL_ERROR; } if (Tcl_IsShared(dictPtr)) { dictPtr = Tcl_DuplicateObj(dictPtr); - allocdict = 1; } /* - * Write back the values from the variables, treating failure to - * read as an instruction to remove the key. + * Write back the values from the variables, treating failure to read as + * an instruction to remove the key. */ - for (i=3 ; i+2<objc ; i+=2) { + Tcl_ListObjGetElements(NULL, argsObj, &objc, &objv); + for (i=0 ; i<objc ; i+=2) { objPtr = Tcl_ObjGetVar2(interp, objv[i+1], NULL, 0); if (objPtr == NULL) { Tcl_DictObjRemove(interp, dictPtr, objv[i]); + } else if (objPtr == dictPtr) { + /* + * Someone is messing us around, trying to build a recursive + * structure. [Bug 1786481] + */ + + Tcl_DictObjPut(interp, dictPtr, objv[i], + Tcl_DuplicateObj(objPtr)); } else { /* Shouldn't fail */ Tcl_DictObjPut(interp, dictPtr, objv[i], objPtr); } } + TclDecrRefCount(argsObj); /* * Write the dictionary back to its variable. */ - if (Tcl_ObjSetVar2(interp, objv[2], NULL, dictPtr, + if (Tcl_ObjSetVar2(interp, varName, NULL, dictPtr, TCL_LEAVE_ERR_MSG) == NULL) { Tcl_DiscardInterpState(state); - if (allocdict) { - TclDecrRefCount(dictPtr); - } + TclDecrRefCount(varName); return TCL_ERROR; } + TclDecrRefCount(varName); return Tcl_RestoreInterpState(interp, state); } @@ -2812,9 +3323,9 @@ DictUpdateCmd(interp, objc, objv) * * DictWithCmd -- * - * This function implements the "dict with" Tcl command. - * See the user documentation for details on what it does, and - * TIP#212 for the formal specification. + * This function implements the "dict with" Tcl command. See the user + * documentation for details on what it does, and TIP#212 for the formal + * specification. * * Results: * A standard Tcl result. @@ -2826,18 +3337,17 @@ DictUpdateCmd(interp, objc, objv) */ static int -DictWithCmd(interp, objc, objv) - Tcl_Interp *interp; - int objc; - Tcl_Obj *CONST *objv; +DictWithCmd( + ClientData dummy, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const *objv) { - Tcl_Obj *dictPtr, *keysPtr, *keyPtr, *valPtr, **keyv, *leafPtr; - Tcl_DictSearch s; - Tcl_InterpState state; - int done, result, keyc, i, allocdict=0; + Interp *iPtr = (Interp *) interp; + Tcl_Obj *dictPtr, *keysPtr, *pathPtr; - if (objc < 4) { - Tcl_WrongNumArgs(interp, 2, objv, "dictVar ?key ...? script"); + if (objc < 3) { + Tcl_WrongNumArgs(interp, 1, objv, "dictVar ?key ...? script"); return TCL_ERROR; } @@ -2845,32 +3355,146 @@ DictWithCmd(interp, objc, objv) * Get the dictionary to open out. */ - dictPtr = Tcl_ObjGetVar2(interp, objv[2], NULL, TCL_LEAVE_ERR_MSG); + dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, TCL_LEAVE_ERR_MSG); if (dictPtr == NULL) { return TCL_ERROR; } - if (objc > 4) { - dictPtr = TclTraceDictPath(interp, dictPtr, objc-4, objv+3, + + keysPtr = TclDictWithInit(interp, dictPtr, objc-3, objv+2); + if (keysPtr == NULL) { + return TCL_ERROR; + } + Tcl_IncrRefCount(keysPtr); + + /* + * Execute the body, while making the invoking context available to the + * loop body (TIP#280) and postponing the cleanup until later (NRE). + */ + + pathPtr = NULL; + if (objc > 3) { + pathPtr = Tcl_NewListObj(objc-3, objv+2); + Tcl_IncrRefCount(pathPtr); + } + Tcl_IncrRefCount(objv[1]); + TclNRAddCallback(interp, FinalizeDictWith, objv[1], keysPtr, pathPtr, + NULL); + + return TclNREvalObjEx(interp, objv[objc-1], 0, iPtr->cmdFramePtr, objc-1); +} + +static int +FinalizeDictWith( + ClientData data[], + Tcl_Interp *interp, + int result) +{ + Tcl_Obj **pathv; + int pathc; + Tcl_InterpState state; + Tcl_Obj *varName = data[0]; + Tcl_Obj *keysPtr = data[1]; + Tcl_Obj *pathPtr = data[2]; + Var *varPtr, *arrayPtr; + + if (result == TCL_ERROR) { + Tcl_AddErrorInfo(interp, "\n (body of \"dict with\")"); + } + + /* + * Save the result state; TDWF doesn't guarantee to not modify that on + * TCL_OK result. + */ + + state = Tcl_SaveInterpState(interp, result); + if (pathPtr != NULL) { + Tcl_ListObjGetElements(NULL, pathPtr, &pathc, &pathv); + } else { + pathc = 0; + pathv = NULL; + } + + /* + * Pack from local variables back into the dictionary. + */ + + varPtr = TclObjLookupVarEx(interp, varName, NULL, TCL_LEAVE_ERR_MSG, "set", + /*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr); + if (varPtr == NULL) { + result = TCL_ERROR; + } else { + result = TclDictWithFinish(interp, varPtr, arrayPtr, varName, NULL, -1, + pathc, pathv, keysPtr); + } + + /* + * Tidy up and return the real result (unless we had an error). + */ + + TclDecrRefCount(varName); + TclDecrRefCount(keysPtr); + if (pathPtr != NULL) { + TclDecrRefCount(pathPtr); + } + if (result != TCL_OK) { + Tcl_DiscardInterpState(state); + return TCL_ERROR; + } + return Tcl_RestoreInterpState(interp, state); +} + +/* + *---------------------------------------------------------------------- + * + * TclDictWithInit -- + * + * Part of the core of [dict with]. Pokes into a dictionary and converts + * the mappings there into assignments to (presumably) local variables. + * Returns a list of all the names that were mapped so that removal of + * either the variable or the dictionary entry won't surprise us when we + * come to stuffing everything back. + * + * Result: + * List of mapped names, or NULL if there was an error. + * + * Side effects: + * Assigns to variables, so potentially legion due to traces. + * + *---------------------------------------------------------------------- + */ + +Tcl_Obj * +TclDictWithInit( + Tcl_Interp *interp, + Tcl_Obj *dictPtr, + int pathc, + Tcl_Obj *const pathv[]) +{ + Tcl_DictSearch s; + Tcl_Obj *keyPtr, *valPtr, *keysPtr; + int done; + + if (pathc > 0) { + dictPtr = TclTraceDictPath(interp, dictPtr, pathc, pathv, DICT_PATH_READ); if (dictPtr == NULL) { - return TCL_ERROR; + return NULL; } } /* - * Go over the list of keys and write each corresponding value to - * a variable in the current context with the same name. Also - * keep a copy of the keys so we can write back properly later on - * even if the dictionary has been structurally modified. + * Go over the list of keys and write each corresponding value to a + * variable in the current context with the same name. Also keep a copy of + * the keys so we can write back properly later on even if the dictionary + * has been structurally modified. */ if (Tcl_DictObjFirst(interp, dictPtr, &s, &keyPtr, &valPtr, &done) != TCL_OK) { - return TCL_ERROR; + return NULL; } TclNewObj(keysPtr); - Tcl_IncrRefCount(keysPtr); for (; !done ; Tcl_DictObjNext(&s, &keyPtr, &valPtr, &done)) { Tcl_ListObjAppendElement(NULL, keysPtr, keyPtr); @@ -2878,72 +3502,109 @@ DictWithCmd(interp, objc, objv) TCL_LEAVE_ERR_MSG) == NULL) { TclDecrRefCount(keysPtr); Tcl_DictObjDone(&s); - return TCL_ERROR; + return NULL; } } - /* - * Execute the body. - */ + return keysPtr; +} + +/* + *---------------------------------------------------------------------- + * + * TclDictWithFinish -- + * + * Part of the core of [dict with]. Reassembles the piece of the dict (in + * varName, location given by pathc/pathv) from the variables named in + * the keysPtr argument. NB, does not try to preserve errors or manage + * argument lifetimes. + * + * Result: + * TCL_OK if we succeeded, or TCL_ERROR if we failed. + * + * Side effects: + * Assigns to a variable, so potentially legion due to traces. Updates + * the dictionary in the named variable. + * + *---------------------------------------------------------------------- + */ - result = Tcl_EvalObjEx(interp, objv[objc-1], 0); - if (result == TCL_ERROR) { - Tcl_AddErrorInfo(interp, "\n (body of \"dict with\")"); - } +int +TclDictWithFinish( + Tcl_Interp *interp, /* Command interpreter in which variable + * exists. Used for state management, traces + * and error reporting. */ + Var *varPtr, /* Reference to the variable holding the + * dictionary. */ + Var *arrayPtr, /* Reference to the array containing the + * variable, or NULL if the variable is a + * scalar. */ + Tcl_Obj *part1Ptr, /* Name of an array (if part2 is non-NULL) or + * the name of a variable. NULL if the 'index' + * parameter is >= 0 */ + Tcl_Obj *part2Ptr, /* If non-NULL, gives the name of an element + * in the array part1. */ + int index, /* Index into the local variable table of the + * variable, or -1. Only used when part1Ptr is + * NULL. */ + int pathc, /* The number of elements in the path into the + * dictionary. */ + Tcl_Obj *const pathv[], /* The elements of the path to the subdict. */ + Tcl_Obj *keysPtr) /* List of keys to be synchronized. This is + * the result value from TclDictWithInit. */ +{ + Tcl_Obj *dictPtr, *leafPtr, *valPtr; + int i, allocdict, keyc; + Tcl_Obj **keyv; /* - * If the dictionary variable doesn't exist, drop everything - * silently. + * If the dictionary variable doesn't exist, drop everything silently. */ - dictPtr = Tcl_ObjGetVar2(interp, objv[2], NULL, 0); + dictPtr = TclPtrGetVar(interp, varPtr, arrayPtr, part1Ptr, part2Ptr, + TCL_LEAVE_ERR_MSG, index); if (dictPtr == NULL) { - TclDecrRefCount(keysPtr); - return result; + return TCL_OK; } /* * Double-check that it is still a dictionary. */ - state = Tcl_SaveInterpState(interp, result); if (Tcl_DictObjSize(interp, dictPtr, &i) != TCL_OK) { - TclDecrRefCount(keysPtr); - Tcl_DiscardInterpState(state); return TCL_ERROR; } if (Tcl_IsShared(dictPtr)) { dictPtr = Tcl_DuplicateObj(dictPtr); allocdict = 1; + } else { + allocdict = 0; } - if (objc > 4) { + if (pathc > 0) { /* - * Want to get to the dictionary which we will update; need to - * do prepare-for-update de-sharing along the path *but* avoid - * generating an error on a non-existant path (we'll treat - * that the same as a non-existant variable. Luckily, the - * de-sharing operation isn't deeply damaging if we don't go - * on to update; it's just less than perfectly efficient (but - * no memory should be leaked). + * Want to get to the dictionary which we will update; need to do + * prepare-for-update de-sharing along the path *but* avoid generating + * an error on a non-existant path (we'll treat that the same as a + * non-existant variable. Luckily, the de-sharing operation isn't + * deeply damaging if we don't go on to update; it's just less than + * perfectly efficient (but no memory should be leaked). */ - leafPtr = TclTraceDictPath(interp, dictPtr, objc-4, objv+3, + + leafPtr = TclTraceDictPath(interp, dictPtr, pathc, pathv, DICT_PATH_EXISTS | DICT_PATH_UPDATE); if (leafPtr == NULL) { - TclDecrRefCount(keysPtr); if (allocdict) { TclDecrRefCount(dictPtr); } - Tcl_DiscardInterpState(state); return TCL_ERROR; } if (leafPtr == DICT_PATH_NON_EXISTENT) { - TclDecrRefCount(keysPtr); if (allocdict) { TclDecrRefCount(dictPtr); } - return Tcl_RestoreInterpState(interp, state); + return TCL_OK; } } else { leafPtr = dictPtr; @@ -2953,23 +3614,29 @@ DictWithCmd(interp, objc, objv) * Now process our updates on the leaf dictionary. */ - Tcl_ListObjGetElements(NULL, keysPtr, &keyc, &keyv); + TclListObjGetElements(NULL, keysPtr, &keyc, &keyv); for (i=0 ; i<keyc ; i++) { valPtr = Tcl_ObjGetVar2(interp, keyv[i], NULL, 0); if (valPtr == NULL) { Tcl_DictObjRemove(NULL, leafPtr, keyv[i]); + } else if (leafPtr == valPtr) { + /* + * Someone is messing us around, trying to build a recursive + * structure. [Bug 1786481] + */ + + Tcl_DictObjPut(NULL, leafPtr, keyv[i], Tcl_DuplicateObj(valPtr)); } else { Tcl_DictObjPut(NULL, leafPtr, keyv[i], valPtr); } } - TclDecrRefCount(keysPtr); /* - * Ensure that none of the dictionaries in the chain still have a - * string rep. + * Ensure that none of the dictionaries in the chain still have a string + * rep. */ - if (objc > 4) { + if (pathc > 0) { InvalidateDictChain(leafPtr); } @@ -2977,88 +3644,45 @@ DictWithCmd(interp, objc, objv) * Write back the outermost dictionary to the variable. */ - if (Tcl_ObjSetVar2(interp, objv[2], NULL, dictPtr, - TCL_LEAVE_ERR_MSG) == NULL) { + if (TclPtrSetVar(interp, varPtr, arrayPtr, part1Ptr, part2Ptr, dictPtr, + TCL_LEAVE_ERR_MSG, index) == NULL) { if (allocdict) { TclDecrRefCount(dictPtr); } - Tcl_DiscardInterpState(state); return TCL_ERROR; } - return Tcl_RestoreInterpState(interp, state); + return TCL_OK; } /* *---------------------------------------------------------------------- * - * Tcl_DictObjCmd -- + * TclInitDictCmd -- * - * This function is invoked to process the "dict" Tcl command. - * See the user documentation for details on what it does, and - * TIP#111 for the formal specification. + * This function is create the "dict" Tcl command. See the user + * documentation for details on what it does, and TIP#111 for the formal + * specification. * * Results: - * A standard Tcl result. + * A Tcl command handle. * * Side effects: - * See the user documentation. + * May advance compilation epoch. * *---------------------------------------------------------------------- */ -int -Tcl_DictObjCmd(/*ignored*/ clientData, interp, objc, objv) - ClientData clientData; - Tcl_Interp *interp; - int objc; - Tcl_Obj *CONST *objv; +Tcl_Command +TclInitDictCmd( + Tcl_Interp *interp) { - static CONST char *subcommands[] = { - "append", "create", "exists", "filter", "for", - "get", "incr", "info", "keys", "lappend", "merge", - "remove", "replace", "set", "size", "unset", - "update", "values", "with", NULL - }; - enum DictSubcommands { - DICT_APPEND, DICT_CREATE, DICT_EXISTS, DICT_FILTER, DICT_FOR, - DICT_GET, DICT_INCR, DICT_INFO, DICT_KEYS, DICT_LAPPEND, DICT_MERGE, - DICT_REMOVE, DICT_REPLACE, DICT_SET, DICT_SIZE, DICT_UNSET, - DICT_UPDATE, DICT_VALUES, DICT_WITH - }; - int index; - - if (objc < 2) { - Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?arg ...?"); - return TCL_ERROR; - } - if (Tcl_GetIndexFromObj(interp, objv[1], subcommands, "subcommand", - 0, &index) != TCL_OK) { - return TCL_ERROR; - } - switch ((enum DictSubcommands) index) { - case DICT_APPEND: return DictAppendCmd(interp, objc, objv); - case DICT_CREATE: return DictCreateCmd(interp, objc, objv); - case DICT_EXISTS: return DictExistsCmd(interp, objc, objv); - case DICT_FILTER: return DictFilterCmd(interp, objc, objv); - case DICT_FOR: return DictForCmd(interp, objc, objv); - case DICT_GET: return DictGetCmd(interp, objc, objv); - case DICT_INCR: return DictIncrCmd(interp, objc, objv); - case DICT_INFO: return DictInfoCmd(interp, objc, objv); - case DICT_KEYS: return DictKeysCmd(interp, objc, objv); - case DICT_LAPPEND: return DictLappendCmd(interp, objc, objv); - case DICT_MERGE: return DictMergeCmd(interp, objc, objv); - case DICT_REMOVE: return DictRemoveCmd(interp, objc, objv); - case DICT_REPLACE: return DictReplaceCmd(interp, objc, objv); - case DICT_SET: return DictSetCmd(interp, objc, objv); - case DICT_SIZE: return DictSizeCmd(interp, objc, objv); - case DICT_UNSET: return DictUnsetCmd(interp, objc, objv); - case DICT_UPDATE: return DictUpdateCmd(interp, objc, objv); - case DICT_VALUES: return DictValuesCmd(interp, objc, objv); - case DICT_WITH: return DictWithCmd(interp, objc, objv); - } - Tcl_Panic("unexpected fallthrough!"); - /* - * Next line is NOT REACHED - stops compliler complaint though... - */ - return TCL_ERROR; + return TclMakeEnsemble(interp, "dict", implementationMap); } + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ |