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
;
15 namespace System
.ComponentModel
.Composition
17 // // Assume TMetadataView is
20 // // public typeRecord1 Record1 { get; }
21 // // public typeRecord2 Record2 { get; }
22 // // public typeRecord3 Record3 { get; }
23 // // public typeRecord4 Record4 { get; }
25 // // The class to be generated will look approximately like:
26 // public class __Foo__MedataViewProxy : TMetadataView
28 // public __Foo__MedataViewProxy (IDictionary<string, object> metadata)
30 // if(metadata == null)
32 // throw InvalidArgumentException("metadata");
36 // Record1 = (typeRecord1)Record1;
37 // Record2 = (typeRecord1)Record2;
38 // Record3 = (typeRecord1)Record3;
39 // Record4 = (typeRecord1)Record4;
41 // catch(InvalidCastException ice)
43 // //Annotate exception .Data with diagnostic info
45 // catch(NulLReferenceException ice)
47 // //Annotate exception .Data with diagnostic info
51 // public typeRecord1 Record1 { get; }
52 // public typeRecord2 Record2 { get; }
53 // public typeRecord3 Record3 { get; }
54 // public typeRecord4 Record4 { get; }
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
);
82 using (new ReadLock(_lock
))
84 foundProxy
= _proxies
.TryGetValue(viewType
, out proxyType
);
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
);
97 proxyType
= GenerateInterfaceViewProxyType(viewType
);
98 Assumes
.NotNull(proxyType
);
100 _proxies
.Add(viewType
, 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
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
,
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;
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
);
179 FieldBuilder proxyFieldBuilder
= proxyTypeBuilder
.DefineField(
181 propertyInfo
.PropertyType
,
182 FieldAttributes
.Private
);
185 PropertyBuilder proxyPropertyBuilder
= proxyTypeBuilder
.DefineProperty(
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);
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
,
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
,
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();