You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

180 lines
8.0 KiB

  1. // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy of this
  4. // software and associated documentation files (the "Software"), to deal in the Software
  5. // without restriction, including without limitation the rights to use, copy, modify, merge,
  6. // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
  7. // to whom the Software is furnished to do so, subject to the following conditions:
  8. //
  9. // The above copyright notice and this permission notice shall be included in all copies or
  10. // substantial portions of the Software.
  11. //
  12. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  13. // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  14. // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
  15. // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  16. // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  17. // DEALINGS IN THE SOFTWARE.
  18. using System;
  19. using System.Collections.Generic;
  20. using System.Linq;
  21. using ICSharpCode.NRefactory.CSharp;
  22. using ICSharpCode.NRefactory.PatternMatching;
  23. using Mono.Cecil;
  24. namespace ICSharpCode.Decompiler.Ast.Transforms
  25. {
  26. /// <summary>
  27. /// If the first element of a constructor is a chained constructor call, convert it into a constructor initializer.
  28. /// </summary>
  29. public class ConvertConstructorCallIntoInitializer : DepthFirstAstVisitor<object, object>, IAstTransform
  30. {
  31. public override object VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data)
  32. {
  33. ExpressionStatement stmt = constructorDeclaration.Body.Statements.FirstOrDefault() as ExpressionStatement;
  34. if (stmt == null)
  35. return null;
  36. InvocationExpression invocation = stmt.Expression as InvocationExpression;
  37. if (invocation == null)
  38. return null;
  39. MemberReferenceExpression mre = invocation.Target as MemberReferenceExpression;
  40. if (mre != null && mre.MemberName == ".ctor") {
  41. ConstructorInitializer ci = new ConstructorInitializer();
  42. if (mre.Target is ThisReferenceExpression)
  43. ci.ConstructorInitializerType = ConstructorInitializerType.This;
  44. else if (mre.Target is BaseReferenceExpression)
  45. ci.ConstructorInitializerType = ConstructorInitializerType.Base;
  46. else
  47. return null;
  48. // Move arguments from invocation to initializer:
  49. invocation.Arguments.MoveTo(ci.Arguments);
  50. // Add the initializer: (unless it is the default 'base()')
  51. if (!(ci.ConstructorInitializerType == ConstructorInitializerType.Base && ci.Arguments.Count == 0))
  52. constructorDeclaration.Initializer = ci.WithAnnotation(invocation.Annotation<MethodReference>());
  53. // Remove the statement:
  54. stmt.Remove();
  55. }
  56. return null;
  57. }
  58. static readonly ExpressionStatement fieldInitializerPattern = new ExpressionStatement {
  59. Expression = new AssignmentExpression {
  60. Left = new NamedNode("fieldAccess", new MemberReferenceExpression { Target = new ThisReferenceExpression() }),
  61. Operator = AssignmentOperatorType.Assign,
  62. Right = new AnyNode("initializer")
  63. }
  64. };
  65. static readonly AstNode thisCallPattern = new ExpressionStatement(new ThisReferenceExpression().Invoke(".ctor", new Repeat(new AnyNode())));
  66. public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data)
  67. {
  68. // Handle initializers on instance fields
  69. HandleInstanceFieldInitializers(typeDeclaration.Members);
  70. // Now convert base constructor calls to initializers:
  71. base.VisitTypeDeclaration(typeDeclaration, data);
  72. // Remove single empty constructor:
  73. RemoveSingleEmptyConstructor(typeDeclaration);
  74. // Handle initializers on static fields:
  75. HandleStaticFieldInitializers(typeDeclaration.Members);
  76. return null;
  77. }
  78. void HandleInstanceFieldInitializers(IEnumerable<AstNode> members)
  79. {
  80. var instanceCtors = members.OfType<ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray();
  81. var instanceCtorsNotChainingWithThis = instanceCtors.Where(ctor => !thisCallPattern.IsMatch(ctor.Body.Statements.FirstOrDefault())).ToArray();
  82. if (instanceCtorsNotChainingWithThis.Length > 0) {
  83. MethodDefinition ctorMethodDef = instanceCtorsNotChainingWithThis[0].Annotation<MethodDefinition>();
  84. if (ctorMethodDef != null && ctorMethodDef.DeclaringType.IsValueType)
  85. return;
  86. // Recognize field initializers:
  87. // Convert first statement in all ctors (if all ctors have the same statement) into a field initializer.
  88. bool allSame;
  89. do {
  90. Match m = fieldInitializerPattern.Match(instanceCtorsNotChainingWithThis[0].Body.FirstOrDefault());
  91. if (!m.Success)
  92. break;
  93. FieldDefinition fieldDef = m.Get<AstNode>("fieldAccess").Single().Annotation<FieldReference>().ResolveWithinSameModule();
  94. if (fieldDef == null)
  95. break;
  96. AstNode fieldOrEventDecl = members.FirstOrDefault(f => f.Annotation<FieldDefinition>() == fieldDef);
  97. if (fieldOrEventDecl == null)
  98. break;
  99. Expression initializer = m.Get<Expression>("initializer").Single();
  100. // 'this'/'base' cannot be used in field initializers
  101. if (initializer.DescendantsAndSelf.Any(n => n is ThisReferenceExpression || n is BaseReferenceExpression))
  102. break;
  103. allSame = true;
  104. for (int i = 1; i < instanceCtorsNotChainingWithThis.Length; i++) {
  105. if (!instanceCtors[0].Body.First().IsMatch(instanceCtorsNotChainingWithThis[i].Body.FirstOrDefault()))
  106. allSame = false;
  107. }
  108. if (allSame) {
  109. foreach (var ctor in instanceCtorsNotChainingWithThis)
  110. ctor.Body.First().Remove();
  111. fieldOrEventDecl.GetChildrenByRole(AstNode.Roles.Variable).Single().Initializer = initializer.Detach();
  112. }
  113. } while (allSame);
  114. }
  115. }
  116. void RemoveSingleEmptyConstructor(TypeDeclaration typeDeclaration)
  117. {
  118. var instanceCtors = typeDeclaration.Members.OfType<ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray();
  119. if (instanceCtors.Length == 1) {
  120. ConstructorDeclaration emptyCtor = new ConstructorDeclaration();
  121. emptyCtor.Modifiers = ((typeDeclaration.Modifiers & Modifiers.Abstract) == Modifiers.Abstract ? Modifiers.Protected : Modifiers.Public);
  122. emptyCtor.Body = new BlockStatement();
  123. if (emptyCtor.IsMatch(instanceCtors[0]))
  124. instanceCtors[0].Remove();
  125. }
  126. }
  127. void HandleStaticFieldInitializers(IEnumerable<AstNode> members)
  128. {
  129. // Convert static constructor into field initializers if the class is BeforeFieldInit
  130. var staticCtor = members.OfType<ConstructorDeclaration>().FirstOrDefault(c => (c.Modifiers & Modifiers.Static) == Modifiers.Static);
  131. if (staticCtor != null) {
  132. MethodDefinition ctorMethodDef = staticCtor.Annotation<MethodDefinition>();
  133. if (ctorMethodDef != null && ctorMethodDef.DeclaringType.IsBeforeFieldInit) {
  134. while (true) {
  135. ExpressionStatement es = staticCtor.Body.Statements.FirstOrDefault() as ExpressionStatement;
  136. if (es == null)
  137. break;
  138. AssignmentExpression assignment = es.Expression as AssignmentExpression;
  139. if (assignment == null || assignment.Operator != AssignmentOperatorType.Assign)
  140. break;
  141. FieldDefinition fieldDef = assignment.Left.Annotation<FieldReference>().ResolveWithinSameModule();
  142. if (fieldDef == null || !fieldDef.IsStatic)
  143. break;
  144. FieldDeclaration fieldDecl = members.OfType<FieldDeclaration>().FirstOrDefault(f => f.Annotation<FieldDefinition>() == fieldDef);
  145. if (fieldDecl == null)
  146. break;
  147. fieldDecl.Variables.Single().Initializer = assignment.Right.Detach();
  148. es.Remove();
  149. }
  150. if (staticCtor.Body.Statements.Count == 0)
  151. staticCtor.Remove();
  152. }
  153. }
  154. }
  155. void IAstTransform.Run(AstNode node)
  156. {
  157. // If we're viewing some set of members (fields are direct children of CompilationUnit),
  158. // we also need to handle those:
  159. HandleInstanceFieldInitializers(node.Children);
  160. HandleStaticFieldInitializers(node.Children);
  161. node.AcceptVisitor(this, null);
  162. }
  163. }
  164. }