PageRenderTime 31ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/TCL/src/base/Interp.cs

https://bitbucket.org/eumario/csharp-sqlite
C# | 2609 lines | 1582 code | 408 blank | 619 comment | 246 complexity | 87d7548632eba221c0971ec69b21f156 MD5 | raw file
  1. #undef DEBUG
  2. /*
  3. * Interp.java --
  4. *
  5. * Implements the core Tcl interpreter.
  6. *
  7. * Copyright (c) 1997 Cornell University.
  8. * Copyright (c) 1997-1998 Sun Microsystems, Inc.
  9. *
  10. * See the file "license.terms" for information on usage and
  11. * redistribution of this file, and for a DISCLAIMER OF ALL
  12. * WARRANTIES.
  13. *
  14. * Included in SQLite3 port to C# for use in testharness only; 2008 Noah B Hart
  15. *
  16. * RCS @(#) $Id: Interp.java,v 1.44 2003/07/25 16:38:35 mdejong Exp $
  17. *
  18. */
  19. using System;
  20. using System.Collections;
  21. using System.IO;
  22. using System.Text;
  23. namespace tcl.lang
  24. {
  25. /// <summary> The Tcl interpreter class.</summary>
  26. public class Interp : EventuallyFreed
  27. {
  28. private void InitBlock()
  29. {
  30. reflectObjTable = new Hashtable();
  31. reflectConflictTable = new Hashtable();
  32. importTable = new Hashtable[] { new Hashtable(), new Hashtable() };
  33. }
  34. /// <summary> Returns the name of the script file currently under execution.
  35. ///
  36. /// </summary>
  37. /// <returns> the name of the script file currently under execution.
  38. /// </returns>
  39. internal string ScriptFile
  40. {
  41. get
  42. {
  43. return dbg.fileName;
  44. }
  45. }
  46. // The following three variables are used to maintain a translation
  47. // table between ReflectObject's and their string names. These
  48. // variables are accessed by the ReflectObject class, they
  49. // are defined here be cause we need them to be per interp data.
  50. // Translates Object to ReflectObject. This makes sure we have only
  51. // one ReflectObject internalRep for the same Object -- this
  52. // way Object identity can be done by string comparison.
  53. internal Hashtable reflectObjTable;
  54. // Number of reflect objects created so far inside this Interp
  55. // (including those that have be freed)
  56. internal long reflectObjCount = 0;
  57. // Table used to store reflect hash index conflicts, see
  58. // ReflectObject implementation for more details
  59. internal Hashtable reflectConflictTable;
  60. // The number of chars to copy from an offending command into error
  61. // message.
  62. private const int MAX_ERR_LENGTH = 200;
  63. // We pretend this is Tcl 8.0, patch level 0.
  64. internal const string TCL_VERSION = "8.0";
  65. internal const string TCL_PATCH_LEVEL = "8.0";
  66. // Total number of times a command procedure
  67. // has been called for this interpreter.
  68. protected internal int cmdCount;
  69. // FIXME : remove later
  70. // Table of commands for this interpreter.
  71. //Hashtable cmdTable;
  72. // Table of channels currently registered in this interp.
  73. internal Hashtable interpChanTable;
  74. // The Notifier associated with this Interp.
  75. private Notifier notifier;
  76. // Hash table for associating data with this interpreter. Cleaned up
  77. // when this interpreter is deleted.
  78. internal Hashtable assocData;
  79. // Current working directory.
  80. private FileInfo workingDir;
  81. // Points to top-most in stack of all nested procedure
  82. // invocations. null means there are no active procedures.
  83. internal CallFrame frame;
  84. // Points to the call frame whose variables are currently in use
  85. // (same as frame unless an "uplevel" command is being
  86. // executed). null means no procedure is active or "uplevel 0" is
  87. // being exec'ed.
  88. internal CallFrame varFrame;
  89. // The interpreter's global namespace.
  90. internal NamespaceCmd.Namespace globalNs;
  91. // Hash table used to keep track of hidden commands on a per-interp basis.
  92. internal Hashtable hiddenCmdTable;
  93. // Information used by InterpCmd.java to keep
  94. // track of master/slave interps on a per-interp basis.
  95. // Keeps track of all interps for which this interp is the Master.
  96. // First, slaveTable (a hashtable) maps from names of commands to
  97. // slave interpreters. This hashtable is used to store information
  98. // about slave interpreters of this interpreter, to map over all slaves, etc.
  99. internal Hashtable slaveTable;
  100. // Hash table for Target Records. Contains all Target records which denote
  101. // aliases from slaves or sibling interpreters that direct to commands in
  102. // this interpreter. This table is used to remove dangling pointers
  103. // from the slave (or sibling) interpreters when this interpreter is deleted.
  104. internal Hashtable targetTable;
  105. // Information necessary for this interp to function as a slave.
  106. internal InterpSlaveCmd slave;
  107. // Table which maps from names of commands in slave interpreter to
  108. // InterpAliasCmd objects.
  109. internal Hashtable aliasTable;
  110. // FIXME : does globalFrame need to be replaced by globalNs?
  111. // Points to the global variable frame.
  112. //CallFrame globalFrame;
  113. // The script file currently under execution. Can be null if the
  114. // interpreter is not evaluating any script file.
  115. internal string scriptFile;
  116. // Number of times the interp.eval() routine has been recursively
  117. // invoked.
  118. internal int nestLevel;
  119. // Used to catch infinite loops in Parser.eval2.
  120. internal int maxNestingDepth;
  121. // Flags used when evaluating a command.
  122. internal int evalFlags;
  123. // Flags used when evaluating a command.
  124. public int flags;
  125. // Is this interpreted marked as safe?
  126. internal bool isSafe;
  127. // Offset of character just after last one compiled or executed
  128. // by Parser.eval2().
  129. internal int termOffset;
  130. // List of name resolution schemes added to this interpreter.
  131. // Schemes are added/removed by calling addInterpResolver and
  132. // removeInterpResolver.
  133. internal ArrayList resolvers;
  134. // The expression parser for this interp.
  135. internal Expression expr;
  136. // Used by the Expression class. If it is equal to zero, then the
  137. // parser will evaluate commands and retrieve variable values from
  138. // the interp.
  139. internal int noEval;
  140. // Used in the Expression.java file for the
  141. // SrandFunction.class and RandFunction.class.
  142. // Set to true if a seed has been set.
  143. internal bool randSeedInit;
  144. // Used in the Expression.java file for the SrandFunction.class and
  145. // RandFunction.class. Stores the value of the seed.
  146. internal long randSeed;
  147. // If returnCode is TCL.CompletionCode.ERROR, stores the errorInfo.
  148. internal string errorInfo;
  149. // If returnCode is TCL.CompletionCode.ERROR, stores the errorCode.
  150. internal string errorCode;
  151. // Completion code to return if current procedure exits with a
  152. // TCL_RETURN code.
  153. protected internal TCL.CompletionCode returnCode;
  154. // True means the interpreter has been deleted: don't process any
  155. // more commands for it, and destroy the structure as soon as all
  156. // nested invocations of eval() are done.
  157. protected internal bool deleted;
  158. // True means an error unwind is already in progress. False
  159. // means a command proc has been invoked since last error occurred.
  160. protected internal bool errInProgress;
  161. // True means information has already been logged in $errorInfo
  162. // for the current eval() instance, so eval() needn't log it
  163. // (used to implement the "error" command).
  164. protected internal bool errAlreadyLogged;
  165. // True means that addErrorInfo has been called to record
  166. // information for the current error. False means Interp.eval
  167. // must clear the errorCode variable if an error is returned.
  168. protected internal bool errCodeSet;
  169. // When TCL_ERROR is returned, this gives the line number within
  170. // the command where the error occurred (1 means first line).
  171. internal int errorLine;
  172. // Stores the current result in the interpreter.
  173. private TclObject m_result;
  174. // Value m_result is set to when resetResult() is called.
  175. private TclObject m_nullResult;
  176. // Used ONLY by PackageCmd.
  177. internal Hashtable packageTable;
  178. internal string packageUnknown;
  179. // Used ONLY by the Parser.
  180. internal TclObject[][][] parserObjv;
  181. internal int[] parserObjvUsed;
  182. internal TclToken[] parserTokens;
  183. internal int parserTokensUsed;
  184. // Used ONLY by JavaImportCmd
  185. internal Hashtable[] importTable;
  186. // List of unsafe commands:
  187. internal static readonly string[] unsafeCmds = new string[] { "encoding", "exit", "load", "cd", "fconfigure", "file", "glob", "open", "pwd", "socket", "beep", "echo", "ls", "resource", "source", "exec", "source" };
  188. // Flags controlling the call of invoke.
  189. internal const int INVOKE_HIDDEN = 1;
  190. internal const int INVOKE_NO_UNKNOWN = 2;
  191. internal const int INVOKE_NO_TRACEBACK = 4;
  192. public Interp()
  193. {
  194. InitBlock();
  195. //freeProc = null;
  196. errorLine = 0;
  197. // An empty result is used pretty often. We will use a shared
  198. // TclObject instance to represent the empty result so that we
  199. // don't need to create a new TclObject instance every time the
  200. // interpreter result is set to empty.
  201. m_nullResult = TclString.newInstance( "" );
  202. m_nullResult.preserve(); // Increment refCount to 1
  203. m_nullResult.preserve(); // Increment refCount to 2 (shared)
  204. m_result = TclString.newInstance( "" ); //m_nullResult; // correcponds to iPtr->objResultPtr
  205. m_result.preserve();
  206. expr = new Expression();
  207. nestLevel = 0;
  208. maxNestingDepth = 1000;
  209. frame = null;
  210. varFrame = null;
  211. returnCode = TCL.CompletionCode.OK;
  212. errorInfo = null;
  213. errorCode = null;
  214. packageTable = new Hashtable();
  215. packageUnknown = null;
  216. cmdCount = 0;
  217. termOffset = 0;
  218. resolvers = null;
  219. evalFlags = 0;
  220. scriptFile = null;
  221. flags = 0;
  222. isSafe = false;
  223. assocData = null;
  224. globalNs = null; // force creation of global ns below
  225. globalNs = NamespaceCmd.createNamespace( this, null, null );
  226. if ( globalNs == null )
  227. {
  228. throw new TclRuntimeError( "Interp(): can't create global namespace" );
  229. }
  230. // Init things that are specific to the Jacl implementation
  231. workingDir = new FileInfo( System.Environment.CurrentDirectory );
  232. noEval = 0;
  233. notifier = Notifier.getNotifierForThread( System.Threading.Thread.CurrentThread );
  234. notifier.preserve();
  235. randSeedInit = false;
  236. deleted = false;
  237. errInProgress = false;
  238. errAlreadyLogged = false;
  239. errCodeSet = false;
  240. dbg = initDebugInfo();
  241. slaveTable = new Hashtable();
  242. targetTable = new Hashtable();
  243. aliasTable = new Hashtable();
  244. // init parser variables
  245. Parser.init( this );
  246. TclParse.init( this );
  247. // Initialize the Global (static) channel table and the local
  248. // interp channel table.
  249. interpChanTable = TclIO.getInterpChanTable( this );
  250. // Sets up the variable trace for tcl_precision.
  251. Util.setupPrecisionTrace( this );
  252. // Create the built-in commands.
  253. createCommands();
  254. try
  255. {
  256. // Set up tcl_platform, tcl_version, tcl_library and other
  257. // global variables.
  258. setVar( "tcl_platform", "platform", "windows", TCL.VarFlag.GLOBAL_ONLY );
  259. setVar( "tcl_platform", "byteOrder", "bigEndian", TCL.VarFlag.GLOBAL_ONLY );
  260. setVar( "tcl_platform", "os", Environment.OSVersion.Platform.ToString(), TCL.VarFlag.GLOBAL_ONLY );
  261. setVar( "tcl_platform", "osVersion", Environment.OSVersion.Version.ToString(), TCL.VarFlag.GLOBAL_ONLY );
  262. setVar( "tcl_platform", "machine", Util.tryGetSystemProperty( "os.arch", "?" ), TCL.VarFlag.GLOBAL_ONLY );
  263. setVar( "tcl_version", TCL_VERSION, TCL.VarFlag.GLOBAL_ONLY );
  264. setVar( "tcl_patchLevel", TCL_PATCH_LEVEL, TCL.VarFlag.GLOBAL_ONLY );
  265. setVar( "tcl_library", "resource:/tcl/lang/library", TCL.VarFlag.GLOBAL_ONLY );
  266. if ( Util.Windows )
  267. {
  268. setVar( "tcl_platform", "host_platform", "windows", TCL.VarFlag.GLOBAL_ONLY );
  269. }
  270. else if ( Util.Mac )
  271. {
  272. setVar( "tcl_platform", "host_platform", "macintosh", TCL.VarFlag.GLOBAL_ONLY );
  273. }
  274. else
  275. {
  276. setVar( "tcl_platform", "host_platform", "unix", TCL.VarFlag.GLOBAL_ONLY );
  277. }
  278. // Create the env array an populated it with proper
  279. // values.
  280. Env.initialize( this );
  281. // Register Tcl's version number. Note: This MUST be
  282. // done before the call to evalResource, otherwise
  283. // calls to "package require tcl" will fail.
  284. pkgProvide( "Tcl", TCL_VERSION );
  285. // Source the init.tcl script to initialize auto-loading.
  286. evalResource( "/tcl/lang/library/init.tcl" );
  287. }
  288. catch ( TclException e )
  289. {
  290. System.Diagnostics.Debug.WriteLine( getResult().ToString() );
  291. SupportClass.WriteStackTrace( e, Console.Error );
  292. throw new TclRuntimeError( "unexpected TclException: " + e.Message, e );
  293. }
  294. }
  295. public override void eventuallyDispose()
  296. {
  297. if ( deleted )
  298. {
  299. return;
  300. }
  301. deleted = true;
  302. if ( nestLevel > 0 )
  303. {
  304. //-- TODO -- Determine why this is an error throw new TclRuntimeError("dispose() called with active evals");
  305. }
  306. // Remove our association with the notifer (if we had one).
  307. if ( notifier != null )
  308. {
  309. notifier.release();
  310. notifier = null;
  311. }
  312. // Dismantle everything in the global namespace except for the
  313. // "errorInfo" and "errorCode" variables. These might be needed
  314. // later on if errors occur while deleting commands. We are careful
  315. // to destroy and recreate the "errorInfo" and "errorCode"
  316. // variables, in case they had any traces on them.
  317. //
  318. // Dismantle the namespace here, before we clear the assocData. If any
  319. // background errors occur here, they will be deleted below.
  320. // FIXME : check impl of TclTeardownNamespace
  321. NamespaceCmd.teardownNamespace( globalNs );
  322. // Delete all variables.
  323. TclObject errorInfoObj = null, errorCodeObj = null;
  324. try
  325. {
  326. errorInfoObj = getVar( "errorInfo", null, TCL.VarFlag.GLOBAL_ONLY );
  327. }
  328. catch ( TclException e )
  329. {
  330. // Do nothing when var does not exist.
  331. }
  332. if ( errorInfoObj != null )
  333. {
  334. errorInfoObj.preserve();
  335. }
  336. try
  337. {
  338. errorCodeObj = getVar( "errorCode", null, TCL.VarFlag.GLOBAL_ONLY );
  339. }
  340. catch ( TclException e )
  341. {
  342. // Do nothing when var does not exist.
  343. }
  344. if ( errorCodeObj != null )
  345. {
  346. errorCodeObj.preserve();
  347. }
  348. frame = null;
  349. varFrame = null;
  350. try
  351. {
  352. if ( errorInfoObj != null )
  353. {
  354. setVar( "errorInfo", null, errorInfoObj, TCL.VarFlag.GLOBAL_ONLY );
  355. errorInfoObj.release();
  356. }
  357. if ( errorCodeObj != null )
  358. {
  359. setVar( "errorCode", null, errorCodeObj, TCL.VarFlag.GLOBAL_ONLY );
  360. errorCodeObj.release();
  361. }
  362. }
  363. catch ( TclException e )
  364. {
  365. // Ignore it -- same behavior as Tcl 8.0.
  366. }
  367. // Tear down the math function table.
  368. expr = null;
  369. // Remove all the assoc data tied to this interp and invoke
  370. // deletion callbacks; note that a callback can create new
  371. // callbacks, so we iterate.
  372. // ATK The java code was somethink strong
  373. if ( assocData != null )
  374. {
  375. foreach ( AssocData data in assocData.Values )
  376. {
  377. data.disposeAssocData( this );
  378. }
  379. assocData.Clear();
  380. }
  381. // Close any remaining channels
  382. for ( IDictionaryEnumerator e = interpChanTable.GetEnumerator(); e.MoveNext(); )
  383. {
  384. Object key = e.Key;
  385. Channel chan = (Channel)e.Value;
  386. try
  387. {
  388. chan.close();
  389. }
  390. catch ( IOException ex )
  391. {
  392. // Ignore any IO errors
  393. }
  394. }
  395. // Finish deleting the global namespace.
  396. // FIXME : check impl of Tcl_DeleteNamespace
  397. NamespaceCmd.deleteNamespace( globalNs );
  398. globalNs = null;
  399. // Free up the result *after* deleting variables, since variable
  400. // deletion could have transferred ownership of the result string
  401. // to Tcl.
  402. frame = null;
  403. varFrame = null;
  404. resolvers = null;
  405. resetResult();
  406. }
  407. ~Interp()
  408. {
  409. dispose();
  410. }
  411. protected internal void createCommands()
  412. {
  413. Extension.loadOnDemand( this, "after", "tcl.lang.AfterCmd" );
  414. Extension.loadOnDemand( this, "append", "tcl.lang.AppendCmd" );
  415. Extension.loadOnDemand( this, "array", "tcl.lang.ArrayCmd" );
  416. Extension.loadOnDemand( this, "binary", "tcl.lang.BinaryCmd" );
  417. Extension.loadOnDemand( this, "break", "tcl.lang.BreakCmd" );
  418. Extension.loadOnDemand( this, "case", "tcl.lang.CaseCmd" );
  419. Extension.loadOnDemand( this, "catch", "tcl.lang.CatchCmd" );
  420. Extension.loadOnDemand( this, "cd", "tcl.lang.CdCmd" );
  421. Extension.loadOnDemand( this, "clock", "tcl.lang.ClockCmd" );
  422. Extension.loadOnDemand( this, "close", "tcl.lang.CloseCmd" );
  423. Extension.loadOnDemand( this, "continue", "tcl.lang.ContinueCmd" );
  424. Extension.loadOnDemand( this, "concat", "tcl.lang.ConcatCmd" );
  425. Extension.loadOnDemand( this, "encoding", "tcl.lang.EncodingCmd" );
  426. Extension.loadOnDemand( this, "eof", "tcl.lang.EofCmd" );
  427. Extension.loadOnDemand( this, "eval", "tcl.lang.EvalCmd" );
  428. Extension.loadOnDemand( this, "error", "tcl.lang.ErrorCmd" );
  429. if ( !Util.Mac )
  430. {
  431. Extension.loadOnDemand( this, "exec", "tcl.lang.ExecCmd" );
  432. }
  433. Extension.loadOnDemand( this, "exit", "tcl.lang.ExitCmd" );
  434. Extension.loadOnDemand( this, "expr", "tcl.lang.ExprCmd" );
  435. Extension.loadOnDemand( this, "fblocked", "tcl.lang.FblockedCmd" );
  436. Extension.loadOnDemand( this, "fconfigure", "tcl.lang.FconfigureCmd" );
  437. Extension.loadOnDemand( this, "file", "tcl.lang.FileCmd" );
  438. Extension.loadOnDemand( this, "flush", "tcl.lang.FlushCmd" );
  439. Extension.loadOnDemand( this, "for", "tcl.lang.ForCmd" );
  440. Extension.loadOnDemand( this, "foreach", "tcl.lang.ForeachCmd" );
  441. Extension.loadOnDemand( this, "format", "tcl.lang.FormatCmd" );
  442. Extension.loadOnDemand( this, "gets", "tcl.lang.GetsCmd" );
  443. Extension.loadOnDemand( this, "global", "tcl.lang.GlobalCmd" );
  444. Extension.loadOnDemand( this, "glob", "tcl.lang.GlobCmd" );
  445. Extension.loadOnDemand( this, "if", "tcl.lang.IfCmd" );
  446. Extension.loadOnDemand( this, "incr", "tcl.lang.IncrCmd" );
  447. Extension.loadOnDemand( this, "info", "tcl.lang.InfoCmd" );
  448. Extension.loadOnDemand( this, "interp", "tcl.lang.InterpCmd" );
  449. Extension.loadOnDemand( this, "list", "tcl.lang.ListCmd" );
  450. Extension.loadOnDemand( this, "join", "tcl.lang.JoinCmd" );
  451. Extension.loadOnDemand( this, "lappend", "tcl.lang.LappendCmd" );
  452. Extension.loadOnDemand( this, "lindex", "tcl.lang.LindexCmd" );
  453. Extension.loadOnDemand( this, "linsert", "tcl.lang.LinsertCmd" );
  454. Extension.loadOnDemand( this, "llength", "tcl.lang.LlengthCmd" );
  455. Extension.loadOnDemand( this, "lrange", "tcl.lang.LrangeCmd" );
  456. Extension.loadOnDemand( this, "lreplace", "tcl.lang.LreplaceCmd" );
  457. Extension.loadOnDemand( this, "lsearch", "tcl.lang.LsearchCmd" );
  458. Extension.loadOnDemand( this, "lset", "tcl.lang.LsetCmd" );
  459. Extension.loadOnDemand( this, "lsort", "tcl.lang.LsortCmd" );
  460. Extension.loadOnDemand( this, "namespace", "tcl.lang.NamespaceCmd" );
  461. Extension.loadOnDemand( this, "open", "tcl.lang.OpenCmd" );
  462. Extension.loadOnDemand( this, "package", "tcl.lang.PackageCmd" );
  463. Extension.loadOnDemand( this, "proc", "tcl.lang.ProcCmd" );
  464. Extension.loadOnDemand( this, "puts", "tcl.lang.PutsCmd" );
  465. Extension.loadOnDemand( this, "pwd", "tcl.lang.PwdCmd" );
  466. Extension.loadOnDemand( this, "read", "tcl.lang.ReadCmd" );
  467. Extension.loadOnDemand( this, "regsub", "tcl.lang.RegsubCmd" );
  468. Extension.loadOnDemand( this, "rename", "tcl.lang.RenameCmd" );
  469. Extension.loadOnDemand( this, "return", "tcl.lang.ReturnCmd" );
  470. Extension.loadOnDemand( this, "scan", "tcl.lang.ScanCmd" );
  471. Extension.loadOnDemand( this, "seek", "tcl.lang.SeekCmd" );
  472. Extension.loadOnDemand( this, "set", "tcl.lang.SetCmd" );
  473. Extension.loadOnDemand( this, "socket", "tcl.lang.SocketCmd" );
  474. Extension.loadOnDemand( this, "source", "tcl.lang.SourceCmd" );
  475. Extension.loadOnDemand( this, "split", "tcl.lang.SplitCmd" );
  476. Extension.loadOnDemand( this, "string", "tcl.lang.StringCmd" );
  477. Extension.loadOnDemand( this, "subst", "tcl.lang.SubstCmd" );
  478. Extension.loadOnDemand( this, "switch", "tcl.lang.SwitchCmd" );
  479. Extension.loadOnDemand( this, "tell", "tcl.lang.TellCmd" );
  480. Extension.loadOnDemand( this, "time", "tcl.lang.TimeCmd" );
  481. Extension.loadOnDemand( this, "trace", "tcl.lang.TraceCmd" );
  482. Extension.loadOnDemand( this, "unset", "tcl.lang.UnsetCmd" );
  483. Extension.loadOnDemand( this, "update", "tcl.lang.UpdateCmd" );
  484. Extension.loadOnDemand( this, "uplevel", "tcl.lang.UplevelCmd" );
  485. Extension.loadOnDemand( this, "upvar", "tcl.lang.UpvarCmd" );
  486. Extension.loadOnDemand( this, "variable", "tcl.lang.VariableCmd" );
  487. Extension.loadOnDemand( this, "vwait", "tcl.lang.VwaitCmd" );
  488. Extension.loadOnDemand( this, "while", "tcl.lang.WhileCmd" );
  489. // Add "regexp" and related commands to this interp.
  490. RegexpCmd.init( this );
  491. // The Java package is only loaded when the user does a
  492. // "package require java" in the interp. We need to create a small
  493. // command that will load when "package require java" is called.
  494. Extension.loadOnDemand( this, "jaclloadjava", "tcl.lang.JaclLoadJavaCmd" );
  495. try
  496. {
  497. eval( "package ifneeded java 1.3.1 jaclloadjava" );
  498. }
  499. catch ( TclException e )
  500. {
  501. System.Diagnostics.Debug.WriteLine( getResult().ToString() );
  502. SupportClass.WriteStackTrace( e, Console.Error );
  503. throw new TclRuntimeError( "unexpected TclException: " + e.Message, e );
  504. }
  505. }
  506. public void setAssocData( string name, AssocData data )
  507. // Object associated with the name.
  508. {
  509. if ( assocData == null )
  510. {
  511. assocData = new Hashtable();
  512. }
  513. SupportClass.PutElement( assocData, name, data );
  514. }
  515. public void deleteAssocData( string name )
  516. // Name of association.
  517. {
  518. if ( assocData == null )
  519. {
  520. return;
  521. }
  522. SupportClass.HashtableRemove( assocData, name );
  523. }
  524. public AssocData getAssocData( string name )
  525. // Name of association.
  526. {
  527. if ( assocData == null )
  528. {
  529. return null;
  530. }
  531. else
  532. {
  533. return (AssocData)assocData[name];
  534. }
  535. }
  536. public void backgroundError()
  537. {
  538. BgErrorMgr mgr = (BgErrorMgr)getAssocData( "tclBgError" );
  539. if ( mgr == null )
  540. {
  541. mgr = new BgErrorMgr( this );
  542. setAssocData( "tclBgError", mgr );
  543. }
  544. mgr.addBgError();
  545. }
  546. /*-----------------------------------------------------------------
  547. *
  548. * VARIABLES
  549. *
  550. *-----------------------------------------------------------------
  551. */
  552. public TclObject setVar( TclObject nameObj, TclObject value, TCL.VarFlag flags )
  553. {
  554. return Var.setVar( this, nameObj, value, ( flags | TCL.VarFlag.LEAVE_ERR_MSG ) );
  555. }
  556. public TclObject setVar( string name, TclObject value, TCL.VarFlag flags )
  557. {
  558. return Var.setVar( this, name, value, ( flags | TCL.VarFlag.LEAVE_ERR_MSG ) );
  559. }
  560. public TclObject setVar( string name1, string name2, TclObject value, TCL.VarFlag flags )
  561. {
  562. return Var.setVar( this, name1, name2, value, ( flags | TCL.VarFlag.LEAVE_ERR_MSG ) );
  563. }
  564. public void setVar( string name, string strValue, TCL.VarFlag flags )
  565. {
  566. Var.setVar( this, name, TclString.newInstance( strValue ), ( flags | TCL.VarFlag.LEAVE_ERR_MSG ) );
  567. }
  568. public void setVar( string name1, string name2, string strValue, TCL.VarFlag flags )
  569. {
  570. Var.setVar( this, name1, name2, TclString.newInstance( strValue ), ( flags | TCL.VarFlag.LEAVE_ERR_MSG ) );
  571. }
  572. public TclObject getVar( TclObject nameObj, TCL.VarFlag flags )
  573. {
  574. return Var.getVar( this, nameObj, ( flags | TCL.VarFlag.LEAVE_ERR_MSG ) );
  575. }
  576. public TclObject getVar( string name, TCL.VarFlag flags )
  577. {
  578. return Var.getVar( this, name, ( flags | TCL.VarFlag.LEAVE_ERR_MSG ) );
  579. }
  580. public TclObject getVar( string name1, string name2, TCL.VarFlag flags )
  581. {
  582. return Var.getVar( this, name1, name2, ( flags | TCL.VarFlag.LEAVE_ERR_MSG ) );
  583. }
  584. public void unsetVar( TclObject nameObj, TCL.VarFlag flags )
  585. {
  586. Var.unsetVar( this, nameObj, ( flags | TCL.VarFlag.LEAVE_ERR_MSG ) );
  587. }
  588. public void unsetVar( string name, TCL.VarFlag flags )
  589. {
  590. Var.unsetVar( this, name, ( flags | TCL.VarFlag.LEAVE_ERR_MSG ) );
  591. }
  592. public void unsetVar( string name1, string name2, TCL.VarFlag flags )
  593. {
  594. Var.unsetVar( this, name1, name2, ( flags | TCL.VarFlag.LEAVE_ERR_MSG ) );
  595. }
  596. public void traceVar( TclObject nameObj, VarTrace trace, TCL.VarFlag flags )
  597. {
  598. Var.traceVar( this, nameObj, flags, trace );
  599. }
  600. public void traceVar( string name, VarTrace trace, TCL.VarFlag flags )
  601. {
  602. Var.traceVar( this, name, flags, trace );
  603. }
  604. public void traceVar( string part1, string part2, VarTrace trace, TCL.VarFlag flags )
  605. {
  606. Var.traceVar( this, part1, part2, flags, trace );
  607. }
  608. public void untraceVar( TclObject nameObj, VarTrace trace, TCL.VarFlag flags )
  609. // OR-ed collection of bits describing current
  610. // trace, including any of TCL.VarFlag.TRACE_READS,
  611. // TCL.VarFlag.TRACE_WRITES, TCL.VarFlag.TRACE_UNSETS,
  612. // TCL.VarFlag.GLOBAL_ONLY and TCL.VarFlag.NAMESPACE_ONLY.
  613. {
  614. Var.untraceVar( this, nameObj, flags, trace );
  615. }
  616. public void untraceVar( string name, VarTrace trace, TCL.VarFlag flags )
  617. // OR-ed collection of bits describing current
  618. // trace, including any of TCL.VarFlag.TRACE_READS,
  619. // TCL.VarFlag.TRACE_WRITES, TCL.VarFlag.TRACE_UNSETS,
  620. // TCL.VarFlag.GLOBAL_ONLY and TCL.VarFlag.NAMESPACE_ONLY.
  621. {
  622. Var.untraceVar( this, name, flags, trace );
  623. }
  624. public void untraceVar( string part1, string part2, VarTrace trace, TCL.VarFlag flags )
  625. // OR-ed collection of bits describing current
  626. // trace, including any of TCL.VarFlag.TRACE_READS,
  627. // TCL.VarFlag.TRACE_WRITES, TCL.VarFlag.TRACE_UNSETS,
  628. // TCL.VarFlag.GLOBAL_ONLY and TCL.VarFlag.NAMESPACE_ONLY.
  629. {
  630. Var.untraceVar( this, part1, part2, flags, trace );
  631. }
  632. public void createCommand( string cmdName, Command cmdImpl )
  633. // Command object to associate with
  634. // cmdName.
  635. {
  636. ImportRef oldRef = null;
  637. NamespaceCmd.Namespace ns;
  638. WrappedCommand cmd, refCmd;
  639. string tail;
  640. ImportedCmdData data;
  641. if ( deleted )
  642. {
  643. // The interpreter is being deleted. Don't create any new
  644. // commands; it's not safe to muck with the interpreter anymore.
  645. return;
  646. }
  647. // Determine where the command should reside. If its name contains
  648. // namespace qualifiers, we put it in the specified namespace;
  649. // otherwise, we always put it in the global namespace.
  650. if ( cmdName.IndexOf( "::" ) != -1 )
  651. {
  652. // Java does not support passing an address so we pass
  653. // an array of size 1 and then assign arr[0] to the value
  654. NamespaceCmd.Namespace[] nsArr = new NamespaceCmd.Namespace[1];
  655. NamespaceCmd.Namespace[] dummyArr = new NamespaceCmd.Namespace[1];
  656. string[] tailArr = new string[1];
  657. NamespaceCmd.getNamespaceForQualName( this, cmdName, null, TCL.VarFlag.CREATE_NS_IF_UNKNOWN, nsArr, dummyArr, dummyArr, tailArr );
  658. ns = nsArr[0];
  659. tail = tailArr[0];
  660. if ( ( ns == null ) || ( (System.Object)tail == null ) )
  661. {
  662. return;
  663. }
  664. }
  665. else
  666. {
  667. ns = globalNs;
  668. tail = cmdName;
  669. }
  670. cmd = (WrappedCommand)ns.cmdTable[tail];
  671. if ( cmd != null )
  672. {
  673. // Command already exists. Delete the old one.
  674. // Be careful to preserve any existing import links so we can
  675. // restore them down below. That way, you can redefine a
  676. // command and its import status will remain intact.
  677. oldRef = cmd.importRef;
  678. cmd.importRef = null;
  679. deleteCommandFromToken( cmd );
  680. // FIXME : create a test case for this condition!
  681. cmd = (WrappedCommand)ns.cmdTable[tail];
  682. if ( cmd != null )
  683. {
  684. // If the deletion callback recreated the command, just throw
  685. // away the new command (if we try to delete it again, we
  686. // could get stuck in an infinite loop).
  687. SupportClass.HashtableRemove( cmd.table, cmd.hashKey );
  688. }
  689. }
  690. cmd = new WrappedCommand();
  691. ns.cmdTable.Add( tail, cmd );
  692. cmd.table = ns.cmdTable;
  693. cmd.hashKey = tail;
  694. cmd.ns = ns;
  695. cmd.cmd = cmdImpl;
  696. cmd.deleted = false;
  697. // FIXME : import feature not implemented
  698. //cmd.importRef = null;
  699. // Plug in any existing import references found above. Be sure
  700. // to update all of these references to point to the new command.
  701. if ( oldRef != null )
  702. {
  703. cmd.importRef = oldRef;
  704. while ( oldRef != null )
  705. {
  706. refCmd = oldRef.importedCmd;
  707. data = (ImportedCmdData)refCmd.cmd;
  708. data.realCmd = cmd;
  709. oldRef = oldRef.next;
  710. }
  711. }
  712. // There are no shadowed commands in Jacl because they are only
  713. // used in the 8.0 compiler
  714. return;
  715. }
  716. /*
  717. *----------------------------------------------------------------------
  718. *
  719. * Tcl_CreateObjCommand --
  720. *
  721. * Define a new object-based command in a command table.
  722. *
  723. * Results:
  724. * The return value is a token for the command, which can
  725. * be used in future calls to Tcl_GetCommandName.
  726. *
  727. * Side effects:
  728. * If no command named "cmdName" already exists for interp, one is
  729. * created. Otherwise, if a command does exist, then if the
  730. * object-based Tcl_ObjCmdProc is TclInvokeStringCommand, we assume
  731. * Tcl_CreateCommand was called previously for the same command and
  732. * just set its Tcl_ObjCmdProc to the argument "proc"; otherwise, we
  733. * delete the old command.
  734. *
  735. * In the future, during bytecode evaluation when "cmdName" is seen as
  736. * the name of a command by Tcl_EvalObj or Tcl_Eval, the object-based
  737. * Tcl_ObjCmdProc proc will be called. When the command is deleted from
  738. * the table, deleteProc will be called. See the manual entry for
  739. * details on the calling sequence.
  740. *
  741. *----------------------------------------------------------------------
  742. */
  743. public delegate int dxObjCmdProc( object clientData, Interp interp, int argc, TclObject[] argv );
  744. public delegate void dxCmdDeleteProc( ref object clientData );
  745. public void createObjCommand( string cmdName, dxObjCmdProc proc, object clientData, dxCmdDeleteProc deleteProc )
  746. // Command object to associate with cmdName.
  747. {
  748. ImportRef oldRef = null;
  749. NamespaceCmd.Namespace ns;
  750. WrappedCommand cmd, refCmd;
  751. string tail;
  752. ImportedCmdData data;
  753. int _new;
  754. if ( deleted )
  755. {
  756. // The interpreter is being deleted. Don't create any new
  757. // commands; it's not safe to muck with the interpreter anymore.
  758. return;
  759. }
  760. // Determine where the command should reside. If its name contains
  761. // namespace qualifiers, we put it in the specified namespace;
  762. // otherwise, we always put it in the global namespace.
  763. if ( cmdName.IndexOf( "::" ) != -1 )
  764. {
  765. // Java does not support passing an address so we pass
  766. // an array of size 1 and then assign arr[0] to the value
  767. NamespaceCmd.Namespace[] nsArr = new NamespaceCmd.Namespace[1];
  768. NamespaceCmd.Namespace[] dummyArr = new NamespaceCmd.Namespace[1];
  769. string[] tailArr = new string[1];
  770. NamespaceCmd.getNamespaceForQualName( this, cmdName, null, TCL.VarFlag.CREATE_NS_IF_UNKNOWN, nsArr, dummyArr, dummyArr, tailArr );
  771. ns = nsArr[0];
  772. tail = tailArr[0];
  773. if ( ( ns == null ) || ( (System.Object)tail == null ) )
  774. {
  775. return;
  776. }
  777. }
  778. else
  779. {
  780. ns = globalNs;
  781. tail = cmdName;
  782. }
  783. cmd = (WrappedCommand)ns.cmdTable[tail];
  784. if ( cmd != null )
  785. {
  786. /*
  787. * Command already exists. If its object-based Tcl_ObjCmdProc is
  788. * TclInvokeStringCommand, we just set its Tcl_ObjCmdProc to the
  789. * argument "proc". Otherwise, we delete the old command.
  790. */
  791. if ( cmd.objProc != null && cmd.objProc.GetType().Name == "TclInvokeStringCommand" )
  792. {
  793. cmd.objProc = proc;
  794. cmd.objClientData = clientData;
  795. cmd.deleteProc = deleteProc;
  796. cmd.deleteData = clientData;
  797. return;
  798. }
  799. /*
  800. * Otherwise, we delete the old command. Be careful to preserve
  801. * any existing import links so we can restore them down below.
  802. * That way, you can redefine a command and its import status
  803. * will remain intact.
  804. */
  805. oldRef = cmd.importRef;
  806. cmd.importRef = null;
  807. deleteCommandFromToken( cmd );
  808. // FIXME : create a test case for this condition!
  809. cmd = (WrappedCommand)ns.cmdTable[tail];
  810. if ( cmd != null )
  811. {
  812. // If the deletion callback recreated the command, just throw
  813. // away the new command (if we try to delete it again, we
  814. // could get stuck in an infinite loop).
  815. SupportClass.HashtableRemove( cmd.table, cmd.hashKey );
  816. }
  817. }
  818. cmd = new WrappedCommand();
  819. ns.cmdTable.Add( tail, cmd );
  820. cmd.table = ns.cmdTable;
  821. cmd.hashKey = tail;
  822. cmd.ns = ns;
  823. cmd.cmd = null;
  824. cmd.deleted = false;
  825. // FIXME : import feature not implemented
  826. //cmd.importRef = null;
  827. // TODO -- Determine if this is all correct
  828. cmd.objProc = proc;
  829. cmd.objClientData = clientData;
  830. //cmd.proc = TclInvokeObjectCommand;
  831. cmd.clientData = (object)cmd;
  832. cmd.deleteProc = deleteProc;
  833. cmd.deleteData = clientData;
  834. cmd.flags = 0;
  835. // Plug in any existing import references found above. Be sure
  836. // to update all of these references to point to the new command.
  837. if ( oldRef != null )
  838. {
  839. cmd.importRef = oldRef;
  840. while ( oldRef != null )
  841. {
  842. refCmd = oldRef.importedCmd;
  843. data = (ImportedCmdData)refCmd.cmd;
  844. data.realCmd = cmd;
  845. oldRef = oldRef.next;
  846. }
  847. }
  848. // There are no shadowed commands in Jacl because they are only
  849. // used in the 8.0 compiler
  850. return;
  851. }
  852. internal string getCommandFullName( WrappedCommand cmd )
  853. // Token for the command.
  854. {
  855. Interp interp = this;
  856. StringBuilder name = new StringBuilder();
  857. // Add the full name of the containing namespace, followed by the "::"
  858. // separator, and the command name.
  859. if ( cmd != null )
  860. {
  861. if ( cmd.ns != null )
  862. {
  863. name.Append( cmd.ns.fullName );
  864. if ( cmd.ns != interp.globalNs )
  865. {
  866. name.Append( "::" );
  867. }
  868. }
  869. if ( cmd.table != null )
  870. {
  871. name.Append( cmd.hashKey );
  872. }
  873. }
  874. return name.ToString();
  875. }
  876. public int deleteCommand( string cmdName )
  877. // Name of command to remove.
  878. {
  879. WrappedCommand cmd;
  880. // Find the desired command and delete it.
  881. try
  882. {
  883. cmd = NamespaceCmd.findCommand( this, cmdName, null, 0 );
  884. }
  885. catch ( TclException e )
  886. {
  887. throw new TclRuntimeError( "unexpected TclException: " + e.Message, e );
  888. }
  889. if ( cmd == null )
  890. {
  891. return -1;
  892. }
  893. if ( cmd.deleteProc != null )
  894. cmd.deleteProc( ref cmd.deleteData );
  895. return deleteCommandFromToken( cmd );
  896. }
  897. protected internal int deleteCommandFromToken( WrappedCommand cmd )
  898. // Wrapper Token for command to delete.
  899. {
  900. if ( cmd == null )
  901. {
  902. return -1;
  903. }
  904. ImportRef ref_Renamed, nextRef;
  905. WrappedCommand importCmd;
  906. // The code here is tricky. We can't delete the hash table entry
  907. // before invoking the deletion callback because there are cases
  908. // where the deletion callback needs to invoke the command (e.g.
  909. // object systems such as OTcl). However, this means that the
  910. // callback could try to delete or rename the command. The deleted
  911. // flag allows us to detect these cases and skip nested deletes.
  912. if ( cmd.deleted )
  913. {
  914. // Another deletion is already in progress. Remove the hash
  915. // table entry now, but don't invoke a callback or free the
  916. // command structure.
  917. if ( (System.Object)cmd.hashKey != null && cmd.table != null )
  918. {
  919. SupportClass.HashtableRemove( cmd.table, cmd.hashKey );
  920. cmd.table = null;
  921. cmd.hashKey = null;
  922. }
  923. return 0;
  924. }
  925. cmd.deleted = true;
  926. if ( cmd.cmd is CommandWithDispose )
  927. {
  928. ( (CommandWithDispose)cmd.cmd ).disposeCmd();
  929. }
  930. if ( cmd.deleteProc != null )
  931. {
  932. cmd.deleteProc( ref cmd.objClientData );
  933. }
  934. // If this command was imported into other namespaces, then imported
  935. // commands were created that refer back to this command. Delete these
  936. // imported commands now.
  937. for ( ref_Renamed = cmd.importRef; ref_Renamed != null; ref_Renamed = nextRef )
  938. {
  939. nextRef = ref_Renamed.next;
  940. importCmd = ref_Renamed.importedCmd;
  941. deleteCommandFromToken( importCmd );
  942. }
  943. // FIXME : what does this mean? Is this a mistake in the C comment?
  944. // Don't use hPtr to delete the hash entry here, because it's
  945. // possible that the deletion callback renamed the command.
  946. // Instead, use cmdPtr->hptr, and make sure that no-one else
  947. // has already deleted the hash entry.
  948. if ( cmd.table != null )
  949. {
  950. SupportClass.HashtableRemove( cmd.table, cmd.hashKey );
  951. cmd.table = null;
  952. cmd.hashKey = null;
  953. }
  954. // Drop the reference to the Command instance inside the WrappedCommand
  955. cmd.cmd = null;
  956. // We do not need to cleanup the WrappedCommand because GC will get it.
  957. return 0;
  958. }
  959. protected internal void renameCommand( string oldName, string newName )
  960. {
  961. Interp interp = this;
  962. string newTail;
  963. NamespaceCmd.Namespace cmdNs, newNs;
  964. WrappedCommand cmd;
  965. Hashtable table, oldTable;
  966. string hashKey, oldHashKey;
  967. // Find the existing command. An error is returned if cmdName can't
  968. // be found.
  969. cmd = NamespaceCmd.findCommand( interp, oldName, null, 0 );
  970. if ( cmd == null )
  971. {
  972. throw new TclException( interp, "can't " + ( ( ( (System.Object)newName == null ) || ( newName.Length == 0 ) ) ? "delete" : "rename" ) + " \"" + oldName + "\": command doesn't exist" );
  973. }
  974. cmdNs = cmd.ns;
  975. // If the new command name is NULL or empty, delete the command. Do this
  976. // with Tcl_DeleteCommandFromToken, since we already have the command.
  977. if ( ( (System.Object)newName == null ) || ( newName.Length == 0 ) )
  978. {
  979. deleteCommandFromToken( cmd );
  980. return;
  981. }
  982. // Make sure that the destination command does not already exist.
  983. // The rename operation is like creating a command, so we should
  984. // automatically create the containing namespaces just like
  985. // Tcl_CreateCommand would.
  986. NamespaceCmd.Namespace[] newNsArr = new NamespaceCmd.Namespace[1];
  987. NamespaceCmd.Namespace[] dummyArr = new NamespaceCmd.Namespace[1];
  988. string[] newTailArr = new string[1];
  989. NamespaceCmd.getNamespaceForQualName( interp, newName, null, TCL.VarFlag.CREATE_NS_IF_UNKNOWN, newNsArr, dummyArr, dummyArr, newTailArr );
  990. newNs = newNsArr[0];
  991. newTail = newTailArr[0];
  992. if ( ( newNs == null ) || ( (System.Object)newTail == null ) )
  993. {
  994. throw new TclException( interp, "can't rename to \"" + newName + "\": bad command name" );
  995. }
  996. if ( newNs.cmdTable[newTail] != null )
  997. {
  998. throw new TclException( interp, "can't rename to \"" + newName + "\": command already exists" );
  999. }
  1000. // Warning: any changes done in the code here are likely
  1001. // to be needed in Tcl_HideCommand() code too.
  1002. // (until the common parts are extracted out) --dl
  1003. // Put the command in the new namespace so we can check for an alias
  1004. // loop. Since we are adding a new command to a namespace, we must
  1005. // handle any shadowing of the global commands that this might create.
  1006. oldTable = cmd.table;
  1007. oldHashKey = cmd.hashKey;
  1008. newNs.cmdTable.Add( newTail, cmd );
  1009. cmd.table = newNs.cmdTable;
  1010. cmd.hashKey = newTail;
  1011. cmd.ns = newNs;
  1012. // FIXME : this is a nasty hack that fixes renaming for Procedures
  1013. // that move from one namespace to another, but the real problem
  1014. // is that a rename does not work for Command instances in general
  1015. if ( cmd.cmd is Procedure )
  1016. {
  1017. Procedure p = (Procedure)cmd.cmd;
  1018. p.ns = cmd.ns;
  1019. }
  1020. // Now check for an alias loop. If we detect one, put everything back
  1021. // the way it was and report the error.
  1022. try
  1023. {
  1024. interp.preventAliasLoop( interp, cmd );
  1025. }
  1026. catch ( TclException e )
  1027. {
  1028. newNs.cmdTable.Remove( newTail );
  1029. cmd.table = oldTable;
  1030. cmd.hashKey = oldHashKey;
  1031. cmd.ns = cmdNs;
  1032. throw;
  1033. }
  1034. // The new command name is okay, so remove the command from its
  1035. // current namespace. This is like deleting the command, so bump
  1036. // the cmdEpoch to invalidate any cached references to the command.
  1037. SupportClass.HashtableRemove( oldTable, oldHashKey );
  1038. return;
  1039. }
  1040. internal void preventAliasLoop( Interp cmdInterp, WrappedCommand cmd )
  1041. {
  1042. // If we are not creating or renaming an alias, then it is
  1043. // always OK to create or rename the command.
  1044. if ( !( cmd.cmd is InterpAliasCmd ) )
  1045. {
  1046. return;
  1047. }
  1048. // OK, we are dealing with an alias, so traverse the chain of aliases.
  1049. // If we encounter the alias we are defining (or renaming to) any in
  1050. // the chain then we have a loop.
  1051. InterpAliasCmd alias = (InterpAliasCmd)cmd.cmd;
  1052. InterpAliasCmd nextAlias = alias;
  1053. while ( true )
  1054. {
  1055. // If the target of the next alias in the chain is the same as
  1056. // the source alias, we have a loop.
  1057. WrappedCommand aliasCmd = nextAlias.getTargetCmd( this );
  1058. if ( aliasCmd == null )
  1059. {
  1060. return;
  1061. }
  1062. if ( aliasCmd.cmd == cmd.cmd )
  1063. {
  1064. throw new TclException( this, "cannot define or rename alias \"" + alias.name + "\": would create a loop" );
  1065. }
  1066. // Otherwise, follow the chain one step further. See if the target
  1067. // command is an alias - if so, follow the loop to its target
  1068. // command. Otherwise we do not have a loop.
  1069. if ( !( aliasCmd.cmd is InterpAliasCmd ) )
  1070. {
  1071. return;
  1072. }
  1073. nextAlias = (InterpAliasCmd)aliasCmd.cmd;
  1074. }
  1075. }
  1076. public Command getCommand( string cmdName )
  1077. // String name of the command.
  1078. {
  1079. // Find the desired command and return it.
  1080. WrappedCommand cmd;
  1081. try
  1082. {
  1083. cmd = NamespaceCmd.findCommand( this, cmdName, null, 0 );
  1084. }
  1085. catch ( TclException e )
  1086. {
  1087. throw new TclRuntimeError( "unexpected TclException: " + e.Message, e );
  1088. }
  1089. return ( ( cmd == null ) ? null : cmd.cmd );
  1090. }
  1091. public WrappedCommand getObjCommand( string cmdName )
  1092. // String name of the command.
  1093. {
  1094. // Find the desired command and return it.
  1095. WrappedCommand cmd;
  1096. try
  1097. {
  1098. cmd = NamespaceCmd.findCommand( this, cmdName, null, 0 );
  1099. }
  1100. catch ( TclException e )
  1101. {
  1102. throw new TclRuntimeError( "unexpected TclException: " + e.Message, e );
  1103. }
  1104. return ( ( cmd == null ) ? null : cmd );
  1105. }
  1106. public static bool commandComplete( string inString )
  1107. // The string to check.
  1108. {
  1109. return Parser.commandComplete( inString, inString.Length );
  1110. }
  1111. /*-----------------------------------------------------------------
  1112. *
  1113. * EVAL
  1114. *
  1115. *-----------------------------------------------------------------
  1116. */
  1117. public TclObject getResult()
  1118. {
  1119. return m_result;
  1120. }
  1121. public void setResult( TclObject r )
  1122. // A Tcl Object to be set as the result.
  1123. {
  1124. if ( r == null )
  1125. {
  1126. throw new System.NullReferenceException( "Interp.setResult() called with null TclObject argument." );
  1127. }
  1128. if ( r == m_result )
  1129. {
  1130. // Setting to current value (including m_nullResult) is a no-op.
  1131. return;
  1132. }
  1133. if ( m_result != m_nullResult )
  1134. {
  1135. m_result.release();
  1136. }
  1137. m_result = r;
  1138. if ( m_result != m_nullResult )
  1139. {
  1140. m_result.preserve();
  1141. }
  1142. }
  1143. public void setResult( string r )
  1144. // A string result.
  1145. {
  1146. if ( (System.Object)r == null )
  1147. {
  1148. resetResult();
  1149. }
  1150. else
  1151. {
  1152. setResult( TclString.newInstance( r ) );
  1153. }
  1154. }
  1155. public void setResult( int r )
  1156. // An int result.
  1157. {
  1158. setResult( TclInteger.newInstance( r ) );
  1159. }
  1160. public void setResult( double r )
  1161. // A double result.
  1162. {
  1163. setResult( TclDouble.newInstance( r ) );
  1164. }
  1165. public void setResult( bool r )
  1166. // A boolean result.
  1167. {
  1168. setResult( TclBoolean.newInstance( r ) );
  1169. }
  1170. public void resetResult()
  1171. {
  1172. if ( m_result != m_nullResult )
  1173. {
  1174. m_result.release();
  1175. m_result = TclString.newInstance( "" ); //m_nullResult;
  1176. m_result.preserve();
  1177. if ( !m_nullResult.Shared )
  1178. {
  1179. throw new TclRuntimeError( "m_nullResult is not shared" );
  1180. }
  1181. }
  1182. errAlreadyLogged = false;
  1183. errInProgress = false;
  1184. errCodeSet = false;
  1185. returnCode = TCL.CompletionCode.OK;
  1186. }
  1187. public void appendElement( object Element )
  1188. {
  1189. TclObject result;
  1190. result = getResult();
  1191. if ( result.Shared )
  1192. {
  1193. result = result.duplicate();
  1194. }
  1195. TclList.append( this, result, TclObj.newInstance( Element ) );
  1196. setResult( result );
  1197. }
  1198. public void appendElement(
  1199. string Element )
  1200. {
  1201. TclObject result;
  1202. result = getResult();
  1203. if ( result.Shared )
  1204. {
  1205. result = result.duplicate();
  1206. }
  1207. TclList.append( this, result, TclString.newInstance( Element ) );
  1208. setResult( result );
  1209. }
  1210. public void eval( string inString, int flags )
  1211. {
  1212. int evalFlags = this.evalFlags;
  1213. this.evalFlags &= ~Parser.TCL_ALLOW_EXCEPTIONS;
  1214. CharPointer script = new CharPointer( inString );
  1215. try
  1216. {
  1217. Parser.eval2( this, script.array, script.index, script.length(), flags );
  1218. }
  1219. catch ( TclException e )
  1220. {
  1221. if ( nestLevel != 0 )
  1222. {
  1223. throw;
  1224. }
  1225. // Update the interpreter's evaluation level count. If we are again at
  1226. // the top level, process any unusual return code returned by the
  1227. // evaluated code. Note that we don't propagate an exception that
  1228. // has a TCL.CompletionCode.RETURN error code when updateReturnInfo() returns TCL.CompletionCode.OK.
  1229. TCL.CompletionCode result = e.getCompletionCode();
  1230. if ( result == TCL.CompletionCode.RETURN )
  1231. {
  1232. result = updateReturnInfo();
  1233. }
  1234. if ( result != TCL.CompletionCode.EXIT && result != TCL.CompletionCode.OK && result != TCL.CompletionCode.ERROR && ( evalFlags & Parser.TCL_ALLOW_EXCEPTIONS ) == 0 )
  1235. {
  1236. processUnexpectedResult( result );
  1237. }
  1238. if ( result != TCL.CompletionCode.OK )
  1239. {
  1240. e.setCompletionCode( result );
  1241. throw;
  1242. }
  1243. }
  1244. }
  1245. public void eval( string script )
  1246. {
  1247. eval( script, 0 );
  1248. }
  1249. public void eval( TclObject tobj, int flags )
  1250. {
  1251. eval( tobj.ToString(), flags );
  1252. }
  1253. public void recordAndEval( TclObject script, int flags )
  1254. {
  1255. // Append the script to the event list by calling "history add <script>".
  1256. // We call the eval method with the command of type TclObject, so that
  1257. // we don't have to deal with funny chars ("{}[]$\) in the script.
  1258. TclObject cmd = null;
  1259. try
  1260. {
  1261. cmd = TclList.newInstance();
  1262. TclList.append( this, cmd, TclString.newInstance( "history" ) );
  1263. TclList.append( this, cmd, TclString.newInstance( "add" ) );
  1264. TclList.append( this, cmd, script );
  1265. cmd.preserve();
  1266. eval( cmd, TCL.EVAL_GLOBAL );
  1267. }
  1268. catch ( System.Exception e )
  1269. {
  1270. }
  1271. finally
  1272. {
  1273. cmd.release();
  1274. }
  1275. // Execute the command.
  1276. if ( ( flags & TCL.NO_EVAL ) == 0 )
  1277. {
  1278. eval( script, flags & TCL.EVAL_GLOBAL );
  1279. }
  1280. }
  1281. public void evalFile( string sFilename )
  1282. {
  1283. string fileContent; // Contains the content of the file.
  1284. fileContent = readScriptFromFile( sFilename );
  1285. if ( (System.Object)fileContent == null )
  1286. {
  1287. throw new TclException( this, "couldn't read file \"" + sFilename + "\"" );
  1288. }
  1289. string oldScript = scriptFile;
  1290. scriptFile = sFilename;
  1291. try
  1292. {
  1293. pushDebugStack( sFilename, 1 );
  1294. eval( fileContent, 0 );
  1295. }
  1296. catch ( TclException e )
  1297. {
  1298. if ( e.getCompletionCode() == TCL.CompletionCode.ERROR )
  1299. {
  1300. addErrorInfo( "\n (file \"" + sFilename + "\" line " + errorLine + ")" );
  1301. }
  1302. throw;
  1303. }
  1304. finally
  1305. {
  1306. scriptFile = oldScript;
  1307. popDebugStack();
  1308. }
  1309. }
  1310. internal void evalURL( System.Uri context, string s )
  1311. {
  1312. string fileContent; // Contains the content of the file.
  1313. fileContent = readScriptFromURL( context, s );
  1314. if ( (System.Object)fileContent == null )
  1315. {
  1316. throw new TclException( this, "cannot read URL \"" + s + "\"" );
  1317. }
  1318. string oldScript = scriptFile;
  1319. scriptFile = s;
  1320. try
  1321. {
  1322. eval( fileContent, 0 );
  1323. }
  1324. finally
  1325. {
  1326. scriptFile = oldScript;
  1327. }
  1328. }
  1329. private string readScriptFromFile( string sFilename )
  1330. // The name of the file.
  1331. {
  1332. FileInfo sourceFile;
  1333. StreamReader fs;
  1334. try
  1335. {
  1336. sourceFile = FileUtil.getNewFileObj( this, sFilename );
  1337. }
  1338. catch ( TclException e )
  1339. {
  1340. resetResult();
  1341. return null;
  1342. }
  1343. catch ( FileNotFoundException e )
  1344. {
  1345. return null;
  1346. }
  1347. catch ( System.Security.SecurityException sec_e )
  1348. {
  1349. return null;
  1350. }
  1351. try
  1352. {
  1353. // HACK only UTF8 will be read
  1354. using ( fs = new StreamReader( sourceFile.FullName, System.Text.Encoding.UTF8 ) )
  1355. {
  1356. // read all an do the new line conversations
  1357. return fs.ReadToEnd().Replace( "\r\n", "\n" );
  1358. }
  1359. }
  1360. catch ( IOException )
  1361. {
  1362. return null;
  1363. }
  1364. }
  1365. private string readScriptFromURL( System.Uri context, string s )
  1366. {
  1367. Object content = null;
  1368. System.Uri url;
  1369. try
  1370. {
  1371. url = new System.Uri( context, s );
  1372. }
  1373. catch ( System.UriFormatException e )
  1374. {
  1375. return null;
  1376. }
  1377. try
  1378. {
  1379. // ATK content = url.getContent();
  1380. content = url.ToString();
  1381. }
  1382. catch ( System.Exception e )
  1383. {
  1384. Type jar_class;
  1385. try
  1386. {
  1387. jar_class = System.Type.GetType( "java.net.JarURLConnection" );
  1388. }
  1389. catch ( System.Exception e2 )
  1390. {
  1391. return null;
  1392. }
  1393. Object jar;
  1394. try
  1395. {
  1396. jar = (System.Net.HttpWebRequest)System.Net.WebRequest.Create( url );
  1397. }
  1398. catch ( IOException e2 )
  1399. {
  1400. return null;
  1401. }
  1402. if ( jar == null )
  1403. {
  1404. return null;
  1405. }
  1406. // We must call JarURLConnection.getInputStream() dynamically
  1407. // Because the class JarURLConnection does not exist in JDK1.1
  1408. try
  1409. {
  1410. System.Reflection.MethodInfo m = jar_class.GetMethod( "openConnection", (System.Type[])null );
  1411. content = m.Invoke( jar, (System.Object[])null );
  1412. }
  1413. catch ( System.Exception e2 )
  1414. {
  1415. return null;
  1416. }
  1417. }
  1418. // HACK
  1419. // catch (IOException e)
  1420. // {
  1421. // return null;
  1422. // }
  1423. // catch (System.Security.SecurityException e)
  1424. // {
  1425. // return null;
  1426. // }
  1427. if ( content is string )
  1428. {
  1429. return (string)content;
  1430. }
  1431. else if ( content is Stream )
  1432. {
  1433. // FIXME : use custom stream handler
  1434. Stream fs = (Stream)content;
  1435. try
  1436. {
  1437. // FIXME : read does not check return values
  1438. long available;
  1439. available = fs.Length - fs.Position;
  1440. byte[] charArray = new byte[(int)available];
  1441. SupportClass.ReadInput( fs, ref charArray, 0, charArray.Length );
  1442. return new string( SupportClass.ToCharArray( charArray ) );
  1443. }
  1444. catch ( IOException e2 )
  1445. {
  1446. return null;
  1447. }
  1448. finally
  1449. {
  1450. closeInputStream( fs );
  1451. }
  1452. }
  1453. else
  1454. {
  1455. return null;
  1456. }
  1457. }
  1458. private void closeInputStream( Stream fs )
  1459. {
  1460. try
  1461. {
  1462. fs.Close();
  1463. }
  1464. catch ( IOException e )
  1465. {
  1466. ;
  1467. }
  1468. }
  1469. internal void evalResource( string resName )
  1470. {
  1471. // Stream stream = null;
  1472. //
  1473. // try
  1474. // {
  1475. //
  1476. // stream = typeof(Interp).getResourceAsStream(resName);
  1477. // }
  1478. // catch (System.Security.SecurityException e2)
  1479. // {
  1480. // // This catch is necessary if Jacl is to work in an applet
  1481. // // at all. Note that java::new will not work from within Jacl
  1482. // // in an applet.
  1483. //
  1484. // System.Console.Error.WriteLine("evalResource: Ignoring SecurityException, " + "it is likely we are running in an applet: " + "cannot read resource \"" + resName + "\"" + e2);
  1485. //
  1486. // return ;
  1487. // }
  1488. //
  1489. // if (stream == null)
  1490. // {
  1491. // throw new TclException(this, "cannot read resource \"" + resName + "\"");
  1492. // }
  1493. //
  1494. // try
  1495. // {
  1496. // // FIXME : ugly JDK 1.2 only hack
  1497. // // Ugly workaround for compressed files BUG in JDK1.2
  1498. // // this bug first showed up in JDK1.2beta4. I have sent
  1499. // // a number of emails to Sun but they have deemed this a "feature"
  1500. // // of 1.2. This is flat out wrong but I do not seem to change thier
  1501. // // minds. Because of this, there is no way to do non blocking IO
  1502. // // on a compressed Stream in Java. (mo)
  1503. //
  1504. //
  1505. // if (System_Renamed.getProperty("java.version").StartsWith("1.2") && stream.GetType().FullName.Equals("java.util.zip.ZipFile$1"))
  1506. // {
  1507. //
  1508. MemoryStream baos = new MemoryStream( 1024 );
  1509. byte[] buffer = new byte[1024];
  1510. // int numRead;
  1511. //
  1512. // // Read all data from the stream into a resizable buffer
  1513. // while ((numRead = SupportClass.ReadInput(stream, ref buffer, 0, buffer.Length)) != - 1)
  1514. // {
  1515. // baos.Write(SupportClass.ToByteArray(buffer), 0, numRead);
  1516. // }
  1517. //
  1518. // // Convert bytes into a String and eval them
  1519. // eval(new string(SupportClass.ToCharArray(SupportClass.ToByteArray(SupportClass.ToSByteArray(baos.ToArray())))), 0);
  1520. // }
  1521. // else
  1522. // {
  1523. // // Other systems do not need the compressed jar hack
  1524. //
  1525. // long available;
  1526. // available = stream.Length - stream.Position;
  1527. // int num = (int) available;
  1528. // byte[] byteArray = new byte[num];
  1529. // int offset = 0;
  1530. // while (num > 0)
  1531. // {
  1532. // int readLen = SupportClass.ReadInput(stream, ref byteArray, offset, num);
  1533. // offset += readLen;
  1534. // num -= readLen;
  1535. // }
  1536. //
  1537. // eval(new string(SupportClass.ToCharArray(SupportClass.ToByteArray(byteArray))), 0);
  1538. // }
  1539. // }
  1540. // catch (IOException e)
  1541. // {
  1542. // return ;
  1543. // }
  1544. // finally
  1545. // {
  1546. // closeInputStream(stream);
  1547. // }
  1548. }
  1549. internal static BackSlashResult backslash( string s, int i, int len )
  1550. {
  1551. CharPointer script = new CharPointer( s.Substring( 0, ( len ) - ( 0 ) ) );
  1552. script.index = i;
  1553. return Parser.backslash( script.array, script.index );
  1554. }
  1555. public void setErrorCode( TclObject code )
  1556. // The errorCode object.
  1557. {
  1558. try
  1559. {
  1560. setVar( "errorCode", null, code, TCL.VarFlag.GLOBAL_ONLY );
  1561. errCodeSet = true;
  1562. }
  1563. catch ( TclException excp )
  1564. {
  1565. // Ignore any TclException's, possibly caused by variable traces on
  1566. // the errorCode variable. This is compatible with the behavior of
  1567. // the Tcl C API.
  1568. }
  1569. }
  1570. public void addErrorInfo( string message )
  1571. // The message to record.
  1572. {
  1573. if ( !errInProgress )
  1574. {
  1575. errInProgress = true;
  1576. try
  1577. {
  1578. setVar( "errorInfo", null, getResult().ToString(), TCL.VarFlag.GLOBAL_ONLY );
  1579. }
  1580. catch ( TclException e1 )
  1581. {
  1582. // Ignore (see try-block above).
  1583. }
  1584. // If the errorCode variable wasn't set by the code
  1585. // that generated the error, set it to "NONE".
  1586. if ( !errCodeSet )
  1587. {
  1588. try
  1589. {
  1590. setVar( "errorCode", null, "NONE", TCL.VarFlag.GLOBAL_ONLY );
  1591. }
  1592. catch ( TclException e1 )
  1593. {
  1594. // Ignore (see try-block above).
  1595. }
  1596. }
  1597. }
  1598. try
  1599. {
  1600. setVar( "errorInfo", null, message, TCL.VarFlag.APPEND_VALUE | TCL.VarFlag.GLOBAL_ONLY );
  1601. }
  1602. catch ( TclException e1 )
  1603. {
  1604. // Ignore (see try-block above).
  1605. }
  1606. }
  1607. internal void processUnexpectedResult( TCL.CompletionCode returnCode )
  1608. {
  1609. resetResult();
  1610. if ( returnCode == TCL.CompletionCode.BREAK )
  1611. {
  1612. throw new TclException( this, "invoked \"break\" outside of a loop" );
  1613. }
  1614. else if ( returnCode == TCL.CompletionCode.CONTINUE )
  1615. {
  1616. throw new TclException( this, "invoked \"continue\" outside of a loop" );
  1617. }
  1618. else
  1619. {
  1620. throw new TclException( this, "command returned bad code: " + returnCode );
  1621. }
  1622. }
  1623. public TCL.CompletionCode updateReturnInfo()
  1624. {
  1625. TCL.CompletionCode code;
  1626. code = returnCode;
  1627. returnCode = TCL.CompletionCode.OK;
  1628. if ( code == TCL.CompletionCode.ERROR )
  1629. {
  1630. try
  1631. {
  1632. setVar( "errorCode", null, ( (System.Object)errorCode != null ) ? errorCode : "NONE", TCL.VarFlag.GLOBAL_ONLY );
  1633. }
  1634. catch ( TclException e )
  1635. {
  1636. // An error may happen during a trace to errorCode. We ignore it.
  1637. // This may leave error messages inside Interp.result (which
  1638. // is compatible with Tcl 8.0 behavior.
  1639. }
  1640. errCodeSet = true;
  1641. if ( (System.Object)errorInfo != null )
  1642. {
  1643. try
  1644. {
  1645. setVar( "errorInfo", null, errorInfo, TCL.VarFlag.GLOBAL_ONLY );
  1646. }
  1647. catch ( TclException e )
  1648. {
  1649. // An error may happen during a trace to errorInfo. We
  1650. // ignore it. This may leave error messages inside
  1651. // Interp.result (which is compatible with Tcl 8.0
  1652. // behavior.
  1653. }
  1654. errInProgress = true;
  1655. }
  1656. }
  1657. return code;
  1658. }
  1659. protected internal CallFrame newCallFrame( Procedure proc, TclObject[] objv )
  1660. {
  1661. return new CallFrame( this, proc, objv );
  1662. }
  1663. protected internal CallFrame newCallFrame()
  1664. {
  1665. return new CallFrame( this );
  1666. }
  1667. internal FileInfo getWorkingDir()
  1668. {
  1669. if ( workingDir == null )
  1670. {
  1671. try
  1672. {
  1673. string dirName = getVar( "env", "HOME", 0 ).ToString();
  1674. workingDir = FileUtil.getNewFileObj( this, dirName );
  1675. }
  1676. catch ( TclException e )
  1677. {
  1678. resetResult();
  1679. }
  1680. workingDir = new FileInfo( Util.tryGetSystemProperty( "user.home", "." ) );
  1681. }
  1682. return workingDir;
  1683. }
  1684. internal void setWorkingDir( string dirName )
  1685. {
  1686. FileInfo dirObj = FileUtil.getNewFileObj( this, dirName );
  1687. // Use the canonical name of the path, if possible.
  1688. try
  1689. {
  1690. dirObj = new FileInfo( dirObj.FullName );
  1691. }
  1692. catch ( IOException e )
  1693. {
  1694. }
  1695. if ( Directory.Exists( dirObj.FullName ) )
  1696. {
  1697. workingDir = dirObj;
  1698. }
  1699. else
  1700. {
  1701. throw new TclException( this, "couldn't change working directory to \"" + dirObj.Name + "\": no such file or directory" );
  1702. }
  1703. }
  1704. public Notifier getNotifier()
  1705. {
  1706. return notifier;
  1707. }
  1708. public void pkgProvide( string name, string version )
  1709. {
  1710. PackageCmd.pkgProvide( this, name, version );
  1711. }
  1712. public string pkgRequire( string pkgname, string version, bool exact )
  1713. {
  1714. return PackageCmd.pkgRequire( this, pkgname, version, exact );
  1715. }
  1716. /*
  1717. * Debugging API.
  1718. *
  1719. * The following section defines two debugging API functions for
  1720. * logging information about the point of execution of Tcl scripts:
  1721. *
  1722. * - pushDebugStack() is called when a procedure body is
  1723. * executed, or when a file is source'd.
  1724. * - popDebugStack() is called when the flow of control is about
  1725. * to return from a procedure body, or from a source'd file.
  1726. *
  1727. * Two other API functions are used to determine the current point of
  1728. * execution:
  1729. *
  1730. * - getScriptFile() returns the script file current being executed.
  1731. * - getArgLineNumber(i) returns the line number of the i-th argument
  1732. * of the current command.
  1733. *
  1734. * Note: The point of execution is automatically maintained for
  1735. * control structures such as while, if, for and foreach,
  1736. * as long as they use Interp.eval(argv[?]) to evaluate control
  1737. * blocks.
  1738. *
  1739. * The case and switch commands need to set dbg.cmdLine explicitly
  1740. * because they may evaluate control blocks that are not elements
  1741. * inside the argv[] array. ** This feature not yet implemented. **
  1742. *
  1743. * The proc command needs to call getScriptFile() and
  1744. * getArgLineNumber(3) to find out the location of the proc
  1745. * body.
  1746. *
  1747. * The debugging API functions in the Interp class are just dummy stub
  1748. * functions. These functions are usually implemented in a subclass of
  1749. * Interp (e.g. DbgInterp) that has real debugging support.
  1750. *
  1751. */
  1752. protected internal DebugInfo dbg;
  1753. /// <summary> Initialize the debugging information.</summary>
  1754. /// <returns> a DebugInfo object used by Interp in non-debugging mode.
  1755. /// </returns>
  1756. protected internal DebugInfo initDebugInfo()
  1757. {
  1758. return new DebugInfo( null, 1 );
  1759. }
  1760. /// <summary> Add more more level at the top of the debug stack.
  1761. ///
  1762. /// </summary>
  1763. /// <param name="fileName">the filename for the new stack level
  1764. /// </param>
  1765. /// <param name="lineNumber">the line number at which the execution of the
  1766. /// new stack level begins.
  1767. /// </param>
  1768. internal void pushDebugStack( string fileName, int lineNumber )
  1769. {
  1770. // do nothing.
  1771. }
  1772. /// <summary> Remove the top-most level of the debug stack.</summary>
  1773. internal void popDebugStack()
  1774. {
  1775. // do nothing
  1776. }
  1777. /// <summary> Returns the line number where the given command argument begins. E.g, if
  1778. /// the following command is at line 10:
  1779. ///
  1780. /// foo {a
  1781. /// b } c
  1782. ///
  1783. /// getArgLine(0) = 10
  1784. /// getArgLine(1) = 10
  1785. /// getArgLine(2) = 11
  1786. ///
  1787. /// </summary>
  1788. /// <param name="index">specifies an argument.
  1789. /// </param>
  1790. /// <returns> the line number of the given argument.
  1791. /// </returns>
  1792. internal int getArgLineNumber( int index )
  1793. {
  1794. return 0;
  1795. }
  1796. internal void transferResult( Interp sourceInterp, TCL.CompletionCode result )
  1797. {
  1798. if ( sourceInterp == this )
  1799. {
  1800. return;
  1801. }
  1802. if ( result == TCL.CompletionCode.ERROR )
  1803. {
  1804. TclObject obj;
  1805. // An error occurred, so transfer error information from the source
  1806. // interpreter to the target interpreter. Setting the flags tells
  1807. // the target interp that it has inherited a partial traceback
  1808. // chain, not just a simple error message.
  1809. if ( !sourceInterp.errAlreadyLogged )
  1810. {
  1811. sourceInterp.addErrorInfo( "" );
  1812. }
  1813. sourceInterp.errAlreadyLogged = true;
  1814. resetResult();
  1815. obj = sourceInterp.getVar( "errorInfo", TCL.VarFlag.GLOBAL_ONLY );
  1816. setVar( "errorInfo", obj, TCL.VarFlag.GLOBAL_ONLY );
  1817. obj = sourceInterp.getVar( "errorCode", TCL.VarFlag.GLOBAL_ONLY );
  1818. setVar( "errorCode", obj, TCL.VarFlag.GLOBAL_ONLY );
  1819. errInProgress = true;
  1820. errCodeSet = true;
  1821. }
  1822. returnCode = result;
  1823. setResult( sourceInterp.getResult() );
  1824. sourceInterp.resetResult();
  1825. if ( result != TCL.CompletionCode.OK )
  1826. {
  1827. throw new TclException( this, getResult().ToString(), result );
  1828. }
  1829. }
  1830. internal void hideCommand( string cmdName, string hiddenCmdToken )
  1831. {
  1832. WrappedCommand cmd;
  1833. if ( deleted )
  1834. {
  1835. // The interpreter is being deleted. Do not create any new
  1836. // structures, because it is not safe to modify the interpreter.
  1837. return;
  1838. }
  1839. // Disallow hiding of commands that are currently in a namespace or
  1840. // renaming (as part of hiding) into a namespace.
  1841. //
  1842. // (because the current implementation with a single global table
  1843. // and the needed uniqueness of names cause problems with namespaces)
  1844. //
  1845. // we don't need to check for "::" in cmdName because the real check is
  1846. // on the nsPtr below.
  1847. //
  1848. // hiddenCmdToken is just a string which is not interpreted in any way.
  1849. // It may contain :: but the string is not interpreted as a namespace
  1850. // qualifier command name. Thus, hiding foo::bar to foo::bar and then
  1851. // trying to expose or invoke ::foo::bar will NOT work; but if the
  1852. // application always uses the same strings it will get consistent
  1853. // behavior.
  1854. //
  1855. // But as we currently limit ourselves to the global namespace only
  1856. // for the source, in order to avoid potential confusion,
  1857. // lets prevent "::" in the token too. --dl
  1858. if ( hiddenCmdToken.IndexOf( "::" ) >= 0 )
  1859. {
  1860. throw new TclException( this, "cannot use namespace qualifiers as " + "hidden commandtoken (rename)" );
  1861. }
  1862. // Find the command to hide. An error is returned if cmdName can't
  1863. // be found. Look up the command only from the global namespace.
  1864. // Full path of the command must be given if using namespaces.
  1865. cmd = NamespaceCmd.findCommand( this, cmdName, null, TCL.VarFlag.LEAVE_ERR_MSG | TCL.VarFlag.GLOBAL_ONLY );
  1866. // Check that the command is really in global namespace
  1867. if ( cmd.ns != globalNs )
  1868. {
  1869. throw new TclException( this, "can only hide global namespace commands" + " (use rename then hide)" );
  1870. }
  1871. // Initialize the hidden command table if necessary.
  1872. if ( hiddenCmdTable == null )
  1873. {
  1874. hiddenCmdTable = new Hashtable();
  1875. }
  1876. // It is an error to move an exposed command to a hidden command with
  1877. // hiddenCmdToken if a hidden command with the name hiddenCmdToken already
  1878. // exists.
  1879. if ( hiddenCmdTable.ContainsKey( hiddenCmdToken ) )
  1880. {
  1881. throw new TclException( this, "hidden command named \"" + hiddenCmdToken + "\" already exists" );
  1882. }
  1883. // Nb : This code is currently 'like' a rename to a specialy set apart
  1884. // name table. Changes here and in TclRenameCommand must
  1885. // be kept in synch untill the common parts are actually
  1886. // factorized out.
  1887. // Remove the hash entry for the command from the interpreter command
  1888. // table. This is like deleting the command, so bump its command epoch;
  1889. // this invalidates any cached references that point to the command.
  1890. if ( cmd.table.ContainsKey( cmd.hashKey ) )
  1891. {
  1892. SupportClass.HashtableRemove( cmd.table, cmd.hashKey );
  1893. }
  1894. // Now link the hash table entry with the command structure.
  1895. // We ensured above that the nsPtr was right.
  1896. cmd.table = hiddenCmdTable;
  1897. cmd.hashKey = hiddenCmdToken;
  1898. SupportClass.PutElement( hiddenCmdTable, hiddenCmdToken, cmd );
  1899. }
  1900. internal void exposeCommand( string hiddenCmdToken, string cmdName )
  1901. {
  1902. WrappedCommand cmd;
  1903. if ( deleted )
  1904. {
  1905. // The interpreter is being deleted. Do not create any new
  1906. // structures, because it is not safe to modify the interpreter.
  1907. return;
  1908. }
  1909. // Check that we have a regular name for the command
  1910. // (that the user is not trying to do an expose and a rename
  1911. // (to another namespace) at the same time)
  1912. if ( cmdName.IndexOf( "::" ) >= 0 )
  1913. {
  1914. throw new TclException( this, "can not expose to a namespace " + "(use expose to toplevel, then rename)" );
  1915. }
  1916. // Get the command from the hidden command table:
  1917. if ( hiddenCmdTable == null || !hiddenCmdTable.ContainsKey( hiddenCmdToken ) )
  1918. {
  1919. throw new TclException( this, "unknown hidden command \"" + hiddenCmdToken + "\"" );
  1920. }
  1921. cmd = (WrappedCommand)hiddenCmdTable[hiddenCmdToken];
  1922. // Check that we have a true global namespace
  1923. // command (enforced by Tcl_HideCommand() but let's double
  1924. // check. (If it was not, we would not really know how to
  1925. // handle it).
  1926. if ( cmd.ns != globalNs )
  1927. {
  1928. // This case is theoritically impossible,
  1929. // we might rather panic() than 'nicely' erroring out ?
  1930. throw new TclException( this, "trying to expose " + "a non global command name space command" );
  1931. }
  1932. // This is the global table
  1933. NamespaceCmd.Namespace ns = cmd.ns;
  1934. // It is an error to overwrite an existing exposed command as a result
  1935. // of exposing a previously hidden command.
  1936. if ( ns.cmdTable.ContainsKey( cmdName ) )
  1937. {
  1938. throw new TclException( this, "exposed command \"" + cmdName + "\" already exists" );
  1939. }
  1940. // Remove the hash entry for the command from the interpreter hidden
  1941. // command table.
  1942. if ( (System.Object)cmd.hashKey != null )
  1943. {
  1944. SupportClass.HashtableRemove( cmd.table, cmd.hashKey );
  1945. cmd.table = ns.cmdTable;
  1946. cmd.hashKey = cmdName;
  1947. }
  1948. // Now link the hash table entry with the command structure.
  1949. // This is like creating a new command, so deal with any shadowing
  1950. // of commands in the global namespace.
  1951. ns.cmdTable.Add( cmdName, cmd );
  1952. // Not needed as we are only in the global namespace
  1953. // (but would be needed again if we supported namespace command hiding)
  1954. // TclResetShadowedCmdRefs(interp, cmdPtr);
  1955. }
  1956. internal void hideUnsafeCommands()
  1957. {
  1958. for ( int ix = 0; ix < unsafeCmds.Length; ix++ )
  1959. {
  1960. try
  1961. {
  1962. hideCommand( unsafeCmds[ix], unsafeCmds[ix] );
  1963. }
  1964. catch ( TclException e )
  1965. {
  1966. if ( !e.Message.StartsWith( "unknown command" ) )
  1967. {
  1968. throw;
  1969. }
  1970. }
  1971. }
  1972. }
  1973. internal TCL.CompletionCode invokeGlobal( TclObject[] objv, int flags )
  1974. {
  1975. CallFrame savedVarFrame = varFrame;
  1976. try
  1977. {
  1978. varFrame = null;
  1979. return invoke( objv, flags );
  1980. }
  1981. finally
  1982. {
  1983. varFrame = savedVarFrame;
  1984. }
  1985. }
  1986. internal TCL.CompletionCode invoke( TclObject[] objv, int flags )
  1987. {
  1988. if ( ( objv.Length < 1 ) || ( objv == null ) )
  1989. {
  1990. throw new TclException( this, "illegal argument vector" );
  1991. }
  1992. string cmdName = objv[0].ToString();
  1993. WrappedCommand cmd;
  1994. TclObject[] localObjv = null;
  1995. if ( ( flags & INVOKE_HIDDEN ) != 0 )
  1996. {
  1997. // We never invoke "unknown" for hidden commands.
  1998. if ( hiddenCmdTable == null || !hiddenCmdTable.ContainsKey( cmdName ) )
  1999. {
  2000. throw new TclException( this, "invalid hidden command name \"" + cmdName + "\"" );
  2001. }
  2002. cmd = (WrappedCommand)hiddenCmdTable[cmdName];
  2003. }
  2004. else
  2005. {
  2006. cmd = NamespaceCmd.findCommand( this, cmdName, null, TCL.VarFlag.GLOBAL_ONLY );
  2007. if ( cmd == null )
  2008. {
  2009. if ( ( flags & INVOKE_NO_UNKNOWN ) == 0 )
  2010. {
  2011. cmd = NamespaceCmd.findCommand( this, "unknown", null, TCL.VarFlag.GLOBAL_ONLY );
  2012. if ( cmd != null )
  2013. {
  2014. localObjv = new TclObject[objv.Length + 1];
  2015. localObjv[0] = TclString.newInstance( "unknown" );
  2016. localObjv[0].preserve();
  2017. for ( int i = 0; i < objv.Length; i++ )
  2018. {
  2019. localObjv[i + 1] = objv[i];
  2020. }
  2021. objv = localObjv;
  2022. }
  2023. }
  2024. // Check again if we found the command. If not, "unknown" is
  2025. // not present and we cannot help, or the caller said not to
  2026. // call "unknown" (they specified TCL_INVOKE_NO_UNKNOWN).
  2027. if ( cmd == null )
  2028. {
  2029. throw new TclException( this, "invalid command name \"" + cmdName + "\"" );
  2030. }
  2031. }
  2032. }
  2033. // Invoke the command procedure. First reset the interpreter's string
  2034. // and object results to their default empty values since they could
  2035. // have gotten changed by earlier invocations.
  2036. resetResult();
  2037. cmdCount++;
  2038. TCL.CompletionCode result = TCL.CompletionCode.OK;
  2039. try
  2040. {
  2041. cmd.cmd.cmdProc( this, objv );
  2042. }
  2043. catch ( TclException e )
  2044. {
  2045. result = e.getCompletionCode();
  2046. }
  2047. // If we invoke a procedure, which was implemented as AutoloadStub,
  2048. // it was entered into the ordinary cmdTable. But here we know
  2049. // for sure, that this command belongs into the hiddenCmdTable.
  2050. // So if we can find an entry in cmdTable with the cmdName, just
  2051. // move it into the hiddenCmdTable.
  2052. if ( ( flags & INVOKE_HIDDEN ) != 0 )
  2053. {
  2054. cmd = NamespaceCmd.findCommand( this, cmdName, null, TCL.VarFlag.GLOBAL_ONLY );
  2055. if ( cmd != null )
  2056. {
  2057. // Basically just do the same as in hideCommand...
  2058. SupportClass.HashtableRemove( cmd.table, cmd.hashKey );
  2059. cmd.table = hiddenCmdTable;
  2060. cmd.hashKey = cmdName;
  2061. SupportClass.PutElement( hiddenCmdTable, cmdName, cmd );
  2062. }
  2063. }
  2064. // If an error occurred, record information about what was being
  2065. // executed when the error occurred.
  2066. if ( ( result == TCL.CompletionCode.ERROR ) && ( ( flags & INVOKE_NO_TRACEBACK ) == 0 ) && !errAlreadyLogged )
  2067. {
  2068. StringBuilder ds;
  2069. if ( errInProgress )
  2070. {
  2071. ds = new StringBuilder( "\n while invoking\n\"" );
  2072. }
  2073. else
  2074. {
  2075. ds = new StringBuilder( "\n invoked from within\n\"" );
  2076. }
  2077. for ( int i = 0; i < objv.Length; i++ )
  2078. {
  2079. ds.Append( objv[i].ToString() );
  2080. if ( i < ( objv.Length - 1 ) )
  2081. {
  2082. ds.Append( " " );
  2083. }
  2084. else if ( ds.Length > 100 )
  2085. {
  2086. ds.Append( "..." );
  2087. break;
  2088. }
  2089. }
  2090. ds.Append( "\"" );
  2091. addErrorInfo( ds.ToString() );
  2092. errInProgress = true;
  2093. }
  2094. // Free any locally allocated storage used to call "unknown".
  2095. if ( localObjv != null )
  2096. {
  2097. localObjv[0].release();
  2098. }
  2099. return result;
  2100. }
  2101. internal void allowExceptions()
  2102. {
  2103. evalFlags |= Parser.TCL_ALLOW_EXCEPTIONS;
  2104. }
  2105. internal class ResolverScheme
  2106. {
  2107. private void InitBlock( Interp enclosingInstance )
  2108. {
  2109. this.enclosingInstance = enclosingInstance;
  2110. }
  2111. private Interp enclosingInstance;
  2112. public Interp Enclosing_Instance
  2113. {
  2114. get
  2115. {
  2116. return enclosingInstance;
  2117. }
  2118. }
  2119. internal string name; // Name identifying this scheme.
  2120. internal Resolver resolver;
  2121. internal ResolverScheme( Interp enclosingInstance, string name, Resolver resolver )
  2122. {
  2123. InitBlock( enclosingInstance );
  2124. this.name = name;
  2125. this.resolver = resolver;
  2126. }
  2127. }
  2128. public void addInterpResolver( string name, Resolver resolver )
  2129. // Object to resolve commands/variables.
  2130. {
  2131. IEnumerator enum_Renamed;
  2132. ResolverScheme res;
  2133. // Look for an existing scheme with the given name.
  2134. // If found, then replace its rules.
  2135. if ( resolvers != null )
  2136. {
  2137. for ( enum_Renamed = resolvers.GetEnumerator(); enum_Renamed.MoveNext(); )
  2138. {
  2139. res = (ResolverScheme)enum_Renamed.Current;
  2140. if ( name.Equals( res.name ) )
  2141. {
  2142. res.resolver = resolver;
  2143. return;
  2144. }
  2145. }
  2146. }
  2147. if ( resolvers == null )
  2148. {
  2149. resolvers = new ArrayList( 10 );
  2150. }
  2151. // Otherwise, this is a new scheme. Add it to the FRONT
  2152. // of the linked list, so that it overrides existing schemes.
  2153. res = new ResolverScheme( this, name, resolver );
  2154. resolvers.Insert( 0, res );
  2155. }
  2156. public Resolver getInterpResolver( string name )
  2157. // Look for a scheme with this name.
  2158. {
  2159. //IEnumerator enum;
  2160. // Look for an existing scheme with the given name. If found,
  2161. // then return pointers to its procedures.
  2162. if ( resolvers != null )
  2163. {
  2164. foreach ( ResolverScheme res in resolvers )
  2165. {
  2166. if ( name.Equals( res.name ) )
  2167. {
  2168. return res.resolver;
  2169. }
  2170. }
  2171. }
  2172. return null;
  2173. }
  2174. internal bool removeInterpResolver( string name )
  2175. // Name of the scheme to be removed.
  2176. {
  2177. ResolverScheme res;
  2178. IEnumerator enum_Renamed;
  2179. bool found = false;
  2180. // Look for an existing scheme with the given name.
  2181. if ( resolvers != null )
  2182. {
  2183. enum_Renamed = resolvers.GetEnumerator();
  2184. while ( !found && enum_Renamed.MoveNext() )
  2185. {
  2186. res = (ResolverScheme)enum_Renamed.Current;
  2187. if ( name.Equals( res.name ) )
  2188. {
  2189. found = true;
  2190. }
  2191. }
  2192. }
  2193. // If we found the scheme, delete it.
  2194. if ( found )
  2195. {
  2196. SupportClass.VectorRemoveElement( resolvers, name );
  2197. }
  2198. return found;
  2199. }
  2200. } // end Interp
  2201. }