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.
371 lines
13 KiB
371 lines
13 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.Collections.Generic; |
|
using System.Collections.Immutable; |
|
using System.Diagnostics; |
|
using System.Linq; |
|
using System.Reflection.Metadata; |
|
using System.Reflection.Metadata.Ecma335; |
|
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) |
|
{ |
|
if (handle.IsNil) |
|
return default; |
|
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) |
|
{ |
|
if (handle.IsNil) |
|
return default; |
|
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; |
|
default: |
|
throw new NotSupportedException(); |
|
} |
|
} |
|
|
|
public static FieldDefinition ResolveAsField(EntityHandle handle, IMetadataResolveContext context) |
|
{ |
|
if (handle.IsNil) |
|
return default; |
|
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; |
|
default: |
|
throw new NotSupportedException(); |
|
} |
|
} |
|
|
|
/// <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) |
|
{ |
|
if (handle.IsNil) |
|
return default; |
|
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, asm)); |
|
if (module == null) |
|
return default; |
|
var @namespace = ResolveNamespace(module.Metadata, ns); |
|
Debug.Assert(@namespace != null); |
|
var type = FindTypeInNamespace(module.Metadata, @namespace.Value, typeName); |
|
Debug.Assert(!type.IsNil); |
|
return new TypeDefinition(module, type); |
|
} |
|
} |
|
|
|
static NamespaceDefinition? ResolveNamespace(MetadataReader metadata, string @namespace) |
|
{ |
|
var currentNamespace = metadata.GetNamespaceDefinitionRoot(); |
|
int startIndex = 0; |
|
int dotIndex; |
|
|
|
do { |
|
dotIndex = @namespace.IndexOf('.', startIndex); |
|
string identifier = dotIndex == -1 ? @namespace : @namespace.Substring(0, dotIndex); |
|
|
|
var next = currentNamespace.NamespaceDefinitions.FirstOrDefault(ns => metadata.StringComparer.Equals(ns, identifier)); |
|
if (next.IsNil) |
|
return null; |
|
|
|
currentNamespace = metadata.GetNamespaceDefinition(next); |
|
startIndex = dotIndex + 1; |
|
} while (dotIndex > 0); |
|
|
|
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); |
|
if (resolvedTypeReference.IsNil) |
|
return default; |
|
targetModule = resolvedTypeReference.Module; |
|
targetMetadata = targetModule.Metadata; |
|
declaringType = targetMetadata.GetTypeDefinition(resolvedTypeReference.Handle); |
|
break; |
|
case HandleKind.TypeSpecification: |
|
resolvedTypeReference = Resolve((TypeSpecificationHandle)mr.Parent, context); |
|
if (resolvedTypeReference.IsNil) |
|
return default; |
|
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); |
|
} |
|
return default; |
|
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)); |
|
} |
|
foreach (var (method, signature) in candidates) { |
|
if (SignatureBlobComparer.EqualsMethodSignature(targetMetadata.GetBlobReader(signature), metadata.GetBlobReader(mr.Signature), targetMetadata, metadata)) |
|
return new MethodDefinition(targetModule, method); |
|
} |
|
return default; |
|
default: |
|
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); |
|
if (unspecialized.IsNil) |
|
return 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); |
|
} |
|
} |
|
|
|
public 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); |
|
} |
|
} |
|
}
|
|
|