1 // -----------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 // -----------------------------------------------------------------------
5 using System.Collections.Generic;
6 using System.ComponentModel;
7 using System.Globalization;
9 using System.Reflection;
10 using System.Threading;
11 using Microsoft.Internal;
12 using System.Reflection.Emit;
13 using System.Collections;
14 using System.Security;
16 namespace System.ComponentModel.Composition
18 // // Assume TMetadataView is
21 // // public typeRecord1 Record1 { get; }
22 // // public typeRecord2 Record2 { get; }
23 // // public typeRecord3 Record3 { get; }
24 // // public typeRecord4 Record4 { get; }
26 // // The class to be generated will look approximately like:
27 // public class __Foo__MedataViewProxy : TMetadataView
29 // public __Foo__MedataViewProxy (IDictionary<string, object> metadata)
31 // if(metadata == null)
33 // throw InvalidArgumentException("metadata");
37 // Record1 = (typeRecord1)Record1;
38 // Record2 = (typeRecord1)Record2;
39 // Record3 = (typeRecord1)Record3;
40 // Record4 = (typeRecord1)Record4;
42 // catch(InvalidCastException ice)
44 // //Annotate exception .Data with diagnostic info
46 // catch(NulLReferenceException ice)
48 // //Annotate exception .Data with diagnostic info
52 // public typeRecord1 Record1 { get; }
53 // public typeRecord2 Record2 { get; }
54 // public typeRecord3 Record3 { get; }
55 // public typeRecord4 Record4 { get; }
57 internal static class MetadataViewGenerator
59 public const string MetadataViewType = "MetadataViewType";
60 public const string MetadataItemKey = "MetadataItemKey";
61 public const string MetadataItemTargetType = "MetadataItemTargetType";
62 public const string MetadataItemSourceType = "MetadataItemSourceType";
63 public const string MetadataItemValue = "MetadataItemValue";
65 private static Lock _lock = new Lock();
66 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()));
69 private static ModuleBuilder criticalProxyModuleBuilder;
70 #endif //FEATURE_CAS_APTCA
71 private static ModuleBuilder transparentProxyModuleBuilder;
73 private static Type[] CtorArgumentTypes = new Type[] { typeof(IDictionary<string, object>) };
74 private static MethodInfo _mdvDictionaryTryGet = CtorArgumentTypes[0].GetMethod("TryGetValue");
75 private static readonly MethodInfo ObjectGetType = typeof(object).GetMethod("GetType", Type.EmptyTypes);
77 private static CustomAttributeBuilder _securityCriticalBuilder =
78 new CustomAttributeBuilder(typeof(SecurityCriticalAttribute).GetConstructor(Type.EmptyTypes), new object[0]);
79 #endif //FEATURE_CAS_APTCA
81 private static AssemblyBuilder CreateProxyAssemblyBuilder(ConstructorInfo constructorInfo)
84 object[] args = new object[0];
85 CustomAttributeBuilder accessAttribute = new CustomAttributeBuilder(constructorInfo, args);
86 CustomAttributeBuilder[] attributes = { accessAttribute };
87 return AppDomain.CurrentDomain.DefineDynamicAssembly(ProxyAssemblyName, AssemblyBuilderAccess.Run, attributes, SecurityContextSource.CurrentAppDomain);
89 return AppDomain.CurrentDomain.DefineDynamicAssembly(ProxyAssemblyName, AssemblyBuilderAccess.Run);
90 #endif //FEATURE_CAS_APTCA
93 // Must be called with _lock held
94 private static ModuleBuilder GetProxyModuleBuilder(bool requiresCritical)
99 // Needed a critical modulebuilder so find or make it
100 if (criticalProxyModuleBuilder == null)
102 var assemblyBuilder = CreateProxyAssemblyBuilder(typeof(AllowPartiallyTrustedCallersAttribute).GetConstructor(Type.EmptyTypes));
103 criticalProxyModuleBuilder = assemblyBuilder.DefineDynamicModule("MetadataViewProxiesModule");
105 return criticalProxyModuleBuilder;
107 #endif //FEATURE_CAS_APTCA
108 if (transparentProxyModuleBuilder == null)
110 // make a new assemblybuilder and modulebuilder
111 var assemblyBuilder = CreateProxyAssemblyBuilder(typeof(SecurityTransparentAttribute).GetConstructor(Type.EmptyTypes));
112 transparentProxyModuleBuilder = assemblyBuilder.DefineDynamicModule("MetadataViewProxiesModule");
115 return transparentProxyModuleBuilder;
118 public static Type GenerateView(Type viewType)
120 Assumes.NotNull(viewType);
121 Assumes.IsTrue(viewType.IsInterface);
126 using (new ReadLock(_lock))
128 foundProxy = _proxies.TryGetValue(viewType, out proxyType);
134 // Try again under a write lock if still none generate the proxy
135 Type generatedProxyType = GenerateInterfaceViewProxyType(viewType);
136 Assumes.NotNull(generatedProxyType);
138 using (new WriteLock(_lock))
140 if (!_proxies.TryGetValue(viewType, out proxyType))
142 proxyType = generatedProxyType;
143 _proxies.Add(viewType, proxyType);
150 private static void GenerateLocalAssignmentFromDefaultAttribute(this ILGenerator IL, DefaultValueAttribute[] attrs, LocalBuilder local)
152 if (attrs.Length > 0)
154 DefaultValueAttribute defaultAttribute = attrs[0];
155 IL.LoadValue(defaultAttribute.Value);
156 if ((defaultAttribute.Value != null) && (defaultAttribute.Value.GetType().IsValueType))
158 IL.Emit(OpCodes.Box, defaultAttribute.Value.GetType());
160 IL.Emit(OpCodes.Stloc, local);
164 private static void GenerateFieldAssignmentFromLocalValue(this ILGenerator IL, LocalBuilder local, FieldBuilder field)
166 IL.Emit(OpCodes.Ldarg_0);
167 IL.Emit(OpCodes.Ldloc, local);
168 IL.Emit(field.FieldType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, field.FieldType);
169 IL.Emit(OpCodes.Stfld, field);
172 private static void GenerateLocalAssignmentFromFlag(this ILGenerator IL, LocalBuilder local, bool flag)
174 IL.Emit(flag ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
175 IL.Emit(OpCodes.Stloc, local);
178 // This must be called with _readerWriterLock held for Write
179 private static Type GenerateInterfaceViewProxyType(Type viewType)
181 // View type is an interface let's cook an implementation
183 TypeBuilder proxyTypeBuilder;
184 Type[] interfaces = { viewType };
185 bool requiresCritical = false;
186 #if FEATURE_CAS_APTCA
187 requiresCritical = !viewType.IsSecurityTransparent;
188 #endif //FEATURE_CAS_APTCA
190 var proxyModuleBuilder = GetProxyModuleBuilder(requiresCritical);
191 proxyTypeBuilder = proxyModuleBuilder.DefineType(
192 string.Format(CultureInfo.InvariantCulture, "_proxy_{0}_{1}", viewType.FullName, Guid.NewGuid()),
193 TypeAttributes.Public,
196 #if FEATURE_CAS_APTCA
197 if (requiresCritical)
199 proxyTypeBuilder.SetCustomAttribute(_securityCriticalBuilder);
201 #endif //FEATURE_CAS_APTCA
202 // Implement Constructor
203 ILGenerator proxyCtorIL = proxyTypeBuilder.CreateGeneratorForPublicConstructor(CtorArgumentTypes);
204 LocalBuilder exception = proxyCtorIL.DeclareLocal(typeof(Exception));
205 LocalBuilder exceptionData = proxyCtorIL.DeclareLocal(typeof(IDictionary));
206 LocalBuilder sourceType = proxyCtorIL.DeclareLocal(typeof(Type));
207 LocalBuilder value = proxyCtorIL.DeclareLocal(typeof(object));
208 LocalBuilder usesExportedMD = proxyCtorIL.DeclareLocal(typeof(bool));
210 Label tryConstructView = proxyCtorIL.BeginExceptionBlock();
212 // Implement interface properties
213 foreach (PropertyInfo propertyInfo in viewType.GetAllProperties())
215 string fieldName = string.Format(CultureInfo.InvariantCulture, "_{0}_{1}", propertyInfo.Name, Guid.NewGuid());
217 // Cache names and type for exception
218 string propertyName = string.Format(CultureInfo.InvariantCulture, "{0}", propertyInfo.Name);
220 Type[] propertyTypeArguments = new Type[] { propertyInfo.PropertyType };
221 Type[] optionalModifiers = null;
222 Type[] requiredModifiers = null;
224 #if FEATURE_ADVANCEDREFLECTION
225 // PropertyInfo does not support GetOptionalCustomModifiers and GetRequiredCustomModifiers on Silverlight
226 optionalModifiers = propertyInfo.GetOptionalCustomModifiers();
227 requiredModifiers = propertyInfo.GetRequiredCustomModifiers();
228 Array.Reverse(optionalModifiers);
229 Array.Reverse(requiredModifiers);
230 #endif //FEATURE_ADVANCEDREFLECTION
233 FieldBuilder proxyFieldBuilder = proxyTypeBuilder.DefineField(
235 propertyInfo.PropertyType,
236 FieldAttributes.Private);
239 PropertyBuilder proxyPropertyBuilder = proxyTypeBuilder.DefineProperty(
241 PropertyAttributes.None,
242 propertyInfo.PropertyType,
243 propertyTypeArguments);
245 // Generate constructor code for retrieving the metadata value and setting the field
246 Label tryCastValue = proxyCtorIL.BeginExceptionBlock();
247 Label innerTryCastValue;
249 DefaultValueAttribute[] attrs = propertyInfo.GetAttributes<DefaultValueAttribute>(false);
252 innerTryCastValue = proxyCtorIL.BeginExceptionBlock();
255 // In constructor set the backing field with the value from the dictionary
256 Label doneGettingDefaultValue = proxyCtorIL.DefineLabel();
257 GenerateLocalAssignmentFromFlag(proxyCtorIL, usesExportedMD, true);
259 proxyCtorIL.Emit(OpCodes.Ldarg_1);
260 proxyCtorIL.Emit(OpCodes.Ldstr, propertyInfo.Name);
261 proxyCtorIL.Emit(OpCodes.Ldloca, value);
262 proxyCtorIL.Emit(OpCodes.Callvirt, _mdvDictionaryTryGet);
263 proxyCtorIL.Emit(OpCodes.Brtrue, doneGettingDefaultValue);
265 proxyCtorIL.GenerateLocalAssignmentFromFlag(usesExportedMD, false);
266 proxyCtorIL.GenerateLocalAssignmentFromDefaultAttribute(attrs, value);
268 proxyCtorIL.MarkLabel(doneGettingDefaultValue);
269 proxyCtorIL.GenerateFieldAssignmentFromLocalValue(value, proxyFieldBuilder);
270 proxyCtorIL.Emit(OpCodes.Leave, tryCastValue);
272 // catch blocks for innerTryCastValue start here
273 if (attrs.Length > 0)
275 proxyCtorIL.BeginCatchBlock(typeof(InvalidCastException));
277 Label notUsesExportedMd = proxyCtorIL.DefineLabel();
278 proxyCtorIL.Emit(OpCodes.Ldloc, usesExportedMD);
279 proxyCtorIL.Emit(OpCodes.Brtrue, notUsesExportedMd);
280 proxyCtorIL.Emit(OpCodes.Rethrow);
281 proxyCtorIL.MarkLabel(notUsesExportedMd);
282 proxyCtorIL.GenerateLocalAssignmentFromDefaultAttribute(attrs, value);
283 proxyCtorIL.GenerateFieldAssignmentFromLocalValue(value, proxyFieldBuilder);
285 proxyCtorIL.EndExceptionBlock();
288 // catch blocks for tryCast start here
289 proxyCtorIL.BeginCatchBlock(typeof(NullReferenceException));
291 proxyCtorIL.Emit(OpCodes.Stloc, exception);
293 proxyCtorIL.GetExceptionDataAndStoreInLocal(exception, exceptionData);
294 proxyCtorIL.AddItemToLocalDictionary(exceptionData, MetadataItemKey, propertyName);
295 proxyCtorIL.AddItemToLocalDictionary(exceptionData, MetadataItemTargetType, propertyInfo.PropertyType);
296 proxyCtorIL.Emit(OpCodes.Rethrow);
299 proxyCtorIL.BeginCatchBlock(typeof(InvalidCastException));
301 proxyCtorIL.Emit(OpCodes.Stloc, exception);
303 proxyCtorIL.GetExceptionDataAndStoreInLocal(exception, exceptionData);
304 proxyCtorIL.AddItemToLocalDictionary(exceptionData, MetadataItemKey, propertyName);
305 proxyCtorIL.AddItemToLocalDictionary(exceptionData, MetadataItemTargetType, propertyInfo.PropertyType);
306 proxyCtorIL.Emit(OpCodes.Rethrow);
309 proxyCtorIL.EndExceptionBlock();
311 if (propertyInfo.CanWrite)
313 // The MetadataView '{0}' is invalid because property '{1}' has a property set method.
314 throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture,
315 Strings.InvalidSetterOnMetadataField,
319 if (propertyInfo.CanRead)
321 // Generate "get" method implementation.
322 MethodBuilder getMethodBuilder = proxyTypeBuilder.DefineMethod(
323 string.Format(CultureInfo.InvariantCulture, "get_{0}", propertyName),
324 MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final,
325 CallingConventions.HasThis,
326 propertyInfo.PropertyType,
329 Type.EmptyTypes, null, null);
331 proxyTypeBuilder.DefineMethodOverride(getMethodBuilder, propertyInfo.GetGetMethod());
332 #if FEATURE_CAS_APTCA
333 if(!viewType.IsSecurityTransparent)
335 getMethodBuilder.SetCustomAttribute(_securityCriticalBuilder);
337 #endif //FEATURE_CAS_APTCA
338 ILGenerator getMethodIL = getMethodBuilder.GetILGenerator();
339 getMethodIL.Emit(OpCodes.Ldarg_0);
340 getMethodIL.Emit(OpCodes.Ldfld, proxyFieldBuilder);
341 getMethodIL.Emit(OpCodes.Ret);
343 proxyPropertyBuilder.SetGetMethod(getMethodBuilder);
347 proxyCtorIL.Emit(OpCodes.Leave, tryConstructView);
349 // catch blocks for constructView start here
350 proxyCtorIL.BeginCatchBlock(typeof(NullReferenceException));
352 proxyCtorIL.Emit(OpCodes.Stloc, exception);
354 proxyCtorIL.GetExceptionDataAndStoreInLocal(exception, exceptionData);
355 proxyCtorIL.AddItemToLocalDictionary(exceptionData, MetadataViewType, viewType);
356 proxyCtorIL.Emit(OpCodes.Rethrow);
358 proxyCtorIL.BeginCatchBlock(typeof(InvalidCastException));
360 proxyCtorIL.Emit(OpCodes.Stloc, exception);
362 proxyCtorIL.GetExceptionDataAndStoreInLocal(exception, exceptionData);
363 proxyCtorIL.Emit(OpCodes.Ldloc, value);
364 proxyCtorIL.Emit(OpCodes.Call, ObjectGetType);
365 proxyCtorIL.Emit(OpCodes.Stloc, sourceType);
366 proxyCtorIL.AddItemToLocalDictionary(exceptionData, MetadataViewType, viewType);
367 proxyCtorIL.AddLocalToLocalDictionary(exceptionData, MetadataItemSourceType, sourceType);
368 proxyCtorIL.AddLocalToLocalDictionary(exceptionData, MetadataItemValue, value);
369 proxyCtorIL.Emit(OpCodes.Rethrow);
371 proxyCtorIL.EndExceptionBlock();
373 // Finished implementing interface and constructor
374 proxyCtorIL.Emit(OpCodes.Ret);
375 proxyType = proxyTypeBuilder.CreateType();