From 7e9f6bb9d6e4090733d9522b9cb10ffef5d6a762 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 15 Nov 2014 17:01:09 +0100 Subject: [PATCH] Fix ConvertConstructorCallIntoInitializer transform. --- ICSharpCode.Decompiler/CSharp/Annotations.cs | 10 +++ .../CSharp/CSharpDecompiler.cs | 75 ++++++++++++------- .../CSharp/Transforms/AddCheckedBlocks.cs | 4 +- .../ConvertConstructorCallIntoInitializer.cs | 42 ++++++----- .../CSharp/Transforms/IAstTransform.cs | 15 +++- .../Transforms/IntroduceUnsafeModifier.cs | 2 +- .../ReplaceMethodCallsWithOperators.cs | 2 +- .../Transforms/TransformationPipeline.cs | 47 ------------ .../ICSharpCode.Decompiler.csproj | 1 - .../Tests/Helpers/Tester.cs | 4 +- ICSharpCode.Decompiler/Tests/TestRunner.cs | 13 ++-- 11 files changed, 108 insertions(+), 107 deletions(-) delete mode 100644 ICSharpCode.Decompiler/CSharp/Transforms/TransformationPipeline.cs diff --git a/ICSharpCode.Decompiler/CSharp/Annotations.cs b/ICSharpCode.Decompiler/CSharp/Annotations.cs index 928c9ab12..32ca01b82 100644 --- a/ICSharpCode.Decompiler/CSharp/Annotations.cs +++ b/ICSharpCode.Decompiler/CSharp/Annotations.cs @@ -11,6 +11,7 @@ using System.Collections; using System.Collections.Generic; using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.Decompiler.IL; namespace ICSharpCode.Decompiler.CSharp @@ -72,5 +73,14 @@ namespace ICSharpCode.Decompiler.CSharp expression.Expression.AddAnnotation(resolveResult); return new ConvertedExpression(expression, resolveResult); } + + /// + /// Retrieves the symbol associated with this AstNode, or null if no symbol is associated with the node. + /// + public static ISymbol GetSymbol(this AstNode node) + { + var rr = node.Annotation(); + return rr != null ? rr.GetSymbol() : null; + } } } diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index eccf2463a..9246eb29a 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -16,20 +16,20 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -using ICSharpCode.Decompiler.CSharp.Transforms; -using ICSharpCode.Decompiler.IL; -using ICSharpCode.NRefactory.CSharp; -using ICSharpCode.NRefactory.CSharp.Refactoring; -using ICSharpCode.NRefactory.TypeSystem; -using ICSharpCode.NRefactory.TypeSystem.Implementation; -using ICSharpCode.NRefactory.Utils; -using Mono.Cecil; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; +using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.CSharp.Refactoring; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using ICSharpCode.NRefactory.Utils; +using Mono.Cecil; +using ICSharpCode.Decompiler.CSharp.Transforms; +using ICSharpCode.Decompiler.IL; namespace ICSharpCode.Decompiler.CSharp { @@ -40,7 +40,22 @@ namespace ICSharpCode.Decompiler.CSharp NRefactoryCecilMapper cecilMapper; ICompilation compilation; TypeSystemAstBuilder typeSystemAstBuilder; - List astTransforms = new List(TransformationPipeline.CreatePipeline()); + List astTransforms = new List { + //new PushNegation(), + //new DelegateConstruction(context), + //new PatternStatementTransform(context), + new ReplaceMethodCallsWithOperators(), + new IntroduceUnsafeModifier(), + new AddCheckedBlocks(), + //new DeclareVariables(context), // should run after most transforms that modify statements + new ConvertConstructorCallIntoInitializer(), // must run after DeclareVariables + //new DecimalConstantTransform(), + //new IntroduceUsingDeclarations(context), + //new IntroduceExtensionMethods(context), // must run after IntroduceUsingDeclarations + //new IntroduceQueryExpressions(context), // must run after IntroduceExtensionMethods + //new CombineQueryExpressions(context), + //new FlattenSwitchBlocks(), + }; public CancellationToken CancellationToken { get; set; } @@ -84,6 +99,14 @@ namespace ICSharpCode.Decompiler.CSharp return null; } + void RunTransforms(AstNode rootNode) + { + var context = new TransformContext(compilation, cecilMapper); + foreach (var transform in astTransforms) + transform.Run(rootNode, context); + rootNode.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true }); + } + public SyntaxTree DecompileWholeModuleAsSingleFile() { SyntaxTree syntaxTree = new SyntaxTree(); @@ -100,10 +123,11 @@ namespace ICSharpCode.Decompiler.CSharp foreach (var typeDef in g) { if (typeDef.Name == "" && typeDef.Members.Count == 0) continue; - var typeDecl = Decompile(typeDef); + var typeDecl = DoDecompile(typeDef); groupNode.AddChild(typeDecl, SyntaxTree.MemberRole); } } + RunTransforms(syntaxTree); return syntaxTree; } @@ -114,24 +138,23 @@ namespace ICSharpCode.Decompiler.CSharp ITypeDefinition typeDef = cecilMapper.GetType(typeDefinition).GetDefinition(); if (typeDef == null) throw new InvalidOperationException("Could not find type definition in NR type system"); - return Decompile(typeDef); + var decl = DoDecompile(typeDef); + RunTransforms(decl); + return decl; } - EntityDeclaration Decompile(ITypeDefinition typeDef) + EntityDeclaration DoDecompile(ITypeDefinition typeDef) { var entityDecl = typeSystemAstBuilder.ConvertEntity(typeDef); var typeDecl = entityDecl as TypeDeclaration; - if (typeDecl == null) - { + if (typeDecl == null) { // e.g. DelegateDeclaration return entityDecl; } - foreach (var method in typeDef.Methods) - { + foreach (var method in typeDef.Methods) { var methodDef = cecilMapper.GetCecil(method) as MethodDefinition; - if (methodDef != null) - { - var memberDecl = Decompile(methodDef, method); + if (methodDef != null) { + var memberDecl = DoDecompile(methodDef, method); typeDecl.Members.Add(memberDecl); } } @@ -145,10 +168,12 @@ namespace ICSharpCode.Decompiler.CSharp var method = cecilMapper.GetMethod(methodDefinition); if (method == null) throw new InvalidOperationException("Could not find method in NR type system"); - return Decompile(methodDefinition, method); + var decl = DoDecompile(methodDefinition, method); + RunTransforms(decl); + return decl; } - EntityDeclaration Decompile(MethodDefinition methodDefinition, IMethod method) + EntityDeclaration DoDecompile(MethodDefinition methodDefinition, IMethod method) { var entityDecl = typeSystemAstBuilder.ConvertEntity(method); if (methodDefinition.HasBody) { @@ -159,9 +184,7 @@ namespace ICSharpCode.Decompiler.CSharp function.CheckInvariant(); var statementBuilder = new StatementBuilder(method, cecilMapper); var body = statementBuilder.ConvertAsBlock(function.Body); - foreach (var transform in astTransforms) { - transform.Run(body); - } + // insert variables at start of body Statement prevVarDecl = null; foreach (var v in function.Variables) { @@ -172,9 +195,7 @@ namespace ICSharpCode.Decompiler.CSharp prevVarDecl = varDecl; } } - body.AcceptVisitor(new InsertParenthesesVisitor { - InsertParenthesesForReadability = true - }); + entityDecl.AddChild(body, Roles.Body); } return entityDecl; diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/AddCheckedBlocks.cs b/ICSharpCode.Decompiler/CSharp/Transforms/AddCheckedBlocks.cs index 51b0db29d..edd7fdf94 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/AddCheckedBlocks.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/AddCheckedBlocks.cs @@ -234,12 +234,12 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } #endregion - public void Run(AstNode node) + public void Run(AstNode node, TransformContext context) { BlockStatement block = node as BlockStatement; if (block == null) { for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { - Run(child); + Run(child, context); } } else { Result r = GetResultFromBlock(block); diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/ConvertConstructorCallIntoInitializer.cs b/ICSharpCode.Decompiler/CSharp/Transforms/ConvertConstructorCallIntoInitializer.cs index 9af544c0f..4f7304071 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/ConvertConstructorCallIntoInitializer.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/ConvertConstructorCallIntoInitializer.cs @@ -21,6 +21,8 @@ using System.Collections.Generic; using System.Linq; using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.PatternMatching; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; using Mono.Cecil; namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -28,9 +30,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms /// /// If the first element of a constructor is a chained constructor call, convert it into a constructor initializer. /// - public class ConvertConstructorCallIntoInitializer : DepthFirstAstVisitor, IAstTransform + public class ConvertConstructorCallIntoInitializer : DepthFirstAstVisitor, IAstTransform { - public override object VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data) + public override object VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, TransformContext context) { ExpressionStatement stmt = constructorDeclaration.Body.Statements.FirstOrDefault() as ExpressionStatement; if (stmt == null) @@ -60,7 +62,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms static readonly ExpressionStatement fieldInitializerPattern = new ExpressionStatement { Expression = new AssignmentExpression { - Left = new NamedNode("fieldAccess", new MemberReferenceExpression { + Left = new NamedNode("fieldAccess", new MemberReferenceExpression { Target = new ThisReferenceExpression(), MemberName = Pattern.AnyString }), @@ -71,19 +73,20 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms static readonly AstNode thisCallPattern = new ExpressionStatement(new ThisReferenceExpression().Invoke(".ctor", new Repeat(new AnyNode()))); - public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) + public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, TransformContext context) { // Handle initializers on instance fields HandleInstanceFieldInitializers(typeDeclaration.Members); // Now convert base constructor calls to initializers: - base.VisitTypeDeclaration(typeDeclaration, data); + base.VisitTypeDeclaration(typeDeclaration, context); // Remove single empty constructor: RemoveSingleEmptyConstructor(typeDeclaration); // Handle initializers on static fields: - HandleStaticFieldInitializers(typeDeclaration.Members); + HandleStaticFieldInitializers(typeDeclaration.Members, context); + return null; } @@ -92,8 +95,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms var instanceCtors = members.OfType().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray(); var instanceCtorsNotChainingWithThis = instanceCtors.Where(ctor => !thisCallPattern.IsMatch(ctor.Body.Statements.FirstOrDefault())).ToArray(); if (instanceCtorsNotChainingWithThis.Length > 0) { - MethodDefinition ctorMethodDef = instanceCtorsNotChainingWithThis[0].Annotation(); - if (ctorMethodDef != null && ctorMethodDef.DeclaringType.IsValueType) + var ctorMethodDef = instanceCtorsNotChainingWithThis[0].GetSymbol() as IMethod; + if (ctorMethodDef != null && ctorMethodDef.DeclaringType.IsReferenceType == false) return; // Recognize field initializers: @@ -104,10 +107,10 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms if (!m.Success) break; - FieldDefinition fieldDef = m.Get("fieldAccess").Single().Annotation().ResolveWithinSameModule(); - if (fieldDef == null) + IField field = m.Get("fieldAccess").Single().GetSymbol() as IField; + if (field == null) break; - AstNode fieldOrEventDecl = members.FirstOrDefault(f => f.Annotation() == fieldDef); + AstNode fieldOrEventDecl = members.FirstOrDefault(f => f.GetSymbol() == field); if (fieldOrEventDecl == null) break; Expression initializer = m.Get("initializer").Single(); @@ -141,12 +144,13 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } } - void HandleStaticFieldInitializers(IEnumerable members) + void HandleStaticFieldInitializers(IEnumerable members, TransformContext context) { // Convert static constructor into field initializers if the class is BeforeFieldInit var staticCtor = members.OfType().FirstOrDefault(c => (c.Modifiers & Modifiers.Static) == Modifiers.Static); if (staticCtor != null) { - MethodDefinition ctorMethodDef = staticCtor.Annotation(); + IMethod ctorMethod = staticCtor.GetSymbol() as IMethod; + MethodDefinition ctorMethodDef = context.CecilMapper.GetCecil(ctorMethod) as MethodDefinition; if (ctorMethodDef != null && ctorMethodDef.DeclaringType.IsBeforeFieldInit) { while (true) { ExpressionStatement es = staticCtor.Body.Statements.FirstOrDefault() as ExpressionStatement; @@ -155,10 +159,10 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms AssignmentExpression assignment = es.Expression as AssignmentExpression; if (assignment == null || assignment.Operator != AssignmentOperatorType.Assign) break; - FieldDefinition fieldDef = assignment.Left.Annotation().ResolveWithinSameModule(); - if (fieldDef == null || !fieldDef.IsStatic) + IField field = assignment.Left.GetSymbol() as IField; + if (field == null || !field.IsStatic) break; - FieldDeclaration fieldDecl = members.OfType().FirstOrDefault(f => f.Annotation() == fieldDef); + FieldDeclaration fieldDecl = members.OfType().FirstOrDefault(f => f.GetSymbol() == field); if (fieldDecl == null) break; fieldDecl.Variables.Single().Initializer = assignment.Right.Detach(); @@ -170,14 +174,14 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } } - void IAstTransform.Run(AstNode node) + void IAstTransform.Run(AstNode node, TransformContext context) { // If we're viewing some set of members (fields are direct children of CompilationUnit), // we also need to handle those: HandleInstanceFieldInitializers(node.Children); - HandleStaticFieldInitializers(node.Children); + HandleStaticFieldInitializers(node.Children, context); - node.AcceptVisitor(this, null); + node.AcceptVisitor(this, context); } } } diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IAstTransform.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IAstTransform.cs index ae19e23fb..80614b3db 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/IAstTransform.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/IAstTransform.cs @@ -19,11 +19,24 @@ using System; using System.Threading; using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Transforms { public interface IAstTransform { - void Run(AstNode compilationUnit); + void Run(AstNode rootNode, TransformContext context); + } + + public class TransformContext + { + internal readonly NRefactoryCecilMapper CecilMapper; + public readonly ICompilation Compilation; + + internal TransformContext(ICompilation compilation, NRefactoryCecilMapper cecilMapper) + { + this.Compilation = compilation; + this.CecilMapper = cecilMapper; + } } } diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs index 2f113c46d..a3372451a 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs @@ -27,7 +27,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms sealed class PointerArithmetic {} - public void Run(AstNode compilationUnit) + public void Run(AstNode compilationUnit, TransformContext context) { compilationUnit.AcceptVisitor(this, null); } diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs index 841c50c28..95cfef795 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs @@ -348,7 +348,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms return null; } - void IAstTransform.Run(AstNode node) + void IAstTransform.Run(AstNode node, TransformContext context) { node.AcceptVisitor(this, null); } diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/TransformationPipeline.cs b/ICSharpCode.Decompiler/CSharp/Transforms/TransformationPipeline.cs deleted file mode 100644 index eafe5cdd0..000000000 --- a/ICSharpCode.Decompiler/CSharp/Transforms/TransformationPipeline.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this -// software and associated documentation files (the "Software"), to deal in the Software -// without restriction, including without limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -// to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -using System; -using System.Threading; -using ICSharpCode.NRefactory.CSharp; - -namespace ICSharpCode.Decompiler.CSharp.Transforms -{ - public static class TransformationPipeline - { - public static IAstTransform[] CreatePipeline() - { - return new IAstTransform[] { - //new PushNegation(), - //new DelegateConstruction(context), - //new PatternStatementTransform(context), - new ReplaceMethodCallsWithOperators(), - new IntroduceUnsafeModifier(), - new AddCheckedBlocks(), - //new DeclareVariables(context), // should run after most transforms that modify statements - new ConvertConstructorCallIntoInitializer(), // must run after DeclareVariables - //new DecimalConstantTransform(), - //new IntroduceUsingDeclarations(context), - //new IntroduceExtensionMethods(context), // must run after IntroduceUsingDeclarations - //new IntroduceQueryExpressions(context), // must run after IntroduceExtensionMethods - //new CombineQueryExpressions(context), - //new FlattenSwitchBlocks(), - }; - } - } -} diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 3ffb9b2e2..5170d0bf3 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -75,7 +75,6 @@ - True True diff --git a/ICSharpCode.Decompiler/Tests/Helpers/Tester.cs b/ICSharpCode.Decompiler/Tests/Helpers/Tester.cs index f2e1e1b9b..d928b0eb3 100644 --- a/ICSharpCode.Decompiler/Tests/Helpers/Tester.cs +++ b/ICSharpCode.Decompiler/Tests/Helpers/Tester.cs @@ -23,7 +23,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers public static class Tester { - public static string CompileCSharp(string sourceFileName, CompilerOptions flags = CompilerOptions.UseDebug) + public static CompilerResults CompileCSharp(string sourceFileName, CompilerOptions flags = CompilerOptions.UseDebug) { CSharpCodeProvider provider = new CSharpCodeProvider(new Dictionary { { "CompilerVersion", "v4.0" } }); CompilerParameters options = new CompilerParameters(); @@ -38,7 +38,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers } throw new Exception(b.ToString()); } - return results.PathToAssembly; + return results; } public static int Run(string assemblyFileName, out string output, out string error) diff --git a/ICSharpCode.Decompiler/Tests/TestRunner.cs b/ICSharpCode.Decompiler/Tests/TestRunner.cs index 0471314cd..b2136e28c 100644 --- a/ICSharpCode.Decompiler/Tests/TestRunner.cs +++ b/ICSharpCode.Decompiler/Tests/TestRunner.cs @@ -1,4 +1,5 @@ using System; +using System.CodeDom.Compiler; using System.Collections.Generic; using System.IO; using System.Linq; @@ -49,24 +50,24 @@ namespace ICSharpCode.Decompiler.Tests void TestCompileDecompileCompileOutput(string testFileName, CompilerOptions options = CompilerOptions.UseDebug) { - string outputFile = null, decompiledOutputFile = null; + CompilerResults outputFile = null, decompiledOutputFile = null; string output1, output2, error1, error2; try { outputFile = Tester.CompileCSharp(Path.Combine(TestCasePath, testFileName), options); - string decompiledCodeFile = Tester.DecompileCSharp(outputFile); + string decompiledCodeFile = Tester.DecompileCSharp(outputFile.PathToAssembly); decompiledOutputFile = Tester.CompileCSharp(decompiledCodeFile, options); - int result1 = Tester.Run(outputFile, out output1, out error1); - int result2 = Tester.Run(decompiledOutputFile, out output2, out error2); + int result1 = Tester.Run(outputFile.PathToAssembly, out output1, out error1); + int result2 = Tester.Run(decompiledOutputFile.PathToAssembly, out output2, out error2); Assert.AreEqual(result1, result2); Assert.AreEqual(output1, output2); Assert.AreEqual(error1, error2); } finally { if (outputFile != null) - File.Delete(outputFile); + outputFile.TempFiles.Delete(); if (decompiledOutputFile != null) - File.Delete(decompiledOutputFile); + decompiledOutputFile.TempFiles.Delete(); } } }