eol
[mcs.git] / mcs / eval.cs
bloba1ba9e235371cb7d79b0420af2067d871c645d43
1 //
2 // eval.cs: Evaluation and Hosting API for the C# compiler
3 //
4 // Authors:
5 // Miguel de Icaza (miguel@gnome.org)
6 //
7 // Dual licensed under the terms of the MIT X11 or GNU GPL
8 //
9 // Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
10 // Copyright 2004, 2005, 2006, 2007, 2008 Novell, Inc
12 using System;
13 using System.Threading;
14 using System.Collections.Generic;
15 using System.Reflection;
16 using System.Reflection.Emit;
17 using System.IO;
18 using System.Globalization;
19 using System.Text;
21 namespace Mono.CSharp {
23 /// <summary>
24 /// Evaluator: provides an API to evaluate C# statements and
25 /// expressions dynamically.
26 /// </summary>
27 /// <remarks>
28 /// This class exposes static methods to evaluate expressions in the
29 /// current program.
30 ///
31 /// To initialize the evaluator with a number of compiler
32 /// options call the Init(string[]args) method with a set of
33 /// command line options that the compiler recognizes.
34 ///
35 /// To interrupt execution of a statement, you can invoke the
36 /// Evaluator.Interrupt method.
37 /// </remarks>
38 public class Evaluator {
40 enum ParseMode {
41 // Parse silently, do not output any error messages
42 Silent,
44 // Report errors during parse
45 ReportErrors,
47 // Auto-complete, means that the tokenizer will start producing
48 // GETCOMPLETIONS tokens when it reaches a certain point.
49 GetCompletions
52 static object evaluator_lock = new object ();
54 static string current_debug_name;
55 static int count;
56 static Thread invoke_thread;
58 static List<NamespaceEntry.UsingAliasEntry> using_alias_list = new List<NamespaceEntry.UsingAliasEntry> ();
59 internal static List<NamespaceEntry.UsingEntry> using_list = new List<NamespaceEntry.UsingEntry> ();
60 static Dictionary<string, FieldInfo> fields = new Dictionary<string, FieldInfo> ();
62 static Type interactive_base_class = typeof (InteractiveBase);
63 static Driver driver;
64 static bool inited;
66 static CompilerContext ctx;
68 public static TextWriter MessageOutput = Console.Out;
70 /// <summary>
71 /// Optional initialization for the Evaluator.
72 /// </summary>
73 /// <remarks>
74 /// Initializes the Evaluator with the command line options
75 /// that would be processed by the command line compiler. Only
76 /// the first call to Init will work, any future invocations are
77 /// ignored.
78 ///
79 /// You can safely avoid calling this method if your application
80 /// does not need any of the features exposed by the command line
81 /// interface.
82 /// </remarks>
83 public static void Init (string [] args)
85 InitAndGetStartupFiles (args);
88 internal static ReportPrinter SetPrinter (ReportPrinter report_printer)
90 return ctx.Report.SetPrinter (report_printer);
93 /// <summary>
94 /// Optional initialization for the Evaluator.
95 /// </summary>
96 /// <remarks>
97 /// Initializes the Evaluator with the command line
98 /// options that would be processed by the command
99 /// line compiler. Only the first call to
100 /// InitAndGetStartupFiles or Init will work, any future
101 /// invocations are ignored.
103 /// You can safely avoid calling this method if your application
104 /// does not need any of the features exposed by the command line
105 /// interface.
107 /// This method return an array of strings that contains any
108 /// files that were specified in `args'.
109 /// </remarks>
110 public static string [] InitAndGetStartupFiles (string [] args)
112 lock (evaluator_lock){
113 if (inited)
114 return new string [0];
116 driver = Driver.Create (args, false, new ConsoleReportPrinter ());
117 if (driver == null)
118 throw new Exception ("Failed to create compiler driver with the given arguments");
120 RootContext.ToplevelTypes = new ModuleCompiled (ctx, true);
122 driver.ProcessDefaultConfig ();
124 var startup_files = new List<string> ();
125 foreach (CompilationUnit file in Location.SourceFiles)
126 startup_files.Add (file.Path);
128 CompilerCallableEntryPoint.Reset ();
129 RootContext.ToplevelTypes = new ModuleCompiled (ctx, true);
131 driver.LoadReferences ();
132 RootContext.EvalMode = true;
133 inited = true;
135 return startup_files.ToArray ();
139 static void Init ()
141 Init (new string [0]);
144 static void Reset ()
146 CompilerCallableEntryPoint.PartialReset ();
148 // Workaround for API limitation where full message printer cannot be passed
149 ReportPrinter printer;
150 if (MessageOutput == Console.Out || MessageOutput == Console.Error){
151 var console_reporter = new ConsoleReportPrinter (MessageOutput);
152 console_reporter.Fatal = driver.fatal_errors;
153 printer = console_reporter;
154 } else
155 printer = new StreamReportPrinter (MessageOutput);
157 ctx = new CompilerContext (new Report (printer));
158 RootContext.ToplevelTypes = new ModuleCompiled (ctx, true);
161 // PartialReset should not reset the core types, this is very redundant.
163 if (!TypeManager.InitCoreTypes (ctx))
164 throw new Exception ("Failed to InitCoreTypes");
165 TypeManager.InitOptionalCoreTypes (ctx);
167 Location.AddFile (null, "{interactive}");
168 Location.Initialize ();
170 current_debug_name = "interactive" + (count++) + ".dll";
171 if (Environment.GetEnvironmentVariable ("SAVE") != null){
172 CodeGen.Init (current_debug_name, current_debug_name, false, ctx);
173 } else
174 CodeGen.InitDynamic (ctx, current_debug_name);
177 /// <summary>
178 /// The base class for the classes that host the user generated code
179 /// </summary>
180 /// <remarks>
182 /// This is the base class that will host the code
183 /// executed by the Evaluator. By default
184 /// this is the Mono.CSharp.InteractiveBase class
185 /// which is useful for interactive use.
187 /// By changing this property you can control the
188 /// base class and the static members that are
189 /// available to your evaluated code.
190 /// </remarks>
191 static public Type InteractiveBaseClass {
192 get {
193 return interactive_base_class;
196 set {
197 if (value == null)
198 throw new ArgumentNullException ();
200 lock (evaluator_lock)
201 interactive_base_class = value;
205 /// <summary>
206 /// Interrupts the evaluation of an expression executing in Evaluate.
207 /// </summary>
208 /// <remarks>
209 /// Use this method to interrupt long-running invocations.
210 /// </remarks>
211 public static void Interrupt ()
213 if (!inited || !invoking)
214 return;
216 if (invoke_thread != null)
217 invoke_thread.Abort ();
220 /// <summary>
221 /// Compiles the input string and returns a delegate that represents the compiled code.
222 /// </summary>
223 /// <remarks>
225 /// Compiles the input string as a C# expression or
226 /// statement, unlike the Evaluate method, the
227 /// resulting delegate can be invoked multiple times
228 /// without incurring in the compilation overhead.
230 /// If the return value of this function is null,
231 /// this indicates that the parsing was complete.
232 /// If the return value is a string it indicates
233 /// that the input string was partial and that the
234 /// invoking code should provide more code before
235 /// the code can be successfully compiled.
237 /// If you know that you will always get full expressions or
238 /// statements and do not care about partial input, you can use
239 /// the other Compile overload.
241 /// On success, in addition to returning null, the
242 /// compiled parameter will be set to the delegate
243 /// that can be invoked to execute the code.
245 /// </remarks>
246 static public string Compile (string input, out CompiledMethod compiled)
248 if (input == null || input.Length == 0){
249 compiled = null;
250 return null;
253 lock (evaluator_lock){
254 if (!inited)
255 Init ();
257 bool partial_input;
258 CSharpParser parser = ParseString (ParseMode.Silent, input, out partial_input);
259 if (parser == null){
260 compiled = null;
261 if (partial_input)
262 return input;
264 ParseString (ParseMode.ReportErrors, input, out partial_input);
265 return null;
268 object parser_result = parser.InteractiveResult;
270 if (!(parser_result is Class)){
271 int errors = ctx.Report.Errors;
273 NamespaceEntry.VerifyAllUsing ();
274 if (errors == ctx.Report.Errors)
275 parser.CurrentNamespace.Extract (using_alias_list, using_list);
278 compiled = CompileBlock (parser_result as Class, parser.undo, ctx.Report);
281 return null;
284 /// <summary>
285 /// Compiles the input string and returns a delegate that represents the compiled code.
286 /// </summary>
287 /// <remarks>
289 /// Compiles the input string as a C# expression or
290 /// statement, unlike the Evaluate method, the
291 /// resulting delegate can be invoked multiple times
292 /// without incurring in the compilation overhead.
294 /// This method can only deal with fully formed input
295 /// strings and does not provide a completion mechanism.
296 /// If you must deal with partial input (for example for
297 /// interactive use) use the other overload.
299 /// On success, a delegate is returned that can be used
300 /// to invoke the method.
302 /// </remarks>
303 static public CompiledMethod Compile (string input)
305 CompiledMethod compiled;
307 // Ignore partial inputs
308 if (Compile (input, out compiled) != null){
309 // Error, the input was partial.
310 return null;
313 // Either null (on error) or the compiled method.
314 return compiled;
318 // Todo: Should we handle errors, or expect the calling code to setup
319 // the recording themselves?
322 /// <summary>
323 /// Evaluates and expression or statement and returns any result values.
324 /// </summary>
325 /// <remarks>
326 /// Evaluates the input string as a C# expression or
327 /// statement. If the input string is an expression
328 /// the result will be stored in the result variable
329 /// and the result_set variable will be set to true.
331 /// It is necessary to use the result/result_set
332 /// pair to identify when a result was set (for
333 /// example, execution of user-provided input can be
334 /// an expression, a statement or others, and
335 /// result_set would only be set if the input was an
336 /// expression.
338 /// If the return value of this function is null,
339 /// this indicates that the parsing was complete.
340 /// If the return value is a string, it indicates
341 /// that the input is partial and that the user
342 /// should provide an updated string.
343 /// </remarks>
344 public static string Evaluate (string input, out object result, out bool result_set)
346 CompiledMethod compiled;
348 result_set = false;
349 result = null;
351 input = Compile (input, out compiled);
352 if (input != null)
353 return input;
355 if (compiled == null)
356 return null;
359 // The code execution does not need to keep the compiler lock
361 object retval = typeof (NoValueSet);
363 try {
364 invoke_thread = System.Threading.Thread.CurrentThread;
365 invoking = true;
366 compiled (ref retval);
367 } catch (ThreadAbortException e){
368 Thread.ResetAbort ();
369 Console.WriteLine ("Interrupted!\n{0}", e);
370 } finally {
371 invoking = false;
375 // We use a reference to a compiler type, in this case
376 // Driver as a flag to indicate that this was a statement
378 if (retval != typeof (NoValueSet)){
379 result_set = true;
380 result = retval;
383 return null;
386 public static string [] GetCompletions (string input, out string prefix)
388 prefix = "";
389 if (input == null || input.Length == 0)
390 return null;
392 lock (evaluator_lock){
393 if (!inited)
394 Init ();
396 bool partial_input;
397 CSharpParser parser = ParseString (ParseMode.GetCompletions, input, out partial_input);
398 if (parser == null){
399 if (CSharpParser.yacc_verbose_flag != 0)
400 Console.WriteLine ("DEBUG: No completions available");
401 return null;
404 Class parser_result = parser.InteractiveResult as Class;
406 if (parser_result == null){
407 if (CSharpParser.yacc_verbose_flag != 0)
408 Console.WriteLine ("Do not know how to cope with !Class yet");
409 return null;
412 try {
413 RootContext.ResolveTree ();
414 if (ctx.Report.Errors != 0)
415 return null;
417 RootContext.PopulateTypes ();
418 if (ctx.Report.Errors != 0)
419 return null;
421 MethodOrOperator method = null;
422 foreach (MemberCore member in parser_result.Methods){
423 if (member.Name != "Host")
424 continue;
426 method = (MethodOrOperator) member;
427 break;
429 if (method == null)
430 throw new InternalErrorException ("did not find the the Host method");
432 BlockContext bc = new BlockContext (method, method.Block, method.ReturnType);
434 try {
435 method.Block.Resolve (null, bc, method.ParameterInfo, method);
436 } catch (CompletionResult cr){
437 prefix = cr.BaseText;
438 return cr.Result;
440 } finally {
441 parser.undo.ExecuteUndo ();
445 return null;
448 /// <summary>
449 /// Executes the given expression or statement.
450 /// </summary>
451 /// <remarks>
452 /// Executes the provided statement, returns true
453 /// on success, false on parsing errors. Exceptions
454 /// might be thrown by the called code.
455 /// </remarks>
456 public static bool Run (string statement)
458 if (!inited)
459 Init ();
461 object result;
462 bool result_set;
464 bool ok = Evaluate (statement, out result, out result_set) == null;
466 return ok;
469 /// <summary>
470 /// Evaluates and expression or statement and returns the result.
471 /// </summary>
472 /// <remarks>
473 /// Evaluates the input string as a C# expression or
474 /// statement and returns the value.
476 /// This method will throw an exception if there is a syntax error,
477 /// of if the provided input is not an expression but a statement.
478 /// </remarks>
479 public static object Evaluate (string input)
481 object result;
482 bool result_set;
484 string r = Evaluate (input, out result, out result_set);
486 if (r != null)
487 throw new ArgumentException ("Syntax error on input: partial input");
489 if (result_set == false)
490 throw new ArgumentException ("The expression did not set a result");
492 return result;
495 enum InputKind {
496 EOF,
497 StatementOrExpression,
498 CompilationUnit,
499 Error
503 // Deambiguates the input string to determine if we
504 // want to process a statement or if we want to
505 // process a compilation unit.
507 // This is done using a top-down predictive parser,
508 // since the yacc/jay parser can not deambiguage this
509 // without more than one lookahead token. There are very
510 // few ambiguities.
512 static InputKind ToplevelOrStatement (SeekableStreamReader seekable)
514 Tokenizer tokenizer = new Tokenizer (seekable, (CompilationUnit) Location.SourceFiles [0], ctx);
516 int t = tokenizer.token ();
517 switch (t){
518 case Token.EOF:
519 return InputKind.EOF;
521 // These are toplevels
522 case Token.EXTERN:
523 case Token.OPEN_BRACKET:
524 case Token.ABSTRACT:
525 case Token.CLASS:
526 case Token.ENUM:
527 case Token.INTERFACE:
528 case Token.INTERNAL:
529 case Token.NAMESPACE:
530 case Token.PRIVATE:
531 case Token.PROTECTED:
532 case Token.PUBLIC:
533 case Token.SEALED:
534 case Token.STATIC:
535 case Token.STRUCT:
536 return InputKind.CompilationUnit;
538 // Definitely expression
539 case Token.FIXED:
540 case Token.BOOL:
541 case Token.BYTE:
542 case Token.CHAR:
543 case Token.DECIMAL:
544 case Token.DOUBLE:
545 case Token.FLOAT:
546 case Token.INT:
547 case Token.LONG:
548 case Token.NEW:
549 case Token.OBJECT:
550 case Token.SBYTE:
551 case Token.SHORT:
552 case Token.STRING:
553 case Token.UINT:
554 case Token.ULONG:
555 return InputKind.StatementOrExpression;
557 // These need deambiguation help
558 case Token.USING:
559 t = tokenizer.token ();
560 if (t == Token.EOF)
561 return InputKind.EOF;
563 if (t == Token.IDENTIFIER)
564 return InputKind.CompilationUnit;
565 return InputKind.StatementOrExpression;
568 // Distinguish between:
569 // delegate opt_anonymous_method_signature block
570 // delegate type
571 case Token.DELEGATE:
572 t = tokenizer.token ();
573 if (t == Token.EOF)
574 return InputKind.EOF;
575 if (t == Token.OPEN_PARENS || t == Token.OPEN_BRACE)
576 return InputKind.StatementOrExpression;
577 return InputKind.CompilationUnit;
579 // Distinguih between:
580 // unsafe block
581 // unsafe as modifier of a type declaration
582 case Token.UNSAFE:
583 t = tokenizer.token ();
584 if (t == Token.EOF)
585 return InputKind.EOF;
586 if (t == Token.OPEN_PARENS)
587 return InputKind.StatementOrExpression;
588 return InputKind.CompilationUnit;
590 // These are errors: we list explicitly what we had
591 // from the grammar, ERROR and then everything else
593 case Token.READONLY:
594 case Token.OVERRIDE:
595 case Token.ERROR:
596 return InputKind.Error;
598 // This catches everything else allowed by
599 // expressions. We could add one-by-one use cases
600 // if needed.
601 default:
602 return InputKind.StatementOrExpression;
607 // Parses the string @input and returns a CSharpParser if succeeful.
609 // if @silent is set to true then no errors are
610 // reported to the user. This is used to do various calls to the
611 // parser and check if the expression is parsable.
613 // @partial_input: if @silent is true, then it returns whether the
614 // parsed expression was partial, and more data is needed
616 static CSharpParser ParseString (ParseMode mode, string input, out bool partial_input)
618 partial_input = false;
619 Reset ();
620 queued_fields.Clear ();
621 Tokenizer.LocatedToken.Initialize ();
623 Stream s = new MemoryStream (Encoding.Default.GetBytes (input));
624 SeekableStreamReader seekable = new SeekableStreamReader (s, Encoding.Default);
626 InputKind kind = ToplevelOrStatement (seekable);
627 if (kind == InputKind.Error){
628 if (mode == ParseMode.ReportErrors)
629 ctx.Report.Error (-25, "Detection Parsing Error");
630 partial_input = false;
631 return null;
634 if (kind == InputKind.EOF){
635 if (mode == ParseMode.ReportErrors)
636 Console.Error.WriteLine ("Internal error: EOF condition should have been detected in a previous call with silent=true");
637 partial_input = true;
638 return null;
641 seekable.Position = 0;
643 CSharpParser parser = new CSharpParser (seekable, (CompilationUnit) Location.SourceFiles [0], ctx);
645 if (kind == InputKind.StatementOrExpression){
646 parser.Lexer.putback_char = Tokenizer.EvalStatementParserCharacter;
647 RootContext.StatementMode = true;
648 } else {
650 // Do not activate EvalCompilationUnitParserCharacter until
651 // I have figured out all the limitations to invoke methods
652 // in the generated classes. See repl.txt
654 parser.Lexer.putback_char = Tokenizer.EvalUsingDeclarationsParserCharacter;
655 //parser.Lexer.putback_char = Tokenizer.EvalCompilationUnitParserCharacter;
656 RootContext.StatementMode = false;
659 if (mode == ParseMode.GetCompletions)
660 parser.Lexer.CompleteOnEOF = true;
662 ReportPrinter old_printer = null;
663 if ((mode == ParseMode.Silent || mode == ParseMode.GetCompletions) && CSharpParser.yacc_verbose_flag == 0)
664 old_printer = SetPrinter (new StreamReportPrinter (TextWriter.Null));
666 try {
667 parser.parse ();
668 } finally {
669 if (ctx.Report.Errors != 0){
670 if (mode != ParseMode.ReportErrors && parser.UnexpectedEOF)
671 partial_input = true;
673 parser.undo.ExecuteUndo ();
674 parser = null;
677 if (old_printer != null)
678 SetPrinter (old_printer);
680 return parser;
684 // Queue all the fields that we use, as we need to then go from FieldBuilder to FieldInfo
685 // or reflection gets confused (it basically gets confused, and variables override each
686 // other).
688 static List<Field> queued_fields = new List<Field> ();
690 //static ArrayList types = new ArrayList ();
692 static volatile bool invoking;
694 static CompiledMethod CompileBlock (Class host, Undo undo, Report Report)
696 RootContext.ResolveTree ();
697 if (Report.Errors != 0){
698 undo.ExecuteUndo ();
699 return null;
702 RootContext.PopulateTypes ();
704 if (Report.Errors != 0){
705 undo.ExecuteUndo ();
706 return null;
709 TypeBuilder tb = null;
710 MethodBuilder mb = null;
712 if (host != null){
713 tb = host.TypeBuilder;
714 mb = null;
715 foreach (MemberCore member in host.Methods){
716 if (member.Name != "Host")
717 continue;
719 MethodOrOperator method = (MethodOrOperator) member;
720 mb = method.MethodBuilder;
721 break;
724 if (mb == null)
725 throw new Exception ("Internal error: did not find the method builder for the generated method");
728 RootContext.EmitCode ();
729 if (Report.Errors != 0){
730 undo.ExecuteUndo ();
731 return null;
734 RootContext.CloseTypes ();
736 if (Environment.GetEnvironmentVariable ("SAVE") != null)
737 CodeGen.Save (current_debug_name, false, Report);
739 if (host == null)
740 return null;
743 // Unlike Mono, .NET requires that the MethodInfo is fetched, it cant
744 // work from MethodBuilders. Retarded, I know.
746 Type tt = CodeGen.Assembly.Builder.GetType (tb.Name);
747 MethodInfo mi = tt.GetMethod (mb.Name);
749 // Pull the FieldInfos from the type, and keep track of them
750 foreach (Field field in queued_fields){
751 FieldInfo fi = tt.GetField (field.Name);
753 FieldInfo old;
755 // If a previous value was set, nullify it, so that we do
756 // not leak memory
757 if (fields.TryGetValue (field.Name, out old)){
758 if (TypeManager.IsStruct (old.FieldType)){
760 // TODO: Clear fields for structs
762 } else {
763 try {
764 old.SetValue (null, null);
765 } catch {
770 fields [field.Name] = fi;
772 //types.Add (tb);
774 queued_fields.Clear ();
776 return (CompiledMethod) System.Delegate.CreateDelegate (typeof (CompiledMethod), mi);
779 static internal void LoadAliases (NamespaceEntry ns)
781 ns.Populate (using_alias_list, using_list);
784 /// <summary>
785 /// A sentinel value used to indicate that no value was
786 /// was set by the compiled function. This is used to
787 /// differentiate between a function not returning a
788 /// value and null.
789 /// </summary>
790 public class NoValueSet {
793 static internal FieldInfo LookupField (string name)
795 FieldInfo fi;
796 if (!fields.TryGetValue (name, out fi))
797 return null;
799 return fi;
803 // Puts the FieldBuilder into a queue of names that will be
804 // registered. We can not register FieldBuilders directly
805 // we need to fetch the FieldInfo after Reflection cooks the
806 // types, or bad things happen (bad means: FieldBuilders behave
807 // incorrectly across multiple assemblies, causing assignments to
808 // invalid areas
810 // This also serves for the parser to register Field classes
811 // that should be exposed as global variables
813 static internal void QueueField (Field f)
815 queued_fields.Add (f);
818 static string Quote (string s)
820 if (s.IndexOf ('"') != -1)
821 s = s.Replace ("\"", "\\\"");
823 return "\"" + s + "\"";
826 static public string GetUsing ()
828 lock (evaluator_lock){
829 StringBuilder sb = new StringBuilder ();
831 foreach (object x in using_alias_list)
832 sb.Append (String.Format ("using {0};\n", x));
834 foreach (object x in using_list)
835 sb.Append (String.Format ("using {0};\n", x));
837 return sb.ToString ();
841 static internal ICollection<string> GetUsingList ()
843 var res = new List<string> (using_list.Count);
844 foreach (object ue in using_list)
845 res.Add (ue.ToString ());
846 return res;
849 static internal string [] GetVarNames ()
851 lock (evaluator_lock){
852 return new List<string> (fields.Keys).ToArray ();
856 static public string GetVars ()
858 lock (evaluator_lock){
859 StringBuilder sb = new StringBuilder ();
861 foreach (var de in fields){
862 FieldInfo fi = LookupField (de.Key);
863 object value = null;
864 bool error = false;
866 try {
867 if (value == null)
868 value = "null";
869 value = fi.GetValue (null);
870 if (value is string)
871 value = Quote ((string)value);
872 } catch {
873 error = true;
876 if (error)
877 sb.Append (String.Format ("{0} {1} <error reading value>", TypeManager.CSharpName(fi.FieldType), de.Key));
878 else
879 sb.Append (String.Format ("{0} {1} = {2}", TypeManager.CSharpName(fi.FieldType), de.Key, value));
882 return sb.ToString ();
886 /// <summary>
887 /// Loads the given assembly and exposes the API to the user.
888 /// </summary>
889 static public void LoadAssembly (string file)
891 lock (evaluator_lock){
892 driver.LoadAssembly (file, false);
893 GlobalRootNamespace.Instance.ComputeNamespaces (ctx);
897 /// <summary>
898 /// Exposes the API of the given assembly to the Evaluator
899 /// </summary>
900 static public void ReferenceAssembly (Assembly a)
902 lock (evaluator_lock){
903 GlobalRootNamespace.Instance.AddAssemblyReference (a);
904 GlobalRootNamespace.Instance.ComputeNamespaces (ctx);
908 /// <summary>
909 /// If true, turns type expressions into valid expressions
910 /// and calls the describe method on it
911 /// </summary>
912 public static bool DescribeTypeExpressions;
916 /// <summary>
917 /// A delegate that can be used to invoke the
918 /// compiled expression or statement.
919 /// </summary>
920 /// <remarks>
921 /// Since the Compile methods will compile
922 /// statements and expressions into the same
923 /// delegate, you can tell if a value was returned
924 /// by checking whether the returned value is of type
925 /// NoValueSet.
926 /// </remarks>
928 public delegate void CompiledMethod (ref object retvalue);
930 /// <summary>
931 /// The default base class for every interaction line
932 /// </summary>
933 /// <remarks>
934 /// The expressions and statements behave as if they were
935 /// a static method of this class. The InteractiveBase class
936 /// contains a number of useful methods, but can be overwritten
937 /// by setting the InteractiveBaseType property in the Evaluator
938 /// </remarks>
939 public class InteractiveBase {
940 /// <summary>
941 /// Determines where the standard output of methods in this class will go.
942 /// </summary>
943 public static TextWriter Output = Console.Out;
945 /// <summary>
946 /// Determines where the standard error of methods in this class will go.
947 /// </summary>
948 public static TextWriter Error = Console.Error;
950 /// <summary>
951 /// The primary prompt used for interactive use.
952 /// </summary>
953 public static string Prompt = "csharp> ";
955 /// <summary>
956 /// The secondary prompt used for interactive use (used when
957 /// an expression is incomplete).
958 /// </summary>
959 public static string ContinuationPrompt = " > ";
961 /// <summary>
962 /// Used to signal that the user has invoked the `quit' statement.
963 /// </summary>
964 public static bool QuitRequested;
966 /// <summary>
967 /// Shows all the variables defined so far.
968 /// </summary>
969 static public void ShowVars ()
971 Output.Write (Evaluator.GetVars ());
972 Output.Flush ();
975 /// <summary>
976 /// Displays the using statements in effect at this point.
977 /// </summary>
978 static public void ShowUsing ()
980 Output.Write (Evaluator.GetUsing ());
981 Output.Flush ();
984 public delegate void Simple ();
986 /// <summary>
987 /// Times the execution of the given delegate
988 /// </summary>
989 static public TimeSpan Time (Simple a)
991 DateTime start = DateTime.Now;
992 a ();
993 return DateTime.Now - start;
996 #if !SMCS_SOURCE
997 /// <summary>
998 /// Loads the assemblies from a package
999 /// </summary>
1000 /// <remarks>
1001 /// Loads the assemblies from a package. This is equivalent
1002 /// to passing the -pkg: command line flag to the C# compiler
1003 /// on the command line.
1004 /// </remarks>
1005 static public void LoadPackage (string pkg)
1007 if (pkg == null){
1008 Error.WriteLine ("Invalid package specified");
1009 return;
1012 string pkgout = Driver.GetPackageFlags (pkg, false, RootContext.ToplevelTypes.Compiler.Report);
1013 if (pkgout == null)
1014 return;
1016 string [] xargs = pkgout.Trim (new Char [] {' ', '\n', '\r', '\t'}).
1017 Split (new Char [] { ' ', '\t'});
1019 foreach (string s in xargs){
1020 if (s.StartsWith ("-r:") || s.StartsWith ("/r:") || s.StartsWith ("/reference:")){
1021 string lib = s.Substring (s.IndexOf (':')+1);
1023 Evaluator.LoadAssembly (lib);
1024 continue;
1028 #endif
1030 /// <summary>
1031 /// Loads the assembly
1032 /// </summary>
1033 /// <remarks>
1034 /// Loads the specified assembly and makes its types
1035 /// available to the evaluator. This is equivalent
1036 /// to passing the -pkg: command line flag to the C#
1037 /// compiler on the command line.
1038 /// </remarks>
1039 static public void LoadAssembly (string assembly)
1041 Evaluator.LoadAssembly (assembly);
1044 /// <summary>
1045 /// Returns a list of available static methods.
1046 /// </summary>
1047 static public string help {
1048 get {
1049 return "Static methods:\n"+
1050 " Describe(obj) - Describes the object's type\n" +
1051 " LoadPackage (pkg); - Loads the given Package (like -pkg:FILE)\n" +
1052 " LoadAssembly (ass) - Loads the given assembly (like -r:ASS)\n" +
1053 " ShowVars (); - Shows defined local variables.\n" +
1054 " ShowUsing (); - Show active using decltions.\n" +
1055 " Prompt - The prompt used by the C# shell\n" +
1056 " ContinuationPrompt - The prompt for partial input\n" +
1057 " Time(() -> { }) - Times the specified code\n" +
1058 " quit;\n" +
1059 " help;\n";
1063 /// <summary>
1064 /// Indicates to the read-eval-print-loop that the interaction should be finished.
1065 /// </summary>
1066 static public object quit {
1067 get {
1068 QuitRequested = true;
1069 return null;
1073 #if !NET_2_1
1074 /// <summary>
1075 /// Describes an object or a type.
1076 /// </summary>
1077 /// <remarks>
1078 /// This method will show a textual representation
1079 /// of the object's type. If the object is a
1080 /// System.Type it renders the type directly,
1081 /// otherwise it renders the type returned by
1082 /// invoking GetType on the object.
1083 /// </remarks>
1084 static public string Describe (object x)
1086 if (x == null)
1087 return "";
1089 Type t = x as Type;
1090 if (t == null)
1091 t = x.GetType ();
1093 StringWriter sw = new StringWriter ();
1094 new Outline (t, sw, true, false, false).OutlineType ();
1095 return sw.ToString ();
1097 #endif
1101 // A local variable reference that will create a Field in a
1102 // Class with the resolved type. This is necessary so we can
1103 // support "var" as a field type in a class declaration.
1105 // We allow LocalVariableReferece to do the heavy lifting, and
1106 // then we insert the field with the resolved type
1108 public class LocalVariableReferenceWithClassSideEffect : LocalVariableReference {
1109 TypeContainer container;
1110 string name;
1112 public LocalVariableReferenceWithClassSideEffect (TypeContainer container, string name, Block current_block, string local_variable_id, LocalInfo li, Location loc)
1113 : base (current_block, local_variable_id, loc, li, false)
1115 this.container = container;
1116 this.name = name;
1119 public override bool Equals (object obj)
1121 LocalVariableReferenceWithClassSideEffect lvr = obj as LocalVariableReferenceWithClassSideEffect;
1122 if (lvr == null)
1123 return false;
1125 if (lvr.name != name || lvr.container != container)
1126 return false;
1128 return base.Equals (obj);
1131 public override int GetHashCode ()
1133 return name.GetHashCode ();
1136 override public Expression DoResolveLValue (ResolveContext ec, Expression right_side)
1138 Expression ret = base.DoResolveLValue (ec, right_side);
1139 if (ret == null)
1140 return null;
1142 Field f = new Field (container, new TypeExpression (ret.Type, Location),
1143 Modifiers.PUBLIC | Modifiers.STATIC,
1144 new MemberName (name, Location), null);
1145 container.AddField (f);
1146 if (f.Define ())
1147 Evaluator.QueueField (f);
1149 return ret;
1153 /// <summary>
1154 /// A class used to assign values if the source expression is not void
1156 /// Used by the interactive shell to allow it to call this code to set
1157 /// the return value for an invocation.
1158 /// </summary>
1159 class OptionalAssign : SimpleAssign {
1160 public OptionalAssign (Expression t, Expression s, Location loc)
1161 : base (t, s, loc)
1165 protected override Expression DoResolve (ResolveContext ec)
1167 CloneContext cc = new CloneContext ();
1168 Expression clone = source.Clone (cc);
1171 // A useful feature for the REPL: if we can resolve the expression
1172 // as a type, Describe the type;
1174 if (Evaluator.DescribeTypeExpressions){
1175 var old_printer = Evaluator.SetPrinter (new StreamReportPrinter (TextWriter.Null));
1176 clone = clone.Resolve (ec);
1177 if (clone == null){
1178 clone = source.Clone (cc);
1179 clone = clone.Resolve (ec, ResolveFlags.Type);
1180 if (clone == null){
1181 Evaluator.SetPrinter (old_printer);
1182 clone = source.Clone (cc);
1183 clone = clone.Resolve (ec);
1184 return null;
1187 Arguments args = new Arguments (1);
1188 args.Add (new Argument (new TypeOf (source, Location)));
1189 source = new Invocation (new SimpleName ("Describe", Location), args).Resolve (ec);
1191 Evaluator.SetPrinter (old_printer);
1192 } else {
1193 clone = clone.Resolve (ec);
1194 if (clone == null)
1195 return null;
1198 // This means its really a statement.
1199 if (clone.Type == TypeManager.void_type){
1200 source = source.Resolve (ec);
1201 target = null;
1202 type = TypeManager.void_type;
1203 eclass = ExprClass.Value;
1204 return this;
1207 return base.DoResolve (ec);
1210 public override void Emit (EmitContext ec)
1212 if (target == null)
1213 source.Emit (ec);
1214 else
1215 base.Emit (ec);
1218 public override void EmitStatement (EmitContext ec)
1220 if (target == null)
1221 source.Emit (ec);
1222 else
1223 base.EmitStatement (ec);
1227 public class Undo {
1228 List<KeyValuePair<TypeContainer, TypeContainer>> undo_types;
1230 public Undo ()
1232 undo_types = new List<KeyValuePair<TypeContainer, TypeContainer>> ();
1235 public void AddTypeContainer (TypeContainer current_container, TypeContainer tc)
1237 if (current_container == tc){
1238 Console.Error.WriteLine ("Internal error: inserting container into itself");
1239 return;
1242 if (undo_types == null)
1243 undo_types = new List<KeyValuePair<TypeContainer, TypeContainer>> ();
1245 undo_types.Add (new KeyValuePair<TypeContainer, TypeContainer> (current_container, tc));
1248 public void ExecuteUndo ()
1250 if (undo_types == null)
1251 return;
1253 foreach (var p in undo_types){
1254 TypeContainer current_container = p.Key;
1256 current_container.RemoveTypeContainer (p.Value);
1258 undo_types = null;