Browse Source

Typed leaves for the last 12 cor tables

Closes out Phase 1e: every CLI metadata table now has its own typed
leaf. Bytes-level table walking goes through MetadataReader.AsBlobReader
(public API) instead of the WPF Helpers' AsReadOnlySpan path; coded-token
columns are decoded by the new MetadataReaderHelpers shim, which
reflects against the same private TypeDefOrRefTag / HasFieldMarshalTag /
MemberForwardedTag.ConvertToHandle methods + ComputeCodedTokenSize that
the WPF host uses (a known forward-compatibility risk per the plan's
risk register).

Assisted-by: Claude:claude-opus-4-7:Claude Code
pull/3755/head
Siegfried Pammer 2 months ago
parent
commit
ef06d5aaf4
  1. 83
      ILSpy/Metadata/CorTables/ClassLayoutTableTreeNode.cs
  2. 80
      ILSpy/Metadata/CorTables/EventMapTableTreeNode.cs
  3. 81
      ILSpy/Metadata/CorTables/FieldLayoutTableTreeNode.cs
  4. 83
      ILSpy/Metadata/CorTables/FieldMarshalTableTreeNode.cs
  5. 80
      ILSpy/Metadata/CorTables/FieldRVATableTreeNode.cs
  6. 95
      ILSpy/Metadata/CorTables/ImplMapTableTreeNode.cs
  7. 82
      ILSpy/Metadata/CorTables/InterfaceImplTableTreeNode.cs
  8. 75
      ILSpy/Metadata/CorTables/MethodImplTableTreeNode.cs
  9. 77
      ILSpy/Metadata/CorTables/MethodSemanticsTableTreeNode.cs
  10. 80
      ILSpy/Metadata/CorTables/NestedClassTableTreeNode.cs
  11. 80
      ILSpy/Metadata/CorTables/PropertyMapTableTreeNode.cs
  12. 88
      ILSpy/Metadata/CorTables/PtrTableTreeNode.cs
  13. 106
      ILSpy/Metadata/Helpers.cs
  14. 13
      ILSpy/Metadata/MetadataTablesTreeNode.cs

83
ILSpy/Metadata/CorTables/ClassLayoutTableTreeNode.cs

@ -0,0 +1,83 @@ @@ -0,0 +1,83 @@
// Copyright (c) 2026 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.Collections.Generic;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using ICSharpCode.Decompiler.Metadata;
namespace ILSpy.Metadata.CorTables
{
/// <summary>
/// View of the ClassLayout table — explicit field-layout overrides on TypeDefs marked
/// Sequential or Explicit (e.g. <c>[StructLayout(LayoutKind.Explicit)]</c>). System.Reflection.Metadata
/// doesn't surface these rows through a typed enumeration so we walk the table bytes
/// directly via a BlobReader.
/// </summary>
public sealed class ClassLayoutTableTreeNode : MetadataTableTreeNode<ClassLayoutTableTreeNode.ClassLayoutEntry>
{
public ClassLayoutTableTreeNode(MetadataFile metadataFile)
: base(TableIndex.ClassLayout, metadataFile)
{
}
protected override IReadOnlyList<ClassLayoutEntry> LoadTable()
{
var list = new List<ClassLayoutEntry>();
var metadata = metadataFile.Metadata;
var length = metadata.GetTableRowCount(TableIndex.ClassLayout);
var reader = metadata.AsBlobReader();
reader.Offset = metadata.GetTableMetadataOffset(TableIndex.ClassLayout);
int typeDefSize = metadata.GetTableRowCount(TableIndex.TypeDef) < ushort.MaxValue ? 2 : 4;
for (int rid = 1; rid <= length; rid++)
{
ushort packingSize = reader.ReadUInt16();
uint classSize = reader.ReadUInt32();
int parentRow = typeDefSize == 2 ? reader.ReadUInt16() : reader.ReadInt32();
list.Add(new ClassLayoutEntry(rid, packingSize, classSize, MetadataTokens.TypeDefinitionHandle(parentRow)));
}
return list;
}
public sealed class ClassLayoutEntry
{
public int RID { get; }
[ColumnInfo("X8")]
public int Token => 0x0F000000 | RID;
[ColumnInfo("X8", Kind = ColumnKind.Token)]
public int Parent => MetadataTokens.GetToken(parent);
public ushort PackingSize { get; }
public uint ClassSize { get; }
readonly TypeDefinitionHandle parent;
public ClassLayoutEntry(int rid, ushort packingSize, uint classSize, TypeDefinitionHandle parent)
{
RID = rid;
PackingSize = packingSize;
ClassSize = classSize;
this.parent = parent;
}
}
}
}

80
ILSpy/Metadata/CorTables/EventMapTableTreeNode.cs

