Skip to content

Commit

Permalink
introduce InterfaceInterceptor{TInterface, TTarget} class
Browse files Browse the repository at this point in the history
  • Loading branch information
Sholtee committed Nov 21, 2022
1 parent 46dff87 commit 8029964
Show file tree
Hide file tree
Showing 10 changed files with 68 additions and 24 deletions.
5 changes: 5 additions & 0 deletions SRC/Private/Extensions/ITypeInfoExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,11 @@ private static IEnumerable<ITypeInfo> IterateOn(this ITypeInfo src, Func<ITypeIn

public static IEnumerable<ITypeInfo> GetParentTypes(this ITypeInfo src) => new Stack<ITypeInfo>(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;
Expand Down
19 changes: 14 additions & 5 deletions SRC/Private/SyntaxFactories/ProxySyntaxFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ internal partial class ProxySyntaxFactory: ProxyUnitSyntaxFactory

public ITypeInfo InterceptorType { get; }

public ITypeInfo TargetType { get; }

public IMethodInfo Invoke { get; }

#if DEBUG
Expand Down Expand Up @@ -53,22 +55,29 @@ protected override IEnumerable<ClassDeclarationSyntax> 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?)
(
interceptorType.QualifiedName == baseInterceptorName
? 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
(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ namespace Solti.Utils.Proxy.Generators
/// Type generator for creating proxies that intercept interface method calls.
/// </summary>
/// <typeparam name="TInterface">The interface for which the proxy will be created.</typeparam>
/// <typeparam name="TInterceptor">An <see cref="InterfaceInterceptor{TInterface}"/> descendant that has at least one public constructor.</typeparam>
public sealed class ProxyGenerator<TInterface, TInterceptor> : Generator<TInterface, ProxyGenerator<TInterface, TInterceptor>> where TInterface : class where TInterceptor: InterfaceInterceptor<TInterface>
/// <typeparam name="TInterceptor">An <see cref="InterfaceInterceptor{TInterface, TTarget}"/> descendant that has at least one public constructor.</typeparam>
public sealed class ProxyGenerator<TInterface, TInterceptor> : Generator<TInterface, ProxyGenerator<TInterface, TInterceptor>>
where TInterface : class
where TInterceptor: InterfaceInterceptor<TInterface, TInterface>
{
/// <inheritdoc/>
protected override Generator GetConcreteGenerator() => new ProxyGenerator(typeof(TInterface), typeof(TInterceptor));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* InterfaceInterceptor.cs *
* InterfaceInterceptor{TInterface, TTarget}.cs *
* *
* Author: Denes Solti *
********************************************************************************/
Expand All @@ -14,30 +14,33 @@ namespace Solti.Utils.Proxy
/// Provides the mechanism for intercepting interface method calls.
/// </summary>
/// <typeparam name="TInterface">The interface to be intercepted.</typeparam>
public class InterfaceInterceptor<TInterface>: GeneratedClass, IHasTarget<TInterface?>, IProxyAccess<TInterface> where TInterface: class
/// <typeparam name="TTarget">The target interface implementation.</typeparam>
public class InterfaceInterceptor<TInterface, TTarget> : GeneratedClass, IHasTarget<TTarget?>, IProxyAccess<TInterface>
where TInterface : class
where TTarget : TInterface
{
/// <summary>
/// The target of this interceptor.
/// </summary>
public TInterface? Target { get; }
public TTarget? Target { get; }

/// <summary>
/// The most outer enclosing proxy.
/// </summary>
public TInterface Proxy
{
set
set
{
if (Target is IProxyAccess<TInterface> proxyAccess)
proxyAccess.Proxy = value ?? throw new ArgumentNullException(nameof(value));
}
}
}

/// <summary>
/// Creates a new <see cref="InterfaceInterceptor{TInterface}"/> instance against the given <paramref name="target"/>.
/// Creates a new <see cref="InterfaceInterceptor{TInterface, TTarget}"/> instance against the given <paramref name="target"/>.
/// </summary>
/// <param name="target">The target of this interceptor.</param>
public InterfaceInterceptor(TInterface? target) => Target = target;
public InterfaceInterceptor(TTarget? target) => Target = target;

/// <summary>
/// Called on proxy method invocation.
Expand All @@ -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);
}
}
}
21 changes: 21 additions & 0 deletions SRC/Public/InterfaceInterceptor{TInterface}r.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/********************************************************************************
* InterfaceInterceptor{TInterface}r.cs *
* *
* Author: Denes Solti *
********************************************************************************/

