2010-05-19 Jb Evain <jbevain@novell.com>
[mcs.git] / mcs / eval.cs
blobe9758dade72d6064887ca088cf68ad751652ef87
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, Tuple<FieldSpec, FieldInfo>> fields = new Dictionary<string, Tuple<FieldSpec, FieldInfo>> ();
62 static TypeSpec interactive_base_class;
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);
130 /*var ctypes = */TypeManager.InitCoreTypes ();
131 TypeManager.InitExpressionTypes ();
133 Import.Initialize ();
134 driver.LoadReferences ();
135 TypeManager.InitOptionalCoreTypes (ctx);
137 RootContext.EvalMode = true;
138 inited = true;
140 return startup_files.ToArray ();
144 static void Init ()
146 Init (new string [0]);
149 static void Reset ()
151 CompilerCallableEntryPoint.PartialReset ();
152 RootContext.PartialReset ();
154 // Workaround for API limitation where full message printer cannot be passed
155 ReportPrinter printer;
156 if (MessageOutput == Console.Out || MessageOutput == Console.Error){
157 var console_reporter = new ConsoleReportPrinter (MessageOutput);
158 console_reporter.Fatal = driver.fatal_errors;
159 printer = console_reporter;
160 } else
161 printer = new StreamReportPrinter (MessageOutput);
163 ctx = new CompilerContext (new Report (printer));
164 RootContext.ToplevelTypes = new ModuleCompiled (ctx, true);
167 // PartialReset should not reset the core types, this is very redundant.
169 // if (!TypeManager.InitCoreTypes (ctx, null))
170 // throw new Exception ("Failed to InitCoreTypes");
171 // TypeManager.InitOptionalCoreTypes (ctx);
173 Location.AddFile (null, "{interactive}");
174 Location.Initialize ();
176 current_debug_name = "interactive" + (count++) + ".dll";
177 if (Environment.GetEnvironmentVariable ("SAVE") != null){
178 CodeGen.Init (current_debug_name, current_debug_name, false, ctx);
179 } else
180 CodeGen.InitDynamic (ctx, current_debug_name);
183 /// <summary>
184 /// The base class for the classes that host the user generated code
185 /// </summary>
186 /// <remarks>
188 /// This is the base class that will host the code
189 /// executed by the Evaluator. By default
190 /// this is the Mono.CSharp.InteractiveBase class
191 /// which is useful for interactive use.
193 /// By changing this property you can control the
194 /// base class and the static members that are
195 /// available to your evaluated code.
196 /// </remarks>
197 static public TypeSpec InteractiveBaseClass {
198 get {
199 if (interactive_base_class != null)
200 return interactive_base_class;
202 return Import.ImportType (typeof (InteractiveBase));
206 public static void SetInteractiveBaseClass (Type type)
208 if (type == null)
209 throw new ArgumentNullException ();
211 lock (evaluator_lock)
212 interactive_base_class = Import.ImportType (type);
215 /// <summary>
216 /// Interrupts the evaluation of an expression executing in Evaluate.
217 /// </summary>
218 /// <remarks>
219 /// Use this method to interrupt long-running invocations.
220 /// </remarks>
221 public static void Interrupt ()
223 if (!inited || !invoking)
224 return;
226 if (invoke_thread != null)
227 invoke_thread.Abort ();
230 /// <summary>
231 /// Compiles the input string and returns a delegate that represents the compiled code.
232 /// </summary>
233 /// <remarks>
235 /// Compiles the input string as a C# expression or
236 /// statement, unlike the Evaluate method, the
237 /// resulting delegate can be invoked multiple times
238 /// without incurring in the compilation overhead.
240 /// If the return value of this function is null,
241 /// this indicates that the parsing was complete.
242 /// If the return value is a string it indicates
243 /// that the input string was partial and that the
244 /// invoking code should provide more code before
245 /// the code can be successfully compiled.
247 /// If you know that you will always get full expressions or
248 /// statements and do not care about partial input, you can use
249 /// the other Compile overload.
251 /// On success, in addition to returning null, the
252 /// compiled parameter will be set to the delegate
253 /// that can be invoked to execute the code.
255 /// </remarks>
256 static public string Compile (string input, out CompiledMethod compiled)
258 if (input == null || input.Length == 0){
259 compiled = null;
260 return null;
263 lock (evaluator_lock){
264 if (!inited)
265 Init ();
267 bool partial_input;
268 CSharpParser parser = ParseString (ParseMode.Silent, input, out partial_input);
269 if (parser == null){
270 compiled = null;
271 if (partial_input)
272 return input;
274 ParseString (ParseMode.ReportErrors, input, out partial_input);
275 return null;
278 object parser_result = parser.InteractiveResult;
280 if (!(parser_result is Class)){
281 int errors = ctx.Report.Errors;
283 NamespaceEntry.VerifyAllUsing ();
284 if (errors == ctx.Report.Errors)
285 parser.CurrentNamespace.Extract (using_alias_list, using_list);
288 compiled = CompileBlock (parser_result as Class, parser.undo, ctx.Report);
291 return null;
294 /// <summary>
295 /// Compiles the input string and returns a delegate that represents the compiled code.
296 /// </summary>
297 /// <remarks>
299 /// Compiles the input string as a C# expression or
300 /// statement, unlike the Evaluate method, the
301 /// resulting delegate can be invoked multiple times
302 /// without incurring in the compilation overhead.
304 /// This method can only deal with fully formed input
305 /// strings and does not provide a completion mechanism.
306 /// If you must deal with partial input (for example for
307 /// interactive use) use the other overload.
309 /// On success, a delegate is returned that can be used
310 /// to invoke the method.
312 /// </remarks>
313 static public CompiledMethod Compile (string input)
315 CompiledMethod compiled;
317 // Ignore partial inputs
318 if (Compile (input, out compiled) != null){
319 // Error, the input was partial.
320 return null;
323 // Either null (on error) or the compiled method.
324 return compiled;
328 // Todo: Should we handle errors, or expect the calling code to setup
329 // the recording themselves?
332 /// <summary>
333 /// Evaluates and expression or statement and returns any result values.
334 /// </summary>
335 /// <remarks>
336 /// Evaluates the input string as a C# expression or
337 /// statement. If the input string is an expression
338 /// the result will be stored in the result variable
339 /// and the result_set variable will be set to true.
341 /// It is necessary to use the result/result_set
342 /// pair to identify when a result was set (for
343 /// example, execution of user-provided input can be
344 /// an expression, a statement or others, and
345 /// result_set would only be set if the input was an
346 /// expression.
348 /// If the return value of this function is null,
349 /// this indicates that the parsing was complete.
350 /// If the return value is a string, it indicates
351 /// that the input is partial and that the user
352 /// should provide an updated string.
353 /// </remarks>
354 public static string Evaluate (string input, out object result, out bool result_set)
356 CompiledMethod compiled;
358 result_set = false;
359 result = null;
361 input = Compile (input, out compiled);
362 if (input != null)
363 return input;
365 if (compiled == null)
366 return null;
369 // The code execution does not need to keep the compiler lock
371 object retval = typeof (NoValueSet);
373 try {
374 invoke_thread = System.Threading.Thread.CurrentThread;
375 invoking = true;
376 compiled (ref retval);
377 } catch (ThreadAbortException e){
378 Thread.ResetAbort ();
379 Console.WriteLine ("Interrupted!\n{0}", e);
380 } finally {
381 invoking = false;
385 // We use a reference to a compiler type, in this case
386 // Driver as a flag to indicate that this was a statement
388 if (retval != typeof (NoValueSet)){
389 result_set = true;
390 result = retval;
393 return null;
396 public static string [] GetCompletions (string input, out string prefix)
398 prefix = "";
399 if (input == null || input.Length == 0)
400 return null;
402 lock (evaluator_lock){
403 if (!inited)
404 Init ();
406 bool partial_input;
407 CSharpParser parser = ParseString (ParseMode.GetCompletions, input, out partial_input);
408 if (parser == null){
409 if (CSharpParser.yacc_verbose_flag != 0)
410 Console.WriteLine ("DEBUG: No completions available");
411 return null;
414 Class parser_result = parser.InteractiveResult as Class;
416 if (parser_result == null){
417 if (CSharpParser.yacc_verbose_flag != 0)
418 Console.WriteLine ("Do not know how to cope with !Class yet");
419 return null;
422 try {
423 RootContext.ResolveTree ();
424 if (ctx.Report.Errors != 0)
425 return null;
427 RootContext.PopulateTypes ();
428 if (ctx.Report.Errors != 0)
429 return null;
431 MethodOrOperator method = null;
432 foreach (MemberCore member in parser_result.Methods){
433 if (member.Name != "Host")
434 continue;
436 method = (MethodOrOperator) member;
437 break;
439 if (method == null)
440 throw new InternalErrorException ("did not find the the Host method");
442 BlockContext bc = new BlockContext (method, method.Block, method.ReturnType);
444 try {
445 method.Block.Resolve (null, bc, method.ParameterInfo, method);
446 } catch (CompletionResult cr){
447 prefix = cr.BaseText;
448 return cr.Result;
450 } finally {
451 parser.undo.ExecuteUndo ();
455 return null;
458 /// <summary>
459 /// Executes the given expression or statement.
460 /// </summary>
461 /// <remarks>
462 /// Executes the provided statement, returns true
463 /// on success, false on parsing errors. Exceptions
464 /// might be thrown by the called code.
465 /// </remarks>
466 public static bool Run (string statement)
468 if (!inited)
469 Init ();
471 object result;
472 bool result_set;
474 bool ok = Evaluate (statement, out result, out result_set) == null;
476 return ok;
479 /// <summary>
480 /// Evaluates and expression or statement and returns the result.
481 /// </summary>
482 /// <remarks>
483 /// Evaluates the input string as a C# expression or
484 /// statement and returns the value.
486 /// This method will throw an exception if there is a syntax error,
487 /// of if the provided input is not an expression but a statement.
488 /// </remarks>
489 public static object Evaluate (string input)
491 object result;
492 bool result_set;
494 string r = Evaluate (input, out result, out result_set);
496 if (r != null)
497 throw new ArgumentException ("Syntax error on input: partial input");
499 if (result_set == false)
500 throw new ArgumentException ("The expression did not set a result");
502 return result;
505 enum InputKind {
506 EOF,
507 StatementOrExpression,
508 CompilationUnit,
509 Error
513 // Deambiguates the input string to determine if we
514 // want to process a statement or if we want to
515 // process a compilation unit.
517 // This is done using a top-down predictive parser,
518 // since the yacc/jay parser can not deambiguage this
519 // without more than one lookahead token. There are very
520 // few ambiguities.
522 static InputKind ToplevelOrStatement (SeekableStreamReader seekable)
524 Tokenizer tokenizer = new Tokenizer (seekable, (CompilationUnit) Location.SourceFiles [0], ctx);
526 int t = tokenizer.token ();
527 switch (t){
528 case Token.EOF:
529 return InputKind.EOF;
531 // These are toplevels
532 case Token.EXTERN:
533 case Token.OPEN_BRACKET:
534 case Token.ABSTRACT:
535 case Token.CLASS:
536 case Token.ENUM:
537 case Token.INTERFACE:
538 case Token.INTERNAL:
539 case Token.NAMESPACE:
540 case Token.PRIVATE:
541 case Token.PROTECTED:
542 case Token.PUBLIC:
543 case Token.SEALED:
544 case Token.STATIC:
545 case Token.STRUCT:
546 return InputKind.CompilationUnit;
548 // Definitely expression
549 case Token.FIXED:
550 case Token.BOOL:
551 case Token.BYTE:
552 case Token.CHAR:
553 case Token.DECIMAL:
554 case Token.DOUBLE:
555 case Token.FLOAT:
556 case Token.INT:
557 case Token.LONG:
558 case Token.NEW:
559 case Token.OBJECT:
560 case Token.SBYTE:
561 case Token.SHORT:
562 case Token.STRING:
563 case Token.UINT:
564 case Token.ULONG:
565 return InputKind.StatementOrExpression;
567 // These need deambiguation help
568 case Token.USING:
569 t = tokenizer.token ();
570 if (t == Token.EOF)
571 return InputKind.EOF;
573 if (t == Token.IDENTIFIER)
574 return InputKind.CompilationUnit;
575 return InputKind.StatementOrExpression;
578 // Distinguish between:
579 // delegate opt_anonymous_method_signature block
580 // delegate type
581 case Token.DELEGATE:
582 t = tokenizer.token ();
583 if (t == Token.EOF)
584 return InputKind.EOF;
585 if (t == Token.OPEN_PARENS || t == Token.OPEN_BRACE)
586 return InputKind.StatementOrExpression;
587 return InputKind.CompilationUnit;
589 // Distinguih between:
590 // unsafe block
591 // unsafe as modifier of a type declaration
592 case Token.UNSAFE:
593 t = tokenizer.token ();
594 if (t == Token.EOF)
595 return InputKind.EOF;
596 if (t == Token.OPEN_PARENS)
597 return InputKind.StatementOrExpression;
598 return InputKind.CompilationUnit;
600 // These are errors: we list explicitly what we had
601 // from the grammar, ERROR and then everything else
603 case Token.READONLY:
604 case Token.OVERRIDE:
605 case Token.ERROR:
606 return InputKind.Error;
608 // This catches everything else allowed by
609 // expressions. We could add one-by-one use cases
610 // if needed.
611 default:
612 return InputKind.StatementOrExpression;
617 // Parses the string @input and returns a CSharpParser if succeeful.
619 // if @silent is set to true then no errors are
620 // reported to the user. This is used to do various calls to the
621 // parser and check if the expression is parsable.
623 // @partial_input: if @silent is true, then it returns whether the
624 // parsed expression was partial, and more data is needed
626 static CSharpParser ParseString (ParseMode mode, string input, out bool partial_input)
628 partial_input = false;
629 Reset ();
630 queued_fields.Clear ();
631 Tokenizer.LocatedToken.Initialize ();
633 Stream s = new MemoryStream (Encoding.Default.GetBytes (input));
634 SeekableStreamReader seekable = new SeekableStreamReader (s, Encoding.Default);
636 InputKind kind = ToplevelOrStatement (seekable);
637 if (kind == InputKind.Error){
638 if (mode == ParseMode.ReportErrors)
639 ctx.Report.Error (-25, "Detection Parsing Error");
640 partial_input = false;
641 return null;
644 if (kind == InputKind.EOF){
645 if (mode == ParseMode.ReportErrors)
646 Console.Error.WriteLine ("Internal error: EOF condition should have been detected in a previous call with silent=true");
647 partial_input = true;
648 return null;
651 seekable.Position = 0;
653 CSharpParser parser = new CSharpParser (seekable, (CompilationUnit) Location.SourceFiles [0], ctx);
655 if (kind == InputKind.StatementOrExpression){
656 parser.Lexer.putback_char = Tokenizer.EvalStatementParserCharacter;
657 RootContext.StatementMode = true;
658 } else {
660 // Do not activate EvalCompilationUnitParserCharacter until
661 // I have figured out all the limitations to invoke methods
662 // in the generated classes. See repl.txt
664 parser.Lexer.putback_char = Tokenizer.EvalUsingDeclarationsParserCharacter;
665 //parser.Lexer.putback_char = Tokenizer.EvalCompilationUnitParserCharacter;
666 RootContext.StatementMode = false;
669 if (mode == ParseMode.GetCompletions)
670 parser.Lexer.CompleteOnEOF = true;
672 ReportPrinter old_printer = null;
673 if ((mode == ParseMode.Silent || mode == ParseMode.GetCompletions) && CSharpParser.yacc_verbose_flag == 0)
674 old_printer = SetPrinter (new StreamReportPrinter (TextWriter.Null));
676 try {
677 parser.parse ();
678 } finally {
679 if (ctx.Report.Errors != 0){
680 if (mode != ParseMode.ReportErrors && parser.UnexpectedEOF)
681 partial_input = true;
683 parser.undo.ExecuteUndo ();
684 parser = null;
687 if (old_printer != null)
688 SetPrinter (old_printer);
690 return parser;
694 // Queue all the fields that we use, as we need to then go from FieldBuilder to FieldInfo
695 // or reflection gets confused (it basically gets confused, and variables override each
696 // other).
698 static List<Field> queued_fields = new List<Field> ();
700 //static ArrayList types = new ArrayList ();
702 static volatile bool invoking;
704 static CompiledMethod CompileBlock (Class host, Undo undo, Report Report)
706 RootContext.ResolveTree ();
707 if (Report.Errors != 0){
708 undo.ExecuteUndo ();
709 return null;
712 RootContext.PopulateTypes ();
714 if (Report.Errors != 0){
715 undo.ExecuteUndo ();
716 return null;
719 TypeBuilder tb = null;
720 MethodBuilder mb = null;
722 if (host != null){
723 tb = host.TypeBuilder;
724 mb = null;
725 foreach (MemberCore member in host.Methods){
726 if (member.Name != "Host")
727 continue;
729 MethodOrOperator method = (MethodOrOperator) member;
730 mb = method.MethodBuilder;
731 break;
734 if (mb == null)
735 throw new Exception ("Internal error: did not find the method builder for the generated method");
738 RootContext.EmitCode ();
739 if (Report.Errors != 0){
740 undo.ExecuteUndo ();
741 return null;
744 RootContext.CloseTypes ();
746 if (Environment.GetEnvironmentVariable ("SAVE") != null)
747 CodeGen.Save (current_debug_name, false, Report);
749 if (host == null)
750 return null;
753 // Unlike Mono, .NET requires that the MethodInfo is fetched, it cant
754 // work from MethodBuilders. Retarded, I know.
756 var tt = CodeGen.Assembly.Builder.GetType (tb.Name);
757 MethodInfo mi = tt.GetMethod (mb.Name);
759 // Pull the FieldInfos from the type, and keep track of them
760 foreach (Field field in queued_fields){
761 FieldInfo fi = tt.GetField (field.Name);
763 Tuple<FieldSpec, FieldInfo> old;
765 // If a previous value was set, nullify it, so that we do
766 // not leak memory
767 if (fields.TryGetValue (field.Name, out old)) {
768 if (old.Item1.MemberType.IsStruct) {
770 // TODO: Clear fields for structs
772 } else {
773 try {
774 old.Item2.SetValue (null, null);
775 } catch {
779 fields [field.Name] = Tuple.Create (old.Item1, fi);
780 } else {
781 fields.Add (field.Name, Tuple.Create (field.Spec, fi));
784 //types.Add (tb);
786 queued_fields.Clear ();
788 return (CompiledMethod) System.Delegate.CreateDelegate (typeof (CompiledMethod), mi);
791 static internal void LoadAliases (NamespaceEntry ns)
793 ns.Populate (using_alias_list, using_list);
796 /// <summary>
797 /// A sentinel value used to indicate that no value was
798 /// was set by the compiled function. This is used to
799 /// differentiate between a function not returning a
800 /// value and null.
801 /// </summary>
802 public class NoValueSet {
805 static internal Tuple<FieldSpec, FieldInfo> LookupField (string name)
807 Tuple<FieldSpec, FieldInfo> fi;
808 fields.TryGetValue (name, out fi);
809 return fi;
813 // Puts the FieldBuilder into a queue of names that will be
814 // registered. We can not register FieldBuilders directly
815 // we need to fetch the FieldInfo after Reflection cooks the
816 // types, or bad things happen (bad means: FieldBuilders behave
817 // incorrectly across multiple assemblies, causing assignments to
818 // invalid areas
820 // This also serves for the parser to register Field classes
821 // that should be exposed as global variables
823 static internal void QueueField (Field f)
825 queued_fields.Add (f);
828 static string Quote (string s)
830 if (s.IndexOf ('"') != -1)
831 s = s.Replace ("\"", "\\\"");
833 return "\"" + s + "\"";
836 static public string GetUsing ()
838 lock (evaluator_lock){
839 StringBuilder sb = new StringBuilder ();
841 foreach (object x in using_alias_list)
842 sb.Append (String.Format ("using {0};\n", x));
844 foreach (object x in using_list)
845 sb.Append (String.Format ("using {0};\n", x));
847 return sb.ToString ();
851 static internal ICollection<string> GetUsingList ()
853 var res = new List<string> (using_list.Count);
854 foreach (object ue in using_list)
855 res.Add (ue.ToString ());
856 return res;
859 static internal string [] GetVarNames ()
861 lock (evaluator_lock){
862 return new List<string> (fields.Keys).ToArray ();
866 static public string GetVars ()
868 lock (evaluator_lock){
869 StringBuilder sb = new StringBuilder ();
871 foreach (var de in fields){
872 var fi = LookupField (de.Key);
873 object value;
874 try {
875 value = fi.Item2.GetValue (null);
876 if (value is string)
877 value = Quote ((string)value);
878 } catch {
879 value = "<error reading value>";
882 sb.AppendFormat ("{0} {1} = {2}", fi.Item1.MemberType.GetSignatureForError (), de.Key, value);
883 sb.AppendLine ();
886 return sb.ToString ();
890 /// <summary>
891 /// Loads the given assembly and exposes the API to the user.
892 /// </summary>
893 static public void LoadAssembly (string file)
895 lock (evaluator_lock){
896 driver.LoadAssembly (file, false);
897 GlobalRootNamespace.Instance.ComputeNamespaces (ctx);
901 /// <summary>
902 /// Exposes the API of the given assembly to the Evaluator
903 /// </summary>
904 static public void ReferenceAssembly (Assembly a)
906 lock (evaluator_lock){
907 // GlobalRootNamespace.Instance.AddAssemblyReference (a);
908 // GlobalRootNamespace.Instance.ComputeNamespaces (ctx);
909 GlobalRootNamespace.Instance.ImportAssembly (a);
913 /// <summary>
914 /// If true, turns type expressions into valid expressions
915 /// and calls the describe method on it
916 /// </summary>
917 public static bool DescribeTypeExpressions;
921 /// <summary>
922 /// A delegate that can be used to invoke the
923 /// compiled expression or statement.
924 /// </summary>
925 /// <remarks>
926 /// Since the Compile methods will compile
927 /// statements and expressions into the same
928 /// delegate, you can tell if a value was returned
929 /// by checking whether the returned value is of type
930 /// NoValueSet.
931 /// </remarks>
933 public delegate void CompiledMethod (ref object retvalue);
935 /// <summary>
936 /// The default base class for every interaction line
937 /// </summary>
938 /// <remarks>
939 /// The expressions and statements behave as if they were
940 /// a static method of this class. The InteractiveBase class
941 /// contains a number of useful methods, but can be overwritten
942 /// by setting the InteractiveBaseType property in the Evaluator
943 /// </remarks>
944 public class InteractiveBase {
945 /// <summary>
946 /// Determines where the standard output of methods in this class will go.
947 /// </summary>
948 public static TextWriter Output = Console.Out;
950 /// <summary>
951 /// Determines where the standard error of methods in this class will go.
952 /// </summary>
953 public static TextWriter Error = Console.Error;
955 /// <summary>
956 /// The primary prompt used for interactive use.
957 /// </summary>
958 public static string Prompt = "csharp> ";
960 /// <summary>
961 /// The secondary prompt used for interactive use (used when
962 /// an expression is incomplete).
963 /// </summary>
964 public static string ContinuationPrompt = " > ";
966 /// <summary>
967 /// Used to signal that the user has invoked the `quit' statement.
968 /// </summary>
969 public static bool QuitRequested;
971 /// <summary>
972 /// Shows all the variables defined so far.
973 /// </summary>
974 static public void ShowVars ()
976 Output.Write (Evaluator.GetVars ());
977 Output.Flush ();
980 /// <summary>
981 /// Displays the using statements in effect at this point.
982 /// </summary>
983 static public void ShowUsing ()
985 Output.Write (Evaluator.GetUsing ());
986 Output.Flush ();
989 public delegate void Simple ();
991 /// <summary>
992 /// Times the execution of the given delegate
993 /// </summary>
994 static public TimeSpan Time (Simple a)
996 DateTime start = DateTime.Now;
997 a ();
998 return DateTime.Now - start;
1001 #if !SMCS_SOURCE
1002 /// <summary>
1003 /// Loads the assemblies from a package
1004 /// </summary>
1005 /// <remarks>
1006 /// Loads the assemblies from a package. This is equivalent
1007 /// to passing the -pkg: command line flag to the C# compiler
1008 /// on the command line.
1009 /// </remarks>
1010 static public void LoadPackage (string pkg)
1012 if (pkg == null){
1013 Error.WriteLine ("Invalid package specified");
1014 return;
1017 string pkgout = Driver.GetPackageFlags (pkg, false, RootContext.ToplevelTypes.Compiler.Report);
1018 if (pkgout == null)
1019 return;
1021 string [] xargs = pkgout.Trim (new Char [] {' ', '\n', '\r', '\t'}).
1022 Split (new Char [] { ' ', '\t'});
1024 foreach (string s in xargs){
1025 if (s.StartsWith ("-r:") || s.StartsWith ("/r:") || s.StartsWith ("/reference:")){
1026 string lib = s.Substring (s.IndexOf (':')+1);
1028 Evaluator.LoadAssembly (lib);
1029 continue;
1033 #endif
1035 /// <summary>
1036 /// Loads the assembly
1037 /// </summary>
1038 /// <remarks>
1039 /// Loads the specified assembly and makes its types
1040 /// available to the evaluator. This is equivalent
1041 /// to passing the -pkg: command line flag to the C#
1042 /// compiler on the command line.
1043 /// </remarks>
1044 static public void LoadAssembly (string assembly)
1046 Evaluator.LoadAssembly (assembly);
1049 /// <summary>
1050 /// Returns a list of available static methods.
1051 /// </summary>
1052 static public string help {
1053 get {
1054 return "Static methods:\n" +
1055 " Describe (object) - Describes the object's type\n" +
1056 " LoadPackage (package); - Loads the given Package (like -pkg:FILE)\n" +
1057 " LoadAssembly (assembly) - Loads the given assembly (like -r:ASSEMBLY)\n" +
1058 " ShowVars (); - Shows defined local variables.\n" +
1059 " ShowUsing (); - Show active using declarations.\n" +
1060 " Prompt - The prompt used by the C# shell\n" +
1061 " ContinuationPrompt - The prompt for partial input\n" +
1062 " Time(() -> { }) - Times the specified code\n" +
1063 " quit; - You'll never believe it - this quits the repl!\n" +
1064 " help; - This help text\n";
1068 /// <summary>
1069 /// Indicates to the read-eval-print-loop that the interaction should be finished.
1070 /// </summary>
1071 static public object quit {
1072 get {
1073 QuitRequested = true;
1074 return null;
1078 #if !NET_2_1
1079 /// <summary>
1080 /// Describes an object or a type.
1081 /// </summary>
1082 /// <remarks>
1083 /// This method will show a textual representation
1084 /// of the object's type. If the object is a
1085 /// System.Type it renders the type directly,
1086 /// otherwise it renders the type returned by
1087 /// invoking GetType on the object.
1088 /// </remarks>
1089 static public string Describe (object x)
1091 if (x == null)
1092 return "<null>";
1094 var type = x as Type ?? x.GetType ();
1096 StringWriter sw = new StringWriter ();
1097 new Outline (type, sw, true, false, false).OutlineType ();
1098 return sw.ToString ();
1100 #endif
1104 // A local variable reference that will create a Field in a
1105 // Class with the resolved type. This is necessary so we can
1106 // support "var" as a field type in a class declaration.
1108 // We allow LocalVariableReferece to do the heavy lifting, and
1109 // then we insert the field with the resolved type
1111 public class LocalVariableReferenceWithClassSideEffect : LocalVariableReference {
1112 TypeContainer container;
1113 string name;
1115 public LocalVariableReferenceWithClassSideEffect (TypeContainer container, string name, Block current_block, string local_variable_id, LocalInfo li, Location loc)
1116 : base (current_block, local_variable_id, loc, li, false)
1118 this.container = container;
1119 this.name = name;
1122 public override bool Equals (object obj)
1124 LocalVariableReferenceWithClassSideEffect lvr = obj as LocalVariableReferenceWithClassSideEffect;
1125 if (lvr == null)
1126 return false;
1128 if (lvr.name != name || lvr.container != container)
1129 return false;
1131 return base.Equals (obj);
1134 public override int GetHashCode ()
1136 return name.GetHashCode ();
1139 override public Expression DoResolveLValue (ResolveContext ec, Expression right_side)
1141 Expression ret = base.DoResolveLValue (ec, right_side);
1142 if (ret == null)
1143 return null;
1145 Field f = new Field (container, new TypeExpression (ret.Type, Location),
1146 Modifiers.PUBLIC | Modifiers.STATIC,
1147 new MemberName (name, Location), null);
1148 container.AddField (f);
1149 if (f.Define ())
1150 Evaluator.QueueField (f);
1152 return ret;
1156 /// <summary>
1157 /// A class used to assign values if the source expression is not void
1159 /// Used by the interactive shell to allow it to call this code to set
1160 /// the return value for an invocation.
1161 /// </summary>
1162 class OptionalAssign : SimpleAssign {
1163 public OptionalAssign (Expression t, Expression s, Location loc)
1164 : base (t, s, loc)
1168 protected override Expression DoResolve (ResolveContext ec)
1170 CloneContext cc = new CloneContext ();
1171 Expression clone = source.Clone (cc);
1174 // A useful feature for the REPL: if we can resolve the expression
1175 // as a type, Describe the type;
1177 if (Evaluator.DescribeTypeExpressions){
1178 var old_printer = Evaluator.SetPrinter (new StreamReportPrinter (TextWriter.Null));
1179 clone = clone.Resolve (ec);
1180 if (clone == null){
1181 clone = source.Clone (cc);
1182 clone = clone.Resolve (ec, ResolveFlags.Type);
1183 if (clone == null){
1184 Evaluator.SetPrinter (old_printer);
1185 clone = source.Clone (cc);
1186 clone = clone.Resolve (ec);
1187 return null;
1190 Arguments args = new Arguments (1);
1191 args.Add (new Argument (new TypeOf (source, Location)));
1192 source = new Invocation (new SimpleName ("Describe", Location), args).Resolve (ec);
1194 Evaluator.SetPrinter (old_printer);
1195 } else {
1196 clone = clone.Resolve (ec);
1197 if (clone == null)
1198 return null;
1201 // This means its really a statement.
1202 if (clone.Type == TypeManager.void_type){
1203 source = source.Resolve (ec);
1204 target = null;
1205 type = TypeManager.void_type;
1206 eclass = ExprClass.Value;
1207 return this;
1210 return base.DoResolve (ec);
1213 public override void Emit (EmitContext ec)
1215 if (target == null)
1216 source.Emit (ec);
1217 else
1218 base.Emit (ec);
1221 public override void EmitStatement (EmitContext ec)
1223 if (target == null)
1224 source.Emit (ec);
1225 else
1226 base.EmitStatement (ec);
1230 public class Undo {
1231 List<KeyValuePair<TypeContainer, TypeContainer>> undo_types;
1233 public Undo ()
1235 undo_types = new List<KeyValuePair<TypeContainer, TypeContainer>> ();
1238 public void AddTypeContainer (TypeContainer current_container, TypeContainer tc)
1240 if (current_container == tc){
1241 Console.Error.WriteLine ("Internal error: inserting container into itself");
1242 return;
1245 if (undo_types == null)
1246 undo_types = new List<KeyValuePair<TypeContainer, TypeContainer>> ();
1248 undo_types.Add (new KeyValuePair<TypeContainer, TypeContainer> (current_container, tc));
1251 public void ExecuteUndo ()
1253 if (undo_types == null)
1254 return;
1256 foreach (var p in undo_types){
1257 TypeContainer current_container = p.Key;
1259 current_container.RemoveTypeContainer (p.Value);
1261 undo_types = null;