Skip to content

Commit 4e9b707

Browse files
committed
add some test cases
1 parent 28b9a4e commit 4e9b707

File tree

5 files changed

+250
-68
lines changed

5 files changed

+250
-68
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// ReSharper disable once CheckNamespace
2+
namespace System.Collections.Generic
3+
{
4+
internal static class CollectionExtensions
5+
{
6+
#if NETSTANDARD2_0
7+
public static TValue GetValueOrDefault<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dictionary, TKey key, TValue defaultValue)
8+
{
9+
return dictionary.TryGetValue(key, out var obj)
10+
? obj
11+
: defaultValue;
12+
}
13+
#endif
14+
}
15+
}

src/AspectCore.Core/Extensions/MethodInfoExtensions.cs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,6 @@ public static IEnumerable<MethodInfo> GetInterfaceDeclarations(this MethodInfo m
2727
}
2828
}
2929

30-
public static bool IsOverriden(this MethodInfo method)
31-
{
32-
return method.GetBaseDefinition() != method;
33-
}
34-
3530
public static bool IsPreserveBaseOverride(this MethodInfo method, bool checkBase)
3631
{
3732
if (PreserveBaseOverridesAttribute is null)
@@ -55,6 +50,26 @@ public static bool IsPreserveBaseOverride(this MethodInfo method, bool checkBase
5550

5651
return false;
5752
}
53+
54+
public static IEnumerable<MethodInfo> EnumerateBaseDefinition(this MethodInfo method)
55+
{
56+
var m = method;
57+
while (true)
58+
{
59+
yield return m;
60+
61+
var b = m.GetBaseDefinition();
62+
if (b == m || b == null)
63+
yield break;
64+
65+
m = b;
66+
}
67+
}
68+
69+
public static bool EqualAnyBaseDefinitionTo(this MethodInfo method, MethodInfo other)
70+
{
71+
return method.EnumerateBaseDefinition().Any(m => m == other);
72+
}
5873
}
5974
}
6075

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reflection;
5+
6+
// ReSharper disable once CheckNamespace
7+
namespace AspectCore.Extensions
8+
{
9+
internal static class TypeExtensions
10+
{
11+
public readonly struct CovariantReturnMethodInfo
12+
{
13+
public readonly MethodInfo CovariantReturnMethod;
14+
public readonly MethodInfo OverridenMethod;
15+
public readonly HashSet<MethodInfo> InterfaceDeclarations;
16+
17+
public CovariantReturnMethodInfo(MethodInfo covariantReturnMethod, MethodInfo overridenMethod, HashSet<MethodInfo> interfaceDeclarations)
18+
{
19+
InterfaceDeclarations = interfaceDeclarations;
20+
OverridenMethod = overridenMethod;
21+
CovariantReturnMethod = covariantReturnMethod;
22+
}
23+
}
24+
25+
public static IReadOnlyList<CovariantReturnMethodInfo> GetCovariantReturnMethods(this Type type)
26+
{
27+
var result = new List<CovariantReturnMethodInfo>();
28+
// No PreserveBaseOverridesAttribute means that the runtime does not support covariant return types.
29+
if (MethodInfoExtensions.PreserveBaseOverridesAttribute is null)
30+
return result;
31+
32+
var methods = type
33+
.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
34+
.GroupBy(m => m.IsPreserveBaseOverride(true))
35+
.ToDictionary(m => m.Key, m => m.ToArray());
36+
37+
var covariantReturnMethods = methods.GetValueOrDefault(true, Array.Empty<MethodInfo>());
38+
var otherMethods = methods.GetValueOrDefault(false, Array.Empty<MethodInfo>());
39+
40+
foreach (var covariantReturnMethod in covariantReturnMethods)
41+
{
42+
var overridenMethod = otherMethods.FirstOrDefault(m => Match(covariantReturnMethod, m));
43+
if (overridenMethod is null)
44+
continue;
45+
46+
var interfaceDeclarations = covariantReturnMethod.GetInterfaceDeclarations().ToHashSet();
47+
result.Add(new CovariantReturnMethodInfo(covariantReturnMethod, overridenMethod, interfaceDeclarations));
48+
}
49+
50+
return result;
51+
52+
bool Match(MethodInfo covariantReturnMethod, MethodInfo other)
53+
{
54+
if (covariantReturnMethod.Name != other.Name)
55+
return false;
56+
57+
// return types should not be the same.
58+
if (covariantReturnMethod.ReturnType == other.ReturnType)
59+
return false;
60+
61+
if (other.ReturnType.IsAssignableFrom(covariantReturnMethod.ReturnType) == false)
62+
return false;
63+
64+
var params1 = covariantReturnMethod.GetParameters();
65+
var params2 = other.GetParameters();
66+
67+
if (params1.Length != params2.Length)
68+
return false;
69+
70+
foreach (var (p1, p2) in params1.Zip(params2))
71+
{
72+
if (p1.ParameterType != p2.ParameterType)
73+
return false;
74+
}
75+
76+
var isGeneric = covariantReturnMethod.IsGenericMethod;
77+
if (isGeneric != other.IsGenericMethod)
78+
return false;
79+
80+
if (isGeneric)
81+
{
82+
var args1 = covariantReturnMethod.GetGenericArguments();
83+
var args2 = other.GetGenericArguments();
84+
if (args1.Length != args2.Length)
85+
return false;
86+
87+
foreach (var (a1, a2) in args1.Zip(args2))
88+
{
89+
if (a1 != a2)
90+
return false;
91+
}
92+
}
93+
94+
return true;
95+
}
96+
}
97+
}
98+
}

