diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs
index 6b3f735a1..f765825e3 100644
--- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs
+++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs
@@ -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}");
}
diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
index fa9d9f74d..01888ac50 100644
--- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
+++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
@@ -1422,7 +1422,7 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitILFunction(ILFunction function, TranslationContext context)
{
- return TranslateFunction(function.ExpressionTreeType, function)
+ return TranslateFunction(function.DelegateType, function)
.WithILInstruction(function);
}
diff --git a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
index 3d86c549f..257d48f78 100644
--- a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
+++ b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
@@ -62,12 +62,12 @@ namespace ICSharpCode.Decompiler.IL
public IType AsyncReturnType;
///
- /// If this is an expression tree, returns Expression{T}, otherwise null.
+ /// 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.
///
- public IType ExpressionTreeType;
+ public IType DelegateType;
- public bool IsExpressionTree => ExpressionTreeType != null;
+ public bool IsExpressionTree => DelegateType != null && DelegateType.FullName == "System.Linq.Expressions.Expression" && DelegateType.TypeParameterCount == 1;
public readonly IType ReturnType;
@@ -78,8 +78,8 @@ namespace ICSharpCode.Decompiler.IL
this.Body = body;
this.Method = method;
this.CecilMethod = cecilMethod;
- this.ReturnType = Method.ReturnType;
- this.Parameters = Method.Parameters;
+ this.ReturnType = Method?.ReturnType;
+ this.Parameters = Method?.Parameters;
this.Variables = new ILVariableCollection(this);
}
@@ -113,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();
diff --git a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs
index 33285198a..47bea003f 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs
@@ -44,13 +44,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
foreach (var call in block.Instructions[i].Descendants.OfType()) {
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
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
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;
diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs
index 8b007e13e..ecbdbeae0 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs
@@ -18,6 +18,7 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.CSharp.Resolver;
using ICSharpCode.Decompiler.TypeSystem;
@@ -150,7 +151,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var functionType = instruction.Method.ReturnType.TypeArguments[0];
var function = new ILFunction(functionType.TypeArguments.LastOrDefault() ?? context.TypeSystem.Compilation.FindType(KnownTypeCode.Void), parameterList, container);
if (isQuotedLambda || lambdaStack.Count == 0)
- function.ExpressionTreeType = instruction.Method.ReturnType;
+ function.DelegateType = instruction.Method.ReturnType;
+ else
+ function.DelegateType = functionType;
function.Variables.AddRange(parameterVariablesList);
lambdaStack.Push(function);
var (bodyInstruction, type) = ConvertInstruction(instruction.Arguments[0]);
@@ -159,11 +162,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return (null, SpecialType.UnknownType);
container.ExpectedResultType = bodyInstruction.ResultType;
container.Blocks.Add(new Block() { Instructions = { new Leave(container, bodyInstruction) } });
- if (!isQuotedLambda && lambdaStack.Count > 0)
- return (new NewObj(functionType.GetConstructors().Single()) {
- Arguments = { new LdNull(), function }
- }, functionType);
- return (function, function.ExpressionTreeType);
+ return (function, function.DelegateType);
}
bool ReadParameters(ILInstruction initializer, IList parameters, IList parameterVariables, ITypeResolveContext resolveContext)
@@ -199,111 +198,140 @@ namespace ICSharpCode.Decompiler.IL.Transforms
(ILInstruction, IType) ConvertInstruction(ILInstruction instruction)
{
- switch (instruction) {
- case CallInstruction invocation:
- if (invocation.Method.DeclaringType.FullName != "System.Linq.Expressions.Expression")
- return (null, SpecialType.UnknownType);
+ var result = Convert();
+ if (result.Item1 != null) {
+ Debug.Assert(result.Item2 != null, "IType must be non-null!");
+ Debug.Assert(result.Item1.ResultType == result.Item2.GetStackType(), "StackTypes must match!");
+ }
+ return result;
- switch (invocation.Method.Name) {
- case "Add":
- return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Add, false);
- case "AddChecked":
- return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Add, true);
- case "And":
- return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.BitAnd);
- case "AndAlso":
- return ConvertLogicOperator(invocation, true);
- case "ArrayAccess":
- case "ArrayIndex":
- return ConvertArrayIndex(invocation);
- case "ArrayLength":
- return ConvertArrayLength(invocation);
- case "Call":
- return ConvertCall(invocation);
- case "Coalesce":
- return ConvertCoalesce(invocation);
- case "Condition":
- return ConvertCondition(invocation);
- case "Constant":
- return ConvertConstant(invocation);
- case "Convert":
- return ConvertCast(invocation, false);
- case "ConvertChecked":
- return ConvertCast(invocation, true);
- case "Divide":
- return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Div);
- case "Equal":
- return ConvertComparison(invocation, ComparisonKind.Equality);
- case "ExclusiveOr":
- return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.BitXor);
- case "Field":
- return ConvertField(invocation);
- case "GreaterThan":
- return ConvertComparison(invocation, ComparisonKind.GreaterThan);
- case "GreaterThanOrEqual":
- return ConvertComparison(invocation, ComparisonKind.GreaterThanOrEqual);
- case "Invoke":
- return ConvertInvoke(invocation);
- case "Lambda":
- return ConvertLambda(invocation);
- case "LeftShift":
- return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.ShiftLeft);
- case "LessThan":
- return ConvertComparison(invocation, ComparisonKind.LessThan);
- case "LessThanOrEqual":
- return ConvertComparison(invocation, ComparisonKind.LessThanOrEqual);
- case "ListInit":
- return ConvertListInit(invocation);
- case "MemberInit":
- return ConvertMemberInit(invocation);
- case "Modulo":
- return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Rem);
- case "Multiply":
- return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Mul, false);
- case "MultiplyChecked":
- return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Mul, true);
- case "Negate":
- return ConvertUnaryNumericOperator(invocation, BinaryNumericOperator.Sub, false);
- case "NegateChecked":
- return ConvertUnaryNumericOperator(invocation, BinaryNumericOperator.Sub, true);
- case "New":
- return ConvertNewObject(invocation);
- case "NewArrayBounds":
- return ConvertNewArrayBounds(invocation);
- case "NewArrayInit":
- return ConvertNewArrayInit(invocation);
- case "Not":
- return ConvertNotOperator(invocation);
- case "NotEqual":
- return ConvertComparison(invocation, ComparisonKind.Inequality);
- case "OnesComplement":
- return ConvertNotOperator(invocation);
- case "Or":
- return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.BitOr);
- case "OrElse":
- return ConvertLogicOperator(invocation, false);
- case "Property":
- return ConvertProperty(invocation);
- case "Quote":
- if (invocation.Arguments.Count == 1)
- return ConvertInstruction(invocation.Arguments.Single());
- else
+ (ILInstruction, IType) Convert() {
+ switch (instruction) {
+ case CallInstruction invocation:
+ if (invocation.Method.DeclaringType.FullName != "System.Linq.Expressions.Expression")
+ return (null, SpecialType.UnknownType);
+
+ switch (invocation.Method.Name) {
+ case "Add":
+ return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Add, false);
+ case "AddChecked":
+ return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Add, true);
+ case "And":
+ return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.BitAnd);
+ case "AndAlso":
+ return ConvertLogicOperator(invocation, true);
+ case "ArrayAccess":
+ case "ArrayIndex":
+ return ConvertArrayIndex(invocation);
+ case "ArrayLength":
+ return ConvertArrayLength(invocation);
+ case "Call":
+ return ConvertCall(invocation);
+ case "Coalesce":
+ return ConvertCoalesce(invocation);
+ case "Condition":
+ return ConvertCondition(invocation);
+ case "Constant":
+ return ConvertConstant(invocation);
+ case "Convert":
+ return ConvertCast(invocation, false);
+ case "ConvertChecked":
+ return ConvertCast(invocation, true);
+ case "Divide":
+ return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Div);
+ case "Equal":
+ return ConvertComparison(invocation, ComparisonKind.Equality);
+ case "ExclusiveOr":
+ return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.BitXor);
+ case "Field":
+ return ConvertField(invocation);
+ case "GreaterThan":
+ return ConvertComparison(invocation, ComparisonKind.GreaterThan);
+ case "GreaterThanOrEqual":
+ return ConvertComparison(invocation, ComparisonKind.GreaterThanOrEqual);
+ case "Invoke":
+ return ConvertInvoke(invocation);
+ case "Lambda":
+ return ConvertLambda(invocation);
+ case "LeftShift":
+ return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.ShiftLeft);
+ case "LessThan":
+ return ConvertComparison(invocation, ComparisonKind.LessThan);
+ case "LessThanOrEqual":
+ return ConvertComparison(invocation, ComparisonKind.LessThanOrEqual);
+ case "ListInit":
+ return ConvertListInit(invocation);
+ case "MemberInit":
+ return ConvertMemberInit(invocation);
+ case "Modulo":
+ return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Rem);
+ case "Multiply":
+ return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Mul, false);
+ case "MultiplyChecked":
+ return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Mul, true);
+ case "Negate":
+ return ConvertUnaryNumericOperator(invocation, BinaryNumericOperator.Sub, false);
+ case "NegateChecked":
+ return ConvertUnaryNumericOperator(invocation, BinaryNumericOperator.Sub, true);
+ case "New":
+ return ConvertNewObject(invocation);
+ case "NewArrayBounds":
+ return ConvertNewArrayBounds(invocation);
+ case "NewArrayInit":
+ return ConvertNewArrayInit(invocation);
+ case "Not":
+ return ConvertNotOperator(invocation);
+ case "NotEqual":
+ return ConvertComparison(invocation, ComparisonKind.Inequality);
+ case "OnesComplement":
+ return ConvertNotOperator(invocation);
+ case "Or":
+ return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.BitOr);
+ case "OrElse":
+ return ConvertLogicOperator(invocation, false);
+ case "Property":
+ return ConvertProperty(invocation);
+ case "Quote":
+ if (invocation.Arguments.Count == 1)
+ return ConvertInstruction(invocation.Arguments.Single());
+ else
+ return (null, SpecialType.UnknownType);
+ case "RightShift":
+ return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.ShiftRight);
+ case "Subtract":
+ return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Sub, false);
+ case "SubtractChecked":
+ return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Sub, true);
+ case "TypeAs":
+ return ConvertTypeAs(invocation);
+ case "TypeIs":
+ return ConvertTypeIs(invocation);
+ }
+ return (null, SpecialType.UnknownType);
+ case ILFunction function:
+ if (function.IsExpressionTree) {
+ function.DelegateType = UnwrapExpressionTree(function.DelegateType);
+ }
+ return (function, function.DelegateType);
+ case LdLoc ldloc:
+ if (IsExpressionTreeParameter(ldloc.Variable)) {
+ if (!parameterMapping.TryGetValue(ldloc.Variable, out var v))
return (null, SpecialType.UnknownType);
- case "RightShift":
- return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.ShiftRight);
- case "Subtract":
- return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Sub, false);
- case "SubtractChecked":
- return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Sub, true);
- case "TypeAs":
- return ConvertTypeAs(invocation);
- case "TypeIs":
- return ConvertTypeIs(invocation);
- }
- return (null, SpecialType.UnknownType);
- default:
- return ConvertValue(instruction, instruction.Parent);
+ return (new LdLoc(v), v.Type);
+ }
+ return (null, SpecialType.UnknownType);
+ default:
+ return (null, SpecialType.UnknownType);
+ }
+ }
+ }
+
+ IType UnwrapExpressionTree(IType delegateType)
+ {
+ if (delegateType is ParameterizedType pt && pt.FullName == "System.Linq.Expressions.Expression" && pt.TypeArguments.Count == 1) {
+ return pt.TypeArguments[0];
}
+ return delegateType;
}
(ILInstruction, IType) ConvertArrayIndex(CallInstruction invocation)
@@ -534,11 +562,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return (null, SpecialType.UnknownType);
if (value.MatchBox(out var arg, out var boxType)) {
if (boxType.Kind == TypeKind.Enum || boxType.IsKnownType(KnownTypeCode.Boolean))
- return (new ExpressionTreeCast(boxType, ConvertValue(arg, invocation).Item1, false), boxType);
- value = ConvertValue(arg, invocation).Item1;
+ return (new ExpressionTreeCast(boxType, ConvertValue(arg, invocation), false), boxType);
+ value = ConvertValue(arg, invocation);
return (value, type);
}
- return ConvertValue(value, invocation);
+ return (ConvertValue(value, invocation), type);
}
(ILInstruction, IType) ConvertElementInit(CallInstruction invocation)
@@ -727,7 +755,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return (null, SpecialType.UnknownType);
indices[i] = index;
}
- return (new NewArr(type, indices), type);
+ return (new NewArr(type, indices), new ArrayType(context.TypeSystem.Compilation, type, arguments.Count));
}
(ILInstruction, IType) ConvertNewArrayInit(CallInstruction invocation)
@@ -764,10 +792,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
switch (invocation.Arguments.Count) {
case 1:
if (MatchGetTypeFromHandle(invocation.Arguments[0], out var type)) {
- var ctors = type.GetConstructors().ToArray();
- if (ctors.Length != 1 || ctors[0].Parameters.Count > 0)
+ var ctor = type.GetConstructors(c => c.Parameters.Count == 0).FirstOrDefault();
+ if (ctor == null)
return (null, SpecialType.UnknownType);
- return (new NewObj(ctors[0]), type);
+ return (new NewObj(ctor), type);
}
if (MatchGetConstructorFromHandle(invocation.Arguments[0], out member)) {
return (new NewObj((IMethod)member), member.DeclaringType);
@@ -917,27 +945,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return (null, SpecialType.UnknownType);
}
- (ILInstruction, IType) ConvertValue(ILInstruction value, ILInstruction context)
+ ILInstruction ConvertValue(ILInstruction value, ILInstruction context)
{
switch (value) {
case LdLoc ldloc:
if (IsExpressionTreeParameter(ldloc.Variable)) {
if (!parameterMapping.TryGetValue(ldloc.Variable, out var v))
- return (null, SpecialType.UnknownType);
+ return null;
if (context is CallInstruction parentCall
&& parentCall.Method.FullName == "System.Linq.Expressions.Expression.Call"
&& v.StackType.IsIntegerType())
- return (new LdLoca(v), new ByReferenceType(v.Type));
- return (new LdLoc(v), v.Type);
+ return new LdLoca(v);
+ return null;
} else {
if (ldloc.Variable.Kind != VariableKind.StackSlot)
- return (new LdLoc(ldloc.Variable), ldloc.Variable.Type);
- return (null, SpecialType.UnknownType);
+ return new LdLoc(ldloc.Variable);
+ return null;
}
default:
- if (SemanticHelper.IsPure(value.Flags))
- return (value.Clone(), value.InferType());
- return (value, value.InferType());
+ return value.Clone();
}
}