Hey,

When using the static methods Attribute.IsDefined or
Attribute.GetCustomAttributes on a ParameterInfo that is defined on a
virtual method, those methods are supposed to be walking the
inheritance chain to collect the attributes over the different
parameter definitions.

This patch adds support for this behavior. So far, we were only
dealing with the parameters defined on the current parameter. Now I
simply added a new internal method GetBaseMethod on MethodInfo that am
using to walk down the hierarchy.

Please review.

-- 
Jb Evain  <j...@nurv.fr>
Index: Test/System/AttributeTest.cs
===================================================================
--- Test/System/AttributeTest.cs	(revision 147238)
+++ Test/System/AttributeTest.cs	(working copy)
@@ -918,4 +918,132 @@
 		{
 		}
 	}
+
+	namespace ParamNamespace {
+
+		class FooAttribute : Attribute {}
+		class BarAttribute : Attribute {}
+
+		class DataAttribute : Attribute {
+
+			public string Data { get; set; }
+
+			public DataAttribute (string data)
+			{
+				this.Data = data;
+			}
+		}
+
+		class UltraBase {
+
+			public virtual void Bar ([Foo] string bar, [Data ("UltraBase.baz")] string baz)
+			{
+			}
+		}
+
+		class Base : UltraBase {
+
+			public override void Bar ([Data ("Base.bar")] string bar, string baz)
+			{
+			}
+		}
+
+		class Derived : Base {
+
+			public override void Bar ([Bar] string bar, [Data ("Derived.baz")] string baz)
+			{
+			}
+		}
+	}
+
+	[TestFixture]
+	public class ParamAttributeTest {
+
+		static ParameterInfo GetParameter (Type type, string method_name, string param_name)
+		{
+			foreach (var method in type.GetMethods ()) {
+				if (method.Name != method_name)
+					continue;
+
+				foreach (var parameter in method.GetParameters ())
+					if (parameter.Name == param_name)
+						return parameter;
+			}
+
+			return null;
+		}
+
+		[Test]
+		public void IsDefinedTopLevel ()
+		{
+			var parameter = GetParameter (typeof (ParamNamespace.Derived), "Bar", "bar");
+
+			Assert.IsNotNull (parameter);
+			Assert.IsTrue (Attribute.IsDefined (parameter, typeof (ParamNamespace.BarAttribute)));
+		}
+
+		[Test]
+		public void IsDefinedHierarchy ()
+		{
+			var parameter = GetParameter (typeof (ParamNamespace.Derived), "Bar", "bar");
+
+			Assert.IsNotNull (parameter);
+			Assert.IsTrue (Attribute.IsDefined (parameter, typeof (ParamNamespace.FooAttribute)));
+		}
+
+		[Test]
+		public void IsDefinedHierarchyMultiple ()
+		{
+			var parameter = GetParameter (typeof (ParamNamespace.Derived), "Bar", "baz");
+
+			Assert.IsNotNull (parameter);
+			Assert.IsTrue (Attribute.IsDefined (parameter, typeof (ParamNamespace.DataAttribute)));
+		}
+
+		[Test]
+		public void GetCustomAttributeTopLevel ()
+		{
+			var parameter = GetParameter (typeof (ParamNamespace.Derived), "Bar", "bar");
+
+			Assert.IsNotNull (Attribute.GetCustomAttribute (parameter, typeof (ParamNamespace.BarAttribute)));
+		}
+
+		[Test]
+		public void GetCustomAttributeHierarchy ()
+		{
+			var parameter = GetParameter (typeof (ParamNamespace.Derived), "Bar", "bar");
+			var data = (ParamNamespace.DataAttribute) Attribute.GetCustomAttribute (parameter, typeof (ParamNamespace.DataAttribute));
+			Assert.IsNotNull (data);
+			Assert.AreEqual ("Base.bar", data.Data);
+		}
+
+		[Test]
+		public void GetCustomAttributeHierarchyMultiple ()
+		{
+			var parameter = GetParameter (typeof (ParamNamespace.Derived), "Bar", "baz");
+			var data = (ParamNamespace.DataAttribute) Attribute.GetCustomAttribute (parameter, typeof (ParamNamespace.DataAttribute));
+			Assert.IsNotNull (data);
+			Assert.AreEqual ("Derived.baz", data.Data);
+		}
+
+		[Test]
+		public void GetAllCustomAttributes ()
+		{
+			var parameter = GetParameter (typeof (ParamNamespace.Derived), "Bar", "bar");
+			var attributes = (Attribute []) Attribute.GetCustomAttributes (parameter, true);
+			Assert.AreEqual (3, attributes.Length);
+			Assert.AreEqual (typeof (ParamNamespace.BarAttribute), attributes [0].GetType ());
+			Assert.AreEqual (typeof (ParamNamespace.DataAttribute), attributes [1].GetType ());
+			Assert.AreEqual (typeof (ParamNamespace.FooAttribute), attributes [2].GetType ());
+		}
+
+		[Test]
+		public void GetDataCustomAttributes ()
+		{
+			var parameter = GetParameter (typeof (ParamNamespace.Derived), "Bar", "baz");
+			var attributes = (ParamNamespace.DataAttribute []) Attribute.GetCustomAttributes (parameter, typeof (ParamNamespace.DataAttribute), true);
+			Assert.AreEqual (1, attributes.Length);
+			Assert.AreEqual ("Derived.baz", attributes [0].Data);
+		}
+	}
 }
