diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs index 5996351a4..e7c5397d3 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs @@ -218,7 +218,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public void MembersBuiltin() { ExpressionTrees.ToCode(ExpressionTrees.X(), () => 1.23m.ToString()); - ExpressionTrees.ToCode(ExpressionTrees.X(), () => AttributeTargets.All.HasFlag((Enum)AttributeTargets.Assembly)); + ExpressionTrees.ToCode(ExpressionTrees.X(), () => ((Enum)(object)AttributeTargets.All).HasFlag((Enum)AttributeTargets.Assembly)); ExpressionTrees.ToCode(ExpressionTrees.X(), () => "abc".Length == 3); ExpressionTrees.ToCode(ExpressionTrees.X(), () => 'a'.CompareTo('b') < 0); } diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index 6ba846dc9..3829ef634 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -1129,16 +1129,19 @@ namespace ICSharpCode.Decompiler.IL value.ILStackWasEmpty = currentStack.IsEmpty; return new StObj(target, value, type); } - + + IType constrainedPrefix; + private ILInstruction DecodeConstrainedCall() { - var typeRef = ReadAndDecodeTypeReference(); + constrainedPrefix = ReadAndDecodeTypeReference(); var inst = DecodeInstruction(); var call = UnpackPush(inst) as CallInstruction; if (call != null) - call.ConstrainedTo = typeRef; + Debug.Assert(call.ConstrainedTo == constrainedPrefix); else Warn("Ignored invalid 'constrained' prefix"); + constrainedPrefix = null; return inst; } @@ -1196,7 +1199,7 @@ namespace ICSharpCode.Decompiler.IL arguments[firstArgument + i] = Pop(method.Parameters[i].Type.GetStackType()); } if (firstArgument == 1) { - arguments[0] = Pop(); + arguments[0] = Pop(CallInstruction.ExpectedTypeForThisPointer(constrainedPrefix ?? method.DeclaringType)); } switch (method.DeclaringType.Kind) { case TypeKind.Array: @@ -1224,6 +1227,7 @@ namespace ICSharpCode.Decompiler.IL default: var call = CallInstruction.Create(opCode, method); call.ILStackWasEmpty = currentStack.IsEmpty; + call.ConstrainedTo = constrainedPrefix; call.Arguments.AddRange(arguments); if (call.ResultType != StackType.Void) return Push(call); diff --git a/ICSharpCode.Decompiler/IL/Instructions/CallInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/CallInstruction.cs index 915ba74bc..83a162c11 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/CallInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/CallInstruction.cs @@ -73,7 +73,34 @@ namespace ICSharpCode.Decompiler.IL return Method.ReturnType.GetStackType(); } } - + + /// + /// Gets the expected stack type for passing the this pointer in a method call. + /// Returns StackType.O for reference types (this pointer passed as object reference), + /// and StackType.Ref for type parameters and value types (this pointer passed as managed reference). + /// + internal static StackType ExpectedTypeForThisPointer(IType type) + { + if (type.Kind == TypeKind.TypeParameter) + return StackType.Ref; + return type.IsReferenceType == true ? StackType.O : StackType.Ref; + } + + internal override void CheckInvariant(ILPhase phase) + { + base.CheckInvariant(phase); + int firstArgument = (OpCode != OpCode.NewObj && !Method.IsStatic) ? 1 : 0; + Debug.Assert(Method.Parameters.Count + firstArgument == Arguments.Count); + if (firstArgument == 1) { + Debug.Assert(Arguments[0].ResultType == ExpectedTypeForThisPointer(ConstrainedTo ?? Method.DeclaringType), + $"Stack type mismatch in 'this' argument in call to {Method.Name}()"); + } + for (int i = 0; i < Method.Parameters.Count; ++i) { + Debug.Assert(Arguments[firstArgument + i].ResultType == Method.Parameters[i].Type.GetStackType(), + $"Stack type mismatch in parameter {i} in call to {Method.Name}()"); + } + } + public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { ILRange.WriteTo(output, options); diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs index 122d642ea..a5db26eb8 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs @@ -437,6 +437,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return (null, SpecialType.UnknownType); IList arguments = null; ILInstruction target = null; + IType targetType = null; if (MatchGetMethodFromHandle(invocation.Arguments[0], out var member)) { // static method if (invocation.Arguments.Count != 2 || !MatchArgumentList(invocation.Arguments[1], out arguments)) { @@ -447,13 +448,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms arguments = new List(invocation.Arguments.Skip(2)); } if (!invocation.Arguments[0].MatchLdNull()) { - 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) @@ -473,17 +470,37 @@ namespace ICSharpCode.Decompiler.IL.Transforms }, delegateType); } CallInstruction call; - if (method.IsAbstract || method.IsVirtual || method.IsOverridable) { + if (method.IsAbstract || method.IsVirtual || method.IsOverride) { call = new CallVirt(method); } else { call = new Call(method); } - if (target != null) - call.Arguments.Add(target); + if (target != null) { + call.Arguments.Add(PrepareCallTarget(method.DeclaringType, target, targetType)); + } call.Arguments.AddRange(arguments); return (call, method.ReturnType); } + ILInstruction PrepareCallTarget(IType expectedType, ILInstruction target, IType targetType) + { + switch (CallInstruction.ExpectedTypeForThisPointer(expectedType)) { + case StackType.Ref: + if (target.ResultType == StackType.Ref) + return target; + else + return new AddressOf(target); + case StackType.O: + if (targetType.IsReferenceType == false) { + return new Box(target, targetType); + } else { + return target; + } + default: + return target; + } + } + (ILInstruction, IType) ConvertCast(CallInstruction invocation, bool isChecked) { if (invocation.Arguments.Count < 2) @@ -651,7 +668,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return (null, SpecialType.UnknownType); arguments[i] = arg; } - var call = new Call(invokeMethod); + var call = new CallVirt(invokeMethod); call.Arguments.Add(target); call.Arguments.AddRange(arguments); return (call, invokeMethod.ReturnType); @@ -879,8 +896,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (invocation.Arguments.Count < 2) return (null, SpecialType.UnknownType); ILInstruction target = null; + IType targetType = null; if (!invocation.Arguments[0].MatchLdNull()) { - target = ConvertInstruction(invocation.Arguments[0]).Item1; + (target, targetType) = ConvertInstruction(invocation.Arguments[0]); if (target == null) return (null, SpecialType.UnknownType); } @@ -896,15 +914,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms return (null, SpecialType.UnknownType); } } - if (target != null) { - arguments.Insert(0, target); - } CallInstruction call; - if (member.IsAbstract || member.IsVirtual || member.IsOverridable) { + if (member.IsAbstract || member.IsVirtual || member.IsOverride) { call = new CallVirt((IMethod)member); } else { call = new Call((IMethod)member); } + if (target != null) { + call.Arguments.Add(PrepareCallTarget(member.DeclaringType, target, targetType)); + } call.Arguments.AddRange(arguments); return (call, member.ReturnType); }