2 // decl.cs: Declaration base class for structs, classes, enums and interfaces.
4 // Author: Miguel de Icaza (miguel@gnu.org)
5 // Marek Safar (marek.safar@seznam.cz)
7 // Licensed under the terms of the GNU GPL
9 // (C) 2001 Ximian, Inc (http://www.ximian.com)
10 // (C) 2004 Novell, Inc
12 // TODO: Move the method verification stuff from the class.cs and interface.cs here
17 using System
.Collections
;
18 using System
.Globalization
;
19 using System
.Reflection
.Emit
;
20 using System
.Reflection
;
23 namespace Mono
.CSharp
{
25 public class MemberName
{
27 public readonly TypeArguments TypeArguments
;
29 public readonly MemberName Left
;
31 public static readonly MemberName Null
= new MemberName ("");
33 public MemberName (string name
)
38 public MemberName (string name
, TypeArguments args
)
41 this.TypeArguments
= args
;
44 public MemberName (MemberName left
, string name
, TypeArguments args
)
50 public MemberName (MemberName left
, MemberName right
)
51 : this (left
, right
.Name
, right
.TypeArguments
)
55 public string GetName ()
58 return Left
.GetName () + "." + Name
;
63 public bool IsGeneric
{
65 if (TypeArguments
!= null)
67 else if (Left
!= null)
68 return Left
.IsGeneric
;
74 public string GetName (bool is_generic
)
76 string name
= is_generic
? Basename
: Name
;
78 return Left
.GetName (is_generic
) + "." + name
;
83 public int CountTypeArguments
{
85 if (TypeArguments
== null)
88 return TypeArguments
.Count
;
92 public string GetMethodName ()
95 return Left
.GetTypeName () + "." + Name
;
100 public static string MakeName (string name
, TypeArguments args
)
105 return name
+ "`" + args
.Count
;
108 public static string MakeName (string name
, int count
)
110 return name
+ "`" + count
;
113 public string GetTypeName ()
116 return Left
.GetTypeName () + "." +
117 MakeName (Name
, TypeArguments
);
119 return MakeName (Name
, TypeArguments
);
122 protected bool IsUnbound
{
124 if ((Left
!= null) && Left
.IsUnbound
)
126 else if (TypeArguments
== null)
129 return TypeArguments
.IsUnbound
;
133 protected bool CheckUnbound (Location loc
)
135 if ((Left
!= null) && !Left
.CheckUnbound (loc
))
137 if ((TypeArguments
!= null) && !TypeArguments
.IsUnbound
) {
138 Report
.Error (1031, loc
, "Type expected");
145 public Expression
GetTypeExpression (Location loc
)
148 if (!CheckUnbound (loc
))
151 return new UnboundTypeExpression (GetTypeName ());
155 Expression lexpr
= Left
.GetTypeExpression (loc
);
157 return new MemberAccess (lexpr
, Name
, TypeArguments
, loc
);
159 if (TypeArguments
!= null)
160 return new ConstructedType (Name
, TypeArguments
, loc
);
162 return new SimpleName (Name
, loc
);
166 public MemberName
Clone ()
169 return new MemberName (Left
.Clone (), Name
, TypeArguments
);
171 return new MemberName (Name
, TypeArguments
);
174 public string Basename
{
176 if (TypeArguments
!= null)
177 return MakeName (Name
, TypeArguments
);
183 public override string ToString ()
186 if (TypeArguments
!= null)
187 full_name
= Name
+ "<" + TypeArguments
+ ">";
192 return Left
+ "." + full_name
;
199 /// Base representation for members. This is used to keep track
200 /// of Name, Location and Modifier flags, and handling Attributes.
202 public abstract class MemberCore
: Attributable
{
208 return MemberName
.GetName (!(this is GenericMethod
) && !(this is Method
));
212 // Is not readonly because of IndexerName attribute
213 public MemberName MemberName
;
216 /// Modifier flags that the user specified in the source code
220 public /*readonly*/ TypeContainer Parent
;
223 /// Location where this declaration happens
225 public readonly Location Location
;
228 /// XML documentation comment
230 public string DocComment
;
233 /// Represents header string for documentation comment
234 /// for each member types.
236 public abstract string DocCommentHeader { get; }
240 Obsolete_Undetected
= 1, // Obsolete attribute has not been detected yet
241 Obsolete
= 1 << 1, // Type has obsolete attribute
242 ClsCompliance_Undetected
= 1 << 2, // CLS Compliance has not been detected yet
243 ClsCompliant
= 1 << 3, // Type is CLS Compliant
244 CloseTypeCreated
= 1 << 4, // Tracks whether we have Closed the type
245 HasCompliantAttribute_Undetected
= 1 << 5, // Presence of CLSCompliantAttribute has not been detected
246 HasClsCompliantAttribute
= 1 << 6, // Type has CLSCompliantAttribute
247 ClsCompliantAttributeTrue
= 1 << 7, // Type has CLSCompliant (true)
248 Excluded_Undetected
= 1 << 8, // Conditional attribute has not been detected yet
249 Excluded
= 1 << 9, // Method is conditional
250 TestMethodDuplication
= 1 << 10 // Test for duplication must be performed
254 /// MemberCore flags at first detected then cached
256 internal Flags caching_flags
;
258 public MemberCore (TypeContainer parent
, MemberName name
, Attributes attrs
,
265 caching_flags
= Flags
.Obsolete_Undetected
| Flags
.ClsCompliance_Undetected
| Flags
.HasCompliantAttribute_Undetected
| Flags
.Excluded_Undetected
;
269 /// Tests presence of ObsoleteAttribute and report proper error
271 protected void CheckUsageOfObsoleteAttribute (Type type
)
276 ObsoleteAttribute obsolete_attr
= AttributeTester
.GetObsoleteAttribute (type
);
277 if (obsolete_attr
== null)
280 AttributeTester
.Report_ObsoleteMessage (obsolete_attr
, type
.FullName
, Location
);
283 public abstract bool Define ();
286 // Returns full member name for error message
288 public virtual string GetSignatureForError ()
294 /// Use this method when MethodBuilder is null
296 public virtual string GetSignatureForError (TypeContainer tc
)
302 /// Base Emit method. This is also entry point for CLS-Compliant verification.
304 public virtual void Emit ()
306 // Hack with Parent == null is for EnumMember
307 if (Parent
== null || (GetObsoleteAttribute (Parent
) == null && Parent
.GetObsoleteAttribute (Parent
) == null))
308 VerifyObsoleteAttribute ();
310 if (!RootContext
.VerifyClsCompliance
)
313 VerifyClsCompliance (Parent
);
316 public bool InUnsafe
{
318 return ((ModFlags
& Modifiers
.UNSAFE
) != 0) || Parent
.UnsafeContext
;
323 // Whehter is it ok to use an unsafe pointer in this type container
325 public bool UnsafeOK (DeclSpace parent
)
328 // First check if this MemberCore modifier flags has unsafe set
330 if ((ModFlags
& Modifiers
.UNSAFE
) != 0)
333 if (parent
.UnsafeContext
)
336 Expression
.UnsafeError (Location
);
341 /// Returns instance of ObsoleteAttribute for this MemberCore
343 public ObsoleteAttribute
GetObsoleteAttribute (DeclSpace ds
)
345 // ((flags & (Flags.Obsolete_Undetected | Flags.Obsolete)) == 0) is slower, but why ?
346 if ((caching_flags
& Flags
.Obsolete_Undetected
) == 0 && (caching_flags
& Flags
.Obsolete
) == 0) {
350 caching_flags
&= ~Flags
.Obsolete_Undetected
;
352 if (OptAttributes
== null)
355 Attribute obsolete_attr
= OptAttributes
.Search (
356 TypeManager
.obsolete_attribute_type
, ds
.EmitContext
);
357 if (obsolete_attr
== null)
360 ObsoleteAttribute obsolete
= obsolete_attr
.GetObsoleteAttribute (ds
.EmitContext
);
361 if (obsolete
== null)
364 caching_flags
|= Flags
.Obsolete
;
369 /// Analyze whether CLS-Compliant verification must be execute for this MemberCore.
371 public override bool IsClsCompliaceRequired (DeclSpace container
)
373 if ((caching_flags
& Flags
.ClsCompliance_Undetected
) == 0)
374 return (caching_flags
& Flags
.ClsCompliant
) != 0;
376 if (GetClsCompliantAttributeValue (container
) && IsExposedFromAssembly (container
)) {
377 caching_flags
&= ~Flags
.ClsCompliance_Undetected
;
378 caching_flags
|= Flags
.ClsCompliant
;
382 caching_flags
&= ~Flags
.ClsCompliance_Undetected
;
387 /// Returns true when MemberCore is exposed from assembly.
389 public bool IsExposedFromAssembly (DeclSpace ds
)
391 if ((ModFlags
& (Modifiers
.PUBLIC
| Modifiers
.PROTECTED
)) == 0)
394 DeclSpace parentContainer
= ds
;
395 while (parentContainer
!= null && parentContainer
.ModFlags
!= 0) {
396 if ((parentContainer
.ModFlags
& (Modifiers
.PUBLIC
| Modifiers
.PROTECTED
)) == 0)
398 parentContainer
= parentContainer
.Parent
;
404 /// Resolve CLSCompliantAttribute value or gets cached value.
406 bool GetClsCompliantAttributeValue (DeclSpace ds
)
408 if (OptAttributes
!= null) {
409 Attribute cls_attribute
= OptAttributes
.Search (
410 TypeManager
.cls_compliant_attribute_type
, ds
.EmitContext
);
411 if (cls_attribute
!= null) {
412 caching_flags
|= Flags
.HasClsCompliantAttribute
;
413 return cls_attribute
.GetClsCompliantAttributeValue (ds
.EmitContext
);
416 return ds
.GetClsCompliantAttributeValue ();
420 /// Returns true if MemberCore is explicitly marked with CLSCompliantAttribute
422 protected bool HasClsCompliantAttribute
{
424 return (caching_flags
& Flags
.HasClsCompliantAttribute
) != 0;
429 /// It helps to handle error 102 & 111 detection
431 public virtual bool MarkForDuplicationCheck ()
437 /// The main virtual method for CLS-Compliant verifications.
438 /// The method returns true if member is CLS-Compliant and false if member is not
439 /// CLS-Compliant which means that CLS-Compliant tests are not necessary. A descendants override it
440 /// and add their extra verifications.
442 protected virtual bool VerifyClsCompliance (DeclSpace ds
)
444 if (!IsClsCompliaceRequired (ds
)) {
445 if (HasClsCompliantAttribute
&& RootContext
.WarningLevel
>= 2) {
446 if (!IsExposedFromAssembly (ds
))
447 Report
.Warning (3019, Location
, "CLS compliance checking will not be performed on '{0}' because it is private or internal", GetSignatureForError ());
448 if (!CodeGen
.Assembly
.IsClsCompliant
)
449 Report
.Warning (3021, Location
, "'{0}' does not need a CLSCompliant attribute because the assembly does not have a CLSCompliant attribute", GetSignatureForError ());
454 if (!CodeGen
.Assembly
.IsClsCompliant
) {
455 if (HasClsCompliantAttribute
) {
456 Report
.Error (3014, Location
, "'{0}' cannot be marked as CLS-compliant because the assembly does not have a CLSCompliant attribute", GetSignatureForError ());
461 int index
= Name
.LastIndexOf ('.');
462 if (Name
[index
> 0 ? index
+ 1 : 0] == '_') {
463 Report
.Error (3008, Location
, "Identifier '{0}' is not CLS-compliant", GetSignatureForError () );
468 protected abstract void VerifyObsoleteAttribute ();
471 // Raised (and passed an XmlElement that contains the comment)
472 // when GenerateDocComment is writing documentation expectedly.
474 internal virtual void OnGenerateDocComment (DeclSpace ds
, XmlElement intermediateNode
)
479 // Returns a string that represents the signature for this
480 // member which should be used in XML documentation.
482 public virtual string GetDocCommentName (DeclSpace ds
)
484 if (ds
== null || this is DeclSpace
)
485 return DocCommentHeader
+ Name
;
487 return String
.Concat (DocCommentHeader
, ds
.Name
, ".", Name
);
491 // Generates xml doc comments (if any), and if required,
492 // handle warning report.
494 internal virtual void GenerateDocComment (DeclSpace ds
)
496 DocUtil
.GenerateDocComment (this, ds
);
501 /// Base class for structs, classes, enumerations and interfaces.
504 /// They all create new declaration spaces. This
505 /// provides the common foundation for managing those name
508 public abstract class DeclSpace
: MemberCore
, IAlias
{
510 /// This points to the actual definition that is being
511 /// created with System.Reflection.Emit
513 public TypeBuilder TypeBuilder
;
516 /// If we are a generic type, this is the type we are
517 /// currently defining. We need to lookup members on this
518 /// instead of the TypeBuilder.
520 public Type CurrentType
;
523 // This is the namespace in which this typecontainer
524 // was declared. We use this to resolve names.
526 public NamespaceEntry NamespaceEntry
;
528 private Hashtable Cache
= new Hashtable ();
530 public string Basename
;
532 protected Hashtable defined_names
;
534 readonly bool is_generic
;
535 readonly int count_type_params
;
537 // The emit context for toplevel objects.
538 protected EmitContext ec
;
540 public EmitContext EmitContext
{
545 // Whether we are Generic
547 public bool IsGeneric
{
551 else if (Parent
!= null)
552 return Parent
.IsGeneric
;
558 static string[] attribute_targets
= new string [] { "type" }
;
560 public DeclSpace (NamespaceEntry ns
, TypeContainer parent
, MemberName name
,
561 Attributes attrs
, Location l
)
562 : base (parent
, name
, attrs
, l
)
565 Basename
= name
.Name
;
566 defined_names
= new Hashtable ();
567 if (name
.TypeArguments
!= null) {
569 count_type_params
= name
.TypeArguments
.Count
;
572 count_type_params
+= parent
.count_type_params
;
576 /// Adds the member to defined_names table. It tests for duplications and enclosing name conflicts
578 protected bool AddToContainer (MemberCore symbol
, string fullname
, string basename
)
580 if (basename
== Basename
&& !(this is Interface
)) {
581 if (symbol
is TypeParameter
)
582 Report
.Error (694, "Type parameter `{0}' has same name as " +
583 "containing type or method", basename
);
585 Report
.SymbolRelatedToPreviousError (this);
586 Report
.Error (542, "'{0}': member names cannot be the same as their " +
587 "enclosing type", symbol
.Location
, symbol
.GetSignatureForError ());
592 MemberCore mc
= (MemberCore
)defined_names
[fullname
];
595 defined_names
.Add (fullname
, symbol
);
599 if (symbol
.MarkForDuplicationCheck () && mc
.MarkForDuplicationCheck ())
602 if (symbol
is TypeParameter
)
603 Report
.Error (692, symbol
.Location
, "Duplicate type parameter `{0}'", basename
);
605 Report
.SymbolRelatedToPreviousError (mc
);
606 Report
.Error (102, symbol
.Location
,
607 "The type '{0}' already contains a definition for '{1}'",
608 GetSignatureForError (), basename
);
613 public void RecordDecl ()
615 if ((NamespaceEntry
!= null) && (Parent
== RootContext
.Tree
.Types
))
616 NamespaceEntry
.DefineName (MemberName
.Basename
, this);
620 /// Returns the MemberCore associated with a given name in the declaration
621 /// space. It doesn't return method based symbols !!
624 public MemberCore
GetDefinition (string name
)
626 return (MemberCore
)defined_names
[name
];
629 bool in_transit
= false;
632 /// This function is used to catch recursive definitions
635 public bool InTransit
{
646 // root_types contains all the types. All TopLevel types
647 // hence have a parent that points to `root_types', that is
648 // why there is a non-obvious test down here.
650 public bool IsTopLevel
{
653 if (Parent
.Parent
== null)
660 public virtual void CloseType ()
662 if ((caching_flags
& Flags
.CloseTypeCreated
) == 0){
664 TypeBuilder
.CreateType ();
667 // The try/catch is needed because
668 // nested enumerations fail to load when they
671 // Even if this is the right order (enumerations
672 // declared after types).
674 // Note that this still creates the type and
675 // it is possible to save it
677 caching_flags
|= Flags
.CloseTypeCreated
;
682 /// Should be overriten by the appropriate declaration space
684 public abstract TypeBuilder
DefineType ();
687 /// Define all members, but don't apply any attributes or do anything which may
688 /// access not-yet-defined classes. This method also creates the MemberCache.
690 public abstract bool DefineMembers (TypeContainer parent
);
693 // Whether this is an `unsafe context'
695 public bool UnsafeContext
{
697 if ((ModFlags
& Modifiers
.UNSAFE
) != 0)
700 return Parent
.UnsafeContext
;
705 public static string MakeFQN (string nsn
, string name
)
709 return String
.Concat (nsn
, ".", name
);
712 EmitContext type_resolve_ec
;
714 public FullNamedExpression
ResolveNestedType (FullNamedExpression t
, Location loc
)
716 TypeContainer tc
= TypeManager
.LookupTypeContainer (t
.Type
);
717 if ((tc
!= null) && tc
.IsGeneric
) {
719 int tnum
= TypeManager
.GetNumberOfTypeArguments (t
.Type
);
720 Report
.Error (305, loc
,
721 "Using the generic type `{0}' " +
722 "requires {1} type arguments",
723 TypeManager
.GetFullName (t
.Type
), tnum
);
727 TypeParameter
[] args
;
728 if (this is GenericMethod
)
729 args
= Parent
.TypeParameters
;
731 args
= TypeParameters
;
733 TypeExpr ctype
= new ConstructedType (t
.Type
, args
, loc
);
734 return ctype
.ResolveAsTypeTerminal (ec
);
741 // Resolves the expression `e' for a type, and will recursively define
742 // types. This should only be used for resolving base types.
744 public TypeExpr
ResolveBaseTypeExpr (Expression e
, bool silent
, Location loc
)
746 if (type_resolve_ec
== null) {
747 // FIXME: I think this should really be one of:
749 // a. type_resolve_ec = Parent.EmitContext;
750 // b. type_resolve_ec = new EmitContext (Parent, Parent, loc, null, null, ModFlags, false);
752 // However, if Parent == RootContext.Tree.Types, its NamespaceEntry will be null.
754 type_resolve_ec
= new EmitContext (Parent
, this, loc
, null, null, ModFlags
, false);
755 type_resolve_ec
.ResolvingTypeTree
= true;
757 type_resolve_ec
.loc
= loc
;
758 if (this is GenericMethod
)
759 type_resolve_ec
.ContainerType
= Parent
.TypeBuilder
;
761 type_resolve_ec
.ContainerType
= TypeBuilder
;
763 return e
.ResolveAsTypeTerminal (type_resolve_ec
);
766 public bool CheckAccessLevel (Type check_type
)
769 if ((this is GenericMethod
) || (this is Iterator
))
770 tb
= Parent
.TypeBuilder
;
774 if (check_type
.IsGenericInstance
)
775 check_type
= check_type
.GetGenericTypeDefinition ();
777 if (check_type
== tb
)
780 if (TypeBuilder
== null)
781 // FIXME: TypeBuilder will be null when invoked by Class.GetNormalBases().
782 // However, this is invoked again later -- so safe to return true.
783 // May also be null when resolving top-level attributes.
786 if (check_type
.IsGenericParameter
)
787 return true; // FIXME
789 TypeAttributes check_attr
= check_type
.Attributes
& TypeAttributes
.VisibilityMask
;
792 // Broken Microsoft runtime, return public for arrays, no matter what
793 // the accessibility is for their underlying class, and they return
794 // NonPublic visibility for pointers
796 if (check_type
.IsArray
|| check_type
.IsPointer
)
797 return CheckAccessLevel (TypeManager
.GetElementType (check_type
));
800 case TypeAttributes
.Public
:
803 case TypeAttributes
.NotPublic
:
805 if (TypeBuilder
== null)
806 // FIXME: TypeBuilder will be null when invoked by Class.GetNormalBases().
807 // However, this is invoked again later -- so safe to return true.
808 // May also be null when resolving top-level attributes.
811 // This test should probably use the declaringtype.
813 return check_type
.Assembly
== TypeBuilder
.Assembly
;
815 case TypeAttributes
.NestedPublic
:
818 case TypeAttributes
.NestedPrivate
:
819 return NestedAccessible (tb
, check_type
);
821 case TypeAttributes
.NestedFamily
:
823 // Only accessible to methods in current type or any subtypes
825 return FamilyAccessible (tb
, check_type
);
827 case TypeAttributes
.NestedFamANDAssem
:
828 return (check_type
.Assembly
== tb
.Assembly
) &&
829 FamilyAccessible (tb
, check_type
);
831 case TypeAttributes
.NestedFamORAssem
:
832 return (check_type
.Assembly
== tb
.Assembly
) ||
833 FamilyAccessible (tb
, check_type
);
835 case TypeAttributes
.NestedAssembly
:
836 return check_type
.Assembly
== tb
.Assembly
;
839 Console
.WriteLine ("HERE: " + check_attr
);
844 protected bool NestedAccessible (Type tb
, Type check_type
)
846 string check_type_name
= check_type
.FullName
;
848 // At this point, we already know check_type is a nested class.
849 int cio
= check_type_name
.LastIndexOf ('+');
851 // Ensure that the string 'container' has a '+' in it to avoid false matches
852 string container
= check_type_name
.Substring (0, cio
+ 1);
854 // Ensure that type_name ends with a '+' so that it can match 'container', if necessary
855 string type_name
= tb
.FullName
+ "+";
857 // If the current class is nested inside the container of check_type,
858 // we can access check_type even if it is private or protected.
859 return type_name
.StartsWith (container
);
862 protected bool FamilyAccessible (Type tb
, Type check_type
)
864 Type declaring
= check_type
.DeclaringType
;
865 if (tb
== declaring
|| TypeManager
.IsFamilyAccessible (tb
, declaring
))
868 return NestedAccessible (tb
, check_type
);
871 // Access level of a type.
873 enum AccessLevel
{ // Each column represents `is this scope larger or equal to Blah scope'
874 // Public Assembly Protected
875 Protected
= (0 << 0) | (0 << 1) | (X
<< 2),
876 Public
= (X
<< 0) | (X
<< 1) | (X
<< 2),
877 Private
= (0 << 0) | (0 << 1) | (0 << 2),
878 Internal
= (0 << 0) | (X
<< 1) | (0 << 2),
879 ProtectedOrInternal
= (0 << 0) | (X
<< 1) | (X
<< 2),
882 static AccessLevel
GetAccessLevelFromModifiers (int flags
)
884 if ((flags
& Modifiers
.INTERNAL
) != 0) {
886 if ((flags
& Modifiers
.PROTECTED
) != 0)
887 return AccessLevel
.ProtectedOrInternal
;
889 return AccessLevel
.Internal
;
891 } else if ((flags
& Modifiers
.PROTECTED
) != 0)
892 return AccessLevel
.Protected
;
893 else if ((flags
& Modifiers
.PRIVATE
) != 0)
894 return AccessLevel
.Private
;
896 return AccessLevel
.Public
;
899 // What is the effective access level of this?
901 AccessLevel EffectiveAccessLevel
{
903 AccessLevel myAccess
= GetAccessLevelFromModifiers (ModFlags
);
904 if (!IsTopLevel
&& (Parent
!= null))
905 return myAccess
& Parent
.EffectiveAccessLevel
;
910 // Return the access level for type `t'
911 static AccessLevel
TypeEffectiveAccessLevel (Type t
)
914 return AccessLevel
.Public
;
915 if (t
.IsNestedPrivate
)
916 return AccessLevel
.Private
;
918 return AccessLevel
.Internal
;
920 // By now, it must be nested
921 AccessLevel parentLevel
= TypeEffectiveAccessLevel (t
.DeclaringType
);
923 if (t
.IsNestedPublic
)
925 if (t
.IsNestedAssembly
)
926 return parentLevel
& AccessLevel
.Internal
;
927 if (t
.IsNestedFamily
)
928 return parentLevel
& AccessLevel
.Protected
;
929 if (t
.IsNestedFamORAssem
)
930 return parentLevel
& AccessLevel
.ProtectedOrInternal
;
931 if (t
.IsNestedFamANDAssem
)
932 throw new NotImplementedException ("NestedFamANDAssem not implemented, cant make this kind of type from c# anyways");
934 // nested private is taken care of
936 throw new Exception ("I give up, what are you?");
940 // This answers `is the type P, as accessible as a member M which has the
941 // accessability @flags which is declared as a nested member of the type T, this declspace'
943 public bool AsAccessible (Type p
, int flags
)
945 if (p
.IsGenericParameter
)
946 return true; // FIXME
949 // 1) if M is private, its accessability is the same as this declspace.
950 // we already know that P is accessible to T before this method, so we
954 if ((flags
& Modifiers
.PRIVATE
) != 0)
957 while (p
.IsArray
|| p
.IsPointer
|| p
.IsByRef
)
958 p
= TypeManager
.GetElementType (p
);
960 AccessLevel pAccess
= TypeEffectiveAccessLevel (p
);
961 AccessLevel mAccess
= this.EffectiveAccessLevel
&
962 GetAccessLevelFromModifiers (flags
);
964 // for every place from which we can access M, we must
965 // be able to access P as well. So, we want
966 // For every bit in M and P, M_i -> P_1 == true
967 // or, ~ (M -> P) == 0 <-> ~ ( ~M | P) == 0
969 return ~
(~ mAccess
| pAccess
) == 0;
972 static DoubleHash dh
= new DoubleHash (1000);
974 Type
DefineTypeAndParents (DeclSpace tc
)
976 DeclSpace container
= tc
.Parent
;
978 if (container
.TypeBuilder
== null && container
.Name
!= "")
979 DefineTypeAndParents (container
);
981 return tc
.DefineType ();
984 FullNamedExpression
LookupInterfaceOrClass (string ns
, string name
, out bool error
)
987 FullNamedExpression result
;
993 if (dh
.Lookup (ns
, name
, out r
))
994 return (FullNamedExpression
) r
;
997 if (Namespace
.IsNamespace (ns
)){
998 string fullname
= (ns
!= "") ? ns
+ "." + name
: name
;
999 t
= TypeManager
.LookupType (fullname
);
1003 t
= TypeManager
.LookupType (name
);
1007 result
= new TypeExpression (t
, Location
.Null
);
1008 dh
.Insert (ns
, name
, result
);
1012 if (ns
!= "" && Namespace
.IsNamespace (ns
)) {
1013 result
= Namespace
.LookupNamespace (ns
, false).Lookup (this, name
, Location
.Null
);
1014 if (result
!= null) {
1015 dh
.Insert (ns
, name
, result
);
1020 if (ns
== "" && Namespace
.IsNamespace (name
)) {
1021 result
= Namespace
.LookupNamespace (name
, false);
1022 dh
.Insert (ns
, name
, result
);
1027 // In case we are fed a composite name, normalize it.
1029 int p
= name
.LastIndexOf ('.');
1031 ns
= MakeFQN (ns
, name
.Substring (0, p
));
1032 name
= name
.Substring (p
+1);
1035 if (ns
.IndexOf ('+') != -1)
1036 ns
= ns
.Replace ('+', '.');
1038 parent
= RootContext
.Tree
.LookupByNamespace (ns
, name
);
1039 if (parent
== null) {
1040 dh
.Insert (ns
, name
, null);
1044 t
= DefineTypeAndParents (parent
);
1050 result
= new TypeExpression (t
, Location
.Null
);
1051 dh
.Insert (ns
, name
, result
);
1055 public static void Error_AmbiguousTypeReference (Location loc
, string name
, string t1
, string t2
)
1057 Report
.Error (104, loc
,
1058 "`{0}' is an ambiguous reference ({1} or {2})",
1062 public Type
FindNestedType (Location loc
, string name
,
1063 out DeclSpace containing_ds
)
1065 FullNamedExpression t
;
1068 containing_ds
= this;
1069 while (containing_ds
!= null){
1070 Type container_type
= containing_ds
.TypeBuilder
;
1071 Type current_type
= container_type
;
1073 while (current_type
!= null && current_type
!= TypeManager
.object_type
) {
1074 string pre
= current_type
.FullName
;
1076 t
= LookupInterfaceOrClass (pre
, name
, out error
);
1080 if ((t
!= null) && containing_ds
.CheckAccessLevel (t
.Type
))
1083 current_type
= current_type
.BaseType
;
1085 containing_ds
= containing_ds
.Parent
;
1092 /// GetType is used to resolve type names at the DeclSpace level.
1093 /// Use this to lookup class/struct bases, interface bases or
1094 /// delegate type references
1098 /// Contrast this to LookupType which is used inside method bodies to
1099 /// lookup types that have already been defined. GetType is used
1100 /// during the tree resolution process and potentially define
1101 /// recursively the type
1103 public FullNamedExpression
FindType (Location loc
, string name
)
1105 FullNamedExpression t
;
1109 // For the case the type we are looking for is nested within this one
1110 // or is in any base class
1113 DeclSpace containing_ds
= this;
1115 while (containing_ds
!= null){
1116 Type container_type
= containing_ds
.TypeBuilder
;
1117 Type current_type
= container_type
;
1119 while (current_type
!= null && current_type
!= TypeManager
.object_type
) {
1120 string pre
= current_type
.FullName
;
1122 t
= LookupInterfaceOrClass (pre
, name
, out error
);
1126 if ((t
!= null) && containing_ds
.CheckAccessLevel (t
.Type
))
1127 return ResolveNestedType (t
, loc
);
1129 current_type
= current_type
.BaseType
;
1131 containing_ds
= containing_ds
.Parent
;
1135 // Attempt to lookup the class on our namespace and all it's implicit parents
1137 for (NamespaceEntry ns
= NamespaceEntry
; ns
!= null; ns
= ns
.ImplicitParent
) {
1138 t
= LookupInterfaceOrClass (ns
.FullName
, name
, out error
);
1147 // Attempt to do a direct unqualified lookup
1149 t
= LookupInterfaceOrClass ("", name
, out error
);
1157 // Attempt to lookup the class on any of the `using'
1161 for (NamespaceEntry ns
= NamespaceEntry
; ns
!= null; ns
= ns
.Parent
){
1163 t
= LookupInterfaceOrClass (ns
.FullName
, name
, out error
);
1170 if (name
.IndexOf ('.') > 0)
1173 t
= ns
.LookupAlias (name
);
1178 // Now check the using clause list
1180 FullNamedExpression match
= null;
1181 foreach (Namespace using_ns
in ns
.GetUsingTable ()) {
1182 match
= LookupInterfaceOrClass (using_ns
.Name
, name
, out error
);
1186 if ((match
!= null) && (match
is TypeExpr
)) {
1187 Type matched
= ((TypeExpr
) match
).Type
;
1188 if (!CheckAccessLevel (matched
))
1191 Error_AmbiguousTypeReference (loc
, name
, t
.FullName
, match
.FullName
);
1201 //Report.Error (246, Location, "Can not find type `"+name+"'");
1206 // Public function used to locate types, this can only
1207 // be used after the ResolveTree function has been invoked.
1209 // Returns: Type or null if they type can not be found.
1211 // Come to think of it, this should be a DeclSpace
1213 public FullNamedExpression
LookupType (string name
, bool silent
, Location loc
)
1215 FullNamedExpression e
;
1217 if (Cache
.Contains (name
)) {
1218 e
= (FullNamedExpression
) Cache
[name
];
1221 // For the case the type we are looking for is nested within this one
1222 // or is in any base class
1224 DeclSpace containing_ds
= this;
1225 while (containing_ds
!= null){
1227 // if the member cache has been created, lets use it.
1228 // the member cache is MUCH faster.
1229 if (containing_ds
.MemberCache
!= null) {
1230 Type t
= containing_ds
.MemberCache
.FindNestedType (name
);
1232 containing_ds
= containing_ds
.Parent
;
1236 e
= new TypeExpression (t
, Location
.Null
);
1237 e
= ResolveNestedType (e
, Location
.Null
);
1242 // no member cache. Do it the hard way -- reflection
1243 Type current_type
= containing_ds
.TypeBuilder
;
1245 while (current_type
!= null &&
1246 current_type
!= TypeManager
.object_type
) {
1250 Type t
= TypeManager
.LookupType (current_type
.FullName
+ "." + name
);
1252 e
= new TypeExpression (t
, Location
.Null
);
1253 e
= ResolveNestedType (e
, Location
.Null
);
1258 current_type
= current_type
.BaseType
;
1261 containing_ds
= containing_ds
.Parent
;
1264 e
= NamespaceEntry
.LookupNamespaceOrType (this, name
, loc
);
1265 if (!silent
|| e
!= null)
1269 if (e
== null && !silent
)
1270 Report
.Error (246, loc
, "Cannot find type `"+name
+"'");
1276 /// This function is broken and not what you're looking for. It should only
1277 /// be used while the type is still being created since it doesn't use the cache
1278 /// and relies on the filter doing the member name check.
1280 public abstract MemberList
FindMembers (MemberTypes mt
, BindingFlags bf
,
1281 MemberFilter filter
, object criteria
);
1284 /// If we have a MemberCache, return it. This property may return null if the
1285 /// class doesn't have a member cache or while it's still being created.
1287 public abstract MemberCache MemberCache
{
1291 public override void ApplyAttributeBuilder (Attribute a
, CustomAttributeBuilder cb
)
1294 TypeBuilder
.SetCustomAttribute (cb
);
1295 } catch (System
.ArgumentException e
) {
1296 Report
.Warning (-21, a
.Location
,
1297 "The CharSet named property on StructLayout\n"+
1298 "\tdoes not work correctly on Microsoft.NET\n"+
1299 "\tYou might want to remove the CharSet declaration\n"+
1300 "\tor compile using the Mono runtime instead of the\n"+
1301 "\tMicrosoft .NET runtime\n"+
1302 "\tThe runtime gave the error: " + e
);
1307 /// Goes through class hierarchy and get value of first CLSCompliantAttribute that found.
1308 /// If no is attribute exists then return assembly CLSCompliantAttribute.
1310 public bool GetClsCompliantAttributeValue ()
1312 if ((caching_flags
& Flags
.HasCompliantAttribute_Undetected
) == 0)
1313 return (caching_flags
& Flags
.ClsCompliantAttributeTrue
) != 0;
1315 caching_flags
&= ~Flags
.HasCompliantAttribute_Undetected
;
1317 if (OptAttributes
!= null) {
1318 Attribute cls_attribute
= OptAttributes
.Search (TypeManager
.cls_compliant_attribute_type
, ec
);
1319 if (cls_attribute
!= null) {
1320 caching_flags
|= Flags
.HasClsCompliantAttribute
;
1321 if (cls_attribute
.GetClsCompliantAttributeValue (ec
)) {
1322 caching_flags
|= Flags
.ClsCompliantAttributeTrue
;
1329 if (Parent
== null) {
1330 if (CodeGen
.Assembly
.IsClsCompliant
) {
1331 caching_flags
|= Flags
.ClsCompliantAttributeTrue
;
1337 if (Parent
.GetClsCompliantAttributeValue ()) {
1338 caching_flags
|= Flags
.ClsCompliantAttributeTrue
;
1345 // Extensions for generics
1347 TypeParameter
[] type_params
;
1348 TypeParameter
[] type_param_list
;
1350 protected string GetInstantiationName ()
1352 StringBuilder sb
= new StringBuilder (Name
);
1354 for (int i
= 0; i
< type_param_list
.Length
; i
++) {
1357 sb
.Append (type_param_list
[i
].Name
);
1360 return sb
.ToString ();
1363 bool check_type_parameter (ArrayList list
, int start
, string name
)
1365 for (int i
= 0; i
< start
; i
++) {
1366 TypeParameter param
= (TypeParameter
) list
[i
];
1368 if (param
.Name
!= name
)
1371 if (RootContext
.WarningLevel
>= 3)
1374 "Type parameter `{0}' has same name " +
1375 "as type parameter from outer type `{1}'",
1376 name
, Parent
.GetInstantiationName ());
1384 TypeParameter
[] initialize_type_params ()
1386 if (type_param_list
!= null)
1387 return type_param_list
;
1389 DeclSpace the_parent
= Parent
;
1390 if (this is GenericMethod
)
1394 TypeParameter
[] parent_params
= null;
1395 if ((the_parent
!= null) && the_parent
.IsGeneric
) {
1396 parent_params
= the_parent
.initialize_type_params ();
1397 start
= parent_params
!= null ? parent_params
.Length
: 0;
1400 ArrayList list
= new ArrayList ();
1401 if (parent_params
!= null)
1402 list
.AddRange (parent_params
);
1404 int count
= type_params
!= null ? type_params
.Length
: 0;
1405 for (int i
= 0; i
< count
; i
++) {
1406 TypeParameter param
= type_params
[i
];
1407 check_type_parameter (list
, start
, param
.Name
);
1411 type_param_list
= new TypeParameter
[list
.Count
];
1412 list
.CopyTo (type_param_list
, 0);
1413 return type_param_list
;
1416 public virtual void SetParameterInfo (ArrayList constraints_list
)
1419 if (constraints_list
!= null) {
1421 80, Location
, "Contraints are not allowed " +
1422 "on non-generic declarations");
1428 string[] names
= MemberName
.TypeArguments
.GetDeclarations ();
1429 type_params
= new TypeParameter
[names
.Length
];
1432 // Register all the names
1434 for (int i
= 0; i
< type_params
.Length
; i
++) {
1435 string name
= names
[i
];
1437 Constraints constraints
= null;
1438 if (constraints_list
!= null) {
1439 foreach (Constraints constraint
in constraints_list
) {
1440 if (constraint
.TypeParameter
== name
) {
1441 constraints
= constraint
;
1447 type_params
[i
] = new TypeParameter (Parent
, name
, constraints
, Location
);
1449 string full_name
= Name
+ "." + name
;
1450 AddToContainer (type_params
[i
], full_name
, name
);
1454 public TypeParameter
[] TypeParameters
{
1457 throw new InvalidOperationException ();
1458 if (type_param_list
== null)
1459 initialize_type_params ();
1461 return type_param_list
;
1465 protected TypeParameter
[] CurrentTypeParameters
{
1468 throw new InvalidOperationException ();
1469 if (type_params
!= null)
1472 return new TypeParameter
[0];
1476 public int CountTypeParameters
{
1478 return count_type_params
;
1482 public TypeParameterExpr
LookupGeneric (string name
, Location loc
)
1487 foreach (TypeParameter type_param
in CurrentTypeParameters
) {
1488 if (type_param
.Name
!= name
)
1491 return new TypeParameterExpr (type_param
, loc
);
1495 return Parent
.LookupGeneric (name
, loc
);
1500 bool IAlias
.IsType
{
1501 get { return true; }
1504 string IAlias
.Name
{
1505 get { return Name; }
1508 TypeExpr IAlias
.ResolveAsType (EmitContext ec
)
1510 if (TypeBuilder
== null)
1511 throw new InvalidOperationException ();
1513 if (CurrentType
!= null)
1514 return new TypeExpression (CurrentType
, Location
);
1516 return new TypeExpression (TypeBuilder
, Location
);
1519 public override string[] ValidAttributeTargets
{
1521 return attribute_targets
;
1527 /// This is a readonly list of MemberInfo's.
1529 public class MemberList
: IList
{
1530 public readonly IList List
;
1534 /// Create a new MemberList from the given IList.
1536 public MemberList (IList list
)
1541 this.List
= new ArrayList ();
1546 /// Concatenate the ILists `first' and `second' to a new MemberList.
1548 public MemberList (IList first
, IList second
)
1550 ArrayList list
= new ArrayList ();
1551 list
.AddRange (first
);
1552 list
.AddRange (second
);
1557 public static readonly MemberList Empty
= new MemberList (new ArrayList ());
1560 /// Cast the MemberList into a MemberInfo[] array.
1563 /// This is an expensive operation, only use it if it's really necessary.
1565 public static explicit operator MemberInfo
[] (MemberList list
)
1567 Timer
.StartTimer (TimerType
.MiscTimer
);
1568 MemberInfo
[] result
= new MemberInfo
[list
.Count
];
1569 list
.CopyTo (result
, 0);
1570 Timer
.StopTimer (TimerType
.MiscTimer
);
1582 public bool IsSynchronized
{
1584 return List
.IsSynchronized
;
1588 public object SyncRoot
{
1590 return List
.SyncRoot
;
1594 public void CopyTo (Array array
, int index
)
1596 List
.CopyTo (array
, index
);
1601 public IEnumerator
GetEnumerator ()
1603 return List
.GetEnumerator ();
1608 public bool IsFixedSize
{
1614 public bool IsReadOnly
{
1620 object IList
.this [int index
] {
1622 return List
[index
];
1626 throw new NotSupportedException ();
1630 // FIXME: try to find out whether we can avoid the cast in this indexer.
1631 public MemberInfo
this [int index
] {
1633 return (MemberInfo
) List
[index
];
1637 public int Add (object value)
1639 throw new NotSupportedException ();
1642 public void Clear ()
1644 throw new NotSupportedException ();
1647 public bool Contains (object value)
1649 return List
.Contains (value);
1652 public int IndexOf (object value)
1654 return List
.IndexOf (value);
1657 public void Insert (int index
, object value)
1659 throw new NotSupportedException ();
1662 public void Remove (object value)
1664 throw new NotSupportedException ();
1667 public void RemoveAt (int index
)
1669 throw new NotSupportedException ();
1674 /// This interface is used to get all members of a class when creating the
1675 /// member cache. It must be implemented by all DeclSpace derivatives which
1676 /// want to support the member cache and by TypeHandle to get caching of
1677 /// non-dynamic types.
1679 public interface IMemberContainer
{
1681 /// The name of the IMemberContainer. This is only used for
1682 /// debugging purposes.
1689 /// The type of this IMemberContainer.
1696 /// Returns the IMemberContainer of the base class or null if this
1697 /// is an interface or TypeManger.object_type.
1698 /// This is used when creating the member cache for a class to get all
1699 /// members from the base class.
1701 MemberCache BaseCache
{
1706 /// Whether this is an interface.
1713 /// Returns all members of this class with the corresponding MemberTypes
1714 /// and BindingFlags.
1717 /// When implementing this method, make sure not to return any inherited
1718 /// members and check the MemberTypes and BindingFlags properly.
1719 /// Unfortunately, System.Reflection is lame and doesn't provide a way to
1720 /// get the BindingFlags (static/non-static,public/non-public) in the
1721 /// MemberInfo class, but the cache needs this information. That's why
1722 /// this method is called multiple times with different BindingFlags.
1724 MemberList
GetMembers (MemberTypes mt
, BindingFlags bf
);
1727 /// Return the container's member cache.
1729 MemberCache MemberCache
{
1735 /// The MemberCache is used by dynamic and non-dynamic types to speed up
1736 /// member lookups. It has a member name based hash table; it maps each member
1737 /// name to a list of CacheEntry objects. Each CacheEntry contains a MemberInfo
1738 /// and the BindingFlags that were initially used to get it. The cache contains
1739 /// all members of the current class and all inherited members. If this cache is
1740 /// for an interface types, it also contains all inherited members.
1742 /// There are two ways to get a MemberCache:
1743 /// * if this is a dynamic type, lookup the corresponding DeclSpace and then
1744 /// use the DeclSpace.MemberCache property.
1745 /// * if this not a dynamic type, call TypeHandle.GetTypeHandle() to get a
1746 /// TypeHandle instance for the type and then use TypeHandle.MemberCache.
1748 public class MemberCache
{
1749 public readonly IMemberContainer Container
;
1750 protected Hashtable member_hash
;
1751 protected Hashtable method_hash
;
1754 /// Create a new MemberCache for the given IMemberContainer `container'.
1756 public MemberCache (IMemberContainer container
)
1758 this.Container
= container
;
1760 Timer
.IncrementCounter (CounterType
.MemberCache
);
1761 Timer
.StartTimer (TimerType
.CacheInit
);
1763 // If we have a base class (we have a base class unless we're
1764 // TypeManager.object_type), we deep-copy its MemberCache here.
1765 if (Container
.BaseCache
!= null)
1766 member_hash
= SetupCache (Container
.BaseCache
);
1768 member_hash
= new Hashtable ();
1770 // If this is neither a dynamic type nor an interface, create a special
1771 // method cache with all declared and inherited methods.
1772 Type type
= container
.Type
;
1773 if (!(type
is TypeBuilder
) && !type
.IsInterface
&& !type
.IsGenericParameter
) {
1774 method_hash
= new Hashtable ();
1778 // Add all members from the current class.
1779 AddMembers (Container
);
1781 Timer
.StopTimer (TimerType
.CacheInit
);
1784 public MemberCache (Type
[] ifaces
)
1787 // The members of this cache all belong to other caches.
1788 // So, 'Container' will not be used.
1790 this.Container
= null;
1792 member_hash
= new Hashtable ();
1796 foreach (Type itype
in ifaces
)
1797 AddCacheContents (TypeManager
.LookupMemberCache (itype
));
1801 /// Bootstrap this member cache by doing a deep-copy of our base.
1803 Hashtable
SetupCache (MemberCache base_class
)
1805 Hashtable hash
= new Hashtable ();
1807 if (base_class
== null)
1810 IDictionaryEnumerator it
= base_class
.member_hash
.GetEnumerator ();
1811 while (it
.MoveNext ()) {
1812 hash
[it
.Key
] = ((ArrayList
) it
.Value
).Clone ();
1819 /// Add the contents of `cache' to the member_hash.
1821 void AddCacheContents (MemberCache cache
)
1823 IDictionaryEnumerator it
= cache
.member_hash
.GetEnumerator ();
1824 while (it
.MoveNext ()) {
1825 ArrayList list
= (ArrayList
) member_hash
[it
.Key
];
1827 member_hash
[it
.Key
] = list
= new ArrayList ();
1829 ArrayList entries
= (ArrayList
) it
.Value
;
1830 for (int i
= entries
.Count
-1; i
>= 0; i
--) {
1831 CacheEntry entry
= (CacheEntry
) entries
[i
];
1833 if (entry
.Container
!= cache
.Container
)
1841 /// Add all members from class `container' to the cache.
1843 void AddMembers (IMemberContainer container
)
1845 // We need to call AddMembers() with a single member type at a time
1846 // to get the member type part of CacheEntry.EntryType right.
1847 if (!container
.IsInterface
) {
1848 AddMembers (MemberTypes
.Constructor
, container
);
1849 AddMembers (MemberTypes
.Field
, container
);
1851 AddMembers (MemberTypes
.Method
, container
);
1852 AddMembers (MemberTypes
.Property
, container
);
1853 AddMembers (MemberTypes
.Event
, container
);
1854 // Nested types are returned by both Static and Instance searches.
1855 AddMembers (MemberTypes
.NestedType
,
1856 BindingFlags
.Static
| BindingFlags
.Public
, container
);
1857 AddMembers (MemberTypes
.NestedType
,
1858 BindingFlags
.Static
| BindingFlags
.NonPublic
, container
);
1861 void AddMembers (MemberTypes mt
, IMemberContainer container
)
1863 AddMembers (mt
, BindingFlags
.Static
| BindingFlags
.Public
, container
);
1864 AddMembers (mt
, BindingFlags
.Static
| BindingFlags
.NonPublic
, container
);
1865 AddMembers (mt
, BindingFlags
.Instance
| BindingFlags
.Public
, container
);
1866 AddMembers (mt
, BindingFlags
.Instance
| BindingFlags
.NonPublic
, container
);
1870 /// Add all members from class `container' with the requested MemberTypes and
1871 /// BindingFlags to the cache. This method is called multiple times with different
1872 /// MemberTypes and BindingFlags.
1874 void AddMembers (MemberTypes mt
, BindingFlags bf
, IMemberContainer container
)
1876 MemberList members
= container
.GetMembers (mt
, bf
);
1878 foreach (MemberInfo member
in members
) {
1879 string name
= member
.Name
;
1881 int pos
= name
.IndexOf ('<');
1883 name
= name
.Substring (0, pos
);
1885 // We use a name-based hash table of ArrayList's.
1886 ArrayList list
= (ArrayList
) member_hash
[name
];
1888 list
= new ArrayList ();
1889 member_hash
.Add (name
, list
);
1892 // When this method is called for the current class, the list will
1893 // already contain all inherited members from our base classes.
1894 // We cannot add new members in front of the list since this'd be an
1895 // expensive operation, that's why the list is sorted in reverse order
1896 // (ie. members from the current class are coming last).
1897 list
.Add (new CacheEntry (container
, member
, mt
, bf
));
1902 /// Add all declared and inherited methods from class `type' to the method cache.
1904 void AddMethods (Type type
)
1906 AddMethods (BindingFlags
.Static
| BindingFlags
.Public
|
1907 BindingFlags
.FlattenHierarchy
, type
);
1908 AddMethods (BindingFlags
.Static
| BindingFlags
.NonPublic
|
1909 BindingFlags
.FlattenHierarchy
, type
);
1910 AddMethods (BindingFlags
.Instance
| BindingFlags
.Public
, type
);
1911 AddMethods (BindingFlags
.Instance
| BindingFlags
.NonPublic
, type
);
1914 void AddMethods (BindingFlags bf
, Type type
)
1916 MemberInfo
[] members
= type
.GetMethods (bf
);
1918 Array
.Reverse (members
);
1920 foreach (MethodBase member
in members
) {
1921 string name
= member
.Name
;
1923 // We use a name-based hash table of ArrayList's.
1924 ArrayList list
= (ArrayList
) method_hash
[name
];
1926 list
= new ArrayList ();
1927 method_hash
.Add (name
, list
);
1930 // Unfortunately, the elements returned by Type.GetMethods() aren't
1931 // sorted so we need to do this check for every member.
1932 BindingFlags new_bf
= bf
;
1933 if (member
.DeclaringType
== type
)
1934 new_bf
|= BindingFlags
.DeclaredOnly
;
1936 list
.Add (new CacheEntry (Container
, member
, MemberTypes
.Method
, new_bf
));
1941 /// Compute and return a appropriate `EntryType' magic number for the given
1942 /// MemberTypes and BindingFlags.
1944 protected static EntryType
GetEntryType (MemberTypes mt
, BindingFlags bf
)
1946 EntryType type
= EntryType
.None
;
1948 if ((mt
& MemberTypes
.Constructor
) != 0)
1949 type
|= EntryType
.Constructor
;
1950 if ((mt
& MemberTypes
.Event
) != 0)
1951 type
|= EntryType
.Event
;
1952 if ((mt
& MemberTypes
.Field
) != 0)
1953 type
|= EntryType
.Field
;
1954 if ((mt
& MemberTypes
.Method
) != 0)
1955 type
|= EntryType
.Method
;
1956 if ((mt
& MemberTypes
.Property
) != 0)
1957 type
|= EntryType
.Property
;
1958 // Nested types are returned by static and instance searches.
1959 if ((mt
& MemberTypes
.NestedType
) != 0)
1960 type
|= EntryType
.NestedType
| EntryType
.Static
| EntryType
.Instance
;
1962 if ((bf
& BindingFlags
.Instance
) != 0)
1963 type
|= EntryType
.Instance
;
1964 if ((bf
& BindingFlags
.Static
) != 0)
1965 type
|= EntryType
.Static
;
1966 if ((bf
& BindingFlags
.Public
) != 0)
1967 type
|= EntryType
.Public
;
1968 if ((bf
& BindingFlags
.NonPublic
) != 0)
1969 type
|= EntryType
.NonPublic
;
1970 if ((bf
& BindingFlags
.DeclaredOnly
) != 0)
1971 type
|= EntryType
.Declared
;
1977 /// The `MemberTypes' enumeration type is a [Flags] type which means that it may
1978 /// denote multiple member types. Returns true if the given flags value denotes a
1979 /// single member types.
1981 public static bool IsSingleMemberType (MemberTypes mt
)
1984 case MemberTypes
.Constructor
:
1985 case MemberTypes
.Event
:
1986 case MemberTypes
.Field
:
1987 case MemberTypes
.Method
:
1988 case MemberTypes
.Property
:
1989 case MemberTypes
.NestedType
:
1998 /// We encode the MemberTypes and BindingFlags of each members in a "magic"
1999 /// number to speed up the searching process.
2002 protected enum EntryType
{
2007 MaskStatic
= Instance
|Static
,
2011 MaskProtection
= Public
|NonPublic
,
2015 Constructor
= 0x020,
2022 MaskType
= Constructor
|Event
|Field
|Method
|Property
|NestedType
2025 protected struct CacheEntry
{
2026 public readonly IMemberContainer Container
;
2027 public readonly EntryType EntryType
;
2028 public readonly MemberInfo Member
;
2030 public CacheEntry (IMemberContainer container
, MemberInfo member
,
2031 MemberTypes mt
, BindingFlags bf
)
2033 this.Container
= container
;
2034 this.Member
= member
;
2035 this.EntryType
= GetEntryType (mt
, bf
);
2038 public override string ToString ()
2040 return String
.Format ("CacheEntry ({0}:{1}:{2})", Container
.Name
,
2046 /// This is called each time we're walking up one level in the class hierarchy
2047 /// and checks whether we can abort the search since we've already found what
2048 /// we were looking for.
2050 protected bool DoneSearching (ArrayList list
)
2053 // We've found exactly one member in the current class and it's not
2054 // a method or constructor.
2056 if (list
.Count
== 1 && !(list
[0] is MethodBase
))
2060 // Multiple properties: we query those just to find out the indexer
2063 if ((list
.Count
> 0) && (list
[0] is PropertyInfo
))
2070 /// Looks up members with name `name'. If you provide an optional
2071 /// filter function, it'll only be called with members matching the
2072 /// requested member name.
2074 /// This method will try to use the cache to do the lookup if possible.
2076 /// Unlike other FindMembers implementations, this method will always
2077 /// check all inherited members - even when called on an interface type.
2079 /// If you know that you're only looking for methods, you should use
2080 /// MemberTypes.Method alone since this speeds up the lookup a bit.
2081 /// When doing a method-only search, it'll try to use a special method
2082 /// cache (unless it's a dynamic type or an interface) and the returned
2083 /// MemberInfo's will have the correct ReflectedType for inherited methods.
2084 /// The lookup process will automatically restart itself in method-only
2085 /// search mode if it discovers that it's about to return methods.
2087 ArrayList
global = new ArrayList ();
2088 bool using_global
= false;
2090 static MemberInfo
[] emptyMemberInfo
= new MemberInfo
[0];
2092 public MemberInfo
[] FindMembers (MemberTypes mt
, BindingFlags bf
, string name
,
2093 MemberFilter filter
, object criteria
)
2096 throw new Exception ();
2098 bool declared_only
= (bf
& BindingFlags
.DeclaredOnly
) != 0;
2099 bool method_search
= mt
== MemberTypes
.Method
;
2100 // If we have a method cache and we aren't already doing a method-only search,
2101 // then we restart a method search if the first match is a method.
2102 bool do_method_search
= !method_search
&& (method_hash
!= null);
2104 ArrayList applicable
;
2106 // If this is a method-only search, we try to use the method cache if
2107 // possible; a lookup in the method cache will return a MemberInfo with
2108 // the correct ReflectedType for inherited methods.
2110 if (method_search
&& (method_hash
!= null))
2111 applicable
= (ArrayList
) method_hash
[name
];
2113 applicable
= (ArrayList
) member_hash
[name
];
2115 if (applicable
== null)
2116 return emptyMemberInfo
;
2119 // 32 slots gives 53 rss/54 size
2120 // 2/4 slots gives 55 rss
2122 // Strange: from 25,000 calls, only 1,800
2123 // are above 2. Why does this impact it?
2126 using_global
= true;
2128 Timer
.StartTimer (TimerType
.CachedLookup
);
2130 EntryType type
= GetEntryType (mt
, bf
);
2132 IMemberContainer current
= Container
;
2135 // `applicable' is a list of all members with the given member name `name'
2136 // in the current class and all its base classes. The list is sorted in
2137 // reverse order due to the way how the cache is initialy created (to speed
2138 // things up, we're doing a deep-copy of our base).
2140 for (int i
= applicable
.Count
-1; i
>= 0; i
--) {
2141 CacheEntry entry
= (CacheEntry
) applicable
[i
];
2143 // This happens each time we're walking one level up in the class
2144 // hierarchy. If we're doing a DeclaredOnly search, we must abort
2145 // the first time this happens (this may already happen in the first
2146 // iteration of this loop if there are no members with the name we're
2147 // looking for in the current class).
2148 if (entry
.Container
!= current
) {
2149 if (declared_only
|| DoneSearching (global))
2152 current
= entry
.Container
;
2155 // Is the member of the correct type ?
2156 if ((entry
.EntryType
& type
& EntryType
.MaskType
) == 0)
2159 // Is the member static/non-static ?
2160 if ((entry
.EntryType
& type
& EntryType
.MaskStatic
) == 0)
2163 // Apply the filter to it.
2164 if (filter (entry
.Member
, criteria
)) {
2165 if ((entry
.EntryType
& EntryType
.MaskType
) != EntryType
.Method
)
2166 do_method_search
= false;
2167 global.Add (entry
.Member
);
2171 Timer
.StopTimer (TimerType
.CachedLookup
);
2173 // If we have a method cache and we aren't already doing a method-only
2174 // search, we restart in method-only search mode if the first match is
2175 // a method. This ensures that we return a MemberInfo with the correct
2176 // ReflectedType for inherited methods.
2177 if (do_method_search
&& (global.Count
> 0)){
2178 using_global
= false;
2180 return FindMembers (MemberTypes
.Method
, bf
, name
, filter
, criteria
);
2183 using_global
= false;
2184 MemberInfo
[] copy
= new MemberInfo
[global.Count
];
2185 global.CopyTo (copy
);
2189 // find the nested type @name in @this.
2190 public Type
FindNestedType (string name
)
2192 ArrayList applicable
= (ArrayList
) member_hash
[name
];
2193 if (applicable
== null)
2196 for (int i
= applicable
.Count
-1; i
>= 0; i
--) {
2197 CacheEntry entry
= (CacheEntry
) applicable
[i
];
2198 if ((entry
.EntryType
& EntryType
.NestedType
& EntryType
.MaskType
) != 0)
2199 return (Type
) entry
.Member
;
2206 // This finds the method or property for us to override. invocationType is the type where
2207 // the override is going to be declared, name is the name of the method/property, and
2208 // paramTypes is the parameters, if any to the method or property
2210 // Because the MemberCache holds members from this class and all the base classes,
2211 // we can avoid tons of reflection stuff.
2213 public MemberInfo
FindMemberToOverride (Type invocationType
, string name
, Type
[] paramTypes
, bool is_property
)
2215 ArrayList applicable
;
2216 if (method_hash
!= null && !is_property
)
2217 applicable
= (ArrayList
) method_hash
[name
];
2219 applicable
= (ArrayList
) member_hash
[name
];
2221 if (applicable
== null)
2224 // Walk the chain of methods, starting from the top.
2226 for (int i
= applicable
.Count
- 1; i
>= 0; i
--) {
2227 CacheEntry entry
= (CacheEntry
) applicable
[i
];
2229 if ((entry
.EntryType
& (is_property
? (EntryType
.Property
| EntryType
.Field
) : EntryType
.Method
)) == 0)
2232 PropertyInfo pi
= null;
2233 MethodInfo mi
= null;
2234 FieldInfo fi
= null;
2235 Type
[] cmpAttrs
= null;
2238 if ((entry
.EntryType
& EntryType
.Field
) != 0) {
2239 fi
= (FieldInfo
)entry
.Member
;
2241 // TODO: For this case we ignore member type
2242 //fb = TypeManager.GetField (fi);
2243 //cmpAttrs = new Type[] { fb.MemberType };
2245 pi
= (PropertyInfo
) entry
.Member
;
2246 cmpAttrs
= TypeManager
.GetArgumentTypes (pi
);
2249 mi
= (MethodInfo
) entry
.Member
;
2250 cmpAttrs
= TypeManager
.GetArgumentTypes (mi
);
2254 // TODO: Almost duplicate !
2256 switch (fi
.Attributes
& FieldAttributes
.FieldAccessMask
) {
2257 case FieldAttributes
.Private
:
2259 // A private method is Ok if we are a nested subtype.
2260 // The spec actually is not very clear about this, see bug 52458.
2262 if (invocationType
!= entry
.Container
.Type
&
2263 TypeManager
.IsNestedChildOf (invocationType
, entry
.Container
.Type
))
2267 case FieldAttributes
.FamANDAssem
:
2268 case FieldAttributes
.Assembly
:
2270 // Check for assembly methods
2272 if (mi
.DeclaringType
.Assembly
!= CodeGen
.Assembly
.Builder
)
2276 return entry
.Member
;
2280 // Check the arguments
2282 if (cmpAttrs
.Length
!= paramTypes
.Length
)
2285 for (int j
= cmpAttrs
.Length
- 1; j
>= 0; j
--) {
2286 if (!TypeManager
.IsEqual (paramTypes
[j
], cmpAttrs
[j
]))
2291 // get one of the methods because this has the visibility info.
2294 mi
= pi
.GetGetMethod (true);
2296 mi
= pi
.GetSetMethod (true);
2302 switch (mi
.Attributes
& MethodAttributes
.MemberAccessMask
) {
2303 case MethodAttributes
.Private
:
2305 // A private method is Ok if we are a nested subtype.
2306 // The spec actually is not very clear about this, see bug 52458.
2308 if (invocationType
.Equals (entry
.Container
.Type
) ||
2309 TypeManager
.IsNestedChildOf (invocationType
, entry
.Container
.Type
))
2310 return entry
.Member
;
2313 case MethodAttributes
.FamANDAssem
:
2314 case MethodAttributes
.Assembly
:
2316 // Check for assembly methods
2318 if (mi
.DeclaringType
.Assembly
== CodeGen
.Assembly
.Builder
)
2319 return entry
.Member
;
2324 // A protected method is ok, because we are overriding.
2325 // public is always ok.
2327 return entry
.Member
;
2337 /// The method is looking for conflict with inherited symbols (errors CS0108, CS0109).
2338 /// We handle two cases. The first is for types without parameters (events, field, properties).
2339 /// The second are methods, indexers and this is why ignore_complex_types is here.
2340 /// The latest param is temporary hack. See DoDefineMembers method for more info.
2342 public MemberInfo
FindMemberWithSameName (string name
, bool ignore_complex_types
, MemberInfo ignore_member
)
2344 ArrayList applicable
= null;
2346 if (method_hash
!= null)
2347 applicable
= (ArrayList
) method_hash
[name
];
2349 if (applicable
!= null) {
2350 for (int i
= applicable
.Count
- 1; i
>= 0; i
--) {
2351 CacheEntry entry
= (CacheEntry
) applicable
[i
];
2352 if ((entry
.EntryType
& EntryType
.Public
) != 0)
2353 return entry
.Member
;
2357 if (member_hash
== null)
2359 applicable
= (ArrayList
) member_hash
[name
];
2361 if (applicable
!= null) {
2362 for (int i
= applicable
.Count
- 1; i
>= 0; i
--) {
2363 CacheEntry entry
= (CacheEntry
) applicable
[i
];
2364 if ((entry
.EntryType
& EntryType
.Public
) != 0 & entry
.Member
!= ignore_member
) {
2365 if (ignore_complex_types
) {
2366 if ((entry
.EntryType
& EntryType
.Method
) != 0)
2369 // Does exist easier way how to detect indexer ?
2370 if ((entry
.EntryType
& EntryType
.Property
) != 0) {
2371 Type
[] arg_types
= TypeManager
.GetArgumentTypes ((PropertyInfo
)entry
.Member
);
2372 if (arg_types
.Length
> 0)
2376 return entry
.Member
;
2383 Hashtable locase_table
;
2386 /// Builds low-case table for CLS Compliance test
2388 public Hashtable
GetPublicMembers ()
2390 if (locase_table
!= null)
2391 return locase_table
;
2393 locase_table
= new Hashtable ();
2394 foreach (DictionaryEntry entry
in member_hash
) {
2395 ArrayList members
= (ArrayList
)entry
.Value
;
2396 for (int ii
= 0; ii
< members
.Count
; ++ii
) {
2397 CacheEntry member_entry
= (CacheEntry
) members
[ii
];
2399 if ((member_entry
.EntryType
& EntryType
.Public
) == 0)
2402 // TODO: Does anyone know easier way how to detect that member is internal ?
2403 switch (member_entry
.EntryType
& EntryType
.MaskType
) {
2404 case EntryType
.Constructor
:
2407 case EntryType
.Field
:
2408 if ((((FieldInfo
)member_entry
.Member
).Attributes
& (FieldAttributes
.Assembly
| FieldAttributes
.Public
)) == FieldAttributes
.Assembly
)
2412 case EntryType
.Method
:
2413 if ((((MethodInfo
)member_entry
.Member
).Attributes
& (MethodAttributes
.Assembly
| MethodAttributes
.Public
)) == MethodAttributes
.Assembly
)
2417 case EntryType
.Property
:
2418 PropertyInfo pi
= (PropertyInfo
)member_entry
.Member
;
2419 if (pi
.GetSetMethod () == null && pi
.GetGetMethod () == null)
2423 case EntryType
.Event
:
2424 EventInfo ei
= (EventInfo
)member_entry
.Member
;
2425 MethodInfo mi
= ei
.GetAddMethod ();
2426 if ((mi
.Attributes
& (MethodAttributes
.Assembly
| MethodAttributes
.Public
)) == MethodAttributes
.Assembly
)
2430 string lcase
= ((string)entry
.Key
).ToLower (System
.Globalization
.CultureInfo
.InvariantCulture
);
2431 locase_table
[lcase
] = member_entry
.Member
;
2435 return locase_table
;
2438 public Hashtable Members
{
2445 /// Cls compliance check whether methods or constructors parameters differing only in ref or out, or in array rank
2447 public void VerifyClsParameterConflict (ArrayList al
, MethodCore method
, MemberInfo this_builder
)
2449 EntryType tested_type
= (method
is Constructor
? EntryType
.Constructor
: EntryType
.Method
) | EntryType
.Public
;
2451 for (int i
= 0; i
< al
.Count
; ++i
) {
2452 MemberCache
.CacheEntry entry
= (MemberCache
.CacheEntry
) al
[i
];
2455 if (entry
.Member
== this_builder
)
2458 if ((entry
.EntryType
& tested_type
) != tested_type
)
2461 MethodBase method_to_compare
= (MethodBase
)entry
.Member
;
2462 if (AttributeTester
.AreOverloadedMethodParamsClsCompliant (method
.ParameterTypes
, TypeManager
.GetArgumentTypes (method_to_compare
)))
2465 IMethodData md
= TypeManager
.GetMethod (method_to_compare
);
2467 // TODO: now we are ignoring CLSCompliance(false) on method from other assembly which is buggy.
2468 // However it is exactly what csc does.
2469 if (md
!= null && !md
.IsClsCompliaceRequired (method
.Parent
))
2472 Report
.SymbolRelatedToPreviousError (entry
.Member
);
2473 Report
.Error (3006, method
.Location
, "Overloaded method '{0}' differing only in ref or out, or in array rank, is not CLS-compliant", method
.GetSignatureForError ());