diff Source/TypeBuilder/TypeFactory.cs @ 0:f990fcb411a9

Копия текущей версии из github
author cin
date Thu, 27 Mar 2014 21:46:09 +0400
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Source/TypeBuilder/TypeFactory.cs	Thu Mar 27 21:46:09 2014 +0400
@@ -0,0 +1,484 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Security.Permissions;
+
+using JetBrains.Annotations;
+
+using BLToolkit.Reflection;
+using BLToolkit.Reflection.Emit;
+using BLToolkit.TypeBuilder.Builders;
+using BLToolkit.Properties;
+#if !SILVERLIGHT
+using BLToolkit.Configuration;
+#endif
+
+namespace BLToolkit.TypeBuilder
+{
+	public static class TypeFactory
+	{
+		static TypeFactory()
+		{
+			SealTypes = true;
+
+#if !SILVERLIGHT
+
+			var section = BLToolkitSection.Instance;
+
+			if (section != null)
+			{
+				var elm = section.TypeFactory;
+
+				if (elm != null)
+				{
+					SaveTypes = elm.SaveTypes;
+					SealTypes = elm.SealTypes;
+					LoadTypes = elm.LoadTypes;
+
+					SetGlobalAssembly(elm.AssemblyPath, elm.Version, elm.KeyFile);
+				}
+			}
+
+#endif
+
+#if !SILVERLIGHT
+
+			var perm = new SecurityPermission(SecurityPermissionFlag.ControlAppDomain);
+
+#if FW4
+			try
+			{
+				//var permissionSet = new PermissionSet(PermissionState.None);
+				//permissionSet.AddPermission(perm);
+
+				//if (permissionSet.IsSubsetOf(AppDomain.CurrentDomain.PermissionSet))
+					SubscribeAssemblyResolver();
+			}
+			catch
+			{
+			}
+#else
+			if (SecurityManager.IsGranted(perm))
+				SubscribeAssemblyResolver();
+#endif
+
+#endif
+		}
+
+		static void SubscribeAssemblyResolver()
+		{
+#if FW4
+			// This hack allows skipping FW 4.0 security check for partial trusted assemblies.
+			//
+
+			var dm   = new DynamicMethod("SubscribeAssemblyResolverEx", typeof(void), null);
+			var emit = new EmitHelper(dm.GetILGenerator());
+
+			emit
+				.call     (typeof(AppDomain).GetProperty("CurrentDomain").GetGetMethod())
+				.ldnull
+				.ldftn    (typeof(TypeFactory).GetMethod("AssemblyResolver"))
+				.newobj   (typeof(ResolveEventHandler).GetConstructor(new[] { typeof(object), typeof(IntPtr) }))
+				.callvirt (typeof(AppDomain).GetEvent("AssemblyResolve").GetAddMethod())
+				.ret()
+				;
+
+			var setter = (Action)dm.CreateDelegate(typeof(Action));
+
+			setter();
+#else
+			AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolver;
+#endif
+		}
+
+		#region Create Assembly
+
+		private static string                _globalAssemblyPath;
+		private static string                _globalAssemblyKeyFile;
+		private static Version               _globalAssemblyVersion;
+		private static AssemblyBuilderHelper _globalAssembly;
+
+		private static AssemblyBuilderHelper GlobalAssemblyBuilder
+		{
+			get
+			{
+				if (_globalAssembly == null && _globalAssemblyPath != null)
+					_globalAssembly = new AssemblyBuilderHelper(_globalAssemblyPath, _globalAssemblyVersion, _globalAssemblyKeyFile);
+
+				return _globalAssembly;
+			}
+		}
+
+		public static bool SaveTypes { get; set; }
+		public static bool SealTypes { get; set; }
+
+		[SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods")]
+		public static void SetGlobalAssembly(string path)
+		{
+			SetGlobalAssembly(path, null, null);
+		}
+
+		[SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods")]
+		public static void SetGlobalAssembly(string path, Version version, string keyFile)
+		{
+			if (_globalAssembly != null)
+				SaveGlobalAssembly();
+
+			if (!string.IsNullOrEmpty(path))
+				_globalAssemblyPath = path;
+
+			_globalAssemblyVersion = version;
+			_globalAssemblyKeyFile = keyFile;
+		}
+
+		public static void SaveGlobalAssembly()
+		{
+			if (_globalAssembly != null)
+			{
+				_globalAssembly.Save();
+
+				WriteDebug("The global assembly saved in '{0}'.", _globalAssembly.Path);
+
+				_globalAssembly        = null;
+				_globalAssemblyPath    = null;
+				_globalAssemblyVersion = null;
+				_globalAssemblyKeyFile = null;
+			}
+		}
+
+		private static AssemblyBuilderHelper GetAssemblyBuilder(Type type, string suffix)
+		{
+			var ab = GlobalAssemblyBuilder;
+
+			if (ab == null)
+			{
+#if SILVERLIGHT
+				var assemblyDir = ".";
+#else
+				var assemblyDir = AppDomain.CurrentDomain.BaseDirectory;
+
+				// Dynamic modules are locationless, so ignore them.
+				// _ModuleBuilder is the base type for both
+				// ModuleBuilder and InternalModuleBuilder classes.
+				//
+				if (!(type.Module is _ModuleBuilder) && type.Module.FullyQualifiedName != null && type.Module.FullyQualifiedName.IndexOf('<') < 0)
+					assemblyDir = Path.GetDirectoryName(type.Module.FullyQualifiedName);
+#endif
+
+				var fullName = type.FullName;
+
+				if (type.IsGenericType)
+					fullName = AbstractClassBuilder.GetTypeFullName(type);
+
+				fullName = fullName.Replace('<', '_').Replace('>', '_');
+
+				ab = new AssemblyBuilderHelper(Path.Combine(assemblyDir, fullName + "." + suffix + ".dll"));
+			}
+
+			return ab;
+		}
+
+		[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+		private static void SaveAssembly(AssemblyBuilderHelper assemblyBuilder, Type type)
+		{
+			if (!SaveTypes || _globalAssembly != null)
+				return;
+			try
+			{
+				assemblyBuilder.Save();
+
+				WriteDebug("The '{0}' type saved in '{1}'.",
+							type.FullName,
+							assemblyBuilder.Path);
+			}
+			catch (Exception ex)
+			{
+				WriteDebug("Can't save the '{0}' assembly for the '{1}' type: {2}.",
+							assemblyBuilder.Path,
+							type.FullName,
+							ex.Message);
+			}
+		}
+
+		#endregion
+
+		#region GetType
+
+		static readonly Dictionary<Type,IDictionary<object,Type>> _builtTypes = new Dictionary<Type,IDictionary<object,Type>>(10);
+		static readonly Dictionary<Assembly,Assembly>             _assemblies = new Dictionary<Assembly, Assembly>(10);
+
+		public static bool LoadTypes { get; set; }
+
+		public static Type GetType(object hashKey, Type sourceType, ITypeBuilder typeBuilder)
+		{
+			if (hashKey     == null) throw new ArgumentNullException("hashKey");
+			if (sourceType  == null) throw new ArgumentNullException("sourceType");
+			if (typeBuilder == null) throw new ArgumentNullException("typeBuilder");
+
+			try
+			{
+				lock (_builtTypes)
+				{
+					Type type;
+					IDictionary<object,Type> builderTable;
+
+					if (_builtTypes.TryGetValue(typeBuilder.GetType(), out builderTable))
+					{
+						if (builderTable.TryGetValue(hashKey, out type))
+							return type;
+					}
+					else
+						_builtTypes.Add(typeBuilder.GetType(), builderTable = new Dictionary<object,Type>());
+
+					if (LoadTypes)
+					{
+						var originalAssembly = sourceType.Assembly;
+
+						Assembly extensionAssembly;
+
+						if (!_assemblies.TryGetValue(originalAssembly, out extensionAssembly))
+						{
+							extensionAssembly = LoadExtensionAssembly(originalAssembly);
+							_assemblies.Add(originalAssembly, extensionAssembly);
+						}
+
+						if (extensionAssembly != null)
+						{
+							type = extensionAssembly.GetType(typeBuilder.GetTypeName());
+
+							if (type != null)
+							{
+								builderTable.Add(hashKey, type);
+								return type;
+							}
+						}
+					}
+
+					var assemblyBuilder = GetAssemblyBuilder(sourceType, typeBuilder.AssemblyNameSuffix);
+
+					type = typeBuilder.Build(assemblyBuilder);
+
+					if (type != null)
+					{
+						builderTable.Add(hashKey, type);
+						SaveAssembly(assemblyBuilder, type);
+					}
+
+					return type;
+				}
+			}
+			catch (TypeBuilderException)
+			{
+				throw;
+			}
+			catch (Exception ex)
+			{
+				// Convert an Exception to TypeBuilderException.
+				//
+				throw new TypeBuilderException(string.Format(Resources.TypeFactory_BuildFailed, sourceType.FullName), ex);
+			}
+		}
+
+		public static Type GetType(Type sourceType)
+		{
+			return
+				TypeHelper.IsScalar(sourceType) || sourceType.IsSealed ||
+						(!sourceType.IsAbstract && sourceType.IsDefined(typeof(BLToolkitGeneratedAttribute), true)) ?
+					sourceType:
+					GetType(sourceType, sourceType, new AbstractClassBuilder(sourceType));
+		}
+
+		static class InstanceCreator<T>
+		{
+			public static readonly Func<T> CreateInstance = Expression.Lambda<Func<T>>(Expression.New(TypeFactory.GetType(typeof(T)))).Compile();
+		}
+
+		public static T CreateInstance<T>() where T: class
+		{
+			return InstanceCreator<T>.CreateInstance();
+		}
+
+		#endregion
+
+		#region Private Helpers
+
+		static Assembly LoadExtensionAssembly(Assembly originalAssembly)
+		{
+#if !SILVERLIGHT
+
+			if (originalAssembly is _AssemblyBuilder)
+			{
+				// This is a generated assembly. Even if it has a valid Location,
+				// there is definitelly no extension assembly at this path.
+				//
+				return null;
+			}
+
+			try
+			{
+				var originalAssemblyLocation  = new Uri(originalAssembly.EscapedCodeBase).LocalPath;
+				var extensionAssemblyLocation = Path.ChangeExtension(
+					originalAssemblyLocation, "BLToolkitExtension.dll");
+
+				if (File.GetLastWriteTime(originalAssemblyLocation) <= File.GetLastWriteTime(extensionAssemblyLocation))
+					return Assembly.LoadFrom(extensionAssemblyLocation);
+
+				Debug.WriteLineIf(File.Exists(extensionAssemblyLocation),
+					string.Format("Extension assembly '{0}' is out of date. Please rebuild.",
+						extensionAssemblyLocation), typeof(TypeAccessor).FullName);
+
+				// Some good man may load this assembly already. Like IIS does it.
+				//
+				var extensionAssemblyName = originalAssembly.GetName(true);
+				extensionAssemblyName.Name += ".BLToolkitExtension";
+
+				foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
+				{
+					// Note that assembly version and strong name are compared too.
+					//
+					if (AssemblyName.ReferenceMatchesDefinition(assembly.GetName(false), extensionAssemblyName))
+						return assembly;
+				}
+			}
+			catch (Exception ex)
+			{
+				// Extension exist, but can't be loaded for some reason.
+				// Switch back to code generation
+				//
+				Debug.WriteLine(ex, typeof(TypeAccessor).FullName);
+			}
+
+#endif
+
+			return null;
+		}
+
+		[Conditional("DEBUG")]
+		private static void WriteDebug(string format, params object[] parameters)
+		{
+			Debug.WriteLine(string.Format(format, parameters));
+		}
+
+		#endregion
+
+		#region Resolve Types
+
+		/// <summary>
+		/// Initializes AssemblyResolve hooks for the current <see cref="AppDomain"/>.
+		/// </summary>
+		public static void Init()
+		{
+			//
+			// The code actually does nothing except an implicit call to the type constructor.
+			//
+		}
+
+		public static Assembly AssemblyResolver(object sender, ResolveEventArgs args)
+		{
+			var name      = args.Name;
+			var nameParts = name.Split(',');
+
+			if (nameParts.Length > 0 && nameParts[0].ToLower().EndsWith(".dll"))
+			{
+				nameParts[0] = nameParts[0].Substring(0, nameParts[0].Length - 4);
+				name         = string.Join(",", nameParts);
+			}
+
+			lock (_builtTypes)
+				foreach (var type in _builtTypes.Keys)
+					if (type.FullName == name)
+						return type.Assembly;
+
+#if !SILVERLIGHT
+
+			var idx = name.IndexOf("." + TypeBuilderConsts.AssemblyNameSuffix);
+
+			if (idx > 0)
+			{
+				var typeName = name.Substring(0, idx);
+				var type     = Type.GetType(typeName);
+
+				if (type == null)
+				{
+					var ass = ((AppDomain)sender).GetAssemblies();
+
+					// CLR can't find an assembly built on previous AssemblyResolve event.
+					//
+					for (var i = ass.Length - 1; i >= 0; i--)
+					{
+						if (string.Compare(ass[i].FullName, name) == 0)
+							return ass[i];
+					}
+
+					for (var i = ass.Length - 1; i >= 0; i--)
+					{
+						var a = ass[i];
+
+						if (!(
+#if FW4
+							a.IsDynamic ||
+#endif
+							a is _AssemblyBuilder) &&
+							(a.CodeBase.IndexOf("Microsoft.NET/Framework") > 0 || a.FullName.StartsWith("System."))) continue;
+
+						type = a.GetType(typeName);
+
+						if (type != null) break;
+
+						foreach (var t in a.GetTypes())
+						{
+							if (!t.IsAbstract)
+								continue;
+
+							if (t.FullName == typeName)
+							{
+								type = t;
+							}
+							else
+							{
+								if (t.FullName.IndexOf('+') > 0)
+								{
+									var s = typeName;
+
+									while (type == null && (idx = s.LastIndexOf(".")) > 0)
+									{
+										s = s.Remove(idx, 1).Insert(idx, "+");
+
+										if (t.FullName == s)
+											type = t;
+									}
+								}
+							}
+
+							if (type != null) break;
+						}
+
+						if (type != null) break;
+					}
+				}
+
+				if (type != null)
+				{
+					var newType = GetType(type);
+
+					if (newType.Assembly.FullName == name)
+						return newType.Assembly;
+				}
+			}
+
+#endif
+
+			return null;
+		}
+
+		#endregion
+	}
+}