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 parser.CurrentNamespace.Extract (using_alias_list, using_list);
208 compiled = CompileBlock (parser_result as Class, parser.undo);
215 /// Compiles the input string and returns a delegate that represents the compiled code.
219 /// Compiles the input string as a C# expression or
220 /// statement, unlike the Evaluate method, the
221 /// resulting delegate can be invoked multiple times
222 /// without incurring in the compilation overhead.
224 /// This method can only deal with fully formed input
225 /// strings and does not provide a completion mechanism.
226 /// If you must deal with partial input (for example for
227 /// interactive use) use the other overload.
229 /// On success, a delegate is returned that can be used
230 /// to invoke the method.
233 static public CompiledMethod Compile (string input)
235 CompiledMethod compiled;
237 // Ignore partial inputs
238 if (Compile (input, out compiled) != null){
239 // Error, the input was partial.
243 // Either null (on error) or the compiled method.
248 // Todo: Should we handle errors, or expect the calling code to setup
249 // the recording themselves?
253 /// Evaluates and expression or statement and returns any result values.
256 /// Evaluates the input string as a C# expression or
257 /// statement. If the input string is an expression
258 /// the result will be stored in the result variable
259 /// and the result_set variable will be set to true.
261 /// It is necessary to use the result/result_set
262 /// pair to identify when a result was set (for
263 /// example, execution of user-provided input can be
264 /// an expression, a statement or others, and
265 /// result_set would only be set if the input was an
268 /// If the return value of this function is null,
269 /// this indicates that the parsing was complete.
270 /// If the return value is a string, it indicates
271 /// that the input is partial and that the user
272 /// should provide an updated string.
274 public static string Evaluate (string input, out object result, out bool result_set)
276 CompiledMethod compiled;
281 input = Compile (input, out compiled);
285 if (compiled == null)
289 // The code execution does not need to keep the compiler lock
291 object retval = typeof (NoValueSet);
294 invoke_thread = System.Threading.Thread.CurrentThread;
296 compiled (ref retval);
297 } catch (ThreadAbortException e){
298 Thread.ResetAbort ();
299 Console.WriteLine ("Interrupted!\n{0}", e);
305 // We use a reference to a compiler type, in this case
306 // Driver as a flag to indicate that this was a statement
308 if (retval != typeof (NoValueSet)){
317 /// Executes the given expression or statement.
320 /// Executes the provided statement, returns true
321 /// on success, false on parsing errors. Exceptions
322 /// might be thrown by the called code.
324 public static bool Run (string statement)
332 bool ok = Evaluate (statement, out result, out result_set) == null;
338 /// Evaluates and expression or statement and returns the result.
341 /// Evaluates the input string as a C# expression or
342 /// statement and returns the value.
344 /// This method will throw an exception if there is a syntax error,
345 /// of if the provided input is not an expression but a statement.
347 public static object Evaluate (string input)
352 string r = Evaluate (input, out result, out result_set);
355 throw new ArgumentException ("Syntax error on input: partial input");
357 if (result_set == false)
358 throw new ArgumentException ("The expression did not set a result");
365 StatementOrExpression,
371 // Deambiguates the input string to determine if we
372 // want to process a statement or if we want to
373 // process a compilation unit.
375 // This is done using a top-down predictive parser,
376 // since the yacc/jay parser can not deambiguage this
377 // without more than one lookahead token. There are very
380 static InputKind ToplevelOrStatement (SeekableStreamReader seekable)
382 Tokenizer tokenizer = new Tokenizer (seekable, Location.SourceFiles [0]);
384 int t = tokenizer.token ();
387 return InputKind.EOF;
389 // These are toplevels
391 case Token.OPEN_BRACKET:
395 case Token.INTERFACE:
397 case Token.NAMESPACE:
399 case Token.PROTECTED:
404 return InputKind.CompilationUnit;
406 // Definitely expression
423 return InputKind.StatementOrExpression;
425 // These need deambiguation help
427 t = tokenizer.token ();
429 return InputKind.EOF;
431 if (t == Token.IDENTIFIER)
432 return InputKind.CompilationUnit;
433 return InputKind.StatementOrExpression;
436 // Distinguish between:
437 // delegate opt_anonymous_method_signature block
440 t = tokenizer.token ();
442 return InputKind.EOF;
443 if (t == Token.OPEN_PARENS || t == Token.OPEN_BRACE)
444 return InputKind.StatementOrExpression;
445 return InputKind.CompilationUnit;
447 // Distinguih between:
449 // unsafe as modifier of a type declaration
451 t = tokenizer.token ();
453 return InputKind.EOF;
454 if (t == Token.OPEN_PARENS)
455 return InputKind.StatementOrExpression;
456 return InputKind.CompilationUnit;
458 // These are errors: we list explicitly what we had
459 // from the grammar, ERROR and then everything else
464 return InputKind.Error;
466 // This catches everything else allowed by
467 // expressions. We could add one-by-one use cases
470 return InputKind.StatementOrExpression;
475 // Parses the string @input and returns a CSharpParser if succeeful.
477 // if @silent is set to true then no errors are
478 // reported to the user. This is used to do various calls to the
479 // parser and check if the expression is parsable.
481 // @partial_input: if @silent is true, then it returns whether the
482 // parsed expression was partial, and more data is needed
484 static CSharpParser ParseString (bool silent, string input, out bool partial_input)
486 partial_input = false;
488 queued_fields.Clear ();
490 Stream s = new MemoryStream (Encoding.Default.GetBytes (input));
491 SeekableStreamReader seekable = new SeekableStreamReader (s, Encoding.Default);
493 InputKind kind = ToplevelOrStatement (seekable);
494 if (kind == InputKind.Error){
496 Report.Error (-25, "Detection Parsing Error");
497 partial_input = false;
501 if (kind == InputKind.EOF){
503 Console.Error.WriteLine ("Internal error: EOF condition should have been detected in a previous call with silent=true");
504 partial_input = true;
508 seekable.Position = 0;
510 CSharpParser parser = new CSharpParser (seekable, Location.SourceFiles [0]);
511 parser.ErrorOutput = Report.Stderr;
513 if (kind == InputKind.StatementOrExpression){
514 parser.Lexer.putback_char = Tokenizer.EvalStatementParserCharacter;
515 RootContext.StatementMode = true;
518 // Do not activate EvalCompilationUnitParserCharacter until
519 // I have figured out all the limitations to invoke methods
520 // in the generated classes. See repl.txt
522 parser.Lexer.putback_char = Tokenizer.EvalUsingDeclarationsParserCharacter;
523 //parser.Lexer.putback_char = Tokenizer.EvalCompilationUnitParserCharacter;
524 RootContext.StatementMode = false;
528 Report.DisableReporting ();
532 if (Report.Errors != 0){
533 if (silent && parser.UnexpectedEOF)
534 partial_input = true;
536 parser.undo.ExecuteUndo ();
541 Report.EnableReporting ();
547 // Queue all the fields that we use, as we need to then go from FieldBuilder to FieldInfo
548 // or reflection gets confused (it basically gets confused, and variables override each
551 static ArrayList queued_fields = new ArrayList ();
553 //static ArrayList types = new ArrayList ();
555 static volatile bool invoking;
557 static CompiledMethod CompileBlock (Class host, Undo undo)
559 RootContext.ResolveTree ();
560 if (Report.Errors != 0){
565 RootContext.PopulateTypes ();
567 if (Report.Errors != 0){
572 TypeBuilder tb = null;
573 MethodBuilder mb = null;
576 tb = host.TypeBuilder;
578 foreach (MemberCore member in host.Methods){
579 if (member.Name != "Host")
582 MethodOrOperator method = (MethodOrOperator) member;
583 mb = method.MethodBuilder;
588 throw new Exception ("Internal error: did not find the method builder for the generated method");
591 RootContext.EmitCode ();
592 if (Report.Errors != 0)
595 RootContext.CloseTypes ();
597 if (Environment.GetEnvironmentVariable ("SAVE") != null)
598 CodeGen.Save (current_debug_name, false);
604 // Unlike Mono, .NET requires that the MethodInfo is fetched, it cant
605 // work from MethodBuilders. Retarded, I know.
607 Type tt = CodeGen.Assembly.Builder.GetType (tb.Name);
608 MethodInfo mi = tt.GetMethod (mb.Name);
610 // Pull the FieldInfos from the type, and keep track of them
611 foreach (Field field in queued_fields){
612 FieldInfo fi = tt.GetField (field.Name);
614 FieldInfo old = (FieldInfo) fields [field.Name];
616 // If a previous value was set, nullify it, so that we do
619 if (old.FieldType.IsValueType){
621 // TODO: Clear fields for structs
625 old.SetValue (null, null);
631 fields [field.Name] = fi;
635 queued_fields.Clear ();
637 return (CompiledMethod) System.Delegate.CreateDelegate (typeof (CompiledMethod), mi);
640 static internal void LoadAliases (NamespaceEntry ns)
642 ns.Populate (using_alias_list, using_list);
646 /// A sentinel value used to indicate that no value was
647 /// was set by the compiled function. This is used to
648 /// differentiate between a function not returning a
651 public class NoValueSet {
654 static internal FieldInfo LookupField (string name)
656 FieldInfo fi = (FieldInfo) fields [name];
662 // Puts the FieldBuilder into a queue of names that will be
663 // registered. We can not register FieldBuilders directly
664 // we need to fetch the FieldInfo after Reflection cooks the
665 // types, or bad things happen (bad means: FieldBuilders behave
666 // incorrectly across multiple assemblies, causing assignments to
669 // This also serves for the parser to register Field classes
670 // that should be exposed as global variables
672 static internal void QueueField (Field f)
674 queued_fields.Add (f);
677 static string Quote (string s)
679 if (s.IndexOf ('"') != -1)
680 s = s.Replace ("\"", "\\\"");
682 return "\"" + s + "\"";
685 static public string GetUsing ()
687 lock (evaluator_lock){
688 StringBuilder sb = new StringBuilder ();
690 foreach (object x in using_alias_list)
691 sb.Append (String.Format ("using {0};\n", x));
693 foreach (object x in using_list)
694 sb.Append (String.Format ("using {0};\n", x));
696 return sb.ToString ();
700 static public string GetVars ()
702 lock (evaluator_lock){
703 StringBuilder sb = new StringBuilder ();
705 foreach (DictionaryEntry de in fields){
706 FieldInfo fi = LookupField ((string) de.Key);
713 value = fi.GetValue (null);
715 value = Quote ((string)value);
721 sb.Append (String.Format ("{0} {1} <error reading value>", TypeManager.CSharpName(fi.FieldType), de.Key));
723 sb.Append (String.Format ("{0} {1} = {2}", TypeManager.CSharpName(fi.FieldType), de.Key, value));
726 return sb.ToString ();
731 /// Loads the given assembly and exposes the API to the user.
733 static public void LoadAssembly (string file)
735 lock (evaluator_lock){
736 Driver.LoadAssembly (file, true);
737 RootNamespace.ComputeNamespaces ();
742 /// Exposes the API of the given assembly to the Evaluator
744 static public void ReferenceAssembly (Assembly a)
746 lock (evaluator_lock){
747 RootNamespace.Global.AddAssemblyReference (a);
748 RootNamespace.ComputeNamespaces ();
756 /// A delegate that can be used to invoke the
757 /// compiled expression or statement.
760 /// Since the Compile methods will compile
761 /// statements and expressions into the same
762 /// delegate, you can tell if a value was returned
763 /// by checking whether the returned value is of type
767 public delegate void CompiledMethod (ref object retvalue);
769 /// The default base class for every interaction line
771 public class InteractiveBase {
772 public static TextWriter Output = Console.Out;
773 public static TextWriter Error = Console.Error;
774 public static string Prompt = "csharp> ";
775 public static string ContinuationPrompt = " > ";
776 public static bool QuitRequested;
778 static public void ShowVars ()
780 Output.Write (Evaluator.GetVars ());
784 static public void ShowUsing ()
786 Output.Write (Evaluator.GetUsing ());
790 public delegate void Simple ();
792 static public TimeSpan Time (Simple a)
794 DateTime start = DateTime.Now;
796 return DateTime.Now - start;
800 static public void LoadPackage (string pkg)
803 Error.WriteLine ("Invalid package specified");
807 string pkgout = Driver.GetPackageFlags (pkg, false);
811 string [] xargs = pkgout.Trim (new Char [] {' ', '\n', '\r', '\t'}).
812 Split (new Char [] { ' ', '\t'});
814 foreach (string s in xargs){
815 if (s.StartsWith ("-r:") || s.StartsWith ("/r:") || s.StartsWith ("/reference:")){
816 string lib = s.Substring (s.IndexOf (':')+1);
818 Evaluator.LoadAssembly (lib);
825 static public void LoadAssembly (string assembly)
827 Evaluator.LoadAssembly (assembly);
830 static public string help {
832 return "Static methods:\n"+
833 " LoadPackage (pkg); - Loads the given Package (like -pkg:FILE)\n" +
834 " LoadAssembly (ass) - Loads the given assembly (like -r:ASS)\n" +
835 " ShowVars (); - Shows defined local variables.\n" +
836 " ShowUsing (); - Show active using decltions.\n" +
837 " Prompt - The prompt used by the C# shell\n" +
838 " ContinuationPrompt - The prompt for partial input\n" +
839 " Time(() -> { }) - Times the specified code\n" +
845 static public object quit {
847 QuitRequested = true;
854 // A local variable reference that will create a Field in a
855 // Class with the resolved type. This is necessary so we can
856 // support "var" as a field type in a class declaration.
858 // We allow LocalVariableReferece to do the heavy lifting, and
859 // then we insert the field with the resolved type
861 public class LocalVariableReferenceWithClassSideEffect : LocalVariableReference {
862 TypeContainer container;
865 public LocalVariableReferenceWithClassSideEffect (TypeContainer container, string name, Block current_block, string local_variable_id, Location loc)
866 : base (current_block, local_variable_id, loc)
868 this.container = container;
872 public override bool Equals (object obj)
874 LocalVariableReferenceWithClassSideEffect lvr = obj as LocalVariableReferenceWithClassSideEffect;
878 if (lvr.name != name || lvr.container != container)
881 return base.Equals (obj);
884 public override int GetHashCode ()
886 return name.GetHashCode ();
889 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
891 Expression ret = base.DoResolveLValue (ec, right_side);
895 Field f = new Field (container, new TypeExpression (ret.Type, Location),
896 Modifiers.PUBLIC | Modifiers.STATIC,
897 name, null, Location);
898 container.AddField (f);
900 Evaluator.QueueField (f);
907 /// A class used to assign values if the source expression is not void
909 /// Used by the interactive shell to allow it to call this code to set
910 /// the return value for an invocation.
912 class OptionalAssign : SimpleAssign {
913 public OptionalAssign (Expression t, Expression s, Location loc)
918 public override Expression DoResolve (EmitContext ec)
920 CloneContext cc = new CloneContext ();
921 Expression clone = source.Clone (cc);
923 clone = clone.Resolve (ec);
927 // This means its really a statement.
928 if (clone.Type == TypeManager.void_type){
929 source = source.Resolve (ec);
931 type = TypeManager.void_type;
932 eclass = ExprClass.Value;
936 return base.DoResolve (ec);
939 public override void Emit (EmitContext ec)
947 public override void EmitStatement (EmitContext ec)
952 base.EmitStatement (ec);
957 ArrayList undo_types;
961 undo_types = new ArrayList ();
964 public void AddTypeContainer (TypeContainer current_container, TypeContainer tc)
966 if (current_container == tc){
967 Console.Error.WriteLine ("Internal error: inserting container into itself");
971 if (undo_types == null)
972 undo_types = new ArrayList ();
973 undo_types.Add (new Pair (current_container, tc));
976 public void ExecuteUndo ()
978 if (undo_types == null)
981 foreach (Pair p in undo_types){
982 TypeContainer current_container = (TypeContainer) p.First;
984 current_container.RemoveTypeContainer ((TypeContainer) p.Second);