Browse Source

Hoist metadata row-offset and heap-view boilerplate to shared bases

The 23 typed table-row viewmodels each inlined the same
MetadataOffset + GetTableMetadataOffset + GetTableRowSize * (rid - 1)
arithmetic, and the four heap tree nodes each repeated an identical
lazy-cache / "{name} Heap (count)" header / CreateTab shell that differed
only in the heap name and how rows are walked. A single mistyped
TableIndex or a divergent cache shell would have been invisible across
that many copies.

Add MetadataTableTreeNode.GetRowOffset (with a MetadataReader+offset
overload for the one entry that lacks a MetadataFile) and a generic
MetadataHeapTreeNode<TEntry> base carrying the cache, header, and tab,
leaving subclasses to supply only the name and row materialiser.

Assisted-by: Claude:claude-opus-4-8:Claude Code
pull/3755/head
Siegfried Pammer 4 weeks ago
parent
commit
464551ea5d
  1. 26
      ILSpy/Metadata/BlobHeapTreeNode.cs
  2. 4
      ILSpy/Metadata/CorTables/AssemblyRefTableTreeNode.cs
  3. 4
      ILSpy/Metadata/CorTables/AssemblyTableTreeNode.cs
  4. 4
      ILSpy/Metadata/CorTables/ConstantTableTreeNode.cs
  5. 4
      ILSpy/Metadata/CorTables/CustomAttributeTableTreeNode.cs
  6. 4
      ILSpy/Metadata/CorTables/DeclSecurityTableTreeNode.cs
  7. 4
      ILSpy/Metadata/CorTables/EventTableTreeNode.cs
  8. 4
      ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs
  9. 4
      ILSpy/Metadata/CorTables/FieldTableTreeNode.cs
  10. 4
      ILSpy/Metadata/CorTables/FileTableTreeNode.cs
  11. 4
      ILSpy/Metadata/CorTables/GenericParamConstraintTableTreeNode.cs
  12. 4
      ILSpy/Metadata/CorTables/GenericParamTableTreeNode.cs
  13. 4
      ILSpy/Metadata/CorTables/ManifestResourceTableTreeNode.cs
  14. 4
      ILSpy/Metadata/CorTables/MemberRefTableTreeNode.cs
  15. 4
      ILSpy/Metadata/CorTables/MethodSpecTableTreeNode.cs
  16. 4
      ILSpy/Metadata/CorTables/MethodTableTreeNode.cs
  17. 4
      ILSpy/Metadata/CorTables/ModuleRefTableTreeNode.cs
  18. 4
      ILSpy/Metadata/CorTables/ModuleTableTreeNode.cs
  19. 4
      ILSpy/Metadata/CorTables/ParamTableTreeNode.cs
  20. 4
      ILSpy/Metadata/CorTables/PropertyTableTreeNode.cs
  21. 4
      ILSpy/Metadata/CorTables/StandAloneSigTableTreeNode.cs
  22. 4
      ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs
  23. 4
      ILSpy/Metadata/CorTables/TypeRefTableTreeNode.cs
  24. 4
      ILSpy/Metadata/CorTables/TypeSpecTableTreeNode.cs
  25. 28
      ILSpy/Metadata/GuidHeapTreeNode.cs
  26. 47
      ILSpy/Metadata/MetadataHeapTreeNode.cs
  27. 15
      ILSpy/Metadata/MetadataTableTreeNode.cs
  28. 26
      ILSpy/Metadata/StringHeapTreeNode.cs
  29. 26
      ILSpy/Metadata/UserStringHeapTreeNode.cs

26
ILSpy/Metadata/BlobHeapTreeNode.cs

