/src/LinFu.Reflection.Emit/TypeDefinitionExtensions.cs

http://github.com/philiplaureano/LinFu · C# · 320 lines · 156 code · 47 blank · 117 comment · 4 complexity · fcad3efb5c518cb32a0ac2860caaa5c3 MD5 · raw file

  1. using System;
  2. using System.Linq;
  3. using System.Reflection;
  4. using Mono.Cecil;
  5. using Mono.Cecil.Cil;
  6. using FieldAttributes = Mono.Cecil.FieldAttributes;
  7. using MethodAttributes = Mono.Cecil.MethodAttributes;
  8. using MethodImplAttributes = Mono.Cecil.MethodImplAttributes;
  9. using PropertyAttributes = Mono.Cecil.PropertyAttributes;
  10. namespace LinFu.Reflection.Emit
  11. {
  12. /// <summary>
  13. /// A class that extends the <see cref="TypeDefinition" />
  14. /// class with features similar to the features in the
  15. /// System.Reflection.Emit namespace.
  16. /// </summary>
  17. public static class TypeDefinitionExtensions
  18. {
  19. /// <summary>
  20. /// Adds a new method to the <paramref name="typeDef">target type</paramref>.
  21. /// </summary>
  22. /// <param name="typeDef">The type that will hold the newly-created method.</param>
  23. /// <param name="attributes">
  24. /// The <see cref="Mono.Cecil.MethodAttributes" /> parameter that describes the characteristics of
  25. /// the method.
  26. /// </param>
  27. /// <param name="methodName">The name to be given to the new method.</param>
  28. /// <param name="returnType">The method return type.</param>
  29. /// <param name="callingConvention">The calling convention of the method being created.</param>
  30. /// <param name="parameterTypes">The list of argument types that will be used to define the method signature.</param>
  31. /// <returns>A <see cref="MethodDefinition" /> instance that represents the newly-created method.</returns>
  32. public static MethodDefinition DefineMethod(this TypeDefinition typeDef, string methodName,
  33. MethodAttributes attributes,
  34. MethodCallingConvention callingConvention, Type returnType,
  35. params Type[] parameterTypes)
  36. {
  37. var method = new MethodDefinition(methodName, attributes, null)
  38. {
  39. CallingConvention = callingConvention
  40. };
  41. typeDef.Methods.Add(method);
  42. // Match the parameter types
  43. method.AddParameters(parameterTypes);
  44. // Match the return type
  45. method.SetReturnType(returnType);
  46. return method;
  47. }
  48. /// <summary>
  49. /// Adds a new method to the <paramref name="typeDef">target type</paramref>.
  50. /// </summary>
  51. /// <param name="typeDef">The type that will hold the newly-created method.</param>
  52. /// <param name="attributes">
  53. /// The <see cref="MethodAttributes" /> parameter that describes the characteristics of the
  54. /// method.
  55. /// </param>
  56. /// <param name="methodName">The name to be given to the new method.</param>
  57. /// <param name="returnType">The method return type.</param>
  58. /// <param name="parameterTypes">The list of argument types that will be used to define the method signature.</param>
  59. /// <param name="genericParameterTypes">
  60. /// The list of generic argument types that will be used to define the method
  61. /// signature.
  62. /// </param>
  63. /// <returns>A <see cref="MethodDefinition" /> instance that represents the newly-created method.</returns>
  64. public static MethodDefinition DefineMethod(this TypeDefinition typeDef, string methodName,
  65. MethodAttributes attributes, Type returnType, Type[] parameterTypes,
  66. Type[] genericParameterTypes)
  67. {
  68. var method = new MethodDefinition(methodName, attributes, null);
  69. typeDef.Methods.Add(method);
  70. //Match the generic parameter types
  71. foreach (var genericParameterType in genericParameterTypes)
  72. method.AddGenericParameter(genericParameterType);
  73. // Match the parameter types
  74. method.AddParameters(parameterTypes);
  75. // Match the return type
  76. method.SetReturnType(returnType);
  77. return method;
  78. }
  79. /// <summary>
  80. /// Adds a default constructor to the target type.
  81. /// </summary>
  82. /// <param name="targetType">The type that will contain the default constructor.</param>
  83. /// <returns>The default constructor.</returns>
  84. public static MethodDefinition AddDefaultConstructor(this TypeDefinition targetType)
  85. {
  86. var parentType = typeof(object);
  87. return AddDefaultConstructor(targetType, parentType);
  88. }
  89. /// <summary>
  90. /// Adds a default constructor to the target type.
  91. /// </summary>
  92. /// <param name="parentType">
  93. /// The base class that contains the default constructor that will be used for constructor
  94. /// chaining..
  95. /// </param>
  96. /// <param name="targetType">The type that will contain the default constructor.</param>
  97. /// <returns>The default constructor.</returns>
  98. public static MethodDefinition AddDefaultConstructor(this TypeDefinition targetType, Type parentType)
  99. {
  100. var module = targetType.Module;
  101. var voidType = module.Import(typeof(void));
  102. var methodAttributes = MethodAttributes.Public | MethodAttributes.HideBySig
  103. | MethodAttributes.SpecialName |
  104. MethodAttributes.RTSpecialName;
  105. var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
  106. var objectConstructor = parentType.GetConstructor(flags, null, new Type[0], null);
  107. // Revert to the System.Object constructor
  108. // if the parent type does not have a default constructor
  109. if (objectConstructor == null)
  110. objectConstructor = typeof(object).GetConstructor(new Type[0]);
  111. var baseConstructor = module.Import(objectConstructor);
  112. // Define the default constructor
  113. var ctor = new MethodDefinition(".ctor", methodAttributes, voidType)
  114. {
  115. CallingConvention = MethodCallingConvention.StdCall,
  116. ImplAttributes = MethodImplAttributes.IL | MethodImplAttributes.Managed
  117. };
  118. var IL = ctor.Body.GetILProcessor();
  119. // Call the constructor for System.Object, and exit
  120. IL.Emit(OpCodes.Ldarg_0);
  121. IL.Emit(OpCodes.Call, baseConstructor);
  122. IL.Emit(OpCodes.Ret);
  123. targetType.Methods.Add(ctor);
  124. return ctor;
  125. }
  126. /// <summary>
  127. /// Adds a rewritable property to the <paramref name="typeDef">target type</paramref>.
  128. /// </summary>
  129. /// <param name="typeDef">The target type that will hold the newly-created property.</param>
  130. /// <param name="propertyName">The name of the property itself.</param>
  131. /// <param name="propertyType">The <see cref="System.Type" /> instance that describes the property type.</param>
  132. public static void AddProperty(this TypeDefinition typeDef, string propertyName, Type propertyType)
  133. {
  134. var module = typeDef.Module;
  135. var typeRef = module.Import(propertyType);
  136. typeDef.AddProperty(propertyName, typeRef);
  137. }
  138. /// <summary>
  139. /// Adds a rewritable property to the <paramref name="typeDef">target type</paramref>.
  140. /// </summary>
  141. /// <param name="typeDef">The target type that will hold the newly-created property.</param>
  142. /// <param name="propertyName">The name of the property itself.</param>
  143. /// <param name="propertyType">The <see cref="TypeReference" /> instance that describes the property type.</param>
  144. public static void AddProperty(this TypeDefinition typeDef, string propertyName,
  145. TypeReference propertyType)
  146. {
  147. var fieldName = string.Format("__{0}_backingField", propertyName);
  148. var actualField = new FieldDefinition(fieldName, FieldAttributes.Private, propertyType);
  149. typeDef.Fields.Add(actualField);
  150. FieldReference backingField = actualField;
  151. if (typeDef.GenericParameters.Count > 0)
  152. backingField = GetBackingField(fieldName, typeDef, propertyType);
  153. var getterName = string.Format("get_{0}", propertyName);
  154. var setterName = string.Format("set_{0}", propertyName);
  155. const MethodAttributes attributes = MethodAttributes.Public | MethodAttributes.HideBySig |
  156. MethodAttributes.SpecialName | MethodAttributes.NewSlot |
  157. MethodAttributes.Virtual;
  158. var module = typeDef.Module;
  159. var voidType = module.Import(typeof(void));
  160. // Implement the getter and the setter
  161. var getter = AddPropertyGetter(propertyType, getterName, attributes, backingField);
  162. var setter = AddPropertySetter(propertyType, attributes, backingField, setterName, voidType);
  163. typeDef.AddProperty(propertyName, propertyType, getter, setter);
  164. }
  165. /// <summary>
  166. /// Adds a rewriteable property to the <paramref name="typeDef">target type</paramref>
  167. /// using an existing <paramref name="getter" /> and <paramref name="setter" />.
  168. /// </summary>
  169. /// <param name="typeDef">The target type that will hold the newly-created property.</param>
  170. /// <param name="propertyName">The name of the property itself.</param>
  171. /// <param name="propertyType">The <see cref="TypeReference" /> instance that describes the property type.</param>
  172. /// <param name="getter">The property getter method.</param>
  173. /// <param name="setter">The property setter method.</param>
  174. public static void AddProperty(this TypeDefinition typeDef, string propertyName, TypeReference propertyType,
  175. MethodDefinition getter, MethodDefinition setter)
  176. {
  177. var newProperty = new PropertyDefinition(propertyName, PropertyAttributes.Unused, propertyType)
  178. {
  179. GetMethod = getter,
  180. SetMethod = setter
  181. };
  182. typeDef.Methods.Add(getter);
  183. typeDef.Methods.Add(setter);
  184. typeDef.Properties.Add(newProperty);
  185. }
  186. /// <summary>
  187. /// Retrieves the method that matches the given <paramref name="methodName" />.
  188. /// </summary>
  189. /// <param name="typeDef">The target type to search.</param>
  190. /// <param name="methodName">The name of the target method.</param>
  191. /// <returns>
  192. /// A method that matches the given <paramref name="methodName" />. If the method is not found, then it will
  193. /// return a <c>null</c> value.
  194. /// </returns>
  195. public static MethodDefinition GetMethod(this TypeDefinition typeDef, string methodName)
  196. {
  197. var result = from MethodDefinition m in typeDef.Methods
  198. where m.Name == methodName
  199. select m;
  200. return result.FirstOrDefault();
  201. }
  202. /// <summary>
  203. /// Resolves the backing field for a generic type declaration.
  204. /// </summary>
  205. /// <param name="fieldName">The name of the field to reference.</param>
  206. /// <param name="typeDef">The type that holds the actual field.</param>
  207. /// <param name="propertyType">The <see cref="TypeReference" /> that describes the property type being referenced.</param>
  208. /// <returns>A <see cref="FieldReference" /> that points to the actual backing field.</returns>
  209. private static FieldReference GetBackingField(string fieldName, TypeDefinition typeDef,
  210. TypeReference propertyType)
  211. {
  212. // If the current type is a generic type,
  213. // the current generic type must be resolved before
  214. // using the actual field
  215. var declaringType = new GenericInstanceType(typeDef);
  216. foreach (GenericParameter parameter in typeDef.GenericParameters)
  217. declaringType.GenericArguments.Add(parameter);
  218. return new FieldReference(fieldName, declaringType, propertyType);
  219. ;
  220. }
  221. /// <summary>
  222. /// Creates a property getter method implementation with the
  223. /// <paramref name="propertyType" /> as the return type.
  224. /// </summary>
  225. /// <param name="propertyType">Represents the <see cref="TypeReference">return type</see> for the getter method.</param>
  226. /// <param name="getterName">The getter method name.</param>
  227. /// <param name="attributes">The method attributes associated with the getter method.</param>
  228. /// <param name="backingField">The field that will store the instance that the getter method will retrieve.</param>
  229. /// <returns>A <see cref="MethodDefinition" /> representing the getter method itself.</returns>
  230. private static MethodDefinition AddPropertyGetter(TypeReference propertyType,
  231. string getterName, MethodAttributes attributes,
  232. FieldReference backingField)
  233. {
  234. var getter = new MethodDefinition(getterName, attributes, propertyType)
  235. {
  236. IsPublic = true,
  237. ImplAttributes = MethodImplAttributes.Managed | MethodImplAttributes.IL
  238. };
  239. var IL = getter.GetILGenerator();
  240. IL.Emit(OpCodes.Ldarg_0);
  241. IL.Emit(OpCodes.Ldfld, backingField);
  242. IL.Emit(OpCodes.Ret);
  243. return getter;
  244. }
  245. /// <summary>
  246. /// Creates a property setter method implementation with the
  247. /// <paramref name="propertyType" /> as the setter parameter.
  248. /// </summary>
  249. /// <param name="propertyType">Represents the <see cref="TypeReference">parameter type</see> for the setter method.</param>
  250. /// <param name="attributes">The method attributes associated with the setter method.</param>
  251. /// <param name="backingField">The field that will store the instance for the setter method.</param>
  252. /// <param name="setterName">The method name of the setter method.</param>
  253. /// <param name="voidType">The <see cref="TypeReference" /> that represents <see cref="Void" />.</param>
  254. /// <returns>A <see cref="MethodDefinition" /> that represents the setter method itself.</returns>
  255. private static MethodDefinition AddPropertySetter(TypeReference propertyType, MethodAttributes attributes,
  256. FieldReference backingField, string setterName,
  257. TypeReference voidType)
  258. {
  259. var setter = new MethodDefinition(setterName, attributes, voidType)
  260. {
  261. IsPublic = true,
  262. ImplAttributes = MethodImplAttributes.Managed | MethodImplAttributes.IL
  263. };
  264. setter.Parameters.Add(new ParameterDefinition(propertyType));
  265. var IL = setter.GetILGenerator();
  266. IL.Emit(OpCodes.Ldarg_0);
  267. IL.Emit(OpCodes.Ldarg_1);
  268. IL.Emit(OpCodes.Stfld, backingField);
  269. IL.Emit(OpCodes.Ret);
  270. return setter;
  271. }
  272. }
  273. }