2010-06-21 Atsushi Enomoto <atsushi@ximian.com>
[mcs.git] / mcs / eval.cs
blob6782d573a406830981cd1b06a90daff32eae586c
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 ();
132 Import.Initialize ();
133 driver.LoadReferences ();
134 TypeManager.InitOptionalCoreTypes (ctx);
136 RootContext.EvalMode = true;
137 inited = true;
139 return startup_files.ToArray ();
143 static void Init ()
145 Init (new string [0]);
148 static void Reset ()
150 CompilerCallableEntryPoint.PartialReset ();
151 RootContext.PartialReset ();
153 // Workaround for API limitation where full message printer cannot be passed
154 ReportPrinter printer;
155 if (MessageOutput == Console.Out || MessageOutput == Console.Error){
156 var console_reporter = new ConsoleReportPrinter (MessageOutput);
157 console_reporter.Fatal = driver.fatal_errors;
158 printer = console_reporter;
159 } else
160 printer = new StreamReportPrinter (MessageOutput);
162 ctx = new CompilerContext (new Report (printer));
163 RootContext.ToplevelTypes = new ModuleCompiled (ctx, true);
166 // PartialReset should not reset the core types, this is very redundant.
168 // if (!TypeManager.InitCoreTypes (ctx, null))
169 // throw new Exception ("Failed to InitCoreTypes");
170 // TypeManager.InitOptionalCoreTypes (ctx);
172 Location.AddFile (null, "{interactive}");
173 Location.Initialize ();
175 current_debug_name = "interactive" + (count++) + ".dll";
176 if (Environment.GetEnvironmentVariable ("SAVE") != null){
177 CodeGen.Init (current_debug_name, current_debug_name, false, ctx);
178 } else
179 CodeGen.InitDynamic (ctx, current_debug_name);
182 /// <summary>
183 /// The base class for the classes that host the user generated code
184 /// </summary>
185 /// <remarks>
187 /// This is the base class that will host the code
188 /// executed by the Evaluator. By default
189 /// this is the Mono.CSharp.InteractiveBase class
190 /// which is useful for interactive use.
192 /// By changing this property you can control the
193 /// base class and the static members that are
194 /// available to your evaluated code.
195 /// </remarks>
196 static public TypeSpec InteractiveBaseClass {
197 get {
198 if (interactive_base_class != null)
199 return interactive_base_class;
201 return Import.ImportType (typeof (InteractiveBase));
205 public static void SetInteractiveBaseClass (Type type)
207 if (type == null)
208 throw new ArgumentNullException ();
210 lock (evaluator_lock)
211 interactive_base_class = Import.ImportType (type);
214 /// <summary>
215 /// Interrupts the evaluation of an expression executing in Evaluate.
216 /// </summary>
217 /// <remarks>
218 /// Use this method to interrupt long-running invocations.
219 /// </remarks>
220 public static void Interrupt ()
222 if (!inited || !invoking)
223 return;
225 if (invoke_thread != null)
226 invoke_thread.Abort ();
229 /// <summary>
230 /// Compiles the input string and returns a delegate that represents the compiled code.
231 /// </summary>
232 /// <remarks>
234 /// Compiles the input string as a C# expression or
235 /// statement, unlike the Evaluate method, the
236 /// resulting delegate can be invoked multiple times
237 /// without incurring in the compilation overhead.
239 /// If the return value of this function is null,
240 /// this indicates that the parsing was complete.
241 /// If the return value is a string it indicates
242 /// that the input string was partial and that the
243 /// invoking code should provide more code before
244 /// the code can be successfully compiled.
246 /// If you know that you will always get full expressions or
247 /// statements and do not care about partial input, you can use
248 /// the other Compile overload.
250 /// On success, in addition to returning null, the
251 /// compiled parameter will be set to the delegate
252 /// that can be invoked to execute the code.
254 /// </remarks>
255 static public string Compile (string input, out CompiledMethod compiled)
257 if (input == null || input.Length == 0){
258 compiled = null;
259 return null;
262 lock (evaluator_lock){
263 if (!inited)
264 Init ();
266 bool partial_input;
267 CSharpParser parser = ParseString (ParseMode.Silent, input, out partial_input);
268 if (parser == null){
269 compiled = null;
270 if (partial_input)
271 return input;
273 ParseString (ParseMode.ReportErrors, input, out partial_input);
274 return null;
277 object parser_result = parser.InteractiveResult;
279 if (!(parser_result is Class)){
280 int errors = ctx.Report.Errors;
282 NamespaceEntry.VerifyAllUsing ();
283 if (errors == ctx.Report.Errors)
284 parser.CurrentNamespace.Extract (using_alias_list, using_list);
287 compiled = CompileBlock (parser_result as Class, parser.undo, ctx.Report);
290 return null;
293 /// <summary>
294 /// Compiles the input string and returns a delegate that represents the compiled code.
295 /// </summary>
296 /// <remarks>
298 /// Compiles the input string as a C# expression or
299 /// statement, unlike the Evaluate method, the
300 /// resulting delegate can be invoked multiple times
301 /// without incurring in the compilation overhead.
303 /// This method can only deal with fully formed input
304 /// strings and does not provide a completion mechanism.
305 /// If you must deal with partial input (for example for
306 /// interactive use) use the other overload.
308 /// On success, a delegate is returned that can be used
309 /// to invoke the method.
311 /// </remarks>
312 static public CompiledMethod Compile (string input)
314 CompiledMethod compiled;
316 // Ignore partial inputs
317 if (Compile (input, out compiled) != null){
318 // Error, the input was partial.
319 return null;
322 // Either null (on error) or the compiled method.
323 return compiled;
327 // Todo: Should we handle errors, or expect the calling code to setup
328 // the recording themselves?
331 /// <summary>
332 /// Evaluates and expression or statement and returns any result values.
333 /// </summary>
334 /// <remarks>
335 /// Evaluates the input string as a C# expression or
336 /// statement. If the input string is an expression
337 /// the result will be stored in the result variable
338 /// and the result_set variable will be set to true.
340 /// It is necessary to use the result/result_set
341 /// pair to identify when a result was set (for
342 /// example, execution of user-provided input can be
343 /// an expression, a statement or others, and
344 /// result_set would only be set if the input was an
345 /// expression.
347 /// If the return value of this function is null,
348 /// this indicates that the parsing was complete.
349 /// If the return value is a string, it indicates
350 /// that the input is partial and that the user
351 /// should provide an updated string.
352 /// </remarks>
353 public static string Evaluate (string input, out object result, out bool result_set)
355 CompiledMethod compiled;
357 result_set = false;
358 result = null;
360 input = Compile (input, out compiled);
361 if (input != null)
362 return input;
364 if (compiled == null)
365 return null;
368 // The code execution does not need to keep the compiler lock
370 object retval = typeof (NoValueSet);
372 try {
373 invoke_thread = System.Threading.Thread.CurrentThread;
374 invoking = true;
375 compiled (ref retval);
376 } catch (ThreadAbortException e){
377 Thread.ResetAbort ();
378 Console.WriteLine ("Interrupted!\n{0}", e);
379 } finally {
380 invoking = false;
384 // We use a reference to a compiler type, in this case
385 // Driver as a flag to indicate that this was a statement
387 if (retval != typeof (NoValueSet)){
388 result_set = true;
389 result = retval;
392 return null;
395 public static string [] GetCompletions (string input, out string prefix)
397 prefix = "";
398 if (input == null || input.Length == 0)
399 return null;
401 lock (evaluator_lock){
402 if (!inited)
403 Init ();
405 bool partial_input;
406 CSharpParser parser = ParseString (ParseMode.GetCompletions, input, out partial_input);
407 if (parser == null){
408 if (CSharpParser.yacc_verbose_flag != 0)
409 Console.WriteLine ("DEBUG: No completions available");
410 return null;
413 Class parser_result = parser.InteractiveResult as Class;
415 if (parser_result == null){
416 if (CSharpParser.yacc_verbose_flag != 0)
417 Console.WriteLine ("Do not know how to cope with !Class yet");
418 return null;
421 try {
422 RootContext.ResolveTree ();
423 if (ctx.Report.Errors != 0)
424 return null;
426 RootContext.PopulateTypes ();
427 if (ctx.Report.Errors != 0)
428 return null;
430 MethodOrOperator method = null;
431 foreach (MemberCore member in parser_result.Methods){
432 if (member.Name != "Host")
433 continue;
435 method = (MethodOrOperator) member;
436 break;
438 if (method == null)
439 throw new InternalErrorException ("did not find the the Host method");
441 BlockContext bc = new BlockContext (method, method.Block, method.ReturnType);
443 try {
444 method.Block.Resolve (null, bc, method.ParameterInfo, method);
445 } catch (CompletionResult cr){
446 prefix = cr.BaseText;
447 return cr.Result;
449 } finally {
450 parser.undo.ExecuteUndo ();
454 return null;
457 /// <summary>
458 /// Executes the given expression or statement.
459 /// </summary>
460 /// <remarks>
461 /// Executes the provided statement, returns true
462 /// on success, false on parsing errors. Exceptions
463 /// might be thrown by the called code.
464 /// </remarks>
465 public static bool Run (string statement)
467 if (!inited)
468 Init ();
470 object result;
471 bool result_set;
473 bool ok = Evaluate (statement, out result, out result_set) == null;
475 return ok;
478 /// <summary>
479 /// Evaluates and expression or statement and returns the result.
480 /// </summary>
481 /// <remarks>
482 /// Evaluates the input string as a C# expression or
483 /// statement and returns the value.
485 /// This method will throw an exception if there is a syntax error,
486 /// of if the provided input is not an expression but a statement.
487 /// </remarks>
488 public static object Evaluate (string input)
490 object result;
491 bool result_set;
493 string r = Evaluate (input, out result, out result_set);
495 if (r != null)
496 throw new ArgumentException ("Syntax error on input: partial input");
498 if (result_set == false)
499 throw new ArgumentException ("The expression did not set a result");
501 return result;
504 enum InputKind {
505 EOF,
506 StatementOrExpression,
507 CompilationUnit,
508 Error
512 // Deambiguates the input string to determine if we
513 // want to process a statement or if we want to
514 // process a compilation unit.
516 // This is done using a top-down predictive parser,
517 // since the yacc/jay parser can not deambiguage this
518 // without more than one lookahead token. There are very
519 // few ambiguities.
521 static InputKind ToplevelOrStatement (SeekableStreamReader seekable)
523 Tokenizer tokenizer = new Tokenizer (seekable, (CompilationUnit) Location.SourceFiles [0], ctx);
525 int t = tokenizer.token ();
526 switch (t){
527 case Token.EOF:
528 return InputKind.EOF;
530 // These are toplevels
531 case Token.EXTERN:
532 case Token.OPEN_BRACKET:
533 case Token.ABSTRACT:
534 case Token.CLASS:
535 case Token.ENUM:
536 case Token.INTERFACE:
537 case Token.INTERNAL:
538 case Token.NAMESPACE:
539 case Token.PRIVATE:
540 case Token.PROTECTED:
541 case Token.PUBLIC:
542 case Token.SEALED:
543 case Token.STATIC:
544 case Token.STRUCT:
545 return InputKind.CompilationUnit;
547 // Definitely expression
548 case Token.FIXED:
549 case Token.BOOL:
550 case Token.BYTE:
551 case Token.CHAR:
552 case Token.DECIMAL:
553 case Token.DOUBLE:
554 case Token.FLOAT:
555 case Token.INT:
556 case Token.LONG:
557 case Token.NEW:
558 case Token.OBJECT:
559 case Token.SBYTE:
560 case Token.SHORT:
561 case Token.STRING:
562 case Token.UINT:
563 case Token.ULONG:
564 return InputKind.StatementOrExpression;
566 // These need deambiguation help
567 case Token.USING:
568 t = tokenizer.token ();
569 if (t == Token.EOF)
570 return InputKind.EOF;
572 if (t == Token.IDENTIFIER)
573 return InputKind.CompilationUnit;
574 return InputKind.StatementOrExpression;
577 // Distinguish between:
578 // delegate opt_anonymous_method_signature block
579 // delegate type
580 case Token.DELEGATE:
581 t = tokenizer.token ();
582 if (t == Token.EOF)
583 return InputKind.EOF;
584 if (t == Token.OPEN_PARENS || t == Token.OPEN_BRACE)
585 return InputKind.StatementOrExpression;
586 return InputKind.CompilationUnit;
588 // Distinguih between:
589 // unsafe block
590 // unsafe as modifier of a type declaration
591 case Token.UNSAFE:
592 t = tokenizer.token ();
593 if (t == Token.EOF)
594 return InputKind.EOF;
595 if (t == Token.OPEN_PARENS)
596 return InputKind.StatementOrExpression;
597 return InputKind.CompilationUnit;
599 // These are errors: we list explicitly what we had
600 // from the grammar, ERROR and then everything else
602 case Token.READONLY:
603 case Token.OVERRIDE:
604 case Token.ERROR:
605 return InputKind.Error;
607 // This catches everything else allowed by
608 // expressions. We could add one-by-one use cases
609 // if needed.
610 default:
611 return InputKind.StatementOrExpression;
616 // Parses the string @input and returns a CSharpParser if succeeful.
618 // if @silent is set to true then no errors are
619 // reported to the user. This is used to do various calls to the
620 // parser and check if the expression is parsable.
622 // @partial_input: if @silent is true, then it returns whether the
623 // parsed expression was partial, and more data is needed
625 static CSharpParser ParseString (ParseMode mode, string input, out bool partial_input)
627 partial_input = false;
628 Reset ();
629 queued_fields.Clear ();
630 Tokenizer.LocatedToken.Initialize ();
632 Stream s = new MemoryStream (Encoding.Default.GetBytes (input));
633 SeekableStreamReader seekable = new SeekableStreamReader (s, Encoding.Default);
635 InputKind kind = ToplevelOrStatement (seekable);
636 if (kind == InputKind.Error){
637 if (mode == ParseMode.ReportErrors)
638 ctx.Report.Error (-25, "Detection Parsing Error");
639 partial_input = false;
640 return null;
643 if (kind == InputKind.EOF){
644 if (mode == ParseMode.ReportErrors)
645 Console.Error.WriteLine ("Internal error: EOF condition should have been detected in a previous call with silent=true");
646 partial_input = true;
647 return null;
650 seekable.Position = 0;
652 CSharpParser parser = new CSharpParser (seekable, (CompilationUnit) Location.SourceFiles [0], ctx);
654 if (kind == InputKind.StatementOrExpression){
655 parser.Lexer.putback_char = Tokenizer.EvalStatementParserCharacter;
656 RootContext.StatementMode = true;
657 } else {
659 // Do not activate EvalCompilationUnitParserCharacter until
660 // I have figured out all the limitations to invoke methods
661 // in the generated classes. See repl.txt
663 parser.Lexer.putback_char = Tokenizer.EvalUsingDeclarationsParserCharacter;
664 //parser.Lexer.putback_char = Tokenizer.EvalCompilationUnitParserCharacter;
665 RootContext.StatementMode = false;
668 if (mode == ParseMode.GetCompletions)
669 parser.Lexer.CompleteOnEOF = true;
671 ReportPrinter old_printer = null;
672 if ((mode == ParseMode.Silent || mode == ParseMode.GetCompletions) && CSharpParser.yacc_verbose_flag == 0)
673 old_printer = SetPrinter (new StreamReportPrinter (TextWriter.Null));
675 try {
676 parser.parse ();
677 } finally {
678 if (ctx.Report.Errors != 0){
679 if (mode != ParseMode.ReportErrors && parser.UnexpectedEOF)
680 partial_input = true;
682 parser.undo.ExecuteUndo ();
683 parser = null;
686 if (old_printer != null)
687 SetPrinter (old_printer);
689 return parser;
693 // Queue all the fields that we use, as we need to then go from FieldBuilder to FieldInfo
694 // or reflection gets confused (it basically gets confused, and variables override each
695 // other).
697 static List<Field> queued_fields = new List<Field> ();
699 //static ArrayList types = new ArrayList ();
701 static volatile bool invoking;
703 static CompiledMethod CompileBlock (Class host, Undo undo, Report Report)
705 RootContext.ResolveTree ();
706 if (Report.Errors != 0){
707 undo.ExecuteUndo ();
708 return null;
711 RootContext.PopulateTypes ();
713 if (Report.Errors != 0){
714 undo.ExecuteUndo ();
715 return null;
718 TypeBuilder tb = null;
719 MethodBuilder mb = null;
721 if (host != null){
722 tb = host.TypeBuilder;
723 mb = null;
724 foreach (MemberCore member in host.Methods){
725 if (member.Name != "Host")
726 continue;
728 MethodOrOperator method = (MethodOrOperator) member;
729 mb = method.MethodBuilder;
730 break;
733 if (mb == null)
734 throw new Exception ("Internal error: did not find the method builder for the generated method");
737 RootContext.EmitCode ();
738 if (Report.Errors != 0){
739 undo.ExecuteUndo ();
740 return null;
743 RootContext.CloseTypes ();
745 if (Environment.GetEnvironmentVariable ("SAVE") != null)
746 CodeGen.Save (current_debug_name, false, Report);
748 if (host == null)
749 return null;
752 // Unlike Mono, .NET requires that the MethodInfo is fetched, it cant
753 // work from MethodBuilders. Retarded, I know.
755 var tt = CodeGen.Assembly.Builder.GetType (tb.Name);
756 MethodInfo mi = tt.GetMethod (mb.Name);
758 // Pull the FieldInfos from the type, and keep track of them
759 foreach (Field field in queued_fields){
760 FieldInfo fi = tt.GetField (field.Name);
762 Tuple<FieldSpec, FieldInfo> old;
764 // If a previous value was set, nullify it, so that we do
765 // not leak memory
766 if (fields.TryGetValue (field.Name, out old)) {
767 if (old.Item1.MemberType.IsStruct) {
769 // TODO: Clear fields for structs
771 } else {
772 try {
773 old.Item2.SetValue (null, null);
774 } catch {
778 fields [field.Name] = Tuple.Create (old.Item1, fi);
779 } else {
780 fields.Add (field.Name, Tuple.Create (field.Spec, fi));
783 //types.Add (tb);
785 queued_fields.Clear ();
787 return (CompiledMethod) System.Delegate.CreateDelegate (typeof (CompiledMethod), mi);
790 static internal void LoadAliases (NamespaceEntry ns)
792 ns.Populate (using_alias_list, using_list);
795 /// <summary>
796 /// A sentinel value used to indicate that no value was
797 /// was set by the compiled function. This is used to
798 /// differentiate between a function not returning a
799 /// value and null.
800 /// </summary>
801 public class NoValueSet {
804 static internal Tuple<FieldSpec, FieldInfo> LookupField (string name)
806 Tuple<FieldSpec, FieldInfo> fi;
807 fields.TryGetValue (name, out fi);
808 return fi;
812 // Puts the FieldBuilder into a queue of names that will be
813 // registered. We can not register FieldBuilders directly
814 // we need to fetch the FieldInfo after Reflection cooks the
815 // types, or bad things happen (bad means: FieldBuilders behave
816 // incorrectly across multiple assemblies, causing assignments to
817 // invalid areas
819 // This also serves for the parser to register Field classes
820 // that should be exposed as global variables
822 static internal void QueueField (Field f)
824 queued_fields.Add (f);
827 static string Quote (string s)
829 if (s.IndexOf ('"') != -1)
830 s = s.Replace ("\"", "\\\"");
832 return "\"" + s + "\"";
835 static public string GetUsing ()
837 lock (evaluator_lock){
838 StringBuilder sb = new StringBuilder ();
840 foreach (object x in using_alias_list)
841 sb.Append (String.Format ("using {0};\n", x));
843 foreach (object x in using_list)
844 sb.Append (String.Format ("using {0};\n", x));
846 return sb.ToString ();
850 static internal ICollection<string> GetUsingList ()
852 var res = new List<string> (using_list.Count);
853 foreach (object ue in using_list)
854 res.Add (ue.ToString ());
855 return res;
858 static internal string [] GetVarNames ()
860 lock (evaluator_lock){
861 return new List<string> (fields.Keys).ToArray ();
865 static public string GetVars ()
867 lock (evaluator_lock){
868 StringBuilder sb = new StringBuilder ();
870 foreach (var de in fields){
871 var fi = LookupField (de.Key);
872 object value;
873 try {
874 value = fi.Item2.GetValue (null);
875 if (value is string)
876 value = Quote ((string)value);
877 } catch {
878 value = "<error reading value>";
881 sb.AppendFormat ("{0} {1} = {2}", fi.Item1.MemberType.GetSignatureForError (), de.Key, value);
882 sb.AppendLine ();
885 return sb.ToString ();
889 /// <summary>
890 /// Loads the given assembly and exposes the API to the user.
891 /// </summary>
892 static public void LoadAssembly (string file)
894 lock (evaluator_lock){
895 driver.LoadAssembly (file, false);
896 GlobalRootNamespace.Instance.ComputeNamespaces (ctx);
900 /// <summary>
901 /// Exposes the API of the given assembly to the Evaluator
902 /// </summary>
903 static public void ReferenceAssembly (Assembly a)
905 lock (evaluator_lock){
906 // GlobalRootNamespace.Instance.AddAssemblyReference (a);
907 // GlobalRootNamespace.Instance.ComputeNamespaces (ctx);
908 GlobalRootNamespace.Instance.ImportAssembly (a);
912 /// <summary>
913 /// If true, turns type expressions into valid expressions
914 /// and calls the describe method on it
915 /// </summary>
916 public static bool DescribeTypeExpressions;
920 /// <summary>
921 /// A delegate that can be used to invoke the
922 /// compiled expression or statement.
923 /// </summary>
924 /// <remarks>
925 /// Since the Compile methods will compile
926 /// statements and expressions into the same
927 /// delegate, you can tell if a value was returned
928 /// by checking whether the returned value is of type
929 /// NoValueSet.
930 /// </remarks>
932 public delegate void CompiledMethod (ref object retvalue);
934 /// <summary>
935 /// The default base class for every interaction line
936 /// </summary>
937 /// <remarks>
938 /// The expressions and statements behave as if they were
939 /// a static method of this class. The InteractiveBase class
940 /// contains a number of useful methods, but can be overwritten
941 /// by setting the InteractiveBaseType property in the Evaluator
942 /// </remarks>
943 public class InteractiveBase {
944 /// <summary>
945 /// Determines where the standard output of methods in this class will go.
946 /// </summary>
947 public static TextWriter Output = Console.Out;
949 /// <summary>
950 /// Determines where the standard error of methods in this class will go.
951 /// </summary>
952 public static TextWriter Error = Console.Error;
954 /// <summary>
955 /// The primary prompt used for interactive use.
956 /// </summary>
957 public static string Prompt = "csharp> ";
959 /// <summary>
960 /// The secondary prompt used for interactive use (used when
961 /// an expression is incomplete).
962 /// </summary>
963 public static string ContinuationPrompt = " > ";
965 /// <summary>
966 /// Used to signal that the user has invoked the `quit' statement.
967 /// </summary>
968 public static bool QuitRequested;
970 /// <summary>
971 /// Shows all the variables defined so far.
972 /// </summary>
973 static public void ShowVars ()
975 Output.Write (Evaluator.GetVars ());
976 Output.Flush ();
979 /// <summary>
980 /// Displays the using statements in effect at this point.
981 /// </summary>
982 static public void ShowUsing ()
984 Output.Write (Evaluator.GetUsing ());
985 Output.Flush ();
988 public delegate void Simple ();
990 /// <summary>
991 /// Times the execution of the given delegate
992 /// </summary>
993 static public TimeSpan Time (Simple a)
995 DateTime start = DateTime.Now;
996 a ();
997 return DateTime.Now - start;
1000 #if !SMCS_SOURCE
1001 /// <summary>
1002 /// Loads the assemblies from a package
1003 /// </summary>
1004 /// <remarks>
1005 /// Loads the assemblies from a package. This is equivalent
1006 /// to passing the -pkg: command line flag to the C# compiler
1007 /// on the command line.
1008 /// </remarks>
1009 static public void LoadPackage (string pkg)
1011 if (pkg == null){
1012 Error.WriteLine ("Invalid package specified");
1013 return;
1016 string pkgout = Driver.GetPackageFlags (pkg, false, RootContext.ToplevelTypes.Compiler.Report);
1017 if (pkgout == null)
1018 return;
1020 string [] xargs = pkgout.Trim (new Char [] {' ', '\n', '\r', '\t'}).
1021 Split (new Char [] { ' ', '\t'});
1023 foreach (string s in xargs){
1024 if (s.StartsWith ("-r:") || s.StartsWith ("/r:") || s.StartsWith ("/reference:")){
1025 string lib = s.Substring (s.IndexOf (':')+1);
1027 Evaluator.LoadAssembly (lib);
1028 continue;
1032 #endif
1034 /// <summary>
1035 /// Loads the assembly
1036 /// </summary>
1037 /// <remarks>
1038 /// Loads the specified assembly and makes its types
1039 /// available to the evaluator. This is equivalent
1040 /// to passing the -pkg: command line flag to the C#
1041 /// compiler on the command line.
1042 /// </remarks>
1043 static public void LoadAssembly (string assembly)
1045 Evaluator.LoadAssembly (assembly);
1048 /// <summary>
1049 /// Returns a list of available static methods.
1050 /// </summary>
1051 static public string help {
1052 get {
1053 return "Static methods:\n" +
1054 " Describe (object) - Describes the object's type\n" +
1055 " LoadPackage (package); - Loads the given Package (like -pkg:FILE)\n" +
1056 " LoadAssembly (assembly) - Loads the given assembly (like -r:ASSEMBLY)\n" +
1057 " ShowVars (); - Shows defined local variables.\n" +
1058 " ShowUsing (); - Show active using declarations.\n" +
1059 " Prompt - The prompt used by the C# shell\n" +
1060 " ContinuationPrompt - The prompt for partial input\n" +
1061 " Time(() -> { }) - Times the specified code\n" +
1062 " quit; - You'll never believe it - this quits the repl!\n" +
1063 " help; - This help text\n";
1067 /// <summary>
1068 /// Indicates to the read-eval-print-loop that the interaction should be finished.
1069 /// </summary>
1070 static public object quit {
1071 get {
1072 QuitRequested = true;
1073 return null;
1077 #if !NET_2_1
1078 /// <summary>
1079 /// Describes an object or a type.
1080 /// </summary>
1081 /// <remarks>
1082 /// This method will show a textual representation
1083 /// of the object's type. If the object is a
1084 /// System.Type it renders the type directly,
1085 /// otherwise it renders the type returned by
1086 /// invoking GetType on the object.
1087 /// </remarks>
1088 static public string Describe (object x)
1090 if (x == null)
1091 return "<null>";
1093 var type = x as Type ?? x.GetType ();
1095 StringWriter sw = new StringWriter ();
1096 new Outline (type, sw, true, false, false).OutlineType ();
1097 return sw.ToString ();
1099 #endif
1103 // A local variable reference that will create a Field in a
1104 // Class with the resolved type. This is necessary so we can
1105 // support "var" as a field type in a class declaration.
1107 // We allow LocalVariableReferece to do the heavy lifting, and
1108 // then we insert the field with the resolved type
1110 public class LocalVariableReferenceWithClassSideEffect : LocalVariableReference {
1111 TypeContainer container;
1112 string name;
1114 public LocalVariableReferenceWithClassSideEffect (TypeContainer container, string name, Block current_block, string local_variable_id, LocalInfo li, Location loc)
1115 : base (current_block, local_variable_id, loc, li, false)
1117 this.container = container;
1118 this.name = name;
1121 public override bool Equals (object obj)
1123 LocalVariableReferenceWithClassSideEffect lvr = obj as LocalVariableReferenceWithClassSideEffect;
1124 if (lvr == null)
1125 return false;
1127 if (lvr.name != name || lvr.container != container)
1128 return false;
1130 return base.Equals (obj);
1133 public override int GetHashCode ()
1135 return name.GetHashCode ();
1138 override public Expression DoResolveLValue (ResolveContext ec, Expression right_side)
1140 Expression ret = base.DoResolveLValue (ec, right_side);
1141 if (ret == null)
1142 return null;
1144 Field f = new Field (container, new TypeExpression (ret.Type, Location),
1145 Modifiers.PUBLIC | Modifiers.STATIC,
1146 new MemberName (name, Location), null);
1147 container.AddField (f);
1148 if (f.Define ())
1149 Evaluator.QueueField (f);
1151 return ret;
1155 /// <summary>
1156 /// A class used to assign values if the source expression is not void
1158 /// Used by the interactive shell to allow it to call this code to set
1159 /// the return value for an invocation.
1160 /// </summary>
1161 class OptionalAssign : SimpleAssign {
1162 public OptionalAssign (Expression t, Expression s, Location loc)
1163 : base (t, s, loc)
1167 protected override Expression DoResolve (ResolveContext ec)
1169 CloneContext cc = new CloneContext ();
1170 Expression clone = source.Clone (cc);
1173 // A useful feature for the REPL: if we can resolve the expression
1174 // as a type, Describe the type;
1176 if (Evaluator.DescribeTypeExpressions){
1177 var old_printer = Evaluator.SetPrinter (new StreamReportPrinter (TextWriter.Null));
1178 clone = clone.Resolve (ec);
1179 if (clone == null){
1180 clone = source.Clone (cc);
1181 clone = clone.Resolve (ec, ResolveFlags.Type);
1182 if (clone == null){
1183 Evaluator.SetPrinter (old_printer);
1184 clone = source.Clone (cc);
1185 clone = clone.Resolve (ec);
1186 return null;
1189 Arguments args = new Arguments (1);
1190 args.Add (new Argument (new TypeOf ((TypeExpr) clone, Location)));
1191 source = new Invocation (new SimpleName ("Describe", Location), args).Resolve (ec);
1193 Evaluator.SetPrinter (old_printer);
1194 } else {
1195 clone = clone.Resolve (ec);
1196 if (clone == null)
1197 return null;
1200 // This means its really a statement.
1201 if (clone.Type == TypeManager.void_type){
1202 source = source.Resolve (ec);
1203 target = null;
1204 type = TypeManager.void_type;
1205 eclass = ExprClass.Value;
1206 return this;
1209 return base.DoResolve (ec);
1212 public override void Emit (EmitContext ec)
1214 if (target == null)
1215 source.Emit (ec);
1216 else
1217 base.Emit (ec);
1220 public override void EmitStatement (EmitContext ec)
1222 if (target == null)
1223 source.Emit (ec);
1224 else
1225 base.EmitStatement (ec);
1229 public class Undo {
1230 List<KeyValuePair<TypeContainer, TypeContainer>> undo_types;
1232 public Undo ()
1234 undo_types = new List<KeyValuePair<TypeContainer, TypeContainer>> ();
1237 public void AddTypeContainer (TypeContainer current_container, TypeContainer tc)
1239 if (current_container == tc){
1240 Console.Error.WriteLine ("Internal error: inserting container into itself");
1241 return;
1244 if (undo_types == null)
1245 undo_types = new List<KeyValuePair<TypeContainer, TypeContainer>> ();
1247 undo_types.Add (new KeyValuePair<TypeContainer, TypeContainer> (current_container, tc));
1250 public void ExecuteUndo ()
1252 if (undo_types == null)
1253 return;
1255 foreach (var p in undo_types){
1256 TypeContainer current_container = p.Key;
1258 current_container.RemoveTypeContainer (p.Value);
1260 undo_types = null;