mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
745 lines
30 KiB
745 lines
30 KiB
// Copyright (c) 2018 Siegfried Pammer |
|
// |
|
// 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.Buffers.Binary; |
|
using System.Collections.Generic; |
|
using System.IO; |
|
using System.Linq; |
|
using System.Reflection; |
|
using System.Reflection.Metadata; |
|
using System.Reflection.Metadata.Ecma335; |
|
using System.Text; |
|
|
|
using ICSharpCode.Decompiler.TypeSystem; |
|
using ICSharpCode.Decompiler.TypeSystem.Implementation; |
|
using ICSharpCode.Decompiler.Util; |
|
|
|
using SRM = System.Reflection.Metadata; |
|
|
|
namespace ICSharpCode.Decompiler.Metadata |
|
{ |
|
public static class MetadataExtensions |
|
{ |
|
static string CalculatePublicKeyToken(BlobHandle blob, MetadataReader reader) |
|
{ |
|
// Calculate public key token: |
|
// 1. hash the public key (the strong-name format mandates SHA-1; a managed |
|
// implementation is used so this works under restrictive crypto policies). |
|
byte[] publicKeyTokenBytes = new byte[20]; |
|
Sha1ForNonSecretPurposes.HashData(reader.GetBlobBytes(blob), publicKeyTokenBytes); |
|
|
|
// 2. take the last 8 bytes |
|
// 3. according to Cecil we need to reverse them, other sources did not mention this. |
|
return publicKeyTokenBytes.TakeLast(8).Reverse().ToHexString(8); |
|
} |
|
|
|
public static string GetPublicKeyToken(this MetadataReader reader) |
|
{ |
|
if (!reader.IsAssembly) |
|
return string.Empty; |
|
var asm = reader.GetAssemblyDefinition(); |
|
string publicKey = "null"; |
|
if (!asm.PublicKey.IsNil) |
|
{ |
|
// AssemblyFlags.PublicKey does not apply to assembly definitions |
|
publicKey = CalculatePublicKeyToken(asm.PublicKey, reader); |
|
} |
|
return publicKey; |
|
} |
|
|
|
public static string GetFullAssemblyName(this MetadataReader reader) |
|
{ |
|
if (!reader.IsAssembly) |
|
return string.Empty; |
|
var asm = reader.GetAssemblyDefinition(); |
|
string publicKey = reader.GetPublicKeyToken(); |
|
return $"{reader.GetString(asm.Name)}, " + |
|
$"Version={asm.Version}, " + |
|
$"Culture={(asm.Culture.IsNil ? "neutral" : reader.GetString(asm.Culture))}, " + |
|
$"PublicKeyToken={publicKey}"; |
|
} |
|
|
|
public static bool TryGetFullAssemblyName(this MetadataReader reader, out string assemblyName) |
|
{ |
|
try |
|
{ |
|
assemblyName = GetFullAssemblyName(reader); |
|
return true; |
|
} |
|
catch (BadImageFormatException) |
|
{ |
|
assemblyName = null; |
|
return false; |
|
} |
|
} |
|
|
|
public static string GetFullAssemblyName(this SRM.AssemblyReference reference, MetadataReader reader) |
|
{ |
|
StringBuilder builder = new StringBuilder(); |
|
builder.Append(reader.GetString(reference.Name)); |
|
builder.Append(", Version="); |
|
builder.Append(reference.Version); |
|
builder.Append(", Culture="); |
|
if (reference.Culture.IsNil) |
|
{ |
|
builder.Append("neutral"); |
|
} |
|
else |
|
{ |
|
builder.Append(reader.GetString(reference.Culture)); |
|
} |
|
|
|
if (reference.PublicKeyOrToken.IsNil) |
|
{ |
|
builder.Append(", PublicKeyToken=null"); |
|
} |
|
else if ((reference.Flags & AssemblyFlags.PublicKey) != 0) |
|
{ |
|
builder.Append(", PublicKeyToken="); |
|
builder.Append(CalculatePublicKeyToken(reference.PublicKeyOrToken, reader)); |
|
} |
|
else |
|
{ |
|
builder.Append(", PublicKeyToken="); |
|
builder.AppendHexString(reader.GetBlobReader(reference.PublicKeyOrToken)); |
|
} |
|
if ((reference.Flags & AssemblyFlags.Retargetable) != 0) |
|
{ |
|
builder.Append(", Retargetable=true"); |
|
} |
|
return builder.ToString(); |
|
} |
|
|
|
public static bool TryGetFullAssemblyName(this SRM.AssemblyReference reference, MetadataReader reader, out string assemblyName) |
|
{ |
|
try |
|
{ |
|
assemblyName = GetFullAssemblyName(reference, reader); |
|
return true; |
|
} |
|
catch (BadImageFormatException) |
|
{ |
|
assemblyName = null; |
|
return false; |
|
} |
|
} |
|
|
|
public static string ToHexString(this IEnumerable<byte> bytes, int estimatedLength) |
|
{ |
|
if (bytes == null) |
|
throw new ArgumentNullException(nameof(bytes)); |
|
|
|
StringBuilder sb = new StringBuilder(estimatedLength * 2); |
|
foreach (var b in bytes) |
|
sb.AppendFormat("{0:x2}", b); |
|
return sb.ToString(); |
|
} |
|
|
|
public static void AppendHexString(this StringBuilder builder, BlobReader reader) |
|
{ |
|
for (int i = 0; i < reader.Length; i++) |
|
{ |
|
builder.AppendFormat("{0:x2}", reader.ReadByte()); |
|
} |
|
} |
|
|
|
public static string ToHexString(this BlobReader reader) |
|
{ |
|
StringBuilder sb = new StringBuilder(reader.Length * 3); |
|
for (int i = 0; i < reader.Length; i++) |
|
{ |
|
if (i == 0) |
|
sb.AppendFormat("{0:X2}", reader.ReadByte()); |
|
else |
|
sb.AppendFormat("-{0:X2}", reader.ReadByte()); |
|
} |
|
return sb.ToString(); |
|
} |
|
|
|
public static IEnumerable<TypeDefinitionHandle> GetTopLevelTypeDefinitions(this MetadataReader reader) |
|
{ |
|
foreach (var handle in reader.TypeDefinitions) |
|
{ |
|
var td = reader.GetTypeDefinition(handle); |
|
if (td.GetDeclaringType().IsNil) |
|
yield return handle; |
|
} |
|
} |
|
|
|
public static string ToILNameString(this FullTypeName typeName, bool omitGenerics = false) |
|
{ |
|
string name; |
|
if (typeName.IsNested) |
|
{ |
|
name = typeName.Name; |
|
if (!omitGenerics) |
|
{ |
|
int localTypeParameterCount = typeName.GetNestedTypeAdditionalTypeParameterCount(typeName.NestingLevel - 1); |
|
if (localTypeParameterCount > 0) |
|
name += "`" + localTypeParameterCount; |
|
} |
|
name = Disassembler.DisassemblerHelpers.Escape(name); |
|
return $"{typeName.GetDeclaringType().ToILNameString(omitGenerics)}/{name}"; |
|
} |
|
if (!string.IsNullOrEmpty(typeName.TopLevelTypeName.Namespace)) |
|
{ |
|
name = $"{typeName.TopLevelTypeName.Namespace}.{typeName.Name}"; |
|
if (!omitGenerics && typeName.TypeParameterCount > 0) |
|
name += "`" + typeName.TypeParameterCount; |
|
} |
|
else |
|
{ |
|
name = typeName.Name; |
|
if (!omitGenerics && typeName.TypeParameterCount > 0) |
|
name += "`" + typeName.TypeParameterCount; |
|
} |
|
return Disassembler.DisassemblerHelpers.Escape(name); |
|
} |
|
|
|
internal static readonly TypeProvider minimalCorlibTypeProvider = |
|
new TypeProvider(new SimpleCompilation(MinimalCorlib.Instance)); |
|
|
|
/// <summary> |
|
/// An attribute type provider that can be used to decode attribute signatures |
|
/// that only mention built-in types. |
|
/// </summary> |
|
public static ICustomAttributeTypeProvider<IType> MinimalAttributeTypeProvider { |
|
get => minimalCorlibTypeProvider; |
|
} |
|
|
|
public static ISignatureTypeProvider<IType, TypeSystem.GenericContext> MinimalSignatureTypeProvider { |
|
get => minimalCorlibTypeProvider; |
|
} |
|
|
|
/// <summary> |
|
/// Converts <see cref="KnownTypeCode"/> to <see cref="PrimitiveTypeCode"/>. |
|
/// Returns 0 for known types that are not primitive types (such as <see cref="Span{T}"/>). |
|
/// </summary> |
|
public static PrimitiveTypeCode ToPrimitiveTypeCode(this KnownTypeCode typeCode) |
|
{ |
|
switch (typeCode) |
|
{ |
|
case KnownTypeCode.Object: |
|
return PrimitiveTypeCode.Object; |
|
case KnownTypeCode.Boolean: |
|
return PrimitiveTypeCode.Boolean; |
|
case KnownTypeCode.Char: |
|
return PrimitiveTypeCode.Char; |
|
case KnownTypeCode.SByte: |
|
return PrimitiveTypeCode.SByte; |
|
case KnownTypeCode.Byte: |
|
return PrimitiveTypeCode.Byte; |
|
case KnownTypeCode.Int16: |
|
return PrimitiveTypeCode.Int16; |
|
case KnownTypeCode.UInt16: |
|
return PrimitiveTypeCode.UInt16; |
|
case KnownTypeCode.Int32: |
|
return PrimitiveTypeCode.Int32; |
|
case KnownTypeCode.UInt32: |
|
return PrimitiveTypeCode.UInt32; |
|
case KnownTypeCode.Int64: |
|
return PrimitiveTypeCode.Int64; |
|
case KnownTypeCode.UInt64: |
|
return PrimitiveTypeCode.UInt64; |
|
case KnownTypeCode.Single: |
|
return PrimitiveTypeCode.Single; |
|
case KnownTypeCode.Double: |
|
return PrimitiveTypeCode.Double; |
|
case KnownTypeCode.String: |
|
return PrimitiveTypeCode.String; |
|
case KnownTypeCode.Void: |
|
return PrimitiveTypeCode.Void; |
|
case KnownTypeCode.TypedReference: |
|
return PrimitiveTypeCode.TypedReference; |
|
case KnownTypeCode.IntPtr: |
|
return PrimitiveTypeCode.IntPtr; |
|
case KnownTypeCode.UIntPtr: |
|
return PrimitiveTypeCode.UIntPtr; |
|
default: |
|
return 0; |
|
} |
|
} |
|
|
|
public static KnownTypeCode ToKnownTypeCode(this PrimitiveTypeCode typeCode) |
|
{ |
|
switch (typeCode) |
|
{ |
|
case PrimitiveTypeCode.Boolean: |
|
return KnownTypeCode.Boolean; |
|
case PrimitiveTypeCode.Byte: |
|
return KnownTypeCode.Byte; |
|
case PrimitiveTypeCode.SByte: |
|
return KnownTypeCode.SByte; |
|
case PrimitiveTypeCode.Char: |
|
return KnownTypeCode.Char; |
|
case PrimitiveTypeCode.Int16: |
|
return KnownTypeCode.Int16; |
|
case PrimitiveTypeCode.UInt16: |
|
return KnownTypeCode.UInt16; |
|
case PrimitiveTypeCode.Int32: |
|
return KnownTypeCode.Int32; |
|
case PrimitiveTypeCode.UInt32: |
|
return KnownTypeCode.UInt32; |
|
case PrimitiveTypeCode.Int64: |
|
return KnownTypeCode.Int64; |
|
case PrimitiveTypeCode.UInt64: |
|
return KnownTypeCode.UInt64; |
|
case PrimitiveTypeCode.Single: |
|
return KnownTypeCode.Single; |
|
case PrimitiveTypeCode.Double: |
|
return KnownTypeCode.Double; |
|
case PrimitiveTypeCode.IntPtr: |
|
return KnownTypeCode.IntPtr; |
|
case PrimitiveTypeCode.UIntPtr: |
|
return KnownTypeCode.UIntPtr; |
|
case PrimitiveTypeCode.Object: |
|
return KnownTypeCode.Object; |
|
case PrimitiveTypeCode.String: |
|
return KnownTypeCode.String; |
|
case PrimitiveTypeCode.TypedReference: |
|
return KnownTypeCode.TypedReference; |
|
case PrimitiveTypeCode.Void: |
|
return KnownTypeCode.Void; |
|
default: |
|
return KnownTypeCode.None; |
|
} |
|
} |
|
|
|
public static IEnumerable<ModuleReferenceHandle> GetModuleReferences(this MetadataReader metadata) |
|
{ |
|
var rowCount = metadata.GetTableRowCount(TableIndex.ModuleRef); |
|
for (int row = 1; row <= rowCount; row++) |
|
{ |
|
yield return MetadataTokens.ModuleReferenceHandle(row); |
|
} |
|
} |
|
|
|
public static IEnumerable<TypeSpecificationHandle> GetTypeSpecifications(this MetadataReader metadata) |
|
{ |
|
var rowCount = metadata.GetTableRowCount(TableIndex.TypeSpec); |
|
for (int row = 1; row <= rowCount; row++) |
|
{ |
|
yield return MetadataTokens.TypeSpecificationHandle(row); |
|
} |
|
} |
|
|
|
public static IEnumerable<MethodSpecificationHandle> GetMethodSpecifications(this MetadataReader metadata) |
|
{ |
|
var rowCount = metadata.GetTableRowCount(TableIndex.MethodSpec); |
|
for (int row = 1; row <= rowCount; row++) |
|
{ |
|
yield return MetadataTokens.MethodSpecificationHandle(row); |
|
} |
|
} |
|
|
|
public static IEnumerable<(Handle Handle, MethodSemanticsAttributes Semantics, MethodDefinitionHandle Method, EntityHandle Association)> GetMethodSemantics(this MetadataReader metadata) |
|
{ |
|
int rowCount = metadata.GetTableRowCount(TableIndex.MethodSemantics); |
|
var reader = metadata.AsBlobReader(); |
|
reader.Offset = metadata.GetTableMetadataOffset(TableIndex.MethodSemantics); |
|
|
|
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); |
|
CheckRowSize(metadata, TableIndex.MethodSemantics, 2 + methodSize + assocSize); |
|
for (int rid = 1; rid <= rowCount; rid++) |
|
{ |
|
var semantics = (MethodSemanticsAttributes)reader.ReadUInt16(); |
|
int methodRow = methodSize == 2 ? reader.ReadUInt16() : reader.ReadInt32(); |
|
int assocTag = assocSize == 2 ? reader.ReadUInt16() : reader.ReadInt32(); |
|
EntityHandle propOrEvent; |
|
if ((assocTag & 0x1) == 1) |
|
{ |
|
propOrEvent = MetadataTokens.PropertyDefinitionHandle(assocTag >> 1); |
|
} |
|
else |
|
{ |
|
propOrEvent = MetadataTokens.EventDefinitionHandle(assocTag >> 1); |
|
} |
|
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; |
|
} |
|
|
|
/// <summary> |
|
/// Size in bytes of the FieldList/MethodList/ParamList member-range columns. SRM's rule |
|
/// (MetadataReader.InitializeTableReaders): the column is wide when the *Ptr table itself |
|
/// is large; otherwise the member table's row count governs the width, whether or not the |
|
/// indirection is present. |
|
/// </summary> |
|
static int ListColumnSize(MetadataReader metadata, TableIndex ptrTable, TableIndex memberTable) |
|
{ |
|
return SimpleIndexSize(metadata, ptrTable) > 2 ? 4 : SimpleIndexSize(metadata, memberTable); |
|
} |
|
|
|
/// <summary> |
|
/// Size in bytes of a #Strings heap index column. The width is declared by the HeapSizes |
|
/// flags in the tables-stream header, not derived from the heap's size (a producer may set |
|
/// the large-heap flag over a small heap). SRM does not expose the flags, but the ModuleRef |
|
/// row consists of a single String column, so its computed row size is exactly this width. |
|
/// </summary> |
|
static int StringIndexSize(MetadataReader metadata) |
|
{ |
|
return metadata.GetTableRowSize(TableIndex.ModuleRef); |
|
} |
|
|
|
/// <summary> |
|
/// Size in bytes of a #Blob heap index column (see <see cref="StringIndexSize"/>; the |
|
/// TypeSpec row consists of a single Blob column). |
|
/// </summary> |
|
static int BlobIndexSize(MetadataReader metadata) |
|
{ |
|
return metadata.GetTableRowSize(TableIndex.TypeSpec); |
|
} |
|
|
|
/// <summary> |
|
/// Verifies that the computed column widths sum to the row size SRM derived from the |
|
/// tables-stream header. A width bug reads plausible-looking garbage rather than failing, |
|
/// and some width rules are not reproducible through public API at all (EnC minimal-delta |
|
/// metadata forces every table and coded index to 4 bytes), so fail loudly on mismatch. |
|
/// </summary> |
|
static void CheckRowSize(MetadataReader metadata, TableIndex table, int computedRowSize) |
|
{ |
|
int actualRowSize = metadata.GetTableRowSize(table); |
|
if (computedRowSize != actualRowSize) |
|
throw new BadImageFormatException($"Unexpected {table} row size: computed {computedRowSize}, actual {actualRowSize}."); |
|
} |
|
|
|
public static IEnumerable<(int PackingSize, uint ClassSize, TypeDefinitionHandle Parent)> GetClassLayouts(this MetadataReader metadata) |
|
{ |
|
int rowCount = metadata.GetTableRowCount(TableIndex.ClassLayout); |
|
var reader = metadata.AsBlobReader(); |
|
reader.Offset = metadata.GetTableMetadataOffset(TableIndex.ClassLayout); |
|
int typeDefSize = SimpleIndexSize(metadata, TableIndex.TypeDef); |
|
CheckRowSize(metadata, TableIndex.ClassLayout, 2 + 4 + typeDefSize); |
|
for (int rid = 1; rid <= rowCount; rid++) |
|
{ |
|
ushort packingSize = reader.ReadUInt16(); |
|
uint classSize = reader.ReadUInt32(); |
|
int parentRow = typeDefSize == 2 ? reader.ReadUInt16() : reader.ReadInt32(); |
|
yield return (packingSize, classSize, MetadataTokens.TypeDefinitionHandle(parentRow)); |
|
} |
|
} |
|
|
|
public static IEnumerable<(int Offset, FieldDefinitionHandle Field)> GetFieldLayoutRows(this MetadataReader metadata) |
|
{ |
|
int rowCount = metadata.GetTableRowCount(TableIndex.FieldLayout); |
|
var reader = metadata.AsBlobReader(); |
|
reader.Offset = metadata.GetTableMetadataOffset(TableIndex.FieldLayout); |
|
int fieldSize = SimpleIndexSize(metadata, TableIndex.Field); |
|
CheckRowSize(metadata, TableIndex.FieldLayout, 4 + fieldSize); |
|
for (int rid = 1; rid <= rowCount; rid++) |
|
{ |
|
int offset = reader.ReadInt32(); |
|
int fieldRow = fieldSize == 2 ? reader.ReadUInt16() : reader.ReadInt32(); |
|
yield return (offset, MetadataTokens.FieldDefinitionHandle(fieldRow)); |
|
} |
|
} |
|
|
|
public static IEnumerable<(TypeDefinitionHandle Parent, EventDefinitionHandle EventList)> GetEventMaps(this MetadataReader metadata) |
|
{ |
|
int rowCount = metadata.GetTableRowCount(TableIndex.EventMap); |
|
var reader = metadata.AsBlobReader(); |
|
reader.Offset = metadata.GetTableMetadataOffset(TableIndex.EventMap); |
|
int typeDefSize = SimpleIndexSize(metadata, TableIndex.TypeDef); |
|
int eventListSize = ListColumnSize(metadata, TableIndex.EventPtr, TableIndex.Event); |
|
CheckRowSize(metadata, TableIndex.EventMap, typeDefSize + eventListSize); |
|
for (int rid = 1; rid <= rowCount; rid++) |
|
{ |
|
int parentRow = typeDefSize == 2 ? reader.ReadUInt16() : reader.ReadInt32(); |
|
int eventListRow = eventListSize == 2 ? reader.ReadUInt16() : reader.ReadInt32(); |
|
yield return (MetadataTokens.TypeDefinitionHandle(parentRow), MetadataTokens.EventDefinitionHandle(eventListRow)); |
|
} |
|
} |
|
|
|
public static IEnumerable<(TypeDefinitionHandle Parent, PropertyDefinitionHandle PropertyList)> GetPropertyMaps(this MetadataReader metadata) |
|
{ |
|
int rowCount = metadata.GetTableRowCount(TableIndex.PropertyMap); |
|
var reader = metadata.AsBlobReader(); |
|
reader.Offset = metadata.GetTableMetadataOffset(TableIndex.PropertyMap); |
|
int typeDefSize = SimpleIndexSize(metadata, TableIndex.TypeDef); |
|
int propertyListSize = ListColumnSize(metadata, TableIndex.PropertyPtr, TableIndex.Property); |
|
CheckRowSize(metadata, TableIndex.PropertyMap, typeDefSize + propertyListSize); |
|
for (int rid = 1; rid <= rowCount; rid++) |
|
{ |
|
int parentRow = typeDefSize == 2 ? reader.ReadUInt16() : reader.ReadInt32(); |
|
int propertyListRow = propertyListSize == 2 ? reader.ReadUInt16() : reader.ReadInt32(); |
|
yield return (MetadataTokens.TypeDefinitionHandle(parentRow), MetadataTokens.PropertyDefinitionHandle(propertyListRow)); |
|
} |
|
} |
|
|
|
public static IEnumerable<(TypeDefinitionHandle NestedClass, TypeDefinitionHandle EnclosingClass)> GetNestedClasses(this MetadataReader metadata) |
|
{ |
|
int rowCount = metadata.GetTableRowCount(TableIndex.NestedClass); |
|
var reader = metadata.AsBlobReader(); |
|
reader.Offset = metadata.GetTableMetadataOffset(TableIndex.NestedClass); |
|
int typeDefSize = SimpleIndexSize(metadata, TableIndex.TypeDef); |
|
CheckRowSize(metadata, TableIndex.NestedClass, 2 * typeDefSize); |
|
for (int rid = 1; rid <= rowCount; rid++) |
|
{ |
|
int nestedRow = typeDefSize == 2 ? reader.ReadUInt16() : reader.ReadInt32(); |
|
int enclosingRow = typeDefSize == 2 ? reader.ReadUInt16() : reader.ReadInt32(); |
|
yield return (MetadataTokens.TypeDefinitionHandle(nestedRow), MetadataTokens.TypeDefinitionHandle(enclosingRow)); |
|
} |
|
} |
|
|
|
public static IEnumerable<(int RelativeVirtualAddress, FieldDefinitionHandle Field)> GetFieldRvas(this MetadataReader metadata) |
|
{ |
|
int rowCount = metadata.GetTableRowCount(TableIndex.FieldRva); |
|
var reader = metadata.AsBlobReader(); |
|
reader.Offset = metadata.GetTableMetadataOffset(TableIndex.FieldRva); |
|
int fieldSize = SimpleIndexSize(metadata, TableIndex.Field); |
|
CheckRowSize(metadata, TableIndex.FieldRva, 4 + fieldSize); |
|
for (int rid = 1; rid <= rowCount; rid++) |
|
{ |
|
int rva = reader.ReadInt32(); |
|
int fieldRow = fieldSize == 2 ? reader.ReadUInt16() : reader.ReadInt32(); |
|
yield return (rva, MetadataTokens.FieldDefinitionHandle(fieldRow)); |
|
} |
|
} |
|
|
|
public static IEnumerable<(EntityHandle Parent, BlobHandle NativeType)> GetFieldMarshals(this MetadataReader metadata) |
|
{ |
|
int rowCount = metadata.GetTableRowCount(TableIndex.FieldMarshal); |
|
var reader = metadata.AsBlobReader(); |
|
reader.Offset = metadata.GetTableMetadataOffset(TableIndex.FieldMarshal); |
|
// HasFieldMarshal coded index: 1 tag bit over Field (tag 0) and Param (tag 1). |
|
int parentSize = CodedIndexSize(metadata, 1, TableIndex.Field, TableIndex.Param); |
|
int blobSize = BlobIndexSize(metadata); |
|
CheckRowSize(metadata, TableIndex.FieldMarshal, parentSize + blobSize); |
|
for (int rid = 1; rid <= rowCount; rid++) |
|
{ |
|
int parentTag = parentSize == 2 ? reader.ReadUInt16() : reader.ReadInt32(); |
|
int blobOffset = blobSize == 2 ? reader.ReadUInt16() : reader.ReadInt32(); |
|
EntityHandle parent = (parentTag & 0x1) == 1 |
|
? MetadataTokens.ParameterHandle(parentTag >> 1) |
|
: MetadataTokens.FieldDefinitionHandle(parentTag >> 1); |
|
yield return (parent, MetadataTokens.BlobHandle(blobOffset)); |
|
} |
|
} |
|
|
|
public static IEnumerable<(System.Reflection.MethodImportAttributes MappingFlags, EntityHandle MemberForwarded, StringHandle ImportName, ModuleReferenceHandle ImportScope)> GetImplMaps(this MetadataReader metadata) |
|
{ |
|
int rowCount = metadata.GetTableRowCount(TableIndex.ImplMap); |
|
var reader = metadata.AsBlobReader(); |
|
reader.Offset = metadata.GetTableMetadataOffset(TableIndex.ImplMap); |
|
// MemberForwarded coded index: 1 tag bit over Field (tag 0) and MethodDef (tag 1). |
|
int memberForwardedSize = CodedIndexSize(metadata, 1, TableIndex.Field, TableIndex.MethodDef); |
|
int stringSize = StringIndexSize(metadata); |
|
int moduleRefSize = SimpleIndexSize(metadata, TableIndex.ModuleRef); |
|
CheckRowSize(metadata, TableIndex.ImplMap, 2 + memberForwardedSize + stringSize + moduleRefSize); |
|
for (int rid = 1; rid <= rowCount; rid++) |
|
{ |
|
var mappingFlags = (System.Reflection.MethodImportAttributes)reader.ReadUInt16(); |
|
int memberForwardedTag = memberForwardedSize == 2 ? reader.ReadUInt16() : reader.ReadInt32(); |
|
int importNameOffset = stringSize == 2 ? reader.ReadUInt16() : reader.ReadInt32(); |
|
int importScopeRow = moduleRefSize == 2 ? reader.ReadUInt16() : reader.ReadInt32(); |
|
EntityHandle memberForwarded = (memberForwardedTag & 0x1) == 1 |
|
? MetadataTokens.MethodDefinitionHandle(memberForwardedTag >> 1) |
|
: MetadataTokens.FieldDefinitionHandle(memberForwardedTag >> 1); |
|
yield return (mappingFlags, memberForwarded, MetadataTokens.StringHandle(importNameOffset), MetadataTokens.ModuleReferenceHandle(importScopeRow)); |
|
} |
|
} |
|
|
|
public static IEnumerable<(TypeDefinitionHandle Class, EntityHandle Interface)> GetInterfaceImplRows(this MetadataReader metadata) |
|
{ |
|
int rowCount = metadata.GetTableRowCount(TableIndex.InterfaceImpl); |
|
var reader = metadata.AsBlobReader(); |
|
reader.Offset = metadata.GetTableMetadataOffset(TableIndex.InterfaceImpl); |
|
int typeDefSize = SimpleIndexSize(metadata, TableIndex.TypeDef); |
|
// TypeDefOrRef coded index: 2 tag bits over TypeDef (0), TypeRef (1) and TypeSpec (2). |
|
int interfaceSize = CodedIndexSize(metadata, 2, TableIndex.TypeDef, TableIndex.TypeRef, TableIndex.TypeSpec); |
|
CheckRowSize(metadata, TableIndex.InterfaceImpl, typeDefSize + interfaceSize); |
|
for (int rid = 1; rid <= rowCount; rid++) |
|
{ |
|
int classRow = typeDefSize == 2 ? reader.ReadUInt16() : reader.ReadInt32(); |
|
int interfaceTag = interfaceSize == 2 ? reader.ReadUInt16() : reader.ReadInt32(); |
|
EntityHandle iface = (interfaceTag & 0x3) switch { |
|
0 => MetadataTokens.TypeDefinitionHandle(interfaceTag >> 2), |
|
1 => MetadataTokens.TypeReferenceHandle(interfaceTag >> 2), |
|
_ => MetadataTokens.TypeSpecificationHandle(interfaceTag >> 2), |
|
}; |
|
yield return (MetadataTokens.TypeDefinitionHandle(classRow), iface); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Enumerates one of the five *Ptr indirection tables (EventPtr, FieldPtr, MethodPtr, |
|
/// ParamPtr, PropertyPtr), yielding the referenced entity of each row. |
|
/// </summary> |
|
public static IEnumerable<EntityHandle> GetPtrRows(this MetadataReader metadata, TableIndex ptrTable) |
|
{ |
|
TableIndex referencedTable = ptrTable 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(ptrTable)), |
|
}; |
|
int rowCount = metadata.GetTableRowCount(ptrTable); |
|
var reader = metadata.AsBlobReader(); |
|
reader.Offset = metadata.GetTableMetadataOffset(ptrTable); |
|
int handleSize = SimpleIndexSize(metadata, referencedTable); |
|
CheckRowSize(metadata, ptrTable, handleSize); |
|
for (int rid = 1; rid <= rowCount; rid++) |
|
{ |
|
int row = handleSize == 2 ? reader.ReadUInt16() : reader.ReadInt32(); |
|
yield return MetadataTokens.EntityHandle(((int)referencedTable << 24) | row); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Reads the FieldList/MethodList columns of the TypeDef table, which SRM does not expose: |
|
/// the stored value is the running list position (the next type's first member row, or one |
|
/// past the member table's end), never 0. The columns sit at the end of the row, so they are |
|
/// read relative to the row end to avoid re-deriving the widths of the preceding columns. |
|
/// </summary> |
|
public static IEnumerable<(TypeDefinitionHandle Type, int FieldList, int MethodList)> GetTypeDefListColumns(this MetadataReader metadata) |
|
{ |
|
int fieldListSize = ListColumnSize(metadata, TableIndex.FieldPtr, TableIndex.Field); |
|
int methodListSize = ListColumnSize(metadata, TableIndex.MethodPtr, TableIndex.MethodDef); |
|
// Flags + Name + Namespace + Extends (TypeDefOrRef coded index) precede the list columns. |
|
CheckRowSize(metadata, TableIndex.TypeDef, |
|
4 + 2 * StringIndexSize(metadata) |
|
+ CodedIndexSize(metadata, 2, TableIndex.TypeDef, TableIndex.TypeRef, TableIndex.TypeSpec) |
|
+ fieldListSize + methodListSize); |
|
int rowSize = metadata.GetTableRowSize(TableIndex.TypeDef); |
|
int tableOffset = metadata.GetTableMetadataOffset(TableIndex.TypeDef); |
|
var reader = metadata.AsBlobReader(); |
|
foreach (var handle in metadata.TypeDefinitions) |
|
{ |
|
reader.Offset = tableOffset + rowSize * MetadataTokens.GetRowNumber(handle) - fieldListSize - methodListSize; |
|
int fieldList = fieldListSize == 2 ? reader.ReadUInt16() : reader.ReadInt32(); |
|
int methodList = methodListSize == 2 ? reader.ReadUInt16() : reader.ReadInt32(); |
|
yield return (handle, fieldList, methodList); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Reads the ParamList column of the MethodDef table (see <see cref="GetTypeDefListColumns"/> |
|
/// for why SRM cannot provide it). |
|
/// </summary> |
|
public static IEnumerable<(MethodDefinitionHandle Method, int ParamList)> GetMethodDefParamLists(this MetadataReader metadata) |
|
{ |
|
int paramListSize = ListColumnSize(metadata, TableIndex.ParamPtr, TableIndex.Param); |
|
// RVA + ImplFlags + Flags + Name + Signature precede the ParamList column. |
|
CheckRowSize(metadata, TableIndex.MethodDef, |
|
4 + 2 + 2 + StringIndexSize(metadata) + BlobIndexSize(metadata) + paramListSize); |
|
int rowSize = metadata.GetTableRowSize(TableIndex.MethodDef); |
|
int tableOffset = metadata.GetTableMetadataOffset(TableIndex.MethodDef); |
|
var reader = metadata.AsBlobReader(); |
|
foreach (var handle in metadata.MethodDefinitions) |
|
{ |
|
reader.Offset = tableOffset + rowSize * MetadataTokens.GetRowNumber(handle) - paramListSize; |
|
int paramList = paramListSize == 2 ? reader.ReadUInt16() : reader.ReadInt32(); |
|
yield return (handle, paramList); |
|
} |
|
} |
|
|
|
public static IEnumerable<EntityHandle> GetFieldLayouts(this MetadataReader metadata) |
|
{ |
|
var rowCount = metadata.GetTableRowCount(TableIndex.FieldLayout); |
|
for (int row = 1; row <= rowCount; row++) |
|
{ |
|
yield return MetadataTokens.EntityHandle(TableIndex.FieldLayout, row); |
|
} |
|
} |
|
|
|
public static (int Offset, FieldDefinitionHandle FieldDef) GetFieldLayout(this MetadataReader metadata, EntityHandle fieldLayoutHandle) |
|
{ |
|
var startPointer = metadata.AsReadOnlySpan(); |
|
int offset = metadata.GetTableMetadataOffset(TableIndex.FieldLayout); |
|
int rowSize = metadata.GetTableRowSize(TableIndex.FieldLayout); |
|
int rowCount = metadata.GetTableRowCount(TableIndex.FieldLayout); |
|
|
|
int fieldRowNo = metadata.GetRowNumber(fieldLayoutHandle); |
|
bool small = metadata.GetTableRowCount(TableIndex.Field) <= ushort.MaxValue; |
|
for (int row = rowCount - 1; row >= 0; row--) |
|
{ |
|
ReadOnlySpan<byte> ptr = startPointer.Slice(offset + rowSize * row); |
|
var rowNoSpan = ptr.Slice(4); |
|
uint rowNo = small ? BinaryPrimitives.ReadUInt16LittleEndian(rowNoSpan) : BinaryPrimitives.ReadUInt32LittleEndian(rowNoSpan); |
|
if (fieldRowNo == rowNo) |
|
{ |
|
return (BinaryPrimitives.ReadInt32LittleEndian(ptr), MetadataTokens.FieldDefinitionHandle(fieldRowNo)); |
|
} |
|
} |
|
return (0, default); |
|
} |
|
|
|
public static ReadOnlySpan<byte> AsReadOnlySpan(this MetadataReader metadataReader) |
|
{ |
|
unsafe |
|
{ |
|
return new(metadataReader.MetadataPointer, metadataReader.MetadataLength); |
|
} |
|
} |
|
|
|
public static BlobReader AsBlobReader(this MetadataReader metadataReader) |
|
{ |
|
unsafe |
|
{ |
|
return new(metadataReader.MetadataPointer, metadataReader.MetadataLength); |
|
} |
|
} |
|
|
|
public static uint ReadULEB128(this BinaryReader reader) |
|
{ |
|
uint val = 0; |
|
int shift = 0; |
|
while (true) |
|
{ |
|
byte b = reader.ReadByte(); |
|
val |= (b & 0b0111_1111u) << shift; |
|
if ((b & 0b1000_0000) == 0) |
|
break; |
|
shift += 7; |
|
if (shift >= 35) |
|
throw new OverflowException(); |
|
} |
|
return val; |
|
} |
|
|
|
} |
|
}
|
|
|