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