2009-12-02 Jb Evain <jbevain@novell.com>
[mcs.git] / mcs / eval.cs
blob1f7d6b35f19904b088cdb170a7c83ffba3087ae1
1 //
2 // eval.cs: Evaluation and Hosting API for the C# compiler
3 //
4 // Authors:
5 // Miguel de Icaza (miguel@gnome.org)
6 //
7 // Dual licensed under the terms of the MIT X11 or GNU GPL
8 //
9 // Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
10 // Copyright 2004, 2005, 2006, 2007, 2008 Novell, Inc
12 using System;
13 using System.Threading;
14 using System.Collections;
15 using System.Reflection;
16 using System.Reflection.Emit;
17 using System.IO;
18 using System.Globalization;
19 using System.Text;
21 namespace Mono.CSharp {
23 /// <summary>
24 /// Evaluator: provides an API to evaluate C# statements and
25 /// expressions dynamically.
26 /// </summary>
27 /// <remarks>
28 /// This class exposes static methods to evaluate expressions in the
29 /// current program.
30 ///
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.
34 ///
35 /// To interrupt execution of a statement, you can invoke the
36 /// Evaluator.Interrupt method.
37 /// </remarks>
38 public class Evaluator {
40 enum ParseMode {
41 // Parse silently, do not output any error messages
42 Silent,
44 // Report errors during parse
45 ReportErrors,
47 // Auto-complete, means that the tokenizer will start producing
48 // GETCOMPLETIONS tokens when it reaches a certain point.
49 GetCompletions
52 static object evaluator_lock = new object ();
54 static string current_debug_name;
55 static int count;
56 static Thread invoke_thread;
58 static ArrayList using_alias_list = new ArrayList ();
59 internal static ArrayList using_list = new ArrayList ();
60 static Hashtable fields = new Hashtable ();
62 static Type interactive_base_class = typeof (InteractiveBase);
63 static Driver driver;
64 static bool inited;
66 static CompilerContext ctx;
68 public static TextWriter MessageOutput = Console.Out;
70 /// <summary>
71 /// Optional initialization for the Evaluator.
72 /// </summary>
73 /// <remarks>
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
77 /// ignored.
78 ///
79 /// You can safely avoid calling this method if your application
80 /// does not need any of the features exposed by the command line
81 /// interface.
82 /// </remarks>
83 public static void Init (string [] args)
85 InitAndGetStartupFiles (args);
89 /// <summary>
90 /// Optional initialization for the Evaluator.
91 /// </summary>
92 /// <remarks>
93 /// Initializes the Evaluator with the command line
94 /// options that would be processed by the command
95 /// line compiler. Only the first call to
96 /// InitAndGetStartupFiles or Init will work, any future
97 /// invocations are ignored.
98 ///
99 /// You can safely avoid calling this method if your application
100 /// does not need any of the features exposed by the command line
101 /// interface.
103 /// This method return an array of strings that contains any
104 /// files that were specified in `args'.
105 /// </remarks>
106 public static string [] InitAndGetStartupFiles (string [] args)
108 lock (evaluator_lock){
109 if (inited)
110 return new string [0];
112 driver = Driver.Create (args, false, new ConsoleReportPrinter ());
113 if (driver == null)
114 throw new Exception ("Failed to create compiler driver with the given arguments");
116 RootContext.ToplevelTypes = new ModuleCompiled (ctx, true);
118 driver.ProcessDefaultConfig ();
120 ArrayList startup_files = new ArrayList ();
121 foreach (CompilationUnit file in Location.SourceFiles)
122 startup_files.Add (file.Path);
124 CompilerCallableEntryPoint.Reset ();
125 RootContext.ToplevelTypes = new ModuleCompiled (ctx, true);
127 driver.LoadReferences ();
128 RootContext.EvalMode = true;
129 inited = true;
131 return (string []) startup_files.ToArray (typeof (string));
135 static void Init ()
137 Init (new string [0]);
140 static void Reset ()
142 CompilerCallableEntryPoint.PartialReset ();
144 // Workaround for API limitation where full message printer cannot be passed
145 ReportPrinter printer = MessageOutput == Console.Out || MessageOutput == Console.Error ?
146 new ConsoleReportPrinter (MessageOutput) :
147 new StreamReportPrinter (MessageOutput);
149 ctx = new CompilerContext (new Report (printer));
150 RootContext.ToplevelTypes = new ModuleCompiled (ctx, true);
153 // PartialReset should not reset the core types, this is very redundant.
155 if (!TypeManager.InitCoreTypes (ctx))
156 throw new Exception ("Failed to InitCoreTypes");
157 TypeManager.InitOptionalCoreTypes (ctx);
159 Location.AddFile (null, "{interactive}");
160 Location.Initialize ();
162 current_debug_name = "interactive" + (count++) + ".dll";
163 if (Environment.GetEnvironmentVariable ("SAVE") != null){
164 CodeGen.Init (current_debug_name, current_debug_name, false, ctx);
165 } else
166 CodeGen.InitDynamic (ctx, current_debug_name);
169 /// <summary>
170 /// The base class for the classes that host the user generated code
171 /// </summary>
172 /// <remarks>
174 /// This is the base class that will host the code
175 /// executed by the Evaluator. By default
176 /// this is the Mono.CSharp.InteractiveBase class
177 /// which is useful for interactive use.
179 /// By changing this property you can control the
180 /// base class and the static members that are
181 /// available to your evaluated code.
182 /// </remarks>
183 static public Type InteractiveBaseClass {
184 get {
185 return interactive_base_class;
188 set {
189 if (value == null)
190 throw new ArgumentNullException ();
192 lock (evaluator_lock)
193 interactive_base_class = value;
197 /// <summary>
198 /// Interrupts the evaluation of an expression executing in Evaluate.
199 /// </summary>
200 /// <remarks>
201 /// Use this method to interrupt long-running invocations.
202 /// </remarks>
203 public static void Interrupt ()
205 if (!inited || !invoking)
206 return;
208 if (invoke_thread != null)
209 invoke_thread.Abort ();
212 /// <summary>
213 /// Compiles the input string and returns a delegate that represents the compiled code.
214 /// </summary>
215 /// <remarks>
217 /// Compiles the input string as a C# expression or
218 /// statement, unlike the Evaluate method, the
219 /// resulting delegate can be invoked multiple times
220 /// without incurring in the compilation overhead.
222 /// If the return value of this function is null,
223 /// this indicates that the parsing was complete.
224 /// If the return value is a string it indicates
225 /// that the input string was partial and that the
226 /// invoking code should provide more code before
227 /// the code can be successfully compiled.
229 /// If you know that you will always get full expressions or
230 /// statements and do not care about partial input, you can use
231 /// the other Compile overload.
233 /// On success, in addition to returning null, the
234 /// compiled parameter will be set to the delegate
235 /// that can be invoked to execute the code.
237 /// </remarks>
238 static public string Compile (string input, out CompiledMethod compiled)
240 if (input == null || input.Length == 0){
241 compiled = null;
242 return null;
245 lock (evaluator_lock){
246 if (!inited)
247 Init ();
249 bool partial_input;
250 CSharpParser parser = ParseString (ParseMode.Silent, input, out partial_input);
251 if (parser == null){
252 compiled = null;
253 if (partial_input)
254 return input;
256 ParseString (ParseMode.ReportErrors, input, out partial_input);
257 return null;
260 object parser_result = parser.InteractiveResult;
262 if (!(parser_result is Class)){
263 int errors = ctx.Report.Errors;
265 NamespaceEntry.VerifyAllUsing ();
266 if (errors == ctx.Report.Errors)
267 parser.CurrentNamespace.Extract (using_alias_list, using_list);
270 compiled = CompileBlock (parser_result as Class, parser.undo, ctx.Report);
273 return null;
276 /// <summary>
277 /// Compiles the input string and returns a delegate that represents the compiled code.
278 /// </summary>
279 /// <remarks>
281 /// Compiles the input string as a C# expression or
282 /// statement, unlike the Evaluate method, the
283 /// resulting delegate can be invoked multiple times
284 /// without incurring in the compilation overhead.
286 /// This method can only deal with fully formed input
287 /// strings and does not provide a completion mechanism.
288 /// If you must deal with partial input (for example for
289 /// interactive use) use the other overload.
291 /// On success, a delegate is returned that can be used
292 /// to invoke the method.
294 /// </remarks>
295 static public CompiledMethod Compile (string input)
297 CompiledMethod compiled;
299 // Ignore partial inputs
300 if (Compile (input, out compiled) != null){
301 // Error, the input was partial.
302 return null;
305 // Either null (on error) or the compiled method.
306 return compiled;
310 // Todo: Should we handle errors, or expect the calling code to setup
311 // the recording themselves?
314 /// <summary>
315 /// Evaluates and expression or statement and returns any result values.
316 /// </summary>
317 /// <remarks>
318 /// Evaluates the input string as a C# expression or
319 /// statement. If the input string is an expression
320 /// the result will be stored in the result variable
321 /// and the result_set variable will be set to true.
323 /// It is necessary to use the result/result_set
324 /// pair to identify when a result was set (for
325 /// example, execution of user-provided input can be
326 /// an expression, a statement or others, and
327 /// result_set would only be set if the input was an
328 /// expression.
330 /// If the return value of this function is null,
331 /// this indicates that the parsing was complete.
332 /// If the return value is a string, it indicates
333 /// that the input is partial and that the user
334 /// should provide an updated string.
335 /// </remarks>
336 public static string Evaluate (string input, out object result, out bool result_set)
338 CompiledMethod compiled;
340 result_set = false;
341 result = null;
343 input = Compile (input, out compiled);
344 if (input != null)
345 return input;
347 if (compiled == null)
348 return null;
351 // The code execution does not need to keep the compiler lock
353 object retval = typeof (NoValueSet);
355 try {
356 invoke_thread = System.Threading.Thread.CurrentThread;
357 invoking = true;
358 compiled (ref retval);
359 } catch (ThreadAbortException e){
360 Thread.ResetAbort ();
361 Console.WriteLine ("Interrupted!\n{0}", e);
362 } finally {
363 invoking = false;
367 // We use a reference to a compiler type, in this case
368 // Driver as a flag to indicate that this was a statement
370 if (retval != typeof (NoValueSet)){
371 result_set = true;
372 result = retval;
375 return null;
378 public static string [] GetCompletions (string input, out string prefix)
380 prefix = "";
381 if (input == null || input.Length == 0)
382 return null;
384 lock (evaluator_lock){
385 if (!inited)
386 Init ();
388 bool partial_input;
389 CSharpParser parser = ParseString (ParseMode.GetCompletions, input, out partial_input);
390 if (parser == null){
391 if (CSharpParser.yacc_verbose_flag != 0)
392 Console.WriteLine ("DEBUG: No completions available");
393 return null;
396 Class parser_result = parser.InteractiveResult as Class;
398 if (parser_result == null){
399 if (CSharpParser.yacc_verbose_flag != 0)
400 Console.WriteLine ("Do not know how to cope with !Class yet");
401 return null;
404 try {
405 RootContext.ResolveTree ();
406 if (ctx.Report.Errors != 0)
407 return null;
409 RootContext.PopulateTypes ();
410 if (ctx.Report.Errors != 0)
411 return null;
413 MethodOrOperator method = null;
414 foreach (MemberCore member in parser_result.Methods){
415 if (member.Name != "Host")
416 continue;
418 method = (MethodOrOperator) member;
419 break;
421 if (method == null)
422 throw new InternalErrorException ("did not find the the Host method");
424 BlockContext bc = new BlockContext (method, method.Block, method.ReturnType);
426 try {
427 method.Block.Resolve (null, bc, method.ParameterInfo, method);
428 } catch (CompletionResult cr){
429 prefix = cr.BaseText;
430 return cr.Result;
432 } finally {
433 parser.undo.ExecuteUndo ();
437 return null;
440 /// <summary>
441 /// Executes the given expression or statement.
442 /// </summary>
443 /// <remarks>
444 /// Executes the provided statement, returns true
445 /// on success, false on parsing errors. Exceptions
446 /// might be thrown by the called code.
447 /// </remarks>
448 public static bool Run (string statement)
450 if (!inited)
451 Init ();
453 object result;
454 bool result_set;
456 bool ok = Evaluate (statement, out result, out result_set) == null;
458 return ok;
461 /// <summary>
462 /// Evaluates and expression or statement and returns the result.
463 /// </summary>
464 /// <remarks>
465 /// Evaluates the input string as a C# expression or
466 /// statement and returns the value.
468 /// This method will throw an exception if there is a syntax error,
469 /// of if the provided input is not an expression but a statement.
470 /// </remarks>
471 public static object Evaluate (string input)
473 object result;
474 bool result_set;
476 string r = Evaluate (input, out result, out result_set);
478 if (r != null)
479 throw new ArgumentException ("Syntax error on input: partial input");
481 if (result_set == false)
482 throw new ArgumentException ("The expression did not set a result");
484 return result;
487 enum InputKind {
488 EOF,
489 StatementOrExpression,
490 CompilationUnit,
491 Error
495 // Deambiguates the input string to determine if we
496 // want to process a statement or if we want to
497 // process a compilation unit.
499 // This is done using a top-down predictive parser,
500 // since the yacc/jay parser can not deambiguage this
501 // without more than one lookahead token. There are very
502 // few ambiguities.
504 static InputKind ToplevelOrStatement (SeekableStreamReader seekable)
506 Tokenizer tokenizer = new Tokenizer (seekable, (CompilationUnit) Location.SourceFiles [0], ctx);
508 int t = tokenizer.token ();
509 switch (t){
510 case Token.EOF:
511 return InputKind.EOF;
513 // These are toplevels
514 case Token.EXTERN:
515 case Token.OPEN_BRACKET:
516 case Token.ABSTRACT:
517 case Token.CLASS:
518 case Token.ENUM:
519 case Token.INTERFACE:
520 case Token.INTERNAL:
521 case Token.NAMESPACE:
522 case Token.PRIVATE:
523 case Token.PROTECTED:
524 case Token.PUBLIC:
525 case Token.SEALED:
526 case Token.STATIC:
527 case Token.STRUCT:
528 return InputKind.CompilationUnit;
530 // Definitely expression
531 case Token.FIXED:
532 case Token.BOOL:
533 case Token.BYTE:
534 case Token.CHAR:
535 case Token.DECIMAL:
536 case Token.DOUBLE:
537 case Token.FLOAT:
538 case Token.INT:
539 case Token.LONG:
540 case Token.NEW:
541 case Token.OBJECT:
542 case Token.SBYTE:
543 case Token.SHORT:
544 case Token.STRING:
545 case Token.UINT:
546 case Token.ULONG:
547 return InputKind.StatementOrExpression;
549 // These need deambiguation help
550 case Token.USING:
551 t = tokenizer.token ();
552 if (t == Token.EOF)
553 return InputKind.EOF;
555 if (t == Token.IDENTIFIER)
556 return InputKind.CompilationUnit;
557 return InputKind.StatementOrExpression;
560 // Distinguish between:
561 // delegate opt_anonymous_method_signature block
562 // delegate type
563 case Token.DELEGATE:
564 t = tokenizer.token ();
565 if (t == Token.EOF)
566 return InputKind.EOF;
567 if (t == Token.OPEN_PARENS || t == Token.OPEN_BRACE)
568 return InputKind.StatementOrExpression;
569 return InputKind.CompilationUnit;
571 // Distinguih between:
572 // unsafe block
573 // unsafe as modifier of a type declaration
574 case Token.UNSAFE:
575 t = tokenizer.token ();
576 if (t == Token.EOF)
577 return InputKind.EOF;
578 if (t == Token.OPEN_PARENS)
579 return InputKind.StatementOrExpression;
580 return InputKind.CompilationUnit;
582 // These are errors: we list explicitly what we had
583 // from the grammar, ERROR and then everything else
585 case Token.READONLY:
586 case Token.OVERRIDE:
587 case Token.ERROR:
588 return InputKind.Error;
590 // This catches everything else allowed by
591 // expressions. We could add one-by-one use cases
592 // if needed.
593 default:
594 return InputKind.StatementOrExpression;
599 // Parses the string @input and returns a CSharpParser if succeeful.
601 // if @silent is set to true then no errors are
602 // reported to the user. This is used to do various calls to the
603 // parser and check if the expression is parsable.
605 // @partial_input: if @silent is true, then it returns whether the
606 // parsed expression was partial, and more data is needed
608 static CSharpParser ParseString (ParseMode mode, string input, out bool partial_input)
610 partial_input = false;
611 Reset ();
612 queued_fields.Clear ();
613 Tokenizer.LocatedToken.Initialize ();
615 Stream s = new MemoryStream (Encoding.Default.GetBytes (input));
616 SeekableStreamReader seekable = new SeekableStreamReader (s, Encoding.Default);
618 InputKind kind = ToplevelOrStatement (seekable);
619 if (kind == InputKind.Error){
620 if (mode == ParseMode.ReportErrors)
621 ctx.Report.Error (-25, "Detection Parsing Error");
622 partial_input = false;
623 return null;
626 if (kind == InputKind.EOF){
627 if (mode == ParseMode.ReportErrors)
628 Console.Error.WriteLine ("Internal error: EOF condition should have been detected in a previous call with silent=true");
629 partial_input = true;
630 return null;
633 seekable.Position = 0;
635 CSharpParser parser = new CSharpParser (seekable, (CompilationUnit) Location.SourceFiles [0], ctx);
637 if (kind == InputKind.StatementOrExpression){
638 parser.Lexer.putback_char = Tokenizer.EvalStatementParserCharacter;
639 RootContext.StatementMode = true;
640 } else {
642 // Do not activate EvalCompilationUnitParserCharacter until
643 // I have figured out all the limitations to invoke methods
644 // in the generated classes. See repl.txt
646 parser.Lexer.putback_char = Tokenizer.EvalUsingDeclarationsParserCharacter;
647 //parser.Lexer.putback_char = Tokenizer.EvalCompilationUnitParserCharacter;
648 RootContext.StatementMode = false;
651 if (mode == ParseMode.GetCompletions)
652 parser.Lexer.CompleteOnEOF = true;
654 bool disable_error_reporting;
655 if ((mode == ParseMode.Silent || mode == ParseMode.GetCompletions) && CSharpParser.yacc_verbose_flag == 0)
656 disable_error_reporting = true;
657 else
658 disable_error_reporting = false;
660 if (disable_error_reporting)
661 ctx.Report.DisableReporting ();
662 try {
663 parser.parse ();
664 } finally {
665 if (ctx.Report.Errors != 0){
666 if (mode != ParseMode.ReportErrors && parser.UnexpectedEOF)
667 partial_input = true;
669 parser.undo.ExecuteUndo ();
670 parser = null;
673 if (disable_error_reporting)
674 ctx.Report.EnableReporting ();
676 return parser;
680 // Queue all the fields that we use, as we need to then go from FieldBuilder to FieldInfo
681 // or reflection gets confused (it basically gets confused, and variables override each
682 // other).
684 static ArrayList queued_fields = new ArrayList ();
686 //static ArrayList types = new ArrayList ();
688 static volatile bool invoking;
690 static CompiledMethod CompileBlock (Class host, Undo undo, Report Report)
692 RootContext.ResolveTree ();
693 if (Report.Errors != 0){
694 undo.ExecuteUndo ();
695 return null;
698 RootContext.PopulateTypes ();
700 if (Report.Errors != 0){
701 undo.ExecuteUndo ();
702 return null;
705 TypeBuilder tb = null;
706 MethodBuilder mb = null;
708 if (host != null){
709 tb = host.TypeBuilder;
710 mb = null;
711 foreach (MemberCore member in host.Methods){
712 if (member.Name != "Host")
713 continue;
715 MethodOrOperator method = (MethodOrOperator) member;
716 mb = method.MethodBuilder;
717 break;
720 if (mb == null)
721 throw new Exception ("Internal error: did not find the method builder for the generated method");
724 RootContext.EmitCode ();
725 if (Report.Errors != 0)
726 return null;
728 RootContext.CloseTypes ();
730 if (Environment.GetEnvironmentVariable ("SAVE") != null)
731 CodeGen.Save (current_debug_name, false, Report);
733 if (host == null)
734 return null;
737 // Unlike Mono, .NET requires that the MethodInfo is fetched, it cant
738 // work from MethodBuilders. Retarded, I know.
740 Type tt = CodeGen.Assembly.Builder.GetType (tb.Name);
741 MethodInfo mi = tt.GetMethod (mb.Name);
743 // Pull the FieldInfos from the type, and keep track of them
744 foreach (Field field in queued_fields){
745 FieldInfo fi = tt.GetField (field.Name);
747 FieldInfo old = (FieldInfo) fields [field.Name];
749 // If a previous value was set, nullify it, so that we do
750 // not leak memory
751 if (old != null){
752 if (TypeManager.IsStruct (old.FieldType)){
754 // TODO: Clear fields for structs
756 } else {
757 try {
758 old.SetValue (null, null);
759 } catch {
764 fields [field.Name] = fi;
766 //types.Add (tb);
768 queued_fields.Clear ();
770 return (CompiledMethod) System.Delegate.CreateDelegate (typeof (CompiledMethod), mi);
773 static internal void LoadAliases (NamespaceEntry ns)
775 ns.Populate (using_alias_list, using_list);
778 /// <summary>
779 /// A sentinel value used to indicate that no value was
780 /// was set by the compiled function. This is used to
781 /// differentiate between a function not returning a
782 /// value and null.
783 /// </summary>
784 public class NoValueSet {
787 static internal FieldInfo LookupField (string name)
789 FieldInfo fi = (FieldInfo) fields [name];
791 return fi;
795 // Puts the FieldBuilder into a queue of names that will be
796 // registered. We can not register FieldBuilders directly
797 // we need to fetch the FieldInfo after Reflection cooks the
798 // types, or bad things happen (bad means: FieldBuilders behave
799 // incorrectly across multiple assemblies, causing assignments to
800 // invalid areas
802 // This also serves for the parser to register Field classes
803 // that should be exposed as global variables
805 static internal void QueueField (Field f)
807 queued_fields.Add (f);
810 static string Quote (string s)
812 if (s.IndexOf ('"') != -1)
813 s = s.Replace ("\"", "\\\"");
815 return "\"" + s + "\"";
818 static public string GetUsing ()
820 lock (evaluator_lock){
821 StringBuilder sb = new StringBuilder ();
823 foreach (object x in using_alias_list)
824 sb.Append (String.Format ("using {0};\n", x));
826 foreach (object x in using_list)
827 sb.Append (String.Format ("using {0};\n", x));
829 return sb.ToString ();
833 static internal ICollection GetUsingList ()
835 ArrayList res = new ArrayList (using_list.Count);
836 foreach (object ue in using_list)
837 res.Add (ue.ToString ());
838 return res;
841 static internal string [] GetVarNames ()
843 lock (evaluator_lock){
844 return (string []) new ArrayList (fields.Keys).ToArray (typeof (string));
848 static public string GetVars ()
850 lock (evaluator_lock){
851 StringBuilder sb = new StringBuilder ();
853 foreach (DictionaryEntry de in fields){
854 FieldInfo fi = LookupField ((string) de.Key);
855 object value = null;
856 bool error = false;
858 try {
859 if (value == null)
860 value = "null";
861 value = fi.GetValue (null);
862 if (value is string)
863 value = Quote ((string)value);
864 } catch {
865 error = true;
868 if (error)
869 sb.Append (String.Format ("{0} {1} <error reading value>", TypeManager.CSharpName(fi.FieldType), de.Key));
870 else
871 sb.Append (String.Format ("{0} {1} = {2}", TypeManager.CSharpName(fi.FieldType), de.Key, value));
874 return sb.ToString ();
878 /// <summary>
879 /// Loads the given assembly and exposes the API to the user.
880 /// </summary>
881 static public void LoadAssembly (string file)
883 lock (evaluator_lock){
884 driver.LoadAssembly (file, false);
885 GlobalRootNamespace.Instance.ComputeNamespaces (ctx);
889 /// <summary>
890 /// Exposes the API of the given assembly to the Evaluator
891 /// </summary>
892 static public void ReferenceAssembly (Assembly a)
894 lock (evaluator_lock){
895 GlobalRootNamespace.Instance.AddAssemblyReference (a);
896 GlobalRootNamespace.Instance.ComputeNamespaces (ctx);
903 /// <summary>
904 /// A delegate that can be used to invoke the
905 /// compiled expression or statement.
906 /// </summary>
907 /// <remarks>
908 /// Since the Compile methods will compile
909 /// statements and expressions into the same
910 /// delegate, you can tell if a value was returned
911 /// by checking whether the returned value is of type
912 /// NoValueSet.
913 /// </remarks>
915 public delegate void CompiledMethod (ref object retvalue);
917 /// <summary>
918 /// The default base class for every interaction line
919 /// </summary>
920 /// <remarks>
921 /// The expressions and statements behave as if they were
922 /// a static method of this class. The InteractiveBase class
923 /// contains a number of useful methods, but can be overwritten
924 /// by setting the InteractiveBaseType property in the Evaluator
925 /// </remarks>
926 public class InteractiveBase {
927 /// <summary>
928 /// Determines where the standard output of methods in this class will go.
929 /// </summary>
930 public static TextWriter Output = Console.Out;
932 /// <summary>
933 /// Determines where the standard error of methods in this class will go.
934 /// </summary>
935 public static TextWriter Error = Console.Error;
937 /// <summary>
938 /// The primary prompt used for interactive use.
939 /// </summary>
940 public static string Prompt = "csharp> ";
942 /// <summary>
943 /// The secondary prompt used for interactive use (used when
944 /// an expression is incomplete).
945 /// </summary>
946 public static string ContinuationPrompt = " > ";
948 /// <summary>
949 /// Used to signal that the user has invoked the `quit' statement.
950 /// </summary>
951 public static bool QuitRequested;
953 /// <summary>
954 /// Shows all the variables defined so far.
955 /// </summary>
956 static public void ShowVars ()
958 Output.Write (Evaluator.GetVars ());
959 Output.Flush ();
962 /// <summary>
963 /// Displays the using statements in effect at this point.
964 /// </summary>
965 static public void ShowUsing ()
967 Output.Write (Evaluator.GetUsing ());
968 Output.Flush ();
971 public delegate void Simple ();
973 /// <summary>
974 /// Times the execution of the given delegate
975 /// </summary>
976 static public TimeSpan Time (Simple a)
978 DateTime start = DateTime.Now;
979 a ();
980 return DateTime.Now - start;
983 #if !SMCS_SOURCE
984 /// <summary>
985 /// Loads the assemblies from a package
986 /// </summary>
987 /// <remarks>
988 /// Loads the assemblies from a package. This is equivalent
989 /// to passing the -pkg: command line flag to the C# compiler
990 /// on the command line.
991 /// </remarks>
992 static public void LoadPackage (string pkg)
994 if (pkg == null){
995 Error.WriteLine ("Invalid package specified");
996 return;
999 string pkgout = Driver.GetPackageFlags (pkg, false, RootContext.ToplevelTypes.Compiler.Report);
1000 if (pkgout == null)
1001 return;
1003 string [] xargs = pkgout.Trim (new Char [] {' ', '\n', '\r', '\t'}).
1004 Split (new Char [] { ' ', '\t'});
1006 foreach (string s in xargs){
1007 if (s.StartsWith ("-r:") || s.StartsWith ("/r:") || s.StartsWith ("/reference:")){
1008 string lib = s.Substring (s.IndexOf (':')+1);
1010 Evaluator.LoadAssembly (lib);
1011 continue;
1015 #endif
1017 /// <summary>
1018 /// Loads the assembly
1019 /// </summary>
1020 /// <remarks>
1021 /// Loads the specified assembly and makes its types
1022 /// available to the evaluator. This is equivalent
1023 /// to passing the -pkg: command line flag to the C#
1024 /// compiler on the command line.
1025 /// </remarks>
1026 static public void LoadAssembly (string assembly)
1028 Evaluator.LoadAssembly (assembly);
1031 /// <summary>
1032 /// Returns a list of available static methods.
1033 /// </summary>
1034 static public string help {
1035 get {
1036 return "Static methods:\n"+
1037 " Describe(obj) - Describes the object's type\n" +
1038 " LoadPackage (pkg); - Loads the given Package (like -pkg:FILE)\n" +
1039 " LoadAssembly (ass) - Loads the given assembly (like -r:ASS)\n" +
1040 " ShowVars (); - Shows defined local variables.\n" +
1041 " ShowUsing (); - Show active using decltions.\n" +
1042 " Prompt - The prompt used by the C# shell\n" +
1043 " ContinuationPrompt - The prompt for partial input\n" +
1044 " Time(() -> { }) - Times the specified code\n" +
1045 " quit;\n" +
1046 " help;\n";
1050 /// <summary>
1051 /// Indicates to the read-eval-print-loop that the interaction should be finished.
1052 /// </summary>
1053 static public object quit {
1054 get {
1055 QuitRequested = true;
1056 return null;
1060 #if !NET_2_1
1061 /// <summary>
1062 /// Describes an object or a type.
1063 /// </summary>
1064 /// <remarks>
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.
1070 /// </remarks>
1071 static public string Describe (object x)
1073 if (x == null)
1074 return "";
1076 Type t = x as Type;
1077 if (t == null)
1078 t = x.GetType ();
1080 StringWriter sw = new StringWriter ();
1081 new Outline (t, sw, true, false, false).OutlineType ();
1082 return sw.ToString ();
1084 #endif
1088 // A local variable reference that will create a Field in a
1089 // Class with the resolved type. This is necessary so we can
1090 // support "var" as a field type in a class declaration.
1092 // We allow LocalVariableReferece to do the heavy lifting, and
1093 // then we insert the field with the resolved type
1095 public class LocalVariableReferenceWithClassSideEffect : LocalVariableReference {
1096 TypeContainer container;
1097 string name;
1099 public LocalVariableReferenceWithClassSideEffect (TypeContainer container, string name, Block current_block, string local_variable_id, LocalInfo li, Location loc)
1100 : base (current_block, local_variable_id, loc, li, false)
1102 this.container = container;
1103 this.name = name;
1106 public override bool Equals (object obj)
1108 LocalVariableReferenceWithClassSideEffect lvr = obj as LocalVariableReferenceWithClassSideEffect;
1109 if (lvr == null)
1110 return false;
1112 if (lvr.name != name || lvr.container != container)
1113 return false;
1115 return base.Equals (obj);
1118 public override int GetHashCode ()
1120 return name.GetHashCode ();
1123 override public Expression DoResolveLValue (ResolveContext ec, Expression right_side)
1125 Expression ret = base.DoResolveLValue (ec, right_side);
1126 if (ret == null)
1127 return null;
1129 Field f = new Field (container, new TypeExpression (ret.Type, Location),
1130 Modifiers.PUBLIC | Modifiers.STATIC,
1131 new MemberName (name, Location), null);
1132 container.AddField (f);
1133 if (f.Define ())
1134 Evaluator.QueueField (f);
1136 return ret;
1140 /// <summary>
1141 /// A class used to assign values if the source expression is not void
1143 /// Used by the interactive shell to allow it to call this code to set
1144 /// the return value for an invocation.
1145 /// </summary>
1146 class OptionalAssign : SimpleAssign {
1147 public OptionalAssign (Expression t, Expression s, Location loc)
1148 : base (t, s, loc)
1152 protected override Expression DoResolve (ResolveContext ec)
1154 CloneContext cc = new CloneContext ();
1155 Expression clone = source.Clone (cc);
1157 clone = clone.Resolve (ec);
1158 if (clone == null)
1159 return null;
1161 // This means its really a statement.
1162 if (clone.Type == TypeManager.void_type){
1163 source = source.Resolve (ec);
1164 target = null;
1165 type = TypeManager.void_type;
1166 eclass = ExprClass.Value;
1167 return this;
1170 return base.DoResolve (ec);
1173 public override void Emit (EmitContext ec)
1175 if (target == null)
1176 source.Emit (ec);
1177 else
1178 base.Emit (ec);
1181 public override void EmitStatement (EmitContext ec)
1183 if (target == null)
1184 source.Emit (ec);
1185 else
1186 base.EmitStatement (ec);
1190 public class Undo {
1191 ArrayList undo_types;
1193 public Undo ()
1195 undo_types = new ArrayList ();
1198 public void AddTypeContainer (TypeContainer current_container, TypeContainer tc)
1200 if (current_container == tc){
1201 Console.Error.WriteLine ("Internal error: inserting container into itself");
1202 return;
1205 if (undo_types == null)
1206 undo_types = new ArrayList ();
1207 undo_types.Add (new Pair (current_container, tc));
1210 public void ExecuteUndo ()
1212 if (undo_types == null)
1213 return;
1215 foreach (Pair p in undo_types){
1216 TypeContainer current_container = (TypeContainer) p.First;
1218 current_container.RemoveTypeContainer ((TypeContainer) p.Second);
1220 undo_types = null;