|
|
@ -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 { |
|
|
|