diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index 29015d86ae..f112879f55 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -105,6 +105,7 @@ + @@ -209,6 +210,8 @@ + + diff --git a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs index f0d8afb67a..ea17c49f82 100644 --- a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs @@ -51,6 +51,12 @@ public void AllFilesHaveTests() } } + [Test] + public async Task AnonymousMethodEdgeCases() + { + await Run(); + } + [Test, Ignore("Need to decide how to represent virtual methods without 'newslot' flag")] public async Task Issue379() { @@ -353,6 +359,12 @@ public async Task GuessAccessors() await Run(); } + [Test] + public async Task InaccessibleParameterTypes() + { + await Run(); + } + [Test] public async Task EmptyBodies() { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/AnonymousMethodEdgeCases.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/AnonymousMethodEdgeCases.cs new file mode 100644 index 0000000000..84d19f5f1d --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/AnonymousMethodEdgeCases.cs @@ -0,0 +1,22 @@ +using System; + +namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty +{ + public class AnonymousMethodEdgeCases + { + public Func AssignmentIsTheLambdaBody() + { + return (int x) => { + int num; + return num = x; + }; + } + + public Action UsedParameterWithInvalidName() + { + return (object value) => { + Console.WriteLine(value); + }; + } + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/AnonymousMethodEdgeCases.il b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/AnonymousMethodEdgeCases.il new file mode 100644 index 0000000000..e227e3be22 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/AnonymousMethodEdgeCases.il @@ -0,0 +1,79 @@ +// Hand-written input for two anonymous-method shapes that C# cannot express directly: +// an expression-bodied lambda whose body is the assignment declaring its own local, and +// a parameter whose metadata name is not a valid C# identifier but IS used in the body. +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) + .ver 4:0:0:0 +} +.assembly extern System.Core +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) + .ver 4:0:0:0 +} +.assembly AnonymousMethodEdgeCases +{ + .ver 1:0:0:0 +} +.module AnonymousMethodEdgeCases.dll +.imagebase 0x00400000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 +.corflags 0x00000001 + +.class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.AnonymousMethodEdgeCases + extends [mscorlib]System.Object +{ + .method public hidebysig specialname rtspecialname instance void .ctor () cil managed + { + .maxstack 8 + ldarg.0 + call instance void [mscorlib]System.Object::.ctor() + ret + } + + .method public hidebysig instance class [System.Core]System.Func`2 AssignmentIsTheLambdaBody () cil managed + { + .maxstack 8 + ldarg.0 + ldftn instance int32 ICSharpCode.Decompiler.Tests.TestCases.ILPretty.AnonymousMethodEdgeCases::'b__1_0'(int32) + newobj instance void class [System.Core]System.Func`2::.ctor(object, native int) + ret + } + + // return num = x; -- the store's value is the return value, so the local has a store and + // no load, and the decompiled lambda body is the assignment itself. + .method private hidebysig instance int32 'b__1_0' (int32 x) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .maxstack 2 + .locals init ( + [0] int32 num + ) + ldarg.1 + dup + stloc.0 + ret + } + + .method public hidebysig instance class [System.Core]System.Action`1 UsedParameterWithInvalidName () cil managed + { + .maxstack 8 + ldarg.0 + ldftn instance void ICSharpCode.Decompiler.Tests.TestCases.ILPretty.AnonymousMethodEdgeCases::'b__2_0'(object) + newobj instance void class [System.Core]System.Action`1::.ctor(object, native int) + ret + } + + // The parameter name '' is what C# emits for an anonymous method declared without a + // parameter list; used here, so the lambda's parameter list has to name it somehow. + .method private hidebysig instance void 'b__2_0' (object '') cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .maxstack 8 + ldarg.1 + call void [mscorlib]System.Console::WriteLine(object) + ret + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/InaccessibleParameterTypes.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/InaccessibleParameterTypes.cs new file mode 100644 index 0000000000..e1dcd80b3e --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/InaccessibleParameterTypes.cs @@ -0,0 +1,31 @@ +using System; + +namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty +{ + public class InaccessibleParameterTypes + { + private class Hidden + { + } + + public delegate void Handler(Hidden h); + + public static void Register(Action callback) + { + } + } + public class InaccessibleParameterTypesConsumer + { + public InaccessibleParameterTypes.Handler Create() + { + return delegate { + }; + } + + public void Run() + { + InaccessibleParameterTypes.Register(delegate { + }); + } + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/InaccessibleParameterTypes.il b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/InaccessibleParameterTypes.il new file mode 100644 index 0000000000..968ca42645 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/InaccessibleParameterTypes.il @@ -0,0 +1,138 @@ +// Anonymous methods whose delegate signature contains a type the use site cannot name. +// IL (unlike C#) permits a public delegate with a less-accessible parameter type, so the +// parameter-list-less "delegate {}" form is the only C# syntax the consumer class below +// could legally have used - the decompiler must not expand it to a lambda parameter list. +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly extern System.Core +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly InaccessibleParameterTypes +{ + .ver 1:0:0:0 +} +.module InaccessibleParameterTypes.dll +.imagebase 0x00400000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00020003 // ILONLY 32BITPREFERRED + +.class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.InaccessibleParameterTypes + extends [mscorlib]System.Object +{ + .class nested private auto ansi beforefieldinit Hidden + extends [mscorlib]System.Object + { + .method public hidebysig specialname rtspecialname instance void .ctor () cil managed + { + .maxstack 8 + + ldarg.0 + call instance void [mscorlib]System.Object::.ctor() + ret + } + } + + .class nested public auto ansi sealed Handler + extends [mscorlib]System.MulticastDelegate + { + .method public hidebysig specialname rtspecialname instance void .ctor (object 'object', native int 'method') runtime managed + { + } + + .method public hidebysig newslot virtual instance void Invoke (class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.InaccessibleParameterTypes/Hidden h) runtime managed + { + } + } + + .method public hidebysig static void Register (class [System.Core]System.Action`1