Tcl Source Code

Artifact [e36657d3c4]
Login

Artifact e36657d3c450dec6bc591f5871c1400fbeb324dc:

Attachment "upvartraces.patch" to ticket [2969488fff] added by ferrieux 2010-03-13 07:18:30.
Index: doc/upvar.n
===================================================================
RCS file: /cvsroot/tcl/tcl/doc/upvar.n,v
retrieving revision 1.21
diff -u -p -r1.21 upvar.n
--- doc/upvar.n	20 Jan 2010 13:42:17 -0000	1.21
+++ doc/upvar.n	13 Mar 2010 00:16:55 -0000
@@ -100,12 +100,6 @@ set originalVar 1
 trace variable originalVar w \fItraceproc\fR
 \fIsetByUpvar\fR originalVar 2
 .CE
-.PP
-If \fIotherVar\fR refers to an element of an array, then variable
-traces set for the entire array will not be invoked when \fImyVar\fR
-is accessed (but traces on the particular element will still be
-invoked).  In particular, if the array is \fBenv\fR, then changes
-made to \fImyVar\fR will not be passed to subprocesses correctly.
 .SH EXAMPLE
 A \fBdecr\fR command that works like \fBincr\fR except it subtracts
 the value from the variable instead of adding it:
Index: generic/tclInt.h
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclInt.h,v
retrieving revision 1.464
diff -u -p -r1.464 tclInt.h
--- generic/tclInt.h	5 Mar 2010 14:34:04 -0000	1.464
+++ generic/tclInt.h	13 Mar 2010 00:17:01 -0000
@@ -191,15 +191,16 @@ typedef struct Tcl_Ensemble Tcl_Ensemble
 typedef struct NamespacePathEntry NamespacePathEntry;
 
 /*
- * Special hashtable for variables: this is just a Tcl_HashTable with an nsPtr
- * field added at the end: in this way variables can find their namespace
- * without having to copy a pointer in their struct: they can access it via
- * their hPtr->tablePtr.
+ * Special hashtable for variables: this is just a Tcl_HashTable with nsPtr
+ * and arrayPtr fields added at the end: in this way variables can find their
+ * namespace and possibly containing array without having to copy a pointer in
+ * their struct: they can access them via their hPtr->tablePtr.
  */
 
 typedef struct TclVarHashTable {
     Tcl_HashTable table;
     struct Namespace *nsPtr;
+    struct Var *arrayPtr;
 } TclVarHashTable;
 
 /*
@@ -831,6 +832,14 @@ typedef struct VarInHash {
  * MODULE_SCOPE int	TclIsVarResolved(Var *varPtr);
  */
 
+#define TclVarFindHiddenArray(varPtr,arrayPtrVar)			\
+    do {								\
+    if ((arrayPtr == NULL) && TclIsVarInHash(varPtr) &&			\
+	(TclVarParentArray(varPtr) != NULL)) {				\
+	arrayPtr = TclVarParentArray(varPtr);				\
+    }									\
+    } while(0)
+
 #define TclIsVarScalar(varPtr) \
     !((varPtr)->flags & (VAR_ARRAY|VAR_LINK))
 
@@ -875,6 +884,9 @@ typedef struct VarInHash {
 	? ((TclVarHashTable *) ((((VarInHash *) (varPtr))->entry.tablePtr)))->nsPtr \
 	: NULL)
 
+#define TclVarParentArray(varPtr)					\
+    ((TclVarHashTable *) ((VarInHash *) (varPtr))->entry.tablePtr)->arrayPtr
+
 #define VarHashRefCount(varPtr) \
     ((VarInHash *) (varPtr))->refCount
 