@ -0,0 +1,80 @@ @@ -0,0 +1,80 @@
// Copyright (c) 2026 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.Collections.Generic;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using ICSharpCode.Decompiler.Metadata;
namespace ILSpy.Metadata.CorTables
{
/// <summary>
/// View of the EventMap table — pairs each TypeDef that declares events with the first
/// row of its event list (later rows belong to the same type until the next EventMap row).
/// </summary>
public sealed class EventMapTableTreeNode : MetadataTableTreeNode<EventMapTableTreeNode.EventMapEntry>
{
public EventMapTableTreeNode(MetadataFile metadataFile)
: base(TableIndex.EventMap, metadataFile)
{
}
protected override IReadOnlyList<EventMapEntry> LoadTable()
{
var list = new List<EventMapEntry>();
var metadata = metadataFile.Metadata;
var length = metadata.GetTableRowCount(TableIndex.EventMap);
var reader = metadata.AsBlobReader();
reader.Offset = metadata.GetTableMetadataOffset(TableIndex.EventMap);
int typeDefSize = metadata.GetTableRowCount(TableIndex.TypeDef) < ushort.MaxValue ? 2 : 4;
int eventDefSize = metadata.GetTableRowCount(TableIndex.Event) < ushort.MaxValue ? 2 : 4;
for (int rid = 1; rid <= length; rid++)
{
int parentRow = typeDefSize == 2 ? reader.ReadUInt16() : reader.ReadInt32();
int eventListRow = eventDefSize == 2 ? reader.ReadUInt16() : reader.ReadInt32();
list.Add(new EventMapEntry(rid, MetadataTokens.TypeDefinitionHandle(parentRow), MetadataTokens.EventDefinitionHandle(eventListRow)));
}
return list;
}
public sealed class EventMapEntry
{
public int RID { get; }
[ColumnInfo("X8")]
public int Token => 0x12000000 | RID;
[ColumnInfo("X8", Kind = ColumnKind.Token)]
public int Parent => MetadataTokens.GetToken(parent);
[ColumnInfo("X8", Kind = ColumnKind.Token)]
public int EventList => MetadataTokens.GetToken(eventList);
readonly TypeDefinitionHandle parent;
readonly EventDefinitionHandle eventList;
public EventMapEntry(int rid, TypeDefinitionHandle parent, EventDefinitionHandle eventList)
{
RID = rid;
this.parent = parent;
this.eventList = eventList;
}
}
}
}

81
ILSpy/Metadata/CorTables/FieldLayoutTableTreeNode.cs

@ -0,0 +1,81 @@ @@ -0,0 +1,81 @@
// Copyright (c) 2026 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.Collections.Generic;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using ICSharpCode.Decompiler.Metadata;
namespace ILSpy.Metadata.CorTables
{
/// <summary>
/// View of the FieldLayout table — explicit per-field byte offsets for fields whose
/// owning TypeDef has <see cref="System.Reflection.TypeAttributes.ExplicitLayout"/> set.
/// </summary>
public sealed class FieldLayoutTableTreeNode : MetadataTableTreeNode<FieldLayoutTableTreeNode.FieldLayoutEntry>
{
public FieldLayoutTableTreeNode(MetadataFile metadataFile)
: base(TableIndex.FieldLayout, metadataFile)
{
}
protected override IReadOnlyList<FieldLayoutEntry> LoadTable()
{
var list = new List<FieldLayoutEntry>();
var metadata = metadataFile.Metadata;
var length = metadata.GetTableRowCount(TableIndex.FieldLayout);
var reader = metadata.AsBlobReader();
reader.Offset = metadata.GetTableMetadataOffset(TableIndex.FieldLayout);
int fieldDefSize = metadata.GetTableRowCount(TableIndex.Field) < ushort.MaxValue ? 2 : 4;
for (int rid = 1; rid <= length; rid++)
{
int offset = reader.ReadInt32();
int fieldRow = fieldDefSize == 2 ? reader.ReadUInt16() : reader.ReadInt32();
list.Add(new FieldLayoutEntry(rid, offset, MetadataTokens.FieldDefinitionHandle(fieldRow)));
}
return list;
}
public sealed class FieldLayoutEntry
{
// Use a verbatim fieldHandle local because C# 14 made `field` contextual inside property
// accessors — referencing it without `@` would name the auto-property's hidden
// backing field instead of this member.
readonly FieldDefinitionHandle fieldHandle;
public int RID { get; }
[ColumnInfo("X8")]
public int Token => 0x10000000 | RID;
[ColumnInfo("X8", Kind = ColumnKind.Token)]
public int Field => MetadataTokens.GetToken(fieldHandle);
[ColumnInfo("X8")]
public int FieldOffset { get; }
public FieldLayoutEntry(int rid, int fieldOffset, FieldDefinitionHandle field)
{
RID = rid;
FieldOffset = fieldOffset;
fieldHandle = field;
}
}
}
}

83
ILSpy/Metadata/CorTables/FieldMarshalTableTreeNode.cs

