Browse Source

Reimplement IntroduceQueryExpressions and CombineQueryExpressions

pull/850/head
Siegfried Pammer 8 years ago
parent
commit
a1256392ac
  1. 1
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  2. 6
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  3. 184
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.cs
  4. 4742
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.il
  5. 3967
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.opt.il
  6. 4471
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.opt.roslyn.il
  7. 4636
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.roslyn.il
  8. 4
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  9. 37
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  10. 15
      ICSharpCode.Decompiler/CSharp/Transforms/CombineQueryExpressions.cs
  11. 2
      ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs
  12. 22
      ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs
  13. 2
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

1
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -86,6 +86,7 @@ @@ -86,6 +86,7 @@
<Compile Include="TestCases\Pretty\HelloWorld.cs" />
<Compile Include="TestCases\Pretty\InlineAssignmentTest.cs" />
<Compile Include="TestCases\Pretty\PropertiesAndEvents.cs" />
<Compile Include="TestCases\Pretty\QueryExpressions.cs" />
<Compile Include="TestCases\Pretty\ShortCircuit.cs" />
<Compile Include="TestTraceListener.cs" />
<Compile Include="Util\IntervalTests.cs" />

6
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -133,6 +133,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -133,6 +133,12 @@ namespace ICSharpCode.Decompiler.Tests
Run(cscOptions: cscOptions);
}
[Test]
public void QueryExpressions([ValueSource("defaultOptions")] CompilerOptions cscOptions)
{
Run(cscOptions: cscOptions);
}
void Run([CallerMemberName] string testName = null, AssemblerOptions asmOptions = AssemblerOptions.None, CompilerOptions cscOptions = CompilerOptions.None)
{
var ilFile = Path.Combine(TestCasePath, testName);

184
ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.cs

@ -0,0 +1,184 @@ @@ -0,0 +1,184 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public class QueryExpressions
{
public class Customer
{
public int CustomerID;
public IEnumerable<Order> Orders;
public string Name;
public string Country;
public string City;
}
public class Order
{
public int OrderID;
public DateTime OrderDate;
public Customer Customer;
public int CustomerID;
public decimal Total;
public IEnumerable<OrderDetail> Details;
}
public class OrderDetail
{
public decimal UnitPrice;
public int Quantity;
}
public IEnumerable<Customer> customers;
public IEnumerable<Order> orders;
public object MultipleWhere()
{
return from c in this.customers
where c.Orders.Count() > 10
where c.Country == "DE"
select c;
}
public object SelectManyFollowedBySelect()
{
return from c in this.customers
from o in c.Orders
select new {
c.Name,
o.OrderID,
o.Total
};
}
public object SelectManyFollowedByOrderBy()
{
return from c in this.customers
from o in c.Orders
orderby o.Total descending
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 from o in this.orders
let t = o.Details.Sum((Func<OrderDetail, decimal>)((OrderDetail d) => d.UnitPrice * d.Quantity))
where t >= 1000m
select new {
OrderID = o.OrderID,
Total = t
};
}
public object MultipleLet()
{
return from a in this.customers
let b = a.Country
let c = a.Name
select b + c;
}
public object Join()
{
return from c in this.customers
join o in this.orders on c.CustomerID equals o.CustomerID
select new {
c.Name,
o.OrderDate,
o.Total
};
}
public object JoinInto()
{
return from c in this.customers
join o in this.orders on c.CustomerID equals o.CustomerID into co
let n = co.Count()
// should be n >= 10
where !(n < 10)
select new {
Name = c.Name,
OrderCount = n
};
}
public object OrderBy()
{
return from o in this.orders
orderby o.Customer.Name, o.Total descending
select o;
}
public object GroupBy()
{
return from c in this.customers
group c.Name by c.Country;
}
public object ExplicitType()
{
return from Customer c in this.customers
where c.City == "London"
select c;
}
public object QueryContinuation()
{
return from c in this.customers
group c by c.Country into g
select new {
Country = g.Key,
CustCount = g.Count()
};
}
}
}

4742
ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.il

File diff suppressed because it is too large Load Diff

3967
ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.opt.il

File diff suppressed because it is too large Load Diff

4471
ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.opt.roslyn.il

File diff suppressed because it is too large Load Diff

4636
ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.roslyn.il

File diff suppressed because it is too large Load Diff

