diff options
Diffstat (limited to 'generic/tclVar.c')
| -rw-r--r-- | generic/tclVar.c | 3098 |
1 files changed, 1368 insertions, 1730 deletions
diff --git a/generic/tclVar.c b/generic/tclVar.c index 5ab6e8b..bdc64b7 100644 --- a/generic/tclVar.c +++ b/generic/tclVar.c @@ -18,7 +18,6 @@ */ #include "tclInt.h" -#include "tclOOInt.h" /* * Prototypes for the variable hash key methods. @@ -27,11 +26,12 @@ static Tcl_HashEntry * AllocVarEntry(Tcl_HashTable *tablePtr, void *keyPtr); static void FreeVarEntry(Tcl_HashEntry *hPtr); static int CompareVarKeys(void *keyPtr, Tcl_HashEntry *hPtr); +static unsigned int HashVarKey(Tcl_HashTable *tablePtr, void *keyPtr); -static const Tcl_HashKeyType tclVarHashKeyType = { +static Tcl_HashKeyType tclVarHashKeyType = { TCL_HASH_KEY_TYPE_VERSION, /* version */ 0, /* flags */ - TclHashObjKey, /* hashKeyProc */ + HashVarKey, /* hashKeyProc */ CompareVarKeys, /* compareKeysProc */ AllocVarEntry, /* allocEntryProc */ FreeVarEntry /* freeEntryProc */ @@ -60,8 +60,8 @@ VarHashCreateVar( Tcl_Obj *key, int *newPtr) { - Tcl_HashEntry *hPtr = Tcl_CreateHashEntry(&tablePtr->table, - key, newPtr); + Tcl_HashEntry *hPtr = Tcl_CreateHashEntry((Tcl_HashTable *) tablePtr, + (char *) key, newPtr); if (hPtr) { return VarHashGetValue(hPtr); @@ -72,15 +72,13 @@ VarHashCreateVar( #define VarHashFindVar(tablePtr, key) \ VarHashCreateVar((tablePtr), (key), NULL) - #define VarHashInvalidateEntry(varPtr) \ ((varPtr)->flags |= VAR_DEAD_HASH) - #define VarHashDeleteEntry(varPtr) \ Tcl_DeleteHashEntry(&(((VarInHash *) varPtr)->entry)) #define VarHashFirstEntry(tablePtr, searchPtr) \ - Tcl_FirstHashEntry(&(tablePtr)->table, (searchPtr)) + Tcl_FirstHashEntry((Tcl_HashTable *) (tablePtr), (searchPtr)) #define VarHashNextEntry(searchPtr) \ Tcl_NextHashEntry((searchPtr)) @@ -116,7 +114,7 @@ VarHashNextVar( (((VarInHash *)(varPtr))->entry.key.objPtr) #define VarHashDeleteTable(tablePtr) \ - Tcl_DeleteHashTable(&(tablePtr)->table) + Tcl_DeleteHashTable((Tcl_HashTable *) (tablePtr)) /* * The strings below are used to indicate what went wrong when a variable @@ -149,7 +147,6 @@ static const char *isArrayElement = */ typedef struct ArraySearch { - Tcl_Obj *name; /* Name of this search */ int id; /* Integer id used to distinguish among * multiple concurrent searches for the same * array. */ @@ -176,8 +173,8 @@ static void AppendLocals(Tcl_Interp *interp, Tcl_Obj *listPtr, Tcl_Obj *patternPtr, int includeLinks); static void DeleteSearches(Interp *iPtr, Var *arrayVarPtr); static void DeleteArray(Interp *iPtr, Tcl_Obj *arrayNamePtr, - Var *varPtr, int flags, int index); -static Tcl_Var ObjFindNamespaceVar(Tcl_Interp *interp, + Var *varPtr, int flags); +static Tcl_Var ObjFindNamespaceVar(Tcl_Interp *interp, Tcl_Obj *namePtr, Tcl_Namespace *contextNsPtr, int flags); static int ObjMakeUpvar(Tcl_Interp *interp, @@ -188,8 +185,9 @@ static ArraySearch * ParseSearchId(Tcl_Interp *interp, const Var *varPtr, Tcl_Obj *varNamePtr, Tcl_Obj *handleObj); static void UnsetVarStruct(Var *varPtr, Var *arrayPtr, Interp *iPtr, Tcl_Obj *part1Ptr, - Tcl_Obj *part2Ptr, int flags, int index); -static Var * VerifyArray(Tcl_Interp *interp, Tcl_Obj *varNameObj); + Tcl_Obj *part2Ptr, int flags); +static int SetArraySearchObj(Tcl_Interp *interp, + Tcl_Obj *objPtr); /* * Functions defined in this file that may be exported in the future for use @@ -202,17 +200,26 @@ MODULE_SCOPE Var * TclLookupSimpleVar(Tcl_Interp *interp, static Tcl_DupInternalRepProc DupLocalVarName; static Tcl_FreeInternalRepProc FreeLocalVarName; +static Tcl_UpdateStringProc PanicOnUpdateVarName; static Tcl_FreeInternalRepProc FreeParsedVarName; static Tcl_DupInternalRepProc DupParsedVarName; +static Tcl_UpdateStringProc UpdateParsedVarName; + +static Tcl_UpdateStringProc PanicOnUpdateVarName; +static Tcl_SetFromAnyProc PanicOnSetVarName; /* * Types of Tcl_Objs used to cache variable lookups. * * localVarName - INTERNALREP DEFINITION: - * twoPtrValue.ptr1: pointer to name obj in varFramePtr->localCache - * or NULL if it is this same obj - * twoPtrValue.ptr2: index into locals table + * ptrAndLongRep.ptr: pointer to name obj in varFramePtr->localCache + * or NULL if it is this same obj + * ptrAndLongRep.value: index into locals table + * + * nsVarName - INTERNALREP DEFINITION: + * twoPtrValue.ptr1: pointer to the namespace containing the reference + * twoPtrValue.ptr2: pointer to the corresponding Var * * parsedVarName - INTERNALREP DEFINITION: * twoPtrValue.ptr1: pointer to the array name Tcl_Obj, or NULL if it is a @@ -221,16 +228,51 @@ static Tcl_DupInternalRepProc DupParsedVarName; * Tcl_Obj), or NULL if it is a scalar variable */ -static const Tcl_ObjType localVarNameType = { +static Tcl_ObjType localVarNameType = { "localVarName", - FreeLocalVarName, DupLocalVarName, NULL, NULL + FreeLocalVarName, DupLocalVarName, PanicOnUpdateVarName, PanicOnSetVarName +}; + +/* + * Caching of namespace variables disabled: no simple way was found to avoid + * interfering with the resolver's idea of variable existence. A cached + * varName may keep a variable's name in the namespace's hash table, which is + * the resolver's criterion for existence (see test namespace-17.10). + */ + +#define ENABLE_NS_VARNAME_CACHING 0 + +#if ENABLE_NS_VARNAME_CACHING +static Tcl_FreeInternalRepProc FreeNsVarName; +static Tcl_DupInternalRepProc DupNsVarName; + +static Tcl_ObjType tclNsVarNameType = { + "namespaceVarName", + FreeNsVarName, DupNsVarName, PanicOnUpdateVarName, PanicOnSetVarName }; +#endif -static const Tcl_ObjType tclParsedVarNameType = { +static Tcl_ObjType tclParsedVarNameType = { "parsedVarName", - FreeParsedVarName, DupParsedVarName, NULL, NULL + FreeParsedVarName, DupParsedVarName, UpdateParsedVarName, PanicOnSetVarName }; +/* + * Type of Tcl_Objs used to speed up array searches. + * + * INTERNALREP DEFINITION: + * twoPtrValue.ptr1: searchIdNumber (cast to pointer) + * twoPtrValue.ptr2: variableNameStartInString (cast to pointer) + * + * Note that the value stored in ptr2 is the offset into the string of the + * start of the variable name and not the address of the variable name itself, + * as this can be safely copied. + */ + +Tcl_ObjType tclArraySearchType = { + "array search", + NULL, NULL, NULL, SetArraySearchObj +}; Var * TclVarHashCreateVar( @@ -282,7 +324,7 @@ CleanupVar( && !TclIsVarTraced(varPtr) && (VarHashRefCount(varPtr) == !TclIsVarDeadHash(varPtr))) { if (VarHashRefCount(varPtr) == 0) { - ckfree(varPtr); + ckfree((char *) varPtr); } else { VarHashDeleteEntry(varPtr); } @@ -291,7 +333,7 @@ CleanupVar( TclIsVarInHash(arrayPtr) && !TclIsVarTraced(arrayPtr) && (VarHashRefCount(arrayPtr) == !TclIsVarDeadHash(arrayPtr))) { if (VarHashRefCount(arrayPtr) == 0) { - ckfree(arrayPtr); + ckfree((char *) arrayPtr); } else { VarHashDeleteEntry(arrayPtr); } @@ -501,20 +543,41 @@ TclObjLookupVarEx( * is set to NULL. */ { Interp *iPtr = (Interp *) interp; - CallFrame *varFramePtr = iPtr->varFramePtr; register Var *varPtr; /* Points to the variable's in-frame Var * structure. */ - const char *errMsg = NULL; - int index, parsed = 0; + char *part1; + int index, len1, len2; + int parsed = 0; + Tcl_Obj *objPtr; const Tcl_ObjType *typePtr = part1Ptr->typePtr; + const char *errMsg = NULL; + CallFrame *varFramePtr = iPtr->varFramePtr; +#if ENABLE_NS_VARNAME_CACHING + Namespace *nsPtr; +#endif + char *part2 = part2Ptr? TclGetString(part2Ptr):NULL; + char *newPart2 = NULL; *arrayPtrPtr = NULL; +#if ENABLE_NS_VARNAME_CACHING + if (varFramePtr) { + nsPtr = varFramePtr->nsPtr; + } else { + /* + * Some variables in the global ns have to be initialized before the + * root call frame is in place. + */ + + nsPtr = NULL; + } +#endif + if (typePtr == &localVarNameType) { int localIndex; localVarNameTypeHandling: - localIndex = PTR2INT(part1Ptr->internalRep.twoPtrValue.ptr2); + localIndex = (int) part1Ptr->internalRep.ptrAndLongRep.value; if (HasLocalVars(varFramePtr) && !(flags & (TCL_GLOBAL_ONLY | TCL_NAMESPACE_ONLY)) && (localIndex < varFramePtr->numCompiledLocals)) { @@ -522,8 +585,9 @@ TclObjLookupVarEx( * Use the cached index if the names coincide. */ - Tcl_Obj *namePtr = part1Ptr->internalRep.twoPtrValue.ptr1; - Tcl_Obj *checkNamePtr = localName(varFramePtr, localIndex); + Tcl_Obj *namePtr = (Tcl_Obj *) + part1Ptr->internalRep.ptrAndLongRep.ptr; + Tcl_Obj *checkNamePtr = localName(iPtr->varFramePtr, localIndex); if ((!namePtr && (checkNamePtr == part1Ptr)) || (namePtr && (checkNamePtr == namePtr))) { @@ -532,6 +596,44 @@ TclObjLookupVarEx( } } goto doneParsing; +#if ENABLE_NS_VARNAME_CACHING + } else if (typePtr == &tclNsVarNameType) { + int useGlobal, useReference; + Namespace *cachedNsPtr = part1Ptr->internalRep.twoPtrValue.ptr1; + varPtr = part1Ptr->internalRep.twoPtrValue.ptr2; + + useGlobal = (cachedNsPtr == iPtr->globalNsPtr) && ( + (flags & TCL_GLOBAL_ONLY) || + (part1[0]==':' && part1[1]==':') || + (!HasLocalVars(varFramePtr) && (nsPtr==iPtr->globalNsPtr))); + + useReference = useGlobal || ((cachedNsPtr == nsPtr) && ( + (flags & TCL_NAMESPACE_ONLY) || + (!HasLocalVars(varFramePtr) && !(flags & TCL_GLOBAL_ONLY) && + /* + * Careful: an undefined ns variable could be hiding a valid + * global reference. + */ + !TclIsVarUndefined(varPtr)))); + + if (useReference && !TclIsVarDeadHash(varPtr)) { + /* + * A straight global or namespace reference, use it. It isn't so + * simple to deal with 'implicit' namespace references, i.e., + * those where the reference could be to either a namespace or a + * global variable. Those we lookup again. + * + * If TclIsVarDeadHash(varPtr), this might be a reference to a + * variable in a deleted namespace, kept alive by e.g. part1Ptr. + * We could conceivably be so unlucky that a new namespace was + * created at the same address as the deleted one, so to be safe + * we test for a valid hPtr. + */ + + goto donePart1; + } + goto doneParsing; +#endif } /* @@ -550,11 +652,16 @@ TclObjLookupVarEx( if (flags & TCL_LEAVE_ERR_MSG) { TclObjVarErrMsg(interp, part1Ptr, part2Ptr, msg, noSuchVar, -1); - Tcl_SetErrorCode(interp, "TCL", "VALUE", "VARNAME", NULL); } return NULL; } - part2Ptr = part1Ptr->internalRep.twoPtrValue.ptr2; + part2 = newPart2 = part1Ptr->internalRep.twoPtrValue.ptr2; + if (newPart2) { + part2Ptr = Tcl_NewStringObj(newPart2, -1); + if (createPart2) { + Tcl_IncrRefCount(part2Ptr); + } + } part1Ptr = part1Ptr->internalRep.twoPtrValue.ptr1; typePtr = part1Ptr->typePtr; if (typePtr == &localVarNameType) { @@ -563,46 +670,70 @@ TclObjLookupVarEx( } parsed = 1; } + part1 = TclGetStringFromObj(part1Ptr, &len1); - if (!parsed) { - + if (!parsed && len1 && (*(part1 + len1 - 1) == ')')) { /* * part1Ptr is possibly an unparsed array element. */ - int len; - const char *part1 = TclGetStringFromObj(part1Ptr, &len); - - if (len > 1 && (part1[len - 1] == ')')) { - - const char *part2 = strchr(part1, '('); - - if (part2) { - Tcl_Obj *arrayPtr; + register int i; + len2 = -1; + for (i = 0; i < len1; i++) { + if (*(part1 + i) == '(') { if (part2Ptr != NULL) { if (flags & TCL_LEAVE_ERR_MSG) { TclObjVarErrMsg(interp, part1Ptr, part2Ptr, msg, needArray, -1); - Tcl_SetErrorCode(interp, "TCL", "VALUE", "VARNAME", - NULL); } return NULL; } - arrayPtr = Tcl_NewStringObj(part1, (part2 - part1)); - part2Ptr = Tcl_NewStringObj(part2 + 1, len - (part2 - part1) - 2); + /* + * part1Ptr points to an array element; first copy the element + * name to a new string part2. + */ - TclFreeIntRep(part1Ptr); + part2 = part1 + i + 1; + len2 = len1 - i - 2; + len1 = i; + + newPart2 = ckalloc((unsigned int) (len2+1)); + memcpy(newPart2, part2, (unsigned int) len2); + *(newPart2+len2) = '\0'; + part2 = newPart2; + part2Ptr = Tcl_NewStringObj(newPart2, -1); + if (createPart2) { + Tcl_IncrRefCount(part2Ptr); + } - Tcl_IncrRefCount(arrayPtr); - part1Ptr->internalRep.twoPtrValue.ptr1 = arrayPtr; - Tcl_IncrRefCount(part2Ptr); - part1Ptr->internalRep.twoPtrValue.ptr2 = part2Ptr; - part1Ptr->typePtr = &tclParsedVarNameType; + /* + * Free the internal rep of the original part1Ptr, now renamed + * objPtr, and set it to tclParsedVarNameType. + */ + + objPtr = part1Ptr; + TclFreeIntRep(objPtr); + objPtr->typePtr = &tclParsedVarNameType; + + /* + * Define a new string object to hold the new part1Ptr, i.e., + * the array name. Set the internal rep of objPtr, reset + * typePtr and part1 to contain the references to the array + * name. + */ + + TclNewStringObj(part1Ptr, part1, len1); + Tcl_IncrRefCount(part1Ptr); + + objPtr->internalRep.twoPtrValue.ptr1 = part1Ptr; + objPtr->internalRep.twoPtrValue.ptr2 = (void *) part2; - part1Ptr = arrayPtr; - } + typePtr = part1Ptr->typePtr; + part1 = TclGetString(part1Ptr); + break; + } } } @@ -612,13 +743,17 @@ TclObjLookupVarEx( * the cached types if possible. */ + TclFreeIntRep(part1Ptr); + part1Ptr->typePtr = NULL; + varPtr = TclLookupSimpleVar(interp, part1Ptr, flags, createPart1, &errMsg, &index); if (varPtr == NULL) { if ((errMsg != NULL) && (flags & TCL_LEAVE_ERR_MSG)) { TclObjVarErrMsg(interp, part1Ptr, part2Ptr, msg, errMsg, -1); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "VARNAME", - TclGetString(part1Ptr), NULL); + } + if (newPart2) { + Tcl_DecrRefCount(part2Ptr); } return NULL; } @@ -627,25 +762,35 @@ TclObjLookupVarEx( * Cache the newly found variable if possible. */ - TclFreeIntRep(part1Ptr); if (index >= 0) { /* * An indexed local variable. */ - Tcl_Obj *cachedNamePtr = localName(varFramePtr, index); part1Ptr->typePtr = &localVarNameType; - if (part1Ptr != cachedNamePtr) { - part1Ptr->internalRep.twoPtrValue.ptr1 = cachedNamePtr; - Tcl_IncrRefCount(cachedNamePtr); - if (cachedNamePtr->typePtr != &localVarNameType - || cachedNamePtr->internalRep.twoPtrValue.ptr1 != NULL) { - TclFreeIntRep(cachedNamePtr); - } + if (part1Ptr != localName(iPtr->varFramePtr, index)) { + part1Ptr->internalRep.ptrAndLongRep.ptr = + localName(iPtr->varFramePtr, index); + Tcl_IncrRefCount((Tcl_Obj *) + part1Ptr->internalRep.ptrAndLongRep.ptr); } else { - part1Ptr->internalRep.twoPtrValue.ptr1 = NULL; + part1Ptr->internalRep.ptrAndLongRep.ptr = NULL; } - part1Ptr->internalRep.twoPtrValue.ptr2 = INT2PTR(index); + part1Ptr->internalRep.ptrAndLongRep.value = (long) index; +#if ENABLE_NS_VARNAME_CACHING + } else if (index > -3) { + /* + * A cacheable namespace or global variable. + */ + + Namespace *nsPtr; + + nsPtr = ((index == -1) ? iPtr->globalNsPtr : varFramePtr->nsPtr); + varPtr->refCount++; + part1Ptr->typePtr = &tclNsVarNameType; + part1Ptr->internalRep.twoPtrValue.ptr1 = nsPtr; + part1Ptr->internalRep.twoPtrValue.ptr2 = varPtr; +#endif } else { /* * At least mark part1Ptr as already parsed. @@ -657,6 +802,16 @@ TclObjLookupVarEx( } donePart1: +#if 0 + if (varPtr == NULL) { + if (flags & TCL_LEAVE_ERR_MSG) { + part1 = TclGetString(part1Ptr); + TclObjVarErrMsg(interp, part1Ptr, part2Ptr, msg, + "Cached variable reference is NULL.", -1); + } + return NULL; + } +#endif while (TclIsVarLink(varPtr)) { varPtr = varPtr->value.linkPtr; } @@ -669,11 +824,29 @@ TclObjLookupVarEx( *arrayPtrPtr = varPtr; varPtr = TclLookupArrayElement(interp, part1Ptr, part2Ptr, flags, msg, createPart1, createPart2, varPtr, -1); + if (newPart2) { + Tcl_DecrRefCount(part2Ptr); + } } return varPtr; } /* + * This flag bit should not interfere with TCL_GLOBAL_ONLY, + * TCL_NAMESPACE_ONLY, or TCL_LEAVE_ERR_MSG; it signals that the variable + * lookup is performed for upvar (or similar) purposes, with slightly + * different rules: + * - Bug #696893 - variable is either proc-local or in the current + * namespace; never follow the second (global) resolution path + * - Bug #631741 - do not use special namespace or interp resolvers + * + * It should also not collide with the (deprecated) TCL_PARSE_PART1 flag + * (Bug #835020) + */ + +#define AVOID_RESOLVERS 0x40000 + +/* *---------------------------------------------------------------------- * * TclLookupSimpleVar -- @@ -722,8 +895,8 @@ TclLookupSimpleVar( Tcl_Obj *varNamePtr, /* This is a simple variable name that could * represent a scalar or an array. */ int flags, /* Only TCL_GLOBAL_ONLY, TCL_NAMESPACE_ONLY, - * TCL_AVOID_RESOLVERS and TCL_LEAVE_ERR_MSG - * bits matter. */ + * AVOID_RESOLVERS and TCL_LEAVE_ERR_MSG bits + * matter. */ const int create, /* If 1, create hash table entry for varname, * if it doesn't already exist. If 0, return * error if it doesn't exist. */ @@ -743,8 +916,8 @@ TclLookupSimpleVar( * the variable. */ Namespace *varNsPtr, *cxtNsPtr, *dummy1Ptr, *dummy2Ptr; ResolverScheme *resPtr; - int isNew, i, result, varLen; - const char *varName = TclGetStringFromObj(varNamePtr, &varLen); + int isNew; + const char *varName = TclGetString(varNamePtr); varPtr = NULL; varNsPtr = NULL; /* Set non-NULL if a nonlocal variable. */ @@ -763,10 +936,12 @@ TclLookupSimpleVar( */ if ((cxtNsPtr->varResProc != NULL || iPtr->resolverPtr != NULL) - && !(flags & TCL_AVOID_RESOLVERS)) { + && !(flags & AVOID_RESOLVERS)) { + int result; + resPtr = iPtr->resolverPtr; if (cxtNsPtr->varResProc) { - result = cxtNsPtr->varResProc(interp, varName, + result = (*cxtNsPtr->varResProc)(interp, varName, (Tcl_Namespace *) cxtNsPtr, flags, &var); } else { result = TCL_CONTINUE; @@ -774,7 +949,7 @@ TclLookupSimpleVar( while (result == TCL_CONTINUE && resPtr) { if (resPtr->varResProc) { - result = resPtr->varResProc(interp, varName, + result = (*resPtr->varResProc)(interp, varName, (Tcl_Namespace *) cxtNsPtr, flags, &var); } resPtr = resPtr->nextPtr; @@ -816,7 +991,7 @@ TclLookupSimpleVar( *indexPtr = -1; flags = (flags | TCL_GLOBAL_ONLY) & ~TCL_NAMESPACE_ONLY; } else { - if (flags & TCL_AVOID_RESOLVERS) { + if (flags & AVOID_RESOLVERS) { flags = (flags | TCL_NAMESPACE_ONLY); } if (flags & TCL_NAMESPACE_ONLY) { @@ -831,11 +1006,12 @@ TclLookupSimpleVar( varPtr = (Var *) ObjFindNamespaceVar(interp, varNamePtr, (Tcl_Namespace *) cxtNsPtr, - (flags | TCL_AVOID_RESOLVERS) & ~TCL_LEAVE_ERR_MSG); + (flags | AVOID_RESOLVERS) & ~TCL_LEAVE_ERR_MSG); if (varPtr == NULL) { - Tcl_Obj *tailPtr; if (create) { /* Var wasn't found so create it. */ + Tcl_Obj *tailPtr; + TclGetNamespaceForQualName(interp, varName, cxtNsPtr, flags, &varNsPtr, &dummy1Ptr, &dummy2Ptr, &tail); if (varNsPtr == NULL) { @@ -869,18 +1045,18 @@ TclLookupSimpleVar( } } } else { /* Local var: look in frame varFramePtr. */ - int localLen, localCt = varFramePtr->numCompiledLocals; + int localCt = varFramePtr->numCompiledLocals; Tcl_Obj **objPtrPtr = &varFramePtr->localCachePtr->varName0; - const char *localNameStr; + int i; for (i=0 ; i<localCt ; i++, objPtrPtr++) { register Tcl_Obj *objPtr = *objPtrPtr; if (objPtr) { - localNameStr = TclGetStringFromObj(objPtr, &localLen); + char *localName = TclGetString(objPtr); - if ((varLen == localLen) && (varName[0] == localNameStr[0]) - && !memcmp(varName, localNameStr, varLen)) { + if ((varName[0] == localName[0]) + && (strcmp(varName, localName) == 0)) { *indexPtr = i; return (Var *) &varFramePtr->compiledLocals[i]; } @@ -889,7 +1065,8 @@ TclLookupSimpleVar( tablePtr = varFramePtr->varTablePtr; if (create) { if (tablePtr == NULL) { - tablePtr = ckalloc(sizeof(TclVarHashTable)); + tablePtr = (TclVarHashTable *) + ckalloc(sizeof(TclVarHashTable)); TclInitVarHashTable(tablePtr, NULL); varFramePtr->varTablePtr = tablePtr; } @@ -969,7 +1146,6 @@ TclLookupArrayElement( int isNew; Var *varPtr; TclVarHashTable *tablePtr; - Namespace *nsPtr; /* * We're dealing with an array element. Make sure the variable is an array @@ -977,12 +1153,12 @@ TclLookupArrayElement( */ if (TclIsVarUndefined(arrayPtr) && !TclIsVarArrayElement(arrayPtr)) { + Namespace *nsPtr; + if (!createArray) { if (flags & TCL_LEAVE_ERR_MSG) { TclObjVarErrMsg(interp, arrayNamePtr, elNamePtr, msg, noSuchVar, index); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "VARNAME", - arrayNamePtr?TclGetString(arrayNamePtr):NULL, NULL); } return NULL; } @@ -996,14 +1172,12 @@ TclLookupArrayElement( if (flags & TCL_LEAVE_ERR_MSG) { TclObjVarErrMsg(interp, arrayNamePtr, elNamePtr, msg, danglingVar, index); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "VARNAME", - arrayNamePtr?TclGetString(arrayNamePtr):NULL, NULL); } return NULL; } TclSetVarArray(arrayPtr); - tablePtr = ckalloc(sizeof(TclVarHashTable)); + tablePtr = (TclVarHashTable *) ckalloc(sizeof(TclVarHashTable)); arrayPtr->value.tablePtr = tablePtr; if (TclIsVarInHash(arrayPtr) && TclGetVarNsPtr(arrayPtr)) { @@ -1016,8 +1190,6 @@ TclLookupArrayElement( if (flags & TCL_LEAVE_ERR_MSG) { TclObjVarErrMsg(interp, arrayNamePtr, elNamePtr, msg, needArray, index); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "VARNAME", - arrayNamePtr?TclGetString(arrayNamePtr):NULL, NULL); } return NULL; } @@ -1077,15 +1249,7 @@ Tcl_GetVar( * TCL_NAMESPACE_ONLY or TCL_LEAVE_ERR_MSG * bits. */ { - Tcl_Obj *varNamePtr = Tcl_NewStringObj(varName, -1); - Tcl_Obj *resultPtr = Tcl_ObjGetVar2(interp, varNamePtr, NULL, flags); - - TclDecrRefCount(varNamePtr); - - if (resultPtr == NULL) { - return NULL; - } - return TclGetString(resultPtr); + return Tcl_GetVar2(interp, varName, NULL, flags); } /* @@ -1123,24 +1287,13 @@ Tcl_GetVar2( * TCL_NAMESPACE_ONLY and TCL_LEAVE_ERR_MSG * * bits. */ { - Tcl_Obj *resultPtr; - Tcl_Obj *part2Ptr = NULL, *part1Ptr = Tcl_NewStringObj(part1, -1); - - if (part2) { - part2Ptr = Tcl_NewStringObj(part2, -1); - Tcl_IncrRefCount(part2Ptr); - } - - resultPtr = Tcl_ObjGetVar2(interp, part1Ptr, part2Ptr, flags); + Tcl_Obj *objPtr; - Tcl_DecrRefCount(part1Ptr); - if (part2Ptr) { - Tcl_DecrRefCount(part2Ptr); - } - if (resultPtr == NULL) { + objPtr = Tcl_GetVar2Ex(interp, part1, part2, flags); + if (objPtr == NULL) { return NULL; } - return TclGetString(resultPtr); + return TclGetString(objPtr); } /* @@ -1331,7 +1484,6 @@ TclPtrGetVar( */ errorReturn: - Tcl_SetErrorCode(interp, "TCL", "READ", "VARNAME", NULL); if (TclIsVarUndefined(varPtr)) { TclCleanupVar(varPtr, arrayPtr); } @@ -1422,17 +1574,7 @@ Tcl_SetVar( * TCL_APPEND_VALUE, TCL_LIST_ELEMENT, * TCL_LEAVE_ERR_MSG. */ { - Tcl_Obj *varValuePtr, *varNamePtr = Tcl_NewStringObj(varName, -1); - - Tcl_IncrRefCount(varNamePtr); - varValuePtr = Tcl_ObjSetVar2(interp, varNamePtr, NULL, - Tcl_NewStringObj(newValue, -1), flags); - Tcl_DecrRefCount(varNamePtr); - - if (varValuePtr == NULL) { - return NULL; - } - return TclGetString(varValuePtr); + return Tcl_SetVar2(interp, varName, NULL, newValue, flags); } /* @@ -1666,7 +1808,6 @@ TclPtrSetVar( Tcl_Obj *oldValuePtr; Tcl_Obj *resultPtr = NULL; int result; - int cleanupOnEarlyError = (newValuePtr->refCount == 0); /* * If the variable is in a hashtable and its hPtr field is NULL, then we @@ -1681,11 +1822,9 @@ TclPtrSetVar( if (TclIsVarArrayElement(varPtr)) { TclObjVarErrMsg(interp, part1Ptr, part2Ptr, "set", danglingElement, index); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "ELEMENT", NULL); } else { TclObjVarErrMsg(interp, part1Ptr, part2Ptr, "set", danglingVar, index); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "VARNAME", NULL); } } goto earlyError; @@ -1698,7 +1837,6 @@ TclPtrSetVar( if (TclIsVarArray(varPtr)) { if (flags & TCL_LEAVE_ERR_MSG) { TclObjVarErrMsg(interp, part1Ptr, part2Ptr, "set", isArray,index); - Tcl_SetErrorCode(interp, "TCL", "WRITE", "ARRAY", NULL); } goto earlyError; } @@ -1708,7 +1846,7 @@ TclPtrSetVar( * requested. This was done for INST_LAPPEND_* but that was inconsistent * with the non-bc instruction, and would cause failures trying to * lappend to any non-existing ::env var, which is inconsistent with - * documented behavior. [Bug #3057639]. + * documented behavior. [Bug #3057639] */ if ((flags & TCL_TRACE_READS) && ((varPtr->flags & VAR_TRACED_READ) @@ -1732,6 +1870,17 @@ TclPtrSetVar( varPtr->value.objPtr = NULL; } if (flags & (TCL_APPEND_VALUE|TCL_LIST_ELEMENT)) { +#if 0 + /* + * Can't happen now! + */ + + if (TclIsVarUndefined(varPtr) && (oldValuePtr != NULL)) { + TclDecrRefCount(oldValuePtr); /* Discard old value. */ + varPtr->value.objPtr = NULL; + oldValuePtr = NULL; + } +#endif if (flags & TCL_LIST_ELEMENT) { /* Append list element. */ if (oldValuePtr == NULL) { TclNewObj(oldValuePtr); @@ -1760,7 +1909,12 @@ TclPtrSetVar( if (Tcl_IsShared(oldValuePtr)) { /* Append to copy. */ varPtr->value.objPtr = Tcl_DuplicateObj(oldValuePtr); - TclContinuationsCopy(varPtr->value.objPtr, oldValuePtr); + /* + * TIP #280. + * Ensure that the continuation line data for the string + * is not lost and applies to the extended script as well. + */ + TclContinuationsCopy (varPtr->value.objPtr, oldValuePtr); TclDecrRefCount(oldValuePtr); oldValuePtr = varPtr->value.objPtr; @@ -1821,16 +1975,13 @@ TclPtrSetVar( */ cleanup: - if (resultPtr == NULL) { - Tcl_SetErrorCode(interp, "TCL", "WRITE", "VARNAME", NULL); - } if (TclIsVarUndefined(varPtr)) { TclCleanupVar(varPtr, arrayPtr); } return resultPtr; earlyError: - if (cleanupOnEarlyError) { + if (newValuePtr->refCount == 0) { Tcl_DecrRefCount(newValuePtr); } goto cleanup; @@ -1885,8 +2036,8 @@ TclIncrObjVar2( varPtr = TclObjLookupVarEx(interp, part1Ptr, part2Ptr, flags, "read", 1, 1, &arrayPtr); if (varPtr == NULL) { - Tcl_AddErrorInfo(interp, - "\n (reading value of variable to increment)"); + Tcl_AddObjErrorInfo(interp, + "\n (reading value of variable to increment)", -1); return NULL; } return TclPtrIncrObjVar(interp, varPtr, arrayPtr, part1Ptr, part2Ptr, @@ -2017,21 +2168,7 @@ Tcl_UnsetVar( * TCL_GLOBAL_ONLY, TCL_NAMESPACE_ONLY or * TCL_LEAVE_ERR_MSG. */ { - int result; - Tcl_Obj *varNamePtr; - - varNamePtr = Tcl_NewStringObj(varName, -1); - Tcl_IncrRefCount(varNamePtr); - - /* - * Filter to pass through only the flags this interface supports. - */ - - flags &= (TCL_GLOBAL_ONLY|TCL_NAMESPACE_ONLY|TCL_LEAVE_ERR_MSG); - result = TclObjUnsetVar2(interp, varNamePtr, NULL, flags); - - Tcl_DecrRefCount(varNamePtr); - return result; + return Tcl_UnsetVar2(interp, varName, NULL, flags); } /* @@ -2117,7 +2254,10 @@ TclObjUnsetVar2( * TCL_GLOBAL_ONLY, TCL_NAMESPACE_ONLY, * TCL_LEAVE_ERR_MSG. */ { - Var *varPtr, *arrayPtr; + Var *varPtr; + Interp *iPtr = (Interp *) interp; + Var *arrayPtr; + int result; varPtr = TclObjLookupVarEx(interp, part1Ptr, part2Ptr, flags, "unset", /*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr); @@ -2125,52 +2265,7 @@ TclObjUnsetVar2( return TCL_ERROR; } - return TclPtrUnsetVar(interp, varPtr, arrayPtr, part1Ptr, part2Ptr, flags, - -1); -} - -/* - *---------------------------------------------------------------------- - * - * TclPtrUnsetVar -- - * - * Delete a variable, given the pointers to the variable's (and possibly - * containing array's) VAR structure. - * - * Results: - * Returns TCL_OK if the variable was successfully deleted, TCL_ERROR if - * the variable can't be unset. In the event of an error, if the - * TCL_LEAVE_ERR_MSG flag is set then an error message is left in the - * interp's result. - * - * Side effects: - * If varPtr and arrayPtr indicate a local or global variable in interp, - * it is deleted. If varPtr is an array reference and part2Ptr is NULL, - * then the whole array is deleted. - * - *---------------------------------------------------------------------- - */ - -int -TclPtrUnsetVar( - Tcl_Interp *interp, /* Command interpreter in which varName is to - * be looked up. */ - register Var *varPtr, /* The variable to be unset. */ - Var *arrayPtr, /* NULL for scalar variables, pointer to the - * containing array otherwise. */ - Tcl_Obj *part1Ptr, /* Name of an array (if part2 is non-NULL) or - * the name of a variable. */ - Tcl_Obj *part2Ptr, /* If non-NULL, gives the name of an element - * in the array part1. */ - const int flags, /* OR-ed combination of any of - * TCL_GLOBAL_ONLY, TCL_NAMESPACE_ONLY, - * TCL_LEAVE_ERR_MSG. */ - int index) /* Index into the local variable table of the - * variable, or -1. Only used when part1Ptr is - * NULL. */ -{ - Interp *iPtr = (Interp *) interp; - int result = (TclIsVarUndefined(varPtr)? TCL_ERROR : TCL_OK); + result = (TclIsVarUndefined(varPtr)? TCL_ERROR : TCL_OK); /* * Keep the variable alive until we're done with it. We used to @@ -2183,7 +2278,7 @@ TclPtrUnsetVar( VarHashRefCount(varPtr)++; } - UnsetVarStruct(varPtr, arrayPtr, iPtr, part1Ptr, part2Ptr, flags, index); + UnsetVarStruct(varPtr, arrayPtr, iPtr, part1Ptr, part2Ptr, flags); /* * It's an error to unset an undefined variable. @@ -2192,11 +2287,23 @@ TclPtrUnsetVar( if (result != TCL_OK) { if (flags & TCL_LEAVE_ERR_MSG) { TclObjVarErrMsg(interp, part1Ptr, part2Ptr, "unset", - ((arrayPtr == NULL) ? noSuchVar : noSuchElement), index); - Tcl_SetErrorCode(interp, "TCL", "UNSET", "VARNAME", NULL); + ((arrayPtr == NULL) ? noSuchVar : noSuchElement), -1); } } +#if ENABLE_NS_VARNAME_CACHING + /* + * Try to avoid keeping the Var struct allocated due to a tclNsVarNameType + * keeping a reference. This removes some additional exteriorisations of + * [Bug 736729], but may be a good thing independently of the bug. + */ + + if (part1Ptr->typePtr == &tclNsVarNameType) { + TclFreeIntRep(part1Ptr); + part1Ptr->typePtr = NULL; + } +#endif + /* * Finally, if the variable is truly not in use then free up its Var * structure and remove it from its hash table, if any. The ref count of @@ -2236,8 +2343,7 @@ UnsetVarStruct( Interp *iPtr, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, - int flags, - int index) + int flags) { Var dummyVar; int traced = TclIsVarTraced(varPtr) @@ -2277,7 +2383,6 @@ UnsetVarStruct( if (traced) { VarTrace *tracePtr = NULL; - Tcl_HashEntry *tPtr; if (TclIsVarTraced(&dummyVar)) { /* @@ -2286,38 +2391,44 @@ UnsetVarStruct( */ int isNew; + Tcl_HashEntry *tPtr = + Tcl_FindHashEntry(&iPtr->varTraces, (char *) varPtr); - tPtr = Tcl_FindHashEntry(&iPtr->varTraces, varPtr); tracePtr = Tcl_GetHashValue(tPtr); varPtr->flags &= ~VAR_ALL_TRACES; Tcl_DeleteHashEntry(tPtr); if (dummyVar.flags & VAR_TRACED_UNSET) { tPtr = Tcl_CreateHashEntry(&iPtr->varTraces, - &dummyVar, &isNew); + (char *) &dummyVar, &isNew); Tcl_SetHashValue(tPtr, tracePtr); + } else { + tPtr = NULL; } } if ((dummyVar.flags & VAR_TRACED_UNSET) || (arrayPtr && (arrayPtr->flags & VAR_TRACED_UNSET))) { + Tcl_HashEntry *tPtr = NULL; + dummyVar.flags &= ~VAR_TRACE_ACTIVE; TclObjCallVarTraces(iPtr, arrayPtr, &dummyVar, part1Ptr, part2Ptr, (flags & (TCL_GLOBAL_ONLY|TCL_NAMESPACE_ONLY)) | TCL_TRACE_UNSETS, - /* leaveErrMsg */ 0, index); + /* leaveErrMsg */ 0, -1); /* * The traces that we just called may have triggered a change in - * the set of traces. If so, reload the traces to manipulate. + * the set of traces. [Bug 2629338] */ tracePtr = NULL; if (TclIsVarTraced(&dummyVar)) { - tPtr = Tcl_FindHashEntry(&iPtr->varTraces, &dummyVar); - if (tPtr) { - tracePtr = Tcl_GetHashValue(tPtr); - Tcl_DeleteHashEntry(tPtr); - } + tPtr = Tcl_FindHashEntry(&iPtr->varTraces, (char *) &dummyVar); + tracePtr = Tcl_GetHashValue(tPtr); + } + + if (tPtr) { + Tcl_DeleteHashEntry(tPtr); } } @@ -2329,7 +2440,7 @@ UnsetVarStruct( tracePtr = tracePtr->nextPtr; prevPtr->nextPtr = NULL; - Tcl_EventuallyFree(prevPtr, TCL_DYNAMIC); + Tcl_EventuallyFree((ClientData) prevPtr, TCL_DYNAMIC); } for (activePtr = iPtr->activeVarTracePtr; activePtr != NULL; activePtr = activePtr->nextPtr) { @@ -2359,8 +2470,7 @@ UnsetVarStruct( */ DeleteArray(iPtr, part1Ptr, (Var *) &dummyVar, (flags - & (TCL_GLOBAL_ONLY|TCL_NAMESPACE_ONLY)) | TCL_TRACE_UNSETS, - index); + & (TCL_GLOBAL_ONLY|TCL_NAMESPACE_ONLY)) | TCL_TRACE_UNSETS); } else if (TclIsVarLink(&dummyVar)) { /* * For global/upvar variables referenced in procedures, decrement the @@ -2410,7 +2520,7 @@ Tcl_UnsetObjCmd( Tcl_Obj *const objv[]) /* Argument objects. */ { register int i, flags = TCL_LEAVE_ERR_MSG; - register const char *name; + register char *name; if (objc == 1) { /* @@ -2477,13 +2587,11 @@ Tcl_AppendObjCmd( int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { - Var *varPtr, *arrayPtr; register Tcl_Obj *varValuePtr = NULL; /* Initialized to avoid compiler warning. */ - int i; if (objc < 2) { - Tcl_WrongNumArgs(interp, 1, objv, "varName ?value ...?"); + Tcl_WrongNumArgs(interp, 1, objv, "varName ?value value ...?"); return TCL_ERROR; } @@ -2493,6 +2601,9 @@ Tcl_AppendObjCmd( return TCL_ERROR; } } else { + Var *arrayPtr, *varPtr; + int i; + varPtr = TclObjLookupVarEx(interp, objv[1], NULL, TCL_LEAVE_ERR_MSG, "set", /*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr); if (varPtr == NULL) { @@ -2544,12 +2655,11 @@ Tcl_LappendObjCmd( Tcl_Obj *const objv[]) /* Argument objects. */ { Tcl_Obj *varValuePtr, *newValuePtr; - int numElems, createdNewObj; - Var *varPtr, *arrayPtr; + int numElems; int result; if (objc < 2) { - Tcl_WrongNumArgs(interp, 1, objv, "varName ?value ...?"); + Tcl_WrongNumArgs(interp, 1, objv, "varName ?value value ...?"); return TCL_ERROR; } if (objc == 2) { @@ -2573,6 +2683,9 @@ Tcl_LappendObjCmd( } } } else { + Var *varPtr, *arrayPtr; + int createdNewObj = 0; + /* * We have arguments to append. We used to call Tcl_SetVar2 to append * each argument one at a time to ensure that traces were run for each @@ -2583,8 +2696,6 @@ Tcl_LappendObjCmd( * copy to modify: this is "copy on write". */ - createdNewObj = 0; - /* * Protect the variable pointers around the TclPtrGetVar call * to insure that they remain valid even if the variable was undefined @@ -2662,206 +2773,66 @@ Tcl_LappendObjCmd( /* *---------------------------------------------------------------------- * - * TclArraySet -- + * Tcl_ArrayObjCmd -- * - * Set the elements of an array. If there are no elements to set, create - * an empty array. This routine is used by the Tcl_ArrayObjCmd and by the - * TclSetupEnv routine. + * This object-based function is invoked to process the "array" Tcl + * command. See the user documentation for details on what it does. * * Results: * A standard Tcl result object. * * Side effects: - * A variable will be created if one does not already exist. - * Callers must Incr arrayNameObj if they pland to Decr it. + * See the user documentation. * *---------------------------------------------------------------------- */ + /* ARGSUSED */ int -TclArraySet( +Tcl_ArrayObjCmd( + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ - Tcl_Obj *arrayNameObj, /* The array name. */ - Tcl_Obj *arrayElemObj) /* The array elements list or dict. If this is - * NULL, create an empty array. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { - Var *varPtr, *arrayPtr; - int result, i; - - varPtr = TclObjLookupVarEx(interp, arrayNameObj, NULL, - /*flags*/ TCL_LEAVE_ERR_MSG, /*msg*/ "set", /*createPart1*/ 1, - /*createPart2*/ 1, &arrayPtr); - if (varPtr == NULL) { - return TCL_ERROR; - } - if (arrayPtr) { - CleanupVar(varPtr, arrayPtr); - TclObjVarErrMsg(interp, arrayNameObj, NULL, "set", needArray, -1); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "VARNAME", - TclGetString(arrayNameObj), NULL); - return TCL_ERROR; - } - - if (arrayElemObj == NULL) { - goto ensureArray; - } - /* - * Install the contents of the dictionary or list into the array. + * The list of constants below should match the arrayOptions string array + * below. */ - if (arrayElemObj->typePtr == &tclDictType) { - Tcl_Obj *keyPtr, *valuePtr; - Tcl_DictSearch search; - int done; - - if (Tcl_DictObjSize(interp, arrayElemObj, &done) != TCL_OK) { - return TCL_ERROR; - } - if (done == 0) { - /* - * Empty, so we'll just force the array to be properly existing - * instead. - */ - - goto ensureArray; - } - - /* - * Don't need to look at result of Tcl_DictObjFirst as we've just - * successfully used a dictionary operation on the same object. - */ - - for (Tcl_DictObjFirst(interp, arrayElemObj, &search, - &keyPtr, &valuePtr, &done) ; !done ; - Tcl_DictObjNext(&search, &keyPtr, &valuePtr, &done)) { - /* - * At this point, it would be nice if the key was directly usable - * by the array. This isn't the case though. - */ - - Var *elemVarPtr = TclLookupArrayElement(interp, arrayNameObj, - keyPtr, TCL_LEAVE_ERR_MSG, "set", 1, 1, varPtr, -1); - - if ((elemVarPtr == NULL) || - (TclPtrSetVar(interp, elemVarPtr, varPtr, arrayNameObj, - keyPtr, valuePtr, TCL_LEAVE_ERR_MSG, -1) == NULL)) { - Tcl_DictObjDone(&search); - return TCL_ERROR; - } - } - return TCL_OK; - } else { - /* - * Not a dictionary, so assume (and convert to, for backward- - * -compatibility reasons) a list. - */ - - int elemLen; - Tcl_Obj **elemPtrs, *copyListObj; - - result = TclListObjGetElements(interp, arrayElemObj, - &elemLen, &elemPtrs); - if (result != TCL_OK) { - return result; - } - if (elemLen & 1) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "list must have an even number of elements", -1)); - Tcl_SetErrorCode(interp, "TCL", "ARGUMENT", "FORMAT", NULL); - return TCL_ERROR; - } - if (elemLen == 0) { - goto ensureArray; - } - - /* - * We needn't worry about traces invalidating arrayPtr: should that be - * the case, TclPtrSetVar will return NULL so that we break out of the - * loop and return an error. - */ + enum { + ARRAY_ANYMORE, ARRAY_DONESEARCH, ARRAY_EXISTS, ARRAY_GET, + ARRAY_NAMES, ARRAY_NEXTELEMENT, ARRAY_SET, ARRAY_SIZE, + ARRAY_STARTSEARCH, ARRAY_STATISTICS, ARRAY_UNSET + }; + static const char *arrayOptions[] = { + "anymore", "donesearch", "exists", "get", "names", "nextelement", + "set", "size", "startsearch", "statistics", "unset", NULL + }; - copyListObj = TclListObjCopy(NULL, arrayElemObj); - for (i=0 ; i<elemLen ; i+=2) { - Var *elemVarPtr = TclLookupArrayElement(interp, arrayNameObj, - elemPtrs[i], TCL_LEAVE_ERR_MSG, "set", 1, 1, varPtr, -1); + Interp *iPtr = (Interp *) interp; + Var *varPtr, *arrayPtr; + Tcl_HashEntry *hPtr; + Tcl_Obj *varNamePtr; + int notArray; + int index, result; - if ((elemVarPtr == NULL) || - (TclPtrSetVar(interp, elemVarPtr, varPtr, arrayNameObj, - elemPtrs[i],elemPtrs[i+1],TCL_LEAVE_ERR_MSG,-1) == NULL)){ - result = TCL_ERROR; - break; - } - } - Tcl_DecrRefCount(copyListObj); - return result; + if (objc < 3) { + Tcl_WrongNumArgs(interp, 1, objv, "option arrayName ?arg ...?"); + return TCL_ERROR; } - /* - * The list is empty make sure we have an array, or create one if - * necessary. - */ - - ensureArray: - if (varPtr != NULL) { - if (TclIsVarArray(varPtr)) { - /* - * Already an array, done. - */ - - return TCL_OK; - } - if (TclIsVarArrayElement(varPtr) || !TclIsVarUndefined(varPtr)) { - /* - * Either an array element, or a scalar: lose! - */ - - TclObjVarErrMsg(interp, arrayNameObj, NULL, "array set", - needArray, -1); - Tcl_SetErrorCode(interp, "TCL", "WRITE", "ARRAY", NULL); - return TCL_ERROR; - } + if (Tcl_GetIndexFromObj(interp, objv[1], arrayOptions, "option", + 0, &index) != TCL_OK) { + return TCL_ERROR; } - TclSetVarArray(varPtr); - varPtr->value.tablePtr = ckalloc(sizeof(TclVarHashTable)); - TclInitVarHashTable(varPtr->value.tablePtr, TclGetVarNsPtr(varPtr)); - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * ArrayStartSearchCmd -- - * - * This object-based function is invoked to process the "array - * startsearch" Tcl command. See the user documentation for details on - * what it does. - * - * Results: - * A standard Tcl result object. - * - * Side effects: - * See the user documentation. - * - *---------------------------------------------------------------------- - */ - - /* ARGSUSED */ - -static Var * -VerifyArray( - Tcl_Interp *interp, - Tcl_Obj *varNameObj) -{ - Interp *iPtr = (Interp *) interp; - const char *varName = TclGetString(varNameObj); - Var *arrayPtr; /* - * Locate the array variable. + * Locate the array variable */ - Var *varPtr = TclObjLookupVarEx(interp, varNameObj, NULL, /*flags*/ 0, + varNamePtr = objv[2]; + varPtr = TclObjLookupVarEx(interp, varNamePtr, NULL, /*flags*/ 0, /*msg*/ 0, /*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr); /* @@ -2871,10 +2842,10 @@ VerifyArray( if (varPtr && (varPtr->flags & VAR_TRACED_ARRAY) && (TclIsVarArray(varPtr) || TclIsVarUndefined(varPtr))) { - if (TclObjCallVarTraces(iPtr, arrayPtr, varPtr, varNameObj, NULL, + if (TclObjCallVarTraces(iPtr, arrayPtr, varPtr, varNamePtr, NULL, (TCL_LEAVE_ERR_MSG|TCL_NAMESPACE_ONLY|TCL_GLOBAL_ONLY| TCL_TRACE_ARRAY), /* leaveErrMsg */ 1, -1) == TCL_ERROR) { - return NULL; + return TCL_ERROR; } } @@ -2884,1165 +2855,682 @@ VerifyArray( * traces. */ - if ((varPtr == NULL) || !TclIsVarArray(varPtr) || TclIsVarUndefined(varPtr)) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "\"%s\" isn't an array", varName)); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "ARRAY", varName, NULL); - return NULL; - } - - return varPtr; -} - -static int -ArrayStartSearchCmd( - ClientData clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - Interp *iPtr = (Interp *) interp; - Var *varPtr; - Tcl_HashEntry *hPtr; - int isNew; - ArraySearch *searchPtr; - - if (objc != 2) { - Tcl_WrongNumArgs(interp, 1, objv, "arrayName"); - return TCL_ERROR; - } - - varPtr = VerifyArray(interp, objv[1]); - if (varPtr == NULL) { - return TCL_ERROR; - } - - /* - * Make a new array search with a free name. - */ - - searchPtr = ckalloc(sizeof(ArraySearch)); - hPtr = Tcl_CreateHashEntry(&iPtr->varSearches, varPtr, &isNew); - if (isNew) { - searchPtr->id = 1; - varPtr->flags |= VAR_SEARCH_ACTIVE; - searchPtr->nextPtr = NULL; - } else { - searchPtr->id = ((ArraySearch *) Tcl_GetHashValue(hPtr))->id + 1; - searchPtr->nextPtr = Tcl_GetHashValue(hPtr); - } - searchPtr->varPtr = varPtr; - searchPtr->nextEntry = VarHashFirstEntry(varPtr->value.tablePtr, - &searchPtr->search); - Tcl_SetHashValue(hPtr, searchPtr); - searchPtr->name = Tcl_ObjPrintf("s-%d-%s", searchPtr->id, TclGetString(objv[1])); - Tcl_IncrRefCount(searchPtr->name); - Tcl_SetObjResult(interp, searchPtr->name); - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * ArrayAnyMoreCmd -- - * - * This object-based function is invoked to process the "array anymore" - * Tcl command. See the user documentation for details on what it does. - * - * Results: - * A standard Tcl result object. - * - * Side effects: - * See the user documentation. - * - *---------------------------------------------------------------------- - */ - - /* ARGSUSED */ -static int -ArrayAnyMoreCmd( - ClientData clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - Interp *iPtr = (Interp *) interp; - Var *varPtr; - Tcl_Obj *varNameObj, *searchObj; - int gotValue; - ArraySearch *searchPtr; - - if (objc != 3) { - Tcl_WrongNumArgs(interp, 1, objv, "arrayName searchId"); - return TCL_ERROR; - } - varNameObj = objv[1]; - searchObj = objv[2]; - - varPtr = VerifyArray(interp, varNameObj); - if (varPtr == NULL) { - return TCL_ERROR; - } - - /* - * Get the search. - */ - - searchPtr = ParseSearchId(interp, varPtr, varNameObj, searchObj); - if (searchPtr == NULL) { - return TCL_ERROR; + notArray = 0; + if ((varPtr == NULL) || !TclIsVarArray(varPtr) + || TclIsVarUndefined(varPtr)) { + notArray = 1; } - /* - * Scan forward to find if there are any further elements in the array - * that are defined. - */ + switch (index) { + case ARRAY_ANYMORE: { + ArraySearch *searchPtr; - while (1) { - if (searchPtr->nextEntry != NULL) { - varPtr = VarHashGetValue(searchPtr->nextEntry); - if (!TclIsVarUndefined(varPtr)) { - gotValue = 1; - break; - } + if (objc != 4) { + Tcl_WrongNumArgs(interp, 2, objv, "arrayName searchId"); + return TCL_ERROR; } - searchPtr->nextEntry = Tcl_NextHashEntry(&searchPtr->search); - if (searchPtr->nextEntry == NULL) { - gotValue = 0; - break; + if (notArray) { + goto error; } - } - Tcl_SetObjResult(interp, iPtr->execEnvPtr->constants[gotValue]); - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * ArrayNextElementCmd -- - * - * This object-based function is invoked to process the "array - * nextelement" Tcl command. See the user documentation for details on - * what it does. - * - * Results: - * A standard Tcl result object. - * - * Side effects: - * See the user documentation. - * - *---------------------------------------------------------------------- - */ - - /* ARGSUSED */ -static int -ArrayNextElementCmd( - ClientData clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - Var *varPtr; - Tcl_Obj *varNameObj, *searchObj; - ArraySearch *searchPtr; - - if (objc != 3) { - Tcl_WrongNumArgs(interp, 1, objv, "arrayName searchId"); - return TCL_ERROR; - } - varNameObj = objv[1]; - searchObj = objv[2]; - - varPtr = VerifyArray(interp, varNameObj); - if (varPtr == NULL) { - return TCL_ERROR; - } - - /* - * Get the search. - */ - - searchPtr = ParseSearchId(interp, varPtr, varNameObj, searchObj); - if (searchPtr == NULL) { - return TCL_ERROR; - } - - /* - * Get the next element from the search, or the empty string on - * exhaustion. Note that the [array anymore] command may well have already - * pulled a value from the hash enumeration, so we have to check the cache - * there first. - */ - - while (1) { - Tcl_HashEntry *hPtr = searchPtr->nextEntry; - - if (hPtr == NULL) { - hPtr = Tcl_NextHashEntry(&searchPtr->search); - if (hPtr == NULL) { + searchPtr = ParseSearchId(interp, varPtr, varNamePtr, objv[3]); + if (searchPtr == NULL) { + return TCL_ERROR; + } + while (1) { + if (searchPtr->nextEntry != NULL) { + Var *varPtr2 = VarHashGetValue(searchPtr->nextEntry); + if (!TclIsVarUndefined(varPtr2)) { + break; + } + } + searchPtr->nextEntry = Tcl_NextHashEntry(&searchPtr->search); + if (searchPtr->nextEntry == NULL) { + Tcl_SetObjResult(interp, iPtr->execEnvPtr->constants[0]); return TCL_OK; } - } else { - searchPtr->nextEntry = NULL; - } - varPtr = VarHashGetValue(hPtr); - if (!TclIsVarUndefined(varPtr)) { - Tcl_SetObjResult(interp, VarHashGetKey(varPtr)); - return TCL_OK; } + Tcl_SetObjResult(interp, iPtr->execEnvPtr->constants[1]); + break; } -} - -/* - *---------------------------------------------------------------------- - * - * ArrayDoneSearchCmd -- - * - * This object-based function is invoked to process the "array - * donesearch" Tcl command. See the user documentation for details on - * what it does. - * - * Results: - * A standard Tcl result object. - * - * Side effects: - * See the user documentation. - * - *---------------------------------------------------------------------- - */ - - /* ARGSUSED */ -static int -ArrayDoneSearchCmd( - ClientData clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - Interp *iPtr = (Interp *) interp; - Var *varPtr; - Tcl_HashEntry *hPtr; - Tcl_Obj *varNameObj, *searchObj; - ArraySearch *searchPtr, *prevPtr; - - if (objc != 3) { - Tcl_WrongNumArgs(interp, 1, objv, "arrayName searchId"); - return TCL_ERROR; - } - varNameObj = objv[1]; - searchObj = objv[2]; - - varPtr = VerifyArray(interp, varNameObj); - if (varPtr == NULL) { - return TCL_ERROR; - } - - /* - * Get the search. - */ + case ARRAY_DONESEARCH: { + ArraySearch *searchPtr, *prevPtr; - searchPtr = ParseSearchId(interp, varPtr, varNameObj, searchObj); - if (searchPtr == NULL) { - return TCL_ERROR; + if (objc != 4) { + Tcl_WrongNumArgs(interp, 2, objv, "arrayName searchId"); + return TCL_ERROR; + } + if (notArray) { + goto error; + } + searchPtr = ParseSearchId(interp, varPtr, varNamePtr, objv[3]); + if (searchPtr == NULL) { + return TCL_ERROR; + } + hPtr = Tcl_FindHashEntry(&iPtr->varSearches,(char *) varPtr); + if (searchPtr == Tcl_GetHashValue(hPtr)) { + if (searchPtr->nextPtr) { + Tcl_SetHashValue(hPtr, searchPtr->nextPtr); + } else { + varPtr->flags &= ~VAR_SEARCH_ACTIVE; + Tcl_DeleteHashEntry(hPtr); + } + } else { + for (prevPtr=Tcl_GetHashValue(hPtr) ;; prevPtr=prevPtr->nextPtr) { + if (prevPtr->nextPtr == searchPtr) { + prevPtr->nextPtr = searchPtr->nextPtr; + break; + } + } + } + ckfree((char *) searchPtr); + break; } + case ARRAY_NEXTELEMENT: { + ArraySearch *searchPtr; + Tcl_HashEntry *hPtr; + Var *varPtr2; - /* - * Unhook the search from the list of searches associated with the - * variable. - */ - - hPtr = Tcl_FindHashEntry(&iPtr->varSearches, varPtr); - if (searchPtr == Tcl_GetHashValue(hPtr)) { - if (searchPtr->nextPtr) { - Tcl_SetHashValue(hPtr, searchPtr->nextPtr); - } else { - varPtr->flags &= ~VAR_SEARCH_ACTIVE; - Tcl_DeleteHashEntry(hPtr); + if (objc != 4) { + Tcl_WrongNumArgs(interp, 2, objv, "arrayName searchId"); + return TCL_ERROR; } - } else { - for (prevPtr=Tcl_GetHashValue(hPtr) ;; prevPtr=prevPtr->nextPtr) { - if (prevPtr->nextPtr == searchPtr) { - prevPtr->nextPtr = searchPtr->nextPtr; + if (notArray) { + goto error; + } + searchPtr = ParseSearchId(interp, varPtr, varNamePtr, objv[3]); + if (searchPtr == NULL) { + return TCL_ERROR; + } + while (1) { + hPtr = searchPtr->nextEntry; + if (hPtr == NULL) { + hPtr = Tcl_NextHashEntry(&searchPtr->search); + if (hPtr == NULL) { + return TCL_OK; + } + } else { + searchPtr->nextEntry = NULL; + } + varPtr2 = VarHashGetValue(hPtr); + if (!TclIsVarUndefined(varPtr2)) { break; } } + Tcl_SetObjResult(interp, VarHashGetKey(varPtr2)); + break; } - Tcl_DecrRefCount(searchPtr->name); - ckfree(searchPtr); - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * ArrayExistsCmd -- - * - * This object-based function is invoked to process the "array exists" - * Tcl command. See the user documentation for details on what it does. - * - * Results: - * A standard Tcl result object. - * - * Side effects: - * See the user documentation. - * - *---------------------------------------------------------------------- - */ - - /* ARGSUSED */ -static int -ArrayExistsCmd( - ClientData clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - Interp *iPtr = (Interp *) interp; - Var *varPtr, *arrayPtr; - Tcl_Obj *arrayNameObj; - int notArray; - - if (objc != 2) { - Tcl_WrongNumArgs(interp, 1, objv, "arrayName"); - return TCL_ERROR; - } - arrayNameObj = objv[1]; - - /* - * Locate the array variable. - */ - - varPtr = TclObjLookupVarEx(interp, arrayNameObj, NULL, /*flags*/ 0, - /*msg*/ 0, /*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr); + case ARRAY_STARTSEARCH: { + ArraySearch *searchPtr; + int isNew; + char *varName = TclGetString(varNamePtr); - /* - * Special array trace used to keep the env array in sync for array names, - * array get, etc. - */ - - if (varPtr && (varPtr->flags & VAR_TRACED_ARRAY) - && (TclIsVarArray(varPtr) || TclIsVarUndefined(varPtr))) { - if (TclObjCallVarTraces(iPtr, arrayPtr, varPtr, arrayNameObj, NULL, - (TCL_LEAVE_ERR_MSG|TCL_NAMESPACE_ONLY|TCL_GLOBAL_ONLY| - TCL_TRACE_ARRAY), /* leaveErrMsg */ 1, -1) == TCL_ERROR) { + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "arrayName"); return TCL_ERROR; } - } - - /* - * Check whether we've actually got an array variable. - */ - - notArray = ((varPtr == NULL) || !TclIsVarArray(varPtr) - || TclIsVarUndefined(varPtr)); - Tcl_SetObjResult(interp, iPtr->execEnvPtr->constants[!notArray]); - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * ArrayGetCmd -- - * - * This object-based function is invoked to process the "array get" Tcl - * command. See the user documentation for details on what it does. - * - * Results: - * A standard Tcl result object. - * - * Side effects: - * See the user documentation. - * - *---------------------------------------------------------------------- - */ - - /* ARGSUSED */ -static int -ArrayGetCmd( - ClientData clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - Interp *iPtr = (Interp *) interp; - Var *varPtr, *arrayPtr, *varPtr2; - Tcl_Obj *varNameObj, *nameObj, *valueObj, *nameLstObj, *tmpResObj; - Tcl_Obj **nameObjPtr, *patternObj; - Tcl_HashSearch search; - const char *pattern; - int i, count, result; + if (notArray) { + goto error; + } + searchPtr = (ArraySearch *) ckalloc(sizeof(ArraySearch)); + hPtr = Tcl_CreateHashEntry(&iPtr->varSearches, + (char *) varPtr, &isNew); + if (isNew) { + searchPtr->id = 1; + Tcl_AppendResult(interp, "s-1-", varName, NULL); + varPtr->flags |= VAR_SEARCH_ACTIVE; + searchPtr->nextPtr = NULL; + } else { + char string[TCL_INTEGER_SPACE]; - switch (objc) { - case 2: - varNameObj = objv[1]; - patternObj = NULL; - break; - case 3: - varNameObj = objv[1]; - patternObj = objv[2]; + searchPtr->id = ((ArraySearch *) Tcl_GetHashValue(hPtr))->id + 1; + TclFormatInt(string, searchPtr->id); + Tcl_AppendResult(interp, "s-", string, "-", varName, NULL); + searchPtr->nextPtr = Tcl_GetHashValue(hPtr); + } + searchPtr->varPtr = varPtr; + searchPtr->nextEntry = VarHashFirstEntry(varPtr->value.tablePtr, + &searchPtr->search); + Tcl_SetHashValue(hPtr, searchPtr); break; - default: - Tcl_WrongNumArgs(interp, 1, objv, "arrayName ?pattern?"); - return TCL_ERROR; } - /* - * Locate the array variable. - */ - - varPtr = TclObjLookupVarEx(interp, varNameObj, NULL, /*flags*/ 0, - /*msg*/ 0, /*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr); - - /* - * Special array trace used to keep the env array in sync for array names, - * array get, etc. - */ - - if (varPtr && (varPtr->flags & VAR_TRACED_ARRAY) - && (TclIsVarArray(varPtr) || TclIsVarUndefined(varPtr))) { - if (TclObjCallVarTraces(iPtr, arrayPtr, varPtr, varNameObj, NULL, - (TCL_LEAVE_ERR_MSG|TCL_NAMESPACE_ONLY|TCL_GLOBAL_ONLY| - TCL_TRACE_ARRAY), /* leaveErrMsg */ 1, -1) == TCL_ERROR) { + case ARRAY_EXISTS: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "arrayName"); return TCL_ERROR; } - } - - /* - * Verify that it is indeed an array variable. This test comes after the - * traces - the variable may actually become an array as an effect of said - * traces. If not an array, it's an empty result. - */ - - if ((varPtr == NULL) || !TclIsVarArray(varPtr) - || TclIsVarUndefined(varPtr)) { - return TCL_OK; - } - - pattern = (patternObj ? TclGetString(patternObj) : NULL); - - /* - * Store the array names in a new object. - */ - - TclNewObj(nameLstObj); - Tcl_IncrRefCount(nameLstObj); - if ((patternObj != NULL) && TclMatchIsTrivial(pattern)) { - varPtr2 = VarHashFindVar(varPtr->value.tablePtr, patternObj); - if (varPtr2 == NULL) { - goto searchDone; + Tcl_SetObjResult(interp, iPtr->execEnvPtr->constants[!notArray]); + break; + case ARRAY_GET: { + Tcl_HashSearch search; + Var *varPtr2; + char *pattern = NULL; + char *name; + Tcl_Obj *namePtr, *valuePtr, *nameLstPtr, *tmpResPtr, **namePtrPtr; + int i, count; + + if ((objc != 3) && (objc != 4)) { + Tcl_WrongNumArgs(interp, 2, objv, "arrayName ?pattern?"); + return TCL_ERROR; } - if (TclIsVarUndefined(varPtr2)) { - goto searchDone; + if (notArray) { + return TCL_OK; } - result = Tcl_ListObjAppendElement(interp, nameLstObj, - VarHashGetKey(varPtr2)); - if (result != TCL_OK) { - TclDecrRefCount(nameLstObj); - return result; + if (objc == 4) { + pattern = TclGetString(objv[3]); } - goto searchDone; - } - for (varPtr2 = VarHashFirstVar(varPtr->value.tablePtr, &search); - varPtr2; varPtr2 = VarHashNextVar(&search)) { - if (TclIsVarUndefined(varPtr2)) { - continue; - } - nameObj = VarHashGetKey(varPtr2); - if (patternObj && !Tcl_StringMatch(TclGetString(nameObj), pattern)) { - continue; /* Element name doesn't match pattern. */ - } + /* + * Store the array names in a new object. + */ - result = Tcl_ListObjAppendElement(interp, nameLstObj, nameObj); - if (result != TCL_OK) { - TclDecrRefCount(nameLstObj); - return result; + TclNewObj(nameLstPtr); + Tcl_IncrRefCount(nameLstPtr); + if ((pattern != NULL) && TclMatchIsTrivial(pattern)) { + varPtr2 = VarHashFindVar(varPtr->value.tablePtr, objv[3]); + if (varPtr2 == NULL) { + goto searchDone; + } + if (TclIsVarUndefined(varPtr2)) { + goto searchDone; + } + result = Tcl_ListObjAppendElement(interp, nameLstPtr, + VarHashGetKey(varPtr2)); + if (result != TCL_OK) { + TclDecrRefCount(nameLstPtr); + return result; + } + goto searchDone; } - } + for (varPtr2 = VarHashFirstVar(varPtr->value.tablePtr, &search); + varPtr2; varPtr2 = VarHashNextVar(&search)) { + if (TclIsVarUndefined(varPtr2)) { + continue; + } + namePtr = VarHashGetKey(varPtr2); + name = TclGetString(namePtr); + if ((objc == 4) && !Tcl_StringMatch(name, pattern)) { + continue; /* Element name doesn't match pattern. */ + } - /* - * Make sure the Var structure of the array is not removed by a trace - * while we're working. - */ + result = Tcl_ListObjAppendElement(interp, nameLstPtr, namePtr); + if (result != TCL_OK) { + TclDecrRefCount(nameLstPtr); + return result; + } + } - searchDone: - if (TclIsVarInHash(varPtr)) { - VarHashRefCount(varPtr)++; - } + searchDone: + /* + * Make sure the Var structure of the array is not removed by a trace + * while we're working. + */ - /* - * Get the array values corresponding to each element name. - */ + if (TclIsVarInHash(varPtr)) { + VarHashRefCount(varPtr)++; + } - TclNewObj(tmpResObj); - result = Tcl_ListObjGetElements(interp, nameLstObj, &count, &nameObjPtr); - if (result != TCL_OK) { - goto errorInArrayGet; - } + /* + * Get the array values corresponding to each element name. + */ - for (i=0 ; i<count ; i++) { - nameObj = *nameObjPtr++; - valueObj = Tcl_ObjGetVar2(interp, varNameObj, nameObj, - TCL_LEAVE_ERR_MSG); - if (valueObj == NULL) { - /* - * Some trace played a trick on us; we need to diagnose to adapt - * our behaviour: was the array element unset, or did the - * modification modify the complete array? - */ + TclNewObj(tmpResPtr); + result = Tcl_ListObjGetElements(interp, nameLstPtr, &count, + &namePtrPtr); + if (result != TCL_OK) { + goto errorInArrayGet; + } - if (TclIsVarArray(varPtr)) { + for (i=0 ; i<count ; i++) { + namePtr = *namePtrPtr++; + valuePtr = Tcl_ObjGetVar2(interp, objv[2], namePtr, + TCL_LEAVE_ERR_MSG); + if (valuePtr == NULL) { /* - * The array itself looks OK, the variable was undefined: - * forget it. + * Some trace played a trick on us; we need to diagnose to + * adapt our behaviour: was the array element unset, or did + * the modification modify the complete array? */ - continue; + if (TclIsVarArray(varPtr)) { + /* + * The array itself looks OK, the variable was undefined: + * forget it. + */ + + continue; + } else { + result = TCL_ERROR; + goto errorInArrayGet; + } + } + result = Tcl_DictObjPut(interp, tmpResPtr, namePtr, valuePtr); + if (result != TCL_OK) { + goto errorInArrayGet; } - result = TCL_ERROR; - goto errorInArrayGet; } - result = Tcl_DictObjPut(interp, tmpResObj, nameObj, valueObj); - if (result != TCL_OK) { - goto errorInArrayGet; + if (TclIsVarInHash(varPtr)) { + VarHashRefCount(varPtr)--; } - } - if (TclIsVarInHash(varPtr)) { - VarHashRefCount(varPtr)--; - } - Tcl_SetObjResult(interp, tmpResObj); - TclDecrRefCount(nameLstObj); - return TCL_OK; - - errorInArrayGet: - if (TclIsVarInHash(varPtr)) { - VarHashRefCount(varPtr)--; - } - TclDecrRefCount(nameLstObj); - TclDecrRefCount(tmpResObj); /* Free unneeded temp result. */ - return result; -} - -/* - *---------------------------------------------------------------------- - * - * ArrayNamesCmd -- - * - * This object-based function is invoked to process the "array names" Tcl - * command. See the user documentation for details on what it does. - * - * Results: - * A standard Tcl result object. - * - * Side effects: - * See the user documentation. - * - *---------------------------------------------------------------------- - */ - - /* ARGSUSED */ -static int -ArrayNamesCmd( - ClientData clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - static const char *const options[] = { - "-exact", "-glob", "-regexp", NULL - }; - enum options { OPT_EXACT, OPT_GLOB, OPT_REGEXP }; - Interp *iPtr = (Interp *) interp; - Var *varPtr, *arrayPtr, *varPtr2; - Tcl_Obj *varNameObj, *nameObj, *resultObj, *patternObj; - Tcl_HashSearch search; - const char *pattern = NULL; - int mode = OPT_GLOB; + Tcl_SetObjResult(interp, tmpResPtr); + TclDecrRefCount(nameLstPtr); + break; - if ((objc < 2) || (objc > 4)) { - Tcl_WrongNumArgs(interp, 1, objv, "arrayName ?mode? ?pattern?"); - return TCL_ERROR; + errorInArrayGet: + if (TclIsVarInHash(varPtr)) { + VarHashRefCount(varPtr)--; + } + TclDecrRefCount(nameLstPtr); + TclDecrRefCount(tmpResPtr); /* Free unneeded temp result. */ + return result; } - varNameObj = objv[1]; - patternObj = (objc > 2 ? objv[objc-1] : NULL); - - /* - * Locate the array variable. - */ - - varPtr = TclObjLookupVarEx(interp, varNameObj, NULL, /*flags*/ 0, - /*msg*/ 0, /*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr); - - /* - * Special array trace used to keep the env array in sync for array names, - * array get, etc. - */ - - if (varPtr && (varPtr->flags & VAR_TRACED_ARRAY) - && (TclIsVarArray(varPtr) || TclIsVarUndefined(varPtr))) { - if (TclObjCallVarTraces(iPtr, arrayPtr, varPtr, varNameObj, NULL, - (TCL_LEAVE_ERR_MSG|TCL_NAMESPACE_ONLY|TCL_GLOBAL_ONLY| - TCL_TRACE_ARRAY), /* leaveErrMsg */ 1, -1) == TCL_ERROR) { + case ARRAY_NAMES: { + Tcl_HashSearch search; + Var *varPtr2; + char *pattern; + char *name; + Tcl_Obj *namePtr, *resultPtr, *patternPtr; + int mode, matched = 0; + static const char *options[] = { + "-exact", "-glob", "-regexp", NULL + }; + enum options { OPT_EXACT, OPT_GLOB, OPT_REGEXP }; + + mode = OPT_GLOB; + + if ((objc < 3) || (objc > 5)) { + Tcl_WrongNumArgs(interp, 2,objv, "arrayName ?mode? ?pattern?"); return TCL_ERROR; } - } - - /* - * Finish parsing the arguments. - */ - - if ((objc == 4) && Tcl_GetIndexFromObj(interp, objv[2], options, "option", - 0, &mode) != TCL_OK) { - return TCL_ERROR; - } - - /* - * Verify that it is indeed an array variable. This test comes after the - * traces - the variable may actually become an array as an effect of said - * traces. If not an array, the result is empty. - */ - - if ((varPtr == NULL) || !TclIsVarArray(varPtr) - || TclIsVarUndefined(varPtr)) { - return TCL_OK; - } - - /* - * Check for the trivial cases where we can use a direct lookup. - */ - - TclNewObj(resultObj); - if (patternObj) { - pattern = TclGetString(patternObj); - } - if ((mode==OPT_GLOB && patternObj && TclMatchIsTrivial(pattern)) - || (mode==OPT_EXACT)) { - varPtr2 = VarHashFindVar(varPtr->value.tablePtr, patternObj); - if ((varPtr2 != NULL) && !TclIsVarUndefined(varPtr2)) { - /* - * This can't fail; lappending to an empty object always works. - */ - - Tcl_ListObjAppendElement(NULL, resultObj, VarHashGetKey(varPtr2)); + if (notArray) { + return TCL_OK; } - Tcl_SetObjResult(interp, resultObj); - return TCL_OK; - } - - /* - * Must scan the array to select the elements. - */ - - for (varPtr2=VarHashFirstVar(varPtr->value.tablePtr, &search); - varPtr2!=NULL ; varPtr2=VarHashNextVar(&search)) { - if (TclIsVarUndefined(varPtr2)) { - continue; - } - nameObj = VarHashGetKey(varPtr2); - if (patternObj) { - const char *name = TclGetString(nameObj); - int matched = 0; - - switch ((enum options) mode) { - case OPT_EXACT: - Tcl_Panic("exact matching shouldn't get here"); - case OPT_GLOB: - matched = Tcl_StringMatch(name, pattern); - break; - case OPT_REGEXP: - matched = Tcl_RegExpMatchObj(interp, nameObj, patternObj); - if (matched < 0) { - TclDecrRefCount(resultObj); - return TCL_ERROR; + if (objc == 4) { + patternPtr = objv[3]; + pattern = TclGetString(patternPtr); + } else if (objc == 5) { + patternPtr = objv[4]; + pattern = TclGetString(patternPtr); + if (Tcl_GetIndexFromObj(interp, objv[3], options, "option", 0, + &mode) != TCL_OK) { + return TCL_ERROR; + } + } else { + patternPtr = NULL; + pattern = NULL; + } + TclNewObj(resultPtr); + if (((enum options) mode)==OPT_GLOB && pattern!=NULL && + TclMatchIsTrivial(pattern)) { + varPtr2 = VarHashFindVar(varPtr->value.tablePtr, patternPtr); + if ((varPtr2 != NULL) && !TclIsVarUndefined(varPtr2)) { + result = Tcl_ListObjAppendElement(interp, resultPtr, + VarHashGetKey(varPtr2)); + if (result != TCL_OK) { + TclDecrRefCount(resultPtr); + return result; } - break; } - if (matched == 0) { + Tcl_SetObjResult(interp, resultPtr); + return TCL_OK; + } + for (varPtr2=VarHashFirstVar(varPtr->value.tablePtr, &search); + varPtr2!=NULL ; varPtr2=VarHashNextVar(&search)) { + if (TclIsVarUndefined(varPtr2)) { continue; } - } - - Tcl_ListObjAppendElement(NULL, resultObj, nameObj); - } - Tcl_SetObjResult(interp, resultObj); - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * TclFindArrayPtrElements -- - * - * Fill out a hash table (which *must* use Tcl_Obj* keys) with an entry - * for each existing element of the given array. The provided hash table - * is assumed to be initially empty. - * - * Result: - * none - * - * Side effects: - * The keys of the array gain an extra reference. The supplied hash table - * has elements added to it. - * - *---------------------------------------------------------------------- - */ - -void -TclFindArrayPtrElements( - Var *arrayPtr, - Tcl_HashTable *tablePtr) -{ - Var *varPtr; - Tcl_HashSearch search; - - if ((arrayPtr == NULL) || !TclIsVarArray(arrayPtr) - || TclIsVarUndefined(arrayPtr)) { - return; - } - - for (varPtr=VarHashFirstVar(arrayPtr->value.tablePtr, &search); - varPtr!=NULL ; varPtr=VarHashNextVar(&search)) { - Tcl_HashEntry *hPtr; - Tcl_Obj *nameObj; - int dummy; + namePtr = VarHashGetKey(varPtr2); + name = TclGetString(namePtr); + if (objc > 3) { + switch ((enum options) mode) { + case OPT_EXACT: + matched = (strcmp(name, pattern) == 0); + break; + case OPT_GLOB: + matched = Tcl_StringMatch(name, pattern); + break; + case OPT_REGEXP: + matched = Tcl_RegExpMatch(interp, name, pattern); + if (matched < 0) { + TclDecrRefCount(resultPtr); + return TCL_ERROR; + } + break; + } + if (matched == 0) { + continue; + } + } - if (TclIsVarUndefined(varPtr)) { - continue; + result = Tcl_ListObjAppendElement(interp, resultPtr, namePtr); + if (result != TCL_OK) { + TclDecrRefCount(namePtr); /* Free unneeded name obj. */ + return result; + } } - nameObj = VarHashGetKey(varPtr); - hPtr = Tcl_CreateHashEntry(tablePtr, (char *) nameObj, &dummy); - Tcl_SetHashValue(hPtr, nameObj); - } -} - -/* - *---------------------------------------------------------------------- - * - * ArraySetCmd -- - * - * This object-based function is invoked to process the "array set" Tcl - * command. See the user documentation for details on what it does. - * - * Results: - * A standard Tcl result object. - * - * Side effects: - * See the user documentation. - * - *---------------------------------------------------------------------- - */ - - /* ARGSUSED */ -static int -ArraySetCmd( - ClientData clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - Interp *iPtr = (Interp *) interp; - Var *varPtr, *arrayPtr; - - if (objc != 3) { - Tcl_WrongNumArgs(interp, 1, objv, "arrayName list"); - return TCL_ERROR; + Tcl_SetObjResult(interp, resultPtr); + break; } + case ARRAY_SET: + if (objc != 4) { + Tcl_WrongNumArgs(interp, 2, objv, "arrayName list"); + return TCL_ERROR; + } + return TclArraySet(interp, objv[2], objv[3]); + case ARRAY_UNSET: + if ((objc != 3) && (objc != 4)) { + Tcl_WrongNumArgs(interp, 2, objv, "arrayName ?pattern?"); + return TCL_ERROR; + } + if (notArray) { + return TCL_OK; + } + if (objc == 3) { + /* + * When no pattern is given, just unset the whole array. + */ - /* - * Locate the array variable. - */ + return TclObjUnsetVar2(interp, varNamePtr, NULL, 0); + } else { + Tcl_HashSearch search; + Var *varPtr2, *protectedVarPtr; + const char *pattern = TclGetString(objv[3]); - varPtr = TclObjLookupVarEx(interp, objv[1], NULL, /*flags*/ 0, - /*msg*/ 0, /*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr); + /* + * With a trivial pattern, we can just unset. + */ - /* - * Special array trace used to keep the env array in sync for array names, - * array get, etc. - */ + if (TclMatchIsTrivial(pattern)) { + varPtr2 = VarHashFindVar(varPtr->value.tablePtr, objv[3]); + if (varPtr2 != NULL && !TclIsVarUndefined(varPtr2)) { + return TclObjUnsetVar2(interp, varNamePtr, objv[3], 0); + } + return TCL_OK; + } - if (varPtr && (varPtr->flags & VAR_TRACED_ARRAY) - && (TclIsVarArray(varPtr) || TclIsVarUndefined(varPtr))) { - if (TclObjCallVarTraces(iPtr, arrayPtr, varPtr, objv[1], NULL, - (TCL_LEAVE_ERR_MSG|TCL_NAMESPACE_ONLY|TCL_GLOBAL_ONLY| - TCL_TRACE_ARRAY), /* leaveErrMsg */ 1, -1) == TCL_ERROR) { - return TCL_ERROR; - } - } + /* + * Non-trivial case (well, deeply tricky really). We peek inside + * the hash iterator in order to allow us to guarantee that the + * following element in the array will not be scrubbed until we + * have dealt with it. This stops the overall iterator from ending + * up pointing into deallocated memory. [Bug 2939073] + */ - return TclArraySet(interp, objv[1], objv[2]); -} - -/* - *---------------------------------------------------------------------- - * - * ArraySizeCmd -- - * - * This object-based function is invoked to process the "array size" Tcl - * command. See the user documentation for details on what it does. - * - * Results: - * A standard Tcl result object. - * - * Side effects: - * See the user documentation. - * - *---------------------------------------------------------------------- - */ + protectedVarPtr = NULL; + for (varPtr2=VarHashFirstVar(varPtr->value.tablePtr, &search); + varPtr2!=NULL ; varPtr2=VarHashNextVar(&search)) { + /* + * Drop the extra ref immediately. We don't need to free it at + * this point though; we'll be unsetting it if necessary soon. + */ - /* ARGSUSED */ -static int -ArraySizeCmd( - ClientData clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - Interp *iPtr = (Interp *) interp; - Var *varPtr, *arrayPtr; - Tcl_Obj *varNameObj; - Tcl_HashSearch search; - Var *varPtr2; - int size = 0; + if (varPtr2 == protectedVarPtr) { + VarHashRefCount(varPtr2)--; + } - if (objc != 2) { - Tcl_WrongNumArgs(interp, 1, objv, "arrayName"); - return TCL_ERROR; - } - varNameObj = objv[1]; + /* + * Guard the next item in the search chain by incrementing its + * refcount. This guarantees that the hash table iterator + * won't be dangling on the next time through the loop. + */ - /* - * Locate the array variable. - */ + if (search.nextEntryPtr != NULL) { + protectedVarPtr = VarHashGetValue(search.nextEntryPtr); + VarHashRefCount(protectedVarPtr)++; + } else { + protectedVarPtr = NULL; + } - varPtr = TclObjLookupVarEx(interp, varNameObj, NULL, /*flags*/ 0, - /*msg*/ 0, /*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr); + if (!TclIsVarUndefined(varPtr2)) { + Tcl_Obj *namePtr = VarHashGetKey(varPtr2); + + if (Tcl_StringMatch(TclGetString(namePtr), pattern) + && TclObjUnsetVar2(interp, varNamePtr, namePtr, + 0) != TCL_OK) { + /* + * If we incremented a refcount, we must decrement it + * here as we will not be coming back properly due to + * the error. + */ + + if (protectedVarPtr) { + VarHashRefCount(protectedVarPtr)--; + CleanupVar(protectedVarPtr, varPtr); + } + return TCL_ERROR; + } + } else { + CleanupVar(varPtr2, varPtr); + } + } + break; + } - /* - * Special array trace used to keep the env array in sync for array names, - * array get, etc. - */ + case ARRAY_SIZE: { + Tcl_HashSearch search; + int size; - if (varPtr && (varPtr->flags & VAR_TRACED_ARRAY) - && (TclIsVarArray(varPtr) || TclIsVarUndefined(varPtr))) { - if (TclObjCallVarTraces(iPtr, arrayPtr, varPtr, varNameObj, NULL, - (TCL_LEAVE_ERR_MSG|TCL_NAMESPACE_ONLY|TCL_GLOBAL_ONLY| - TCL_TRACE_ARRAY), /* leaveErrMsg */ 1, -1) == TCL_ERROR) { + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "arrayName"); return TCL_ERROR; } - } - - /* - * Verify that it is indeed an array variable. This test comes after the - * traces - the variable may actually become an array as an effect of said - * traces. We can only iterate over the array if it exists... - */ + size = 0; - if (varPtr && TclIsVarArray(varPtr) && !TclIsVarUndefined(varPtr)) { /* * Must iterate in order to get chance to check for present but * "undefined" entries. */ - for (varPtr2=VarHashFirstVar(varPtr->value.tablePtr, &search); - varPtr2!=NULL ; varPtr2=VarHashNextVar(&search)) { - if (!TclIsVarUndefined(varPtr2)) { + if (!notArray) { + Var *varPtr2; + + for (varPtr2=VarHashFirstVar(varPtr->value.tablePtr, &search); + varPtr2!=NULL ; varPtr2=VarHashNextVar(&search)) { + if (TclIsVarUndefined(varPtr2)) { + continue; + } size++; } } + Tcl_SetObjResult(interp, Tcl_NewIntObj(size)); + break; } - Tcl_SetObjResult(interp, Tcl_NewIntObj(size)); - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * ArrayStatsCmd -- - * - * This object-based function is invoked to process the "array - * statistics" Tcl command. See the user documentation for details on - * what it does. - * - * Results: - * A standard Tcl result object. - * - * Side effects: - * See the user documentation. - * - *---------------------------------------------------------------------- - */ - - /* ARGSUSED */ -static int -ArrayStatsCmd( - ClientData clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - Interp *iPtr = (Interp *) interp; - Var *varPtr, *arrayPtr; - Tcl_Obj *varNameObj; - char *stats; - - if (objc != 2) { - Tcl_WrongNumArgs(interp, 1, objv, "arrayName"); - return TCL_ERROR; - } - varNameObj = objv[1]; - - /* - * Locate the array variable. - */ - - varPtr = TclObjLookupVarEx(interp, varNameObj, NULL, /*flags*/ 0, - /*msg*/ 0, /*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr); + case ARRAY_STATISTICS: { + const char *stats; - /* - * Special array trace used to keep the env array in sync for array names, - * array get, etc. - */ + if (notArray) { + goto error; + } - if (varPtr && (varPtr->flags & VAR_TRACED_ARRAY) - && (TclIsVarArray(varPtr) || TclIsVarUndefined(varPtr))) { - if (TclObjCallVarTraces(iPtr, arrayPtr, varPtr, varNameObj, NULL, - (TCL_LEAVE_ERR_MSG|TCL_NAMESPACE_ONLY|TCL_GLOBAL_ONLY| - TCL_TRACE_ARRAY), /* leaveErrMsg */ 1, -1) == TCL_ERROR) { + stats = Tcl_HashStats((Tcl_HashTable *) varPtr->value.tablePtr); + if (stats != NULL) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(stats, -1)); + ckfree((void *)stats); + } else { + Tcl_SetResult(interp,"error reading array statistics",TCL_STATIC); return TCL_ERROR; } + break; } - - /* - * Verify that it is indeed an array variable. This test comes after the - * traces - the variable may actually become an array as an effect of said - * traces. - */ - - if ((varPtr == NULL) || !TclIsVarArray(varPtr) - || TclIsVarUndefined(varPtr)) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "\"%s\" isn't an array", TclGetString(varNameObj))); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "ARRAY", - TclGetString(varNameObj), NULL); - return TCL_ERROR; - } - - stats = Tcl_HashStats((Tcl_HashTable *) varPtr->value.tablePtr); - if (stats == NULL) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "error reading array statistics", -1)); - return TCL_ERROR; } - Tcl_SetObjResult(interp, Tcl_NewStringObj(stats, -1)); - ckfree(stats); return TCL_OK; + + error: + Tcl_AppendResult(interp, "\"", TclGetString(varNamePtr), + "\" isn't an array", NULL); + return TCL_ERROR; } /* *---------------------------------------------------------------------- * - * ArrayUnsetCmd -- + * TclArraySet -- * - * This object-based function is invoked to process the "array unset" Tcl - * command. See the user documentation for details on what it does. + * Set the elements of an array. If there are no elements to set, create + * an empty array. This routine is used by the Tcl_ArrayObjCmd and by the + * TclSetupEnv routine. * * Results: * A standard Tcl result object. * * Side effects: - * See the user documentation. + * A variable will be created if one does not already exist. + * Callers must Incr arrayNameObj if they pland to Decr it. * *---------------------------------------------------------------------- */ - /* ARGSUSED */ -static int -ArrayUnsetCmd( - ClientData clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) +int +TclArraySet( + Tcl_Interp *interp, /* Current interpreter. */ + Tcl_Obj *arrayNameObj, /* The array name. */ + Tcl_Obj *arrayElemObj) /* The array elements list or dict. If this is + * NULL, create an empty array. */ { - Interp *iPtr = (Interp *) interp; - Var *varPtr, *arrayPtr, *varPtr2, *protectedVarPtr; - Tcl_Obj *varNameObj, *patternObj, *nameObj; - Tcl_HashSearch search; - const char *pattern; - const int unsetFlags = 0; /* Should this be TCL_LEAVE_ERR_MSG? */ + Var *varPtr, *arrayPtr; + int result, i; - switch (objc) { - case 2: - varNameObj = objv[1]; - patternObj = NULL; - break; - case 3: - varNameObj = objv[1]; - patternObj = objv[2]; - break; - default: - Tcl_WrongNumArgs(interp, 1, objv, "arrayName ?pattern?"); + varPtr = TclObjLookupVarEx(interp, arrayNameObj, NULL, + /*flags*/ TCL_LEAVE_ERR_MSG, /*msg*/ "set", /*createPart1*/ 1, + /*createPart2*/ 1, &arrayPtr); + if (varPtr == NULL) { + return TCL_ERROR; + } + if (arrayPtr) { + CleanupVar(varPtr, arrayPtr); + TclObjVarErrMsg(interp, arrayNameObj, NULL, "set", needArray, -1); return TCL_ERROR; } - /* - * Locate the array variable - */ - - varPtr = TclObjLookupVarEx(interp, varNameObj, NULL, /*flags*/ 0, - /*msg*/ 0, /*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr); + if (arrayElemObj == NULL) { + goto ensureArray; + } /* - * Special array trace used to keep the env array in sync for array names, - * array get, etc. + * Install the contents of the dictionary or list into the array. */ - if (varPtr && (varPtr->flags & VAR_TRACED_ARRAY) - && (TclIsVarArray(varPtr) || TclIsVarUndefined(varPtr))) { - if (TclObjCallVarTraces(iPtr, arrayPtr, varPtr, varNameObj, NULL, - (TCL_LEAVE_ERR_MSG|TCL_NAMESPACE_ONLY|TCL_GLOBAL_ONLY| - TCL_TRACE_ARRAY), /* leaveErrMsg */ 1, -1) == TCL_ERROR) { + if (arrayElemObj->typePtr == &tclDictType) { + Tcl_Obj *keyPtr, *valuePtr; + Tcl_DictSearch search; + int done; + + if (Tcl_DictObjSize(interp, arrayElemObj, &done) != TCL_OK) { return TCL_ERROR; } - } - - /* - * Verify that it is indeed an array variable. This test comes after the - * traces - the variable may actually become an array as an effect of said - * traces. - */ + if (done == 0) { + /* + * Empty, so we'll just force the array to be properly existing + * instead. + */ - if ((varPtr == NULL) || !TclIsVarArray(varPtr) - || TclIsVarUndefined(varPtr)) { - return TCL_OK; - } + goto ensureArray; + } - if (!patternObj) { /* - * When no pattern is given, just unset the whole array. + * Don't need to look at result of Tcl_DictObjFirst as we've just + * successfully used a dictionary operation on the same object. */ - return TclObjUnsetVar2(interp, varNameObj, NULL, 0); - } + for (Tcl_DictObjFirst(interp, arrayElemObj, &search, + &keyPtr, &valuePtr, &done) ; !done ; + Tcl_DictObjNext(&search, &keyPtr, &valuePtr, &done)) { + /* + * At this point, it would be nice if the key was directly usable + * by the array. This isn't the case though. + */ - /* - * With a trivial pattern, we can just unset. - */ + Var *elemVarPtr = TclLookupArrayElement(interp, arrayNameObj, + keyPtr, TCL_LEAVE_ERR_MSG, "set", 1, 1, varPtr, -1); - pattern = TclGetString(patternObj); - if (TclMatchIsTrivial(pattern)) { - varPtr2 = VarHashFindVar(varPtr->value.tablePtr, patternObj); - if (!varPtr2 || TclIsVarUndefined(varPtr2)) { - return TCL_OK; + if ((elemVarPtr == NULL) || + (TclPtrSetVar(interp, elemVarPtr, varPtr, arrayNameObj, + keyPtr, valuePtr, TCL_LEAVE_ERR_MSG, -1) == NULL)) { + Tcl_DictObjDone(&search); + return TCL_ERROR; + } } - return TclPtrUnsetVar(interp, varPtr2, varPtr, varNameObj, patternObj, - unsetFlags, -1); - } - - /* - * Non-trivial case (well, deeply tricky really). We peek inside the hash - * iterator in order to allow us to guarantee that the following element - * in the array will not be scrubbed until we have dealt with it. This - * stops the overall iterator from ending up pointing into deallocated - * memory. [Bug 2939073] - */ - - protectedVarPtr = NULL; - for (varPtr2=VarHashFirstVar(varPtr->value.tablePtr, &search); - varPtr2!=NULL ; varPtr2=VarHashNextVar(&search)) { + return TCL_OK; + } else { /* - * Drop the extra ref immediately. We don't need to free it at this - * point though; we'll be unsetting it if necessary soon. + * Not a dictionary, so assume (and convert to, for backward- + * -compatability reasons) a list. */ - if (varPtr2 == protectedVarPtr) { - VarHashRefCount(varPtr2)--; + int elemLen; + Tcl_Obj **elemPtrs, *copyListObj; + + result = TclListObjGetElements(interp, arrayElemObj, + &elemLen, &elemPtrs); + if (result != TCL_OK) { + return result; + } + if (elemLen & 1) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "list must have an even number of elements", -1)); + return TCL_ERROR; + } + if (elemLen == 0) { + goto ensureArray; } /* - * Guard the next (peeked) item in the search chain by incrementing - * its refcount. This guarantees that the hash table iterator won't be - * dangling on the next time through the loop. + * We needn't worry about traces invalidating arrayPtr: should that be + * the case, TclPtrSetVar will return NULL so that we break out of the + * loop and return an error. */ - if (search.nextEntryPtr != NULL) { - protectedVarPtr = VarHashGetValue(search.nextEntryPtr); - VarHashRefCount(protectedVarPtr)++; - } else { - protectedVarPtr = NULL; + copyListObj = TclListObjCopy(NULL, arrayElemObj); + for (i=0 ; i<elemLen ; i+=2) { + Var *elemVarPtr = TclLookupArrayElement(interp, arrayNameObj, + elemPtrs[i], TCL_LEAVE_ERR_MSG, "set", 1, 1, varPtr, -1); + + if ((elemVarPtr == NULL) || + (TclPtrSetVar(interp, elemVarPtr, varPtr, arrayNameObj, + elemPtrs[i],elemPtrs[i+1],TCL_LEAVE_ERR_MSG,-1) == NULL)){ + result = TCL_ERROR; + break; + } } + Tcl_DecrRefCount(copyListObj); + return result; + } - /* - * If the variable is undefined, clean it out as it has been hit by - * something else (i.e., an unset trace). - */ + /* + * The list is empty make sure we have an array, or create one if + * necessary. + */ - if (TclIsVarUndefined(varPtr2)) { - CleanupVar(varPtr2, varPtr); - continue; - } + ensureArray: + if (varPtr != NULL) { + if (TclIsVarArray(varPtr)) { + /* + * Already an array, done. + */ - nameObj = VarHashGetKey(varPtr2); - if (Tcl_StringMatch(TclGetString(nameObj), pattern) - && TclPtrUnsetVar(interp, varPtr2, varPtr, varNameObj, - nameObj, unsetFlags, -1) != TCL_OK) { + return TCL_OK; + } + if (TclIsVarArrayElement(varPtr) || !TclIsVarUndefined(varPtr)) { /* - * If we incremented a refcount, we must decrement it here as we - * will not be coming back properly due to the error. + * Either an array element, or a scalar: lose! */ - if (protectedVarPtr) { - VarHashRefCount(protectedVarPtr)--; - CleanupVar(protectedVarPtr, varPtr); - } + TclObjVarErrMsg(interp, arrayNameObj, NULL, "array set", + needArray, -1); return TCL_ERROR; } } + TclSetVarArray(varPtr); + varPtr->value.tablePtr = (TclVarHashTable *) + ckalloc(sizeof(TclVarHashTable)); + TclInitVarHashTable(varPtr->value.tablePtr, TclGetVarNsPtr(varPtr)); return TCL_OK; } /* *---------------------------------------------------------------------- * - * TclInitArrayCmd -- - * - * This creates the ensemble for the "array" command. - * - * Results: - * The handle for the created ensemble. - * - * Side effects: - * Creates a command in the global namespace. - * - *---------------------------------------------------------------------- - */ - - /* ARGSUSED */ -Tcl_Command -TclInitArrayCmd( - Tcl_Interp *interp) /* Current interpreter. */ -{ - static const EnsembleImplMap arrayImplMap[] = { - {"anymore", ArrayAnyMoreCmd, TclCompileBasic2ArgCmd, NULL, NULL, 0}, - {"donesearch", ArrayDoneSearchCmd, TclCompileBasic2ArgCmd, NULL, NULL, 0}, - {"exists", ArrayExistsCmd, TclCompileArrayExistsCmd, NULL, NULL, 0}, - {"get", ArrayGetCmd, TclCompileBasic1Or2ArgCmd, NULL, NULL, 0}, - {"names", ArrayNamesCmd, TclCompileBasic1To3ArgCmd, NULL, NULL, 0}, - {"nextelement", ArrayNextElementCmd, TclCompileBasic2ArgCmd, NULL, NULL, 0}, - {"set", ArraySetCmd, TclCompileArraySetCmd, NULL, NULL, 0}, - {"size", ArraySizeCmd, TclCompileBasic1ArgCmd, NULL, NULL, 0}, - {"startsearch", ArrayStartSearchCmd, TclCompileBasic1ArgCmd, NULL, NULL, 0}, - {"statistics", ArrayStatsCmd, TclCompileBasic1ArgCmd, NULL, NULL, 0}, - {"unset", ArrayUnsetCmd, TclCompileArrayUnsetCmd, NULL, NULL, 0}, - {NULL, NULL, NULL, NULL, NULL, 0} - }; - - return TclMakeEnsemble(interp, "array", arrayImplMap); -} - -/* - *---------------------------------------------------------------------- - * * ObjMakeUpvar -- * * This function does all of the work of the "global" and "upvar" @@ -4122,11 +3610,9 @@ ObjMakeUpvar( || (varFramePtr == NULL) || !HasLocalVars(varFramePtr) || (strstr(TclGetString(myNamePtr), "::") != NULL))) { - Tcl_SetObjResult((Tcl_Interp *) iPtr, Tcl_ObjPrintf( - "bad variable name \"%s\": can't create namespace " - "variable that refers to procedure variable", - TclGetString(myNamePtr))); - Tcl_SetErrorCode(interp, "TCL", "UPVAR", "INVERTED", NULL); + Tcl_AppendResult((Tcl_Interp *) iPtr, "bad variable name \"", + TclGetString(myNamePtr), "\": can't create namespace " + "variable that refers to procedure variable", NULL); return TCL_ERROR; } } @@ -4196,7 +3682,7 @@ TclPtrObjMakeUpvar( { Interp *iPtr = (Interp *) interp; CallFrame *varFramePtr = iPtr->varFramePtr; - const char *errMsg, *p, *myName; + const char *errMsg, *myName; Var *varPtr; if (index >= 0) { @@ -4207,6 +3693,7 @@ TclPtrObjMakeUpvar( myNamePtr = localName(iPtr->varFramePtr, index); myName = myNamePtr? TclGetString(myNamePtr) : NULL; } else { + const char *p; /* * Do not permit the new variable to look like an array reference, as * it will not be reachable in that case [Bug 600812, TIP 184]. The @@ -4223,49 +3710,41 @@ TclPtrObjMakeUpvar( * myName looks like an array reference. */ - Tcl_SetObjResult((Tcl_Interp *) iPtr, Tcl_ObjPrintf( - "bad variable name \"%s\": can't create a scalar " - "variable that looks like an array element", myName)); - Tcl_SetErrorCode(interp, "TCL", "UPVAR", "LOCAL_ELEMENT", - NULL); + Tcl_AppendResult((Tcl_Interp *) iPtr, "bad variable name \"", + myName, "\": can't create a scalar variable that " + "looks like an array element", NULL); return TCL_ERROR; } } /* * Lookup and eventually create the new variable. Set the flag bit - * TCL_AVOID_RESOLVERS to indicate the special resolution rules for - * upvar purposes: + * AVOID_RESOLVERS to indicate the special resolution rules for upvar + * purposes: * - Bug #696893 - variable is either proc-local or in the current * namespace; never follow the second (global) resolution path. * - Bug #631741 - do not use special namespace or interp resolvers. */ varPtr = TclLookupSimpleVar(interp, myNamePtr, - myFlags|TCL_AVOID_RESOLVERS, /* create */ 1, &errMsg, &index); + myFlags|AVOID_RESOLVERS, /* create */ 1, &errMsg, &index); if (varPtr == NULL) { TclObjVarErrMsg(interp, myNamePtr, NULL, "create", errMsg, -1); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "VARNAME", - TclGetString(myNamePtr), NULL); return TCL_ERROR; } } if (varPtr == otherPtr) { - Tcl_SetObjResult((Tcl_Interp *) iPtr, Tcl_NewStringObj( - "can't upvar from variable to itself", -1)); - Tcl_SetErrorCode(interp, "TCL", "UPVAR", "SELF", NULL); + Tcl_SetResult((Tcl_Interp *) iPtr, + "can't upvar from variable to itself", TCL_STATIC); return TCL_ERROR; } if (TclIsVarTraced(varPtr)) { - Tcl_SetObjResult((Tcl_Interp *) iPtr, Tcl_ObjPrintf( - "variable \"%s\" has traces: can't use for upvar", myName)); - Tcl_SetErrorCode(interp, "TCL", "UPVAR", "TRACED", NULL); + Tcl_AppendResult((Tcl_Interp *) iPtr, "variable \"", myName, + "\" has traces: can't use for upvar", NULL); return TCL_ERROR; } else if (!TclIsVarUndefined(varPtr)) { - Var *linkPtr; - /* * The variable already existed. Make sure this variable "varPtr" * isn't the same as "otherPtr" (avoid circular links). Also, if it's @@ -4273,22 +3752,21 @@ TclPtrObjMakeUpvar( * disconnect it from the thing it currently refers to. */ - if (!TclIsVarLink(varPtr)) { - Tcl_SetObjResult((Tcl_Interp *) iPtr, Tcl_ObjPrintf( - "variable \"%s\" already exists", myName)); - Tcl_SetErrorCode(interp, "TCL", "UPVAR", "EXISTS", NULL); - return TCL_ERROR; - } - - linkPtr = varPtr->value.linkPtr; - if (linkPtr == otherPtr) { - return TCL_OK; - } - if (TclIsVarInHash(linkPtr)) { - VarHashRefCount(linkPtr)--; - if (TclIsVarUndefined(linkPtr)) { - CleanupVar(linkPtr, NULL); + if (TclIsVarLink(varPtr)) { + Var *linkPtr = varPtr->value.linkPtr; + if (linkPtr == otherPtr) { + return TCL_OK; + } + if (TclIsVarInHash(linkPtr)) { + VarHashRefCount(linkPtr)--; + if (TclIsVarUndefined(linkPtr)) { + CleanupVar(linkPtr, NULL); + } } + } else { + Tcl_AppendResult((Tcl_Interp *) iPtr, "variable \"", myName, + "\" already exists", NULL); + return TCL_ERROR; } } TclSetVarLink(varPtr); @@ -4313,9 +3791,8 @@ TclPtrObjMakeUpvar( * * Side effects: * The variable in frameName whose name is given by varName becomes - * accessible under the name localNameStr, so that references to - * localNameStr are redirected to the other variable like a symbolic - * link. + * accessible under the name localName, so that references to localName + * are redirected to the other variable like a symbolic link. * *---------------------------------------------------------------------- */ @@ -4330,28 +3807,11 @@ Tcl_UpVar( const char *varName, /* Name of a variable in interp to link to. * May be either a scalar name or an element * in an array. */ - const char *localNameStr, /* Name of link variable. */ + const char *localName, /* Name of link variable. */ int flags) /* 0, TCL_GLOBAL_ONLY or TCL_NAMESPACE_ONLY: - * indicates scope of localNameStr. */ + * indicates scope of localName. */ { - int result; - CallFrame *framePtr; - Tcl_Obj *varNamePtr, *localNamePtr; - - if (TclGetFrame(interp, frameName, &framePtr) == -1) { - return TCL_ERROR; - } - - varNamePtr = Tcl_NewStringObj(varName, -1); - Tcl_IncrRefCount(varNamePtr); - localNamePtr = Tcl_NewStringObj(localNameStr, -1); - Tcl_IncrRefCount(localNamePtr); - - result = ObjMakeUpvar(interp, framePtr, varNamePtr, NULL, 0, - localNamePtr, flags, -1); - Tcl_DecrRefCount(varNamePtr); - Tcl_DecrRefCount(localNamePtr); - return result; + return Tcl_UpVar2(interp, frameName, varName, NULL, localName, flags); } /* @@ -4368,9 +3828,8 @@ Tcl_UpVar( * * Side effects: * The variable in frameName whose name is given by part1 and part2 - * becomes accessible under the name localNameStr, so that references to - * localNameStr are redirected to the other variable like a symbolic - * link. + * becomes accessible under the name localName, so that references to + * localName are redirected to the other variable like a symbolic link. * *---------------------------------------------------------------------- */ @@ -4384,9 +3843,9 @@ Tcl_UpVar2( const char *part1, const char *part2, /* Two parts of source variable name to link * to. */ - const char *localNameStr, /* Name of link variable. */ + const char *localName, /* Name of link variable. */ int flags) /* 0, TCL_GLOBAL_ONLY or TCL_NAMESPACE_ONLY: - * indicates scope of localNameStr. */ + * indicates scope of localName. */ { int result; CallFrame *framePtr; @@ -4398,7 +3857,7 @@ Tcl_UpVar2( part1Ptr = Tcl_NewStringObj(part1, -1); Tcl_IncrRefCount(part1Ptr); - localNamePtr = Tcl_NewStringObj(localNameStr, -1); + localNamePtr = Tcl_NewStringObj(localName, -1); Tcl_IncrRefCount(localNamePtr); result = ObjMakeUpvar(interp, framePtr, part1Ptr, part2, 0, @@ -4437,36 +3896,38 @@ Tcl_GetVariableFullName( { Interp *iPtr = (Interp *) interp; register Var *varPtr = (Var *) variable; - Tcl_Obj *namePtr; - Namespace *nsPtr; - - if (!varPtr || TclIsVarArrayElement(varPtr)) { - return; - } /* * Add the full name of the containing namespace (if any), followed by the * "::" separator, then the variable name. */ - nsPtr = TclGetVarNsPtr(varPtr); - if (nsPtr) { - Tcl_AppendToObj(objPtr, nsPtr->fullName, -1); - if (nsPtr != iPtr->globalNsPtr) { - Tcl_AppendToObj(objPtr, "::", 2); - } - } - if (TclIsVarInHash(varPtr)) { - if (!TclIsVarDeadHash(varPtr)) { - namePtr = VarHashGetKey(varPtr); - Tcl_AppendObjToObj(objPtr, namePtr); - } - } else if (iPtr->varFramePtr->procPtr) { - int index = varPtr - iPtr->varFramePtr->compiledLocals; + if (varPtr) { + if (!TclIsVarArrayElement(varPtr)) { + Tcl_Obj *namePtr; + Namespace *nsPtr; - if (index >= 0 && index < iPtr->varFramePtr->numCompiledLocals) { - namePtr = localName(iPtr->varFramePtr, index); - Tcl_AppendObjToObj(objPtr, namePtr); + nsPtr = TclGetVarNsPtr(varPtr); + if (nsPtr) { + Tcl_AppendToObj(objPtr, nsPtr->fullName, -1); + if (nsPtr != iPtr->globalNsPtr) { + Tcl_AppendToObj(objPtr, "::", 2); + } + } + if (TclIsVarInHash(varPtr)) { + if (!TclIsVarDeadHash(varPtr)) { + namePtr = VarHashGetKey(varPtr); + Tcl_AppendObjToObj(objPtr, namePtr); + } + } else if (iPtr->varFramePtr->procPtr) { + int index = varPtr - iPtr->varFramePtr->compiledLocals; + + if (index >= 0 + && index < iPtr->varFramePtr->numCompiledLocals) { + namePtr = localName(iPtr->varFramePtr, index); + Tcl_AppendObjToObj(objPtr, namePtr); + } + } } } } @@ -4497,9 +3958,14 @@ Tcl_GlobalObjCmd( { Interp *iPtr = (Interp *) interp; register Tcl_Obj *objPtr, *tailPtr; - const char *varName; - register const char *tail; - int result, i; + char *varName; + register char *tail; + int i; + + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "varName ?varName ...?"); + return TCL_ERROR; + } /* * If we are not executing inside a Tcl procedure, just return. @@ -4510,6 +3976,8 @@ Tcl_GlobalObjCmd( } for (i=1 ; i<objc ; i++) { + int result; + /* * Make a local variable linked to its counterpart in the global :: * namespace. @@ -4600,13 +4068,19 @@ Tcl_VariableObjCmd( Tcl_Obj *const objv[]) /* Argument objects. */ { Interp *iPtr = (Interp *) interp; - const char *varName, *tail, *cp; - Var *varPtr, *arrayPtr; + char *varName, *tail, *cp; Tcl_Obj *varValuePtr; int i, result; Tcl_Obj *varNamePtr, *tailPtr; + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "?name value...? name ?value?"); + return TCL_ERROR; + } + for (i=1 ; i<objc ; i+=2) { + Var *varPtr, *arrayPtr; + /* * Look up each variable in the current namespace context, creating it * if necessary. @@ -4626,7 +4100,6 @@ Tcl_VariableObjCmd( TclObjVarErrMsg(interp, varNamePtr, NULL, "define", isArrayElement, -1); - Tcl_SetErrorCode(interp, "TCL", "UPVAR", "LOCAL_ELEMENT", NULL); return TCL_ERROR; } @@ -4733,60 +4206,29 @@ Tcl_UpvarObjCmd( Tcl_Obj *const objv[]) /* Argument objects. */ { CallFrame *framePtr; - int result, hasLevel; - Tcl_Obj *levelObj; + int result; if (objc < 3) { + upvarSyntax: Tcl_WrongNumArgs(interp, 1, objv, "?level? otherVar localVar ?otherVar localVar ...?"); return TCL_ERROR; } - if (objc & 1) { - /* - * Even number of arguments, so use the default level of "1" by - * passing NULL to TclObjGetFrame. - */ - - levelObj = NULL; - hasLevel = 0; - } else { - /* - * Odd number of arguments, so objv[1] must contain the level. - */ - - levelObj = objv[1]; - hasLevel = 1; - } - /* * Find the call frame containing each of the "other variables" to be * linked to. */ - result = TclObjGetFrame(interp, levelObj, &framePtr); + result = TclObjGetFrame(interp, objv[1], &framePtr); if (result == -1) { return TCL_ERROR; } - if ((result == 0) && hasLevel) { - /* - * Synthesize an error message since TclObjGetFrame doesn't do this - * for this particular case. - */ - - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "bad level \"%s\"", TclGetString(levelObj))); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "LEVEL", - TclGetString(levelObj), NULL); - return TCL_ERROR; + objc -= result+1; + if ((objc & 1) != 0) { + goto upvarSyntax; } - - /* - * We've now finished with parsing levels; skip to the variable names. - */ - - objc -= hasLevel + 1; - objv += hasLevel + 1; + objv += result+1; /* * Iterate over each (other variable, local variable) pair. Divide the @@ -4807,6 +4249,73 @@ Tcl_UpvarObjCmd( /* *---------------------------------------------------------------------- * + * SetArraySearchObj -- + * + * This function converts the given tcl object into one that has the + * "array search" internal type. + * + * Results: + * TCL_OK if the conversion succeeded, and TCL_ERROR if it failed (when + * an error message will be placed in the interpreter's result.) + * + * Side effects: + * Updates the internal type and representation of the object to make + * this an array-search object. See the tclArraySearchType declaration + * above for details of the internal representation. + * + *---------------------------------------------------------------------- + */ + +static int +SetArraySearchObj( + Tcl_Interp *interp, + Tcl_Obj *objPtr) +{ + char *string; + char *end; + int id; + size_t offset; + + /* + * Get the string representation. Make it up-to-date if necessary. + */ + + string = TclGetString(objPtr); + + /* + * Parse the id into the three parts separated by dashes. + */ + + if ((string[0] != 's') || (string[1] != '-')) { + goto syntax; + } + id = strtoul(string+2, &end, 10); + if ((end == (string+2)) || (*end != '-')) { + goto syntax; + } + + /* + * Can't perform value check in this context, so place reference to place + * in string to use for the check in the object instead. + */ + + end++; + offset = end - string; + + TclFreeIntRep(objPtr); + objPtr->typePtr = &tclArraySearchType; + objPtr->internalRep.twoPtrValue.ptr1 = INT2PTR(id); + objPtr->internalRep.twoPtrValue.ptr2 = INT2PTR(offset); + return TCL_OK; + + syntax: + Tcl_AppendResult(interp, "illegal search identifier \"",string,"\"",NULL); + return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * * ParseSearchId -- * * This function translates from a tcl object to a pointer to an active @@ -4817,6 +4326,10 @@ Tcl_UpvarObjCmd( * or NULL if there isn't one. If NULL is returned, the interp's result * contains an error message. * + * Side effects: + * The tcl object might have its internal type and representation + * modified. + * *---------------------------------------------------------------------- */ @@ -4832,43 +4345,70 @@ ParseSearchId( * name. */ { Interp *iPtr = (Interp *) interp; + register char *string; + register size_t offset; + int id; ArraySearch *searchPtr; - const char *handle = TclGetString(handleObj); - char *end; + char *varName = TclGetString(varNamePtr); + + /* + * Parse the id. + */ + + if (Tcl_ConvertToType(interp, handleObj, &tclArraySearchType) != TCL_OK) { + return NULL; + } + + /* + * Extract the information out of the Tcl_Obj. + */ + +#if 1 + id = PTR2INT(handleObj->internalRep.twoPtrValue.ptr1); + string = TclGetString(handleObj); + offset = PTR2INT(handleObj->internalRep.twoPtrValue.ptr2); +#else + id = (int)(((char *) handleObj->internalRep.twoPtrValue.ptr1) - + ((char *) NULL)); + string = TclGetString(handleObj); + offset = (((char *) handleObj->internalRep.twoPtrValue.ptr2) - + ((char *) NULL)); +#endif + + /* + * This test cannot be placed inside the Tcl_Obj machinery, since it is + * dependent on the variable context. + */ + + if (strcmp(string+offset, varName) != 0) { + Tcl_AppendResult(interp, "search identifier \"", string, + "\" isn't for variable \"", varName, "\"", NULL); + goto badLookup; + } + + /* + * Search through the list of active searches on the interpreter to see if + * the desired one exists. + * + * Note that we cannot store the searchPtr directly in the Tcl_Obj as that + * would run into trouble when DeleteSearches() was called so we must scan + * this list every time. + */ if (varPtr->flags & VAR_SEARCH_ACTIVE) { Tcl_HashEntry *hPtr = - Tcl_FindHashEntry(&iPtr->varSearches, varPtr); + Tcl_FindHashEntry(&iPtr->varSearches, (char *) varPtr); - /* First look for same (Tcl_Obj *) */ - for (searchPtr = Tcl_GetHashValue(hPtr); searchPtr != NULL; - searchPtr = searchPtr->nextPtr) { - if (searchPtr->name == handleObj) { + for (searchPtr = (ArraySearch *) Tcl_GetHashValue(hPtr); + searchPtr != NULL; searchPtr = searchPtr->nextPtr) { + if (searchPtr->id == id) { return searchPtr; } } - /* Fallback: do string compares. */ - for (searchPtr = Tcl_GetHashValue(hPtr); searchPtr != NULL; - searchPtr = searchPtr->nextPtr) { - if (strcmp(TclGetString(searchPtr->name), handle) == 0) { - return searchPtr; - } - } - } - if ((handle[0] != 's') || (handle[1] != '-') - || (strtoul(handle + 2, &end, 10), end == (handle + 2)) - || (*end != '-')) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "illegal search identifier \"%s\"", handle)); - } else if (strcmp(end + 1, TclGetString(varNamePtr)) != 0) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "search identifier \"%s\" isn't for variable \"%s\"", - handle, TclGetString(varNamePtr))); - } else { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "couldn't find search \"%s\"", handle)); } - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "ARRAYSEARCH", handle, NULL); + Tcl_AppendResult(interp, "couldn't find search \"", string, "\"", NULL); + badLookup: + Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "ARRAYSEARCH", string, NULL); return NULL; } @@ -4895,16 +4435,15 @@ DeleteSearches( register Var *arrayVarPtr) /* Variable whose searches are to be * deleted. */ { - ArraySearch *searchPtr, *nextPtr; - Tcl_HashEntry *sPtr; - if (arrayVarPtr->flags & VAR_SEARCH_ACTIVE) { - sPtr = Tcl_FindHashEntry(&iPtr->varSearches, arrayVarPtr); - for (searchPtr = Tcl_GetHashValue(sPtr); searchPtr != NULL; - searchPtr = nextPtr) { + ArraySearch *searchPtr, *nextPtr; + Tcl_HashEntry *sPtr; + + sPtr = Tcl_FindHashEntry(&iPtr->varSearches, (char *) arrayVarPtr); + for (searchPtr = (ArraySearch *) Tcl_GetHashValue(sPtr); + searchPtr != NULL; searchPtr = nextPtr) { nextPtr = searchPtr->nextPtr; - Tcl_DecrRefCount(searchPtr->name); - ckfree(searchPtr); + ckfree((char *) searchPtr); } arrayVarPtr->flags &= ~VAR_SEARCH_ACTIVE; Tcl_DeleteHashEntry(sPtr); @@ -4953,32 +4492,31 @@ TclDeleteNamespaceVars( for (varPtr = VarHashFirstVar(tablePtr, &search); varPtr != NULL; varPtr = VarHashFirstVar(tablePtr, &search)) { Tcl_Obj *objPtr = Tcl_NewObj(); + VarHashRefCount(varPtr)++; /* Make sure we get to remove from * hash. */ Tcl_GetVariableFullName(interp, (Tcl_Var) varPtr, objPtr); UnsetVarStruct(varPtr, NULL, iPtr, /* part1 */ objPtr, - NULL, flags, -1); + NULL, flags); + Tcl_DecrRefCount(objPtr); /* free no longer needed obj */ /* - * We just unset the variable. However, an unset trace might - * have re-set it, or might have re-established traces on it. - * This namespace and its vartable are going away unconditionally, - * so we cannot let such things linger. That would be a leak. - * - * First we destroy all traces. ... + * Remove the variable from the table and force it undefined in case + * an unset trace brought it back from the dead. */ if (TclIsVarTraced(varPtr)) { - Tcl_HashEntry *tPtr = Tcl_FindHashEntry(&iPtr->varTraces, varPtr); - VarTrace *tracePtr = Tcl_GetHashValue(tPtr); ActiveVarTrace *activePtr; + Tcl_HashEntry *tPtr = Tcl_FindHashEntry(&iPtr->varTraces, + (char *) varPtr); + VarTrace *tracePtr = (VarTrace *) Tcl_GetHashValue(tPtr); while (tracePtr) { VarTrace *prevPtr = tracePtr; tracePtr = tracePtr->nextPtr; prevPtr->nextPtr = NULL; - Tcl_EventuallyFree(prevPtr, TCL_DYNAMIC); + Tcl_EventuallyFree((ClientData) prevPtr, TCL_DYNAMIC); } Tcl_DeleteHashEntry(tPtr); varPtr->flags &= ~VAR_ALL_TRACES; @@ -4989,17 +4527,6 @@ TclDeleteNamespaceVars( } } } - - /* - * ...and then, if the variable still holds a value, we unset it - * again. This time with no traces left, we're sure it goes away. - */ - - if (!TclIsVarUndefined(varPtr)) { - UnsetVarStruct(varPtr, NULL, iPtr, /* part1 */ objPtr, - NULL, flags, -1); - } - Tcl_DecrRefCount(objPtr); /* free no longer needed obj */ VarHashRefCount(varPtr)--; VarHashDeleteEntry(varPtr); } @@ -5050,9 +4577,9 @@ TclDeleteVars( } for (varPtr = VarHashFirstVar(tablePtr, &search); varPtr != NULL; - varPtr = VarHashFirstVar(tablePtr, &search)) { - UnsetVarStruct(varPtr, NULL, iPtr, VarHashGetKey(varPtr), NULL, flags, - -1); + varPtr = VarHashFirstVar(tablePtr, &search)) { + + UnsetVarStruct(varPtr, NULL, iPtr, VarHashGetKey(varPtr), NULL, flags); VarHashDeleteEntry(varPtr); } VarHashDeleteTable(tablePtr); @@ -5095,7 +4622,7 @@ TclDeleteCompiledLocalVars( namePtrPtr = &localName(framePtr, 0); for (i=0 ; i<numLocals ; i++, namePtrPtr++, varPtr++) { UnsetVarStruct(varPtr, NULL, iPtr, *namePtrPtr, NULL, - TCL_TRACE_UNSETS, i); + TCL_TRACE_UNSETS); } framePtr->numCompiledLocals = 0; } @@ -5128,10 +4655,9 @@ DeleteArray( * or NULL if it is to be computed on * demand. */ Var *varPtr, /* Pointer to variable structure. */ - int flags, /* Flags to pass to TclCallVarTraces: + int flags) /* Flags to pass to TclCallVarTraces: * TCL_TRACE_UNSETS and sometimes * TCL_NAMESPACE_ONLY or TCL_GLOBAL_ONLY. */ - int index) { Tcl_HashSearch search; Tcl_HashEntry *tPtr; @@ -5167,16 +4693,15 @@ DeleteArray( elPtr->flags &= ~VAR_TRACE_ACTIVE; TclObjCallVarTraces(iPtr, NULL, elPtr, arrayNamePtr, - elNamePtr, flags,/* leaveErrMsg */ 0, index); + elNamePtr, flags,/* leaveErrMsg */ 0, -1); } - tPtr = Tcl_FindHashEntry(&iPtr->varTraces, elPtr); - tracePtr = Tcl_GetHashValue(tPtr); + tPtr = Tcl_FindHashEntry(&iPtr->varTraces, (char *) elPtr); + tracePtr = (VarTrace *) Tcl_GetHashValue(tPtr); while (tracePtr) { VarTrace *prevPtr = tracePtr; tracePtr = tracePtr->nextPtr; - prevPtr->nextPtr = NULL; - Tcl_EventuallyFree(prevPtr, TCL_DYNAMIC); + Tcl_EventuallyFree((ClientData) prevPtr, TCL_DYNAMIC); } Tcl_DeleteHashEntry(tPtr); elPtr->flags &= ~VAR_ALL_TRACES; @@ -5199,13 +4724,13 @@ DeleteArray( TclClearVarNamespaceVar(elPtr); } VarHashDeleteTable(varPtr->value.tablePtr); - ckfree(varPtr->value.tablePtr); + ckfree((char *) varPtr->value.tablePtr); } /* *---------------------------------------------------------------------- * - * TclObjVarErrMsg -- + * TclTclObjVarErrMsg -- * * Generate a reasonable error message describing why a variable * operation failed. @@ -5257,9 +4782,6 @@ TclObjVarErrMsg( * NULL. */ { if (!part1Ptr) { - if (index == -1) { - Tcl_Panic("invalid part1Ptr and invalid index together"); - } part1Ptr = localName(((Interp *)interp)->varFramePtr, index); } Tcl_SetObjResult(interp, Tcl_ObjPrintf("can't %s \"%s%s%s%s\": %s", @@ -5277,20 +4799,41 @@ TclObjVarErrMsg( */ /* + * Panic functions that should never be called in normal operation. + */ + +static void +PanicOnUpdateVarName( + Tcl_Obj *objPtr) +{ + Tcl_Panic("%s of type %s should not be called", "updateStringProc", + objPtr->typePtr->name); +} + +static int +PanicOnSetVarName( + Tcl_Interp *interp, + Tcl_Obj *objPtr) +{ + Tcl_Panic("%s of type %s should not be called", "setFromAnyProc", + objPtr->typePtr->name); + return TCL_ERROR; +} + +/* * localVarName - * * INTERNALREP DEFINITION: - * twoPtrValue.ptr1: pointer to name obj in varFramePtr->localCache - * or NULL if it is this same obj - * twoPtrValue.ptr2: index into locals table + * ptrAndLongRep.ptr: pointer to name obj in varFramePtr->localCache + * or NULL if it is this same obj + * ptrAndLongRep.value: index into locals table */ static void FreeLocalVarName( Tcl_Obj *objPtr) { - Tcl_Obj *namePtr = objPtr->internalRep.twoPtrValue.ptr1; - + Tcl_Obj *namePtr = (Tcl_Obj *) objPtr->internalRep.ptrAndLongRep.ptr; if (namePtr) { Tcl_DecrRefCount(namePtr); } @@ -5302,19 +4845,60 @@ DupLocalVarName( Tcl_Obj *srcPtr, Tcl_Obj *dupPtr) { - Tcl_Obj *namePtr = srcPtr->internalRep.twoPtrValue.ptr1; + Tcl_Obj *namePtr = srcPtr->internalRep.ptrAndLongRep.ptr; if (!namePtr) { namePtr = srcPtr; } - dupPtr->internalRep.twoPtrValue.ptr1 = namePtr; + dupPtr->internalRep.ptrAndLongRep.ptr = namePtr; Tcl_IncrRefCount(namePtr); - dupPtr->internalRep.twoPtrValue.ptr2 = - srcPtr->internalRep.twoPtrValue.ptr2; + dupPtr->internalRep.ptrAndLongRep.value = + srcPtr->internalRep.ptrAndLongRep.value; dupPtr->typePtr = &localVarNameType; } +#if ENABLE_NS_VARNAME_CACHING +/* + * nsVarName - + * + * INTERNALREP DEFINITION: + * twoPtrValue.ptr1: pointer to the namespace containing the reference. + * twoPtrValue.ptr2: pointer to the corresponding Var + */ + +static void +FreeNsVarName( + Tcl_Obj *objPtr) +{ + register Var *varPtr = objPtr->internalRep.twoPtrValue.ptr2; + + if (TclIsVarInHash(varPtr)) { + varPtr->refCount--; + if (TclIsVarUndefined(varPtr) && (varPtr->refCount == 0)) { + CleanupVar(varPtr, NULL); + } + } + objPtr->typePtr = NULL; +} + +static void +DupNsVarName( + Tcl_Obj *srcPtr, + Tcl_Obj *dupPtr) +{ + Namespace *nsPtr = srcPtr->internalRep.twoPtrValue.ptr1; + register Var *varPtr = srcPtr->internalRep.twoPtrValue.ptr2; + + dupPtr->internalRep.twoPtrValue.ptr1 = nsPtr; + dupPtr->internalRep.twoPtrValue.ptr2 = varPtr; + if (TclIsVarInHash(varPtr)) { + varPtr->refCount++; + } + dupPtr->typePtr = &tclNsVarNameType; +} +#endif + /* * parsedVarName - * @@ -5329,11 +4913,11 @@ FreeParsedVarName( Tcl_Obj *objPtr) { register Tcl_Obj *arrayPtr = objPtr->internalRep.twoPtrValue.ptr1; - register Tcl_Obj *elem = objPtr->internalRep.twoPtrValue.ptr2; + register char *elem = objPtr->internalRep.twoPtrValue.ptr2; if (arrayPtr != NULL) { TclDecrRefCount(arrayPtr); - TclDecrRefCount(elem); + ckfree(elem); } objPtr->typePtr = NULL; } @@ -5344,17 +4928,58 @@ DupParsedVarName( Tcl_Obj *dupPtr) { register Tcl_Obj *arrayPtr = srcPtr->internalRep.twoPtrValue.ptr1; - register Tcl_Obj *elem = srcPtr->internalRep.twoPtrValue.ptr2; + register char *elem = srcPtr->internalRep.twoPtrValue.ptr2; + char *elemCopy; if (arrayPtr != NULL) { + unsigned int elemLen; + Tcl_IncrRefCount(arrayPtr); - Tcl_IncrRefCount(elem); + elemLen = strlen(elem); + elemCopy = ckalloc(elemLen+1); + memcpy(elemCopy, elem, elemLen); + *(elemCopy + elemLen) = '\0'; + elem = elemCopy; } dupPtr->internalRep.twoPtrValue.ptr1 = arrayPtr; dupPtr->internalRep.twoPtrValue.ptr2 = elem; dupPtr->typePtr = &tclParsedVarNameType; } + +static void +UpdateParsedVarName( + Tcl_Obj *objPtr) +{ + Tcl_Obj *arrayPtr = objPtr->internalRep.twoPtrValue.ptr1; + char *part2 = objPtr->internalRep.twoPtrValue.ptr2; + char *part1, *p; + int len1, len2, totalLen; + + if (arrayPtr == NULL) { + /* + * This is a parsed scalar name: what is it doing here? + */ + + Tcl_Panic("scalar parsedVarName without a string rep"); + } + + part1 = TclGetStringFromObj(arrayPtr, &len1); + len2 = strlen(part2); + + totalLen = len1 + len2 + 2; + p = ckalloc((unsigned int) totalLen + 1); + objPtr->bytes = p; + objPtr->length = totalLen; + + memcpy(p, part1, (unsigned int) len1); + p += len1; + *p++ = '('; + memcpy(p, part2, (unsigned int) len2); + p += len2; + *p++ = ')'; + *p = '\0'; +} /* *---------------------------------------------------------------------- @@ -5390,12 +5015,11 @@ Tcl_FindNamespaceVar( * Otherwise, points to namespace in which to * resolve name. If NULL, look up name in the * current namespace. */ - int flags) /* An OR'd combination of: - * TCL_AVOID_RESOLVERS, TCL_GLOBAL_ONLY (look - * up name only in global namespace), - * TCL_NAMESPACE_ONLY (look up only in - * contextNsPtr, or the current namespace if - * contextNsPtr is NULL), and + int flags) /* An OR'd combination of: AVOID_RESOLVERS, + * TCL_GLOBAL_ONLY (look up name only in + * global namespace), TCL_NAMESPACE_ONLY (look + * up only in contextNsPtr, or the current + * namespace if contextNsPtr is NULL), and * TCL_LEAVE_ERR_MSG. If both TCL_GLOBAL_ONLY * and TCL_NAMESPACE_ONLY are given, * TCL_GLOBAL_ONLY is ignored. */ @@ -5421,12 +5045,11 @@ ObjFindNamespaceVar( * Otherwise, points to namespace in which to * resolve name. If NULL, look up name in the * current namespace. */ - int flags) /* An OR'd combination of: - * TCL_AVOID_RESOLVERS, TCL_GLOBAL_ONLY (look - * up name only in global namespace), - * TCL_NAMESPACE_ONLY (look up only in - * contextNsPtr, or the current namespace if - * contextNsPtr is NULL), and + int flags) /* An OR'd combination of: AVOID_RESOLVERS, + * TCL_GLOBAL_ONLY (look up name only in + * global namespace), TCL_NAMESPACE_ONLY (look + * up only in contextNsPtr, or the current + * namespace if contextNsPtr is NULL), and * TCL_LEAVE_ERR_MSG. If both TCL_GLOBAL_ONLY * and TCL_NAMESPACE_ONLY are given, * TCL_GLOBAL_ONLY is ignored. */ @@ -5437,10 +5060,9 @@ ObjFindNamespaceVar( const char *simpleName; Var *varPtr; register int search; - int result; Tcl_Var var; Tcl_Obj *simpleNamePtr; - const char *name = TclGetString(namePtr); + char *name = TclGetString(namePtr); /* * If this namespace has a variable resolver, then give it first crack at @@ -5456,12 +5078,14 @@ ObjFindNamespaceVar( cxtNsPtr = (Namespace *) TclGetCurrentNamespace(interp); } - if (!(flags & TCL_AVOID_RESOLVERS) && + if (!(flags & AVOID_RESOLVERS) && (cxtNsPtr->varResProc != NULL || iPtr->resolverPtr != NULL)) { + int result; + resPtr = iPtr->resolverPtr; if (cxtNsPtr->varResProc) { - result = cxtNsPtr->varResProc(interp, name, + result = (*cxtNsPtr->varResProc)(interp, name, (Tcl_Namespace *) cxtNsPtr, flags, &var); } else { result = TCL_CONTINUE; @@ -5469,7 +5093,7 @@ ObjFindNamespaceVar( while (result == TCL_CONTINUE && resPtr) { if (resPtr->varResProc) { - result = resPtr->varResProc(interp, name, + result = (*resPtr->varResProc)(interp, name, (Tcl_Namespace *) cxtNsPtr, flags, &var); } resPtr = resPtr->nextPtr; @@ -5478,7 +5102,7 @@ ObjFindNamespaceVar( if (result == TCL_OK) { return var; } else if (result != TCL_CONTINUE) { - return NULL; + return (Tcl_Var) NULL; } } @@ -5511,8 +5135,8 @@ ObjFindNamespaceVar( Tcl_DecrRefCount(simpleNamePtr); } if ((varPtr == NULL) && (flags & TCL_LEAVE_ERR_MSG)) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "unknown variable \"%s\"", name)); + Tcl_ResetResult(interp); + Tcl_AppendResult(interp, "unknown variable \"", name, "\"", NULL); Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "VARIABLE", name, NULL); } return (Tcl_Var) varPtr; @@ -5550,15 +5174,15 @@ TclInfoVarsCmd( Tcl_Obj *const objv[]) /* Argument objects. */ { Interp *iPtr = (Interp *) interp; - const char *varName, *pattern, *simplePattern; + char *pattern; + const char *simplePattern; Tcl_HashSearch search; - Var *varPtr; Namespace *nsPtr; Namespace *globalNsPtr = (Namespace *) Tcl_GetGlobalNamespace(interp); Namespace *currNsPtr = (Namespace *) Tcl_GetCurrentNamespace(interp); - Tcl_Obj *listPtr, *elemObjPtr, *varNamePtr; + Tcl_Obj *listPtr; int specificNsInPattern = 0;/* Init. to avoid compiler warning. */ - Tcl_Obj *simplePatternPtr = NULL; + Tcl_Obj *simplePatternPtr = NULL, *varNamePtr; /* * Get the pattern and find the "effective namespace" in which to list @@ -5582,8 +5206,9 @@ TclInfoVarsCmd( Namespace *dummy1NsPtr, *dummy2NsPtr; pattern = TclGetString(objv[1]); - TclGetNamespaceForQualName(interp, pattern, NULL, /*flags*/ 0, - &nsPtr, &dummy1NsPtr, &dummy2NsPtr, &simplePattern); + TclGetNamespaceForQualName(interp, pattern, (Namespace *) NULL, + /*flags*/ 0, &nsPtr, &dummy1NsPtr, &dummy2NsPtr, + &simplePattern); if (nsPtr != NULL) { /* We successfully found the pattern's ns. */ specificNsInPattern = (strcmp(simplePattern, pattern) != 0); @@ -5609,7 +5234,11 @@ TclInfoVarsCmd( listPtr = Tcl_NewListObj(0, NULL); - if (!HasLocalVars(iPtr->varFramePtr) || specificNsInPattern) { + if (!(iPtr->varFramePtr->isProcCallFrame & FRAME_IS_PROC) + || specificNsInPattern) { + Var *varPtr; + Tcl_Obj *elemObjPtr; + /* * There is no frame pointer, the frame pointer was pushed only to * activate a namespace, or we are in a procedure call frame but a @@ -5650,9 +5279,11 @@ TclInfoVarsCmd( /* * Have to scan the tables of variables. */ + char *varName; varPtr = VarHashFirstVar(&nsPtr->varTable, &search); while (varPtr) { + if (!TclIsVarUndefined(varPtr) || TclIsVarNamespaceVar(varPtr)) { varNamePtr = VarHashGetKey(varPtr); @@ -5701,7 +5332,7 @@ TclInfoVarsCmd( } } } - } else if (iPtr->varFramePtr->procPtr != NULL) { + } else if (((Interp *)interp)->varFramePtr->procPtr != NULL) { AppendLocals(interp, listPtr, simplePatternPtr, 1); } @@ -5740,11 +5371,11 @@ TclInfoGlobalsCmd( int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { - const char *varName, *pattern; + char *pattern; Namespace *globalNsPtr = (Namespace *) Tcl_GetGlobalNamespace(interp); Tcl_HashSearch search; Var *varPtr; - Tcl_Obj *listPtr, *varNamePtr, *patternPtr; + Tcl_Obj *listPtr, *patternPtr; if (objc == 1) { pattern = NULL; @@ -5791,6 +5422,9 @@ TclInfoGlobalsCmd( for (varPtr = VarHashFirstVar(&globalNsPtr->varTable, &search); varPtr != NULL; varPtr = VarHashNextVar(&search)) { + char *varName; + Tcl_Obj *varNamePtr; + if (TclIsVarUndefined(varPtr)) { continue; } @@ -5834,7 +5468,8 @@ TclInfoLocalsCmd( Tcl_Obj *const objv[]) /* Argument objects. */ { Interp *iPtr = (Interp *) interp; - Tcl_Obj *patternPtr, *listPtr; + Tcl_Obj *patternPtr; + Tcl_Obj *listPtr; if (objc == 1) { patternPtr = NULL; @@ -5845,7 +5480,7 @@ TclInfoLocalsCmd( return TCL_ERROR; } - if (!HasLocalVars(iPtr->varFramePtr)) { + if (!(iPtr->varFramePtr->isProcCallFrame & FRAME_IS_PROC )) { return TCL_OK; } @@ -5887,21 +5522,18 @@ AppendLocals( { Interp *iPtr = (Interp *) interp; Var *varPtr; - int i, localVarCt, added; - Tcl_Obj **varNamePtr, *objNamePtr; - const char *varName; + int i, localVarCt; + Tcl_Obj **varNamePtr; + char *varName; TclVarHashTable *localVarTablePtr; Tcl_HashSearch search; - Tcl_HashTable addedTable; const char *pattern = patternPtr? TclGetString(patternPtr) : NULL; + Tcl_Obj *objNamePtr; localVarCt = iPtr->varFramePtr->numCompiledLocals; varPtr = iPtr->varFramePtr->compiledLocals; localVarTablePtr = iPtr->varFramePtr->varTablePtr; varNamePtr = &iPtr->varFramePtr->localCachePtr->varName0; - if (includeLinks) { - Tcl_InitObjHashTable(&addedTable); - } for (i = 0; i < localVarCt; i++, varNamePtr++) { /* @@ -5913,9 +5545,6 @@ AppendLocals( varName = TclGetString(*varNamePtr); if ((pattern == NULL) || Tcl_StringMatch(varName, pattern)) { Tcl_ListObjAppendElement(interp, listPtr, *varNamePtr); - if (includeLinks) { - Tcl_CreateHashEntry(&addedTable, *varNamePtr, &added); - } } } varPtr++; @@ -5926,7 +5555,7 @@ AppendLocals( */ if (localVarTablePtr == NULL) { - goto objectVars; + return; } /* @@ -5940,13 +5569,9 @@ AppendLocals( && (includeLinks || !TclIsVarLink(varPtr))) { Tcl_ListObjAppendElement(interp, listPtr, VarHashGetKey(varPtr)); - if (includeLinks) { - Tcl_CreateHashEntry(&addedTable, VarHashGetKey(varPtr), - &added); - } } } - goto objectVars; + return; } /* @@ -5962,41 +5587,9 @@ AppendLocals( varName = TclGetString(objNamePtr); if ((pattern == NULL) || Tcl_StringMatch(varName, pattern)) { Tcl_ListObjAppendElement(interp, listPtr, objNamePtr); - if (includeLinks) { - Tcl_CreateHashEntry(&addedTable, objNamePtr, &added); - } - } - } - } - - objectVars: - if (!includeLinks) { - return; - } - - if (iPtr->varFramePtr->isProcCallFrame & FRAME_IS_METHOD) { - CallContext *contextPtr = iPtr->varFramePtr->clientData; - Method *mPtr = contextPtr->callPtr->chain[contextPtr->index].mPtr; - - if (mPtr->declaringObjectPtr) { - FOREACH(objNamePtr, mPtr->declaringObjectPtr->variables) { - Tcl_CreateHashEntry(&addedTable, objNamePtr, &added); - if (added && (!pattern || - Tcl_StringMatch(TclGetString(objNamePtr), pattern))) { - Tcl_ListObjAppendElement(interp, listPtr, objNamePtr); - } - } - } else { - FOREACH(objNamePtr, mPtr->declaringClassPtr->variables) { - Tcl_CreateHashEntry(&addedTable, objNamePtr, &added); - if (added && (!pattern || - Tcl_StringMatch(TclGetString(objNamePtr), pattern))) { - Tcl_ListObjAppendElement(interp, listPtr, objNamePtr); - } } } } - Tcl_DeleteHashTable(&addedTable); } /* @@ -6018,16 +5611,16 @@ AllocVarEntry( Tcl_HashTable *tablePtr, /* Hash table. */ void *keyPtr) /* Key to store in the hash table entry. */ { - Tcl_Obj *objPtr = keyPtr; + Tcl_Obj *objPtr = (Tcl_Obj *) keyPtr; Tcl_HashEntry *hPtr; Var *varPtr; - varPtr = ckalloc(sizeof(VarInHash)); + varPtr = (Var *) ckalloc(sizeof(VarInHash)); varPtr->flags = VAR_IN_HASHTABLE; varPtr->value.objPtr = NULL; VarHashRefCount(varPtr) = 1; - hPtr = &(((VarInHash *) varPtr)->entry); + hPtr = &(((VarInHash *)varPtr)->entry); Tcl_SetHashValue(hPtr, varPtr); hPtr->key.objPtr = objPtr; Tcl_IncrRefCount(objPtr); @@ -6044,7 +5637,7 @@ FreeVarEntry( if (TclIsVarUndefined(varPtr) && !TclIsVarTraced(varPtr) && (VarHashRefCount(varPtr) == 1)) { - ckfree(varPtr); + ckfree((char *) varPtr); } else { VarHashInvalidateEntry(varPtr); TclSetVarUndefined(varPtr); @@ -6058,17 +5651,18 @@ CompareVarKeys( void *keyPtr, /* New key to compare. */ Tcl_HashEntry *hPtr) /* Existing key to compare. */ { - Tcl_Obj *objPtr1 = keyPtr; + Tcl_Obj *objPtr1 = (Tcl_Obj *) keyPtr; Tcl_Obj *objPtr2 = hPtr->key.objPtr; register const char *p1, *p2; register int l1, l2; /* * If the object pointers are the same then they match. - * OPT: this comparison was moved to the caller + */ - if (objPtr1 == objPtr2) return 1; - */ + if (objPtr1 == objPtr2) { + return 1; + } /* * Don't use Tcl_GetStringFromObj as it would prevent l1 and l2 being in a @@ -6081,10 +5675,54 @@ CompareVarKeys( l2 = objPtr2->length; /* - * Only compare string representations of the same length. + * Only compare if the string representations are of the same length. + */ + + if (l1 == l2) { + for (;; p1++, p2++, l1--) { + if (*p1 != *p2) { + break; + } + if (l1 == 0) { + return 1; + } + } + } + + return 0; +} + +static unsigned int +HashVarKey( + Tcl_HashTable *tablePtr, /* Hash table. */ + void *keyPtr) /* Key from which to compute hash value. */ +{ + Tcl_Obj *objPtr = (Tcl_Obj *) keyPtr; + const char *string = TclGetString(objPtr); + int length = objPtr->length; + unsigned int result = 0; + int i; + + /* + * I tried a zillion different hash functions and asked many other people + * for advice. Many people had their own favorite functions, all + * different, but no-one had much idea why they were good ones. I chose + * the one below (multiply by 9 and add new character) because of the + * following reasons: + * + * 1. Multiplying by 10 is perfect for keys that are decimal strings, and + * multiplying by 9 is just about as good. + * 2. Times-9 is (shift-left-3) plus (old). This means that each + * character's bits hang around in the low-order bits of the hash value + * for ever, plus they spread fairly rapidly up to the high-order bits + * to fill out the hash value. This seems works well both for decimal + * and non-decimal strings. */ - return ((l1 == l2) && !memcmp(p1, p2, l1)); + for (i=0 ; i<length ; i++) { + result += (result << 3) + string[i]; + } + return result; } /* |
