diff options
Diffstat (limited to 'ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs')
-rw-r--r-- | ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs | 528 |
1 files changed, 528 insertions, 0 deletions
diff --git a/ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs b/ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs new file mode 100644 index 00000000..e4131904 --- /dev/null +++ b/ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs @@ -0,0 +1,528 @@ +// 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.Resolve(); + if (comparableBaseType == null) + return false; + while (derivedType.BaseType != null) { + var resolvedBaseType = derivedType.BaseType.Resolve(); + if (resolvedBaseType == null) + return false; + if (comparableBaseType == resolvedBaseType) + return true; + derivedType = resolvedBaseType; + } + return false; + } + } + + /// <summary> + /// Determines whether one method overrides or hides another method. + /// </summary> + /// <param name="parentMethod">The method declared in a base type.</param> + /// <param name="childMethod">The method declared in a derived type.</param> + /// <returns>true if <paramref name="childMethod"/> hides or overrides <paramref name="parentMethod"/>, + /// otherwise false.</returns> + 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.); + } + + /// <summary> + /// Determines whether a property overrides or hides another property. + /// </summary> + /// <param name="parentProperty">The property declared in a base type.</param> + /// <param name="childProperty">The property declared in a derived type.</param> + /// <returns>true if the <paramref name="childProperty"/> hides or overrides <paramref name="parentProperty"/>, + /// otherwise false.</returns> + 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); + } + + /// <summary> + /// Finds all methods from base types overridden or hidden by the specified method. + /// </summary> + /// <param name="method">The method which overrides or hides methods from base types.</param> + /// <returns>Methods overriden or hidden by the specified method.</returns> + public static IEnumerable<MethodDefinition> 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; + } + } + + /// <summary> + /// Finds all properties from base types overridden or hidden by the specified property. + /// </summary> + /// <param name="property">The property which overrides or hides properties from base types.</param> + /// <returns>Properties overriden or hidden by the specified property.</returns> + public static IEnumerable<PropertyDefinition> 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<EventDefinition> 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; + } + + } + + /// <summary> + /// Determinates whether member of the base type is visible from a derived type. + /// </summary> + /// <param name="baseMember">The member which visibility is checked.</param> + /// <param name="derivedType">The derived type.</param> + /// <returns>true if the member is visible from derived type, othewise false.</returns> + public static bool IsVisibleFromDerived(IMemberDefinition baseMember, TypeDefinition derivedType) + { + if (baseMember == null) + throw new ArgumentNullException("baseMember"); + if (derivedType == null) + throw new ArgumentNullException("derivedType"); + + MethodAttributes attrs = GetAccessAttributes(baseMember) & MethodAttributes.MemberAccessMask; + if (attrs == MethodAttributes.Private) + return false; + + if (baseMember.DeclaringType.Module == derivedType.Module) + return true; + + if (attrs == MethodAttributes.Assembly || attrs == MethodAttributes.FamANDAssem) { + var derivedTypeAsm = derivedType.Module.Assembly; + var asm = baseMember.DeclaringType.Module.Assembly; + + if (asm.HasCustomAttributes) { + var attributes = asm.CustomAttributes + .Where(attr => attr.AttributeType.FullName == "System.Runtime.CompilerServices.InternalsVisibleToAttribute"); + foreach (var attribute in attributes) { + string assemblyName = attribute.ConstructorArguments[0].Value as string; + assemblyName = assemblyName.Split(',')[0]; // strip off any public key info + if (assemblyName == derivedTypeAsm.Name.Name) + return true; + } + } + + return false; + } + + return true; + } + + 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(); + } + + static bool MatchMethod(GenericContext<MethodDefinition> candidate, GenericContext<MethodDefinition> 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<TypeDefinition>(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); + } + } + + static bool MatchProperty(GenericContext<PropertyDefinition> candidate, GenericContext<PropertyDefinition> 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; + } + + static bool MatchEvent(GenericContext<EventDefinition> candidate, GenericContext<EventDefinition> 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; + } + + static bool MatchParameters(GenericContext<ParameterDefinition> baseParameterType, GenericContext<ParameterDefinition> 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); + } + + 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; + } + + static IEnumerable<GenericContext<TypeDefinition>> BaseTypes(TypeDefinition type) + { + return BaseTypes(CreateGenericContext(type)); + } + + static IEnumerable<GenericContext<TypeDefinition>> BaseTypes(GenericContext<TypeDefinition> type) + { + while (type.Item.BaseType != null) { + var baseType = type.Item.BaseType; + var genericBaseType = baseType as GenericInstanceType; + if (genericBaseType != null) { + type = new GenericContext<TypeDefinition>(genericBaseType.ResolveOrThrow(), + genericBaseType.GenericArguments.Select(t => type.ResolveWithContext(t))); + } else + type = new GenericContext<TypeDefinition>(baseType.ResolveOrThrow()); + yield return type; + } + } + + static GenericContext<TypeDefinition> CreateGenericContext(TypeDefinition type) + { + return type.HasGenericParameters + ? new GenericContext<TypeDefinition>(type, type.GenericParameters) + : new GenericContext<TypeDefinition>(type); + } + + struct GenericContext<T> where T : class + { + static readonly ReadOnlyCollection<TypeReference> Empty = new ReadOnlyCollection<TypeReference>(new List<TypeReference>()); + + static readonly GenericParameter UnresolvedGenericTypeParameter = + new DummyGenericParameterProvider(false).DummyParameter; + + static readonly GenericParameter UnresolvedGenericMethodParameter = + new DummyGenericParameterProvider(true).DummyParameter; + + public readonly T Item; + public readonly ReadOnlyCollection<TypeReference> TypeArguments; + + public GenericContext(T item) + { + if (item == null) + throw new ArgumentNullException("item"); + + Item = item; + TypeArguments = Empty; + } + + public GenericContext(T item, IEnumerable<TypeReference> typeArguments) + { + if (item == null) + throw new ArgumentNullException("item"); + + Item = item; + var list = new List<TypeReference>(); + foreach (var arg in typeArguments) { + var resolved = arg != null ? arg.Resolve() : arg; + list.Add(resolved != null ? resolved : arg); + } + TypeArguments = new ReadOnlyCollection<TypeReference>(list); + } + + GenericContext(T item, ReadOnlyCollection<TypeReference> 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 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(); + } + + 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<T2> ApplyTo<T2>(T2 item) where T2 : class + { + return new GenericContext<T2>(item, TypeArguments); + } + + class DummyGenericParameterProvider : IGenericParameterProvider + { + readonly GenericParameterType type; + readonly Mono.Collections.Generic.Collection<GenericParameter> parameters; + + public DummyGenericParameterProvider(bool methodTypeParameter) + { + type = methodTypeParameter ? GenericParameterType.Method : + GenericParameterType.Type; + parameters = new Mono.Collections.Generic.Collection<GenericParameter>(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<GenericParameter> IGenericParameterProvider.GenericParameters + { + get { return parameters; } + } + + GenericParameterType IGenericParameterProvider.GenericParameterType + { + get { return type; } + } + + MetadataToken IMetadataTokenProvider.MetadataToken + { + get + { + throw new NotImplementedException(); + } + set + { + throw new NotImplementedException(); + } + } + } + } + } +} |