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