4
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -133,8 +133,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -133,8 +133,8 @@ namespace ICSharpCode.Decompiler.CSharp
new DecimalConstantTransform(),
new IntroduceUsingDeclarations(),
new IntroduceExtensionMethods(), // must run after IntroduceUsingDeclarations
//new IntroduceQueryExpressions(context), // must run after IntroduceExtensionMethods
//new CombineQueryExpressions(context),
new IntroduceQueryExpressions(), // must run after IntroduceExtensionMethods
new CombineQueryExpressions(),
//new FlattenSwitchBlocks(),
new FixNameCollisions(),
new AddXmlDocumentationTransform(),

37
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -1138,10 +1138,11 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1138,10 +1138,11 @@ namespace ICSharpCode.Decompiler.CSharp
} else {
var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentAssembly);
var result = lookup.Lookup(target.ResolveResult, method.Name, EmptyList<IType>.Instance, true) as MethodGroupResolveResult;
bool requireTypeArguments = false;
IType[] typeArguments = Array.Empty<IType>();
// 1.
// Try overload resolution and check if the correct call is selected with the given casts.
// If anything goes wrong, apply explicit casts to all arguments.
if (result == null) {
for (int i = 0; i < arguments.Length; i++) {
if (!method.Parameters[i].Type.ContainsAnonymousType())
@ -1150,15 +1151,25 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1150,15 +1151,25 @@ namespace ICSharpCode.Decompiler.CSharp
} else {
var or = new OverloadResolution(resolver.Compilation, arguments.Select(a => a.ResolveResult).ToArray());
or.AddMethodLists(result.MethodsGroupedByDeclaringType.ToArray());
if (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(method, or.BestCandidate, inst.OpCode == OpCode.CallVirt)) {
for (int i = 0; i < arguments.Length; i++) {
if (!method.Parameters[i].Type.ContainsAnonymousType())
arguments[i] = arguments[i].ConvertTo(method.Parameters[i].Type, this);
}
switch (or.BestCandidateErrors) {
case OverloadResolutionErrors.TypeInferenceFailed:
case OverloadResolutionErrors.WrongNumberOfTypeArguments:
requireTypeArguments = true;
typeArguments = method.TypeArguments.ToArray();
break;
default:
if (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(method, or.GetBestCandidateWithSubstitutedTypeArguments(), inst.OpCode == OpCode.CallVirt)) {
for (int i = 0; i < arguments.Length; i++) {
if (!method.Parameters[i].Type.ContainsAnonymousType())
arguments[i] = arguments[i].ConvertTo(method.Parameters[i].Type, this);
}
}
break;
}
}
result = lookup.Lookup(target.ResolveResult, method.Name, EmptyList<IType>.Instance, true) as MethodGroupResolveResult;
result = lookup.Lookup(target.ResolveResult, method.Name, typeArguments, true) as MethodGroupResolveResult;
// 2.
// Try overload resolution again and if anything goes wrong,
@ -1166,14 +1177,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1166,14 +1177,12 @@ namespace ICSharpCode.Decompiler.CSharp
if (result == null) {
target = target.ConvertTo(method.DeclaringType, this);
} else {
var or = new OverloadResolution(resolver.Compilation, arguments.Select(a => a.ResolveResult).ToArray());
var or = new OverloadResolution(resolver.Compilation, arguments.Select(a => a.ResolveResult).ToArray(), typeArguments: typeArguments);
or.AddMethodLists(result.MethodsGroupedByDeclaringType.ToArray());
if (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(method, or.BestCandidate, inst.OpCode == OpCode.CallVirt))
if (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(method, or.GetBestCandidateWithSubstitutedTypeArguments(), inst.OpCode == OpCode.CallVirt))
target = target.ConvertTo(method.DeclaringType, this);
}
bool requireTypeArguments = false;
result = lookup.Lookup(target.ResolveResult, method.Name, EmptyList<IType>.Instance, true) as MethodGroupResolveResult;
result = lookup.Lookup(target.ResolveResult, method.Name, typeArguments, true) as MethodGroupResolveResult;
// 3.
// Try overload resolution again and if anything goes wrong,
@ -1181,9 +1190,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1181,9 +1190,9 @@ namespace ICSharpCode.Decompiler.CSharp
if (result == null) {
requireTypeArguments = true;
} else {
var or = new OverloadResolution(resolver.Compilation, arguments.Select(a => a.ResolveResult).ToArray());
var or = new OverloadResolution(resolver.Compilation, arguments.Select(a => a.ResolveResult).ToArray(), typeArguments: typeArguments);
or.AddMethodLists(result.MethodsGroupedByDeclaringType.ToArray());
if (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(method, or.BestCandidate, inst.OpCode == OpCode.CallVirt))
if (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(method, or.GetBestCandidateWithSubstitutedTypeArguments(), inst.OpCode == OpCode.CallVirt))
requireTypeArguments = true;
}

15
ICSharpCode.Decompiler/CSharp/Transforms/CombineQueryExpressions.cs

@ -18,8 +18,8 @@ @@ -18,8 +18,8 @@
using System;
using System.Linq;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.PatternMatching;
using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching;
namespace ICSharpCode.Decompiler.CSharp.Transforms
{
@ -28,18 +28,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -28,18 +28,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
/// </summary>
public class CombineQueryExpressions : IAstTransform
{
readonly DecompilerContext context;
public CombineQueryExpressions(DecompilerContext context)
{
this.context = context;
}
public void Run(AstNode compilationUnit)
public void Run(AstNode rootNode, TransformContext context)
{
if (!context.Settings.QueryExpressions)
return;
CombineQueries(compilationUnit);
CombineQueries(rootNode);
}
static readonly InvocationExpression castPattern = new InvocationExpression {

2
ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs

@ -128,7 +128,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -128,7 +128,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
var firstArgument = invocationExpression.Arguments.First();
var target = firstArgument.GetResolveResult();
var args = invocationExpression.Arguments.Skip(1).Select(a => a.GetResolveResult()).ToArray();
var rr = resolver.ResolveMemberAccess(target, method.Name, EmptyList<IType>.Instance, NameLookupMode.InvocationTarget) as MethodGroupResolveResult;
var rr = resolver.ResolveMemberAccess(target, method.Name, method.TypeArguments, NameLookupMode.InvocationTarget) as MethodGroupResolveResult;
if (rr == null)
return;
var or = rr.PerformOverloadResolution(resolveContext.Compilation, args, allowExtensionMethods: true);

22
ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs

@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
using System;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.Decompiler.CSharp.Syntax;
namespace ICSharpCode.Decompiler.CSharp.Transforms
{
@ -29,21 +29,14 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -29,21 +29,14 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
/// </summary>
public class IntroduceQueryExpressions : IAstTransform
{
readonly DecompilerContext context;
public IntroduceQueryExpressions(DecompilerContext context)
{
this.context = context;
}
public void Run(AstNode compilationUnit)
public void Run(AstNode rootNode, TransformContext context)
{
if (!context.Settings.QueryExpressions)
return;
DecompileQueries(compilationUnit);
DecompileQueries(rootNode);
// 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>()) {
foreach (QueryExpression query in rootNode.Descendants.OfType<QueryExpression>()) {
QueryFromClause fromClause = (QueryFromClause)query.Clauses.First();
if (IsDegenerateQuery(query)) {
// introduce select for degenerate query
@ -278,7 +271,12 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -278,7 +271,12 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
/// <summary>Matches simple lambdas of the form "a => b"</summary>
bool MatchSimpleLambda(Expression expr, out string parameterName, out Expression body)
{
LambdaExpression lambda = expr as LambdaExpression;
// HACK : remove workaround after all unnecessary casts are eliminated.
LambdaExpression lambda;
if (expr is CastExpression cast)
lambda = cast.Expression as LambdaExpression;
else
lambda = expr as LambdaExpression;
if (lambda != null && lambda.Parameters.Count == 1 && lambda.Body is Expression) {
ParameterDeclaration p = lambda.Parameters.Single();
if (p.ParameterModifier == ParameterModifier.None) {

2
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -218,7 +218,9 @@ @@ -218,7 +218,9 @@
<Compile Include="CSharp\Resolver\ReducedExtensionMethod.cs" />
<Compile Include="CSharp\Resolver\RenameCallbackArguments.cs" />
<Compile Include="CSharp\Resolver\TypeInference.cs" />
<Compile Include="CSharp\Transforms\CombineQueryExpressions.cs" />
<Compile Include="CSharp\Transforms\IntroduceExtensionMethods.cs" />
<Compile Include="CSharp\Transforms\IntroduceQueryExpressions.cs" />
<Compile Include="CSharp\Transforms\RemoveCLSCompliantAttribute.cs" />
<Compile Include="CSharp\TranslationContext.cs" />
<Compile Include="CSharp\TypeSystem\AliasNamespaceReference.cs" />

Loading…
Cancel
Save