From 55b100e1671e305afebfc68de62f549644c58343 Mon Sep 17 00:00:00 2001 From: James May Date: Fri, 6 Oct 2023 00:35:16 +1100 Subject: [PATCH] add ExportedType, nested TypeReferences --- .../Disassembler/ReflectionDisassembler.cs | 4 +- .../ICSharpCode.Decompiler.csproj | 1 + .../Metadata/AssemblyReferences.cs | 21 +++- .../Metadata/ExportedType.cs | 100 ++++++++++++++++++ .../Metadata/TypeReference.cs | 17 +++ .../TypeSystem/MetadataModule.cs | 6 +- ILSpy/Languages/CSharpLanguage.cs | 1 + ILSpy/Languages/Language.cs | 3 + .../CorTables/ExportedTypeTableTreeNode.cs | 6 +- ...ssemblyReferenceReferencedTypesTreeNode.cs | 7 +- ILSpy/TreeNodes/ExportedTypeTreeNode.cs | 62 +++++++++++ ILSpy/TreeNodes/TypeReferenceTreeNode.cs | 5 +- 12 files changed, 223 insertions(+), 10 deletions(-) create mode 100644 ICSharpCode.Decompiler/Metadata/ExportedType.cs create mode 100644 ILSpy/TreeNodes/ExportedTypeTreeNode.cs diff --git a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs index b268ff30148..d4a2c53a6a1 100644 --- a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs @@ -29,6 +29,8 @@ using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Metadata; +using SRM = System.Reflection.Metadata; + namespace ICSharpCode.Decompiler.Disassembler { /// @@ -2016,7 +2018,7 @@ public void WriteModuleHeader(PEFile module, bool skipMVID = false) { var metadata = module.Metadata; - void WriteExportedType(ExportedType exportedType) + void WriteExportedType(SRM.ExportedType exportedType) { if (!exportedType.Namespace.IsNil) { diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 3c57114ce5b..4583a60f2c6 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -358,6 +358,7 @@ + diff --git a/ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs b/ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs index 06b5977b536..6d00bdaf7c3 100644 --- a/ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs +++ b/ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs @@ -277,7 +277,6 @@ public string FullName { } ImmutableArray typeReferences; - public ImmutableArray TypeReferences { get { var value = typeReferences; @@ -287,7 +286,7 @@ public ImmutableArray TypeReferences { .Select(r => new TypeReference(Metadata, r)) .Where(r => r.ResolutionScope == Handle) .OrderBy(r => r.Namespace) - .ThenBy(r=> r.Name) + .ThenBy(r => r.Name) .ToImmutableArray(); typeReferences = value; } @@ -295,6 +294,24 @@ public ImmutableArray TypeReferences { } } + ImmutableArray exportedTypes; + public ImmutableArray ExportedTypes { + get { + var value = exportedTypes; + if (value.IsDefault) + { + value = Metadata.ExportedTypes + .Select(r => new ExportedType(Metadata, r)) + .Where(r => r.Implementation == Handle) + .OrderBy(r => r.Namespace) + .ThenBy(r => r.Name) + .ToImmutableArray(); + exportedTypes = value; + } + return value; + } + } + public AssemblyReference(MetadataReader metadata, AssemblyReferenceHandle handle) { if (metadata == null) diff --git a/ICSharpCode.Decompiler/Metadata/ExportedType.cs b/ICSharpCode.Decompiler/Metadata/ExportedType.cs new file mode 100644 index 00000000000..33371ea6333 --- /dev/null +++ b/ICSharpCode.Decompiler/Metadata/ExportedType.cs @@ -0,0 +1,100 @@ +// Copyright (c) 2023 James May +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#nullable enable + +using System; +using System.Collections.Immutable; +using System.Linq; +using System.Reflection; +using System.Reflection.Metadata; + +namespace ICSharpCode.Decompiler.Metadata +{ +#if !VSADDIN + public sealed class ExportedType + { + readonly System.Reflection.Metadata.ExportedType entry; + + public MetadataReader Metadata { get; } + public ExportedTypeHandle Handle { get; } + + string? name; + public string Name { + get { + try + { + return name ??= Metadata.GetString(entry.Name); + } + catch (BadImageFormatException) + { + return name = $"ET:{Handle}"; + } + } + } + + string? @namespace; + public string Namespace { + get { + try + { + return @namespace ??= Metadata.GetString(entry.Namespace); + } + catch (BadImageFormatException) + { + return @namespace = $"namespace(ET:{Handle})"; + } + } + } + + public EntityHandle Implementation => entry.Implementation; + public TypeAttributes Attributes => entry.Attributes; + public bool IsForwarder => entry.IsForwarder; + public NamespaceDefinition NamespaceDefinition => Metadata.GetNamespaceDefinition(entry.NamespaceDefinition); + + ImmutableArray exportedTypes; + public ImmutableArray ExportedTypes { + get { + var value = exportedTypes; + if (value.IsDefault) + { + value = Metadata.ExportedTypes + .Select(r => new ExportedType(Metadata, r)) + .Where(r => r.Implementation == Handle) + .OrderBy(r => r.Namespace) + .ThenBy(r => r.Name) + .ToImmutableArray(); + exportedTypes = value; + } + return value; + } + } + + public ExportedType(MetadataReader metadata, ExportedTypeHandle handle) + { + Metadata = metadata ?? throw new ArgumentNullException(nameof(metadata)); + if (handle.IsNil) + throw new ArgumentNullException(nameof(handle)); + Handle = handle; + entry = metadata.GetExportedType(handle); + } + + public override string ToString() => $"{Namespace}::{Name}"; + } +#endif +} diff --git a/ICSharpCode.Decompiler/Metadata/TypeReference.cs b/ICSharpCode.Decompiler/Metadata/TypeReference.cs index 78d903c5313..adf44d1e677 100644 --- a/ICSharpCode.Decompiler/Metadata/TypeReference.cs +++ b/ICSharpCode.Decompiler/Metadata/TypeReference.cs @@ -80,6 +80,23 @@ public ImmutableArray MemberReferences { } } + ImmutableArray typeReferences; + public ImmutableArray TypeReferences { + get { + var value = typeReferences; + if (value.IsDefault) + { + value = Metadata.TypeReferences + .Select(r => new TypeReference(Metadata, r)) + .Where(r => r.ResolutionScope == Handle) + .OrderBy(r => r.Name) + .ToImmutableArray(); + typeReferences = value; + } + return value; + } + } + public TypeReference(MetadataReader metadata, TypeReferenceHandle handle) { Metadata = metadata ?? throw new ArgumentNullException(nameof(metadata)); diff --git a/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs b/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs index a7f4943736b..4b6e0eb9c1d 100644 --- a/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs +++ b/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs @@ -29,6 +29,8 @@ using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; +using SRM = System.Reflection.Metadata; + namespace ICSharpCode.Decompiler.TypeSystem { /// @@ -885,7 +887,7 @@ void AddTypeForwarderAttributes(ref AttributeListBuilder b) } } - IType ResolveForwardedType(ExportedType forwarder) + IType ResolveForwardedType(SRM.ExportedType forwarder) { IModule module = ResolveModule(forwarder); var typeName = forwarder.GetFullTypeName(metadata); @@ -904,7 +906,7 @@ IType ResolveForwardedType(ExportedType forwarder) } return new UnknownType(typeName); - IModule ResolveModule(ExportedType type) + IModule ResolveModule(SRM.ExportedType type) { switch (type.Implementation.Kind) { diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs index 3c7034d6082..b4b13d37a94 100644 --- a/ILSpy/Languages/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -719,6 +719,7 @@ public override string GetEntityName(PEFile module, EntityHandle handle, bool fu if (fullName && !declaringType.IsNil) return ToCSharpString(metadata, declaringType, fullName, omitGenerics) + "." + metadata.GetString(pd.Name); return metadata.GetString(pd.Name); + case HandleKind.ExportedType: case HandleKind.TypeReference: case HandleKind.MemberReference: // TODO: C# Implementation diff --git a/ILSpy/Languages/Language.cs b/ILSpy/Languages/Language.cs index 727082229f8..173469b880a 100644 --- a/ILSpy/Languages/Language.cs +++ b/ILSpy/Languages/Language.cs @@ -518,6 +518,9 @@ public virtual string GetEntityName(PEFile module, EntityHandle handle, bool ful if (fullName) return EscapeName(declaringType.GetFullTypeName(metadata).ToILNameString(omitGenerics) + "." + metadata.GetString(pd.Name)); return EscapeName(metadata.GetString(pd.Name)); + case HandleKind.ExportedType: + var et = metadata.GetExportedType((ExportedTypeHandle)handle); + return et.GetFullTypeName(metadata).ToILNameString(); case HandleKind.TypeReference: var tr = metadata.GetTypeReference((TypeReferenceHandle)handle); if (fullName) diff --git a/ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs b/ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs index b95a5a9e8e9..db4844b1685 100644 --- a/ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs @@ -25,6 +25,8 @@ using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Metadata; +using SRM = System.Reflection.Metadata; + namespace ICSharpCode.ILSpy.Metadata { internal class ExportedTypeTableTreeNode : MetadataTableTreeNode @@ -76,7 +78,7 @@ struct ExportedTypeEntry readonly PEFile module; readonly MetadataReader metadata; readonly ExportedTypeHandle handle; - readonly ExportedType type; + readonly SRM.ExportedType type; public int RID => MetadataTokens.GetRowNumber(handle); @@ -121,7 +123,7 @@ public void OnImplementationClick() string implementationTooltip; public string ImplementationTooltip => GenerateTooltip(ref implementationTooltip, module, type.Implementation); - public ExportedTypeEntry(int metadataOffset, PEFile module, ExportedTypeHandle handle, ExportedType type) + public ExportedTypeEntry(int metadataOffset, PEFile module, ExportedTypeHandle handle, SRM.ExportedType type) { this.metadataOffset = metadataOffset; this.module = module; diff --git a/ILSpy/TreeNodes/AssemblyReferenceReferencedTypesTreeNode.cs b/ILSpy/TreeNodes/AssemblyReferenceReferencedTypesTreeNode.cs index 0946d355a97..6d4734a916e 100644 --- a/ILSpy/TreeNodes/AssemblyReferenceReferencedTypesTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyReferenceReferencedTypesTreeNode.cs @@ -39,16 +39,19 @@ public AssemblyReferenceReferencedTypesTreeNode(PEFile module, AssemblyReference this.LazyLoading = true; } - public override object Text => $"Referenced Types ({r.TypeReferences.Length})"; + public override object Text => $"Referenced Types ({r.TypeReferences.Length + r.ExportedTypes.Length})"; public override object Icon => Images.Class; protected override void LoadChildren() { foreach (var typeRef in r.TypeReferences) this.Children.Add(new TypeReferenceTreeNode(module, typeRef)); + + foreach (var exportedType in r.ExportedTypes) + this.Children.Add(new ExportedTypeTreeNode(module, exportedType)); } - public override bool ShowExpander => !r.TypeReferences.IsEmpty; + public override bool ShowExpander => !r.TypeReferences.IsEmpty || !r.ExportedTypes.IsEmpty; public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { diff --git a/ILSpy/TreeNodes/ExportedTypeTreeNode.cs b/ILSpy/TreeNodes/ExportedTypeTreeNode.cs new file mode 100644 index 00000000000..fa31a7fbf3e --- /dev/null +++ b/ILSpy/TreeNodes/ExportedTypeTreeNode.cs @@ -0,0 +1,62 @@ +// Copyright (c) 2023 James May +// +// 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 ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.Metadata; + +using ExportedType = ICSharpCode.Decompiler.Metadata.ExportedType; + +namespace ICSharpCode.ILSpy.TreeNodes +{ + /// + /// exported type within assembly reference list. + /// + public sealed class ExportedTypeTreeNode : ILSpyTreeNode + { + readonly PEFile module; + private readonly ExportedType r; + + public ExportedTypeTreeNode(PEFile module, ExportedType r) + { + this.module = module ?? throw new ArgumentNullException(nameof(module)); + this.r = r ?? throw new ArgumentNullException(nameof(r)); + + this.LazyLoading = true; + } + + public override object Text + => Language.GetEntityName(module, r.Handle, fullName: true, omitGenerics: false) + GetSuffixString(r.Handle); + + public override object Icon => Images.Library; + + protected override void LoadChildren() + { + foreach (var exportedType in r.ExportedTypes) + this.Children.Add(new ExportedTypeTreeNode(module, exportedType)); + } + + public override bool ShowExpander => !r.ExportedTypes.IsEmpty; + + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + { + language.WriteCommentLine(output, $"{Language.GetEntityName(module, r.Handle, fullName: true, omitGenerics: false)} (Exported, IsForwarder: {r.IsForwarder}, Attributes: {r.Attributes})"); + } + } +} diff --git a/ILSpy/TreeNodes/TypeReferenceTreeNode.cs b/ILSpy/TreeNodes/TypeReferenceTreeNode.cs index c015854cea8..d82d48fc3ed 100644 --- a/ILSpy/TreeNodes/TypeReferenceTreeNode.cs +++ b/ILSpy/TreeNodes/TypeReferenceTreeNode.cs @@ -48,11 +48,14 @@ public override object Text protected override void LoadChildren() { + foreach (var typeRef in r.TypeReferences) + this.Children.Add(new TypeReferenceTreeNode(module, typeRef)); + foreach (var memberRef in r.MemberReferences) this.Children.Add(new MemberReferenceTreeNode(module, memberRef)); } - public override bool ShowExpander => !r.MemberReferences.IsEmpty; + public override bool ShowExpander => !r.TypeReferences.IsEmpty || !r.MemberReferences.IsEmpty; public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) {