Browse Source

Rename ExpressionTreeType to DelegateType and use ILFunction.DelegateType instead of the NewObj(LdNull, ILFunction) pattern in DelegateConstruction and ExpressionTrees

pull/988/head
Siegfried Pammer 8 years ago
parent
commit
25eecb90e1
  1. 4
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  2. 2
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  3. 18
      ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
  4. 14
      ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs
  5. 274
      ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs

4
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

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

2
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -1422,7 +1422,7 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitILFunction(ILFunction function, TranslationContext context) protected internal override TranslatedExpression VisitILFunction(ILFunction function, TranslationContext context)
{ {
return TranslateFunction(function.ExpressionTreeType, function) return TranslateFunction(function.DelegateType, function)
.WithILInstruction(function); .WithILInstruction(function);
} }

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

@ -62,12 +62,12 @@ namespace ICSharpCode.Decompiler.IL
public IType AsyncReturnType; public IType AsyncReturnType;
/// <summary> /// <summary>
/// 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. /// T is the delegate type that matches the signature of this method.
/// </summary> /// </summary>
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; public readonly IType ReturnType;
@ -78,8 +78,8 @@ namespace ICSharpCode.Decompiler.IL
this.Body = body; this.Body = body;
this.Method = method; this.Method = method;
this.CecilMethod = cecilMethod; this.CecilMethod = cecilMethod;
this.ReturnType = Method.ReturnType; this.ReturnType = Method?.ReturnType;
this.Parameters = Method.Parameters; this.Parameters = Method?.Parameters;
this.Variables = new ILVariableCollection(this); this.Variables = new ILVariableCollection(this);
} }
@ -113,6 +113,14 @@ namespace ICSharpCode.Decompiler.IL
output.Write(' '); output.Write(' ');
Method.WriteTo(output); Method.WriteTo(output);
} }
if (IsExpressionTree) {
output.Write(".ET");
}
if (DelegateType != null) {
output.Write("[");
DelegateType.WriteTo(output);
output.Write("]");
}
output.WriteLine(" {"); output.WriteLine(" {");
output.Indent(); output.Indent();

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

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

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

