2010-06-04 Jb Evain <jbevain@novell.com>
[mcs.git] / mcs / membercache.cs
blob495ba1951dd2abb4a1f7d873ebe5152c22719105
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;
20 using System.Linq;
22 namespace Mono.CSharp {
24 [Flags]
25 public enum MemberKind
27 Constructor = 1,
28 Event = 1 << 1,
29 Field = 1 << 2,
30 Method = 1 << 3,
31 Property = 1 << 4,
32 Indexer = 1 << 5,
33 Operator = 1 << 6,
34 Destructor = 1 << 7,
36 Class = 1 << 11,
37 Struct = 1 << 12,
38 Delegate = 1 << 13,
39 Enum = 1 << 14,
40 Interface = 1 << 15,
41 TypeParameter = 1 << 16,
43 ArrayType = 1 << 19,
44 PointerType = 1 << 20,
45 InternalCompilerType = 1 << 21,
46 FakeMethod = 1 << 22,
48 NestedMask = Class | Struct | Delegate | Enum | Interface,
49 GenericMask = Method | Class | Struct | Delegate | Interface,
50 MaskType = Constructor | Event | Field | Method | Property | Indexer | Operator | Destructor | NestedMask,
51 All = MaskType
54 [Flags]
55 public enum BindingRestriction
57 None = 0,
59 // Member has to be accessible
60 AccessibleOnly = 1,
62 // Inspect only queried type members
63 DeclaredOnly = 1 << 1,
65 // Exclude static
66 InstanceOnly = 1 << 2,
68 // Ignore member overrides
69 NoOverrides = 1 << 3
72 public struct MemberFilter : IEquatable<MemberSpec>
74 public readonly string Name;
75 public readonly MemberKind Kind;
76 public readonly AParametersCollection Parameters;
77 public readonly TypeSpec MemberType;
79 int arity; // -1 to ignore the check
80 TypeSpec invocation_type;
82 private MemberFilter (string name, MemberKind kind)
84 Name = name;
85 Kind = kind;
86 Parameters = null;
87 MemberType = null;
88 arity = -1;
89 invocation_type = null;
92 public MemberFilter (MethodSpec m)
94 Name = m.Name;
95 Kind = MemberKind.Method;
96 Parameters = m.Parameters;
97 MemberType = m.ReturnType;
98 arity = m.Arity;
99 invocation_type = null;
102 public MemberFilter (string name, int arity, MemberKind kind, AParametersCollection param, TypeSpec type)
104 Name = name;
105 Kind = kind;
106 Parameters = param;
107 MemberType = type;
108 this.arity = arity;
109 invocation_type = null;
112 public TypeSpec InvocationType {
113 get {
114 return invocation_type;
116 set {
117 invocation_type = value;
121 public static MemberFilter Constructor (AParametersCollection param)
123 return new MemberFilter (System.Reflection.ConstructorInfo.ConstructorName, 0, MemberKind.Constructor, param, null);
126 public static MemberFilter Property (string name, TypeSpec type)
128 return new MemberFilter (name, 0, MemberKind.Property, null, type);
131 public static MemberFilter Field (string name, TypeSpec type)
133 return new MemberFilter (name, 0, MemberKind.Field, null, type);
136 public static MemberFilter Method (string name, int arity, AParametersCollection param, TypeSpec type)
138 return new MemberFilter (name, arity, MemberKind.Method, param, type);
141 #region IEquatable<MemberSpec> Members
143 public bool Equals (MemberSpec other)
145 // Is the member of the correct type ?
146 // TODO: Isn't this redundant ?
147 if ((other.Kind & Kind & MemberKind.MaskType) == 0)
148 return false;
150 // Check arity when not disabled
151 if (arity >= 0 && arity != other.Arity)
152 return false;
154 if (Parameters != null) {
155 if (other is IParametersMember) {
156 var other_param = ((IParametersMember) other).Parameters;
157 if (!TypeSpecComparer.Override.IsEqual (Parameters, other_param))
158 return false;
159 } else {
160 return false;
164 if (MemberType != null) {
165 if (other is IInterfaceMemberSpec) {
166 var other_type = ((IInterfaceMemberSpec) other).MemberType;
167 if (!TypeSpecComparer.Override.IsEqual (other_type, MemberType))
168 return false;
169 } else {
170 return false;
174 if (invocation_type != null && !IsAccessible (other))
175 return false;
177 return true;
180 bool IsAccessible (MemberSpec other)
182 bool extra;
183 return Expression.IsMemberAccessible (invocation_type, other, out extra);
186 #endregion
189 /// <summary>
190 /// The MemberCache is used by dynamic and non-dynamic types to speed up
191 /// member lookups. It has a member name based hash table; it maps each member
192 /// name to a list of CacheEntry objects. Each CacheEntry contains a MemberInfo
193 /// and the BindingFlags that were initially used to get it. The cache contains
194 /// all members of the current class and all inherited members. If this cache is
195 /// for an interface types, it also contains all inherited members.
197 /// There are two ways to get a MemberCache:
198 /// * if this is a dynamic type, lookup the corresponding DeclSpace and then
199 /// use the DeclSpace.MemberCache property.
200 /// * if this not a dynamic type, call TypeHandle.GetTypeHandle() to get a
201 /// TypeHandle instance for the type and then use TypeHandle.MemberCache.
202 /// </summary>
203 public class MemberCache
205 enum StateFlags
207 HasNoImplicitOperator = 1,
208 HasNoExplicitOperator = 1 << 1
211 readonly Dictionary<string, IList<MemberSpec>> member_hash;
212 Dictionary<string, MemberSpec[]> locase_members;
213 IList<MethodSpec> missing_abstract;
214 StateFlags state;
216 public static readonly string IndexerNameAlias = "<this>";
218 public static readonly MemberCache Empty = new MemberCache (0);
220 public MemberCache ()
221 : this (16)
225 public MemberCache (int capacity)
227 member_hash = new Dictionary<string, IList<MemberSpec>> (capacity);
230 public MemberCache (MemberCache cache)
231 : this (cache.member_hash.Count)
236 // Creates a new MemberCache for the given `container'.
238 public MemberCache (TypeContainer container)
239 : this () // TODO: Optimize the size
244 // Member-cache does not contain base members but it does
245 // contain all base interface members, so the Lookup code
246 // can use simple inheritance rules.
248 public void AddInterface (TypeSpec iface)
250 var cache = iface.MemberCache;
252 IList<MemberSpec> list;
253 foreach (var entry in cache.member_hash) {
254 if (!member_hash.TryGetValue (entry.Key, out list)) {
255 if (entry.Value.Count == 1) {
256 list = entry.Value;
257 } else {
258 list = new List<MemberSpec> (entry.Value);
261 member_hash.Add (entry.Key, list);
262 continue;
265 foreach (var ce in entry.Value) {
266 if (ce.DeclaringType != iface)
267 break;
269 if (list.Contains (ce))
270 continue;
272 if (AddInterfaceMember (ce, ref list))
273 member_hash[entry.Key] = list;
278 public void AddMember (InterfaceMemberBase imb, string exlicitName, MemberSpec ms)
280 // Explicit names cannot be looked-up but can be used for
281 // collision checking (no name mangling needed)
282 if (imb.IsExplicitImpl)
283 AddMember (exlicitName, ms);
284 else
285 AddMember (ms);
289 // Add non-explicit member to member cache
291 public void AddMember (MemberSpec ms)
293 AddMember (GetLookupName (ms), ms);
296 void AddMember (string name, MemberSpec member)
298 IList<MemberSpec> list;
299 if (!member_hash.TryGetValue (name, out list)) {
300 member_hash.Add (name, new MemberSpec[] { member });
301 return;
304 if (member.DeclaringType.IsInterface) {
305 if (AddInterfaceMember (member, ref list))
306 member_hash[name] = list;
307 } else {
308 if (list is MemberSpec[]) {
309 list = new List<MemberSpec> () { list[0] };
310 member_hash[name] = list;
313 list.Add (member);
318 // Ignores any base interface member which can be hidden
319 // by this interface
321 static bool AddInterfaceMember (MemberSpec member, ref IList<MemberSpec> existing)
323 var member_param = member is IParametersMember ? ((IParametersMember) member).Parameters : ParametersCompiled.EmptyReadOnlyParameters;
326 // interface IA : IB { int Prop { set; } }
327 // interface IB { bool Prop { get; } }
329 // IB.Prop is never accessible from IA interface
331 for (int i = 0; i < existing.Count; ++i) {
332 var entry = existing[i];
334 if (entry.Arity != member.Arity)
335 continue;
337 if (entry is IParametersMember) {
338 var entry_param = ((IParametersMember) entry).Parameters;
339 if (!TypeSpecComparer.Override.IsEqual (entry_param, member_param))
340 continue;
343 if (member.DeclaringType.ImplementsInterface (entry.DeclaringType)) {
344 if (existing is MemberSpec[]) {
345 existing = new MemberSpec[] { member };
346 return true;
349 existing.RemoveAt (i--);
350 continue;
353 if (entry.DeclaringType == member.DeclaringType || entry.DeclaringType.ImplementsInterface (member.DeclaringType))
354 return false;
357 if (existing is MemberSpec[]) {
358 existing = new List<MemberSpec> () { existing[0], member };
359 return true;
362 existing.Add (member);
363 return false;
366 public static IEnumerable<IndexerSpec> FindIndexers (TypeSpec container, BindingRestriction restrictions)
368 var filter = new MemberFilter (IndexerNameAlias, 0, MemberKind.Indexer, null, null);
369 var found = FindMembers (container, filter, restrictions);
370 return found == null ? null : found.Cast<IndexerSpec> ();
373 public static MemberSpec FindMember (TypeSpec container, MemberFilter filter, BindingRestriction restrictions)
375 do {
376 IList<MemberSpec> applicable;
377 if (container.MemberCache.member_hash.TryGetValue (filter.Name, out applicable)) {
378 // Start from the end because interface members are in reverse order
379 for (int i = applicable.Count - 1; i >= 0; i--) {
380 var entry = applicable [i];
382 if ((restrictions & BindingRestriction.InstanceOnly) != 0 && entry.IsStatic)
383 continue;
385 if (filter.Equals (entry))
386 return entry;
388 // TODO MemberCache:
389 //if ((restrictions & BindingRestriction.AccessibleOnly) != 0)
390 // throw new NotImplementedException ("net");
394 container = container.BaseType;
395 } while (container != null && (restrictions & BindingRestriction.DeclaredOnly) == 0);
397 return null;
401 // Returns the first set of members starting from container
403 public static IList<MemberSpec> FindMembers (TypeSpec container, MemberFilter filter, BindingRestriction restrictions)
405 IList<MemberSpec> applicable;
406 IList<MemberSpec> found = null;
408 do {
409 if (container.MemberCache.member_hash.TryGetValue (filter.Name, out applicable)) {
410 for (int i = 0; i < applicable.Count; ++i) {
411 var entry = applicable [i];
413 // Is the member of the correct type
414 if ((entry.Kind & filter.Kind & MemberKind.MaskType) == 0)
415 continue;
418 // When using overloadable overrides filter ignore members which
419 // are not base members. Including properties because overrides can
420 // implement get or set only and we are looking for complete base member
422 const MemberKind overloadable = MemberKind.Indexer | MemberKind.Method | MemberKind.Property;
423 if ((restrictions & BindingRestriction.NoOverrides) != 0 && (entry.Kind & overloadable) != 0) {
424 if ((entry.Modifiers & Modifiers.OVERRIDE) != 0)
425 continue;
427 if ((entry.Modifiers & Modifiers.OVERRIDE_UNCHECKED) != 0) {
428 bool is_override = true;
429 var ms = entry as MethodSpec;
430 if (ms != null) {
431 is_override = IsRealMethodOverride (ms);
432 } else {
433 var ps = (PropertySpec) entry;
434 if (ps.HasGet)
435 is_override = IsRealMethodOverride (ps.Get);
436 if (is_override && ps.HasSet)
437 is_override = IsRealMethodOverride (ps.Set);
440 if (is_override) {
441 entry.Modifiers = (entry.Modifiers & ~Modifiers.OVERRIDE_UNCHECKED) | Modifiers.OVERRIDE;
442 continue;
447 if ((restrictions & BindingRestriction.InstanceOnly) != 0 && entry.IsStatic)
448 continue;
450 // Apply the filter to it.
451 if (!filter.Equals (entry))
452 continue;
454 if (found == null) {
455 if (i == 0) {
456 found = applicable;
457 } else {
458 found = new List<MemberSpec> ();
459 found.Add (entry);
461 } else if (found == applicable) {
462 found = new List<MemberSpec> ();
463 found.Add (applicable[0]);
464 found.Add (entry);
465 } else {
466 found.Add (entry);
470 if (found != null) {
471 if (found == applicable && applicable.Count != 1)
472 return new MemberSpec[] { found[0] };
474 return found;
478 container = container.BaseType;
479 } while (container != null && (restrictions & BindingRestriction.DeclaredOnly) == 0);
481 return found;
485 // Finds the nested type in container
487 public static TypeSpec FindNestedType (TypeSpec container, string name, int arity)
489 IList<MemberSpec> applicable;
490 TypeSpec best_match = null;
491 do {
492 // TODO: Don't know how to handle this yet
493 // When resolving base type of nested type, parent type must have
494 // base type resolved to scan full hierarchy correctly
495 // Similarly MemberCacheTypes will inflate BaseType and Interfaces
496 // based on type definition
497 var tc = container.MemberDefinition as TypeContainer;
498 if (tc != null)
499 tc.DefineType ();
501 if (container.MemberCacheTypes.member_hash.TryGetValue (name, out applicable)) {
502 for (int i = applicable.Count - 1; i >= 0; i--) {
503 var entry = applicable[i];
504 if ((entry.Kind & MemberKind.NestedMask) == 0)
505 continue;
507 var ts = (TypeSpec) entry;
508 if (arity == ts.Arity)
509 return ts;
511 if (arity < 0) {
512 if (best_match == null) {
513 best_match = ts;
514 } else if (System.Math.Abs (ts.Arity + arity) < System.Math.Abs (ts.Arity + arity)) {
515 best_match = ts;
521 container = container.BaseType;
522 } while (container != null);
524 return best_match;
528 // Looks for extension methods with defined name and extension type
530 public List<MethodSpec> FindExtensionMethods (TypeSpec invocationType, TypeSpec extensionType, string name, int arity)
532 IList<MemberSpec> entries;
533 if (!member_hash.TryGetValue (name, out entries))
534 return null;
536 List<MethodSpec> candidates = null;
537 foreach (var entry in entries) {
538 if (entry.Kind != MemberKind.Method || (arity >= 0 && entry.Arity != arity))
539 continue;
541 var ms = (MethodSpec) entry;
542 if (!ms.IsExtensionMethod)
543 continue;
545 bool extra;
546 if (!Expression.IsMemberAccessible (invocationType, ms, out extra))
547 continue;
549 // TODO: CodeGen.Assembly.Builder
550 if ((ms.DeclaringType.Modifiers & Modifiers.INTERNAL) != 0 &&
551 !TypeManager.IsThisOrFriendAssembly (CodeGen.Assembly.Builder, ms.Assembly))
552 continue;
554 if (candidates == null)
555 candidates = new List<MethodSpec> ();
556 candidates.Add (ms);
559 return candidates;
563 // Returns base members of @member member if no exact match is found @bestCandidate returns
564 // the best match
566 public static MemberSpec FindBaseMember (MemberCore member, out MemberSpec bestCandidate)
568 bestCandidate = null;
569 var container = member.Parent.PartialContainer.Definition;
570 if (!container.IsInterface)
571 container = container.BaseType;
573 string name = GetLookupName (member);
574 IList<MemberSpec> applicable;
575 var member_param = member is IParametersMember ? ((IParametersMember) member).Parameters : null;
577 var mkind = GetMemberCoreKind (member);
579 do {
580 if (container.MemberCache.member_hash.TryGetValue (name, out applicable)) {
581 for (int i = 0; i < applicable.Count; ++i) {
582 var entry = applicable [i];
584 if ((entry.Modifiers & Modifiers.PRIVATE) != 0)
585 continue;
587 if ((entry.Modifiers & Modifiers.AccessibilityMask) == Modifiers.INTERNAL) {
588 if (!TypeManager.IsThisOrFriendAssembly (member.Assembly, entry.Assembly))
589 continue;
593 // Is the member of the correct type ?
594 // Destructors are ignored as they cannot be overridden by user
595 if ((entry.Kind & ~MemberKind.Destructor & mkind & MemberKind.MaskType) == 0) {
596 if ((entry.Kind & MemberKind.Destructor) == 0 && (member_param == null || !(entry is IParametersMember))) {
597 bestCandidate = entry;
598 return null;
601 continue;
604 if (member_param == null)
605 return entry;
607 // Check arity match
608 int arity = member.MemberName.Arity;
609 if (arity != entry.Arity)
610 continue;
612 if (entry is IParametersMember) {
613 if (entry.IsAccessor != member is AbstractPropertyEventMethod)
614 continue;
616 var entry_param = ((IParametersMember) entry).Parameters;
617 if (TypeSpecComparer.Override.IsEqual (entry_param, member_param))
618 return entry;
620 continue;
623 if (bestCandidate == null)
624 bestCandidate = entry;
627 if (member_param == null)
628 return null;
631 if (container.IsInterface)
632 break;
634 container = container.BaseType;
635 } while (container != null);
637 return null;
641 // Returns inflated version of MemberSpec, it works similarly to
642 // SRE TypeBuilder.GetMethod
644 public static T GetMember<T> (TypeSpec container, T spec) where T : MemberSpec
646 IList<MemberSpec> applicable;
647 if (container.MemberCache.member_hash.TryGetValue (GetLookupName (spec), out applicable)) {
648 for (int i = applicable.Count - 1; i >= 0; i--) {
649 var entry = applicable[i];
650 if (entry.MemberDefinition == spec.MemberDefinition)
651 return (T) entry;
655 throw new InternalErrorException ("Missing member `{0}' on inflated type `{1}'",
656 spec.GetSignatureForError (), container.GetSignatureForError ());
659 static MemberKind GetMemberCoreKind (MemberCore member)
661 if (member is FieldBase)
662 return MemberKind.Field;
663 if (member is Indexer)
664 return MemberKind.Indexer;
665 if (member is Class)
666 return MemberKind.Class;
667 if (member is Struct)
668 return MemberKind.Struct;
669 if (member is Destructor)
670 return MemberKind.Destructor;
671 if (member is Method)
672 return MemberKind.Method;
673 if (member is Property)
674 return MemberKind.Property;
675 if (member is EventField)
676 return MemberKind.Event;
677 if (member is Interface)
678 return MemberKind.Interface;
679 if (member is EventProperty)
680 return MemberKind.Event;
682 throw new NotImplementedException (member.GetType ().ToString ());
685 public static IList<MemberSpec> GetCompletitionMembers (TypeSpec container, string name)
687 var matches = new List<MemberSpec> ();
688 foreach (var entry in container.MemberCache.member_hash) {
689 foreach (var name_entry in entry.Value) {
690 if (name_entry.IsAccessor)
691 continue;
693 if ((name_entry.Kind & (MemberKind.Constructor | MemberKind.FakeMethod | MemberKind.Destructor)) != 0)
694 continue;
696 bool extra;
697 if (!Expression.IsMemberAccessible (InternalType.FakeInternalType, name_entry, out extra))
698 continue;
700 if (name == null || name_entry.Name.StartsWith (name)) {
701 matches.Add (name_entry);
706 return matches;
710 // Returns members of @iface only, base members are ignored
712 public static IList<MethodSpec> GetInterfaceMembers (TypeSpec iface)
715 // MemberCache flatten interfaces, therefore in cases like this one
717 // interface IA : IB {}
718 // interface IB { void Foo () }
720 // we would return Foo inside IA which is not expected in this case
722 var methods = new List<MethodSpec> ();
723 foreach (var entry in iface.MemberCache.member_hash.Values) {
724 foreach (var name_entry in entry) {
725 if (iface == name_entry.DeclaringType) {
726 if (name_entry.Kind == MemberKind.Method) {
727 methods.Add ((MethodSpec) name_entry);
733 return methods;
736 public static IList<MethodSpec> GetNotImplementedAbstractMethods (TypeSpec type)
738 if (type.MemberCache.missing_abstract != null)
739 return type.MemberCache.missing_abstract;
741 var abstract_methods = new List<MethodSpec> ();
742 List<TypeSpec> hierarchy = null;
745 // Stage 1: top-to-bottom scan for abstract members
747 var abstract_type = type;
748 while (true) {
749 foreach (var entry in abstract_type.MemberCache.member_hash) {
750 foreach (var name_entry in entry.Value) {
751 if ((name_entry.Modifiers & Modifiers.ABSTRACT) == 0)
752 continue;
754 if (name_entry.Kind != MemberKind.Method)
755 continue;
757 abstract_methods.Add ((MethodSpec) name_entry);
761 var base_type = abstract_type.BaseType;
762 if (!base_type.IsAbstract)
763 break;
765 if (hierarchy == null)
766 hierarchy = new List<TypeSpec> ();
768 hierarchy.Add (abstract_type);
769 abstract_type = base_type;
772 int not_implemented_count = abstract_methods.Count;
773 if (not_implemented_count == 0 || hierarchy == null) {
774 type.MemberCache.missing_abstract = abstract_methods;
775 return type.MemberCache.missing_abstract;
779 // Stage 2: Remove already implemented methods
781 foreach (var type_up in hierarchy) {
782 var members = type_up.MemberCache.member_hash;
783 if (members.Count == 0)
784 continue;
786 for (int i = 0; i < abstract_methods.Count; ++i) {
787 var candidate = abstract_methods [i];
788 if (candidate == null)
789 continue;
791 IList<MemberSpec> applicable;
792 if (!members.TryGetValue (candidate.Name, out applicable))
793 continue;
795 var filter = new MemberFilter (candidate);
796 foreach (var item in applicable) {
797 // TODO: Need to test what should happen for OVERRIDE_UNCHECKED
798 if ((item.Modifiers & (Modifiers.OVERRIDE | Modifiers.OVERRIDE_UNCHECKED | Modifiers.VIRTUAL)) == 0)
799 continue;
801 if (filter.Equals (item)) {
802 --not_implemented_count;
803 abstract_methods [i] = null;
804 break;
810 if (not_implemented_count == abstract_methods.Count) {
811 type.MemberCache.missing_abstract = abstract_methods;
812 return type.MemberCache.missing_abstract;
815 var not_implemented = new MethodSpec[not_implemented_count];
816 int counter = 0;
817 foreach (var m in abstract_methods) {
818 if (m == null)
819 continue;
821 not_implemented[counter++] = m;
824 type.MemberCache.missing_abstract = not_implemented;
825 return type.MemberCache.missing_abstract;
828 static string GetLookupName (MemberSpec ms)
830 if (ms.Kind == MemberKind.Indexer)
831 return IndexerNameAlias;
833 if (ms.Kind == MemberKind.Constructor) {
834 if (ms.IsStatic)
835 return ConstructorInfo.TypeConstructorName;
837 return ConstructorInfo.ConstructorName;
840 return ms.Name;
843 static string GetLookupName (MemberCore mc)
845 if (mc is Indexer)
846 return IndexerNameAlias;
848 if (mc is Constructor)
849 return ConstructorInfo.ConstructorName;
851 return mc.MemberName.Name;
855 // Returns all operators declared on container and its base types
857 public static MethodSpec[] GetUserOperator (TypeSpec container, Operator.OpType op, bool declaredOnly)
859 MethodSpec[] found = null;
861 IList<MemberSpec> applicable;
862 do {
863 var mc = container.MemberCache;
864 if (op == Operator.OpType.Implicit && (mc.state & StateFlags.HasNoImplicitOperator) == 0 ||
865 op == Operator.OpType.Explicit && (mc.state & StateFlags.HasNoExplicitOperator) == 0) {
867 if (mc.member_hash.TryGetValue (Operator.GetMetadataName (op), out applicable)) {
868 int start_index;
869 if (found == null) {
870 start_index = 0;
871 found = new MethodSpec[applicable.Count];
872 } else {
873 start_index = found.Length - 1;
874 Array.Resize (ref found, found.Length + applicable.Count);
877 for (int i = 0; i < applicable.Count; ++i) {
878 if (applicable[i].Kind == MemberKind.Operator)
879 found[i + start_index] = (MethodSpec) applicable[i];
881 } else if (op == Operator.OpType.Implicit) {
882 mc.state |= StateFlags.HasNoImplicitOperator;
883 } else if (op == Operator.OpType.Explicit) {
884 mc.state |= StateFlags.HasNoExplicitOperator;
888 // BaseType call can be expensive
889 if (declaredOnly)
890 break;
892 container = container.BaseType;
893 } while (container != null && container.BaseType != null);
895 return found;
899 // Inflates all member cache nested types
901 public void InflateTypes (MemberCache inflated_cache, TypeParameterInflator inflator)
903 foreach (var item in member_hash) {
904 IList<MemberSpec> inflated_members = null;
905 for (int i = 0; i < item.Value.Count; ++i ) {
906 var member = item.Value[i];
908 // FIXME: When inflating members refering nested types before they are inflated
909 if (member == null)
910 continue;
912 if ((member.Kind & MemberKind.NestedMask) != 0 &&
913 (member.Modifiers & Modifiers.COMPILER_GENERATED) == 0) {
914 if (inflated_members == null) {
915 inflated_members = new MemberSpec[item.Value.Count];
916 inflated_cache.member_hash.Add (item.Key, inflated_members);
919 inflated_members [i] = member.InflateMember (inflator);
926 // Inflates all open type members, requires InflateTypes to be called before
928 public void InflateMembers (MemberCache cacheToInflate, TypeSpec inflatedType, TypeParameterInflator inflator)
930 var inflated_member_hash = cacheToInflate.member_hash;
931 Dictionary<MethodSpec, MethodSpec> accessor_relation = null;
932 List<MemberSpec> accessor_members = null;
934 foreach (var item in member_hash) {
935 var members = item.Value;
936 IList<MemberSpec> inflated_members = null;
937 for (int i = 0; i < members.Count; ++i ) {
938 var member = members[i];
941 // All nested types have been inflated earlier except for
942 // compiler types which are created later and could miss InflateTypes
944 if ((member.Kind & MemberKind.NestedMask) != 0 &&
945 (member.Modifiers & Modifiers.COMPILER_GENERATED) == 0) {
946 if (inflated_members == null)
947 inflated_members = inflated_member_hash[item.Key];
949 continue;
953 // Clone the container first
955 if (inflated_members == null) {
956 inflated_members = new MemberSpec [item.Value.Count];
957 inflated_member_hash.Add (item.Key, inflated_members);
960 var local_inflator = inflator;
962 if (member.DeclaringType != inflatedType) {
964 // Don't inflate non generic interface members
965 // merged into generic interface
967 if (!member.DeclaringType.IsGeneric) {
968 inflated_members [i] = member;
969 continue;
973 // Needed when inflating flatten interfaces. It inflates
974 // container type only, type parameters are already done
976 // Handles cases like:
978 // interface I<T> {}
979 // interface I<U, V> : I<U> {}
981 // class C: I<int, bool> {}
983 var inflated_parent = inflator.Inflate (member.DeclaringType);
984 if (inflated_parent != inflator.TypeInstance)
985 local_inflator = new TypeParameterInflator (inflator, inflated_parent);
989 // Inflate every member, its parent is now different
991 var inflated = member.InflateMember (local_inflator);
992 inflated_members [i] = inflated;
994 if (member is PropertySpec || member is EventSpec) {
995 if (accessor_members == null)
996 accessor_members = new List<MemberSpec> ();
998 accessor_members.Add (inflated);
999 continue;
1002 if (member.IsAccessor) {
1003 if (accessor_relation == null)
1004 accessor_relation = new Dictionary<MethodSpec, MethodSpec> ();
1005 accessor_relation.Add ((MethodSpec) member, (MethodSpec) inflated);
1010 if (accessor_members != null) {
1011 foreach (var member in accessor_members) {
1012 var prop = member as PropertySpec;
1013 if (prop != null) {
1014 if (prop.Get != null)
1015 prop.Get = accessor_relation[prop.Get];
1016 if (prop.Set != null)
1017 prop.Set = accessor_relation[prop.Set];
1019 continue;
1022 var ev = (EventSpec) member;
1023 ev.AccessorAdd = accessor_relation[ev.AccessorAdd];
1024 ev.AccessorRemove = accessor_relation[ev.AccessorRemove];
1030 // For imported class method do additional validation to be sure that metadata
1031 // override flag was correct
1033 static bool IsRealMethodOverride (MethodSpec ms)
1035 IList<MemberSpec> candidates;
1036 var dt = ms.DeclaringType;
1037 while (dt.BaseType != null) {
1038 var base_cache = dt.BaseType.MemberCache;
1039 if (base_cache.member_hash.TryGetValue (ms.Name, out candidates)) {
1040 foreach (var candidate in candidates) {
1041 if (candidate.Kind != ms.Kind)
1042 continue;
1044 if (candidate.Arity != ms.Arity)
1045 continue;
1047 if (!TypeSpecComparer.Override.IsEqual (((MethodSpec) candidate).Parameters, ms.Parameters))
1048 continue;
1050 // Everything matches except modifiers, it's not correct soverride
1051 if ((candidate.Modifiers & Modifiers.AccessibilityMask) != (ms.Modifiers & Modifiers.AccessibilityMask))
1052 return false;
1054 return true;
1058 dt = dt.BaseType;
1061 return false;
1065 // Checks all appropriate container members for CLS compliance
1067 public void VerifyClsCompliance (TypeSpec container, Report report)
1069 if (locase_members != null)
1070 return;
1072 if (container.BaseType == null) {
1073 locase_members = new Dictionary<string, MemberSpec[]> (member_hash.Count); // StringComparer.OrdinalIgnoreCase);
1074 } else {
1075 container.BaseType.MemberCache.VerifyClsCompliance (container.BaseType, report);
1076 locase_members = new Dictionary<string, MemberSpec[]> (container.BaseType.MemberCache.locase_members); //, StringComparer.OrdinalIgnoreCase);
1079 var is_imported_type = container.MemberDefinition.IsImported;
1080 foreach (var entry in container.MemberCache.member_hash) {
1081 for (int i = 0; i < entry.Value.Count; ++i ) {
1082 var name_entry = entry.Value[i];
1083 if ((name_entry.Modifiers & (Modifiers.PUBLIC | Modifiers.PROTECTED)) == 0)
1084 continue;
1086 if ((name_entry.Modifiers & (Modifiers.OVERRIDE | Modifiers.COMPILER_GENERATED)) != 0)
1087 continue;
1089 if ((name_entry.Kind & MemberKind.MaskType) == 0)
1090 continue;
1092 if (name_entry.MemberDefinition.IsNotCLSCompliant ())
1093 continue;
1095 IParametersMember p_a = name_entry as IParametersMember;
1096 if (p_a != null && !name_entry.IsAccessor) {
1097 if (!is_imported_type) {
1098 var p_a_pd = p_a.Parameters;
1099 for (int ii = i + 1; ii < entry.Value.Count; ++ii) {
1100 var checked_entry = entry.Value[ii];
1101 IParametersMember p_b = checked_entry as IParametersMember;
1102 if (p_b == null)
1103 continue;
1105 if (p_a_pd.Count != p_b.Parameters.Count)
1106 continue;
1108 if (checked_entry.IsAccessor)
1109 continue;
1111 var res = ParametersCompiled.IsSameClsSignature (p_a.Parameters, p_b.Parameters);
1112 if (res != 0) {
1113 var last = GetLaterDefinedMember (checked_entry, name_entry);
1114 if (last == checked_entry.MemberDefinition) {
1115 report.SymbolRelatedToPreviousError (name_entry);
1116 } else {
1117 report.SymbolRelatedToPreviousError (checked_entry);
1120 if ((res & 1) != 0) {
1121 report.Warning (3006, 1, last.Location,
1122 "Overloaded method `{0}' differing only in ref or out, or in array rank, is not CLS-compliant",
1123 name_entry.GetSignatureForError ());
1126 if ((res & 2) != 0) {
1127 report.Warning (3007, 1, last.Location,
1128 "Overloaded method `{0}' differing only by unnamed array types is not CLS-compliant",
1129 name_entry.GetSignatureForError ());
1136 if (i > 0 || name_entry.Kind == MemberKind.Constructor || name_entry.Kind == MemberKind.Indexer)
1137 continue;
1139 var name_entry_locase = name_entry.Name.ToLowerInvariant ();
1141 MemberSpec[] found;
1142 if (!locase_members.TryGetValue (name_entry_locase, out found)) {
1143 found = new MemberSpec[] { name_entry };
1144 locase_members.Add (name_entry_locase, found);
1145 } else {
1146 bool same_names_only = true;
1147 foreach (var f in found) {
1148 if (f.Name == name_entry.Name)
1149 continue;
1151 // if (f.IsAccessor && name_entry.IsAccessor)
1152 // continue;
1154 same_names_only = false;
1155 if (!is_imported_type) {
1156 var last = GetLaterDefinedMember (f, name_entry);
1157 if (last == f.MemberDefinition) {
1158 report.SymbolRelatedToPreviousError (name_entry);
1159 } else {
1160 report.SymbolRelatedToPreviousError (f);
1163 report.Warning (3005, 1, last.Location,
1164 "Identifier `{0}' differing only in case is not CLS-compliant", last.GetSignatureForError ());
1168 if (!same_names_only) {
1169 Array.Resize (ref found, found.Length + 1);
1170 found[found.Length - 1] = name_entry;
1171 locase_members[name_entry_locase] = found;
1179 // Local report helper to issue correctly ordered members stored in hashtable
1181 static MemberCore GetLaterDefinedMember (MemberSpec a, MemberSpec b)
1183 var mc_a = a.MemberDefinition as MemberCore;
1184 var mc_b = b.MemberDefinition as MemberCore;
1185 if (mc_a == null)
1186 return mc_b;
1188 if (mc_b == null)
1189 return mc_a;
1191 if (mc_a.Location.File != mc_a.Location.File)
1192 return mc_b;
1194 return mc_b.Location.Row > mc_a.Location.Row ? mc_b : mc_a;
1197 public bool CheckExistingMembersOverloads (MemberCore member, AParametersCollection parameters)
1199 var name = GetLookupName (member);
1200 var imb = member as InterfaceMemberBase;
1201 if (imb != null && imb.IsExplicitImpl) {
1202 name = imb.GetFullName (name);
1205 return CheckExistingMembersOverloads (member, name, parameters);
1208 public bool CheckExistingMembersOverloads (MemberCore member, string name, AParametersCollection parameters)
1210 IList<MemberSpec> entries;
1211 if (!member_hash.TryGetValue (name, out entries))
1212 return false;
1214 var Report = member.Compiler.Report;
1216 int method_param_count = parameters.Count;
1217 for (int i = entries.Count - 1; i >= 0; --i) {
1218 var ce = entries[i];
1219 var pm = ce as IParametersMember;
1220 var pd = pm == null ? ParametersCompiled.EmptyReadOnlyParameters : pm.Parameters;
1221 if (pd.Count != method_param_count)
1222 continue;
1224 if (ce.Arity != member.MemberName.Arity)
1225 continue;
1227 // Ignore merged interface members
1228 if (member.Parent.PartialContainer != ce.DeclaringType.MemberDefinition)
1229 continue;
1231 var p_types = pd.Types;
1232 if (method_param_count > 0) {
1233 int ii = method_param_count - 1;
1234 TypeSpec type_a, type_b;
1235 do {
1236 type_a = parameters.Types [ii];
1237 type_b = p_types [ii];
1239 if ((pd.FixedParameters [ii].ModFlags & Parameter.Modifier.ISBYREF) !=
1240 (parameters.FixedParameters [ii].ModFlags & Parameter.Modifier.ISBYREF))
1241 break;
1243 } while (TypeSpecComparer.Override.IsEqual (type_a, type_b) && ii-- != 0);
1245 if (ii >= 0)
1246 continue;
1249 // Operators can differ in return type only
1251 if (member is Operator && ce.Kind == MemberKind.Operator && ((MethodSpec) ce).ReturnType != ((Operator) member).ReturnType)
1252 continue;
1255 // Report difference in parameter modifiers only
1257 if (pd != null && member is MethodCore) {
1258 ii = method_param_count;
1259 while (ii-- != 0 && parameters.FixedParameters[ii].ModFlags == pd.FixedParameters[ii].ModFlags &&
1260 parameters.ExtensionMethodType == pd.ExtensionMethodType) ;
1262 if (ii >= 0) {
1263 var mc = ce as MethodSpec;
1264 member.Compiler.Report.SymbolRelatedToPreviousError (ce);
1265 if ((member.ModFlags & Modifiers.PARTIAL) != 0 && (mc.Modifiers & Modifiers.PARTIAL) != 0) {
1266 if (parameters.HasParams || pd.HasParams) {
1267 Report.Error (758, member.Location,
1268 "A partial method declaration and partial method implementation cannot differ on use of `params' modifier");
1269 } else {
1270 Report.Error (755, member.Location,
1271 "A partial method declaration and partial method implementation must be both an extension method or neither");
1273 } else if (member is Constructor) {
1274 Report.Error (851, member.Location,
1275 "Overloaded contructor `{0}' cannot differ on use of parameter modifiers only",
1276 member.GetSignatureForError ());
1277 } else {
1278 Report.Error (663, member.Location,
1279 "Overloaded method `{0}' cannot differ on use of parameter modifiers only",
1280 member.GetSignatureForError ());
1282 return false;
1287 if ((ce.Kind & (MemberKind.Method | MemberKind.FakeMethod)) != 0) {
1288 Method method_a = member as Method;
1289 Method method_b = ce.MemberDefinition as Method;
1290 if (method_a != null && method_b != null && (method_a.ModFlags & method_b.ModFlags & Modifiers.PARTIAL) != 0) {
1291 const Modifiers partial_modifiers = Modifiers.STATIC | Modifiers.UNSAFE;
1292 if (method_a.IsPartialDefinition == method_b.IsPartialImplementation) {
1293 if ((method_a.ModFlags & partial_modifiers) == (method_b.ModFlags & partial_modifiers) ||
1294 method_a.Parent.IsUnsafe && method_b.Parent.IsUnsafe) {
1295 if (method_a.IsPartialImplementation) {
1296 method_a.SetPartialDefinition (method_b);
1297 if (entries.Count == 1)
1298 member_hash.Remove (name);
1299 else
1300 entries.RemoveAt (i);
1301 } else {
1302 method_b.SetPartialDefinition (method_a);
1303 method_a.caching_flags |= MemberCore.Flags.PartialDefinitionExists;
1305 continue;
1308 if (method_a.IsStatic != method_b.IsStatic) {
1309 Report.SymbolRelatedToPreviousError (ce);
1310 Report.Error (763, member.Location,
1311 "A partial method declaration and partial method implementation must be both `static' or neither");
1314 Report.SymbolRelatedToPreviousError (ce);
1315 Report.Error (764, member.Location,
1316 "A partial method declaration and partial method implementation must be both `unsafe' or neither");
1317 return false;
1320 Report.SymbolRelatedToPreviousError (ce);
1321 if (method_a.IsPartialDefinition) {
1322 Report.Error (756, member.Location, "A partial method `{0}' declaration is already defined",
1323 member.GetSignatureForError ());
1326 Report.Error (757, member.Location, "A partial method `{0}' implementation is already defined",
1327 member.GetSignatureForError ());
1328 return false;
1331 Report.SymbolRelatedToPreviousError (ce);
1333 bool is_reserved_a = member is AbstractPropertyEventMethod || member is Operator;
1334 bool is_reserved_b = ((MethodSpec) ce).IsReservedMethod;
1336 if (is_reserved_a || is_reserved_b) {
1337 Report.Error (82, member.Location, "A member `{0}' is already reserved",
1338 is_reserved_a ?
1339 ce.GetSignatureForError () :
1340 member.GetSignatureForError ());
1341 return false;
1343 } else {
1344 Report.SymbolRelatedToPreviousError (ce);
1347 if (member is Operator && ce.Kind == MemberKind.Operator) {
1348 Report.Error (557, member.Location, "Duplicate user-defined conversion in type `{0}'",
1349 member.Parent.GetSignatureForError ());
1350 return false;
1353 Report.Error (111, member.Location,
1354 "A member `{0}' is already defined. Rename this member or use different parameter types",
1355 member.GetSignatureForError ());
1356 return false;
1359 return true;