GenericParameter.cs: override Module properly
[mcs.git] / tools / csharp / repl.cs
blob668602e794a053903d7a049ebd24c4ea5ae005a7
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
13 // TODO:
14 // Do not print results in Evaluate, do that elsewhere in preparation for Eval refactoring.
15 // Driver.PartialReset should not reset the coretypes, nor the optional types, to avoid
16 // computing that on every call.
18 using System;
19 using System.IO;
20 using System.Text;
21 using System.Globalization;
22 using System.Collections;
23 using System.Reflection;
24 using System.Reflection.Emit;
25 using System.Threading;
26 using System.Net;
27 using System.Net.Sockets;
28 using System.Collections.Generic;
30 using Mono.CSharp;
31 using Mono.Attach;
33 namespace Mono {
35 public class Driver {
37 static int Main (string [] args)
39 if (args.Length > 0 && args [0] == "--attach") {
40 new ClientCSharpShell (Int32.Parse (args [1])).Run (null);
41 return 0;
42 } else if (args.Length > 0 && args [0].StartsWith ("--agent:")) {
43 new CSharpAgent (args [0]);
44 return 0;
45 } else {
46 string [] startup_files;
47 try {
48 startup_files = Evaluator.InitAndGetStartupFiles (args);
49 Evaluator.DescribeTypeExpressions = true;
50 Evaluator.InteractiveBaseClass = typeof (InteractiveBaseShell);
51 } catch {
52 return 1;
55 return new CSharpShell ().Run (startup_files);
60 public class InteractiveBaseShell : InteractiveBase {
61 static bool tab_at_start_completes;
63 static InteractiveBaseShell ()
65 tab_at_start_completes = false;
68 internal static Mono.Terminal.LineEditor Editor;
70 public static bool TabAtStartCompletes {
71 get {
72 return tab_at_start_completes;
75 set {
76 tab_at_start_completes = value;
77 if (Editor != null)
78 Editor.TabAtStartCompletes = value;
82 public static new string help {
83 get {
84 return InteractiveBase.help +
85 " TabAtStartCompletes - Whether tab will complete even on emtpy lines\n";
90 public class CSharpShell {
91 static bool isatty = true;
92 string [] startup_files;
94 Mono.Terminal.LineEditor editor;
95 bool dumb;
97 protected virtual void ConsoleInterrupt (object sender, ConsoleCancelEventArgs a)
99 // Do not about our program
100 a.Cancel = true;
102 Mono.CSharp.Evaluator.Interrupt ();
105 void SetupConsole ()
107 string term = Environment.GetEnvironmentVariable ("TERM");
108 dumb = term == "dumb" || term == null || isatty == false;
110 editor = new Mono.Terminal.LineEditor ("csharp", 300);
111 InteractiveBaseShell.Editor = editor;
113 editor.AutoCompleteEvent += delegate (string s, int pos){
114 string prefix = null;
116 string complete = s.Substring (0, pos);
118 string [] completions = Evaluator.GetCompletions (complete, out prefix);
120 return new Mono.Terminal.LineEditor.Completion (prefix, completions);
123 #if false
125 // This is a sample of how completions sould be implemented.
127 editor.AutoCompleteEvent += delegate (string s, int pos){
129 // Single match: "Substring": Sub-string
130 if (s.EndsWith ("Sub")){
131 return new string [] { "string" };
134 // Multiple matches: "ToString" and "ToLower"
135 if (s.EndsWith ("T")){
136 return new string [] { "ToString", "ToLower" };
138 return null;
140 #endif
142 Console.CancelKeyPress += ConsoleInterrupt;
145 string GetLine (bool primary)
147 string prompt = primary ? InteractiveBase.Prompt : InteractiveBase.ContinuationPrompt;
149 if (dumb){
150 if (isatty)
151 Console.Write (prompt);
153 return Console.ReadLine ();
154 } else {
155 return editor.Edit (prompt, "");
159 delegate string ReadLiner (bool primary);
161 void InitializeUsing ()
163 Evaluate ("using System; using System.Linq; using System.Collections.Generic; using System.Collections;");
166 void InitTerminal ()
168 isatty = UnixUtils.isatty (0) && UnixUtils.isatty (1);
170 // Work around, since Console is not accounting for
171 // cursor position when writing to Stderr. It also
172 // has the undesirable side effect of making
173 // errors plain, with no coloring.
174 // Report.Stderr = Console.Out;
175 SetupConsole ();
177 if (isatty)
178 Console.WriteLine ("Mono C# Shell, type \"help;\" for help\n\nEnter statements below.");
182 void ExecuteSources (IEnumerable<string> sources, bool ignore_errors)
184 foreach (string file in sources){
185 try {
186 try {
187 using (System.IO.StreamReader r = System.IO.File.OpenText (file)){
188 ReadEvalPrintLoopWith (p => r.ReadLine ());
190 } catch (FileNotFoundException){
191 Console.Error.WriteLine ("cs2001: Source file `{0}' not found", file);
192 return;
194 } catch {
195 if (!ignore_errors)
196 throw;
201 protected virtual void LoadStartupFiles ()
203 string dir = Path.Combine (
204 Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData),
205 "csharp");
206 if (!Directory.Exists (dir))
207 return;
209 List<string> sources = new List<string> ();
210 List<string> libraries = new List<string> ();
212 foreach (string file in System.IO.Directory.GetFiles (dir)){
213 string l = file.ToLower ();
215 if (l.EndsWith (".cs"))
216 sources.Add (file);
217 else if (l.EndsWith (".dll"))
218 libraries.Add (file);
221 foreach (string file in libraries)
222 Evaluator.LoadAssembly (file);
224 ExecuteSources (sources, true);
227 void ReadEvalPrintLoopWith (ReadLiner readline)
229 string expr = null;
230 while (!InteractiveBase.QuitRequested){
231 string input = readline (expr == null);
232 if (input == null)
233 return;
235 if (input == "")
236 continue;
238 expr = expr == null ? input : expr + "\n" + input;
240 expr = Evaluate (expr);
244 public int ReadEvalPrintLoop ()
246 if (startup_files != null && startup_files.Length == 0)
247 InitTerminal ();
249 InitializeUsing ();
251 LoadStartupFiles ();
254 // Interactive or startup files provided?
256 if (startup_files.Length != 0)
257 ExecuteSources (startup_files, false);
258 else
259 ReadEvalPrintLoopWith (GetLine);
261 return 0;
264 protected virtual string Evaluate (string input)
266 bool result_set;
267 object result;
269 try {
270 input = Evaluator.Evaluate (input, out result, out result_set);
272 if (result_set){
273 PrettyPrint (Console.Out, result);
274 Console.WriteLine ();
276 } catch (Exception e){
277 Console.WriteLine (e);
278 return null;
281 return input;
284 static void p (TextWriter output, string s)
286 output.Write (s);
289 static string EscapeString (string s)
291 return s.Replace ("\"", "\\\"");
294 static void EscapeChar (TextWriter output, char c)
296 if (c == '\''){
297 output.Write ("'\\''");
298 return;
300 if (c > 32){
301 output.Write ("'{0}'", c);
302 return;
304 switch (c){
305 case '\a':
306 output.Write ("'\\a'");
307 break;
309 case '\b':
310 output.Write ("'\\b'");
311 break;
313 case '\n':
314 output.Write ("'\\n'");
315 break;
317 case '\v':
318 output.Write ("'\\v'");
319 break;
321 case '\r':
322 output.Write ("'\\r'");
323 break;
325 case '\f':
326 output.Write ("'\\f'");
327 break;
329 case '\t':
330 output.Write ("'\\t");
331 break;
333 default:
334 output.Write ("'\\x{0:x}", (int) c);
335 break;
339 internal static void PrettyPrint (TextWriter output, object result)
341 if (result == null){
342 p (output, "null");
343 return;
346 if (result is Array){
347 Array a = (Array) result;
349 p (output, "{ ");
350 int top = a.GetUpperBound (0);
351 for (int i = a.GetLowerBound (0); i <= top; i++){
352 PrettyPrint (output, a.GetValue (i));
353 if (i != top)
354 p (output, ", ");
356 p (output, " }");
357 } else if (result is bool){
358 if ((bool) result)
359 p (output, "true");
360 else
361 p (output, "false");
362 } else if (result is string){
363 p (output, String.Format ("\"{0}\"", EscapeString ((string)result)));
364 } else if (result is IDictionary){
365 IDictionary dict = (IDictionary) result;
366 int top = dict.Count, count = 0;
368 p (output, "{");
369 foreach (DictionaryEntry entry in dict){
370 count++;
371 p (output, "{ ");
372 PrettyPrint (output, entry.Key);
373 p (output, ", ");
374 PrettyPrint (output, entry.Value);
375 if (count != top)
376 p (output, " }, ");
377 else
378 p (output, " }");
380 p (output, "}");
381 } else if (result is IEnumerable) {
382 int i = 0;
383 p (output, "{ ");
384 foreach (object item in (IEnumerable) result) {
385 if (i++ != 0)
386 p (output, ", ");
388 PrettyPrint (output, item);
390 p (output, " }");
391 } else if (result is char) {
392 EscapeChar (output, (char) result);
393 } else {
394 p (output, result.ToString ());
398 public CSharpShell ()
402 public virtual int Run (string [] startup_files)
404 this.startup_files = startup_files;
405 return ReadEvalPrintLoop ();
411 // A shell connected to a CSharpAgent running in a remote process.
412 // - maybe add 'class_name' and 'method_name' arguments to LoadAgent.
413 // - Support Gtk and Winforms main loops if detected, this should
414 // probably be done as a separate agent in a separate place.
416 class ClientCSharpShell : CSharpShell {
417 NetworkStream ns, interrupt_stream;
419 public ClientCSharpShell (int pid)
421 // Create a server socket we listen on whose address is passed to the agent
422 TcpListener listener = new TcpListener (new IPEndPoint (IPAddress.Loopback, 0));
423 listener.Start ();
424 TcpListener interrupt_listener = new TcpListener (new IPEndPoint (IPAddress.Loopback, 0));
425 interrupt_listener.Start ();
427 string agent_assembly = typeof (ClientCSharpShell).Assembly.Location;
428 string agent_arg = String.Format ("--agent:{0}:{1}" ,
429 ((IPEndPoint)listener.Server.LocalEndPoint).Port,
430 ((IPEndPoint)interrupt_listener.Server.LocalEndPoint).Port);
432 VirtualMachine vm = new VirtualMachine (pid);
433 vm.Attach (agent_assembly, agent_arg);
435 /* Wait for the client to connect */
436 TcpClient client = listener.AcceptTcpClient ();
437 ns = client.GetStream ();
438 TcpClient interrupt_client = interrupt_listener.AcceptTcpClient ();
439 interrupt_stream = interrupt_client.GetStream ();
441 Console.WriteLine ("Connected.");
445 // A remote version of Evaluate
447 protected override string Evaluate (string input)
449 ns.WriteString (input);
450 while (true) {
451 AgentStatus s = (AgentStatus) ns.ReadByte ();
453 switch (s){
454 case AgentStatus.PARTIAL_INPUT:
455 return input;
457 case AgentStatus.ERROR:
458 string err = ns.GetString ();
459 Console.Error.WriteLine (err);
460 break;
462 case AgentStatus.RESULT_NOT_SET:
463 return null;
465 case AgentStatus.RESULT_SET:
466 string res = ns.GetString ();
467 Console.WriteLine (res);
468 return null;
473 public override int Run (string [] startup_files)
475 // The difference is that we do not call Evaluator.Init, that is done on the target
476 return ReadEvalPrintLoop ();
479 protected override void ConsoleInterrupt (object sender, ConsoleCancelEventArgs a)
481 // Do not about our program
482 a.Cancel = true;
484 interrupt_stream.WriteByte (0);
485 int c = interrupt_stream.ReadByte ();
486 if (c != -1)
487 Console.WriteLine ("Execution interrupted");
493 // Stream helper extension methods
495 public static class StreamHelper {
496 static DataConverter converter = DataConverter.LittleEndian;
498 public static int GetInt (this Stream stream)
500 byte [] b = new byte [4];
501 if (stream.Read (b, 0, 4) != 4)
502 throw new IOException ("End reached");
503 return converter.GetInt32 (b, 0);
506 public static string GetString (this Stream stream)
508 int len = stream.GetInt ();
509 byte [] b = new byte [len];
510 if (stream.Read (b, 0, len) != len)
511 throw new IOException ("End reached");
512 return Encoding.UTF8.GetString (b);
515 public static void WriteInt (this Stream stream, int n)
517 byte [] bytes = converter.GetBytes (n);
518 stream.Write (bytes, 0, bytes.Length);
521 public static void WriteString (this Stream stream, string s)
523 stream.WriteInt (s.Length);
524 byte [] bytes = Encoding.UTF8.GetBytes (s);
525 stream.Write (bytes, 0, bytes.Length);
529 public enum AgentStatus : byte {
530 // Received partial input, complete
531 PARTIAL_INPUT = 1,
533 // The result was set, expect the string with the result
534 RESULT_SET = 2,
536 // No result was set, complete
537 RESULT_NOT_SET = 3,
539 // Errors and warnings string follows
540 ERROR = 4,
544 // This is the agent loaded into the target process when using --attach.
546 class CSharpAgent
548 NetworkStream interrupt_stream;
550 public CSharpAgent (String arg)
552 new Thread (new ParameterizedThreadStart (Run)).Start (arg);
555 public void InterruptListener ()
557 while (true){
558 int b = interrupt_stream.ReadByte();
559 if (b == -1)
560 return;
561 Evaluator.Interrupt ();
562 interrupt_stream.WriteByte (0);
566 public void Run (object o)
568 string arg = (string)o;
569 string ports = arg.Substring (8);
570 int sp = ports.IndexOf (':');
571 int port = Int32.Parse (ports.Substring (0, sp));
572 int interrupt_port = Int32.Parse (ports.Substring (sp+1));
574 Console.WriteLine ("csharp-agent: started, connecting to localhost:" + port);
576 TcpClient client = new TcpClient ("127.0.0.1", port);
577 TcpClient interrupt_client = new TcpClient ("127.0.0.1", interrupt_port);
578 Console.WriteLine ("csharp-agent: connected.");
580 NetworkStream s = client.GetStream ();
581 interrupt_stream = interrupt_client.GetStream ();
582 new Thread (InterruptListener).Start ();
584 try {
585 Evaluator.Init (new string [0]);
586 } catch {
587 // TODO: send a result back.
588 Console.WriteLine ("csharp-agent: initialization failed");
589 return;
592 try {
593 // Add all assemblies loaded later
594 AppDomain.CurrentDomain.AssemblyLoad += AssemblyLoaded;
596 // Add all currently loaded assemblies
597 foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies ())
598 Evaluator.ReferenceAssembly (a);
600 RunRepl (s);
601 } finally {
602 AppDomain.CurrentDomain.AssemblyLoad -= AssemblyLoaded;
603 client.Close ();
604 interrupt_client.Close ();
605 Console.WriteLine ("csharp-agent: disconnected.");
609 static void AssemblyLoaded (object sender, AssemblyLoadEventArgs e)
611 Evaluator.ReferenceAssembly (e.LoadedAssembly);
614 public void RunRepl (NetworkStream s)
616 string input = null;
618 while (!InteractiveBase.QuitRequested) {
619 try {
620 string error_string;
621 StringWriter error_output = new StringWriter ();
622 // Report.Stderr = error_output;
624 string line = s.GetString ();
626 bool result_set;
627 object result;
629 if (input == null)
630 input = line;
631 else
632 input = input + "\n" + line;
634 try {
635 input = Evaluator.Evaluate (input, out result, out result_set);
636 } catch (Exception e) {
637 s.WriteByte ((byte) AgentStatus.ERROR);
638 s.WriteString (e.ToString ());
639 s.WriteByte ((byte) AgentStatus.RESULT_NOT_SET);
640 continue;
643 if (input != null){
644 s.WriteByte ((byte) AgentStatus.PARTIAL_INPUT);
645 continue;
648 // Send warnings and errors back
649 error_string = error_output.ToString ();
650 if (error_string.Length != 0){
651 s.WriteByte ((byte) AgentStatus.ERROR);
652 s.WriteString (error_output.ToString ());
655 if (result_set){
656 s.WriteByte ((byte) AgentStatus.RESULT_SET);
657 StringWriter sr = new StringWriter ();
658 CSharpShell.PrettyPrint (sr, result);
659 s.WriteString (sr.ToString ());
660 } else {
661 s.WriteByte ((byte) AgentStatus.RESULT_NOT_SET);
663 } catch (IOException) {
664 break;
665 } catch (Exception e){
666 Console.WriteLine (e);
672 public class UnixUtils {
673 [System.Runtime.InteropServices.DllImport ("libc", EntryPoint="isatty")]
674 extern static int _isatty (int fd);
676 public static bool isatty (int fd)
678 try {
679 return _isatty (fd) == 1;
680 } catch {
681 return false;
687 namespace Mono.Management
689 interface IVirtualMachine {
690 void LoadAgent (string filename, string args);