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/EnumerableExtensions.cs b/src/AspectCore.Core/Extensions/EnumerableExtensions.cs new file mode 100644 index 00000000..0706d89f --- /dev/null +++ b/src/AspectCore.Core/Extensions/EnumerableExtensions.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +// ReSharper disable once CheckNamespace +namespace System.Linq +{ + 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..dbb62c4e --- /dev/null +++ b/src/AspectCore.Core/Extensions/MethodInfoExtensions.cs @@ -0,0 +1,45 @@ +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 IEnumerable GetInterfaceDeclarations(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; + } + } + } + + public static bool IsPreserveBaseOverride(this MethodInfo method, bool checkBase) + { + if (PreserveBaseOverridesAttribute is null) + return false; + + if (method.IsDefined(PreserveBaseOverridesAttribute)) + return true; + + return checkBase && method.GetBaseDefinition().IsDefined(PreserveBaseOverridesAttribute); + } + + public static bool IsSameBaseDefinition(this MethodInfo method, MethodInfo other) + { + return method.GetBaseDefinition() == other.GetBaseDefinition(); + } + } +} + diff --git a/src/AspectCore.Core/Extensions/TypeExtensions.cs b/src/AspectCore.Core/Extensions/TypeExtensions.cs new file mode 100644 index 00000000..8512cd33 --- /dev/null +++ b/src/AspectCore.Core/Extensions/TypeExtensions.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +// ReSharper disable once CheckNamespace +namespace AspectCore.Extensions +{ + internal 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; + } + } + + internal static class TypeExtensions + { + public static readonly Type PreserveBaseOverridesAttribute = Type.GetType("System.Runtime.CompilerServices.PreserveBaseOverridesAttribute", false); + + public static IReadOnlyList GetCovariantReturnMethods(this Type type) + { + var result = new List(); + // No PreserveBaseOverridesAttribute means that the runtime does not support covariant return types. + if (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 0c635387..ca3dc6a6 100644 --- a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs +++ b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; using System.Linq; @@ -9,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using AspectCore.DynamicProxy; +using AspectCore.Extensions; using AspectCore.Extensions.Reflection; using AspectCore.Extensions.Reflection.Emit; @@ -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) { @@ -392,25 +392,53 @@ internal static MethodBuilder DefineInterfaceImplMethod(MethodInfo method, TypeB internal static void DefineInterfaceProxyMethods(Type interfaceType, Type targetType, Type[] additionalInterfaces, TypeDesc typeDesc) { + var covariantReturnMethods = targetType.GetCovariantReturnMethods(); foreach (var method in interfaceType.GetTypeInfo().DeclaredMethods.Where(x => !x.IsPropertyBinding())) { - DefineInterfaceMethod(method, targetType, typeDesc); + 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())) { - DefineExplicitMethod(method, targetType, typeDesc); + 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) { + var covariantReturnMethods = implType.GetCovariantReturnMethods(); 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)) + { + 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(covariantReturn.CovariantReturnMethod, implType, typeDesc); + } + + // covariantReturnMethod is found, do not add method to implType. + continue; + } + DefineClassMethod(method, implType, typeDesc); + } } foreach (var item in additionalInterfaces) { @@ -421,16 +449,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; } @@ -441,24 +469,24 @@ 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; } 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) + 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()); @@ -473,10 +501,10 @@ private static MethodBuilder DefineMethod(MethodInfo method, string name, Method methodBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(customAttributeData)); } - //define paramters + //define parameters ParameterBuilderUtils.DefineParameters(method, methodBuilder); - var implementationMethod = implType.GetTypeInfo().GetMethodBySignature(method); + var implementationMethod = covariantReturnMethod ?? implType.GetTypeInfo().GetMethodBySignature(method); if (implementationMethod == null) { var interfaces = implType.GetInterfaces(); @@ -608,7 +636,7 @@ void EmitProxyMethodBody() ilGen.Emit(OpCodes.Callvirt, MethodUtils.CreateAspectActivator); ilGen.Emit(OpCodes.Ldloc, activatorContext); - EmitReturnVaule(ilGen); + EmitReturnValue(ilGen); if (method.ReturnType != typeof(void)) { @@ -699,7 +727,7 @@ void EmitInitializeMetaData(ILGenerator ilGen) } } - void EmitReturnVaule(ILGenerator ilGen) + void EmitReturnValue(ILGenerator ilGen) { if (method.ReturnType == typeof(void)) { @@ -736,31 +764,66 @@ private class PropertyBuilderUtils { public static void DefineInterfaceProxyProperties(Type interfaceType, Type implType, Type[] additionalInterfaces, TypeDesc typeDesc) { + var covariantReturnMethods = implType.GetCovariantReturnMethods(); + foreach (var property in interfaceType.GetTypeInfo().DeclaredProperties) { + var covariantReturnGetter = FindCovariantReturnGetter(property); var builder = DefineInterfaceProxyProperty(property, property.Name, implType, typeDesc); - DefineInterfacePropertyMethod(builder, property, implType, typeDesc); + 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); - DefineExplicitPropertyMethod(builder, property, implType, typeDesc); + 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) { + var covariantReturnMethods = implType.GetCovariantReturnMethods(); + foreach (var property in serviceType.GetTypeInfo().GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) { 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); } } + foreach (var item in additionalInterfaces) { foreach (var property in item.GetTypeInfo().DeclaredProperties) @@ -769,6 +832,24 @@ 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) @@ -777,6 +858,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) { @@ -785,11 +873,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) @@ -799,11 +887,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) @@ -910,10 +998,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)); } @@ -1084,13 +1172,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/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/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.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); } } } 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(); diff --git a/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs b/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs new file mode 100644 index 00000000..8ba5ca11 --- /dev/null +++ b/tests/AspectCore.Tests/DynamicProxy/CovariantReturnMethodTests.cs @@ -0,0 +1,133 @@ +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 Property { get; } + object Method(); + + object ProxyProperty { [Interceptor] get; } + [Interceptor] + object ProxyMethod(); + } + + 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(); + } + + 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); + } + + [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() + { + 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()); + } + + [Fact] + 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()); + } + + [Fact] + 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()); + } + + [Fact] + public void CreateClassProxy_CovariantReturnsService_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()); + } + + [Fact] + 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()); + } + + [Fact] + 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()); + } +}