Browse Source

Fix await in finally pattern with using statement.

pull/1108/head
Siegfried Pammer 7 years ago
parent
commit
57d59a703e
  1. 16
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/Async.cs
  2. 58
      ICSharpCode.Decompiler/IL/ControlFlow/AwaitInCatchTransform.cs

16
ICSharpCode.Decompiler.Tests/TestCases/Correctness/Async.cs

@ -57,6 +57,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -57,6 +57,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
await AwaitInCatch(Task.FromResult(1), Task.FromResult(2));
await AwaitInFinally(Task.FromResult(2), Task.FromResult(4));
await AwaitInCatchAndFinally(Task.FromResult(3), Task.FromResult(6), Task.FromResult(9));
Console.WriteLine(await AwaitInFinallyInUsing(Task.FromResult<IDisposable>(new StringWriter()), Task.FromResult(6), Task.FromResult(9)));
#endif
}
@ -264,6 +265,21 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -264,6 +265,21 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
}
Console.WriteLine("End Method");
}
public async Task<int> AwaitInFinallyInUsing(Task<IDisposable> task1, Task<int> task2, Task<int> task3)
{
using (await task1) {
Console.WriteLine("Start using");
try {
Console.WriteLine("Before return");
return await task2;
} finally {
Console.WriteLine("Start finally");
await task3;
Console.WriteLine("End finally");
}
}
}
#endif
}
}

58
ICSharpCode.Decompiler/IL/ControlFlow/AwaitInCatchTransform.cs

@ -221,17 +221,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -221,17 +221,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
var tempStore = globalCopyVar.LoadInstructions[0].Parent as StLoc;
if (tempStore == null || !MatchExceptionCaptureBlock(tempStore, out var exitOfFinally, out var afterFinally, out var blocksToRemove))
continue;
if (afterFinally.Instructions.Count < 2)
continue;
int offset = 0;
if (afterFinally.Instructions[0].MatchLdLoc(out var identifierVariable)) {
if (identifierVariable.LoadCount != 1 || identifierVariable.StoreCount != 1)
continue;
offset = 1;
}
if (!afterFinally.Instructions[offset].MatchStLoc(out var globalCopyVarSplitted, out var ldnull) || !ldnull.MatchLdNull())
continue;
if (globalCopyVarSplitted.StoreCount != 1 || globalCopyVarSplitted.LoadCount != 0)
if (!MatchAfterFinallyBlock(ref afterFinally, blocksToRemove, out bool removeFirstInstructionInAfterFinally))
continue;
var cfg = new ControlFlowGraph(container, context.CancellationToken);
var exitOfFinallyNode = cfg.GetNode(exitOfFinally);
@ -269,9 +259,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -269,9 +259,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
var finallyContainer = new BlockContainer();
entryPointOfFinally.Remove();
if (offset == 1)
if (removeFirstInstructionInAfterFinally)
afterFinally.Instructions.RemoveAt(0);
changedContainers.Add(container);
var outer = BlockContainer.FindClosestContainer(container.Parent);
if (outer != null) changedContainers.Add(outer);
finallyContainer.Blocks.Add(entryPointOfFinally);
exitOfFinally.Instructions.RemoveRange(tempStore.ChildIndex, 3);
exitOfFinally.Instructions.Add(new Leave(finallyContainer));
@ -366,5 +358,47 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -366,5 +358,47 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
blocksToRemove.Add(captureBlock);
return true;
}
static bool MatchAfterFinallyBlock(ref Block afterFinally, List<Block> blocksToRemove, out bool removeFirstInstructionInAfterFinally)
{
removeFirstInstructionInAfterFinally = false;
if (afterFinally.Instructions.Count < 2)
return false;
ILVariable globalCopyVarSplitted;
switch (afterFinally.Instructions[0]) {
case IfInstruction ifInst:
if (ifInst.Condition.MatchCompEquals(out var load, out var ldone) && ldone.MatchLdcI4(1) && load.MatchLdLoc(out var variable)) {
if (!ifInst.TrueInst.MatchBranch(out var targetBlock))
return false;
blocksToRemove.Add(afterFinally);
afterFinally = targetBlock;
return true;
} else if (ifInst.Condition.MatchCompNotEquals(out load, out ldone) && ldone.MatchLdcI4(1) && load.MatchLdLoc(out variable)) {
if (!afterFinally.Instructions[1].MatchBranch(out var targetBlock))
return false;
blocksToRemove.Add(afterFinally);
afterFinally = targetBlock;
return true;
}
return false;
case LdLoc ldLoc:
if (ldLoc.Variable.LoadCount != 1 || ldLoc.Variable.StoreCount != 1)
return false;
if (!afterFinally.Instructions[1].MatchStLoc(out globalCopyVarSplitted, out var ldnull) || !ldnull.MatchLdNull())
return false;
removeFirstInstructionInAfterFinally = true;
break;
case StLoc stloc:
globalCopyVarSplitted = stloc.Variable;
if (!stloc.Value.MatchLdNull())
return false;
break;
default:
return false;
}
if (globalCopyVarSplitted.StoreCount != 1 || globalCopyVarSplitted.LoadCount != 0)
return false;
return true;
}
}
}

Loading…
Cancel
Save