PageRenderTime 23ms CodeModel.GetById 13ms app.highlight 6ms RepoModel.GetById 2ms app.codeStats 0ms

/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
  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}