@ -0,0 +1,83 @@ @@ -0,0 +1,83 @@
// Copyright (c) 2026 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.Collections.Generic;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using ICSharpCode.Decompiler.Metadata;
namespace ILSpy.Metadata.CorTables
{
/// <summary>
/// View of the FieldMarshal table — native-marshalling descriptors attached to fields
/// or parameters. Parent is a HasFieldMarshal coded token (Field / Param); NativeType
/// is the heap-offset of a marshal-spec blob.
/// </summary>
public sealed class FieldMarshalTableTreeNode : MetadataTableTreeNode<FieldMarshalTableTreeNode.FieldMarshalEntry>
{
public FieldMarshalTableTreeNode(MetadataFile metadataFile)
: base(TableIndex.FieldMarshal, metadataFile)
{
}
protected override IReadOnlyList<FieldMarshalEntry> LoadTable()
{
var list = new List<FieldMarshalEntry>();
var metadata = metadataFile.Metadata;
var length = metadata.GetTableRowCount(TableIndex.FieldMarshal);
var reader = metadata.AsBlobReader();
reader.Offset = metadata.GetTableMetadataOffset(TableIndex.FieldMarshal);
int hasFieldMarshalRefSize = metadata.ComputeCodedTokenSize(32768, TableMask.Field | TableMask.Param);
int blobHeapSize = metadata.GetHeapSize(HeapIndex.Blob) < ushort.MaxValue ? 2 : 4;
for (int rid = 1; rid <= length; rid++)
{
uint parentTag = (uint)(hasFieldMarshalRefSize == 2 ? reader.ReadUInt16() : reader.ReadInt32());
int blobOffset = blobHeapSize == 2 ? reader.ReadUInt16() : reader.ReadInt32();
list.Add(new FieldMarshalEntry(rid,
MetadataReaderHelpers.FromHasFieldMarshalTag(parentTag),
MetadataTokens.BlobHandle(blobOffset)));
}
return list;
}
public sealed class FieldMarshalEntry
{
public int RID { get; }
[ColumnInfo("X8")]
public int Token => 0x0D000000 | RID;
[ColumnInfo("X8", Kind = ColumnKind.Token)]
public int Parent => MetadataTokens.GetToken(parent);
[ColumnInfo("X8", Kind = ColumnKind.HeapOffset)]
public int NativeType => MetadataTokens.GetHeapOffset(nativeType);
readonly EntityHandle parent;
readonly BlobHandle nativeType;
public FieldMarshalEntry(int rid, EntityHandle parent, BlobHandle nativeType)
{
RID = rid;
this.parent = parent;
this.nativeType = nativeType;
}
}
}
}

80
ILSpy/Metadata/CorTables/FieldRVATableTreeNode.cs

@ -0,0 +1,80 @@ @@ -0,0 +1,80 @@
// Copyright (c) 2026 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.Collections.Generic;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using ICSharpCode.Decompiler.Metadata;
namespace ILSpy.Metadata.CorTables
{
/// <summary>
/// View of the FieldRVA table — for static fields backed by a fixed PE-image RVA, the
/// table maps each FieldDef to its data location. Common with <c>fixed</c> array initializers
/// (the compiler emits a synthesised type whose static fields point at the .rdata bytes).
/// </summary>
public sealed class FieldRVATableTreeNode : MetadataTableTreeNode<FieldRVATableTreeNode.FieldRVAEntry>
{
public FieldRVATableTreeNode(MetadataFile metadataFile)
: base(TableIndex.FieldRva, metadataFile)
{
}
protected override IReadOnlyList<FieldRVAEntry> LoadTable()
{
var list = new List<FieldRVAEntry>();
var metadata = metadataFile.Metadata;
var length = metadata.GetTableRowCount(TableIndex.FieldRva);
var reader = metadata.AsBlobReader();
reader.Offset = metadata.GetTableMetadataOffset(TableIndex.FieldRva);
int fieldDefSize = metadata.GetTableRowCount(TableIndex.Field) < ushort.MaxValue ? 2 : 4;
for (int rid = 1; rid <= length; rid++)
{
int rva = reader.ReadInt32();
int fieldRow = fieldDefSize == 2 ? reader.ReadUInt16() : reader.ReadInt32();
list.Add(new FieldRVAEntry(rid, rva, MetadataTokens.FieldDefinitionHandle(fieldRow)));
}
return list;
}
public sealed class FieldRVAEntry
{
// Verbatim fieldHandle — see FieldLayoutTableTreeNode for the C# 14 keyword note.
readonly FieldDefinitionHandle fieldHandle;
public int RID { get; }
[ColumnInfo("X8")]
public int Token => 0x1D000000 | RID;
[ColumnInfo("X8", Kind = ColumnKind.Token)]
public int Field => MetadataTokens.GetToken(fieldHandle);
[ColumnInfo("X8")]
public int RVA { get; }
public FieldRVAEntry(int rid, int rva, FieldDefinitionHandle field)
{
RID = rid;
RVA = rva;
fieldHandle = field;
}
}
}
}

95
ILSpy/Metadata/CorTables/ImplMapTableTreeNode.cs