src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs

Lines changed: 53 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -162,29 +162,6 @@ private Type CreateClassProxyInternal(string name, Type serviceType, Type implTy
162162
return typeDesc.Compile();
163163
}
164164

165-
// key: covariant return method
166-
// value: interface method declarations
167-
internal static IReadOnlyDictionary<MethodInfo, HashSet<MethodInfo>> GetCovariantReturnMethodMap(Type implType)
168-
{
169-
var result = new Dictionary<MethodInfo, HashSet<MethodInfo>>();
170-
// No PreserveBaseOverridesAttribute means that the runtime does not support covariant return types.
171-
if (AspectCore.Extensions.MethodInfoExtensions.PreserveBaseOverridesAttribute is null)
172-
return result;
173-
174-
var covariantReturnMethods = implType
175-
.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
176-
.Where(m => m.IsPreserveBaseOverride(true))
177-
.ToHashSet();
178-
179-
foreach (var method in covariantReturnMethods)
180-
{
181-
var interfaceDeclarations = method.GetInterfaceDeclarations().ToHashSet();
182-
result[method] = interfaceDeclarations;
183-
}
184-
185-
return result;
186-
}
187-
188165
private class ProxyNameUtils
189166
{
190167
private readonly Dictionary<string, ProxyNameIndex> _indexes = new Dictionary<string, ProxyNameIndex>();
@@ -415,26 +392,36 @@ internal static MethodBuilder DefineInterfaceImplMethod(MethodInfo method, TypeB
415392

416393
internal static void DefineInterfaceProxyMethods(Type interfaceType, Type targetType, Type[] additionalInterfaces, TypeDesc typeDesc)
417394
{
418-
var covariantReturnMethodMap = GetCovariantReturnMethodMap(targetType);
395+
var covariantReturnMethodMap = targetType.GetCovariantReturnMethods();
419396
foreach (var method in interfaceType.GetTypeInfo().DeclaredMethods.Where(x => !x.IsPropertyBinding()))
420397
{
421-
var covariantReturnMethod = covariantReturnMethodMap.FirstOrDefault(m => m.Value.Contains(method)).Key;
398+
var covariantReturnMethod = covariantReturnMethodMap
399+
.FirstOrDefault(m => m.InterfaceDeclarations.Contains(method))
400+
.CovariantReturnMethod;
401+
422402
DefineInterfaceMethod(method, targetType, typeDesc, covariantReturnMethod);
423403
}
424404
foreach (var item in additionalInterfaces)
425405
{
426406
foreach (var method in item.GetTypeInfo().DeclaredMethods.Where(x => !x.IsPropertyBinding()))
427407
{
428-
var covariantReturnMethod = covariantReturnMethodMap.FirstOrDefault(m => m.Value.Contains(method)).Key;
408+
var covariantReturnMethod = covariantReturnMethodMap
409+
.FirstOrDefault(m => m.InterfaceDeclarations.Contains(method))
410+
.CovariantReturnMethod;
411+
429412
DefineExplicitMethod(method, targetType, typeDesc, covariantReturnMethod);
430413
}
431414
}
432415
}
433416

434417
internal static void DefineClassProxyMethods(Type serviceType, Type implType, Type[] additionalInterfaces, TypeDesc typeDesc)
435418
{
419+
var covariantReturnMethodMap = implType.GetCovariantReturnMethods();
436420
foreach (var method in serviceType.GetTypeInfo().GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(x => !x.IsPropertyBinding()))
437421
{
422+
if (covariantReturnMethodMap.Any(m => m.OverridenMethod.EqualAnyBaseDefinitionTo(method)))
423+
continue;
424+
438425
if (method.IsVisibleAndVirtual() && !_ignores.Contains(method.Name))
439426
DefineClassMethod(method, implType, typeDesc);
440427
}
@@ -461,7 +448,7 @@ internal static MethodBuilder DefineExplicitMethod(MethodInfo method, Type implT
461448
return methodBuilder;
462449
}
463450

464-
internal static MethodBuilder DefineClassMethod(MethodInfo method, Type implType, TypeDesc typeDesc)
451+
internal static MethodBuilder DefineClassMethod(MethodInfo method, Type implType, TypeDesc typeDesc, bool isNewSlot = false)
465452
{
466453
var attributes = OverrideMethodAttributes;
467454

@@ -480,15 +467,22 @@ internal static MethodBuilder DefineClassMethod(MethodInfo method, Type implType
480467
attributes |= MethodAttributes.FamORAssem;
481468
}
482469

470+
if (isNewSlot)
471+
{
472+
attributes |= MethodAttributes.NewSlot;
473+
}
474+
483475
var methodBuilder = DefineMethod(method, method.Name, attributes, implType, typeDesc);
484476
return methodBuilder;
485477
}
486-
478+
487479
// NOTE: when a covariant return method is handling:
488480
// For class proxy: We just define the covariant return methods in the implementation type like normal methods, the CLR will handle the propagation. (in this case covariantReturnMethod is null)
489481
// For interface proxy: We need to use the covariant return methods as the interface methods' implementation. (in this case covariantReturnMethod is not null)
490482
private static MethodBuilder DefineMethod(MethodInfo method, string name, MethodAttributes attributes, Type implType, TypeDesc typeDesc, MethodInfo covariantReturnMethod = null)
491483
{
484+
var methodBuilder = typeDesc.Builder.DefineMethod(name, attributes, method.CallingConvention, method.ReturnType, method.GetParameterTypes());
485+
492486
var implementationMethod = covariantReturnMethod ?? implType.GetTypeInfo().GetMethodBySignature(method);
493487
if (implementationMethod == null)
494488
{
@@ -520,20 +514,6 @@ private static MethodBuilder DefineMethod(MethodInfo method, string name, Method
520514
}
521515
}
522516

523-
// NOTE: both covariant return method and its corresponding overridden method should be defined with NewSlot attribute.
524-
if (method.IsPreserveBaseOverride(true))
525-
{
526-
// PreserveBaseOverridesAttribute is used to indicate that the method is a covariant return method.
527-
attributes |= MethodAttributes.NewSlot;
528-
}
529-
else if (implementationMethod.Attributes.HasFlag(MethodAttributes.NewSlot) && implementationMethod.IsOverriden())
530-
{
531-
// an overridden method with NewSlot attribute is a method overriden by a covariant return method.
532-
attributes |= MethodAttributes.NewSlot;
533-
}
534-
535-
var methodBuilder = typeDesc.Builder.DefineMethod(name, attributes, method.CallingConvention, method.ReturnType, method.GetParameterTypes());
536-
537517
GenericParameterUtils.DefineGenericParameter(method, methodBuilder);
538518

539519
//define method attributes
@@ -542,6 +522,9 @@ private static MethodBuilder DefineMethod(MethodInfo method, string name, Method
542522
//inherit targetMethod's attribute
543523
foreach (var customAttributeData in method.CustomAttributes)
544524
{
525+
if (customAttributeData.AttributeType == AspectCore.Extensions.MethodInfoExtensions.PreserveBaseOverridesAttribute)
526+
continue;
527+
545528
methodBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(customAttributeData));
546529
}
547530

@@ -777,13 +760,13 @@ private class PropertyBuilderUtils
777760
{
778761
public static void DefineInterfaceProxyProperties(Type interfaceType, Type implType, Type[] additionalInterfaces, TypeDesc typeDesc)
779762
{
780-
var covariantReturnMethodMap = GetCovariantReturnMethodMap(implType);
763+
var covariantReturnMethodMap = implType.GetCovariantReturnMethods();
781764

782765
foreach (var property in interfaceType.GetTypeInfo().DeclaredProperties)
783766
{
784767
var builder = DefineInterfaceProxyProperty(property, property.Name, implType, typeDesc);
785768
var covariantReturnGetter = property.CanRead
786-
? covariantReturnMethodMap.FirstOrDefault(m => m.Value.Contains(property.GetMethod)).Key
769+
? covariantReturnMethodMap.FirstOrDefault(m => m.InterfaceDeclarations.Contains(property.GetMethod)).CovariantReturnMethod
787770
: null;
788771
DefineInterfacePropertyMethod(builder, property, implType, typeDesc, covariantReturnGetter);
789772
}
@@ -793,7 +776,7 @@ public static void DefineInterfaceProxyProperties(Type interfaceType, Type implT
793776
{
794777
var builder = DefineInterfaceProxyProperty(property, property.GetDisplayName(), implType, typeDesc);
795778
var covariantReturnGetter = property.CanRead
796-
? covariantReturnMethodMap.FirstOrDefault(m => m.Value.Contains(property.GetMethod)).Key
779+
? covariantReturnMethodMap.FirstOrDefault(m => m.InterfaceDeclarations.Contains(property.GetMethod)).CovariantReturnMethod
797780
: null;
798781
DefineExplicitPropertyMethod(builder, property, implType, typeDesc, covariantReturnGetter);
799782
}
@@ -802,12 +785,31 @@ public static void DefineInterfaceProxyProperties(Type interfaceType, Type implT
802785

803786
internal static void DefineClassProxyProperties(Type serviceType, Type implType, Type[] additionalInterfaces, TypeDesc typeDesc)
804787
{
788+
var covariantReturnMethodMap = implType.GetCovariantReturnMethods();
789+
805790
foreach (var property in serviceType.GetTypeInfo().GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
806791
{
792+
var isNewSlot = false;
793+
794+
if (property.CanRead)
795+
{
796+
// skip if the property is overridden by a covariant return method
797+
if (covariantReturnMethodMap.Any(m => m.CovariantReturnMethod.EqualAnyBaseDefinitionTo(property.GetMethod)
798+
&& m.OverridenMethod.ReturnType == property.PropertyType))
799+
continue;
800+
801+
if (covariantReturnMethodMap.Any(m => m.CovariantReturnMethod.EqualAnyBaseDefinitionTo(property.GetMethod)
802+
&& m.CovariantReturnMethod.ReturnType == property.PropertyType))
803+
{
804+
// this property's getter is a covariant return method.
805+
isNewSlot = true;
806+
}
807+
}
808+
807809
if (property.IsVisibleAndVirtual())
808810
{
809811
var builder = DefineInterfaceProxyProperty(property, property.Name, implType, typeDesc);
810-
DefineClassPropertyMethod(builder, property, implType, typeDesc);
812+
DefineClassPropertyMethod(builder, property, implType, typeDesc, isNewSlot);
811813
}
812814
}
813815
foreach (var item in additionalInterfaces)
@@ -820,11 +822,11 @@ internal static void DefineClassProxyProperties(Type serviceType, Type implType,
820822
}
821823
}
822824

823-
private static void DefineClassPropertyMethod(PropertyBuilder propertyBuilder, PropertyInfo property, Type implType, TypeDesc typeDesc)
825+
private static void DefineClassPropertyMethod(PropertyBuilder propertyBuilder, PropertyInfo property, Type implType, TypeDesc typeDesc, bool isNewSlot = false)
824826
{
825827
if (property.CanRead)
826828
{
827-
var method = MethodBuilderUtils.DefineClassMethod(property.GetMethod, implType, typeDesc);
829+
var method = MethodBuilderUtils.DefineClassMethod(property.GetMethod, implType, typeDesc, isNewSlot);
828830
propertyBuilder.SetGetMethod(method);
829831
}
830832
if (property.CanWrite)
@@ -1133,13 +1135,13 @@ internal static void DefineGenericParameter(Type targetType, TypeBuilder typeBui
11331135
}
11341136
}
11351137

1136-
internal static void DefineGenericParameter(MethodInfo tergetMethod, MethodBuilder methodBuilder)
1138+
internal static void DefineGenericParameter(MethodInfo targetMethod, MethodBuilder methodBuilder)
11371139
{
1138-
if (!tergetMethod.IsGenericMethod)
1140+
if (!targetMethod.IsGenericMethod)
11391141
{
11401142
return;
11411143
}
1142-
var genericArguments = tergetMethod.GetGenericArguments().Select(t => t.GetTypeInfo()).ToArray();
1144+
var genericArguments = targetMethod.GetGenericArguments().Select(t => t.GetTypeInfo()).ToArray();
11431145
var genericArgumentsBuilders = methodBuilder.DefineGenericParameters(genericArguments.Select(a => a.Name).ToArray());
11441146
for (var index = 0; index < genericArguments.Length; index++)
11451147
{

0 commit comments

Comments
 (0)