diff --git a/ICSharpCode.Decompiler.Tests/DisassemblerPrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/DisassemblerPrettyTestRunner.cs index 8cec9947b..8c3477840 100644 --- a/ICSharpCode.Decompiler.Tests/DisassemblerPrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/DisassemblerPrettyTestRunner.cs @@ -58,15 +58,22 @@ namespace ICSharpCode.Decompiler.Tests await Run(); } - async Task Run([CallerMemberName] string testName = null) + [Test] + public async Task SortMembers() + { + await Run(ilExpectedFile: Path.Combine(TestCasePath, "SortMembers.expected.il"), asmOptions: AssemblerOptions.SortedOutput); + } + + async Task Run([CallerMemberName] string testName = null, string ilExpectedFile = null, AssemblerOptions asmOptions = AssemblerOptions.None) { - var ilExpectedFile = Path.Combine(TestCasePath, testName + ".il"); + var ilInputFile = Path.Combine(TestCasePath, testName + ".il"); + ilExpectedFile ??= ilInputFile; var ilResultFile = Path.Combine(TestCasePath, testName + ".result.il"); - var executable = await Tester.AssembleIL(ilExpectedFile, AssemblerOptions.Library).ConfigureAwait(false); - var disassembled = await Tester.Disassemble(executable, ilResultFile, AssemblerOptions.UseOwnDisassembler).ConfigureAwait(false); + var executable = await Tester.AssembleIL(ilInputFile, AssemblerOptions.Library).ConfigureAwait(false); + var disassembled = await Tester.Disassemble(executable, ilResultFile, AssemblerOptions.UseOwnDisassembler | asmOptions).ConfigureAwait(false); - CodeAssert.FilesAreEqual(ilExpectedFile, ilResultFile); + CodeAssert.FilesAreEqual(ilExpectedFile, disassembled); } } } diff --git a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs index 1c6e5ac09..3d35779e1 100644 --- a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs +++ b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs @@ -85,6 +85,8 @@ namespace ICSharpCode.Decompiler.Tests.Helpers UseOwnDisassembler = 0x8, /// Work around bug in .NET 5 ilasm (https://github.com/dotnet/runtime/issues/32400) UseLegacyAssembler = 0x10, + /// UseSortByNameFilter, implies UseOwnDisassembler + SortedOutput = 0x20, } public static partial class Tester @@ -196,7 +198,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers public static async Task Disassemble(string sourceFileName, string outputFile, AssemblerOptions asmOptions) { - if (asmOptions.HasFlag(AssemblerOptions.UseOwnDisassembler)) + if (asmOptions.HasFlag(AssemblerOptions.UseOwnDisassembler) || asmOptions.HasFlag(AssemblerOptions.SortedOutput)) { using (var peFileStream = new FileStream(sourceFileName, FileMode.Open, FileAccess.Read)) using (var peFile = new PEFile(sourceFileName, peFileStream)) @@ -205,7 +207,11 @@ namespace ICSharpCode.Decompiler.Tests.Helpers var metadata = peFile.Metadata; var output = new PlainTextOutput(writer); ReflectionDisassembler rd = new ReflectionDisassembler(output, CancellationToken.None); - rd.AssemblyResolver = new UniversalAssemblyResolver(sourceFileName, true, null); + if (asmOptions.HasFlag(AssemblerOptions.SortedOutput)) + { + rd.EntityProcessor = new SortByNameProcessor(); + } + rd.AssemblyResolver = new UniversalAssemblyResolver(sourceFileName, throwOnError: true, null); rd.DetectControlStructure = false; rd.WriteAssemblyReferences(metadata); if (metadata.IsAssembly) diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index 074902fe0..21351c47b 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -73,6 +73,8 @@ + + diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Disassembler/Pretty/.gitignore b/ICSharpCode.Decompiler.Tests/TestCases/Disassembler/Pretty/.gitignore new file mode 100644 index 000000000..5bb64d7b2 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Disassembler/Pretty/.gitignore @@ -0,0 +1,2 @@ +*.result.il +*.dll diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Disassembler/Pretty/SortMembers.expected.il b/ICSharpCode.Decompiler.Tests/TestCases/Disassembler/Pretty/SortMembers.expected.il new file mode 100644 index 000000000..2d5208085 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Disassembler/Pretty/SortMembers.expected.il @@ -0,0 +1,395 @@ +.assembly extern mscorlib +{ + .publickeytoken = ( + b7 7a 5c 56 19 34 e0 89 + ) + .ver 4:0:0:0 +} +.assembly SecurityDeclarations +{ + .custom instance void [mscorlib]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( + 01 00 07 31 2e 30 2e 30 2e 30 00 00 + ) + .hash algorithm 0x00008004 // SHA1 + .ver 1:0:0:0 +} + +.module SecurityDeclarations.dll +.imagebase 0x10000000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WindowsCui +.corflags 0x00000001 // ILOnly + +.class private auto ansi '' +{ +} // end of class + +.class private auto ansi beforefieldinit SecurityDeclarations.NestedArrays + extends [mscorlib]System.Object +{ + .permissionset assert = { + class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = { + field object TestBoxed2 = object(object[4](int32(1) int32(2) int32(3) object[3](int32(4) int32(5) int32(6)))) + } + } + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x20d4 + // Header size: 1 + // Code size: 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method NestedArrays::.ctor + +} // end of class SecurityDeclarations.NestedArrays + +.class private auto ansi beforefieldinit SecurityDeclarations.SecurityAttrTest + extends [mscorlib]System.Security.Permissions.SecurityAttribute +{ + // Fields + .field private valuetype SecurityDeclarations.TestEnum[] _testEnumArray + .field private int32[] _testInt32Array + .field private string[] _testStringArray + .field private class [mscorlib]System.Type[] _testTypeArray + .field public object TestBoxed + .field public object TestBoxed2 + .field public object TestBoxedArray + .field public object TestBoxedString + .field public object TestBoxedType + .field public valuetype SecurityDeclarations.TestEnum TestEnumType + .field public int32 TestInt32 + .field public string TestString + .field public class [mscorlib]System.Type TestType + + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor ( + valuetype [mscorlib]System.Security.Permissions.SecurityAction action + ) cil managed + { + // Method begins at RVA 0x2059 + // Header size: 1 + // Code size: 10 (0xa) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: call instance void [mscorlib]System.Security.Permissions.SecurityAttribute::.ctor(valuetype [mscorlib]System.Security.Permissions.SecurityAction) + IL_0007: nop + IL_0008: nop + IL_0009: ret + } // end of method SecurityAttrTest::.ctor + + .method public hidebysig virtual + instance class [mscorlib]System.Security.IPermission CreatePermission () cil managed + { + // Method begins at RVA 0x2064 + // Header size: 1 + // Code size: 7 (0x7) + .maxstack 8 + + IL_0000: nop + IL_0001: newobj instance void [mscorlib]System.NotImplementedException::.ctor() + IL_0006: throw + } // end of method SecurityAttrTest::CreatePermission + + .method public hidebysig specialname + instance valuetype SecurityDeclarations.TestEnum[] get_TestEnumArray () cil managed + { + // Method begins at RVA 0x208e + // Header size: 1 + // Code size: 7 (0x7) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: ldfld valuetype SecurityDeclarations.TestEnum[] SecurityDeclarations.SecurityAttrTest::_testEnumArray + IL_0006: ret + } // end of method SecurityAttrTest::get_TestEnumArray + + .method public hidebysig specialname + instance int32[] get_TestInt32Array () cil managed + { + // Method begins at RVA 0x207d + // Header size: 1 + // Code size: 7 (0x7) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: ldfld int32[] SecurityDeclarations.SecurityAttrTest::_testInt32Array + IL_0006: ret + } // end of method SecurityAttrTest::get_TestInt32Array + + .method public hidebysig specialname + instance string[] get_TestStringArray () cil managed + { + // Method begins at RVA 0x206c + // Header size: 1 + // Code size: 7 (0x7) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: ldfld string[] SecurityDeclarations.SecurityAttrTest::_testStringArray + IL_0006: ret + } // end of method SecurityAttrTest::get_TestStringArray + + .method public hidebysig specialname + instance class [mscorlib]System.Type[] get_TestTypeArray () cil managed + { + // Method begins at RVA 0x209f + // Header size: 1 + // Code size: 7 (0x7) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: ldfld class [mscorlib]System.Type[] SecurityDeclarations.SecurityAttrTest::_testTypeArray + IL_0006: ret + } // end of method SecurityAttrTest::get_TestTypeArray + + .method public hidebysig specialname + instance void set_TestEnumArray ( + valuetype SecurityDeclarations.TestEnum[] 'value' + ) cil managed + { + // Method begins at RVA 0x2096 + // Header size: 1 + // Code size: 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld valuetype SecurityDeclarations.TestEnum[] SecurityDeclarations.SecurityAttrTest::_testEnumArray + IL_0007: ret + } // end of method SecurityAttrTest::set_TestEnumArray + + .method public hidebysig specialname + instance void set_TestInt32Array ( + int32[] 'value' + ) cil managed + { + // Method begins at RVA 0x2085 + // Header size: 1 + // Code size: 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld int32[] SecurityDeclarations.SecurityAttrTest::_testInt32Array + IL_0007: ret + } // end of method SecurityAttrTest::set_TestInt32Array + + .method public hidebysig specialname + instance void set_TestStringArray ( + string[] 'value' + ) cil managed + { + // Method begins at RVA 0x2074 + // Header size: 1 + // Code size: 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld string[] SecurityDeclarations.SecurityAttrTest::_testStringArray + IL_0007: ret + } // end of method SecurityAttrTest::set_TestStringArray + + .method public hidebysig specialname + instance void set_TestTypeArray ( + class [mscorlib]System.Type[] 'value' + ) cil managed + { + // Method begins at RVA 0x20a7 + // Header size: 1 + // Code size: 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld class [mscorlib]System.Type[] SecurityDeclarations.SecurityAttrTest::_testTypeArray + IL_0007: ret + } // end of method SecurityAttrTest::set_TestTypeArray + + // Properties + .property instance valuetype SecurityDeclarations.TestEnum[] TestEnumArray() + { + .get instance valuetype SecurityDeclarations.TestEnum[] SecurityDeclarations.SecurityAttrTest::get_TestEnumArray() + .set instance void SecurityDeclarations.SecurityAttrTest::set_TestEnumArray(valuetype SecurityDeclarations.TestEnum[]) + } + .property instance int32[] TestInt32Array() + { + .get instance int32[] SecurityDeclarations.SecurityAttrTest::get_TestInt32Array() + .set instance void SecurityDeclarations.SecurityAttrTest::set_TestInt32Array(int32[]) + } + .property instance string[] TestStringArray() + { + .get instance string[] SecurityDeclarations.SecurityAttrTest::get_TestStringArray() + .set instance void SecurityDeclarations.SecurityAttrTest::set_TestStringArray(string[]) + } + .property instance class [mscorlib]System.Type[] TestTypeArray() + { + .get instance class [mscorlib]System.Type[] SecurityDeclarations.SecurityAttrTest::get_TestTypeArray() + .set instance void SecurityDeclarations.SecurityAttrTest::set_TestTypeArray(class [mscorlib]System.Type[]) + } + +} // end of class SecurityDeclarations.SecurityAttrTest + +.class private auto ansi beforefieldinit SecurityDeclarations.SimpleType + extends [mscorlib]System.Object +{ + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2050 + // Header size: 1 + // Code size: 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method SimpleType::.ctor + +} // end of class SecurityDeclarations.SimpleType + +.class private auto ansi sealed SecurityDeclarations.TestEnum + extends [mscorlib]System.Enum +{ + // Fields + .field public static literal valuetype SecurityDeclarations.TestEnum A = int32(0) + .field public static literal valuetype SecurityDeclarations.TestEnum B = int32(1) + .field public static literal valuetype SecurityDeclarations.TestEnum C = int32(2) + .field public specialname rtspecialname int32 value__ + +} // end of class SecurityDeclarations.TestEnum + +.class private auto ansi beforefieldinit SecurityDeclarations.TestEnumTypes + extends [mscorlib]System.Object +{ + .permissionset inheritcheck = { + class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = { + field enum SecurityDeclarations.TestEnum TestEnumType = int32(0) + field object TestBoxed = object(int32(1)) + property enum SecurityDeclarations.TestEnum[] TestEnumArray = int32[3](0 1 2) + field object TestBoxed2 = object(object[4](int32(0) int32(1) int32(2) object[1](int32(3)))) + field object TestBoxedArray = object(int32[3](0 1 2)) + } + } + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x20c2 + // Header size: 1 + // Code size: 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestEnumTypes::.ctor + +} // end of class SecurityDeclarations.TestEnumTypes + +.class private auto ansi beforefieldinit SecurityDeclarations.TestInt32Types + extends [mscorlib]System.Object +{ + .permissionset permitonly = { + class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = { + field int32 TestInt32 = int32(5) + field object TestBoxed = object(int32(10)) + property int32[] TestInt32Array = int32[3](1 2 3) + field object TestBoxedArray = object(int32[3](4 5 6)) + field object TestBoxed2 = object(object[3](int32(7) int32(8) int32(9))) + } + } + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x20cb + // Header size: 1 + // Code size: 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestInt32Types::.ctor + +} // end of class SecurityDeclarations.TestInt32Types + +.class private auto ansi beforefieldinit SecurityDeclarations.TestStringTypes + extends [mscorlib]System.Object +{ + .permissionset assert = { + class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = { + field string TestString = string('Hello World!') + field object TestBoxedString = object(string('Boxed String')) + property string[] TestStringArray = string[2]('a' 'b') + field object TestBoxedArray = object(string[2]('c' 'd')) + } + } + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x20b0 + // Header size: 1 + // Code size: 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestStringTypes::.ctor + +} // end of class SecurityDeclarations.TestStringTypes + +.class private sequential ansi sealed beforefieldinit SecurityDeclarations.TestStruct + extends [mscorlib]System.ValueType +{ + .pack 0 + .size 1 + +} // end of class SecurityDeclarations.TestStruct + +.class private auto ansi beforefieldinit SecurityDeclarations.TestTypeTypes + extends [mscorlib]System.Object +{ + .permissionset demand = { + class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = { + field type TestType = type(SecurityDeclarations.SimpleType) + field object TestBoxed = object(type(SecurityDeclarations.TestEnum)) + property type[] TestTypeArray = type[2](SecurityDeclarations.TestStruct SecurityDeclarations.SimpleType) + field object TestBoxedArray = object(type[2](SecurityDeclarations.TestStringTypes SecurityDeclarations.TestTypeTypes)) + } + } + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x20b9 + // Header size: 1 + // Code size: 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestTypeTypes::.ctor + +} // end of class SecurityDeclarations.TestTypeTypes + diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Disassembler/Pretty/SortMembers.il b/ICSharpCode.Decompiler.Tests/TestCases/Disassembler/Pretty/SortMembers.il new file mode 100644 index 000000000..056544a9d --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Disassembler/Pretty/SortMembers.il @@ -0,0 +1,381 @@ +.assembly extern mscorlib +{ + .publickeytoken = ( + b7 7a 5c 56 19 34 e0 89 + ) + .ver 4:0:0:0 +} +.assembly SecurityDeclarations +{ + .custom instance void [mscorlib]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( + 01 00 07 31 2e 30 2e 30 2e 30 00 00 + ) + .hash algorithm 0x00008004 // SHA1 + .ver 1:0:0:0 +} + +.module SecurityDeclarations.dll +// MVID: {761F919A-2373-48EB-9282-9DAB26913D43} +.imagebase 0x10000000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WindowsCui +.corflags 0x00000001 // ILOnly + + +.class private auto ansi '' +{ +} // end of class + +.class private sequential ansi sealed beforefieldinit SecurityDeclarations.TestStruct + extends [mscorlib]System.ValueType +{ + .pack 0 + .size 1 + +} // end of class SecurityDeclarations.TestStruct + +.class private auto ansi beforefieldinit SecurityDeclarations.SimpleType + extends [mscorlib]System.Object +{ + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2050 + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method SimpleType::.ctor + +} // end of class SecurityDeclarations.SimpleType + +.class private auto ansi sealed SecurityDeclarations.TestEnum + extends [mscorlib]System.Enum +{ + // Fields + .field public specialname rtspecialname int32 value__ + .field public static literal valuetype SecurityDeclarations.TestEnum A = int32(0) + .field public static literal valuetype SecurityDeclarations.TestEnum B = int32(1) + .field public static literal valuetype SecurityDeclarations.TestEnum C = int32(2) + +} // end of class SecurityDeclarations.TestEnum + +.class private auto ansi beforefieldinit SecurityDeclarations.SecurityAttrTest + extends [mscorlib]System.Security.Permissions.SecurityAttribute +{ + // Fields + .field private string[] _testStringArray + .field private int32[] _testInt32Array + .field private valuetype SecurityDeclarations.TestEnum[] _testEnumArray + .field private class [mscorlib]System.Type[] _testTypeArray + .field public int32 TestInt32 + .field public class [mscorlib]System.Type TestType + .field public valuetype SecurityDeclarations.TestEnum TestEnumType + .field public object TestBoxed + .field public object TestBoxed2 + .field public string TestString + .field public object TestBoxedString + .field public object TestBoxedArray + .field public object TestBoxedType + + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor ( + valuetype [mscorlib]System.Security.Permissions.SecurityAction action + ) cil managed + { + // Method begins at RVA 0x2059 + // Code size 10 (0xa) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: call instance void [mscorlib]System.Security.Permissions.SecurityAttribute::.ctor(valuetype [mscorlib]System.Security.Permissions.SecurityAction) + IL_0007: nop + IL_0008: nop + IL_0009: ret + } // end of method SecurityAttrTest::.ctor + + .method public hidebysig virtual + instance class [mscorlib]System.Security.IPermission CreatePermission () cil managed + { + // Method begins at RVA 0x2064 + // Code size 7 (0x7) + .maxstack 8 + + IL_0000: nop + IL_0001: newobj instance void [mscorlib]System.NotImplementedException::.ctor() + IL_0006: throw + } // end of method SecurityAttrTest::CreatePermission + + .method public hidebysig specialname + instance string[] get_TestStringArray () cil managed + { + // Method begins at RVA 0x206c + // Code size 7 (0x7) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: ldfld string[] SecurityDeclarations.SecurityAttrTest::_testStringArray + IL_0006: ret + } // end of method SecurityAttrTest::get_TestStringArray + + .method public hidebysig specialname + instance void set_TestStringArray ( + string[] 'value' + ) cil managed + { + // Method begins at RVA 0x2074 + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld string[] SecurityDeclarations.SecurityAttrTest::_testStringArray + IL_0007: ret + } // end of method SecurityAttrTest::set_TestStringArray + + .method public hidebysig specialname + instance int32[] get_TestInt32Array () cil managed + { + // Method begins at RVA 0x207d + // Code size 7 (0x7) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: ldfld int32[] SecurityDeclarations.SecurityAttrTest::_testInt32Array + IL_0006: ret + } // end of method SecurityAttrTest::get_TestInt32Array + + .method public hidebysig specialname + instance void set_TestInt32Array ( + int32[] 'value' + ) cil managed + { + // Method begins at RVA 0x2085 + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld int32[] SecurityDeclarations.SecurityAttrTest::_testInt32Array + IL_0007: ret + } // end of method SecurityAttrTest::set_TestInt32Array + + .method public hidebysig specialname + instance valuetype SecurityDeclarations.TestEnum[] get_TestEnumArray () cil managed + { + // Method begins at RVA 0x208e + // Code size 7 (0x7) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: ldfld valuetype SecurityDeclarations.TestEnum[] SecurityDeclarations.SecurityAttrTest::_testEnumArray + IL_0006: ret + } // end of method SecurityAttrTest::get_TestEnumArray + + .method public hidebysig specialname + instance void set_TestEnumArray ( + valuetype SecurityDeclarations.TestEnum[] 'value' + ) cil managed + { + // Method begins at RVA 0x2096 + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld valuetype SecurityDeclarations.TestEnum[] SecurityDeclarations.SecurityAttrTest::_testEnumArray + IL_0007: ret + } // end of method SecurityAttrTest::set_TestEnumArray + + .method public hidebysig specialname + instance class [mscorlib]System.Type[] get_TestTypeArray () cil managed + { + // Method begins at RVA 0x209f + // Code size 7 (0x7) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: ldfld class [mscorlib]System.Type[] SecurityDeclarations.SecurityAttrTest::_testTypeArray + IL_0006: ret + } // end of method SecurityAttrTest::get_TestTypeArray + + .method public hidebysig specialname + instance void set_TestTypeArray ( + class [mscorlib]System.Type[] 'value' + ) cil managed + { + // Method begins at RVA 0x20a7 + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld class [mscorlib]System.Type[] SecurityDeclarations.SecurityAttrTest::_testTypeArray + IL_0007: ret + } // end of method SecurityAttrTest::set_TestTypeArray + + // Properties + .property instance string[] TestStringArray() + { + .get instance string[] SecurityDeclarations.SecurityAttrTest::get_TestStringArray() + .set instance void SecurityDeclarations.SecurityAttrTest::set_TestStringArray(string[]) + } + .property instance int32[] TestInt32Array() + { + .get instance int32[] SecurityDeclarations.SecurityAttrTest::get_TestInt32Array() + .set instance void SecurityDeclarations.SecurityAttrTest::set_TestInt32Array(int32[]) + } + .property instance valuetype SecurityDeclarations.TestEnum[] TestEnumArray() + { + .get instance valuetype SecurityDeclarations.TestEnum[] SecurityDeclarations.SecurityAttrTest::get_TestEnumArray() + .set instance void SecurityDeclarations.SecurityAttrTest::set_TestEnumArray(valuetype SecurityDeclarations.TestEnum[]) + } + .property instance class [mscorlib]System.Type[] TestTypeArray() + { + .get instance class [mscorlib]System.Type[] SecurityDeclarations.SecurityAttrTest::get_TestTypeArray() + .set instance void SecurityDeclarations.SecurityAttrTest::set_TestTypeArray(class [mscorlib]System.Type[]) + } + +} // end of class SecurityDeclarations.SecurityAttrTest + +.class private auto ansi beforefieldinit SecurityDeclarations.TestStringTypes + extends [mscorlib]System.Object +{ + .permissionset assert = { + class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = { + field string TestString = string('Hello World!') + field object TestBoxedString = object(string('Boxed String')) + property string[] TestStringArray = string[2]('a' 'b') + field object TestBoxedArray = object(string[2]('c' 'd')) + } + } + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2050 + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestStringTypes::.ctor + +} // end of class SecurityDeclarations.TestStringTypes + +.class private auto ansi beforefieldinit SecurityDeclarations.TestTypeTypes + extends [mscorlib]System.Object +{ + .permissionset demand = { + class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = { + field type TestType = type(SecurityDeclarations.SimpleType) + field object TestBoxed = object(type(SecurityDeclarations.TestEnum)) + property type[] TestTypeArray = type[2](SecurityDeclarations.TestStruct SecurityDeclarations.SimpleType) + field object TestBoxedArray = object(type[2](SecurityDeclarations.TestStringTypes SecurityDeclarations.TestTypeTypes)) + } + } + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2050 + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestTypeTypes::.ctor + +} // end of class SecurityDeclarations.TestTypeTypes + +.class private auto ansi beforefieldinit SecurityDeclarations.TestEnumTypes + extends [mscorlib]System.Object +{ + .permissionset inheritcheck = { + class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = { + field enum SecurityDeclarations.TestEnum TestEnumType = int32(0) + field object TestBoxed = object(int32(1)) + property enum SecurityDeclarations.TestEnum[] TestEnumArray = int32[3](0 1 2) + field object TestBoxed2 = object(object[4](int32(0) int32(1) int32(2) object[1](int32(3)))) + field object TestBoxedArray = object(int32[3](0 1 2)) + } + } + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2050 + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestEnumTypes::.ctor + +} // end of class SecurityDeclarations.TestEnumTypes + +.class private auto ansi beforefieldinit SecurityDeclarations.TestInt32Types + extends [mscorlib]System.Object +{ + .permissionset permitonly = { + class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = { + field int32 TestInt32 = int32(5) + field object TestBoxed = object(int32(10)) + property int32[] TestInt32Array = int32[3](1 2 3) + field object TestBoxedArray = object(int32[3](4 5 6)) + field object TestBoxed2 = object(object[3](int32(7) int32(8) int32(9))) + } + } + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2050 + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestInt32Types::.ctor + +} // end of class SecurityDeclarations.TestInt32Types + +.class private auto ansi beforefieldinit SecurityDeclarations.NestedArrays + extends [mscorlib]System.Object +{ + .permissionset assert = { + class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = { + field object TestBoxed2 = object(object[4](int32(1) int32(2) int32(3) object[3](int32(4) int32(5) int32(6)))) + } + } + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2050 + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method NestedArrays::.ctor + +} // end of class SecurityDeclarations.NestedArrays + diff --git a/ICSharpCode.Decompiler/Disassembler/IEntityProcessor.cs b/ICSharpCode.Decompiler/Disassembler/IEntityProcessor.cs new file mode 100644 index 000000000..5d94500d1 --- /dev/null +++ b/ICSharpCode.Decompiler/Disassembler/IEntityProcessor.cs @@ -0,0 +1,44 @@ +// Copyright (c) 2022 Tom Englert +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#nullable enable + +using System.Collections.Generic; +using System.Reflection.Metadata; + +using ICSharpCode.Decompiler.Metadata; + +namespace ICSharpCode.Decompiler.Disassembler +{ + public interface IEntityProcessor + { + IReadOnlyCollection Process(PEFile module, IReadOnlyCollection items); + + IReadOnlyCollection Process(PEFile module, IReadOnlyCollection items); + + IReadOnlyCollection Process(PEFile module, IReadOnlyCollection items); + + IReadOnlyCollection Process(PEFile module, IReadOnlyCollection items); + + IReadOnlyCollection Process(PEFile module, IReadOnlyCollection items); + + IReadOnlyCollection Process(PEFile module, IReadOnlyCollection items); + + IReadOnlyCollection Process(PEFile module, IReadOnlyCollection items); + } +} \ No newline at end of file diff --git a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs index 3cd91ae14..4bb4ed6a1 100644 --- a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs @@ -71,10 +71,12 @@ namespace ICSharpCode.Decompiler.Disassembler set => methodBodyDisassembler.DebugInfo = value; } - public bool ExpandMemberDefinitions { get; set; } = false; + public bool ExpandMemberDefinitions { get; set; } public IAssemblyResolver AssemblyResolver { get; set; } + public IEntityProcessor EntityProcessor { get; set; } + public ReflectionDisassembler(ITextOutput output, CancellationToken cancellationToken) : this(output, new MethodBodyDisassembler(output, cancellationToken), cancellationToken) { @@ -1560,7 +1562,7 @@ namespace ICSharpCode.Decompiler.Disassembler DisassembleTypeHeaderInternal(module, type, typeDefinition, genericContext); - var interfaces = typeDefinition.GetInterfaceImplementations(); + var interfaces = Process(module, typeDefinition.GetInterfaceImplementations()); if (interfaces.Count > 0) { output.Indent(); @@ -1599,8 +1601,8 @@ namespace ICSharpCode.Decompiler.Disassembler output.WriteLine(".size {0}", layout.Size); output.WriteLine(); } - var nestedTypes = typeDefinition.GetNestedTypes(); - if (!nestedTypes.IsEmpty) + var nestedTypes = Process(module, typeDefinition.GetNestedTypes()); + if (nestedTypes.Any()) { output.WriteLine("// Nested Types"); foreach (var nestedType in nestedTypes) @@ -1611,7 +1613,7 @@ namespace ICSharpCode.Decompiler.Disassembler } output.WriteLine(); } - var fields = typeDefinition.GetFields(); + var fields = Process(module, typeDefinition.GetFields()); if (fields.Any()) { output.WriteLine("// Fields"); @@ -1622,7 +1624,7 @@ namespace ICSharpCode.Decompiler.Disassembler } output.WriteLine(); } - var methods = typeDefinition.GetMethods(); + var methods = Process(module, typeDefinition.GetMethods()); if (methods.Any()) { output.WriteLine("// Methods"); @@ -1633,7 +1635,7 @@ namespace ICSharpCode.Decompiler.Disassembler output.WriteLine(); } } - var events = typeDefinition.GetEvents(); + var events = Process(module, typeDefinition.GetEvents()); if (events.Any()) { output.WriteLine("// Events"); @@ -1645,7 +1647,7 @@ namespace ICSharpCode.Decompiler.Disassembler } output.WriteLine(); } - var properties = typeDefinition.GetProperties(); + var properties = Process(module, typeDefinition.GetProperties()); if (properties.Any()) { output.WriteLine("// Properties"); @@ -1747,11 +1749,51 @@ namespace ICSharpCode.Decompiler.Disassembler } #endregion + #region Processing + + private IReadOnlyCollection Process(PEFile module, IReadOnlyCollection items) + { + return EntityProcessor?.Process(module, items) ?? items; + } + + private IReadOnlyCollection Process(PEFile module, IReadOnlyCollection items) + { + return EntityProcessor?.Process(module, items) ?? items; + } + + private IReadOnlyCollection Process(PEFile module, IReadOnlyCollection items) + { + return EntityProcessor?.Process(module, items) ?? items; + } + + private IReadOnlyCollection Process(PEFile module, IReadOnlyCollection items) + { + return EntityProcessor?.Process(module, items) ?? items; + } + + private IReadOnlyCollection Process(PEFile module, IReadOnlyCollection items) + { + return EntityProcessor?.Process(module, items) ?? items; + } + + private IReadOnlyCollection Process(PEFile module, IReadOnlyCollection items) + { + return EntityProcessor?.Process(module, items) ?? items; + } + + private IReadOnlyCollection Process(PEFile module, IReadOnlyCollection items) + { + return EntityProcessor?.Process(module, items) ?? items; + } + + #endregion + #region Helper methods + void WriteAttributes(PEFile module, CustomAttributeHandleCollection attributes) { var metadata = module.Metadata; - foreach (CustomAttributeHandle a in attributes) + foreach (CustomAttributeHandle a in Process(module, attributes)) { output.Write(".custom "); var attr = metadata.GetCustomAttribute(a); @@ -2037,7 +2079,7 @@ namespace ICSharpCode.Decompiler.Disassembler public void WriteModuleContents(PEFile module) { - foreach (var handle in module.Metadata.GetTopLevelTypeDefinitions()) + foreach (var handle in Process(module, module.Metadata.GetTopLevelTypeDefinitions().ToArray())) { DisassembleType(module, handle); output.WriteLine(); diff --git a/ICSharpCode.Decompiler/Disassembler/SortByNameProcessor.cs b/ICSharpCode.Decompiler/Disassembler/SortByNameProcessor.cs new file mode 100644 index 000000000..cda178056 --- /dev/null +++ b/ICSharpCode.Decompiler/Disassembler/SortByNameProcessor.cs @@ -0,0 +1,122 @@ +// Copyright (c) 2022 Tom Englert +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection.Metadata; + +using ICSharpCode.Decompiler.IL; +using ICSharpCode.Decompiler.Metadata; + +namespace ICSharpCode.Decompiler.Disassembler +{ + public class SortByNameProcessor : IEntityProcessor + { + public IReadOnlyCollection Process(PEFile module, + IReadOnlyCollection items) + { + return items.OrderBy(item => GetSortKey(item, module)).ToArray(); + } + + public IReadOnlyCollection Process(PEFile module, + IReadOnlyCollection items) + { + return items.OrderBy(item => GetSortKey(item, module)).ToArray(); + } + + public IReadOnlyCollection Process(PEFile module, + IReadOnlyCollection items) + { + return items.OrderBy(item => GetSortKey(item, module)).ToArray(); + } + + public IReadOnlyCollection Process(PEFile module, + IReadOnlyCollection items) + { + return items.OrderBy(item => GetSortKey(item, module)).ToArray(); + } + + public IReadOnlyCollection Process(PEFile module, + IReadOnlyCollection items) + { + return items.OrderBy(item => GetSortKey(item, module)).ToArray(); + } + + public IReadOnlyCollection Process(PEFile module, + IReadOnlyCollection items) + { + return items.OrderBy(item => GetSortKey(item, module)).ToArray(); + } + + public IReadOnlyCollection Process(PEFile module, + IReadOnlyCollection items) + { + return items.OrderBy(item => GetSortKey(item, module)).ToArray(); + } + + private static string GetSortKey(TypeDefinitionHandle handle, PEFile module) => + handle.GetFullTypeName(module.Metadata).ToILNameString(); + + private static string GetSortKey(MethodDefinitionHandle handle, PEFile module) + { + PlainTextOutput output = new PlainTextOutput(); + MethodDefinition definition = module.Metadata.GetMethodDefinition(handle); + + // Start with the methods name, skip return type + output.Write(module.Metadata.GetString(definition.Name)); + + DisassemblerSignatureTypeProvider signatureProvider = new DisassemblerSignatureTypeProvider(module, output); + MethodSignature> signature = + definition.DecodeSignature(signatureProvider, new MetadataGenericContext(handle, module)); + + if (signature.GenericParameterCount > 0) + { + output.Write($"`{signature.GenericParameterCount}"); + } + + InstructionOutputExtensions.WriteParameterList(output, signature); + + return output.ToString(); + } + + private static string GetSortKey(InterfaceImplementationHandle handle, PEFile module) => + module.Metadata.GetInterfaceImplementation(handle) + .Interface + .GetFullTypeName(module.Metadata) + .ToILNameString(); + + private static string GetSortKey(FieldDefinitionHandle handle, PEFile module) => + module.Metadata.GetString(module.Metadata.GetFieldDefinition(handle).Name); + + private static string GetSortKey(PropertyDefinitionHandle handle, PEFile module) => + module.Metadata.GetString(module.Metadata.GetPropertyDefinition(handle).Name); + + private static string GetSortKey(EventDefinitionHandle handle, PEFile module) => + module.Metadata.GetString(module.Metadata.GetEventDefinition(handle).Name); + + private static string GetSortKey(CustomAttributeHandle handle, PEFile module) => + module.Metadata.GetCustomAttribute(handle) + .Constructor + .GetDeclaringType(module.Metadata) + .GetFullTypeName(module.Metadata) + .ToILNameString(); + } +} \ No newline at end of file diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 0c8245377..5ee433d8a 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -92,6 +92,8 @@ + + diff --git a/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs b/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs index 75579f41d..be5ebcd2d 100644 --- a/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs +++ b/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs @@ -321,7 +321,7 @@ namespace ICSharpCode.Decompiler.IL output.Write('>'); } - static void WriteParameterList(ITextOutput output, MethodSignature> methodSignature) + internal static void WriteParameterList(ITextOutput output, MethodSignature> methodSignature) { output.Write("("); for (int i = 0; i < methodSignature.ParameterTypes.Length; ++i)