Browse Source

Merge pull request #988 from icsharpcode/expression-tree-instruction

Implement support for expression trees
pull/992/head
Siegfried Pammer 8 years ago committed by GitHub
parent
commit
66cce9180c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs
  2. 2
      ICSharpCode.Decompiler.Tests/DataFlowTest.cs
  3. 370
      ICSharpCode.Decompiler.Tests/ExpressionTrees.cs
  4. 5
      ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
  5. 2
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  6. 6
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  7. 33
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/ExpressionTrees.cs
  8. 761
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs
  9. 10889
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.il
  10. 10282
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.opt.il
  11. 9687
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.opt.roslyn.il
  12. 9946
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.roslyn.il
  13. 5
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  14. 4
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  15. 27
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  16. 4
      ICSharpCode.Decompiler/CSharp/Resolver/LambdaResolveResult.cs
  17. 12
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  18. 2
      ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs
  19. 2
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  20. 55
      ICSharpCode.Decompiler/IL/Instructions.cs
  21. 4
      ICSharpCode.Decompiler/IL/Instructions.tt
  22. 30
      ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
  23. 11
      ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs
  24. 14
      ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs
  25. 31
      ICSharpCode.Decompiler/IL/Transforms/ExpressionTreeCast.cs
  26. 1115
      ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs

6
ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs

