PageRenderTime 32ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/src/LinFu.Reflection.Emit/CilWorkerExtensions.cs

http://github.com/philiplaureano/LinFu
C# | 363 lines | 203 code | 47 blank | 113 comment | 14 complexity | a0a94936879ce93cc79a130c99709ba4 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Reflection;
  5. using Mono.Cecil;
  6. using Mono.Cecil.Cil;
  7. namespace LinFu.Reflection.Emit
  8. {
  9. /// <summary>
  10. /// A class that extends the <see cref="ILProcessor" /> class
  11. /// with helper methods that make it easier to save
  12. /// information about the method currently being implemented.
  13. /// </summary>
  14. public static class ILProcessorExtensions
  15. {
  16. private static readonly Dictionary<string, OpCode> stindMap = new Dictionary<string, OpCode>();
  17. static ILProcessorExtensions()
  18. {
  19. stindMap["Bool&"] = OpCodes.Stind_I1;
  20. stindMap["Int8&"] = OpCodes.Stind_I1;
  21. stindMap["Uint8&"] = OpCodes.Stind_I1;
  22. stindMap["Int16&"] = OpCodes.Stind_I2;
  23. stindMap["Uint16&"] = OpCodes.Stind_I2;
  24. stindMap["Uint32&"] = OpCodes.Stind_I4;
  25. stindMap["Int32&"] = OpCodes.Stind_I4;
  26. stindMap["IntPtr"] = OpCodes.Stind_I4;
  27. stindMap["Uint64&"] = OpCodes.Stind_I8;
  28. stindMap["Int64&"] = OpCodes.Stind_I8;
  29. stindMap["Float32&"] = OpCodes.Stind_R4;
  30. stindMap["Float64&"] = OpCodes.Stind_R8;
  31. }
  32. /// <summary>
  33. /// Emits a Console.WriteLine call to using the current ILProcessor that will only be called if the contents
  34. /// of the target variable are null at runtime.
  35. /// </summary>
  36. /// <param name="IL">The target ILProcessor.</param>
  37. /// <param name="text">The text that will be written to the console.</param>
  38. /// <param name="targetVariable">The target variable that will be checked for null at runtime.</param>
  39. public static void EmitWriteLineIfNull(this ILProcessor IL, string text, VariableDefinition targetVariable)
  40. {
  41. var skipWrite = IL.Create(OpCodes.Nop);
  42. IL.Emit(OpCodes.Ldloc, targetVariable);
  43. IL.Emit(OpCodes.Brtrue, skipWrite);
  44. IL.EmitWriteLine(text);
  45. IL.Append(skipWrite);
  46. }
  47. /// <summary>
  48. /// Emits a Console.WriteLine call using the current ILProcessor.
  49. /// </summary>
  50. /// <param name="IL">The target ILProcessor.</param>
  51. /// <param name="text">The text that will be written to the console.</param>
  52. public static void EmitWriteLine(this ILProcessor IL, string text)
  53. {
  54. var body = IL.GetBody();
  55. var method = body.Method;
  56. var declaringType = method.DeclaringType;
  57. var module = declaringType.Module;
  58. var writeLineMethod = typeof(Console).GetMethod("WriteLine",
  59. BindingFlags.Public | BindingFlags.Static, null,
  60. new[] {typeof(string)}, null);
  61. IL.Emit(OpCodes.Ldstr, text);
  62. IL.Emit(OpCodes.Call, module.Import(writeLineMethod));
  63. }
  64. /// <summary>
  65. /// Pushes the current <paramref name="method" /> onto the stack.
  66. /// </summary>
  67. /// <param name="IL">The <see cref="ILProcessor" /> that will be used to create the instructions.</param>
  68. /// <param name="method">The method that represents the <see cref="MethodInfo" /> that will be pushed onto the stack.</param>
  69. /// <param name="module">The module that contains the host method.</param>
  70. public static void PushMethod(this ILProcessor IL, MethodReference method, ModuleDefinition module)
  71. {
  72. var getMethodFromHandle = module.ImportMethod<MethodBase>("GetMethodFromHandle",
  73. typeof(RuntimeMethodHandle),
  74. typeof(RuntimeTypeHandle));
  75. var declaringType = method.DeclaringType;
  76. // Instantiate the generic type before determining
  77. // the current method
  78. if (declaringType.GenericParameters.Count > 0)
  79. {
  80. var genericType = new GenericInstanceType(declaringType);
  81. foreach (GenericParameter parameter in declaringType.GenericParameters)
  82. genericType.GenericArguments.Add(parameter);
  83. declaringType = genericType;
  84. }
  85. IL.Emit(OpCodes.Ldtoken, method);
  86. IL.Emit(OpCodes.Ldtoken, declaringType);
  87. IL.Emit(OpCodes.Call, getMethodFromHandle);
  88. }
  89. /// <summary>
  90. /// Gets the declaring type for the target method.
  91. /// </summary>
  92. /// <param name="method">The target method.</param>
  93. /// <returns>The declaring type.</returns>
  94. public static TypeReference GetDeclaringType(this MethodReference method)
  95. {
  96. var declaringType = method.DeclaringType;
  97. return GetDeclaringType(declaringType);
  98. }
  99. /// <summary>
  100. /// Pushes a <paramref name="Type" /> instance onto the stack.
  101. /// </summary>
  102. /// <param name="IL">The <see cref="ILProcessor" /> that will be used to create the instructions.</param>
  103. /// <param name="type">The type that represents the <see cref="Type" /> that will be pushed onto the stack.</param>
  104. /// <param name="module">The module that contains the host method.</param>
  105. public static void PushType(this ILProcessor IL, TypeReference type, ModuleDefinition module)
  106. {
  107. var getTypeFromHandle = module.ImportMethod<Type>("GetTypeFromHandle",
  108. typeof(RuntimeTypeHandle));
  109. // Instantiate the generic type before pushing it onto the stack
  110. var declaringType = GetDeclaringType(type);
  111. IL.Emit(OpCodes.Ldtoken, declaringType);
  112. IL.Emit(OpCodes.Call, getTypeFromHandle);
  113. }
  114. /// <summary>
  115. /// Pushes the current <paramref name="field" /> onto the stack.
  116. /// </summary>
  117. /// <param name="IL">The <see cref="ILProcessor" /> that will be used to create the instructions.</param>
  118. /// <param name="field">The field that represents the <see cref="FieldInfo" /> that will be pushed onto the stack.</param>
  119. /// <param name="module">The module that contains the target field.</param>
  120. public static void PushField(this ILProcessor IL, FieldReference field, ModuleDefinition module)
  121. {
  122. var getFieldFromHandle = module.ImportMethod<FieldInfo>("GetFieldFromHandle",
  123. typeof(RuntimeFieldHandle),
  124. typeof(RuntimeTypeHandle));
  125. var declaringType = GetDeclaringType(field.DeclaringType);
  126. IL.Emit(OpCodes.Ldtoken, field);
  127. IL.Emit(OpCodes.Ldtoken, declaringType);
  128. IL.Emit(OpCodes.Call, getFieldFromHandle);
  129. }
  130. /// <summary>
  131. /// Pushes the arguments of a method onto the stack.
  132. /// </summary>
  133. /// <param name="IL">The <see cref="ILProcessor" /> that will be used to create the instructions.</param>
  134. /// <param name="module">The module that contains the host method.</param>
  135. /// <param name="method">The target method.</param>
  136. /// <param name="arguments">The <see cref="VariableDefinition">local variable</see> that will hold the array of arguments.</param>
  137. public static void PushArguments(this ILProcessor IL, IMethodSignature method, ModuleDefinition module,
  138. VariableDefinition arguments)
  139. {
  140. var objectType = module.ImportType(typeof(object));
  141. var parameterCount = method.Parameters.Count;
  142. IL.Emit(OpCodes.Ldc_I4, parameterCount);
  143. IL.Emit(OpCodes.Newarr, objectType);
  144. IL.Emit(OpCodes.Stloc, arguments);
  145. if (parameterCount == 0)
  146. return;
  147. var index = 0;
  148. foreach (ParameterDefinition param in method.Parameters) IL.PushParameter(index++, arguments, param);
  149. }
  150. /// <summary>
  151. /// Pushes the stack trace of the currently executing method onto the stack.
  152. /// </summary>
  153. /// <param name="IL">The <see cref="ILProcessor" /> that will be used to create the instructions.</param>
  154. /// <param name="module">The module that contains the host method.</param>
  155. public static void PushStackTrace(this ILProcessor IL, ModuleDefinition module)
  156. {
  157. var stackTraceConstructor =
  158. typeof(StackTrace).GetConstructor(new[] {typeof(int), typeof(bool)});
  159. var stackTraceCtor = module.Import(stackTraceConstructor);
  160. var addDebugSymbols = OpCodes.Ldc_I4_0;
  161. IL.Emit(OpCodes.Ldc_I4_1);
  162. IL.Emit(addDebugSymbols);
  163. IL.Emit(OpCodes.Newobj, stackTraceCtor);
  164. }
  165. /// <summary>
  166. /// Saves the generic type arguments that were used to construct the method.
  167. /// </summary>
  168. /// <param name="IL">The <see cref="ILProcessor" /> that will be used to create the instructions.</param>
  169. /// <param name="method">
  170. /// The target method whose generic type arguments (if any) will be saved into the
  171. /// <paramref name="typeArguments">local variable</paramref>.
  172. /// </param>
  173. /// <param name="module">The module that contains the host method.</param>
  174. /// <param name="typeArguments">The local variable that will store the resulting array of <see cref="Type" /> objects.</param>
  175. public static void PushGenericArguments(this ILProcessor IL, IGenericParameterProvider method,
  176. ModuleDefinition module, VariableDefinition typeArguments)
  177. {
  178. var getTypeFromHandle = module.ImportMethod<Type>("GetTypeFromHandle",
  179. BindingFlags.Public | BindingFlags.Static);
  180. var genericParameterCount = method.GenericParameters.Count;
  181. var genericParameters = method.GenericParameters;
  182. for (var index = 0; index < genericParameterCount; index++)
  183. {
  184. var current = genericParameters[index];
  185. IL.Emit(OpCodes.Ldloc, typeArguments);
  186. IL.Emit(OpCodes.Ldc_I4, index);
  187. IL.Emit(OpCodes.Ldtoken, current);
  188. IL.Emit(OpCodes.Call, getTypeFromHandle);
  189. IL.Emit(OpCodes.Stelem_Ref);
  190. }
  191. }
  192. /// <summary>
  193. /// Saves the current method signature of a method into an array
  194. /// of <see cref="System.Type" /> objects. This can be used to determine the
  195. /// signature of methods with generic type parameters or methods that have
  196. /// parameters that are generic parameters specified by the type itself.
  197. /// </summary>
  198. /// <param name="IL">The <see cref="ILProcessor" /> that will be used to create the instructions.</param>
  199. /// <param name="method">The target method whose generic type arguments (if any) will be saved into the local variable .</param>
  200. /// <param name="module">The module that contains the host method.</param>
  201. /// <param name="parameterTypes">The local variable that will store the current method signature.</param>
  202. public static void SaveParameterTypes(this ILProcessor IL, MethodReference method, ModuleDefinition module,
  203. VariableDefinition parameterTypes)
  204. {
  205. var getTypeFromHandle = module.ImportMethod<Type>("GetTypeFromHandle",
  206. BindingFlags.Public | BindingFlags.Static);
  207. var parameterCount = method.Parameters.Count;
  208. for (var index = 0; index < parameterCount; index++)
  209. {
  210. var current = method.Parameters[index];
  211. IL.Emit(OpCodes.Ldloc, parameterTypes);
  212. IL.Emit(OpCodes.Ldc_I4, index);
  213. IL.Emit(OpCodes.Ldtoken, current.ParameterType);
  214. IL.Emit(OpCodes.Call, getTypeFromHandle);
  215. IL.Emit(OpCodes.Stelem_Ref);
  216. }
  217. }
  218. /// <summary>
  219. /// Converts the return value of a method into the <paramref name="returnType">target type</paramref>.
  220. /// If the target type is void, then the value will simply be popped from the stack.
  221. /// </summary>
  222. /// <param name="IL">The <see cref="ILProcessor" /> that will be used to create the instructions.</param>
  223. /// <param name="module">The module that contains the host method.</param>
  224. /// <param name="returnType">The method return type itself.</param>
  225. public static void PackageReturnValue(this ILProcessor IL, ModuleDefinition module, TypeReference returnType)
  226. {
  227. var voidType = module.ImportType(typeof(void));
  228. if (returnType == voidType)
  229. {
  230. IL.Emit(OpCodes.Pop);
  231. return;
  232. }
  233. IL.Emit(OpCodes.Unbox_Any, returnType);
  234. }
  235. /// <summary>
  236. /// Emits the proper Stind (store indirect) IL instruction for the <paramref name="currentType" />.
  237. /// </summary>
  238. /// <param name="IL">The target <see cref="ILProcessor" /> that will emit the IL.</param>
  239. /// <param name="currentType">The type of data being stored.</param>
  240. public static void Stind(this ILProcessor IL, TypeReference currentType)
  241. {
  242. var typeName = currentType.Name;
  243. var opCode = OpCodes.Nop;
  244. if (!currentType.IsValueType && !typeName.EndsWith("&"))
  245. opCode = OpCodes.Stind_Ref;
  246. opCode = !stindMap.ContainsKey(typeName) ? OpCodes.Stind_Ref : stindMap[typeName];
  247. IL.Emit(opCode);
  248. }
  249. /// <summary>
  250. /// Stores the <paramref name="param">current parameter value</paramref>
  251. /// into the array of method <paramref name="arguments" />.
  252. /// </summary>
  253. /// <param name="IL">The <see cref="ILProcessor" /> that will be used to create the instructions.</param>
  254. /// <param name="arguments">The local variable that will store the method arguments.</param>
  255. /// <param name="index">The array index that indicates where the parameter value will be stored in the array of arguments.</param>
  256. /// <param name="param">The current argument value being stored.</param>
  257. private static void PushParameter(this ILProcessor IL, int index, VariableDefinition arguments,
  258. ParameterDefinition param)
  259. {
  260. var parameterType = param.ParameterType;
  261. IL.Emit(OpCodes.Ldloc, arguments);
  262. IL.Emit(OpCodes.Ldc_I4, index);
  263. // Zero out the [out] parameters
  264. if (param.IsOut || param.IsByRef())
  265. {
  266. IL.Emit(OpCodes.Ldnull);
  267. IL.Emit(OpCodes.Stelem_Ref);
  268. return;
  269. }
  270. IL.Emit(OpCodes.Ldarg, param);
  271. if (parameterType.IsValueType || parameterType is GenericParameter)
  272. IL.Emit(OpCodes.Box, param.ParameterType);
  273. IL.Emit(OpCodes.Stelem_Ref);
  274. }
  275. /// <summary>
  276. /// Obtains the method definition that contains the current <see cref="ILProcessor" />.
  277. /// </summary>
  278. /// <param name="IL">The <see cref="ILProcessor" /> responsible for the method body.</param>
  279. /// <returns>A method definition.</returns>
  280. public static MethodDefinition GetMethod(this ILProcessor IL)
  281. {
  282. var body = IL.GetBody();
  283. var targetMethod = body.Method;
  284. return targetMethod;
  285. }
  286. /// <summary>
  287. /// Obtains the module that contains the current <see cref="ILProcessor" />.
  288. /// </summary>
  289. /// <param name="IL">The <see cref="ILProcessor" /> responsible for the method body.</param>
  290. /// <returns>The host module.</returns>
  291. public static ModuleDefinition GetModule(this ILProcessor IL)
  292. {
  293. var method = IL.GetMethod();
  294. var declaringType = method.DeclaringType;
  295. return declaringType.Module;
  296. }
  297. /// <summary>
  298. /// Obtains the declaring type for a given type reference.
  299. /// </summary>
  300. /// <param name="declaringType">The declaring ty pe.</param>
  301. /// <returns>The actual declaring type.</returns>
  302. private static TypeReference GetDeclaringType(TypeReference declaringType)
  303. {
  304. // Instantiate the generic type before determining
  305. // the current method
  306. if (declaringType.GenericParameters.Count > 0)
  307. {
  308. var genericType = new GenericInstanceType(declaringType);
  309. foreach (GenericParameter parameter in declaringType.GenericParameters)
  310. genericType.GenericArguments.Add(parameter);
  311. declaringType = genericType;
  312. }
  313. return declaringType;
  314. }
  315. }
  316. }