2010-04-07 Jb Evain <jbevain@novell.com>
[mcs.git] / mcs / membercache.cs
blob9e22cdf99d1fa86d6ffcbbfaedb63fb4dedad2f1
1 //
2 // membercache.cs: A container for all member lookups
3 //
4 // Author: Miguel de Icaza (miguel@gnu.org)
5 // Marek Safar (marek.safar@gmail.com)
6 //
7 // Dual licensed under the terms of the MIT X11 or GNU GPL
8 //
9 // Copyright 2001 Ximian, Inc (http://www.ximian.com)
10 // Copyright 2004-2010 Novell, Inc
14 using System;
15 using System.Text;
16 using System.Collections.Generic;
17 using System.Globalization;
18 using System.Reflection.Emit;
19 using System.Reflection;
21 namespace Mono.CSharp {
23 [Flags]
24 public enum MemberKind
26 Constructor = 1,
27 Event = 1 << 1,
28 Field = 1 << 2,
29 Method = 1 << 3,
30 Property = 1 << 4,
31 Indexer = 1 << 5,
32 Operator = 1 << 6,
33 Destructor = 1 << 7,
34 //Constant = 1 << 8,
36 NestedType = 1 << 10,
38 Class = 1 << 11,
39 Struct = 1 << 12,
40 Delegate = 1 << 13,
41 Enum = 1 << 14,
42 Interface = 1 << 15,
44 MaskType = Constructor | Event | Field | Method | Property | NestedType | Indexer | Operator | Destructor,
45 All = MaskType
48 [Flags]
49 public enum BindingRestriction
51 None = 0,
53 // Member has to be accessible
54 AccessibleOnly = 1,
56 // Inspect only queried type members
57 DeclaredOnly = 1 << 1,
59 // Excluded static
60 InstanceOnly = 1 << 2,
62 //
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)
83 Name = name;
84 Kind = kind;
85 Parameters = null;
86 MemberType = null;
89 public MemberFilter (string name, MemberKind kind, TypeSpec[] param, TypeSpec type)
90 : this (name, kind)
92 Name = name;
93 Kind = kind;
94 Parameters = param;
95 MemberType = 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)
124 return false;
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))
130 return true;
133 return false;
136 if (MemberType != null) {
137 //throw new NotImplementedException ();
140 return true;
143 #endregion
146 /// <summary>
147 /// This is a readonly list of MemberInfo's.
148 /// </summary>
149 public class MemberList : IList<MemberInfo> {
150 public readonly IList<MemberInfo> List;
151 int count;
153 /// <summary>
154 /// Create a new MemberList from the given IList.
155 /// </summary>
156 public MemberList (IList<MemberInfo> list)
158 if (list != null)
159 this.List = list;
160 else
161 this.List = new List<MemberInfo> ();
162 count = List.Count;
165 /// <summary>
166 /// Concatenate the ILists `first' and `second' to a new MemberList.
167 /// </summary>
168 public MemberList (IList<MemberInfo> first, IList<MemberInfo> second)
170 var list = new List<MemberInfo> ();
171 list.AddRange (first);
172 list.AddRange (second);
173 count = list.Count;
174 List = list;
177 public static readonly MemberList Empty = new MemberList (Array.AsReadOnly (new MemberInfo[0]));
179 /// <summary>
180 /// Cast the MemberList into a MemberInfo[] array.
181 /// </summary>
182 /// <remarks>
183 /// This is an expensive operation, only use it if it's really necessary.
184 /// </remarks>
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);
191 return result;
194 // ICollection
196 public int Count {
197 get {
198 return count;
202 public void CopyTo (MemberInfo[] array, int index)
204 List.CopyTo (array, index);
207 // IEnumerable
209 public IEnumerator<MemberInfo> GetEnumerator ()
211 return List.GetEnumerator ();
214 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
216 return List.GetEnumerator ();
219 // IList
221 public bool IsFixedSize {
222 get {
223 return true;
227 public bool IsReadOnly {
228 get {
229 return true;
233 MemberInfo IList<MemberInfo>.this [int index] {
234 get {
235 return List [index];
238 set {
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] {
245 get {
246 return (MemberInfo) List [index];
250 public void Add (MemberInfo value)
252 throw new NotSupportedException ();
255 public void Clear ()
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 ();
286 /// <summary>
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.
291 /// </summary>
292 public interface IMemberContainer {
293 /// <summary>
294 /// The name of the IMemberContainer. This is only used for
295 /// debugging purposes.
296 /// </summary>
297 string Name {
298 get;
301 /// <summary>
302 /// The type of this IMemberContainer.
303 /// </summary>
304 Type Type {
305 get;
308 /// <summary>
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.
313 /// </summary>
314 MemberCache BaseCache {
315 get;
318 /// <summary>
319 /// Whether this is an interface.
320 /// </summary>
321 bool IsInterface {
322 get;
325 /// <summary>
326 /// Returns all members of this class with the corresponding MemberTypes
327 /// and BindingFlags.
328 /// </summary>
329 /// <remarks>
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.
336 /// </remarks>
337 MemberList GetMembers (MemberTypes mt, BindingFlags bf);
340 /// <summary>
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.
353 /// </summary>
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> ();
363 /// <summary>
364 /// Create a new MemberCache for the given IMemberContainer `container'.
365 /// </summary>
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);
377 else
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>> ();
388 AddMethods (type);
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>> ();
402 else
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>> ();
415 if (ifaces == null)
416 return;
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);
430 else
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);
438 if (cache != null)
439 AddCacheContents (cache);
444 /// <summary>
445 /// Bootstrap this member cache by doing a deep-copy of our base.
446 /// </summary>
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));
458 return hash;
462 // Converts ModFlags to BindingFlags
464 static BindingFlags GetBindingFlags (Modifiers modifiers)
466 BindingFlags bf;
467 if ((modifiers & Modifiers.STATIC) != 0)
468 bf = BindingFlags.Static;
469 else
470 bf = BindingFlags.Instance;
472 if ((modifiers & Modifiers.PRIVATE) != 0)
473 bf |= BindingFlags.NonPublic;
474 else
475 bf |= BindingFlags.Public;
477 return bf;
480 /// <summary>
481 /// Add the contents of `cache' to the member_hash.
482 /// </summary>
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)
496 break;
497 list.Add (entry);
502 /// <summary>
503 /// Add all members from class `container' to the cache.
504 /// </summary>
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));
572 /// <summary>
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.
576 /// </summary>
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);
588 if (gname != name)
589 AddMember (mt, bf, container, gname, member);
594 /// <summary>
595 /// Add all declared and inherited methods from class `type' to the method cache.
596 /// </summary>
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.
629 break;
631 overrides.Add (curr);
632 list.Add (new CacheEntry (null, base_method, MemberTypes.Method, bf));
633 curr = base_method;
636 if (overrides.Count > 0) {
637 for (int i = 0; i < overrides.Count; ++i)
638 TypeManager.RegisterOverride ((MethodBase) overrides [i], curr);
639 overrides.Clear ();
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));
652 /// <summary>
653 /// Compute and return a appropriate `EntryType' magic number for the given
654 /// MemberTypes and BindingFlags.
655 /// </summary>
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;
685 return type;
688 /// <summary>
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.
692 /// </summary>
693 public static bool IsSingleMemberType (MemberTypes mt)
695 switch (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:
702 return true;
704 default:
705 return false;
709 /// <summary>
710 /// We encode the MemberTypes and BindingFlags of each members in a "magic"
711 /// number to speed up the searching process.
712 /// </summary>
713 [Flags]
714 public enum EntryType {
715 None = 0x000,
717 Instance = 0x001,
718 Static = 0x002,
719 MaskStatic = Instance|Static,
721 Public = 0x004,
722 NonPublic = 0x008,
723 MaskProtection = Public|NonPublic,
725 Declared = 0x010,
727 Constructor = 0x020,
728 Event = 0x040,
729 Field = 0x080,
730 Method = 0x100,
731 Property = 0x200,
732 NestedType = 0x400,
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,
755 EntryType, Member);
759 /// <summary>
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.
763 /// </summary>
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))
771 return true;
774 // Multiple properties: we query those just to find out the indexer
775 // name
777 if ((list.Count > 0) && (list [0] is PropertyInfo))
778 return true;
780 return false;
783 /// <summary>
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.
800 /// </summary>
801 List<MemberInfo> global = new List<MemberInfo> ();
802 bool using_global;
804 static MemberInfo [] emptyMemberInfo = new MemberInfo [0];
806 public MemberInfo [] FindMembers (MemberTypes mt, BindingFlags bf, string name,
807 MemberFilter filter, object criteria)
809 if (using_global)
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);
826 else
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?
839 global.Clear ();
840 using_global = true;
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) {
864 if (declared_only)
865 break;
867 if (!do_interface_search && DoneSearching (global))
868 break;
870 current = entry.Container;
873 // Is the member of the correct type ?
874 if ((entry.EntryType & type & EntryType.MaskType) == 0)
875 continue;
877 // Is the member static/non-static ?
878 if ((entry.EntryType & type & EntryType.MaskStatic) == 0)
879 continue;
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)
894 continue;
896 if (IsInterfaceBaseInterface (TypeManager.GetInterfaces (mi.DeclaringType), entry.Member.DeclaringType)) {
897 member_already_exists = true;
898 break;
901 if (member_already_exists)
902 continue;
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);
924 return copy;
927 /// <summary>
928 /// Returns true if iterface exists in any base interfaces (ifaces)
929 /// </summary>
930 static bool IsInterfaceBaseInterface (Type[] ifaces, Type ifaceToFind)
932 foreach (Type iface in ifaces) {
933 if (iface == ifaceToFind)
934 return true;
936 Type[] base_ifaces = TypeManager.GetInterfaces (iface);
937 if (base_ifaces.Length > 0 && IsInterfaceBaseInterface (base_ifaces, ifaceToFind))
938 return true;
940 return false;
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))
948 return null;
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;
956 return null;
959 public MemberInfo FindBaseEvent (Type invocation_type, string name)
961 List<CacheEntry> applicable;
962 if (!member_hash.TryGetValue (name, out applicable))
963 return null;
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)
972 continue;
974 EventInfo ei = (EventInfo)entry.Member;
975 return ei.GetAddMethod (true);
978 return null;
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);
989 else {
990 member_hash.TryGetValue (name, out entries);
993 if (entries == null)
994 return null;
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)
1008 continue;
1010 if (!TypeManager.IsThisOrFriendAssembly (thisAssembly, mb.DeclaringType.Assembly))
1011 continue;
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;
1021 continue;
1024 if (candidates == null)
1025 candidates = new List<MethodSpec> (2);
1026 candidates.Add (Import.CreateMethod (mb));
1030 return candidates;
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);
1046 else
1047 member_hash.TryGetValue (name, out applicable);
1049 if (applicable == null)
1050 return 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)
1058 continue;
1060 PropertyInfo pi = null;
1061 MethodInfo mi = null;
1062 FieldInfo fi = null;
1063 AParametersCollection cmp_attrs;
1065 if (is_property) {
1066 if ((entry.EntryType & EntryType.Field) != 0) {
1067 fi = (FieldInfo)entry.Member;
1068 cmp_attrs = ParametersCompiled.EmptyReadOnlyParameters;
1069 } else {
1070 pi = (PropertyInfo) entry.Member;
1071 cmp_attrs = TypeManager.GetParameterData (pi);
1073 } else {
1074 mi = (MethodInfo) entry.Member;
1075 cmp_attrs = TypeManager.GetParameterData (mi);
1078 if (fi != null) {
1079 // TODO: Almost duplicate !
1080 // Check visibility
1081 switch (fi.Attributes & FieldAttributes.FieldAccessMask) {
1082 case FieldAttributes.PrivateScope:
1083 continue;
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))
1091 continue;
1092 break;
1093 case FieldAttributes.FamANDAssem:
1094 case FieldAttributes.Assembly:
1096 // Check for assembly methods
1098 if (fi.DeclaringType.Assembly != CodeGen.Assembly.Builder)
1099 continue;
1100 break;
1102 return entry.Member;
1106 // Check the arguments
1108 if (cmp_attrs.Count != parameters.Count)
1109 continue;
1111 int j;
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))
1118 break;
1120 if (!TypeManager.IsEqual (parameters.Types [j], cmp_attrs.Types [j]))
1121 break;
1124 if (j < cmp_attrs.Count)
1125 continue;
1128 // check generic arguments for methods
1130 if (mi != null) {
1131 Type [] cmpGenArgs = TypeManager.GetGenericArguments (mi);
1132 if (generic_method == null && cmpGenArgs != null && cmpGenArgs.Length != 0)
1133 continue;
1134 if (generic_method != null && cmpGenArgs != null && cmpGenArgs.Length != generic_method.TypeParameters.Length)
1135 continue;
1139 // get one of the methods because this has the visibility info.
1141 if (is_property) {
1142 mi = pi.GetGetMethod (true);
1143 if (mi == null)
1144 mi = pi.GetSetMethod (true);
1148 // Check visibility
1150 switch (mi.Attributes & MethodAttributes.MemberAccessMask) {
1151 case MethodAttributes.PrivateScope:
1152 continue;
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))
1160 continue;
1161 break;
1162 case MethodAttributes.FamANDAssem:
1163 case MethodAttributes.Assembly:
1165 // Check for assembly methods
1167 if (!TypeManager.IsThisOrFriendAssembly (invocation_type.Assembly, mi.DeclaringType.Assembly))
1168 continue;
1169 break;
1171 return entry.Member;
1174 return null;
1177 /// <summary>
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.
1182 /// </summary>
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)
1199 return 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)
1207 continue;
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)
1213 continue;
1216 return entry.Member;
1220 return null;
1224 /// <summary>
1225 /// Builds low-case table for CLS Compliance test
1226 /// </summary>
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)
1239 continue;
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:
1244 continue;
1246 case EntryType.Field:
1247 if ((((FieldInfo)member_entry.Member).Attributes & (FieldAttributes.Assembly | FieldAttributes.Public)) == FieldAttributes.Assembly)
1248 continue;
1249 break;
1251 case EntryType.Method:
1252 if ((((MethodInfo)member_entry.Member).Attributes & (MethodAttributes.Assembly | MethodAttributes.Public)) == MethodAttributes.Assembly)
1253 continue;
1254 break;
1256 case EntryType.Property:
1257 PropertyInfo pi = (PropertyInfo)member_entry.Member;
1258 if (pi.GetSetMethod () == null && pi.GetGetMethod () == null)
1259 continue;
1260 break;
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)
1266 continue;
1267 break;
1269 string lcase = ((string)entry.Key).ToLower (System.Globalization.CultureInfo.InvariantCulture);
1270 locase_table [lcase] = member_entry.Member;
1271 break;
1274 return locase_table;
1277 public IDictionary<string, List<CacheEntry>> Members {
1278 get {
1279 return member_hash;
1283 /// <summary>
1284 /// Cls compliance check whether methods or constructors parameters differing only in ref or out, or in array rank
1285 /// </summary>
1286 ///
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) {
1293 var entry = al [i];
1295 // skip itself
1296 if (entry.Member == this_builder)
1297 continue;
1299 if ((entry.EntryType & tested_type) != tested_type)
1300 continue;
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)
1307 continue;
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 ())
1314 continue;
1316 Report.SymbolRelatedToPreviousError (entry.Member);
1317 switch (result) {
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 ());
1322 continue;
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 ());
1327 continue;
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))
1338 return true;
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)
1345 return true;
1347 Type [] p_types;
1348 AParametersCollection pd;
1349 if ((ce.EntryType & EntryType.Property) != 0) {
1350 pd = TypeManager.GetParameterData ((PropertyInfo) ce.Member);
1351 p_types = pd.Types;
1352 } else {
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)
1358 continue;
1360 pd = TypeManager.GetParameterData (mb);
1361 p_types = pd.Types;
1364 if (p_types.Length != method_param_count)
1365 continue;
1367 if (method_param_count > 0) {
1368 int ii = method_param_count - 1;
1369 Type type_a, type_b;
1370 do {
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))
1382 break;
1384 } while (TypeManager.IsEqual (type_a, type_b) && ii-- != 0);
1386 if (ii >= 0)
1387 continue;
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)
1395 continue;
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);
1406 if (ii >= 0) {
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");
1413 } else {
1414 Report.Error (755, member.Location,
1415 "A partial method declaration and partial method implementation must be both an extension method or neither");
1417 } else {
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 ());
1422 } else {
1423 Report.Error (663, member.Location,
1424 "Overloaded method `{0}' cannot differ on use of parameter modifiers only",
1425 member.GetSignatureForError ());
1428 return false;
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);
1444 } else {
1445 method_b.SetPartialDefinition (method_a);
1446 method_a.caching_flags |= MemberCore.Flags.PartialDefinitionExists;
1448 continue;
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");
1460 return false;
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 ());
1467 } else {
1468 Report.Error (757, member.Location, "A partial method `{0}' implementation is already defined",
1469 member.GetSignatureForError ());
1472 return false;
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 ());
1480 return false;
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",
1488 is_reserved_a ?
1489 TypeManager.GetFullNameSignature (ce.Member) :
1490 member.GetSignatureForError ());
1491 return false;
1493 } else {
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 ());
1500 return false;
1503 return true;