Skip to content

Commit

Permalink
Cache substituted lambdas
Browse files Browse the repository at this point in the history
See #135
  • Loading branch information
hazzik committed Aug 9, 2019
1 parent bb03a23 commit f775f17
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 43 deletions.
18 changes: 13 additions & 5 deletions src/DelegateDecompiler.Tests/DecompilerTestsBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace DelegateDecompiler.Tests
{
public class DecompilerTestsBase
{
private static readonly Func<Expression, string> debugView = BuildDebugView();
static readonly Func<Expression, string> DebugView = BuildDebugView();

private static Func<Expression, string> BuildDebugView()
{
Expand All @@ -25,7 +25,7 @@ protected static void Test<T>(Expression<T> expected, T compiled)
var y = decompiled.Body.ToString();
Console.WriteLine(y);
Assert.AreEqual(x, y);
Assert.AreEqual(debugView(expected.Body), debugView(decompiled.Body));
Assert.AreEqual(DebugView(expected.Body), DebugView(decompiled.Body));
}

protected static void Test<T>(Expression<T> expected, MethodInfo compiled)
Expand All @@ -38,10 +38,10 @@ protected static void Test<T>(Expression<T> expected, MethodInfo compiled)
var y = decompiled.Body.ToString();
Console.WriteLine(y);
Assert.AreEqual(x, y);
Assert.AreEqual(debugView(expected.Body), debugView(decompiled.Body));
Assert.AreEqual(DebugView(expected.Body), DebugView(decompiled.Body));
}

protected static void Test<T>(Expression<T> expected1, Expression<T> expected2, T compiled)
protected static void Test<T>(Expression<T> expected1, Expression<T> expected2, T compiled, bool compareDebugView = true)
{
//Double cast required as we can not convert T to Delegate directly
var decompiled = ((Delegate) ((object) compiled)).Decompile();
Expand All @@ -53,7 +53,15 @@ protected static void Test<T>(Expression<T> expected1, Expression<T> expected2,
var y = decompiled.Body.ToString();
Console.WriteLine(y);
Assert.That(y, Is.EqualTo(x1).Or.EqualTo(x2));
Assert.That(debugView(decompiled.Body), Is.EqualTo(debugView(expected1.Body)).Or.EqualTo(debugView(expected2.Body)));
if (compareDebugView)
Assert.That(DebugView(decompiled.Body), Is.EqualTo(DebugView(expected1.Body)).Or.EqualTo(DebugView(expected2.Body)));
}

protected static void AssertAreEqual(Expression expected, Expression actual, bool compareDebugView = true)
{
Assert.AreEqual(expected.ToString(), actual.ToString());
if (compareDebugView)
Assert.AreEqual(DebugView(expected), DebugView(actual));
}
}
}
141 changes: 141 additions & 0 deletions src/DelegateDecompiler.Tests/Issue135.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using NUnit.Framework;

