Browse Source

Get rid of transparent identifiers in query expressions.

pull/118/head
Daniel Grunwald 14 years ago
parent
commit
6f4fdd00f7
  1. 124
      ICSharpCode.Decompiler/Ast/Transforms/CombineQueryExpressions.cs
  2. 3
      ICSharpCode.Decompiler/Ast/Transforms/IntroduceExtensionMethods.cs
  3. 4
      ICSharpCode.Decompiler/Ast/Transforms/IntroduceQueryExpressions.cs
  4. 1
      ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs
  5. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  6. 23
      ICSharpCode.Decompiler/Tests/QueryExpressions.cs

124
ICSharpCode.Decompiler/Ast/Transforms/CombineQueryExpressions.cs

@ -0,0 +1,124 @@ @@ -0,0 +1,124 @@
// 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;
}
}
}
}
}

3
ICSharpCode.Decompiler/Ast/Transforms/IntroduceExtensionMethods.cs

@ -31,7 +31,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -31,7 +31,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
foreach (var ca in d.CustomAttributes) {
if (ca.AttributeType.Name == "ExtensionAttribute" && ca.AttributeType.Namespace == "System.Runtime.CompilerServices") {
mre.Target = invocation.Arguments.First().Detach();
mre.TypeArguments.Clear();
if (invocation.Arguments.Any())
mre.TypeArguments.Clear();
break;
}
}

4
ICSharpCode.Decompiler/Ast/Transforms/IntroduceQueryExpressions.cs

@ -26,12 +26,16 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -26,12 +26,16 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
if (!context.Settings.QueryExpressions)
return;
DecompileQueries(compilationUnit);
// After all queries were decompiled, detect degenerate queries (queries not property terminated with 'select' or 'group')
// and fix them, either by adding a degenerate select, or by combining them with another query.
foreach (QueryExpression query in compilationUnit.Descendants.OfType<QueryExpression>()) {
QueryFromClause fromClause = (QueryFromClause)query.Clauses.First();
if (IsDegenerateQuery(query)) {
// introduce select for degenerate query
query.Clauses.Add(new QuerySelectClause { Expression = new IdentifierExpression(fromClause.Identifier) });
}
// See if the data source of this query is a degenerate query,
// and combine the queries if possible.
QueryExpression innerQuery = fromClause.Expression as QueryExpression;
while (IsDegenerateQuery(innerQuery)) {
QueryFromClause innerFromClause = (QueryFromClause)innerQuery.Clauses.First();

1
ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs

@ -28,6 +28,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -28,6 +28,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
new IntroduceUsingDeclarations(context),
new IntroduceExtensionMethods(context), // must run after IntroduceUsingDeclarations
new IntroduceQueryExpressions(context), // must run after IntroduceExtensionMethods
new CombineQueryExpressions(context),
};
}

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -58,6 +58,7 @@ @@ -58,6 +58,7 @@
<Compile Include="Ast\NRefactoryExtensions.cs" />
<Compile Include="Ast\TextOutputFormatter.cs" />
<Compile Include="Ast\Transforms\AddCheckedBlocks.cs" />
<Compile Include="Ast\Transforms\CombineQueryExpressions.cs" />
<Compile Include="Ast\Transforms\ContextTrackingVisitor.cs" />
<Compile Include="Ast\Transforms\ConvertConstructorCallIntoInitializer.cs" />
<Compile Include="Ast\Transforms\DeclareVariables.cs" />

23
ICSharpCode.Decompiler/Tests/QueryExpressions.cs

@ -43,7 +43,7 @@ public class QueryExpressions @@ -43,7 +43,7 @@ public class QueryExpressions
select c;
}
public object MultipleFromFollowedBySelect()
public object SelectManyFollowedBySelect()
{
return
from c in this.customers
@ -51,7 +51,7 @@ public class QueryExpressions @@ -51,7 +51,7 @@ public class QueryExpressions
select new { c.Name, o.OrderID, o.Total };
}
public object MultipleFromFollowedByOrderBy()
public object SelectManyFollowedByOrderBy()
{
return
from c in this.customers
@ -60,6 +60,25 @@ public class QueryExpressions @@ -60,6 +60,25 @@ public class QueryExpressions
select new { c.Name, o.OrderID, o.Total };
}
public object MultipleSelectManyFollowedBySelect()
{
return
from c in this.customers
from o in c.Orders
from d in o.Details
select new { c.Name, o.OrderID, d.Quantity };
}
public object MultipleSelectManyFollowedByLet()
{
return
from c in this.customers
from o in c.Orders
from d in o.Details
let x = d.Quantity * d.UnitPrice
select new { c.Name, o.OrderID, x };
}
public object FromLetWhereSelect()
{
return

Loading…
Cancel
Save