diff Source/TypeBuilder/Builders/AbstractClassBuilder.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/Builders/AbstractClassBuilder.cs	Thu Mar 27 21:46:09 2014 +0400
@@ -0,0 +1,746 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Diagnostics.CodeAnalysis;
+
+namespace BLToolkit.TypeBuilder.Builders
+{
+	using Properties;
+	using Reflection;
+	using Reflection.Emit;
+
+	internal class AbstractClassBuilder : ITypeBuilder
+	{
+		public AbstractClassBuilder(Type sourceType)
+		{
+			_sourceType = sourceType;
+		}
+
+		readonly Type _sourceType;
+
+		public string AssemblyNameSuffix
+		{
+			get { return TypeBuilderConsts.AssemblyNameSuffix; }
+		}
+
+		public Type Build(AssemblyBuilderHelper assemblyBuilder)
+		{
+			_context  = new BuildContext(_sourceType);
+			_builders = new AbstractTypeBuilderList();
+
+			_context.TypeBuilders    = GetBuilderList(_context.Type);
+			_context.AssemblyBuilder = assemblyBuilder;
+
+			_builders.AddRange(_context.TypeBuilders);
+			_builders.Add(_defaultTypeBuilder);
+
+			return Build();
+		}
+
+		internal static string GetTypeFullName(Type type)
+		{
+			var name = type.FullName;
+
+			if (type.IsGenericType)
+			{
+				name = name.Split('`')[0];
+
+				foreach (var t in type.GetGenericArguments())
+					name += "_" + GetTypeFullName(t).Replace('+', '_').Replace('.', '_');
+			}
+
+			return name;
+		}
+
+		internal static string GetTypeShortName(Type type)
+		{
+			var name = type.Name;
+
+			if (type.IsGenericType)
+			{
+				name = name.Split('`')[0];
+
+				foreach (var t in type.GetGenericArguments())
+					name += "_" + GetTypeFullName(t).Replace('+', '_').Replace('.', '_');
+			}
+
+			return name;
+		}
+
+		public string GetTypeName()
+		{
+			var typeFullName  = _sourceType.FullName;
+			var typeShortName = _sourceType.Name;
+
+			if (_sourceType.IsGenericType)
+			{
+				typeFullName  = GetTypeFullName (_sourceType);
+				typeShortName = GetTypeShortName(_sourceType);
+			}
+
+			typeFullName  = typeFullName. Replace('+', '.');
+			typeShortName = typeShortName.Replace('+', '.');
+
+			typeFullName = typeFullName.Substring(0, typeFullName.Length - typeShortName.Length);
+			typeFullName = typeFullName + "BLToolkitExtension." + typeShortName;
+
+			return typeFullName;
+		}
+
+		public Type GetBuildingType()
+		{
+			return _sourceType;
+		}
+
+		private static AbstractTypeBuilderList GetBuilderList(TypeHelper type)
+		{
+			var attrs    = type.GetAttributes(typeof(AbstractTypeBuilderAttribute));
+			var builders = new AbstractTypeBuilderList(attrs.Length);
+
+			foreach (AbstractTypeBuilderAttribute attr in attrs)
+			{
+				var builder = attr.TypeBuilder;
+
+				if (builder != null)
+				{
+					builder.TargetElement = type;
+					builders.Add(builder);
+				}
+			}
+
+			return builders;
+		}
+
+		private static readonly DefaultTypeBuilder _defaultTypeBuilder = new DefaultTypeBuilder();
+
+		private BuildContext            _context;
+		private AbstractTypeBuilderList _builders;
+
+		private Type Build()
+		{
+			DefineNonAbstractType();
+
+			SetID(_builders);
+
+			_context.BuildElement = BuildElement.Type;
+
+			Build(BuildStep.Before, _builders);
+			Build(BuildStep.Build,  _builders);
+
+			var ids = _builders.ToDictionary(builder => builder, builder => builder.ID);
+
+			DefineAbstractProperties();
+			DefineAbstractMethods();
+			OverrideVirtualProperties();
+			OverrideVirtualMethods();
+			DefineInterfaces();
+
+			foreach (var builder in ids.Keys)
+				builder.ID = ids[builder];
+
+			_context.BuildElement = BuildElement.Type;
+
+			Build(BuildStep.After, _builders);
+
+			var initMethod = _context.Type.GetMethod("InitInstance", typeof(InitContext));
+
+			// Finalize constructors.
+			//
+			if (_context.TypeBuilder.IsDefaultConstructorDefined)
+			{
+				if (initMethod != null)
+					_context.TypeBuilder.DefaultConstructor.Emitter
+						.ldarg_0
+						.ldnull
+						.callvirt (initMethod)
+						;
+
+				_context.TypeBuilder.DefaultConstructor.Emitter.ret();
+			}
+
+			if (_context.TypeBuilder.IsInitConstructorDefined)
+			{
+				if (initMethod != null)
+					_context.TypeBuilder.InitConstructor.Emitter
+						.ldarg_0
+						.ldarg_1
+						.callvirt (initMethod)
+						;
+
+				_context.TypeBuilder.InitConstructor.Emitter.ret();
+			}
+
+			if (_context.TypeBuilder.IsTypeInitializerDefined)
+				_context.TypeBuilder.TypeInitializer.Emitter.ret();
+
+			// Create the type.
+			//
+			return _context.TypeBuilder.Create();
+		}
+
+		private static int _idCounter;
+
+		private static void SetID(AbstractTypeBuilderList builders)
+		{
+			foreach (var builder in builders)
+				builder.ID = ++_idCounter;
+		}
+
+		private static void CheckCompatibility(BuildContext context, AbstractTypeBuilderList builders)
+		{
+			for (var i = 0; i < builders.Count; i++)
+			{
+				var cur = builders[i];
+
+				if (cur == null)
+					continue;
+
+				for (var j = 0; j < builders.Count; j++)
+				{
+					var test = builders[j];
+
+					if (i == j || test == null)
+						continue;
+
+					if (cur.IsCompatible(context, test) == false)
+						builders[j] = null;
+				}
+			}
+
+			for (var i = 0; i < builders.Count; i++)
+				if (builders[i] == null)
+					builders.RemoveAt(i--);
+		}
+
+		private void DefineNonAbstractType()
+		{
+			var interfaces = new List<Type>();
+
+			if (_context.Type.IsInterface)
+			{
+				interfaces.Add(_context.Type);
+				_context.InterfaceMap.Add(_context.Type, null);
+			}
+
+			foreach (var tb in _builders)
+			{
+				var types = tb.GetInterfaces();
+
+				if (types != null)
+				{
+					foreach (var t in types)
+					{
+						if (t == null)
+							continue;
+
+						if (!t.IsInterface)
+						{
+							throw new InvalidOperationException(
+								string.Format(Resources.AbstractClassBuilder_TypeIsNotAnInterface, t.FullName));
+						}
+
+						if (interfaces.Contains(t) == false)
+						{
+							interfaces.Add(t);
+							_context.InterfaceMap.Add(t, tb);
+						}
+					}
+				}
+			}
+
+			var typeName = GetTypeName();
+
+			_context.TypeBuilder = _context.AssemblyBuilder.DefineType(
+				typeName,
+				TypeAttributes.Public | TypeAttributes.BeforeFieldInit | (TypeFactory.SealTypes? TypeAttributes.Sealed: 0),
+				_context.Type.IsInterface? typeof(object): (Type)_context.Type,
+				interfaces.ToArray());
+
+			if (_context.Type.IsSerializable)
+				_context.TypeBuilder.SetCustomAttribute(typeof(SerializableAttribute));
+		}
+
+		class BuilderComparer : IComparer<IAbstractTypeBuilder>
+		{
+			public BuilderComparer(BuildContext context)
+			{
+				_context = context;
+			}
+
+			readonly BuildContext _context;
+
+			[SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods")]
+			public int Compare(IAbstractTypeBuilder x, IAbstractTypeBuilder y)
+			{
+				return y.GetPriority(_context) - x.GetPriority(_context);
+			}
+		}
+
+		private void Build(BuildStep step, AbstractTypeBuilderList builders)
+		{
+			_context.Step = step;
+			_context.TypeBuilders.Clear();
+
+			foreach (var builder in builders)
+				if (builder.IsApplied(_context, builders))
+					_context.TypeBuilders.Add(builder);
+
+			if (_context.IsVirtualMethod || _context.IsVirtualProperty)
+				_context.TypeBuilders.Add(_defaultTypeBuilder);
+
+			if (_context.TypeBuilders.Count == 0)
+				return;
+
+			CheckCompatibility(_context, _context.TypeBuilders);
+
+			_context.TypeBuilders.Sort(new BuilderComparer(_context));
+
+			for (var i = 0; i < _context.TypeBuilders.Count; i++)
+			{
+				var builder = _context.TypeBuilders[i];
+
+				builder.Build(_context);
+			}
+		}
+
+		private void BeginEmitMethod(MethodInfo method)
+		{
+			_context.CurrentMethod = method;
+			_context.MethodBuilder = _context.TypeBuilder.DefineMethod(method);
+
+			var emit = _context.MethodBuilder.Emitter;
+
+			// Label right before return and catch block.
+			//
+			_context.ReturnLabel = emit.DefineLabel();
+
+			// Create return value.
+			//
+			if (method.ReturnType != typeof(void))
+			{
+				_context.ReturnValue = _context.MethodBuilder.Emitter.DeclareLocal(method.ReturnType);
+				emit.Init(_context.ReturnValue);
+			}
+
+			// Initialize out parameters.
+			//
+			var parameters = method.GetParameters();
+
+			if (parameters != null)
+				emit.InitOutParameters(parameters);
+		}
+
+		private void EmitMethod(
+			AbstractTypeBuilderList builders, MethodInfo methdoInfo, BuildElement buildElement)
+		{
+			SetID(builders);
+
+			_context.BuildElement = buildElement;
+
+			var isCatchBlockRequired   = false;
+			var isFinallyBlockRequired = false;
+
+			foreach (var builder in builders)
+			{
+				isCatchBlockRequired   = isCatchBlockRequired   || IsApplied(builder, builders, BuildStep.Catch);
+				isFinallyBlockRequired = isFinallyBlockRequired || IsApplied(builder, builders, BuildStep.Finally);
+			}
+
+			BeginEmitMethod(methdoInfo);
+
+			Build(BuildStep.Begin,  builders);
+
+			var emit        = _context.MethodBuilder.Emitter;
+			var returnLabel = _context.ReturnLabel;
+
+			// Begin catch block.
+			//
+
+			if (isCatchBlockRequired || isFinallyBlockRequired)
+			{
+				_context.ReturnLabel = emit.DefineLabel();
+				emit.BeginExceptionBlock();
+			}
+
+			Build(BuildStep.Before, builders);
+			Build(BuildStep.Build,  builders);
+			Build(BuildStep.After,  builders);
+
+			if (isCatchBlockRequired || isFinallyBlockRequired)
+			{
+				emit.MarkLabel(_context.ReturnLabel);
+				_context.ReturnLabel = returnLabel;
+			}
+
+			// End catch block.
+			//
+			if (isCatchBlockRequired)
+			{
+				emit
+					.BeginCatchBlock(typeof(Exception));
+
+				_context.ReturnLabel = emit.DefineLabel();
+				_context.Exception   = emit.DeclareLocal(typeof(Exception));
+
+				emit
+					.stloc (_context.Exception);
+
+				Build(BuildStep.Catch, builders);
+
+				emit
+					.rethrow
+					.end();
+
+				emit.MarkLabel(_context.ReturnLabel);
+				_context.ReturnLabel = returnLabel;
+				_context.Exception   = null;
+			}
+
+			if (isFinallyBlockRequired)
+			{
+				emit.BeginFinallyBlock();
+				_context.ReturnLabel = emit.DefineLabel();
+
+				Build(BuildStep.Finally, builders);
+
+				emit.MarkLabel(_context.ReturnLabel);
+				_context.ReturnLabel = returnLabel;
+			}
+
+			if (isCatchBlockRequired || isFinallyBlockRequired)
+				emit.EndExceptionBlock();
+
+			Build(BuildStep.End, builders);
+
+			EndEmitMethod();
+		}
+
+		private void EndEmitMethod()
+		{
+			var emit = _context.MethodBuilder.Emitter;
+
+			// Prepare return.
+			//
+			emit.MarkLabel(_context.ReturnLabel);
+
+			if (_context.ReturnValue != null)
+				emit.ldloc(_context.ReturnValue);
+
+			emit.ret();
+
+			// Cleanup the context.
+			//
+			_context.ReturnValue   = null;
+			_context.CurrentMethod = null;
+			_context.MethodBuilder = null;
+		}
+
+		private static AbstractTypeBuilderList GetBuilders(object[] attributes, object target)
+		{
+			var builders = new AbstractTypeBuilderList(attributes.Length);
+
+			foreach (AbstractTypeBuilderAttribute attr in attributes)
+			{
+				var builder = attr.TypeBuilder;
+
+				builder.TargetElement = target;
+				builders.Add(builder);
+			}
+
+			return builders;
+		}
+
+		private static AbstractTypeBuilderList GetBuilders(MemberInfo memberInfo)
+		{
+			return GetBuilders(
+				memberInfo.GetCustomAttributes(typeof(AbstractTypeBuilderAttribute), true), memberInfo);
+		}
+
+		private static AbstractTypeBuilderList GetBuilders(ParameterInfo parameterInfo)
+		{
+			return GetBuilders(
+				parameterInfo.GetCustomAttributes(typeof(AbstractTypeBuilderAttribute), true), parameterInfo);
+		}
+
+		private static AbstractTypeBuilderList GetBuilders(ParameterInfo[] parameters)
+		{
+			var builders = new AbstractTypeBuilderList();
+
+			foreach (var pi in parameters)
+			{
+				var attributes = pi.GetCustomAttributes(typeof(AbstractTypeBuilderAttribute), true);
+
+				foreach (AbstractTypeBuilderAttribute attr in attributes)
+				{
+					var builder = attr.TypeBuilder;
+
+					builder.TargetElement = pi;
+					builders.Add(builder);
+				}
+			}
+
+			return builders;
+		}
+
+		private static AbstractTypeBuilderList Combine(params AbstractTypeBuilderList[] builders)
+		{
+			var list = new AbstractTypeBuilderList();
+
+			foreach (var l in builders)
+				list.AddRange(l);
+
+			return list;
+		}
+
+		private bool IsApplied(IAbstractTypeBuilder builder, AbstractTypeBuilderList builders, BuildStep buildStep)
+		{
+			_context.Step = buildStep;
+			return builder.IsApplied(_context, builders);
+		}
+
+		private bool IsApplied(BuildElement element, AbstractTypeBuilderList builders)
+		{
+			_context.BuildElement = element;
+
+			foreach (var builder in builders)
+			{
+				if (IsApplied(builder, builders, BuildStep.Before))  return true;
+				if (IsApplied(builder, builders, BuildStep.Build))   return true;
+				if (IsApplied(builder, builders, BuildStep.After))   return true;
+				if (IsApplied(builder, builders, BuildStep.Catch))   return true;
+				if (IsApplied(builder, builders, BuildStep.Finally)) return true;
+			}
+
+			return false;
+		}
+
+		private static void GetAbstractProperties(Type type, List<PropertyInfo> props)
+		{
+			if (props.FirstOrDefault(mi => mi.DeclaringType == type) == null)
+			{
+				props.AddRange(
+					type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance));
+
+				if (type.IsInterface)
+					foreach (var t in type.GetInterfaces())
+						GetAbstractProperties(t, props);
+			}
+		}
+
+		private void DefineAbstractProperties()
+		{
+			var props = new List<PropertyInfo>();
+
+			GetAbstractProperties(_context.Type, props);
+
+			foreach (var pi in props)
+			{
+				_context.CurrentProperty = pi;
+
+				var propertyBuilders = GetBuilders(pi);
+
+				var getter = pi.GetGetMethod(true);
+				var setter = pi.GetSetMethod(true);
+
+				if (getter != null && getter.IsAbstract ||
+					setter != null && setter.IsAbstract)
+				{
+					DefineAbstractGetter(pi, getter, propertyBuilders);
+					DefineAbstractSetter(pi, setter, propertyBuilders);
+				}
+			}
+
+			_context.CurrentProperty = null;
+		}
+
+		private void DefineAbstractGetter(
+			PropertyInfo propertyInfo, MethodInfo getter, AbstractTypeBuilderList propertyBuilders)
+		{
+			// Getter can be not defined. We will generate it anyway.
+			//
+			if (getter == null)
+#if SILVERLIGHT
+				return;
+#else
+				getter = new FakeGetter(propertyInfo);
+#endif
+
+			var builders = Combine(
+				GetBuilders(getter.GetParameters()),
+				GetBuilders(getter.ReturnParameter),
+				GetBuilders(getter),
+				propertyBuilders,
+				_builders);
+
+			EmitMethod(builders, getter, BuildElement.AbstractGetter);
+		}
+
+		private void DefineAbstractSetter(
+			PropertyInfo            propertyInfo,
+			MethodInfo              setter,
+			AbstractTypeBuilderList propertyBuilders)
+		{
+			// Setter can be not defined. We will generate it anyway.
+			//
+			if (setter == null)
+#if SILVERLIGHT
+				return;
+#else
+				setter = new FakeSetter(propertyInfo);
+#endif
+
+			var builders = Combine(
+				GetBuilders(setter.GetParameters()),
+				GetBuilders(setter.ReturnParameter),
+				GetBuilders(setter),
+				propertyBuilders,
+				_builders);
+
+			EmitMethod(builders, setter, BuildElement.AbstractSetter);
+		}
+
+		private static void GetAbstractMethods(Type type, List<MethodInfo> methods)
+		{
+			if (!methods.Exists(mi => mi.DeclaringType == type))
+			{
+				methods.AddRange(
+					type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance));
+
+				if (type.IsInterface)
+					foreach (var t in type.GetInterfaces())
+						GetAbstractMethods(t, methods);
+			}
+		}
+
+		private void DefineAbstractMethods()
+		{
+			var methods = new List<MethodInfo>();
+
+			GetAbstractMethods(_context.Type, methods);
+
+			foreach (var method in methods)
+			{
+				if (method.IsAbstract && (method.Attributes & MethodAttributes.SpecialName) == 0)
+				{
+					var builders = Combine(
+						GetBuilders(method.GetParameters()),
+						GetBuilders(method.ReturnParameter),
+						GetBuilders(method),
+						_builders);
+
+					EmitMethod(builders, method, BuildElement.AbstractMethod);
+				}
+			}
+		}
+
+		private void OverrideVirtualProperties()
+		{
+			var props = _context.Type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
+
+			foreach (var pi in props)
+			{
+				_context.CurrentProperty = pi;
+
+				var propertyBuilders = GetBuilders(pi);
+
+				var getter = pi.GetGetMethod(true);
+
+				if (getter != null && getter.IsVirtual && !getter.IsAbstract && !getter.IsFinal)
+					OverrideGetter(getter, propertyBuilders);
+
+				var setter = pi.GetSetMethod(true);
+
+				if (setter != null && setter.IsVirtual && !setter.IsAbstract && !setter.IsFinal)
+					OverrideSetter(setter, propertyBuilders);
+			}
+
+			_context.CurrentProperty = null;
+		}
+
+		private void OverrideGetter(MethodInfo getter, AbstractTypeBuilderList propertyBuilders)
+		{
+			var builders = Combine(
+				GetBuilders(getter.GetParameters()),
+				GetBuilders(getter.ReturnParameter),
+				GetBuilders(getter),
+				propertyBuilders,
+				_builders);
+
+			if (IsApplied(BuildElement.VirtualGetter, builders))
+				EmitMethod(builders, getter, BuildElement.VirtualGetter);
+		}
+
+		private void OverrideSetter(MethodInfo setter, AbstractTypeBuilderList propertyBuilders)
+		{
+			var builders = Combine(
+				GetBuilders(setter.GetParameters()),
+				GetBuilders(setter.ReturnParameter),
+				GetBuilders(setter),
+				propertyBuilders,
+				_builders);
+
+			if (IsApplied(BuildElement.VirtualSetter, builders))
+				EmitMethod(builders, setter, BuildElement.VirtualSetter);
+		}
+
+		private void OverrideVirtualMethods()
+		{
+			var methods = _context.Type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
+
+			foreach (var method in methods)
+			{
+				if (method.IsVirtual &&
+					method.IsAbstract == false &&
+					method.IsFinal    == false &&
+					(method.Attributes & MethodAttributes.SpecialName) == 0 &&
+					method.DeclaringType != typeof(object))
+				{
+					var builders = Combine(
+						GetBuilders(method.GetParameters()),
+						GetBuilders(method.ReturnParameter),
+						GetBuilders(method),
+						_builders);
+
+					if (IsApplied(BuildElement.VirtualMethod, builders))
+						EmitMethod(builders, method, BuildElement.VirtualMethod);
+				}
+			}
+		}
+
+		private void DefineInterfaces()
+		{
+			foreach (var de in _context.InterfaceMap)
+			{
+				_context.CurrentInterface = de.Key;
+
+				var interfaceMethods = _context.CurrentInterface.GetMethods();
+
+				foreach (var m in interfaceMethods)
+				{
+					if (_context.TypeBuilder.OverriddenMethods.ContainsKey(m))
+						continue;
+
+					BeginEmitMethod(m);
+
+					// Call builder to build the method.
+					//
+					var builder = de.Value;
+
+					if (builder != null)
+					{
+						builder.ID = ++_idCounter;
+
+						_context.BuildElement = BuildElement.InterfaceMethod;
+						_context.Step         = BuildStep.Build;
+						builder.Build(_context);
+					}
+
+					EndEmitMethod();
+				}
+
+				_context.CurrentInterface = null;
+			}
+		}
+	}
+}