more docs
[mcs.git] / mcs / eval.cs
bloba17a7bc94b843d4e3d919f8b57e0a30bb4a7f2c6
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 static object evaluator_lock = new object ();
42 static string current_debug_name;
43 static int count;
44 static Thread invoke_thread;
46 static ArrayList using_alias_list = new ArrayList ();
47 static ArrayList using_list = new ArrayList ();
48 static Hashtable fields = new Hashtable ();
50 static Type interactive_base_class = typeof (InteractiveBase);
51 static Driver driver;
52 static bool inited;
54 /// <summary>
55 /// Optional initialization for the Evaluator.
56 /// </summary>
57 /// <remarks>
58 /// Initializes the Evaluator with the command line options
59 /// that would be processed by the command line compiler. Only
60 /// the first call to Init will work, any future invocations are
61 /// ignored.
62 ///
63 /// You can safely avoid calling this method if your application
64 /// does not need any of the features exposed by the command line
65 /// interface.
66 /// </remarks>
67 public static void Init (string [] args)
69 lock (evaluator_lock){
70 if (inited)
71 return;
73 RootContext.Version = LanguageVersion.Default;
74 driver = Driver.Create (args, false);
75 if (driver == null)
76 throw new Exception ("Failed to create compiler driver with the given arguments");
78 driver.ProcessDefaultConfig ();
79 CompilerCallableEntryPoint.Reset ();
80 Driver.LoadReferences ();
81 RootContext.EvalMode = true;
82 inited = true;
86 static void Init ()
88 Init (new string [0]);
91 static void Reset ()
93 CompilerCallableEntryPoint.PartialReset ();
96 // PartialReset should not reset the core types, this is very redundant.
98 if (!TypeManager.InitCoreTypes ())
99 throw new Exception ("Failed to InitCoreTypes");
100 TypeManager.InitOptionalCoreTypes ();
102 Location.AddFile ("{interactive}");
103 Location.Initialize ();
105 current_debug_name = "interactive" + (count++) + ".dll";
106 if (Environment.GetEnvironmentVariable ("SAVE") != null){
107 CodeGen.Init (current_debug_name, current_debug_name, false);
108 } else
109 CodeGen.InitDynamic (current_debug_name);
112 /// <summary>
113 /// The base class for the classes that host the user generated code
114 /// </summary>
115 /// <remarks>
117 /// This is the base class that will host the code
118 /// executed by the Evaluator. By default
119 /// this is the Mono.CSharp.InteractiveBase class
120 /// which is useful for interactive use.
122 /// By changing this property you can control the
123 /// base class and the static members that are
124 /// available to your evaluated code.
125 /// </remarks>
126 static public Type InteractiveBaseClass {
127 get {
128 return interactive_base_class;
131 set {
132 if (value == null)
133 throw new ArgumentNullException ();
135 lock (evaluator_lock)
136 interactive_base_class = value;
140 /// <summary>
141 /// Interrupts the evaluation of an expression executing in Evaluate.
142 /// </summary>
143 /// <remarks>
144 /// Use this method to interrupt long-running invocations.
145 /// </remarks>
146 public static void Interrupt ()
148 if (!inited || !invoking)
149 return;
151 if (invoke_thread != null)
152 invoke_thread.Abort ();
155 /// <summary>
156 /// Compiles the input string and returns a delegate that represents the compiled code.
157 /// </summary>
158 /// <remarks>
160 /// Compiles the input string as a C# expression or
161 /// statement, unlike the Evaluate method, the
162 /// resulting delegate can be invoked multiple times
163 /// without incurring in the compilation overhead.
165 /// If the return value of this function is null,
166 /// this indicates that the parsing was complete.
167 /// If the return value is a string it indicates
168 /// that the input string was partial and that the
169 /// invoking code should provide more code before
170 /// the code can be successfully compiled.
172 /// If you know that you will always get full expressions or
173 /// statements and do not care about partial input, you can use
174 /// the other Compile overload.
176 /// On success, in addition to returning null, the
177 /// compiled parameter will be set to the delegate
178 /// that can be invoked to execute the code.
180 /// </remarks>
181 static public string Compile (string input, out CompiledMethod compiled)
183 if (input == null || input.Length == 0){
184 compiled = null;
185 return null;
188 lock (evaluator_lock){
189 if (!inited)
190 Init ();
192 bool partial_input;
193 CSharpParser parser = ParseString (true, input, out partial_input);
194 if (parser == null){
195 compiled = null;
196 if (partial_input)
197 return input;
199 ParseString (false, input, out partial_input);
200 return null;
203 object parser_result = parser.InteractiveResult;
205 if (!(parser_result is Class)){
206 int errors = Report.Errors;
208 NamespaceEntry.VerifyAllUsing ();
209 if (errors == Report.Errors)
210 parser.CurrentNamespace.Extract (using_alias_list, using_list);
213 compiled = CompileBlock (parser_result as Class, parser.undo);
216 return null;
219 /// <summary>
220 /// Compiles the input string and returns a delegate that represents the compiled code.
221 /// </summary>
222 /// <remarks>
224 /// Compiles the input string as a C# expression or
225 /// statement, unlike the Evaluate method, the
226 /// resulting delegate can be invoked multiple times
227 /// without incurring in the compilation overhead.
229 /// This method can only deal with fully formed input
230 /// strings and does not provide a completion mechanism.
231 /// If you must deal with partial input (for example for
232 /// interactive use) use the other overload.
234 /// On success, a delegate is returned that can be used
235 /// to invoke the method.
237 /// </remarks>
238 static public CompiledMethod Compile (string input)
240 CompiledMethod compiled;
242 // Ignore partial inputs
243 if (Compile (input, out compiled) != null){
244 // Error, the input was partial.
245 return null;
248 // Either null (on error) or the compiled method.
249 return compiled;
253 // Todo: Should we handle errors, or expect the calling code to setup
254 // the recording themselves?
257 /// <summary>
258 /// Evaluates and expression or statement and returns any result values.
259 /// </summary>
260 /// <remarks>
261 /// Evaluates the input string as a C# expression or
262 /// statement. If the input string is an expression
263 /// the result will be stored in the result variable
264 /// and the result_set variable will be set to true.
266 /// It is necessary to use the result/result_set
267 /// pair to identify when a result was set (for
268 /// example, execution of user-provided input can be
269 /// an expression, a statement or others, and
270 /// result_set would only be set if the input was an
271 /// expression.
273 /// If the return value of this function is null,
274 /// this indicates that the parsing was complete.
275 /// If the return value is a string, it indicates
276 /// that the input is partial and that the user
277 /// should provide an updated string.
278 /// </remarks>
279 public static string Evaluate (string input, out object result, out bool result_set)
281 CompiledMethod compiled;
283 result_set = false;
284 result = null;
286 input = Compile (input, out compiled);
287 if (input != null)
288 return input;
290 if (compiled == null)
291 return null;
294 // The code execution does not need to keep the compiler lock
296 object retval = typeof (NoValueSet);
298 try {
299 invoke_thread = System.Threading.Thread.CurrentThread;
300 invoking = true;
301 compiled (ref retval);
302 } catch (ThreadAbortException e){
303 Thread.ResetAbort ();
304 Console.WriteLine ("Interrupted!\n{0}", e);
305 } finally {
306 invoking = false;
310 // We use a reference to a compiler type, in this case
311 // Driver as a flag to indicate that this was a statement
313 if (retval != typeof (NoValueSet)){
314 result_set = true;
315 result = retval;
318 return null;
321 /// <summary>
322 /// Executes the given expression or statement.
323 /// </summary>
324 /// <remarks>
325 /// Executes the provided statement, returns true
326 /// on success, false on parsing errors. Exceptions
327 /// might be thrown by the called code.
328 /// </remarks>
329 public static bool Run (string statement)
331 if (!inited)
332 Init ();
334 object result;
335 bool result_set;
337 bool ok = Evaluate (statement, out result, out result_set) == null;
339 return ok;
342 /// <summary>
343 /// Evaluates and expression or statement and returns the result.
344 /// </summary>
345 /// <remarks>
346 /// Evaluates the input string as a C# expression or
347 /// statement and returns the value.
349 /// This method will throw an exception if there is a syntax error,
350 /// of if the provided input is not an expression but a statement.
351 /// </remarks>
352 public static object Evaluate (string input)
354 object result;
355 bool result_set;
357 string r = Evaluate (input, out result, out result_set);
359 if (r != null)
360 throw new ArgumentException ("Syntax error on input: partial input");
362 if (result_set == false)
363 throw new ArgumentException ("The expression did not set a result");
365 return result;
368 enum InputKind {
369 EOF,
370 StatementOrExpression,
371 CompilationUnit,
372 Error
376 // Deambiguates the input string to determine if we
377 // want to process a statement or if we want to
378 // process a compilation unit.
380 // This is done using a top-down predictive parser,
381 // since the yacc/jay parser can not deambiguage this
382 // without more than one lookahead token. There are very
383 // few ambiguities.
385 static InputKind ToplevelOrStatement (SeekableStreamReader seekable)
387 Tokenizer tokenizer = new Tokenizer (seekable, Location.SourceFiles [0]);
389 int t = tokenizer.token ();
390 switch (t){
391 case Token.EOF:
392 return InputKind.EOF;
394 // These are toplevels
395 case Token.EXTERN:
396 case Token.OPEN_BRACKET:
397 case Token.ABSTRACT:
398 case Token.CLASS:
399 case Token.ENUM:
400 case Token.INTERFACE:
401 case Token.INTERNAL:
402 case Token.NAMESPACE:
403 case Token.PRIVATE:
404 case Token.PROTECTED:
405 case Token.PUBLIC:
406 case Token.SEALED:
407 case Token.STATIC:
408 case Token.STRUCT:
409 return InputKind.CompilationUnit;
411 // Definitely expression
412 case Token.FIXED:
413 case Token.BOOL:
414 case Token.BYTE:
415 case Token.CHAR:
416 case Token.DECIMAL:
417 case Token.DOUBLE:
418 case Token.FLOAT:
419 case Token.INT:
420 case Token.LONG:
421 case Token.NEW:
422 case Token.OBJECT:
423 case Token.SBYTE:
424 case Token.SHORT:
425 case Token.STRING:
426 case Token.UINT:
427 case Token.ULONG:
428 return InputKind.StatementOrExpression;
430 // These need deambiguation help
431 case Token.USING:
432 t = tokenizer.token ();
433 if (t == Token.EOF)
434 return InputKind.EOF;
436 if (t == Token.IDENTIFIER)
437 return InputKind.CompilationUnit;
438 return InputKind.StatementOrExpression;
441 // Distinguish between:
442 // delegate opt_anonymous_method_signature block
443 // delegate type
444 case Token.DELEGATE:
445 t = tokenizer.token ();
446 if (t == Token.EOF)
447 return InputKind.EOF;
448 if (t == Token.OPEN_PARENS || t == Token.OPEN_BRACE)
449 return InputKind.StatementOrExpression;
450 return InputKind.CompilationUnit;
452 // Distinguih between:
453 // unsafe block
454 // unsafe as modifier of a type declaration
455 case Token.UNSAFE:
456 t = tokenizer.token ();
457 if (t == Token.EOF)
458 return InputKind.EOF;
459 if (t == Token.OPEN_PARENS)
460 return InputKind.StatementOrExpression;
461 return InputKind.CompilationUnit;
463 // These are errors: we list explicitly what we had
464 // from the grammar, ERROR and then everything else
466 case Token.READONLY:
467 case Token.OVERRIDE:
468 case Token.ERROR:
469 return InputKind.Error;
471 // This catches everything else allowed by
472 // expressions. We could add one-by-one use cases
473 // if needed.
474 default:
475 return InputKind.StatementOrExpression;
480 // Parses the string @input and returns a CSharpParser if succeeful.
482 // if @silent is set to true then no errors are
483 // reported to the user. This is used to do various calls to the
484 // parser and check if the expression is parsable.
486 // @partial_input: if @silent is true, then it returns whether the
487 // parsed expression was partial, and more data is needed
489 static CSharpParser ParseString (bool silent, string input, out bool partial_input)
491 partial_input = false;
492 Reset ();
493 queued_fields.Clear ();
495 Stream s = new MemoryStream (Encoding.Default.GetBytes (input));
496 SeekableStreamReader seekable = new SeekableStreamReader (s, Encoding.Default);
498 InputKind kind = ToplevelOrStatement (seekable);
499 if (kind == InputKind.Error){
500 if (!silent)
501 Report.Error (-25, "Detection Parsing Error");
502 partial_input = false;
503 return null;
506 if (kind == InputKind.EOF){
507 if (silent == false)
508 Console.Error.WriteLine ("Internal error: EOF condition should have been detected in a previous call with silent=true");
509 partial_input = true;
510 return null;
513 seekable.Position = 0;
515 CSharpParser parser = new CSharpParser (seekable, Location.SourceFiles [0]);
516 parser.ErrorOutput = Report.Stderr;
518 if (kind == InputKind.StatementOrExpression){
519 parser.Lexer.putback_char = Tokenizer.EvalStatementParserCharacter;
520 RootContext.StatementMode = true;
521 } else {
523 // Do not activate EvalCompilationUnitParserCharacter until
524 // I have figured out all the limitations to invoke methods
525 // in the generated classes. See repl.txt
527 parser.Lexer.putback_char = Tokenizer.EvalUsingDeclarationsParserCharacter;
528 //parser.Lexer.putback_char = Tokenizer.EvalCompilationUnitParserCharacter;
529 RootContext.StatementMode = false;
532 if (silent)
533 Report.DisableReporting ();
534 try {
535 parser.parse ();
536 } finally {
537 if (Report.Errors != 0){
538 if (silent && parser.UnexpectedEOF)
539 partial_input = true;
541 parser.undo.ExecuteUndo ();
542 parser = null;
545 if (silent)
546 Report.EnableReporting ();
548 return parser;
552 // Queue all the fields that we use, as we need to then go from FieldBuilder to FieldInfo
553 // or reflection gets confused (it basically gets confused, and variables override each
554 // other).
556 static ArrayList queued_fields = new ArrayList ();
558 //static ArrayList types = new ArrayList ();
560 static volatile bool invoking;
562 static CompiledMethod CompileBlock (Class host, Undo undo)
564 RootContext.ResolveTree ();
565 if (Report.Errors != 0){
566 undo.ExecuteUndo ();
567 return null;
570 RootContext.PopulateTypes ();
572 if (Report.Errors != 0){
573 undo.ExecuteUndo ();
574 return null;
577 TypeBuilder tb = null;
578 MethodBuilder mb = null;
580 if (host != null){
581 tb = host.TypeBuilder;
582 mb = null;
583 foreach (MemberCore member in host.Methods){
584 if (member.Name != "Host")
585 continue;
587 MethodOrOperator method = (MethodOrOperator) member;
588 mb = method.MethodBuilder;
589 break;
592 if (mb == null)
593 throw new Exception ("Internal error: did not find the method builder for the generated method");
596 RootContext.EmitCode ();
597 if (Report.Errors != 0)
598 return null;
600 RootContext.CloseTypes ();
602 if (Environment.GetEnvironmentVariable ("SAVE") != null)
603 CodeGen.Save (current_debug_name, false);
605 if (host == null)
606 return null;
609 // Unlike Mono, .NET requires that the MethodInfo is fetched, it cant
610 // work from MethodBuilders. Retarded, I know.
612 Type tt = CodeGen.Assembly.Builder.GetType (tb.Name);
613 MethodInfo mi = tt.GetMethod (mb.Name);
615 // Pull the FieldInfos from the type, and keep track of them
616 foreach (Field field in queued_fields){
617 FieldInfo fi = tt.GetField (field.Name);
619 FieldInfo old = (FieldInfo) fields [field.Name];
621 // If a previous value was set, nullify it, so that we do
622 // not leak memory
623 if (old != null){
624 if (old.FieldType.IsValueType){
626 // TODO: Clear fields for structs
628 } else {
629 try {
630 old.SetValue (null, null);
631 } catch {
636 fields [field.Name] = fi;
638 //types.Add (tb);
640 queued_fields.Clear ();
642 return (CompiledMethod) System.Delegate.CreateDelegate (typeof (CompiledMethod), mi);
645 static internal void LoadAliases (NamespaceEntry ns)
647 ns.Populate (using_alias_list, using_list);
650 /// <summary>
651 /// A sentinel value used to indicate that no value was
652 /// was set by the compiled function. This is used to
653 /// differentiate between a function not returning a
654 /// value and null.
655 /// </summary>
656 public class NoValueSet {
659 static internal FieldInfo LookupField (string name)
661 FieldInfo fi = (FieldInfo) fields [name];
663 return fi;
667 // Puts the FieldBuilder into a queue of names that will be
668 // registered. We can not register FieldBuilders directly
669 // we need to fetch the FieldInfo after Reflection cooks the
670 // types, or bad things happen (bad means: FieldBuilders behave
671 // incorrectly across multiple assemblies, causing assignments to
672 // invalid areas
674 // This also serves for the parser to register Field classes
675 // that should be exposed as global variables
677 static internal void QueueField (Field f)
679 queued_fields.Add (f);
682 static string Quote (string s)
684 if (s.IndexOf ('"') != -1)
685 s = s.Replace ("\"", "\\\"");
687 return "\"" + s + "\"";
690 static public string GetUsing ()
692 lock (evaluator_lock){
693 StringBuilder sb = new StringBuilder ();
695 foreach (object x in using_alias_list)
696 sb.Append (String.Format ("using {0};\n", x));
698 foreach (object x in using_list)
699 sb.Append (String.Format ("using {0};\n", x));
701 return sb.ToString ();
705 static public string GetVars ()
707 lock (evaluator_lock){
708 StringBuilder sb = new StringBuilder ();
710 foreach (DictionaryEntry de in fields){
711 FieldInfo fi = LookupField ((string) de.Key);
712 object value = null;
713 bool error = false;
715 try {
716 if (value == null)
717 value = "null";
718 value = fi.GetValue (null);
719 if (value is string)
720 value = Quote ((string)value);
721 } catch {
722 error = true;
725 if (error)
726 sb.Append (String.Format ("{0} {1} <error reading value>", TypeManager.CSharpName(fi.FieldType), de.Key));
727 else
728 sb.Append (String.Format ("{0} {1} = {2}", TypeManager.CSharpName(fi.FieldType), de.Key, value));
731 return sb.ToString ();
735 /// <summary>
736 /// Loads the given assembly and exposes the API to the user.
737 /// </summary>
738 static public void LoadAssembly (string file)
740 lock (evaluator_lock){
741 Driver.LoadAssembly (file, false);
742 RootNamespace.ComputeNamespaces ();
746 /// <summary>
747 /// Exposes the API of the given assembly to the Evaluator
748 /// </summary>
749 static public void ReferenceAssembly (Assembly a)
751 lock (evaluator_lock){
752 RootNamespace.Global.AddAssemblyReference (a);
753 RootNamespace.ComputeNamespaces ();
760 /// <summary>
761 /// A delegate that can be used to invoke the
762 /// compiled expression or statement.
763 /// </summary>
764 /// <remarks>
765 /// Since the Compile methods will compile
766 /// statements and expressions into the same
767 /// delegate, you can tell if a value was returned
768 /// by checking whether the returned value is of type
769 /// NoValueSet.
770 /// </remarks>
772 public delegate void CompiledMethod (ref object retvalue);
774 /// <summary>
775 /// The default base class for every interaction line
776 /// </summary>
777 /// <remarks>
778 /// The expressions and statements behave as if they were
779 /// a static method of this class. The InteractiveBase class
780 /// contains a number of useful methods, but can be overwritten
781 /// by setting the InteractiveBaseType property in the Evaluator
782 /// </remarks>
783 public class InteractiveBase {
784 /// <summary>
785 /// Determines where the standard output of methods in this class will go.
786 /// </summary>
787 public static TextWriter Output = Console.Out;
789 /// <summary>
790 /// Determines where the standard error of methods in this class will go.
791 /// </summary>
792 public static TextWriter Error = Console.Error;
794 /// <summary>
795 /// The primary prompt used for interactive use.
796 /// </summary>
797 public static string Prompt = "csharp> ";
799 /// <summary>
800 /// The secondary prompt used for interactive use (used when
801 /// an expression is incomplete).
802 /// </summary>
803 public static string ContinuationPrompt = " > ";
805 /// <summary>
806 /// Used to signal that the user has invoked the `quit' statement.
807 /// </summary>
808 public static bool QuitRequested;
810 /// <summary>
811 /// Shows all the variables defined so far.
812 /// </summary>
813 static public void ShowVars ()
815 Output.Write (Evaluator.GetVars ());
816 Output.Flush ();
819 /// <summary>
820 /// Displays the using statements in effect at this point.
821 /// </summary>
822 static public void ShowUsing ()
824 Output.Write (Evaluator.GetUsing ());
825 Output.Flush ();
828 public delegate void Simple ();
830 /// <summary>
831 /// Times the execution of the given delegate
832 /// </summary>
833 static public TimeSpan Time (Simple a)
835 DateTime start = DateTime.Now;
836 a ();
837 return DateTime.Now - start;
840 #if !SMCS_SOURCE
841 /// <summary>
842 /// Loads the assemblies from a package
843 /// </summary>
844 /// <remarks>
845 /// Loads the assemblies from a package. This is equivalent
846 /// to passing the -pkg: command line flag to the C# compiler
847 /// on the command line.
848 /// </remarks>
849 static public void LoadPackage (string pkg)
851 if (pkg == null){
852 Error.WriteLine ("Invalid package specified");
853 return;
856 string pkgout = Driver.GetPackageFlags (pkg, false);
857 if (pkgout == null)
858 return;
860 string [] xargs = pkgout.Trim (new Char [] {' ', '\n', '\r', '\t'}).
861 Split (new Char [] { ' ', '\t'});
863 foreach (string s in xargs){
864 if (s.StartsWith ("-r:") || s.StartsWith ("/r:") || s.StartsWith ("/reference:")){
865 string lib = s.Substring (s.IndexOf (':')+1);
867 Evaluator.LoadAssembly (lib);
868 continue;
872 #endif
874 /// <summary>
875 /// Loads the assembly
876 /// </summary>
877 /// <remarks>
878 /// Loads the specified assembly and makes its types
879 /// available to the evaluator. This is equivalent
880 /// to passing the -pkg: command line flag to the C#
881 /// compiler on the command line.
882 /// </remarks>
883 static public void LoadAssembly (string assembly)
885 Evaluator.LoadAssembly (assembly);
888 /// <summary>
889 /// Returns a list of available static methods.
890 /// </summary>
891 static public string help {
892 get {
893 return "Static methods:\n"+
894 " Describe(obj) - Describes the object's type\n" +
895 " LoadPackage (pkg); - Loads the given Package (like -pkg:FILE)\n" +
896 " LoadAssembly (ass) - Loads the given assembly (like -r:ASS)\n" +
897 " ShowVars (); - Shows defined local variables.\n" +
898 " ShowUsing (); - Show active using decltions.\n" +
899 " Prompt - The prompt used by the C# shell\n" +
900 " ContinuationPrompt - The prompt for partial input\n" +
901 " Time(() -> { }) - Times the specified code\n" +
902 " quit;\n" +
903 " help;\n";
907 /// <summary>
908 /// Indicates to the read-eval-print-loop that the interaction should be finished.
909 /// </summary>
910 static public object quit {
911 get {
912 QuitRequested = true;
913 return null;
917 #if !NET_2_1
918 /// <summary>
919 /// Describes an object or a type.
920 /// </summary>
921 /// <remarks>
922 /// This method will show a textual representation
923 /// of the object's type. If the object is a
924 /// System.Type it renders the type directly,
925 /// otherwise it renders the type returned by
926 /// invoking GetType on the object.
927 /// </remarks>
928 static public string Describe (object x)
930 if (x == null)
931 return "";
933 Type t = x as Type;
934 if (t == null)
935 t = x.GetType ();
937 StringWriter sw = new StringWriter ();
938 new Outline (t, sw, true, false, false).OutlineType ();
939 return sw.ToString ();
941 #endif
945 // A local variable reference that will create a Field in a
946 // Class with the resolved type. This is necessary so we can
947 // support "var" as a field type in a class declaration.
949 // We allow LocalVariableReferece to do the heavy lifting, and
950 // then we insert the field with the resolved type
952 public class LocalVariableReferenceWithClassSideEffect : LocalVariableReference {
953 TypeContainer container;
954 string name;
956 public LocalVariableReferenceWithClassSideEffect (TypeContainer container, string name, Block current_block, string local_variable_id, Location loc)
957 : base (current_block, local_variable_id, loc)
959 this.container = container;
960 this.name = name;
963 public override bool Equals (object obj)
965 LocalVariableReferenceWithClassSideEffect lvr = obj as LocalVariableReferenceWithClassSideEffect;
966 if (lvr == null)
967 return false;
969 if (lvr.name != name || lvr.container != container)
970 return false;
972 return base.Equals (obj);
975 public override int GetHashCode ()
977 return name.GetHashCode ();
980 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
982 Expression ret = base.DoResolveLValue (ec, right_side);
983 if (ret == null)
984 return null;
986 Field f = new Field (container, new TypeExpression (ret.Type, Location),
987 Modifiers.PUBLIC | Modifiers.STATIC,
988 new MemberName (name, Location), null);
989 container.AddField (f);
990 if (f.Define ())
991 Evaluator.QueueField (f);
993 return ret;
997 /// <summary>
998 /// A class used to assign values if the source expression is not void
1000 /// Used by the interactive shell to allow it to call this code to set
1001 /// the return value for an invocation.
1002 /// </summary>
1003 class OptionalAssign : SimpleAssign {
1004 public OptionalAssign (Expression t, Expression s, Location loc)
1005 : base (t, s, loc)
1009 public override Expression DoResolve (EmitContext ec)
1011 CloneContext cc = new CloneContext ();
1012 Expression clone = source.Clone (cc);
1014 clone = clone.Resolve (ec);
1015 if (clone == null)
1016 return null;
1018 // This means its really a statement.
1019 if (clone.Type == TypeManager.void_type){
1020 source = source.Resolve (ec);
1021 target = null;
1022 type = TypeManager.void_type;
1023 eclass = ExprClass.Value;
1024 return this;
1027 return base.DoResolve (ec);
1030 public override void Emit (EmitContext ec)
1032 if (target == null)
1033 source.Emit (ec);
1034 else
1035 base.Emit (ec);
1038 public override void EmitStatement (EmitContext ec)
1040 if (target == null)
1041 source.Emit (ec);
1042 else
1043 base.EmitStatement (ec);
1047 public class Undo {
1048 ArrayList undo_types;
1050 public Undo ()
1052 undo_types = new ArrayList ();
1055 public void AddTypeContainer (TypeContainer current_container, TypeContainer tc)
1057 if (current_container == tc){
1058 Console.Error.WriteLine ("Internal error: inserting container into itself");
1059 return;
1062 if (undo_types == null)
1063 undo_types = new ArrayList ();
1064 undo_types.Add (new Pair (current_container, tc));
1067 public void ExecuteUndo ()
1069 if (undo_types == null)
1070 return;
1072 foreach (Pair p in undo_types){
1073 TypeContainer current_container = (TypeContainer) p.First;
1075 current_container.RemoveTypeContainer ((TypeContainer) p.Second);
1077 undo_types = null;