X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fmcs%2Feval.cs;h=a1ba9e235371cb7d79b0420af2067d871c645d43;hb=3e476138366f3dd57de9c1c65961adb3e1321e31;hp=2fec41eb895729034612aeda327373aca43668ff;hpb=0166aedda3143fb68f79d7caf99bfb3947609af5;p=mono.git diff --git a/mcs/mcs/eval.cs b/mcs/mcs/eval.cs index 2fec41eb895..a1ba9e23537 100644 --- a/mcs/mcs/eval.cs +++ b/mcs/mcs/eval.cs @@ -11,7 +11,7 @@ // using System; using System.Threading; -using System.Collections; +using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; using System.IO; @@ -36,18 +36,37 @@ namespace Mono.CSharp { /// Evaluator.Interrupt method. /// public class Evaluator { + + enum ParseMode { + // Parse silently, do not output any error messages + Silent, + + // Report errors during parse + ReportErrors, + + // Auto-complete, means that the tokenizer will start producing + // GETCOMPLETIONS tokens when it reaches a certain point. + GetCompletions + } + + static object evaluator_lock = new object (); + static string current_debug_name; static int count; static Thread invoke_thread; - - static ArrayList using_alias_list = new ArrayList (); - static ArrayList using_list = new ArrayList (); - static Hashtable fields = new Hashtable (); + + static List using_alias_list = new List (); + internal static List using_list = new List (); + static Dictionary fields = new Dictionary (); static Type interactive_base_class = typeof (InteractiveBase); static Driver driver; static bool inited; + static CompilerContext ctx; + + public static TextWriter MessageOutput = Console.Out; + /// /// Optional initialization for the Evaluator. /// @@ -63,19 +82,58 @@ namespace Mono.CSharp { /// public static void Init (string [] args) { - if (inited) - return; + InitAndGetStartupFiles (args); + } + + internal static ReportPrinter SetPrinter (ReportPrinter report_printer) + { + return ctx.Report.SetPrinter (report_printer); + } + + /// + /// Optional initialization for the Evaluator. + /// + /// + /// Initializes the Evaluator with the command line + /// options that would be processed by the command + /// line compiler. Only the first call to + /// InitAndGetStartupFiles or Init will work, any future + /// invocations are ignored. + /// + /// You can safely avoid calling this method if your application + /// does not need any of the features exposed by the command line + /// interface. + /// + /// This method return an array of strings that contains any + /// files that were specified in `args'. + /// + public static string [] InitAndGetStartupFiles (string [] args) + { + lock (evaluator_lock){ + if (inited) + return new string [0]; + + driver = Driver.Create (args, false, new ConsoleReportPrinter ()); + if (driver == null) + throw new Exception ("Failed to create compiler driver with the given arguments"); + + RootContext.ToplevelTypes = new ModuleCompiled (ctx, true); + + driver.ProcessDefaultConfig (); + + var startup_files = new List (); + foreach (CompilationUnit file in Location.SourceFiles) + startup_files.Add (file.Path); + + CompilerCallableEntryPoint.Reset (); + RootContext.ToplevelTypes = new ModuleCompiled (ctx, true); - RootContext.Version = LanguageVersion.Default; - driver = Driver.Create (args, false); - if (driver == null) - throw new Exception ("Failed to create compiler driver with the given arguments"); + driver.LoadReferences (); + RootContext.EvalMode = true; + inited = true; - driver.ProcessDefaultConfig (); - CompilerCallableEntryPoint.Reset (); - Driver.LoadReferences (); - RootContext.EvalMode = true; - inited = true; + return startup_files.ToArray (); + } } static void Init () @@ -86,22 +144,34 @@ namespace Mono.CSharp { static void Reset () { CompilerCallableEntryPoint.PartialReset (); + + // Workaround for API limitation where full message printer cannot be passed + ReportPrinter printer; + if (MessageOutput == Console.Out || MessageOutput == Console.Error){ + var console_reporter = new ConsoleReportPrinter (MessageOutput); + console_reporter.Fatal = driver.fatal_errors; + printer = console_reporter; + } else + printer = new StreamReportPrinter (MessageOutput); + + ctx = new CompilerContext (new Report (printer)); + RootContext.ToplevelTypes = new ModuleCompiled (ctx, true); // // PartialReset should not reset the core types, this is very redundant. // - if (!TypeManager.InitCoreTypes ()) + if (!TypeManager.InitCoreTypes (ctx)) throw new Exception ("Failed to InitCoreTypes"); - TypeManager.InitOptionalCoreTypes (); + TypeManager.InitOptionalCoreTypes (ctx); - Location.AddFile (""); + Location.AddFile (null, "{interactive}"); Location.Initialize (); current_debug_name = "interactive" + (count++) + ".dll"; if (Environment.GetEnvironmentVariable ("SAVE") != null){ - CodeGen.Init (current_debug_name, current_debug_name, false); + CodeGen.Init (current_debug_name, current_debug_name, false, ctx); } else - CodeGen.InitDynamic (current_debug_name); + CodeGen.InitDynamic (ctx, current_debug_name); } /// @@ -126,13 +196,14 @@ namespace Mono.CSharp { set { if (value == null) throw new ArgumentNullException (); - - interactive_base_class = value; + + lock (evaluator_lock) + interactive_base_class = value; } } /// - /// Interrupts the evaluation of an expression. + /// Interrupts the evaluation of an expression executing in Evaluate. /// /// /// Use this method to interrupt long-running invocations. @@ -146,6 +217,103 @@ namespace Mono.CSharp { invoke_thread.Abort (); } + /// + /// Compiles the input string and returns a delegate that represents the compiled code. + /// + /// + /// + /// Compiles the input string as a C# expression or + /// statement, unlike the Evaluate method, the + /// resulting delegate can be invoked multiple times + /// without incurring in the compilation overhead. + /// + /// If the return value of this function is null, + /// this indicates that the parsing was complete. + /// If the return value is a string it indicates + /// that the input string was partial and that the + /// invoking code should provide more code before + /// the code can be successfully compiled. + /// + /// If you know that you will always get full expressions or + /// statements and do not care about partial input, you can use + /// the other Compile overload. + /// + /// On success, in addition to returning null, the + /// compiled parameter will be set to the delegate + /// that can be invoked to execute the code. + /// + /// + static public string Compile (string input, out CompiledMethod compiled) + { + if (input == null || input.Length == 0){ + compiled = null; + return null; + } + + lock (evaluator_lock){ + if (!inited) + Init (); + + bool partial_input; + CSharpParser parser = ParseString (ParseMode.Silent, input, out partial_input); + if (parser == null){ + compiled = null; + if (partial_input) + return input; + + ParseString (ParseMode.ReportErrors, input, out partial_input); + return null; + } + + object parser_result = parser.InteractiveResult; + + if (!(parser_result is Class)){ + int errors = ctx.Report.Errors; + + NamespaceEntry.VerifyAllUsing (); + if (errors == ctx.Report.Errors) + parser.CurrentNamespace.Extract (using_alias_list, using_list); + } + + compiled = CompileBlock (parser_result as Class, parser.undo, ctx.Report); + } + + return null; + } + + /// + /// Compiles the input string and returns a delegate that represents the compiled code. + /// + /// + /// + /// Compiles the input string as a C# expression or + /// statement, unlike the Evaluate method, the + /// resulting delegate can be invoked multiple times + /// without incurring in the compilation overhead. + /// + /// This method can only deal with fully formed input + /// strings and does not provide a completion mechanism. + /// If you must deal with partial input (for example for + /// interactive use) use the other overload. + /// + /// On success, a delegate is returned that can be used + /// to invoke the method. + /// + /// + static public CompiledMethod Compile (string input) + { + CompiledMethod compiled; + + // Ignore partial inputs + if (Compile (input, out compiled) != null){ + // Error, the input was partial. + return null; + } + + // Either null (on error) or the compiled method. + return compiled; + } + // // Todo: Should we handle errors, or expect the calling code to setup // the recording themselves? @@ -175,41 +343,108 @@ namespace Mono.CSharp { /// public static string Evaluate (string input, out object result, out bool result_set) { + CompiledMethod compiled; + result_set = false; result = null; - - if (input == null || input.Length == 0) - return null; - if (!inited) - Init (); + input = Compile (input, out compiled); + if (input != null) + return input; - bool partial_input; - CSharpParser parser = ParseString (true, input, out partial_input); - if (parser == null){ - if (partial_input) - return input; - - ParseString (false, input, out partial_input); + if (compiled == null) return null; - } + + // + // The code execution does not need to keep the compiler lock + // + object retval = typeof (NoValueSet); - object parser_result = parser.InteractiveResult; - - if (!(parser_result is Class)) - parser.CurrentNamespace.Extract (using_alias_list, using_list); + try { + invoke_thread = System.Threading.Thread.CurrentThread; + invoking = true; + compiled (ref retval); + } catch (ThreadAbortException e){ + Thread.ResetAbort (); + Console.WriteLine ("Interrupted!\n{0}", e); + } finally { + invoking = false; + } - result = ExecuteBlock (parser_result as Class, parser.undo); // // We use a reference to a compiler type, in this case // Driver as a flag to indicate that this was a statement // - if (result != typeof (NoValueSet)) + if (retval != typeof (NoValueSet)){ result_set = true; + result = retval; + } return null; } + public static string [] GetCompletions (string input, out string prefix) + { + prefix = ""; + if (input == null || input.Length == 0) + return null; + + lock (evaluator_lock){ + if (!inited) + Init (); + + bool partial_input; + CSharpParser parser = ParseString (ParseMode.GetCompletions, input, out partial_input); + if (parser == null){ + if (CSharpParser.yacc_verbose_flag != 0) + Console.WriteLine ("DEBUG: No completions available"); + return null; + } + + Class parser_result = parser.InteractiveResult as Class; + + if (parser_result == null){ + if (CSharpParser.yacc_verbose_flag != 0) + Console.WriteLine ("Do not know how to cope with !Class yet"); + return null; + } + + try { + RootContext.ResolveTree (); + if (ctx.Report.Errors != 0) + return null; + + RootContext.PopulateTypes (); + if (ctx.Report.Errors != 0) + return null; + + MethodOrOperator method = null; + foreach (MemberCore member in parser_result.Methods){ + if (member.Name != "Host") + continue; + + method = (MethodOrOperator) member; + break; + } + if (method == null) + throw new InternalErrorException ("did not find the the Host method"); + + BlockContext bc = new BlockContext (method, method.Block, method.ReturnType); + + try { + method.Block.Resolve (null, bc, method.ParameterInfo, method); + } catch (CompletionResult cr){ + prefix = cr.BaseText; + return cr.Result; + } + } finally { + parser.undo.ExecuteUndo (); + } + + } + return null; + } + /// /// Executes the given expression or statement. /// @@ -222,10 +457,10 @@ namespace Mono.CSharp { { if (!inited) Init (); - + object result; bool result_set; - + bool ok = Evaluate (statement, out result, out result_set) == null; return ok; @@ -256,7 +491,7 @@ namespace Mono.CSharp { return result; } - + enum InputKind { EOF, StatementOrExpression, @@ -276,7 +511,7 @@ namespace Mono.CSharp { // static InputKind ToplevelOrStatement (SeekableStreamReader seekable) { - Tokenizer tokenizer = new Tokenizer (seekable, Location.SourceFiles [0]); + Tokenizer tokenizer = new Tokenizer (seekable, (CompilationUnit) Location.SourceFiles [0], ctx); int t = tokenizer.token (); switch (t){ @@ -378,25 +613,26 @@ namespace Mono.CSharp { // @partial_input: if @silent is true, then it returns whether the // parsed expression was partial, and more data is needed // - static CSharpParser ParseString (bool silent, string input, out bool partial_input) + static CSharpParser ParseString (ParseMode mode, string input, out bool partial_input) { partial_input = false; Reset (); queued_fields.Clear (); + Tokenizer.LocatedToken.Initialize (); Stream s = new MemoryStream (Encoding.Default.GetBytes (input)); SeekableStreamReader seekable = new SeekableStreamReader (s, Encoding.Default); InputKind kind = ToplevelOrStatement (seekable); if (kind == InputKind.Error){ - if (!silent) - Report.Error (-25, "Detection Parsing Error"); + if (mode == ParseMode.ReportErrors) + ctx.Report.Error (-25, "Detection Parsing Error"); partial_input = false; return null; } if (kind == InputKind.EOF){ - if (silent == false) + if (mode == ParseMode.ReportErrors) Console.Error.WriteLine ("Internal error: EOF condition should have been detected in a previous call with silent=true"); partial_input = true; return null; @@ -404,8 +640,7 @@ namespace Mono.CSharp { } seekable.Position = 0; - CSharpParser parser = new CSharpParser (seekable, Location.SourceFiles [0]); - parser.ErrorOutput = Report.Stderr; + CSharpParser parser = new CSharpParser (seekable, (CompilationUnit) Location.SourceFiles [0], ctx); if (kind == InputKind.StatementOrExpression){ parser.Lexer.putback_char = Tokenizer.EvalStatementParserCharacter; @@ -421,51 +656,54 @@ namespace Mono.CSharp { RootContext.StatementMode = false; } - if (silent) - Report.DisableReporting (); + if (mode == ParseMode.GetCompletions) + parser.Lexer.CompleteOnEOF = true; + + ReportPrinter old_printer = null; + if ((mode == ParseMode.Silent || mode == ParseMode.GetCompletions) && CSharpParser.yacc_verbose_flag == 0) + old_printer = SetPrinter (new StreamReportPrinter (TextWriter.Null)); + try { parser.parse (); } finally { - if (Report.Errors != 0){ - if (silent && parser.UnexpectedEOF) + if (ctx.Report.Errors != 0){ + if (mode != ParseMode.ReportErrors && parser.UnexpectedEOF) partial_input = true; parser.undo.ExecuteUndo (); parser = null; } - if (silent) - Report.EnableReporting (); + if (old_printer != null) + SetPrinter (old_printer); } return parser; } - delegate void HostSignature (ref object retvalue); - // // Queue all the fields that we use, as we need to then go from FieldBuilder to FieldInfo // or reflection gets confused (it basically gets confused, and variables override each // other). // - static ArrayList queued_fields = new ArrayList (); + static List queued_fields = new List (); //static ArrayList types = new ArrayList (); static volatile bool invoking; - static object ExecuteBlock (Class host, Undo undo) + static CompiledMethod CompileBlock (Class host, Undo undo, Report Report) { RootContext.ResolveTree (); if (Report.Errors != 0){ undo.ExecuteUndo (); - return typeof (NoValueSet); + return null; } RootContext.PopulateTypes (); if (Report.Errors != 0){ undo.ExecuteUndo (); - return typeof (NoValueSet); + return null; } TypeBuilder tb = null; @@ -488,16 +726,18 @@ namespace Mono.CSharp { } RootContext.EmitCode (); - if (Report.Errors != 0) - return typeof (NoValueSet); + if (Report.Errors != 0){ + undo.ExecuteUndo (); + return null; + } RootContext.CloseTypes (); if (Environment.GetEnvironmentVariable ("SAVE") != null) - CodeGen.Save (current_debug_name, false); + CodeGen.Save (current_debug_name, false, Report); if (host == null) - return typeof (NoValueSet); + return null; // // Unlike Mono, .NET requires that the MethodInfo is fetched, it cant @@ -510,12 +750,12 @@ namespace Mono.CSharp { foreach (Field field in queued_fields){ FieldInfo fi = tt.GetField (field.Name); - FieldInfo old = (FieldInfo) fields [field.Name]; + FieldInfo old; // If a previous value was set, nullify it, so that we do // not leak memory - if (old != null){ - if (old.FieldType.IsValueType){ + if (fields.TryGetValue (field.Name, out old)){ + if (TypeManager.IsStruct (old.FieldType)){ // // TODO: Clear fields for structs // @@ -533,39 +773,28 @@ namespace Mono.CSharp { queued_fields.Clear (); - HostSignature invoker = (HostSignature) System.Delegate.CreateDelegate (typeof (HostSignature), mi); - object retval = typeof (NoValueSet); - - try { - invoke_thread = System.Threading.Thread.CurrentThread; - invoking = true; - invoker (ref retval); - } catch (ThreadAbortException e){ - Thread.ResetAbort (); - Console.WriteLine ("Interrupted!\n{0}", e); - } finally { - invoking = false; - } - - // d.DynamicInvoke (new object [] { retval }); - - return retval; + return (CompiledMethod) System.Delegate.CreateDelegate (typeof (CompiledMethod), mi); } - + static internal void LoadAliases (NamespaceEntry ns) { ns.Populate (using_alias_list, using_list); } - // - // Just a placeholder class, used as a sentinel to determine that the - // generated code did not set a value - class NoValueSet { + /// + /// A sentinel value used to indicate that no value was + /// was set by the compiled function. This is used to + /// differentiate between a function not returning a + /// value and null. + /// + public class NoValueSet { } static internal FieldInfo LookupField (string name) { - FieldInfo fi = (FieldInfo) fields [name]; + FieldInfo fi; + if (!fields.TryGetValue (name, out fi)) + return null; return fi; } @@ -596,43 +825,62 @@ namespace Mono.CSharp { static public string GetUsing () { - StringBuilder sb = new StringBuilder (); - - foreach (object x in using_alias_list) - sb.Append (String.Format ("using {0};\n", x)); - - foreach (object x in using_list) - sb.Append (String.Format ("using {0};\n", x)); - - return sb.ToString (); + lock (evaluator_lock){ + StringBuilder sb = new StringBuilder (); + + foreach (object x in using_alias_list) + sb.Append (String.Format ("using {0};\n", x)); + + foreach (object x in using_list) + sb.Append (String.Format ("using {0};\n", x)); + + return sb.ToString (); + } } + static internal ICollection GetUsingList () + { + var res = new List (using_list.Count); + foreach (object ue in using_list) + res.Add (ue.ToString ()); + return res; + } + + static internal string [] GetVarNames () + { + lock (evaluator_lock){ + return new List (fields.Keys).ToArray (); + } + } + static public string GetVars () { - StringBuilder sb = new StringBuilder (); - - foreach (DictionaryEntry de in fields){ - FieldInfo fi = LookupField ((string) de.Key); - object value = null; - bool error = false; + lock (evaluator_lock){ + StringBuilder sb = new StringBuilder (); - try { - if (value == null) - value = "null"; - value = fi.GetValue (null); - if (value is string) - value = Quote ((string)value); - } catch { - error = true; + foreach (var de in fields){ + FieldInfo fi = LookupField (de.Key); + object value = null; + bool error = false; + + try { + if (value == null) + value = "null"; + value = fi.GetValue (null); + if (value is string) + value = Quote ((string)value); + } catch { + error = true; + } + + if (error) + sb.Append (String.Format ("{0} {1} ", TypeManager.CSharpName(fi.FieldType), de.Key)); + else + sb.Append (String.Format ("{0} {1} = {2}", TypeManager.CSharpName(fi.FieldType), de.Key, value)); } - - if (error) - sb.Append (String.Format ("{0} {1} ", TypeManager.CSharpName(fi.FieldType), de.Key)); - else - sb.Append (String.Format ("{0} {1} = {2}", TypeManager.CSharpName(fi.FieldType), de.Key, value)); + + return sb.ToString (); } - - return sb.ToString (); } /// @@ -640,7 +888,10 @@ namespace Mono.CSharp { /// static public void LoadAssembly (string file) { - Driver.LoadAssembly (file, true); + lock (evaluator_lock){ + driver.LoadAssembly (file, false); + GlobalRootNamespace.Instance.ComputeNamespaces (ctx); + } } /// @@ -648,26 +899,82 @@ namespace Mono.CSharp { /// static public void ReferenceAssembly (Assembly a) { - RootNamespace.Global.AddAssemblyReference (a); + lock (evaluator_lock){ + GlobalRootNamespace.Instance.AddAssemblyReference (a); + GlobalRootNamespace.Instance.ComputeNamespaces (ctx); + } } - + + /// + /// If true, turns type expressions into valid expressions + /// and calls the describe method on it + /// + public static bool DescribeTypeExpressions; } + + /// + /// A delegate that can be used to invoke the + /// compiled expression or statement. + /// + /// + /// Since the Compile methods will compile + /// statements and expressions into the same + /// delegate, you can tell if a value was returned + /// by checking whether the returned value is of type + /// NoValueSet. + /// + + public delegate void CompiledMethod (ref object retvalue); + /// /// The default base class for every interaction line /// + /// + /// The expressions and statements behave as if they were + /// a static method of this class. The InteractiveBase class + /// contains a number of useful methods, but can be overwritten + /// by setting the InteractiveBaseType property in the Evaluator + /// public class InteractiveBase { + /// + /// Determines where the standard output of methods in this class will go. + /// public static TextWriter Output = Console.Out; + + /// + /// Determines where the standard error of methods in this class will go. + /// public static TextWriter Error = Console.Error; + + /// + /// The primary prompt used for interactive use. + /// public static string Prompt = "csharp> "; + + /// + /// The secondary prompt used for interactive use (used when + /// an expression is incomplete). + /// public static string ContinuationPrompt = " > "; + /// + /// Used to signal that the user has invoked the `quit' statement. + /// + public static bool QuitRequested; + + /// + /// Shows all the variables defined so far. + /// static public void ShowVars () { Output.Write (Evaluator.GetVars ()); Output.Flush (); } + /// + /// Displays the using statements in effect at this point. + /// static public void ShowUsing () { Output.Write (Evaluator.GetUsing ()); @@ -676,6 +983,9 @@ namespace Mono.CSharp { public delegate void Simple (); + /// + /// Times the execution of the given delegate + /// static public TimeSpan Time (Simple a) { DateTime start = DateTime.Now; @@ -684,6 +994,14 @@ namespace Mono.CSharp { } #if !SMCS_SOURCE + /// + /// Loads the assemblies from a package + /// + /// + /// Loads the assemblies from a package. This is equivalent + /// to passing the -pkg: command line flag to the C# compiler + /// on the command line. + /// static public void LoadPackage (string pkg) { if (pkg == null){ @@ -691,7 +1009,7 @@ namespace Mono.CSharp { return; } - string pkgout = Driver.GetPackageFlags (pkg, false); + string pkgout = Driver.GetPackageFlags (pkg, false, RootContext.ToplevelTypes.Compiler.Report); if (pkgout == null) return; @@ -702,21 +1020,34 @@ namespace Mono.CSharp { if (s.StartsWith ("-r:") || s.StartsWith ("/r:") || s.StartsWith ("/reference:")){ string lib = s.Substring (s.IndexOf (':')+1); - Driver.LoadAssembly (lib, true); + Evaluator.LoadAssembly (lib); continue; } } } #endif + /// + /// Loads the assembly + /// + /// + /// Loads the specified assembly and makes its types + /// available to the evaluator. This is equivalent + /// to passing the -pkg: command line flag to the C# + /// compiler on the command line. + /// static public void LoadAssembly (string assembly) { - Driver.LoadAssembly (assembly, true); + Evaluator.LoadAssembly (assembly); } + /// + /// Returns a list of available static methods. + /// static public string help { get { return "Static methods:\n"+ + " Describe(obj) - Describes the object's type\n" + " LoadPackage (pkg); - Loads the given Package (like -pkg:FILE)\n" + " LoadAssembly (ass) - Loads the given assembly (like -r:ASS)\n" + " ShowVars (); - Shows defined local variables.\n" + @@ -729,12 +1060,41 @@ namespace Mono.CSharp { } } + /// + /// Indicates to the read-eval-print-loop that the interaction should be finished. + /// static public object quit { get { - Environment.Exit (0); + QuitRequested = true; return null; } } + +#if !NET_2_1 + /// + /// Describes an object or a type. + /// + /// + /// This method will show a textual representation + /// of the object's type. If the object is a + /// System.Type it renders the type directly, + /// otherwise it renders the type returned by + /// invoking GetType on the object. + /// + static public string Describe (object x) + { + if (x == null) + return ""; + + Type t = x as Type; + if (t == null) + t = x.GetType (); + + StringWriter sw = new StringWriter (); + new Outline (t, sw, true, false, false).OutlineType (); + return sw.ToString (); + } +#endif } // @@ -749,8 +1109,8 @@ namespace Mono.CSharp { TypeContainer container; string name; - public LocalVariableReferenceWithClassSideEffect (TypeContainer container, string name, Block current_block, string local_variable_id, Location loc) - : base (current_block, local_variable_id, loc) + public LocalVariableReferenceWithClassSideEffect (TypeContainer container, string name, Block current_block, string local_variable_id, LocalInfo li, Location loc) + : base (current_block, local_variable_id, loc, li, false) { this.container = container; this.name = name; @@ -773,7 +1133,7 @@ namespace Mono.CSharp { return name.GetHashCode (); } - override public Expression DoResolveLValue (EmitContext ec, Expression right_side) + override public Expression DoResolveLValue (ResolveContext ec, Expression right_side) { Expression ret = base.DoResolveLValue (ec, right_side); if (ret == null) @@ -781,7 +1141,7 @@ namespace Mono.CSharp { Field f = new Field (container, new TypeExpression (ret.Type, Location), Modifiers.PUBLIC | Modifiers.STATIC, - name, null, Location); + new MemberName (name, Location), null); container.AddField (f); if (f.Define ()) Evaluator.QueueField (f); @@ -802,15 +1162,39 @@ namespace Mono.CSharp { { } - public override Expression DoResolve (EmitContext ec) + protected override Expression DoResolve (ResolveContext ec) { CloneContext cc = new CloneContext (); Expression clone = source.Clone (cc); - clone = clone.Resolve (ec); - if (clone == null) - return null; - + // + // A useful feature for the REPL: if we can resolve the expression + // as a type, Describe the type; + // + if (Evaluator.DescribeTypeExpressions){ + var old_printer = Evaluator.SetPrinter (new StreamReportPrinter (TextWriter.Null)); + clone = clone.Resolve (ec); + if (clone == null){ + clone = source.Clone (cc); + clone = clone.Resolve (ec, ResolveFlags.Type); + if (clone == null){ + Evaluator.SetPrinter (old_printer); + clone = source.Clone (cc); + clone = clone.Resolve (ec); + return null; + } + + Arguments args = new Arguments (1); + args.Add (new Argument (new TypeOf (source, Location))); + source = new Invocation (new SimpleName ("Describe", Location), args).Resolve (ec); + } + Evaluator.SetPrinter (old_printer); + } else { + clone = clone.Resolve (ec); + if (clone == null) + return null; + } + // This means its really a statement. if (clone.Type == TypeManager.void_type){ source = source.Resolve (ec); @@ -841,11 +1225,11 @@ namespace Mono.CSharp { } public class Undo { - ArrayList undo_types; + List> undo_types; public Undo () { - undo_types = new ArrayList (); + undo_types = new List> (); } public void AddTypeContainer (TypeContainer current_container, TypeContainer tc) @@ -854,10 +1238,11 @@ namespace Mono.CSharp { Console.Error.WriteLine ("Internal error: inserting container into itself"); return; } - + if (undo_types == null) - undo_types = new ArrayList (); - undo_types.Add (new Pair (current_container, tc)); + undo_types = new List> (); + + undo_types.Add (new KeyValuePair (current_container, tc)); } public void ExecuteUndo () @@ -865,14 +1250,14 @@ namespace Mono.CSharp { if (undo_types == null) return; - foreach (Pair p in undo_types){ - TypeContainer current_container = (TypeContainer) p.First; + foreach (var p in undo_types){ + TypeContainer current_container = p.Key; - current_container.RemoveTypeContainer ((TypeContainer) p.Second); + current_container.RemoveTypeContainer (p.Value); } undo_types = null; } } } - \ No newline at end of file +