Cleanup repl from most of static stuff and fix many hidden issues
[mono.git] / mcs / mcs / eval.cs
1 //
2 // eval.cs: Evaluation and Hosting API for the C# compiler
3 //
4 // Authors:
5 //   Miguel de Icaza (miguel@gnome.org)
6 //   Marek Safar (marek.safar@gmail.com)
7 //
8 // Dual licensed under the terms of the MIT X11 or GNU GPL
9 //
10 // Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
11 // Copyright 2004-2011 Novell, Inc
12 //
13
14 using System;
15 using System.Threading;
16 using System.Collections.Generic;
17 using System.Reflection;
18 using System.Reflection.Emit;
19 using System.IO;
20 using System.Text;
21 using System.Linq;
22
23 namespace Mono.CSharp
24 {
25
26         /// <summary>
27         ///   Evaluator: provides an API to evaluate C# statements and
28         ///   expressions dynamically.
29         /// </summary>
30         /// <remarks>
31         ///   This class exposes static methods to evaluate expressions in the
32         ///   current program.
33         ///
34         ///   To initialize the evaluator with a number of compiler
35         ///   options call the Init(string[]args) method with a set of
36         ///   command line options that the compiler recognizes.
37         ///
38         ///   To interrupt execution of a statement, you can invoke the
39         ///   Evaluator.Interrupt method.
40         /// </remarks>
41         public class Evaluator {
42
43                 enum ParseMode {
44                         // Parse silently, do not output any error messages
45                         Silent,
46
47                         // Report errors during parse
48                         ReportErrors,
49
50                         // Auto-complete, means that the tokenizer will start producing
51                         // GETCOMPLETIONS tokens when it reaches a certain point.
52                         GetCompletions
53                 }
54
55                 static object evaluator_lock = new object ();
56                 static volatile bool invoking;
57                 
58                 static int count;
59                 static Thread invoke_thread;
60
61                 readonly Dictionary<string, Tuple<FieldSpec, FieldInfo>> fields;
62
63                 Type base_class;
64                 bool inited;
65
66                 readonly CompilerContext ctx;
67                 readonly ModuleContainer module;
68                 readonly ReflectionImporter importer;
69
70                 // TODO: somehow merge with module
71                 NamespaceEntry ns;
72                 
73                 public Evaluator (CompilerSettings settings, Report report)
74                 {
75                         ctx = new CompilerContext (settings, report);
76
77                         module = new ModuleContainer (ctx);
78                         module.Evaluator = this;
79
80                         // FIXME: Importer needs this assembly for internalsvisibleto
81                         module.SetDeclaringAssembly (new AssemblyDefinitionDynamic (module, "evaluator"));
82                         importer = new ReflectionImporter (module, ctx.BuildinTypes);
83
84                         InteractiveBaseClass = typeof (InteractiveBase);
85                         fields = new Dictionary<string, Tuple<FieldSpec, FieldInfo>> ();
86                 }
87
88                 void Init ()
89                 {
90                         var loader = new DynamicLoader (importer, ctx);
91
92                         CompilerCallableEntryPoint.Reset ();
93                         RootContext.ToplevelTypes = module;
94
95                         //var startup_files = new List<string> ();
96                         //foreach (CompilationUnit file in Location.SourceFiles)
97                         //    startup_files.Add (file.Path);
98
99                         loader.LoadReferences (module);
100                         ctx.BuildinTypes.CheckDefinitions (module);
101                         module.InitializePredefinedTypes ();
102
103                         inited = true;
104                 }
105
106                 static void Reset ()
107                 {
108                         CompilerCallableEntryPoint.PartialReset ();
109                         
110                         Location.AddFile (null, "{interactive}");
111                         Location.Initialize ();
112                 }
113
114                 /// <summary>
115                 ///   If true, turns type expressions into valid expressions
116                 ///   and calls the describe method on it
117                 /// </summary>
118                 public bool DescribeTypeExpressions;
119
120                 /// <summary>
121                 ///   The base class for the classes that host the user generated code
122                 /// </summary>
123                 /// <remarks>
124                 ///
125                 ///   This is the base class that will host the code
126                 ///   executed by the Evaluator.  By default
127                 ///   this is the Mono.CSharp.InteractiveBase class
128                 ///   which is useful for interactive use.
129                 ///
130                 ///   By changing this property you can control the
131                 ///   base class and the static members that are
132                 ///   available to your evaluated code.
133                 /// </remarks>
134                 public Type InteractiveBaseClass {
135                         get {
136                                 return base_class;
137                         }
138                         set {
139                                 base_class = value;
140
141                                 if (value != null && typeof (InteractiveBase).IsAssignableFrom (value))
142                                         InteractiveBase.Evaluator = this;
143                         }
144                 }
145
146                 /// <summary>
147                 ///   Interrupts the evaluation of an expression executing in Evaluate.
148                 /// </summary>
149                 /// <remarks>
150                 ///   Use this method to interrupt long-running invocations.
151                 /// </remarks>
152                 public void Interrupt ()
153                 {
154                         if (!inited || !invoking)
155                                 return;
156                         
157                         if (invoke_thread != null)
158                                 invoke_thread.Abort ();
159                 }
160
161                 /// <summary>
162                 ///   Compiles the input string and returns a delegate that represents the compiled code.
163                 /// </summary>
164                 /// <remarks>
165                 ///
166                 ///   Compiles the input string as a C# expression or
167                 ///   statement, unlike the Evaluate method, the
168                 ///   resulting delegate can be invoked multiple times
169                 ///   without incurring in the compilation overhead.
170                 ///
171                 ///   If the return value of this function is null,
172                 ///   this indicates that the parsing was complete.
173                 ///   If the return value is a string it indicates
174                 ///   that the input string was partial and that the
175                 ///   invoking code should provide more code before
176                 ///   the code can be successfully compiled.
177                 ///
178                 ///   If you know that you will always get full expressions or
179                 ///   statements and do not care about partial input, you can use
180                 ///   the other Compile overload. 
181                 ///
182                 ///   On success, in addition to returning null, the
183                 ///   compiled parameter will be set to the delegate
184                 ///   that can be invoked to execute the code.
185                 ///
186             /// </remarks>
187                 public string Compile (string input, out CompiledMethod compiled)
188                 {
189                         if (input == null || input.Length == 0){
190                                 compiled = null;
191                                 return null;
192                         }
193
194                         lock (evaluator_lock){
195                                 if (!inited)
196                                         Init ();
197                                 else
198                                         ctx.Report.Printer.Reset ();
199
200                                 bool partial_input;
201                                 CSharpParser parser = ParseString (ParseMode.Silent, input, out partial_input);
202                                 if (parser == null){
203                                         compiled = null;
204                                         if (partial_input)
205                                                 return input;
206                                         
207                                         ParseString (ParseMode.ReportErrors, input, out partial_input);
208                                         return null;
209                                 }
210                                 
211                                 Class parser_result = parser.InteractiveResult;
212                                 compiled = CompileBlock (parser_result, parser.undo, ctx.Report);
213                                 return null;
214                         }
215                 }
216
217                 /// <summary>
218                 ///   Compiles the input string and returns a delegate that represents the compiled code.
219                 /// </summary>
220                 /// <remarks>
221                 ///
222                 ///   Compiles the input string as a C# expression or
223                 ///   statement, unlike the Evaluate method, the
224                 ///   resulting delegate can be invoked multiple times
225                 ///   without incurring in the compilation overhead.
226                 ///
227                 ///   This method can only deal with fully formed input
228                 ///   strings and does not provide a completion mechanism.
229                 ///   If you must deal with partial input (for example for
230                 ///   interactive use) use the other overload. 
231                 ///
232                 ///   On success, a delegate is returned that can be used
233                 ///   to invoke the method.
234                 ///
235                 /// </remarks>
236                 public CompiledMethod Compile (string input)
237                 {
238                         CompiledMethod compiled;
239
240                         // Ignore partial inputs
241                         if (Compile (input, out compiled) != null){
242                                 // Error, the input was partial.
243                                 return null;
244                         }
245
246                         // Either null (on error) or the compiled method.
247                         return compiled;
248                 }
249
250                 /// <summary>
251                 ///   Evaluates and expression or statement and returns any result values.
252                 /// </summary>
253                 /// <remarks>
254                 ///   Evaluates the input string as a C# expression or
255                 ///   statement.  If the input string is an expression
256                 ///   the result will be stored in the result variable
257                 ///   and the result_set variable will be set to true.
258                 ///
259                 ///   It is necessary to use the result/result_set
260                 ///   pair to identify when a result was set (for
261                 ///   example, execution of user-provided input can be
262                 ///   an expression, a statement or others, and
263                 ///   result_set would only be set if the input was an
264                 ///   expression.
265                 ///
266                 ///   If the return value of this function is null,
267                 ///   this indicates that the parsing was complete.
268                 ///   If the return value is a string, it indicates
269                 ///   that the input is partial and that the user
270                 ///   should provide an updated string.
271                 /// </remarks>
272                 public string Evaluate (string input, out object result, out bool result_set)
273                 {
274                         CompiledMethod compiled;
275
276                         result_set = false;
277                         result = null;
278
279                         input = Compile (input, out compiled);
280                         if (input != null)
281                                 return input;
282                         
283                         if (compiled == null)
284                                 return null;
285                                 
286                         //
287                         // The code execution does not need to keep the compiler lock
288                         //
289                         object retval = typeof (QuitValue);
290
291                         try {
292                                 invoke_thread = System.Threading.Thread.CurrentThread;
293                                 invoking = true;
294                                 compiled (ref retval);
295                         } catch (ThreadAbortException e){
296                                 Thread.ResetAbort ();
297                                 Console.WriteLine ("Interrupted!\n{0}", e);
298                         } finally {
299                                 invoking = false;
300                         }
301
302                         //
303                         // We use a reference to a compiler type, in this case
304                         // Driver as a flag to indicate that this was a statement
305                         //
306                         if (!ReferenceEquals (retval, typeof (QuitValue))) {
307                                 result_set = true;
308                                 result = retval; 
309                         }
310
311                         return null;
312                 }
313
314                 public string [] GetCompletions (string input, out string prefix)
315                 {
316                         prefix = "";
317                         if (input == null || input.Length == 0)
318                                 return null;
319                         
320                         lock (evaluator_lock){
321                                 if (!inited)
322                                         Init ();
323                                 
324                                 bool partial_input;
325                                 CSharpParser parser = ParseString (ParseMode.GetCompletions, input, out partial_input);
326                                 if (parser == null){
327                                         if (CSharpParser.yacc_verbose_flag != 0)
328                                                 Console.WriteLine ("DEBUG: No completions available");
329                                         return null;
330                                 }
331                                 
332                                 Class parser_result = parser.InteractiveResult;
333
334 #if NET_4_0
335                                 var access = AssemblyBuilderAccess.RunAndCollect;
336 #else
337                                 var access = AssemblyBuilderAccess.Run;
338 #endif
339                                 var a = new AssemblyDefinitionDynamic (module, "completions");
340                                 a.Create (AppDomain.CurrentDomain, access);
341                                 module.SetDeclaringAssembly (a);
342
343                                 // Need to setup MemberCache
344                                 parser_result.CreateType ();
345
346                                 var method = parser_result.Methods[0] as Method;
347                                 BlockContext bc = new BlockContext (method, method.Block, TypeManager.void_type);
348
349                                 try {
350                                         method.Block.Resolve (null, bc, method);
351                                 } catch (CompletionResult cr) {
352                                         prefix = cr.BaseText;
353                                         return cr.Result;
354                                 } 
355                         }
356                         return null;
357                 }
358
359                 /// <summary>
360                 ///   Executes the given expression or statement.
361                 /// </summary>
362                 /// <remarks>
363                 ///    Executes the provided statement, returns true
364                 ///    on success, false on parsing errors.  Exceptions
365                 ///    might be thrown by the called code.
366                 /// </remarks>
367                 public bool Run (string statement)
368                 {
369                         object result;
370                         bool result_set;
371
372                         return Evaluate (statement, out result, out result_set) == null;
373                 }
374
375                 /// <summary>
376                 ///   Evaluates and expression or statement and returns the result.
377                 /// </summary>
378                 /// <remarks>
379                 ///   Evaluates the input string as a C# expression or
380                 ///   statement and returns the value.   
381                 ///
382                 ///   This method will throw an exception if there is a syntax error,
383                 ///   of if the provided input is not an expression but a statement.
384                 /// </remarks>
385                 public object Evaluate (string input)
386                 {
387                         object result;
388                         bool result_set;
389                         
390                         string r = Evaluate (input, out result, out result_set);
391
392                         if (r != null)
393                                 throw new ArgumentException ("Syntax error on input: partial input");
394                         
395                         if (result_set == false)
396                                 throw new ArgumentException ("The expression did not set a result");
397
398                         return result;
399                 }
400
401                 enum InputKind {
402                         EOF,
403                         StatementOrExpression,
404                         CompilationUnit,
405                         Error
406                 }
407
408                 //
409                 // Deambiguates the input string to determine if we
410                 // want to process a statement or if we want to
411                 // process a compilation unit.
412                 //
413                 // This is done using a top-down predictive parser,
414                 // since the yacc/jay parser can not deambiguage this
415                 // without more than one lookahead token.   There are very
416                 // few ambiguities.
417                 //
418                 InputKind ToplevelOrStatement (SeekableStreamReader seekable)
419                 {
420                         Tokenizer tokenizer = new Tokenizer (seekable, (CompilationUnit) Location.SourceFiles [0], ctx);
421                         
422                         int t = tokenizer.token ();
423                         switch (t){
424                         case Token.EOF:
425                                 return InputKind.EOF;
426                                 
427                         // These are toplevels
428                         case Token.EXTERN:
429                         case Token.OPEN_BRACKET:
430                         case Token.ABSTRACT:
431                         case Token.CLASS:
432                         case Token.ENUM:
433                         case Token.INTERFACE:
434                         case Token.INTERNAL:
435                         case Token.NAMESPACE:
436                         case Token.PRIVATE:
437                         case Token.PROTECTED:
438                         case Token.PUBLIC:
439                         case Token.SEALED:
440                         case Token.STATIC:
441                         case Token.STRUCT:
442                                 return InputKind.CompilationUnit;
443                                 
444                         // Definitely expression
445                         case Token.FIXED:
446                         case Token.BOOL:
447                         case Token.BYTE:
448                         case Token.CHAR:
449                         case Token.DECIMAL:
450                         case Token.DOUBLE:
451                         case Token.FLOAT:
452                         case Token.INT:
453                         case Token.LONG:
454                         case Token.NEW:
455                         case Token.OBJECT:
456                         case Token.SBYTE:
457                         case Token.SHORT:
458                         case Token.STRING:
459                         case Token.UINT:
460                         case Token.ULONG:
461                                 return InputKind.StatementOrExpression;
462
463                         // These need deambiguation help
464                         case Token.USING:
465                                 t = tokenizer.token ();
466                                 if (t == Token.EOF)
467                                         return InputKind.EOF;
468
469                                 if (t == Token.IDENTIFIER)
470                                         return InputKind.CompilationUnit;
471                                 return InputKind.StatementOrExpression;
472
473
474                         // Distinguish between:
475                         //    delegate opt_anonymous_method_signature block
476                         //    delegate type 
477                         case Token.DELEGATE:
478                                 t = tokenizer.token ();
479                                 if (t == Token.EOF)
480                                         return InputKind.EOF;
481                                 if (t == Token.OPEN_PARENS || t == Token.OPEN_BRACE)
482                                         return InputKind.StatementOrExpression;
483                                 return InputKind.CompilationUnit;
484
485                         // Distinguih between:
486                         //    unsafe block
487                         //    unsafe as modifier of a type declaration
488                         case Token.UNSAFE:
489                                 t = tokenizer.token ();
490                                 if (t == Token.EOF)
491                                         return InputKind.EOF;
492                                 if (t == Token.OPEN_PARENS)
493                                         return InputKind.StatementOrExpression;
494                                 return InputKind.CompilationUnit;
495                                 
496                         // These are errors: we list explicitly what we had
497                         // from the grammar, ERROR and then everything else
498
499                         case Token.READONLY:
500                         case Token.OVERRIDE:
501                         case Token.ERROR:
502                                 return InputKind.Error;
503
504                         // This catches everything else allowed by
505                         // expressions.  We could add one-by-one use cases
506                         // if needed.
507                         default:
508                                 return InputKind.StatementOrExpression;
509                         }
510                 }
511                 
512                 //
513                 // Parses the string @input and returns a CSharpParser if succeeful.
514                 //
515                 // if @silent is set to true then no errors are
516                 // reported to the user.  This is used to do various calls to the
517                 // parser and check if the expression is parsable.
518                 //
519                 // @partial_input: if @silent is true, then it returns whether the
520                 // parsed expression was partial, and more data is needed
521                 //
522                 CSharpParser ParseString (ParseMode mode, string input, out bool partial_input)
523                 {
524                         partial_input = false;
525                         Reset ();
526                         Tokenizer.LocatedToken.Initialize ();
527
528                         Stream s = new MemoryStream (Encoding.Default.GetBytes (input));
529                         SeekableStreamReader seekable = new SeekableStreamReader (s, Encoding.Default);
530
531                         InputKind kind = ToplevelOrStatement (seekable);
532                         if (kind == InputKind.Error){
533                                 if (mode == ParseMode.ReportErrors)
534                                         ctx.Report.Error (-25, "Detection Parsing Error");
535                                 partial_input = false;
536                                 return null;
537                         }
538
539                         if (kind == InputKind.EOF){
540                                 if (mode == ParseMode.ReportErrors)
541                                         Console.Error.WriteLine ("Internal error: EOF condition should have been detected in a previous call with silent=true");
542                                 partial_input = true;
543                                 return null;
544                                 
545                         }
546                         seekable.Position = 0;
547
548                         if (ns == null)
549                                 ns = new NamespaceEntry (module, null, Location.SourceFiles[0], null);
550
551                         CSharpParser parser = new CSharpParser (seekable, Location.SourceFiles [0], module, ns);
552
553                         if (kind == InputKind.StatementOrExpression){
554                                 parser.Lexer.putback_char = Tokenizer.EvalStatementParserCharacter;
555                                 ctx.Settings.StatementMode = true;
556                         } else {
557                                 parser.Lexer.putback_char = Tokenizer.EvalCompilationUnitParserCharacter;
558                                 ctx.Settings.StatementMode = false;
559                         }
560
561                         if (mode == ParseMode.GetCompletions)
562                                 parser.Lexer.CompleteOnEOF = true;
563
564                         ReportPrinter old_printer = null;
565                         if ((mode == ParseMode.Silent || mode == ParseMode.GetCompletions) && CSharpParser.yacc_verbose_flag == 0)
566                                 old_printer = ctx.Report.SetPrinter (new StreamReportPrinter (TextWriter.Null));
567
568                         try {
569                                 parser.parse ();
570                         } finally {
571                                 if (ctx.Report.Errors != 0){
572                                         if (mode != ParseMode.ReportErrors  && parser.UnexpectedEOF)
573                                                 partial_input = true;
574
575                                         if (parser.undo != null)
576                                                 parser.undo.ExecuteUndo ();
577
578                                         parser = null;
579                                 }
580
581                                 if (old_printer != null)
582                                         ctx.Report.SetPrinter (old_printer);
583                         }
584                         return parser;
585                 }
586
587                 CompiledMethod CompileBlock (Class host, Undo undo, Report Report)
588                 {
589                         string current_debug_name = "eval-" + count + ".dll";
590                         ++count;
591 #if STATIC
592                         throw new NotSupportedException ();
593 #else
594                         AssemblyDefinitionDynamic assembly;
595                         AssemblyBuilderAccess access;
596
597                         if (Environment.GetEnvironmentVariable ("SAVE") != null) {
598                                 access = AssemblyBuilderAccess.RunAndSave;
599                                 assembly = new AssemblyDefinitionDynamic (module, current_debug_name, current_debug_name);
600                                 assembly.Importer = importer;
601                         } else {
602 #if NET_4_0
603                                 access = AssemblyBuilderAccess.RunAndCollect;
604 #else
605                                 access = AssemblyBuilderAccess.Run;
606 #endif
607                                 assembly = new AssemblyDefinitionDynamic (module, current_debug_name);
608                         }
609
610                         assembly.Create (AppDomain.CurrentDomain, access);
611
612                         Method expression_method;
613                         if (host != null) {
614                                 var base_class_imported = importer.ImportType (base_class);
615                                 var baseclass_list = new List<FullNamedExpression> (1) {
616                                         new TypeExpression (base_class_imported, host.Location)
617                                 };
618
619                                 host.AddBasesForPart (host, baseclass_list);
620
621                                 host.CreateType ();
622                                 host.DefineType ();
623                                 host.Define ();
624
625                                 expression_method = (Method) host.Methods[0];
626                         } else {
627                                 expression_method = null;
628                         }
629
630                         module.CreateType ();
631                         module.Define ();
632
633                         if (Report.Errors != 0){
634                                 if (undo != null)
635                                         undo.ExecuteUndo ();
636
637                                 return null;
638                         }
639
640                         if (host != null){
641                                 host.EmitType ();
642                         }
643                         
644                         module.Emit ();
645                         if (Report.Errors != 0){
646                                 if (undo != null)
647                                         undo.ExecuteUndo ();
648                                 return null;
649                         }
650
651                         module.CloseType ();
652                         if (host != null)
653                                 host.CloseType ();
654
655                         if (access == AssemblyBuilderAccess.RunAndSave)
656                                 assembly.Save ();
657
658                         if (host == null)
659                                 return null;
660                         
661                         //
662                         // Unlike Mono, .NET requires that the MethodInfo is fetched, it cant
663                         // work from MethodBuilders.   Retarded, I know.
664                         //
665                         var tt = assembly.Builder.GetType (host.TypeBuilder.Name);
666                         var mi = tt.GetMethod (expression_method.Name);
667
668                         if (host.Fields != null) {
669                                 //
670                                 // We need to then go from FieldBuilder to FieldInfo
671                                 // or reflection gets confused (it basically gets confused, and variables override each
672                                 // other).
673                                 //
674                                 foreach (Field field in host.Fields) {
675                                         var fi = tt.GetField (field.Name);
676
677                                         Tuple<FieldSpec, FieldInfo> old;
678
679                                         // If a previous value was set, nullify it, so that we do
680                                         // not leak memory
681                                         if (fields.TryGetValue (field.Name, out old)) {
682                                                 if (old.Item1.MemberType.IsStruct) {
683                                                         //
684                                                         // TODO: Clear fields for structs
685                                                         //
686                                                 } else {
687                                                         try {
688                                                                 old.Item2.SetValue (null, null);
689                                                         } catch {
690                                                         }
691                                                 }
692                                         }
693
694                                         fields[field.Name] = Tuple.Create (field.Spec, fi);
695                                 }
696                         }
697                         
698                         return (CompiledMethod) System.Delegate.CreateDelegate (typeof (CompiledMethod), mi);
699 #endif
700                 }
701
702                 /// <summary>
703                 ///   A sentinel value used to indicate that no value was
704                 ///   was set by the compiled function.   This is used to
705                 ///   differentiate between a function not returning a
706                 ///   value and null.
707                 /// </summary>
708                 internal static class QuitValue { }
709
710                 internal Tuple<FieldSpec, FieldInfo> LookupField (string name)
711                 {
712                         Tuple<FieldSpec, FieldInfo> fi;
713                         fields.TryGetValue (name, out fi);
714                         return fi;
715                 }
716
717                 static string Quote (string s)
718                 {
719                         if (s.IndexOf ('"') != -1)
720                                 s = s.Replace ("\"", "\\\"");
721                         
722                         return "\"" + s + "\"";
723                 }
724
725                 public string GetUsing ()
726                 {
727                         if (ns == null)
728                                 return null;
729
730                         StringBuilder sb = new StringBuilder ();
731                         // TODO:
732                         //foreach (object x in ns.using_alias_list)
733                         //    sb.AppendFormat ("using {0};\n", x);
734
735                         foreach (var ue in ns.Usings) {
736                                 sb.AppendFormat ("using {0};", ue.ToString ());
737                                 sb.Append (Environment.NewLine);
738                         }
739
740                         return sb.ToString ();
741                 }
742
743                 internal ICollection<string> GetUsingList ()
744                 {
745                         var res = new List<string> ();
746
747                         foreach (var ue in ns.Usings)
748                                 res.Add (ue.ToString ());
749                         return res;
750                 }
751                 
752                 internal string [] GetVarNames ()
753                 {
754                         lock (evaluator_lock){
755                                 return new List<string> (fields.Keys).ToArray ();
756                         }
757                 }
758                 
759                 public string GetVars ()
760                 {
761                         lock (evaluator_lock){
762                                 StringBuilder sb = new StringBuilder ();
763                                 
764                                 foreach (var de in fields){
765                                         var fi = LookupField (de.Key);
766                                         object value;
767                                         try {
768                                                 value = fi.Item2.GetValue (null);
769                                                 if (value is string)
770                                                         value = Quote ((string)value);
771                                         } catch {
772                                                 value = "<error reading value>";
773                                         }
774
775                                         sb.AppendFormat ("{0} {1} = {2}", fi.Item1.MemberType.GetSignatureForError (), de.Key, value);
776                                         sb.AppendLine ();
777                                 }
778                                 
779                                 return sb.ToString ();
780                         }
781                 }
782
783                 /// <summary>
784                 ///    Loads the given assembly and exposes the API to the user.
785                 /// </summary>
786                 public void LoadAssembly (string file)
787                 {
788                         var loader = new DynamicLoader (importer, ctx);
789                         var assembly = loader.LoadAssemblyFile (file);
790                         if (assembly == null)
791                                 return;
792
793                         lock (evaluator_lock){
794                                 importer.ImportAssembly (assembly, module.GlobalRootNamespace);
795                         }
796                 }
797
798                 /// <summary>
799                 ///    Exposes the API of the given assembly to the Evaluator
800                 /// </summary>
801                 public void ReferenceAssembly (Assembly a)
802                 {
803                         lock (evaluator_lock){
804                                 importer.ImportAssembly (a, module.GlobalRootNamespace);
805                         }
806                 }
807         }
808
809         
810         /// <summary>
811         ///   A delegate that can be used to invoke the
812         ///   compiled expression or statement.
813         /// </summary>
814         /// <remarks>
815         ///   Since the Compile methods will compile
816         ///   statements and expressions into the same
817         ///   delegate, you can tell if a value was returned
818         ///   by checking whether the returned value is of type
819         ///   NoValueSet.   
820         /// </remarks>
821         
822         public delegate void CompiledMethod (ref object retvalue);
823
824         /// <summary>
825         ///   The default base class for every interaction line
826         /// </summary>
827         /// <remarks>
828         ///   The expressions and statements behave as if they were
829         ///   a static method of this class.   The InteractiveBase class
830         ///   contains a number of useful methods, but can be overwritten
831         ///   by setting the InteractiveBaseType property in the Evaluator
832         /// </remarks>
833         public class InteractiveBase {
834                 /// <summary>
835                 ///   Determines where the standard output of methods in this class will go. 
836                 /// </summary>
837                 public static TextWriter Output = Console.Out;
838
839                 /// <summary>
840                 ///   Determines where the standard error of methods in this class will go. 
841                 /// </summary>
842                 public static TextWriter Error = Console.Error;
843
844                 /// <summary>
845                 ///   The primary prompt used for interactive use.
846                 /// </summary>
847                 public static string Prompt             = "csharp> ";
848
849                 /// <summary>
850                 ///   The secondary prompt used for interactive use (used when
851                 ///   an expression is incomplete).
852                 /// </summary>
853                 public static string ContinuationPrompt = "      > ";
854
855                 /// <summary>
856                 ///   Used to signal that the user has invoked the  `quit' statement.
857                 /// </summary>
858                 public static bool QuitRequested;
859
860                 public static Evaluator Evaluator;
861                 
862                 /// <summary>
863                 ///   Shows all the variables defined so far.
864                 /// </summary>
865                 static public void ShowVars ()
866                 {
867                         Output.Write (Evaluator.GetVars ());
868                         Output.Flush ();
869                 }
870
871                 /// <summary>
872                 ///   Displays the using statements in effect at this point. 
873                 /// </summary>
874                 static public void ShowUsing ()
875                 {
876                         Output.Write (Evaluator.GetUsing ());
877                         Output.Flush ();
878                 }
879         
880                 /// <summary>
881                 ///   Times the execution of the given delegate
882                 /// </summary>
883                 static public TimeSpan Time (Action a)
884                 {
885                         DateTime start = DateTime.Now;
886                         a ();
887                         return DateTime.Now - start;
888                 }
889                 
890                 /// <summary>
891                 ///   Loads the assemblies from a package
892                 /// </summary>
893                 /// <remarks>
894                 ///   Loads the assemblies from a package.   This is equivalent
895                 ///   to passing the -pkg: command line flag to the C# compiler
896                 ///   on the command line. 
897                 /// </remarks>
898                 static public void LoadPackage (string pkg)
899                 {
900                         if (pkg == null){
901                                 Error.WriteLine ("Invalid package specified");
902                                 return;
903                         }
904
905                         string pkgout = Driver.GetPackageFlags (pkg, null);
906
907                         string [] xargs = pkgout.Trim (new Char [] {' ', '\n', '\r', '\t'}).
908                                 Split (new Char [] { ' ', '\t'});
909
910                         foreach (string s in xargs){
911                                 if (s.StartsWith ("-r:") || s.StartsWith ("/r:") || s.StartsWith ("/reference:")){
912                                         string lib = s.Substring (s.IndexOf (':')+1);
913
914                                         Evaluator.LoadAssembly (lib);
915                                         continue;
916                                 }
917                         }
918                 }
919
920                 /// <summary>
921                 ///   Loads the assembly
922                 /// </summary>
923                 /// <remarks>
924                 ///   Loads the specified assembly and makes its types
925                 ///   available to the evaluator.  This is equivalent
926                 ///   to passing the -pkg: command line flag to the C#
927                 ///   compiler on the command line.
928                 /// </remarks>
929                 static public void LoadAssembly (string assembly)
930                 {
931                         Evaluator.LoadAssembly (assembly);
932                 }
933
934                 static public void print (string obj)
935                 {
936                         Output.WriteLine (obj);
937                 }
938
939                 static public void print (string fmt, params object [] args)
940                 {
941                         Output.WriteLine (fmt, args);
942                 }
943                 
944                 /// <summary>
945                 ///   Returns a list of available static methods. 
946                 /// </summary>
947                 static public string help {
948                         get {
949                                 return "Static methods:\n" +
950                                         "  Describe (object)       - Describes the object's type\n" +
951                                         "  LoadPackage (package);  - Loads the given Package (like -pkg:FILE)\n" +
952                                         "  LoadAssembly (assembly) - Loads the given assembly (like -r:ASSEMBLY)\n" +
953                                         "  ShowVars ();            - Shows defined local variables.\n" +
954                                         "  ShowUsing ();           - Show active using declarations.\n" +
955                                         "  Prompt                  - The prompt used by the C# shell\n" +
956                                         "  ContinuationPrompt      - The prompt for partial input\n" +
957                                         "  Time(() -> { })         - Times the specified code\n" +
958                                         "  print (obj)             - Shorthand for Console.WriteLine\n" +
959                                         "  quit;                   - You'll never believe it - this quits the repl!\n" +
960                                         "  help;                   - This help text\n";
961                         }
962                 }
963
964                 /// <summary>
965                 ///   Indicates to the read-eval-print-loop that the interaction should be finished. 
966                 /// </summary>
967                 static public object quit {
968                         get {
969                                 QuitRequested = true;
970
971                                 // To avoid print null at the exit
972                                 return typeof (Evaluator.QuitValue);
973                         }
974                 }
975
976 #if !NET_2_1
977                 /// <summary>
978                 ///   Describes an object or a type.
979                 /// </summary>
980                 /// <remarks>
981                 ///   This method will show a textual representation
982                 ///   of the object's type.  If the object is a
983                 ///   System.Type it renders the type directly,
984                 ///   otherwise it renders the type returned by
985                 ///   invoking GetType on the object.
986                 /// </remarks>
987                 static public string Describe (object x)
988                 {
989                         if (x == null)
990                                 return "<null>";
991
992                         var type = x as Type ?? x.GetType ();
993
994                         StringWriter sw = new StringWriter ();
995                         new Outline (type, sw, true, false, false).OutlineType ();
996                         return sw.ToString ();
997                 }
998 #endif
999         }
1000
1001         class HoistedEvaluatorVariable : HoistedVariable
1002         {
1003                 public HoistedEvaluatorVariable (Field field)
1004                         : base (null, field)
1005                 {
1006                 }
1007
1008                 public override void EmitSymbolInfo ()
1009                 {
1010                 }
1011
1012                 protected override FieldExpr GetFieldExpression (EmitContext ec)
1013                 {
1014                         return new FieldExpr (field, field.Location);
1015                 }
1016         }
1017
1018         /// <summary>
1019         ///    A class used to assign values if the source expression is not void
1020         ///
1021         ///    Used by the interactive shell to allow it to call this code to set
1022         ///    the return value for an invocation.
1023         /// </summary>
1024         class OptionalAssign : SimpleAssign {
1025                 public OptionalAssign (Expression t, Expression s, Location loc)
1026                         : base (t, s, loc)
1027                 {
1028                 }
1029
1030                 protected override Expression DoResolve (ResolveContext ec)
1031                 {
1032                         CloneContext cc = new CloneContext ();
1033                         Expression clone = source.Clone (cc);
1034
1035                         //
1036                         // A useful feature for the REPL: if we can resolve the expression
1037                         // as a type, Describe the type;
1038                         //
1039                         if (ec.Module.Evaluator.DescribeTypeExpressions){
1040                                 var old_printer = ec.Report.SetPrinter (new SessionReportPrinter ());
1041                                 try {
1042                                         clone = clone.Resolve (ec);
1043                                         if (clone == null) {
1044                                                 clone = source.Clone (cc);
1045                                                 clone = clone.Resolve (ec, ResolveFlags.Type);
1046                                                 if (clone == null) {
1047                                                         clone = source.Clone (cc);
1048                                                         clone = clone.Resolve (ec);
1049                                                         return null;
1050                                                 }
1051
1052                                                 Arguments args = new Arguments (1);
1053                                                 args.Add (new Argument (new TypeOf ((TypeExpr) clone, Location)));
1054                                                 source = new Invocation (new SimpleName ("Describe", Location), args).Resolve (ec);
1055                                         }
1056                                 } finally {
1057                                         ec.Report.SetPrinter (old_printer);
1058                                 }
1059                         } else {
1060                                 clone = clone.Resolve (ec);
1061                                 if (clone == null)
1062                                         return null;
1063                         }
1064         
1065                         // This means its really a statement.
1066                         if (clone.Type == TypeManager.void_type || clone is DynamicInvocation || clone is Assign) {
1067                                 return clone;
1068                         }
1069
1070                         return base.DoResolve (ec);
1071                 }
1072         }
1073
1074         public class Undo
1075         {
1076                 List<Action> undo_actions;
1077                 
1078                 public Undo ()
1079                 {
1080                 }
1081
1082                 public void AddTypeContainer (TypeContainer current_container, TypeContainer tc)
1083                 {
1084                         if (current_container == tc){
1085                                 Console.Error.WriteLine ("Internal error: inserting container into itself");
1086                                 return;
1087                         }
1088
1089                         if (undo_actions == null)
1090                                 undo_actions = new List<Action> ();
1091
1092                         var existing = current_container.Types.FirstOrDefault (l => l.MemberName.Basename == tc.MemberName.Basename);
1093                         if (existing != null) {
1094                                 current_container.RemoveTypeContainer (existing);
1095                                 undo_actions.Add (() => current_container.AddTypeContainer (existing));
1096                         }
1097
1098                         undo_actions.Add (() => current_container.RemoveTypeContainer (tc));
1099                 }
1100
1101                 public void ExecuteUndo ()
1102                 {
1103                         if (undo_actions == null)
1104                                 return;
1105
1106                         foreach (var p in undo_actions){
1107                                 p ();
1108                         }
1109
1110                         undo_actions = null;
1111                 }
1112         }
1113         
1114 }