From 4b87f8d4c45976891c9963b3725a1281da182313 Mon Sep 17 00:00:00 2001 From: huoshan12345 Date: Sat, 19 Jul 2025 00:07:51 +0800 Subject: [PATCH 01/19] fix typo --- .../InterceptorAttributeWithArrayMemberTests.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/AspectCore.Extensions.DependencyInjection.Test/Issues/InterceptorAttributeWithArrayMemberTests.cs b/tests/AspectCore.Extensions.DependencyInjection.Test/Issues/InterceptorAttributeWithArrayMemberTests.cs index 29267e8d..48c0954f 100644 --- a/tests/AspectCore.Extensions.DependencyInjection.Test/Issues/InterceptorAttributeWithArrayMemberTests.cs +++ b/tests/AspectCore.Extensions.DependencyInjection.Test/Issues/InterceptorAttributeWithArrayMemberTests.cs @@ -29,20 +29,20 @@ public override async Task Invoke(AspectContext context, AspectDelegate next) public interface IUserAppService { - int ExcuteTimes { get; } + int ExecuteTimes { get; } - [Test(Times = new int[] { 10, 100 })] + [Test(Times = new[] { 10, 100 })] string DisplayName(string firstName, string lastName); } public class UserAppService : IUserAppService { - private int _excuteTimes; - public int ExcuteTimes => _excuteTimes; + private int _executeTimes; + public int ExecuteTimes => _executeTimes; public string DisplayName(string firstName, string lastName) { - Interlocked.Increment(ref _excuteTimes); + Interlocked.Increment(ref _executeTimes); var fullName = $"{firstName} {lastName}"; return fullName; } @@ -59,7 +59,7 @@ public void InterceptorAttributeWithArrayMember_Property_Test() var usrAppSrv = sp.GetRequiredService(); var name = usrAppSrv.DisplayName("gain", "loss"); Assert.Equal("gain loss", name); - Assert.Equal(10 + 100, usrAppSrv.ExcuteTimes); + Assert.Equal(10 + 100, usrAppSrv.ExecuteTimes); } } } From 3623e9c3249e2179f18dc1b45e5efdc2520b1817 Mon Sep 17 00:00:00 2001 From: huoshan12345 Date: Sat, 19 Jul 2025 00:32:09 +0800 Subject: [PATCH 02/19] add test cases --- .../DynamicProxy/CovariantReturnTypesTests.cs | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 tests/AspectCore.Tests/DynamicProxy/CovariantReturnTypesTests.cs diff --git a/tests/AspectCore.Tests/DynamicProxy/CovariantReturnTypesTests.cs b/tests/AspectCore.Tests/DynamicProxy/CovariantReturnTypesTests.cs new file mode 100644 index 00000000..8e4da9ff --- /dev/null +++ b/tests/AspectCore.Tests/DynamicProxy/CovariantReturnTypesTests.cs @@ -0,0 +1,49 @@ +using AspectCore.DynamicProxy; +using Xunit; + +namespace AspectCore.Tests.DynamicProxy; + +public class CovariantReturnTypesTests : DynamicProxyTestBase +{ + public interface IService + { + object Method(); + object Property { get; } + } + + public class Service : IService + { + public virtual object Method() => nameof(Service); + public virtual object Property { get; } = nameof(Service); + } + + public class CovariantReturnsService : Service + { + public override string Method() => nameof(CovariantReturnsService); + public override string Property { get; } = nameof(CovariantReturnsService); + } + + [Fact] + public void CreateClassProxy_CovariantReturns_Test() + { + var service = ProxyGenerator.CreateClassProxy(); + Assert.Equal(nameof(CovariantReturnsService), service.Method()); + Assert.Equal(nameof(CovariantReturnsService), service.Property); + } + + [Fact] + public void CreateClassProxy_WithServiceType_CovariantReturns_Test() + { + var service = ProxyGenerator.CreateClassProxy(); + Assert.Equal(nameof(CovariantReturnsService), service.Method()); + Assert.Equal(nameof(CovariantReturnsService), service.Property); + } + + [Fact] + public void CreateInterfaceProxy_CovariantReturns_Test() + { + var service = ProxyGenerator.CreateInterfaceProxy(); + Assert.Equal(nameof(CovariantReturnsService), service.Method()); + Assert.Equal(nameof(CovariantReturnsService), service.Property); + } +} From 4d508bd3d82bcdbb948a4b395af2583d16c69092 Mon Sep 17 00:00:00 2001 From: huoshan12345 Date: Sat, 19 Jul 2025 02:03:35 +0800 Subject: [PATCH 03/19] add NewSlot attribute --- src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs | 14 +++++++++++--- .../DynamicProxy/CovariantReturnTypesTests.cs | 7 ++++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs index 0c635387..c9ca63e7 100644 --- a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs +++ b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs @@ -441,17 +441,22 @@ internal static MethodBuilder DefineClassMethod(MethodInfo method, Type implType if (method.Attributes.HasFlag(MethodAttributes.Public)) { - attributes = attributes | MethodAttributes.Public; + attributes |= MethodAttributes.Public; } if (method.Attributes.HasFlag(MethodAttributes.Family)) { - attributes = attributes | MethodAttributes.Family; + attributes |= MethodAttributes.Family; } if (method.Attributes.HasFlag(MethodAttributes.FamORAssem)) { - attributes = attributes | MethodAttributes.FamORAssem; + attributes |= MethodAttributes.FamORAssem; + } + + if (method.Attributes.HasFlag(MethodAttributes.NewSlot)) + { + attributes |= MethodAttributes.NewSlot; } var methodBuilder = DefineMethod(method, method.Name, attributes, implType, typeDesc); @@ -470,6 +475,9 @@ private static MethodBuilder DefineMethod(MethodInfo method, string name, Method //inherit targetMethod's attribute foreach (var customAttributeData in method.CustomAttributes) { + if (customAttributeData.AttributeType.Name == "PreserveBaseOverridesAttribute") + continue; // Skip PreserveBaseOverridesAttribute as it is not needed in dynamic proxy generation. + methodBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(customAttributeData)); } diff --git a/tests/AspectCore.Tests/DynamicProxy/CovariantReturnTypesTests.cs b/tests/AspectCore.Tests/DynamicProxy/CovariantReturnTypesTests.cs index 8e4da9ff..84d88f1f 100644 --- a/tests/AspectCore.Tests/DynamicProxy/CovariantReturnTypesTests.cs +++ b/tests/AspectCore.Tests/DynamicProxy/CovariantReturnTypesTests.cs @@ -1,4 +1,5 @@ -using AspectCore.DynamicProxy; +using System.Reflection; +using AspectCore.DynamicProxy; using Xunit; namespace AspectCore.Tests.DynamicProxy; @@ -13,8 +14,8 @@ public interface IService public class Service : IService { - public virtual object Method() => nameof(Service); - public virtual object Property { get; } = nameof(Service); + public virtual object Method() => new(); + public virtual object Property { get; } = new(); } public class CovariantReturnsService : Service From ee52db7cb84f5b3cdf87f538c40248489de0e9a7 Mon Sep 17 00:00:00 2001 From: huoshan12345 Date: Sun, 20 Jul 2025 17:28:48 +0800 Subject: [PATCH 04/19] support interface proxy --- .../Extensions/EnumerableExtensions.cs | 23 +++++++++ .../Extensions/MethodInfoExtensions.cs | 28 +++++++++++ ...oxyGeneratorUtils.CovariantReturnMethod.cs | 42 ++++++++++++++++ .../Utils/ProxyGeneratorUtils.cs | 49 +++++++++++-------- ...Tests.cs => CovariantReturnMethodTests.cs} | 6 +-- 5 files changed, 125 insertions(+), 23 deletions(-) create mode 100644 src/AspectCore.Core/Extensions/EnumerableExtensions.cs create mode 100644 src/AspectCore.Core/Extensions/MethodInfoExtensions.cs create mode 100644 src/AspectCore.Core/Utils/ProxyGeneratorUtils.CovariantReturnMethod.cs rename tests/AspectCore.Tests/DynamicProxy/{CovariantReturnTypesTests.cs => CovariantReturnMethodTests.cs} (89%) diff --git a/src/AspectCore.Core/Extensions/EnumerableExtensions.cs b/src/AspectCore.Core/Extensions/EnumerableExtensions.cs new file mode 100644 index 00000000..40ac4fe6 --- /dev/null +++ b/src/AspectCore.Core/Extensions/EnumerableExtensions.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using System.Linq; + +// ReSharper disable once CheckNamespace +namespace AspectCore.Extensions +{ + internal static class EnumerableExtensions + { +#if NETSTANDARD2_0 || NETSTANDARD2_1 + public static IEnumerable<(TFirst First, TSecond Second)> Zip(this IEnumerable first, IEnumerable second) + { + return first.Zip(second, (f, s) => (f, s)); + } +#endif + +#if NETSTANDARD2_0 + public static HashSet ToHashSet(this IEnumerable source, IEqualityComparer comparer = null) + { + return new HashSet(source, comparer); + } +#endif + } +} \ No newline at end of file diff --git a/src/AspectCore.Core/Extensions/MethodInfoExtensions.cs b/src/AspectCore.Core/Extensions/MethodInfoExtensions.cs new file mode 100644 index 00000000..201cfd60 --- /dev/null +++ b/src/AspectCore.Core/Extensions/MethodInfoExtensions.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +// ReSharper disable once CheckNamespace +namespace AspectCore.Extensions +{ + internal static class MethodInfoExtensions + { + public static IEnumerable GetInterfaceDeclarationsForMethod(this MethodInfo method) + { + var typeInfo = method.ReflectedType?.GetTypeInfo(); + if (typeInfo is null) + yield break; + + foreach (var implementedInterface in typeInfo.ImplementedInterfaces) + { + var map = typeInfo.GetInterfaceMap(implementedInterface); + foreach (var (interfaceMethod, targetMethod) in map.InterfaceMethods.Zip(map.TargetMethods)) + { + if (targetMethod == method) + yield return interfaceMethod; + } + } + } + } +} + diff --git a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.CovariantReturnMethod.cs b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.CovariantReturnMethod.cs new file mode 100644 index 00000000..ab90639e --- /dev/null +++ b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.CovariantReturnMethod.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using AspectCore.Extensions; + +// ReSharper disable once CheckNamespace +namespace AspectCore.Utils +{ + // NOTE: + // For class proxy: We just define the covariant return methods in the implementation type like normal methods, the CLR will handle the propagation. + // For interface proxy: We need to use the covariant return methods as the interface methods' implementation. + internal partial class ProxyGeneratorUtils + { + private static readonly Type PreserveBaseOverridesAttribute = Type.GetType("System.Runtime.CompilerServices.PreserveBaseOverridesAttribute", false); + + // key: covariant return method + // value: overridden method's interface declarations + internal static IReadOnlyDictionary> GetCovariantReturnMethodMap(Type implType) + { + var result = new Dictionary>(); + // No PreserveBaseOverridesAttribute means that the runtime does not support covariant return types. + if (PreserveBaseOverridesAttribute is null) + return result; + + const MethodAttributes attributes = MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.NewSlot; + var covariantReturnMethods = implType + .GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + .Where(m => (m.Attributes & attributes) == attributes) + .Where(m => m.IsDefined(PreserveBaseOverridesAttribute)) + .ToHashSet(); + + foreach (var method in covariantReturnMethods) + { + var interfaceDeclarations = method.GetInterfaceDeclarationsForMethod().ToHashSet(); + result[method] = interfaceDeclarations; + } + + return result; + } + } +} \ No newline at end of file diff --git a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs index c9ca63e7..443c11f5 100644 --- a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs +++ b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs @@ -14,7 +14,7 @@ namespace AspectCore.Utils { - internal class ProxyGeneratorUtils + internal partial class ProxyGeneratorUtils { private const string ProxyNameSpace = "AspectCore.DynamicGenerated"; private const string ProxyAssemblyName = "AspectCore.DynamicProxy.Generator"; @@ -392,15 +392,18 @@ internal static MethodBuilder DefineInterfaceImplMethod(MethodInfo method, TypeB internal static void DefineInterfaceProxyMethods(Type interfaceType, Type targetType, Type[] additionalInterfaces, TypeDesc typeDesc) { + var covariantReturnMethodMap = GetCovariantReturnMethodMap(targetType); foreach (var method in interfaceType.GetTypeInfo().DeclaredMethods.Where(x => !x.IsPropertyBinding())) { - DefineInterfaceMethod(method, targetType, typeDesc); + var covariantReturnMethod = covariantReturnMethodMap.FirstOrDefault(m => m.Value.Contains(method)).Key; + DefineInterfaceMethod(method, targetType, typeDesc, covariantReturnMethod); } foreach (var item in additionalInterfaces) { foreach (var method in item.GetTypeInfo().DeclaredMethods.Where(x => !x.IsPropertyBinding())) { - DefineExplicitMethod(method, targetType, typeDesc); + var covariantReturnMethod = covariantReturnMethodMap.FirstOrDefault(m => m.Value.Contains(method)).Key; + DefineExplicitMethod(method, targetType, typeDesc, covariantReturnMethod); } } } @@ -421,16 +424,16 @@ internal static void DefineClassProxyMethods(Type serviceType, Type implType, Ty } } - internal static MethodBuilder DefineInterfaceMethod(MethodInfo method, Type implType, TypeDesc typeDesc) + internal static MethodBuilder DefineInterfaceMethod(MethodInfo method, Type implType, TypeDesc typeDesc, MethodInfo covariantReturnMethod = null) { - var methodBuilder = DefineMethod(method, method.Name, InterfaceMethodAttributes, implType, typeDesc); + var methodBuilder = DefineMethod(method, method.Name, InterfaceMethodAttributes, implType, typeDesc, covariantReturnMethod); typeDesc.Builder.DefineMethodOverride(methodBuilder, method); return methodBuilder; } - internal static MethodBuilder DefineExplicitMethod(MethodInfo method, Type implType, TypeDesc typeDesc) + internal static MethodBuilder DefineExplicitMethod(MethodInfo method, Type implType, TypeDesc typeDesc, MethodInfo covariantReturnMethod = null) { - var methodBuilder = DefineMethod(method, method.GetName(), ExplicitMethodAttributes, implType, typeDesc); + var methodBuilder = DefineMethod(method, method.GetName(), ExplicitMethodAttributes, implType, typeDesc, covariantReturnMethod); typeDesc.Builder.DefineMethodOverride(methodBuilder, method); return methodBuilder; } @@ -454,6 +457,7 @@ internal static MethodBuilder DefineClassMethod(MethodInfo method, Type implType attributes |= MethodAttributes.FamORAssem; } + // NewSlot is required for covariant return types. if (method.Attributes.HasFlag(MethodAttributes.NewSlot)) { attributes |= MethodAttributes.NewSlot; @@ -463,7 +467,7 @@ internal static MethodBuilder DefineClassMethod(MethodInfo method, Type implType return methodBuilder; } - private static MethodBuilder DefineMethod(MethodInfo method, string name, MethodAttributes attributes, Type implType, TypeDesc typeDesc) + private static MethodBuilder DefineMethod(MethodInfo method, string name, MethodAttributes attributes, Type implType, TypeDesc typeDesc, MethodInfo covariantReturnMethod = null) { var methodBuilder = typeDesc.Builder.DefineMethod(name, attributes, method.CallingConvention, method.ReturnType, method.GetParameterTypes()); @@ -475,16 +479,13 @@ private static MethodBuilder DefineMethod(MethodInfo method, string name, Method //inherit targetMethod's attribute foreach (var customAttributeData in method.CustomAttributes) { - if (customAttributeData.AttributeType.Name == "PreserveBaseOverridesAttribute") - continue; // Skip PreserveBaseOverridesAttribute as it is not needed in dynamic proxy generation. - methodBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(customAttributeData)); } //define paramters ParameterBuilderUtils.DefineParameters(method, methodBuilder); - var implementationMethod = implType.GetTypeInfo().GetMethodBySignature(method); + var implementationMethod = covariantReturnMethod ?? implType.GetTypeInfo().GetMethodBySignature(method); if (implementationMethod == null) { var interfaces = implType.GetInterfaces(); @@ -616,7 +617,7 @@ void EmitProxyMethodBody() ilGen.Emit(OpCodes.Callvirt, MethodUtils.CreateAspectActivator); ilGen.Emit(OpCodes.Ldloc, activatorContext); - EmitReturnVaule(ilGen); + EmitReturnValue(ilGen); if (method.ReturnType != typeof(void)) { @@ -707,7 +708,7 @@ void EmitInitializeMetaData(ILGenerator ilGen) } } - void EmitReturnVaule(ILGenerator ilGen) + void EmitReturnValue(ILGenerator ilGen) { if (method.ReturnType == typeof(void)) { @@ -744,17 +745,25 @@ private class PropertyBuilderUtils { public static void DefineInterfaceProxyProperties(Type interfaceType, Type implType, Type[] additionalInterfaces, TypeDesc typeDesc) { + var covariantReturnMethodMap = GetCovariantReturnMethodMap(implType); + foreach (var property in interfaceType.GetTypeInfo().DeclaredProperties) { var builder = DefineInterfaceProxyProperty(property, property.Name, implType, typeDesc); - DefineInterfacePropertyMethod(builder, property, implType, typeDesc); + var covariantReturnGetter = property.CanRead + ? covariantReturnMethodMap.FirstOrDefault(m => m.Value.Contains(property.GetMethod)).Key + : null; + DefineInterfacePropertyMethod(builder, property, implType, typeDesc, covariantReturnGetter); } foreach (var item in additionalInterfaces) { foreach (var property in item.GetTypeInfo().DeclaredProperties) { var builder = DefineInterfaceProxyProperty(property, property.GetDisplayName(), implType, typeDesc); - DefineExplicitPropertyMethod(builder, property, implType, typeDesc); + var covariantReturnGetter = property.CanRead + ? covariantReturnMethodMap.FirstOrDefault(m => m.Value.Contains(property.GetMethod)).Key + : null; + DefineExplicitPropertyMethod(builder, property, implType, typeDesc, covariantReturnGetter); } } } @@ -793,11 +802,11 @@ private static void DefineClassPropertyMethod(PropertyBuilder propertyBuilder, P } } - private static void DefineInterfacePropertyMethod(PropertyBuilder propertyBuilder, PropertyInfo property, Type implType, TypeDesc typeDesc) + private static void DefineInterfacePropertyMethod(PropertyBuilder propertyBuilder, PropertyInfo property, Type implType, TypeDesc typeDesc, MethodInfo covariantReturnGetter = null) { if (property.CanRead) { - var method = MethodBuilderUtils.DefineInterfaceMethod(property.GetMethod, implType, typeDesc); + var method = MethodBuilderUtils.DefineInterfaceMethod(property.GetMethod, implType, typeDesc, covariantReturnGetter); propertyBuilder.SetGetMethod(method); } if (property.CanWrite) @@ -807,11 +816,11 @@ private static void DefineInterfacePropertyMethod(PropertyBuilder propertyBuilde } } - private static void DefineExplicitPropertyMethod(PropertyBuilder propertyBuilder, PropertyInfo property, Type implType, TypeDesc typeDesc) + private static void DefineExplicitPropertyMethod(PropertyBuilder propertyBuilder, PropertyInfo property, Type implType, TypeDesc typeDesc, MethodInfo covariantReturnGetter = null) { if (property.CanRead) { - var method = MethodBuilderUtils.DefineExplicitMethod(property.GetMethod, implType, typeDesc); + var method = MethodBuilderUtils.DefineExplicitMethod(property.GetMethod, implType, typeDesc, covariantReturnGetter); propertyBuilder.SetGetMethod(method); } if (property.CanWrite) diff --git a/tests/AspectCore.Tests/DynamicProxy/CovariantReturnTypesTests.cs b/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs similarity index 89% rename from tests/AspectCore.Tests/DynamicProxy/CovariantReturnTypesTests.cs rename to tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs index 84d88f1f..a38892a8 100644 --- a/tests/AspectCore.Tests/DynamicProxy/CovariantReturnTypesTests.cs +++ b/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs @@ -1,10 +1,10 @@ -using System.Reflection; +using System.Linq; using AspectCore.DynamicProxy; using Xunit; namespace AspectCore.Tests.DynamicProxy; -public class CovariantReturnTypesTests : DynamicProxyTestBase +public class CovariantReturnMethodTests : DynamicProxyTestBase { public interface IService { @@ -20,7 +20,7 @@ public class Service : IService public class CovariantReturnsService : Service { - public override string Method() => nameof(CovariantReturnsService); + public sealed override string Method() => nameof(CovariantReturnsService); public override string Property { get; } = nameof(CovariantReturnsService); } From 4e3c5c12abc6280a2f8950980ce60993e5967af3 Mon Sep 17 00:00:00 2001 From: huoshan12345 Date: Sun, 20 Jul 2025 17:32:33 +0800 Subject: [PATCH 05/19] fix comments --- src/AspectCore.Core/Extensions/MethodInfoExtensions.cs | 2 +- .../Utils/ProxyGeneratorUtils.CovariantReturnMethod.cs | 4 ++-- .../DynamicProxy/CovariantReturnMethodTests.cs | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/AspectCore.Core/Extensions/MethodInfoExtensions.cs b/src/AspectCore.Core/Extensions/MethodInfoExtensions.cs index 201cfd60..99bf1659 100644 --- a/src/AspectCore.Core/Extensions/MethodInfoExtensions.cs +++ b/src/AspectCore.Core/Extensions/MethodInfoExtensions.cs @@ -7,7 +7,7 @@ namespace AspectCore.Extensions { internal static class MethodInfoExtensions { - public static IEnumerable GetInterfaceDeclarationsForMethod(this MethodInfo method) + public static IEnumerable GetInterfaceDeclarations(this MethodInfo method) { var typeInfo = method.ReflectedType?.GetTypeInfo(); if (typeInfo is null) diff --git a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.CovariantReturnMethod.cs b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.CovariantReturnMethod.cs index ab90639e..c2e14843 100644 --- a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.CovariantReturnMethod.cs +++ b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.CovariantReturnMethod.cs @@ -15,7 +15,7 @@ internal partial class ProxyGeneratorUtils private static readonly Type PreserveBaseOverridesAttribute = Type.GetType("System.Runtime.CompilerServices.PreserveBaseOverridesAttribute", false); // key: covariant return method - // value: overridden method's interface declarations + // value: interface method declarations internal static IReadOnlyDictionary> GetCovariantReturnMethodMap(Type implType) { var result = new Dictionary>(); @@ -32,7 +32,7 @@ internal static IReadOnlyDictionary> GetCovarian foreach (var method in covariantReturnMethods) { - var interfaceDeclarations = method.GetInterfaceDeclarationsForMethod().ToHashSet(); + var interfaceDeclarations = method.GetInterfaceDeclarations().ToHashSet(); result[method] = interfaceDeclarations; } diff --git a/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs b/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs index a38892a8..4575e4cc 100644 --- a/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs +++ b/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs @@ -1,5 +1,4 @@ -using System.Linq; -using AspectCore.DynamicProxy; +using AspectCore.DynamicProxy; using Xunit; namespace AspectCore.Tests.DynamicProxy; From 75bf6056747960b0800be6c8f5497cb187128d4d Mon Sep 17 00:00:00 2001 From: huoshan12345 Date: Sun, 20 Jul 2025 17:53:41 +0800 Subject: [PATCH 06/19] fix method attribute --- .../Utils/ProxyGeneratorUtils.cs | 14 ++++----- ...ttribute.cs => AsyncIncrementAttribute.cs} | 2 +- .../AsyncInterceptorTests.cs | 28 ++++++++--------- .../RegistryTests.cs | 6 ++-- .../AsyncInterceptorTests.cs | 30 +++++++++---------- 5 files changed, 40 insertions(+), 40 deletions(-) rename tests/AspectCore.Extensions.LightInject.Test/{AsyncIncreamentAttribute.cs => AsyncIncrementAttribute.cs} (93%) diff --git a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs index 443c11f5..f03c4a1a 100644 --- a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs +++ b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs @@ -164,16 +164,16 @@ private Type CreateClassProxyInternal(string name, Type serviceType, Type implTy private class ProxyNameUtils { - private readonly Dictionary _indexs = new Dictionary(); + private readonly Dictionary _indexes = new Dictionary(); private readonly Dictionary, string> _indexMaps = new Dictionary, string>(); private string GetProxyTypeIndex(string className, Type serviceType, Type implementationType) { ProxyNameIndex nameIndex; - if (!_indexs.TryGetValue(className, out nameIndex)) + if (!_indexes.TryGetValue(className, out nameIndex)) { nameIndex = new ProxyNameIndex(); - _indexs[className] = nameIndex; + _indexes[className] = nameIndex; } var key = Tuple.Create(serviceType, implementationType); string index; @@ -360,7 +360,7 @@ private class MethodBuilderUtils const MethodAttributes ExplicitMethodAttributes = MethodAttributes.Private | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual; internal const MethodAttributes InterfaceMethodAttributes = MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual; const MethodAttributes OverrideMethodAttributes = MethodAttributes.HideBySig | MethodAttributes.Virtual; - private static readonly HashSet ignores = new HashSet { "Finalize" }; + private static readonly HashSet _ignores = new HashSet { "Finalize" }; internal static void DefineInterfaceImplMethods(Type[] interfaceTypes, TypeBuilder implTypeBuilder) { @@ -412,7 +412,7 @@ internal static void DefineClassProxyMethods(Type serviceType, Type implType, Ty { foreach (var method in serviceType.GetTypeInfo().GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(x => !x.IsPropertyBinding())) { - if (method.IsVisibleAndVirtual() && !ignores.Contains(method.Name)) + if (method.IsVisibleAndVirtual() && !_ignores.Contains(method.Name)) DefineClassMethod(method, implType, typeDesc); } foreach (var item in additionalInterfaces) @@ -458,7 +458,7 @@ internal static MethodBuilder DefineClassMethod(MethodInfo method, Type implType } // NewSlot is required for covariant return types. - if (method.Attributes.HasFlag(MethodAttributes.NewSlot)) + if (method.IsDefined(PreserveBaseOverridesAttribute) && method.Attributes.HasFlag(MethodAttributes.NewSlot)) { attributes |= MethodAttributes.NewSlot; } @@ -482,7 +482,7 @@ private static MethodBuilder DefineMethod(MethodInfo method, string name, Method methodBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(customAttributeData)); } - //define paramters + //define parameters ParameterBuilderUtils.DefineParameters(method, methodBuilder); var implementationMethod = covariantReturnMethod ?? implType.GetTypeInfo().GetMethodBySignature(method); diff --git a/tests/AspectCore.Extensions.LightInject.Test/AsyncIncreamentAttribute.cs b/tests/AspectCore.Extensions.LightInject.Test/AsyncIncrementAttribute.cs similarity index 93% rename from tests/AspectCore.Extensions.LightInject.Test/AsyncIncreamentAttribute.cs rename to tests/AspectCore.Extensions.LightInject.Test/AsyncIncrementAttribute.cs index 055e270d..f70dae3b 100644 --- a/tests/AspectCore.Extensions.LightInject.Test/AsyncIncreamentAttribute.cs +++ b/tests/AspectCore.Extensions.LightInject.Test/AsyncIncrementAttribute.cs @@ -5,7 +5,7 @@ namespace AspectCoreTest.LightInject { [AttributeUsage(AttributeTargets.Method)] - public class AsyncIncreamentAttribute : AbstractInterceptorAttribute + public class AsyncIncrementAttribute : AbstractInterceptorAttribute { public override async Task Invoke(AspectContext context, AspectDelegate next) { diff --git a/tests/AspectCore.Extensions.LightInject.Test/AsyncInterceptorTests.cs b/tests/AspectCore.Extensions.LightInject.Test/AsyncInterceptorTests.cs index d9e8456f..183f0120 100644 --- a/tests/AspectCore.Extensions.LightInject.Test/AsyncInterceptorTests.cs +++ b/tests/AspectCore.Extensions.LightInject.Test/AsyncInterceptorTests.cs @@ -8,31 +8,31 @@ namespace AspectCoreTest.LightInject { public class AsyncService { - [AsyncIncreament] - public virtual void DonotGet(int num) + [AsyncIncrement] + public virtual void DoNotGet(int num) { } - [AsyncIncreament] - public virtual Task DonotGetAsync(int num) + [AsyncIncrement] + public virtual Task DoNotGetAsync(int num) { return Task.CompletedTask; } - [AsyncIncreament] + [AsyncIncrement] public virtual int Get(int num) { return num; } - [AsyncIncreament] + [AsyncIncrement] public virtual async Task GetAsyncWithTask(int num) { await Task.Delay(100); return num; } - [AsyncIncreament] + [AsyncIncrement] public virtual async ValueTask GetAsyncWithValueTask(int num) { await Task.Delay(100); @@ -59,25 +59,25 @@ private static IServiceContainer CreateContainer() [Theory] [MemberData(nameof(GetNumbers))] - public void TestIncreamentForVoid(int input) + public void TestIncrementForVoid(int input) { var container = CreateContainer(); var service = container.GetInstance(); - service.DonotGet(input); + service.DoNotGet(input); } [Theory] [MemberData(nameof(GetNumbers))] - public async Task TestIncreamentForTask(int input) + public async Task TestIncrementForTask(int input) { var container = CreateContainer(); var service = container.GetInstance(); - await service.DonotGetAsync(input); + await service.DoNotGetAsync(input); } [Theory] [MemberData(nameof(GetNumbers))] - public void TestIncreamentForResult(int input) + public void TestIncrementForResult(int input) { var container = CreateContainer(); var service = container.GetInstance(); @@ -86,7 +86,7 @@ public void TestIncreamentForResult(int input) [Theory] [MemberData(nameof(GetNumbers))] - public async Task TestIncreamentForTaskResult(int input) + public async Task TestIncrementForTaskResult(int input) { var container = CreateContainer(); var service = container.GetInstance(); @@ -95,7 +95,7 @@ public async Task TestIncreamentForTaskResult(int input) [Theory] [MemberData(nameof(GetNumbers))] - public async Task TestIncreamentForValueTaskResult(int input) + public async Task TestIncrementForValueTaskResult(int input) { var container = CreateContainer(); var service = container.GetInstance(); diff --git a/tests/AspectCore.Extensions.LightInject.Test/RegistryTests.cs b/tests/AspectCore.Extensions.LightInject.Test/RegistryTests.cs index dd6f3a5a..47a23b22 100644 --- a/tests/AspectCore.Extensions.LightInject.Test/RegistryTests.cs +++ b/tests/AspectCore.Extensions.LightInject.Test/RegistryTests.cs @@ -15,12 +15,12 @@ public class RegistryTests public interface IService { - [AsyncIncreament] + [AsyncIncrement] int Foo(); } public class Service : IService { - [AsyncIncreament] + [AsyncIncrement] public virtual int Foo() => Result; } public class ServiceWithRef : IService @@ -32,7 +32,7 @@ public ServiceWithRef(IService service) _service = service; } - [AsyncIncreament] + [AsyncIncrement] public virtual int Foo() => _service.Foo(); } diff --git a/tests/AspectCore.Extensions.Windsor.Test/AsyncInterceptorTests.cs b/tests/AspectCore.Extensions.Windsor.Test/AsyncInterceptorTests.cs index 3deff0fe..f7967611 100644 --- a/tests/AspectCore.Extensions.Windsor.Test/AsyncInterceptorTests.cs +++ b/tests/AspectCore.Extensions.Windsor.Test/AsyncInterceptorTests.cs @@ -10,7 +10,7 @@ namespace AspectCoreTest.Windsor { [AttributeUsage(AttributeTargets.Method)] - public class AsyncIncreamentAttribute : AbstractInterceptorAttribute + public class AsyncIncrementAttribute : AbstractInterceptorAttribute { public override async Task Invoke(AspectContext context, AspectDelegate next) { @@ -36,31 +36,31 @@ public override async Task Invoke(AspectContext context, AspectDelegate next) public class AsyncService { - [AsyncIncreament] - public virtual void DonotGet(int num) + [AsyncIncrement] + public virtual void DoNotGet(int num) { } - [AsyncIncreament] - public virtual Task DonotGetAsync(int num) + [AsyncIncrement] + public virtual Task DoNotGetAsync(int num) { return Task.CompletedTask; } - [AsyncIncreament] + [AsyncIncrement] public virtual int Get(int num) { return num; } - [AsyncIncreament] + [AsyncIncrement] public virtual async Task GetAsyncWithTask(int num) { await Task.Delay(100); return num; } - [AsyncIncreament] + [AsyncIncrement] public virtual async ValueTask GetAsyncWithValueTask(int num) { await Task.Delay(100); @@ -86,25 +86,25 @@ private static IWindsorContainer CreateWindsorContainer() [Theory] [MemberData(nameof(GetNumbers))] - public void TestIncreamentForVoid(int input) + public void TestIncrementForVoid(int input) { var container = CreateWindsorContainer(); var service = container.Resolve(); - service.DonotGet(input); + service.DoNotGet(input); } [Theory] [MemberData(nameof(GetNumbers))] - public async Task TestIncreamentForTask(int input) + public async Task TestIncrementForTask(int input) { var container = CreateWindsorContainer(); var service = container.Resolve(); - await service.DonotGetAsync(input); + await service.DoNotGetAsync(input); } [Theory] [MemberData(nameof(GetNumbers))] - public void TestIncreamentForResult(int input) + public void TestIncrementForResult(int input) { var container = CreateWindsorContainer(); var service = container.Resolve(); @@ -113,7 +113,7 @@ public void TestIncreamentForResult(int input) [Theory] [MemberData(nameof(GetNumbers))] - public async Task TestIncreamentForTaskResult(int input) + public async Task TestIncrementForTaskResult(int input) { var container = CreateWindsorContainer(); var service = container.Resolve(); @@ -122,7 +122,7 @@ public async Task TestIncreamentForTaskResult(int input) [Theory] [MemberData(nameof(GetNumbers))] - public async Task TestIncreamentForValueTaskResult(int input) + public async Task TestIncrementForValueTaskResult(int input) { var container = CreateWindsorContainer(); var service = container.Resolve(); From bd11d184a843cc139704bf24b3eda1dadb33d1b6 Mon Sep 17 00:00:00 2001 From: huoshan12345 Date: Sun, 20 Jul 2025 19:42:49 +0800 Subject: [PATCH 07/19] fix test cases --- .../Extensions/EnumerableExtensions.cs | 3 +- .../Extensions/MethodInfoExtensions.cs | 34 ++++++++++++- ...oxyGeneratorUtils.CovariantReturnMethod.cs | 11 ++-- .../Utils/ProxyGeneratorUtils.cs | 51 +++++++++++-------- .../MethodSignature.cs | 10 ++-- .../CovariantReturnMethodTests.cs | 45 ++++++++++++++-- 6 files changed, 115 insertions(+), 39 deletions(-) diff --git a/src/AspectCore.Core/Extensions/EnumerableExtensions.cs b/src/AspectCore.Core/Extensions/EnumerableExtensions.cs index 40ac4fe6..0706d89f 100644 --- a/src/AspectCore.Core/Extensions/EnumerableExtensions.cs +++ b/src/AspectCore.Core/Extensions/EnumerableExtensions.cs @@ -1,8 +1,7 @@ using System.Collections.Generic; -using System.Linq; // ReSharper disable once CheckNamespace -namespace AspectCore.Extensions +namespace System.Linq { internal static class EnumerableExtensions { diff --git a/src/AspectCore.Core/Extensions/MethodInfoExtensions.cs b/src/AspectCore.Core/Extensions/MethodInfoExtensions.cs index 99bf1659..9e4a109c 100644 --- a/src/AspectCore.Core/Extensions/MethodInfoExtensions.cs +++ b/src/AspectCore.Core/Extensions/MethodInfoExtensions.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -7,6 +8,8 @@ namespace AspectCore.Extensions { internal static class MethodInfoExtensions { + public static readonly Type PreserveBaseOverridesAttribute = Type.GetType("System.Runtime.CompilerServices.PreserveBaseOverridesAttribute", false); + public static IEnumerable GetInterfaceDeclarations(this MethodInfo method) { var typeInfo = method.ReflectedType?.GetTypeInfo(); @@ -23,6 +26,35 @@ public static IEnumerable GetInterfaceDeclarations(this MethodInfo m } } } + + public static bool IsOverriden(this MethodInfo method) + { + return method.GetBaseDefinition() != method; + } + + public static bool IsPreserveBaseOverride(this MethodInfo method, bool checkBase) + { + if (PreserveBaseOverridesAttribute is null) + return false; + + var m = method; + while (true) + { + if (m.IsDefined(PreserveBaseOverridesAttribute)) + return true; + + if (checkBase == false) + break; + + var b = m.GetBaseDefinition(); + if (b == m || b == null) + break; + + m = b; + } + + return false; + } } } diff --git a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.CovariantReturnMethod.cs b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.CovariantReturnMethod.cs index c2e14843..793d83b5 100644 --- a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.CovariantReturnMethod.cs +++ b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.CovariantReturnMethod.cs @@ -12,22 +12,23 @@ namespace AspectCore.Utils // For interface proxy: We need to use the covariant return methods as the interface methods' implementation. internal partial class ProxyGeneratorUtils { - private static readonly Type PreserveBaseOverridesAttribute = Type.GetType("System.Runtime.CompilerServices.PreserveBaseOverridesAttribute", false); - // key: covariant return method // value: interface method declarations internal static IReadOnlyDictionary> GetCovariantReturnMethodMap(Type implType) { var result = new Dictionary>(); // No PreserveBaseOverridesAttribute means that the runtime does not support covariant return types. - if (PreserveBaseOverridesAttribute is null) + if (AspectCore.Extensions.MethodInfoExtensions.PreserveBaseOverridesAttribute is null) return result; + + var methods = implType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + .ToHashSet(); + const MethodAttributes attributes = MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.NewSlot; var covariantReturnMethods = implType .GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) - .Where(m => (m.Attributes & attributes) == attributes) - .Where(m => m.IsDefined(PreserveBaseOverridesAttribute)) + .Where(m => m.IsPreserveBaseOverride(true)) .ToHashSet(); foreach (var method in covariantReturnMethods) diff --git a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs index f03c4a1a..6add3016 100644 --- a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs +++ b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using AspectCore.DynamicProxy; +using AspectCore.Extensions; using AspectCore.Extensions.Reflection; using AspectCore.Extensions.Reflection.Emit; @@ -457,34 +458,12 @@ internal static MethodBuilder DefineClassMethod(MethodInfo method, Type implType attributes |= MethodAttributes.FamORAssem; } - // NewSlot is required for covariant return types. - if (method.IsDefined(PreserveBaseOverridesAttribute) && method.Attributes.HasFlag(MethodAttributes.NewSlot)) - { - attributes |= MethodAttributes.NewSlot; - } - var methodBuilder = DefineMethod(method, method.Name, attributes, implType, typeDesc); return methodBuilder; } private static MethodBuilder DefineMethod(MethodInfo method, string name, MethodAttributes attributes, Type implType, TypeDesc typeDesc, MethodInfo covariantReturnMethod = null) { - var methodBuilder = typeDesc.Builder.DefineMethod(name, attributes, method.CallingConvention, method.ReturnType, method.GetParameterTypes()); - - GenericParameterUtils.DefineGenericParameter(method, methodBuilder); - - //define method attributes - methodBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(typeof(DynamicallyAttribute))); - - //inherit targetMethod's attribute - foreach (var customAttributeData in method.CustomAttributes) - { - methodBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(customAttributeData)); - } - - //define parameters - ParameterBuilderUtils.DefineParameters(method, methodBuilder); - var implementationMethod = covariantReturnMethod ?? implType.GetTypeInfo().GetMethodBySignature(method); if (implementationMethod == null) { @@ -516,6 +495,34 @@ private static MethodBuilder DefineMethod(MethodInfo method, string name, Method } } + // NOTE: both covariant return method and its corresponding overridden method should be defined with NewSlot attribute. + if (method.IsPreserveBaseOverride(true)) + { + // PreserveBaseOverridesAttribute is used to indicate that the method is a covariant return method. + attributes |= MethodAttributes.NewSlot; + } + else if (implementationMethod.Attributes.HasFlag(MethodAttributes.NewSlot) && implementationMethod.IsOverriden()) + { + // an overridden method with NewSlot attribute is a method overriden covariant return method. + attributes |= MethodAttributes.NewSlot; + } + + var methodBuilder = typeDesc.Builder.DefineMethod(name, attributes, method.CallingConvention, method.ReturnType, method.GetParameterTypes()); + + GenericParameterUtils.DefineGenericParameter(method, methodBuilder); + + //define method attributes + methodBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(typeof(DynamicallyAttribute))); + + //inherit targetMethod's attribute + foreach (var customAttributeData in method.CustomAttributes) + { + methodBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(customAttributeData)); + } + + //define parameters + ParameterBuilderUtils.DefineParameters(method, methodBuilder); + if (method.IsNonAspect()) { EmitMethodBody(); diff --git a/src/AspectCore.Extensions.Reflection/MethodSignature.cs b/src/AspectCore.Extensions.Reflection/MethodSignature.cs index 07688452..52d6439b 100644 --- a/src/AspectCore.Extensions.Reflection/MethodSignature.cs +++ b/src/AspectCore.Extensions.Reflection/MethodSignature.cs @@ -63,19 +63,19 @@ private static int GetSignatureCode(Pair pair) if (parameterTypes.Length > 0) { signatureCode = (signatureCode * 397) ^ parameterTypes.Length.GetHashCode(); - foreach (var paramterType in parameterTypes) + foreach (var parameterType in parameterTypes) { - if (paramterType.IsGenericParameter) + if (parameterType.IsGenericParameter) { continue; } - else if (paramterType.GetTypeInfo().IsGenericType) + else if (parameterType.GetTypeInfo().IsGenericType) { - signatureCode = GetSignatureCode(signatureCode, paramterType); + signatureCode = GetSignatureCode(signatureCode, parameterType); } else { - signatureCode = (signatureCode * 397) ^ paramterType.GetHashCode(); + signatureCode = (signatureCode * 397) ^ parameterType.GetHashCode(); } } } diff --git a/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs b/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs index 4575e4cc..177c4374 100644 --- a/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs +++ b/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs @@ -19,12 +19,17 @@ public class Service : IService public class CovariantReturnsService : Service { - public sealed override string Method() => nameof(CovariantReturnsService); + public override string Method() => nameof(CovariantReturnsService); public override string Property { get; } = nameof(CovariantReturnsService); } + public class DerivedCovariantReturnsService : CovariantReturnsService + { + public override string Method() => nameof(DerivedCovariantReturnsService); + } + [Fact] - public void CreateClassProxy_CovariantReturns_Test() + public void CreateClassProxy_CovariantReturnsService_Test() { var service = ProxyGenerator.CreateClassProxy(); Assert.Equal(nameof(CovariantReturnsService), service.Method()); @@ -32,7 +37,15 @@ public void CreateClassProxy_CovariantReturns_Test() } [Fact] - public void CreateClassProxy_WithServiceType_CovariantReturns_Test() + public void CreateClassProxy_DerivedCovariantReturnsService_Test() + { + var service = ProxyGenerator.CreateClassProxy(); + Assert.Equal(nameof(DerivedCovariantReturnsService), service.Method()); + Assert.Equal(nameof(CovariantReturnsService), service.Property); + } + + [Fact] + public void CreateClassProxy_Service_CovariantReturnsService_Test() { var service = ProxyGenerator.CreateClassProxy(); Assert.Equal(nameof(CovariantReturnsService), service.Method()); @@ -40,10 +53,34 @@ public void CreateClassProxy_WithServiceType_CovariantReturns_Test() } [Fact] - public void CreateInterfaceProxy_CovariantReturns_Test() + public void CreateClassProxy_Service_DerivedCovariantReturnsService_Test() + { + var service = ProxyGenerator.CreateClassProxy(); + Assert.Equal(nameof(DerivedCovariantReturnsService), service.Method()); + Assert.Equal(nameof(CovariantReturnsService), service.Property); + } + + [Fact] + public void CreateClassProxy_CovariantReturnsService_DerivedCovariantReturnsService_Test() + { + var service = ProxyGenerator.CreateClassProxy(); + Assert.Equal(nameof(DerivedCovariantReturnsService), service.Method()); + Assert.Equal(nameof(CovariantReturnsService), service.Property); + } + + [Fact] + public void CreateInterfaceProxy_IService_CovariantReturnsService_Test() { var service = ProxyGenerator.CreateInterfaceProxy(); Assert.Equal(nameof(CovariantReturnsService), service.Method()); Assert.Equal(nameof(CovariantReturnsService), service.Property); } + + [Fact] + public void CreateInterfaceProxy_IService_DerivedCovariantReturnsService_Test() + { + var service = ProxyGenerator.CreateInterfaceProxy(); + Assert.Equal(nameof(DerivedCovariantReturnsService), service.Method()); + Assert.Equal(nameof(CovariantReturnsService), service.Property); + } } From 4401de76e614f9e9c53dafe4d78b0e9aee733806 Mon Sep 17 00:00:00 2001 From: huoshan12345 Date: Sun, 20 Jul 2025 19:45:11 +0800 Subject: [PATCH 08/19] remove unused code --- .../Utils/ProxyGeneratorUtils.CovariantReturnMethod.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.CovariantReturnMethod.cs b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.CovariantReturnMethod.cs index 793d83b5..415eb162 100644 --- a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.CovariantReturnMethod.cs +++ b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.CovariantReturnMethod.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Reflection; using AspectCore.Extensions; +using static AspectCore.Extensions.MethodInfoExtensions; // ReSharper disable once CheckNamespace namespace AspectCore.Utils @@ -18,14 +19,9 @@ internal static IReadOnlyDictionary> GetCovarian { var result = new Dictionary>(); // No PreserveBaseOverridesAttribute means that the runtime does not support covariant return types. - if (AspectCore.Extensions.MethodInfoExtensions.PreserveBaseOverridesAttribute is null) + if (PreserveBaseOverridesAttribute is null) return result; - - var methods = implType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) - .ToHashSet(); - - const MethodAttributes attributes = MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.NewSlot; var covariantReturnMethods = implType .GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .Where(m => m.IsPreserveBaseOverride(true)) From a92c66507ee909b6f219e00ee4dc2ca827c91789 Mon Sep 17 00:00:00 2001 From: huoshan12345 Date: Sun, 20 Jul 2025 20:03:08 +0800 Subject: [PATCH 09/19] fix comment --- src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs index 6add3016..5b13da2c 100644 --- a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs +++ b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs @@ -503,7 +503,7 @@ private static MethodBuilder DefineMethod(MethodInfo method, string name, Method } else if (implementationMethod.Attributes.HasFlag(MethodAttributes.NewSlot) && implementationMethod.IsOverriden()) { - // an overridden method with NewSlot attribute is a method overriden covariant return method. + // an overridden method with NewSlot attribute is a method overriden by a covariant return method. attributes |= MethodAttributes.NewSlot; } From 19354eb01222da706bc55314d286eb1472e750f2 Mon Sep 17 00:00:00 2001 From: huoshan12345 Date: Sun, 20 Jul 2025 21:40:51 +0800 Subject: [PATCH 10/19] merge files --- ...oxyGeneratorUtils.CovariantReturnMethod.cs | 39 ------------------- .../Utils/ProxyGeneratorUtils.cs | 29 +++++++++++++- .../CovariantReturnMethodTests.cs | 2 +- 3 files changed, 29 insertions(+), 41 deletions(-) delete mode 100644 src/AspectCore.Core/Utils/ProxyGeneratorUtils.CovariantReturnMethod.cs diff --git a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.CovariantReturnMethod.cs b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.CovariantReturnMethod.cs deleted file mode 100644 index 415eb162..00000000 --- a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.CovariantReturnMethod.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using AspectCore.Extensions; -using static AspectCore.Extensions.MethodInfoExtensions; - -// ReSharper disable once CheckNamespace -namespace AspectCore.Utils -{ - // NOTE: - // For class proxy: We just define the covariant return methods in the implementation type like normal methods, the CLR will handle the propagation. - // For interface proxy: We need to use the covariant return methods as the interface methods' implementation. - internal partial class ProxyGeneratorUtils - { - // key: covariant return method - // value: interface method declarations - internal static IReadOnlyDictionary> GetCovariantReturnMethodMap(Type implType) - { - var result = new Dictionary>(); - // No PreserveBaseOverridesAttribute means that the runtime does not support covariant return types. - if (PreserveBaseOverridesAttribute is null) - return result; - - var covariantReturnMethods = implType - .GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) - .Where(m => m.IsPreserveBaseOverride(true)) - .ToHashSet(); - - foreach (var method in covariantReturnMethods) - { - var interfaceDeclarations = method.GetInterfaceDeclarations().ToHashSet(); - result[method] = interfaceDeclarations; - } - - return result; - } - } -} \ No newline at end of file diff --git a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs index 5b13da2c..4d4d7493 100644 --- a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs +++ b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Reflection; using System.Reflection.Emit; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using AspectCore.DynamicProxy; @@ -163,6 +164,29 @@ private Type CreateClassProxyInternal(string name, Type serviceType, Type implTy return typeDesc.Compile(); } + // key: covariant return method + // value: interface method declarations + internal static IReadOnlyDictionary> GetCovariantReturnMethodMap(Type implType) + { + var result = new Dictionary>(); + // No PreserveBaseOverridesAttribute means that the runtime does not support covariant return types. + if (AspectCore.Extensions.MethodInfoExtensions.PreserveBaseOverridesAttribute is null) + return result; + + var covariantReturnMethods = implType + .GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + .Where(m => m.IsPreserveBaseOverride(true)) + .ToHashSet(); + + foreach (var method in covariantReturnMethods) + { + var interfaceDeclarations = method.GetInterfaceDeclarations().ToHashSet(); + result[method] = interfaceDeclarations; + } + + return result; + } + private class ProxyNameUtils { private readonly Dictionary _indexes = new Dictionary(); @@ -461,7 +485,10 @@ internal static MethodBuilder DefineClassMethod(MethodInfo method, Type implType var methodBuilder = DefineMethod(method, method.Name, attributes, implType, typeDesc); return methodBuilder; } - + + // NOTE: when a covariant return method is handling: + // 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) + // For interface proxy: We need to use the covariant return methods as the interface methods' implementation. (in this case covariantReturnMethod is not null) private static MethodBuilder DefineMethod(MethodInfo method, string name, MethodAttributes attributes, Type implType, TypeDesc typeDesc, MethodInfo covariantReturnMethod = null) { var implementationMethod = covariantReturnMethod ?? implType.GetTypeInfo().GetMethodBySignature(method); diff --git a/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs b/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs index 177c4374..0c00096d 100644 --- a/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs +++ b/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs @@ -67,7 +67,7 @@ public void CreateClassProxy_CovariantReturnsService_DerivedCovariantReturnsServ Assert.Equal(nameof(DerivedCovariantReturnsService), service.Method()); Assert.Equal(nameof(CovariantReturnsService), service.Property); } - + [Fact] public void CreateInterfaceProxy_IService_CovariantReturnsService_Test() { From 28b9a4e838260046457fcacb46b5aa35a9e98058 Mon Sep 17 00:00:00 2001 From: huoshan12345 Date: Sun, 20 Jul 2025 21:44:06 +0800 Subject: [PATCH 11/19] cleanup --- src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs index 4d4d7493..98a28b11 100644 --- a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs +++ b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs @@ -1,12 +1,10 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Reflection.Emit; -using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using AspectCore.DynamicProxy; @@ -16,7 +14,7 @@ namespace AspectCore.Utils { - internal partial class ProxyGeneratorUtils + internal class ProxyGeneratorUtils { private const string ProxyNameSpace = "AspectCore.DynamicGenerated"; private const string ProxyAssemblyName = "AspectCore.DynamicProxy.Generator"; From 4e9b707c6ea99abc6514b1a20a98712bcb2eb8fe Mon Sep 17 00:00:00 2001 From: huoshan12345 Date: Mon, 21 Jul 2025 07:03:30 +0800 Subject: [PATCH 12/19] add some test cases --- .../Extensions/CollectionExtensions.cs | 15 +++ .../Extensions/MethodInfoExtensions.cs | 25 ++++- .../Extensions/TypeExtensions.cs | 98 +++++++++++++++++ .../Utils/ProxyGeneratorUtils.cs | 104 +++++++++--------- .../CovariantReturnMethodTests.cs | 76 +++++++++++-- 5 files changed, 250 insertions(+), 68 deletions(-) create mode 100644 src/AspectCore.Core/Extensions/CollectionExtensions.cs create mode 100644 src/AspectCore.Core/Extensions/TypeExtensions.cs diff --git a/src/AspectCore.Core/Extensions/CollectionExtensions.cs b/src/AspectCore.Core/Extensions/CollectionExtensions.cs new file mode 100644 index 00000000..cd6d1249 --- /dev/null +++ b/src/AspectCore.Core/Extensions/CollectionExtensions.cs @@ -0,0 +1,15 @@ +// ReSharper disable once CheckNamespace +namespace System.Collections.Generic +{ + internal static class CollectionExtensions + { +#if NETSTANDARD2_0 + public static TValue GetValueOrDefault(this IReadOnlyDictionary dictionary, TKey key, TValue defaultValue) + { + return dictionary.TryGetValue(key, out var obj) + ? obj + : defaultValue; + } +#endif + } +} diff --git a/src/AspectCore.Core/Extensions/MethodInfoExtensions.cs b/src/AspectCore.Core/Extensions/MethodInfoExtensions.cs index 9e4a109c..465c5243 100644 --- a/src/AspectCore.Core/Extensions/MethodInfoExtensions.cs +++ b/src/AspectCore.Core/Extensions/MethodInfoExtensions.cs @@ -27,11 +27,6 @@ public static IEnumerable GetInterfaceDeclarations(this MethodInfo m } } - public static bool IsOverriden(this MethodInfo method) - { - return method.GetBaseDefinition() != method; - } - public static bool IsPreserveBaseOverride(this MethodInfo method, bool checkBase) { if (PreserveBaseOverridesAttribute is null) @@ -55,6 +50,26 @@ public static bool IsPreserveBaseOverride(this MethodInfo method, bool checkBase return false; } + + public static IEnumerable EnumerateBaseDefinition(this MethodInfo method) + { + var m = method; + while (true) + { + yield return m; + + var b = m.GetBaseDefinition(); + if (b == m || b == null) + yield break; + + m = b; + } + } + + public static bool EqualAnyBaseDefinitionTo(this MethodInfo method, MethodInfo other) + { + return method.EnumerateBaseDefinition().Any(m => m == other); + } } } diff --git a/src/AspectCore.Core/Extensions/TypeExtensions.cs b/src/AspectCore.Core/Extensions/TypeExtensions.cs new file mode 100644 index 00000000..00a581ae --- /dev/null +++ b/src/AspectCore.Core/Extensions/TypeExtensions.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +// ReSharper disable once CheckNamespace +namespace AspectCore.Extensions +{ + internal static class TypeExtensions + { + public readonly struct CovariantReturnMethodInfo + { + public readonly MethodInfo CovariantReturnMethod; + public readonly MethodInfo OverridenMethod; + public readonly HashSet InterfaceDeclarations; + + public CovariantReturnMethodInfo(MethodInfo covariantReturnMethod, MethodInfo overridenMethod, HashSet interfaceDeclarations) + { + InterfaceDeclarations = interfaceDeclarations; + OverridenMethod = overridenMethod; + CovariantReturnMethod = covariantReturnMethod; + } + } + + public static IReadOnlyList GetCovariantReturnMethods(this Type type) + { + var result = new List(); + // No PreserveBaseOverridesAttribute means that the runtime does not support covariant return types. + if (MethodInfoExtensions.PreserveBaseOverridesAttribute is null) + return result; + + var methods = type + .GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + .GroupBy(m => m.IsPreserveBaseOverride(true)) + .ToDictionary(m => m.Key, m => m.ToArray()); + + var covariantReturnMethods = methods.GetValueOrDefault(true, Array.Empty()); + var otherMethods = methods.GetValueOrDefault(false, Array.Empty()); + + foreach (var covariantReturnMethod in covariantReturnMethods) + { + var overridenMethod = otherMethods.FirstOrDefault(m => Match(covariantReturnMethod, m)); + if (overridenMethod is null) + continue; + + var interfaceDeclarations = covariantReturnMethod.GetInterfaceDeclarations().ToHashSet(); + result.Add(new CovariantReturnMethodInfo(covariantReturnMethod, overridenMethod, interfaceDeclarations)); + } + + return result; + + bool Match(MethodInfo covariantReturnMethod, MethodInfo other) + { + if (covariantReturnMethod.Name != other.Name) + return false; + + // return types should not be the same. + if (covariantReturnMethod.ReturnType == other.ReturnType) + return false; + + if (other.ReturnType.IsAssignableFrom(covariantReturnMethod.ReturnType) == false) + return false; + + var params1 = covariantReturnMethod.GetParameters(); + var params2 = other.GetParameters(); + + if (params1.Length != params2.Length) + return false; + + foreach (var (p1, p2) in params1.Zip(params2)) + { + if (p1.ParameterType != p2.ParameterType) + return false; + } + + var isGeneric = covariantReturnMethod.IsGenericMethod; + if (isGeneric != other.IsGenericMethod) + return false; + + if (isGeneric) + { + var args1 = covariantReturnMethod.GetGenericArguments(); + var args2 = other.GetGenericArguments(); + if (args1.Length != args2.Length) + return false; + + foreach (var (a1, a2) in args1.Zip(args2)) + { + if (a1 != a2) + return false; + } + } + + return true; + } + } + } +} diff --git a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs index 98a28b11..3c8116f3 100644 --- a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs +++ b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs @@ -162,29 +162,6 @@ private Type CreateClassProxyInternal(string name, Type serviceType, Type implTy return typeDesc.Compile(); } - // key: covariant return method - // value: interface method declarations - internal static IReadOnlyDictionary> GetCovariantReturnMethodMap(Type implType) - { - var result = new Dictionary>(); - // No PreserveBaseOverridesAttribute means that the runtime does not support covariant return types. - if (AspectCore.Extensions.MethodInfoExtensions.PreserveBaseOverridesAttribute is null) - return result; - - var covariantReturnMethods = implType - .GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) - .Where(m => m.IsPreserveBaseOverride(true)) - .ToHashSet(); - - foreach (var method in covariantReturnMethods) - { - var interfaceDeclarations = method.GetInterfaceDeclarations().ToHashSet(); - result[method] = interfaceDeclarations; - } - - return result; - } - private class ProxyNameUtils { private readonly Dictionary _indexes = new Dictionary(); @@ -415,17 +392,23 @@ internal static MethodBuilder DefineInterfaceImplMethod(MethodInfo method, TypeB internal static void DefineInterfaceProxyMethods(Type interfaceType, Type targetType, Type[] additionalInterfaces, TypeDesc typeDesc) { - var covariantReturnMethodMap = GetCovariantReturnMethodMap(targetType); + var covariantReturnMethodMap = targetType.GetCovariantReturnMethods(); foreach (var method in interfaceType.GetTypeInfo().DeclaredMethods.Where(x => !x.IsPropertyBinding())) { - var covariantReturnMethod = covariantReturnMethodMap.FirstOrDefault(m => m.Value.Contains(method)).Key; + var covariantReturnMethod = covariantReturnMethodMap + .FirstOrDefault(m => m.InterfaceDeclarations.Contains(method)) + .CovariantReturnMethod; + DefineInterfaceMethod(method, targetType, typeDesc, covariantReturnMethod); } foreach (var item in additionalInterfaces) { foreach (var method in item.GetTypeInfo().DeclaredMethods.Where(x => !x.IsPropertyBinding())) { - var covariantReturnMethod = covariantReturnMethodMap.FirstOrDefault(m => m.Value.Contains(method)).Key; + var covariantReturnMethod = covariantReturnMethodMap + .FirstOrDefault(m => m.InterfaceDeclarations.Contains(method)) + .CovariantReturnMethod; + DefineExplicitMethod(method, targetType, typeDesc, covariantReturnMethod); } } @@ -433,8 +416,12 @@ internal static void DefineInterfaceProxyMethods(Type interfaceType, Type target internal static void DefineClassProxyMethods(Type serviceType, Type implType, Type[] additionalInterfaces, TypeDesc typeDesc) { + var covariantReturnMethodMap = implType.GetCovariantReturnMethods(); foreach (var method in serviceType.GetTypeInfo().GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(x => !x.IsPropertyBinding())) { + if (covariantReturnMethodMap.Any(m => m.OverridenMethod.EqualAnyBaseDefinitionTo(method))) + continue; + if (method.IsVisibleAndVirtual() && !_ignores.Contains(method.Name)) DefineClassMethod(method, implType, typeDesc); } @@ -461,7 +448,7 @@ internal static MethodBuilder DefineExplicitMethod(MethodInfo method, Type implT return methodBuilder; } - internal static MethodBuilder DefineClassMethod(MethodInfo method, Type implType, TypeDesc typeDesc) + internal static MethodBuilder DefineClassMethod(MethodInfo method, Type implType, TypeDesc typeDesc, bool isNewSlot = false) { var attributes = OverrideMethodAttributes; @@ -480,15 +467,22 @@ internal static MethodBuilder DefineClassMethod(MethodInfo method, Type implType attributes |= MethodAttributes.FamORAssem; } + if (isNewSlot) + { + attributes |= MethodAttributes.NewSlot; + } + var methodBuilder = DefineMethod(method, method.Name, attributes, implType, typeDesc); return methodBuilder; } - + // NOTE: when a covariant return method is handling: // 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) // For interface proxy: We need to use the covariant return methods as the interface methods' implementation. (in this case covariantReturnMethod is not null) private static MethodBuilder DefineMethod(MethodInfo method, string name, MethodAttributes attributes, Type implType, TypeDesc typeDesc, MethodInfo covariantReturnMethod = null) { + var methodBuilder = typeDesc.Builder.DefineMethod(name, attributes, method.CallingConvention, method.ReturnType, method.GetParameterTypes()); + var implementationMethod = covariantReturnMethod ?? implType.GetTypeInfo().GetMethodBySignature(method); if (implementationMethod == null) { @@ -520,20 +514,6 @@ private static MethodBuilder DefineMethod(MethodInfo method, string name, Method } } - // NOTE: both covariant return method and its corresponding overridden method should be defined with NewSlot attribute. - if (method.IsPreserveBaseOverride(true)) - { - // PreserveBaseOverridesAttribute is used to indicate that the method is a covariant return method. - attributes |= MethodAttributes.NewSlot; - } - else if (implementationMethod.Attributes.HasFlag(MethodAttributes.NewSlot) && implementationMethod.IsOverriden()) - { - // an overridden method with NewSlot attribute is a method overriden by a covariant return method. - attributes |= MethodAttributes.NewSlot; - } - - var methodBuilder = typeDesc.Builder.DefineMethod(name, attributes, method.CallingConvention, method.ReturnType, method.GetParameterTypes()); - GenericParameterUtils.DefineGenericParameter(method, methodBuilder); //define method attributes @@ -542,6 +522,9 @@ private static MethodBuilder DefineMethod(MethodInfo method, string name, Method //inherit targetMethod's attribute foreach (var customAttributeData in method.CustomAttributes) { + if (customAttributeData.AttributeType == AspectCore.Extensions.MethodInfoExtensions.PreserveBaseOverridesAttribute) + continue; + methodBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(customAttributeData)); } @@ -777,13 +760,13 @@ private class PropertyBuilderUtils { public static void DefineInterfaceProxyProperties(Type interfaceType, Type implType, Type[] additionalInterfaces, TypeDesc typeDesc) { - var covariantReturnMethodMap = GetCovariantReturnMethodMap(implType); + var covariantReturnMethodMap = implType.GetCovariantReturnMethods(); foreach (var property in interfaceType.GetTypeInfo().DeclaredProperties) { var builder = DefineInterfaceProxyProperty(property, property.Name, implType, typeDesc); var covariantReturnGetter = property.CanRead - ? covariantReturnMethodMap.FirstOrDefault(m => m.Value.Contains(property.GetMethod)).Key + ? covariantReturnMethodMap.FirstOrDefault(m => m.InterfaceDeclarations.Contains(property.GetMethod)).CovariantReturnMethod : null; DefineInterfacePropertyMethod(builder, property, implType, typeDesc, covariantReturnGetter); } @@ -793,7 +776,7 @@ public static void DefineInterfaceProxyProperties(Type interfaceType, Type implT { var builder = DefineInterfaceProxyProperty(property, property.GetDisplayName(), implType, typeDesc); var covariantReturnGetter = property.CanRead - ? covariantReturnMethodMap.FirstOrDefault(m => m.Value.Contains(property.GetMethod)).Key + ? covariantReturnMethodMap.FirstOrDefault(m => m.InterfaceDeclarations.Contains(property.GetMethod)).CovariantReturnMethod : null; DefineExplicitPropertyMethod(builder, property, implType, typeDesc, covariantReturnGetter); } @@ -802,12 +785,31 @@ public static void DefineInterfaceProxyProperties(Type interfaceType, Type implT internal static void DefineClassProxyProperties(Type serviceType, Type implType, Type[] additionalInterfaces, TypeDesc typeDesc) { + var covariantReturnMethodMap = implType.GetCovariantReturnMethods(); + foreach (var property in serviceType.GetTypeInfo().GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) { + var isNewSlot = false; + + if (property.CanRead) + { + // skip if the property is overridden by a covariant return method + if (covariantReturnMethodMap.Any(m => m.CovariantReturnMethod.EqualAnyBaseDefinitionTo(property.GetMethod) + && m.OverridenMethod.ReturnType == property.PropertyType)) + continue; + + if (covariantReturnMethodMap.Any(m => m.CovariantReturnMethod.EqualAnyBaseDefinitionTo(property.GetMethod) + && m.CovariantReturnMethod.ReturnType == property.PropertyType)) + { + // this property's getter is a covariant return method. + isNewSlot = true; + } + } + if (property.IsVisibleAndVirtual()) { var builder = DefineInterfaceProxyProperty(property, property.Name, implType, typeDesc); - DefineClassPropertyMethod(builder, property, implType, typeDesc); + DefineClassPropertyMethod(builder, property, implType, typeDesc, isNewSlot); } } foreach (var item in additionalInterfaces) @@ -820,11 +822,11 @@ internal static void DefineClassProxyProperties(Type serviceType, Type implType, } } - private static void DefineClassPropertyMethod(PropertyBuilder propertyBuilder, PropertyInfo property, Type implType, TypeDesc typeDesc) + private static void DefineClassPropertyMethod(PropertyBuilder propertyBuilder, PropertyInfo property, Type implType, TypeDesc typeDesc, bool isNewSlot = false) { if (property.CanRead) { - var method = MethodBuilderUtils.DefineClassMethod(property.GetMethod, implType, typeDesc); + var method = MethodBuilderUtils.DefineClassMethod(property.GetMethod, implType, typeDesc, isNewSlot); propertyBuilder.SetGetMethod(method); } if (property.CanWrite) @@ -1133,13 +1135,13 @@ internal static void DefineGenericParameter(Type targetType, TypeBuilder typeBui } } - internal static void DefineGenericParameter(MethodInfo tergetMethod, MethodBuilder methodBuilder) + internal static void DefineGenericParameter(MethodInfo targetMethod, MethodBuilder methodBuilder) { - if (!tergetMethod.IsGenericMethod) + if (!targetMethod.IsGenericMethod) { return; } - var genericArguments = tergetMethod.GetGenericArguments().Select(t => t.GetTypeInfo()).ToArray(); + var genericArguments = targetMethod.GetGenericArguments().Select(t => t.GetTypeInfo()).ToArray(); var genericArgumentsBuilders = methodBuilder.DefineGenericParameters(genericArguments.Select(a => a.Name).ToArray()); for (var index = 0; index < genericArguments.Length; index++) { diff --git a/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs b/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs index 0c00096d..f7f41300 100644 --- a/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs +++ b/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs @@ -1,86 +1,138 @@ -using AspectCore.DynamicProxy; +using System.Linq; +using System.Threading.Tasks; +using AspectCore.DynamicProxy; using Xunit; namespace AspectCore.Tests.DynamicProxy; public class CovariantReturnMethodTests : DynamicProxyTestBase { + public class Interceptor : AbstractInterceptorAttribute + { + public override async Task Invoke(AspectContext context, AspectDelegate next) + { + await context.Invoke(next); + + var returnType = context.ImplementationMethod.ReturnType; + if (returnType == typeof(string)) + { + context.ReturnValue += nameof(Interceptor); + } + else if (returnType == typeof(object)) + { + context.ReturnValue = nameof(Interceptor); + } + } + } + public interface IService { - object Method(); object Property { get; } + object Method(); + + [Interceptor] + object ProxyMethod(); } public class Service : IService { - public virtual object Method() => new(); - public virtual object Property { get; } = new(); + public virtual object Property { get; } = 1; + public virtual object Method() => 1; + + [Interceptor] + public virtual object ProxyMethod() => new(); } public class CovariantReturnsService : Service { - public override string Method() => nameof(CovariantReturnsService); public override string Property { get; } = nameof(CovariantReturnsService); + public override string Method() => nameof(CovariantReturnsService); + + [Interceptor] + public override string ProxyMethod() => nameof(CovariantReturnsService); } public class DerivedCovariantReturnsService : CovariantReturnsService { public override string Method() => nameof(DerivedCovariantReturnsService); + + [Interceptor] + public override string ProxyMethod() => nameof(DerivedCovariantReturnsService); + } + + [Fact] + public void CreateClassProxy_Service_Test() + { + var service = ProxyGenerator.CreateClassProxy(); + Assert.Equal(1, service.Property); + Assert.Equal(1, service.Method()); + Assert.Equal(nameof(Interceptor), service.ProxyMethod()); } [Fact] public void CreateClassProxy_CovariantReturnsService_Test() { var service = ProxyGenerator.CreateClassProxy(); - Assert.Equal(nameof(CovariantReturnsService), service.Method()); Assert.Equal(nameof(CovariantReturnsService), service.Property); + Assert.Equal(nameof(CovariantReturnsService), service.Method()); + Assert.Equal(nameof(CovariantReturnsService) + nameof(Interceptor), service.ProxyMethod()); } [Fact] public void CreateClassProxy_DerivedCovariantReturnsService_Test() { var service = ProxyGenerator.CreateClassProxy(); - Assert.Equal(nameof(DerivedCovariantReturnsService), service.Method()); Assert.Equal(nameof(CovariantReturnsService), service.Property); + Assert.Equal(nameof(DerivedCovariantReturnsService), service.Method()); + Assert.Equal(nameof(DerivedCovariantReturnsService) + nameof(Interceptor), service.ProxyMethod()); } [Fact] public void CreateClassProxy_Service_CovariantReturnsService_Test() { var service = ProxyGenerator.CreateClassProxy(); - Assert.Equal(nameof(CovariantReturnsService), service.Method()); Assert.Equal(nameof(CovariantReturnsService), service.Property); + Assert.Equal(nameof(CovariantReturnsService), service.Method()); + Assert.Equal(nameof(CovariantReturnsService) + nameof(Interceptor), service.ProxyMethod()); } [Fact] public void CreateClassProxy_Service_DerivedCovariantReturnsService_Test() { var service = ProxyGenerator.CreateClassProxy(); - Assert.Equal(nameof(DerivedCovariantReturnsService), service.Method()); Assert.Equal(nameof(CovariantReturnsService), service.Property); + Assert.Equal(nameof(DerivedCovariantReturnsService), service.Method()); + Assert.Equal(nameof(DerivedCovariantReturnsService) + nameof(Interceptor), service.ProxyMethod()); } [Fact] public void CreateClassProxy_CovariantReturnsService_DerivedCovariantReturnsService_Test() { var service = ProxyGenerator.CreateClassProxy(); - Assert.Equal(nameof(DerivedCovariantReturnsService), service.Method()); Assert.Equal(nameof(CovariantReturnsService), service.Property); + Assert.Equal(nameof(DerivedCovariantReturnsService), service.Method()); + Assert.Equal(nameof(DerivedCovariantReturnsService) + nameof(Interceptor), service.ProxyMethod()); } [Fact] public void CreateInterfaceProxy_IService_CovariantReturnsService_Test() { var service = ProxyGenerator.CreateInterfaceProxy(); - Assert.Equal(nameof(CovariantReturnsService), service.Method()); Assert.Equal(nameof(CovariantReturnsService), service.Property); + Assert.Equal(nameof(CovariantReturnsService), service.Method()); + Assert.Equal(nameof(CovariantReturnsService) + nameof(Interceptor), service.ProxyMethod()); } [Fact] public void CreateInterfaceProxy_IService_DerivedCovariantReturnsService_Test() { + var methods = typeof(DerivedCovariantReturnsService).GetMethods() + .Where(m => m.Name == nameof(IService.ProxyMethod)) + .ToArray(); + var service = ProxyGenerator.CreateInterfaceProxy(); - Assert.Equal(nameof(DerivedCovariantReturnsService), service.Method()); Assert.Equal(nameof(CovariantReturnsService), service.Property); + Assert.Equal(nameof(DerivedCovariantReturnsService), service.Method()); + Assert.Equal(nameof(DerivedCovariantReturnsService) + nameof(Interceptor), service.ProxyMethod()); } } From a83d93fdcc3007a3142ff6992ef032b31d2772ff Mon Sep 17 00:00:00 2001 From: huoshan12345 Date: Tue, 22 Jul 2025 01:19:38 +0800 Subject: [PATCH 13/19] fix some cases --- .../Extensions/MethodInfoExtensions.cs | 38 +------- .../Properties/AssemblyInfo.cs | 2 + .../Utils/ProxyGeneratorUtils.cs | 96 +++++++++---------- .../CovariantReturnMethodTests.cs | 36 +++---- 4 files changed, 66 insertions(+), 106 deletions(-) diff --git a/src/AspectCore.Core/Extensions/MethodInfoExtensions.cs b/src/AspectCore.Core/Extensions/MethodInfoExtensions.cs index 465c5243..ba79a0b2 100644 --- a/src/AspectCore.Core/Extensions/MethodInfoExtensions.cs +++ b/src/AspectCore.Core/Extensions/MethodInfoExtensions.cs @@ -32,43 +32,15 @@ public static bool IsPreserveBaseOverride(this MethodInfo method, bool checkBase if (PreserveBaseOverridesAttribute is null) return false; - var m = method; - while (true) - { - if (m.IsDefined(PreserveBaseOverridesAttribute)) - return true; - - if (checkBase == false) - break; - - var b = m.GetBaseDefinition(); - if (b == m || b == null) - break; - - m = b; - } - - return false; - } - - public static IEnumerable EnumerateBaseDefinition(this MethodInfo method) - { - var m = method; - while (true) - { - yield return m; - - var b = m.GetBaseDefinition(); - if (b == m || b == null) - yield break; + if (method.IsDefined(PreserveBaseOverridesAttribute)) + return true; - m = b; - } + return checkBase && method.GetBaseDefinition().IsDefined(PreserveBaseOverridesAttribute); } - public static bool EqualAnyBaseDefinitionTo(this MethodInfo method, MethodInfo other) + public static bool IsSameBaseDefinition(this MethodInfo method, MethodInfo other) { - return method.EnumerateBaseDefinition().Any(m => m == other); + return method.GetBaseDefinition() == other.GetBaseDefinition(); } } } diff --git a/src/AspectCore.Core/Properties/AssemblyInfo.cs b/src/AspectCore.Core/Properties/AssemblyInfo.cs index 28b81503..b2f77286 100644 --- a/src/AspectCore.Core/Properties/AssemblyInfo.cs +++ b/src/AspectCore.Core/Properties/AssemblyInfo.cs @@ -28,3 +28,5 @@ "e15b6849fbabea83fc9b8b6abf959e606f5e51b268a6a6c2d4757bbc3ae33689373faaedf61077" + "59678c9b")] #endif + +[assembly: InternalsVisibleTo("AspectCore.Tests")] diff --git a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs index 3c8116f3..e5acd3cf 100644 --- a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs +++ b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs @@ -392,10 +392,10 @@ internal static MethodBuilder DefineInterfaceImplMethod(MethodInfo method, TypeB internal static void DefineInterfaceProxyMethods(Type interfaceType, Type targetType, Type[] additionalInterfaces, TypeDesc typeDesc) { - var covariantReturnMethodMap = targetType.GetCovariantReturnMethods(); + var covariantReturnMethods = targetType.GetCovariantReturnMethods(); foreach (var method in interfaceType.GetTypeInfo().DeclaredMethods.Where(x => !x.IsPropertyBinding())) { - var covariantReturnMethod = covariantReturnMethodMap + var covariantReturnMethod = covariantReturnMethods .FirstOrDefault(m => m.InterfaceDeclarations.Contains(method)) .CovariantReturnMethod; @@ -405,7 +405,7 @@ internal static void DefineInterfaceProxyMethods(Type interfaceType, Type target { foreach (var method in item.GetTypeInfo().DeclaredMethods.Where(x => !x.IsPropertyBinding())) { - var covariantReturnMethod = covariantReturnMethodMap + var covariantReturnMethod = covariantReturnMethods .FirstOrDefault(m => m.InterfaceDeclarations.Contains(method)) .CovariantReturnMethod; @@ -416,14 +416,28 @@ internal static void DefineInterfaceProxyMethods(Type interfaceType, Type target internal static void DefineClassProxyMethods(Type serviceType, Type implType, Type[] additionalInterfaces, TypeDesc typeDesc) { - var covariantReturnMethodMap = implType.GetCovariantReturnMethods(); + var covariantReturnMethods = implType.GetCovariantReturnMethods(); foreach (var method in serviceType.GetTypeInfo().GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(x => !x.IsPropertyBinding())) { - if (covariantReturnMethodMap.Any(m => m.OverridenMethod.EqualAnyBaseDefinitionTo(method))) - continue; - if (method.IsVisibleAndVirtual() && !_ignores.Contains(method.Name)) + { + var covariantReturnMethod = covariantReturnMethods.FirstOrDefault(m => m.OverridenMethod.IsSameBaseDefinition(method)); + var overriden = covariantReturnMethod.OverridenMethod; + if (overriden != null) + { + // if method is the base definition of the overriden method, the CovariantReturnMethod is not in serviceType, so we need to add CovariantReturnMethod to implType. + // otherwise, the CovariantReturnMethod is also in serviceType, which will be added to implType next for-loops. + if (overriden.GetBaseDefinition() == method) + { + DefineClassMethod(covariantReturnMethod.CovariantReturnMethod, implType, typeDesc); + } + + // covariantReturnMethod is found, do not add method to implType. + continue; + } + DefineClassMethod(method, implType, typeDesc); + } } foreach (var item in additionalInterfaces) { @@ -448,7 +462,7 @@ internal static MethodBuilder DefineExplicitMethod(MethodInfo method, Type implT return methodBuilder; } - internal static MethodBuilder DefineClassMethod(MethodInfo method, Type implType, TypeDesc typeDesc, bool isNewSlot = false) + internal static MethodBuilder DefineClassMethod(MethodInfo method, Type implType, TypeDesc typeDesc, MethodInfo covariantReturnMethod = null, bool isNewSlot = false) { var attributes = OverrideMethodAttributes; @@ -472,17 +486,31 @@ internal static MethodBuilder DefineClassMethod(MethodInfo method, Type implType attributes |= MethodAttributes.NewSlot; } - var methodBuilder = DefineMethod(method, method.Name, attributes, implType, typeDesc); + var methodBuilder = DefineMethod(method, method.Name, attributes, implType, typeDesc, covariantReturnMethod); return methodBuilder; } - // NOTE: when a covariant return method is handling: - // 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) - // For interface proxy: We need to use the covariant return methods as the interface methods' implementation. (in this case covariantReturnMethod is not null) private static MethodBuilder DefineMethod(MethodInfo method, string name, MethodAttributes attributes, Type implType, TypeDesc typeDesc, MethodInfo covariantReturnMethod = null) { var methodBuilder = typeDesc.Builder.DefineMethod(name, attributes, method.CallingConvention, method.ReturnType, method.GetParameterTypes()); + GenericParameterUtils.DefineGenericParameter(method, methodBuilder); + + //define method attributes + methodBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(typeof(DynamicallyAttribute))); + + //inherit targetMethod's attribute + foreach (var customAttributeData in method.CustomAttributes) + { + if (customAttributeData.AttributeType == AspectCore.Extensions.MethodInfoExtensions.PreserveBaseOverridesAttribute) + continue; + + methodBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(customAttributeData)); + } + + //define parameters + ParameterBuilderUtils.DefineParameters(method, methodBuilder); + var implementationMethod = covariantReturnMethod ?? implType.GetTypeInfo().GetMethodBySignature(method); if (implementationMethod == null) { @@ -514,23 +542,6 @@ private static MethodBuilder DefineMethod(MethodInfo method, string name, Method } } - GenericParameterUtils.DefineGenericParameter(method, methodBuilder); - - //define method attributes - methodBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(typeof(DynamicallyAttribute))); - - //inherit targetMethod's attribute - foreach (var customAttributeData in method.CustomAttributes) - { - if (customAttributeData.AttributeType == AspectCore.Extensions.MethodInfoExtensions.PreserveBaseOverridesAttribute) - continue; - - methodBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(customAttributeData)); - } - - //define parameters - ParameterBuilderUtils.DefineParameters(method, methodBuilder); - if (method.IsNonAspect()) { EmitMethodBody(); @@ -760,13 +771,13 @@ private class PropertyBuilderUtils { public static void DefineInterfaceProxyProperties(Type interfaceType, Type implType, Type[] additionalInterfaces, TypeDesc typeDesc) { - var covariantReturnMethodMap = implType.GetCovariantReturnMethods(); + var covariantReturnMethods = implType.GetCovariantReturnMethods(); foreach (var property in interfaceType.GetTypeInfo().DeclaredProperties) { var builder = DefineInterfaceProxyProperty(property, property.Name, implType, typeDesc); var covariantReturnGetter = property.CanRead - ? covariantReturnMethodMap.FirstOrDefault(m => m.InterfaceDeclarations.Contains(property.GetMethod)).CovariantReturnMethod + ? covariantReturnMethods.FirstOrDefault(m => m.InterfaceDeclarations.Contains(property.GetMethod)).CovariantReturnMethod : null; DefineInterfacePropertyMethod(builder, property, implType, typeDesc, covariantReturnGetter); } @@ -776,7 +787,7 @@ public static void DefineInterfaceProxyProperties(Type interfaceType, Type implT { var builder = DefineInterfaceProxyProperty(property, property.GetDisplayName(), implType, typeDesc); var covariantReturnGetter = property.CanRead - ? covariantReturnMethodMap.FirstOrDefault(m => m.InterfaceDeclarations.Contains(property.GetMethod)).CovariantReturnMethod + ? covariantReturnMethods.FirstOrDefault(m => m.InterfaceDeclarations.Contains(property.GetMethod)).CovariantReturnMethod : null; DefineExplicitPropertyMethod(builder, property, implType, typeDesc, covariantReturnGetter); } @@ -785,31 +796,20 @@ public static void DefineInterfaceProxyProperties(Type interfaceType, Type implT internal static void DefineClassProxyProperties(Type serviceType, Type implType, Type[] additionalInterfaces, TypeDesc typeDesc) { - var covariantReturnMethodMap = implType.GetCovariantReturnMethods(); + var covariantReturnMethods = implType.GetCovariantReturnMethods(); foreach (var property in serviceType.GetTypeInfo().GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) { - var isNewSlot = false; + MethodInfo covariantReturnGetter = null; if (property.CanRead) { - // skip if the property is overridden by a covariant return method - if (covariantReturnMethodMap.Any(m => m.CovariantReturnMethod.EqualAnyBaseDefinitionTo(property.GetMethod) - && m.OverridenMethod.ReturnType == property.PropertyType)) - continue; - - if (covariantReturnMethodMap.Any(m => m.CovariantReturnMethod.EqualAnyBaseDefinitionTo(property.GetMethod) - && m.CovariantReturnMethod.ReturnType == property.PropertyType)) - { - // this property's getter is a covariant return method. - isNewSlot = true; - } } if (property.IsVisibleAndVirtual()) { var builder = DefineInterfaceProxyProperty(property, property.Name, implType, typeDesc); - DefineClassPropertyMethod(builder, property, implType, typeDesc, isNewSlot); + DefineClassPropertyMethod(builder, property, implType, typeDesc, covariantReturnGetter); } } foreach (var item in additionalInterfaces) @@ -822,11 +822,11 @@ internal static void DefineClassProxyProperties(Type serviceType, Type implType, } } - private static void DefineClassPropertyMethod(PropertyBuilder propertyBuilder, PropertyInfo property, Type implType, TypeDesc typeDesc, bool isNewSlot = false) + private static void DefineClassPropertyMethod(PropertyBuilder propertyBuilder, PropertyInfo property, Type implType, TypeDesc typeDesc, MethodInfo covariantReturnGetter = null) { if (property.CanRead) { - var method = MethodBuilderUtils.DefineClassMethod(property.GetMethod, implType, typeDesc, isNewSlot); + var method = MethodBuilderUtils.DefineClassMethod(property.GetMethod, implType, typeDesc, covariantReturnGetter); propertyBuilder.SetGetMethod(method); } if (property.CanWrite) diff --git a/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs b/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs index f7f41300..cca2b395 100644 --- a/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs +++ b/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs @@ -1,5 +1,4 @@ -using System.Linq; -using System.Threading.Tasks; +using System.Threading.Tasks; using AspectCore.DynamicProxy; using Xunit; @@ -27,7 +26,7 @@ public override async Task Invoke(AspectContext context, AspectDelegate next) public interface IService { - object Property { get; } + //object Property { get; } object Method(); [Interceptor] @@ -36,7 +35,7 @@ public interface IService public class Service : IService { - public virtual object Property { get; } = 1; + //public virtual object Property { get; } = 1; public virtual object Method() => 1; [Interceptor] @@ -45,7 +44,7 @@ public class Service : IService public class CovariantReturnsService : Service { - public override string Property { get; } = nameof(CovariantReturnsService); + //public override string Property { get; } = nameof(CovariantReturnsService); public override string Method() => nameof(CovariantReturnsService); [Interceptor] @@ -60,20 +59,11 @@ public class DerivedCovariantReturnsService : CovariantReturnsService public override string ProxyMethod() => nameof(DerivedCovariantReturnsService); } - [Fact] - public void CreateClassProxy_Service_Test() - { - var service = ProxyGenerator.CreateClassProxy(); - Assert.Equal(1, service.Property); - Assert.Equal(1, service.Method()); - Assert.Equal(nameof(Interceptor), service.ProxyMethod()); - } - [Fact] public void CreateClassProxy_CovariantReturnsService_Test() { var service = ProxyGenerator.CreateClassProxy(); - Assert.Equal(nameof(CovariantReturnsService), service.Property); + //Assert.Equal(nameof(CovariantReturnsService), service.Property); Assert.Equal(nameof(CovariantReturnsService), service.Method()); Assert.Equal(nameof(CovariantReturnsService) + nameof(Interceptor), service.ProxyMethod()); } @@ -82,7 +72,7 @@ public void CreateClassProxy_CovariantReturnsService_Test() public void CreateClassProxy_DerivedCovariantReturnsService_Test() { var service = ProxyGenerator.CreateClassProxy(); - Assert.Equal(nameof(CovariantReturnsService), service.Property); + //Assert.Equal(nameof(CovariantReturnsService), service.Property); Assert.Equal(nameof(DerivedCovariantReturnsService), service.Method()); Assert.Equal(nameof(DerivedCovariantReturnsService) + nameof(Interceptor), service.ProxyMethod()); } @@ -91,7 +81,7 @@ public void CreateClassProxy_DerivedCovariantReturnsService_Test() public void CreateClassProxy_Service_CovariantReturnsService_Test() { var service = ProxyGenerator.CreateClassProxy(); - Assert.Equal(nameof(CovariantReturnsService), service.Property); + //Assert.Equal(nameof(CovariantReturnsService), service.Property); Assert.Equal(nameof(CovariantReturnsService), service.Method()); Assert.Equal(nameof(CovariantReturnsService) + nameof(Interceptor), service.ProxyMethod()); } @@ -100,7 +90,7 @@ public void CreateClassProxy_Service_CovariantReturnsService_Test() public void CreateClassProxy_Service_DerivedCovariantReturnsService_Test() { var service = ProxyGenerator.CreateClassProxy(); - Assert.Equal(nameof(CovariantReturnsService), service.Property); + //Assert.Equal(nameof(CovariantReturnsService), service.Property); Assert.Equal(nameof(DerivedCovariantReturnsService), service.Method()); Assert.Equal(nameof(DerivedCovariantReturnsService) + nameof(Interceptor), service.ProxyMethod()); } @@ -109,7 +99,7 @@ public void CreateClassProxy_Service_DerivedCovariantReturnsService_Test() public void CreateClassProxy_CovariantReturnsService_DerivedCovariantReturnsService_Test() { var service = ProxyGenerator.CreateClassProxy(); - Assert.Equal(nameof(CovariantReturnsService), service.Property); + //Assert.Equal(nameof(CovariantReturnsService), service.Property); Assert.Equal(nameof(DerivedCovariantReturnsService), service.Method()); Assert.Equal(nameof(DerivedCovariantReturnsService) + nameof(Interceptor), service.ProxyMethod()); } @@ -118,7 +108,7 @@ public void CreateClassProxy_CovariantReturnsService_DerivedCovariantReturnsServ public void CreateInterfaceProxy_IService_CovariantReturnsService_Test() { var service = ProxyGenerator.CreateInterfaceProxy(); - Assert.Equal(nameof(CovariantReturnsService), service.Property); + //Assert.Equal(nameof(CovariantReturnsService), service.Property); Assert.Equal(nameof(CovariantReturnsService), service.Method()); Assert.Equal(nameof(CovariantReturnsService) + nameof(Interceptor), service.ProxyMethod()); } @@ -126,12 +116,8 @@ public void CreateInterfaceProxy_IService_CovariantReturnsService_Test() [Fact] public void CreateInterfaceProxy_IService_DerivedCovariantReturnsService_Test() { - var methods = typeof(DerivedCovariantReturnsService).GetMethods() - .Where(m => m.Name == nameof(IService.ProxyMethod)) - .ToArray(); - var service = ProxyGenerator.CreateInterfaceProxy(); - Assert.Equal(nameof(CovariantReturnsService), service.Property); + //Assert.Equal(nameof(CovariantReturnsService), service.Property); Assert.Equal(nameof(DerivedCovariantReturnsService), service.Method()); Assert.Equal(nameof(DerivedCovariantReturnsService) + nameof(Interceptor), service.ProxyMethod()); } From ca54e21093cefd3387e2ae1cbf5c9ea90c9e169a Mon Sep 17 00:00:00 2001 From: huoshan12345 Date: Tue, 22 Jul 2025 01:22:40 +0800 Subject: [PATCH 14/19] fix comment --- src/AspectCore.Core/Properties/AssemblyInfo.cs | 2 -- src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/AspectCore.Core/Properties/AssemblyInfo.cs b/src/AspectCore.Core/Properties/AssemblyInfo.cs index b2f77286..28b81503 100644 --- a/src/AspectCore.Core/Properties/AssemblyInfo.cs +++ b/src/AspectCore.Core/Properties/AssemblyInfo.cs @@ -28,5 +28,3 @@ "e15b6849fbabea83fc9b8b6abf959e606f5e51b268a6a6c2d4757bbc3ae33689373faaedf61077" + "59678c9b")] #endif - -[assembly: InternalsVisibleTo("AspectCore.Tests")] diff --git a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs index e5acd3cf..a6bd57b1 100644 --- a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs +++ b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs @@ -426,7 +426,7 @@ internal static void DefineClassProxyMethods(Type serviceType, Type implType, Ty if (overriden != null) { // if method is the base definition of the overriden method, the CovariantReturnMethod is not in serviceType, so we need to add CovariantReturnMethod to implType. - // otherwise, the CovariantReturnMethod is also in serviceType, which will be added to implType next for-loops. + // otherwise, the CovariantReturnMethod is also in serviceType, which will be added to implType in next for-loops. if (overriden.GetBaseDefinition() == method) { DefineClassMethod(covariantReturnMethod.CovariantReturnMethod, implType, typeDesc); From 5ee9105951f7c7a42cc3c87bc5977d4842f2880d Mon Sep 17 00:00:00 2001 From: huoshan12345 Date: Tue, 22 Jul 2025 01:23:30 +0800 Subject: [PATCH 15/19] revert some code --- src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs index a6bd57b1..cbabdacb 100644 --- a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs +++ b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs @@ -462,7 +462,7 @@ internal static MethodBuilder DefineExplicitMethod(MethodInfo method, Type implT return methodBuilder; } - internal static MethodBuilder DefineClassMethod(MethodInfo method, Type implType, TypeDesc typeDesc, MethodInfo covariantReturnMethod = null, bool isNewSlot = false) + internal static MethodBuilder DefineClassMethod(MethodInfo method, Type implType, TypeDesc typeDesc, MethodInfo covariantReturnMethod = null) { var attributes = OverrideMethodAttributes; @@ -481,11 +481,6 @@ internal static MethodBuilder DefineClassMethod(MethodInfo method, Type implType attributes |= MethodAttributes.FamORAssem; } - if (isNewSlot) - { - attributes |= MethodAttributes.NewSlot; - } - var methodBuilder = DefineMethod(method, method.Name, attributes, implType, typeDesc, covariantReturnMethod); return methodBuilder; } From e640c153005deaa230b3f3531d814a2b9c2c1fe8 Mon Sep 17 00:00:00 2001 From: huoshan12345 Date: Tue, 22 Jul 2025 07:46:00 +0800 Subject: [PATCH 16/19] fix some cases --- .../Extensions/MethodInfoExtensions.cs | 6 +- .../Extensions/TypeExtensions.cs | 31 ++++++---- .../Utils/ProxyGeneratorUtils.cs | 57 +++++++++++++++---- .../CovariantReturnMethodTests.cs | 37 ++++++------ 4 files changed, 89 insertions(+), 42 deletions(-) diff --git a/src/AspectCore.Core/Extensions/MethodInfoExtensions.cs b/src/AspectCore.Core/Extensions/MethodInfoExtensions.cs index ba79a0b2..dbb62c4e 100644 --- a/src/AspectCore.Core/Extensions/MethodInfoExtensions.cs +++ b/src/AspectCore.Core/Extensions/MethodInfoExtensions.cs @@ -1,15 +1,13 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Reflection; +using static AspectCore.Extensions.TypeExtensions; // ReSharper disable once CheckNamespace namespace AspectCore.Extensions { internal static class MethodInfoExtensions { - public static readonly Type PreserveBaseOverridesAttribute = Type.GetType("System.Runtime.CompilerServices.PreserveBaseOverridesAttribute", false); - public static IEnumerable GetInterfaceDeclarations(this MethodInfo method) { var typeInfo = method.ReflectedType?.GetTypeInfo(); diff --git a/src/AspectCore.Core/Extensions/TypeExtensions.cs b/src/AspectCore.Core/Extensions/TypeExtensions.cs index 00a581ae..8d9a4790 100644 --- a/src/AspectCore.Core/Extensions/TypeExtensions.cs +++ b/src/AspectCore.Core/Extensions/TypeExtensions.cs @@ -6,27 +6,34 @@ // ReSharper disable once CheckNamespace namespace AspectCore.Extensions { - internal static class TypeExtensions + internal readonly struct CovariantReturnMethodInfo { - public readonly struct CovariantReturnMethodInfo + public readonly MethodInfo CovariantReturnMethod; + public readonly MethodInfo OverridenMethod; + public readonly HashSet InterfaceDeclarations; + + public CovariantReturnMethodInfo(MethodInfo covariantReturnMethod, MethodInfo overridenMethod, HashSet interfaceDeclarations) { - public readonly MethodInfo CovariantReturnMethod; - public readonly MethodInfo OverridenMethod; - public readonly HashSet InterfaceDeclarations; + InterfaceDeclarations = interfaceDeclarations; + OverridenMethod = overridenMethod; + CovariantReturnMethod = covariantReturnMethod; + } + } - public CovariantReturnMethodInfo(MethodInfo covariantReturnMethod, MethodInfo overridenMethod, HashSet interfaceDeclarations) - { - InterfaceDeclarations = interfaceDeclarations; - OverridenMethod = overridenMethod; - CovariantReturnMethod = covariantReturnMethod; - } + internal static class TypeExtensions + { + public static readonly Type PreserveBaseOverridesAttribute = Type.GetType("System.Runtime.CompilerServices.PreserveBaseOverridesAttribute", false); + + public static bool IsPreserveBaseOverrides(this Type type) + { + return type == PreserveBaseOverridesAttribute; } public static IReadOnlyList GetCovariantReturnMethods(this Type type) { var result = new List(); // No PreserveBaseOverridesAttribute means that the runtime does not support covariant return types. - if (MethodInfoExtensions.PreserveBaseOverridesAttribute is null) + if (PreserveBaseOverridesAttribute is null) return result; var methods = type diff --git a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs index cbabdacb..2378e114 100644 --- a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs +++ b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs @@ -5,12 +5,15 @@ using System.Linq; using System.Reflection; using System.Reflection.Emit; +using System.Runtime.InteropServices.ComTypes; using System.Threading; using System.Threading.Tasks; +using System.Xml.Linq; using AspectCore.DynamicProxy; using AspectCore.Extensions; using AspectCore.Extensions.Reflection; using AspectCore.Extensions.Reflection.Emit; +using TypeExtensions = AspectCore.Extensions.TypeExtensions; namespace AspectCore.Utils { @@ -497,7 +500,7 @@ private static MethodBuilder DefineMethod(MethodInfo method, string name, Method //inherit targetMethod's attribute foreach (var customAttributeData in method.CustomAttributes) { - if (customAttributeData.AttributeType == AspectCore.Extensions.MethodInfoExtensions.PreserveBaseOverridesAttribute) + if (customAttributeData.AttributeType.IsPreserveBaseOverrides()) continue; methodBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(customAttributeData)); @@ -795,18 +798,34 @@ internal static void DefineClassProxyProperties(Type serviceType, Type implType, foreach (var property in serviceType.GetTypeInfo().GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) { - MethodInfo covariantReturnGetter = null; - - if (property.CanRead) - { - } - if (property.IsVisibleAndVirtual()) { + // covariant return property can only have getter. + if (property.CanRead && property.CanWrite == false) + { + var covariantReturn = covariantReturnMethods.FirstOrDefault(m => IsOverriddenByCovariantReturnProperty(property, m)); + var overriden = covariantReturn.OverridenMethod; + if (overriden != null) + { + // if method is the base definition of the overriden method, the CovariantReturnMethod is not in serviceType, so we need to add CovariantReturnMethod to implType. + // otherwise, the CovariantReturnMethod is also in serviceType, which will be added to implType in next for-loops. + if (overriden.GetBaseDefinition() == property.GetMethod) + { + var propertyBuilder = DefineInterfaceProxyProperty(property, property.Name, implType, typeDesc); + var method = MethodBuilderUtils.DefineClassMethod(covariantReturn.CovariantReturnMethod, implType, typeDesc); + propertyBuilder.SetGetMethod(method); + } + + // covariant return property is found, do not add property to implType. + continue; + } + } + var builder = DefineInterfaceProxyProperty(property, property.Name, implType, typeDesc); - DefineClassPropertyMethod(builder, property, implType, typeDesc, covariantReturnGetter); + DefineClassPropertyMethod(builder, property, implType, typeDesc); } } + foreach (var item in additionalInterfaces) { foreach (var property in item.GetTypeInfo().DeclaredProperties) @@ -815,13 +834,31 @@ internal static void DefineClassProxyProperties(Type serviceType, Type implType, DefineExplicitPropertyMethod(builder, property, implType, typeDesc); } } + + bool IsOverriddenByCovariantReturnProperty(PropertyInfo property, CovariantReturnMethodInfo info) + { + // this case occurs when the property is not overridden in the serviceType. + var get = property.GetMethod; + if (info.OverridenMethod.IsSameBaseDefinition(get)) + return true; + + // this case occurs when the property is overridden in the serviceType. + // in this case, the property type is super class of (and not the same as) the getter's type. + var covariantReturn = info.CovariantReturnMethod; + if (covariantReturn.IsSameBaseDefinition(get) + && covariantReturn.ReturnType != property.PropertyType + && property.PropertyType.IsAssignableFrom(covariantReturn.ReturnType)) + return true; + + return false; + } } - private static void DefineClassPropertyMethod(PropertyBuilder propertyBuilder, PropertyInfo property, Type implType, TypeDesc typeDesc, MethodInfo covariantReturnGetter = null) + private static void DefineClassPropertyMethod(PropertyBuilder propertyBuilder, PropertyInfo property, Type implType, TypeDesc typeDesc) { if (property.CanRead) { - var method = MethodBuilderUtils.DefineClassMethod(property.GetMethod, implType, typeDesc, covariantReturnGetter); + var method = MethodBuilderUtils.DefineClassMethod(property.GetMethod, implType, typeDesc); propertyBuilder.SetGetMethod(method); } if (property.CanWrite) diff --git a/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs b/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs index cca2b395..3cd662d3 100644 --- a/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs +++ b/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs @@ -26,7 +26,7 @@ public override async Task Invoke(AspectContext context, AspectDelegate next) public interface IService { - //object Property { get; } + object Property { get; } object Method(); [Interceptor] @@ -35,7 +35,7 @@ public interface IService public class Service : IService { - //public virtual object Property { get; } = 1; + public virtual object Property { get; } = 1; public virtual object Method() => 1; [Interceptor] @@ -44,7 +44,7 @@ public class Service : IService public class CovariantReturnsService : Service { - //public override string Property { get; } = nameof(CovariantReturnsService); + public override string Property { get; } = nameof(CovariantReturnsService); public override string Method() => nameof(CovariantReturnsService); [Interceptor] @@ -53,17 +53,20 @@ public class CovariantReturnsService : Service public class DerivedCovariantReturnsService : CovariantReturnsService { - public override string Method() => nameof(DerivedCovariantReturnsService); - [Interceptor] public override string ProxyMethod() => nameof(DerivedCovariantReturnsService); } + public class DerivedCovariantReturnsService2 : DerivedCovariantReturnsService + { + public override string Property { get; } = nameof(CovariantReturnsService); + } + [Fact] public void CreateClassProxy_CovariantReturnsService_Test() { var service = ProxyGenerator.CreateClassProxy(); - //Assert.Equal(nameof(CovariantReturnsService), service.Property); + Assert.Equal(nameof(CovariantReturnsService), service.Property); Assert.Equal(nameof(CovariantReturnsService), service.Method()); Assert.Equal(nameof(CovariantReturnsService) + nameof(Interceptor), service.ProxyMethod()); } @@ -71,9 +74,11 @@ public void CreateClassProxy_CovariantReturnsService_Test() [Fact] public void CreateClassProxy_DerivedCovariantReturnsService_Test() { + return; // 会死锁, 待修. DefineClassPropertyMethod + var service = ProxyGenerator.CreateClassProxy(); - //Assert.Equal(nameof(CovariantReturnsService), service.Property); - Assert.Equal(nameof(DerivedCovariantReturnsService), service.Method()); + Assert.Equal(nameof(CovariantReturnsService), service.Property); + Assert.Equal(nameof(CovariantReturnsService), service.Method()); Assert.Equal(nameof(DerivedCovariantReturnsService) + nameof(Interceptor), service.ProxyMethod()); } @@ -81,7 +86,7 @@ public void CreateClassProxy_DerivedCovariantReturnsService_Test() public void CreateClassProxy_Service_CovariantReturnsService_Test() { var service = ProxyGenerator.CreateClassProxy(); - //Assert.Equal(nameof(CovariantReturnsService), service.Property); + Assert.Equal(nameof(CovariantReturnsService), service.Property); Assert.Equal(nameof(CovariantReturnsService), service.Method()); Assert.Equal(nameof(CovariantReturnsService) + nameof(Interceptor), service.ProxyMethod()); } @@ -90,8 +95,8 @@ public void CreateClassProxy_Service_CovariantReturnsService_Test() public void CreateClassProxy_Service_DerivedCovariantReturnsService_Test() { var service = ProxyGenerator.CreateClassProxy(); - //Assert.Equal(nameof(CovariantReturnsService), service.Property); - Assert.Equal(nameof(DerivedCovariantReturnsService), service.Method()); + Assert.Equal(nameof(CovariantReturnsService), service.Property); + Assert.Equal(nameof(CovariantReturnsService), service.Method()); Assert.Equal(nameof(DerivedCovariantReturnsService) + nameof(Interceptor), service.ProxyMethod()); } @@ -99,8 +104,8 @@ public void CreateClassProxy_Service_DerivedCovariantReturnsService_Test() public void CreateClassProxy_CovariantReturnsService_DerivedCovariantReturnsService_Test() { var service = ProxyGenerator.CreateClassProxy(); - //Assert.Equal(nameof(CovariantReturnsService), service.Property); - Assert.Equal(nameof(DerivedCovariantReturnsService), service.Method()); + Assert.Equal(nameof(CovariantReturnsService), service.Property); + Assert.Equal(nameof(CovariantReturnsService), service.Method()); Assert.Equal(nameof(DerivedCovariantReturnsService) + nameof(Interceptor), service.ProxyMethod()); } @@ -108,7 +113,7 @@ public void CreateClassProxy_CovariantReturnsService_DerivedCovariantReturnsServ public void CreateInterfaceProxy_IService_CovariantReturnsService_Test() { var service = ProxyGenerator.CreateInterfaceProxy(); - //Assert.Equal(nameof(CovariantReturnsService), service.Property); + Assert.Equal(nameof(CovariantReturnsService), service.Property); Assert.Equal(nameof(CovariantReturnsService), service.Method()); Assert.Equal(nameof(CovariantReturnsService) + nameof(Interceptor), service.ProxyMethod()); } @@ -117,8 +122,8 @@ public void CreateInterfaceProxy_IService_CovariantReturnsService_Test() public void CreateInterfaceProxy_IService_DerivedCovariantReturnsService_Test() { var service = ProxyGenerator.CreateInterfaceProxy(); - //Assert.Equal(nameof(CovariantReturnsService), service.Property); - Assert.Equal(nameof(DerivedCovariantReturnsService), service.Method()); + Assert.Equal(nameof(CovariantReturnsService), service.Property); + Assert.Equal(nameof(CovariantReturnsService), service.Method()); Assert.Equal(nameof(DerivedCovariantReturnsService) + nameof(Interceptor), service.ProxyMethod()); } } From 2caca2f77999affd226dfa9e72017dfcd6d6a61d Mon Sep 17 00:00:00 2001 From: huoshan12345 Date: Tue, 22 Jul 2025 07:52:29 +0800 Subject: [PATCH 17/19] cleanup --- src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs index 2378e114..e3cd0a14 100644 --- a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs +++ b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs @@ -5,15 +5,12 @@ using System.Linq; using System.Reflection; using System.Reflection.Emit; -using System.Runtime.InteropServices.ComTypes; using System.Threading; using System.Threading.Tasks; -using System.Xml.Linq; using AspectCore.DynamicProxy; using AspectCore.Extensions; using AspectCore.Extensions.Reflection; using AspectCore.Extensions.Reflection.Emit; -using TypeExtensions = AspectCore.Extensions.TypeExtensions; namespace AspectCore.Utils { @@ -465,7 +462,7 @@ internal static MethodBuilder DefineExplicitMethod(MethodInfo method, Type implT return methodBuilder; } - internal static MethodBuilder DefineClassMethod(MethodInfo method, Type implType, TypeDesc typeDesc, MethodInfo covariantReturnMethod = null) + internal static MethodBuilder DefineClassMethod(MethodInfo method, Type implType, TypeDesc typeDesc) { var attributes = OverrideMethodAttributes; @@ -484,7 +481,7 @@ internal static MethodBuilder DefineClassMethod(MethodInfo method, Type implType attributes |= MethodAttributes.FamORAssem; } - var methodBuilder = DefineMethod(method, method.Name, attributes, implType, typeDesc, covariantReturnMethod); + var methodBuilder = DefineMethod(method, method.Name, attributes, implType, typeDesc); return methodBuilder; } From 5d607cba334404bcc7a251390f7d424675441d13 Mon Sep 17 00:00:00 2001 From: huoshan12345 Date: Tue, 22 Jul 2025 22:53:39 +0800 Subject: [PATCH 18/19] fix a test case --- .../Extensions/TypeExtensions.cs | 5 ----- .../Utils/ProxyGeneratorUtils.cs | 16 ++++++++++------ src/AspectCore.Core/Utils/ReflectionUtils.cs | 2 +- .../DynamicProxy/CovariantReturnMethodTests.cs | 18 +++++++++++------- 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/AspectCore.Core/Extensions/TypeExtensions.cs b/src/AspectCore.Core/Extensions/TypeExtensions.cs index 8d9a4790..8512cd33 100644 --- a/src/AspectCore.Core/Extensions/TypeExtensions.cs +++ b/src/AspectCore.Core/Extensions/TypeExtensions.cs @@ -24,11 +24,6 @@ internal static class TypeExtensions { public static readonly Type PreserveBaseOverridesAttribute = Type.GetType("System.Runtime.CompilerServices.PreserveBaseOverridesAttribute", false); - public static bool IsPreserveBaseOverrides(this Type type) - { - return type == PreserveBaseOverridesAttribute; - } - public static IReadOnlyList GetCovariantReturnMethods(this Type type) { var result = new List(); diff --git a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs index e3cd0a14..efd72c20 100644 --- a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs +++ b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs @@ -497,9 +497,6 @@ private static MethodBuilder DefineMethod(MethodInfo method, string name, Method //inherit targetMethod's attribute foreach (var customAttributeData in method.CustomAttributes) { - if (customAttributeData.AttributeType.IsPreserveBaseOverrides()) - continue; - methodBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(customAttributeData)); } @@ -857,6 +854,13 @@ private static void DefineClassPropertyMethod(PropertyBuilder propertyBuilder, P { var method = MethodBuilderUtils.DefineClassMethod(property.GetMethod, implType, typeDesc); propertyBuilder.SetGetMethod(method); + + if (property.GetMethod.IsPreserveBaseOverride(true)) + { + // property.GetMethod is a covariant return type method, we need to define an override for it. + // otherwise, the TypeDesc.Compile() will run forever. + typeDesc.Builder.DefineMethodOverride(method, property.GetMethod); + } } if (property.CanWrite) { @@ -990,10 +994,10 @@ public static void DefineParameters(MethodInfo targetMethod, MethodBuilder metho } } - var returnParamter = targetMethod.ReturnParameter; - var returnParameterBuilder = methodBuilder.DefineParameter(0, returnParamter.Attributes, returnParamter.Name); + var returnParameter = targetMethod.ReturnParameter; + var returnParameterBuilder = methodBuilder.DefineParameter(0, returnParameter.Attributes, returnParameter.Name); returnParameterBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(typeof(DynamicallyAttribute))); - foreach (var attribute in returnParamter.CustomAttributes) + foreach (var attribute in returnParameter.CustomAttributes) { returnParameterBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(attribute)); } diff --git a/src/AspectCore.Core/Utils/ReflectionUtils.cs b/src/AspectCore.Core/Utils/ReflectionUtils.cs index b4a67aae..ec13b0a8 100644 --- a/src/AspectCore.Core/Utils/ReflectionUtils.cs +++ b/src/AspectCore.Core/Utils/ReflectionUtils.cs @@ -168,7 +168,7 @@ public static bool IsVisibleAndVirtual(this PropertyInfo property) throw new ArgumentNullException(nameof(property)); } return (property.CanRead && property.GetMethod.IsVisibleAndVirtual()) || - (property.CanWrite && property.GetMethod.IsVisibleAndVirtual()); + (property.CanWrite && property.SetMethod.IsVisibleAndVirtual()); } public static bool IsVisibleAndVirtual(this MethodInfo method) diff --git a/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs b/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs index 3cd662d3..8ba5ca11 100644 --- a/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs +++ b/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs @@ -29,6 +29,7 @@ public interface IService object Property { get; } object Method(); + object ProxyProperty { [Interceptor] get; } [Interceptor] object ProxyMethod(); } @@ -38,6 +39,7 @@ public class Service : IService public virtual object Property { get; } = 1; public virtual object Method() => 1; + public virtual object ProxyProperty { [Interceptor] get; } = new(); [Interceptor] public virtual object ProxyMethod() => new(); } @@ -47,38 +49,35 @@ public class CovariantReturnsService : Service public override string Property { get; } = nameof(CovariantReturnsService); public override string Method() => nameof(CovariantReturnsService); + public override string ProxyProperty { [Interceptor] get; } = nameof(CovariantReturnsService); [Interceptor] public override string ProxyMethod() => nameof(CovariantReturnsService); } public class DerivedCovariantReturnsService : CovariantReturnsService { + public override string ProxyProperty { [Interceptor] get; } = nameof(DerivedCovariantReturnsService); [Interceptor] public override string ProxyMethod() => nameof(DerivedCovariantReturnsService); } - public class DerivedCovariantReturnsService2 : DerivedCovariantReturnsService - { - public override string Property { get; } = nameof(CovariantReturnsService); - } - [Fact] public void CreateClassProxy_CovariantReturnsService_Test() { var service = ProxyGenerator.CreateClassProxy(); Assert.Equal(nameof(CovariantReturnsService), service.Property); Assert.Equal(nameof(CovariantReturnsService), service.Method()); + Assert.Equal(nameof(CovariantReturnsService) + nameof(Interceptor), service.ProxyProperty); Assert.Equal(nameof(CovariantReturnsService) + nameof(Interceptor), service.ProxyMethod()); } [Fact] public void CreateClassProxy_DerivedCovariantReturnsService_Test() { - return; // 会死锁, 待修. DefineClassPropertyMethod - var service = ProxyGenerator.CreateClassProxy(); Assert.Equal(nameof(CovariantReturnsService), service.Property); Assert.Equal(nameof(CovariantReturnsService), service.Method()); + Assert.Equal(nameof(DerivedCovariantReturnsService) + nameof(Interceptor), service.ProxyProperty); Assert.Equal(nameof(DerivedCovariantReturnsService) + nameof(Interceptor), service.ProxyMethod()); } @@ -88,6 +87,7 @@ public void CreateClassProxy_Service_CovariantReturnsService_Test() var service = ProxyGenerator.CreateClassProxy(); Assert.Equal(nameof(CovariantReturnsService), service.Property); Assert.Equal(nameof(CovariantReturnsService), service.Method()); + Assert.Equal(nameof(CovariantReturnsService) + nameof(Interceptor), service.ProxyProperty); Assert.Equal(nameof(CovariantReturnsService) + nameof(Interceptor), service.ProxyMethod()); } @@ -97,6 +97,7 @@ public void CreateClassProxy_Service_DerivedCovariantReturnsService_Test() var service = ProxyGenerator.CreateClassProxy(); Assert.Equal(nameof(CovariantReturnsService), service.Property); Assert.Equal(nameof(CovariantReturnsService), service.Method()); + Assert.Equal(nameof(DerivedCovariantReturnsService) + nameof(Interceptor), service.ProxyProperty); Assert.Equal(nameof(DerivedCovariantReturnsService) + nameof(Interceptor), service.ProxyMethod()); } @@ -106,6 +107,7 @@ public void CreateClassProxy_CovariantReturnsService_DerivedCovariantReturnsServ var service = ProxyGenerator.CreateClassProxy(); Assert.Equal(nameof(CovariantReturnsService), service.Property); Assert.Equal(nameof(CovariantReturnsService), service.Method()); + Assert.Equal(nameof(DerivedCovariantReturnsService) + nameof(Interceptor), service.ProxyProperty); Assert.Equal(nameof(DerivedCovariantReturnsService) + nameof(Interceptor), service.ProxyMethod()); } @@ -115,6 +117,7 @@ public void CreateInterfaceProxy_IService_CovariantReturnsService_Test() var service = ProxyGenerator.CreateInterfaceProxy(); Assert.Equal(nameof(CovariantReturnsService), service.Property); Assert.Equal(nameof(CovariantReturnsService), service.Method()); + Assert.Equal(nameof(CovariantReturnsService) + nameof(Interceptor), service.ProxyProperty); Assert.Equal(nameof(CovariantReturnsService) + nameof(Interceptor), service.ProxyMethod()); } @@ -124,6 +127,7 @@ public void CreateInterfaceProxy_IService_DerivedCovariantReturnsService_Test() var service = ProxyGenerator.CreateInterfaceProxy(); Assert.Equal(nameof(CovariantReturnsService), service.Property); Assert.Equal(nameof(CovariantReturnsService), service.Method()); + Assert.Equal(nameof(DerivedCovariantReturnsService) + nameof(Interceptor), service.ProxyProperty); Assert.Equal(nameof(DerivedCovariantReturnsService) + nameof(Interceptor), service.ProxyMethod()); } } From 38311afa8e39fec837131e99da9453f2ab4d3b00 Mon Sep 17 00:00:00 2001 From: huoshan12345 Date: Tue, 22 Jul 2025 23:39:58 +0800 Subject: [PATCH 19/19] refine code --- .../Utils/ProxyGeneratorUtils.cs | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs index efd72c20..ca3dc6a6 100644 --- a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs +++ b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs @@ -395,23 +395,24 @@ internal static void DefineInterfaceProxyMethods(Type interfaceType, Type target var covariantReturnMethods = targetType.GetCovariantReturnMethods(); foreach (var method in interfaceType.GetTypeInfo().DeclaredMethods.Where(x => !x.IsPropertyBinding())) { - var covariantReturnMethod = covariantReturnMethods - .FirstOrDefault(m => m.InterfaceDeclarations.Contains(method)) - .CovariantReturnMethod; - + var covariantReturnMethod = GetCovariantReturnMethod(method); DefineInterfaceMethod(method, targetType, typeDesc, covariantReturnMethod); } foreach (var item in additionalInterfaces) { foreach (var method in item.GetTypeInfo().DeclaredMethods.Where(x => !x.IsPropertyBinding())) { - var covariantReturnMethod = covariantReturnMethods - .FirstOrDefault(m => m.InterfaceDeclarations.Contains(method)) - .CovariantReturnMethod; - + var covariantReturnMethod = GetCovariantReturnMethod(method); DefineExplicitMethod(method, targetType, typeDesc, covariantReturnMethod); } } + + MethodInfo GetCovariantReturnMethod(MethodInfo interfaceMethod) + { + return covariantReturnMethods + .FirstOrDefault(m => m.InterfaceDeclarations.Contains(interfaceMethod)) + .CovariantReturnMethod; + } } internal static void DefineClassProxyMethods(Type serviceType, Type implType, Type[] additionalInterfaces, TypeDesc typeDesc) @@ -421,15 +422,15 @@ internal static void DefineClassProxyMethods(Type serviceType, Type implType, Ty { if (method.IsVisibleAndVirtual() && !_ignores.Contains(method.Name)) { - var covariantReturnMethod = covariantReturnMethods.FirstOrDefault(m => m.OverridenMethod.IsSameBaseDefinition(method)); - var overriden = covariantReturnMethod.OverridenMethod; + var covariantReturn = covariantReturnMethods.FirstOrDefault(m => m.OverridenMethod.IsSameBaseDefinition(method)); + var overriden = covariantReturn.OverridenMethod; if (overriden != null) { // if method is the base definition of the overriden method, the CovariantReturnMethod is not in serviceType, so we need to add CovariantReturnMethod to implType. // otherwise, the CovariantReturnMethod is also in serviceType, which will be added to implType in next for-loops. if (overriden.GetBaseDefinition() == method) { - DefineClassMethod(covariantReturnMethod.CovariantReturnMethod, implType, typeDesc); + DefineClassMethod(covariantReturn.CovariantReturnMethod, implType, typeDesc); } // covariantReturnMethod is found, do not add method to implType. @@ -767,23 +768,26 @@ public static void DefineInterfaceProxyProperties(Type interfaceType, Type implT foreach (var property in interfaceType.GetTypeInfo().DeclaredProperties) { + var covariantReturnGetter = FindCovariantReturnGetter(property); var builder = DefineInterfaceProxyProperty(property, property.Name, implType, typeDesc); - var covariantReturnGetter = property.CanRead - ? covariantReturnMethods.FirstOrDefault(m => m.InterfaceDeclarations.Contains(property.GetMethod)).CovariantReturnMethod - : null; DefineInterfacePropertyMethod(builder, property, implType, typeDesc, covariantReturnGetter); } foreach (var item in additionalInterfaces) { foreach (var property in item.GetTypeInfo().DeclaredProperties) { + var covariantReturnGetter = FindCovariantReturnGetter(property); var builder = DefineInterfaceProxyProperty(property, property.GetDisplayName(), implType, typeDesc); - var covariantReturnGetter = property.CanRead - ? covariantReturnMethods.FirstOrDefault(m => m.InterfaceDeclarations.Contains(property.GetMethod)).CovariantReturnMethod - : null; DefineExplicitPropertyMethod(builder, property, implType, typeDesc, covariantReturnGetter); } } + + MethodInfo FindCovariantReturnGetter(PropertyInfo property) + { + return property.CanRead + ? covariantReturnMethods.FirstOrDefault(m => m.InterfaceDeclarations.Contains(property.GetMethod)).CovariantReturnMethod + : null; + } } internal static void DefineClassProxyProperties(Type serviceType, Type implType, Type[] additionalInterfaces, TypeDesc typeDesc)