Merge branch 'master' of github.com:mono/mono
[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, ctx.BuildinTypes.Void);
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                         ns.DeclarationFound = false;
552                         CSharpParser parser = new CSharpParser (seekable, Location.SourceFiles [0], module, ns);
553
554                         if (kind == InputKind.StatementOrExpression){
555                                 parser.Lexer.putback_char = Tokenizer.EvalStatementParserCharacter;
556                                 ctx.Settings.StatementMode = true;
557                         } else {
558                                 parser.Lexer.putback_char = Tokenizer.EvalCompilationUnitParserCharacter;
559                                 ctx.Settings.StatementMode = false;
560                         }
561
562                         if (mode == ParseMode.GetCompletions)
563                                 parser.Lexer.CompleteOnEOF = true;
564
565                         ReportPrinter old_printer = null;
566                         if ((mode == ParseMode.Silent || mode == ParseMode.GetCompletions) && CSharpParser.yacc_verbose_flag == 0)
567                                 old_printer = ctx.Report.SetPrinter (new StreamReportPrinter (TextWriter.Null));
568
569                         try {
570                                 parser.parse ();
571                         } finally {
572                                 if (ctx.Report.Errors != 0){
573                                         if (mode != ParseMode.ReportErrors  && parser.UnexpectedEOF)
574                                                 partial_input = true;
575
576                                         if (parser.undo != null)
577                                                 parser.undo.ExecuteUndo ();
578
579                                         parser = null;
580                                 }
581
582                                 if (old_printer != null)
583                                         ctx.Report.SetPrinter (old_printer);
584                         }
585                         return parser;
586                 }
587
588                 CompiledMethod CompileBlock (Class host, Undo undo, Report Report)
589                 {
590                         string current_debug_name = "eval-" + count + ".dll";
591                         ++count;
592 #if STATIC
593                         throw new NotSupportedException ();
594 #else
595                         AssemblyDefinitionDynamic assembly;
596                         AssemblyBuilderAccess access;
597
598                         if (Environment.GetEnvironmentVariable ("SAVE") != null) {
599                                 access = AssemblyBuilderAccess.RunAndSave;
600                                 assembly = new AssemblyDefinitionDynamic (module, current_debug_name, current_debug_name);
601                                 assembly.Importer = importer;
602                         } else {
603 #if NET_4_0
604                                 access = AssemblyBuilderAccess.RunAndCollect;
605 #else
606                                 access = AssemblyBuilderAccess.Run;
607 #endif
608                                 assembly = new AssemblyDefinitionDynamic (module, current_debug_name);
609                         }
610
611                         assembly.Create (AppDomain.CurrentDomain, access);
612
613                         Method expression_method;
614                         if (host != null) {
615                                 var base_class_imported = importer.ImportType (base_class);
616                                 var baseclass_list = new List<FullNamedExpression> (1) {
617                                         new TypeExpression (base_class_imported, host.Location)
618                                 };
619
620                                 host.AddBasesForPart (host, baseclass_list);
621
622                                 host.CreateType ();
623                                 host.DefineType ();
624                                 host.Define ();
625
626                                 expression_method = (Method) host.Methods[0];
627                         } else {
628                                 expression_method = null;
629                         }
630
631                         module.CreateType ();
632                         module.Define ();
633
634                         if (Report.Errors != 0){
635                                 if (undo != null)
636                                         undo.ExecuteUndo ();
637
638                                 return null;
639                         }
640
641                         if (host != null){
642                                 host.EmitType ();
643                         }
644                         
645                         module.Emit ();
646                         if (Report.Errors != 0){
647                                 if (undo != null)
648                                         undo.ExecuteUndo ();
649                                 return null;
650                         }
651
652                         module.CloseType ();
653                         if (host != null)
654                                 host.CloseType ();
655
656                         if (access == AssemblyBuilderAccess.RunAndSave)
657                                 assembly.Save ();
658
659                         if (host == null)
660                                 return null;
661                         
662                         //
663                         // Unlike Mono, .NET requires that the MethodInfo is fetched, it cant
664                         // work from MethodBuilders.   Retarded, I know.
665                         //
666                         var tt = assembly.Builder.GetType (host.TypeBuilder.Name);
667                         var mi = tt.GetMethod (expression_method.Name);
668
669                         if (host.Fields != null) {
670                                 //
671                                 // We need to then go from FieldBuilder to FieldInfo
672                                 // or reflection gets confused (it basically gets confused, and variables override each
673                                 // other).
674                                 //
675                                 foreach (Field field in host.Fields) {
676                                         var fi = tt.GetField (field.Name);
677
678                                         Tuple<FieldSpec, FieldInfo> old;
679
680                                         // If a previous value was set, nullify it, so that we do
681                                         // not leak memory
682                                         if (fields.TryGetValue (field.Name, out old)) {
683                                                 if (old.Item1.MemberType.IsStruct) {
684                                                         //
685                                                         // TODO: Clear fields for structs
686                                                         //
687                                                 } else {
688                                                         try {
689                                                                 old.Item2.SetValue (null, null);
690                                                         } catch {
691                                                         }
692                                                 }
693                                         }
694
695                                         fields[field.Name] = Tuple.Create (field.Spec, fi);
696                                 }
697                         }
698                         
699                         return (CompiledMethod) System.Delegate.CreateDelegate (typeof (CompiledMethod), mi);
700 #endif
701                 }
702
703                 /// <summary>
704                 ///   A sentinel value used to indicate that no value was
705                 ///   was set by the compiled function.   This is used to
706                 ///   differentiate between a function not returning a
707                 ///   value and null.
708                 /// </summary>
709                 internal static class QuitValue { }
710
711                 internal Tuple<FieldSpec, FieldInfo> LookupField (string name)
712                 {
713                         Tuple<FieldSpec, FieldInfo> fi;
714                         fields.TryGetValue (name, out fi);
715                         return fi;
716                 }
717
718                 static string Quote (string s)
719                 {
720                         if (s.IndexOf ('"') != -1)
721                                 s = s.Replace ("\"", "\\\"");
722                         
723                         return "\"" + s + "\"";
724                 }
725
726                 public string GetUsing ()
727                 {
728                         if (ns == null)
729                                 return null;
730
731                         StringBuilder sb = new StringBuilder ();
732                         // TODO:
733                         //foreach (object x in ns.using_alias_list)
734                         //    sb.AppendFormat ("using {0};\n", x);
735
736                         foreach (var ue in ns.Usings) {
737                                 sb.AppendFormat ("using {0};", ue.ToString ());
738                                 sb.Append (Environment.NewLine);
739                         }
740
741                         return sb.ToString ();
742                 }
743
744                 internal ICollection<string> GetUsingList ()
745                 {
746                         var res = new List<string> ();
747
748                         foreach (var ue in ns.Usings)
749                                 res.Add (ue.ToString ());
750                         return res;
751                 }
752                 
753                 internal string [] GetVarNames ()
754                 {
755                         lock (evaluator_lock){
756                                 return new List<string> (fields.Keys).ToArray ();
757                         }
758                 }
759                 
760                 public string GetVars ()
761                 {
762                         lock (evaluator_lock){
763                                 StringBuilder sb = new StringBuilder ();
764                                 
765                                 foreach (var de in fields){
766                                         var fi = LookupField (de.Key);
767                                         object value;
768                                         try {
769                                                 value = fi.Item2.GetValue (null);
770                                                 if (value is string)
771                                                         value = Quote ((string)value);
772                                         } catch {
773                                                 value = "<error reading value>";
774                                         }
775
776                                         sb.AppendFormat ("{0} {1} = {2}", fi.Item1.MemberType.GetSignatureForError (), de.Key, value);
777                                         sb.AppendLine ();
778                                 }
779                                 
780                                 return sb.ToString ();
781                         }
782                 }
783
784                 /// <summary>
785                 ///    Loads the given assembly and exposes the API to the user.
786                 /// </summary>
787                 public void LoadAssembly (string file)
788                 {
789                         var loader = new DynamicLoader (importer, ctx);
790                         var assembly = loader.LoadAssemblyFile (file);
791                         if (assembly == null)
792                                 return;
793
794                         lock (evaluator_lock){
795                                 importer.ImportAssembly (assembly, module.GlobalRootNamespace);
796                         }
797                 }
798
799                 /// <summary>
800                 ///    Exposes the API of the given assembly to the Evaluator
801                 /// </summary>
802                 public void ReferenceAssembly (Assembly a)
803                 {
804                         lock (evaluator_lock){
805                                 importer.ImportAssembly (a, module.GlobalRootNamespace);
806                         }
807                 }
808         }
809
810         
811         /// <summary>
812         ///   A delegate that can be used to invoke the
813         ///   compiled expression or statement.
814         /// </summary>
815         /// <remarks>
816         ///   Since the Compile methods will compile
817         ///   statements and expressions into the same
818         ///   delegate, you can tell if a value was returned
819         ///   by checking whether the returned value is of type
820         ///   NoValueSet.   
821         /// </remarks>
822         
823         public delegate void CompiledMethod (ref object retvalue);
824
825         /// <summary>
826         ///   The default base class for every interaction line
827         /// </summary>
828         /// <remarks>
829         ///   The expressions and statements behave as if they were
830         ///   a static method of this class.   The InteractiveBase class
831         ///   contains a number of useful methods, but can be overwritten
832         ///   by setting the InteractiveBaseType property in the Evaluator
833         /// </remarks>
834         public class InteractiveBase {
835                 /// <summary>
836                 ///   Determines where the standard output of methods in this class will go. 
837                 /// </summary>
838                 public static TextWriter Output = Console.Out;
839
840                 /// <summary>
841                 ///   Determines where the standard error of methods in this class will go. 
842                 /// </summary>
843                 public static TextWriter Error = Console.Error;
844
845                 /// <summary>
846                 ///   The primary prompt used for interactive use.
847                 /// </summary>
848                 public static string Prompt             = "csharp> ";
849
850                 /// <summary>
851                 ///   The secondary prompt used for interactive use (used when
852                 ///   an expression is incomplete).
853                 /// </summary>
854                 public static string ContinuationPrompt = "      > ";
855
856                 /// <summary>
857                 ///   Used to signal that the user has invoked the  `quit' statement.
858                 /// </summary>
859                 public static bool QuitRequested;
860
861                 public static Evaluator Evaluator;
862                 
863                 /// <summary>
864                 ///   Shows all the variables defined so far.
865                 /// </summary>
866                 static public void ShowVars ()
867                 {
868                         Output.Write (Evaluator.GetVars ());
869                         Output.Flush ();
870                 }
871
872                 /// <summary>
873                 ///   Displays the using statements in effect at this point. 
874                 /// </summary>
875                 static public void ShowUsing ()
876                 {
877                         Output.Write (Evaluator.GetUsing ());
878                         Output.Flush ();
879                 }
880         
881                 /// <summary>
882                 ///   Times the execution of the given delegate
883                 /// </summary>
884                 static public TimeSpan Time (Action a)
885                 {
886                         DateTime start = DateTime.Now;
887                         a ();
888                         return DateTime.Now - start;
889                 }
890                 
891                 /// <summary>
892                 ///   Loads the assemblies from a package
893                 /// </summary>
894                 /// <remarks>
895                 ///   Loads the assemblies from a package.   This is equivalent
896                 ///   to passing the -pkg: command line flag to the C# compiler
897                 ///   on the command line. 
898                 /// </remarks>
899                 static public void LoadPackage (string pkg)
900                 {
901                         if (pkg == null){
902                                 Error.WriteLine ("Invalid package specified");
903                                 return;
904                         }
905
906                         string pkgout = Driver.GetPackageFlags (pkg, null);
907
908                         string [] xargs = pkgout.Trim (new Char [] {' ', '\n', '\r', '\t'}).
909                                 Split (new Char [] { ' ', '\t'});
910
911                         foreach (string s in xargs){
912                                 if (s.StartsWith ("-r:") || s.StartsWith ("/r:") || s.StartsWith ("/reference:")){
913                                         string lib = s.Substring (s.IndexOf (':')+1);
914
915                                         Evaluator.LoadAssembly (lib);
916                                         continue;
917                                 }
918                         }
919                 }
920
921                 /// <summary>
922                 ///   Loads the assembly
923                 /// </summary>
924                 /// <remarks>
925                 ///   Loads the specified assembly and makes its types
926                 ///   available to the evaluator.  This is equivalent
927                 ///   to passing the -pkg: command line flag to the C#
928                 ///   compiler on the command line.
929                 /// </remarks>
930                 static public void LoadAssembly (string assembly)
931                 {
932                         Evaluator.LoadAssembly (assembly);
933                 }
934
935                 static public void print (string obj)
936                 {
937                         Output.WriteLine (obj);
938                 }
939
940                 static public void print (string fmt, params object [] args)
941                 {
942                         Output.WriteLine (fmt, args);
943                 }
944                 
945                 /// <summary>
946                 ///   Returns a list of available static methods. 
947                 /// </summary>
948                 static public string help {
949                         get {
950                                 return "Static methods:\n" +
951                                         "  Describe (object)       - Describes the object's type\n" +
952                                         "  LoadPackage (package);  - Loads the given Package (like -pkg:FILE)\n" +
953                                         "  LoadAssembly (assembly) - Loads the given assembly (like -r:ASSEMBLY)\n" +
954                                         "  ShowVars ();            - Shows defined local variables.\n" +
955                                         "  ShowUsing ();           - Show active using declarations.\n" +
956                                         "  Prompt                  - The prompt used by the C# shell\n" +
957                                         "  ContinuationPrompt      - The prompt for partial input\n" +
958                                         "  Time(() -> { })         - Times the specified code\n" +
959                                         "  print (obj)             - Shorthand for Console.WriteLine\n" +
960                                         "  quit;                   - You'll never believe it - this quits the repl!\n" +
961                                         "  help;                   - This help text\n";
962                         }
963                 }
964
965                 /// <summary>
966                 ///   Indicates to the read-eval-print-loop that the interaction should be finished. 
967                 /// </summary>
968                 static public object quit {
969                         get {
970                                 QuitRequested = true;
971
972                                 // To avoid print null at the exit
973                                 return typeof (Evaluator.QuitValue);
974                         }
975                 }
976
977 #if !NET_2_1
978                 /// <summary>
979                 ///   Describes an object or a type.
980                 /// </summary>
981                 /// <remarks>
982                 ///   This method will show a textual representation
983                 ///   of the object's type.  If the object is a
984                 ///   System.Type it renders the type directly,
985                 ///   otherwise it renders the type returned by
986                 ///   invoking GetType on the object.
987                 /// </remarks>
988                 static public string Describe (object x)
989                 {
990                         if (x == null)
991                                 return "<null>";
992
993                         var type = x as Type ?? x.GetType ();
994
995                         StringWriter sw = new StringWriter ();
996                         new Outline (type, sw, true, false, false).OutlineType ();
997                         return sw.ToString ();
998                 }
999 #endif
1000         }
1001
1002         class HoistedEvaluatorVariable : HoistedVariable
1003         {
1004                 public HoistedEvaluatorVariable (Field field)
1005                         : base (null, field)
1006                 {
1007                 }
1008
1009                 public override void EmitSymbolInfo ()
1010                 {
1011                 }
1012
1013                 protected override FieldExpr GetFieldExpression (EmitContext ec)
1014                 {
1015                         return new FieldExpr (field, field.Location);
1016                 }
1017         }
1018
1019         /// <summary>
1020         ///    A class used to assign values if the source expression is not void
1021         ///
1022         ///    Used by the interactive shell to allow it to call this code to set
1023         ///    the return value for an invocation.
1024         /// </summary>
1025         class OptionalAssign : SimpleAssign {
1026                 public OptionalAssign (Expression t, Expression s, Location loc)
1027                         : base (t, s, loc)
1028                 {
1029                 }
1030
1031                 protected override Expression DoResolve (ResolveContext ec)
1032                 {
1033                         CloneContext cc = new CloneContext ();
1034                         Expression clone = source.Clone (cc);
1035
1036                         clone = clone.Resolve (ec);
1037                         if (clone == null)
1038                                 return null;
1039
1040                         //
1041                         // A useful feature for the REPL: if we can resolve the expression
1042                         // as a type, Describe the type;
1043                         //
1044                         if (ec.Module.Evaluator.DescribeTypeExpressions){
1045                                 var old_printer = ec.Report.SetPrinter (new SessionReportPrinter ());
1046                                 Expression tclone;
1047                                 try {
1048                                         tclone = source.Clone (cc);
1049                                         tclone = tclone.Resolve (ec, ResolveFlags.Type);
1050                                         if (ec.Report.Errors > 0)
1051                                                 tclone = null;
1052                                 } finally {
1053                                         ec.Report.SetPrinter (old_printer);
1054                                 }
1055
1056                                 if (tclone != null) {
1057                                         Arguments args = new Arguments (1);
1058                                         args.Add (new Argument (new TypeOf ((TypeExpr) clone, Location)));
1059                                         return new Invocation (new SimpleName ("Describe", Location), args).Resolve (ec);
1060                                 }
1061                         }
1062
1063                         // This means its really a statement.
1064                         if (clone.Type.Kind == MemberKind.Void || clone is DynamicInvocation || clone is Assign) {
1065                                 return clone;
1066                         }
1067
1068                         source = clone;
1069                         return base.DoResolve (ec);
1070                 }
1071         }
1072
1073         public class Undo
1074         {
1075                 List<Action> undo_actions;
1076                 
1077                 public Undo ()
1078                 {
1079                 }
1080
1081                 public void AddTypeContainer (TypeContainer current_container, TypeContainer tc)
1082                 {
1083                         if (current_container == tc){
1084                                 Console.Error.WriteLine ("Internal error: inserting container into itself");
1085                                 return;
1086                         }
1087
1088                         if (undo_actions == null)
1089                                 undo_actions = new List<Action> ();
1090
1091                         var existing = current_container.Types.FirstOrDefault (l => l.MemberName.Basename == tc.MemberName.Basename);
1092                         if (existing != null) {
1093                                 current_container.RemoveTypeContainer (existing);
1094                                 existing.NamespaceEntry.SlaveDeclSpace.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 }