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.
498 lines
18 KiB
498 lines
18 KiB
using System; |
|
using System.Collections.Generic; |
|
using System.Collections.Immutable; |
|
using System.Linq; |
|
using System.Reflection.Metadata; |
|
using System.Reflection.Metadata.Ecma335; |
|
using System.Reflection.PortableExecutable; |
|
using System.Text; |
|
using System.Threading.Tasks; |
|
using ICSharpCode.Decompiler.Util; |
|
using SRM = System.Reflection.Metadata; |
|
|
|
namespace ICSharpCode.Decompiler.Metadata |
|
{ |
|
public interface IMetadataResolveContext |
|
{ |
|
PEFile CurrentModule { get; } |
|
PEFile ResolveAssembly(IAssemblyReference reference); |
|
} |
|
|
|
public class SimpleMetadataResolveContext : IMetadataResolveContext |
|
{ |
|
readonly PEFile mainModule; |
|
readonly IAssemblyResolver assemblyResolver; |
|
readonly Dictionary<IAssemblyReference, PEFile> loadedModules; |
|
|
|
public SimpleMetadataResolveContext(PEFile mainModule) |
|
{ |
|
this.mainModule = mainModule; |
|
this.assemblyResolver = mainModule.AssemblyResolver; |
|
this.loadedModules = new Dictionary<IAssemblyReference, PEFile>(); |
|
} |
|
|
|
public SimpleMetadataResolveContext(PEFile mainModule, IMetadataResolveContext parentContext) |
|
{ |
|
this.mainModule = mainModule; |
|
this.assemblyResolver = mainModule.AssemblyResolver; |
|
this.loadedModules = parentContext is SimpleMetadataResolveContext simple ? simple.loadedModules : new Dictionary<IAssemblyReference, PEFile>(); |
|
} |
|
|
|
public PEFile CurrentModule => mainModule; |
|
|
|
public PEFile ResolveAssembly(IAssemblyReference reference) |
|
{ |
|
if (loadedModules.TryGetValue(reference, out var module)) |
|
return module; |
|
var resolved = assemblyResolver.Resolve(reference); |
|
loadedModules.Add(reference, resolved); |
|
return resolved; |
|
} |
|
} |
|
|
|
public static class MetadataResolver |
|
{ |
|
public static TypeDefinition ResolveType(EntityHandle handle, IMetadataResolveContext context) |
|
{ |
|
switch (handle.Kind) { |
|
case HandleKind.TypeDefinition: |
|
return new TypeDefinition(context.CurrentModule, (TypeDefinitionHandle)handle); |
|
case HandleKind.TypeReference: |
|
return Resolve((TypeReferenceHandle)handle, context); |
|
case HandleKind.TypeSpecification: |
|
return Resolve((TypeSpecificationHandle)handle, context); |
|
default: |
|
throw new NotSupportedException(); |
|
} |
|
} |
|
|
|
public static MethodDefinition ResolveAsMethod(EntityHandle handle, IMetadataResolveContext context) |
|
{ |
|
switch (handle.Kind) { |
|
case HandleKind.MethodDefinition: |
|
return new MethodDefinition(context.CurrentModule, (MethodDefinitionHandle)handle); |
|
case HandleKind.MemberReference: |
|
var resolved = ((MemberReferenceHandle)handle).Resolve(context); |
|
if (resolved is MethodDefinition m) |
|
return m; |
|
return default; |
|
case HandleKind.MethodSpecification: |
|
resolved = ((MethodSpecificationHandle)handle).Resolve(context); |
|
if (resolved is MethodDefinition m2) |
|
return m2; |
|
return default; |
|
} |
|
throw new NotImplementedException(); |
|
} |
|
|
|
public static FieldDefinition ResolveAsField(EntityHandle handle, IMetadataResolveContext context) |
|
{ |
|
switch (handle.Kind) { |
|
case HandleKind.FieldDefinition: |
|
return new FieldDefinition(context.CurrentModule, (FieldDefinitionHandle)handle); |
|
case HandleKind.MemberReference: |
|
var resolved = ((MemberReferenceHandle)handle).Resolve(context); |
|
if (resolved is FieldDefinition m) |
|
return m; |
|
return default; |
|
} |
|
throw new NotImplementedException(); |
|
} |
|
|
|
/// <summary> |
|
/// Implements resolving of TypeReferences to TypeDefinitions as decribed in II.7.3 of ECMA-335 6th edition. |
|
/// </summary> |
|
public static TypeDefinition Resolve(this TypeReferenceHandle handle, IMetadataResolveContext context) |
|
{ |
|
var metadata = context.CurrentModule.Metadata; |
|
var tr = metadata.GetTypeReference(handle); |
|
if (tr.ResolutionScope.IsNil) { |
|
foreach (var h in metadata.ExportedTypes) { |
|
var exportedType = metadata.GetExportedType(h); |
|
if (exportedType.Name == tr.Name && exportedType.Namespace == tr.Namespace) { |
|
switch (exportedType.Implementation.Kind) { |
|
case HandleKind.AssemblyFile: |
|
throw new NotSupportedException(); |
|
case HandleKind.AssemblyReference: |
|
return ResolveTypeInOtherAssembly((AssemblyReferenceHandle)exportedType.Implementation, metadata.GetString(tr.Namespace), metadata.GetString(tr.Name)); |
|
default: |
|
throw new NotSupportedException(); |
|
} |
|
} |
|
} |
|
} |
|
switch (tr.ResolutionScope.Kind) { |
|
case HandleKind.TypeReference: |
|
var outerType = Resolve((TypeReferenceHandle)tr.ResolutionScope, context); |
|
if (outerType == null) |
|
throw new NotSupportedException(); |
|
var td = outerType.Module.Metadata.GetTypeDefinition(outerType.Handle); |
|
var name = metadata.GetString(tr.Name); |
|
foreach (var nestedType in td.GetNestedTypes()) { |
|
var nestedTypeDef = outerType.Module.Metadata.GetTypeDefinition(nestedType); |
|
if (outerType.Module.Metadata.GetString(nestedTypeDef.Name) == name) |
|
return new TypeDefinition(outerType.Module, nestedType); |
|
} |
|
break; |
|
case HandleKind.ModuleReference: |
|
break; |
|
case HandleKind.AssemblyReference: |
|
return ResolveTypeInOtherAssembly((AssemblyReferenceHandle)tr.ResolutionScope, metadata.GetString(tr.Namespace), metadata.GetString(tr.Name)); |
|
} |
|
throw new NotSupportedException(); |
|
|
|
TypeDefinition ResolveTypeInOtherAssembly(AssemblyReferenceHandle asm, string ns, string typeName) |
|
{ |
|
var module = context.ResolveAssembly(new AssemblyReference(context.CurrentModule, (AssemblyReferenceHandle)tr.ResolutionScope)); |
|
var moduleMetadata = module.Metadata; |
|
var @namespace = ResolveNamespace(moduleMetadata, ns.Split('.')); |
|
if (@namespace == null) |
|
throw new NotSupportedException(); |
|
var type = FindTypeInNamespace(moduleMetadata, @namespace.Value, typeName); |
|
if (type.IsNil) |
|
throw new NotSupportedException(); |
|
return new TypeDefinition(module, type); |
|
} |
|
} |
|
|
|
static NamespaceDefinition? ResolveNamespace(MetadataReader metadata, string[] namespaceParts) |
|
{ |
|
var currentNamespace = metadata.GetNamespaceDefinitionRoot(); |
|
for (int i = 0; i < namespaceParts.Length; i++) { |
|
string identifier = namespaceParts[i]; |
|
var next = currentNamespace.NamespaceDefinitions.FirstOrDefault(ns => metadata.StringComparer.Equals(ns, identifier)); |
|
if (next.IsNil) |
|
return null; |
|
currentNamespace = metadata.GetNamespaceDefinition(next); |
|
} |
|
return currentNamespace; |
|
} |
|
|
|
static TypeDefinitionHandle FindTypeInNamespace(MetadataReader metadata, NamespaceDefinition @namespace, string name) |
|
{ |
|
foreach (var type in @namespace.TypeDefinitions) { |
|
if (metadata.StringComparer.Equals(metadata.GetTypeDefinition(type).Name, name)) |
|
return type; |
|
} |
|
return default; |
|
} |
|
|
|
public static IMetadataEntity Resolve(this MemberReferenceHandle handle, IMetadataResolveContext context) |
|
{ |
|
var metadata = context.CurrentModule.Metadata; |
|
var mr = metadata.GetMemberReference(handle); |
|
SRM.TypeDefinition declaringType; |
|
MetadataReader targetMetadata; |
|
PEFile targetModule; |
|
switch (mr.Parent.Kind) { |
|
case HandleKind.TypeDefinition: |
|
declaringType = metadata.GetTypeDefinition((TypeDefinitionHandle)mr.Parent); |
|
targetMetadata = metadata; |
|
targetModule = context.CurrentModule; |
|
break; |
|
case HandleKind.TypeReference: |
|
var resolvedTypeReference = Resolve((TypeReferenceHandle)mr.Parent, context); |
|
targetModule = resolvedTypeReference.Module; |
|
targetMetadata = targetModule.Metadata; |
|
declaringType = targetMetadata.GetTypeDefinition(resolvedTypeReference.Handle); |
|
break; |
|
case HandleKind.TypeSpecification: |
|
resolvedTypeReference = Resolve((TypeSpecificationHandle)mr.Parent, context); |
|
targetModule = resolvedTypeReference.Module; |
|
targetMetadata = targetModule.Metadata; |
|
declaringType = targetMetadata.GetTypeDefinition(resolvedTypeReference.Handle); |
|
break; |
|
case HandleKind.MethodDefinition: |
|
case HandleKind.ModuleReference: |
|
throw new NotImplementedException(); |
|
default: |
|
throw new NotSupportedException(); |
|
} |
|
var name = metadata.GetString(mr.Name); |
|
switch (mr.GetKind()) { |
|
case MemberReferenceKind.Field: |
|
foreach (var f in declaringType.GetFields()) { |
|
var fd = targetMetadata.GetFieldDefinition(f); |
|
if (targetMetadata.StringComparer.Equals(fd.Name, name)) |
|
return new FieldDefinition(targetModule, f); |
|
} |
|
throw new NotSupportedException(); |
|
case MemberReferenceKind.Method: |
|
var candidates = new List<(MethodDefinitionHandle, BlobHandle)>(); |
|
foreach (var m in declaringType.GetMethods()) { |
|
var md = targetMetadata.GetMethodDefinition(m); |
|
if (targetMetadata.StringComparer.Equals(md.Name, name)) |
|
candidates.Add((m, md.Signature)); |
|
} |
|
if (candidates.Count == 0) |
|
throw new NotSupportedException(); |
|
foreach (var (method, signature) in candidates) { |
|
if (SignatureBlobComparer.EqualsMethodSignature(targetMetadata.GetBlobReader(signature), metadata.GetBlobReader(mr.Signature), targetMetadata, metadata)) |
|
return new MethodDefinition(targetModule, method); |
|
} |
|
throw new NotSupportedException(); |
|
} |
|
throw new NotSupportedException(); |
|
} |
|
|
|
public static TypeDefinition Resolve(this TypeSpecificationHandle handle, IMetadataResolveContext context) |
|
{ |
|
var metadata = context.CurrentModule.Metadata; |
|
var ts = metadata.GetTypeSpecification(handle); |
|
var unspecialized = ts.DecodeSignature(new Unspecializer(), default); |
|
switch (unspecialized.Kind) { |
|
case HandleKind.TypeDefinition: |
|
return new TypeDefinition(context.CurrentModule, (TypeDefinitionHandle)unspecialized); |
|
case HandleKind.TypeReference: |
|
return Resolve((TypeReferenceHandle)unspecialized, context); |
|
default: |
|
throw new NotImplementedException(); |
|
} |
|
} |
|
|
|
public static MethodDefinition Resolve(this MethodSpecificationHandle handle, IMetadataResolveContext context) |
|
{ |
|
var metadata = context.CurrentModule.Metadata; |
|
var ms = metadata.GetMethodSpecification(handle); |
|
return ResolveAsMethod(ms.Method, context); |
|
} |
|
|
|
class Unspecializer : ISignatureTypeProvider<EntityHandle, Unit> |
|
{ |
|
public EntityHandle GetArrayType(EntityHandle elementType, ArrayShape shape) |
|
{ |
|
return elementType; |
|
} |
|
|
|
public EntityHandle GetByReferenceType(EntityHandle elementType) |
|
{ |
|
return elementType; |
|
} |
|
|
|
public EntityHandle GetFunctionPointerType(MethodSignature<EntityHandle> signature) |
|
{ |
|
return MetadataTokens.EntityHandle(0); |
|
} |
|
|
|
public EntityHandle GetGenericInstantiation(EntityHandle genericType, ImmutableArray<EntityHandle> typeArguments) |
|
{ |
|
return genericType; |
|
} |
|
|
|
public EntityHandle GetGenericMethodParameter(Unit genericContext, int index) |
|
{ |
|
return MetadataTokens.EntityHandle(0); |
|
} |
|
|
|
public EntityHandle GetGenericTypeParameter(Unit genericContext, int index) |
|
{ |
|
return MetadataTokens.EntityHandle(0); |
|
} |
|
|
|
public EntityHandle GetModifiedType(EntityHandle modifier, EntityHandle unmodifiedType, bool isRequired) |
|
{ |
|
return unmodifiedType; |
|
} |
|
|
|
public EntityHandle GetPinnedType(EntityHandle elementType) |
|
{ |
|
return elementType; |
|
} |
|
|
|
public EntityHandle GetPointerType(EntityHandle elementType) |
|
{ |
|
return elementType; |
|
} |
|
|
|
public EntityHandle GetPrimitiveType(PrimitiveTypeCode typeCode) |
|
{ |
|
return MetadataTokens.EntityHandle(0); |
|
} |
|
|
|
public EntityHandle GetSZArrayType(EntityHandle elementType) |
|
{ |
|
return MetadataTokens.EntityHandle(0); |
|
} |
|
|
|
public EntityHandle GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) |
|
{ |
|
return handle; |
|
} |
|
|
|
public EntityHandle GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) |
|
{ |
|
return handle; |
|
} |
|
|
|
public EntityHandle GetTypeFromSpecification(MetadataReader reader, Unit genericContext, TypeSpecificationHandle handle, byte rawTypeKind) |
|
{ |
|
return reader.GetTypeSpecification(handle).DecodeSignature(this, genericContext); |
|
} |
|
} |
|
} |
|
|
|
public static class SignatureBlobComparer |
|
{ |
|
public static bool EqualsMethodSignature(BlobReader a, BlobReader b, MetadataReader contextForA, MetadataReader contextForB) |
|
{ |
|
return EqualsMethodSignature(ref a, ref b, contextForA, contextForB); |
|
} |
|
|
|
static bool EqualsMethodSignature(ref BlobReader a, ref BlobReader b, MetadataReader contextForA, MetadataReader contextForB) |
|
{ |
|
SignatureHeader header; |
|
// compare signature headers |
|
if (a.RemainingBytes == 0 || b.RemainingBytes == 0 || (header = a.ReadSignatureHeader()) != b.ReadSignatureHeader()) |
|
return false; |
|
if (header.IsGeneric) { |
|
// read & compare generic parameter count |
|
if (!IsSameCompressedInteger(ref a, ref b, out _)) |
|
return false; |
|
} |
|
// read & compare parameter count |
|
if (!IsSameCompressedInteger(ref a, ref b, out int totalParameterCount)) |
|
return false; |
|
if (!IsSameCompressedInteger(ref a, ref b, out int typeCode)) |
|
return false; |
|
if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode)) |
|
return false; |
|
int i = 0; |
|
for (; i < totalParameterCount; i++) { |
|
if (!IsSameCompressedInteger(ref a, ref b, out typeCode)) |
|
return false; |
|
// |
|
if (typeCode == 65) |
|
break; |
|
if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode)) |
|
return false; |
|
} |
|
for (; i < totalParameterCount; i++) { |
|
if (!IsSameCompressedInteger(ref a, ref b, out typeCode)) |
|
return false; |
|
if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode)) |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
static bool IsSameCompressedInteger(ref BlobReader a, ref BlobReader b, out int value) |
|
{ |
|
return a.TryReadCompressedInteger(out value) && b.TryReadCompressedInteger(out int otherValue) && value == otherValue; |
|
} |
|
|
|
static bool IsSameCompressedSignedInteger(ref BlobReader a, ref BlobReader b, out int value) |
|
{ |
|
return a.TryReadCompressedSignedInteger(out value) && b.TryReadCompressedSignedInteger(out int otherValue) && value == otherValue; |
|
} |
|
|
|
static bool TypesAreEqual(ref BlobReader a, ref BlobReader b, MetadataReader contextForA, MetadataReader contextForB, int typeCode) |
|
{ |
|
switch (typeCode) { |
|
case 0x1: // ELEMENT_TYPE_VOID |
|
case 0x2: // ELEMENT_TYPE_BOOLEAN |
|
case 0x3: // ELEMENT_TYPE_CHAR |
|
case 0x4: // ELEMENT_TYPE_I1 |
|
case 0x5: // ELEMENT_TYPE_U1 |
|
case 0x6: // ELEMENT_TYPE_I2 |
|
case 0x7: // ELEMENT_TYPE_U2 |
|
case 0x8: // ELEMENT_TYPE_I4 |
|
case 0x9: // ELEMENT_TYPE_U4 |
|
case 0xA: // ELEMENT_TYPE_I8 |
|
case 0xB: // ELEMENT_TYPE_U8 |
|
case 0xC: // ELEMENT_TYPE_R4 |
|
case 0xD: // ELEMENT_TYPE_R8 |
|
case 0xE: // ELEMENT_TYPE_STRING |
|
case 0x16: // ELEMENT_TYPE_TYPEDBYREF |
|
case 0x18: // ELEMENT_TYPE_I |
|
case 0x19: // ELEMENT_TYPE_U |
|
case 0x1C: // ELEMENT_TYPE_OBJECT |
|
return true; |
|
case 0xF: // ELEMENT_TYPE_PTR |
|
case 0x10: // ELEMENT_TYPE_BYREF |
|
case 0x45: // ELEMENT_TYPE_PINNED |
|
case 0x1D: // ELEMENT_TYPE_SZARRAY |
|
if (!IsSameCompressedInteger(ref a, ref b, out typeCode)) |
|
return false; |
|
if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode)) |
|
return false; |
|
return true; |
|
case 0x1B: // ELEMENT_TYPE_FNPTR |
|
if (!EqualsMethodSignature(ref a, ref b, contextForA, contextForB)) |
|
return false; |
|
return true; |
|
case 0x14: // ELEMENT_TYPE_ARRAY |
|
// element type |
|
if (!IsSameCompressedInteger(ref a, ref b, out typeCode)) |
|
return false; |
|
if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode)) |
|
return false; |
|
// rank |
|
if (!IsSameCompressedInteger(ref a, ref b, out _)) |
|
return false; |
|
// sizes |
|
if (!IsSameCompressedInteger(ref a, ref b, out int numOfSizes)) |
|
return false; |
|
for (int i = 0; i < numOfSizes; i++) { |
|
if (!IsSameCompressedInteger(ref a, ref b, out _)) |
|
return false; |
|
} |
|
// lower bounds |
|
if (!IsSameCompressedInteger(ref a, ref b, out int numOfLowerBounds)) |
|
return false; |
|
for (int i = 0; i < numOfLowerBounds; i++) { |
|
if (!IsSameCompressedSignedInteger(ref a, ref b, out _)) |
|
return false; |
|
} |
|
return true; |
|
case 0x1F: // ELEMENT_TYPE_CMOD_REQD |
|
case 0x20: // ELEMENT_TYPE_CMOD_OPT |
|
// modifier |
|
if (!TypeHandleEquals(ref a, ref b, contextForA, contextForB)) |
|
return false; |
|
// unmodified type |
|
if (!IsSameCompressedInteger(ref a, ref b, out typeCode)) |
|
return false; |
|
if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode)) |
|
return false; |
|
return true; |
|
case 0x15: // ELEMENT_TYPE_GENERICINST |
|
// generic type |
|
if (!IsSameCompressedInteger(ref a, ref b, out typeCode)) |
|
return false; |
|
if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode)) |
|
return false; |
|
if (!IsSameCompressedInteger(ref a, ref b, out int numOfArguments)) |
|
return false; |
|
for (int i = 0; i < numOfArguments; i++) { |
|
if (!IsSameCompressedInteger(ref a, ref b, out typeCode)) |
|
return false; |
|
if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode)) |
|
return false; |
|
} |
|
return true; |
|
case 0x13: // ELEMENT_TYPE_VAR |
|
case 0x1E: // ELEMENT_TYPE_MVAR |
|
// index |
|
if (!IsSameCompressedInteger(ref a, ref b, out _)) |
|
return false; |
|
return true; |
|
case 0x11: // ELEMENT_TYPE_VALUETYPE |
|
case 0x12: // ELEMENT_TYPE_CLASS |
|
if (!TypeHandleEquals(ref a, ref b, contextForA, contextForB)) |
|
return false; |
|
return true; |
|
default: |
|
return false; |
|
} |
|
} |
|
|
|
static bool TypeHandleEquals(ref BlobReader a, ref BlobReader b, MetadataReader contextForA, MetadataReader contextForB) |
|
{ |
|
var typeA = a.ReadTypeHandle(); |
|
var typeB = b.ReadTypeHandle(); |
|
if (typeA.IsNil || typeB.IsNil) |
|
return false; |
|
return typeA.GetFullTypeName(contextForA) == typeB.GetFullTypeName(contextForB); |
|
} |
|
} |
|
}
|
|
|