@ -17,7 +17,6 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata; using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335; using System.Reflection.Metadata.Ecma335;
@ -33,42 +32,23 @@ namespace ILSpy.Metadata
/// dump; full structural decoding (e.g. parsed type signatures) is out of scope for /// dump; full structural decoding (e.g. parsed type signatures) is out of scope for
/// the metadata viewer. /// the metadata viewer.
/// </summary> /// </summary>
public sealed class BlobHeapTreeNode : MetadataHeapTreeNode public sealed class BlobHeapTreeNode : MetadataHeapTreeNode<BlobHeapTreeNode.BlobHeapEntry>
{ {
List<BlobHeapEntry>? entries;
public BlobHeapTreeNode(MetadataFile metadataFile) public BlobHeapTreeNode(MetadataFile metadataFile)
: base(HandleKind.Blob, metadataFile) : base(HandleKind.Blob, metadataFile)
{ {
} }
public override object Text => $"Blob Heap ({EnsureEntries().Count})"; protected override string HeapName => "Blob Heap";
public override string ToString() => "Blob Heap";
public override ContentPageModel CreateTab()
{
var page = new MetadataTablePageModel {
Title = "Blob Heap",
Items = EnsureEntries(),
};
MetadataColumnBuilder.Populate<BlobHeapEntry>(page);
return page;
}
List<BlobHeapEntry> EnsureEntries() protected override void LoadEntries(MetadataReader metadata, List<BlobHeapEntry> list)
{ {
if (entries is { } cached)
return cached;
var list = new List<BlobHeapEntry>();
var metadata = metadataFile.Metadata;
var handle = MetadataTokens.BlobHandle(0); var handle = MetadataTokens.BlobHandle(0);
do do
{ {
list.Add(new BlobHeapEntry(metadata, handle)); list.Add(new BlobHeapEntry(metadata, handle));
handle = metadata.GetNextHandle(handle); handle = metadata.GetNextHandle(handle);
} while (!handle.IsNil); } while (!handle.IsNil);
entries = list;
return list;
} }
public sealed class BlobHeapEntry public sealed class BlobHeapEntry

4
ILSpy/Metadata/CorTables/AssemblyRefTableTreeNode.cs

@ -58,9 +58,7 @@ namespace ILSpy.Metadata.CorTables
public int Token => MetadataTokens.GetToken(handle); public int Token => MetadataTokens.GetToken(handle);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public int Offset => metadataFile.MetadataOffset public int Offset => GetRowOffset(metadataFile, TableIndex.AssemblyRef, RID);
+ metadataFile.Metadata.GetTableMetadataOffset(TableIndex.AssemblyRef)
+ metadataFile.Metadata.GetTableRowSize(TableIndex.AssemblyRef) * (RID - 1);
public Version Version => assemblyRef.Version; public Version Version => assemblyRef.Version;

4
ILSpy/Metadata/CorTables/AssemblyTableTreeNode.cs

@ -55,9 +55,7 @@ namespace ILSpy.Metadata.CorTables
public int Token => MetadataTokens.GetToken(EntityHandle.AssemblyDefinition); public int Token => MetadataTokens.GetToken(EntityHandle.AssemblyDefinition);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public int Offset => metadataOffset public int Offset => GetRowOffset(metadata, metadataOffset, TableIndex.Assembly, RID);
+ metadata.GetTableMetadataOffset(TableIndex.Assembly)
+ metadata.GetTableRowSize(TableIndex.Assembly) * (RID - 1);
[ColumnInfo("X4")] [ColumnInfo("X4")]
public AssemblyHashAlgorithm HashAlgorithm => assembly.HashAlgorithm; public AssemblyHashAlgorithm HashAlgorithm => assembly.HashAlgorithm;

4
ILSpy/Metadata/CorTables/ConstantTableTreeNode.cs

@ -56,9 +56,7 @@ namespace ILSpy.Metadata.CorTables
public int Token => MetadataTokens.GetToken(handle); public int Token => MetadataTokens.GetToken(handle);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public int Offset => metadataFile.MetadataOffset public int Offset => GetRowOffset(metadataFile, TableIndex.Constant, RID);
+ metadataFile.Metadata.GetTableMetadataOffset(TableIndex.Constant)
+ metadataFile.Metadata.GetTableRowSize(TableIndex.Constant) * (RID - 1);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public ConstantTypeCode Type => constant.TypeCode; public ConstantTypeCode Type => constant.TypeCode;

4
ILSpy/Metadata/CorTables/CustomAttributeTableTreeNode.cs

@ -57,9 +57,7 @@ namespace ILSpy.Metadata.CorTables
public int Token => MetadataTokens.GetToken(handle); public int Token => MetadataTokens.GetToken(handle);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public int Offset => metadataFile.MetadataOffset public int Offset => GetRowOffset(metadataFile, TableIndex.CustomAttribute, RID);
+ metadataFile.Metadata.GetTableMetadataOffset(TableIndex.CustomAttribute)
+ metadataFile.Metadata.GetTableRowSize(TableIndex.CustomAttribute) * (RID - 1);
[ColumnInfo("X8", Kind = ColumnKind.Token)] [ColumnInfo("X8", Kind = ColumnKind.Token)]
public int Parent => MetadataTokens.GetToken(customAttr.Parent); public int Parent => MetadataTokens.GetToken(customAttr.Parent);

4
ILSpy/Metadata/CorTables/DeclSecurityTableTreeNode.cs

@ -57,9 +57,7 @@ namespace ILSpy.Metadata.CorTables
public int Token => MetadataTokens.GetToken(handle); public int Token => MetadataTokens.GetToken(handle);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public int Offset => metadataFile.MetadataOffset public int Offset => GetRowOffset(metadataFile, TableIndex.DeclSecurity, RID);
+ metadataFile.Metadata.GetTableMetadataOffset(TableIndex.DeclSecurity)
+ metadataFile.Metadata.GetTableRowSize(TableIndex.DeclSecurity) * (RID - 1);
[ColumnInfo("X8", Kind = ColumnKind.Token)] [ColumnInfo("X8", Kind = ColumnKind.Token)]
public int Parent => MetadataTokens.GetToken(declSecAttr.Parent); public int Parent => MetadataTokens.GetToken(declSecAttr.Parent);

4
ILSpy/Metadata/CorTables/EventTableTreeNode.cs

@ -57,9 +57,7 @@ namespace ILSpy.Metadata.CorTables
public int Token => MetadataTokens.GetToken(handle); public int Token => MetadataTokens.GetToken(handle);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public int Offset => metadataFile.MetadataOffset public int Offset => GetRowOffset(metadataFile, TableIndex.Event, RID);
+ metadataFile.Metadata.GetTableMetadataOffset(TableIndex.Event)
+ metadataFile.Metadata.GetTableRowSize(TableIndex.Event) * (RID - 1);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public EventAttributes Attributes => eventDef.Attributes; public EventAttributes Attributes => eventDef.Attributes;

4
ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs

@ -58,9 +58,7 @@ namespace ILSpy.Metadata.CorTables
public int Token => MetadataTokens.GetToken(handle); public int Token => MetadataTokens.GetToken(handle);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public int Offset => metadataFile.MetadataOffset public int Offset => GetRowOffset(metadataFile, TableIndex.ExportedType, RID);
+ metadataFile.Metadata.GetTableMetadataOffset(TableIndex.ExportedType)
+ metadataFile.Metadata.GetTableRowSize(TableIndex.ExportedType) * (RID - 1);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public TypeAttributes Attributes => type.Attributes; public TypeAttributes Attributes => type.Attributes;

4
ILSpy/Metadata/CorTables/FieldTableTreeNode.cs

@ -57,9 +57,7 @@ namespace ILSpy.Metadata.CorTables
public int Token => MetadataTokens.GetToken(handle); public int Token => MetadataTokens.GetToken(handle);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public int Offset => metadataFile.MetadataOffset public int Offset => GetRowOffset(metadataFile, TableIndex.Field, RID);
+ metadataFile.Metadata.GetTableMetadataOffset(TableIndex.Field)
+ metadataFile.Metadata.GetTableRowSize(TableIndex.Field) * (RID - 1);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public FieldAttributes Attributes => fieldDef.Attributes; public FieldAttributes Attributes => fieldDef.Attributes;

4
ILSpy/Metadata/CorTables/FileTableTreeNode.cs

@ -55,9 +55,7 @@ namespace ILSpy.Metadata.CorTables
public int Token => MetadataTokens.GetToken(handle); public int Token => MetadataTokens.GetToken(handle);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public int Offset => metadataFile.MetadataOffset public int Offset => GetRowOffset(metadataFile, TableIndex.File, RID);
+ metadataFile.Metadata.GetTableMetadataOffset(TableIndex.File)
+ metadataFile.Metadata.GetTableRowSize(TableIndex.File) * (RID - 1);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public int Attributes => assemblyFile.ContainsMetadata ? 1 : 0; public int Attributes => assemblyFile.ContainsMetadata ? 1 : 0;

4
ILSpy/Metadata/CorTables/GenericParamConstraintTableTreeNode.cs

@ -59,9 +59,7 @@ namespace ILSpy.Metadata.CorTables
public int Token => MetadataTokens.GetToken(handle); public int Token => MetadataTokens.GetToken(handle);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public int Offset => metadataFile.MetadataOffset public int Offset => GetRowOffset(metadataFile, TableIndex.GenericParamConstraint, RID);
+ metadataFile.Metadata.GetTableMetadataOffset(TableIndex.GenericParamConstraint)
+ metadataFile.Metadata.GetTableRowSize(TableIndex.GenericParamConstraint) * (RID - 1);
[ColumnInfo("X8", Kind = ColumnKind.Token)] [ColumnInfo("X8", Kind = ColumnKind.Token)]
public int Owner => MetadataTokens.GetToken(genericParamConstraint.Parameter); public int Owner => MetadataTokens.GetToken(genericParamConstraint.Parameter);

4
ILSpy/Metadata/CorTables/GenericParamTableTreeNode.cs

@ -57,9 +57,7 @@ namespace ILSpy.Metadata.CorTables
public int Token => MetadataTokens.GetToken(handle); public int Token => MetadataTokens.GetToken(handle);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public int Offset => metadataFile.MetadataOffset public int Offset => GetRowOffset(metadataFile, TableIndex.GenericParam, RID);
+ metadataFile.Metadata.GetTableMetadataOffset(TableIndex.GenericParam)
+ metadataFile.Metadata.GetTableRowSize(TableIndex.GenericParam) * (RID - 1);
public int Number => genericParam.Index; public int Number => genericParam.Index;

4
ILSpy/Metadata/CorTables/ManifestResourceTableTreeNode.cs

@ -58,9 +58,7 @@ namespace ILSpy.Metadata.CorTables
public int Token => MetadataTokens.GetToken(handle); public int Token => MetadataTokens.GetToken(handle);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public int Offset => metadataFile.MetadataOffset public int Offset => GetRowOffset(metadataFile, TableIndex.ManifestResource, RID);
+ metadataFile.Metadata.GetTableMetadataOffset(TableIndex.ManifestResource)
+ metadataFile.Metadata.GetTableRowSize(TableIndex.ManifestResource) * (RID - 1);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public ManifestResourceAttributes Attributes => manifestResource.Attributes; public ManifestResourceAttributes Attributes => manifestResource.Attributes;

4
ILSpy/Metadata/CorTables/MemberRefTableTreeNode.cs

@ -56,9 +56,7 @@ namespace ILSpy.Metadata.CorTables
public int Token => MetadataTokens.GetToken(handle); public int Token => MetadataTokens.GetToken(handle);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public int Offset => metadataFile.MetadataOffset public int Offset => GetRowOffset(metadataFile, TableIndex.MemberRef, RID);
+ metadataFile.Metadata.GetTableMetadataOffset(TableIndex.MemberRef)
+ metadataFile.Metadata.GetTableRowSize(TableIndex.MemberRef) * (RID - 1);
[ColumnInfo("X8", Kind = ColumnKind.Token)] [ColumnInfo("X8", Kind = ColumnKind.Token)]
public int Parent => MetadataTokens.GetToken(memberRef.Parent); public int Parent => MetadataTokens.GetToken(memberRef.Parent);

4
ILSpy/Metadata/CorTables/MethodSpecTableTreeNode.cs

@ -58,9 +58,7 @@ namespace ILSpy.Metadata.CorTables
public int Token => MetadataTokens.GetToken(handle); public int Token => MetadataTokens.GetToken(handle);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public int Offset => metadataFile.MetadataOffset public int Offset => GetRowOffset(metadataFile, TableIndex.MethodSpec, RID);
+ metadataFile.Metadata.GetTableMetadataOffset(TableIndex.MethodSpec)
+ metadataFile.Metadata.GetTableRowSize(TableIndex.MethodSpec) * (RID - 1);
[ColumnInfo("X8", Kind = ColumnKind.Token)] [ColumnInfo("X8", Kind = ColumnKind.Token)]
public int Method => MetadataTokens.GetToken(methodSpec.Method); public int Method => MetadataTokens.GetToken(methodSpec.Method);

4
ILSpy/Metadata/CorTables/MethodTableTreeNode.cs

@ -58,9 +58,7 @@ namespace ILSpy.Metadata.CorTables
public int Token => MetadataTokens.GetToken(handle); public int Token => MetadataTokens.GetToken(handle);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public int Offset => metadataFile.MetadataOffset public int Offset => GetRowOffset(metadataFile, TableIndex.MethodDef, RID);
+ metadataFile.Metadata.GetTableMetadataOffset(TableIndex.MethodDef)
+ metadataFile.Metadata.GetTableRowSize(TableIndex.MethodDef) * (RID - 1);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public MethodAttributes Attributes => methodDef.Attributes; public MethodAttributes Attributes => methodDef.Attributes;

4
ILSpy/Metadata/CorTables/ModuleRefTableTreeNode.cs

@ -55,9 +55,7 @@ namespace ILSpy.Metadata.CorTables
public int Token => MetadataTokens.GetToken(handle); public int Token => MetadataTokens.GetToken(handle);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public int Offset => metadataFile.MetadataOffset public int Offset => GetRowOffset(metadataFile, TableIndex.ModuleRef, RID);
+ metadataFile.Metadata.GetTableMetadataOffset(TableIndex.ModuleRef)
+ metadataFile.Metadata.GetTableRowSize(TableIndex.ModuleRef) * (RID - 1);
public string Name => metadataFile.Metadata.GetString(moduleRef.Name); public string Name => metadataFile.Metadata.GetString(moduleRef.Name);

4
ILSpy/Metadata/CorTables/ModuleTableTreeNode.cs

@ -51,9 +51,7 @@ namespace ILSpy.Metadata.CorTables
public int Token => MetadataTokens.GetToken(handle); public int Token => MetadataTokens.GetToken(handle);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public int Offset => metadataFile.MetadataOffset public int Offset => GetRowOffset(metadataFile, TableIndex.Module, RID);
+ metadataFile.Metadata.GetTableMetadataOffset(TableIndex.Module)
+ metadataFile.Metadata.GetTableRowSize(TableIndex.Module) * (RID - 1);
public int Generation => moduleDef.Generation; public int Generation => moduleDef.Generation;

4
ILSpy/Metadata/CorTables/ParamTableTreeNode.cs

@ -56,9 +56,7 @@ namespace ILSpy.Metadata.CorTables
public int Token => MetadataTokens.GetToken(handle); public int Token => MetadataTokens.GetToken(handle);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public int Offset => metadataFile.MetadataOffset public int Offset => GetRowOffset(metadataFile, TableIndex.Param, RID);
+ metadataFile.Metadata.GetTableMetadataOffset(TableIndex.Param)
+ metadataFile.Metadata.GetTableRowSize(TableIndex.Param) * (RID - 1);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public ParameterAttributes Attributes => param.Attributes; public ParameterAttributes Attributes => param.Attributes;

4
ILSpy/Metadata/CorTables/PropertyTableTreeNode.cs

@ -57,9 +57,7 @@ namespace ILSpy.Metadata.CorTables
public int Token => MetadataTokens.GetToken(handle); public int Token => MetadataTokens.GetToken(handle);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public int Offset => metadataFile.MetadataOffset public int Offset => GetRowOffset(metadataFile, TableIndex.Property, RID);
+ metadataFile.Metadata.GetTableMetadataOffset(TableIndex.Property)
+ metadataFile.Metadata.GetTableRowSize(TableIndex.Property) * (RID - 1);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public PropertyAttributes Attributes => propertyDef.Attributes; public PropertyAttributes Attributes => propertyDef.Attributes;

4
ILSpy/Metadata/CorTables/StandAloneSigTableTreeNode.cs

@ -56,9 +56,7 @@ namespace ILSpy.Metadata.CorTables
public int Token => MetadataTokens.GetToken(handle); public int Token => MetadataTokens.GetToken(handle);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public int Offset => metadataFile.MetadataOffset public int Offset => GetRowOffset(metadataFile, TableIndex.StandAloneSig, RID);
+ metadataFile.Metadata.GetTableMetadataOffset(TableIndex.StandAloneSig)
+ metadataFile.Metadata.GetTableRowSize(TableIndex.StandAloneSig) * (RID - 1);
[ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)]
public int Signature => MetadataTokens.GetHeapOffset(standaloneSig.Signature); public int Signature => MetadataTokens.GetHeapOffset(standaloneSig.Signature);

4
ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs

@ -61,9 +61,7 @@ namespace ILSpy.Metadata.CorTables
public int Token => MetadataTokens.GetToken(handle); public int Token => MetadataTokens.GetToken(handle);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public int Offset => metadataFile.MetadataOffset public int Offset => GetRowOffset(metadataFile, TableIndex.TypeDef, RID);
+ metadataFile.Metadata.GetTableMetadataOffset(TableIndex.TypeDef)
+ metadataFile.Metadata.GetTableRowSize(TableIndex.TypeDef) * (RID - 1);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public TypeAttributes Attributes => typeDef.Attributes; public TypeAttributes Attributes => typeDef.Attributes;

4
ILSpy/Metadata/CorTables/TypeRefTableTreeNode.cs

@ -56,9 +56,7 @@ namespace ILSpy.Metadata.CorTables
public int Token => MetadataTokens.GetToken(handle); public int Token => MetadataTokens.GetToken(handle);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public int Offset => metadataFile.MetadataOffset public int Offset => GetRowOffset(metadataFile, TableIndex.TypeRef, RID);
+ metadataFile.Metadata.GetTableMetadataOffset(TableIndex.TypeRef)
+ metadataFile.Metadata.GetTableRowSize(TableIndex.TypeRef) * (RID - 1);
[ColumnInfo("X8", Kind = ColumnKind.Token)] [ColumnInfo("X8", Kind = ColumnKind.Token)]
public int ResolutionScope => MetadataTokens.GetToken(typeRef.ResolutionScope); public int ResolutionScope => MetadataTokens.GetToken(typeRef.ResolutionScope);

4
ILSpy/Metadata/CorTables/TypeSpecTableTreeNode.cs

@ -58,9 +58,7 @@ namespace ILSpy.Metadata.CorTables
public int Token => MetadataTokens.GetToken(handle); public int Token => MetadataTokens.GetToken(handle);
[ColumnInfo("X8")] [ColumnInfo("X8")]
public int Offset => metadataFile.MetadataOffset public int Offset => GetRowOffset(metadataFile, TableIndex.TypeSpec, RID);
+ metadataFile.Metadata.GetTableMetadataOffset(TableIndex.TypeSpec)
+ metadataFile.Metadata.GetTableRowSize(TableIndex.TypeSpec) * (RID - 1);
[ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)]
public int Signature => MetadataTokens.GetHeapOffset(typeSpec.Signature); public int Signature => MetadataTokens.GetHeapOffset(typeSpec.Signature);