namespace DelegateDecompiler.Tests
{
[TestFixture]
public class Issue135 : DecompilerTestsBase
{
public class Post {
public bool IsActive { get; set; }
}

public class Blog
{
public bool HasBar { get; }
public bool HasBaz { get; }

public IEnumerable<Post> Posts { get; }


[Decompile]
public bool HasFoo
{
get { return (HasBar || HasBaz) && Posts.Any(x => x.IsActive); }
}

[Decompile]
public bool HasFoo2
{
get { return (HasBar && Posts.Any(x => x.IsActive)) || (HasBaz && Posts.Any(x => x.IsActive)); }
}

[Decompile]
public bool HasFoo3
{
get { return Posts.Any(x => x.IsActive) && (HasBar || HasBaz); }
}
}

[Test]
public void Test1()
{
Expression<Func<Blog, bool>> expected1 = b =>
(b.HasBar || b.HasBaz) && b.Posts.Any(x => x.IsActive);

Expression<Func<Blog, bool>> expected2 = b =>
b.HasBar ? b.Posts.Any(x => x.IsActive) : b.HasBaz && b.Posts.Any(x => x.IsActive);

Func<Blog, bool> actual = b =>
(b.HasBar || b.HasBaz) && b.Posts.Any(x => x.IsActive);

Test(expected1, expected2, actual, false);
}

[Test]
public void Test2()
{
Expression<Func<Blog, bool>> expected1 = b =>
b.HasBar && b.Posts.Any(x => x.IsActive) || b.HasBaz && b.Posts.Any(x => x.IsActive);

Expression<Func<Blog, bool>> expected2 = b =>
b.HasBar
? b.Posts.Any(x => x.IsActive) || b.HasBaz && b.Posts.Any(x => x.IsActive)
: b.HasBaz && b.Posts.Any(x => x.IsActive);

Func<Blog, bool> actual = b =>
b.HasBar && b.Posts.Any(x => x.IsActive) || b.HasBaz && b.Posts.Any(x => x.IsActive);

Test(expected1, expected2, actual, false);
}

[Test]
public void Test3()
{
Expression<Func<Blog, bool>> expected = b =>
b.Posts.Any(x => x.IsActive) && (b.HasBar || b.HasBaz);

Func<Blog, bool> actual = b =>
b.Posts.Any(x => x.IsActive) && (b.HasBar || b.HasBaz);

Test(expected, actual);
}

[Test, Ignore("Not fixed yet.")]
public void TestQueryable1()
{
var blogs = new[] {new Blog()}.AsQueryable();

var expected = (
from b in blogs
where (b.HasBar || b.HasBaz) && b.Posts.Any(x => x.IsActive)
select b);

var actual = (
from b in blogs
where b.HasFoo
select b).Decompile();

AssertAreEqual(expected.Expression, actual.Expression, compareDebugView: false);
}

[Test, Ignore("Not fixed yet.")]
public void TestQueryable2()
{
var blogs = new[] {new Blog()}.AsQueryable();

var expected = (
from b in blogs
where (b.HasBar && b.Posts.Any(x => x.IsActive)) || (b.HasBaz && b.Posts.Any(x => x.IsActive))
select b);

var actual = (
from b in blogs
where b.HasFoo2
select b).Decompile();

AssertAreEqual(expected.Expression, actual.Expression, compareDebugView: false);
}

[Test]
public void TestQueryable3()
{
var blogs = new[] {new Blog()}.AsQueryable();

var expected = (
from b in blogs
where b.Posts.Any(x => x.IsActive) && (b.HasBar || b.HasBaz)
select b);

var actual = (
from b in blogs
where b.HasFoo3
select b).Decompile();

AssertAreEqual(expected.Expression, actual.Expression, compareDebugView: false);
}
}
}
42 changes: 21 additions & 21 deletions src/DelegateDecompiler.Tests/QueryableExtensionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace DelegateDecompiler.Tests
{
[TestFixture]
public class QueryableExtensionsTests
public class QueryableExtensionsTests : DecompilerTestsBase
{
[Test]
public void InlinePropertyWithoutAttribute()
Expand All @@ -20,7 +20,7 @@ public void InlinePropertyWithoutAttribute()
where employee.FullNameWithoutAttribute.Computed() == "Test User"
select employee).Decompile();

Assert.AreEqual(expected.Expression.ToString(), actual.Expression.ToString());
AssertAreEqual(expected.Expression, actual.Expression);
}

[Test]
Expand All @@ -36,7 +36,7 @@ public void InlineProperty()
where employee.FullName == "Test User"
select employee).Decompile();

Assert.AreEqual(expected.Expression.ToString(), actual.Expression.ToString());
AssertAreEqual(expected.Expression, actual.Expression);
}

[Test]
Expand All @@ -52,7 +52,7 @@ public void ConcatNonStringInlineProperty()
where employee.FromTo == "0-100"
select employee).Decompile();

Assert.AreEqual(expected.Expression.ToString(), actual.Expression.ToString());
AssertAreEqual(expected.Expression, actual.Expression);
}

[Test]
Expand All @@ -70,7 +70,7 @@ public void InlinePropertyOrderBy()
orderby employee.FullName
select employee);

Assert.AreEqual(expected.Expression.ToString(), actual.Expression.ToString());
AssertAreEqual(expected.Expression, actual.Expression);
}

[Test]
Expand All @@ -88,7 +88,7 @@ public void InlinePropertyOrderByThenBy()
orderby employee.FullName
select employee).ThenBy(x => x.IsActive);

Assert.AreEqual(expected.Expression.ToString(), actual.Expression.ToString());
AssertAreEqual(expected.Expression, actual.Expression);
}

[Test]
Expand All @@ -104,7 +104,7 @@ where true
where employee.IsActive
select employee).Decompile();

Assert.AreEqual(expected.Expression.ToString(), actual.Expression.ToString());
AssertAreEqual(expected.Expression, actual.Expression);
}

[Test]
Expand All @@ -120,7 +120,7 @@ public void TestLdflda()
where employee.Count == 0
select employee).Decompile();

Assert.AreEqual(expected.Expression.ToString(), actual.Expression.ToString());
AssertAreEqual(expected.Expression, actual.Expression);
}

[Test]
Expand All @@ -136,7 +136,7 @@ public void InlineTooDeepProperty()
where employee.TooDeepName == "Test User"
select employee).Decompile();

Assert.AreEqual(expected.Expression.ToString(), actual.Expression.ToString());
AssertAreEqual(expected.Expression, actual.Expression);
}

