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
13 using System.Threading;
14 using System.Collections.Generic;
15 using System.Reflection;
16 using System.Reflection.Emit;
20 namespace Mono.CSharp {
23 /// Evaluator: provides an API to evaluate C# statements and
24 /// expressions dynamically.
27 /// This class exposes static methods to evaluate expressions in the
30 /// To initialize the evaluator with a number of compiler
31 /// options call the Init(string[]args) method with a set of
32 /// command line options that the compiler recognizes.
34 /// To interrupt execution of a statement, you can invoke the
35 /// Evaluator.Interrupt method.
37 public class Evaluator {
40 // Parse silently, do not output any error messages
43 // Report errors during parse
46 // Auto-complete, means that the tokenizer will start producing
47 // GETCOMPLETIONS tokens when it reaches a certain point.
51 static object evaluator_lock = new object ();
53 static string current_debug_name;
55 static Thread invoke_thread;
57 static List<NamespaceEntry.UsingAliasEntry> using_alias_list = new List<NamespaceEntry.UsingAliasEntry> ();
58 internal static List<NamespaceEntry.UsingEntry> using_list = new List<NamespaceEntry.UsingEntry> ();
59 static Dictionary<string, Tuple<FieldSpec, FieldInfo>> fields = new Dictionary<string, Tuple<FieldSpec, FieldInfo>> ();
61 static TypeSpec interactive_base_class;
65 static CompilerContext ctx;
66 static DynamicLoader loader;
68 public static TextWriter MessageOutput = Console.Out;
71 /// Optional initialization for the Evaluator.
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
79 /// You can safely avoid calling this method if your application
80 /// does not need any of the features exposed by the command line
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);
94 /// Optional initialization for the Evaluator.
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
107 /// This method return an array of strings that contains any
108 /// files that were specified in `args'.
110 public static string [] InitAndGetStartupFiles (string [] args)
112 lock (evaluator_lock){
114 return new string [0];
116 driver = Driver.Create (args, false, new ConsoleReportPrinter ());
118 throw new Exception ("Failed to create compiler driver with the given arguments");
122 RootContext.ToplevelTypes = new ModuleContainer (ctx);
124 var startup_files = new List<string> ();
125 foreach (CompilationUnit file in Location.SourceFiles)
126 startup_files.Add (file.Path);
128 CompilerCallableEntryPoint.Reset ();
130 var importer = new ReflectionImporter (ctx.BuildinTypes);
131 loader = new DynamicLoader (importer, ctx);
133 RootContext.ToplevelTypes.MakeExecutable ("temp");
134 loader.LoadReferences (RootContext.ToplevelTypes);
135 ctx.BuildinTypes.CheckDefinitions (RootContext.ToplevelTypes);
136 RootContext.ToplevelTypes.InitializePredefinedTypes ();
138 RootContext.EvalMode = true;
141 return startup_files.ToArray ();
147 Init (new string [0]);
152 CompilerCallableEntryPoint.PartialReset ();
154 Location.AddFile (null, "{interactive}");
155 Location.Initialize ();
157 current_debug_name = "interactive" + (count++) + ".dll";
161 /// The base class for the classes that host the user generated code
165 /// This is the base class that will host the code
166 /// executed by the Evaluator. By default
167 /// this is the Mono.CSharp.InteractiveBase class
168 /// which is useful for interactive use.
170 /// By changing this property you can control the
171 /// base class and the static members that are
172 /// available to your evaluated code.
174 static public TypeSpec InteractiveBaseClass {
176 if (interactive_base_class != null)
177 return interactive_base_class;
179 return loader.Importer.ImportType (typeof (InteractiveBase));
183 public static void SetInteractiveBaseClass (Type type)
186 throw new ArgumentNullException ();
188 lock (evaluator_lock)
189 interactive_base_class = loader.Importer.ImportType (type);
193 /// Interrupts the evaluation of an expression executing in Evaluate.
196 /// Use this method to interrupt long-running invocations.
198 public static void Interrupt ()
200 if (!inited || !invoking)
203 if (invoke_thread != null)
204 invoke_thread.Abort ();
208 /// Compiles the input string and returns a delegate that represents the compiled code.
212 /// Compiles the input string as a C# expression or
213 /// statement, unlike the Evaluate method, the
214 /// resulting delegate can be invoked multiple times
215 /// without incurring in the compilation overhead.
217 /// If the return value of this function is null,
218 /// this indicates that the parsing was complete.
219 /// If the return value is a string it indicates
220 /// that the input string was partial and that the
221 /// invoking code should provide more code before
222 /// the code can be successfully compiled.
224 /// If you know that you will always get full expressions or
225 /// statements and do not care about partial input, you can use
226 /// the other Compile overload.
228 /// On success, in addition to returning null, the
229 /// compiled parameter will be set to the delegate
230 /// that can be invoked to execute the code.
233 static public string Compile (string input, out CompiledMethod compiled)
235 if (input == null || input.Length == 0){
240 lock (evaluator_lock){
244 // RootContext.ToplevelTypes = new ModuleContainer (ctx);
247 CSharpParser parser = ParseString (ParseMode.Silent, input, out partial_input);
253 ParseString (ParseMode.ReportErrors, input, out partial_input);
257 object parser_result = parser.InteractiveResult;
259 if (!(parser_result is Class)){
260 int errors = ctx.Report.Errors;
262 NamespaceEntry.VerifyAllUsing ();
263 if (errors == ctx.Report.Errors)
264 parser.CurrentNamespace.Extract (using_alias_list, using_list);
267 compiled = CompileBlock (parser_result as Class, parser.undo, ctx.Report);
274 /// Compiles the input string and returns a delegate that represents the compiled code.
278 /// Compiles the input string as a C# expression or
279 /// statement, unlike the Evaluate method, the
280 /// resulting delegate can be invoked multiple times
281 /// without incurring in the compilation overhead.
283 /// This method can only deal with fully formed input
284 /// strings and does not provide a completion mechanism.
285 /// If you must deal with partial input (for example for
286 /// interactive use) use the other overload.
288 /// On success, a delegate is returned that can be used
289 /// to invoke the method.
292 static public CompiledMethod Compile (string input)
294 CompiledMethod compiled;
296 // Ignore partial inputs
297 if (Compile (input, out compiled) != null){
298 // Error, the input was partial.
302 // Either null (on error) or the compiled method.
307 // Todo: Should we handle errors, or expect the calling code to setup
308 // the recording themselves?
312 /// Evaluates and expression or statement and returns any result values.
315 /// Evaluates the input string as a C# expression or
316 /// statement. If the input string is an expression
317 /// the result will be stored in the result variable
318 /// and the result_set variable will be set to true.
320 /// It is necessary to use the result/result_set
321 /// pair to identify when a result was set (for
322 /// example, execution of user-provided input can be
323 /// an expression, a statement or others, and
324 /// result_set would only be set if the input was an
327 /// If the return value of this function is null,
328 /// this indicates that the parsing was complete.
329 /// If the return value is a string, it indicates
330 /// that the input is partial and that the user
331 /// should provide an updated string.
333 public static string Evaluate (string input, out object result, out bool result_set)
335 CompiledMethod compiled;
340 input = Compile (input, out compiled);
344 if (compiled == null)
348 // The code execution does not need to keep the compiler lock
350 object retval = typeof (NoValueSet);
353 invoke_thread = System.Threading.Thread.CurrentThread;
355 compiled (ref retval);
356 } catch (ThreadAbortException e){
357 Thread.ResetAbort ();
358 Console.WriteLine ("Interrupted!\n{0}", e);
364 // We use a reference to a compiler type, in this case
365 // Driver as a flag to indicate that this was a statement
367 if (retval != typeof (NoValueSet)){
375 public static string [] GetCompletions (string input, out string prefix)
378 if (input == null || input.Length == 0)
381 lock (evaluator_lock){
386 CSharpParser parser = ParseString (ParseMode.GetCompletions, input, out partial_input);
388 if (CSharpParser.yacc_verbose_flag != 0)
389 Console.WriteLine ("DEBUG: No completions available");
393 Class parser_result = parser.InteractiveResult as Class;
395 if (parser_result == null){
396 if (CSharpParser.yacc_verbose_flag != 0)
397 Console.WriteLine ("Do not know how to cope with !Class yet");
402 var a = RootContext.ToplevelTypes.MakeExecutable ("temp");
403 a.Create (AppDomain.CurrentDomain, AssemblyBuilderAccess.Run);
404 RootContext.ToplevelTypes.Define ();
405 if (ctx.Report.Errors != 0)
408 MethodOrOperator method = null;
409 foreach (MemberCore member in parser_result.Methods){
410 if (member.Name != "Host")
413 method = (MethodOrOperator) member;
417 throw new InternalErrorException ("did not find the the Host method");
419 BlockContext bc = new BlockContext (method, method.Block, method.ReturnType);
422 method.Block.Resolve (null, bc, method);
423 } catch (CompletionResult cr){
424 prefix = cr.BaseText;
428 parser.undo.ExecuteUndo ();
436 /// Executes the given expression or statement.
439 /// Executes the provided statement, returns true
440 /// on success, false on parsing errors. Exceptions
441 /// might be thrown by the called code.
443 public static bool Run (string statement)
451 bool ok = Evaluate (statement, out result, out result_set) == null;
457 /// Evaluates and expression or statement and returns the result.
460 /// Evaluates the input string as a C# expression or
461 /// statement and returns the value.
463 /// This method will throw an exception if there is a syntax error,
464 /// of if the provided input is not an expression but a statement.
466 public static object Evaluate (string input)
471 string r = Evaluate (input, out result, out result_set);
474 throw new ArgumentException ("Syntax error on input: partial input");
476 if (result_set == false)
477 throw new ArgumentException ("The expression did not set a result");
484 StatementOrExpression,
490 // Deambiguates the input string to determine if we
491 // want to process a statement or if we want to
492 // process a compilation unit.
494 // This is done using a top-down predictive parser,
495 // since the yacc/jay parser can not deambiguage this
496 // without more than one lookahead token. There are very
499 static InputKind ToplevelOrStatement (SeekableStreamReader seekable)
501 Tokenizer tokenizer = new Tokenizer (seekable, (CompilationUnit) Location.SourceFiles [0], ctx);
503 int t = tokenizer.token ();
506 return InputKind.EOF;
508 // These are toplevels
510 case Token.OPEN_BRACKET:
514 case Token.INTERFACE:
516 case Token.NAMESPACE:
518 case Token.PROTECTED:
523 return InputKind.CompilationUnit;
525 // Definitely expression
542 return InputKind.StatementOrExpression;
544 // These need deambiguation help
546 t = tokenizer.token ();
548 return InputKind.EOF;
550 if (t == Token.IDENTIFIER)
551 return InputKind.CompilationUnit;
552 return InputKind.StatementOrExpression;
555 // Distinguish between:
556 // delegate opt_anonymous_method_signature block
559 t = tokenizer.token ();
561 return InputKind.EOF;
562 if (t == Token.OPEN_PARENS || t == Token.OPEN_BRACE)
563 return InputKind.StatementOrExpression;
564 return InputKind.CompilationUnit;
566 // Distinguih between:
568 // unsafe as modifier of a type declaration
570 t = tokenizer.token ();
572 return InputKind.EOF;
573 if (t == Token.OPEN_PARENS)
574 return InputKind.StatementOrExpression;
575 return InputKind.CompilationUnit;
577 // These are errors: we list explicitly what we had
578 // from the grammar, ERROR and then everything else
583 return InputKind.Error;
585 // This catches everything else allowed by
586 // expressions. We could add one-by-one use cases
589 return InputKind.StatementOrExpression;
594 // Parses the string @input and returns a CSharpParser if succeeful.
596 // if @silent is set to true then no errors are
597 // reported to the user. This is used to do various calls to the
598 // parser and check if the expression is parsable.
600 // @partial_input: if @silent is true, then it returns whether the
601 // parsed expression was partial, and more data is needed
603 static CSharpParser ParseString (ParseMode mode, string input, out bool partial_input)
605 partial_input = false;
607 queued_fields.Clear ();
608 Tokenizer.LocatedToken.Initialize ();
610 Stream s = new MemoryStream (Encoding.Default.GetBytes (input));
611 SeekableStreamReader seekable = new SeekableStreamReader (s, Encoding.Default);
613 InputKind kind = ToplevelOrStatement (seekable);
614 if (kind == InputKind.Error){
615 if (mode == ParseMode.ReportErrors)
616 ctx.Report.Error (-25, "Detection Parsing Error");
617 partial_input = false;
621 if (kind == InputKind.EOF){
622 if (mode == ParseMode.ReportErrors)
623 Console.Error.WriteLine ("Internal error: EOF condition should have been detected in a previous call with silent=true");
624 partial_input = true;
628 seekable.Position = 0;
630 CSharpParser parser = new CSharpParser (seekable, Location.SourceFiles [0], RootContext.ToplevelTypes);
632 if (kind == InputKind.StatementOrExpression){
633 parser.Lexer.putback_char = Tokenizer.EvalStatementParserCharacter;
634 RootContext.StatementMode = true;
637 // Do not activate EvalCompilationUnitParserCharacter until
638 // I have figured out all the limitations to invoke methods
639 // in the generated classes. See repl.txt
641 parser.Lexer.putback_char = Tokenizer.EvalUsingDeclarationsParserCharacter;
642 //parser.Lexer.putback_char = Tokenizer.EvalCompilationUnitParserCharacter;
643 RootContext.StatementMode = false;
646 if (mode == ParseMode.GetCompletions)
647 parser.Lexer.CompleteOnEOF = true;
649 ReportPrinter old_printer = null;
650 if ((mode == ParseMode.Silent || mode == ParseMode.GetCompletions) && CSharpParser.yacc_verbose_flag == 0)
651 old_printer = SetPrinter (new StreamReportPrinter (TextWriter.Null));
656 if (ctx.Report.Errors != 0){
657 if (mode != ParseMode.ReportErrors && parser.UnexpectedEOF)
658 partial_input = true;
660 parser.undo.ExecuteUndo ();
664 if (old_printer != null)
665 SetPrinter (old_printer);
671 // Queue all the fields that we use, as we need to then go from FieldBuilder to FieldInfo
672 // or reflection gets confused (it basically gets confused, and variables override each
675 static List<Field> queued_fields = new List<Field> ();
677 //static ArrayList types = new ArrayList ();
679 static volatile bool invoking;
681 static CompiledMethod CompileBlock (Class host, Undo undo, Report Report)
683 AssemblyDefinition assembly;
685 if (Environment.GetEnvironmentVariable ("SAVE") != null) {
686 assembly = RootContext.ToplevelTypes.MakeExecutable (current_debug_name, current_debug_name);
687 assembly.Importer = loader.Importer;
689 assembly = RootContext.ToplevelTypes.MakeExecutable (current_debug_name);
692 assembly.Create (AppDomain.CurrentDomain, AssemblyBuilderAccess.RunAndSave);
693 RootContext.ToplevelTypes.Define ();
695 if (Report.Errors != 0){
700 TypeBuilder tb = null;
701 MethodBuilder mb = null;
704 tb = host.TypeBuilder;
706 foreach (MemberCore member in host.Methods){
707 if (member.Name != "Host")
710 MethodOrOperator method = (MethodOrOperator) member;
711 mb = method.MethodBuilder;
716 throw new Exception ("Internal error: did not find the method builder for the generated method");
719 RootContext.ToplevelTypes.Emit ();
720 if (Report.Errors != 0){
725 RootContext.ToplevelTypes.CloseType ();
727 if (Environment.GetEnvironmentVariable ("SAVE") != null)
734 // Unlike Mono, .NET requires that the MethodInfo is fetched, it cant
735 // work from MethodBuilders. Retarded, I know.
737 var tt = assembly.Builder.GetType (tb.Name);
738 MethodInfo mi = tt.GetMethod (mb.Name);
740 // Pull the FieldInfos from the type, and keep track of them
741 foreach (Field field in queued_fields){
742 FieldInfo fi = tt.GetField (field.Name);
744 Tuple<FieldSpec, FieldInfo> old;
746 // If a previous value was set, nullify it, so that we do
748 if (fields.TryGetValue (field.Name, out old)) {
749 if (old.Item1.MemberType.IsStruct) {
751 // TODO: Clear fields for structs
755 old.Item2.SetValue (null, null);
760 fields [field.Name] = Tuple.Create (old.Item1, fi);
762 fields.Add (field.Name, Tuple.Create (field.Spec, fi));
767 queued_fields.Clear ();
769 return (CompiledMethod) System.Delegate.CreateDelegate (typeof (CompiledMethod), mi);
772 static internal void LoadAliases (NamespaceEntry ns)
774 ns.Populate (using_alias_list, using_list);
778 /// A sentinel value used to indicate that no value was
779 /// was set by the compiled function. This is used to
780 /// differentiate between a function not returning a
783 public class NoValueSet {
786 static internal Tuple<FieldSpec, FieldInfo> LookupField (string name)
788 Tuple<FieldSpec, FieldInfo> fi;
789 fields.TryGetValue (name, out fi);
794 // Puts the FieldBuilder into a queue of names that will be
795 // registered. We can not register FieldBuilders directly
796 // we need to fetch the FieldInfo after Reflection cooks the
797 // types, or bad things happen (bad means: FieldBuilders behave
798 // incorrectly across multiple assemblies, causing assignments to
801 // This also serves for the parser to register Field classes
802 // that should be exposed as global variables
804 static internal void QueueField (Field f)
806 queued_fields.Add (f);
809 static string Quote (string s)
811 if (s.IndexOf ('"') != -1)
812 s = s.Replace ("\"", "\\\"");
814 return "\"" + s + "\"";
817 static public string GetUsing ()
819 lock (evaluator_lock){
820 StringBuilder sb = new StringBuilder ();
822 foreach (object x in using_alias_list)
823 sb.Append (String.Format ("using {0};\n", x));
825 foreach (object x in using_list)
826 sb.Append (String.Format ("using {0};\n", x));
828 return sb.ToString ();
832 static internal ICollection<string> GetUsingList ()
834 var res = new List<string> (using_list.Count);
835 foreach (object ue in using_list)
836 res.Add (ue.ToString ());
840 static internal string [] GetVarNames ()
842 lock (evaluator_lock){
843 return new List<string> (fields.Keys).ToArray ();
847 static public string GetVars ()
849 lock (evaluator_lock){
850 StringBuilder sb = new StringBuilder ();
852 foreach (var de in fields){
853 var fi = LookupField (de.Key);
856 value = fi.Item2.GetValue (null);
858 value = Quote ((string)value);
860 value = "<error reading value>";
863 sb.AppendFormat ("{0} {1} = {2}", fi.Item1.MemberType.GetSignatureForError (), de.Key, value);
867 return sb.ToString ();
872 /// Loads the given assembly and exposes the API to the user.
874 static public void LoadAssembly (string file)
876 lock (evaluator_lock){
877 var a = loader.LoadAssemblyFile (file, false);
879 loader.Importer.ImportAssembly (a, RootContext.ToplevelTypes.GlobalRootNamespace);
884 /// Exposes the API of the given assembly to the Evaluator
886 static public void ReferenceAssembly (Assembly a)
888 lock (evaluator_lock){
889 loader.Importer.ImportAssembly (a, RootContext.ToplevelTypes.GlobalRootNamespace);
894 /// If true, turns type expressions into valid expressions
895 /// and calls the describe method on it
897 public static bool DescribeTypeExpressions;
902 /// A delegate that can be used to invoke the
903 /// compiled expression or statement.
906 /// Since the Compile methods will compile
907 /// statements and expressions into the same
908 /// delegate, you can tell if a value was returned
909 /// by checking whether the returned value is of type
913 public delegate void CompiledMethod (ref object retvalue);
916 /// The default base class for every interaction line
919 /// The expressions and statements behave as if they were
920 /// a static method of this class. The InteractiveBase class
921 /// contains a number of useful methods, but can be overwritten
922 /// by setting the InteractiveBaseType property in the Evaluator
924 public class InteractiveBase {
926 /// Determines where the standard output of methods in this class will go.
928 public static TextWriter Output = Console.Out;
931 /// Determines where the standard error of methods in this class will go.
933 public static TextWriter Error = Console.Error;
936 /// The primary prompt used for interactive use.
938 public static string Prompt = "csharp> ";
941 /// The secondary prompt used for interactive use (used when
942 /// an expression is incomplete).
944 public static string ContinuationPrompt = " > ";
947 /// Used to signal that the user has invoked the `quit' statement.
949 public static bool QuitRequested;
952 /// Shows all the variables defined so far.
954 static public void ShowVars ()
956 Output.Write (Evaluator.GetVars ());
961 /// Displays the using statements in effect at this point.
963 static public void ShowUsing ()
965 Output.Write (Evaluator.GetUsing ());
969 public delegate void Simple ();
972 /// Times the execution of the given delegate
974 static public TimeSpan Time (Simple a)
976 DateTime start = DateTime.Now;
978 return DateTime.Now - start;
983 /// Loads the assemblies from a package
986 /// Loads the assemblies from a package. This is equivalent
987 /// to passing the -pkg: command line flag to the C# compiler
988 /// on the command line.
990 static public void LoadPackage (string pkg)
993 Error.WriteLine ("Invalid package specified");
997 string pkgout = Driver.GetPackageFlags (pkg, false, RootContext.ToplevelTypes.Compiler.Report);
1001 string [] xargs = pkgout.Trim (new Char [] {' ', '\n', '\r', '\t'}).
1002 Split (new Char [] { ' ', '\t'});
1004 foreach (string s in xargs){
1005 if (s.StartsWith ("-r:") || s.StartsWith ("/r:") || s.StartsWith ("/reference:")){
1006 string lib = s.Substring (s.IndexOf (':')+1);
1008 Evaluator.LoadAssembly (lib);
1016 /// Loads the assembly
1019 /// Loads the specified assembly and makes its types
1020 /// available to the evaluator. This is equivalent
1021 /// to passing the -pkg: command line flag to the C#
1022 /// compiler on the command line.
1024 static public void LoadAssembly (string assembly)
1026 Evaluator.LoadAssembly (assembly);
1030 /// Returns a list of available static methods.
1032 static public string help {
1034 return "Static methods:\n" +
1035 " Describe (object) - Describes the object's type\n" +
1036 " LoadPackage (package); - Loads the given Package (like -pkg:FILE)\n" +
1037 " LoadAssembly (assembly) - Loads the given assembly (like -r:ASSEMBLY)\n" +
1038 " ShowVars (); - Shows defined local variables.\n" +
1039 " ShowUsing (); - Show active using declarations.\n" +
1040 " Prompt - The prompt used by the C# shell\n" +
1041 " ContinuationPrompt - The prompt for partial input\n" +
1042 " Time(() -> { }) - Times the specified code\n" +
1043 " quit; - You'll never believe it - this quits the repl!\n" +
1044 " help; - This help text\n";
1049 /// Indicates to the read-eval-print-loop that the interaction should be finished.
1051 static public object quit {
1053 QuitRequested = true;
1055 // To avoid print null at the exit
1056 return typeof (Evaluator.NoValueSet);
1062 /// Describes an object or a type.
1065 /// This method will show a textual representation
1066 /// of the object's type. If the object is a
1067 /// System.Type it renders the type directly,
1068 /// otherwise it renders the type returned by
1069 /// invoking GetType on the object.
1071 static public string Describe (object x)
1076 var type = x as Type ?? x.GetType ();
1078 StringWriter sw = new StringWriter ();
1079 new Outline (type, sw, true, false, false).OutlineType ();
1080 return sw.ToString ();
1085 class HoistedEvaluatorVariable : HoistedVariable
1087 public HoistedEvaluatorVariable (Field field)
1088 : base (null, field)
1092 public override void EmitSymbolInfo ()
1096 protected override FieldExpr GetFieldExpression (EmitContext ec)
1098 return new FieldExpr (field, field.Location);
1103 /// A class used to assign values if the source expression is not void
1105 /// Used by the interactive shell to allow it to call this code to set
1106 /// the return value for an invocation.
1108 class OptionalAssign : SimpleAssign {
1109 public OptionalAssign (Expression t, Expression s, Location loc)
1114 protected override Expression DoResolve (ResolveContext ec)
1116 CloneContext cc = new CloneContext ();
1117 Expression clone = source.Clone (cc);
1120 // A useful feature for the REPL: if we can resolve the expression
1121 // as a type, Describe the type;
1123 if (Evaluator.DescribeTypeExpressions){
1124 var old_printer = Evaluator.SetPrinter (new StreamReportPrinter (TextWriter.Null));
1125 clone = clone.Resolve (ec);
1127 clone = source.Clone (cc);
1128 clone = clone.Resolve (ec, ResolveFlags.Type);
1130 Evaluator.SetPrinter (old_printer);
1131 clone = source.Clone (cc);
1132 clone = clone.Resolve (ec);
1136 Arguments args = new Arguments (1);
1137 args.Add (new Argument (new TypeOf ((TypeExpr) clone, Location)));
1138 source = new Invocation (new SimpleName ("Describe", Location), args).Resolve (ec);
1140 Evaluator.SetPrinter (old_printer);
1142 clone = clone.Resolve (ec);
1147 // This means its really a statement.
1148 if (clone.Type == TypeManager.void_type || clone is DynamicInvocation || clone is Assign) {
1152 return base.DoResolve (ec);
1157 List<KeyValuePair<TypeContainer, TypeContainer>> undo_types;
1161 undo_types = new List<KeyValuePair<TypeContainer, TypeContainer>> ();
1164 public void AddTypeContainer (TypeContainer current_container, TypeContainer tc)
1166 if (current_container == tc){
1167 Console.Error.WriteLine ("Internal error: inserting container into itself");
1171 if (undo_types == null)
1172 undo_types = new List<KeyValuePair<TypeContainer, TypeContainer>> ();
1174 undo_types.Add (new KeyValuePair<TypeContainer, TypeContainer> (current_container, tc));
1177 public void ExecuteUndo ()
1179 if (undo_types == null)
1182 foreach (var p in undo_types){
1183 TypeContainer current_container = p.Key;
1185 current_container.RemoveTypeContainer (p.Value);