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 }