[fix] #631810: Form.DialogResult needs to call its close events *before* closing.
[mono.git] / mcs / mcs / membercache.cs
index d33c9dfbf622292a8cbf5ec9060d540f07828c4e..0dabf85b80f696ca392f8a65fb6d88001c0e8292 100644 (file)
@@ -14,8 +14,6 @@
 using System;
 using System.Text;
 using System.Collections.Generic;
-using System.Globalization;
-using System.Reflection.Emit;
 using System.Reflection;
 using System.Linq;
 
@@ -40,9 +38,9 @@ namespace Mono.CSharp {
                Interface       = 1 << 15,
                TypeParameter = 1 << 16,
 
+               ArrayType = 1 << 19,
                PointerType = 1 << 20,
                InternalCompilerType = 1 << 21,
-               FakeMethod = 1 << 22,
 
                NestedMask = Class | Struct | Delegate | Enum | Interface,
                GenericMask = Method | Class | Struct | Delegate | Interface,
@@ -55,17 +53,13 @@ namespace Mono.CSharp {
        {
                None = 0,
 
-               // Member has to be accessible
-               AccessibleOnly = 1,
-
                // Inspect only queried type members
                DeclaredOnly = 1 << 1,
 
                // Exclude static
                InstanceOnly = 1 << 2,
 
-               // Ignore member overrides
-               NoOverrides     = 1 << 3
+               NoAccessors = 1 << 3
        }
 
        public struct MemberFilter : IEquatable<MemberSpec>
@@ -76,7 +70,6 @@ namespace Mono.CSharp {
                public readonly TypeSpec MemberType;
 
                int arity; // -1 to ignore the check
-               TypeSpec invocation_type;
 
                private MemberFilter (string name, MemberKind kind)
                {
@@ -85,7 +78,6 @@ namespace Mono.CSharp {
                        Parameters = null;
                        MemberType = null;
                        arity = -1;
-                       invocation_type = null;
                }
 
                public MemberFilter (MethodSpec m)
@@ -95,7 +87,6 @@ namespace Mono.CSharp {
                        Parameters = m.Parameters;
                        MemberType = m.ReturnType;
                        arity = m.Arity;
-                       invocation_type = null;
                }
 
                public MemberFilter (string name, int arity, MemberKind kind, AParametersCollection param, TypeSpec type)
@@ -105,16 +96,6 @@ namespace Mono.CSharp {
                        Parameters = param;
                        MemberType = type;
                        this.arity = arity;
-                       invocation_type = null;
-               }
-
-               public TypeSpec InvocationType {
-                       get {
-                               return invocation_type;
-                       }
-                       set {
-                               invocation_type = value;
-                       }
                }
 
                public static MemberFilter Constructor (AParametersCollection param)
@@ -170,40 +151,33 @@ namespace Mono.CSharp {
                                }
                        }
 
-                       if (invocation_type != null && !IsAccessible (other))
-                               return false;
-
                        return true;
                }
 
-               bool IsAccessible (MemberSpec other)
-               {
-                       bool extra;
-                       return Expression.IsMemberAccessible (invocation_type, other, out extra);
-               }
-
                #endregion
        }
 
-       /// <summary>
-       ///   The MemberCache is used by dynamic and non-dynamic types to speed up
-       ///   member lookups.  It has a member name based hash table; it maps each member
-       ///   name to a list of CacheEntry objects.  Each CacheEntry contains a MemberInfo
-       ///   and the BindingFlags that were initially used to get it.  The cache contains
-       ///   all members of the current class and all inherited members.  If this cache is
-       ///   for an interface types, it also contains all inherited members.
-       ///
-       ///   There are two ways to get a MemberCache:
-       ///   * if this is a dynamic type, lookup the corresponding DeclSpace and then
-       ///     use the DeclSpace.MemberCache property.
-       ///   * if this not a dynamic type, call TypeHandle.GetTypeHandle() to get a
-       ///     TypeHandle instance for the type and then use TypeHandle.MemberCache.
-       /// </summary>
+       //
+       // The MemberCache is the main members container used by compiler. It contains
+       // all members imported or defined during compilation using on demand filling
+       // process. Inflated containers are also using MemberCache to make inflated
+       // members look like normal definition.
+       //
+       // All of the methods are performance and memory sensitive as the MemberCache
+       // is the underlying engine of all member based operations.
+       //
        public class MemberCache
        {
+               enum StateFlags
+               {
+                       HasConversionOperator = 1 << 1,
+                       HasUserOperator = 1 << 2
+               }
+
                readonly Dictionary<string, IList<MemberSpec>> member_hash;
                Dictionary<string, MemberSpec[]> locase_members;
                IList<MethodSpec> missing_abstract;
+               StateFlags state;
 
                public static readonly string IndexerNameAlias = "<this>";
 
@@ -222,6 +196,7 @@ namespace Mono.CSharp {
                public MemberCache (MemberCache cache)
                        : this (cache.member_hash.Count)
                {
+                       this.state = cache.state;
                }
 
                //
@@ -255,9 +230,6 @@ namespace Mono.CSharp {
                                }
 
                                foreach (var ce in entry.Value) {
-                                       if (ce.DeclaringType != iface)
-                                               break;
-
                                        if (list.Contains (ce))
                                                continue;
 
@@ -265,6 +237,12 @@ namespace Mono.CSharp {
                                                member_hash[entry.Key] = list;
                                }
                        }
+
+                       // Add also all base interfaces
+                       if (iface.Interfaces != null) {
+                               foreach (var base_iface in iface.Interfaces)
+                                       AddInterface (base_iface);
+                       }
                }
 
                public void AddMember (InterfaceMemberBase imb, string exlicitName, MemberSpec ms)
@@ -287,6 +265,21 @@ namespace Mono.CSharp {
 
                void AddMember (string name, MemberSpec member)
                {
+                       if (member.Kind == MemberKind.Operator) {
+                               var dt = member.DeclaringType;
+                               if (dt == TypeManager.string_type || dt == TypeManager.delegate_type || dt == TypeManager.multicast_delegate_type) {
+                                       // Some core types have user operators but they cannot be used as normal
+                                       // user operators as they are predefined and therefore having different
+                                       // rules (e.g. binary operators) by not setting the flag we hide them for
+                                       // user conversions
+                                       // TODO: Should I do this for all core types ?
+                               } else if (name == Operator.GetMetadataName (Operator.OpType.Implicit) || name == Operator.GetMetadataName (Operator.OpType.Explicit)) {
+                                       state |= StateFlags.HasConversionOperator;
+                               } else {
+                                       state |= StateFlags.HasUserOperator;
+                               }
+                       }
+
                        IList<MemberSpec> list;
                        if (!member_hash.TryGetValue (name, out list)) {
                                member_hash.Add (name, new MemberSpec[] { member });
@@ -332,7 +325,7 @@ namespace Mono.CSharp {
                                                continue;
                                }
 
-                               if (member.DeclaringType.ImplementsInterface (entry.DeclaringType)) {
+                               if (member.DeclaringType.ImplementsInterface (entry.DeclaringType, false)) {
                                        if (existing is MemberSpec[]) {
                                                existing = new MemberSpec[] { member };
                                                return true;
@@ -342,7 +335,8 @@ namespace Mono.CSharp {
                                        continue;
                                }
 
-                               if (entry.DeclaringType == member.DeclaringType || entry.DeclaringType.ImplementsInterface (member.DeclaringType))
+                               if ((entry.DeclaringType == member.DeclaringType && entry.IsAccessor == member.IsAccessor) ||
+                                       entry.DeclaringType.ImplementsInterface (member.DeclaringType, false))
                                        return false;
                        }
 
@@ -355,13 +349,6 @@ namespace Mono.CSharp {
                        return false;
                }
 
-               public static IEnumerable<IndexerSpec> FindIndexers (TypeSpec container, BindingRestriction restrictions)
-               {
-                       var filter = new MemberFilter (IndexerNameAlias, 0, MemberKind.Indexer, null, null);
-                       var found = FindMembers (container, filter, restrictions);
-                       return found == null ? null : found.Cast<IndexerSpec> ();
-               }
-
                public static MemberSpec FindMember (TypeSpec container, MemberFilter filter, BindingRestriction restrictions)
                {
                        do {
@@ -374,103 +361,44 @@ namespace Mono.CSharp {
                                                if ((restrictions & BindingRestriction.InstanceOnly) != 0 && entry.IsStatic)
                                                        continue;
 
-                                               if (filter.Equals (entry))
-                                                       return entry;
+                                               if ((restrictions & BindingRestriction.NoAccessors) != 0 && entry.IsAccessor)
+                                                       continue;
+
+                                               if (!filter.Equals (entry))
+                                                       continue;
 
-                                               // TODO MemberCache:
-                                               //if ((restrictions & BindingRestriction.AccessibleOnly) != 0)
-                                               //      throw new NotImplementedException ("net");
+                                               if ((restrictions & BindingRestriction.DeclaredOnly) != 0 && container.IsInterface && entry.DeclaringType != container)
+                                                       continue;
+
+                                               return entry;
                                        }
                                }
 
+                               if ((restrictions & BindingRestriction.DeclaredOnly) != 0)
+                                       break;
+
                                container = container.BaseType;
-                       } while (container != null && (restrictions & BindingRestriction.DeclaredOnly) == 0);
+                       } while (container != null);
 
                        return null;
                }
 
                //
-               // Returns the first set of members starting from container
+               // A special method to work with member lookup only. It returns a list of all members named @name
+               // starting from @container. It's very performance sensitive
                //
-               public static IList<MemberSpec> FindMembers (TypeSpec container, MemberFilter filter, BindingRestriction restrictions)
+               public static IList<MemberSpec> FindMembers (TypeSpec container, string name, bool declaredOnly)
                {
                        IList<MemberSpec> applicable;
-                       IList<MemberSpec> found = null;
 
                        do {
-                               if (container.MemberCache.member_hash.TryGetValue (filter.Name, out applicable)) {
-                                       for (int i = 0; i < applicable.Count; ++i) {
-                                               var entry = applicable [i];
-
-                                               // Is the member of the correct type
-                                               if ((entry.Kind & filter.Kind & MemberKind.MaskType) == 0)
-                                                       continue;
-
-                                               //
-                                               // When using overloadable overrides filter ignore members which
-                                               // are not base members. Including properties because overrides can
-                                               // implement get or set only and we are looking for complete base member
-                                               //
-                                               const MemberKind overloadable = MemberKind.Indexer | MemberKind.Method | MemberKind.Property;
-                                               if ((restrictions & BindingRestriction.NoOverrides) != 0 && (entry.Kind & overloadable) != 0) {
-                                                       if ((entry.Modifiers & Modifiers.OVERRIDE) != 0)
-                                                               continue;
-
-                                                       if ((entry.Modifiers & Modifiers.OVERRIDE_UNCHECKED) != 0) {
-                                                               bool is_override = true;
-                                                               var ms = entry as MethodSpec;
-                                                               if (ms != null) {
-                                                                       is_override = IsRealMethodOverride (ms);
-                                                               } else {
-                                                                       var ps = (PropertySpec) entry;
-                                                                       if (ps.HasGet)
-                                                                               is_override = IsRealMethodOverride (ps.Get);
-                                                                       if (is_override && ps.HasSet)
-                                                                               is_override = IsRealMethodOverride (ps.Set);
-                                                               }
-
-                                                               if (is_override) {
-                                                                       entry.Modifiers = (entry.Modifiers & ~Modifiers.OVERRIDE_UNCHECKED) | Modifiers.OVERRIDE;
-                                                                       continue;
-                                                               }
-                                                       }
-                                               }
-
-                                               if ((restrictions & BindingRestriction.InstanceOnly) != 0 && entry.IsStatic)
-                                                       continue;
-
-                                               // Apply the filter to it.
-                                               if (!filter.Equals (entry))
-                                                       continue;
-
-                                               if (found == null) {
-                                                       if (i == 0) {
-                                                               found = applicable;
-                                                       } else {
-                                                               found = new List<MemberSpec> ();
-                                                               found.Add (entry);
-                                                       }
-                                               } else if (found == applicable) {
-                                                       found = new List<MemberSpec> ();
-                                                       found.Add (applicable[0]);
-                                                       found.Add (entry);
-                                               } else {
-                                                       found.Add (entry);
-                                               }
-                                       }
-
-                                       if (found != null) {
-                                               if (found == applicable && applicable.Count != 1)
-                                                       return new MemberSpec[] { found[0] };
-
-                                               return found;
-                                       }
-                               }
+                               if (container.MemberCache.member_hash.TryGetValue (name, out applicable) || declaredOnly)
+                                       return applicable;
 
                                container = container.BaseType;
-                       } while (container != null && (restrictions & BindingRestriction.DeclaredOnly) == 0);
+                       } while (container != null);
 
-                       return found;
+                       return null;
                }
 
                //
@@ -527,15 +455,14 @@ namespace Mono.CSharp {
 
                        List<MethodSpec> candidates = null;
                        foreach (var entry in entries) {
-                               if (entry.Kind != MemberKind.Method || (arity >= 0 && entry.Arity != arity))
+                               if (entry.Kind != MemberKind.Method || (arity > 0 && entry.Arity != arity))
                                        continue;
 
                                var ms = (MethodSpec) entry;
                                if (!ms.IsExtensionMethod)
                                        continue;
 
-                               bool extra;
-                               if (!Expression.IsMemberAccessible (invocationType, ms, out extra))
+                               if (!ms.IsAccessible (invocationType))
                                        continue;
 
                                // TODO: CodeGen.Assembly.Builder
@@ -567,6 +494,7 @@ namespace Mono.CSharp {
                        var member_param = member is IParametersMember ? ((IParametersMember) member).Parameters : null;
 
                        var mkind = GetMemberCoreKind (member);
+                       bool member_with_accessors = mkind == MemberKind.Indexer || mkind == MemberKind.Property;
 
                        do {
                                if (container.MemberCache.member_hash.TryGetValue (name, out applicable)) {
@@ -582,10 +510,18 @@ namespace Mono.CSharp {
                                                }
 
                                                //
-                                               // Is the member of the correct type ?
-                                               // Destructors are ignored as they cannot be overridden by user
+                                               // Is the member of same type ?
+                                               //
                                                if ((entry.Kind & ~MemberKind.Destructor & mkind & MemberKind.MaskType) == 0) {
-                                                       if ((entry.Kind & MemberKind.Destructor) == 0 && (member_param == null || !(entry is IParametersMember))) {
+                                                       // Destructors are ignored as they cannot be overridden by user
+                                                       if ((entry.Kind & MemberKind.Destructor) != 0)
+                                                               continue;
+
+                                                       // Only different arity methods hide
+                                                       if (mkind != MemberKind.Method && member.MemberName.Arity != entry.Arity)
+                                                               continue;
+                                                       
+                                                       if ((member_param == null || !(entry is IParametersMember))) {
                                                                bestCandidate = entry;
                                                                return null;
                                                        }
@@ -593,31 +529,55 @@ namespace Mono.CSharp {
                                                        continue;
                                                }
 
-                                               if (member_param == null)
-                                                       return entry;
+                                               if (entry.Kind != mkind) {
+                                                       if (bestCandidate == null)
+                                                               bestCandidate = entry;
 
-                                               // Check arity match
-                                               int arity = member.MemberName.Arity;
-                                               if (arity != entry.Arity)
                                                        continue;
+                                               }
+
+                                               if (member_param != null) {
+                                                       // Check arity match
+                                                       int arity = member.MemberName.Arity;
+                                                       if (arity != entry.Arity)
+                                                               continue;
+
+                                                       var pm = entry as IParametersMember;
+                                                       AParametersCollection entry_parameters;
+                                                       if (pm == null) {
+                                                               if (entry.Kind != MemberKind.Delegate)
+                                                                       continue;
+
+                                                               // TODO: I don't have DelegateSpec
+                                                               entry_parameters = Delegate.GetParameters (member.Compiler, (TypeSpec) entry);
+                                                       } else {
+                                                               entry_parameters = pm.Parameters;
+                                                       }
 
-                                               if (entry is IParametersMember) {
                                                        if (entry.IsAccessor != member is AbstractPropertyEventMethod)
                                                                continue;
 
-                                                       var entry_param = ((IParametersMember) entry).Parameters;
-                                                       if (TypeSpecComparer.Override.IsEqual (entry_param, member_param))
-                                                               return entry;
+                                                       if (!TypeSpecComparer.Override.IsEqual (entry_parameters, member_param))
+                                                               continue;
+                                               }
 
-                                                       continue;
+                                               //
+                                               // Skip override members with accessors they may not fully implement the base member
+                                               //
+                                               if (member_with_accessors) {
+                                                       if ((entry.Modifiers & (Modifiers.OVERRIDE | Modifiers.SEALED)) == Modifiers.OVERRIDE) {
+                                                               //
+                                                               // Set candidate to member override to flag we found an implementation
+                                                               //
+                                                               bestCandidate = entry;
+                                                               continue;
+                                                       }
+                                               } else {
+                                                       bestCandidate = null;
                                                }
 
-                                               if (bestCandidate == null)
-                                                       bestCandidate = entry;
+                                               return entry;
                                        }
-
-                                       if (member_param == null)
-                                               return null;
                                }
 
                                if (container.IsInterface)
@@ -670,6 +630,10 @@ namespace Mono.CSharp {
                                return MemberKind.Interface;
                        if (member is EventProperty)
                                return MemberKind.Event;
+                       if (member is Delegate)
+                               return MemberKind.Delegate;
+                       if (member is Enum)
+                               return MemberKind.Enum;
 
                        throw new NotImplementedException (member.GetType ().ToString ());
                }
@@ -682,11 +646,10 @@ namespace Mono.CSharp {
                                        if (name_entry.IsAccessor)
                                                continue;
 
-                                       if ((name_entry.Kind & (MemberKind.Constructor | MemberKind.FakeMethod | MemberKind.Destructor)) != 0)
+                                       if ((name_entry.Kind & (MemberKind.Constructor | MemberKind.Destructor | MemberKind.Operator)) != 0)
                                                continue;
 
-                                       bool extra;
-                                       if (!Expression.IsMemberAccessible (InternalType.FakeInternalType, name_entry, out extra))
+                                       if (!name_entry.IsAccessible (InternalType.FakeInternalType))
                                                continue;
 
                                        if (name == null || name_entry.Name.StartsWith (name)) {
@@ -701,7 +664,7 @@ namespace Mono.CSharp {
                //
                // Returns members of @iface only, base members are ignored
                //
-               public static IList<MethodSpec> GetInterfaceMembers (TypeSpec iface)
+               public static IList<MethodSpec> GetInterfaceMethods (TypeSpec iface)
                {
                        //
                        // MemberCache flatten interfaces, therefore in cases like this one
@@ -725,6 +688,10 @@ namespace Mono.CSharp {
                        return methods;
                }
 
+               //
+               // Returns all not implememted abstract members inside abstract type
+               // NOTE: Returned list is shared and must not be modified
+               //
                public static IList<MethodSpec> GetNotImplementedAbstractMethods (TypeSpec type)
                {
                        if (type.MemberCache.missing_abstract != null)
@@ -786,8 +753,7 @@ namespace Mono.CSharp {
 
                                        var filter = new MemberFilter (candidate);
                                        foreach (var item in applicable) {
-                                               // TODO: Need to test what should happen for OVERRIDE_UNCHECKED
-                                               if ((item.Modifiers & (Modifiers.OVERRIDE | Modifiers.OVERRIDE_UNCHECKED | Modifiers.VIRTUAL)) == 0)
+                                               if ((item.Modifiers & (Modifiers.OVERRIDE | Modifiers.VIRTUAL)) == 0)
                                                        continue;
 
                                                if (filter.Equals (item)) {
@@ -843,6 +809,70 @@ namespace Mono.CSharp {
                        return mc.MemberName.Name;
                }
 
+               //
+               // Returns all operators declared on container and its base types (until declaredOnly is used)
+               //
+               public static IList<MemberSpec> GetUserOperator (TypeSpec container, Operator.OpType op, bool declaredOnly)
+               {
+                       IList<MemberSpec> found = null;
+
+                       IList<MemberSpec> applicable;
+                       do {
+                               var mc = container.MemberCache;
+
+                               if (((op == Operator.OpType.Implicit || op == Operator.OpType.Explicit) && (mc.state & StateFlags.HasConversionOperator) != 0) ||
+                                        (mc.state & StateFlags.HasUserOperator) != 0) {
+
+                                       if (mc.member_hash.TryGetValue (Operator.GetMetadataName (op), out applicable)) {
+                                               int match_count = 0;
+                                               for (int i = 0; i < applicable.Count; ++i) {
+                                                       if (applicable[i].Kind == MemberKind.Operator) {
+                                                               ++match_count;
+                                                               continue;
+                                                       }
+
+                                                       // Handles very rare case where a method exists with same name as operator (op_xxxx)
+                                                       if (found == null) {
+                                                               found = new List<MemberSpec> ();
+                                                               found.Add (applicable [i]);
+                                                       } else {
+                                                               var prev = found as List<MemberSpec>;
+                                                               if (prev == null) {
+                                                                       prev = new List<MemberSpec> (found.Count + 1);
+                                                                       prev.AddRange (found);
+                                                               }
+
+                                                               prev.Add (applicable[i]);
+                                                       }
+                                               }
+
+                                               if (match_count > 0 && match_count == applicable.Count) {
+                                                       if (found == null) {
+                                                               found = applicable;
+                                                       } else {
+                                                               var merged = found as List<MemberSpec>;
+                                                               if (merged == null) {
+                                                                       merged = new List<MemberSpec> (found.Count + applicable.Count);
+                                                                       merged.AddRange (found);
+                                                                       found = merged;
+                                                               }
+
+                                                               merged.AddRange (applicable);
+                                                       }
+                                               }
+                                       }
+                               }
+
+                               // BaseType call can be expensive
+                               if (declaredOnly)
+                                       break;
+
+                               container = container.BaseType;
+                       } while (container != null);
+
+                       return found;
+               }
+
                //
                // Inflates all member cache nested types
                //
@@ -879,6 +909,9 @@ namespace Mono.CSharp {
                        Dictionary<MethodSpec, MethodSpec> accessor_relation = null;
                        List<MemberSpec> accessor_members = null;
 
+                       // Copy member specific flags when all members were added
+                       cacheToInflate.state = state;
+
                        foreach (var item in member_hash) {
                                var members = item.Value;
                                IList<MemberSpec> inflated_members = null;
@@ -974,41 +1007,6 @@ namespace Mono.CSharp {
                        }
                }
 
-               //
-               // For imported class method do additional validation to be sure that metadata
-               // override flag was correct
-               //
-               static bool IsRealMethodOverride (MethodSpec ms)
-               {
-                       IList<MemberSpec> candidates;
-                       var dt = ms.DeclaringType;
-                       while (dt.BaseType != null) {
-                               var base_cache = dt.BaseType.MemberCache;
-                               if (base_cache.member_hash.TryGetValue (ms.Name, out candidates)) {
-                                       foreach (var candidate in candidates) {
-                                               if (candidate.Kind != ms.Kind)
-                                                       continue;
-
-                                               if (candidate.Arity != ms.Arity)
-                                                       continue;
-
-                                               if (!TypeSpecComparer.Override.IsEqual (((MethodSpec) candidate).Parameters, ms.Parameters))
-                                                       continue;
-
-                                               // Everything matches except modifiers, it's not correct soverride
-                                               if ((candidate.Modifiers & Modifiers.AccessibilityMask) != (ms.Modifiers & Modifiers.AccessibilityMask))
-                                                       return false;
-
-                                               return true;
-                                       }
-                               }
-
-                               dt = dt.BaseType;
-                       }
-
-                       return false;
-               }
-
                //
                // Checks all appropriate container members for CLS compliance
                //
@@ -1232,7 +1230,7 @@ namespace Mono.CSharp {
                                        }
                                }
 
-                               if ((ce.Kind & (MemberKind.Method | MemberKind.FakeMethod)) != 0) {
+                               if ((ce.Kind & MemberKind.Method) != 0) {
                                        Method method_a = member as Method;
                                        Method method_b = ce.MemberDefinition as Method;
                                        if (method_a != null && method_b != null && (method_a.ModFlags & method_b.ModFlags & Modifiers.PARTIAL) != 0) {