2 // MonoCustomAttrs.cs: Hooks into the runtime to get custom attributes for reflection handles
5 // Paolo Molaro (lupus@ximian.com)
6 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 // Marek Safar (marek.safar@gmail.com)
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)
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:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
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.
34 using System.Reflection;
35 using System.Collections;
36 using System.Runtime.CompilerServices;
38 using System.Reflection.Emit;
41 using System.Collections.Generic;
45 static class MonoCustomAttrs
47 static Assembly corlib;
49 /* Treat as user types all corlib types extending System.Type that are not MonoType and TypeBuilder */
50 static bool IsUserCattrProvider (object obj)
52 Type type = obj as Type;
54 if ((type is MonoType) || (type is TypeBuilder))
62 corlib = typeof (int).Assembly;
63 return obj.GetType ().Assembly != corlib;
66 [MethodImplAttribute (MethodImplOptions.InternalCall)]
67 internal static extern object[] GetCustomAttributesInternal (ICustomAttributeProvider obj, Type attributeType, bool pseudoAttrs);
69 internal static object[] GetPseudoCustomAttributes (ICustomAttributeProvider obj, Type attributeType) {
70 object[] pseudoAttrs = null;
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 ();
80 pseudoAttrs = ((Type)obj).GetPseudoCustomAttributes ();
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)
88 return new object [] { pseudoAttrs [i] };
89 return EmptyArray<object>.Value;
95 internal static object[] GetCustomAttributesBase (ICustomAttributeProvider obj, Type attributeType, bool inheritedOnly)
98 if (IsUserCattrProvider (obj))
99 attrs = obj.GetCustomAttributes (attributeType, true);
101 attrs = GetCustomAttributesInternal (obj, attributeType, false);
104 // All pseudo custom attributes are Inherited = false hence we can avoid
105 // building attributes array which would be discarded by inherited checks
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);
120 internal static Attribute GetCustomAttribute (ICustomAttributeProvider obj,
124 object[] res = GetCustomAttributes (obj, attributeType, inherit);
129 else if (res.Length > 1)
131 string msg = "'{0}' has more than one attribute of type '{1}";
132 msg = String.Format (msg, obj, attributeType);
133 throw new AmbiguousMatchException (msg);
136 return (Attribute) res[0];
139 internal static object[] GetCustomAttributes (ICustomAttributeProvider obj, Type attributeType, bool inherit)
142 throw new ArgumentNullException ("obj");
143 if (attributeType == null)
144 throw new ArgumentNullException ("attributeType");
146 if (attributeType == typeof (MonoCustomAttrs))
147 attributeType = null;
150 object[] res = GetCustomAttributesBase (obj, attributeType, false);
152 if (!inherit && res.Length == 1)
155 throw new CustomAttributeFormatException ("Invalid custom attribute format");
157 if (attributeType != null)
159 if (attributeType.IsAssignableFrom (res[0].GetType ()))
161 r = (object[]) Array.CreateInstance (attributeType, 1);
166 r = (object[]) Array.CreateInstance (attributeType, 0);
171 r = (object[]) Array.CreateInstance (res[0].GetType (), 1);
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)
181 AttributeUsageAttribute usageAttribute = RetrieveAttributeUsage (
183 if (!usageAttribute.Inherited)
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;
194 int inheritanceLevel = 0;
198 foreach (object attr in res)
200 AttributeUsageAttribute usage;
202 throw new CustomAttributeFormatException ("Invalid custom attribute format");
204 Type attrType = attr.GetType ();
205 if (attributeType != null)
207 if (!attributeType.IsAssignableFrom (attrType))
213 AttributeInfo firstAttribute;
214 if (attributeInfos.TryGetValue (attrType, out firstAttribute))
216 usage = firstAttribute.Usage;
220 usage = RetrieveAttributeUsage (attrType);
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
226 // - multiple attributes of the type are allowed
228 // - this is the first attribute we've discovered
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))))
239 if (firstAttribute == null)
241 attributeInfos.Add (attrType, new AttributeInfo (usage, inheritanceLevel));
245 if ((btype = GetBase (btype)) != null)
248 res = GetCustomAttributesBase (btype, attributeType, true);
250 } while (inherit && btype != null);
252 object[] array = null;
253 if (attributeType == null || attributeType.IsValueType)
255 array = (object[]) Array.CreateInstance (typeof(Attribute), a.Count);
259 array = Array.CreateInstance (attributeType, a.Count) as object[];
262 // copy attributes to array
268 internal static object[] GetCustomAttributes (ICustomAttributeProvider obj, bool inherit)
271 throw new ArgumentNullException ("obj");
274 return (object[]) GetCustomAttributesBase (obj, null, false).Clone ();
276 return GetCustomAttributes (obj, typeof (MonoCustomAttrs), inherit);
279 [MethodImplAttribute (MethodImplOptions.InternalCall)]
280 static extern CustomAttributeData [] GetCustomAttributesDataInternal (ICustomAttributeProvider obj);
282 internal static IList<CustomAttributeData> GetCustomAttributesData (ICustomAttributeProvider obj)
285 throw new ArgumentNullException ("obj");
287 CustomAttributeData [] attrs = GetCustomAttributesDataInternal (obj);
288 return Array.AsReadOnly<CustomAttributeData> (attrs);
291 internal static bool IsDefined (ICustomAttributeProvider obj, Type attributeType, bool inherit)
293 if (attributeType == null)
294 throw new ArgumentNullException ("attributeType");
296 AttributeUsageAttribute usage = null;
298 if (IsUserCattrProvider (obj))
299 return obj.IsDefined (attributeType, inherit);
301 if (IsDefinedInternal (obj, attributeType))
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 ()))
315 usage = RetrieveAttributeUsage (attributeType);
316 if (!usage.Inherited)
321 } while (obj != null);
326 [MethodImplAttribute (MethodImplOptions.InternalCall)]
327 internal static extern bool IsDefinedInternal (ICustomAttributeProvider obj, Type AttributeType);
329 static PropertyInfo GetBasePropertyDefinition (PropertyInfo property)
331 MethodInfo method = property.GetGetMethod (true);
332 if (method == null || !method.IsVirtual)
333 method = property.GetSetMethod (true);
334 if (method == null || !method.IsVirtual)
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,
347 return baseMethod.DeclaringType.GetProperty (property.Name, property.PropertyType);
354 static EventInfo GetBaseEventDefinition (EventInfo evt)
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)
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;
369 return baseMethod.DeclaringType.GetEvent (evt.Name, flags);
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)
383 return ((Type) obj).BaseType;
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;
394 * ParameterInfo -> null
399 if (method == null || !method.IsVirtual)
402 MethodInfo baseMethod = method.GetBaseMethod ();
403 if (baseMethod == method)
409 private static AttributeUsageAttribute RetrieveAttributeUsage (Type attributeType)
411 if (attributeType == typeof (AttributeUsageAttribute))
412 /* Avoid endless recursion */
413 return new AttributeUsageAttribute (AttributeTargets.Class);
415 AttributeUsageAttribute usageAttribute = null;
416 object[] attribs = GetCustomAttributes (attributeType, typeof(AttributeUsageAttribute), false);
417 if (attribs.Length == 0)
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)
423 usageAttribute = RetrieveAttributeUsage (attributeType.BaseType);
426 if (usageAttribute != null)
428 // return AttributeUsage of base class
429 return usageAttribute;
432 // return default AttributeUsageAttribute if no AttributeUsage
433 // was defined on attribute, or its base class
434 return DefaultAttributeUsage;
436 // check if more than one AttributeUsageAttribute has been specified
438 // NOTE: compilers should prevent this, but that doesn't prevent
439 // anyone from using IL ofcourse
440 if (attribs.Length > 1)
442 throw new FormatException ("Duplicate AttributeUsageAttribute cannot be specified on an attribute type.");
445 return ((AttributeUsageAttribute) attribs[0]);
448 private static readonly AttributeUsageAttribute DefaultAttributeUsage =
449 new AttributeUsageAttribute (AttributeTargets.All);
451 private class AttributeInfo
453 private AttributeUsageAttribute _usage;
454 private int _inheritanceLevel;
456 public AttributeInfo (AttributeUsageAttribute usage, int inheritanceLevel)
459 _inheritanceLevel = inheritanceLevel;
462 public AttributeUsageAttribute Usage
470 public int InheritanceLevel
474 return _inheritanceLevel;