@ -0,0 +1,95 @@ @@ -0,0 +1,95 @@
// Copyright (c) 2026 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.Collections.Generic;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using ICSharpCode.Decompiler.Metadata;
namespace ILSpy.Metadata.CorTables
{
/// <summary>
/// View of the ImplMap table — P/Invoke linkage. Each row maps a managed MethodDef (or
/// Field, in legacy CIL) to its native entry point: the <c>extern</c> name plus the
/// ModuleRef holding the DLL identifier.
/// </summary>
public sealed class ImplMapTableTreeNode : MetadataTableTreeNode<ImplMapTableTreeNode.ImplMapEntry>
{
public ImplMapTableTreeNode(MetadataFile metadataFile)
: base(TableIndex.ImplMap, metadataFile)
{
}
protected override IReadOnlyList<ImplMapEntry> LoadTable()
{
var list = new List<ImplMapEntry>();
var metadata = metadataFile.Metadata;
var length = metadata.GetTableRowCount(TableIndex.ImplMap);
var reader = metadata.AsBlobReader();
reader.Offset = metadata.GetTableMetadataOffset(TableIndex.ImplMap);
int moduleRefSize = metadata.GetTableRowCount(TableIndex.ModuleRef) < ushort.MaxValue ? 2 : 4;
int memberForwardedTagSize = metadata.ComputeCodedTokenSize(32768, TableMask.MethodDef | TableMask.Field);
int stringHandleSize = metadata.GetHeapSize(HeapIndex.String) < ushort.MaxValue ? 2 : 4;
for (int rid = 1; rid <= length; rid++)
{
MethodImportAttributes mappingFlags = (MethodImportAttributes)reader.ReadUInt16();
uint memberForwardedTag = (uint)(memberForwardedTagSize == 2 ? reader.ReadUInt16() : reader.ReadInt32());
int importNameOffset = stringHandleSize == 2 ? reader.ReadUInt16() : reader.ReadInt32();
int importScopeRow = moduleRefSize == 2 ? reader.ReadUInt16() : reader.ReadInt32();
list.Add(new ImplMapEntry(rid, mappingFlags,
MetadataReaderHelpers.FromMemberForwardedTag(memberForwardedTag),
metadata.GetString(MetadataTokens.StringHandle(importNameOffset)),
MetadataTokens.ModuleReferenceHandle(importScopeRow)));
}
return list;
}
public sealed class ImplMapEntry
{
public int RID { get; }
[ColumnInfo("X8")]
public int Token => 0x1C000000 | RID;
[ColumnInfo("X8")]
public MethodImportAttributes MappingFlags { get; }
[ColumnInfo("X8", Kind = ColumnKind.Token)]
public int MemberForwarded => MetadataTokens.GetToken(memberForwarded);
public string ImportName { get; }
[ColumnInfo("X8", Kind = ColumnKind.Token)]
public int ImportScope => MetadataTokens.GetToken(importScope);
readonly EntityHandle memberForwarded;
readonly ModuleReferenceHandle importScope;
public ImplMapEntry(int rid, MethodImportAttributes mappingFlags, EntityHandle memberForwarded, string importName, ModuleReferenceHandle importScope)
{
RID = rid;
MappingFlags = mappingFlags;
this.memberForwarded = memberForwarded;
ImportName = importName;
this.importScope = importScope;
}
}
}
}

82
ILSpy/Metadata/CorTables/InterfaceImplTableTreeNode.cs

@ -0,0 +1,82 @@ @@ -0,0 +1,82 @@
// Copyright (c) 2026 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.Collections.Generic;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using ICSharpCode.Decompiler.Metadata;
namespace ILSpy.Metadata.CorTables
{
/// <summary>
/// View of the InterfaceImpl table — every type/interface implementation pair. Class is
/// a TypeDef row; Interface is a TypeDefOrRef coded token (TypeDef / TypeRef / TypeSpec).
/// </summary>
public sealed class InterfaceImplTableTreeNode : MetadataTableTreeNode<InterfaceImplTableTreeNode.InterfaceImplEntry>
{
public InterfaceImplTableTreeNode(MetadataFile metadataFile)
: base(TableIndex.InterfaceImpl, metadataFile)
{
}
protected override IReadOnlyList<InterfaceImplEntry> LoadTable()
{
var list = new List<InterfaceImplEntry>();
var metadata = metadataFile.Metadata;
var length = metadata.GetTableRowCount(TableIndex.InterfaceImpl);
var reader = metadata.AsBlobReader();
reader.Offset = metadata.GetTableMetadataOffset(TableIndex.InterfaceImpl);
int typeDefSize = metadata.GetTableRowCount(TableIndex.TypeDef) < ushort.MaxValue ? 2 : 4;
int interfaceTagSize = metadata.ComputeCodedTokenSize(32768, TableMask.TypeDef | TableMask.TypeRef | TableMask.TypeSpec);
for (int rid = 1; rid <= length; rid++)
{
int classRow = typeDefSize == 2 ? reader.ReadUInt16() : reader.ReadInt32();
uint interfaceTag = (uint)(interfaceTagSize == 2 ? reader.ReadUInt16() : reader.ReadInt32());
list.Add(new InterfaceImplEntry(rid,
MetadataTokens.TypeDefinitionHandle(classRow),
MetadataReaderHelpers.FromTypeDefOrRefTag(interfaceTag)));
}
return list;
}
public sealed class InterfaceImplEntry
{
public int RID { get; }
[ColumnInfo("X8")]
public int Token => 0x09000000 | RID;
[ColumnInfo("X8", Kind = ColumnKind.Token)]
public int Class => MetadataTokens.GetToken(@class);
[ColumnInfo("X8", Kind = ColumnKind.Token)]
public int Interface => MetadataTokens.GetToken(@interface);
readonly TypeDefinitionHandle @class;
readonly EntityHandle @interface;
public InterfaceImplEntry(int rid, TypeDefinitionHandle @class, EntityHandle @interface)
{
RID = rid;
this.@class = @class;
this.@interface = @interface;
}
}
}
}

