In ilasm/codegen:
[mcs.git] / bmcs / decl.cs
blob659b541db8c68fe252a32074191f36f33b9ec4d2
1 //
2 // decl.cs: Declaration base class for structs, classes, enums and interfaces.
3 //
4 // Author: Miguel de Icaza (miguel@gnu.org)
5 // Marek Safar (marek.safar@seznam.cz)
6 //
7 // Licensed under the terms of the GNU GPL
8 //
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
15 using System;
16 using System.Text;
17 using System.Collections;
18 using System.Globalization;
19 using System.Reflection.Emit;
20 using System.Reflection;
21 using System.Xml;
23 namespace Mono.CSharp {
25 public class MemberName {
26 public string Name;
27 public readonly TypeArguments TypeArguments;
29 public readonly MemberName Left;
31 public static readonly MemberName Null = new MemberName ("");
33 public MemberName (string name)
35 this.Name = name;
38 public MemberName (string name, TypeArguments args)
39 : this (name)
41 this.TypeArguments = args;
44 public MemberName (MemberName left, string name, TypeArguments args)
45 : this (name, args)
47 this.Left = left;
50 public MemberName (MemberName left, MemberName right)
51 : this (left, right.Name, right.TypeArguments)
55 public string GetName ()
57 if (Left != null)
58 return Left.GetName () + "." + Name;
59 else
60 return Name;
63 public bool IsGeneric {
64 get {
65 if (TypeArguments != null)
66 return true;
67 else if (Left != null)
68 return Left.IsGeneric;
69 else
70 return false;
74 public string GetName (bool is_generic)
76 string name = is_generic ? Basename : Name;
77 if (Left != null)
78 return Left.GetName (is_generic) + "." + name;
79 else
80 return name;
83 public int CountTypeArguments {
84 get {
85 if (TypeArguments == null)
86 return 0;
87 else
88 return TypeArguments.Count;
92 public string GetMethodName ()
94 if (Left != null)
95 return Left.GetTypeName () + "." + Name;
96 else
97 return Name;
100 public static string MakeName (string name, TypeArguments args)
102 if (args == null)
103 return name;
104 else
105 return name + "`" + args.Count;
108 public static string MakeName (string name, int count)
110 return name + "`" + count;
113 public string GetTypeName ()
115 if (Left != null)
116 return Left.GetTypeName () + "." +
117 MakeName (Name, TypeArguments);
118 else
119 return MakeName (Name, TypeArguments);
122 protected bool IsUnbound {
123 get {
124 if ((Left != null) && Left.IsUnbound)
125 return true;
126 else if (TypeArguments == null)
127 return false;
128 else
129 return TypeArguments.IsUnbound;
133 protected bool CheckUnbound (Location loc)
135 if ((Left != null) && !Left.CheckUnbound (loc))
136 return false;
137 if ((TypeArguments != null) && !TypeArguments.IsUnbound) {
138 Report.Error (1031, loc, "Type expected");
139 return false;
142 return true;
145 public Expression GetTypeExpression (Location loc)
147 if (IsUnbound) {
148 if (!CheckUnbound (loc))
149 return null;
151 return new UnboundTypeExpression (GetTypeName ());
154 if (Left != null) {
155 Expression lexpr = Left.GetTypeExpression (loc);
157 return new MemberAccess (lexpr, Name, TypeArguments, loc);
158 } else {
159 if (TypeArguments != null)
160 return new ConstructedType (Name, TypeArguments, loc);
161 else
162 return new SimpleName (Name, loc);
166 public MemberName Clone ()
168 if (Left != null)
169 return new MemberName (Left.Clone (), Name, TypeArguments);
170 else
171 return new MemberName (Name, TypeArguments);
174 public string Basename {
175 get {
176 if (TypeArguments != null)
177 return MakeName (Name, TypeArguments);
178 else
179 return Name;
183 public override string ToString ()
185 string full_name;
186 if (TypeArguments != null)
187 full_name = Name + "<" + TypeArguments + ">";
188 else
189 full_name = Name;
191 if (Left != null)
192 return Left + "." + full_name;
193 else
194 return full_name;
198 /// <summary>
199 /// Base representation for members. This is used to keep track
200 /// of Name, Location and Modifier flags, and handling Attributes.
201 /// </summary>
202 public abstract class MemberCore : Attributable {
203 /// <summary>
204 /// Public name
205 /// </summary>
206 public string Name {
207 get {
208 return MemberName.GetName (!(this is GenericMethod) && !(this is Method));
212 // Is not readonly because of IndexerName attribute
213 public MemberName MemberName;
215 /// <summary>
216 /// Modifier flags that the user specified in the source code
217 /// </summary>
218 public int ModFlags;
220 public /*readonly*/ TypeContainer Parent;
222 /// <summary>
223 /// Location where this declaration happens
224 /// </summary>
225 public readonly Location Location;
227 /// <summary>
228 /// XML documentation comment
229 /// </summary>
230 public string DocComment;
232 /// <summary>
233 /// Represents header string for documentation comment
234 /// for each member types.
235 /// </summary>
236 public abstract string DocCommentHeader { get; }
238 [Flags]
239 public enum Flags {
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
253 /// <summary>
254 /// MemberCore flags at first detected then cached
255 /// </summary>
256 internal Flags caching_flags;
258 public MemberCore (TypeContainer parent, MemberName name, Attributes attrs,
259 Location loc)
260 : base (attrs)
262 Parent = parent;
263 MemberName = name;
264 Location = loc;
265 caching_flags = Flags.Obsolete_Undetected | Flags.ClsCompliance_Undetected | Flags.HasCompliantAttribute_Undetected | Flags.Excluded_Undetected;
268 /// <summary>
269 /// Tests presence of ObsoleteAttribute and report proper error
270 /// </summary>
271 protected void CheckUsageOfObsoleteAttribute (Type type)
273 if (type == null)
274 return;
276 ObsoleteAttribute obsolete_attr = AttributeTester.GetObsoleteAttribute (type);
277 if (obsolete_attr == null)
278 return;
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 ()
290 return Name;
293 /// <summary>
294 /// Use this method when MethodBuilder is null
295 /// </summary>
296 public virtual string GetSignatureForError (TypeContainer tc)
298 return Name;
301 /// <summary>
302 /// Base Emit method. This is also entry point for CLS-Compliant verification.
303 /// </summary>
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)
311 return;
313 VerifyClsCompliance (Parent);
316 public bool InUnsafe {
317 get {
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)
331 return true;
333 if (parent.UnsafeContext)
334 return true;
336 Expression.UnsafeError (Location);
337 return false;
340 /// <summary>
341 /// Returns instance of ObsoleteAttribute for this MemberCore
342 /// </summary>
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) {
347 return null;
350 caching_flags &= ~Flags.Obsolete_Undetected;
352 if (OptAttributes == null)
353 return null;
355 Attribute obsolete_attr = OptAttributes.Search (
356 TypeManager.obsolete_attribute_type, ds.EmitContext);
357 if (obsolete_attr == null)
358 return null;
360 ObsoleteAttribute obsolete = obsolete_attr.GetObsoleteAttribute (ds.EmitContext);
361 if (obsolete == null)
362 return null;
364 caching_flags |= Flags.Obsolete;
365 return obsolete;
368 /// <summary>
369 /// Analyze whether CLS-Compliant verification must be execute for this MemberCore.
370 /// </summary>
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;
379 return true;
382 caching_flags &= ~Flags.ClsCompliance_Undetected;
383 return false;
386 /// <summary>
387 /// Returns true when MemberCore is exposed from assembly.
388 /// </summary>
389 public bool IsExposedFromAssembly (DeclSpace ds)
391 if ((ModFlags & (Modifiers.PUBLIC | Modifiers.PROTECTED)) == 0)
392 return false;
394 DeclSpace parentContainer = ds;
395 while (parentContainer != null && parentContainer.ModFlags != 0) {
396 if ((parentContainer.ModFlags & (Modifiers.PUBLIC | Modifiers.PROTECTED)) == 0)
397 return false;
398 parentContainer = parentContainer.Parent;
400 return true;
403 /// <summary>
404 /// Resolve CLSCompliantAttribute value or gets cached value.
405 /// </summary>
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 ();
419 /// <summary>
420 /// Returns true if MemberCore is explicitly marked with CLSCompliantAttribute
421 /// </summary>
422 protected bool HasClsCompliantAttribute {
423 get {
424 return (caching_flags & Flags.HasClsCompliantAttribute) != 0;
428 /// <summary>
429 /// It helps to handle error 102 & 111 detection
430 /// </summary>
431 public virtual bool MarkForDuplicationCheck ()
433 return false;
436 /// <summary>
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.
441 /// </summary>
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 ());
451 return false;
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 ());
458 return false;
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 () );
465 return true;
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;
486 else
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);
500 /// <summary>
501 /// Base class for structs, classes, enumerations and interfaces.
502 /// </summary>
503 /// <remarks>
504 /// They all create new declaration spaces. This
505 /// provides the common foundation for managing those name
506 /// spaces.
507 /// </remarks>
508 public abstract class DeclSpace : MemberCore, IAlias {
509 /// <summary>
510 /// This points to the actual definition that is being
511 /// created with System.Reflection.Emit
512 /// </summary>
513 public TypeBuilder TypeBuilder;
515 /// <summary>
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.
519 /// </summary>
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 {
541 get { return ec; }
545 // Whether we are Generic
547 public bool IsGeneric {
548 get {
549 if (is_generic)
550 return true;
551 else if (Parent != null)
552 return Parent.IsGeneric;
553 else
554 return false;
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)
564 NamespaceEntry = ns;
565 Basename = name.Name;
566 defined_names = new Hashtable ();
567 if (name.TypeArguments != null) {
568 is_generic = true;
569 count_type_params = name.TypeArguments.Count;
571 if (parent != null)
572 count_type_params += parent.count_type_params;
575 /// <summary>
576 /// Adds the member to defined_names table. It tests for duplications and enclosing name conflicts
577 /// </summary>
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);
584 else {
585 Report.SymbolRelatedToPreviousError (this);
586 Report.Error (542, "'{0}': member names cannot be the same as their " +
587 "enclosing type", symbol.Location, symbol.GetSignatureForError ());
589 return false;
592 MemberCore mc = (MemberCore)defined_names [fullname];
594 if (mc == null) {
595 defined_names.Add (fullname, symbol);
596 return true;
599 if (symbol.MarkForDuplicationCheck () && mc.MarkForDuplicationCheck ())
600 return true;
602 if (symbol is TypeParameter)
603 Report.Error (692, symbol.Location, "Duplicate type parameter `{0}'", basename);
604 else {
605 Report.SymbolRelatedToPreviousError (mc);
606 Report.Error (102, symbol.Location,
607 "The type '{0}' already contains a definition for '{1}'",
608 GetSignatureForError (), basename);
610 return false;
613 public void RecordDecl ()
615 if ((NamespaceEntry != null) && (Parent == RootContext.Tree.Types))
616 NamespaceEntry.DefineName (MemberName.Basename, this);
619 /// <summary>
620 /// Returns the MemberCore associated with a given name in the declaration
621 /// space. It doesn't return method based symbols !!
622 /// </summary>
623 ///
624 public MemberCore GetDefinition (string name)
626 return (MemberCore)defined_names [name];
629 bool in_transit = false;
631 /// <summary>
632 /// This function is used to catch recursive definitions
633 /// in declarations.
634 /// </summary>
635 public bool InTransit {
636 get {
637 return in_transit;
640 set {
641 in_transit = value;
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 {
651 get {
652 if (Parent != null){
653 if (Parent.Parent == null)
654 return true;
656 return false;
660 public virtual void CloseType ()
662 if ((caching_flags & Flags.CloseTypeCreated) == 0){
663 try {
664 TypeBuilder.CreateType ();
665 } catch {
667 // The try/catch is needed because
668 // nested enumerations fail to load when they
669 // are defined.
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;
681 /// <remarks>
682 /// Should be overriten by the appropriate declaration space
683 /// </remarks>
684 public abstract TypeBuilder DefineType ();
686 /// <summary>
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.
689 /// </summary>
690 public abstract bool DefineMembers (TypeContainer parent);
693 // Whether this is an `unsafe context'
695 public bool UnsafeContext {
696 get {
697 if ((ModFlags & Modifiers.UNSAFE) != 0)
698 return true;
699 if (Parent != null)
700 return Parent.UnsafeContext;
701 return false;
705 public static string MakeFQN (string nsn, string name)
707 if (nsn == "")
708 return 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) {
718 if (!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);
724 return null;
727 TypeParameter[] args;
728 if (this is GenericMethod)
729 args = Parent.TypeParameters;
730 else
731 args = TypeParameters;
733 TypeExpr ctype = new ConstructedType (t.Type, args, loc);
734 return ctype.ResolveAsTypeTerminal (ec);
737 return t;
740 // <summary>
741 // Resolves the expression `e' for a type, and will recursively define
742 // types. This should only be used for resolving base types.
743 // </summary>
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;
760 else
761 type_resolve_ec.ContainerType = TypeBuilder;
763 return e.ResolveAsTypeTerminal (type_resolve_ec);
766 public bool CheckAccessLevel (Type check_type)
768 TypeBuilder tb;
769 if ((this is GenericMethod) || (this is Iterator))
770 tb = Parent.TypeBuilder;
771 else
772 tb = TypeBuilder;
774 if (check_type.IsGenericInstance)
775 check_type = check_type.GetGenericTypeDefinition ();
777 if (check_type == tb)
778 return true;
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.
784 return true;
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));
799 switch (check_attr){
800 case TypeAttributes.Public:
801 return true;
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.
809 return true;
811 // This test should probably use the declaringtype.
813 return check_type.Assembly == TypeBuilder.Assembly;
815 case TypeAttributes.NestedPublic:
816 return true;
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);
840 return false;
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))
866 return true;
868 return NestedAccessible (tb, check_type);
871 // Access level of a type.
872 const int X = 1;
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;
888 else
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;
895 else
896 return AccessLevel.Public;
899 // What is the effective access level of this?
900 // TODO: Cache this?
901 AccessLevel EffectiveAccessLevel {
902 get {
903 AccessLevel myAccess = GetAccessLevelFromModifiers (ModFlags);
904 if (!IsTopLevel && (Parent != null))
905 return myAccess & Parent.EffectiveAccessLevel;
906 return myAccess;
910 // Return the access level for type `t'
911 static AccessLevel TypeEffectiveAccessLevel (Type t)
913 if (t.IsPublic)
914 return AccessLevel.Public;
915 if (t.IsNestedPrivate)
916 return AccessLevel.Private;
917 if (t.IsNotPublic)
918 return AccessLevel.Internal;
920 // By now, it must be nested
921 AccessLevel parentLevel = TypeEffectiveAccessLevel (t.DeclaringType);
923 if (t.IsNestedPublic)
924 return parentLevel;
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
951 // may return true.
954 if ((flags & Modifiers.PRIVATE) != 0)
955 return true;
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)
986 DeclSpace parent;
987 FullNamedExpression result;
988 Type t;
989 object r;
991 error = false;
993 if (dh.Lookup (ns, name, out r))
994 return (FullNamedExpression) r;
995 else {
996 if (ns != ""){
997 if (Namespace.IsNamespace (ns)){
998 string fullname = (ns != "") ? ns + "." + name : name;
999 t = TypeManager.LookupType (fullname);
1000 } else
1001 t = null;
1002 } else
1003 t = TypeManager.LookupType (name);
1006 if (t != null) {
1007 result = new TypeExpression (t, Location.Null);
1008 dh.Insert (ns, name, result);
1009 return 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);
1016 return result;
1020 if (ns == "" && Namespace.IsNamespace (name)) {
1021 result = Namespace.LookupNamespace (name, false);
1022 dh.Insert (ns, name, result);
1023 return result;
1027 // In case we are fed a composite name, normalize it.
1029 int p = name.LastIndexOf ('.');
1030 if (p != -1){
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);
1041 return null;
1044 t = DefineTypeAndParents (parent);
1045 if (t == null){
1046 error = true;
1047 return null;
1050 result = new TypeExpression (t, Location.Null);
1051 dh.Insert (ns, name, result);
1052 return 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})",
1059 name, t1, t2);
1062 public Type FindNestedType (Location loc, string name,
1063 out DeclSpace containing_ds)
1065 FullNamedExpression t;
1066 bool error;
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);
1077 if (error)
1078 return null;
1080 if ((t != null) && containing_ds.CheckAccessLevel (t.Type))
1081 return t.Type;
1083 current_type = current_type.BaseType;
1085 containing_ds = containing_ds.Parent;
1088 return null;
1091 /// <summary>
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
1095 /// </summary>
1097 /// <remarks>
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
1102 /// </remarks>
1103 public FullNamedExpression FindType (Location loc, string name)
1105 FullNamedExpression t;
1106 bool error;
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);
1123 if (error)
1124 return null;
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);
1139 if (error)
1140 return null;
1142 if (t != null)
1143 return t;
1147 // Attempt to do a direct unqualified lookup
1149 t = LookupInterfaceOrClass ("", name, out error);
1150 if (error)
1151 return null;
1153 if (t != null)
1154 return t;
1157 // Attempt to lookup the class on any of the `using'
1158 // namespaces
1161 for (NamespaceEntry ns = NamespaceEntry; ns != null; ns = ns.Parent){
1163 t = LookupInterfaceOrClass (ns.FullName, name, out error);
1164 if (error)
1165 return null;
1167 if (t != null)
1168 return t;
1170 if (name.IndexOf ('.') > 0)
1171 continue;
1173 t = ns.LookupAlias (name);
1174 if (t != null)
1175 return t;
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);
1183 if (error)
1184 return null;
1186 if ((match != null) && (match is TypeExpr)) {
1187 Type matched = ((TypeExpr) match).Type;
1188 if (!CheckAccessLevel (matched))
1189 continue;
1190 if (t != null){
1191 Error_AmbiguousTypeReference (loc, name, t.FullName, match.FullName);
1192 return null;
1194 t = match;
1197 if (t != null)
1198 return t;
1201 //Report.Error (246, Location, "Can not find type `"+name+"'");
1202 return null;
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];
1219 } else {
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);
1231 if (t == null) {
1232 containing_ds = containing_ds.Parent;
1233 continue;
1236 e = new TypeExpression (t, Location.Null);
1237 e = ResolveNestedType (e, Location.Null);
1238 Cache [name] = e;
1239 return e;
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) {
1248 // nested class
1250 Type t = TypeManager.LookupType (current_type.FullName + "." + name);
1251 if (t != null){
1252 e = new TypeExpression (t, Location.Null);
1253 e = ResolveNestedType (e, Location.Null);
1254 Cache [name] = e;
1255 return e;
1258 current_type = current_type.BaseType;
1261 containing_ds = containing_ds.Parent;
1264 e = NamespaceEntry.LookupNamespaceOrType (this, name, loc);
1265 if (!silent || e != null)
1266 Cache [name] = e;
1269 if (e == null && !silent)
1270 Report.Error (246, loc, "Cannot find type `"+name+"'");
1272 return e;
1275 /// <remarks>
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.
1279 /// </remarks>
1280 public abstract MemberList FindMembers (MemberTypes mt, BindingFlags bf,
1281 MemberFilter filter, object criteria);
1283 /// <remarks>
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.
1286 /// </remarks>
1287 public abstract MemberCache MemberCache {
1288 get;
1291 public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder cb)
1293 try {
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);
1306 /// <summary>
1307 /// Goes through class hierarchy and get value of first CLSCompliantAttribute that found.
1308 /// If no is attribute exists then return assembly CLSCompliantAttribute.
1309 /// </summary>
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;
1323 return true;
1325 return false;
1329 if (Parent == null) {
1330 if (CodeGen.Assembly.IsClsCompliant) {
1331 caching_flags |= Flags.ClsCompliantAttributeTrue;
1332 return true;
1334 return false;
1337 if (Parent.GetClsCompliantAttributeValue ()) {
1338 caching_flags |= Flags.ClsCompliantAttributeTrue;
1339 return true;
1341 return false;
1345 // Extensions for generics
1347 TypeParameter[] type_params;
1348 TypeParameter[] type_param_list;
1350 protected string GetInstantiationName ()
1352 StringBuilder sb = new StringBuilder (Name);
1353 sb.Append ("<");
1354 for (int i = 0; i < type_param_list.Length; i++) {
1355 if (i > 0)
1356 sb.Append (",");
1357 sb.Append (type_param_list [i].Name);
1359 sb.Append (">");
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)
1369 continue;
1371 if (RootContext.WarningLevel >= 3)
1372 Report.Warning (
1373 693, Location,
1374 "Type parameter `{0}' has same name " +
1375 "as type parameter from outer type `{1}'",
1376 name, Parent.GetInstantiationName ());
1378 return false;
1381 return true;
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)
1391 the_parent = null;
1393 int start = 0;
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);
1408 list.Add (param);
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)
1418 if (!is_generic) {
1419 if (constraints_list != null) {
1420 Report.Error (
1421 80, Location, "Contraints are not allowed " +
1422 "on non-generic declarations");
1425 return;
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;
1442 break;
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 {
1455 get {
1456 if (!IsGeneric)
1457 throw new InvalidOperationException ();
1458 if (type_param_list == null)
1459 initialize_type_params ();
1461 return type_param_list;
1465 protected TypeParameter[] CurrentTypeParameters {
1466 get {
1467 if (!IsGeneric)
1468 throw new InvalidOperationException ();
1469 if (type_params != null)
1470 return type_params;
1471 else
1472 return new TypeParameter [0];
1476 public int CountTypeParameters {
1477 get {
1478 return count_type_params;
1482 public TypeParameterExpr LookupGeneric (string name, Location loc)
1484 if (!IsGeneric)
1485 return null;
1487 foreach (TypeParameter type_param in CurrentTypeParameters) {
1488 if (type_param.Name != name)
1489 continue;
1491 return new TypeParameterExpr (type_param, loc);
1494 if (Parent != null)
1495 return Parent.LookupGeneric (name, loc);
1497 return null;
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);
1515 else
1516 return new TypeExpression (TypeBuilder, Location);
1519 public override string[] ValidAttributeTargets {
1520 get {
1521 return attribute_targets;
1526 /// <summary>
1527 /// This is a readonly list of MemberInfo's.
1528 /// </summary>
1529 public class MemberList : IList {
1530 public readonly IList List;
1531 int count;
1533 /// <summary>
1534 /// Create a new MemberList from the given IList.
1535 /// </summary>
1536 public MemberList (IList list)
1538 if (list != null)
1539 this.List = list;
1540 else
1541 this.List = new ArrayList ();
1542 count = List.Count;
1545 /// <summary>
1546 /// Concatenate the ILists `first' and `second' to a new MemberList.
1547 /// </summary>
1548 public MemberList (IList first, IList second)
1550 ArrayList list = new ArrayList ();
1551 list.AddRange (first);
1552 list.AddRange (second);
1553 count = list.Count;
1554 List = list;
1557 public static readonly MemberList Empty = new MemberList (new ArrayList ());
1559 /// <summary>
1560 /// Cast the MemberList into a MemberInfo[] array.
1561 /// </summary>
1562 /// <remarks>
1563 /// This is an expensive operation, only use it if it's really necessary.
1564 /// </remarks>
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);
1571 return result;
1574 // ICollection
1576 public int Count {
1577 get {
1578 return count;
1582 public bool IsSynchronized {
1583 get {
1584 return List.IsSynchronized;
1588 public object SyncRoot {
1589 get {
1590 return List.SyncRoot;
1594 public void CopyTo (Array array, int index)
1596 List.CopyTo (array, index);
1599 // IEnumerable
1601 public IEnumerator GetEnumerator ()
1603 return List.GetEnumerator ();
1606 // IList
1608 public bool IsFixedSize {
1609 get {
1610 return true;
1614 public bool IsReadOnly {
1615 get {
1616 return true;
1620 object IList.this [int index] {
1621 get {
1622 return List [index];
1625 set {
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] {
1632 get {
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 ();
1673 /// <summary>
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.
1678 /// </summary>
1679 public interface IMemberContainer {
1680 /// <summary>
1681 /// The name of the IMemberContainer. This is only used for
1682 /// debugging purposes.
1683 /// </summary>
1684 string Name {
1685 get;
1688 /// <summary>
1689 /// The type of this IMemberContainer.
1690 /// </summary>
1691 Type Type {
1692 get;
1695 /// <summary>
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.
1700 /// </summary>
1701 MemberCache BaseCache {
1702 get;
1705 /// <summary>
1706 /// Whether this is an interface.
1707 /// </summary>
1708 bool IsInterface {
1709 get;
1712 /// <summary>
1713 /// Returns all members of this class with the corresponding MemberTypes
1714 /// and BindingFlags.
1715 /// </summary>
1716 /// <remarks>
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.
1723 /// </remarks>
1724 MemberList GetMembers (MemberTypes mt, BindingFlags bf);
1726 /// <summary>
1727 /// Return the container's member cache.
1728 /// </summary>
1729 MemberCache MemberCache {
1730 get;
1734 /// <summary>
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.
1747 /// </summary>
1748 public class MemberCache {
1749 public readonly IMemberContainer Container;
1750 protected Hashtable member_hash;
1751 protected Hashtable method_hash;
1753 /// <summary>
1754 /// Create a new MemberCache for the given IMemberContainer `container'.
1755 /// </summary>
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);
1767 else
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 ();
1775 AddMethods (type);
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 ();
1793 if (ifaces == null)
1794 return;
1796 foreach (Type itype in ifaces)
1797 AddCacheContents (TypeManager.LookupMemberCache (itype));
1800 /// <summary>
1801 /// Bootstrap this member cache by doing a deep-copy of our base.
1802 /// </summary>
1803 Hashtable SetupCache (MemberCache base_class)
1805 Hashtable hash = new Hashtable ();
1807 if (base_class == null)
1808 return hash;
1810 IDictionaryEnumerator it = base_class.member_hash.GetEnumerator ();
1811 while (it.MoveNext ()) {
1812 hash [it.Key] = ((ArrayList) it.Value).Clone ();
1815 return hash;
1818 /// <summary>
1819 /// Add the contents of `cache' to the member_hash.
1820 /// </summary>
1821 void AddCacheContents (MemberCache cache)
1823 IDictionaryEnumerator it = cache.member_hash.GetEnumerator ();
1824 while (it.MoveNext ()) {
1825 ArrayList list = (ArrayList) member_hash [it.Key];
1826 if (list == null)
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)
1834 break;
1835 list.Add (entry);
1840 /// <summary>
1841 /// Add all members from class `container' to the cache.
1842 /// </summary>
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);
1869 /// <summary>
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.
1873 /// </summary>
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 ('<');
1882 if (pos > 0)
1883 name = name.Substring (0, pos);
1885 // We use a name-based hash table of ArrayList's.
1886 ArrayList list = (ArrayList) member_hash [name];
1887 if (list == null) {
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));
1901 /// <summary>
1902 /// Add all declared and inherited methods from class `type' to the method cache.
1903 /// </summary>
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];
1925 if (list == null) {
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));
1940 /// <summary>
1941 /// Compute and return a appropriate `EntryType' magic number for the given
1942 /// MemberTypes and BindingFlags.
1943 /// </summary>
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;
1973 return type;
1976 /// <summary>
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.
1980 /// </summary>
1981 public static bool IsSingleMemberType (MemberTypes mt)
1983 switch (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:
1990 return true;
1992 default:
1993 return false;
1997 /// <summary>
1998 /// We encode the MemberTypes and BindingFlags of each members in a "magic"
1999 /// number to speed up the searching process.
2000 /// </summary>
2001 [Flags]
2002 protected enum EntryType {
2003 None = 0x000,
2005 Instance = 0x001,
2006 Static = 0x002,
2007 MaskStatic = Instance|Static,
2009 Public = 0x004,
2010 NonPublic = 0x008,
2011 MaskProtection = Public|NonPublic,
2013 Declared = 0x010,
2015 Constructor = 0x020,
2016 Event = 0x040,
2017 Field = 0x080,
2018 Method = 0x100,
2019 Property = 0x200,
2020 NestedType = 0x400,
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,
2041 EntryType, Member);
2045 /// <summary>
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.
2049 /// </summary>
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))
2057 return true;
2060 // Multiple properties: we query those just to find out the indexer
2061 // name
2063 if ((list.Count > 0) && (list [0] is PropertyInfo))
2064 return true;
2066 return false;
2069 /// <summary>
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.
2086 /// </summary>
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)
2095 if (using_global)
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];
2112 else
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?
2125 global.Clear ();
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))
2150 break;
2152 current = entry.Container;
2155 // Is the member of the correct type ?
2156 if ((entry.EntryType & type & EntryType.MaskType) == 0)
2157 continue;
2159 // Is the member static/non-static ?
2160 if ((entry.EntryType & type & EntryType.MaskStatic) == 0)
2161 continue;
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);
2186 return 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)
2194 return 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;
2202 return null;
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];
2218 else
2219 applicable = (ArrayList) member_hash [name];
2221 if (applicable == null)
2222 return 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)
2230 continue;
2232 PropertyInfo pi = null;
2233 MethodInfo mi = null;
2234 FieldInfo fi = null;
2235 Type [] cmpAttrs = null;
2237 if (is_property) {
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 };
2244 } else {
2245 pi = (PropertyInfo) entry.Member;
2246 cmpAttrs = TypeManager.GetArgumentTypes (pi);
2248 } else {
2249 mi = (MethodInfo) entry.Member;
2250 cmpAttrs = TypeManager.GetArgumentTypes (mi);
2253 if (fi != null) {
2254 // TODO: Almost duplicate !
2255 // Check visibility
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))
2264 continue;
2266 break;
2267 case FieldAttributes.FamANDAssem:
2268 case FieldAttributes.Assembly:
2270 // Check for assembly methods
2272 if (mi.DeclaringType.Assembly != CodeGen.Assembly.Builder)
2273 continue;
2274 break;
2276 return entry.Member;
2280 // Check the arguments
2282 if (cmpAttrs.Length != paramTypes.Length)
2283 continue;
2285 for (int j = cmpAttrs.Length - 1; j >= 0; j --) {
2286 if (!TypeManager.IsEqual (paramTypes [j], cmpAttrs [j]))
2287 goto next;
2291 // get one of the methods because this has the visibility info.
2293 if (is_property) {
2294 mi = pi.GetGetMethod (true);
2295 if (mi == null)
2296 mi = pi.GetSetMethod (true);
2300 // Check visibility
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;
2312 break;
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;
2321 break;
2322 default:
2324 // A protected method is ok, because we are overriding.
2325 // public is always ok.
2327 return entry.Member;
2329 next:
2333 return null;
2336 /// <summary>
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.
2341 /// </summary>
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)
2358 return 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)
2367 continue;
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)
2373 continue;
2376 return entry.Member;
2380 return null;
2383 Hashtable locase_table;
2385 /// <summary>
2386 /// Builds low-case table for CLS Compliance test
2387 /// </summary>
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)
2400 continue;
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:
2405 continue;
2407 case EntryType.Field:
2408 if ((((FieldInfo)member_entry.Member).Attributes & (FieldAttributes.Assembly | FieldAttributes.Public)) == FieldAttributes.Assembly)
2409 continue;
2410 break;
2412 case EntryType.Method:
2413 if ((((MethodInfo)member_entry.Member).Attributes & (MethodAttributes.Assembly | MethodAttributes.Public)) == MethodAttributes.Assembly)
2414 continue;
2415 break;
2417 case EntryType.Property:
2418 PropertyInfo pi = (PropertyInfo)member_entry.Member;
2419 if (pi.GetSetMethod () == null && pi.GetGetMethod () == null)
2420 continue;
2421 break;
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)
2427 continue;
2428 break;
2430 string lcase = ((string)entry.Key).ToLower (System.Globalization.CultureInfo.InvariantCulture);
2431 locase_table [lcase] = member_entry.Member;
2432 break;
2435 return locase_table;
2438 public Hashtable Members {
2439 get {
2440 return member_hash;
2444 /// <summary>
2445 /// Cls compliance check whether methods or constructors parameters differing only in ref or out, or in array rank
2446 /// </summary>
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];
2454 // skip itself
2455 if (entry.Member == this_builder)
2456 continue;
2458 if ((entry.EntryType & tested_type) != tested_type)
2459 continue;
2461 MethodBase method_to_compare = (MethodBase)entry.Member;
2462 if (AttributeTester.AreOverloadedMethodParamsClsCompliant (method.ParameterTypes, TypeManager.GetArgumentTypes (method_to_compare)))
2463 continue;
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))
2470 continue;
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 ());