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