diff options
Diffstat (limited to 'generic/tclDictObj.c')
| -rw-r--r-- | generic/tclDictObj.c | 2251 | 
1 files changed, 1470 insertions, 781 deletions
| diff --git a/generic/tclDictObj.c b/generic/tclDictObj.c index 12907db..970978f 100644 --- a/generic/tclDictObj.c +++ b/generic/tclDictObj.c @@ -4,12 +4,10 @@   *	This file contains functions that implement the Tcl dict object type   *	and its accessor command.   * - * Copyright (c) 2002 by Donal K. Fellows. + * Copyright (c) 2002-2010 by Donal K. Fellows.   *   * See the file "license.terms" for information on usage and redistribution of   * this file, and for a DISCLAIMER OF ALL WARRANTIES. - * - * RCS: @(#) $Id: tclDictObj.c,v 1.39 2005/11/04 22:38:38 msofer Exp $   */  #include "tclInt.h" @@ -25,49 +23,98 @@ struct Dict;   */  static void		DeleteDict(struct Dict *dict); -static int		DictAppendCmd(Tcl_Interp *interp, -			    int objc, Tcl_Obj *CONST *objv); -static int		DictCreateCmd(Tcl_Interp *interp, -			    int objc, Tcl_Obj *CONST *objv); -static int		DictExistsCmd(Tcl_Interp *interp, -			    int objc, Tcl_Obj *CONST *objv); -static int		DictFilterCmd(Tcl_Interp *interp, -			    int objc, Tcl_Obj *CONST *objv); -static int		DictForCmd(Tcl_Interp *interp, -			    int objc, Tcl_Obj *CONST *objv); -static int		DictGetCmd(Tcl_Interp *interp, -			    int objc, Tcl_Obj *CONST *objv); -static int		DictIncrCmd(Tcl_Interp *interp, -			    int objc, Tcl_Obj *CONST *objv); -static int		DictInfoCmd(Tcl_Interp *interp, -			    int objc, Tcl_Obj *CONST *objv); -static int		DictKeysCmd(Tcl_Interp *interp, -			    int objc, Tcl_Obj *CONST *objv); -static int		DictLappendCmd(Tcl_Interp *interp, -			    int objc, Tcl_Obj *CONST *objv); -static int		DictMergeCmd(Tcl_Interp *interp, -			    int objc, Tcl_Obj *CONST *objv); -static int		DictRemoveCmd(Tcl_Interp *interp, -			    int objc, Tcl_Obj *CONST *objv); -static int		DictReplaceCmd(Tcl_Interp *interp, -			    int objc, Tcl_Obj *CONST *objv); -static int		DictSetCmd(Tcl_Interp *interp, -			    int objc, Tcl_Obj *CONST *objv); -static int		DictSizeCmd(Tcl_Interp *interp, -			    int objc, Tcl_Obj *CONST *objv); -static int		DictUnsetCmd(Tcl_Interp *interp, -			    int objc, Tcl_Obj *CONST *objv); -static int		DictValuesCmd(Tcl_Interp *interp, -			    int objc, Tcl_Obj *CONST *objv); -static int		DictUpdateCmd(Tcl_Interp *interp, -			    int objc, Tcl_Obj *CONST *objv); -static int		DictWithCmd(Tcl_Interp *interp, -			    int objc, Tcl_Obj *CONST *objv); +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 Tcl_NRPostProc	FinalizeDictUpdate; +static Tcl_NRPostProc	FinalizeDictWith; +static Tcl_ObjCmdProc	DictForNRCmd; +static Tcl_ObjCmdProc	DictMapNRCmd; +static Tcl_NRPostProc	DictForLoopCallback; +static Tcl_NRPostProc	DictMapLoopCallback; + +/* + * Table of dict subcommand names and implementations. + */ + +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. @@ -86,25 +133,208 @@ static void		UpdateStringOfDict(Tcl_Obj *dictPtr);  typedef struct Dict {      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) */ +    size_t refCount;		/* Reference counter (see above) */      Tcl_Obj *chain;		/* Linked list used for invalidating the  				 * string representations of updated nested  				 * dictionaries. */  } Dict;  /* + * Accessor macro for converting between a Tcl_Obj* and a Dict. Note that this + * must be assignable as well as readable. + */ + +#define DICT(dictObj)   (*((Dict **)&(dictObj)->internalRep.twoPtrValue.ptr1)) + +/*   * The structure below defines the dictionary object type by means of   * 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; +}  /*   *---------------------------------------------------------------------- @@ -133,37 +363,43 @@ 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 = DICT(srcPtr); +    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; +    newDict->refCount = 1;      /*       * Store in the object.       */ -    copyPtr->internalRep.otherValuePtr = (void *) newDict; + +    DICT(copyPtr) = newDict; +    copyPtr->internalRep.twoPtrValue.ptr2 = NULL;      copyPtr->typePtr = &tclDictType;  } @@ -189,14 +425,12 @@ static void  FreeDictInternalRep(      Tcl_Obj *dictPtr)  { -    Dict *dict = (Dict *) dictPtr->internalRep.otherValuePtr; +    Dict *dict = DICT(dictPtr); -    --dict->refcount; -    if (dict->refcount <= 0) { +    if (dict->refCount-- <= 1) {  	DeleteDict(dict);      } - -    dictPtr->internalRep.otherValuePtr = NULL;	/* Belt and braces! */ +    dictPtr->typePtr = NULL;  }  /* @@ -223,23 +457,8 @@ static void  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);  }  /* @@ -269,20 +488,28 @@ 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 = DICT(dictPtr); +    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...       */ -    numElems = dict->table.numEntries * 2; +    int numElems = dict->table.numEntries * 2; + +    /* Handle empty list case first, simplifies what follows */ +    if (numElems == 0) { +	dictPtr->bytes = &tclEmptyString; +	dictPtr->length = 0; +	return; +    }      /*       * Pass 1: estimate space, gather flags. @@ -290,57 +517,63 @@ UpdateStringOfDict(      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 +	 * 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;  }  /* @@ -368,15 +601,11 @@ 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; +    Dict *dict = ckalloc(sizeof(Dict)); + +    InitChainTable(dict);      /*       * Since lists and dictionaries have very closely-related string @@ -388,143 +617,92 @@ SetDictFromAny(  	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; +	    goto missingValue;  	} -	/* -	 * 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); -	} - -	/* -	 * 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 */ -	} - -	/* -	 * 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); - -    /* -     * Allocate a new HashTable that has objects for keys and objects for -     * values. -     */ - -    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; -	} - -	/* -	 * Allocate a Tcl object for the element and initialize it from the -	 * "elemSize" bytes starting at "elemStart". -	 */ - -	s = ckalloc((unsigned) elemSize + 1); -	if (hasBrace) { -	    memcpy((void *) s, (void *) elemStart, (size_t) elemSize); -	    s[elemSize] = 0; -	} else { -	    elemSize = TclCopyAndCollapse(elemSize, elemStart, s); -	} +		Tcl_Obj *discardedValue = Tcl_GetHashValue(hPtr); -	TclNewObj(keyPtr); -        keyPtr->bytes = s; -        keyPtr->length = elemSize; +		/* +		 * Not really a well-formed dictionary as there are duplicate +		 * keys, so better get the string rep here so that we can +		 * convert back. +		 */ -	p = nextElem; -	lenRemain = (limit - nextElem); -	if (lenRemain <= 0) { -	    goto missingKey; -	} +		(void) Tcl_GetString(objPtr); -	result = TclFindElement(interp, p, lenRemain, -		&elemStart, &nextElem, &elemSize, &hasBrace); -	if (result != TCL_OK) { -	    TclDecrRefCount(keyPtr); -	    goto errorExit; -	} -	if (elemStart >= limit) { -	    goto missingKey; +		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); + +	while (nextElem < limit) { +	    Tcl_Obj *keyPtr, *valuePtr; +	    const char *elemStart; +	    int elemSize, literal; + +	    if (TclFindDictElement(interp, nextElem, (limit - nextElem), +		    &elemStart, &nextElem, &elemSize, &literal) != TCL_OK) { +		goto errorInFindDictElement; +	    } +	    if (elemStart == limit) { +		break; +	    } +	    if (nextElem == limit) { +		goto missingValue; +	    } -	/* -	 * Allocate a Tcl object for the element and initialize it from the -	 * "elemSize" bytes starting at "elemStart". -	 */ +	    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); +	    } -	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 (TclFindDictElement(interp, nextElem, (limit - nextElem), +		    &elemStart, &nextElem, &elemSize, &literal) != TCL_OK) { +		TclDecrRefCount(keyPtr); +		goto errorInFindDictElement; +	    } -	TclNewObj(valuePtr); -        valuePtr->bytes = s; -        valuePtr->length = elemSize; +	    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); +	    } -	/* -	 * Store key and value in the hash table we're building. -	 */ +	    /* Store key and value in the hash table we're building. */ +	    hPtr = CreateChainEntry(dict, keyPtr, &isNew); +	    if (!isNew) { +		Tcl_Obj *discardedValue = Tcl_GetHashValue(hPtr); -	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 @@ -534,27 +712,22 @@ SetDictFromAny(      TclFreeIntRep(objPtr);      dict->epoch = 0;      dict->chain = NULL; -    dict->refcount = 1; -    objPtr->internalRep.otherValuePtr = (void *) dict; +    dict->refCount = 1; +    DICT(objPtr) = dict; +    objPtr->internalRep.twoPtrValue.ptr2 = NULL;      objPtr->typePtr = &tclDictType;      return TCL_OK; - missingKey: +  missingValue:      if (interp != NULL) { -	Tcl_SetObjResult(interp, -		Tcl_NewStringObj("missing value to go with key", -1)); -    } -    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); +	Tcl_SetObjResult(interp, Tcl_NewStringObj( +		"missing value to go with key", -1)); +	Tcl_SetErrorCode(interp, "TCL", "VALUE", "DICTIONARY", NULL);      } -    Tcl_DeleteHashTable(&dict->table); -    ckfree((char *) dict); -    return result; +  errorInFindDictElement: +    DeleteChainTable(dict); +    ckfree(dict); +    return TCL_ERROR;  }  /* @@ -594,36 +767,38 @@ TclTraceDictPath(      Tcl_Interp *interp,      Tcl_Obj *dictPtr,      int keyc, -    Tcl_Obj *CONST keyv[], +    Tcl_Obj *const keyv[],      int flags)  {      Dict *dict, *newDict;      int i; -    if (dictPtr->typePtr != &tclDictType) { -	if (SetDictFromAny(interp, dictPtr) != TCL_OK) { -	    return NULL; -	} +    if (dictPtr->typePtr != &tclDictType +	    && SetDictFromAny(interp, dictPtr) != TCL_OK) { +	return NULL;      } -    dict = (Dict *) dictPtr->internalRep.otherValuePtr; +    dict = DICT(dictPtr);      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;  	    } @@ -631,28 +806,28 @@ TclTraceDictPath(  	    /*  	     * 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); -	    if (tmpObj->typePtr != &tclDictType) { -		if (SetDictFromAny(interp, tmpObj) != TCL_OK) { -		    return NULL; -		} +	    tmpObj = Tcl_GetHashValue(hPtr); +	    if (tmpObj->typePtr != &tclDictType +		    && SetDictFromAny(interp, tmpObj) != TCL_OK) { +		return NULL;  	    }  	} -	newDict = (Dict *) tmpObj->internalRep.otherValuePtr; +	newDict = DICT(tmpObj);  	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 = DICT(tmpObj);  	    }  	    newDict->chain = dictPtr; @@ -687,17 +862,17 @@ static void  InvalidateDictChain(      Tcl_Obj *dictObj)  { -    Dict *dict = (Dict *) dictObj->internalRep.otherValuePtr; +    Dict *dict = DICT(dictObj);      do { -	Tcl_InvalidateStringRep(dictObj); +	TclInvalidateStringRep(dictObj);  	dict->epoch++;  	dictObj = dict->chain;  	if (dictObj == NULL) {  	    break;  	}  	dict->chain = NULL; -	dict = (Dict *) dictObj->internalRep.otherValuePtr; +	dict = DICT(dictObj);      } while (dict != NULL);  } @@ -732,24 +907,23 @@ Tcl_DictObjPut(      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->typePtr != &tclDictType +	    && SetDictFromAny(interp, dictPtr) != TCL_OK) { +	return TCL_ERROR;      }      if (dictPtr->bytes != NULL) { -	Tcl_InvalidateStringRep(dictPtr); +	TclInvalidateStringRep(dictPtr);      } -    dict = (Dict *) dictPtr->internalRep.otherValuePtr; -    hPtr = Tcl_CreateHashEntry(&dict->table, (char *)keyPtr, &isNew); +    dict = DICT(dictPtr); +    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); @@ -787,19 +961,18 @@ Tcl_DictObjGet(      Dict *dict;      Tcl_HashEntry *hPtr; -    if (dictPtr->typePtr != &tclDictType) { -	int result = SetDictFromAny(interp, dictPtr); -	if (result != TCL_OK) { -	    return result; -	} +    if (dictPtr->typePtr != &tclDictType +	    && SetDictFromAny(interp, dictPtr) != TCL_OK) { +	*valuePtrPtr = NULL; +	return TCL_ERROR;      } -    dict = (Dict *) dictPtr->internalRep.otherValuePtr; -    hPtr = Tcl_FindHashEntry(&dict->table, (char *)keyPtr); +    dict = DICT(dictPtr); +    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;  } @@ -830,29 +1003,21 @@ Tcl_DictObjRemove(      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) { -	int result = SetDictFromAny(interp, dictPtr); -	if (result != TCL_OK) { -	    return result; -	} -    } - -    if (dictPtr->bytes != NULL) { -	Tcl_InvalidateStringRep(dictPtr); +    if (dictPtr->typePtr != &tclDictType +	    && SetDictFromAny(interp, dictPtr) != TCL_OK) { +	return TCL_ERROR;      } -    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 = DICT(dictPtr); +    if (DeleteChainEntry(dict, keyPtr)) { +	if (dictPtr->bytes != NULL) { +	    TclInvalidateStringRep(dictPtr); +	}  	dict->epoch++;      }      return TCL_OK; @@ -884,14 +1049,12 @@ Tcl_DictObjSize(  {      Dict *dict; -    if (dictPtr->typePtr != &tclDictType) { -	int result = SetDictFromAny(interp, dictPtr); -	if (result != TCL_OK) { -	    return result; -	} +    if (dictPtr->typePtr != &tclDictType +	    && SetDictFromAny(interp, dictPtr) != TCL_OK) { +	return TCL_ERROR;      } -    dict = (Dict *) dictPtr->internalRep.otherValuePtr; +    dict = DICT(dictPtr);      *sizePtr = dict->table.numEntries;      return TCL_OK;  } @@ -936,30 +1099,29 @@ Tcl_DictObjFirst(  				 * otherwise. */  {      Dict *dict; -    Tcl_HashEntry *hPtr; +    ChainEntry *cPtr; -    if (dictPtr->typePtr != &tclDictType) { -	int result = SetDictFromAny(interp, dictPtr); -	if (result != TCL_OK) { -	    return result; -	} +    if (dictPtr->typePtr != &tclDictType +	    && SetDictFromAny(interp, dictPtr) != TCL_OK) { +	return TCL_ERROR;      } -    dict = (Dict *) dictPtr->internalRep.otherValuePtr; -    hPtr = Tcl_FirstHashEntry(&dict->table, &searchPtr->search); -    if (hPtr == NULL) { +    dict = DICT(dictPtr); +    cPtr = dict->entryChainHead; +    if (cPtr == NULL) {  	searchPtr->epoch = -1;  	*donePtr = 1;      } else {  	*donePtr = 0;  	searchPtr->dictionaryPtr = (Tcl_Dict) dict;  	searchPtr->epoch = dict->epoch; -	dict->refcount++; +	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; @@ -1002,7 +1164,7 @@ Tcl_DictObjNext(  				 * values in the dictionary, or a 0  				 * otherwise. */  { -    Tcl_HashEntry *hPtr; +    ChainEntry *cPtr;      /*       * If the searh is done; we do no work. @@ -1022,20 +1184,21 @@ Tcl_DictObjNext(  	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);      }  } @@ -1067,8 +1230,7 @@ Tcl_DictObjDone(      if (searchPtr->epoch != -1) {  	searchPtr->epoch = -1;  	dict = (Dict *) searchPtr->dictionaryPtr; -	dict->refcount--; -	if (dict->refcount <= 0) { +	if (dict->refCount-- <= 1) {  	    DeleteDict(dict);  	}      } @@ -1099,7 +1261,7 @@ Tcl_DictObjPutKeyList(      Tcl_Interp *interp,      Tcl_Obj *dictPtr,      int keyc, -    Tcl_Obj *CONST keyv[], +    Tcl_Obj *const keyv[],      Tcl_Obj *valuePtr)  {      Dict *dict; @@ -1107,10 +1269,10 @@ Tcl_DictObjPutKeyList(      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); @@ -1118,11 +1280,12 @@ Tcl_DictObjPutKeyList(  	return TCL_ERROR;      } -    dict = (Dict *) dictPtr->internalRep.otherValuePtr; -    hPtr = Tcl_CreateHashEntry(&dict->table, (char *)keyv[keyc-1], &isNew); +    dict = DICT(dictPtr); +    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); @@ -1158,16 +1321,15 @@ Tcl_DictObjRemoveKeyList(      Tcl_Interp *interp,      Tcl_Obj *dictPtr,      int keyc, -    Tcl_Obj *CONST keyv[]) +    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); @@ -1175,13 +1337,8 @@ Tcl_DictObjRemoveKeyList(  	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 = DICT(dictPtr); +    DeleteChainEntry(dict, keyv[keyc-1]);      InvalidateDictChain(dictPtr);      return TCL_OK;  } @@ -1220,13 +1377,14 @@ Tcl_NewDictObj(void)      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; +    dict->refCount = 1; +    DICT(dictPtr) = dict; +    dictPtr->internalRep.twoPtrValue.ptr2 = NULL;      dictPtr->typePtr = &tclDictType;      return dictPtr;  #endif @@ -1261,7 +1419,7 @@ Tcl_NewDictObj(void)  Tcl_Obj *  Tcl_DbNewDictObj( -    CONST char *file, +    const char *file,      int line)  {  #ifdef TCL_MEM_DEBUG @@ -1269,13 +1427,14 @@ Tcl_DbNewDictObj(      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; +    dict->refCount = 1; +    DICT(dictPtr) = dict; +    dictPtr->internalRep.twoPtrValue.ptr2 = NULL;      dictPtr->typePtr = &tclDictType;      return dictPtr;  #else /* !TCL_MEM_DEBUG */ @@ -1305,9 +1464,10 @@ Tcl_DbNewDictObj(  static int  DictCreateCmd( +    ClientData dummy,      Tcl_Interp *interp,      int objc, -    Tcl_Obj *CONST *objv) +    Tcl_Obj *const *objv)  {      Tcl_Obj *dictObj;      int i; @@ -1318,17 +1478,17 @@ DictCreateCmd(       * 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...  	 */ -	Tcl_DictObjPut(interp, dictObj, objv[i], objv[i+1]); +	Tcl_DictObjPut(NULL, dictObj, objv[i], objv[i+1]);      }      Tcl_SetObjResult(interp, dictObj);      return TCL_OK; @@ -1354,15 +1514,16 @@ DictCreateCmd(  static int  DictGetCmd( +    ClientData dummy,      Tcl_Interp *interp,      int objc, -    Tcl_Obj *CONST *objv) +    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;      } @@ -1372,12 +1533,12 @@ DictGetCmd(       * 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; @@ -1406,7 +1567,7 @@ DictGetCmd(       * 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;      } @@ -1415,9 +1576,11 @@ DictGetCmd(  	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); @@ -1444,32 +1607,32 @@ DictGetCmd(  static int  DictReplaceCmd( +    ClientData dummy,      Tcl_Interp *interp,      int objc, -    Tcl_Obj *CONST *objv) +    Tcl_Obj *const *objv)  {      Tcl_Obj *dictPtr; -    int i, result; -    int allocatedDict = 0; +    int i; -    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 (dictPtr->typePtr != &tclDictType +	    && SetDictFromAny(interp, dictPtr) != TCL_OK) { +	return TCL_ERROR; +    }      if (Tcl_IsShared(dictPtr)) {  	dictPtr = Tcl_DuplicateObj(dictPtr); -	allocatedDict = 1;      } -    for (i=3 ; i<objc ; i+=2) { -	result = Tcl_DictObjPut(interp, dictPtr, objv[i], objv[i+1]); -	if (result != TCL_OK) { -	    if (allocatedDict) { -		TclDecrRefCount(dictPtr); -	    } -	    return TCL_ERROR; -	} +    if (dictPtr->bytes != NULL) { +	TclInvalidateStringRep(dictPtr); +    } +    for (i=2 ; i<objc ; i+=2) { +	Tcl_DictObjPut(NULL, dictPtr, objv[i], objv[i+1]);      }      Tcl_SetObjResult(interp, dictPtr);      return TCL_OK; @@ -1495,32 +1658,32 @@ DictReplaceCmd(  static int  DictRemoveCmd( +    ClientData dummy,      Tcl_Interp *interp,      int objc, -    Tcl_Obj *CONST *objv) +    Tcl_Obj *const *objv)  {      Tcl_Obj *dictPtr; -    int i, result; -    int allocatedDict = 0; +    int i; -    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 (dictPtr->typePtr != &tclDictType +	    && SetDictFromAny(interp, dictPtr) != TCL_OK) { +	return TCL_ERROR; +    }      if (Tcl_IsShared(dictPtr)) {  	dictPtr = Tcl_DuplicateObj(dictPtr); -	allocatedDict = 1;      } -    for (i=3 ; i<objc ; i++) { -	result = Tcl_DictObjRemove(interp, dictPtr, objv[i]); -	if (result != TCL_OK) { -	    if (allocatedDict) { -		TclDecrRefCount(dictPtr); -	    } -	    return TCL_ERROR; -	} +    if (dictPtr->bytes != NULL) { +	TclInvalidateStringRep(dictPtr); +    } +    for (i=2 ; i<objc ; i++) { +	Tcl_DictObjRemove(NULL, dictPtr, objv[i]);      }      Tcl_SetObjResult(interp, dictPtr);      return TCL_OK; @@ -1546,16 +1709,17 @@ DictRemoveCmd(  static int  DictMergeCmd( +    ClientData dummy,      Tcl_Interp *interp,      int objc, -    Tcl_Obj *CONST *objv) +    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).  	 */ @@ -1563,18 +1727,22 @@ DictMergeCmd(  	return TCL_OK;      } -    if (objc == 3) { +    /* +     * Make sure first argument is a dictionary. +     */ + +    targetObj = objv[1]; +    if (targetObj->typePtr != &tclDictType +	    && 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;      } @@ -1582,12 +1750,11 @@ DictMergeCmd(       * 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) { @@ -1596,16 +1763,15 @@ DictMergeCmd(  	    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; @@ -1631,47 +1797,62 @@ DictMergeCmd(  static int  DictKeysCmd( +    ClientData dummy,      Tcl_Interp *interp,      int objc, -    Tcl_Obj *CONST *objv) +    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) { +    /* +     * 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 +	    && SetDictFromAny(interp, objv[1]) != TCL_OK) {  	return TCL_ERROR;      } -    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. -	     */ +    } else { +	Tcl_DictSearch search; +	Tcl_Obj *keyPtr = NULL; +	int done = 0; -	    Tcl_ListObjAppendElement(interp, listPtr, keyPtr); +	/* +	 * 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;  } @@ -1696,26 +1877,29 @@ DictKeysCmd(  static int  DictValuesCmd( +    ClientData dummy,      Tcl_Interp *interp,      int objc, -    Tcl_Obj *CONST *objv) +    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)) { @@ -1727,6 +1911,7 @@ DictValuesCmd(  	    Tcl_ListObjAppendElement(interp, listPtr, valuePtr);  	}      } +    Tcl_DictObjDone(&search);      Tcl_SetObjResult(interp, listPtr);      return TCL_OK; @@ -1752,17 +1937,18 @@ DictValuesCmd(  static int  DictSizeCmd( +    ClientData dummy,      Tcl_Interp *interp,      int objc, -    Tcl_Obj *CONST *objv) +    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));      } @@ -1789,32 +1975,27 @@ DictSizeCmd(  static int  DictExistsCmd( +    ClientData dummy,      Tcl_Interp *interp,      int objc, -    Tcl_Obj *CONST *objv) +    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;  } @@ -1838,31 +2019,30 @@ DictExistsCmd(  static int  DictInfoCmd( +    ClientData dummy,      Tcl_Interp *interp,      int objc, -    Tcl_Obj *CONST *objv) +    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]; -    if (dictPtr->typePtr != &tclDictType) { -	int result = SetDictFromAny(interp, dictPtr); -	if (result != TCL_OK) { -	    return result; -	} +    dictPtr = objv[1]; +    if (dictPtr->typePtr != &tclDictType +	    && SetDictFromAny(interp, dictPtr) != TCL_OK) { +	return TCL_ERROR;      } -    dict = (Dict *)dictPtr->internalRep.otherValuePtr; -    /* -     * This next cast is actually OK. -     */ +    dict = DICT(dictPtr); -    Tcl_SetResult(interp, (char *)Tcl_HashStats(&dict->table), TCL_DYNAMIC); +    statsStr = Tcl_HashStats(&dict->table); +    Tcl_SetObjResult(interp, Tcl_NewStringObj(statsStr, -1)); +    ckfree(statsStr);      return TCL_OK;  } @@ -1886,26 +2066,27 @@ DictInfoCmd(  static int  DictIncrCmd( +    ClientData dummy,      Tcl_Interp *interp,      int objc, -    Tcl_Obj *CONST *objv) +    Tcl_Obj *const *objv)  {      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, "dictVarName key ?increment?");  	return TCL_ERROR;      } -    dictPtr = Tcl_ObjGetVar2(interp, objv[2], NULL, 0); +    dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, 0);      if (dictPtr == NULL) {  	/*  	 * Variable didn't yet exist. Create new dictionary value.  	 */  	dictPtr = Tcl_NewDictObj(); -    } else if (Tcl_DictObjGet(interp, dictPtr, objv[3], &valuePtr) != TCL_OK) { +    } else if (Tcl_DictObjGet(interp, dictPtr, objv[2], &valuePtr) != TCL_OK) {  	/*  	 * Variable contents are not a dict, report error.  	 */ @@ -1919,31 +2100,38 @@ DictIncrCmd(  	 */  	char *saved = dictPtr->bytes; +	Tcl_Obj *oldPtr = dictPtr;  	dictPtr->bytes = NULL;  	dictPtr = Tcl_DuplicateObj(dictPtr); -	dictPtr->bytes = saved; +	oldPtr->bytes = saved;      }      if (valuePtr == NULL) {  	/*  	 * Key not in dictionary. Create new key with increment as value.  	 */ -	if (objc == 5) { +	if (objc == 4) {  	    /*  	     * Verify increment is an integer.  	     */  	    mp_int increment; -	    code = Tcl_GetBignumFromObj(interp, objv[4], &increment); +	    code = Tcl_GetBignumFromObj(interp, objv[3], &increment);  	    if (code != TCL_OK) {  		Tcl_AddErrorInfo(interp, "\n    (reading increment)");  	    } else { -		Tcl_DictObjPut(interp, dictPtr, objv[3], objv[4]); +		/* +		 * Remember to dispose with the bignum as we're not actually +		 * using it directly. [Bug 2874678] +		 */ + +		mp_clear(&increment); +		Tcl_DictObjPut(NULL, dictPtr, objv[2], objv[3]);  	    }  	} else { -	    Tcl_DictObjPut(interp, dictPtr, objv[3], Tcl_NewIntObj(1)); +	    Tcl_DictObjPut(NULL, dictPtr, objv[2], Tcl_NewIntObj(1));  	}      } else {  	/* @@ -1952,20 +2140,21 @@ DictIncrCmd(  	if (Tcl_IsShared(valuePtr)) {  	    valuePtr = Tcl_DuplicateObj(valuePtr); -	    Tcl_DictObjPut(interp, dictPtr, objv[3], valuePtr); +	    Tcl_DictObjPut(NULL, dictPtr, objv[2], valuePtr);  	} -	if (objc == 5) { -	    code = TclIncrObj(interp, valuePtr, objv[4]); +	if (objc == 4) { +	    code = TclIncrObj(interp, valuePtr, objv[3]);  	} else {  	    Tcl_Obj *incrPtr = Tcl_NewIntObj(1); +  	    Tcl_IncrRefCount(incrPtr);  	    code = TclIncrObj(interp, valuePtr, incrPtr); -	    Tcl_DecrRefCount(incrPtr); +	    TclDecrRefCount(incrPtr);  	}      }      if (code == TCL_OK) { -	Tcl_InvalidateStringRep(dictPtr); -	valuePtr = Tcl_ObjSetVar2(interp, objv[2], NULL, +	TclInvalidateStringRep(dictPtr); +	valuePtr = Tcl_ObjSetVar2(interp, objv[1], NULL,  		dictPtr, TCL_LEAVE_ERR_MSG);  	if (valuePtr == NULL) {  	    code = TCL_ERROR; @@ -1973,7 +2162,7 @@ DictIncrCmd(  	    Tcl_SetObjResult(interp, valuePtr);  	}      } else if (dictPtr->refCount == 0) { -	Tcl_DecrRefCount(dictPtr); +	TclDecrRefCount(dictPtr);      }      return code;  } @@ -1998,19 +2187,20 @@ DictIncrCmd(  static int  DictLappendCmd( +    ClientData dummy,      Tcl_Interp *interp,      int objc, -    Tcl_Obj *CONST *objv) +    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, "dictVarName 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(); @@ -2019,7 +2209,7 @@ DictLappendCmd(  	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);  	} @@ -2027,7 +2217,7 @@ DictLappendCmd(      }      if (valuePtr == NULL) { -	valuePtr = Tcl_NewListObj(objc-4, objv+4); +	valuePtr = Tcl_NewListObj(objc-3, objv+3);  	allocatedValue = 1;      } else {  	if (Tcl_IsShared(valuePtr)) { @@ -2035,7 +2225,7 @@ DictLappendCmd(  	    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) { @@ -2050,12 +2240,12 @@ DictLappendCmd(      }      if (allocatedValue) { -	Tcl_DictObjPut(interp, dictPtr, objv[3], valuePtr); +	Tcl_DictObjPut(NULL, dictPtr, objv[2], valuePtr);      } else if (dictPtr->bytes != NULL) { -	Tcl_InvalidateStringRep(dictPtr); +	TclInvalidateStringRep(dictPtr);      } -    resultPtr = Tcl_ObjSetVar2(interp, objv[2], NULL, dictPtr, +    resultPtr = Tcl_ObjSetVar2(interp, objv[1], NULL, dictPtr,  	    TCL_LEAVE_ERR_MSG);      if (resultPtr == NULL) {  	return TCL_ERROR; @@ -2084,19 +2274,20 @@ DictLappendCmd(  static int  DictAppendCmd( +    ClientData dummy,      Tcl_Interp *interp,      int objc, -    Tcl_Obj *CONST *objv) +    Tcl_Obj *const *objv)  {      Tcl_Obj *dictPtr, *valuePtr, *resultPtr; -    int i, allocatedDict = 0; +    int allocatedDict = 0; -    if (objc < 4) { -	Tcl_WrongNumArgs(interp, 2, objv, "varName key ?value ...?"); +    if (objc < 3) { +	Tcl_WrongNumArgs(interp, 1, objv, "dictVarName 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(); @@ -2105,28 +2296,53 @@ DictAppendCmd(  	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);  	}  	return TCL_ERROR;      } -    if (valuePtr == NULL) { -	TclNewObj(valuePtr); -    } else { -	if (Tcl_IsShared(valuePtr)) { -	    valuePtr = Tcl_DuplicateObj(valuePtr); +    if ((objc > 3) || (valuePtr == NULL)) { +	/* Only go through append activites when something will change. */ +	Tcl_Obj *appendObjPtr = NULL; + +	if (objc > 3) { +	    /* Something to append */ + +	    if (objc == 4) { +		appendObjPtr = objv[3]; +	    } else if (TCL_OK != TclStringCatObjv(interp, /* inPlace */ 1, +		    objc-3, objv+3, &appendObjPtr)) { +		return TCL_ERROR; +	    }  	} -    } -    for (i=4 ; i<objc ; i++) { -	Tcl_AppendObjToObj(valuePtr, objv[i]); +	if (appendObjPtr == NULL) { +	    /* => (objc == 3) => (valuePtr == NULL) */ +	    TclNewObj(valuePtr); +	} else if (valuePtr == NULL) { +	    valuePtr = appendObjPtr; +	    appendObjPtr = NULL; +	} + +	if (appendObjPtr) { +	    if (Tcl_IsShared(valuePtr)) { +		valuePtr = Tcl_DuplicateObj(valuePtr); +	    } + +	    Tcl_AppendObjToObj(valuePtr, appendObjPtr); +	} + +	Tcl_DictObjPut(NULL, dictPtr, objv[2], valuePtr);      } -    Tcl_DictObjPut(interp, dictPtr, objv[3], valuePtr); +    /* +     * Even if nothing changed, we still overwrite so that variable +     * trace expectations are met. +     */ -    resultPtr = Tcl_ObjSetVar2(interp, objv[2], NULL, dictPtr, +    resultPtr = Tcl_ObjSetVar2(interp, objv[1], NULL, dictPtr,  	    TCL_LEAVE_ERR_MSG);      if (resultPtr == NULL) {  	return TCL_ERROR; @@ -2138,9 +2354,9 @@ DictAppendCmd(  /*   *----------------------------------------------------------------------   * - * DictForCmd -- + * DictForNRCmd --   * - *	This function implements the "dict for" Tcl command. See the user + *	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.   * @@ -2154,33 +2370,51 @@ DictAppendCmd(   */  static int -DictForCmd( +DictForNRCmd( +    ClientData dummy,      Tcl_Interp *interp,      int objc, -    Tcl_Obj *CONST *objv) +    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, -		"{keyVar valueVar} dictionary script"); +    if (objc != 4) { +	Tcl_WrongNumArgs(interp, 1, objv, +		"{keyVarName valueVarName} 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) {  	Tcl_SetObjResult(interp, Tcl_NewStringObj(  		"must have exactly two variable names", -1)); +	Tcl_SetErrorCode(interp, "TCL", "SYNTAX", "dict", "for", NULL); +	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 @@ -2192,65 +2426,335 @@ DictForCmd(      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. +     */ + +    Tcl_IncrRefCount(valueObj); +    if (Tcl_ObjSetVar2(interp, keyVarObj, NULL, keyObj, +	    TCL_LEAVE_ERR_MSG) == NULL) { +	TclDecrRefCount(valueObj); +	goto error; +    } +    TclDecrRefCount(valueObj); +    if (Tcl_ObjSetVar2(interp, valueVarObj, NULL, valueObj, +	    TCL_LEAVE_ERR_MSG) == NULL) { +	goto error;      } -    while (!done) { -	/* -	 * Stop the value from getting hit in any way by any traces on the key -	 * variable. -	 */ +    /* +     * Run the script. +     */ -	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), "\"", NULL); -	    TclDecrRefCount(valueObj); -	    result = TCL_ERROR; -	    goto doneFor; -	} -	TclDecrRefCount(valueObj); -	if (Tcl_ObjSetVar2(interp, valueVarObj, NULL, valueObj, 0) == NULL) { -	    Tcl_ResetResult(interp); -	    Tcl_AppendResult(interp, "couldn't set value variable: \"", -		    TclGetString(valueVarObj), "\"", NULL); -	    result = TCL_ERROR; -	    goto doneFor; -	} +    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; +} -	result = Tcl_EvalObjEx(interp, scriptObj, 0); -	if (result == TCL_CONTINUE) { +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);  	    result = TCL_OK; -	} else if (result != TCL_OK) { -	    if (result == TCL_BREAK) { -		result = TCL_OK; -	    } else if (result == TCL_ERROR) { -		TclFormatToErrorInfo(interp, -			"\n    (\"dict for\" body line %d)", interp->errorLine); -	    } -	    break; +	} else if (result == TCL_ERROR) { +	    Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( +		    "\n    (\"dict for\" body line %d)", +		    Tcl_GetErrorLine(interp)));  	} +	goto done; +    } + +    /* +     * Get the next mapping from the dictionary. +     */ -	Tcl_DictObjNext(&search, &keyObj, &valueObj, &done); +    Tcl_DictObjNext(searchPtr, &keyObj, &valueObj, &done); +    if (done) { +	Tcl_ResetResult(interp); +	goto done;      } - doneFor:      /* -     * Stop holding a reference to these objects. +     * 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, +	    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; +    } + +    /* +     * 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, +		"{keyVarName valueVarName} 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)); +	Tcl_SetErrorCode(interp, "TCL", "SYNTAX", "dict", "map", NULL); +	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;  } @@ -2274,19 +2778,20 @@ DictForCmd(  static int  DictSetCmd( +    ClientData dummy,      Tcl_Interp *interp,      int objc, -    Tcl_Obj *CONST *objv) +    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, "dictVarName 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(); @@ -2295,7 +2800,7 @@ DictSetCmd(  	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) { @@ -2304,7 +2809,7 @@ DictSetCmd(  	return TCL_ERROR;      } -    resultPtr = Tcl_ObjSetVar2(interp, objv[2], NULL, dictPtr, +    resultPtr = Tcl_ObjSetVar2(interp, objv[1], NULL, dictPtr,  	    TCL_LEAVE_ERR_MSG);      if (resultPtr == NULL) {  	return TCL_ERROR; @@ -2333,19 +2838,20 @@ DictSetCmd(  static int  DictUnsetCmd( +    ClientData dummy,      Tcl_Interp *interp,      int objc, -    Tcl_Obj *CONST *objv) +    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, "dictVarName 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(); @@ -2354,7 +2860,7 @@ DictUnsetCmd(  	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); @@ -2362,7 +2868,7 @@ DictUnsetCmd(  	return TCL_ERROR;      } -    resultPtr = Tcl_ObjSetVar2(interp, objv[2], NULL, dictPtr, +    resultPtr = Tcl_ObjSetVar2(interp, objv[1], NULL, dictPtr,  	    TCL_LEAVE_ERR_MSG);      if (resultPtr == NULL) {  	return TCL_ERROR; @@ -2391,57 +2897,89 @@ DictUnsetCmd(  static int  DictFilterCmd( +    ClientData dummy,      Tcl_Interp *interp,      int objc, -    Tcl_Obj *CONST *objv) +    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; +    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(NULL, resultObj, objv[3], valueObj); +		} +	    } else { +		while (!done) { +		    if (Tcl_StringMatch(TclGetString(keyObj), pattern)) { +			Tcl_DictObjPut(NULL, 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(NULL, resultObj, keyObj, valueObj); +			break;		/* stop inner loop */ +		    }  		}  		Tcl_DictObjNext(&search, &keyObj, &valueObj, &done);  	    } @@ -2450,24 +2988,24 @@ DictFilterCmd(  	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(NULL, resultObj, keyObj, valueObj); +		    break;		/* stop inner loop */ +		}  	    }  	    Tcl_DictObjNext(&search, &keyObj, &valueObj, &done);  	} @@ -2475,9 +3013,9 @@ DictFilterCmd(  	return TCL_OK;      case FILTER_SCRIPT: -	if (objc != 6) { -	    Tcl_WrongNumArgs(interp, 2, objv, -		    "dictionary script {keyVar valueVar} filterScript"); +	if (objc != 5) { +	    Tcl_WrongNumArgs(interp, 1, objv, +		    "dictionary script {keyVarName valueVarName} filterScript");  	    return TCL_ERROR;  	} @@ -2487,17 +3025,18 @@ DictFilterCmd(  	 * 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) {  	    Tcl_SetObjResult(interp, Tcl_NewStringObj(  		    "must have exactly two variable names", -1)); +	    Tcl_SetErrorCode(interp, "TCL", "SYNTAX", "dict", "filter", NULL);  	    return TCL_ERROR;  	}  	keyVarObj = varv[0];  	valueVarObj = varv[1]; -	scriptObj = objv[5]; +	scriptObj = objv[4];  	/*  	 * Make sure that these objects (which we need throughout the body of @@ -2510,7 +3049,7 @@ DictFilterCmd(  	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); @@ -2531,21 +3070,24 @@ DictFilterCmd(  	    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), "\"", NULL); +		Tcl_AddErrorInfo(interp, +			"\n    (\"dict filter\" filter script key variable)");  		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), "\"", NULL); +		Tcl_AddErrorInfo(interp, +			"\n    (\"dict filter\" filter script value variable)"); +		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); @@ -2559,7 +3101,7 @@ DictFilterCmd(  		}  		TclDecrRefCount(boolObj);  		if (satisfied) { -		    Tcl_DictObjPut(interp, resultObj, keyObj, valueObj); +		    Tcl_DictObjPut(NULL, resultObj, keyObj, valueObj);  		}  		break;  	    case TCL_BREAK: @@ -2575,9 +3117,9 @@ DictFilterCmd(  		result = TCL_OK;  		break;  	    case TCL_ERROR: -		TclFormatToErrorInfo(interp, +		Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(  			"\n    (\"dict filter\" script line %d)", -			interp->errorLine); +			Tcl_GetErrorLine(interp)));  	    default:  		goto abnormalResult;  	    } @@ -2639,21 +3181,22 @@ DictFilterCmd(  static int  DictUpdateCmd( +    ClientData clientData,      Tcl_Interp *interp,      int objc, -    Tcl_Obj *CONST *objv) +    Tcl_Obj *const *objv)  { +    Interp *iPtr = (Interp *) interp;      Tcl_Obj *dictPtr, *objPtr; -    int i, result, dummy; -    Tcl_InterpState state; +    int i, dummy; -    if (objc < 6 || objc & 1) { -	Tcl_WrongNumArgs(interp, 2, objv, -		"varName key varName ?key varName ...? script"); +    if (objc < 5 || !(objc & 1)) { +	Tcl_WrongNumArgs(interp, 1, objv, +		"dictVarName 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;      } @@ -2661,7 +3204,7 @@ DictUpdateCmd(  	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; @@ -2678,10 +3221,34 @@ DictUpdateCmd(      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\")");      } @@ -2690,8 +3257,10 @@ DictUpdateCmd(       * 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;      } @@ -2700,8 +3269,10 @@ DictUpdateCmd(       */      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;      } @@ -2714,26 +3285,37 @@ DictUpdateCmd(       * 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]); +	    Tcl_DictObjRemove(NULL, dictPtr, objv[i]); +	} else if (objPtr == dictPtr) { +	    /* +	     * Someone is messing us around, trying to build a recursive +	     * structure. [Bug 1786481] +	     */ + +	    Tcl_DictObjPut(NULL, dictPtr, objv[i], Tcl_DuplicateObj(objPtr));  	} else {  	    /* Shouldn't fail */ -	    Tcl_DictObjPut(interp, dictPtr, objv[i], objPtr); +	    Tcl_DictObjPut(NULL, 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); +	TclDecrRefCount(varName);  	return TCL_ERROR;      } +    TclDecrRefCount(varName);      return Tcl_RestoreInterpState(interp, state);  } @@ -2757,17 +3339,16 @@ DictUpdateCmd(  static int  DictWithCmd( +    ClientData dummy,      Tcl_Interp *interp,      int objc, -    Tcl_Obj *CONST *objv) +    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, "dictVarName ?key ...? script");  	return TCL_ERROR;      } @@ -2775,15 +3356,130 @@ DictWithCmd(       * 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;  	}      } @@ -2796,11 +3492,10 @@ DictWithCmd(      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); @@ -2808,46 +3503,87 @@ DictWithCmd(  		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.       */ -    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 @@ -2857,22 +3593,19 @@ DictWithCmd(  	 * 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; @@ -2882,23 +3615,29 @@ DictWithCmd(       * 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.       */ -    if (objc > 4) { +    if (pathc > 0) {  	InvalidateDictChain(leafPtr);      } @@ -2906,89 +3645,39 @@ DictWithCmd(       * Write back the outermost dictionary to the variable.       */ -    if (Tcl_ObjSetVar2(interp, objv[2], NULL, dictPtr, -	    TCL_LEAVE_ERR_MSG) == NULL) { -	Tcl_DiscardInterpState(state); +    if (TclPtrSetVar(interp, varPtr, arrayPtr, part1Ptr, part2Ptr, dictPtr, +	    TCL_LEAVE_ERR_MSG, index) == NULL) { +	if (allocdict) { +	    TclDecrRefCount(dictPtr); +	}  	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 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);  }  /* | 