@ -155,6 +155,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -155,6 +155,12 @@ namespace ICSharpCode.Decompiler.Tests
RunCS(options: options);
}
[Test]
public void ExpressionTrees([ValueSource("defaultOptions")] CompilerOptions options)
{
RunCS(options: options);
}
[Test]
public void BitNot([Values(false, true)] bool force32Bit)
{

2
ICSharpCode.Decompiler.Tests/DataFlowTest.cs

@ -55,7 +55,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -55,7 +55,7 @@ namespace ICSharpCode.Decompiler.Tests
public void TryFinallyWithAssignmentInFinally()
{
ILVariable v = new ILVariable(VariableKind.Local, SpecialType.UnknownType, 0);
ILFunction f = new ILFunction(null, null, new TryFinally(
ILFunction f = new ILFunction((IMethod)null, null, new TryFinally(
new Nop(),
new StLoc(v, new LdcI4(0))
));

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>());
}
}

5
ICSharpCode.Decompiler.Tests/Helpers/Tester.cs

@ -154,7 +154,8 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -154,7 +154,8 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
{
MetadataReference.CreateFromFile(Path.Combine(refAsmPath, "mscorlib.dll")),
MetadataReference.CreateFromFile(Path.Combine(refAsmPath, "System.dll")),
MetadataReference.CreateFromFile(Path.Combine(refAsmPath, "System.Core.dll"))
MetadataReference.CreateFromFile(Path.Combine(refAsmPath, "System.Core.dll")),
MetadataReference.CreateFromFile(Path.Combine(refAsmPath, "System.Xml.dll"))
};
});
@ -222,7 +223,9 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -222,7 +223,9 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
options.OutputAssembly = outputFileName;
}
options.ReferencedAssemblies.Add("System.dll");
options.ReferencedAssemblies.Add("System.Core.dll");
options.ReferencedAssemblies.Add("System.Xml.dll");
CompilerResults results = provider.CompileAssemblyFromFile(options, sourceFileNames.ToArray());
if (results.Errors.Cast<CompilerError>().Any(e => !e.IsWarning)) {
StringBuilder b = new StringBuilder("Compiler error:");

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)
{

33
ICSharpCode.Decompiler.Tests/TestCases/Correctness/ExpressionTrees.cs

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
{
class ExpressionTrees
{
static void Main()
{
Test();
var twice = GetExpression(Expression.Constant(2)).Compile();
Console.WriteLine(twice(21));
}
static void Test()
{
int i = 0;
Expression<Func<int>> expression = () => i;
i = 1;
Console.WriteLine(expression.Compile()());
}
static Expression<Func<int, int>> GetExpression(Expression factor)
{
ParameterExpression parameterExpression = Expression.Parameter(typeof(int), "x");
return Expression.Lambda<Func<int, int>>(Expression.Multiply(parameterExpression, factor), parameterExpression);
}
}
}

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

@ -0,0 +1,761 @@ @@ -0,0 +1,761 @@
// 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;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
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<Action<R>> expr)
{
return expr;
}
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 ToCode<T, T2, R>(object x, Expression<Func<T, T2, 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 int[3] {
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>)((int 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()
{
Dictionary<string, int> dict = Enumerable.Range(1, 20).ToDictionary((int 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 + (decimal)(-i) > 0m || 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(), () => ((Array)null).Length == 0);
ExpressionTrees.ToCode(ExpressionTrees.X(), () => ((Type)null).IsLayoutSequential);
ExpressionTrees.ToCode(ExpressionTrees.X(), () => ((List<int>)null).Count);
ExpressionTrees.ToCode(ExpressionTrees.X(), () => ((Array)null).Clone() == null);
ExpressionTrees.ToCode(ExpressionTrees.X(), () => ((Type)null).IsInstanceOfType(new object()));
ExpressionTrees.ToCode(ExpressionTrees.X(), () => ((List<int>)null).AsReadOnly());
}
public void DoAssert()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => this.field != this.C());
ExpressionTrees.ToCode(ExpressionTrees.X(), () => !object.ReferenceEquals(this, new ExpressionTrees()));
ExpressionTrees.ToCode(ExpressionTrees.X(), () => this.MyEquals(this) && !this.MyEquals(null));
}
private int C()
{
throw new NotImplementedException();
}
private bool MyEquals(ExpressionTrees other)
{
throw new NotImplementedException();
}
public void MethodGroupAsExtensionMethod()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), (Expression<Func<Func<bool>>>)(() => ((IEnumerable<int>)new int[4] {
2000,
2004,
2008,
2012
}).Any<int>));
}
public void MethodGroupConstant()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => Array.TrueForAll(new int[4] {
2000,
2004,
2008,
2012
}, DateTime.IsLeapYear));
HashSet<int> set = new HashSet<int>();
ExpressionTrees.ToCode(ExpressionTrees.X(), () => new int[4] {
2000,
2004,
2008,
2012
}.All(set.Add));
Func<Func<object, object, bool>, bool> sink = (Func<object, object, bool> f) => f(null, null);
ExpressionTrees.ToCode(ExpressionTrees.X(), () => sink(object.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 = (Func<int> f) => f();
//no params
ExpressionTrees.ToCode(ExpressionTrees.X(), () => call(() => 42));
//one param
ExpressionTrees.ToCode(ExpressionTrees.X(), () => from x in new int[2] {
37,
42
}
select x * 2);
//two params
ExpressionTrees.ToCode(ExpressionTrees.X(), () => new int[2] {
37,
42
}.Select((int x, int i) => x * 2));
}
public void CurriedLambda()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), (Expression<Func<int, Func<int, Func<int, int>>>>)((int a) => (int b) => (int c) => a + b + c));
}
private bool Fizz(Func<int, bool> a)
{
return a(42);
}
private bool Buzz(Func<int, bool> a)
{
return a(42);
}
private bool Fizz(Func<string, bool> a)
{
return a("42");
}
private bool Fizz(Func<Action, bool> a)
{
return a(null);
}
public void NestedLambda2()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => this.Fizz((string x) => x == "a"));
ExpressionTrees.ToCode(ExpressionTrees.X(), () => this.Fizz((string x) => x != "a"));
ExpressionTrees.ToCode(ExpressionTrees.X(), () => this.Fizz((Action x) => x == new Action(this.NestedLambda2)));
ExpressionTrees.ToCode(ExpressionTrees.X(), () => this.Fizz((Action x) => x != new Action(this.NestedLambda2)));
ExpressionTrees.ToCode(ExpressionTrees.X(), () => this.Fizz((int x) => x == 37));
ExpressionTrees.ToCode(ExpressionTrees.X(), () => this.Fizz((int x) => true));
ExpressionTrees.ToCode(ExpressionTrees.X(), () => this.Buzz((int x) => true));
}
public void NewArrayAndExtensionMethod()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => new double[3] {
1.0,
2.01,
3.5
}.SequenceEqual(new double[3] {
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(), () => ~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>>)((int n, string 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(), () => (from o in new[] {
new {
X = "a",
Y = "b"
}
}
select o.X + o.Y).Single());
}
public void StaticCall()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => object.Equals(3, 0));
}
public void ThisCall()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => !this.Equals(3));
}
public void ThisExplicit()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => object.Equals(this, 3));
}
public void TypedConstant()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => new Type[2] {
typeof(int),
typeof(string)
});
}
public void StaticCallImplicitCast()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => object.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 GenericClassInstance()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => (double)new GenericClass<int>().InstanceField + new GenericClass<double>().InstanceProperty);
}
public void GenericClassStatic()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => (double)GenericClass<int>.StaticField + GenericClass<double>.StaticProperty);
}
public void InvokeGenericMethod()
{
ExpressionTrees.ToCode(ExpressionTrees.X(), () => GenericClass<int>.GenericMethod<double>());
}
private static void Test<T>(T delegateExpression, Expression<T> expressionTree)
{
}
public static void ArrayIndexer()
{
ExpressionTrees.Test((Func<int[], int>)((int[] array) => array[0]), (Expression<Func<int[], int>>)((int[] array) => array[0]));
ExpressionTrees.Test((Func<int[], int, int>)((int[] array, int index) => array[index]), (Expression<Func<int[], int, int>>)((int[] array, int index) => array[index]));
ExpressionTrees.Test((Func<int[,], int>)((int[,] array) => array[0, 5]), (Expression<Func<int[,], int>>)((int[,] array) => array[0, 5]));
ExpressionTrees.Test((Func<int[,], int, int>)((int[,] array, int index) => array[index, 7]), (Expression<Func<int[,], int, int>>)((int[,] array, int index) => array[index, 7]));
ExpressionTrees.Test((Func<int[][], int, int>)((int[][] array, int index) => array[index][7]), (Expression<Func<int[][], int, int>>)((int[][] array, int index) => array[index][7]));
}
public static void ArrayLength()
{
ExpressionTrees.Test((Func<int[], int>)((int[] array) => array.Length), (Expression<Func<int[], int>>)((int[] array) => array.Length));
ExpressionTrees.Test((Func<int>)(() => ((Array)null).Length), (Expression<Func<int>>)(() => ((Array)null).Length));
}
public static void NewObj()
{
ExpressionTrees.Test((Func<object>)(() => new SimpleType()), (Expression<Func<object>>)(() => new SimpleType()));
ExpressionTrees.Test((Func<object>)(() => new SimpleTypeWithCtor(5)), (Expression<Func<object>>)(() => new SimpleTypeWithCtor(5)));
ExpressionTrees.Test((Func<object>)(() => new SimpleTypeWithMultipleCtors()), (Expression<Func<object>>)(() => new SimpleTypeWithMultipleCtors()));
ExpressionTrees.Test((Func<object>)(() => new SimpleTypeWithMultipleCtors(5)), (Expression<Func<object>>)(() => new SimpleTypeWithMultipleCtors(5)));
ExpressionTrees.Test((Func<object>)(() => new GenericClass<int>()), (Expression<Func<object>>)(() => new GenericClass<int>()));
ExpressionTrees.Test((Func<object>)(() => new GenericClassWithCtor<int>()), (Expression<Func<object>>)(() => new GenericClassWithCtor<int>()));
ExpressionTrees.Test((Func<object>)(() => new GenericClassWithMultipleCtors<int>(5)), (Expression<Func<object>>)(() => new GenericClassWithMultipleCtors<int>(5)));
}
public unsafe static void TypeOfExpr()
{
ExpressionTrees.Test((Func<Type>)(() => typeof(int)), (Expression<Func<Type>>)(() => typeof(int)));
ExpressionTrees.Test((Func<Type>)(() => typeof(object)), (Expression<Func<Type>>)(() => typeof(object)));
ExpressionTrees.Test((Func<Type>)(() => typeof(List<>)), (Expression<Func<Type>>)(() => typeof(List<>)));
ExpressionTrees.Test((Func<Type>)(() => typeof(List<int>)), (Expression<Func<Type>>)(() => typeof(List<int>)));
ExpressionTrees.Test((Func<Type>)(() => typeof(int*)), (Expression<Func<Type>>)(() => typeof(int*)));
}
public static void AsTypeExpr()
{
ExpressionTrees.Test((Func<object, MyClass>)((object obj) => obj as MyClass), (Expression<Func<object, MyClass>>)((object obj) => obj as MyClass));
ExpressionTrees.Test((Func<object, GenericClass<object>>)((object obj) => obj as GenericClass<object>), (Expression<Func<object, GenericClass<object>>>)((object obj) => obj as GenericClass<object>));
}
public static void IsTypeExpr()
{
ExpressionTrees.Test((Func<object, bool>)((object obj) => obj is MyClass), (Expression<Func<object, bool>>)((object obj) => obj is MyClass));
}
public static void UnaryLogicalOperators()
{
ExpressionTrees.Test((Func<bool, bool>)((bool a) => !a), (Expression<Func<bool, bool>>)((bool a) => !a));
}
public static void ConditionalOperator()
{
ExpressionTrees.ToCode(null, (bool a) => a ? 5 : 10);
ExpressionTrees.ToCode(null, (object a) => a ?? new MyClass());
}
public static void BinaryLogicalOperators()
{
ExpressionTrees.ToCode(null, (int a, int b) => a == b);
ExpressionTrees.ToCode(null, (int a, int b) => a != b);
ExpressionTrees.ToCode(null, (int a, int b) => a < b);
ExpressionTrees.ToCode(null, (int a, int b) => a <= b);
ExpressionTrees.ToCode(null, (int a, int b) => a > b);
ExpressionTrees.ToCode(null, (int a, int b) => a >= b);
ExpressionTrees.ToCode(null, (int a, int b) => a == 1 && b == 2);
ExpressionTrees.ToCode(null, (int a, int b) => a == 1 || b == 2);
ExpressionTrees.ToCode(null, (int a, short b) => a == b);
ExpressionTrees.ToCode(null, (ushort a, int b) => a != b);
ExpressionTrees.ToCode(null, (int a, long b) => (long)a < b);
ExpressionTrees.ToCode(null, (ulong a, uint b) => a <= (ulong)b);
ExpressionTrees.ToCode(null, (int a, uint b) => (long)a <= (long)b);
ExpressionTrees.ToCode(null, (int a, long b) => (long)a > b);
ExpressionTrees.ToCode(null, (short a, long b) => (long)a >= b);
ExpressionTrees.ToCode(null, (int a, int b) => a == 1 && b == 2);
ExpressionTrees.ToCode(null, (int a, int b) => a == 1 || b == 2);
}
public static void UnaryArithmeticOperators()
{
ExpressionTrees.Test((Func<int, int>)((int a) => a), (Expression<Func<int, int>>)((int a) => a));
ExpressionTrees.Test((Func<int, int>)((int a) => -a), (Expression<Func<int, int>>)((int a) => -a));
}
public static void BinaryArithmeticOperators()
{
ExpressionTrees.Test((Func<int, int, int>)((int a, int b) => a + b), (Expression<Func<int, int, int>>)((int a, int b) => a + b));
ExpressionTrees.Test((Func<int, int, int>)((int a, int b) => a - b), (Expression<Func<int, int, int>>)((int a, int b) => a - b));
ExpressionTrees.Test((Func<int, int, int>)((int a, int b) => a * b), (Expression<Func<int, int, int>>)((int a, int b) => a * b));
ExpressionTrees.Test((Func<int, int, int>)((int a, int b) => a / b), (Expression<Func<int, int, int>>)((int a, int b) => a / b));
ExpressionTrees.Test((Func<int, int, int>)((int a, int b) => a % b), (Expression<Func<int, int, int>>)((int a, int b) => a % b));
ExpressionTrees.Test((Func<long, int, long>)((long a, int b) => a + b), (Expression<Func<long, int, long>>)((long a, int b) => a + (long)b));
ExpressionTrees.Test((Func<long, int, long>)((long a, int b) => a - b), (Expression<Func<long, int, long>>)((long a, int b) => a - (long)b));
ExpressionTrees.Test((Func<long, int, long>)((long a, int b) => a * b), (Expression<Func<long, int, long>>)((long a, int b) => a * (long)b));
ExpressionTrees.Test((Func<long, int, long>)((long a, int b) => a / b), (Expression<Func<long, int, long>>)((long a, int b) => a / (long)b));
ExpressionTrees.Test((Func<long, int, long>)((long a, int b) => a % b), (Expression<Func<long, int, long>>)((long a, int b) => a % (long)b));
ExpressionTrees.Test((Func<short, int, int>)((short a, int b) => a + b), (Expression<Func<short, int, int>>)((short a, int b) => a + b));
ExpressionTrees.Test((Func<int, short, int>)((int a, short b) => a - b), (Expression<Func<int, short, int>>)((int a, short b) => a - b));
ExpressionTrees.Test((Func<short, int, int>)((short a, int b) => a * b), (Expression<Func<short, int, int>>)((short a, int b) => a * b));
ExpressionTrees.Test((Func<int, short, int>)((int a, short b) => a / b), (Expression<Func<int, short, int>>)((int a, short b) => a / b));
ExpressionTrees.Test((Func<short, int, int>)((short a, int b) => a % b), (Expression<Func<short, int, int>>)((short a, int b) => a % b));
}
public static void BitOperators()
{
ExpressionTrees.Test((Func<int, int>)((int a) => ~a), (Expression<Func<int, int>>)((int a) => ~a));
ExpressionTrees.Test((Func<int, int, int>)((int a, int b) => a & b), (Expression<Func<int, int, int>>)((int a, int b) => a & b));
ExpressionTrees.Test((Func<int, int, int>)((int a, int b) => a | b), (Expression<Func<int, int, int>>)((int a, int b) => a | b));
ExpressionTrees.Test((Func<int, int, int>)((int a, int b) => a ^ b), (Expression<Func<int, int, int>>)((int a, int b) => a ^ b));
}
public static void ShiftOperators()
{
ExpressionTrees.Test((Func<int, int>)((int a) => a >> 2), (Expression<Func<int, int>>)((int a) => a >> 2));
ExpressionTrees.Test((Func<int, int>)((int a) => a << 2), (Expression<Func<int, int>>)((int a) => a << 2));
ExpressionTrees.Test((Func<long, long>)((long a) => a >> 2), (Expression<Func<long, long>>)((long a) => a >> 2));
ExpressionTrees.Test((Func<long, long>)((long a) => a << 2), (Expression<Func<long, long>>)((long a) => a << 2));
}
public static void SimpleExpressions()
{
ExpressionTrees.Test((Func<int>)(() => 0), (Expression<Func<int>>)(() => 0));
ExpressionTrees.Test((Func<int, int>)((int a) => a), (Expression<Func<int, int>>)((int a) => a));
}
public static void Capturing()
{
int captured = 5;
ExpressionTrees.Test((Func<int>)(() => captured), (Expression<Func<int>>)(() => captured));
}
public static void FieldAndPropertyAccess()
{
ExpressionTrees.ToCode(null, () => 1);
ExpressionTrees.ToCode(null, () => SimpleType.StaticField);
ExpressionTrees.ToCode(null, () => SimpleType.StaticReadonlyField);
ExpressionTrees.ToCode(null, () => SimpleType.StaticProperty);
ExpressionTrees.ToCode(null, () => SimpleType.StaticReadonlyProperty);
ExpressionTrees.ToCode(null, (SimpleType a) => a.Field);
ExpressionTrees.ToCode(null, (SimpleType a) => a.Property);
ExpressionTrees.ToCode(null, (SimpleType a) => a.ReadonlyField);
ExpressionTrees.ToCode(null, (SimpleType a) => a.ReadonlyProperty);
}
public static void Call()
{
ExpressionTrees.ToCode(null, (string a) => Console.WriteLine(a));
ExpressionTrees.Test((Func<string, string>)((string a) => a.ToString()), (Expression<Func<string, string>>)((string a) => a.ToString()));
ExpressionTrees.Test((Func<int, string>)((int a) => a.ToString()), (Expression<Func<int, string>>)((int a) => a.ToString()));
ExpressionTrees.Test((Func<string, char[]>)((string a) => a.ToArray()), (Expression<Func<string, char[]>>)((string a) => a.ToArray()));
ExpressionTrees.Test((Func<bool>)(() => 'a'.CompareTo('b') < 0), (Expression<Func<bool>>)(() => 'a'.CompareTo('b') < 0));
}
public static void Quote()
{
ExpressionTrees.Test((Func<bool>)(() => (Expression<Func<int, string, string>>)((int n, string s) => s + n.ToString()) != null), (Expression<Func<bool>>)(() => (Expression<Func<int, string, string>>)((int n, string s) => s + n.ToString()) != null));
}
public static void ArrayInitializer()
{
ExpressionTrees.Test((Func<int[]>)(() => new int[3] {
1,
2,
3
}), (Expression<Func<int[]>>)(() => new int[3] {
1,
2,
3
}));
ExpressionTrees.Test((Func<int[]>)(() => new int[3]), (Expression<Func<int[]>>)(() => new int[3]));
ExpressionTrees.Test((Func<int[,]>)(() => new int[3, 5]), (Expression<Func<int[,]>>)(() => new int[3, 5]));
ExpressionTrees.Test((Func<int[][]>)(() => new int[3][]), (Expression<Func<int[][]>>)(() => new int[3][]));
ExpressionTrees.Test((Func<int[][]>)(() => new int[1][] {
new int[3] {
1,
2,
3
}
}), (Expression<Func<int[][]>>)(() => new int[1][] {
new int[3] {
1,
2,
3
}
}));
}
public static void AnonymousTypes()
{
ExpressionTrees.Test((Func<object>)(() => new {
A = 5,
B = "Test"
}), (Expression<Func<object>>)(() => new {
A = 5,
B = "Test"
}));
}
public static void ObjectInit()
{
ExpressionTrees.ToCode(null, () => new SimpleType {
Property = 4,
Field = 3
});
}
}
internal class MyClass
{
public static MyClass operator +(MyClass a, MyClass b)
{
return new MyClass();
}
}
internal class SimpleType
{
public const int ConstField = 1;
public static readonly int StaticReadonlyField = 2;
public static int StaticField = 3;
public readonly int ReadonlyField = 2;
public int Field = 3;
public static int StaticReadonlyProperty {
get {
return 0;
}
}
public static int StaticProperty {
get;
set;
}
public int ReadonlyProperty {
get {
return 0;
}
}
public int Property {
get;
set;
}
}
internal class SimpleTypeWithCtor
{
public SimpleTypeWithCtor(int i)
{
}
}
internal class SimpleTypeWithMultipleCtors
{
public SimpleTypeWithMultipleCtors()
{
}
public SimpleTypeWithMultipleCtors(int i)
{
}
}
internal class GenericClassWithCtor<T>
{
}
internal class GenericClassWithMultipleCtors<T>
{
public GenericClassWithMultipleCtors()
{
}
public GenericClassWithMultipleCtors(int x)
{
}
}
internal class GenericClass<T>
{
}
}

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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

