mirror of https://github.com/icsharpcode/ILSpy.git
3 changed files with 180 additions and 101 deletions
@ -0,0 +1,133 @@ |
|||||||
|
// Copyright (c) 2017 Siegfried Pammer
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Linq; |
||||||
|
using ICSharpCode.Decompiler.Util; |
||||||
|
|
||||||
|
namespace ICSharpCode.Decompiler.IL |
||||||
|
{ |
||||||
|
public enum LoopKind { While, DoWhile, For } |
||||||
|
|
||||||
|
public class DetectedLoop |
||||||
|
{ |
||||||
|
public BlockContainer Container { get; } |
||||||
|
public LoopKind Kind { get; private set; } |
||||||
|
public ILInstruction Condition { get; private set; } |
||||||
|
public Block IncrementBlock { get; private set; } |
||||||
|
public Block ContinueJumpTarget { get; private set; } // jumps to this block are "continue;" jumps
|
||||||
|
public ILInstruction Body { get; private set; } // null in case of DoWhile
|
||||||
|
public Block[] AdditionalBlocks { get; private set; } // blocks to be merged into the loop body
|
||||||
|
|
||||||
|
private DetectedLoop(BlockContainer container) |
||||||
|
{ |
||||||
|
this.Container = container; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets whether the statement is 'simple' (usable as for loop iterator):
|
||||||
|
/// Currently we only accept calls and assignments.
|
||||||
|
/// </summary>
|
||||||
|
private static bool IsSimpleStatement(ILInstruction inst) |
||||||
|
{ |
||||||
|
switch (inst.OpCode) { |
||||||
|
case OpCode.Call: |
||||||
|
case OpCode.CallVirt: |
||||||
|
case OpCode.NewObj: |
||||||
|
case OpCode.StLoc: |
||||||
|
case OpCode.StObj: |
||||||
|
case OpCode.CompoundAssignmentInstruction: |
||||||
|
return true; |
||||||
|
default: |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static DetectedLoop DetectLoop(BlockContainer container) |
||||||
|
{ |
||||||
|
return new DetectedLoop(container).DetectLoopInternal(); |
||||||
|
} |
||||||
|
|
||||||
|
private static Block FindDoWhileConditionBlock(BlockContainer container, out ILInstruction condition) |
||||||
|
{ |
||||||
|
condition = null; |
||||||
|
foreach (var b in container.Blocks) { |
||||||
|
if (b.Instructions.Last().MatchBranch(container.EntryPoint)) { |
||||||
|
// potentially the do-while-condition block
|
||||||
|
int i = b.Instructions.Count - 2; |
||||||
|
while (i >= 0 && b.Instructions[i] is IfInstruction ifInst |
||||||
|
&& ifInst.TrueInst.MatchLeave(container) && ifInst.FalseInst.MatchNop()) { |
||||||
|
if (condition == null) |
||||||
|
condition = new LogicNot(ifInst.Condition); |
||||||
|
else |
||||||
|
condition = IfInstruction.LogicAnd(new LogicNot(ifInst.Condition), condition); |
||||||
|
i--; |
||||||
|
} |
||||||
|
if (i == -1) { |
||||||
|
return b; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
private DetectedLoop DetectLoopInternal() |
||||||
|
{ |
||||||
|
Kind = LoopKind.While; |
||||||
|
ContinueJumpTarget = Container.EntryPoint; |
||||||
|
if (Container.EntryPoint.Instructions.Count == 2 |
||||||
|
&& Container.EntryPoint.Instructions[0].MatchIfInstruction(out var conditionInst, out var trueInst) |
||||||
|
&& Container.EntryPoint.Instructions[1].MatchLeave(Container)) { |
||||||
|
// detected while(condition)-loop or for-loop
|
||||||
|
// we have to check if there's an increment block before converting the loop body using ConvertAsBlock(trueInst)
|
||||||
|
// and set the continueJumpTarget to correctly convert 'br incrementBlock' instructions to continue;
|
||||||
|
IncrementBlock = null; |
||||||
|
if (Container.EntryPoint.IncomingEdgeCount == 2) { |
||||||
|
IncrementBlock = Container.Blocks.SingleOrDefault( |
||||||
|
b => b.Instructions.Last().MatchBranch(Container.EntryPoint) |
||||||
|
&& b.Instructions.SkipLast(1).All(IsSimpleStatement)); |
||||||
|
if (IncrementBlock != null) |
||||||
|
ContinueJumpTarget = IncrementBlock; |
||||||
|
} |
||||||
|
Condition = conditionInst; |
||||||
|
Body = trueInst; |
||||||
|
if (IncrementBlock != null) { |
||||||
|
// for-loop
|
||||||
|
Kind = LoopKind.For; |
||||||
|
AdditionalBlocks = Container.Blocks.Skip(1).Where(b => b != IncrementBlock).ToArray(); |
||||||
|
} else { |
||||||
|
AdditionalBlocks = Container.Blocks.Skip(1).ToArray(); |
||||||
|
} |
||||||
|
} else { |
||||||
|
// do-while or while(true)-loop
|
||||||
|
if (Container.EntryPoint.IncomingEdgeCount == 2) { |
||||||
|
Block conditionBlock = FindDoWhileConditionBlock(Container, out var conditionInst2); |
||||||
|
if (conditionBlock != null) { |
||||||
|
Kind = LoopKind.DoWhile; |
||||||
|
ContinueJumpTarget = conditionBlock; |
||||||
|
Body = null; |
||||||
|
Condition = conditionInst2; |
||||||
|
AdditionalBlocks = Container.Blocks.Where(b => b != conditionBlock).ToArray(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue