mirror of https://github.com/icsharpcode/ILSpy.git
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.
124 lines
4.5 KiB
124 lines
4.5 KiB
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) |
|
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) |
|
|
|
using System; |
|
using System.Linq; |
|
using ICSharpCode.NRefactory.CSharp; |
|
using ICSharpCode.NRefactory.CSharp.PatternMatching; |
|
|
|
namespace ICSharpCode.Decompiler.Ast.Transforms |
|
{ |
|
/// <summary> |
|
/// Combines query expressions and removes transparent identifiers. |
|
/// </summary> |
|
public class CombineQueryExpressions : IAstTransform |
|
{ |
|
readonly DecompilerContext context; |
|
|
|
public CombineQueryExpressions(DecompilerContext context) |
|
{ |
|
this.context = context; |
|
} |
|
|
|
public void Run(AstNode compilationUnit) |
|
{ |
|
if (!context.Settings.QueryExpressions) |
|
return; |
|
CombineQueries(compilationUnit); |
|
} |
|
|
|
void CombineQueries(AstNode node) |
|
{ |
|
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { |
|
CombineQueries(child); |
|
} |
|
QueryExpression query = node as QueryExpression; |
|
if (query != null) { |
|
QueryFromClause fromClause = (QueryFromClause)query.Clauses.First(); |
|
QueryExpression innerQuery = fromClause.Expression as QueryExpression; |
|
if (innerQuery != null) { |
|
if (TryRemoveTransparentIdentifier(query, fromClause, innerQuery)) { |
|
RemoveTransparentIdentifierReferences(query); |
|
} |
|
} |
|
} |
|
} |
|
|
|
static readonly QuerySelectClause selectTransparentIdentifierPattern = new QuerySelectClause { |
|
Expression = new ObjectCreateExpression { |
|
Initializer = new ArrayInitializerExpression { |
|
Elements = { |
|
new NamedNode("nae1", new NamedArgumentExpression { Expression = new IdentifierExpression() }), |
|
new NamedNode("nae2", new NamedArgumentExpression { Expression = new AnyNode() }) |
|
} |
|
} |
|
}}; |
|
|
|
bool IsTransparentIdentifier(string identifier) |
|
{ |
|
return identifier.StartsWith("<>", StringComparison.Ordinal) && identifier.Contains("TransparentIdentifier"); |
|
} |
|
|
|
bool TryRemoveTransparentIdentifier(QueryExpression query, QueryFromClause fromClause, QueryExpression innerQuery) |
|
{ |
|
if (!IsTransparentIdentifier(fromClause.Identifier)) |
|
return false; |
|
Match match = selectTransparentIdentifierPattern.Match(innerQuery.Clauses.Last()); |
|
if (match == null) |
|
return false; |
|
QuerySelectClause selectClause = (QuerySelectClause)innerQuery.Clauses.Last(); |
|
NamedArgumentExpression nae1 = match.Get<NamedArgumentExpression>("nae1").Single(); |
|
NamedArgumentExpression nae2 = match.Get<NamedArgumentExpression>("nae2").Single(); |
|
if (nae1.Identifier != ((IdentifierExpression)nae1.Expression).Identifier) |
|
return false; |
|
IdentifierExpression nae2IdentExpr = nae2.Expression as IdentifierExpression; |
|
if (nae2IdentExpr != null && nae2.Identifier == nae2IdentExpr.Identifier) { |
|
// from * in (from x in ... select new { x = x, y = y }) ... |
|
// => |
|
// from x in ... ... |
|
fromClause.Remove(); |
|
selectClause.Remove(); |
|
// Move clauses from innerQuery to query |
|
QueryClause insertionPos = null; |
|
foreach (var clause in innerQuery.Clauses) { |
|
query.Clauses.InsertAfter(insertionPos, insertionPos = clause.Detach()); |
|
} |
|
} else { |
|
// from * in (from x in ... select new { x = x, y = expr }) ... |
|
// => |
|
// from x in ... let y = expr ... |
|
fromClause.Remove(); |
|
selectClause.Remove(); |
|
// Move clauses from innerQuery to query |
|
QueryClause insertionPos = null; |
|
foreach (var clause in innerQuery.Clauses) { |
|
query.Clauses.InsertAfter(insertionPos, insertionPos = clause.Detach()); |
|
} |
|
query.Clauses.InsertAfter(insertionPos, new QueryLetClause { Identifier = nae2.Identifier, Expression = nae2.Expression.Detach() }); |
|
} |
|
return true; |
|
} |
|
|
|
/// <summary> |
|
/// Removes all occurrences of transparent identifiers |
|
/// </summary> |
|
void RemoveTransparentIdentifierReferences(AstNode node) |
|
{ |
|
foreach (AstNode child in node.Children) { |
|
RemoveTransparentIdentifierReferences(child); |
|
} |
|
MemberReferenceExpression mre = node as MemberReferenceExpression; |
|
if (mre != null) { |
|
IdentifierExpression ident = mre.Target as IdentifierExpression; |
|
if (ident != null && IsTransparentIdentifier(ident.Identifier)) { |
|
IdentifierExpression newIdent = new IdentifierExpression(mre.MemberName); |
|
mre.TypeArguments.MoveTo(newIdent.TypeArguments); |
|
newIdent.CopyAnnotationsFrom(mre); |
|
newIdent.RemoveAnnotations<PropertyDeclaration>(); // remove the reference to the property of the anonymous type |
|
mre.ReplaceWith(newIdent); |
|
return; |
|
} |
|
} |
|
} |
|
} |
|
}
|
|
|