/// <summary> /// Gets the method the given method overrides. /// </summary> public static MethodDefinition GetBaseMethod(this MethodDefinition method) { var declaringType = method.DeclaringType; var baseTypeRef = declaringType.BaseType; var resolver = new GenericsResolver(method.DeclaringType); while (baseTypeRef != null) { var baseType = baseTypeRef.Resolve(); if (baseType == null) return null; var result = baseType.Methods.FirstOrDefault(x => x.AreSame(method, resolver.Resolve)); if (result != null) return result; baseTypeRef = baseType.BaseType; } return null; }
/// <summary> /// Gets the first method the given method overrides from an implemented interface. /// </summary> public static MethodDefinition GetBaseInterfaceMethod(this MethodDefinition method) { var declaringType = method.DeclaringType; var resolver = new GenericsResolver(method.DeclaringType); while (declaringType != null) { foreach (var ifaceRef in declaringType.Interfaces.Select(x => x.Interface)) { var iface = ifaceRef.Resolve(); if (iface == null) continue; var result = iface.Methods.FirstOrDefault(x => x.AreSame(method, resolver.Resolve)); if (result != null) return result; } declaringType = (declaringType.BaseType != null) ? declaringType.BaseType.Resolve() : null; } return null; }
/// <summary> /// Is the given method an implementation (implicit or explicit) of the given interface method. /// </summary> /// <param name="method">The method to investigate</param> /// <param name="iMethod">The interface method.</param> public static bool IsImplementationOf(this MethodDefinition method, MethodDefinition iMethod) { // Try explicit first var resolver = new GenericsResolver(method.DeclaringType); if (method.IsExplicitImplementation()) { return method.Overrides.Any(x => x.GetElementMethod().AreSameIncludingDeclaringType(iMethod, resolver.Resolve)); } // Private methods cannot be an implicit implementation if (method.IsPrivate) return false; // Try implicit if (method.AreSame(iMethod, resolver.Resolve)) { // If the declaring class also has an explicit implementation of the method we have no match. // Otherwise we have a match. return !method.DeclaringType.HasExplicitImplementationOf(iMethod); } return false; }
/// <summary> /// Is the given method an explicit implementation of the given interface method. /// </summary> /// <param name="method">The method to investigate</param> /// <param name="iMethod">The interface method.</param> public static bool IsExplicitImplementationOf(this MethodDefinition method, MethodDefinition iMethod, GenericsResolver resolver = null) { resolver = resolver ?? new GenericsResolver(method.DeclaringType); if (method.IsExplicitImplementation()) { return method.Overrides.Any(x => x.GetElementMethod().AreSameIncludingDeclaringType(iMethod, resolver.Resolve)); } return false; }
/// <summary> /// Is the given method an implicit interface implementation? /// </summary> internal static bool IsImplicitImplementation(this MethodDefinition method) { if (method.IsPrivate) return false; var declaringType = method.DeclaringType; var resolver = new GenericsResolver(method.DeclaringType); return declaringType.GetImplementedInterfaces().OfType<TypeDefinition>().SelectMany(x => x.Methods).Any(x => x.AreSame(method, resolver.Resolve)); }
/// <summary> /// Try to resolve definition from reference. /// </summary> internal static MethodDefinition Resolve(this MethodReference method, ReachableContext context) { var declType = method.DeclaringType.Resolve(context); var resolver = new GenericsResolver(declType); return (declType == null) ? null : declType.Methods.FirstOrDefault(x => x.AreSame(method, resolver.Resolve)); }
/// <summary> /// Gets the first implementation of the given interface method in the given type. /// </summary> public static MethodDefinition GetImplementation(this MethodDefinition interfaceMethod, TypeDefinition type) { if (!interfaceMethod.DeclaringType.IsInterface) throw new ArgumentException("interfaceMethod"); var resolver = new GenericsResolver(type); while (type != null) { // Look for explicit implementation var result = type.Methods.FirstOrDefault(x => x.IsExplicitImplementationOf(interfaceMethod, resolver)); if (result != null) return result; // Look for implicit implementation result = type.Methods.FirstOrDefault(x => x.AreSame(interfaceMethod, resolver.Resolve)); if (result != null) return result; type = (type.BaseType != null) ? type.BaseType.Resolve() : null; } return null; }
/// <summary> /// Try to resolve definition from reference. /// </summary> internal static EventDefinition Resolve(this EventReference evt, ReachableContext context) { var declType = evt.DeclaringType.Resolve(context); var resolver = new GenericsResolver(declType); return (declType == null) ? null : declType.Events.FirstOrDefault(x => x.AreSame(evt, resolver.Resolve)); }
/// <summary> /// Try to resolve definition from reference. /// </summary> internal static PropertyDefinition Resolve(this PropertyReference prop, ReachableContext context) { var declType = prop.DeclaringType.Resolve(context); var resolver = new GenericsResolver(declType); return (declType == null) ? null : declType.Properties.FirstOrDefault(x => x.AreSame(prop, resolver.Resolve)); }
/// <summary> /// Is the given instruction a call to a ctor of the same class as the given ctor? /// </summary> private static bool IsCallToThisCtor(Instruction ins, MethodDefinition ctor) { if (ins.OpCode.Code != Mono.Cecil.Cil.Code.Call) return false; var ctorRef = ins.Operand as MethodReference; if (ctorRef == null) return false; if (ctorRef.Name != ".ctor") return false; var resolver = new GenericsResolver(ctor.DeclaringType); return ctorRef.DeclaringType.AreSame(ctor.DeclaringType, resolver.Resolve); }
/// <summary> /// Rename the given method and all references to it from code. /// </summary> private void Rename(MethodDefinition method, string newName) { // Rename reference to method foreach (var body in reachableMethods.Select(x => x.Body).Where(x => x != null)) { var resolver = new GenericsResolver(method.DeclaringType); foreach (var ins in body.Instructions.Where(x => x.Operand is MethodReference)) { var methodRef = ((MethodReference) ins.Operand).GetElementMethod(); if (!ReferenceEquals(methodRef, method) && methodRef.AreSameIncludingDeclaringType(method, resolver.Resolve)) { methodRef.Name = newName; } } } // Rename method itself method.Name = newName; }
/// <summary> /// Convert interface methods that have an explicit implementation. /// </summary> public void Convert(ReachableContext reachableContext) { this.reachableContext = reachableContext; // Do we need to convert anything? if (!reachableContext.ReachableTypes.SelectMany(x => x.Methods).Any(NeedsConversion)) return; // Initialize some sets reachableMethods = reachableContext.ReachableTypes.OrderBy(r=>r.FullName) // order,so we get a stable output. useful for debugging. .SelectMany(x => x.Methods) .Where(m => m.IsReachable) .ToList(); methodNames = new NameSet(reachableMethods.Select(m => m.Name)); interfaces = reachableContext.ReachableTypes.Where(x => x.IsInterface).ToList(); var interfaceToImplementingTypes = interfaces.ToDictionary(i=>i, i=>reachableContext.ReachableTypes .Where(x=>x.Implements(i)) .ToList()); // Go over all interfaces foreach (var iType in interfaces) { foreach (var iMethod in iType.Methods) { ConvertInterfaceMethod(iType, iMethod, interfaceToImplementingTypes); } } // Remove added stubs that are an override of another added stub. foreach (var stubPair in addedStubs) { var stub = stubPair.Item1; var oldName = stubPair.Item2; if (stub.GetBaseMethod() != null) { stub.DeclaringType.Methods.Remove(stub); } else { // Check for duplicate methods var resolver = new GenericsResolver(stub.DeclaringType); //var methodsWithSameName = stub.DeclaringType.Methods.Where(x => (x != stub) && (x.Name == stub.Name)).ToList(); //var duplicate = methodsWithSameName.FirstOrDefault(x => (x != stub) && x.AreSame(stub, resolver.Resolve)); var duplicate = stub.DeclaringType.Methods.FirstOrDefault(x => (x != stub) && x.AreSame(stub, resolver.Resolve)); if (duplicate != null) { stub.DeclaringType.Methods.Remove(stub); continue; } if (oldName != stub.Name) { var newName = stub.Name; stub.Name = oldName; duplicate = stub.DeclaringType.Methods.FirstOrDefault(x => (x != stub) && x.AreSame(stub, resolver.Resolve)); if (duplicate != null) { stub.DeclaringType.Methods.Remove(stub); continue; } stub.Name = newName; } } } }
/// <summary> /// Rename the given method and all references to it from code. /// </summary> private void Rename(MethodDefinition method, string newName) { methodReferences = methodReferences ?? InterfaceHelper.GetReachableMethodReferencesByName(reachableMethods); var resolver = new GenericsResolver(method.DeclaringType); foreach (var methodRef in methodReferences[method.Name]) { if (ReferenceEquals(method, methodRef)) continue; if (methodRef.AreSameIncludingDeclaringType(method, resolver.Resolve)) { methodRef.Name = newName; } } method.SetName(newName); }
/// <summary> /// Rename the given method and all references to it from code. /// /// Note that the passed lookup will not be accurate after completion of this method. /// If you rename methods multiple times, but know that you will rename each single method /// only once, this should not be a problem. /// </summary> public static void Rename(MethodDefinition method, string newName, ILookup<string, MethodReference> reachableMethodReferencesByMethodName) { var resolver = new GenericsResolver(method.DeclaringType); // Rename reference to method foreach (var methodRef in reachableMethodReferencesByMethodName[method.Name]) { if (!ReferenceEquals(methodRef, method) && methodRef.AreSameIncludingDeclaringType(method, resolver.Resolve)) { methodRef.Name = newName; } } // Rename method itself method.SetName(newName); }