You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

400 lines
16 KiB

  1. #region License
  2. // Copyright (c) 2007 James Newton-King
  3. //
  4. // Permission is hereby granted, free of charge, to any person
  5. // obtaining a copy of this software and associated documentation
  6. // files (the "Software"), to deal in the Software without
  7. // restriction, including without limitation the rights to use,
  8. // copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the
  10. // Software is furnished to do so, subject to the following
  11. // conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be
  14. // included in all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  18. // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  19. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  20. // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  21. // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  22. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  23. // OTHER DEALINGS IN THE SOFTWARE.
  24. #endregion
  25. #if NETFX
  26. using System;
  27. using System.Collections.Generic;
  28. #if NET20
  29. using Newtonsoft.Json.Utilities.LinqBridge;
  30. #endif
  31. using System.Reflection;
  32. using System.Reflection.Emit;
  33. using Newtonsoft.Json.Serialization;
  34. using System.Globalization;
  35. namespace Newtonsoft.Json.Utilities
  36. {
  37. internal class DynamicReflectionDelegateFactory : ReflectionDelegateFactory
  38. {
  39. internal static DynamicReflectionDelegateFactory Instance { get; } = new DynamicReflectionDelegateFactory();
  40. private static DynamicMethod CreateDynamicMethod(string name, Type returnType, Type[] parameterTypes, Type owner)
  41. {
  42. DynamicMethod dynamicMethod = !owner.IsInterface()
  43. ? new DynamicMethod(name, returnType, parameterTypes, owner, true)
  44. : new DynamicMethod(name, returnType, parameterTypes, owner.Module, true);
  45. return dynamicMethod;
  46. }
  47. public override ObjectConstructor<object> CreateParameterizedConstructor(MethodBase method)
  48. {
  49. DynamicMethod dynamicMethod = CreateDynamicMethod(method.ToString(), typeof(object), new[] { typeof(object[]) }, method.DeclaringType);
  50. ILGenerator generator = dynamicMethod.GetILGenerator();
  51. GenerateCreateMethodCallIL(method, generator, 0);
  52. return (ObjectConstructor<object>)dynamicMethod.CreateDelegate(typeof(ObjectConstructor<object>));
  53. }
  54. public override MethodCall<T, object> CreateMethodCall<T>(MethodBase method)
  55. {
  56. DynamicMethod dynamicMethod = CreateDynamicMethod(method.ToString(), typeof(object), new[] { typeof(object), typeof(object[]) }, method.DeclaringType);
  57. ILGenerator generator = dynamicMethod.GetILGenerator();
  58. GenerateCreateMethodCallIL(method, generator, 1);
  59. return (MethodCall<T, object>)dynamicMethod.CreateDelegate(typeof(MethodCall<T, object>));
  60. }
  61. private void GenerateCreateMethodCallIL(MethodBase method, ILGenerator generator, int argsIndex)
  62. {
  63. ParameterInfo[] args = method.GetParameters();
  64. Label argsOk = generator.DefineLabel();
  65. // throw an error if the number of argument values doesn't match method parameters
  66. generator.Emit(OpCodes.Ldarg, argsIndex);
  67. generator.Emit(OpCodes.Ldlen);
  68. generator.Emit(OpCodes.Ldc_I4, args.Length);
  69. generator.Emit(OpCodes.Beq, argsOk);
  70. generator.Emit(OpCodes.Newobj, typeof(TargetParameterCountException).GetConstructor(ReflectionUtils.EmptyTypes));
  71. generator.Emit(OpCodes.Throw);
  72. generator.MarkLabel(argsOk);
  73. if (!method.IsConstructor && !method.IsStatic)
  74. {
  75. generator.PushInstance(method.DeclaringType);
  76. }
  77. LocalBuilder localConvertible = generator.DeclareLocal(typeof(IConvertible));
  78. LocalBuilder localObject = generator.DeclareLocal(typeof(object));
  79. for (int i = 0; i < args.Length; i++)
  80. {
  81. ParameterInfo parameter = args[i];
  82. Type parameterType = parameter.ParameterType;
  83. if (parameterType.IsByRef)
  84. {
  85. parameterType = parameterType.GetElementType();
  86. LocalBuilder localVariable = generator.DeclareLocal(parameterType);
  87. // don't need to set variable for 'out' parameter
  88. if (!parameter.IsOut)
  89. {
  90. generator.PushArrayInstance(argsIndex, i);
  91. if (parameterType.IsValueType())
  92. {
  93. Label skipSettingDefault = generator.DefineLabel();
  94. Label finishedProcessingParameter = generator.DefineLabel();
  95. // check if parameter is not null
  96. generator.Emit(OpCodes.Brtrue_S, skipSettingDefault);
  97. // parameter has no value, initialize to default
  98. generator.Emit(OpCodes.Ldloca_S, localVariable);
  99. generator.Emit(OpCodes.Initobj, parameterType);
  100. generator.Emit(OpCodes.Br_S, finishedProcessingParameter);
  101. // parameter has value, get value from array again and unbox and set to variable
  102. generator.MarkLabel(skipSettingDefault);
  103. generator.PushArrayInstance(argsIndex, i);
  104. generator.UnboxIfNeeded(parameterType);
  105. generator.Emit(OpCodes.Stloc_S, localVariable);
  106. // parameter finished, we out!
  107. generator.MarkLabel(finishedProcessingParameter);
  108. }
  109. else
  110. {
  111. generator.UnboxIfNeeded(parameterType);
  112. generator.Emit(OpCodes.Stloc_S, localVariable);
  113. }
  114. }
  115. generator.Emit(OpCodes.Ldloca_S, localVariable);
  116. }
  117. else if (parameterType.IsValueType())
  118. {
  119. generator.PushArrayInstance(argsIndex, i);
  120. generator.Emit(OpCodes.Stloc_S, localObject);
  121. // have to check that value type parameters aren't null
  122. // otherwise they will error when unboxed
  123. Label skipSettingDefault = generator.DefineLabel();
  124. Label finishedProcessingParameter = generator.DefineLabel();
  125. // check if parameter is not null
  126. generator.Emit(OpCodes.Ldloc_S, localObject);
  127. generator.Emit(OpCodes.Brtrue_S, skipSettingDefault);
  128. // parameter has no value, initialize to default
  129. LocalBuilder localVariable = generator.DeclareLocal(parameterType);
  130. generator.Emit(OpCodes.Ldloca_S, localVariable);
  131. generator.Emit(OpCodes.Initobj, parameterType);
  132. generator.Emit(OpCodes.Ldloc_S, localVariable);
  133. generator.Emit(OpCodes.Br_S, finishedProcessingParameter);
  134. // argument has value, try to convert it to parameter type
  135. generator.MarkLabel(skipSettingDefault);
  136. if (parameterType.IsPrimitive())
  137. {
  138. // for primitive types we need to handle type widening (e.g. short -> int)
  139. MethodInfo toParameterTypeMethod = typeof(IConvertible)
  140. .GetMethod("To" + parameterType.Name, new[] { typeof(IFormatProvider) });
  141. if (toParameterTypeMethod != null)
  142. {
  143. Label skipConvertible = generator.DefineLabel();
  144. // check if argument type is an exact match for parameter type
  145. // in this case we may use cheap unboxing instead
  146. generator.Emit(OpCodes.Ldloc_S, localObject);
  147. generator.Emit(OpCodes.Isinst, parameterType);
  148. generator.Emit(OpCodes.Brtrue_S, skipConvertible);
  149. // types don't match, check if argument implements IConvertible
  150. generator.Emit(OpCodes.Ldloc_S, localObject);
  151. generator.Emit(OpCodes.Isinst, typeof(IConvertible));
  152. generator.Emit(OpCodes.Stloc_S, localConvertible);
  153. generator.Emit(OpCodes.Ldloc_S, localConvertible);
  154. generator.Emit(OpCodes.Brfalse_S, skipConvertible);
  155. // convert argument to parameter type
  156. generator.Emit(OpCodes.Ldloc_S, localConvertible);
  157. generator.Emit(OpCodes.Ldnull);
  158. generator.Emit(OpCodes.Callvirt, toParameterTypeMethod);
  159. generator.Emit(OpCodes.Br_S, finishedProcessingParameter);
  160. generator.MarkLabel(skipConvertible);
  161. }
  162. }
  163. // we got here because either argument type matches parameter (conversion will succeed),
  164. // or argument type doesn't match parameter, but we're out of options (conversion will fail)
  165. generator.Emit(OpCodes.Ldloc_S, localObject);
  166. generator.UnboxIfNeeded(parameterType);
  167. // parameter finished, we out!
  168. generator.MarkLabel(finishedProcessingParameter);
  169. }
  170. else
  171. {
  172. generator.PushArrayInstance(argsIndex, i);
  173. generator.UnboxIfNeeded(parameterType);
  174. }
  175. }
  176. if (method.IsConstructor)
  177. {
  178. generator.Emit(OpCodes.Newobj, (ConstructorInfo)method);
  179. }
  180. else
  181. {
  182. generator.CallMethod((MethodInfo)method);
  183. }
  184. Type returnType = method.IsConstructor
  185. ? method.DeclaringType
  186. : ((MethodInfo)method).ReturnType;
  187. if (returnType != typeof(void))
  188. {
  189. generator.BoxIfNeeded(returnType);
  190. }
  191. else
  192. {
  193. generator.Emit(OpCodes.Ldnull);
  194. }
  195. generator.Return();
  196. }
  197. public override Func<T> CreateDefaultConstructor<T>(Type type)
  198. {
  199. DynamicMethod dynamicMethod = CreateDynamicMethod("Create" + type.FullName, typeof(T), ReflectionUtils.EmptyTypes, type);
  200. dynamicMethod.InitLocals = true;
  201. ILGenerator generator = dynamicMethod.GetILGenerator();
  202. GenerateCreateDefaultConstructorIL(type, generator, typeof(T));
  203. return (Func<T>)dynamicMethod.CreateDelegate(typeof(Func<T>));
  204. }
  205. private void GenerateCreateDefaultConstructorIL(Type type, ILGenerator generator, Type delegateType)
  206. {
  207. if (type.IsValueType())
  208. {
  209. generator.DeclareLocal(type);
  210. generator.Emit(OpCodes.Ldloc_0);
  211. // only need to box if the delegate isn't returning the value type
  212. if (type != delegateType)
  213. {
  214. generator.Emit(OpCodes.Box, type);
  215. }
  216. }
  217. else
  218. {
  219. ConstructorInfo constructorInfo =
  220. type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, ReflectionUtils.EmptyTypes, null);
  221. if (constructorInfo == null)
  222. {
  223. throw new ArgumentException("Could not get constructor for {0}.".FormatWith(CultureInfo.InvariantCulture, type));
  224. }
  225. generator.Emit(OpCodes.Newobj, constructorInfo);
  226. }
  227. generator.Return();
  228. }
  229. public override Func<T, object> CreateGet<T>(PropertyInfo propertyInfo)
  230. {
  231. DynamicMethod dynamicMethod = CreateDynamicMethod("Get" + propertyInfo.Name, typeof(object), new[] { typeof(T) }, propertyInfo.DeclaringType);
  232. ILGenerator generator = dynamicMethod.GetILGenerator();
  233. GenerateCreateGetPropertyIL(propertyInfo, generator);
  234. return (Func<T, object>)dynamicMethod.CreateDelegate(typeof(Func<T, object>));
  235. }
  236. private void GenerateCreateGetPropertyIL(PropertyInfo propertyInfo, ILGenerator generator)
  237. {
  238. MethodInfo getMethod = propertyInfo.GetGetMethod(true);
  239. if (getMethod == null)
  240. {
  241. throw new ArgumentException("Property '{0}' does not have a getter.".FormatWith(CultureInfo.InvariantCulture, propertyInfo.Name));
  242. }
  243. if (!getMethod.IsStatic)
  244. {
  245. generator.PushInstance(propertyInfo.DeclaringType);
  246. }
  247. generator.CallMethod(getMethod);
  248. generator.BoxIfNeeded(propertyInfo.PropertyType);
  249. generator.Return();
  250. }
  251. public override Func<T, object> CreateGet<T>(FieldInfo fieldInfo)
  252. {
  253. if (fieldInfo.IsLiteral)
  254. {
  255. object constantValue = fieldInfo.GetValue(null);
  256. Func<T, object> getter = o => constantValue;
  257. return getter;
  258. }
  259. DynamicMethod dynamicMethod = CreateDynamicMethod("Get" + fieldInfo.Name, typeof(T), new[] { typeof(object) }, fieldInfo.DeclaringType);
  260. ILGenerator generator = dynamicMethod.GetILGenerator();
  261. GenerateCreateGetFieldIL(fieldInfo, generator);
  262. return (Func<T, object>)dynamicMethod.CreateDelegate(typeof(Func<T, object>));
  263. }
  264. private void GenerateCreateGetFieldIL(FieldInfo fieldInfo, ILGenerator generator)
  265. {
  266. if (!fieldInfo.IsStatic)
  267. {
  268. generator.PushInstance(fieldInfo.DeclaringType);
  269. generator.Emit(OpCodes.Ldfld, fieldInfo);
  270. }
  271. else
  272. {
  273. generator.Emit(OpCodes.Ldsfld, fieldInfo);
  274. }
  275. generator.BoxIfNeeded(fieldInfo.FieldType);
  276. generator.Return();
  277. }
  278. public override Action<T, object> CreateSet<T>(FieldInfo fieldInfo)
  279. {
  280. DynamicMethod dynamicMethod = CreateDynamicMethod("Set" + fieldInfo.Name, null, new[] { typeof(T), typeof(object) }, fieldInfo.DeclaringType);
  281. ILGenerator generator = dynamicMethod.GetILGenerator();
  282. GenerateCreateSetFieldIL(fieldInfo, generator);
  283. return (Action<T, object>)dynamicMethod.CreateDelegate(typeof(Action<T, object>));
  284. }
  285. internal static void GenerateCreateSetFieldIL(FieldInfo fieldInfo, ILGenerator generator)
  286. {
  287. if (!fieldInfo.IsStatic)
  288. {
  289. generator.PushInstance(fieldInfo.DeclaringType);
  290. }
  291. generator.Emit(OpCodes.Ldarg_1);
  292. generator.UnboxIfNeeded(fieldInfo.FieldType);
  293. if (!fieldInfo.IsStatic)
  294. {
  295. generator.Emit(OpCodes.Stfld, fieldInfo);
  296. }
  297. else
  298. {
  299. generator.Emit(OpCodes.Stsfld, fieldInfo);
  300. }
  301. generator.Return();
  302. }
  303. public override Action<T, object> CreateSet<T>(PropertyInfo propertyInfo)
  304. {
  305. DynamicMethod dynamicMethod = CreateDynamicMethod("Set" + propertyInfo.Name, null, new[] { typeof(T), typeof(object) }, propertyInfo.DeclaringType);
  306. ILGenerator generator = dynamicMethod.GetILGenerator();
  307. GenerateCreateSetPropertyIL(propertyInfo, generator);
  308. return (Action<T, object>)dynamicMethod.CreateDelegate(typeof(Action<T, object>));
  309. }
  310. internal static void GenerateCreateSetPropertyIL(PropertyInfo propertyInfo, ILGenerator generator)
  311. {
  312. MethodInfo setMethod = propertyInfo.GetSetMethod(true);
  313. if (!setMethod.IsStatic)
  314. {
  315. generator.PushInstance(propertyInfo.DeclaringType);
  316. }
  317. generator.Emit(OpCodes.Ldarg_1);
  318. generator.UnboxIfNeeded(propertyInfo.PropertyType);
  319. generator.CallMethod(setMethod);
  320. generator.Return();
  321. }
  322. }
  323. }
  324. #endif