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