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 {
39 static string current_debug_name;
41 static Thread invoke_thread;
43 static ArrayList using_alias_list = new ArrayList ();
44 static ArrayList using_list = new ArrayList ();
45 static Hashtable fields = new Hashtable ();
47 static Type interactive_base_class = typeof (InteractiveBase);
52 /// Optional initialization for the Evaluator.
55 /// Initializes the Evaluator with the command line options
56 /// that would be processed by the command line compiler. Only
57 /// the first call to Init will work, any future invocations are
60 /// You can safely avoid calling this method if your application
61 /// does not need any of the features exposed by the command line
64 public static void Init (string [] args)
69 RootContext.Version = LanguageVersion.Default;
70 driver = Driver.Create (args, false);
72 throw new Exception ("Failed to create compiler driver with the given arguments");
74 driver.ProcessDefaultConfig ();
75 CompilerCallableEntryPoint.Reset ();
76 Driver.LoadReferences ();
77 RootContext.EvalMode = true;
83 Init (new string [0]);
88 CompilerCallableEntryPoint.PartialReset ();
91 // PartialReset should not reset the core types, this is very redundant.
93 if (!TypeManager.InitCoreTypes ())
94 throw new Exception ("Failed to InitCoreTypes");
95 TypeManager.InitOptionalCoreTypes ();
97 Location.AddFile ("<interactive>");
98 Location.Initialize ();
100 current_debug_name = "interactive" + (count++) + ".dll";
101 if (Environment.GetEnvironmentVariable ("SAVE") != null){
102 CodeGen.Init (current_debug_name, current_debug_name, false);
104 CodeGen.InitDynamic (current_debug_name);
108 /// The base class for the classes that host the user generated code
112 /// This is the base class that will host the code
113 /// executed by the Evaluator. By default
114 /// this is the Mono.CSharp.InteractiveBase class
115 /// which is useful for interactive use.
117 /// By changing this property you can control the
118 /// base class and the static members that are
119 /// available to your evaluated code.
121 static public Type InteractiveBaseClass {
123 return interactive_base_class;
128 throw new ArgumentNullException ();
130 interactive_base_class = value;
135 /// Interrupts the evaluation of an expression.
138 /// Use this method to interrupt long-running invocations.
140 public static void Interrupt ()
142 if (!inited || !invoking)
145 if (invoke_thread != null)
146 invoke_thread.Abort ();
150 // Todo: Should we handle errors, or expect the calling code to setup
151 // the recording themselves?
155 /// Evaluates and expression or statement and returns any result values.
158 /// Evaluates the input string as a C# expression or
159 /// statement. If the input string is an expression
160 /// the result will be stored in the result variable
161 /// and the result_set variable will be set to true.
163 /// It is necessary to use the result/result_set
164 /// pair to identify when a result was set (for
165 /// example, execution of user-provided input can be
166 /// an expression, a statement or others, and
167 /// result_set would only be set if the input was an
170 /// If the return value of this function is null,
171 /// this indicates that the parsing was complete.
172 /// If the return value is a string, it indicates
173 /// that the input is partial and that the user
174 /// should provide an updated string.
176 public static string Evaluate (string input, out object result, out bool result_set)
181 if (input == null || input.Length == 0)
188 CSharpParser parser = ParseString (true, input, out partial_input);
193 ParseString (false, input, out partial_input);
197 object parser_result = parser.InteractiveResult;
199 if (!(parser_result is Class))
200 parser.CurrentNamespace.Extract (using_alias_list, using_list);
202 result = ExecuteBlock (parser_result as Class, parser.undo);
204 // We use a reference to a compiler type, in this case
205 // Driver as a flag to indicate that this was a statement
207 if (result != typeof (NoValueSet))
214 /// Executes the given expression or statement.
217 /// Executes the provided statement, returns true
218 /// on success, false on parsing errors. Exceptions
219 /// might be thrown by the called code.
221 public static bool Run (string statement)
229 bool ok = Evaluate (statement, out result, out result_set) == null;
235 /// Evaluates and expression or statement and returns the result.
238 /// Evaluates the input string as a C# expression or
239 /// statement and returns the value.
241 /// This method will throw an exception if there is a syntax error,
242 /// of if the provided input is not an expression but a statement.
244 public static object Evaluate (string input)
249 string r = Evaluate (input, out result, out result_set);
252 throw new ArgumentException ("Syntax error on input: partial input");
254 if (result_set == false)
255 throw new ArgumentException ("The expression did not set a result");
262 StatementOrExpression,
268 // Deambiguates the input string to determine if we
269 // want to process a statement or if we want to
270 // process a compilation unit.
272 // This is done using a top-down predictive parser,
273 // since the yacc/jay parser can not deambiguage this
274 // without more than one lookahead token. There are very
277 static InputKind ToplevelOrStatement (SeekableStreamReader seekable)
279 Tokenizer tokenizer = new Tokenizer (seekable, Location.SourceFiles [0]);
281 int t = tokenizer.token ();
284 return InputKind.EOF;
286 // These are toplevels
288 case Token.OPEN_BRACKET:
292 case Token.INTERFACE:
294 case Token.NAMESPACE:
296 case Token.PROTECTED:
301 return InputKind.CompilationUnit;
303 // Definitely expression
320 return InputKind.StatementOrExpression;
322 // These need deambiguation help
324 t = tokenizer.token ();
326 return InputKind.EOF;
328 if (t == Token.IDENTIFIER)
329 return InputKind.CompilationUnit;
330 return InputKind.StatementOrExpression;
333 // Distinguish between:
334 // delegate opt_anonymous_method_signature block
337 t = tokenizer.token ();
339 return InputKind.EOF;
340 if (t == Token.OPEN_PARENS || t == Token.OPEN_BRACE)
341 return InputKind.StatementOrExpression;
342 return InputKind.CompilationUnit;
344 // Distinguih between:
346 // unsafe as modifier of a type declaration
348 t = tokenizer.token ();
350 return InputKind.EOF;
351 if (t == Token.OPEN_PARENS)
352 return InputKind.StatementOrExpression;
353 return InputKind.CompilationUnit;
355 // These are errors: we list explicitly what we had
356 // from the grammar, ERROR and then everything else
361 return InputKind.Error;
363 // This catches everything else allowed by
364 // expressions. We could add one-by-one use cases
367 return InputKind.StatementOrExpression;
372 // Parses the string @input and returns a CSharpParser if succeeful.
374 // if @silent is set to true then no errors are
375 // reported to the user. This is used to do various calls to the
376 // parser and check if the expression is parsable.
378 // @partial_input: if @silent is true, then it returns whether the
379 // parsed expression was partial, and more data is needed
381 static CSharpParser ParseString (bool silent, string input, out bool partial_input)
383 partial_input = false;
385 queued_fields.Clear ();
387 Stream s = new MemoryStream (Encoding.Default.GetBytes (input));
388 SeekableStreamReader seekable = new SeekableStreamReader (s, Encoding.Default);
390 InputKind kind = ToplevelOrStatement (seekable);
391 if (kind == InputKind.Error){
393 Report.Error (-25, "Detection Parsing Error");
394 partial_input = false;
398 if (kind == InputKind.EOF){
400 Console.Error.WriteLine ("Internal error: EOF condition should have been detected in a previous call with silent=true");
401 partial_input = true;
405 seekable.Position = 0;
407 CSharpParser parser = new CSharpParser (seekable, Location.SourceFiles [0]);
408 parser.ErrorOutput = Report.Stderr;
410 if (kind == InputKind.StatementOrExpression){
411 parser.Lexer.putback_char = Tokenizer.EvalStatementParserCharacter;
412 RootContext.StatementMode = true;
415 // Do not activate EvalCompilationUnitParserCharacter until
416 // I have figured out all the limitations to invoke methods
417 // in the generated classes. See repl.txt
419 parser.Lexer.putback_char = Tokenizer.EvalUsingDeclarationsParserCharacter;
420 //parser.Lexer.putback_char = Tokenizer.EvalCompilationUnitParserCharacter;
421 RootContext.StatementMode = false;
425 Report.DisableReporting ();
429 if (Report.Errors != 0){
430 if (silent && parser.UnexpectedEOF)
431 partial_input = true;
433 parser.undo.ExecuteUndo ();
438 Report.EnableReporting ();
443 delegate void HostSignature (ref object retvalue);
446 // Queue all the fields that we use, as we need to then go from FieldBuilder to FieldInfo
447 // or reflection gets confused (it basically gets confused, and variables override each
450 static ArrayList queued_fields = new ArrayList ();
452 //static ArrayList types = new ArrayList ();
454 static volatile bool invoking;
456 static object ExecuteBlock (Class host, Undo undo)
458 RootContext.ResolveTree ();
459 if (Report.Errors != 0){
461 return typeof (NoValueSet);
464 RootContext.PopulateTypes ();
465 RootContext.DefineTypes ();
467 if (Report.Errors != 0){
469 return typeof (NoValueSet);
472 TypeBuilder tb = null;
473 MethodBuilder mb = null;
476 tb = host.TypeBuilder;
478 foreach (MemberCore member in host.Methods){
479 if (member.Name != "Host")
482 MethodOrOperator method = (MethodOrOperator) member;
483 mb = method.MethodBuilder;
488 throw new Exception ("Internal error: did not find the method builder for the generated method");
491 RootContext.EmitCode ();
492 if (Report.Errors != 0)
493 return typeof (NoValueSet);
495 RootContext.CloseTypes ();
497 if (Environment.GetEnvironmentVariable ("SAVE") != null)
498 CodeGen.Save (current_debug_name, false);
501 return typeof (NoValueSet);
504 // Unlike Mono, .NET requires that the MethodInfo is fetched, it cant
505 // work from MethodBuilders. Retarded, I know.
507 Type tt = CodeGen.Assembly.Builder.GetType (tb.Name);
508 MethodInfo mi = tt.GetMethod (mb.Name);
510 // Pull the FieldInfos from the type, and keep track of them
511 foreach (Field field in queued_fields){
512 FieldInfo fi = tt.GetField (field.Name);
514 FieldInfo old = (FieldInfo) fields [field.Name];
516 // If a previous value was set, nullify it, so that we do
519 if (old.FieldType.IsValueType){
521 // TODO: Clear fields for structs
525 old.SetValue (null, null);
531 fields [field.Name] = fi;
535 queued_fields.Clear ();
537 HostSignature invoker = (HostSignature) System.Delegate.CreateDelegate (typeof (HostSignature), mi);
538 object retval = typeof (NoValueSet);
541 invoke_thread = System.Threading.Thread.CurrentThread;
543 invoker (ref retval);
544 } catch (ThreadAbortException e){
545 Thread.ResetAbort ();
546 Console.WriteLine ("Interrupted!\n{0}", e);
551 // d.DynamicInvoke (new object [] { retval });
556 static internal void LoadAliases (NamespaceEntry ns)
558 ns.Populate (using_alias_list, using_list);
562 // Just a placeholder class, used as a sentinel to determine that the
563 // generated code did not set a value
567 static internal FieldInfo LookupField (string name)
569 FieldInfo fi = (FieldInfo) fields [name];
575 // Puts the FieldBuilder into a queue of names that will be
576 // registered. We can not register FieldBuilders directly
577 // we need to fetch the FieldInfo after Reflection cooks the
578 // types, or bad things happen (bad means: FieldBuilders behave
579 // incorrectly across multiple assemblies, causing assignments to
582 // This also serves for the parser to register Field classes
583 // that should be exposed as global variables
585 static internal void QueueField (Field f)
587 queued_fields.Add (f);
590 static string Quote (string s)
592 if (s.IndexOf ('"') != -1)
593 s = s.Replace ("\"", "\\\"");
595 return "\"" + s + "\"";
598 static public string GetUsing ()
600 StringBuilder sb = new StringBuilder ();
602 foreach (object x in using_alias_list)
603 sb.Append (String.Format ("using {0};\n", x));
605 foreach (object x in using_list)
606 sb.Append (String.Format ("using {0};\n", x));
608 return sb.ToString ();
611 static public string GetVars ()
613 StringBuilder sb = new StringBuilder ();
615 foreach (DictionaryEntry de in fields){
616 FieldInfo fi = LookupField ((string) de.Key);
623 value = fi.GetValue (null);
625 value = Quote ((string)value);
631 sb.Append (String.Format ("{0} {1} <error reading value>", TypeManager.CSharpName(fi.FieldType), de.Key));
633 sb.Append (String.Format ("{0} {1} = {2}", TypeManager.CSharpName(fi.FieldType), de.Key, value));
636 return sb.ToString ();
640 /// Loads the given assembly and exposes the API to the user.
642 static public void LoadAssembly (string file)
644 Driver.LoadAssembly (file, true);
648 /// Exposes the API of the given assembly to the Evaluator
650 static public void ReferenceAssembly (Assembly a)
652 RootNamespace.Global.AddAssemblyReference (a);
658 /// The default base class for every interaction line
660 public class InteractiveBase {
661 public static TextWriter Output = Console.Out;
662 public static TextWriter Error = Console.Error;
663 public static string Prompt = "csharp> ";
664 public static string ContinuationPrompt = " > ";
666 static public void ShowVars ()
668 Output.Write (Evaluator.GetVars ());
672 static public void ShowUsing ()
674 Output.Write (Evaluator.GetUsing ());
678 public delegate void Simple ();
680 static public TimeSpan Time (Simple a)
682 DateTime start = DateTime.Now;
684 return DateTime.Now - start;
688 static public void LoadPackage (string pkg)
691 Error.WriteLine ("Invalid package specified");
695 string pkgout = Driver.GetPackageFlags (pkg, false);
699 string [] xargs = pkgout.Trim (new Char [] {' ', '\n', '\r', '\t'}).
700 Split (new Char [] { ' ', '\t'});
702 foreach (string s in xargs){
703 if (s.StartsWith ("-r:") || s.StartsWith ("/r:") || s.StartsWith ("/reference:")){
704 string lib = s.Substring (s.IndexOf (':')+1);
706 Driver.LoadAssembly (lib, true);
713 static public void LoadAssembly (string assembly)
715 Driver.LoadAssembly (assembly, true);
718 static public string help {
720 return "Static methods:\n"+
721 " LoadPackage (pkg); - Loads the given Package (like -pkg:FILE)\n" +
722 " LoadAssembly (ass) - Loads the given assembly (like -r:ASS)\n" +
723 " ShowVars (); - Shows defined local variables.\n" +
724 " ShowUsing (); - Show active using decltions.\n" +
725 " Prompt - The prompt used by the C# shell\n" +
726 " ContinuationPrompt - The prompt for partial input\n" +
727 " Time(() -> { }) - Times the specified code\n" +
733 static public object quit {
735 Environment.Exit (0);
742 // A local variable reference that will create a Field in a
743 // Class with the resolved type. This is necessary so we can
744 // support "var" as a field type in a class declaration.
746 // We allow LocalVariableReferece to do the heavy lifting, and
747 // then we insert the field with the resolved type
749 public class LocalVariableReferenceWithClassSideEffect : LocalVariableReference {
750 TypeContainer container;
753 public LocalVariableReferenceWithClassSideEffect (TypeContainer container, string name, Block current_block, string local_variable_id, Location loc)
754 : base (current_block, local_variable_id, loc)
756 this.container = container;
760 public override bool Equals (object obj)
762 LocalVariableReferenceWithClassSideEffect lvr = obj as LocalVariableReferenceWithClassSideEffect;
766 if (lvr.name != name || lvr.container != container)
769 return base.Equals (obj);
772 public override int GetHashCode ()
774 return name.GetHashCode ();
777 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
779 Expression ret = base.DoResolveLValue (ec, right_side);
783 Field f = new Field (container, new TypeExpression (ret.Type, Location),
784 Modifiers.PUBLIC | Modifiers.STATIC,
785 name, null, Location);
786 container.AddField (f);
788 Evaluator.QueueField (f);
795 /// A class used to assign values if the source expression is not void
797 /// Used by the interactive shell to allow it to call this code to set
798 /// the return value for an invocation.
800 class OptionalAssign : SimpleAssign {
801 public OptionalAssign (Expression t, Expression s, Location loc)
806 public override Expression DoResolve (EmitContext ec)
808 CloneContext cc = new CloneContext ();
809 Expression clone = source.Clone (cc);
811 clone = clone.Resolve (ec);
815 // This means its really a statement.
816 if (clone.Type == TypeManager.void_type){
817 source = source.Resolve (ec);
819 type = TypeManager.void_type;
820 eclass = ExprClass.Value;
824 return base.DoResolve (ec);
827 public override void Emit (EmitContext ec)
835 public override void EmitStatement (EmitContext ec)
840 base.EmitStatement (ec);
845 ArrayList undo_types;
849 undo_types = new ArrayList ();
852 public void AddTypeContainer (TypeContainer current_container, TypeContainer tc)
854 if (current_container == tc){
855 Console.Error.WriteLine ("Internal error: inserting container into itself");
859 if (undo_types == null)
860 undo_types = new ArrayList ();
861 undo_types.Add (new Pair (current_container, tc));
864 public void ExecuteUndo ()
866 if (undo_types == null)
869 foreach (Pair p in undo_types){
870 TypeContainer current_container = (TypeContainer) p.First;
872 current_container.RemoveTypeContainer ((TypeContainer) p.Second);