* HtmlControl.cs: Make disabled an attribute of the control so it
[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                         return type_resolve_ec;
315                 }
316
317                 // <summary>
318                 //    Looks up the type, as parsed into the expression `e' 
319                 // </summary>
320                 public Type ResolveType (Expression e, bool silent, Location loc)
321                 {
322                         if (type_resolve_ec == null)
323                                 type_resolve_ec = GetTypeResolveEmitContext (parent, loc);
324                         type_resolve_ec.loc = loc;
325                         type_resolve_ec.ContainerType = TypeBuilder;
326
327                         int errors = Report.Errors;
328                         Expression d = e.ResolveAsTypeTerminal (type_resolve_ec);
329                         
330                         if (d == null || d.eclass != ExprClass.Type){
331                                 if (!silent && errors == Report.Errors){
332                                         Report.Error (246, loc, "Cannot find type `"+ e.ToString () +"'");
333                                 }
334                                 return null;
335                         }
336
337                         if (!CheckAccessLevel (d.Type)) {
338                                 Report. Error (122, loc,  "`" + d.Type + "' " +
339                                        "is inaccessible because of its protection level");
340                                 return null;
341                         }
342
343                         return d.Type;
344                 }
345
346                 // <summary>
347                 //    Resolves the expression `e' for a type, and will recursively define
348                 //    types. 
349                 // </summary>
350                 public Expression ResolveTypeExpr (Expression e, bool silent, Location loc)
351                 {
352                         if (type_resolve_ec == null)
353                                 type_resolve_ec = GetTypeResolveEmitContext (parent, loc);
354                         type_resolve_ec.loc = loc;
355                         type_resolve_ec.ContainerType = TypeBuilder;
356
357                         Expression d = e.ResolveAsTypeTerminal (type_resolve_ec);
358                          
359                         if (d == null || d.eclass != ExprClass.Type){
360                                 if (!silent){
361                                         Report.Error (246, loc, "Cannot find type `"+ e +"'");
362                                 }
363                                 return null;
364                         }
365
366                         return d;
367                 }
368                 
369                 public bool CheckAccessLevel (Type check_type) 
370                 {
371                         if (check_type == TypeBuilder)
372                                 return true;
373                         
374                         TypeAttributes check_attr = check_type.Attributes & TypeAttributes.VisibilityMask;
375                         
376                         //
377                         // Broken Microsoft runtime, return public for arrays, no matter what 
378                         // the accessibility is for their underlying class, and they return 
379                         // NonPublic visibility for pointers
380                         //
381                         if (check_type.IsArray || check_type.IsPointer)
382                                 return CheckAccessLevel (TypeManager.GetElementType (check_type));
383
384                         switch (check_attr){
385                         case TypeAttributes.Public:
386                                 return true;
387
388                         case TypeAttributes.NotPublic:
389                                 //
390                                 // This test should probably use the declaringtype.
391                                 //
392                                 if (check_type.Assembly == TypeBuilder.Assembly){
393                                         return true;
394                                 }
395                                 return false;
396                                 
397                         case TypeAttributes.NestedPublic:
398                                 return true;
399
400                         case TypeAttributes.NestedPrivate:
401                                 string check_type_name = check_type.FullName;
402                                 string type_name = TypeBuilder.FullName;
403                                 
404                                 int cio = check_type_name.LastIndexOf ("+");
405                                 string container = check_type_name.Substring (0, cio);
406
407                                 //
408                                 // Check if the check_type is a nested class of the current type
409                                 //
410                                 if (check_type_name.StartsWith (type_name + "+")){
411                                         return true;
412                                 }
413                                 
414                                 if (type_name.StartsWith (container)){
415                                         return true;
416                                 }
417
418                                 return false;
419
420                         case TypeAttributes.NestedFamily:
421                                 //
422                                 // Only accessible to methods in current type or any subtypes
423                                 //
424                                 return FamilyAccessible (check_type);
425
426                         case TypeAttributes.NestedFamANDAssem:
427                                 return (check_type.Assembly == TypeBuilder.Assembly) &&
428                                         FamilyAccessible (check_type);
429
430                         case TypeAttributes.NestedFamORAssem:
431                                 return (check_type.Assembly == TypeBuilder.Assembly) ||
432                                         FamilyAccessible (check_type);
433
434                         case TypeAttributes.NestedAssembly:
435                                 return check_type.Assembly == TypeBuilder.Assembly;
436                         }
437
438                         Console.WriteLine ("HERE: " + check_attr);
439                         return false;
440
441                 }
442
443                 protected bool FamilyAccessible (Type check_type)
444                 {
445                         Type declaring = check_type.DeclaringType;
446                         if (TypeBuilder.IsSubclassOf (declaring))
447                                 return true;
448
449                         string check_type_name = check_type.FullName;
450                         string type_name = TypeBuilder.FullName;
451                         
452                         int cio = check_type_name.LastIndexOf ("+");
453                         string container = check_type_name.Substring (0, cio);
454                         
455                         //
456                         // Check if the check_type is a nested class of the current type
457                         //
458                         if (check_type_name.StartsWith (container + "+"))
459                                 return true;
460
461                         return false;
462                 }
463
464                 // Access level of a type.
465                 enum AccessLevel {
466                         Public                  = 0,
467                         ProtectedInternal       = 1,
468                         Internal                = 2,
469                         Protected               = 3,
470                         Private                 = 4
471                 }
472
473                 // Check whether `flags' denotes a more restricted access than `level'
474                 // and return the new level.
475                 static AccessLevel CheckAccessLevel (AccessLevel level, int flags)
476                 {
477                         AccessLevel old_level = level;
478
479                         if ((flags & Modifiers.INTERNAL) != 0) {
480                                 if ((flags & Modifiers.PROTECTED) != 0) {
481                                         if ((int) level < (int) AccessLevel.ProtectedInternal)
482                                                 level = AccessLevel.ProtectedInternal;
483                                 } else {
484                                         if ((int) level < (int) AccessLevel.Internal)
485                                                 level = AccessLevel.Internal;
486                                 }
487                         } else if ((flags & Modifiers.PROTECTED) != 0) {
488                                 if ((int) level < (int) AccessLevel.Protected)
489                                         level = AccessLevel.Protected;
490                         } else if ((flags & Modifiers.PRIVATE) != 0)
491                                 level = AccessLevel.Private;
492
493                         return level;
494                 }
495
496                 // Return the access level for a new member which is defined in the current
497                 // TypeContainer with access modifiers `flags'.
498                 AccessLevel GetAccessLevel (int flags)
499                 {
500                         if ((flags & Modifiers.PRIVATE) != 0)
501                                 return AccessLevel.Private;
502
503                         AccessLevel level;
504                         if (!IsTopLevel && (Parent != null))
505                                 level = Parent.GetAccessLevel (flags);
506                         else
507                                 level = AccessLevel.Public;
508
509                         return CheckAccessLevel (CheckAccessLevel (level, flags), ModFlags);
510                 }
511
512                 // Return the access level for type `t', but don't give more access than `flags'.
513                 static AccessLevel GetAccessLevel (Type t, int flags)
514                 {
515                         if (((flags & Modifiers.PRIVATE) != 0) || t.IsNestedPrivate)
516                                 return AccessLevel.Private;
517
518                         AccessLevel level;
519                         if (TypeManager.IsBuiltinType (t))
520                                 return AccessLevel.Public;
521                         else if ((t.DeclaringType != null) && (t != t.DeclaringType))
522                                 level = GetAccessLevel (t.DeclaringType, flags);
523                         else {
524                                 level = CheckAccessLevel (AccessLevel.Public, flags);
525                         }
526
527                         if (t.IsNestedPublic)
528                                 return level;
529
530                         if (t.IsNestedAssembly || t.IsNotPublic) {
531                                 if ((int) level < (int) AccessLevel.Internal)
532                                         level = AccessLevel.Internal;
533                         }
534
535                         if (t.IsNestedFamily) {
536                                 if ((int) level < (int) AccessLevel.Protected)
537                                         level = AccessLevel.Protected;
538                         }
539
540                         if (t.IsNestedFamORAssem) {
541                                 if ((int) level < (int) AccessLevel.ProtectedInternal)
542                                         level = AccessLevel.ProtectedInternal;
543                         }
544
545                         return level;
546                 }
547
548                 //
549                 // Returns true if `parent' is as accessible as the flags `flags'
550                 // given for this member.
551                 //
552                 public bool AsAccessible (Type parent, int flags)
553                 {
554                         while (parent.IsArray || parent.IsPointer || parent.IsByRef)
555                                 parent = TypeManager.GetElementType (parent);
556
557                         AccessLevel level = GetAccessLevel (flags);
558                         AccessLevel level2 = GetAccessLevel (parent, flags);
559
560                         return (int) level >= (int) level2;
561                 }
562                 
563                 static DoubleHash dh = new DoubleHash ();
564
565                 Type LookupInterfaceOrClass (string ns, string name, out bool error)
566                 {
567                         DeclSpace parent;
568                         Type t;
569                         object r;
570                         
571                         error = false;
572
573                         if (dh.Lookup (ns, name, out r))
574                                 t = (Type) r;
575                         else {
576                                 if (ns != ""){
577                                         if (Namespace.IsNamespace (ns)){
578                                                 string fullname = (ns != "") ? ns + "." + name : name;
579                                                 t = TypeManager.LookupType (fullname);
580                                         } else
581                                                 t = null;
582                                 } else
583                                         t = TypeManager.LookupType (name);
584                         }
585                         
586                         if (t != null)
587                                 return t;
588
589                         //
590                         // In case we are fed a composite name, normalize it.
591                         //
592                         int p = name.LastIndexOf ('.');
593                         if (p != -1){
594                                 ns = MakeFQN (ns, name.Substring (0, p));
595                                 name = name.Substring (p+1);
596                         }
597                         
598                         parent = RootContext.Tree.LookupByNamespace (ns, name);
599                         if (parent == null)
600                                 return null;
601
602                         t = parent.DefineType ();
603                         dh.Insert (ns, name, t);
604                         if (t == null){
605                                 error = true;
606                                 return null;
607                         }
608                         return t;
609                 }
610
611                 public static void Error_AmbiguousTypeReference (Location loc, string name, Type t1, Type t2)
612                 {
613                         Report.Error (104, loc,
614                                       String.Format ("`{0}' is an ambiguous reference ({1} or {2}) ", name,
615                                                      t1.FullName, t2.FullName));
616                 }
617
618                 /// <summary>
619                 ///   GetType is used to resolve type names at the DeclSpace level.
620                 ///   Use this to lookup class/struct bases, interface bases or 
621                 ///   delegate type references
622                 /// </summary>
623                 ///
624                 /// <remarks>
625                 ///   Contrast this to LookupType which is used inside method bodies to 
626                 ///   lookup types that have already been defined.  GetType is used
627                 ///   during the tree resolution process and potentially define
628                 ///   recursively the type
629                 /// </remarks>
630                 public Type FindType (Location loc, string name)
631                 {
632                         Type t;
633                         bool error;
634
635                         //
636                         // For the case the type we are looking for is nested within this one
637                         // or is in any base class
638                         //
639                         DeclSpace containing_ds = this;
640
641                         while (containing_ds != null){
642                                 Type container_type = containing_ds.TypeBuilder;
643                                 Type current_type = container_type;
644
645                                 while (current_type != null) {
646                                         string pre = current_type.FullName;
647
648                                         t = LookupInterfaceOrClass (pre, name, out error);
649                                         if (error)
650                                                 return null;
651                                 
652                                         if ((t != null) && containing_ds.CheckAccessLevel (t))
653                                                 return t;
654
655                                         current_type = current_type.BaseType;
656                                 }
657                                 containing_ds = containing_ds.Parent;
658                         }
659
660                         //
661                         // Attempt to lookup the class on our namespace and all it's implicit parents
662                         //
663                         for (NamespaceEntry ns = NamespaceEntry; ns != null; ns = ns.ImplicitParent) {
664                                 t = LookupInterfaceOrClass (ns.FullName, name, out error);
665                                 if (error)
666                                         return null;
667                                 
668                                 if (t != null) 
669                                         return t;
670                         }
671                         
672                         //
673                         // Attempt to do a direct unqualified lookup
674                         //
675                         t = LookupInterfaceOrClass ("", name, out error);
676                         if (error)
677                                 return null;
678                         
679                         if (t != null)
680                                 return t;
681                         
682                         //
683                         // Attempt to lookup the class on any of the `using'
684                         // namespaces
685                         //
686
687                         for (NamespaceEntry ns = NamespaceEntry; ns != null; ns = ns.Parent){
688
689                                 t = LookupInterfaceOrClass (ns.FullName, name, out error);
690                                 if (error)
691                                         return null;
692
693                                 if (t != null)
694                                         return t;
695
696                                 //
697                                 // Now check the using clause list
698                                 //
699                                 Type match = null;
700                                 foreach (Namespace using_ns in ns.GetUsingTable ()) {
701                                         match = LookupInterfaceOrClass (using_ns.Name, name, out error);
702                                         if (error)
703                                                 return null;
704
705                                         if (match != null){
706                                                 if (t != null){
707                                                         if (CheckAccessLevel (match)) {
708                                                                 Error_AmbiguousTypeReference (loc, name, t, match);
709                                                                 return null;
710                                                         }
711                                                         continue;
712                                                 }
713                                                 
714                                                 t = match;
715                                         }
716                                 }
717                                 if (t != null)
718                                         return t;
719                         }
720
721                         //Report.Error (246, Location, "Can not find type `"+name+"'");
722                         return null;
723                 }
724
725                 /// <remarks>
726                 ///   This function is broken and not what you're looking for.  It should only
727                 ///   be used while the type is still being created since it doesn't use the cache
728                 ///   and relies on the filter doing the member name check.
729                 /// </remarks>
730                 public abstract MemberList FindMembers (MemberTypes mt, BindingFlags bf,
731                                                         MemberFilter filter, object criteria);
732
733                 /// <remarks>
734                 ///   If we have a MemberCache, return it.  This property may return null if the
735                 ///   class doesn't have a member cache or while it's still being created.
736                 /// </remarks>
737                 public abstract MemberCache MemberCache {
738                         get;
739                 }
740         }
741
742         /// <summary>
743         ///   This is a readonly list of MemberInfo's.      
744         /// </summary>
745         public class MemberList : IList {
746                 public readonly IList List;
747                 int count;
748
749                 /// <summary>
750                 ///   Create a new MemberList from the given IList.
751                 /// </summary>
752                 public MemberList (IList list)
753                 {
754                         if (list != null)
755                                 this.List = list;
756                         else
757                                 this.List = new ArrayList ();
758                         count = List.Count;
759                 }
760
761                 /// <summary>
762                 ///   Concatenate the ILists `first' and `second' to a new MemberList.
763                 /// </summary>
764                 public MemberList (IList first, IList second)
765                 {
766                         ArrayList list = new ArrayList ();
767                         list.AddRange (first);
768                         list.AddRange (second);
769                         count = list.Count;
770                         List = list;
771                 }
772
773                 public static readonly MemberList Empty = new MemberList (new ArrayList ());
774
775                 /// <summary>
776                 ///   Cast the MemberList into a MemberInfo[] array.
777                 /// </summary>
778                 /// <remarks>
779                 ///   This is an expensive operation, only use it if it's really necessary.
780                 /// </remarks>
781                 public static explicit operator MemberInfo [] (MemberList list)
782                 {
783                         Timer.StartTimer (TimerType.MiscTimer);
784                         MemberInfo [] result = new MemberInfo [list.Count];
785                         list.CopyTo (result, 0);
786                         Timer.StopTimer (TimerType.MiscTimer);
787                         return result;
788                 }
789
790                 // ICollection
791
792                 public int Count {
793                         get {
794                                 return count;
795                         }
796                 }
797
798                 public bool IsSynchronized {
799                         get {
800                                 return List.IsSynchronized;
801                         }
802                 }
803
804                 public object SyncRoot {
805                         get {
806                                 return List.SyncRoot;
807                         }
808                 }
809
810                 public void CopyTo (Array array, int index)
811                 {
812                         List.CopyTo (array, index);
813                 }
814
815                 // IEnumerable
816
817                 public IEnumerator GetEnumerator ()
818                 {
819                         return List.GetEnumerator ();
820                 }
821
822                 // IList
823
824                 public bool IsFixedSize {
825                         get {
826                                 return true;
827                         }
828                 }
829
830                 public bool IsReadOnly {
831                         get {
832                                 return true;
833                         }
834                 }
835
836                 object IList.this [int index] {
837                         get {
838                                 return List [index];
839                         }
840
841                         set {
842                                 throw new NotSupportedException ();
843                         }
844                 }
845
846                 // FIXME: try to find out whether we can avoid the cast in this indexer.
847                 public MemberInfo this [int index] {
848                         get {
849                                 return (MemberInfo) List [index];
850                         }
851                 }
852
853                 public int Add (object value)
854                 {
855                         throw new NotSupportedException ();
856                 }
857
858                 public void Clear ()
859                 {
860                         throw new NotSupportedException ();
861                 }
862
863                 public bool Contains (object value)
864                 {
865                         return List.Contains (value);
866                 }
867
868                 public int IndexOf (object value)
869                 {
870                         return List.IndexOf (value);
871                 }
872
873                 public void Insert (int index, object value)
874                 {
875                         throw new NotSupportedException ();
876                 }
877
878                 public void Remove (object value)
879                 {
880                         throw new NotSupportedException ();
881                 }
882
883                 public void RemoveAt (int index)
884                 {
885                         throw new NotSupportedException ();
886                 }
887         }
888
889         /// <summary>
890         ///   This interface is used to get all members of a class when creating the
891         ///   member cache.  It must be implemented by all DeclSpace derivatives which
892         ///   want to support the member cache and by TypeHandle to get caching of
893         ///   non-dynamic types.
894         /// </summary>
895         public interface IMemberContainer {
896                 /// <summary>
897                 ///   The name of the IMemberContainer.  This is only used for
898                 ///   debugging purposes.
899                 /// </summary>
900                 string Name {
901                         get;
902                 }
903
904                 /// <summary>
905                 ///   The type of this IMemberContainer.
906                 /// </summary>
907                 Type Type {
908                         get;
909                 }
910
911                 /// <summary>
912                 ///   Returns the IMemberContainer of the parent class or null if this
913                 ///   is an interface or TypeManger.object_type.
914                 ///   This is used when creating the member cache for a class to get all
915                 ///   members from the parent class.
916                 /// </summary>
917                 IMemberContainer Parent {
918                         get;
919                 }
920
921                 /// <summary>
922                 ///   Whether this is an interface.
923                 /// </summary>
924                 bool IsInterface {
925                         get;
926                 }
927
928                 /// <summary>
929                 ///   Returns all members of this class with the corresponding MemberTypes
930                 ///   and BindingFlags.
931                 /// </summary>
932                 /// <remarks>
933                 ///   When implementing this method, make sure not to return any inherited
934                 ///   members and check the MemberTypes and BindingFlags properly.
935                 ///   Unfortunately, System.Reflection is lame and doesn't provide a way to
936                 ///   get the BindingFlags (static/non-static,public/non-public) in the
937                 ///   MemberInfo class, but the cache needs this information.  That's why
938                 ///   this method is called multiple times with different BindingFlags.
939                 /// </remarks>
940                 MemberList GetMembers (MemberTypes mt, BindingFlags bf);
941
942                 /// <summary>
943                 ///   Return the container's member cache.
944                 /// </summary>
945                 MemberCache MemberCache {
946                         get;
947                 }
948         }
949
950         /// <summary>
951         ///   The MemberCache is used by dynamic and non-dynamic types to speed up
952         ///   member lookups.  It has a member name based hash table; it maps each member
953         ///   name to a list of CacheEntry objects.  Each CacheEntry contains a MemberInfo
954         ///   and the BindingFlags that were initially used to get it.  The cache contains
955         ///   all members of the current class and all inherited members.  If this cache is
956         ///   for an interface types, it also contains all inherited members.
957         ///
958         ///   There are two ways to get a MemberCache:
959         ///   * if this is a dynamic type, lookup the corresponding DeclSpace and then
960         ///     use the DeclSpace.MemberCache property.
961         ///   * if this not a dynamic type, call TypeHandle.GetTypeHandle() to get a
962         ///     TypeHandle instance for the type and then use TypeHandle.MemberCache.
963         /// </summary>
964         public class MemberCache {
965                 public readonly IMemberContainer Container;
966                 protected Hashtable member_hash;
967                 protected Hashtable method_hash;
968                 protected Hashtable interface_hash;
969
970                 /// <summary>
971                 ///   Create a new MemberCache for the given IMemberContainer `container'.
972                 /// </summary>
973                 public MemberCache (IMemberContainer container)
974                 {
975                         this.Container = container;
976
977                         Timer.IncrementCounter (CounterType.MemberCache);
978                         Timer.StartTimer (TimerType.CacheInit);
979
980                         interface_hash = new Hashtable ();
981
982                         // If we have a parent class (we have a parent class unless we're
983                         // TypeManager.object_type), we deep-copy its MemberCache here.
984                         if (Container.IsInterface) {
985                                 MemberCache parent;
986                                 if (Container.Parent != null)
987                                         parent = Container.Parent.MemberCache;
988                                 else
989                                         parent = TypeHandle.ObjectType.MemberCache;
990                                 member_hash = SetupCacheForInterface (parent);
991                         } else if (Container.Parent != null)
992                                 member_hash = SetupCache (Container.Parent.MemberCache);
993                         else
994                                 member_hash = new Hashtable ();
995
996                         // If this is neither a dynamic type nor an interface, create a special
997                         // method cache with all declared and inherited methods.
998                         Type type = container.Type;
999                         if (!(type is TypeBuilder) && !type.IsInterface) {
1000                                 method_hash = new Hashtable ();
1001                                 AddMethods (type);
1002                         }
1003
1004                         // Add all members from the current class.
1005                         AddMembers (Container);
1006
1007                         Timer.StopTimer (TimerType.CacheInit);
1008                 }
1009
1010                 /// <summary>
1011                 ///   Bootstrap this member cache by doing a deep-copy of our parent.
1012                 /// </summary>
1013                 Hashtable SetupCache (MemberCache parent)
1014                 {
1015                         Hashtable hash = new Hashtable ();
1016
1017                         IDictionaryEnumerator it = parent.member_hash.GetEnumerator ();
1018                         while (it.MoveNext ()) {
1019                                 hash [it.Key] = ((ArrayList) it.Value).Clone ();
1020                         }
1021                                 
1022                         return hash;
1023                 }
1024
1025                 void AddInterfaces (MemberCache parent)
1026                 {
1027                         foreach (Type iface in parent.interface_hash.Keys) {
1028                                 if (!interface_hash.Contains (iface))
1029                                         interface_hash.Add (iface, true);
1030                         }
1031                 }
1032
1033                 /// <summary>
1034                 ///   Add the contents of `new_hash' to `hash'.
1035                 /// </summary>
1036                 void AddHashtable (Hashtable hash, Hashtable new_hash)
1037                 {
1038                         IDictionaryEnumerator it = new_hash.GetEnumerator ();
1039                         while (it.MoveNext ()) {
1040                                 ArrayList list = (ArrayList) hash [it.Key];
1041                                 if (list != null)
1042                                         list.AddRange ((ArrayList) it.Value);
1043                                 else
1044                                         hash [it.Key] = ((ArrayList) it.Value).Clone ();
1045                         }
1046                 }
1047
1048                 /// <summary>
1049                 ///   Bootstrap the member cache for an interface type.
1050                 ///   Type.GetMembers() won't return any inherited members for interface types,
1051                 ///   so we need to do this manually.  Interfaces also inherit from System.Object.
1052                 /// </summary>
1053                 Hashtable SetupCacheForInterface (MemberCache parent)
1054                 {
1055                         Hashtable hash = SetupCache (parent);
1056                         Type [] ifaces = TypeManager.GetInterfaces (Container.Type);
1057
1058                         foreach (Type iface in ifaces) {
1059                                 if (interface_hash.Contains (iface))
1060                                         continue;
1061                                 interface_hash.Add (iface, true);
1062
1063                                 IMemberContainer iface_container =
1064                                         TypeManager.LookupMemberContainer (iface);
1065
1066                                 MemberCache iface_cache = iface_container.MemberCache;
1067
1068                                 AddHashtable (hash, iface_cache.member_hash);
1069                                 AddInterfaces (iface_cache);
1070                         }
1071
1072                         return hash;
1073                 }
1074
1075                 /// <summary>
1076                 ///   Add all members from class `container' to the cache.
1077                 /// </summary>
1078                 void AddMembers (IMemberContainer container)
1079                 {
1080                         // We need to call AddMembers() with a single member type at a time
1081                         // to get the member type part of CacheEntry.EntryType right.
1082                         AddMembers (MemberTypes.Constructor, container);
1083                         AddMembers (MemberTypes.Field, container);
1084                         AddMembers (MemberTypes.Method, container);
1085                         AddMembers (MemberTypes.Property, container);
1086                         AddMembers (MemberTypes.Event, container);
1087                         // Nested types are returned by both Static and Instance searches.
1088                         AddMembers (MemberTypes.NestedType,
1089                                     BindingFlags.Static | BindingFlags.Public, container);
1090                         AddMembers (MemberTypes.NestedType,
1091                                     BindingFlags.Static | BindingFlags.NonPublic, container);
1092                 }
1093
1094                 void AddMembers (MemberTypes mt, IMemberContainer container)
1095                 {
1096                         AddMembers (mt, BindingFlags.Static | BindingFlags.Public, container);
1097                         AddMembers (mt, BindingFlags.Static | BindingFlags.NonPublic, container);
1098                         AddMembers (mt, BindingFlags.Instance | BindingFlags.Public, container);
1099                         AddMembers (mt, BindingFlags.Instance | BindingFlags.NonPublic, container);
1100                 }
1101
1102                 /// <summary>
1103                 ///   Add all members from class `container' with the requested MemberTypes and
1104                 ///   BindingFlags to the cache.  This method is called multiple times with different
1105                 ///   MemberTypes and BindingFlags.
1106                 /// </summary>
1107                 void AddMembers (MemberTypes mt, BindingFlags bf, IMemberContainer container)
1108                 {
1109                         MemberList members = container.GetMembers (mt, bf);
1110                         BindingFlags new_bf = (container == Container) ?
1111                                 bf | BindingFlags.DeclaredOnly : bf;
1112
1113                         foreach (MemberInfo member in members) {
1114                                 string name = member.Name;
1115
1116                                 // We use a name-based hash table of ArrayList's.
1117                                 ArrayList list = (ArrayList) member_hash [name];
1118                                 if (list == null) {
1119                                         list = new ArrayList ();
1120                                         member_hash.Add (name, list);
1121                                 }
1122
1123                                 // When this method is called for the current class, the list will
1124                                 // already contain all inherited members from our parent classes.
1125                                 // We cannot add new members in front of the list since this'd be an
1126                                 // expensive operation, that's why the list is sorted in reverse order
1127                                 // (ie. members from the current class are coming last).
1128                                 list.Add (new CacheEntry (container, member, mt, bf));
1129                         }
1130                 }
1131
1132                 /// <summary>
1133                 ///   Add all declared and inherited methods from class `type' to the method cache.
1134                 /// </summary>
1135                 void AddMethods (Type type)
1136                 {
1137                         AddMethods (BindingFlags.Static | BindingFlags.Public |
1138                                     BindingFlags.FlattenHierarchy, type);
1139                         AddMethods (BindingFlags.Static | BindingFlags.NonPublic |
1140                                     BindingFlags.FlattenHierarchy, type);
1141                         AddMethods (BindingFlags.Instance | BindingFlags.Public, type);
1142                         AddMethods (BindingFlags.Instance | BindingFlags.NonPublic, type);
1143                 }
1144
1145                 void AddMethods (BindingFlags bf, Type type)
1146                 {
1147                         MemberInfo [] members = type.GetMethods (bf);
1148
1149                         Array.Reverse (members);
1150                         
1151                         foreach (MethodBase member in members) {
1152                                 string name = member.Name;
1153
1154                                 // Varargs methods aren't allowed in C# code.
1155                                 if ((member.CallingConvention & CallingConventions.VarArgs) != 0)
1156                                         continue;
1157
1158                                 // We use a name-based hash table of ArrayList's.
1159                                 ArrayList list = (ArrayList) method_hash [name];
1160                                 if (list == null) {
1161                                         list = new ArrayList ();
1162                                         method_hash.Add (name, list);
1163                                 }
1164
1165                                 // Unfortunately, the elements returned by Type.GetMethods() aren't
1166                                 // sorted so we need to do this check for every member.
1167                                 BindingFlags new_bf = bf;
1168                                 if (member.DeclaringType == type)
1169                                         new_bf |= BindingFlags.DeclaredOnly;
1170
1171                                 list.Add (new CacheEntry (Container, member, MemberTypes.Method, new_bf));
1172                         }
1173
1174                         
1175                 }
1176
1177                 /// <summary>
1178                 ///   Compute and return a appropriate `EntryType' magic number for the given
1179                 ///   MemberTypes and BindingFlags.
1180                 /// </summary>
1181                 protected static EntryType GetEntryType (MemberTypes mt, BindingFlags bf)
1182                 {
1183                         EntryType type = EntryType.None;
1184
1185                         if ((mt & MemberTypes.Constructor) != 0)
1186                                 type |= EntryType.Constructor;
1187                         if ((mt & MemberTypes.Event) != 0)
1188                                 type |= EntryType.Event;
1189                         if ((mt & MemberTypes.Field) != 0)
1190                                 type |= EntryType.Field;
1191                         if ((mt & MemberTypes.Method) != 0)
1192                                 type |= EntryType.Method;
1193                         if ((mt & MemberTypes.Property) != 0)
1194                                 type |= EntryType.Property;
1195                         // Nested types are returned by static and instance searches.
1196                         if ((mt & MemberTypes.NestedType) != 0)
1197                                 type |= EntryType.NestedType | EntryType.Static | EntryType.Instance;
1198
1199                         if ((bf & BindingFlags.Instance) != 0)
1200                                 type |= EntryType.Instance;
1201                         if ((bf & BindingFlags.Static) != 0)
1202                                 type |= EntryType.Static;
1203                         if ((bf & BindingFlags.Public) != 0)
1204                                 type |= EntryType.Public;
1205                         if ((bf & BindingFlags.NonPublic) != 0)
1206                                 type |= EntryType.NonPublic;
1207                         if ((bf & BindingFlags.DeclaredOnly) != 0)
1208                                 type |= EntryType.Declared;
1209
1210                         return type;
1211                 }
1212
1213                 /// <summary>
1214                 ///   The `MemberTypes' enumeration type is a [Flags] type which means that it may
1215                 ///   denote multiple member types.  Returns true if the given flags value denotes a
1216                 ///   single member types.
1217                 /// </summary>
1218                 public static bool IsSingleMemberType (MemberTypes mt)
1219                 {
1220                         switch (mt) {
1221                         case MemberTypes.Constructor:
1222                         case MemberTypes.Event:
1223                         case MemberTypes.Field:
1224                         case MemberTypes.Method:
1225                         case MemberTypes.Property:
1226                         case MemberTypes.NestedType:
1227                                 return true;
1228
1229                         default:
1230                                 return false;
1231                         }
1232                 }
1233
1234                 /// <summary>
1235                 ///   We encode the MemberTypes and BindingFlags of each members in a "magic"
1236                 ///   number to speed up the searching process.
1237                 /// </summary>
1238                 [Flags]
1239                 protected enum EntryType {
1240                         None            = 0x000,
1241
1242                         Instance        = 0x001,
1243                         Static          = 0x002,
1244                         MaskStatic      = Instance|Static,
1245
1246                         Public          = 0x004,
1247                         NonPublic       = 0x008,
1248                         MaskProtection  = Public|NonPublic,
1249
1250                         Declared        = 0x010,
1251
1252                         Constructor     = 0x020,
1253                         Event           = 0x040,
1254                         Field           = 0x080,
1255                         Method          = 0x100,
1256                         Property        = 0x200,
1257                         NestedType      = 0x400,
1258
1259                         MaskType        = Constructor|Event|Field|Method|Property|NestedType
1260                 }
1261
1262                 protected struct CacheEntry {
1263                         public readonly IMemberContainer Container;
1264                         public readonly EntryType EntryType;
1265                         public readonly MemberInfo Member;
1266
1267                         public CacheEntry (IMemberContainer container, MemberInfo member,
1268                                            MemberTypes mt, BindingFlags bf)
1269                         {
1270                                 this.Container = container;
1271                                 this.Member = member;
1272                                 this.EntryType = GetEntryType (mt, bf);
1273                         }
1274                 }
1275
1276                 /// <summary>
1277                 ///   This is called each time we're walking up one level in the class hierarchy
1278                 ///   and checks whether we can abort the search since we've already found what
1279                 ///   we were looking for.
1280                 /// </summary>
1281                 protected bool DoneSearching (ArrayList list)
1282                 {
1283                         //
1284                         // We've found exactly one member in the current class and it's not
1285                         // a method or constructor.
1286                         //
1287                         if (list.Count == 1 && !(list [0] is MethodBase))
1288                                 return true;
1289
1290                         //
1291                         // Multiple properties: we query those just to find out the indexer
1292                         // name
1293                         //
1294                         if ((list.Count > 0) && (list [0] is PropertyInfo))
1295                                 return true;
1296
1297                         return false;
1298                 }
1299
1300                 /// <summary>
1301                 ///   Looks up members with name `name'.  If you provide an optional
1302                 ///   filter function, it'll only be called with members matching the
1303                 ///   requested member name.
1304                 ///
1305                 ///   This method will try to use the cache to do the lookup if possible.
1306                 ///
1307                 ///   Unlike other FindMembers implementations, this method will always
1308                 ///   check all inherited members - even when called on an interface type.
1309                 ///
1310                 ///   If you know that you're only looking for methods, you should use
1311                 ///   MemberTypes.Method alone since this speeds up the lookup a bit.
1312                 ///   When doing a method-only search, it'll try to use a special method
1313                 ///   cache (unless it's a dynamic type or an interface) and the returned
1314                 ///   MemberInfo's will have the correct ReflectedType for inherited methods.
1315                 ///   The lookup process will automatically restart itself in method-only
1316                 ///   search mode if it discovers that it's about to return methods.
1317                 /// </summary>
1318                 ArrayList global = new ArrayList ();
1319                 bool using_global = false;
1320                 
1321                 public MemberList FindMembers (MemberTypes mt, BindingFlags bf, string name,
1322                                                MemberFilter filter, object criteria)
1323                 {
1324                         if (using_global)
1325                                 throw new Exception ();
1326                         
1327                         bool declared_only = (bf & BindingFlags.DeclaredOnly) != 0;
1328                         bool method_search = mt == MemberTypes.Method;
1329                         // If we have a method cache and we aren't already doing a method-only search,
1330                         // then we restart a method search if the first match is a method.
1331                         bool do_method_search = !method_search && (method_hash != null);
1332
1333                         ArrayList applicable;
1334
1335                         // If this is a method-only search, we try to use the method cache if
1336                         // possible; a lookup in the method cache will return a MemberInfo with
1337                         // the correct ReflectedType for inherited methods.
1338                         
1339                         if (method_search && (method_hash != null))
1340                                 applicable = (ArrayList) method_hash [name];
1341                         else
1342                                 applicable = (ArrayList) member_hash [name];
1343                         
1344                         if (applicable == null)
1345                                 return MemberList.Empty;
1346
1347                         //
1348                         // 32  slots gives 53 rss/54 size
1349                         // 2/4 slots gives 55 rss
1350                         //
1351                         // Strange: from 25,000 calls, only 1,800
1352                         // are above 2.  Why does this impact it?
1353                         //
1354                         global.Clear ();
1355                         using_global = true;
1356
1357                         Timer.StartTimer (TimerType.CachedLookup);
1358
1359                         EntryType type = GetEntryType (mt, bf);
1360
1361                         IMemberContainer current = Container;
1362
1363                         // `applicable' is a list of all members with the given member name `name'
1364                         // in the current class and all its parent classes.  The list is sorted in
1365                         // reverse order due to the way how the cache is initialy created (to speed
1366                         // things up, we're doing a deep-copy of our parent).
1367
1368                         for (int i = applicable.Count-1; i >= 0; i--) {
1369                                 CacheEntry entry = (CacheEntry) applicable [i];
1370
1371                                 // This happens each time we're walking one level up in the class
1372                                 // hierarchy.  If we're doing a DeclaredOnly search, we must abort
1373                                 // the first time this happens (this may already happen in the first
1374                                 // iteration of this loop if there are no members with the name we're
1375                                 // looking for in the current class).
1376                                 if (entry.Container != current) {
1377                                         if (declared_only || DoneSearching (global))
1378                                                 break;
1379
1380                                         current = entry.Container;
1381                                 }
1382
1383                                 // Is the member of the correct type ?
1384                                 if ((entry.EntryType & type & EntryType.MaskType) == 0)
1385                                         continue;
1386
1387                                 // Is the member static/non-static ?
1388                                 if ((entry.EntryType & type & EntryType.MaskStatic) == 0)
1389                                         continue;
1390
1391                                 // Apply the filter to it.
1392                                 if (filter (entry.Member, criteria)) {
1393                                         if ((entry.EntryType & EntryType.MaskType) != EntryType.Method)
1394                                                 do_method_search = false;
1395                                         global.Add (entry.Member);
1396                                 }
1397                         }
1398
1399                         Timer.StopTimer (TimerType.CachedLookup);
1400
1401                         // If we have a method cache and we aren't already doing a method-only
1402                         // search, we restart in method-only search mode if the first match is
1403                         // a method.  This ensures that we return a MemberInfo with the correct
1404                         // ReflectedType for inherited methods.
1405                         if (do_method_search && (global.Count > 0)){
1406                                 using_global = false;
1407
1408                                 return FindMembers (MemberTypes.Method, bf, name, filter, criteria);
1409                         }
1410
1411                         using_global = false;
1412                         MemberInfo [] copy = new MemberInfo [global.Count];
1413                         global.CopyTo (copy);
1414                         return new MemberList (copy);
1415                 }
1416         }
1417 }