Browse Source

Add unit tests, directly use ILFunction instead of NewObj(ILFunction) for expression trees.

pull/988/head
Siegfried Pammer 8 years ago
parent
commit
049cff2324
  1. 370
      ICSharpCode.Decompiler.Tests/ExpressionTrees.cs
  2. 2
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  3. 6
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  4. 428
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs
  5. 5185
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.il
  6. 5020
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.opt.il
  7. 4804
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.opt.roslyn.il
  8. 4933
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.roslyn.il
  9. 6
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  10. 4
      ICSharpCode.Decompiler/CSharp/Resolver/LambdaResolveResult.cs
  11. 2
      ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs
  12. 8
      ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
  13. 42
      ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs

370
ICSharpCode.Decompiler.Tests/ExpressionTrees.cs

@ -1,370 +0,0 @@ @@ -1,370 +0,0 @@
// 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;
using System.Linq.Expressions;
using System.Xml;
public class ExpressionTrees
{
class GenericClass<X>
{
public static X StaticField;
public X InstanceField;
public static X StaticProperty { get; set; }
public X InstanceProperty { get; set; }
public static bool GenericMethod<Y>()
{
return false;
}
}
int field;
static object ToCode<R>(object x, Expression<Func<R>> expr)
{
return expr;
}
static object ToCode<T, R>(object x, Expression<Func<T, R>> expr)
{
return expr;
}
static object X()
{
return null;
}
public void Parameter(bool a)
{
ToCode(X(), () => a);
}
public void LocalVariable()
{
bool a = true;
ToCode(X(), () => a);
}
public void LambdaParameter()
{
ToCode(X(), (bool a) => a);
}
public void AddOperator(int x)
{
ToCode(X(), () => 1 + x + 2);
}
public void AnonymousClasses()
{
ToCode(X(), () => new { X = 3, A = "a" });
}
public void ArrayIndex()
{
ToCode(X(), () => new[] { 3, 4, 5 }[0 + (int)(DateTime.Now.Ticks % 3)]);
}
public void ArrayLengthAndDoubles()
{
ToCode(X(), () => new[] { 1.0, 2.01, 3.5 }.Concat(new[] { 1.0, 2.0 }).ToArray().Length);
}
public void AsOperator()
{
ToCode(X(), () => new object() as string);
}
public void ComplexGenericName()
{
ToCode(X(), () => ((Func<int, bool>)(x => x > 0))(0));
}
public void DefaultValue()
{
ToCode(X(), () => new TimeSpan(1, 2, 3) == default(TimeSpan));
}
public void EnumConstant()
{
ToCode(X(), () => new object().Equals(MidpointRounding.ToEven));
}
public void IndexerAccess()
{
var dict = Enumerable.Range(1, 20).ToDictionary(n => n.ToString());
ToCode(X(), () => dict["3"] == 3);
}
public void IsOperator()
{
ToCode(X(), () => new object() is string);
}
public void ListInitializer()
{
ToCode(X(), () => new Dictionary<int, int> { { 1, 1 }, { 2, 2 }, { 3, 4 } }.Count == 3);
}
public void ListInitializer2()
{
ToCode(X(), () => new List<int>(50) { 1, 2, 3 }.Count == 3);
}
public void ListInitializer3()
{
ToCode(X(), () => new List<int> { 1, 2, 3 }.Count == 3);
}
public void LiteralCharAndProperty()
{
ToCode(X(), () => new string(' ', 3).Length == 1);
}
public void CharNoCast()
{
ToCode(X(), () => "abc"[1] == 'b');
}
public void StringsImplicitCast()
{
int i = 1;
string x = "X";
ToCode(X(), () => (("a\n\\b" ?? x) + x).Length == 2 ? false : true && (1m + -i > 0 || false));
}
public void NotImplicitCast()
{
byte z = 42;
ToCode(X(), () => ~z == 0);
}
public void MembersBuiltin()
{
ToCode(X(), () => 1.23m.ToString());
ToCode(X(), () => AttributeTargets.All.HasFlag((Enum)AttributeTargets.Assembly));
ToCode(X(), () => "abc".Length == 3);
ToCode(X(), () => 'a'.CompareTo('b') < 0);
}
public void MembersDefault()
{
ToCode(X(), () => default(DateTime).Ticks == 0);
ToCode(X(), () => default(int[]).Length == 0);
ToCode(X(), () => default(Type).IsLayoutSequential);
ToCode(X(), () => default(List<int>).Count);
ToCode(X(), () => default(int[]).Clone() == null);
ToCode(X(), () => default(Type).IsInstanceOfType(new object()));
ToCode(X(), () => default(List<int>).AsReadOnly());
}
public void DoAssert()
{
field = 37;
ToCode(X(), () => field != C());
ToCode(X(), () => !ReferenceEquals(this, new ExpressionTrees()));
ToCode(X(), () => MyEquals(this) && !MyEquals(default(ExpressionTrees)));
}
int C()
{
return field + 5;
}
bool MyEquals(ExpressionTrees other)
{
return other != null && field == other.field;
}
public void MethodGroupAsExtensionMethod()
{
ToCode(X(), () => (Func<bool>)new[] { 2000, 2004, 2008, 2012 }.Any);
}
public void MethodGroupConstant()
{
ToCode(X(), () => Array.TrueForAll(new[] { 2000, 2004, 2008, 2012 }, DateTime.IsLeapYear));
HashSet<int> set = new HashSet<int>();
ToCode(X(), () => new[] { 2000, 2004, 2008, 2012 }.All(set.Add));
Func<Func<object, object, bool>, bool> sink = f => f(null, null);
ToCode(X(), () => sink(int.Equals));
}
public void MultipleCasts()
{
ToCode(X(), () => 1 == (int)(object)1);
}
public void MultipleDots()
{
ToCode(X(), () => 3.ToString().ToString().Length > 0);
}
public void NestedLambda()
{
Func<Func<int>, int> call = f => f();
//no params
ToCode(X(), () => call(() => 42));
//one param
ToCode(X(), () => new[] { 37, 42 }.Select(x => x * 2));
//two params
ToCode(X(), () => new[] { 37, 42 }.Select((x, i) => x * 2));
}
public void CurriedLambda()
{
ToCode<int, Func<int, Func<int, int>>>(X(), a => b => c => a + b + c);
}
bool Fizz(Func<int, bool> a)
{
return a(42);
}
bool Buzz(Func<int, bool> a)
{
return a(42);
}
bool Fizz(Func<string, bool> a)
{
return a("42");
}
public void NestedLambda2()
{
ToCode(X(), () => Fizz(x => x == "a"));
ToCode(X(), () => Fizz(x => x == 37));
ToCode(X(), () => Fizz((int x) => true));
ToCode(X(), () => Buzz(x => true));
}
public void NewArrayAndExtensionMethod()
{
ToCode(X(), () => new[] { 1.0, 2.01, 3.5 }.SequenceEqual(new[] { 1.0, 2.01, 3.5 }));
}
public void NewMultiDimArray()
{
ToCode(X(), () => new int[3, 4].Length == 1);
}
public void NewObject()
{
ToCode(X(), () => new object() != new object());
}
public void NotOperator()
{
bool x = true;
int y = 3;
byte z = 42;
ToCode(X(), () => ~(int)z == 0);
ToCode(X(), () => ~y == 0);
ToCode(X(), () => !x);
}
public void ObjectInitializers()
{
XmlReaderSettings s = new XmlReaderSettings {
CloseInput = false,
CheckCharacters = false
};
ToCode(X(), () => new XmlReaderSettings { CloseInput = s.CloseInput, CheckCharacters = s.CheckCharacters }.Equals(s));
}
public void Quoted()
{
ToCode(X(), () => (Expression<Func<int, string, string>>)((n, s) => s + n.ToString()) != null);
}
public void Quoted2()
{
ToCode(X(), () => ToCode(X(), () => true).Equals(null));
}
public void QuotedWithAnonymous()
{
ToCode(X(), () => new[] { new { X = "a", Y = "b" } }.Select(o => o.X + o.Y).Single());
}
public void StaticCall()
{
ToCode(X(), () => Equals(3, 0));
}
public void ThisCall()
{
ToCode(X(), () => !Equals(3));
}
public void ThisExplicit()
{
ToCode(X(), () => object.Equals(this, 3));
}
public void TypedConstant()
{
ToCode(X(), () => new[] { typeof(int), typeof(string) });
}
public void StaticCallImplicitCast()
{
ToCode(X(), () => Equals(3, 0));
}
public void StaticMembers()
{
ToCode(X(), () => (DateTime.Now > DateTime.Now + TimeSpan.FromMilliseconds(10.001)).ToString() == "False");
}
public void Strings()
{
int i = 1;
string x = "X";
ToCode(X(), () => (("a\n\\b" ?? x) + x).Length == 2 ? false : true && (1m + (decimal)-i > 0m || false));
}
public void StringAccessor()
{
ToCode(X(), () => (int)"abc"[1] == 98);
}
public void GenericClassInstance()
{
ToCode(X(), () => new GenericClass<int>().InstanceField + new GenericClass<double>().InstanceProperty);
}
public void GenericClassStatic()
{
ToCode(X(), () => GenericClass<int>.StaticField + GenericClass<double>.StaticProperty);
}
public void InvokeGenericMethod()
{
ToCode(X(), () => GenericClass<int>.GenericMethod<double>());
}
}