75
ILSpy/Metadata/CorTables/MethodImplTableTreeNode.cs

@ -0,0 +1,75 @@ @@ -0,0 +1,75 @@
// Copyright (c) 2026 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.Collections.Generic;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using ICSharpCode.Decompiler.Metadata;
namespace ILSpy.Metadata.CorTables
{
/// <summary>
/// View of the MethodImpl table — explicit interface implementations and method-body
/// overrides where the declared signature differs from the impl signature. Each row
/// pairs a Type, a MethodDeclaration (the override target), and a MethodBody (the impl).
/// </summary>
public sealed class MethodImplTableTreeNode : MetadataTableTreeNode<MethodImplTableTreeNode.MethodImplEntry>
{
public MethodImplTableTreeNode(MetadataFile metadataFile)
: base(TableIndex.MethodImpl, metadataFile)
{
}
protected override IReadOnlyList<MethodImplEntry> LoadTable()
{
var list = new List<MethodImplEntry>();
for (int row = 1; row <= metadataFile.Metadata.GetTableRowCount(TableIndex.MethodImpl); row++)
list.Add(new MethodImplEntry(metadataFile, MetadataTokens.MethodImplementationHandle(row)));
return list;
}
public sealed class MethodImplEntry
{
readonly MetadataFile metadataFile;
readonly MethodImplementationHandle handle;
readonly MethodImplementation methodImpl;
public int RID => MetadataTokens.GetToken(handle) & 0xFFFFFF;
[ColumnInfo("X8")]
public int Token => MetadataTokens.GetToken(handle);
[ColumnInfo("X8", Kind = ColumnKind.Token)]
public int Type => MetadataTokens.GetToken(methodImpl.Type);
[ColumnInfo("X8", Kind = ColumnKind.Token)]
public int MethodDeclaration => MetadataTokens.GetToken(methodImpl.MethodDeclaration);
[ColumnInfo("X8", Kind = ColumnKind.Token)]
public int MethodBody => MetadataTokens.GetToken(methodImpl.MethodBody);
public MethodImplEntry(MetadataFile metadataFile, MethodImplementationHandle handle)
{
this.metadataFile = metadataFile;
this.handle = handle;
methodImpl = metadataFile.Metadata.GetMethodImplementation(handle);
}
}
}
}

77
ILSpy/Metadata/CorTables/MethodSemanticsTableTreeNode.cs

@ -0,0 +1,77 @@ @@ -0,0 +1,77 @@
// Copyright (c) 2026 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.Collections.Generic;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using ICSharpCode.Decompiler.Metadata;
namespace ILSpy.Metadata.CorTables
{
/// <summary>
/// View of the MethodSemantics table — links accessor methods (get/set/add/remove/raise)
/// to the property or event they implement. <see cref="MetadataReader.GetMethodSemantics"/>
/// returns a typed enumeration so byte-level reading isn't required.
/// </summary>
public sealed class MethodSemanticsTableTreeNode : MetadataTableTreeNode<MethodSemanticsTableTreeNode.MethodSemanticsEntry>
{
public MethodSemanticsTableTreeNode(MetadataFile metadataFile)
: base(TableIndex.MethodSemantics, metadataFile)
{
}
protected override IReadOnlyList<MethodSemanticsEntry> LoadTable()
{
var list = new List<MethodSemanticsEntry>();
foreach (var row in metadataFile.Metadata.GetMethodSemantics())
list.Add(new MethodSemanticsEntry(row.Handle, row.Semantics, row.Method, row.Association));
return list;
}
public sealed class MethodSemanticsEntry
{
public int RID => MetadataTokens.GetToken(handle) & 0xFFFFFF;
[ColumnInfo("X8")]
public int Token => MetadataTokens.GetToken(handle);
[ColumnInfo("X8")]
public MethodSemanticsAttributes Semantics { get; }
[ColumnInfo("X8", Kind = ColumnKind.Token)]
public int Method => MetadataTokens.GetToken(method);
[ColumnInfo("X8", Kind = ColumnKind.Token)]
public int Association => MetadataTokens.GetToken(association);
readonly Handle handle;
readonly MethodDefinitionHandle method;
readonly EntityHandle association;
public MethodSemanticsEntry(Handle handle, MethodSemanticsAttributes semantics, MethodDefinitionHandle method, EntityHandle association)
{
this.handle = handle;
Semantics = semantics;
this.method = method;
this.association = association;
}
}
}
}

