|
|
|
@ -73,12 +73,6 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
@@ -73,12 +73,6 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
|
|
|
|
|
public override AstNode VisitExpressionStatement(ExpressionStatement expressionStatement) |
|
|
|
|
{ |
|
|
|
|
AstNode result; |
|
|
|
|
if (context.Settings.UsingStatement) |
|
|
|
|
{ |
|
|
|
|
result = TransformNonGenericForEach(expressionStatement); |
|
|
|
|
if (result != null) |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
result = TransformFor(expressionStatement); |
|
|
|
|
if (result != null) |
|
|
|
|
return result; |
|
|
|
@ -95,16 +89,6 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
@@ -95,16 +89,6 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
|
|
|
|
|
return base.VisitExpressionStatement(expressionStatement); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public override AstNode VisitUsingStatement(UsingStatement usingStatement) |
|
|
|
|
{ |
|
|
|
|
if (context.Settings.ForEachStatement) { |
|
|
|
|
AstNode result = TransformForeach(usingStatement); |
|
|
|
|
if (result != null) |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
return base.VisitUsingStatement(usingStatement); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public override AstNode VisitWhileStatement(WhileStatement whileStatement) |
|
|
|
|
{ |
|
|
|
|
return TransformDoWhile(whileStatement) ?? base.VisitWhileStatement(whileStatement); |
|
|
|
@ -165,244 +149,6 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
@@ -165,244 +149,6 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
|
|
|
|
|
new AnyNode("initializer") |
|
|
|
|
)); |
|
|
|
|
|
|
|
|
|
#region foreach (generic)
|
|
|
|
|
static readonly UsingStatement genericForeachPattern = new UsingStatement { |
|
|
|
|
ResourceAcquisition = new VariableDeclarationStatement { |
|
|
|
|
Type = new AnyNode("enumeratorType"), |
|
|
|
|
Variables = { |
|
|
|
|
new NamedNode( |
|
|
|
|
"enumeratorVariable", |
|
|
|
|
new VariableInitializer { |
|
|
|
|
Name = Pattern.AnyString, |
|
|
|
|
Initializer = new InvocationExpression(new MemberReferenceExpression(new AnyNode("collection").ToExpression(), "GetEnumerator")) |
|
|
|
|
} |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
EmbeddedStatement = new BlockStatement { |
|
|
|
|
new Repeat( |
|
|
|
|
new VariableDeclarationStatement { Type = new AnyNode(), Variables = { new VariableInitializer(Pattern.AnyString) } }.WithName("variablesOutsideLoop") |
|
|
|
|
).ToStatement(), |
|
|
|
|
new WhileStatement { |
|
|
|
|
Condition = new InvocationExpression(new MemberReferenceExpression(new IdentifierExpressionBackreference("enumeratorVariable").ToExpression(), "MoveNext")), |
|
|
|
|
EmbeddedStatement = new BlockStatement { |
|
|
|
|
new Repeat( |
|
|
|
|
new VariableDeclarationStatement { |
|
|
|
|
Type = new AnyNode(), |
|
|
|
|
Variables = { new VariableInitializer(Pattern.AnyString) } |
|
|
|
|
}.WithName("variablesInsideLoop") |
|
|
|
|
).ToStatement(), |
|
|
|
|
new AssignmentExpression { |
|
|
|
|
Left = new IdentifierExpression(Pattern.AnyString).WithName("itemVariable"), |
|
|
|
|
Operator = AssignmentOperatorType.Assign, |
|
|
|
|
Right = new MemberReferenceExpression(new IdentifierExpressionBackreference("enumeratorVariable").ToExpression(), "Current") |
|
|
|
|
}, |
|
|
|
|
new Repeat(new AnyNode("statement")).ToStatement() |
|
|
|
|
} |
|
|
|
|
}.WithName("loop"), |
|
|
|
|
new OptionalNode(new ReturnStatement(new IdentifierExpression(Pattern.AnyString)).WithName("optionalReturn")).ToStatement() |
|
|
|
|
}}; |
|
|
|
|
|
|
|
|
|
public ForeachStatement TransformForeach(UsingStatement node) |
|
|
|
|
{ |
|
|
|
|
Match m = genericForeachPattern.Match(node); |
|
|
|
|
if (!m.Success) |
|
|
|
|
return null; |
|
|
|
|
if (!(node.Parent is BlockStatement) && m.Has("variablesOutsideLoop")) { |
|
|
|
|
// if there are variables outside the loop, we need to put those into the parent block, and that won't work if the direct parent isn't a block
|
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
VariableInitializer enumeratorVar = m.Get<VariableInitializer>("enumeratorVariable").Single(); |
|
|
|
|
var itemVar = m.Get<IdentifierExpression>("itemVariable").Single().GetILVariable(); |
|
|
|
|
WhileStatement loop = m.Get<WhileStatement>("loop").Single(); |
|
|
|
|
|
|
|
|
|
if (!VariableCanBeDeclaredInLoop(itemVar, loop)) { |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Make sure that the enumerator variable is not used inside the body
|
|
|
|
|
var enumeratorId = Identifier.Create(enumeratorVar.Name); |
|
|
|
|
foreach (Statement stmt in m.Get<Statement>("statement")) { |
|
|
|
|
if (stmt.Descendants.OfType<Identifier>().Any(id => enumeratorId.IsMatch(id))) |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
BlockStatement newBody = new BlockStatement(); |
|
|
|
|
foreach (Statement stmt in m.Get<Statement>("variablesInsideLoop")) |
|
|
|
|
newBody.Add(stmt.Detach()); |
|
|
|
|
foreach (Statement stmt in m.Get<Statement>("statement")) |
|
|
|
|
newBody.Add(stmt.Detach()); |
|
|
|
|
|
|
|
|
|
itemVar.Kind = IL.VariableKind.ForeachLocal; |
|
|
|
|
ForeachStatement foreachStatement = new ForeachStatement { |
|
|
|
|
VariableType = context.Settings.AnonymousTypes && itemVar.Type.ContainsAnonymousType() ? new SimpleType("var") : context.TypeSystemAstBuilder.ConvertType(itemVar.Type), |
|
|
|
|
VariableName = itemVar.Name, |
|
|
|
|
InExpression = m.Get<Expression>("collection").Single().Detach(), |
|
|
|
|
EmbeddedStatement = newBody |
|
|
|
|
}.WithILVariable(itemVar); |
|
|
|
|
foreachStatement.CopyAnnotationsFrom(loop); |
|
|
|
|
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()); |
|
|
|
|
} |
|
|
|
|
var optionalReturnAfterLoop = m.Get<ReturnStatement>("optionalReturn").SingleOrDefault(); |
|
|
|
|
if (optionalReturnAfterLoop != null) |
|
|
|
|
((BlockStatement)foreachStatement.Parent).Statements.InsertAfter(foreachStatement, optionalReturnAfterLoop.Detach()); |
|
|
|
|
return foreachStatement; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool VariableCanBeDeclaredInLoop(IL.ILVariable itemVar, WhileStatement loop) |
|
|
|
|
{ |
|
|
|
|
if (itemVar == null || !(itemVar.Kind == IL.VariableKind.Local || itemVar.Kind == IL.VariableKind.StackSlot)) { |
|
|
|
|
// only locals/temporaries can be converted into foreach loop variable
|
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var blockContainer = loop.Annotation<IL.BlockContainer>(); |
|
|
|
|
|
|
|
|
|
if (!itemVar.IsSingleDefinition) { |
|
|
|
|
// foreach variable cannot be assigned to.
|
|
|
|
|
// As a special case, we accept taking the address for a method call,
|
|
|
|
|
// but only if the call is the only use, so that any mutation by the call
|
|
|
|
|
// cannot be observed.
|
|
|
|
|
if (!AddressUsedForSingleCall(itemVar, blockContainer)) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (itemVar.CaptureScope != null && itemVar.CaptureScope != blockContainer) { |
|
|
|
|
// captured variables cannot be declared in the loop unless the loop is their capture scope
|
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
AstNode declPoint = declareVariables.GetDeclarationPoint(itemVar); |
|
|
|
|
return declPoint.Ancestors.Contains(loop); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static bool AddressUsedForSingleCall(IL.ILVariable v, IL.BlockContainer loop) |
|
|
|
|
{ |
|
|
|
|
if (v.StoreCount == 1 && v.AddressCount == 1 && v.LoadCount == 0 && v.Type.IsReferenceType == false) { |
|
|
|
|
if (v.AddressInstructions[0].Parent is IL.Call call |
|
|
|
|
&& v.AddressInstructions[0].ChildIndex == 0 |
|
|
|
|
&& !call.Method.IsStatic) { |
|
|
|
|
// used as this pointer for a method call
|
|
|
|
|
// this is OK iff the call is not within a nested loop
|
|
|
|
|
for (var node = call.Parent; node != null; node = node.Parent) { |
|
|
|
|
if (node == loop) |
|
|
|
|
return true; |
|
|
|
|
else if (node is IL.BlockContainer) |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region foreach (non-generic)
|
|
|
|
|
ExpressionStatement getEnumeratorPattern = new ExpressionStatement( |
|
|
|
|
new AssignmentExpression( |
|
|
|
|
new NamedNode("left", new IdentifierExpression(Pattern.AnyString)), |
|
|
|
|
new InvocationExpression(new MemberReferenceExpression(new AnyNode("collection").ToExpression(), "GetEnumerator")) |
|
|
|
|
)); |
|
|
|
|
|
|
|
|
|
TryCatchStatement nonGenericForeachPattern = new TryCatchStatement { |
|
|
|
|
TryBlock = new BlockStatement { |
|
|
|
|
new WhileStatement { |
|
|
|
|
Condition = new InvocationExpression(new MemberReferenceExpression(new IdentifierExpression(Pattern.AnyString).WithName("enumerator"), "MoveNext")), |
|
|
|
|
EmbeddedStatement = new BlockStatement { |
|
|
|
|
new AssignmentExpression( |
|
|
|
|
new IdentifierExpression(Pattern.AnyString).WithName("itemVar"), |
|
|
|
|
new Choice { |
|
|
|
|
new MemberReferenceExpression(new Backreference("enumerator").ToExpression(), "Current"), |
|
|
|
|
new CastExpression { |
|
|
|
|
Type = new AnyNode("castType"), |
|
|
|
|
Expression = new MemberReferenceExpression(new Backreference("enumerator").ToExpression(), "Current") |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
), |
|
|
|
|
new Repeat(new AnyNode("stmt")).ToStatement() |
|
|
|
|
} |
|
|
|
|
}.WithName("loop") |
|
|
|
|
}, |
|
|
|
|
FinallyBlock = new BlockStatement { |
|
|
|
|
new AssignmentExpression( |
|
|
|
|
new IdentifierExpression(Pattern.AnyString).WithName("disposable"), |
|
|
|
|
new AsExpression(new Backreference("enumerator").ToExpression(), new TypePattern(typeof(IDisposable))) |
|
|
|
|
), |
|
|
|
|
new IfElseStatement { |
|
|
|
|
Condition = new BinaryOperatorExpression { |
|
|
|
|
Left = new Backreference("disposable"), |
|
|
|
|
Operator = BinaryOperatorType.InEquality, |
|
|
|
|
Right = new NullReferenceExpression() |
|
|
|
|
}, |
|
|
|
|
TrueStatement = new BlockStatement { |
|
|
|
|
new InvocationExpression(new MemberReferenceExpression(new Backreference("disposable").ToExpression(), "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(); |
|
|
|
|
var itemVar = m2.Get<IdentifierExpression>("itemVar").Single().GetILVariable(); |
|
|
|
|
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; |
|
|
|
|
|
|
|
|
|
if (!VariableCanBeDeclaredInLoop(itemVar, loop)) |
|
|
|
|
return null; |
|
|
|
|
|
|
|
|
|
itemVar.Kind = IL.VariableKind.ForeachLocal; |
|
|
|
|
ForeachStatement foreachStatement = new ForeachStatement |
|
|
|
|
{ |
|
|
|
|
VariableType = context.Settings.AnonymousTypes && itemVar.Type.ContainsAnonymousType() ? new SimpleType("var") : context.TypeSystemAstBuilder.ConvertType(itemVar.Type), |
|
|
|
|
VariableName = itemVar.Name, |
|
|
|
|
}.WithILVariable(itemVar); |
|
|
|
|
BlockStatement body = new BlockStatement(); |
|
|
|
|
foreachStatement.EmbeddedStatement = body; |
|
|
|
|
foreachStatement.CopyAnnotationsFrom(loop); |
|
|
|
|
((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.Detach()); |
|
|
|
|
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 { |
|
|
|
|