+
+ //
+ // This finds the method or property for us to override. invocationType is the type where
+ // the override is going to be declared, name is the name of the method/property, and
+ // paramTypes is the parameters, if any to the method or property
+ //
+ // Because the MemberCache holds members from this class and all the base classes,
+ // we can avoid tons of reflection stuff.
+ //
+ public MemberInfo FindMemberToOverride (Type invocationType, string name, Type [] paramTypes, bool is_property)
+ {
+ ArrayList applicable;
+ if (method_hash != null && !is_property)
+ applicable = (ArrayList) method_hash [name];
+ else
+ applicable = (ArrayList) member_hash [name];
+
+ if (applicable == null)
+ return null;
+ //
+ // Walk the chain of methods, starting from the top.
+ //
+ for (int i = applicable.Count - 1; i >= 0; i--) {
+ CacheEntry entry = (CacheEntry) applicable [i];
+
+ if ((entry.EntryType & (is_property ? (EntryType.Property | EntryType.Field) : EntryType.Method)) == 0)
+ continue;
+
+ PropertyInfo pi = null;
+ MethodInfo mi = null;
+ FieldInfo fi = null;
+ Type [] cmpAttrs = null;
+
+ if (is_property) {
+ if ((entry.EntryType & EntryType.Field) != 0) {
+ fi = (FieldInfo)entry.Member;
+
+ // TODO: For this case we ignore member type
+ //fb = TypeManager.GetField (fi);
+ //cmpAttrs = new Type[] { fb.MemberType };
+ } else {
+ pi = (PropertyInfo) entry.Member;
+ cmpAttrs = TypeManager.GetArgumentTypes (pi);
+ }
+ } else {
+ mi = (MethodInfo) entry.Member;
+ cmpAttrs = TypeManager.GetArgumentTypes (mi);
+ }
+
+ if (fi != null) {
+ // TODO: Almost duplicate !
+ // Check visibility
+ switch (fi.Attributes & FieldAttributes.FieldAccessMask) {
+ case FieldAttributes.Private:
+ //
+ // A private method is Ok if we are a nested subtype.
+ // The spec actually is not very clear about this, see bug 52458.
+ //
+ if (invocationType != entry.Container.Type &
+ TypeManager.IsNestedChildOf (invocationType, entry.Container.Type))
+ continue;
+
+ break;
+ case FieldAttributes.FamANDAssem:
+ case FieldAttributes.Assembly:
+ //
+ // Check for assembly methods
+ //
+ if (mi.DeclaringType.Assembly != CodeGen.Assembly.Builder)
+ continue;
+ break;
+ }
+ return entry.Member;
+ }
+
+ //
+ // Check the arguments
+ //
+ if (cmpAttrs.Length != paramTypes.Length)
+ continue;
+
+ for (int j = cmpAttrs.Length - 1; j >= 0; j --)
+ if (paramTypes [j] != cmpAttrs [j])
+ goto next;
+
+ //
+ // get one of the methods because this has the visibility info.
+ //
+ if (is_property) {
+ mi = pi.GetGetMethod (true);
+ if (mi == null)
+ mi = pi.GetSetMethod (true);
+ }
+
+ //
+ // Check visibility
+ //
+ switch (mi.Attributes & MethodAttributes.MemberAccessMask) {
+ case MethodAttributes.Private:
+ //
+ // A private method is Ok if we are a nested subtype.
+ // The spec actually is not very clear about this, see bug 52458.
+ //
+ if (invocationType == entry.Container.Type ||
+ TypeManager.IsNestedChildOf (invocationType, entry.Container.Type))
+ return entry.Member;
+
+ break;
+ case MethodAttributes.FamANDAssem:
+ case MethodAttributes.Assembly:
+ //
+ // Check for assembly methods
+ //
+ if (mi.DeclaringType.Assembly == CodeGen.Assembly.Builder)
+ return entry.Member;
+
+ break;
+ default:
+ //
+ // A protected method is ok, because we are overriding.
+ // public is always ok.
+ //
+ return entry.Member;
+ }
+ next:
+ ;
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// The method is looking for conflict with inherited symbols (errors CS0108, CS0109).
+ /// We handle two cases. The first is for types without parameters (events, field, properties).
+ /// The second are methods, indexers and this is why ignore_complex_types is here.
+ /// The latest param is temporary hack. See DoDefineMembers method for more info.
+ /// </summary>
+ public MemberInfo FindMemberWithSameName (string name, bool ignore_complex_types, MemberInfo ignore_member)
+ {
+ ArrayList applicable = null;
+
+ if (method_hash != null)
+ applicable = (ArrayList) method_hash [name];
+
+ if (applicable != null) {
+ for (int i = applicable.Count - 1; i >= 0; i--) {
+ CacheEntry entry = (CacheEntry) applicable [i];
+ if ((entry.EntryType & EntryType.Public) != 0)
+ return entry.Member;
+ }
+ }
+
+ if (member_hash == null)
+ return null;
+ applicable = (ArrayList) member_hash [name];
+
+ if (applicable != null) {
+ for (int i = applicable.Count - 1; i >= 0; i--) {
+ CacheEntry entry = (CacheEntry) applicable [i];
+ if ((entry.EntryType & EntryType.Public) != 0 & entry.Member != ignore_member) {
+ if (ignore_complex_types) {
+ if ((entry.EntryType & EntryType.Method) != 0)
+ continue;
+
+ // Does exist easier way how to detect indexer ?
+ if ((entry.EntryType & EntryType.Property) != 0) {
+ Type[] arg_types = TypeManager.GetArgumentTypes ((PropertyInfo)entry.Member);
+ if (arg_types.Length > 0)
+ continue;
+ }
+ }
+ return entry.Member;
+ }
+ }
+ }
+ return null;
+ }
+
+ Hashtable locase_table;
+
+ /// <summary>
+ /// Builds low-case table for CLS Compliance test
+ /// </summary>
+ public Hashtable GetPublicMembers ()
+ {
+ if (locase_table != null)
+ return locase_table;
+
+ locase_table = new Hashtable ();
+ foreach (DictionaryEntry entry in member_hash) {
+ ArrayList members = (ArrayList)entry.Value;
+ for (int ii = 0; ii < members.Count; ++ii) {
+ CacheEntry member_entry = (CacheEntry) members [ii];
+
+ if ((member_entry.EntryType & EntryType.Public) == 0)
+ continue;
+
+ // TODO: Does anyone know easier way how to detect that member is internal ?
+ switch (member_entry.EntryType & EntryType.MaskType) {
+ case EntryType.Constructor:
+ continue;
+
+ case EntryType.Field:
+ if ((((FieldInfo)member_entry.Member).Attributes & (FieldAttributes.Assembly | FieldAttributes.Public)) == FieldAttributes.Assembly)
+ continue;
+ break;
+
+ case EntryType.Method:
+ if ((((MethodInfo)member_entry.Member).Attributes & (MethodAttributes.Assembly | MethodAttributes.Public)) == MethodAttributes.Assembly)
+ continue;
+ break;
+
+ case EntryType.Property:
+ PropertyInfo pi = (PropertyInfo)member_entry.Member;
+ if (pi.GetSetMethod () == null && pi.GetGetMethod () == null)
+ continue;
+ break;
+
+ case EntryType.Event:
+ EventInfo ei = (EventInfo)member_entry.Member;
+ MethodInfo mi = ei.GetAddMethod ();
+ if ((mi.Attributes & (MethodAttributes.Assembly | MethodAttributes.Public)) == MethodAttributes.Assembly)
+ continue;
+ break;
+ }
+ string lcase = ((string)entry.Key).ToLower (System.Globalization.CultureInfo.InvariantCulture);
+ locase_table [lcase] = member_entry.Member;
+ break;
+ }
+ }
+ return locase_table;
+ }
+
+ public Hashtable Members {
+ get {
+ return member_hash;
+ }
+ }
+
+ /// <summary>
+ /// Cls compliance check whether methods or constructors parameters differing only in ref or out, or in array rank
+ /// </summary>
+ public void VerifyClsParameterConflict (ArrayList al, MethodCore method, MemberInfo this_builder)
+ {
+ EntryType tested_type = (method is Constructor ? EntryType.Constructor : EntryType.Method) | EntryType.Public;
+
+ for (int i = 0; i < al.Count; ++i) {
+ MemberCache.CacheEntry entry = (MemberCache.CacheEntry) al [i];
+
+ // skip itself
+ if (entry.Member == this_builder)
+ continue;
+
+ if ((entry.EntryType & tested_type) != tested_type)
+ continue;
+
+ MethodBase method_to_compare = (MethodBase)entry.Member;
+ if (AttributeTester.AreOverloadedMethodParamsClsCompliant (method.ParameterTypes, TypeManager.GetArgumentTypes (method_to_compare)))
+ continue;
+
+ IMethodData md = TypeManager.GetMethod (method_to_compare);
+
+ // TODO: now we are ignoring CLSCompliance(false) on method from other assembly which is buggy.
+ // However it is exactly what csc does.
+ if (md != null && !md.IsClsCompliaceRequired (method.Parent))
+ continue;
+
+ Report.SymbolRelatedToPreviousError (entry.Member);
+ Report.Error (3006, method.Location, "Overloaded method '{0}' differing only in ref or out, or in array rank, is not CLS-compliant", method.GetSignatureForError ());
+ }
+ }