2 // membercache.cs: A container for all member lookups
4 // Author: Miguel de Icaza (miguel@gnu.org)
5 // Marek Safar (marek.safar@gmail.com)
7 // Dual licensed under the terms of the MIT X11 or GNU GPL
9 // Copyright 2001 Ximian, Inc (http://www.ximian.com)
10 // Copyright 2004-2010 Novell, Inc
16 using System
.Collections
.Generic
;
17 using System
.Globalization
;
18 using System
.Reflection
.Emit
;
19 using System
.Reflection
;
21 namespace Mono
.CSharp
{
24 public enum MemberKind
44 MaskType
= Constructor
| Event
| Field
| Method
| Property
| NestedType
| Indexer
| Operator
| Destructor
,
49 public enum BindingRestriction
53 // Member has to be accessible
56 // Inspect only queried type members
57 DeclaredOnly
= 1 << 1,
60 InstanceOnly
= 1 << 2,
63 NoOverloadableOverrides
= 1 << 3
66 public struct MemberFilter : IEquatable<MemberCore>
68 public readonly string Name;
69 public readonly MemberKind Kind;
70 public readonly TypeSpec[] Parameters;
71 public readonly TypeSpec MemberType;
73 public MemberFilter (IMethod m)
75 Name = m.MethodBuilder.Name;
76 Kind = MemberKind.Method;
77 Parameters = m.Parameters.Types;
78 MemberType = m.ReturnType;
81 public MemberFilter (string name, MemberKind kind)
89 public MemberFilter (string name, MemberKind kind, TypeSpec[] param, TypeSpec type)
98 public static MemberFilter Constuctor (TypeSpec[] param)
100 return new MemberFilter (System.Reflection.ConstructorInfo.ConstructorName, MemberKind.Constructor, param, null);
103 public static MemberFilter Property (string name, TypeSpec type)
105 return new MemberFilter (name, MemberKind.Property, null, type);
108 public static MemberFilter Field (string name, TypeSpec type)
110 return new MemberFilter (name, MemberKind.Field, null, type);
113 public static MemberFilter Method (string name, TypeSpec[] param, TypeSpec type)
115 return new MemberFilter (name, MemberKind.Method, param, type);
118 #region IEquatable<MemberCore> Members
120 public bool Equals (MemberCore other)
122 // Is the member of the correct type ?
123 if ((other.MemberKind & Kind & MemberKind.MaskType) == 0)
126 if (Parameters != null) {
127 if (other is IParametersMember) {
128 AParametersCollection other_param = ((IParametersMember) other).Parameters;
129 if (TypeSpecArrayComparer.Default.Equals (Parameters, other_param.Types))
136 if (MemberType != null) {
137 //throw new NotImplementedException ();
147 /// This is a readonly list of MemberInfo's.
149 public class MemberList
: IList
<MemberInfo
> {
150 public readonly IList
<MemberInfo
> List
;
154 /// Create a new MemberList from the given IList.
156 public MemberList (IList
<MemberInfo
> list
)
161 this.List
= new List
<MemberInfo
> ();
166 /// Concatenate the ILists `first' and `second' to a new MemberList.
168 public MemberList (IList
<MemberInfo
> first
, IList
<MemberInfo
> second
)
170 var list
= new List
<MemberInfo
> ();
171 list
.AddRange (first
);
172 list
.AddRange (second
);
177 public static readonly MemberList Empty
= new MemberList (Array
.AsReadOnly (new MemberInfo
[0]));
180 /// Cast the MemberList into a MemberInfo[] array.
183 /// This is an expensive operation, only use it if it's really necessary.
185 public static explicit operator MemberInfo
[] (MemberList list
)
187 Timer
.StartTimer (TimerType
.MiscTimer
);
188 MemberInfo
[] result
= new MemberInfo
[list
.Count
];
189 list
.CopyTo (result
, 0);
190 Timer
.StopTimer (TimerType
.MiscTimer
);
202 public void CopyTo (MemberInfo
[] array
, int index
)
204 List
.CopyTo (array
, index
);
209 public IEnumerator
<MemberInfo
> GetEnumerator ()
211 return List
.GetEnumerator ();
214 System
.Collections
.IEnumerator System
.Collections
.IEnumerable
.GetEnumerator ()
216 return List
.GetEnumerator ();
221 public bool IsFixedSize
{
227 public bool IsReadOnly
{
233 MemberInfo IList
<MemberInfo
>.this [int index
] {
239 throw new NotSupportedException ();
243 // FIXME: try to find out whether we can avoid the cast in this indexer.
244 public MemberInfo
this [int index
] {
246 return (MemberInfo
) List
[index
];
250 public void Add (MemberInfo
value)
252 throw new NotSupportedException ();
257 throw new NotSupportedException ();
260 public bool Contains (MemberInfo
value)
262 return List
.Contains (value);
265 public int IndexOf (MemberInfo
value)
267 return List
.IndexOf (value);
270 public void Insert (int index
, MemberInfo
value)
272 throw new NotSupportedException ();
275 public bool Remove (MemberInfo
value)
277 throw new NotSupportedException ();
280 public void RemoveAt (int index
)
282 throw new NotSupportedException ();
287 /// This interface is used to get all members of a class when creating the
288 /// member cache. It must be implemented by all DeclSpace derivatives which
289 /// want to support the member cache and by TypeHandle to get caching of
290 /// non-dynamic types.
292 public interface IMemberContainer
{
294 /// The name of the IMemberContainer. This is only used for
295 /// debugging purposes.
302 /// The type of this IMemberContainer.
309 /// Returns the IMemberContainer of the base class or null if this
310 /// is an interface or TypeManger.object_type.
311 /// This is used when creating the member cache for a class to get all
312 /// members from the base class.
314 MemberCache BaseCache
{
319 /// Whether this is an interface.
326 /// Returns all members of this class with the corresponding MemberTypes
327 /// and BindingFlags.
330 /// When implementing this method, make sure not to return any inherited
331 /// members and check the MemberTypes and BindingFlags properly.
332 /// Unfortunately, System.Reflection is lame and doesn't provide a way to
333 /// get the BindingFlags (static/non-static,public/non-public) in the
334 /// MemberInfo class, but the cache needs this information. That's why
335 /// this method is called multiple times with different BindingFlags.
337 MemberList
GetMembers (MemberTypes mt
, BindingFlags bf
);
341 /// The MemberCache is used by dynamic and non-dynamic types to speed up
342 /// member lookups. It has a member name based hash table; it maps each member
343 /// name to a list of CacheEntry objects. Each CacheEntry contains a MemberInfo
344 /// and the BindingFlags that were initially used to get it. The cache contains
345 /// all members of the current class and all inherited members. If this cache is
346 /// for an interface types, it also contains all inherited members.
348 /// There are two ways to get a MemberCache:
349 /// * if this is a dynamic type, lookup the corresponding DeclSpace and then
350 /// use the DeclSpace.MemberCache property.
351 /// * if this not a dynamic type, call TypeHandle.GetTypeHandle() to get a
352 /// TypeHandle instance for the type and then use TypeHandle.MemberCache.
354 public class MemberCache
{
355 public readonly IMemberContainer Container
;
356 protected Dictionary
<string, List
<CacheEntry
>> member_hash
;
357 protected Dictionary
<string, List
<CacheEntry
>> method_hash
;
359 Dictionary
<string, object> locase_table
;
361 static List
<MethodInfo
> overrides
= new List
<MethodInfo
> ();
364 /// Create a new MemberCache for the given IMemberContainer `container'.
366 public MemberCache (IMemberContainer container
)
368 this.Container
= container
;
370 Timer
.IncrementCounter (CounterType
.MemberCache
);
371 Timer
.StartTimer (TimerType
.CacheInit
);
373 // If we have a base class (we have a base class unless we're
374 // TypeManager.object_type), we deep-copy its MemberCache here.
375 if (Container
.BaseCache
!= null)
376 member_hash
= SetupCache (Container
.BaseCache
);
378 member_hash
= new Dictionary
<string, List
<CacheEntry
>> ();
380 // If this is neither a dynamic type nor an interface, create a special
381 // method cache with all declared and inherited methods.
382 Type type
= container
.Type
;
383 if (!(type
is TypeBuilder
) && !type
.IsInterface
&&
384 // !(type.IsGenericType && (type.GetGenericTypeDefinition () is TypeBuilder)) &&
385 !TypeManager
.IsGenericType (type
) && !TypeManager
.IsGenericParameter (type
) &&
386 (Container
.BaseCache
== null || Container
.BaseCache
.method_hash
!= null)) {
387 method_hash
= new Dictionary
<string, List
<CacheEntry
>> ();
391 // Add all members from the current class.
392 AddMembers (Container
);
394 Timer
.StopTimer (TimerType
.CacheInit
);
397 public MemberCache (Type baseType
, IMemberContainer container
)
399 this.Container
= container
;
400 if (baseType
== null)
401 this.member_hash
= new Dictionary
<string, List
<CacheEntry
>> ();
403 this.member_hash
= SetupCache (TypeManager
.LookupMemberCache (baseType
));
406 public MemberCache (Type
[] ifaces
)
409 // The members of this cache all belong to other caches.
410 // So, 'Container' will not be used.
412 this.Container
= null;
414 member_hash
= new Dictionary
<string, List
<CacheEntry
>> ();
418 foreach (Type itype
in ifaces
)
419 AddCacheContents (TypeManager
.LookupMemberCache (itype
));
422 public MemberCache (IMemberContainer container
, Type base_class
, Type
[] ifaces
)
424 this.Container
= container
;
426 // If we have a base class (we have a base class unless we're
427 // TypeManager.object_type), we deep-copy its MemberCache here.
428 if (Container
.BaseCache
!= null)
429 member_hash
= SetupCache (Container
.BaseCache
);
431 member_hash
= new Dictionary
<string, List
<CacheEntry
>> ();
433 if (base_class
!= null)
434 AddCacheContents (TypeManager
.LookupMemberCache (base_class
));
435 if (ifaces
!= null) {
436 foreach (Type itype
in ifaces
) {
437 MemberCache cache
= TypeManager
.LookupMemberCache (itype
);
439 AddCacheContents (cache
);
445 /// Bootstrap this member cache by doing a deep-copy of our base.
447 static Dictionary
<string, List
<CacheEntry
>> SetupCache (MemberCache base_class
)
449 if (base_class
== null)
450 return new Dictionary
<string, List
<CacheEntry
>> ();
452 var hash
= new Dictionary
<string, List
<CacheEntry
>> (base_class
.member_hash
.Count
);
453 var it
= base_class
.member_hash
.GetEnumerator ();
454 while (it
.MoveNext ()) {
455 hash
.Add (it
.Current
.Key
, new List
<CacheEntry
> (it
.Current
.Value
));
462 // Converts ModFlags to BindingFlags
464 static BindingFlags
GetBindingFlags (Modifiers modifiers
)
467 if ((modifiers
& Modifiers
.STATIC
) != 0)
468 bf
= BindingFlags
.Static
;
470 bf
= BindingFlags
.Instance
;
472 if ((modifiers
& Modifiers
.PRIVATE
) != 0)
473 bf
|= BindingFlags
.NonPublic
;
475 bf
|= BindingFlags
.Public
;
481 /// Add the contents of `cache' to the member_hash.
483 void AddCacheContents (MemberCache cache
)
485 var it
= cache
.member_hash
.GetEnumerator ();
486 while (it
.MoveNext ()) {
487 List
<CacheEntry
> list
;
488 if (!member_hash
.TryGetValue (it
.Current
.Key
, out list
))
489 member_hash
[it
.Current
.Key
] = list
= new List
<CacheEntry
> ();
491 var entries
= it
.Current
.Value
;
492 for (int i
= entries
.Count
-1; i
>= 0; i
--) {
493 var entry
= entries
[i
];
495 if (entry
.Container
!= cache
.Container
)
503 /// Add all members from class `container' to the cache.
505 void AddMembers (IMemberContainer container
)
507 // We need to call AddMembers() with a single member type at a time
508 // to get the member type part of CacheEntry.EntryType right.
509 if (!container
.IsInterface
) {
510 AddMembers (MemberTypes
.Constructor
, container
);
511 AddMembers (MemberTypes
.Field
, container
);
513 AddMembers (MemberTypes
.Method
, container
);
514 AddMembers (MemberTypes
.Property
, container
);
515 AddMembers (MemberTypes
.Event
, container
);
516 // Nested types are returned by both Static and Instance searches.
517 AddMembers (MemberTypes
.NestedType
,
518 BindingFlags
.Static
| BindingFlags
.Public
, container
);
519 AddMembers (MemberTypes
.NestedType
,
520 BindingFlags
.Static
| BindingFlags
.NonPublic
, container
);
523 void AddMembers (MemberTypes mt
, IMemberContainer container
)
525 AddMembers (mt
, BindingFlags
.Static
| BindingFlags
.Public
, container
);
526 AddMembers (mt
, BindingFlags
.Static
| BindingFlags
.NonPublic
, container
);
527 AddMembers (mt
, BindingFlags
.Instance
| BindingFlags
.Public
, container
);
528 AddMembers (mt
, BindingFlags
.Instance
| BindingFlags
.NonPublic
, container
);
531 public void AddMember (MemberInfo mi
, MemberSpec mc
)
533 AddMember (mi
.MemberType
, GetBindingFlags (mc
.Modifiers
), Container
, mi
.Name
, mi
);
536 public void AddGenericMember (MemberInfo mi
, InterfaceMemberBase mc
)
538 AddMember (mi
.MemberType
, GetBindingFlags (mc
.ModFlags
), Container
,
539 MemberName
.MakeName (mc
.GetFullName (mc
.MemberName
), mc
.MemberName
.TypeArguments
), mi
);
542 public void AddNestedType (DeclSpace type
)
544 AddMember (MemberTypes
.NestedType
, GetBindingFlags (type
.ModFlags
), (IMemberContainer
) type
.Parent
,
545 type
.TypeBuilder
.Name
, type
.TypeBuilder
);
548 public void AddInterface (MemberCache baseCache
)
550 if (baseCache
.member_hash
.Count
> 0)
551 AddCacheContents (baseCache
);
554 void AddMember (MemberTypes mt
, BindingFlags bf
, IMemberContainer container
,
555 string name
, MemberInfo member
)
557 // We use a name-based hash table of ArrayList's.
558 List
<CacheEntry
> list
;
559 if (!member_hash
.TryGetValue (name
, out list
)) {
560 list
= new List
<CacheEntry
> (1);
561 member_hash
.Add (name
, list
);
564 // When this method is called for the current class, the list will
565 // already contain all inherited members from our base classes.
566 // We cannot add new members in front of the list since this'd be an
567 // expensive operation, that's why the list is sorted in reverse order
568 // (ie. members from the current class are coming last).
569 list
.Add (new CacheEntry (container
, member
, mt
, bf
));
573 /// Add all members from class `container' with the requested MemberTypes and
574 /// BindingFlags to the cache. This method is called multiple times with different
575 /// MemberTypes and BindingFlags.
577 void AddMembers (MemberTypes mt
, BindingFlags bf
, IMemberContainer container
)
579 MemberList members
= container
.GetMembers (mt
, bf
);
581 foreach (MemberInfo member
in members
) {
582 string name
= member
.Name
;
584 AddMember (mt
, bf
, container
, name
, member
);
586 if (member
is MethodInfo
) {
587 string gname
= TypeManager
.GetMethodName ((MethodInfo
) member
);
589 AddMember (mt
, bf
, container
, gname
, member
);
595 /// Add all declared and inherited methods from class `type' to the method cache.
597 void AddMethods (Type type
)
599 AddMethods (BindingFlags
.Static
| BindingFlags
.Public
|
600 BindingFlags
.FlattenHierarchy
, type
);
601 AddMethods (BindingFlags
.Static
| BindingFlags
.NonPublic
|
602 BindingFlags
.FlattenHierarchy
, type
);
603 AddMethods (BindingFlags
.Instance
| BindingFlags
.Public
, type
);
604 AddMethods (BindingFlags
.Instance
| BindingFlags
.NonPublic
, type
);
607 void AddMethods (BindingFlags bf
, Type type
)
609 MethodBase
[] members
= type
.GetMethods (bf
);
611 Array
.Reverse (members
);
613 foreach (MethodBase member
in members
) {
614 string name
= member
.Name
;
616 // We use a name-based hash table of ArrayList's.
617 List
<CacheEntry
> list
;
618 if (!method_hash
.TryGetValue (name
, out list
)) {
619 list
= new List
<CacheEntry
> (1);
620 method_hash
.Add (name
, list
);
623 MethodInfo curr
= (MethodInfo
) member
;
624 while (curr
.IsVirtual
&& (curr
.Attributes
& MethodAttributes
.NewSlot
) == 0) {
625 MethodInfo base_method
= curr
.GetBaseDefinition ();
627 if (base_method
== curr
)
628 // Not every virtual function needs to have a NewSlot flag.
631 overrides
.Add (curr
);
632 list
.Add (new CacheEntry (null, base_method
, MemberTypes
.Method
, bf
));
636 if (overrides
.Count
> 0) {
637 for (int i
= 0; i
< overrides
.Count
; ++i
)
638 TypeManager
.RegisterOverride ((MethodBase
) overrides
[i
], curr
);
642 // Unfortunately, the elements returned by Type.GetMethods() aren't
643 // sorted so we need to do this check for every member.
644 BindingFlags new_bf
= bf
;
645 if (member
.DeclaringType
== type
)
646 new_bf
|= BindingFlags
.DeclaredOnly
;
648 list
.Add (new CacheEntry (Container
, member
, MemberTypes
.Method
, new_bf
));
653 /// Compute and return a appropriate `EntryType' magic number for the given
654 /// MemberTypes and BindingFlags.
656 protected static EntryType
GetEntryType (MemberTypes mt
, BindingFlags bf
)
658 EntryType type
= EntryType
.None
;
660 if ((mt
& MemberTypes
.Constructor
) != 0)
661 type
|= EntryType
.Constructor
;
662 if ((mt
& MemberTypes
.Event
) != 0)
663 type
|= EntryType
.Event
;
664 if ((mt
& MemberTypes
.Field
) != 0)
665 type
|= EntryType
.Field
;
666 if ((mt
& MemberTypes
.Method
) != 0)
667 type
|= EntryType
.Method
;
668 if ((mt
& MemberTypes
.Property
) != 0)
669 type
|= EntryType
.Property
;
670 // Nested types are returned by static and instance searches.
671 if ((mt
& MemberTypes
.NestedType
) != 0)
672 type
|= EntryType
.NestedType
| EntryType
.Static
| EntryType
.Instance
;
674 if ((bf
& BindingFlags
.Instance
) != 0)
675 type
|= EntryType
.Instance
;
676 if ((bf
& BindingFlags
.Static
) != 0)
677 type
|= EntryType
.Static
;
678 if ((bf
& BindingFlags
.Public
) != 0)
679 type
|= EntryType
.Public
;
680 if ((bf
& BindingFlags
.NonPublic
) != 0)
681 type
|= EntryType
.NonPublic
;
682 if ((bf
& BindingFlags
.DeclaredOnly
) != 0)
683 type
|= EntryType
.Declared
;
689 /// The `MemberTypes' enumeration type is a [Flags] type which means that it may
690 /// denote multiple member types. Returns true if the given flags value denotes a
691 /// single member types.
693 public static bool IsSingleMemberType (MemberTypes mt
)
696 case MemberTypes
.Constructor
:
697 case MemberTypes
.Event
:
698 case MemberTypes
.Field
:
699 case MemberTypes
.Method
:
700 case MemberTypes
.Property
:
701 case MemberTypes
.NestedType
:
710 /// We encode the MemberTypes and BindingFlags of each members in a "magic"
711 /// number to speed up the searching process.
714 public enum EntryType
{
719 MaskStatic
= Instance
|Static
,
723 MaskProtection
= Public
|NonPublic
,
734 NotExtensionMethod
= 0x800,
736 MaskType
= Constructor
|Event
|Field
|Method
|Property
|NestedType
739 public class CacheEntry
{
740 public readonly IMemberContainer Container
;
741 public EntryType EntryType
;
742 public readonly MemberInfo Member
;
744 public CacheEntry (IMemberContainer container
, MemberInfo member
,
745 MemberTypes mt
, BindingFlags bf
)
747 this.Container
= container
;
748 this.Member
= member
;
749 this.EntryType
= GetEntryType (mt
, bf
);
752 public override string ToString ()
754 return String
.Format ("CacheEntry ({0}:{1}:{2})", Container
.Name
,
760 /// This is called each time we're walking up one level in the class hierarchy
761 /// and checks whether we can abort the search since we've already found what
762 /// we were looking for.
764 protected bool DoneSearching (IList
<MemberInfo
> list
)
767 // We've found exactly one member in the current class and it's not
768 // a method or constructor.
770 if (list
.Count
== 1 && !(list
[0] is MethodBase
))
774 // Multiple properties: we query those just to find out the indexer
777 if ((list
.Count
> 0) && (list
[0] is PropertyInfo
))
784 /// Looks up members with name `name'. If you provide an optional
785 /// filter function, it'll only be called with members matching the
786 /// requested member name.
788 /// This method will try to use the cache to do the lookup if possible.
790 /// Unlike other FindMembers implementations, this method will always
791 /// check all inherited members - even when called on an interface type.
793 /// If you know that you're only looking for methods, you should use
794 /// MemberTypes.Method alone since this speeds up the lookup a bit.
795 /// When doing a method-only search, it'll try to use a special method
796 /// cache (unless it's a dynamic type or an interface) and the returned
797 /// MemberInfo's will have the correct ReflectedType for inherited methods.
798 /// The lookup process will automatically restart itself in method-only
799 /// search mode if it discovers that it's about to return methods.
801 List
<MemberInfo
> global = new List
<MemberInfo
> ();
804 static MemberInfo
[] emptyMemberInfo
= new MemberInfo
[0];
806 public MemberInfo
[] FindMembers (MemberTypes mt
, BindingFlags bf
, string name
,
807 MemberFilter filter
, object criteria
)
810 throw new Exception ();
812 bool declared_only
= (bf
& BindingFlags
.DeclaredOnly
) != 0;
813 bool method_search
= mt
== MemberTypes
.Method
;
814 // If we have a method cache and we aren't already doing a method-only search,
815 // then we restart a method search if the first match is a method.
816 bool do_method_search
= !method_search
&& (method_hash
!= null);
818 List
<CacheEntry
> applicable
;
820 // If this is a method-only search, we try to use the method cache if
821 // possible; a lookup in the method cache will return a MemberInfo with
822 // the correct ReflectedType for inherited methods.
824 if (method_search
&& (method_hash
!= null))
825 method_hash
.TryGetValue (name
, out applicable
);
827 member_hash
.TryGetValue (name
, out applicable
);
829 if (applicable
== null)
830 return emptyMemberInfo
;
833 // 32 slots gives 53 rss/54 size
834 // 2/4 slots gives 55 rss
836 // Strange: from 25,000 calls, only 1,800
837 // are above 2. Why does this impact it?
842 Timer
.StartTimer (TimerType
.CachedLookup
);
844 EntryType type
= GetEntryType (mt
, bf
);
846 IMemberContainer current
= Container
;
848 bool do_interface_search
= current
.IsInterface
;
850 // `applicable' is a list of all members with the given member name `name'
851 // in the current class and all its base classes. The list is sorted in
852 // reverse order due to the way how the cache is initialy created (to speed
853 // things up, we're doing a deep-copy of our base).
855 for (int i
= applicable
.Count
-1; i
>= 0; i
--) {
856 CacheEntry entry
= (CacheEntry
) applicable
[i
];
858 // This happens each time we're walking one level up in the class
859 // hierarchy. If we're doing a DeclaredOnly search, we must abort
860 // the first time this happens (this may already happen in the first
861 // iteration of this loop if there are no members with the name we're
862 // looking for in the current class).
863 if (entry
.Container
!= current
) {
867 if (!do_interface_search
&& DoneSearching (global))
870 current
= entry
.Container
;
873 // Is the member of the correct type ?
874 if ((entry
.EntryType
& type
& EntryType
.MaskType
) == 0)
877 // Is the member static/non-static ?
878 if ((entry
.EntryType
& type
& EntryType
.MaskStatic
) == 0)
881 // Apply the filter to it.
882 if (filter (entry
.Member
, criteria
)) {
883 if ((entry
.EntryType
& EntryType
.MaskType
) != EntryType
.Method
) {
884 do_method_search
= false;
887 // Because interfaces support multiple inheritance we have to be sure that
888 // base member is from same interface, so only top level member will be returned
889 if (do_interface_search
&& global.Count
> 0) {
890 bool member_already_exists
= false;
892 foreach (MemberInfo mi
in global) {
893 if (mi
is MethodBase
)
896 if (IsInterfaceBaseInterface (TypeManager
.GetInterfaces (mi
.DeclaringType
), entry
.Member
.DeclaringType
)) {
897 member_already_exists
= true;
901 if (member_already_exists
)
905 global.Add (entry
.Member
);
909 Timer
.StopTimer (TimerType
.CachedLookup
);
911 // If we have a method cache and we aren't already doing a method-only
912 // search, we restart in method-only search mode if the first match is
913 // a method. This ensures that we return a MemberInfo with the correct
914 // ReflectedType for inherited methods.
915 if (do_method_search
&& (global.Count
> 0)){
916 using_global
= false;
918 return FindMembers (MemberTypes
.Method
, bf
, name
, filter
, criteria
);
921 using_global
= false;
922 MemberInfo
[] copy
= new MemberInfo
[global.Count
];
923 global.CopyTo (copy
);
928 /// Returns true if iterface exists in any base interfaces (ifaces)
930 static bool IsInterfaceBaseInterface (Type
[] ifaces
, Type ifaceToFind
)
932 foreach (Type iface
in ifaces
) {
933 if (iface
== ifaceToFind
)
936 Type
[] base_ifaces
= TypeManager
.GetInterfaces (iface
);
937 if (base_ifaces
.Length
> 0 && IsInterfaceBaseInterface (base_ifaces
, ifaceToFind
))
943 // find the nested type @name in @this.
944 public Type
FindNestedType (string name
)
946 List
<CacheEntry
> applicable
;
947 if (!member_hash
.TryGetValue (name
, out applicable
))
950 for (int i
= applicable
.Count
-1; i
>= 0; i
--) {
951 CacheEntry entry
= applicable
[i
];
952 if ((entry
.EntryType
& EntryType
.NestedType
& EntryType
.MaskType
) != 0)
953 return (Type
) entry
.Member
;
959 public MemberInfo
FindBaseEvent (Type invocation_type
, string name
)
961 List
<CacheEntry
> applicable
;
962 if (!member_hash
.TryGetValue (name
, out applicable
))
966 // Walk the chain of events, starting from the top.
968 for (int i
= applicable
.Count
- 1; i
>= 0; i
--)
970 CacheEntry entry
= applicable
[i
];
971 if ((entry
.EntryType
& EntryType
.Event
) == 0)
974 EventInfo ei
= (EventInfo
)entry
.Member
;
975 return ei
.GetAddMethod (true);
982 // Looks for extension methods with defined name and extension type
984 public List
<MethodSpec
> FindExtensionMethods (Assembly thisAssembly
, Type extensionType
, string name
, bool publicOnly
)
986 List
<CacheEntry
> entries
;
987 if (method_hash
!= null)
988 method_hash
.TryGetValue (name
, out entries
);
990 member_hash
.TryGetValue (name
, out entries
);
996 EntryType entry_type
= EntryType
.Static
| EntryType
.Method
| EntryType
.NotExtensionMethod
;
997 EntryType found_entry_type
= entry_type
& ~EntryType
.NotExtensionMethod
;
999 List
<MethodSpec
> candidates
= null;
1000 foreach (CacheEntry entry
in entries
) {
1001 if ((entry
.EntryType
& entry_type
) == found_entry_type
) {
1002 MethodBase mb
= (MethodBase
)entry
.Member
;
1004 // Simple accessibility check
1005 if ((entry
.EntryType
& EntryType
.Public
) == 0 && publicOnly
) {
1006 MethodAttributes ma
= mb
.Attributes
& MethodAttributes
.MemberAccessMask
;
1007 if (ma
!= MethodAttributes
.Assembly
&& ma
!= MethodAttributes
.FamORAssem
)
1010 if (!TypeManager
.IsThisOrFriendAssembly (thisAssembly
, mb
.DeclaringType
.Assembly
))
1014 IMethodData md
= TypeManager
.GetMethod (mb
);
1015 AParametersCollection pd
= md
== null ?
1016 TypeManager
.GetParameterData (mb
) : md
.ParameterInfo
;
1018 Type ex_type
= pd
.ExtensionMethodType
;
1019 if (ex_type
== null) {
1020 entry
.EntryType
|= EntryType
.NotExtensionMethod
;
1024 if (candidates
== null)
1025 candidates
= new List
<MethodSpec
> (2);
1026 candidates
.Add (Import
.CreateMethod (mb
));
1034 // This finds the method or property for us to override. invocation_type is the type where
1035 // the override is going to be declared, name is the name of the method/property, and
1036 // param_types is the parameters, if any to the method or property
1038 // Because the MemberCache holds members from this class and all the base classes,
1039 // we can avoid tons of reflection stuff.
1041 public MemberInfo
FindMemberToOverride (Type invocation_type
, string name
, AParametersCollection parameters
, GenericMethod generic_method
, bool is_property
)
1043 List
<CacheEntry
> applicable
;
1044 if (method_hash
!= null && !is_property
)
1045 method_hash
.TryGetValue (name
, out applicable
);
1047 member_hash
.TryGetValue (name
, out applicable
);
1049 if (applicable
== null)
1052 // Walk the chain of methods, starting from the top.
1054 for (int i
= applicable
.Count
- 1; i
>= 0; i
--) {
1055 CacheEntry entry
= applicable
[i
];
1057 if ((entry
.EntryType
& (is_property
? (EntryType
.Property
| EntryType
.Field
) : EntryType
.Method
)) == 0)
1060 PropertyInfo pi
= null;
1061 MethodInfo mi
= null;
1062 FieldInfo fi
= null;
1063 AParametersCollection cmp_attrs
;
1066 if ((entry
.EntryType
& EntryType
.Field
) != 0) {
1067 fi
= (FieldInfo
)entry
.Member
;
1068 cmp_attrs
= ParametersCompiled
.EmptyReadOnlyParameters
;
1070 pi
= (PropertyInfo
) entry
.Member
;
1071 cmp_attrs
= TypeManager
.GetParameterData (pi
);
1074 mi
= (MethodInfo
) entry
.Member
;
1075 cmp_attrs
= TypeManager
.GetParameterData (mi
);
1079 // TODO: Almost duplicate !
1081 switch (fi
.Attributes
& FieldAttributes
.FieldAccessMask
) {
1082 case FieldAttributes
.PrivateScope
:
1084 case FieldAttributes
.Private
:
1086 // A private method is Ok if we are a nested subtype.
1087 // The spec actually is not very clear about this, see bug 52458.
1089 if (!invocation_type
.Equals (entry
.Container
.Type
) &&
1090 !TypeManager
.IsNestedChildOf (invocation_type
, entry
.Container
.Type
))
1093 case FieldAttributes
.FamANDAssem
:
1094 case FieldAttributes
.Assembly
:
1096 // Check for assembly methods
1098 if (fi
.DeclaringType
.Assembly
!= CodeGen
.Assembly
.Builder
)
1102 return entry
.Member
;
1106 // Check the arguments
1108 if (cmp_attrs
.Count
!= parameters
.Count
)
1112 for (j
= 0; j
< cmp_attrs
.Count
; ++j
) {
1114 // LAMESPEC: No idea why `params' modifier is ignored
1116 if ((parameters
.FixedParameters
[j
].ModFlags
& ~Parameter
.Modifier
.PARAMS
) !=
1117 (cmp_attrs
.FixedParameters
[j
].ModFlags
& ~Parameter
.Modifier
.PARAMS
))
1120 if (!TypeManager
.IsEqual (parameters
.Types
[j
], cmp_attrs
.Types
[j
]))
1124 if (j
< cmp_attrs
.Count
)
1128 // check generic arguments for methods
1131 Type
[] cmpGenArgs
= TypeManager
.GetGenericArguments (mi
);
1132 if (generic_method
== null && cmpGenArgs
!= null && cmpGenArgs
.Length
!= 0)
1134 if (generic_method
!= null && cmpGenArgs
!= null && cmpGenArgs
.Length
!= generic_method
.TypeParameters
.Length
)
1139 // get one of the methods because this has the visibility info.
1142 mi
= pi
.GetGetMethod (true);
1144 mi
= pi
.GetSetMethod (true);
1150 switch (mi
.Attributes
& MethodAttributes
.MemberAccessMask
) {
1151 case MethodAttributes
.PrivateScope
:
1153 case MethodAttributes
.Private
:
1155 // A private method is Ok if we are a nested subtype.
1156 // The spec actually is not very clear about this, see bug 52458.
1158 if (!invocation_type
.Equals (entry
.Container
.Type
) &&
1159 !TypeManager
.IsNestedChildOf (invocation_type
, entry
.Container
.Type
))
1162 case MethodAttributes
.FamANDAssem
:
1163 case MethodAttributes
.Assembly
:
1165 // Check for assembly methods
1167 if (!TypeManager
.IsThisOrFriendAssembly (invocation_type
.Assembly
, mi
.DeclaringType
.Assembly
))
1171 return entry
.Member
;
1178 /// The method is looking for conflict with inherited symbols (errors CS0108, CS0109).
1179 /// We handle two cases. The first is for types without parameters (events, field, properties).
1180 /// The second are methods, indexers and this is why ignore_complex_types is here.
1181 /// The latest param is temporary hack. See DoDefineMembers method for more info.
1183 public MemberInfo
FindMemberWithSameName (string name
, bool ignore_complex_types
, MemberInfo ignore_member
)
1185 List
<CacheEntry
> applicable
= null;
1187 if (method_hash
!= null)
1188 method_hash
.TryGetValue (name
, out applicable
);
1190 if (applicable
!= null) {
1191 for (int i
= applicable
.Count
- 1; i
>= 0; i
--) {
1192 CacheEntry entry
= (CacheEntry
) applicable
[i
];
1193 if ((entry
.EntryType
& EntryType
.Public
) != 0)
1194 return entry
.Member
;
1198 if (member_hash
== null)
1201 if (member_hash
.TryGetValue (name
, out applicable
)) {
1202 for (int i
= applicable
.Count
- 1; i
>= 0; i
--) {
1203 CacheEntry entry
= (CacheEntry
) applicable
[i
];
1204 if ((entry
.EntryType
& EntryType
.Public
) != 0 & entry
.Member
!= ignore_member
) {
1205 if (ignore_complex_types
) {
1206 if ((entry
.EntryType
& EntryType
.Method
) != 0)
1209 // Does exist easier way how to detect indexer ?
1210 if ((entry
.EntryType
& EntryType
.Property
) != 0) {
1211 AParametersCollection arg_types
= TypeManager
.GetParameterData ((PropertyInfo
)entry
.Member
);
1212 if (arg_types
.Count
> 0)
1216 return entry
.Member
;
1225 /// Builds low-case table for CLS Compliance test
1227 public Dictionary
<string, object> GetPublicMembers ()
1229 if (locase_table
!= null)
1230 return locase_table
;
1232 locase_table
= new Dictionary
<string, object> ();
1233 foreach (var entry
in member_hash
) {
1234 var members
= entry
.Value
;
1235 for (int ii
= 0; ii
< members
.Count
; ++ii
) {
1236 CacheEntry member_entry
= members
[ii
];
1238 if ((member_entry
.EntryType
& EntryType
.Public
) == 0)
1241 // TODO: Does anyone know easier way how to detect that member is internal ?
1242 switch (member_entry
.EntryType
& EntryType
.MaskType
) {
1243 case EntryType
.Constructor
:
1246 case EntryType
.Field
:
1247 if ((((FieldInfo
)member_entry
.Member
).Attributes
& (FieldAttributes
.Assembly
| FieldAttributes
.Public
)) == FieldAttributes
.Assembly
)
1251 case EntryType
.Method
:
1252 if ((((MethodInfo
)member_entry
.Member
).Attributes
& (MethodAttributes
.Assembly
| MethodAttributes
.Public
)) == MethodAttributes
.Assembly
)
1256 case EntryType
.Property
:
1257 PropertyInfo pi
= (PropertyInfo
)member_entry
.Member
;
1258 if (pi
.GetSetMethod () == null && pi
.GetGetMethod () == null)
1262 case EntryType
.Event
:
1263 EventInfo ei
= (EventInfo
)member_entry
.Member
;
1264 MethodInfo mi
= ei
.GetAddMethod ();
1265 if ((mi
.Attributes
& (MethodAttributes
.Assembly
| MethodAttributes
.Public
)) == MethodAttributes
.Assembly
)
1269 string lcase
= ((string)entry
.Key
).ToLower (System
.Globalization
.CultureInfo
.InvariantCulture
);
1270 locase_table
[lcase
] = member_entry
.Member
;
1274 return locase_table
;
1277 public IDictionary
<string, List
<CacheEntry
>> Members
{
1284 /// Cls compliance check whether methods or constructors parameters differing only in ref or out, or in array rank
1287 // TODO: refactor as method is always 'this'
1288 public static void VerifyClsParameterConflict (IList
<CacheEntry
> al
, MethodCore method
, MemberInfo this_builder
, Report Report
)
1290 EntryType tested_type
= (method
is Constructor
? EntryType
.Constructor
: EntryType
.Method
) | EntryType
.Public
;
1292 for (int i
= 0; i
< al
.Count
; ++i
) {
1296 if (entry
.Member
== this_builder
)
1299 if ((entry
.EntryType
& tested_type
) != tested_type
)
1302 MethodBase method_to_compare
= (MethodBase
)entry
.Member
;
1303 AttributeTester
.Result result
= AttributeTester
.AreOverloadedMethodParamsClsCompliant (
1304 method
.Parameters
, TypeManager
.GetParameterData (method_to_compare
));
1306 if (result
== AttributeTester
.Result
.Ok
)
1309 IMethodData md
= TypeManager
.GetMethod (method_to_compare
);
1311 // TODO: now we are ignoring CLSCompliance(false) on method from other assembly which is buggy.
1312 // However it is exactly what csc does.
1313 if (md
!= null && !md
.IsClsComplianceRequired ())
1316 Report
.SymbolRelatedToPreviousError (entry
.Member
);
1318 case AttributeTester
.Result
.RefOutArrayError
:
1319 Report
.Warning (3006, 1, method
.Location
,
1320 "Overloaded method `{0}' differing only in ref or out, or in array rank, is not CLS-compliant",
1321 method
.GetSignatureForError ());
1323 case AttributeTester
.Result
.ArrayArrayError
:
1324 Report
.Warning (3007, 1, method
.Location
,
1325 "Overloaded method `{0}' differing only by unnamed array types is not CLS-compliant",
1326 method
.GetSignatureForError ());
1330 throw new NotImplementedException (result
.ToString ());
1334 public bool CheckExistingMembersOverloads (MemberCore member
, string name
, ParametersCompiled parameters
, Report Report
)
1336 List
<CacheEntry
> entries
;
1337 if (!member_hash
.TryGetValue (name
, out entries
))
1340 int method_param_count
= parameters
.Count
;
1341 for (int i
= entries
.Count
- 1; i
>= 0; --i
) {
1342 CacheEntry ce
= (CacheEntry
) entries
[i
];
1344 if (ce
.Container
!= member
.Parent
.PartialContainer
)
1348 AParametersCollection pd
;
1349 if ((ce
.EntryType
& EntryType
.Property
) != 0) {
1350 pd
= TypeManager
.GetParameterData ((PropertyInfo
) ce
.Member
);
1353 MethodBase mb
= (MethodBase
) ce
.Member
;
1355 // TODO: This is more like a hack, because we are adding generic methods
1356 // twice with and without arity name
1357 if (TypeManager
.IsGenericMethod (mb
) && !member
.MemberName
.IsGeneric
)
1360 pd
= TypeManager
.GetParameterData (mb
);
1364 if (p_types
.Length
!= method_param_count
)
1367 if (method_param_count
> 0) {
1368 int ii
= method_param_count
- 1;
1369 Type type_a
, type_b
;
1371 type_a
= parameters
.Types
[ii
];
1372 type_b
= p_types
[ii
];
1374 if (TypeManager
.IsGenericParameter (type_a
) && type_a
.DeclaringMethod
!= null)
1375 type_a
= typeof (TypeParameter
);
1377 if (TypeManager
.IsGenericParameter (type_b
) && type_b
.DeclaringMethod
!= null)
1378 type_b
= typeof (TypeParameter
);
1380 if ((pd
.FixedParameters
[ii
].ModFlags
& Parameter
.Modifier
.ISBYREF
) !=
1381 (parameters
.FixedParameters
[ii
].ModFlags
& Parameter
.Modifier
.ISBYREF
))
1384 } while (TypeManager
.IsEqual (type_a
, type_b
) && ii
-- != 0);
1390 // Operators can differ in return type only
1392 if (member
is Operator
) {
1393 Operator op
= TypeManager
.GetMethod ((MethodBase
) ce
.Member
) as Operator
;
1394 if (op
!= null && op
.ReturnType
!= ((Operator
) member
).ReturnType
)
1399 // Report difference in parameter modifiers only
1401 if (pd
!= null && member
is MethodCore
) {
1402 ii
= method_param_count
;
1403 while (ii
-- != 0 && parameters
.FixedParameters
[ii
].ModFlags
== pd
.FixedParameters
[ii
].ModFlags
&&
1404 parameters
.ExtensionMethodType
== pd
.ExtensionMethodType
);
1407 MethodCore mc
= TypeManager
.GetMethod ((MethodBase
) ce
.Member
) as MethodCore
;
1408 Report
.SymbolRelatedToPreviousError (ce
.Member
);
1409 if ((member
.ModFlags
& Modifiers
.PARTIAL
) != 0 && (mc
.ModFlags
& Modifiers
.PARTIAL
) != 0) {
1410 if (parameters
.HasParams
|| pd
.HasParams
) {
1411 Report
.Error (758, member
.Location
,
1412 "A partial method declaration and partial method implementation cannot differ on use of `params' modifier");
1414 Report
.Error (755, member
.Location
,
1415 "A partial method declaration and partial method implementation must be both an extension method or neither");
1418 if (member
is Constructor
) {
1419 Report
.Error (851, member
.Location
,
1420 "Overloaded contructor `{0}' cannot differ on use of parameter modifiers only",
1421 member
.GetSignatureForError ());
1423 Report
.Error (663, member
.Location
,
1424 "Overloaded method `{0}' cannot differ on use of parameter modifiers only",
1425 member
.GetSignatureForError ());
1433 if ((ce
.EntryType
& EntryType
.Method
) != 0) {
1434 Method method_a
= member
as Method
;
1435 Method method_b
= TypeManager
.GetMethod ((MethodBase
) ce
.Member
) as Method
;
1436 if (method_a
!= null && method_b
!= null && (method_a
.ModFlags
& method_b
.ModFlags
& Modifiers
.PARTIAL
) != 0) {
1437 const Modifiers partial_modifiers
= Modifiers
.STATIC
| Modifiers
.UNSAFE
;
1438 if (method_a
.IsPartialDefinition
== method_b
.IsPartialImplementation
) {
1439 if ((method_a
.ModFlags
& partial_modifiers
) == (method_b
.ModFlags
& partial_modifiers
) ||
1440 method_a
.Parent
.IsUnsafe
&& method_b
.Parent
.IsUnsafe
) {
1441 if (method_a
.IsPartialImplementation
) {
1442 method_a
.SetPartialDefinition (method_b
);
1443 entries
.RemoveAt (i
);
1445 method_b
.SetPartialDefinition (method_a
);
1446 method_a
.caching_flags
|= MemberCore
.Flags
.PartialDefinitionExists
;
1451 if ((method_a
.ModFlags
& Modifiers
.STATIC
) != (method_b
.ModFlags
& Modifiers
.STATIC
)) {
1452 Report
.SymbolRelatedToPreviousError (ce
.Member
);
1453 Report
.Error (763, member
.Location
,
1454 "A partial method declaration and partial method implementation must be both `static' or neither");
1457 Report
.SymbolRelatedToPreviousError (ce
.Member
);
1458 Report
.Error (764, member
.Location
,
1459 "A partial method declaration and partial method implementation must be both `unsafe' or neither");
1463 Report
.SymbolRelatedToPreviousError (ce
.Member
);
1464 if (method_a
.IsPartialDefinition
) {
1465 Report
.Error (756, member
.Location
, "A partial method `{0}' declaration is already defined",
1466 member
.GetSignatureForError ());
1468 Report
.Error (757, member
.Location
, "A partial method `{0}' implementation is already defined",
1469 member
.GetSignatureForError ());
1475 Report
.SymbolRelatedToPreviousError (ce
.Member
);
1476 IMethodData duplicate_member
= TypeManager
.GetMethod ((MethodBase
) ce
.Member
);
1477 if (member
is Operator
&& duplicate_member
is Operator
) {
1478 Report
.Error (557, member
.Location
, "Duplicate user-defined conversion in type `{0}'",
1479 member
.Parent
.GetSignatureForError ());
1483 bool is_reserved_a
= member
is AbstractPropertyEventMethod
|| member
is Operator
;
1484 bool is_reserved_b
= duplicate_member
is AbstractPropertyEventMethod
|| duplicate_member
is Operator
;
1486 if (is_reserved_a
|| is_reserved_b
) {
1487 Report
.Error (82, member
.Location
, "A member `{0}' is already reserved",
1489 TypeManager
.GetFullNameSignature (ce
.Member
) :
1490 member
.GetSignatureForError ());
1494 Report
.SymbolRelatedToPreviousError (ce
.Member
);
1497 Report
.Error (111, member
.Location
,
1498 "A member `{0}' is already defined. Rename this member or use different parameter types",
1499 member
.GetSignatureForError ());