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 @@ -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 @@ -118,7 +121,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
#endregion
/// <summary>
/// $type $variable = $initializer;
/// $variable = $initializer;
/// </summary>
static readonly AstNode variableAssignPattern = new ExpressionStatement(
new AssignmentExpression(
@ -235,8 +238,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -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 @@ -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 @@ -308,8 +311,9 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
InExpression = m.Get<Expression>("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<Statement>("variablesOutsideLoop")) {
((BlockStatement)foreachStatement.Parent).Statements.InsertAfter(null, stmt.Detach());
@ -318,6 +322,117 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -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<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
static readonly WhileStatement forPattern = new WhileStatement {
Condition = new BinaryOperatorExpression {

23
ICSharpCode.Decompiler/Tests/Loops.cs

@ -2,6 +2,7 @@ @@ -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 @@ -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++)

2
ICSharpCode.Decompiler/Tests/TestRunner.cs

@ -61,7 +61,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -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");

Loading…
Cancel
Save