386 lines
12 KiB
C#
386 lines
12 KiB
C#
//
|
|
// Author:
|
|
// Jb Evain (jbevain@gmail.com)
|
|
//
|
|
// Copyright (c) 2008 - 2015 Jb Evain
|
|
// Copyright (c) 2008 - 2011 Novell, Inc.
|
|
//
|
|
// Licensed under the MIT/X11 license.
|
|
//
|
|
|
|
using MonoFN.Collections.Generic;
|
|
using System;
|
|
|
|
namespace MonoFN.Cecil
|
|
{
|
|
public interface IAssemblyResolver : IDisposable
|
|
{
|
|
AssemblyDefinition Resolve(AssemblyNameReference name);
|
|
AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters);
|
|
}
|
|
|
|
public interface IMetadataResolver
|
|
{
|
|
TypeDefinition Resolve(TypeReference type);
|
|
FieldDefinition Resolve(FieldReference field);
|
|
MethodDefinition Resolve(MethodReference method);
|
|
}
|
|
|
|
#if !NET_CORE
|
|
[Serializable]
|
|
#endif
|
|
public sealed class ResolutionException : Exception
|
|
{
|
|
public MemberReference Member { get; }
|
|
public IMetadataScope Scope
|
|
{
|
|
get
|
|
{
|
|
TypeReference type = Member as TypeReference;
|
|
if (type != null)
|
|
return type.Scope;
|
|
|
|
TypeReference declaring_type = Member.DeclaringType;
|
|
if (declaring_type != null)
|
|
return declaring_type.Scope;
|
|
|
|
throw new NotSupportedException();
|
|
}
|
|
}
|
|
|
|
public ResolutionException(MemberReference member) : base("Failed to resolve " + member.FullName)
|
|
{
|
|
if (member == null)
|
|
throw new ArgumentNullException("member");
|
|
|
|
this.Member = member;
|
|
}
|
|
|
|
public ResolutionException(MemberReference member, Exception innerException) : base("Failed to resolve " + member.FullName, innerException)
|
|
{
|
|
if (member == null)
|
|
throw new ArgumentNullException("member");
|
|
|
|
this.Member = member;
|
|
}
|
|
|
|
#if !NET_CORE
|
|
private ResolutionException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
|
|
#endif
|
|
}
|
|
|
|
public class MetadataResolver : IMetadataResolver
|
|
{
|
|
public IAssemblyResolver AssemblyResolver { get; }
|
|
|
|
public MetadataResolver(IAssemblyResolver assemblyResolver)
|
|
{
|
|
if (assemblyResolver == null)
|
|
throw new ArgumentNullException("assemblyResolver");
|
|
|
|
AssemblyResolver = assemblyResolver;
|
|
}
|
|
|
|
public virtual TypeDefinition Resolve(TypeReference type)
|
|
{
|
|
Mixin.CheckType(type);
|
|
|
|
type = type.GetElementType();
|
|
|
|
IMetadataScope scope = type.Scope;
|
|
|
|
if (scope == null)
|
|
return null;
|
|
|
|
switch (scope.MetadataScopeType)
|
|
{
|
|
case MetadataScopeType.AssemblyNameReference:
|
|
AssemblyDefinition assembly = AssemblyResolver.Resolve((AssemblyNameReference)scope);
|
|
if (assembly == null)
|
|
return null;
|
|
|
|
return GetType(assembly.MainModule, type);
|
|
case MetadataScopeType.ModuleDefinition:
|
|
return GetType((ModuleDefinition)scope, type);
|
|
case MetadataScopeType.ModuleReference:
|
|
if (type.Module.Assembly == null)
|
|
return null;
|
|
|
|
Collection<ModuleDefinition> modules = type.Module.Assembly.Modules;
|
|
ModuleReference module_ref = (ModuleReference)scope;
|
|
for (int i = 0; i < modules.Count; i++)
|
|
{
|
|
ModuleDefinition netmodule = modules[i];
|
|
if (netmodule.Name == module_ref.Name)
|
|
return GetType(netmodule, type);
|
|
}
|
|
break;
|
|
}
|
|
|
|
throw new NotSupportedException();
|
|
}
|
|
|
|
private static TypeDefinition GetType(ModuleDefinition module, TypeReference reference)
|
|
{
|
|
TypeDefinition type = GetTypeDefinition(module, reference);
|
|
if (type != null)
|
|
return type;
|
|
|
|
if (!module.HasExportedTypes)
|
|
return null;
|
|
|
|
Collection<ExportedType> exported_types = module.ExportedTypes;
|
|
|
|
for (int i = 0; i < exported_types.Count; i++)
|
|
{
|
|
ExportedType exported_type = exported_types[i];
|
|
if (exported_type.Name != reference.Name)
|
|
continue;
|
|
|
|
if (exported_type.Namespace != reference.Namespace)
|
|
continue;
|
|
|
|
return exported_type.Resolve();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static TypeDefinition GetTypeDefinition(ModuleDefinition module, TypeReference type)
|
|
{
|
|
if (!type.IsNested)
|
|
return module.GetType(type.Namespace, type.Name);
|
|
|
|
TypeDefinition declaring_type = type.DeclaringType.Resolve();
|
|
if (declaring_type == null)
|
|
return null;
|
|
|
|
return declaring_type.GetNestedType(type.TypeFullName());
|
|
}
|
|
|
|
public virtual FieldDefinition Resolve(FieldReference field)
|
|
{
|
|
Mixin.CheckField(field);
|
|
|
|
TypeDefinition type = Resolve(field.DeclaringType);
|
|
if (type == null)
|
|
return null;
|
|
|
|
if (!type.HasFields)
|
|
return null;
|
|
|
|
return GetField(type, field);
|
|
}
|
|
|
|
private FieldDefinition GetField(TypeDefinition type, FieldReference reference)
|
|
{
|
|
while (type != null)
|
|
{
|
|
FieldDefinition field = GetField(type.Fields, reference);
|
|
if (field != null)
|
|
return field;
|
|
|
|
if (type.BaseType == null)
|
|
return null;
|
|
|
|
type = Resolve(type.BaseType);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static FieldDefinition GetField(Collection<FieldDefinition> fields, FieldReference reference)
|
|
{
|
|
for (int i = 0; i < fields.Count; i++)
|
|
{
|
|
FieldDefinition field = fields[i];
|
|
|
|
if (field.Name != reference.Name)
|
|
continue;
|
|
|
|
if (!AreSame(field.FieldType, reference.FieldType))
|
|
continue;
|
|
|
|
return field;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public virtual MethodDefinition Resolve(MethodReference method)
|
|
{
|
|
Mixin.CheckMethod(method);
|
|
|
|
TypeDefinition type = Resolve(method.DeclaringType);
|
|
if (type == null)
|
|
return null;
|
|
|
|
method = method.GetElementMethod();
|
|
|
|
if (!type.HasMethods)
|
|
return null;
|
|
|
|
return GetMethod(type, method);
|
|
}
|
|
|
|
private MethodDefinition GetMethod(TypeDefinition type, MethodReference reference)
|
|
{
|
|
while (type != null)
|
|
{
|
|
MethodDefinition method = GetMethod(type.Methods, reference);
|
|
if (method != null)
|
|
return method;
|
|
|
|
if (type.BaseType == null)
|
|
return null;
|
|
|
|
type = Resolve(type.BaseType);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static MethodDefinition GetMethod(Collection<MethodDefinition> methods, MethodReference reference)
|
|
{
|
|
for (int i = 0; i < methods.Count; i++)
|
|
{
|
|
MethodDefinition method = methods[i];
|
|
|
|
if (method.Name != reference.Name)
|
|
continue;
|
|
|
|
if (method.HasGenericParameters != reference.HasGenericParameters)
|
|
continue;
|
|
|
|
if (method.HasGenericParameters && method.GenericParameters.Count != reference.GenericParameters.Count)
|
|
continue;
|
|
|
|
if (!AreSame(method.ReturnType, reference.ReturnType))
|
|
continue;
|
|
|
|
if (method.IsVarArg() != reference.IsVarArg())
|
|
continue;
|
|
|
|
if (method.IsVarArg() && IsVarArgCallTo(method, reference))
|
|
return method;
|
|
|
|
if (method.HasParameters != reference.HasParameters)
|
|
continue;
|
|
|
|
if (!method.HasParameters && !reference.HasParameters)
|
|
return method;
|
|
|
|
if (!AreSame(method.Parameters, reference.Parameters))
|
|
continue;
|
|
|
|
return method;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static bool AreSame(Collection<ParameterDefinition> a, Collection<ParameterDefinition> b)
|
|
{
|
|
int count = a.Count;
|
|
|
|
if (count != b.Count)
|
|
return false;
|
|
|
|
if (count == 0)
|
|
return true;
|
|
|
|
for (int i = 0; i < count; i++)
|
|
if (!AreSame(a[i].ParameterType, b[i].ParameterType))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
private static bool IsVarArgCallTo(MethodDefinition method, MethodReference reference)
|
|
{
|
|
if (method.Parameters.Count >= reference.Parameters.Count)
|
|
return false;
|
|
|
|
if (reference.GetSentinelPosition() != method.Parameters.Count)
|
|
return false;
|
|
|
|
for (int i = 0; i < method.Parameters.Count; i++)
|
|
if (!AreSame(method.Parameters[i].ParameterType, reference.Parameters[i].ParameterType))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
private static bool AreSame(TypeSpecification a, TypeSpecification b)
|
|
{
|
|
if (!AreSame(a.ElementType, b.ElementType))
|
|
return false;
|
|
|
|
if (a.IsGenericInstance)
|
|
return AreSame((GenericInstanceType)a, (GenericInstanceType)b);
|
|
|
|
if (a.IsRequiredModifier || a.IsOptionalModifier)
|
|
return AreSame((IModifierType)a, (IModifierType)b);
|
|
|
|
if (a.IsArray)
|
|
return AreSame((ArrayType)a, (ArrayType)b);
|
|
|
|
return true;
|
|
}
|
|
|
|
private static bool AreSame(ArrayType a, ArrayType b)
|
|
{
|
|
if (a.Rank != b.Rank)
|
|
return false;
|
|
|
|
// TODO: dimensions
|
|
|
|
return true;
|
|
}
|
|
|
|
private static bool AreSame(IModifierType a, IModifierType b)
|
|
{
|
|
return AreSame(a.ModifierType, b.ModifierType);
|
|
}
|
|
|
|
private static bool AreSame(GenericInstanceType a, GenericInstanceType b)
|
|
{
|
|
if (a.GenericArguments.Count != b.GenericArguments.Count)
|
|
return false;
|
|
|
|
for (int i = 0; i < a.GenericArguments.Count; i++)
|
|
if (!AreSame(a.GenericArguments[i], b.GenericArguments[i]))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
private static bool AreSame(GenericParameter a, GenericParameter b)
|
|
{
|
|
return a.Position == b.Position;
|
|
}
|
|
|
|
private static bool AreSame(TypeReference a, TypeReference b)
|
|
{
|
|
if (ReferenceEquals(a, b))
|
|
return true;
|
|
|
|
if (a == null || b == null)
|
|
return false;
|
|
|
|
if (a.etype != b.etype)
|
|
return false;
|
|
|
|
if (a.IsGenericParameter)
|
|
return AreSame((GenericParameter)a, (GenericParameter)b);
|
|
|
|
if (a.IsTypeSpecification())
|
|
return AreSame((TypeSpecification)a, (TypeSpecification)b);
|
|
|
|
if (a.Name != b.Name || a.Namespace != b.Namespace)
|
|
return false;
|
|
|
|
return AreSame(a.DeclaringType, b.DeclaringType);
|
|
}
|
|
}
|
|
} |