New test.
[mono.git] / mcs / class / System.Web.Mvc2 / System.Web.Mvc / DynamicTypeGenerator.cs
1 /* ****************************************************************************\r
2  *\r
3  * Copyright (c) Microsoft Corporation. All rights reserved.\r
4  *\r
5  * This software is subject to the Microsoft Public License (Ms-PL). \r
6  * A copy of the license can be found in the license.htm file included \r
7  * in this distribution.\r
8  *\r
9  * You must not remove this notice, or any other, from this software.\r
10  *\r
11  * ***************************************************************************/\r
12 \r
13 namespace System.Web.Mvc {\r
14     using System;\r
15     using System.Collections.Generic;\r
16     using System.Linq;\r
17     using System.Reflection;\r
18     using System.Reflection.Emit;\r
19     using System.Security;\r
20 \r
21     internal static class DynamicTypeGenerator {\r
22 \r
23         private static readonly ModuleBuilder _dynamicModule = CreateDynamicModule();\r
24 \r
25         private static ModuleBuilder CreateDynamicModule() {\r
26             // DDB 226615 - since MVC is [SecurityTransparent], the dynamic assembly must declare itself likewise\r
27             CustomAttributeBuilder builder = new CustomAttributeBuilder(\r
28                 typeof(SecurityTransparentAttribute).GetConstructor(Type.EmptyTypes), new object[0]);\r
29             CustomAttributeBuilder[] assemblyAttributes = new CustomAttributeBuilder[] { builder };\r
30             AssemblyBuilder dynamicAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(\r
31                 new AssemblyName("System.Web.Mvc.{Dynamic}"), AssemblyBuilderAccess.Run, assemblyAttributes);\r
32             ModuleBuilder dynamicModule = dynamicAssembly.DefineDynamicModule("System.Web.Mvc.{Dynamic}.dll");\r
33             return dynamicModule;\r
34         }\r
35 \r
36         // Creates a new dynamic type that is a subclassed type of baseType and also implements methods of the specified\r
37         // interfaces. The base type must already have method signatures that implicitly implement the given\r
38         // interfaces. The signatures of all public (e.g. not private / internal) constructors from the baseType\r
39         // will be duplicated for the subclassed type and the new constructors made public.\r
40         public static Type GenerateType(string dynamicTypeName, Type baseType, IEnumerable<Type> interfaceTypes) {\r
41             TypeBuilder newType = _dynamicModule.DefineType(\r
42                 "System.Web.Mvc.{Dynamic}." + dynamicTypeName,\r
43                 TypeAttributes.AutoLayout | TypeAttributes.Public | TypeAttributes.Class,\r
44                 baseType);\r
45 \r
46             foreach (Type interfaceType in interfaceTypes) {\r
47                 newType.AddInterfaceImplementation(interfaceType);\r
48                 foreach (MethodInfo interfaceMethod in interfaceType.GetMethods()) {\r
49                     ImplementInterfaceMethod(newType, interfaceMethod);\r
50                 }\r
51             }\r
52 \r
53             // generate new constructors for each accessible base constructor\r
54             foreach (ConstructorInfo ctor in baseType.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) {\r
55                 switch (ctor.Attributes & MethodAttributes.MemberAccessMask) {\r
56                     case MethodAttributes.Family:\r
57                     case MethodAttributes.Public:\r
58                     case MethodAttributes.FamORAssem:\r
59                         ImplementConstructor(newType, ctor);\r
60                         break;\r
61                 }\r
62             }\r
63 \r
64             Type bakedType = newType.CreateType();\r
65             return bakedType;\r
66         }\r
67 \r
68         // generates this constructor:\r
69         // public NewType(param0, param1, ...) : base(param0, param1, ...) { }\r
70         private static void ImplementConstructor(TypeBuilder newType, ConstructorInfo baseCtor) {\r
71             ParameterInfo[] parameters = baseCtor.GetParameters();\r
72             Type[] parameterTypes = (from p in parameters select p.ParameterType).ToArray();\r
73 \r
74             ConstructorBuilder newCtor = newType.DefineConstructor(\r
75                 (baseCtor.Attributes & ~MethodAttributes.MemberAccessMask) | MethodAttributes.Public /* force public constructor */,\r
76                 baseCtor.CallingConvention, parameterTypes);\r
77 \r
78             // parameter 0 is 'this', so we start at index 1\r
79             for (int i = 0; i < parameters.Length; i++) {\r
80                 newCtor.DefineParameter(i + 1, parameters[i].Attributes, parameters[i].Name);\r
81             }\r
82 \r
83             // load all arguments (including 'this') in proper order, then call and return\r
84             ILGenerator ilGen = newCtor.GetILGenerator();\r
85             for (int i = 0; i <= parameterTypes.Length; i++) {\r
86                 ilGen.Emit(OpCodes.Ldarg_S, (byte)i);\r
87             }\r
88             ilGen.Emit(OpCodes.Call, baseCtor);\r
89             ilGen.Emit(OpCodes.Ret);\r
90         }\r
91 \r
92         // generates this explicit interface method:\r
93         // public new Interface.Method(param0, param1, ...) {\r
94         //   return base.Method(param0, param1, ...);\r
95         // }\r
96         private static void ImplementInterfaceMethod(TypeBuilder newType, MethodInfo interfaceMethod) {\r
97             ParameterInfo[] parameters = interfaceMethod.GetParameters();\r
98             Type[] parameterTypes = (from p in parameters select p.ParameterType).ToArray();\r
99 \r
100             // based on http://msdn.microsoft.com/en-us/library/system.reflection.emit.typebuilder.definemethodoverride.aspx\r
101             MethodBuilder newMethod = newType.DefineMethod(interfaceMethod.DeclaringType.Name + "." + interfaceMethod.Name,\r
102                 MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final,\r
103                 interfaceMethod.ReturnType, parameterTypes);\r
104 \r
105             MethodInfo baseMethod = newType.BaseType.GetMethod(interfaceMethod.Name, parameterTypes);\r
106 \r
107             // parameter 0 is 'this', so we start at index 1\r
108             for (int i = 0; i < parameters.Length; i++) {\r
109                 newMethod.DefineParameter(i + 1, parameters[i].Attributes, parameters[i].Name);\r
110             }\r
111 \r
112             // load all arguments (including 'this') in proper order, then call and return\r
113             ILGenerator ilGen = newMethod.GetILGenerator();\r
114             for (int i = 0; i <= parameterTypes.Length; i++) {\r
115                 ilGen.Emit(OpCodes.Ldarg_S, (byte)i);\r
116             }\r
117             ilGen.Emit(OpCodes.Call, baseMethod);\r
118             ilGen.Emit(OpCodes.Ret);\r
119 \r
120             // finally, hook the new method up to the interface mapping\r
121             newType.DefineMethodOverride(newMethod, interfaceMethod);\r
122         }\r
123 \r
124     }\r
125 }\r