diff --git a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
index e8c619143..cd5a7eded 100644
--- a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
+++ b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
@@ -47,6 +47,9 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
result = TransformUsings(expressionStatement);
if (result != null)
return result;
+ result = TransformNonGenericForEach(expressionStatement);
+ if (result != null)
+ return result;
}
result = TransformFor(expressionStatement);
if (result != null)
@@ -118,7 +121,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
#endregion
///
- /// $type $variable = $initializer;
+ /// $variable = $initializer;
///
static readonly AstNode variableAssignPattern = new ExpressionStatement(
new AssignmentExpression(
@@ -235,8 +238,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
}
#endregion
- #region foreach
- static readonly UsingStatement foreachPattern = new UsingStatement {
+ #region foreach (generic)
+ static readonly UsingStatement genericForeachPattern = new UsingStatement {
ResourceAcquisition = new VariableDeclarationStatement {
Type = new AnyNode("enumeratorType"),
Variables = {
@@ -270,7 +273,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
public ForeachStatement TransformForeach(UsingStatement node)
{
- Match m = foreachPattern.Match(node);
+ Match m = genericForeachPattern.Match(node);
if (!m.Success)
return null;
if (!(node.Parent is BlockStatement) && m.Has("variablesOutsideLoop")) {
@@ -308,8 +311,9 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
InExpression = m.Get("collection").Single().Detach(),
EmbeddedStatement = newBody
};
- if (foreachStatement.InExpression is BaseReferenceExpression)
- foreachStatement.InExpression = new ThisReferenceExpression();
+ if (foreachStatement.InExpression is BaseReferenceExpression) {
+ foreachStatement.InExpression = new ThisReferenceExpression().CopyAnnotationsFrom(foreachStatement.InExpression);
+ }
node.ReplaceWith(foreachStatement);
foreach (Statement stmt in m.Get("variablesOutsideLoop")) {
((BlockStatement)foreachStatement.Parent).Statements.InsertAfter(null, stmt.Detach());
@@ -318,6 +322,117 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
}
#endregion
+ #region foreach (non-generic)
+ ExpressionStatement getEnumeratorPattern = new ExpressionStatement(
+ new AssignmentExpression(
+ new NamedNode("left", new IdentifierExpression()),
+ new AnyNode("collection").ToExpression().Invoke("GetEnumerator")
+ ));
+
+ TryCatchStatement nonGenericForeachPattern = new TryCatchStatement {
+ TryBlock = new BlockStatement {
+ new WhileStatement {
+ Condition = new IdentifierExpression().WithName("enumerator").Invoke("MoveNext"),
+ EmbeddedStatement = new BlockStatement {
+ new AssignmentExpression(
+ new IdentifierExpression().WithName("itemVar"),
+ new Choice {
+ new Backreference("enumerator").ToExpression().Member("Current"),
+ new CastExpression {
+ Type = new AnyNode("castType"),
+ Expression = new Backreference("enumerator").ToExpression().Member("Current")
+ }
+ }
+ ),
+ new Repeat(new AnyNode("stmt")).ToStatement()
+ }
+ }.WithName("loop")
+ },
+ FinallyBlock = new BlockStatement {
+ new AssignmentExpression(
+ new IdentifierExpression().WithName("disposable"),
+ new Backreference("enumerator").ToExpression().CastAs(new TypePattern(typeof(IDisposable)))
+ ),
+ new IfElseStatement {
+ Condition = new BinaryOperatorExpression {
+ Left = new Backreference("disposable"),
+ Operator = BinaryOperatorType.InEquality,
+ Right = new NullReferenceExpression()
+ },
+ TrueStatement = new BlockStatement {
+ new Backreference("disposable").ToExpression().Invoke("Dispose")
+ }
+ }
+ }};
+
+ public ForeachStatement TransformNonGenericForEach(ExpressionStatement node)
+ {
+ Match m1 = getEnumeratorPattern.Match(node);
+ if (!m1.Success) return null;
+ AstNode tryCatch = node.NextSibling;
+ Match m2 = nonGenericForeachPattern.Match(tryCatch);
+ if (!m2.Success) return null;
+
+ IdentifierExpression enumeratorVar = m2.Get("enumerator").Single();
+ IdentifierExpression itemVar = m2.Get("itemVar").Single();
+ WhileStatement loop = m2.Get("loop").Single();
+
+ // verify that the getEnumeratorPattern assigns to the same variable as the nonGenericForeachPattern is reading from
+ if (!enumeratorVar.IsMatch(m1.Get("left").Single()))
+ return null;
+
+ VariableDeclarationStatement enumeratorVarDecl = FindVariableDeclaration(loop, enumeratorVar.Identifier);
+ if (enumeratorVarDecl == null || !(enumeratorVarDecl.Parent is BlockStatement))
+ return null;
+
+ // Find the declaration of the item variable:
+ // Because we look only outside the loop, we won't make the mistake of moving a captured variable across the loop boundary
+ VariableDeclarationStatement itemVarDecl = FindVariableDeclaration(loop, itemVar.Identifier);
+ if (itemVarDecl == null || !(itemVarDecl.Parent is BlockStatement))
+ return null;
+
+ // Now verify that we can move the variable declaration in front of the loop:
+ Statement declarationPoint;
+ CanMoveVariableDeclarationIntoStatement(itemVarDecl, loop, out declarationPoint);
+ // We ignore the return value because we don't care whether we can move the variable into the loop
+ // (that is possible only with non-captured variables).
+ // We just care that we can move it in front of the loop:
+ if (declarationPoint != loop)
+ return null;
+
+ ForeachStatement foreachStatement = new ForeachStatement();
+ foreachStatement.VariableType = itemVarDecl.Type.Clone();
+ foreachStatement.VariableName = itemVar.Identifier;
+ BlockStatement body = new BlockStatement();
+ foreachStatement.EmbeddedStatement = body;
+ ((BlockStatement)node.Parent).Statements.InsertBefore(node, foreachStatement);
+
+ body.Add(node.Detach());
+ body.Add((Statement)tryCatch.Detach());
+
+ // Now that we moved the whole try-catch into the foreach loop; verify that we can
+ // move the enumerator into the foreach loop:
+ CanMoveVariableDeclarationIntoStatement(enumeratorVarDecl, foreachStatement, out declarationPoint);
+ if (declarationPoint != foreachStatement) {
+ // oops, the enumerator variable can't be moved into the foreach loop
+ // Undo our AST changes:
+ ((BlockStatement)foreachStatement.Parent).Statements.InsertBefore(foreachStatement, node);
+ foreachStatement.ReplaceWith(tryCatch);
+ return null;
+ }
+
+ // Now create the correct body for the foreach statement:
+ foreachStatement.InExpression = m1.Get("collection").Single().Detach();
+ if (foreachStatement.InExpression is BaseReferenceExpression) {
+ foreachStatement.InExpression = new ThisReferenceExpression().CopyAnnotationsFrom(foreachStatement.InExpression);
+ }
+ body.Statements.Clear();
+ body.Statements.AddRange(m2.Get("stmt").Select(stmt => stmt.Detach()));
+
+ return foreachStatement;
+ }
+ #endregion
+
#region for
static readonly WhileStatement forPattern = new WhileStatement {
Condition = new BinaryOperatorExpression {
diff --git a/ICSharpCode.Decompiler/Tests/Loops.cs b/ICSharpCode.Decompiler/Tests/Loops.cs
index fa96e28f4..6a88b4f81 100644
--- a/ICSharpCode.Decompiler/Tests/Loops.cs
+++ b/ICSharpCode.Decompiler/Tests/Loops.cs
@@ -2,6 +2,7 @@
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
+using System.Collections;
using System.Collections.Generic;
public class Loops
@@ -23,14 +24,30 @@ public class Loops
}
}
- public void ForEachOverArray(string[] array)
+ public void ForEachOverNonGenericEnumerable(IEnumerable enumerable)
{
- foreach (string text in array)
+ foreach (object current in enumerable)
{
- text.ToLower();
+ current.ToString();
}
}
+ public void ForEachOverNonGenericEnumerableWithAutomaticCast(IEnumerable enumerable)
+ {
+ foreach (int num in enumerable)
+ {
+ num.ToString();
+ }
+ }
+
+// public void ForEachOverArray(string[] array)
+// {
+// foreach (string text in array)
+// {
+// text.ToLower();
+// }
+// }
+
public void ForOverArray(string[] array)
{
for (int i = 0; i < array.Length; i++)
diff --git a/ICSharpCode.Decompiler/Tests/TestRunner.cs b/ICSharpCode.Decompiler/Tests/TestRunner.cs
index 23a7651d4..eb6261f7e 100644
--- a/ICSharpCode.Decompiler/Tests/TestRunner.cs
+++ b/ICSharpCode.Decompiler/Tests/TestRunner.cs
@@ -61,7 +61,7 @@ namespace ICSharpCode.Decompiler.Tests
TestFile(@"..\..\Tests\InitializerTests.cs");
}
- [Test, Ignore("ForEachOverArray not supported")]
+ [Test]
public void Loops()
{
TestFile(@"..\..\Tests\Loops.cs");