2003-11-08 Martin Baulig <martin@ximian.com>
[mono.git] / mcs / mcs / decl.cs
1 //
2 // decl.cs: Declaration base class for structs, classes, enums and interfaces.
3 //
4 // Author: Miguel de Icaza (miguel@gnu.org)
5 //
6 // Licensed under the terms of the GNU GPL
7 //
8 // (C) 2001 Ximian, Inc (http://www.ximian.com)
9 //
10 // TODO: Move the method verification stuff from the class.cs and interface.cs here
11 //
12
13 using System;
14 using System.Collections;
15 using System.Reflection.Emit;
16 using System.Reflection;
17
18 namespace Mono.CSharp {
19
20         /// <summary>
21         ///   Base representation for members.  This is only used to keep track
22         ///   of Name, Location and Modifier flags.
23         /// </summary>
24         public abstract class MemberCore {
25                 /// <summary>
26                 ///   Public name
27                 /// </summary>
28                 public string Name;
29
30                 /// <summary>
31                 ///   Modifier flags that the user specified in the source code
32                 /// </summary>
33                 public int ModFlags;
34
35                 /// <summary>
36                 ///   Location where this declaration happens
37                 /// </summary>
38                 public readonly Location Location;
39
40                 public MemberCore (string name, Location loc)
41                 {
42                         Name = name;
43                         Location = loc;
44                 }
45
46                 public abstract bool Define (TypeContainer parent);
47
48                 // 
49                 // Whehter is it ok to use an unsafe pointer in this type container
50                 //
51                 public bool UnsafeOK (DeclSpace parent)
52                 {
53                         //
54                         // First check if this MemberCore modifier flags has unsafe set
55                         //
56                         if ((ModFlags & Modifiers.UNSAFE) != 0)
57                                 return true;
58
59                         if (parent.UnsafeContext)
60                                 return true;
61
62                         Expression.UnsafeError (Location);
63                         return false;
64                 }
65         }
66
67         /// <summary>
68         ///   Base class for structs, classes, enumerations and interfaces.  
69         /// </summary>
70         /// <remarks>
71         ///   They all create new declaration spaces.  This
72         ///   provides the common foundation for managing those name
73         ///   spaces.
74         /// </remarks>
75         public abstract class DeclSpace : MemberCore {
76                 /// <summary>
77                 ///   this points to the actual definition that is being
78                 ///   created with System.Reflection.Emit
79                 /// </summary>
80                 public TypeBuilder TypeBuilder;
81
82                 /// <summary>
83                 ///   This variable tracks whether we have Closed the type
84                 /// </summary>
85                 public bool Created = false;
86                 
87                 //
88                 // This is the namespace in which this typecontainer
89                 // was declared.  We use this to resolve names.
90                 //
91                 public NamespaceEntry NamespaceEntry;
92
93                 public Hashtable Cache = new Hashtable ();
94                 
95                 public string Basename;
96                 
97                 /// <summary>
98                 ///   defined_names is used for toplevel objects
99                 /// </summary>
100                 protected Hashtable defined_names;
101
102                 TypeContainer parent;           
103
104                 public DeclSpace (NamespaceEntry ns, TypeContainer parent, string name, Location l)
105                         : base (name, l)
106                 {
107                         NamespaceEntry = ns;
108                         Basename = name.Substring (1 + name.LastIndexOf ('.'));
109                         defined_names = new Hashtable ();
110                         this.parent = parent;
111                 }
112
113                 public void RecordDecl ()
114                 {
115                         if ((NamespaceEntry != null) && (parent == RootContext.Tree.Types))
116                                 NamespaceEntry.DefineName (Basename, this);
117                 }
118
119                 /// <summary>
120                 ///   The result value from adding an declaration into
121                 ///   a struct or a class
122                 /// </summary>
123                 public enum AdditionResult {
124                         /// <summary>
125                         /// The declaration has been successfully
126                         /// added to the declation space.
127                         /// </summary>
128                         Success,
129
130                         /// <summary>
131                         ///   The symbol has already been defined.
132                         /// </summary>
133                         NameExists,
134
135                         /// <summary>
136                         ///   Returned if the declation being added to the
137                         ///   name space clashes with its container name.
138                         ///
139                         ///   The only exceptions for this are constructors
140                         ///   and static constructors
141                         /// </summary>
142                         EnclosingClash,
143
144                         /// <summary>
145                         ///   Returned if a constructor was created (because syntactically
146                         ///   it looked like a constructor) but was not (because the name
147                         ///   of the method is not the same as the container class
148                         /// </summary>
149                         NotAConstructor,
150
151                         /// <summary>
152                         ///   This is only used by static constructors to emit the
153                         ///   error 111, but this error for other things really
154                         ///   happens at another level for other functions.
155                         /// </summary>
156                         MethodExists,
157
158                         /// <summary>
159                         ///   Some other error.
160                         /// </summary>
161                         Error
162                 }
163
164                 /// <summary>
165                 ///   Returns a status code based purely on the name
166                 ///   of the member being added
167                 /// </summary>
168                 protected AdditionResult IsValid (string basename, string name)
169                 {
170                         if (basename == Basename)
171                                 return AdditionResult.EnclosingClash;
172
173                         if (defined_names.Contains (name))
174                                 return AdditionResult.NameExists;
175
176                         return AdditionResult.Success;
177                 }
178
179                 public static int length;
180                 public static int small;
181                 
182                 /// <summary>
183                 ///   Introduce @name into this declaration space and
184                 ///   associates it with the object @o.  Note that for
185                 ///   methods this will just point to the first method. o
186                 /// </summary>
187                 protected void DefineName (string name, object o)
188                 {
189                         defined_names.Add (name, o);
190
191 #if DEBUGME
192                         int p = name.LastIndexOf (".");
193                         int l = name.Length;
194                         length += l;
195                         small += l -p;
196 #endif
197                 }
198
199                 /// <summary>
200                 ///   Returns the object associated with a given name in the declaration
201                 ///   space.  This is the inverse operation of `DefineName'
202                 /// </summary>
203                 public object GetDefinition (string name)
204                 {
205                         return defined_names [name];
206                 }
207                 
208                 bool in_transit = false;
209                 
210                 /// <summary>
211                 ///   This function is used to catch recursive definitions
212                 ///   in declarations.
213                 /// </summary>
214                 public bool InTransit {
215                         get {
216                                 return in_transit;
217                         }
218
219                         set {
220                                 in_transit = value;
221                         }
222                 }
223
224                 public TypeContainer Parent {
225                         get {
226                                 return parent;
227                         }
228                 }
229
230                 /// <summary>
231                 ///   Looks up the alias for the name
232                 /// </summary>
233                 public string LookupAlias (string name)
234                 {
235                         if (NamespaceEntry != null)
236                                 return NamespaceEntry.LookupAlias (name);
237                         else
238                                 return null;
239                 }
240                 
241                 // 
242                 // root_types contains all the types.  All TopLevel types
243                 // hence have a parent that points to `root_types', that is
244                 // why there is a non-obvious test down here.
245                 //
246                 public bool IsTopLevel {
247                         get {
248                                 if (parent != null){
249                                         if (parent.parent == null)
250                                                 return true;
251                                 }
252                                 return false;
253                         }
254                 }
255
256                 public virtual void CloseType ()
257                 {
258                         if (!Created){
259                                 try {
260                                         TypeBuilder.CreateType ();
261                                 } catch {
262                                         //
263                                         // The try/catch is needed because
264                                         // nested enumerations fail to load when they
265                                         // are defined.
266                                         //
267                                         // Even if this is the right order (enumerations
268                                         // declared after types).
269                                         //
270                                         // Note that this still creates the type and
271                                         // it is possible to save it
272                                 }
273                                 Created = true;
274                         }
275                 }
276
277                 /// <remarks>
278                 ///  Should be overriten by the appropriate declaration space
279                 /// </remarks>
280                 public abstract TypeBuilder DefineType ();
281                 
282                 /// <summary>
283                 ///   Define all members, but don't apply any attributes or do anything which may
284                 ///   access not-yet-defined classes.  This method also creates the MemberCache.
285                 /// </summary>
286                 public abstract bool DefineMembers (TypeContainer parent);
287
288                 //
289                 // Whether this is an `unsafe context'
290                 //
291                 public bool UnsafeContext {
292                         get {
293                                 if ((ModFlags & Modifiers.UNSAFE) != 0)
294                                         return true;
295                                 if (parent != null)
296                                         return parent.UnsafeContext;
297                                 return false;
298                         }
299                 }
300
301                 public static string MakeFQN (string nsn, string name)
302                 {
303                         if (nsn == "")
304                                 return name;
305                         return String.Concat (nsn, ".", name);
306                 }
307
308                 EmitContext type_resolve_ec;
309                 EmitContext GetTypeResolveEmitContext (TypeContainer parent, Location loc)
310                 {
311                         type_resolve_ec = new EmitContext (parent, this, loc, null, null, ModFlags, false);
312                         type_resolve_ec.ResolvingTypeTree = true;
313
314                         TypeManager.ResolveExpressionTypes (type_resolve_ec);
315
316                         return type_resolve_ec;
317                 }
318
319                 // <summary>
320                 //    Looks up the type, as parsed into the expression `e' 
321                 // </summary>
322                 public Type ResolveType (Expression e, bool silent, Location loc)
323                 {
324                         if (type_resolve_ec == null)
325                                 type_resolve_ec = GetTypeResolveEmitContext (parent, loc);
326                         type_resolve_ec.loc = loc;
327                         type_resolve_ec.ContainerType = TypeBuilder;
328
329                         int errors = Report.Errors;
330                         TypeExpr d = e.ResolveAsTypeTerminal (type_resolve_ec);
331                         
332                         if (d == null || d.eclass != ExprClass.Type){
333                                 if (!silent && errors == Report.Errors){
334                                         Report.Error (246, loc, "Cannot find type `"+ e.ToString () +"'");
335                                 }
336                                 return null;
337                         }
338
339                         if (!d.CheckAccessLevel (this)) {
340                                 Report. Error (122, loc,  "`" + d.Name + "' " +
341                                        "is inaccessible because of its protection level");
342                                 return null;
343                         }
344
345                         return d.Type;
346                 }
347
348                 // <summary>
349                 //    Resolves the expression `e' for a type, and will recursively define
350                 //    types. 
351                 // </summary>
352                 public TypeExpr ResolveTypeExpr (Expression e, bool silent, Location loc)
353                 {
354                         if (type_resolve_ec == null)
355                                 type_resolve_ec = GetTypeResolveEmitContext (parent, loc);
356                         type_resolve_ec.loc = loc;
357                         type_resolve_ec.ContainerType = TypeBuilder;
358
359                         TypeExpr d = e.ResolveAsTypeTerminal (type_resolve_ec);
360                          
361                         if (d == null || d.eclass != ExprClass.Type){
362                                 if (!silent){
363                                         Report.Error (246, loc, "Cannot find type `"+ e +"'");
364                                 }
365                                 return null;
366                         }
367
368                         return d;
369                 }
370                 
371                 public bool CheckAccessLevel (Type check_type) 
372                 {
373                         if (check_type == TypeBuilder)
374                                 return true;
375                         
376                         TypeAttributes check_attr = check_type.Attributes & TypeAttributes.VisibilityMask;
377                         
378                         //
379                         // Broken Microsoft runtime, return public for arrays, no matter what 
380                         // the accessibility is for their underlying class, and they return 
381                         // NonPublic visibility for pointers
382                         //
383                         if (check_type.IsArray || check_type.IsPointer)
384                                 return CheckAccessLevel (TypeManager.GetElementType (check_type));
385
386                         switch (check_attr){
387                         case TypeAttributes.Public:
388                                 return true;
389
390                         case TypeAttributes.NotPublic:
391                                 //
392                                 // This test should probably use the declaringtype.
393                                 //
394                                 if (check_type.Assembly == TypeBuilder.Assembly){
395                                         return true;
396                                 }
397                                 return false;
398                                 
399                         case TypeAttributes.NestedPublic:
400                                 return true;
401
402                         case TypeAttributes.NestedPrivate:
403                                 string check_type_name = check_type.FullName;
404                                 string type_name = TypeBuilder.FullName;
405                                 
406                                 int cio = check_type_name.LastIndexOf ("+");
407                                 string container = check_type_name.Substring (0, cio);
408
409                                 //
410                                 // Check if the check_type is a nested class of the current type
411                                 //
412                                 if (check_type_name.StartsWith (type_name + "+")){
413                                         return true;
414                                 }
415                                 
416                                 if (type_name.StartsWith (container)){
417                                         return true;
418                                 }
419
420                                 return false;
421
422                         case TypeAttributes.NestedFamily:
423                                 //
424                                 // Only accessible to methods in current type or any subtypes
425                                 //
426                                 return FamilyAccessible (check_type);
427
428                         case TypeAttributes.NestedFamANDAssem:
429                                 return (check_type.Assembly == TypeBuilder.Assembly) &&
430                                         FamilyAccessible (check_type);
431
432                         case TypeAttributes.NestedFamORAssem:
433                                 return (check_type.Assembly == TypeBuilder.Assembly) ||
434                                         FamilyAccessible (check_type);
435
436                         case TypeAttributes.NestedAssembly:
437                                 return check_type.Assembly == TypeBuilder.Assembly;
438                         }
439
440                         Console.WriteLine ("HERE: " + check_attr);
441                         return false;
442
443                 }
444
445                 protected bool FamilyAccessible (Type check_type)
446                 {
447                         Type declaring = check_type.DeclaringType;
448                         if (TypeBuilder.IsSubclassOf (declaring))
449                                 return true;
450
451                         string check_type_name = check_type.FullName;
452                         string type_name = TypeBuilder.FullName;
453                         
454                         int cio = check_type_name.LastIndexOf ("+");
455                         string container = check_type_name.Substring (0, cio);
456                         
457                         //
458                         // Check if the check_type is a nested class of the current type
459                         //
460                         if (check_type_name.StartsWith (container + "+"))
461                                 return true;
462
463                         return false;
464                 }
465
466                 // Access level of a type.
467                 enum AccessLevel {
468                         Public                  = 0,
469                         ProtectedInternal       = 1,
470                         Internal                = 2,
471                         Protected               = 3,
472                         Private                 = 4
473                 }
474
475                 // Check whether `flags' denotes a more restricted access than `level'
476                 // and return the new level.
477                 static AccessLevel CheckAccessLevel (AccessLevel level, int flags)
478                 {
479                         AccessLevel old_level = level;
480
481                         if ((flags & Modifiers.INTERNAL) != 0) {
482                                 if ((flags & Modifiers.PROTECTED) != 0) {
483                                         if ((int) level < (int) AccessLevel.ProtectedInternal)
484                                                 level = AccessLevel.ProtectedInternal;
485                                 } else {
486                                         if ((int) level < (int) AccessLevel.Internal)
487                                                 level = AccessLevel.Internal;
488                                 }
489                         } else if ((flags & Modifiers.PROTECTED) != 0) {
490                                 if ((int) level < (int) AccessLevel.Protected)
491                                         level = AccessLevel.Protected;
492                         } else if ((flags & Modifiers.PRIVATE) != 0)
493                                 level = AccessLevel.Private;
494
495                         return level;
496                 }
497
498                 // Return the access level for a new member which is defined in the current
499                 // TypeContainer with access modifiers `flags'.
500                 AccessLevel GetAccessLevel (int flags)
501                 {
502                         if ((flags & Modifiers.PRIVATE) != 0)
503                                 return AccessLevel.Private;
504
505                         AccessLevel level;
506                         if (!IsTopLevel && (Parent != null))
507                                 level = Parent.GetAccessLevel (flags);
508                         else
509                                 level = AccessLevel.Public;
510
511                         return CheckAccessLevel (CheckAccessLevel (level, flags), ModFlags);
512                 }
513
514                 // Return the access level for type `t', but don't give more access than `flags'.
515                 static AccessLevel GetAccessLevel (Type t, int flags)
516                 {
517                         if (((flags & Modifiers.PRIVATE) != 0) || t.IsNestedPrivate)
518                                 return AccessLevel.Private;
519
520                         AccessLevel level;
521                         if (TypeManager.IsBuiltinType (t))
522                                 return AccessLevel.Public;
523                         else if ((t.DeclaringType != null) && (t != t.DeclaringType))
524                                 level = GetAccessLevel (t.DeclaringType, flags);
525                         else {
526                                 level = CheckAccessLevel (AccessLevel.Public, flags);
527                         }
528
529                         if (t.IsNestedPublic)
530                                 return level;
531
532                         if (t.IsNestedAssembly || t.IsNotPublic) {
533                                 if ((int) level < (int) AccessLevel.Internal)
534                                         level = AccessLevel.Internal;
535                         }
536
537                         if (t.IsNestedFamily) {
538                                 if ((int) level < (int) AccessLevel.Protected)
539                                         level = AccessLevel.Protected;
540                         }
541
542                         if (t.IsNestedFamORAssem) {
543                                 if ((int) level < (int) AccessLevel.ProtectedInternal)
544                                         level = AccessLevel.ProtectedInternal;
545                         }
546
547                         return level;
548                 }
549
550                 //
551                 // Returns true if `parent' is as accessible as the flags `flags'
552                 // given for this member.
553                 //
554                 public bool AsAccessible (Type parent, int flags)
555                 {
556                         while (parent.IsArray || parent.IsPointer || parent.IsByRef)
557                                 parent = TypeManager.GetElementType (parent);
558
559                         AccessLevel level = GetAccessLevel (flags);
560                         AccessLevel level2 = GetAccessLevel (parent, flags);
561
562                         return (int) level >= (int) level2;
563                 }
564                 
565                 static DoubleHash dh = new DoubleHash ();
566
567                 Type LookupInterfaceOrClass (string ns, string name, out bool error)
568                 {
569                         DeclSpace parent;
570                         Type t;
571                         object r;
572                         
573                         error = false;
574
575                         if (dh.Lookup (ns, name, out r))
576                                 t = (Type) r;
577                         else {
578                                 if (ns != ""){
579                                         if (Namespace.IsNamespace (ns)){
580                                                 string fullname = (ns != "") ? ns + "." + name : name;
581                                                 t = TypeManager.LookupType (fullname);
582                                         } else
583                                                 t = null;
584                                 } else
585                                         t = TypeManager.LookupType (name);
586                         }
587                         
588                         if (t != null)
589                                 return t;
590
591                         //
592                         // In case we are fed a composite name, normalize it.
593                         //
594                         int p = name.LastIndexOf ('.');
595                         if (p != -1){
596                                 ns = MakeFQN (ns, name.Substring (0, p));
597                                 name = name.Substring (p+1);
598                         }
599                         
600                         parent = RootContext.Tree.LookupByNamespace (ns, name);
601                         if (parent == null)
602                                 return null;
603
604                         t = parent.DefineType ();
605                         dh.Insert (ns, name, t);
606                         if (t == null){
607                                 error = true;
608                                 return null;
609                         }
610                         return t;
611                 }
612
613                 public static void Error_AmbiguousTypeReference (Location loc, string name, Type t1, Type t2)
614                 {
615                         Report.Error (104, loc,
616                                       String.Format ("`{0}' is an ambiguous reference ({1} or {2}) ", name,
617                                                      t1.FullName, t2.FullName));
618                 }
619
620                 /// <summary>
621                 ///   GetType is used to resolve type names at the DeclSpace level.
622                 ///   Use this to lookup class/struct bases, interface bases or 
623                 ///   delegate type references
624                 /// </summary>
625                 ///
626                 /// <remarks>
627                 ///   Contrast this to LookupType which is used inside method bodies to 
628                 ///   lookup types that have already been defined.  GetType is used
629                 ///   during the tree resolution process and potentially define
630                 ///   recursively the type
631                 /// </remarks>
632                 public Type FindType (Location loc, string name)
633                 {
634                         Type t;
635                         bool error;
636
637                         //
638                         // For the case the type we are looking for is nested within this one
639                         // or is in any base class
640                         //
641                         DeclSpace containing_ds = this;
642
643                         while (containing_ds != null){
644                                 Type container_type = containing_ds.TypeBuilder;
645                                 Type current_type = container_type;
646
647                                 while (current_type != null) {
648                                         string pre = current_type.FullName;
649
650                                         t = LookupInterfaceOrClass (pre, name, out error);
651                                         if (error)
652                                                 return null;
653                                 
654                                         if ((t != null) && containing_ds.CheckAccessLevel (t))
655                                                 return t;
656
657                                         current_type = current_type.BaseType;
658                                 }
659                                 containing_ds = containing_ds.Parent;
660                         }
661
662                         //
663                         // Attempt to lookup the class on our namespace and all it's implicit parents
664                         //
665                         for (NamespaceEntry ns = NamespaceEntry; ns != null; ns = ns.ImplicitParent) {
666                                 t = LookupInterfaceOrClass (ns.FullName, name, out error);
667                                 if (error)
668                                         return null;
669                                 
670                                 if (t != null) 
671                                         return t;
672                         }
673                         
674                         //
675                         // Attempt to do a direct unqualified lookup
676                         //
677                         t = LookupInterfaceOrClass ("", name, out error);
678                         if (error)
679                                 return null;
680                         
681                         if (t != null)
682                                 return t;
683                         
684                         //
685                         // Attempt to lookup the class on any of the `using'
686                         // namespaces
687                         //
688
689                         for (NamespaceEntry ns = NamespaceEntry; ns != null; ns = ns.Parent){
690
691                                 t = LookupInterfaceOrClass (ns.FullName, name, out error);
692                                 if (error)
693                                         return null;
694
695                                 if (t != null)
696                                         return t;
697
698                                 //
699                                 // Now check the using clause list
700                                 //
701                                 Type match = null;
702                                 foreach (Namespace using_ns in ns.GetUsingTable ()) {
703                                         match = LookupInterfaceOrClass (using_ns.Name, name, out error);
704                                         if (error)
705                                                 return null;
706
707                                         if (match != null){
708                                                 if (t != null){
709                                                         if (CheckAccessLevel (match)) {
710                                                                 Error_AmbiguousTypeReference (loc, name, t, match);
711                                                                 return null;
712                                                         }
713                                                         continue;
714                                                 }
715                                                 
716                                                 t = match;
717                                         }
718                                 }
719                                 if (t != null)
720                                         return t;
721                         }
722
723                         //Report.Error (246, Location, "Can not find type `"+name+"'");
724                         return null;
725                 }
726
727                 /// <remarks>
728                 ///   This function is broken and not what you're looking for.  It should only
729                 ///   be used while the type is still being created since it doesn't use the cache
730                 ///   and relies on the filter doing the member name check.
731                 /// </remarks>
732                 public abstract MemberList FindMembers (MemberTypes mt, BindingFlags bf,
733                                                         MemberFilter filter, object criteria);
734
735                 /// <remarks>
736                 ///   If we have a MemberCache, return it.  This property may return null if the
737                 ///   class doesn't have a member cache or while it's still being created.
738                 /// </remarks>
739                 public abstract MemberCache MemberCache {
740                         get;
741                 }
742         }
743
744         /// <summary>
745         ///   This is a readonly list of MemberInfo's.      
746         /// </summary>
747         public class MemberList : IList {
748                 public readonly IList List;
749                 int count;
750
751                 /// <summary>
752                 ///   Create a new MemberList from the given IList.
753                 /// </summary>
754                 public MemberList (IList list)
755                 {
756                         if (list != null)
757                                 this.List = list;
758                         else
759                                 this.List = new ArrayList ();
760                         count = List.Count;
761                 }
762
763                 /// <summary>
764                 ///   Concatenate the ILists `first' and `second' to a new MemberList.
765                 /// </summary>
766                 public MemberList (IList first, IList second)
767                 {
768                         ArrayList list = new ArrayList ();
769                         list.AddRange (first);
770                         list.AddRange (second);
771                         count = list.Count;
772                         List = list;
773                 }
774
775                 public static readonly MemberList Empty = new MemberList (new ArrayList ());
776
777                 /// <summary>
778                 ///   Cast the MemberList into a MemberInfo[] array.
779                 /// </summary>
780                 /// <remarks>
781                 ///   This is an expensive operation, only use it if it's really necessary.
782                 /// </remarks>
783                 public static explicit operator MemberInfo [] (MemberList list)
784                 {
785                         Timer.StartTimer (TimerType.MiscTimer);
786                         MemberInfo [] result = new MemberInfo [list.Count];
787                         list.CopyTo (result, 0);
788                         Timer.StopTimer (TimerType.MiscTimer);
789                         return result;
790                 }
791
792                 // ICollection
793
794                 public int Count {
795                         get {
796                                 return count;
797                         }
798                 }
799
800                 public bool IsSynchronized {
801                         get {
802                                 return List.IsSynchronized;
803                         }
804                 }
805
806                 public object SyncRoot {
807                         get {
808                                 return List.SyncRoot;
809                         }
810                 }
811
812                 public void CopyTo (Array array, int index)
813                 {
814                         List.CopyTo (array, index);
815                 }
816
817                 // IEnumerable
818
819                 public IEnumerator GetEnumerator ()
820                 {
821                         return List.GetEnumerator ();
822                 }
823
824                 // IList
825
826                 public bool IsFixedSize {
827                         get {
828                                 return true;
829                         }
830                 }
831
832                 public bool IsReadOnly {
833                         get {
834                                 return true;
835                         }
836                 }
837
838                 object IList.this [int index] {
839                         get {
840                                 return List [index];
841                         }
842
843                         set {
844                                 throw new NotSupportedException ();
845                         }
846                 }
847
848                 // FIXME: try to find out whether we can avoid the cast in this indexer.
849                 public MemberInfo this [int index] {
850                         get {
851                                 return (MemberInfo) List [index];
852                         }
853                 }
854
855                 public int Add (object value)
856                 {
857                         throw new NotSupportedException ();
858                 }
859
860                 public void Clear ()
861                 {
862                         throw new NotSupportedException ();
863                 }
864
865                 public bool Contains (object value)
866                 {
867                         return List.Contains (value);
868                 }
869
870                 public int IndexOf (object value)
871                 {
872                         return List.IndexOf (value);
873                 }
874
875                 public void Insert (int index, object value)
876                 {
877                         throw new NotSupportedException ();
878                 }
879
880                 public void Remove (object value)
881                 {
882                         throw new NotSupportedException ();
883                 }
884
885                 public void RemoveAt (int index)
886                 {
887                         throw new NotSupportedException ();
888                 }
889         }
890
891         /// <summary>
892         ///   This interface is used to get all members of a class when creating the
893         ///   member cache.  It must be implemented by all DeclSpace derivatives which
894         ///   want to support the member cache and by TypeHandle to get caching of
895         ///   non-dynamic types.
896         /// </summary>
897         public interface IMemberContainer {
898                 /// <summary>
899                 ///   The name of the IMemberContainer.  This is only used for
900                 ///   debugging purposes.
901                 /// </summary>
902                 string Name {
903                         get;
904                 }
905
906                 /// <summary>
907                 ///   The type of this IMemberContainer.
908                 /// </summary>
909                 Type Type {
910                         get;
911                 }
912
913                 /// <summary>
914                 ///   Returns the IMemberContainer of the parent class or null if this
915                 ///   is an interface or TypeManger.object_type.
916                 ///   This is used when creating the member cache for a class to get all
917                 ///   members from the parent class.
918                 /// </summary>
919                 IMemberContainer Parent {
920                         get;
921                 }
922
923                 /// <summary>
924                 ///   Whether this is an interface.
925                 /// </summary>
926                 bool IsInterface {
927                         get;
928                 }
929
930                 /// <summary>
931                 ///   Returns all members of this class with the corresponding MemberTypes
932                 ///   and BindingFlags.
933                 /// </summary>
934                 /// <remarks>
935                 ///   When implementing this method, make sure not to return any inherited
936                 ///   members and check the MemberTypes and BindingFlags properly.
937                 ///   Unfortunately, System.Reflection is lame and doesn't provide a way to
938                 ///   get the BindingFlags (static/non-static,public/non-public) in the
939                 ///   MemberInfo class, but the cache needs this information.  That's why
940                 ///   this method is called multiple times with different BindingFlags.
941                 /// </remarks>
942                 MemberList GetMembers (MemberTypes mt, BindingFlags bf);
943
944                 /// <summary>
945                 ///   Return the container's member cache.
946                 /// </summary>
947                 MemberCache MemberCache {
948                         get;
949                 }
950         }
951
952         /// <summary>
953         ///   The MemberCache is used by dynamic and non-dynamic types to speed up
954         ///   member lookups.  It has a member name based hash table; it maps each member
955         ///   name to a list of CacheEntry objects.  Each CacheEntry contains a MemberInfo
956         ///   and the BindingFlags that were initially used to get it.  The cache contains
957         ///   all members of the current class and all inherited members.  If this cache is
958         ///   for an interface types, it also contains all inherited members.
959         ///
960         ///   There are two ways to get a MemberCache:
961         ///   * if this is a dynamic type, lookup the corresponding DeclSpace and then
962         ///     use the DeclSpace.MemberCache property.
963         ///   * if this not a dynamic type, call TypeHandle.GetTypeHandle() to get a
964         ///     TypeHandle instance for the type and then use TypeHandle.MemberCache.
965         /// </summary>
966         public class MemberCache {
967                 public readonly IMemberContainer Container;
968                 protected Hashtable member_hash;
969                 protected Hashtable method_hash;
970                 protected Hashtable interface_hash;
971
972                 /// <summary>
973                 ///   Create a new MemberCache for the given IMemberContainer `container'.
974                 /// </summary>
975                 public MemberCache (IMemberContainer container)
976                 {
977                         this.Container = container;
978
979                         Timer.IncrementCounter (CounterType.MemberCache);
980                         Timer.StartTimer (TimerType.CacheInit);
981
982                         interface_hash = new Hashtable ();
983
984                         // If we have a parent class (we have a parent class unless we're
985                         // TypeManager.object_type), we deep-copy its MemberCache here.
986                         if (Container.IsInterface) {
987                                 MemberCache parent;
988                                 if (Container.Parent != null)
989                                         parent = Container.Parent.MemberCache;
990                                 else
991                                         parent = TypeHandle.ObjectType.MemberCache;
992                                 member_hash = SetupCacheForInterface (parent);
993                         } else if (Container.Parent != null)
994                                 member_hash = SetupCache (Container.Parent.MemberCache);
995                         else
996                                 member_hash = new Hashtable ();
997
998                         // If this is neither a dynamic type nor an interface, create a special
999                         // method cache with all declared and inherited methods.
1000                         Type type = container.Type;
1001                         if (!(type is TypeBuilder) && !type.IsInterface) {
1002                                 method_hash = new Hashtable ();
1003                                 AddMethods (type);
1004                         }
1005
1006                         // Add all members from the current class.
1007                         AddMembers (Container);
1008
1009                         Timer.StopTimer (TimerType.CacheInit);
1010                 }
1011
1012                 /// <summary>
1013                 ///   Bootstrap this member cache by doing a deep-copy of our parent.
1014                 /// </summary>
1015                 Hashtable SetupCache (MemberCache parent)
1016                 {
1017                         Hashtable hash = new Hashtable ();
1018
1019                         IDictionaryEnumerator it = parent.member_hash.GetEnumerator ();
1020                         while (it.MoveNext ()) {
1021                                 hash [it.Key] = ((ArrayList) it.Value).Clone ();
1022                         }
1023                                 
1024                         return hash;
1025                 }
1026
1027                 void AddInterfaces (MemberCache parent)
1028                 {
1029                         foreach (Type iface in parent.interface_hash.Keys) {
1030                                 if (!interface_hash.Contains (iface))
1031                                         interface_hash.Add (iface, true);
1032                         }
1033                 }
1034
1035                 /// <summary>
1036                 ///   Add the contents of `new_hash' to `hash'.
1037                 /// </summary>
1038                 void AddHashtable (Hashtable hash, Hashtable new_hash)
1039                 {
1040                         IDictionaryEnumerator it = new_hash.GetEnumerator ();
1041                         while (it.MoveNext ()) {
1042                                 ArrayList list = (ArrayList) hash [it.Key];
1043                                 if (list != null)
1044                                         list.AddRange ((ArrayList) it.Value);
1045                                 else
1046                                         hash [it.Key] = ((ArrayList) it.Value).Clone ();
1047                         }
1048                 }
1049
1050                 /// <summary>
1051                 ///   Bootstrap the member cache for an interface type.
1052                 ///   Type.GetMembers() won't return any inherited members for interface types,
1053                 ///   so we need to do this manually.  Interfaces also inherit from System.Object.
1054                 /// </summary>
1055                 Hashtable SetupCacheForInterface (MemberCache parent)
1056                 {
1057                         Hashtable hash = SetupCache (parent);
1058                         TypeExpr [] ifaces = TypeManager.GetInterfaces (Container.Type);
1059
1060                         foreach (TypeExpr iface in ifaces) {
1061                                 Type itype = iface.Type;
1062
1063                                 if (interface_hash.Contains (itype))
1064                                         continue;
1065                                 interface_hash.Add (itype, true);
1066
1067                                 IMemberContainer iface_container =
1068                                         TypeManager.LookupMemberContainer (itype);
1069
1070                                 MemberCache iface_cache = iface_container.MemberCache;
1071
1072                                 AddHashtable (hash, iface_cache.member_hash);
1073                                 AddInterfaces (iface_cache);
1074                         }
1075
1076                         return hash;
1077                 }
1078
1079                 /// <summary>
1080                 ///   Add all members from class `container' to the cache.
1081                 /// </summary>
1082                 void AddMembers (IMemberContainer container)
1083                 {
1084                         // We need to call AddMembers() with a single member type at a time
1085                         // to get the member type part of CacheEntry.EntryType right.
1086                         AddMembers (MemberTypes.Constructor, container);
1087                         AddMembers (MemberTypes.Field, container);
1088                         AddMembers (MemberTypes.Method, container);
1089                         AddMembers (MemberTypes.Property, container);
1090                         AddMembers (MemberTypes.Event, container);
1091                         // Nested types are returned by both Static and Instance searches.
1092                         AddMembers (MemberTypes.NestedType,
1093                                     BindingFlags.Static | BindingFlags.Public, container);
1094                         AddMembers (MemberTypes.NestedType,
1095                                     BindingFlags.Static | BindingFlags.NonPublic, container);
1096                 }
1097
1098                 void AddMembers (MemberTypes mt, IMemberContainer container)
1099                 {
1100                         AddMembers (mt, BindingFlags.Static | BindingFlags.Public, container);
1101                         AddMembers (mt, BindingFlags.Static | BindingFlags.NonPublic, container);
1102                         AddMembers (mt, BindingFlags.Instance | BindingFlags.Public, container);
1103                         AddMembers (mt, BindingFlags.Instance | BindingFlags.NonPublic, container);
1104                 }
1105
1106                 /// <summary>
1107                 ///   Add all members from class `container' with the requested MemberTypes and
1108                 ///   BindingFlags to the cache.  This method is called multiple times with different
1109                 ///   MemberTypes and BindingFlags.
1110                 /// </summary>
1111                 void AddMembers (MemberTypes mt, BindingFlags bf, IMemberContainer container)
1112                 {
1113                         MemberList members = container.GetMembers (mt, bf);
1114                         BindingFlags new_bf = (container == Container) ?
1115                                 bf | BindingFlags.DeclaredOnly : bf;
1116
1117                         foreach (MemberInfo member in members) {
1118                                 string name = member.Name;
1119
1120                                 // We use a name-based hash table of ArrayList's.
1121                                 ArrayList list = (ArrayList) member_hash [name];
1122                                 if (list == null) {
1123                                         list = new ArrayList ();
1124                                         member_hash.Add (name, list);
1125                                 }
1126
1127                                 // When this method is called for the current class, the list will
1128                                 // already contain all inherited members from our parent classes.
1129                                 // We cannot add new members in front of the list since this'd be an
1130                                 // expensive operation, that's why the list is sorted in reverse order
1131                                 // (ie. members from the current class are coming last).
1132                                 list.Add (new CacheEntry (container, member, mt, bf));
1133                         }
1134                 }
1135
1136                 /// <summary>
1137                 ///   Add all declared and inherited methods from class `type' to the method cache.
1138                 /// </summary>
1139                 void AddMethods (Type type)
1140                 {
1141                         AddMethods (BindingFlags.Static | BindingFlags.Public |
1142                                     BindingFlags.FlattenHierarchy, type);
1143                         AddMethods (BindingFlags.Static | BindingFlags.NonPublic |
1144                                     BindingFlags.FlattenHierarchy, type);
1145                         AddMethods (BindingFlags.Instance | BindingFlags.Public, type);
1146                         AddMethods (BindingFlags.Instance | BindingFlags.NonPublic, type);
1147                 }
1148
1149                 void AddMethods (BindingFlags bf, Type type)
1150                 {
1151                         MemberInfo [] members = type.GetMethods (bf);
1152
1153                         Array.Reverse (members);
1154                         
1155                         foreach (MethodBase member in members) {
1156                                 string name = member.Name;
1157
1158                                 // Varargs methods aren't allowed in C# code.
1159                                 if ((member.CallingConvention & CallingConventions.VarArgs) != 0)
1160                                         continue;
1161
1162                                 // We use a name-based hash table of ArrayList's.
1163                                 ArrayList list = (ArrayList) method_hash [name];
1164                                 if (list == null) {
1165                                         list = new ArrayList ();
1166                                         method_hash.Add (name, list);
1167                                 }
1168
1169                                 // Unfortunately, the elements returned by Type.GetMethods() aren't
1170                                 // sorted so we need to do this check for every member.
1171                                 BindingFlags new_bf = bf;
1172                                 if (member.DeclaringType == type)
1173                                         new_bf |= BindingFlags.DeclaredOnly;
1174
1175                                 list.Add (new CacheEntry (Container, member, MemberTypes.Method, new_bf));
1176                         }
1177
1178                         
1179                 }
1180
1181                 /// <summary>
1182                 ///   Compute and return a appropriate `EntryType' magic number for the given
1183                 ///   MemberTypes and BindingFlags.
1184                 /// </summary>
1185                 protected static EntryType GetEntryType (MemberTypes mt, BindingFlags bf)
1186                 {
1187                         EntryType type = EntryType.None;
1188
1189                         if ((mt & MemberTypes.Constructor) != 0)
1190                                 type |= EntryType.Constructor;
1191                         if ((mt & MemberTypes.Event) != 0)
1192                                 type |= EntryType.Event;
1193                         if ((mt & MemberTypes.Field) != 0)
1194                                 type |= EntryType.Field;
1195                         if ((mt & MemberTypes.Method) != 0)
1196                                 type |= EntryType.Method;
1197                         if ((mt & MemberTypes.Property) != 0)
1198                                 type |= EntryType.Property;
1199                         // Nested types are returned by static and instance searches.
1200                         if ((mt & MemberTypes.NestedType) != 0)
1201                                 type |= EntryType.NestedType | EntryType.Static | EntryType.Instance;
1202
1203                         if ((bf & BindingFlags.Instance) != 0)
1204                                 type |= EntryType.Instance;
1205                         if ((bf & BindingFlags.Static) != 0)
1206                                 type |= EntryType.Static;
1207                         if ((bf & BindingFlags.Public) != 0)
1208                                 type |= EntryType.Public;
1209                         if ((bf & BindingFlags.NonPublic) != 0)
1210                                 type |= EntryType.NonPublic;
1211                         if ((bf & BindingFlags.DeclaredOnly) != 0)
1212                                 type |= EntryType.Declared;
1213
1214                         return type;
1215                 }
1216
1217                 /// <summary>
1218                 ///   The `MemberTypes' enumeration type is a [Flags] type which means that it may
1219                 ///   denote multiple member types.  Returns true if the given flags value denotes a
1220                 ///   single member types.
1221                 /// </summary>
1222                 public static bool IsSingleMemberType (MemberTypes mt)
1223                 {
1224                         switch (mt) {
1225                         case MemberTypes.Constructor:
1226                         case MemberTypes.Event:
1227                         case MemberTypes.Field:
1228                         case MemberTypes.Method:
1229                         case MemberTypes.Property:
1230                         case MemberTypes.NestedType:
1231                                 return true;
1232
1233                         default:
1234                                 return false;
1235                         }
1236                 }
1237
1238                 /// <summary>
1239                 ///   We encode the MemberTypes and BindingFlags of each members in a "magic"
1240                 ///   number to speed up the searching process.
1241                 /// </summary>
1242                 [Flags]
1243                 protected enum EntryType {
1244                         None            = 0x000,
1245
1246                         Instance        = 0x001,
1247                         Static          = 0x002,
1248                         MaskStatic      = Instance|Static,
1249
1250                         Public          = 0x004,
1251                         NonPublic       = 0x008,
1252                         MaskProtection  = Public|NonPublic,
1253
1254                         Declared        = 0x010,
1255
1256                         Constructor     = 0x020,
1257                         Event           = 0x040,
1258                         Field           = 0x080,
1259                         Method          = 0x100,
1260                         Property        = 0x200,
1261                         NestedType      = 0x400,
1262
1263                         MaskType        = Constructor|Event|Field|Method|Property|NestedType
1264                 }
1265
1266                 protected struct CacheEntry {
1267                         public readonly IMemberContainer Container;
1268                         public readonly EntryType EntryType;
1269                         public readonly MemberInfo Member;
1270
1271                         public CacheEntry (IMemberContainer container, MemberInfo member,
1272                                            MemberTypes mt, BindingFlags bf)
1273                         {
1274                                 this.Container = container;
1275                                 this.Member = member;
1276                                 this.EntryType = GetEntryType (mt, bf);
1277                         }
1278                 }
1279
1280                 /// <summary>
1281                 ///   This is called each time we're walking up one level in the class hierarchy
1282                 ///   and checks whether we can abort the search since we've already found what
1283                 ///   we were looking for.
1284                 /// </summary>
1285                 protected bool DoneSearching (ArrayList list)
1286                 {
1287                         //
1288                         // We've found exactly one member in the current class and it's not
1289                         // a method or constructor.
1290                         //
1291                         if (list.Count == 1 && !(list [0] is MethodBase))
1292                                 return true;
1293
1294                         //
1295                         // Multiple properties: we query those just to find out the indexer
1296                         // name
1297                         //
1298                         if ((list.Count > 0) && (list [0] is PropertyInfo))
1299                                 return true;
1300
1301                         return false;
1302                 }
1303
1304                 /// <summary>
1305                 ///   Looks up members with name `name'.  If you provide an optional
1306                 ///   filter function, it'll only be called with members matching the
1307                 ///   requested member name.
1308                 ///
1309                 ///   This method will try to use the cache to do the lookup if possible.
1310                 ///
1311                 ///   Unlike other FindMembers implementations, this method will always
1312                 ///   check all inherited members - even when called on an interface type.
1313                 ///
1314                 ///   If you know that you're only looking for methods, you should use
1315                 ///   MemberTypes.Method alone since this speeds up the lookup a bit.
1316                 ///   When doing a method-only search, it'll try to use a special method
1317                 ///   cache (unless it's a dynamic type or an interface) and the returned
1318                 ///   MemberInfo's will have the correct ReflectedType for inherited methods.
1319                 ///   The lookup process will automatically restart itself in method-only
1320                 ///   search mode if it discovers that it's about to return methods.
1321                 /// </summary>
1322                 ArrayList global = new ArrayList ();
1323                 bool using_global = false;
1324                 
1325                 public MemberList FindMembers (MemberTypes mt, BindingFlags bf, string name,
1326                                                MemberFilter filter, object criteria)
1327                 {
1328                         if (using_global)
1329                                 throw new Exception ();
1330                         
1331                         bool declared_only = (bf & BindingFlags.DeclaredOnly) != 0;
1332                         bool method_search = mt == MemberTypes.Method;
1333                         // If we have a method cache and we aren't already doing a method-only search,
1334                         // then we restart a method search if the first match is a method.
1335                         bool do_method_search = !method_search && (method_hash != null);
1336
1337                         ArrayList applicable;
1338
1339                         // If this is a method-only search, we try to use the method cache if
1340                         // possible; a lookup in the method cache will return a MemberInfo with
1341                         // the correct ReflectedType for inherited methods.
1342                         
1343                         if (method_search && (method_hash != null))
1344                                 applicable = (ArrayList) method_hash [name];
1345                         else
1346                                 applicable = (ArrayList) member_hash [name];
1347                         
1348                         if (applicable == null)
1349                                 return MemberList.Empty;
1350
1351                         //
1352                         // 32  slots gives 53 rss/54 size
1353                         // 2/4 slots gives 55 rss
1354                         //
1355                         // Strange: from 25,000 calls, only 1,800
1356                         // are above 2.  Why does this impact it?
1357                         //
1358                         global.Clear ();
1359                         using_global = true;
1360
1361                         Timer.StartTimer (TimerType.CachedLookup);
1362
1363                         EntryType type = GetEntryType (mt, bf);
1364
1365                         IMemberContainer current = Container;
1366
1367                         // `applicable' is a list of all members with the given member name `name'
1368                         // in the current class and all its parent classes.  The list is sorted in
1369                         // reverse order due to the way how the cache is initialy created (to speed
1370                         // things up, we're doing a deep-copy of our parent).
1371
1372                         for (int i = applicable.Count-1; i >= 0; i--) {
1373                                 CacheEntry entry = (CacheEntry) applicable [i];
1374
1375                                 // This happens each time we're walking one level up in the class
1376                                 // hierarchy.  If we're doing a DeclaredOnly search, we must abort
1377                                 // the first time this happens (this may already happen in the first
1378                                 // iteration of this loop if there are no members with the name we're
1379                                 // looking for in the current class).
1380                                 if (entry.Container != current) {
1381                                         if (declared_only || DoneSearching (global))
1382                                                 break;
1383
1384                                         current = entry.Container;
1385                                 }
1386
1387                                 // Is the member of the correct type ?
1388                                 if ((entry.EntryType & type & EntryType.MaskType) == 0)
1389                                         continue;
1390
1391                                 // Is the member static/non-static ?
1392                                 if ((entry.EntryType & type & EntryType.MaskStatic) == 0)
1393                                         continue;
1394
1395                                 // Apply the filter to it.
1396                                 if (filter (entry.Member, criteria)) {
1397                                         if ((entry.EntryType & EntryType.MaskType) != EntryType.Method)
1398                                                 do_method_search = false;
1399                                         global.Add (entry.Member);
1400                                 }
1401                         }
1402
1403                         Timer.StopTimer (TimerType.CachedLookup);
1404
1405                         // If we have a method cache and we aren't already doing a method-only
1406                         // search, we restart in method-only search mode if the first match is
1407                         // a method.  This ensures that we return a MemberInfo with the correct
1408                         // ReflectedType for inherited methods.
1409                         if (do_method_search && (global.Count > 0)){
1410                                 using_global = false;
1411
1412                                 return FindMembers (MemberTypes.Method, bf, name, filter, criteria);
1413                         }
1414
1415                         using_global = false;
1416                         MemberInfo [] copy = new MemberInfo [global.Count];
1417                         global.CopyTo (copy);
1418                         return new MemberList (copy);
1419                 }
1420         }
1421 }