2004-09-23 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 #if NET_2_0 || BOOTSTRAP_NET_2_0
44                 internal static readonly bool pseudoAttrs = true;
45 #else
46                 internal static readonly bool pseudoAttrs = false;
47 #endif
48
49                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
50                 internal static extern object[] GetCustomAttributesInternal (ICustomAttributeProvider obj, bool pseudoAttrs);
51
52                 internal static Attribute GetCustomAttribute (ICustomAttributeProvider obj,
53                                                                 Type attributeType,
54                                                                 bool inherit)
55                 {
56                         object[] res = GetCustomAttributes (obj, attributeType, inherit);
57                         if (res.Length == 0)
58                         {
59                                 return null;
60                         }
61                         else if (res.Length > 1)
62                         {
63                                 string msg = "'{0}' has more than one attribute of type '{1}";
64                                 msg = String.Format (msg, obj, attributeType);
65                                 throw new AmbiguousMatchException (msg);
66                         }
67
68                         return (Attribute) res[0];
69                 }
70
71                 internal static object[] GetCustomAttributes (ICustomAttributeProvider obj, Type attributeType, bool inherit)
72                 {
73                         if (obj == null)
74                                 throw new ArgumentNullException ("obj");
75
76                         object[] r;
77                         object[] res = GetCustomAttributesInternal (obj, pseudoAttrs);
78                         // shortcut
79                         if (!inherit && res.Length == 1)
80                         {
81                                 if (attributeType != null)
82                                 {
83                                         if (attributeType.IsAssignableFrom (res[0].GetType ()))
84                                         {
85                                                 r = (object[]) Array.CreateInstance (attributeType, 1);
86                                                 r[0] = res[0];
87                                         }
88                                         else
89                                         {
90                                                 r = (object[]) Array.CreateInstance (attributeType, 0);
91                                         }
92                                 }
93                                 else
94                                 {
95                                         r = (object[]) Array.CreateInstance (res[0].GetType (), 1);
96                                         r[0] = res[0];
97                                 }
98                                 return r;
99                         }
100
101                         // if AttributeType is sealed, and Inherited is set to false, then 
102                         // there's no use in scanning base types 
103                         if ((attributeType != null && attributeType.IsSealed) && !inherit)
104                         {
105                                 AttributeUsageAttribute usageAttribute = RetrieveAttributeUsage (
106                                         attributeType);
107                                 if (!usageAttribute.Inherited)
108                                 {
109                                         inherit = false;
110                                 }
111                         }
112
113                         int initialSize = res.Length < 16 ? res.Length : 16;
114
115                         Hashtable attributeInfos = new Hashtable (initialSize);
116                         ArrayList a = new ArrayList (initialSize);
117                         ICustomAttributeProvider btype = obj;
118
119                         int inheritanceLevel = 0;
120
121                         do
122                         {
123                                 foreach (object attr in res)
124                                 {
125                                         AttributeUsageAttribute usage;
126
127                                         Type attrType = attr.GetType ();
128                                         if (attributeType != null)
129                                         {
130                                                 if (!attributeType.IsAssignableFrom (attrType))
131                                                 {
132                                                         continue;
133                                                 }
134                                         }
135
136                                         AttributeInfo firstAttribute = (AttributeInfo) attributeInfos[attrType];
137                                         if (firstAttribute != null)
138                                         {
139                                                 usage = firstAttribute.Usage;
140                                         }
141                                         else
142                                         {
143                                                 usage = RetrieveAttributeUsage (attrType);
144                                         }
145
146                                         // only add attribute to the list of attributes if 
147                                         // - we are on the first inheritance level, or the attribute can be inherited anyway
148                                         // and (
149                                         // - multiple attributes of the type are allowed
150                                         // or (
151                                         // - this is the first attribute we've discovered
152                                         // or
153                                         // - the attribute is on same inheritance level than the first 
154                                         //   attribute that was discovered for this attribute type ))
155                                         if ((inheritanceLevel == 0 || usage.Inherited) && (usage.AllowMultiple || 
156                                                 (firstAttribute == null || (firstAttribute != null 
157                                                         && firstAttribute.InheritanceLevel == inheritanceLevel))))
158                                         {
159                                                 a.Add (attr);
160                                         }
161
162                                         if (firstAttribute == null)
163                                         {
164                                                 attributeInfos.Add (attrType, new AttributeInfo (usage, inheritanceLevel));
165                                         }
166                                 }
167
168                                 if ((btype = GetBase (btype)) != null)
169                                 {
170                                         inheritanceLevel++;
171                                         res = GetCustomAttributesInternal (btype, pseudoAttrs);
172                                 }
173                         } while (inherit && btype != null);
174
175                         object[] array = null;
176                         if (attributeType == null || attributeType.IsValueType)
177                         {
178                                 array = (object[]) Array.CreateInstance (typeof(Attribute), a.Count);
179                         }
180                         else
181                         {
182                                 array = Array.CreateInstance (attributeType, a.Count) as object[];
183                         }
184
185                         // copy attributes to array
186                         a.CopyTo (array, 0);
187
188                         return array;
189                 }
190
191                 internal static object[] GetCustomAttributes (ICustomAttributeProvider obj, bool inherit)
192                 {
193                         if (obj == null)
194                                 throw new ArgumentNullException ("obj");
195
196                         if (!inherit)
197                                 return (object[]) GetCustomAttributesInternal (obj, pseudoAttrs).Clone ();
198
199                         return GetCustomAttributes (obj, null, inherit);
200                 }
201
202                 internal static bool IsDefined (ICustomAttributeProvider obj, Type attributeType, bool inherit)
203                 {
204                         object [] res = GetCustomAttributesInternal (obj, pseudoAttrs);
205                         foreach (object attr in res)
206                                 if (attributeType.Equals (attr.GetType ()))
207                                         return true;
208
209                         ICustomAttributeProvider btype;
210                         if (inherit && ((btype = GetBase (obj)) != null))
211                                 return IsDefined (btype, attributeType, inherit);
212
213                         return false;
214                 }
215
216                 // Handles Type, MonoProperty and MonoMethod.
217                 // The runtime has also cases for MonoEvent, MonoField, Assembly and ParameterInfo,
218                 // but for those we return null here.
219                 static ICustomAttributeProvider GetBase (ICustomAttributeProvider obj)
220                 {
221                         if (obj == null)
222                                 return null;
223
224                         if (obj is Type)
225                                 return ((Type) obj).BaseType;
226
227                         MethodInfo method = null;
228                         if (obj is MonoProperty)
229                         {
230                                 MonoProperty prop = (MonoProperty) obj;
231                                 method = prop.GetGetMethod (true);
232                                 if (method == null)
233                                         method = prop.GetSetMethod (true);
234                         }
235                         else if (obj is MonoMethod)
236                         {
237                                 method = (MethodInfo) obj;
238                         }
239
240                         /**
241                          * ParameterInfo -> null
242                          * Assembly -> null
243                          * MonoEvent -> null
244                          * MonoField -> null
245                          */
246                         if (method == null || !method.IsVirtual)
247                                 return null;
248
249                         MethodInfo baseMethod = method.GetBaseDefinition ();
250                         if (baseMethod == method)
251                                 return null;
252
253                         return baseMethod;
254                 }
255
256                 private static AttributeUsageAttribute RetrieveAttributeUsage (Type attributeType)
257                 {
258                         AttributeUsageAttribute usageAttribute = null;
259                         object[] attribs = GetCustomAttributes (attributeType,
260                                 MonoCustomAttrs.AttributeUsageType, false);
261                         if (attribs.Length == 0)
262                         {
263                                 // if no AttributeUsage was defined on the attribute level, then
264                                 // try to retrieve if from its base type
265                                 if (attributeType.BaseType != null)
266                                 {
267                                         usageAttribute = RetrieveAttributeUsage (attributeType.BaseType);
268
269                                 }
270                                 if (usageAttribute != null)
271                                 {
272                                         // return AttributeUsage of base class
273                                         return usageAttribute;
274
275                                 }
276                                 // return default AttributeUsageAttribute if no AttributeUsage 
277                                 // was defined on attribute, or its base class
278                                 return DefaultAttributeUsage;
279                         }
280                         // check if more than one AttributeUsageAttribute has been specified 
281                         // on the type
282                         // NOTE: compilers should prevent this, but that doesn't prevent
283                         // anyone from using IL ofcourse
284                         if (attribs.Length > 1)
285                         {
286                                 throw new FormatException ("Duplicate AttributeUsageAttribute cannot be specified on an attribute type.");
287                         }
288
289                         return ((AttributeUsageAttribute) attribs[0]);
290                 }
291
292                 private static readonly Type AttributeUsageType = typeof(AttributeUsageAttribute);
293                 private static readonly AttributeUsageAttribute DefaultAttributeUsage =
294                         new AttributeUsageAttribute (AttributeTargets.All);
295
296                 private class AttributeInfo
297                 {
298                         private AttributeUsageAttribute _usage;
299                         private int _inheritanceLevel;
300
301                         public AttributeInfo (AttributeUsageAttribute usage, int inheritanceLevel)
302                         {
303                                 _usage = usage;
304                                 _inheritanceLevel = inheritanceLevel;
305                         }
306
307                         public AttributeUsageAttribute Usage
308                         {
309                                 get
310                                 {
311                                         return _usage;
312                                 }
313                         }
314
315                         public int InheritanceLevel
316                         {
317                                 get
318                                 {
319                                         return _inheritanceLevel;
320                                 }
321                         }
322                 }
323         }
324 }
325