PageRenderTime 63ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/mcs/tools/csharp/repl.cs

https://bitbucket.org/steenlund/mono-2.6.7-for-amiga
C# | 693 lines | 512 code | 122 blank | 59 comment | 87 complexity | d4f66c6d87fa9219c31299013c30a398 MD5 | raw file
Possible License(s): LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0, LGPL-2.1
  1. //
  2. // repl.cs: Support for using the compiler in interactive mode (read-eval-print loop)
  3. //
  4. // Authors:
  5. // Miguel de Icaza (miguel@gnome.org)
  6. //
  7. // Dual licensed under the terms of the MIT X11 or GNU GPL
  8. //
  9. // Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
  10. // Copyright 2004, 2005, 2006, 2007, 2008 Novell, Inc
  11. //
  12. //
  13. // TODO:
  14. // Do not print results in Evaluate, do that elsewhere in preparation for Eval refactoring.
  15. // Driver.PartialReset should not reset the coretypes, nor the optional types, to avoid
  16. // computing that on every call.
  17. //
  18. using System;
  19. using System.IO;
  20. using System.Text;
  21. using System.Globalization;
  22. using System.Collections;
  23. using System.Reflection;
  24. using System.Reflection.Emit;
  25. using System.Threading;
  26. using System.Net;
  27. using System.Net.Sockets;
  28. using System.Collections.Generic;
  29. using Mono.CSharp;
  30. using Mono.Attach;
  31. namespace Mono {
  32. public class Driver {
  33. static int Main (string [] args)
  34. {
  35. if (args.Length > 0 && args [0] == "--attach") {
  36. new ClientCSharpShell (Int32.Parse (args [1])).Run (null);
  37. return 0;
  38. } else if (args.Length > 0 && args [0].StartsWith ("--agent:")) {
  39. new CSharpAgent (args [0]);
  40. return 0;
  41. } else {
  42. string [] startup_files;
  43. try {
  44. startup_files = Evaluator.InitAndGetStartupFiles (args);
  45. Evaluator.InteractiveBaseClass = typeof (InteractiveBaseShell);
  46. } catch {
  47. return 1;
  48. }
  49. return new CSharpShell ().Run (startup_files);
  50. }
  51. }
  52. }
  53. public class InteractiveBaseShell : InteractiveBase {
  54. static bool tab_at_start_completes;
  55. static InteractiveBaseShell ()
  56. {
  57. tab_at_start_completes = false;
  58. }
  59. internal static Mono.Terminal.LineEditor Editor;
  60. public static bool TabAtStartCompletes {
  61. get {
  62. return tab_at_start_completes;
  63. }
  64. set {
  65. tab_at_start_completes = value;
  66. if (Editor != null)
  67. Editor.TabAtStartCompletes = value;
  68. }
  69. }
  70. public static new string help {
  71. get {
  72. return InteractiveBase.help +
  73. " TabAtStartCompletes - Whether tab will complete even on emtpy lines\n";
  74. }
  75. }
  76. }
  77. public class CSharpShell {
  78. static bool isatty = true;
  79. string [] startup_files;
  80. Mono.Terminal.LineEditor editor;
  81. bool dumb;
  82. protected virtual void ConsoleInterrupt (object sender, ConsoleCancelEventArgs a)
  83. {
  84. // Do not about our program
  85. a.Cancel = true;
  86. Mono.CSharp.Evaluator.Interrupt ();
  87. }
  88. void SetupConsole ()
  89. {
  90. string term = Environment.GetEnvironmentVariable ("TERM");
  91. dumb = term == "dumb" || term == null || isatty == false;
  92. editor = new Mono.Terminal.LineEditor ("csharp", 300);
  93. InteractiveBaseShell.Editor = editor;
  94. editor.AutoCompleteEvent += delegate (string s, int pos){
  95. string prefix = null;
  96. string complete = s.Substring (0, pos);
  97. string [] completions = Evaluator.GetCompletions (complete, out prefix);
  98. return new Mono.Terminal.LineEditor.Completion (prefix, completions);
  99. };
  100. #if false
  101. //
  102. // This is a sample of how completions sould be implemented.
  103. //
  104. editor.AutoCompleteEvent += delegate (string s, int pos){
  105. // Single match: "Substring": Sub-string
  106. if (s.EndsWith ("Sub")){
  107. return new string [] { "string" };
  108. }
  109. // Multiple matches: "ToString" and "ToLower"
  110. if (s.EndsWith ("T")){
  111. return new string [] { "ToString", "ToLower" };
  112. }
  113. return null;
  114. };
  115. #endif
  116. Console.CancelKeyPress += ConsoleInterrupt;
  117. }
  118. string GetLine (bool primary)
  119. {
  120. string prompt = primary ? InteractiveBase.Prompt : InteractiveBase.ContinuationPrompt;
  121. if (dumb){
  122. if (isatty)
  123. Console.Write (prompt);
  124. return Console.ReadLine ();
  125. } else {
  126. return editor.Edit (prompt, "");
  127. }
  128. }
  129. delegate string ReadLiner (bool primary);
  130. void InitializeUsing ()
  131. {
  132. Evaluate ("using System; using System.Linq; using System.Collections.Generic; using System.Collections;");
  133. }
  134. void InitTerminal ()
  135. {
  136. isatty = UnixUtils.isatty (0) && UnixUtils.isatty (1);
  137. // Work around, since Console is not accounting for
  138. // cursor position when writing to Stderr. It also
  139. // has the undesirable side effect of making
  140. // errors plain, with no coloring.
  141. // Report.Stderr = Console.Out;
  142. SetupConsole ();
  143. if (isatty)
  144. Console.WriteLine ("Mono C# Shell, type \"help;\" for help\n\nEnter statements below.");
  145. }
  146. void ExecuteSources (IEnumerable<string> sources, bool ignore_errors)
  147. {
  148. foreach (string file in sources){
  149. try {
  150. try {
  151. using (System.IO.StreamReader r = System.IO.File.OpenText (file)){
  152. ReadEvalPrintLoopWith (p => r.ReadLine ());
  153. }
  154. } catch (FileNotFoundException){
  155. Console.Error.WriteLine ("cs2001: Source file `{0}' not found", file);
  156. return;
  157. }
  158. } catch {
  159. if (!ignore_errors)
  160. throw;
  161. }
  162. }
  163. }
  164. protected virtual void LoadStartupFiles ()
  165. {
  166. string dir = Path.Combine (
  167. Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData),
  168. "csharp");
  169. if (!Directory.Exists (dir))
  170. return;
  171. List<string> sources = new List<string> ();
  172. List<string> libraries = new List<string> ();
  173. foreach (string file in System.IO.Directory.GetFiles (dir)){
  174. string l = file.ToLower ();
  175. if (l.EndsWith (".cs"))
  176. sources.Add (file);
  177. else if (l.EndsWith (".dll"))
  178. libraries.Add (file);
  179. }
  180. foreach (string file in libraries)
  181. Evaluator.LoadAssembly (file);
  182. ExecuteSources (sources, true);
  183. }
  184. void ReadEvalPrintLoopWith (ReadLiner readline)
  185. {
  186. string expr = null;
  187. while (!InteractiveBase.QuitRequested){
  188. string input = readline (expr == null);
  189. if (input == null)
  190. return;
  191. if (input == "")
  192. continue;
  193. expr = expr == null ? input : expr + "\n" + input;
  194. expr = Evaluate (expr);
  195. }
  196. }
  197. public int ReadEvalPrintLoop ()
  198. {
  199. if (startup_files != null && startup_files.Length == 0)
  200. InitTerminal ();
  201. InitializeUsing ();
  202. LoadStartupFiles ();
  203. //
  204. // Interactive or startup files provided?
  205. //
  206. if (startup_files.Length != 0)
  207. ExecuteSources (startup_files, false);
  208. else
  209. ReadEvalPrintLoopWith (GetLine);
  210. return 0;
  211. }
  212. protected virtual string Evaluate (string input)
  213. {
  214. bool result_set;
  215. object result;
  216. try {
  217. input = Evaluator.Evaluate (input, out result, out result_set);
  218. if (result_set){
  219. PrettyPrint (Console.Out, result);
  220. Console.WriteLine ();
  221. }
  222. } catch (Exception e){
  223. Console.WriteLine (e);
  224. return null;
  225. }
  226. return input;
  227. }
  228. static void p (TextWriter output, string s)
  229. {
  230. output.Write (s);
  231. }
  232. static string EscapeString (string s)
  233. {
  234. return s.Replace ("\"", "\\\"");
  235. }
  236. static void EscapeChar (TextWriter output, char c)
  237. {
  238. if (c == '\''){
  239. output.Write ("'\\''");
  240. return;
  241. }
  242. if (c > 32){
  243. output.Write ("'{0}'", c);
  244. return;
  245. }
  246. switch (c){
  247. case '\a':
  248. output.Write ("'\\a'");
  249. break;
  250. case '\b':
  251. output.Write ("'\\b'");
  252. break;
  253. case '\n':
  254. output.Write ("'\\n'");
  255. break;
  256. case '\v':
  257. output.Write ("'\\v'");
  258. break;
  259. case '\r':
  260. output.Write ("'\\r'");
  261. break;
  262. case '\f':
  263. output.Write ("'\\f'");
  264. break;
  265. case '\t':
  266. output.Write ("'\\t");
  267. break;
  268. default:
  269. output.Write ("'\\x{0:x}", (int) c);
  270. break;
  271. }
  272. }
  273. internal static void PrettyPrint (TextWriter output, object result)
  274. {
  275. if (result == null){
  276. p (output, "null");
  277. return;
  278. }
  279. if (result is Array){
  280. Array a = (Array) result;
  281. p (output, "{ ");
  282. int top = a.GetUpperBound (0);
  283. for (int i = a.GetLowerBound (0); i <= top; i++){
  284. PrettyPrint (output, a.GetValue (i));
  285. if (i != top)
  286. p (output, ", ");
  287. }
  288. p (output, " }");
  289. } else if (result is bool){
  290. if ((bool) result)
  291. p (output, "true");
  292. else
  293. p (output, "false");
  294. } else if (result is string){
  295. p (output, String.Format ("\"{0}\"", EscapeString ((string)result)));
  296. } else if (result is IDictionary){
  297. IDictionary dict = (IDictionary) result;
  298. int top = dict.Count, count = 0;
  299. p (output, "{");
  300. foreach (DictionaryEntry entry in dict){
  301. count++;
  302. p (output, "{ ");
  303. PrettyPrint (output, entry.Key);
  304. p (output, ", ");
  305. PrettyPrint (output, entry.Value);
  306. if (count != top)
  307. p (output, " }, ");
  308. else
  309. p (output, " }");
  310. }
  311. p (output, "}");
  312. } else if (result is IEnumerable) {
  313. int i = 0;
  314. p (output, "{ ");
  315. foreach (object item in (IEnumerable) result) {
  316. if (i++ != 0)
  317. p (output, ", ");
  318. PrettyPrint (output, item);
  319. }
  320. p (output, " }");
  321. } else if (result is char) {
  322. EscapeChar (output, (char) result);
  323. } else {
  324. p (output, result.ToString ());
  325. }
  326. }
  327. public CSharpShell ()
  328. {
  329. }
  330. public virtual int Run (string [] startup_files)
  331. {
  332. this.startup_files = startup_files;
  333. return ReadEvalPrintLoop ();
  334. }
  335. }
  336. //
  337. // A shell connected to a CSharpAgent running in a remote process.
  338. // - maybe add 'class_name' and 'method_name' arguments to LoadAgent.
  339. // - Support Gtk and Winforms main loops if detected, this should
  340. // probably be done as a separate agent in a separate place.
  341. //
  342. class ClientCSharpShell : CSharpShell {
  343. NetworkStream ns, interrupt_stream;
  344. public ClientCSharpShell (int pid)
  345. {
  346. // Create a server socket we listen on whose address is passed to the agent
  347. TcpListener listener = new TcpListener (new IPEndPoint (IPAddress.Loopback, 0));
  348. listener.Start ();
  349. TcpListener interrupt_listener = new TcpListener (new IPEndPoint (IPAddress.Loopback, 0));
  350. interrupt_listener.Start ();
  351. string agent_assembly = typeof (ClientCSharpShell).Assembly.Location;
  352. string agent_arg = String.Format ("--agent:{0}:{1}" ,
  353. ((IPEndPoint)listener.Server.LocalEndPoint).Port,
  354. ((IPEndPoint)interrupt_listener.Server.LocalEndPoint).Port);
  355. VirtualMachine vm = new VirtualMachine (pid);
  356. vm.Attach (agent_assembly, agent_arg);
  357. /* Wait for the client to connect */
  358. TcpClient client = listener.AcceptTcpClient ();
  359. ns = client.GetStream ();
  360. TcpClient interrupt_client = interrupt_listener.AcceptTcpClient ();
  361. interrupt_stream = interrupt_client.GetStream ();
  362. Console.WriteLine ("Connected.");
  363. }
  364. //
  365. // A remote version of Evaluate
  366. //
  367. protected override string Evaluate (string input)
  368. {
  369. ns.WriteString (input);
  370. while (true) {
  371. AgentStatus s = (AgentStatus) ns.ReadByte ();
  372. switch (s){
  373. case AgentStatus.PARTIAL_INPUT:
  374. return input;
  375. case AgentStatus.ERROR:
  376. string err = ns.GetString ();
  377. Console.Error.WriteLine (err);
  378. break;
  379. case AgentStatus.RESULT_NOT_SET:
  380. return null;
  381. case AgentStatus.RESULT_SET:
  382. string res = ns.GetString ();
  383. Console.WriteLine (res);
  384. return null;
  385. }
  386. }
  387. }
  388. public override int Run (string [] startup_files)
  389. {
  390. // The difference is that we do not call Evaluator.Init, that is done on the target
  391. return ReadEvalPrintLoop ();
  392. }
  393. protected override void ConsoleInterrupt (object sender, ConsoleCancelEventArgs a)
  394. {
  395. // Do not about our program
  396. a.Cancel = true;
  397. interrupt_stream.WriteByte (0);
  398. int c = interrupt_stream.ReadByte ();
  399. if (c != -1)
  400. Console.WriteLine ("Execution interrupted");
  401. }
  402. }
  403. //
  404. // Stream helper extension methods
  405. //
  406. public static class StreamHelper {
  407. static DataConverter converter = DataConverter.LittleEndian;
  408. public static int GetInt (this Stream stream)
  409. {
  410. byte [] b = new byte [4];
  411. if (stream.Read (b, 0, 4) != 4)
  412. throw new IOException ("End reached");
  413. return converter.GetInt32 (b, 0);
  414. }
  415. public static string GetString (this Stream stream)
  416. {
  417. int len = stream.GetInt ();
  418. byte [] b = new byte [len];
  419. if (stream.Read (b, 0, len) != len)
  420. throw new IOException ("End reached");
  421. return Encoding.UTF8.GetString (b);
  422. }
  423. public static void WriteInt (this Stream stream, int n)
  424. {
  425. byte [] bytes = converter.GetBytes (n);
  426. stream.Write (bytes, 0, bytes.Length);
  427. }
  428. public static void WriteString (this Stream stream, string s)
  429. {
  430. stream.WriteInt (s.Length);
  431. byte [] bytes = Encoding.UTF8.GetBytes (s);
  432. stream.Write (bytes, 0, bytes.Length);
  433. }
  434. }
  435. public enum AgentStatus : byte {
  436. // Received partial input, complete
  437. PARTIAL_INPUT = 1,
  438. // The result was set, expect the string with the result
  439. RESULT_SET = 2,
  440. // No result was set, complete
  441. RESULT_NOT_SET = 3,
  442. // Errors and warnings string follows
  443. ERROR = 4,
  444. }
  445. //
  446. // This is the agent loaded into the target process when using --attach.
  447. //
  448. class CSharpAgent
  449. {
  450. NetworkStream interrupt_stream;
  451. public CSharpAgent (String arg)
  452. {
  453. new Thread (new ParameterizedThreadStart (Run)).Start (arg);
  454. }
  455. public void InterruptListener ()
  456. {
  457. while (true){
  458. int b = interrupt_stream.ReadByte();
  459. if (b == -1)
  460. return;
  461. Evaluator.Interrupt ();
  462. interrupt_stream.WriteByte (0);
  463. }
  464. }
  465. public void Run (object o)
  466. {
  467. string arg = (string)o;
  468. string ports = arg.Substring (8);
  469. int sp = ports.IndexOf (':');
  470. int port = Int32.Parse (ports.Substring (0, sp));
  471. int interrupt_port = Int32.Parse (ports.Substring (sp+1));
  472. Console.WriteLine ("csharp-agent: started, connecting to localhost:" + port);
  473. TcpClient client = new TcpClient ("127.0.0.1", port);
  474. TcpClient interrupt_client = new TcpClient ("127.0.0.1", interrupt_port);
  475. Console.WriteLine ("csharp-agent: connected.");
  476. NetworkStream s = client.GetStream ();
  477. interrupt_stream = interrupt_client.GetStream ();
  478. new Thread (InterruptListener).Start ();
  479. try {
  480. Evaluator.Init (new string [0]);
  481. } catch {
  482. // TODO: send a result back.
  483. Console.WriteLine ("csharp-agent: initialization failed");
  484. return;
  485. }
  486. try {
  487. // Add all assemblies loaded later
  488. AppDomain.CurrentDomain.AssemblyLoad += AssemblyLoaded;
  489. // Add all currently loaded assemblies
  490. foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies ())
  491. Evaluator.ReferenceAssembly (a);
  492. RunRepl (s);
  493. } finally {
  494. AppDomain.CurrentDomain.AssemblyLoad -= AssemblyLoaded;
  495. client.Close ();
  496. interrupt_client.Close ();
  497. Console.WriteLine ("csharp-agent: disconnected.");
  498. }
  499. }
  500. static void AssemblyLoaded (object sender, AssemblyLoadEventArgs e)
  501. {
  502. Evaluator.ReferenceAssembly (e.LoadedAssembly);
  503. }
  504. public void RunRepl (NetworkStream s)
  505. {
  506. string input = null;
  507. while (!InteractiveBase.QuitRequested) {
  508. try {
  509. string error_string;
  510. StringWriter error_output = new StringWriter ();
  511. // Report.Stderr = error_output;
  512. string line = s.GetString ();
  513. bool result_set;
  514. object result;
  515. if (input == null)
  516. input = line;
  517. else
  518. input = input + "\n" + line;
  519. try {
  520. input = Evaluator.Evaluate (input, out result, out result_set);
  521. } catch (Exception e) {
  522. s.WriteByte ((byte) AgentStatus.ERROR);
  523. s.WriteString (e.ToString ());
  524. s.WriteByte ((byte) AgentStatus.RESULT_NOT_SET);
  525. continue;
  526. }
  527. if (input != null){
  528. s.WriteByte ((byte) AgentStatus.PARTIAL_INPUT);
  529. continue;
  530. }
  531. // Send warnings and errors back
  532. error_string = error_output.ToString ();
  533. if (error_string.Length != 0){
  534. s.WriteByte ((byte) AgentStatus.ERROR);
  535. s.WriteString (error_output.ToString ());
  536. }
  537. if (result_set){
  538. s.WriteByte ((byte) AgentStatus.RESULT_SET);
  539. StringWriter sr = new StringWriter ();
  540. CSharpShell.PrettyPrint (sr, result);
  541. s.WriteString (sr.ToString ());
  542. } else {
  543. s.WriteByte ((byte) AgentStatus.RESULT_NOT_SET);
  544. }
  545. } catch (IOException) {
  546. break;
  547. } catch (Exception e){
  548. Console.WriteLine (e);
  549. }
  550. }
  551. }
  552. }
  553. public class UnixUtils {
  554. [System.Runtime.InteropServices.DllImport ("libc", EntryPoint="isatty")]
  555. extern static int _isatty (int fd);
  556. public static bool isatty (int fd)
  557. {
  558. try {
  559. return _isatty (fd) == 1;
  560. } catch {
  561. return false;
  562. }
  563. }
  564. }
  565. }
  566. namespace Mono.Management
  567. {
  568. interface IVirtualMachine {
  569. void LoadAgent (string filename, string args);
  570. }
  571. }