wasCSharpSQLite – Blame information for rev
?pathlinks?
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 | } |