From 2a6e1414651620a5549519da3932c121d74fa15e Mon Sep 17 00:00:00 2001 From: James May Date: Thu, 5 Oct 2023 23:20:24 +1100 Subject: [PATCH 1/8] add "Referenced Types" under References --- .../ICSharpCode.Decompiler.csproj | 1 + .../Metadata/AssemblyReferences.cs | 19 ++++ .../Metadata/TypeReferenceMetadata.cs | 95 +++++++++++++++++++ ...ssemblyReferenceReferencedTypesTreeNode.cs | 60 ++++++++++++ ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs | 16 ++-- ILSpy/TreeNodes/ReferenceFolderTreeNode.cs | 2 +- ILSpy/TreeNodes/TypeReferenceTreeNode.cs | 59 ++++++++++++ 7 files changed, 245 insertions(+), 7 deletions(-) create mode 100644 ICSharpCode.Decompiler/Metadata/TypeReferenceMetadata.cs create mode 100644 ILSpy/TreeNodes/AssemblyReferenceReferencedTypesTreeNode.cs create mode 100644 ILSpy/TreeNodes/TypeReferenceTreeNode.cs diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 87ee648aa8..bb3b5aab23 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -362,6 +362,7 @@ + diff --git a/ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs b/ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs index d1b642a308..b65380ff79 100644 --- a/ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs +++ b/ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs @@ -19,6 +19,7 @@ #nullable enable using System; +using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; @@ -275,6 +276,24 @@ public string FullName { return bytes; } + ImmutableArray typeReferences; + public ImmutableArray TypeReferences { + get { + var value = typeReferences; + if (value.IsDefault) + { + value = Metadata.TypeReferences + .Select(r => new TypeReferenceMetadata(Metadata, r)) + .Where(r => r.ResolutionScope == Handle) + .OrderBy(r => r.Namespace) + .ThenBy(r => r.Name) + .ToImmutableArray(); + typeReferences = value; + } + return value; + } + } + public AssemblyReference(MetadataReader metadata, AssemblyReferenceHandle handle) { if (metadata == null) diff --git a/ICSharpCode.Decompiler/Metadata/TypeReferenceMetadata.cs b/ICSharpCode.Decompiler/Metadata/TypeReferenceMetadata.cs new file mode 100644 index 0000000000..0c2c4b5dbc --- /dev/null +++ b/ICSharpCode.Decompiler/Metadata/TypeReferenceMetadata.cs @@ -0,0 +1,95 @@ +// 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.Metadata; + +namespace ICSharpCode.Decompiler.Metadata +{ +#if !VSADDIN + public sealed class TypeReferenceMetadata + { + readonly TypeReference entry; + + public MetadataReader Metadata { get; } + public TypeReferenceHandle Handle { get; } + + string? name; + public string Name { + get { + try + { + return name ??= Metadata.GetString(entry.Name); + } + catch (BadImageFormatException) + { + return name = $"TR:{Handle}"; + } + } + } + + string? @namespace; + public string Namespace { + get { + try + { + return @namespace ??= Metadata.GetString(entry.Namespace); + } + catch (BadImageFormatException) + { + return @namespace = $"namespace(TR:{Handle})"; + } + } + } + + public EntityHandle ResolutionScope => entry.ResolutionScope; + + ImmutableArray typeReferences; + public ImmutableArray TypeReferences { + get { + var value = typeReferences; + if (value.IsDefault) + { + value = Metadata.TypeReferences + .Select(r => new TypeReferenceMetadata(Metadata, r)) + .Where(r => r.ResolutionScope == Handle) + .OrderBy(r => r.Name) + .ToImmutableArray(); + typeReferences = value; + } + return value; + } + } + + public TypeReferenceMetadata(MetadataReader metadata, TypeReferenceHandle handle) + { + Metadata = metadata ?? throw new ArgumentNullException(nameof(metadata)); + if (handle.IsNil) + throw new ArgumentNullException(nameof(handle)); + Handle = handle; + entry = metadata.GetTypeReference(handle); + } + + public override string ToString() => $"{Namespace}::{Name}"; + } +#endif +} diff --git a/ILSpy/TreeNodes/AssemblyReferenceReferencedTypesTreeNode.cs b/ILSpy/TreeNodes/AssemblyReferenceReferencedTypesTreeNode.cs new file mode 100644 index 0000000000..0946d355a9 --- /dev/null +++ b/ILSpy/TreeNodes/AssemblyReferenceReferencedTypesTreeNode.cs @@ -0,0 +1,60 @@ +// 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; + +namespace ICSharpCode.ILSpy.TreeNodes +{ + /// + /// Referenced Types node in assembly reference list + /// + public sealed class AssemblyReferenceReferencedTypesTreeNode : ILSpyTreeNode + { + private readonly PEFile module; + private readonly AssemblyReference r; + + public AssemblyReferenceReferencedTypesTreeNode(PEFile module, AssemblyReference r) + { + this.module = module ?? throw new ArgumentNullException(nameof(module)); + this.r = r ?? throw new ArgumentNullException(nameof(r)); + + this.LazyLoading = true; + } + + public override object Text => $"Referenced Types ({r.TypeReferences.Length})"; + public override object Icon => Images.Class; + + protected override void LoadChildren() + { + foreach (var typeRef in r.TypeReferences) + this.Children.Add(new TypeReferenceTreeNode(module, typeRef)); + } + + public override bool ShowExpander => !r.TypeReferences.IsEmpty; + + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + { + EnsureLazyChildren(); + foreach (ILSpyTreeNode child in Children) + child.Decompile(language, output, options); + } + } +} diff --git a/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs b/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs index ad003ffcc8..8aa2501a37 100644 --- a/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs @@ -29,17 +29,19 @@ namespace ICSharpCode.ILSpy.TreeNodes /// public sealed class AssemblyReferenceTreeNode : ILSpyTreeNode { + readonly PEFile module; readonly AssemblyReference r; readonly AssemblyTreeNode parentAssembly; - public AssemblyReferenceTreeNode(AssemblyReference r, AssemblyTreeNode parentAssembly) + public AssemblyReferenceTreeNode(PEFile module, AssemblyReference r, AssemblyTreeNode parentAssembly) { + this.module = module ?? throw new ArgumentNullException(nameof(module)); this.r = r ?? throw new ArgumentNullException(nameof(r)); this.parentAssembly = parentAssembly ?? throw new ArgumentNullException(nameof(parentAssembly)); this.LazyLoading = true; } - public IAssemblyReference AssemblyNameReference => r; + public AssemblyReference AssemblyReference => r; public override object Text { get { return Language.EscapeName(r.Name) + GetSuffixString(r.Handle); } @@ -75,12 +77,14 @@ public override void ActivateItem(System.Windows.RoutedEventArgs e) protected override void LoadChildren() { + this.Children.Add(new AssemblyReferenceReferencedTypesTreeNode(module, r)); + var resolver = parentAssembly.LoadedAssembly.GetAssemblyResolver(MainWindow.Instance.CurrentDecompilerSettings.AutoLoadAssemblyReferences); - var module = resolver.Resolve(r); - if (module != null) + var referencedModule = resolver.Resolve(r); + if (referencedModule != null) { - foreach (var childRef in module.AssemblyReferences) - this.Children.Add(new AssemblyReferenceTreeNode(childRef, parentAssembly)); + foreach (var childRef in referencedModule.AssemblyReferences) + this.Children.Add(new AssemblyReferenceTreeNode(referencedModule, childRef, parentAssembly)); } } diff --git a/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs b/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs index 347e07d670..adb72732c6 100644 --- a/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs +++ b/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs @@ -49,7 +49,7 @@ protected override void LoadChildren() { var metadata = module.Metadata; foreach (var r in module.AssemblyReferences.OrderBy(r => r.Name)) - this.Children.Add(new AssemblyReferenceTreeNode(r, parentAssembly)); + this.Children.Add(new AssemblyReferenceTreeNode(module, r, parentAssembly)); foreach (var r in metadata.GetModuleReferences().OrderBy(r => metadata.GetString(metadata.GetModuleReference(r).Name))) this.Children.Add(new ModuleReferenceTreeNode(parentAssembly, r, metadata)); } diff --git a/ILSpy/TreeNodes/TypeReferenceTreeNode.cs b/ILSpy/TreeNodes/TypeReferenceTreeNode.cs new file mode 100644 index 0000000000..aeb4519bbd --- /dev/null +++ b/ILSpy/TreeNodes/TypeReferenceTreeNode.cs @@ -0,0 +1,59 @@ +// 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; + +namespace ICSharpCode.ILSpy.TreeNodes +{ + /// + /// referenced type within assembly reference list. + /// + public sealed class TypeReferenceTreeNode : ILSpyTreeNode + { + readonly PEFile module; + private readonly TypeReferenceMetadata r; + + public TypeReferenceTreeNode(PEFile module, TypeReferenceMetadata 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.Class; + + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + { + language.WriteCommentLine(output, $"{Language.GetEntityName(module, r.Handle, fullName: true, omitGenerics: false)}"); + EnsureLazyChildren(); + foreach (ILSpyTreeNode child in Children) + { + output.Indent(); + child.Decompile(language, output, options); + output.Unindent(); + } + } + } +} From dbb834022f8575cbde6446f97af150356eb45dc2 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 16 Mar 2024 12:51:21 +0100 Subject: [PATCH 2/8] Add treenode for MemberReferences. --- .../ICSharpCode.Decompiler.csproj | 1 + .../Metadata/MemberReferenceMetadata.cs | 69 +++++++++++++++++++ .../Metadata/TypeReferenceMetadata.cs | 17 +++++ ILSpy/TreeNodes/MemberReferenceTreeNode.cs | 60 ++++++++++++++++ ILSpy/TreeNodes/TypeReferenceTreeNode.cs | 8 +++ 5 files changed, 155 insertions(+) create mode 100644 ICSharpCode.Decompiler/Metadata/MemberReferenceMetadata.cs create mode 100644 ILSpy/TreeNodes/MemberReferenceTreeNode.cs diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index bb3b5aab23..7378034a73 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -362,6 +362,7 @@ + diff --git a/ICSharpCode.Decompiler/Metadata/MemberReferenceMetadata.cs b/ICSharpCode.Decompiler/Metadata/MemberReferenceMetadata.cs new file mode 100644 index 0000000000..001f58776f --- /dev/null +++ b/ICSharpCode.Decompiler/Metadata/MemberReferenceMetadata.cs @@ -0,0 +1,69 @@ +// 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.Reflection.Metadata; + +namespace ICSharpCode.Decompiler.Metadata +{ +#if !VSADDIN + /// + /// Convenience wrapper for and . + /// + public sealed class MemberReferenceMetadata + { + readonly MemberReference entry; + + public MetadataReader Metadata { get; } + public MemberReferenceHandle Handle { get; } + + string? name; + + public string Name { + get { + try + { + return name ??= Metadata.GetString(entry.Name); + } + catch (BadImageFormatException) + { + return name = $"MR:{Handle}"; + } + } + } + + public EntityHandle Parent => entry.Parent; + + public MemberReferenceKind MemberReferenceKind => entry.GetKind(); + + public MemberReferenceMetadata(MetadataReader metadata, MemberReferenceHandle handle) + { + Metadata = metadata ?? throw new ArgumentNullException(nameof(metadata)); + if (handle.IsNil) + throw new ArgumentNullException(nameof(handle)); + Handle = handle; + entry = metadata.GetMemberReference(handle); + } + + public override string ToString() + => Name; + } +#endif +} diff --git a/ICSharpCode.Decompiler/Metadata/TypeReferenceMetadata.cs b/ICSharpCode.Decompiler/Metadata/TypeReferenceMetadata.cs index 0c2c4b5dbc..fb2c826048 100644 --- a/ICSharpCode.Decompiler/Metadata/TypeReferenceMetadata.cs +++ b/ICSharpCode.Decompiler/Metadata/TypeReferenceMetadata.cs @@ -63,6 +63,23 @@ public string Namespace { public EntityHandle ResolutionScope => entry.ResolutionScope; + ImmutableArray memberReferences; + public ImmutableArray MemberReferences { + get { + var value = memberReferences; + if (value.IsDefault) + { + value = Metadata.MemberReferences + .Select(r => new MemberReferenceMetadata(Metadata, r)) + .Where(r => r.Parent == Handle) + .OrderBy(r => r.Name) + .ToImmutableArray(); + memberReferences = value; + } + return value; + } + } + ImmutableArray typeReferences; public ImmutableArray TypeReferences { get { diff --git a/ILSpy/TreeNodes/MemberReferenceTreeNode.cs b/ILSpy/TreeNodes/MemberReferenceTreeNode.cs new file mode 100644 index 0000000000..c694430295 --- /dev/null +++ b/ILSpy/TreeNodes/MemberReferenceTreeNode.cs @@ -0,0 +1,60 @@ +// 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.Reflection.Metadata; + +using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.Metadata; + +namespace ICSharpCode.ILSpy.TreeNodes +{ + /// + /// Reference to member from assembly reference list. + /// + public sealed class MemberReferenceTreeNode : ILSpyTreeNode + { + readonly PEFile module; + private readonly MemberReferenceMetadata r; + + public MemberReferenceTreeNode(PEFile module, MemberReferenceMetadata r) + { + this.module = module ?? throw new ArgumentNullException(nameof(module)); + this.r = r; + } + + public override object Text => r.Name + GetSuffixString(r.Handle); + + public override object Icon => r.MemberReferenceKind switch { + MemberReferenceKind.Method => Images.Method, + MemberReferenceKind.Field => Images.Field, + _ => Images.Class, + }; + + public string Signature => Language.GetEntityName(module, r.Handle, fullName: true, omitGenerics: false); + + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + { + language.WriteCommentLine(output, Signature); + } + + public override object ToolTip => Signature; + } +} diff --git a/ILSpy/TreeNodes/TypeReferenceTreeNode.cs b/ILSpy/TreeNodes/TypeReferenceTreeNode.cs index aeb4519bbd..a0f76fa0c9 100644 --- a/ILSpy/TreeNodes/TypeReferenceTreeNode.cs +++ b/ILSpy/TreeNodes/TypeReferenceTreeNode.cs @@ -44,6 +44,14 @@ public override object Text public override object Icon => Images.Class; + protected override void LoadChildren() + { + foreach (var memberRef in r.MemberReferences) + this.Children.Add(new MemberReferenceTreeNode(module, memberRef)); + } + + public override bool ShowExpander => !r.MemberReferences.IsEmpty; + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, $"{Language.GetEntityName(module, r.Handle, fullName: true, omitGenerics: false)}"); From f9c9733d8f51e51f6cf3cdf8b1c57e410df2f885 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 16 Mar 2024 15:20:06 +0100 Subject: [PATCH 3/8] add nested types --- ILSpy/TreeNodes/TypeReferenceTreeNode.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ILSpy/TreeNodes/TypeReferenceTreeNode.cs b/ILSpy/TreeNodes/TypeReferenceTreeNode.cs index a0f76fa0c9..83872ebe6f 100644 --- a/ILSpy/TreeNodes/TypeReferenceTreeNode.cs +++ b/ILSpy/TreeNodes/TypeReferenceTreeNode.cs @@ -46,11 +46,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) { From 514551ef9f8c601c74fc75e1609238c6a39b9457 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 16 Mar 2024 15:23:24 +0100 Subject: [PATCH 4/8] Add exported types --- .../ICSharpCode.Decompiler.csproj | 1 + .../Metadata/AssemblyReferences.cs | 18 +++ .../Metadata/ExportedTypeMetadata.cs | 103 ++++++++++++++++++ ...ssemblyReferenceReferencedTypesTreeNode.cs | 7 +- ILSpy/TreeNodes/ExportedTypeTreeNode.cs | 61 +++++++++++ 5 files changed, 188 insertions(+), 2 deletions(-) create mode 100644 ICSharpCode.Decompiler/Metadata/ExportedTypeMetadata.cs create mode 100644 ILSpy/TreeNodes/ExportedTypeTreeNode.cs diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 7378034a73..5c4539d5a5 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -362,6 +362,7 @@ + diff --git a/ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs b/ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs index b65380ff79..983eaf0f58 100644 --- a/ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs +++ b/ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs @@ -294,6 +294,24 @@ public ImmutableArray TypeReferences { } } + ImmutableArray exportedTypes; + public ImmutableArray ExportedTypes { + get { + var value = exportedTypes; + if (value.IsDefault) + { + value = Metadata.ExportedTypes + .Select(r => new ExportedTypeMetadata(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/ExportedTypeMetadata.cs b/ICSharpCode.Decompiler/Metadata/ExportedTypeMetadata.cs new file mode 100644 index 0000000000..3bada57eed --- /dev/null +++ b/ICSharpCode.Decompiler/Metadata/ExportedTypeMetadata.cs @@ -0,0 +1,103 @@ +// 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 + /// + /// Convenience wrapper for and . + /// + public sealed class ExportedTypeMetadata + { + readonly 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 ExportedTypeMetadata(Metadata, r)) + .Where(r => r.Implementation == Handle) + .OrderBy(r => r.Namespace) + .ThenBy(r => r.Name) + .ToImmutableArray(); + exportedTypes = value; + } + return value; + } + } + + public ExportedTypeMetadata(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/ILSpy/TreeNodes/AssemblyReferenceReferencedTypesTreeNode.cs b/ILSpy/TreeNodes/AssemblyReferenceReferencedTypesTreeNode.cs index 0946d355a9..6d4734a916 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 0000000000..bb21545c8f --- /dev/null +++ b/ILSpy/TreeNodes/ExportedTypeTreeNode.cs @@ -0,0 +1,61 @@ +// 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 ICSharpCode.Decompiler.TypeSystem; + +namespace ICSharpCode.ILSpy.TreeNodes +{ + /// + /// exported type within assembly reference list. + /// + public sealed class ExportedTypeTreeNode : ILSpyTreeNode + { + readonly PEFile module; + private readonly ExportedTypeMetadata r; + + public ExportedTypeTreeNode(PEFile module, ExportedTypeMetadata 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})"); + } + } +} From 63224425fea566727c20b9144852be607ce76fc2 Mon Sep 17 00:00:00 2001 From: James May Date: Thu, 9 Nov 2023 19:12:55 +1100 Subject: [PATCH 5/8] Add ModuleReferences --- .../ICSharpCode.Decompiler.csproj | 1 + .../Metadata/ModuleReferenceMetadata.cs | 132 ++++++++++++++++++ ICSharpCode.Decompiler/Metadata/PEFile.cs | 16 +++ ILSpy/TreeNodes/ModuleReferenceTreeNode.cs | 18 ++- ILSpy/TreeNodes/ReferenceFolderTreeNode.cs | 2 +- 5 files changed, 162 insertions(+), 7 deletions(-) create mode 100644 ICSharpCode.Decompiler/Metadata/ModuleReferenceMetadata.cs diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 5c4539d5a5..36513a7547 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -98,6 +98,7 @@ + diff --git a/ICSharpCode.Decompiler/Metadata/ModuleReferenceMetadata.cs b/ICSharpCode.Decompiler/Metadata/ModuleReferenceMetadata.cs new file mode 100644 index 0000000000..eee2078f88 --- /dev/null +++ b/ICSharpCode.Decompiler/Metadata/ModuleReferenceMetadata.cs @@ -0,0 +1,132 @@ +// 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 class ModuleReferenceMetadata /* : IModuleReference*/ + { + readonly ModuleReference entry; + + public MetadataReader Metadata { get; } + public ModuleReferenceHandle Handle { get; } + + string? name; + public string Name { + get { + if (name == null) + { + try + { + name = Metadata.GetString(entry.Name); + } + catch (BadImageFormatException) + { + name = $"AR:{Handle}"; + } + } + return name; + } + } + + ImmutableArray attributes; + public ImmutableArray Attributes { + get { + var value = attributes; + if (value.IsDefault) + { + value = entry.GetCustomAttributes().Select(Metadata.GetCustomAttribute).ToImmutableArray(); + attributes = value; + } + return value; + } + } + + ImmutableArray typeReferences; + public ImmutableArray TypeReferences { + get { + var value = typeReferences; + if (value.IsDefault) + { + value = Metadata.TypeReferences + .Select(r => new TypeReferenceMetadata(Metadata, r)) + .Where(r => r.ResolutionScope == Handle) + .OrderBy(r => r.Namespace) + .ThenBy(r => r.Name) + .ToImmutableArray(); + typeReferences = value; + } + return value; + } + } + + ImmutableArray exportedTypes; + public ImmutableArray ExportedTypes { + get { + var value = exportedTypes; + if (value.IsDefault) + { + value = Metadata.ExportedTypes + .Select(r => new ExportedTypeMetadata(Metadata, r)) + .Where(r => r.Implementation == Handle) + .OrderBy(r => r.Namespace) + .ThenBy(r => r.Name) + .ToImmutableArray(); + exportedTypes = value; + } + return value; + } + } + + public ModuleReferenceMetadata(MetadataReader metadata, ModuleReferenceHandle handle) + { + if (metadata == null) + throw new ArgumentNullException(nameof(metadata)); + if (handle.IsNil) + throw new ArgumentNullException(nameof(handle)); + Metadata = metadata; + Handle = handle; + entry = metadata.GetModuleReference(handle); + } + + public ModuleReferenceMetadata(PEFile module, ModuleReferenceHandle handle) + { + if (module == null) + throw new ArgumentNullException(nameof(module)); + if (handle.IsNil) + throw new ArgumentNullException(nameof(handle)); + Metadata = module.Metadata; + Handle = handle; + entry = Metadata.GetModuleReference(handle); + } + + public override string ToString() + { + return Name; + } + } +#endif +} diff --git a/ICSharpCode.Decompiler/Metadata/PEFile.cs b/ICSharpCode.Decompiler/Metadata/PEFile.cs index 8279407671..c180de1792 100644 --- a/ICSharpCode.Decompiler/Metadata/PEFile.cs +++ b/ICSharpCode.Decompiler/Metadata/PEFile.cs @@ -177,6 +177,22 @@ public ImmutableArray AssemblyReferences { } } + ImmutableArray moduleReferences; + public ImmutableArray ModuleReferences { + get { + var value = moduleReferences; + if (value.IsDefault) + { + value = Metadata.GetModuleReferences() + .Select(m => new ModuleReferenceMetadata(this, m)) + .ToImmutableArray(); + + moduleReferences = value; + } + return value; + } + } + public ImmutableArray Resources => GetResources().ToImmutableArray(); IEnumerable GetResources() diff --git a/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs b/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs index b800e55911..c0c37654a6 100644 --- a/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs +++ b/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs @@ -17,9 +17,13 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Linq; using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.ILSpy.Metadata; namespace ICSharpCode.ILSpy.TreeNodes { @@ -28,6 +32,7 @@ namespace ICSharpCode.ILSpy.TreeNodes /// sealed class ModuleReferenceTreeNode : ILSpyTreeNode { + readonly PEFile module; readonly AssemblyTreeNode parentAssembly; readonly MetadataReader metadata; readonly ModuleReferenceHandle handle; @@ -37,20 +42,21 @@ sealed class ModuleReferenceTreeNode : ILSpyTreeNode readonly string moduleName; readonly bool containsMetadata; - public ModuleReferenceTreeNode(AssemblyTreeNode parentAssembly, ModuleReferenceHandle r, MetadataReader module) + public ModuleReferenceTreeNode(AssemblyTreeNode parentAssembly, ModuleReferenceHandle r, PEFile module) { this.parentAssembly = parentAssembly ?? throw new ArgumentNullException(nameof(parentAssembly)); if (r.IsNil) throw new ArgumentNullException(nameof(r)); - this.metadata = module; this.handle = r; - this.reference = module.GetModuleReference(r); + this.module = module ?? throw new ArgumentNullException(nameof(module)); + this.metadata = module.Metadata; + this.reference = module.Metadata.GetModuleReference(r); this.moduleName = Language.EscapeName(metadata.GetString(reference.Name)); - foreach (var h in module.AssemblyFiles) + foreach (var h in metadata.AssemblyFiles) { - var file = module.GetAssemblyFile(h); - if (module.StringComparer.Equals(file.Name, moduleName)) + var file = metadata.GetAssemblyFile(h); + if (metadata.StringComparer.Equals(file.Name, moduleName)) { this.file = file; this.fileHandle = h; diff --git a/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs b/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs index adb72732c6..9d8452d4a5 100644 --- a/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs +++ b/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs @@ -51,7 +51,7 @@ protected override void LoadChildren() foreach (var r in module.AssemblyReferences.OrderBy(r => r.Name)) this.Children.Add(new AssemblyReferenceTreeNode(module, r, parentAssembly)); foreach (var r in metadata.GetModuleReferences().OrderBy(r => metadata.GetString(metadata.GetModuleReference(r).Name))) - this.Children.Add(new ModuleReferenceTreeNode(parentAssembly, r, metadata)); + this.Children.Add(new ModuleReferenceTreeNode(parentAssembly, r, module)); } public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) From f93c478a6d2373a99d07ed7165c59506e0a34a2b Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 16 Mar 2024 15:28:14 +0100 Subject: [PATCH 6/8] ExportedTypes, TypeReference and MemberReferences: Use UnknownType/FakeMember for pretty-printing of signatures in TreeView. --- ...ssemblyReferenceReferencedTypesTreeNode.cs | 7 ++++--- ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs | 8 +++++--- ILSpy/TreeNodes/ExportedTypeTreeNode.cs | 12 +++++++----- ILSpy/TreeNodes/MemberReferenceTreeNode.cs | 19 +++++++++++++------ ILSpy/TreeNodes/ReferenceFolderTreeNode.cs | 4 +++- ILSpy/TreeNodes/TypeReferenceTreeNode.cs | 13 ++++++++----- 6 files changed, 40 insertions(+), 23 deletions(-) diff --git a/ILSpy/TreeNodes/AssemblyReferenceReferencedTypesTreeNode.cs b/ILSpy/TreeNodes/AssemblyReferenceReferencedTypesTreeNode.cs index 6d4734a916..37511236f2 100644 --- a/ILSpy/TreeNodes/AssemblyReferenceReferencedTypesTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyReferenceReferencedTypesTreeNode.cs @@ -20,6 +20,7 @@ using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpy.TreeNodes { @@ -28,10 +29,10 @@ namespace ICSharpCode.ILSpy.TreeNodes /// public sealed class AssemblyReferenceReferencedTypesTreeNode : ILSpyTreeNode { - private readonly PEFile module; - private readonly AssemblyReference r; + readonly MetadataModule module; + readonly AssemblyReference r; - public AssemblyReferenceReferencedTypesTreeNode(PEFile module, AssemblyReference r) + public AssemblyReferenceReferencedTypesTreeNode(MetadataModule module, AssemblyReference r) { this.module = module ?? throw new ArgumentNullException(nameof(module)); this.r = r ?? throw new ArgumentNullException(nameof(r)); diff --git a/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs b/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs index 8aa2501a37..3db5f974ee 100644 --- a/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs @@ -21,6 +21,7 @@ using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpy.TreeNodes { @@ -29,11 +30,11 @@ namespace ICSharpCode.ILSpy.TreeNodes /// public sealed class AssemblyReferenceTreeNode : ILSpyTreeNode { - readonly PEFile module; + readonly MetadataModule module; readonly AssemblyReference r; readonly AssemblyTreeNode parentAssembly; - public AssemblyReferenceTreeNode(PEFile module, AssemblyReference r, AssemblyTreeNode parentAssembly) + public AssemblyReferenceTreeNode(MetadataModule module, AssemblyReference r, AssemblyTreeNode parentAssembly) { this.module = module ?? throw new ArgumentNullException(nameof(module)); this.r = r ?? throw new ArgumentNullException(nameof(r)); @@ -83,8 +84,9 @@ protected override void LoadChildren() var referencedModule = resolver.Resolve(r); if (referencedModule != null) { + var module = (MetadataModule)referencedModule.GetTypeSystemWithCurrentOptionsOrNull().MainModule; foreach (var childRef in referencedModule.AssemblyReferences) - this.Children.Add(new AssemblyReferenceTreeNode(referencedModule, childRef, parentAssembly)); + this.Children.Add(new AssemblyReferenceTreeNode(module, childRef, parentAssembly)); } } diff --git a/ILSpy/TreeNodes/ExportedTypeTreeNode.cs b/ILSpy/TreeNodes/ExportedTypeTreeNode.cs index bb21545c8f..b01e0c1dfc 100644 --- a/ILSpy/TreeNodes/ExportedTypeTreeNode.cs +++ b/ILSpy/TreeNodes/ExportedTypeTreeNode.cs @@ -29,19 +29,21 @@ namespace ICSharpCode.ILSpy.TreeNodes /// public sealed class ExportedTypeTreeNode : ILSpyTreeNode { - readonly PEFile module; - private readonly ExportedTypeMetadata r; + readonly MetadataModule module; + readonly ExportedTypeMetadata r; + readonly IType resolvedType; - public ExportedTypeTreeNode(PEFile module, ExportedTypeMetadata r) + public ExportedTypeTreeNode(MetadataModule module, ExportedTypeMetadata r) { this.module = module ?? throw new ArgumentNullException(nameof(module)); this.r = r ?? throw new ArgumentNullException(nameof(r)); + this.resolvedType = module.ResolveType(r.Handle, default); this.LazyLoading = true; } public override object Text - => Language.GetEntityName(module, r.Handle, fullName: true, omitGenerics: false) + GetSuffixString(r.Handle); + => Language.TypeToString(resolvedType, includeNamespace: true) + GetSuffixString(r.Handle); public override object Icon => Images.Library; @@ -55,7 +57,7 @@ protected override void LoadChildren() 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})"); + language.WriteCommentLine(output, $"{Language.TypeToString(resolvedType, includeNamespace: true)} (Exported, IsForwarder: {r.IsForwarder}, Attributes: {(int)r.Attributes:X8})"); } } } diff --git a/ILSpy/TreeNodes/MemberReferenceTreeNode.cs b/ILSpy/TreeNodes/MemberReferenceTreeNode.cs index c694430295..a320d5ccb7 100644 --- a/ILSpy/TreeNodes/MemberReferenceTreeNode.cs +++ b/ILSpy/TreeNodes/MemberReferenceTreeNode.cs @@ -23,6 +23,7 @@ using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpy.TreeNodes { @@ -31,16 +32,22 @@ namespace ICSharpCode.ILSpy.TreeNodes /// public sealed class MemberReferenceTreeNode : ILSpyTreeNode { - readonly PEFile module; - private readonly MemberReferenceMetadata r; + readonly MetadataModule module; + readonly MemberReferenceMetadata r; + readonly IMember resolvedMember; - public MemberReferenceTreeNode(PEFile module, MemberReferenceMetadata r) + public MemberReferenceTreeNode(MetadataModule module, MemberReferenceMetadata r) { this.module = module ?? throw new ArgumentNullException(nameof(module)); this.r = r; + this.resolvedMember = r.MemberReferenceKind switch { + MemberReferenceKind.Method => module.ResolveMethod(r.Handle, default), + MemberReferenceKind.Field => (IMember)module.ResolveEntity(r.Handle), + _ => throw new NotSupportedException(), + }; } - public override object Text => r.Name + GetSuffixString(r.Handle); + public override object Text => Signature + GetSuffixString(r.Handle); public override object Icon => r.MemberReferenceKind switch { MemberReferenceKind.Method => Images.Method, @@ -48,13 +55,13 @@ public MemberReferenceTreeNode(PEFile module, MemberReferenceMetadata r) _ => Images.Class, }; - public string Signature => Language.GetEntityName(module, r.Handle, fullName: true, omitGenerics: false); + public string Signature => resolvedMember is IMethod m ? Language.MethodToString(m, false, false, false) : Language.FieldToString((IField)resolvedMember, false, false, false); public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, Signature); } - public override object ToolTip => Signature; + public override object ToolTip => Language.GetRichTextTooltip(resolvedMember); } } diff --git a/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs b/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs index 9d8452d4a5..e978a7e8a5 100644 --- a/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs +++ b/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs @@ -22,6 +22,7 @@ using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.Properties; namespace ICSharpCode.ILSpy.TreeNodes @@ -48,8 +49,9 @@ public ReferenceFolderTreeNode(PEFile module, AssemblyTreeNode parentAssembly) protected override void LoadChildren() { var metadata = module.Metadata; + var metadataModule = (MetadataModule)module.GetTypeSystemWithCurrentOptionsOrNull().MainModule; foreach (var r in module.AssemblyReferences.OrderBy(r => r.Name)) - this.Children.Add(new AssemblyReferenceTreeNode(module, r, parentAssembly)); + this.Children.Add(new AssemblyReferenceTreeNode(metadataModule, r, parentAssembly)); foreach (var r in metadata.GetModuleReferences().OrderBy(r => metadata.GetString(metadata.GetModuleReference(r).Name))) this.Children.Add(new ModuleReferenceTreeNode(parentAssembly, r, module)); } diff --git a/ILSpy/TreeNodes/TypeReferenceTreeNode.cs b/ILSpy/TreeNodes/TypeReferenceTreeNode.cs index 83872ebe6f..36ddc15b8c 100644 --- a/ILSpy/TreeNodes/TypeReferenceTreeNode.cs +++ b/ILSpy/TreeNodes/TypeReferenceTreeNode.cs @@ -20,6 +20,7 @@ using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpy.TreeNodes { @@ -28,19 +29,21 @@ namespace ICSharpCode.ILSpy.TreeNodes /// public sealed class TypeReferenceTreeNode : ILSpyTreeNode { - readonly PEFile module; - private readonly TypeReferenceMetadata r; + readonly MetadataModule module; + readonly TypeReferenceMetadata r; + readonly IType resolvedType; - public TypeReferenceTreeNode(PEFile module, TypeReferenceMetadata r) + public TypeReferenceTreeNode(MetadataModule module, TypeReferenceMetadata r) { this.module = module ?? throw new ArgumentNullException(nameof(module)); this.r = r ?? throw new ArgumentNullException(nameof(r)); + this.resolvedType = module.ResolveType(r.Handle, default); this.LazyLoading = true; } public override object Text - => Language.GetEntityName(module, r.Handle, fullName: true, omitGenerics: false) + GetSuffixString(r.Handle); + => Language.TypeToString(resolvedType, includeNamespace: false) + GetSuffixString(r.Handle); public override object Icon => Images.Class; @@ -57,7 +60,7 @@ protected override void LoadChildren() public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { - language.WriteCommentLine(output, $"{Language.GetEntityName(module, r.Handle, fullName: true, omitGenerics: false)}"); + language.WriteCommentLine(output, Language.TypeToString(resolvedType, includeNamespace: true)); EnsureLazyChildren(); foreach (ILSpyTreeNode child in Children) { From cc58644082d93acdd8aafdccd383db8827faeb65 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 15 Mar 2024 21:59:39 +0100 Subject: [PATCH 7/8] Added new icons for TypeReference, ExportedType and MemberReference --- ILSpy/Images/ExportOverlay.svg | 86 +++++++++++++++++++ ILSpy/Images/ExportOverlay.xaml | 18 ++++ ILSpy/Images/Images.cs | 81 ++++++++++------- ILSpy/Images/README.md | 4 +- ILSpy/Images/ReferenceOverlay.svg | 55 ++++++++++++ ILSpy/Images/ReferenceOverlay.xaml | 12 +++ ...ssemblyReferenceReferencedTypesTreeNode.cs | 2 +- ILSpy/TreeNodes/ExportedTypeTreeNode.cs | 2 +- ILSpy/TreeNodes/MemberReferenceTreeNode.cs | 6 +- ILSpy/TreeNodes/TypeReferenceTreeNode.cs | 2 +- 10 files changed, 228 insertions(+), 40 deletions(-) create mode 100644 ILSpy/Images/ExportOverlay.svg create mode 100644 ILSpy/Images/ExportOverlay.xaml create mode 100644 ILSpy/Images/ReferenceOverlay.svg create mode 100644 ILSpy/Images/ReferenceOverlay.xaml diff --git a/ILSpy/Images/ExportOverlay.svg b/ILSpy/Images/ExportOverlay.svg new file mode 100644 index 0000000000..d396359683 --- /dev/null +++ b/ILSpy/Images/ExportOverlay.svg @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ILSpy/Images/ExportOverlay.xaml b/ILSpy/Images/ExportOverlay.xaml new file mode 100644 index 0000000000..387c04fb4e --- /dev/null +++ b/ILSpy/Images/ExportOverlay.xaml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ILSpy/Images/Images.cs b/ILSpy/Images/Images.cs index ab1012278f..5b48f10bc8 100644 --- a/ILSpy/Images/Images.cs +++ b/ILSpy/Images/Images.cs @@ -27,6 +27,8 @@ namespace ICSharpCode.ILSpy { static class Images { + private static readonly Rect iconRect = new Rect(0, 0, 16, 16); + static ImageSource Load(string icon) { var image = new DrawingImage(LoadDrawingGroup(null, "Images/" + icon)); @@ -85,6 +87,7 @@ static ImageSource Load(string icon) public static readonly ImageSource Interface = Load("Interface"); public static readonly ImageSource Delegate = Load("Delegate"); public static readonly ImageSource Enum = Load("Enum"); + public static readonly ImageSource Type = Load("ShowPublicOnly"); public static readonly ImageSource Field = Load("Field"); public static readonly ImageSource FieldReadOnly = Load("FieldReadOnly"); @@ -109,9 +112,15 @@ static ImageSource Load(string icon) private static readonly ImageSource OverlayPrivate = Load("OverlayPrivate"); private static readonly ImageSource OverlayPrivateProtected = Load("OverlayPrivateProtected"); private static readonly ImageSource OverlayCompilerControlled = Load("OverlayCompilerControlled"); + private static readonly ImageSource OverlayReference = Load("ReferenceOverlay"); private static readonly ImageSource OverlayStatic = Load("OverlayStatic"); + public static readonly ImageSource TypeReference = GetIcon("ShowPublicOnly", "ReferenceOverlay"); + public static readonly ImageSource MethodReference = GetIcon("Method", "ReferenceOverlay"); + public static readonly ImageSource FieldReference = GetIcon("Field", "ReferenceOverlay"); + public static readonly ImageSource ExportedType = GetIcon("ShowPublicOnly", "ExportOverlay"); + public static ImageSource Load(object part, string icon) { if (icon.EndsWith(".png", StringComparison.OrdinalIgnoreCase)) @@ -202,6 +211,45 @@ public static ImageSource GetIcon(MemberIcon icon, AccessOverlayIcon overlay, bo return memberIconCache.GetIcon(icon, overlay, isStatic); } + private static ImageSource GetIcon(string baseImage, string overlay = null, bool isStatic = false) + { + ImageSource baseImageSource = Load(baseImage); + ImageSource overlayImageSource = overlay != null ? Load(overlay) : null; + + return CreateOverlayImage(baseImageSource, overlayImageSource, isStatic); + } + + private static ImageSource CreateOverlayImage(ImageSource baseImage, ImageSource overlay, bool isStatic) + { + var group = new DrawingGroup(); + + Drawing baseDrawing = new ImageDrawing(baseImage, iconRect); + + if (overlay != null) + { + var nestedGroup = new DrawingGroup { Transform = new ScaleTransform(0.8, 0.8) }; + nestedGroup.Children.Add(baseDrawing); + group.Children.Add(nestedGroup); + group.Children.Add(new ImageDrawing(overlay, iconRect)); + } + else + { + group.Children.Add(baseDrawing); + } + + if (isStatic) + { + group.Children.Add(new ImageDrawing(Images.OverlayStatic, iconRect)); + } + + var image = new DrawingImage(group); + if (image.CanFreeze) + { + image.Freeze(); + } + return image; + } + #region icon caches & overlay management private class TypeIconCache : IconCache @@ -380,39 +428,6 @@ private static ImageSource GetOverlayImage(AccessOverlayIcon overlay) } return overlayImage; } - - private static readonly Rect iconRect = new Rect(0, 0, 16, 16); - - private static ImageSource CreateOverlayImage(ImageSource baseImage, ImageSource overlay, bool isStatic) - { - var group = new DrawingGroup(); - - Drawing baseDrawing = new ImageDrawing(baseImage, iconRect); - - if (overlay != null) - { - var nestedGroup = new DrawingGroup { Transform = new ScaleTransform(0.8, 0.8) }; - nestedGroup.Children.Add(baseDrawing); - group.Children.Add(nestedGroup); - group.Children.Add(new ImageDrawing(overlay, iconRect)); - } - else - { - group.Children.Add(baseDrawing); - } - - if (isStatic) - { - group.Children.Add(new ImageDrawing(Images.OverlayStatic, iconRect)); - } - - var image = new DrawingImage(group); - if (image.CanFreeze) - { - image.Freeze(); - } - return image; - } } #endregion diff --git a/ILSpy/Images/README.md b/ILSpy/Images/README.md index 82decd3387..05dcc6f092 100644 --- a/ILSpy/Images/README.md +++ b/ILSpy/Images/README.md @@ -18,6 +18,7 @@ Icons used in ILSpy: | Enum | x | x | VS 2017 Icon Pack (Enumerator) | | | EnumValue | x | x | VS 2017 Icon Pack (EnumItem) | | | Event | x | x | VS 2017 Icon Pack (Event) | | +| ExportOverlay | x | x | slightly modified VS 2017 Icon Pack (Export) | | | ExtensionMethod | x | x | VS 2017 Icon Pack (ExtensionMethod) | | | Field | x | x | VS 2017 Icon Pack (Field) | | | FieldReadOnly | x | x | VS 2017 Icon Pack (Field) with different color | | @@ -53,12 +54,13 @@ Icons used in ILSpy: | ProgramDebugDatabase | x | x | VS 2017 Icon Pack (ProgramDebugDatabase) | | | Property | x | x | VS 2017 Icon Pack (Property) | | | ReferenceFolder | x | x | combined VS 2017 Icon Pack (Reference) two times | | +| ReferenceOverlay | x | x | extracted arrow from VS 2017 Icon Pack (TypeShortcut) | | | Refresh | x | x | VS 2017 Icon Pack (Refresh) | | | Resource | x | x | VS 2017 Icon Pack (Document) | | | ResourceImage | x | x | VS 2017 Icon Pack (Image) | | | ResourceResourcesFile | x | x | VS 2017 Icon Pack (LocalResources) | | | ResourceXml | x | x | VS 2017 Icon Pack (XMLFile) | | -| ResourceXsd | x | x | combined VS 2017 Icon Pack (XMLSchema) with the "file symbol in ResourceXslt | | +| ResourceXsd | x | x | combined VS 2017 Icon Pack (XMLSchema) with the file symbol in ResourceXslt | | | ResourceXsl | x | x | VS 2017 Icon Pack (XMLTransformation) | | | ResourceXslt | x | x | VS 2017 Icon Pack (XSLTTemplate) | | | Save | x | x | VS 2017 Icon Pack (Save) | | diff --git a/ILSpy/Images/ReferenceOverlay.svg b/ILSpy/Images/ReferenceOverlay.svg new file mode 100644 index 0000000000..8d6a8ef7af --- /dev/null +++ b/ILSpy/Images/ReferenceOverlay.svg @@ -0,0 +1,55 @@ + + + + + + + + + + + + diff --git a/ILSpy/Images/ReferenceOverlay.xaml b/ILSpy/Images/ReferenceOverlay.xaml new file mode 100644 index 0000000000..a07e0cb790 --- /dev/null +++ b/ILSpy/Images/ReferenceOverlay.xaml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/ILSpy/TreeNodes/AssemblyReferenceReferencedTypesTreeNode.cs b/ILSpy/TreeNodes/AssemblyReferenceReferencedTypesTreeNode.cs index 37511236f2..642cb80eef 100644 --- a/ILSpy/TreeNodes/AssemblyReferenceReferencedTypesTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyReferenceReferencedTypesTreeNode.cs @@ -41,7 +41,7 @@ public AssemblyReferenceReferencedTypesTreeNode(MetadataModule module, AssemblyR } public override object Text => $"Referenced Types ({r.TypeReferences.Length + r.ExportedTypes.Length})"; - public override object Icon => Images.Class; + public override object Icon => Images.MetadataTable; protected override void LoadChildren() { diff --git a/ILSpy/TreeNodes/ExportedTypeTreeNode.cs b/ILSpy/TreeNodes/ExportedTypeTreeNode.cs index b01e0c1dfc..262d4c7a99 100644 --- a/ILSpy/TreeNodes/ExportedTypeTreeNode.cs +++ b/ILSpy/TreeNodes/ExportedTypeTreeNode.cs @@ -45,7 +45,7 @@ public ExportedTypeTreeNode(MetadataModule module, ExportedTypeMetadata r) public override object Text => Language.TypeToString(resolvedType, includeNamespace: true) + GetSuffixString(r.Handle); - public override object Icon => Images.Library; + public override object Icon => Images.ExportedType; protected override void LoadChildren() { diff --git a/ILSpy/TreeNodes/MemberReferenceTreeNode.cs b/ILSpy/TreeNodes/MemberReferenceTreeNode.cs index a320d5ccb7..33ae47cffc 100644 --- a/ILSpy/TreeNodes/MemberReferenceTreeNode.cs +++ b/ILSpy/TreeNodes/MemberReferenceTreeNode.cs @@ -50,9 +50,9 @@ public MemberReferenceTreeNode(MetadataModule module, MemberReferenceMetadata r) public override object Text => Signature + GetSuffixString(r.Handle); public override object Icon => r.MemberReferenceKind switch { - MemberReferenceKind.Method => Images.Method, - MemberReferenceKind.Field => Images.Field, - _ => Images.Class, + MemberReferenceKind.Method => Images.MethodReference, + MemberReferenceKind.Field => Images.FieldReference, + _ => throw new NotSupportedException(), }; public string Signature => resolvedMember is IMethod m ? Language.MethodToString(m, false, false, false) : Language.FieldToString((IField)resolvedMember, false, false, false); diff --git a/ILSpy/TreeNodes/TypeReferenceTreeNode.cs b/ILSpy/TreeNodes/TypeReferenceTreeNode.cs index 36ddc15b8c..d8039181cf 100644 --- a/ILSpy/TreeNodes/TypeReferenceTreeNode.cs +++ b/ILSpy/TreeNodes/TypeReferenceTreeNode.cs @@ -45,7 +45,7 @@ public TypeReferenceTreeNode(MetadataModule module, TypeReferenceMetadata r) public override object Text => Language.TypeToString(resolvedType, includeNamespace: false) + GetSuffixString(r.Handle); - public override object Icon => Images.Class; + public override object Icon => Images.TypeReference; protected override void LoadChildren() { From 2e777201f3160b9c96b2872299f08fd38f9c4776 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 15 Mar 2024 22:01:16 +0100 Subject: [PATCH 8/8] Fix bug in UnknownType: FullName of nested unknown types did not contain the outer type name(s), but only namespace and nested type name. --- .../TypeSystem/FullTypeName.cs | 16 ++++++++++++++++ .../TypeSystem/Implementation/UnknownType.cs | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/ICSharpCode.Decompiler/TypeSystem/FullTypeName.cs b/ICSharpCode.Decompiler/TypeSystem/FullTypeName.cs index 9df4bf8a41..36712ee645 100644 --- a/ICSharpCode.Decompiler/TypeSystem/FullTypeName.cs +++ b/ICSharpCode.Decompiler/TypeSystem/FullTypeName.cs @@ -165,6 +165,22 @@ public string ReflectionName { } } + public string FullName { + get { + if (nestedTypes == null) + return topLevelType.Namespace + "." + topLevelType.Name; + StringBuilder b = new StringBuilder(topLevelType.Namespace); + b.Append('.'); + b.Append(topLevelType.Name); + foreach (NestedTypeName nt in nestedTypes) + { + b.Append('.'); + b.Append(nt.Name); + } + return b.ToString(); + } + } + /// /// Gets the total type parameter count. /// diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/UnknownType.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/UnknownType.cs index 28a0f33b7e..9489194bf4 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/UnknownType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/UnknownType.cs @@ -95,6 +95,10 @@ public override string ReflectionName { get { return namespaceKnown ? fullTypeName.ReflectionName : "?"; } } + public override string FullName { + get { return namespaceKnown ? fullTypeName.FullName : "?"; } + } + public FullTypeName FullTypeName => fullTypeName; public override int TypeParameterCount => fullTypeName.TypeParameterCount;