From 416b5efbe8fc9c4f15e2d696de88d289bc649d2a Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 28 Dec 2019 00:38:45 +0100 Subject: [PATCH] Work on metadata tooltips, performance, etc. --- .../CorTables/AssemblyRefTableTreeNode.cs | 20 ++- .../CorTables/AssemblyTableTreeNode.cs | 19 +-- .../CorTables/ClassLayoutTableTreeNode.cs | 15 +- .../CorTables/ConstantTableTreeNode.cs | 21 ++- .../CorTables/CustomAttributeTableTreeNode.cs | 15 +- .../CorTables/DeclSecurityTableTreeNode.cs | 25 +++- .../CorTables/EventMapTableTreeNode.cs | 13 +- .../Metadata/CorTables/EventTableTreeNode.cs | 22 ++- .../CorTables/ExportedTypeTableTreeNode.cs | 34 ++++- .../CorTables/FieldLayoutTableTreeNode.cs | 13 +- .../CorTables/FieldMarshalTableTreeNode.cs | 17 ++- .../CorTables/FieldRVATableTreeNode.cs | 13 +- .../Metadata/CorTables/FieldTableTreeNode.cs | 52 ++++++- ILSpy/Metadata/CorTables/FileTableTreeNode.cs | 15 +- .../GenericParamConstraintTableTreeNode.cs | 13 +- .../CorTables/GenericParamTableTreeNode.cs | 24 +++- .../CorTables/ImplMapTableTreeNode.cs | 22 ++- .../CorTables/InterfaceImplTableTreeNode.cs | 13 +- .../ManifestResourceTableTreeNode.cs | 19 ++- .../CorTables/MemberRefTableTreeNode.cs | 18 ++- .../CorTables/MethodImplTableTreeNode.cs | 16 ++- .../CorTables/MethodSemanticsTableTreeNode.cs | 29 +++- .../CorTables/MethodSpecTableTreeNode.cs | 24 +++- .../Metadata/CorTables/MethodTableTreeNode.cs | 33 ++++- .../CorTables/ModuleRefTableTreeNode.cs | 18 ++- .../Metadata/CorTables/ModuleTableTreeNode.cs | 12 +- .../CorTables/NestedClassTableTreeNode.cs | 13 +- .../Metadata/CorTables/ParamTableTreeNode.cs | 18 ++- .../CorTables/PropertyMapTableTreeNode.cs | 13 +- .../CorTables/PropertyTableTreeNode.cs | 22 ++- .../CorTables/StandAloneSigTableTreeNode.cs | 21 ++- .../CorTables/TypeDefTableTreeNode.cs | 53 ++++++- .../CorTables/TypeRefTableTreeNode.cs | 18 ++- .../CorTables/TypeSpecTableTreeNode.cs | 17 ++- ILSpy/Metadata/DataGridCustomTextColumn.cs | 11 +- ILSpy/Metadata/FlagsFilterControl.xaml.cs | 20 +-- ILSpy/Metadata/FlagsTooltip.xaml | 36 +++-- ILSpy/Metadata/FlagsTooltip.xaml.cs | 135 +++++++++++++++--- ILSpy/Metadata/Helpers.cs | 25 ++-- ILSpy/Metadata/MetadataProtocolHandler.cs | 4 +- ILSpy/Metadata/MetadataTableTreeNode.cs | 10 ++ ILSpy/Metadata/MetadataTableViews.xaml | 8 ++ ILSpy/Metadata/MetadataTreeNode.cs | 12 +- ILSpy/Metadata/OptionalHeaderTreeNode.cs | 28 ++-- 44 files changed, 830 insertions(+), 169 deletions(-) diff --git a/ILSpy/Metadata/CorTables/AssemblyRefTableTreeNode.cs b/ILSpy/Metadata/CorTables/AssemblyRefTableTreeNode.cs index f895281a3..e2b2a4068 100644 --- a/ILSpy/Metadata/CorTables/AssemblyRefTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/AssemblyRefTableTreeNode.cs @@ -46,14 +46,24 @@ namespace ICSharpCode.ILSpy.Metadata var view = Helpers.PrepareDataGrid(tabPage); var metadata = module.Metadata; var list = new List(); + AssemblyRefEntry scrollTargetEntry = default; foreach (var row in metadata.AssemblyReferences) { - list.Add(new AssemblyRefEntry(module, row)); + AssemblyRefEntry entry = new AssemblyRefEntry(module, row); + if (scrollTarget == MetadataTokens.GetRowNumber(row)) { + scrollTargetEntry = entry; + } + list.Add(entry); } view.ItemsSource = list; - tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } @@ -75,10 +85,14 @@ namespace ICSharpCode.ILSpy.Metadata public Version Version => assemblyRef.Version; + [StringFormat("X8")] public AssemblyFlags Flags => assemblyRef.Flags; - public object FlagsTooltip => new FlagsTooltip((int)assemblyRef.Flags, typeof(AssemblyFlags)); + public object FlagsTooltip => new FlagsTooltip((int)assemblyRef.Flags, null) { + FlagGroup.CreateMultipleChoiceGroup(typeof(AssemblyFlags), selectedValue: (int)assemblyRef.Flags, includeAll: false) + }; + [StringFormat("X")] public int PublicKeyOrToken => MetadataTokens.GetHeapOffset(assemblyRef.PublicKeyOrToken); public string PublicKeyOrTokenTooltip { diff --git a/ILSpy/Metadata/CorTables/AssemblyTableTreeNode.cs b/ILSpy/Metadata/CorTables/AssemblyTableTreeNode.cs index 71f3fb19b..d8cbc00a7 100644 --- a/ILSpy/Metadata/CorTables/AssemblyTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/AssemblyTableTreeNode.cs @@ -44,12 +44,7 @@ namespace ICSharpCode.ILSpy.Metadata tabPage.SupportsLanguageSwitching = false; var view = Helpers.PrepareDataGrid(tabPage); - var list = new List(); - - list.Add(new AssemblyEntry(module)); - - view.ItemsSource = list; - + view.ItemsSource = new[] { new AssemblyEntry(module) }; tabPage.Content = view; return true; } @@ -69,13 +64,19 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.Assembly) + metadata.GetTableRowSize(TableIndex.Assembly) * (RID - 1); - public int HashAlgorithm => (int)assembly.HashAlgorithm; + [StringFormat("X4")] + public AssemblyHashAlgorithm HashAlgorithm => assembly.HashAlgorithm; - public string HashAlgorithmTooltip => assembly.HashAlgorithm.ToString(); + public object HashAlgorithmTooltip => new FlagsTooltip() { + FlagGroup.CreateSingleChoiceGroup(typeof(AssemblyHashAlgorithm), selectedValue: (int)assembly.HashAlgorithm, defaultFlag: new Flag("None (0000)", 0, false), includeAny: false) + }; + [StringFormat("X4")] public AssemblyFlags Flags => assembly.Flags; - public object FlagsTooltip => new FlagsTooltip((int)assembly.Flags, typeof(AssemblyFlags)); + public object FlagsTooltip => new FlagsTooltip() { + FlagGroup.CreateMultipleChoiceGroup(typeof(AssemblyFlags), selectedValue: (int)assembly.Flags, includeAll: false) + }; public Version Version => assembly.Version; diff --git a/ILSpy/Metadata/CorTables/ClassLayoutTableTreeNode.cs b/ILSpy/Metadata/CorTables/ClassLayoutTableTreeNode.cs index 4152ea7e5..74f69c8e1 100644 --- a/ILSpy/Metadata/CorTables/ClassLayoutTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ClassLayoutTableTreeNode.cs @@ -51,13 +51,24 @@ namespace ICSharpCode.ILSpy.Metadata var length = metadata.GetTableRowCount(TableIndex.ClassLayout); byte* ptr = metadata.MetadataPointer; int metadataOffset = module.Reader.PEHeaders.MetadataStartOffset; + ClassLayoutEntry scrollTargetEntry = default; + for (int rid = 1; rid <= length; rid++) { - list.Add(new ClassLayoutEntry(module, ptr, metadataOffset, rid)); + ClassLayoutEntry entry = new ClassLayoutEntry(module, ptr, metadataOffset, rid); + if (scrollTarget == rid) { + scrollTargetEntry = entry; + } + list.Add(entry); } view.ItemsSource = list; - tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } diff --git a/ILSpy/Metadata/CorTables/ConstantTableTreeNode.cs b/ILSpy/Metadata/CorTables/ConstantTableTreeNode.cs index e1ab81953..0a10fd5ad 100644 --- a/ILSpy/Metadata/CorTables/ConstantTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ConstantTableTreeNode.cs @@ -48,14 +48,24 @@ namespace ICSharpCode.ILSpy.Metadata var metadata = module.Metadata; var list = new List(); + ConstantEntry scrollTargetEntry = default; for (int row = 1; row <= metadata.GetTableRowCount(TableIndex.Constant); row++) { - list.Add(new ConstantEntry(module, MetadataTokens.ConstantHandle(row))); + ConstantEntry entry = new ConstantEntry(module, MetadataTokens.ConstantHandle(row)); + if (scrollTarget == row) { + scrollTargetEntry = entry; + } + list.Add(entry); } view.ItemsSource = list; - tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } @@ -75,11 +85,13 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.Constant) + metadata.GetTableRowSize(TableIndex.Constant) * (RID - 1); - public int Type => (int)constant.TypeCode; + [StringFormat("X8")] + public ConstantTypeCode Type => constant.TypeCode; public string TypeTooltip => constant.TypeCode.ToString(); - public int ParentHandle => MetadataTokens.GetToken(constant.Parent); + [StringFormat("X8")] + public int Parent => MetadataTokens.GetToken(constant.Parent); public string ParentTooltip { get { @@ -90,6 +102,7 @@ namespace ICSharpCode.ILSpy.Metadata } } + [StringFormat("X")] public int Value => MetadataTokens.GetHeapOffset(constant.Value); public string ValueTooltip { diff --git a/ILSpy/Metadata/CorTables/CustomAttributeTableTreeNode.cs b/ILSpy/Metadata/CorTables/CustomAttributeTableTreeNode.cs index 8311c0935..d7b1c56e8 100644 --- a/ILSpy/Metadata/CorTables/CustomAttributeTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/CustomAttributeTableTreeNode.cs @@ -47,14 +47,25 @@ namespace ICSharpCode.ILSpy.Metadata var metadata = module.Metadata; var list = new List(); + CustomAttributeEntry scrollTargetEntry = default; foreach (var row in metadata.CustomAttributes) { - list.Add(new CustomAttributeEntry(module, row)); + CustomAttributeEntry entry = new CustomAttributeEntry(module, row); + if (scrollTarget == MetadataTokens.GetRowNumber(row)) { + scrollTargetEntry = entry; + } + list.Add(entry); } view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } @@ -98,7 +109,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X8")] + [StringFormat("X")] public int Value => MetadataTokens.GetHeapOffset(customAttr.Value); public string ValueTooltip { diff --git a/ILSpy/Metadata/CorTables/DeclSecurityTableTreeNode.cs b/ILSpy/Metadata/CorTables/DeclSecurityTableTreeNode.cs index 28a53074c..aa0f98d83 100644 --- a/ILSpy/Metadata/CorTables/DeclSecurityTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/DeclSecurityTableTreeNode.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System.Collections.Generic; +using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; @@ -47,14 +48,25 @@ namespace ICSharpCode.ILSpy.Metadata var metadata = module.Metadata; var list = new List(); + DeclSecurityEntry scrollTargetEntry = default; foreach (var row in metadata.DeclarativeSecurityAttributes) { - list.Add(new DeclSecurityEntry(module, row)); + var entry = new DeclSecurityEntry(module, row); + if (scrollTarget == MetadataTokens.GetRowNumber(row)) { + scrollTargetEntry = entry; + } + list.Add(entry); } view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } @@ -74,7 +86,8 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.DeclSecurity) + metadata.GetTableRowSize(TableIndex.DeclSecurity) * (RID - 1); - public int ParentHandle => MetadataTokens.GetToken(declSecAttr.Parent); + [StringFormat("X8")] + public int Parent => MetadataTokens.GetToken(declSecAttr.Parent); public string ParentTooltip { get { @@ -85,15 +98,17 @@ namespace ICSharpCode.ILSpy.Metadata } } - public int Action => (int)declSecAttr.Action; + [StringFormat("X8")] + public DeclarativeSecurityAction Action => declSecAttr.Action; public string ActionTooltip { get { - return null; + return declSecAttr.Action.ToString(); } } - public int PermissionSetHandle => MetadataTokens.GetHeapOffset(declSecAttr.PermissionSet); + [StringFormat("X")] + public int PermissionSet => MetadataTokens.GetHeapOffset(declSecAttr.PermissionSet); public string PermissionSetTooltip { get { diff --git a/ILSpy/Metadata/CorTables/EventMapTableTreeNode.cs b/ILSpy/Metadata/CorTables/EventMapTableTreeNode.cs index 823ee74e1..11c48393f 100644 --- a/ILSpy/Metadata/CorTables/EventMapTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/EventMapTableTreeNode.cs @@ -47,17 +47,28 @@ namespace ICSharpCode.ILSpy.Metadata var metadata = module.Metadata; var list = new List(); + EventMapEntry scrollTargetEntry = default; var length = metadata.GetTableRowCount(TableIndex.EventMap); byte* ptr = metadata.MetadataPointer; int metadataOffset = module.Reader.PEHeaders.MetadataStartOffset; for (int rid = 1; rid <= length; rid++) { - list.Add(new EventMapEntry(module, ptr, metadataOffset, rid)); + EventMapEntry entry = new EventMapEntry(module, ptr, metadataOffset, rid); + if (entry.RID == this.scrollTarget) { + scrollTargetEntry = entry; + } + list.Add(entry); } view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } diff --git a/ILSpy/Metadata/CorTables/EventTableTreeNode.cs b/ILSpy/Metadata/CorTables/EventTableTreeNode.cs index 7c9912693..46ad2ac8b 100644 --- a/ILSpy/Metadata/CorTables/EventTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/EventTableTreeNode.cs @@ -50,13 +50,25 @@ namespace ICSharpCode.ILSpy.Metadata var metadata = module.Metadata; var list = new List(); + EventDefEntry scrollTargetEntry = default; - foreach (var row in metadata.EventDefinitions) - list.Add(new EventDefEntry(module, row)); + foreach (var row in metadata.EventDefinitions) { + EventDefEntry entry = new EventDefEntry(module, row); + if (entry.RID == this.scrollTarget) { + scrollTargetEntry = entry; + } + list.Add(entry); + } view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } @@ -76,9 +88,12 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.Event) + metadata.GetTableRowSize(TableIndex.Event) * (RID - 1); + [StringFormat("X8")] public EventAttributes Attributes => eventDef.Attributes; - public object AttributesTooltip => new FlagsTooltip((int)eventDef.Attributes, typeof(EventAttributes)); + public object AttributesTooltip => new FlagsTooltip { + FlagGroup.CreateMultipleChoiceGroup(typeof(EventAttributes), selectedValue: (int)eventDef.Attributes, includeAll: false), + }; public string NameTooltip => $"{MetadataTokens.GetHeapOffset(eventDef.Name):X} \"{Name}\""; @@ -86,6 +101,7 @@ namespace ICSharpCode.ILSpy.Metadata IEntity IMemberTreeNode.Member => ((MetadataModule)module.GetTypeSystemOrNull()?.MainModule).GetDefinition(handle); + [StringFormat("X8")] public int Type => MetadataTokens.GetToken(eventDef.Type); public string TypeTooltip { diff --git a/ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs b/ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs index 4a1415c39..5e4761f2b 100644 --- a/ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs @@ -22,6 +22,7 @@ using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata @@ -45,20 +46,32 @@ namespace ICSharpCode.ILSpy.Metadata var view = Helpers.PrepareDataGrid(tabPage); var metadata = module.Metadata; var list = new List(); - + ExportedTypeEntry scrollTargetEntry = default; + foreach (var row in metadata.ExportedTypes) { - list.Add(new ExportedTypeEntry(module.Reader.PEHeaders.MetadataStartOffset, metadata, row, metadata.GetExportedType(row))); + ExportedTypeEntry entry = new ExportedTypeEntry(module.Reader.PEHeaders.MetadataStartOffset, module, row, metadata.GetExportedType(row)); + if (entry.RID == this.scrollTarget) { + scrollTargetEntry = entry; + } + list.Add(entry); } view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } struct ExportedTypeEntry { readonly int metadataOffset; + readonly PEFile module; readonly MetadataReader metadata; readonly ExportedTypeHandle handle; readonly ExportedType type; @@ -85,12 +98,25 @@ namespace ICSharpCode.ILSpy.Metadata public string TypeNamespace => metadata.GetString(type.Name); + [StringFormat("X8")] public int Implementation => MetadataTokens.GetToken(type.Implementation); - public ExportedTypeEntry(int metadataOffset, MetadataReader metadata, ExportedTypeHandle handle, ExportedType type) + public string ImplementationTooltip { + get { + if (type.Implementation.IsNil) + return null; + ITextOutput output = new PlainTextOutput(); + var context = new GenericContext(default(TypeDefinitionHandle), module); + type.Implementation.WriteTo(module, output, context); + return output.ToString(); + } + } + + public ExportedTypeEntry(int metadataOffset, PEFile module, ExportedTypeHandle handle, ExportedType type) { this.metadataOffset = metadataOffset; - this.metadata = metadata; + this.module = module; + this.metadata = module.Metadata; this.handle = handle; this.type = type; } diff --git a/ILSpy/Metadata/CorTables/FieldLayoutTableTreeNode.cs b/ILSpy/Metadata/CorTables/FieldLayoutTableTreeNode.cs index 44d801930..9461e2a5c 100644 --- a/ILSpy/Metadata/CorTables/FieldLayoutTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/FieldLayoutTableTreeNode.cs @@ -47,17 +47,28 @@ namespace ICSharpCode.ILSpy.Metadata var metadata = module.Metadata; var list = new List(); + FieldLayoutEntry scrollTargetEntry = default; var length = metadata.GetTableRowCount(TableIndex.FieldLayout); byte* ptr = metadata.MetadataPointer; int metadataOffset = module.Reader.PEHeaders.MetadataStartOffset; for (int rid = 1; rid <= length; rid++) { - list.Add(new FieldLayoutEntry(module, ptr, metadataOffset, rid)); + FieldLayoutEntry entry = new FieldLayoutEntry(module, ptr, metadataOffset, rid); + if (entry.RID == this.scrollTarget) { + scrollTargetEntry = entry; + } + list.Add(entry); } view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } diff --git a/ILSpy/Metadata/CorTables/FieldMarshalTableTreeNode.cs b/ILSpy/Metadata/CorTables/FieldMarshalTableTreeNode.cs index 9a6191fb5..d719fe6b3 100644 --- a/ILSpy/Metadata/CorTables/FieldMarshalTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/FieldMarshalTableTreeNode.cs @@ -47,17 +47,28 @@ namespace ICSharpCode.ILSpy.Metadata var metadata = module.Metadata; var list = new List(); + FieldMarshalEntry scrollTargetEntry = default; var length = metadata.GetTableRowCount(TableIndex.FieldMarshal); byte* ptr = metadata.MetadataPointer; int metadataOffset = module.Reader.PEHeaders.MetadataStartOffset; for (int rid = 1; rid <= length; rid++) { - list.Add(new FieldMarshalEntry(module, ptr, metadataOffset, rid)); + FieldMarshalEntry entry = new FieldMarshalEntry(module, ptr, metadataOffset, rid); + if (entry.RID == this.scrollTarget) { + scrollTargetEntry = entry; + } + list.Add(entry); } view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } @@ -97,8 +108,8 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X8")] - public int NativeType => (int)MetadataTokens.GetHeapOffset(fieldMarshal.NativeType); + [StringFormat("X")] + public int NativeType => MetadataTokens.GetHeapOffset(fieldMarshal.NativeType); public FieldMarshalEntry(PEFile module, byte* ptr, int metadataOffset, int row) { diff --git a/ILSpy/Metadata/CorTables/FieldRVATableTreeNode.cs b/ILSpy/Metadata/CorTables/FieldRVATableTreeNode.cs index b825a7b7d..261da84a7 100644 --- a/ILSpy/Metadata/CorTables/FieldRVATableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/FieldRVATableTreeNode.cs @@ -47,17 +47,28 @@ namespace ICSharpCode.ILSpy.Metadata var metadata = module.Metadata; var list = new List(); + FieldRVAEntry scrollTargetEntry = default; var length = metadata.GetTableRowCount(TableIndex.FieldRva); byte* ptr = metadata.MetadataPointer; int metadataOffset = module.Reader.PEHeaders.MetadataStartOffset; for (int rid = 1; rid <= length; rid++) { - list.Add(new FieldRVAEntry(module, ptr, metadataOffset, rid)); + FieldRVAEntry entry = new FieldRVAEntry(module, ptr, metadataOffset, rid); + if (entry.RID == this.scrollTarget) { + scrollTargetEntry = entry; + } + list.Add(entry); } view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } diff --git a/ILSpy/Metadata/CorTables/FieldTableTreeNode.cs b/ILSpy/Metadata/CorTables/FieldTableTreeNode.cs index 888e57b45..e0e59d1a8 100644 --- a/ILSpy/Metadata/CorTables/FieldTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/FieldTableTreeNode.cs @@ -16,11 +16,14 @@ // 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 System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Threading; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.IL; @@ -45,18 +48,30 @@ namespace ICSharpCode.ILSpy.Metadata { tabPage.Title = Text.ToString(); tabPage.SupportsLanguageSwitching = false; - var view = Helpers.PrepareDataGrid(tabPage); var metadata = module.Metadata; var list = new List(); - foreach (var row in metadata.FieldDefinitions) - list.Add(new FieldDefEntry(module, row)); + FieldDefEntry scrollTargetEntry = default; + + foreach (var row in metadata.FieldDefinitions) { + var entry = new FieldDefEntry(module, row); + if (scrollTarget.Equals(row)) { + scrollTargetEntry = entry; + } + list.Add(entry); + } view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } @@ -76,9 +91,15 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.Field) + metadata.GetTableRowSize(TableIndex.Field) * (RID - 1); + [StringFormat("X8")] public FieldAttributes Attributes => fieldDef.Attributes; - public object AttributesTooltip => new FlagsTooltip((int)fieldDef.Attributes, typeof(FieldAttributes)); + const FieldAttributes otherFlagsMask = ~(FieldAttributes.FieldAccessMask); + + public object AttributesTooltip => new FlagsTooltip() { + FlagGroup.CreateSingleChoiceGroup(typeof(FieldAttributes), "Field access: ", (int)FieldAttributes.FieldAccessMask, (int)(fieldDef.Attributes & FieldAttributes.FieldAccessMask), new Flag("CompilerControlled (0000)", 0, false), includeAny: false), + FlagGroup.CreateMultipleChoiceGroup(typeof(FieldAttributes), "Flags:", (int)otherFlagsMask, (int)(fieldDef.Attributes & otherFlagsMask), includeAll: false), + }; public string Name => metadata.GetString(fieldDef.Name); @@ -86,7 +107,7 @@ namespace ICSharpCode.ILSpy.Metadata IEntity IMemberTreeNode.Member => ((MetadataModule)module.GetTypeSystemOrNull()?.MainModule).GetDefinition(handle); - [StringFormat("X8")] + [StringFormat("X")] public int Signature => MetadataTokens.GetHeapOffset(fieldDef.Signature); public string SignatureTooltip { @@ -114,3 +135,22 @@ namespace ICSharpCode.ILSpy.Metadata } } } + +class Time : IDisposable +{ + readonly System.Diagnostics.Stopwatch stopwatch; + readonly string title; + + public Time(string title) + { + this.title = title; + this.stopwatch = new System.Diagnostics.Stopwatch(); + stopwatch.Start(); + } + + public void Dispose() + { + stopwatch.Stop(); + System.Diagnostics.Debug.WriteLine(title + " took " + stopwatch.ElapsedMilliseconds + "ms"); + } +} \ No newline at end of file diff --git a/ILSpy/Metadata/CorTables/FileTableTreeNode.cs b/ILSpy/Metadata/CorTables/FileTableTreeNode.cs index 234475e6d..81135a3af 100644 --- a/ILSpy/Metadata/CorTables/FileTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/FileTableTreeNode.cs @@ -45,14 +45,25 @@ namespace ICSharpCode.ILSpy.Metadata var metadata = module.Metadata; var list = new List(); + FileEntry scrollTargetEntry = default; foreach (var row in metadata.AssemblyFiles) { - list.Add(new FileEntry(module, row)); + FileEntry entry = new FileEntry(module, row); + if (entry.RID == this.scrollTarget) { + scrollTargetEntry = entry; + } + list.Add(entry); } view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } @@ -72,6 +83,7 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.File) + metadata.GetTableRowSize(TableIndex.File) * (RID - 1); + [StringFormat("X8")] public int Attributes => assemblyFile.ContainsMetadata ? 1 : 0; public string AttributesTooltip => assemblyFile.ContainsMetadata ? "ContainsMetaData" : "ContainsNoMetaData"; @@ -80,6 +92,7 @@ namespace ICSharpCode.ILSpy.Metadata public string NameTooltip => $"{MetadataTokens.GetHeapOffset(assemblyFile.Name):X} \"{Name}\""; + [StringFormat("X")] public int HashValue => MetadataTokens.GetHeapOffset(assemblyFile.HashValue); public string HashValueTooltip { diff --git a/ILSpy/Metadata/CorTables/GenericParamConstraintTableTreeNode.cs b/ILSpy/Metadata/CorTables/GenericParamConstraintTableTreeNode.cs index 0687d2e5c..1930023c6 100644 --- a/ILSpy/Metadata/CorTables/GenericParamConstraintTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/GenericParamConstraintTableTreeNode.cs @@ -47,13 +47,24 @@ namespace ICSharpCode.ILSpy.Metadata var metadata = module.Metadata; var list = new List(); + GenericParamConstraintEntry scrollTargetEntry = default; for (int row = 1; row <= metadata.GetTableRowCount(TableIndex.GenericParamConstraint); row++) { - list.Add(new GenericParamConstraintEntry(module, MetadataTokens.GenericParameterConstraintHandle(row))); + GenericParamConstraintEntry entry = new GenericParamConstraintEntry(module, MetadataTokens.GenericParameterConstraintHandle(row)); + if (entry.RID == this.scrollTarget) { + scrollTargetEntry = entry; + } + list.Add(entry); } view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } diff --git a/ILSpy/Metadata/CorTables/GenericParamTableTreeNode.cs b/ILSpy/Metadata/CorTables/GenericParamTableTreeNode.cs index 960fc821a..474267689 100644 --- a/ILSpy/Metadata/CorTables/GenericParamTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/GenericParamTableTreeNode.cs @@ -48,13 +48,24 @@ namespace ICSharpCode.ILSpy.Metadata var metadata = module.Metadata; var list = new List(); + GenericParamEntry scrollTargetEntry = default; for (int row = 1; row <= module.Metadata.GetTableRowCount(TableIndex.GenericParam); row++) { - list.Add(new GenericParamEntry(module, MetadataTokens.GenericParameterHandle(row))); + GenericParamEntry entry = new GenericParamEntry(module, MetadataTokens.GenericParameterHandle(row)); + if (entry.RID == this.scrollTarget) { + scrollTargetEntry = entry; + } + list.Add(entry); } view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } @@ -76,13 +87,18 @@ namespace ICSharpCode.ILSpy.Metadata public int Number => genericParam.Index; + [StringFormat("X8")] public GenericParameterAttributes Attributes => genericParam.Attributes; - public object AttributesTooltip => new FlagsTooltip((int)genericParam.Attributes, typeof(GenericParameterAttributes)); + public object AttributesTooltip => new FlagsTooltip { + FlagGroup.CreateSingleChoiceGroup(typeof(GenericParameterAttributes), "Code type: ", (int)GenericParameterAttributes.VarianceMask, (int)(genericParam.Attributes & GenericParameterAttributes.VarianceMask), new Flag("None (0000)", 0, false), includeAny: false), + FlagGroup.CreateSingleChoiceGroup(typeof(GenericParameterAttributes), "Managed type: ", (int)GenericParameterAttributes.SpecialConstraintMask, (int)(genericParam.Attributes & GenericParameterAttributes.SpecialConstraintMask), new Flag("None (0000)", 0, false), includeAny: false), + }; - public int OwnerHandle => MetadataTokens.GetToken(genericParam.Parent); + [StringFormat("X8")] + public int Owner => MetadataTokens.GetToken(genericParam.Parent); - public string Owner { + public string OwnerTooltip { get { ITextOutput output = new PlainTextOutput(); genericParam.Parent.WriteTo(module, output, GenericContext.Empty); diff --git a/ILSpy/Metadata/CorTables/ImplMapTableTreeNode.cs b/ILSpy/Metadata/CorTables/ImplMapTableTreeNode.cs index bb2144879..9ab610236 100644 --- a/ILSpy/Metadata/CorTables/ImplMapTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ImplMapTableTreeNode.cs @@ -49,17 +49,28 @@ namespace ICSharpCode.ILSpy.Metadata var metadata = module.Metadata; var list = new List(); + ImplMapEntry scrollTargetEntry = default; var length = metadata.GetTableRowCount(TableIndex.ImplMap); byte* ptr = metadata.MetadataPointer; int metadataOffset = module.Reader.PEHeaders.MetadataStartOffset; for (int rid = 1; rid <= length; rid++) { - list.Add(new ImplMapEntry(module, ptr, metadataOffset, rid)); + ImplMapEntry entry = new ImplMapEntry(module, ptr, metadataOffset, rid); + if (entry.RID == this.scrollTarget) { + scrollTargetEntry = entry; + } + list.Add(entry); } view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } @@ -91,9 +102,16 @@ namespace ICSharpCode.ILSpy.Metadata public int Offset { get; } + [StringFormat("X8")] public PInvokeAttributes MappingFlags => implMap.MappingFlags; - public object MappingFlagsTooltip => new FlagsTooltip((int)implMap.MappingFlags, typeof(PInvokeAttributes)); + const PInvokeAttributes otherFlagsMask = ~(PInvokeAttributes.CallConvMask | PInvokeAttributes.CharSetMask); + + public object MappingFlagsTooltip => new FlagsTooltip { + FlagGroup.CreateSingleChoiceGroup(typeof(PInvokeAttributes), "Character set: ", (int)PInvokeAttributes.CharSetMask, (int)(implMap.MappingFlags & PInvokeAttributes.CharSetMask), new Flag("CharSetNotSpec (0000)", 0, false), includeAny: false), + FlagGroup.CreateSingleChoiceGroup(typeof(PInvokeAttributes), "Calling convention: ", (int)PInvokeAttributes.CallConvMask, (int)(implMap.MappingFlags & PInvokeAttributes.CallConvMask), new Flag("Invalid (0000)", 0, false), includeAny: false), + FlagGroup.CreateMultipleChoiceGroup(typeof(PInvokeAttributes), "Flags:", (int)otherFlagsMask, (int)(implMap.MappingFlags & otherFlagsMask), includeAll: false), + }; [StringFormat("X8")] public int MemberForwarded => MetadataTokens.GetToken(implMap.MemberForwarded); diff --git a/ILSpy/Metadata/CorTables/InterfaceImplTableTreeNode.cs b/ILSpy/Metadata/CorTables/InterfaceImplTableTreeNode.cs index d533bfb8b..3510a3bd5 100644 --- a/ILSpy/Metadata/CorTables/InterfaceImplTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/InterfaceImplTableTreeNode.cs @@ -47,17 +47,28 @@ namespace ICSharpCode.ILSpy.Metadata var metadata = module.Metadata; var list = new List(); + InterfaceImplEntry scrollTargetEntry = default; var length = metadata.GetTableRowCount(TableIndex.InterfaceImpl); byte* ptr = metadata.MetadataPointer; int metadataOffset = module.Reader.PEHeaders.MetadataStartOffset; for (int rid = 1; rid <= length; rid++) { - list.Add(new InterfaceImplEntry(module, ptr, metadataOffset, rid)); + InterfaceImplEntry entry = new InterfaceImplEntry(module, ptr, metadataOffset, rid); + if (entry.RID == this.scrollTarget) { + scrollTargetEntry = entry; + } + list.Add(entry); } view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } diff --git a/ILSpy/Metadata/CorTables/ManifestResourceTableTreeNode.cs b/ILSpy/Metadata/CorTables/ManifestResourceTableTreeNode.cs index 948b9f247..1a81d973b 100644 --- a/ILSpy/Metadata/CorTables/ManifestResourceTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ManifestResourceTableTreeNode.cs @@ -48,14 +48,25 @@ namespace ICSharpCode.ILSpy.Metadata var metadata = module.Metadata; var list = new List(); + ManifestResourceEntry scrollTargetEntry = default; foreach (var row in metadata.ManifestResources) { - list.Add(new ManifestResourceEntry(module, row)); + ManifestResourceEntry entry = new ManifestResourceEntry(module, row); + if (entry.RID == this.scrollTarget) { + scrollTargetEntry = entry; + } + list.Add(entry); } view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } @@ -75,15 +86,17 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.ManifestResource) + metadata.GetTableRowSize(TableIndex.ManifestResource) * (RID - 1); + [StringFormat("X8")] public ManifestResourceAttributes Attributes => manifestResource.Attributes; - public object AttributesTooltip => new FlagsTooltip((int)manifestResource.Attributes, typeof(ManifestResourceAttributes)); + public object AttributesTooltip => manifestResource.Attributes.ToString(); public string Name => metadata.GetString(manifestResource.Name); public string NameTooltip => $"{MetadataTokens.GetHeapOffset(manifestResource.Name):X} \"{Name}\""; - public int ImplementationHandle => MetadataTokens.GetToken(manifestResource.Implementation); + [StringFormat("X8")] + public int Implementation => MetadataTokens.GetToken(manifestResource.Implementation); public string ImplementationTooltip { get { diff --git a/ILSpy/Metadata/CorTables/MemberRefTableTreeNode.cs b/ILSpy/Metadata/CorTables/MemberRefTableTreeNode.cs index 4b2fc896d..2f5f6e04b 100644 --- a/ILSpy/Metadata/CorTables/MemberRefTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MemberRefTableTreeNode.cs @@ -47,13 +47,25 @@ namespace ICSharpCode.ILSpy.Metadata var metadata = module.Metadata; var list = new List(); + MemberRefEntry scrollTargetEntry = default; - foreach (var row in metadata.MemberReferences) - list.Add(new MemberRefEntry(module, row)); + foreach (var row in metadata.MemberReferences) { + MemberRefEntry entry = new MemberRefEntry(module, row); + if (entry.RID == this.scrollTarget) { + scrollTargetEntry = entry; + } + list.Add(entry); + } view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } @@ -88,7 +100,7 @@ namespace ICSharpCode.ILSpy.Metadata public string NameTooltip => $"{MetadataTokens.GetHeapOffset(memberRef.Name):X} \"{Name}\""; - [StringFormat("X8")] + [StringFormat("X")] public int Signature => MetadataTokens.GetHeapOffset(memberRef.Signature); public string SignatureTooltip { diff --git a/ILSpy/Metadata/CorTables/MethodImplTableTreeNode.cs b/ILSpy/Metadata/CorTables/MethodImplTableTreeNode.cs index 4cf31cfc4..411b7df1a 100644 --- a/ILSpy/Metadata/CorTables/MethodImplTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MethodImplTableTreeNode.cs @@ -47,14 +47,25 @@ namespace ICSharpCode.ILSpy.Metadata var metadata = module.Metadata; var list = new List(); + MethodImplEntry scrollTargetEntry = default; for (int row = 1; row <= module.Metadata.GetTableRowCount(TableIndex.MethodImpl); row++) { - list.Add(new MethodImplEntry(module, MetadataTokens.MethodImplementationHandle(row))); + MethodImplEntry entry = new MethodImplEntry(module, MetadataTokens.MethodImplementationHandle(row)); + if (entry.RID == this.scrollTarget) { + scrollTargetEntry = entry; + } + list.Add(entry); } view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } @@ -74,6 +85,7 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.MethodDef) + metadata.GetTableRowSize(TableIndex.MethodDef) * (RID - 1); + [StringFormat("X8")] public int MethodDeclaration => MetadataTokens.GetToken(methodImpl.MethodDeclaration); public string MethodDeclarationTooltip { @@ -84,6 +96,7 @@ namespace ICSharpCode.ILSpy.Metadata } } + [StringFormat("X8")] public int MethodBody => MetadataTokens.GetToken(methodImpl.MethodBody); public string MethodBodyTooltip { @@ -94,6 +107,7 @@ namespace ICSharpCode.ILSpy.Metadata } } + [StringFormat("X8")] public int Type => MetadataTokens.GetToken(methodImpl.Type); public string TypeTooltip { diff --git a/ILSpy/Metadata/CorTables/MethodSemanticsTableTreeNode.cs b/ILSpy/Metadata/CorTables/MethodSemanticsTableTreeNode.cs index 328464917..b37149104 100644 --- a/ILSpy/Metadata/CorTables/MethodSemanticsTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MethodSemanticsTableTreeNode.cs @@ -48,13 +48,25 @@ namespace ICSharpCode.ILSpy.Metadata var metadata = module.Metadata; var list = new List(); + MethodSemanticsEntry scrollTargetEntry = default; - foreach (var row in metadata.GetMethodSemantics()) - list.Add(new MethodSemanticsEntry(module, row.Handle, row.Semantics, row.Method, row.Association)); + foreach (var row in metadata.GetMethodSemantics()) { + MethodSemanticsEntry entry = new MethodSemanticsEntry(module, row.Handle, row.Semantics, row.Method, row.Association); + if (entry.RID == this.scrollTarget) { + scrollTargetEntry = entry; + } + list.Add(entry); + } view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } @@ -76,13 +88,15 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.MethodDef) + metadata.GetTableRowSize(TableIndex.MethodDef) * (RID - 1); - public int Semantics => (int)semantics; + [StringFormat("X8")] + public MethodSemanticsAttributes Semantics => semantics; public string SemanticsTooltip => semantics.ToString(); - public int MethodHandle => MetadataTokens.GetToken(method); + [StringFormat("X8")] + public int Method => MetadataTokens.GetToken(method); - public string Method { + public string MethodTooltip { get { ITextOutput output = new PlainTextOutput(); ((EntityHandle)method).WriteTo(module, output, Decompiler.Metadata.GenericContext.Empty); @@ -90,9 +104,10 @@ namespace ICSharpCode.ILSpy.Metadata } } - public int AssociationHandle => MetadataTokens.GetToken(association); + [StringFormat("X8")] + public int Association => MetadataTokens.GetToken(association); - public string Association { + public string AssociationTooltip { get { ITextOutput output = new PlainTextOutput(); association.WriteTo(module, output, Decompiler.Metadata.GenericContext.Empty); diff --git a/ILSpy/Metadata/CorTables/MethodSpecTableTreeNode.cs b/ILSpy/Metadata/CorTables/MethodSpecTableTreeNode.cs index 8f176eb42..7e81f2e66 100644 --- a/ILSpy/Metadata/CorTables/MethodSpecTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MethodSpecTableTreeNode.cs @@ -47,13 +47,25 @@ namespace ICSharpCode.ILSpy.Metadata var metadata = module.Metadata; var list = new List(); - - foreach (var row in metadata.GetMethodSpecifications()) - list.Add(new MethodSpecEntry(module, row)); + MethodSpecEntry scrollTargetEntry = default; + + foreach (var row in metadata.GetMethodSpecifications()) { + MethodSpecEntry entry = new MethodSpecEntry(module, row); + if (entry.RID == this.scrollTarget) { + scrollTargetEntry = entry; + } + list.Add(entry); + } view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } @@ -73,9 +85,10 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.MethodSpec) + metadata.GetTableRowSize(TableIndex.MethodSpec) * (RID-1); - public int MethodHandle => MetadataTokens.GetToken(methodSpec.Method); + [StringFormat("X8")] + public int Method => MetadataTokens.GetToken(methodSpec.Method); - public string Method { + public string MethodTooltip { get { ITextOutput output = new PlainTextOutput(); methodSpec.Method.WriteTo(module, output, GenericContext.Empty); @@ -83,6 +96,7 @@ namespace ICSharpCode.ILSpy.Metadata } } + [StringFormat("X")] public int Signature => MetadataTokens.GetHeapOffset(methodSpec.Signature); public string SignatureTooltip { diff --git a/ILSpy/Metadata/CorTables/MethodTableTreeNode.cs b/ILSpy/Metadata/CorTables/MethodTableTreeNode.cs index d6173f413..73fe31429 100644 --- a/ILSpy/Metadata/CorTables/MethodTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MethodTableTreeNode.cs @@ -49,13 +49,25 @@ namespace ICSharpCode.ILSpy.Metadata var view = Helpers.PrepareDataGrid(tabPage); var metadata = module.Metadata; var list = new List(); + MethodDefEntry scrollTargetEntry = default; - foreach (var row in metadata.MethodDefinitions) - list.Add(new MethodDefEntry(module, row)); + foreach (var row in metadata.MethodDefinitions) { + MethodDefEntry entry = new MethodDefEntry(module, row); + if (entry.RID == scrollTarget) { + scrollTargetEntry = entry; + } + list.Add(entry); + } view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } @@ -75,13 +87,24 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.MethodDef) + metadata.GetTableRowSize(TableIndex.MethodDef) * (RID - 1); + [StringFormat("X8")] public MethodAttributes Attributes => methodDef.Attributes; - public object AttributesTooltip => new FlagsTooltip((int)methodDef.Attributes, typeof(MethodAttributes)); + const MethodAttributes otherFlagsMask = ~(MethodAttributes.MemberAccessMask | MethodAttributes.VtableLayoutMask); + + public object AttributesTooltip => new FlagsTooltip { + FlagGroup.CreateSingleChoiceGroup(typeof(MethodAttributes), "Member access: ", (int)MethodAttributes.MemberAccessMask, (int)(methodDef.Attributes & MethodAttributes.MemberAccessMask), new Flag("CompilerControlled (0000)", 0, false), includeAny: false), + FlagGroup.CreateSingleChoiceGroup(typeof(MethodAttributes), "Vtable layout: ", (int)MethodAttributes.VtableLayoutMask, (int)(methodDef.Attributes & MethodAttributes.VtableLayoutMask), new Flag("ReuseSlot (0000)", 0, false), includeAny: false), + FlagGroup.CreateMultipleChoiceGroup(typeof(MethodAttributes), "Flags:", (int)otherFlagsMask, (int)(methodDef.Attributes & otherFlagsMask), includeAll: false), + }; + [StringFormat("X8")] public MethodImplAttributes ImplAttributes => methodDef.ImplAttributes; - public object ImplAttributesTooltip => new FlagsTooltip((int)methodDef.ImplAttributes, typeof(MethodImplAttributes)); + public object ImplAttributesTooltip => new FlagsTooltip { + FlagGroup.CreateSingleChoiceGroup(typeof(MethodImplAttributes), "Code type: ", (int)MethodImplAttributes.CodeTypeMask, (int)(methodDef.ImplAttributes & MethodImplAttributes.CodeTypeMask), new Flag("IL (0000)", 0, false), includeAny: false), + FlagGroup.CreateSingleChoiceGroup(typeof(MethodImplAttributes), "Managed type: ", (int)MethodImplAttributes.ManagedMask, (int)(methodDef.ImplAttributes & MethodImplAttributes.ManagedMask), new Flag("Managed (0000)", 0, false), includeAny: false), + }; public int RVA => methodDef.RelativeVirtualAddress; @@ -89,7 +112,7 @@ namespace ICSharpCode.ILSpy.Metadata public string NameTooltip => $"{MetadataTokens.GetHeapOffset(methodDef.Name):X} \"{Name}\""; - [StringFormat("X8")] + [StringFormat("X")] public int Signature => MetadataTokens.GetHeapOffset(methodDef.Signature); string signatureTooltip; diff --git a/ILSpy/Metadata/CorTables/ModuleRefTableTreeNode.cs b/ILSpy/Metadata/CorTables/ModuleRefTableTreeNode.cs index 5177f31ee..61f88a2e1 100644 --- a/ILSpy/Metadata/CorTables/ModuleRefTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ModuleRefTableTreeNode.cs @@ -45,13 +45,25 @@ namespace ICSharpCode.ILSpy.Metadata var metadata = module.Metadata; var list = new List(); - - foreach (var row in metadata.GetModuleReferences()) - list.Add(new ModuleRefEntry(module, row)); + ModuleRefEntry scrollTargetEntry = default; + + foreach (var row in metadata.GetModuleReferences()) { + ModuleRefEntry entry = new ModuleRefEntry(module, row); + if (entry.RID == this.scrollTarget) { + scrollTargetEntry = entry; + } + list.Add(entry); + } view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } diff --git a/ILSpy/Metadata/CorTables/ModuleTableTreeNode.cs b/ILSpy/Metadata/CorTables/ModuleTableTreeNode.cs index 3fd2994b6..a5965e722 100644 --- a/ILSpy/Metadata/CorTables/ModuleTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ModuleTableTreeNode.cs @@ -45,12 +45,19 @@ namespace ICSharpCode.ILSpy.Metadata var metadata = module.Metadata; var list = new List(); - + ModuleEntry scrollTargetEntry = default; + list.Add(new ModuleEntry(module, EntityHandle.ModuleDefinition)); view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } @@ -76,14 +83,17 @@ namespace ICSharpCode.ILSpy.Metadata public string NameTooltip => $"{MetadataTokens.GetHeapOffset(moduleDef.Name):X} \"{Name}\""; + [StringFormat("X")] public int Mvid => MetadataTokens.GetHeapOffset(moduleDef.Mvid); public string MvidTooltip => metadata.GetGuid(moduleDef.Mvid).ToString(); + [StringFormat("X")] public int GenerationId => MetadataTokens.GetHeapOffset(moduleDef.GenerationId); public string GenerationIdTooltip => moduleDef.GenerationId.IsNil ? null : metadata.GetGuid(moduleDef.GenerationId).ToString(); + [StringFormat("X")] public int BaseGenerationId => MetadataTokens.GetHeapOffset(moduleDef.BaseGenerationId); public string BaseGenerationIdTooltip => moduleDef.BaseGenerationId.IsNil ? null : metadata.GetGuid(moduleDef.BaseGenerationId).ToString(); diff --git a/ILSpy/Metadata/CorTables/NestedClassTableTreeNode.cs b/ILSpy/Metadata/CorTables/NestedClassTableTreeNode.cs index b6f89cc1e..f7a1d27aa 100644 --- a/ILSpy/Metadata/CorTables/NestedClassTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/NestedClassTableTreeNode.cs @@ -47,17 +47,28 @@ namespace ICSharpCode.ILSpy.Metadata var metadata = module.Metadata; var list = new List(); + NestedClassEntry scrollTargetEntry = default; var length = metadata.GetTableRowCount(TableIndex.NestedClass); byte* ptr = metadata.MetadataPointer; int metadataOffset = module.Reader.PEHeaders.MetadataStartOffset; for (int rid = 1; rid <= length; rid++) { - list.Add(new NestedClassEntry(module, ptr, metadataOffset, rid)); + NestedClassEntry entry = new NestedClassEntry(module, ptr, metadataOffset, rid); + if (entry.RID == this.scrollTarget) { + scrollTargetEntry = entry; + } + list.Add(entry); } view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } diff --git a/ILSpy/Metadata/CorTables/ParamTableTreeNode.cs b/ILSpy/Metadata/CorTables/ParamTableTreeNode.cs index f12e93c93..6e9e33b07 100644 --- a/ILSpy/Metadata/CorTables/ParamTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ParamTableTreeNode.cs @@ -46,14 +46,25 @@ namespace ICSharpCode.ILSpy.Metadata var metadata = module.Metadata; var list = new List(); + ParamEntry scrollTargetEntry = default; for (int row = 1; row <= module.Metadata.GetTableRowCount(TableIndex.Param); row++) { - list.Add(new ParamEntry(module, MetadataTokens.ParameterHandle(row))); + ParamEntry entry = new ParamEntry(module, MetadataTokens.ParameterHandle(row)); + if (entry.RID == this.scrollTarget) { + scrollTargetEntry = entry; + } + list.Add(entry); } view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } @@ -73,9 +84,12 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.Param) + metadata.GetTableRowSize(TableIndex.Param) * (RID-1); + [StringFormat("X8")] public ParameterAttributes Attributes => param.Attributes; - public object AttributesTooltip => new FlagsTooltip((int)param.Attributes, typeof(ParameterAttributes)); + public object AttributesTooltip => new FlagsTooltip { + FlagGroup.CreateMultipleChoiceGroup(typeof(ParameterAttributes), selectedValue: (int)param.Attributes, includeAll: false) + }; public string Name => metadata.GetString(param.Name); diff --git a/ILSpy/Metadata/CorTables/PropertyMapTableTreeNode.cs b/ILSpy/Metadata/CorTables/PropertyMapTableTreeNode.cs index c196ac9f2..be6d33b72 100644 --- a/ILSpy/Metadata/CorTables/PropertyMapTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/PropertyMapTableTreeNode.cs @@ -47,17 +47,28 @@ namespace ICSharpCode.ILSpy.Metadata var metadata = module.Metadata; var list = new List(); + PropertyMapEntry scrollTargetEntry = default; var length = metadata.GetTableRowCount(TableIndex.PropertyMap); byte* ptr = metadata.MetadataPointer; int metadataOffset = module.Reader.PEHeaders.MetadataStartOffset; for (int rid = 1; rid <= length; rid++) { - list.Add(new PropertyMapEntry(module, ptr, metadataOffset, rid)); + PropertyMapEntry entry = new PropertyMapEntry(module, ptr, metadataOffset, rid); + if (entry.RID == this.scrollTarget) { + scrollTargetEntry = entry; + } + list.Add(entry); } view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } diff --git a/ILSpy/Metadata/CorTables/PropertyTableTreeNode.cs b/ILSpy/Metadata/CorTables/PropertyTableTreeNode.cs index 82ce43c66..a2e651437 100644 --- a/ILSpy/Metadata/CorTables/PropertyTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/PropertyTableTreeNode.cs @@ -50,13 +50,25 @@ namespace ICSharpCode.ILSpy.Metadata var metadata = module.Metadata; var list = new List(); + PropertyDefEntry scrollTargetEntry = default; - foreach (var row in metadata.PropertyDefinitions) - list.Add(new PropertyDefEntry(module, row)); + foreach (var row in metadata.PropertyDefinitions) { + PropertyDefEntry entry = new PropertyDefEntry(module, row); + if (entry.RID == this.scrollTarget) { + scrollTargetEntry = entry; + } + list.Add(entry); + } view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } @@ -76,9 +88,12 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.Property) + metadata.GetTableRowSize(TableIndex.Property) * (RID - 1); + [StringFormat("X8")] public PropertyAttributes Attributes => propertyDef.Attributes; - public object AttributesTooltip => new FlagsTooltip((int)propertyDef.Attributes, typeof(PropertyAttributes)); + public object AttributesTooltip => new FlagsTooltip { + FlagGroup.CreateMultipleChoiceGroup(typeof(PropertyAttributes), selectedValue: (int)propertyDef.Attributes, includeAll: false), + }; public string Name => metadata.GetString(propertyDef.Name); @@ -86,6 +101,7 @@ namespace ICSharpCode.ILSpy.Metadata IEntity IMemberTreeNode.Member => ((MetadataModule)module.GetTypeSystemOrNull()?.MainModule).GetDefinition(handle); + [StringFormat("X")] public int Signature => MetadataTokens.GetHeapOffset(propertyDef.Signature); public string SignatureTooltip { diff --git a/ILSpy/Metadata/CorTables/StandAloneSigTableTreeNode.cs b/ILSpy/Metadata/CorTables/StandAloneSigTableTreeNode.cs index 02f1716fd..c34e6b855 100644 --- a/ILSpy/Metadata/CorTables/StandAloneSigTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/StandAloneSigTableTreeNode.cs @@ -21,6 +21,8 @@ using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.Disassembler; +using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata @@ -45,14 +47,25 @@ namespace ICSharpCode.ILSpy.Metadata var metadata = module.Metadata; var list = new List(); + StandAloneSigEntry scrollTargetEntry = default; for (int row = 1; row <= module.Metadata.GetTableRowCount(TableIndex.StandAloneSig); row++) { - list.Add(new StandAloneSigEntry(module, MetadataTokens.StandaloneSignatureHandle(row))); + StandAloneSigEntry entry = new StandAloneSigEntry(module, MetadataTokens.StandaloneSignatureHandle(row)); + if (entry.RID == this.scrollTarget) { + scrollTargetEntry = entry; + } + list.Add(entry); } view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } @@ -72,11 +85,15 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.StandAloneSig) + metadata.GetTableRowSize(TableIndex.StandAloneSig) * (RID - 1); + [StringFormat("X")] public int Signature => MetadataTokens.GetHeapOffset(standaloneSig.Signature); public string SignatureTooltip { get { - return null; + ITextOutput output = new PlainTextOutput(); + var context = new Decompiler.Metadata.GenericContext(default(TypeDefinitionHandle), module); + ((EntityHandle)handle).WriteTo(module, output, context); + return output.ToString(); } } diff --git a/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs b/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs index ac773084d..7bd699c14 100644 --- a/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs @@ -24,6 +24,7 @@ using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Disassembler; +using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.TreeNodes; @@ -50,13 +51,25 @@ namespace ICSharpCode.ILSpy.Metadata var metadata = module.Metadata; var list = new List(); + TypeDefEntry scrollTargetEntry = default; - foreach (var row in metadata.TypeDefinitions) - list.Add(new TypeDefEntry(module, row)); + foreach (var row in metadata.TypeDefinitions) { + TypeDefEntry entry = new TypeDefEntry(module, row); + if (entry.RID == this.scrollTarget) { + scrollTargetEntry = entry; + } + list.Add(entry); + } view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } @@ -76,9 +89,19 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.TypeDef) + metadata.GetTableRowSize(TableIndex.TypeDef) * (RID-1); + [StringFormat("X8")] public TypeAttributes Attributes => typeDef.Attributes; - public object AttributesTooltip => new FlagsTooltip((int)typeDef.Attributes, typeof(TypeAttributes)); + const TypeAttributes otherFlagsMask = ~(TypeAttributes.VisibilityMask | TypeAttributes.LayoutMask | TypeAttributes.ClassSemanticsMask | TypeAttributes.StringFormatMask | TypeAttributes.CustomFormatMask); + + public object AttributesTooltip => new FlagsTooltip { + FlagGroup.CreateSingleChoiceGroup(typeof(TypeAttributes), "Visibility: ", (int)TypeAttributes.VisibilityMask, (int)(typeDef.Attributes & TypeAttributes.VisibilityMask), new Flag("NotPublic (0000)", 0, false), includeAny: false), + FlagGroup.CreateSingleChoiceGroup(typeof(TypeAttributes), "Class layout: ", (int)TypeAttributes.LayoutMask, (int)(typeDef.Attributes & TypeAttributes.LayoutMask), new Flag("AutoLayout (0000)", 0, false), includeAny: false), + FlagGroup.CreateSingleChoiceGroup(typeof(TypeAttributes), "Class semantics: ", (int)TypeAttributes.ClassSemanticsMask, (int)(typeDef.Attributes & TypeAttributes.ClassSemanticsMask), new Flag("Class (0000)", 0, false), includeAny: false), + FlagGroup.CreateSingleChoiceGroup(typeof(TypeAttributes), "String format: ", (int)TypeAttributes.StringFormatMask, (int)(typeDef.Attributes & TypeAttributes.StringFormatMask), new Flag("AnsiClass (0000)", 0, false), includeAny: false), + FlagGroup.CreateSingleChoiceGroup(typeof(TypeAttributes), "Custom format: ", (int)TypeAttributes.CustomFormatMask, (int)(typeDef.Attributes & TypeAttributes.CustomFormatMask), new Flag("Value0 (0000)", 0, false), includeAny: false), + FlagGroup.CreateMultipleChoiceGroup(typeof(TypeAttributes), "Flags:", (int)otherFlagsMask, (int)(typeDef.Attributes & otherFlagsMask), includeAll: false), + }; public string NameTooltip => $"{MetadataTokens.GetHeapOffset(typeDef.Name):X} \"{Name}\""; @@ -116,9 +139,33 @@ namespace ICSharpCode.ILSpy.Metadata [StringFormat("X8")] public int FieldList => MetadataTokens.GetToken(typeDef.GetFields().FirstOrDefault()); + public string FieldListTooltip { + get { + var field = typeDef.GetFields().FirstOrDefault(); + if (field.IsNil) + return null; + ITextOutput output = new PlainTextOutput(); + var context = new Decompiler.Metadata.GenericContext(default(TypeDefinitionHandle), module); + ((EntityHandle)field).WriteTo(module, output, context); + return output.ToString(); + } + } + [StringFormat("X8")] public int MethodList => MetadataTokens.GetToken(typeDef.GetMethods().FirstOrDefault()); + public string MethodListTooltip { + get { + var method = typeDef.GetMethods().FirstOrDefault(); + if (method.IsNil) + return null; + ITextOutput output = new PlainTextOutput(); + var context = new Decompiler.Metadata.GenericContext(default(TypeDefinitionHandle), module); + ((EntityHandle)method).WriteTo(module, output, context); + return output.ToString(); + } + } + IEntity IMemberTreeNode.Member => ((MetadataModule)module.GetTypeSystemOrNull()?.MainModule).GetDefinition(handle); public TypeDefEntry(PEFile module, TypeDefinitionHandle handle) diff --git a/ILSpy/Metadata/CorTables/TypeRefTableTreeNode.cs b/ILSpy/Metadata/CorTables/TypeRefTableTreeNode.cs index 921bb5c85..fe1c2e99b 100644 --- a/ILSpy/Metadata/CorTables/TypeRefTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/TypeRefTableTreeNode.cs @@ -46,13 +46,25 @@ namespace ICSharpCode.ILSpy.Metadata var metadata = module.Metadata; var list = new List(); - - foreach (var row in metadata.TypeReferences) - list.Add(new TypeRefEntry(module, row)); + TypeRefEntry scrollTargetEntry = default; + + foreach (var row in metadata.TypeReferences) { + TypeRefEntry entry = new TypeRefEntry(module, row); + if (entry.RID == this.scrollTarget) { + scrollTargetEntry = entry; + } + list.Add(entry); + } view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } diff --git a/ILSpy/Metadata/CorTables/TypeSpecTableTreeNode.cs b/ILSpy/Metadata/CorTables/TypeSpecTableTreeNode.cs index 84c400781..68d7f56b6 100644 --- a/ILSpy/Metadata/CorTables/TypeSpecTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/TypeSpecTableTreeNode.cs @@ -46,13 +46,25 @@ namespace ICSharpCode.ILSpy.Metadata var metadata = module.Metadata; var list = new List(); + TypeSpecEntry scrollTargetEntry = default; - foreach (var row in metadata.GetTypeSpecifications()) - list.Add(new TypeSpecEntry(module, row)); + foreach (var row in metadata.GetTypeSpecifications()) { + TypeSpecEntry entry = new TypeSpecEntry(module, row); + if (scrollTarget.Equals(row)) { + scrollTargetEntry = entry; + } + list.Add(entry); + } view.ItemsSource = list; tabPage.Content = view; + + if (scrollTargetEntry.RID > 0) { + view.ScrollIntoView(scrollTargetEntry); + this.scrollTarget = default; + } + return true; } @@ -72,6 +84,7 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.TypeSpec) + metadata.GetTableRowSize(TableIndex.TypeSpec) * (RID-1); + [StringFormat("X")] public int Signature => MetadataTokens.GetHeapOffset(typeSpec.Signature); public string SignatureTooltip { diff --git a/ILSpy/Metadata/DataGridCustomTextColumn.cs b/ILSpy/Metadata/DataGridCustomTextColumn.cs index 5366abfa1..43aa6ede9 100644 --- a/ILSpy/Metadata/DataGridCustomTextColumn.cs +++ b/ILSpy/Metadata/DataGridCustomTextColumn.cs @@ -19,6 +19,7 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Data; +using System.Windows.Input; using System.Windows.Media; namespace ICSharpCode.ILSpy.Metadata @@ -32,9 +33,17 @@ namespace ICSharpCode.ILSpy.Metadata TextBox textBox = new TextBox() { Style = (Style)MetadataTableViews.Instance["DataGridCustomTextColumnTextBoxStyle"] }; BindingOperations.SetBinding(textBox, TextBox.TextProperty, Binding); if (ToolTipBinding != null) { - BindingOperations.SetBinding(textBox, TextBox.ToolTipProperty, ToolTipBinding); + textBox.MouseMove += TextBox_MouseMove; } return textBox; } + + private void TextBox_MouseMove(object sender, MouseEventArgs e) + { + e.Handled = true; + var textBox = (TextBox)sender; + BindingOperations.SetBinding(textBox, TextBox.ToolTipProperty, ToolTipBinding); + textBox.MouseMove -= TextBox_MouseMove; + } } } diff --git a/ILSpy/Metadata/FlagsFilterControl.xaml.cs b/ILSpy/Metadata/FlagsFilterControl.xaml.cs index 89c00b65f..f1b21f1d8 100644 --- a/ILSpy/Metadata/FlagsFilterControl.xaml.cs +++ b/ILSpy/Metadata/FlagsFilterControl.xaml.cs @@ -41,7 +41,7 @@ namespace ICSharpCode.ILSpy.Metadata base.OnApplyTemplate(); listBox = Template.FindName("ListBox", this) as ListBox; - listBox.ItemsSource = CreateFlags(FlagsType); + listBox.ItemsSource = FlagGroup.GetFlags(FlagsType, neutralItem: ""); var filter = Filter; @@ -50,19 +50,6 @@ namespace ICSharpCode.ILSpy.Metadata } } - internal static IEnumerable CreateFlags(Type flagsType, bool includeAll = true) - { - if (includeAll) - yield return new Flag("", -1); - - foreach (var item in flagsType.GetFields(BindingFlags.Static | BindingFlags.Public)) { - if (item.Name.EndsWith("Mask", StringComparison.Ordinal)) - continue; - int value = (int)CSharpPrimitiveCast.Cast(TypeCode.Int32, item.GetRawConstantValue(), false); - yield return new Flag($"{item.Name} ({value:X4})", value); - } - } - private void Filter_Changed() { var filter = Filter; @@ -110,7 +97,10 @@ namespace ICSharpCode.ILSpy.Metadata public bool IsMatch(object value) { - return (Mask & (int)value) != 0; + if (value == null) + return true; + + return Mask == -1 || (Mask & (int)value) != 0; } } } diff --git a/ILSpy/Metadata/FlagsTooltip.xaml b/ILSpy/Metadata/FlagsTooltip.xaml index 07e5b8351..dd3fb04a2 100644 --- a/ILSpy/Metadata/FlagsTooltip.xaml +++ b/ILSpy/Metadata/FlagsTooltip.xaml @@ -8,17 +8,37 @@ mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> - + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + diff --git a/ILSpy/Metadata/FlagsTooltip.xaml.cs b/ILSpy/Metadata/FlagsTooltip.xaml.cs index a52e77389..d0a7d096f 100644 --- a/ILSpy/Metadata/FlagsTooltip.xaml.cs +++ b/ILSpy/Metadata/FlagsTooltip.xaml.cs @@ -1,34 +1,59 @@ -using System; +// Copyright (c) 2011 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; using System.Collections.Generic; using System.Globalization; using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Reflection; using System.Windows; -using System.Windows.Controls; using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; +using ICSharpCode.Decompiler.Util; namespace ICSharpCode.ILSpy.Metadata { /// /// Interaction logic for FlagsTooltip.xaml /// - public partial class FlagsTooltip + public partial class FlagsTooltip : IEnumerable { - public FlagsTooltip(int value, Type flagsType) + public FlagsTooltip(int value = 0, Type flagsType = null) { - this.Flags = FlagsFilterControl.CreateFlags(flagsType, includeAll: false); InitializeComponent(); - ((FlagActiveConverter)Resources["flagActiveConv"]).Value = value; } - public IEnumerable Flags { get; } + public void Add(FlagGroup group) + { + Groups.Add(group); + } + + public IEnumerator GetEnumerator() + { + return Groups.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return Groups.GetEnumerator(); + } + + public List Groups { get; } = new List(); } class FlagActiveConverter : DependencyObject, IValueConverter @@ -56,11 +81,89 @@ namespace ICSharpCode.ILSpy.Metadata { public string Name { get; } public int Value { get; } + public bool IsSelected { get; } - public Flag(string name, int value) + public Flag(string name, int value, bool isSelected) { this.Name = name; this.Value = value; + this.IsSelected = isSelected; + } + } + + public abstract class FlagGroup + { + public static MultipleChoiceGroup CreateMultipleChoiceGroup(Type flagsType, string header = null, int mask = -1, int selectedValue = 0, bool includeAll = true) + { + MultipleChoiceGroup group = new MultipleChoiceGroup(GetFlags(flagsType, mask, selectedValue, includeAll ? "" : null)); + group.Header = header; + group.SelectedFlags = selectedValue; + return group; + } + + public static SingleChoiceGroup CreateSingleChoiceGroup(Type flagsType, string header = null, int mask = -1, int selectedValue = 0, Flag defaultFlag = default, bool includeAny = true) + { + var group = new SingleChoiceGroup(GetFlags(flagsType, mask, selectedValue, includeAny ? "" : null)); + group.Header = header; + group.SelectedFlag = group.Flags.SingleOrDefault(f => f.Value == selectedValue); + if (group.SelectedFlag.Name == null) { + group.SelectedFlag = defaultFlag; + } + return group; + } + + public static IEnumerable GetFlags(Type flagsType, int mask = -1, int selectedValues = 0, string neutralItem = null) + { + if (neutralItem != null) + yield return new Flag(neutralItem, -1, false); + + foreach (var item in flagsType.GetFields(BindingFlags.Static | BindingFlags.Public)) { + if (item.Name.EndsWith("Mask", StringComparison.Ordinal)) + continue; + int value = (int)CSharpPrimitiveCast.Cast(TypeCode.Int32, item.GetRawConstantValue(), false); + if ((value & mask) == 0) + continue; + yield return new Flag($"{item.Name} ({value:X4})", value, (selectedValues & value) != 0); + } + } + + public string Header { get; set; } + + public IList Flags { get; protected set; } + } + + public class MultipleChoiceGroup : FlagGroup + { + public MultipleChoiceGroup(IEnumerable flags) + { + this.Flags = flags.ToList(); + } + + public int SelectedFlags { get; set; } + } + + public class SingleChoiceGroup : FlagGroup + { + public SingleChoiceGroup(IEnumerable flags) + { + this.Flags = flags.ToList(); + } + + public Flag SelectedFlag { get; set; } + } + + class NullVisibilityConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value == null) + return Visibility.Collapsed; + return Visibility.Visible; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); } } } diff --git a/ILSpy/Metadata/Helpers.cs b/ILSpy/Metadata/Helpers.cs index f3ff67320..47ca7e1b1 100644 --- a/ILSpy/Metadata/Helpers.cs +++ b/ILSpy/Metadata/Helpers.cs @@ -34,6 +34,7 @@ using System.Windows.Media; using DataGridExtensions; using ICSharpCode.Decompiler.Util; using ICSharpCode.ILSpy.Controls; +using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.ViewModels; namespace ICSharpCode.ILSpy.Metadata @@ -50,6 +51,9 @@ namespace ICSharpCode.ILSpy.Metadata CanUserDeleteRows = false, CanUserReorderColumns = false, RowHeaderWidth = 0, + EnableColumnVirtualization = true, + EnableRowVirtualization = true, + IsReadOnly = true, SelectionMode = DataGridSelectionMode.Single, SelectionUnit = DataGridSelectionUnit.FullRow, CellStyle = new Style { @@ -66,6 +70,11 @@ namespace ICSharpCode.ILSpy.Metadata DataGridFilter.SetIsAutoFilterEnabled(view, true); DataGridFilter.SetContentFilterFactory(view, new RegexContentFilterFactory()); } + view.RowDetailsTemplateSelector = null; + view.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.Collapsed; + if (!view.AutoGenerateColumns) + view.Columns.Clear(); + view.AutoGenerateColumns = true; view.AutoGeneratingColumn += View_AutoGeneratingColumn; view.AutoGeneratedColumns += View_AutoGeneratedColumns; @@ -99,14 +108,6 @@ namespace ICSharpCode.ILSpy.Metadata e.Column.SetTemplate((ControlTemplate)MetadataTableViews.Instance["HexFilter"]); ((DataGridCustomTextColumn)e.Column).ToolTipBinding = null; break; - case "Flags": - case "Attributes": - case "ImplAttributes": - case "MappingFlags": - binding.Converter = new UnderlyingEnumValueConverter(); - binding.StringFormat = "X4"; - e.Column.SetTemplate((ControlTemplate)MetadataTableViews.Instance[e.PropertyType.Name + "Filter"]); - break; default: e.Cancel = e.PropertyName.Contains("Tooltip"); if (!e.Cancel) { @@ -119,12 +120,16 @@ namespace ICSharpCode.ILSpy.Metadata } } - static void ApplyAttributes(PropertyDescriptor descriptor, BindingBase binding, DataGridColumn column) + static void ApplyAttributes(PropertyDescriptor descriptor, Binding binding, DataGridColumn column) { + if (descriptor.PropertyType.IsEnum) { + binding.Converter = new UnderlyingEnumValueConverter(); + column.SetTemplate((ControlTemplate)MetadataTableViews.Instance[descriptor.PropertyType.Name + "Filter"]); + } var stringFormat = descriptor.Attributes.OfType().FirstOrDefault(); if (stringFormat != null) { binding.StringFormat = stringFormat.Format; - if (stringFormat.Format.StartsWith("X", StringComparison.OrdinalIgnoreCase)) { + if (!descriptor.PropertyType.IsEnum && stringFormat.Format.StartsWith("X", StringComparison.OrdinalIgnoreCase)) { column.SetTemplate((ControlTemplate)MetadataTableViews.Instance["HexFilter"]); } } diff --git a/ILSpy/Metadata/MetadataProtocolHandler.cs b/ILSpy/Metadata/MetadataProtocolHandler.cs index 58a1c72b7..41d9ea749 100644 --- a/ILSpy/Metadata/MetadataProtocolHandler.cs +++ b/ILSpy/Metadata/MetadataProtocolHandler.cs @@ -24,7 +24,9 @@ namespace ICSharpCode.ILSpy.Metadata var mxNode = assemblyTreeNode.Children.OfType().FirstOrDefault(); if (mxNode != null) { mxNode.EnsureLazyChildren(); - return mxNode.FindNodeByHandleKind(handle.Kind); + var node = mxNode.FindNodeByHandleKind(handle.Kind); + node?.ScrollTo(handle); + return node; } return null; } diff --git a/ILSpy/Metadata/MetadataTableTreeNode.cs b/ILSpy/Metadata/MetadataTableTreeNode.cs index c333ffa95..39be684a2 100644 --- a/ILSpy/Metadata/MetadataTableTreeNode.cs +++ b/ILSpy/Metadata/MetadataTableTreeNode.cs @@ -16,9 +16,13 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System; using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.TreeNodes; +using ICSharpCode.ILSpy.ViewModels; using ICSharpCode.TreeView; namespace ICSharpCode.ILSpy.Metadata @@ -26,6 +30,7 @@ namespace ICSharpCode.ILSpy.Metadata internal abstract class MetadataTableTreeNode : ILSpyTreeNode { protected PEFile module; + protected int scrollTarget; public HandleKind Kind { get; } @@ -34,5 +39,10 @@ namespace ICSharpCode.ILSpy.Metadata this.module = module; this.Kind = kind; } + + internal void ScrollTo(Handle handle) + { + this.scrollTarget = MetadataTokens.GetRowNumber((EntityHandle)handle); + } } } \ No newline at end of file diff --git a/ILSpy/Metadata/MetadataTableViews.xaml b/ILSpy/Metadata/MetadataTableViews.xaml index 9bd6430b1..a0b52ecf0 100644 --- a/ILSpy/Metadata/MetadataTableViews.xaml +++ b/ILSpy/Metadata/MetadataTableViews.xaml @@ -48,6 +48,10 @@ + + + + @@ -56,6 +60,10 @@ + + + + diff --git a/ILSpy/Metadata/MetadataTreeNode.cs b/ILSpy/Metadata/MetadataTreeNode.cs index 1ddda5dfb..572d74c49 100644 --- a/ILSpy/Metadata/MetadataTreeNode.cs +++ b/ILSpy/Metadata/MetadataTreeNode.cs @@ -104,7 +104,7 @@ namespace ICSharpCode.ILSpy.Metadata this.Children.Add(new GenericParamConstraintTableTreeNode(module)); } - public ILSpyTreeNode FindNodeByHandleKind(HandleKind kind) + public MetadataTableTreeNode FindNodeByHandleKind(HandleKind kind) { return this.Children.OfType().SingleOrDefault(x => x.Kind == kind); } @@ -112,11 +112,11 @@ namespace ICSharpCode.ILSpy.Metadata class Entry { - public string Member { get; set; } - public int Offset { get; set; } - public int Size { get; set; } - public object Value { get; set; } - public string Meaning { get; set; } + public string Member { get; } + public int Offset { get; } + public int Size { get; } + public object Value { get; } + public string Meaning { get; } public Entry(int offset, object value, int size, string member, string meaning) { diff --git a/ILSpy/Metadata/OptionalHeaderTreeNode.cs b/ILSpy/Metadata/OptionalHeaderTreeNode.cs index 8f8e6768c..3411507ec 100644 --- a/ILSpy/Metadata/OptionalHeaderTreeNode.cs +++ b/ILSpy/Metadata/OptionalHeaderTreeNode.cs @@ -43,20 +43,19 @@ namespace ICSharpCode.ILSpy.Metadata public override bool View(ViewModels.TabPageModel tabPage) { - var dataGrid = new DataGrid { - Columns = { - new DataGridTextColumn { IsReadOnly = true, Header = "Member", Binding = new Binding("Member") }, - new DataGridTextColumn { IsReadOnly = true, Header = "Offset", Binding = new Binding("Offset") { StringFormat = "X8" } }, - new DataGridTextColumn { IsReadOnly = true, Header = "Size", Binding = new Binding("Size") }, - new DataGridTextColumn { IsReadOnly = true, Header = "Value", Binding = new Binding(".") { Converter = ByteWidthConverter.Instance } }, - new DataGridTextColumn { IsReadOnly = true, Header = "Meaning", Binding = new Binding("Meaning") }, - }, - AutoGenerateColumns = false, - CanUserAddRows = false, - CanUserDeleteRows = false, - RowDetailsTemplateSelector = new DllCharacteristicsDataTemplateSelector(), - RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.Visible - }; + tabPage.Title = Text.ToString(); + tabPage.SupportsLanguageSwitching = false; + + var dataGrid = Helpers.PrepareDataGrid(tabPage); + dataGrid.RowDetailsTemplateSelector = new DllCharacteristicsDataTemplateSelector(); + dataGrid.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.Visible; + dataGrid.AutoGenerateColumns = false; + dataGrid.Columns.Add(new DataGridCustomTextColumn { Header = "Member", Binding = new Binding("Member") { Mode = BindingMode.OneWay } }); + dataGrid.Columns.Add(new DataGridCustomTextColumn { Header = "Offset", Binding = new Binding("Offset") { StringFormat = "X8", Mode = BindingMode.OneWay } }); + dataGrid.Columns.Add(new DataGridCustomTextColumn { Header = "Size", Binding = new Binding("Size") { Mode = BindingMode.OneWay } }); + dataGrid.Columns.Add(new DataGridCustomTextColumn { Header = "Value", Binding = new Binding(".") { Converter = ByteWidthConverter.Instance, Mode = BindingMode.OneWay } }); + dataGrid.Columns.Add(new DataGridCustomTextColumn { Header = "Meaning", Binding = new Binding("Meaning") { Mode = BindingMode.OneWay } }); + var headers = module.Reader.PEHeaders; var reader = module.Reader.GetEntireImage().GetReader(headers.PEHeaderStartOffset, 128); var header = headers.PEHeader; @@ -129,6 +128,7 @@ namespace ICSharpCode.ILSpy.Metadata new { Value = (flags & 0x4000) != 0, Meaning = "Image supports Control Flow Guard" }, new { Value = (flags & 0x8000) != 0, Meaning = "Image is Terminal Server aware" }, }); + dataGridFactory.SetValue(DataGrid.GridLinesVisibilityProperty, DataGridGridLinesVisibility.None); DataTemplate template = new DataTemplate(); template.VisualTree = dataGridFactory; return template;