80
ILSpy/Metadata/CorTables/NestedClassTableTreeNode.cs

@ -0,0 +1,80 @@ @@ -0,0 +1,80 @@
// Copyright (c) 2026 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.Collections.Generic;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using ICSharpCode.Decompiler.Metadata;
namespace ILSpy.Metadata.CorTables
{
/// <summary>
/// View of the NestedClass table — pairs each nested type with its enclosing type. Walked
/// directly from the table bytes because System.Reflection.Metadata only exposes
/// nested-type discovery as a per-typedef enumeration.
/// </summary>
public sealed class NestedClassTableTreeNode : MetadataTableTreeNode<NestedClassTableTreeNode.NestedClassEntry>
{
public NestedClassTableTreeNode(MetadataFile metadataFile)
: base(TableIndex.NestedClass, metadataFile)
{
}
protected override IReadOnlyList<NestedClassEntry> LoadTable()
{
var list = new List<NestedClassEntry>();
var metadata = metadataFile.Metadata;
var length = metadata.GetTableRowCount(TableIndex.NestedClass);
var reader = metadata.AsBlobReader();
reader.Offset = metadata.GetTableMetadataOffset(TableIndex.NestedClass);
int typeDefSize = metadata.GetTableRowCount(TableIndex.TypeDef) < ushort.MaxValue ? 2 : 4;
for (int rid = 1; rid <= length; rid++)
{
int nestedRow = typeDefSize == 2 ? reader.ReadUInt16() : reader.ReadInt32();
int enclosingRow = typeDefSize == 2 ? reader.ReadUInt16() : reader.ReadInt32();
list.Add(new NestedClassEntry(rid, MetadataTokens.TypeDefinitionHandle(nestedRow), MetadataTokens.TypeDefinitionHandle(enclosingRow)));
}
return list;
}
public sealed class NestedClassEntry
{
public int RID { get; }
[ColumnInfo("X8")]
public int Token => 0x29000000 | RID;
[ColumnInfo("X8", Kind = ColumnKind.Token)]
public int NestedClass => MetadataTokens.GetToken(nested);
[ColumnInfo("X8", Kind = ColumnKind.Token)]
public int EnclosingClass => MetadataTokens.GetToken(enclosing);
readonly TypeDefinitionHandle nested;
readonly TypeDefinitionHandle enclosing;
public NestedClassEntry(int rid, TypeDefinitionHandle nested, TypeDefinitionHandle enclosing)
{
RID = rid;
this.nested = nested;
this.enclosing = enclosing;
}
}
}
}

80
ILSpy/Metadata/CorTables/PropertyMapTableTreeNode.cs

@ -0,0 +1,80 @@ @@ -0,0 +1,80 @@
// Copyright (c) 2026 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.Collections.Generic;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using ICSharpCode.Decompiler.Metadata;
namespace ILSpy.Metadata.CorTables
{
/// <summary>
/// View of the PropertyMap table — pairs each TypeDef that declares properties with the
/// first row of its property list. Same shape as EventMap but for properties.
/// </summary>
public sealed class PropertyMapTableTreeNode : MetadataTableTreeNode<PropertyMapTableTreeNode.PropertyMapEntry>
{
public PropertyMapTableTreeNode(MetadataFile metadataFile)
: base(TableIndex.PropertyMap, metadataFile)
{
}
protected override IReadOnlyList<PropertyMapEntry> LoadTable()
{
var list = new List<PropertyMapEntry>();
var metadata = metadataFile.Metadata;
var length = metadata.GetTableRowCount(TableIndex.PropertyMap);
var reader = metadata.AsBlobReader();
reader.Offset = metadata.GetTableMetadataOffset(TableIndex.PropertyMap);
int typeDefSize = metadata.GetTableRowCount(TableIndex.TypeDef) < ushort.MaxValue ? 2 : 4;
int propertyDefSize = metadata.GetTableRowCount(TableIndex.Property) < ushort.MaxValue ? 2 : 4;
for (int rid = 1; rid <= length; rid++)
{
int parentRow = typeDefSize == 2 ? reader.ReadUInt16() : reader.ReadInt32();
int propertyListRow = propertyDefSize == 2 ? reader.ReadUInt16() : reader.ReadInt32();
list.Add(new PropertyMapEntry(rid, MetadataTokens.TypeDefinitionHandle(parentRow), MetadataTokens.PropertyDefinitionHandle(propertyListRow)));
}
return list;
}
public sealed class PropertyMapEntry
{
public int RID { get; }
[ColumnInfo("X8")]
public int Token => 0x15000000 | RID;
[ColumnInfo("X8", Kind = ColumnKind.Token)]
public int Parent => MetadataTokens.GetToken(parent);
[ColumnInfo("X8", Kind = ColumnKind.Token)]
public int PropertyList => MetadataTokens.GetToken(propertyList);
readonly TypeDefinitionHandle parent;
readonly PropertyDefinitionHandle propertyList;
public PropertyMapEntry(int rid, TypeDefinitionHandle parent, PropertyDefinitionHandle propertyList)
{
RID = rid;
this.parent = parent;
this.propertyList = propertyList;
}
}
}
}

