Browse Source

Remove foreach from PatternStatementTransform.

pull/870/merge
Siegfried Pammer 8 years ago
parent
commit
0691aee363
  1. 254
      ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs

254
ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs

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

Loading…
Cancel
Save