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
.Generic
;
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 List
<NamespaceEntry
.UsingAliasEntry
> using_alias_list
= new List
<NamespaceEntry
.UsingAliasEntry
> ();
59 internal static List
<NamespaceEntry
.UsingEntry
> using_list
= new List
<NamespaceEntry
.UsingEntry
> ();
60 static Dictionary
<string, Tuple
<FieldSpec
, FieldInfo
>> fields
= new Dictionary
<string, Tuple
<FieldSpec
, FieldInfo
>> ();
62 static TypeSpec interactive_base_class
;
66 static CompilerContext ctx
;
68 public static TextWriter MessageOutput
= Console
.Out
;
71 /// Optional initialization for the Evaluator.
74 /// Initializes the Evaluator with the command line options
75 /// that would be processed by the command line compiler. Only
76 /// the first call to Init will work, any future invocations are
79 /// You can safely avoid calling this method if your application
80 /// does not need any of the features exposed by the command line
83 public static void Init (string [] args
)
85 InitAndGetStartupFiles (args
);
88 internal static ReportPrinter
SetPrinter (ReportPrinter report_printer
)
90 return ctx
.Report
.SetPrinter (report_printer
);
94 /// Optional initialization for the Evaluator.
97 /// Initializes the Evaluator with the command line
98 /// options that would be processed by the command
99 /// line compiler. Only the first call to
100 /// InitAndGetStartupFiles or Init will work, any future
101 /// invocations are ignored.
103 /// You can safely avoid calling this method if your application
104 /// does not need any of the features exposed by the command line
107 /// This method return an array of strings that contains any
108 /// files that were specified in `args'.
110 public static string [] InitAndGetStartupFiles (string [] args
)
112 lock (evaluator_lock
){
114 return new string [0];
116 driver
= Driver
.Create (args
, false, new ConsoleReportPrinter ());
118 throw new Exception ("Failed to create compiler driver with the given arguments");
120 RootContext
.ToplevelTypes
= new ModuleCompiled (ctx
, true);
122 driver
.ProcessDefaultConfig ();
124 var startup_files
= new List
<string> ();
125 foreach (CompilationUnit file
in Location
.SourceFiles
)
126 startup_files
.Add (file
.Path
);
128 CompilerCallableEntryPoint
.Reset ();
129 RootContext
.ToplevelTypes
= new ModuleCompiled (ctx
, true);
130 /*var ctypes = */TypeManager
.InitCoreTypes ();
131 TypeManager
.InitExpressionTypes ();
133 Import
.Initialize ();
134 driver
.LoadReferences ();
135 TypeManager
.InitOptionalCoreTypes (ctx
);
137 RootContext
.EvalMode
= true;
140 return startup_files
.ToArray ();
146 Init (new string [0]);
151 CompilerCallableEntryPoint
.PartialReset ();
152 RootContext
.PartialReset ();
154 // Workaround for API limitation where full message printer cannot be passed
155 ReportPrinter printer
;
156 if (MessageOutput
== Console
.Out
|| MessageOutput
== Console
.Error
){
157 var console_reporter
= new ConsoleReportPrinter (MessageOutput
);
158 console_reporter
.Fatal
= driver
.fatal_errors
;
159 printer
= console_reporter
;
161 printer
= new StreamReportPrinter (MessageOutput
);
163 ctx
= new CompilerContext (new Report (printer
));
164 RootContext
.ToplevelTypes
= new ModuleCompiled (ctx
, true);
167 // PartialReset should not reset the core types, this is very redundant.
169 // if (!TypeManager.InitCoreTypes (ctx, null))
170 // throw new Exception ("Failed to InitCoreTypes");
171 // TypeManager.InitOptionalCoreTypes (ctx);
173 Location
.AddFile (null, "{interactive}");
174 Location
.Initialize ();
176 current_debug_name
= "interactive" + (count
++) + ".dll";
177 if (Environment
.GetEnvironmentVariable ("SAVE") != null){
178 CodeGen
.Init (current_debug_name
, current_debug_name
, false, ctx
);
180 CodeGen
.InitDynamic (ctx
, current_debug_name
);
184 /// The base class for the classes that host the user generated code
188 /// This is the base class that will host the code
189 /// executed by the Evaluator. By default
190 /// this is the Mono.CSharp.InteractiveBase class
191 /// which is useful for interactive use.
193 /// By changing this property you can control the
194 /// base class and the static members that are
195 /// available to your evaluated code.
197 static public TypeSpec InteractiveBaseClass
{
199 if (interactive_base_class
!= null)
200 return interactive_base_class
;
202 return Import
.ImportType (typeof (InteractiveBase
));
206 public static void SetInteractiveBaseClass (Type type
)
209 throw new ArgumentNullException ();
211 lock (evaluator_lock
)
212 interactive_base_class
= Import
.ImportType (type
);
216 /// Interrupts the evaluation of an expression executing in Evaluate.
219 /// Use this method to interrupt long-running invocations.
221 public static void Interrupt ()
223 if (!inited
|| !invoking
)
226 if (invoke_thread
!= null)
227 invoke_thread
.Abort ();
231 /// Compiles the input string and returns a delegate that represents the compiled code.
235 /// Compiles the input string as a C# expression or
236 /// statement, unlike the Evaluate method, the
237 /// resulting delegate can be invoked multiple times
238 /// without incurring in the compilation overhead.
240 /// If the return value of this function is null,
241 /// this indicates that the parsing was complete.
242 /// If the return value is a string it indicates
243 /// that the input string was partial and that the
244 /// invoking code should provide more code before
245 /// the code can be successfully compiled.
247 /// If you know that you will always get full expressions or
248 /// statements and do not care about partial input, you can use
249 /// the other Compile overload.
251 /// On success, in addition to returning null, the
252 /// compiled parameter will be set to the delegate
253 /// that can be invoked to execute the code.
256 static public string Compile (string input
, out CompiledMethod compiled
)
258 if (input
== null || input
.Length
== 0){
263 lock (evaluator_lock
){
268 CSharpParser parser
= ParseString (ParseMode
.Silent
, input
, out partial_input
);
274 ParseString (ParseMode
.ReportErrors
, input
, out partial_input
);
278 object parser_result
= parser
.InteractiveResult
;
280 if (!(parser_result
is Class
)){
281 int errors
= ctx
.Report
.Errors
;
283 NamespaceEntry
.VerifyAllUsing ();
284 if (errors
== ctx
.Report
.Errors
)
285 parser
.CurrentNamespace
.Extract (using_alias_list
, using_list
);
288 compiled
= CompileBlock (parser_result
as Class
, parser
.undo
, ctx
.Report
);
295 /// Compiles the input string and returns a delegate that represents the compiled code.
299 /// Compiles the input string as a C# expression or
300 /// statement, unlike the Evaluate method, the
301 /// resulting delegate can be invoked multiple times
302 /// without incurring in the compilation overhead.
304 /// This method can only deal with fully formed input
305 /// strings and does not provide a completion mechanism.
306 /// If you must deal with partial input (for example for
307 /// interactive use) use the other overload.
309 /// On success, a delegate is returned that can be used
310 /// to invoke the method.
313 static public CompiledMethod
Compile (string input
)
315 CompiledMethod compiled
;
317 // Ignore partial inputs
318 if (Compile (input
, out compiled
) != null){
319 // Error, the input was partial.
323 // Either null (on error) or the compiled method.
328 // Todo: Should we handle errors, or expect the calling code to setup
329 // the recording themselves?
333 /// Evaluates and expression or statement and returns any result values.
336 /// Evaluates the input string as a C# expression or
337 /// statement. If the input string is an expression
338 /// the result will be stored in the result variable
339 /// and the result_set variable will be set to true.
341 /// It is necessary to use the result/result_set
342 /// pair to identify when a result was set (for
343 /// example, execution of user-provided input can be
344 /// an expression, a statement or others, and
345 /// result_set would only be set if the input was an
348 /// If the return value of this function is null,
349 /// this indicates that the parsing was complete.
350 /// If the return value is a string, it indicates
351 /// that the input is partial and that the user
352 /// should provide an updated string.
354 public static string Evaluate (string input
, out object result
, out bool result_set
)
356 CompiledMethod compiled
;
361 input
= Compile (input
, out compiled
);
365 if (compiled
== null)
369 // The code execution does not need to keep the compiler lock
371 object retval
= typeof (NoValueSet
);
374 invoke_thread
= System
.Threading
.Thread
.CurrentThread
;
376 compiled (ref retval
);
377 } catch (ThreadAbortException e
){
378 Thread
.ResetAbort ();
379 Console
.WriteLine ("Interrupted!\n{0}", e
);
385 // We use a reference to a compiler type, in this case
386 // Driver as a flag to indicate that this was a statement
388 if (retval
!= typeof (NoValueSet
)){
396 public static string [] GetCompletions (string input
, out string prefix
)
399 if (input
== null || input
.Length
== 0)
402 lock (evaluator_lock
){
407 CSharpParser parser
= ParseString (ParseMode
.GetCompletions
, input
, out partial_input
);
409 if (CSharpParser
.yacc_verbose_flag
!= 0)
410 Console
.WriteLine ("DEBUG: No completions available");
414 Class parser_result
= parser
.InteractiveResult
as Class
;
416 if (parser_result
== null){
417 if (CSharpParser
.yacc_verbose_flag
!= 0)
418 Console
.WriteLine ("Do not know how to cope with !Class yet");
423 RootContext
.ResolveTree ();
424 if (ctx
.Report
.Errors
!= 0)
427 RootContext
.PopulateTypes ();
428 if (ctx
.Report
.Errors
!= 0)
431 MethodOrOperator method
= null;
432 foreach (MemberCore member
in parser_result
.Methods
){
433 if (member
.Name
!= "Host")
436 method
= (MethodOrOperator
) member
;
440 throw new InternalErrorException ("did not find the the Host method");
442 BlockContext bc
= new BlockContext (method
, method
.Block
, method
.ReturnType
);
445 method
.Block
.Resolve (null, bc
, method
.ParameterInfo
, method
);
446 } catch (CompletionResult cr
){
447 prefix
= cr
.BaseText
;
451 parser
.undo
.ExecuteUndo ();
459 /// Executes the given expression or statement.
462 /// Executes the provided statement, returns true
463 /// on success, false on parsing errors. Exceptions
464 /// might be thrown by the called code.
466 public static bool Run (string statement
)
474 bool ok
= Evaluate (statement
, out result
, out result_set
) == null;
480 /// Evaluates and expression or statement and returns the result.
483 /// Evaluates the input string as a C# expression or
484 /// statement and returns the value.
486 /// This method will throw an exception if there is a syntax error,
487 /// of if the provided input is not an expression but a statement.
489 public static object Evaluate (string input
)
494 string r
= Evaluate (input
, out result
, out result_set
);
497 throw new ArgumentException ("Syntax error on input: partial input");
499 if (result_set
== false)
500 throw new ArgumentException ("The expression did not set a result");
507 StatementOrExpression
,
513 // Deambiguates the input string to determine if we
514 // want to process a statement or if we want to
515 // process a compilation unit.
517 // This is done using a top-down predictive parser,
518 // since the yacc/jay parser can not deambiguage this
519 // without more than one lookahead token. There are very
522 static InputKind
ToplevelOrStatement (SeekableStreamReader seekable
)
524 Tokenizer tokenizer
= new Tokenizer (seekable
, (CompilationUnit
) Location
.SourceFiles
[0], ctx
);
526 int t
= tokenizer
.token ();
529 return InputKind
.EOF
;
531 // These are toplevels
533 case Token
.OPEN_BRACKET
:
537 case Token
.INTERFACE
:
539 case Token
.NAMESPACE
:
541 case Token
.PROTECTED
:
546 return InputKind
.CompilationUnit
;
548 // Definitely expression
565 return InputKind
.StatementOrExpression
;
567 // These need deambiguation help
569 t
= tokenizer
.token ();
571 return InputKind
.EOF
;
573 if (t
== Token
.IDENTIFIER
)
574 return InputKind
.CompilationUnit
;
575 return InputKind
.StatementOrExpression
;
578 // Distinguish between:
579 // delegate opt_anonymous_method_signature block
582 t
= tokenizer
.token ();
584 return InputKind
.EOF
;
585 if (t
== Token
.OPEN_PARENS
|| t
== Token
.OPEN_BRACE
)
586 return InputKind
.StatementOrExpression
;
587 return InputKind
.CompilationUnit
;
589 // Distinguih between:
591 // unsafe as modifier of a type declaration
593 t
= tokenizer
.token ();
595 return InputKind
.EOF
;
596 if (t
== Token
.OPEN_PARENS
)
597 return InputKind
.StatementOrExpression
;
598 return InputKind
.CompilationUnit
;
600 // These are errors: we list explicitly what we had
601 // from the grammar, ERROR and then everything else
606 return InputKind
.Error
;
608 // This catches everything else allowed by
609 // expressions. We could add one-by-one use cases
612 return InputKind
.StatementOrExpression
;
617 // Parses the string @input and returns a CSharpParser if succeeful.
619 // if @silent is set to true then no errors are
620 // reported to the user. This is used to do various calls to the
621 // parser and check if the expression is parsable.
623 // @partial_input: if @silent is true, then it returns whether the
624 // parsed expression was partial, and more data is needed
626 static CSharpParser
ParseString (ParseMode mode
, string input
, out bool partial_input
)
628 partial_input
= false;
630 queued_fields
.Clear ();
631 Tokenizer
.LocatedToken
.Initialize ();
633 Stream s
= new MemoryStream (Encoding
.Default
.GetBytes (input
));
634 SeekableStreamReader seekable
= new SeekableStreamReader (s
, Encoding
.Default
);
636 InputKind kind
= ToplevelOrStatement (seekable
);
637 if (kind
== InputKind
.Error
){
638 if (mode
== ParseMode
.ReportErrors
)
639 ctx
.Report
.Error (-25, "Detection Parsing Error");
640 partial_input
= false;
644 if (kind
== InputKind
.EOF
){
645 if (mode
== ParseMode
.ReportErrors
)
646 Console
.Error
.WriteLine ("Internal error: EOF condition should have been detected in a previous call with silent=true");
647 partial_input
= true;
651 seekable
.Position
= 0;
653 CSharpParser parser
= new CSharpParser (seekable
, (CompilationUnit
) Location
.SourceFiles
[0], ctx
);
655 if (kind
== InputKind
.StatementOrExpression
){
656 parser
.Lexer
.putback_char
= Tokenizer
.EvalStatementParserCharacter
;
657 RootContext
.StatementMode
= true;
660 // Do not activate EvalCompilationUnitParserCharacter until
661 // I have figured out all the limitations to invoke methods
662 // in the generated classes. See repl.txt
664 parser
.Lexer
.putback_char
= Tokenizer
.EvalUsingDeclarationsParserCharacter
;
665 //parser.Lexer.putback_char = Tokenizer.EvalCompilationUnitParserCharacter;
666 RootContext
.StatementMode
= false;
669 if (mode
== ParseMode
.GetCompletions
)
670 parser
.Lexer
.CompleteOnEOF
= true;
672 ReportPrinter old_printer
= null;
673 if ((mode
== ParseMode
.Silent
|| mode
== ParseMode
.GetCompletions
) && CSharpParser
.yacc_verbose_flag
== 0)
674 old_printer
= SetPrinter (new StreamReportPrinter (TextWriter
.Null
));
679 if (ctx
.Report
.Errors
!= 0){
680 if (mode
!= ParseMode
.ReportErrors
&& parser
.UnexpectedEOF
)
681 partial_input
= true;
683 parser
.undo
.ExecuteUndo ();
687 if (old_printer
!= null)
688 SetPrinter (old_printer
);
694 // Queue all the fields that we use, as we need to then go from FieldBuilder to FieldInfo
695 // or reflection gets confused (it basically gets confused, and variables override each
698 static List
<Field
> queued_fields
= new List
<Field
> ();
700 //static ArrayList types = new ArrayList ();
702 static volatile bool invoking
;
704 static CompiledMethod
CompileBlock (Class host
, Undo undo
, Report Report
)
706 RootContext
.ResolveTree ();
707 if (Report
.Errors
!= 0){
712 RootContext
.PopulateTypes ();
714 if (Report
.Errors
!= 0){
719 TypeBuilder tb
= null;
720 MethodBuilder mb
= null;
723 tb
= host
.TypeBuilder
;
725 foreach (MemberCore member
in host
.Methods
){
726 if (member
.Name
!= "Host")
729 MethodOrOperator method
= (MethodOrOperator
) member
;
730 mb
= method
.MethodBuilder
;
735 throw new Exception ("Internal error: did not find the method builder for the generated method");
738 RootContext
.EmitCode ();
739 if (Report
.Errors
!= 0){
744 RootContext
.CloseTypes ();
746 if (Environment
.GetEnvironmentVariable ("SAVE") != null)
747 CodeGen
.Save (current_debug_name
, false, Report
);
753 // Unlike Mono, .NET requires that the MethodInfo is fetched, it cant
754 // work from MethodBuilders. Retarded, I know.
756 var tt
= CodeGen
.Assembly
.Builder
.GetType (tb
.Name
);
757 MethodInfo mi
= tt
.GetMethod (mb
.Name
);
759 // Pull the FieldInfos from the type, and keep track of them
760 foreach (Field field
in queued_fields
){
761 FieldInfo fi
= tt
.GetField (field
.Name
);
763 Tuple
<FieldSpec
, FieldInfo
> old
;
765 // If a previous value was set, nullify it, so that we do
767 if (fields
.TryGetValue (field
.Name
, out old
)) {
768 if (old
.Item1
.MemberType
.IsStruct
) {
770 // TODO: Clear fields for structs
774 old
.Item2
.SetValue (null, null);
779 fields
[field
.Name
] = Tuple
.Create (old
.Item1
, fi
);
781 fields
.Add (field
.Name
, Tuple
.Create (field
.Spec
, fi
));
786 queued_fields
.Clear ();
788 return (CompiledMethod
) System
.Delegate
.CreateDelegate (typeof (CompiledMethod
), mi
);
791 static internal void LoadAliases (NamespaceEntry ns
)
793 ns
.Populate (using_alias_list
, using_list
);
797 /// A sentinel value used to indicate that no value was
798 /// was set by the compiled function. This is used to
799 /// differentiate between a function not returning a
802 public class NoValueSet
{
805 static internal Tuple
<FieldSpec
, FieldInfo
> LookupField (string name
)
807 Tuple
<FieldSpec
, FieldInfo
> fi
;
808 fields
.TryGetValue (name
, out fi
);
813 // Puts the FieldBuilder into a queue of names that will be
814 // registered. We can not register FieldBuilders directly
815 // we need to fetch the FieldInfo after Reflection cooks the
816 // types, or bad things happen (bad means: FieldBuilders behave
817 // incorrectly across multiple assemblies, causing assignments to
820 // This also serves for the parser to register Field classes
821 // that should be exposed as global variables
823 static internal void QueueField (Field f
)
825 queued_fields
.Add (f
);
828 static string Quote (string s
)
830 if (s
.IndexOf ('"') != -1)
831 s
= s
.Replace ("\"", "\\\"");
833 return "\"" + s
+ "\"";
836 static public string GetUsing ()
838 lock (evaluator_lock
){
839 StringBuilder sb
= new StringBuilder ();
841 foreach (object x
in using_alias_list
)
842 sb
.Append (String
.Format ("using {0};\n", x
));
844 foreach (object x
in using_list
)
845 sb
.Append (String
.Format ("using {0};\n", x
));
847 return sb
.ToString ();
851 static internal ICollection
<string> GetUsingList ()
853 var res
= new List
<string> (using_list
.Count
);
854 foreach (object ue
in using_list
)
855 res
.Add (ue
.ToString ());
859 static internal string [] GetVarNames ()
861 lock (evaluator_lock
){
862 return new List
<string> (fields
.Keys
).ToArray ();
866 static public string GetVars ()
868 lock (evaluator_lock
){
869 StringBuilder sb
= new StringBuilder ();
871 foreach (var de
in fields
){
872 var fi
= LookupField (de
.Key
);
875 value = fi
.Item2
.GetValue (null);
877 value = Quote ((string)value);
879 value = "<error reading value>";
882 sb
.AppendFormat ("{0} {1} = {2}", fi
.Item1
.MemberType
.GetSignatureForError (), de
.Key
, value);
886 return sb
.ToString ();
891 /// Loads the given assembly and exposes the API to the user.
893 static public void LoadAssembly (string file
)
895 lock (evaluator_lock
){
896 driver
.LoadAssembly (file
, false);
897 GlobalRootNamespace
.Instance
.ComputeNamespaces (ctx
);
902 /// Exposes the API of the given assembly to the Evaluator
904 static public void ReferenceAssembly (Assembly a
)
906 lock (evaluator_lock
){
907 // GlobalRootNamespace.Instance.AddAssemblyReference (a);
908 // GlobalRootNamespace.Instance.ComputeNamespaces (ctx);
909 GlobalRootNamespace
.Instance
.ImportAssembly (a
);
914 /// If true, turns type expressions into valid expressions
915 /// and calls the describe method on it
917 public static bool DescribeTypeExpressions
;
922 /// A delegate that can be used to invoke the
923 /// compiled expression or statement.
926 /// Since the Compile methods will compile
927 /// statements and expressions into the same
928 /// delegate, you can tell if a value was returned
929 /// by checking whether the returned value is of type
933 public delegate void CompiledMethod (ref object retvalue
);
936 /// The default base class for every interaction line
939 /// The expressions and statements behave as if they were
940 /// a static method of this class. The InteractiveBase class
941 /// contains a number of useful methods, but can be overwritten
942 /// by setting the InteractiveBaseType property in the Evaluator
944 public class InteractiveBase
{
946 /// Determines where the standard output of methods in this class will go.
948 public static TextWriter Output
= Console
.Out
;
951 /// Determines where the standard error of methods in this class will go.
953 public static TextWriter Error
= Console
.Error
;
956 /// The primary prompt used for interactive use.
958 public static string Prompt
= "csharp> ";
961 /// The secondary prompt used for interactive use (used when
962 /// an expression is incomplete).
964 public static string ContinuationPrompt
= " > ";
967 /// Used to signal that the user has invoked the `quit' statement.
969 public static bool QuitRequested
;
972 /// Shows all the variables defined so far.
974 static public void ShowVars ()
976 Output
.Write (Evaluator
.GetVars ());
981 /// Displays the using statements in effect at this point.
983 static public void ShowUsing ()
985 Output
.Write (Evaluator
.GetUsing ());
989 public delegate void Simple ();
992 /// Times the execution of the given delegate
994 static public TimeSpan
Time (Simple a
)
996 DateTime start
= DateTime
.Now
;
998 return DateTime
.Now
- start
;
1003 /// Loads the assemblies from a package
1006 /// Loads the assemblies from a package. This is equivalent
1007 /// to passing the -pkg: command line flag to the C# compiler
1008 /// on the command line.
1010 static public void LoadPackage (string pkg
)
1013 Error
.WriteLine ("Invalid package specified");
1017 string pkgout
= Driver
.GetPackageFlags (pkg
, false, RootContext
.ToplevelTypes
.Compiler
.Report
);
1021 string [] xargs
= pkgout
.Trim (new Char
[] {' ', '\n', '\r', '\t'}
).
1022 Split (new Char
[] { ' ', '\t'}
);
1024 foreach (string s
in xargs
){
1025 if (s
.StartsWith ("-r:") || s
.StartsWith ("/r:") || s
.StartsWith ("/reference:")){
1026 string lib
= s
.Substring (s
.IndexOf (':')+1);
1028 Evaluator
.LoadAssembly (lib
);
1036 /// Loads the assembly
1039 /// Loads the specified assembly and makes its types
1040 /// available to the evaluator. This is equivalent
1041 /// to passing the -pkg: command line flag to the C#
1042 /// compiler on the command line.
1044 static public void LoadAssembly (string assembly
)
1046 Evaluator
.LoadAssembly (assembly
);
1050 /// Returns a list of available static methods.
1052 static public string help
{
1054 return "Static methods:\n" +
1055 " Describe (object) - Describes the object's type\n" +
1056 " LoadPackage (package); - Loads the given Package (like -pkg:FILE)\n" +
1057 " LoadAssembly (assembly) - Loads the given assembly (like -r:ASSEMBLY)\n" +
1058 " ShowVars (); - Shows defined local variables.\n" +
1059 " ShowUsing (); - Show active using declarations.\n" +
1060 " Prompt - The prompt used by the C# shell\n" +
1061 " ContinuationPrompt - The prompt for partial input\n" +
1062 " Time(() -> { }) - Times the specified code\n" +
1063 " quit; - You'll never believe it - this quits the repl!\n" +
1064 " help; - This help text\n";
1069 /// Indicates to the read-eval-print-loop that the interaction should be finished.
1071 static public object quit
{
1073 QuitRequested
= true;
1080 /// Describes an object or a type.
1083 /// This method will show a textual representation
1084 /// of the object's type. If the object is a
1085 /// System.Type it renders the type directly,
1086 /// otherwise it renders the type returned by
1087 /// invoking GetType on the object.
1089 static public string Describe (object x
)
1094 var type
= x
as Type
?? x
.GetType ();
1096 StringWriter sw
= new StringWriter ();
1097 new Outline (type
, sw
, true, false, false).OutlineType ();
1098 return sw
.ToString ();
1104 // A local variable reference that will create a Field in a
1105 // Class with the resolved type. This is necessary so we can
1106 // support "var" as a field type in a class declaration.
1108 // We allow LocalVariableReferece to do the heavy lifting, and
1109 // then we insert the field with the resolved type
1111 public class LocalVariableReferenceWithClassSideEffect
: LocalVariableReference
{
1112 TypeContainer container
;
1115 public LocalVariableReferenceWithClassSideEffect (TypeContainer container
, string name
, Block current_block
, string local_variable_id
, LocalInfo li
, Location loc
)
1116 : base (current_block
, local_variable_id
, loc
, li
, false)
1118 this.container
= container
;
1122 public override bool Equals (object obj
)
1124 LocalVariableReferenceWithClassSideEffect lvr
= obj
as LocalVariableReferenceWithClassSideEffect
;
1128 if (lvr
.name
!= name
|| lvr
.container
!= container
)
1131 return base.Equals (obj
);
1134 public override int GetHashCode ()
1136 return name
.GetHashCode ();
1139 override public Expression
DoResolveLValue (ResolveContext ec
, Expression right_side
)
1141 Expression ret
= base.DoResolveLValue (ec
, right_side
);
1145 Field f
= new Field (container
, new TypeExpression (ret
.Type
, Location
),
1146 Modifiers
.PUBLIC
| Modifiers
.STATIC
,
1147 new MemberName (name
, Location
), null);
1148 container
.AddField (f
);
1150 Evaluator
.QueueField (f
);
1157 /// A class used to assign values if the source expression is not void
1159 /// Used by the interactive shell to allow it to call this code to set
1160 /// the return value for an invocation.
1162 class OptionalAssign
: SimpleAssign
{
1163 public OptionalAssign (Expression t
, Expression s
, Location loc
)
1168 protected override Expression
DoResolve (ResolveContext ec
)
1170 CloneContext cc
= new CloneContext ();
1171 Expression clone
= source
.Clone (cc
);
1174 // A useful feature for the REPL: if we can resolve the expression
1175 // as a type, Describe the type;
1177 if (Evaluator
.DescribeTypeExpressions
){
1178 var old_printer
= Evaluator
.SetPrinter (new StreamReportPrinter (TextWriter
.Null
));
1179 clone
= clone
.Resolve (ec
);
1181 clone
= source
.Clone (cc
);
1182 clone
= clone
.Resolve (ec
, ResolveFlags
.Type
);
1184 Evaluator
.SetPrinter (old_printer
);
1185 clone
= source
.Clone (cc
);
1186 clone
= clone
.Resolve (ec
);
1190 Arguments args
= new Arguments (1);
1191 args
.Add (new Argument (new TypeOf (source
, Location
)));
1192 source
= new Invocation (new SimpleName ("Describe", Location
), args
).Resolve (ec
);
1194 Evaluator
.SetPrinter (old_printer
);
1196 clone
= clone
.Resolve (ec
);
1201 // This means its really a statement.
1202 if (clone
.Type
== TypeManager
.void_type
){
1203 source
= source
.Resolve (ec
);
1205 type
= TypeManager
.void_type
;
1206 eclass
= ExprClass
.Value
;
1210 return base.DoResolve (ec
);
1213 public override void Emit (EmitContext ec
)
1221 public override void EmitStatement (EmitContext ec
)
1226 base.EmitStatement (ec
);
1231 List
<KeyValuePair
<TypeContainer
, TypeContainer
>> undo_types
;
1235 undo_types
= new List
<KeyValuePair
<TypeContainer
, TypeContainer
>> ();
1238 public void AddTypeContainer (TypeContainer current_container
, TypeContainer tc
)
1240 if (current_container
== tc
){
1241 Console
.Error
.WriteLine ("Internal error: inserting container into itself");
1245 if (undo_types
== null)
1246 undo_types
= new List
<KeyValuePair
<TypeContainer
, TypeContainer
>> ();
1248 undo_types
.Add (new KeyValuePair
<TypeContainer
, TypeContainer
> (current_container
, tc
));
1251 public void ExecuteUndo ()
1253 if (undo_types
== null)
1256 foreach (var p
in undo_types
){
1257 TypeContainer current_container
= p
.Key
;
1259 current_container
.RemoveTypeContainer (p
.Value
);