wasCSharpSQLite – Blame information for rev 4

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /*
2 * Var.java
3 *
4 * Copyright (c) 1997 Sun Microsystems, Inc.
5 *
6 * See the file "license.terms" for information on usage and
7 * redistribution of this file, and for a DISCLAIMER OF ALL
8 * WARRANTIES.
9 *
10 * Included in SQLite3 port to C# for use in testharness only; 2008 Noah B Hart
11 *
12 * RCS @(#) $Id: Var.java,v 1.11 2003/01/09 02:15:39 mdejong Exp $
13 *
14 */
15 using System;
16 using System.Collections;
17 using System.Text;
18  
19 namespace tcl.lang
20 {
21  
22 /// <summary> Flag bits for variables. The first three (SCALAR, ARRAY, and
23 /// LINK) are mutually exclusive and give the "type" of the variable.
24 /// UNDEFINED is independent of the variable's type.
25 ///
26 /// SCALAR - 1 means this is a scalar variable and not
27 /// an array or link. The value field points
28 /// to the variable's value, a Tcl object.
29 /// ARRAY - 1 means this is an array variable rather
30 /// than a scalar variable or link. The
31 /// table field points to the array's
32 /// hashtable for its elements.
33 /// LINK - 1 means this Var structure contains a
34 /// reference to another Var structure that
35 /// either has the real value or is itself
36 /// another LINK pointer. Variables like
37 /// this come about through "upvar" and "global"
38 /// commands, or through references to variables
39 /// in enclosing namespaces.
40 /// UNDEFINED - 1 means that the variable is in the process
41 /// of being deleted. An undefined variable
42 /// logically does not exist and survives only
43 /// while it has a trace, or if it is a global
44 /// variable currently being used by some
45 /// procedure.
46 /// IN_HASHTABLE - 1 means this variable is in a hashtable. 0 if
47 /// a local variable that was assigned a slot
48 /// in a procedure frame by the compiler so the
49 /// Var storage is part of the call frame.
50 /// TRACE_ACTIVE - 1 means that trace processing is currently
51 /// underway for a read or write access, so
52 /// new read or write accesses should not cause
53 /// trace procedures to be called and the
54 /// variable can't be deleted.
55 /// ARRAY_ELEMENT - 1 means that this variable is an array
56 /// element, so it is not legal for it to be
57 /// an array itself (the ARRAY flag had
58 /// better not be set).
59 /// NAMESPACE_VAR - 1 means that this variable was declared
60 /// as a namespace variable. This flag ensures
61 /// it persists until its namespace is
62 /// destroyed or until the variable is unset;
63 /// it will persist even if it has not been
64 /// initialized and is marked undefined.
65 /// The variable's refCount is incremented to
66 /// reflect the "reference" from its namespace.
67 ///
68 /// </summary>
69  
70 [Flags()]
71 public enum VarFlags
72 {
73 SCALAR = 0x1,
74 ARRAY = 0x2,
75 LINK = 0x4,
76 UNDEFINED = 0x8,
77 IN_HASHTABLE = 0x10,
78 TRACE_ACTIVE = 0x20,
79 ARRAY_ELEMENT = 0x40,
80 NAMESPACE_VAR = 0x80,
81 SQLITE3_LINK_INT = 0x100,
82 SQLITE3_LINK_DOUBLE = 0x200,
83 SQLITE3_LINK_BOOLEAN = 0x400,
84 SQLITE3_LINK_STRING = 0x800,
85 SQLITE3_LINK_WIDE_INT = 0x1000,
86 SQLITE3_LINK = 0x10000,
87 SQLITE3_LINK_READ_ONLY = 0x20000,
88 };
89  
90  
91 /*
92 * Implements variables in Tcl. The Var class encapsulates most of the functionality
93 * of the methods in generic/tclVar.c and the structure TCL.Tcl_Var from the C version.
94 */
95  
96 public class Var
97 {
98 /// <summary> Used by ArrayCmd to create a unique searchId string. If the
99 /// sidVec Vector is empty then simply return 1. Else return 1
100 /// plus the SearchId.index value of the last Object in the vector.
101 ///
102 /// </summary>
103 /// <param name="">None
104 /// </param>
105 /// <returns> The int value for unique SearchId string.
106 /// </returns>
107 protected internal int NextIndex
108 {
109 get
110 {
111 lock ( this )
112 {
113 if ( sidVec.Count == 0 )
114 {
115 return 1;
116 }
117 SearchId sid = (SearchId)SupportClass.VectorLastElement( sidVec );
118 return ( sid.Index + 1 );
119 }
120 }
121  
122 }
123  
124 // internal const int SCALAR = 0x1;
125 // internal const int ARRAY = 0x2;
126 // internal const int LINK = 0x4;
127 // internal const int UNDEFINED = 0x8;
128 // internal const int IN_HASHTABLE = 0x10;
129 // internal const int TRACE_ACTIVE = 0x20;
130 // internal const int ARRAY_ELEMENT = 0x40;
131 // internal const int NAMESPACE_VAR = 0x80;
132  
133 // Methods to read various flag bits of variables.
134  
135 internal bool isVarScalar()
136 {
137 return ( ( flags & VarFlags.SCALAR ) != 0 );
138 }
139  
140 internal bool isVarLink()
141 {
142 return ( ( flags & VarFlags.LINK ) != 0 );
143 }
144  
145 internal bool isVarArray()
146 {
147 return ( ( flags & VarFlags.ARRAY ) != 0 );
148 }
149  
150 internal bool isVarUndefined()
151 {
152 return ( ( flags & VarFlags.UNDEFINED ) != 0 );
153 }
154  
155 internal bool isVarArrayElement()
156 {
157 return ( ( flags & VarFlags.ARRAY_ELEMENT ) != 0 );
158 }
159  
160 // Methods to ensure that various flag bits are set properly for variables.
161  
162 internal void setVarScalar()
163 {
164 flags = ( flags & ~( VarFlags.ARRAY | VarFlags.LINK ) ) | VarFlags.SCALAR;
165 }
166  
167 internal void setVarArray()
168 {
169 flags = ( flags & ~( VarFlags.SCALAR | VarFlags.LINK ) ) | VarFlags.ARRAY;
170 }
171  
172 internal void setVarLink()
173 {
174 flags = ( flags & ~( VarFlags.SCALAR | VarFlags.ARRAY ) ) | VarFlags.LINK;
175 }
176  
177 internal void setVarArrayElement()
178 {
179 flags = ( flags & ~VarFlags.ARRAY ) | VarFlags.ARRAY_ELEMENT;
180 }
181  
182 internal void setVarUndefined()
183 {
184 flags |= VarFlags.UNDEFINED;
185 }
186  
187 internal void clearVarUndefined()
188 {
189 flags &= ~VarFlags.UNDEFINED;
190 }
191  
192 /// <summary> Stores the "value" of the variable. It stored different information
193 /// depending on the type of the variable: <ul>
194 /// <li>Scalar variable - (TclObject) value is the object stored in the
195 /// variable.
196 /// <li> Array variable - (Hashtable) value is the hashtable that stores
197 /// all the elements. <p>
198 /// <li> Upvar (Link) - (Var) value is the variable associated by this upvar.
199 /// </ul>
200 /// </summary>
201  
202 internal Object value;
203  
204 /// <summary> Vector that holds the traces that were placed in this Var</summary>
205  
206 internal ArrayList traces;
207  
208  
209 internal ArrayList sidVec;
210  
211 /// <summary> Miscellaneous bits of information about variable.
212 ///
213 /// </summary>
214 /// <seealso cref="Var#SCALAR">
215 /// </seealso>
216 /// <seealso cref="Var#ARRAY">
217 /// </seealso>
218 /// <seealso cref="Var#LINK">
219 /// </seealso>
220 /// <seealso cref="Var#UNDEFINED">
221 /// </seealso>
222 /// <seealso cref="Var#IN_HASHTABLE">
223 /// </seealso>
224 /// <seealso cref="Var#TRACE_ACTIVE">
225 /// </seealso>
226 /// <seealso cref="Var#ARRAY_ELEMENT">
227 /// </seealso>
228 /// <seealso cref="Var#NAMESPACE_VAR">
229 /// </seealso>
230  
231 internal VarFlags flags;
232  
233 /// <summary> If variable is in a hashtable, either the
234 /// hash table entry that refers to this
235 /// variable or null if the variable has been
236 /// detached from its hash table (e.g. an
237 /// array is deleted, but some of its
238 /// elements are still referred to in
239 /// upvars). null if the variable is not in a
240 /// hashtable. This is used to delete an
241 /// variable from its hashtable if it is no
242 /// longer needed.
243 /// </summary>
244  
245 internal Hashtable table;
246  
247 /// <summary> The key under which this variable is stored in the hash table.</summary>
248  
249 internal string hashKey;
250  
251 /// <summary> Counts number of active uses of this
252 /// variable, not including its entry in the
253 /// call frame or the hash table: 1 for each
254 /// additional variable whose link points
255 /// here, 1 for each nested trace active on
256 /// variable, and 1 if the variable is a
257 /// namespace variable. This record can't be
258 /// deleted until refCount becomes 0.
259 /// </summary>
260  
261 internal int refCount;
262  
263 /// <summary> Reference to the namespace that contains
264 /// this variable or null if the variable is
265 /// a local variable in a Tcl procedure.
266 /// </summary>
267  
268 internal NamespaceCmd.Namespace ns;
269  
270 public class SQLITE3_GETSET
271 {
272 string name = "";
273 int _Integer = 0; // Internal integer value
274 StringBuilder _StringBuilder = null; // Internal string value
275 public SQLITE3_GETSET( string name )
276 {
277 this._Integer = 0;
278 this._StringBuilder = new StringBuilder( 500 );
279 this.name = name;
280 }
281 public int iValue
282 {
283 get
284 {
285 return _Integer;
286 }
287 set
288 {
289 _Integer = value;
290 }
291 }
292 public string sValue
293 {
294 get
295 {
296 return _StringBuilder.ToString();
297 }
298 set
299 {
300 _StringBuilder.Length = 0;
301 _StringBuilder.Append( value );
302 }
303 }
304 public void Append( byte[] append )
305 {
306 _StringBuilder.Append( Encoding.UTF8.GetString( append, 0, append.Length ) );
307 }
308  
309 public void Append( string append )
310 {
311 _StringBuilder.Append( append );
312 }
313 public void Trim()
314 {
315 _StringBuilder = new StringBuilder( _StringBuilder.ToString().Trim() );
316 }
317  
318 public int Length
319 {
320 get
321 {
322 return _StringBuilder.Length;
323 }
324 }
325 }
326 /// <summary> Reference to the object the allows getting & setting the sqlite3 linked variable
327 /// </summary>
328 internal object sqlite3_get_set;
329 internal TclObject sqlite3_get()
330 {
331 TclObject to;
332 if ( ( flags & VarFlags.SQLITE3_LINK_READ_ONLY ) != 0 && ( flags & VarFlags.SQLITE3_LINK_INT ) != 0 )
333 if ( sqlite3_get_set.GetType().Name == "Int32" )
334 to = TclInteger.newInstance( (Int32)sqlite3_get_set );
335 else
336 to = TclInteger.newInstance( ( (SQLITE3_GETSET)sqlite3_get_set ).iValue );
337 else if ( ( flags & VarFlags.SQLITE3_LINK_INT ) != 0 )
338 {
339 if ( sqlite3_get_set.GetType().Name == "Int32" )
340 to = TclInteger.newInstance( (Int32)sqlite3_get_set );
341 else
342 to = TclInteger.newInstance( ( (SQLITE3_GETSET)sqlite3_get_set ).iValue );
343 }
344 else
345 to = TclString.newInstance( ( (SQLITE3_GETSET)sqlite3_get_set ).sValue );
346 to.preserve();
347 return to;
348 }
349 internal void sqlite3_set( TclObject to )
350 {
351 if ( ( flags & VarFlags.SQLITE3_LINK_READ_ONLY ) == 0 )
352 {
353 if ( ( flags & VarFlags.SQLITE3_LINK_INT ) != 0 )
354 ( (SQLITE3_GETSET)sqlite3_get_set ).iValue = Convert.ToInt32( to.ToString() );
355 else
356 if ( ( flags & VarFlags.SQLITE3_LINK_STRING ) != 0 )
357 ( (SQLITE3_GETSET)sqlite3_get_set ).sValue = to.ToString();
358 else
359 ( (SQLITE3_GETSET)sqlite3_get_set ).sValue = to.ToString();
360 }
361 }
362  
363 internal bool isSQLITE3_Link()
364 {
365 return ( ( flags & VarFlags.SQLITE3_LINK ) != 0 );
366 }
367  
368  
369 /// <summary> NewVar -> Var
370 ///
371 /// Construct a variable and initialize its fields.
372 /// </summary>
373  
374 internal Var()
375 {
376 value = null;
377 //name = null; // Like hashKey in Jacl
378 ns = null;
379 hashKey = null; // Like hPtr in the C implementation
380 table = null; // Like hPtr in the C implementation
381 refCount = 0;
382 traces = null;
383 //search = null;
384 sidVec = null; // Like search in the C implementation
385 flags = ( VarFlags.SCALAR | VarFlags.UNDEFINED | VarFlags.IN_HASHTABLE );
386 }
387  
388 /// <summary> Used to create a String that describes this variable
389 ///
390 /// </summary>
391  
392 public override string ToString()
393 {
394 StringBuilder sb = new StringBuilder();
395 sb.Append( ns );
396 if ( sb.Length == 2 )
397 {
398 // It is in the global namespace
399 sb.Append( hashKey );
400 }
401 else
402 {
403 // It is not in the global namespaces
404 sb.Append( "::" );
405 sb.Append( hashKey );
406 }
407 return sb.ToString();
408 }
409  
410 /// <summary> Find the SearchId that in the sidVec Vector that is equal the
411 /// unique String s and returns the enumeration associated with
412 /// that SearchId.
413 ///
414 /// </summary>
415 /// <param name="s">String that ia a unique identifier for a SearchId object
416 /// </param>
417 /// <returns> Enumeration if a match is found else null.
418 /// </returns>
419  
420 protected internal SearchId getSearch( string s )
421 {
422 SearchId sid;
423 for ( int i = 0; i < sidVec.Count; i++ )
424 {
425 sid = (SearchId)sidVec[i];
426 if ( sid.equals( s ) )
427 {
428 return sid;
429 }
430 }
431 return null;
432 }
433  
434  
435 /// <summary> Find the SearchId object in the sidVec Vector and remove it.
436 ///
437 /// </summary>
438 /// <param name="sid">String that ia a unique identifier for a SearchId object.
439 /// </param>
440  
441 protected internal bool removeSearch( string sid )
442 {
443 SearchId curSid;
444  
445 for ( int i = 0; i < sidVec.Count; i++ )
446 {
447 curSid = (SearchId)sidVec[i];
448 if ( curSid.equals( sid ) )
449 {
450 sidVec.RemoveAt( i );
451 return true;
452 }
453 }
454 return false;
455 }
456  
457  
458  
459  
460  
461 // End of the instance method for the Var class, the rest of the methods
462 // are Var related methods ported from the code in generic/tclVar.c
463  
464  
465  
466 // The strings below are used to indicate what went wrong when a
467 // variable access is denied.
468  
469 internal const string noSuchVar = "no such variable";
470 internal const string isArray = "variable is array";
471 internal const string needArray = "variable isn't array";
472 internal const string noSuchElement = "no such element in array";
473 internal const string danglingElement = "upvar refers to element in deleted array";
474 internal const string danglingVar = "upvar refers to variable in deleted namespace";
475 internal const string badNamespace = "parent namespace doesn't exist";
476 internal const string missingName = "missing variable name";
477  
478  
479  
480 /// <summary> TclLookupVar -> lookupVar
481 ///
482 /// This procedure is used by virtually all of the variable
483 /// code to locate a variable given its name(s).
484 ///
485 /// </summary>
486 /// <param name="part1">if part2 isn't NULL, this is the name of an array.
487 /// Otherwise, this is a full variable name that could include
488 /// a parenthesized array elemnt or a scalar.
489 /// </param>
490 /// <param name="part2">Name of an element within array, or null.
491 /// </param>
492 /// <param name="flags">Only the TCL.VarFlag.GLOBAL_ONLY bit matters.
493 /// </param>
494 /// <param name="msg">Verb to use in error messages, e.g. "read" or "set".
495 /// </param>
496 /// <param name="create">OR'ed combination of CRT_PART1 and CRT_PART2.
497 /// Tells which entries to create if they don't already exist.
498 /// </param>
499 /// <param name="throwException">true if an exception should be throw if the
500 /// variable cannot be found.
501 /// </param>
502 /// <returns> a two element array. a[0] is the variable indicated by
503 /// part1 and part2, or null if the variable couldn't be
504 /// found and throwException is false.
505 /// <p>
506 /// If the variable is found, a[1] is the array that
507 /// contains the variable (or null if the variable is a scalar).
508 /// If the variable can't be found and either createPart1 or
509 /// createPart2 are true, a new as-yet-undefined (VAR_UNDEFINED)
510 /// variable instance is created, entered into a hash
511 /// table, and returned.
512 /// Note: it's possible that var.value of the returned variable
513 /// may be null (variable undefined), even if createPart1 or createPart2
514 /// are true (these only cause the hash table entry or array to be created).
515 /// For example, the variable might be a global that has been unset but
516 /// is still referenced by a procedure, or a variable that has been unset
517 /// but it only being kept in existence by a trace.
518 /// </returns>
519 /// <exception cref=""> TclException if the variable cannot be found and
520 /// throwException is true.
521 ///
522 /// </exception>
523  
524 internal static Var[] lookupVar( Interp interp, string part1, string part2, TCL.VarFlag flags, string msg, bool createPart1, bool createPart2 )
525 {
526 CallFrame varFrame = interp.varFrame;
527 // Reference to the procedure call frame whose
528 // variables are currently in use. Same as
529 // the current procedure's frame, if any,
530 // unless an "uplevel" is executing.
531 Hashtable table; // to the hashtable, if any, in which
532 // to look up the variable.
533 Var var; // Used to search for global names.
534 string elName; // Name of array element or null.
535 int openParen;
536 // If this procedure parses a name into
537 // array and index, these point to the
538 // parens around the index. Otherwise they
539 // are -1. These are needed to restore
540 // the parens after parsing the name.
541 NamespaceCmd.Namespace varNs, cxtNs;
542 int p;
543 int i, result;
544  
545 var = null;
546 openParen = -1;
547 varNs = null; // set non-null if a nonlocal variable
548  
549 // Parse part1 into array name and index.
550 // Always check if part1 is an array element name and allow it only if
551 // part2 is not given.
552 // (if one does not care about creating array elements that can't be used
553 // from tcl, and prefer slightly better performance, one can put
554 // the following in an if (part2 == null) { ... } block and remove
555 // the part2's test and error reporting or move that code in array set)
556 elName = part2;
557 int len = part1.Length;
558 for ( p = 0; p < len; p++ )
559 {
560 if ( part1[p] == '(' )
561 {
562 openParen = p;
563 p = len - 1;
564 if ( part1[p] == ')' )
565 {
566 if ( (System.Object)part2 != null )
567 {
568 if ( ( flags & TCL.VarFlag.LEAVE_ERR_MSG ) != 0 )
569 {
570 throw new TclVarException( interp, part1, part2, msg, needArray );
571 }
572 return null;
573 }
574 elName = part1.Substring( openParen + 1, ( len - 1 ) - ( openParen + 1 ) );
575 part2 = elName; // same as elName, only used in error reporting
576 part1 = part1.Substring( 0, ( openParen ) - ( 0 ) );
577 }
578 break;
579 }
580 }
581  
582  
583  
584 // If this namespace has a variable resolver, then give it first
585 // crack at the variable resolution. It may return a Var
586 // value, it may signal to continue onward, or it may signal
587 // an error.
588  
589 if ( ( ( flags & TCL.VarFlag.GLOBAL_ONLY ) != 0 ) || ( interp.varFrame == null ) )
590 {
591 cxtNs = interp.globalNs;
592 }
593 else
594 {
595 cxtNs = interp.varFrame.ns;
596 }
597  
598 if ( cxtNs.resolver != null || interp.resolvers != null )
599 {
600 try
601 {
602 if ( cxtNs.resolver != null )
603 {
604 var = cxtNs.resolver.resolveVar( interp, part1, cxtNs, flags );
605 }
606 else
607 {
608 var = null;
609 }
610  
611 if ( var == null && interp.resolvers != null )
612 {
613 IEnumerator enum_Renamed = interp.resolvers.GetEnumerator();
614 foreach ( Interp.ResolverScheme res in interp.resolvers )
615 {
616 var = res.resolver.resolveVar( interp, part1, cxtNs, flags );
617 if ( var != null )
618 break;
619 }
620 }
621 }
622 catch ( TclException e )
623 {
624 var = null;
625 }
626 }
627  
628 // Look up part1. Look it up as either a namespace variable or as a
629 // local variable in a procedure call frame (varFrame).
630 // Interpret part1 as a namespace variable if:
631 // 1) so requested by a TCL.VarFlag.GLOBAL_ONLY or TCL.VarFlag.NAMESPACE_ONLY flag,
632 // 2) there is no active frame (we're at the global :: scope),
633 // 3) the active frame was pushed to define the namespace context
634 // for a "namespace eval" or "namespace inscope" command,
635 // 4) the name has namespace qualifiers ("::"s).
636 // Otherwise, if part1 is a local variable, search first in the
637 // frame's array of compiler-allocated local variables, then in its
638 // hashtable for runtime-created local variables.
639 //
640 // If createPart1 and the variable isn't found, create the variable and,
641 // if necessary, create varFrame's local var hashtable.
642  
643 if ( ( ( flags & ( TCL.VarFlag.GLOBAL_ONLY | TCL.VarFlag.NAMESPACE_ONLY ) ) != 0 ) || ( varFrame == null ) || !varFrame.isProcCallFrame || ( part1.IndexOf( "::" ) != -1 ) )
644 {
645 string tail;
646  
647 // Don't pass TCL.VarFlag.LEAVE_ERR_MSG, we may yet create the variable,
648 // or otherwise generate our own error!
649  
650 var = NamespaceCmd.findNamespaceVar( interp, part1, null, flags & ~TCL.VarFlag.LEAVE_ERR_MSG );
651 if ( var == null )
652 {
653 if ( createPart1 )
654 {
655 // var wasn't found so create it
656  
657 // Java does not support passing an address so we pass
658 // an array of size 1 and then assign arr[0] to the value
659 NamespaceCmd.Namespace[] varNsArr = new NamespaceCmd.Namespace[1];
660 NamespaceCmd.Namespace[] dummyArr = new NamespaceCmd.Namespace[1];
661 string[] tailArr = new string[1];
662  
663 NamespaceCmd.getNamespaceForQualName( interp, part1, null, flags, varNsArr, dummyArr, dummyArr, tailArr );
664  
665 // Get the values out of the arrays!
666 varNs = varNsArr[0];
667 tail = tailArr[0];
668  
669 if ( varNs == null )
670 {
671 if ( ( flags & TCL.VarFlag.LEAVE_ERR_MSG ) != 0 )
672 {
673 throw new TclVarException( interp, part1, part2, msg, badNamespace );
674 }
675 return null;
676 }
677 if ( (System.Object)tail == null )
678 {
679 if ( ( flags & TCL.VarFlag.LEAVE_ERR_MSG ) != 0 )
680 {
681 throw new TclVarException( interp, part1, part2, msg, missingName );
682 }
683 return null;
684 }
685 var = new Var();
686 varNs.varTable.Add( tail, var );
687  
688 // There is no hPtr member in Jacl, The hPtr combines the table
689 // and the key used in a table lookup.
690 var.hashKey = tail;
691 var.table = varNs.varTable;
692  
693 var.ns = varNs;
694 }
695 else
696 {
697 // var wasn't found and not to create it
698 if ( ( flags & TCL.VarFlag.LEAVE_ERR_MSG ) != 0 )
699 {
700 throw new TclVarException( interp, part1, part2, msg, noSuchVar );
701 }
702 return null;
703 }
704 }
705 }
706 else
707 {
708 // local var: look in frame varFrame
709 // removed code block that searches for local compiled vars
710  
711 if ( var == null )
712 {
713 // look in the frame's var hash table
714 table = varFrame.varTable;
715 if ( createPart1 )
716 {
717 if ( table == null )
718 {
719 table = new Hashtable();
720 varFrame.varTable = table;
721 }
722 var = (Var)table[part1];
723 if ( var == null )
724 {
725 // we are adding a new entry
726 var = new Var();
727 SupportClass.PutElement( table, part1, var );
728  
729 // There is no hPtr member in Jacl, The hPtr combines
730 // the table and the key used in a table lookup.
731 var.hashKey = part1;
732 var.table = table;
733  
734 var.ns = null; // a local variable
735 }
736 }
737 else
738 {
739 if ( table != null )
740 {
741 var = (Var)table[part1];
742 }
743 if ( var == null )
744 {
745 if ( ( flags & TCL.VarFlag.LEAVE_ERR_MSG ) != 0 )
746 {
747 throw new TclVarException( interp, part1, part2, msg, noSuchVar );
748 }
749 return null;
750 }
751 }
752 }
753 }
754  
755 // If var is a link variable, we have a reference to some variable
756 // that was created through an "upvar" or "global" command. Traverse
757 // through any links until we find the referenced variable.
758  
759 while ( var.isVarLink() )
760 {
761 var = (Var)var.value;
762 }
763  
764 // If we're not dealing with an array element, return var.
765  
766 if ( (System.Object)elName == null )
767 {
768 var ret = new Var[2];
769 ret[0] = var;
770 ret[1] = null;
771 return ret;
772 }
773  
774 // We're dealing with an array element. Make sure the variable is an
775 // array and look up the element (create the element if desired).
776  
777 if ( var.isVarUndefined() && !var.isVarArrayElement() )
778 {
779 if ( !createPart1 )
780 {
781 if ( ( flags & TCL.VarFlag.LEAVE_ERR_MSG ) != 0 )
782 {
783 throw new TclVarException( interp, part1, part2, msg, noSuchVar );
784 }
785 return null;
786 }
787  
788 // Make sure we are not resurrecting a namespace variable from a
789 // deleted namespace!
790  
791 if ( ( ( var.flags & VarFlags.IN_HASHTABLE ) != 0 ) && ( var.table == null ) )
792 {
793 if ( ( flags & TCL.VarFlag.LEAVE_ERR_MSG ) != 0 )
794 {
795 throw new TclVarException( interp, part1, part2, msg, danglingVar );
796 }
797 return null;
798 }
799  
800 var.setVarArray();
801 var.clearVarUndefined();
802 var.value = new Hashtable();
803 }
804 else if ( !var.isVarArray() )
805 {
806 if ( ( flags & TCL.VarFlag.LEAVE_ERR_MSG ) != 0 )
807 {
808 throw new TclVarException( interp, part1, part2, msg, needArray );
809 }
810 return null;
811 }
812  
813 Var arrayVar = var;
814 Hashtable arrayTable = (Hashtable)var.value;
815 if ( createPart2 )
816 {
817 Var searchvar = (Var)arrayTable[elName];
818  
819 if ( searchvar == null )
820 {
821 // new entry
822 if ( var.sidVec != null )
823 {
824 deleteSearches( var );
825 }
826  
827 var = new Var();
828 SupportClass.PutElement( arrayTable, elName, var );
829  
830 // There is no hPtr member in Jacl, The hPtr combines the table
831 // and the key used in a table lookup.
832 var.hashKey = elName;
833 var.table = arrayTable;
834  
835 var.ns = varNs;
836 var.setVarArrayElement();
837 }
838 else
839 {
840 var = searchvar;
841 }
842 }
843 else
844 {
845 var = (Var)arrayTable[elName];
846 if ( var == null )
847 {
848 if ( ( flags & TCL.VarFlag.LEAVE_ERR_MSG ) != 0 )
849 {
850 throw new TclVarException( interp, part1, part2, msg, noSuchElement );
851 }
852 return null;
853 }
854 }
855  
856 var ret2 = new Var[2];
857 ret2[0] = var; // The Var in the array
858 ret2[1] = arrayVar; // The array (Hashtable) Var
859 return ret2;
860 }
861  
862  
863 /// <summary> Query the value of a variable whose name is stored in a Tcl object.
864 ///
865 /// </summary>
866 /// <param name="interp">the interp that holds the variable
867 /// </param>
868 /// <param name="nameObj">name of the variable.
869 /// </param>
870 /// <param name="flags">misc flags that control the actions of this method.
871 /// </param>
872 /// <returns> the value of the variable.
873 /// </returns>
874  
875 internal static TclObject getVar( Interp interp, TclObject nameObj, TCL.VarFlag flags )
876 {
877  
878 return getVar( interp, nameObj.ToString(), null, flags );
879 }
880  
881 /// <summary> Query the value of a variable.
882 ///
883 /// </summary>
884 /// <param name="interp">the interp that holds the variable
885 /// </param>
886 /// <param name="name">name of the variable.
887 /// </param>
888 /// <param name="flags">misc flags that control the actions of this method.
889 /// </param>
890 /// <returns> the value of the variable.
891 /// </returns>
892  
893 internal static TclObject getVar( Interp interp, string name, TCL.VarFlag flags )
894 {
895 return getVar( interp, name, null, flags );
896 }
897  
898 /// <summary> Tcl_ObjGetVar2 -> getVar
899 ///
900 /// Query the value of a variable.
901 ///
902 /// </summary>
903 /// <param name="interp">the interp that holds the variable
904 /// </param>
905 /// <param name="part1">1st part of the variable name.
906 /// </param>
907 /// <param name="part2">2nd part of the variable name.
908 /// </param>
909 /// <param name="flags">misc flags that control the actions of this method.
910 /// </param>
911 /// <returns> the value of the variable.
912 /// </returns>
913  
914 internal static TclObject getVar( Interp interp, TclObject part1Obj, TclObject part2Obj, TCL.VarFlag flags )
915 {
916 string part1, part2;
917  
918  
919 part1 = part1Obj.ToString();
920  
921 if ( part2Obj != null )
922 {
923  
924 part2 = part2Obj.ToString();
925 }
926 else
927 {
928 part2 = null;
929 }
930  
931 return getVar( interp, part1, part2, flags );
932 }
933  
934 /// <summary> TCL.Tcl_GetVar2Ex -> getVar
935 ///
936 /// Query the value of a variable, given a two-part name consisting
937 /// of array name and element within array.
938 ///
939 /// </summary>
940 /// <param name="interp">the interp that holds the variable
941 /// </param>
942 /// <param name="part1">1st part of the variable name.
943 /// </param>
944 /// <param name="part2">2nd part of the variable name.
945 /// </param>
946 /// <param name="flags">misc flags that control the actions of this method.
947 /// </param>
948 /// <returns> the value of the variable.
949 /// </returns>
950  
951 internal static TclObject getVar( Interp interp, string part1, string part2, TCL.VarFlag flags )
952 {
953 Var[] result = lookupVar( interp, part1, part2, flags, "read", false, true );
954  
955 if ( result == null )
956 {
957 // lookupVar() returns null only if TCL.VarFlag.LEAVE_ERR_MSG is
958 // not part of the flags argument, return null in this case.
959  
960 return null;
961 }
962  
963 Var var = result[0];
964 Var array = result[1];
965  
966 try
967 {
968 // Invoke any traces that have been set for the variable.
969  
970 if ( ( var.traces != null ) || ( ( array != null ) && ( array.traces != null ) ) )
971 {
972 string msg = callTraces( interp, array, var, part1, part2, ( flags & ( TCL.VarFlag.NAMESPACE_ONLY | TCL.VarFlag.GLOBAL_ONLY ) ) | TCL.VarFlag.TRACE_READS );
973 if ( (System.Object)msg != null )
974 {
975 if ( ( flags & TCL.VarFlag.LEAVE_ERR_MSG ) != 0 )
976 {
977 throw new TclVarException( interp, part1, part2, "read", msg );
978 }
979 return null;
980 }
981 }
982  
983 if ( var.isVarScalar() && !var.isVarUndefined() )
984 {
985 return (TclObject)var.value;
986 }
987  
988 if ( var.isSQLITE3_Link() )
989 return var.sqlite3_get();
990  
991 if ( ( flags & TCL.VarFlag.LEAVE_ERR_MSG ) != 0 )
992 {
993 string msg;
994 if ( var.isVarUndefined() && ( array != null ) && !array.isVarUndefined() )
995 {
996 msg = noSuchElement;
997 }
998 else if ( var.isVarArray() )
999 {
1000 msg = isArray;
1001 }
1002 else
1003 {
1004 msg = noSuchVar;
1005 }
1006 throw new TclVarException( interp, part1, part2, "read", msg );
1007 }
1008 }
1009 finally
1010 {
1011 // If the variable doesn't exist anymore and no-one's using it,
1012 // then free up the relevant structures and hash table entries.
1013  
1014 if ( var.isVarUndefined() )
1015 {
1016 cleanupVar( var, array );
1017 }
1018 }
1019  
1020 return null;
1021 }
1022  
1023 /// <summary> Set a variable whose name is stored in a Tcl object.
1024 ///
1025 /// </summary>
1026 /// <param name="interp">the interp that holds the variable
1027 /// </param>
1028 /// <param name="nameObj">name of the variable.
1029 /// </param>
1030 /// <param name="value">the new value for the variable
1031 /// </param>
1032 /// <param name="flags">misc flags that control the actions of this method.
1033 /// </param>
1034  
1035 internal static TclObject setVar( Interp interp, TclObject nameObj, TclObject value, TCL.VarFlag flags )
1036 {
1037  
1038 return setVar( interp, nameObj.ToString(), null, value, flags );
1039 }
1040  
1041 /// <summary> Set a variable.
1042 ///
1043 /// </summary>
1044 /// <param name="interp">the interp that holds the variable
1045 /// </param>
1046 /// <param name="name">name of the variable.
1047 /// </param>
1048 /// <param name="value">the new value for the variable
1049 /// </param>
1050 /// <param name="flags">misc flags that control the actions of this method
1051 /// </param>
1052  
1053 internal static TclObject setVar( Interp interp, string name, TclObject value, TCL.VarFlag flags )
1054 {
1055 return setVar( interp, name, null, value, flags );
1056 }
1057  
1058 /// <summary> Tcl_ObjSetVar2 -> setVar
1059 ///
1060 /// Set the value of a variable.
1061 ///
1062 /// </summary>
1063 /// <param name="interp">the interp that holds the variable
1064 /// </param>
1065 /// <param name="part1">1st part of the variable name.
1066 /// </param>
1067 /// <param name="part2">2nd part of the variable name.
1068 /// </param>
1069 /// <param name="newValue">the new value for the variable
1070 /// </param>
1071 /// <param name="flags">misc flags that control the actions of this method
1072 /// </param>
1073  
1074 internal static TclObject setVar( Interp interp, TclObject part1Obj, TclObject part2Obj, TclObject newValue, TCL.VarFlag flags )
1075 {
1076 string part1, part2;
1077  
1078  
1079 part1 = part1Obj.ToString();
1080  
1081 if ( part2Obj != null )
1082 {
1083  
1084 part2 = part2Obj.ToString();
1085 }
1086 else
1087 {
1088 part2 = null;
1089 }
1090  
1091 return setVar( interp, part1, part2, newValue, flags );
1092 }
1093  
1094  
1095 /// <summary> TCL.Tcl_SetVar2Ex -> setVar
1096 ///
1097 /// Given a two-part variable name, which may refer either to a scalar
1098 /// variable or an element of an array, change the value of the variable
1099 /// to a new Tcl object value. If the named scalar or array or element
1100 /// doesn't exist then create one.
1101 ///
1102 /// </summary>
1103 /// <param name="interp">the interp that holds the variable
1104 /// </param>
1105 /// <param name="part1">1st part of the variable name.
1106 /// </param>
1107 /// <param name="part2">2nd part of the variable name.
1108 /// </param>
1109 /// <param name="newValue">the new value for the variable
1110 /// </param>
1111 /// <param name="flags">misc flags that control the actions of this method
1112 ///
1113 /// Returns a pointer to the TclObject holding the new value of the
1114 /// variable. If the write operation was disallowed because an array was
1115 /// expected but not found (or vice versa), then null is returned; if
1116 /// the TCL.VarFlag.LEAVE_ERR_MSG flag is set, then an exception will be raised.
1117 /// Note that the returned object may not be the same one referenced
1118 /// by newValue because variable traces may modify the variable's value.
1119 /// The value of the given variable is set. If either the array or the
1120 /// entry didn't exist then a new variable is created.
1121 ///
1122 /// The reference count is decremented for any old value of the variable
1123 /// and incremented for its new value. If the new value for the variable
1124 /// is not the same one referenced by newValue (perhaps as a result
1125 /// of a variable trace), then newValue's ref count is left unchanged
1126 /// by TCL.Tcl_SetVar2Ex. newValue's ref count is also left unchanged if
1127 /// we are appending it as a string value: that is, if "flags" includes
1128 /// TCL.VarFlag.APPEND_VALUE but not TCL.VarFlag.LIST_ELEMENT.
1129 ///
1130 /// The reference count for the returned object is _not_ incremented: if
1131 /// you want to keep a reference to the object you must increment its
1132 /// ref count yourself.
1133 /// </param>
1134  
1135 internal static TclObject setVar( Interp interp, string part1, string part2, TclObject newValue, TCL.VarFlag flags )
1136 {
1137 Var var;
1138 Var array;
1139 TclObject oldValue;
1140 string bytes;
1141  
1142 Var[] result = lookupVar( interp, part1, part2, flags, "set", true, true );
1143 if ( result == null )
1144 {
1145 return null;
1146 }
1147  
1148 var = result[0];
1149 array = result[1];
1150  
1151 // If the variable is in a hashtable and its table field is null, then we
1152 // may have an upvar to an array element where the array was deleted
1153 // or an upvar to a namespace variable whose namespace was deleted.
1154 // Generate an error (allowing the variable to be reset would screw up
1155 // our storage allocation and is meaningless anyway).
1156  
1157 if ( ( ( var.flags & VarFlags.IN_HASHTABLE ) != 0 ) && ( var.table == null ) )
1158 {
1159 if ( ( flags & TCL.VarFlag.LEAVE_ERR_MSG ) != 0 )
1160 {
1161 if ( var.isVarArrayElement() )
1162 {
1163 throw new TclVarException( interp, part1, part2, "set", danglingElement );
1164 }
1165 else
1166 {
1167 throw new TclVarException( interp, part1, part2, "set", danglingVar );
1168 }
1169 }
1170 return null;
1171 }
1172  
1173 // It's an error to try to set an array variable itself.
1174  
1175 if ( var.isVarArray() && !var.isVarUndefined() )
1176 {
1177 if ( ( flags & TCL.VarFlag.LEAVE_ERR_MSG ) != 0 )
1178 {
1179 throw new TclVarException( interp, part1, part2, "set", isArray );
1180 }
1181 return null;
1182 }
1183  
1184  
1185 // At this point, if we were appending, we used to call read traces: we
1186 // treated append as a read-modify-write. However, it seemed unlikely to
1187 // us that a real program would be interested in such reads being done
1188 // during a set operation.
1189  
1190 // Set the variable's new value. If appending, append the new value to
1191 // the variable, either as a list element or as a string. Also, if
1192 // appending, then if the variable's old value is unshared we can modify
1193 // it directly, otherwise we must create a new copy to modify: this is
1194 // "copy on write".
1195  
1196 try
1197 {
1198 if ( var.isSQLITE3_Link() )
1199 {
1200 var.sqlite3_set( newValue );
1201 return var.sqlite3_get();
1202 }
1203 else
1204 {
1205 oldValue = (TclObject)var.value;
1206  
1207 if ( ( flags & TCL.VarFlag.APPEND_VALUE ) != 0 )
1208 {
1209 if ( var.isVarUndefined() && ( oldValue != null ) )
1210 {
1211 oldValue.release(); // discard old value
1212 var.value = null;
1213 oldValue = null;
1214 }
1215 if ( ( flags & TCL.VarFlag.LIST_ELEMENT ) != 0 )
1216 {
1217 // append list element
1218 if ( oldValue == null )
1219 {
1220 oldValue = TclList.newInstance();
1221 var.value = oldValue;
1222 oldValue.preserve(); // since var is referenced
1223 }
1224 else if ( oldValue.Shared )
1225 {
1226 // append to copy
1227 var.value = oldValue.duplicate();
1228 oldValue.release();
1229 oldValue = (TclObject)var.value;
1230 oldValue.preserve(); // since var is referenced
1231 }
1232 TclList.append( interp, oldValue, newValue );
1233 }
1234 else
1235 {
1236 // append string
1237 // We append newValuePtr's bytes but don't change its ref count.
1238  
1239  
1240 bytes = newValue.ToString();
1241 if ( oldValue == null )
1242 {
1243 var.value = TclString.newInstance( bytes );
1244 ( (TclObject)var.value ).preserve();
1245 }
1246 else
1247 {
1248 if ( oldValue.Shared )
1249 {
1250 // append to copy
1251 var.value = oldValue.duplicate();
1252 oldValue.release();
1253 oldValue = (TclObject)var.value;
1254 oldValue.preserve(); // since var is referenced
1255 }
1256 TclString.append( oldValue, newValue );
1257 }
1258 }
1259 }
1260 else
1261 {
1262 if ( ( flags & TCL.VarFlag.LIST_ELEMENT ) != 0 )
1263 {
1264 // set var to list element
1265 int listFlags;
1266  
1267 // We set the variable to the result of converting newValue's
1268 // string rep to a list element. We do not change newValue's
1269 // ref count.
1270  
1271 if ( oldValue != null )
1272 {
1273 oldValue.release(); // discard old value
1274 }
1275  
1276 bytes = newValue.ToString();
1277 listFlags = Util.scanElement( interp, bytes );
1278 oldValue = TclString.newInstance( Util.convertElement( bytes, listFlags ) );
1279 var.value = oldValue;
1280 ( (TclObject)var.value ).preserve();
1281 }
1282 else if ( newValue != oldValue )
1283 {
1284 var.value = newValue.duplicate();
1285 ( (TclObject)var.value ).preserve(); // var is another ref
1286 if ( oldValue != null )
1287 {
1288 oldValue.release(); // discard old value
1289 }
1290 }
1291 }
1292 var.setVarScalar();
1293 var.clearVarUndefined();
1294 if ( array != null )
1295 {
1296 array.clearVarUndefined();
1297 }
1298  
1299 // Invoke any write traces for the variable.
1300  
1301 if ( ( var.traces != null ) || ( ( array != null ) && ( array.traces != null ) ) )
1302 {
1303  
1304 string msg = callTraces( interp, array, var, part1, part2, ( flags & ( TCL.VarFlag.GLOBAL_ONLY | TCL.VarFlag.NAMESPACE_ONLY ) ) | TCL.VarFlag.TRACE_WRITES );
1305 if ( (System.Object)msg != null )
1306 {
1307 if ( ( flags & TCL.VarFlag.LEAVE_ERR_MSG ) != 0 )
1308 {
1309 throw new TclVarException( interp, part1, part2, "set", msg );
1310 }
1311 return null; // Same as "goto cleanup" in C verison
1312 }
1313 }
1314  
1315 // Return the variable's value unless the variable was changed in some
1316 // gross way by a trace (e.g. it was unset and then recreated as an
1317 // array).
1318  
1319 if ( var.isVarScalar() && !var.isVarUndefined() )
1320 {
1321 return (TclObject)var.value;
1322 }
1323  
1324 // A trace changed the value in some gross way. Return an empty string
1325 // object.
1326  
1327 return TclString.newInstance( "" );
1328 }
1329 }
1330 finally
1331 {
1332 // If the variable doesn't exist anymore and no-one's using it,
1333 // then free up the relevant structures and hash table entries.
1334  
1335 if ( var.isVarUndefined() )
1336 {
1337 cleanupVar( var, array );
1338 }
1339 }
1340 }
1341  
1342  
1343 /// <summary> TclIncrVar2 -> incrVar
1344 ///
1345 /// Given a two-part variable name, which may refer either to a scalar
1346 /// variable or an element of an array, increment the Tcl object value
1347 /// of the variable by a specified amount.
1348 ///
1349 /// </summary>
1350 /// <param name="part1">1st part of the variable name.
1351 /// </param>
1352 /// <param name="part2">2nd part of the variable name.
1353 /// </param>
1354 /// <param name="incrAmount">Amount to be added to variable.
1355 /// </param>
1356 /// <param name="flags">misc flags that control the actions of this method
1357 ///
1358 /// Results:
1359 /// Returns a reference to the TclObject holding the new value of the
1360 /// variable. If the specified variable doesn't exist, or there is a
1361 /// clash in array usage, or an error occurs while executing variable
1362 /// traces, then a TclException will be raised.
1363 ///
1364 /// Side effects:
1365 /// The value of the given variable is incremented by the specified
1366 /// amount. If either the array or the entry didn't exist then a new
1367 /// variable is created. The ref count for the returned object is _not_
1368 /// incremented to reflect the returned reference; if you want to keep a
1369 /// reference to the object you must increment its ref count yourself.
1370 ///
1371 /// ----------------------------------------------------------------------
1372 /// </param>
1373  
1374 internal static TclObject incrVar( Interp interp, TclObject part1, TclObject part2, int incrAmount, TCL.VarFlag flags )
1375 {
1376 TclObject varValue = null;
1377 bool createdNewObj; // Set to true if var's value object is shared
1378 // so we must increment a copy (i.e. copy
1379 // on write).
1380 int i;
1381 bool err;
1382  
1383 // There are two possible error conditions that depend on the setting of
1384 // TCL.VarFlag.LEAVE_ERR_MSG. an exception could be raised or null could be returned
1385 err = false;
1386 try
1387 {
1388 varValue = getVar( interp, part1, part2, flags );
1389 }
1390 catch ( TclException e )
1391 {
1392 err = true;
1393 throw;
1394 }
1395 finally
1396 {
1397 // FIXME : is this the correct way to catch the error?
1398 if ( err || varValue == null )
1399 interp.addErrorInfo( "\n (reading value of variable to increment)" );
1400 }
1401  
1402  
1403 // Increment the variable's value. If the object is unshared we can
1404 // modify it directly, otherwise we must create a new copy to modify:
1405 // this is "copy on write". Then free the variable's old string
1406 // representation, if any, since it will no longer be valid.
1407  
1408 createdNewObj = false;
1409 if ( varValue.Shared )
1410 {
1411 varValue = varValue.duplicate();
1412 createdNewObj = true;
1413 }
1414  
1415 try
1416 {
1417 i = TclInteger.get( interp, varValue );
1418 }
1419 catch ( TclException e )
1420 {
1421 if ( createdNewObj )
1422 {
1423 varValue.release(); // free unneeded copy
1424 }
1425 throw;
1426 }
1427  
1428 TclInteger.set( varValue, ( i + incrAmount ) );
1429  
1430 // Store the variable's new value and run any write traces.
1431  
1432 return setVar( interp, part1, part2, varValue, flags );
1433 }
1434  
1435 /// <summary> Unset a variable whose name is stored in a Tcl object.
1436 ///
1437 /// </summary>
1438 /// <param name="nameObj">name of the variable.
1439 /// </param>
1440 /// <param name="flags">misc flags that control the actions of this method.
1441 /// </param>
1442  
1443 internal static void unsetVar( Interp interp, TclObject nameObj, TCL.VarFlag flags )
1444 {
1445  
1446 unsetVar( interp, nameObj.ToString(), null, flags );
1447 }
1448  
1449 /// <summary> Unset a variable.
1450 ///
1451 /// </summary>
1452 /// <param name="name">name of the variable.
1453 /// </param>
1454 /// <param name="flags">misc flags that control the actions of this method.
1455 /// </param>
1456  
1457 internal static void unsetVar( Interp interp, string name, TCL.VarFlag flags )
1458 {
1459 unsetVar( interp, name, null, flags );
1460 }
1461  
1462 /// <summary> TCL.Tcl_UnsetVar2 -> unsetVar
1463 ///
1464 /// Unset a variable, given a two-part name consisting of array
1465 /// name and element within array.
1466 ///
1467 /// </summary>
1468 /// <param name="part1">1st part of the variable name.
1469 /// </param>
1470 /// <param name="part2">2nd part of the variable name.
1471 /// </param>
1472 /// <param name="flags">misc flags that control the actions of this method.
1473 ///
1474 /// If part1 and part2 indicate a local or global variable in interp,
1475 /// it is deleted. If part1 is an array name and part2 is null, then
1476 /// the whole array is deleted.
1477 ///
1478 /// </param>
1479  
1480 internal static void unsetVar( Interp interp, string part1, string part2, TCL.VarFlag flags )
1481 {
1482 Var dummyVar;
1483 Var var;
1484 Var array;
1485 //ActiveVarTrace active;
1486 TclObject obj;
1487 TCL.CompletionCode result;
1488  
1489 // FIXME : what about the null return vs exception thing here?
1490 Var[] lookup_result = lookupVar( interp, part1, part2, flags, "unset", false, false );
1491 if ( lookup_result == null )
1492 {
1493 if ( ( flags & TCL.VarFlag.LEAVE_ERR_MSG ) != 0 )
1494 throw new TclRuntimeError( "unexpected null reference" );
1495 else
1496 return;
1497 }
1498  
1499 var = lookup_result[0];
1500 array = lookup_result[1];
1501  
1502 result = ( var.isVarUndefined() ? TCL.CompletionCode.ERROR : TCL.CompletionCode.OK );
1503  
1504 if ( ( array != null ) && ( array.sidVec != null ) )
1505 {
1506 deleteSearches( array );
1507 }
1508  
1509  
1510 // The code below is tricky, because of the possibility that
1511 // a trace procedure might try to access a variable being
1512 // deleted. To handle this situation gracefully, do things
1513 // in three steps:
1514 // 1. Copy the contents of the variable to a dummy variable
1515 // structure, and mark the original Var structure as undefined.
1516 // 2. Invoke traces and clean up the variable, using the dummy copy.
1517 // 3. If at the end of this the original variable is still
1518 // undefined and has no outstanding references, then delete
1519 // it (but it could have gotten recreated by a trace).
1520  
1521 dummyVar = new Var();
1522 //FIXME: Var class really should implement clone to make a bit copy.
1523 dummyVar.value = var.value;
1524 dummyVar.traces = var.traces;
1525 dummyVar.flags = var.flags;
1526 dummyVar.hashKey = var.hashKey;
1527 dummyVar.table = var.table;
1528 dummyVar.refCount = var.refCount;
1529 dummyVar.ns = var.ns;
1530  
1531 var.setVarUndefined();
1532 var.setVarScalar();
1533 var.value = null; // dummyVar points to any value object
1534 var.traces = null;
1535 var.sidVec = null;
1536  
1537 // Call trace procedures for the variable being deleted. Then delete
1538 // its traces. Be sure to abort any other traces for the variable
1539 // that are still pending. Special tricks:
1540 // 1. We need to increment var's refCount around this: CallTraces
1541 // will use dummyVar so it won't increment var's refCount itself.
1542 // 2. Turn off the TRACE_ACTIVE flag in dummyVar: we want to
1543 // call unset traces even if other traces are pending.
1544  
1545 if ( ( dummyVar.traces != null ) || ( ( array != null ) && ( array.traces != null ) ) )
1546 {
1547 var.refCount++;
1548 dummyVar.flags &= ~VarFlags.TRACE_ACTIVE;
1549 callTraces( interp, array, dummyVar, part1, part2, ( flags & ( TCL.VarFlag.GLOBAL_ONLY | TCL.VarFlag.NAMESPACE_ONLY ) ) | TCL.VarFlag.TRACE_UNSETS );
1550  
1551 dummyVar.traces = null;
1552  
1553 // Active trace stuff is not part of Jacl's interp
1554  
1555 var.refCount--;
1556 }
1557  
1558 // If the variable is an array, delete all of its elements. This must be
1559 // done after calling the traces on the array, above (that's the way
1560 // traces are defined). If it is a scalar, "discard" its object
1561 // (decrement the ref count of its object, if any).
1562  
1563 if ( dummyVar.isVarArray() && !dummyVar.isVarUndefined() )
1564 {
1565 deleteArray( interp, part1, dummyVar, ( flags & ( TCL.VarFlag.GLOBAL_ONLY | TCL.VarFlag.NAMESPACE_ONLY ) ) | TCL.VarFlag.TRACE_UNSETS );
1566 }
1567 if ( dummyVar.isVarScalar() && ( dummyVar.value != null ) )
1568 {
1569 obj = (TclObject)dummyVar.value;
1570 obj.release();
1571 dummyVar.value = null;
1572 }
1573  
1574 // If the variable was a namespace variable, decrement its reference count.
1575  
1576 if ( ( var.flags & VarFlags.NAMESPACE_VAR ) != 0 )
1577 {
1578 var.flags &= ~VarFlags.NAMESPACE_VAR;
1579 var.refCount--;
1580 }
1581  
1582 // Finally, if the variable is truly not in use then free up its Var
1583 // structure and remove it from its hash table, if any. The ref count of
1584 // its value object, if any, was decremented above.
1585  
1586 cleanupVar( var, array );
1587  
1588 // It's an error to unset an undefined variable.
1589  
1590 if ( result != TCL.CompletionCode.OK )
1591 {
1592 if ( ( flags & TCL.VarFlag.LEAVE_ERR_MSG ) != 0 )
1593 {
1594 throw new TclVarException( interp, part1, part2, "unset", ( ( array == null ) ? noSuchVar : noSuchElement ) );
1595 }
1596 }
1597 }
1598  
1599  
1600 /// <summary> Trace a variable whose name is stored in a Tcl object.
1601 ///
1602 /// </summary>
1603 /// <param name="nameObj">name of the variable.
1604 /// </param>
1605 /// <param name="trace">the trace to add.
1606 /// </param>
1607 /// <param name="flags">misc flags that control the actions of this method.
1608 /// </param>
1609  
1610 internal static void traceVar( Interp interp, TclObject nameObj, TCL.VarFlag flags, VarTrace proc )
1611 {
1612  
1613 traceVar( interp, nameObj.ToString(), null, flags, proc );
1614 }
1615  
1616 /// <summary> Trace a variable.
1617 ///
1618 /// </summary>
1619 /// <param name="name">name of the variable.
1620 /// </param>
1621 /// <param name="trace">the trace to add.
1622 /// </param>
1623 /// <param name="flags">misc flags that control the actions of this method.
1624 /// </param>
1625  
1626 internal static void traceVar( Interp interp, string name, TCL.VarFlag flags, VarTrace proc )
1627 {
1628 traceVar( interp, name, null, flags, proc );
1629 }
1630  
1631 /// <summary> TCL.Tcl_TraceVar2 -> traceVar
1632 ///
1633 /// Trace a variable, given a two-part name consisting of array
1634 /// name and element within array.
1635 ///
1636 /// </summary>
1637 /// <param name="part1">1st part of the variable name.
1638 /// </param>
1639 /// <param name="part2">2nd part of the variable name.
1640 /// </param>
1641 /// <param name="flags">misc flags that control the actions of this method.
1642 /// </param>
1643 /// <param name="trace">the trace to comand to add.
1644 /// </param>
1645  
1646 internal static void traceVar( Interp interp, string part1, string part2, TCL.VarFlag flags, VarTrace proc )
1647 {
1648 Var[] result;
1649 Var var, array;
1650  
1651 // FIXME: what about the exception problem here?
1652 result = lookupVar( interp, part1, part2, ( flags | TCL.VarFlag.LEAVE_ERR_MSG ), "trace", true, true );
1653 if ( result == null )
1654 {
1655 throw new TclException( interp, "" );
1656 }
1657  
1658 var = result[0];
1659 array = result[1];
1660  
1661 // Set up trace information.
1662  
1663 if ( var.traces == null )
1664 {
1665 var.traces = new ArrayList( 10 );
1666 }
1667  
1668 var rec = new TraceRecord();
1669 rec.trace = proc;
1670 rec.flags = flags & ( TCL.VarFlag.TRACE_READS | TCL.VarFlag.TRACE_WRITES | TCL.VarFlag.TRACE_UNSETS | TCL.VarFlag.TRACE_ARRAY );
1671  
1672 var.traces.Insert( 0, rec );
1673  
1674  
1675 // FIXME: is this needed ?? It was in Jacl but not 8.1
1676  
1677 /*
1678 // When inserting a trace for an array on an UNDEFINED variable,
1679 // the search IDs for that array are reset.
1680  
1681 if (array != null && var.isVarUndefined()) {
1682 array.sidVec = null;
1683 }
1684 */
1685 }
1686  
1687  
1688 /// <summary> Untrace a variable whose name is stored in a Tcl object.
1689 ///
1690 /// </summary>
1691 /// <param name="nameObj">name of the variable.
1692 /// </param>
1693 /// <param name="trace">the trace to delete.
1694 /// </param>
1695 /// <param name="flags">misc flags that control the actions of this method.
1696 /// </param>
1697  
1698 internal static void untraceVar( Interp interp, TclObject nameObj, TCL.VarFlag flags, VarTrace proc )
1699 {
1700  
1701 untraceVar( interp, nameObj.ToString(), null, flags, proc );
1702 }
1703  
1704 /// <summary> Untrace a variable.
1705 ///
1706 /// </summary>
1707 /// <param name="name">name of the variable.
1708 /// </param>
1709 /// <param name="trace">the trace to delete.
1710 /// </param>
1711 /// <param name="flags">misc flags that control the actions of this method.
1712 /// </param>
1713  
1714 internal static void untraceVar( Interp interp, string name, TCL.VarFlag flags, VarTrace proc )
1715 {
1716 untraceVar( interp, name, null, flags, proc );
1717 }
1718  
1719 /// <summary> TCL.Tcl_UntraceVar2 -> untraceVar
1720 ///
1721 /// Untrace a variable, given a two-part name consisting of array
1722 /// name and element within array. This will Remove a
1723 /// previously-created trace for a variable.
1724 ///
1725 /// </summary>
1726 /// <param name="interp">Interpreter containing variable.
1727 /// </param>
1728 /// <param name="part1">1st part of the variable name.
1729 /// </param>
1730 /// <param name="part2">2nd part of the variable name.
1731 /// </param>
1732 /// <param name="flags">misc flags that control the actions of this method.
1733 /// </param>
1734 /// <param name="proc">the trace to delete.
1735 /// </param>
1736  
1737 internal static void untraceVar( Interp interp, string part1, string part2, TCL.VarFlag flags, VarTrace proc )
1738 {
1739 Var[] result = null;
1740 Var var;
1741  
1742 try
1743 {
1744 result = lookupVar( interp, part1, part2, flags & ( TCL.VarFlag.GLOBAL_ONLY | TCL.VarFlag.NAMESPACE_ONLY ), null, false, false );
1745 if ( result == null )
1746 {
1747 return;
1748 }
1749 }
1750 catch ( TclException e )
1751 {
1752 // FIXME: check for problems in exception in lookupVar
1753  
1754 // We have set throwException argument to false in the
1755 // lookupVar() call, so an exception should never be
1756 // thrown.
1757  
1758 throw new TclRuntimeError( "unexpected TclException: " + e.Message, e );
1759 }
1760  
1761 var = result[0];
1762  
1763 if ( var.traces != null )
1764 {
1765 int len = var.traces.Count;
1766 for ( int i = 0; i < len; i++ )
1767 {
1768 TraceRecord rec = (TraceRecord)var.traces[i];
1769 if ( rec.trace == proc )
1770 {
1771 var.traces.RemoveAt( i );
1772 break;
1773 }
1774 }
1775 }
1776  
1777 // If this is the last trace on the variable, and the variable is
1778 // unset and unused, then free up the variable.
1779  
1780 if ( var.isVarUndefined() )
1781 {
1782 cleanupVar( var, null );
1783 }
1784 }
1785  
1786 /// <summary> TCL.Tcl_VarTraceInfo -> getTraces
1787 ///
1788 /// </summary>
1789 /// <param name="interp">Interpreter containing variable.
1790 /// </param>
1791 /// <param name="name">name of the variable.
1792 /// </param>
1793 /// <param name="flags">flags that control the actions of this method.
1794 /// </param>
1795 /// <returns> the Vector of traces of a variable.
1796 /// </returns>
1797  
1798 static protected internal ArrayList getTraces( Interp interp, string name, TCL.VarFlag flags )
1799 {
1800 return getTraces( interp, name, null, flags );
1801 }
1802  
1803 /// <summary> TCL.Tcl_VarTraceInfo2 -> getTraces
1804 ///
1805 /// </summary>
1806 /// <returns> the list of traces of a variable.
1807 ///
1808 /// </returns>
1809 /// <param name="interp">Interpreter containing variable.
1810 /// </param>
1811 /// <param name="part1">1st part of the variable name.
1812 /// </param>
1813 /// <param name="part2">2nd part of the variable name (can be null).
1814 /// </param>
1815 /// <param name="flags">misc flags that control the actions of this method.
1816 /// </param>
1817  
1818 static protected internal ArrayList getTraces( Interp interp, string part1, string part2, TCL.VarFlag flags )
1819 {
1820 Var[] result;
1821  
1822 result = lookupVar( interp, part1, part2, flags & ( TCL.VarFlag.GLOBAL_ONLY | TCL.VarFlag.NAMESPACE_ONLY ), null, false, false );
1823  
1824 if ( result == null )
1825 {
1826 return null;
1827 }
1828  
1829 return result[0].traces;
1830 }
1831  
1832  
1833 /// <summary> MakeUpvar -> makeUpvar
1834 ///
1835 /// Create a reference of a variable in otherFrame in the current
1836 /// CallFrame, given a two-part name consisting of array name and
1837 /// element within array.
1838 ///
1839 /// </summary>
1840 /// <param name="interp">Interp containing the variables
1841 /// </param>
1842 /// <param name="frame">CallFrame containing "other" variable.
1843 /// null means use global context.
1844 /// </param>
1845 /// <param name="otherP1">the 1st part name of the variable in the "other" frame.
1846 /// </param>
1847 /// <param name="otherP2">the 2nd part name of the variable in the "other" frame.
1848 /// </param>
1849 /// <param name="otherFlags">the flags for scaope of "other" variable
1850 /// </param>
1851 /// <param name="myName">Name of scalar variable which will refer to otherP1/otherP2.
1852 /// </param>
1853 /// <param name="myFlags">only the TCL.VarFlag.GLOBAL_ONLY bit matters,
1854 /// indicating the scope of myName.
1855 /// </param>
1856 /// <exception cref=""> TclException if the upvar cannot be created.
1857 /// </exception>
1858  
1859 protected internal static void makeUpvar( Interp interp, CallFrame frame, string otherP1, string otherP2, TCL.VarFlag otherFlags, string myName, TCL.VarFlag myFlags )
1860 {
1861 Var other, var, array;
1862 Var[] result;
1863 CallFrame varFrame;
1864 CallFrame savedFrame = null;
1865 Hashtable table;
1866 NamespaceCmd.Namespace ns, altNs;
1867 string tail;
1868 bool newvar = false;
1869  
1870 // Find "other" in "frame". If not looking up other in just the
1871 // current namespace, temporarily replace the current var frame
1872 // pointer in the interpreter in order to use TclLookupVar.
1873  
1874 if ( ( otherFlags & TCL.VarFlag.NAMESPACE_ONLY ) == 0 )
1875 {
1876 savedFrame = interp.varFrame;
1877 interp.varFrame = frame;
1878 }
1879 result = lookupVar( interp, otherP1, otherP2, ( otherFlags | TCL.VarFlag.LEAVE_ERR_MSG ), "access", true, true );
1880  
1881 if ( ( otherFlags & TCL.VarFlag.NAMESPACE_ONLY ) == 0 )
1882 {
1883 interp.varFrame = savedFrame;
1884 }
1885  
1886 other = result[0];
1887 array = result[1];
1888  
1889 if ( other == null )
1890 {
1891 // FIXME : leave error message thing again
1892 throw new TclRuntimeError( "unexpected null reference" );
1893 }
1894  
1895 // Now create a hashtable entry for "myName". Create it as either a
1896 // namespace variable or as a local variable in a procedure call
1897 // frame. Interpret myName as a namespace variable if:
1898 // 1) so requested by a TCL.VarFlag.GLOBAL_ONLY or TCL.VarFlag.NAMESPACE_ONLY flag,
1899 // 2) there is no active frame (we're at the global :: scope),
1900 // 3) the active frame was pushed to define the namespace context
1901 // for a "namespace eval" or "namespace inscope" command,
1902 // 4) the name has namespace qualifiers ("::"s).
1903 // If creating myName in the active procedure, look in its
1904 // hashtable for runtime-created local variables. Create that
1905 // procedure's local variable hashtable if necessary.
1906  
1907 varFrame = interp.varFrame;
1908 if ( ( ( myFlags & ( TCL.VarFlag.GLOBAL_ONLY | TCL.VarFlag.NAMESPACE_ONLY ) ) != 0 ) || ( varFrame == null ) || !varFrame.isProcCallFrame || ( myName.IndexOf( "::" ) != -1 ) )
1909 {
1910  
1911 // Java does not support passing an address so we pass
1912 // an array of size 1 and then assign arr[0] to the value
1913 NamespaceCmd.Namespace[] nsArr = new NamespaceCmd.Namespace[1];
1914 NamespaceCmd.Namespace[] altNsArr = new NamespaceCmd.Namespace[1];
1915 NamespaceCmd.Namespace[] dummyNsArr = new NamespaceCmd.Namespace[1];
1916 string[] tailArr = new string[1];
1917  
1918 NamespaceCmd.getNamespaceForQualName( interp, myName, null, myFlags, nsArr, altNsArr, dummyNsArr, tailArr );
1919  
1920 // Get the values out of the arrays!
1921 ns = nsArr[0];
1922 altNs = altNsArr[0];
1923 tail = tailArr[0];
1924  
1925 if ( ns == null )
1926 {
1927 ns = altNs;
1928 }
1929 if ( ns == null )
1930 {
1931 throw new TclException( interp, "bad variable name \"" + myName + "\": unknown namespace" );
1932 }
1933  
1934 // Check that we are not trying to create a namespace var linked to
1935 // a local variable in a procedure. If we allowed this, the local
1936 // variable in the shorter-lived procedure frame could go away
1937 // leaving the namespace var's reference invalid.
1938  
1939 if ( ( ( (System.Object)otherP2 != null ) ? array.ns : other.ns ) == null )
1940 {
1941 throw new TclException( interp, "bad variable name \"" + myName + "\": upvar won't create namespace variable that refers to procedure variable" );
1942 }
1943  
1944 // AKT var = (Var) ns.varTable.get(tail);
1945 var = (Var)ns.varTable[tail];
1946 if ( var == null )
1947 {
1948 // we are adding a new entry
1949 newvar = true;
1950 var = new Var();
1951 // ATK ns.varTable.put(tail, var);
1952 ns.varTable.Add( tail, var );
1953  
1954 // There is no hPtr member in Jacl, The hPtr combines the table
1955 // and the key used in a table lookup.
1956 var.hashKey = tail;
1957 var.table = ns.varTable;
1958  
1959 var.ns = ns;
1960 }
1961 }
1962 else
1963 {
1964 // Skip Compiled Local stuff
1965 var = null;
1966 if ( var == null )
1967 {
1968 // look in frame's local var hashtable
1969 table = varFrame.varTable;
1970 if ( table == null )
1971 {
1972 table = new Hashtable();
1973 varFrame.varTable = table;
1974 }
1975  
1976 var = (Var)table[myName];
1977 if ( var == null )
1978 {
1979 // we are adding a new entry
1980 newvar = true;
1981 var = new Var();
1982 SupportClass.PutElement( table, myName, var );
1983  
1984 // There is no hPtr member in Jacl, The hPtr combines the table
1985 // and the key used in a table lookup.
1986 var.hashKey = myName;
1987 var.table = table;
1988  
1989 var.ns = varFrame.ns;
1990 }
1991 }
1992 }
1993  
1994 if ( !newvar )
1995 {
1996 // The variable already exists. Make sure this variable "var"
1997 // isn't the same as "other" (avoid circular links). Also, if
1998 // it's not an upvar then it's an error. If it is an upvar, then
1999 // just disconnect it from the thing it currently refers to.
2000  
2001 if ( var == other )
2002 {
2003 throw new TclException( interp, "can't upvar from variable to itself" );
2004 }
2005 if ( var.isVarLink() )
2006 {
2007 Var link = (Var)var.value;
2008 if ( link == other )
2009 {
2010 return;
2011 }
2012 link.refCount--;
2013 if ( link.isVarUndefined() )
2014 {
2015 cleanupVar( link, null );
2016 }
2017 }
2018 else if ( !var.isVarUndefined() )
2019 {
2020 throw new TclException( interp, "variable \"" + myName + "\" already exists" );
2021 }
2022 else if ( var.traces != null )
2023 {
2024 throw new TclException( interp, "variable \"" + myName + "\" has traces: can't use for upvar" );
2025 }
2026 }
2027  
2028 var.setVarLink();
2029 var.clearVarUndefined();
2030 var.value = other;
2031 other.refCount++;
2032 return;
2033 }
2034  
2035 /*
2036 *----------------------------------------------------------------------
2037 *
2038 * TCL.Tcl_GetVariableFullName -> getVariableFullName
2039 *
2040 * Given a Var token returned by NamespaceCmd.FindNamespaceVar, this
2041 * procedure appends to an object the namespace variable's full
2042 * name, qualified by a sequence of parent namespace names.
2043 *
2044 * Results:
2045 * None.
2046 *
2047 * Side effects:
2048 * The variable's fully-qualified name is returned.
2049 *
2050 *----------------------------------------------------------------------
2051 */
2052  
2053 internal static string getVariableFullName( Interp interp, Var var )
2054 {
2055 StringBuilder buff = new StringBuilder();
2056  
2057 // Add the full name of the containing namespace (if any), followed by
2058 // the "::" separator, then the variable name.
2059  
2060 if ( var != null )
2061 {
2062 if ( !var.isVarArrayElement() )
2063 {
2064 if ( var.ns != null )
2065 {
2066 buff.Append( var.ns.fullName );
2067 if ( var.ns != interp.globalNs )
2068 {
2069 buff.Append( "::" );
2070 }
2071 }
2072 // Jacl's Var class does not include the "name" member
2073 // We use the "hashKey" member which is equivalent
2074  
2075 if ( (System.Object)var.hashKey != null )
2076 {
2077 buff.Append( var.hashKey );
2078 }
2079 }
2080 }
2081  
2082 return buff.ToString();
2083 }
2084  
2085 /// <summary> CallTraces -> callTraces
2086 ///
2087 /// This procedure is invoked to find and invoke relevant
2088 /// trace procedures associated with a particular operation on
2089 /// a variable. This procedure invokes traces both on the
2090 /// variable and on its containing array (where relevant).
2091 ///
2092 /// </summary>
2093 /// <param name="interp">Interpreter containing variable.
2094 /// </param>
2095 /// <param name="array">array variable that contains the variable, or null
2096 /// if the variable isn't an element of an array.
2097 /// </param>
2098 /// <param name="var">Variable whose traces are to be invoked.
2099 /// </param>
2100 /// <param name="part1">the first part of a variable name.
2101 /// </param>
2102 /// <param name="part2">the second part of a variable name.
2103 /// </param>
2104 /// <param name="flags">Flags to pass to trace procedures: indicates
2105 /// what's happening to variable, plus other stuff like
2106 /// TCL.VarFlag.GLOBAL_ONLY, TCL.VarFlag.NAMESPACE_ONLY, and TCL.VarFlag.INTERP_DESTROYED.
2107 /// </param>
2108 /// <returns> null if no trace procedures were invoked, or
2109 /// if all the invoked trace procedures returned successfully.
2110 /// The return value is non-null if a trace procedure returned an
2111 /// error (in this case no more trace procedures were invoked
2112 /// after the error was returned). In this case the return value
2113 /// is a pointer to a string describing the error.
2114 /// </returns>
2115  
2116 static protected internal string callTraces( Interp interp, Var array, Var var, string part1, string part2, TCL.VarFlag flags )
2117 {
2118 TclObject oldResult;
2119 int i;
2120  
2121 // If there are already similar trace procedures active for the
2122 // variable, don't call them again.
2123  
2124 if ( ( var.flags & VarFlags.TRACE_ACTIVE ) != 0 )
2125 {
2126 return null;
2127 }
2128 var.flags |= VarFlags.TRACE_ACTIVE;
2129 var.refCount++;
2130  
2131 // If the variable name hasn't been parsed into array name and
2132 // element, do it here. If there really is an array element,
2133 // make a copy of the original name so that nulls can be
2134 // inserted into it to separate the names (can't modify the name
2135 // string in place, because the string might get used by the
2136 // callbacks we invoke).
2137  
2138 // FIXME : come up with parsing code to use for all situations!
2139 if ( (System.Object)part2 == null )
2140 {
2141 int len = part1.Length;
2142  
2143 if ( len > 0 )
2144 {
2145 if ( part1[len - 1] == ')' )
2146 {
2147 for ( i = 0; i < len - 1; i++ )
2148 {
2149 if ( part1[i] == '(' )
2150 {
2151 break;
2152 }
2153 }
2154 if ( i < len - 1 )
2155 {
2156 if ( i < len - 2 )
2157 {
2158 part2 = part1.Substring( i + 1, ( len - 1 ) - ( i + 1 ) );
2159 part1 = part1.Substring( 0, ( i ) - ( 0 ) );
2160 }
2161 }
2162 }
2163 }
2164 }
2165  
2166 oldResult = interp.getResult();
2167 oldResult.preserve();
2168 interp.resetResult();
2169  
2170 try
2171 {
2172 // Invoke traces on the array containing the variable, if relevant.
2173  
2174 if ( array != null )
2175 {
2176 array.refCount++;
2177 }
2178 if ( ( array != null ) && ( array.traces != null ) )
2179 {
2180 for ( i = 0; ( array.traces != null ) && ( i < array.traces.Count ); i++ )
2181 {
2182 TraceRecord rec = (TraceRecord)array.traces[i];
2183 if ( ( rec.flags & flags ) != 0 )
2184 {
2185 try
2186 {
2187 rec.trace.traceProc( interp, part1, part2, flags );
2188 }
2189 catch ( TclException e )
2190 {
2191 if ( ( flags & TCL.VarFlag.TRACE_UNSETS ) == 0 )
2192 {
2193  
2194 return interp.getResult().ToString();
2195 }
2196 }
2197 }
2198 }
2199 }
2200  
2201 // Invoke traces on the variable itself.
2202  
2203 if ( ( flags & TCL.VarFlag.TRACE_UNSETS ) != 0 )
2204 {
2205 flags |= TCL.VarFlag.TRACE_DESTROYED;
2206 }
2207  
2208 for ( i = 0; ( var.traces != null ) && ( i < var.traces.Count ); i++ )
2209 {
2210 TraceRecord rec = (TraceRecord)var.traces[i];
2211 if ( ( rec.flags & flags ) != 0 )
2212 {
2213 try
2214 {
2215 rec.trace.traceProc( interp, part1, part2, flags );
2216 }
2217 catch ( TclException e )
2218 {
2219 if ( ( flags & TCL.VarFlag.TRACE_UNSETS ) == 0 )
2220 {
2221  
2222 return interp.getResult().ToString();
2223 }
2224 }
2225 }
2226 }
2227  
2228 return null;
2229 }
2230 finally
2231 {
2232 if ( array != null )
2233 {
2234 array.refCount--;
2235 }
2236 var.flags &= ~VarFlags.TRACE_ACTIVE;
2237 var.refCount--;
2238  
2239 interp.setResult( oldResult );
2240 oldResult.release();
2241 }
2242 }
2243  
2244 /// <summary> DeleteSearches -> deleteSearches
2245 ///
2246 /// This procedure is called to free up all of the searches
2247 /// associated with an array variable.
2248 ///
2249 /// </summary>
2250 /// <param name="interp">Interpreter containing array.
2251 /// </param>
2252 /// <param name="arrayVar">the array variable to delete searches from.
2253 /// </param>
2254  
2255 static protected internal void deleteSearches( Var arrayVar )
2256 // Variable whose searches are to be deleted.
2257 {
2258 arrayVar.sidVec = null;
2259 }
2260  
2261 /// <summary> TclDeleteVars -> deleteVars
2262 ///
2263 /// This procedure is called to recycle all the storage space
2264 /// associated with a table of variables. For this procedure
2265 /// to work correctly, it must not be possible for any of the
2266 /// variables in the table to be accessed from Tcl commands
2267 /// (e.g. from trace procedures).
2268 ///
2269 /// </summary>
2270 /// <param name="interp">Interpreter containing array.
2271 /// </param>
2272 /// <param name="table">Hashtbale that holds the Vars to delete
2273 /// </param>
2274  
2275 static protected internal void deleteVars( Interp interp, Hashtable table )
2276 {
2277 IEnumerator search;
2278 string hashKey;
2279 Var var;
2280 Var link;
2281 TCL.VarFlag flags;
2282 //ActiveVarTrace active;
2283 TclObject obj;
2284 NamespaceCmd.Namespace currNs = NamespaceCmd.getCurrentNamespace( interp );
2285  
2286 // Determine what flags to pass to the trace callback procedures.
2287  
2288 flags = TCL.VarFlag.TRACE_UNSETS;
2289 if ( table == interp.globalNs.varTable )
2290 {
2291 flags |= ( TCL.VarFlag.INTERP_DESTROYED | TCL.VarFlag.GLOBAL_ONLY );
2292 }
2293 else if ( table == currNs.varTable )
2294 {
2295 flags |= TCL.VarFlag.NAMESPACE_ONLY;
2296 }
2297  
2298  
2299 for ( search = table.Values.GetEnumerator(); search.MoveNext(); )
2300 {
2301 var = (Var)search.Current;
2302  
2303 // For global/upvar variables referenced in procedures, decrement
2304 // the reference count on the variable referred to, and free
2305 // the referenced variable if it's no longer needed. Don't delete
2306 // the hash entry for the other variable if it's in the same table
2307 // as us: this will happen automatically later on.
2308  
2309 if ( var.isVarLink() )
2310 {
2311 link = (Var)var.value;
2312 link.refCount--;
2313 if ( ( link.refCount == 0 ) && link.isVarUndefined() && ( link.traces == null ) && ( ( link.flags & VarFlags.IN_HASHTABLE ) != 0 ) )
2314 {
2315  
2316 if ( (System.Object)link.hashKey == null )
2317 {
2318 var.value = null; // Drops reference to the link Var
2319 }
2320 else if ( link.table != table )
2321 {
2322 SupportClass.HashtableRemove( link.table, link.hashKey );
2323 link.table = null; // Drops the link var's table reference
2324 var.value = null; // Drops reference to the link Var
2325 }
2326 }
2327 }
2328  
2329 // free up the variable's space (no need to free the hash entry
2330 // here, unless we're dealing with a global variable: the
2331 // hash entries will be deleted automatically when the whole
2332 // table is deleted). Note that we give callTraces the variable's
2333 // fully-qualified name so that any called trace procedures can
2334 // refer to these variables being deleted.
2335  
2336 if ( var.traces != null )
2337 {
2338 string fullname = getVariableFullName( interp, var );
2339  
2340 callTraces( interp, null, var, fullname, null, flags );
2341  
2342 // The var.traces = null statement later will drop all the
2343 // references to the traces which will free them up
2344 }
2345  
2346 if ( var.isVarArray() )
2347 {
2348 deleteArray( interp, var.hashKey, var, flags );
2349 var.value = null;
2350 }
2351 if ( var.isVarScalar() && ( var.value != null ) )
2352 {
2353 obj = (TclObject)var.value;
2354 obj.release();
2355 var.value = null;
2356 }
2357  
2358 // There is no hPtr member in Jacl, The hPtr combines the table
2359 // and the key used in a table lookup.
2360 var.hashKey = null;
2361 var.table = null;
2362 var.traces = null;
2363 var.setVarUndefined();
2364 var.setVarScalar();
2365  
2366 // If the variable was a namespace variable, decrement its
2367 // reference count. We are in the process of destroying its
2368 // namespace so that namespace will no longer "refer" to the
2369 // variable.
2370  
2371 if ( ( var.flags & VarFlags.NAMESPACE_VAR ) != 0 )
2372 {
2373 var.flags &= ~VarFlags.NAMESPACE_VAR;
2374 var.refCount--;
2375 }
2376  
2377 // Recycle the variable's memory space if there aren't any upvar's
2378 // pointing to it. If there are upvars to this variable, then the
2379 // variable will get freed when the last upvar goes away.
2380  
2381 if ( var.refCount == 0 )
2382 {
2383 // When we drop the last reference it will be freeded
2384 }
2385 }
2386 table.Clear();
2387 }
2388  
2389  
2390 /// <summary> DeleteArray -> deleteArray
2391 ///
2392 /// This procedure is called to free up everything in an array
2393 /// variable. It's the caller's responsibility to make sure
2394 /// that the array is no longer accessible before this procedure
2395 /// is called.
2396 ///
2397 /// </summary>
2398 /// <param name="interp">Interpreter containing array.
2399 /// </param>
2400 /// <param name="arrayName">name of array (used for trace callbacks).
2401 /// </param>
2402 /// <param name="var">the array variable to delete.
2403 /// </param>
2404 /// <param name="flags">Flags to pass to CallTraces.
2405 /// </param>
2406  
2407 static protected internal void deleteArray( Interp interp, string arrayName, Var var, TCL.VarFlag flags )
2408 {
2409 IEnumerator search;
2410 Var el;
2411 TclObject obj;
2412  
2413 deleteSearches( var );
2414 Hashtable table = (Hashtable)var.value;
2415  
2416 Var dummyVar;
2417 for ( search = table.Values.GetEnumerator(); search.MoveNext(); )
2418 {
2419 el = (Var)search.Current;
2420  
2421 if ( el.isVarScalar() && ( el.value != null ) )
2422 {
2423 obj = (TclObject)el.value;
2424 obj.release();
2425 el.value = null;
2426 }
2427  
2428 string tmpkey = (string)el.hashKey;
2429 // There is no hPtr member in Jacl, The hPtr combines the table
2430 // and the key used in a table lookup.
2431 el.hashKey = null;
2432 el.table = null;
2433 if ( el.traces != null )
2434 {
2435 el.flags &= ~VarFlags.TRACE_ACTIVE;
2436 // FIXME : Old Jacl impl passed a dummy var to callTraces, should we?
2437 callTraces( interp, null, el, arrayName, tmpkey, flags );
2438 el.traces = null;
2439 // Active trace stuff is not part of Jacl
2440 }
2441 el.setVarUndefined();
2442 el.setVarScalar();
2443 if ( el.refCount == 0 )
2444 {
2445 // We are no longer using the element
2446 // element Vars are IN_HASHTABLE
2447 }
2448 }
2449 ( (Hashtable)var.value ).Clear();
2450 var.value = null;
2451 }
2452  
2453  
2454 /// <summary> CleanupVar -> cleanupVar
2455 ///
2456 /// This procedure is called when it looks like it may be OK
2457 /// to free up the variable's record and hash table entry, and
2458 /// those of its containing parent. It's called, for example,
2459 /// when a trace on a variable deletes the variable.
2460 ///
2461 /// </summary>
2462 /// <param name="var">variable that may be a candidate for being expunged.
2463 /// </param>
2464 /// <param name="array">Array that contains the variable, or NULL if this
2465 /// variable isn't an array element.
2466 /// </param>
2467  
2468 static protected internal void cleanupVar( Var var, Var array )
2469 {
2470 if ( var.isVarUndefined() && ( var.refCount == 0 ) && ( var.traces == null ) && ( ( var.flags & VarFlags.IN_HASHTABLE ) != 0 ) )
2471 {
2472 if ( var.table != null )
2473 {
2474 SupportClass.HashtableRemove( var.table, var.hashKey );
2475 var.table = null;
2476 var.hashKey = null;
2477 }
2478 }
2479 if ( array != null )
2480 {
2481 if ( array.isVarUndefined() && ( array.refCount == 0 ) && ( array.traces == null ) && ( ( array.flags & VarFlags.IN_HASHTABLE ) != 0 ) )
2482 {
2483 if ( array.table != null )
2484 {
2485 SupportClass.HashtableRemove( array.table, array.hashKey );
2486 array.table = null;
2487 array.hashKey = null;
2488 }
2489 }
2490 }
2491 }
2492 } // End of Var class
2493 }