88
ILSpy/Metadata/CorTables/PtrTableTreeNode.cs

@ -0,0 +1,88 @@ @@ -0,0 +1,88 @@
// Copyright (c) 2026 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.Generic;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using ICSharpCode.Decompiler.Metadata;
namespace ILSpy.Metadata.CorTables
{
/// <summary>
/// View of the five Ptr tables (FieldPtr / MethodPtr / ParamPtr / EventPtr / PropertyPtr).
/// Present only in metadata produced with #~ + indirection (rare; mostly EnC or some
/// custom emitters), and each row carries a single handle into the table the Ptr points
/// at. One node type drives all five since the row shape is identical.
/// </summary>
public sealed class PtrTableTreeNode : MetadataTableTreeNode<PtrTableTreeNode.PtrEntry>
{
readonly TableIndex referencedTableKind;
public PtrTableTreeNode(TableIndex kind, MetadataFile metadataFile)
: base(kind, metadataFile)
{
referencedTableKind = kind switch {
TableIndex.EventPtr => TableIndex.Event,
TableIndex.FieldPtr => TableIndex.Field,
TableIndex.MethodPtr => TableIndex.MethodDef,
TableIndex.ParamPtr => TableIndex.Param,
TableIndex.PropertyPtr => TableIndex.Property,
_ => throw new ArgumentOutOfRangeException(nameof(kind), $"PtrTable does not support {kind}"),
};
}
protected override IReadOnlyList<PtrEntry> LoadTable()
{
var list = new List<PtrEntry>();
var metadata = metadataFile.Metadata;
var length = metadata.GetTableRowCount(Kind);
var reader = metadata.AsBlobReader();
reader.Offset = metadata.GetTableMetadataOffset(Kind);
int handleSize = metadata.GetTableRowCount(referencedTableKind) < ushort.MaxValue ? 2 : 4;
for (int rid = 1; rid <= length; rid++)
{
int handleRow = handleSize == 2 ? reader.ReadUInt16() : reader.ReadInt32();
list.Add(new PtrEntry(rid, Kind, MetadataTokens.EntityHandle(((int)referencedTableKind << 24) | handleRow)));
}
return list;
}
public sealed class PtrEntry
{
public int RID { get; }
[ColumnInfo("X8")]
public int Token => ((int)kind << 24) | RID;
[ColumnInfo("X8", Kind = ColumnKind.Token)]
public int Handle => MetadataTokens.GetToken(handle);
readonly TableIndex kind;
readonly EntityHandle handle;
public PtrEntry(int rid, TableIndex kind, EntityHandle handle)
{
RID = rid;
this.kind = kind;
this.handle = handle;
}
}
}
}

106
ILSpy/Metadata/Helpers.cs