5
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -128,7 +128,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -128,7 +128,8 @@ namespace ICSharpCode.Decompiler.CSharp
new NullCoalescingTransform(),
new NullableLiftingStatementTransform(),
new TransformArrayInitializers(),
new TransformCollectionAndObjectInitializers()
new TransformCollectionAndObjectInitializers(),
new TransformExpressionTrees()
),
}
},
@ -762,7 +763,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -762,7 +763,7 @@ namespace ICSharpCode.Decompiler.CSharp
}
AddDefinesForConditionalAttributes(function);
var statementBuilder = new StatementBuilder(specializingTypeSystem, decompilationContext, method, function, settings, CancellationToken);
var statementBuilder = new StatementBuilder(specializingTypeSystem, decompilationContext, function, settings, CancellationToken);
var body = statementBuilder.ConvertAsBlock(function.Body);
Comment prev = null;

4
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -420,10 +420,6 @@ namespace ICSharpCode.Decompiler.CSharp @@ -420,10 +420,6 @@ namespace ICSharpCode.Decompiler.CSharp
case OpCode.LdVirtFtn:
method = ((LdVirtFtn)func).Method;
break;
case OpCode.ILFunction:
method = ((ILFunction)func).Method;
return expressionBuilder.TranslateFunction(inst.Method.DeclaringType, (ILFunction)func)
.WithILInstruction(inst);
default:
throw new ArgumentException($"Unknown instruction type: {func.OpCode}");
}

