diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
index d4cbbebbb..b6bb3bf99 100644
--- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
+++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
@@ -414,11 +414,12 @@ namespace ICSharpCode.Decompiler.CSharp
// If there's an extra leave inside the block, extract it into optionalReturnAfterLoop.
var loopContainer = UnwrapNestedContainerIfPossible(container, out var optionalReturnAfterLoop);
// Detect whether we're dealing with a while loop with multiple embedded statements.
- var loop = DetectedLoop.DetectLoop(loopContainer);
- if (loop.Kind != LoopKind.While || !(loop.Body is Block body))
+ if (loopContainer.Kind != ContainerKind.While)
+ return null;
+ if (!loopContainer.MatchConditionBlock(loopContainer.EntryPoint, out var conditionInst, out var body))
return null;
// The loop condition must be a call to enumerator.MoveNext()
- var condition = exprBuilder.TranslateCondition(loop.Conditions.Single());
+ var condition = exprBuilder.TranslateCondition(conditionInst);
var m2 = moveNextConditionPattern.Match(condition.Expression);
if (!m2.Success)
return null;
@@ -427,8 +428,7 @@ namespace ICSharpCode.Decompiler.CSharp
if (enumeratorVar2 != enumeratorVar)
return null;
// Detect which foreach-variable transformation is necessary/possible.
- var transformation = DetectGetCurrentTransformation(container, body, enumeratorVar, condition.ILInstructions.Single(),
- out var singleGetter, out var foreachVariable);
+ var transformation = DetectGetCurrentTransformation(container, body, enumeratorVar, conditionInst, out var singleGetter, out var foreachVariable);
if (transformation == RequiredGetCurrentTransformation.NoForeach)
return null;
// The existing foreach variable, if found, can only be used in the loop container.
@@ -475,7 +475,7 @@ namespace ICSharpCode.Decompiler.CSharp
// Convert the modified body to C# AST:
var whileLoop = (WhileStatement)ConvertAsBlock(container).First();
BlockStatement foreachBody = (BlockStatement)whileLoop.EmbeddedStatement.Detach();
-
+
// Remove the first statement, as it is the foreachVariable = enumerator.Current; statement.
Statement firstStatement = foreachBody.Statements.First();
if (firstStatement is LabelStatement) {
@@ -494,7 +494,7 @@ namespace ICSharpCode.Decompiler.CSharp
};
// Add the variable annotation for highlighting (TokenTextWriter expects it directly on the ForeachStatement).
foreachStmt.AddAnnotation(new ILVariableResolveResult(foreachVariable, foreachVariable.Type));
- foreachStmt.AddAnnotation(new ForeachAnnotation(inst.ResourceExpression, loop.Conditions.Single(), singleGetter));
+ foreachStmt.AddAnnotation(new ForeachAnnotation(inst.ResourceExpression, conditionInst, singleGetter));
// If there was an optional return statement, return it as well.
if (optionalReturnAfterLoop != null) {
return new BlockStatement {
diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
index 272c9c294..bb98e3b53 100644
--- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
+++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
@@ -300,7 +300,6 @@
-
diff --git a/ICSharpCode.Decompiler/IL/DetectedLoop.cs b/ICSharpCode.Decompiler/IL/DetectedLoop.cs
deleted file mode 100644
index 4d234be29..000000000
--- a/ICSharpCode.Decompiler/IL/DetectedLoop.cs
+++ /dev/null
@@ -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;
- }
-
- ///
- /// Gets whether the statement is 'simple' (usable as for loop iterator):
- /// Currently we only accept calls and assignments.
- ///
- 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 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();
- 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);
- }
- }
-}
diff --git a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs
index e7967e44e..bfe03f90f 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs
@@ -156,10 +156,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var loopCounters = new HashSet();
foreach (BlockContainer possibleLoop in function.Descendants.OfType()) {
- if (possibleLoop.EntryPoint.IncomingEdgeCount == 1) continue;
- var loop = DetectedLoop.DetectLoop(possibleLoop);
- if (loop.Kind != LoopKind.For || loop.IncrementTarget == null) continue;
- loopCounters.Add(loop.IncrementTarget);
+ if (possibleLoop.Kind != ContainerKind.For) continue;
+ foreach (var inst in possibleLoop.Blocks.Last().Instructions) {
+ if (HighLevelLoopTransform.MatchIncrement(inst, out var variable))
+ loopCounters.Add(variable);
+ }
}
return loopCounters;
diff --git a/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs
index 26ec3c5f8..0931887b4 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs
@@ -167,7 +167,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true;
}
- static bool MatchIncrement(ILInstruction inst, out ILVariable variable)
+ public static bool MatchIncrement(ILInstruction inst, out ILVariable variable)
{
if (!inst.MatchStLoc(out variable, out var value))
return false;