Browse Source

Refactor TransformExpressionTrees to use an (ILInstruction, IType) pair as result of every Convert call.

pull/988/head
Siegfried Pammer 8 years ago
parent
commit
e3606374a4
  1. 41
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs
  2. 544
      ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs

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

@ -231,17 +231,17 @@ public class ExpressionTrees
private int C() private int C()
{ {
return field + 5; return this.field + 5;
} }
private bool MyEquals(ExpressionTrees other) private bool MyEquals(ExpressionTrees other)
{ {
return other != null && field == other.field; return other != null && this.field == other.field;
} }
public void MethodGroupAsExtensionMethod() public void MethodGroupAsExtensionMethod()
{ {
ExpressionTrees.ToCode(ExpressionTrees.X(), () => (Func<bool>)new[] { ExpressionTrees.ToCode(ExpressionTrees.X(), () => (Func<bool>)new int[4] {
2000, 2000,
2004, 2004,
2008, 2008,
@ -251,7 +251,7 @@ public class ExpressionTrees
public void MethodGroupConstant() public void MethodGroupConstant()
{ {
ExpressionTrees.ToCode(ExpressionTrees.X(), () => Array.TrueForAll(new[] { ExpressionTrees.ToCode(ExpressionTrees.X(), () => Array.TrueForAll(new int[4] {
2000, 2000,
2004, 2004,
2008, 2008,
@ -259,7 +259,7 @@ public class ExpressionTrees
}, DateTime.IsLeapYear)); }, DateTime.IsLeapYear));
HashSet<int> set = new HashSet<int>(); HashSet<int> set = new HashSet<int>();
ExpressionTrees.ToCode(ExpressionTrees.X(), () => new[] { ExpressionTrees.ToCode(ExpressionTrees.X(), () => new int[4] {
2000, 2000,
2004, 2004,
2008, 2008,
@ -267,7 +267,7 @@ public class ExpressionTrees
}.All(set.Add)); }.All(set.Add));
Func<Func<object, object, bool>, bool> sink = f => f(null, null); Func<Func<object, object, bool>, bool> sink = f => f(null, null);
ExpressionTrees.ToCode(ExpressionTrees.X(), () => sink(int.Equals)); ExpressionTrees.ToCode(ExpressionTrees.X(), () => sink(object.Equals));
} }
public void MultipleCasts() public void MultipleCasts()
@ -282,13 +282,19 @@ public class ExpressionTrees
public void NestedLambda() public void NestedLambda()
{ {
Func<Func<int>, int> call = f => f(); Func<Func<int>, int> call = (Func<int> f) => f();
//no params //no params
ExpressionTrees.ToCode(ExpressionTrees.X(), () => call(() => 42)); ExpressionTrees.ToCode(ExpressionTrees.X(), () => call(() => 42));
//one param //one param
ExpressionTrees.ToCode(ExpressionTrees.X(), () => new[] { 37, 42 }.Select(x => x * 2)); ExpressionTrees.ToCode(ExpressionTrees.X(), () => new int[2] {
37,
42
}.Select(x => x * 2));
//two params //two params
ExpressionTrees.ToCode(ExpressionTrees.X(), () => new[] { 37, 42 }.Select((x, i) => x * 2)); ExpressionTrees.ToCode(ExpressionTrees.X(), () => new int[2] {
37,
42
}.Select((int x, int i) => x * 2));
} }
public void CurriedLambda() public void CurriedLambda()
@ -367,7 +373,7 @@ public class ExpressionTrees
public void Quoted() public void Quoted()
{ {
ExpressionTrees.ToCode(ExpressionTrees.X(), () => (Expression<Func<int, string, string>>)((n, s) => s + n.ToString()) != null); ExpressionTrees.ToCode(ExpressionTrees.X(), () => (Expression<Func<int, string, string>>)((int n, string s) => s + n.ToString()) != null);
} }
public void Quoted2() public void Quoted2()
@ -387,12 +393,12 @@ public class ExpressionTrees
public void StaticCall() public void StaticCall()
{ {
ExpressionTrees.ToCode(ExpressionTrees.X(), () => Equals(3, 0)); ExpressionTrees.ToCode(ExpressionTrees.X(), () => object.Equals(3, 0));
} }
public void ThisCall() public void ThisCall()
{ {
ExpressionTrees.ToCode(ExpressionTrees.X(), () => !Equals(3)); ExpressionTrees.ToCode(ExpressionTrees.X(), () => !this.Equals(3));
} }
public void ThisExplicit() public void ThisExplicit()
@ -402,12 +408,15 @@ public class ExpressionTrees
public void TypedConstant() public void TypedConstant()
{ {
ExpressionTrees.ToCode(ExpressionTrees.X(), () => new[] { typeof(int), typeof(string) }); ExpressionTrees.ToCode(ExpressionTrees.X(), () => new Type[2] {
typeof(int),
typeof(string)
});
} }
public void StaticCallImplicitCast() public void StaticCallImplicitCast()
{ {
ExpressionTrees.ToCode(ExpressionTrees.X(), () => Equals(3, 0)); ExpressionTrees.ToCode(ExpressionTrees.X(), () => object.Equals(3, 0));
} }
public void StaticMembers() public void StaticMembers()
@ -429,12 +438,12 @@ public class ExpressionTrees
public void GenericClassInstance() public void GenericClassInstance()
{ {
ExpressionTrees.ToCode(ExpressionTrees.X(), () => new GenericClass<int>().InstanceField + new GenericClass<double>().InstanceProperty); ExpressionTrees.ToCode(ExpressionTrees.X(), () => (double)new GenericClass<int>().InstanceField + new GenericClass<double>().InstanceProperty);
} }
public void GenericClassStatic() public void GenericClassStatic()
{ {
ExpressionTrees.ToCode(ExpressionTrees.X(), () => GenericClass<int>.StaticField + GenericClass<double>.StaticProperty); ExpressionTrees.ToCode(ExpressionTrees.X(), () => (double)GenericClass<int>.StaticField + GenericClass<double>.StaticProperty);
} }
public void InvokeGenericMethod() public void InvokeGenericMethod()

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

@ -91,10 +91,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
Dictionary<ILVariable, ILVariable> parameterMapping; Dictionary<ILVariable, ILVariable> parameterMapping;
List<ILInstruction> instructionsToRemove; List<ILInstruction> instructionsToRemove;
Stack<ILFunction> lambdaStack; Stack<ILFunction> lambdaStack;
CSharpConversions conversions;
public void Run(Block block, int pos, StatementTransformContext context) public void Run(Block block, int pos, StatementTransformContext context)
{ {
this.context = context; this.context = context;
this.conversions = CSharpConversions.Get(context.TypeSystem.Compilation);
this.parameters = new Dictionary<ILVariable, (IType, string)>(); this.parameters = new Dictionary<ILVariable, (IType, string)>();
this.parameterMapping = new Dictionary<ILVariable, ILVariable>(); this.parameterMapping = new Dictionary<ILVariable, ILVariable>();
this.instructionsToRemove = new List<ILInstruction>(); this.instructionsToRemove = new List<ILInstruction>();
@ -116,7 +118,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
bool TryConvertExpressionTree(ILInstruction instruction, ILInstruction statement) bool TryConvertExpressionTree(ILInstruction instruction, ILInstruction statement)
{ {
if (MightBeExpressionTree(instruction, statement)) { if (MightBeExpressionTree(instruction, statement)) {
var lambda = ConvertLambda((CallInstruction)instruction); var (lambda, type) = ConvertLambda((CallInstruction)instruction);
if (lambda != null) { if (lambda != null) {
context.Step("Convert Expression Tree", instruction); context.Step("Convert Expression Tree", instruction);
instruction.ReplaceWith(lambda); instruction.ReplaceWith(lambda);
@ -135,14 +137,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// Converts a Expression.Lambda call into an ILFunction. /// Converts a Expression.Lambda call into an ILFunction.
/// If the conversion fails, null is returned. /// If the conversion fails, null is returned.
/// </summary> /// </summary>
ILInstruction ConvertLambda(CallInstruction instruction) (ILInstruction, IType) ConvertLambda(CallInstruction instruction)
{ {
if (instruction.Method.Name != "Lambda" || instruction.Arguments.Count != 2 || instruction.Method.ReturnType.FullName != "System.Linq.Expressions.Expression" || instruction.Method.ReturnType.TypeArguments.Count != 1) if (instruction.Method.Name != "Lambda" || instruction.Arguments.Count != 2 || instruction.Method.ReturnType.FullName != "System.Linq.Expressions.Expression" || instruction.Method.ReturnType.TypeArguments.Count != 1)
return null; return (null, SpecialType.UnknownType);
var parameterList = new List<IParameter>(); var parameterList = new List<IParameter>();
var parameterVariablesList = new List<ILVariable>(); var parameterVariablesList = new List<ILVariable>();
if (!ReadParameters(instruction.Arguments[1], parameterList, parameterVariablesList, new SimpleTypeResolveContext(context.Function.Method))) if (!ReadParameters(instruction.Arguments[1], parameterList, parameterVariablesList, new SimpleTypeResolveContext(context.Function.Method)))
return null; return (null, SpecialType.UnknownType);
bool isQuotedLambda = instruction.Parent is CallInstruction call && call.Method.FullName == "System.Linq.Expressions.Expression.Quote"; bool isQuotedLambda = instruction.Parent is CallInstruction call && call.Method.FullName == "System.Linq.Expressions.Expression.Quote";
var container = new BlockContainer(); var container = new BlockContainer();
var functionType = instruction.Method.ReturnType.TypeArguments[0]; var functionType = instruction.Method.ReturnType.TypeArguments[0];
@ -151,17 +153,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
function.ExpressionTreeType = instruction.Method.ReturnType; function.ExpressionTreeType = instruction.Method.ReturnType;
function.Variables.AddRange(parameterVariablesList); function.Variables.AddRange(parameterVariablesList);
lambdaStack.Push(function); lambdaStack.Push(function);
var bodyInstruction = ConvertInstruction(instruction.Arguments[0]); var (bodyInstruction, type) = ConvertInstruction(instruction.Arguments[0]);
lambdaStack.Pop(); lambdaStack.Pop();
if (bodyInstruction == null) if (bodyInstruction == null)
return null; 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) if (!isQuotedLambda && lambdaStack.Count > 0)
return new NewObj(functionType.GetConstructors().Single()) { return (new NewObj(functionType.GetConstructors().Single()) {
Arguments = { new LdNull(), function } Arguments = { new LdNull(), function }
}; }, functionType);
return function; 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)
@ -195,12 +197,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
} }
ILInstruction ConvertInstruction(ILInstruction instruction) (ILInstruction, IType) ConvertInstruction(ILInstruction instruction)
{ {
switch (instruction) { switch (instruction) {
case CallInstruction invocation: case CallInstruction invocation:
if (invocation.Method.DeclaringType.FullName != "System.Linq.Expressions.Expression") if (invocation.Method.DeclaringType.FullName != "System.Linq.Expressions.Expression")
return null; return (null, SpecialType.UnknownType);
switch (invocation.Method.Name) { switch (invocation.Method.Name) {
case "Add": case "Add":
@ -286,7 +288,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (invocation.Arguments.Count == 1) if (invocation.Arguments.Count == 1)
return ConvertInstruction(invocation.Arguments.Single()); return ConvertInstruction(invocation.Arguments.Single());
else else
return null; return (null, SpecialType.UnknownType);
case "RightShift": case "RightShift":
return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.ShiftRight); return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.ShiftRight);
case "Subtract": case "Subtract":
@ -298,104 +300,111 @@ namespace ICSharpCode.Decompiler.IL.Transforms
case "TypeIs": case "TypeIs":
return ConvertTypeIs(invocation); return ConvertTypeIs(invocation);
} }
return null; return (null, SpecialType.UnknownType);
default: default:
return ConvertValue(instruction, instruction.Parent); return ConvertValue(instruction, instruction.Parent);
} }
} }
ILInstruction ConvertArrayIndex(CallInstruction invocation) (ILInstruction, IType) ConvertArrayIndex(CallInstruction invocation)
{ {
if (invocation.Arguments.Count != 2) if (invocation.Arguments.Count != 2)
return null; return (null, SpecialType.UnknownType);
var array = ConvertInstruction(invocation.Arguments[0]); var (array, arrayType) = ConvertInstruction(invocation.Arguments[0]);
if (array == null) if (array == null)
return null; return (null, SpecialType.UnknownType);
var arrayType = InferType(array);
if (!(arrayType is ArrayType type)) if (!(arrayType is ArrayType type))
return null; return (null, SpecialType.UnknownType);
if (!MatchArgumentList(invocation.Arguments[1], out var arguments)) if (!MatchArgumentList(invocation.Arguments[1], out var arguments))
arguments = new[] { invocation.Arguments[1] }; arguments = new[] { invocation.Arguments[1] };
for (int i = 0; i < arguments.Count; i++) { for (int i = 0; i < arguments.Count; i++) {
var converted = ConvertInstruction(arguments[i]); var (converted, indexType) = ConvertInstruction(arguments[i]);
if (converted == null) if (converted == null)
return null; return (null, SpecialType.UnknownType);
arguments[i] = converted; arguments[i] = converted;
} }
return new LdObj(new LdElema(type.ElementType, array, arguments.ToArray()), type.ElementType); return (new LdObj(new LdElema(type.ElementType, array, arguments.ToArray()), type.ElementType), type.ElementType);
} }
ILInstruction ConvertArrayLength(CallInstruction invocation) (ILInstruction, IType) ConvertArrayLength(CallInstruction invocation)
{ {
if (invocation.Arguments.Count != 1) if (invocation.Arguments.Count != 1)
return null; return (null, SpecialType.UnknownType);
var converted = ConvertInstruction(invocation.Arguments[0]); var (converted, arrayType) = ConvertInstruction(invocation.Arguments[0]);
if (converted == null) if (converted == null)
return null; return (null, SpecialType.UnknownType);
return new LdLen(StackType.I4, converted); return (new LdLen(StackType.I4, converted), context.TypeSystem.Compilation.FindType(KnownTypeCode.Int32));
} }
ILInstruction ConvertBinaryNumericOperator(CallInstruction invocation, BinaryNumericOperator op, bool? isChecked = null) (ILInstruction, IType) ConvertBinaryNumericOperator(CallInstruction invocation, BinaryNumericOperator op, bool? isChecked = null)
{ {
if (invocation.Arguments.Count < 2) if (invocation.Arguments.Count < 2)
return null; return (null, SpecialType.UnknownType);
var left = ConvertInstruction(invocation.Arguments[0]); var (left, leftType) = ConvertInstruction(invocation.Arguments[0]);
if (left == null) if (left == null)
return null; return (null, SpecialType.UnknownType);
var right = ConvertInstruction(invocation.Arguments[1]); var (right, rightType) = ConvertInstruction(invocation.Arguments[1]);
if (right == null) if (right == null)
return null; return (null, SpecialType.UnknownType);
IMember method; IMember method;
switch (invocation.Arguments.Count) { switch (invocation.Arguments.Count) {
case 2: case 2:
return new BinaryNumericInstruction(op, left, right, isChecked == true, Sign.None); if (op == BinaryNumericOperator.ShiftLeft || op == BinaryNumericOperator.ShiftRight) {
if (!rightType.IsKnownType(KnownTypeCode.Int32))
return (null, SpecialType.UnknownType);
} else {
if (!rightType.Equals(leftType))
return (null, SpecialType.UnknownType);
}
return (new BinaryNumericInstruction(op, left, right, isChecked == true, leftType.GetSign()), leftType);
case 3: case 3:
if (!MatchGetMethodFromHandle(invocation.Arguments[2], out method)) if (!MatchGetMethodFromHandle(invocation.Arguments[2], out method))
return null; return (null, SpecialType.UnknownType);
return new Call((IMethod)method) { return (new Call((IMethod)method) {
Arguments = { left, right } Arguments = { left, right }
}; }, method.ReturnType);
case 4: case 4:
if (!invocation.Arguments[2].MatchLdcI4(out var isLifted)) if (!invocation.Arguments[2].MatchLdcI4(out var isLifted))
return null; return (null, SpecialType.UnknownType);
if (!MatchGetMethodFromHandle(invocation.Arguments[3], out method)) if (!MatchGetMethodFromHandle(invocation.Arguments[3], out method))
return null; return (null, SpecialType.UnknownType);
if (isLifted != 0) if (isLifted != 0)
method = CSharpOperators.LiftUserDefinedOperator((IMethod)method); method = CSharpOperators.LiftUserDefinedOperator((IMethod)method);
return new Call((IMethod)method) { return (new Call((IMethod)method) {
Arguments = { left, right } Arguments = { left, right }
}; }, method.ReturnType);
default: default:
return null; return (null, SpecialType.UnknownType);
} }
} }
ILInstruction ConvertBind(CallInstruction invocation, ILVariable targetVariable) (ILInstruction, IType) ConvertBind(CallInstruction invocation, ILVariable targetVariable)
{ {
if (invocation.Arguments.Count != 2) if (invocation.Arguments.Count != 2)
return null; return (null, SpecialType.UnknownType);
var value = ConvertInstruction(invocation.Arguments[1]); var (value, typeValue) = ConvertInstruction(invocation.Arguments[1]);
if (value == null) if (value == null)
return null; return (null, SpecialType.UnknownType);
if (MatchGetMethodFromHandle(invocation.Arguments[0], out var member)) { if (MatchGetMethodFromHandle(invocation.Arguments[0], out var member)) {
} else if (MatchGetFieldFromHandle(invocation.Arguments[0], out member)) { } else if (MatchGetFieldFromHandle(invocation.Arguments[0], out member)) {
} else { } else {
return null; return (null, SpecialType.UnknownType);
} }
switch (member) { switch (member) {
case IMethod method: case IMethod method:
return new Call(method) { Arguments = { new LdLoc(targetVariable), value } }; return (new Call(method) { Arguments = { new LdLoc(targetVariable), value } }, method.ReturnType);
case IField field: case IField field:
return new StObj(new LdFlda(new LdLoc(targetVariable), (IField)member), value, member.ReturnType); return (new StObj(new LdFlda(new LdLoc(targetVariable), (IField)member), value, member.ReturnType), field.ReturnType);
} }
return null; return (null, SpecialType.UnknownType);
} }
ILInstruction ConvertCall(CallInstruction invocation) (ILInstruction, IType) ConvertCall(CallInstruction invocation)
{ {
if (invocation.Arguments.Count < 2) if (invocation.Arguments.Count < 2)
return null; return (null, SpecialType.UnknownType);
IList<ILInstruction> arguments = null; IList<ILInstruction> arguments = null;
ILInstruction target = null;
if (MatchGetMethodFromHandle(invocation.Arguments[0], out var member)) { if (MatchGetMethodFromHandle(invocation.Arguments[0], out var member)) {
// static method // static method
if (invocation.Arguments.Count != 2 || !MatchArgumentList(invocation.Arguments[1], out arguments)) { if (invocation.Arguments.Count != 2 || !MatchArgumentList(invocation.Arguments[1], out arguments)) {
@ -405,23 +414,31 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (invocation.Arguments.Count != 3 || !MatchArgumentList(invocation.Arguments[2], out arguments)) { if (invocation.Arguments.Count != 3 || !MatchArgumentList(invocation.Arguments[2], out arguments)) {
arguments = new List<ILInstruction>(invocation.Arguments.Skip(2)); arguments = new List<ILInstruction>(invocation.Arguments.Skip(2));
} }
if (!invocation.Arguments[0].MatchLdNull()) if (!invocation.Arguments[0].MatchLdNull()) {
arguments.Insert(0, invocation.Arguments[0]); IType targetType;
(target, targetType) = ConvertInstruction(invocation.Arguments[0]);
if (target == null)
return (null, SpecialType.UnknownType);
if (targetType.IsReferenceType == false) {
target = new AddressOf(target);
}
}
} }
if (arguments == null) if (arguments == null)
return null; return (null, SpecialType.UnknownType);
arguments = arguments.Select(ConvertInstruction).ToArray(); // TODO : do we need the types here?
arguments = arguments.Select(i => ConvertInstruction(i).Item1).ToArray();
if (arguments.Any(p => p == null)) if (arguments.Any(p => p == null))
return null; return (null, SpecialType.UnknownType);
IMethod method = (IMethod)member; IMethod method = (IMethod)member;
if (method.FullName == "System.Reflection.MethodInfo.CreateDelegate" && method.Parameters.Count == 2) { if (method.FullName == "System.Reflection.MethodInfo.CreateDelegate" && method.Parameters.Count == 2) {
if (!MatchGetMethodFromHandle(arguments[0], out var targetMethod)) if (!MatchGetMethodFromHandle(target, out var targetMethod))
return null; return (null, SpecialType.UnknownType);
if (!MatchGetTypeFromHandle(arguments[1], out var delegateType)) if (!MatchGetTypeFromHandle(arguments[0], out var delegateType))
return null; return (null, SpecialType.UnknownType);
return new NewObj(delegateType.GetConstructors().Single()) { return (new NewObj(delegateType.GetConstructors().Single()) {
Arguments = { arguments[2], new LdFtn((IMethod)targetMethod) } Arguments = { arguments[1], new LdFtn((IMethod)targetMethod) }
}; }, delegateType);
} }
CallInstruction call; CallInstruction call;
if (method.IsAbstract || method.IsVirtual || method.IsOverridable) { if (method.IsAbstract || method.IsVirtual || method.IsOverridable) {
@ -429,176 +446,191 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} else { } else {
call = new Call(method); call = new Call(method);
} }
if (target != null)
call.Arguments.Add(target);
call.Arguments.AddRange(arguments); call.Arguments.AddRange(arguments);
return call; return (call, method.ReturnType);
} }
ILInstruction ConvertCast(CallInstruction invocation, bool isChecked) (ILInstruction, IType) ConvertCast(CallInstruction invocation, bool isChecked)
{ {
if (invocation.Arguments.Count < 2) if (invocation.Arguments.Count < 2)
return null; return (null, SpecialType.UnknownType);
if (!MatchGetTypeFromHandle(invocation.Arguments[1], out var targetType)) if (!MatchGetTypeFromHandle(invocation.Arguments[1], out var targetType))
return null; return (null, SpecialType.UnknownType);
var expr = ConvertInstruction(invocation.Arguments[0]); var (expr, exprType) = ConvertInstruction(invocation.Arguments[0]);
if (expr == null) if (expr == null)
return null; return (null, SpecialType.UnknownType);
return new ExpressionTreeCast(targetType, expr, isChecked); return (new ExpressionTreeCast(targetType, expr, isChecked), targetType);
} }
ILInstruction ConvertCoalesce(CallInstruction invocation) (ILInstruction, IType) ConvertCoalesce(CallInstruction invocation)
{ {
if (invocation.Arguments.Count != 2) if (invocation.Arguments.Count != 2)
return null; return (null, SpecialType.UnknownType);
var trueInst = ConvertInstruction(invocation.Arguments[0]); var (trueInst, trueInstType) = ConvertInstruction(invocation.Arguments[0]);
if (trueInst == null) if (trueInst == null)
return null; return (null, SpecialType.UnknownType);
var fallbackInst = ConvertInstruction(invocation.Arguments[1]); var (fallbackInst, fallbackInstType) = ConvertInstruction(invocation.Arguments[1]);
if (fallbackInst == null) if (fallbackInst == null)
return null; return (null, SpecialType.UnknownType);
// TODO : nullable types? var kind = NullCoalescingKind.Ref;
return new NullCoalescingInstruction(NullCoalescingKind.Ref, trueInst, fallbackInst); var trueInstTypeNonNullable = NullableType.GetUnderlyingType(trueInstType);
IType targetType;
if (NullableType.IsNullable(trueInstType) && conversions.ImplicitConversion(fallbackInstType, trueInstTypeNonNullable).IsValid) {
targetType = trueInstTypeNonNullable;
kind = NullableType.IsNullable(fallbackInstType) ? NullCoalescingKind.Nullable : NullCoalescingKind.NullableWithValueFallback;
} else if (conversions.ImplicitConversion(fallbackInstType, trueInstType).IsValid) {
targetType = trueInstType;
} else {
targetType = fallbackInstType;
}
return (new NullCoalescingInstruction(kind, trueInst, fallbackInst), targetType);
} }
ILInstruction ConvertComparison(CallInstruction invocation, ComparisonKind kind) (ILInstruction, IType) ConvertComparison(CallInstruction invocation, ComparisonKind kind)
{ {
if (invocation.Arguments.Count < 2) if (invocation.Arguments.Count < 2)
return null; return (null, SpecialType.UnknownType);
var left = ConvertInstruction(invocation.Arguments[0]); var (left, leftType) = ConvertInstruction(invocation.Arguments[0]);
if (left == null) if (left == null)
return null; return (null, SpecialType.UnknownType);
var right = ConvertInstruction(invocation.Arguments[1]); var (right, rightType) = ConvertInstruction(invocation.Arguments[1]);
if (right == null) if (right == null)
return null; return (null, SpecialType.UnknownType);
if (invocation.Arguments.Count == 4 && invocation.Arguments[2].MatchLdcI4(out var isLifted) && MatchGetMethodFromHandle(invocation.Arguments[3], out var method)) { if (invocation.Arguments.Count == 4 && invocation.Arguments[2].MatchLdcI4(out var isLifted) && MatchGetMethodFromHandle(invocation.Arguments[3], out var method)) {
if (isLifted != 0) { if (isLifted != 0) {
method = CSharpOperators.LiftUserDefinedOperator((IMethod)method); method = CSharpOperators.LiftUserDefinedOperator((IMethod)method);
} }
return new Call((IMethod)method) { Arguments = { left, right } }; return (new Call((IMethod)method) { Arguments = { left, right } }, method.ReturnType);
} }
// TODO: Sign?? var resultType = context.TypeSystem.Compilation.FindType(KnownTypeCode.Boolean);
return new Comp(kind, Sign.None, left, right); return (new Comp(kind, NullableType.IsNullable(leftType) ? ComparisonLiftingKind.CSharp : ComparisonLiftingKind.None, leftType.GetStackType(), leftType.GetSign(), left, right), resultType);
} }
ILInstruction ConvertCondition(CallInstruction invocation) (ILInstruction, IType) ConvertCondition(CallInstruction invocation)
{ {
if (invocation.Arguments.Count != 3) if (invocation.Arguments.Count != 3)
return null; return (null, SpecialType.UnknownType);
var condition = ConvertInstruction(invocation.Arguments[0]); var (condition, conditionType) = ConvertInstruction(invocation.Arguments[0]);
if (condition == null) if (condition == null || !conditionType.IsKnownType(KnownTypeCode.Boolean))
return null; return (null, SpecialType.UnknownType);
var trueInst = ConvertInstruction(invocation.Arguments[1]); var (trueInst, trueInstType) = ConvertInstruction(invocation.Arguments[1]);
if (trueInst == null) if (trueInst == null)
return null; return (null, SpecialType.UnknownType);
var falseInst = ConvertInstruction(invocation.Arguments[2]); var (falseInst, falseInstType) = ConvertInstruction(invocation.Arguments[2]);
if (falseInst == null) if (falseInst == null)
return null; return (null, SpecialType.UnknownType);
return new IfInstruction(condition, trueInst, falseInst); if (!trueInstType.Equals(falseInstType))
return (null, SpecialType.UnknownType);
return (new IfInstruction(condition, trueInst, falseInst), trueInstType);
} }
ILInstruction ConvertConstant(CallInstruction invocation) (ILInstruction, IType) ConvertConstant(CallInstruction invocation)
{ {
if (!MatchConstantCall(invocation, out var value, out var type)) if (!MatchConstantCall(invocation, out var value, out var type))
return null; 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) if (boxType.Kind == TypeKind.Enum)
return new ExpressionTreeCast(boxType, ConvertValue(arg, invocation), false); return (new ExpressionTreeCast(boxType, ConvertValue(arg, invocation).Item1, false), boxType);
value = arg; value = ConvertValue(arg, invocation).Item1;
return (value, type);
} }
return ConvertValue(value, invocation); return ConvertValue(value, invocation);
} }
ILInstruction ConvertElementInit(CallInstruction invocation) (ILInstruction, IType) ConvertElementInit(CallInstruction invocation)
{ {
if (invocation.Arguments.Count != 2) if (invocation.Arguments.Count != 2)
return null; return (null, SpecialType.UnknownType);
if (!MatchGetMethodFromHandle(invocation.Arguments[0], out var member)) if (!MatchGetMethodFromHandle(invocation.Arguments[0], out var member))
return null; return (null, SpecialType.UnknownType);
if (!MatchArgumentList(invocation.Arguments[1], out var arguments)) if (!MatchArgumentList(invocation.Arguments[1], out var arguments))
return null; return (null, SpecialType.UnknownType);
CallInstruction call = new Call((IMethod)member); CallInstruction call = new Call((IMethod)member);
for (int i = 0; i < arguments.Count; i++) { for (int i = 0; i < arguments.Count; i++) {
ILInstruction arg = ConvertInstruction(arguments[i]); ILInstruction arg = ConvertInstruction(arguments[i]).Item1;
if (arg == null) if (arg == null)
return null; return (null, SpecialType.UnknownType);
arguments[i] = arg; arguments[i] = arg;
} }
call.Arguments.AddRange(arguments); call.Arguments.AddRange(arguments);
return call; return (call, member.ReturnType);
} }
ILInstruction ConvertField(CallInstruction invocation) (ILInstruction, IType) ConvertField(CallInstruction invocation)
{ {
if (invocation.Arguments.Count != 2) if (invocation.Arguments.Count != 2)
return null; return (null, SpecialType.UnknownType);
ILInstruction target = null; ILInstruction target = null;
if (!invocation.Arguments[0].MatchLdNull()) { if (!invocation.Arguments[0].MatchLdNull()) {
target = ConvertInstruction(invocation.Arguments[0]); target = ConvertInstruction(invocation.Arguments[0]).Item1;
if (target == null) if (target == null)
return null; return (null, SpecialType.UnknownType);
} }
if (!MatchGetFieldFromHandle(invocation.Arguments[1], out var member)) if (!MatchGetFieldFromHandle(invocation.Arguments[1], out var member))
return null; return (null, SpecialType.UnknownType);
if (target == null) { if (target == null) {
return new LdObj(new LdsFlda((IField)member), member.ReturnType); return (new LdObj(new LdsFlda((IField)member), member.ReturnType), member.ReturnType);
} else { } else {
return new LdObj(new LdFlda(target, (IField)member), member.ReturnType); return (new LdObj(new LdFlda(target, (IField)member), member.ReturnType), member.ReturnType);
} }
} }
ILInstruction ConvertInvoke(CallInstruction invocation) (ILInstruction, IType) ConvertInvoke(CallInstruction invocation)
{ {
if (invocation.Arguments.Count != 2) if (invocation.Arguments.Count != 2)
return null; return (null, SpecialType.UnknownType);
var target = ConvertInstruction(invocation.Arguments[0]); var (target, targetType) = ConvertInstruction(invocation.Arguments[0]);
if (target == null) if (target == null)
return null; return (null, SpecialType.UnknownType);
var invokeMethod = InferType(target).GetDelegateInvokeMethod(); var invokeMethod = targetType.GetDelegateInvokeMethod();
if (invokeMethod == null) if (invokeMethod == null)
return null; return (null, SpecialType.UnknownType);
if (!MatchArgumentList(invocation.Arguments[1], out var arguments)) if (!MatchArgumentList(invocation.Arguments[1], out var arguments))
return null; return (null, SpecialType.UnknownType);
for (int i = 0; i < arguments.Count; i++) { for (int i = 0; i < arguments.Count; i++) {
var arg = ConvertInstruction(arguments[i]); var arg = ConvertInstruction(arguments[i]).Item1;
if (arg == null) if (arg == null)
return null; return (null, SpecialType.UnknownType);
arguments[i] = arg; arguments[i] = arg;
} }
var call = new Call(invokeMethod); var call = new Call(invokeMethod);
call.Arguments.Add(target); call.Arguments.Add(target);
call.Arguments.AddRange(arguments); call.Arguments.AddRange(arguments);
return call; return (call, invokeMethod.ReturnType);
} }
ILInstruction ConvertListInit(CallInstruction invocation) (ILInstruction, IType) ConvertListInit(CallInstruction invocation)
{ {
if (invocation.Arguments.Count < 2) if (invocation.Arguments.Count < 2)
return null; return (null, SpecialType.UnknownType);
var newObj = ConvertInstruction(invocation.Arguments[0]) as NewObj; var newObj = ConvertInstruction(invocation.Arguments[0]).Item1 as NewObj;
if (newObj == null) if (newObj == null)
return null; return (null, SpecialType.UnknownType);
IList<ILInstruction> arguments = null; IList<ILInstruction> arguments = null;
ILFunction function = lambdaStack.Peek(); ILFunction function = lambdaStack.Peek();
if (!MatchGetMethodFromHandle(invocation.Arguments[1], out var member)) { if (!MatchGetMethodFromHandle(invocation.Arguments[1], out var member)) {
if (!MatchArgumentList(invocation.Arguments[1], out arguments)) if (!MatchArgumentList(invocation.Arguments[1], out arguments))
return null; return (null, SpecialType.UnknownType);
} else { } else {
if (invocation.Arguments.Count != 3 || !MatchArgumentList(invocation.Arguments[2], out arguments)) if (invocation.Arguments.Count != 3 || !MatchArgumentList(invocation.Arguments[2], out arguments))
return null; return (null, SpecialType.UnknownType);
} }
if (arguments == null || arguments.Count == 0) if (arguments == null || arguments.Count == 0)
return null; return (null, SpecialType.UnknownType);
var initializer = function.RegisterVariable(VariableKind.InitializerTarget, newObj.Method.DeclaringType); var initializer = function.RegisterVariable(VariableKind.InitializerTarget, newObj.Method.DeclaringType);
for (int i = 0; i < arguments.Count; i++) { for (int i = 0; i < arguments.Count; i++) {
ILInstruction arg; ILInstruction arg;
if (arguments[i] is CallInstruction elementInit && elementInit.Method.FullName == "System.Linq.Expressions.Expression.ElementInit") { if (arguments[i] is CallInstruction elementInit && elementInit.Method.FullName == "System.Linq.Expressions.Expression.ElementInit") {
arg = ConvertElementInit(elementInit); arg = ConvertElementInit(elementInit).Item1;
if (arg == null) if (arg == null)
return null; return (null, SpecialType.UnknownType);
((CallInstruction)arg).Arguments.Insert(0, new LdLoc(initializer)); ((CallInstruction)arg).Arguments.Insert(0, new LdLoc(initializer));
} else { } else {
arg = ConvertInstruction(arguments[i]); arg = ConvertInstruction(arguments[i]).Item1;
if (arg == null) if (arg == null)
return null; return (null, SpecialType.UnknownType);
} }
arguments[i] = arg; arguments[i] = arg;
} }
@ -606,65 +638,66 @@ namespace ICSharpCode.Decompiler.IL.Transforms
initializerBlock.FinalInstruction = new LdLoc(initializer); initializerBlock.FinalInstruction = new LdLoc(initializer);
initializerBlock.Instructions.Add(new StLoc(initializer, newObj)); initializerBlock.Instructions.Add(new StLoc(initializer, newObj));
initializerBlock.Instructions.AddRange(arguments); initializerBlock.Instructions.AddRange(arguments);
return initializerBlock; return (initializerBlock, initializer.Type);
} }
ILInstruction ConvertLogicOperator(CallInstruction invocation, bool and) (ILInstruction, IType) ConvertLogicOperator(CallInstruction invocation, bool and)
{ {
if (invocation.Arguments.Count < 2) if (invocation.Arguments.Count < 2)
return null; return (null, SpecialType.UnknownType);
var left = ConvertInstruction(invocation.Arguments[0]); var (left, leftType) = ConvertInstruction(invocation.Arguments[0]);
if (left == null) if (left == null)
return null; return (null, SpecialType.UnknownType);
var right = ConvertInstruction(invocation.Arguments[1]); var (right, rightType) = ConvertInstruction(invocation.Arguments[1]);
if (right == null) if (right == null)
return null; return (null, SpecialType.UnknownType);
IMember method; IMember method;
switch (invocation.Arguments.Count) { switch (invocation.Arguments.Count) {
case 2: case 2:
return and ? IfInstruction.LogicAnd(left, right) : IfInstruction.LogicOr(left, right); var resultType = context.TypeSystem.Compilation.FindType(KnownTypeCode.Boolean);
return (and ? IfInstruction.LogicAnd(left, right) : IfInstruction.LogicOr(left, right), resultType);
case 3: case 3:
if (!MatchGetMethodFromHandle(invocation.Arguments[2], out method)) if (!MatchGetMethodFromHandle(invocation.Arguments[2], out method))
return null; return (null, SpecialType.UnknownType);
return new Call((IMethod)method) { return (new Call((IMethod)method) {
Arguments = { left, right } Arguments = { left, right }
}; }, method.ReturnType);
case 4: case 4:
if (!invocation.Arguments[2].MatchLdcI4(out var isLifted)) if (!invocation.Arguments[2].MatchLdcI4(out var isLifted))
return null; return (null, SpecialType.UnknownType);
if (!MatchGetMethodFromHandle(invocation.Arguments[3], out method)) if (!MatchGetMethodFromHandle(invocation.Arguments[3], out method))
return null; return (null, SpecialType.UnknownType);
if (isLifted != 0) if (isLifted != 0)
method = CSharpOperators.LiftUserDefinedOperator((IMethod)method); method = CSharpOperators.LiftUserDefinedOperator((IMethod)method);
return new Call((IMethod)method) { return (new Call((IMethod)method) {
Arguments = { left, right } Arguments = { left, right }
}; }, method.ReturnType);
default: default:
return null; return (null, SpecialType.UnknownType);
} }
} }
ILInstruction ConvertMemberInit(CallInstruction invocation) (ILInstruction, IType) ConvertMemberInit(CallInstruction invocation)
{ {
if (invocation.Arguments.Count != 2) if (invocation.Arguments.Count != 2)
return null; return (null, SpecialType.UnknownType);
var newObj = ConvertInstruction(invocation.Arguments[0]) as NewObj; var newObj = ConvertInstruction(invocation.Arguments[0]).Item1 as NewObj;
if (newObj == null) if (newObj == null)
return null; return (null, SpecialType.UnknownType);
if (!MatchArgumentList(invocation.Arguments[1], out var arguments)) if (!MatchArgumentList(invocation.Arguments[1], out var arguments))
return null; return (null, SpecialType.UnknownType);
if (arguments == null || arguments.Count == 0) if (arguments == null || arguments.Count == 0)
return null; return (null, SpecialType.UnknownType);
var function = lambdaStack.Peek(); var function = lambdaStack.Peek();
var initializer = function.RegisterVariable(VariableKind.InitializerTarget, newObj.Method.DeclaringType); var initializer = function.RegisterVariable(VariableKind.InitializerTarget, newObj.Method.DeclaringType);
for (int i = 0; i < arguments.Count; i++) { for (int i = 0; i < arguments.Count; i++) {
ILInstruction arg; ILInstruction arg;
if (arguments[i] is CallInstruction bind && bind.Method.FullName == "System.Linq.Expressions.Expression.Bind") { if (arguments[i] is CallInstruction bind && bind.Method.FullName == "System.Linq.Expressions.Expression.Bind") {
arg = ConvertBind(bind, initializer); arg = ConvertBind(bind, initializer).Item1;
if (arg == null) if (arg == null)
return null; return (null, SpecialType.UnknownType);
} else { } else {
return null; return (null, SpecialType.UnknownType);
} }
arguments[i] = arg; arguments[i] = arg;
} }
@ -672,39 +705,39 @@ namespace ICSharpCode.Decompiler.IL.Transforms
initializerBlock.FinalInstruction = new LdLoc(initializer); initializerBlock.FinalInstruction = new LdLoc(initializer);
initializerBlock.Instructions.Add(new StLoc(initializer, newObj)); initializerBlock.Instructions.Add(new StLoc(initializer, newObj));
initializerBlock.Instructions.AddRange(arguments); initializerBlock.Instructions.AddRange(arguments);
return initializerBlock; return (initializerBlock, initializer.Type);
} }
ILInstruction ConvertNewArrayBounds(CallInstruction invocation) (ILInstruction, IType) ConvertNewArrayBounds(CallInstruction invocation)
{ {
if (invocation.Arguments.Count != 2) if (invocation.Arguments.Count != 2)
return null; return (null, SpecialType.UnknownType);
if (!MatchGetTypeFromHandle(invocation.Arguments[0], out var type)) if (!MatchGetTypeFromHandle(invocation.Arguments[0], out var type))
return null; return (null, SpecialType.UnknownType);
if (!MatchArgumentList(invocation.Arguments[1], out var arguments)) if (!MatchArgumentList(invocation.Arguments[1], out var arguments))
return null; return (null, SpecialType.UnknownType);
if (arguments.Count == 0) if (arguments.Count == 0)
return null; return (null, SpecialType.UnknownType);
var indices = new ILInstruction[arguments.Count]; var indices = new ILInstruction[arguments.Count];
for (int i = 0; i < arguments.Count; i++) { for (int i = 0; i < arguments.Count; i++) {
var index = ConvertInstruction(arguments[i]); var index = ConvertInstruction(arguments[i]).Item1;
if (index == null) if (index == null)
return null; return (null, SpecialType.UnknownType);
indices[i] = index; indices[i] = index;
} }
return new NewArr(type, indices); return (new NewArr(type, indices), type);
} }
ILInstruction ConvertNewArrayInit(CallInstruction invocation) (ILInstruction, IType) ConvertNewArrayInit(CallInstruction invocation)
{ {
if (invocation.Arguments.Count != 2) if (invocation.Arguments.Count != 2)
return null; return (null, SpecialType.UnknownType);
if (!MatchGetTypeFromHandle(invocation.Arguments[0], out var type)) if (!MatchGetTypeFromHandle(invocation.Arguments[0], out var type))
return null; return (null, SpecialType.UnknownType);
if (!MatchArgumentList(invocation.Arguments[1], out var arguments)) if (!MatchArgumentList(invocation.Arguments[1], out var arguments))
return null; return (null, SpecialType.UnknownType);
if (arguments.Count == 0) if (arguments.Count == 0)
return null; return (null, SpecialType.UnknownType);
var block = (Block)invocation.Arguments[1]; var block = (Block)invocation.Arguments[1];
var function = lambdaStack.Peek(); var function = lambdaStack.Peek();
var variable = function.RegisterVariable(VariableKind.InitializerTarget, new ArrayType(context.BlockContext.TypeSystem.Compilation, type)); var variable = function.RegisterVariable(VariableKind.InitializerTarget, new ArrayType(context.BlockContext.TypeSystem.Compilation, type));
@ -712,16 +745,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms
int i = 0; int i = 0;
initializer.Instructions.Add(new StLoc(variable, new NewArr(type, new LdcI4(arguments.Count)))); initializer.Instructions.Add(new StLoc(variable, new NewArr(type, new LdcI4(arguments.Count))));
foreach (var item in arguments) { foreach (var item in arguments) {
var value = ConvertInstruction(item); var value = ConvertInstruction(item).Item1;
if (value == null) if (value == null)
return null; return (null, SpecialType.UnknownType);
initializer.Instructions.Add(new StObj(new LdElema(type, new LdLoc(variable), new LdcI4(i)), value, type)); initializer.Instructions.Add(new StObj(new LdElema(type, new LdLoc(variable), new LdcI4(i)), value, type));
} }
initializer.FinalInstruction = new LdLoc(variable); initializer.FinalInstruction = new LdLoc(variable);
return initializer; return (initializer, variable.Type);
} }
ILInstruction ConvertNewObject(CallInstruction invocation) (ILInstruction, IType) ConvertNewObject(CallInstruction invocation)
{ {
IMember member; IMember member;
IList<ILInstruction> arguments; IList<ILInstruction> arguments;
@ -731,80 +764,80 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (MatchGetTypeFromHandle(invocation.Arguments[0], out var type)) { if (MatchGetTypeFromHandle(invocation.Arguments[0], out var type)) {
var ctors = type.GetConstructors().ToArray(); var ctors = type.GetConstructors().ToArray();
if (ctors.Length != 1 || ctors[0].Parameters.Count > 0) if (ctors.Length != 1 || ctors[0].Parameters.Count > 0)
return null; return (null, SpecialType.UnknownType);
return new NewObj(ctors[0]); return (new NewObj(ctors[0]), type);
} }
if (MatchGetConstructorFromHandle(invocation.Arguments[0], out member)) { if (MatchGetConstructorFromHandle(invocation.Arguments[0], out member)) {
return new NewObj((IMethod)member); return (new NewObj((IMethod)member), member.DeclaringType);
} }
return null; return (null, SpecialType.UnknownType);
case 2: case 2:
if (!MatchGetConstructorFromHandle(invocation.Arguments[0], out member)) if (!MatchGetConstructorFromHandle(invocation.Arguments[0], out member))
return null; return (null, SpecialType.UnknownType);
if (!MatchArgumentList(invocation.Arguments[1], out arguments)) if (!MatchArgumentList(invocation.Arguments[1], out arguments))
return null; return (null, SpecialType.UnknownType);
var args = arguments.SelectArray(ConvertInstruction); var args = arguments.SelectArray(arg => ConvertInstruction(arg).Item1);
if (args.Any(a => a == null)) if (args.Any(a => a == null))
return null; return (null, SpecialType.UnknownType);
newObj = new NewObj((IMethod)member); newObj = new NewObj((IMethod)member);
newObj.Arguments.AddRange(args); newObj.Arguments.AddRange(args);
return newObj; return (newObj, member.DeclaringType);
case 3: case 3:
if (!MatchGetConstructorFromHandle(invocation.Arguments[0], out member)) if (!MatchGetConstructorFromHandle(invocation.Arguments[0], out member))
return null; return (null, SpecialType.UnknownType);
if (!MatchArgumentList(invocation.Arguments[1], out arguments)) if (!MatchArgumentList(invocation.Arguments[1], out arguments))
return null; return (null, SpecialType.UnknownType);
var args2 = arguments.SelectArray(ConvertInstruction); var args2 = arguments.SelectArray(arg => ConvertInstruction(arg).Item1);
if (args2.Any(a => a == null)) if (args2.Any(a => a == null))
return null; return (null, SpecialType.UnknownType);
newObj = new NewObj((IMethod)member); newObj = new NewObj((IMethod)member);
newObj.Arguments.AddRange(args2); newObj.Arguments.AddRange(args2);
return newObj; return (newObj, member.DeclaringType);
} }
return null; return (null, SpecialType.UnknownType);
} }
ILInstruction ConvertNotOperator(CallInstruction invocation) (ILInstruction, IType) ConvertNotOperator(CallInstruction invocation)
{ {
if (invocation.Arguments.Count < 1) if (invocation.Arguments.Count < 1)
return null; return (null, SpecialType.UnknownType);
var argument = ConvertInstruction(invocation.Arguments[0]); var (argument, argumentType) = ConvertInstruction(invocation.Arguments[0]);
if (argument == null) if (argument == null)
return null; return (null, SpecialType.UnknownType);
switch (invocation.Arguments.Count) { switch (invocation.Arguments.Count) {
case 1: case 1:
return InferType(argument).IsKnownType(KnownTypeCode.Boolean) ? Comp.LogicNot(argument) : (ILInstruction)new BitNot(argument); return (argumentType.IsKnownType(KnownTypeCode.Boolean) ? Comp.LogicNot(argument) : (ILInstruction)new BitNot(argument), argumentType);
case 2: case 2:
if (!MatchGetMethodFromHandle(invocation.Arguments[1], out var method)) if (!MatchGetMethodFromHandle(invocation.Arguments[1], out var method))
return null; return (null, SpecialType.UnknownType);
return new Call((IMethod)method) { return (new Call((IMethod)method) {
Arguments = { argument } Arguments = { argument }
}; }, method.ReturnType);
default: default:
return null; return (null, SpecialType.UnknownType);
} }
} }
ILInstruction ConvertProperty(CallInstruction invocation) (ILInstruction, IType) ConvertProperty(CallInstruction invocation)
{ {
if (invocation.Arguments.Count < 2) if (invocation.Arguments.Count < 2)
return null; return (null, SpecialType.UnknownType);
ILInstruction target = null; ILInstruction target = null;
if (!invocation.Arguments[0].MatchLdNull()) { if (!invocation.Arguments[0].MatchLdNull()) {
target = ConvertInstruction(invocation.Arguments[0]); target = ConvertInstruction(invocation.Arguments[0]).Item1;
if (target == null) if (target == null)
return null; return (null, SpecialType.UnknownType);
} }
if (!MatchGetMethodFromHandle(invocation.Arguments[1], out var member)) if (!MatchGetMethodFromHandle(invocation.Arguments[1], out var member))
return null; return (null, SpecialType.UnknownType);
IList<ILInstruction> arguments; IList<ILInstruction> arguments;
if (invocation.Arguments.Count != 3 || !MatchArgumentList(invocation.Arguments[2], out arguments)) { if (invocation.Arguments.Count != 3 || !MatchArgumentList(invocation.Arguments[2], out arguments)) {
arguments = new List<ILInstruction>(); arguments = new List<ILInstruction>();
} else { } else {
for (int i = 0; i < arguments.Count; i++) { for (int i = 0; i < arguments.Count; i++) {
arguments[i] = ConvertInstruction(arguments[i]); arguments[i] = ConvertInstruction(arguments[i]).Item1;
if (arguments[i] == null) if (arguments[i] == null)
return null; return (null, SpecialType.UnknownType);
} }
} }
if (target != null) { if (target != null) {
@ -817,74 +850,92 @@ namespace ICSharpCode.Decompiler.IL.Transforms
call = new Call((IMethod)member); call = new Call((IMethod)member);
} }
call.Arguments.AddRange(arguments); call.Arguments.AddRange(arguments);
return call; return (call, member.ReturnType);
} }
ILInstruction ConvertTypeAs(CallInstruction invocation) (ILInstruction, IType) ConvertTypeAs(CallInstruction invocation)
{ {
if (invocation.Arguments.Count != 2) if (invocation.Arguments.Count != 2)
return null; return (null, SpecialType.UnknownType);
var converted = ConvertInstruction(invocation.Arguments[0]); var converted = ConvertInstruction(invocation.Arguments[0]).Item1;
if (!MatchGetTypeFromHandle(invocation.Arguments[1], out var type)) if (!MatchGetTypeFromHandle(invocation.Arguments[1], out var type))
return null; return (null, SpecialType.UnknownType);
if (converted != null) if (converted != null)
return new IsInst(converted, type); return (new IsInst(converted, type), type);
return null; return (null, SpecialType.UnknownType);
} }
ILInstruction ConvertTypeIs(CallInstruction invocation) (ILInstruction, IType) ConvertTypeIs(CallInstruction invocation)
{ {
if (invocation.Arguments.Count != 2) if (invocation.Arguments.Count != 2)
return null; return (null, SpecialType.UnknownType);
var converted = ConvertInstruction(invocation.Arguments[0]); var converted = ConvertInstruction(invocation.Arguments[0]).Item1;
if (!MatchGetTypeFromHandle(invocation.Arguments[1], out var type)) if (!MatchGetTypeFromHandle(invocation.Arguments[1], out var type))
return null; return (null, SpecialType.UnknownType);
var resultType = context.TypeSystem.Compilation.FindType(KnownTypeCode.Boolean);
if (converted != null) if (converted != null)
return new Comp(ComparisonKind.Inequality, Sign.None, new IsInst(converted, type), new LdNull()); return (new Comp(ComparisonKind.Inequality, Sign.None, new IsInst(converted, type), new LdNull()), resultType);
return null; return (null, SpecialType.UnknownType);
} }
ILInstruction ConvertUnaryNumericOperator(CallInstruction invocation, BinaryNumericOperator op, bool? isChecked = null) (ILInstruction, IType) ConvertUnaryNumericOperator(CallInstruction invocation, BinaryNumericOperator op, bool? isChecked = null)
{ {
if (invocation.Arguments.Count < 1) if (invocation.Arguments.Count < 1)
return null; return (null, SpecialType.UnknownType);
var argument = ConvertInstruction(invocation.Arguments[0]); var (argument, argumentType) = ConvertInstruction(invocation.Arguments[0]);
if (argument == null) if (argument == null)
return null; return (null, SpecialType.UnknownType);
switch (invocation.Arguments.Count) { switch (invocation.Arguments.Count) {
case 1: case 1:
return new BinaryNumericInstruction(op, new LdcI4(0), argument, isChecked == true, Sign.None); ILInstruction left;
switch (argument.ResultType) {
case StackType.I4:
left = new LdcI4(0);
break;
case StackType.I8:
left = new LdcI8(0);
break;
case StackType.I:
left = new Conv(new LdcI4(0), PrimitiveType.I, false, Sign.None);
break;
case StackType.F:
left = new LdcF(0);
break;
default:
return (null, SpecialType.UnknownType);
}
return (new BinaryNumericInstruction(op, left, argument, isChecked == true, argumentType.GetSign()), argumentType);
case 2: case 2:
if (!MatchGetMethodFromHandle(invocation.Arguments[1], out var method)) if (!MatchGetMethodFromHandle(invocation.Arguments[1], out var method))
return null; return (null, SpecialType.UnknownType);
return new Call((IMethod)method) { return (new Call((IMethod)method) {
Arguments = { argument } Arguments = { argument }
}; }, method.ReturnType);
} }
return null; return (null, SpecialType.UnknownType);
} }
ILInstruction ConvertValue(ILInstruction value, ILInstruction context) (ILInstruction, IType) 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; return (null, SpecialType.UnknownType);
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); return (new LdLoca(v), new ByReferenceType(v.Type));
return new LdLoc(v); return (new LdLoc(v), v.Type);
} else { } else {
if (ldloc.Variable.Kind != VariableKind.StackSlot) if (ldloc.Variable.Kind != VariableKind.StackSlot)
return new LdLoc(ldloc.Variable); return (new LdLoc(ldloc.Variable), ldloc.Variable.Type);
return null; return (null, SpecialType.UnknownType);
} }
default: default:
if (SemanticHelper.IsPure(value.Flags)) if (SemanticHelper.IsPure(value.Flags))
return value.Clone(); return (value.Clone(), value.InferType());
return value; return (value, value.InferType());
} }
} }
@ -901,7 +952,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
value = call.Arguments[0]; value = call.Arguments[0];
if (call.Arguments.Count == 2) if (call.Arguments.Count == 2)
return MatchGetTypeFromHandle(call.Arguments[1], out type); return MatchGetTypeFromHandle(call.Arguments[1], out type);
type = InferType(value); type = value.InferType();
return true; return true;
} }
return false; return false;
@ -1006,14 +1057,5 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
return true; return true;
} }
IType InferType(ILInstruction inst)
{
if (inst is Block b && b.Kind == BlockKind.ArrayInitializer)
return b.FinalInstruction.InferType();
if (inst is ExpressionTreeCast cast)
return cast.Type;
return inst.InferType();
}
} }
} }

Loading…
Cancel
Save