Mercurial > pub > bltoolkit
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; + } + } + } +}