2 // eval.cs: Evaluation and Hosting API for the C# compiler
5 // Miguel de Icaza (miguel@gnome.org)
7 // Dual licensed under the terms of the MIT X11 or GNU GPL
9 // Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
10 // Copyright 2004, 2005, 2006, 2007, 2008 Novell, Inc
13 using System
.Threading
;
14 using System
.Collections
;
15 using System
.Reflection
;
16 using System
.Reflection
.Emit
;
18 using System
.Globalization
;
21 namespace Mono
.CSharp
{
24 /// Evaluator: provides an API to evaluate C# statements and
25 /// expressions dynamically.
28 /// This class exposes static methods to evaluate expressions in the
31 /// To initialize the evaluator with a number of compiler
32 /// options call the Init(string[]args) method with a set of
33 /// command line options that the compiler recognizes.
35 /// To interrupt execution of a statement, you can invoke the
36 /// Evaluator.Interrupt method.
38 public class Evaluator
{
41 // Parse silently, do not output any error messages
44 // Report errors during parse
47 // Auto-complete, means that the tokenizer will start producing
48 // GETCOMPLETIONS tokens when it reaches a certain point.
52 static object evaluator_lock
= new object ();
54 static string current_debug_name
;
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
);
67 /// Optional initialization for the Evaluator.
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
75 /// You can safely avoid calling this method if your application
76 /// does not need any of the features exposed by the command line
79 public static void Init (string [] args
)
81 InitAndGetStartupFiles (args
);
86 /// Optional initialization for the Evaluator.
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.
95 /// You can safely avoid calling this method if your application
96 /// does not need any of the features exposed by the command line
99 /// This method return an array of strings that contains any
100 /// files that were specified in `args'.
102 public static string [] InitAndGetStartupFiles (string [] args
)
104 lock (evaluator_lock
){
106 return new string [0];
108 RootContext
.Version
= LanguageVersion
.Default
;
109 driver
= Driver
.Create (args
, false);
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;
124 return (string []) startup_files
.ToArray (typeof (string));
130 Init (new string [0]);
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);
151 CodeGen
.InitDynamic (current_debug_name
);
155 /// The base class for the classes that host the user generated code
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.
168 static public Type InteractiveBaseClass
{
170 return interactive_base_class
;
175 throw new ArgumentNullException ();
177 lock (evaluator_lock
)
178 interactive_base_class
= value;
183 /// Interrupts the evaluation of an expression executing in Evaluate.
186 /// Use this method to interrupt long-running invocations.
188 public static void Interrupt ()
190 if (!inited
|| !invoking
)
193 if (invoke_thread
!= null)
194 invoke_thread
.Abort ();
198 /// Compiles the input string and returns a delegate that represents the compiled code.
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.
223 static public string Compile (string input
, out CompiledMethod compiled
)
225 if (input
== null || input
.Length
== 0){
230 lock (evaluator_lock
){
235 CSharpParser parser
= ParseString (ParseMode
.Silent
, input
, out partial_input
);
241 ParseString (ParseMode
.ReportErrors
, input
, out partial_input
);
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
);
262 /// Compiles the input string and returns a delegate that represents the compiled code.
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.
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.
290 // Either null (on error) or the compiled method.
295 // Todo: Should we handle errors, or expect the calling code to setup
296 // the recording themselves?
300 /// Evaluates and expression or statement and returns any result values.
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
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.
321 public static string Evaluate (string input
, out object result
, out bool result_set
)
323 CompiledMethod compiled
;
328 input
= Compile (input
, out compiled
);
332 if (compiled
== null)
336 // The code execution does not need to keep the compiler lock
338 object retval
= typeof (NoValueSet
);
341 invoke_thread
= System
.Threading
.Thread
.CurrentThread
;
343 compiled (ref retval
);
344 } catch (ThreadAbortException e
){
345 Thread
.ResetAbort ();
346 Console
.WriteLine ("Interrupted!\n{0}", e
);
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
)){
363 public static string [] GetCompletions (string input
, out string prefix
)
366 if (input
== null || input
.Length
== 0)
369 lock (evaluator_lock
){
374 CSharpParser parser
= ParseString (ParseMode
.GetCompletions
, input
, out partial_input
);
376 if (CSharpParser
.yacc_verbose_flag
!= 0)
377 Console
.WriteLine ("DEBUG: No completions available");
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");
390 RootContext
.ResolveTree ();
391 if (Report
.Errors
!= 0)
394 RootContext
.PopulateTypes ();
395 if (Report
.Errors
!= 0)
398 MethodOrOperator method
= null;
399 foreach (MemberCore member
in parser_result
.Methods
){
400 if (member
.Name
!= "Host")
403 method
= (MethodOrOperator
) member
;
407 throw new InternalErrorException ("did not find the the Host method");
409 EmitContext ec
= method
.CreateEmitContext (method
.Parent
, null);
413 ec
.ResolveTopBlock (null, method
.Block
, method
.ParameterInfo
, method
, out unreach
);
414 } catch (CompletionResult cr
){
415 prefix
= cr
.BaseText
;
419 parser
.undo
.ExecuteUndo ();
427 /// Executes the given expression or statement.
430 /// Executes the provided statement, returns true
431 /// on success, false on parsing errors. Exceptions
432 /// might be thrown by the called code.
434 public static bool Run (string statement
)
442 bool ok
= Evaluate (statement
, out result
, out result_set
) == null;
448 /// Evaluates and expression or statement and returns the result.
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.
457 public static object Evaluate (string input
)
462 string r
= Evaluate (input
, out result
, out result_set
);
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");
475 StatementOrExpression
,
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
490 static InputKind
ToplevelOrStatement (SeekableStreamReader seekable
)
492 Tokenizer tokenizer
= new Tokenizer (seekable
, (CompilationUnit
) Location
.SourceFiles
[0]);
494 int t
= tokenizer
.token ();
497 return InputKind
.EOF
;
499 // These are toplevels
501 case Token
.OPEN_BRACKET
:
505 case Token
.INTERFACE
:
507 case Token
.NAMESPACE
:
509 case Token
.PROTECTED
:
514 return InputKind
.CompilationUnit
;
516 // Definitely expression
533 return InputKind
.StatementOrExpression
;
535 // These need deambiguation help
537 t
= tokenizer
.token ();
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
550 t
= tokenizer
.token ();
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:
559 // unsafe as modifier of a type declaration
561 t
= tokenizer
.token ();
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
574 return InputKind
.Error
;
576 // This catches everything else allowed by
577 // expressions. We could add one-by-one use cases
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;
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;
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;
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;
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;
644 disable_error_reporting
= false;
646 if (disable_error_reporting
)
647 Report
.DisableReporting ();
651 if (Report
.Errors
!= 0){
652 if (mode
!= ParseMode
.ReportErrors
&& parser
.UnexpectedEOF
)
653 partial_input
= true;
655 parser
.undo
.ExecuteUndo ();
659 if (disable_error_reporting
)
660 Report
.EnableReporting ();
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
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){
684 RootContext
.PopulateTypes ();
686 if (Report
.Errors
!= 0){
691 TypeBuilder tb
= null;
692 MethodBuilder mb
= null;
695 tb
= host
.TypeBuilder
;
697 foreach (MemberCore member
in host
.Methods
){
698 if (member
.Name
!= "Host")
701 MethodOrOperator method
= (MethodOrOperator
) member
;
702 mb
= method
.MethodBuilder
;
707 throw new Exception ("Internal error: did not find the method builder for the generated method");
710 RootContext
.EmitCode ();
711 if (Report
.Errors
!= 0)
714 RootContext
.CloseTypes ();
716 if (Environment
.GetEnvironmentVariable ("SAVE") != null)
717 CodeGen
.Save (current_debug_name
, false);
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
738 if (TypeManager
.IsStruct (old
.FieldType
)){
740 // TODO: Clear fields for structs
744 old
.SetValue (null, null);
750 fields
[field
.Name
] = fi
;
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
);
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
770 public class NoValueSet
{
773 static internal FieldInfo
LookupField (string name
)
775 FieldInfo fi
= (FieldInfo
) fields
[name
];
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
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 ());
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
);
847 value = fi
.GetValue (null);
849 value = Quote ((string)value);
855 sb
.Append (String
.Format ("{0} {1} <error reading value>", TypeManager
.CSharpName(fi
.FieldType
), de
.Key
));
857 sb
.Append (String
.Format ("{0} {1} = {2}", TypeManager
.CSharpName(fi
.FieldType
), de
.Key
, value));
860 return sb
.ToString ();
865 /// Loads the given assembly and exposes the API to the user.
867 static public void LoadAssembly (string file
)
869 lock (evaluator_lock
){
870 Driver
.LoadAssembly (file
, false);
871 GlobalRootNamespace
.Instance
.ComputeNamespaces ();
876 /// Exposes the API of the given assembly to the Evaluator
878 static public void ReferenceAssembly (Assembly a
)
880 lock (evaluator_lock
){
881 GlobalRootNamespace
.Instance
.AddAssemblyReference (a
);
882 GlobalRootNamespace
.Instance
.ComputeNamespaces ();
890 /// A delegate that can be used to invoke the
891 /// compiled expression or statement.
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
901 public delegate void CompiledMethod (ref object retvalue
);
904 /// The default base class for every interaction line
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
912 public class InteractiveBase
{
914 /// Determines where the standard output of methods in this class will go.
916 public static TextWriter Output
= Console
.Out
;
919 /// Determines where the standard error of methods in this class will go.
921 public static TextWriter Error
= Console
.Error
;
924 /// The primary prompt used for interactive use.
926 public static string Prompt
= "csharp> ";
929 /// The secondary prompt used for interactive use (used when
930 /// an expression is incomplete).
932 public static string ContinuationPrompt
= " > ";
935 /// Used to signal that the user has invoked the `quit' statement.
937 public static bool QuitRequested
;
940 /// Shows all the variables defined so far.
942 static public void ShowVars ()
944 Output
.Write (Evaluator
.GetVars ());
949 /// Displays the using statements in effect at this point.
951 static public void ShowUsing ()
953 Output
.Write (Evaluator
.GetUsing ());
957 public delegate void Simple ();
960 /// Times the execution of the given delegate
962 static public TimeSpan
Time (Simple a
)
964 DateTime start
= DateTime
.Now
;
966 return DateTime
.Now
- start
;
971 /// Loads the assemblies from a package
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.
978 static public void LoadPackage (string pkg
)
981 Error
.WriteLine ("Invalid package specified");
985 string pkgout
= Driver
.GetPackageFlags (pkg
, false);
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
);
1004 /// Loads the assembly
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.
1012 static public void LoadAssembly (string assembly
)
1014 Evaluator
.LoadAssembly (assembly
);
1018 /// Returns a list of available static methods.
1020 static public string help
{
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" +
1037 /// Indicates to the read-eval-print-loop that the interaction should be finished.
1039 static public object quit
{
1041 QuitRequested
= true;
1048 /// Describes an object or a type.
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.
1057 static public string Describe (object x
)
1066 StringWriter sw
= new StringWriter ();
1067 new Outline (t
, sw
, true, false, false).OutlineType ();
1068 return sw
.ToString ();
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
;
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
;
1092 public override bool Equals (object obj
)
1094 LocalVariableReferenceWithClassSideEffect lvr
= obj
as LocalVariableReferenceWithClassSideEffect
;
1098 if (lvr
.name
!= name
|| lvr
.container
!= container
)
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
);
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
);
1120 Evaluator
.QueueField (f
);
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.
1132 class OptionalAssign
: SimpleAssign
{
1133 public OptionalAssign (Expression t
, Expression s
, Location loc
)
1138 public override Expression
DoResolve (EmitContext ec
)
1140 CloneContext cc
= new CloneContext ();
1141 Expression clone
= source
.Clone (cc
);
1143 clone
= clone
.Resolve (ec
);
1147 // This means its really a statement.
1148 if (clone
.Type
== TypeManager
.void_type
){
1149 source
= source
.Resolve (ec
);
1151 type
= TypeManager
.void_type
;
1152 eclass
= ExprClass
.Value
;
1156 return base.DoResolve (ec
);
1159 public override void Emit (EmitContext ec
)
1167 public override void EmitStatement (EmitContext ec
)
1172 base.EmitStatement (ec
);
1177 ArrayList undo_types
;
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");
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)
1201 foreach (Pair p
in undo_types
){
1202 TypeContainer current_container
= (TypeContainer
) p
.First
;
1204 current_container
.RemoveTypeContainer ((TypeContainer
) p
.Second
);