// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team // // 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.ObjectModel; using System.Linq; using System.Text; using Mono.Cecil; namespace ICSharpCode.Decompiler.Ast { public static class TypesHierarchyHelpers { public static bool IsBaseType(TypeDefinition baseType, TypeDefinition derivedType, bool resolveTypeArguments) { if (resolveTypeArguments) return BaseTypes(derivedType).Any(t => t.Item == baseType); else { var comparableBaseType = baseType.ResolveOrThrow(); while (derivedType.BaseType != null) { var resolvedBaseType = derivedType.BaseType.ResolveOrThrow(); if (resolvedBaseType == null) return false; if (comparableBaseType == resolvedBaseType) return true; derivedType = resolvedBaseType; } return false; } } /// /// Determines whether one method overrides or hides another method. /// /// The method declared in a base type. /// The method declared in a derived type. /// true if hides or overrides , /// otherwise false. public static bool IsBaseMethod(MethodDefinition parentMethod, MethodDefinition childMethod) { if (parentMethod == null) throw new ArgumentNullException("parentMethod"); if (childMethod == null) throw new ArgumentNullException("childMethod"); if (parentMethod.Name != childMethod.Name) return false; if (parentMethod.HasParameters || childMethod.HasParameters) if (!parentMethod.HasParameters || !childMethod.HasParameters || parentMethod.Parameters.Count != childMethod.Parameters.Count) return false; return FindBaseMethods(childMethod).Any(m => m == parentMethod);// || (parentMethod.HasGenericParameters && m.); } /// /// Determines whether a property overrides or hides another property. /// /// The property declared in a base type. /// The property declared in a derived type. /// true if the hides or overrides , /// otherwise false. public static bool IsBaseProperty(PropertyDefinition parentProperty, PropertyDefinition childProperty) { if (parentProperty == null) throw new ArgumentNullException("parentProperty"); if (childProperty == null) throw new ArgumentNullException("childProperty"); if (parentProperty.Name != childProperty.Name) return false; if (parentProperty.HasParameters || childProperty.HasParameters) if (!parentProperty.HasParameters || !childProperty.HasParameters || parentProperty.Parameters.Count != childProperty.Parameters.Count) return false; return FindBaseProperties(childProperty).Any(m => m == parentProperty); } public static bool IsBaseEvent(EventDefinition parentEvent, EventDefinition childEvent) { if (parentEvent.Name != childEvent.Name) return false; return FindBaseEvents(childEvent).Any(m => m == parentEvent); } /// /// Finds all methods from base types overridden or hidden by the specified method. /// /// The method which overrides or hides methods from base types. /// Methods overriden or hidden by the specified method. public static IEnumerable FindBaseMethods(MethodDefinition method) { if (method == null) throw new ArgumentNullException("method"); var typeContext = CreateGenericContext(method.DeclaringType); var gMethod = typeContext.ApplyTo(method); foreach (var baseType in BaseTypes(method.DeclaringType)) foreach (var baseMethod in baseType.Item.Methods) if (MatchMethod(baseType.ApplyTo(baseMethod), gMethod) && IsVisibleFromDerived(baseMethod, method.DeclaringType)) { yield return baseMethod; if (baseMethod.IsNewSlot == baseMethod.IsVirtual) yield break; } } /// /// Finds all properties from base types overridden or hidden by the specified property. /// /// The property which overrides or hides properties from base types. /// Properties overriden or hidden by the specified property. public static IEnumerable FindBaseProperties(PropertyDefinition property) { if (property == null) throw new ArgumentNullException("property"); if ((property.GetMethod ?? property.SetMethod).HasOverrides) yield break; var typeContext = CreateGenericContext(property.DeclaringType); var gProperty = typeContext.ApplyTo(property); bool isIndexer = property.IsIndexer(); foreach (var baseType in BaseTypes(property.DeclaringType)) foreach (var baseProperty in baseType.Item.Properties) if (MatchProperty(baseType.ApplyTo(baseProperty), gProperty) && IsVisibleFromDerived(baseProperty, property.DeclaringType)) { if (isIndexer != baseProperty.IsIndexer()) continue; yield return baseProperty; var anyPropertyAccessor = baseProperty.GetMethod ?? baseProperty.SetMethod; if (anyPropertyAccessor.IsNewSlot == anyPropertyAccessor.IsVirtual) yield break; } } public static IEnumerable FindBaseEvents(EventDefinition eventDef) { if (eventDef == null) throw new ArgumentNullException("eventDef"); var typeContext = CreateGenericContext(eventDef.DeclaringType); var gEvent = typeContext.ApplyTo(eventDef); foreach (var baseType in BaseTypes(eventDef.DeclaringType)) foreach (var baseEvent in baseType.Item.Events) if (MatchEvent(baseType.ApplyTo(baseEvent), gEvent) && IsVisibleFromDerived(baseEvent, eventDef.DeclaringType)) { yield return baseEvent; var anyEventAccessor = baseEvent.AddMethod ?? baseEvent.RemoveMethod; if (anyEventAccessor.IsNewSlot == anyEventAccessor.IsVirtual) yield break; } } /// /// Determinates whether member of the base type is visible from a derived type. /// /// The member which visibility is checked. /// The derived type. /// true if the member is visible from derived type, othewise false. public static bool IsVisibleFromDerived(IMemberDefinition baseMember, TypeDefinition derivedType) { if (baseMember == null) throw new ArgumentNullException("baseMember"); if (derivedType == null) throw new ArgumentNullException("derivedType"); var visibility = IsVisibleFromDerived(baseMember); if (visibility.HasValue) return visibility.Value; if (baseMember.DeclaringType.Module == derivedType.Module) return true; // TODO: Check also InternalsVisibleToAttribute. return false; } private static bool? IsVisibleFromDerived(IMemberDefinition member) { MethodAttributes attrs = GetAccessAttributes(member) & MethodAttributes.MemberAccessMask; if (attrs == MethodAttributes.Private) return false; if (attrs == MethodAttributes.Assembly || attrs == MethodAttributes.FamANDAssem) return null; return true; } private static MethodAttributes GetAccessAttributes(IMemberDefinition member) { var fld = member as FieldDefinition; if (fld != null) return (MethodAttributes)fld.Attributes; var method = member as MethodDefinition; if (method != null) return method.Attributes; var prop = member as PropertyDefinition; if (prop != null) { return (prop.GetMethod ?? prop.SetMethod).Attributes; } var evnt = member as EventDefinition; if (evnt != null) { return (evnt.AddMethod ?? evnt.RemoveMethod).Attributes; } var nestedType = member as TypeDefinition; if (nestedType != null) { if (nestedType.IsNestedPrivate) return MethodAttributes.Private; if (nestedType.IsNestedAssembly || nestedType.IsNestedFamilyAndAssembly) return MethodAttributes.Assembly; return MethodAttributes.Public; } throw new NotSupportedException(); } private static bool MatchMethod(GenericContext candidate, GenericContext method) { var mCandidate = candidate.Item; var mMethod = method.Item; if (mCandidate.Name != mMethod.Name) return false; if (mCandidate.HasOverrides) return false; if (mCandidate.IsSpecialName != method.Item.IsSpecialName) return false; if (mCandidate.HasGenericParameters || mMethod.HasGenericParameters) { if (!mCandidate.HasGenericParameters || !mMethod.HasGenericParameters || mCandidate.GenericParameters.Count != mMethod.GenericParameters.Count) return false; } if (mCandidate.HasParameters || mMethod.HasParameters) { if (!mCandidate.HasParameters || !mMethod.HasParameters || mCandidate.Parameters.Count != mMethod.Parameters.Count) return false; for (int index = 0; index < mCandidate.Parameters.Count; index++) { if (!MatchParameters(candidate.ApplyTo(mCandidate.Parameters[index]), method.ApplyTo(mMethod.Parameters[index]))) return false; } } return true; } public static bool MatchInterfaceMethod(MethodDefinition candidate, MethodDefinition method, TypeReference interfaceContextType) { var candidateContext = CreateGenericContext(candidate.DeclaringType); var gCandidate = candidateContext.ApplyTo(candidate); if (interfaceContextType is GenericInstanceType) { var methodContext = new GenericContext(interfaceContextType.Resolve(), ((GenericInstanceType)interfaceContextType).GenericArguments); var gMethod = methodContext.ApplyTo(method); return MatchMethod(gCandidate, gMethod); } else { var methodContext = CreateGenericContext(interfaceContextType.Resolve()); var gMethod = candidateContext.ApplyTo(method); return MatchMethod(gCandidate, gMethod); } } private static bool MatchProperty(GenericContext candidate, GenericContext property) { var mCandidate = candidate.Item; var mProperty = property.Item; if (mCandidate.Name != mProperty.Name) return false; if ((mCandidate.GetMethod ?? mCandidate.SetMethod).HasOverrides) return false; if (mCandidate.HasParameters || mProperty.HasParameters) { if (!mCandidate.HasParameters || !mProperty.HasParameters || mCandidate.Parameters.Count != mProperty.Parameters.Count) return false; for (int index = 0; index < mCandidate.Parameters.Count; index++) { if (!MatchParameters(candidate.ApplyTo(mCandidate.Parameters[index]), property.ApplyTo(mProperty.Parameters[index]))) return false; } } return true; } private static bool MatchEvent(GenericContext candidate, GenericContext ev) { var mCandidate = candidate.Item; var mEvent = ev.Item; if (mCandidate.Name != mEvent.Name) return false; if ((mCandidate.AddMethod ?? mCandidate.RemoveMethod).HasOverrides) return false; if (!IsSameType(candidate.ResolveWithContext(mCandidate.EventType), ev.ResolveWithContext(mEvent.EventType))) return false; return true; } private static bool MatchParameters(GenericContext baseParameterType, GenericContext parameterType) { if (baseParameterType.Item.IsIn != parameterType.Item.IsIn || baseParameterType.Item.IsOut != parameterType.Item.IsOut) return false; var baseParam = baseParameterType.ResolveWithContext(baseParameterType.Item.ParameterType); var param = parameterType.ResolveWithContext(parameterType.Item.ParameterType); return IsSameType(baseParam, param); } private static bool IsSameType(TypeReference tr1, TypeReference tr2) { if (tr1 == tr2) return true; if (tr1 == null || tr2 == null) return false; if (tr1.GetType() != tr2.GetType()) return false; if (tr1.Name == tr2.Name && tr1.FullName == tr2.FullName) return true; return false; } private static IEnumerable> BaseTypes(TypeDefinition type) { return BaseTypes(CreateGenericContext(type)); } private static IEnumerable> BaseTypes(GenericContext type) { while (type.Item.BaseType != null) { var baseType = type.Item.BaseType; var genericBaseType = baseType as GenericInstanceType; if (genericBaseType != null) { type = new GenericContext(genericBaseType.ResolveOrThrow(), genericBaseType.GenericArguments.Select(t => type.ResolveWithContext(t))); } else type = new GenericContext(baseType.ResolveOrThrow()); yield return type; } } private static GenericContext CreateGenericContext(TypeDefinition type) { return type.HasGenericParameters ? new GenericContext(type, type.GenericParameters) : new GenericContext(type); } struct GenericContext where T : class { private static readonly ReadOnlyCollection Empty = new ReadOnlyCollection(new List()); private static readonly GenericParameter UnresolvedGenericTypeParameter = new DummyGenericParameterProvider(false).DummyParameter; private static readonly GenericParameter UnresolvedGenericMethodParameter = new DummyGenericParameterProvider(true).DummyParameter; public readonly T Item; public readonly ReadOnlyCollection TypeArguments; public GenericContext(T item) { if (item == null) throw new ArgumentNullException("item"); Item = item; TypeArguments = Empty; } public GenericContext(T item, IEnumerable typeArguments) { if (item == null) throw new ArgumentNullException("item"); Item = item; var list = new List(); foreach (var arg in typeArguments) { var resolved = arg != null ? arg.Resolve() : arg; list.Add(resolved != null ? resolved : arg); } TypeArguments = new ReadOnlyCollection(list); } private GenericContext(T item, ReadOnlyCollection typeArguments) { Item = item; TypeArguments = typeArguments; } public TypeReference ResolveWithContext(TypeReference type) { var genericParameter = type as GenericParameter; if (genericParameter != null) if (genericParameter.Owner.GenericParameterType == GenericParameterType.Type) return this.TypeArguments[genericParameter.Position]; else return genericParameter.Owner.GenericParameterType == GenericParameterType.Type ? UnresolvedGenericTypeParameter : UnresolvedGenericMethodParameter; var typeSpecification = type as TypeSpecification; if (typeSpecification != null) { var resolvedElementType = ResolveWithContext(typeSpecification.ElementType); return ReplaceElementType(typeSpecification, resolvedElementType); } return type.ResolveOrThrow(); } private TypeReference ReplaceElementType(TypeSpecification ts, TypeReference newElementType) { var arrayType = ts as ArrayType; if (arrayType != null) { if (newElementType == arrayType.ElementType) return arrayType; var newArrayType = new ArrayType(newElementType, arrayType.Rank); for (int dimension = 0; dimension < arrayType.Rank; dimension++) newArrayType.Dimensions[dimension] = arrayType.Dimensions[dimension]; return newArrayType; } var byReferenceType = ts as ByReferenceType; if (byReferenceType != null) { return new ByReferenceType(newElementType); } // TODO: should we throw an exception instead calling Resolve method? return ts.ResolveOrThrow(); } public GenericContext ApplyTo(T2 item) where T2 : class { return new GenericContext(item, this.TypeArguments); } private class DummyGenericParameterProvider : IGenericParameterProvider { readonly Mono.Cecil.GenericParameterType type; readonly Mono.Collections.Generic.Collection parameters; public DummyGenericParameterProvider(bool methodTypeParameter) { type = methodTypeParameter ? Mono.Cecil.GenericParameterType.Method : Mono.Cecil.GenericParameterType.Type; parameters = new Mono.Collections.Generic.Collection(1); parameters.Add(new GenericParameter(this)); } public GenericParameter DummyParameter { get { return parameters[0]; } } bool IGenericParameterProvider.HasGenericParameters { get { throw new NotImplementedException(); } } bool IGenericParameterProvider.IsDefinition { get { throw new NotImplementedException(); } } ModuleDefinition IGenericParameterProvider.Module { get { throw new NotImplementedException(); } } Mono.Collections.Generic.Collection IGenericParameterProvider.GenericParameters { get { return parameters; } } GenericParameterType IGenericParameterProvider.GenericParameterType { get { return type; } } MetadataToken IMetadataTokenProvider.MetadataToken { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } } } } }