📄 decl.cs
字号:
/// <summary> /// The `MemberTypes' enumeration type is a [Flags] type which means that it may /// denote multiple member types. Returns true if the given flags value denotes a /// single member types. /// </summary> public static bool IsSingleMemberType (MemberTypes mt) { switch (mt) { case MemberTypes.Constructor: case MemberTypes.Event: case MemberTypes.Field: case MemberTypes.Method: case MemberTypes.Property: case MemberTypes.NestedType: return true; default: return false; } } /// <summary> /// We encode the MemberTypes and BindingFlags of each members in a "magic" /// number to speed up the searching process. /// </summary> [Flags] protected enum EntryType { None = 0x000, Instance = 0x001, Static = 0x002, MaskStatic = Instance|Static, Public = 0x004, NonPublic = 0x008, MaskProtection = Public|NonPublic, Declared = 0x010, Constructor = 0x020, Event = 0x040, Field = 0x080, Method = 0x100, Property = 0x200, NestedType = 0x400, MaskType = Constructor|Event|Field|Method|Property|NestedType } protected class CacheEntry { public readonly IMemberContainer Container; public readonly EntryType EntryType; public readonly MemberInfo Member; public CacheEntry (IMemberContainer container, MemberInfo member, MemberTypes mt, BindingFlags bf) { this.Container = container; this.Member = member; this.EntryType = GetEntryType (mt, bf); } public override string ToString () { return String.Format ("CacheEntry ({0}:{1}:{2})", Container.Name, EntryType, Member); } } /// <summary> /// This is called each time we're walking up one level in the class hierarchy /// and checks whether we can abort the search since we've already found what /// we were looking for. /// </summary> protected bool DoneSearching (ArrayList list) { // // We've found exactly one member in the current class and it's not // a method or constructor. // if (list.Count == 1 && !(list [0] is MethodBase)) return true; // // Multiple properties: we query those just to find out the indexer // name // if ((list.Count > 0) && (list [0] is PropertyInfo)) return true; return false; } /// <summary> /// Looks up members with name `name'. If you provide an optional /// filter function, it'll only be called with members matching the /// requested member name. /// /// This method will try to use the cache to do the lookup if possible. /// /// Unlike other FindMembers implementations, this method will always /// check all inherited members - even when called on an interface type. /// /// If you know that you're only looking for methods, you should use /// MemberTypes.Method alone since this speeds up the lookup a bit. /// When doing a method-only search, it'll try to use a special method /// cache (unless it's a dynamic type or an interface) and the returned /// MemberInfo's will have the correct ReflectedType for inherited methods. /// The lookup process will automatically restart itself in method-only /// search mode if it discovers that it's about to return methods. /// </summary> ArrayList global = new ArrayList (); bool using_global = false; static MemberInfo [] emptyMemberInfo = new MemberInfo [0]; public MemberInfo [] FindMembers (MemberTypes mt, BindingFlags bf, string name, MemberFilter filter, object criteria) { if (using_global) throw new Exception (); bool declared_only = (bf & BindingFlags.DeclaredOnly) != 0; bool method_search = mt == MemberTypes.Method; // If we have a method cache and we aren't already doing a method-only search, // then we restart a method search if the first match is a method. bool do_method_search = !method_search && (method_hash != null); ArrayList applicable; // If this is a method-only search, we try to use the method cache if // possible; a lookup in the method cache will return a MemberInfo with // the correct ReflectedType for inherited methods. if (method_search && (method_hash != null)) applicable = (ArrayList) method_hash [name]; else applicable = (ArrayList) member_hash [name]; if (applicable == null) return emptyMemberInfo; // // 32 slots gives 53 rss/54 size // 2/4 slots gives 55 rss // // Strange: from 25,000 calls, only 1,800 // are above 2. Why does this impact it? // global.Clear (); using_global = true; Timer.StartTimer (TimerType.CachedLookup); EntryType type = GetEntryType (mt, bf); IMemberContainer current = Container; // `applicable' is a list of all members with the given member name `name' // in the current class and all its base classes. The list is sorted in // reverse order due to the way how the cache is initialy created (to speed // things up, we're doing a deep-copy of our base). for (int i = applicable.Count-1; i >= 0; i--) { CacheEntry entry = (CacheEntry) applicable [i]; // This happens each time we're walking one level up in the class // hierarchy. If we're doing a DeclaredOnly search, we must abort // the first time this happens (this may already happen in the first // iteration of this loop if there are no members with the name we're // looking for in the current class). if (entry.Container != current) { if (declared_only || DoneSearching (global)) break; current = entry.Container; } // Is the member of the correct type ? if ((entry.EntryType & type & EntryType.MaskType) == 0) continue; // Is the member static/non-static ? if ((entry.EntryType & type & EntryType.MaskStatic) == 0) continue; // Apply the filter to it. if (filter (entry.Member, criteria)) { if ((entry.EntryType & EntryType.MaskType) != EntryType.Method) do_method_search = false; global.Add (entry.Member); } } Timer.StopTimer (TimerType.CachedLookup); // If we have a method cache and we aren't already doing a method-only // search, we restart in method-only search mode if the first match is // a method. This ensures that we return a MemberInfo with the correct // ReflectedType for inherited methods. if (do_method_search && (global.Count > 0)){ using_global = false; return FindMembers (MemberTypes.Method, bf, name, filter, criteria); } using_global = false; MemberInfo [] copy = new MemberInfo [global.Count]; global.CopyTo (copy); return copy; } // find the nested type @name in @this. public Type FindNestedType (string name) { ArrayList applicable = (ArrayList) member_hash [name]; if (applicable == null) return null; for (int i = applicable.Count-1; i >= 0; i--) { CacheEntry entry = (CacheEntry) applicable [i]; if ((entry.EntryType & EntryType.NestedType & EntryType.MaskType) != 0) return (Type) entry.Member; } return null; } // // 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; AttributeTester.Result result = AttributeTester.AreOverloadedMethodParamsClsCompliant ( method.ParameterTypes, TypeManager.GetArgumentTypes (method_to_compare)); if (result == AttributeTester.Result.Ok) 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); switch (result) { case AttributeTester.Result.RefOutArrayError: Report.Error (3006, method.Location, "Overloaded method `{0}' differing only in ref or out, or in array rank, is not CLS-compliant", method.GetSignatureForError ()); continue; case AttributeTester.Result.ArrayArrayError: Report.Error (3007, method.Location, "Overloaded method `{0}' differing only by unnamed array types is not CLS-compliant", method.GetSignatureForError ()); continue; } throw new NotImplementedException (result.ToString ()); } } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -