Mercurial > pub > bltoolkit
view Source/TypeBuilder/Builders/AbstractClassBuilder.cs @ 5:f7d63a092920
Исправлено условие Where в тех случаях, когда репозитарий не является генериком
author | cin |
---|---|
date | Tue, 10 Mar 2015 16:02:11 +0300 |
parents | f990fcb411a9 |
children |
line wrap: on
line source
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; } } } }