Mercurial > pub > bltoolkit
comparison Source/TypeBuilder/TypeFactory.cs @ 0:f990fcb411a9
Копия текущей версии из github
author | cin |
---|---|
date | Thu, 27 Mar 2014 21:46:09 +0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:f990fcb411a9 |
---|---|
1 using System; | |
2 using System.Collections; | |
3 using System.Collections.Generic; | |
4 using System.Diagnostics; | |
5 using System.Diagnostics.CodeAnalysis; | |
6 using System.IO; | |
7 using System.Linq.Expressions; | |
8 using System.Reflection; | |
9 using System.Reflection.Emit; | |
10 using System.Runtime.InteropServices; | |
11 using System.Security; | |
12 using System.Security.Permissions; | |
13 | |
14 using JetBrains.Annotations; | |
15 | |
16 using BLToolkit.Reflection; | |
17 using BLToolkit.Reflection.Emit; | |
18 using BLToolkit.TypeBuilder.Builders; | |
19 using BLToolkit.Properties; | |
20 #if !SILVERLIGHT | |
21 using BLToolkit.Configuration; | |
22 #endif | |
23 | |
24 namespace BLToolkit.TypeBuilder | |
25 { | |
26 public static class TypeFactory | |
27 { | |
28 static TypeFactory() | |
29 { | |
30 SealTypes = true; | |
31 | |
32 #if !SILVERLIGHT | |
33 | |
34 var section = BLToolkitSection.Instance; | |
35 | |
36 if (section != null) | |
37 { | |
38 var elm = section.TypeFactory; | |
39 | |
40 if (elm != null) | |
41 { | |
42 SaveTypes = elm.SaveTypes; | |
43 SealTypes = elm.SealTypes; | |
44 LoadTypes = elm.LoadTypes; | |
45 | |
46 SetGlobalAssembly(elm.AssemblyPath, elm.Version, elm.KeyFile); | |
47 } | |
48 } | |
49 | |
50 #endif | |
51 | |
52 #if !SILVERLIGHT | |
53 | |
54 var perm = new SecurityPermission(SecurityPermissionFlag.ControlAppDomain); | |
55 | |
56 #if FW4 | |
57 try | |
58 { | |
59 //var permissionSet = new PermissionSet(PermissionState.None); | |
60 //permissionSet.AddPermission(perm); | |
61 | |
62 //if (permissionSet.IsSubsetOf(AppDomain.CurrentDomain.PermissionSet)) | |
63 SubscribeAssemblyResolver(); | |
64 } | |
65 catch | |
66 { | |
67 } | |
68 #else | |
69 if (SecurityManager.IsGranted(perm)) | |
70 SubscribeAssemblyResolver(); | |
71 #endif | |
72 | |
73 #endif | |
74 } | |
75 | |
76 static void SubscribeAssemblyResolver() | |
77 { | |
78 #if FW4 | |
79 // This hack allows skipping FW 4.0 security check for partial trusted assemblies. | |
80 // | |
81 | |
82 var dm = new DynamicMethod("SubscribeAssemblyResolverEx", typeof(void), null); | |
83 var emit = new EmitHelper(dm.GetILGenerator()); | |
84 | |
85 emit | |
86 .call (typeof(AppDomain).GetProperty("CurrentDomain").GetGetMethod()) | |
87 .ldnull | |
88 .ldftn (typeof(TypeFactory).GetMethod("AssemblyResolver")) | |
89 .newobj (typeof(ResolveEventHandler).GetConstructor(new[] { typeof(object), typeof(IntPtr) })) | |
90 .callvirt (typeof(AppDomain).GetEvent("AssemblyResolve").GetAddMethod()) | |
91 .ret() | |
92 ; | |
93 | |
94 var setter = (Action)dm.CreateDelegate(typeof(Action)); | |
95 | |
96 setter(); | |
97 #else | |
98 AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolver; | |
99 #endif | |
100 } | |
101 | |
102 #region Create Assembly | |
103 | |
104 private static string _globalAssemblyPath; | |
105 private static string _globalAssemblyKeyFile; | |
106 private static Version _globalAssemblyVersion; | |
107 private static AssemblyBuilderHelper _globalAssembly; | |
108 | |
109 private static AssemblyBuilderHelper GlobalAssemblyBuilder | |
110 { | |
111 get | |
112 { | |
113 if (_globalAssembly == null && _globalAssemblyPath != null) | |
114 _globalAssembly = new AssemblyBuilderHelper(_globalAssemblyPath, _globalAssemblyVersion, _globalAssemblyKeyFile); | |
115 | |
116 return _globalAssembly; | |
117 } | |
118 } | |
119 | |
120 public static bool SaveTypes { get; set; } | |
121 public static bool SealTypes { get; set; } | |
122 | |
123 [SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods")] | |
124 public static void SetGlobalAssembly(string path) | |
125 { | |
126 SetGlobalAssembly(path, null, null); | |
127 } | |
128 | |
129 [SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods")] | |
130 public static void SetGlobalAssembly(string path, Version version, string keyFile) | |
131 { | |
132 if (_globalAssembly != null) | |
133 SaveGlobalAssembly(); | |
134 | |
135 if (!string.IsNullOrEmpty(path)) | |
136 _globalAssemblyPath = path; | |
137 | |
138 _globalAssemblyVersion = version; | |
139 _globalAssemblyKeyFile = keyFile; | |
140 } | |
141 | |
142 public static void SaveGlobalAssembly() | |
143 { | |
144 if (_globalAssembly != null) | |
145 { | |
146 _globalAssembly.Save(); | |
147 | |
148 WriteDebug("The global assembly saved in '{0}'.", _globalAssembly.Path); | |
149 | |
150 _globalAssembly = null; | |
151 _globalAssemblyPath = null; | |
152 _globalAssemblyVersion = null; | |
153 _globalAssemblyKeyFile = null; | |
154 } | |
155 } | |
156 | |
157 private static AssemblyBuilderHelper GetAssemblyBuilder(Type type, string suffix) | |
158 { | |
159 var ab = GlobalAssemblyBuilder; | |
160 | |
161 if (ab == null) | |
162 { | |
163 #if SILVERLIGHT | |
164 var assemblyDir = "."; | |
165 #else | |
166 var assemblyDir = AppDomain.CurrentDomain.BaseDirectory; | |
167 | |
168 // Dynamic modules are locationless, so ignore them. | |
169 // _ModuleBuilder is the base type for both | |
170 // ModuleBuilder and InternalModuleBuilder classes. | |
171 // | |
172 if (!(type.Module is _ModuleBuilder) && type.Module.FullyQualifiedName != null && type.Module.FullyQualifiedName.IndexOf('<') < 0) | |
173 assemblyDir = Path.GetDirectoryName(type.Module.FullyQualifiedName); | |
174 #endif | |
175 | |
176 var fullName = type.FullName; | |
177 | |
178 if (type.IsGenericType) | |
179 fullName = AbstractClassBuilder.GetTypeFullName(type); | |
180 | |
181 fullName = fullName.Replace('<', '_').Replace('>', '_'); | |
182 | |
183 ab = new AssemblyBuilderHelper(Path.Combine(assemblyDir, fullName + "." + suffix + ".dll")); | |
184 } | |
185 | |
186 return ab; | |
187 } | |
188 | |
189 [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] | |
190 private static void SaveAssembly(AssemblyBuilderHelper assemblyBuilder, Type type) | |
191 { | |
192 if (!SaveTypes || _globalAssembly != null) | |
193 return; | |
194 try | |
195 { | |
196 assemblyBuilder.Save(); | |
197 | |
198 WriteDebug("The '{0}' type saved in '{1}'.", | |
199 type.FullName, | |
200 assemblyBuilder.Path); | |
201 } | |
202 catch (Exception ex) | |
203 { | |
204 WriteDebug("Can't save the '{0}' assembly for the '{1}' type: {2}.", | |
205 assemblyBuilder.Path, | |
206 type.FullName, | |
207 ex.Message); | |
208 } | |
209 } | |
210 | |
211 #endregion | |
212 | |
213 #region GetType | |
214 | |
215 static readonly Dictionary<Type,IDictionary<object,Type>> _builtTypes = new Dictionary<Type,IDictionary<object,Type>>(10); | |
216 static readonly Dictionary<Assembly,Assembly> _assemblies = new Dictionary<Assembly, Assembly>(10); | |
217 | |
218 public static bool LoadTypes { get; set; } | |
219 | |
220 public static Type GetType(object hashKey, Type sourceType, ITypeBuilder typeBuilder) | |
221 { | |
222 if (hashKey == null) throw new ArgumentNullException("hashKey"); | |
223 if (sourceType == null) throw new ArgumentNullException("sourceType"); | |
224 if (typeBuilder == null) throw new ArgumentNullException("typeBuilder"); | |
225 | |
226 try | |
227 { | |
228 lock (_builtTypes) | |
229 { | |
230 Type type; | |
231 IDictionary<object,Type> builderTable; | |
232 | |
233 if (_builtTypes.TryGetValue(typeBuilder.GetType(), out builderTable)) | |
234 { | |
235 if (builderTable.TryGetValue(hashKey, out type)) | |
236 return type; | |
237 } | |
238 else | |
239 _builtTypes.Add(typeBuilder.GetType(), builderTable = new Dictionary<object,Type>()); | |
240 | |
241 if (LoadTypes) | |
242 { | |
243 var originalAssembly = sourceType.Assembly; | |
244 | |
245 Assembly extensionAssembly; | |
246 | |
247 if (!_assemblies.TryGetValue(originalAssembly, out extensionAssembly)) | |
248 { | |
249 extensionAssembly = LoadExtensionAssembly(originalAssembly); | |
250 _assemblies.Add(originalAssembly, extensionAssembly); | |
251 } | |
252 | |
253 if (extensionAssembly != null) | |
254 { | |
255 type = extensionAssembly.GetType(typeBuilder.GetTypeName()); | |
256 | |
257 if (type != null) | |
258 { | |
259 builderTable.Add(hashKey, type); | |
260 return type; | |
261 } | |
262 } | |
263 } | |
264 | |
265 var assemblyBuilder = GetAssemblyBuilder(sourceType, typeBuilder.AssemblyNameSuffix); | |
266 | |
267 type = typeBuilder.Build(assemblyBuilder); | |
268 | |
269 if (type != null) | |
270 { | |
271 builderTable.Add(hashKey, type); | |
272 SaveAssembly(assemblyBuilder, type); | |
273 } | |
274 | |
275 return type; | |
276 } | |
277 } | |
278 catch (TypeBuilderException) | |
279 { | |
280 throw; | |
281 } | |
282 catch (Exception ex) | |
283 { | |
284 // Convert an Exception to TypeBuilderException. | |
285 // | |
286 throw new TypeBuilderException(string.Format(Resources.TypeFactory_BuildFailed, sourceType.FullName), ex); | |
287 } | |
288 } | |
289 | |
290 public static Type GetType(Type sourceType) | |
291 { | |
292 return | |
293 TypeHelper.IsScalar(sourceType) || sourceType.IsSealed || | |
294 (!sourceType.IsAbstract && sourceType.IsDefined(typeof(BLToolkitGeneratedAttribute), true)) ? | |
295 sourceType: | |
296 GetType(sourceType, sourceType, new AbstractClassBuilder(sourceType)); | |
297 } | |
298 | |
299 static class InstanceCreator<T> | |
300 { | |
301 public static readonly Func<T> CreateInstance = Expression.Lambda<Func<T>>(Expression.New(TypeFactory.GetType(typeof(T)))).Compile(); | |
302 } | |
303 | |
304 public static T CreateInstance<T>() where T: class | |
305 { | |
306 return InstanceCreator<T>.CreateInstance(); | |
307 } | |
308 | |
309 #endregion | |
310 | |
311 #region Private Helpers | |
312 | |
313 static Assembly LoadExtensionAssembly(Assembly originalAssembly) | |
314 { | |
315 #if !SILVERLIGHT | |
316 | |
317 if (originalAssembly is _AssemblyBuilder) | |
318 { | |
319 // This is a generated assembly. Even if it has a valid Location, | |
320 // there is definitelly no extension assembly at this path. | |
321 // | |
322 return null; | |
323 } | |
324 | |
325 try | |
326 { | |
327 var originalAssemblyLocation = new Uri(originalAssembly.EscapedCodeBase).LocalPath; | |
328 var extensionAssemblyLocation = Path.ChangeExtension( | |
329 originalAssemblyLocation, "BLToolkitExtension.dll"); | |
330 | |
331 if (File.GetLastWriteTime(originalAssemblyLocation) <= File.GetLastWriteTime(extensionAssemblyLocation)) | |
332 return Assembly.LoadFrom(extensionAssemblyLocation); | |
333 | |
334 Debug.WriteLineIf(File.Exists(extensionAssemblyLocation), | |
335 string.Format("Extension assembly '{0}' is out of date. Please rebuild.", | |
336 extensionAssemblyLocation), typeof(TypeAccessor).FullName); | |
337 | |
338 // Some good man may load this assembly already. Like IIS does it. | |
339 // | |
340 var extensionAssemblyName = originalAssembly.GetName(true); | |
341 extensionAssemblyName.Name += ".BLToolkitExtension"; | |
342 | |
343 foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) | |
344 { | |
345 // Note that assembly version and strong name are compared too. | |
346 // | |
347 if (AssemblyName.ReferenceMatchesDefinition(assembly.GetName(false), extensionAssemblyName)) | |
348 return assembly; | |
349 } | |
350 } | |
351 catch (Exception ex) | |
352 { | |
353 // Extension exist, but can't be loaded for some reason. | |
354 // Switch back to code generation | |
355 // | |
356 Debug.WriteLine(ex, typeof(TypeAccessor).FullName); | |
357 } | |
358 | |
359 #endif | |
360 | |
361 return null; | |
362 } | |
363 | |
364 [Conditional("DEBUG")] | |
365 private static void WriteDebug(string format, params object[] parameters) | |
366 { | |
367 Debug.WriteLine(string.Format(format, parameters)); | |
368 } | |
369 | |
370 #endregion | |
371 | |
372 #region Resolve Types | |
373 | |
374 /// <summary> | |
375 /// Initializes AssemblyResolve hooks for the current <see cref="AppDomain"/>. | |
376 /// </summary> | |
377 public static void Init() | |
378 { | |
379 // | |
380 // The code actually does nothing except an implicit call to the type constructor. | |
381 // | |
382 } | |
383 | |
384 public static Assembly AssemblyResolver(object sender, ResolveEventArgs args) | |
385 { | |
386 var name = args.Name; | |
387 var nameParts = name.Split(','); | |
388 | |
389 if (nameParts.Length > 0 && nameParts[0].ToLower().EndsWith(".dll")) | |
390 { | |
391 nameParts[0] = nameParts[0].Substring(0, nameParts[0].Length - 4); | |
392 name = string.Join(",", nameParts); | |
393 } | |
394 | |
395 lock (_builtTypes) | |
396 foreach (var type in _builtTypes.Keys) | |
397 if (type.FullName == name) | |
398 return type.Assembly; | |
399 | |
400 #if !SILVERLIGHT | |
401 | |
402 var idx = name.IndexOf("." + TypeBuilderConsts.AssemblyNameSuffix); | |
403 | |
404 if (idx > 0) | |
405 { | |
406 var typeName = name.Substring(0, idx); | |
407 var type = Type.GetType(typeName); | |
408 | |
409 if (type == null) | |
410 { | |
411 var ass = ((AppDomain)sender).GetAssemblies(); | |
412 | |
413 // CLR can't find an assembly built on previous AssemblyResolve event. | |
414 // | |
415 for (var i = ass.Length - 1; i >= 0; i--) | |
416 { | |
417 if (string.Compare(ass[i].FullName, name) == 0) | |
418 return ass[i]; | |
419 } | |
420 | |
421 for (var i = ass.Length - 1; i >= 0; i--) | |
422 { | |
423 var a = ass[i]; | |
424 | |
425 if (!( | |
426 #if FW4 | |
427 a.IsDynamic || | |
428 #endif | |
429 a is _AssemblyBuilder) && | |
430 (a.CodeBase.IndexOf("Microsoft.NET/Framework") > 0 || a.FullName.StartsWith("System."))) continue; | |
431 | |
432 type = a.GetType(typeName); | |
433 | |
434 if (type != null) break; | |
435 | |
436 foreach (var t in a.GetTypes()) | |
437 { | |
438 if (!t.IsAbstract) | |
439 continue; | |
440 | |
441 if (t.FullName == typeName) | |
442 { | |
443 type = t; | |
444 } | |
445 else | |
446 { | |
447 if (t.FullName.IndexOf('+') > 0) | |
448 { | |
449 var s = typeName; | |
450 | |
451 while (type == null && (idx = s.LastIndexOf(".")) > 0) | |
452 { | |
453 s = s.Remove(idx, 1).Insert(idx, "+"); | |
454 | |
455 if (t.FullName == s) | |
456 type = t; | |
457 } | |
458 } | |
459 } | |
460 | |
461 if (type != null) break; | |
462 } | |
463 | |
464 if (type != null) break; | |
465 } | |
466 } | |
467 | |
468 if (type != null) | |
469 { | |
470 var newType = GetType(type); | |
471 | |
472 if (newType.Assembly.FullName == name) | |
473 return newType.Assembly; | |
474 } | |
475 } | |
476 | |
477 #endif | |
478 | |
479 return null; | |
480 } | |
481 | |
482 #endregion | |
483 } | |
484 } |