return field;
}
- public Field AddCapturedLocalVariable (TypeSpec type)
+ public Field AddCapturedLocalVariable (TypeSpec type, bool requiresUninitialized = false)
{
if (mutator != null)
type = mutator.Mutate (type);
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;
//
// 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;
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)
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)
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;
// 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);
}
//
- // Indicates we have captured return value
+ // Indicates we have captured exit jump
//
if (setReturnState) {
var value = new IntConstant (initializer.HoistedReturnState.Type, index, Location.Null);
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)
--- /dev/null
+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
</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+<YieldValue>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+<BreakTest>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+<ContinueTest>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