27
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -1350,15 +1350,15 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1350,15 +1350,15 @@ namespace ICSharpCode.Decompiler.CSharp
internal ExpressionWithResolveResult TranslateFunction(IType delegateType, ILFunction function)
{
var method = function.Method.MemberDefinition as IMethod;
Debug.Assert(method != null);
var method = function.Method?.MemberDefinition as IMethod;
// Create AnonymousMethodExpression and prepare parameters
AnonymousMethodExpression ame = new AnonymousMethodExpression();
ame.IsAsync = function.IsAsync;
ame.Parameters.AddRange(MakeParameters(method, function));
ame.Parameters.AddRange(MakeParameters(function.Parameters, function));
ame.HasParameterList = ame.Parameters.Count > 0;
StatementBuilder builder = new StatementBuilder(typeSystem.GetSpecializingTypeSystem(new SimpleTypeResolveContext(method)), this.decompilationContext, method, function, settings, cancellationToken);
var context = method == null ? decompilationContext : new SimpleTypeResolveContext(method);
StatementBuilder builder = new StatementBuilder(typeSystem.GetSpecializingTypeSystem(context), this.decompilationContext, function, settings, cancellationToken);
var body = builder.ConvertAsBlock(function.Body);
Comment prev = null;
@ -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.DelegateType, function)
.WithILInstruction(function);
}
IType InferReturnType(BlockStatement body)
{
var returnExpressions = new List<ResolveResult>();
@ -1459,17 +1465,17 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1459,17 +1465,17 @@ namespace ICSharpCode.Decompiler.CSharp
return SpecialType.UnknownType;
}
IEnumerable<ParameterDeclaration> MakeParameters(IMethod method, ILFunction function)
IEnumerable<ParameterDeclaration> MakeParameters(IList<IParameter> parameters, ILFunction function)
{
var variables = function.Variables.Where(v => v.Kind == VariableKind.Parameter).ToDictionary(v => v.Index);
int i = 0;
foreach (var parameter in method.Parameters) {
foreach (var parameter in parameters) {
var pd = astBuilder.ConvertParameter(parameter);
if (settings.AnonymousTypes && parameter.Type.ContainsAnonymousType())
pd.Type = null;
ILVariable v;
if (variables.TryGetValue(i, out v))
pd.AddAnnotation(new ILVariableResolveResult(v, method.Parameters[i].Type));
pd.AddAnnotation(new ILVariableResolveResult(v, parameters[i].Type));
yield return pd;
i++;
}
@ -1701,7 +1707,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1701,7 +1707,12 @@ namespace ICSharpCode.Decompiler.CSharp
{
return Translate(inst.Argument).ConvertTo(inst.Type, this);
}
protected internal override TranslatedExpression VisitExpressionTreeCast(ExpressionTreeCast inst, TranslationContext context)
{
return Translate(inst.Argument).ConvertTo(inst.Type, this, inst.IsChecked);
}
protected internal override TranslatedExpression VisitArglist(Arglist inst, TranslationContext context)
{
return new UndocumentedExpression { UndocumentedExpressionType = UndocumentedExpressionType.ArgListAccess }

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; }

12
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -35,16 +35,16 @@ namespace ICSharpCode.Decompiler.CSharp @@ -35,16 +35,16 @@ namespace ICSharpCode.Decompiler.CSharp
{
internal readonly ExpressionBuilder exprBuilder;
readonly ILFunction currentFunction;
readonly IMethod currentMethod;
readonly IDecompilerTypeSystem typeSystem;
readonly DecompilerSettings settings;
readonly CancellationToken cancellationToken;
public StatementBuilder(IDecompilerTypeSystem typeSystem, ITypeResolveContext decompilationContext, IMethod currentMethod, ILFunction currentFunction, DecompilerSettings settings, CancellationToken cancellationToken)
public StatementBuilder(IDecompilerTypeSystem typeSystem, ITypeResolveContext decompilationContext, ILFunction currentFunction, DecompilerSettings settings, CancellationToken cancellationToken)
{
Debug.Assert(typeSystem != null && decompilationContext != null && currentMethod != null);
Debug.Assert(typeSystem != null && decompilationContext != null);
this.exprBuilder = new ExpressionBuilder(typeSystem, decompilationContext, settings, cancellationToken);
this.currentFunction = currentFunction;
this.currentMethod = currentMethod;
this.typeSystem = typeSystem;
this.settings = settings;
this.cancellationToken = cancellationToken;
}
@ -263,7 +263,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -263,7 +263,7 @@ namespace ICSharpCode.Decompiler.CSharp
if (currentFunction.IsIterator)
return new YieldBreakStatement();
else if (!inst.Value.MatchNop()) {
IType targetType = currentFunction.IsAsync ? currentFunction.AsyncReturnType : currentMethod.ReturnType;
IType targetType = currentFunction.IsAsync ? currentFunction.AsyncReturnType : currentFunction.ReturnType;
return new ReturnStatement(exprBuilder.Translate(inst.Value).ConvertTo(targetType, exprBuilder, allowImplicitConversion: true));
} else
return new ReturnStatement();
@ -288,7 +288,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -288,7 +288,7 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override Statement VisitYieldReturn(YieldReturn inst)
{
var elementType = currentMethod.ReturnType.GetElementTypeFromIEnumerable(currentMethod.Compilation, true, out var isGeneric);
var elementType = currentFunction.ReturnType.GetElementTypeFromIEnumerable(typeSystem.Compilation, true, out var isGeneric);
return new YieldReturnStatement {
Expression = exprBuilder.Translate(inst.Value).ConvertTo(elementType, exprBuilder)
};

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);

2
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -283,6 +283,7 @@ @@ -283,6 +283,7 @@
<Compile Include="IL\Instructions\CallIndirect.cs" />
<Compile Include="IL\Instructions\DefaultValue.cs" />
<Compile Include="IL\Transforms\EarlyExpressionTransforms.cs" />
<Compile Include="IL\Transforms\ExpressionTreeCast.cs" />
<Compile Include="IL\Transforms\HighLevelLoopTransform.cs" />
<Compile Include="IL\Transforms\ProxyCallReplacer.cs" />
<Compile Include="IL\Instructions\StringToInt.cs" />
@ -290,6 +291,7 @@ @@ -290,6 +291,7 @@
<Compile Include="IL\Transforms\InlineReturnTransform.cs" />
<Compile Include="IL\Transforms\SwitchOnNullableTransform.cs" />
<Compile Include="IL\Transforms\SwitchOnStringTransform.cs" />
<Compile Include="IL\Transforms\TransformExpressionTrees.cs" />
<Compile Include="IL\Transforms\UsingTransform.cs" />
<Compile Include="IL\ControlFlow\AsyncAwaitDecompiler.cs" />
<Compile Include="IL\ControlFlow\ControlFlowGraph.cs" />

55
ICSharpCode.Decompiler/IL/Instructions.cs

@ -168,6 +168,8 @@ namespace ICSharpCode.Decompiler.IL @@ -168,6 +168,8 @@ namespace ICSharpCode.Decompiler.IL
ArrayToPointer,
/// <summary>Maps a string value to an integer. This is used in switch(string).</summary>
StringToInt,
/// <summary>ILAst representation of Expression.Convert.</summary>
ExpressionTreeCast,
/// <summary>Push a typed reference of type class onto the stack.</summary>
MakeRefAny,
/// <summary>Push the type token stored in a typed reference.</summary>
@ -4493,6 +4495,46 @@ namespace ICSharpCode.Decompiler.IL @@ -4493,6 +4495,46 @@ namespace ICSharpCode.Decompiler.IL
}
}
namespace ICSharpCode.Decompiler.IL
{
/// <summary>ILAst representation of Expression.Convert.</summary>
public sealed partial class ExpressionTreeCast : UnaryInstruction
{
IType type;
/// <summary>Returns the type operand.</summary>
public IType Type {
get { return type; }
set { type = value; InvalidateFlags(); }
}
public override StackType ResultType { get { return type.GetStackType(); } }
protected override InstructionFlags ComputeFlags()
{
return base.ComputeFlags() | InstructionFlags.MayThrow;
}
public override InstructionFlags DirectFlags {
get {
return base.DirectFlags | InstructionFlags.MayThrow;
}
}
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitExpressionTreeCast(this);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{
return visitor.VisitExpressionTreeCast(this);
}
public override T AcceptVisitor<C, T>(ILVisitor<C, T> visitor, C context)
{
return visitor.VisitExpressionTreeCast(this, context);
}
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{
var o = other as ExpressionTreeCast;
return o != null && this.Argument.PerformMatch(o.Argument, ref match) && type.Equals(o.type) && this.IsChecked == o.IsChecked;
}
}
}
namespace ICSharpCode.Decompiler.IL
{
/// <summary>Push a typed reference of type class onto the stack.</summary>
public sealed partial class MakeRefAny : UnaryInstruction
@ -5132,6 +5174,10 @@ namespace ICSharpCode.Decompiler.IL @@ -5132,6 +5174,10 @@ namespace ICSharpCode.Decompiler.IL
{
Default(inst);
}
protected internal virtual void VisitExpressionTreeCast(ExpressionTreeCast inst)
{
Default(inst);
}
protected internal virtual void VisitMakeRefAny(MakeRefAny inst)
{
Default(inst);
@ -5434,6 +5480,10 @@ namespace ICSharpCode.Decompiler.IL @@ -5434,6 +5480,10 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst);
}
protected internal virtual T VisitExpressionTreeCast(ExpressionTreeCast inst)
{
return Default(inst);
}
protected internal virtual T VisitMakeRefAny(MakeRefAny inst)
{
return Default(inst);
@ -5736,6 +5786,10 @@ namespace ICSharpCode.Decompiler.IL @@ -5736,6 +5786,10 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst, context);
}
protected internal virtual T VisitExpressionTreeCast(ExpressionTreeCast inst, C context)
{
return Default(inst, context);
}
protected internal virtual T VisitMakeRefAny(MakeRefAny inst, C context)
{
return Default(inst, context);
@ -5829,6 +5883,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5829,6 +5883,7 @@ namespace ICSharpCode.Decompiler.IL
"ldelema",
"array.to.pointer",
"string.to.int",
"expression.tree.cast",
"mkrefany",
"refanytype",
"refanyval",

4
ICSharpCode.Decompiler/IL/Instructions.tt

@ -240,6 +240,10 @@ @@ -240,6 +240,10 @@
new OpCode("string.to.int", "Maps a string value to an integer. This is used in switch(string).",
CustomArguments("argument"), CustomConstructor, CustomWriteTo, ResultType("I4")),
new OpCode("expression.tree.cast", "ILAst representation of Expression.Convert.",
CustomClassName("ExpressionTreeCast"), Unary, HasTypeOperand, MayThrow, CustomConstructor, CustomWriteTo, ResultType("type.GetStackType()"),
MatchCondition("this.IsChecked == o.IsChecked")),
new OpCode("mkrefany", "Push a typed reference of type class onto the stack.",
CustomClassName("MakeRefAny"), Unary, HasTypeOperand, ResultType("O")),
new OpCode("refanytype", "Push the type token stored in a typed reference.",

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

@ -61,11 +61,33 @@ namespace ICSharpCode.Decompiler.IL @@ -61,11 +61,33 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
public IType AsyncReturnType;
/// <summary>
/// If this is an expression tree or delegate, returns the expression tree type Expression{T} or T.
/// T is the delegate type that matches the signature of this method.
/// </summary>
public IType DelegateType;
public bool IsExpressionTree => DelegateType != null && DelegateType.FullName == "System.Linq.Expressions.Expression" && DelegateType.TypeParameterCount == 1;
public readonly IType ReturnType;
public readonly IList<IParameter> Parameters;
public ILFunction(IMethod method, MethodDefinition cecilMethod, ILInstruction body) : base(OpCode.ILFunction)
{
this.Body = body;
this.Method = method;
this.CecilMethod = cecilMethod;
this.ReturnType = Method?.ReturnType;
this.Parameters = Method?.Parameters;
this.Variables = new ILVariableCollection(this);
}
public ILFunction(IType returnType, IList<IParameter> parameters, ILInstruction body) : base(OpCode.ILFunction)
{
this.Body = body;
this.ReturnType = returnType;
this.Parameters = parameters;
this.Variables = new ILVariableCollection(this);
}
@ -91,6 +113,14 @@ namespace ICSharpCode.Decompiler.IL @@ -91,6 +113,14 @@ namespace ICSharpCode.Decompiler.IL
output.Write(' ');
Method.WriteTo(output);
}
if (IsExpressionTree) {
output.Write(".ET");
}
if (DelegateType != null) {
output.Write("[");
DelegateType.WriteTo(output);
output.Write("]");
}
output.WriteLine(" {");
output.Indent();

11
ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs

@ -61,8 +61,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -61,8 +61,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
currentFieldNames = function.CecilMethod.DeclaringType.Fields.Select(f => f.Name).ToArray();
reservedVariableNames = new Dictionary<string, int>();
loopCounters = CollectLoopCounters(function);
foreach (var p in function.Descendants.OfType<ILFunction>().Select(f => f.Method).SelectMany(m => m.Parameters))
AddExistingName(reservedVariableNames, p.Name);
foreach (var f in function.Descendants.OfType<ILFunction>()) {
if (f.Method != null) {
foreach (var p in f.Method.Parameters)
AddExistingName(reservedVariableNames, p.Name);
} else {
foreach (var p in f.Variables.Where(v => v.Kind == VariableKind.Parameter))
AddExistingName(reservedVariableNames, p.Name);
}
}
foreach (ILFunction f in function.Descendants.OfType<ILFunction>().Reverse()) {
PerformAssignment(f);
}

14
ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs

@ -44,13 +44,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -44,13 +44,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
foreach (var call in block.Instructions[i].Descendants.OfType<NewObj>()) {
ILFunction f = TransformDelegateConstruction(call, out ILInstruction target);
if (f != null) {
f.AddILRange(call.Arguments[0].ILRange);
f.AddILRange(call.Arguments[1].ILRange);
call.Arguments[0].ReplaceWith(new LdNull());
call.Arguments[1].ReplaceWith(f);
if (target is IInstructionWithVariableOperand && !target.MatchLdThis())
targetsToReplace.Add((IInstructionWithVariableOperand)target);
}
call.ReplaceWith(f);
if (target is IInstructionWithVariableOperand && !target.MatchLdThis())
targetsToReplace.Add((IInstructionWithVariableOperand)target);
}
}
if (block.Instructions[i].MatchStLoc(out ILVariable targetVariable, out ILInstruction value)) {
var newObj = value as NewObj;
@ -144,6 +141,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -144,6 +141,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var ilReader = new ILReader(localTypeSystem);
ilReader.UseDebugSymbols = context.Settings.UseDebugSymbols;
var function = ilReader.ReadIL(methodDefinition.Body, context.CancellationToken);
function.DelegateType = value.Method.DeclaringType;
function.CheckInvariant(ILPhase.Normal);
var contextPrefix = targetMethod.Name;
@ -158,6 +156,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -158,6 +156,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
function.AcceptVisitor(new ReplaceDelegateTargetVisitor(target, function.Variables.SingleOrDefault(v => v.Index == -1 && v.Kind == VariableKind.Parameter)));
// handle nested lambdas
((IILTransform)new DelegateConstruction()).Run(function, nestedContext);
function.AddILRange(target.ILRange);
function.AddILRange(value.Arguments[1].ILRange);
return function;
}
return null;

31
ICSharpCode.Decompiler/IL/Transforms/ExpressionTreeCast.cs

@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Text;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
partial class ExpressionTreeCast
{
public bool IsChecked { get; set; }
public ExpressionTreeCast(IType type, ILInstruction argument, bool isChecked)
: base(OpCode.ExpressionTreeCast, argument)
{
this.Type = type;
this.IsChecked = isChecked;
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
if (IsChecked) output.Write(".checked");
output.Write(' ');
type.WriteTo(output);
output.Write('(');
Argument.WriteTo(output, options);
output.Write(')');
}
}
}

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

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save