2 // eval.cs: Evaluation and Hosting API for the C# compiler
5 // Miguel de Icaza (miguel@gnome.org)
7 // Dual licensed under the terms of the MIT X11 or GNU GPL
9 // Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
10 // Copyright 2004, 2005, 2006, 2007, 2008 Novell, Inc
14 using System.Threading;
15 using System.Collections.Generic;
16 using System.Reflection;
17 using System.Reflection.Emit;
25 /// Evaluator: provides an API to evaluate C# statements and
26 /// expressions dynamically.
29 /// This class exposes static methods to evaluate expressions in the
32 /// To initialize the evaluator with a number of compiler
33 /// options call the Init(string[]args) method with a set of
34 /// command line options that the compiler recognizes.
36 /// To interrupt execution of a statement, you can invoke the
37 /// Evaluator.Interrupt method.
39 public class Evaluator {
42 // Parse silently, do not output any error messages
45 // Report errors during parse
48 // Auto-complete, means that the tokenizer will start producing
49 // GETCOMPLETIONS tokens when it reaches a certain point.
53 static object evaluator_lock = new object ();
55 static string current_debug_name;
57 static Thread invoke_thread;
59 static List<NamespaceEntry.UsingAliasEntry> using_alias_list = new List<NamespaceEntry.UsingAliasEntry> ();
60 internal static List<NamespaceEntry.UsingEntry> using_list = new List<NamespaceEntry.UsingEntry> ();
61 static Dictionary<string, Tuple<FieldSpec, FieldInfo>> fields = new Dictionary<string, Tuple<FieldSpec, FieldInfo>> ();
63 static TypeSpec interactive_base_class;
67 static CompilerContext ctx;
68 static DynamicLoader loader;
70 public static TextWriter MessageOutput = Console.Out;
73 /// Optional initialization for the Evaluator.
76 /// Initializes the Evaluator with the command line options
77 /// that would be processed by the command line compiler. Only
78 /// the first call to Init will work, any future invocations are
81 /// You can safely avoid calling this method if your application
82 /// does not need any of the features exposed by the command line
85 public static void Init (string [] args)
87 InitAndGetStartupFiles (args);
90 internal static ReportPrinter SetPrinter (ReportPrinter report_printer)
92 return ctx.Report.SetPrinter (report_printer);
96 /// Optional initialization for the Evaluator.
99 /// Initializes the Evaluator with the command line
100 /// options that would be processed by the command
101 /// line compiler. Only the first call to
102 /// InitAndGetStartupFiles or Init will work, any future
103 /// invocations are ignored.
105 /// You can safely avoid calling this method if your application
106 /// does not need any of the features exposed by the command line
109 /// This method return an array of strings that contains any
110 /// files that were specified in `args'.
112 public static string [] InitAndGetStartupFiles (string [] args)
114 lock (evaluator_lock){
116 return new string [0];
118 var crp = new ConsoleReportPrinter ();
119 driver = Driver.Create (args, false, crp);
121 throw new Exception ("Failed to create compiler driver with the given arguments");
123 crp.Fatal = driver.fatal_errors;
126 CompilerCallableEntryPoint.Reset ();
127 RootContext.ToplevelTypes = new ModuleContainer (ctx);
129 var startup_files = new List<string> ();
130 foreach (CompilationUnit file in Location.SourceFiles)
131 startup_files.Add (file.Path);
133 CompilerCallableEntryPoint.PartialReset ();
135 var importer = new ReflectionImporter (ctx.BuildinTypes);
136 loader = new DynamicLoader (importer, ctx);
138 RootContext.ToplevelTypes.SetDeclaringAssembly (new AssemblyDefinitionDynamic (RootContext.ToplevelTypes, "temp"));
140 loader.LoadReferences (RootContext.ToplevelTypes);
141 ctx.BuildinTypes.CheckDefinitions (RootContext.ToplevelTypes);
142 RootContext.ToplevelTypes.InitializePredefinedTypes ();
144 RootContext.EvalMode = true;
147 return startup_files.ToArray ();
153 Init (new string [0]);
158 CompilerCallableEntryPoint.PartialReset ();
160 Location.AddFile (null, "{interactive}");
161 Location.Initialize ();
163 current_debug_name = "interactive" + (count++) + ".dll";
167 /// The base class for the classes that host the user generated code
171 /// This is the base class that will host the code
172 /// executed by the Evaluator. By default
173 /// this is the Mono.CSharp.InteractiveBase class
174 /// which is useful for interactive use.
176 /// By changing this property you can control the
177 /// base class and the static members that are
178 /// available to your evaluated code.
180 static public TypeSpec InteractiveBaseClass {
182 if (interactive_base_class != null)
183 return interactive_base_class;
185 return loader.Importer.ImportType (typeof (InteractiveBase));
189 public static void SetInteractiveBaseClass (Type type)
192 throw new ArgumentNullException ();
195 throw new Exception ("Evaluator has to be initiated before seting custom InteractiveBase class");
197 lock (evaluator_lock)
198 interactive_base_class = loader.Importer.ImportType (type);
202 /// Interrupts the evaluation of an expression executing in Evaluate.
205 /// Use this method to interrupt long-running invocations.
207 public static void Interrupt ()
209 if (!inited || !invoking)
212 if (invoke_thread != null)
213 invoke_thread.Abort ();
217 /// Compiles the input string and returns a delegate that represents the compiled code.
221 /// Compiles the input string as a C# expression or
222 /// statement, unlike the Evaluate method, the
223 /// resulting delegate can be invoked multiple times
224 /// without incurring in the compilation overhead.
226 /// If the return value of this function is null,
227 /// this indicates that the parsing was complete.
228 /// If the return value is a string it indicates
229 /// that the input string was partial and that the
230 /// invoking code should provide more code before
231 /// the code can be successfully compiled.
233 /// If you know that you will always get full expressions or
234 /// statements and do not care about partial input, you can use
235 /// the other Compile overload.
237 /// On success, in addition to returning null, the
238 /// compiled parameter will be set to the delegate
239 /// that can be invoked to execute the code.
242 static public string Compile (string input, out CompiledMethod compiled)
244 if (input == null || input.Length == 0){
249 lock (evaluator_lock){
253 ctx.Report.Printer.Reset ();
255 // RootContext.ToplevelTypes = new ModuleContainer (ctx);
258 CSharpParser parser = ParseString (ParseMode.Silent, input, out partial_input);
264 ParseString (ParseMode.ReportErrors, input, out partial_input);
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);
277 NamespaceEntry.Reset ();
281 throw new NotSupportedException ();
283 compiled = CompileBlock (parser_result as Class, parser.undo, ctx.Report);
290 /// Compiles the input string and returns a delegate that represents the compiled code.
294 /// Compiles the input string as a C# expression or
295 /// statement, unlike the Evaluate method, the
296 /// resulting delegate can be invoked multiple times
297 /// without incurring in the compilation overhead.
299 /// This method can only deal with fully formed input
300 /// strings and does not provide a completion mechanism.
301 /// If you must deal with partial input (for example for
302 /// interactive use) use the other overload.
304 /// On success, a delegate is returned that can be used
305 /// to invoke the method.
308 static public CompiledMethod Compile (string input)
310 CompiledMethod compiled;
312 // Ignore partial inputs
313 if (Compile (input, out compiled) != null){
314 // Error, the input was partial.
318 // Either null (on error) or the compiled method.
323 // Todo: Should we handle errors, or expect the calling code to setup
324 // the recording themselves?
328 /// Evaluates and expression or statement and returns any result values.
331 /// Evaluates the input string as a C# expression or
332 /// statement. If the input string is an expression
333 /// the result will be stored in the result variable
334 /// and the result_set variable will be set to true.
336 /// It is necessary to use the result/result_set
337 /// pair to identify when a result was set (for
338 /// example, execution of user-provided input can be
339 /// an expression, a statement or others, and
340 /// result_set would only be set if the input was an
343 /// If the return value of this function is null,
344 /// this indicates that the parsing was complete.
345 /// If the return value is a string, it indicates
346 /// that the input is partial and that the user
347 /// should provide an updated string.
349 public static string Evaluate (string input, out object result, out bool result_set)
351 CompiledMethod compiled;
356 input = Compile (input, out compiled);
360 if (compiled == null)
364 // The code execution does not need to keep the compiler lock
366 object retval = typeof (NoValueSet);
369 invoke_thread = System.Threading.Thread.CurrentThread;
371 compiled (ref retval);
372 } catch (ThreadAbortException e){
373 Thread.ResetAbort ();
374 Console.WriteLine ("Interrupted!\n{0}", e);
380 // We use a reference to a compiler type, in this case
381 // Driver as a flag to indicate that this was a statement
383 if (retval != typeof (NoValueSet)){
391 public static string [] GetCompletions (string input, out string prefix)
394 if (input == null || input.Length == 0)
397 lock (evaluator_lock){
402 CSharpParser parser = ParseString (ParseMode.GetCompletions, input, out partial_input);
404 if (CSharpParser.yacc_verbose_flag != 0)
405 Console.WriteLine ("DEBUG: No completions available");
409 Class parser_result = parser.InteractiveResult as Class;
411 if (parser_result == null){
412 if (CSharpParser.yacc_verbose_flag != 0)
413 Console.WriteLine ("Do not know how to cope with !Class yet");
418 var a = new AssemblyDefinitionDynamic (RootContext.ToplevelTypes, "temp");
419 a.Create (AppDomain.CurrentDomain, AssemblyBuilderAccess.Run);
420 RootContext.ToplevelTypes.SetDeclaringAssembly (a);
421 RootContext.ToplevelTypes.CreateType ();
422 RootContext.ToplevelTypes.Define ();
423 if (ctx.Report.Errors != 0)
426 MethodOrOperator method = null;
427 foreach (MemberCore member in parser_result.Methods){
428 if (member.Name != "Host")
431 method = (MethodOrOperator) member;
435 throw new InternalErrorException ("did not find the the Host method");
437 BlockContext bc = new BlockContext (method, method.Block, method.ReturnType);
440 method.Block.Resolve (null, bc, method);
441 } catch (CompletionResult cr){
442 prefix = cr.BaseText;
446 parser.undo.ExecuteUndo ();
454 /// Executes the given expression or statement.
457 /// Executes the provided statement, returns true
458 /// on success, false on parsing errors. Exceptions
459 /// might be thrown by the called code.
461 public static bool Run (string statement)
469 bool ok = Evaluate (statement, out result, out result_set) == null;
475 /// Evaluates and expression or statement and returns the result.
478 /// Evaluates the input string as a C# expression or
479 /// statement and returns the value.
481 /// This method will throw an exception if there is a syntax error,
482 /// of if the provided input is not an expression but a statement.
484 public static object Evaluate (string input)
489 string r = Evaluate (input, out result, out result_set);
492 throw new ArgumentException ("Syntax error on input: partial input");
494 if (result_set == false)
495 throw new ArgumentException ("The expression did not set a result");
502 StatementOrExpression,
508 // Deambiguates the input string to determine if we
509 // want to process a statement or if we want to
510 // process a compilation unit.
512 // This is done using a top-down predictive parser,
513 // since the yacc/jay parser can not deambiguage this
514 // without more than one lookahead token. There are very
517 static InputKind ToplevelOrStatement (SeekableStreamReader seekable)
519 Tokenizer tokenizer = new Tokenizer (seekable, (CompilationUnit) Location.SourceFiles [0], ctx);
521 int t = tokenizer.token ();
524 return InputKind.EOF;
526 // These are toplevels
528 case Token.OPEN_BRACKET:
532 case Token.INTERFACE:
534 case Token.NAMESPACE:
536 case Token.PROTECTED:
541 return InputKind.CompilationUnit;
543 // Definitely expression
560 return InputKind.StatementOrExpression;
562 // These need deambiguation help
564 t = tokenizer.token ();
566 return InputKind.EOF;
568 if (t == Token.IDENTIFIER)
569 return InputKind.CompilationUnit;
570 return InputKind.StatementOrExpression;
573 // Distinguish between:
574 // delegate opt_anonymous_method_signature block
577 t = tokenizer.token ();
579 return InputKind.EOF;
580 if (t == Token.OPEN_PARENS || t == Token.OPEN_BRACE)
581 return InputKind.StatementOrExpression;
582 return InputKind.CompilationUnit;
584 // Distinguih between:
586 // unsafe as modifier of a type declaration
588 t = tokenizer.token ();
590 return InputKind.EOF;
591 if (t == Token.OPEN_PARENS)
592 return InputKind.StatementOrExpression;
593 return InputKind.CompilationUnit;
595 // These are errors: we list explicitly what we had
596 // from the grammar, ERROR and then everything else
601 return InputKind.Error;
603 // This catches everything else allowed by
604 // expressions. We could add one-by-one use cases
607 return InputKind.StatementOrExpression;
612 // Parses the string @input and returns a CSharpParser if succeeful.
614 // if @silent is set to true then no errors are
615 // reported to the user. This is used to do various calls to the
616 // parser and check if the expression is parsable.
618 // @partial_input: if @silent is true, then it returns whether the
619 // parsed expression was partial, and more data is needed
621 static CSharpParser ParseString (ParseMode mode, string input, out bool partial_input)
623 partial_input = false;
625 queued_fields.Clear ();
626 Tokenizer.LocatedToken.Initialize ();
628 Stream s = new MemoryStream (Encoding.Default.GetBytes (input));
629 SeekableStreamReader seekable = new SeekableStreamReader (s, Encoding.Default);
631 InputKind kind = ToplevelOrStatement (seekable);
632 if (kind == InputKind.Error){
633 if (mode == ParseMode.ReportErrors)
634 ctx.Report.Error (-25, "Detection Parsing Error");
635 partial_input = false;
639 if (kind == InputKind.EOF){
640 if (mode == ParseMode.ReportErrors)
641 Console.Error.WriteLine ("Internal error: EOF condition should have been detected in a previous call with silent=true");
642 partial_input = true;
646 seekable.Position = 0;
648 CSharpParser parser = new CSharpParser (seekable, Location.SourceFiles [0], RootContext.ToplevelTypes);
650 if (kind == InputKind.StatementOrExpression){
651 parser.Lexer.putback_char = Tokenizer.EvalStatementParserCharacter;
652 RootContext.StatementMode = true;
655 // Do not activate EvalCompilationUnitParserCharacter until
656 // I have figured out all the limitations to invoke methods
657 // in the generated classes. See repl.txt
659 parser.Lexer.putback_char = Tokenizer.EvalUsingDeclarationsParserCharacter;
660 //parser.Lexer.putback_char = Tokenizer.EvalCompilationUnitParserCharacter;
661 RootContext.StatementMode = false;
664 if (mode == ParseMode.GetCompletions)
665 parser.Lexer.CompleteOnEOF = true;
667 ReportPrinter old_printer = null;
668 if ((mode == ParseMode.Silent || mode == ParseMode.GetCompletions) && CSharpParser.yacc_verbose_flag == 0)
669 old_printer = SetPrinter (new StreamReportPrinter (TextWriter.Null));
674 if (ctx.Report.Errors != 0){
675 if (mode != ParseMode.ReportErrors && parser.UnexpectedEOF)
676 partial_input = true;
678 parser.undo.ExecuteUndo ();
682 if (old_printer != null)
683 SetPrinter (old_printer);
689 // Queue all the fields that we use, as we need to then go from FieldBuilder to FieldInfo
690 // or reflection gets confused (it basically gets confused, and variables override each
693 static List<Field> queued_fields = new List<Field> ();
695 //static ArrayList types = new ArrayList ();
697 static volatile bool invoking;
699 static CompiledMethod CompileBlock (Class host, Undo undo, Report Report)
701 AssemblyDefinitionDynamic assembly;
703 if (Environment.GetEnvironmentVariable ("SAVE") != null) {
704 assembly = new AssemblyDefinitionDynamic (RootContext.ToplevelTypes, current_debug_name, current_debug_name);
705 assembly.Importer = loader.Importer;
707 assembly = new AssemblyDefinitionDynamic (RootContext.ToplevelTypes, current_debug_name);
710 assembly.Create (AppDomain.CurrentDomain, AssemblyBuilderAccess.RunAndSave);
711 RootContext.ToplevelTypes.CreateType ();
712 RootContext.ToplevelTypes.Define ();
714 if (Report.Errors != 0){
719 TypeBuilder tb = null;
720 MethodBuilder mb = null;
723 tb = host.TypeBuilder;
725 foreach (MemberCore member in host.Methods){
726 if (member.Name != "Host")
729 MethodOrOperator method = (MethodOrOperator) member;
730 mb = method.MethodBuilder;
735 throw new Exception ("Internal error: did not find the method builder for the generated method");
738 RootContext.ToplevelTypes.Emit ();
739 if (Report.Errors != 0){
744 RootContext.ToplevelTypes.CloseType ();
746 if (Environment.GetEnvironmentVariable ("SAVE") != null)
753 // Unlike Mono, .NET requires that the MethodInfo is fetched, it cant
754 // work from MethodBuilders. Retarded, I know.
756 var tt = 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
767 if (fields.TryGetValue (field.Name, out old)) {
768 if (old.Item1.MemberType.IsStruct) {
770 // TODO: Clear fields for structs
774 old.Item2.SetValue (null, null);
779 fields [field.Name] = Tuple.Create (field.Spec, fi);
781 fields.Add (field.Name, Tuple.Create (field.Spec, fi));
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);
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
802 public class NoValueSet {
805 static internal Tuple<FieldSpec, FieldInfo> LookupField (string name)
807 Tuple<FieldSpec, FieldInfo> fi;
808 fields.TryGetValue (name, out 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
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 ());
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);
875 value = fi.Item2.GetValue (null);
877 value = Quote ((string)value);
879 value = "<error reading value>";
882 sb.AppendFormat ("{0} {1} = {2}", fi.Item1.MemberType.GetSignatureForError (), de.Key, value);
886 return sb.ToString ();
891 /// Loads the given assembly and exposes the API to the user.
893 static public void LoadAssembly (string file)
895 lock (evaluator_lock){
896 var a = loader.LoadAssemblyFile (file);
898 loader.Importer.ImportAssembly (a, RootContext.ToplevelTypes.GlobalRootNamespace);
903 /// Exposes the API of the given assembly to the Evaluator
905 static public void ReferenceAssembly (Assembly a)
907 lock (evaluator_lock){
908 loader.Importer.ImportAssembly (a, RootContext.ToplevelTypes.GlobalRootNamespace);
913 /// If true, turns type expressions into valid expressions
914 /// and calls the describe method on it
916 public static bool DescribeTypeExpressions;
921 /// A delegate that can be used to invoke the
922 /// compiled expression or statement.
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
932 public delegate void CompiledMethod (ref object retvalue);
935 /// The default base class for every interaction line
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
943 public class InteractiveBase {
945 /// Determines where the standard output of methods in this class will go.
947 public static TextWriter Output = Console.Out;
950 /// Determines where the standard error of methods in this class will go.
952 public static TextWriter Error = Console.Error;
955 /// The primary prompt used for interactive use.
957 public static string Prompt = "csharp> ";
960 /// The secondary prompt used for interactive use (used when
961 /// an expression is incomplete).
963 public static string ContinuationPrompt = " > ";
966 /// Used to signal that the user has invoked the `quit' statement.
968 public static bool QuitRequested;
971 /// Shows all the variables defined so far.
973 static public void ShowVars ()
975 Output.Write (Evaluator.GetVars ());
980 /// Displays the using statements in effect at this point.
982 static public void ShowUsing ()
984 Output.Write (Evaluator.GetUsing ());
988 public delegate void Simple ();
991 /// Times the execution of the given delegate
993 static public TimeSpan Time (Simple a)
995 DateTime start = DateTime.Now;
997 return DateTime.Now - start;
1002 /// Loads the assemblies from a package
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.
1009 static public void LoadPackage (string pkg)
1012 Error.WriteLine ("Invalid package specified");
1016 string pkgout = Driver.GetPackageFlags (pkg, false, RootContext.ToplevelTypes.Compiler.Report);
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);
1036 /// Loads the assembly
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.
1044 static public void LoadAssembly (string assembly)
1046 Evaluator.LoadAssembly (assembly);
1051 /// Returns a list of available static methods.
1053 static public string help {
1055 return "Static methods:\n" +
1056 " Describe (object) - Describes the object's type\n" +
1057 " LoadPackage (package); - Loads the given Package (like -pkg:FILE)\n" +
1058 " LoadAssembly (assembly) - Loads the given assembly (like -r:ASSEMBLY)\n" +
1059 " ShowVars (); - Shows defined local variables.\n" +
1060 " ShowUsing (); - Show active using declarations.\n" +
1061 " Prompt - The prompt used by the C# shell\n" +
1062 " ContinuationPrompt - The prompt for partial input\n" +
1063 " Time(() -> { }) - Times the specified code\n" +
1064 " quit; - You'll never believe it - this quits the repl!\n" +
1065 " help; - This help text\n";
1070 /// Indicates to the read-eval-print-loop that the interaction should be finished.
1072 static public object quit {
1074 QuitRequested = true;
1076 // To avoid print null at the exit
1077 return typeof (Evaluator.NoValueSet);
1083 /// Describes an object or a type.
1086 /// This method will show a textual representation
1087 /// of the object's type. If the object is a
1088 /// System.Type it renders the type directly,
1089 /// otherwise it renders the type returned by
1090 /// invoking GetType on the object.
1092 static public string Describe (object x)
1097 var type = x as Type ?? x.GetType ();
1099 StringWriter sw = new StringWriter ();
1100 new Outline (type, sw, true, false, false).OutlineType ();
1101 return sw.ToString ();
1106 class HoistedEvaluatorVariable : HoistedVariable
1108 public HoistedEvaluatorVariable (Field field)
1109 : base (null, field)
1113 public override void EmitSymbolInfo ()
1117 protected override FieldExpr GetFieldExpression (EmitContext ec)
1119 return new FieldExpr (field, field.Location);
1124 /// A class used to assign values if the source expression is not void
1126 /// Used by the interactive shell to allow it to call this code to set
1127 /// the return value for an invocation.
1129 class OptionalAssign : SimpleAssign {
1130 public OptionalAssign (Expression t, Expression s, Location loc)
1135 protected override Expression DoResolve (ResolveContext ec)
1137 CloneContext cc = new CloneContext ();
1138 Expression clone = source.Clone (cc);
1141 // A useful feature for the REPL: if we can resolve the expression
1142 // as a type, Describe the type;
1144 if (Evaluator.DescribeTypeExpressions){
1145 var old_printer = Evaluator.SetPrinter (new StreamReportPrinter (TextWriter.Null));
1146 clone = clone.Resolve (ec);
1148 clone = source.Clone (cc);
1149 clone = clone.Resolve (ec, ResolveFlags.Type);
1151 Evaluator.SetPrinter (old_printer);
1152 clone = source.Clone (cc);
1153 clone = clone.Resolve (ec);
1157 Arguments args = new Arguments (1);
1158 args.Add (new Argument (new TypeOf ((TypeExpr) clone, Location)));
1159 source = new Invocation (new SimpleName ("Describe", Location), args).Resolve (ec);
1161 Evaluator.SetPrinter (old_printer);
1163 clone = clone.Resolve (ec);
1168 // This means its really a statement.
1169 if (clone.Type == TypeManager.void_type || clone is DynamicInvocation || clone is Assign) {
1173 return base.DoResolve (ec);
1178 List<KeyValuePair<TypeContainer, TypeContainer>> undo_types;
1182 undo_types = new List<KeyValuePair<TypeContainer, TypeContainer>> ();
1185 public void AddTypeContainer (TypeContainer current_container, TypeContainer tc)
1187 if (current_container == tc){
1188 Console.Error.WriteLine ("Internal error: inserting container into itself");
1192 if (undo_types == null)
1193 undo_types = new List<KeyValuePair<TypeContainer, TypeContainer>> ();
1195 undo_types.Add (new KeyValuePair<TypeContainer, TypeContainer> (current_container, tc));
1198 public void ExecuteUndo ()
1200 if (undo_types == null)
1203 foreach (var p in undo_types){
1204 TypeContainer current_container = p.Key;
1206 current_container.RemoveTypeContainer (p.Value);