Browse Source

Fix per-row reads in MetadataReader.GetMethodSemantics

The Semantics and Association columns were read from offsets relative
to the metadata root instead of the current row: Semantics always
decoded the first two bytes of the metadata header and Association a
constant offset near it, so only the Method column ever carried real
row data. The Association coded-index width also used the plain-index
threshold (2^16) instead of the coded one (2^15 for one tag bit).
Rewrite the loop with a BlobReader positioned at the table start,
reading each column in sequence, and introduce SimpleIndexSize and
CodedIndexSize helpers encoding the ECMA-335 II.24.2.6 width rules.

The GUI's MethodSemantics metadata table view consumes this helper and
displayed the garbage values.

Assisted-by: Claude:claude-fable-5:Claude Code
pull/3947/head
Siegfried Pammer 2 months ago committed by Siegfried Pammer
parent
commit
313d1c6af0
  1. 58
      ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs

58
ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs

@ -350,36 +350,54 @@ namespace ICSharpCode.Decompiler.Metadata @@ -350,36 +350,54 @@ namespace ICSharpCode.Decompiler.Metadata
public static IEnumerable<(Handle Handle, MethodSemanticsAttributes Semantics, MethodDefinitionHandle Method, EntityHandle Association)> GetMethodSemantics(this MetadataReader metadata)
{
int offset = metadata.GetTableMetadataOffset(TableIndex.MethodSemantics);
int rowSize = metadata.GetTableRowSize(TableIndex.MethodSemantics);
int rowCount = metadata.GetTableRowCount(TableIndex.MethodSemantics);
var reader = metadata.AsBlobReader();
reader.Offset = metadata.GetTableMetadataOffset(TableIndex.MethodSemantics);
bool methodSmall = metadata.GetTableRowCount(TableIndex.MethodDef) <= ushort.MaxValue;
bool assocSmall = metadata.GetTableRowCount(TableIndex.Property) <= ushort.MaxValue && metadata.GetTableRowCount(TableIndex.Event) <= ushort.MaxValue;
int assocOffset = (methodSmall ? 2 : 4) + 2;
for (int row = 0; row < rowCount; row++)
int methodSize = SimpleIndexSize(metadata, TableIndex.MethodDef);
// HasSemantics coded index: 1 tag bit over Event (tag 0) and Property (tag 1).
int assocSize = CodedIndexSize(metadata, 1, TableIndex.Event, TableIndex.Property);
for (int rid = 1; rid <= rowCount; rid++)
{
yield return Read(row);
}
(Handle Handle, MethodSemanticsAttributes Semantics, MethodDefinitionHandle Method, EntityHandle Association) Read(int row)
{
var span = metadata.AsReadOnlySpan();
var methodDefSpan = span.Slice(offset + rowSize * row + 2);
int methodDef = methodSmall ? BinaryPrimitives.ReadUInt16LittleEndian(methodDefSpan) : (int)BinaryPrimitives.ReadUInt32LittleEndian(methodDefSpan);
var assocSpan = span.Slice(assocOffset);
int assocDef = assocSmall ? BinaryPrimitives.ReadUInt16LittleEndian(assocSpan) : (int)BinaryPrimitives.ReadUInt32LittleEndian(assocSpan);
var semantics = (MethodSemanticsAttributes)reader.ReadUInt16();
int methodRow = methodSize == 2 ? reader.ReadUInt16() : reader.ReadInt32();
int assocTag = assocSize == 2 ? reader.ReadUInt16() : reader.ReadInt32();
EntityHandle propOrEvent;
if ((assocDef & 0x1) == 1)
if ((assocTag & 0x1) == 1)
{
propOrEvent = MetadataTokens.PropertyDefinitionHandle(assocDef >> 1);
propOrEvent = MetadataTokens.PropertyDefinitionHandle(assocTag >> 1);
}
else
{
propOrEvent = MetadataTokens.EventDefinitionHandle(assocDef >> 1);
propOrEvent = MetadataTokens.EventDefinitionHandle(assocTag >> 1);
}
return (MetadataTokens.Handle(0x18000000 | (row + 1)), (MethodSemanticsAttributes)(BinaryPrimitives.ReadUInt16LittleEndian(span)), MetadataTokens.MethodDefinitionHandle(methodDef), propOrEvent);
yield return (MetadataTokens.Handle(((int)TableIndex.MethodSemantics << 24) | rid), semantics, MetadataTokens.MethodDefinitionHandle(methodRow), propOrEvent);
}
}
/// <summary>
/// Size in bytes of a column indexing <paramref name="table"/> directly
/// (ECMA-335 II.24.2.6: 2 bytes while the table has fewer than 2^16 rows).
/// </summary>
static int SimpleIndexSize(MetadataReader metadata, TableIndex table)
{
return metadata.GetTableRowCount(table) < (1 << 16) ? 2 : 4;
}
/// <summary>
/// Size in bytes of a coded-index column over <paramref name="tables"/> with
/// <paramref name="tagBits"/> tag bits (ECMA-335 II.24.2.6: 2 bytes while every
/// indexed table has fewer than 2^(16 - tagBits) rows).
/// </summary>
static int CodedIndexSize(MetadataReader metadata, int tagBits, params TableIndex[] tables)
{
int smallLimit = 1 << (16 - tagBits);
foreach (var table in tables)
{
if (metadata.GetTableRowCount(table) >= smallLimit)
return 4;
}
return 2;
}
public static IEnumerable<EntityHandle> GetFieldLayouts(this MetadataReader metadata)

Loading…
Cancel
Save