PageRenderTime 77ms CodeModel.GetById 42ms RepoModel.GetById 0ms app.codeStats 0ms

/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs

https://github.com/siegfriedpammer/ILSpy
C# | 183 lines | 134 code | 16 blank | 33 comment | 58 complexity | b061b88d39343c6f7fc7c8fd905f3afb MD5 | raw file
  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 {
  61. Target = new ThisReferenceExpression(),
  62. MemberName = Pattern.AnyString
  63. }),
  64. Operator = AssignmentOperatorType.Assign,
  65. Right = new AnyNode("initializer")
  66. }
  67. };
  68. static readonly AstNode thisCallPattern = new ExpressionStatement(new ThisReferenceExpression().Invoke(".ctor", new Repeat(new AnyNode())));
  69. public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data)
  70. {
  71. // Handle initializers on instance fields
  72. HandleInstanceFieldInitializers(typeDeclaration.Members);
  73. // Now convert base constructor calls to initializers:
  74. base.VisitTypeDeclaration(typeDeclaration, data);
  75. // Remove single empty constructor:
  76. RemoveSingleEmptyConstructor(typeDeclaration);
  77. // Handle initializers on static fields:
  78. HandleStaticFieldInitializers(typeDeclaration.Members);
  79. return null;
  80. }
  81. void HandleInstanceFieldInitializers(IEnumerable<AstNode> members)
  82. {
  83. var instanceCtors = members.OfType<ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray();
  84. var instanceCtorsNotChainingWithThis = instanceCtors.Where(ctor => !thisCallPattern.IsMatch(ctor.Body.Statements.FirstOrDefault())).ToArray();
  85. if (instanceCtorsNotChainingWithThis.Length > 0) {
  86. MethodDefinition ctorMethodDef = instanceCtorsNotChainingWithThis[0].Annotation<MethodDefinition>();
  87. if (ctorMethodDef != null && ctorMethodDef.DeclaringType.IsValueType)
  88. return;
  89. // Recognize field initializers:
  90. // Convert first statement in all ctors (if all ctors have the same statement) into a field initializer.
  91. bool allSame;
  92. do {
  93. Match m = fieldInitializerPattern.Match(instanceCtorsNotChainingWithThis[0].Body.FirstOrDefault());
  94. if (!m.Success)
  95. break;
  96. FieldDefinition fieldDef = m.Get<AstNode>("fieldAccess").Single().Annotation<FieldReference>().ResolveWithinSameModule();
  97. if (fieldDef == null)
  98. break;
  99. AstNode fieldOrEventDecl = members.FirstOrDefault(f => f.Annotation<FieldDefinition>() == fieldDef);
  100. if (fieldOrEventDecl == null)
  101. break;
  102. Expression initializer = m.Get<Expression>("initializer").Single();
  103. // 'this'/'base' cannot be used in field initializers
  104. if (initializer.DescendantsAndSelf.Any(n => n is ThisReferenceExpression || n is BaseReferenceExpression))
  105. break;
  106. allSame = true;
  107. for (int i = 1; i < instanceCtorsNotChainingWithThis.Length; i++) {
  108. if (!instanceCtors[0].Body.First().IsMatch(instanceCtorsNotChainingWithThis[i].Body.FirstOrDefault()))
  109. allSame = false;
  110. }
  111. if (allSame) {
  112. foreach (var ctor in instanceCtorsNotChainingWithThis)
  113. ctor.Body.First().Remove();
  114. fieldOrEventDecl.GetChildrenByRole(Roles.Variable).Single().Initializer = initializer.Detach();
  115. }
  116. } while (allSame);
  117. }
  118. }
  119. void RemoveSingleEmptyConstructor(TypeDeclaration typeDeclaration)
  120. {
  121. var instanceCtors = typeDeclaration.Members.OfType<ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray();
  122. if (instanceCtors.Length == 1) {
  123. ConstructorDeclaration emptyCtor = new ConstructorDeclaration();
  124. emptyCtor.Modifiers = ((typeDeclaration.Modifiers & Modifiers.Abstract) == Modifiers.Abstract ? Modifiers.Protected : Modifiers.Public);
  125. emptyCtor.Body = new BlockStatement();
  126. if (emptyCtor.IsMatch(instanceCtors[0]))
  127. instanceCtors[0].Remove();
  128. }
  129. }
  130. void HandleStaticFieldInitializers(IEnumerable<AstNode> members)
  131. {
  132. // Convert static constructor into field initializers if the class is BeforeFieldInit
  133. var staticCtor = members.OfType<ConstructorDeclaration>().FirstOrDefault(c => (c.Modifiers & Modifiers.Static) == Modifiers.Static);
  134. if (staticCtor != null) {
  135. MethodDefinition ctorMethodDef = staticCtor.Annotation<MethodDefinition>();
  136. if (ctorMethodDef != null && ctorMethodDef.DeclaringType.IsBeforeFieldInit) {
  137. while (true) {
  138. ExpressionStatement es = staticCtor.Body.Statements.FirstOrDefault() as ExpressionStatement;
  139. if (es == null)
  140. break;
  141. AssignmentExpression assignment = es.Expression as AssignmentExpression;
  142. if (assignment == null || assignment.Operator != AssignmentOperatorType.Assign)
  143. break;
  144. FieldDefinition fieldDef = assignment.Left.Annotation<FieldReference>().ResolveWithinSameModule();
  145. if (fieldDef == null || !fieldDef.IsStatic)
  146. break;
  147. FieldDeclaration fieldDecl = members.OfType<FieldDeclaration>().FirstOrDefault(f => f.Annotation<FieldDefinition>() == fieldDef);
  148. if (fieldDecl == null)
  149. break;
  150. fieldDecl.Variables.Single().Initializer = assignment.Right.Detach();
  151. es.Remove();
  152. }
  153. if (staticCtor.Body.Statements.Count == 0)
  154. staticCtor.Remove();
  155. }
  156. }
  157. }
  158. void IAstTransform.Run(AstNode node)
  159. {
  160. // If we're viewing some set of members (fields are direct children of CompilationUnit),
  161. // we also need to handle those:
  162. HandleInstanceFieldInitializers(node.Children);
  163. HandleStaticFieldInitializers(node.Children);
  164. node.AcceptVisitor(this, null);
  165. }
  166. }
  167. }