mirror of https://github.com/icsharpcode/ILSpy.git
5 changed files with 13 additions and 172 deletions
@ -1,159 +0,0 @@
@@ -1,159 +0,0 @@
|
||||
// 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 { None, While, DoWhile, For } |
||||
|
||||
public class DetectedLoop |
||||
{ |
||||
public BlockContainer Container { get; } |
||||
public LoopKind Kind { get; private set; } |
||||
public ILInstruction[] Conditions { 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
|
||||
public ILVariable IncrementTarget { get; private set; } // null, except in case of For
|
||||
|
||||
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, List<ILInstruction> conditions) |
||||
{ |
||||
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()) { |
||||
conditions.Add(ifInst.Condition); |
||||
i--; |
||||
} |
||||
if (i == -1 && conditions.Any()) { |
||||
return b; |
||||
} |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
private DetectedLoop DetectLoopInternal() |
||||
{ |
||||
if (Container.EntryPoint.IncomingEdgeCount <= 1) { |
||||
Kind = LoopKind.None; |
||||
return this; |
||||
} |
||||
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; |
||||
} |
||||
Conditions = new[] { conditionInst }; |
||||
Body = trueInst; |
||||
if (IncrementBlock != null) { |
||||
// for-loop
|
||||
Kind = LoopKind.For; |
||||
if (IncrementBlock.Instructions[0] is StLoc increment) |
||||
IncrementTarget = increment.Variable; |
||||
AdditionalBlocks = Container.Blocks.Skip(1).Where(b => b != IncrementBlock).ToArray(); |
||||
return this; |
||||
} else if (trueInst is Block block) { |
||||
var last = block.Instructions.LastOrDefault(); |
||||
var secondToLast = block.Instructions.SecondToLastOrDefault(); |
||||
if (last != null && secondToLast != null && last.MatchBranch(Container.EntryPoint) && |
||||
MatchIncrement(secondToLast, out var variable) && conditionInst.Children.Any(c => c.MatchLdLoc(variable))) { |
||||
Kind = LoopKind.For; |
||||
IncrementTarget = variable; |
||||
AdditionalBlocks = Container.Blocks.Skip(1).ToArray(); |
||||
return this; |
||||
} |
||||
} |
||||
AdditionalBlocks = Container.Blocks.Skip(1).ToArray(); |
||||
} else { |
||||
// do-while or while(true)-loop
|
||||
if (Container.EntryPoint.IncomingEdgeCount == 2) { |
||||
var conditions = new List<ILInstruction>(); |
||||
Block conditionBlock = FindDoWhileConditionBlock(Container, conditions); |
||||
if (conditionBlock != null) { |
||||
Kind = LoopKind.DoWhile; |
||||
ContinueJumpTarget = conditionBlock; |
||||
Body = null; |
||||
Conditions = conditions.ToArray(); |
||||
AdditionalBlocks = Container.Blocks.Where(b => b != conditionBlock).ToArray(); |
||||
} |
||||
} |
||||
} |
||||
return this; |
||||
} |
||||
|
||||
static bool MatchIncrement(ILInstruction inst, out ILVariable variable) |
||||
{ |
||||
if (!inst.MatchStLoc(out variable, out var value)) |
||||
return false; |
||||
if (!value.MatchBinaryNumericInstruction(BinaryNumericOperator.Add, out var left, out var right)) { |
||||
if (value is CompoundAssignmentInstruction cai) { |
||||
left = cai.Target; |
||||
} else return false; |
||||
} |
||||
return left.MatchLdLoc(variable); |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue