2009-06-26 Zoltan Varga <vargaz@gmail.com>
[mcs.git] / mcs / eval.cs
blobc17dd23c04dd9b67b70bbde5193cf8edf222521a
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 driver = Driver.Create (args, false);
109 if (driver == null)
110 throw new Exception ("Failed to create compiler driver with the given arguments");
112 driver.ProcessDefaultConfig ();
114 ArrayList startup_files = new ArrayList ();
115 foreach (CompilationUnit file in Location.SourceFiles)
116 startup_files.Add (file.Path);
118 CompilerCallableEntryPoint.Reset ();
119 driver.LoadReferences ();
120 RootContext.EvalMode = true;
121 inited = true;
123 return (string []) startup_files.ToArray (typeof (string));
127 static void Init ()
129 Init (new string [0]);
132 static void Reset ()
134 CompilerCallableEntryPoint.PartialReset ();
137 // PartialReset should not reset the core types, this is very redundant.
139 if (!TypeManager.InitCoreTypes ())
140 throw new Exception ("Failed to InitCoreTypes");
141 TypeManager.InitOptionalCoreTypes ();
143 Location.AddFile ("{interactive}");
144 Location.Initialize ();
146 current_debug_name = "interactive" + (count++) + ".dll";
147 if (Environment.GetEnvironmentVariable ("SAVE") != null){
148 CodeGen.Init (current_debug_name, current_debug_name, false);
149 } else
150 CodeGen.InitDynamic (current_debug_name);
153 /// <summary>
154 /// The base class for the classes that host the user generated code
155 /// </summary>
156 /// <remarks>
158 /// This is the base class that will host the code
159 /// executed by the Evaluator. By default
160 /// this is the Mono.CSharp.InteractiveBase class
161 /// which is useful for interactive use.
163 /// By changing this property you can control the
164 /// base class and the static members that are
165 /// available to your evaluated code.
166 /// </remarks>
167 static public Type InteractiveBaseClass {
168 get {
169 return interactive_base_class;
172 set {
173 if (value == null)
174 throw new ArgumentNullException ();
176 lock (evaluator_lock)
177 interactive_base_class = value;
181 /// <summary>
182 /// Interrupts the evaluation of an expression executing in Evaluate.
183 /// </summary>
184 /// <remarks>
185 /// Use this method to interrupt long-running invocations.
186 /// </remarks>
187 public static void Interrupt ()
189 if (!inited || !invoking)
190 return;
192 if (invoke_thread != null)
193 invoke_thread.Abort ();
196 /// <summary>
197 /// Compiles the input string and returns a delegate that represents the compiled code.
198 /// </summary>
199 /// <remarks>
201 /// Compiles the input string as a C# expression or
202 /// statement, unlike the Evaluate method, the
203 /// resulting delegate can be invoked multiple times
204 /// without incurring in the compilation overhead.
206 /// If the return value of this function is null,
207 /// this indicates that the parsing was complete.
208 /// If the return value is a string it indicates
209 /// that the input string was partial and that the
210 /// invoking code should provide more code before
211 /// the code can be successfully compiled.
213 /// If you know that you will always get full expressions or
214 /// statements and do not care about partial input, you can use
215 /// the other Compile overload.
217 /// On success, in addition to returning null, the
218 /// compiled parameter will be set to the delegate
219 /// that can be invoked to execute the code.
221 /// </remarks>
222 static public string Compile (string input, out CompiledMethod compiled)
224 if (input == null || input.Length == 0){
225 compiled = null;
226 return null;
229 lock (evaluator_lock){
230 if (!inited)
231 Init ();
233 bool partial_input;
234 CSharpParser parser = ParseString (ParseMode.Silent, input, out partial_input);
235 if (parser == null){
236 compiled = null;
237 if (partial_input)
238 return input;
240 ParseString (ParseMode.ReportErrors, input, out partial_input);
241 return null;
244 object parser_result = parser.InteractiveResult;
246 if (!(parser_result is Class)){
247 int errors = Report.Errors;
249 NamespaceEntry.VerifyAllUsing ();
250 if (errors == Report.Errors)
251 parser.CurrentNamespace.Extract (using_alias_list, using_list);
254 compiled = CompileBlock (parser_result as Class, parser.undo);
257 return null;
260 /// <summary>
261 /// Compiles the input string and returns a delegate that represents the compiled code.
262 /// </summary>
263 /// <remarks>
265 /// Compiles the input string as a C# expression or
266 /// statement, unlike the Evaluate method, the
267 /// resulting delegate can be invoked multiple times
268 /// without incurring in the compilation overhead.
270 /// This method can only deal with fully formed input
271 /// strings and does not provide a completion mechanism.
272 /// If you must deal with partial input (for example for
273 /// interactive use) use the other overload.
275 /// On success, a delegate is returned that can be used
276 /// to invoke the method.
278 /// </remarks>
279 static public CompiledMethod Compile (string input)
281 CompiledMethod compiled;
283 // Ignore partial inputs
284 if (Compile (input, out compiled) != null){
285 // Error, the input was partial.
286 return null;
289 // Either null (on error) or the compiled method.
290 return compiled;
294 // Todo: Should we handle errors, or expect the calling code to setup
295 // the recording themselves?
298 /// <summary>
299 /// Evaluates and expression or statement and returns any result values.
300 /// </summary>
301 /// <remarks>
302 /// Evaluates the input string as a C# expression or
303 /// statement. If the input string is an expression
304 /// the result will be stored in the result variable
305 /// and the result_set variable will be set to true.
307 /// It is necessary to use the result/result_set
308 /// pair to identify when a result was set (for
309 /// example, execution of user-provided input can be
310 /// an expression, a statement or others, and
311 /// result_set would only be set if the input was an
312 /// expression.
314 /// If the return value of this function is null,
315 /// this indicates that the parsing was complete.
316 /// If the return value is a string, it indicates
317 /// that the input is partial and that the user
318 /// should provide an updated string.
319 /// </remarks>
320 public static string Evaluate (string input, out object result, out bool result_set)
322 CompiledMethod compiled;
324 result_set = false;
325 result = null;
327 input = Compile (input, out compiled);
328 if (input != null)
329 return input;
331 if (compiled == null)
332 return null;
335 // The code execution does not need to keep the compiler lock
337 object retval = typeof (NoValueSet);
339 try {
340 invoke_thread = System.Threading.Thread.CurrentThread;
341 invoking = true;
342 compiled (ref retval);
343 } catch (ThreadAbortException e){
344 Thread.ResetAbort ();
345 Console.WriteLine ("Interrupted!\n{0}", e);
346 } finally {
347 invoking = false;
351 // We use a reference to a compiler type, in this case
352 // Driver as a flag to indicate that this was a statement
354 if (retval != typeof (NoValueSet)){
355 result_set = true;
356 result = retval;
359 return null;
362 public static string [] GetCompletions (string input, out string prefix)
364 prefix = "";
365 if (input == null || input.Length == 0)
366 return null;
368 lock (evaluator_lock){
369 if (!inited)
370 Init ();
372 bool partial_input;
373 CSharpParser parser = ParseString (ParseMode.GetCompletions, input, out partial_input);
374 if (parser == null){
375 if (CSharpParser.yacc_verbose_flag != 0)
376 Console.WriteLine ("DEBUG: No completions available");
377 return null;
380 Class parser_result = parser.InteractiveResult as Class;
382 if (parser_result == null){
383 if (CSharpParser.yacc_verbose_flag != 0)
384 Console.WriteLine ("Do not know how to cope with !Class yet");
385 return null;
388 try {
389 RootContext.ResolveTree ();
390 if (Report.Errors != 0)
391 return null;
393 RootContext.PopulateTypes ();
394 if (Report.Errors != 0)
395 return null;
397 MethodOrOperator method = null;
398 foreach (MemberCore member in parser_result.Methods){
399 if (member.Name != "Host")
400 continue;
402 method = (MethodOrOperator) member;
403 break;
405 if (method == null)
406 throw new InternalErrorException ("did not find the the Host method");
408 EmitContext ec = method.CreateEmitContext (method.Parent, null);
409 bool unreach;
411 try {
412 ec.ResolveTopBlock (null, method.Block, method.ParameterInfo, method, out unreach);
413 } catch (CompletionResult cr){
414 prefix = cr.BaseText;
415 return cr.Result;
417 } finally {
418 parser.undo.ExecuteUndo ();
422 return null;
425 /// <summary>
426 /// Executes the given expression or statement.
427 /// </summary>
428 /// <remarks>
429 /// Executes the provided statement, returns true
430 /// on success, false on parsing errors. Exceptions
431 /// might be thrown by the called code.
432 /// </remarks>
433 public static bool Run (string statement)
435 if (!inited)
436 Init ();
438 object result;
439 bool result_set;
441 bool ok = Evaluate (statement, out result, out result_set) == null;
443 return ok;
446 /// <summary>
447 /// Evaluates and expression or statement and returns the result.
448 /// </summary>
449 /// <remarks>
450 /// Evaluates the input string as a C# expression or
451 /// statement and returns the value.
453 /// This method will throw an exception if there is a syntax error,
454 /// of if the provided input is not an expression but a statement.
455 /// </remarks>
456 public static object Evaluate (string input)
458 object result;
459 bool result_set;
461 string r = Evaluate (input, out result, out result_set);
463 if (r != null)
464 throw new ArgumentException ("Syntax error on input: partial input");
466 if (result_set == false)
467 throw new ArgumentException ("The expression did not set a result");
469 return result;
472 enum InputKind {
473 EOF,
474 StatementOrExpression,
475 CompilationUnit,
476 Error
480 // Deambiguates the input string to determine if we
481 // want to process a statement or if we want to
482 // process a compilation unit.
484 // This is done using a top-down predictive parser,
485 // since the yacc/jay parser can not deambiguage this
486 // without more than one lookahead token. There are very
487 // few ambiguities.
489 static InputKind ToplevelOrStatement (SeekableStreamReader seekable)
491 Tokenizer tokenizer = new Tokenizer (seekable, (CompilationUnit) Location.SourceFiles [0]);
493 int t = tokenizer.token ();
494 switch (t){
495 case Token.EOF:
496 return InputKind.EOF;
498 // These are toplevels
499 case Token.EXTERN:
500 case Token.OPEN_BRACKET:
501 case Token.ABSTRACT:
502 case Token.CLASS:
503 case Token.ENUM:
504 case Token.INTERFACE:
505 case Token.INTERNAL:
506 case Token.NAMESPACE:
507 case Token.PRIVATE:
508 case Token.PROTECTED:
509 case Token.PUBLIC:
510 case Token.SEALED:
511 case Token.STATIC:
512 case Token.STRUCT:
513 return InputKind.CompilationUnit;
515 // Definitely expression
516 case Token.FIXED:
517 case Token.BOOL:
518 case Token.BYTE:
519 case Token.CHAR:
520 case Token.DECIMAL:
521 case Token.DOUBLE:
522 case Token.FLOAT:
523 case Token.INT:
524 case Token.LONG:
525 case Token.NEW:
526 case Token.OBJECT:
527 case Token.SBYTE:
528 case Token.SHORT:
529 case Token.STRING:
530 case Token.UINT:
531 case Token.ULONG:
532 return InputKind.StatementOrExpression;
534 // These need deambiguation help
535 case Token.USING:
536 t = tokenizer.token ();
537 if (t == Token.EOF)
538 return InputKind.EOF;
540 if (t == Token.IDENTIFIER)
541 return InputKind.CompilationUnit;
542 return InputKind.StatementOrExpression;
545 // Distinguish between:
546 // delegate opt_anonymous_method_signature block
547 // delegate type
548 case Token.DELEGATE:
549 t = tokenizer.token ();
550 if (t == Token.EOF)
551 return InputKind.EOF;
552 if (t == Token.OPEN_PARENS || t == Token.OPEN_BRACE)
553 return InputKind.StatementOrExpression;
554 return InputKind.CompilationUnit;
556 // Distinguih between:
557 // unsafe block
558 // unsafe as modifier of a type declaration
559 case Token.UNSAFE:
560 t = tokenizer.token ();
561 if (t == Token.EOF)
562 return InputKind.EOF;
563 if (t == Token.OPEN_PARENS)
564 return InputKind.StatementOrExpression;
565 return InputKind.CompilationUnit;
567 // These are errors: we list explicitly what we had
568 // from the grammar, ERROR and then everything else
570 case Token.READONLY:
571 case Token.OVERRIDE:
572 case Token.ERROR:
573 return InputKind.Error;
575 // This catches everything else allowed by
576 // expressions. We could add one-by-one use cases
577 // if needed.
578 default:
579 return InputKind.StatementOrExpression;
584 // Parses the string @input and returns a CSharpParser if succeeful.
586 // if @silent is set to true then no errors are
587 // reported to the user. This is used to do various calls to the
588 // parser and check if the expression is parsable.
590 // @partial_input: if @silent is true, then it returns whether the
591 // parsed expression was partial, and more data is needed
593 static CSharpParser ParseString (ParseMode mode, string input, out bool partial_input)
595 partial_input = false;
596 Reset ();
597 queued_fields.Clear ();
599 Stream s = new MemoryStream (Encoding.Default.GetBytes (input));
600 SeekableStreamReader seekable = new SeekableStreamReader (s, Encoding.Default);
602 InputKind kind = ToplevelOrStatement (seekable);
603 if (kind == InputKind.Error){
604 if (mode == ParseMode.ReportErrors)
605 Report.Error (-25, "Detection Parsing Error");
606 partial_input = false;
607 return null;
610 if (kind == InputKind.EOF){
611 if (mode == ParseMode.ReportErrors)
612 Console.Error.WriteLine ("Internal error: EOF condition should have been detected in a previous call with silent=true");
613 partial_input = true;
614 return null;
617 seekable.Position = 0;
619 CSharpParser parser = new CSharpParser (seekable, (CompilationUnit) Location.SourceFiles [0]);
620 parser.ErrorOutput = Report.Stderr;
622 if (kind == InputKind.StatementOrExpression){
623 parser.Lexer.putback_char = Tokenizer.EvalStatementParserCharacter;
624 RootContext.StatementMode = true;
625 } else {
627 // Do not activate EvalCompilationUnitParserCharacter until
628 // I have figured out all the limitations to invoke methods
629 // in the generated classes. See repl.txt
631 parser.Lexer.putback_char = Tokenizer.EvalUsingDeclarationsParserCharacter;
632 //parser.Lexer.putback_char = Tokenizer.EvalCompilationUnitParserCharacter;
633 RootContext.StatementMode = false;
636 if (mode == ParseMode.GetCompletions)
637 parser.Lexer.CompleteOnEOF = true;
639 bool disable_error_reporting;
640 if ((mode == ParseMode.Silent || mode == ParseMode.GetCompletions) && CSharpParser.yacc_verbose_flag == 0)
641 disable_error_reporting = true;
642 else
643 disable_error_reporting = false;
645 if (disable_error_reporting)
646 Report.DisableReporting ();
647 try {
648 parser.parse ();
649 } finally {
650 if (Report.Errors != 0){
651 if (mode != ParseMode.ReportErrors && parser.UnexpectedEOF)
652 partial_input = true;
654 parser.undo.ExecuteUndo ();
655 parser = null;
658 if (disable_error_reporting)
659 Report.EnableReporting ();
661 return parser;
665 // Queue all the fields that we use, as we need to then go from FieldBuilder to FieldInfo
666 // or reflection gets confused (it basically gets confused, and variables override each
667 // other).
669 static ArrayList queued_fields = new ArrayList ();
671 //static ArrayList types = new ArrayList ();
673 static volatile bool invoking;
675 static CompiledMethod CompileBlock (Class host, Undo undo)
677 RootContext.ResolveTree ();
678 if (Report.Errors != 0){
679 undo.ExecuteUndo ();
680 return null;
683 RootContext.PopulateTypes ();
685 if (Report.Errors != 0){
686 undo.ExecuteUndo ();
687 return null;
690 TypeBuilder tb = null;
691 MethodBuilder mb = null;
693 if (host != null){
694 tb = host.TypeBuilder;
695 mb = null;
696 foreach (MemberCore member in host.Methods){
697 if (member.Name != "Host")
698 continue;
700 MethodOrOperator method = (MethodOrOperator) member;
701 mb = method.MethodBuilder;
702 break;
705 if (mb == null)
706 throw new Exception ("Internal error: did not find the method builder for the generated method");
709 RootContext.EmitCode ();
710 if (Report.Errors != 0)
711 return null;
713 RootContext.CloseTypes ();
715 if (Environment.GetEnvironmentVariable ("SAVE") != null)
716 CodeGen.Save (current_debug_name, false);
718 if (host == null)
719 return null;
722 // Unlike Mono, .NET requires that the MethodInfo is fetched, it cant
723 // work from MethodBuilders. Retarded, I know.
725 Type tt = CodeGen.Assembly.Builder.GetType (tb.Name);
726 MethodInfo mi = tt.GetMethod (mb.Name);
728 // Pull the FieldInfos from the type, and keep track of them
729 foreach (Field field in queued_fields){
730 FieldInfo fi = tt.GetField (field.Name);
732 FieldInfo old = (FieldInfo) fields [field.Name];
734 // If a previous value was set, nullify it, so that we do
735 // not leak memory
736 if (old != null){
737 if (TypeManager.IsStruct (old.FieldType)){
739 // TODO: Clear fields for structs
741 } else {
742 try {
743 old.SetValue (null, null);
744 } catch {
749 fields [field.Name] = fi;
751 //types.Add (tb);
753 queued_fields.Clear ();
755 return (CompiledMethod) System.Delegate.CreateDelegate (typeof (CompiledMethod), mi);
758 static internal void LoadAliases (NamespaceEntry ns)
760 ns.Populate (using_alias_list, using_list);
763 /// <summary>
764 /// A sentinel value used to indicate that no value was
765 /// was set by the compiled function. This is used to
766 /// differentiate between a function not returning a
767 /// value and null.
768 /// </summary>
769 public class NoValueSet {
772 static internal FieldInfo LookupField (string name)
774 FieldInfo fi = (FieldInfo) fields [name];
776 return fi;
780 // Puts the FieldBuilder into a queue of names that will be
781 // registered. We can not register FieldBuilders directly
782 // we need to fetch the FieldInfo after Reflection cooks the
783 // types, or bad things happen (bad means: FieldBuilders behave
784 // incorrectly across multiple assemblies, causing assignments to
785 // invalid areas
787 // This also serves for the parser to register Field classes
788 // that should be exposed as global variables
790 static internal void QueueField (Field f)
792 queued_fields.Add (f);
795 static string Quote (string s)
797 if (s.IndexOf ('"') != -1)
798 s = s.Replace ("\"", "\\\"");
800 return "\"" + s + "\"";
803 static public string GetUsing ()
805 lock (evaluator_lock){
806 StringBuilder sb = new StringBuilder ();
808 foreach (object x in using_alias_list)
809 sb.Append (String.Format ("using {0};\n", x));
811 foreach (object x in using_list)
812 sb.Append (String.Format ("using {0};\n", x));
814 return sb.ToString ();
818 static internal ICollection GetUsingList ()
820 ArrayList res = new ArrayList (using_list.Count);
821 foreach (object ue in using_list)
822 res.Add (ue.ToString ());
823 return res;
826 static internal string [] GetVarNames ()
828 lock (evaluator_lock){
829 return (string []) new ArrayList (fields.Keys).ToArray (typeof (string));
833 static public string GetVars ()
835 lock (evaluator_lock){
836 StringBuilder sb = new StringBuilder ();
838 foreach (DictionaryEntry de in fields){
839 FieldInfo fi = LookupField ((string) de.Key);
840 object value = null;
841 bool error = false;
843 try {
844 if (value == null)
845 value = "null";
846 value = fi.GetValue (null);
847 if (value is string)
848 value = Quote ((string)value);
849 } catch {
850 error = true;
853 if (error)
854 sb.Append (String.Format ("{0} {1} <error reading value>", TypeManager.CSharpName(fi.FieldType), de.Key));
855 else
856 sb.Append (String.Format ("{0} {1} = {2}", TypeManager.CSharpName(fi.FieldType), de.Key, value));
859 return sb.ToString ();
863 /// <summary>
864 /// Loads the given assembly and exposes the API to the user.
865 /// </summary>
866 static public void LoadAssembly (string file)
868 lock (evaluator_lock){
869 Driver.LoadAssembly (file, false);
870 GlobalRootNamespace.Instance.ComputeNamespaces ();
874 /// <summary>
875 /// Exposes the API of the given assembly to the Evaluator
876 /// </summary>
877 static public void ReferenceAssembly (Assembly a)
879 lock (evaluator_lock){
880 GlobalRootNamespace.Instance.AddAssemblyReference (a);
881 GlobalRootNamespace.Instance.ComputeNamespaces ();
888 /// <summary>
889 /// A delegate that can be used to invoke the
890 /// compiled expression or statement.
891 /// </summary>
892 /// <remarks>
893 /// Since the Compile methods will compile
894 /// statements and expressions into the same
895 /// delegate, you can tell if a value was returned
896 /// by checking whether the returned value is of type
897 /// NoValueSet.
898 /// </remarks>
900 public delegate void CompiledMethod (ref object retvalue);
902 /// <summary>
903 /// The default base class for every interaction line
904 /// </summary>
905 /// <remarks>
906 /// The expressions and statements behave as if they were
907 /// a static method of this class. The InteractiveBase class
908 /// contains a number of useful methods, but can be overwritten
909 /// by setting the InteractiveBaseType property in the Evaluator
910 /// </remarks>
911 public class InteractiveBase {
912 /// <summary>
913 /// Determines where the standard output of methods in this class will go.
914 /// </summary>
915 public static TextWriter Output = Console.Out;
917 /// <summary>
918 /// Determines where the standard error of methods in this class will go.
919 /// </summary>
920 public static TextWriter Error = Console.Error;
922 /// <summary>
923 /// The primary prompt used for interactive use.
924 /// </summary>
925 public static string Prompt = "csharp> ";
927 /// <summary>
928 /// The secondary prompt used for interactive use (used when
929 /// an expression is incomplete).
930 /// </summary>
931 public static string ContinuationPrompt = " > ";
933 /// <summary>
934 /// Used to signal that the user has invoked the `quit' statement.
935 /// </summary>
936 public static bool QuitRequested;
938 /// <summary>
939 /// Shows all the variables defined so far.
940 /// </summary>
941 static public void ShowVars ()
943 Output.Write (Evaluator.GetVars ());
944 Output.Flush ();
947 /// <summary>
948 /// Displays the using statements in effect at this point.
949 /// </summary>
950 static public void ShowUsing ()
952 Output.Write (Evaluator.GetUsing ());
953 Output.Flush ();
956 public delegate void Simple ();
958 /// <summary>
959 /// Times the execution of the given delegate
960 /// </summary>
961 static public TimeSpan Time (Simple a)
963 DateTime start = DateTime.Now;
964 a ();
965 return DateTime.Now - start;
968 #if !SMCS_SOURCE
969 /// <summary>
970 /// Loads the assemblies from a package
971 /// </summary>
972 /// <remarks>
973 /// Loads the assemblies from a package. This is equivalent
974 /// to passing the -pkg: command line flag to the C# compiler
975 /// on the command line.
976 /// </remarks>
977 static public void LoadPackage (string pkg)
979 if (pkg == null){
980 Error.WriteLine ("Invalid package specified");
981 return;
984 string pkgout = Driver.GetPackageFlags (pkg, false);
985 if (pkgout == null)
986 return;
988 string [] xargs = pkgout.Trim (new Char [] {' ', '\n', '\r', '\t'}).
989 Split (new Char [] { ' ', '\t'});
991 foreach (string s in xargs){
992 if (s.StartsWith ("-r:") || s.StartsWith ("/r:") || s.StartsWith ("/reference:")){
993 string lib = s.Substring (s.IndexOf (':')+1);
995 Evaluator.LoadAssembly (lib);
996 continue;
1000 #endif
1002 /// <summary>
1003 /// Loads the assembly
1004 /// </summary>
1005 /// <remarks>
1006 /// Loads the specified assembly and makes its types
1007 /// available to the evaluator. This is equivalent
1008 /// to passing the -pkg: command line flag to the C#
1009 /// compiler on the command line.
1010 /// </remarks>
1011 static public void LoadAssembly (string assembly)
1013 Evaluator.LoadAssembly (assembly);
1016 /// <summary>
1017 /// Returns a list of available static methods.
1018 /// </summary>
1019 static public string help {
1020 get {
1021 return "Static methods:\n"+
1022 " Describe(obj) - Describes the object's type\n" +
1023 " LoadPackage (pkg); - Loads the given Package (like -pkg:FILE)\n" +
1024 " LoadAssembly (ass) - Loads the given assembly (like -r:ASS)\n" +
1025 " ShowVars (); - Shows defined local variables.\n" +
1026 " ShowUsing (); - Show active using decltions.\n" +
1027 " Prompt - The prompt used by the C# shell\n" +
1028 " ContinuationPrompt - The prompt for partial input\n" +
1029 " Time(() -> { }) - Times the specified code\n" +
1030 " quit;\n" +
1031 " help;\n";
1035 /// <summary>
1036 /// Indicates to the read-eval-print-loop that the interaction should be finished.
1037 /// </summary>
1038 static public object quit {
1039 get {
1040 QuitRequested = true;
1041 return null;
1045 #if !NET_2_1
1046 /// <summary>
1047 /// Describes an object or a type.
1048 /// </summary>
1049 /// <remarks>
1050 /// This method will show a textual representation
1051 /// of the object's type. If the object is a
1052 /// System.Type it renders the type directly,
1053 /// otherwise it renders the type returned by
1054 /// invoking GetType on the object.
1055 /// </remarks>
1056 static public string Describe (object x)
1058 if (x == null)
1059 return "";
1061 Type t = x as Type;
1062 if (t == null)
1063 t = x.GetType ();
1065 StringWriter sw = new StringWriter ();
1066 new Outline (t, sw, true, false, false).OutlineType ();
1067 return sw.ToString ();
1069 #endif
1073 // A local variable reference that will create a Field in a
1074 // Class with the resolved type. This is necessary so we can
1075 // support "var" as a field type in a class declaration.
1077 // We allow LocalVariableReferece to do the heavy lifting, and
1078 // then we insert the field with the resolved type
1080 public class LocalVariableReferenceWithClassSideEffect : LocalVariableReference {
1081 TypeContainer container;
1082 string name;
1084 public LocalVariableReferenceWithClassSideEffect (TypeContainer container, string name, Block current_block, string local_variable_id, Location loc)
1085 : base (current_block, local_variable_id, loc)
1087 this.container = container;
1088 this.name = name;
1091 public override bool Equals (object obj)
1093 LocalVariableReferenceWithClassSideEffect lvr = obj as LocalVariableReferenceWithClassSideEffect;
1094 if (lvr == null)
1095 return false;
1097 if (lvr.name != name || lvr.container != container)
1098 return false;
1100 return base.Equals (obj);
1103 public override int GetHashCode ()
1105 return name.GetHashCode ();
1108 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
1110 Expression ret = base.DoResolveLValue (ec, right_side);
1111 if (ret == null)
1112 return null;
1114 Field f = new Field (container, new TypeExpression (ret.Type, Location),
1115 Modifiers.PUBLIC | Modifiers.STATIC,
1116 new MemberName (name, Location), null);
1117 container.AddField (f);
1118 if (f.Define ())
1119 Evaluator.QueueField (f);
1121 return ret;
1125 /// <summary>
1126 /// A class used to assign values if the source expression is not void
1128 /// Used by the interactive shell to allow it to call this code to set
1129 /// the return value for an invocation.
1130 /// </summary>
1131 class OptionalAssign : SimpleAssign {
1132 public OptionalAssign (Expression t, Expression s, Location loc)
1133 : base (t, s, loc)
1137 public override Expression DoResolve (EmitContext ec)
1139 CloneContext cc = new CloneContext ();
1140 Expression clone = source.Clone (cc);
1142 clone = clone.Resolve (ec);
1143 if (clone == null)
1144 return null;
1146 // This means its really a statement.
1147 if (clone.Type == TypeManager.void_type){
1148 source = source.Resolve (ec);
1149 target = null;
1150 type = TypeManager.void_type;
1151 eclass = ExprClass.Value;
1152 return this;
1155 return base.DoResolve (ec);
1158 public override void Emit (EmitContext ec)
1160 if (target == null)
1161 source.Emit (ec);
1162 else
1163 base.Emit (ec);
1166 public override void EmitStatement (EmitContext ec)
1168 if (target == null)
1169 source.Emit (ec);
1170 else
1171 base.EmitStatement (ec);
1175 public class Undo {
1176 ArrayList undo_types;
1178 public Undo ()
1180 undo_types = new ArrayList ();
1183 public void AddTypeContainer (TypeContainer current_container, TypeContainer tc)
1185 if (current_container == tc){
1186 Console.Error.WriteLine ("Internal error: inserting container into itself");
1187 return;
1190 if (undo_types == null)
1191 undo_types = new ArrayList ();
1192 undo_types.Add (new Pair (current_container, tc));
1195 public void ExecuteUndo ()
1197 if (undo_types == null)
1198 return;
1200 foreach (Pair p in undo_types){
1201 TypeContainer current_container = (TypeContainer) p.First;
1203 current_container.RemoveTypeContainer ((TypeContainer) p.Second);
1205 undo_types = null;