From c7bbdcd0cb81c182318f188a1ef4e077ed0f403f Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 20 Mar 2011 02:59:59 +0100 Subject: [PATCH] Improved variable placement. The variable placement step now happens much later in the decompiler pipeline, which simplifies some AST transforms. --- .../Ast/AstMethodBodyBuilder.cs | 8 +- .../Ast/DeclareVariableInSmallestScope.cs | 91 ------- .../Ast/Transforms/DeclareVariables.cs | 255 ++++++++++++++++++ .../Ast/Transforms/DelegateConstruction.cs | 27 +- .../Transforms/IntroduceUsingDeclarations.cs | 2 +- .../Transforms/PatternStatementTransform.cs | 190 +++++-------- .../Ast/Transforms/TransformationPipeline.cs | 1 + .../ICSharpCode.Decompiler.csproj | 2 +- 8 files changed, 345 insertions(+), 231 deletions(-) delete mode 100644 ICSharpCode.Decompiler/Ast/DeclareVariableInSmallestScope.cs create mode 100644 ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index b6c626e6a..6bd62aad8 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -22,7 +22,6 @@ namespace ICSharpCode.Decompiler.Ast TypeSystem typeSystem; DecompilerContext context; HashSet localVariablesToDefine = new HashSet(); // local variables that are missing a definition - HashSet implicitlyDefinedVariables = new HashSet(); // local variables that are implicitly defined (e.g. catch handler) /// /// Creates the body for the method definition. @@ -89,8 +88,9 @@ namespace ICSharpCode.Decompiler.Ast context.CancellationToken.ThrowIfCancellationRequested(); Ast.BlockStatement astBlock = TransformBlock(ilMethod); CommentStatement.ReplaceAll(astBlock); // convert CommentStatements to Comments - foreach (ILVariable v in localVariablesToDefine.Except(implicitlyDefinedVariables)) { - DeclareVariableInSmallestScope.DeclareVariable(astBlock, AstBuilder.ConvertType(v.Type), v.Name); + foreach (ILVariable v in localVariablesToDefine) { + var newVarDecl = new VariableDeclarationStatement(AstBuilder.ConvertType(v.Type), v.Name); + astBlock.Statements.InsertAfter(null, newVarDecl); } return astBlock; @@ -158,8 +158,6 @@ namespace ICSharpCode.Decompiler.Ast var tryCatchStmt = new Ast.TryCatchStatement(); tryCatchStmt.TryBlock = TransformBlock(tryCatchNode.TryBlock); foreach (var catchClause in tryCatchNode.CatchBlocks) { - if (catchClause.ExceptionVariable != null) - implicitlyDefinedVariables.Add(catchClause.ExceptionVariable); tryCatchStmt.CatchClauses.Add( new Ast.CatchClause { Type = AstBuilder.ConvertType(catchClause.ExceptionType), diff --git a/ICSharpCode.Decompiler/Ast/DeclareVariableInSmallestScope.cs b/ICSharpCode.Decompiler/Ast/DeclareVariableInSmallestScope.cs deleted file mode 100644 index f609c2414..000000000 --- a/ICSharpCode.Decompiler/Ast/DeclareVariableInSmallestScope.cs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under MIT X11 license (for details please see \doc\license.txt) - -using System; -using System.Linq; -using ICSharpCode.NRefactory.CSharp; -using ICSharpCode.NRefactory.CSharp.PatternMatching; - -namespace ICSharpCode.Decompiler.Ast -{ - /// - /// Helper class for declaring variables. - /// - public static class DeclareVariableInSmallestScope - { - static readonly ExpressionStatement assignmentPattern = new ExpressionStatement( - new AssignmentExpression( - new NamedNode("ident", new IdentifierExpression()), - new AnyNode("init") - )); - - /// - /// Declares a variable in the smallest required scope. - /// - /// The root of the subtree being searched for the best insertion position - /// The type of the new variable - /// The name of the new variable - /// Whether the variable is allowed to be placed inside a loop - public static VariableDeclarationStatement DeclareVariable(AstNode node, AstType type, string name, bool allowPassIntoLoops = true) - { - VariableDeclarationStatement result = null; - AstNode pos = FindInsertPos(node, name, allowPassIntoLoops); - if (pos != null) { - Match m = assignmentPattern.Match(pos); - if (m != null && m.Get("ident").Single().Identifier == name) { - result = new VariableDeclarationStatement(type, name, m.Get("init").Single().Detach()); - result.Variables.Single().CopyAnnotationsFrom(((ExpressionStatement)pos).Expression); - result.CopyAnnotationsFrom(pos); - pos.ReplaceWith(result); - } else { - result = new VariableDeclarationStatement(type, name); - pos.Parent.InsertChildBefore(pos, result, BlockStatement.StatementRole); - } - } - return result; - } - - static AstNode FindInsertPos(AstNode node, string name, bool allowPassIntoLoops) - { - AstNode pos = null; - AstNode withinPos = null; - while (node != null) { - IdentifierExpression ident = node as IdentifierExpression; - if (ident != null && ident.Identifier == name && ident.TypeArguments.Count == 0) - return node; - - FixedStatement fixedStatement = node as FixedStatement; - if (fixedStatement != null) { - foreach (VariableInitializer v in fixedStatement.Variables) { - if (v.Name == name) - return null; // no need to introduce the variable here - } - } - - AstNode withinCurrent = FindInsertPos(node.FirstChild, name, allowPassIntoLoops); - if (withinCurrent != null) { - if (pos == null) { - pos = node; - withinPos = withinCurrent; - } else { - return pos; - } - } - node = node.NextSibling; - } - if (withinPos != null && withinPos.Role == BlockStatement.StatementRole && AllowPassInto(pos, allowPassIntoLoops)) - return withinPos; - else - return pos; - } - - static bool AllowPassInto(AstNode node, bool allowPassIntoLoops) - { - if (node is AnonymousMethodExpression || node is LambdaExpression) - return false; - if (node is ForStatement || node is ForeachStatement || node is DoWhileStatement || node is WhileStatement) - return allowPassIntoLoops; - return true; - } - } -} diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs b/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs new file mode 100644 index 000000000..0d85de650 --- /dev/null +++ b/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs @@ -0,0 +1,255 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.CSharp.Analysis; + +namespace ICSharpCode.Decompiler.Ast.Transforms +{ + /// + /// Moves variable declarations to improved positions. + /// + public class DeclareVariables : IAstTransform + { + sealed class DeclaredVariableAnnotation { + public readonly ExpressionStatement OriginalAssignmentStatement; + + public DeclaredVariableAnnotation(ExpressionStatement originalAssignmentStatement) + { + this.OriginalAssignmentStatement = originalAssignmentStatement; + } + } + static readonly DeclaredVariableAnnotation declaredVariableAnnotation = new DeclaredVariableAnnotation(null); + + readonly CancellationToken cancellationToken; + + public DeclareVariables(DecompilerContext context) + { + this.cancellationToken = context.CancellationToken; + } + + public void Run(AstNode node) + { + Run(node, null); + } + + void Run(AstNode node, DefiniteAssignmentAnalysis daa) + { + BlockStatement block = node as BlockStatement; + if (block != null) { + var variables = block.Statements.TakeWhile(stmt => stmt is VariableDeclarationStatement + && stmt.Annotation() == null) + .Cast().ToList(); + if (variables.Count > 0) { + // remove old variable declarations: + foreach (VariableDeclarationStatement varDecl in variables) { + Debug.Assert(varDecl.Variables.Single().Initializer.IsNull); + varDecl.Remove(); + } + if (daa == null) { + // If possible, reuse the DefiniteAssignmentAnalysis that was created for the parent block + daa = new DefiniteAssignmentAnalysis(block, cancellationToken); + } + foreach (VariableDeclarationStatement varDecl in variables) { + string variableName = varDecl.Variables.Single().Name; + bool allowPassIntoLoops = varDecl.Variables.Single().Annotation() == null; + DeclareVariableInBlock(daa, block, varDecl.Type, variableName, allowPassIntoLoops); + } + } + } + for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { + Run(child, daa); + } + } + + void DeclareVariableInBlock(DefiniteAssignmentAnalysis daa, BlockStatement block, AstType type, string variableName, bool allowPassIntoLoops) + { + // declarationPoint: The point where the variable would be declared, if we decide to declare it in this block + Statement declarationPoint = null; + // Check whether we can move down the variable into the sub-blocks + bool ok = true; + foreach (Statement stmt in block.Statements) { + if (UsesVariable(stmt, variableName)) { + if (declarationPoint == null) + declarationPoint = stmt; + if (!CanMoveVariableUseIntoSubBlock(stmt, variableName, allowPassIntoLoops)) { + // If it's not possible to move the variable use into a nested block, + // we need to declare the variable in this block + ok = false; + break; + } + // If we can move the variable into the sub-block, we need to ensure that the remaining code + // does not use the value that was assigend by the first sub-block + Statement nextStatement = stmt.NextStatement; + // The next statement might be a variable declaration that we inserted, and thus does not exist + // in the definite assignment graph. Thus we need to look up the corresponding instruction + // prior to the introduction of the VariableDeclarationStatement. + while (nextStatement is VariableDeclarationStatement) { + DeclaredVariableAnnotation annotation = nextStatement.Annotation(); + if (annotation == null) + break; + if (annotation.OriginalAssignmentStatement != null) { + nextStatement = annotation.OriginalAssignmentStatement; + break; + } + nextStatement = nextStatement.NextStatement; + } + if (nextStatement != null) { + // Analyze the range from the next statement to the end of the block + daa.SetAnalyzedRange(nextStatement, block); + daa.Analyze(variableName); + if (daa.UnassignedVariableUses.Count > 0) { + ok = false; + break; + } + } + } + } + if (declarationPoint == null) { + // The variable isn't used at all + return; + } + if (ok) { + // Declare the variable within the sub-blocks + foreach (Statement stmt in block.Statements) { + foreach (BlockStatement subBlock in stmt.Children.OfType()) { + DeclareVariableInBlock(daa, subBlock, type, variableName, allowPassIntoLoops); + } + } + } else { + // Try converting an assignment expression into a VariableDeclarationStatement + ExpressionStatement es = declarationPoint as ExpressionStatement; + if (es != null) { + AssignmentExpression ae = es.Expression as AssignmentExpression; + if (ae != null && ae.Operator == AssignmentOperatorType.Assign) { + IdentifierExpression ident = ae.Left as IdentifierExpression; + if (ident != null && ident.Identifier == variableName) { + // convert the declarationPoint into a VariableDeclarationStatement + declarationPoint.ReplaceWith( + new VariableDeclarationStatement { + Type = (AstType)type.Clone(), + Variables = { + new VariableInitializer(variableName, ae.Right.Detach()).CopyAnnotationsFrom(ae) + } + }.CopyAnnotationsFrom(es).WithAnnotation(new DeclaredVariableAnnotation(es))); + return; + } + } + } + // Declare the variable in front of declarationPoint + block.Statements.InsertBefore( + declarationPoint, + new VariableDeclarationStatement((AstType)type.Clone(), variableName) + .WithAnnotation(declaredVariableAnnotation)); + } + } + + bool CanMoveVariableUseIntoSubBlock(Statement stmt, string variableName, bool allowPassIntoLoops) + { + if (!allowPassIntoLoops && (stmt is ForStatement || stmt is ForeachStatement || stmt is DoWhileStatement || stmt is WhileStatement)) + return false; + ForStatement forStatement = stmt as ForStatement; + if (forStatement != null && forStatement.Initializers.Count == 1) { + // for-statement is special case: we can move variable declarations into the initializer + ExpressionStatement es = forStatement.Initializers.Single() as ExpressionStatement; + if (es != null) { + AssignmentExpression ae = es.Expression as AssignmentExpression; + if (ae != null && ae.Operator == AssignmentOperatorType.Assign) { + IdentifierExpression ident = ae.Left as IdentifierExpression; + if (ident != null && ident.Identifier == variableName) { + return !UsesVariable(ae.Right, variableName); + } + } + } + } + // We can move the variable into a sub-block only if the variable is used in only that sub-block + for (AstNode child = stmt.FirstChild; child != null; child = child.NextSibling) { + if (!(child is BlockStatement) && UsesVariable(child, variableName)) + return false; + } + return true; + } + + bool UsesVariable(AstNode node, string variableName) + { + IdentifierExpression ie = node as IdentifierExpression; + if (ie != null && ie.Identifier == variableName) + return true; + + FixedStatement fixedStatement = node as FixedStatement; + if (fixedStatement != null) { + foreach (VariableInitializer v in fixedStatement.Variables) { + if (v.Name == variableName) + return false; // no need to introduce the variable here + } + } + + ForeachStatement foreachStatement = node as ForeachStatement; + if (foreachStatement != null) { + if (foreachStatement.VariableName == variableName) + return false; // no need to introduce the variable here + } + + for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { + if (UsesVariable(child, variableName)) + return true; + } + return false; + } + + #region FindInsertPos + static AstNode FindInsertPos(AstNode node, string name, bool allowPassIntoLoops) + { + AstNode pos = null; + AstNode withinPos = null; + while (node != null) { + IdentifierExpression ident = node as IdentifierExpression; + if (ident != null && ident.Identifier == name && ident.TypeArguments.Count == 0) + return node; + + FixedStatement fixedStatement = node as FixedStatement; + if (fixedStatement != null) { + foreach (VariableInitializer v in fixedStatement.Variables) { + if (v.Name == name) + return null; // no need to introduce the variable here + } + } + ForeachStatement foreachStatement = node as ForeachStatement; + if (foreachStatement != null) { + if (foreachStatement.VariableName == name) + return null; // no need to introduce the variable here + } + + AstNode withinCurrent = FindInsertPos(node.FirstChild, name, allowPassIntoLoops); + if (withinCurrent != null) { + if (pos == null) { + pos = node; + withinPos = withinCurrent; + } else { + return pos; + } + } + node = node.NextSibling; + } + if (withinPos != null && withinPos.Role == BlockStatement.StatementRole && AllowPassInto(pos, allowPassIntoLoops)) + return withinPos; + else + return pos; + } + + static bool AllowPassInto(AstNode node, bool allowPassIntoLoops) + { + if (node is AnonymousMethodExpression || node is LambdaExpression) + return false; + + return allowPassIntoLoops; + return true; + } + #endregion + } +} diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs index 48a2ddf6c..b619230a9 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs @@ -179,23 +179,33 @@ namespace ICSharpCode.Decompiler.Ast.Transforms return true; } + static readonly ExpressionStatement displayClassAssignmentPattern = + new ExpressionStatement(new AssignmentExpression( + new NamedNode("variable", new IdentifierExpression()), + new ObjectCreateExpression { Type = new AnyNode("type") } + )); + public override object VisitBlockStatement(BlockStatement blockStatement, object data) { base.VisitBlockStatement(blockStatement, data); - foreach (VariableDeclarationStatement stmt in blockStatement.Statements.OfType().ToArray()) { - if (stmt.Variables.Count() != 1) + foreach (ExpressionStatement stmt in blockStatement.Statements.OfType().ToArray()) { + Match displayClassAssignmentMatch = displayClassAssignmentPattern.Match(stmt); + if (displayClassAssignmentMatch == null) + continue; + + ILVariable variable = displayClassAssignmentMatch.Get("variable").Single().Annotation(); + if (variable == null) continue; - var variable = stmt.Variables.Single(); - TypeDefinition type = stmt.Type.Annotation().ResolveWithinSameModule(); + TypeDefinition type = variable.Type.ResolveWithinSameModule(); if (!IsPotentialClosure(context, type)) continue; - ObjectCreateExpression oce = variable.Initializer as ObjectCreateExpression; - if (oce == null || oce.Type.Annotation().ResolveWithinSameModule() != type || oce.Arguments.Any() || !oce.Initializer.IsNull) + if (displayClassAssignmentMatch.Get("type").Single().Annotation().ResolveWithinSameModule() != type) continue; + // Looks like we found a display class creation. Now let's verify that the variable is used only for field accesses: bool ok = true; foreach (var identExpr in blockStatement.Descendants.OfType()) { - if (identExpr.Identifier == variable.Name) { + if (identExpr.Identifier == variable.Name && identExpr != displayClassAssignmentMatch.Get("variable").Single()) { if (!(identExpr.Parent is MemberReferenceExpression && identExpr.Parent.Annotation() != null)) ok = false; } @@ -267,9 +277,10 @@ namespace ICSharpCode.Decompiler.Ast.Transforms } // Now insert the variable declarations (we can do this after the replacements only so that the scope detection works): foreach (var tuple in variablesToDeclare) { - var newVarDecl = DeclareVariableInSmallestScope.DeclareVariable(blockStatement, tuple.Item1, tuple.Item2, allowPassIntoLoops: false); + var newVarDecl = new VariableDeclarationStatement(tuple.Item1, tuple.Item2); if (newVarDecl != null) newVarDecl.Variables.Single().AddAnnotation(new CapturedVariableAnnotation()); + blockStatement.Statements.InsertAfter(null, newVarDecl); } } return null; diff --git a/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs b/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs index 0b0a40564..9b0c3d713 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs @@ -25,7 +25,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms public void Run(AstNode compilationUnit) { // Don't show using when decompiling a single method or nested types: - if (context.CurrentMethod != null || (context.CurrentType != null && context.CurrentType.IsNested)) + if (context.CurrentType != null) return; // First determine all the namespaces that need to be imported: diff --git a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs index e2d6536d3..6cdc1f5da 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using ICSharpCode.Decompiler.ILAst; using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp.PatternMatching; using Mono.Cecil; @@ -37,23 +38,23 @@ namespace ICSharpCode.Decompiler.Ast.Transforms return node; } - public override AstNode VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, object data) + public override AstNode VisitExpressionStatement(ExpressionStatement expressionStatement, object data) { AstNode result; if (context.Settings.UsingStatement) { - result = TransformUsings(variableDeclarationStatement); + result = TransformUsings(expressionStatement); if (result != null) return result; } - result = TransformFor(variableDeclarationStatement); + result = TransformFor(expressionStatement); if (result != null) return result; if (context.Settings.LockStatement) { - result = TransformLock(variableDeclarationStatement); + result = TransformLock(expressionStatement); if (result != null) return result; } - return base.VisitVariableDeclarationStatement(variableDeclarationStatement, data); + return base.VisitExpressionStatement(expressionStatement, data); } public override AstNode VisitUsingStatement(UsingStatement usingStatement, object data) @@ -117,27 +118,11 @@ namespace ICSharpCode.Decompiler.Ast.Transforms /// /// $type $variable = $initializer; /// - static readonly AstNode variableDeclPattern = new VariableDeclarationStatement { - Type = new AnyNode("type"), - Variables = { - new NamedNode( - "variable", - new VariableInitializer { - Initializer = new AnyNode("initializer") - } - ) - } - }; - - /// - /// Variable declaration without initializer. - /// - static readonly AstNode simpleVariableDefinition = new VariableDeclarationStatement { - Type = new AnyNode(), - Variables = { - new VariableInitializer() // any name but no initializer - } - }; + static readonly AstNode variableAssignPattern = new ExpressionStatement( + new AssignmentExpression( + new NamedNode("variable", new IdentifierExpression()), + new AnyNode("initializer") + )); #region using static readonly AstNode usingTryCatchPattern = new TryCatchStatement { @@ -163,20 +148,18 @@ namespace ICSharpCode.Decompiler.Ast.Transforms } }; - public UsingStatement TransformUsings(VariableDeclarationStatement node) + public UsingStatement TransformUsings(ExpressionStatement node) { - Match m1 = variableDeclPattern.Match(node); + Match m1 = variableAssignPattern.Match(node); if (m1 == null) return null; AstNode tryCatch = node.NextSibling; - while (simpleVariableDefinition.Match(tryCatch) != null) - tryCatch = tryCatch.NextSibling; Match m2 = usingTryCatchPattern.Match(tryCatch); if (m2 == null) return null; - if (m1.Get("variable").Single().Name == m2.Get("ident").Single().Identifier) { + if (m1.Get("variable").Single().Identifier == m2.Get("ident").Single().Identifier) { if (m2.Has("valueType")) { // if there's no if(x!=null), then it must be a value type - TypeReference tr = m1.Get("type").Single().Annotation(); - if (tr == null || !tr.IsValueType) + ILVariable v = m1.Get("variable").Single().Annotation(); + if (v == null || v.Type == null || !v.Type.IsValueType) return null; } BlockStatement body = m2.Get("body").Single(); @@ -184,6 +167,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms usingStatement.ResourceAcquisition = node.Detach(); usingStatement.EmbeddedStatement = body.Detach(); tryCatch.ReplaceWith(usingStatement); + // TODO: Move the variable declaration into the resource acquisition, if possible + // This is necessary for the foreach-pattern to work on the result of the using-pattern return usingStatement; } return null; @@ -203,55 +188,22 @@ namespace ICSharpCode.Decompiler.Ast.Transforms ) } }, - EmbeddedStatement = new Choice { - // There are two forms of the foreach statement: - // one where the item variable is declared inside the loop, - // and one where it is declared outside of the loop. - // In the former case, we can apply the foreach pattern only if the variable wasn't captured. - { "itemVariableInsideLoop", - new BlockStatement { - new WhileStatement { - Condition = new IdentifierExpressionBackreference("enumeratorVariable").ToExpression().Invoke("MoveNext"), - EmbeddedStatement = new BlockStatement { - new VariableDeclarationStatement { - Type = new AnyNode("itemType"), - Variables = { - new NamedNode( - "itemVariable", - new VariableInitializer { - Initializer = new IdentifierExpressionBackreference("enumeratorVariable").ToExpression().Member("Current") - } - ) - } - }, - new Repeat(new AnyNode("statement")).ToStatement() - } - } - } - }, - { "itemVariableOutsideLoop", - new BlockStatement { - new VariableDeclarationStatement { - Type = new AnyNode("itemType"), - Variables = { - new NamedNode("itemVariable", new VariableInitializer()) - } + EmbeddedStatement = new BlockStatement { + new Repeat( + new NamedNode("variableDeclaration", new VariableDeclarationStatement { Type = new AnyNode(), Variables = { new AnyNode() } }) + ).ToStatement(), + new WhileStatement { + Condition = new IdentifierExpressionBackreference("enumeratorVariable").ToExpression().Invoke("MoveNext"), + EmbeddedStatement = new BlockStatement { + new AssignmentExpression { + Left = new NamedNode("itemVariable", new IdentifierExpression()), + Operator = AssignmentOperatorType.Assign, + Right = new IdentifierExpressionBackreference("enumeratorVariable").ToExpression().Member("Current") }, - new WhileStatement { - Condition = new IdentifierExpressionBackreference("enumeratorVariable").ToExpression().Invoke("MoveNext"), - EmbeddedStatement = new BlockStatement { - new AssignmentExpression { - Left = new IdentifierExpressionBackreference("itemVariable"), - Operator = AssignmentOperatorType.Assign, - Right = new IdentifierExpressionBackreference("enumeratorVariable").ToExpression().Member("Current") - }, - new Repeat(new AnyNode("statement")).ToStatement() - } - } + new Repeat(new AnyNode("statement")).ToStatement() } } - }.ToStatement() - }; + }}; public ForeachStatement TransformForeach(UsingStatement node) { @@ -259,14 +211,18 @@ namespace ICSharpCode.Decompiler.Ast.Transforms if (m == null) return null; VariableInitializer enumeratorVar = m.Get("enumeratorVariable").Single(); - VariableInitializer itemVar = m.Get("itemVariable").Single(); - if (m.Has("itemVariableInsideLoop") && itemVar.Annotation() != null) { - // cannot move captured variables out of loops + ILVariable itemVar = m.Get("itemVariable").Single().Annotation(); + if (itemVar == null) return null; - } + return null; // TODO +// if (m.Has("itemVariableInsideLoop") && itemVar.Annotation() != null) { +// // cannot move captured variables out of loops +// return null; +// } BlockStatement newBody = new BlockStatement(); foreach (Statement stmt in m.Get("statement")) newBody.Add(stmt.Detach()); + ForeachStatement foreachStatement = new ForeachStatement { VariableType = m.Get("itemType").Single().Detach(), VariableName = itemVar.Name, @@ -299,17 +255,15 @@ namespace ICSharpCode.Decompiler.Ast.Transforms } }}; - public ForStatement TransformFor(VariableDeclarationStatement node) + public ForStatement TransformFor(ExpressionStatement node) { - Match m1 = variableDeclPattern.Match(node); + Match m1 = variableAssignPattern.Match(node); if (m1 == null) return null; AstNode next = node.NextSibling; - while (simpleVariableDefinition.Match(next) != null) - next = next.NextSibling; Match m2 = forPattern.Match(next); if (m2 == null) return null; // ensure the variable in the for pattern is the same as in the declaration - if (m1.Get("variable").Single().Name != m2.Get("ident").Single().Identifier) + if (m1.Get("variable").Single().Identifier != m2.Get("ident").Single().Identifier) return null; WhileStatement loop = (WhileStatement)next; node.Remove(); @@ -374,16 +328,11 @@ namespace ICSharpCode.Decompiler.Ast.Transforms #endregion #region lock - static readonly AstNode lockFlagInitPattern = new VariableDeclarationStatement { - Type = new PrimitiveType("bool"), - Variables = { - new NamedNode( - "variable", - new VariableInitializer { - Initializer = new PrimitiveExpression(false) - } - ) - }}; + static readonly AstNode lockFlagInitPattern = new ExpressionStatement( + new AssignmentExpression( + new NamedNode("variable", new IdentifierExpression()), + new PrimitiveExpression(false) + )); static readonly AstNode lockTryCatchPattern = new TryCatchStatement { TryBlock = new BlockStatement { @@ -404,16 +353,14 @@ namespace ICSharpCode.Decompiler.Ast.Transforms } }}; - public LockStatement TransformLock(VariableDeclarationStatement node) + public LockStatement TransformLock(ExpressionStatement node) { Match m1 = lockFlagInitPattern.Match(node); if (m1 == null) return null; AstNode tryCatch = node.NextSibling; - while (simpleVariableDefinition.Match(tryCatch) != null) - tryCatch = tryCatch.NextSibling; Match m2 = lockTryCatchPattern.Match(tryCatch); if (m2 == null) return null; - if (m1.Get("variable").Single().Name == m2.Get("flag").Single().Identifier) { + if (m1.Get("variable").Single().Identifier == m2.Get("flag").Single().Identifier) { Expression enter = m2.Get("enter").Single(); IdentifierExpression exit = m2.Get("exit").Single(); if (exit.Match(enter) == null) { @@ -630,32 +577,25 @@ namespace ICSharpCode.Decompiler.Ast.Transforms #region Automatic Events static readonly Accessor automaticEventPatternV4 = new Accessor { Body = new BlockStatement { - new VariableDeclarationStatement { - Type = new AnyNode("type"), - Variables = { - new NamedNode( - "var1", new VariableInitializer { - Initializer = new NamedNode("field", new MemberReferenceExpression { Target = new ThisReferenceExpression() }) - })} - }, - new VariableDeclarationStatement { - Type = new Backreference("type"), - Variables = { new NamedNode("var2", new VariableInitializer()) } + new VariableDeclarationStatement { Type = new AnyNode("type"), Variables = { new AnyNode() } }, + new VariableDeclarationStatement { Type = new Backreference("type"), Variables = { new AnyNode() } }, + new VariableDeclarationStatement { Type = new Backreference("type"), Variables = { new AnyNode() } }, + new AssignmentExpression { + Left = new NamedNode("var1", new IdentifierExpression()), + Operator = AssignmentOperatorType.Assign, + Right = new NamedNode("field", new MemberReferenceExpression { Target = new ThisReferenceExpression() }) }, new DoWhileStatement { EmbeddedStatement = new BlockStatement { - new AssignmentExpression(new IdentifierExpressionBackreference("var2"), new IdentifierExpressionBackreference("var1")), - new VariableDeclarationStatement { - Type = new Backreference("type"), - Variables = { - new NamedNode( - "var3", new VariableInitializer { - Initializer = new AnyNode("delegateCombine").ToExpression().Invoke( - new IdentifierExpressionBackreference("var2"), - new IdentifierExpression("value") - ).CastTo(new Backreference("type")) - }) - }}, + new AssignmentExpression(new NamedNode("var2", new IdentifierExpression()), new IdentifierExpressionBackreference("var1")), + new AssignmentExpression { + Left = new NamedNode("var3", new IdentifierExpression()), + Operator = AssignmentOperatorType.Assign, + Right = new AnyNode("delegateCombine").ToExpression().Invoke( + new IdentifierExpressionBackreference("var2"), + new IdentifierExpression("value") + ).CastTo(new Backreference("type")) + }, new AssignmentExpression { Left = new IdentifierExpressionBackreference("var1"), Right = new TypePattern(typeof(System.Threading.Interlocked)).ToType().Invoke( diff --git a/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs index eff00c6ed..57a5ada6c 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs @@ -23,6 +23,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms new ConvertConstructorCallIntoInitializer(), new ReplaceMethodCallsWithOperators(), new IntroduceUnsafeModifier(), + new DeclareVariables(context), new IntroduceUsingDeclarations(context) }; } diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 945b4d705..b7c2151d6 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -53,13 +53,13 @@ - +