namespace Solti.Utils.Proxy
{
/// <summary>
/// Provides the mechanism for intercepting interface method calls.
/// </summary>
/// <typeparam name="TInterface">The interface to be intercepted.</typeparam>
public class InterfaceInterceptor<TInterface> : InterfaceInterceptor<TInterface, TInterface> where TInterface : class
{
/// <summary>
/// Creates a new <see cref="InterfaceInterceptor{TInterface}"/> instance against the given <paramref name="target"/>.
/// </summary>
/// <param name="target">The target of this interceptor.</param>
public InterfaceInterceptor(TInterface? target): base(target) { }
}
}
2 changes: 1 addition & 1 deletion SRC/Public/InvocationContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public InvocationContext(object?[] args, MethodContext methodContext): base(meth
/// <summary>
/// The arguments passed by the caller.
/// </summary>
/// <remarks>Before the <see cref="InterfaceInterceptor{TInterface}.Target"/> 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.</remarks>
/// <remarks>Before the <see cref="InterfaceInterceptor{TInterface, TTarget}.Target"/> 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.</remarks>
[SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "End user is allowed to modify the argument list.")]
public object?[] Args { get; }
}
Expand Down
5 changes: 1 addition & 4 deletions SRC/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ Solti.Utils.Proxy.Generators.ProxyGenerator<TInterface, TInterceptor>
Solti.Utils.Proxy.Generators.ProxyGenerator<TInterface, TInterceptor>.ProxyGenerator() -> void
Solti.Utils.Proxy.InterfaceInterceptor<TInterface>
Solti.Utils.Proxy.InterfaceInterceptor<TInterface>.InterfaceInterceptor(TInterface? target) -> void
Solti.Utils.Proxy.InterfaceInterceptor<TInterface>.Proxy.set -> void
Solti.Utils.Proxy.InterfaceInterceptor<TInterface>.Target.get -> TInterface?
Solti.Utils.Proxy.Internals.DuckBase<T>
Solti.Utils.Proxy.Internals.DuckBase<T>.DuckBase(T target) -> void
Solti.Utils.Proxy.Internals.DuckBase<T>.Target.get -> T
Expand Down Expand Up @@ -49,5 +47,4 @@ static Solti.Utils.Proxy.Internals.Generator<TInterface, TDescendant>.Activate(o
static Solti.Utils.Proxy.Internals.Generator<TInterface, TDescendant>.ActivateAsync(object? tuple, System.Threading.CancellationToken cancellation = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<TInterface>!
static Solti.Utils.Proxy.Internals.Generator<TInterface, TDescendant>.GetGeneratedType() -> System.Type!
static Solti.Utils.Proxy.Internals.Generator<TInterface, TDescendant>.GetGeneratedTypeAsync(System.Threading.CancellationToken cancellation = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Type!>!
static Solti.Utils.Proxy.Internals.Generator<TInterface, TDescendant>.Instance.get -> Solti.Utils.Proxy.Internals.Generator!
virtual Solti.Utils.Proxy.InterfaceInterceptor<TInterface>.Invoke(Solti.Utils.Proxy.InvocationContext! context) -> object?
static Solti.Utils.Proxy.Internals.Generator<TInterface, TDescendant>.Instance.get -> Solti.Utils.Proxy.Internals.Generator!
5 changes: 5 additions & 0 deletions SRC/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Solti.Utils.Proxy.InterfaceInterceptor<TInterface, TTarget>
Solti.Utils.Proxy.InterfaceInterceptor<TInterface, TTarget>.InterfaceInterceptor(TTarget? target) -> void
Solti.Utils.Proxy.InterfaceInterceptor<TInterface, TTarget>.Proxy.set -> void
Solti.Utils.Proxy.InterfaceInterceptor<TInterface, TTarget>.Target.get -> TTarget?
virtual Solti.Utils.Proxy.InterfaceInterceptor<TInterface, TTarget>.Invoke(Solti.Utils.Proxy.InvocationContext! context) -> object?
5 changes: 1 addition & 4 deletions SRC/PublicAPI/netstandard2.1/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ Solti.Utils.Proxy.Generators.ProxyGenerator<TInterface, TInterceptor>
Solti.Utils.Proxy.Generators.ProxyGenerator<TInterface, TInterceptor>.ProxyGenerator() -> void
Solti.Utils.Proxy.InterfaceInterceptor<TInterface>
Solti.Utils.Proxy.InterfaceInterceptor<TInterface>.InterfaceInterceptor(TInterface? target) -> void
Solti.Utils.Proxy.InterfaceInterceptor<TInterface>.Proxy.set -> void
Solti.Utils.Proxy.InterfaceInterceptor<TInterface>.Target.get -> TInterface?
Solti.Utils.Proxy.Internals.DuckBase<T>
Solti.Utils.Proxy.Internals.DuckBase<T>.DuckBase(T target) -> void
Solti.Utils.Proxy.Internals.DuckBase<T>.Target.get -> T
Expand Down Expand Up @@ -49,5 +47,4 @@ static Solti.Utils.Proxy.Internals.Generator<TInterface, TDescendant>.Activate(S
static Solti.Utils.Proxy.Internals.Generator<TInterface, TDescendant>.ActivateAsync(System.Runtime.CompilerServices.ITuple? tuple, System.Threading.CancellationToken cancellation = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<TInterface>!
static Solti.Utils.Proxy.Internals.Generator<TInterface, TDescendant>.GetGeneratedType() -> System.Type!
static Solti.Utils.Proxy.Internals.Generator<TInterface, TDescendant>.GetGeneratedTypeAsync(System.Threading.CancellationToken cancellation = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Type!>!
static Solti.Utils.Proxy.Internals.Generator<TInterface, TDescendant>.Instance.get -> Solti.Utils.Proxy.Internals.Generator!
virtual Solti.Utils.Proxy.InterfaceInterceptor<TInterface>.Invoke(Solti.Utils.Proxy.InvocationContext! context) -> object?
static Solti.Utils.Proxy.Internals.Generator<TInterface, TDescendant>.Instance.get -> Solti.Utils.Proxy.Internals.Generator!
5 changes: 5 additions & 0 deletions SRC/PublicAPI/netstandard2.1/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Solti.Utils.Proxy.InterfaceInterceptor<TInterface, TTarget>
Solti.Utils.Proxy.InterfaceInterceptor<TInterface, TTarget>.InterfaceInterceptor(TTarget? target) -> void
Solti.Utils.Proxy.InterfaceInterceptor<TInterface, TTarget>.Proxy.set -> void
Solti.Utils.Proxy.InterfaceInterceptor<TInterface, TTarget>.Target.get -> TTarget?
virtual Solti.Utils.Proxy.InterfaceInterceptor<TInterface, TTarget>.Invoke(Solti.Utils.Proxy.InvocationContext! context) -> object?

0 comments on commit 8029964

Please sign in to comment.