diff --git a/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs b/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs index 7acab4d21..0a4602084 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.CSharp.PatternMatching; using Mono.Cecil; namespace ICSharpCode.Decompiler.Ast.Transforms @@ -32,26 +33,91 @@ namespace ICSharpCode.Decompiler.Ast.Transforms return null; // Move arguments from invocation to initializer: invocation.Arguments.MoveTo(ci.Arguments); - // Add the initializer: - constructorDeclaration.Initializer = ci.WithAnnotation(invocation.Annotation()); + // Add the initializer: (unless it is the default 'base()') + if (!(ci.ConstructorInitializerType == ConstructorInitializerType.Base && ci.Arguments.Count == 0)) + constructorDeclaration.Initializer = ci.WithAnnotation(invocation.Annotation()); // Remove the statement: stmt.Remove(); } return null; } + static readonly ExpressionStatement fieldInitializerPattern = new ExpressionStatement { + Expression = new AssignmentExpression { + Left = new NamedNode("fieldAccess", new MemberReferenceExpression { Target = new ThisReferenceExpression() }), + Operator = AssignmentOperatorType.Assign, + Right = new AnyNode("initializer") + } + }; + public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) { + var instanceCtors = typeDeclaration.Members.OfType().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray(); + if (instanceCtors.Length > 0 && typeDeclaration.ClassType == NRefactory.TypeSystem.ClassType.Class) { + // Recognize field initializers: + // Convert first statement in all ctors (if all ctors have the same statement) into a field initializer. + bool allSame; + do { + Match m = fieldInitializerPattern.Match(instanceCtors[0].Body.FirstOrDefault()); + if (m == null) + break; + + FieldDefinition fieldDef = m.Get("fieldAccess").Single().Annotation(); + if (fieldDef == null) + break; + FieldDeclaration fieldDecl = typeDeclaration.Members.OfType().FirstOrDefault(f => f.Annotation() == fieldDef); + if (fieldDecl == null) + break; + + allSame = true; + for (int i = 1; i < instanceCtors.Length; i++) { + if (instanceCtors[0].Body.First().Match(instanceCtors[i].Body.FirstOrDefault()) == null) + allSame = false; + } + if (allSame) { + foreach (var ctor in instanceCtors) + ctor.Body.First().Remove(); + fieldDecl.Variables.Single().Initializer = m.Get("initializer").Single().Detach(); + } + } while (allSame); + } + + // Now convert base constructor calls to initializers: base.VisitTypeDeclaration(typeDeclaration, data); + // Remove single empty constructor: - var ctors = typeDeclaration.Members.OfType().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray(); - if (ctors.Length == 1 && ctors[0].Body.Children.Count() == 0 - && ctors[0].Initializer.ConstructorInitializerType == ConstructorInitializerType.Base - && ctors[0].Initializer.Arguments.Count() == 0 - && ctors[0].Parameters.Count == 0 - && ctors[0].Modifiers == ((typeDeclaration.Modifiers & Modifiers.Abstract) == Modifiers.Abstract ? Modifiers.Protected : Modifiers.Public)) - { - ctors[0].Remove(); + if (instanceCtors.Length == 1) { + ConstructorDeclaration emptyCtor = new ConstructorDeclaration(); + emptyCtor.Modifiers = ((typeDeclaration.Modifiers & Modifiers.Abstract) == Modifiers.Abstract ? Modifiers.Protected : Modifiers.Public); + emptyCtor.Body = new BlockStatement(); + if (emptyCtor.Match(instanceCtors[0]) != null) + instanceCtors[0].Remove(); + } + + // Convert static constructor into field initializers if the class is BeforeFieldInit + var staticCtor = typeDeclaration.Members.OfType().FirstOrDefault(c => (c.Modifiers & Modifiers.Static) == Modifiers.Static); + if (staticCtor != null) { + TypeDefinition typeDef = typeDeclaration.Annotation(); + if (typeDef != null && typeDef.IsBeforeFieldInit) { + while (true) { + ExpressionStatement es = staticCtor.Body.Statements.FirstOrDefault() as ExpressionStatement; + if (es == null) + break; + AssignmentExpression assignment = es.Expression as AssignmentExpression; + if (assignment == null || assignment.Operator != AssignmentOperatorType.Assign) + break; + FieldDefinition fieldDef = assignment.Left.Annotation(); + if (fieldDef == null || !fieldDef.IsStatic) + break; + FieldDeclaration fieldDecl = typeDeclaration.Members.OfType().FirstOrDefault(f => f.Annotation() == fieldDef); + if (fieldDecl == null) + break; + fieldDecl.Variables.Single().Initializer = assignment.Right.Detach(); + es.Remove(); + } + if (staticCtor.Body.Statements.Count == 0) + staticCtor.Remove(); + } } return null; }