[Test]
Expand All @@ -154,7 +154,7 @@ public void InlinePropertyWithVariableClosure()
select employee).Decompile();

Console.WriteLine(expected);
Assert.AreEqual(expected.Expression.ToString(), actual.Expression.ToString());
AssertAreEqual(expected.Expression, actual.Expression);
}

[Test]
Expand All @@ -170,7 +170,7 @@ public void InlineMethod()
where employee.FullNameMethod() == "Test User"
select employee).Decompile();

Assert.AreEqual(expected.Expression.ToString(), actual.Expression.ToString());
AssertAreEqual(expected.Expression, actual.Expression);
}

[Test]
Expand All @@ -186,7 +186,7 @@ public void InlineMethodWithArg()
where employee.FullNameMethod("Mr ") == "Mr Test User"
select employee).Decompile();

Assert.AreEqual(expected.Expression.ToString(), actual.Expression.ToString());
AssertAreEqual(expected.Expression, actual.Expression);
}

[Test]
Expand All @@ -202,7 +202,7 @@ public void InlineMethodWithTwoArgs()
where employee.FullNameMethod("Mr ", " Jr.") == "Mr Test User Jr."
select employee).Decompile();

Assert.AreEqual(expected.Expression.ToString(), actual.Expression.ToString());
AssertAreEqual(expected.Expression, actual.Expression);
}

[Test, Ignore("Minor differences")]
Expand All @@ -218,7 +218,7 @@ public void Issue39()
where employee.Test
select employee).Decompile();

Assert.AreEqual(expected.Expression.ToString(), actual.Expression.ToString());
AssertAreEqual(expected.Expression, actual.Expression);
}

[Test]
Expand All @@ -230,7 +230,7 @@ public void Issue58()

var actual = employees.AsQueryable().Where(_ => _.ComplexProperty == 1).Decompile();

Assert.AreEqual(expected.Expression.ToString(), actual.Expression.ToString());
AssertAreEqual(expected.Expression, actual.Expression);
}

[Test]
Expand All @@ -246,7 +246,7 @@ public void InlineExtensionMethod()
where employee.FullName().Computed() == "Test User"
select employee).Decompile();

Assert.AreEqual(expected.Expression.ToString(), actual.Expression.ToString());
AssertAreEqual(expected.Expression, actual.Expression);
}

[Test]
Expand All @@ -264,7 +264,7 @@ where employee.FullName().Computed() == "Test User"
orderby employee.FullName ().Computed()
select employee);

Assert.AreEqual(expected.Expression.ToString(), actual.Expression.ToString());
AssertAreEqual(expected.Expression, actual.Expression);
}

[Test]
Expand All @@ -282,7 +282,7 @@ where employee.FullName().Computed() == "Test User"
orderby employee.FullName().Computed()
select employee).ThenBy(x => x.IsActive);

Assert.AreEqual(expected.Expression.ToString(), actual.Expression.ToString());
AssertAreEqual(expected.Expression, actual.Expression);
}

[Test]
Expand All @@ -298,7 +298,7 @@ public void InlinePropertyNullableShortColeasce1()
where employee.TheBad > (short)0
select employee);

Assert.AreEqual(expected.Expression.ToString(), actual.Expression.ToString());
AssertAreEqual(expected.Expression, actual.Expression);
}

[Test]
Expand All @@ -314,7 +314,7 @@ public void InlinePropertyNullableShortColeasce2()
where (employee.MyField.HasValue ? (short)0 : (short)1) > 0
select employee);

Assert.AreEqual(expected.Expression.ToString(), actual.Expression.ToString());
AssertAreEqual(expected.Expression, actual.Expression);
}

[Test, Ignore("Minor differences")]
Expand All @@ -328,7 +328,7 @@ public void Issue78()

var actual = employees.AsQueryable().Select(e => e.TotalHoursDb).Decompile();

Assert.AreEqual(expected.Expression.ToString(), actual.Expression.ToString());
AssertAreEqual(expected.Expression, actual.Expression);
}
}
}
5 changes: 4 additions & 1 deletion src/DelegateDecompiler/OptimizeExpressionVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,13 @@ protected override Expression VisitConditional(ConditionalExpression node)

if (test.NodeType == ExpressionType.Not)
{
var testOperand = ((UnaryExpression) test).Operand;
if (ifTrueConstant?.Value as bool? == false)
{
return Expression.AndAlso(((UnaryExpression) test).Operand, ifFalse);
return Expression.AndAlso(testOperand, ifFalse);
}

return Visit(node.Update(testOperand, ifFalse, ifTrue));
}

return node.Update(test, ifTrue, ifFalse);
Expand Down
Loading

0 comments on commit f775f17

Please sign in to comment.