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