2008-03-06 Marek Safar <marek.safar@gmail.com>
[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 #if  NET_2_0
40 using System.Collections.Generic;
41 #endif
42
43 namespace System
44 {
45         internal class MonoCustomAttrs
46         {
47                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
48                 internal static extern object[] GetCustomAttributesInternal (ICustomAttributeProvider obj, Type attributeType, bool pseudoAttrs);
49
50                 internal static object[] GetPseudoCustomAttributes (ICustomAttributeProvider obj, Type attributeType) {
51 #if NET_2_0 || BOOTSTRAP_NET_2_0
52                         object[] pseudoAttrs = null;
53
54                         /* FIXME: Add other types */
55                         if (obj is MonoMethod)
56                                 pseudoAttrs = ((MonoMethod)obj).GetPseudoCustomAttributes ();
57                         else if (obj is FieldInfo)
58                                 pseudoAttrs = ((FieldInfo)obj).GetPseudoCustomAttributes ();
59                         else if (obj is ParameterInfo)
60                                 pseudoAttrs = ((ParameterInfo)obj).GetPseudoCustomAttributes ();
61                         else if (obj is Type)
62                                 pseudoAttrs = ((Type)obj).GetPseudoCustomAttributes ();
63
64                         if ((attributeType != null) && (pseudoAttrs != null)) {
65                                 for (int i = 0; i < pseudoAttrs.Length; ++i)
66                                         if (attributeType.IsAssignableFrom (pseudoAttrs [i].GetType ()))
67                                                 if (pseudoAttrs.Length == 1)
68                                                         return pseudoAttrs;
69                                                 else
70                                                         return new object [] { pseudoAttrs [i] };
71                                 return new object [0];
72                         }
73                         else
74                                 return pseudoAttrs;
75 #else
76                         return null;
77 #endif
78                 }
79
80                 internal static object[] GetCustomAttributesBase (ICustomAttributeProvider obj, Type attributeType)
81                 {
82                         object[] attrs = GetCustomAttributesInternal (obj, attributeType, false);
83
84                         object[] pseudoAttrs = GetPseudoCustomAttributes (obj, attributeType);
85                         if (pseudoAttrs != null) {
86                                 object[] res = new object [attrs.Length + pseudoAttrs.Length];
87                                 System.Array.Copy (attrs, res, attrs.Length);
88                                 System.Array.Copy (pseudoAttrs, 0, res, attrs.Length, pseudoAttrs.Length);
89                                 return res;
90                         }
91                         else
92                                 return attrs;
93                 }
94
95                 internal static Attribute GetCustomAttribute (ICustomAttributeProvider obj,
96                                                                 Type attributeType,
97                                                                 bool inherit)
98                 {
99                         object[] res = GetCustomAttributes (obj, attributeType, inherit);
100                         if (res.Length == 0)
101                         {
102                                 return null;
103                         }
104                         else if (res.Length > 1)
105                         {
106                                 string msg = "'{0}' has more than one attribute of type '{1}";
107                                 msg = String.Format (msg, obj, attributeType);
108                                 throw new AmbiguousMatchException (msg);
109                         }
110
111                         return (Attribute) res[0];
112                 }
113
114                 internal static object[] GetCustomAttributes (ICustomAttributeProvider obj, Type attributeType, bool inherit)
115                 {
116                         if (obj == null)
117                                 throw new ArgumentNullException ("obj");
118                         if (attributeType == null)
119                                 throw new ArgumentNullException ("attributeType");      
120
121                         if (attributeType == typeof (MonoCustomAttrs))
122                                 attributeType = null;
123                         
124                         object[] r;
125                         object[] res = GetCustomAttributesBase (obj, attributeType);
126                         // shortcut
127                         if (!inherit && res.Length == 1)
128                         {
129                                 if (attributeType != null)
130                                 {
131                                         if (attributeType.IsAssignableFrom (res[0].GetType ()))
132                                         {
133                                                 r = (object[]) Array.CreateInstance (attributeType, 1);
134                                                 r[0] = res[0];
135                                         }
136                                         else
137                                         {
138                                                 r = (object[]) Array.CreateInstance (attributeType, 0);
139                                         }
140                                 }
141                                 else
142                                 {
143                                         r = (object[]) Array.CreateInstance (res[0].GetType (), 1);
144                                         r[0] = res[0];
145                                 }
146                                 return r;
147                         }
148
149                         // if AttributeType is sealed, and Inherited is set to false, then 
150                         // there's no use in scanning base types 
151                         if ((attributeType != null && attributeType.IsSealed) && inherit)
152                         {
153                                 AttributeUsageAttribute usageAttribute = RetrieveAttributeUsage (
154                                         attributeType);
155                                 if (!usageAttribute.Inherited)
156                                 {
157                                         inherit = false;
158                                 }
159                         }
160
161                         int initialSize = res.Length < 16 ? res.Length : 16;
162
163                         Hashtable attributeInfos = new Hashtable (initialSize);
164                         ArrayList a = new ArrayList (initialSize);
165                         ICustomAttributeProvider btype = obj;
166
167                         int inheritanceLevel = 0;
168
169                         do
170                         {
171                                 foreach (object attr in res)
172                                 {
173                                         AttributeUsageAttribute usage;
174
175                                         Type attrType = attr.GetType ();
176                                         if (attributeType != null)
177                                         {
178                                                 if (!attributeType.IsAssignableFrom (attrType))
179                                                 {
180                                                         continue;
181                                                 }
182                                         }
183
184                                         AttributeInfo firstAttribute = (AttributeInfo) attributeInfos[attrType];
185                                         if (firstAttribute != null)
186                                         {
187                                                 usage = firstAttribute.Usage;
188                                         }
189                                         else
190                                         {
191                                                 usage = RetrieveAttributeUsage (attrType);
192                                         }
193
194                                         // only add attribute to the list of attributes if 
195                                         // - we are on the first inheritance level, or the attribute can be inherited anyway
196                                         // and (
197                                         // - multiple attributes of the type are allowed
198                                         // or (
199                                         // - this is the first attribute we've discovered
200                                         // or
201                                         // - the attribute is on same inheritance level than the first 
202                                         //   attribute that was discovered for this attribute type ))
203                                         if ((inheritanceLevel == 0 || usage.Inherited) && (usage.AllowMultiple || 
204                                                 (firstAttribute == null || (firstAttribute != null 
205                                                         && firstAttribute.InheritanceLevel == inheritanceLevel))))
206                                         {
207                                                 a.Add (attr);
208                                         }
209
210                                         if (firstAttribute == null)
211                                         {
212                                                 attributeInfos.Add (attrType, new AttributeInfo (usage, inheritanceLevel));
213                                         }
214                                 }
215
216                                 if ((btype = GetBase (btype)) != null)
217                                 {
218                                         inheritanceLevel++;
219                                         res = GetCustomAttributesBase (btype, attributeType);
220                                 }
221                         } while (inherit && btype != null);
222
223                         object[] array = null;
224                         if (attributeType == null || attributeType.IsValueType)
225                         {
226                                 array = (object[]) Array.CreateInstance (typeof(Attribute), a.Count);
227                         }
228                         else
229                         {
230                                 array = Array.CreateInstance (attributeType, a.Count) as object[];
231                         }
232
233                         // copy attributes to array
234                         a.CopyTo (array, 0);
235
236                         return array;
237                 }
238
239                 internal static object[] GetCustomAttributes (ICustomAttributeProvider obj, bool inherit)
240                 {
241                         if (obj == null)
242                                 throw new ArgumentNullException ("obj");
243
244                         if (!inherit)
245                                 return (object[]) GetCustomAttributesBase (obj, null).Clone ();
246
247                         return GetCustomAttributes (obj, typeof (MonoCustomAttrs), inherit);
248                 }
249
250 #if NET_2_0
251                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
252                 static extern CustomAttributeData [] GetCustomAttributesDataInternal (ICustomAttributeProvider obj);
253
254                 internal static IList<CustomAttributeData> GetCustomAttributesData (ICustomAttributeProvider obj)
255                 {
256                         if (obj == null)
257                                 throw new ArgumentNullException ("obj");
258
259                         CustomAttributeData [] attrs = GetCustomAttributesDataInternal (obj);
260                         return Array.AsReadOnly<CustomAttributeData> (attrs);
261                 }
262 #endif
263
264                 internal static bool IsDefined (ICustomAttributeProvider obj, Type attributeType, bool inherit)
265                 {
266                         if (attributeType == null)
267                                 throw new ArgumentNullException ("attributeType");
268
269                         if (obj.GetType ().Assembly != typeof (int).Assembly)
270                                 // User types might overwrite GetCustomAttributes () but not 
271                                 // IsDefined ().
272                                 return obj.GetCustomAttributes (attributeType, inherit).Length > 0;
273
274                         if (IsDefinedInternal (obj, attributeType))
275                                 return true;
276
277                         object[] pseudoAttrs = GetPseudoCustomAttributes (obj, attributeType);
278                         if (pseudoAttrs != null) {
279                                 for (int i = 0; i < pseudoAttrs.Length; ++i)
280                                         if (attributeType.IsAssignableFrom (pseudoAttrs [i].GetType ()))
281                                                 return true;
282                         }
283
284 #if ONLY_1_1
285                         if (inherit) {
286                                 AttributeUsageAttribute usage = RetrieveAttributeUsage (attributeType);
287                                 if (!usage.Inherited)
288                                         inherit = false;
289                         }
290 #endif
291
292                         // FIXME (bug #82431):
293                         // on 2.0 profile we should always walk the inheritance
294                         // chain and base the behavior on the inheritance level:
295                         //
296                         // 0  : return true if "attributeType" is assignable from
297                         // any of the custom attributes
298                         //
299                         // > 0: return true if "attributeType" is assignable from
300                         // any of the custom attributes and AttributeUsageAttribute
301                         // .Inherited of the assignable attribute is true
302
303                         ICustomAttributeProvider btype;
304                         if (inherit && ((btype = GetBase (obj)) != null))
305                                 return IsDefined (btype, attributeType, inherit);
306
307                         return false;
308                 }
309
310                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
311                 internal static extern bool IsDefinedInternal (ICustomAttributeProvider obj, Type AttributeType);
312
313                 static PropertyInfo GetBasePropertyDefinition (PropertyInfo property)
314                 {
315                         MethodInfo method = property.GetGetMethod (true);
316                         if (method == null || !method.IsVirtual)
317                                 method = property.GetSetMethod (true);
318                         if (method == null || !method.IsVirtual)
319                                 return null;
320
321                         MethodInfo baseMethod = method.GetBaseDefinition ();
322                         if (baseMethod != null && baseMethod != method) {
323                                 ParameterInfo[] parameters = property.GetIndexParameters ();
324                                 if (parameters != null && parameters.Length > 0) {
325                                         Type[] paramTypes = new Type[parameters.Length];
326                                         for (int i=0; i < paramTypes.Length; i++)
327                                                 paramTypes[i] = parameters[i].ParameterType;
328                                         return baseMethod.DeclaringType.GetProperty (property.Name, property.PropertyType, 
329                                                                                      paramTypes);
330                                 } else {
331                                         return baseMethod.DeclaringType.GetProperty (property.Name, property.PropertyType);
332                                 }
333                         }
334                         return null;
335
336                 }
337
338                 // Handles Type, MonoProperty and MonoMethod.
339                 // The runtime has also cases for MonoEvent, MonoField, Assembly and ParameterInfo,
340                 // but for those we return null here.
341                 static ICustomAttributeProvider GetBase (ICustomAttributeProvider obj)
342                 {
343                         if (obj == null)
344                                 return null;
345
346                         if (obj is Type)
347                                 return ((Type) obj).BaseType;
348
349                         MethodInfo method = null;
350                         if (obj is MonoProperty)
351                                 return GetBasePropertyDefinition ((MonoProperty) obj);
352                         else if (obj is MonoMethod)
353                                 method = (MethodInfo) obj;
354
355                         /**
356                          * ParameterInfo -> null
357                          * Assembly -> null
358                          * MonoEvent -> null
359                          * MonoField -> null
360                          */
361                         if (method == null || !method.IsVirtual)
362                                 return null;
363
364                         MethodInfo baseMethod = method.GetBaseDefinition ();
365                         if (baseMethod == method)
366                                 return null;
367
368                         return baseMethod;
369                 }
370
371                 private static AttributeUsageAttribute RetrieveAttributeUsage (Type attributeType)
372                 {
373                         if (attributeType == typeof (AttributeUsageAttribute))
374                                 /* Avoid endless recursion */
375                                 return new AttributeUsageAttribute (AttributeTargets.Class);
376
377                         AttributeUsageAttribute usageAttribute = null;
378                         object[] attribs = GetCustomAttributes (attributeType,
379                                 MonoCustomAttrs.AttributeUsageType, false);
380                         if (attribs.Length == 0)
381                         {
382                                 // if no AttributeUsage was defined on the attribute level, then
383                                 // try to retrieve if from its base type
384                                 if (attributeType.BaseType != null)
385                                 {
386                                         usageAttribute = RetrieveAttributeUsage (attributeType.BaseType);
387
388                                 }
389                                 if (usageAttribute != null)
390                                 {
391                                         // return AttributeUsage of base class
392                                         return usageAttribute;
393
394                                 }
395                                 // return default AttributeUsageAttribute if no AttributeUsage 
396                                 // was defined on attribute, or its base class
397                                 return DefaultAttributeUsage;
398                         }
399                         // check if more than one AttributeUsageAttribute has been specified 
400                         // on the type
401                         // NOTE: compilers should prevent this, but that doesn't prevent
402                         // anyone from using IL ofcourse
403                         if (attribs.Length > 1)
404                         {
405                                 throw new FormatException ("Duplicate AttributeUsageAttribute cannot be specified on an attribute type.");
406                         }
407
408                         return ((AttributeUsageAttribute) attribs[0]);
409                 }
410
411                 private static readonly Type AttributeUsageType = typeof(AttributeUsageAttribute);
412                 private static readonly AttributeUsageAttribute DefaultAttributeUsage =
413                         new AttributeUsageAttribute (AttributeTargets.All);
414
415                 private class AttributeInfo
416                 {
417                         private AttributeUsageAttribute _usage;
418                         private int _inheritanceLevel;
419
420                         public AttributeInfo (AttributeUsageAttribute usage, int inheritanceLevel)
421                         {
422                                 _usage = usage;
423                                 _inheritanceLevel = inheritanceLevel;
424                         }
425
426                         public AttributeUsageAttribute Usage
427                         {
428                                 get
429                                 {
430                                         return _usage;
431                                 }
432                         }
433
434                         public int InheritanceLevel
435                         {
436                                 get
437                                 {
438                                         return _inheritanceLevel;
439                                 }
440                         }
441                 }
442         }
443 }
444