Browse Source

Implemented foreach pattern over non-generic collections.

pull/150/head
Daniel Grunwald 14 years ago
parent
commit
9f842fcda2
  1. 127
      ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
  2. 23
      ICSharpCode.Decompiler/Tests/Loops.cs
  3. 2
      ICSharpCode.Decompiler/Tests/TestRunner.cs

127
ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs

@ -47,6 +47,9 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
result = TransformUsings(expressionStatement); result = TransformUsings(expressionStatement);
if (result != null) if (result != null)
return result; return result;
result = TransformNonGenericForEach(expressionStatement);
if (result != null)
return result;
} }
result = TransformFor(expressionStatement); result = TransformFor(expressionStatement);
if (result != null) if (result != null)
@ -118,7 +121,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
#endregion #endregion
/// <summary> /// <summary>
/// $type $variable = $initializer; /// $variable = $initializer;
/// </summary> /// </summary>
static readonly AstNode variableAssignPattern = new ExpressionStatement( static readonly AstNode variableAssignPattern = new ExpressionStatement(
new AssignmentExpression( new AssignmentExpression(
@ -235,8 +238,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
} }
#endregion #endregion
#region foreach #region foreach (generic)
static readonly UsingStatement foreachPattern = new UsingStatement { static readonly UsingStatement genericForeachPattern = new UsingStatement {
ResourceAcquisition = new VariableDeclarationStatement { ResourceAcquisition = new VariableDeclarationStatement {
Type = new AnyNode("enumeratorType"), Type = new AnyNode("enumeratorType"),
Variables = { Variables = {
@ -270,7 +273,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
public ForeachStatement TransformForeach(UsingStatement node) public ForeachStatement TransformForeach(UsingStatement node)
{ {
Match m = foreachPattern.Match(node); Match m = genericForeachPattern.Match(node);
if (!m.Success) if (!m.Success)
return null; return null;
if (!(node.Parent is BlockStatement) && m.Has("variablesOutsideLoop")) { if (!(node.Parent is BlockStatement) && m.Has("variablesOutsideLoop")) {
@ -308,8 +311,9 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
InExpression = m.Get<Expression>("collection").Single().Detach(), InExpression = m.Get<Expression>("collection").Single().Detach(),
EmbeddedStatement = newBody EmbeddedStatement = newBody
}; };
if (foreachStatement.InExpression is BaseReferenceExpression) if (foreachStatement.InExpression is BaseReferenceExpression) {
foreachStatement.InExpression = new ThisReferenceExpression(); foreachStatement.InExpression = new ThisReferenceExpression().CopyAnnotationsFrom(foreachStatement.InExpression);
}
node.ReplaceWith(foreachStatement); node.ReplaceWith(foreachStatement);
foreach (Statement stmt in m.Get<Statement>("variablesOutsideLoop")) { foreach (Statement stmt in m.Get<Statement>("variablesOutsideLoop")) {
((BlockStatement)foreachStatement.Parent).Statements.InsertAfter(null, stmt.Detach()); ((BlockStatement)foreachStatement.Parent).Statements.InsertAfter(null, stmt.Detach());
@ -318,6 +322,117 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
} }
#endregion #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<IdentifierExpression>("enumerator").Single();
IdentifierExpression itemVar = m2.Get<IdentifierExpression>("itemVar").Single();
WhileStatement loop = m2.Get<WhileStatement>("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<Expression>("collection").Single().Detach();
if (foreachStatement.InExpression is BaseReferenceExpression) {
foreachStatement.InExpression = new ThisReferenceExpression().CopyAnnotationsFrom(foreachStatement.InExpression);
}
body.Statements.Clear();
body.Statements.AddRange(m2.Get<Statement>("stmt").Select(stmt => stmt.Detach()));
return foreachStatement;
}
#endregion
#region for #region for
static readonly WhileStatement forPattern = new WhileStatement { static readonly WhileStatement forPattern = new WhileStatement {
Condition = new BinaryOperatorExpression { Condition = new BinaryOperatorExpression {

23
ICSharpCode.Decompiler/Tests/Loops.cs

@ -2,6 +2,7 @@
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) // This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
public class Loops 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) public void ForOverArray(string[] array)
{ {
for (int i = 0; i < array.Length; i++) for (int i = 0; i < array.Length; i++)

2
ICSharpCode.Decompiler/Tests/TestRunner.cs

@ -61,7 +61,7 @@ namespace ICSharpCode.Decompiler.Tests
TestFile(@"..\..\Tests\InitializerTests.cs"); TestFile(@"..\..\Tests\InitializerTests.cs");
} }
[Test, Ignore("ForEachOverArray not supported")] [Test]
public void Loops() public void Loops()
{ {
TestFile(@"..\..\Tests\Loops.cs"); TestFile(@"..\..\Tests\Loops.cs");

Loading…
Cancel
Save