diff --git a/ILSpy/Metadata/BlobHeapTreeNode.cs b/ILSpy/Metadata/BlobHeapTreeNode.cs index 239428398..f6aa0e7d3 100644 --- a/ILSpy/Metadata/BlobHeapTreeNode.cs +++ b/ILSpy/Metadata/BlobHeapTreeNode.cs @@ -43,12 +43,8 @@ namespace ILSpy.Metadata protected override void LoadEntries(MetadataReader metadata, List list) { - var handle = MetadataTokens.BlobHandle(0); - do - { - list.Add(new BlobHeapEntry(metadata, handle)); - handle = metadata.GetNextHandle(handle); - } while (!handle.IsNil); + WalkHeap(list, MetadataTokens.BlobHandle(0), + h => new BlobHeapEntry(metadata, h), metadata.GetNextHandle, h => h.IsNil); } public sealed class BlobHeapEntry diff --git a/ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs b/ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs index c6340ae18..f33cb6377 100644 --- a/ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs @@ -63,16 +63,7 @@ namespace ILSpy.Metadata.CorTables [ColumnInfo("X8")] public TypeAttributes Attributes => type.Attributes; - 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)(type.Attributes & TypeAttributes.VisibilityMask), new Flag("NotPublic (0000)", 0, false), includeAny: false), - FlagGroup.CreateSingleChoiceGroup(typeof(TypeAttributes), "Class layout: ", (int)TypeAttributes.LayoutMask, (int)(type.Attributes & TypeAttributes.LayoutMask), new Flag("AutoLayout (0000)", 0, false), includeAny: false), - FlagGroup.CreateSingleChoiceGroup(typeof(TypeAttributes), "Class semantics: ", (int)TypeAttributes.ClassSemanticsMask, (int)(type.Attributes & TypeAttributes.ClassSemanticsMask), new Flag("Class (0000)", 0, false), includeAny: false), - FlagGroup.CreateSingleChoiceGroup(typeof(TypeAttributes), "String format: ", (int)TypeAttributes.StringFormatMask, (int)(type.Attributes & TypeAttributes.StringFormatMask), new Flag("AnsiClass (0000)", 0, false), includeAny: false), - FlagGroup.CreateSingleChoiceGroup(typeof(TypeAttributes), "Custom format: ", (int)TypeAttributes.CustomFormatMask, (int)(type.Attributes & TypeAttributes.CustomFormatMask), new Flag("Value0 (0000)", 0, false), includeAny: false), - FlagGroup.CreateMultipleChoiceGroup(typeof(TypeAttributes), "Flags:", (int)otherFlagsMask, (int)(type.Attributes & otherFlagsMask), includeAll: false), - }; + public object AttributesTooltip => FlagsTooltip.ForTypeAttributes(type.Attributes); [ColumnInfo("X8")] public int TypeDefId => type.GetTypeDefinitionId(); diff --git a/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs b/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs index 1ebe044e8..ce02a13a7 100644 --- a/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs @@ -66,16 +66,7 @@ namespace ILSpy.Metadata.CorTables [ColumnInfo("X8")] public TypeAttributes Attributes => typeDef.Attributes; - 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 object AttributesTooltip => FlagsTooltip.ForTypeAttributes(typeDef.Attributes); public string Name => metadataFile.Metadata.GetString(typeDef.Name); diff --git a/ILSpy/Metadata/FlagsTooltip.cs b/ILSpy/Metadata/FlagsTooltip.cs index fe88c4af8..597c873d9 100644 --- a/ILSpy/Metadata/FlagsTooltip.cs +++ b/ILSpy/Metadata/FlagsTooltip.cs @@ -64,6 +64,25 @@ namespace ILSpy.Metadata panel.Children.Add(group.Build()); return panel; } + + /// + /// Tooltip for a column: one single-choice group per + /// mutually exclusive sub-range (visibility, layout, class semantics, string format, + /// custom format) plus a multiple-choice group for the remaining independent bits. + /// Shared by every table whose rows carry type attributes (TypeDef, ExportedType). + /// + public static FlagsTooltip ForTypeAttributes(TypeAttributes attributes) + { + const TypeAttributes otherFlagsMask = ~(TypeAttributes.VisibilityMask | TypeAttributes.LayoutMask | TypeAttributes.ClassSemanticsMask | TypeAttributes.StringFormatMask | TypeAttributes.CustomFormatMask); + return new FlagsTooltip { + FlagGroup.CreateSingleChoiceGroup(typeof(TypeAttributes), "Visibility: ", (int)TypeAttributes.VisibilityMask, (int)(attributes & TypeAttributes.VisibilityMask), new Flag("NotPublic (0000)", 0, false), includeAny: false), + FlagGroup.CreateSingleChoiceGroup(typeof(TypeAttributes), "Class layout: ", (int)TypeAttributes.LayoutMask, (int)(attributes & TypeAttributes.LayoutMask), new Flag("AutoLayout (0000)", 0, false), includeAny: false), + FlagGroup.CreateSingleChoiceGroup(typeof(TypeAttributes), "Class semantics: ", (int)TypeAttributes.ClassSemanticsMask, (int)(attributes & TypeAttributes.ClassSemanticsMask), new Flag("Class (0000)", 0, false), includeAny: false), + FlagGroup.CreateSingleChoiceGroup(typeof(TypeAttributes), "String format: ", (int)TypeAttributes.StringFormatMask, (int)(attributes & TypeAttributes.StringFormatMask), new Flag("AnsiClass (0000)", 0, false), includeAny: false), + FlagGroup.CreateSingleChoiceGroup(typeof(TypeAttributes), "Custom format: ", (int)TypeAttributes.CustomFormatMask, (int)(attributes & TypeAttributes.CustomFormatMask), new Flag("Value0 (0000)", 0, false), includeAny: false), + FlagGroup.CreateMultipleChoiceGroup(typeof(TypeAttributes), "Flags:", (int)otherFlagsMask, (int)(attributes & otherFlagsMask), includeAll: false), + }; + } } public readonly struct Flag diff --git a/ILSpy/Metadata/MetadataHeapTreeNode.cs b/ILSpy/Metadata/MetadataHeapTreeNode.cs index 8cfc4321d..d00ad85a8 100644 --- a/ILSpy/Metadata/MetadataHeapTreeNode.cs +++ b/ILSpy/Metadata/MetadataHeapTreeNode.cs @@ -92,5 +92,24 @@ namespace ILSpy.Metadata /// Materialises every heap row into . protected abstract void LoadEntries(MetadataReader metadata, List list); + + /// + /// Shared walk for the offset-chained heaps (#Strings, #US, #Blob). Handle 0 is a + /// real first entry (the heap's empty/zero slot) even though it is the nil handle, + /// so each entry is added before is consulted; the + /// walk ends when the returned handle is nil. + /// + protected static void WalkHeap(List list, THandle firstHandle, + Func createEntry, Func getNextHandle, + Predicate isNil) + where THandle : struct + { + var handle = firstHandle; + do + { + list.Add(createEntry(handle)); + handle = getNextHandle(handle); + } while (!isNil(handle)); + } } } diff --git a/ILSpy/Metadata/StringHeapTreeNode.cs b/ILSpy/Metadata/StringHeapTreeNode.cs index a2bdf6319..fd06c836d 100644 --- a/ILSpy/Metadata/StringHeapTreeNode.cs +++ b/ILSpy/Metadata/StringHeapTreeNode.cs @@ -43,12 +43,8 @@ namespace ILSpy.Metadata protected override void LoadEntries(MetadataReader metadata, List list) { - var handle = MetadataTokens.StringHandle(0); - do - { - list.Add(new StringHeapEntry(metadata, handle)); - handle = metadata.GetNextHandle(handle); - } while (!handle.IsNil); + WalkHeap(list, MetadataTokens.StringHandle(0), + h => new StringHeapEntry(metadata, h), metadata.GetNextHandle, h => h.IsNil); } public sealed class StringHeapEntry diff --git a/ILSpy/Metadata/UserStringHeapTreeNode.cs b/ILSpy/Metadata/UserStringHeapTreeNode.cs index 97ddb8b57..3c0e56091 100644 --- a/ILSpy/Metadata/UserStringHeapTreeNode.cs +++ b/ILSpy/Metadata/UserStringHeapTreeNode.cs @@ -42,12 +42,8 @@ namespace ILSpy.Metadata protected override void LoadEntries(MetadataReader metadata, List list) { - var handle = MetadataTokens.UserStringHandle(0); - do - { - list.Add(new UserStringHeapEntry(metadata, handle)); - handle = metadata.GetNextHandle(handle); - } while (!handle.IsNil); + WalkHeap(list, MetadataTokens.UserStringHandle(0), + h => new UserStringHeapEntry(metadata, h), metadata.GetNextHandle, h => h.IsNil); } public sealed class UserStringHeapEntry