using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using ModestTree; using Zenject.ReflectionBaking.Mono.Cecil; using Zenject.ReflectionBaking.Mono.Collections.Generic; using ICustomAttributeProvider = Zenject.ReflectionBaking.Mono.Cecil.ICustomAttributeProvider; namespace Zenject.ReflectionBaking { public static class CecilExtensions { public static Type TryGetActualType(this TypeReference typeRef, Assembly assembly) { var reflectionName = GetReflectionName(typeRef); return assembly.GetType(reflectionName); } static string GetReflectionName(TypeReference type) { if (type.IsGenericInstance) { var genericInstance = (GenericInstanceType)type; return string.Format( "{0}.{1}[{2}]", genericInstance.Namespace, type.Name, String.Join(",", genericInstance.GenericArguments.Select(p => GetReflectionName(p)).ToArray())); } return type.FullName; } public static List LookupAllTypes(this ModuleDefinition module) { var allTypes = new List(); foreach (var type in module.Types) { LookupAllTypesInternal(type, allTypes); } return allTypes; } static void LookupAllTypesInternal(TypeDefinition type, List buffer) { buffer.Add(type); foreach (var nestedType in type.NestedTypes) { LookupAllTypesInternal(nestedType, buffer); } } public static TypeReference ImportType(this ModuleDefinition module) { return module.ImportType(typeof(T)); } public static TypeReference ImportType(this ModuleDefinition module, Type type) { return module.Import(type); } public static MethodReference ImportMethod(this ModuleDefinition module, string methodName) { return module.ImportMethod(typeof(T), methodName); } public static MethodReference ImportMethod( this ModuleDefinition module, Type type, string methodName) { return module.Import( module.ImportType(type).Resolve().GetMethod(methodName)); } public static MethodReference ImportMethod( this ModuleDefinition module, string methodName, int numArgs) { return module.ImportMethod(typeof(T), methodName, numArgs); } public static MethodReference ImportMethod( this ModuleDefinition module, Type type, string methodName, int numArgs) { return module.Import( module.ImportType(type).Resolve().GetMethod(methodName, numArgs)); } public static MethodDefinition GetMethod(this TypeDefinition instance, string name) { for (int i = 0; i < instance.Methods.Count; i++) { MethodDefinition methodDef = instance.Methods[i]; if (string.CompareOrdinal(methodDef.Name, name) == 0) { return methodDef; } } return null; } public static MethodDefinition GetMethod(this TypeDefinition instance, string name, params Type[] parameterTypes) { for (int i = 0; i < instance.Methods.Count; i++) { MethodDefinition methodDefinition = instance.Methods[i]; if (!string.Equals(methodDefinition.Name, name, StringComparison.Ordinal) || parameterTypes.Length != methodDefinition.Parameters.Count) { continue; } MethodDefinition result = methodDefinition; for (int x = methodDefinition.Parameters.Count - 1; x >= 0; x--) { ParameterDefinition parameter = methodDefinition.Parameters[x]; if (!string.Equals(parameter.ParameterType.Name, parameterTypes[x].Name, StringComparison.Ordinal)) { break; } if (x == 0) { return result; } } } return null; } public static MethodDefinition GetMethod(this TypeDefinition instance, string name, params TypeReference[] parameterTypes) { if (instance.Methods != null) { for (int i = 0; i < instance.Methods.Count; i++) { MethodDefinition methodDefinition = instance.Methods[i]; if (string.Equals(methodDefinition.Name, name, StringComparison.Ordinal) // Names Match && parameterTypes.Length == methodDefinition.Parameters.Count) // The same number of parameters { MethodDefinition result = methodDefinition; for (int x = methodDefinition.Parameters.Count - 1; x >= 0; x--) { ParameterDefinition parameter = methodDefinition.Parameters[x]; if (!string.Equals(parameter.ParameterType.Name, parameterTypes[x].Name, StringComparison.Ordinal)) { break; } if (x == 0) { return result; } } } } } return null; } public static MethodDefinition GetMethod(this TypeDefinition instance, string name, int argCount) { for (int i = 0; i < instance.Methods.Count; i++) { MethodDefinition methodDef = instance.Methods[i]; if (string.CompareOrdinal(methodDef.Name, name) == 0 && methodDef.Parameters.Count == argCount) { return methodDef; } } return null; } public static PropertyDefinition GetPropertyDefinition(this TypeDefinition instance, string name) { for (int i = 0; i < instance.Properties.Count; i++) { PropertyDefinition preopertyDef = instance.Properties[i]; // Properties can only have one argument or they are an indexer. if (string.CompareOrdinal(preopertyDef.Name, name) == 0 && preopertyDef.Parameters.Count == 0) { return preopertyDef; } } return null; } public static bool HasCustomAttribute(this ICustomAttributeProvider instance) { if (!instance.HasCustomAttributes) { return false; } Collection attributes = instance.CustomAttributes; for(int i = 0; i < attributes.Count; i++) { if (attributes[i].AttributeType.FullName.Equals(typeof(T).FullName, StringComparison.Ordinal)) { return true; } } return false; } public static MethodReference ChangeDeclaringType( this MethodReference methodDef, TypeReference typeRef) { var newMethodRef = new MethodReference( methodDef.Name, methodDef.ReturnType, typeRef); newMethodRef.HasThis = methodDef.HasThis; foreach (var arg in methodDef.Parameters) { var paramDef = new ParameterDefinition(arg.ParameterType); newMethodRef.Parameters.Add(paramDef); } return newMethodRef; } public static FieldReference ChangeDeclaringType( this FieldReference fieldDef, TypeReference typeRef) { return new FieldReference( fieldDef.Name, fieldDef.FieldType, typeRef); } public static CustomAttribute GetCustomAttribute(this ICustomAttributeProvider instance) { if (!instance.HasCustomAttributes) { return null; } Collection attributes = instance.CustomAttributes; for (int i = 0; i < attributes.Count; i++) { if (attributes[i].AttributeType.FullName.Equals(typeof(T).FullName, StringComparison.Ordinal)) { return attributes[i]; } } return null; } public static IEnumerable GetSpecificBaseTypesAndSelf( this TypeReference specificTypeRef) { yield return specificTypeRef; foreach (var ancestor in specificTypeRef.GetSpecificBaseTypesAndSelf()) { yield return ancestor; } } public static IEnumerable GetSpecificBaseTypes( this TypeReference specificTypeRef) { var specificBaseTypeRef = specificTypeRef.TryGetSpecificBaseType(); if (specificBaseTypeRef != null) { yield return specificBaseTypeRef; foreach (var ancestor in GetSpecificBaseTypes(specificBaseTypeRef)) { yield return ancestor; } } } public static IEnumerable AllNestParentsAndSelf(this TypeReference specificTypeRef) { yield return specificTypeRef; foreach (var ancestor in specificTypeRef.AllNestParents()) { yield return ancestor; } } public static IEnumerable AllNestParents(this TypeReference specificTypeRef) { if (specificTypeRef.DeclaringType != null) { yield return specificTypeRef.DeclaringType; foreach (var ancestor in specificTypeRef.DeclaringType.AllNestParents()) { yield return ancestor; } } } public static TypeReference TryResolve(this TypeReference typeRef) { try { return typeRef.Resolve(); } catch { return null; } } public static TypeReference TryGetSpecificBaseType(this TypeReference specificTypeRef) { var typeDef = specificTypeRef.Resolve(); if (typeDef.BaseType == null || typeDef.BaseType.FullName == "System.Object") { return null; } var specificBaseTypeRef = typeDef.BaseType; if (specificBaseTypeRef.ContainsGenericParameter) { var genericArgMap = new Dictionary(); foreach (var ancestor in specificTypeRef.AllNestParentsAndSelf()) { var specificTypeRefGenericInstance = ancestor as GenericInstanceType; if (specificTypeRefGenericInstance != null) { for (int i = 0; i < typeDef.GenericParameters.Count; i++) { genericArgMap[typeDef.GenericParameters[i].Name] = specificTypeRefGenericInstance.GenericArguments[i]; } } } specificBaseTypeRef = FillInGenericParameters(specificBaseTypeRef, genericArgMap); } return specificBaseTypeRef; } public static TypeReference FillInGenericParameters( TypeReference type, Dictionary genericArgMap) { var genericType = type as GenericInstanceType; Assert.IsNotNull(genericType); var genericTypeClone = new GenericInstanceType(type.Resolve()); for (int i = 0; i < genericType.GenericArguments.Count; i++) { var arg = genericType.GenericArguments[i]; if (arg.IsGenericParameter) { Assert.That(genericArgMap.ContainsKey(arg.Name), "Could not find key '{0}' for type '{1}'", arg.Name, type.FullName); genericTypeClone.GenericArguments.Add(genericArgMap[arg.Name]); } else { genericTypeClone.GenericArguments.Add(arg); } } return genericTypeClone; } } }