Index: appdomain.c
===================================================================
--- appdomain.c	(revision 147245)
+++ appdomain.c	(working copy)
@@ -73,7 +73,7 @@
  * Changes which are already detected at runtime, like the addition
  * of icalls, do not require an increment.
  */
-#define MONO_CORLIB_VERSION 86
+#define MONO_CORLIB_VERSION 87
 
 typedef struct
 {
Index: icall-def.h
===================================================================
--- icall-def.h	(revision 147245)
+++ icall-def.h	(working copy)
@@ -617,7 +618,7 @@
 ICALL(MMETH_5, "MakeGenericMethod_impl", mono_reflection_bind_generic_method_parameters)
 ICALL(MMETH_6, "get_IsGenericMethod", ves_icall_MonoMethod_get_IsGenericMethod)
 ICALL(MMETH_7, "get_IsGenericMethodDefinition", ves_icall_MonoMethod_get_IsGenericMethodDefinition)
-ICALL(MMETH_8, "get_base_definition", ves_icall_MonoMethod_get_base_definition)
+ICALL(MMETH_8, "get_base_method", ves_icall_MonoMethod_get_base_method)
 ICALL(MMETH_9, "get_name", ves_icall_MonoMethod_get_name)
 
 ICALL_TYPE(MMETHI, "System.Reflection.MonoMethodInfo", MMETHI_1)
Index: icall.c
===================================================================
--- icall.c	(revision 147245)
+++ icall.c	(working copy)
@@ -6882,7 +6900,7 @@
 }
 
 static MonoReflectionMethod *
-ves_icall_MonoMethod_get_base_definition (MonoReflectionMethod *m)
+ves_icall_MonoMethod_get_base_method (MonoReflectionMethod *m, gboolean definition)
 {
 	MonoClass *klass, *parent;
 	MonoMethod *method = m->method;
@@ -6902,13 +6920,19 @@
 	if (klass->generic_class)
 		klass = klass->generic_class->container_class;
 
-	/* At the end of the loop, klass points to the eldest class that has this virtual function slot. */
-	for (parent = klass->parent; parent != NULL; parent = parent->parent) {
-		mono_class_setup_vtable (parent);
-		if (parent->vtable_size <= method->slot)
-			break;
-		klass = parent;
-	}		
+	if (definition) {
+		/* At the end of the loop, klass points to the eldest class that has this virtual function slot. */
+		for (parent = klass->parent; parent != NULL; parent = parent->parent) {
+			mono_class_setup_vtable (parent);
+			if (parent->vtable_size <= method->slot)
+				break;
+			klass = parent;
+		}
+	} else {
+		klass = klass->parent;
+		if (!klass)
+			return m;
+	}
 
 	if (klass == method->klass)
 		return m;
Index: System/Attribute.cs
===================================================================
--- System/Attribute.cs	(revision 147238)
+++ System/Attribute.cs	(working copy)
@@ -29,6 +29,7 @@
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
+using System.Collections.Generic;
 using System.Reflection;
 using System.Runtime.InteropServices;
 
@@ -36,7 +37,6 @@
 {
 	[AttributeUsage (AttributeTargets.All)]
 	[Serializable]
-
 	[ComVisible (true)]
 	[ComDefaultInterface (typeof (_Attribute))]
 	[ClassInterfaceAttribute (ClassInterfaceType.None)]
@@ -131,9 +131,7 @@
 			// neither parameter is allowed to be null
 			CheckParameters (element, attributeType);
 
-			// ParameterInfo inheritance hierarchies CAN NOT be searched for attributes, so the second
-			// parameter of GetCustomAttributes () is IGNORED.
-			object[] attributes = element.GetCustomAttributes (attributeType, inherit);
+			object[] attributes = GetCustomAttributes (element, attributeType, inherit);
 
 			return FindAttribute (attributes);
 		}
@@ -201,6 +199,10 @@
 			// element parameter is not allowed to be null
 			CheckParameters (element, attributeType);
 
+			Attribute [] attributes;
+			if (inherit && TryGetParamCustomAttributes (element, attributeType, out attributes))
+				return attributes;
+
 			return (Attribute []) element.GetCustomAttributes (attributeType, inherit);
 		}
 
@@ -263,7 +265,7 @@
 			// element parameter is not allowed to be null
 			CheckParameters (element, typeof (Attribute));
 
