diff Source/TypeBuilder/Builders/InstanceTypeBuilder.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/InstanceTypeBuilder.cs	Thu Mar 27 21:46:09 2014 +0400
@@ -0,0 +1,313 @@
+using System;
+using System.Reflection;
+using System.Reflection.Emit;
+
+using BLToolkit.Properties;
+using BLToolkit.Reflection;
+using BLToolkit.Reflection.Emit;
+
+namespace BLToolkit.TypeBuilder.Builders
+{
+	class InstanceTypeBuilder : DefaultTypeBuilder
+	{
+		public InstanceTypeBuilder(Type instanceType, bool isObjectHolder)
+		{
+			_instanceType   = instanceType;
+			_isObjectHolder = isObjectHolder;
+		}
+
+		public InstanceTypeBuilder(Type propertyType, Type instanceType, bool isObjectHolder)
+		{
+			_propertyType   = propertyType;
+			_instanceType   = instanceType;
+			_isObjectHolder = isObjectHolder;
+		}
+
+		private readonly bool _isObjectHolder;
+
+		private readonly Type _propertyType;
+		public           Type  PropertyType
+		{
+			get { return _propertyType; }
+		}
+
+		private readonly Type _instanceType;
+		public           Type  InstanceType
+		{
+			get { return _instanceType; }
+		}
+
+		public override bool IsApplied(BuildContext context, AbstractTypeBuilderList builders)
+		{
+			if (context == null) throw new ArgumentNullException("context");
+
+			return
+				base.IsApplied(context, builders) &&
+				context.CurrentProperty != null &&
+				context.CurrentProperty.GetIndexParameters().Length == 0 &&
+				(PropertyType == null ||
+				 TypeHelper.IsSameOrParent(PropertyType, context.CurrentProperty.PropertyType));
+		}
+
+		protected override Type GetFieldType()
+		{
+			return InstanceType;
+		}
+
+		protected override Type GetObjectType()
+		{
+			return IsObjectHolder? Context.CurrentProperty.PropertyType: base.GetObjectType();
+		}
+
+		protected override bool IsObjectHolder
+		{
+			get { return _isObjectHolder && Context.CurrentProperty.PropertyType.IsClass; }
+		}
+
+		protected override void BuildAbstractGetter()
+		{
+			var field        = GetField();
+			var emit         = Context.MethodBuilder.Emitter;
+			var propertyType = Context.CurrentProperty.PropertyType;
+			var getter       = GetGetter();
+
+			if (InstanceType.IsValueType) emit.ldarg_0.ldflda(field);
+			else                          emit.ldarg_0.ldfld (field);
+
+			Type memberType;
+
+			if (getter is PropertyInfo)
+			{
+				var pi = ((PropertyInfo)getter);
+
+				if (InstanceType.IsValueType) emit.call    (pi.GetGetMethod());
+				else                          emit.callvirt(pi.GetGetMethod());
+
+				memberType = pi.PropertyType;
+			}
+			else if (getter is FieldInfo)
+			{
+				var fi = (FieldInfo)getter;
+
+				emit.ldfld(fi);
+
+				memberType = fi.FieldType;
+			}
+			else
+			{
+				var mi    = (MethodInfo)getter;
+				var pi = mi.GetParameters();
+
+				for (var k = 0; k < pi.Length; k++)
+				{
+					var p = pi[k];
+
+					if (p.IsDefined(typeof(ParentAttribute), true))
+					{
+						// Parent - set this.
+						//
+						emit.ldarg_0.end();
+
+						if (!TypeHelper.IsSameOrParent(p.ParameterType, Context.Type))
+							emit.castclass(p.ParameterType);
+					}
+					else if (p.IsDefined(typeof (PropertyInfoAttribute), true))
+					{
+						// PropertyInfo.
+						//
+						emit.ldsfld(GetPropertyInfoField()).end();
+					}
+					else
+						throw new TypeBuilderException(string.Format(
+							Resources.TypeBuilder_UnknownParameterType,
+							mi.Name, mi.DeclaringType.FullName, p.Name));
+
+				}
+
+				if (InstanceType.IsValueType) emit.call    (mi);
+				else                          emit.callvirt(mi);
+
+				memberType = mi.ReturnType;
+			}
+
+			if (propertyType.IsValueType)
+			{
+				if (memberType.IsValueType == false)
+					emit.CastFromObject(propertyType);
+			}
+			else
+			{
+				if (memberType != propertyType)
+					emit.castclass(propertyType);
+			}
+
+			emit.stloc(Context.ReturnValue);
+		}
+
+		protected override void BuildAbstractSetter()
+		{
+			var field        = GetField();
+			var emit         = Context.MethodBuilder.Emitter;
+			var propertyType = Context.CurrentProperty.PropertyType;
+			var setter       = GetSetter();
+
+			if (InstanceType.IsValueType) emit.ldarg_0.ldflda(field);
+			else                          emit.ldarg_0.ldfld (field);
+
+			if (setter is PropertyInfo)
+			{
+				var pi = ((PropertyInfo)setter);
+
+				emit.ldarg_1.end();
+
+				if (propertyType.IsValueType && !pi.PropertyType.IsValueType)
+					emit.box(propertyType);
+
+				if (InstanceType.IsValueType) emit.call    (pi.GetSetMethod());
+				else                          emit.callvirt(pi.GetSetMethod());
+			}
+			else if (setter is FieldInfo)
+			{
+				var fi = (FieldInfo)setter;
+
+				emit.ldarg_1.end();
+
+				if (propertyType.IsValueType && !fi.FieldType.IsValueType)
+					emit.box(propertyType);
+
+				emit.stfld(fi);
+			}
+			else
+			{
+				var mi            = (MethodInfo)setter;
+				var pi            = mi.GetParameters();
+				var gotValueParam = false;
+
+				for (var k = 0; k < pi.Length; k++)
+				{
+					var p = pi[k];
+
+					if (p.IsDefined(typeof (ParentAttribute), true))
+					{
+						// Parent - set this.
+						//
+						emit.ldarg_0.end();
+
+						if (!TypeHelper.IsSameOrParent(p.ParameterType, Context.Type))
+							emit.castclass(p.ParameterType);
+					}
+					else if (p.IsDefined(typeof (PropertyInfoAttribute), true))
+					{
+						// PropertyInfo.
+						//
+						emit.ldsfld(GetPropertyInfoField()).end();
+					}
+					else if (!gotValueParam)
+					{
+						// This must be a value.
+						//
+						emit.ldarg_1.end();
+
+						if (propertyType.IsValueType && !p.ParameterType.IsValueType)
+							emit.box(propertyType);
+
+						gotValueParam = true;
+					}
+					else
+						throw new TypeBuilderException(string.Format(
+							Resources.TypeBuilder_UnknownParameterType,
+							mi.Name, mi.DeclaringType.FullName, p.Name));
+				}
+
+				if (InstanceType.IsValueType) emit.call(mi);
+				else                          emit.callvirt(mi);
+			}
+		}
+
+		private MemberInfo GetGetter()
+		{
+			const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance;
+
+			var propertyType = Context.CurrentProperty.PropertyType;
+			var fields       = InstanceType.GetFields(bindingFlags);
+
+			foreach (var field in fields)
+			{
+				var attrs = field.GetCustomAttributes(typeof(GetValueAttribute), true);
+
+				if (attrs.Length > 0 && TypeHelper.IsSameOrParent(field.FieldType, propertyType))
+					return field;
+			}
+
+			var props = InstanceType.GetProperties(bindingFlags);
+
+			foreach (var prop in props)
+			{
+				var attrs = prop.GetCustomAttributes(typeof(GetValueAttribute), true);
+
+				if (attrs.Length > 0 && TypeHelper.IsSameOrParent(prop.PropertyType, propertyType))
+					return prop;
+			}
+
+			foreach (var field in fields)
+				if (field.Name == "Value" && TypeHelper.IsSameOrParent(field.FieldType, propertyType))
+					return field;
+
+			foreach (var prop in props)
+				if (prop.Name == "Value" && TypeHelper.IsSameOrParent(prop.PropertyType, propertyType))
+					return prop;
+
+			var method = TypeHelper.GetMethod(InstanceType, false, "GetValue", bindingFlags);
+
+			if (method != null && TypeHelper.IsSameOrParent(propertyType, method.ReturnType))
+				return method;
+
+			throw new TypeBuilderException(string.Format(
+				Resources.TypeBuilder_CannotGetGetter, InstanceType.FullName,
+				propertyType.FullName, Context.CurrentProperty.Name, Context.Type.FullName));
+		}
+
+		private MemberInfo GetSetter()
+		{
+			const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance;
+
+			var propertyType = Context.CurrentProperty.PropertyType;
+			var fields       = InstanceType.GetFields(bindingFlags);
+
+			foreach (var field in fields)
+			{
+				var attrs = field.GetCustomAttributes(typeof(SetValueAttribute), true);
+
+				if (attrs.Length > 0 && TypeHelper.IsSameOrParent(field.FieldType, propertyType))
+					return field;
+			}
+
+			var props = InstanceType.GetProperties(bindingFlags);
+
+			foreach (var prop in props)
+			{
+				var attrs = prop.GetCustomAttributes(typeof(SetValueAttribute), true);
+
+				if (attrs.Length > 0 && TypeHelper.IsSameOrParent(prop.PropertyType, propertyType))
+					return prop;
+			}
+
+			foreach (var field in fields)
+				if (field.Name == "Value" && TypeHelper.IsSameOrParent(field.FieldType, propertyType))
+					return field;
+
+			foreach (var prop in props)
+				if (prop.Name == "Value" && TypeHelper.IsSameOrParent(prop.PropertyType, propertyType))
+					return prop;
+
+			var method = TypeHelper.GetMethod(InstanceType, false, "SetValue", bindingFlags);
+
+			if (method != null && method.ReturnType == typeof(void))
+				return method;
+
+			throw new TypeBuilderException(string.Format(
+				Resources.TypeBuilder_CannotGetSetter, InstanceType.FullName,
+				propertyType.FullName, Context.CurrentProperty.Name, Context.Type.FullName));
+		}
+	}
+}