0
|
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 }
|