-			return (Attribute []) element.GetCustomAttributes (inherit);
+			return GetCustomAttributes (element, typeof (Attribute), inherit);
 		}
 
 		public override int GetHashCode ()
@@ -331,7 +333,6 @@
 			return element.IsDefined (attributeType, inherit);
 		}
 
-		// FIXME: MS apparently walks the inheritance way in some form.
 		public static bool IsDefined (ParameterInfo element, Type attributeType, bool inherit)
 		{
 			CheckParameters (element, attributeType);
@@ -339,10 +340,76 @@
 			if (element.IsDefined (attributeType, inherit))
 				return true;
 
-			// FIXME: MS walks up the inheritance chain in some crazy way
-			return IsDefined (element.Member, attributeType, inherit);
+			if (inherit)
+				return IsDefinedOnParameter (element, attributeType);
+
+			return false;
 		}
 
+		static bool IsDefinedOnParameter (ParameterInfo parameter, Type attributeType)
+		{
+			var member = parameter.Member;
+			if (member.MemberType != MemberTypes.Method)
+				return false;
+
+			var method = ((MethodInfo) member).GetBaseMethod ();
+
+			while (true) {
+				var param = method.GetParameters () [parameter.Position];
+				if (param.IsDefined (attributeType, false))
+					return true;
+
+				var base_method = method.GetBaseMethod ();
+				if (base_method == method)
+					break;
+
+				method = base_method;
+			}
+
+			return false;
+		}
+
+		static bool TryGetParamCustomAttributes (ParameterInfo parameter, Type attributeType, out Attribute [] attributes)
+		{
+			attributes = null;
+
+			if (parameter.Member.MemberType != MemberTypes.Method)
+				return false;
+
+			var method = (MethodInfo) parameter.Member;
+			var definition = method.GetBaseDefinition ();
+
+			if (method == definition)
+				return false;
+
+			var types = new List<Type> ();
+			var custom_attributes = new List<Attribute> ();
+
+			while (true) {
+				var param = method.GetParameters () [parameter.Position];
+				var param_attributes = (Attribute []) param.GetCustomAttributes (attributeType, false);
+				foreach (var param_attribute in param_attributes) {
+					var param_type = param_attribute.GetType ();
+					if (types.Contains (param_type))
+						continue;
+
+					types.Add (param_type);
+					custom_attributes.Add (param_attribute);
+				}
+
+				var base_method = method.GetBaseMethod ();
+				if (base_method == method)
+					break;
+
+				method = base_method;
+			}
+
+			attributes = (Attribute []) Array.CreateInstance (attributeType, custom_attributes.Count);
+			custom_attributes.CopyTo (attributes, 0);
+
+			return true;
+		}
+
 		public virtual bool Match (object obj)
 		{
 			// default action is the same as Equals.
Index: System/Environment.cs
===================================================================
--- System/Environment.cs	(revision 147238)
+++ System/Environment.cs	(working copy)
@@ -54,7 +54,7 @@
 		 * Changes which are already detected at runtime, like the addition
 		 * of icalls, do not require an increment.
 		 */
-		private const int mono_corlib_version = 86;
+		private const int mono_corlib_version = 87;
 
 		[ComVisible (true)]
 		public enum SpecialFolder
Index: System.Reflection/MonoMethod.cs
===================================================================
--- System.Reflection/MonoMethod.cs	(revision 147238)
+++ System.Reflection/MonoMethod.cs	(working copy)
@@ -126,13 +126,18 @@
 		internal static extern string get_name (MethodBase method);
 
 		[MethodImplAttribute(MethodImplOptions.InternalCall)]
-		internal static extern MonoMethod get_base_definition (MonoMethod method);
+		internal static extern MonoMethod get_base_method (MonoMethod method, bool definition);
 
 		public override MethodInfo GetBaseDefinition ()
 		{
-			return get_base_definition (this);
+			return get_base_method (this, true);
 		}
 
+		internal override MethodInfo GetBaseMethod ()
+		{
+			return get_base_method (this, false);
+		}
+
 		public override ParameterInfo ReturnParameter {
 			get {
 				return MonoMethodInfo.GetReturnParameterInfo (this);
Index: System.Reflection/MethodInfo.cs
===================================================================
--- System.Reflection/MethodInfo.cs	(revision 147238)
+++ System.Reflection/MethodInfo.cs	(working copy)
@@ -41,6 +41,11 @@
 
 		public abstract MethodInfo GetBaseDefinition();
 
+		internal virtual MethodInfo GetBaseMethod ()
+		{
+			return this;
+		}
+
 		protected MethodInfo() {
 		}
 
_______________________________________________
Mono-devel-list mailing list
Mono-devel-list@lists.ximian.com
http://lists.ximian.com/mailman/listinfo/mono-devel-list

Reply via email to