Browse Source

Fix #1391, #1393, #1458: Fix ReduceNestingTransform modifying the Blocks collection while iterating over it.

pull/1556/head
Daniel Grunwald 6 years ago
parent
commit
7f27768ff9
  1. 32
      ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs

32
ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs

@ -65,8 +65,13 @@ namespace ICSharpCode.Decompiler.IL @@ -65,8 +65,13 @@ namespace ICSharpCode.Decompiler.IL
break;
}
foreach (var block in container.Blocks)
for (int i = 0; i < container.Blocks.Count; i++) {
var block = container.Blocks[i];
// Note: it's possible for additional blocks to be appended to the container
// by the Visit() call; but there should be no other changes to the Blocks collection.
Visit(block, continueTarget);
Debug.Assert(container.Blocks[i] == block);
}
}
/// <summary>
@ -94,10 +99,10 @@ namespace ICSharpCode.Decompiler.IL @@ -94,10 +99,10 @@ namespace ICSharpCode.Decompiler.IL
// reduce nesting in switch blocks
if (container.Kind == ContainerKind.Switch &&
CanDuplicateExit(NextInsn(), continueTarget) &&
ReduceNesting(block, container, NextInsn()))
CanDuplicateExit(NextInsn(), continueTarget) &&
ReduceSwitchNesting(block, container, NextInsn())) {
RemoveRedundantExit(block, nextInstruction);
}
break;
case IfInstruction ifInst:
ImproveILOrdering(block, ifInst);
@ -200,7 +205,7 @@ namespace ICSharpCode.Decompiler.IL @@ -200,7 +205,7 @@ namespace ICSharpCode.Decompiler.IL
/// Reduce Nesting in switch statements by replacing break; in cases with the block exit, and extracting the default case
/// Does not affect IL order
/// </summary>
private bool ReduceNesting(Block parentBlock, BlockContainer switchContainer, ILInstruction exitInst)
private bool ReduceSwitchNesting(Block parentBlock, BlockContainer switchContainer, ILInstruction exitInst)
{
// break; from outer container cannot be brought inside the switch as the meaning would change
if (exitInst is Leave leave && !leave.IsLeavingFunction)
@ -229,7 +234,10 @@ namespace ICSharpCode.Decompiler.IL @@ -229,7 +234,10 @@ namespace ICSharpCode.Decompiler.IL
var defaultTree = TreeTraversal.PreOrder(defaultNode, n => n.DominatorTreeChildren).ToList();
if (defaultTree.SelectMany(n => n.Successors).Any(n => !defaultNode.Dominates(n)))
return false;
if (defaultTree.Count > 1 && !(parentBlock.Parent is BlockContainer))
return false;
EnsureEndPointUnreachable(parentBlock, exitInst);
context.Step("Extract default case of switch", switchContainer);
@ -247,14 +255,18 @@ namespace ICSharpCode.Decompiler.IL @@ -247,14 +255,18 @@ namespace ICSharpCode.Decompiler.IL
switchContainer.Blocks.Remove(block);
// replace the parent block exit with the default case instructions
Debug.Assert(parentBlock.Instructions.Last() == exitInst);
parentBlock.Instructions.RemoveLast();
parentBlock.Instructions.AddRange(defaultBlock.Instructions);
// add any additional blocks from the default case to the parent container
var parentContainer = (BlockContainer)parentBlock.Ancestors.First(p => p is BlockContainer);
int insertAt = parentContainer.Blocks.IndexOf(parentBlock) + 1;
foreach (var block in defaultBlocks.Skip(1))
parentContainer.Blocks.Insert(insertAt++, block);
Debug.Assert(defaultBlocks[0] == defaultBlock);
if (defaultBlocks.Count > 1) {
var parentContainer = (BlockContainer)parentBlock.Parent;
int insertAt = parentContainer.Blocks.IndexOf(parentBlock) + 1;
foreach (var block in defaultBlocks.Skip(1))
parentContainer.Blocks.Insert(insertAt++, block);
}
return true;
}

Loading…
Cancel
Save