Merge pull request #819 from brendanzagaeski/patch-1
[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                         InteractiveBaseShell.Editor = editor;
202
203                         editor.AutoCompleteEvent += delegate (string s, int pos){
204                                 string prefix = null;
205
206                                 string complete = s.Substring (0, pos);
207                                 
208                                 string [] completions = evaluator.GetCompletions (complete, out prefix);
209                                 
210                                 return new Mono.Terminal.LineEditor.Completion (prefix, completions);
211                         };
212                         
213 #if false
214                         //
215                         // This is a sample of how completions sould be implemented.
216                         //
217                         editor.AutoCompleteEvent += delegate (string s, int pos){
218
219                                 // Single match: "Substring": Sub-string
220                                 if (s.EndsWith ("Sub")){
221                                         return new string [] { "string" };
222                                 }
223
224                                 // Multiple matches: "ToString" and "ToLower"
225                                 if (s.EndsWith ("T")){
226                                         return new string [] { "ToString", "ToLower" };
227                                 }
228                                 return null;
229                         };
230 #endif
231                         
232                         Console.CancelKeyPress += ConsoleInterrupt;
233                 }
234
235                 string GetLine (bool primary)
236                 {
237                         string prompt = primary ? InteractiveBase.Prompt : InteractiveBase.ContinuationPrompt;
238
239                         if (dumb){
240                                 if (isatty)
241                                         Console.Write (prompt);
242
243                                 return Console.ReadLine ();
244                         } else {
245                                 return editor.Edit (prompt, "");
246                         }
247                 }
248
249                 delegate string ReadLiner (bool primary);
250
251                 void InitializeUsing ()
252                 {
253                         Evaluate ("using System; using System.Linq; using System.Collections.Generic; using System.Collections;");
254                 }
255
256                 void InitTerminal (bool show_banner)
257                 {
258                         int p = (int) Environment.OSVersion.Platform;
259                         is_unix = (p == 4) || (p == 128);
260
261 #if NET_4_5
262                         isatty = !Console.IsInputRedirected && !Console.IsOutputRedirected;
263 #else
264                         isatty = true;
265 #endif
266
267                         // Work around, since Console is not accounting for
268                         // cursor position when writing to Stderr.  It also
269                         // has the undesirable side effect of making
270                         // errors plain, with no coloring.
271 //                      Report.Stderr = Console.Out;
272                         SetupConsole ();
273
274                         if (isatty && show_banner)
275                                 Console.WriteLine ("Mono C# Shell, type \"help;\" for help\n\nEnter statements below.");
276
277                 }
278
279                 void ExecuteSources (IEnumerable<string> sources, bool ignore_errors)
280                 {
281                         foreach (string file in sources){
282                                 try {
283                                         try {
284                                                 bool first = true;
285                         
286                                                 using (System.IO.StreamReader r = System.IO.File.OpenText (file)){
287                                                         ReadEvalPrintLoopWith (p => {
288                                                                 var line = r.ReadLine ();
289                                                                 if (first){
290                                                                         if (line.StartsWith ("#!"))
291                                                                                 line = r.ReadLine ();
292                                                                         first = false;
293                                                                 }
294                                                                 return line;
295                                                         });
296                                                 }
297                                         } catch (FileNotFoundException){
298                                                 Console.Error.WriteLine ("cs2001: Source file `{0}' not found", file);
299                                                 return;
300                                         }
301                                 } catch {
302                                         if (!ignore_errors)
303                                                 throw;
304                                 }
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
316                         List<string> sources = new List<string> ();
317                         List<string> libraries = new List<string> ();
318                         
319                         foreach (string file in System.IO.Directory.GetFiles (dir)){
320                                 string l = file.ToLower ();
321                                 
322                                 if (l.EndsWith (".cs"))
323                                         sources.Add (file);
324                                 else if (l.EndsWith (".dll"))
325                                         libraries.Add (file);
326                         }
327
328                         foreach (string file in libraries)
329                                 evaluator.LoadAssembly (file);
330
331                         ExecuteSources (sources, true);
332                 }
333
334                 void ReadEvalPrintLoopWith (ReadLiner readline)
335                 {
336                         string expr = null;
337                         while (!InteractiveBase.QuitRequested){
338                                 string input = readline (expr == null);
339                                 if (input == null)
340                                         return;
341
342                                 if (input == "")
343                                         continue;
344
345                                 expr = expr == null ? input : expr + "\n" + input;
346                                 
347                                 expr = Evaluate (expr);
348                         }
349                 }
350
351                 public int ReadEvalPrintLoop ()
352                 {
353                         if (startup_files != null && startup_files.Length == 0)
354                                 InitTerminal (startup_files.Length == 0 && Driver.StartupEvalExpression == null);
355
356                         InitializeUsing ();
357
358                         LoadStartupFiles ();
359
360                         if (startup_files != null && startup_files.Length != 0) {
361                                 ExecuteSources (startup_files, false);
362                         } else {
363                                 if (Driver.StartupEvalExpression != null){
364                                         ReadEvalPrintLoopWith (p => {
365                                                 var ret = Driver.StartupEvalExpression;
366                                                 Driver.StartupEvalExpression = null;
367                                                 return ret;
368                                                 });
369                                 } else {
370                                         ReadEvalPrintLoopWith (GetLine);
371                                 }
372                                 
373                                 editor.SaveHistory ();
374                         }
375
376                         Console.CancelKeyPress -= ConsoleInterrupt;
377                         
378                         return 0;
379                 }
380
381                 protected virtual string Evaluate (string input)
382                 {
383                         bool result_set;
384                         object result;
385
386                         try {
387                                 input = evaluator.Evaluate (input, out result, out result_set);
388
389                                 if (result_set){
390                                         PrettyPrint (Console.Out, result);
391                                         Console.WriteLine ();
392                                 }
393                         } catch (Exception e){
394                                 Console.WriteLine (e);
395                                 return null;
396                         }
397                         
398                         return input;
399                 }
400
401                 static void p (TextWriter output, string s)
402                 {
403                         output.Write (s);
404                 }
405
406                 static string EscapeString (string s)
407                 {
408                         return s.Replace ("\"", "\\\"");
409                 }
410                 
411                 static void EscapeChar (TextWriter output, char c)
412                 {
413                         if (c == '\''){
414                                 output.Write ("'\\''");
415                                 return;
416                         }
417                         if (c > 32){
418                                 output.Write ("'{0}'", c);
419                                 return;
420                         }
421                         switch (c){
422                         case '\a':
423                                 output.Write ("'\\a'");
424                                 break;
425
426                         case '\b':
427                                 output.Write ("'\\b'");
428                                 break;
429                                 
430                         case '\n':
431                                 output.Write ("'\\n'");
432                                 break;
433                                 
434                         case '\v':
435                                 output.Write ("'\\v'");
436                                 break;
437                                 
438                         case '\r':
439                                 output.Write ("'\\r'");
440                                 break;
441                                 
442                         case '\f':
443                                 output.Write ("'\\f'");
444                                 break;
445                                 
446                         case '\t':
447                                 output.Write ("'\\t");
448                                 break;
449
450                         default:
451                                 output.Write ("'\\x{0:x}", (int) c);
452                                 break;
453                         }
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                 
474                 internal static void PrettyPrint (TextWriter output, object result)
475                 {
476                         if (result == null){
477                                 p (output, "null");
478                                 return;
479                         }
480                         
481                         if (result is Array){
482                                 Array a = (Array) result;
483                                 
484                                 p (output, "{ ");
485                                 int top = a.GetUpperBound (0);
486                                 for (int i = a.GetLowerBound (0); i <= top; i++){
487                                         PrettyPrint (output, a.GetValue (i));
488                                         if (i != top)
489                                                 p (output, ", ");
490                                 }
491                                 p (output, " }");
492                         } else if (result is bool){
493                                 if ((bool) result)
494                                         p (output, "true");
495                                 else
496                                         p (output, "false");
497                         } else if (result is string){
498                                 p (output, String.Format ("\"{0}\"", EscapeString ((string)result)));
499                         } else if (result is IDictionary){
500                                 IDictionary dict = (IDictionary) result;
501                                 int top = dict.Count, count = 0;
502                                 
503                                 p (output, "{");
504                                 foreach (DictionaryEntry entry in dict){
505                                         count++;
506                                         p (output, "{ ");
507                                         PrettyPrint (output, entry.Key);
508                                         p (output, ", ");
509                                         PrettyPrint (output, entry.Value);
510                                         if (count != top)
511                                                 p (output, " }, ");
512                                         else
513                                                 p (output, " }");
514                                 }
515                                 p (output, "}");
516                         } else if (WorksAsEnumerable (result)) {
517                                 int i = 0;
518                                 p (output, "{ ");
519                                 foreach (object item in (IEnumerable) result) {
520                                         if (i++ != 0)
521                                                 p (output, ", ");
522
523                                         PrettyPrint (output, item);
524                                 }
525                                 p (output, " }");
526                         } else if (result is char) {
527                                 EscapeChar (output, (char) result);
528                         } else {
529                                 p (output, result.ToString ());
530                         }
531                 }
532
533                 public virtual int Run (string [] startup_files)
534                 {
535                         this.startup_files = startup_files;
536                         return ReadEvalPrintLoop ();
537                 }
538                 
539         }
540
541         //
542         // Stream helper extension methods
543         //
544         public static class StreamHelper {
545                 static DataConverter converter = DataConverter.LittleEndian;
546                 
547                 static void GetBuffer (this Stream stream, byte [] b)
548                 {
549                         int n, offset = 0;
550                         int len = b.Length;
551
552                         do {
553                                 n = stream.Read (b, offset, len);
554                                 if (n == 0)
555                                         throw new IOException ("End reached");
556
557                                 offset += n;
558                                 len -= n;
559                         } while (len > 0);
560                 }
561
562                 public static int GetInt (this Stream stream)
563                 {
564                         byte [] b = new byte [4];
565                         stream.GetBuffer (b);
566                         return converter.GetInt32 (b, 0);
567                 }
568
569                 public static string GetString (this Stream stream)
570                 {
571                         int len = stream.GetInt ();
572                         if (len == 0)
573                                 return "";
574
575                         byte [] b = new byte [len];
576                         stream.GetBuffer (b);
577
578                         return Encoding.UTF8.GetString (b);
579                 }
580
581                 public static void WriteInt (this Stream stream, int n)
582                 {
583                         byte [] bytes = converter.GetBytes (n);
584                         stream.Write (bytes, 0, bytes.Length);
585                 }
586         
587                 public static void WriteString (this Stream stream, string s)
588                 {
589                         stream.WriteInt (s.Length);
590                         byte [] bytes = Encoding.UTF8.GetBytes (s);
591                         stream.Write (bytes, 0, bytes.Length);
592                 }
593         }
594         
595         public enum AgentStatus : byte {
596                 // Received partial input, complete
597                 PARTIAL_INPUT  = 1,
598         
599                 // The result was set, expect the string with the result
600                 RESULT_SET     = 2,
601         
602                 // No result was set, complete
603                 RESULT_NOT_SET = 3,
604         
605                 // Errors and warnings string follows
606                 ERROR          = 4,
607
608                 // Stdout
609                 STDOUT         = 5,
610         }
611
612         class ClientCSharpShell : CSharpShell {
613                 string target_host;
614                 int target_port;
615                 
616                 public ClientCSharpShell (Evaluator evaluator, string target_host, int target_port) : base (evaluator)
617                 {
618                         this.target_port = target_port;
619                         this.target_host = target_host;
620                 }
621
622                 T ConnectServer<T> (Func<NetworkStream,T> callback, Action<Exception> error)
623                 {
624                         try {
625                                 var client = new TcpClient (target_host, target_port);
626                                 var ns = client.GetStream ();
627                                 T ret = callback (ns);
628                                 ns.Flush ();
629                                 ns.Close ();
630                                 client.Close ();
631                                 return ret;
632                         } catch (Exception e){
633                                 error (e);
634                                 return default(T);
635                         }
636                 }
637                 
638                 protected override string Evaluate (string input)
639                 {
640                         return ConnectServer<string> ((ns)=> {
641                                 try {
642                                         ns.WriteString ("EVALTXT");
643                                         ns.WriteString (input);
644
645                                         while (true) {
646                                                 AgentStatus s = (AgentStatus) ns.ReadByte ();
647                                         
648                                                 switch (s){
649                                                 case AgentStatus.PARTIAL_INPUT:
650                                                         return input;
651                                                 
652                                                 case AgentStatus.ERROR:
653                                                         string err = ns.GetString ();
654                                                         Console.Error.WriteLine (err);
655                                                         break;
656
657                                                 case AgentStatus.STDOUT:
658                                                         string stdout = ns.GetString ();
659                                                         Console.WriteLine (stdout);
660                                                         break;
661                                                 
662                                                 case AgentStatus.RESULT_NOT_SET:
663                                                         return null;
664                                                 
665                                                 case AgentStatus.RESULT_SET:
666                                                         string res = ns.GetString ();
667                                                         Console.WriteLine (res);
668                                                         return null;
669                                                 }
670                                         }
671                                 } catch (Exception e){
672                                         Console.Error.WriteLine ("Error evaluating expression, exception: {0}", e);
673                                 }
674                                 return null;
675                         }, (e) => {
676                                 Console.Error.WriteLine ("Error communicating with server {0}", e);
677                         });
678                 }
679                 
680                 public override int Run (string [] startup_files)
681                 {
682                         // The difference is that we do not call Evaluator.Init, that is done on the target
683                         this.startup_files = startup_files;
684                         return ReadEvalPrintLoop ();
685                 }
686         
687                 protected override void ConsoleInterrupt (object sender, ConsoleCancelEventArgs a)
688                 {
689                         ConnectServer<int> ((ns)=> {
690                                 ns.WriteString ("INTERRUPT");
691                                 return 0;
692                         }, (e) => { });
693                 }
694                         
695         }
696
697 #if !ON_DOTNET
698         //
699         // A shell connected to a CSharpAgent running in a remote process.
700         //  - maybe add 'class_name' and 'method_name' arguments to LoadAgent.
701         //  - Support Gtk and Winforms main loops if detected, this should
702         //    probably be done as a separate agent in a separate place.
703         //
704         class ClientCSharpShell_v1 : CSharpShell {
705                 NetworkStream ns, interrupt_stream;
706                 
707                 public ClientCSharpShell_v1 (Evaluator evaluator, int pid)
708                         : base (evaluator)
709                 {
710                         // Create a server socket we listen on whose address is passed to the agent
711                         TcpListener listener = new TcpListener (new IPEndPoint (IPAddress.Loopback, 0));
712                         listener.Start ();
713                         TcpListener interrupt_listener = new TcpListener (new IPEndPoint (IPAddress.Loopback, 0));
714                         interrupt_listener.Start ();
715         
716                         string agent_assembly = typeof (ClientCSharpShell).Assembly.Location;
717                         string agent_arg = String.Format ("--agent:{0}:{1}" ,
718                                                           ((IPEndPoint)listener.Server.LocalEndPoint).Port,
719                                                           ((IPEndPoint)interrupt_listener.Server.LocalEndPoint).Port);
720         
721                         var vm = new Attach.VirtualMachine (pid);
722                         vm.Attach (agent_assembly, agent_arg);
723         
724                         /* Wait for the client to connect */
725                         TcpClient client = listener.AcceptTcpClient ();
726                         ns = client.GetStream ();
727                         TcpClient interrupt_client = interrupt_listener.AcceptTcpClient ();
728                         interrupt_stream = interrupt_client.GetStream ();
729         
730                         Console.WriteLine ("Connected.");
731                 }
732
733                 //
734                 // A remote version of Evaluate
735                 //
736                 protected override string Evaluate (string input)
737                 {
738                         ns.WriteString (input);
739                         while (true) {
740                                 AgentStatus s = (AgentStatus) ns.ReadByte ();
741         
742                                 switch (s){
743                                 case AgentStatus.PARTIAL_INPUT:
744                                         return input;
745         
746                                 case AgentStatus.ERROR:
747                                         string err = ns.GetString ();
748                                         Console.Error.WriteLine (err);
749                                         break;
750         
751                                 case AgentStatus.RESULT_NOT_SET:
752                                         return null;
753         
754                                 case AgentStatus.RESULT_SET:
755                                         string res = ns.GetString ();
756                                         Console.WriteLine (res);
757                                         return null;
758                                 }
759                         }
760                 }
761                 
762                 public override int Run (string [] startup_files)
763                 {
764                         // The difference is that we do not call Evaluator.Init, that is done on the target
765                         this.startup_files = startup_files;
766                         return ReadEvalPrintLoop ();
767                 }
768         
769                 protected override void ConsoleInterrupt (object sender, ConsoleCancelEventArgs a)
770                 {
771                         // Do not about our program
772                         a.Cancel = true;
773         
774                         interrupt_stream.WriteByte (0);
775                         int c = interrupt_stream.ReadByte ();
776                         if (c != -1)
777                                 Console.WriteLine ("Execution interrupted");
778                 }
779                         
780         }
781
782         //
783         // This is the agent loaded into the target process when using --attach.
784         //
785         class CSharpAgent
786         {
787                 NetworkStream interrupt_stream;
788                 readonly Evaluator evaluator;
789                 TextWriter stderr;
790                 
791                 public CSharpAgent (Evaluator evaluator, String arg, TextWriter stderr)
792                 {
793                         this.evaluator = evaluator;
794                         this.stderr = stderr;
795                         new Thread (new ParameterizedThreadStart (Run)).Start (arg);
796                 }
797
798                 public void InterruptListener ()
799                 {
800                         while (true){
801                                 int b = interrupt_stream.ReadByte();
802                                 if (b == -1)
803                                         return;
804                                 evaluator.Interrupt ();
805                                 interrupt_stream.WriteByte (0);
806                         }
807                 }
808                 
809                 public void Run (object o)
810                 {
811                         string arg = (string)o;
812                         string ports = arg.Substring (8);
813                         int sp = ports.IndexOf (':');
814                         int port = Int32.Parse (ports.Substring (0, sp));
815                         int interrupt_port = Int32.Parse (ports.Substring (sp+1));
816         
817                         Console.WriteLine ("csharp-agent: started, connecting to localhost:" + port);
818         
819                         TcpClient client = new TcpClient ("127.0.0.1", port);
820                         TcpClient interrupt_client = new TcpClient ("127.0.0.1", interrupt_port);
821                         Console.WriteLine ("csharp-agent: connected.");
822         
823                         NetworkStream s = client.GetStream ();
824                         interrupt_stream = interrupt_client.GetStream ();
825                         new Thread (InterruptListener).Start ();
826
827                         try {
828                                 // Add all assemblies loaded later
829                                 AppDomain.CurrentDomain.AssemblyLoad += AssemblyLoaded;
830         
831                                 // Add all currently loaded assemblies
832                                 foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies ()) {
833                                         // Some assemblies seem to be already loaded, and loading them again causes 'defined multiple times' errors
834                                         if (a.GetName ().Name != "mscorlib" && a.GetName ().Name != "System.Core" && a.GetName ().Name != "System")
835                                                 evaluator.ReferenceAssembly (a);
836                                 }
837         
838                                 RunRepl (s);
839                         } finally {
840                                 AppDomain.CurrentDomain.AssemblyLoad -= AssemblyLoaded;
841                                 client.Close ();
842                                 interrupt_client.Close ();
843                                 Console.WriteLine ("csharp-agent: disconnected.");                      
844                         }
845                 }
846         
847                 void AssemblyLoaded (object sender, AssemblyLoadEventArgs e)
848                 {
849                         evaluator.ReferenceAssembly (e.LoadedAssembly);
850                 }
851         
852                 public void RunRepl (NetworkStream s)
853                 {
854                         string input = null;
855
856                         while (!InteractiveBase.QuitRequested) {
857                                 try {
858                                         string error_string;
859                                         StringWriter error_output = (StringWriter)stderr;
860
861                                         string line = s.GetString ();
862         
863                                         bool result_set;
864                                         object result;
865         
866                                         if (input == null)
867                                                 input = line;
868                                         else
869                                                 input = input + "\n" + line;
870         
871                                         try {
872                                                 input = evaluator.Evaluate (input, out result, out result_set);
873                                         } catch (Exception e) {
874                                                 s.WriteByte ((byte) AgentStatus.ERROR);
875                                                 s.WriteString (e.ToString ());
876                                                 s.WriteByte ((byte) AgentStatus.RESULT_NOT_SET);
877                                                 continue;
878                                         }
879                                         
880                                         if (input != null){
881                                                 s.WriteByte ((byte) AgentStatus.PARTIAL_INPUT);
882                                                 continue;
883                                         }
884         
885                                         // Send warnings and errors back
886                                         error_string = error_output.ToString ();
887                                         if (error_string.Length != 0){
888                                                 s.WriteByte ((byte) AgentStatus.ERROR);
889                                                 s.WriteString (error_output.ToString ());
890                                                 error_output.GetStringBuilder ().Clear ();
891                                         }
892         
893                                         if (result_set){
894                                                 s.WriteByte ((byte) AgentStatus.RESULT_SET);
895                                                 StringWriter sr = new StringWriter ();
896                                                 CSharpShell.PrettyPrint (sr, result);
897                                                 s.WriteString (sr.ToString ());
898                                         } else {
899                                                 s.WriteByte ((byte) AgentStatus.RESULT_NOT_SET);
900                                         }
901                                 } catch (IOException) {
902                                         break;
903                                 } catch (Exception e){
904                                         Console.WriteLine (e);
905                                 }
906                         }
907                 }
908         }
909
910         public class UnixUtils {
911                 [System.Runtime.InteropServices.DllImport ("libc", EntryPoint="isatty")]
912                 extern static int _isatty (int fd);
913                         
914                 public static bool isatty (int fd)
915                 {
916                         try {
917                                 return _isatty (fd) == 1;
918                         } catch {
919                                 return false;
920                         }
921                 }
922         }
923 #endif
924 }
925         
926 namespace Mono.Management
927 {
928         interface IVirtualMachine {
929                 void LoadAgent (string filename, string args);
930         }
931 }
932