2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / corlib / System / MonoCustomAttrs.cs
index 4f338eda78dd3461bcf4cbf8ceab9990589db1a1..4a00d26446edf57982764e80cbab879c37522208 100755 (executable)
 // (c) 2002,2003 Ximian, Inc. (http://www.ximian.com)
 //
 
+//
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
 using System;
 using System.Reflection;
 using System.Collections;
 using System.Runtime.CompilerServices;
 
-namespace System {
-       internal class MonoCustomAttrs {
+namespace System
+{
+       internal class MonoCustomAttrs
+       {
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               internal static extern object[] GetCustomAttributesInternal (ICustomAttributeProvider obj, bool pseudoAttrs);
 
-               static Hashtable handle_to_attrs = new Hashtable ();
+               internal static object[] GetCustomAttributesBase (ICustomAttributeProvider obj)
+               {
+                       object[] attrs = GetCustomAttributesInternal (obj, false);
 
-               [MethodImplAttribute(MethodImplOptions.InternalCall)]
-               internal static extern object[] GetCustomAttributes (ICustomAttributeProvider obj);
+#if NET_2_0 || BOOTSTRAP_NET_2_0
+                       object[] pseudoAttrs = null;
 
-               private static object[] from_cache (ICustomAttributeProvider obj)
-               {
-                       object[] res = (object []) handle_to_attrs [obj];
-                       if (res != null)
+                       /* FIXME: Add other types */
+                       if (obj is Type)
+                               pseudoAttrs = ((Type)obj).GetPseudoCustomAttributes ();
+                       else if (obj is MonoMethod)
+                               pseudoAttrs = ((MonoMethod)obj).GetPseudoCustomAttributes ();
+                       else if (obj is FieldInfo)
+                               pseudoAttrs = ((FieldInfo)obj).GetPseudoCustomAttributes ();
+                       else if (obj is ParameterInfo)
+                               pseudoAttrs = ((ParameterInfo)obj).GetPseudoCustomAttributes ();
+
+                       if (pseudoAttrs != null) {
+                               object[] res = new object [attrs.Length + pseudoAttrs.Length];
+                               System.Array.Copy (attrs, res, attrs.Length);
+                               System.Array.Copy (pseudoAttrs, 0, res, attrs.Length, pseudoAttrs.Length);
                                return res;
-                       res = GetCustomAttributes (obj);
-                       handle_to_attrs.Add (obj, res);
-                       return res;
+                       }
+                       else
+                               return attrs;
+#else
+                       return attrs;
+#endif
                }
 
                internal static Attribute GetCustomAttribute (ICustomAttributeProvider obj,
-                                                             Type attributeType,
-                                                             bool inherit)
+                                                               Type attributeType,
+                                                               bool inherit)
                {
-                       if (obj == null)
-                               throw new ArgumentNullException ("attribute_type"); // argument name in the caller
-
-                       object[] res = from_cache (obj);
-                       ICustomAttributeProvider btype = obj;
-                       Attribute result = null;
-
-                       do {
-                               foreach (object attr in res) {
-                                       if (!attributeType.IsAssignableFrom (attr.GetType ()))
-                                               continue;
-
-                                       if (result != null) {
-                                               string msg = "'{0}' has more than one attribute of type '{1}";
-                                               msg = String.Format (msg, obj, attributeType);
-                                               throw new AmbiguousMatchException (msg);
-                                       }
-                                       result = (Attribute) attr;
-                               }
-
-                               if (inherit && (btype = GetBase (btype)) != null)
-                                       res = from_cache (btype);
-
-                       // Stop when encounters the first one for a given provider.
-                       } while (inherit && result == null && btype != null);
+                       object[] res = GetCustomAttributes (obj, attributeType, inherit);
+                       if (res.Length == 0)
+                       {
+                               return null;
+                       }
+                       else if (res.Length > 1)
+                       {
+                               string msg = "'{0}' has more than one attribute of type '{1}";
+                               msg = String.Format (msg, obj, attributeType);
+                               throw new AmbiguousMatchException (msg);
+                       }
 
-                       return result;
+                       return (Attribute) res[0];
                }
 
                internal static object[] GetCustomAttributes (ICustomAttributeProvider obj, Type attributeType, bool inherit)
                {
                        if (obj == null)
-                               return (object []) Array.CreateInstance (attributeType, 0);
+                               throw new ArgumentNullException ("obj");
 
                        object[] r;
-                       object[] res = from_cache (obj);
+                       object[] res = GetCustomAttributesBase (obj);
                        // shortcut
-                       if (!inherit && res.Length == 1) {
-                               if (attributeType.IsAssignableFrom (res[0].GetType ())) {
-                                       r = (object []) Array.CreateInstance (attributeType, 1);
-                                       r [0] = res [0];
-                               } else {
-                                       r = (object []) Array.CreateInstance (attributeType, 0);
+                       if (!inherit && res.Length == 1)
+                       {
+                               if (attributeType != null)
+                               {
+                                       if (attributeType.IsAssignableFrom (res[0].GetType ()))
+                                       {
+                                               r = (object[]) Array.CreateInstance (attributeType, 1);
+                                               r[0] = res[0];
+                                       }
+                                       else
+                                       {
+                                               r = (object[]) Array.CreateInstance (attributeType, 0);
+                                       }
+                               }
+                               else
+                               {
+                                       r = (object[]) Array.CreateInstance (res[0].GetType (), 1);
+                                       r[0] = res[0];
                                }
                                return r;
                        }
 
-                       ArrayList a = new ArrayList ();
+                       // if AttributeType is sealed, and Inherited is set to false, then 
+                       // there's no use in scanning base types 
+                       if ((attributeType != null && attributeType.IsSealed) && inherit)
+                       {
+                               AttributeUsageAttribute usageAttribute = RetrieveAttributeUsage (
+                                       attributeType);
+                               if (!usageAttribute.Inherited)
+                               {
+                                       inherit = false;
+                               }
+                       }
+
+                       int initialSize = res.Length < 16 ? res.Length : 16;
+
+                       Hashtable attributeInfos = new Hashtable (initialSize);
+                       ArrayList a = new ArrayList (initialSize);
                        ICustomAttributeProvider btype = obj;
-                       do {
+
+                       int inheritanceLevel = 0;
+
+                       do
+                       {
                                foreach (object attr in res)
-                                       if (attributeType.IsAssignableFrom (attr.GetType ()))
+                               {
+                                       AttributeUsageAttribute usage;
+
+                                       Type attrType = attr.GetType ();
+                                       if (attributeType != null)
+                                       {
+                                               if (!attributeType.IsAssignableFrom (attrType))
+                                               {
+                                                       continue;
+                                               }
+                                       }
+
+                                       AttributeInfo firstAttribute = (AttributeInfo) attributeInfos[attrType];
+                                       if (firstAttribute != null)
+                                       {
+                                               usage = firstAttribute.Usage;
+                                       }
+                                       else
+                                       {
+                                               usage = RetrieveAttributeUsage (attrType);
+                                       }
+
+                                       // only add attribute to the list of attributes if 
+                                       // - we are on the first inheritance level, or the attribute can be inherited anyway
+                                       // and (
+                                       // - multiple attributes of the type are allowed
+                                       // or (
+                                       // - this is the first attribute we've discovered
+                                       // or
+                                       // - the attribute is on same inheritance level than the first 
+                                       //   attribute that was discovered for this attribute type ))
+                                       if ((inheritanceLevel == 0 || usage.Inherited) && (usage.AllowMultiple || 
+                                               (firstAttribute == null || (firstAttribute != null 
+                                                       && firstAttribute.InheritanceLevel == inheritanceLevel))))
+                                       {
                                                a.Add (attr);
+                                       }
+
+                                       if (firstAttribute == null)
+                                       {
+                                               attributeInfos.Add (attrType, new AttributeInfo (usage, inheritanceLevel));
+                                       }
+                               }
 
                                if ((btype = GetBase (btype)) != null)
-                                       res = from_cache (btype);
+                               {
+                                       inheritanceLevel++;
+                                       res = GetCustomAttributesBase (btype);
+                               }
                        } while (inherit && btype != null);
 
-                       return (object []) a.ToArray (attributeType);
+                       object[] array = null;
+                       if (attributeType == null || attributeType.IsValueType)
+                       {
+                               array = (object[]) Array.CreateInstance (typeof(Attribute), a.Count);
+                       }
+                       else
+                       {
+                               array = Array.CreateInstance (attributeType, a.Count) as object[];
+                       }
+
+                       // copy attributes to array
+                       a.CopyTo (array, 0);
+
+                       return array;
                }
 
-               internal static object [] GetCustomAttributes (ICustomAttributeProvider obj, bool inherit)
+               internal static object[] GetCustomAttributes (ICustomAttributeProvider obj, bool inherit)
                {
                        if (obj == null)
-                               return new object [0]; //FIXME: Should i throw an exception here?
+                               throw new ArgumentNullException ("obj");
 
                        if (!inherit)
-                               return (object []) from_cache (obj).Clone ();
+                               return (object[]) GetCustomAttributesBase (obj).Clone ();
 
-                       ArrayList a = new ArrayList ();
-                       ICustomAttributeProvider btype = obj;
-                       a.AddRange (from_cache (btype));
-                       while ((btype = GetBase (btype)) != null)
-                               a.AddRange (from_cache (btype));
-
-                       return (object []) a.ToArray (typeof (Attribute));
+                       return GetCustomAttributes (obj, null, inherit);
                }
 
                internal static bool IsDefined (ICustomAttributeProvider obj, Type attributeType, bool inherit)
                {
-                       object[] res = from_cache (obj);
-                       foreach (object attr in res) {
+                       object [] res = GetCustomAttributesBase (obj);
+                       foreach (object attr in res)
                                if (attributeType.Equals (attr.GetType ()))
                                        return true;
-                       }
 
-                       ICustomAttributeProvider btype = GetBase (obj);
-                       if (inherit && (btype != null))
+                       ICustomAttributeProvider btype;
+                       if (inherit && ((btype = GetBase (obj)) != null))
                                return IsDefined (btype, attributeType, inherit);
 
                        return false;
@@ -140,13 +249,16 @@ namespace System {
                                return ((Type) obj).BaseType;
 
                        MethodInfo method = null;
-                       if (obj is MonoProperty) {
+                       if (obj is MonoProperty)
+                       {
                                MonoProperty prop = (MonoProperty) obj;
-                               method = prop.GetGetMethod ();
+                               method = prop.GetGetMethod (true);
                                if (method == null)
-                                       method = prop.GetSetMethod ();
-                       } else if (obj is MonoMethod) {
-                               method = (MethodInfo) obj; 
+                                       method = prop.GetSetMethod (true);
+                       }
+                       else if (obj is MonoMethod)
+                       {
+                               method = (MethodInfo) obj;
                        }
 
                        /**
@@ -164,6 +276,78 @@ namespace System {
 
                        return baseMethod;
                }
+
+               private static AttributeUsageAttribute RetrieveAttributeUsage (Type attributeType)
+               {
+                       if (attributeType == typeof (AttributeUsageAttribute))
+                               /* Avoid endless recursion */
+                               return new AttributeUsageAttribute (AttributeTargets.Class);
+
+                       AttributeUsageAttribute usageAttribute = null;
+                       object[] attribs = GetCustomAttributes (attributeType,
+                               MonoCustomAttrs.AttributeUsageType, false);
+                       if (attribs.Length == 0)
+                       {
+                               // if no AttributeUsage was defined on the attribute level, then
+                               // try to retrieve if from its base type
+                               if (attributeType.BaseType != null)
+                               {
+                                       usageAttribute = RetrieveAttributeUsage (attributeType.BaseType);
+
+                               }
+                               if (usageAttribute != null)
+                               {
+                                       // return AttributeUsage of base class
+                                       return usageAttribute;
+
+                               }
+                               // return default AttributeUsageAttribute if no AttributeUsage 
+                               // was defined on attribute, or its base class
+                               return DefaultAttributeUsage;
+                       }
+                       // check if more than one AttributeUsageAttribute has been specified 
+                       // on the type
+                       // NOTE: compilers should prevent this, but that doesn't prevent
+                       // anyone from using IL ofcourse
+                       if (attribs.Length > 1)
+                       {
+                               throw new FormatException ("Duplicate AttributeUsageAttribute cannot be specified on an attribute type.");
+                       }
+
+                       return ((AttributeUsageAttribute) attribs[0]);
+               }
+
+               private static readonly Type AttributeUsageType = typeof(AttributeUsageAttribute);
+               private static readonly AttributeUsageAttribute DefaultAttributeUsage =
+                       new AttributeUsageAttribute (AttributeTargets.All);
+
+               private class AttributeInfo
+               {
+                       private AttributeUsageAttribute _usage;
+                       private int _inheritanceLevel;
+
+                       public AttributeInfo (AttributeUsageAttribute usage, int inheritanceLevel)
+                       {
+                               _usage = usage;
+                               _inheritanceLevel = inheritanceLevel;
+                       }
+
+                       public AttributeUsageAttribute Usage
+                       {
+                               get
+                               {
+                                       return _usage;
+                               }
+                       }
+
+                       public int InheritanceLevel
+                       {
+                               get
+                               {
+                                       return _inheritanceLevel;
+                               }
+                       }
+               }
        }
 }