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