Merge pull request #686 from LHCGreg/12971-IsRedirectedAfterSet
[mono.git] / mcs / mcs / eval.cs
index 1399ddf882b3bb786639dbc73a416286e0da3439..9607ffd8b7945c0535fe516305dca14b20b2059d 100644 (file)
@@ -72,18 +72,17 @@ namespace Mono.CSharp
                readonly ReflectionImporter importer;
                readonly CompilationSourceFile source_file;
                
-               public Evaluator (CompilerSettings settings, Report report)
+               public Evaluator (CompilerContext ctx)
                {
-                       ctx = new CompilerContext (settings, report);
+                       this.ctx = ctx;
 
                        module = new ModuleContainer (ctx);
                        module.Evaluator = this;
 
-                       source_file = new CompilationSourceFile ("{interactive}", "", 1);
-                       source_file.NamespaceContainer = new NamespaceContainer (null, module, null, source_file);
+                       source_file = new CompilationSourceFile (module, null);
+                       module.AddTypeContainer (source_file);
 
                        startup_files = ctx.SourceFiles.Count;
-                       ctx.SourceFiles.Add (source_file);
 
                        // FIXME: Importer needs this assembly for internalsvisibleto
                        module.SetDeclaringAssembly (new AssemblyDefinitionDynamic (module, "evaluator"));
@@ -117,9 +116,10 @@ namespace Mono.CSharp
 
                        Location.Initialize (ctx.SourceFiles);
 
+                       var parser_session = new ParserSession ();
                        for (int i = 0; i < startup_files; ++i) {
-                               var sf = ctx.Settings.SourceFiles [i];
-                               d.Parse (sf, module);
+                               var sf = ctx.SourceFiles [i];
+                               d.Parse (sf, module, parser_session, ctx.Report);
                        }
                }
 
@@ -131,6 +131,12 @@ namespace Mono.CSharp
                        Location.Initialize (ctx.SourceFiles);
                }
 
+               /// <summary>
+               /// When set evaluator will automatically wait on Task of async methods. When not
+               /// set it's called responsibility to handle Task execution
+               /// </summary>
+               public bool WaitOnTask { get; set; }
+
                /// <summary>
                ///   If true, turns type expressions into valid expressions
                ///   and calls the describe method on it
@@ -226,9 +232,16 @@ namespace Mono.CSharp
 
                                bool partial_input;
                                CSharpParser parser = ParseString (ParseMode.Silent, input, out partial_input);
+
+                               // Terse mode, try to provide the trailing semicolon automatically.
                                if (parser == null && Terse && partial_input){
                                        bool ignore;
-                                       parser = ParseString (ParseMode.Silent, input + ";", out ignore);
+
+                                       // check if the source would compile with a block, if so, we should not
+                                       // add the semicolon.
+                                       var needs_block = ParseString (ParseMode.Silent, input + "{}", out ignore) != null;
+                                       if (!needs_block)
+                                               parser = ParseString (ParseMode.Silent, input + ";", out ignore);
                                }
                                if (parser == null){
                                        compiled = null;
@@ -355,8 +368,6 @@ namespace Mono.CSharp
                                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;
                                }
                                
@@ -372,9 +383,9 @@ namespace Mono.CSharp
                                module.SetDeclaringAssembly (a);
 
                                // Need to setup MemberCache
-                               parser_result.CreateType ();
+                               parser_result.CreateContainer ();
 
-                               var method = parser_result.Methods[0] as Method;
+                               var method = parser_result.Members[0] as Method;
                                BlockContext bc = new BlockContext (method, method.Block, ctx.BuiltinTypes.Void);
 
                                try {
@@ -424,7 +435,7 @@ namespace Mono.CSharp
                                throw new ArgumentException ("Syntax error on input: partial input");
                        
                        if (result_set == false)
-                               throw new ArgumentException ("The expression did not set a result");
+                               throw new ArgumentException ("The expression failed to resolve");
 
                        return result;
                }