28
ILSpy/Metadata/GuidHeapTreeNode.cs

@ -17,14 +17,11 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata; using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335; using System.Reflection.Metadata.Ecma335;
using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Metadata;
using ILSpy.ViewModels;
namespace ILSpy.Metadata namespace ILSpy.Metadata
{ {
/// <summary> /// <summary>
@ -32,39 +29,20 @@ namespace ILSpy.Metadata
/// rows and a handful of other tables. Indexing starts at 1 because index 0 is reserved /// rows and a handful of other tables. Indexing starts at 1 because index 0 is reserved
/// for "no GUID". /// for "no GUID".
/// </summary> /// </summary>
public sealed class GuidHeapTreeNode : MetadataHeapTreeNode public sealed class GuidHeapTreeNode : MetadataHeapTreeNode<GuidHeapTreeNode.GuidHeapEntry>
{ {
List<GuidHeapEntry>? entries;
public GuidHeapTreeNode(MetadataFile metadataFile) public GuidHeapTreeNode(MetadataFile metadataFile)
: base(HandleKind.Guid, metadataFile) : base(HandleKind.Guid, metadataFile)
{ {
} }
public override object Text => $"Guid Heap ({EnsureEntries().Count})"; protected override string HeapName => "Guid Heap";
public override string ToString() => "Guid Heap";
public override ContentPageModel CreateTab()
{
var page = new MetadataTablePageModel {
Title = "Guid Heap",
Items = EnsureEntries(),
};
MetadataColumnBuilder.Populate<GuidHeapEntry>(page);
return page;
}
List<GuidHeapEntry> EnsureEntries() protected override void LoadEntries(MetadataReader metadata, List<GuidHeapEntry> list)
{ {
if (entries is { } cached)
return cached;
var list = new List<GuidHeapEntry>();
var metadata = metadataFile.Metadata;
int count = metadata.GetHeapSize(HeapIndex.Guid) >> 4; int count = metadata.GetHeapSize(HeapIndex.Guid) >> 4;
for (int i = 1; i <= count; i++) for (int i = 1; i <= count; i++)
list.Add(new GuidHeapEntry(metadata, MetadataTokens.GuidHandle(i))); list.Add(new GuidHeapEntry(metadata, MetadataTokens.GuidHandle(i)));
entries = list;
return list;
} }
public sealed class GuidHeapEntry public sealed class GuidHeapEntry

47
ILSpy/Metadata/MetadataHeapTreeNode.cs

@ -17,11 +17,13 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.Generic;
using System.Reflection.Metadata; using System.Reflection.Metadata;
using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Metadata;
using ILSpy.TreeNodes; using ILSpy.TreeNodes;
using ILSpy.ViewModels;
namespace ILSpy.Metadata namespace ILSpy.Metadata
{ {
@ -46,4 +48,49 @@ namespace ILSpy.Metadata
public override object Icon => Images.Images.Heap; public override object Icon => Images.Images.Heap;
} }
/// <summary>
/// Typed companion that owns the lazy row cache, the "{name} Heap (count)" header, and the
/// DataGrid tab. Subclasses supply the heap's display name and materialise its rows, which is
/// all that differs between the four heaps.
/// </summary>
public abstract class MetadataHeapTreeNode<TEntry> : MetadataHeapTreeNode
where TEntry : class
{
List<TEntry>? entries;
protected MetadataHeapTreeNode(HandleKind kind, MetadataFile metadataFile)
: base(kind, metadataFile)
{
}
/// <summary>The heap's display name, e.g. "Blob Heap".</summary>
protected abstract string HeapName { get; }
public override object Text => $"{HeapName} ({EnsureEntries().Count})";
public override string ToString() => HeapName;
public override ContentPageModel CreateTab()
{
var page = new MetadataTablePageModel {
Title = HeapName,
Items = EnsureEntries(),
};
MetadataColumnBuilder.Populate<TEntry>(page);
return page;
}
protected List<TEntry> EnsureEntries()
{
if (entries is { } cached)
return cached;
var list = new List<TEntry>();
LoadEntries(metadataFile.Metadata, list);
entries = list;
return list;
}
/// <summary>Materialises every heap row into <paramref name="list"/>.</summary>
protected abstract void LoadEntries(MetadataReader metadata, List<TEntry> list);
}
} }

15
ILSpy/Metadata/MetadataTableTreeNode.cs

@ -53,6 +53,21 @@ namespace ILSpy.Metadata
public override object Icon => Images.Images.MetadataTable; public override object Icon => Images.Images.MetadataTable;
/// <summary>
/// Byte offset of row <paramref name="rid"/> (1-based) in <paramref name="table"/>, relative to
/// the start of the PE file. Every typed row viewmodel exposes this from its <c>Offset</c>
/// property so the hex view can jump to the raw bytes.
/// </summary>
protected static int GetRowOffset(MetadataFile file, TableIndex table, int rid)
=> GetRowOffset(file.Metadata, file.MetadataOffset, table, rid);
/// <summary>
/// Overload for the few entries that hold a raw <see cref="MetadataReader"/> + offset rather
/// than a <see cref="MetadataFile"/>.
/// </summary>
protected static int GetRowOffset(MetadataReader metadata, int metadataOffset, TableIndex table, int rid)
=> metadataOffset + metadata.GetTableMetadataOffset(table) + metadata.GetTableRowSize(table) * (rid - 1);
/// <summary> /// <summary>
/// Builds (and caches into <paramref name="tooltip"/>) a human-readable description of the /// Builds (and caches into <paramref name="tooltip"/>) a human-readable description of the
/// entity a token column points at, e.g. <c>(AssemblyReference) System.Runtime, ...</c>. /// entity a token column points at, e.g. <c>(AssemblyReference) System.Runtime, ...</c>.

26
ILSpy/Metadata/StringHeapTreeNode.cs

@ -17,7 +17,6 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata; using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335; using System.Reflection.Metadata.Ecma335;
@ -33,42 +32,23 @@ namespace ILSpy.Metadata
/// demand via <see cref="MetadataReader.GetNextHandle(StringHandle)"/> the first time a /// demand via <see cref="MetadataReader.GetNextHandle(StringHandle)"/> the first time a
/// caller asks for the count or a preview row. /// caller asks for the count or a preview row.
/// </summary> /// </summary>
public sealed class StringHeapTreeNode : MetadataHeapTreeNode public sealed class StringHeapTreeNode : MetadataHeapTreeNode<StringHeapTreeNode.StringHeapEntry>
{ {
List<StringHeapEntry>? entries;
public StringHeapTreeNode(MetadataFile metadataFile) public StringHeapTreeNode(MetadataFile metadataFile)
: base(HandleKind.String, metadataFile) : base(HandleKind.String, metadataFile)
{ {
} }
public override object Text => $"String Heap ({EnsureEntries().Count})"; protected override string HeapName => "String Heap";
public override string ToString() => "String Heap";
public override ContentPageModel CreateTab()
{
var page = new MetadataTablePageModel {
Title = "String Heap",
Items = EnsureEntries(),
};
MetadataColumnBuilder.Populate<StringHeapEntry>(page);
return page;
}
List<StringHeapEntry> EnsureEntries() protected override void LoadEntries(MetadataReader metadata, List<StringHeapEntry> list)
{ {
if (entries is { } cached)
return cached;
var list = new List<StringHeapEntry>();
var metadata = metadataFile.Metadata;
var handle = MetadataTokens.StringHandle(0); var handle = MetadataTokens.StringHandle(0);
do do
{ {
list.Add(new StringHeapEntry(metadata, handle)); list.Add(new StringHeapEntry(metadata, handle));
handle = metadata.GetNextHandle(handle); handle = metadata.GetNextHandle(handle);
} while (!handle.IsNil); } while (!handle.IsNil);
entries = list;
return list;
} }
public sealed class StringHeapEntry public sealed class StringHeapEntry

26
ILSpy/Metadata/UserStringHeapTreeNode.cs

@ -17,7 +17,6 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata; using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335; using System.Reflection.Metadata.Ecma335;
@ -32,42 +31,23 @@ namespace ILSpy.Metadata
/// instruction. Distinct from #Strings (which carries identifier names); user strings /// instruction. Distinct from #Strings (which carries identifier names); user strings
/// are referenced by metadata tokens, not by member-name resolution. /// are referenced by metadata tokens, not by member-name resolution.
/// </summary> /// </summary>
public sealed class UserStringHeapTreeNode : MetadataHeapTreeNode public sealed class UserStringHeapTreeNode : MetadataHeapTreeNode<UserStringHeapTreeNode.UserStringHeapEntry>
{ {
List<UserStringHeapEntry>? entries;
public UserStringHeapTreeNode(MetadataFile metadataFile) public UserStringHeapTreeNode(MetadataFile metadataFile)
: base(HandleKind.UserString, metadataFile) : base(HandleKind.UserString, metadataFile)
{ {
} }
public override object Text => $"UserString Heap ({EnsureEntries().Count})"; protected override string HeapName => "UserString Heap";
public override string ToString() => "UserString Heap";
public override ContentPageModel CreateTab()
{
var page = new MetadataTablePageModel {
Title = "UserString Heap",
Items = EnsureEntries(),
};
MetadataColumnBuilder.Populate<UserStringHeapEntry>(page);
return page;
}
List<UserStringHeapEntry> EnsureEntries() protected override void LoadEntries(MetadataReader metadata, List<UserStringHeapEntry> list)
{ {
if (entries is { } cached)
return cached;
var list = new List<UserStringHeapEntry>();
var metadata = metadataFile.Metadata;
var handle = MetadataTokens.UserStringHandle(0); var handle = MetadataTokens.UserStringHandle(0);
do do
{ {
list.Add(new UserStringHeapEntry(metadata, handle)); list.Add(new UserStringHeapEntry(metadata, handle));
handle = metadata.GetNextHandle(handle); handle = metadata.GetNextHandle(handle);
} while (!handle.IsNil); } while (!handle.IsNil);
entries = list;
return list;
} }
public sealed class UserStringHeapEntry public sealed class UserStringHeapEntry

Loading…
Cancel
Save