@ -18,6 +18,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Resolver;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
@ -150,7 +151,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var functionType = instruction.Method.ReturnType.TypeArguments[0]; var functionType = instruction.Method.ReturnType.TypeArguments[0];
var function = new ILFunction(functionType.TypeArguments.LastOrDefault() ?? context.TypeSystem.Compilation.FindType(KnownTypeCode.Void), parameterList, container); var function = new ILFunction(functionType.TypeArguments.LastOrDefault() ?? context.TypeSystem.Compilation.FindType(KnownTypeCode.Void), parameterList, container);
if (isQuotedLambda || lambdaStack.Count == 0) if (isQuotedLambda || lambdaStack.Count == 0)
function.ExpressionTreeType = instruction.Method.ReturnType; function.DelegateType = instruction.Method.ReturnType;
else
function.DelegateType = functionType;
function.Variables.AddRange(parameterVariablesList); function.Variables.AddRange(parameterVariablesList);
lambdaStack.Push(function); lambdaStack.Push(function);
var (bodyInstruction, type) = ConvertInstruction(instruction.Arguments[0]); var (bodyInstruction, type) = ConvertInstruction(instruction.Arguments[0]);
@ -159,11 +162,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
container.ExpectedResultType = bodyInstruction.ResultType; container.ExpectedResultType = bodyInstruction.ResultType;
container.Blocks.Add(new Block() { Instructions = { new Leave(container, bodyInstruction) } }); container.Blocks.Add(new Block() { Instructions = { new Leave(container, bodyInstruction) } });
if (!isQuotedLambda && lambdaStack.Count > 0) return (function, function.DelegateType);
return (new NewObj(functionType.GetConstructors().Single()) {
Arguments = { new LdNull(), function }
}, functionType);
return (function, function.ExpressionTreeType);
} }
bool ReadParameters(ILInstruction initializer, IList<IParameter> parameters, IList<ILVariable> parameterVariables, ITypeResolveContext resolveContext) bool ReadParameters(ILInstruction initializer, IList<IParameter> parameters, IList<ILVariable> parameterVariables, ITypeResolveContext resolveContext)
@ -199,111 +198,140 @@ namespace ICSharpCode.Decompiler.IL.Transforms
(ILInstruction, IType) ConvertInstruction(ILInstruction instruction) (ILInstruction, IType) ConvertInstruction(ILInstruction instruction)
{ {
switch (instruction) { var result = Convert();
case CallInstruction invocation: if (result.Item1 != null) {
if (invocation.Method.DeclaringType.FullName != "System.Linq.Expressions.Expression") Debug.Assert(result.Item2 != null, "IType must be non-null!");
return (null, SpecialType.UnknownType); Debug.Assert(result.Item1.ResultType == result.Item2.GetStackType(), "StackTypes must match!");
}
return result;
switch (invocation.Method.Name) { (ILInstruction, IType) Convert() {
case "Add": switch (instruction) {
return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Add, false); case CallInstruction invocation:
case "AddChecked": if (invocation.Method.DeclaringType.FullName != "System.Linq.Expressions.Expression")
return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Add, true); return (null, SpecialType.UnknownType);
case "And":
return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.BitAnd); switch (invocation.Method.Name) {
case "AndAlso": case "Add":
return ConvertLogicOperator(invocation, true); return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Add, false);
case "ArrayAccess": case "AddChecked":
case "ArrayIndex": return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Add, true);
return ConvertArrayIndex(invocation); case "And":
case "ArrayLength": return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.BitAnd);
return ConvertArrayLength(invocation); case "AndAlso":
case "Call": return ConvertLogicOperator(invocation, true);
return ConvertCall(invocation); case "ArrayAccess":
case "Coalesce": case "ArrayIndex":
return ConvertCoalesce(invocation); return ConvertArrayIndex(invocation);
case "Condition": case "ArrayLength":
return ConvertCondition(invocation); return ConvertArrayLength(invocation);
case "Constant": case "Call":
return ConvertConstant(invocation); return ConvertCall(invocation);
case "Convert": case "Coalesce":
return ConvertCast(invocation, false); return ConvertCoalesce(invocation);
case "ConvertChecked": case "Condition":
return ConvertCast(invocation, true); return ConvertCondition(invocation);
case "Divide": case "Constant":
return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Div); return ConvertConstant(invocation);
case "Equal": case "Convert":
return ConvertComparison(invocation, ComparisonKind.Equality); return ConvertCast(invocation, false);
case "ExclusiveOr": case "ConvertChecked":
return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.BitXor); return ConvertCast(invocation, true);
case "Field": case "Divide":
return ConvertField(invocation); return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Div);
case "GreaterThan": case "Equal":
return ConvertComparison(invocation, ComparisonKind.GreaterThan); return ConvertComparison(invocation, ComparisonKind.Equality);
case "GreaterThanOrEqual": case "ExclusiveOr":
return ConvertComparison(invocation, ComparisonKind.GreaterThanOrEqual); return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.BitXor);
case "Invoke": case "Field":
return ConvertInvoke(invocation); return ConvertField(invocation);
case "Lambda": case "GreaterThan":
return ConvertLambda(invocation); return ConvertComparison(invocation, ComparisonKind.GreaterThan);
case "LeftShift": case "GreaterThanOrEqual":
return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.ShiftLeft); return ConvertComparison(invocation, ComparisonKind.GreaterThanOrEqual);
case "LessThan": case "Invoke":
return ConvertComparison(invocation, ComparisonKind.LessThan); return ConvertInvoke(invocation);
case "LessThanOrEqual": case "Lambda":
return ConvertComparison(invocation, ComparisonKind.LessThanOrEqual); return ConvertLambda(invocation);
case "ListInit": case "LeftShift":
return ConvertListInit(invocation); return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.ShiftLeft);
case "MemberInit": case "LessThan":
return ConvertMemberInit(invocation); return ConvertComparison(invocation, ComparisonKind.LessThan);
case "Modulo": case "LessThanOrEqual":
return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Rem); return ConvertComparison(invocation, ComparisonKind.LessThanOrEqual);
case "Multiply": case "ListInit":
return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Mul, false); return ConvertListInit(invocation);
case "MultiplyChecked": case "MemberInit":
return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Mul, true); return ConvertMemberInit(invocation);
case "Negate": case "Modulo":
return ConvertUnaryNumericOperator(invocation, BinaryNumericOperator.Sub, false); return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Rem);
case "NegateChecked": case "Multiply":
return ConvertUnaryNumericOperator(invocation, BinaryNumericOperator.Sub, true); return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Mul, false);
case "New": case "MultiplyChecked":
return ConvertNewObject(invocation); return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Mul, true);
case "NewArrayBounds": case "Negate":
return ConvertNewArrayBounds(invocation); return ConvertUnaryNumericOperator(invocation, BinaryNumericOperator.Sub, false);
case "NewArrayInit": case "NegateChecked":
return ConvertNewArrayInit(invocation); return ConvertUnaryNumericOperator(invocation, BinaryNumericOperator.Sub, true);
case "Not": case "New":
return ConvertNotOperator(invocation); return ConvertNewObject(invocation);
case "NotEqual": case "NewArrayBounds":
return ConvertComparison(invocation, ComparisonKind.Inequality); return ConvertNewArrayBounds(invocation);
case "OnesComplement": case "NewArrayInit":
return ConvertNotOperator(invocation); return ConvertNewArrayInit(invocation);
case "Or": case "Not":
return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.BitOr); return ConvertNotOperator(invocation);
case "OrElse": case "NotEqual":
return ConvertLogicOperator(invocation, false); return ConvertComparison(invocation, ComparisonKind.Inequality);
case "Property": case "OnesComplement":
return ConvertProperty(invocation); return ConvertNotOperator(invocation);
case "Quote": case "Or":
if (invocation.Arguments.Count == 1) return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.BitOr);
return ConvertInstruction(invocation.Arguments.Single()); case "OrElse":
else 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); return (null, SpecialType.UnknownType);
case "RightShift": return (new LdLoc(v), v.Type);
return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.ShiftRight); }
case "Subtract": return (null, SpecialType.UnknownType);
return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Sub, false); default:
case "SubtractChecked": return (null, SpecialType.UnknownType);
return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Sub, true); }
case "TypeAs": }
return ConvertTypeAs(invocation); }
case "TypeIs":
return ConvertTypeIs(invocation); IType UnwrapExpressionTree(IType delegateType)
} {
return (null, SpecialType.UnknownType); if (delegateType is ParameterizedType pt && pt.FullName == "System.Linq.Expressions.Expression" && pt.TypeArguments.Count == 1) {
default: return pt.TypeArguments[0];
return ConvertValue(instruction, instruction.Parent);
} }
return delegateType;
} }
(ILInstruction, IType) ConvertArrayIndex(CallInstruction invocation) (ILInstruction, IType) ConvertArrayIndex(CallInstruction invocation)
@ -534,11 +562,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
if (value.MatchBox(out var arg, out var boxType)) { if (value.MatchBox(out var arg, out var boxType)) {
if (boxType.Kind == TypeKind.Enum || boxType.IsKnownType(KnownTypeCode.Boolean)) if (boxType.Kind == TypeKind.Enum || boxType.IsKnownType(KnownTypeCode.Boolean))
return (new ExpressionTreeCast(boxType, ConvertValue(arg, invocation).Item1, false), boxType); return (new ExpressionTreeCast(boxType, ConvertValue(arg, invocation), false), boxType);
value = ConvertValue(arg, invocation).Item1; value = ConvertValue(arg, invocation);
return (value, type); return (value, type);
} }
return ConvertValue(value, invocation); return (ConvertValue(value, invocation), type);
} }
(ILInstruction, IType) ConvertElementInit(CallInstruction invocation) (ILInstruction, IType) ConvertElementInit(CallInstruction invocation)
@ -727,7 +755,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
indices[i] = index; 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) (ILInstruction, IType) ConvertNewArrayInit(CallInstruction invocation)
@ -764,10 +792,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
switch (invocation.Arguments.Count) { switch (invocation.Arguments.Count) {
case 1: case 1:
if (MatchGetTypeFromHandle(invocation.Arguments[0], out var type)) { if (MatchGetTypeFromHandle(invocation.Arguments[0], out var type)) {
var ctors = type.GetConstructors().ToArray(); var ctor = type.GetConstructors(c => c.Parameters.Count == 0).FirstOrDefault();
if (ctors.Length != 1 || ctors[0].Parameters.Count > 0) if (ctor == null)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
return (new NewObj(ctors[0]), type); return (new NewObj(ctor), type);
} }
if (MatchGetConstructorFromHandle(invocation.Arguments[0], out member)) { if (MatchGetConstructorFromHandle(invocation.Arguments[0], out member)) {
return (new NewObj((IMethod)member), member.DeclaringType); return (new NewObj((IMethod)member), member.DeclaringType);
@ -917,27 +945,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
} }
(ILInstruction, IType) ConvertValue(ILInstruction value, ILInstruction context) ILInstruction ConvertValue(ILInstruction value, ILInstruction context)
{ {
switch (value) { switch (value) {
case LdLoc ldloc: case LdLoc ldloc:
if (IsExpressionTreeParameter(ldloc.Variable)) { if (IsExpressionTreeParameter(ldloc.Variable)) {
if (!parameterMapping.TryGetValue(ldloc.Variable, out var v)) if (!parameterMapping.TryGetValue(ldloc.Variable, out var v))
return (null, SpecialType.UnknownType); return null;
if (context is CallInstruction parentCall if (context is CallInstruction parentCall
&& parentCall.Method.FullName == "System.Linq.Expressions.Expression.Call" && parentCall.Method.FullName == "System.Linq.Expressions.Expression.Call"
&& v.StackType.IsIntegerType()) && v.StackType.IsIntegerType())
return (new LdLoca(v), new ByReferenceType(v.Type)); return new LdLoca(v);
return (new LdLoc(v), v.Type); return null;
} else { } else {
if (ldloc.Variable.Kind != VariableKind.StackSlot) if (ldloc.Variable.Kind != VariableKind.StackSlot)
return (new LdLoc(ldloc.Variable), ldloc.Variable.Type); return new LdLoc(ldloc.Variable);
return (null, SpecialType.UnknownType); return null;
} }
default: default:
if (SemanticHelper.IsPure(value.Flags)) return value.Clone();
return (value.Clone(), value.InferType());
return (value, value.InferType());
} }
} }

Loading…
Cancel
Save