Browse Source

Fix ReduceNestingTransform when extracting default block of switch in a try container

pull/1880/head
Chicken-Bones 5 years ago
parent
commit
e0e26a0e77
  1. 64
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ReduceNesting.cs
  2. 17
      ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs

64
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ReduceNesting.cs

@ -399,5 +399,69 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
throw new Exception(); throw new Exception();
} }
} }
public void SwitchInTry()
{
try {
switch (I(0)) {
case 1:
Console.WriteLine(1);
return;
case 2:
Console.WriteLine(2);
return;
}
Console.WriteLine(3);
for (int i = 0; i < 10; i++) {
Console.WriteLine(i);
}
} catch {
throw;
}
}
public void SwitchInTryInLoopReturn()
{
for (int i = 0; i < 10; i++) {
try {
switch (I(0)) {
case 1:
Console.WriteLine(1);
return;
case 2:
Console.WriteLine(2);
return;
}
Console.WriteLine(3);
for (int j = 0; j < 10; j++) {
Console.WriteLine(j);
}
} catch {
throw;
}
}
}
public void SwitchInTryInLoopContinue()
{
for (int i = 0; i < 10; i++) {
try {
switch (I(0)) {
case 1:
Console.WriteLine(1);
continue;
case 2:
Console.WriteLine(2);
continue;
}
Console.WriteLine(3);
for (int j = 0; j < 10; j++) {
Console.WriteLine(j);
}
} catch {
throw;
}
}
}
} }
} }

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

@ -307,6 +307,16 @@ namespace ICSharpCode.Decompiler.IL
context.Step("Extract default case of switch", switchContainer); context.Step("Extract default case of switch", switchContainer);
// if the switch container is followed by an instruction, it must be a Leave from a try/pinned/etc or exitInst
// When it's a leave from a container, it's better to let the extracted default block 'fall through' rather than duplicating whatever
// instruction eventually follows the container
if (parentBlock.Instructions.SecondToLastOrDefault() == switchContainer) {
if (defaultBlock.Instructions.Last().MatchLeave(switchContainer))
defaultBlock.Instructions.Last().ReplaceWith(parentBlock.Instructions.Last());
parentBlock.Instructions.RemoveLast();
}
// replace all break; statements with the exitInst // replace all break; statements with the exitInst
var leaveInstructions = switchContainer.Descendants.Where(inst => inst.MatchLeave(switchContainer)); var leaveInstructions = switchContainer.Descendants.Where(inst => inst.MatchLeave(switchContainer));
foreach (var leaveInst in leaveInstructions.ToArray()) foreach (var leaveInst in leaveInstructions.ToArray())
@ -320,13 +330,6 @@ namespace ICSharpCode.Decompiler.IL
foreach (var block in defaultBlocks) foreach (var block in defaultBlocks)
switchContainer.Blocks.Remove(block); switchContainer.Blocks.Remove(block);
// replace the parent block exit with the default case instructions
if (parentBlock.Instructions.Last() == exitInst) {
parentBlock.Instructions.RemoveLast();
}
// Note: even though we don't check that the switchContainer is near the end of the block,
// we know this must be the case because we know "exitInst" is a leave/branch and directly
// follows the switchContainer.
Debug.Assert(parentBlock.Instructions.Last() == switchContainer); Debug.Assert(parentBlock.Instructions.Last() == switchContainer);
parentBlock.Instructions.AddRange(defaultBlock.Instructions); parentBlock.Instructions.AddRange(defaultBlock.Instructions);

Loading…
Cancel
Save