2
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -58,8 +58,10 @@ @@ -58,8 +58,10 @@
<ItemGroup>
<Compile Include="DataFlowTest.cs" />
<Compile Include="TestCases\Correctness\ExpressionTrees.cs" />
<Compile Include="TestCases\Correctness\LINQRaytracer.cs" />
<Compile Include="TestCases\Correctness\MiniJSON.cs" />
<Compile Include="TestCases\Pretty\ExpressionTrees.cs" />
<None Include="TestCases\ILPretty\Issue959.cs" />
<None Include="TestCases\ILPretty\FSharpLoops_Debug.cs" />
<None Include="TestCases\ILPretty\FSharpLoops_Release.cs" />

6
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -200,6 +200,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -200,6 +200,12 @@ namespace ICSharpCode.Decompiler.Tests
Run(cscOptions: cscOptions);
}
[Test]
public void ExpressionTrees([ValueSource("defaultOptions")] CompilerOptions cscOptions)
{
Run(cscOptions: cscOptions);
}
[Test]
public void FixProxyCalls([Values(CompilerOptions.None, CompilerOptions.Optimize, CompilerOptions.UseRoslyn)] CompilerOptions cscOptions)
{

428
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs

@ -0,0 +1,428 @@ @@ -0,0 +1,428 @@
// 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;
using System.Linq.Expressions;
using System.Xml;
public class ExpressionTrees
{
private class GenericClass<X>
{
public static X StaticField;
public X InstanceField;
public static X StaticProperty {
get;
set;
}
public X InstanceProperty {
get;
set;
}
public static bool GenericMethod<Y>()
{
return false;
}
}
private int field;
private static object ToCode<R>(object x, Expression<Func<R>> expr)
{
return expr;
}
private static object ToCode<T, R>(object x, Expression<Func<T, R>> expr)
{
return expr;
}
private static object X()
{
return null;
}
public void Parameter(bool a)
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => a);
}
public void LocalVariable()
{
bool a = true;
ExpressionTrees.ToCode(ExpressionTrees.X(), () => a);
}
public void LambdaParameter()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), (bool a) => a);
}
public void AddOperator(int x)
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => 1 + x + 2);
}
public void AnonymousClasses()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => new {
X = 3,
A = "a"
});
}
public void ArrayIndex()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => (new[] {
3,
4,
5
})[0 + (int)(DateTime.Now.Ticks % 3)]);
}
public void ArrayLengthAndDoubles()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => new double[3] {
1.0,
2.01,
3.5
}.Concat(new double[2] {
1.0,
2.0
}).ToArray().Length);
}
public void AsOperator()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => new object() as string);
}
public void ComplexGenericName()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => ((Func<int, bool>)(x => x > 0))(0));
}
public void DefaultValue()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => new TimeSpan(1, 2, 3) == default(TimeSpan));
}
public void EnumConstant()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => new object().Equals(MidpointRounding.ToEven));
}
public void IndexerAccess()
{
var dict = Enumerable.Range(1, 20).ToDictionary(n => n.ToString());
ExpressionTrees.ToCode(ExpressionTrees.X(), () => dict["3"] == 3);
}
public void IsOperator()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => new object() is string);
}
public void ListInitializer()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => new Dictionary<int, int> {
{
1,
1
},
{
2,
2
},
{
3,
4
}
}.Count == 3);
}
public void ListInitializer2()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => new List<int>(50) {
1,
2,
3
}.Count == 3);
}
public void ListInitializer3()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => new List<int> {
1,
2,
3
}.Count == 3);
}
public void LiteralCharAndProperty()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => new string(' ', 3).Length == 1);
}
public void CharNoCast()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => "abc"[1] == 'b');
}
public void StringsImplicitCast()
{
int i = 1;
string x = "X";
ExpressionTrees.ToCode(ExpressionTrees.X(), () => (("a\n\\b" ?? x) + x).Length == 2 ? false : true && (1m + -i > 0 || false));
}
public void NotImplicitCast()
{
byte z = 42;
ExpressionTrees.ToCode(ExpressionTrees.X(), () => ~z == 0);
}
public void MembersBuiltin()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => 1.23m.ToString());
ExpressionTrees.ToCode(ExpressionTrees.X(), () => AttributeTargets.All.HasFlag((Enum)AttributeTargets.Assembly));
ExpressionTrees.ToCode(ExpressionTrees.X(), () => "abc".Length == 3);
ExpressionTrees.ToCode(ExpressionTrees.X(), () => 'a'.CompareTo('b') < 0);
}
public void MembersDefault()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => default(DateTime).Ticks == 0);
ExpressionTrees.ToCode(ExpressionTrees.X(), () => default(int[]).Length == 0);
ExpressionTrees.ToCode(ExpressionTrees.X(), () => default(Type).IsLayoutSequential);
ExpressionTrees.ToCode(ExpressionTrees.X(), () => default(List<int>).Count);
ExpressionTrees.ToCode(ExpressionTrees.X(), () => default(int[]).Clone() == null);
ExpressionTrees.ToCode(ExpressionTrees.X(), () => default(Type).IsInstanceOfType(new object()));
ExpressionTrees.ToCode(ExpressionTrees.X(), () => default(List<int>).AsReadOnly());
}
public void DoAssert()
{
field = 37;
ExpressionTrees.ToCode(ExpressionTrees.X(), () => field != C());
ExpressionTrees.ToCode(ExpressionTrees.X(), () => !ReferenceEquals(this, new ExpressionTrees()));
ExpressionTrees.ToCode(ExpressionTrees.X(), () => MyEquals(this) && !MyEquals(default(ExpressionTrees)));
}
int C()
{
return field + 5;
}
bool MyEquals(ExpressionTrees other)
{
return other != null && field == other.field;
}
public void MethodGroupAsExtensionMethod()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => (Func<bool>)new[] {
2000,
2004,
2008,
2012
}.Any);
}
public void MethodGroupConstant()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => Array.TrueForAll(new[] {
2000,
2004,
2008,
2012
}, DateTime.IsLeapYear));
HashSet<int> set = new HashSet<int>();
ExpressionTrees.ToCode(ExpressionTrees.X(), () => new[] {
2000,
2004,
2008,
2012
}.All(set.Add));
Func<Func<object, object, bool>, bool> sink = f => f(null, null);
ExpressionTrees.ToCode(ExpressionTrees.X(), () => sink(int.Equals));
}
public void MultipleCasts()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => 1 == (int)(object)1);
}
public void MultipleDots()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => 3.ToString().ToString().Length > 0);
}
public void NestedLambda()
{
Func<Func<int>, int> call = f => f();
//no params
ExpressionTrees.ToCode(ExpressionTrees.X(), () => call(() => 42));
//one param
ExpressionTrees.ToCode(ExpressionTrees.X(), () => new[] { 37, 42 }.Select(x => x * 2));
//two params
ExpressionTrees.ToCode(ExpressionTrees.X(), () => new[] { 37, 42 }.Select((x, i) => x * 2));
}
public void CurriedLambda()
{
ExpressionTrees.ToCode<int, Func<int, Func<int, int>>>(ExpressionTrees.X(), a => b => c => a + b + c);
}
bool Fizz(Func<int, bool> a)
{
return a(42);
}
bool Buzz(Func<int, bool> a)
{
return a(42);
}
bool Fizz(Func<string, bool> a)
{
return a("42");
}
public void NestedLambda2()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => Fizz(x => x == "a"));
ExpressionTrees.ToCode(ExpressionTrees.X(), () => Fizz(x => x == 37));
ExpressionTrees.ToCode(ExpressionTrees.X(), () => Fizz((int x) => true));
ExpressionTrees.ToCode(ExpressionTrees.X(), () => Buzz(x => true));
}
public void NewArrayAndExtensionMethod()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => new[] { 1.0, 2.01, 3.5 }.SequenceEqual(new[] { 1.0, 2.01, 3.5 }));
}
public void NewMultiDimArray()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => new int[3, 4].Length == 1);
}
public void NewObject()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => new object() != new object());
}
public void NotOperator()
{
bool x = true;
int y = 3;
byte z = 42;
ExpressionTrees.ToCode(ExpressionTrees.X(), () => ~(int)z == 0);
ExpressionTrees.ToCode(ExpressionTrees.X(), () => ~y == 0);
ExpressionTrees.ToCode(ExpressionTrees.X(), () => !x);
}
public void ObjectInitializers()
{
XmlReaderSettings s = new XmlReaderSettings {
CloseInput = false,
CheckCharacters = false
};
ExpressionTrees.ToCode(ExpressionTrees.X(), () => new XmlReaderSettings { CloseInput = s.CloseInput, CheckCharacters = s.CheckCharacters }.Equals(s));
}
public void Quoted()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => (Expression<Func<int, string, string>>)((n, s) => s + n.ToString()) != null);
}
public void Quoted2()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => ExpressionTrees.ToCode(ExpressionTrees.X(), () => true).Equals(null));
}
public void QuotedWithAnonymous()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => new[] { new { X = "a", Y = "b" } }.Select(o => o.X + o.Y).Single());
}
public void StaticCall()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => Equals(3, 0));
}
public void ThisCall()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => !Equals(3));
}
public void ThisExplicit()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => object.Equals(this, 3));
}
public void TypedConstant()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => new[] { typeof(int), typeof(string) });
}
public void StaticCallImplicitCast()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => Equals(3, 0));
}
public void StaticMembers()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => (DateTime.Now > DateTime.Now + TimeSpan.FromMilliseconds(10.001)).ToString() == "False");
}
public void Strings()
{
int i = 1;
string x = "X";
ExpressionTrees.ToCode(ExpressionTrees.X(), () => (("a\n\\b" ?? x) + x).Length == 2 ? false : true && (1m + (decimal)-i > 0m || false));
}
public void StringAccessor()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => (int)"abc"[1] == 98);
}
public void GenericClassInstance()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => new GenericClass<int>().InstanceField + new GenericClass<double>().InstanceProperty);
}
public void GenericClassStatic()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => GenericClass<int>.StaticField + GenericClass<double>.StaticProperty);
}
public void InvokeGenericMethod()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => GenericClass<int>.GenericMethod<double>());
}
}

