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
;
16 using Lock
= Microsoft
.Internal
.Lock
;
18 namespace System
.ComponentModel
.Composition
20 // // Assume TMetadataView is
23 // // public typeRecord1 Record1 { get; }
24 // // public typeRecord2 Record2 { get; }
25 // // public typeRecord3 Record3 { get; }
26 // // public typeRecord4 Record4 { get; }
28 // // The class to be generated will look approximately like:
29 // public class __Foo__MedataViewProxy : TMetadataView
31 // public __Foo__MedataViewProxy (IDictionary<string, object> metadata)
33 // if(metadata == null)
35 // throw InvalidArgumentException("metadata");
39 // Record1 = (typeRecord1)Record1;
40 // Record2 = (typeRecord1)Record2;
41 // Record3 = (typeRecord1)Record3;
42 // Record4 = (typeRecord1)Record4;
44 // catch(InvalidCastException ice)
46 // //Annotate exception .Data with diagnostic info
48 // catch(NulLReferenceException ice)
50 // //Annotate exception .Data with diagnostic info
54 // public typeRecord1 Record1 { get; }
55 // public typeRecord2 Record2 { get; }
56 // public typeRecord3 Record3 { get; }
57 // public typeRecord4 Record4 { get; }
59 internal static class MetadataViewGenerator
61 public const string MetadataViewType
= "MetadataViewType";
62 public const string MetadataItemKey
= "MetadataItemKey";
63 public const string MetadataItemTargetType
= "MetadataItemTargetType";
64 public const string MetadataItemSourceType
= "MetadataItemSourceType";
65 public const string MetadataItemValue
= "MetadataItemValue";
67 private static Lock _lock
= new Lock();
68 private static Dictionary
<Type
, Type
> _proxies
= new Dictionary
<Type
, Type
>();
69 private static AssemblyName ProxyAssemblyName
= new AssemblyName(string.Format(CultureInfo
.InvariantCulture
, "MetadataViewProxies_{0}", Guid
.NewGuid()));
71 private static ModuleBuilder criticalProxyModuleBuilder
;
72 #endif //FEATURE_CAS_APTCA
73 private static ModuleBuilder transparentProxyModuleBuilder
;
75 private static Type
[] CtorArgumentTypes
= new Type
[] { typeof(IDictionary<string, object>) }
;
76 private static MethodInfo _mdvDictionaryTryGet
= CtorArgumentTypes
[0].GetMethod("TryGetValue");
77 private static readonly MethodInfo ObjectGetType
= typeof(object).GetMethod("GetType", Type
.EmptyTypes
);
79 private static CustomAttributeBuilder _securityCriticalBuilder
=
80 new CustomAttributeBuilder(typeof(SecurityCriticalAttribute
).GetConstructor(Type
.EmptyTypes
), new object[0]);
81 #endif //FEATURE_CAS_APTCA
83 private static AssemblyBuilder
CreateProxyAssemblyBuilder(ConstructorInfo constructorInfo
)
86 object[] args
= new object[0];
87 CustomAttributeBuilder accessAttribute
= new CustomAttributeBuilder(constructorInfo
, args
);
88 CustomAttributeBuilder
[] attributes
= { accessAttribute }
;
89 return AppDomain
.CurrentDomain
.DefineDynamicAssembly(ProxyAssemblyName
, AssemblyBuilderAccess
.Run
, attributes
, SecurityContextSource
.CurrentAppDomain
);
91 return AppDomain
.CurrentDomain
.DefineDynamicAssembly(ProxyAssemblyName
, AssemblyBuilderAccess
.Run
);
92 #endif //FEATURE_CAS_APTCA
95 // Must be called with _lock held
96 private static ModuleBuilder
GetProxyModuleBuilder(bool requiresCritical
)
101 // Needed a critical modulebuilder so find or make it
102 if (criticalProxyModuleBuilder
== null)
104 var assemblyBuilder
= CreateProxyAssemblyBuilder(typeof(AllowPartiallyTrustedCallersAttribute
).GetConstructor(Type
.EmptyTypes
));
105 criticalProxyModuleBuilder
= assemblyBuilder
.DefineDynamicModule("MetadataViewProxiesModule");
107 return criticalProxyModuleBuilder
;
109 #endif //FEATURE_CAS_APTCA
110 if (transparentProxyModuleBuilder
== null)
112 // make a new assemblybuilder and modulebuilder
113 var assemblyBuilder
= CreateProxyAssemblyBuilder(typeof(SecurityTransparentAttribute
).GetConstructor(Type
.EmptyTypes
));
114 transparentProxyModuleBuilder
= assemblyBuilder
.DefineDynamicModule("MetadataViewProxiesModule");
117 return transparentProxyModuleBuilder
;
120 public static Type
GenerateView(Type viewType
)
122 Assumes
.NotNull(viewType
);
123 Assumes
.IsTrue(viewType
.IsInterface
);
128 using (new ReadLock(_lock
))
130 foundProxy
= _proxies
.TryGetValue(viewType
, out proxyType
);
136 // Try again under a write lock if still none generate the proxy
137 Type generatedProxyType
= GenerateInterfaceViewProxyType(viewType
);
138 Assumes
.NotNull(generatedProxyType
);
140 using (new WriteLock(_lock
))
142 if (!_proxies
.TryGetValue(viewType
, out proxyType
))
144 proxyType
= generatedProxyType
;
145 _proxies
.Add(viewType
, proxyType
);
152 private static void GenerateLocalAssignmentFromDefaultAttribute(this ILGenerator IL
, DefaultValueAttribute
[] attrs
, LocalBuilder local
)
154 if (attrs
.Length
> 0)
156 DefaultValueAttribute defaultAttribute
= attrs
[0];
157 IL
.LoadValue(defaultAttribute
.Value
);
158 if ((defaultAttribute
.Value
!= null) && (defaultAttribute
.Value
.GetType().IsValueType
))
160 IL
.Emit(OpCodes
.Box
, defaultAttribute
.Value
.GetType());
162 IL
.Emit(OpCodes
.Stloc
, local
);
166 private static void GenerateFieldAssignmentFromLocalValue(this ILGenerator IL
, LocalBuilder local
, FieldBuilder field
)
168 IL
.Emit(OpCodes
.Ldarg_0
);
169 IL
.Emit(OpCodes
.Ldloc
, local
);
170 IL
.Emit(field
.FieldType
.IsValueType
? OpCodes
.Unbox_Any
: OpCodes
.Castclass
, field
.FieldType
);
171 IL
.Emit(OpCodes
.Stfld
, field
);
174 private static void GenerateLocalAssignmentFromFlag(this ILGenerator IL
, LocalBuilder local
, bool flag
)
176 IL
.Emit(flag
? OpCodes
.Ldc_I4_1
: OpCodes
.Ldc_I4_0
);
177 IL
.Emit(OpCodes
.Stloc
, local
);
180 // This must be called with _readerWriterLock held for Write
181 private static Type
GenerateInterfaceViewProxyType(Type viewType
)
183 // View type is an interface let's cook an implementation
185 TypeBuilder proxyTypeBuilder
;
186 Type
[] interfaces
= { viewType }
;
187 bool requiresCritical
= false;
188 #if FEATURE_CAS_APTCA
189 requiresCritical
= !viewType
.IsSecurityTransparent
;
190 #endif //FEATURE_CAS_APTCA
192 var proxyModuleBuilder
= GetProxyModuleBuilder(requiresCritical
);
193 proxyTypeBuilder
= proxyModuleBuilder
.DefineType(
194 string.Format(CultureInfo
.InvariantCulture
, "_proxy_{0}_{1}", viewType
.FullName
, Guid
.NewGuid()),
195 TypeAttributes
.Public
,
198 #if FEATURE_CAS_APTCA
199 if (requiresCritical
)
201 proxyTypeBuilder
.SetCustomAttribute(_securityCriticalBuilder
);
203 #endif //FEATURE_CAS_APTCA
204 // Implement Constructor
205 ILGenerator proxyCtorIL
= proxyTypeBuilder
.CreateGeneratorForPublicConstructor(CtorArgumentTypes
);
206 LocalBuilder exception
= proxyCtorIL
.DeclareLocal(typeof(Exception
));
207 LocalBuilder exceptionData
= proxyCtorIL
.DeclareLocal(typeof(IDictionary
));
208 LocalBuilder sourceType
= proxyCtorIL
.DeclareLocal(typeof(Type
));
209 LocalBuilder
value = proxyCtorIL
.DeclareLocal(typeof(object));
210 LocalBuilder usesExportedMD
= proxyCtorIL
.DeclareLocal(typeof(bool));
212 Label tryConstructView
= proxyCtorIL
.BeginExceptionBlock();
214 // Implement interface properties
215 foreach (PropertyInfo propertyInfo
in viewType
.GetAllProperties())
217 string fieldName
= string.Format(CultureInfo
.InvariantCulture
, "_{0}_{1}", propertyInfo
.Name
, Guid
.NewGuid());
219 // Cache names and type for exception
220 string propertyName
= string.Format(CultureInfo
.InvariantCulture
, "{0}", propertyInfo
.Name
);
222 Type
[] propertyTypeArguments
= new Type
[] { propertyInfo.PropertyType }
;
223 Type
[] optionalModifiers
= null;
224 Type
[] requiredModifiers
= null;
226 #if FEATURE_ADVANCEDREFLECTION
227 // PropertyInfo does not support GetOptionalCustomModifiers and GetRequiredCustomModifiers on Silverlight
228 optionalModifiers
= propertyInfo
.GetOptionalCustomModifiers();
229 requiredModifiers
= propertyInfo
.GetRequiredCustomModifiers();
230 Array
.Reverse(optionalModifiers
);
231 Array
.Reverse(requiredModifiers
);
232 #endif //FEATURE_ADVANCEDREFLECTION
235 FieldBuilder proxyFieldBuilder
= proxyTypeBuilder
.DefineField(
237 propertyInfo
.PropertyType
,
238 FieldAttributes
.Private
);
241 PropertyBuilder proxyPropertyBuilder
= proxyTypeBuilder
.DefineProperty(
243 PropertyAttributes
.None
,
244 propertyInfo
.PropertyType
,
245 propertyTypeArguments
);
247 // Generate constructor code for retrieving the metadata value and setting the field
248 Label tryCastValue
= proxyCtorIL
.BeginExceptionBlock();
249 Label innerTryCastValue
;
251 DefaultValueAttribute
[] attrs
= propertyInfo
.GetAttributes
<DefaultValueAttribute
>(false);
254 innerTryCastValue
= proxyCtorIL
.BeginExceptionBlock();
257 // In constructor set the backing field with the value from the dictionary
258 Label doneGettingDefaultValue
= proxyCtorIL
.DefineLabel();
259 GenerateLocalAssignmentFromFlag(proxyCtorIL
, usesExportedMD
, true);
261 proxyCtorIL
.Emit(OpCodes
.Ldarg_1
);
262 proxyCtorIL
.Emit(OpCodes
.Ldstr
, propertyInfo
.Name
);
263 proxyCtorIL
.Emit(OpCodes
.Ldloca
, value);
264 proxyCtorIL
.Emit(OpCodes
.Callvirt
, _mdvDictionaryTryGet
);
265 proxyCtorIL
.Emit(OpCodes
.Brtrue
, doneGettingDefaultValue
);
267 proxyCtorIL
.GenerateLocalAssignmentFromFlag(usesExportedMD
, false);
268 proxyCtorIL
.GenerateLocalAssignmentFromDefaultAttribute(attrs
, value);
270 proxyCtorIL
.MarkLabel(doneGettingDefaultValue
);
271 proxyCtorIL
.GenerateFieldAssignmentFromLocalValue(value, proxyFieldBuilder
);
272 proxyCtorIL
.Emit(OpCodes
.Leave
, tryCastValue
);
274 // catch blocks for innerTryCastValue start here
275 if (attrs
.Length
> 0)
277 proxyCtorIL
.BeginCatchBlock(typeof(InvalidCastException
));
279 Label notUsesExportedMd
= proxyCtorIL
.DefineLabel();
280 proxyCtorIL
.Emit(OpCodes
.Ldloc
, usesExportedMD
);
281 proxyCtorIL
.Emit(OpCodes
.Brtrue
, notUsesExportedMd
);
282 proxyCtorIL
.Emit(OpCodes
.Rethrow
);
283 proxyCtorIL
.MarkLabel(notUsesExportedMd
);
284 proxyCtorIL
.GenerateLocalAssignmentFromDefaultAttribute(attrs
, value);
285 proxyCtorIL
.GenerateFieldAssignmentFromLocalValue(value, proxyFieldBuilder
);
287 proxyCtorIL
.EndExceptionBlock();
290 // catch blocks for tryCast start here
291 proxyCtorIL
.BeginCatchBlock(typeof(NullReferenceException
));
293 proxyCtorIL
.Emit(OpCodes
.Stloc
, exception
);
295 proxyCtorIL
.GetExceptionDataAndStoreInLocal(exception
, exceptionData
);
296 proxyCtorIL
.AddItemToLocalDictionary(exceptionData
, MetadataItemKey
, propertyName
);
297 proxyCtorIL
.AddItemToLocalDictionary(exceptionData
, MetadataItemTargetType
, propertyInfo
.PropertyType
);
298 proxyCtorIL
.Emit(OpCodes
.Rethrow
);
301 proxyCtorIL
.BeginCatchBlock(typeof(InvalidCastException
));
303 proxyCtorIL
.Emit(OpCodes
.Stloc
, exception
);
305 proxyCtorIL
.GetExceptionDataAndStoreInLocal(exception
, exceptionData
);
306 proxyCtorIL
.AddItemToLocalDictionary(exceptionData
, MetadataItemKey
, propertyName
);
307 proxyCtorIL
.AddItemToLocalDictionary(exceptionData
, MetadataItemTargetType
, propertyInfo
.PropertyType
);
308 proxyCtorIL
.Emit(OpCodes
.Rethrow
);
311 proxyCtorIL
.EndExceptionBlock();
313 if (propertyInfo
.CanWrite
)
315 // The MetadataView '{0}' is invalid because property '{1}' has a property set method.
316 throw new NotSupportedException(string.Format(CultureInfo
.CurrentCulture
,
317 Strings
.InvalidSetterOnMetadataField
,
321 if (propertyInfo
.CanRead
)
323 // Generate "get" method implementation.
324 MethodBuilder getMethodBuilder
= proxyTypeBuilder
.DefineMethod(
325 string.Format(CultureInfo
.InvariantCulture
, "get_{0}", propertyName
),
326 MethodAttributes
.Public
| MethodAttributes
.HideBySig
| MethodAttributes
.SpecialName
| MethodAttributes
.NewSlot
| MethodAttributes
.Virtual
| MethodAttributes
.Final
,
327 CallingConventions
.HasThis
,
328 propertyInfo
.PropertyType
,
331 Type
.EmptyTypes
, null, null);
333 proxyTypeBuilder
.DefineMethodOverride(getMethodBuilder
, propertyInfo
.GetGetMethod());
334 #if FEATURE_CAS_APTCA
335 if(!viewType
.IsSecurityTransparent
)
337 getMethodBuilder
.SetCustomAttribute(_securityCriticalBuilder
);
339 #endif //FEATURE_CAS_APTCA
340 ILGenerator getMethodIL
= getMethodBuilder
.GetILGenerator();
341 getMethodIL
.Emit(OpCodes
.Ldarg_0
);
342 getMethodIL
.Emit(OpCodes
.Ldfld
, proxyFieldBuilder
);
343 getMethodIL
.Emit(OpCodes
.Ret
);
345 proxyPropertyBuilder
.SetGetMethod(getMethodBuilder
);
349 proxyCtorIL
.Emit(OpCodes
.Leave
, tryConstructView
);
351 // catch blocks for constructView start here
352 proxyCtorIL
.BeginCatchBlock(typeof(NullReferenceException
));
354 proxyCtorIL
.Emit(OpCodes
.Stloc
, exception
);
356 proxyCtorIL
.GetExceptionDataAndStoreInLocal(exception
, exceptionData
);
357 proxyCtorIL
.AddItemToLocalDictionary(exceptionData
, MetadataViewType
, viewType
);
358 proxyCtorIL
.Emit(OpCodes
.Rethrow
);
360 proxyCtorIL
.BeginCatchBlock(typeof(InvalidCastException
));
362 proxyCtorIL
.Emit(OpCodes
.Stloc
, exception
);
364 proxyCtorIL
.GetExceptionDataAndStoreInLocal(exception
, exceptionData
);
365 proxyCtorIL
.Emit(OpCodes
.Ldloc
, value);
366 proxyCtorIL
.Emit(OpCodes
.Call
, ObjectGetType
);
367 proxyCtorIL
.Emit(OpCodes
.Stloc
, sourceType
);
368 proxyCtorIL
.AddItemToLocalDictionary(exceptionData
, MetadataViewType
, viewType
);
369 proxyCtorIL
.AddLocalToLocalDictionary(exceptionData
, MetadataItemSourceType
, sourceType
);
370 proxyCtorIL
.AddLocalToLocalDictionary(exceptionData
, MetadataItemValue
, value);
371 proxyCtorIL
.Emit(OpCodes
.Rethrow
);
373 proxyCtorIL
.EndExceptionBlock();
375 // Finished implementing interface and constructor
376 proxyCtorIL
.Emit(OpCodes
.Ret
);
377 proxyType
= proxyTypeBuilder
.CreateType();