* Vbc.cs (AddResponseFileCommands): Escape DefineConstants as it can
[mcs.git] / mcs / eval.cs
blob560b277d15ccda9c32db5cdb21c9464fed50b629
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 /// <summary>
67 /// Optional initialization for the Evaluator.
68 /// </summary>
69 /// <remarks>
70 /// Initializes the Evaluator with the command line options
71 /// that would be processed by the command line compiler. Only
72 /// the first call to Init will work, any future invocations are
73 /// ignored.
74 ///
75 /// You can safely avoid calling this method if your application
76 /// does not need any of the features exposed by the command line
77 /// interface.
78 /// </remarks>
79 public static void Init (string [] args)
81 InitAndGetStartupFiles (args);
85 /// <summary>
86 /// Optional initialization for the Evaluator.
87 /// </summary>
88 /// <remarks>
89 /// Initializes the Evaluator with the command line
90 /// options that would be processed by the command
91 /// line compiler. Only the first call to
92 /// InitAndGetStartupFiles or Init will work, any future
93 /// invocations are ignored.
94 ///
95 /// You can safely avoid calling this method if your application
96 /// does not need any of the features exposed by the command line
97 /// interface.
98 ///
99 /// This method return an array of strings that contains any
100 /// files that were specified in `args'.
101 /// </remarks>
102 public static string [] InitAndGetStartupFiles (string [] args)
104 lock (evaluator_lock){
105 if (inited)
106 return new string [0];
108 RootContext.Version = LanguageVersion.Default;
109 driver = Driver.Create (args, false);
110 if (driver == null)
111 throw new Exception ("Failed to create compiler driver with the given arguments");
113 driver.ProcessDefaultConfig ();
115 ArrayList startup_files = new ArrayList ();
116 foreach (CompilationUnit file in Location.SourceFiles)
117 startup_files.Add (file.Path);
119 CompilerCallableEntryPoint.Reset ();
120 driver.LoadReferences ();
121 RootContext.EvalMode = true;
122 inited = true;
124 return (string []) startup_files.ToArray (typeof (string));
128 static void Init ()
130 Init (new string [0]);
133 static void Reset ()
135 CompilerCallableEntryPoint.PartialReset ();
138 // PartialReset should not reset the core types, this is very redundant.
140 if (!TypeManager.InitCoreTypes ())
141 throw new Exception ("Failed to InitCoreTypes");
142 TypeManager.InitOptionalCoreTypes ();
144 Location.AddFile ("{interactive}");
145 Location.Initialize ();
147 current_debug_name = "interactive" + (count++) + ".dll";
148 if (Environment.GetEnvironmentVariable ("SAVE") != null){
149 CodeGen.Init (current_debug_name, current_debug_name, false);
150 } else
151 CodeGen.InitDynamic (current_debug_name);
154 /// <summary>
155 /// The base class for the classes that host the user generated code
156 /// </summary>
157 /// <remarks>
159 /// This is the base class that will host the code
160 /// executed by the Evaluator. By default
161 /// this is the Mono.CSharp.InteractiveBase class
162 /// which is useful for interactive use.
164 /// By changing this property you can control the
165 /// base class and the static members that are
166 /// available to your evaluated code.
167 /// </remarks>
168 static public Type InteractiveBaseClass {
169 get {
170 return interactive_base_class;
173 set {
174 if (value == null)
175 throw new ArgumentNullException ();
177 lock (evaluator_lock)
178 interactive_base_class = value;
182 /// <summary>
183 /// Interrupts the evaluation of an expression executing in Evaluate.
184 /// </summary>
185 /// <remarks>
186 /// Use this method to interrupt long-running invocations.
187 /// </remarks>
188 public static void Interrupt ()
190 if (!inited || !invoking)
191 return;
193 if (invoke_thread != null)
194 invoke_thread.Abort ();
197 /// <summary>
198 /// Compiles the input string and returns a delegate that represents the compiled code.
199 /// </summary>
200 /// <remarks>
202 /// Compiles the input string as a C# expression or
203 /// statement, unlike the Evaluate method, the
204 /// resulting delegate can be invoked multiple times
205 /// without incurring in the compilation overhead.
207 /// If the return value of this function is null,
208 /// this indicates that the parsing was complete.
209 /// If the return value is a string it indicates
210 /// that the input string was partial and that the
211 /// invoking code should provide more code before
212 /// the code can be successfully compiled.
214 /// If you know that you will always get full expressions or
215 /// statements and do not care about partial input, you can use
216 /// the other Compile overload.
218 /// On success, in addition to returning null, the
219 /// compiled parameter will be set to the delegate
220 /// that can be invoked to execute the code.
222 /// </remarks>
223 static public string Compile (string input, out CompiledMethod compiled)
225 if (input == null || input.Length == 0){
226 compiled = null;
227 return null;
230 lock (evaluator_lock){
231 if (!inited)
232 Init ();
234 bool partial_input;
235 CSharpParser parser = ParseString (ParseMode.Silent, input, out partial_input);
236 if (parser == null){
237 compiled = null;
238 if (partial_input)
239 return input;
241 ParseString (ParseMode.ReportErrors, input, out partial_input);
242 return null;
245 object parser_result = parser.InteractiveResult;
247 if (!(parser_result is Class)){
248 int errors = Report.Errors;
250 NamespaceEntry.VerifyAllUsing ();
251 if (errors == Report.Errors)
252 parser.CurrentNamespace.Extract (using_alias_list, using_list);
255 compiled = CompileBlock (parser_result as Class, parser.undo);
258 return null;
261 /// <summary>
262 /// Compiles the input string and returns a delegate that represents the compiled code.
263 /// </summary>
264 /// <remarks>
266 /// Compiles the input string as a C# expression or
267 /// statement, unlike the Evaluate method, the
268 /// resulting delegate can be invoked multiple times
269 /// without incurring in the compilation overhead.
271 /// This method can only deal with fully formed input
272 /// strings and does not provide a completion mechanism.
273 /// If you must deal with partial input (for example for
274 /// interactive use) use the other overload.
276 /// On success, a delegate is returned that can be used
277 /// to invoke the method.
279 /// </remarks>
280 static public CompiledMethod Compile (string input)
282 CompiledMethod compiled;
284 // Ignore partial inputs
285 if (Compile (input, out compiled) != null){
286 // Error, the input was partial.
287 return null;
290 // Either null (on error) or the compiled method.
291 return compiled;
295 // Todo: Should we handle errors, or expect the calling code to setup
296 // the recording themselves?
299 /// <summary>
300 /// Evaluates and expression or statement and returns any result values.
301 /// </summary>
302 /// <remarks>
303 /// Evaluates the input string as a C# expression or
304 /// statement. If the input string is an expression
305 /// the result will be stored in the result variable
306 /// and the result_set variable will be set to true.
308 /// It is necessary to use the result/result_set
309 /// pair to identify when a result was set (for
310 /// example, execution of user-provided input can be
311 /// an expression, a statement or others, and
312 /// result_set would only be set if the input was an
313 /// expression.
315 /// If the return value of this function is null,
316 /// this indicates that the parsing was complete.
317 /// If the return value is a string, it indicates
318 /// that the input is partial and that the user
319 /// should provide an updated string.
320 /// </remarks>
321 public static string Evaluate (string input, out object result, out bool result_set)
323 CompiledMethod compiled;
325 result_set = false;
326 result = null;
328 input = Compile (input, out compiled);
329 if (input != null)
330 return input;
332 if (compiled == null)
333 return null;
336 // The code execution does not need to keep the compiler lock
338 object retval = typeof (NoValueSet);
340 try {
341 invoke_thread = System.Threading.Thread.CurrentThread;
342 invoking = true;
343 compiled (ref retval);
344 } catch (ThreadAbortException e){
345 Thread.ResetAbort ();
346 Console.WriteLine ("Interrupted!\n{0}", e);
347 } finally {
348 invoking = false;
352 // We use a reference to a compiler type, in this case
353 // Driver as a flag to indicate that this was a statement
355 if (retval != typeof (NoValueSet)){
356 result_set = true;
357 result = retval;
360 return null;
363 public static string [] GetCompletions (string input, out string prefix)
365 prefix = "";
366 if (input == null || input.Length == 0)
367 return null;
369 lock (evaluator_lock){
370 if (!inited)
371 Init ();
373 bool partial_input;
374 CSharpParser parser = ParseString (ParseMode.GetCompletions, input, out partial_input);
375 if (parser == null){
376 if (CSharpParser.yacc_verbose_flag != 0)
377 Console.WriteLine ("DEBUG: No completions available");
378 return null;
381 Class parser_result = parser.InteractiveResult as Class;
383 if (parser_result == null){
384 if (CSharpParser.yacc_verbose_flag != 0)
385 Console.WriteLine ("Do not know how to cope with !Class yet");
386 return null;
389 try {
390 RootContext.ResolveTree ();
391 if (Report.Errors != 0)
392 return null;
394 RootContext.PopulateTypes ();
395 if (Report.Errors != 0)
396 return null;
398 MethodOrOperator method = null;
399 foreach (MemberCore member in parser_result.Methods){
400 if (member.Name != "Host")
401 continue;
403 method = (MethodOrOperator) member;
404 break;
406 if (method == null)
407 throw new InternalErrorException ("did not find the the Host method");
409 EmitContext ec = method.CreateEmitContext (method.Parent, null);
410 bool unreach;
412 try {
413 ec.ResolveTopBlock (null, method.Block, method.ParameterInfo, method, out unreach);
414 } catch (CompletionResult cr){
415 prefix = cr.BaseText;
416 return cr.Result;
418 } finally {
419 parser.undo.ExecuteUndo ();
423 return null;
426 /// <summary>
427 /// Executes the given expression or statement.
428 /// </summary>
429 /// <remarks>
430 /// Executes the provided statement, returns true
431 /// on success, false on parsing errors. Exceptions
432 /// might be thrown by the called code.
433 /// </remarks>
434 public static bool Run (string statement)
436 if (!inited)
437 Init ();
439 object result;
440 bool result_set;
442 bool ok = Evaluate (statement, out result, out result_set) == null;
444 return ok;
447 /// <summary>
448 /// Evaluates and expression or statement and returns the result.
449 /// </summary>
450 /// <remarks>
451 /// Evaluates the input string as a C# expression or
452 /// statement and returns the value.
454 /// This method will throw an exception if there is a syntax error,
455 /// of if the provided input is not an expression but a statement.
456 /// </remarks>
457 public static object Evaluate (string input)
459 object result;
460 bool result_set;
462 string r = Evaluate (input, out result, out result_set);
464 if (r != null)
465 throw new ArgumentException ("Syntax error on input: partial input");
467 if (result_set == false)
468 throw new ArgumentException ("The expression did not set a result");
470 return result;
473 enum InputKind {
474 EOF,
475 StatementOrExpression,
476 CompilationUnit,
477 Error
481 // Deambiguates the input string to determine if we
482 // want to process a statement or if we want to
483 // process a compilation unit.
485 // This is done using a top-down predictive parser,
486 // since the yacc/jay parser can not deambiguage this
487 // without more than one lookahead token. There are very
488 // few ambiguities.
490 static InputKind ToplevelOrStatement (SeekableStreamReader seekable)
492 Tokenizer tokenizer = new Tokenizer (seekable, (CompilationUnit) Location.SourceFiles [0]);
494 int t = tokenizer.token ();
495 switch (t){
496 case Token.EOF:
497 return InputKind.EOF;
499 // These are toplevels
500 case Token.EXTERN:
501 case Token.OPEN_BRACKET:
502 case Token.ABSTRACT:
503 case Token.CLASS:
504 case Token.ENUM:
505 case Token.INTERFACE:
506 case Token.INTERNAL:
507 case Token.NAMESPACE:
508 case Token.PRIVATE:
509 case Token.PROTECTED:
510 case Token.PUBLIC:
511 case Token.SEALED:
512 case Token.STATIC:
513 case Token.STRUCT:
514 return InputKind.CompilationUnit;
516 // Definitely expression
517 case Token.FIXED:
518 case Token.BOOL:
519 case Token.BYTE:
520 case Token.CHAR:
521 case Token.DECIMAL:
522 case Token.DOUBLE:
523 case Token.FLOAT:
524 case Token.INT:
525 case Token.LONG:
526 case Token.NEW:
527 case Token.OBJECT:
528 case Token.SBYTE:
529 case Token.SHORT:
530 case Token.STRING:
531 case Token.UINT:
532 case Token.ULONG:
533 return InputKind.StatementOrExpression;
535 // These need deambiguation help
536 case Token.USING:
537 t = tokenizer.token ();
538 if (t == Token.EOF)
539 return InputKind.EOF;
541 if (t == Token.IDENTIFIER)
542 return InputKind.CompilationUnit;
543 return InputKind.StatementOrExpression;
546 // Distinguish between:
547 // delegate opt_anonymous_method_signature block
548 // delegate type
549 case Token.DELEGATE:
550 t = tokenizer.token ();
551 if (t == Token.EOF)
552 return InputKind.EOF;
553 if (t == Token.OPEN_PARENS || t == Token.OPEN_BRACE)
554 return InputKind.StatementOrExpression;
555 return InputKind.CompilationUnit;
557 // Distinguih between:
558 // unsafe block
559 // unsafe as modifier of a type declaration
560 case Token.UNSAFE:
561 t = tokenizer.token ();
562 if (t == Token.EOF)
563 return InputKind.EOF;
564 if (t == Token.OPEN_PARENS)
565 return InputKind.StatementOrExpression;
566 return InputKind.CompilationUnit;
568 // These are errors: we list explicitly what we had
569 // from the grammar, ERROR and then everything else
571 case Token.READONLY:
572 case Token.OVERRIDE:
573 case Token.ERROR:
574 return InputKind.Error;
576 // This catches everything else allowed by
577 // expressions. We could add one-by-one use cases
578 // if needed.
579 default:
580 return InputKind.StatementOrExpression;
585 // Parses the string @input and returns a CSharpParser if succeeful.
587 // if @silent is set to true then no errors are
588 // reported to the user. This is used to do various calls to the
589 // parser and check if the expression is parsable.
591 // @partial_input: if @silent is true, then it returns whether the
592 // parsed expression was partial, and more data is needed
594 static CSharpParser ParseString (ParseMode mode, string input, out bool partial_input)
596 partial_input = false;
597 Reset ();
598 queued_fields.Clear ();
600 Stream s = new MemoryStream (Encoding.Default.GetBytes (input));
601 SeekableStreamReader seekable = new SeekableStreamReader (s, Encoding.Default);
603 InputKind kind = ToplevelOrStatement (seekable);
604 if (kind == InputKind.Error){
605 if (mode == ParseMode.ReportErrors)
606 Report.Error (-25, "Detection Parsing Error");
607 partial_input = false;
608 return null;
611 if (kind == InputKind.EOF){
612 if (mode == ParseMode.ReportErrors)
613 Console.Error.WriteLine ("Internal error: EOF condition should have been detected in a previous call with silent=true");
614 partial_input = true;
615 return null;
618 seekable.Position = 0;
620 CSharpParser parser = new CSharpParser (seekable, (CompilationUnit) Location.SourceFiles [0]);
621 parser.ErrorOutput = Report.Stderr;
623 if (kind == InputKind.StatementOrExpression){
624 parser.Lexer.putback_char = Tokenizer.EvalStatementParserCharacter;
625 RootContext.StatementMode = true;
626 } else {
628 // Do not activate EvalCompilationUnitParserCharacter until
629 // I have figured out all the limitations to invoke methods
630 // in the generated classes. See repl.txt
632 parser.Lexer.putback_char = Tokenizer.EvalUsingDeclarationsParserCharacter;
633 //parser.Lexer.putback_char = Tokenizer.EvalCompilationUnitParserCharacter;
634 RootContext.StatementMode = false;
637 if (mode == ParseMode.GetCompletions)
638 parser.Lexer.CompleteOnEOF = true;
640 bool disable_error_reporting;
641 if ((mode == ParseMode.Silent || mode == ParseMode.GetCompletions) && CSharpParser.yacc_verbose_flag == 0)
642 disable_error_reporting = true;
643 else
644 disable_error_reporting = false;
646 if (disable_error_reporting)
647 Report.DisableReporting ();
648 try {
649 parser.parse ();
650 } finally {
651 if (Report.Errors != 0){
652 if (mode != ParseMode.ReportErrors && parser.UnexpectedEOF)
653 partial_input = true;
655 parser.undo.ExecuteUndo ();
656 parser = null;
659 if (disable_error_reporting)
660 Report.EnableReporting ();
662 return parser;
666 // Queue all the fields that we use, as we need to then go from FieldBuilder to FieldInfo
667 // or reflection gets confused (it basically gets confused, and variables override each
668 // other).
670 static ArrayList queued_fields = new ArrayList ();
672 //static ArrayList types = new ArrayList ();
674 static volatile bool invoking;
676 static CompiledMethod CompileBlock (Class host, Undo undo)
678 RootContext.ResolveTree ();
679 if (Report.Errors != 0){
680 undo.ExecuteUndo ();
681 return null;
684 RootContext.PopulateTypes ();
686 if (Report.Errors != 0){
687 undo.ExecuteUndo ();
688 return null;
691 TypeBuilder tb = null;
692 MethodBuilder mb = null;
694 if (host != null){
695 tb = host.TypeBuilder;
696 mb = null;
697 foreach (MemberCore member in host.Methods){
698 if (member.Name != "Host")
699 continue;
701 MethodOrOperator method = (MethodOrOperator) member;
702 mb = method.MethodBuilder;
703 break;
706 if (mb == null)
707 throw new Exception ("Internal error: did not find the method builder for the generated method");
710 RootContext.EmitCode ();
711 if (Report.Errors != 0)
712 return null;
714 RootContext.CloseTypes ();
716 if (Environment.GetEnvironmentVariable ("SAVE") != null)
717 CodeGen.Save (current_debug_name, false);
719 if (host == null)
720 return null;
723 // Unlike Mono, .NET requires that the MethodInfo is fetched, it cant
724 // work from MethodBuilders. Retarded, I know.
726 Type tt = CodeGen.Assembly.Builder.GetType (tb.Name);
727 MethodInfo mi = tt.GetMethod (mb.Name);
729 // Pull the FieldInfos from the type, and keep track of them
730 foreach (Field field in queued_fields){
731 FieldInfo fi = tt.GetField (field.Name);
733 FieldInfo old = (FieldInfo) fields [field.Name];
735 // If a previous value was set, nullify it, so that we do
736 // not leak memory
737 if (old != null){
738 if (TypeManager.IsStruct (old.FieldType)){
740 // TODO: Clear fields for structs
742 } else {
743 try {
744 old.SetValue (null, null);
745 } catch {
750 fields [field.Name] = fi;
752 //types.Add (tb);
754 queued_fields.Clear ();
756 return (CompiledMethod) System.Delegate.CreateDelegate (typeof (CompiledMethod), mi);
759 static internal void LoadAliases (NamespaceEntry ns)
761 ns.Populate (using_alias_list, using_list);
764 /// <summary>
765 /// A sentinel value used to indicate that no value was
766 /// was set by the compiled function. This is used to
767 /// differentiate between a function not returning a
768 /// value and null.
769 /// </summary>
770 public class NoValueSet {
773 static internal FieldInfo LookupField (string name)
775 FieldInfo fi = (FieldInfo) fields [name];
777 return fi;
781 // Puts the FieldBuilder into a queue of names that will be
782 // registered. We can not register FieldBuilders directly
783 // we need to fetch the FieldInfo after Reflection cooks the
784 // types, or bad things happen (bad means: FieldBuilders behave
785 // incorrectly across multiple assemblies, causing assignments to
786 // invalid areas
788 // This also serves for the parser to register Field classes
789 // that should be exposed as global variables
791 static internal void QueueField (Field f)
793 queued_fields.Add (f);
796 static string Quote (string s)
798 if (s.IndexOf ('"') != -1)
799 s = s.Replace ("\"", "\\\"");
801 return "\"" + s + "\"";
804 static public string GetUsing ()
806 lock (evaluator_lock){
807 StringBuilder sb = new StringBuilder ();
809 foreach (object x in using_alias_list)
810 sb.Append (String.Format ("using {0};\n", x));
812 foreach (object x in using_list)
813 sb.Append (String.Format ("using {0};\n", x));
815 return sb.ToString ();
819 static internal ICollection GetUsingList ()
821 ArrayList res = new ArrayList (using_list.Count);
822 foreach (object ue in using_list)
823 res.Add (ue.ToString ());
824 return res;
827 static internal string [] GetVarNames ()
829 lock (evaluator_lock){
830 return (string []) new ArrayList (fields.Keys).ToArray (typeof (string));
834 static public string GetVars ()
836 lock (evaluator_lock){
837 StringBuilder sb = new StringBuilder ();
839 foreach (DictionaryEntry de in fields){
840 FieldInfo fi = LookupField ((string) de.Key);
841 object value = null;
842 bool error = false;
844 try {
845 if (value == null)
846 value = "null";
847 value = fi.GetValue (null);
848 if (value is string)
849 value = Quote ((string)value);
850 } catch {
851 error = true;
854 if (error)
855 sb.Append (String.Format ("{0} {1} <error reading value>", TypeManager.CSharpName(fi.FieldType), de.Key));
856 else
857 sb.Append (String.Format ("{0} {1} = {2}", TypeManager.CSharpName(fi.FieldType), de.Key, value));
860 return sb.ToString ();
864 /// <summary>
865 /// Loads the given assembly and exposes the API to the user.
866 /// </summary>
867 static public void LoadAssembly (string file)
869 lock (evaluator_lock){
870 Driver.LoadAssembly (file, false);
871 GlobalRootNamespace.Instance.ComputeNamespaces ();
875 /// <summary>
876 /// Exposes the API of the given assembly to the Evaluator
877 /// </summary>
878 static public void ReferenceAssembly (Assembly a)
880 lock (evaluator_lock){
881 GlobalRootNamespace.Instance.AddAssemblyReference (a);
882 GlobalRootNamespace.Instance.ComputeNamespaces ();
889 /// <summary>
890 /// A delegate that can be used to invoke the
891 /// compiled expression or statement.
892 /// </summary>
893 /// <remarks>
894 /// Since the Compile methods will compile
895 /// statements and expressions into the same
896 /// delegate, you can tell if a value was returned
897 /// by checking whether the returned value is of type
898 /// NoValueSet.
899 /// </remarks>
901 public delegate void CompiledMethod (ref object retvalue);
903 /// <summary>
904 /// The default base class for every interaction line
905 /// </summary>
906 /// <remarks>
907 /// The expressions and statements behave as if they were
908 /// a static method of this class. The InteractiveBase class
909 /// contains a number of useful methods, but can be overwritten
910 /// by setting the InteractiveBaseType property in the Evaluator
911 /// </remarks>
912 public class InteractiveBase {
913 /// <summary>
914 /// Determines where the standard output of methods in this class will go.
915 /// </summary>
916 public static TextWriter Output = Console.Out;
918 /// <summary>
919 /// Determines where the standard error of methods in this class will go.
920 /// </summary>
921 public static TextWriter Error = Console.Error;
923 /// <summary>
924 /// The primary prompt used for interactive use.
925 /// </summary>
926 public static string Prompt = "csharp> ";
928 /// <summary>
929 /// The secondary prompt used for interactive use (used when
930 /// an expression is incomplete).
931 /// </summary>
932 public static string ContinuationPrompt = " > ";
934 /// <summary>
935 /// Used to signal that the user has invoked the `quit' statement.
936 /// </summary>
937 public static bool QuitRequested;
939 /// <summary>
940 /// Shows all the variables defined so far.
941 /// </summary>
942 static public void ShowVars ()
944 Output.Write (Evaluator.GetVars ());
945 Output.Flush ();
948 /// <summary>
949 /// Displays the using statements in effect at this point.
950 /// </summary>
951 static public void ShowUsing ()
953 Output.Write (Evaluator.GetUsing ());
954 Output.Flush ();
957 public delegate void Simple ();
959 /// <summary>
960 /// Times the execution of the given delegate
961 /// </summary>
962 static public TimeSpan Time (Simple a)
964 DateTime start = DateTime.Now;
965 a ();
966 return DateTime.Now - start;
969 #if !SMCS_SOURCE
970 /// <summary>
971 /// Loads the assemblies from a package
972 /// </summary>
973 /// <remarks>
974 /// Loads the assemblies from a package. This is equivalent
975 /// to passing the -pkg: command line flag to the C# compiler
976 /// on the command line.
977 /// </remarks>
978 static public void LoadPackage (string pkg)
980 if (pkg == null){
981 Error.WriteLine ("Invalid package specified");
982 return;
985 string pkgout = Driver.GetPackageFlags (pkg, false);
986 if (pkgout == null)
987 return;
989 string [] xargs = pkgout.Trim (new Char [] {' ', '\n', '\r', '\t'}).
990 Split (new Char [] { ' ', '\t'});
992 foreach (string s in xargs){
993 if (s.StartsWith ("-r:") || s.StartsWith ("/r:") || s.StartsWith ("/reference:")){
994 string lib = s.Substring (s.IndexOf (':')+1);
996 Evaluator.LoadAssembly (lib);
997 continue;
1001 #endif
1003 /// <summary>
1004 /// Loads the assembly
1005 /// </summary>
1006 /// <remarks>
1007 /// Loads the specified assembly and makes its types
1008 /// available to the evaluator. This is equivalent
1009 /// to passing the -pkg: command line flag to the C#
1010 /// compiler on the command line.
1011 /// </remarks>
1012 static public void LoadAssembly (string assembly)
1014 Evaluator.LoadAssembly (assembly);
1017 /// <summary>
1018 /// Returns a list of available static methods.
1019 /// </summary>
1020 static public string help {
1021 get {
1022 return "Static methods:\n"+
1023 " Describe(obj) - Describes the object's type\n" +
1024 " LoadPackage (pkg); - Loads the given Package (like -pkg:FILE)\n" +
1025 " LoadAssembly (ass) - Loads the given assembly (like -r:ASS)\n" +
1026 " ShowVars (); - Shows defined local variables.\n" +
1027 " ShowUsing (); - Show active using decltions.\n" +
1028 " Prompt - The prompt used by the C# shell\n" +
1029 " ContinuationPrompt - The prompt for partial input\n" +
1030 " Time(() -> { }) - Times the specified code\n" +
1031 " quit;\n" +
1032 " help;\n";
1036 /// <summary>
1037 /// Indicates to the read-eval-print-loop that the interaction should be finished.
1038 /// </summary>
1039 static public object quit {
1040 get {
1041 QuitRequested = true;
1042 return null;
1046 #if !NET_2_1
1047 /// <summary>
1048 /// Describes an object or a type.
1049 /// </summary>
1050 /// <remarks>
1051 /// This method will show a textual representation
1052 /// of the object's type. If the object is a
1053 /// System.Type it renders the type directly,
1054 /// otherwise it renders the type returned by
1055 /// invoking GetType on the object.
1056 /// </remarks>
1057 static public string Describe (object x)
1059 if (x == null)
1060 return "";
1062 Type t = x as Type;
1063 if (t == null)
1064 t = x.GetType ();
1066 StringWriter sw = new StringWriter ();
1067 new Outline (t, sw, true, false, false).OutlineType ();
1068 return sw.ToString ();
1070 #endif
1074 // A local variable reference that will create a Field in a
1075 // Class with the resolved type. This is necessary so we can
1076 // support "var" as a field type in a class declaration.
1078 // We allow LocalVariableReferece to do the heavy lifting, and
1079 // then we insert the field with the resolved type
1081 public class LocalVariableReferenceWithClassSideEffect : LocalVariableReference {
1082 TypeContainer container;
1083 string name;
1085 public LocalVariableReferenceWithClassSideEffect (TypeContainer container, string name, Block current_block, string local_variable_id, Location loc)
1086 : base (current_block, local_variable_id, loc)
1088 this.container = container;
1089 this.name = name;
1092 public override bool Equals (object obj)
1094 LocalVariableReferenceWithClassSideEffect lvr = obj as LocalVariableReferenceWithClassSideEffect;
1095 if (lvr == null)
1096 return false;
1098 if (lvr.name != name || lvr.container != container)
1099 return false;
1101 return base.Equals (obj);
1104 public override int GetHashCode ()
1106 return name.GetHashCode ();
1109 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
1111 Expression ret = base.DoResolveLValue (ec, right_side);
1112 if (ret == null)
1113 return null;
1115 Field f = new Field (container, new TypeExpression (ret.Type, Location),
1116 Modifiers.PUBLIC | Modifiers.STATIC,
1117 new MemberName (name, Location), null);
1118 container.AddField (f);
1119 if (f.Define ())
1120 Evaluator.QueueField (f);
1122 return ret;
1126 /// <summary>
1127 /// A class used to assign values if the source expression is not void
1129 /// Used by the interactive shell to allow it to call this code to set
1130 /// the return value for an invocation.
1131 /// </summary>
1132 class OptionalAssign : SimpleAssign {
1133 public OptionalAssign (Expression t, Expression s, Location loc)
1134 : base (t, s, loc)
1138 public override Expression DoResolve (EmitContext ec)
1140 CloneContext cc = new CloneContext ();
1141 Expression clone = source.Clone (cc);
1143 clone = clone.Resolve (ec);
1144 if (clone == null)
1145 return null;
1147 // This means its really a statement.
1148 if (clone.Type == TypeManager.void_type){
1149 source = source.Resolve (ec);
1150 target = null;
1151 type = TypeManager.void_type;
1152 eclass = ExprClass.Value;
1153 return this;
1156 return base.DoResolve (ec);
1159 public override void Emit (EmitContext ec)
1161 if (target == null)
1162 source.Emit (ec);
1163 else
1164 base.Emit (ec);
1167 public override void EmitStatement (EmitContext ec)
1169 if (target == null)
1170 source.Emit (ec);
1171 else
1172 base.EmitStatement (ec);
1176 public class Undo {
1177 ArrayList undo_types;
1179 public Undo ()
1181 undo_types = new ArrayList ();
1184 public void AddTypeContainer (TypeContainer current_container, TypeContainer tc)
1186 if (current_container == tc){
1187 Console.Error.WriteLine ("Internal error: inserting container into itself");
1188 return;
1191 if (undo_types == null)
1192 undo_types = new ArrayList ();
1193 undo_types.Add (new Pair (current_container, tc));
1196 public void ExecuteUndo ()
1198 if (undo_types == null)
1199 return;
1201 foreach (Pair p in undo_types){
1202 TypeContainer current_container = (TypeContainer) p.First;
1204 current_container.RemoveTypeContainer ((TypeContainer) p.Second);
1206 undo_types = null;