1 // -----------------------------------------------------------------------
\r
2 // Copyright (c) Microsoft Corporation. All rights reserved.
\r
3 // -----------------------------------------------------------------------
\r
5 using System.Collections.Generic;
\r
6 using System.ComponentModel;
\r
7 using System.Globalization;
\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
15 namespace System.ComponentModel.Composition
\r
17 // // Assume TMetadataView is
\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
25 // // The class to be generated will look approximately like:
\r
26 // public class __Foo__MedataViewProxy : TMetadataView
\r
28 // public __Foo__MedataViewProxy (IDictionary<string, object> metadata)
\r
30 // if(metadata == null)
\r
32 // throw InvalidArgumentException("metadata");
\r
36 // Record1 = (typeRecord1)Record1;
\r
37 // Record2 = (typeRecord1)Record2;
\r
38 // Record3 = (typeRecord1)Record3;
\r
39 // Record4 = (typeRecord1)Record4;
\r
41 // catch(InvalidCastException ice)
\r
43 // //Annotate exception .Data with diagnostic info
\r
45 // catch(NulLReferenceException ice)
\r
47 // //Annotate exception .Data with diagnostic info
\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
56 internal static class MetadataViewGenerator
\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
64 private static Lock _lock = new Lock();
\r
65 private static Dictionary<Type, Type> _proxies = new Dictionary<Type, Type>();
\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
74 public static Type GenerateView(Type viewType)
\r
76 Assumes.NotNull(viewType);
\r
77 Assumes.IsTrue(viewType.IsInterface);
\r
82 using (new ReadLock(_lock))
\r
84 foundProxy = _proxies.TryGetValue(viewType, out proxyType);
\r
87 // No factory exists
\r
90 // Try again under a write lock if still none generate the proxy
\r
91 using (new WriteLock(_lock))
\r
93 foundProxy = _proxies.TryGetValue(viewType, out proxyType);
\r
97 proxyType = GenerateInterfaceViewProxyType(viewType);
\r
98 Assumes.NotNull(proxyType);
\r
100 _proxies.Add(viewType, proxyType);
\r
107 private static void GenerateLocalAssignmentFromDefaultAttribute(this ILGenerator IL, DefaultValueAttribute[] attrs, LocalBuilder local)
\r
109 if (attrs.Length > 0)
\r
111 DefaultValueAttribute defaultAttribute = attrs[0];
\r
112 IL.LoadValue(defaultAttribute.Value);
\r
113 if ((defaultAttribute.Value != null) && (defaultAttribute.Value.GetType().IsValueType))
\r
115 IL.Emit(OpCodes.Box, defaultAttribute.Value.GetType());
\r
117 IL.Emit(OpCodes.Stloc, local);
\r
121 private static void GenerateFieldAssignmentFromLocalValue(this ILGenerator IL, LocalBuilder local, FieldBuilder field)
\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
129 private static void GenerateLocalAssignmentFromFlag(this ILGenerator IL, LocalBuilder local, bool flag)
\r
131 IL.Emit(flag ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
\r
132 IL.Emit(OpCodes.Stloc, local);
\r
135 // This must be called with _readerWriterLock held for Write
\r
136 private static Type GenerateInterfaceViewProxyType(Type viewType)
\r
138 // View type is an interface let's cook an implementation
\r
140 TypeBuilder proxyTypeBuilder;
\r
141 Type[] interfaces = { viewType };
\r
143 proxyTypeBuilder = ProxyModuleBuilder.DefineType(
\r
144 string.Format(CultureInfo.InvariantCulture, "_proxy_{0}_{1}", viewType.FullName, Guid.NewGuid()),
\r
145 TypeAttributes.Public,
\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
157 Label tryConstructView = proxyCtorIL.BeginExceptionBlock();
\r
159 // Implement interface properties
\r
160 foreach (PropertyInfo propertyInfo in viewType.GetAllProperties())
\r
162 string fieldName = string.Format(CultureInfo.InvariantCulture, "_{0}_{1}", propertyInfo.Name, Guid.NewGuid());
\r
164 // Cache names and type for exception
\r
165 string propertyName = string.Format(CultureInfo.InvariantCulture, "{0}", propertyInfo.Name);
\r
167 Type[] propertyTypeArguments = new Type[] { propertyInfo.PropertyType };
\r
168 Type[] optionalModifiers = null;
\r
169 Type[] requiredModifiers = null;
\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
179 FieldBuilder proxyFieldBuilder = proxyTypeBuilder.DefineField(
\r
181 propertyInfo.PropertyType,
\r
182 FieldAttributes.Private);
\r
184 // Generate property
\r
185 PropertyBuilder proxyPropertyBuilder = proxyTypeBuilder.DefineProperty(
\r
187 PropertyAttributes.None,
\r
188 propertyInfo.PropertyType,
\r
189 propertyTypeArguments);
\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
195 DefaultValueAttribute[] attrs = propertyInfo.GetAttributes<DefaultValueAttribute>(false);
\r
196 if(attrs.Length > 0)
\r
198 innerTryCastValue = proxyCtorIL.BeginExceptionBlock();
\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
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
211 proxyCtorIL.GenerateLocalAssignmentFromFlag(usesExportedMD, false);
\r
212 proxyCtorIL.GenerateLocalAssignmentFromDefaultAttribute(attrs, value);
\r
214 proxyCtorIL.MarkLabel(doneGettingDefaultValue);
\r
215 proxyCtorIL.GenerateFieldAssignmentFromLocalValue(value, proxyFieldBuilder);
\r
216 proxyCtorIL.Emit(OpCodes.Leave, tryCastValue);
\r
218 // catch blocks for innerTryCastValue start here
\r
219 if (attrs.Length > 0)
\r
221 proxyCtorIL.BeginCatchBlock(typeof(InvalidCastException));
\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
231 proxyCtorIL.EndExceptionBlock();
\r
234 // catch blocks for tryCast start here
\r
235 proxyCtorIL.BeginCatchBlock(typeof(NullReferenceException));
\r
237 proxyCtorIL.Emit(OpCodes.Stloc, exception);
\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
245 proxyCtorIL.BeginCatchBlock(typeof(InvalidCastException));
\r
247 proxyCtorIL.Emit(OpCodes.Stloc, exception);
\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
255 proxyCtorIL.EndExceptionBlock();
\r
257 if (propertyInfo.CanWrite)
\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
265 if (propertyInfo.CanRead)
\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
275 Type.EmptyTypes, null, null);
\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
283 proxyPropertyBuilder.SetGetMethod(getMethodBuilder);
\r
287 proxyCtorIL.Emit(OpCodes.Leave, tryConstructView);
\r
289 // catch blocks for constructView start here
\r
290 proxyCtorIL.BeginCatchBlock(typeof(NullReferenceException));
\r
292 proxyCtorIL.Emit(OpCodes.Stloc, exception);
\r
294 proxyCtorIL.GetExceptionDataAndStoreInLocal(exception, exceptionData);
\r
295 proxyCtorIL.AddItemToLocalDictionary(exceptionData, MetadataViewType, viewType);
\r
296 proxyCtorIL.Emit(OpCodes.Rethrow);
\r
298 proxyCtorIL.BeginCatchBlock(typeof(InvalidCastException));
\r
300 proxyCtorIL.Emit(OpCodes.Stloc, exception);
\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
311 proxyCtorIL.EndExceptionBlock();
\r
313 // Finished implementing interface and constructor
\r
314 proxyCtorIL.Emit(OpCodes.Ret);
\r
315 proxyType = proxyTypeBuilder.CreateType();
\r