3
using Cecil.Decompiler.Ast;
7
namespace Cecil.Decompiler.Steps {
9
internal class RebuildForeachStatements : Ast.BaseCodeVisitor, IDecompilationStep {
11
public static readonly IDecompilationStep Instance = new RebuildForeachStatements ();
13
DecompilationContext context;
15
public override void VisitBlockStatement (BlockStatement node)
19
foreach (var statement in node.Statements)
23
void ProcessBlock (BlockStatement node)
25
for (int i = 0; i < node.Statements.ToList ().Count - 1; i++) {
26
var matcher = new ForeachMatcher (node.Statements [i], node.Statements [i + 1]);
27
if (!matcher.Match ())
29
context.RemoveVariable (matcher.Enumerator);
30
node.Statements.RemoveAt (i); // enumerator declaration/assignment
31
node.Statements.RemoveAt (i); // try
32
node.Statements.Insert (i, matcher.Foreach);
33
ProcessBlock (matcher.Foreach.Body);
37
public BlockStatement Process (DecompilationContext context, BlockStatement body)
39
this.context = context;
44
class ForeachMatcher {
53
ForEachStatement @foreach;
54
VariableReference enumerator;
57
BlockStatement while_body;
58
VariableDefinition variable;
62
readonly Statement statement;
63
readonly Statement next_statement;
65
public ForEachStatement Foreach {
67
@foreach = @foreach ?? new ForEachStatement (
68
new VariableDeclarationExpression (this.variable),
75
public VariableReference Enumerator {
76
get { return this.enumerator; }
79
public ForeachMatcher (Statement statement, Statement next_statement)
81
this.statement = statement;
82
this.next_statement = next_statement;
85
internal bool Match ()
89
if (!VisitExpressionStatement (this.statement))
92
if (!VisitTryStatement (this.next_statement))
98
private bool VisitExpressionStatement (Statement node)
100
var expression_statement = node as ExpressionStatement;
101
if (expression_statement == null)
106
case State.WhileBody:
107
if (!VisitAssignExpression (expression_statement.Expression))
112
if (!VisitMethodInvocationExpression (expression_statement.Expression))
120
bool VisitAssignExpression (Expression node)
122
var assign_expression = node as AssignExpression;
123
if (assign_expression == null)
126
if (!VisitVariableReferenceExpression (assign_expression.Target))
129
if (!VisitMethodInvocationExpression (assign_expression.Expression))
135
bool VisitTryStatement (Statement node)
137
var try_statement = node as TryStatement;
138
if (try_statement == null)
141
if (!IsValidTryStatement (try_statement))
144
if (!VisitWhileStatement (try_statement.Try.Statements [0]))
147
if (!VisitIfStatement (try_statement.Finally.Statements [0]))
153
bool VisitIfStatement (Statement statement)
155
var if_statement = statement as IfStatement;
156
if (if_statement == null || if_statement.Else != null || if_statement.Then.Statements.Count != 1)
159
var binary_expression = if_statement.Condition as BinaryExpression;
160
if (binary_expression == null || binary_expression.Operator != BinaryOperator.ValueInequality)
163
var enumerator_reference = binary_expression.Left as VariableReferenceExpression;
164
if (enumerator_reference == null || !IsEnumerator (enumerator_reference))
167
var null_literal = binary_expression.Right as LiteralExpression;
168
if (null_literal == null || null_literal.Value != null)
171
state = State.IfBody;
173
if (!VisitExpressionStatement (if_statement.Then.Statements [0]))
179
bool VisitWhileStatement (Statement node)
181
var while_statement = node as WhileStatement;
182
if (while_statement == null || while_statement.Body.Statements.Count < 1)
185
state = State.WhileCondition;
187
if (!VisitMethodInvocationExpression (while_statement.Condition))
190
state = State.WhileBody;
192
if (!VisitExpressionStatement (while_statement.Body.Statements [0]))
195
this.while_body = new BlockStatement ();
196
for (int i = 1; i < while_statement.Body.Statements.Count; i++) {
197
this.while_body.Statements.Add (while_statement.Body.Statements [i]);
203
bool VisitMethodInvocationExpression (Expression node)
205
var invocation_expression = node as MethodInvocationExpression;
206
if (invocation_expression == null)
209
var method_reference_expression = invocation_expression.Method as MethodReferenceExpression;
210
if (method_reference_expression == null)
213
// todo : use a resolver for method calls
216
if (method_reference_expression.Method.Name != "GetEnumerator")
218
this.source = method_reference_expression.Target;
221
case State.WhileCondition:
222
if (!IsCallOnEnumerator (method_reference_expression, "MoveNext"))
226
case State.WhileBody:
227
if (!IsCallOnEnumerator (method_reference_expression, "get_Current"))
232
if (!IsCallOnEnumerator (method_reference_expression, "Dispose"))
240
bool VisitVariableReferenceExpression (Expression node)
242
var variable_reference_expression = node as VariableReferenceExpression;
243
if (variable_reference_expression == null)
248
this.enumerator = variable_reference_expression.Variable;
251
case State.WhileBody:
252
this.variable = variable_reference_expression.Variable as VariableDefinition;
259
bool IsCallOnEnumerator (MethodReferenceExpression method_reference_expression, string method_name)
261
if (method_reference_expression.Method.Name != method_name)
263
if (!IsEnumerator (method_reference_expression.Target))
268
bool IsEnumerator (Expression node)
270
var variable_reference = node as VariableReferenceExpression;
271
if (variable_reference == null || variable_reference.Variable != this.enumerator)
276
static bool IsValidTryStatement (TryStatement try_statement)
278
return try_statement.CatchClauses.Count == 0 &&
279
try_statement.Try.Statements.Count == 1 &&
280
try_statement.Finally != null &&
281
try_statement.Finally.Statements.Count == 1;
b'\\ No newline at end of file'