repl.cs 25 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027
  1. //
  2. // repl.cs: Support for using the compiler in interactive mode (read-eval-print loop)
  3. //
  4. // Authors:
  5. // Miguel de Icaza ([email protected])
  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. // Copyright 2011-2013 Xamarin Inc
  12. //
  13. //
  14. // TODO:
  15. // Do not print results in Evaluate, do that elsewhere in preparation for Eval refactoring.
  16. // Driver.PartialReset should not reset the coretypes, nor the optional types, to avoid
  17. // computing that on every call.
  18. //
  19. using System;
  20. using System.IO;
  21. using System.Text;
  22. using System.Globalization;
  23. using System.Collections;
  24. using System.Reflection;
  25. using System.Reflection.Emit;
  26. using System.Threading;
  27. using System.Net;
  28. using System.Net.Sockets;
  29. using System.Collections.Generic;
  30. using Mono.CSharp;
  31. namespace Mono {
  32. public class Driver {
  33. public static string StartupEvalExpression;
  34. static int? attach;
  35. static string target_host;
  36. static int target_port;
  37. static string agent;
  38. static string [] script_args;
  39. public static string [] ScriptArgs => script_args;
  40. static int Main (string [] args)
  41. {
  42. if (!SplitDriverAndScriptArguments (ref args, out script_args))
  43. return 1;
  44. var cmd = new CommandLineParser (Console.Out);
  45. cmd.UnknownOptionHandler += HandleExtraArguments;
  46. // Enable unsafe code by default
  47. var settings = new CompilerSettings () {
  48. Unsafe = true
  49. };
  50. if (!cmd.ParseArguments (settings, args))
  51. return 1;
  52. var startup_files = new string [settings.SourceFiles.Count];
  53. int i = 0;
  54. foreach (var source in settings.SourceFiles)
  55. startup_files [i++] = source.OriginalFullPathName;
  56. settings.SourceFiles.Clear ();
  57. TextWriter agent_stderr = null;
  58. ReportPrinter printer;
  59. if (agent != null) {
  60. agent_stderr = new StringWriter ();
  61. printer = new StreamReportPrinter (agent_stderr);
  62. } else {
  63. printer = new ConsoleReportPrinter ();
  64. }
  65. var eval = new Evaluator (new CompilerContext (settings, printer));
  66. eval.InteractiveBaseClass = typeof (InteractiveBaseShell);
  67. eval.DescribeTypeExpressions = true;
  68. eval.WaitOnTask = true;
  69. CSharpShell shell;
  70. #if !ON_DOTNET
  71. if (attach.HasValue) {
  72. shell = new ClientCSharpShell_v1 (eval, attach.Value);
  73. } else if (agent != null) {
  74. new CSharpAgent (eval, agent, agent_stderr).Run (startup_files);
  75. return 0;
  76. } else
  77. #endif
  78. if (target_host != null)
  79. shell = new ClientCSharpShell (eval, target_host, target_port);
  80. else
  81. shell = new CSharpShell (eval);
  82. return shell.Run (startup_files);
  83. }
  84. static bool SplitDriverAndScriptArguments (ref string [] driver_args, out string [] script_args)
  85. {
  86. // split command line arguments into two groups:
  87. // - anything before '--' or '-s' goes to the mcs driver, which may
  88. // call back into the csharp driver for further processing
  89. // - anything after '--' or '-s' is made available to the REPL/script
  90. // via the 'Args' global, similar to csi.
  91. // - if '-s' is used, the argument immediately following it will
  92. // also be processed by the mcs driver (e.g. a source file)
  93. int driver_args_count = 0;
  94. int script_args_offset = 0;
  95. string script_file = null;
  96. while (driver_args_count < driver_args.Length && script_args_offset == 0) {
  97. switch (driver_args [driver_args_count]) {
  98. case "--":
  99. script_args_offset = driver_args_count + 1;
  100. break;
  101. case "-s":
  102. if (driver_args_count + 1 >= driver_args.Length) {
  103. script_args = null;
  104. Console.Error.WriteLine ("usage is: -s SCRIPT_FILE");
  105. return false;
  106. }
  107. driver_args_count++;
  108. script_file = driver_args [driver_args_count];
  109. script_args_offset = driver_args_count + 1;
  110. break;
  111. default:
  112. driver_args_count++;
  113. break;
  114. }
  115. }
  116. if (script_args_offset > 0) {
  117. int script_args_count = driver_args.Length - script_args_offset;
  118. script_args = new string [script_args_count];
  119. Array.Copy (driver_args, script_args_offset, script_args, 0, script_args_count);
  120. } else
  121. script_args = Array.Empty<string> ();
  122. Array.Resize (ref driver_args, driver_args_count);
  123. if (script_file != null)
  124. driver_args [driver_args_count - 1] = script_file;
  125. return true;
  126. }
  127. static int HandleExtraArguments (string [] args, int pos)
  128. {
  129. switch (args [pos]) {
  130. case "-e":
  131. if (pos + 1 < args.Length) {
  132. StartupEvalExpression = args[pos + 1];
  133. return pos + 1;
  134. }
  135. break;
  136. case "--attach":
  137. if (pos + 1 < args.Length) {
  138. attach = Int32.Parse (args[1]);
  139. return pos + 1;
  140. }
  141. break;
  142. default:
  143. if (args [pos].StartsWith ("--server=")){
  144. var hostport = args [pos].Substring (9);
  145. int p = hostport.IndexOf (':');
  146. if (p == -1){
  147. target_host = hostport;
  148. target_port = 10000;
  149. } else {
  150. target_host = hostport.Substring (0,p);
  151. if (!int.TryParse (hostport.Substring (p), out target_port)){
  152. Console.Error.WriteLine ("Usage is: --server[=host[:port]");
  153. Environment.Exit (1);
  154. }
  155. }
  156. return pos + 1;
  157. }
  158. if (args [pos].StartsWith ("--client")){
  159. target_host = "localhost";
  160. target_port = 10000;
  161. return pos + 1;
  162. }
  163. if (args [pos].StartsWith ("--agent:")) {
  164. agent = args[pos];
  165. return pos + 1;
  166. } else {
  167. return -1;
  168. }
  169. }
  170. return -1;
  171. }
  172. }
  173. public class InteractiveBaseShell : InteractiveBase {
  174. static bool tab_at_start_completes;
  175. static InteractiveBaseShell ()
  176. {
  177. tab_at_start_completes = false;
  178. }
  179. internal static Mono.Terminal.LineEditor Editor;
  180. public static bool TabAtStartCompletes {
  181. get {
  182. return tab_at_start_completes;
  183. }
  184. set {
  185. tab_at_start_completes = value;
  186. if (Editor != null)
  187. Editor.TabAtStartCompletes = value;
  188. }
  189. }
  190. public static new string help {
  191. get {
  192. return InteractiveBase.help +
  193. " TabAtStartCompletes - Whether tab will complete even on empty lines\n" +
  194. " Args - Any command line arguments passed to csharp\n" +
  195. " after the '--' (stop processing) argument";
  196. }
  197. }
  198. public static string [] Args => Driver.ScriptArgs;
  199. }
  200. public class CSharpShell {
  201. static bool isatty = true, is_unix = false;
  202. protected string [] startup_files;
  203. Mono.Terminal.LineEditor editor;
  204. bool dumb;
  205. readonly Evaluator evaluator;
  206. public CSharpShell (Evaluator evaluator)
  207. {
  208. this.evaluator = evaluator;
  209. }
  210. protected virtual void ConsoleInterrupt (object sender, ConsoleCancelEventArgs a)
  211. {
  212. // Do not about our program
  213. a.Cancel = true;
  214. evaluator.Interrupt ();
  215. }
  216. void SetupConsole ()
  217. {
  218. if (is_unix){
  219. string term = Environment.GetEnvironmentVariable ("TERM");
  220. dumb = term == "dumb" || term == null || isatty == false;
  221. } else
  222. dumb = false;
  223. editor = new Mono.Terminal.LineEditor ("csharp", 300) {
  224. HeuristicsMode = "csharp"
  225. };
  226. InteractiveBaseShell.Editor = editor;
  227. editor.AutoCompleteEvent += delegate (string s, int pos){
  228. string prefix = null;
  229. string complete = s.Substring (0, pos);
  230. string [] completions = evaluator.GetCompletions (complete, out prefix);
  231. return new Mono.Terminal.LineEditor.Completion (prefix, completions);
  232. };
  233. #if false
  234. //
  235. // This is a sample of how completions sould be implemented.
  236. //
  237. editor.AutoCompleteEvent += delegate (string s, int pos){
  238. // Single match: "Substring": Sub-string
  239. if (s.EndsWith ("Sub")){
  240. return new string [] { "string" };
  241. }
  242. // Multiple matches: "ToString" and "ToLower"
  243. if (s.EndsWith ("T")){
  244. return new string [] { "ToString", "ToLower" };
  245. }
  246. return null;
  247. };
  248. #endif
  249. Console.CancelKeyPress += ConsoleInterrupt;
  250. }
  251. string GetLine (bool primary)
  252. {
  253. string prompt = primary ? InteractiveBase.Prompt : InteractiveBase.ContinuationPrompt;
  254. if (dumb){
  255. if (isatty)
  256. Console.Write (prompt);
  257. return Console.ReadLine ();
  258. } else {
  259. return editor.Edit (prompt, "");
  260. }
  261. }
  262. delegate string ReadLiner (bool primary);
  263. void InitializeUsing ()
  264. {
  265. Evaluate ("using System; using System.Linq; using System.Collections.Generic; using System.Collections;");
  266. }
  267. void InitTerminal (bool show_banner)
  268. {
  269. int p = (int) Environment.OSVersion.Platform;
  270. is_unix = (p == 4) || (p == 128);
  271. isatty = !Console.IsInputRedirected && !Console.IsOutputRedirected;
  272. // Work around, since Console is not accounting for
  273. // cursor position when writing to Stderr. It also
  274. // has the undesirable side effect of making
  275. // errors plain, with no coloring.
  276. // Report.Stderr = Console.Out;
  277. SetupConsole ();
  278. if (isatty && show_banner)
  279. Console.WriteLine ("Mono C# Shell, type \"help;\" for help\n\nEnter statements below.");
  280. }
  281. void ExecuteSources (IEnumerable<string> sources, bool ignore_errors)
  282. {
  283. foreach (string file in sources){
  284. try {
  285. try {
  286. bool first = true;
  287. using (System.IO.StreamReader r = System.IO.File.OpenText (file)){
  288. ReadEvalPrintLoopWith (p => {
  289. var line = r.ReadLine ();
  290. if (first){
  291. if (line.StartsWith ("#!"))
  292. line = r.ReadLine ();
  293. first = false;
  294. }
  295. return line;
  296. });
  297. }
  298. } catch (FileNotFoundException){
  299. Console.Error.WriteLine ("cs2001: Source file `{0}' not found", file);
  300. return;
  301. }
  302. } catch {
  303. if (!ignore_errors)
  304. throw;
  305. }
  306. }
  307. }
  308. protected virtual void LoadStartupFiles ()
  309. {
  310. string dir = Path.Combine (
  311. Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData),
  312. "csharp");
  313. if (!Directory.Exists (dir))
  314. return;
  315. List<string> sources = new List<string> ();
  316. List<string> libraries = new List<string> ();
  317. foreach (string file in System.IO.Directory.GetFiles (dir)){
  318. string l = file.ToLower ();
  319. if (l.EndsWith (".cs"))
  320. sources.Add (file);
  321. else if (l.EndsWith (".dll"))
  322. libraries.Add (file);
  323. }
  324. foreach (string file in libraries)
  325. evaluator.LoadAssembly (file);
  326. ExecuteSources (sources, true);
  327. }
  328. void ReadEvalPrintLoopWith (ReadLiner readline)
  329. {
  330. string expr = null;
  331. while (!InteractiveBase.QuitRequested){
  332. string input = readline (expr == null);
  333. if (input == null)
  334. return;
  335. if (input == "")
  336. continue;
  337. expr = expr == null ? input : expr + "\n" + input;
  338. expr = Evaluate (expr);
  339. }
  340. }
  341. public int ReadEvalPrintLoop ()
  342. {
  343. if (startup_files != null && startup_files.Length == 0)
  344. InitTerminal (startup_files.Length == 0 && Driver.StartupEvalExpression == null);
  345. InitializeUsing ();
  346. LoadStartupFiles ();
  347. if (startup_files != null && startup_files.Length != 0) {
  348. ExecuteSources (startup_files, false);
  349. } else {
  350. if (Driver.StartupEvalExpression != null){
  351. ReadEvalPrintLoopWith (p => {
  352. var ret = Driver.StartupEvalExpression;
  353. Driver.StartupEvalExpression = null;
  354. return ret;
  355. });
  356. } else {
  357. ReadEvalPrintLoopWith (GetLine);
  358. }
  359. editor.SaveHistory ();
  360. }
  361. Console.CancelKeyPress -= ConsoleInterrupt;
  362. return 0;
  363. }
  364. protected virtual string Evaluate (string input)
  365. {
  366. bool result_set;
  367. object result;
  368. try {
  369. input = evaluator.Evaluate (input, out result, out result_set);
  370. if (result_set){
  371. PrettyPrint (Console.Out, result);
  372. Console.WriteLine ();
  373. }
  374. } catch (Exception e){
  375. Console.WriteLine (e);
  376. return null;
  377. }
  378. return input;
  379. }
  380. static void p (TextWriter output, string s)
  381. {
  382. output.Write (s);
  383. }
  384. static void EscapeString (TextWriter output, string s)
  385. {
  386. foreach (var c in s){
  387. if (c >= 32){
  388. output.Write (c);
  389. continue;
  390. }
  391. switch (c){
  392. case '\"':
  393. output.Write ("\\\""); break;
  394. case '\a':
  395. output.Write ("\\a"); break;
  396. case '\b':
  397. output.Write ("\\b"); break;
  398. case '\n':
  399. output.Write ("\n");
  400. break;
  401. case '\v':
  402. output.Write ("\\v");
  403. break;
  404. case '\r':
  405. output.Write ("\\r");
  406. break;
  407. case '\f':
  408. output.Write ("\\f");
  409. break;
  410. case '\t':
  411. output.Write ("\\t");
  412. break;
  413. default:
  414. output.Write ("\\x{0:x}", (int) c);
  415. break;
  416. }
  417. }
  418. }
  419. static void EscapeChar (TextWriter output, char c)
  420. {
  421. if (c == '\''){
  422. output.Write ("'\\''");
  423. return;
  424. }
  425. if (c >= 32){
  426. output.Write ("'{0}'", c);
  427. return;
  428. }
  429. switch (c){
  430. case '\a':
  431. output.Write ("'\\a'");
  432. break;
  433. case '\b':
  434. output.Write ("'\\b'");
  435. break;
  436. case '\n':
  437. output.Write ("'\\n'");
  438. break;
  439. case '\v':
  440. output.Write ("'\\v'");
  441. break;
  442. case '\r':
  443. output.Write ("'\\r'");
  444. break;
  445. case '\f':
  446. output.Write ("'\\f'");
  447. break;
  448. case '\t':
  449. output.Write ("'\\t");
  450. break;
  451. default:
  452. output.Write ("'\\x{0:x}'", (int) c);
  453. break;
  454. }
  455. }
  456. // Some types (System.Json.JsonPrimitive) implement
  457. // IEnumerator and yet, throw an exception when we
  458. // try to use them, helper function to check for that
  459. // condition
  460. static internal bool WorksAsEnumerable (object obj)
  461. {
  462. IEnumerable enumerable = obj as IEnumerable;
  463. if (enumerable != null){
  464. try {
  465. enumerable.GetEnumerator ();
  466. return true;
  467. } catch {
  468. // nothing, we return false below
  469. }
  470. }
  471. return false;
  472. }
  473. internal static void PrettyPrint (TextWriter output, object result)
  474. {
  475. if (result == null){
  476. p (output, "null");
  477. return;
  478. }
  479. if (result is Array){
  480. Array a = (Array) result;
  481. p (output, "{ ");
  482. int top = a.GetUpperBound (0);
  483. for (int i = a.GetLowerBound (0); i <= top; i++){
  484. PrettyPrint (output, a.GetValue (i));
  485. if (i != top)
  486. p (output, ", ");
  487. }
  488. p (output, " }");
  489. } else if (result is bool){
  490. if ((bool) result)
  491. p (output, "true");
  492. else
  493. p (output, "false");
  494. } else if (result is string){
  495. p (output, "\"");
  496. EscapeString (output, (string)result);
  497. p (output, "\"");
  498. } else if (result is IDictionary){
  499. IDictionary dict = (IDictionary) result;
  500. int top = dict.Count, count = 0;
  501. p (output, "{");
  502. foreach (DictionaryEntry entry in dict){
  503. count++;
  504. p (output, "{ ");
  505. PrettyPrint (output, entry.Key);
  506. p (output, ", ");
  507. PrettyPrint (output, entry.Value);
  508. if (count != top)
  509. p (output, " }, ");
  510. else
  511. p (output, " }");
  512. }
  513. p (output, "}");
  514. } else if (WorksAsEnumerable (result)) {
  515. int i = 0;
  516. p (output, "{ ");
  517. foreach (object item in (IEnumerable) result) {
  518. if (i++ != 0)
  519. p (output, ", ");
  520. PrettyPrint (output, item);
  521. }
  522. p (output, " }");
  523. } else if (result is char) {
  524. EscapeChar (output, (char) result);
  525. } else {
  526. p (output, result.ToString ());
  527. }
  528. }
  529. public virtual int Run (string [] startup_files)
  530. {
  531. this.startup_files = startup_files;
  532. return ReadEvalPrintLoop ();
  533. }
  534. }
  535. //
  536. // Stream helper extension methods
  537. //
  538. public static class StreamHelper {
  539. static DataConverter converter = DataConverter.LittleEndian;
  540. static void GetBuffer (this Stream stream, byte [] b)
  541. {
  542. int n, offset = 0;
  543. int len = b.Length;
  544. do {
  545. n = stream.Read (b, offset, len);
  546. if (n == 0)
  547. throw new IOException ("End reached");
  548. offset += n;
  549. len -= n;
  550. } while (len > 0);
  551. }
  552. public static int GetInt (this Stream stream)
  553. {
  554. byte [] b = new byte [4];
  555. stream.GetBuffer (b);
  556. return converter.GetInt32 (b, 0);
  557. }
  558. public static string GetString (this Stream stream)
  559. {
  560. int len = stream.GetInt ();
  561. if (len == 0)
  562. return "";
  563. byte [] b = new byte [len];
  564. stream.GetBuffer (b);
  565. return Encoding.UTF8.GetString (b);
  566. }
  567. public static void WriteInt (this Stream stream, int n)
  568. {
  569. byte [] bytes = converter.GetBytes (n);
  570. stream.Write (bytes, 0, bytes.Length);
  571. }
  572. public static void WriteString (this Stream stream, string s)
  573. {
  574. stream.WriteInt (s.Length);
  575. byte [] bytes = Encoding.UTF8.GetBytes (s);
  576. stream.Write (bytes, 0, bytes.Length);
  577. }
  578. }
  579. public enum AgentStatus : byte {
  580. // Received partial input, complete
  581. PARTIAL_INPUT = 1,
  582. // The result was set, expect the string with the result
  583. RESULT_SET = 2,
  584. // No result was set, complete
  585. RESULT_NOT_SET = 3,
  586. // Errors and warnings string follows
  587. ERROR = 4,
  588. // Stdout
  589. STDOUT = 5,
  590. }
  591. class ClientCSharpShell : CSharpShell {
  592. string target_host;
  593. int target_port;
  594. public ClientCSharpShell (Evaluator evaluator, string target_host, int target_port) : base (evaluator)
  595. {
  596. this.target_port = target_port;
  597. this.target_host = target_host;
  598. }
  599. T ConnectServer<T> (Func<NetworkStream,T> callback, Action<Exception> error)
  600. {
  601. try {
  602. var client = new TcpClient (target_host, target_port);
  603. var ns = client.GetStream ();
  604. T ret = callback (ns);
  605. ns.Flush ();
  606. ns.Close ();
  607. client.Close ();
  608. return ret;
  609. } catch (Exception e){
  610. error (e);
  611. return default(T);
  612. }
  613. }
  614. protected override string Evaluate (string input)
  615. {
  616. return ConnectServer<string> ((ns)=> {
  617. try {
  618. ns.WriteString ("EVALTXT");
  619. ns.WriteString (input);
  620. while (true) {
  621. AgentStatus s = (AgentStatus) ns.ReadByte ();
  622. switch (s){
  623. case AgentStatus.PARTIAL_INPUT:
  624. return input;
  625. case AgentStatus.ERROR:
  626. string err = ns.GetString ();
  627. Console.Error.WriteLine (err);
  628. break;
  629. case AgentStatus.STDOUT:
  630. string stdout = ns.GetString ();
  631. Console.WriteLine (stdout);
  632. break;
  633. case AgentStatus.RESULT_NOT_SET:
  634. return null;
  635. case AgentStatus.RESULT_SET:
  636. string res = ns.GetString ();
  637. Console.WriteLine (res);
  638. return null;
  639. }
  640. }
  641. } catch (Exception e){
  642. Console.Error.WriteLine ("Error evaluating expression, exception: {0}", e);
  643. }
  644. return null;
  645. }, (e) => {
  646. Console.Error.WriteLine ("Error communicating with server {0}", e);
  647. });
  648. }
  649. public override int Run (string [] startup_files)
  650. {
  651. // The difference is that we do not call Evaluator.Init, that is done on the target
  652. this.startup_files = startup_files;
  653. return ReadEvalPrintLoop ();
  654. }
  655. protected override void ConsoleInterrupt (object sender, ConsoleCancelEventArgs a)
  656. {
  657. ConnectServer<int> ((ns)=> {
  658. ns.WriteString ("INTERRUPT");
  659. return 0;
  660. }, (e) => { });
  661. }
  662. }
  663. #if !ON_DOTNET
  664. //
  665. // A shell connected to a CSharpAgent running in a remote process.
  666. // - maybe add 'class_name' and 'method_name' arguments to LoadAgent.
  667. // - Support Gtk and Winforms main loops if detected, this should
  668. // probably be done as a separate agent in a separate place.
  669. //
  670. class ClientCSharpShell_v1 : CSharpShell {
  671. NetworkStream ns, interrupt_stream;
  672. public ClientCSharpShell_v1 (Evaluator evaluator, int pid)
  673. : base (evaluator)
  674. {
  675. // Create a server socket we listen on whose address is passed to the agent
  676. TcpListener listener = new TcpListener (new IPEndPoint (IPAddress.Loopback, 0));
  677. listener.Start ();
  678. TcpListener interrupt_listener = new TcpListener (new IPEndPoint (IPAddress.Loopback, 0));
  679. interrupt_listener.Start ();
  680. string agent_assembly = typeof (ClientCSharpShell).Assembly.Location;
  681. string agent_arg = String.Format ("--agent:{0}:{1}" ,
  682. ((IPEndPoint)listener.Server.LocalEndPoint).Port,
  683. ((IPEndPoint)interrupt_listener.Server.LocalEndPoint).Port);
  684. var vm = new Attach.VirtualMachine (pid);
  685. vm.Attach (agent_assembly, agent_arg);
  686. /* Wait for the client to connect */
  687. TcpClient client = listener.AcceptTcpClient ();
  688. ns = client.GetStream ();
  689. TcpClient interrupt_client = interrupt_listener.AcceptTcpClient ();
  690. interrupt_stream = interrupt_client.GetStream ();
  691. Console.WriteLine ("Connected.");
  692. }
  693. //
  694. // A remote version of Evaluate
  695. //
  696. protected override string Evaluate (string input)
  697. {
  698. ns.WriteString (input);
  699. while (true) {
  700. AgentStatus s = (AgentStatus) ns.ReadByte ();
  701. switch (s){
  702. case AgentStatus.PARTIAL_INPUT:
  703. return input;
  704. case AgentStatus.ERROR:
  705. string err = ns.GetString ();
  706. Console.Error.WriteLine (err);
  707. break;
  708. case AgentStatus.RESULT_NOT_SET:
  709. return null;
  710. case AgentStatus.RESULT_SET:
  711. string res = ns.GetString ();
  712. Console.WriteLine (res);
  713. return null;
  714. }
  715. }
  716. }
  717. public override int Run (string [] startup_files)
  718. {
  719. // The difference is that we do not call Evaluator.Init, that is done on the target
  720. this.startup_files = startup_files;
  721. return ReadEvalPrintLoop ();
  722. }
  723. protected override void ConsoleInterrupt (object sender, ConsoleCancelEventArgs a)
  724. {
  725. // Do not about our program
  726. a.Cancel = true;
  727. interrupt_stream.WriteByte (0);
  728. int c = interrupt_stream.ReadByte ();
  729. if (c != -1)
  730. Console.WriteLine ("Execution interrupted");
  731. }
  732. }
  733. //
  734. // This is the agent loaded into the target process when using --attach.
  735. //
  736. class CSharpAgent
  737. {
  738. NetworkStream interrupt_stream;
  739. readonly Evaluator evaluator;
  740. TextWriter stderr;
  741. public CSharpAgent (Evaluator evaluator, String arg, TextWriter stderr)
  742. {
  743. this.evaluator = evaluator;
  744. this.stderr = stderr;
  745. new Thread (new ParameterizedThreadStart (Run)).Start (arg);
  746. }
  747. public void InterruptListener ()
  748. {
  749. while (true){
  750. int b = interrupt_stream.ReadByte();
  751. if (b == -1)
  752. return;
  753. evaluator.Interrupt ();
  754. interrupt_stream.WriteByte (0);
  755. }
  756. }
  757. public void Run (object o)
  758. {
  759. string arg = (string)o;
  760. string ports = arg.Substring (8);
  761. int sp = ports.IndexOf (':');
  762. int port = Int32.Parse (ports.Substring (0, sp));
  763. int interrupt_port = Int32.Parse (ports.Substring (sp+1));
  764. Console.WriteLine ("csharp-agent: started, connecting to localhost:" + port);
  765. TcpClient client = new TcpClient ("127.0.0.1", port);
  766. TcpClient interrupt_client = new TcpClient ("127.0.0.1", interrupt_port);
  767. Console.WriteLine ("csharp-agent: connected.");
  768. NetworkStream s = client.GetStream ();
  769. interrupt_stream = interrupt_client.GetStream ();
  770. new Thread (InterruptListener).Start ();
  771. try {
  772. // Add all assemblies loaded later
  773. AppDomain.CurrentDomain.AssemblyLoad += AssemblyLoaded;
  774. // Add all currently loaded assemblies
  775. foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies ()) {
  776. // Some assemblies seem to be already loaded, and loading them again causes 'defined multiple times' errors
  777. if (a.GetName ().Name != "mscorlib" && a.GetName ().Name != "System.Core" && a.GetName ().Name != "System")
  778. evaluator.ReferenceAssembly (a);
  779. }
  780. RunRepl (s);
  781. } finally {
  782. AppDomain.CurrentDomain.AssemblyLoad -= AssemblyLoaded;
  783. client.Close ();
  784. interrupt_client.Close ();
  785. Console.WriteLine ("csharp-agent: disconnected.");
  786. }
  787. }
  788. void AssemblyLoaded (object sender, AssemblyLoadEventArgs e)
  789. {
  790. evaluator.ReferenceAssembly (e.LoadedAssembly);
  791. }
  792. public void RunRepl (NetworkStream s)
  793. {
  794. string input = null;
  795. while (!InteractiveBase.QuitRequested) {
  796. try {
  797. string error_string;
  798. StringWriter error_output = (StringWriter)stderr;
  799. string line = s.GetString ();
  800. bool result_set;
  801. object result;
  802. if (input == null)
  803. input = line;
  804. else
  805. input = input + "\n" + line;
  806. try {
  807. input = evaluator.Evaluate (input, out result, out result_set);
  808. } catch (Exception e) {
  809. s.WriteByte ((byte) AgentStatus.ERROR);
  810. s.WriteString (e.ToString ());
  811. s.WriteByte ((byte) AgentStatus.RESULT_NOT_SET);
  812. continue;
  813. }
  814. if (input != null){
  815. s.WriteByte ((byte) AgentStatus.PARTIAL_INPUT);
  816. continue;
  817. }
  818. // Send warnings and errors back
  819. error_string = error_output.ToString ();
  820. if (error_string.Length != 0){
  821. s.WriteByte ((byte) AgentStatus.ERROR);
  822. s.WriteString (error_output.ToString ());
  823. error_output.GetStringBuilder ().Clear ();
  824. }
  825. if (result_set){
  826. s.WriteByte ((byte) AgentStatus.RESULT_SET);
  827. StringWriter sr = new StringWriter ();
  828. CSharpShell.PrettyPrint (sr, result);
  829. s.WriteString (sr.ToString ());
  830. } else {
  831. s.WriteByte ((byte) AgentStatus.RESULT_NOT_SET);
  832. }
  833. } catch (IOException) {
  834. break;
  835. } catch (Exception e){
  836. Console.WriteLine (e);
  837. }
  838. }
  839. }
  840. }
  841. public class UnixUtils {
  842. [System.Runtime.InteropServices.DllImport ("libc", EntryPoint="isatty")]
  843. extern static int _isatty (int fd);
  844. public static bool isatty (int fd)
  845. {
  846. try {
  847. return _isatty (fd) == 1;
  848. } catch {
  849. return false;
  850. }
  851. }
  852. }
  853. #endif
  854. }
  855. namespace Mono.Management
  856. {
  857. interface IVirtualMachine {
  858. void LoadAgent (string filename, string args);
  859. }
  860. }