// // 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.Cecil.Metadata; using MonoFN.Collections.Generic; using System; using System.Collections.Generic; using SR = System.Reflection; namespace MonoFN.Cecil { public interface IMetadataImporterProvider { IMetadataImporter GetMetadataImporter(ModuleDefinition module); } public interface IMetadataImporter { AssemblyNameReference ImportReference(AssemblyNameReference reference); TypeReference ImportReference(TypeReference type, IGenericParameterProvider context); FieldReference ImportReference(FieldReference field, IGenericParameterProvider context); MethodReference ImportReference(MethodReference method, IGenericParameterProvider context); } public interface IReflectionImporterProvider { IReflectionImporter GetReflectionImporter(ModuleDefinition module); } public interface IReflectionImporter { AssemblyNameReference ImportReference(SR.AssemblyName reference); TypeReference ImportReference(Type type, IGenericParameterProvider context); FieldReference ImportReference(SR.FieldInfo field, IGenericParameterProvider context); MethodReference ImportReference(SR.MethodBase method, IGenericParameterProvider context); } struct ImportGenericContext { Collection stack; public bool IsEmpty { get { return stack == null; } } public ImportGenericContext(IGenericParameterProvider provider) { if (provider == null) throw new ArgumentNullException("provider"); stack = null; Push(provider); } public void Push(IGenericParameterProvider provider) { if (stack == null) stack = new Collection(1) { provider }; else stack.Add(provider); } public void Pop() { stack.RemoveAt(stack.Count - 1); } public TypeReference MethodParameter(string method, int position) { for (int i = stack.Count - 1; i >= 0; i--) { var candidate = stack[i] as MethodReference; if (candidate == null) continue; if (method != NormalizeMethodName(candidate)) continue; return candidate.GenericParameters[position]; } throw new InvalidOperationException(); } public string NormalizeMethodName(MethodReference method) { return method.DeclaringType.GetElementType().FullName + "." + method.Name; } public TypeReference TypeParameter(string type, int position) { for (int i = stack.Count - 1; i >= 0; i--) { var candidate = GenericTypeFor(stack[i]); if (candidate.FullName != type) continue; return candidate.GenericParameters[position]; } throw new InvalidOperationException(); } static TypeReference GenericTypeFor(IGenericParameterProvider context) { var type = context as TypeReference; if (type != null) return type.GetElementType(); var method = context as MethodReference; if (method != null) return method.DeclaringType.GetElementType(); throw new InvalidOperationException(); } public static ImportGenericContext For(IGenericParameterProvider context) { return context != null ? new ImportGenericContext(context) : default(ImportGenericContext); } } public class DefaultReflectionImporter : IReflectionImporter { readonly protected ModuleDefinition module; public DefaultReflectionImporter(ModuleDefinition module) { Mixin.CheckModule(module); this.module = module; } enum ImportGenericKind { Definition, Open, } static readonly Dictionary type_etype_mapping = new Dictionary(18) { { typeof (void), ElementType.Void }, { typeof (bool), ElementType.Boolean }, { typeof (char), ElementType.Char }, { typeof (sbyte), ElementType.I1 }, { typeof (byte), ElementType.U1 }, { typeof (short), ElementType.I2 }, { typeof (ushort), ElementType.U2 }, { typeof (int), ElementType.I4 }, { typeof (uint), ElementType.U4 }, { typeof (long), ElementType.I8 }, { typeof (ulong), ElementType.U8 }, { typeof (float), ElementType.R4 }, { typeof (double), ElementType.R8 }, { typeof (string), ElementType.String }, { typeof (TypedReference), ElementType.TypedByRef }, { typeof (IntPtr), ElementType.I }, { typeof (UIntPtr), ElementType.U }, { typeof (object), ElementType.Object }, }; TypeReference ImportType(Type type, ImportGenericContext context) { return ImportType(type, context, ImportGenericKind.Open); } TypeReference ImportType(Type type, ImportGenericContext context, ImportGenericKind import_kind) { if (IsTypeSpecification(type) || ImportOpenGenericType(type, import_kind)) return ImportTypeSpecification(type, context); var reference = new TypeReference( string.Empty, type.Name, module, ImportScope(type), type.IsValueType); reference.etype = ImportElementType(type); if (IsNestedType(type)) reference.DeclaringType = ImportType(type.DeclaringType, context, import_kind); else reference.Namespace = type.Namespace ?? string.Empty; if (type.IsGenericType) ImportGenericParameters(reference, type.GetGenericArguments()); return reference; } protected virtual IMetadataScope ImportScope(Type type) { return ImportScope(type.Assembly); } static bool ImportOpenGenericType(Type type, ImportGenericKind import_kind) { return type.IsGenericType && type.IsGenericTypeDefinition && import_kind == ImportGenericKind.Open; } static bool ImportOpenGenericMethod(SR.MethodBase method, ImportGenericKind import_kind) { return method.IsGenericMethod && method.IsGenericMethodDefinition && import_kind == ImportGenericKind.Open; } static bool IsNestedType(Type type) { return type.IsNested; } TypeReference ImportTypeSpecification(Type type, ImportGenericContext context) { if (type.IsByRef) return new ByReferenceType(ImportType(type.GetElementType(), context)); if (type.IsPointer) return new PointerType(ImportType(type.GetElementType(), context)); if (type.IsArray) return new ArrayType(ImportType(type.GetElementType(), context), type.GetArrayRank()); if (type.IsGenericType) return ImportGenericInstance(type, context); if (type.IsGenericParameter) return ImportGenericParameter(type, context); throw new NotSupportedException(type.FullName); } static TypeReference ImportGenericParameter(Type type, ImportGenericContext context) { if (context.IsEmpty) throw new InvalidOperationException(); if (type.DeclaringMethod != null) return context.MethodParameter(NormalizeMethodName(type.DeclaringMethod), type.GenericParameterPosition); if (type.DeclaringType != null) return context.TypeParameter(NormalizeTypeFullName(type.DeclaringType), type.GenericParameterPosition); throw new InvalidOperationException(); } static string NormalizeMethodName(SR.MethodBase method) { return NormalizeTypeFullName(method.DeclaringType) + "." + method.Name; } static string NormalizeTypeFullName(Type type) { if (IsNestedType(type)) return NormalizeTypeFullName(type.DeclaringType) + "/" + type.Name; return type.FullName; } TypeReference ImportGenericInstance(Type type, ImportGenericContext context) { var element_type = ImportType(type.GetGenericTypeDefinition(), context, ImportGenericKind.Definition); var arguments = type.GetGenericArguments(); var instance = new GenericInstanceType(element_type, arguments.Length); var instance_arguments = instance.GenericArguments; context.Push(element_type); try { for (int i = 0; i < arguments.Length; i++) instance_arguments.Add(ImportType(arguments[i], context)); return instance; } finally { context.Pop(); } } static bool IsTypeSpecification(Type type) { return type.HasElementType || IsGenericInstance(type) || type.IsGenericParameter; } static bool IsGenericInstance(Type type) { return type.IsGenericType && !type.IsGenericTypeDefinition; } static ElementType ImportElementType(Type type) { ElementType etype; if (!type_etype_mapping.TryGetValue(type, out etype)) return ElementType.None; return etype; } protected AssemblyNameReference ImportScope(SR.Assembly assembly) { return ImportReference(assembly.GetName()); } public virtual AssemblyNameReference ImportReference(SR.AssemblyName name) { Mixin.CheckName(name); AssemblyNameReference reference; if (TryGetAssemblyNameReference(name, out reference)) return reference; reference = new AssemblyNameReference(name.Name, name.Version) { PublicKeyToken = name.GetPublicKeyToken(), Culture = name.CultureInfo.Name, HashAlgorithm = (AssemblyHashAlgorithm)name.HashAlgorithm, }; module.AssemblyReferences.Add(reference); return reference; } bool TryGetAssemblyNameReference(SR.AssemblyName name, out AssemblyNameReference assembly_reference) { var references = module.AssemblyReferences; for (int i = 0; i < references.Count; i++) { var reference = references[i]; if (name.FullName != reference.FullName) // TODO compare field by field continue; assembly_reference = reference; return true; } assembly_reference = null; return false; } FieldReference ImportField(SR.FieldInfo field, ImportGenericContext context) { var declaring_type = ImportType(field.DeclaringType, context); if (IsGenericInstance(field.DeclaringType)) field = ResolveFieldDefinition(field); context.Push(declaring_type); try { return new FieldReference { Name = field.Name, DeclaringType = declaring_type, FieldType = ImportType(field.FieldType, context), }; } finally { context.Pop(); } } static SR.FieldInfo ResolveFieldDefinition(SR.FieldInfo field) { return field.Module.ResolveField(field.MetadataToken); } static SR.MethodBase ResolveMethodDefinition(SR.MethodBase method) { return method.Module.ResolveMethod(method.MetadataToken); } MethodReference ImportMethod(SR.MethodBase method, ImportGenericContext context, ImportGenericKind import_kind) { if (IsMethodSpecification(method) || ImportOpenGenericMethod(method, import_kind)) return ImportMethodSpecification(method, context); var declaring_type = ImportType(method.DeclaringType, context); if (IsGenericInstance(method.DeclaringType)) method = ResolveMethodDefinition(method); var reference = new MethodReference { Name = method.Name, HasThis = HasCallingConvention(method, SR.CallingConventions.HasThis), ExplicitThis = HasCallingConvention(method, SR.CallingConventions.ExplicitThis), DeclaringType = ImportType(method.DeclaringType, context, ImportGenericKind.Definition), }; if (HasCallingConvention(method, SR.CallingConventions.VarArgs)) reference.CallingConvention &= MethodCallingConvention.VarArg; if (method.IsGenericMethod) ImportGenericParameters(reference, method.GetGenericArguments()); context.Push(reference); try { var method_info = method as SR.MethodInfo; reference.ReturnType = method_info != null ? ImportType(method_info.ReturnType, context) : ImportType(typeof(void), default(ImportGenericContext)); var parameters = method.GetParameters(); var reference_parameters = reference.Parameters; for (int i = 0; i < parameters.Length; i++) reference_parameters.Add( new ParameterDefinition(ImportType(parameters[i].ParameterType, context))); reference.DeclaringType = declaring_type; return reference; } finally { context.Pop(); } } static void ImportGenericParameters(IGenericParameterProvider provider, Type[] arguments) { var provider_parameters = provider.GenericParameters; for (int i = 0; i < arguments.Length; i++) provider_parameters.Add(new GenericParameter(arguments[i].Name, provider)); } static bool IsMethodSpecification(SR.MethodBase method) { return method.IsGenericMethod && !method.IsGenericMethodDefinition; } MethodReference ImportMethodSpecification(SR.MethodBase method, ImportGenericContext context) { var method_info = method as SR.MethodInfo; if (method_info == null) throw new InvalidOperationException(); var element_method = ImportMethod(method_info.GetGenericMethodDefinition(), context, ImportGenericKind.Definition); var instance = new GenericInstanceMethod(element_method); var arguments = method.GetGenericArguments(); var instance_arguments = instance.GenericArguments; context.Push(element_method); try { for (int i = 0; i < arguments.Length; i++) instance_arguments.Add(ImportType(arguments[i], context)); return instance; } finally { context.Pop(); } } static bool HasCallingConvention(SR.MethodBase method, SR.CallingConventions conventions) { return (method.CallingConvention & conventions) != 0; } public virtual TypeReference ImportReference(Type type, IGenericParameterProvider context) { Mixin.CheckType(type); return ImportType( type, ImportGenericContext.For(context), context != null ? ImportGenericKind.Open : ImportGenericKind.Definition); } public virtual FieldReference ImportReference(SR.FieldInfo field, IGenericParameterProvider context) { Mixin.CheckField(field); return ImportField(field, ImportGenericContext.For(context)); } public virtual MethodReference ImportReference(SR.MethodBase method, IGenericParameterProvider context) { Mixin.CheckMethod(method); return ImportMethod(method, ImportGenericContext.For(context), context != null ? ImportGenericKind.Open : ImportGenericKind.Definition); } } public class DefaultMetadataImporter : IMetadataImporter { readonly protected ModuleDefinition module; public DefaultMetadataImporter(ModuleDefinition module) { Mixin.CheckModule(module); this.module = module; } TypeReference ImportType(TypeReference type, ImportGenericContext context) { if (type.IsTypeSpecification()) return ImportTypeSpecification(type, context); var reference = new TypeReference( type.Namespace, type.Name, module, ImportScope(type), type.IsValueType); MetadataSystem.TryProcessPrimitiveTypeReference(reference); if (type.IsNested) reference.DeclaringType = ImportType(type.DeclaringType, context); if (type.HasGenericParameters) ImportGenericParameters(reference, type); return reference; } protected virtual IMetadataScope ImportScope(TypeReference type) { return ImportScope(type.Scope); } protected IMetadataScope ImportScope(IMetadataScope scope) { switch (scope.MetadataScopeType) { case MetadataScopeType.AssemblyNameReference: return ImportReference((AssemblyNameReference)scope); case MetadataScopeType.ModuleDefinition: if (scope == module) return scope; return ImportReference(((ModuleDefinition)scope).Assembly.Name); case MetadataScopeType.ModuleReference: throw new NotImplementedException(); } throw new NotSupportedException(); } public virtual AssemblyNameReference ImportReference(AssemblyNameReference name) { Mixin.CheckName(name); AssemblyNameReference reference; if (module.TryGetAssemblyNameReference(name, out reference)) return reference; reference = new AssemblyNameReference(name.Name, name.Version) { Culture = name.Culture, HashAlgorithm = name.HashAlgorithm, IsRetargetable = name.IsRetargetable, IsWindowsRuntime = name.IsWindowsRuntime, }; var pk_token = !name.PublicKeyToken.IsNullOrEmpty() ? new byte[name.PublicKeyToken.Length] : Empty.Array; if (pk_token.Length > 0) Buffer.BlockCopy(name.PublicKeyToken, 0, pk_token, 0, pk_token.Length); reference.PublicKeyToken = pk_token; //Only add if not self. if (CanAddAssemblyNameReference(module, reference)) module.AssemblyReferences.Add(reference); return reference; } private bool CanAddAssemblyNameReference(ModuleDefinition module, AssemblyNameReference nameRef) { return true; //return (module.assembly.FullName != nameRef.FullName); } static void ImportGenericParameters(IGenericParameterProvider imported, IGenericParameterProvider original) { var parameters = original.GenericParameters; var imported_parameters = imported.GenericParameters; for (int i = 0; i < parameters.Count; i++) imported_parameters.Add(new GenericParameter(parameters[i].Name, imported)); } TypeReference ImportTypeSpecification(TypeReference type, ImportGenericContext context) { switch (type.etype) { case ElementType.SzArray: var vector = (ArrayType)type; return new ArrayType(ImportType(vector.ElementType, context)); case ElementType.Ptr: var pointer = (PointerType)type; return new PointerType(ImportType(pointer.ElementType, context)); case ElementType.ByRef: var byref = (ByReferenceType)type; return new ByReferenceType(ImportType(byref.ElementType, context)); case ElementType.Pinned: var pinned = (PinnedType)type; return new PinnedType(ImportType(pinned.ElementType, context)); case ElementType.Sentinel: var sentinel = (SentinelType)type; return new SentinelType(ImportType(sentinel.ElementType, context)); case ElementType.FnPtr: var fnptr = (FunctionPointerType)type; var imported_fnptr = new FunctionPointerType() { HasThis = fnptr.HasThis, ExplicitThis = fnptr.ExplicitThis, CallingConvention = fnptr.CallingConvention, ReturnType = ImportType(fnptr.ReturnType, context), }; if (!fnptr.HasParameters) return imported_fnptr; for (int i = 0; i < fnptr.Parameters.Count; i++) imported_fnptr.Parameters.Add(new ParameterDefinition( ImportType(fnptr.Parameters[i].ParameterType, context))); return imported_fnptr; case ElementType.CModOpt: var modopt = (OptionalModifierType)type; return new OptionalModifierType( ImportType(modopt.ModifierType, context), ImportType(modopt.ElementType, context)); case ElementType.CModReqD: var modreq = (RequiredModifierType)type; return new RequiredModifierType( ImportType(modreq.ModifierType, context), ImportType(modreq.ElementType, context)); case ElementType.Array: var array = (ArrayType)type; var imported_array = new ArrayType(ImportType(array.ElementType, context)); if (array.IsVector) return imported_array; var dimensions = array.Dimensions; var imported_dimensions = imported_array.Dimensions; imported_dimensions.Clear(); for (int i = 0; i < dimensions.Count; i++) { var dimension = dimensions[i]; imported_dimensions.Add(new ArrayDimension(dimension.LowerBound, dimension.UpperBound)); } return imported_array; case ElementType.GenericInst: var instance = (GenericInstanceType)type; var element_type = ImportType(instance.ElementType, context); var arguments = instance.GenericArguments; var imported_instance = new GenericInstanceType(element_type, arguments.Count); var imported_arguments = imported_instance.GenericArguments; for (int i = 0; i < arguments.Count; i++) imported_arguments.Add(ImportType(arguments[i], context)); return imported_instance; case ElementType.Var: var var_parameter = (GenericParameter)type; if (var_parameter.DeclaringType == null) throw new InvalidOperationException(); return context.TypeParameter(var_parameter.DeclaringType.FullName, var_parameter.Position); case ElementType.MVar: var mvar_parameter = (GenericParameter)type; if (mvar_parameter.DeclaringMethod == null) throw new InvalidOperationException(); return context.MethodParameter(context.NormalizeMethodName(mvar_parameter.DeclaringMethod), mvar_parameter.Position); } throw new NotSupportedException(type.etype.ToString()); } FieldReference ImportField(FieldReference field, ImportGenericContext context) { var declaring_type = ImportType(field.DeclaringType, context); context.Push(declaring_type); try { return new FieldReference { Name = field.Name, DeclaringType = declaring_type, FieldType = ImportType(field.FieldType, context), }; } finally { context.Pop(); } } MethodReference ImportMethod(MethodReference method, ImportGenericContext context) { if (method.IsGenericInstance) return ImportMethodSpecification(method, context); var declaring_type = ImportType(method.DeclaringType, context); var reference = new MethodReference { Name = method.Name, HasThis = method.HasThis, ExplicitThis = method.ExplicitThis, DeclaringType = declaring_type, CallingConvention = method.CallingConvention, }; if (method.HasGenericParameters) ImportGenericParameters(reference, method); context.Push(reference); try { reference.ReturnType = ImportType(method.ReturnType, context); if (!method.HasParameters) return reference; var parameters = method.Parameters; var reference_parameters = reference.parameters = new ParameterDefinitionCollection(reference, parameters.Count); for (int i = 0; i < parameters.Count; i++) reference_parameters.Add( new ParameterDefinition(ImportType(parameters[i].ParameterType, context))); return reference; } finally { context.Pop(); } } MethodSpecification ImportMethodSpecification(MethodReference method, ImportGenericContext context) { if (!method.IsGenericInstance) throw new NotSupportedException(); var instance = (GenericInstanceMethod)method; var element_method = ImportMethod(instance.ElementMethod, context); var imported_instance = new GenericInstanceMethod(element_method); var arguments = instance.GenericArguments; var imported_arguments = imported_instance.GenericArguments; for (int i = 0; i < arguments.Count; i++) imported_arguments.Add(ImportType(arguments[i], context)); return imported_instance; } public virtual TypeReference ImportReference(TypeReference type, IGenericParameterProvider context) { Mixin.CheckType(type); return ImportType(type, ImportGenericContext.For(context)); } public virtual FieldReference ImportReference(FieldReference field, IGenericParameterProvider context) { Mixin.CheckField(field); return ImportField(field, ImportGenericContext.For(context)); } public virtual MethodReference ImportReference(MethodReference method, IGenericParameterProvider context) { Mixin.CheckMethod(method); return ImportMethod(method, ImportGenericContext.For(context)); } } static partial class Mixin { public static void CheckModule(ModuleDefinition module) { if (module == null) throw new ArgumentNullException(Argument.module.ToString()); } public static bool TryGetAssemblyNameReference(this ModuleDefinition module, AssemblyNameReference name_reference, out AssemblyNameReference assembly_reference) { var references = module.AssemblyReferences; for (int i = 0; i < references.Count; i++) { var reference = references[i]; if (!Equals(name_reference, reference)) continue; assembly_reference = reference; return true; } assembly_reference = null; return false; } static bool Equals(byte[] a, byte[] b) { if (ReferenceEquals(a, b)) return true; if (a == null) return false; if (a.Length != b.Length) return false; for (int i = 0; i < a.Length; i++) if (a[i] != b[i]) return false; return true; } static bool Equals(T a, T b) where T : class, IEquatable { if (ReferenceEquals(a, b)) return true; if (a == null) return false; return a.Equals(b); } static bool Equals(AssemblyNameReference a, AssemblyNameReference b) { if (ReferenceEquals(a, b)) return true; if (a.Name != b.Name) return false; if (!Equals(a.Version, b.Version)) return false; if (a.Culture != b.Culture) return false; if (!Equals(a.PublicKeyToken, b.PublicKeyToken)) return false; return true; } } }