Browse Source

Fix #1140: Fix assertion when finally block unconditionally throws an exception.

pull/1600/head
Daniel Grunwald 7 years ago
parent
commit
50509c4985
  1. 8
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExceptionHandling.cs
  2. 23
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExceptionHandling.il
  3. 19
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExceptionHandling.opt.il
  4. 19
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExceptionHandling.opt.roslyn.il
  5. 23
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExceptionHandling.roslyn.il
  6. 33
      ICSharpCode.Decompiler/IL/ControlFlow/ExitPoints.cs

8
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExceptionHandling.cs

@ -188,6 +188,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -188,6 +188,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
public void ThrowInFinally()
{
try {
} finally {
throw new Exception();
}
}
}
}

23
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExceptionHandling.il

@ -411,6 +411,29 @@ @@ -411,6 +411,29 @@
IL_0022: ret
} // end of method ExceptionHandling::NoUsingStatementBecauseTheVariableIsAssignedTo
.method public hidebysig instance void
ThrowInFinally() cil managed
{
// Code size 14 (0xe)
.maxstack 1
IL_0000: nop
.try
{
IL_0001: nop
IL_0002: nop
IL_0003: leave.s IL_000c
} // end .try
finally
{
IL_0005: nop
IL_0006: newobj instance void [mscorlib]System.Exception::.ctor()
IL_000b: throw
} // end handler
IL_000c: br.s IL_000c
} // end of method ExceptionHandling::ThrowInFinally
.method family hidebysig specialname rtspecialname
instance void .ctor() cil managed
{

19
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExceptionHandling.opt.il

@ -318,6 +318,25 @@ @@ -318,6 +318,25 @@
IL_0014: ret
} // end of method ExceptionHandling::NoUsingStatementBecauseTheVariableIsAssignedTo
.method public hidebysig instance void
ThrowInFinally() cil managed
{
// Code size 10 (0xa)
.maxstack 1
.try
{
IL_0000: leave.s IL_0008
} // end .try
finally
{
IL_0002: newobj instance void [mscorlib]System.Exception::.ctor()
IL_0007: throw
} // end handler
IL_0008: br.s IL_0008
} // end of method ExceptionHandling::ThrowInFinally
.method family hidebysig specialname rtspecialname
instance void .ctor() cil managed
{

19
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExceptionHandling.opt.roslyn.il

@ -670,6 +670,25 @@ @@ -670,6 +670,25 @@
IL_0014: ret
} // end of method ExceptionHandling::NoUsingStatementBecauseTheVariableIsAssignedTo
.method public hidebysig instance void
ThrowInFinally() cil managed
{
// Code size 10 (0xa)
.maxstack 1
.try
{
IL_0000: leave.s IL_0008
} // end .try
finally
{
IL_0002: newobj instance void [mscorlib]System.Exception::.ctor()
IL_0007: throw
} // end handler
IL_0008: br.s IL_0008
} // end of method ExceptionHandling::ThrowInFinally
.method family hidebysig specialname rtspecialname
instance void .ctor() cil managed
{

23
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExceptionHandling.roslyn.il

@ -831,6 +831,29 @@ @@ -831,6 +831,29 @@
IL_0021: ret
} // end of method ExceptionHandling::NoUsingStatementBecauseTheVariableIsAssignedTo
.method public hidebysig instance void
ThrowInFinally() cil managed
{
// Code size 14 (0xe)
.maxstack 1
IL_0000: nop
.try
{
IL_0001: nop
IL_0002: nop
IL_0003: leave.s IL_000c
} // end .try
finally
{
IL_0005: nop
IL_0006: newobj instance void [mscorlib]System.Exception::.ctor()
IL_000b: throw
} // end handler
IL_000c: br.s IL_000c
} // end of method ExceptionHandling::ThrowInFinally
.method family hidebysig specialname rtspecialname
instance void .ctor() cil managed
{

33
ICSharpCode.Decompiler/IL/ControlFlow/ExitPoints.cs

@ -119,13 +119,32 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -119,13 +119,32 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// </summary>
List<ILInstruction> potentialExits;
readonly List<Block> blocksPotentiallyMadeUnreachable = new List<Block>();
public void Run(ILFunction function, ILTransformContext context)
{
cancellationToken = context.CancellationToken;
currentExit = NoExit;
blocksPotentiallyMadeUnreachable.Clear();
function.AcceptVisitor(this);
// It's possible that there are unreachable code blocks which we only
// detect as such during exit point detection.
// Clean them up.
foreach (var block in blocksPotentiallyMadeUnreachable) {
if (block.IncomingEdgeCount == 0 || block.IncomingEdgeCount == 1 && IsInfiniteLoop(block)) {
block.Remove();
}
}
blocksPotentiallyMadeUnreachable.Clear();
}
static bool IsInfiniteLoop(Block block)
{
return block.Instructions.Count == 1
&& block.Instructions[0] is Branch b
&& b.TargetBlock == block;
}
protected override void Default(ILInstruction inst)
{
foreach (var child in inst.Children)
@ -157,7 +176,17 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -157,7 +176,17 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
while (inst.Parent.OpCode != OpCode.Block)
inst = inst.Parent;
Block block = (Block)inst.Parent;
block.Instructions.Add(currentExit);
if (block.HasFlag(InstructionFlags.EndPointUnreachable)) {
// Special case: despite replacing the exits with leave(currentContainer),
// we still have an unreachable endpoint.
// The appended currentExit instruction would not be reachable!
// This happens in test case ExceptionHandling.ThrowInFinally()
if (currentExit is Branch b) {
blocksPotentiallyMadeUnreachable.Add(b.TargetBlock);
}
} else {
block.Instructions.Add(currentExit);
}
} else {
Debug.Assert(thisExit == currentExit);
}

Loading…
Cancel
Save