Replace some hashtables with dictionaries
[mono.git] / mcs / class / corlib / System / MonoCustomAttrs.cs
1 //
2 // MonoCustomAttrs.cs: 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 //      Marek Safar (marek.safar@gmail.com)
8 //
9 // (c) 2002,2003 Ximian, Inc. (http://www.ximian.com)
10 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
11 // Copyright (C) 2013 Xamarin, Inc (http://www.xamarin.com)
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System;
34 using System.Reflection;
35 using System.Collections;
36 using System.Runtime.CompilerServices;
37 #if !FULL_AOT_RUNTIME
38 using System.Reflection.Emit;
39 #endif
40
41 using System.Collections.Generic;
42
43 namespace System
44 {
45         static class MonoCustomAttrs
46         {
47                 static Assembly corlib;
48
49                 /* Treat as user types all corlib types extending System.Type that are not MonoType and TypeBuilder */
50                 static bool IsUserCattrProvider (object obj)
51                 {
52                         Type type = obj as Type;
53 #if !FULL_AOT_RUNTIME
54                         if ((type is MonoType) || (type is TypeBuilder))
55 #else
56                         if (type is MonoType)
57 #endif
58                                 return false;
59                         if ((obj is Type))
60                                 return true;
61                         if (corlib == null)
62                                  corlib = typeof (int).Assembly;
63                         return obj.GetType ().Assembly != corlib;
64                 }
65         
66                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
67                 internal static extern object[] GetCustomAttributesInternal (ICustomAttributeProvider obj, Type attributeType, bool pseudoAttrs);
68
69                 internal static object[] GetPseudoCustomAttributes (ICustomAttributeProvider obj, Type attributeType) {
70                         object[] pseudoAttrs = null;
71
72                         /* FIXME: Add other types */
73                         if (obj is MonoMethod)
74                                 pseudoAttrs = ((MonoMethod)obj).GetPseudoCustomAttributes ();
75                         else if (obj is FieldInfo)
76                                 pseudoAttrs = ((FieldInfo)obj).GetPseudoCustomAttributes ();
77                         else if (obj is ParameterInfo)
78                                 pseudoAttrs = ((ParameterInfo)obj).GetPseudoCustomAttributes ();
79                         else if (obj is Type)
80                                 pseudoAttrs = ((Type)obj).GetPseudoCustomAttributes ();
81
82                         if ((attributeType != null) && (pseudoAttrs != null)) {
83                                 for (int i = 0; i < pseudoAttrs.Length; ++i)
84                                         if (attributeType.IsAssignableFrom (pseudoAttrs [i].GetType ()))
85                                                 if (pseudoAttrs.Length == 1)
86                                                         return pseudoAttrs;
87                                                 else
88                                                         return new object [] { pseudoAttrs [i] };
89                                 return EmptyArray<object>.Value;
90                         }
91
92                         return pseudoAttrs;
93                 }
94
95                 internal static object[] GetCustomAttributesBase (ICustomAttributeProvider obj, Type attributeType, bool inheritedOnly)
96                 {
97                         object[] attrs;
98                         if (IsUserCattrProvider (obj))
99                                 attrs = obj.GetCustomAttributes (attributeType, true);
100                         else
101                                 attrs = GetCustomAttributesInternal (obj, attributeType, false);
102
103                         //
104                         // All pseudo custom attributes are Inherited = false hence we can avoid
105                         // building attributes array which would be discarded by inherited checks
106                         //
107                         if (!inheritedOnly) {
108                                 object[] pseudoAttrs = GetPseudoCustomAttributes (obj, attributeType);
109                                 if (pseudoAttrs != null) {
110                                         object[] res = new object [attrs.Length + pseudoAttrs.Length];
111                                         System.Array.Copy (attrs, res, attrs.Length);
112                                         System.Array.Copy (pseudoAttrs, 0, res, attrs.Length, pseudoAttrs.Length);
113                                         return res;
114                                 }
115                         }
116
117                         return attrs;
118                 }
119
120                 internal static Attribute GetCustomAttribute (ICustomAttributeProvider obj,
121                                                                 Type attributeType,
122                                                                 bool inherit)
123                 {
124                         object[] res = GetCustomAttributes (obj, attributeType, inherit);
125                         if (res.Length == 0)
126                         {
127                                 return null;
128                         }
129                         else if (res.Length > 1)
130                         {
131                                 string msg = "'{0}' has more than one attribute of type '{1}";
132                                 msg = String.Format (msg, obj, attributeType);
133                                 throw new AmbiguousMatchException (msg);
134                         }
135
136                         return (Attribute) res[0];
137                 }
138
139                 internal static object[] GetCustomAttributes (ICustomAttributeProvider obj, Type attributeType, bool inherit)
140                 {
141                         if (obj == null)
142                                 throw new ArgumentNullException ("obj");
143                         if (attributeType == null)
144                                 throw new ArgumentNullException ("attributeType");      
145
146                         if (attributeType == typeof (MonoCustomAttrs))
147                                 attributeType = null;
148                         
149                         object[] r;
150                         object[] res = GetCustomAttributesBase (obj, attributeType, false);
151                         // shortcut
152                         if (!inherit && res.Length == 1)
153                         {
154                                 if (res [0] == null)
155                                         throw new CustomAttributeFormatException ("Invalid custom attribute format");
156
157                                 if (attributeType != null)
158                                 {
159                                         if (attributeType.IsAssignableFrom (res[0].GetType ()))
160                                         {
161                                                 r = (object[]) Array.CreateInstance (attributeType, 1);
162                                                 r[0] = res[0];
163                                         }
164                                         else
165                                         {
166                                                 r = (object[]) Array.CreateInstance (attributeType, 0);
167                                         }
168                                 }
169                                 else
170                                 {
171                                         r = (object[]) Array.CreateInstance (res[0].GetType (), 1);
172                                         r[0] = res[0];
173                                 }
174                                 return r;
175                         }
176
177                         // if AttributeType is sealed, and Inherited is set to false, then 
178                         // there's no use in scanning base types 
179                         if ((attributeType != null && attributeType.IsSealed) && inherit)
180                         {
181                                 AttributeUsageAttribute usageAttribute = RetrieveAttributeUsage (
182                                         attributeType);
183                                 if (!usageAttribute.Inherited)
184                                 {
185                                         inherit = false;
186                                 }
187                         }
188
189                         var initialSize = Math.Max (res.Length, 16);
190                         var attributeInfos = new Dictionary<Type, AttributeInfo> (initialSize);
191                         var a = new List<object> (initialSize);
192                         ICustomAttributeProvider btype = obj;
193
194                         int inheritanceLevel = 0;
195
196                         do
197                         {
198                                 foreach (object attr in res)
199                                 {
200                                         AttributeUsageAttribute usage;
201                                         if (attr == null)
202                                                 throw new CustomAttributeFormatException ("Invalid custom attribute format");
203
204                                         Type attrType = attr.GetType ();
205                                         if (attributeType != null)
206                                         {
207                                                 if (!attributeType.IsAssignableFrom (attrType))
208                                                 {
209                                                         continue;
210                                                 }
211                                         }
212
213                                         AttributeInfo firstAttribute;
214                                         if (attributeInfos.TryGetValue (attrType, out firstAttribute))
215                                         {
216                                                 usage = firstAttribute.Usage;
217                                         }
218                                         else
219                                         {
220                                                 usage = RetrieveAttributeUsage (attrType);
221                                         }
222
223                                         // only add attribute to the list of attributes if 
224                                         // - we are on the first inheritance level, or the attribute can be inherited anyway
225                                         // and (
226                                         // - multiple attributes of the type are allowed
227                                         // or (
228                                         // - this is the first attribute we've discovered
229                                         // or
230                                         // - the attribute is on same inheritance level than the first 
231                                         //   attribute that was discovered for this attribute type ))
232                                         if ((inheritanceLevel == 0 || usage.Inherited) && (usage.AllowMultiple || 
233                                                 (firstAttribute == null || (firstAttribute != null 
234                                                         && firstAttribute.InheritanceLevel == inheritanceLevel))))
235                                         {
236                                                 a.Add (attr);
237                                         }
238
239                                         if (firstAttribute == null)
240                                         {
241                                                 attributeInfos.Add (attrType, new AttributeInfo (usage, inheritanceLevel));
242                                         }
243                                 }
244
245                                 if ((btype = GetBase (btype)) != null)
246                                 {
247                                         inheritanceLevel++;
248                                         res = GetCustomAttributesBase (btype, attributeType, true);
249                                 }
250                         } while (inherit && btype != null);
251
252                         object[] array = null;
253                         if (attributeType == null || attributeType.IsValueType)
254                         {
255                                 array = (object[]) Array.CreateInstance (typeof(Attribute), a.Count);
256                         }
257                         else
258                         {
259                                 array = Array.CreateInstance (attributeType, a.Count) as object[];
260                         }
261
262                         // copy attributes to array
263                         a.CopyTo (array, 0);
264
265                         return array;
266                 }
267
268                 internal static object[] GetCustomAttributes (ICustomAttributeProvider obj, bool inherit)
269                 {
270                         if (obj == null)
271                                 throw new ArgumentNullException ("obj");
272
273                         if (!inherit)
274                                 return (object[]) GetCustomAttributesBase (obj, null, false).Clone ();
275
276                         return GetCustomAttributes (obj, typeof (MonoCustomAttrs), inherit);
277                 }
278
279                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
280                 static extern CustomAttributeData [] GetCustomAttributesDataInternal (ICustomAttributeProvider obj);
281
282                 internal static IList<CustomAttributeData> GetCustomAttributesData (ICustomAttributeProvider obj)
283                 {
284                         if (obj == null)
285                                 throw new ArgumentNullException ("obj");
286
287                         CustomAttributeData [] attrs = GetCustomAttributesDataInternal (obj);
288                         return Array.AsReadOnly<CustomAttributeData> (attrs);
289                 }
290
291                 internal static bool IsDefined (ICustomAttributeProvider obj, Type attributeType, bool inherit)
292                 {
293                         if (attributeType == null)
294                                 throw new ArgumentNullException ("attributeType");
295
296                         AttributeUsageAttribute usage = null;
297                         do {
298                                 if (IsUserCattrProvider (obj))
299                                         return obj.IsDefined (attributeType, inherit);
300
301                                 if (IsDefinedInternal (obj, attributeType))
302                                         return true;
303
304                                 object[] pseudoAttrs = GetPseudoCustomAttributes (obj, attributeType);
305                                 if (pseudoAttrs != null) {
306                                         for (int i = 0; i < pseudoAttrs.Length; ++i)
307                                                 if (attributeType.IsAssignableFrom (pseudoAttrs[i].GetType ()))
308                                                         return true;
309                                 }
310
311                                 if (usage == null) {
312                                         if (!inherit)
313                                                 return false;
314
315                                         usage = RetrieveAttributeUsage (attributeType);
316                                         if (!usage.Inherited)
317                                                 return false;
318                                 }
319
320                                 obj = GetBase (obj);
321                         } while (obj != null);
322
323                         return false;
324                 }
325
326                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
327                 internal static extern bool IsDefinedInternal (ICustomAttributeProvider obj, Type AttributeType);
328
329                 static PropertyInfo GetBasePropertyDefinition (PropertyInfo property)
330                 {
331                         MethodInfo method = property.GetGetMethod (true);
332                         if (method == null || !method.IsVirtual)
333                                 method = property.GetSetMethod (true);
334                         if (method == null || !method.IsVirtual)
335                                 return null;
336
337                         MethodInfo baseMethod = method.GetBaseMethod ();
338                         if (baseMethod != null && baseMethod != method) {
339                                 ParameterInfo[] parameters = property.GetIndexParameters ();
340                                 if (parameters != null && parameters.Length > 0) {
341                                         Type[] paramTypes = new Type[parameters.Length];
342                                         for (int i=0; i < paramTypes.Length; i++)
343                                                 paramTypes[i] = parameters[i].ParameterType;
344                                         return baseMethod.DeclaringType.GetProperty (property.Name, property.PropertyType, 
345                                                                                      paramTypes);
346                                 } else {
347                                         return baseMethod.DeclaringType.GetProperty (property.Name, property.PropertyType);
348                                 }
349                         }
350                         return null;
351
352                 }
353
354                 static EventInfo GetBaseEventDefinition (EventInfo evt)
355                 {
356                         MethodInfo method = evt.GetAddMethod (true);
357                         if (method == null || !method.IsVirtual)
358                                 method = evt.GetRaiseMethod (true);
359                         if (method == null || !method.IsVirtual)
360                                 method = evt.GetRemoveMethod (true);
361                         if (method == null || !method.IsVirtual)
362                                 return null;
363
364                         MethodInfo baseMethod = method.GetBaseMethod ();
365                         if (baseMethod != null && baseMethod != method) {
366                                 BindingFlags flags = method.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic;
367                                 flags |= method.IsStatic ? BindingFlags.Static : BindingFlags.Instance;
368
369                                 return baseMethod.DeclaringType.GetEvent (evt.Name, flags);
370                         }
371                         return null;
372                 }
373
374                 // Handles Type, MonoProperty and MonoMethod.
375                 // The runtime has also cases for MonoEvent, MonoField, Assembly and ParameterInfo,
376                 // but for those we return null here.
377                 static ICustomAttributeProvider GetBase (ICustomAttributeProvider obj)
378                 {
379                         if (obj == null)
380                                 return null;
381
382                         if (obj is Type)
383                                 return ((Type) obj).BaseType;
384
385                         MethodInfo method = null;
386                         if (obj is MonoProperty)
387                                 return GetBasePropertyDefinition ((MonoProperty) obj);
388                         else if (obj is MonoEvent)
389                                 return GetBaseEventDefinition ((MonoEvent)obj);
390                         else if (obj is MonoMethod)
391                                 method = (MethodInfo) obj;
392
393                         /**
394                          * ParameterInfo -> null
395                          * Assembly -> null
396                          * MonoEvent -> null
397                          * MonoField -> null
398                          */
399                         if (method == null || !method.IsVirtual)
400                                 return null;
401
402                         MethodInfo baseMethod = method.GetBaseMethod ();
403                         if (baseMethod == method)
404                                 return null;
405
406                         return baseMethod;
407                 }
408
409                 private static AttributeUsageAttribute RetrieveAttributeUsage (Type attributeType)
410                 {
411                         if (attributeType == typeof (AttributeUsageAttribute))
412                                 /* Avoid endless recursion */
413                                 return new AttributeUsageAttribute (AttributeTargets.Class);
414
415                         AttributeUsageAttribute usageAttribute = null;
416                         object[] attribs = GetCustomAttributes (attributeType, typeof(AttributeUsageAttribute), false);
417                         if (attribs.Length == 0)
418                         {
419                                 // if no AttributeUsage was defined on the attribute level, then
420                                 // try to retrieve if from its base type
421                                 if (attributeType.BaseType != null)
422                                 {
423                                         usageAttribute = RetrieveAttributeUsage (attributeType.BaseType);
424
425                                 }
426                                 if (usageAttribute != null)
427                                 {
428                                         // return AttributeUsage of base class
429                                         return usageAttribute;
430
431                                 }
432                                 // return default AttributeUsageAttribute if no AttributeUsage 
433                                 // was defined on attribute, or its base class
434                                 return DefaultAttributeUsage;
435                         }
436                         // check if more than one AttributeUsageAttribute has been specified 
437                         // on the type
438                         // NOTE: compilers should prevent this, but that doesn't prevent
439                         // anyone from using IL ofcourse
440                         if (attribs.Length > 1)
441                         {
442                                 throw new FormatException ("Duplicate AttributeUsageAttribute cannot be specified on an attribute type.");
443                         }
444
445                         return ((AttributeUsageAttribute) attribs[0]);
446                 }
447
448                 private static readonly AttributeUsageAttribute DefaultAttributeUsage =
449                         new AttributeUsageAttribute (AttributeTargets.All);
450
451                 private class AttributeInfo
452                 {
453                         private AttributeUsageAttribute _usage;
454                         private int _inheritanceLevel;
455
456                         public AttributeInfo (AttributeUsageAttribute usage, int inheritanceLevel)
457                         {
458                                 _usage = usage;
459                                 _inheritanceLevel = inheritanceLevel;
460                         }
461
462                         public AttributeUsageAttribute Usage
463                         {
464                                 get
465                                 {
466                                         return _usage;
467                                 }
468                         }
469
470                         public int InheritanceLevel
471                         {
472                                 get
473                                 {
474                                         return _inheritanceLevel;
475                                 }
476                         }
477                 }
478         }
479 }
480