Browse Source

#2070: Allow `PickExitPoint` heuristic to choose a `return;`/`yield break;` that is part of a condition block.

pull/2077/head
Daniel Grunwald 5 years ago
parent
commit
c647016bfb
  1. 13
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.cs
  2. 22
      ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs

13
ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.cs

@ -356,5 +356,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -356,5 +356,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
Console.WriteLine("normal exit");
}
internal IEnumerable<int> ForLoopWithYieldReturn(int end, int evil)
{
// This loop needs to pick the implicit "yield break;" as exit point
// in order to produce pretty code; not the "throw" which would
// be a less-pretty option.
for (int i = 0; i < end; i++) {
if (i == evil) {
throw new InvalidOperationException("Found evil number");
}
yield return i;
}
}
}
}

22
ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs

@ -315,6 +315,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -315,6 +315,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
ControlFlowNode exitPoint = null;
int exitPointILOffset = -1;
ConsiderReturnAsExitPoint((Block)loopHead.UserData, ref exitPoint, ref exitPointILOffset);
foreach (var node in loopHead.DominatorTreeChildren) {
PickExitPoint(node, ref exitPoint, ref exitPointILOffset);
}
@ -457,12 +458,31 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -457,12 +458,31 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
exitPoint = node;
exitPointILOffset = block.StartILOffset;
return; // don't visit children, they are likely to have even later IL offsets and we'd end up
// moving almost all of the code into the loop.
// moving almost all of the code into the loop.
}
ConsiderReturnAsExitPoint(block, ref exitPoint, ref exitPointILOffset);
foreach (var child in node.DominatorTreeChildren) {
PickExitPoint(child, ref exitPoint, ref exitPointILOffset);
}
}
private static void ConsiderReturnAsExitPoint(Block block, ref ControlFlowNode exitPoint, ref int exitPointILOffset)
{
// It's possible that the real exit point of the loop is a "return;" that has been combined (by ControlFlowSimplification)
// with the condition block.
if (!block.MatchIfAtEndOfBlock(out _, out var trueInst, out var falseInst))
return;
if (trueInst.StartILOffset > exitPointILOffset && trueInst is Leave { IsLeavingFunction: true, Value: Nop _ }) {
// By using NoExitPoint, everything (including the "return;") becomes part of the loop body
// Then DetectExitPoint will move the "return;" out of the loop body.
exitPoint = NoExitPoint;
exitPointILOffset = trueInst.StartILOffset;
}
if (falseInst.StartILOffset > exitPointILOffset && falseInst is Leave { IsLeavingFunction: true, Value: Nop _ }) {
exitPoint = NoExitPoint;
exitPointILOffset = falseInst.StartILOffset;
}
}
/// <summary>
/// Constructs a new control flow graph.

Loading…
Cancel
Save