diff Source/TypeBuilder/Builders/DuckTypeBuilder.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/DuckTypeBuilder.cs	Thu Mar 27 21:46:09 2014 +0400
@@ -0,0 +1,282 @@
+using System;
+using System.Reflection;
+using System.Reflection.Emit;
+
+using BLToolkit.Patterns;
+using BLToolkit.Properties;
+using BLToolkit.Reflection.Emit;
+
+namespace BLToolkit.TypeBuilder.Builders
+{
+	class DuckTypeBuilder : ITypeBuilder
+	{
+		public DuckTypeBuilder(MustImplementAttribute defaultAttribute, Type interfaceType, Type[] objectTypes)
+		{
+			_interfaceType    = interfaceType;
+			_objectTypes      = objectTypes;
+			_defaultAttribute = defaultAttribute;
+		}
+
+		private readonly Type                   _interfaceType;
+		private readonly Type[]                 _objectTypes;
+		private          TypeBuilderHelper      _typeBuilder;
+		private readonly MustImplementAttribute _defaultAttribute;
+
+		#region ITypeBuilder Members
+
+		public string AssemblyNameSuffix
+		{
+			get { return "DuckType." + AbstractClassBuilder.GetTypeFullName(_interfaceType).Replace('+', '.'); }
+		}
+
+		public Type Build(AssemblyBuilderHelper assemblyBuilder)
+		{
+			_typeBuilder = assemblyBuilder.DefineType(GetTypeName(), typeof(DuckType), _interfaceType);
+
+			if (!BuildMembers(_interfaceType))
+				return null;
+
+			foreach (Type t in _interfaceType.GetInterfaces())
+				if (!BuildMembers(t))
+					return null;
+
+			return _typeBuilder.Create();
+		}
+
+		public string GetTypeName()
+		{
+			string name = String.Empty;
+
+			foreach (Type t in _objectTypes)
+			{
+				if (t != null)
+					name += AbstractClassBuilder.GetTypeFullName(t).Replace('+', '.');
+				name += "$";
+			}
+
+			return name + AssemblyNameSuffix;
+		}
+
+		public Type GetBuildingType()
+		{
+			return _interfaceType;
+		}
+
+		#endregion
+
+		private static bool CompareMethodSignature(MethodInfo m1, MethodInfo m2)
+		{
+			if (m1 == m2)
+				return true;
+
+			if (m1.Name != m2.Name)
+				return false;
+
+			if (m1.ReturnType != m2.ReturnType)
+				return false;
+
+			ParameterInfo[] ps1 = m1.GetParameters();
+			ParameterInfo[] ps2 = m2.GetParameters();
+
+			if (ps1.Length != ps2.Length)
+				return false;
+
+			for (int i = 0; i < ps1.Length; i++)
+			{
+				ParameterInfo p1 = ps1[i];
+				ParameterInfo p2 = ps2[i];
+
+				if (p1.ParameterType != p2.ParameterType ||
+#if !SILVERLIGHT
+					p1.IsIn != p2.IsIn ||
+#endif
+					p1.IsOut != p2.IsOut)
+					return false;
+			}
+
+			return true;
+		}
+
+		private bool BuildMembers(Type interfaceType)
+		{
+			FieldInfo    objectsField = typeof(DuckType).GetField("_objects", BindingFlags.NonPublic | BindingFlags.Instance);
+			BindingFlags flags        = BindingFlags.Public | BindingFlags.Instance
+				| (DuckTyping.AllowStaticMembers? BindingFlags.Static | BindingFlags.FlattenHierarchy: 0);
+
+			foreach (MethodInfo interfaceMethod in interfaceType.GetMethods(BindingFlags.Public | BindingFlags.Instance))
+			{
+				MethodInfo      targetMethod = null;
+				int             typeIndex    = 0;
+
+				for (; typeIndex < _objectTypes.Length; typeIndex++)
+				{
+					if (_objectTypes[typeIndex] == null)
+						continue;
+
+					foreach (MethodInfo mi in _objectTypes[typeIndex].GetMethods(flags))
+					{
+						if (CompareMethodSignature(interfaceMethod, mi))
+						{
+							targetMethod = mi;
+							break;
+						}
+					}
+
+					if (targetMethod == null)
+					{
+						foreach (Type intf in _objectTypes[typeIndex].GetInterfaces())
+						{
+							if (intf.IsPublic || intf.IsNestedPublic)
+							{
+								foreach (MethodInfo mi in intf.GetMethods(flags))
+								{
+									if (CompareMethodSignature(interfaceMethod, mi))
+									{
+										targetMethod = mi;
+										break;
+									}
+								}
+
+								if (targetMethod != null)
+									break;
+							}
+						}
+					}
+
+					if (targetMethod != null)
+						break;
+				}
+
+				ParameterInfo[]     ips     = interfaceMethod.GetParameters();
+				MethodBuilderHelper builder = _typeBuilder.DefineMethod(interfaceMethod);
+				EmitHelper          emit    = builder.Emitter;
+
+				if (targetMethod != null)
+				{
+					Type targetType = targetMethod.DeclaringType;
+
+					if (!targetMethod.IsStatic)
+					{
+						emit
+							.ldarg_0
+							.ldfld         (objectsField)
+							.ldc_i4        (typeIndex)
+							.ldelem_ref
+							.end()
+							;
+
+						if (targetType.IsValueType)
+						{
+							// For value types we have to use stack.
+							//
+							LocalBuilder obj = emit.DeclareLocal(targetType);
+
+							emit
+								.unbox_any (targetType)
+								.stloc     (obj)
+								.ldloca    (obj)
+								;
+						}
+						else
+							emit
+								.castclass (targetType)
+								;
+					}
+
+					foreach (ParameterInfo p in ips)
+						emit.ldarg(p);
+
+					if (targetMethod.IsStatic || targetMethod.IsFinal || targetMethod.DeclaringType.IsSealed)
+						emit
+							.call     (targetMethod)
+							.ret();
+					else
+						emit
+							.callvirt (targetMethod)
+							.ret();
+				}
+				else
+				{
+					// Method or property was not found.
+					// Insert an empty stub or stub that throws the NotImplementedException.
+					//
+					MustImplementAttribute attr = (MustImplementAttribute)
+						Attribute.GetCustomAttribute(interfaceMethod, typeof (MustImplementAttribute));
+
+					if (attr == null)
+					{
+						attr = (MustImplementAttribute)Attribute.GetCustomAttribute(
+							interfaceMethod.DeclaringType, typeof (MustImplementAttribute));
+						if (attr == null)
+							attr = _defaultAttribute;
+					}
+
+					// When the member is marked as 'Required' throw a build-time exception.
+					//
+					if (attr.Implement)
+					{
+						if (attr.ThrowException)
+						{
+							throw new TypeBuilderException(string.Format(
+								Resources.TypeBuilder_PublicMethodMustBeImplemented,
+								_objectTypes.Length > 0 && _objectTypes[0] != null ? _objectTypes[0].FullName : "???",
+								interfaceMethod));
+						}
+						else
+						{
+							// Implement == true, but ThrowException == false.
+							// In this case the null pointer will be returned.
+							// This mimics the 'as' operator behaviour.
+							//
+							return false;
+						}
+					}
+
+					if (attr.ThrowException)
+					{
+						string message = attr.ExceptionMessage;
+
+						if (message == null)
+						{
+							message = string.Format(Resources.TypeBuilder_PublicMethodNotImplemented,
+								_objectTypes.Length > 0 && _objectTypes[0] != null ? _objectTypes[0].FullName : "???",
+								interfaceMethod);
+						}
+
+						emit
+							.ldstr  (message)
+							.newobj (typeof(InvalidOperationException), typeof(string))
+							.@throw
+							.end();
+					}
+					else
+					{
+						// Emit a 'do nothing' stub.
+						//
+						LocalBuilder returnValue = null;
+
+						if (interfaceMethod.ReturnType != typeof(void))
+						{
+							returnValue = emit.DeclareLocal(interfaceMethod.ReturnType);
+							emit.Init(returnValue);
+						}
+
+						// Initialize out parameters.
+						//
+						ParameterInfo[] parameters = ips;
+
+						if (parameters != null)
+							emit.InitOutParameters(parameters);
+
+						if (returnValue != null)
+							emit.ldloc(returnValue);
+
+						emit.ret();
+					}
+				}
+			}
+
+			return true;
+		}
+	}
+}