[mcs] Fix local exits from try-finally scope with awaited statements
authorMarek Safar <marek.safar@gmail.com>
Thu, 26 Jun 2014 14:22:55 +0000 (16:22 +0200)
committerMarek Safar <marek.safar@gmail.com>
Thu, 26 Jun 2014 15:20:29 +0000 (17:20 +0200)
mcs/mcs/async.cs
mcs/mcs/codegen.cs
mcs/mcs/statement.cs
mcs/tests/test-async-72.cs [new file with mode: 0644]
mcs/tests/ver-il-net_4_5.xml

index 516c9666b16aec97bc9622ed666ba716cb36f2b7..c11216d9bae5a435698471e5861927c7862c4b73 100644 (file)
@@ -567,7 +567,7 @@ namespace Mono.CSharp
                        return field;
                }
 
-               public Field AddCapturedLocalVariable (TypeSpec type)
+               public Field AddCapturedLocalVariable (TypeSpec type, bool requiresUninitialized = false)
                {
                        if (mutator != null)
                                type = mutator.Mutate (type);
@@ -575,7 +575,7 @@ namespace Mono.CSharp
                        List<Field> existing_fields = null;
                        if (stack_fields == null) {
                                stack_fields = new Dictionary<TypeSpec, List<Field>> ();
-                       } else if (stack_fields.TryGetValue (type, out existing_fields)) {
+                       } else if (stack_fields.TryGetValue (type, out existing_fields) && !requiresUninitialized) {
                                foreach (var f in existing_fields) {
                                        if (f.IsAvailableForReuse) {
                                                f.IsAvailableForReuse = false;
index a5260e1f869283402e514fcfd286adea8bf556ad..cc6e875b0bc3c75ac42b9affa34f2d4b5f5fdf07 100644 (file)
@@ -390,9 +390,9 @@ namespace Mono.CSharp
                //
                // Creates temporary field in current async storey
                //
-               public StackFieldExpr GetTemporaryField (TypeSpec type)
+               public StackFieldExpr GetTemporaryField (TypeSpec type, bool initializedFieldRequired = false)
                {
-                       var f = AsyncTaskStorey.AddCapturedLocalVariable (type);
+                       var f = AsyncTaskStorey.AddCapturedLocalVariable (type, initializedFieldRequired);
                        var fexpr = new StackFieldExpr (f);
                        fexpr.InstanceExpression = new CompilerGeneratedThis (CurrentType, Location.Null);
                        return fexpr;
index 2239bfde58236d517ac8ac61bbaca7a459ddb211..870310939cc309cb9cfac5639817dc24d4376e2d 100644 (file)
@@ -1879,7 +1879,14 @@ namespace Mono.CSharp {
 
                protected override void DoEmit (EmitContext ec)
                {
-                       ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
+                       var l = ec.LoopEnd;
+
+                       if (ec.TryFinallyUnwind != null) {
+                               var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
+                               l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block);
+                       }
+
+                       ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
                }
 
                protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
@@ -1920,7 +1927,14 @@ namespace Mono.CSharp {
 
                protected override void DoEmit (EmitContext ec)
                {
-                       ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
+                       var l = ec.LoopBegin;
+
+                       if (ec.TryFinallyUnwind != null) {
+                               var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
+                               l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block);
+                       }
+
+                       ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
                }
 
                protected override bool DoResolve (BlockContext bc)
@@ -6740,9 +6754,21 @@ namespace Mono.CSharp {
 
                public static Label EmitRedirectedJump (EmitContext ec, AsyncInitializer initializer, Label label, Block labelBlock)
                {
+                       int idx;
+                       if (labelBlock != null) {
+                               for (idx = ec.TryFinallyUnwind.Count; idx != 0; --idx) {
+                                       var fin = ec.TryFinallyUnwind [idx - 1];
+                                       if (!fin.IsParentBlock (labelBlock))
+                                               break;
+                               }
+                       } else {
+                               idx = 0;
+                       }
+
                        bool set_return_state = true;
 
-                       foreach (var fin in ec.TryFinallyUnwind) {
+                       for (; idx < ec.TryFinallyUnwind.Count; ++idx) {
+                               var fin = ec.TryFinallyUnwind [idx];
                                if (labelBlock != null && !fin.IsParentBlock (labelBlock))
                                        break;
 
@@ -6771,8 +6797,9 @@ namespace Mono.CSharp {
 
                                // Add fallthrough label
                                redirected_jumps.Add (ec.DefineLabel ());
+
                                if (setReturnState)
-                                       initializer.HoistedReturnState = ec.GetTemporaryField (ec.Module.Compiler.BuiltinTypes.Int);
+                                       initializer.HoistedReturnState = ec.GetTemporaryField (ec.Module.Compiler.BuiltinTypes.Int, true);
                        }
 
                        int index = redirected_jumps.IndexOf (label);
@@ -6782,7 +6809,7 @@ namespace Mono.CSharp {
                        }
 
                        //
-                       // Indicates we have captured return value
+                       // Indicates we have captured exit jump
                        //
                        if (setReturnState) {
                                var value = new IntConstant (initializer.HoistedReturnState.Type, index, Location.Null);
@@ -6981,8 +7008,13 @@ namespace Mono.CSharp {
                                c.Emit (ec);
 
                                if (catch_sm != null) {
-                                       if (state_variable == null)
-                                               state_variable = ec.GetTemporaryLocal (ec.Module.Compiler.BuiltinTypes.Int);
+                                       if (state_variable == null) {
+                                               //
+                                               // Cannot reuse temp variable because non-catch path assumes the value is 0
+                                               // which may not be true for reused local variable
+                                               //
+                                               state_variable = ec.DeclareLocal (ec.Module.Compiler.BuiltinTypes.Int, false);
+                                       }
 
                                        var index = catch_sm.IndexOf (c);
                                        if (index < 0)
diff --git a/mcs/tests/test-async-72.cs b/mcs/tests/test-async-72.cs
new file mode 100644 (file)
index 0000000..3621863
--- /dev/null
@@ -0,0 +1,88 @@
+using System;
+using System.Threading.Tasks;
+
+class Test
+{
+       static async Task<int> YieldValue (int a)
+       {
+               await Task.Yield ();
+               return a;
+       }
+
+       public static async Task<int> BreakTest ()
+       {
+               int value = 0;
+               try {
+                       for (int i = 0; i < 8; ++i) {
+                               try {
+                                       try {
+                                               value += await YieldValue (1);
+
+                                               Console.WriteLine ("i = " + i);
+
+                                               if (i > 2)
+                                                       break;
+
+                                               if (i > 1)
+                                                       throw new ApplicationException ();
+                                       } catch (ApplicationException) {
+                                               Console.WriteLine ("catch");
+                                               value += await YieldValue (100);
+                                       }
+                               } finally {
+                                       Console.WriteLine ("F1");
+                                       value += await YieldValue (10);
+                               }
+                       }
+               } finally {
+                       Console.WriteLine ("F2");
+                       value += await YieldValue (1000);
+               }
+
+               return value;
+       }
+
+       public static async Task<int> ContinueTest ()
+       {
+               int value = 0;
+               try {
+                       for (int i = 0; i < 8; ++i) {
+                               try {
+                                       try {
+                                               value += await YieldValue (1);
+
+                                               Console.WriteLine ("i = " + i);
+
+                                               if (i < 2)
+                                                       continue;
+
+                                               if (i > 1)
+                                                       throw new ApplicationException ();
+                                       } catch (ApplicationException) {
+                                               Console.WriteLine ("catch");
+                                               value += await YieldValue (100);
+                                       }
+                               } finally {
+                                       Console.WriteLine ("F1");
+                                       value += await YieldValue (10);
+                               }
+                       }
+               } finally {
+                       Console.WriteLine ("F2");
+                       value += await YieldValue (1000);
+               }
+
+               return value;
+       }
+
+       public static int Main ()
+       {
+               if (BreakTest ().Result != 1144)
+                       return 1;
+
+               if (ContinueTest ().Result != 1688)
+                       return 1;
+
+               return 0;
+       }
+}
\ No newline at end of file
index f572bea996884a39f8f5dc350817b988d7974563..902a9be637d1e1334d23f629ea2cae333b94da70 100644 (file)
       </method>\r
     </type>\r
   </test>\r
+  <test name="test-async-72.cs">\r
+    <type name="Test">\r
+      <method name="System.Threading.Tasks.Task`1[System.Int32] YieldValue(Int32)" attrs="145">\r
+        <size>41</size>\r
+      </method>\r
+      <method name="System.Threading.Tasks.Task`1[System.Int32] BreakTest()" attrs="150">\r
+        <size>33</size>\r
+      </method>\r
+      <method name="System.Threading.Tasks.Task`1[System.Int32] ContinueTest()" attrs="150">\r
+        <size>33</size>\r
+      </method>\r
+      <method name="Int32 Main()" attrs="150">\r
+        <size>64</size>\r
+      </method>\r
+      <method name="Void .ctor()" attrs="6278">\r
+        <size>7</size>\r
+      </method>\r
+    </type>\r
+    <type name="Test+&lt;YieldValue&gt;c__async0">\r
+      <method name="Void MoveNext()" attrs="486">\r
+        <size>172</size>\r
+      </method>\r
+      <method name="Void SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine)" attrs="486">\r
+        <size>13</size>\r
+      </method>\r
+    </type>\r
+    <type name="Test+&lt;BreakTest&gt;c__async1">\r
+      <method name="Void MoveNext()" attrs="486">\r
+        <size>898</size>\r
+      </method>\r
+      <method name="Void SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine)" attrs="486">\r
+        <size>13</size>\r
+      </method>\r
+    </type>\r
+    <type name="Test+&lt;ContinueTest&gt;c__async2">\r
+      <method name="Void MoveNext()" attrs="486">\r
+        <size>898</size>\r
+      </method>\r
+      <method name="Void SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine)" attrs="486">\r
+        <size>13</size>\r
+      </method>\r
+    </type>\r
+  </test>\r
   <test name="test-cls-00.cs">\r
     <type name="CLSCLass_6">\r
       <method name="Void add_Disposed(Delegate)" attrs="2182">\r