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.
424 lines
15 KiB
424 lines
15 KiB
using System; |
|
using System.Collections.Generic; |
|
using System.Collections.Immutable; |
|
using System.Linq; |
|
using System.Reflection; |
|
using SRM = System.Reflection.Metadata; |
|
using System.Reflection.Metadata; |
|
using System.Reflection.Metadata.Ecma335; |
|
using System.Reflection.PortableExecutable; |
|
using System.Security.Cryptography; |
|
using System.Text; |
|
using System.Threading.Tasks; |
|
using ICSharpCode.Decompiler.TypeSystem; |
|
using ICSharpCode.Decompiler.TypeSystem.Implementation; |
|
using ICSharpCode.Decompiler.Util; |
|
|
|
namespace ICSharpCode.Decompiler.Metadata |
|
{ |
|
public static class MetadataExtensions |
|
{ |
|
#region Resolver |
|
public static TypeDefinition ResolveAsType(this EntityHandle entity, PEFile module) |
|
{ |
|
return new Entity(module, entity).ResolveAsType(); |
|
} |
|
|
|
public static FieldDefinition ResolveAsField(this EntityHandle entity, PEFile module) |
|
{ |
|
return new Entity(module, entity).ResolveAsField(); |
|
} |
|
|
|
public static MethodDefinition ResolveAsMethod(this EntityHandle entity, PEFile module) |
|
{ |
|
return new Entity(module, entity).ResolveAsMethod(); |
|
} |
|
#endregion |
|
|
|
public static MethodDefinition AsMethod(this IMetadataEntity entity) |
|
{ |
|
if (entity is MethodDefinition method) |
|
return method; |
|
if (entity is Entity e) |
|
return e; |
|
throw new NotSupportedException(); |
|
} |
|
|
|
public static bool IsNil(this IAssemblyReference reference) |
|
{ |
|
return reference == null || (reference is Metadata.AssemblyReference ar && ar.IsNil); |
|
} |
|
|
|
public static string GetFullAssemblyName(this MetadataReader reader) |
|
{ |
|
if (!reader.IsAssembly) |
|
return string.Empty; |
|
var asm = reader.GetAssemblyDefinition(); |
|
string publicKey = "null"; |
|
if (!asm.PublicKey.IsNil) { |
|
SHA1 sha1 = SHA1.Create(); |
|
var publicKeyTokenBytes = sha1.ComputeHash(reader.GetBlobBytes(asm.PublicKey)).Skip(12).ToArray(); |
|
publicKey = publicKeyTokenBytes.ToHexString(); |
|
} |
|
return $"{reader.GetString(asm.Name)}, Version={asm.Version}, Culture={(asm.Culture.IsNil ? "neutral" : reader.GetString(asm.Culture))}, PublicKeyToken={publicKey}"; |
|
} |
|
|
|
public static string GetFullAssemblyName(this SRM.AssemblyReference reference, MetadataReader reader) |
|
{ |
|
string publicKey = "null"; |
|
if (!reference.PublicKeyOrToken.IsNil) { |
|
byte[] publicKeyTokenBytes = reader.GetBlobBytes(reference.PublicKeyOrToken); |
|
if ((reference.Flags & AssemblyFlags.PublicKey) != 0) { |
|
SHA1 sha1 = SHA1.Create(); |
|
publicKeyTokenBytes = sha1.ComputeHash(publicKeyTokenBytes).Skip(12).ToArray(); |
|
} |
|
publicKey = publicKeyTokenBytes.ToHexString(); |
|
} |
|
string properties = ""; |
|
if ((reference.Flags & AssemblyFlags.Retargetable) != 0) |
|
properties = ", Retargetable=true"; |
|
return $"{reader.GetString(reference.Name)}, Version={reference.Version}, Culture={(reference.Culture.IsNil ? "neutral" : reader.GetString(reference.Culture))}, PublicKeyToken={publicKey}{properties}"; |
|
} |
|
|
|
static string ToHexString(this byte[] bytes) |
|
{ |
|
StringBuilder sb = new StringBuilder(bytes.Length * 2); |
|
foreach (var b in bytes) |
|
sb.AppendFormat("{0:x2}", b); |
|
return sb.ToString(); |
|
} |
|
|
|
public static IEnumerable<TypeDefinitionHandle> GetTopLevelTypeDefinitions(this MetadataReader reader) |
|
{ |
|
unsafe HashSet<uint> GetNestedTypes() |
|
{ |
|
byte* startPointer = reader.MetadataPointer; |
|
int offset = reader.GetTableMetadataOffset(TableIndex.NestedClass); |
|
int rowSize = reader.GetTableRowSize(TableIndex.NestedClass); |
|
int rowCount = reader.GetTableRowCount(TableIndex.NestedClass); |
|
var typeDefSize = reader.GetReferenceSize(TableIndex.TypeDef); |
|
|
|
var set = new HashSet<uint>(); |
|
|
|
for (int row = 0; row < rowCount; row++) { |
|
byte* ptr = startPointer + offset + rowSize * row; |
|
uint currentTypeRow = typeDefSize == 2 ? *(ushort*)ptr : *(uint*)ptr; |
|
set.Add(currentTypeRow); |
|
} |
|
|
|
return set; |
|
} |
|
|
|
HashSet<uint> nestedTypes = GetNestedTypes(); |
|
|
|
foreach (var handle in reader.TypeDefinitions) { |
|
if (!nestedTypes.Contains((uint)reader.GetRowNumber(handle))) |
|
yield return handle; |
|
} |
|
} |
|
|
|
public static string ToILNameString(this FullTypeName typeName) |
|
{ |
|
string escapedName; |
|
if (typeName.IsNested) { |
|
escapedName = Disassembler.DisassemblerHelpers.Escape(typeName.Name); |
|
if (typeName.TypeParameterCount > 0) |
|
escapedName += "`" + typeName.TypeParameterCount; |
|
return $"{typeName.GetDeclaringType().ToILNameString()}/{escapedName}"; |
|
} else if (!string.IsNullOrEmpty(typeName.TopLevelTypeName.Namespace)) { |
|
escapedName = Disassembler.DisassemblerHelpers.Escape($"{typeName.TopLevelTypeName.Namespace}.{typeName.Name}"); |
|
if (typeName.TypeParameterCount > 0) |
|
escapedName += "`" + typeName.TypeParameterCount; |
|
} else { |
|
escapedName = Disassembler.DisassemblerHelpers.Escape(typeName.Name); |
|
} |
|
return escapedName; |
|
} |
|
|
|
public static AssemblyReferenceHandle GetDeclaringAssembly(this TypeReferenceHandle handle, MetadataReader reader) |
|
{ |
|
var tr = reader.GetTypeReference(handle); |
|
switch (tr.ResolutionScope.Kind) { |
|
case HandleKind.TypeReference: |
|
return ((TypeReferenceHandle)tr.ResolutionScope).GetDeclaringAssembly(reader); |
|
case HandleKind.AssemblyReference: |
|
return (AssemblyReferenceHandle)tr.ResolutionScope; |
|
default: |
|
return default; |
|
} |
|
} |
|
|
|
public static bool IsConstructor(this SRM.MethodDefinition methodDefinition, MetadataReader reader) |
|
{ |
|
string name = reader.GetString(methodDefinition.Name); |
|
return (methodDefinition.Attributes & (MethodAttributes.RTSpecialName | MethodAttributes.SpecialName)) != 0 |
|
&& (name == ".cctor" || name == ".ctor"); |
|
} |
|
|
|
public static bool HasMatchingDefaultMemberAttribute(this PropertyDefinitionHandle handle, PEFile module, out CustomAttributeHandle defaultMemberAttribute) |
|
{ |
|
defaultMemberAttribute = default(CustomAttributeHandle); |
|
var metadata = module.Metadata; |
|
var propertyDefinition = metadata.GetPropertyDefinition(handle); |
|
var accessorHandle = propertyDefinition.GetAccessors().GetAny(); |
|
var accessor = metadata.GetMethodDefinition(accessorHandle); |
|
if (accessor.GetParameters().Count > 0) { |
|
var basePropDef = propertyDefinition; |
|
var firstOverrideHandle = accessorHandle.GetMethodImplementations(metadata).FirstOrDefault(); |
|
if (!firstOverrideHandle.IsNil) { |
|
// if the property is explicitly implementing an interface, look up the property in the interface: |
|
var firstOverride = metadata.GetMethodImplementation(firstOverrideHandle); |
|
var baseAccessor = new Metadata.Entity(module, firstOverride.MethodDeclaration).ResolveAsMethod(); |
|
if (!baseAccessor.IsNil) { |
|
var declaringType = metadata.GetTypeDefinition(metadata.GetMethodDefinition(baseAccessor.Handle).GetDeclaringType()); |
|
foreach (var basePropHandle in declaringType.GetProperties()) { |
|
var baseProp = metadata.GetPropertyDefinition(basePropHandle); |
|
var accessors = baseProp.GetAccessors(); |
|
if (accessors.Getter == baseAccessor.Handle || accessors.Setter == baseAccessor.Handle) { |
|
basePropDef = baseProp; |
|
break; |
|
} |
|
} |
|
} else |
|
return false; |
|
} |
|
var defaultMemberName = accessor.GetDeclaringType().GetDefaultMemberName(metadata, out var attr); |
|
if (defaultMemberName == metadata.GetString(basePropDef.Name)) { |
|
defaultMemberAttribute = attr; |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
public static string GetDefaultMemberName(this TypeDefinitionHandle type, MetadataReader reader) |
|
{ |
|
return type.GetDefaultMemberName(reader, out var attr); |
|
} |
|
|
|
static readonly ITypeResolveContext minimalCorlibContext = new SimpleTypeResolveContext(MinimalCorlib.Instance.CreateCompilation()); |
|
|
|
public static string GetDefaultMemberName(this TypeDefinitionHandle type, MetadataReader reader, out CustomAttributeHandle defaultMemberAttribute) |
|
{ |
|
var td = reader.GetTypeDefinition(type); |
|
|
|
foreach (var h in td.GetCustomAttributes()) { |
|
var ca = reader.GetCustomAttribute(h); |
|
if (ca.GetAttributeType(reader).ToString() == "System.Reflection.DefaultMemberAttribute") { |
|
var decodedValues = ca.DecodeValue(new TypeSystemAttributeTypeProvider(minimalCorlibContext)); |
|
if (decodedValues.FixedArguments.Length == 1 && decodedValues.FixedArguments[0].Value is string value) { |
|
defaultMemberAttribute = h; |
|
return value; |
|
} |
|
} |
|
} |
|
|
|
defaultMemberAttribute = default(CustomAttributeHandle); |
|
return null; |
|
} |
|
|
|
public static bool HasOverrides(this MethodDefinitionHandle handle, MetadataReader reader) |
|
{ |
|
for (int row = 1; row <= reader.GetTableRowCount(TableIndex.MethodImpl); row++) { |
|
var impl = reader.GetMethodImplementation(MetadataTokens.MethodImplementationHandle(row)); |
|
if (impl.MethodBody == handle) return true; |
|
} |
|
return false; |
|
} |
|
|
|
public static bool HasParameters(this PropertyDefinitionHandle handle, MetadataReader reader) |
|
{ |
|
var a = reader.GetPropertyDefinition(handle).GetAccessors(); |
|
if (!a.Getter.IsNil) { |
|
var m = reader.GetMethodDefinition(a.Getter); |
|
return m.GetParameters().Count > 0; |
|
} |
|
if (!a.Setter.IsNil) { |
|
var m = reader.GetMethodDefinition(a.Setter); |
|
return m.GetParameters().Count > 1; |
|
} |
|
return false; |
|
} |
|
|
|
public static PrimitiveTypeCode ToPrimtiveTypeCode(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: |
|
throw new NotSupportedException(); |
|
} |
|
} |
|
|
|
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 bool IsSmallReference(this MetadataReader reader, TableIndex table) |
|
{ |
|
// TODO detect whether #JTD is present (EnC) |
|
return reader.GetTableRowCount(table) <= ushort.MaxValue; |
|
} |
|
|
|
public static int GetReferenceSize(this MetadataReader reader, TableIndex table) |
|
{ |
|
return IsSmallReference(reader, table) ? 2 : 4; |
|
} |
|
|
|
public static unsafe (int startRow, int endRow) BinarySearchRange(this MetadataReader reader, TableIndex table, int valueOffset, uint referenceValue, bool small) |
|
{ |
|
int offset = reader.GetTableMetadataOffset(table); |
|
int rowSize = reader.GetTableRowSize(table); |
|
int rowCount = reader.GetTableRowCount(table); |
|
int tableLength = rowSize * rowCount; |
|
byte* startPointer = reader.MetadataPointer; |
|
|
|
int result = BinarySearch(); |
|
if (result == -1) |
|
return (-1, -1); |
|
|
|
int start = result; |
|
|
|
while (start > 0 && GetValue(start - 1) == referenceValue) |
|
start--; |
|
|
|
int end = result; |
|
|
|
while (end + 1 < tableLength && GetValue(end + 1) == referenceValue) |
|
end++; |
|
|
|
return (start, end); |
|
|
|
uint GetValue(int row) |
|
{ |
|
if (small) |
|
return *(ushort*)(startPointer + offset + row * rowSize + valueOffset); |
|
else |
|
return *(uint*)(startPointer + offset + row * rowSize + valueOffset); |
|
} |
|
|
|
int BinarySearch() |
|
{ |
|
int startRow = 0; |
|
int endRow = rowCount - 1; |
|
while (startRow <= endRow) { |
|
int row = (startRow + endRow) / 2; |
|
uint currentValue = GetValue(row); |
|
if (referenceValue > currentValue) { |
|
startRow = row + 1; |
|
} else if (referenceValue < currentValue) { |
|
endRow = row - 1; |
|
} else { |
|
return row; |
|
} |
|
} |
|
return -1; |
|
} |
|
} |
|
|
|
public static AssemblyDefinition? GetAssemblyDefinition(this PEReader reader) |
|
{ |
|
var metadata = reader.GetMetadataReader(); |
|
if (metadata.IsAssembly) |
|
return metadata.GetAssemblyDefinition(); |
|
return null; |
|
} |
|
|
|
public unsafe static ParameterHandle At(this ParameterHandleCollection collection, MetadataReader metadata, int index) |
|
{ |
|
if (metadata.GetTableRowCount(TableIndex.ParamPtr) > 0) { |
|
int rowSize = metadata.GetTableRowSize(TableIndex.ParamPtr); |
|
int paramRefSize = (metadata.GetReferenceSize(TableIndex.ParamPtr) > 2) ? 4 : metadata.GetReferenceSize(TableIndex.Param); |
|
int offset = metadata.GetTableMetadataOffset(TableIndex.ParamPtr) + index * rowSize; |
|
byte* ptr = metadata.MetadataPointer + offset; |
|
if (paramRefSize == 2) |
|
return MetadataTokens.ParameterHandle(*(ushort*)ptr); |
|
return MetadataTokens.ParameterHandle((int)*(uint*)ptr); |
|
} |
|
return MetadataTokens.ParameterHandle((index + 1) & 0xFFFFFF); |
|
} |
|
|
|
|
|
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); |
|
} |
|
} |
|
} |
|
}
|
|
|