PageRenderTime 70ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/trunk/managed/csharpsqlite/TCL/src/base/Var.cs

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