1*1b3f573fSAndroid Build Coastguard Worker #region Copyright notice and license 2*1b3f573fSAndroid Build Coastguard Worker // Protocol Buffers - Google's data interchange format 3*1b3f573fSAndroid Build Coastguard Worker // Copyright 2008 Google Inc. All rights reserved. 4*1b3f573fSAndroid Build Coastguard Worker // https://developers.google.com/protocol-buffers/ 5*1b3f573fSAndroid Build Coastguard Worker // 6*1b3f573fSAndroid Build Coastguard Worker // Redistribution and use in source and binary forms, with or without 7*1b3f573fSAndroid Build Coastguard Worker // modification, are permitted provided that the following conditions are 8*1b3f573fSAndroid Build Coastguard Worker // met: 9*1b3f573fSAndroid Build Coastguard Worker // 10*1b3f573fSAndroid Build Coastguard Worker // * Redistributions of source code must retain the above copyright 11*1b3f573fSAndroid Build Coastguard Worker // notice, this list of conditions and the following disclaimer. 12*1b3f573fSAndroid Build Coastguard Worker // * Redistributions in binary form must reproduce the above 13*1b3f573fSAndroid Build Coastguard Worker // copyright notice, this list of conditions and the following disclaimer 14*1b3f573fSAndroid Build Coastguard Worker // in the documentation and/or other materials provided with the 15*1b3f573fSAndroid Build Coastguard Worker // distribution. 16*1b3f573fSAndroid Build Coastguard Worker // * Neither the name of Google Inc. nor the names of its 17*1b3f573fSAndroid Build Coastguard Worker // contributors may be used to endorse or promote products derived from 18*1b3f573fSAndroid Build Coastguard Worker // this software without specific prior written permission. 19*1b3f573fSAndroid Build Coastguard Worker // 20*1b3f573fSAndroid Build Coastguard Worker // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21*1b3f573fSAndroid Build Coastguard Worker // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22*1b3f573fSAndroid Build Coastguard Worker // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23*1b3f573fSAndroid Build Coastguard Worker // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24*1b3f573fSAndroid Build Coastguard Worker // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25*1b3f573fSAndroid Build Coastguard Worker // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26*1b3f573fSAndroid Build Coastguard Worker // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27*1b3f573fSAndroid Build Coastguard Worker // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28*1b3f573fSAndroid Build Coastguard Worker // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29*1b3f573fSAndroid Build Coastguard Worker // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30*1b3f573fSAndroid Build Coastguard Worker // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31*1b3f573fSAndroid Build Coastguard Worker #endregion 32*1b3f573fSAndroid Build Coastguard Worker 33*1b3f573fSAndroid Build Coastguard Worker using Google.Protobuf.Compatibility; 34*1b3f573fSAndroid Build Coastguard Worker using System; 35*1b3f573fSAndroid Build Coastguard Worker using System.Diagnostics.CodeAnalysis; 36*1b3f573fSAndroid Build Coastguard Worker using System.Reflection; 37*1b3f573fSAndroid Build Coastguard Worker 38*1b3f573fSAndroid Build Coastguard Worker namespace Google.Protobuf.Reflection 39*1b3f573fSAndroid Build Coastguard Worker { 40*1b3f573fSAndroid Build Coastguard Worker /// <summary> 41*1b3f573fSAndroid Build Coastguard Worker /// The methods in this class are somewhat evil, and should not be tampered with lightly. 42*1b3f573fSAndroid Build Coastguard Worker /// Basically they allow the creation of relatively weakly typed delegates from MethodInfos 43*1b3f573fSAndroid Build Coastguard Worker /// which are more strongly typed. They do this by creating an appropriate strongly typed 44*1b3f573fSAndroid Build Coastguard Worker /// delegate from the MethodInfo, and then calling that within an anonymous method. 45*1b3f573fSAndroid Build Coastguard Worker /// Mind-bending stuff (at least to your humble narrator) but the resulting delegates are 46*1b3f573fSAndroid Build Coastguard Worker /// very fast compared with calling Invoke later on. 47*1b3f573fSAndroid Build Coastguard Worker /// </summary> 48*1b3f573fSAndroid Build Coastguard Worker internal static class ReflectionUtil 49*1b3f573fSAndroid Build Coastguard Worker { ReflectionUtil()50*1b3f573fSAndroid Build Coastguard Worker static ReflectionUtil() 51*1b3f573fSAndroid Build Coastguard Worker { 52*1b3f573fSAndroid Build Coastguard Worker ForceInitialize<string>(); // Handles all reference types 53*1b3f573fSAndroid Build Coastguard Worker ForceInitialize<int>(); 54*1b3f573fSAndroid Build Coastguard Worker ForceInitialize<long>(); 55*1b3f573fSAndroid Build Coastguard Worker ForceInitialize<uint>(); 56*1b3f573fSAndroid Build Coastguard Worker ForceInitialize<ulong>(); 57*1b3f573fSAndroid Build Coastguard Worker ForceInitialize<float>(); 58*1b3f573fSAndroid Build Coastguard Worker ForceInitialize<double>(); 59*1b3f573fSAndroid Build Coastguard Worker ForceInitialize<bool>(); 60*1b3f573fSAndroid Build Coastguard Worker ForceInitialize<int?>(); 61*1b3f573fSAndroid Build Coastguard Worker ForceInitialize<long?>(); 62*1b3f573fSAndroid Build Coastguard Worker ForceInitialize<uint?>(); 63*1b3f573fSAndroid Build Coastguard Worker ForceInitialize<ulong?>(); 64*1b3f573fSAndroid Build Coastguard Worker ForceInitialize<float?>(); 65*1b3f573fSAndroid Build Coastguard Worker ForceInitialize<double?>(); 66*1b3f573fSAndroid Build Coastguard Worker ForceInitialize<bool?>(); 67*1b3f573fSAndroid Build Coastguard Worker ForceInitialize<SampleEnum>(); 68*1b3f573fSAndroid Build Coastguard Worker SampleEnumMethod(); 69*1b3f573fSAndroid Build Coastguard Worker } 70*1b3f573fSAndroid Build Coastguard Worker ForceInitialize()71*1b3f573fSAndroid Build Coastguard Worker internal static void ForceInitialize<T>() => new ReflectionHelper<IMessage, T>(); 72*1b3f573fSAndroid Build Coastguard Worker 73*1b3f573fSAndroid Build Coastguard Worker /// <summary> 74*1b3f573fSAndroid Build Coastguard Worker /// Empty Type[] used when calling GetProperty to force property instead of indexer fetching. 75*1b3f573fSAndroid Build Coastguard Worker /// </summary> 76*1b3f573fSAndroid Build Coastguard Worker internal static readonly Type[] EmptyTypes = new Type[0]; 77*1b3f573fSAndroid Build Coastguard Worker 78*1b3f573fSAndroid Build Coastguard Worker /// <summary> 79*1b3f573fSAndroid Build Coastguard Worker /// Creates a delegate which will cast the argument to the type that declares the method, 80*1b3f573fSAndroid Build Coastguard Worker /// call the method on it, then convert the result to object. 81*1b3f573fSAndroid Build Coastguard Worker /// </summary> 82*1b3f573fSAndroid Build Coastguard Worker /// <param name="method">The method to create a delegate for, which must be declared in an IMessage 83*1b3f573fSAndroid Build Coastguard Worker /// implementation.</param> 84*1b3f573fSAndroid Build Coastguard Worker internal static Func<IMessage, object> CreateFuncIMessageObject(MethodInfo method) => 85*1b3f573fSAndroid Build Coastguard Worker GetReflectionHelper(method.DeclaringType, method.ReturnType).CreateFuncIMessageObject(method); 86*1b3f573fSAndroid Build Coastguard Worker 87*1b3f573fSAndroid Build Coastguard Worker /// <summary> 88*1b3f573fSAndroid Build Coastguard Worker /// Creates a delegate which will cast the argument to the type that declares the method, 89*1b3f573fSAndroid Build Coastguard Worker /// call the method on it, then convert the result to the specified type. The method is expected 90*1b3f573fSAndroid Build Coastguard Worker /// to actually return an enum (because of where we're calling it - for oneof cases). Sometimes that 91*1b3f573fSAndroid Build Coastguard Worker /// means we need some extra work to perform conversions. 92*1b3f573fSAndroid Build Coastguard Worker /// </summary> 93*1b3f573fSAndroid Build Coastguard Worker /// <param name="method">The method to create a delegate for, which must be declared in an IMessage 94*1b3f573fSAndroid Build Coastguard Worker /// implementation.</param> 95*1b3f573fSAndroid Build Coastguard Worker internal static Func<IMessage, int> CreateFuncIMessageInt32(MethodInfo method) => 96*1b3f573fSAndroid Build Coastguard Worker GetReflectionHelper(method.DeclaringType, method.ReturnType).CreateFuncIMessageInt32(method); 97*1b3f573fSAndroid Build Coastguard Worker 98*1b3f573fSAndroid Build Coastguard Worker /// <summary> 99*1b3f573fSAndroid Build Coastguard Worker /// Creates a delegate which will execute the given method after casting the first argument to 100*1b3f573fSAndroid Build Coastguard Worker /// the type that declares the method, and the second argument to the first parameter type of the method. 101*1b3f573fSAndroid Build Coastguard Worker /// </summary> 102*1b3f573fSAndroid Build Coastguard Worker /// <param name="method">The method to create a delegate for, which must be declared in an IMessage 103*1b3f573fSAndroid Build Coastguard Worker /// implementation.</param> 104*1b3f573fSAndroid Build Coastguard Worker internal static Action<IMessage, object> CreateActionIMessageObject(MethodInfo method) => 105*1b3f573fSAndroid Build Coastguard Worker GetReflectionHelper(method.DeclaringType, method.GetParameters()[0].ParameterType).CreateActionIMessageObject(method); 106*1b3f573fSAndroid Build Coastguard Worker 107*1b3f573fSAndroid Build Coastguard Worker /// <summary> 108*1b3f573fSAndroid Build Coastguard Worker /// Creates a delegate which will execute the given method after casting the first argument to 109*1b3f573fSAndroid Build Coastguard Worker /// type that declares the method. 110*1b3f573fSAndroid Build Coastguard Worker /// </summary> 111*1b3f573fSAndroid Build Coastguard Worker /// <param name="method">The method to create a delegate for, which must be declared in an IMessage 112*1b3f573fSAndroid Build Coastguard Worker /// implementation.</param> 113*1b3f573fSAndroid Build Coastguard Worker internal static Action<IMessage> CreateActionIMessage(MethodInfo method) => 114*1b3f573fSAndroid Build Coastguard Worker GetReflectionHelper(method.DeclaringType, typeof(object)).CreateActionIMessage(method); 115*1b3f573fSAndroid Build Coastguard Worker 116*1b3f573fSAndroid Build Coastguard Worker internal static Func<IMessage, bool> CreateFuncIMessageBool(MethodInfo method) => 117*1b3f573fSAndroid Build Coastguard Worker GetReflectionHelper(method.DeclaringType, method.ReturnType).CreateFuncIMessageBool(method); 118*1b3f573fSAndroid Build Coastguard Worker 119*1b3f573fSAndroid Build Coastguard Worker [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Type parameter members are preserved with DynamicallyAccessedMembers on GeneratedClrTypeInfo.ctor clrType parameter.")] 120*1b3f573fSAndroid Build Coastguard Worker internal static Func<IMessage, bool> CreateIsInitializedCaller([DynamicallyAccessedMembers(GeneratedClrTypeInfo.MessageAccessibility)]Type msg) => 121*1b3f573fSAndroid Build Coastguard Worker ((IExtensionSetReflector)Activator.CreateInstance(typeof(ExtensionSetReflector<>).MakeGenericType(msg))).CreateIsInitializedCaller(); 122*1b3f573fSAndroid Build Coastguard Worker 123*1b3f573fSAndroid Build Coastguard Worker /// <summary> 124*1b3f573fSAndroid Build Coastguard Worker /// Creates a delegate which will execute the given method after casting the first argument to 125*1b3f573fSAndroid Build Coastguard Worker /// the type that declares the method, and the second argument to the first parameter type of the method. 126*1b3f573fSAndroid Build Coastguard Worker /// </summary> 127*1b3f573fSAndroid Build Coastguard Worker [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Type parameter members are preserved with DynamicallyAccessedMembers on GeneratedClrTypeInfo.ctor clrType parameter.")] 128*1b3f573fSAndroid Build Coastguard Worker internal static IExtensionReflectionHelper CreateExtensionHelper(Extension extension) => 129*1b3f573fSAndroid Build Coastguard Worker (IExtensionReflectionHelper)Activator.CreateInstance(typeof(ExtensionReflectionHelper<,>).MakeGenericType(extension.TargetType, extension.GetType().GenericTypeArguments[1]), extension); 130*1b3f573fSAndroid Build Coastguard Worker 131*1b3f573fSAndroid Build Coastguard Worker /// <summary> 132*1b3f573fSAndroid Build Coastguard Worker /// Creates a reflection helper for the given type arguments. Currently these are created on demand 133*1b3f573fSAndroid Build Coastguard Worker /// rather than cached; this will be "busy" when initially loading a message's descriptor, but after that 134*1b3f573fSAndroid Build Coastguard Worker /// they can be garbage collected. We could cache them by type if that proves to be important, but creating 135*1b3f573fSAndroid Build Coastguard Worker /// an object is pretty cheap. 136*1b3f573fSAndroid Build Coastguard Worker /// </summary> 137*1b3f573fSAndroid Build Coastguard Worker [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Type parameter members are preserved with DynamicallyAccessedMembers on GeneratedClrTypeInfo.ctor clrType parameter.")] GetReflectionHelper(Type t1, Type t2)138*1b3f573fSAndroid Build Coastguard Worker private static IReflectionHelper GetReflectionHelper(Type t1, Type t2) => 139*1b3f573fSAndroid Build Coastguard Worker (IReflectionHelper) Activator.CreateInstance(typeof(ReflectionHelper<,>).MakeGenericType(t1, t2)); 140*1b3f573fSAndroid Build Coastguard Worker 141*1b3f573fSAndroid Build Coastguard Worker // Non-generic interface allowing us to use an instance of ReflectionHelper<T1, T2> without statically 142*1b3f573fSAndroid Build Coastguard Worker // knowing the types involved. 143*1b3f573fSAndroid Build Coastguard Worker private interface IReflectionHelper 144*1b3f573fSAndroid Build Coastguard Worker { CreateFuncIMessageInt32(MethodInfo method)145*1b3f573fSAndroid Build Coastguard Worker Func<IMessage, int> CreateFuncIMessageInt32(MethodInfo method); CreateActionIMessage(MethodInfo method)146*1b3f573fSAndroid Build Coastguard Worker Action<IMessage> CreateActionIMessage(MethodInfo method); CreateFuncIMessageObject(MethodInfo method)147*1b3f573fSAndroid Build Coastguard Worker Func<IMessage, object> CreateFuncIMessageObject(MethodInfo method); CreateActionIMessageObject(MethodInfo method)148*1b3f573fSAndroid Build Coastguard Worker Action<IMessage, object> CreateActionIMessageObject(MethodInfo method); CreateFuncIMessageBool(MethodInfo method)149*1b3f573fSAndroid Build Coastguard Worker Func<IMessage, bool> CreateFuncIMessageBool(MethodInfo method); 150*1b3f573fSAndroid Build Coastguard Worker } 151*1b3f573fSAndroid Build Coastguard Worker 152*1b3f573fSAndroid Build Coastguard Worker internal interface IExtensionReflectionHelper 153*1b3f573fSAndroid Build Coastguard Worker { GetExtension(IMessage message)154*1b3f573fSAndroid Build Coastguard Worker object GetExtension(IMessage message); SetExtension(IMessage message, object value)155*1b3f573fSAndroid Build Coastguard Worker void SetExtension(IMessage message, object value); HasExtension(IMessage message)156*1b3f573fSAndroid Build Coastguard Worker bool HasExtension(IMessage message); ClearExtension(IMessage message)157*1b3f573fSAndroid Build Coastguard Worker void ClearExtension(IMessage message); 158*1b3f573fSAndroid Build Coastguard Worker } 159*1b3f573fSAndroid Build Coastguard Worker 160*1b3f573fSAndroid Build Coastguard Worker private interface IExtensionSetReflector 161*1b3f573fSAndroid Build Coastguard Worker { CreateIsInitializedCaller()162*1b3f573fSAndroid Build Coastguard Worker Func<IMessage, bool> CreateIsInitializedCaller(); 163*1b3f573fSAndroid Build Coastguard Worker } 164*1b3f573fSAndroid Build Coastguard Worker 165*1b3f573fSAndroid Build Coastguard Worker private class ReflectionHelper<T1, T2> : IReflectionHelper 166*1b3f573fSAndroid Build Coastguard Worker { 167*1b3f573fSAndroid Build Coastguard Worker CreateFuncIMessageInt32(MethodInfo method)168*1b3f573fSAndroid Build Coastguard Worker public Func<IMessage, int> CreateFuncIMessageInt32(MethodInfo method) 169*1b3f573fSAndroid Build Coastguard Worker { 170*1b3f573fSAndroid Build Coastguard Worker // On pleasant runtimes, we can create a Func<int> from a method returning 171*1b3f573fSAndroid Build Coastguard Worker // an enum based on an int. That's the fast path. 172*1b3f573fSAndroid Build Coastguard Worker if (CanConvertEnumFuncToInt32Func) 173*1b3f573fSAndroid Build Coastguard Worker { 174*1b3f573fSAndroid Build Coastguard Worker var del = (Func<T1, int>) method.CreateDelegate(typeof(Func<T1, int>)); 175*1b3f573fSAndroid Build Coastguard Worker return message => del((T1) message); 176*1b3f573fSAndroid Build Coastguard Worker } 177*1b3f573fSAndroid Build Coastguard Worker else 178*1b3f573fSAndroid Build Coastguard Worker { 179*1b3f573fSAndroid Build Coastguard Worker // On some runtimes (e.g. old Mono) the return type has to be exactly correct, 180*1b3f573fSAndroid Build Coastguard Worker // so we go via boxing. Reflection is already fairly inefficient, and this is 181*1b3f573fSAndroid Build Coastguard Worker // only used for one-of case checking, fortunately. 182*1b3f573fSAndroid Build Coastguard Worker var del = (Func<T1, T2>) method.CreateDelegate(typeof(Func<T1, T2>)); 183*1b3f573fSAndroid Build Coastguard Worker return message => (int) (object) del((T1) message); 184*1b3f573fSAndroid Build Coastguard Worker } 185*1b3f573fSAndroid Build Coastguard Worker } 186*1b3f573fSAndroid Build Coastguard Worker CreateActionIMessage(MethodInfo method)187*1b3f573fSAndroid Build Coastguard Worker public Action<IMessage> CreateActionIMessage(MethodInfo method) 188*1b3f573fSAndroid Build Coastguard Worker { 189*1b3f573fSAndroid Build Coastguard Worker var del = (Action<T1>) method.CreateDelegate(typeof(Action<T1>)); 190*1b3f573fSAndroid Build Coastguard Worker return message => del((T1) message); 191*1b3f573fSAndroid Build Coastguard Worker } 192*1b3f573fSAndroid Build Coastguard Worker CreateFuncIMessageObject(MethodInfo method)193*1b3f573fSAndroid Build Coastguard Worker public Func<IMessage, object> CreateFuncIMessageObject(MethodInfo method) 194*1b3f573fSAndroid Build Coastguard Worker { 195*1b3f573fSAndroid Build Coastguard Worker var del = (Func<T1, T2>) method.CreateDelegate(typeof(Func<T1, T2>)); 196*1b3f573fSAndroid Build Coastguard Worker return message => del((T1) message); 197*1b3f573fSAndroid Build Coastguard Worker } 198*1b3f573fSAndroid Build Coastguard Worker CreateActionIMessageObject(MethodInfo method)199*1b3f573fSAndroid Build Coastguard Worker public Action<IMessage, object> CreateActionIMessageObject(MethodInfo method) 200*1b3f573fSAndroid Build Coastguard Worker { 201*1b3f573fSAndroid Build Coastguard Worker var del = (Action<T1, T2>) method.CreateDelegate(typeof(Action<T1, T2>)); 202*1b3f573fSAndroid Build Coastguard Worker return (message, arg) => del((T1) message, (T2) arg); 203*1b3f573fSAndroid Build Coastguard Worker } 204*1b3f573fSAndroid Build Coastguard Worker CreateFuncIMessageBool(MethodInfo method)205*1b3f573fSAndroid Build Coastguard Worker public Func<IMessage, bool> CreateFuncIMessageBool(MethodInfo method) 206*1b3f573fSAndroid Build Coastguard Worker { 207*1b3f573fSAndroid Build Coastguard Worker var del = (Func<T1, bool>)method.CreateDelegate(typeof(Func<T1, bool>)); 208*1b3f573fSAndroid Build Coastguard Worker return message => del((T1)message); 209*1b3f573fSAndroid Build Coastguard Worker } 210*1b3f573fSAndroid Build Coastguard Worker } 211*1b3f573fSAndroid Build Coastguard Worker 212*1b3f573fSAndroid Build Coastguard Worker private class ExtensionReflectionHelper<T1, T3> : IExtensionReflectionHelper 213*1b3f573fSAndroid Build Coastguard Worker where T1 : IExtendableMessage<T1> 214*1b3f573fSAndroid Build Coastguard Worker { 215*1b3f573fSAndroid Build Coastguard Worker private readonly Extension extension; 216*1b3f573fSAndroid Build Coastguard Worker ExtensionReflectionHelper(Extension extension)217*1b3f573fSAndroid Build Coastguard Worker public ExtensionReflectionHelper(Extension extension) 218*1b3f573fSAndroid Build Coastguard Worker { 219*1b3f573fSAndroid Build Coastguard Worker this.extension = extension; 220*1b3f573fSAndroid Build Coastguard Worker } 221*1b3f573fSAndroid Build Coastguard Worker GetExtension(IMessage message)222*1b3f573fSAndroid Build Coastguard Worker public object GetExtension(IMessage message) 223*1b3f573fSAndroid Build Coastguard Worker { 224*1b3f573fSAndroid Build Coastguard Worker if (!(message is T1)) 225*1b3f573fSAndroid Build Coastguard Worker { 226*1b3f573fSAndroid Build Coastguard Worker throw new InvalidCastException("Cannot access extension on message that isn't IExtensionMessage"); 227*1b3f573fSAndroid Build Coastguard Worker } 228*1b3f573fSAndroid Build Coastguard Worker 229*1b3f573fSAndroid Build Coastguard Worker T1 extensionMessage = (T1)message; 230*1b3f573fSAndroid Build Coastguard Worker 231*1b3f573fSAndroid Build Coastguard Worker if (extension is Extension<T1, T3>) 232*1b3f573fSAndroid Build Coastguard Worker { 233*1b3f573fSAndroid Build Coastguard Worker return extensionMessage.GetExtension(extension as Extension<T1, T3>); 234*1b3f573fSAndroid Build Coastguard Worker } 235*1b3f573fSAndroid Build Coastguard Worker else if (extension is RepeatedExtension<T1, T3>) 236*1b3f573fSAndroid Build Coastguard Worker { 237*1b3f573fSAndroid Build Coastguard Worker return extensionMessage.GetOrInitializeExtension(extension as RepeatedExtension<T1, T3>); 238*1b3f573fSAndroid Build Coastguard Worker } 239*1b3f573fSAndroid Build Coastguard Worker else 240*1b3f573fSAndroid Build Coastguard Worker { 241*1b3f573fSAndroid Build Coastguard Worker throw new InvalidCastException("The provided extension is not a valid extension identifier type"); 242*1b3f573fSAndroid Build Coastguard Worker } 243*1b3f573fSAndroid Build Coastguard Worker } 244*1b3f573fSAndroid Build Coastguard Worker HasExtension(IMessage message)245*1b3f573fSAndroid Build Coastguard Worker public bool HasExtension(IMessage message) 246*1b3f573fSAndroid Build Coastguard Worker { 247*1b3f573fSAndroid Build Coastguard Worker if (!(message is T1)) 248*1b3f573fSAndroid Build Coastguard Worker { 249*1b3f573fSAndroid Build Coastguard Worker throw new InvalidCastException("Cannot access extension on message that isn't IExtensionMessage"); 250*1b3f573fSAndroid Build Coastguard Worker } 251*1b3f573fSAndroid Build Coastguard Worker 252*1b3f573fSAndroid Build Coastguard Worker T1 extensionMessage = (T1)message; 253*1b3f573fSAndroid Build Coastguard Worker 254*1b3f573fSAndroid Build Coastguard Worker if (extension is Extension<T1, T3>) 255*1b3f573fSAndroid Build Coastguard Worker { 256*1b3f573fSAndroid Build Coastguard Worker return extensionMessage.HasExtension(extension as Extension<T1, T3>); 257*1b3f573fSAndroid Build Coastguard Worker } 258*1b3f573fSAndroid Build Coastguard Worker else if (extension is RepeatedExtension<T1, T3>) 259*1b3f573fSAndroid Build Coastguard Worker { 260*1b3f573fSAndroid Build Coastguard Worker throw new InvalidOperationException("HasValue is not implemented for repeated extensions"); 261*1b3f573fSAndroid Build Coastguard Worker } 262*1b3f573fSAndroid Build Coastguard Worker else 263*1b3f573fSAndroid Build Coastguard Worker { 264*1b3f573fSAndroid Build Coastguard Worker throw new InvalidCastException("The provided extension is not a valid extension identifier type"); 265*1b3f573fSAndroid Build Coastguard Worker } 266*1b3f573fSAndroid Build Coastguard Worker } 267*1b3f573fSAndroid Build Coastguard Worker SetExtension(IMessage message, object value)268*1b3f573fSAndroid Build Coastguard Worker public void SetExtension(IMessage message, object value) 269*1b3f573fSAndroid Build Coastguard Worker { 270*1b3f573fSAndroid Build Coastguard Worker if (!(message is T1)) 271*1b3f573fSAndroid Build Coastguard Worker { 272*1b3f573fSAndroid Build Coastguard Worker throw new InvalidCastException("Cannot access extension on message that isn't IExtensionMessage"); 273*1b3f573fSAndroid Build Coastguard Worker } 274*1b3f573fSAndroid Build Coastguard Worker 275*1b3f573fSAndroid Build Coastguard Worker T1 extensionMessage = (T1)message; 276*1b3f573fSAndroid Build Coastguard Worker 277*1b3f573fSAndroid Build Coastguard Worker if (extension is Extension<T1, T3>) 278*1b3f573fSAndroid Build Coastguard Worker { 279*1b3f573fSAndroid Build Coastguard Worker extensionMessage.SetExtension(extension as Extension<T1, T3>, (T3)value); 280*1b3f573fSAndroid Build Coastguard Worker } 281*1b3f573fSAndroid Build Coastguard Worker else if (extension is RepeatedExtension<T1, T3>) 282*1b3f573fSAndroid Build Coastguard Worker { 283*1b3f573fSAndroid Build Coastguard Worker throw new InvalidOperationException("SetValue is not implemented for repeated extensions"); 284*1b3f573fSAndroid Build Coastguard Worker } 285*1b3f573fSAndroid Build Coastguard Worker else 286*1b3f573fSAndroid Build Coastguard Worker { 287*1b3f573fSAndroid Build Coastguard Worker throw new InvalidCastException("The provided extension is not a valid extension identifier type"); 288*1b3f573fSAndroid Build Coastguard Worker } 289*1b3f573fSAndroid Build Coastguard Worker } 290*1b3f573fSAndroid Build Coastguard Worker ClearExtension(IMessage message)291*1b3f573fSAndroid Build Coastguard Worker public void ClearExtension(IMessage message) 292*1b3f573fSAndroid Build Coastguard Worker { 293*1b3f573fSAndroid Build Coastguard Worker if (!(message is T1)) 294*1b3f573fSAndroid Build Coastguard Worker { 295*1b3f573fSAndroid Build Coastguard Worker throw new InvalidCastException("Cannot access extension on message that isn't IExtensionMessage"); 296*1b3f573fSAndroid Build Coastguard Worker } 297*1b3f573fSAndroid Build Coastguard Worker 298*1b3f573fSAndroid Build Coastguard Worker T1 extensionMessage = (T1)message; 299*1b3f573fSAndroid Build Coastguard Worker 300*1b3f573fSAndroid Build Coastguard Worker if (extension is Extension<T1, T3>) 301*1b3f573fSAndroid Build Coastguard Worker { 302*1b3f573fSAndroid Build Coastguard Worker extensionMessage.ClearExtension(extension as Extension<T1, T3>); 303*1b3f573fSAndroid Build Coastguard Worker } 304*1b3f573fSAndroid Build Coastguard Worker else if (extension is RepeatedExtension<T1, T3>) 305*1b3f573fSAndroid Build Coastguard Worker { 306*1b3f573fSAndroid Build Coastguard Worker extensionMessage.GetExtension(extension as RepeatedExtension<T1, T3>).Clear(); 307*1b3f573fSAndroid Build Coastguard Worker } 308*1b3f573fSAndroid Build Coastguard Worker else 309*1b3f573fSAndroid Build Coastguard Worker { 310*1b3f573fSAndroid Build Coastguard Worker throw new InvalidCastException("The provided extension is not a valid extension identifier type"); 311*1b3f573fSAndroid Build Coastguard Worker } 312*1b3f573fSAndroid Build Coastguard Worker } 313*1b3f573fSAndroid Build Coastguard Worker } 314*1b3f573fSAndroid Build Coastguard Worker 315*1b3f573fSAndroid Build Coastguard Worker private class ExtensionSetReflector< 316*1b3f573fSAndroid Build Coastguard Worker [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] 317*1b3f573fSAndroid Build Coastguard Worker T1> : IExtensionSetReflector where T1 : IExtendableMessage<T1> 318*1b3f573fSAndroid Build Coastguard Worker { CreateIsInitializedCaller()319*1b3f573fSAndroid Build Coastguard Worker public Func<IMessage, bool> CreateIsInitializedCaller() 320*1b3f573fSAndroid Build Coastguard Worker { 321*1b3f573fSAndroid Build Coastguard Worker var prop = typeof(T1).GetTypeInfo().GetDeclaredProperty("_Extensions"); 322*1b3f573fSAndroid Build Coastguard Worker var getFunc = (Func<T1, ExtensionSet<T1>>)prop.GetMethod.CreateDelegate(typeof(Func<T1, ExtensionSet<T1>>)); 323*1b3f573fSAndroid Build Coastguard Worker var initializedFunc = (Func<ExtensionSet<T1>, bool>) 324*1b3f573fSAndroid Build Coastguard Worker typeof(ExtensionSet<T1>) 325*1b3f573fSAndroid Build Coastguard Worker .GetTypeInfo() 326*1b3f573fSAndroid Build Coastguard Worker .GetDeclaredMethod("IsInitialized") 327*1b3f573fSAndroid Build Coastguard Worker .CreateDelegate(typeof(Func<ExtensionSet<T1>, bool>)); 328*1b3f573fSAndroid Build Coastguard Worker return (m) => { 329*1b3f573fSAndroid Build Coastguard Worker var set = getFunc((T1)m); 330*1b3f573fSAndroid Build Coastguard Worker return set == null || initializedFunc(set); 331*1b3f573fSAndroid Build Coastguard Worker }; 332*1b3f573fSAndroid Build Coastguard Worker } 333*1b3f573fSAndroid Build Coastguard Worker } 334*1b3f573fSAndroid Build Coastguard Worker 335*1b3f573fSAndroid Build Coastguard Worker // Runtime compatibility checking code - see ReflectionHelper<T1, T2>.CreateFuncIMessageInt32 for 336*1b3f573fSAndroid Build Coastguard Worker // details about why we're doing this. 337*1b3f573fSAndroid Build Coastguard Worker 338*1b3f573fSAndroid Build Coastguard Worker // Deliberately not inside the generic type. We only want to check this once. 339*1b3f573fSAndroid Build Coastguard Worker private static bool CanConvertEnumFuncToInt32Func { get; } = CheckCanConvertEnumFuncToInt32Func(); 340*1b3f573fSAndroid Build Coastguard Worker CheckCanConvertEnumFuncToInt32Func()341*1b3f573fSAndroid Build Coastguard Worker private static bool CheckCanConvertEnumFuncToInt32Func() 342*1b3f573fSAndroid Build Coastguard Worker { 343*1b3f573fSAndroid Build Coastguard Worker try 344*1b3f573fSAndroid Build Coastguard Worker { 345*1b3f573fSAndroid Build Coastguard Worker // Try to do the conversion using reflection, so we can see whether it's supported. 346*1b3f573fSAndroid Build Coastguard Worker MethodInfo method = typeof(ReflectionUtil).GetMethod(nameof(SampleEnumMethod)); 347*1b3f573fSAndroid Build Coastguard Worker // If this passes, we're in a reasonable runtime. 348*1b3f573fSAndroid Build Coastguard Worker method.CreateDelegate(typeof(Func<int>)); 349*1b3f573fSAndroid Build Coastguard Worker return true; 350*1b3f573fSAndroid Build Coastguard Worker } 351*1b3f573fSAndroid Build Coastguard Worker catch (ArgumentException) 352*1b3f573fSAndroid Build Coastguard Worker { 353*1b3f573fSAndroid Build Coastguard Worker return false; 354*1b3f573fSAndroid Build Coastguard Worker } 355*1b3f573fSAndroid Build Coastguard Worker } 356*1b3f573fSAndroid Build Coastguard Worker 357*1b3f573fSAndroid Build Coastguard Worker public enum SampleEnum 358*1b3f573fSAndroid Build Coastguard Worker { 359*1b3f573fSAndroid Build Coastguard Worker X 360*1b3f573fSAndroid Build Coastguard Worker } 361*1b3f573fSAndroid Build Coastguard Worker 362*1b3f573fSAndroid Build Coastguard Worker // Public to make the reflection simpler. SampleEnumMethod()363*1b3f573fSAndroid Build Coastguard Worker public static SampleEnum SampleEnumMethod() => SampleEnum.X; 364*1b3f573fSAndroid Build Coastguard Worker } 365*1b3f573fSAndroid Build Coastguard Worker } 366