From 368cac6c7f631e12a1293b20c76e815d9e8efdfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Artur=20Zgodzi=C5=84ski?= Date: Wed, 6 Apr 2011 18:26:33 +0200 Subject: [PATCH] Added support for new modifier (methods and properties only). --- ICSharpCode.Decompiler/Ast/AstBuilder.cs | 247 +++++++++++++++--- .../Tests/Types/S_TypeMemberDeclarations.cs | 243 ++++++++++++++++- 2 files changed, 449 insertions(+), 41 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index 832afe92f..d67128779 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.Linq; @@ -626,9 +627,13 @@ namespace ICSharpCode.Decompiler.Ast astMethod.Parameters.AddRange(MakeParameters(methodDef.Parameters)); astMethod.Constraints.AddRange(MakeConstraints(methodDef.GenericParameters)); if (!methodDef.DeclaringType.IsInterface) { - if (!methodDef.HasOverrides) + if (!methodDef.HasOverrides) { astMethod.Modifiers = ConvertModifiers(methodDef); - else + if (methodDef.IsVirtual ^ !methodDef.IsNewSlot) { + if (TypeMap.FindBaseMethods(methodDef).Any()) + astMethod.Modifiers |= Modifiers.New; + } + } else astMethod.PrivateImplementationType = ConvertType(methodDef.Overrides.First().DeclaringType); astMethod.Body = AstMethodBodyBuilder.CreateMethodBody(methodDef, context, astMethod.Parameters); } @@ -658,7 +663,7 @@ namespace ICSharpCode.Decompiler.Ast } return astMethod; } - + IEnumerable MakeTypeParameters(IEnumerable genericParameters) { foreach (var gp in genericParameters) { @@ -739,19 +744,17 @@ namespace ICSharpCode.Decompiler.Ast getterModifiers = ConvertModifiers(propDef.GetMethod); setterModifiers = ConvertModifiers(propDef.SetMethod); astProp.Modifiers = FixUpVisibility(getterModifiers | setterModifiers); - if (accessor.IsVirtual && !accessor.IsNewSlot && (propDef.GetMethod == null || propDef.SetMethod == null)) { - var baseType = propDef.DeclaringType.BaseType.Resolve(); - while (baseType != null && baseType.BaseType != null) { - var basePropDef = baseType.Properties.FirstOrDefault(pd => CouldOverride(pd, propDef)); - if (basePropDef != null) - if (basePropDef.GetMethod != null && basePropDef.SetMethod != null) { - var propVisibilityModifiers = ConvertModifiers(basePropDef.GetMethod) | ConvertModifiers(basePropDef.SetMethod); - astProp.Modifiers = FixUpVisibility((astProp.Modifiers & ~Modifiers.VisibilityMask) | (propVisibilityModifiers & Modifiers.VisibilityMask)); - break; - } else if ((basePropDef.GetMethod ?? basePropDef.SetMethod).IsNewSlot) - break; - baseType = baseType.BaseType.Resolve(); - } + if (accessor.IsVirtual && !accessor.IsNewSlot && (propDef.GetMethod == null || propDef.SetMethod == null)) + foreach (var basePropDef in TypeMap.FindBaseProperties(propDef)) + if (basePropDef.GetMethod != null && basePropDef.SetMethod != null) { + var propVisibilityModifiers = ConvertModifiers(basePropDef.GetMethod) | ConvertModifiers(basePropDef.SetMethod); + astProp.Modifiers = FixUpVisibility((astProp.Modifiers & ~Modifiers.VisibilityMask) | (propVisibilityModifiers & Modifiers.VisibilityMask)); + break; + } else if ((basePropDef.GetMethod ?? basePropDef.SetMethod).IsNewSlot) + break; + if (accessor.IsVirtual ^ !accessor.IsNewSlot) { + if (TypeMap.FindBaseProperties(propDef).Any()) + astProp.Modifiers |= Modifiers.New; } } astProp.Name = CleanName(propDef.Name); @@ -805,30 +808,6 @@ namespace ICSharpCode.Decompiler.Ast return astProp; } - bool CouldOverride(PropertyDefinition baseProperty, PropertyDefinition property) - { - if (baseProperty.Name != property.Name || baseProperty.PropertyType.Resolve() != property.PropertyType.Resolve()) - return false; - - if (baseProperty.HasParameters) { - if (!property.HasParameters) { - return false; - } - - var parameters1 = baseProperty.Parameters; - var parameters2 = property.Parameters; - if (parameters1.Count != parameters2.Count) - return false; - - for (int index = 0; index < parameters1.Count; index++) { - if (parameters1[index].ParameterType.Resolve() != parameters2[index].ParameterType.Resolve()) - return false; - } - } - - return true; - } - bool IsDefaultMemberAttribute(CustomAttribute ca) { return ca.AttributeType.Name == "DefaultMemberAttribute" && ca.AttributeType.Namespace == "System.Reflection"; @@ -1322,5 +1301,193 @@ namespace ICSharpCode.Decompiler.Ast return new Tuple(null, null); } + + class TypeMap + { + public static IEnumerable FindBaseMethods(MethodDefinition 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) && IsVisbleFrom(baseMethod, method)) { + yield return baseMethod; + if (!(baseMethod.IsNewSlot ^ baseMethod.IsVirtual)) + yield break; + } + } + + public static IEnumerable FindBaseProperties(PropertyDefinition property) + { + var typeContext = CreateGenericContext(property.DeclaringType); + var gProperty = typeContext.ApplyTo(property); + + foreach (var baseType in BaseTypes(property.DeclaringType)) + foreach (var baseProperty in baseType.Item.Properties) + if (MatchProperty(baseType.ApplyTo(baseProperty), gProperty) && IsVisbleFrom(baseProperty, property)) { + yield return baseProperty; + var anyPropertyAccessor = baseProperty.GetMethod ?? baseProperty.SetMethod; + if (!(anyPropertyAccessor.IsNewSlot ^ anyPropertyAccessor.IsVirtual)) + yield break; + } + + } + + private static bool IsVisbleFrom(MethodDefinition baseCandidate, MethodDefinition method) + { + if (baseCandidate.IsPrivate) + return false; + if ((baseCandidate.IsAssembly || baseCandidate.IsFamilyAndAssembly) && baseCandidate.Module != method.Module) + return false; + return true; + } + + private static bool IsVisbleFrom(PropertyDefinition baseCandidate, PropertyDefinition property) + { + if (baseCandidate.GetMethod != null && property.GetMethod != null && IsVisbleFrom(baseCandidate.GetMethod, property.GetMethod)) + return true; + if (baseCandidate.SetMethod != null && property.SetMethod != null && IsVisbleFrom(baseCandidate.SetMethod, property.SetMethod)) + return true; + return false; + } + + 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 (candidate.Resolve(mCandidate.ReturnType) != method.Resolve(mMethod.ReturnType)) + 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; + } + + 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 (candidate.Resolve(mCandidate.PropertyType) != property.Resolve(mProperty.PropertyType)) + 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 MatchParameters(GenericContext baseParameterType, GenericContext parameterType) + { + var baseParam = baseParameterType.Resolve(baseParameterType.Item.ParameterType); + var param = parameterType.Resolve(parameterType.Item.ParameterType); + return baseParam == param; + } + + 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.Resolve(), + genericBaseType.GenericArguments.Select(t => type.Resolve(t))); + } else + type = new GenericContext(baseType.Resolve()); + yield return type; + } + } + + private static GenericContext CreateGenericContext(TypeDefinition type) + { + return new GenericContext(type, type.GenericParameters); + } + + struct GenericContext + { + private static readonly ReadOnlyCollection Empty = new ReadOnlyCollection(new List()); + + public readonly T Item; + public readonly ReadOnlyCollection TypeArguments; + + public GenericContext(T item) + { + Item = item; + TypeArguments = Empty; + } + + public GenericContext(T item, IEnumerable typeArguments) + { + Item = item; + var list = new List(); + foreach (var arg in typeArguments) { + if (arg == null) + throw new ArgumentNullException("typeArguments"); + if (!arg.IsGenericParameter) + list.Add(arg.Resolve()); + else + list.Add(arg); + } + TypeArguments = new ReadOnlyCollection(list); + } + + private GenericContext(T item, ReadOnlyCollection typeArguments) + { + Item = item; + TypeArguments = typeArguments; + } + + public TypeReference Resolve(TypeReference type) + { + var genericParameter = type as GenericParameter; + if (genericParameter != null && genericParameter.Owner.GenericParameterType == GenericParameterType.Type) { + return this.TypeArguments[genericParameter.Position]; + } + return type.Resolve(); + } + + public GenericContext ApplyTo(T2 item) + { + return new GenericContext(item, this.TypeArguments); + } + } + } } } diff --git a/ICSharpCode.Decompiler/Tests/Types/S_TypeMemberDeclarations.cs b/ICSharpCode.Decompiler/Tests/Types/S_TypeMemberDeclarations.cs index 6feec5bc0..46b417cdb 100644 --- a/ICSharpCode.Decompiler/Tests/Types/S_TypeMemberDeclarations.cs +++ b/ICSharpCode.Decompiler/Tests/Types/S_TypeMemberDeclarations.cs @@ -374,7 +374,7 @@ namespace PropertyOverrideOneAccessor } public class DerivedNew : MyClass { - public virtual int MyProperty + public new virtual int MyProperty { set { @@ -428,3 +428,244 @@ namespace IndexerOverrideRestrictedAccessorOnly } } } +//$$ PropertyHiding +namespace PropertyHiding +{ + public class A + { + public virtual int P + { + get + { + return 0; + } + set + { + } + } + } + public class B : A + { + private new int P + { + get + { + return 0; + } + set + { + } + } + } + public class C : B + { + public override int P + { + set + { + } + } + } +} +//$$ IndexerHidingGeneric +namespace IndexerHidingGeneric +{ + public class A + { + public virtual int this[T r] + { + get + { + return 0; + } + set + { + } + } + } + public class B : A + { + private new int this[int k] + { + get + { + return 0; + } + set + { + } + } + } + public class C : A + { + public override int this[T s] + { + set + { + } + } + } + public class D : C + { + public new virtual int this[T s] + { + set + { + } + } + } +} +//$$ MethodHiding +namespace MethodHiding +{ + public class A + { + public virtual void F() + { + } + } + public class B : A + { + private new void F() + { + base.F(); + } + } + public class C : B + { + public override void F() + { + base.F(); + } + } +} +//$$ MethodHideGeneric +namespace MethodHideGeneric +{ + public class A + { + public virtual void F(T s) + { + } + public new static bool Equals(object o1, object o2) + { + return true; + } + } + public class B : A + { + private new void F(string k) + { + } + public void F(int i) + { + } + } + public class C : A + { + public override void F(T r) + { + } + public void G(T t) + { + } + } + public class D : C + { + public new virtual void F(T1 k) + { + } + public virtual void F(T2 k) + { + } + public virtual void G(T2 t) + { + } + } +} +//$$ MethodHideGenericSkipPrivate +namespace MethodHideGenericSkipPrivate +{ + public class A + { + public virtual void F(T t) + { + } + } + public class B : A + { + private new void F(T t) + { + } + private void K() + { + } + } + public class C : B + { + public override void F(T tt) + { + } + public void K() + { + } + } + public class D : B + { + public override void F(int t) + { + } + } +} +//$$ MethodHideGeneric2 +namespace MethodHideGeneric2 +{ + public class A + { + public virtual void F(int i) + { + } + public void K() + { + } + } + public class B : A + { + protected virtual void F(T t) + { + } + public void K() + { + } + } + public class C : B + { + protected override void F(int k) + { + } + public new void K() + { + } + } + public class D : B + { + public override void F(int k) + { + } + public void L() + { + } + } + public class E + { + public void M(T t, T2 t2) + { + } + } + public class F : E + { + public void M(T t1, T t2) + { + } + } +}