Browse Source

Fix #1946: Don't move `return` into `try` block when doing so changes the semantics of a `finally` block.

pull/1994/head
Daniel Grunwald 5 years ago
parent
commit
c1265ad830
  1. 14
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/ControlFlow.cs
  2. 17
      ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs

14
ICSharpCode.Decompiler.Tests/TestCases/Correctness/ControlFlow.cs

@ -41,6 +41,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -41,6 +41,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
BreakUnlessContinue(true);
BreakUnlessContinue(false);
TestConditionals();
Console.WriteLine("Issue1946:\n" + Issue1946());
return 0;
}
@ -149,5 +150,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -149,5 +150,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
byte answer = (byte)(value == 128 ? 255 : 0);
return answer;
}
static string Issue1946()
{
string obj = "1";
try {
obj = "2";
} catch {
obj = "3";
} finally {
obj = "4";
}
return obj;
}
}
}

17
ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs

@ -197,9 +197,22 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -197,9 +197,22 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
bool IsBranchToReturnBlock(Branch branch)
{
var targetBlock = branch.TargetBlock;
if (targetBlock.Instructions.Count != 1 || targetBlock.FinalInstruction.OpCode != OpCode.Nop)
if (targetBlock.Instructions.Count != 1)
return false;
return targetBlock.Instructions[0].MatchReturn(out var value) && value is LdLoc;
if (!targetBlock.Instructions[0].MatchReturn(out var value))
return false;
if (!targetBlock.MatchLdLoc(out var returnVar))
return false;
var container = branch.TargetContainer;
for (ILInstruction inst = branch; inst != container; inst = inst.Parent) {
if (inst.Parent is TryFinally tryFinally && inst.SlotInfo == TryFinally.TryBlockSlot) {
// The branch will trigger the finally block.
// Moving the return block into the try is only possible if the finally block doesn't touch the return variable.
if (returnVar.IsUsedWithin(tryFinally.FinallyBlock))
return false;
}
}
return true;
}
static bool CombineBlockWithNextBlock(BlockContainer container, Block block, ILTransformContext context)

Loading…
Cancel
Save