using System; using System.Collections; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Security; using System.Security.Permissions; #if FW3 using System.Linq.Expressions; #endif using BLToolkit.Reflection; using BLToolkit.Reflection.Emit; using BLToolkit.TypeBuilder.Builders; using BLToolkit.Properties; using BLToolkit.Configuration; namespace BLToolkit.TypeBuilder { public static class TypeFactory { static TypeFactory() { BLToolkitSection section = BLToolkitSection.Instance; if (section != null) { TypeFactoryElement elm = section.TypeFactory; if (elm != null) { SaveTypes = elm.SaveTypes; SealTypes = elm.SealTypes; LoadTypes = elm.LoadTypes; SetGlobalAssembly(elm.AssemblyPath, elm.Version, elm.KeyFile); } } if (SecurityManager.IsGranted(new SecurityPermission(SecurityPermissionFlag.ControlAppDomain))) SubscribeAssemblyResolver(); } static void SubscribeAssemblyResolver() { AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; } #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; } } private static bool _saveTypes; public static bool SaveTypes { get { return _saveTypes; } set { _saveTypes = value; } } private static bool _sealTypes = true; public static bool SealTypes { get { return _sealTypes; } set { _sealTypes = value; } } [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) { AssemblyBuilderHelper ab = GlobalAssemblyBuilder; if (ab == null) { string 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)) assemblyDir = Path.GetDirectoryName(type.Module.FullyQualifiedName); string fullName = type.FullName; if (type.IsGenericType) fullName = AbstractClassBuilder.GetTypeFullName(type); ab = new AssemblyBuilderHelper(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 private static readonly Hashtable _builtTypes = new Hashtable(10); private static readonly Hashtable _assemblies = new Hashtable(10); private static bool _loadTypes; public static bool LoadTypes { get { return _loadTypes; } set { _loadTypes = value; } } 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 { Hashtable builderTable = (Hashtable)_builtTypes[typeBuilder.GetType()]; Type type; if (builderTable != null) { type = (Type)builderTable[hashKey]; if (type != null) return type; } lock (_builtTypes.SyncRoot) { builderTable = (Hashtable)_builtTypes[typeBuilder.GetType()]; if (builderTable != null) { type = (Type)builderTable[hashKey]; if (type != null) return type; } else { _builtTypes.Add(typeBuilder.GetType(), builderTable = new Hashtable()); } if (_loadTypes) { Assembly originalAssembly = sourceType.Assembly; Assembly extensionAssembly; if (_assemblies.Contains(originalAssembly)) extensionAssembly = (Assembly)_assemblies[originalAssembly]; else { extensionAssembly = LoadExtensionAssembly(originalAssembly); _assemblies.Add(originalAssembly, extensionAssembly); } if (extensionAssembly != null) { type = extensionAssembly.GetType(typeBuilder.GetTypeName()); if (type != null) { builderTable.Add(hashKey, type); return type; } } } AssemblyBuilderHelper 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)); } #if FW3 static class InstanceCreator { public static readonly Func CreateInstance = Expression.Lambda>(Expression.New(TypeFactory.GetType(typeof(T)))).Compile(); } #endif public static T CreateInstance() where T: class { #if FW3 return InstanceCreator.CreateInstance(); #else return (T)Activator.CreateInstance(GetType(typeof(T))); #endif } #endregion #region Private Helpers private static Assembly LoadExtensionAssembly(Assembly originalAssembly) { 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 { string originalAssemblyLocation = new Uri(originalAssembly.EscapedCodeBase).AbsolutePath; string 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. // AssemblyName extensionAssemblyName = originalAssembly.GetName(true); extensionAssemblyName.Name += ".BLToolkitExtension"; foreach (Assembly 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); } return null; } [System.Diagnostics.Conditional("DEBUG")] private static void WriteDebug(string format, params object[] parameters) { System.Diagnostics.Debug.WriteLine(string.Format(format, parameters)); } #endregion #region Resolve Types /// /// Initializes AssemblyResolve hooks for the current . /// public static void Init() { // // The code actually does nothing except an implicit call to the type constructor. // } private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { string name = args.Name; string[] 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.SyncRoot) { foreach (Type type in _builtTypes.Keys) if (type.FullName == name) return type.Assembly; } int idx = name.IndexOf("." + TypeBuilderConsts.AssemblyNameSuffix); if (idx > 0) { string typeName = name.Substring(0, idx); Type type = Type.GetType(typeName); if (type == null) { Assembly[] ass = ((AppDomain)sender).GetAssemblies(); // CLR can't find an assembly built on previous AssemblyResolve event. // for (int i = ass.Length - 1; i >= 0; i--) { if (string.Compare(ass[i].FullName, name) == 0) return ass[i]; } for (int i = ass.Length - 1; i >= 0; i--) { Assembly a = ass[i]; if (!(a is _AssemblyBuilder) && (a.CodeBase.IndexOf("Microsoft.NET/Framework") > 0 || a.FullName.StartsWith("System."))) continue; type = a.GetType(typeName); if (type != null) break; foreach (Type t in a.GetTypes()) { if (!t.IsAbstract) continue; if (t.FullName == typeName) { type = t; } else { if (t.FullName.IndexOf('+') > 0) { string 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) { Type newType = GetType(type); if (newType.Assembly.FullName == name) return newType.Assembly; } } return null; } #endregion } }