2 // repl.cs: Support for using the compiler in interactive mode (read-eval-print loop)
5 // Miguel de Icaza (miguel@gnome.org)
7 // Dual licensed under the terms of the MIT X11 or GNU GPL
9 // Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
10 // Copyright 2004, 2005, 2006, 2007, 2008 Novell, Inc
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.
21 using System.Globalization;
22 using System.Collections;
23 using System.Reflection;
24 using System.Reflection.Emit;
25 using System.Threading;
27 using System.Net.Sockets;
34 public static class CSharpShell {
35 static bool isatty = true;
37 static Mono.Terminal.LineEditor editor;
40 static void ConsoleInterrupt (object sender, ConsoleCancelEventArgs a)
42 // Do not about our program
45 Mono.CSharp.Evaluator.Interrupt ();
48 static void SetupConsole ()
50 string term = Environment.GetEnvironmentVariable ("TERM");
51 dumb = term == "dumb" || term == null || isatty == false;
53 editor = new Mono.Terminal.LineEditor ("csharp", 300);
54 Console.CancelKeyPress += ConsoleInterrupt;
57 static string GetLine (bool primary)
59 string prompt = primary ? InteractiveBase.Prompt : InteractiveBase.ContinuationPrompt;
63 Console.Write (prompt);
65 return Console.ReadLine ();
67 return editor.Edit (prompt, "");
71 delegate string ReadLiner (bool primary);
73 static void InitializeUsing ()
75 Evaluate ("using System; using System.Linq; using System.Collections.Generic; using System.Collections;");
78 static void InitTerminal ()
80 isatty = UnixUtils.isatty (0) && UnixUtils.isatty (1);
82 // Work around, since Console is not accounting for
83 // cursor position when writing to Stderr. It also
84 // has the undesirable side effect of making
85 // errors plain, with no coloring.
86 Report.Stderr = Console.Out;
90 Console.WriteLine ("Mono C# Shell, type \"help;\" for help\n\nEnter statements below.");
94 static void LoadStartupFiles ()
96 string dir = Path.Combine (
97 Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData),
99 if (!Directory.Exists (dir))
102 foreach (string file in Directory.GetFiles (dir)){
103 string l = file.ToLower ();
105 if (l.EndsWith (".cs")){
107 using (StreamReader r = File.OpenText (file)){
108 ReadEvalPrintLoopWith (p => r.ReadLine ());
112 } else if (l.EndsWith (".dll")){
113 Evaluator.LoadAssembly (file);
118 static void ReadEvalPrintLoopWith (ReadLiner readline)
122 string input = readline (expr == null);
129 expr = expr == null ? input : expr + "\n" + input;
131 expr = Evaluate (expr);
135 static public int ReadEvalPrintLoop ()
142 ReadEvalPrintLoopWith (GetLine);
147 static string Evaluate (string input)
153 input = Evaluator.Evaluate (input, out result, out result_set);
156 PrettyPrint (result);
157 Console.WriteLine ();
159 } catch (Exception e){
160 Console.WriteLine (e);
167 static void p (string s)
172 static string EscapeString (string s)
174 return s.Replace ("\"", "\\\"");
177 static void PrettyPrint (object result)
184 if (result is Array){
185 Array a = (Array) result;
188 int top = a.GetUpperBound (0);
189 for (int i = a.GetLowerBound (0); i <= top; i++){
190 PrettyPrint (a.GetValue (i));
195 } else if (result is bool){
200 } else if (result is string){
201 p (String.Format ("\"{0}\"", EscapeString ((string)result)));
202 } else if (result is IDictionary){
203 IDictionary dict = (IDictionary) result;
204 int top = dict.Count, count = 0;
207 foreach (DictionaryEntry entry in dict){
210 PrettyPrint (entry.Key);
212 PrettyPrint (entry.Value);
219 } else if (result is IEnumerable) {
222 foreach (object item in (IEnumerable) result) {
230 p (result.ToString ());
234 static int Main (string [] args)
236 if (args.Length > 0 && args [0] == "--attach") {
237 new AttachedCSharpShell (Int32.Parse (args [1]));
239 } else if (args.Length > 0 && args [0].StartsWith ("--agent")) {
240 new CSharpAgent (args [0]);
245 Evaluator.Init (args);
250 return ReadEvalPrintLoop ();
256 * A shell connected to a CSharpAgent running in a remote process.
258 * - using NOT.EXISTS works, but leads to an error later when mcs tries to search
260 * - it would be nice to provide some kind of autocompletion even in remote mode.
261 * - maybe add 'class_name' and 'method_name' arguments to LoadAgent.
263 class AttachedCSharpShell {
265 public AttachedCSharpShell (int pid) {
266 /* Create a server socket we listen on whose address is passed to the agent */
267 TcpListener listener = new TcpListener (new IPEndPoint (IPAddress.Loopback, 0));
270 string agent_assembly = typeof (AttachedCSharpShell).Assembly.Location;
271 string agent_arg = "--agent:" + ((IPEndPoint)listener.Server.LocalEndPoint).Port;
273 VirtualMachine vm = new VirtualMachine (pid);
274 vm.Attach (agent_assembly, agent_arg);
276 /* Wait for the client to connect */
277 TcpClient client = listener.AcceptTcpClient ();
278 NetworkStream s = client.GetStream ();
279 StreamReader sr = new StreamReader (s);
280 StreamWriter sw = new StreamWriter (s);
282 Console.WriteLine ("Connected.");
286 sw.WriteLine ("using System; using System.Linq; using System.Collections.Generic; using System.Collections;");
290 string line = sr.ReadLine ();
295 //LoadStartupFiles ();
300 string input = GetLine (expr == "");
307 sw.WriteLine (input);
310 /* Read the (possible) error messages */
312 string line = sr.ReadLine ();
317 if (line == "<RESULT>")
321 Console.WriteLine (line);
323 /* Read the result */
325 string line = sr.ReadLine ();
330 if (line == "<INPUT>")
333 Console.WriteLine (line);
335 /* Read the (possible) incomplete input */
338 string line = sr.ReadLine ();
351 static bool isatty = true;
353 static Mono.Terminal.LineEditor editor;
356 static void ConsoleInterrupt (object sender, ConsoleCancelEventArgs a)
358 // Do not about our program
361 Mono.CSharp.Evaluator.Interrupt ();
364 static void SetupConsole ()
366 string term = Environment.GetEnvironmentVariable ("TERM");
367 dumb = term == "dumb" || term == null || isatty == false;
369 editor = new Mono.Terminal.LineEditor ("csharp", 300);
370 Console.CancelKeyPress += ConsoleInterrupt;
373 static string GetLine (bool primary)
375 string prompt = primary ? InteractiveBase.Prompt : InteractiveBase.ContinuationPrompt;
379 Console.Write (prompt);
381 return Console.ReadLine ();
383 return editor.Edit (prompt, "");
387 static void InitTerminal ()
389 isatty = UnixUtils.isatty (0) && UnixUtils.isatty (1);
394 Console.WriteLine ("Mono C# Shell, type \"help;\" for help\n\nEnter statements below.");
398 namespace Mono.Management
400 interface IVirtualMachine {
401 void LoadAgent (string filename, string args);
406 * This is the agent loaded into the target process when using --attach.
410 public CSharpAgent (String arg) {
411 new Thread (new ParameterizedThreadStart (Run)).Start (arg);
414 public void Run (object o) {
415 string arg = (string)o;
416 int port = Int32.Parse (arg.Substring (arg.IndexOf (":") + 1));
418 Console.WriteLine ("csharp-agent: started, connecting to localhost:" + port);
420 TcpClient client = new TcpClient ("127.0.0.1", port);
421 Console.WriteLine ("csharp-agent: connected.");
423 NetworkStream s = client.GetStream ();
426 Evaluator.Init (new string [0]);
432 // Add all assemblies loaded later
433 AppDomain.CurrentDomain.AssemblyLoad += AssemblyLoaded;
435 // Add all currently loaded assemblies
436 foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies ())
437 Evaluator.ReferenceAssembly (a);
441 AppDomain.CurrentDomain.AssemblyLoad -= AssemblyLoaded;
443 Console.WriteLine ("csharp-agent: disconnected.");
447 static void AssemblyLoaded (object sender, AssemblyLoadEventArgs e) {
448 Evaluator.ReferenceAssembly (e.LoadedAssembly);
451 public void RunRepl (NetworkStream s) {
452 StreamReader r = new StreamReader (s);
453 StreamWriter w = new StreamWriter (s);
460 string line = r.ReadLine ();
468 input = input + "\n" + line;
470 // This will print any error messages to w
471 input = Evaluator.Evaluate (input, out result, out result_set);
475 // This separates the result from the possible error messages
476 w.WriteLine ("<RESULT>");
481 w.Write (result.ToString ());
484 // FIXME: This might occur in the output as well.
485 w.WriteLine ("<INPUT>");
486 /* The rest of the input */
488 w.WriteLine ("<END>");
490 } catch (IOException) {
492 } catch (Exception e){
493 Console.WriteLine (e);