diff options
Diffstat (limited to 'generic/tclVar.c')
| -rw-r--r-- | generic/tclVar.c | 4274 |
1 files changed, 1560 insertions, 2714 deletions
diff --git a/generic/tclVar.c b/generic/tclVar.c index 48e8cc3..bdc64b7 100644 --- a/generic/tclVar.c +++ b/generic/tclVar.c @@ -7,18 +7,17 @@ * The implementation of arrays is modelled after an initial * implementation by Mark Diekhans and Karl Lehenbauer. * - * Copyright © 1987-1994 The Regents of the University of California. - * Copyright © 1994-1997 Sun Microsystems, Inc. - * Copyright © 1998-1999 Scriptics Corporation. - * Copyright © 2001 Kevin B. Kenny. All rights reserved. - * Copyright © 2007 Miguel Sofer + * Copyright (c) 1987-1994 The Regents of the University of California. + * Copyright (c) 1994-1997 Sun Microsystems, Inc. + * Copyright (c) 1998-1999 by Scriptics Corporation. + * Copyright (c) 2001 by Kevin B. Kenny. All rights reserved. + * Copyright (c) 2007 Miguel Sofer * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ #include "tclInt.h" -#include "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 */ @@ -45,7 +45,7 @@ static inline Var * VarHashNextVar(Tcl_HashSearch *searchPtr); static inline void CleanupVar(Var *varPtr, Var *arrayPtr); #define VarHashGetValue(hPtr) \ - ((Var *) ((char *)hPtr - offsetof(VarInHash, entry))) + ((Var *) ((char *)hPtr - TclOffset(VarInHash, entry))) /* * NOTE: VarHashCreateVar increments the recount of its key argument. @@ -60,25 +60,25 @@ 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) { + if (hPtr) { + return VarHashGetValue(hPtr); + } else { return NULL; } - return VarHashGetValue(hPtr); } #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)) @@ -90,10 +90,11 @@ VarHashFirstVar( { Tcl_HashEntry *hPtr = VarHashFirstEntry(tablePtr, searchPtr); - if (!hPtr) { + if (hPtr) { + return VarHashGetValue(hPtr); + } else { return NULL; } - return VarHashGetValue(hPtr); } static inline Var * @@ -102,31 +103,35 @@ VarHashNextVar( { Tcl_HashEntry *hPtr = VarHashNextEntry(searchPtr); - if (!hPtr) { + if (hPtr) { + return VarHashGetValue(hPtr); + } else { return NULL; } - return VarHashGetValue(hPtr); } +#define VarHashGetKey(varPtr) \ + (((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 * access is denied. */ -static const char NOSUCHVAR[] = "no such variable"; -static const char ISARRAY[] = "variable is array"; -static const char NEEDARRAY[] = "variable isn't array"; -static const char NOSUCHELEMENT[] = "no such element in array"; -static const char DANGLINGELEMENT[] = +static const char *noSuchVar = "no such variable"; +static const char *isArray = "variable is array"; +static const char *needArray = "variable isn't array"; +static const char *noSuchElement = "no such element in array"; +static const char *danglingElement = "upvar refers to element in deleted array"; -static const char DANGLINGVAR[] = +static const char *danglingVar = "upvar refers to variable in deleted namespace"; -static const char BADNAMESPACE[] = "parent namespace doesn't exist"; -static const char MISSINGNAME[] = "missing variable name"; -static const char ISARRAYELEMENT[] = +static const char *badNamespace = "parent namespace doesn't exist"; +static const char *missingName = "missing variable name"; +static const char *isArrayElement = "name refers to an element in an array"; /* @@ -142,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. */ @@ -162,56 +166,28 @@ typedef struct ArraySearch { } ArraySearch; /* - * TIP #508: [array default] - * - * The following structure extends the regular TclVarHashTable used by array - * variables to store their optional default value. - */ - -typedef struct ArrayVarHashTable { - TclVarHashTable table; - Tcl_Obj *defaultObj; -} ArrayVarHashTable; - -/* * Forward references to functions defined later in this file: */ static void AppendLocals(Tcl_Interp *interp, Tcl_Obj *listPtr, Tcl_Obj *patternPtr, int includeLinks); -static void ArrayPopulateSearch(Tcl_Interp *interp, - Tcl_Obj *arrayNameObj, Var *varPtr, - ArraySearch *searchPtr); -static void ArrayDoneSearch(Interp *iPtr, Var *varPtr, - ArraySearch *searchPtr); -static Tcl_NRPostProc ArrayForLoopCallback; -static Tcl_ObjCmdProc ArrayForNRCmd; static void DeleteSearches(Interp *iPtr, Var *arrayVarPtr); static void DeleteArray(Interp *iPtr, Tcl_Obj *arrayNamePtr, - Var *varPtr, int flags, int index); -static int LocateArray(Tcl_Interp *interp, Tcl_Obj *name, - Var **varPtrPtr, int *isArrayPtr); -static int NotArrayError(Tcl_Interp *interp, Tcl_Obj *name); -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, CallFrame *framePtr, Tcl_Obj *otherP1Ptr, - const char *otherP2, int otherFlags, + const char *otherP2, const int otherFlags, Tcl_Obj *myNamePtr, int myFlags, int index); 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); - -/* - * TIP #508: [array default] - */ - -static Tcl_ObjCmdProc ArrayDefaultCmd; -static void DeleteArrayVar(Var *arrayPtr); -static void SetArrayDefault(Var *arrayPtr, Tcl_Obj *defaultObj); + 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 @@ -219,22 +195,31 @@ static void SetArrayDefault(Var *arrayPtr, Tcl_Obj *defaultObj); */ MODULE_SCOPE Var * TclLookupSimpleVar(Tcl_Interp *interp, - Tcl_Obj *varNamePtr, int flags, int create, + Tcl_Obj *varNamePtr, int flags, const int create, const char **errMsgPtr, int *indexPtr); 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 @@ -243,54 +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 }; -#define LocalSetInternalRep(objPtr, index, namePtr) \ - do { \ - Tcl_ObjInternalRep ir; \ - Tcl_Obj *ptr = (namePtr); \ - if (ptr) {Tcl_IncrRefCount(ptr);} \ - ir.twoPtrValue.ptr1 = ptr; \ - ir.twoPtrValue.ptr2 = INT2PTR(index); \ - Tcl_StoreInternalRep((objPtr), &localVarNameType, &ir); \ - } while (0) - -#define LocalGetInternalRep(objPtr, index, name) \ - do { \ - const Tcl_ObjInternalRep *irPtr; \ - irPtr = TclFetchInternalRep((objPtr), &localVarNameType); \ - (name) = irPtr ? (Tcl_Obj *)irPtr->twoPtrValue.ptr1 : NULL; \ - (index) = irPtr ? PTR2INT(irPtr->twoPtrValue.ptr2) : TCL_INDEX_NONE; \ - } while (0) - -static const Tcl_ObjType parsedVarNameType = { +/* + * 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 Tcl_ObjType tclParsedVarNameType = { "parsedVarName", - FreeParsedVarName, DupParsedVarName, NULL, NULL + FreeParsedVarName, DupParsedVarName, UpdateParsedVarName, PanicOnSetVarName }; -#define ParsedSetInternalRep(objPtr, arrayPtr, elem) \ - do { \ - Tcl_ObjInternalRep ir; \ - Tcl_Obj *ptr1 = (arrayPtr); \ - Tcl_Obj *ptr2 = (elem); \ - if (ptr1) {Tcl_IncrRefCount(ptr1);} \ - if (ptr2) {Tcl_IncrRefCount(ptr2);} \ - ir.twoPtrValue.ptr1 = ptr1; \ - ir.twoPtrValue.ptr2 = ptr2; \ - Tcl_StoreInternalRep((objPtr), &parsedVarNameType, &ir); \ - } while (0) - -#define ParsedGetInternalRep(objPtr, parsed, array, elem) \ - do { \ - const Tcl_ObjInternalRep *irPtr; \ - irPtr = TclFetchInternalRep((objPtr), &parsedVarNameType); \ - (parsed) = (irPtr != NULL); \ - (array) = irPtr ? (Tcl_Obj *)irPtr->twoPtrValue.ptr1 : NULL; \ - (elem) = irPtr ? (Tcl_Obj *)irPtr->twoPtrValue.ptr2 : NULL; \ - } while (0) +/* + * 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( @@ -308,42 +290,6 @@ TclVarHashCreateVar( return varPtr; } - -static int -LocateArray( - Tcl_Interp *interp, - Tcl_Obj *name, - Var **varPtrPtr, - int *isArrayPtr) -{ - Var *arrayPtr, *varPtr = TclObjLookupVarEx(interp, name, NULL, /*flags*/ 0, - /*msg*/ 0, /*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr); - - if (TclCheckArrayTraces(interp, varPtr, arrayPtr, name, -1) == TCL_ERROR) { - return TCL_ERROR; - } - if (varPtrPtr) { - *varPtrPtr = varPtr; - } - if (isArrayPtr) { - *isArrayPtr = varPtr && !TclIsVarUndefined(varPtr) - && TclIsVarArray(varPtr); - } - return TCL_OK; -} - -static int -NotArrayError( - Tcl_Interp *interp, - Tcl_Obj *name) -{ - const char *nameStr = Tcl_GetString(name); - - Tcl_SetObjResult(interp, - Tcl_ObjPrintf("\"%s\" isn't an array", nameStr)); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "ARRAY", nameStr, (void *)NULL); - return TCL_ERROR; -} /* *---------------------------------------------------------------------- @@ -376,20 +322,18 @@ CleanupVar( { if (TclIsVarUndefined(varPtr) && TclIsVarInHash(varPtr) && !TclIsVarTraced(varPtr) - && (VarHashRefCount(varPtr) == (Tcl_Size) - !TclIsVarDeadHash(varPtr))) { + && (VarHashRefCount(varPtr) == !TclIsVarDeadHash(varPtr))) { if (VarHashRefCount(varPtr) == 0) { - ckfree(varPtr); + ckfree((char *) varPtr); } else { VarHashDeleteEntry(varPtr); } } if (arrayPtr != NULL && TclIsVarUndefined(arrayPtr) && TclIsVarInHash(arrayPtr) && !TclIsVarTraced(arrayPtr) && - (VarHashRefCount(arrayPtr) == (Tcl_Size) - !TclIsVarDeadHash(arrayPtr))) { + (VarHashRefCount(arrayPtr) == !TclIsVarDeadHash(arrayPtr))) { if (VarHashRefCount(arrayPtr) == 0) { - ckfree(arrayPtr); + ckfree((char *) arrayPtr); } else { VarHashDeleteEntry(arrayPtr); } @@ -515,8 +459,9 @@ TclLookupVar( * * Side effects: * New hashtable entries may be created if createPart1 or createPart2 - * are 1. The object part1Ptr is converted to one of localVarNameType - * or parsedVarNameType and caches as much of the lookup as it can. + * are 1. The object part1Ptr is converted to one of localVarNameType, + * tclNsVarNameType or tclParsedVarNameType and caches as much of the + * lookup as it can. * When createPart1 is 1, callers must IncrRefCount part1Ptr if they * plan to DecrRefCount it. * @@ -526,7 +471,7 @@ TclLookupVar( Var * TclObjLookupVar( Tcl_Interp *interp, /* Interpreter to use for lookup. */ - Tcl_Obj *part1Ptr, /* If part2 isn't NULL, this is the name of an + register Tcl_Obj *part1Ptr, /* If part2 isn't NULL, this is the name of an * array. Otherwise, this is a full variable * name that could include a parenthesized * array element. */ @@ -536,10 +481,10 @@ TclObjLookupVar( const char *msg, /* Verb to use in error messages, e.g. "read" * or "set". Only needed if TCL_LEAVE_ERR_MSG * is set in flags. */ - int createPart1, /* If 1, create hash table entry for part 1 of + const int createPart1, /* If 1, create hash table entry for part 1 of * name, if it doesn't already exist. If 0, * return error if it doesn't exist. */ - int createPart2, /* If 1, create hash table entry for part 2 of + const int createPart2, /* If 1, create hash table entry for part 2 of * name, if it doesn't already exist. If 0, * return error if it doesn't exist. */ Var **arrayPtrPtr) /* If the name refers to an element of an @@ -586,10 +531,10 @@ TclObjLookupVarEx( const char *msg, /* Verb to use in error messages, e.g. "read" * or "set". Only needed if TCL_LEAVE_ERR_MSG * is set in flags. */ - int createPart1, /* If 1, create hash table entry for part 1 of + const int createPart1, /* If 1, create hash table entry for part 1 of * name, if it doesn't already exist. If 0, * return error if it doesn't exist. */ - int createPart2, /* If 1, create hash table entry for part 2 of + const int createPart2, /* If 1, create hash table entry for part 2 of * name, if it doesn't already exist. If 0, * return error if it doesn't exist. */ Var **arrayPtrPtr) /* If the name refers to an element of an @@ -598,20 +543,41 @@ TclObjLookupVarEx( * is set to NULL. */ { Interp *iPtr = (Interp *) interp; - CallFrame *varFramePtr = iPtr->varFramePtr; - Var *varPtr; /* Points to the variable's in-frame Var + register Var *varPtr; /* Points to the variable's in-frame Var * structure. */ + char *part1; + int index, len1, len2; + int parsed = 0; + Tcl_Obj *objPtr; + const Tcl_ObjType *typePtr = part1Ptr->typePtr; const char *errMsg = NULL; - int index, parsed = 0; - - Tcl_Size localIndex; - Tcl_Obj *namePtr, *arrayPtr, *elem; + CallFrame *varFramePtr = iPtr->varFramePtr; +#if ENABLE_NS_VARNAME_CACHING + Namespace *nsPtr; +#endif + char *part2 = part2Ptr? TclGetString(part2Ptr):NULL; + char *newPart2 = NULL; *arrayPtrPtr = NULL; - restart: - LocalGetInternalRep(part1Ptr, localIndex, namePtr); - if (localIndex >= 0) { +#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 = (int) part1Ptr->internalRep.ptrAndLongRep.value; if (HasLocalVars(varFramePtr) && !(flags & (TCL_GLOBAL_ONLY | TCL_NAMESPACE_ONLY)) && (localIndex < varFramePtr->numCompiledLocals)) { @@ -619,7 +585,9 @@ TclObjLookupVarEx( * Use the cached index if the names coincide. */ - 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))) { @@ -628,14 +596,53 @@ 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 } /* - * If part1Ptr is a parsedVarNameType, retrieve the preparsed parts. + * If part1Ptr is a tclParsedVarNameType, separate it into the pre-parsed + * parts. */ - ParsedGetInternalRep(part1Ptr, parsed, arrayPtr, elem); - if (parsed && arrayPtr) { + if (typePtr == &tclParsedVarNameType) { + if (part1Ptr->internalRep.twoPtrValue.ptr1 != NULL) { if (part2Ptr != NULL) { /* * ERROR: part1Ptr is already an array element, cannot specify @@ -644,45 +651,88 @@ TclObjLookupVarEx( if (flags & TCL_LEAVE_ERR_MSG) { TclObjVarErrMsg(interp, part1Ptr, part2Ptr, msg, - NOSUCHVAR, -1); - Tcl_SetErrorCode(interp, "TCL", "VALUE", "VARNAME", (void *)NULL); + noSuchVar, -1); } return NULL; } - part2Ptr = elem; - part1Ptr = arrayPtr; - goto restart; + 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) { + goto localVarNameTypeHandling; + } + } + parsed = 1; } + part1 = TclGetStringFromObj(part1Ptr, &len1); - if (!parsed) { + if (!parsed && len1 && (*(part1 + len1 - 1) == ')')) { /* * part1Ptr is possibly an unparsed array element. */ - Tcl_Size len; - const char *part1 = TclGetStringFromObj(part1Ptr, &len); - - if ((len > 1) && (part1[len - 1] == ')')) { - const char *part2 = strchr(part1, '('); + register int i; - if (part2) { + 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", - (void *)NULL); + needArray, -1); } 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. + */ - ParsedSetInternalRep(part1Ptr, arrayPtr, part2Ptr); + 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); + } - part1Ptr = arrayPtr; + /* + * 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; + + typePtr = part1Ptr->typePtr; + part1 = TclGetString(part1Ptr); + break; } } } @@ -693,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), (void *)NULL); + } + if (newPart2) { + Tcl_DecrRefCount(part2Ptr); } return NULL; } @@ -713,48 +767,51 @@ TclObjLookupVarEx( * An indexed local variable. */ - Tcl_Obj *cachedNamePtr = localName(varFramePtr, index); - - if (part1Ptr == cachedNamePtr) { - LocalSetInternalRep(part1Ptr, index, NULL); + part1Ptr->typePtr = &localVarNameType; + if (part1Ptr != localName(iPtr->varFramePtr, index)) { + part1Ptr->internalRep.ptrAndLongRep.ptr = + localName(iPtr->varFramePtr, index); + Tcl_IncrRefCount((Tcl_Obj *) + part1Ptr->internalRep.ptrAndLongRep.ptr); } else { - /* - * [80304238ac] Trickiness here. We will store and incr the - * refcount on cachedNamePtr. Trouble is that it's possible - * (see test var-22.1) for cachedNamePtr to have an internalrep - * that contains a stored and refcounted part1Ptr. This - * would be a reference cycle which leads to a memory leak. - * - * The solution here is to wipe away all internalrep(s) in - * cachedNamePtr and leave it as string only. This is - * radical and destructive, so a better idea would be welcome. - */ - - /* - * Firstly set cached local var reference (avoid free before set, - * see [45b9faf103f2]) - */ - LocalSetInternalRep(part1Ptr, index, cachedNamePtr); + part1Ptr->internalRep.ptrAndLongRep.ptr = NULL; + } + part1Ptr->internalRep.ptrAndLongRep.value = (long) index; +#if ENABLE_NS_VARNAME_CACHING + } else if (index > -3) { + /* + * A cacheable namespace or global variable. + */ - /* Then wipe it */ - TclFreeInternalRep(cachedNamePtr); + Namespace *nsPtr; - /* - * Now go ahead and convert it the the "localVarName" type, - * since we suspect at least some use of the value as a - * varname and we want to resolve it quickly. - */ - LocalSetInternalRep(cachedNamePtr, index, NULL); - } + 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. */ - ParsedSetInternalRep(part1Ptr, NULL, NULL); + part1Ptr->typePtr = &tclParsedVarNameType; + part1Ptr->internalRep.twoPtrValue.ptr1 = NULL; + part1Ptr->internalRep.twoPtrValue.ptr2 = NULL; } 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; } @@ -767,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 -- @@ -792,7 +867,7 @@ TclObjLookupVarEx( * TclObjLookupVar): * -1 a global reference * -2 a reference to a namespace variable - * -3 a non-cacheable reference, i.e., one of: + * -3 a non-cachable reference, i.e., one of: * . non-indexed local var * . a reference of unknown origin; * . resolution by a namespace or interp resolver @@ -820,9 +895,9 @@ 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. */ - int create, /* If 1, create hash table entry for varname, + * 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. */ const char **errMsgPtr, @@ -841,9 +916,8 @@ TclLookupSimpleVar( * the variable. */ Namespace *varNsPtr, *cxtNsPtr, *dummy1Ptr, *dummy2Ptr; ResolverScheme *resPtr; - int isNew, result; - Tcl_Size i, 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. */ @@ -862,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; @@ -873,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; @@ -915,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) { @@ -930,72 +1006,67 @@ 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 and not to create it. */ - *errMsgPtr = NOSUCHVAR; - return NULL; - } + if (create) { /* Var wasn't found so create it. */ + Tcl_Obj *tailPtr; - /* - * Var wasn't found so create it. - */ - - TclGetNamespaceForQualName(interp, varName, cxtNsPtr, flags, - &varNsPtr, &dummy1Ptr, &dummy2Ptr, &tail); - if (varNsPtr == NULL) { - *errMsgPtr = BADNAMESPACE; - return NULL; - } else if (tail == NULL) { - *errMsgPtr = MISSINGNAME; + TclGetNamespaceForQualName(interp, varName, cxtNsPtr, + flags, &varNsPtr, &dummy1Ptr, &dummy2Ptr, &tail); + if (varNsPtr == NULL) { + *errMsgPtr = badNamespace; + return NULL; + } else if (tail == NULL) { + *errMsgPtr = missingName; + return NULL; + } + if (tail != varName) { + tailPtr = Tcl_NewStringObj(tail, -1); + } else { + tailPtr = varNamePtr; + } + varPtr = VarHashCreateVar(&varNsPtr->varTable, tailPtr, + &isNew); + if (lookGlobal) { + /* + * The variable was created starting from the global + * namespace: a global reference is returned even if it + * wasn't explicitly requested. + */ + + *indexPtr = -1; + } else { + *indexPtr = -2; + } + } else { /* Var wasn't found and not to create it. */ + *errMsgPtr = noSuchVar; return NULL; } - if (tail != varName) { - tailPtr = Tcl_NewStringObj(tail, -1); - } else { - tailPtr = varNamePtr; - } - varPtr = VarHashCreateVar(&varNsPtr->varTable, tailPtr, &isNew); - if (lookGlobal) { - /* - * The variable was created starting from the global - * namespace: a global reference is returned even if it wasn't - * explicitly requested. - */ - - *indexPtr = -1; - } else { - *indexPtr = -2; - } } } else { /* Local var: look in frame varFramePtr. */ - Tcl_Size localCt = varFramePtr->numCompiledLocals; - - if (localCt > 0) { - Tcl_Obj **objPtrPtr = &varFramePtr->localCachePtr->varName0; - const char *localNameStr; - Tcl_Size localLen; + int localCt = varFramePtr->numCompiledLocals; + Tcl_Obj **objPtrPtr = &varFramePtr->localCachePtr->varName0; + int i; - for (i=0 ; i<localCt ; i++, objPtrPtr++) { - Tcl_Obj *objPtr = *objPtrPtr; + for (i=0 ; i<localCt ; i++, objPtrPtr++) { + register Tcl_Obj *objPtr = *objPtrPtr; - if (objPtr) { - localNameStr = TclGetStringFromObj(objPtr, &localLen); + if (objPtr) { + char *localName = TclGetString(objPtr); - if ((varLen == localLen) && (varName[0] == localNameStr[0]) - && !memcmp(varName, localNameStr, varLen)) { - *indexPtr = i; - return (Var *) &varFramePtr->compiledLocals[i]; - } + if ((varName[0] == localName[0]) + && (strcmp(varName, localName) == 0)) { + *indexPtr = i; + return (Var *) &varFramePtr->compiledLocals[i]; } } } tablePtr = varFramePtr->varTablePtr; if (create) { if (tablePtr == NULL) { - tablePtr = (TclVarHashTable *)ckalloc(sizeof(TclVarHashTable)); + tablePtr = (TclVarHashTable *) + ckalloc(sizeof(TclVarHashTable)); TclInitVarHashTable(tablePtr, NULL); varFramePtr->varTablePtr = tablePtr; } @@ -1006,7 +1077,7 @@ TclLookupSimpleVar( varPtr = VarHashFindVar(tablePtr, varNamePtr); } if (varPtr == NULL) { - *errMsgPtr = NOSUCHVAR; + *errMsgPtr = noSuchVar; } } } @@ -1058,15 +1129,15 @@ TclLookupArrayElement( Tcl_Obj *arrayNamePtr, /* This is the name of the array, or NULL if * index>= 0. */ Tcl_Obj *elNamePtr, /* Name of element within array. */ - int flags, /* Only TCL_LEAVE_ERR_MSG bit matters. */ + const int flags, /* Only TCL_LEAVE_ERR_MSG bit matters. */ const char *msg, /* Verb to use in error messages, e.g. "read" * or "set". Only needed if TCL_LEAVE_ERR_MSG * is set in flags. */ - int createArray, /* If 1, transform arrayName to be an array if + const int createArray, /* If 1, transform arrayName to be an array if * it isn't one yet and the transformation is * possible. If 0, return error if it isn't * already an array. */ - int createElem, /* If 1, create hash table entry for the + const int createElem, /* If 1, create hash table entry for the * element, if it doesn't already exist. If 0, * return error if it doesn't exist. */ Var *arrayPtr, /* Pointer to the array's Var structure. */ @@ -1074,6 +1145,7 @@ TclLookupArrayElement( { int isNew; Var *varPtr; + TclVarHashTable *tablePtr; /* * We're dealing with an array element. Make sure the variable is an array @@ -1081,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, (void *)NULL); + noSuchVar, index); } return NULL; } @@ -1099,20 +1171,25 @@ TclLookupArrayElement( if (TclIsVarDeadHash(arrayPtr)) { if (flags & TCL_LEAVE_ERR_MSG) { TclObjVarErrMsg(interp, arrayNamePtr, elNamePtr, msg, - DANGLINGVAR, index); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "VARNAME", - arrayNamePtr?TclGetString(arrayNamePtr):NULL, (void *)NULL); + danglingVar, index); } return NULL; } - TclInitArrayVar(arrayPtr); + TclSetVarArray(arrayPtr); + tablePtr = (TclVarHashTable *) ckalloc(sizeof(TclVarHashTable)); + arrayPtr->value.tablePtr = tablePtr; + + if (TclIsVarInHash(arrayPtr) && TclGetVarNsPtr(arrayPtr)) { + nsPtr = TclGetVarNsPtr(arrayPtr); + } else { + nsPtr = NULL; + } + TclInitVarHashTable(arrayPtr->value.tablePtr, nsPtr); } else if (!TclIsVarArray(arrayPtr)) { if (flags & TCL_LEAVE_ERR_MSG) { - TclObjVarErrMsg(interp, arrayNamePtr, elNamePtr, msg, NEEDARRAY, + TclObjVarErrMsg(interp, arrayNamePtr, elNamePtr, msg, needArray, index); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "VARNAME", - arrayNamePtr?TclGetString(arrayNamePtr):NULL, (void *)NULL); } return NULL; } @@ -1131,9 +1208,9 @@ TclLookupArrayElement( if (varPtr == NULL) { if (flags & TCL_LEAVE_ERR_MSG) { TclObjVarErrMsg(interp, arrayNamePtr, elNamePtr, msg, - NOSUCHELEMENT, index); + noSuchElement, index); Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "ELEMENT", - TclGetString(elNamePtr), (void *)NULL); + TclGetString(elNamePtr), NULL); } } } @@ -1162,7 +1239,6 @@ TclLookupArrayElement( *---------------------------------------------------------------------- */ -#ifndef TCL_NO_DEPRECATED #undef Tcl_GetVar const char * Tcl_GetVar( @@ -1173,17 +1249,8 @@ 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); } -#endif /* TCL_NO_DEPRECATED */ /* *---------------------------------------------------------------------- @@ -1220,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); } /* @@ -1320,10 +1376,10 @@ Tcl_Obj * Tcl_ObjGetVar2( Tcl_Interp *interp, /* Command interpreter in which variable is to * be looked up. */ - Tcl_Obj *part1Ptr, /* Points to an object holding the name of an + register Tcl_Obj *part1Ptr, /* Points to an object holding the name of an * array (if part2 is non-NULL) or the name of * a variable. */ - Tcl_Obj *part2Ptr, /* If non-null, points to an object holding + register Tcl_Obj *part2Ptr, /* If non-null, points to an object holding * the name of an element in the array * part1Ptr. */ int flags) /* OR-ed combination of TCL_GLOBAL_ONLY and @@ -1342,7 +1398,7 @@ Tcl_ObjGetVar2( return NULL; } - return TclPtrGetVarIdx(interp, varPtr, arrayPtr, part1Ptr, part2Ptr, + return TclPtrGetVar(interp, varPtr, arrayPtr, part1Ptr, part2Ptr, flags, -1); } @@ -1372,60 +1428,14 @@ Tcl_Obj * TclPtrGetVar( Tcl_Interp *interp, /* Command interpreter in which variable is to * be looked up. */ - Tcl_Var varPtr, /* The variable to be read.*/ - Tcl_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. */ - int flags) /* OR-ed combination of TCL_GLOBAL_ONLY, and - * TCL_LEAVE_ERR_MSG bits. */ -{ - if (varPtr == NULL) { - Tcl_Panic("varPtr must not be NULL"); - } - if (part1Ptr == NULL) { - Tcl_Panic("part1Ptr must not be NULL"); - } - return TclPtrGetVarIdx(interp, (Var *) varPtr, (Var *) arrayPtr, - part1Ptr, part2Ptr, flags, -1); -} - -/* - *---------------------------------------------------------------------- - * - * TclPtrGetVarIdx -- - * - * Return the value of a Tcl variable as a Tcl object, given the pointers - * to the variable's (and possibly containing array's) VAR structure. - * - * Results: - * The return value points to the current object value of the variable - * given by varPtr. If the specified variable doesn't exist, or if there - * is a clash in array usage, then NULL is returned and a message will be - * left in the interpreter's result if the TCL_LEAVE_ERR_MSG flag is set. - * - * Side effects: - * The ref count for the returned object is _not_ incremented to reflect - * the returned reference; if you want to keep a reference to the object - * you must increment its ref count yourself. - * - *---------------------------------------------------------------------- - */ - -Tcl_Obj * -TclPtrGetVarIdx( - Tcl_Interp *interp, /* Command interpreter in which variable is to - * be looked up. */ - Var *varPtr, /* The variable to be read.*/ + register Var *varPtr, /* The variable to be read.*/ 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. */ - int flags, /* OR-ed combination of TCL_GLOBAL_ONLY, and + const int flags, /* OR-ed combination of TCL_GLOBAL_ONLY, and * TCL_LEAVE_ERR_MSG bits. */ int index) /* Index into the local variable table of the * variable, or -1. Only used when part1Ptr is @@ -1456,36 +1466,14 @@ TclPtrGetVarIdx( return varPtr->value.objPtr; } - /* - * Return the array default value if any. - */ - - if (arrayPtr && TclIsVarArray(arrayPtr) && TclGetArrayDefault(arrayPtr)) { - return TclGetArrayDefault(arrayPtr); - } - if (TclIsVarArrayElement(varPtr) && !arrayPtr) { - /* - * UGLY! Peek inside the implementation of things. This lets us get - * the default of an array even when we've been [upvar]ed to just an - * element of the array. - */ - - ArrayVarHashTable *avhtPtr = (ArrayVarHashTable *) - ((VarInHash *) varPtr)->entry.tablePtr; - - if (avhtPtr->defaultObj) { - return avhtPtr->defaultObj; - } - } - if (flags & TCL_LEAVE_ERR_MSG) { if (TclIsVarUndefined(varPtr) && arrayPtr && !TclIsVarUndefined(arrayPtr)) { - msg = NOSUCHELEMENT; + msg = noSuchElement; } else if (TclIsVarArray(varPtr)) { - msg = ISARRAY; + msg = isArray; } else { - msg = NOSUCHVAR; + msg = noSuchVar; } TclObjVarErrMsg(interp, part1Ptr, part2Ptr, "read", msg, index); } @@ -1496,7 +1484,6 @@ TclPtrGetVarIdx( */ errorReturn: - Tcl_SetErrorCode(interp, "TCL", "READ", "VARNAME", (void *)NULL); if (TclIsVarUndefined(varPtr)) { TclCleanupVar(varPtr, arrayPtr); } @@ -1520,10 +1507,11 @@ TclPtrGetVarIdx( *---------------------------------------------------------------------- */ + /* ARGSUSED */ int Tcl_SetObjCmd( - TCL_UNUSED(void *), - Tcl_Interp *interp,/* Current interpreter. */ + ClientData dummy, /* Not used. */ + register Tcl_Interp *interp,/* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { @@ -1574,7 +1562,6 @@ Tcl_SetObjCmd( *---------------------------------------------------------------------- */ -#ifndef TCL_NO_DEPRECATED #undef Tcl_SetVar const char * Tcl_SetVar( @@ -1587,15 +1574,8 @@ Tcl_SetVar( * TCL_APPEND_VALUE, TCL_LIST_ELEMENT, * TCL_LEAVE_ERR_MSG. */ { - Tcl_Obj *varValuePtr = Tcl_SetVar2Ex(interp, varName, NULL, - Tcl_NewStringObj(newValue, -1), flags); - - if (varValuePtr == NULL) { - return NULL; - } - return TclGetString(varValuePtr); + return Tcl_SetVar2(interp, varName, NULL, newValue, flags); } -#endif /* TCL_NO_DEPRECATED */ /* *---------------------------------------------------------------------- @@ -1747,10 +1727,10 @@ Tcl_Obj * Tcl_ObjSetVar2( Tcl_Interp *interp, /* Command interpreter in which variable is to * be found. */ - Tcl_Obj *part1Ptr, /* Points to an object holding the name of an + register Tcl_Obj *part1Ptr, /* Points to an object holding the name of an * array (if part2 is non-NULL) or the name of * a variable. */ - Tcl_Obj *part2Ptr, /* If non-NULL, points to an object holding + register Tcl_Obj *part2Ptr, /* If non-NULL, points to an object holding * the name of an element in the array * part1Ptr. */ Tcl_Obj *newValuePtr, /* New value for variable. */ @@ -1776,7 +1756,7 @@ Tcl_ObjSetVar2( return NULL; } - return TclPtrSetVarIdx(interp, varPtr, arrayPtr, part1Ptr, part2Ptr, + return TclPtrSetVar(interp, varPtr, arrayPtr, part1Ptr, part2Ptr, newValuePtr, flags, -1); } @@ -1809,185 +1789,7 @@ Tcl_Obj * TclPtrSetVar( Tcl_Interp *interp, /* Command interpreter in which variable is to * be looked up. */ - Tcl_Var varPtr, /* Reference to the variable to set. */ - Tcl_Var arrayPtr, /* Reference to the array containing the - * variable, or NULL if the variable is a - * scalar. */ - Tcl_Obj *part1Ptr, /* Name of an array (if part2 is non-NULL) or - * the name of a variable. */ - Tcl_Obj *part2Ptr, /* If non-NULL, gives the name of an element - * in the array part1. */ - Tcl_Obj *newValuePtr, /* New value for variable. */ - int flags) /* OR-ed combination of TCL_GLOBAL_ONLY, and - * TCL_LEAVE_ERR_MSG bits. */ -{ - if (varPtr == NULL) { - Tcl_Panic("varPtr must not be NULL"); - } - if (part1Ptr == NULL) { - Tcl_Panic("part1Ptr must not be NULL"); - } - if (newValuePtr == NULL) { - Tcl_Panic("newValuePtr must not be NULL"); - } - return TclPtrSetVarIdx(interp, (Var *) varPtr, (Var *) arrayPtr, - part1Ptr, part2Ptr, newValuePtr, flags, -1); -} - -/* - *---------------------------------------------------------------------- - * - * ListAppendInVar, StringAppendInVar -- - * - * Support functions for TclPtrSetVarIdx that implement various types of - * appending operations. - * - * Results: - * ListAppendInVar returns a Tcl result code (from the core list append - * operation). StringAppendInVar has no return value. - * - * Side effects: - * The variable or element of the array is updated. This may make the - * variable/element exist. Reference counts of values may be updated. - * - *---------------------------------------------------------------------- - */ - -static inline int -ListAppendInVar( - Tcl_Interp *interp, - Var *varPtr, - Var *arrayPtr, - Tcl_Obj *oldValuePtr, - Tcl_Obj *newValuePtr) -{ - if (oldValuePtr == NULL) { - /* - * No previous value. Check for defaults if there's an array we can - * ask this of. - */ - - if (arrayPtr) { - Tcl_Obj *defValuePtr = TclGetArrayDefault(arrayPtr); - - if (defValuePtr) { - oldValuePtr = Tcl_DuplicateObj(defValuePtr); - } - } - - if (oldValuePtr == NULL) { - /* - * No default. [lappend] semantics say this is like being an empty - * string. - */ - - TclNewObj(oldValuePtr); - } - varPtr->value.objPtr = oldValuePtr; - Tcl_IncrRefCount(oldValuePtr); /* Since var is referenced. */ - } else if (Tcl_IsShared(oldValuePtr)) { - varPtr->value.objPtr = Tcl_DuplicateObj(oldValuePtr); - TclDecrRefCount(oldValuePtr); - oldValuePtr = varPtr->value.objPtr; - Tcl_IncrRefCount(oldValuePtr); /* Since var is referenced. */ - } - - return Tcl_ListObjAppendElement(interp, oldValuePtr, newValuePtr); -} - -static inline void -StringAppendInVar( - Var *varPtr, - Var *arrayPtr, - Tcl_Obj *oldValuePtr, - Tcl_Obj *newValuePtr) -{ - /* - * If there was no previous value, either we use the array's default (if - * this is an array with a default at all) or we treat this as a simple - * set. - */ - - if (oldValuePtr == NULL) { - if (arrayPtr) { - Tcl_Obj *defValuePtr = TclGetArrayDefault(arrayPtr); - - if (defValuePtr) { - /* - * This is *almost* the same as the shared path below, except - * that the original value reference in defValuePtr is not - * decremented. - */ - - Tcl_Obj *valuePtr = Tcl_DuplicateObj(defValuePtr); - - varPtr->value.objPtr = valuePtr; - TclContinuationsCopy(valuePtr, defValuePtr); - Tcl_IncrRefCount(valuePtr); - Tcl_AppendObjToObj(valuePtr, newValuePtr); - if (newValuePtr->refCount == 0) { - Tcl_DecrRefCount(newValuePtr); - } - return; - } - } - varPtr->value.objPtr = newValuePtr; - Tcl_IncrRefCount(newValuePtr); - return; - } - - /* - * We append newValuePtr's bytes but don't change its ref count. Unless - * the reference is shared, when we have to duplicate in order to be safe - * to modify at all. - */ - - if (Tcl_IsShared(oldValuePtr)) { /* Append to copy. */ - varPtr->value.objPtr = Tcl_DuplicateObj(oldValuePtr); - - TclContinuationsCopy(varPtr->value.objPtr, oldValuePtr); - - TclDecrRefCount(oldValuePtr); - oldValuePtr = varPtr->value.objPtr; - Tcl_IncrRefCount(oldValuePtr); /* Since var is ref */ - } - - Tcl_AppendObjToObj(oldValuePtr, newValuePtr); - if (newValuePtr->refCount == 0) { - Tcl_DecrRefCount(newValuePtr); - } -} - -/* - *---------------------------------------------------------------------- - * - * TclPtrSetVarIdx -- - * - * This function is the same as Tcl_SetVar2Ex above, except that it - * requires pointers to the variable's Var structs in addition to the - * variable names. - * - * Results: - * Returns a pointer to the Tcl_Obj holding the new value of the - * variable. If the write operation was disallowed because an array was - * expected but not found (or vice versa), then NULL is returned; if the - * TCL_LEAVE_ERR_MSG flag is set, then an explanatory message will be - * left in the interpreter's result. Note that the returned object may - * not be the same one referenced by newValuePtr; this is because - * variable traces may modify the variable's value. - * - * Side effects: - * The value of the given variable is set. If either the array or the - * entry didn't exist then a new variable is created. - * - *---------------------------------------------------------------------- - */ - -Tcl_Obj * -TclPtrSetVarIdx( - Tcl_Interp *interp, /* Command interpreter in which variable is to - * be looked up. */ - Var *varPtr, /* Reference to the variable to set. */ + register Var *varPtr, /* Reference to the variable to set. */ Var *arrayPtr, /* Reference to the array containing the * variable, or NULL if the variable is a * scalar. */ @@ -1997,7 +1799,7 @@ TclPtrSetVarIdx( Tcl_Obj *part2Ptr, /* If non-NULL, gives the name of an element * in the array part1. */ Tcl_Obj *newValuePtr, /* New value for variable. */ - int flags, /* OR-ed combination of TCL_GLOBAL_ONLY, and + const int flags, /* OR-ed combination of TCL_GLOBAL_ONLY, and * TCL_LEAVE_ERR_MSG bits. */ int index) /* Index of local var where part1 is to be * found. */ @@ -2006,7 +1808,6 @@ TclPtrSetVarIdx( 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 @@ -2020,12 +1821,10 @@ TclPtrSetVarIdx( if (flags & TCL_LEAVE_ERR_MSG) { if (TclIsVarArrayElement(varPtr)) { TclObjVarErrMsg(interp, part1Ptr, part2Ptr, "set", - DANGLINGELEMENT, index); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "ELEMENT", (void *)NULL); + danglingElement, index); } else { TclObjVarErrMsg(interp, part1Ptr, part2Ptr, "set", - DANGLINGVAR, index); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "VARNAME", (void *)NULL); + danglingVar, index); } } goto earlyError; @@ -2037,8 +1836,7 @@ TclPtrSetVarIdx( if (TclIsVarArray(varPtr)) { if (flags & TCL_LEAVE_ERR_MSG) { - TclObjVarErrMsg(interp, part1Ptr, part2Ptr, "set", ISARRAY,index); - Tcl_SetErrorCode(interp, "TCL", "WRITE", "ARRAY", (void *)NULL); + TclObjVarErrMsg(interp, part1Ptr, part2Ptr, "set", isArray,index); } goto earlyError; } @@ -2048,7 +1846,7 @@ TclPtrSetVarIdx( * 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) @@ -2072,14 +1870,61 @@ TclPtrSetVarIdx( 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. */ - result = ListAppendInVar(interp, varPtr, arrayPtr, oldValuePtr, + if (oldValuePtr == NULL) { + TclNewObj(oldValuePtr); + varPtr->value.objPtr = oldValuePtr; + Tcl_IncrRefCount(oldValuePtr); /* Since var is referenced. */ + } else if (Tcl_IsShared(oldValuePtr)) { + varPtr->value.objPtr = Tcl_DuplicateObj(oldValuePtr); + TclDecrRefCount(oldValuePtr); + oldValuePtr = varPtr->value.objPtr; + Tcl_IncrRefCount(oldValuePtr); /* Since var is referenced. */ + } + result = Tcl_ListObjAppendElement(interp, oldValuePtr, newValuePtr); if (result != TCL_OK) { goto earlyError; } } else { /* Append string. */ - StringAppendInVar(varPtr, arrayPtr, oldValuePtr, newValuePtr); + /* + * We append newValuePtr's bytes but don't change its ref count. + */ + + if (oldValuePtr == NULL) { + varPtr->value.objPtr = newValuePtr; + Tcl_IncrRefCount(newValuePtr); + } else { + if (Tcl_IsShared(oldValuePtr)) { /* Append to copy. */ + varPtr->value.objPtr = Tcl_DuplicateObj(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; + Tcl_IncrRefCount(oldValuePtr); /* Since var is ref */ + } + Tcl_AppendObjToObj(oldValuePtr, newValuePtr); + if (newValuePtr->refCount == 0) { + Tcl_DecrRefCount(newValuePtr); + } + } } } else if (newValuePtr != oldValuePtr) { /* @@ -2130,16 +1975,13 @@ TclPtrSetVarIdx( */ cleanup: - if (resultPtr == NULL) { - Tcl_SetErrorCode(interp, "TCL", "WRITE", "VARNAME", (void *)NULL); - } if (TclIsVarUndefined(varPtr)) { TclCleanupVar(varPtr, arrayPtr); } return resultPtr; earlyError: - if (cleanupOnEarlyError) { + if (newValuePtr->refCount == 0) { Tcl_DecrRefCount(newValuePtr); } goto cleanup; @@ -2194,11 +2036,11 @@ 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 TclPtrIncrObjVarIdx(interp, varPtr, arrayPtr, part1Ptr, part2Ptr, + return TclPtrIncrObjVar(interp, varPtr, arrayPtr, part1Ptr, part2Ptr, incrPtr, flags, -1); } @@ -2231,62 +2073,6 @@ Tcl_Obj * TclPtrIncrObjVar( Tcl_Interp *interp, /* Command interpreter in which variable is to * be found. */ - Tcl_Var varPtr, /* Reference to the variable to set. */ - Tcl_Var arrayPtr, /* Reference to the array containing the - * variable, or NULL if the variable is a - * scalar. */ - Tcl_Obj *part1Ptr, /* Points to an object holding the name of an - * array (if part2 is non-NULL) or the name of - * a variable. */ - Tcl_Obj *part2Ptr, /* If non-null, points to an object holding - * the name of an element in the array - * part1Ptr. */ - Tcl_Obj *incrPtr, /* Increment value. */ -/* TODO: Which of these flag values really make sense? */ - int flags) /* Various flags that tell how to incr value: - * any of TCL_GLOBAL_ONLY, TCL_NAMESPACE_ONLY, - * TCL_APPEND_VALUE, TCL_LIST_ELEMENT, - * TCL_LEAVE_ERR_MSG. */ -{ - if (varPtr == NULL) { - Tcl_Panic("varPtr must not be NULL"); - } - if (part1Ptr == NULL) { - Tcl_Panic("part1Ptr must not be NULL"); - } - return TclPtrIncrObjVarIdx(interp, (Var *) varPtr, (Var *) arrayPtr, - part1Ptr, part2Ptr, incrPtr, flags, -1); -} - -/* - *---------------------------------------------------------------------- - * - * TclPtrIncrObjVarIdx -- - * - * Given the pointers to a variable and possible containing array, - * increment the Tcl object value of the variable by a Tcl_Obj increment. - * - * Results: - * Returns a pointer to the Tcl_Obj holding the new value of the - * variable. If the specified variable doesn't exist, or there is a clash - * in array usage, or an error occurs while executing variable traces, - * then NULL is returned and a message will be left in the interpreter's - * result. - * - * Side effects: - * The value of the given variable is incremented by the specified - * amount. If either the array or the entry didn't exist then a new - * variable is created. The ref count for the returned object is _not_ - * incremented to reflect the returned reference; if you want to keep a - * reference to the object you must increment its ref count yourself. - * - *---------------------------------------------------------------------- - */ - -Tcl_Obj * -TclPtrIncrObjVarIdx( - Tcl_Interp *interp, /* Command interpreter in which variable is to - * be found. */ Var *varPtr, /* Reference to the variable to set. */ Var *arrayPtr, /* Reference to the array containing the * variable, or NULL if the variable is a @@ -2299,7 +2085,7 @@ TclPtrIncrObjVarIdx( * part1Ptr. */ Tcl_Obj *incrPtr, /* Increment value. */ /* TODO: Which of these flag values really make sense? */ - int flags, /* Various flags that tell how to incr value: + const int flags, /* Various flags that tell how to incr value: * any of TCL_GLOBAL_ONLY, TCL_NAMESPACE_ONLY, * TCL_APPEND_VALUE, TCL_LIST_ELEMENT, * TCL_LEAVE_ERR_MSG. */ @@ -2307,26 +2093,26 @@ TclPtrIncrObjVarIdx( * variable, or -1. Only used when part1Ptr is * NULL. */ { - Tcl_Obj *varValuePtr; + register Tcl_Obj *varValuePtr; if (TclIsVarInHash(varPtr)) { VarHashRefCount(varPtr)++; } - varValuePtr = TclPtrGetVarIdx(interp, varPtr, arrayPtr, part1Ptr, - part2Ptr, flags, index); + varValuePtr = TclPtrGetVar(interp, varPtr, arrayPtr, part1Ptr, part2Ptr, + flags, index); if (TclIsVarInHash(varPtr)) { VarHashRefCount(varPtr)--; } if (varValuePtr == NULL) { - TclNewIntObj(varValuePtr, 0); + varValuePtr = Tcl_NewIntObj(0); } if (Tcl_IsShared(varValuePtr)) { /* Copy on write */ varValuePtr = Tcl_DuplicateObj(varValuePtr); if (TCL_OK == TclIncrObj(interp, varValuePtr, incrPtr)) { - return TclPtrSetVarIdx(interp, varPtr, arrayPtr, part1Ptr, - part2Ptr, varValuePtr, flags, index); + return TclPtrSetVar(interp, varPtr, arrayPtr, part1Ptr, part2Ptr, + varValuePtr, flags, index); } else { Tcl_DecrRefCount(varValuePtr); return NULL; @@ -2334,6 +2120,7 @@ TclPtrIncrObjVarIdx( } else { /* Unshared - can Incr in place */ if (TCL_OK == TclIncrObj(interp, varValuePtr, incrPtr)) { + /* * This seems dumb to write the incremeted value into the var * after we just adjusted the value in place, but the spec for @@ -2341,8 +2128,8 @@ TclPtrIncrObjVarIdx( * is the way to make that happen. */ - return TclPtrSetVarIdx(interp, varPtr, arrayPtr, part1Ptr, - part2Ptr, varValuePtr, flags, index); + return TclPtrSetVar(interp, varPtr, arrayPtr, part1Ptr, part2Ptr, + varValuePtr, flags, index); } else { return NULL; } @@ -2369,7 +2156,6 @@ TclPtrIncrObjVarIdx( *---------------------------------------------------------------------- */ -#ifndef TCL_NO_DEPRECATED #undef Tcl_UnsetVar int Tcl_UnsetVar( @@ -2382,23 +2168,8 @@ 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); } -#endif /* TCL_NO_DEPRECATED */ /* *---------------------------------------------------------------------- @@ -2483,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); @@ -2491,99 +2265,7 @@ TclObjUnsetVar2( return TCL_ERROR; } - return TclPtrUnsetVarIdx(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. */ - Tcl_Var varPtr, /* The variable to be unset. */ - Tcl_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. */ - int flags) /* OR-ed combination of any of - * TCL_GLOBAL_ONLY, TCL_NAMESPACE_ONLY, - * TCL_LEAVE_ERR_MSG. */ -{ - if (varPtr == NULL) { - Tcl_Panic("varPtr must not be NULL"); - } - if (part1Ptr == NULL) { - Tcl_Panic("part1Ptr must not be NULL"); - } - return TclPtrUnsetVarIdx(interp, (Var *) varPtr, (Var *) arrayPtr, - part1Ptr, part2Ptr, flags, -1); -} - -/* - *---------------------------------------------------------------------- - * - * TclPtrUnsetVarIdx -- - * - * 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 -TclPtrUnsetVarIdx( - Tcl_Interp *interp, /* Command interpreter in which varName is to - * be looked up. */ - 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. */ - 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 @@ -2596,7 +2278,7 @@ TclPtrUnsetVarIdx( 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. @@ -2605,11 +2287,23 @@ TclPtrUnsetVarIdx( 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", (void *)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 @@ -2649,8 +2343,7 @@ UnsetVarStruct( Interp *iPtr, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, - int flags, - int index) + int flags) { Var dummyVar; int traced = TclIsVarTraced(varPtr) @@ -2690,7 +2383,6 @@ UnsetVarStruct( if (traced) { VarTrace *tracePtr = NULL; - Tcl_HashEntry *tPtr; if (TclIsVarTraced(&dummyVar)) { /* @@ -2699,38 +2391,44 @@ UnsetVarStruct( */ int isNew; + Tcl_HashEntry *tPtr = + Tcl_FindHashEntry(&iPtr->varTraces, (char *) varPtr); - tPtr = Tcl_FindHashEntry(&iPtr->varTraces, varPtr); - tracePtr = (VarTrace *)Tcl_GetHashValue(tPtr); + 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 = (VarTrace *)Tcl_GetHashValue(tPtr); - Tcl_DeleteHashEntry(tPtr); - } + tPtr = Tcl_FindHashEntry(&iPtr->varTraces, (char *) &dummyVar); + tracePtr = Tcl_GetHashValue(tPtr); + } + + if (tPtr) { + Tcl_DeleteHashEntry(tPtr); } } @@ -2742,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) { @@ -2772,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 @@ -2814,15 +2511,16 @@ UnsetVarStruct( *---------------------------------------------------------------------- */ + /* ARGSUSED */ int Tcl_UnsetObjCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { - int i, flags = TCL_LEAVE_ERR_MSG; - const char *name; + register int i, flags = TCL_LEAVE_ERR_MSG; + register char *name; if (objc == 1) { /* @@ -2881,20 +2579,19 @@ Tcl_UnsetObjCmd( *---------------------------------------------------------------------- */ + /* ARGSUSED */ int Tcl_AppendObjCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { - Var *varPtr, *arrayPtr; - Tcl_Obj *varValuePtr = NULL; + 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; } @@ -2904,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) { @@ -2913,11 +2613,11 @@ Tcl_AppendObjCmd( /* * Note that we do not need to increase the refCount of the Var * pointers: should a trace delete the variable, the return value - * of TclPtrSetVarIdx will be NULL or emptyObjPtr, and we will not + * of TclPtrSetVar will be NULL or emptyObjPtr, and we will not * access the variable again. */ - varValuePtr = TclPtrSetVarIdx(interp, varPtr, arrayPtr, objv[1], + varValuePtr = TclPtrSetVar(interp, varPtr, arrayPtr, objv[1], NULL, objv[i], TCL_APPEND_VALUE|TCL_LEAVE_ERR_MSG, -1); if ((varValuePtr == NULL) || (varValuePtr == ((Interp *) interp)->emptyObjPtr)) { @@ -2946,20 +2646,20 @@ Tcl_AppendObjCmd( *---------------------------------------------------------------------- */ + /* ARGSUSED */ int Tcl_LappendObjCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { Tcl_Obj *varValuePtr, *newValuePtr; - Tcl_Size numElems; - Var *varPtr, *arrayPtr; - int result, createdNewObj; + 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) { @@ -2983,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 @@ -2993,10 +2696,8 @@ Tcl_LappendObjCmd( * copy to modify: this is "copy on write". */ - createdNewObj = 0; - /* - * Protect the variable pointers around the TclPtrGetVarIdx call + * Protect the variable pointers around the TclPtrGetVar call * to insure that they remain valid even if the variable was undefined * and unused. */ @@ -3012,7 +2713,7 @@ Tcl_LappendObjCmd( if (arrayPtr && TclIsVarInHash(arrayPtr)) { VarHashRefCount(arrayPtr)++; } - varValuePtr = TclPtrGetVarIdx(interp, varPtr, arrayPtr, objv[1], NULL, + varValuePtr = TclPtrGetVar(interp, varPtr, arrayPtr, objv[1], NULL, TCL_LEAVE_ERR_MSG, -1); if (TclIsVarInHash(varPtr)) { VarHashRefCount(varPtr)--; @@ -3053,7 +2754,7 @@ Tcl_LappendObjCmd( * and we didn't create the variable. */ - newValuePtr = TclPtrSetVarIdx(interp, varPtr, arrayPtr, objv[1], NULL, + newValuePtr = TclPtrSetVar(interp, varPtr, arrayPtr, objv[1], NULL, varValuePtr, TCL_LEAVE_ERR_MSG, -1); if (newValuePtr == NULL) { return TCL_ERROR; @@ -3072,1036 +2773,624 @@ Tcl_LappendObjCmd( /* *---------------------------------------------------------------------- * - * ArrayForObjCmd, ArrayForNRCmd, ArrayForLoopCallback, ArrayObjNext -- - * - * These functions implement the "array for" Tcl command. - * array for {k v} a {} - * The array for command iterates over the array, setting the the - * specified loop variables, and executing the body each iteration. - * - * ArrayForObjCmd() is the standard wrapper around ArrayForNRCmd(). + * Tcl_ArrayObjCmd -- * - * ArrayForNRCmd() sets up the ArraySearch structure, sets arrayNamePtr - * inside the structure and calls VarHashFirstEntry to start the hash - * iteration. + * This object-based function is invoked to process the "array" Tcl + * command. See the user documentation for details on what it does. * - * ArrayForNRCmd() does not execute the body or set the loop variables, - * it only initializes the iterator. + * Results: + * A standard Tcl result object. * - * ArrayForLoopCallback() iterates over the entire array, executing the - * body each time. + * Side effects: + * See the user documentation. * *---------------------------------------------------------------------- */ -static int -ArrayObjNext( - Tcl_Interp *interp, - Tcl_Obj *arrayNameObj, /* array */ - Var *varPtr, /* array */ - ArraySearch *searchPtr, - Tcl_Obj **keyPtrPtr, /* Pointer to a variable to have the key - * written into, or NULL. */ - Tcl_Obj **valuePtrPtr) /* Pointer to a variable to have the - * value written into, or NULL.*/ -{ - Tcl_Obj *keyObj; - Tcl_Obj *valueObj = NULL; - int gotValue; - int donerc; - - donerc = TCL_BREAK; - - if ((varPtr->flags & VAR_SEARCH_ACTIVE) != VAR_SEARCH_ACTIVE) { - donerc = TCL_ERROR; - return donerc; - } - - gotValue = 0; - while (1) { - Tcl_HashEntry *hPtr = searchPtr->nextEntry; - - if (hPtr != NULL) { - searchPtr->nextEntry = NULL; - } else { - hPtr = Tcl_NextHashEntry(&searchPtr->search); - if (hPtr == NULL) { - gotValue = 0; - break; - } - } - varPtr = VarHashGetValue(hPtr); - if (!TclIsVarUndefined(varPtr)) { - gotValue = 1; - break; - } - } - - if (!gotValue) { - return donerc; - } - - donerc = TCL_CONTINUE; - - keyObj = VarHashGetKey(varPtr); - *keyPtrPtr = keyObj; - valueObj = Tcl_ObjGetVar2(interp, arrayNameObj, keyObj, - TCL_LEAVE_ERR_MSG); - *valuePtrPtr = valueObj; - - return donerc; -} - -static int -ArrayForObjCmd( - void *clientData, + /* ARGSUSED */ +int +Tcl_ArrayObjCmd( + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { - return Tcl_NRCallObjProc(interp, ArrayForNRCmd, clientData, objc, objv); -} - -static int -ArrayForNRCmd( - TCL_UNUSED(void *), - Tcl_Interp *interp, - int objc, - Tcl_Obj *const *objv) -{ - Tcl_Obj *varListObj, *arrayNameObj, *scriptObj; - ArraySearch *searchPtr = NULL; - Var *varPtr; - int isArray; - Tcl_Size numVars; - /* - * array for {k v} a body + * The list of constants below should match the arrayOptions string array + * below. */ - if (objc != 4) { - Tcl_WrongNumArgs(interp, 1, objv, "{key value} arrayName script"); - return TCL_ERROR; - } - - /* - * Parse arguments. - */ + 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 + }; - if (TclListObjLength(interp, objv[1], &numVars) != TCL_OK) { - return TCL_ERROR; - } + Interp *iPtr = (Interp *) interp; + Var *varPtr, *arrayPtr; + Tcl_HashEntry *hPtr; + Tcl_Obj *varNamePtr; + int notArray; + int index, result; - if (numVars != 2) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "must have two variable names", -1)); - Tcl_SetErrorCode(interp, "TCL", "SYNTAX", "array", "for", (void *)NULL); + if (objc < 3) { + Tcl_WrongNumArgs(interp, 1, objv, "option arrayName ?arg ...?"); return TCL_ERROR; } - arrayNameObj = objv[2]; - - if (TCL_ERROR == LocateArray(interp, arrayNameObj, &varPtr, &isArray)) { + if (Tcl_GetIndexFromObj(interp, objv[1], arrayOptions, "option", + 0, &index) != TCL_OK) { return TCL_ERROR; } - if (!isArray) { - return NotArrayError(interp, arrayNameObj); - } - /* - * Make a new array search, put it on the stack. + * Locate the array variable */ - searchPtr = (ArraySearch *)ckalloc(sizeof(ArraySearch)); - ArrayPopulateSearch(interp, arrayNameObj, varPtr, searchPtr); - - /* - * Make sure that these objects (which we need throughout the body of the - * loop) don't vanish. - */ - - /* Do not use TclListObjCopy here - shimmers arithseries to list */ - varListObj = Tcl_DuplicateObj(objv[1]); - if (!varListObj) { - return TCL_ERROR; - } - scriptObj = objv[3]; - Tcl_IncrRefCount(scriptObj); - - /* - * Run the script. - */ - - TclNRAddCallback(interp, ArrayForLoopCallback, searchPtr, varListObj, - arrayNameObj, scriptObj); - return TCL_OK; -} - -static int -ArrayForLoopCallback( - void *data[], - Tcl_Interp *interp, - int result) -{ - Interp *iPtr = (Interp *) interp; - ArraySearch *searchPtr = (ArraySearch *)data[0]; - Tcl_Obj *varListObj = (Tcl_Obj *)data[1]; - Tcl_Obj *arrayNameObj = (Tcl_Obj *)data[2]; - Tcl_Obj *scriptObj = (Tcl_Obj *)data[3]; - Tcl_Obj **varv; - Tcl_Obj *keyObj, *valueObj; - Var *varPtr; - Var *arrayPtr; - int done; - Tcl_Size varc; + varNamePtr = objv[2]; + varPtr = TclObjLookupVarEx(interp, varNamePtr, NULL, /*flags*/ 0, + /*msg*/ 0, /*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr); /* - * Process the result from the previous execution of the script body. + * Special array trace used to keep the env array in sync for array names, + * array get, etc. */ - done = TCL_ERROR; - - if (result == TCL_CONTINUE) { - result = TCL_OK; - } else if (result != TCL_OK) { - if (result == TCL_BREAK) { - Tcl_ResetResult(interp); - result = TCL_OK; - } else if (result == TCL_ERROR) { - Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( - "\n (\"array for\" body line %d)", - Tcl_GetErrorLine(interp))); + if (varPtr && (varPtr->flags & VAR_TRACED_ARRAY) + && (TclIsVarArray(varPtr) || TclIsVarUndefined(varPtr))) { + 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 TCL_ERROR; } - goto arrayfordone; } /* - * Get the next mapping from the array. + * 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. */ - keyObj = NULL; - valueObj = NULL; - varPtr = TclObjLookupVarEx(interp, arrayNameObj, NULL, /*flags*/ 0, - /*msg*/ 0, /*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr); - if (varPtr == NULL) { - done = TCL_ERROR; - } else { - done = ArrayObjNext(interp, arrayNameObj, varPtr, searchPtr, &keyObj, - &valueObj); + notArray = 0; + if ((varPtr == NULL) || !TclIsVarArray(varPtr) + || TclIsVarUndefined(varPtr)) { + notArray = 1; } - result = TCL_OK; - if (done != TCL_CONTINUE) { - Tcl_ResetResult(interp); - if (done == TCL_ERROR) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "array changed during iteration", -1)); - Tcl_SetErrorCode(interp, "TCL", "READ", "array", "for", (void *)NULL); - varPtr->flags |= TCL_LEAVE_ERR_MSG; - result = done; - } - goto arrayfordone; - } + switch (index) { + case ARRAY_ANYMORE: { + ArraySearch *searchPtr; - result = TclListObjGetElements(NULL, varListObj, &varc, &varv); - if (result != TCL_OK) { - goto arrayfordone; - } - if (Tcl_ObjSetVar2(interp, varv[0], NULL, keyObj, - TCL_LEAVE_ERR_MSG) == NULL) { - result = TCL_ERROR; - goto arrayfordone; - } - if (valueObj != NULL) { - if (Tcl_ObjSetVar2(interp, varv[1], NULL, valueObj, - TCL_LEAVE_ERR_MSG) == NULL) { - result = TCL_ERROR; - goto arrayfordone; + if (objc != 4) { + Tcl_WrongNumArgs(interp, 2, objv, "arrayName searchId"); + return TCL_ERROR; } - } - - /* - * Run the script. - */ - - TclNRAddCallback(interp, ArrayForLoopCallback, searchPtr, varListObj, - arrayNameObj, scriptObj); - return TclNREvalObjEx(interp, scriptObj, 0, iPtr->cmdFramePtr, 3); - - /* - * For unwinding everything once the iterating is done. - */ - - arrayfordone: - if (done != TCL_ERROR) { - /* - * If the search was terminated by an array change, the - * VAR_SEARCH_ACTIVE flag will no longer be set. - */ - - ArrayDoneSearch(iPtr, varPtr, searchPtr); - Tcl_DecrRefCount(searchPtr->name); - ckfree(searchPtr); - } - - TclDecrRefCount(varListObj); - TclDecrRefCount(scriptObj); - return result; -} - -/* - * ArrayPopulateSearch - */ - -static void -ArrayPopulateSearch( - Tcl_Interp *interp, - Tcl_Obj *arrayNameObj, - Var *varPtr, - ArraySearch *searchPtr) -{ - Interp *iPtr = (Interp *) interp; - Tcl_HashEntry *hPtr; - int isNew; - - 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 = (ArraySearch *)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(arrayNameObj)); - Tcl_IncrRefCount(searchPtr->name); -} -/* - *---------------------------------------------------------------------- - * - * 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. - * - *---------------------------------------------------------------------- - */ - -static int -ArrayStartSearchCmd( - TCL_UNUSED(void *), - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - Var *varPtr; - int isArray; - ArraySearch *searchPtr; - - if (objc != 2) { - Tcl_WrongNumArgs(interp, 1, objv, "arrayName"); - return TCL_ERROR; - } - - if (TCL_ERROR == LocateArray(interp, objv[1], &varPtr, &isArray)) { - return TCL_ERROR; - } - - if (!isArray) { - return NotArrayError(interp, objv[1]); - } - - /* - * Make a new array search with a free name. - */ - - searchPtr = (ArraySearch *)ckalloc(sizeof(ArraySearch)); - ArrayPopulateSearch(interp, objv[1], varPtr, searchPtr); - Tcl_SetObjResult(interp, searchPtr->name); - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * ArrayDoneSearch -- - * - * Removes the search from the hash of active searches. - * - *---------------------------------------------------------------------- - */ -static void -ArrayDoneSearch( - Interp *iPtr, - Var *varPtr, - ArraySearch *searchPtr) -{ - Tcl_HashEntry *hPtr; - ArraySearch *prevPtr; - - /* - * Unhook the search from the list of searches associated with the - * variable. - */ - - hPtr = Tcl_FindHashEntry(&iPtr->varSearches, varPtr); - if (hPtr == NULL) { - return; - } - if (searchPtr == Tcl_GetHashValue(hPtr)) { - if (searchPtr->nextPtr) { - Tcl_SetHashValue(hPtr, searchPtr->nextPtr); - } else { - varPtr->flags &= ~VAR_SEARCH_ACTIVE; - Tcl_DeleteHashEntry(hPtr); + if (notArray) { + goto error; } - } else { - for (prevPtr = (ArraySearch *)Tcl_GetHashValue(hPtr); ; prevPtr=prevPtr->nextPtr) { - if (prevPtr->nextPtr == searchPtr) { - prevPtr->nextPtr = searchPtr->nextPtr; - break; + 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; } } + Tcl_SetObjResult(interp, iPtr->execEnvPtr->constants[1]); + break; } -} - -/* - *---------------------------------------------------------------------- - * - * 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. - * - *---------------------------------------------------------------------- - */ - -static int -ArrayAnyMoreCmd( - TCL_UNUSED(void *), - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - Interp *iPtr = (Interp *) interp; - Var *varPtr; - Tcl_Obj *varNameObj, *searchObj; - int gotValue, isArray; - ArraySearch *searchPtr; - - if (objc != 3) { - Tcl_WrongNumArgs(interp, 1, objv, "arrayName searchId"); - return TCL_ERROR; - } - varNameObj = objv[1]; - searchObj = objv[2]; - - if (TCL_ERROR == LocateArray(interp, varNameObj, &varPtr, &isArray)) { - return TCL_ERROR; - } - - if (!isArray) { - return NotArrayError(interp, varNameObj); - } - - /* - * 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; - /* - * Scan forward to find if there are any further elements in the array - * that are defined. - */ - - while (1) { - if (searchPtr->nextEntry != NULL) { - varPtr = VarHashGetValue(searchPtr->nextEntry); - if (!TclIsVarUndefined(varPtr)) { - gotValue = 1; + 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; + } + 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; } } - searchPtr->nextEntry = Tcl_NextHashEntry(&searchPtr->search); - if (searchPtr->nextEntry == NULL) { - gotValue = 0; - break; - } - } - 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. - * - *---------------------------------------------------------------------- - */ - -static int -ArrayNextElementCmd( - TCL_UNUSED(void *), - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - Var *varPtr; - Tcl_Obj *varNameObj, *searchObj; - ArraySearch *searchPtr; - int isArray; - - if (objc != 3) { - Tcl_WrongNumArgs(interp, 1, objv, "arrayName searchId"); - return TCL_ERROR; - } - varNameObj = objv[1]; - searchObj = objv[2]; - - if (TCL_ERROR == LocateArray(interp, varNameObj, &varPtr, &isArray)) { - return TCL_ERROR; - } - - if (!isArray) { - return NotArrayError(interp, varNameObj); + Tcl_SetObjResult(interp, VarHashGetKey(varPtr2)); + break; } + case ARRAY_STARTSEARCH: { + ArraySearch *searchPtr; + int isNew; + char *varName = TclGetString(varNamePtr); - /* - * Get the search. - */ + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "arrayName"); + return TCL_ERROR; + } + 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]; - searchPtr = ParseSearchId(interp, varPtr, varNameObj, searchObj); - if (searchPtr == NULL) { - return TCL_ERROR; + 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; } - /* - * 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) { - return TCL_OK; - } - } else { - searchPtr->nextEntry = NULL; + case ARRAY_EXISTS: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "arrayName"); + return TCL_ERROR; } - varPtr = VarHashGetValue(hPtr); - if (!TclIsVarUndefined(varPtr)) { - Tcl_SetObjResult(interp, VarHashGetKey(varPtr)); + 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 (notArray) { return TCL_OK; } - } -} - -/* - *---------------------------------------------------------------------- - * - * 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. - * - *---------------------------------------------------------------------- - */ - -static int -ArrayDoneSearchCmd( - TCL_UNUSED(void *), - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - Interp *iPtr = (Interp *) interp; - Var *varPtr; - Tcl_Obj *varNameObj, *searchObj; - ArraySearch *searchPtr; - int isArray; - - if (objc != 3) { - Tcl_WrongNumArgs(interp, 1, objv, "arrayName searchId"); - return TCL_ERROR; - } - varNameObj = objv[1]; - searchObj = objv[2]; + if (objc == 4) { + pattern = TclGetString(objv[3]); + } - if (TCL_ERROR == LocateArray(interp, varNameObj, &varPtr, &isArray)) { - return TCL_ERROR; - } + /* + * Store the array names in a new object. + */ - if (!isArray) { - return NotArrayError(interp, varNameObj); - } + 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. */ + } - /* - * Get the search. - */ + result = Tcl_ListObjAppendElement(interp, nameLstPtr, namePtr); + if (result != TCL_OK) { + TclDecrRefCount(nameLstPtr); + return result; + } + } - searchPtr = ParseSearchId(interp, varPtr, varNameObj, searchObj); - if (searchPtr == NULL) { - return TCL_ERROR; - } + searchDone: + /* + * Make sure the Var structure of the array is not removed by a trace + * while we're working. + */ - ArrayDoneSearch(iPtr, varPtr, searchPtr); - 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. - * - *---------------------------------------------------------------------- - */ + if (TclIsVarInHash(varPtr)) { + VarHashRefCount(varPtr)++; + } -static int -ArrayExistsCmd( - TCL_UNUSED(void *), - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - Interp *iPtr = (Interp *)interp; - int isArray; + /* + * Get the array values corresponding to each element name. + */ - if (objc != 2) { - Tcl_WrongNumArgs(interp, 1, objv, "arrayName"); - return TCL_ERROR; - } + TclNewObj(tmpResPtr); + result = Tcl_ListObjGetElements(interp, nameLstPtr, &count, + &namePtrPtr); + if (result != TCL_OK) { + goto errorInArrayGet; + } - if (TCL_ERROR == LocateArray(interp, objv[1], NULL, &isArray)) { - return TCL_ERROR; - } + for (i=0 ; i<count ; i++) { + namePtr = *namePtrPtr++; + valuePtr = Tcl_ObjGetVar2(interp, objv[2], namePtr, + TCL_LEAVE_ERR_MSG); + if (valuePtr == 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? + */ - Tcl_SetObjResult(interp, iPtr->execEnvPtr->constants[isArray]); - 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. - * - *---------------------------------------------------------------------- - */ + if (TclIsVarArray(varPtr)) { + /* + * The array itself looks OK, the variable was undefined: + * forget it. + */ -static int -ArrayGetCmd( - TCL_UNUSED(void *), - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - Var *varPtr, *varPtr2; - Tcl_Obj *varNameObj, *nameObj, *valueObj, *nameLstObj, *tmpResObj; - Tcl_Obj **nameObjPtr, *patternObj; - Tcl_HashSearch search; - const char *pattern; - Tcl_Size i, count; - int result, isArray; - - switch (objc) { - case 2: - varNameObj = objv[1]; - patternObj = NULL; - break; - case 3: - varNameObj = objv[1]; - patternObj = objv[2]; + continue; + } else { + result = TCL_ERROR; + goto errorInArrayGet; + } + } + result = Tcl_DictObjPut(interp, tmpResPtr, namePtr, valuePtr); + if (result != TCL_OK) { + goto errorInArrayGet; + } + } + if (TclIsVarInHash(varPtr)) { + VarHashRefCount(varPtr)--; + } + Tcl_SetObjResult(interp, tmpResPtr); + TclDecrRefCount(nameLstPtr); break; - default: - Tcl_WrongNumArgs(interp, 1, objv, "arrayName ?pattern?"); - return TCL_ERROR; - } - if (TCL_ERROR == LocateArray(interp, varNameObj, &varPtr, &isArray)) { - return TCL_ERROR; - } - - /* If not an array, it's an empty result. */ - if (!isArray) { - return TCL_OK; + errorInArrayGet: + if (TclIsVarInHash(varPtr)) { + VarHashRefCount(varPtr)--; + } + TclDecrRefCount(nameLstPtr); + TclDecrRefCount(tmpResPtr); /* Free unneeded temp result. */ + return result; } - - 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; + 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; } - 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) { + 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; + } + } + Tcl_SetObjResult(interp, resultPtr); + return TCL_OK; } - goto searchDone; - } + for (varPtr2=VarHashFirstVar(varPtr->value.tablePtr, &search); + varPtr2!=NULL ; varPtr2=VarHashNextVar(&search)) { + if (TclIsVarUndefined(varPtr2)) { + continue; + } + 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; + } + } - for (varPtr2 = VarHashFirstVar(varPtr->value.tablePtr, &search); - varPtr2; varPtr2 = VarHashNextVar(&search)) { - if (TclIsVarUndefined(varPtr2)) { - continue; + result = Tcl_ListObjAppendElement(interp, resultPtr, namePtr); + if (result != TCL_OK) { + TclDecrRefCount(namePtr); /* Free unneeded name obj. */ + return result; + } } - nameObj = VarHashGetKey(varPtr2); - if (patternObj && !Tcl_StringMatch(TclGetString(nameObj), pattern)) { - continue; /* Element name doesn't match pattern. */ + Tcl_SetObjResult(interp, resultPtr); + break; + } + case ARRAY_SET: + if (objc != 4) { + Tcl_WrongNumArgs(interp, 2, objv, "arrayName list"); + return TCL_ERROR; } - - result = Tcl_ListObjAppendElement(interp, nameLstObj, nameObj); - if (result != TCL_OK) { - TclDecrRefCount(nameLstObj); - return result; + 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; } - } - - /* - * Make sure the Var structure of the array is not removed by a trace - * while we're working. - */ + if (notArray) { + return TCL_OK; + } + if (objc == 3) { + /* + * When no pattern is given, just unset the whole array. + */ - searchDone: - if (TclIsVarInHash(varPtr)) { - VarHashRefCount(varPtr)++; - } + return TclObjUnsetVar2(interp, varNamePtr, NULL, 0); + } else { + Tcl_HashSearch search; + Var *varPtr2, *protectedVarPtr; + const char *pattern = TclGetString(objv[3]); - /* - * Get the array values corresponding to each element name. - */ + /* + * With a trivial pattern, we can just unset. + */ - TclNewObj(tmpResObj); - result = TclListObjGetElements(interp, nameLstObj, &count, &nameObjPtr); - if (result != TCL_OK) { - goto errorInArrayGet; - } + 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; + } - 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? + * 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] */ - if (TclIsVarArray(varPtr)) { + protectedVarPtr = NULL; + for (varPtr2=VarHashFirstVar(varPtr->value.tablePtr, &search); + varPtr2!=NULL ; varPtr2=VarHashNextVar(&search)) { /* - * The array itself looks OK, the variable was undefined: - * forget it. + * Drop the extra ref immediately. We don't need to free it at + * this point though; we'll be unsetting it if necessary soon. */ - continue; - } - result = TCL_ERROR; - goto errorInArrayGet; - } - result = Tcl_DictObjPut(interp, tmpResObj, nameObj, valueObj); - if (result != TCL_OK) { - goto errorInArrayGet; - } - } - 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. - * - *---------------------------------------------------------------------- - */ - -static int -ArrayNamesCmd( - TCL_UNUSED(void *), - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - static const char *const options[] = { - "-exact", "-glob", "-regexp", NULL - }; - enum arrayNamesOptionsEnum { OPT_EXACT, OPT_GLOB, OPT_REGEXP }; - Var *varPtr, *varPtr2; - Tcl_Obj *nameObj, *resultObj, *patternObj; - Tcl_HashSearch search; - const char *pattern = NULL; - int isArray, mode = OPT_GLOB; - - if ((objc < 2) || (objc > 4)) { - Tcl_WrongNumArgs(interp, 1, objv, "arrayName ?mode? ?pattern?"); - return TCL_ERROR; - } - patternObj = (objc > 2 ? objv[objc-1] : NULL); - - if (TCL_ERROR == LocateArray(interp, objv[1], &varPtr, &isArray)) { - return TCL_ERROR; - } - - /* - * Finish parsing the arguments. - */ - - if ((objc == 4) && Tcl_GetIndexFromObj(interp, objv[2], options, "option", - 0, &mode) != TCL_OK) { - return TCL_ERROR; - } + if (varPtr2 == protectedVarPtr) { + VarHashRefCount(varPtr2)--; + } - /* If not an array, the result is empty. */ + /* + * 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. + */ - if (!isArray) { - return TCL_OK; - } + if (search.nextEntryPtr != NULL) { + protectedVarPtr = VarHashGetValue(search.nextEntryPtr); + VarHashRefCount(protectedVarPtr)++; + } else { + protectedVarPtr = NULL; + } - /* - * Check for the trivial cases where we can use a direct lookup. - */ + 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; + } - 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. - */ + case ARRAY_SIZE: { + Tcl_HashSearch search; + int size; - Tcl_ListObjAppendElement(NULL, resultObj, VarHashGetKey(varPtr2)); + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "arrayName"); + return TCL_ERROR; } - Tcl_SetObjResult(interp, resultObj); - return TCL_OK; - } + size = 0; - /* - * Must scan the array to select the elements. - */ + /* + * 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)) { - continue; - } - nameObj = VarHashGetKey(varPtr2); - if (patternObj) { - const char *name = TclGetString(nameObj); - int matched = 0; - - switch ((enum arrayNamesOptionsEnum) 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 (!notArray) { + Var *varPtr2; + + for (varPtr2=VarHashFirstVar(varPtr->value.tablePtr, &search); + varPtr2!=NULL ; varPtr2=VarHashNextVar(&search)) { + if (TclIsVarUndefined(varPtr2)) { + continue; } - break; - } - if (matched == 0) { - continue; + size++; } } - - Tcl_ListObjAppendElement(NULL, resultObj, nameObj); + Tcl_SetObjResult(interp, Tcl_NewIntObj(size)); + break; } - 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; + case ARRAY_STATISTICS: { + const char *stats; - 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; + if (notArray) { + goto error; + } - if (TclIsVarUndefined(varPtr)) { - continue; + 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; } - nameObj = VarHashGetKey(varPtr); - hPtr = Tcl_CreateHashEntry(tablePtr, (char *) nameObj, &dummy); - Tcl_SetHashValue(hPtr, nameObj); + break; + } } + return TCL_OK; + + error: + Tcl_AppendResult(interp, "\"", TclGetString(varNamePtr), + "\" isn't an array", NULL); + return TCL_ERROR; } /* *---------------------------------------------------------------------- * - * ArraySetCmd -- + * TclArraySet -- * - * This object-based function is invoked to process the "array set" 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. * *---------------------------------------------------------------------- */ -static int -ArraySetCmd( - TCL_UNUSED(void *), - 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. */ { - Tcl_Obj *arrayNameObj; - Tcl_Obj *arrayElemObj; Var *varPtr, *arrayPtr; - int result; - - if (objc != 3) { - Tcl_WrongNumArgs(interp, 1, objv, "arrayName list"); - return TCL_ERROR; - } - - if (TCL_ERROR == LocateArray(interp, objv[1], NULL, NULL)) { - return TCL_ERROR; - } + int result, i; - arrayNameObj = objv[1]; varPtr = TclObjLookupVarEx(interp, arrayNameObj, NULL, /*flags*/ TCL_LEAVE_ERR_MSG, /*msg*/ "set", /*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr); @@ -4110,18 +3399,19 @@ ArraySetCmd( } if (arrayPtr) { CleanupVar(varPtr, arrayPtr); - TclObjVarErrMsg(interp, arrayNameObj, NULL, "set", NEEDARRAY, -1); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "VARNAME", - TclGetString(arrayNameObj), (void *)NULL); + TclObjVarErrMsg(interp, arrayNameObj, NULL, "set", needArray, -1); return TCL_ERROR; } + if (arrayElemObj == NULL) { + goto ensureArray; + } + /* * Install the contents of the dictionary or list into the array. */ - arrayElemObj = objv[2]; - if (TclHasInternalRep(arrayElemObj, &tclDictType) && arrayElemObj->bytes == NULL) { + if (arrayElemObj->typePtr == &tclDictType) { Tcl_Obj *keyPtr, *valuePtr; Tcl_DictSearch search; int done; @@ -4155,7 +3445,7 @@ ArraySetCmd( keyPtr, TCL_LEAVE_ERR_MSG, "set", 1, 1, varPtr, -1); if ((elemVarPtr == NULL) || - (TclPtrSetVarIdx(interp, elemVarPtr, varPtr, arrayNameObj, + (TclPtrSetVar(interp, elemVarPtr, varPtr, arrayNameObj, keyPtr, valuePtr, TCL_LEAVE_ERR_MSG, -1) == NULL)) { Tcl_DictObjDone(&search); return TCL_ERROR; @@ -4165,50 +3455,40 @@ ArraySetCmd( } else { /* * Not a dictionary, so assume (and convert to, for backward- - * -compatibility reasons) a list. + * -compatability reasons) a list. */ - Tcl_Size elemLen; + int elemLen; Tcl_Obj **elemPtrs, *copyListObj; - Tcl_Size i; - result = TclListObjLength(interp, arrayElemObj, &elemLen); + 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", (void *)NULL); return TCL_ERROR; } if (elemLen == 0) { goto ensureArray; } - result = TclListObjGetElements(interp, arrayElemObj, - &elemLen, &elemPtrs); - if (result != TCL_OK) { - return result; - } /* * We needn't worry about traces invalidating arrayPtr: should that be - * the case, TclPtrSetVarIdx will return NULL so that we break out of - * the loop and return an error. + * the case, TclPtrSetVar will return NULL so that we break out of the + * loop and return an error. */ copyListObj = TclListObjCopy(NULL, arrayElemObj); - if (!copyListObj) { - return TCL_ERROR; - } 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) || - (TclPtrSetVarIdx(interp, elemVarPtr, varPtr, arrayNameObj, - elemPtrs[i], elemPtrs[i+1], TCL_LEAVE_ERR_MSG, - -1) == NULL)) { + (TclPtrSetVar(interp, elemVarPtr, varPtr, arrayNameObj, + elemPtrs[i],elemPtrs[i+1],TCL_LEAVE_ERR_MSG,-1) == NULL)){ result = TCL_ERROR; break; } @@ -4237,308 +3517,20 @@ ArraySetCmd( */ TclObjVarErrMsg(interp, arrayNameObj, NULL, "array set", - NEEDARRAY, -1); - Tcl_SetErrorCode(interp, "TCL", "WRITE", "ARRAY", (void *)NULL); - return TCL_ERROR; - } - } - TclInitArrayVar(varPtr); - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * 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. - * - *---------------------------------------------------------------------- - */ - -static int -ArraySizeCmd( - TCL_UNUSED(void *), - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - Var *varPtr; - Tcl_HashSearch search; - Var *varPtr2; - int isArray, size = 0; - - if (objc != 2) { - Tcl_WrongNumArgs(interp, 1, objv, "arrayName"); - return TCL_ERROR; - } - - if (TCL_ERROR == LocateArray(interp, objv[1], &varPtr, &isArray)) { - return TCL_ERROR; - } - - /* We can only iterate over the array if it exists... */ - - if (isArray) { - /* - * 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)) { - size++; - } - } - } - - Tcl_SetObjResult(interp, Tcl_NewWideIntObj(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. - * - *---------------------------------------------------------------------- - */ - -static int -ArrayStatsCmd( - TCL_UNUSED(void *), - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - Var *varPtr; - Tcl_Obj *varNameObj; - char *stats; - int isArray; - - if (objc != 2) { - Tcl_WrongNumArgs(interp, 1, objv, "arrayName"); - return TCL_ERROR; - } - varNameObj = objv[1]; - - if (TCL_ERROR == LocateArray(interp, varNameObj, &varPtr, &isArray)) { - return TCL_ERROR; - } - - if (!isArray) { - return NotArrayError(interp, varNameObj); - } - - 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; -} - -/* - *---------------------------------------------------------------------- - * - * ArrayUnsetCmd -- - * - * This object-based function is invoked to process the "array unset" Tcl - * command. See the user documentation for details on what it does. - * - * Results: - * A standard Tcl result object. - * - * Side effects: - * See the user documentation. - * - *---------------------------------------------------------------------- - */ - -static int -ArrayUnsetCmd( - TCL_UNUSED(void *), - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - Var *varPtr, *varPtr2, *protectedVarPtr; - Tcl_Obj *varNameObj, *patternObj, *nameObj; - Tcl_HashSearch search; - const char *pattern; - int unsetFlags = 0; /* Should this be TCL_LEAVE_ERR_MSG? */ - int isArray; - - 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?"); - return TCL_ERROR; - } - - if (TCL_ERROR == LocateArray(interp, varNameObj, &varPtr, &isArray)) { - return TCL_ERROR; - } - - if (!isArray) { - return TCL_OK; - } - - if (!patternObj) { - /* - * When no pattern is given, just unset the whole array. - */ - - return TclObjUnsetVar2(interp, varNameObj, NULL, 0); - } - - /* - * With a trivial pattern, we can just unset. - */ - - pattern = TclGetString(patternObj); - if (TclMatchIsTrivial(pattern)) { - varPtr2 = VarHashFindVar(varPtr->value.tablePtr, patternObj); - if (!varPtr2 || TclIsVarUndefined(varPtr2)) { - return TCL_OK; - } - return TclPtrUnsetVarIdx(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)) { - /* - * Drop the extra ref immediately. We don't need to free it at this - * point though; we'll be unsetting it if necessary soon. - */ - - if (varPtr2 == protectedVarPtr) { - VarHashRefCount(varPtr2)--; - } - - /* - * 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. - */ - - if (search.nextEntryPtr != NULL) { - protectedVarPtr = VarHashGetValue(search.nextEntryPtr); - VarHashRefCount(protectedVarPtr)++; - } else { - protectedVarPtr = NULL; - } - - /* - * If the variable is undefined, clean it out as it has been hit by - * something else (i.e., an unset trace). - */ - - if (TclIsVarUndefined(varPtr2)) { - CleanupVar(varPtr2, varPtr); - continue; - } - - nameObj = VarHashGetKey(varPtr2); - if (Tcl_StringMatch(TclGetString(nameObj), pattern) - && TclPtrUnsetVarIdx(interp, varPtr2, varPtr, varNameObj, - nameObj, unsetFlags, -1) != 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); - } + 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. - * - *---------------------------------------------------------------------- - */ - -Tcl_Command -TclInitArrayCmd( - Tcl_Interp *interp) /* Current interpreter. */ -{ - static const EnsembleImplMap arrayImplMap[] = { - {"anymore", ArrayAnyMoreCmd, TclCompileBasic2ArgCmd, NULL, NULL, 0}, - {"default", ArrayDefaultCmd, TclCompileBasic2Or3ArgCmd, NULL, NULL, 0}, - {"donesearch", ArrayDoneSearchCmd, TclCompileBasic2ArgCmd, NULL, NULL, 0}, - {"exists", ArrayExistsCmd, TclCompileArrayExistsCmd, NULL, NULL, 0}, - {"for", ArrayForObjCmd, TclCompileBasic3ArgCmd, ArrayForNRCmd, 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" @@ -4546,7 +3538,7 @@ TclInitArrayCmd( * * Results: * A standard Tcl completion code. If an error occurs then an error - * message is left in interp. + * message is left in iPtr->result. * * Side effects: * The variable given by myName is linked to the variable in framePtr @@ -4566,7 +3558,7 @@ ObjMakeUpvar( * NULL means use global :: context. */ Tcl_Obj *otherP1Ptr, const char *otherP2, /* Two-part name of variable in framePtr. */ - int otherFlags, /* 0, TCL_GLOBAL_ONLY or TCL_NAMESPACE_ONLY: + const int otherFlags, /* 0, TCL_GLOBAL_ONLY or TCL_NAMESPACE_ONLY: * indicates scope of "other" variable. */ Tcl_Obj *myNamePtr, /* Name of variable which will refer to * otherP1/otherP2. Must be a scalar. */ @@ -4618,16 +3610,14 @@ 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", (void *)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; } } - return TclPtrObjMakeUpvarIdx(interp, otherPtr, myNamePtr, myFlags, index); + return TclPtrObjMakeUpvar(interp, otherPtr, myNamePtr, myFlags, index); } /* @@ -4640,7 +3630,7 @@ ObjMakeUpvar( * * Results: * A standard Tcl completion code. If an error occurs then an error - * message is left in interp. + * message is left in iPtr->result. * * Side effects: * The variable given by myName is linked to the variable in framePtr @@ -4669,32 +3659,17 @@ TclPtrMakeUpvar( myNamePtr = Tcl_NewStringObj(myName, -1); Tcl_IncrRefCount(myNamePtr); } - result = TclPtrObjMakeUpvarIdx(interp, otherPtr, myNamePtr, myFlags, - index); + result = TclPtrObjMakeUpvar(interp, otherPtr, myNamePtr, myFlags, index); if (myNamePtr) { Tcl_DecrRefCount(myNamePtr); } return result; } -int -TclPtrObjMakeUpvar( - Tcl_Interp *interp, /* Interpreter containing variables. Used for - * error messages, too. */ - Tcl_Var otherPtr, /* Pointer to the variable being linked-to. */ - Tcl_Obj *myNamePtr, /* Name of variable which will refer to - * otherP1/otherP2. Must be a scalar. */ - int myFlags) /* 0, TCL_GLOBAL_ONLY or TCL_NAMESPACE_ONLY: - * indicates scope of myName. */ -{ - return TclPtrObjMakeUpvarIdx(interp, (Var *) otherPtr, myNamePtr, myFlags, - -1); -} - /* Callers must Incr myNamePtr if they plan to Decr it. */ int -TclPtrObjMakeUpvarIdx( +TclPtrObjMakeUpvar( Tcl_Interp *interp, /* Interpreter containing variables. Used for * error messages, too. */ Var *otherPtr, /* Pointer to the variable being linked-to. */ @@ -4707,7 +3682,7 @@ TclPtrObjMakeUpvarIdx( { Interp *iPtr = (Interp *) interp; CallFrame *varFramePtr = iPtr->varFramePtr; - const char *errMsg, *p, *myName; + const char *errMsg, *myName; Var *varPtr; if (index >= 0) { @@ -4718,6 +3693,7 @@ TclPtrObjMakeUpvarIdx( 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 @@ -4734,49 +3710,41 @@ TclPtrObjMakeUpvarIdx( * 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", - (void *)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), (void *)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", (void *)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", (void *)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 @@ -4784,22 +3752,21 @@ TclPtrObjMakeUpvarIdx( * 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", (void *)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); @@ -4824,14 +3791,12 @@ TclPtrObjMakeUpvarIdx( * * 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. * *---------------------------------------------------------------------- */ -#ifndef TCL_NO_DEPRECATED #undef Tcl_UpVar int Tcl_UpVar( @@ -4842,30 +3807,12 @@ 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); } -#endif /* TCL_NO_DEPRECATED */ /* *---------------------------------------------------------------------- @@ -4881,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. * *---------------------------------------------------------------------- */ @@ -4897,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; @@ -4911,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, @@ -4949,37 +3895,39 @@ Tcl_GetVariableFullName( * variable's full name is appended. */ { Interp *iPtr = (Interp *) interp; - Var *varPtr = (Var *) variable; - Tcl_Obj *namePtr; - Namespace *nsPtr; - - if (!varPtr || TclIsVarArrayElement(varPtr)) { - return; - } + register Var *varPtr = (Var *) variable; /* * 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) { - Tcl_Size 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); + } + } } } } @@ -5003,16 +3951,21 @@ Tcl_GetVariableFullName( int Tcl_GlobalObjCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { Interp *iPtr = (Interp *) interp; - Tcl_Obj *objPtr, *tailPtr; - const char *varName; - const char *tail; - int result, i; + register Tcl_Obj *objPtr, *tailPtr; + 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. @@ -5023,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. @@ -5107,19 +4062,25 @@ Tcl_GlobalObjCmd( int Tcl_VariableObjCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ 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. @@ -5138,8 +4099,7 @@ Tcl_VariableObjCmd( */ TclObjVarErrMsg(interp, varNamePtr, NULL, "define", - ISARRAYELEMENT, -1); - Tcl_SetErrorCode(interp, "TCL", "UPVAR", "LOCAL_ELEMENT", (void *)NULL); + isArrayElement, -1); return TCL_ERROR; } @@ -5163,9 +4123,8 @@ Tcl_VariableObjCmd( */ if (i+1 < objc) { /* A value was specified. */ - varValuePtr = TclPtrSetVarIdx(interp, varPtr, arrayPtr, - varNamePtr, NULL, objv[i+1], - (TCL_NAMESPACE_ONLY | TCL_LEAVE_ERR_MSG), -1); + varValuePtr = TclPtrSetVar(interp, varPtr, arrayPtr, varNamePtr, + NULL, objv[i+1], TCL_NAMESPACE_ONLY|TCL_LEAVE_ERR_MSG,-1); if (varValuePtr == NULL) { return TCL_ERROR; } @@ -5238,68 +4197,38 @@ Tcl_VariableObjCmd( *---------------------------------------------------------------------- */ + /* ARGSUSED */ int Tcl_UpvarObjCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ 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), (void *)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 @@ -5320,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 @@ -5330,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. + * *---------------------------------------------------------------------- */ @@ -5345,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 = (ArraySearch *)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 = (ArraySearch *)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, (void *)NULL); + Tcl_AppendResult(interp, "couldn't find search \"", string, "\"", NULL); + badLookup: + Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "ARRAYSEARCH", string, NULL); return NULL; } @@ -5405,19 +4432,18 @@ ParseSearchId( static void DeleteSearches( Interp *iPtr, - Var *arrayVarPtr) /* Variable whose searches are to be + 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 = (ArraySearch *)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); @@ -5465,34 +4491,32 @@ TclDeleteNamespaceVars( for (varPtr = VarHashFirstVar(tablePtr, &search); varPtr != NULL; varPtr = VarHashFirstVar(tablePtr, &search)) { - Tcl_Obj *objPtr; - TclNewObj(objPtr); + 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 = (VarTrace *)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; @@ -5503,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); } @@ -5548,7 +4561,7 @@ TclDeleteVars( { Tcl_Interp *interp = (Tcl_Interp *) iPtr; Tcl_HashSearch search; - Var *varPtr; + register Var *varPtr; int flags; Namespace *currNsPtr = (Namespace *) TclGetCurrentNamespace(interp); @@ -5564,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); @@ -5600,8 +4613,8 @@ TclDeleteCompiledLocalVars( CallFrame *framePtr) /* Procedure call frame containing compiler- * assigned local variables to delete. */ { - Var *varPtr; - Tcl_Size numLocals, i; + register Var *varPtr; + int numLocals, i; Tcl_Obj **namePtrPtr; numLocals = framePtr->numCompiledLocals; @@ -5609,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; } @@ -5642,18 +4655,20 @@ 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; - Var *elPtr; + register Var *elPtr; ActiveVarTrace *activePtr; Tcl_Obj *objPtr; VarTrace *tracePtr; + if (varPtr->flags & VAR_SEARCH_ACTIVE) { + DeleteSearches(iPtr, varPtr); + } for (elPtr = VarHashFirstVar(varPtr->value.tablePtr, &search); elPtr != NULL; elPtr = VarHashNextVar(&search)) { if (TclIsVarScalar(elPtr) && (elPtr->value.objPtr != NULL)) { @@ -5678,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 = (VarTrace *)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; @@ -5709,13 +4723,14 @@ DeleteArray( TclClearVarNamespaceVar(elPtr); } - DeleteArrayVar(varPtr); + VarHashDeleteTable(varPtr->value.tablePtr); + ckfree((char *) varPtr->value.tablePtr); } /* *---------------------------------------------------------------------- * - * TclObjVarErrMsg -- + * TclTclObjVarErrMsg -- * * Generate a reasonable error message describing why a variable * operation failed. @@ -5767,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", @@ -5787,27 +4799,45 @@ 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_Size index; - Tcl_Obj *namePtr; - - LocalGetInternalRep(objPtr, index, namePtr); - - index++; /* Compiler warning bait. */ + Tcl_Obj *namePtr = (Tcl_Obj *) objPtr->internalRep.ptrAndLongRep.ptr; if (namePtr) { Tcl_DecrRefCount(namePtr); } + objPtr->typePtr = NULL; } static void @@ -5815,16 +4845,60 @@ DupLocalVarName( Tcl_Obj *srcPtr, Tcl_Obj *dupPtr) { - Tcl_Size index; - Tcl_Obj *namePtr; + Tcl_Obj *namePtr = srcPtr->internalRep.ptrAndLongRep.ptr; - LocalGetInternalRep(srcPtr, index, namePtr); if (!namePtr) { namePtr = srcPtr; } - LocalSetInternalRep(dupPtr, index, namePtr); + dupPtr->internalRep.ptrAndLongRep.ptr = namePtr; + Tcl_IncrRefCount(namePtr); + + 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 - * @@ -5838,16 +4912,14 @@ static void FreeParsedVarName( Tcl_Obj *objPtr) { - Tcl_Obj *arrayPtr, *elem; - int parsed; - - ParsedGetInternalRep(objPtr, parsed, arrayPtr, elem); + register Tcl_Obj *arrayPtr = objPtr->internalRep.twoPtrValue.ptr1; + register char *elem = objPtr->internalRep.twoPtrValue.ptr2; - parsed++; /* Silence compiler. */ if (arrayPtr != NULL) { TclDecrRefCount(arrayPtr); - TclDecrRefCount(elem); + ckfree(elem); } + objPtr->typePtr = NULL; } static void @@ -5855,13 +4927,58 @@ DupParsedVarName( Tcl_Obj *srcPtr, Tcl_Obj *dupPtr) { - Tcl_Obj *arrayPtr, *elem; - int parsed; + register Tcl_Obj *arrayPtr = srcPtr->internalRep.twoPtrValue.ptr1; + register char *elem = srcPtr->internalRep.twoPtrValue.ptr2; + char *elemCopy; + + if (arrayPtr != NULL) { + unsigned int elemLen; + + Tcl_IncrRefCount(arrayPtr); + 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); - ParsedGetInternalRep(srcPtr, parsed, arrayPtr, elem); + totalLen = len1 + len2 + 2; + p = ckalloc((unsigned int) totalLen + 1); + objPtr->bytes = p; + objPtr->length = totalLen; - parsed++; /* Silence compiler. */ - ParsedSetInternalRep(dupPtr, arrayPtr, elem); + memcpy(p, part1, (unsigned int) len1); + p += len1; + *p++ = '('; + memcpy(p, part2, (unsigned int) len2); + p += len2; + *p++ = ')'; + *p = '\0'; } /* @@ -5898,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. */ @@ -5929,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. */ @@ -5944,11 +5059,10 @@ ObjFindNamespaceVar( Namespace *nsPtr[2], *cxtNsPtr; const char *simpleName; Var *varPtr; - int search; - int result; + register int search; 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 @@ -5964,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; @@ -5977,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; @@ -5986,7 +5102,7 @@ ObjFindNamespaceVar( if (result == TCL_OK) { return var; } else if (result != TCL_CONTINUE) { - return NULL; + return (Tcl_Var) NULL; } } @@ -6019,9 +5135,9 @@ ObjFindNamespaceVar( Tcl_DecrRefCount(simpleNamePtr); } if ((varPtr == NULL) && (flags & TCL_LEAVE_ERR_MSG)) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "unknown variable \"%s\"", name)); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "VARIABLE", name, (void *)NULL); + Tcl_ResetResult(interp); + Tcl_AppendResult(interp, "unknown variable \"", name, "\"", NULL); + Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "VARIABLE", name, NULL); } return (Tcl_Var) varPtr; } @@ -6052,21 +5168,21 @@ ObjFindNamespaceVar( int TclInfoVarsCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ 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 @@ -6090,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); @@ -6117,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 @@ -6135,7 +5256,7 @@ TclInfoVarsCmd( if (!TclIsVarUndefined(varPtr) || TclIsVarNamespaceVar(varPtr)) { if (specificNsInPattern) { - TclNewObj(elemObjPtr); + elemObjPtr = Tcl_NewObj(); Tcl_GetVariableFullName(interp, (Tcl_Var) varPtr, elemObjPtr); } else { @@ -6158,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); @@ -6168,7 +5291,7 @@ TclInfoVarsCmd( if ((simplePattern == NULL) || Tcl_StringMatch(varName, simplePattern)) { if (specificNsInPattern) { - TclNewObj(elemObjPtr); + elemObjPtr = Tcl_NewObj(); Tcl_GetVariableFullName(interp, (Tcl_Var) varPtr, elemObjPtr); } else { @@ -6190,7 +5313,7 @@ TclInfoVarsCmd( */ if ((nsPtr != globalNsPtr) && !specificNsInPattern) { - varPtr = VarHashFirstVar(&globalNsPtr->varTable, &search); + varPtr = VarHashFirstVar(&globalNsPtr->varTable,&search); while (varPtr) { if (!TclIsVarUndefined(varPtr) || TclIsVarNamespaceVar(varPtr)) { @@ -6209,7 +5332,7 @@ TclInfoVarsCmd( } } } - } else if (iPtr->varFramePtr->procPtr != NULL) { + } else if (((Interp *)interp)->varFramePtr->procPtr != NULL) { AppendLocals(interp, listPtr, simplePatternPtr, 1); } @@ -6243,16 +5366,16 @@ TclInfoVarsCmd( int TclInfoGlobalsCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ 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; @@ -6299,6 +5422,9 @@ TclInfoGlobalsCmd( for (varPtr = VarHashFirstVar(&globalNsPtr->varTable, &search); varPtr != NULL; varPtr = VarHashNextVar(&search)) { + char *varName; + Tcl_Obj *varNamePtr; + if (TclIsVarUndefined(varPtr)) { continue; } @@ -6336,13 +5462,14 @@ TclInfoGlobalsCmd( int TclInfoLocalsCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ 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; @@ -6353,7 +5480,7 @@ TclInfoLocalsCmd( return TCL_ERROR; } - if (!HasLocalVars(iPtr->varFramePtr)) { + if (!(iPtr->varFramePtr->isProcCallFrame & FRAME_IS_PROC )) { return TCL_OK; } @@ -6395,42 +5522,32 @@ AppendLocals( { Interp *iPtr = (Interp *) interp; Var *varPtr; - Tcl_Size i, localVarCt; - int added; - Tcl_Obj *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; - if (includeLinks) { - Tcl_InitObjHashTable(&addedTable); - } - - if (localVarCt > 0) { - Tcl_Obj **varNamePtr = &iPtr->varFramePtr->localCachePtr->varName0; + varNamePtr = &iPtr->varFramePtr->localCachePtr->varName0; - for (i = 0; i < localVarCt; i++, varNamePtr++) { - /* - * Skip nameless (temporary) variables and undefined variables. - */ + for (i = 0; i < localVarCt; i++, varNamePtr++) { + /* + * Skip nameless (temporary) variables and undefined variables. + */ - if (*varNamePtr && !TclIsVarUndefined(varPtr) + if (*varNamePtr && !TclIsVarUndefined(varPtr) && (includeLinks || !TclIsVarLink(varPtr))) { - varName = TclGetString(*varNamePtr); - if ((pattern == NULL) || Tcl_StringMatch(varName, pattern)) { - Tcl_ListObjAppendElement(interp, listPtr, *varNamePtr); - if (includeLinks) { - Tcl_CreateHashEntry(&addedTable, *varNamePtr, &added); - } - } + varName = TclGetString(*varNamePtr); + if ((pattern == NULL) || Tcl_StringMatch(varName, pattern)) { + Tcl_ListObjAppendElement(interp, listPtr, *varNamePtr); } - varPtr++; } + varPtr++; } /* @@ -6438,7 +5555,7 @@ AppendLocals( */ if (localVarTablePtr == NULL) { - goto objectVars; + return; } /* @@ -6452,13 +5569,9 @@ AppendLocals( && (includeLinks || !TclIsVarLink(varPtr))) { Tcl_ListObjAppendElement(interp, listPtr, VarHashGetKey(varPtr)); - if (includeLinks) { - Tcl_CreateHashEntry(&addedTable, VarHashGetKey(varPtr), - &added); - } } } - goto objectVars; + return; } /* @@ -6474,66 +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) { - Method *mPtr = (Method *) - Tcl_ObjectContextMethod((Tcl_ObjectContext)iPtr->varFramePtr->clientData); - PrivateVariableMapping *privatePtr; - - if (mPtr->declaringObjectPtr) { - Object *oPtr = mPtr->declaringObjectPtr; - - FOREACH(objNamePtr, oPtr->variables) { - Tcl_CreateHashEntry(&addedTable, objNamePtr, &added); - if (added && (!pattern || - Tcl_StringMatch(TclGetString(objNamePtr), pattern))) { - Tcl_ListObjAppendElement(interp, listPtr, objNamePtr); - } - } - FOREACH_STRUCT(privatePtr, oPtr->privateVariables) { - Tcl_CreateHashEntry(&addedTable, privatePtr->variableObj, - &added); - if (added && (!pattern || - Tcl_StringMatch(TclGetString(privatePtr->variableObj), - pattern))) { - Tcl_ListObjAppendElement(interp, listPtr, - privatePtr->variableObj); - } - } - } else { - Class *clsPtr = mPtr->declaringClassPtr; - - FOREACH(objNamePtr, clsPtr->variables) { - Tcl_CreateHashEntry(&addedTable, objNamePtr, &added); - if (added && (!pattern || - Tcl_StringMatch(TclGetString(objNamePtr), pattern))) { - Tcl_ListObjAppendElement(interp, listPtr, objNamePtr); - } - } - FOREACH_STRUCT(privatePtr, clsPtr->privateVariables) { - Tcl_CreateHashEntry(&addedTable, privatePtr->variableObj, - &added); - if (added && (!pattern || - Tcl_StringMatch(TclGetString(privatePtr->variableObj), - pattern))) { - Tcl_ListObjAppendElement(interp, listPtr, - privatePtr->variableObj); - } - } - } - } - Tcl_DeleteHashTable(&addedTable); } /* @@ -6552,19 +5608,19 @@ TclInitVarHashTable( static Tcl_HashEntry * AllocVarEntry( - TCL_UNUSED(Tcl_HashTable *), + Tcl_HashTable *tablePtr, /* Hash table. */ void *keyPtr) /* Key to store in the hash table entry. */ { - Tcl_Obj *objPtr = (Tcl_Obj *)keyPtr; + Tcl_Obj *objPtr = (Tcl_Obj *) keyPtr; Tcl_HashEntry *hPtr; Var *varPtr; - varPtr = (Var *)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); @@ -6581,7 +5637,7 @@ FreeVarEntry( if (TclIsVarUndefined(varPtr) && !TclIsVarTraced(varPtr) && (VarHashRefCount(varPtr) == 1)) { - ckfree(varPtr); + ckfree((char *) varPtr); } else { VarHashInvalidateEntry(varPtr); TclSetVarUndefined(varPtr); @@ -6592,291 +5648,81 @@ FreeVarEntry( static int CompareVarKeys( - void *keyPtr, /* New key to compare. */ + void *keyPtr, /* New key to compare. */ Tcl_HashEntry *hPtr) /* Existing key to compare. */ { - Tcl_Obj *objPtr1 = (Tcl_Obj *)keyPtr; + Tcl_Obj *objPtr1 = (Tcl_Obj *) keyPtr; Tcl_Obj *objPtr2 = hPtr->key.objPtr; - const char *p1, *p2; - Tcl_Size l1, l2; + 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 * register. */ - p1 = TclGetStringFromObj(objPtr1, &l1); - p2 = TclGetStringFromObj(objPtr2, &l2); - - /* - * Only compare string representations of the same length. - */ - - return ((l1 == l2) && !memcmp(p1, p2, l1)); -} - -/*---------------------------------------------------------------------- - * - * ArrayDefaultCmd -- - * - * This function implements the 'array default' Tcl command. - * Refer to the user documentation for details on what it does. - * - * Results: - * Returns a standard Tcl result. - * - * Side effects: - * See the user documentation. - * - *---------------------------------------------------------------------- - */ - -static int -ArrayDefaultCmd( - TCL_UNUSED(void *), - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - static const char *const options[] = { - "get", "set", "exists", "unset", NULL - }; - enum arrayDefaultOptionsEnum { OPT_GET, OPT_SET, OPT_EXISTS, OPT_UNSET }; - Tcl_Obj *arrayNameObj, *defaultValueObj; - Var *varPtr, *arrayPtr; - int isArray, option; + p1 = TclGetString(objPtr1); + l1 = objPtr1->length; + p2 = TclGetString(objPtr2); + l2 = objPtr2->length; /* - * Parse arguments. + * Only compare if the string representations are of the same length. */ - if (objc != 3 && objc != 4) { - Tcl_WrongNumArgs(interp, 1, objv, "option arrayName ?value?"); - return TCL_ERROR; - } - if (Tcl_GetIndexFromObj(interp, objv[1], options, "option", - 0, &option) != TCL_OK) { - return TCL_ERROR; - } - - arrayNameObj = objv[2]; - - if (TCL_ERROR == LocateArray(interp, arrayNameObj, &varPtr, &isArray)) { - return TCL_ERROR; - } - - switch ((enum arrayDefaultOptionsEnum)option) { - case OPT_GET: - if (objc != 3) { - Tcl_WrongNumArgs(interp, 2, objv, "arrayName"); - return TCL_ERROR; - } - if (!varPtr || TclIsVarUndefined(varPtr) || !isArray) { - return NotArrayError(interp, arrayNameObj); - } - - defaultValueObj = TclGetArrayDefault(varPtr); - if (!defaultValueObj) { - /* Array default must exist. */ - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "array has no default value", -1)); - Tcl_SetErrorCode(interp, "TCL", "READ", "ARRAY", "DEFAULT", (void *)NULL); - return TCL_ERROR; - } - Tcl_SetObjResult(interp, defaultValueObj); - return TCL_OK; - - case OPT_SET: - if (objc != 4) { - Tcl_WrongNumArgs(interp, 2, objv, "arrayName value"); - return TCL_ERROR; - } - - /* - * Attempt to create array if needed. - */ - varPtr = TclObjLookupVarEx(interp, arrayNameObj, NULL, - /*flags*/ TCL_LEAVE_ERR_MSG, /*msg*/ "array default set", - /*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr); - if (varPtr == NULL) { - return TCL_ERROR; - } - if (arrayPtr) { - /* - * Not a valid array name. - */ - - CleanupVar(varPtr, arrayPtr); - TclObjVarErrMsg(interp, arrayNameObj, NULL, "array default set", - NEEDARRAY, -1); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "VARNAME", - TclGetString(arrayNameObj), (void *)NULL); - return TCL_ERROR; - } - if (!TclIsVarArray(varPtr) && !TclIsVarUndefined(varPtr)) { - /* - * Not an array. - */ - - TclObjVarErrMsg(interp, arrayNameObj, NULL, "array default set", - NEEDARRAY, -1); - Tcl_SetErrorCode(interp, "TCL", "WRITE", "ARRAY", (void *)NULL); - return TCL_ERROR; - } - - if (!TclIsVarArray(varPtr)) { - TclInitArrayVar(varPtr); - } - defaultValueObj = objv[3]; - SetArrayDefault(varPtr, defaultValueObj); - return TCL_OK; - - case OPT_EXISTS: - if (objc != 3) { - Tcl_WrongNumArgs(interp, 2, objv, "arrayName"); - return TCL_ERROR; - } - - /* - * Undefined variables (whether or not they have storage allocated) do - * not have defaults, and this is not an error case. - */ - - if (!varPtr || TclIsVarUndefined(varPtr)) { - Tcl_SetObjResult(interp, Tcl_NewBooleanObj(0)); - } else if (!isArray) { - return NotArrayError(interp, arrayNameObj); - } else { - defaultValueObj = TclGetArrayDefault(varPtr); - Tcl_SetObjResult(interp, Tcl_NewBooleanObj(!!defaultValueObj)); - } - return TCL_OK; - - case OPT_UNSET: - if (objc != 3) { - Tcl_WrongNumArgs(interp, 2, objv, "arrayName"); - return TCL_ERROR; - } - - if (varPtr && !TclIsVarUndefined(varPtr)) { - if (!isArray) { - return NotArrayError(interp, arrayNameObj); + if (l1 == l2) { + for (;; p1++, p2++, l1--) { + if (*p1 != *p2) { + break; + } + if (l1 == 0) { + return 1; } - SetArrayDefault(varPtr, NULL); } - return TCL_OK; } - /* Unreached */ - return TCL_ERROR; -} - -/* - * Initialize array variable. - */ - -void -TclInitArrayVar( - Var *arrayPtr) -{ - ArrayVarHashTable *tablePtr = (ArrayVarHashTable *)ckalloc(sizeof(ArrayVarHashTable)); - - /* - * Mark the variable as an array. - */ - - TclSetVarArray(arrayPtr); - - /* - * Regular TclVarHashTable initialization. - */ - - arrayPtr->value.tablePtr = (TclVarHashTable *) tablePtr; - TclInitVarHashTable(arrayPtr->value.tablePtr, TclGetVarNsPtr(arrayPtr)); - - /* - * Default value initialization. - */ - - tablePtr->defaultObj = NULL; -} - -/* - * Cleanup array variable. - */ - -static void -DeleteArrayVar( - Var *arrayPtr) -{ - ArrayVarHashTable *tablePtr = (ArrayVarHashTable *) - arrayPtr->value.tablePtr; - - /* - * Default value cleanup. - */ - - SetArrayDefault(arrayPtr, NULL); - - /* - * Regular TclVarHashTable cleanup. - */ - - VarHashDeleteTable(arrayPtr->value.tablePtr); - ckfree(tablePtr); -} - -/* - * Get array default value if any. - */ - -Tcl_Obj * -TclGetArrayDefault( - Var *arrayPtr) -{ - ArrayVarHashTable *tablePtr = (ArrayVarHashTable *) - arrayPtr->value.tablePtr; - - return tablePtr->defaultObj; + return 0; } - -/* - * Set/replace/unset array default value. - */ -static void -SetArrayDefault( - Var *arrayPtr, - Tcl_Obj *defaultObj) +static unsigned int +HashVarKey( + Tcl_HashTable *tablePtr, /* Hash table. */ + void *keyPtr) /* Key from which to compute hash value. */ { - ArrayVarHashTable *tablePtr = (ArrayVarHashTable *) - arrayPtr->value.tablePtr; + Tcl_Obj *objPtr = (Tcl_Obj *) keyPtr; + const char *string = TclGetString(objPtr); + int length = objPtr->length; + unsigned int result = 0; + int i; /* - * Increment/decrement refcount twice to ensure that the object is shared, - * so that it doesn't get modified accidentally by the folling code: + * 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: * - * array default set v 1 - * lappend v(a) 2; # returns a new object {1 2} - * set v(b); # returns the original default object "1" + * 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. */ - if (tablePtr->defaultObj) { - Tcl_DecrRefCount(tablePtr->defaultObj); - Tcl_DecrRefCount(tablePtr->defaultObj); - } - tablePtr->defaultObj = defaultObj; - if (tablePtr->defaultObj) { - Tcl_IncrRefCount(tablePtr->defaultObj); - Tcl_IncrRefCount(tablePtr->defaultObj); + for (i=0 ; i<length ; i++) { + result += (result << 3) + string[i]; } + return result; } /* |
