diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.Expected.cs b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.Expected.cs index dfe0e184b..72d6c3b3e 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.Expected.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.Expected.cs @@ -19,5 +19,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly { return value; } + + internal static Func ExtensionMethodAsStaticFunc() + { + return Return; + } + + internal static Func ExtensionMethodBoundToNull() + { + return new Func(null, __ldftn(Return)); + } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.cs b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.cs index 61b9b43bb..294b71938 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.cs @@ -13,5 +13,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly { return value; } + + internal static Func ExtensionMethodAsStaticFunc() + { + return Return; + } + + internal static Func ExtensionMethodBoundToNull() + { + return ((object)null).Return; + } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.opt.roslyn.il index 41b3e7b87..12d81400b 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.opt.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.opt.roslyn.il @@ -61,6 +61,30 @@ IL_0001: ret } // end of method NoExtensionMethods::Return + .method assembly hidebysig static class [mscorlib]System.Func`2 + ExtensionMethodAsStaticFunc() cil managed + { + // Code size 13 (0xd) + .maxstack 8 + IL_0000: ldnull + IL_0001: ldftn !!0 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods::Return(!!0) + IL_0007: newobj instance void class [mscorlib]System.Func`2::.ctor(object, + native int) + IL_000c: ret + } // end of method NoExtensionMethods::ExtensionMethodAsStaticFunc + + .method assembly hidebysig static class [mscorlib]System.Func`1 + ExtensionMethodBoundToNull() cil managed + { + // Code size 13 (0xd) + .maxstack 8 + IL_0000: ldnull + IL_0001: ldftn !!0 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods::Return(!!0) + IL_0007: newobj instance void class [mscorlib]System.Func`1::.ctor(object, + native int) + IL_000c: ret + } // end of method NoExtensionMethods::ExtensionMethodBoundToNull + } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.roslyn.il index 60af94216..a740ada26 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.roslyn.il @@ -73,6 +73,42 @@ IL_0006: ret } // end of method NoExtensionMethods::Return + .method assembly hidebysig static class [mscorlib]System.Func`2 + ExtensionMethodAsStaticFunc() cil managed + { + // Code size 18 (0x12) + .maxstack 2 + .locals init (class [mscorlib]System.Func`2 V_0) + IL_0000: nop + IL_0001: ldnull + IL_0002: ldftn !!0 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods::Return(!!0) + IL_0008: newobj instance void class [mscorlib]System.Func`2::.ctor(object, + native int) + IL_000d: stloc.0 + IL_000e: br.s IL_0010 + + IL_0010: ldloc.0 + IL_0011: ret + } // end of method NoExtensionMethods::ExtensionMethodAsStaticFunc + + .method assembly hidebysig static class [mscorlib]System.Func`1 + ExtensionMethodBoundToNull() cil managed + { + // Code size 18 (0x12) + .maxstack 2 + .locals init (class [mscorlib]System.Func`1 V_0) + IL_0000: nop + IL_0001: ldnull + IL_0002: ldftn !!0 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods::Return(!!0) + IL_0008: newobj instance void class [mscorlib]System.Func`1::.ctor(object, + native int) + IL_000d: stloc.0 + IL_000e: br.s IL_0010 + + IL_0010: ldloc.0 + IL_0011: ret + } // end of method NoExtensionMethods::ExtensionMethodBoundToNull + } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 7b6c1b2bf..f7e2b5a67 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -1235,12 +1235,38 @@ namespace ICSharpCode.Decompiler.CSharp default: throw new ArgumentException($"Unknown instruction type: {func.OpCode}"); } - if (method.IsStatic && !method.IsExtensionMethod) { + if (CanUseDelegateConstruction(method, thisArg, inst.Method.DeclaringType.GetDelegateInvokeMethod())) { + return HandleDelegateConstruction(inst.Method.DeclaringType, method, expectedTargetDetails, thisArg, inst); + } else { var argumentList = BuildArgumentList(expectedTargetDetails, null, inst.Method, 0, inst.Arguments, null); return HandleConstructorCall(new ExpectedTargetDetails { CallOpCode = OpCode.NewObj }, null, inst.Method, argumentList).WithILInstruction(inst); } - return HandleDelegateConstruction(inst.Method.DeclaringType, method, expectedTargetDetails, thisArg, inst); + } + + private bool CanUseDelegateConstruction(IMethod targetMethod, ILInstruction thisArg, IMethod invokeMethod) + { + if (targetMethod.IsStatic) { + // If the invoke method is known, we can compare the parameter counts to figure out whether the + // delegate is static or binds the first argument + if (invokeMethod != null) { + if (invokeMethod.Parameters.Count == targetMethod.Parameters.Count) { + return thisArg.MatchLdNull(); + } else if (targetMethod.IsExtensionMethod && invokeMethod.Parameters.Count == targetMethod.Parameters.Count - 1) { + return true; + } else { + return false; + } + } else { + // delegate type unknown: + return thisArg.MatchLdNull() || targetMethod.IsExtensionMethod; + } + } else { + // targetMethod is instance method + if (invokeMethod != null && invokeMethod.Parameters.Count != targetMethod.Parameters.Count) + return false; + return true; + } } internal TranslatedExpression Build(LdVirtDelegate inst)