Implement MachineKey.Protect and MachineKey.Unprotect
[mono.git] / mcs / class / System.ComponentModel.Composition / src / ComponentModel / System / ComponentModel / Composition / MetadataViewGenerator.cs
1 // -----------------------------------------------------------------------\r
2 // Copyright (c) Microsoft Corporation.  All rights reserved.\r
3 // -----------------------------------------------------------------------\r
4 using System;\r
5 using System.Collections.Generic;\r
6 using System.ComponentModel;\r
7 using System.Globalization;\r
8 using System.Linq;\r
9 using System.Reflection;\r
10 using System.Threading;\r
11 using Microsoft.Internal;\r
12 using System.Reflection.Emit;\r
13 using System.Collections;\r
14 \r
15 namespace System.ComponentModel.Composition\r
16 {\r
17     // // Assume TMetadataView is\r
18     // //interface Foo\r
19     // //{\r
20     // //    public typeRecord1 Record1 { get; }\r
21     // //    public typeRecord2 Record2 { get; }\r
22     // //    public typeRecord3 Record3 { get; }\r
23     // //    public typeRecord4 Record4 { get; }\r
24     // //}\r
25     // // The class to be generated will look approximately like:\r
26     // public class __Foo__MedataViewProxy : TMetadataView\r
27     // {\r
28     //     public __Foo__MedataViewProxy (IDictionary<string, object> metadata)\r
29     //     {\r
30     //         if(metadata == null)\r
31     //         {\r
32     //             throw InvalidArgumentException("metadata");\r
33     //         }\r
34     //         try\r
35     //         {\r
36     //              Record1 = (typeRecord1)Record1;\r
37     //              Record2 = (typeRecord1)Record2;\r
38     //              Record3 = (typeRecord1)Record3;\r
39     //              Record4 = (typeRecord1)Record4;\r
40     //          }\r
41     //          catch(InvalidCastException ice)\r
42     //          {\r
43     //              //Annotate exception .Data with diagnostic info\r
44     //          }\r
45     //          catch(NulLReferenceException ice)\r
46     //          {\r
47     //              //Annotate exception .Data with diagnostic info\r
48     //          }\r
49     //     }\r
50     //     // Interface\r
51     //     public typeRecord1 Record1 { get; }\r
52     //     public typeRecord2 Record2 { get; }\r
53     //     public typeRecord3 Record3 { get; }\r
54     //     public typeRecord4 Record4 { get; }\r
55     // }\r
56     internal static class MetadataViewGenerator\r
57     {\r
58         public const string MetadataViewType       = "MetadataViewType";\r
59         public const string MetadataItemKey        = "MetadataItemKey";\r
60         public const string MetadataItemTargetType = "MetadataItemTargetType";\r
61         public const string MetadataItemSourceType = "MetadataItemSourceType";\r
62         public const string MetadataItemValue      = "MetadataItemValue";\r
63 \r
64         private static Lock _lock = new Lock();\r
65         private static Dictionary<Type, Type> _proxies = new Dictionary<Type, Type>();\r
66 \r
67         private static AssemblyName ProxyAssemblyName = new AssemblyName(string.Format(CultureInfo.InvariantCulture, "MetadataViewProxies_{0}", Guid.NewGuid()));\r
68         private static AssemblyBuilder ProxyAssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(ProxyAssemblyName, AssemblyBuilderAccess.Run);\r
69         private static ModuleBuilder ProxyModuleBuilder = ProxyAssemblyBuilder.DefineDynamicModule("MetadataViewProxiesModule");\r
70         private static Type[] CtorArgumentTypes = new Type[] { typeof(IDictionary<string, object>) };\r
71         private static MethodInfo _mdvDictionaryTryGet = CtorArgumentTypes[0].GetMethod("TryGetValue");\r
72         private static readonly MethodInfo ObjectGetType = typeof(object).GetMethod("GetType", Type.EmptyTypes);\r
73 \r
74         public static Type GenerateView(Type viewType)\r
75         {\r
76             Assumes.NotNull(viewType);\r
77             Assumes.IsTrue(viewType.IsInterface);\r
78 \r
79             Type proxyType;\r
80             bool foundProxy;\r
81 \r
82             using (new ReadLock(_lock))\r
83             {\r
84                 foundProxy = _proxies.TryGetValue(viewType, out proxyType);\r
85             }\r
86 \r
87             // No factory exists\r
88             if(!foundProxy)\r
89             {\r
90                 // Try again under a write lock if still none generate the proxy\r
91                 using (new WriteLock(_lock))\r
92                 {\r
93                     foundProxy = _proxies.TryGetValue(viewType, out proxyType);\r
94 \r
95                     if (!foundProxy)\r
96                     {\r
97                         proxyType = GenerateInterfaceViewProxyType(viewType);\r
98                         Assumes.NotNull(proxyType);\r
99 \r
100                         _proxies.Add(viewType, proxyType);\r
101                     }\r
102                 }\r
103             }\r
104             return proxyType;\r
105         }\r
106 \r
107         private static void GenerateLocalAssignmentFromDefaultAttribute(this ILGenerator IL, DefaultValueAttribute[] attrs, LocalBuilder local)\r
108         {\r
109             if (attrs.Length > 0)\r
110             {\r
111                 DefaultValueAttribute defaultAttribute = attrs[0];\r
112                 IL.LoadValue(defaultAttribute.Value);\r
113                 if ((defaultAttribute.Value != null) && (defaultAttribute.Value.GetType().IsValueType))\r
114                 {\r
115                     IL.Emit(OpCodes.Box, defaultAttribute.Value.GetType());\r
116                 }\r
117                 IL.Emit(OpCodes.Stloc, local);\r
118             }\r
119         }\r
120 \r
121         private static void GenerateFieldAssignmentFromLocalValue(this ILGenerator IL, LocalBuilder local, FieldBuilder field)\r
122         {\r
123             IL.Emit(OpCodes.Ldarg_0);\r
124             IL.Emit(OpCodes.Ldloc, local);\r
125             IL.Emit(field.FieldType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, field.FieldType);\r
126             IL.Emit(OpCodes.Stfld, field);\r
127         }\r
128 \r
129         private static void GenerateLocalAssignmentFromFlag(this ILGenerator IL, LocalBuilder local, bool flag)\r
130         {\r
131             IL.Emit(flag ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);\r
132             IL.Emit(OpCodes.Stloc, local);\r
133         }\r
134 \r
135         // This must be called with _readerWriterLock held for Write\r
136         private static Type GenerateInterfaceViewProxyType(Type viewType)\r
137         {\r
138             // View type is an interface let's cook an implementation\r
139             Type proxyType;\r
140             TypeBuilder proxyTypeBuilder;\r
141             Type[] interfaces = { viewType };\r
142 \r
143             proxyTypeBuilder = ProxyModuleBuilder.DefineType(\r
144                 string.Format(CultureInfo.InvariantCulture, "_proxy_{0}_{1}", viewType.FullName, Guid.NewGuid()),\r
145                 TypeAttributes.Public,\r
146                 typeof(object),\r
147                 interfaces);\r
148 \r
149             // Implement Constructor\r
150             ILGenerator proxyCtorIL = proxyTypeBuilder.CreateGeneratorForPublicConstructor(CtorArgumentTypes);\r
151             LocalBuilder exception = proxyCtorIL.DeclareLocal(typeof(Exception));\r
152             LocalBuilder exceptionData = proxyCtorIL.DeclareLocal(typeof(IDictionary));\r
153             LocalBuilder sourceType = proxyCtorIL.DeclareLocal(typeof(Type));\r
154             LocalBuilder value = proxyCtorIL.DeclareLocal(typeof(object));\r
155             LocalBuilder usesExportedMD = proxyCtorIL.DeclareLocal(typeof(bool));\r
156 \r
157             Label tryConstructView = proxyCtorIL.BeginExceptionBlock();\r
158 \r
159             // Implement interface properties\r
160             foreach (PropertyInfo propertyInfo in viewType.GetAllProperties())\r
161             {\r
162                 string fieldName = string.Format(CultureInfo.InvariantCulture, "_{0}_{1}", propertyInfo.Name, Guid.NewGuid());\r
163 \r
164                 // Cache names and type for exception\r
165                 string propertyName = string.Format(CultureInfo.InvariantCulture, "{0}", propertyInfo.Name);\r
166 \r
167                 Type[] propertyTypeArguments = new Type[] { propertyInfo.PropertyType };\r
168                 Type[] optionalModifiers = null;\r
169                 Type[] requiredModifiers = null;\r
170 \r
171 #if !SILVERLIGHT\r
172                 // PropertyInfo does not support GetOptionalCustomModifiers and GetRequiredCustomModifiers on Silverlight\r
173                 optionalModifiers = propertyInfo.GetOptionalCustomModifiers();\r
174                 requiredModifiers = propertyInfo.GetRequiredCustomModifiers();\r
175                 Array.Reverse(optionalModifiers);\r
176                 Array.Reverse(requiredModifiers);\r
177 #endif\r
178                 // Generate field\r
179                 FieldBuilder proxyFieldBuilder = proxyTypeBuilder.DefineField(\r
180                     fieldName,\r
181                     propertyInfo.PropertyType,\r
182                     FieldAttributes.Private);\r
183 \r
184                 // Generate property\r
185                 PropertyBuilder proxyPropertyBuilder = proxyTypeBuilder.DefineProperty(\r
186                     propertyName,\r
187                     PropertyAttributes.None,\r
188                     propertyInfo.PropertyType,\r
189                     propertyTypeArguments);\r
190 \r
191                 // Generate constructor code for retrieving the metadata value and setting the field\r
192                 Label tryCastValue = proxyCtorIL.BeginExceptionBlock();\r
193                 Label innerTryCastValue;\r
194 \r
195                 DefaultValueAttribute[] attrs = propertyInfo.GetAttributes<DefaultValueAttribute>(false);\r
196                 if(attrs.Length > 0)\r
197                 {\r
198                     innerTryCastValue = proxyCtorIL.BeginExceptionBlock();\r
199                 }\r
200 \r
201                 // In constructor set the backing field with the value from the dictionary\r
202                 Label doneGettingDefaultValue = proxyCtorIL.DefineLabel();\r
203                 GenerateLocalAssignmentFromFlag(proxyCtorIL, usesExportedMD, true);\r
204 \r
205                 proxyCtorIL.Emit(OpCodes.Ldarg_1);\r
206                 proxyCtorIL.Emit(OpCodes.Ldstr, propertyInfo.Name);\r
207                 proxyCtorIL.Emit(OpCodes.Ldloca, value);\r
208                 proxyCtorIL.Emit(OpCodes.Callvirt, _mdvDictionaryTryGet);\r
209                 proxyCtorIL.Emit(OpCodes.Brtrue, doneGettingDefaultValue);\r
210 \r
211                 proxyCtorIL.GenerateLocalAssignmentFromFlag(usesExportedMD, false);\r
212                 proxyCtorIL.GenerateLocalAssignmentFromDefaultAttribute(attrs, value);\r
213 \r
214                 proxyCtorIL.MarkLabel(doneGettingDefaultValue);\r
215                 proxyCtorIL.GenerateFieldAssignmentFromLocalValue(value, proxyFieldBuilder);\r
216                 proxyCtorIL.Emit(OpCodes.Leave, tryCastValue);\r
217 \r
218                 // catch blocks for innerTryCastValue start here\r
219                 if (attrs.Length > 0)\r
220                 {\r
221                     proxyCtorIL.BeginCatchBlock(typeof(InvalidCastException));\r
222                     {\r
223                         Label notUsesExportedMd = proxyCtorIL.DefineLabel();\r
224                         proxyCtorIL.Emit(OpCodes.Ldloc, usesExportedMD);\r
225                         proxyCtorIL.Emit(OpCodes.Brtrue, notUsesExportedMd);\r
226                         proxyCtorIL.Emit(OpCodes.Rethrow);\r
227                         proxyCtorIL.MarkLabel(notUsesExportedMd);\r
228                         proxyCtorIL.GenerateLocalAssignmentFromDefaultAttribute(attrs, value);\r
229                         proxyCtorIL.GenerateFieldAssignmentFromLocalValue(value, proxyFieldBuilder);\r
230                     }\r
231                     proxyCtorIL.EndExceptionBlock();\r
232                 }\r
233 \r
234                 // catch blocks for tryCast start here\r
235                 proxyCtorIL.BeginCatchBlock(typeof(NullReferenceException));\r
236                 {\r
237                     proxyCtorIL.Emit(OpCodes.Stloc, exception);\r
238 \r
239                     proxyCtorIL.GetExceptionDataAndStoreInLocal(exception, exceptionData);\r
240                     proxyCtorIL.AddItemToLocalDictionary(exceptionData, MetadataItemKey, propertyName);\r
241                     proxyCtorIL.AddItemToLocalDictionary(exceptionData, MetadataItemTargetType, propertyInfo.PropertyType);\r
242                     proxyCtorIL.Emit(OpCodes.Rethrow);\r
243                 }\r
244 \r
245                 proxyCtorIL.BeginCatchBlock(typeof(InvalidCastException));\r
246                 {\r
247                     proxyCtorIL.Emit(OpCodes.Stloc, exception);\r
248 \r
249                     proxyCtorIL.GetExceptionDataAndStoreInLocal(exception, exceptionData);\r
250                     proxyCtorIL.AddItemToLocalDictionary(exceptionData, MetadataItemKey, propertyName);\r
251                     proxyCtorIL.AddItemToLocalDictionary(exceptionData, MetadataItemTargetType, propertyInfo.PropertyType);\r
252                     proxyCtorIL.Emit(OpCodes.Rethrow);\r
253                 }\r
254 \r
255                 proxyCtorIL.EndExceptionBlock();\r
256 \r
257                 if (propertyInfo.CanWrite)\r
258                 {\r
259                     // The MetadataView '{0}' is invalid because property '{1}' has a property set method.\r
260                     throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture,\r
261                         Strings.InvalidSetterOnMetadataField,\r
262                         viewType,\r
263                         propertyName));\r
264                 }\r
265                 if (propertyInfo.CanRead)\r
266                 {\r
267                     // Generate "get" method implementation.\r
268                     MethodBuilder getMethodBuilder = proxyTypeBuilder.DefineMethod(\r
269                         string.Format(CultureInfo.InvariantCulture, "get_{0}", propertyName),\r
270                         MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final,\r
271                         CallingConventions.HasThis,\r
272                         propertyInfo.PropertyType,\r
273                         requiredModifiers,\r
274                         optionalModifiers,\r
275                         Type.EmptyTypes, null, null);\r
276 \r
277                     proxyTypeBuilder.DefineMethodOverride(getMethodBuilder, propertyInfo.GetGetMethod());\r
278                     ILGenerator getMethodIL = getMethodBuilder.GetILGenerator();\r
279                     getMethodIL.Emit(OpCodes.Ldarg_0);\r
280                     getMethodIL.Emit(OpCodes.Ldfld, proxyFieldBuilder);\r
281                     getMethodIL.Emit(OpCodes.Ret);\r
282 \r
283                     proxyPropertyBuilder.SetGetMethod(getMethodBuilder);\r
284                 }\r
285             }\r
286 \r
287             proxyCtorIL.Emit(OpCodes.Leave, tryConstructView);\r
288 \r
289             // catch blocks for constructView start here\r
290             proxyCtorIL.BeginCatchBlock(typeof(NullReferenceException));\r
291             {\r
292                 proxyCtorIL.Emit(OpCodes.Stloc, exception);\r
293 \r
294                 proxyCtorIL.GetExceptionDataAndStoreInLocal(exception, exceptionData);\r
295                 proxyCtorIL.AddItemToLocalDictionary(exceptionData, MetadataViewType, viewType);\r
296                 proxyCtorIL.Emit(OpCodes.Rethrow);\r
297             }\r
298             proxyCtorIL.BeginCatchBlock(typeof(InvalidCastException));\r
299             {\r
300                 proxyCtorIL.Emit(OpCodes.Stloc, exception);\r
301 \r
302                 proxyCtorIL.GetExceptionDataAndStoreInLocal(exception, exceptionData);\r
303                 proxyCtorIL.Emit(OpCodes.Ldloc, value);\r
304                 proxyCtorIL.Emit(OpCodes.Call, ObjectGetType);\r
305                 proxyCtorIL.Emit(OpCodes.Stloc, sourceType);\r
306                 proxyCtorIL.AddItemToLocalDictionary(exceptionData, MetadataViewType, viewType);\r
307                 proxyCtorIL.AddLocalToLocalDictionary(exceptionData, MetadataItemSourceType, sourceType);\r
308                 proxyCtorIL.AddLocalToLocalDictionary(exceptionData, MetadataItemValue, value);\r
309                 proxyCtorIL.Emit(OpCodes.Rethrow);\r
310             }\r
311             proxyCtorIL.EndExceptionBlock();\r
312 \r
313             // Finished implementing interface and constructor\r
314             proxyCtorIL.Emit(OpCodes.Ret);\r
315             proxyType = proxyTypeBuilder.CreateType();\r
316 \r
317             return proxyType;\r
318         }\r
319              \r
320     }\r
321 }\r