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;
26 /// Evaluator: provides an API to evaluate C# statements and
27 /// expressions dynamically.
30 /// This class exposes static methods to evaluate expressions in the
33 /// To initialize the evaluator with a number of compiler
34 /// options call the Init(string[]args) method with a set of
35 /// command line options that the compiler recognizes.
37 /// To interrupt execution of a statement, you can invoke the
38 /// Evaluator.Interrupt method.
40 public class Evaluator {
43 // Parse silently, do not output any error messages
46 // Report errors during parse
49 // Auto-complete, means that the tokenizer will start producing
50 // GETCOMPLETIONS tokens when it reaches a certain point.
54 static object evaluator_lock = new object ();
56 static string current_debug_name;
58 static Thread invoke_thread;
60 static Dictionary<string, Tuple<FieldSpec, FieldInfo>> fields = new Dictionary<string, Tuple<FieldSpec, FieldInfo>> ();
62 static TypeSpec interactive_base_class;
66 static CompilerContext ctx;
67 static DynamicLoader loader;
68 static NamespaceEntry ns;
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);
95 public static string [] InitAndGetStartupFiles (string [] args)
97 return InitAndGetStartupFiles (args, null);
101 /// Optional initialization for the Evaluator.
104 /// Initializes the Evaluator with the command line
105 /// options that would be processed by the command
106 /// line compiler. Only the first call to
107 /// InitAndGetStartupFiles or Init will work, any future
108 /// invocations are ignored.
110 /// You can safely avoid calling this method if your application
111 /// does not need any of the features exposed by the command line
114 /// This method return an array of strings that contains any
115 /// files that were specified in `args'.
117 /// If the unknownOptionParser is not null, this function is invoked
118 /// with the current args array and the index of the option that is not
119 /// known. A value of true means that the value was processed, otherwise
120 /// it will be reported as an error
122 public static string [] InitAndGetStartupFiles (string [] args, Func<string [], int, int> unknownOptionParser)
124 lock (evaluator_lock){
126 return new string [0];
128 CompilerCallableEntryPoint.Reset ();
129 var crp = new ConsoleReportPrinter ();
130 driver = Driver.Create (args, false, unknownOptionParser, crp);
132 throw new Exception ("Failed to create compiler driver with the given arguments");
134 crp.Fatal = driver.fatal_errors;
137 RootContext.ToplevelTypes = new ModuleContainer (ctx);
139 var startup_files = new List<string> ();
140 foreach (CompilationUnit file in Location.SourceFiles)
141 startup_files.Add (file.Path);
143 CompilerCallableEntryPoint.PartialReset ();
145 var importer = new ReflectionImporter (ctx.BuildinTypes);
146 loader = new DynamicLoader (importer, ctx);
148 RootContext.ToplevelTypes.SetDeclaringAssembly (new AssemblyDefinitionDynamic (RootContext.ToplevelTypes, "temp"));
150 loader.LoadReferences (RootContext.ToplevelTypes);
151 ctx.BuildinTypes.CheckDefinitions (RootContext.ToplevelTypes);
152 RootContext.ToplevelTypes.InitializePredefinedTypes ();
154 RootContext.EvalMode = true;
157 return startup_files.ToArray ();
163 Init (new string [0]);
168 CompilerCallableEntryPoint.PartialReset ();
170 Location.AddFile (null, "{interactive}");
171 Location.Initialize ();
173 current_debug_name = "interactive" + (count++) + ".dll";
177 /// The base class for the classes that host the user generated code
181 /// This is the base class that will host the code
182 /// executed by the Evaluator. By default
183 /// this is the Mono.CSharp.InteractiveBase class
184 /// which is useful for interactive use.
186 /// By changing this property you can control the
187 /// base class and the static members that are
188 /// available to your evaluated code.
190 static public TypeSpec InteractiveBaseClass {
192 if (interactive_base_class != null)
193 return interactive_base_class;
195 return loader.Importer.ImportType (typeof (InteractiveBase));
199 public static void SetInteractiveBaseClass (Type type)
202 throw new ArgumentNullException ();
205 throw new Exception ("Evaluator has to be initiated before seting custom InteractiveBase class");
207 lock (evaluator_lock)
208 interactive_base_class = loader.Importer.ImportType (type);
212 /// Interrupts the evaluation of an expression executing in Evaluate.
215 /// Use this method to interrupt long-running invocations.
217 public static void Interrupt ()
219 if (!inited || !invoking)
222 if (invoke_thread != null)
223 invoke_thread.Abort ();
227 /// Compiles the input string and returns a delegate that represents the compiled code.
231 /// Compiles the input string as a C# expression or
232 /// statement, unlike the Evaluate method, the
233 /// resulting delegate can be invoked multiple times
234 /// without incurring in the compilation overhead.
236 /// If the return value of this function is null,
237 /// this indicates that the parsing was complete.
238 /// If the return value is a string it indicates
239 /// that the input string was partial and that the
240 /// invoking code should provide more code before
241 /// the code can be successfully compiled.
243 /// If you know that you will always get full expressions or
244 /// statements and do not care about partial input, you can use
245 /// the other Compile overload.
247 /// On success, in addition to returning null, the
248 /// compiled parameter will be set to the delegate
249 /// that can be invoked to execute the code.
252 static public string Compile (string input, out CompiledMethod compiled)
254 if (input == null || input.Length == 0){
259 lock (evaluator_lock){
263 ctx.Report.Printer.Reset ();
265 // RootContext.ToplevelTypes = new ModuleContainer (ctx);
268 CSharpParser parser = ParseString (ParseMode.Silent, input, out partial_input);
274 ParseString (ParseMode.ReportErrors, input, out partial_input);
279 throw new NotSupportedException ();
281 Class parser_result = parser.InteractiveResult;
282 compiled = CompileBlock (parser_result, parser.undo, ctx.Report);
289 /// Compiles the input string and returns a delegate that represents the compiled code.
293 /// Compiles the input string as a C# expression or
294 /// statement, unlike the Evaluate method, the
295 /// resulting delegate can be invoked multiple times
296 /// without incurring in the compilation overhead.
298 /// This method can only deal with fully formed input
299 /// strings and does not provide a completion mechanism.
300 /// If you must deal with partial input (for example for
301 /// interactive use) use the other overload.
303 /// On success, a delegate is returned that can be used
304 /// to invoke the method.
307 static public CompiledMethod Compile (string input)
309 CompiledMethod compiled;
311 // Ignore partial inputs
312 if (Compile (input, out compiled) != null){
313 // Error, the input was partial.
317 // Either null (on error) or the compiled method.
322 // Todo: Should we handle errors, or expect the calling code to setup
323 // the recording themselves?
327 /// Evaluates and expression or statement and returns any result values.
330 /// Evaluates the input string as a C# expression or
331 /// statement. If the input string is an expression
332 /// the result will be stored in the result variable
333 /// and the result_set variable will be set to true.
335 /// It is necessary to use the result/result_set
336 /// pair to identify when a result was set (for
337 /// example, execution of user-provided input can be
338 /// an expression, a statement or others, and
339 /// result_set would only be set if the input was an
342 /// If the return value of this function is null,
343 /// this indicates that the parsing was complete.
344 /// If the return value is a string, it indicates
345 /// that the input is partial and that the user
346 /// should provide an updated string.
348 public static string Evaluate (string input, out object result, out bool result_set)
350 CompiledMethod compiled;
355 input = Compile (input, out compiled);
359 if (compiled == null)
363 // The code execution does not need to keep the compiler lock
365 object retval = typeof (NoValueSet);
368 invoke_thread = System.Threading.Thread.CurrentThread;
370 compiled (ref retval);
371 } catch (ThreadAbortException e){
372 Thread.ResetAbort ();
373 Console.WriteLine ("Interrupted!\n{0}", e);
379 // We use a reference to a compiler type, in this case
380 // Driver as a flag to indicate that this was a statement
382 if (retval != typeof (NoValueSet)){
390 public static string [] GetCompletions (string input, out string prefix)
393 if (input == null || input.Length == 0)
396 lock (evaluator_lock){
401 CSharpParser parser = ParseString (ParseMode.GetCompletions, input, out partial_input);
403 if (CSharpParser.yacc_verbose_flag != 0)
404 Console.WriteLine ("DEBUG: No completions available");
408 Class parser_result = parser.InteractiveResult;
411 var a = new AssemblyDefinitionDynamic (RootContext.ToplevelTypes, "temp");
412 a.Create (AppDomain.CurrentDomain, AssemblyBuilderAccess.Run);
413 RootContext.ToplevelTypes.SetDeclaringAssembly (a);
414 RootContext.ToplevelTypes.CreateType ();
415 RootContext.ToplevelTypes.Define ();
417 parser_result.CreateType ();
418 parser_result.Define ();
419 if (ctx.Report.Errors != 0)
422 MethodOrOperator method = null;
423 foreach (MemberCore member in parser_result.Methods){
424 if (member.Name != "Host")
427 method = (MethodOrOperator) member;
431 throw new InternalErrorException ("did not find the the Host method");
433 BlockContext bc = new BlockContext (method, method.Block, method.ReturnType);
436 method.Block.Resolve (null, bc, method);
437 } catch (CompletionResult cr){
438 prefix = cr.BaseText;
442 parser.undo.ExecuteUndo ();
450 /// Executes the given expression or statement.
453 /// Executes the provided statement, returns true
454 /// on success, false on parsing errors. Exceptions
455 /// might be thrown by the called code.
457 public static bool Run (string statement)
465 bool ok = Evaluate (statement, out result, out result_set) == null;
471 /// Evaluates and expression or statement and returns the result.
474 /// Evaluates the input string as a C# expression or
475 /// statement and returns the value.
477 /// This method will throw an exception if there is a syntax error,
478 /// of if the provided input is not an expression but a statement.
480 public static object Evaluate (string input)
485 string r = Evaluate (input, out result, out result_set);
488 throw new ArgumentException ("Syntax error on input: partial input");
490 if (result_set == false)
491 throw new ArgumentException ("The expression did not set a result");
498 StatementOrExpression,
504 // Deambiguates the input string to determine if we
505 // want to process a statement or if we want to
506 // process a compilation unit.
508 // This is done using a top-down predictive parser,
509 // since the yacc/jay parser can not deambiguage this
510 // without more than one lookahead token. There are very
513 static InputKind ToplevelOrStatement (SeekableStreamReader seekable)
515 Tokenizer tokenizer = new Tokenizer (seekable, (CompilationUnit) Location.SourceFiles [0], ctx);
517 int t = tokenizer.token ();
520 return InputKind.EOF;
522 // These are toplevels
524 case Token.OPEN_BRACKET:
528 case Token.INTERFACE:
530 case Token.NAMESPACE:
532 case Token.PROTECTED:
537 return InputKind.CompilationUnit;
539 // Definitely expression
556 return InputKind.StatementOrExpression;
558 // These need deambiguation help
560 t = tokenizer.token ();
562 return InputKind.EOF;
564 if (t == Token.IDENTIFIER)
565 return InputKind.CompilationUnit;
566 return InputKind.StatementOrExpression;
569 // Distinguish between:
570 // delegate opt_anonymous_method_signature block
573 t = tokenizer.token ();
575 return InputKind.EOF;
576 if (t == Token.OPEN_PARENS || t == Token.OPEN_BRACE)
577 return InputKind.StatementOrExpression;
578 return InputKind.CompilationUnit;
580 // Distinguih between:
582 // unsafe as modifier of a type declaration
584 t = tokenizer.token ();
586 return InputKind.EOF;
587 if (t == Token.OPEN_PARENS)
588 return InputKind.StatementOrExpression;
589 return InputKind.CompilationUnit;
591 // These are errors: we list explicitly what we had
592 // from the grammar, ERROR and then everything else
597 return InputKind.Error;
599 // This catches everything else allowed by
600 // expressions. We could add one-by-one use cases
603 return InputKind.StatementOrExpression;
608 // Parses the string @input and returns a CSharpParser if succeeful.
610 // if @silent is set to true then no errors are
611 // reported to the user. This is used to do various calls to the
612 // parser and check if the expression is parsable.
614 // @partial_input: if @silent is true, then it returns whether the
615 // parsed expression was partial, and more data is needed
617 static CSharpParser ParseString (ParseMode mode, string input, out bool partial_input)
619 partial_input = false;
621 queued_fields.Clear ();
622 Tokenizer.LocatedToken.Initialize ();
624 Stream s = new MemoryStream (Encoding.Default.GetBytes (input));
625 SeekableStreamReader seekable = new SeekableStreamReader (s, Encoding.Default);
627 InputKind kind = ToplevelOrStatement (seekable);
628 if (kind == InputKind.Error){
629 if (mode == ParseMode.ReportErrors)
630 ctx.Report.Error (-25, "Detection Parsing Error");
631 partial_input = false;
635 if (kind == InputKind.EOF){
636 if (mode == ParseMode.ReportErrors)
637 Console.Error.WriteLine ("Internal error: EOF condition should have been detected in a previous call with silent=true");
638 partial_input = true;
642 seekable.Position = 0;
645 ns = new NamespaceEntry (RootContext.ToplevelTypes, null, Location.SourceFiles[0], null);
647 CSharpParser parser = new CSharpParser (seekable, Location.SourceFiles [0], RootContext.ToplevelTypes, ns);
649 if (kind == InputKind.StatementOrExpression){
650 parser.Lexer.putback_char = Tokenizer.EvalStatementParserCharacter;
651 RootContext.StatementMode = true;
653 parser.Lexer.putback_char = Tokenizer.EvalCompilationUnitParserCharacter;
654 RootContext.StatementMode = false;
657 if (mode == ParseMode.GetCompletions)
658 parser.Lexer.CompleteOnEOF = true;
660 ReportPrinter old_printer = null;
661 if ((mode == ParseMode.Silent || mode == ParseMode.GetCompletions) && CSharpParser.yacc_verbose_flag == 0)
662 old_printer = SetPrinter (new StreamReportPrinter (TextWriter.Null));
667 if (ctx.Report.Errors != 0){
668 if (mode != ParseMode.ReportErrors && parser.UnexpectedEOF)
669 partial_input = true;
671 parser.undo.ExecuteUndo ();
675 if (old_printer != null)
676 SetPrinter (old_printer);
682 // Queue all the fields that we use, as we need to then go from FieldBuilder to FieldInfo
683 // or reflection gets confused (it basically gets confused, and variables override each
686 static List<Field> queued_fields = new List<Field> ();
688 //static ArrayList types = new ArrayList ();
690 static volatile bool invoking;
692 static CompiledMethod CompileBlock (Class host, Undo undo, Report Report)
694 AssemblyDefinitionDynamic assembly;
695 AssemblyBuilderAccess access;
697 if (Environment.GetEnvironmentVariable ("SAVE") != null) {
698 access = AssemblyBuilderAccess.RunAndSave;
699 assembly = new AssemblyDefinitionDynamic (RootContext.ToplevelTypes, current_debug_name, current_debug_name);
700 assembly.Importer = loader.Importer;
703 access = AssemblyBuilderAccess.RunAndCollect;
705 access = AssemblyBuilderAccess.Run;
707 assembly = new AssemblyDefinitionDynamic (RootContext.ToplevelTypes, current_debug_name);
710 assembly.Create (AppDomain.CurrentDomain, access);
717 RootContext.ToplevelTypes.CreateType ();
718 RootContext.ToplevelTypes.Define ();
720 if (Report.Errors != 0){
725 TypeBuilder tb = null;
726 MethodBuilder mb = null;
729 tb = host.TypeBuilder;
731 foreach (MemberCore member in host.Methods){
732 if (member.Name != "Host")
735 MethodOrOperator method = (MethodOrOperator) member;
736 mb = method.MethodBuilder;
741 throw new Exception ("Internal error: did not find the method builder for the generated method");
746 RootContext.ToplevelTypes.Emit ();
747 if (Report.Errors != 0){
752 RootContext.ToplevelTypes.CloseType ();
756 if (access == AssemblyBuilderAccess.RunAndSave)
763 // Unlike Mono, .NET requires that the MethodInfo is fetched, it cant
764 // work from MethodBuilders. Retarded, I know.
766 var tt = assembly.Builder.GetType (tb.Name);
767 MethodInfo mi = tt.GetMethod (mb.Name);
769 // Pull the FieldInfos from the type, and keep track of them
770 foreach (Field field in queued_fields){
771 FieldInfo fi = tt.GetField (field.Name);
773 Tuple<FieldSpec, FieldInfo> old;
775 // If a previous value was set, nullify it, so that we do
777 if (fields.TryGetValue (field.Name, out old)) {
778 if (old.Item1.MemberType.IsStruct) {
780 // TODO: Clear fields for structs
784 old.Item2.SetValue (null, null);
789 fields [field.Name] = Tuple.Create (field.Spec, fi);
791 fields.Add (field.Name, Tuple.Create (field.Spec, fi));
794 queued_fields.Clear ();
796 return (CompiledMethod) System.Delegate.CreateDelegate (typeof (CompiledMethod), mi);
801 /// A sentinel value used to indicate that no value was
802 /// was set by the compiled function. This is used to
803 /// differentiate between a function not returning a
806 public class NoValueSet {
809 static internal Tuple<FieldSpec, FieldInfo> LookupField (string name)
811 Tuple<FieldSpec, FieldInfo> fi;
812 fields.TryGetValue (name, out fi);
817 // Puts the FieldBuilder into a queue of names that will be
818 // registered. We can not register FieldBuilders directly
819 // we need to fetch the FieldInfo after Reflection cooks the
820 // types, or bad things happen (bad means: FieldBuilders behave
821 // incorrectly across multiple assemblies, causing assignments to
824 // This also serves for the parser to register Field classes
825 // that should be exposed as global variables
827 static internal void QueueField (Field f)
829 queued_fields.Add (f);
832 static string Quote (string s)
834 if (s.IndexOf ('"') != -1)
835 s = s.Replace ("\"", "\\\"");
837 return "\"" + s + "\"";
840 static public string GetUsing ()
842 lock (evaluator_lock){
846 StringBuilder sb = new StringBuilder ();
848 //foreach (object x in ns.using_alias_list)
849 // sb.AppendFormat ("using {0};\n", x);
851 foreach (var ue in ns.Usings) {
852 sb.AppendFormat ("using {0};", ue.ToString ());
853 sb.Append (Environment.NewLine);
856 return sb.ToString ();
860 static internal ICollection<string> GetUsingList ()
862 var res = new List<string> ();
864 foreach (var ue in ns.Usings)
865 res.Add (ue.ToString ());
869 static internal string [] GetVarNames ()
871 lock (evaluator_lock){
872 return new List<string> (fields.Keys).ToArray ();
876 static public string GetVars ()
878 lock (evaluator_lock){
879 StringBuilder sb = new StringBuilder ();
881 foreach (var de in fields){
882 var fi = LookupField (de.Key);
885 value = fi.Item2.GetValue (null);
887 value = Quote ((string)value);
889 value = "<error reading value>";
892 sb.AppendFormat ("{0} {1} = {2}", fi.Item1.MemberType.GetSignatureForError (), de.Key, value);
896 return sb.ToString ();
901 /// Loads the given assembly and exposes the API to the user.
903 static public void LoadAssembly (string file)
905 lock (evaluator_lock){
906 var a = loader.LoadAssemblyFile (file);
908 loader.Importer.ImportAssembly (a, RootContext.ToplevelTypes.GlobalRootNamespace);
913 /// Exposes the API of the given assembly to the Evaluator
915 static public void ReferenceAssembly (Assembly a)
917 lock (evaluator_lock){
918 loader.Importer.ImportAssembly (a, RootContext.ToplevelTypes.GlobalRootNamespace);
923 /// If true, turns type expressions into valid expressions
924 /// and calls the describe method on it
926 public static bool DescribeTypeExpressions;
931 /// A delegate that can be used to invoke the
932 /// compiled expression or statement.
935 /// Since the Compile methods will compile
936 /// statements and expressions into the same
937 /// delegate, you can tell if a value was returned
938 /// by checking whether the returned value is of type
942 public delegate void CompiledMethod (ref object retvalue);
945 /// The default base class for every interaction line
948 /// The expressions and statements behave as if they were
949 /// a static method of this class. The InteractiveBase class
950 /// contains a number of useful methods, but can be overwritten
951 /// by setting the InteractiveBaseType property in the Evaluator
953 public class InteractiveBase {
955 /// Determines where the standard output of methods in this class will go.
957 public static TextWriter Output = Console.Out;
960 /// Determines where the standard error of methods in this class will go.
962 public static TextWriter Error = Console.Error;
965 /// The primary prompt used for interactive use.
967 public static string Prompt = "csharp> ";
970 /// The secondary prompt used for interactive use (used when
971 /// an expression is incomplete).
973 public static string ContinuationPrompt = " > ";
976 /// Used to signal that the user has invoked the `quit' statement.
978 public static bool QuitRequested;
981 /// Shows all the variables defined so far.
983 static public void ShowVars ()
985 Output.Write (Evaluator.GetVars ());
990 /// Displays the using statements in effect at this point.
992 static public void ShowUsing ()
994 Output.Write (Evaluator.GetUsing ());
998 public delegate void Simple ();
1001 /// Times the execution of the given delegate
1003 static public TimeSpan Time (Simple a)
1005 DateTime start = DateTime.Now;
1007 return DateTime.Now - start;
1012 /// Loads the assemblies from a package
1015 /// Loads the assemblies from a package. This is equivalent
1016 /// to passing the -pkg: command line flag to the C# compiler
1017 /// on the command line.
1019 static public void LoadPackage (string pkg)
1022 Error.WriteLine ("Invalid package specified");
1026 string pkgout = Driver.GetPackageFlags (pkg, false, RootContext.ToplevelTypes.Compiler.Report);
1030 string [] xargs = pkgout.Trim (new Char [] {' ', '\n', '\r', '\t'}).
1031 Split (new Char [] { ' ', '\t'});
1033 foreach (string s in xargs){
1034 if (s.StartsWith ("-r:") || s.StartsWith ("/r:") || s.StartsWith ("/reference:")){
1035 string lib = s.Substring (s.IndexOf (':')+1);
1037 Evaluator.LoadAssembly (lib);
1046 /// Loads the assembly
1049 /// Loads the specified assembly and makes its types
1050 /// available to the evaluator. This is equivalent
1051 /// to passing the -pkg: command line flag to the C#
1052 /// compiler on the command line.
1054 static public void LoadAssembly (string assembly)
1056 Evaluator.LoadAssembly (assembly);
1059 static public void print (string obj)
1061 Output.WriteLine (obj);
1064 static public void print (string fmt, params object [] args)
1066 Output.WriteLine (fmt, args);
1071 /// Returns a list of available static methods.
1073 static public string help {
1075 return "Static methods:\n" +
1076 " Describe (object) - Describes the object's type\n" +
1077 " LoadPackage (package); - Loads the given Package (like -pkg:FILE)\n" +
1078 " LoadAssembly (assembly) - Loads the given assembly (like -r:ASSEMBLY)\n" +
1079 " ShowVars (); - Shows defined local variables.\n" +
1080 " ShowUsing (); - Show active using declarations.\n" +
1081 " Prompt - The prompt used by the C# shell\n" +
1082 " ContinuationPrompt - The prompt for partial input\n" +
1083 " Time(() -> { }) - Times the specified code\n" +
1084 " print (obj) - Shorthand for Console.WriteLine\n" +
1085 " quit; - You'll never believe it - this quits the repl!\n" +
1086 " help; - This help text\n";
1091 /// Indicates to the read-eval-print-loop that the interaction should be finished.
1093 static public object quit {
1095 QuitRequested = true;
1097 // To avoid print null at the exit
1098 return typeof (Evaluator.NoValueSet);
1104 /// Describes an object or a type.
1107 /// This method will show a textual representation
1108 /// of the object's type. If the object is a
1109 /// System.Type it renders the type directly,
1110 /// otherwise it renders the type returned by
1111 /// invoking GetType on the object.
1113 static public string Describe (object x)
1118 var type = x as Type ?? x.GetType ();
1120 StringWriter sw = new StringWriter ();
1121 new Outline (type, sw, true, false, false).OutlineType ();
1122 return sw.ToString ();
1127 class HoistedEvaluatorVariable : HoistedVariable
1129 public HoistedEvaluatorVariable (Field field)
1130 : base (null, field)
1134 public override void EmitSymbolInfo ()
1138 protected override FieldExpr GetFieldExpression (EmitContext ec)
1140 return new FieldExpr (field, field.Location);
1145 /// A class used to assign values if the source expression is not void
1147 /// Used by the interactive shell to allow it to call this code to set
1148 /// the return value for an invocation.
1150 class OptionalAssign : SimpleAssign {
1151 public OptionalAssign (Expression t, Expression s, Location loc)
1156 protected override Expression DoResolve (ResolveContext ec)
1158 CloneContext cc = new CloneContext ();
1159 Expression clone = source.Clone (cc);
1162 // A useful feature for the REPL: if we can resolve the expression
1163 // as a type, Describe the type;
1165 if (Evaluator.DescribeTypeExpressions){
1166 var old_printer = Evaluator.SetPrinter (new StreamReportPrinter (TextWriter.Null));
1167 clone = clone.Resolve (ec);
1169 clone = source.Clone (cc);
1170 clone = clone.Resolve (ec, ResolveFlags.Type);
1172 Evaluator.SetPrinter (old_printer);
1173 clone = source.Clone (cc);
1174 clone = clone.Resolve (ec);
1178 Arguments args = new Arguments (1);
1179 args.Add (new Argument (new TypeOf ((TypeExpr) clone, Location)));
1180 source = new Invocation (new SimpleName ("Describe", Location), args).Resolve (ec);
1182 Evaluator.SetPrinter (old_printer);
1184 clone = clone.Resolve (ec);
1189 // This means its really a statement.
1190 if (clone.Type == TypeManager.void_type || clone is DynamicInvocation || clone is Assign) {
1194 return base.DoResolve (ec);
1200 List<Action> undo_actions;
1206 public void AddTypeContainer (TypeContainer current_container, TypeContainer tc)
1208 if (current_container == tc){
1209 Console.Error.WriteLine ("Internal error: inserting container into itself");
1213 if (undo_actions == null)
1214 undo_actions = new List<Action> ();
1216 var existing = current_container.Types.FirstOrDefault (l => l.MemberName.Basename == tc.MemberName.Basename);
1217 if (existing != null) {
1218 current_container.RemoveTypeContainer (existing);
1219 undo_actions.Add (() => current_container.AddTypeContainer (existing));
1222 undo_actions.Add (() => current_container.RemoveTypeContainer (tc));
1225 public void ExecuteUndo ()
1227 if (undo_actions == null)
1230 foreach (var p in undo_actions){
1234 undo_actions = null;