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;
15 using System.Reflection;
16 using System.Reflection.Emit;
18 using System.Globalization;
21 namespace Mono.CSharp {
24 /// Evaluator: provides an API to evaluate C# statements and
25 /// expressions dynamically.
28 /// This class exposes static methods to evaluate expressions in the
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.
35 /// To interrupt execution of a statement, you can invoke the
36 /// Evaluator.Interrupt method.
38 public class Evaluator {
40 static object evaluator_lock = new object ();
42 static string current_debug_name;
44 static Thread invoke_thread;
46 static ArrayList using_alias_list = new ArrayList ();
47 static ArrayList using_list = new ArrayList ();
48 static Hashtable fields = new Hashtable ();
50 static Type interactive_base_class = typeof (InteractiveBase);
55 /// Optional initialization for the Evaluator.
58 /// Initializes the Evaluator with the command line options
59 /// that would be processed by the command line compiler. Only
60 /// the first call to Init will work, any future invocations are
63 /// You can safely avoid calling this method if your application
64 /// does not need any of the features exposed by the command line
67 public static void Init (string [] args)
69 lock (evaluator_lock){
73 RootContext.Version = LanguageVersion.Default;
74 driver = Driver.Create (args, false);
76 throw new Exception ("Failed to create compiler driver with the given arguments");
78 driver.ProcessDefaultConfig ();
79 CompilerCallableEntryPoint.Reset ();
80 driver.LoadReferences ();
81 RootContext.EvalMode = true;
88 Init (new string [0]);
93 CompilerCallableEntryPoint.PartialReset ();
96 // PartialReset should not reset the core types, this is very redundant.
98 if (!TypeManager.InitCoreTypes ())
99 throw new Exception ("Failed to InitCoreTypes");
100 TypeManager.InitOptionalCoreTypes ();
102 Location.AddFile ("{interactive}");
103 Location.Initialize ();
105 current_debug_name = "interactive" + (count++) + ".dll";
106 if (Environment.GetEnvironmentVariable ("SAVE") != null){
107 CodeGen.Init (current_debug_name, current_debug_name, false);
109 CodeGen.InitDynamic (current_debug_name);
113 /// The base class for the classes that host the user generated code
117 /// This is the base class that will host the code
118 /// executed by the Evaluator. By default
119 /// this is the Mono.CSharp.InteractiveBase class
120 /// which is useful for interactive use.
122 /// By changing this property you can control the
123 /// base class and the static members that are
124 /// available to your evaluated code.
126 static public Type InteractiveBaseClass {
128 return interactive_base_class;
133 throw new ArgumentNullException ();
135 lock (evaluator_lock)
136 interactive_base_class = value;
141 /// Interrupts the evaluation of an expression executing in Evaluate.
144 /// Use this method to interrupt long-running invocations.
146 public static void Interrupt ()
148 if (!inited || !invoking)
151 if (invoke_thread != null)
152 invoke_thread.Abort ();
156 /// Compiles the input string and returns a delegate that represents the compiled code.
160 /// Compiles the input string as a C# expression or
161 /// statement, unlike the Evaluate method, the
162 /// resulting delegate can be invoked multiple times
163 /// without incurring in the compilation overhead.
165 /// If the return value of this function is null,
166 /// this indicates that the parsing was complete.
167 /// If the return value is a string it indicates
168 /// that the input string was partial and that the
169 /// invoking code should provide more code before
170 /// the code can be successfully compiled.
172 /// If you know that you will always get full expressions or
173 /// statements and do not care about partial input, you can use
174 /// the other Compile overload.
176 /// On success, in addition to returning null, the
177 /// compiled parameter will be set to the delegate
178 /// that can be invoked to execute the code.
181 static public string Compile (string input, out CompiledMethod compiled)
183 if (input == null || input.Length == 0){
188 lock (evaluator_lock){
193 CSharpParser parser = ParseString (true, input, out partial_input);
199 ParseString (false, input, out partial_input);
203 object parser_result = parser.InteractiveResult;
205 if (!(parser_result is Class)){
206 int errors = Report.Errors;
208 NamespaceEntry.VerifyAllUsing ();
209 if (errors == Report.Errors)
210 parser.CurrentNamespace.Extract (using_alias_list, using_list);
213 compiled = CompileBlock (parser_result as Class, parser.undo);
220 /// Compiles the input string and returns a delegate that represents the compiled code.
224 /// Compiles the input string as a C# expression or
225 /// statement, unlike the Evaluate method, the
226 /// resulting delegate can be invoked multiple times
227 /// without incurring in the compilation overhead.
229 /// This method can only deal with fully formed input
230 /// strings and does not provide a completion mechanism.
231 /// If you must deal with partial input (for example for
232 /// interactive use) use the other overload.
234 /// On success, a delegate is returned that can be used
235 /// to invoke the method.
238 static public CompiledMethod Compile (string input)
240 CompiledMethod compiled;
242 // Ignore partial inputs
243 if (Compile (input, out compiled) != null){
244 // Error, the input was partial.
248 // Either null (on error) or the compiled method.
253 // Todo: Should we handle errors, or expect the calling code to setup
254 // the recording themselves?
258 /// Evaluates and expression or statement and returns any result values.
261 /// Evaluates the input string as a C# expression or
262 /// statement. If the input string is an expression
263 /// the result will be stored in the result variable
264 /// and the result_set variable will be set to true.
266 /// It is necessary to use the result/result_set
267 /// pair to identify when a result was set (for
268 /// example, execution of user-provided input can be
269 /// an expression, a statement or others, and
270 /// result_set would only be set if the input was an
273 /// If the return value of this function is null,
274 /// this indicates that the parsing was complete.
275 /// If the return value is a string, it indicates
276 /// that the input is partial and that the user
277 /// should provide an updated string.
279 public static string Evaluate (string input, out object result, out bool result_set)
281 CompiledMethod compiled;
286 input = Compile (input, out compiled);
290 if (compiled == null)
294 // The code execution does not need to keep the compiler lock
296 object retval = typeof (NoValueSet);
299 invoke_thread = System.Threading.Thread.CurrentThread;
301 compiled (ref retval);
302 } catch (ThreadAbortException e){
303 Thread.ResetAbort ();
304 Console.WriteLine ("Interrupted!\n{0}", e);
310 // We use a reference to a compiler type, in this case
311 // Driver as a flag to indicate that this was a statement
313 if (retval != typeof (NoValueSet)){
322 /// Executes the given expression or statement.
325 /// Executes the provided statement, returns true
326 /// on success, false on parsing errors. Exceptions
327 /// might be thrown by the called code.
329 public static bool Run (string statement)
337 bool ok = Evaluate (statement, out result, out result_set) == null;
343 /// Evaluates and expression or statement and returns the result.
346 /// Evaluates the input string as a C# expression or
347 /// statement and returns the value.
349 /// This method will throw an exception if there is a syntax error,
350 /// of if the provided input is not an expression but a statement.
352 public static object Evaluate (string input)
357 string r = Evaluate (input, out result, out result_set);
360 throw new ArgumentException ("Syntax error on input: partial input");
362 if (result_set == false)
363 throw new ArgumentException ("The expression did not set a result");
370 StatementOrExpression,
376 // Deambiguates the input string to determine if we
377 // want to process a statement or if we want to
378 // process a compilation unit.
380 // This is done using a top-down predictive parser,
381 // since the yacc/jay parser can not deambiguage this
382 // without more than one lookahead token. There are very
385 static InputKind ToplevelOrStatement (SeekableStreamReader seekable)
387 Tokenizer tokenizer = new Tokenizer (seekable, (CompilationUnit) Location.SourceFiles [0]);
389 int t = tokenizer.token ();
392 return InputKind.EOF;
394 // These are toplevels
396 case Token.OPEN_BRACKET:
400 case Token.INTERFACE:
402 case Token.NAMESPACE:
404 case Token.PROTECTED:
409 return InputKind.CompilationUnit;
411 // Definitely expression
428 return InputKind.StatementOrExpression;
430 // These need deambiguation help
432 t = tokenizer.token ();
434 return InputKind.EOF;
436 if (t == Token.IDENTIFIER)
437 return InputKind.CompilationUnit;
438 return InputKind.StatementOrExpression;
441 // Distinguish between:
442 // delegate opt_anonymous_method_signature block
445 t = tokenizer.token ();
447 return InputKind.EOF;
448 if (t == Token.OPEN_PARENS || t == Token.OPEN_BRACE)
449 return InputKind.StatementOrExpression;
450 return InputKind.CompilationUnit;
452 // Distinguih between:
454 // unsafe as modifier of a type declaration
456 t = tokenizer.token ();
458 return InputKind.EOF;
459 if (t == Token.OPEN_PARENS)
460 return InputKind.StatementOrExpression;
461 return InputKind.CompilationUnit;
463 // These are errors: we list explicitly what we had
464 // from the grammar, ERROR and then everything else
469 return InputKind.Error;
471 // This catches everything else allowed by
472 // expressions. We could add one-by-one use cases
475 return InputKind.StatementOrExpression;
480 // Parses the string @input and returns a CSharpParser if succeeful.
482 // if @silent is set to true then no errors are
483 // reported to the user. This is used to do various calls to the
484 // parser and check if the expression is parsable.
486 // @partial_input: if @silent is true, then it returns whether the
487 // parsed expression was partial, and more data is needed
489 static CSharpParser ParseString (bool silent, string input, out bool partial_input)
491 partial_input = false;
493 queued_fields.Clear ();
495 Stream s = new MemoryStream (Encoding.Default.GetBytes (input));
496 SeekableStreamReader seekable = new SeekableStreamReader (s, Encoding.Default);
498 InputKind kind = ToplevelOrStatement (seekable);
499 if (kind == InputKind.Error){
501 Report.Error (-25, "Detection Parsing Error");
502 partial_input = false;
506 if (kind == InputKind.EOF){
508 Console.Error.WriteLine ("Internal error: EOF condition should have been detected in a previous call with silent=true");
509 partial_input = true;
513 seekable.Position = 0;
515 CSharpParser parser = new CSharpParser (seekable, (CompilationUnit) Location.SourceFiles [0]);
516 parser.ErrorOutput = Report.Stderr;
518 if (kind == InputKind.StatementOrExpression){
519 parser.Lexer.putback_char = Tokenizer.EvalStatementParserCharacter;
520 RootContext.StatementMode = true;
523 // Do not activate EvalCompilationUnitParserCharacter until
524 // I have figured out all the limitations to invoke methods
525 // in the generated classes. See repl.txt
527 parser.Lexer.putback_char = Tokenizer.EvalUsingDeclarationsParserCharacter;
528 //parser.Lexer.putback_char = Tokenizer.EvalCompilationUnitParserCharacter;
529 RootContext.StatementMode = false;
533 Report.DisableReporting ();
537 if (Report.Errors != 0){
538 if (silent && parser.UnexpectedEOF)
539 partial_input = true;
541 parser.undo.ExecuteUndo ();
546 Report.EnableReporting ();
552 // Queue all the fields that we use, as we need to then go from FieldBuilder to FieldInfo
553 // or reflection gets confused (it basically gets confused, and variables override each
556 static ArrayList queued_fields = new ArrayList ();
558 //static ArrayList types = new ArrayList ();
560 static volatile bool invoking;
562 static CompiledMethod CompileBlock (Class host, Undo undo)
564 RootContext.ResolveTree ();
565 if (Report.Errors != 0){
570 RootContext.PopulateTypes ();
572 if (Report.Errors != 0){
577 TypeBuilder tb = null;
578 MethodBuilder mb = null;
581 tb = host.TypeBuilder;
583 foreach (MemberCore member in host.Methods){
584 if (member.Name != "Host")
587 MethodOrOperator method = (MethodOrOperator) member;
588 mb = method.MethodBuilder;
593 throw new Exception ("Internal error: did not find the method builder for the generated method");
596 RootContext.EmitCode ();
597 if (Report.Errors != 0)
600 RootContext.CloseTypes ();
602 if (Environment.GetEnvironmentVariable ("SAVE") != null)
603 CodeGen.Save (current_debug_name, false);
609 // Unlike Mono, .NET requires that the MethodInfo is fetched, it cant
610 // work from MethodBuilders. Retarded, I know.
612 Type tt = CodeGen.Assembly.Builder.GetType (tb.Name);
613 MethodInfo mi = tt.GetMethod (mb.Name);
615 // Pull the FieldInfos from the type, and keep track of them
616 foreach (Field field in queued_fields){
617 FieldInfo fi = tt.GetField (field.Name);
619 FieldInfo old = (FieldInfo) fields [field.Name];
621 // If a previous value was set, nullify it, so that we do
624 if (TypeManager.IsStruct (old.FieldType)){
626 // TODO: Clear fields for structs
630 old.SetValue (null, null);
636 fields [field.Name] = fi;
640 queued_fields.Clear ();
642 return (CompiledMethod) System.Delegate.CreateDelegate (typeof (CompiledMethod), mi);
645 static internal void LoadAliases (NamespaceEntry ns)
647 ns.Populate (using_alias_list, using_list);
651 /// A sentinel value used to indicate that no value was
652 /// was set by the compiled function. This is used to
653 /// differentiate between a function not returning a
656 public class NoValueSet {
659 static internal FieldInfo LookupField (string name)
661 FieldInfo fi = (FieldInfo) fields [name];
667 // Puts the FieldBuilder into a queue of names that will be
668 // registered. We can not register FieldBuilders directly
669 // we need to fetch the FieldInfo after Reflection cooks the
670 // types, or bad things happen (bad means: FieldBuilders behave
671 // incorrectly across multiple assemblies, causing assignments to
674 // This also serves for the parser to register Field classes
675 // that should be exposed as global variables
677 static internal void QueueField (Field f)
679 queued_fields.Add (f);
682 static string Quote (string s)
684 if (s.IndexOf ('"') != -1)
685 s = s.Replace ("\"", "\\\"");
687 return "\"" + s + "\"";
690 static public string GetUsing ()
692 lock (evaluator_lock){
693 StringBuilder sb = new StringBuilder ();
695 foreach (object x in using_alias_list)
696 sb.Append (String.Format ("using {0};\n", x));
698 foreach (object x in using_list)
699 sb.Append (String.Format ("using {0};\n", x));
701 return sb.ToString ();
705 static public string GetVars ()
707 lock (evaluator_lock){
708 StringBuilder sb = new StringBuilder ();
710 foreach (DictionaryEntry de in fields){
711 FieldInfo fi = LookupField ((string) de.Key);
718 value = fi.GetValue (null);
720 value = Quote ((string)value);
726 sb.Append (String.Format ("{0} {1} <error reading value>", TypeManager.CSharpName(fi.FieldType), de.Key));
728 sb.Append (String.Format ("{0} {1} = {2}", TypeManager.CSharpName(fi.FieldType), de.Key, value));
731 return sb.ToString ();
736 /// Loads the given assembly and exposes the API to the user.
738 static public void LoadAssembly (string file)
740 lock (evaluator_lock){
741 Driver.LoadAssembly (file, false);
742 GlobalRootNamespace.Instance.ComputeNamespaces ();
747 /// Exposes the API of the given assembly to the Evaluator
749 static public void ReferenceAssembly (Assembly a)
751 lock (evaluator_lock){
752 GlobalRootNamespace.Instance.AddAssemblyReference (a);
753 GlobalRootNamespace.Instance.ComputeNamespaces ();
761 /// A delegate that can be used to invoke the
762 /// compiled expression or statement.
765 /// Since the Compile methods will compile
766 /// statements and expressions into the same
767 /// delegate, you can tell if a value was returned
768 /// by checking whether the returned value is of type
772 public delegate void CompiledMethod (ref object retvalue);
775 /// The default base class for every interaction line
778 /// The expressions and statements behave as if they were
779 /// a static method of this class. The InteractiveBase class
780 /// contains a number of useful methods, but can be overwritten
781 /// by setting the InteractiveBaseType property in the Evaluator
783 public class InteractiveBase {
785 /// Determines where the standard output of methods in this class will go.
787 public static TextWriter Output = Console.Out;
790 /// Determines where the standard error of methods in this class will go.
792 public static TextWriter Error = Console.Error;
795 /// The primary prompt used for interactive use.
797 public static string Prompt = "csharp> ";
800 /// The secondary prompt used for interactive use (used when
801 /// an expression is incomplete).
803 public static string ContinuationPrompt = " > ";
806 /// Used to signal that the user has invoked the `quit' statement.
808 public static bool QuitRequested;
811 /// Shows all the variables defined so far.
813 static public void ShowVars ()
815 Output.Write (Evaluator.GetVars ());
820 /// Displays the using statements in effect at this point.
822 static public void ShowUsing ()
824 Output.Write (Evaluator.GetUsing ());
828 public delegate void Simple ();
831 /// Times the execution of the given delegate
833 static public TimeSpan Time (Simple a)
835 DateTime start = DateTime.Now;
837 return DateTime.Now - start;
842 /// Loads the assemblies from a package
845 /// Loads the assemblies from a package. This is equivalent
846 /// to passing the -pkg: command line flag to the C# compiler
847 /// on the command line.
849 static public void LoadPackage (string pkg)
852 Error.WriteLine ("Invalid package specified");
856 string pkgout = Driver.GetPackageFlags (pkg, false);
860 string [] xargs = pkgout.Trim (new Char [] {' ', '\n', '\r', '\t'}).
861 Split (new Char [] { ' ', '\t'});
863 foreach (string s in xargs){
864 if (s.StartsWith ("-r:") || s.StartsWith ("/r:") || s.StartsWith ("/reference:")){
865 string lib = s.Substring (s.IndexOf (':')+1);
867 Evaluator.LoadAssembly (lib);
875 /// Loads the assembly
878 /// Loads the specified assembly and makes its types
879 /// available to the evaluator. This is equivalent
880 /// to passing the -pkg: command line flag to the C#
881 /// compiler on the command line.
883 static public void LoadAssembly (string assembly)
885 Evaluator.LoadAssembly (assembly);
889 /// Returns a list of available static methods.
891 static public string help {
893 return "Static methods:\n"+
894 " Describe(obj) - Describes the object's type\n" +
895 " LoadPackage (pkg); - Loads the given Package (like -pkg:FILE)\n" +
896 " LoadAssembly (ass) - Loads the given assembly (like -r:ASS)\n" +
897 " ShowVars (); - Shows defined local variables.\n" +
898 " ShowUsing (); - Show active using decltions.\n" +
899 " Prompt - The prompt used by the C# shell\n" +
900 " ContinuationPrompt - The prompt for partial input\n" +
901 " Time(() -> { }) - Times the specified code\n" +
908 /// Indicates to the read-eval-print-loop that the interaction should be finished.
910 static public object quit {
912 QuitRequested = true;
919 /// Describes an object or a type.
922 /// This method will show a textual representation
923 /// of the object's type. If the object is a
924 /// System.Type it renders the type directly,
925 /// otherwise it renders the type returned by
926 /// invoking GetType on the object.
928 static public string Describe (object x)
937 StringWriter sw = new StringWriter ();
938 new Outline (t, sw, true, false, false).OutlineType ();
939 return sw.ToString ();
945 // A local variable reference that will create a Field in a
946 // Class with the resolved type. This is necessary so we can
947 // support "var" as a field type in a class declaration.
949 // We allow LocalVariableReferece to do the heavy lifting, and
950 // then we insert the field with the resolved type
952 public class LocalVariableReferenceWithClassSideEffect : LocalVariableReference {
953 TypeContainer container;
956 public LocalVariableReferenceWithClassSideEffect (TypeContainer container, string name, Block current_block, string local_variable_id, Location loc)
957 : base (current_block, local_variable_id, loc)
959 this.container = container;
963 public override bool Equals (object obj)
965 LocalVariableReferenceWithClassSideEffect lvr = obj as LocalVariableReferenceWithClassSideEffect;
969 if (lvr.name != name || lvr.container != container)
972 return base.Equals (obj);
975 public override int GetHashCode ()
977 return name.GetHashCode ();
980 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
982 Expression ret = base.DoResolveLValue (ec, right_side);
986 Field f = new Field (container, new TypeExpression (ret.Type, Location),
987 Modifiers.PUBLIC | Modifiers.STATIC,
988 new MemberName (name, Location), null);
989 container.AddField (f);
991 Evaluator.QueueField (f);
998 /// A class used to assign values if the source expression is not void
1000 /// Used by the interactive shell to allow it to call this code to set
1001 /// the return value for an invocation.
1003 class OptionalAssign : SimpleAssign {
1004 public OptionalAssign (Expression t, Expression s, Location loc)
1009 public override Expression DoResolve (EmitContext ec)
1011 CloneContext cc = new CloneContext ();
1012 Expression clone = source.Clone (cc);
1014 clone = clone.Resolve (ec);
1018 // This means its really a statement.
1019 if (clone.Type == TypeManager.void_type){
1020 source = source.Resolve (ec);
1022 type = TypeManager.void_type;
1023 eclass = ExprClass.Value;
1027 return base.DoResolve (ec);
1030 public override void Emit (EmitContext ec)
1038 public override void EmitStatement (EmitContext ec)
1043 base.EmitStatement (ec);
1048 ArrayList undo_types;
1052 undo_types = new ArrayList ();
1055 public void AddTypeContainer (TypeContainer current_container, TypeContainer tc)
1057 if (current_container == tc){
1058 Console.Error.WriteLine ("Internal error: inserting container into itself");
1062 if (undo_types == null)
1063 undo_types = new ArrayList ();
1064 undo_types.Add (new Pair (current_container, tc));
1067 public void ExecuteUndo ()
1069 if (undo_types == null)
1072 foreach (Pair p in undo_types){
1073 TypeContainer current_container = (TypeContainer) p.First;
1075 current_container.RemoveTypeContainer ((TypeContainer) p.Second);