5185
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.il

File diff suppressed because it is too large Load Diff

5020
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.opt.il

File diff suppressed because it is too large Load Diff

4804
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.opt.roslyn.il

File diff suppressed because it is too large Load Diff

4933
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.roslyn.il

File diff suppressed because it is too large Load Diff

6
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -1420,6 +1420,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1420,6 +1420,12 @@ namespace ICSharpCode.Decompiler.CSharp
.WithRR(new ConversionResolveResult(delegateType, rr, LambdaConversion.Instance));
}
protected internal override TranslatedExpression VisitILFunction(ILFunction function, TranslationContext context)
{
return TranslateFunction(function.ExpressionTreeType, function)
.WithILInstruction(function);
}
IType InferReturnType(BlockStatement body)
{
var returnExpressions = new List<ResolveResult>();

4
ICSharpCode.Decompiler/CSharp/Resolver/LambdaResolveResult.cs

@ -132,8 +132,8 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -132,8 +132,8 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
public override bool IsImplicitlyTyped { get; }
public override bool IsAsync => function.IsAsync;
public override IList<IParameter> Parameters => function.Method.Parameters;
public override IType ReturnType => function.Method.ReturnType;
public override IList<IParameter> Parameters => function.Parameters;
public override IType ReturnType => function.ReturnType;
public override ResolveResult Body { get; }

2
ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs

@ -178,7 +178,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -178,7 +178,7 @@ namespace ICSharpCode.Decompiler.CSharp
case ConversionResolveResult conversion: {
if (Expression is CastExpression cast
&& (type.IsKnownType(KnownTypeCode.Object) && conversion.Conversion.IsBoxingConversion
|| type.Kind == TypeKind.Delegate && conversion.Conversion.IsAnonymousFunctionConversion
|| conversion.Conversion.IsAnonymousFunctionConversion
|| (conversion.Conversion.IsImplicit && (conversion.Conversion.IsUserDefined || targetType.IsKnownType(KnownTypeCode.Decimal)))
)) {
return this.UnwrapChild(cast.Expression);

8
ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs

@ -61,7 +61,13 @@ namespace ICSharpCode.Decompiler.IL @@ -61,7 +61,13 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
public IType AsyncReturnType;
public bool IsExpressionTree;
/// <summary>
/// If this is an expression tree, returns Expression{T}, otherwise null.
/// T is the delegate type that matches the signature of this method.
/// </summary>
public IType ExpressionTreeType;
public bool IsExpressionTree => ExpressionTreeType != null;
public readonly IType ReturnType;

42
ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs

@ -19,16 +19,20 @@ @@ -19,16 +19,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.IL.Transforms
{
/// <summary>
/// Converts LINQ Expression Trees to ILFunctions/ILAst instructions.
/// </summary>
public class TransformExpressionTrees : IStatementTransform
{
/// <summary>
/// Returns true if the instruction matches the pattern for Expression.Lambda calls.
/// </summary>
static bool MightBeExpressionTree(ILInstruction inst, ILInstruction stmt)
{
if (!(inst is CallInstruction call
@ -85,6 +89,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -85,6 +89,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
Dictionary<ILVariable, (IType, string)> parameters;
Dictionary<ILVariable, ILVariable> parameterMapping;
List<ILInstruction> instructionsToRemove;
Stack<ILFunction> lambdaStack;
public void Run(Block block, int pos, StatementTransformContext context)
{
@ -92,6 +97,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -92,6 +97,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
this.parameters = new Dictionary<ILVariable, (IType, string)>();
this.parameterMapping = new Dictionary<ILVariable, ILVariable>();
this.instructionsToRemove = new List<ILInstruction>();
this.lambdaStack = new Stack<ILFunction>();
for (int i = pos; i < block.Instructions.Count; i++) {
if (MatchParameterVariableAssignment(block.Instructions[i], out var v, out var type, out var name)) {
parameters.Add(v, (type, name));
@ -124,7 +130,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -124,7 +130,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
}
NewObj ConvertLambda(CallInstruction instruction)
/// <summary>
/// Converts a Expression.Lambda call into an ILFunction.
/// If the conversion fails, null is returned.
/// </summary>
ILFunction ConvertLambda(CallInstruction instruction)
{
if (instruction.Method.Name != "Lambda" || instruction.Arguments.Count != 2 || instruction.Method.ReturnType.FullName != "System.Linq.Expressions.Expression" || instruction.Method.ReturnType.TypeArguments.Count != 1)
return null;
@ -132,20 +142,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -132,20 +142,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var parameterVariablesList = new List<ILVariable>();
if (!ReadParameters(instruction.Arguments[1], parameterList, parameterVariablesList, new SimpleTypeResolveContext(context.Function.Method)))
return null;
var container = new BlockContainer();
var function = new ILFunction(instruction.Method.ReturnType.TypeArguments[0].TypeArguments.LastOrDefault() ?? context.TypeSystem.Compilation.FindType(KnownTypeCode.Void), parameterList, container);
function.ExpressionTreeType = instruction.Method.ReturnType;
function.Variables.AddRange(parameterVariablesList);
lambdaStack.Push(function);
var bodyInstruction = ConvertInstruction(instruction.Arguments[0]);
lambdaStack.Pop();
if (bodyInstruction == null)
return null;
var container = new BlockContainer(expectedResultType: bodyInstruction.ResultType);
container.ExpectedResultType = bodyInstruction.ResultType;
container.Blocks.Add(new Block() { Instructions = { new Leave(container, bodyInstruction) } });
var function = new ILFunction(instruction.Method.ReturnType.TypeArguments[0].TypeArguments.LastOrDefault() ?? context.TypeSystem.Compilation.FindType(KnownTypeCode.Void), parameterList, container);
function.IsExpressionTree = true;
function.Variables.AddRange(parameterVariablesList);
return new NewObj(instruction.Method.ReturnType.TypeArguments[0].GetConstructors().First()) {
Arguments = {
new LdNull(),
function
}
};
return function;
}
bool ReadParameters(ILInstruction initializer, IList<IParameter> parameters, IList<ILVariable> parameterVariables, ITypeResolveContext resolveContext)
@ -556,15 +564,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -556,15 +564,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (newObj == null)
return null;
IList<ILInstruction> arguments = null;
ILFunction function;
ILFunction function = lambdaStack.Peek();
if (!MatchGetMethodFromHandle(invocation.Arguments[1], out var member)) {
if (!MatchArgumentList(invocation.Arguments[1], out arguments))
return null;
function = ((LdLoc)((Block)invocation.Arguments[1]).FinalInstruction).Variable.Function;
} else {
if (invocation.Arguments.Count != 3 || !MatchArgumentList(invocation.Arguments[2], out arguments))
return null;
function = ((LdLoc)((Block)invocation.Arguments[2]).FinalInstruction).Variable.Function;
}
if (arguments == null || arguments.Count == 0)
return null;
@ -634,7 +640,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -634,7 +640,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return null;
if (arguments == null || arguments.Count == 0)
return null;
var function = ((LdLoc)((Block)invocation.Arguments[1]).FinalInstruction).Variable.Function;
var function = lambdaStack.Peek();
var initializer = function.RegisterVariable(VariableKind.InitializerTarget, newObj.Method.DeclaringType);
for (int i = 0; i < arguments.Count; i++) {
ILInstruction arg;
@ -685,7 +691,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -685,7 +691,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (arguments.Count == 0)
return null;
var block = (Block)invocation.Arguments[1];
var function = ((LdLoc)block.FinalInstruction).Variable.Function;
var function = lambdaStack.Peek();
var variable = function.RegisterVariable(VariableKind.InitializerTarget, new ArrayType(context.BlockContext.TypeSystem.Compilation, type));
Block initializer = new Block(BlockKind.ArrayInitializer);
int i = 0;

Loading…
Cancel
Save