2004-09-24 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / corlib / System / MonoCustomAttrs.cs
1 // System.MonoCustomAttrs.cs
2 // Hooks into the runtime to get custom attributes for reflection handles
3 //
4 // Authors:
5 //      Paolo Molaro (lupus@ximian.com)
6 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 //
8 // (c) 2002,2003 Ximian, Inc. (http://www.ximian.com)
9 //
10
11 //
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 using System;
35 using System.Reflection;
36 using System.Collections;
37 using System.Runtime.CompilerServices;
38
39 namespace System
40 {
41         internal class MonoCustomAttrs
42         {
43                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
44                 internal static extern object[] GetCustomAttributesInternal (ICustomAttributeProvider obj, bool pseudoAttrs);
45
46                 internal static object[] GetCustomAttributesBase (ICustomAttributeProvider obj)
47                 {
48                         object[] attrs = GetCustomAttributesInternal (obj, false);
49
50 #if NET_2_0 || BOOTSTRAP_NET_2_0
51                         object[] pseudoAttrs = null;
52
53                         /* FIXME: Add other types */
54                         if (obj is Type)
55                                 pseudoAttrs = ((Type)obj).GetPseudoCustomAttributes ();
56
57                         if (pseudoAttrs != null) {
58                                 object[] res = new object [attrs.Length + pseudoAttrs.Length];
59                                 System.Array.Copy (attrs, res, attrs.Length);
60                                 System.Array.Copy (pseudoAttrs, 0, res, attrs.Length, pseudoAttrs.Length);
61                                 return res;
62                         }
63                         else
64                                 return attrs;
65 #else
66                         return attrs;
67 #endif
68                 }
69
70                 internal static Attribute GetCustomAttribute (ICustomAttributeProvider obj,
71                                                                 Type attributeType,
72                                                                 bool inherit)
73                 {
74                         object[] res = GetCustomAttributes (obj, attributeType, inherit);
75                         if (res.Length == 0)
76                         {
77                                 return null;
78                         }
79                         else if (res.Length > 1)
80                         {
81                                 string msg = "'{0}' has more than one attribute of type '{1}";
82                                 msg = String.Format (msg, obj, attributeType);
83                                 throw new AmbiguousMatchException (msg);
84                         }
85
86                         return (Attribute) res[0];
87                 }
88
89                 internal static object[] GetCustomAttributes (ICustomAttributeProvider obj, Type attributeType, bool inherit)
90                 {
91                         if (obj == null)
92                                 throw new ArgumentNullException ("obj");
93
94                         object[] r;
95                         object[] res = GetCustomAttributesBase (obj);
96                         // shortcut
97                         if (!inherit && res.Length == 1)
98                         {
99                                 if (attributeType != null)
100                                 {
101                                         if (attributeType.IsAssignableFrom (res[0].GetType ()))
102                                         {
103                                                 r = (object[]) Array.CreateInstance (attributeType, 1);
104                                                 r[0] = res[0];
105                                         }
106                                         else
107                                         {
108                                                 r = (object[]) Array.CreateInstance (attributeType, 0);
109                                         }
110                                 }
111                                 else
112                                 {
113                                         r = (object[]) Array.CreateInstance (res[0].GetType (), 1);
114                                         r[0] = res[0];
115                                 }
116                                 return r;
117                         }
118
119                         // if AttributeType is sealed, and Inherited is set to false, then 
120                         // there's no use in scanning base types 
121                         if ((attributeType != null && attributeType.IsSealed) && inherit)
122                         {
123                                 AttributeUsageAttribute usageAttribute = RetrieveAttributeUsage (
124                                         attributeType);
125                                 if (!usageAttribute.Inherited)
126                                 {
127                                         inherit = false;
128                                 }
129                         }
130
131                         int initialSize = res.Length < 16 ? res.Length : 16;
132
133                         Hashtable attributeInfos = new Hashtable (initialSize);
134                         ArrayList a = new ArrayList (initialSize);
135                         ICustomAttributeProvider btype = obj;
136
137                         int inheritanceLevel = 0;
138
139                         do
140                         {
141                                 foreach (object attr in res)
142                                 {
143                                         AttributeUsageAttribute usage;
144
145                                         Type attrType = attr.GetType ();
146                                         if (attributeType != null)
147                                         {
148                                                 if (!attributeType.IsAssignableFrom (attrType))
149                                                 {
150                                                         continue;
151                                                 }
152                                         }
153
154                                         AttributeInfo firstAttribute = (AttributeInfo) attributeInfos[attrType];
155                                         if (firstAttribute != null)
156                                         {
157                                                 usage = firstAttribute.Usage;
158                                         }
159                                         else
160                                         {
161                                                 usage = RetrieveAttributeUsage (attrType);
162                                         }
163
164                                         // only add attribute to the list of attributes if 
165                                         // - we are on the first inheritance level, or the attribute can be inherited anyway
166                                         // and (
167                                         // - multiple attributes of the type are allowed
168                                         // or (
169                                         // - this is the first attribute we've discovered
170                                         // or
171                                         // - the attribute is on same inheritance level than the first 
172                                         //   attribute that was discovered for this attribute type ))
173                                         if ((inheritanceLevel == 0 || usage.Inherited) && (usage.AllowMultiple || 
174                                                 (firstAttribute == null || (firstAttribute != null 
175                                                         && firstAttribute.InheritanceLevel == inheritanceLevel))))
176                                         {
177                                                 a.Add (attr);
178                                         }
179
180                                         if (firstAttribute == null)
181                                         {
182                                                 attributeInfos.Add (attrType, new AttributeInfo (usage, inheritanceLevel));
183                                         }
184                                 }
185
186                                 if ((btype = GetBase (btype)) != null)
187                                 {
188                                         inheritanceLevel++;
189                                         res = GetCustomAttributesBase (btype);
190                                 }
191                         } while (inherit && btype != null);
192
193                         object[] array = null;
194                         if (attributeType == null || attributeType.IsValueType)
195                         {
196                                 array = (object[]) Array.CreateInstance (typeof(Attribute), a.Count);
197                         }
198                         else
199                         {
200                                 array = Array.CreateInstance (attributeType, a.Count) as object[];
201                         }
202
203                         // copy attributes to array
204                         a.CopyTo (array, 0);
205
206                         return array;
207                 }
208
209                 internal static object[] GetCustomAttributes (ICustomAttributeProvider obj, bool inherit)
210                 {
211                         if (obj == null)
212                                 throw new ArgumentNullException ("obj");
213
214                         if (!inherit)
215                                 return (object[]) GetCustomAttributesBase (obj).Clone ();
216
217                         return GetCustomAttributes (obj, null, inherit);
218                 }
219
220                 internal static bool IsDefined (ICustomAttributeProvider obj, Type attributeType, bool inherit)
221                 {
222                         object [] res = GetCustomAttributesBase (obj);
223                         foreach (object attr in res)
224                                 if (attributeType.Equals (attr.GetType ()))
225                                         return true;
226
227                         ICustomAttributeProvider btype;
228                         if (inherit && ((btype = GetBase (obj)) != null))
229                                 return IsDefined (btype, attributeType, inherit);
230
231                         return false;
232                 }
233
234                 // Handles Type, MonoProperty and MonoMethod.
235                 // The runtime has also cases for MonoEvent, MonoField, Assembly and ParameterInfo,
236                 // but for those we return null here.
237                 static ICustomAttributeProvider GetBase (ICustomAttributeProvider obj)
238                 {
239                         if (obj == null)
240                                 return null;
241
242                         if (obj is Type)
243                                 return ((Type) obj).BaseType;
244
245                         MethodInfo method = null;
246                         if (obj is MonoProperty)
247                         {
248                                 MonoProperty prop = (MonoProperty) obj;
249                                 method = prop.GetGetMethod (true);
250                                 if (method == null)
251                                         method = prop.GetSetMethod (true);
252                         }
253                         else if (obj is MonoMethod)
254                         {
255                                 method = (MethodInfo) obj;
256                         }
257
258                         /**
259                          * ParameterInfo -> null
260                          * Assembly -> null
261                          * MonoEvent -> null
262                          * MonoField -> null
263                          */
264                         if (method == null || !method.IsVirtual)
265                                 return null;
266
267                         MethodInfo baseMethod = method.GetBaseDefinition ();
268                         if (baseMethod == method)
269                                 return null;
270
271                         return baseMethod;
272                 }
273
274                 private static AttributeUsageAttribute RetrieveAttributeUsage (Type attributeType)
275                 {
276                         if (attributeType == typeof (AttributeUsageAttribute))
277                                 /* Avoid endless recursion */
278                                 return new AttributeUsageAttribute (AttributeTargets.Class);
279
280                         AttributeUsageAttribute usageAttribute = null;
281                         object[] attribs = GetCustomAttributes (attributeType,
282                                 MonoCustomAttrs.AttributeUsageType, false);
283                         if (attribs.Length == 0)
284                         {
285                                 // if no AttributeUsage was defined on the attribute level, then
286                                 // try to retrieve if from its base type
287                                 if (attributeType.BaseType != null)
288                                 {
289                                         usageAttribute = RetrieveAttributeUsage (attributeType.BaseType);
290
291                                 }
292                                 if (usageAttribute != null)
293                                 {
294                                         // return AttributeUsage of base class
295                                         return usageAttribute;
296
297                                 }
298                                 // return default AttributeUsageAttribute if no AttributeUsage 
299                                 // was defined on attribute, or its base class
300                                 return DefaultAttributeUsage;
301                         }
302                         // check if more than one AttributeUsageAttribute has been specified 
303                         // on the type
304                         // NOTE: compilers should prevent this, but that doesn't prevent
305                         // anyone from using IL ofcourse
306                         if (attribs.Length > 1)
307                         {
308                                 throw new FormatException ("Duplicate AttributeUsageAttribute cannot be specified on an attribute type.");
309                         }
310
311                         return ((AttributeUsageAttribute) attribs[0]);
312                 }
313
314                 private static readonly Type AttributeUsageType = typeof(AttributeUsageAttribute);
315                 private static readonly AttributeUsageAttribute DefaultAttributeUsage =
316                         new AttributeUsageAttribute (AttributeTargets.All);
317
318                 private class AttributeInfo
319                 {
320                         private AttributeUsageAttribute _usage;
321                         private int _inheritanceLevel;
322
323                         public AttributeInfo (AttributeUsageAttribute usage, int inheritanceLevel)
324                         {
325                                 _usage = usage;
326                                 _inheritanceLevel = inheritanceLevel;
327                         }
328
329                         public AttributeUsageAttribute Usage
330                         {
331                                 get
332                                 {
333                                         return _usage;
334                                 }
335                         }
336
337                         public int InheritanceLevel
338                         {
339                                 get
340                                 {
341                                         return _inheritanceLevel;
342                                 }
343                         }
344                 }
345         }
346 }
347