@ -18,6 +18,8 @@ @@ -18,6 +18,8 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Metadata;
namespace ILSpy.Metadata
{
@ -90,4 +92,108 @@ namespace ILSpy.Metadata @@ -90,4 +92,108 @@ namespace ILSpy.Metadata
Meaning = meaning;
}
}
/// <summary>
/// Bitmask covering each CLI metadata table. Used as the second argument to
/// <see cref="MetadataReaderHelpers.ComputeCodedTokenSize"/> when computing how many bytes
/// a coded-token column occupies in a given metadata blob — same encoding as the WPF
/// host's <c>TableMask</c>.
/// </summary>
[Flags]
public enum TableMask : ulong
{
Module = 0x1,
TypeRef = 0x2,
TypeDef = 0x4,
FieldPtr = 0x8,
Field = 0x10,
MethodPtr = 0x20,
MethodDef = 0x40,
ParamPtr = 0x80,
Param = 0x100,
InterfaceImpl = 0x200,
MemberRef = 0x400,
Constant = 0x800,
CustomAttribute = 0x1000,
FieldMarshal = 0x2000,
DeclSecurity = 0x4000,
ClassLayout = 0x8000,
FieldLayout = 0x10000,
StandAloneSig = 0x20000,
EventMap = 0x40000,
EventPtr = 0x80000,
Event = 0x100000,
PropertyMap = 0x200000,
PropertyPtr = 0x400000,
Property = 0x800000,
MethodSemantics = 0x1000000,
MethodImpl = 0x2000000,
ModuleRef = 0x4000000,
TypeSpec = 0x8000000,
ImplMap = 0x10000000,
FieldRva = 0x20000000,
EnCLog = 0x40000000,
EnCMap = 0x80000000,
Assembly = 0x100000000,
AssemblyRef = 0x800000000,
File = 0x4000000000,
ExportedType = 0x8000000000,
ManifestResource = 0x10000000000,
NestedClass = 0x20000000000,
GenericParam = 0x40000000000,
MethodSpec = 0x80000000000,
GenericParamConstraint = 0x100000000000,
Document = 0x1000000000000,
MethodDebugInformation = 0x2000000000000,
LocalScope = 0x4000000000000,
LocalVariable = 0x8000000000000,
LocalConstant = 0x10000000000000,
ImportScope = 0x20000000000000,
StateMachineMethod = 0x40000000000000,
CustomDebugInformation = 0x80000000000000,
}
/// <summary>
/// Reflection-backed shims onto <c>System.Reflection.Metadata</c> internals — needed to
/// decode coded-token columns and compute their on-disk widths. The corresponding public
/// APIs do not exist; the WPF host carries the same reflection infrastructure. Pre-existing
/// risk: if a future runtime renames <c>TypeDefOrRefTag.ConvertToHandle</c> or the
/// <c>TableRowCounts</c> field on <c>MetadataReader</c>, these methods will throw and
/// the affected metadata-table viewers will break.
/// </summary>
public static class MetadataReaderHelpers
{
static readonly FieldInfo? rowCountsField;
static readonly MethodInfo? computeCodedTokenSizeMethod;
static readonly MethodInfo? typeDefOrRefConvert;
static readonly MethodInfo? hasFieldMarshalConvert;
static readonly MethodInfo? memberForwardedConvert;
static MetadataReaderHelpers()
{
rowCountsField = typeof(MetadataReader)
.GetField("TableRowCounts", BindingFlags.NonPublic | BindingFlags.Instance);
computeCodedTokenSizeMethod = typeof(MetadataReader)
.GetMethod("ComputeCodedTokenSize", BindingFlags.Instance | BindingFlags.NonPublic);
var asm = typeof(TypeDefinitionHandle).Assembly;
typeDefOrRefConvert = asm.GetType("System.Reflection.Metadata.Ecma335.TypeDefOrRefTag")
?.GetMethod("ConvertToHandle", BindingFlags.Static | BindingFlags.NonPublic);
hasFieldMarshalConvert = asm.GetType("System.Reflection.Metadata.Ecma335.HasFieldMarshalTag")
?.GetMethod("ConvertToHandle", BindingFlags.Static | BindingFlags.NonPublic);
memberForwardedConvert = asm.GetType("System.Reflection.Metadata.Ecma335.MemberForwardedTag")
?.GetMethod("ConvertToHandle", BindingFlags.Static | BindingFlags.NonPublic);
}
public static EntityHandle FromTypeDefOrRefTag(uint tag)
=> (EntityHandle)typeDefOrRefConvert!.Invoke(null, [tag])!;
public static EntityHandle FromHasFieldMarshalTag(uint tag)
=> (EntityHandle)hasFieldMarshalConvert!.Invoke(null, [tag])!;
public static EntityHandle FromMemberForwardedTag(uint tag)
=> (EntityHandle)memberForwardedConvert!.Invoke(null, [tag])!;
public static int ComputeCodedTokenSize(this MetadataReader metadata, int largeRowSize, TableMask mask)
=> (int)computeCodedTokenSizeMethod!.Invoke(metadata, [largeRowSize, rowCountsField!.GetValue(metadata)!, (ulong)mask])!;
}
}

13
ILSpy/Metadata/MetadataTablesTreeNode.cs

@ -106,6 +106,19 @@ namespace ILSpy.Metadata @@ -106,6 +106,19 @@ namespace ILSpy.Metadata
TableIndex.ImportScope => new ImportScopeTableTreeNode(metadataFile),
TableIndex.StateMachineMethod => new StateMachineMethodTableTreeNode(metadataFile),
TableIndex.CustomDebugInformation => new CustomDebugInformationTableTreeNode(metadataFile),
TableIndex.MethodImpl => new MethodImplTableTreeNode(metadataFile),
TableIndex.MethodSemantics => new MethodSemanticsTableTreeNode(metadataFile),
TableIndex.ClassLayout => new ClassLayoutTableTreeNode(metadataFile),
TableIndex.FieldLayout => new FieldLayoutTableTreeNode(metadataFile),
TableIndex.FieldRva => new FieldRVATableTreeNode(metadataFile),
TableIndex.NestedClass => new NestedClassTableTreeNode(metadataFile),
TableIndex.EventMap => new EventMapTableTreeNode(metadataFile),
TableIndex.PropertyMap => new PropertyMapTableTreeNode(metadataFile),
TableIndex.InterfaceImpl => new InterfaceImplTableTreeNode(metadataFile),
TableIndex.FieldMarshal => new FieldMarshalTableTreeNode(metadataFile),
TableIndex.ImplMap => new ImplMapTableTreeNode(metadataFile),
TableIndex.FieldPtr or TableIndex.MethodPtr or TableIndex.ParamPtr
or TableIndex.EventPtr or TableIndex.PropertyPtr => new PtrTableTreeNode(table, metadataFile),
_ => new UnsupportedMetadataTableTreeNode(table, metadataFile),
};
}

Loading…
Cancel
Save