@ -13,378 +13,363 @@ using System.Reflection;
using System.Runtime.CompilerServices ;
using Google.Protobuf.Compatibility ;
namespace Google.Protobuf.Reflection
{
namespace Google.Protobuf.Reflection {
/// <summary>
/// The methods in this class are somewhat evil, and should not be tampered with lightly.
/// Basically they allow the creation of relatively weakly typed delegates from MethodInfos
/// which are more strongly typed. They do this by creating an appropriate strongly typed
/// delegate from the MethodInfo, and then calling that within an anonymous method.
/// Mind-bending stuff (at least to your humble narrator) but the resulting delegates are
/// very fast compared with calling Invoke later on.
/// </summary>
internal static class ReflectionUtil {
static ReflectionUtil ( ) {
ForceInitialize < string > ( ) ; // Handles all reference types
ForceInitialize < int > ( ) ;
ForceInitialize < long > ( ) ;
ForceInitialize < uint > ( ) ;
ForceInitialize < ulong > ( ) ;
ForceInitialize < float > ( ) ;
ForceInitialize < double > ( ) ;
ForceInitialize < bool > ( ) ;
ForceInitialize < int ? > ( ) ;
ForceInitialize < long ? > ( ) ;
ForceInitialize < uint ? > ( ) ;
ForceInitialize < ulong ? > ( ) ;
ForceInitialize < float ? > ( ) ;
ForceInitialize < double ? > ( ) ;
ForceInitialize < bool ? > ( ) ;
ForceInitialize < SampleEnum > ( ) ;
SampleEnumMethod ( ) ;
}
internal static void ForceInitialize < T > ( ) = > new ReflectionHelper < IMessage , T > ( ) ;
/// <summary>
/// The methods in this class are somewhat evil, and should not be tampered with lightly.
/// Basically they allow the creation of relatively weakly typed delegates from MethodInfos
/// which are more strongly typed. They do this by creating an appropriate strongly typed
/// delegate from the MethodInfo, and then calling that within an anonymous method.
/// Mind-bending stuff (at least to your humble narrator) but the resulting delegates are
/// very fast compared with calling Invoke later on.
/// Empty Type[] used when calling GetProperty to force property instead of indexer fetching.
/// </summary>
internal static class ReflectionUtil
{
static ReflectionUtil ( )
{
ForceInitialize < string > ( ) ; // Handles all reference types
ForceInitialize < int > ( ) ;
ForceInitialize < long > ( ) ;
ForceInitialize < uint > ( ) ;
ForceInitialize < ulong > ( ) ;
ForceInitialize < float > ( ) ;
ForceInitialize < double > ( ) ;
ForceInitialize < bool > ( ) ;
ForceInitialize < int? > ( ) ;
ForceInitialize < long? > ( ) ;
ForceInitialize < uint? > ( ) ;
ForceInitialize < ulong? > ( ) ;
ForceInitialize < float? > ( ) ;
ForceInitialize < double? > ( ) ;
ForceInitialize < bool? > ( ) ;
ForceInitialize < SampleEnum > ( ) ;
SampleEnumMethod ( ) ;
}
internal static readonly Type [ ] EmptyTypes = new Type [ 0 ] ;
/// <summary>
/// Creates a delegate which will cast the argument to the type that declares the method,
/// call the method on it, then convert the result to object.
/// </summary>
/// <param name="method">The method to create a delegate for, which must be declared in an
/// IMessage implementation.</param>
internal static Func < IMessage , object > CreateFuncIMessageObject ( MethodInfo method ) = >
GetReflectionHelper ( method . DeclaringType , method . ReturnType )
. CreateFuncIMessageObject ( method ) ;
/// <summary>
/// Creates a delegate which will cast the argument to the type that declares the method,
/// call the method on it, then convert the result to the specified type. The method is expected
/// to actually return an enum (because of where we're calling it - for oneof cases). Sometimes
/// that means we need some extra work to perform conversions.
/// </summary>
/// <param name="method">The method to create a delegate for, which must be declared in an
/// IMessage implementation.</param>
internal static Func < IMessage , int > CreateFuncIMessageInt32 ( MethodInfo method ) = >
GetReflectionHelper ( method . DeclaringType , method . ReturnType )
. CreateFuncIMessageInt32 ( method ) ;
/// <summary>
/// Creates a delegate which will execute the given method after casting the first argument to
/// the type that declares the method, and the second argument to the first parameter type of
/// the method.
/// </summary>
/// <param name="method">The method to create a delegate for, which must be declared in an
/// IMessage implementation.</param>
internal static Action < IMessage , object > CreateActionIMessageObject ( MethodInfo method ) = >
GetReflectionHelper ( method . DeclaringType , method . GetParameters ( ) [ 0 ] . ParameterType )
. CreateActionIMessageObject ( method ) ;
internal static void ForceInitialize < T > ( ) = > new ReflectionHelper < IMessage , T > ( ) ;
/// <summary>
/// Empty Type[] used when calling GetProperty to force property instead of indexer fetching.
/// </summary>
internal static readonly Type [ ] EmptyTypes = new Type [ 0 ] ;
/// <summary>
/// Creates a delegate which will cast the argument to the type that declares the method,
/// call the method on it, then convert the result to object.
/// </summary>
/// <param name="method">The method to create a delegate for, which must be declared in an IMessage
/// implementation.</param>
internal static Func < IMessage , object > CreateFuncIMessageObject ( MethodInfo method ) = >
GetReflectionHelper ( method . DeclaringType , method . ReturnType ) . CreateFuncIMessageObject ( method ) ;
/// <summary>
/// Creates a delegate which will cast the argument to the type that declares the method,
/// call the method on it, then convert the result to the specified type. The method is expected
/// to actually return an enum (because of where we're calling it - for oneof cases). Sometimes that
/// means we need some extra work to perform conversions.
/// </summary>
/// <param name="method">The method to create a delegate for, which must be declared in an IMessage
/// implementation.</param>
internal static Func < IMessage , int > CreateFuncIMessageInt32 ( MethodInfo method ) = >
GetReflectionHelper ( method . DeclaringType , method . ReturnType ) . CreateFuncIMessageInt32 ( method ) ;
/// <summary>
/// Creates a delegate which will execute the given method after casting the first argument to
/// the type that declares the method, and the second argument to the first parameter type of the method.
/// </summary>
/// <param name="method">The method to create a delegate for, which must be declared in an IMessage
/// implementation.</param>
internal static Action < IMessage , object > CreateActionIMessageObject ( MethodInfo method ) = >
GetReflectionHelper ( method . DeclaringType , method . GetParameters ( ) [ 0 ] . ParameterType ) . CreateActionIMessageObject ( method ) ;
/// <summary>
/// Creates a delegate which will execute the given method after casting the first argument to
/// type that declares the method.
/// </summary>
/// <param name="method">The method to create a delegate for, which must be declared in an IMessage
/// implementation.</param>
internal static Action < IMessage > CreateActionIMessage ( MethodInfo method ) = >
GetReflectionHelper ( method . DeclaringType , typeof ( object ) ) . CreateActionIMessage ( method ) ;
internal static Func < IMessage , bool > CreateFuncIMessageBool ( MethodInfo method ) = >
GetReflectionHelper ( method . DeclaringType , method . ReturnType ) . CreateFuncIMessageBool ( method ) ;
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Type parameter members are preserved with DynamicallyAccessedMembers on GeneratedClrTypeInfo.ctor clrType parameter.")]
[UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "Type definition is explicitly specified and type argument is always a message type.")]
internal static Func < IMessage , bool > CreateIsInitializedCaller ( [ DynamicallyAccessedMembers ( GeneratedClrTypeInfo . MessageAccessibility ) ] Type msg ) = >
( ( IExtensionSetReflector ) Activator . CreateInstance ( typeof ( ExtensionSetReflector < > ) . MakeGenericType ( msg ) ) ) . CreateIsInitializedCaller ( ) ;
/// <summary>
/// Creates a delegate which will execute the given method after casting the first argument to
/// the type that declares the method, and the second argument to the first parameter type of the method.
/// </summary>
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Type parameter members are preserved with DynamicallyAccessedMembers on GeneratedClrTypeInfo.ctor clrType parameter.")]
internal static IExtensionReflectionHelper CreateExtensionHelper ( Extension extension )
{
/// <summary>
/// Creates a delegate which will execute the given method after casting the first argument to
/// type that declares the method.
/// </summary>
/// <param name="method">The method to create a delegate for, which must be declared in an
/// IMessage implementation.</param>
internal static Action < IMessage > CreateActionIMessage ( MethodInfo method ) = >
GetReflectionHelper ( method . DeclaringType , typeof ( object ) ) . CreateActionIMessage ( method ) ;
internal static Func < IMessage , bool > CreateFuncIMessageBool ( MethodInfo method ) = >
GetReflectionHelper ( method . DeclaringType , method . ReturnType ) . CreateFuncIMessageBool ( method ) ;
[ UnconditionalSuppressMessage (
"Trimming" , "IL2026" ,
Justification =
"Type parameter members are preserved with DynamicallyAccessedMembers on GeneratedClrTypeInfo.ctor clrType parameter." ) ]
[ UnconditionalSuppressMessage (
"AotAnalysis" , "IL3050:RequiresDynamicCode" ,
Justification =
"Type definition is explicitly specified and type argument is always a message type." ) ]
internal static Func < IMessage , bool > CreateIsInitializedCaller ( [
DynamicallyAccessedMembers ( GeneratedClrTypeInfo . MessageAccessibility )
] Type msg ) = > ( ( IExtensionSetReflector ) Activator
. CreateInstance ( typeof ( ExtensionSetReflector < > ) . MakeGenericType ( msg ) ) )
. CreateIsInitializedCaller ( ) ;
/// <summary>
/// Creates a delegate which will execute the given method after casting the first argument to
/// the type that declares the method, and the second argument to the first parameter type of
/// the method.
/// </summary>
[ UnconditionalSuppressMessage (
"Trimming" , "IL2026" ,
Justification =
"Type parameter members are preserved with DynamicallyAccessedMembers on GeneratedClrTypeInfo.ctor clrType parameter." ) ]
[ UnconditionalSuppressMessage ( "AOT" , "IL3050" ,
Justification = "Dynamic code won't call Type.MakeGenericType." ) ]
internal static IExtensionReflectionHelper CreateExtensionHelper ( Extension extension ) {
#if NET5_0_OR_GREATER
if ( ! RuntimeFeature . IsDynamicCodeSupported )
{
// Using extensions with reflection is not supported with AOT.
// This helper is created when descriptors are populated. Delay throwing error until an app
// uses IFieldAccessor with an extension field.
return new AotExtensionReflectionHelper ( ) ;
}
if ( ! RuntimeFeature . IsDynamicCodeSupported ) {
// Using extensions with reflection is not supported with AOT.
// This helper is created when descriptors are populated. Delay throwing error until an app
// uses IFieldAccessor with an extension field.
return new AotExtensionReflectionHelper ( ) ;
}
# endif
var t1 = extension . TargetType ;
var t3 = extension . GetType ( ) . GenericTypeArguments [ 1 ] ;
return ( IExtensionReflectionHelper ) Activator . CreateInstance ( typeof ( ExtensionReflectionHelper < , > ) . MakeGenericType ( t1 , t3 ) , extension ) ;
}
var t1 = extension . TargetType ;
var t3 = extension . GetType ( ) . GenericTypeArguments [ 1 ] ;
return ( IExtensionReflectionHelper ) Activator . CreateInstance (
typeof ( ExtensionReflectionHelper < , > ) . MakeGenericType ( t1 , t3 ) , extension ) ;
}
/// <summary>
/// Creates a reflection helper for the given type arguments. Currently these are created on demand
/// rather than cached; this will be "busy" when initially loading a message's descriptor, but after that
/// they can be garbage collected. We could cache them by type if that proves to be important, but creating
/// an object is pretty cheap.
/// </summary>
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Type parameter members are preserved with DynamicallyAccessedMembers on GeneratedClrTypeInfo.ctor clrType parameter.")]
private static IReflectionHelper GetReflectionHelper ( Type t1 , Type t2 )
{
/// <summary>
/// Creates a reflection helper for the given type arguments. Currently these are created on
/// demand rather than cached; this will be "busy" when initially loading a message's
/// descriptor, but after that they can be garbage collected. We could cache them by type if
/// that proves to be important, but creating an object is pretty cheap.
/// </summary>
[ UnconditionalSuppressMessage (
"Trimming" , "IL2026" ,
Justification =
"Type parameter members are preserved with DynamicallyAccessedMembers on GeneratedClrTypeInfo.ctor clrType parameter." ) ]
[ UnconditionalSuppressMessage ( "AOT" , "IL3050" ,
Justification = "Dynamic code won't call Type.MakeGenericType." ) ]
private static IReflectionHelper GetReflectionHelper ( Type t1 , Type t2 ) {
#if NET5_0_OR_GREATER
if ( ! RuntimeFeature . IsDynamicCodeSupported )
{
return new AotReflectionHelper ( ) ;
}
if ( ! RuntimeFeature . IsDynamicCodeSupported ) {
return new AotReflectionHelper ( ) ;
}
# endif
return ( IReflectionHelper ) Activator . CreateInstance ( typeof ( ReflectionHelper < , > ) . MakeGenericType ( t1 , t2 ) ) ;
}
return ( IReflectionHelper ) Activator . CreateInstance (
typeof ( ReflectionHelper < , > ) . MakeGenericType ( t1 , t2 ) ) ;
}
// Non-generic interface allowing us to use an instance of ReflectionHelper<T1, T2> without statically
// knowing the types involved.
private interface IReflectionHelper
{
Func < IMessage , int > CreateFuncIMessageInt32 ( MethodInfo method ) ;
Action < IMessage > CreateActionIMessage ( MethodInfo method ) ;
Func < IMessage , object > CreateFuncIMessageObject ( MethodInfo method ) ;
Action < IMessage , object > CreateActionIMessageObject ( MethodInfo method ) ;
Func < IMessage , bool > CreateFuncIMessageBool ( MethodInfo method ) ;
}
// Non-generic interface allowing us to use an instance of ReflectionHelper<T1, T2> without
// statically knowing the types involved.
private interface IReflectionHelper {
Func < IMessage , int > CreateFuncIMessageInt32 ( MethodInfo method ) ;
Action < IMessage > CreateActionIMessage ( MethodInfo method ) ;
Func < IMessage , object > CreateFuncIMessageObject ( MethodInfo method ) ;
Action < IMessage , object > CreateActionIMessageObject ( MethodInfo method ) ;
Func < IMessage , bool > CreateFuncIMessageBool ( MethodInfo method ) ;
}
internal interface IExtensionReflectionHelper {
object GetExtension ( IMessage message ) ;
void SetExtension ( IMessage message , object value ) ;
bool HasExtension ( IMessage message ) ;
void ClearExtension ( IMessage message ) ;
}
internal interface IExtensionReflectionHelper
{
object GetExtension ( IMessage message ) ;
void SetExtension ( IMessage message , object value ) ;
bool HasExtension ( IMessage message ) ;
void ClearExtension ( IMessage message ) ;
private interface IExtensionSetReflector {
Func < IMessage , bool > CreateIsInitializedCaller ( ) ;
}
private sealed class ReflectionHelper < T1 , T2 > : IReflectionHelper {
public Func < IMessage , int > CreateFuncIMessageInt32 ( MethodInfo method ) {
// On pleasant runtimes, we can create a Func<int> from a method returning
// an enum based on an int. That's the fast path.
if ( CanConvertEnumFuncToInt32Func ) {
var del = ( Func < T1 , int > ) method . CreateDelegate ( typeof ( Func < T1 , int > ) ) ;
return message = > del ( ( T1 ) message ) ;
} else {
// On some runtimes (e.g. old Mono) the return type has to be exactly correct,
// so we go via boxing. Reflection is already fairly inefficient, and this is
// only used for one-of case checking, fortunately.
var del = ( Func < T1 , T2 > ) method . CreateDelegate ( typeof ( Func < T1 , T2 > ) ) ;
return message = > ( int ) ( object ) del ( ( T1 ) message ) ;
}
}
public Action < IMessage > CreateActionIMessage ( MethodInfo method ) {
var del = ( Action < T1 > ) method . CreateDelegate ( typeof ( Action < T1 > ) ) ;
return message = > del ( ( T1 ) message ) ;
}
public Func < IMessage , object > CreateFuncIMessageObject ( MethodInfo method ) {
var del = ( Func < T1 , T2 > ) method . CreateDelegate ( typeof ( Func < T1 , T2 > ) ) ;
return message = > del ( ( T1 ) message ) ;
}
public Action < IMessage , object > CreateActionIMessageObject ( MethodInfo method ) {
var del = ( Action < T1 , T2 > ) method . CreateDelegate ( typeof ( Action < T1 , T2 > ) ) ;
return ( message , arg ) = > del ( ( T1 ) message , ( T2 ) arg ) ;
}
public Func < IMessage , bool > CreateFuncIMessageBool ( MethodInfo method ) {
var del = ( Func < T1 , bool > ) method . CreateDelegate ( typeof ( Func < T1 , bool > ) ) ;
return message = > del ( ( T1 ) message ) ;
}
}
private sealed class ExtensionReflectionHelper < T1 , T3 > : IExtensionReflectionHelper
where T1 : IExtendableMessage < T1 > {
private readonly Extension extension ;
private interface IExtensionSetReflector
{
Func < IMessage , bool > CreateIsInitializedCaller ( ) ;
public ExtensionReflectionHelper ( Extension extension ) {
this . extension = extension ;
}
public object GetExtension ( IMessage message ) {
if ( message is not T1 extensionMessage ) {
throw new InvalidCastException (
"Cannot access extension on message that isn't IExtensionMessage" ) ;
}
private sealed class ReflectionHelper < T1 , T2 > : IReflectionHelper
{
public Func < IMessage , int > CreateFuncIMessageInt32 ( MethodInfo method )
{
// On pleasant runtimes, we can create a Func<int> from a method returning
// an enum based on an int. That's the fast path.
if ( CanConvertEnumFuncToInt32Func )
{
var del = ( Func < T1 , int > ) method . CreateDelegate ( typeof ( Func < T1 , int > ) ) ;
return message = > del ( ( T1 ) message ) ;
}
else
{
// On some runtimes (e.g. old Mono) the return type has to be exactly correct,
// so we go via boxing. Reflection is already fairly inefficient, and this is
// only used for one-of case checking, fortunately.
var del = ( Func < T1 , T2 > ) method . CreateDelegate ( typeof ( Func < T1 , T2 > ) ) ;
return message = > ( int ) ( object ) del ( ( T1 ) message ) ;
}
}
public Action < IMessage > CreateActionIMessage ( MethodInfo method )
{
var del = ( Action < T1 > ) method . CreateDelegate ( typeof ( Action < T1 > ) ) ;
return message = > del ( ( T1 ) message ) ;
}
public Func < IMessage , object > CreateFuncIMessageObject ( MethodInfo method )
{
var del = ( Func < T1 , T2 > ) method . CreateDelegate ( typeof ( Func < T1 , T2 > ) ) ;
return message = > del ( ( T1 ) message ) ;
}
public Action < IMessage , object > CreateActionIMessageObject ( MethodInfo method )
{
var del = ( Action < T1 , T2 > ) method . CreateDelegate ( typeof ( Action < T1 , T2 > ) ) ;
return ( message , arg ) = > del ( ( T1 ) message , ( T2 ) arg ) ;
}
public Func < IMessage , bool > CreateFuncIMessageBool ( MethodInfo method )
{
var del = ( Func < T1 , bool > ) method . CreateDelegate ( typeof ( Func < T1 , bool > ) ) ;
return message = > del ( ( T1 ) message ) ;
}
if ( extension is Extension < T1 , T3 > ext13 ) {
return extensionMessage . GetExtension ( ext13 ) ;
} else if ( extension is RepeatedExtension < T1 , T3 > repeatedExt13 ) {
return extensionMessage . GetOrInitializeExtension ( repeatedExt13 ) ;
} else {
throw new InvalidCastException (
"The provided extension is not a valid extension identifier type" ) ;
}
}
private sealed class ExtensionReflectionHelper < T1 , T3 > : IExtensionReflectionHelper
where T1 : IExtendableMessage < T1 >
{
private readonly Extension extension ;
public ExtensionReflectionHelper ( Extension extension )
{
this . extension = extension ;
}
public object GetExtension ( IMessage message )
{
if ( message is not T1 extensionMessage )
{
throw new InvalidCastException ( "Cannot access extension on message that isn't IExtensionMessage" ) ;
}
if ( extension is Extension < T1 , T3 > ext13 )
{
return extensionMessage . GetExtension ( ext13 ) ;
}
else if ( extension is RepeatedExtension < T1 , T3 > repeatedExt13 )
{
return extensionMessage . GetOrInitializeExtension ( repeatedExt13 ) ;
}
else
{
throw new InvalidCastException ( "The provided extension is not a valid extension identifier type" ) ;
}
}
public bool HasExtension ( IMessage message )
{
if ( message is not T1 extensionMessage )
{
throw new InvalidCastException ( "Cannot access extension on message that isn't IExtensionMessage" ) ;
}
if ( extension is Extension < T1 , T3 > ext13 )
{
return extensionMessage . HasExtension ( ext13 ) ;
}
else if ( extension is RepeatedExtension < T1 , T3 > )
{
throw new InvalidOperationException ( "HasValue is not implemented for repeated extensions" ) ;
}
else
{
throw new InvalidCastException ( "The provided extension is not a valid extension identifier type" ) ;
}
}
public void SetExtension ( IMessage message , object value )
{
if ( message is not T1 extensionMessage )
{
throw new InvalidCastException ( "Cannot access extension on message that isn't IExtensionMessage" ) ;
}
if ( extension is Extension < T1 , T3 > ext13 )
{
extensionMessage . SetExtension ( ext13 , ( T3 ) value ) ;
}
else if ( extension is RepeatedExtension < T1 , T3 > )
{
throw new InvalidOperationException ( "SetValue is not implemented for repeated extensions" ) ;
}
else
{
throw new InvalidCastException ( "The provided extension is not a valid extension identifier type" ) ;
}
}
public void ClearExtension ( IMessage message )
{
if ( message is not T1 extensionMessage )
{
throw new InvalidCastException ( "Cannot access extension on message that isn't IExtensionMessage" ) ;
}
if ( extension is Extension < T1 , T3 > ext13 )
{
extensionMessage . ClearExtension ( ext13 ) ;
}
else if ( extension is RepeatedExtension < T1 , T3 > repeatedExt13 )
{
extensionMessage . GetExtension ( repeatedExt13 ) . Clear ( ) ;
}
else
{
throw new InvalidCastException ( "The provided extension is not a valid extension identifier type" ) ;
}
}
public bool HasExtension ( IMessage message ) {
if ( message is not T1 extensionMessage ) {
throw new InvalidCastException (
"Cannot access extension on message that isn't IExtensionMessage" ) ;
}
#if NET5_0_OR_GREATER
/// <summary>
/// This helper is compatible with .NET Native AOT.
/// MakeGenericType doesn't work when a type argument is a value type in AOT.
/// MethodInfo.Invoke is used instead of compiled expressions because it's faster in AOT.
/// </summary>
private sealed class AotReflectionHelper : IReflectionHelper
{
private static readonly object [ ] EmptyObjectArray = new object [ 0 ] ;
public Action < IMessage > CreateActionIMessage ( MethodInfo method ) = > message = > method . Invoke ( message , EmptyObjectArray ) ;
public Action < IMessage , object > CreateActionIMessageObject ( MethodInfo method ) = > ( message , arg ) = > method . Invoke ( message , new object [ ] { arg } ) ;
public Func < IMessage , bool > CreateFuncIMessageBool ( MethodInfo method ) = > message = > ( bool ) method . Invoke ( message , EmptyObjectArray ) ;
public Func < IMessage , int > CreateFuncIMessageInt32 ( MethodInfo method ) = > message = > ( int ) method . Invoke ( message , EmptyObjectArray ) ;
public Func < IMessage , object > CreateFuncIMessageObject ( MethodInfo method ) = > message = > method . Invoke ( message , EmptyObjectArray ) ;
if ( extension is Extension < T1 , T3 > ext13 ) {
return extensionMessage . HasExtension ( ext13 ) ;
} else if ( extension is RepeatedExtension < T1 , T3 > ) {
throw new InvalidOperationException (
"HasValue is not implemented for repeated extensions" ) ;
} else {
throw new InvalidCastException (
"The provided extension is not a valid extension identifier type" ) ;
}
}
/// <summary>
/// Reflection with extensions isn't supported because IExtendableMessage members are used to get values.
/// Can't use reflection to invoke those methods because they have a generic argument.
/// MakeGenericMethod can't be used because it will break whenever the extension type is a value type.
/// This could be made to work if there were non-generic methods available for getting and setting extension values.
/// </summary>
private sealed class AotExtensionReflectionHelper : IExtensionReflectionHelper
{
private const string Message = "Extensions reflection is not supported with AOT." ;
public object GetExtension ( IMessage message ) = > throw new NotSupportedException ( Message ) ;
public bool HasExtension ( IMessage message ) = > throw new NotSupportedException ( Message ) ;
public void SetExtension ( IMessage message , object value ) = > throw new NotSupportedException ( Message ) ;
public void ClearExtension ( IMessage message ) = > throw new NotSupportedException ( Message ) ;
public void SetExtension ( IMessage message , object value ) {
if ( message is not T1 extensionMessage ) {
throw new InvalidCastException (
"Cannot access extension on message that isn't IExtensionMessage" ) ;
}
# endif
private sealed class ExtensionSetReflector <
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)]
T1 > : IExtensionSetReflector where T1 : IExtendableMessage < T1 >
{
public Func < IMessage , bool > CreateIsInitializedCaller ( )
{
var prop = typeof ( T1 ) . GetTypeInfo ( ) . GetDeclaredProperty ( "_Extensions" ) ;
var getFunc = ( Func < T1 , ExtensionSet < T1 > > ) prop . GetMethod . CreateDelegate ( typeof ( Func < T1 , ExtensionSet < T1 > > ) ) ;
var initializedFunc = ( Func < ExtensionSet < T1 > , bool > )
typeof ( ExtensionSet < T1 > )
. GetTypeInfo ( )
. GetDeclaredMethod ( "IsInitialized" )
. CreateDelegate ( typeof ( Func < ExtensionSet < T1 > , bool > ) ) ;
return ( m ) = > {
var set = getFunc ( ( T1 ) m ) ;
return set = = null | | initializedFunc ( set ) ;
} ;
}
if ( extension is Extension < T1 , T3 > ext13 ) {
extensionMessage . SetExtension ( ext13 , ( T3 ) value ) ;
} else if ( extension is RepeatedExtension < T1 , T3 > ) {
throw new InvalidOperationException (
"SetValue is not implemented for repeated extensions" ) ;
} else {
throw new InvalidCastException (
"The provided extension is not a valid extension identifier type" ) ;
}
}
// Runtime compatibility checking code - see ReflectionHelper<T1, T2>.CreateFuncIMessageInt32 for
// details about why we're doing this.
// Deliberately not inside the generic type. We only want to check this once.
private static bool CanConvertEnumFuncToInt32Func { get ; } = CheckCanConvertEnumFuncToInt32Func ( ) ;
private static bool CheckCanConvertEnumFuncToInt32Func ( )
{
try
{
// Try to do the conversion using reflection, so we can see whether it's supported.
MethodInfo method = typeof ( ReflectionUtil ) . GetMethod ( nameof ( SampleEnumMethod ) ) ;
// If this passes, we're in a reasonable runtime.
method . CreateDelegate ( typeof ( Func < int > ) ) ;
return true ;
}
catch ( ArgumentException )
{
return false ;
}
public void ClearExtension ( IMessage message ) {
if ( message is not T1 extensionMessage ) {
throw new InvalidCastException (
"Cannot access extension on message that isn't IExtensionMessage" ) ;
}
public enum SampleEnum
{
X
if ( extension is Extension < T1 , T3 > ext13 ) {
extensionMessage . ClearExtension ( ext13 ) ;
} else if ( extension is RepeatedExtension < T1 , T3 > repeatedExt13 ) {
extensionMessage . GetExtension ( repeatedExt13 ) . Clear ( ) ;
} else {
throw new InvalidCastException (
"The provided extension is not a valid extension identifier type" ) ;
}
}
}
// Public to make the reflection simpler.
public static SampleEnum SampleEnumMethod ( ) = > SampleEnum . X ;
#if NET5_0_OR_GREATER
/// <summary>
/// This helper is compatible with .NET Native AOT.
/// MakeGenericType doesn't work when a type argument is a value type in AOT.
/// MethodInfo.Invoke is used instead of compiled expressions because it's faster in AOT.
/// </summary>
private sealed class AotReflectionHelper : IReflectionHelper {
private static readonly object [ ] EmptyObjectArray = new object [ 0 ] ;
public Action < IMessage > CreateActionIMessage ( MethodInfo method ) = > message = >
method . Invoke ( message , EmptyObjectArray ) ;
public Action < IMessage , object > CreateActionIMessageObject ( MethodInfo method ) = >
( message , arg ) = > method . Invoke ( message , new object [ ] { arg } ) ;
public Func < IMessage , bool > CreateFuncIMessageBool ( MethodInfo method ) = > message = >
( bool ) method . Invoke ( message , EmptyObjectArray ) ;
public Func < IMessage , int > CreateFuncIMessageInt32 ( MethodInfo method ) = > message = >
( int ) method . Invoke ( message , EmptyObjectArray ) ;
public Func < IMessage , object > CreateFuncIMessageObject ( MethodInfo method ) = > message = >
method . Invoke ( message , EmptyObjectArray ) ;
}
/// <summary>
/// Reflection with extensions isn't supported because IExtendableMessage members are used to
/// get values. Can't use reflection to invoke those methods because they have a generic
/// argument. MakeGenericMethod can't be used because it will break whenever the extension type
/// is a value type. This could be made to work if there were non-generic methods available for
/// getting and setting extension values.
/// </summary>
private sealed class AotExtensionReflectionHelper : IExtensionReflectionHelper {
private const string Message = "Extensions reflection is not supported with AOT." ;
public object GetExtension ( IMessage message ) = > throw new NotSupportedException ( Message ) ;
public bool HasExtension ( IMessage message ) = > throw new NotSupportedException ( Message ) ;
public void SetExtension ( IMessage message ,
object value ) = > throw new NotSupportedException ( Message ) ;
public void ClearExtension ( IMessage message ) = > throw new NotSupportedException ( Message ) ;
}
# endif
private sealed class ExtensionSetReflector < [ DynamicallyAccessedMembers (
DynamicallyAccessedMemberTypes . PublicProperties |
DynamicallyAccessedMemberTypes . NonPublicProperties ) ] T1 > : IExtensionSetReflector
where T1 : IExtendableMessage < T1 > {
public Func < IMessage , bool > CreateIsInitializedCaller ( ) {
var prop = typeof ( T1 ) . GetTypeInfo ( ) . GetDeclaredProperty ( "_Extensions" ) ;
var getFunc = ( Func < T1 , ExtensionSet < T1 > > ) prop . GetMethod . CreateDelegate (
typeof ( Func < T1 , ExtensionSet < T1 > > ) ) ;
var initializedFunc = ( Func < ExtensionSet < T1 > , bool > ) typeof ( ExtensionSet < T1 > )
. GetTypeInfo ( )
. GetDeclaredMethod ( "IsInitialized" )
. CreateDelegate ( typeof ( Func < ExtensionSet < T1 > , bool > ) ) ;
return ( m ) = > {
var set = getFunc ( ( T1 ) m ) ;
return set = = null | | initializedFunc ( set ) ;
} ;
}
}
// Runtime compatibility checking code - see ReflectionHelper<T1, T2>.CreateFuncIMessageInt32
// for details about why we're doing this.
// Deliberately not inside the generic type. We only want to check this once.
private static bool CanConvertEnumFuncToInt32Func { get ; } =
CheckCanConvertEnumFuncToInt32Func ( ) ;
private static bool CheckCanConvertEnumFuncToInt32Func ( ) {
try {
// Try to do the conversion using reflection, so we can see whether it's supported.
MethodInfo method = typeof ( ReflectionUtil ) . GetMethod ( nameof ( SampleEnumMethod ) ) ;
// If this passes, we're in a reasonable runtime.
method . CreateDelegate ( typeof ( Func < int > ) ) ;
return true ;
} catch ( ArgumentException ) {
return false ;
}
}
public enum SampleEnum { X }
// Public to make the reflection simpler.
public static SampleEnum SampleEnumMethod ( ) = > SampleEnum . X ;
}
}