@@ -448,8 +459,11 @@ namespace Mono.CSharp
                //
                InputKind ToplevelOrStatement (SeekableStreamReader seekable)
                {
-                       Tokenizer tokenizer = new Tokenizer (seekable, source_file, ctx);
+                       Tokenizer tokenizer = new Tokenizer (seekable, source_file, new ParserSession ());
                        
+                       // Prefer contextual block keywords over identifiers
+                       tokenizer.parsing_block++;
+
                        int t = tokenizer.token ();
                        switch (t){
                        case Token.EOF:
@@ -554,7 +568,6 @@ namespace Mono.CSharp
                {
                        partial_input = false;
                        Reset ();
-                       Tokenizer.LocatedToken.Initialize ();
 
                        var enc = ctx.Settings.Encoding;
                        var s = new MemoryStream (enc.GetBytes (input));
@@ -577,11 +590,12 @@ namespace Mono.CSharp
                        }
                        seekable.Position = 0;
 
-                       source_file.NamespaceContainer.DeclarationFound = false;
-                       CSharpParser parser = new CSharpParser (seekable, source_file);
+                       source_file.DeclarationFound = false;
+                       CSharpParser parser = new CSharpParser (seekable, source_file, new ParserSession ());
 
                        if (kind == InputKind.StatementOrExpression){
                                parser.Lexer.putback_char = Tokenizer.EvalStatementParserCharacter;
+                               parser.Lexer.parsing_block++;
                                ctx.Settings.StatementMode = true;
                        } else {
                                parser.Lexer.putback_char = Tokenizer.EvalCompilationUnitParserCharacter;
@@ -592,7 +606,7 @@ namespace Mono.CSharp
                                parser.Lexer.CompleteOnEOF = true;
 
                        ReportPrinter old_printer = null;
-                       if ((mode == ParseMode.Silent || mode == ParseMode.GetCompletions) && CSharpParser.yacc_verbose_flag == 0)
+                       if ((mode == ParseMode.Silent || mode == ParseMode.GetCompletions))
                                old_printer = ctx.Report.SetPrinter (new StreamReportPrinter (TextWriter.Null));
 
                        try {
@@ -647,18 +661,59 @@ namespace Mono.CSharp
                                        new TypeExpression (base_class_imported, host.Location)
                                };
 
-                               host.AddBasesForPart (host, baseclass_list);
+                               host.SetBaseTypes (baseclass_list);
+
+                               expression_method = (Method) host.Members[0];
+
+                               if ((expression_method.ModFlags & Modifiers.ASYNC) != 0) {
+                                       //
+                                       // Host method is async. When WaitOnTask is set we wrap it with wait
+                                       //
+                                       // void AsyncWait (ref object $retval) {
+                                       //      $retval = Host();
+                                       //      ((Task)$retval).Wait();  // When WaitOnTask is set
+                                       // }
+                                       //
+                                       var p = new ParametersCompiled (
+                                               new Parameter (new TypeExpression (module.Compiler.BuiltinTypes.Object, Location.Null), "$retval", Parameter.Modifier.REF, null, Location.Null)
+                                       );
+
+                                       var method = new Method(host, new TypeExpression(module.Compiler.BuiltinTypes.Void, Location.Null),
+                                               Modifiers.PUBLIC | Modifiers.STATIC, new MemberName("AsyncWait"), p, null);
+
+                                       method.Block = new ToplevelBlock(method.Compiler, p, Location.Null);
+                                       method.Block.AddStatement(new StatementExpression (new SimpleAssign(
+                                               new SimpleName(p [0].Name, Location.Null),
+                                               new Invocation(new SimpleName(expression_method.MemberName.Name, Location.Null), new Arguments(0)),
+                                               Location.Null), Location.Null));
+
+                                       if (WaitOnTask) {
+                                               var task = new Cast (expression_method.TypeExpression, new SimpleName (p [0].Name, Location.Null), Location.Null);
+
+                                               method.Block.AddStatement (new StatementExpression (new Invocation (
+                                                               new MemberAccess (task, "Wait", Location.Null),
+                                                       new Arguments (0)), Location.Null));
+                                       }
+
+                                       host.AddMember(method);
+
+                                       expression_method = method;
+                               }
 
-                               host.CreateType ();
-                               host.DefineType ();
-                               host.Define ();
+                               host.CreateContainer();
+                               host.DefineContainer();
+                               host.Define();
 
-                               expression_method = (Method) host.Methods[0];
                        } else {
                                expression_method = null;
                        }
 
-                       module.CreateType ();
+                       module.CreateContainer ();
+
+                       // Disable module and source file re-definition checks
+                       module.EnableRedefinition ();
+                       source_file.EnableRedefinition ();
+
                        module.Define ();
 
                        if (Report.Errors != 0){
@@ -669,19 +724,20 @@ namespace Mono.CSharp
                        }
 
                        if (host != null){
-                               host.EmitType ();
+                               host.PrepareEmit ();
+                               host.EmitContainer ();
                        }
                        
-                       module.Emit ();
+                       module.EmitContainer ();
                        if (Report.Errors != 0){
                                if (undo != null)
                                        undo.ExecuteUndo ();
                                return null;
                        }
 
-                       module.CloseType ();
+                       module.CloseContainer ();
                        if (host != null)
-                               host.CloseType ();
+                               host.CloseContainer ();
 
                        if (access == AssemblyBuilderAccess.RunAndSave)
                                assembly.Save ();
@@ -694,36 +750,38 @@ namespace Mono.CSharp
                        // work from MethodBuilders.   Retarded, I know.
                        //
                        var tt = assembly.Builder.GetType (host.TypeBuilder.Name);
-                       var mi = tt.GetMethod (expression_method.Name);
-
-                       if (host.Fields != null) {
-                               //
-                               // We need to then go from FieldBuilder to FieldInfo
-                               // or reflection gets confused (it basically gets confused, and variables override each
-                               // other).
-                               //
-                               foreach (Field field in host.Fields) {
-                                       var fi = tt.GetField (field.Name);
-
-                                       Tuple<FieldSpec, FieldInfo> old;
-
-                                       // If a previous value was set, nullify it, so that we do
-                                       // not leak memory
-                                       if (fields.TryGetValue (field.Name, out old)) {
-                                               if (old.Item1.MemberType.IsStruct) {
-                                                       //
-                                                       // TODO: Clear fields for structs
-                                                       //
-                                               } else {
-                                                       try {
-                                                               old.Item2.SetValue (null, null);
-                                                       } catch {
-                                                       }
+                       var mi = tt.GetMethod (expression_method.MemberName.Name);
+
+                       //
+                       // We need to then go from FieldBuilder to FieldInfo
+                       // or reflection gets confused (it basically gets confused, and variables override each
+                       // other).
+                       //
+                       foreach (var member in host.Members) {
+                               var field = member as Field;
+                               if (field == null)
+                                       continue;
+
+                               var fi = tt.GetField (field.Name);
+
+                               Tuple<FieldSpec, FieldInfo> old;
+
+                               // If a previous value was set, nullify it, so that we do
+                               // not leak memory
+                               if (fields.TryGetValue (field.Name, out old)) {
+                                       if (old.Item1.MemberType.IsStruct) {
+                                               //
+                                               // TODO: Clear fields for structs
+                                               //
+                                       } else {
+                                               try {
+                                                       old.Item2.SetValue (null, null);
+                                               } catch {
                                                }
                                        }
-
-                                       fields[field.Name] = Tuple.Create (field.Spec, fi);
                                }
+
+                               fields[field.Name] = Tuple.Create (field.Spec, fi);
                        }
                        
                        return (CompiledMethod) System.Delegate.CreateDelegate (typeof (CompiledMethod), mi);
@@ -760,7 +818,7 @@ namespace Mono.CSharp
                        //foreach (object x in ns.using_alias_list)
                        //    sb.AppendFormat ("using {0};\n", x);
 
-                       foreach (var ue in source_file.NamespaceContainer.Usings) {
+                       foreach (var ue in source_file.Usings) {
                                sb.AppendFormat ("using {0};", ue.ToString ());
                                sb.Append (Environment.NewLine);
                        }
@@ -768,12 +826,17 @@ namespace Mono.CSharp
                        return sb.ToString ();
                }
 
-               internal ICollection<string> GetUsingList ()
+               internal List<string> GetUsingList ()
                {
                        var res = new List<string> ();
 
-                       foreach (var ue in source_file.NamespaceContainer.Usings)
-                               res.Add (ue.Name);
+                       foreach (var ue in source_file.Usings) {
+                               if (ue.Alias != null || ue.ResolvedExpression == null)
+                                       continue;
+
+                               res.Add (ue.NamespaceExpression.Name);
+                       }
+
                        return res;
                }
                
@@ -975,7 +1038,9 @@ namespace Mono.CSharp
                static public string help {
                        get {
                                return "Static methods:\n" +
+#if !NET_2_1
                                        "  Describe (object);       - Describes the object's type\n" +
+#endif
                                        "  LoadPackage (package);   - Loads the given Package (like -pkg:FILE)\n" +
                                        "  LoadAssembly (assembly); - Loads the given assembly (like -r:ASSEMBLY)\n" +
                                        "  ShowVars ();             - Shows defined local variables.\n" +
@@ -1033,14 +1098,31 @@ namespace Mono.CSharp
 #endif
        }
 
-       class HoistedEvaluatorVariable : HoistedVariable
+       class InteractiveMethod : Method
        {
-               public HoistedEvaluatorVariable (Field field)
-                       : base (null, field)
+               public InteractiveMethod(TypeDefinition parent, FullNamedExpression returnType, Modifiers mod, ParametersCompiled parameters)
+                       : base(parent, returnType, mod, new MemberName("Host"), parameters, null)
+               {
+               }
+
+               public void ChangeToAsync ()
+               {
+                       ModFlags |= Modifiers.ASYNC;
+                       ModFlags &= ~Modifiers.UNSAFE;
+                       type_expr = new TypeExpression(Module.PredefinedTypes.Task.TypeSpec, Location);
+                       parameters = ParametersCompiled.EmptyReadOnlyParameters;
+               }
+
+               public override string GetSignatureForError()
                {
+                       return "InteractiveHost";
                }
+       }
 
-               public override void EmitSymbolInfo ()
+       class HoistedEvaluatorVariable : HoistedVariable
+       {
+               public HoistedEvaluatorVariable (Field field)
+                       : base (null, field)
                {
                }
 
@@ -1057,11 +1139,17 @@ namespace Mono.CSharp
        ///    the return value for an invocation.
        /// </summary>
        class OptionalAssign : SimpleAssign {
-               public OptionalAssign (Expression t, Expression s, Location loc)
-                       : base (t, s, loc)
+               public OptionalAssign (Expression s, Location loc)
+                       : base (null, s, loc)
                {
                }
 
+               public override Location StartLocation {
+                       get {
+                               return Location.Null;
+                       }
+               }
+
                protected override Expression DoResolve (ResolveContext ec)
                {
                        Expression clone = source.Clone (new CloneContext ());
@@ -1074,7 +1162,7 @@ namespace Mono.CSharp
                        // A useful feature for the REPL: if we can resolve the expression
                        // as a type, Describe the type;
                        //
-                       if (ec.Module.Evaluator.DescribeTypeExpressions){
+                       if (ec.Module.Evaluator.DescribeTypeExpressions && !(ec.CurrentAnonymousMethod is AsyncInitializer)) {
                                var old_printer = ec.Report.SetPrinter (new SessionReportPrinter ());
                                Expression tclone;
                                try {
@@ -1100,8 +1188,29 @@ namespace Mono.CSharp
                        }
 
                        source = clone;
+
+                       var host = (Method) ec.MemberContext.CurrentMemberDefinition;
+
+                       if (host.ParameterInfo.IsEmpty) {
+                               eclass = ExprClass.Value;
+                               type = InternalType.FakeInternalType;
+                               return this;
+                       }
+
+                       target = new SimpleName (host.ParameterInfo[0].Name, Location);
+
                        return base.DoResolve (ec);
                }
+
+               public override void EmitStatement(EmitContext ec)
+               {
+                       if (target == null) {
+                               source.Emit (ec);
+                               return;
+                       }
+
+                       base.EmitStatement(ec);
+               }
        }
 
        public class Undo
@@ -1112,7 +1221,7 @@ namespace Mono.CSharp
                {
                }
 
-               public void AddTypeContainer (TypeContainer current_container, TypeContainer tc)
+               public void AddTypeContainer (TypeContainer current_container, TypeDefinition tc)
                {
                        if (current_container == tc){
                                Console.Error.WriteLine ("Internal error: inserting container into itself");
@@ -1122,14 +1231,13 @@ namespace Mono.CSharp
                        if (undo_actions == null)
                                undo_actions = new List<Action> ();
 
-                       var existing = current_container.Types.FirstOrDefault (l => l.MemberName.Basename == tc.MemberName.Basename);
+                       var existing = current_container.Containers.FirstOrDefault (l => l.Basename == tc.Basename);
                        if (existing != null) {
-                               current_container.RemoveTypeContainer (existing);
-                               existing.NamespaceEntry.SlaveDeclSpace.RemoveTypeContainer (existing);
+                               current_container.RemoveContainer (existing);
                                undo_actions.Add (() => current_container.AddTypeContainer (existing));
                        }
 
-                       undo_actions.Add (() => current_container.RemoveTypeContainer (tc));
+                       undo_actions.Add (() => current_container.RemoveContainer (tc));
                }
 
                public void ExecuteUndo ()