From 0f8c310de212844dce336a8b1b870277c5a3d050 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 24 Jun 2018 19:47:14 +0200 Subject: [PATCH] Started work on new resolved TS implementation that directly uses SRM. This is a work-in-progress; ILSpy is not functional with this commit. The old code path still exists but is broken because some classes were modified for the new system. The new system is still highly incomplete (types only have fields, but no methods). --- .../ICSharpCode.Decompiler.Tests.csproj | 1 + .../PrettyTestRunner.cs | 6 + .../TestCases/Pretty/MemberTests.cs | 38 ++ .../TestCases/Pretty/MemberTests.il | 110 ++++ .../TestCases/Pretty/MemberTests.opt.il | 107 ++++ .../Pretty/MemberTests.opt.roslyn.il | 111 ++++ .../TestCases/Pretty/MemberTests.roslyn.il | 114 ++++ .../TypeSystem/TypeSystemLoaderTests.cs | 26 +- .../CSharp/RequiredNamespaceCollector.cs | 2 +- .../CSharp/Syntax/TypeSystemAstBuilder.cs | 14 +- ...odTypeParameterWithInheritedConstraints.cs | 121 ----- .../Disassembler/ReflectionDisassembler.cs | 16 +- .../ICSharpCode.Decompiler.csproj | 12 +- .../IL/Transforms/AssignVariableNames.cs | 5 +- .../Metadata/CustomAttributeDecoder.cs | 217 ++++++++ ICSharpCode.Decompiler/Metadata/Dom.cs | 37 +- .../Metadata/MetadataExtensions.cs | 28 +- .../Output/TextTokenWriter.cs | 11 +- ICSharpCode.Decompiler/SRMExtensions.cs | 89 ++-- .../Semantics/ArrayCreateResolveResult.cs | 6 +- .../TypeSystem/ApplyAttributeTypeVisitor.cs | 9 +- .../TypeSystem/DecompilerTypeSystem.cs | 60 +-- .../TypeSystem/FullTypeName.cs | 4 +- .../TypeSystem/GenericContext.cs | 63 +++ .../TypeSystem/IAssembly.cs | 9 +- .../TypeSystem/ITypeDefinition.cs | 5 - .../AbstractResolvedTypeParameter.cs | 20 +- .../Implementation/AttributeListBuilder.cs | 293 +++++++++++ .../Implementation/CustomAttribute.cs | 131 +++++ .../DefaultResolvedTypeDefinition.cs | 1 - .../DefaultResolvedTypeParameter.cs | 193 +------ .../DefaultUnresolvedAssembly.cs | 6 + .../Implementation/KnownAttributes.cs | 92 ++++ .../Implementation/MetadataField.cs | 215 ++++++++ .../Implementation/MetadataNamespace.cs | 99 ++++ .../Implementation/MetadataTypeDefinition.cs | 487 ++++++++++++++++++ .../Implementation/MetadataTypeParameter.cs | 124 +++++ .../Implementation/MetadataTypeReference.cs | 24 +- .../Implementation/SimpleCompilation.cs | 4 +- .../Implementation/SpecializedMethod.cs | 6 +- .../Implementation/TypeSpecification.cs | 4 +- .../TypeSystem/KnownTypeReference.cs | 4 +- .../TypeSystem/MetadataAssembly.cs | 290 +++++++++++ .../TypeSystem/MetadataLoader.cs | 50 +- .../TypeSystem/ModifiedType.cs | 130 +++++ .../TypeSystem/TopLevelTypeName.cs | 2 +- ICSharpCode.Decompiler/TypeSystem/TypeKind.cs | 10 +- .../TypeSystem/TypeProvider.cs | 41 +- .../TypeSystem/TypeSystemExtensions.cs | 39 +- .../TypeSystem/TypeVisitor.cs | 13 + ILSpy/Languages/CSharpLanguage.cs | 3 +- ILSpy/SearchPane.cs | 2 +- 52 files changed, 2927 insertions(+), 577 deletions(-) create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Pretty/MemberTests.cs create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Pretty/MemberTests.il create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Pretty/MemberTests.opt.il create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Pretty/MemberTests.opt.roslyn.il create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Pretty/MemberTests.roslyn.il delete mode 100644 ICSharpCode.Decompiler/CSharp/TypeSystem/MethodTypeParameterWithInheritedConstraints.cs create mode 100644 ICSharpCode.Decompiler/Metadata/CustomAttributeDecoder.cs create mode 100644 ICSharpCode.Decompiler/TypeSystem/GenericContext.cs create mode 100644 ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs create mode 100644 ICSharpCode.Decompiler/TypeSystem/Implementation/CustomAttribute.cs create mode 100644 ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs create mode 100644 ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataField.cs create mode 100644 ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataNamespace.cs create mode 100644 ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs create mode 100644 ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs create mode 100644 ICSharpCode.Decompiler/TypeSystem/MetadataAssembly.cs create mode 100644 ICSharpCode.Decompiler/TypeSystem/ModifiedType.cs diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index 7576afbeb..5d9ccba1c 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -68,6 +68,7 @@ + diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index ac71f7e9c..915616065 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -297,6 +297,12 @@ namespace ICSharpCode.Decompiler.Tests RunForLibrary(cscOptions: cscOptions); } + [Test] + public void MemberTests([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions) + { + RunForLibrary(cscOptions: cscOptions); + } + void RunForLibrary([CallerMemberName] string testName = null, AssemblerOptions asmOptions = AssemblerOptions.None, CSharpCompilerOptions cscOptions = CSharpCompilerOptions.None, DecompilerSettings decompilerSettings = null) { Run(testName, asmOptions | AssemblerOptions.Library, cscOptions | CSharpCompilerOptions.Library, decompilerSettings); diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/MemberTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/MemberTests.cs new file mode 100644 index 000000000..03834fc5f --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/MemberTests.cs @@ -0,0 +1,38 @@ +// Copyright (c) 2008 Daniel Grunwald +// +// 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. + +using System; + +namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty +{ + internal class MemberTests + { + public const int IntConstant = 1; + public const decimal DecimalConstant = 2m; + + private volatile int volatileField = 3; + private static volatile int staticVolatileField = 4; + + public void UseVolatileFields() + { + Console.WriteLine(volatileField + staticVolatileField); + volatileField++; + staticVolatileField++; + } + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/MemberTests.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/MemberTests.il new file mode 100644 index 000000000..da40d0564 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/MemberTests.il @@ -0,0 +1,110 @@ + + + + +// Metadata version: v4.0.30319 +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly MemberTests +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) + .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx + 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. + .permissionset reqmin + = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module MemberTests.dll +.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) +.imagebase 0x10000000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000001 // ILONLY + + +// =============== CLASS MEMBERS DECLARATION =================== + +.class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests + extends [mscorlib]System.Object +{ + .field public static literal int32 IntConstant = int32(0x00000001) + .field public static initonly valuetype [mscorlib]System.Decimal DecimalConstant + .custom instance void [mscorlib]System.Runtime.CompilerServices.DecimalConstantAttribute::.ctor(uint8, + uint8, + uint32, + uint32, + uint32) = ( 01 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 + 00 00 ) + .field private int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) volatileField + .field private static int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) staticVolatileField + .method public hidebysig instance void + UseVolatileFields() cil managed + { + // Code size 58 (0x3a) + .maxstack 8 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: volatile. + IL_0004: ldfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::volatileField + IL_0009: volatile. + IL_000b: ldsfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::staticVolatileField + IL_0010: add + IL_0011: call void [mscorlib]System.Console::WriteLine(int32) + IL_0016: nop + IL_0017: ldarg.0 + IL_0018: dup + IL_0019: volatile. + IL_001b: ldfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::volatileField + IL_0020: ldc.i4.1 + IL_0021: add + IL_0022: volatile. + IL_0024: stfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::volatileField + IL_0029: volatile. + IL_002b: ldsfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::staticVolatileField + IL_0030: ldc.i4.1 + IL_0031: add + IL_0032: volatile. + IL_0034: stsfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::staticVolatileField + IL_0039: ret + } // end of method MemberTests::UseVolatileFields + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 17 (0x11) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldc.i4.3 + IL_0002: volatile. + IL_0004: stfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::volatileField + IL_0009: ldarg.0 + IL_000a: call instance void [mscorlib]System.Object::.ctor() + IL_000f: nop + IL_0010: ret + } // end of method MemberTests::.ctor + + .method private hidebysig specialname rtspecialname static + void .cctor() cil managed + { + // Code size 20 (0x14) + .maxstack 8 + IL_0000: ldc.i4.2 + IL_0001: newobj instance void [mscorlib]System.Decimal::.ctor(int32) + IL_0006: stsfld valuetype [mscorlib]System.Decimal ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::DecimalConstant + IL_000b: ldc.i4.4 + IL_000c: volatile. + IL_000e: stsfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::staticVolatileField + IL_0013: ret + } // end of method MemberTests::.cctor + +} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests + + +// ============================================================= + +// *********** DISASSEMBLY COMPLETE *********************** diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/MemberTests.opt.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/MemberTests.opt.il new file mode 100644 index 000000000..ca67438f1 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/MemberTests.opt.il @@ -0,0 +1,107 @@ + + + + +// Metadata version: v4.0.30319 +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly MemberTests.opt +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) + .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx + 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. + .permissionset reqmin + = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module MemberTests.opt.dll +.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) +.imagebase 0x10000000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000001 // ILONLY + + +// =============== CLASS MEMBERS DECLARATION =================== + +.class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests + extends [mscorlib]System.Object +{ + .field public static literal int32 IntConstant = int32(0x00000001) + .field public static initonly valuetype [mscorlib]System.Decimal DecimalConstant + .custom instance void [mscorlib]System.Runtime.CompilerServices.DecimalConstantAttribute::.ctor(uint8, + uint8, + uint32, + uint32, + uint32) = ( 01 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 + 00 00 ) + .field private int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) volatileField + .field private static int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) staticVolatileField + .method public hidebysig instance void + UseVolatileFields() cil managed + { + // Code size 56 (0x38) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: volatile. + IL_0003: ldfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::volatileField + IL_0008: volatile. + IL_000a: ldsfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::staticVolatileField + IL_000f: add + IL_0010: call void [mscorlib]System.Console::WriteLine(int32) + IL_0015: ldarg.0 + IL_0016: dup + IL_0017: volatile. + IL_0019: ldfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::volatileField + IL_001e: ldc.i4.1 + IL_001f: add + IL_0020: volatile. + IL_0022: stfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::volatileField + IL_0027: volatile. + IL_0029: ldsfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::staticVolatileField + IL_002e: ldc.i4.1 + IL_002f: add + IL_0030: volatile. + IL_0032: stsfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::staticVolatileField + IL_0037: ret + } // end of method MemberTests::UseVolatileFields + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 16 (0x10) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldc.i4.3 + IL_0002: volatile. + IL_0004: stfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::volatileField + IL_0009: ldarg.0 + IL_000a: call instance void [mscorlib]System.Object::.ctor() + IL_000f: ret + } // end of method MemberTests::.ctor + + .method private hidebysig specialname rtspecialname static + void .cctor() cil managed + { + // Code size 20 (0x14) + .maxstack 8 + IL_0000: ldc.i4.2 + IL_0001: newobj instance void [mscorlib]System.Decimal::.ctor(int32) + IL_0006: stsfld valuetype [mscorlib]System.Decimal ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::DecimalConstant + IL_000b: ldc.i4.4 + IL_000c: volatile. + IL_000e: stsfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::staticVolatileField + IL_0013: ret + } // end of method MemberTests::.cctor + +} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests + + +// ============================================================= + +// *********** DISASSEMBLY COMPLETE *********************** diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/MemberTests.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/MemberTests.opt.roslyn.il new file mode 100644 index 000000000..80e7127ac --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/MemberTests.opt.roslyn.il @@ -0,0 +1,111 @@ + + + + +// Metadata version: v4.0.30319 +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly MemberTests +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) + .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx + 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. + + // --- The following custom attribute is added automatically, do not uncomment ------- + // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 ) + + .permissionset reqmin + = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module MemberTests.dll +.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) +.imagebase 0x10000000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000001 // ILONLY + + +// =============== CLASS MEMBERS DECLARATION =================== + +.class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests + extends [mscorlib]System.Object +{ + .field public static literal int32 IntConstant = int32(0x00000001) + .field public static initonly valuetype [mscorlib]System.Decimal DecimalConstant + .custom instance void [mscorlib]System.Runtime.CompilerServices.DecimalConstantAttribute::.ctor(uint8, + uint8, + uint32, + uint32, + uint32) = ( 01 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 + 00 00 ) + .field private int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) volatileField + .field private static int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) staticVolatileField + .method public hidebysig instance void + UseVolatileFields() cil managed + { + // Code size 56 (0x38) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: volatile. + IL_0003: ldfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::volatileField + IL_0008: volatile. + IL_000a: ldsfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::staticVolatileField + IL_000f: add + IL_0010: call void [mscorlib]System.Console::WriteLine(int32) + IL_0015: ldarg.0 + IL_0016: ldarg.0 + IL_0017: volatile. + IL_0019: ldfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::volatileField + IL_001e: ldc.i4.1 + IL_001f: add + IL_0020: volatile. + IL_0022: stfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::volatileField + IL_0027: volatile. + IL_0029: ldsfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::staticVolatileField + IL_002e: ldc.i4.1 + IL_002f: add + IL_0030: volatile. + IL_0032: stsfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::staticVolatileField + IL_0037: ret + } // end of method MemberTests::UseVolatileFields + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 16 (0x10) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldc.i4.3 + IL_0002: volatile. + IL_0004: stfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::volatileField + IL_0009: ldarg.0 + IL_000a: call instance void [mscorlib]System.Object::.ctor() + IL_000f: ret + } // end of method MemberTests::.ctor + + .method private hidebysig specialname rtspecialname static + void .cctor() cil managed + { + // Code size 20 (0x14) + .maxstack 8 + IL_0000: ldc.i4.2 + IL_0001: newobj instance void [mscorlib]System.Decimal::.ctor(int32) + IL_0006: stsfld valuetype [mscorlib]System.Decimal ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::DecimalConstant + IL_000b: ldc.i4.4 + IL_000c: volatile. + IL_000e: stsfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::staticVolatileField + IL_0013: ret + } // end of method MemberTests::.cctor + +} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests + + +// ============================================================= + +// *********** DISASSEMBLY COMPLETE *********************** diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/MemberTests.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/MemberTests.roslyn.il new file mode 100644 index 000000000..d718bbccd --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/MemberTests.roslyn.il @@ -0,0 +1,114 @@ + + + + +// Metadata version: v4.0.30319 +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly MemberTests +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) + .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx + 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. + + // --- The following custom attribute is added automatically, do not uncomment ------- + // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) + + .permissionset reqmin + = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module MemberTests.dll +.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) +.imagebase 0x10000000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000001 // ILONLY + + +// =============== CLASS MEMBERS DECLARATION =================== + +.class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests + extends [mscorlib]System.Object +{ + .field public static literal int32 IntConstant = int32(0x00000001) + .field public static initonly valuetype [mscorlib]System.Decimal DecimalConstant + .custom instance void [mscorlib]System.Runtime.CompilerServices.DecimalConstantAttribute::.ctor(uint8, + uint8, + uint32, + uint32, + uint32) = ( 01 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 + 00 00 ) + .field private int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) volatileField + .field private static int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) staticVolatileField + .method public hidebysig instance void + UseVolatileFields() cil managed + { + // Code size 58 (0x3a) + .maxstack 8 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: volatile. + IL_0004: ldfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::volatileField + IL_0009: volatile. + IL_000b: ldsfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::staticVolatileField + IL_0010: add + IL_0011: call void [mscorlib]System.Console::WriteLine(int32) + IL_0016: nop + IL_0017: ldarg.0 + IL_0018: ldarg.0 + IL_0019: volatile. + IL_001b: ldfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::volatileField + IL_0020: ldc.i4.1 + IL_0021: add + IL_0022: volatile. + IL_0024: stfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::volatileField + IL_0029: volatile. + IL_002b: ldsfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::staticVolatileField + IL_0030: ldc.i4.1 + IL_0031: add + IL_0032: volatile. + IL_0034: stsfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::staticVolatileField + IL_0039: ret + } // end of method MemberTests::UseVolatileFields + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 17 (0x11) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldc.i4.3 + IL_0002: volatile. + IL_0004: stfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::volatileField + IL_0009: ldarg.0 + IL_000a: call instance void [mscorlib]System.Object::.ctor() + IL_000f: nop + IL_0010: ret + } // end of method MemberTests::.ctor + + .method private hidebysig specialname rtspecialname static + void .cctor() cil managed + { + // Code size 20 (0x14) + .maxstack 8 + IL_0000: ldc.i4.2 + IL_0001: newobj instance void [mscorlib]System.Decimal::.ctor(int32) + IL_0006: stsfld valuetype [mscorlib]System.Decimal ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::DecimalConstant + IL_000b: ldc.i4.4 + IL_000c: volatile. + IL_000e: stsfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests::staticVolatileField + IL_0013: ret + } // end of method MemberTests::.cctor + +} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.MemberTests + + +// ============================================================= + +// *********** DISASSEMBLY COMPLETE *********************** diff --git a/ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemLoaderTests.cs b/ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemLoaderTests.cs index 44a6422d5..2af568fbe 100644 --- a/ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemLoaderTests.cs +++ b/ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemLoaderTests.cs @@ -18,10 +18,13 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Reflection.PortableExecutable; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; +using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; @@ -32,24 +35,29 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem [TestFixture] public class TypeSystemLoaderTests { - static readonly Lazy mscorlib = new Lazy( + static PEFile LoadAssembly(string filename) + { + return new PEFile(filename, new FileStream(filename, FileMode.Open, FileAccess.Read)); + } + + static readonly Lazy mscorlib = new Lazy( delegate { - return new MetadataLoader().LoadAssemblyFile(typeof(object).Assembly.Location); + return LoadAssembly(typeof(object).Assembly.Location); }); - static readonly Lazy systemCore = new Lazy( + static readonly Lazy systemCore = new Lazy( delegate { - return new MetadataLoader().LoadAssemblyFile(typeof(System.Linq.Enumerable).Assembly.Location); + return LoadAssembly(typeof(System.Linq.Enumerable).Assembly.Location); }); - static readonly Lazy testAssembly = new Lazy( + static readonly Lazy testAssembly = new Lazy( delegate { - return new MetadataLoader { IncludeInternalMembers = true }.LoadAssemblyFile(typeof(SimplePublicClass).Assembly.Location); + return LoadAssembly(typeof(SimplePublicClass).Assembly.Location); }); - public static IUnresolvedAssembly Mscorlib { get { return mscorlib.Value; } } - public static IUnresolvedAssembly SystemCore { get { return systemCore.Value; } } - public static IUnresolvedAssembly TestAssembly { get { return testAssembly.Value; } } + public static PEFile Mscorlib { get { return mscorlib.Value; } } + public static PEFile SystemCore { get { return systemCore.Value; } } + public static PEFile TestAssembly { get { return testAssembly.Value; } } [OneTimeSetUp] public void FixtureSetUp() diff --git a/ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs b/ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs index 35d6c3af1..8cf6daaed 100644 --- a/ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs +++ b/ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs @@ -21,7 +21,7 @@ namespace ICSharpCode.Decompiler.CSharp { public static void CollectNamespaces(DecompilerTypeSystem typeSystem, HashSet namespaces) { - foreach (var type in typeSystem.MainAssembly.GetAllTypeDefinitions()) { + foreach (var type in typeSystem.MainAssembly.TypeDefinitions) { CollectNamespaces(type, typeSystem, namespaces); } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index 0ac002e06..3addb4931 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -437,11 +437,15 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax foreach (ResolveResult arg in attribute.PositionalArguments) { attr.Arguments.Add(ConvertConstantValue(arg)); } - foreach (var pair in attribute.NamedArguments) { - NamedExpression namedArgument = new NamedExpression(pair.Key.Name, ConvertConstantValue(pair.Value)); - if (AddResolveResultAnnotations) - namedArgument.AddAnnotation(new MemberResolveResult(new InitializedObjectResolveResult(attribute.AttributeType), pair.Key)); - attr.Arguments.Add(namedArgument); + if (attribute.NamedArguments.Count > 0) { + InitializedObjectResolveResult targetResult = new InitializedObjectResolveResult(attribute.AttributeType); + foreach (var pair in attribute.NamedArguments) { + NamedExpression namedArgument = new NamedExpression(pair.Key.Name, ConvertConstantValue(pair.Value)); + if (AddResolveResultAnnotations) { + namedArgument.AddAnnotation(new MemberResolveResult(targetResult, pair.Key)); + } + attr.Arguments.Add(namedArgument); + } } return attr; } diff --git a/ICSharpCode.Decompiler/CSharp/TypeSystem/MethodTypeParameterWithInheritedConstraints.cs b/ICSharpCode.Decompiler/CSharp/TypeSystem/MethodTypeParameterWithInheritedConstraints.cs deleted file mode 100644 index 82106ca33..000000000 --- a/ICSharpCode.Decompiler/CSharp/TypeSystem/MethodTypeParameterWithInheritedConstraints.cs +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team -// -// 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. - -using System; -using System.Collections.Generic; -using System.Linq; -using ICSharpCode.Decompiler.TypeSystem; -using ICSharpCode.Decompiler.TypeSystem.Implementation; -using ICSharpCode.Decompiler.Util; - -namespace ICSharpCode.Decompiler.CSharp.TypeSystem -{ - [Serializable] - public sealed class MethodTypeParameterWithInheritedConstraints : DefaultUnresolvedTypeParameter - { - public MethodTypeParameterWithInheritedConstraints(int index, string name) - : base(SymbolKind.Method, index, name) - { - } - - static ITypeParameter ResolveBaseTypeParameter(IMethod parentMethod, int index) - { - IMethod baseMethod = null; - if (parentMethod.IsOverride) { - foreach (IMethod m in InheritanceHelper.GetBaseMembers(parentMethod, false).OfType()) { - if (!m.IsOverride) { - baseMethod = m; - break; - } - } - } else if (parentMethod.IsExplicitInterfaceImplementation && parentMethod.ImplementedInterfaceMembers.Count == 1) { - baseMethod = parentMethod.ImplementedInterfaceMembers[0] as IMethod; - } - if (baseMethod != null && index < baseMethod.TypeParameters.Count) - return baseMethod.TypeParameters[index]; - else - return null; - } - - public override ITypeParameter CreateResolvedTypeParameter(ITypeResolveContext context) - { - if (context.CurrentMember is IMethod) { - return new ResolvedMethodTypeParameterWithInheritedConstraints(this, context); - } else { - return base.CreateResolvedTypeParameter(context); - } - } - - sealed class ResolvedMethodTypeParameterWithInheritedConstraints : AbstractTypeParameter - { - volatile ITypeParameter baseTypeParameter; - - public ResolvedMethodTypeParameterWithInheritedConstraints(MethodTypeParameterWithInheritedConstraints unresolved, ITypeResolveContext context) - : base(context.CurrentMember, unresolved.Index, unresolved.Name, unresolved.Variance, - unresolved.Attributes.CreateResolvedAttributes(context)) - { - } - - ITypeParameter GetBaseTypeParameter() - { - ITypeParameter baseTP = this.baseTypeParameter; - if (baseTP == null) { - // ResolveBaseTypeParameter() is idempotent, so this is thread-safe. - this.baseTypeParameter = baseTP = ResolveBaseTypeParameter((IMethod)this.Owner, this.Index); - } - return baseTP; - } - - public override bool HasValueTypeConstraint { - get { - ITypeParameter baseTP = GetBaseTypeParameter(); - return baseTP != null ? baseTP.HasValueTypeConstraint : false; - } - } - - public override bool HasReferenceTypeConstraint { - get { - ITypeParameter baseTP = GetBaseTypeParameter(); - return baseTP != null ? baseTP.HasReferenceTypeConstraint : false; - } - } - - public override bool HasDefaultConstructorConstraint { - get { - ITypeParameter baseTP = GetBaseTypeParameter(); - return baseTP != null ? baseTP.HasDefaultConstructorConstraint : false; - } - } - - public override IEnumerable DirectBaseTypes { - get { - ITypeParameter baseTP = GetBaseTypeParameter(); - if (baseTP != null) { - // Substitute occurrences of the base method's type parameters in the constraints - // with the type parameters from the - IMethod owner = (IMethod)this.Owner; - var substitution = new TypeParameterSubstitution(null, owner.TypeParameters); - return baseTP.DirectBaseTypes.Select(t => t.AcceptVisitor(substitution)); - } else { - return EmptyList.Instance; - } - } - } - } - } -} diff --git a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs index 397f31fdb..7bad20aca 100644 --- a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs @@ -285,6 +285,9 @@ namespace ICSharpCode.Decompiler.Disassembler output.WriteLine(); } + foreach (var p in methodDefinition.GetGenericParameters()) { + WriteGenericParameterAttributes(module, p); + } foreach (var p in methodDefinition.GetParameters()) { WriteParameterAttributes(module, p); } @@ -940,6 +943,17 @@ namespace ICSharpCode.Decompiler.Disassembler } } + void WriteGenericParameterAttributes(PEFile module, GenericParameterHandle handle) + { + var metadata = module.Metadata; + var p = metadata.GetGenericParameter(handle); + if (p.GetCustomAttributes().Count == 0) + return; + output.Write(".param type {0}", metadata.GetString(p.Name)); + output.WriteLine(); + WriteAttributes(module, p.GetCustomAttributes()); + } + void WriteParameterAttributes(PEFile module, ParameterHandle handle) { var metadata = module.Metadata; @@ -1576,7 +1590,7 @@ namespace ICSharpCode.Decompiler.Disassembler public void WriteModuleContents(PEFile module) { - foreach (var handle in module.TypeDefinitions) { + foreach (var handle in module.TopLevelTypeDefinitions) { DisassembleType(handle); output.WriteLine(); } diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 1bc1d4a49..b4afb1b72 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -240,7 +240,6 @@ - @@ -280,6 +279,7 @@ + @@ -339,8 +339,18 @@ + + + + + + + + + + diff --git a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs index e4db6cf65..38ffea9fb 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs @@ -359,10 +359,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms static string GetNameByType(IType type) { - var git = type as ParameterizedType; - if (git != null && git.FullName == "System.Nullable`1" && git.TypeArguments.Count == 1) { - type = git.TypeArguments[0]; - } + type = NullableType.GetUnderlyingType(type); string name; if (type is ArrayType) { diff --git a/ICSharpCode.Decompiler/Metadata/CustomAttributeDecoder.cs b/ICSharpCode.Decompiler/Metadata/CustomAttributeDecoder.cs new file mode 100644 index 000000000..2e7e7fe58 --- /dev/null +++ b/ICSharpCode.Decompiler/Metadata/CustomAttributeDecoder.cs @@ -0,0 +1,217 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Reflection.Metadata; + +namespace ICSharpCode.Decompiler.Metadata +{ + /// + /// Decodes custom attribute blobs. + /// + internal readonly struct CustomAttributeDecoder + { + // This is a stripped-down copy of SRM's internal CustomAttributeDecoder. + // We need it to decode security declarations. + + private readonly ICustomAttributeTypeProvider _provider; + private readonly MetadataReader _reader; + + public CustomAttributeDecoder(ICustomAttributeTypeProvider provider, MetadataReader reader) + { + _reader = reader; + _provider = provider; + } + + public ImmutableArray> DecodeNamedArguments(ref BlobReader valueReader, int count) + { + var arguments = ImmutableArray.CreateBuilder>(count); + for (int i = 0; i < count; i++) { + CustomAttributeNamedArgumentKind kind = (CustomAttributeNamedArgumentKind)valueReader.ReadSerializationTypeCode(); + if (kind != CustomAttributeNamedArgumentKind.Field && kind != CustomAttributeNamedArgumentKind.Property) { + throw new BadImageFormatException(); + } + + ArgumentTypeInfo info = DecodeNamedArgumentType(ref valueReader); + string name = valueReader.ReadSerializedString(); + CustomAttributeTypedArgument argument = DecodeArgument(ref valueReader, info); + arguments.Add(new CustomAttributeNamedArgument(name, kind, argument.Type, argument.Value)); + } + + return arguments.MoveToImmutable(); + } + + private struct ArgumentTypeInfo + { + public TType Type; + public TType ElementType; + public SerializationTypeCode TypeCode; + public SerializationTypeCode ElementTypeCode; + } + + private ArgumentTypeInfo DecodeNamedArgumentType(ref BlobReader valueReader, bool isElementType = false) + { + var info = new ArgumentTypeInfo { + TypeCode = valueReader.ReadSerializationTypeCode(), + }; + + switch (info.TypeCode) { + case SerializationTypeCode.Boolean: + case SerializationTypeCode.Byte: + case SerializationTypeCode.Char: + case SerializationTypeCode.Double: + case SerializationTypeCode.Int16: + case SerializationTypeCode.Int32: + case SerializationTypeCode.Int64: + case SerializationTypeCode.SByte: + case SerializationTypeCode.Single: + case SerializationTypeCode.String: + case SerializationTypeCode.UInt16: + case SerializationTypeCode.UInt32: + case SerializationTypeCode.UInt64: + info.Type = _provider.GetPrimitiveType((PrimitiveTypeCode)info.TypeCode); + break; + + case SerializationTypeCode.Type: + info.Type = _provider.GetSystemType(); + break; + + case SerializationTypeCode.TaggedObject: + info.Type = _provider.GetPrimitiveType(PrimitiveTypeCode.Object); + break; + + case SerializationTypeCode.SZArray: + if (isElementType) { + // jagged arrays are not allowed. + throw new BadImageFormatException(); + } + + var elementInfo = DecodeNamedArgumentType(ref valueReader, isElementType: true); + info.ElementType = elementInfo.Type; + info.ElementTypeCode = elementInfo.TypeCode; + info.Type = _provider.GetSZArrayType(info.ElementType); + break; + + case SerializationTypeCode.Enum: + string typeName = valueReader.ReadSerializedString(); + info.Type = _provider.GetTypeFromSerializedName(typeName); + info.TypeCode = (SerializationTypeCode)_provider.GetUnderlyingEnumType(info.Type); + break; + + default: + throw new BadImageFormatException(); + } + + return info; + } + + private CustomAttributeTypedArgument DecodeArgument(ref BlobReader valueReader, ArgumentTypeInfo info) + { + if (info.TypeCode == SerializationTypeCode.TaggedObject) { + info = DecodeNamedArgumentType(ref valueReader); + } + + // PERF_TODO: https://github.com/dotnet/corefx/issues/6533 + // Cache /reuse common arguments to avoid boxing (small integers, true, false). + object value; + switch (info.TypeCode) { + case SerializationTypeCode.Boolean: + value = valueReader.ReadBoolean(); + break; + + case SerializationTypeCode.Byte: + value = valueReader.ReadByte(); + break; + + case SerializationTypeCode.Char: + value = valueReader.ReadChar(); + break; + + case SerializationTypeCode.Double: + value = valueReader.ReadDouble(); + break; + + case SerializationTypeCode.Int16: + value = valueReader.ReadInt16(); + break; + + case SerializationTypeCode.Int32: + value = valueReader.ReadInt32(); + break; + + case SerializationTypeCode.Int64: + value = valueReader.ReadInt64(); + break; + + case SerializationTypeCode.SByte: + value = valueReader.ReadSByte(); + break; + + case SerializationTypeCode.Single: + value = valueReader.ReadSingle(); + break; + + case SerializationTypeCode.UInt16: + value = valueReader.ReadUInt16(); + break; + + case SerializationTypeCode.UInt32: + value = valueReader.ReadUInt32(); + break; + + case SerializationTypeCode.UInt64: + value = valueReader.ReadUInt64(); + break; + + case SerializationTypeCode.String: + value = valueReader.ReadSerializedString(); + break; + + case SerializationTypeCode.Type: + string typeName = valueReader.ReadSerializedString(); + value = _provider.GetTypeFromSerializedName(typeName); + break; + + case SerializationTypeCode.SZArray: + value = DecodeArrayArgument(ref valueReader, info); + break; + + default: + throw new BadImageFormatException(); + } + + return new CustomAttributeTypedArgument(info.Type, value); + } + + private ImmutableArray>? DecodeArrayArgument(ref BlobReader blobReader, ArgumentTypeInfo info) + { + int count = blobReader.ReadInt32(); + if (count == -1) { + return null; + } + + if (count == 0) { + return ImmutableArray>.Empty; + } + + if (count < 0) { + throw new BadImageFormatException(); + } + + var elementInfo = new ArgumentTypeInfo { + Type = info.ElementType, + TypeCode = info.ElementTypeCode, + }; + + var array = ImmutableArray.CreateBuilder>(count); + + for (int i = 0; i < count; i++) { + array.Add(DecodeArgument(ref blobReader, elementInfo)); + } + + return array.MoveToImmutable(); + } + } +} \ No newline at end of file diff --git a/ICSharpCode.Decompiler/Metadata/Dom.cs b/ICSharpCode.Decompiler/Metadata/Dom.cs index d4eb11753..7f5c5ef7d 100644 --- a/ICSharpCode.Decompiler/Metadata/Dom.cs +++ b/ICSharpCode.Decompiler/Metadata/Dom.cs @@ -92,7 +92,7 @@ namespace ICSharpCode.Decompiler.Metadata Net_4_0 } - public class PEFile : IDisposable + public class PEFile : IDisposable, TypeSystem.IAssemblyReference { public string FileName { get; } public PEReader Reader { get; } @@ -150,7 +150,7 @@ namespace ICSharpCode.Decompiler.Metadata public ImmutableArray AssemblyReferences => Metadata.AssemblyReferences.Select(r => new AssemblyReference(this, r)).ToImmutableArray(); public ImmutableArray ModuleReferences => Metadata.GetModuleReferences().ToImmutableArray(); - public ImmutableArray TypeDefinitions => Metadata.GetTopLevelTypeDefinitions().Select(t => new TypeDefinition(this, t)).ToImmutableArray(); + public ImmutableArray TopLevelTypeDefinitions => Metadata.GetTopLevelTypeDefinitions().Select(t => new TypeDefinition(this, t)).ToImmutableArray(); public ImmutableArray Resources => GetResources().ToImmutableArray(); IEnumerable GetResources() @@ -165,6 +165,39 @@ namespace ICSharpCode.Decompiler.Metadata { Reader.Dispose(); } + + Dictionary typeLookup; + + /// + /// Finds the top-level-type with the specified name. + /// + public TypeDefinitionHandle GetTypeDefinition(TopLevelTypeName typeName) + { + var lookup = LazyInit.VolatileRead(ref typeLookup); + if (lookup == null) { + lookup = new Dictionary(); + foreach (var handle in Metadata.TypeDefinitions) { + var td = Metadata.GetTypeDefinition(handle); + if (!td.GetDeclaringType().IsNil) { + continue; // nested type + } + var nsHandle = td.Namespace; + string ns = nsHandle.IsNil ? string.Empty : Metadata.GetString(nsHandle); + string name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(Metadata.GetString(td.Name), out int typeParameterCount); + lookup[new TopLevelTypeName(ns, name, typeParameterCount)] = handle; + } + lookup = LazyInit.GetOrSet(ref typeLookup, lookup); + } + if (lookup.TryGetValue(typeName, out var resultHandle)) + return resultHandle; + else + return default; + } + + IAssembly TypeSystem.IAssemblyReference.Resolve(ITypeResolveContext context) + { + return new MetadataAssembly(context.Compilation, this, TypeSystemOptions.Default); + } } public enum ResourceType diff --git a/ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs b/ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs index a178c1b77..f4ec65fce 100644 --- a/ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs +++ b/ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs @@ -64,29 +64,9 @@ namespace ICSharpCode.Decompiler.Metadata public static IEnumerable GetTopLevelTypeDefinitions(this MetadataReader reader) { - unsafe HashSet GetNestedTypes() - { - byte* startPointer = reader.MetadataPointer; - int offset = reader.GetTableMetadataOffset(TableIndex.NestedClass); - int rowSize = reader.GetTableRowSize(TableIndex.NestedClass); - int rowCount = reader.GetTableRowCount(TableIndex.NestedClass); - var typeDefSize = reader.GetReferenceSize(TableIndex.TypeDef); - - var set = new HashSet(); - - for (int row = 0; row < rowCount; row++) { - byte* ptr = startPointer + offset + rowSize * row; - uint currentTypeRow = typeDefSize == 2 ? *(ushort*)ptr : *(uint*)ptr; - set.Add(currentTypeRow); - } - - return set; - } - - HashSet nestedTypes = GetNestedTypes(); - foreach (var handle in reader.TypeDefinitions) { - if (!nestedTypes.Contains((uint)reader.GetRowNumber(handle))) + var td = reader.GetTypeDefinition(handle); + if (td.GetDeclaringType().IsNil) yield return handle; } } @@ -171,7 +151,7 @@ namespace ICSharpCode.Decompiler.Metadata } internal static readonly TypeProvider minimalCorlibTypeProvider = - new TypeProvider(MinimalCorlib.Instance.CreateCompilation().MainAssembly); + new TypeProvider(MinimalCorlib.Instance.CreateCompilation()); /// /// An attribute type provider that can be used to decode attribute signatures @@ -187,7 +167,7 @@ namespace ICSharpCode.Decompiler.Metadata foreach (var h in td.GetCustomAttributes()) { var ca = reader.GetCustomAttribute(h); - if (ca.GetAttributeType(reader).IsTopLevelType(reader, "System.Reflection", "DefaultMemberAttribute")) { + if (ca.IsKnownAttribute(reader, KnownAttribute.DefaultMember)) { var decodedValues = ca.DecodeValue(minimalCorlibTypeProvider); if (decodedValues.FixedArguments.Length == 1 && decodedValues.FixedArguments[0].Value is string value) { defaultMemberAttribute = h; diff --git a/ICSharpCode.Decompiler/Output/TextTokenWriter.cs b/ICSharpCode.Decompiler/Output/TextTokenWriter.cs index 68ec3cb33..c9cf25359 100644 --- a/ICSharpCode.Decompiler/Output/TextTokenWriter.cs +++ b/ICSharpCode.Decompiler/Output/TextTokenWriter.cs @@ -25,6 +25,7 @@ using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; +using SRM = System.Reflection.Metadata; namespace ICSharpCode.Decompiler { @@ -106,15 +107,15 @@ namespace ICSharpCode.Decompiler var definition = type.GetDefinition(); if (definition == null) return null; - return new TypeDefinition(typeSystem.GetModuleDefinition(definition.ParentAssembly), (System.Reflection.Metadata.TypeDefinitionHandle)definition.MetadataToken); + return new TypeDefinition(typeSystem.GetModuleDefinition(definition.ParentAssembly), (SRM.TypeDefinitionHandle)definition.MetadataToken); case IMethod method: - return new MethodDefinition(typeSystem.GetModuleDefinition(method.ParentAssembly), (System.Reflection.Metadata.MethodDefinitionHandle)method.MetadataToken); + return new MethodDefinition(typeSystem.GetModuleDefinition(method.ParentAssembly), (SRM.MethodDefinitionHandle)method.MetadataToken); case IProperty property: - return new PropertyDefinition(typeSystem.GetModuleDefinition(property.ParentAssembly), (System.Reflection.Metadata.PropertyDefinitionHandle)property.MetadataToken); + return new PropertyDefinition(typeSystem.GetModuleDefinition(property.ParentAssembly), (SRM.PropertyDefinitionHandle)property.MetadataToken); case IEvent @event: - return new EventDefinition(typeSystem.GetModuleDefinition(@event.ParentAssembly), (System.Reflection.Metadata.EventDefinitionHandle)@event.MetadataToken); + return new EventDefinition(typeSystem.GetModuleDefinition(@event.ParentAssembly), (SRM.EventDefinitionHandle)@event.MetadataToken); case IField field: - return new FieldDefinition(typeSystem.GetModuleDefinition(field.ParentAssembly), (System.Reflection.Metadata.FieldDefinitionHandle)field.MetadataToken); + return new FieldDefinition(typeSystem.GetModuleDefinition(field.ParentAssembly), (SRM.FieldDefinitionHandle)field.MetadataToken); default: return null; } diff --git a/ICSharpCode.Decompiler/SRMExtensions.cs b/ICSharpCode.Decompiler/SRMExtensions.cs index 11992b89a..ca301aeff 100644 --- a/ICSharpCode.Decompiler/SRMExtensions.cs +++ b/ICSharpCode.Decompiler/SRMExtensions.cs @@ -4,8 +4,10 @@ using System.Collections.Immutable; using System.Linq; using System.Reflection; using System.Reflection.Metadata; +using SRM = System.Reflection.Metadata; using System.Reflection.PortableExecutable; using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler @@ -29,13 +31,15 @@ namespace ICSharpCode.Decompiler public static bool IsValueType(this TypeDefinition typeDefinition, MetadataReader reader) { - if (typeDefinition.BaseType.IsNil) + var baseType = typeDefinition.BaseType; + if (baseType.IsNil) return false; - var baseType = typeDefinition.BaseType.GetFullTypeName(reader).ToString(); - if (baseType == "System.Enum") + if (baseType.IsKnownType(reader, KnownTypeCode.Enum)) return true; - var thisType = typeDefinition.GetFullTypeName(reader).ToString(); - return baseType == "System.ValueType" && thisType != "System.Enum"; + if (!baseType.IsKnownType(reader, KnownTypeCode.ValueType)) + return false; + var thisType = typeDefinition.GetFullTypeName(reader); + return !thisType.IsKnownType(KnownTypeCode.Enum); } public static bool IsEnum(this TypeDefinitionHandle handle, MetadataReader reader) @@ -47,7 +51,7 @@ namespace ICSharpCode.Decompiler { if (typeDefinition.BaseType.IsNil) return false; - return typeDefinition.BaseType.GetFullTypeName(reader).ToString() == "System.Enum"; + return typeDefinition.BaseType.IsKnownType(reader, KnownTypeCode.Enum); } public static bool IsEnum(this TypeDefinitionHandle handle, MetadataReader reader, out PrimitiveTypeCode underlyingType) @@ -60,7 +64,7 @@ namespace ICSharpCode.Decompiler underlyingType = 0; if (typeDefinition.BaseType.IsNil) return false; - if (typeDefinition.BaseType.GetFullTypeName(reader).ToString() != "System.Enum") + if (!typeDefinition.BaseType.IsKnownType(reader, KnownTypeCode.Enum)) return false; var field = reader.GetFieldDefinition(typeDefinition.GetFields().First()); var blob = reader.GetBlobReader(field.Signature); @@ -78,13 +82,13 @@ namespace ICSharpCode.Decompiler public static bool IsDelegate(this TypeDefinition typeDefinition, MetadataReader reader) { var baseType = typeDefinition.BaseType; - return !baseType.IsNil && baseType.GetFullTypeName(reader).ToString() == typeof(MulticastDelegate).FullName; + return !baseType.IsNil && baseType.IsKnownType(reader, KnownTypeCode.MulticastDelegate); } public static bool IsExtensionMethod(this MethodDefinition methodDefinition, MetadataReader reader) { if (methodDefinition.HasFlag(MethodAttributes.Static)) { - return methodDefinition.GetCustomAttributes().HasAttributeOfType(reader); + return methodDefinition.GetCustomAttributes().HasKnownAttribute(reader, KnownAttribute.Extension); } return false; } @@ -92,22 +96,16 @@ namespace ICSharpCode.Decompiler public static bool HasBody(this MethodDefinitionHandle handle, MetadataReader reader) { var methodDefinition = reader.GetMethodDefinition(handle); - return (methodDefinition.Attributes & MethodAttributes.Abstract) == 0 && - (methodDefinition.Attributes & MethodAttributes.PinvokeImpl) == 0 && - (methodDefinition.ImplAttributes & MethodImplAttributes.InternalCall) == 0 && - (methodDefinition.ImplAttributes & MethodImplAttributes.Native) == 0 && - (methodDefinition.ImplAttributes & MethodImplAttributes.Unmanaged) == 0 && - (methodDefinition.ImplAttributes & MethodImplAttributes.Runtime) == 0; + return methodDefinition.HasBody(); } public static bool HasBody(this MethodDefinition methodDefinition) { - return (methodDefinition.Attributes & MethodAttributes.Abstract) == 0 && - (methodDefinition.Attributes & MethodAttributes.PinvokeImpl) == 0 && - (methodDefinition.ImplAttributes & MethodImplAttributes.InternalCall) == 0 && - (methodDefinition.ImplAttributes & MethodImplAttributes.Native) == 0 && - (methodDefinition.ImplAttributes & MethodImplAttributes.Unmanaged) == 0 && - (methodDefinition.ImplAttributes & MethodImplAttributes.Runtime) == 0; + const MethodAttributes noBodyAttrs = MethodAttributes.Abstract | MethodAttributes.PinvokeImpl; + const MethodImplAttributes noBodyImplAttrs = MethodImplAttributes.InternalCall + | MethodImplAttributes.Native | MethodImplAttributes.Unmanaged | MethodImplAttributes.Runtime; + return (methodDefinition.Attributes & noBodyAttrs) == 0 && + (methodDefinition.ImplAttributes & noBodyImplAttrs) == 0; } public static int GetCodeSize(this MethodBodyBlock body) @@ -183,14 +181,14 @@ namespace ICSharpCode.Decompiler } } - public static bool IsTopLevelType(this EntityHandle handle, MetadataReader reader, string namespaceName, string name, int typeParameterCount = 0) + public static bool IsKnownType(this EntityHandle handle, MetadataReader reader, KnownTypeCode knownType) { - return GetFullTypeName(handle, reader) == new TopLevelTypeName(namespaceName, name, typeParameterCount); + return GetFullTypeName(handle, reader) == KnownTypeReference.Get(knownType).TypeName; } - - public static bool IsAttributeType(this CustomAttribute attr, MetadataReader reader, string namespaceName, string name) + + internal static bool IsKnownType(this EntityHandle handle, MetadataReader reader, KnownAttribute knownType) { - return attr.GetAttributeType(reader).IsTopLevelType(reader, namespaceName, name); + return GetFullTypeName(handle, reader) == knownType.GetTypeName(); } public static FullTypeName GetFullTypeName(this TypeSpecificationHandle handle, MetadataReader reader) @@ -296,7 +294,7 @@ namespace ICSharpCode.Decompiler public static bool IsCompilerGenerated(this MethodDefinition method, MetadataReader metadata) { - return method.GetCustomAttributes().HasCompilerGeneratedAttribute(metadata); + return method.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.CompilerGenerated); } public static bool IsCompilerGenerated(this FieldDefinitionHandle handle, MetadataReader metadata) @@ -306,7 +304,7 @@ namespace ICSharpCode.Decompiler public static bool IsCompilerGenerated(this FieldDefinition field, MetadataReader metadata) { - return field.GetCustomAttributes().HasCompilerGeneratedAttribute(metadata); + return field.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.CompilerGenerated); } public static bool IsCompilerGenerated(this TypeDefinitionHandle handle, MetadataReader metadata) @@ -316,7 +314,7 @@ namespace ICSharpCode.Decompiler public static bool IsCompilerGenerated(this TypeDefinition type, MetadataReader metadata) { - return type.GetCustomAttributes().HasCompilerGeneratedAttribute(metadata); + return type.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.CompilerGenerated); } #endregion @@ -325,7 +323,7 @@ namespace ICSharpCode.Decompiler /// /// Gets the type of the attribute. /// - public static EntityHandle GetAttributeType(this CustomAttribute attribute, MetadataReader reader) + public static EntityHandle GetAttributeType(this SRM.CustomAttribute attribute, MetadataReader reader) { switch (attribute.Constructor.Kind) { case HandleKind.MethodDefinition: @@ -338,41 +336,24 @@ namespace ICSharpCode.Decompiler throw new NotSupportedException(); } } - - public static bool HasCompilerGeneratedAttribute(this CustomAttributeHandleCollection customAttributes, MetadataReader metadata) - { - return customAttributes.HasAttributeOfType(metadata); - } - - public static bool HasParamArrayAttribute(this CustomAttributeHandleCollection customAttributes, MetadataReader metadata) + + internal static bool HasKnownAttribute(this CustomAttributeHandleCollection customAttributes, MetadataReader metadata, KnownAttribute type) { - return customAttributes.HasAttributeOfType(metadata); - } - - public static bool HasDefaultMemberAttribute(this CustomAttributeHandleCollection customAttributes, MetadataReader metadata) - { - return customAttributes.HasAttributeOfType(metadata); - } - - public static bool HasAttributeOfType(this CustomAttributeHandleCollection customAttributes, MetadataReader metadata, Type type) - { - var typeName = type.FullName; foreach (var handle in customAttributes) { var customAttribute = metadata.GetCustomAttribute(handle); - var attributeTypeName = customAttribute.GetAttributeType(metadata).GetFullTypeName(metadata).ToString(); - if (typeName == attributeTypeName) + if (customAttribute.IsKnownAttribute(metadata, type)) return true; } return false; } - - public static bool HasAttributeOfType(this CustomAttributeHandleCollection customAttributes, MetadataReader metadata) where TAttribute : Attribute + + internal static bool IsKnownAttribute(this SRM.CustomAttribute attr, MetadataReader metadata, KnownAttribute attrType) { - return HasAttributeOfType(customAttributes, metadata, typeof(TAttribute)); + return attr.GetAttributeType(metadata).IsKnownType(metadata, attrType); } #endregion - public static unsafe BlobReader GetInitialValue(this FieldDefinition field, PEReader pefile) + public static unsafe SRM.BlobReader GetInitialValue(this FieldDefinition field, PEReader pefile) { if (!field.HasFlag(FieldAttributes.HasFieldRVA) || field.GetRelativeVirtualAddress() == 0) return default; diff --git a/ICSharpCode.Decompiler/Semantics/ArrayCreateResolveResult.cs b/ICSharpCode.Decompiler/Semantics/ArrayCreateResolveResult.cs index 4f62989c0..d30d46524 100644 --- a/ICSharpCode.Decompiler/Semantics/ArrayCreateResolveResult.cs +++ b/ICSharpCode.Decompiler/Semantics/ArrayCreateResolveResult.cs @@ -31,15 +31,15 @@ namespace ICSharpCode.Decompiler.Semantics /// /// Gets the size arguments. /// - public readonly IList SizeArguments; + public readonly IReadOnlyList SizeArguments; /// /// Gets the initializer elements. /// This field may be null if no initializer was specified. /// - public readonly IList InitializerElements; + public readonly IReadOnlyList InitializerElements; - public ArrayCreateResolveResult(IType arrayType, IList sizeArguments, IList initializerElements) + public ArrayCreateResolveResult(IType arrayType, IReadOnlyList sizeArguments, IReadOnlyList initializerElements) : base(arrayType) { if (sizeArguments == null) diff --git a/ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs b/ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs index fee0d5c0d..fa9c5f2ac 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs @@ -20,6 +20,7 @@ using System; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; +using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; using SRM = System.Reflection.Metadata; @@ -40,16 +41,16 @@ namespace ICSharpCode.Decompiler.TypeSystem if ((options & (TypeSystemOptions.Dynamic | TypeSystemOptions.Tuple)) == TypeSystemOptions.None) { return inputType; } + bool useDynamicType = (options & TypeSystemOptions.Dynamic) != 0; + bool useTupleTypes = (options & TypeSystemOptions.Tuple) != 0; bool hasDynamicAttribute = false; bool[] dynamicAttributeData = null; - bool useTupleTypes = (options & TypeSystemOptions.Tuple) != 0; string[] tupleElementNames = null; if (attributes != null) { foreach (var attrHandle in attributes.Value) { var attr = metadata.GetCustomAttribute(attrHandle); var attrType = attr.GetAttributeType(metadata); - if ((options & TypeSystemOptions.Dynamic) != 0 - && attrType.IsTopLevelType(metadata, "System.Runtime.CompilerServices", "DynamicAttribute")) { + if (useDynamicType && attrType.IsKnownType(metadata, KnownAttribute.Dynamic)) { hasDynamicAttribute = true; var ctor = attr.DecodeValue(Metadata.MetadataExtensions.minimalCorlibTypeProvider); if (ctor.FixedArguments.Length == 1) { @@ -59,7 +60,7 @@ namespace ICSharpCode.Decompiler.TypeSystem dynamicAttributeData = values.SelectArray(v => (bool)v.Value); } } - } else if (useTupleTypes && attrType.IsTopLevelType(metadata, "System.Runtime.CompilerServices", "TupleElementNamesAttribute")) { + } else if (useTupleTypes && attrType.IsKnownType(metadata, KnownAttribute.TupleElementNames)) { var ctor = attr.DecodeValue(Metadata.MetadataExtensions.minimalCorlibTypeProvider); if (ctor.FixedArguments.Length == 1) { var arg = ctor.FixedArguments[0]; diff --git a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs index 35a04bad0..7e3984ed3 100644 --- a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs +++ b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs @@ -75,6 +75,7 @@ namespace ICSharpCode.Decompiler.TypeSystem readonly ICompilation compilation; readonly ITypeResolveContext context; readonly TypeSystemOptions typeSystemOptions; + readonly MetadataAssembly mainAssembly; Dictionary fieldLookupCache = new Dictionary(); Dictionary propertyLookupCache = new Dictionary(); @@ -141,8 +142,8 @@ namespace ICSharpCode.Decompiler.TypeSystem get { return compilation; } } - public IAssembly MainAssembly { - get { return compilation.MainAssembly; } + public MetadataAssembly MainAssembly { + get { return mainAssembly; } } public Metadata.PEFile ModuleDefinition { @@ -153,11 +154,10 @@ namespace ICSharpCode.Decompiler.TypeSystem public Metadata.PEFile GetModuleDefinition(IAssembly assembly) { - if (assembly == MainAssembly) - return ModuleDefinition; - if (!moduleLookup.TryGetValue(assembly.UnresolvedAssembly, out var file)) - return null; - return file; + if (assembly is MetadataAssembly asm) { + return asm.PEFile; + } + return null; } public IMember ResolveAsMember(SRM.EntityHandle memberReference) @@ -193,7 +193,8 @@ namespace ICSharpCode.Decompiler.TypeSystem return MetadataTypeReference.Resolve( typeReference, moduleDefinition.Metadata, - context, + mainAssembly.TypeProvider, + new GenericContext(), typeSystemOptions ); } @@ -204,7 +205,8 @@ namespace ICSharpCode.Decompiler.TypeSystem return MetadataTypeReference.Resolve( declaringTypeReference, moduleDefinition.Metadata, - context, + mainAssembly.TypeProvider, + new GenericContext(context), typeSystemOptions & ~(TypeSystemOptions.Dynamic | TypeSystemOptions.Tuple) ); } @@ -216,8 +218,8 @@ namespace ICSharpCode.Decompiler.TypeSystem if (standaloneSignature.GetKind() != SRM.StandaloneSignatureKind.Method) throw new InvalidOperationException("Expected Method signature"); return standaloneSignature.DecodeMethodSignature( - new TypeProvider(compilation.MainAssembly), - context + mainAssembly.TypeProvider, + new GenericContext(context) ); } @@ -227,8 +229,8 @@ namespace ICSharpCode.Decompiler.TypeSystem if (standaloneSignature.GetKind() != SRM.StandaloneSignatureKind.LocalVariables) throw new InvalidOperationException("Expected Local signature"); return standaloneSignature.DecodeLocalSignature( - new TypeProvider(compilation.MainAssembly), - context + mainAssembly.TypeProvider, + new GenericContext(context) ); } @@ -294,7 +296,7 @@ namespace ICSharpCode.Decompiler.TypeSystem return field; } } - var returnType = memberRef.DecodeFieldSignature(new TypeProvider(context.CurrentAssembly), context); + var returnType = memberRef.DecodeFieldSignature(mainAssembly.TypeProvider, new GenericContext(context)); return new FakeField(compilation) { DeclaringType = declaringType, Name = name, @@ -322,7 +324,7 @@ namespace ICSharpCode.Decompiler.TypeSystem break; case SRM.HandleKind.MethodSpecification: var methodSpec = metadata.GetMethodSpecification((SRM.MethodSpecificationHandle)methodReference); - var methodTypeArgs = methodSpec.DecodeSignature(new TypeProvider(context.CurrentAssembly), context); + var methodTypeArgs = methodSpec.DecodeSignature(mainAssembly.TypeProvider, new GenericContext(context)); if (methodSpec.Method.Kind == SRM.HandleKind.MethodDefinition) { // generic instance of a methoddef (=generic method in non-generic class in current assembly) method = ResolveMethodDefinition(metadata, (SRM.MethodDefinitionHandle)methodSpec.Method); @@ -342,27 +344,9 @@ namespace ICSharpCode.Decompiler.TypeSystem IMethod ResolveMethodDefinition(SRM.MetadataReader metadata, SRM.MethodDefinitionHandle methodDefHandle, bool expandVarArgs = true) { - var methodDef = metadata.GetMethodDefinition(methodDefHandle); - var declaringType = ResolveDeclaringType(methodDef.GetDeclaringType()); - var declaringTypeDefinition = declaringType.GetDefinition(); - string name = metadata.GetString(methodDef.Name); - IMethod method; - if (declaringTypeDefinition != null) { - if (name == ".ctor") { - method = declaringTypeDefinition.GetConstructors(m => m.MetadataToken == methodDefHandle, GetMemberOptions.IgnoreInheritedMembers).FirstOrDefault(); - } else if (name == ".cctor") { - method = declaringTypeDefinition.Methods.FirstOrDefault(m => m.MetadataToken == methodDefHandle); - } else { - method = declaringTypeDefinition.GetMethods(m => m.MetadataToken == methodDefHandle, GetMemberOptions.IgnoreInheritedMembers) - .Concat(declaringTypeDefinition.GetAccessors(m => m.MetadataToken == methodDefHandle, GetMemberOptions.IgnoreInheritedMembers)).FirstOrDefault(); - } - } else { - method = null; - } + var method = mainAssembly.GetDefinition(methodDefHandle); if (method == null) { - var signature = methodDef.DecodeSignature(new TypeProvider(context.CurrentAssembly), - context.WithCurrentTypeDefinition(declaringTypeDefinition)); - method = CreateFakeMethod(declaringType, metadata.GetString(methodDef.Name), signature); + throw new NotImplementedException(); } if (expandVarArgs && method.Parameters.LastOrDefault()?.Type.Kind == TypeKind.ArgList) { method = new VarArgInstanceMethod(method, EmptyList.Instance); @@ -386,7 +370,7 @@ namespace ICSharpCode.Decompiler.TypeSystem IMethod method; if (memberRef.Parent.Kind == SRM.HandleKind.MethodDefinition) { method = ResolveMethodDefinition(metadata, (SRM.MethodDefinitionHandle)memberRef.Parent, expandVarArgs: false); - signature = memberRef.DecodeMethodSignature(new TypeProvider(context.CurrentAssembly), context); + signature = memberRef.DecodeMethodSignature(mainAssembly.TypeProvider, new GenericContext(context)); } else { var declaringType = ResolveDeclaringType(memberRef.Parent); var declaringTypeDefinition = declaringType.GetDefinition(); @@ -396,8 +380,8 @@ namespace ICSharpCode.Decompiler.TypeSystem // Note: declaringType might be parameterized, but the signature is for the original method definition. // We'll have to search the member directly on declaringTypeDefinition. string name = metadata.GetString(memberRef.Name); - signature = memberRef.DecodeMethodSignature(new TypeProvider(context.CurrentAssembly), - context.WithCurrentTypeDefinition(declaringTypeDefinition)); + signature = memberRef.DecodeMethodSignature(mainAssembly.TypeProvider, + new GenericContext(declaringTypeDefinition?.TypeParameters)); if (declaringTypeDefinition != null) { // Find the set of overloads to search: IEnumerable methods; diff --git a/ICSharpCode.Decompiler/TypeSystem/FullTypeName.cs b/ICSharpCode.Decompiler/TypeSystem/FullTypeName.cs index 6508ec0e7..636d0b6b6 100644 --- a/ICSharpCode.Decompiler/TypeSystem/FullTypeName.cs +++ b/ICSharpCode.Decompiler/TypeSystem/FullTypeName.cs @@ -34,10 +34,10 @@ namespace ICSharpCode.Decompiler.TypeSystem /// NamespaceName '.' TopLevelTypeName ['`'#] { '+' NestedTypeName ['`'#] } /// [Serializable] - public struct FullTypeName : IEquatable + public readonly struct FullTypeName : IEquatable { [Serializable] - struct NestedTypeName + readonly struct NestedTypeName { public readonly string Name; public readonly int AdditionalTypeParameterCount; diff --git a/ICSharpCode.Decompiler/TypeSystem/GenericContext.cs b/ICSharpCode.Decompiler/TypeSystem/GenericContext.cs new file mode 100644 index 000000000..fd37fa49e --- /dev/null +++ b/ICSharpCode.Decompiler/TypeSystem/GenericContext.cs @@ -0,0 +1,63 @@ +// Copyright (c) 2018 Daniel Grunwald +// +// 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. + +using System.Collections.Generic; +using ICSharpCode.Decompiler.TypeSystem.Implementation; + +namespace ICSharpCode.Decompiler.TypeSystem +{ + public readonly struct GenericContext + { + public readonly IReadOnlyList ClassTypeParameters; + public readonly IReadOnlyList MethodTypeParameters; + + public GenericContext(IReadOnlyList classTypeParameters) + { + this.ClassTypeParameters = classTypeParameters; + this.MethodTypeParameters = null; + } + + public GenericContext(IReadOnlyList classTypeParameters, IReadOnlyList methodTypeParameters) + { + this.ClassTypeParameters = classTypeParameters; + this.MethodTypeParameters = methodTypeParameters; + } + + internal GenericContext(ITypeResolveContext context) + { + this.ClassTypeParameters = context.CurrentTypeDefinition?.TypeParameters; + this.MethodTypeParameters = (context.CurrentMember as IMethod)?.TypeParameters; + } + + public ITypeParameter GetClassTypeParameter(int index) + { + if (index < ClassTypeParameters?.Count) + return ClassTypeParameters[index]; + else + return DummyTypeParameter.GetClassTypeParameter(index); + } + + public ITypeParameter GetMethodTypeParameter(int index) + { + if (index < MethodTypeParameters?.Count) + return MethodTypeParameters[index]; + else + return DummyTypeParameter.GetMethodTypeParameter(index); + } + } +} diff --git a/ICSharpCode.Decompiler/TypeSystem/IAssembly.cs b/ICSharpCode.Decompiler/TypeSystem/IAssembly.cs index 30975b443..9aa7f7e48 100644 --- a/ICSharpCode.Decompiler/TypeSystem/IAssembly.cs +++ b/ICSharpCode.Decompiler/TypeSystem/IAssembly.cs @@ -65,11 +65,6 @@ namespace ICSharpCode.Decompiler.TypeSystem /// public interface IAssembly : ICompilationProvider { - /// - /// Gets the original unresolved assembly. - /// - IUnresolvedAssembly UnresolvedAssembly { get; } - /// /// Gets whether this assembly is the main assembly of the compilation. /// @@ -120,8 +115,8 @@ namespace ICSharpCode.Decompiler.TypeSystem IEnumerable TopLevelTypeDefinitions { get; } /// - /// Gets the type definition from the metadata token, or null if not found. + /// Gets all types in the assembly, including nested types. /// - ITypeDefinition ResolveTypeDefToken(TypeDefinitionHandle token); + IEnumerable TypeDefinitions { get; } } } diff --git a/ICSharpCode.Decompiler/TypeSystem/ITypeDefinition.cs b/ICSharpCode.Decompiler/TypeSystem/ITypeDefinition.cs index 94b205e3a..7c10d2681 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ITypeDefinition.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ITypeDefinition.cs @@ -132,11 +132,6 @@ namespace ICSharpCode.Decompiler.TypeSystem /// This property is used to speed up the search for extension methods. bool HasExtensionMethods { get; } - /// - /// Gets whether this type definition is made up of one or more partial classes. - /// - bool IsPartial { get; } - /// /// Determines how this type is implementing the specified interface member. /// diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractResolvedTypeParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractResolvedTypeParameter.cs index 8e5a862c2..deaf7f4ad 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractResolvedTypeParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractResolvedTypeParameter.cs @@ -31,10 +31,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation readonly IEntity owner; readonly int index; readonly string name; - readonly IReadOnlyList attributes; readonly VarianceModifier variance; - protected AbstractTypeParameter(IEntity owner, int index, string name, VarianceModifier variance, IReadOnlyList attributes) + protected AbstractTypeParameter(IEntity owner, int index, string name, VarianceModifier variance) { if (owner == null) throw new ArgumentNullException("owner"); @@ -43,11 +42,10 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation this.ownerType = owner.SymbolKind; this.index = index; this.name = name ?? ((this.OwnerType == SymbolKind.Method ? "!!" : "!") + index.ToString(CultureInfo.InvariantCulture)); - this.attributes = attributes ?? EmptyList.Instance; this.variance = variance; } - protected AbstractTypeParameter(ICompilation compilation, SymbolKind ownerType, int index, string name, VarianceModifier variance, IReadOnlyList attributes) + protected AbstractTypeParameter(ICompilation compilation, SymbolKind ownerType, int index, string name, VarianceModifier variance) { if (compilation == null) throw new ArgumentNullException("compilation"); @@ -55,7 +53,6 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation this.ownerType = ownerType; this.index = index; this.name = name ?? ((this.OwnerType == SymbolKind.Method ? "!!" : "!") + index.ToString(CultureInfo.InvariantCulture)); - this.attributes = attributes ?? EmptyList.Instance; this.variance = variance; } @@ -75,9 +72,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation get { return index; } } - public IReadOnlyList Attributes { - get { return attributes; } - } + public abstract IReadOnlyList Attributes { get; } public VarianceModifier Variance { get { return variance; } @@ -247,11 +242,6 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation return this; } - public ITypeReference ToTypeReference() - { - return TypeParameterReference.Create(this.OwnerType, this.Index); - } - IEnumerable IType.GetNestedTypes(Predicate filter, GetMemberOptions options) { return EmptyList.Instance; @@ -333,12 +323,12 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation return GetMembersHelper.GetAccessors(this, FilterNonStatic(filter), options); } - public TypeParameterSubstitution GetSubstitution() + TypeParameterSubstitution IType.GetSubstitution() { return TypeParameterSubstitution.Identity; } - public TypeParameterSubstitution GetSubstitution(IReadOnlyList methodTypeArguments) + TypeParameterSubstitution IType.GetSubstitution(IReadOnlyList methodTypeArguments) { return TypeParameterSubstitution.Identity; } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs new file mode 100644 index 000000000..0319742f8 --- /dev/null +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs @@ -0,0 +1,293 @@ +// Copyright (c) 2018 Daniel Grunwald +// +// 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. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Reflection.Metadata; +using SRM = System.Reflection.Metadata; +using System.Text; +using ICSharpCode.Decompiler.Util; +using ICSharpCode.Decompiler.Semantics; +using System.Runtime.InteropServices; + +namespace ICSharpCode.Decompiler.TypeSystem.Implementation +{ + readonly struct AttributeListBuilder + { + readonly MetadataAssembly assembly; + readonly List attributes; + + public AttributeListBuilder(MetadataAssembly assembly) + { + Debug.Assert(assembly != null); + this.assembly = assembly; + this.attributes = new List(); + } + + public AttributeListBuilder(MetadataAssembly assembly, int capacity) + { + Debug.Assert(assembly != null); + this.assembly = assembly; + this.attributes = new List(capacity); + } + + public void Add(IAttribute attr) + { + attributes.Add(attr); + } + + /// + /// Add a builtin attribute without any arguments. + /// + public void Add(KnownAttribute type) + { + // use the assemblies' cache for simple attributes + Add(assembly.MakeAttribute(type)); + } + + /// + /// Construct a builtin attribute with a single positional argument of known type. + /// + public void Add(KnownAttribute type, KnownTypeCode argType, object argValue) + { + Add(type, new ConstantResolveResult(assembly.Compilation.FindType(argType), argValue)); + } + + /// + /// Construct a builtin attribute. + /// + public void Add(KnownAttribute type, params ResolveResult[] positionalArguments) + { + Add(new DefaultAttribute(assembly.GetAttributeType(type), positionalArguments)); + } + + internal KeyValuePair MakeNamedArg(IType attrType, string name, KnownTypeCode valueType, object value) + { + return MakeNamedArg(attrType, name, assembly.Compilation.FindType(valueType), value); + } + + internal KeyValuePair MakeNamedArg(IType attrType, string name, IType valueType, object value) + { + var rr = new ConstantResolveResult(valueType, value); + return Implementation.CustomAttribute.MakeNamedArg(assembly.Compilation, attrType, name, rr); + } + + #region MarshalAsAttribute (ConvertMarshalInfo) + internal void AddMarshalInfo(BlobHandle marshalInfo) + { + if (marshalInfo.IsNil) return; + var metadata = assembly.metadata; + Add(ConvertMarshalInfo(metadata.GetBlobReader(marshalInfo))); + } + + const string InteropServices = "System.Runtime.InteropServices"; + + IAttribute ConvertMarshalInfo(SRM.BlobReader marshalInfo) + { + IType marshalAsAttributeType = assembly.GetAttributeType(KnownAttribute.MarshalAs); + IType unmanagedTypeType = assembly.Compilation.FindType(new TopLevelTypeName(InteropServices, nameof(UnmanagedType))); + + int type = marshalInfo.ReadByte(); + var positionalArguments = new ResolveResult[] { + new ConstantResolveResult(unmanagedTypeType, type) + }; + var namedArguments = new List>(); + + int size; + switch (type) { + case 0x1e: // FixedArray + if (!marshalInfo.TryReadCompressedInteger(out size)) + size = 0; + namedArguments.Add(MakeNamedArg(marshalAsAttributeType, "SizeConst", KnownTypeCode.Int32, size)); + if (marshalInfo.RemainingBytes > 0) { + type = marshalInfo.ReadByte(); + if (type != 0x66) // None + namedArguments.Add(MakeNamedArg(marshalAsAttributeType, "ArraySubType", unmanagedTypeType, type)); + } + break; + case 0x1d: // SafeArray + if (marshalInfo.RemainingBytes > 0) { + VarEnum varType = (VarEnum)marshalInfo.ReadByte(); + if (varType != VarEnum.VT_EMPTY) { + var varEnumType = assembly.Compilation.FindType(new TopLevelTypeName(InteropServices, nameof(VarEnum))); + namedArguments.Add(MakeNamedArg(marshalAsAttributeType, + "SafeArraySubType", varEnumType, (int)varType)); + } + } + break; + case 0x2a: // NATIVE_TYPE_ARRAY + if (marshalInfo.RemainingBytes > 0) { + type = marshalInfo.ReadByte(); + } else { + type = 0x66; // Cecil uses NativeType.None as default. + } + if (type != 0x50) { // Max + namedArguments.Add(MakeNamedArg(marshalAsAttributeType, + "ArraySubType", unmanagedTypeType, type)); + } + int sizeParameterIndex = marshalInfo.TryReadCompressedInteger(out int value) ? value : -1; + size = marshalInfo.TryReadCompressedInteger(out value) ? value : -1; + int sizeParameterMultiplier = marshalInfo.TryReadCompressedInteger(out value) ? value : -1; + if (size >= 0) { + namedArguments.Add(MakeNamedArg(marshalAsAttributeType, + "SizeConst", KnownTypeCode.Int32, size)); + } + if (sizeParameterMultiplier != 0 && sizeParameterIndex >= 0) { + namedArguments.Add(MakeNamedArg(marshalAsAttributeType, + "SizeParamIndex", KnownTypeCode.Int16, (short)sizeParameterIndex)); + } + break; + case 0x2c: // CustomMarshaler + string guidValue = marshalInfo.ReadSerializedString(); + string unmanagedType = marshalInfo.ReadSerializedString(); + string managedType = marshalInfo.ReadSerializedString(); + string cookie = marshalInfo.ReadSerializedString(); + if (managedType != null) { + namedArguments.Add(MakeNamedArg(marshalAsAttributeType, + "MarshalType", KnownTypeCode.String, managedType)); + } + if (!string.IsNullOrEmpty(cookie)) { + namedArguments.Add(MakeNamedArg(marshalAsAttributeType, + "MarshalCookie", KnownTypeCode.String, cookie)); + } + break; + case 0x17: // FixedSysString + namedArguments.Add(MakeNamedArg(marshalAsAttributeType, + "SizeConst", KnownTypeCode.Int32, marshalInfo.ReadCompressedInteger())); + break; + } + + return new DefaultAttribute(marshalAsAttributeType, positionalArguments, namedArguments); + } + #endregion + + #region Custom Attributes (ReadAttribute) + public void Add(CustomAttributeHandleCollection attributes) + { + var metadata = assembly.metadata; + foreach (var handle in attributes) { + var attribute = metadata.GetCustomAttribute(handle); + var ctor = assembly.ResolveMethod(attribute.Constructor); + var type = ctor.DeclaringType; + if (IgnoreAttribute(type)) { + continue; + } + Add(new CustomAttribute(assembly, ctor, handle)); + } + } + + bool IgnoreAttribute(IType attributeType) + { + if (attributeType.DeclaringType != null || attributeType.TypeParameterCount != 0) + return false; + switch (attributeType.Namespace) { + case "System.Runtime.CompilerServices": + var options = assembly.TypeSystemOptions; + switch (attributeType.Name) { + case "DynamicAttribute": + return (options & TypeSystemOptions.Dynamic) != 0; + case "TupleElementNamesAttribute": + return (options & TypeSystemOptions.Tuple) != 0; + case "ExtensionAttribute": + return (options & TypeSystemOptions.ExtensionMethods) != 0; + case "DecimalConstantAttribute": + return true; + default: + return false; + } + case "System": + return attributeType.Name == "ParamArrayAttribute"; + default: + return false; + } + } + #endregion + + #region Security Attributes + public void AddSecurityAttributes(DeclarativeSecurityAttributeHandleCollection securityDeclarations) + { + var metadata = assembly.metadata; + foreach (var secDecl in securityDeclarations) { + if (secDecl.IsNil) + continue; + AddSecurityAttributes(metadata.GetDeclarativeSecurityAttribute(secDecl)); + } + } + + public void AddSecurityAttributes(DeclarativeSecurityAttribute secDecl) + { + var securityActionType = assembly.Compilation.FindType(new TopLevelTypeName("System.Security.Permissions", "SecurityAction")); + var securityAction = new ConstantResolveResult(securityActionType, (int)secDecl.Action); + var metadata = assembly.metadata; + var reader = metadata.GetBlobReader(secDecl.PermissionSet); + if (reader.ReadByte() == '.') { + // binary attribute + int attributeCount = reader.ReadCompressedInteger(); + for (int i = 0; i < attributeCount; i++) { + Add(ReadBinarySecurityAttribute(ref reader, securityAction)); + } + } else { + // for backward compatibility with .NET 1.0: XML-encoded attribute + reader.Reset(); + ReadXmlSecurityAttribute(ref reader, securityAction); + } + } + + private void ReadXmlSecurityAttribute(ref SRM.BlobReader reader, ConstantResolveResult securityAction) + { + string xml = reader.ReadUTF16(reader.RemainingBytes); + var permissionSetAttributeType = assembly.GetAttributeType(KnownAttribute.PermissionSet); + Add(new DefaultAttribute( + permissionSetAttributeType, + positionalArguments: new ResolveResult[] { securityAction }, + namedArguments: new[] { + MakeNamedArg(permissionSetAttributeType, "XML", assembly.Compilation.FindType(KnownTypeCode.String), xml) + } + )); + } + + private IAttribute ReadBinarySecurityAttribute(ref SRM.BlobReader reader, ResolveResult securityActionRR) + { + string attributeTypeName = reader.ReadSerializedString(); + IType attributeType = assembly.Compilation.FindType(new FullTypeName(attributeTypeName)); + + reader.ReadCompressedInteger(); // ?? + // The specification seems to be incorrect here, so I'm using the logic from Cecil instead. + int numNamed = reader.ReadCompressedInteger(); + + var decoder = new Metadata.CustomAttributeDecoder(assembly.TypeProvider, assembly.metadata); + var namedArgs = decoder.DecodeNamedArguments(ref reader, numNamed); + + return new DefaultAttribute( + attributeType, + positionalArguments: new ResolveResult[] { securityActionRR }, + namedArguments: CustomAttribute.ConvertNamedArguments(assembly.Compilation, attributeType, namedArgs)); + } + #endregion + + public IAttribute[] Build() + { + if (attributes.Count == 0) + return Empty.Array; + else + return attributes.ToArray(); + } + } +} diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/CustomAttribute.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/CustomAttribute.cs new file mode 100644 index 000000000..db99e81c5 --- /dev/null +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/CustomAttribute.cs @@ -0,0 +1,131 @@ +// Copyright (c) 2018 Daniel Grunwald +// +// 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. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using System.Text; +using ICSharpCode.Decompiler.Semantics; +using ICSharpCode.Decompiler.Util; +using SRM = System.Reflection.Metadata; + +namespace ICSharpCode.Decompiler.TypeSystem.Implementation +{ + /// + /// Custom attribute loaded from metadata. + /// + sealed class CustomAttribute : IAttribute + { + readonly MetadataAssembly assembly; + readonly SRM.CustomAttributeHandle handle; + public IMethod Constructor { get; } + + // lazy-loaded: + IReadOnlyList positionalArguments; + IReadOnlyList> namedArguments; + + internal CustomAttribute(MetadataAssembly assembly, IMethod attrCtor, SRM.CustomAttributeHandle handle) + { + Debug.Assert(assembly != null); + Debug.Assert(attrCtor != null); + Debug.Assert(!handle.IsNil); + this.assembly = assembly; + this.Constructor = attrCtor; + this.handle = handle; + } + + public IType AttributeType => Constructor.DeclaringType; + + public IReadOnlyList PositionalArguments { + get { + var args = LazyInit.VolatileRead(ref this.positionalArguments); + if (args != null) + return args; + DecodeValue(); + return this.positionalArguments; + } + } + + public IReadOnlyList> NamedArguments { + get { + var namedArgs = LazyInit.VolatileRead(ref this.namedArguments); + if (namedArgs != null) + return namedArgs; + DecodeValue(); + return this.namedArguments; + } + } + + void DecodeValue() + { + var metadata = assembly.metadata; + var attr = metadata.GetCustomAttribute(handle); + var attrVal = attr.DecodeValue(assembly.TypeProvider); + LazyInit.GetOrSet(ref this.positionalArguments, ConvertArguments(attrVal.FixedArguments)); + LazyInit.GetOrSet(ref this.namedArguments, + ConvertNamedArguments(assembly.Compilation, AttributeType, attrVal.NamedArguments)); + } + + internal static KeyValuePair MakeNamedArg(ICompilation compilation, IType attrType, string name, ResolveResult rr) + { + var field = attrType.GetFields(f => f.Name == name).FirstOrDefault(); + if (field != null) { + return new KeyValuePair(field, rr); + } + var prop = attrType.GetProperties(f => f.Name == name).FirstOrDefault(); + if (prop != null) { + return new KeyValuePair(prop, rr); + } + field = new FakeField(compilation) { + DeclaringType = attrType, + Name = name, + ReturnType = rr.Type + }; + return new KeyValuePair(field, rr); + } + + internal static IReadOnlyList> ConvertNamedArguments( + ICompilation compilation, IType attributeType, ImmutableArray> namedArgs) + { + var arr = new KeyValuePair[namedArgs.Length]; + for (int i = 0; i < arr.Length; i++) { + var namedArg = namedArgs[i]; + arr[i] = MakeNamedArg(compilation, attributeType, namedArg.Name, ConvertArgument(namedArg.Type, namedArg.Value)); + } + return arr; + } + + private static ResolveResult ConvertArgument(IType type, object value) + { + if (value is ImmutableArray> arr) { + return new ArrayCreateResolveResult(type, null, ConvertArguments(arr)); + } else if (value is IType valueType) { + return new TypeOfResolveResult(type, valueType); + } else { + return new ConstantResolveResult(type, value); + } + } + + private static IReadOnlyList ConvertArguments(ImmutableArray> arr) + { + return arr.SelectArray(arg => ConvertArgument(arg.Type, arg.Value)); + } + } +} diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs index 61382a067..3aa621b31 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs @@ -436,7 +436,6 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation switch (this.Kind) { case TypeKind.Class: case TypeKind.Interface: - case TypeKind.Module: case TypeKind.Delegate: return true; case TypeKind.Struct: diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultResolvedTypeParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultResolvedTypeParameter.cs index 0d4e1dd96..e6bc15eff 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultResolvedTypeParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultResolvedTypeParameter.cs @@ -27,7 +27,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation readonly bool hasReferenceTypeConstraint; readonly bool hasDefaultConstructorConstraint; readonly IReadOnlyList constraints; - + readonly IReadOnlyList attributes; + public DefaultTypeParameter( IEntity owner, int index, string name = null, @@ -35,12 +36,13 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation IReadOnlyList attributes = null, bool hasValueTypeConstraint = false, bool hasReferenceTypeConstraint = false, bool hasDefaultConstructorConstraint = false, IReadOnlyList constraints = null) - : base(owner, index, name, variance, attributes) + : base(owner, index, name, variance) { this.hasValueTypeConstraint = hasValueTypeConstraint; this.hasReferenceTypeConstraint = hasReferenceTypeConstraint; this.hasDefaultConstructorConstraint = hasDefaultConstructorConstraint; this.constraints = constraints ?? EmptyList.Instance; + this.attributes = attributes ?? EmptyList.Instance; } public DefaultTypeParameter( @@ -50,26 +52,21 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation IReadOnlyList attributes = null, bool hasValueTypeConstraint = false, bool hasReferenceTypeConstraint = false, bool hasDefaultConstructorConstraint = false, IReadOnlyList constraints = null) - : base(compilation, ownerType, index, name, variance, attributes) + : base(compilation, ownerType, index, name, variance) { this.hasValueTypeConstraint = hasValueTypeConstraint; this.hasReferenceTypeConstraint = hasReferenceTypeConstraint; this.hasDefaultConstructorConstraint = hasDefaultConstructorConstraint; this.constraints = constraints ?? EmptyList.Instance; + this.attributes = attributes ?? EmptyList.Instance; } - - public override bool HasValueTypeConstraint { - get { return hasValueTypeConstraint; } - } - - public override bool HasReferenceTypeConstraint { - get { return hasReferenceTypeConstraint; } - } - - public override bool HasDefaultConstructorConstraint { - get { return hasDefaultConstructorConstraint; } - } - + + public override IReadOnlyList Attributes => attributes; + + public override bool HasValueTypeConstraint => hasValueTypeConstraint; + public override bool HasReferenceTypeConstraint => hasReferenceTypeConstraint; + public override bool HasDefaultConstructorConstraint => hasDefaultConstructorConstraint; + public override IEnumerable DirectBaseTypes { get { bool hasNonInterfaceConstraint = false; @@ -85,168 +82,4 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } } } - - /* - /// - /// Default implementation of . - /// - [Serializable] - public sealed class DefaultTypeParameter : AbstractTypeParameter - { - IList constraints; - - BitVector16 flags; - - const ushort FlagReferenceTypeConstraint = 0x0001; - const ushort FlagValueTypeConstraint = 0x0002; - const ushort FlagDefaultConstructorConstraint = 0x0004; - - protected override void FreezeInternal() - { - constraints = FreezeList(constraints); - base.FreezeInternal(); - } - - public DefaultTypeParameter(SymbolKind ownerType, int index, string name) - : base(ownerType, index, name) - { - } - - public IList Constraints { - get { - if (constraints == null) - constraints = new List(); - return constraints; - } - } - - public bool HasDefaultConstructorConstraint { - get { return flags[FlagDefaultConstructorConstraint]; } - set { - CheckBeforeMutation(); - flags[FlagDefaultConstructorConstraint] = value; - } - } - - public bool HasReferenceTypeConstraint { - get { return flags[FlagReferenceTypeConstraint]; } - set { - CheckBeforeMutation(); - flags[FlagReferenceTypeConstraint] = value; - } - } - - public bool HasValueTypeConstraint { - get { return flags[FlagValueTypeConstraint]; } - set { - CheckBeforeMutation(); - flags[FlagValueTypeConstraint] = value; - } - } - - public override bool? IsReferenceType(ITypeResolveContext context) - { - switch (flags.Data & (FlagReferenceTypeConstraint | FlagValueTypeConstraint)) { - case FlagReferenceTypeConstraint: - return true; - case FlagValueTypeConstraint: - return false; - } - - return base.IsReferenceTypeHelper(GetEffectiveBaseClass(context)); - } - - public override IType GetEffectiveBaseClass(ITypeResolveContext context) - { - // protect against cyclic type parameters - using (var busyLock = BusyManager.Enter(this)) { - if (!busyLock.Success) - return SpecialTypes.UnknownType; - - if (HasValueTypeConstraint) - return context.GetTypeDefinition("System", "ValueType", 0, StringComparer.Ordinal) ?? SpecialTypes.UnknownType; - - List classTypeConstraints = new List(); - foreach (ITypeReference constraintRef in this.Constraints) { - IType constraint = constraintRef.Resolve(context); - if (constraint.Kind == TypeKind.Class) { - classTypeConstraints.Add(constraint); - } else if (constraint.Kind == TypeKind.TypeParameter) { - IType baseClass = ((ITypeParameter)constraint).GetEffectiveBaseClass(context); - if (baseClass.Kind == TypeKind.Class) - classTypeConstraints.Add(baseClass); - } - } - if (classTypeConstraints.Count == 0) - return KnownTypeReference.Object.Resolve(context); - // Find the derived-most type in the resulting set: - IType result = classTypeConstraints[0]; - for (int i = 1; i < classTypeConstraints.Count; i++) { - if (classTypeConstraints[i].GetDefinition().IsDerivedFrom(result.GetDefinition(), context)) - result = classTypeConstraints[i]; - } - return result; - } - } - - public override IEnumerable GetEffectiveInterfaceSet(ITypeResolveContext context) - { - List result = new List(); - // protect against cyclic type parameters - using (var busyLock = BusyManager.Enter(this)) { - if (busyLock.Success) { - foreach (ITypeReference constraintRef in this.Constraints) { - IType constraint = constraintRef.Resolve(context); - if (constraint.Kind == TypeKind.Interface) { - result.Add(constraint); - } else if (constraint.Kind == TypeKind.TypeParameter) { - result.AddRange(((ITypeParameter)constraint).GetEffectiveInterfaceSet(context)); - } - } - } - } - return result.Distinct(); - } - - public override ITypeParameterConstraints GetConstraints(ITypeResolveContext context) - { - return new DefaultTypeParameterConstraints( - this.Constraints.Select(c => c.Resolve(context)), - this.HasDefaultConstructorConstraint, this.HasReferenceTypeConstraint, this.HasValueTypeConstraint); - } - - /* - * Interning for type parameters is disabled; we can't intern cyclic structures as might - * occur in the constraints, and incomplete interning is dangerous for type parameters - * as we use reference equality. - void ISupportsInterning.PrepareForInterning(IInterningProvider provider) - { - // protect against cyclic constraints - using (var busyLock = BusyManager.Enter(this)) { - if (busyLock.Success) { - constraints = provider.InternList(constraints); - base.PrepareForInterning(provider); - } - } - } - - int ISupportsInterning.GetHashCodeForInterning() - { - unchecked { - int hashCode = base.GetHashCodeForInterning(); - if (constraints != null) - hashCode += constraints.GetHashCode(); - hashCode += 771 * flags.Data; - return hashCode; - } - } - - bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) - { - DefaultTypeParameter o = other as DefaultTypeParameter; - return base.EqualsForInterning(o) - && this.constraints == o.constraints - && this.flags == o.flags; - } - }*/ } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs index 151f75da0..5962458e8 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs @@ -443,6 +443,12 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } } + public IEnumerable TypeDefinitions { + get { + return TreeTraversal.PreOrder(TopLevelTypeDefinitions, td => td.NestedTypes); + } + } + public ITypeDefinition ResolveTypeDefToken(System.Reflection.Metadata.TypeDefinitionHandle token) { var td = unresolvedAssembly.GetTypeDefByToken(token); diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs new file mode 100644 index 000000000..8f7215fa7 --- /dev/null +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; + +namespace ICSharpCode.Decompiler.TypeSystem.Implementation +{ + enum KnownAttribute + { + /// + /// Not a known attribute + /// + None, + + CompilerGenerated, + /// + /// Marks a method as extension method; or a class as containing extension methods. + /// + Extension, + Dynamic, + TupleElementNames, + + // Assembly attributes: + AssemblyVersion, + InternalsVisibleTo, + + // Type attributes: + Serializable, + ComImport, + StructLayout, + DefaultMember, + + // Field attributes: + FieldOffset, + NonSerialized, + + // Method attributes: + + // Parameter attributes: + ParamArray, + + // Marshalling attributes: + MarshalAs, + + // Security attributes: + PermissionSet, + } + + static class KnownAttributes + { + internal const int Count = (int)KnownAttribute.PermissionSet + 1; + + static readonly TopLevelTypeName[] typeNames = new TopLevelTypeName[Count]{ + default, + new TopLevelTypeName("System.Runtime.CompilerServices", nameof(CompilerGeneratedAttribute)), + new TopLevelTypeName("System.Runtime.CompilerServices", nameof(ExtensionAttribute)), + new TopLevelTypeName("System.Runtime.CompilerServices", nameof(DynamicAttribute)), + new TopLevelTypeName("System.Runtime.CompilerServices", nameof(TupleElementNamesAttribute)), + // Assembly attributes: + new TopLevelTypeName("System.Reflection", nameof(AssemblyVersionAttribute)), + new TopLevelTypeName("System.Runtime.CompilerServices", nameof(InternalsVisibleToAttribute)), + // Type attributes: + new TopLevelTypeName("System", nameof(SerializableAttribute)), + new TopLevelTypeName("System.Runtime.InteropServices", nameof(ComImportAttribute)), + new TopLevelTypeName("System.Runtime.InteropServices", nameof(StructLayoutAttribute)), + new TopLevelTypeName("System.Runtime", nameof(DefaultMemberAttribute)), + // Field attributes: + new TopLevelTypeName("System.Runtime.InteropServices", nameof(FieldOffsetAttribute)), + new TopLevelTypeName("System", nameof(NonSerializedAttribute)), + // Parameter attributes: + new TopLevelTypeName("System", nameof(ParamArrayAttribute)), + // Marshalling attributes: + new TopLevelTypeName("System.Runtime.InteropServices", nameof(MarshalAsAttribute)), + // Security attributes: + new TopLevelTypeName("System.Security", "PermissionSetAttribute"), + }; + + public static ref readonly TopLevelTypeName GetTypeName(this KnownAttribute attr) + { + Debug.Assert(attr != KnownAttribute.None); + return ref typeNames[(int)attr]; + } + + public static IType FindType(this ICompilation compilation, KnownAttribute attrType) + { + return compilation.FindType(attrType.GetTypeName()); + } + +} diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataField.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataField.cs new file mode 100644 index 000000000..c9e09f532 --- /dev/null +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataField.cs @@ -0,0 +1,215 @@ +// Copyright (c) 2018 Daniel Grunwald +// +// 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. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Runtime.InteropServices; +using System.Threading; +using ICSharpCode.Decompiler.Semantics; +using ICSharpCode.Decompiler.Util; + +namespace ICSharpCode.Decompiler.TypeSystem.Implementation +{ + /// + /// Field definition backed by System.Reflection.Metadata + /// + class MetadataField : IField + { + readonly MetadataAssembly assembly; + readonly FieldDefinitionHandle handle; + readonly FieldAttributes attributes; + + // lazy-loaded fields: + ITypeDefinition declaringType; + string name; + object constantValue; + IType type; + bool isVolatile; // initialized together with this.type + IAttribute[] customAttributes; + + internal MetadataField(MetadataAssembly assembly, FieldDefinitionHandle handle) + { + Debug.Assert(assembly != null); + Debug.Assert(!handle.IsNil); + this.assembly = assembly; + this.handle = handle; + var def = assembly.metadata.GetFieldDefinition(handle); + this.attributes = def.Attributes; + } + + public EntityHandle MetadataToken => handle; + + public override string ToString() + { + return $"{MetadataTokens.GetToken(handle):X8} {DeclaringType?.ReflectionName}.{Name}"; + } + + public string Name { + get { + string name = LazyInit.VolatileRead(ref this.name); + if (name != null) + return name; + var metadata = assembly.metadata; + var fieldDef = metadata.GetFieldDefinition(handle); + return LazyInit.GetOrSet(ref this.name, metadata.GetString(fieldDef.Name)); + } + } + + public Accessibility Accessibility => MetadataLoader.GetAccessibility(attributes); + public bool IsReadOnly => (attributes & FieldAttributes.InitOnly) != 0; + public bool IsStatic => (attributes & FieldAttributes.Static) != 0; + + // not sure if we need IsFixed anywhere... + public bool IsFixed => throw new NotImplementedException(); + + // Do we still want IsShadowing in the TS? + // We never set it for assemblies loaded from disk; only for those parsed from C#... + bool IEntity.IsShadowing => throw new NotImplementedException(); + + SymbolKind ISymbol.SymbolKind => SymbolKind.Field; + IMember IMember.MemberDefinition => this; + TypeParameterSubstitution IMember.Substitution => TypeParameterSubstitution.Identity; + + // Fields can't implement interfaces: + IReadOnlyList IMember.ImplementedInterfaceMembers => EmptyList.Instance; + bool IMember.IsExplicitInterfaceImplementation => false; + bool IMember.IsVirtual => false; + bool IMember.IsOverride => false; + bool IMember.IsOverridable => false; + bool IEntity.IsAbstract => false; + bool IEntity.IsSealed => false; + + public ITypeDefinition DeclaringTypeDefinition { + get { + var declType = LazyInit.VolatileRead(ref this.declaringType); + if (declType != null) { + return declType; + } else { + var def = assembly.metadata.GetFieldDefinition(handle); + return LazyInit.GetOrSet(ref this.declaringType, + assembly.GetDefinition(def.GetDeclaringType())); + } + } + } + + public IType DeclaringType => DeclaringTypeDefinition; + public IAssembly ParentAssembly => assembly; + public ICompilation Compilation => assembly.Compilation; + + public IReadOnlyList Attributes { + get { + var attr = LazyInit.VolatileRead(ref this.customAttributes); + if (attr != null) + return attr; + return LazyInit.GetOrSet(ref this.customAttributes, DecodeAttributes()); + } + } + + IAttribute[] DecodeAttributes() + { + var b = new AttributeListBuilder(assembly); + var metadata = assembly.metadata; + var fieldDef = metadata.GetFieldDefinition(handle); + + // FieldOffsetAttribute + int offset = fieldDef.GetOffset(); + if (offset != -1) { + b.Add(KnownAttribute.FieldOffset, KnownTypeCode.Int32, offset); + } + + // NonSerializedAttribute + if ((fieldDef.Attributes & FieldAttributes.NotSerialized) != 0) { + b.Add(KnownAttribute.NonSerialized); + } + + b.AddMarshalInfo(fieldDef.GetMarshallingDescriptor()); + b.Add(fieldDef.GetCustomAttributes()); + + return b.Build(); + } + + public string FullName => $"{DeclaringType?.FullName}.{Name}"; + public string ReflectionName => $"{DeclaringType?.ReflectionName}.{Name}"; + public string Namespace => DeclaringType?.Namespace; + + public bool IsVolatile { + get { + if (LazyInit.VolatileRead(ref this.type) == null) { + DecodeTypeAndVolatileFlag(); + } + return this.isVolatile; + } + } + IType IMember.ReturnType => Type; + public IType Type { + get { + var ty = LazyInit.VolatileRead(ref this.type); + if (ty != null) { + return ty; + } + return DecodeTypeAndVolatileFlag(); + } + } + + private IType DecodeTypeAndVolatileFlag() + { + var metadata = assembly.metadata; + var fieldDef = metadata.GetFieldDefinition(handle); + var ty = fieldDef.DecodeSignature(assembly.TypeProvider, new GenericContext(DeclaringType?.TypeParameters)); + if (ty is ModifiedType mod && mod.Modifier.Name == "IsVolatile" && mod.Modifier.Namespace == "System.Runtime.CompilerServices") { + Volatile.Write(ref this.isVolatile, true); + ty = mod.ElementType; + } + return LazyInit.GetOrSet(ref this.type, ty); + } + + // TODO: decimal constants + public bool IsConst => (attributes & FieldAttributes.Literal) != 0; + + public object ConstantValue { + get { + object val = LazyInit.VolatileRead(ref this.constantValue); + if (val != null) + return val; + var metadata = assembly.metadata; + var fieldDef = metadata.GetFieldDefinition(handle); + var constantHandle = fieldDef.GetDefaultValue(); + if (constantHandle.IsNil) + return null; + var constant = metadata.GetConstant(constantHandle); + var blobReader = metadata.GetBlobReader(constant.Value); + val = blobReader.ReadConstant(constant.TypeCode); + return LazyInit.GetOrSet(ref this.constantValue, val); + } + } + + public bool Equals(IMember obj, TypeVisitor typeNormalization) + { + return this == obj; + } + + public IMember Specialize(TypeParameterSubstitution substitution) + { + return SpecializedField.Create(this, substitution); + } + } +} diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataNamespace.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataNamespace.cs new file mode 100644 index 000000000..af033365c --- /dev/null +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataNamespace.cs @@ -0,0 +1,99 @@ +// Copyright (c) 2018 Daniel Grunwald +// +// 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. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection.Metadata; +using ICSharpCode.Decompiler.Util; + +namespace ICSharpCode.Decompiler.TypeSystem.Implementation +{ + sealed class MetadataNamespace : INamespace + { + readonly MetadataAssembly assembly; + readonly NamespaceDefinition ns; + + public INamespace ParentNamespace { get; } + public string FullName { get; } + public string Name { get; } + + public MetadataNamespace(MetadataAssembly assembly, INamespace parent, string fullName, NamespaceDefinition ns) + { + Debug.Assert(assembly != null); + Debug.Assert(fullName != null); + this.assembly = assembly; + this.ParentNamespace = parent; + this.ns = ns; + this.FullName = fullName; + this.Name = assembly.GetString(ns.Name); + } + + string INamespace.ExternAlias => string.Empty; + + INamespace[] childNamespaces; + + public IEnumerable ChildNamespaces { + get { + var children = LazyInit.VolatileRead(ref childNamespaces); + if (children != null) { + return children; + } + var nsDefs = ns.NamespaceDefinitions; + children = new INamespace[nsDefs.Length]; + for (int i = 0; i < children.Length; i++) { + var nsHandle = nsDefs[i]; + string fullName = assembly.metadata.GetString(nsHandle); + children[i] = new MetadataNamespace(assembly, this, fullName, + assembly.metadata.GetNamespaceDefinition(nsHandle)); + } + return LazyInit.GetOrSet(ref childNamespaces, children); + } + } + + IEnumerable INamespace.Types { + get { + foreach (var typeHandle in ns.TypeDefinitions) { + var def = assembly.GetDefinition(typeHandle); + if (def != null) + yield return def; + } + } + } + + IEnumerable INamespace.ContributingAssemblies => new[] { assembly }; + + SymbolKind ISymbol.SymbolKind => SymbolKind.Namespace; + + ICompilation ICompilationProvider.Compilation => assembly.Compilation; + + INamespace INamespace.GetChildNamespace(string name) + { + foreach (var ns in ChildNamespaces) { + if (ns.Name == name) + return ns; + } + return null; + } + + ITypeDefinition INamespace.GetTypeDefinition(string name, int typeParameterCount) + { + return assembly.GetTypeDefinition(FullName, name, typeParameterCount); + } + } +} diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs new file mode 100644 index 000000000..1546bf93f --- /dev/null +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs @@ -0,0 +1,487 @@ +// Copyright (c) 2018 Daniel Grunwald +// +// 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. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.Semantics; +using ICSharpCode.Decompiler.Util; + +namespace ICSharpCode.Decompiler.TypeSystem.Implementation +{ + /// + /// Type definition backed by System.Reflection.Metadata + /// + sealed class MetadataTypeDefinition : ITypeDefinition + { + readonly MetadataAssembly assembly; + readonly TypeDefinitionHandle handle; + + // eagerly loaded: + readonly FullTypeName fullTypeName; + readonly TypeAttributes attributes; + public TypeKind Kind { get; } + public ITypeDefinition DeclaringTypeDefinition { get; } + public IReadOnlyList TypeParameters { get; } + public KnownTypeCode KnownTypeCode { get; } + public IType EnumUnderlyingType { get; } + + // lazy-loaded: + IAttribute[] customAttributes; + + internal MetadataTypeDefinition(MetadataAssembly assembly, TypeDefinitionHandle handle) + { + Debug.Assert(assembly != null); + Debug.Assert(!handle.IsNil); + this.assembly = assembly; + this.handle = handle; + var metadata = assembly.metadata; + var td = metadata.GetTypeDefinition(handle); + this.attributes = td.Attributes; + this.fullTypeName = td.GetFullTypeName(metadata); + // Find DeclaringType + KnownTypeCode: + if (fullTypeName.IsNested) { + this.DeclaringTypeDefinition = assembly.GetDefinition(td.GetDeclaringType()); + } else { + var topLevelTypeName = fullTypeName.TopLevelTypeName; + for (int i = 0; i < KnownTypeReference.KnownTypeCodeCount; i++) { + var ktr = KnownTypeReference.Get((KnownTypeCode)i); + if (ktr != null && ktr.TypeName == topLevelTypeName) { + this.KnownTypeCode = (KnownTypeCode)i; + break; + } + } + } + // Find type kind: + if ((attributes & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Interface) { + this.Kind = TypeKind.Interface; + } else if (td.IsEnum(metadata, out var underlyingType)) { + this.Kind = TypeKind.Enum; + this.EnumUnderlyingType = assembly.Compilation.FindType(underlyingType.ToKnownTypeCode()); + } else if (td.IsValueType(metadata)) { + this.Kind = TypeKind.Struct; + } else if (td.IsDelegate(metadata)) { + this.Kind = TypeKind.Delegate; + } else { + this.Kind = TypeKind.Class; + } + // Create type parameters: + this.TypeParameters = MetadataTypeParameter.Create(assembly, this, td.GetGenericParameters()); + } + + public override string ToString() + { + return $"{MetadataTokens.GetToken(handle):X8} {fullTypeName}"; + } + + public IReadOnlyList NestedTypes => throw new NotImplementedException(); + + IMember[] members; + + public IReadOnlyList Members { + get { + var members = LazyInit.VolatileRead(ref this.members); + if (members != null) + return members; + members = this.Fields.Concat(this.Methods).Concat(this.Properties).Concat(this.Events).ToArray(); + return LazyInit.GetOrSet(ref this.members, members); + } + } + + IField[] fields; + + public IEnumerable Fields { + get { + var fields = LazyInit.VolatileRead(ref this.fields); + if (fields != null) + return fields; + var metadata = assembly.metadata; + var fieldCollection = metadata.GetTypeDefinition(handle).GetFields(); + var fieldList = new List(fieldCollection.Count); + foreach (FieldDefinitionHandle h in fieldCollection) { + var field = metadata.GetFieldDefinition(h); + var attr = field.Attributes; + if (assembly.IsVisible(attr) && (attr & FieldAttributes.SpecialName) == 0) { + fieldList.Add(assembly.GetDefinition(h)); + } + } + return LazyInit.GetOrSet(ref this.fields, fieldList.ToArray()); + } + } + + public IEnumerable Methods => throw new NotImplementedException(); + + public IEnumerable Properties => throw new NotImplementedException(); + + public IEnumerable Events => throw new NotImplementedException(); + + + + public IType DeclaringType => DeclaringTypeDefinition; + + public bool HasExtensionMethods => throw new NotImplementedException(); + + public bool? IsReferenceType { + get { + switch (Kind) { + case TypeKind.Struct: + case TypeKind.Enum: + case TypeKind.Void: + return false; + default: + return true; + } + } + } + + public int TypeParameterCount => TypeParameters.Count; + + IReadOnlyList IType.TypeArguments => TypeParameters; + + public IEnumerable DirectBaseTypes => throw new NotImplementedException(); + + public EntityHandle MetadataToken => handle; + + public FullTypeName FullTypeName => fullTypeName; + public string Name => fullTypeName.Name; + + public IAssembly ParentAssembly => assembly; + + #region Type Attributes + public IReadOnlyList Attributes { + get { + var attr = LazyInit.VolatileRead(ref this.customAttributes); + if (attr != null) + return attr; + return LazyInit.GetOrSet(ref this.customAttributes, DecodeAttributes()); + } + } + + IAttribute[] DecodeAttributes() + { + var b = new AttributeListBuilder(assembly); + var metadata = assembly.metadata; + var typeDefinition = metadata.GetTypeDefinition(handle); + + // SerializableAttribute + if ((typeDefinition.Attributes & TypeAttributes.Serializable) != 0) + b.Add(KnownAttribute.Serializable); + + // ComImportAttribute + if ((typeDefinition.Attributes & TypeAttributes.Import) != 0) + b.Add(KnownAttribute.ComImport); + + #region StructLayoutAttribute + LayoutKind layoutKind = LayoutKind.Auto; + switch (typeDefinition.Attributes & TypeAttributes.LayoutMask) { + case TypeAttributes.SequentialLayout: + layoutKind = LayoutKind.Sequential; + break; + case TypeAttributes.ExplicitLayout: + layoutKind = LayoutKind.Explicit; + break; + } + CharSet charSet = CharSet.None; + switch (typeDefinition.Attributes & TypeAttributes.StringFormatMask) { + case TypeAttributes.AnsiClass: + charSet = CharSet.Ansi; + break; + case TypeAttributes.AutoClass: + charSet = CharSet.Auto; + break; + case TypeAttributes.UnicodeClass: + charSet = CharSet.Unicode; + break; + } + var layout = typeDefinition.GetLayout(); + LayoutKind defaultLayoutKind = Kind == TypeKind.Struct ? LayoutKind.Sequential : LayoutKind.Auto; + if (layoutKind != defaultLayoutKind || charSet != CharSet.Ansi || layout.PackingSize > 0 || layout.Size > 0) { + var structLayoutAttributeType = Compilation.FindType(KnownAttribute.StructLayout); + var layoutKindType = Compilation.FindType(new TopLevelTypeName("System.Runtime.InteropServices", "LayoutKind")); + var positionalArguments = new ResolveResult[] { + new ConstantResolveResult(layoutKindType, (int)layoutKind) + }; + var namedArguments = new List>(3); + if (charSet != CharSet.Ansi) { + var charSetType = Compilation.FindType(new TopLevelTypeName("System.Runtime.InteropServices", "CharSet")); + namedArguments.Add(b.MakeNamedArg(structLayoutAttributeType, + "CharSet", charSetType, (int)charSet)); + } + if (layout.PackingSize > 0) { + namedArguments.Add(b.MakeNamedArg(structLayoutAttributeType, + "Pack", KnownTypeCode.Int32, (int)layout.PackingSize)); + } + if (layout.Size > 0) { + namedArguments.Add(b.MakeNamedArg(structLayoutAttributeType, + "Size", KnownTypeCode.Int32, (int)layout.Size)); + } + b.Add(new DefaultAttribute( + structLayoutAttributeType, + positionalArguments, namedArguments + )); + } + #endregion + + b.Add(typeDefinition.GetCustomAttributes()); + b.AddSecurityAttributes(typeDefinition.GetDeclarativeSecurityAttributes()); + + return b.Build(); + } + #endregion + + public Accessibility Accessibility { + get { + switch (attributes & TypeAttributes.VisibilityMask) { + case TypeAttributes.NotPublic: + case TypeAttributes.NestedAssembly: + return Accessibility.Internal; + case TypeAttributes.Public: + case TypeAttributes.NestedPublic: + return Accessibility.Public; + case TypeAttributes.NestedPrivate: + return Accessibility.Private; + case TypeAttributes.NestedFamily: + return Accessibility.Protected; + case TypeAttributes.NestedFamANDAssem: + return Accessibility.ProtectedAndInternal; + case TypeAttributes.NestedFamORAssem: + return Accessibility.ProtectedOrInternal; + default: + return Accessibility.None; + } + } + } + + public bool IsStatic => (attributes & (TypeAttributes.Abstract | TypeAttributes.Sealed)) == (TypeAttributes.Abstract | TypeAttributes.Sealed); + public bool IsAbstract => (attributes & TypeAttributes.Abstract) != 0; + public bool IsSealed => (attributes & TypeAttributes.Sealed) != 0; + + bool IEntity.IsShadowing => throw new NotImplementedException(); + + public SymbolKind SymbolKind => SymbolKind.TypeDefinition; + + public ICompilation Compilation => assembly.Compilation; + + public string FullName => throw new NotImplementedException(); + public string ReflectionName => fullTypeName.ReflectionName; + public string Namespace => fullTypeName.TopLevelTypeName.Namespace; + + ITypeDefinition IType.GetDefinition() => this; + TypeParameterSubstitution IType.GetSubstitution() => TypeParameterSubstitution.Identity; + TypeParameterSubstitution IType.GetSubstitution(IReadOnlyList methodTypeArguments) => TypeParameterSubstitution.Identity; + + public IType AcceptVisitor(TypeVisitor visitor) + { + return visitor.VisitTypeDefinition(this); + } + + IType IType.VisitChildren(TypeVisitor visitor) + { + return this; + } + + bool IEquatable.Equals(IType other) + { + return this == other; + } + + #region GetNestedTypes + public IEnumerable GetNestedTypes(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) + { + const GetMemberOptions opt = GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions; + if ((options & opt) == opt) { + return GetFiltered(this.NestedTypes, filter); + } else { + return GetMembersHelper.GetNestedTypes(this, filter, options); + } + } + + public IEnumerable GetNestedTypes(IReadOnlyList typeArguments, Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) + { + return GetMembersHelper.GetNestedTypes(this, typeArguments, filter, options); + } + #endregion + + #region GetMembers() + IEnumerable GetFiltered(IEnumerable input, Predicate filter) where T : class + { + if (filter == null) + return input; + else + return ApplyFilter(input, filter); + } + + IEnumerable ApplyFilter(IEnumerable input, Predicate filter) where T : class + { + foreach (var member in input) { + if (filter(member)) + yield return member; + } + } + + public IEnumerable GetMethods(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) + { + if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) { + return GetFiltered(this.Methods, ExtensionMethods.And(m => !m.IsConstructor, filter)); + } else { + return GetMembersHelper.GetMethods(this, filter, options); + } + } + + public IEnumerable GetMethods(IReadOnlyList typeArguments, Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) + { + return GetMembersHelper.GetMethods(this, typeArguments, filter, options); + } + + public IEnumerable GetConstructors(Predicate filter = null, GetMemberOptions options = GetMemberOptions.IgnoreInheritedMembers) + { + if (ComHelper.IsComImport(this)) { + IType coClass = ComHelper.GetCoClass(this); + using (var busyLock = BusyManager.Enter(this)) { + if (busyLock.Success) { + return coClass.GetConstructors(filter, options) + .Select(m => new SpecializedMethod(m, m.Substitution) { DeclaringType = this }); + } + } + return EmptyList.Instance; + } + if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) { + return GetFiltered(this.Methods, ExtensionMethods.And(m => m.IsConstructor && !m.IsStatic, filter)); + } else { + return GetMembersHelper.GetConstructors(this, filter, options); + } + } + + public IEnumerable GetProperties(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) + { + if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) { + return GetFiltered(this.Properties, filter); + } else { + return GetMembersHelper.GetProperties(this, filter, options); + } + } + + public IEnumerable GetFields(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) + { + if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) { + return GetFiltered(this.Fields, filter); + } else { + return GetMembersHelper.GetFields(this, filter, options); + } + } + + public IEnumerable GetEvents(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) + { + if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) { + return GetFiltered(this.Events, filter); + } else { + return GetMembersHelper.GetEvents(this, filter, options); + } + } + + public IEnumerable GetMembers(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) + { + if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) { + return GetFiltered(this.Members, filter); + } else { + return GetMembersHelper.GetMembers(this, filter, options); + } + } + + public IEnumerable GetAccessors(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) + { + if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) { + return GetFilteredAccessors(filter); + } else { + return GetMembersHelper.GetAccessors(this, filter, options); + } + } + + IEnumerable GetFilteredAccessors(Predicate filter) + { + foreach (var prop in this.Properties) { + var getter = prop.Getter; + if (getter != null && (filter == null || filter(getter))) + yield return getter; + var setter = prop.Setter; + if (setter != null && (filter == null || filter(setter))) + yield return setter; + } + foreach (var ev in this.Events) { + var adder = ev.AddAccessor; + if (adder != null && (filter == null || filter(adder))) + yield return adder; + var remover = ev.RemoveAccessor; + if (remover != null && (filter == null || filter(remover))) + yield return remover; + var invoker = ev.InvokeAccessor; + if (invoker != null && (filter == null || filter(invoker))) + yield return remover; + } + } + #endregion + + #region GetInterfaceImplementation + public IMember GetInterfaceImplementation(IMember interfaceMember) + { + return GetInterfaceImplementation(new[] { interfaceMember })[0]; + } + + public IReadOnlyList GetInterfaceImplementation(IReadOnlyList interfaceMembers) + { + // TODO: review the subtle rules for interface reimplementation, + // write tests and fix this method. + // Also virtual/override is going to be tricky - + // I think we'll need to consider the 'virtual' method first for + // reimplemenatation purposes, but then actually return the 'override' + // (as that's the method that ends up getting called) + + interfaceMembers = interfaceMembers.ToList(); // avoid evaluating more than once + + var result = new IMember[interfaceMembers.Count]; + var signatureToIndexDict = new MultiDictionary(SignatureComparer.Ordinal); + for (int i = 0; i < interfaceMembers.Count; i++) { + signatureToIndexDict.Add(interfaceMembers[i], i); + } + foreach (var member in GetMembers(m => !m.IsExplicitInterfaceImplementation)) { + foreach (int interfaceMemberIndex in signatureToIndexDict[member]) { + result[interfaceMemberIndex] = member; + } + } + foreach (var explicitImpl in GetMembers(m => m.IsExplicitInterfaceImplementation)) { + foreach (var interfaceMember in explicitImpl.ImplementedInterfaceMembers) { + foreach (int potentialMatchingIndex in signatureToIndexDict[interfaceMember]) { + if (interfaceMember.Equals(interfaceMembers[potentialMatchingIndex])) { + result[potentialMatchingIndex] = explicitImpl; + } + } + } + } + return result; + } + #endregion + } +} diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs new file mode 100644 index 000000000..100870cb6 --- /dev/null +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs @@ -0,0 +1,124 @@ +// Copyright (c) 2018 Daniel Grunwald +// +// 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. + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using ICSharpCode.Decompiler.Util; + +namespace ICSharpCode.Decompiler.TypeSystem.Implementation +{ + sealed class MetadataTypeParameter : AbstractTypeParameter + { + readonly MetadataAssembly assembly; + readonly GenericParameterHandle handle; + + readonly GenericParameterAttributes attr; + + // lazy-loaded: + IReadOnlyList customAttributes; + IReadOnlyList constraints; + + public static IReadOnlyList Create(MetadataAssembly assembly, IEntity owner, GenericParameterHandleCollection handles) + { + if (handles.Count == 0) + return EmptyList.Instance; + var tps = new ITypeParameter[handles.Count]; + int i = 0; + foreach (var handle in handles) { + tps[i] = Create(assembly, owner, handle); + } + return tps; + } + + public static MetadataTypeParameter Create(MetadataAssembly assembly, IEntity owner, GenericParameterHandle handle) + { + var metadata = assembly.metadata; + var gp = metadata.GetGenericParameter(handle); + return new MetadataTypeParameter(assembly, owner, gp.Index, assembly.GetString(gp.Name), handle, gp.Attributes); + } + + private MetadataTypeParameter(MetadataAssembly assembly, IEntity owner, int index, string name, + GenericParameterHandle handle, GenericParameterAttributes attr) + : base(owner, index, name, GetVariance(attr)) + { + this.assembly = assembly; + this.handle = handle; + this.attr = attr; + } + + private static VarianceModifier GetVariance(GenericParameterAttributes attr) + { + switch (attr & GenericParameterAttributes.VarianceMask) { + case GenericParameterAttributes.Contravariant: + return VarianceModifier.Contravariant; + case GenericParameterAttributes.Covariant: + return VarianceModifier.Covariant; + default: + return VarianceModifier.Invariant; + } + } + + public GenericParameterHandle MetadataToken => handle; + + public override IReadOnlyList Attributes { + get { + var attr = LazyInit.VolatileRead(ref this.customAttributes); + if (attr != null) + return attr; + return LazyInit.GetOrSet(ref this.customAttributes, DecodeAttributes()); + } + } + + IAttribute[] DecodeAttributes() + { + var metadata = assembly.metadata; + var gp = metadata.GetGenericParameter(handle); + + var attributes = gp.GetCustomAttributes(); + var b = new AttributeListBuilder(assembly, attributes.Count); + b.Add(attributes); + return b.Build(); + } + + public override bool HasDefaultConstructorConstraint => (attr & GenericParameterAttributes.DefaultConstructorConstraint) != 0; + public override bool HasReferenceTypeConstraint => (attr & GenericParameterAttributes.ReferenceTypeConstraint) != 0; + public override bool HasValueTypeConstraint => (attr & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0; + + public override IEnumerable DirectBaseTypes { + get { + var constraints = LazyInit.VolatileRead(ref this.constraints); + if (constraints != null) + return constraints; + return LazyInit.GetOrSet(ref this.constraints, DecodeConstraints()); + } + } + + private IReadOnlyList DecodeConstraints() + { + throw new NotImplementedException(); + } + + public override string ToString() + { + return $"{MetadataTokens.GetToken(handle):X8} Index={Index} Owner={Owner}"; + } + } +} \ No newline at end of file diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeReference.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeReference.cs index 1283c53b6..16d2941d6 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeReference.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeReference.cs @@ -45,37 +45,39 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public IType Resolve(ITypeResolveContext context) { - return Resolve(type, metadata, context, + return Resolve(type, metadata, + new TypeProvider(context.CurrentAssembly), + new GenericContext(context), options, typeAttributes); } public static IType Resolve(SRM.EntityHandle type, SRM.MetadataReader metadata, - ITypeResolveContext context, + TypeProvider typeProvider, + GenericContext genericContext, TypeSystemOptions options, SRM.CustomAttributeHandleCollection? typeAttributes = null) { if (type.IsNil) return SpecialType.UnknownType; - var tp = new TypeProvider(context.CurrentAssembly); IType ty; switch (type.Kind) { case SRM.HandleKind.TypeDefinition: - ty = tp.GetTypeFromDefinition(metadata, (SRM.TypeDefinitionHandle)type, 0); + ty = typeProvider.GetTypeFromDefinition(metadata, (SRM.TypeDefinitionHandle)type, 0); break; case SRM.HandleKind.TypeReference: - ty = tp.GetTypeFromReference(metadata, (SRM.TypeReferenceHandle)type, 0); + ty = typeProvider.GetTypeFromReference(metadata, (SRM.TypeReferenceHandle)type, 0); break; case SRM.HandleKind.TypeSpecification: var typeSpec = metadata.GetTypeSpecification((SRM.TypeSpecificationHandle)type); - ty = typeSpec.DecodeSignature(tp, context); + ty = typeSpec.DecodeSignature(typeProvider, genericContext); break; default: Debug.Fail("Not a type handle"); ty = SpecialType.UnknownType; break; } - ty = ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, context.Compilation, + ty = ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, typeProvider.Compilation, typeAttributes, metadata, options); return ty; } @@ -111,7 +113,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation return Resolve((SRM.FieldDefinitionHandle)fieldHandle, metadata, context, options); } else { var memberRef = metadata.GetMemberReference((SRM.MemberReferenceHandle)fieldHandle); - IType ty = memberRef.DecodeFieldSignature(new TypeProvider(context.CurrentAssembly), context); + IType ty = memberRef.DecodeFieldSignature(new TypeProvider(context.CurrentAssembly), new GenericContext(context)); ty = ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, context.Compilation, memberRef.GetCustomAttributes(), metadata, options); return ty; @@ -124,7 +126,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation TypeSystemOptions options) { var fieldDef = metadata.GetFieldDefinition(fieldHandle); - IType ty = fieldDef.DecodeSignature(new TypeProvider(context.CurrentAssembly), context); + IType ty = fieldDef.DecodeSignature(new TypeProvider(context.CurrentAssembly), new GenericContext(context)); ty = ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, context.Compilation, fieldDef.GetCustomAttributes(), metadata, options); return ty; @@ -184,7 +186,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation TypeSystemOptions options) { var typeProvider = new TypeProvider(context.CurrentAssembly); - var signature = methodDef.DecodeSignature(typeProvider, context); + var signature = methodDef.DecodeSignature(typeProvider, new GenericContext(context)); return ApplyAttributes(signature, methodDef.GetParameters(), context.Compilation, metadata, options); } @@ -193,7 +195,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation TypeSystemOptions options) { var typeProvider = new TypeProvider(context.CurrentAssembly); - var signature = propertyDef.DecodeSignature(typeProvider, context); + var signature = propertyDef.DecodeSignature(typeProvider, new GenericContext(context)); var accessors = propertyDef.GetAccessors(); SRM.ParameterHandleCollection? parameterHandles = null; if (!accessors.Getter.IsNil) { diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/SimpleCompilation.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/SimpleCompilation.cs index 607258166..8359a7947 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/SimpleCompilation.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/SimpleCompilation.cs @@ -35,12 +35,12 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation readonly IList referencedAssemblies; INamespace rootNamespace; - public SimpleCompilation(IUnresolvedAssembly mainAssembly, params IAssemblyReference[] assemblyReferences) + public SimpleCompilation(IAssemblyReference mainAssembly, params IAssemblyReference[] assemblyReferences) : this(mainAssembly, (IEnumerable)assemblyReferences) { } - public SimpleCompilation(IUnresolvedAssembly mainAssembly, IEnumerable assemblyReferences) + public SimpleCompilation(IAssemblyReference mainAssembly, IEnumerable assemblyReferences) { if (mainAssembly == null) throw new ArgumentNullException("mainAssembly"); diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs index 8ab6f9602..b49fe531a 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs @@ -223,13 +223,15 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation internal TypeVisitor substitution; public SpecializedTypeParameter(ITypeParameter baseTp, IMethod specializedOwner) - : base(specializedOwner, baseTp.Index, baseTp.Name, baseTp.Variance, baseTp.Attributes) + : base(specializedOwner, baseTp.Index, baseTp.Name, baseTp.Variance) { // We don't have to consider already-specialized baseTps because // we read the baseTp directly from the unpacked memberDefinition. this.baseTp = baseTp; } - + + public override IReadOnlyList Attributes => baseTp.Attributes; + public override int GetHashCode() { return baseTp.GetHashCode() ^ this.Owner.GetHashCode(); diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/TypeSpecification.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/TypeSpecification.cs index 55d0df833..6d6f75575 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/TypeSpecification.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/TypeSpecification.cs @@ -94,7 +94,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public IType Resolve(ITypeResolveContext context) { - ITypeDefinition td = context.CurrentAssembly.ResolveTypeDefToken(token); + ITypeDefinition td = ((MetadataAssembly)context.CurrentAssembly).GetDefinition(token); if (td != null) return td; return SpecialType.UnknownType; @@ -112,7 +112,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public IType Resolve(ITypeResolveContext context) { - return typeSpec.DecodeSignature(new TypeProvider(context.CurrentAssembly), context); + return typeSpec.DecodeSignature(new TypeProvider(context.CurrentAssembly), new GenericContext(context)); } } diff --git a/ICSharpCode.Decompiler/TypeSystem/KnownTypeReference.cs b/ICSharpCode.Decompiler/TypeSystem/KnownTypeReference.cs index 65ef41514..a85fbbb8d 100644 --- a/ICSharpCode.Decompiler/TypeSystem/KnownTypeReference.cs +++ b/ICSharpCode.Decompiler/TypeSystem/KnownTypeReference.cs @@ -451,7 +451,9 @@ namespace ICSharpCode.Decompiler.TypeSystem public int TypeParameterCount { get { return typeParameterCount; } } - + + public TopLevelTypeName TypeName => new TopLevelTypeName(namespaceName, name, typeParameterCount); + public IType Resolve(ITypeResolveContext context) { return context.Compilation.FindType(knownTypeCode); diff --git a/ICSharpCode.Decompiler/TypeSystem/MetadataAssembly.cs b/ICSharpCode.Decompiler/TypeSystem/MetadataAssembly.cs new file mode 100644 index 000000000..7abb95089 --- /dev/null +++ b/ICSharpCode.Decompiler/TypeSystem/MetadataAssembly.cs @@ -0,0 +1,290 @@ +// Copyright (c) 2018 Daniel Grunwald +// +// 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. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Runtime.InteropServices; +using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.Semantics; +using ICSharpCode.Decompiler.TypeSystem.Implementation; +using ICSharpCode.Decompiler.Util; +using SRM = System.Reflection.Metadata; + +namespace ICSharpCode.Decompiler.TypeSystem +{ + /// + /// Used as context object for metadata TS entities; + /// should be turned into IAssembly implementation when the TS refactoring is complete. + /// + [DebuggerDisplay("")] + public class MetadataAssembly : IAssembly + { + public ICompilation Compilation { get; } + internal readonly Metadata.PEFile PEFile; + internal readonly MetadataReader metadata; + readonly TypeSystemOptions options; + internal readonly TypeProvider TypeProvider; + + readonly MetadataNamespace rootNamespace; + readonly MetadataTypeDefinition[] typeDefs; + readonly MetadataField[] fieldDefs; + + internal MetadataAssembly(ICompilation compilation, Metadata.PEFile peFile, TypeSystemOptions options) + { + this.Compilation = compilation; + this.PEFile = peFile; + this.metadata = peFile.Metadata; + this.options = options; + this.TypeProvider = new TypeProvider(this); + + // assembly metadata + if (metadata.IsAssembly) { + var asmdef = metadata.GetAssemblyDefinition(); + this.AssemblyName = metadata.GetString(asmdef.Name); + this.FullAssemblyName = metadata.GetFullAssemblyName(); + } else { + var moddef = metadata.GetModuleDefinition(); + this.AssemblyName = metadata.GetString(moddef.Name); + this.FullAssemblyName = this.AssemblyName; + } + // create arrays for resolved entities, indexed by row index + this.typeDefs = new MetadataTypeDefinition[metadata.TypeDefinitions.Count + 1]; + this.fieldDefs = new MetadataField[metadata.FieldDefinitions.Count + 1]; + } + + internal string GetString(StringHandle name) + { + return metadata.GetString(name); + } + + public TypeSystemOptions TypeSystemOptions => options; + + #region IAssembly interface + public bool IsMainAssembly => this == Compilation.MainAssembly; + + public string AssemblyName { get; } + public string FullAssemblyName { get; } + + public INamespace RootNamespace => rootNamespace; + + public IEnumerable TopLevelTypeDefinitions => TypeDefinitions.Where(td => td.DeclaringTypeDefinition == null); + + public ITypeDefinition GetTypeDefinition(TopLevelTypeName topLevelTypeName) + { + var typeDefHandle = PEFile.GetTypeDefinition(topLevelTypeName); + return GetDefinition(typeDefHandle); + } + #endregion + + #region InternalsVisibleTo + public bool InternalsVisibleTo(IAssembly assembly) + { + if (this == assembly) + return true; + foreach (string shortName in GetInternalsVisibleTo()) { + if (assembly.AssemblyName == shortName) + return true; + } + return false; + } + + string[] internalsVisibleTo; + + string[] GetInternalsVisibleTo() + { + var result = LazyInit.VolatileRead(ref this.internalsVisibleTo); + if (result != null) { + return result; + } + if (metadata.IsAssembly) { + var list = new List(); + foreach (var attrHandle in metadata.GetAssemblyDefinition().GetCustomAttributes()) { + var attr = metadata.GetCustomAttribute(attrHandle); + if (attr.IsKnownAttribute(metadata, KnownAttribute.InternalsVisibleTo)) { + var attrValue = attr.DecodeValue(this.TypeProvider); + if (attrValue.FixedArguments.Length == 1) { + if (attrValue.FixedArguments[0].Value is string s) { + list.Add(s); + } + } + } + } + result = list.ToArray(); + } else { + result = Empty.Array; + } + return LazyInit.GetOrSet(ref this.internalsVisibleTo, result); + } + #endregion + + #region GetDefinition + /// + /// Gets all types in the assembly, including nested types. + /// + public IEnumerable TypeDefinitions { + get { + for (int row = 1; row < typeDefs.Length; row++) { + var typeDef = LazyInit.VolatileRead(ref typeDefs[row]); + if (typeDef != null) { + yield return typeDef; + } else { + typeDef = new MetadataTypeDefinition(this, MetadataTokens.TypeDefinitionHandle(row)); + yield return LazyInit.GetOrSet(ref typeDefs[row], typeDef); + } + } + } + } + + public ITypeDefinition GetDefinition(TypeDefinitionHandle handle) + { + int row = MetadataTokens.GetRowNumber(handle); + if (row >= typeDefs.Length) + return null; + var typeDef = LazyInit.VolatileRead(ref typeDefs[row]); + if (typeDef != null || handle.IsNil) + return typeDef; + typeDef = new MetadataTypeDefinition(this, handle); + return LazyInit.GetOrSet(ref typeDefs[row], typeDef); + } + + public IField GetDefinition(FieldDefinitionHandle handle) + { + int row = MetadataTokens.GetRowNumber(handle); + if (row >= fieldDefs.Length) + return null; + var field = LazyInit.VolatileRead(ref fieldDefs[row]); + if (field != null || handle.IsNil) + return field; + field = new MetadataField(this, handle); + return LazyInit.GetOrSet(ref fieldDefs[row], field); + } + + public IMethod GetDefinition(MethodDefinitionHandle handle) + { + throw new NotImplementedException(); + } + + public IProperty GetDefinition(PropertyDefinitionHandle handle) + { + throw new NotImplementedException(); + } + + public IEvent GetDefinition(EventDefinitionHandle handle) + { + throw new NotImplementedException(); + } + #endregion + + public IMethod ResolveMethod(EntityHandle methodRefDefSpec, GenericContext context = default) + { + throw new NotImplementedException(); + } + + public IType ResolveType(EntityHandle typeRefDefSpec, GenericContext context = default, CustomAttributeHandleCollection? typeAttributes = null) + { + return MetadataTypeReference.Resolve(typeRefDefSpec, metadata, TypeProvider, context, options, typeAttributes); + } + + #region Module / Assembly attributes + IAttribute[] assemblyAttributes; + IAttribute[] moduleAttributes; + + /// + /// Gets the list of all assembly attributes in the project. + /// + public IReadOnlyList AssemblyAttributes { + get { + var attrs = LazyInit.VolatileRead(ref this.assemblyAttributes); + if (attrs != null) + return attrs; + var b = new AttributeListBuilder(this); + if (metadata.IsAssembly) { + var assembly = metadata.GetAssemblyDefinition(); + b.Add(metadata.GetCustomAttributes(Handle.AssemblyDefinition)); + b.AddSecurityAttributes(assembly.GetDeclarativeSecurityAttributes()); + + // AssemblyVersionAttribute + if (assembly.Version != null) { + b.Add(KnownAttribute.AssemblyVersion, KnownTypeCode.String, assembly.Version.ToString()); + } + } + return LazyInit.GetOrSet(ref this.assemblyAttributes, b.Build()); + } + } + + /// + /// Gets the list of all module attributes in the project. + /// + public IReadOnlyList ModuleAttributes { + get { + var attrs = LazyInit.VolatileRead(ref this.moduleAttributes); + if (attrs != null) + return attrs; + var b = new AttributeListBuilder(this); + b.Add(metadata.GetCustomAttributes(Handle.ModuleDefinition)); + return LazyInit.GetOrSet(ref this.moduleAttributes, b.Build()); + } + } + #endregion + + #region Attribute Helpers + /// + /// Cache for parameterless known attribute types. + /// + readonly IType[] knownAttributeTypes = new IType[KnownAttributes.Count]; + + internal IType GetAttributeType(KnownAttribute attr) + { + var ty = LazyInit.VolatileRead(ref knownAttributeTypes[(int)attr]); + if (ty != null) + return ty; + ty = Compilation.FindType(attr.GetTypeName()); + return LazyInit.GetOrSet(ref knownAttributeTypes[(int)attr], ty); + } + + /// + /// Cache for parameterless known attributes. + /// + readonly IAttribute[] knownAttributes = new IAttribute[KnownAttributes.Count]; + + /// + /// Construct a builtin attribute. + /// + internal IAttribute MakeAttribute(KnownAttribute type) + { + var attr = LazyInit.VolatileRead(ref knownAttributes[(int)type]); + if (attr != null) + return attr; + attr = new DefaultAttribute(GetAttributeType(type)); + return LazyInit.GetOrSet(ref knownAttributes[(int)type], attr); + } + #endregion + + #region Visibility Filter + internal bool IsVisible(FieldAttributes attributes) + { + return true; + } + #endregion + } +} diff --git a/ICSharpCode.Decompiler/TypeSystem/MetadataLoader.cs b/ICSharpCode.Decompiler/TypeSystem/MetadataLoader.cs index da0d2796d..98d20020b 100644 --- a/ICSharpCode.Decompiler/TypeSystem/MetadataLoader.cs +++ b/ICSharpCode.Decompiler/TypeSystem/MetadataLoader.cs @@ -52,7 +52,7 @@ namespace ICSharpCode.Decompiler.TypeSystem /// /// Gets/Sets type system options. /// - public TypeSystemOptions Options { get; set; } + public TypeSystemOptions Options { get; set; } = TypeSystemOptions.Default; /// /// Gets/Sets the cancellation token used by the assembly loader. @@ -555,6 +555,7 @@ namespace ICSharpCode.Decompiler.TypeSystem if ((fieldDefinition.Attributes & FieldAttributes.NotSerialized) != 0) { targetEntity.Attributes.Add(nonSerializedAttribute); } + AddMarshalInfo(fieldDefinition.GetMarshallingDescriptor(), targetEntity.Attributes); AddCustomAttributes(fieldDefinition.GetCustomAttributes(), targetEntity.Attributes); } @@ -852,10 +853,8 @@ namespace ICSharpCode.Decompiler.TypeSystem return TypeKind.Enum; } else if (typeDefinition.IsValueType(module)) { return TypeKind.Struct; - } else if (IsDelegate(module, typeDefinition)) { + } else if (typeDefinition.IsDelegate(module)) { return TypeKind.Delegate; - } else if (IsModule(module, typeDefinition)) { - return TypeKind.Module; } else { return TypeKind.Class; } @@ -888,32 +887,7 @@ namespace ICSharpCode.Decompiler.TypeSystem break; } } - - static bool IsDelegate(MetadataReader currentModule, TypeDefinition type) - { - if (!type.BaseType.IsNil) { - var baseTypeName = type.BaseType.GetFullTypeName(currentModule).ToString(); - if (baseTypeName == "System.MulticastDelegate") - return true; - var thisTypeName = type.GetFullTypeName(currentModule).ToString(); - if (baseTypeName == "Delegate" && thisTypeName != "System.MulticastDelegate") - return true; - } - return false; - } - - static bool IsModule(MetadataReader reader, TypeDefinition type) - { - foreach (var h in type.GetCustomAttributes()) { - var attType = reader.GetCustomAttribute(h).GetAttributeType(reader).GetFullTypeName(reader); - if (attType.ToString() == "Microsoft.VisualBasic.CompilerServices.StandardModuleAttribute" - || attType.ToString() == "System.Runtime.CompilerServices.CompilerGlobalScopeAttribute") { - return true; - } - } - return false; - } - + void InitMembers(TypeDefinition typeDefinition, IUnresolvedTypeDefinition td, IList members) { foreach (MethodDefinitionHandle h in typeDefinition.GetMethods()) { @@ -971,8 +945,7 @@ namespace ICSharpCode.Decompiler.TypeSystem { foreach (var h in typeDefinition.GetCustomAttributes()) { var a = reader.GetCustomAttribute(h); - var type = a.GetAttributeType(reader); - if (!type.IsTopLevelType(reader, "System.Reflection", "DefaultMemberAttribute")) + if (!a.IsKnownAttribute(reader, KnownAttribute.DefaultMember)) continue; var value = a.DecodeValue(Metadata.MetadataExtensions.minimalCorlibTypeProvider); if (value.FixedArguments.Length == 1 && value.FixedArguments[0].Value is string name) @@ -1333,12 +1306,7 @@ namespace ICSharpCode.Decompiler.TypeSystem { if ((Options & TypeSystemOptions.ExtensionMethods) == 0) return false; - foreach (var h in attributes) { - var attr = metadata.GetCustomAttribute(h); - if (attr.IsAttributeType(metadata, "System.Runtime.CompilerServices", "ExtensionAttribute")) - return true; - } - return false; + return attributes.HasKnownAttribute(metadata, KnownAttribute.Extension); } bool IsVisible(MethodAttributes att) @@ -1350,7 +1318,7 @@ namespace ICSharpCode.Decompiler.TypeSystem || att == MethodAttributes.FamORAssem; } - static Accessibility GetAccessibility(MethodAttributes attr) + internal static Accessibility GetAccessibility(MethodAttributes attr) { switch (attr & MethodAttributes.MemberAccessMask) { case MethodAttributes.Public: @@ -1423,7 +1391,7 @@ namespace ICSharpCode.Decompiler.TypeSystem if (type == SignatureTypeCode.SZArray) { foreach (CustomAttributeHandle h in parameter.GetCustomAttributes()) { var att = currentMetadata.GetCustomAttribute(h); - if (att.IsAttributeType(currentMetadata, "System", "ParamArrayAttribute")) { + if (att.IsKnownAttribute(currentMetadata, KnownAttribute.ParamArray)) { p.IsParams = true; break; } @@ -1510,7 +1478,7 @@ namespace ICSharpCode.Decompiler.TypeSystem return f; } - static Accessibility GetAccessibility(FieldAttributes attr) + internal static Accessibility GetAccessibility(FieldAttributes attr) { switch (attr & FieldAttributes.FieldAccessMask) { case FieldAttributes.Public: diff --git a/ICSharpCode.Decompiler/TypeSystem/ModifiedType.cs b/ICSharpCode.Decompiler/TypeSystem/ModifiedType.cs new file mode 100644 index 000000000..bcc1fab4a --- /dev/null +++ b/ICSharpCode.Decompiler/TypeSystem/ModifiedType.cs @@ -0,0 +1,130 @@ +// Copyright (c) 2018 AlphaSierraPapa for the SharpDevelop Team +// +// 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. + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.Decompiler.TypeSystem.Implementation +{ + /// + /// Represents a modopt or modreq type. + /// + public class ModifiedType : TypeWithElementType, IType + { + readonly TypeKind kind; + readonly IType modifier; + + public ModifiedType(IType modifier, IType unmodifiedType, bool isRequired) : base(unmodifiedType) + { + this.kind = isRequired ? TypeKind.ModReq : TypeKind.ModOpt; + this.modifier = modifier ?? throw new ArgumentNullException(nameof(modifier)); + } + + public IType Modifier => modifier; + public override TypeKind Kind => kind; + + public override string NameSuffix => (kind == TypeKind.ModReq ? "modreq" : "modopt") + $"({modifier.FullName})"; + + public override bool? IsReferenceType => elementType.IsReferenceType; + + public override ITypeDefinition GetDefinition() + { + return elementType.GetDefinition(); + } + + public override IEnumerable GetAccessors(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) + { + return elementType.GetAccessors(filter, options); + } + + public override IEnumerable GetConstructors(Predicate filter = null, GetMemberOptions options = GetMemberOptions.IgnoreInheritedMembers) + { + return elementType.GetConstructors(filter, options); + } + + public override IEnumerable GetEvents(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) + { + return elementType.GetEvents(filter, options); + } + + public override IEnumerable GetFields(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) + { + return elementType.GetFields(filter, options); + } + + public override IEnumerable GetMembers(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) + { + return elementType.GetMembers(filter, options); + } + + public override IEnumerable GetMethods(IReadOnlyList typeArguments, Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) + { + return elementType.GetMethods(typeArguments, filter, options); + } + + public override IEnumerable GetMethods(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) + { + return elementType.GetMethods(filter, options); + } + + public override IEnumerable GetNestedTypes(IReadOnlyList typeArguments, Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) + { + return elementType.GetNestedTypes(typeArguments, filter, options); + } + + public override IEnumerable GetNestedTypes(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) + { + return elementType.GetNestedTypes(filter, options); + } + + public override IEnumerable GetProperties(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) + { + return elementType.GetProperties(filter, options); + } + + public override IType VisitChildren(TypeVisitor visitor) + { + var newElementType = elementType.AcceptVisitor(visitor); + var newModifier = modifier.AcceptVisitor(visitor); + if (newModifier != modifier || newElementType != elementType) { + return new ModifiedType(newModifier, newElementType, kind == TypeKind.ModReq); + } + return this; + } + + public override IType AcceptVisitor(TypeVisitor visitor) + { + if (kind == TypeKind.ModReq) + return visitor.VisitModReq(this); + else + return visitor.VisitModOpt(this); + } + + public override bool Equals(IType other) + { + return other is ModifiedType o && kind == o.kind && modifier.Equals(o.modifier) && elementType.Equals(o.elementType); + } + + public override int GetHashCode() + { + unchecked { + return (int)kind ^ (elementType.GetHashCode() * 1344795899) ^ (modifier.GetHashCode() * 901375117); + } + } + } +} diff --git a/ICSharpCode.Decompiler/TypeSystem/TopLevelTypeName.cs b/ICSharpCode.Decompiler/TypeSystem/TopLevelTypeName.cs index a8c778d00..1ece4144e 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TopLevelTypeName.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TopLevelTypeName.cs @@ -27,7 +27,7 @@ namespace ICSharpCode.Decompiler.TypeSystem /// This struct cannot refer to nested classes. /// [Serializable] - public struct TopLevelTypeName : IEquatable + public readonly struct TopLevelTypeName : IEquatable { readonly string namespaceName; readonly string name; diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeKind.cs b/ICSharpCode.Decompiler/TypeSystem/TypeKind.cs index 70ca03b78..9556ef249 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeKind.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeKind.cs @@ -39,8 +39,6 @@ namespace ICSharpCode.Decompiler.TypeSystem /// A that is an enum. /// System.Enum itself is TypeKind.Class Enum, - /// A that is a module (VB). - Module, /// The System.Void type. /// @@ -91,5 +89,13 @@ namespace ICSharpCode.Decompiler.TypeSystem /// /// Tuple, + /// + /// Modified type, with optional modifier. + /// + ModOpt, + /// + /// Modified type, with required modifier. + /// + ModReq, } } diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs b/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs index 473dd8c94..e987bafa2 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs @@ -28,23 +28,26 @@ namespace ICSharpCode.Decompiler.TypeSystem /// /// Allows decoding signatures using decompiler types. /// - sealed class TypeProvider : SRM.ISignatureTypeProvider, SRM.ICustomAttributeTypeProvider + sealed class TypeProvider : ICompilationProvider, + SRM.ISignatureTypeProvider, + SRM.ICustomAttributeTypeProvider { - readonly IAssembly assembly; + readonly MetadataAssembly assembly; readonly ICompilation compilation; public TypeProvider(IAssembly assembly) { - this.assembly = assembly; + this.assembly = (MetadataAssembly)assembly; // TODO: change parameter type instead of casting this.compilation = assembly.Compilation; } public TypeProvider(ICompilation compilation) { - this.assembly = null; - this.compilation = compilation ?? throw new ArgumentNullException(nameof(compilation)); + this.compilation = compilation; } + public ICompilation Compilation => compilation; + public IType GetArrayType(IType elementType, SRM.ArrayShape shape) { return new ArrayType(compilation, elementType, shape.Rank); @@ -65,29 +68,19 @@ namespace ICSharpCode.Decompiler.TypeSystem return new ParameterizedType(genericType, typeArguments); } - public IType GetGenericMethodParameter(ITypeResolveContext genericContext, int index) + public IType GetGenericMethodParameter(GenericContext genericContext, int index) { - // Note: returning type parameter, never type argument. - // Otherwise we risk screwing up the counting for dynamicTypeIndex. - IMethod method = genericContext.CurrentMember as IMethod; - if (method != null && index < method.TypeParameters.Count) { - return method.TypeParameters[index]; - } - return DummyTypeParameter.GetMethodTypeParameter(index); + return genericContext.GetMethodTypeParameter(index); } - public IType GetGenericTypeParameter(ITypeResolveContext genericContext, int index) + public IType GetGenericTypeParameter(GenericContext genericContext, int index) { - ITypeDefinition typeDef = genericContext.CurrentTypeDefinition; - if (typeDef != null && index < typeDef.TypeParameters.Count) { - return typeDef.TypeParameters[index]; - } - return DummyTypeParameter.GetClassTypeParameter(index); + return genericContext.GetClassTypeParameter(index); } public IType GetModifiedType(IType modifier, IType unmodifiedType, bool isRequired) { - return unmodifiedType; + return new ModifiedType(modifier, unmodifiedType, isRequired); } public IType GetPinnedType(IType elementType) @@ -129,7 +122,7 @@ namespace ICSharpCode.Decompiler.TypeSystem public IType GetTypeFromDefinition(SRM.MetadataReader reader, SRM.TypeDefinitionHandle handle, byte rawTypeKind) { - ITypeDefinition td = assembly?.ResolveTypeDefToken(handle); + ITypeDefinition td = assembly?.GetDefinition(handle); if (td != null) return td; bool? isReferenceType = IsReferenceType(reader, handle, rawTypeKind); @@ -155,10 +148,10 @@ namespace ICSharpCode.Decompiler.TypeSystem return new GetClassTypeReference(new FullTypeName(name)) .Resolve(assembly != null ? new SimpleTypeResolveContext(assembly) : new SimpleTypeResolveContext(compilation)); } - - public IType GetTypeFromSpecification(SRM.MetadataReader reader, ITypeResolveContext genericContext, SRM.TypeSpecificationHandle handle, byte rawTypeKind) + + public IType GetTypeFromSpecification(SRM.MetadataReader reader, GenericContext genericContext, SRM.TypeSpecificationHandle handle, byte rawTypeKind) { - return reader.GetTypeSpecification(handle).DecodeSignature(this, genericContext); + return reader.GetTypeSpecification(handle).DecodeSignature(this, genericContext); } public SRM.PrimitiveTypeCode GetUnderlyingEnumType(IType type) diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs index a8fb66936..2295b2641 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs @@ -205,8 +205,38 @@ namespace ICSharpCode.Decompiler.TypeSystem var def = type.GetDefinition(); return def != null && def.KnownTypeCode == knownType; } + + /// + /// Gets whether the type is the specified known type. + /// For generic known types, this returns true any parameterization of the type (and also for the definition itself). + /// + internal static bool IsKnownType(this IType type, KnownAttribute knownType) + { + var def = type.GetDefinition(); + return def != null && def.FullTypeName.IsKnownType(knownType); + } + + public static bool IsKnownType(this FullTypeName typeName, KnownTypeCode knownType) + { + return typeName == KnownTypeReference.Get(knownType).TypeName; + } + + public static bool IsKnownType(this TopLevelTypeName typeName, KnownTypeCode knownType) + { + return typeName == KnownTypeReference.Get(knownType).TypeName; + } + + internal static bool IsKnownType(this FullTypeName typeName, KnownAttribute knownType) + { + return typeName == knownType.GetTypeName(); + } + + internal static bool IsKnownType(this TopLevelTypeName typeName, KnownAttribute knownType) + { + return typeName == knownType.GetTypeName(); + } #endregion - + #region GetDelegateInvokeMethod /// /// Gets the invoke method for a delegate type. @@ -235,18 +265,13 @@ namespace ICSharpCode.Decompiler.TypeSystem return TreeTraversal.PreOrder(assembly.TopLevelTypeDefinitions, t => t.NestedTypes); } - public static IEnumerable GetAllTypeDefinitions (this IAssembly assembly) - { - return TreeTraversal.PreOrder(assembly.TopLevelTypeDefinitions, t => t.NestedTypes); - } - /// /// Gets all type definitions in the compilation. /// This may include types from referenced assemblies that are not accessible in the main assembly. /// public static IEnumerable GetAllTypeDefinitions (this ICompilation compilation) { - return compilation.Assemblies.SelectMany(a => a.GetAllTypeDefinitions()); + return compilation.Assemblies.SelectMany(a => a.TypeDefinitions); } /// diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeVisitor.cs b/ICSharpCode.Decompiler/TypeSystem/TypeVisitor.cs index 152d397e0..4816587b0 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeVisitor.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeVisitor.cs @@ -16,6 +16,9 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System; +using ICSharpCode.Decompiler.TypeSystem.Implementation; + namespace ICSharpCode.Decompiler.TypeSystem { /// @@ -62,5 +65,15 @@ namespace ICSharpCode.Decompiler.TypeSystem { return type.VisitChildren(this); } + + public virtual IType VisitModReq(ModifiedType type) + { + return type.VisitChildren(this); + } + + public virtual IType VisitModOpt(ModifiedType type) + { + return type.VisitChildren(this); + } } } diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs index 2bec9dd16..cf97f377d 100644 --- a/ILSpy/Languages/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -41,6 +41,7 @@ using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; using System.Reflection; using ICSharpCode.Decompiler.Disassembler; +using GenericContext = ICSharpCode.Decompiler.Metadata.GenericContext; namespace ICSharpCode.ILSpy { @@ -404,7 +405,7 @@ namespace ICSharpCode.ILSpy } } - public override string TypeToString(Entity type, GenericContext genericContext = null, bool includeNamespace = true) + public override string TypeToString(Entity type, Decompiler.Metadata.GenericContext genericContext = null, bool includeNamespace = true) { if (type.Handle.IsNil) throw new ArgumentNullException(nameof(type)); diff --git a/ILSpy/SearchPane.cs b/ILSpy/SearchPane.cs index a3ba16a5f..434d7e464 100644 --- a/ILSpy/SearchPane.cs +++ b/ILSpy/SearchPane.cs @@ -221,7 +221,7 @@ namespace ICSharpCode.ILSpy continue; CancellationToken cancellationToken = cts.Token; - foreach (var type in module.TypeDefinitions) { + foreach (var type in module.TopLevelTypeDefinitions) { cancellationToken.ThrowIfCancellationRequested(); searcher.Search(type, language, AddResult); }