From 80299643e7ffa7ab8e5a1daa90c2c9b92de6a081 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A9nes=20Solti?= Date: Mon, 21 Nov 2022 06:42:20 +0100 Subject: [PATCH] introduce InterfaceInterceptor{TInterface, TTarget} class --- SRC/Private/Extensions/ITypeInfoExtensions.cs | 5 +++++ .../SyntaxFactories/ProxySyntaxFactory.cs | 19 ++++++++++++----- ...roxyGenerator{TInterface, TInterceptor}.cs | 6 ++++-- ...erfaceInterceptor{TInterface, TTarget}.cs} | 19 ++++++++++------- .../InterfaceInterceptor{TInterface}r.cs | 21 +++++++++++++++++++ SRC/Public/InvocationContext.cs | 2 +- .../netstandard2.0/PublicAPI.Shipped.txt | 5 +---- .../netstandard2.0/PublicAPI.Unshipped.txt | 5 +++++ .../netstandard2.1/PublicAPI.Shipped.txt | 5 +---- .../netstandard2.1/PublicAPI.Unshipped.txt | 5 +++++ 10 files changed, 68 insertions(+), 24 deletions(-) rename SRC/Public/{InterfaceInterceptor.cs => InterfaceInterceptor{TInterface, TTarget}.cs} (73%) create mode 100644 SRC/Public/InterfaceInterceptor{TInterface}r.cs diff --git a/SRC/Private/Extensions/ITypeInfoExtensions.cs b/SRC/Private/Extensions/ITypeInfoExtensions.cs index 12d8cd88..54d1c471 100644 --- a/SRC/Private/Extensions/ITypeInfoExtensions.cs +++ b/SRC/Private/Extensions/ITypeInfoExtensions.cs @@ -160,6 +160,11 @@ private static IEnumerable IterateOn(this ITypeInfo src, Func GetParentTypes(this ITypeInfo src) => new Stack(src.GetEnclosingTypes()); + public static bool IsAccessibleFrom(this ITypeInfo src, ITypeInfo type) => + type.EqualsTo(src) || + type.Interfaces.Some(iface => iface.EqualsTo(src)) || + type.GetBaseTypes().Some(baseType => baseType.EqualsTo(src)); + public static ITypeSymbol ToSymbol(this ITypeInfo src, Compilation compilation) { INamedTypeSymbol? symbol; diff --git a/SRC/Private/SyntaxFactories/ProxySyntaxFactory.cs b/SRC/Private/SyntaxFactories/ProxySyntaxFactory.cs index c95eb914..d2d9db50 100644 --- a/SRC/Private/SyntaxFactories/ProxySyntaxFactory.cs +++ b/SRC/Private/SyntaxFactories/ProxySyntaxFactory.cs @@ -20,6 +20,8 @@ internal partial class ProxySyntaxFactory: ProxyUnitSyntaxFactory public ITypeInfo InterceptorType { get; } + public ITypeInfo TargetType { get; } + public IMethodInfo Invoke { get; } #if DEBUG @@ -53,7 +55,10 @@ protected override IEnumerable ResolveClasses(object con if (interfaceType is IGenericTypeInfo genericIface && genericIface.IsGenericDefinition) throw new ArgumentException(Resources.GENERIC_IFACE, nameof(interfaceType)); - string baseInterceptorName = typeof(InterfaceInterceptor<>).FullName; + if (interceptorType is IGenericTypeInfo genericInterceptor && genericInterceptor.IsGenericDefinition) + throw new ArgumentException(Resources.GENERIC_INTERCEPTOR, nameof(interceptorType)); + + string baseInterceptorName = typeof(InterfaceInterceptor<,>).FullName; IGenericTypeInfo? baseInterceptor = (IGenericTypeInfo?) ( @@ -61,14 +66,18 @@ protected override IEnumerable ResolveClasses(object con ? interceptorType : interceptorType.GetBaseTypes().Single(ic => ic.QualifiedName == baseInterceptorName, throwOnEmpty: false) ); - if (baseInterceptor?.GenericArguments?.Single()?.EqualsTo(interfaceType) is not true) - throw new ArgumentException(Resources.NOT_AN_INTERCEPTOR, nameof(interceptorType)); - if (interceptorType is IGenericTypeInfo genericInterceptor && genericInterceptor.IsGenericDefinition) - throw new ArgumentException(Resources.GENERIC_INTERCEPTOR, nameof(interceptorType)); + bool validInterceptor = + baseInterceptor is not null && + baseInterceptor.GenericArguments[0].Equals(interfaceType) && + interfaceType.IsAccessibleFrom(baseInterceptor.GenericArguments[1]); + + if (!validInterceptor) + throw new ArgumentException(Resources.NOT_AN_INTERCEPTOR, nameof(interceptorType)); InterfaceType = interfaceType; InterceptorType = interceptorType; + TargetType = baseInterceptor!.GenericArguments[1]; Invoke = InterceptorType.Methods.Single ( diff --git a/SRC/Public/Generators/ProxyGenerator{TInterface, TInterceptor}.cs b/SRC/Public/Generators/ProxyGenerator{TInterface, TInterceptor}.cs index 44a0c930..79e7ac8c 100644 --- a/SRC/Public/Generators/ProxyGenerator{TInterface, TInterceptor}.cs +++ b/SRC/Public/Generators/ProxyGenerator{TInterface, TInterceptor}.cs @@ -11,8 +11,10 @@ namespace Solti.Utils.Proxy.Generators /// Type generator for creating proxies that intercept interface method calls. /// /// The interface for which the proxy will be created. - /// An descendant that has at least one public constructor. - public sealed class ProxyGenerator : Generator> where TInterface : class where TInterceptor: InterfaceInterceptor + /// An descendant that has at least one public constructor. + public sealed class ProxyGenerator : Generator> + where TInterface : class + where TInterceptor: InterfaceInterceptor { /// protected override Generator GetConcreteGenerator() => new ProxyGenerator(typeof(TInterface), typeof(TInterceptor)); diff --git a/SRC/Public/InterfaceInterceptor.cs b/SRC/Public/InterfaceInterceptor{TInterface, TTarget}.cs similarity index 73% rename from SRC/Public/InterfaceInterceptor.cs rename to SRC/Public/InterfaceInterceptor{TInterface, TTarget}.cs index ecf2abb2..c9c56542 100644 --- a/SRC/Public/InterfaceInterceptor.cs +++ b/SRC/Public/InterfaceInterceptor{TInterface, TTarget}.cs @@ -1,5 +1,5 @@ /******************************************************************************** -* InterfaceInterceptor.cs * +* InterfaceInterceptor{TInterface, TTarget}.cs * * * * Author: Denes Solti * ********************************************************************************/ @@ -14,30 +14,33 @@ namespace Solti.Utils.Proxy /// Provides the mechanism for intercepting interface method calls. /// /// The interface to be intercepted. - public class InterfaceInterceptor: GeneratedClass, IHasTarget, IProxyAccess where TInterface: class + /// The target interface implementation. + public class InterfaceInterceptor : GeneratedClass, IHasTarget, IProxyAccess + where TInterface : class + where TTarget : TInterface { /// /// The target of this interceptor. /// - public TInterface? Target { get; } + public TTarget? Target { get; } /// /// The most outer enclosing proxy. /// public TInterface Proxy { - set + set { if (Target is IProxyAccess proxyAccess) proxyAccess.Proxy = value ?? throw new ArgumentNullException(nameof(value)); - } + } } /// - /// Creates a new instance against the given . + /// Creates a new instance against the given . /// /// The target of this interceptor. - public InterfaceInterceptor(TInterface? target) => Target = target; + public InterfaceInterceptor(TTarget? target) => Target = target; /// /// Called on proxy method invocation. @@ -51,7 +54,7 @@ public TInterface Proxy if (Target is null) throw new InvalidOperationException(Resources.NULL_TARGET); - return context.Dispatch(Target, context.Args); + return context.Dispatch(Target, context.Args); } } } diff --git a/SRC/Public/InterfaceInterceptor{TInterface}r.cs b/SRC/Public/InterfaceInterceptor{TInterface}r.cs new file mode 100644 index 00000000..abe5dfae --- /dev/null +++ b/SRC/Public/InterfaceInterceptor{TInterface}r.cs @@ -0,0 +1,21 @@ +/******************************************************************************** +* InterfaceInterceptor{TInterface}r.cs * +* * +* Author: Denes Solti * +********************************************************************************/ + +namespace Solti.Utils.Proxy +{ + /// + /// Provides the mechanism for intercepting interface method calls. + /// + /// The interface to be intercepted. + public class InterfaceInterceptor : InterfaceInterceptor where TInterface : class + { + /// + /// Creates a new instance against the given . + /// + /// The target of this interceptor. + public InterfaceInterceptor(TInterface? target): base(target) { } + } +} diff --git a/SRC/Public/InvocationContext.cs b/SRC/Public/InvocationContext.cs index b37422ed..61456374 100644 --- a/SRC/Public/InvocationContext.cs +++ b/SRC/Public/InvocationContext.cs @@ -29,7 +29,7 @@ public InvocationContext(object?[] args, MethodContext methodContext): base(meth /// /// The arguments passed by the caller. /// - /// Before the gets called you may use this property to inspect or modify parameters passed by the caller. After it you can read or amend the "by ref" parameters set by the target method. + /// Before the gets called you may use this property to inspect or modify parameters passed by the caller. After it you can read or amend the "by ref" parameters set by the target method. [SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "End user is allowed to modify the argument list.")] public object?[] Args { get; } } diff --git a/SRC/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt b/SRC/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt index 0aa81599..450cd1c0 100644 --- a/SRC/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt +++ b/SRC/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt @@ -17,8 +17,6 @@ Solti.Utils.Proxy.Generators.ProxyGenerator Solti.Utils.Proxy.Generators.ProxyGenerator.ProxyGenerator() -> void Solti.Utils.Proxy.InterfaceInterceptor Solti.Utils.Proxy.InterfaceInterceptor.InterfaceInterceptor(TInterface? target) -> void -Solti.Utils.Proxy.InterfaceInterceptor.Proxy.set -> void -Solti.Utils.Proxy.InterfaceInterceptor.Target.get -> TInterface? Solti.Utils.Proxy.Internals.DuckBase Solti.Utils.Proxy.Internals.DuckBase.DuckBase(T target) -> void Solti.Utils.Proxy.Internals.DuckBase.Target.get -> T @@ -49,5 +47,4 @@ static Solti.Utils.Proxy.Internals.Generator.Activate(o static Solti.Utils.Proxy.Internals.Generator.ActivateAsync(object? tuple, System.Threading.CancellationToken cancellation = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task! static Solti.Utils.Proxy.Internals.Generator.GetGeneratedType() -> System.Type! static Solti.Utils.Proxy.Internals.Generator.GetGeneratedTypeAsync(System.Threading.CancellationToken cancellation = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task! -static Solti.Utils.Proxy.Internals.Generator.Instance.get -> Solti.Utils.Proxy.Internals.Generator! -virtual Solti.Utils.Proxy.InterfaceInterceptor.Invoke(Solti.Utils.Proxy.InvocationContext! context) -> object? \ No newline at end of file +static Solti.Utils.Proxy.Internals.Generator.Instance.get -> Solti.Utils.Proxy.Internals.Generator! \ No newline at end of file diff --git a/SRC/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt b/SRC/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt index e69de29b..0ea77bf7 100644 --- a/SRC/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt +++ b/SRC/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt @@ -0,0 +1,5 @@ +Solti.Utils.Proxy.InterfaceInterceptor +Solti.Utils.Proxy.InterfaceInterceptor.InterfaceInterceptor(TTarget? target) -> void +Solti.Utils.Proxy.InterfaceInterceptor.Proxy.set -> void +Solti.Utils.Proxy.InterfaceInterceptor.Target.get -> TTarget? +virtual Solti.Utils.Proxy.InterfaceInterceptor.Invoke(Solti.Utils.Proxy.InvocationContext! context) -> object? \ No newline at end of file diff --git a/SRC/PublicAPI/netstandard2.1/PublicAPI.Shipped.txt b/SRC/PublicAPI/netstandard2.1/PublicAPI.Shipped.txt index 33066037..3e4f6f52 100644 --- a/SRC/PublicAPI/netstandard2.1/PublicAPI.Shipped.txt +++ b/SRC/PublicAPI/netstandard2.1/PublicAPI.Shipped.txt @@ -17,8 +17,6 @@ Solti.Utils.Proxy.Generators.ProxyGenerator Solti.Utils.Proxy.Generators.ProxyGenerator.ProxyGenerator() -> void Solti.Utils.Proxy.InterfaceInterceptor Solti.Utils.Proxy.InterfaceInterceptor.InterfaceInterceptor(TInterface? target) -> void -Solti.Utils.Proxy.InterfaceInterceptor.Proxy.set -> void -Solti.Utils.Proxy.InterfaceInterceptor.Target.get -> TInterface? Solti.Utils.Proxy.Internals.DuckBase Solti.Utils.Proxy.Internals.DuckBase.DuckBase(T target) -> void Solti.Utils.Proxy.Internals.DuckBase.Target.get -> T @@ -49,5 +47,4 @@ static Solti.Utils.Proxy.Internals.Generator.Activate(S static Solti.Utils.Proxy.Internals.Generator.ActivateAsync(System.Runtime.CompilerServices.ITuple? tuple, System.Threading.CancellationToken cancellation = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task! static Solti.Utils.Proxy.Internals.Generator.GetGeneratedType() -> System.Type! static Solti.Utils.Proxy.Internals.Generator.GetGeneratedTypeAsync(System.Threading.CancellationToken cancellation = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task! -static Solti.Utils.Proxy.Internals.Generator.Instance.get -> Solti.Utils.Proxy.Internals.Generator! -virtual Solti.Utils.Proxy.InterfaceInterceptor.Invoke(Solti.Utils.Proxy.InvocationContext! context) -> object? \ No newline at end of file +static Solti.Utils.Proxy.Internals.Generator.Instance.get -> Solti.Utils.Proxy.Internals.Generator! \ No newline at end of file diff --git a/SRC/PublicAPI/netstandard2.1/PublicAPI.Unshipped.txt b/SRC/PublicAPI/netstandard2.1/PublicAPI.Unshipped.txt index e69de29b..0ea77bf7 100644 --- a/SRC/PublicAPI/netstandard2.1/PublicAPI.Unshipped.txt +++ b/SRC/PublicAPI/netstandard2.1/PublicAPI.Unshipped.txt @@ -0,0 +1,5 @@ +Solti.Utils.Proxy.InterfaceInterceptor +Solti.Utils.Proxy.InterfaceInterceptor.InterfaceInterceptor(TTarget? target) -> void +Solti.Utils.Proxy.InterfaceInterceptor.Proxy.set -> void +Solti.Utils.Proxy.InterfaceInterceptor.Target.get -> TTarget? +virtual Solti.Utils.Proxy.InterfaceInterceptor.Invoke(Solti.Utils.Proxy.InvocationContext! context) -> object? \ No newline at end of file