@@ -882,19 +894,26 @@ typedef struct VarInHash {
  * Macros for direct variable access by TEBC.
  */
 
-#define TclIsVarDirectReadable(varPtr) \
-    (   !((varPtr)->flags & (VAR_ARRAY|VAR_LINK|VAR_TRACED_READ)) \
-    &&  (varPtr)->value.objPtr)
+#define TclIsVarTricky(varPtr,trickyFlags)				\
+    (									\
+     ((varPtr)->flags & (VAR_ARRAY|VAR_LINK|trickyFlags))		\
+     ||    (TclIsVarInHash(varPtr)					\
+	    && (TclVarParentArray(varPtr) != NULL)			\
+	    && (TclVarParentArray(varPtr)->flags & (trickyFlags))))
+
+#define TclIsVarDirectReadable(varPtr)					\
+    (   (!TclIsVarTricky(varPtr,VAR_TRACED_READ))			\
+	&&  (varPtr)->value.objPtr)
 
 #define TclIsVarDirectWritable(varPtr) \
-    !((varPtr)->flags & (VAR_ARRAY|VAR_LINK|VAR_TRACED_WRITE|VAR_DEAD_HASH))
-
+    (!TclIsVarTricky(varPtr,VAR_TRACED_WRITE|VAR_DEAD_HASH))
+    
 #define TclIsVarDirectUnsettable(varPtr) \
-    !((varPtr)->flags & (VAR_ARRAY|VAR_LINK|VAR_TRACED_UNSET|VAR_DEAD_HASH))
+    (!TclIsVarTricky(varPtr,VAR_TRACED_UNSET|VAR_DEAD_HASH))
 
 #define TclIsVarDirectModifyable(varPtr) \
-    (   !((varPtr)->flags & (VAR_ARRAY|VAR_LINK|VAR_TRACED_READ|VAR_TRACED_WRITE)) \
-    &&  (varPtr)->value.objPtr)
+    (   (!TclIsVarTricky(varPtr,VAR_TRACED_READ|VAR_TRACED_WRITE))	\
+	&&  (varPtr)->value.objPtr)
 
 #define TclIsVarDirectReadable2(varPtr, arrayPtr) \
     (TclIsVarDirectReadable(varPtr) &&\
Index: generic/tclVar.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclVar.c,v
retrieving revision 1.198
diff -u -p -r1.198 tclVar.c
--- generic/tclVar.c	5 Mar 2010 14:34:04 -0000	1.198
+++ generic/tclVar.c	13 Mar 2010 00:17:07 -0000
@@ -1147,6 +1147,7 @@ TclLookupArrayElement(
 	    nsPtr = NULL;
 	}
 	TclInitVarHashTable(arrayPtr->value.tablePtr, nsPtr);
+	tablePtr->arrayPtr = arrayPtr;
     } else if (!TclIsVarArray(arrayPtr)) {
 	if (flags & TCL_LEAVE_ERR_MSG) {
 	    TclObjVarErrMsg(interp, arrayNamePtr, elNamePtr, msg, needArray,
@@ -1432,6 +1433,8 @@ TclPtrGetVar(
     Interp *iPtr = (Interp *) interp;
     const char *msg;
 
+    TclVarFindHiddenArray(varPtr, arrayPtr);
+
     /*
      * Invoke any read traces that have been set for the variable.
      */
@@ -1865,6 +1868,8 @@ TclPtrSetVar(
 	goto earlyError;
     }
 
+    TclVarFindHiddenArray(varPtr, arrayPtr);
+
     /*
      * Invoke any read traces that have been set for the variable if it is
      * requested; this is only done in the core by the INST_LAPPEND_*
@@ -2338,6 +2343,8 @@ TclPtrUnsetVar(
 	VarHashRefCount(varPtr)++;
     }
 
+    TclVarFindHiddenArray(varPtr, arrayPtr);
+
     UnsetVarStruct(varPtr, arrayPtr, iPtr, part1Ptr, part2Ptr, flags, index);
 
     /*
@@ -2993,6 +3000,7 @@ TclArraySet(
     varPtr->value.tablePtr = (TclVarHashTable *)
 	    ckalloc(sizeof(TclVarHashTable));
     TclInitVarHashTable(varPtr->value.tablePtr, TclGetVarNsPtr(varPtr));
+    varPtr->value.tablePtr->arrayPtr = varPtr;
     return TCL_OK;
 }
 
@@ -6362,6 +6370,7 @@ TclInitVarHashTable(
     Tcl_InitCustomHashTable(&tablePtr->table,
 	    TCL_CUSTOM_TYPE_KEYS, &tclVarHashKeyType);
     tablePtr->nsPtr = nsPtr;
+    tablePtr->arrayPtr = NULL;
 }
 
 static Tcl_HashEntry *