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.

173 lines
6.7 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.Linq;
  20. using ICSharpCode.NRefactory.CSharp;
  21. using ICSharpCode.NRefactory.PatternMatching;
  22. namespace ICSharpCode.Decompiler.Ast.Transforms
  23. {
  24. /// <summary>
  25. /// Combines query expressions and removes transparent identifiers.
  26. /// </summary>
  27. public class CombineQueryExpressions : IAstTransform
  28. {
  29. readonly DecompilerContext context;
  30. public CombineQueryExpressions(DecompilerContext context)
  31. {
  32. this.context = context;
  33. }
  34. public void Run(AstNode compilationUnit)
  35. {
  36. if (!context.Settings.QueryExpressions)
  37. return;
  38. CombineQueries(compilationUnit);
  39. }
  40. static readonly InvocationExpression castPattern = new InvocationExpression {
  41. Target = new MemberReferenceExpression {
  42. Target = new AnyNode("inExpr"),
  43. MemberName = "Cast",
  44. TypeArguments = { new AnyNode("targetType") }
  45. }};
  46. void CombineQueries(AstNode node)
  47. {
  48. for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
  49. CombineQueries(child);
  50. }
  51. QueryExpression query = node as QueryExpression;
  52. if (query != null) {
  53. QueryFromClause fromClause = (QueryFromClause)query.Clauses.First();
  54. QueryExpression innerQuery = fromClause.Expression as QueryExpression;
  55. if (innerQuery != null) {
  56. if (TryRemoveTransparentIdentifier(query, fromClause, innerQuery)) {
  57. RemoveTransparentIdentifierReferences(query);
  58. } else {
  59. QueryContinuationClause continuation = new QueryContinuationClause();
  60. continuation.PrecedingQuery = innerQuery.Detach();
  61. continuation.Identifier = fromClause.Identifier;
  62. fromClause.ReplaceWith(continuation);
  63. }
  64. } else {
  65. Match m = castPattern.Match(fromClause.Expression);
  66. if (m.Success) {
  67. fromClause.Type = m.Get<AstType>("targetType").Single().Detach();
  68. fromClause.Expression = m.Get<Expression>("inExpr").Single().Detach();
  69. }
  70. }
  71. }
  72. }
  73. static readonly QuerySelectClause selectTransparentIdentifierPattern = new QuerySelectClause {
  74. Expression = new Choice {
  75. new AnonymousTypeCreateExpression {
  76. Initializers = {
  77. new NamedNode("nae1", new NamedExpression { Expression = new IdentifierExpression() }),
  78. new NamedNode("nae2", new NamedExpression { Expression = new AnyNode("nae2Expr") })
  79. }
  80. },
  81. new AnonymousTypeCreateExpression {
  82. Initializers = {
  83. new NamedNode("identifier", new IdentifierExpression()),
  84. new AnyNode("nae2Expr")
  85. }
  86. }
  87. }};
  88. bool IsTransparentIdentifier(string identifier)
  89. {
  90. return identifier.StartsWith("<>", StringComparison.Ordinal) && identifier.Contains("TransparentIdentifier");
  91. }
  92. bool TryRemoveTransparentIdentifier(QueryExpression query, QueryFromClause fromClause, QueryExpression innerQuery)
  93. {
  94. if (!IsTransparentIdentifier(fromClause.Identifier))
  95. return false;
  96. Match match = selectTransparentIdentifierPattern.Match(innerQuery.Clauses.Last());
  97. if (!match.Success)
  98. return false;
  99. QuerySelectClause selectClause = (QuerySelectClause)innerQuery.Clauses.Last();
  100. NamedExpression nae1 = match.Get<NamedExpression>("nae1").SingleOrDefault();
  101. NamedExpression nae2 = match.Get<NamedExpression>("nae2").SingleOrDefault();
  102. if (nae1 != null && nae1.Identifier != ((IdentifierExpression)nae1.Expression).Identifier)
  103. return false;
  104. Expression nae2Expr = match.Get<Expression>("nae2Expr").Single();
  105. IdentifierExpression nae2IdentExpr = nae2Expr as IdentifierExpression;
  106. if (nae2IdentExpr != null && (nae2 == null || nae2.Identifier == nae2IdentExpr.Identifier)) {
  107. // from * in (from x in ... select new { x = x, y = y }) ...
  108. // =>
  109. // from x in ... ...
  110. fromClause.Remove();
  111. selectClause.Remove();
  112. // Move clauses from innerQuery to query
  113. QueryClause insertionPos = null;
  114. foreach (var clause in innerQuery.Clauses) {
  115. query.Clauses.InsertAfter(insertionPos, insertionPos = clause.Detach());
  116. }
  117. } else {
  118. // from * in (from x in ... select new { x = x, y = expr }) ...
  119. // =>
  120. // from x in ... let y = expr ...
  121. fromClause.Remove();
  122. selectClause.Remove();
  123. // Move clauses from innerQuery to query
  124. QueryClause insertionPos = null;
  125. foreach (var clause in innerQuery.Clauses) {
  126. query.Clauses.InsertAfter(insertionPos, insertionPos = clause.Detach());
  127. }
  128. string ident;
  129. if (nae2 != null)
  130. ident = nae2.Identifier;
  131. else if (nae2Expr is IdentifierExpression)
  132. ident = ((IdentifierExpression)nae2Expr).Identifier;
  133. else if (nae2Expr is MemberReferenceExpression)
  134. ident = ((MemberReferenceExpression)nae2Expr).MemberName;
  135. else
  136. throw new InvalidOperationException("Could not infer name from initializer in AnonymousTypeCreateExpression");
  137. query.Clauses.InsertAfter(insertionPos, new QueryLetClause { Identifier = ident, Expression = nae2Expr.Detach() });
  138. }
  139. return true;
  140. }
  141. /// <summary>
  142. /// Removes all occurrences of transparent identifiers
  143. /// </summary>
  144. void RemoveTransparentIdentifierReferences(AstNode node)
  145. {
  146. foreach (AstNode child in node.Children) {
  147. RemoveTransparentIdentifierReferences(child);
  148. }
  149. MemberReferenceExpression mre = node as MemberReferenceExpression;
  150. if (mre != null) {
  151. IdentifierExpression ident = mre.Target as IdentifierExpression;
  152. if (ident != null && IsTransparentIdentifier(ident.Identifier)) {
  153. IdentifierExpression newIdent = new IdentifierExpression(mre.MemberName);
  154. mre.TypeArguments.MoveTo(newIdent.TypeArguments);
  155. newIdent.CopyAnnotationsFrom(mre);
  156. newIdent.RemoveAnnotations<PropertyDeclaration>(); // remove the reference to the property of the anonymous type
  157. mre.ReplaceWith(newIdent);
  158. return;
  159. }
  160. }
  161. }
  162. }
  163. }