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.

367 lines
14 KiB

2 years ago
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Reflection;
  4. using System.Reflection.Emit;
  5. namespace EasyNet.Common
  6. {
  7. /// <summary>
  8. /// Delegate for calling a method that is not known at runtime.
  9. /// </summary>
  10. /// <param name="target">the object to be called or null if the call is to a static method.</param>
  11. /// <param name="paramters">the parameters to the method.</param>
  12. /// <returns>the return value for the method or null if it doesn't return anything.</returns>
  13. public delegate object FastInvokeHandler(object target, object[] parameters);
  14. /// <summary>
  15. /// Delegate for creating and object at runtime using the default constructor.
  16. /// </summary>
  17. /// <returns>the newly created object.</returns>
  18. public delegate object FastCreateInstanceHandler();
  19. /// <summary>
  20. /// Delegate to get an arbitraty property at runtime.
  21. /// </summary>
  22. /// <param name="target">the object instance whose property will be obtained.</param>
  23. /// <returns>the property value.</returns>
  24. public delegate object FastPropertyGetHandler(object target);
  25. /// <summary>
  26. /// Delegate to set an arbitrary property at runtime.
  27. /// </summary>
  28. /// <param name="target">the object instance whose property will be modified.</param>
  29. /// <param name="parameter"></param>
  30. public delegate void FastPropertySetHandler(object target, object parameter);
  31. /// <summary>
  32. /// Class with helper methods for dynamic invocation generating IL on the fly.
  33. /// </summary>
  34. public static class DynamicCalls
  35. {
  36. /// <summary>
  37. /// 用於存放GetMethodInvoker的Dictionary
  38. /// </summary>
  39. private static Dictionary<MethodInfo, FastInvokeHandler> dictInvoker = new Dictionary<MethodInfo, FastInvokeHandler>();
  40. public static FastInvokeHandler GetMethodInvoker(MethodInfo methodInfo)
  41. {
  42. lock (dictInvoker)
  43. {
  44. if (dictInvoker.ContainsKey(methodInfo)) return (FastInvokeHandler)dictInvoker[methodInfo];
  45. // generates a dynamic method to generate a FastInvokeHandler delegate
  46. var dynamicMethod = new DynamicMethod(string.Empty, typeof(object), new Type[] { typeof(object), typeof(object[]) }, methodInfo.DeclaringType.Module);
  47. var ilGenerator = dynamicMethod.GetILGenerator();
  48. var parameters = methodInfo.GetParameters();
  49. var paramTypes = new Type[parameters.Length];
  50. // copies the parameter types to an array
  51. for (int i = 0; i < paramTypes.Length; i++)
  52. {
  53. if (parameters[i].ParameterType.IsByRef)
  54. paramTypes[i] = parameters[i].ParameterType.GetElementType();
  55. else
  56. paramTypes[i] = parameters[i].ParameterType;
  57. }
  58. var locals = new LocalBuilder[paramTypes.Length];
  59. // generates a local variable for each parameter
  60. for (int i = 0; i < paramTypes.Length; i++)
  61. {
  62. locals[i] = ilGenerator.DeclareLocal(paramTypes[i], true);
  63. }
  64. // creates code to copy the parameters to the local variables
  65. for (int i = 0; i < paramTypes.Length; i++)
  66. {
  67. ilGenerator.Emit(OpCodes.Ldarg_1);
  68. EmitFastInt(ilGenerator, i);
  69. ilGenerator.Emit(OpCodes.Ldelem_Ref);
  70. EmitCastToReference(ilGenerator, paramTypes[i]);
  71. ilGenerator.Emit(OpCodes.Stloc, locals[i]);
  72. }
  73. if (!methodInfo.IsStatic)
  74. {
  75. // loads the object into the stack
  76. ilGenerator.Emit(OpCodes.Ldarg_0);
  77. }
  78. // loads the parameters copied to the local variables into the stack
  79. for (int i = 0; i < paramTypes.Length; i++)
  80. {
  81. if (parameters[i].ParameterType.IsByRef)
  82. ilGenerator.Emit(OpCodes.Ldloca_S, locals[i]);
  83. else
  84. ilGenerator.Emit(OpCodes.Ldloc, locals[i]);
  85. }
  86. // calls the method
  87. if (!methodInfo.IsStatic)
  88. {
  89. ilGenerator.EmitCall(OpCodes.Callvirt, methodInfo, null);
  90. }
  91. else
  92. {
  93. ilGenerator.EmitCall(OpCodes.Call, methodInfo, null);
  94. }
  95. // creates code for handling the return value
  96. if (methodInfo.ReturnType == typeof(void))
  97. {
  98. ilGenerator.Emit(OpCodes.Ldnull);
  99. }
  100. else
  101. {
  102. EmitBoxIfNeeded(ilGenerator, methodInfo.ReturnType);
  103. }
  104. // iterates through the parameters updating the parameters passed by ref
  105. for (int i = 0; i < paramTypes.Length; i++)
  106. {
  107. if (parameters[i].ParameterType.IsByRef)
  108. {
  109. ilGenerator.Emit(OpCodes.Ldarg_1);
  110. EmitFastInt(ilGenerator, i);
  111. ilGenerator.Emit(OpCodes.Ldloc, locals[i]);
  112. if (locals[i].LocalType.IsValueType)
  113. ilGenerator.Emit(OpCodes.Box, locals[i].LocalType);
  114. ilGenerator.Emit(OpCodes.Stelem_Ref);
  115. }
  116. }
  117. // returns the value to the caller
  118. ilGenerator.Emit(OpCodes.Ret);
  119. // converts the DynamicMethod to a FastInvokeHandler delegate to call to the method
  120. var invoker = (FastInvokeHandler)dynamicMethod.CreateDelegate(typeof(FastInvokeHandler));
  121. dictInvoker.Add(methodInfo, invoker);
  122. return invoker;
  123. }
  124. }
  125. /// <summary>
  126. /// 用於存放GetInstanceCreator的Dictionary
  127. /// </summary>
  128. private static Dictionary<Type, FastCreateInstanceHandler> dictCreator = new Dictionary<Type, FastCreateInstanceHandler>();
  129. /// <summary>
  130. /// Gets the instance creator delegate that can be use to create instances of the specified type.
  131. /// </summary>
  132. /// <param name="type">The type of the objects we want to create.</param>
  133. /// <returns>A delegate that can be used to create the objects.</returns>
  134. public static FastCreateInstanceHandler GetInstanceCreator(Type type)
  135. {
  136. lock (dictCreator)
  137. {
  138. if (dictCreator.ContainsKey(type)) return (FastCreateInstanceHandler)dictCreator[type];
  139. // generates a dynamic method to generate a FastCreateInstanceHandler delegate
  140. var dynamicMethod = new DynamicMethod(string.Empty, type, new Type[0], typeof(DynamicCalls).Module);
  141. var ilGenerator = dynamicMethod.GetILGenerator();
  142. // generates code to create a new object of the specified type using the default constructor
  143. ilGenerator.Emit(OpCodes.Newobj, type.GetConstructor(Type.EmptyTypes));
  144. // returns the value to the caller
  145. ilGenerator.Emit(OpCodes.Ret);
  146. // converts the DynamicMethod to a FastCreateInstanceHandler delegate to create the object
  147. var creator = (FastCreateInstanceHandler)dynamicMethod.CreateDelegate(typeof(FastCreateInstanceHandler));
  148. dictCreator.Add(type, creator);
  149. return creator;
  150. }
  151. }
  152. /// <summary>
  153. /// 用於存放GetPropertyGetter的Dictionary
  154. /// </summary>
  155. private static Dictionary<PropertyInfo, FastPropertyGetHandler> dictGetter = new Dictionary<PropertyInfo, FastPropertyGetHandler>();
  156. public static FastPropertyGetHandler GetPropertyGetter(PropertyInfo propInfo)
  157. {
  158. lock (dictGetter)
  159. {
  160. if (dictGetter.ContainsKey(propInfo)) return (FastPropertyGetHandler)dictGetter[propInfo];
  161. // generates a dynamic method to generate a FastPropertyGetHandler delegate
  162. var dynamicMethod = new DynamicMethod(string.Empty, typeof(object), new Type[] { typeof(object) }, propInfo.DeclaringType.Module);
  163. var ilGenerator = dynamicMethod.GetILGenerator();
  164. // loads the object into the stack
  165. ilGenerator.Emit(OpCodes.Ldarg_0);
  166. // calls the getter
  167. ilGenerator.EmitCall(OpCodes.Callvirt, propInfo.GetGetMethod(), null);
  168. // creates code for handling the return value
  169. EmitBoxIfNeeded(ilGenerator, propInfo.PropertyType);
  170. // returns the value to the caller
  171. ilGenerator.Emit(OpCodes.Ret);
  172. // converts the DynamicMethod to a FastPropertyGetHandler delegate to get the property
  173. var getter = (FastPropertyGetHandler)dynamicMethod.CreateDelegate(typeof(FastPropertyGetHandler));
  174. dictGetter.Add(propInfo, getter);
  175. return getter;
  176. }
  177. }
  178. /// <summary>
  179. /// 用於存放SetPropertySetter的Dictionary
  180. /// </summary>
  181. private static Dictionary<PropertyInfo, FastPropertySetHandler> dictSetter = new Dictionary<PropertyInfo, FastPropertySetHandler>();
  182. public static FastPropertySetHandler GetPropertySetter(PropertyInfo propInfo)
  183. {
  184. lock (dictSetter)
  185. {
  186. if (dictSetter.ContainsKey(propInfo)) return (FastPropertySetHandler)dictSetter[propInfo];
  187. // generates a dynamic method to generate a FastPropertySetHandler delegate
  188. var dynamicMethod = new DynamicMethod(string.Empty, null, new Type[] { typeof(object), typeof(object) }, propInfo.DeclaringType.Module);
  189. var ilGenerator = dynamicMethod.GetILGenerator();
  190. // loads the object into the stack
  191. ilGenerator.Emit(OpCodes.Ldarg_0);
  192. // loads the parameter from the stack
  193. ilGenerator.Emit(OpCodes.Ldarg_1);
  194. // cast to the proper type (unboxing if needed)
  195. EmitCastToReference(ilGenerator, propInfo.PropertyType);
  196. // calls the setter
  197. ilGenerator.EmitCall(OpCodes.Callvirt, propInfo.GetSetMethod(), null);
  198. // terminates the call
  199. ilGenerator.Emit(OpCodes.Ret);
  200. // converts the DynamicMethod to a FastPropertyGetHandler delegate to get the property
  201. var setter = (FastPropertySetHandler)dynamicMethod.CreateDelegate(typeof(FastPropertySetHandler));
  202. dictSetter.Add(propInfo, setter);
  203. return setter;
  204. }
  205. }
  206. /// <summary>
  207. /// Emits the cast to a reference, unboxing if needed.
  208. /// </summary>
  209. /// <param name="type">The type to cast.</param>
  210. /// <param name="ilGenerator">todo: describe ilGenerator parameter on EmitCastToReference</param>
  211. private static void EmitCastToReference(ILGenerator ilGenerator, System.Type type)
  212. {
  213. if (type.IsValueType)
  214. {
  215. ilGenerator.Emit(OpCodes.Unbox_Any, type);
  216. }
  217. else
  218. {
  219. ilGenerator.Emit(OpCodes.Castclass, type);
  220. }
  221. }
  222. /// <summary>
  223. /// Boxes a type if needed.
  224. /// </summary>
  225. /// <param name="ilGenerator">The MSIL generator.</param>
  226. /// <param name="type">The type.</param>
  227. private static void EmitBoxIfNeeded(ILGenerator ilGenerator, System.Type type)
  228. {
  229. if (type.IsValueType)
  230. {
  231. ilGenerator.Emit(OpCodes.Box, type);
  232. }
  233. }
  234. /// <summary>
  235. /// Emits code to save an integer to the evaluation stack.
  236. /// </summary>
  237. /// <param name="value">The value to push.</param>
  238. /// <param name="ilGenerator">todo: describe ilGenerator parameter on EmitFastInt</param>
  239. private static void EmitFastInt(ILGenerator ilGenerator, int value)
  240. {
  241. // for small integers, emit the proper opcode
  242. switch (value)
  243. {
  244. case -1:
  245. {
  246. ilGenerator.Emit(OpCodes.Ldc_I4_M1);
  247. return;
  248. }
  249. case 0:
  250. {
  251. ilGenerator.Emit(OpCodes.Ldc_I4_0);
  252. return;
  253. }
  254. case 1:
  255. {
  256. ilGenerator.Emit(OpCodes.Ldc_I4_1);
  257. return;
  258. }
  259. case 2:
  260. {
  261. ilGenerator.Emit(OpCodes.Ldc_I4_2);
  262. return;
  263. }
  264. case 3:
  265. {
  266. ilGenerator.Emit(OpCodes.Ldc_I4_3);
  267. return;
  268. }
  269. case 4:
  270. {
  271. ilGenerator.Emit(OpCodes.Ldc_I4_4);
  272. return;
  273. }
  274. case 5:
  275. {
  276. ilGenerator.Emit(OpCodes.Ldc_I4_5);
  277. return;
  278. }
  279. case 6:
  280. {
  281. ilGenerator.Emit(OpCodes.Ldc_I4_6);
  282. return;
  283. }
  284. case 7:
  285. {
  286. ilGenerator.Emit(OpCodes.Ldc_I4_7);
  287. return;
  288. }
  289. case 8:
  290. {
  291. ilGenerator.Emit(OpCodes.Ldc_I4_8);
  292. return;
  293. }
  294. default:
  295. break;
  296. }
  297. // for bigger values emit the short or long opcode
  298. if (value > -129 && value < 128)
  299. {
  300. ilGenerator.Emit(OpCodes.Ldc_I4_S, (SByte)value);
  301. }
  302. else
  303. {
  304. ilGenerator.Emit(OpCodes.Ldc_I4, value);
  305. }
  306. }
  307. }
  308. }