|
|
|
@ -78,6 +78,26 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow |
|
|
|
// usually an IfInstruction with a branch:
|
|
|
|
// usually an IfInstruction with a branch:
|
|
|
|
IfInstruction ifInst = block.Instructions.SecondToLastOrDefault() as IfInstruction; |
|
|
|
IfInstruction ifInst = block.Instructions.SecondToLastOrDefault() as IfInstruction; |
|
|
|
if (ifInst != null && ifInst.FalseInst.OpCode == OpCode.Nop) { |
|
|
|
if (ifInst != null && ifInst.FalseInst.OpCode == OpCode.Nop) { |
|
|
|
|
|
|
|
HandleIfInstruction(cfgNode, block, ifInst, ref exitInst); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
SwitchInstruction switchInst = block.Instructions.SecondToLastOrDefault() as SwitchInstruction; |
|
|
|
|
|
|
|
if (switchInst != null) { |
|
|
|
|
|
|
|
HandleSwitchInstruction(cfgNode, block, switchInst, ref exitInst); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (IsUsableBranchToChild(cfgNode, exitInst)) { |
|
|
|
|
|
|
|
// "...; goto usableblock;"
|
|
|
|
|
|
|
|
// -> embed target block in this block
|
|
|
|
|
|
|
|
var targetBlock = ((Branch)exitInst).TargetBlock; |
|
|
|
|
|
|
|
Debug.Assert(exitInst == block.Instructions.Last()); |
|
|
|
|
|
|
|
block.Instructions.RemoveAt(block.Instructions.Count - 1); |
|
|
|
|
|
|
|
block.Instructions.AddRange(targetBlock.Instructions); |
|
|
|
|
|
|
|
targetBlock.Instructions.Clear(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void HandleIfInstruction(ControlFlowNode cfgNode, Block block, IfInstruction ifInst, ref ILInstruction exitInst) |
|
|
|
|
|
|
|
{ |
|
|
|
if (IsBranchToLaterTarget(ifInst.TrueInst, exitInst)) { |
|
|
|
if (IsBranchToLaterTarget(ifInst.TrueInst, exitInst)) { |
|
|
|
// "if (c) goto lateBlock; goto earlierBlock;"
|
|
|
|
// "if (c) goto lateBlock; goto earlierBlock;"
|
|
|
|
// -> "if (!c)" goto earlierBlock; goto lateBlock;
|
|
|
|
// -> "if (!c)" goto earlierBlock; goto lateBlock;
|
|
|
|
@ -125,8 +145,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (ifInst.FalseInst.OpCode != OpCode.Nop && ifInst.FalseInst.ILRange.Start < ifInst.TrueInst.ILRange.Start |
|
|
|
if (ifInst.FalseInst.OpCode != OpCode.Nop && ifInst.FalseInst.ILRange.Start < ifInst.TrueInst.ILRange.Start |
|
|
|
|| ifInst.TrueInst.OpCode == OpCode.Nop) |
|
|
|
|| ifInst.TrueInst.OpCode == OpCode.Nop) { |
|
|
|
{ |
|
|
|
|
|
|
|
// swap true and false branches of if, to bring them in the same order as the IL code
|
|
|
|
// swap true and false branches of if, to bring them in the same order as the IL code
|
|
|
|
var oldTrue = ifInst.TrueInst; |
|
|
|
var oldTrue = ifInst.TrueInst; |
|
|
|
ifInst.TrueInst = ifInst.FalseInst; |
|
|
|
ifInst.TrueInst = ifInst.FalseInst; |
|
|
|
@ -134,16 +153,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow |
|
|
|
ifInst.Condition = new LogicNot(ifInst.Condition); |
|
|
|
ifInst.Condition = new LogicNot(ifInst.Condition); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (IsUsableBranchToChild(cfgNode, exitInst)) { |
|
|
|
|
|
|
|
// "...; goto usableblock;"
|
|
|
|
|
|
|
|
// -> embed target block in this block
|
|
|
|
|
|
|
|
var targetBlock = ((Branch)exitInst).TargetBlock; |
|
|
|
|
|
|
|
Debug.Assert(exitInst == block.Instructions.Last()); |
|
|
|
|
|
|
|
block.Instructions.RemoveAt(block.Instructions.Count - 1); |
|
|
|
|
|
|
|
block.Instructions.AddRange(targetBlock.Instructions); |
|
|
|
|
|
|
|
targetBlock.Instructions.Clear(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool IsBranchToLaterTarget(ILInstruction inst1, ILInstruction inst2) |
|
|
|
bool IsBranchToLaterTarget(ILInstruction inst1, ILInstruction inst2) |
|
|
|
{ |
|
|
|
{ |
|
|
|
@ -185,5 +194,38 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void HandleSwitchInstruction(ControlFlowNode cfgNode, Block block, SwitchInstruction sw, ref ILInstruction exitInst) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Debug.Assert(sw.DefaultBody is Nop); |
|
|
|
|
|
|
|
foreach (var section in sw.Sections) { |
|
|
|
|
|
|
|
if (IsUsableBranchToChild(cfgNode, section.Body)) { |
|
|
|
|
|
|
|
// case ...: goto targetBlock;
|
|
|
|
|
|
|
|
var targetBlock = ((Branch)section.Body).TargetBlock; |
|
|
|
|
|
|
|
section.Body = targetBlock; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (IsUsableBranchToChild(cfgNode, exitInst)) { |
|
|
|
|
|
|
|
// switch(...){} goto targetBlock;
|
|
|
|
|
|
|
|
// ---> switch(..) { default: { targetBlock } }
|
|
|
|
|
|
|
|
var targetBlock = ((Branch)exitInst).TargetBlock; |
|
|
|
|
|
|
|
sw.DefaultBody = targetBlock; |
|
|
|
|
|
|
|
if (targetBlock.Instructions.Last().OpCode == OpCode.Branch || targetBlock.Instructions.Last().OpCode == OpCode.Leave) { |
|
|
|
|
|
|
|
exitInst = block.Instructions[block.Instructions.Count - 1] = targetBlock.Instructions.Last(); |
|
|
|
|
|
|
|
targetBlock.Instructions.RemoveAt(targetBlock.Instructions.Count - 1); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
exitInst = null; |
|
|
|
|
|
|
|
block.Instructions.RemoveAt(block.Instructions.Count - 1); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Remove compatible exitInsts from switch sections:
|
|
|
|
|
|
|
|
foreach (var section in sw.Sections) { |
|
|
|
|
|
|
|
Block sectionBlock = section.Body as Block; |
|
|
|
|
|
|
|
if (sectionBlock != null && CompatibleExitInstruction(exitInst, sectionBlock.Instructions.Last())) { |
|
|
|
|
|
|
|
sectionBlock.Instructions.RemoveAt(sectionBlock.Instructions.Count - 1); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
sw.Sections.ReplaceList(sw.Sections.OrderBy(s => s.Body.ILRange.Start)); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|