2 // -----------------------------------------------------------------------
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // -----------------------------------------------------------------------
6 using System.Collections.Generic;
7 using System.ComponentModel;
8 using System.Globalization;
10 using System.Reflection;
11 using System.Threading;
12 using Microsoft.Internal;
13 using System.Reflection.Emit;
14 using System.Collections;
15 using System.Security;
17 namespace System.ComponentModel.Composition
19 // // Assume TMetadataView is
22 // // public typeRecord1 Record1 { get; }
23 // // public typeRecord2 Record2 { get; }
24 // // public typeRecord3 Record3 { get; }
25 // // public typeRecord4 Record4 { get; }
27 // // The class to be generated will look approximately like:
28 // public class __Foo__MedataViewProxy : TMetadataView
30 // public __Foo__MedataViewProxy (IDictionary<string, object> metadata)
32 // if(metadata == null)
34 // throw InvalidArgumentException("metadata");
38 // Record1 = (typeRecord1)Record1;
39 // Record2 = (typeRecord1)Record2;
40 // Record3 = (typeRecord1)Record3;
41 // Record4 = (typeRecord1)Record4;
43 // catch(InvalidCastException ice)
45 // //Annotate exception .Data with diagnostic info
47 // catch(NulLReferenceException ice)
49 // //Annotate exception .Data with diagnostic info
53 // public typeRecord1 Record1 { get; }
54 // public typeRecord2 Record2 { get; }
55 // public typeRecord3 Record3 { get; }
56 // public typeRecord4 Record4 { get; }
58 internal static class MetadataViewGenerator
60 public const string MetadataViewType = "MetadataViewType";
61 public const string MetadataItemKey = "MetadataItemKey";
62 public const string MetadataItemTargetType = "MetadataItemTargetType";
63 public const string MetadataItemSourceType = "MetadataItemSourceType";
64 public const string MetadataItemValue = "MetadataItemValue";
66 private static Lock _lock = new Lock();
67 private static Dictionary<Type, Type> _proxies = new Dictionary<Type, Type>();
68 private static AssemblyName ProxyAssemblyName = new AssemblyName(string.Format(CultureInfo.InvariantCulture, "MetadataViewProxies_{0}", Guid.NewGuid()));
70 private static ModuleBuilder criticalProxyModuleBuilder;
71 #endif //FEATURE_CAS_APTCA
72 private static ModuleBuilder transparentProxyModuleBuilder;
74 private static Type[] CtorArgumentTypes = new Type[] { typeof(IDictionary<string, object>) };
75 private static MethodInfo _mdvDictionaryTryGet = CtorArgumentTypes[0].GetMethod("TryGetValue");
76 private static readonly MethodInfo ObjectGetType = typeof(object).GetMethod("GetType", Type.EmptyTypes);
78 private static CustomAttributeBuilder _securityCriticalBuilder =
79 new CustomAttributeBuilder(typeof(SecurityCriticalAttribute).GetConstructor(Type.EmptyTypes), new object[0]);
80 #endif //FEATURE_CAS_APTCA
82 private static AssemblyBuilder CreateProxyAssemblyBuilder(ConstructorInfo constructorInfo)
85 object[] args = new object[0];
86 CustomAttributeBuilder accessAttribute = new CustomAttributeBuilder(constructorInfo, args);
87 CustomAttributeBuilder[] attributes = { accessAttribute };
88 return AppDomain.CurrentDomain.DefineDynamicAssembly(ProxyAssemblyName, AssemblyBuilderAccess.Run, attributes, SecurityContextSource.CurrentAppDomain);
90 return AppDomain.CurrentDomain.DefineDynamicAssembly(ProxyAssemblyName, AssemblyBuilderAccess.Run);
91 #endif //FEATURE_CAS_APTCA
94 // Must be called with _lock held
95 private static ModuleBuilder GetProxyModuleBuilder(bool requiresCritical)
100 // Needed a critical modulebuilder so find or make it
101 if (criticalProxyModuleBuilder == null)
103 var assemblyBuilder = CreateProxyAssemblyBuilder(typeof(AllowPartiallyTrustedCallersAttribute).GetConstructor(Type.EmptyTypes));
104 criticalProxyModuleBuilder = assemblyBuilder.DefineDynamicModule("MetadataViewProxiesModule");
106 return criticalProxyModuleBuilder;
108 #endif //FEATURE_CAS_APTCA
109 if (transparentProxyModuleBuilder == null)
111 // make a new assemblybuilder and modulebuilder
112 var assemblyBuilder = CreateProxyAssemblyBuilder(typeof(SecurityTransparentAttribute).GetConstructor(Type.EmptyTypes));
113 transparentProxyModuleBuilder = assemblyBuilder.DefineDynamicModule("MetadataViewProxiesModule");
116 return transparentProxyModuleBuilder;
119 public static Type GenerateView(Type viewType)
121 Assumes.NotNull(viewType);
122 Assumes.IsTrue(viewType.IsInterface);
127 using (new ReadLock(_lock))
129 foundProxy = _proxies.TryGetValue(viewType, out proxyType);
135 // Try again under a write lock if still none generate the proxy
136 Type generatedProxyType = GenerateInterfaceViewProxyType(viewType);
137 Assumes.NotNull(generatedProxyType);
139 using (new WriteLock(_lock))
141 if (!_proxies.TryGetValue(viewType, out proxyType))
143 proxyType = generatedProxyType;
144 _proxies.Add(viewType, proxyType);
151 private static void GenerateLocalAssignmentFromDefaultAttribute(this ILGenerator IL, DefaultValueAttribute[] attrs, LocalBuilder local)
153 if (attrs.Length > 0)
155 DefaultValueAttribute defaultAttribute = attrs[0];
156 IL.LoadValue(defaultAttribute.Value);
157 if ((defaultAttribute.Value != null) && (defaultAttribute.Value.GetType().IsValueType))
159 IL.Emit(OpCodes.Box, defaultAttribute.Value.GetType());
161 IL.Emit(OpCodes.Stloc, local);
165 private static void GenerateFieldAssignmentFromLocalValue(this ILGenerator IL, LocalBuilder local, FieldBuilder field)
167 IL.Emit(OpCodes.Ldarg_0);
168 IL.Emit(OpCodes.Ldloc, local);
169 IL.Emit(field.FieldType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, field.FieldType);
170 IL.Emit(OpCodes.Stfld, field);
173 private static void GenerateLocalAssignmentFromFlag(this ILGenerator IL, LocalBuilder local, bool flag)
175 IL.Emit(flag ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
176 IL.Emit(OpCodes.Stloc, local);
179 // This must be called with _readerWriterLock held for Write
180 private static Type GenerateInterfaceViewProxyType(Type viewType)
182 // View type is an interface let's cook an implementation
184 TypeBuilder proxyTypeBuilder;
185 Type[] interfaces = { viewType };
186 bool requiresCritical = false;
187 #if FEATURE_CAS_APTCA
188 requiresCritical = !viewType.IsSecurityTransparent;
189 #endif //FEATURE_CAS_APTCA
191 var proxyModuleBuilder = GetProxyModuleBuilder(requiresCritical);
192 proxyTypeBuilder = proxyModuleBuilder.DefineType(
193 string.Format(CultureInfo.InvariantCulture, "_proxy_{0}_{1}", viewType.FullName, Guid.NewGuid()),
194 TypeAttributes.Public,
197 #if FEATURE_CAS_APTCA
198 if (requiresCritical)
200 proxyTypeBuilder.SetCustomAttribute(_securityCriticalBuilder);
202 #endif //FEATURE_CAS_APTCA
203 // Implement Constructor
204 ILGenerator proxyCtorIL = proxyTypeBuilder.CreateGeneratorForPublicConstructor(CtorArgumentTypes);
205 LocalBuilder exception = proxyCtorIL.DeclareLocal(typeof(Exception));
206 LocalBuilder exceptionData = proxyCtorIL.DeclareLocal(typeof(IDictionary));
207 LocalBuilder sourceType = proxyCtorIL.DeclareLocal(typeof(Type));
208 LocalBuilder value = proxyCtorIL.DeclareLocal(typeof(object));
209 LocalBuilder usesExportedMD = proxyCtorIL.DeclareLocal(typeof(bool));
211 Label tryConstructView = proxyCtorIL.BeginExceptionBlock();
213 // Implement interface properties
214 foreach (PropertyInfo propertyInfo in viewType.GetAllProperties())
216 string fieldName = string.Format(CultureInfo.InvariantCulture, "_{0}_{1}", propertyInfo.Name, Guid.NewGuid());
218 // Cache names and type for exception
219 string propertyName = string.Format(CultureInfo.InvariantCulture, "{0}", propertyInfo.Name);
221 Type[] propertyTypeArguments = new Type[] { propertyInfo.PropertyType };
222 Type[] optionalModifiers = null;
223 Type[] requiredModifiers = null;
225 #if FEATURE_ADVANCEDREFLECTION
226 // PropertyInfo does not support GetOptionalCustomModifiers and GetRequiredCustomModifiers on Silverlight
227 optionalModifiers = propertyInfo.GetOptionalCustomModifiers();
228 requiredModifiers = propertyInfo.GetRequiredCustomModifiers();
229 Array.Reverse(optionalModifiers);
230 Array.Reverse(requiredModifiers);
231 #endif //FEATURE_ADVANCEDREFLECTION
234 FieldBuilder proxyFieldBuilder = proxyTypeBuilder.DefineField(
236 propertyInfo.PropertyType,
237 FieldAttributes.Private);
240 PropertyBuilder proxyPropertyBuilder = proxyTypeBuilder.DefineProperty(
242 PropertyAttributes.None,
243 propertyInfo.PropertyType,
244 propertyTypeArguments);
246 // Generate constructor code for retrieving the metadata value and setting the field
247 Label tryCastValue = proxyCtorIL.BeginExceptionBlock();
248 Label innerTryCastValue;
250 DefaultValueAttribute[] attrs = propertyInfo.GetAttributes<DefaultValueAttribute>(false);
253 innerTryCastValue = proxyCtorIL.BeginExceptionBlock();
256 // In constructor set the backing field with the value from the dictionary
257 Label doneGettingDefaultValue = proxyCtorIL.DefineLabel();
258 GenerateLocalAssignmentFromFlag(proxyCtorIL, usesExportedMD, true);
260 proxyCtorIL.Emit(OpCodes.Ldarg_1);
261 proxyCtorIL.Emit(OpCodes.Ldstr, propertyInfo.Name);
262 proxyCtorIL.Emit(OpCodes.Ldloca, value);
263 proxyCtorIL.Emit(OpCodes.Callvirt, _mdvDictionaryTryGet);
264 proxyCtorIL.Emit(OpCodes.Brtrue, doneGettingDefaultValue);
266 proxyCtorIL.GenerateLocalAssignmentFromFlag(usesExportedMD, false);
267 proxyCtorIL.GenerateLocalAssignmentFromDefaultAttribute(attrs, value);
269 proxyCtorIL.MarkLabel(doneGettingDefaultValue);
270 proxyCtorIL.GenerateFieldAssignmentFromLocalValue(value, proxyFieldBuilder);
271 proxyCtorIL.Emit(OpCodes.Leave, tryCastValue);
273 // catch blocks for innerTryCastValue start here
274 if (attrs.Length > 0)
276 proxyCtorIL.BeginCatchBlock(typeof(InvalidCastException));
278 Label notUsesExportedMd = proxyCtorIL.DefineLabel();
279 proxyCtorIL.Emit(OpCodes.Ldloc, usesExportedMD);
280 proxyCtorIL.Emit(OpCodes.Brtrue, notUsesExportedMd);
281 proxyCtorIL.Emit(OpCodes.Rethrow);
282 proxyCtorIL.MarkLabel(notUsesExportedMd);
283 proxyCtorIL.GenerateLocalAssignmentFromDefaultAttribute(attrs, value);
284 proxyCtorIL.GenerateFieldAssignmentFromLocalValue(value, proxyFieldBuilder);
286 proxyCtorIL.EndExceptionBlock();
289 // catch blocks for tryCast start here
290 proxyCtorIL.BeginCatchBlock(typeof(NullReferenceException));
292 proxyCtorIL.Emit(OpCodes.Stloc, exception);
294 proxyCtorIL.GetExceptionDataAndStoreInLocal(exception, exceptionData);
295 proxyCtorIL.AddItemToLocalDictionary(exceptionData, MetadataItemKey, propertyName);
296 proxyCtorIL.AddItemToLocalDictionary(exceptionData, MetadataItemTargetType, propertyInfo.PropertyType);
297 proxyCtorIL.Emit(OpCodes.Rethrow);
300 proxyCtorIL.BeginCatchBlock(typeof(InvalidCastException));
302 proxyCtorIL.Emit(OpCodes.Stloc, exception);
304 proxyCtorIL.GetExceptionDataAndStoreInLocal(exception, exceptionData);
305 proxyCtorIL.AddItemToLocalDictionary(exceptionData, MetadataItemKey, propertyName);
306 proxyCtorIL.AddItemToLocalDictionary(exceptionData, MetadataItemTargetType, propertyInfo.PropertyType);
307 proxyCtorIL.Emit(OpCodes.Rethrow);
310 proxyCtorIL.EndExceptionBlock();
312 if (propertyInfo.CanWrite)
314 // The MetadataView '{0}' is invalid because property '{1}' has a property set method.
315 throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture,
316 Strings.InvalidSetterOnMetadataField,
320 if (propertyInfo.CanRead)
322 // Generate "get" method implementation.
323 MethodBuilder getMethodBuilder = proxyTypeBuilder.DefineMethod(
324 string.Format(CultureInfo.InvariantCulture, "get_{0}", propertyName),
325 MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final,
326 CallingConventions.HasThis,
327 propertyInfo.PropertyType,
330 Type.EmptyTypes, null, null);
332 proxyTypeBuilder.DefineMethodOverride(getMethodBuilder, propertyInfo.GetGetMethod());
333 #if FEATURE_CAS_APTCA
334 if(!viewType.IsSecurityTransparent)
336 getMethodBuilder.SetCustomAttribute(_securityCriticalBuilder);
338 #endif //FEATURE_CAS_APTCA
339 ILGenerator getMethodIL = getMethodBuilder.GetILGenerator();
340 getMethodIL.Emit(OpCodes.Ldarg_0);
341 getMethodIL.Emit(OpCodes.Ldfld, proxyFieldBuilder);
342 getMethodIL.Emit(OpCodes.Ret);
344 proxyPropertyBuilder.SetGetMethod(getMethodBuilder);
348 proxyCtorIL.Emit(OpCodes.Leave, tryConstructView);
350 // catch blocks for constructView start here
351 proxyCtorIL.BeginCatchBlock(typeof(NullReferenceException));
353 proxyCtorIL.Emit(OpCodes.Stloc, exception);
355 proxyCtorIL.GetExceptionDataAndStoreInLocal(exception, exceptionData);
356 proxyCtorIL.AddItemToLocalDictionary(exceptionData, MetadataViewType, viewType);
357 proxyCtorIL.Emit(OpCodes.Rethrow);
359 proxyCtorIL.BeginCatchBlock(typeof(InvalidCastException));
361 proxyCtorIL.Emit(OpCodes.Stloc, exception);
363 proxyCtorIL.GetExceptionDataAndStoreInLocal(exception, exceptionData);
364 proxyCtorIL.Emit(OpCodes.Ldloc, value);
365 proxyCtorIL.Emit(OpCodes.Call, ObjectGetType);
366 proxyCtorIL.Emit(OpCodes.Stloc, sourceType);
367 proxyCtorIL.AddItemToLocalDictionary(exceptionData, MetadataViewType, viewType);
368 proxyCtorIL.AddLocalToLocalDictionary(exceptionData, MetadataItemSourceType, sourceType);
369 proxyCtorIL.AddLocalToLocalDictionary(exceptionData, MetadataItemValue, value);
370 proxyCtorIL.Emit(OpCodes.Rethrow);
372 proxyCtorIL.EndExceptionBlock();
374 // Finished implementing interface and constructor
375 proxyCtorIL.Emit(OpCodes.Ret);
376 proxyType = proxyTypeBuilder.CreateType();