2019-06-12 21:56:55 +02:00
using System ;
using System.Linq ;
using System.Runtime.CompilerServices ;
namespace Capnp.Rpc
{
/// <summary>
/// Provides functionality to construct Proxy and Skeleton instances from capability interfaces and objects implementing capability interfaces.
/// A capability interface is any .NET interface which is annotated with <see cref="ProxyAttribute"/> and <see cref="SkeletonAttribute"/>.
/// There are some intricacies to consider that you usually don't need to care about, since all that stuff will be generated.
/// </summary>
public static class CapabilityReflection
{
interface IBrokenFactory
{
System . Exception Exception { get ; }
}
abstract class ProxyFactory
{
public abstract Proxy NewProxy ( ) ;
}
class ProxyFactory < T > : ProxyFactory where T : Proxy , new ( )
{
public override Proxy NewProxy ( ) = > new T ( ) ;
}
class BrokenProxyFactory : ProxyFactory , IBrokenFactory
{
readonly System . Exception _exception ;
public BrokenProxyFactory ( System . Exception exception )
{
_exception = exception ;
}
public System . Exception Exception = > _exception ;
public override Proxy NewProxy ( )
{
throw _exception ;
}
}
abstract class SkeletonFactory
{
public abstract Skeleton NewSkeleton ( ) ;
}
class SkeletonFactory < T > : SkeletonFactory where T : Skeleton , new ( )
{
public override Skeleton NewSkeleton ( ) = > new T ( ) ;
}
class BrokenSkeletonFactory : SkeletonFactory , IBrokenFactory
{
System . Exception _exception ;
public BrokenSkeletonFactory ( System . Exception exception )
{
_exception = exception ;
}
public System . Exception Exception = > _exception ;
public override Skeleton NewSkeleton ( )
{
throw _exception ;
}
}
class PolySkeletonFactory : SkeletonFactory
{
readonly SkeletonFactory [ ] _monoFactories ;
public PolySkeletonFactory ( SkeletonFactory [ ] monoFactories )
{
_monoFactories = monoFactories ;
}
public override Skeleton NewSkeleton ( )
{
var poly = new PolySkeleton ( ) ;
foreach ( var fac in _monoFactories )
{
poly . AddInterface ( fac . NewSkeleton ( ) ) ;
}
return poly ;
}
}
static ConditionalWeakTable < Type , ProxyFactory > _proxyMap =
new ConditionalWeakTable < Type , ProxyFactory > ( ) ;
static ConditionalWeakTable < Type , SkeletonFactory > _skeletonMap =
new ConditionalWeakTable < Type , SkeletonFactory > ( ) ;
static CapabilityReflection ( )
{
_proxyMap . Add ( typeof ( BareProxy ) , new ProxyFactory < BareProxy > ( ) ) ;
2019-10-25 19:29:44 +02:00
_proxyMap . Add ( typeof ( object ) , new ProxyFactory < BareProxy > ( ) ) ;
2019-06-12 21:56:55 +02:00
}
static SkeletonFactory CreateMonoSkeletonFactory ( SkeletonAttribute attr , Type [ ] genericArguments )
{
var skeletonClass = attr . SkeletonClass ;
if ( genericArguments . Length > 0 )
skeletonClass = skeletonClass . MakeGenericType ( genericArguments ) ;
return ( SkeletonFactory ) Activator . CreateInstance (
typeof ( SkeletonFactory < > )
2020-01-11 17:21:31 +01:00
. MakeGenericType ( skeletonClass ) ) ! ;
2019-06-12 21:56:55 +02:00
}
static SkeletonFactory GetSkeletonFactory ( Type type )
{
return _skeletonMap . GetValue ( type , _ = >
{
try
{
var attrs = ( from iface in _ . GetInterfaces ( )
2019-10-19 13:52:16 +02:00
let generics = iface . GetGenericArguments ( )
from attr in iface . GetCustomAttributes ( typeof ( SkeletonAttribute ) , false )
select ( Attr : ( SkeletonAttribute ) attr , Generics : generics ) ) . ToArray ( ) ;
2019-06-12 21:56:55 +02:00
if ( attrs . Length = = 0 )
throw new InvalidCapabilityInterfaceException ( "No 'Skeleton' attribute defined, don't know how to create the skeleton" ) ;
if ( attrs . Length = = 1 )
{
2019-10-19 13:52:16 +02:00
return CreateMonoSkeletonFactory ( attrs [ 0 ] . Attr , attrs [ 0 ] . Generics ) ;
2019-06-12 21:56:55 +02:00
}
else
{
2019-10-19 13:52:16 +02:00
var monoFactories = attrs . Select ( a = > CreateMonoSkeletonFactory ( a . Attr , a . Generics ) ) . ToArray ( ) ;
2019-06-12 21:56:55 +02:00
return new PolySkeletonFactory ( monoFactories ) ;
}
}
catch ( System . Exception exception )
{
return new BrokenSkeletonFactory ( exception ) ;
}
} ) ;
}
/// <summary>
/// Creates a Skeleton for a given interface implementation.
/// </summary>
/// <param name="obj">Interface implementation. Must implement at least one interface which is annotated with a <see cref="SkeletonAttribute"/>.</param>
/// <returns>The Skeleton</returns>
/// <exception cref="ArgumentNullException"><paramref name="obj"/> is null.</exception>
/// <exception cref="InvalidCapabilityInterfaceException">No <see cref="SkeletonAttribute"/> found on implemented interface(s).</exception>
/// <exception cref="InvalidOperationException">Mismatch between generic type arguments (if capability interface is generic).</exception>
/// <exception cref="ArgumentException">Mismatch between generic type arguments (if capability interface is generic).</exception>
/// <exception cref="System.Reflection.TargetInvocationException">Problem with instatiating the Skeleton (constructor threw exception).</exception>
/// <exception cref="MemberAccessException">Caller does not have permission to invoke the Skeleton constructor.</exception>
/// <exception cref="TypeLoadException">Problem with building the Skeleton type, or problem with loading some dependent class.</exception>
public static Skeleton CreateSkeleton ( object obj )
{
if ( obj = = null )
throw new ArgumentNullException ( nameof ( obj ) ) ;
var factory = GetSkeletonFactory ( obj . GetType ( ) ) ;
var skeleton = factory . NewSkeleton ( ) ;
skeleton . Bind ( obj ) ;
return skeleton ;
}
static ProxyFactory GetProxyFactory ( Type type )
{
return _proxyMap . GetValue ( type , _ = >
{
try
{
var attrs = type
2019-10-19 13:52:16 +02:00
. GetCustomAttributes ( typeof ( ProxyAttribute ) , false )
2019-06-12 21:56:55 +02:00
. Cast < ProxyAttribute > ( )
. ToArray ( ) ;
if ( attrs . Length = = 0 )
throw new InvalidCapabilityInterfaceException ( "No 'Proxy' attribute defined, don't know how to create the proxy" ) ;
if ( attrs . Length = = 1 )
{
Type proxyClass = attrs [ 0 ] . ProxyClass ;
Type [ ] genericArguments = type . GetGenericArguments ( ) ;
if ( genericArguments . Length > 0 )
2019-10-25 19:29:44 +02:00
proxyClass = proxyClass . MakeGenericType ( genericArguments ) ;
2019-06-12 21:56:55 +02:00
return ( ProxyFactory ) Activator . CreateInstance (
typeof ( ProxyFactory < > )
2020-01-11 17:21:31 +01:00
. MakeGenericType ( proxyClass ) ) ! ;
2019-06-12 21:56:55 +02:00
}
else
{
throw new InvalidCapabilityInterfaceException ( "Multiple 'Proxy' attributes defined, don't know which one to take" ) ;
}
}
catch ( System . Exception exception )
{
return new BrokenProxyFactory ( exception ) ;
}
} ) ;
}
/// <summary>
/// Validates that a given type qualifies as cpapbility interface, throws <see cref="InvalidCapabilityInterfaceException"/> on failure.
/// </summary>
/// <param name="interfaceType">type to check</param>
/// <exception cref="ArgumentNullException"><paramref name="interfaceType"/> is null.</exception>
/// <exception cref="InvalidCapabilityInterfaceException">Given typ did not qualify as capability interface.
/// Message and probably InnterException give more details.</exception>
public static void ValidateCapabilityInterface ( Type interfaceType )
{
if ( interfaceType = = null )
{
throw new ArgumentNullException ( nameof ( interfaceType ) ) ;
}
var proxyFactory = GetProxyFactory ( interfaceType ) ;
if ( proxyFactory is IBrokenFactory brokenFactory )
{
throw new InvalidCapabilityInterfaceException (
"Given type did not qualify as capability interface, see inner exception." ,
brokenFactory . Exception ) ;
}
}
/// <summary>
/// Checkes whether a given type qualifies as cpapbility interface./> on failure.
/// </summary>
/// <param name="interfaceType">type to check</param>
/// <returns>true when <paramref name="interfaceType"/> is a capability interface</returns>
/// <exception cref="ArgumentNullException"><paramref name="interfaceType"/> is null.</exception>
public static bool IsValidCapabilityInterface ( Type interfaceType )
{
if ( interfaceType = = null )
{
throw new ArgumentNullException ( nameof ( interfaceType ) ) ;
}
var proxyFactory = GetProxyFactory ( interfaceType ) ;
return ! ( proxyFactory is IBrokenFactory ) ;
}
/// <summary>
/// Constructs a Proxy for given capability interface and wraps it around given low-level capability.
/// </summary>
/// <typeparam name="TInterface">Capability interface. Must be annotated with <see cref="ProxyAttribute"/>.</typeparam>
/// <param name="cap">low-level capability</param>
2019-07-12 21:48:01 +02:00
/// <param name="memberName">debugging aid</param>
/// <param name="sourceFilePath">debugging aid</param>
/// <param name="sourceLineNumber">debugging aid</param>
2019-06-12 21:56:55 +02:00
/// <returns>The Proxy instance which implements <typeparamref name="TInterface"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="cap"/> is null.</exception>
/// <exception cref="InvalidCapabilityInterfaceException"><typeparamref name="TInterface"/> did not qualify as capability interface.</exception>
/// <exception cref="InvalidOperationException">Mismatch between generic type arguments (if capability interface is generic).</exception>
/// <exception cref="ArgumentException">Mismatch between generic type arguments (if capability interface is generic).</exception>
/// <exception cref="System.Reflection.TargetInvocationException">Problem with instatiating the Proxy (constructor threw exception).</exception>
/// <exception cref="MemberAccessException">Caller does not have permission to invoke the Proxy constructor.</exception>
/// <exception cref="TypeLoadException">Problem with building the Proxy type, or problem with loading some dependent class.</exception>
2020-03-21 13:27:46 +01:00
public static Proxy CreateProxy < TInterface > ( ConsumedCapability ? cap )
2019-06-12 21:56:55 +02:00
{
var factory = GetProxyFactory ( typeof ( TInterface ) ) ;
var proxy = factory . NewProxy ( ) ;
proxy . Bind ( cap ) ;
return proxy ;
}
}
2020-01-11 17:56:12 +01:00
}