2003-12-13 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                         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                         TypeExpr 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 (!d.CheckAccessLevel (this)) {
338                                 Report. Error (122, loc,  "`" + d.Name + "' " +
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 TypeExpr 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                         TypeExpr 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 (1000);
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                                 return (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                                 dh.Insert (ns, name, t);
588                                 return t;
589                         }
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                                 dh.Insert (ns, name, null);
603                                 return null;
604                         }
605
606                         t = parent.DefineType ();
607                         if (t == null){
608                                 error = true;
609                                 return null;
610                         }
611                         
612                         dh.Insert (ns, name, t);
613                         return t;
614                 }
615
616                 public static void Error_AmbiguousTypeReference (Location loc, string name, Type t1, Type t2)
617                 {
618                         Report.Error (104, loc,
619                                       String.Format ("`{0}' is an ambiguous reference ({1} or {2}) ", name,
620                                                      t1.FullName, t2.FullName));
621                 }
622
623                 /// <summary>
624                 ///   GetType is used to resolve type names at the DeclSpace level.
625                 ///   Use this to lookup class/struct bases, interface bases or 
626                 ///   delegate type references
627                 /// </summary>
628                 ///
629                 /// <remarks>
630                 ///   Contrast this to LookupType which is used inside method bodies to 
631                 ///   lookup types that have already been defined.  GetType is used
632                 ///   during the tree resolution process and potentially define
633                 ///   recursively the type
634                 /// </remarks>
635                 public Type FindType (Location loc, string name)
636                 {
637                         Type t;
638                         bool error;
639
640                         //
641                         // For the case the type we are looking for is nested within this one
642                         // or is in any base class
643                         //
644                         DeclSpace containing_ds = this;
645
646                         while (containing_ds != null){
647                                 Type container_type = containing_ds.TypeBuilder;
648                                 Type current_type = container_type;
649
650                                 while (current_type != null && current_type != TypeManager.object_type) {
651                                         string pre = current_type.FullName;
652
653                                         t = LookupInterfaceOrClass (pre, name, out error);
654                                         if (error)
655                                                 return null;
656                                 
657                                         if ((t != null) && containing_ds.CheckAccessLevel (t))
658                                                 return t;
659
660                                         current_type = current_type.BaseType;
661                                 }
662                                 containing_ds = containing_ds.Parent;
663                         }
664
665                         //
666                         // Attempt to lookup the class on our namespace and all it's implicit parents
667                         //
668                         for (NamespaceEntry ns = NamespaceEntry; ns != null; ns = ns.ImplicitParent) {
669                                 t = LookupInterfaceOrClass (ns.FullName, name, out error);
670                                 if (error)
671                                         return null;
672                                 
673                                 if (t != null) 
674                                         return t;
675                         }
676                         
677                         //
678                         // Attempt to do a direct unqualified lookup
679                         //
680                         t = LookupInterfaceOrClass ("", name, out error);
681                         if (error)
682                                 return null;
683                         
684                         if (t != null)
685                                 return t;
686                         
687                         //
688                         // Attempt to lookup the class on any of the `using'
689                         // namespaces
690                         //
691
692                         for (NamespaceEntry ns = NamespaceEntry; ns != null; ns = ns.Parent){
693
694                                 t = LookupInterfaceOrClass (ns.FullName, name, out error);
695                                 if (error)
696                                         return null;
697
698                                 if (t != null)
699                                         return t;
700
701                                 //
702                                 // Now check the using clause list
703                                 //
704                                 Type match = null;
705                                 foreach (Namespace using_ns in ns.GetUsingTable ()) {
706                                         match = LookupInterfaceOrClass (using_ns.Name, name, out error);
707                                         if (error)
708                                                 return null;
709
710                                         if (match != null){
711                                                 if (t != null){
712                                                         if (CheckAccessLevel (match)) {
713                                                                 Error_AmbiguousTypeReference (loc, name, t, match);
714                                                                 return null;
715                                                         }
716                                                         continue;
717                                                 }
718                                                 
719                                                 t = match;
720                                         }
721                                 }
722                                 if (t != null)
723                                         return t;
724                         }
725
726                         //Report.Error (246, Location, "Can not find type `"+name+"'");
727                         return null;
728                 }
729
730                 /// <remarks>
731                 ///   This function is broken and not what you're looking for.  It should only
732                 ///   be used while the type is still being created since it doesn't use the cache
733                 ///   and relies on the filter doing the member name check.
734                 /// </remarks>
735                 public abstract MemberList FindMembers (MemberTypes mt, BindingFlags bf,
736                                                         MemberFilter filter, object criteria);
737
738                 /// <remarks>
739                 ///   If we have a MemberCache, return it.  This property may return null if the
740                 ///   class doesn't have a member cache or while it's still being created.
741                 /// </remarks>
742                 public abstract MemberCache MemberCache {
743                         get;
744                 }
745         }
746
747         /// <summary>
748         ///   This is a readonly list of MemberInfo's.      
749         /// </summary>
750         public class MemberList : IList {
751                 public readonly IList List;
752                 int count;
753
754                 /// <summary>
755                 ///   Create a new MemberList from the given IList.
756                 /// </summary>
757                 public MemberList (IList list)
758                 {
759                         if (list != null)
760                                 this.List = list;
761                         else
762                                 this.List = new ArrayList ();
763                         count = List.Count;
764                 }
765
766                 /// <summary>
767                 ///   Concatenate the ILists `first' and `second' to a new MemberList.
768                 /// </summary>
769                 public MemberList (IList first, IList second)
770                 {
771                         ArrayList list = new ArrayList ();
772                         list.AddRange (first);
773                         list.AddRange (second);
774                         count = list.Count;
775                         List = list;
776                 }
777
778                 public static readonly MemberList Empty = new MemberList (new ArrayList ());
779
780                 /// <summary>
781                 ///   Cast the MemberList into a MemberInfo[] array.
782                 /// </summary>
783                 /// <remarks>
784                 ///   This is an expensive operation, only use it if it's really necessary.
785                 /// </remarks>
786                 public static explicit operator MemberInfo [] (MemberList list)
787                 {
788                         Timer.StartTimer (TimerType.MiscTimer);
789                         MemberInfo [] result = new MemberInfo [list.Count];
790                         list.CopyTo (result, 0);
791                         Timer.StopTimer (TimerType.MiscTimer);
792                         return result;
793                 }
794
795                 // ICollection
796
797                 public int Count {
798                         get {
799                                 return count;
800                         }
801                 }
802
803                 public bool IsSynchronized {
804                         get {
805                                 return List.IsSynchronized;
806                         }
807                 }
808
809                 public object SyncRoot {
810                         get {
811                                 return List.SyncRoot;
812                         }
813                 }
814
815                 public void CopyTo (Array array, int index)
816                 {
817                         List.CopyTo (array, index);
818                 }
819
820                 // IEnumerable
821
822                 public IEnumerator GetEnumerator ()
823                 {
824                         return List.GetEnumerator ();
825                 }
826
827                 // IList
828
829                 public bool IsFixedSize {
830                         get {
831                                 return true;
832                         }
833                 }
834
835                 public bool IsReadOnly {
836                         get {
837                                 return true;
838                         }
839                 }
840
841                 object IList.this [int index] {
842                         get {
843                                 return List [index];
844                         }
845
846                         set {
847                                 throw new NotSupportedException ();
848                         }
849                 }
850
851                 // FIXME: try to find out whether we can avoid the cast in this indexer.
852                 public MemberInfo this [int index] {
853                         get {
854                                 return (MemberInfo) List [index];
855                         }
856                 }
857
858                 public int Add (object value)
859                 {
860                         throw new NotSupportedException ();
861                 }
862
863                 public void Clear ()
864                 {
865                         throw new NotSupportedException ();
866                 }
867
868                 public bool Contains (object value)
869                 {
870                         return List.Contains (value);
871                 }
872
873                 public int IndexOf (object value)
874                 {
875                         return List.IndexOf (value);
876                 }
877
878                 public void Insert (int index, object value)
879                 {
880                         throw new NotSupportedException ();
881                 }
882
883                 public void Remove (object value)
884                 {
885                         throw new NotSupportedException ();
886                 }
887
888                 public void RemoveAt (int index)
889                 {
890                         throw new NotSupportedException ();
891                 }
892         }
893
894         /// <summary>
895         ///   This interface is used to get all members of a class when creating the
896         ///   member cache.  It must be implemented by all DeclSpace derivatives which
897         ///   want to support the member cache and by TypeHandle to get caching of
898         ///   non-dynamic types.
899         /// </summary>
900         public interface IMemberContainer {
901                 /// <summary>
902                 ///   The name of the IMemberContainer.  This is only used for
903                 ///   debugging purposes.
904                 /// </summary>
905                 string Name {
906                         get;
907                 }
908
909                 /// <summary>
910                 ///   The type of this IMemberContainer.
911                 /// </summary>
912                 Type Type {
913                         get;
914                 }
915
916                 /// <summary>
917                 ///   Returns the IMemberContainer of the parent class or null if this
918                 ///   is an interface or TypeManger.object_type.
919                 ///   This is used when creating the member cache for a class to get all
920                 ///   members from the parent class.
921                 /// </summary>
922                 IMemberContainer Parent {
923                         get;
924                 }
925
926                 /// <summary>
927                 ///   Whether this is an interface.
928                 /// </summary>
929                 bool IsInterface {
930                         get;
931                 }
932
933                 /// <summary>
934                 ///   Returns all members of this class with the corresponding MemberTypes
935                 ///   and BindingFlags.
936                 /// </summary>
937                 /// <remarks>
938                 ///   When implementing this method, make sure not to return any inherited
939                 ///   members and check the MemberTypes and BindingFlags properly.
940                 ///   Unfortunately, System.Reflection is lame and doesn't provide a way to
941                 ///   get the BindingFlags (static/non-static,public/non-public) in the
942                 ///   MemberInfo class, but the cache needs this information.  That's why
943                 ///   this method is called multiple times with different BindingFlags.
944                 /// </remarks>
945                 MemberList GetMembers (MemberTypes mt, BindingFlags bf);
946
947                 /// <summary>
948                 ///   Return the container's member cache.
949                 /// </summary>
950                 MemberCache MemberCache {
951                         get;
952                 }
953         }
954
955         /// <summary>
956         ///   The MemberCache is used by dynamic and non-dynamic types to speed up
957         ///   member lookups.  It has a member name based hash table; it maps each member
958         ///   name to a list of CacheEntry objects.  Each CacheEntry contains a MemberInfo
959         ///   and the BindingFlags that were initially used to get it.  The cache contains
960         ///   all members of the current class and all inherited members.  If this cache is
961         ///   for an interface types, it also contains all inherited members.
962         ///
963         ///   There are two ways to get a MemberCache:
964         ///   * if this is a dynamic type, lookup the corresponding DeclSpace and then
965         ///     use the DeclSpace.MemberCache property.
966         ///   * if this not a dynamic type, call TypeHandle.GetTypeHandle() to get a
967         ///     TypeHandle instance for the type and then use TypeHandle.MemberCache.
968         /// </summary>
969         public class MemberCache {
970                 public readonly IMemberContainer Container;
971                 protected Hashtable member_hash;
972                 protected Hashtable method_hash;
973                 protected Hashtable interface_hash;
974
975                 /// <summary>
976                 ///   Create a new MemberCache for the given IMemberContainer `container'.
977                 /// </summary>
978                 public MemberCache (IMemberContainer container)
979                 {
980                         this.Container = container;
981
982                         Timer.IncrementCounter (CounterType.MemberCache);
983                         Timer.StartTimer (TimerType.CacheInit);
984
985                         interface_hash = new Hashtable ();
986
987                         // If we have a parent class (we have a parent class unless we're
988                         // TypeManager.object_type), we deep-copy its MemberCache here.
989                         if (Container.IsInterface) {
990                                 MemberCache parent;
991                                 if (Container.Parent != null)
992                                         parent = Container.Parent.MemberCache;
993                                 else
994                                         parent = TypeHandle.ObjectType.MemberCache;
995                                 member_hash = SetupCacheForInterface (parent);
996                         } else if (Container.Parent != null)
997                                 member_hash = SetupCache (Container.Parent.MemberCache);
998                         else
999                                 member_hash = new Hashtable ();
1000
1001                         // If this is neither a dynamic type nor an interface, create a special
1002                         // method cache with all declared and inherited methods.
1003                         Type type = container.Type;
1004                         if (!(type is TypeBuilder) && !type.IsInterface) {
1005                                 method_hash = new Hashtable ();
1006                                 AddMethods (type);
1007                         }
1008
1009                         // Add all members from the current class.
1010                         AddMembers (Container);
1011
1012                         Timer.StopTimer (TimerType.CacheInit);
1013                 }
1014
1015                 /// <summary>
1016                 ///   Bootstrap this member cache by doing a deep-copy of our parent.
1017                 /// </summary>
1018                 Hashtable SetupCache (MemberCache parent)
1019                 {
1020                         Hashtable hash = new Hashtable ();
1021
1022                         IDictionaryEnumerator it = parent.member_hash.GetEnumerator ();
1023                         while (it.MoveNext ()) {
1024                                 hash [it.Key] = ((ArrayList) it.Value).Clone ();
1025                         }
1026                                 
1027                         return hash;
1028                 }
1029
1030                 void AddInterfaces (MemberCache parent)
1031                 {
1032                         foreach (Type iface in parent.interface_hash.Keys) {
1033                                 if (!interface_hash.Contains (iface))
1034                                         interface_hash.Add (iface, true);
1035                         }
1036                 }
1037
1038                 /// <summary>
1039                 ///   Add the contents of `new_hash' to `hash'.
1040                 /// </summary>
1041                 void AddHashtable (Hashtable hash, Hashtable new_hash)
1042                 {
1043                         IDictionaryEnumerator it = new_hash.GetEnumerator ();
1044                         while (it.MoveNext ()) {
1045                                 ArrayList list = (ArrayList) hash [it.Key];
1046                                 if (list != null)
1047                                         list.AddRange ((ArrayList) it.Value);
1048                                 else
1049                                         hash [it.Key] = ((ArrayList) it.Value).Clone ();
1050                         }
1051                 }
1052
1053                 /// <summary>
1054                 ///   Bootstrap the member cache for an interface type.
1055                 ///   Type.GetMembers() won't return any inherited members for interface types,
1056                 ///   so we need to do this manually.  Interfaces also inherit from System.Object.
1057                 /// </summary>
1058                 Hashtable SetupCacheForInterface (MemberCache parent)
1059                 {
1060                         Hashtable hash = SetupCache (parent);
1061                         TypeExpr [] ifaces = TypeManager.GetInterfaces (Container.Type);
1062
1063                         foreach (TypeExpr iface in ifaces) {
1064                                 Type itype = iface.Type;
1065
1066                                 if (interface_hash.Contains (itype))
1067                                         continue;
1068                                 interface_hash.Add (itype, true);
1069
1070                                 IMemberContainer iface_container =
1071                                         TypeManager.LookupMemberContainer (itype);
1072
1073                                 MemberCache iface_cache = iface_container.MemberCache;
1074
1075                                 AddHashtable (hash, iface_cache.member_hash);
1076                                 AddInterfaces (iface_cache);
1077                         }
1078
1079                         return hash;
1080                 }
1081
1082                 /// <summary>
1083                 ///   Add all members from class `container' to the cache.
1084                 /// </summary>
1085                 void AddMembers (IMemberContainer container)
1086                 {
1087                         // We need to call AddMembers() with a single member type at a time
1088                         // to get the member type part of CacheEntry.EntryType right.
1089                         AddMembers (MemberTypes.Constructor, container);
1090                         AddMembers (MemberTypes.Field, container);
1091                         AddMembers (MemberTypes.Method, container);
1092                         AddMembers (MemberTypes.Property, container);
1093                         AddMembers (MemberTypes.Event, container);
1094                         // Nested types are returned by both Static and Instance searches.
1095                         AddMembers (MemberTypes.NestedType,
1096                                     BindingFlags.Static | BindingFlags.Public, container);
1097                         AddMembers (MemberTypes.NestedType,
1098                                     BindingFlags.Static | BindingFlags.NonPublic, container);
1099                 }
1100
1101                 void AddMembers (MemberTypes mt, IMemberContainer container)
1102                 {
1103                         AddMembers (mt, BindingFlags.Static | BindingFlags.Public, container);
1104                         AddMembers (mt, BindingFlags.Static | BindingFlags.NonPublic, container);
1105                         AddMembers (mt, BindingFlags.Instance | BindingFlags.Public, container);
1106                         AddMembers (mt, BindingFlags.Instance | BindingFlags.NonPublic, container);
1107                 }
1108
1109                 /// <summary>
1110                 ///   Add all members from class `container' with the requested MemberTypes and
1111                 ///   BindingFlags to the cache.  This method is called multiple times with different
1112                 ///   MemberTypes and BindingFlags.
1113                 /// </summary>
1114                 void AddMembers (MemberTypes mt, BindingFlags bf, IMemberContainer container)
1115                 {
1116                         MemberList members = container.GetMembers (mt, bf);
1117                         BindingFlags new_bf = (container == Container) ?
1118                                 bf | BindingFlags.DeclaredOnly : bf;
1119
1120                         foreach (MemberInfo member in members) {
1121                                 string name = member.Name;
1122
1123                                 // We use a name-based hash table of ArrayList's.
1124                                 ArrayList list = (ArrayList) member_hash [name];
1125                                 if (list == null) {
1126                                         list = new ArrayList ();
1127                                         member_hash.Add (name, list);
1128                                 }
1129
1130                                 // When this method is called for the current class, the list will
1131                                 // already contain all inherited members from our parent classes.
1132                                 // We cannot add new members in front of the list since this'd be an
1133                                 // expensive operation, that's why the list is sorted in reverse order
1134                                 // (ie. members from the current class are coming last).
1135                                 list.Add (new CacheEntry (container, member, mt, bf));
1136                         }
1137                 }
1138
1139                 /// <summary>
1140                 ///   Add all declared and inherited methods from class `type' to the method cache.
1141                 /// </summary>
1142                 void AddMethods (Type type)
1143                 {
1144                         AddMethods (BindingFlags.Static | BindingFlags.Public |
1145                                     BindingFlags.FlattenHierarchy, type);
1146                         AddMethods (BindingFlags.Static | BindingFlags.NonPublic |
1147                                     BindingFlags.FlattenHierarchy, type);
1148                         AddMethods (BindingFlags.Instance | BindingFlags.Public, type);
1149                         AddMethods (BindingFlags.Instance | BindingFlags.NonPublic, type);
1150                 }
1151
1152                 void AddMethods (BindingFlags bf, Type type)
1153                 {
1154                         MemberInfo [] members = type.GetMethods (bf);
1155
1156                         Array.Reverse (members);
1157                         
1158                         foreach (MethodBase member in members) {
1159                                 string name = member.Name;
1160
1161                                 // Varargs methods aren't allowed in C# code.
1162                                 if ((member.CallingConvention & CallingConventions.VarArgs) != 0)
1163                                         continue;
1164
1165                                 // We use a name-based hash table of ArrayList's.
1166                                 ArrayList list = (ArrayList) method_hash [name];
1167                                 if (list == null) {
1168                                         list = new ArrayList ();
1169                                         method_hash.Add (name, list);
1170                                 }
1171
1172                                 // Unfortunately, the elements returned by Type.GetMethods() aren't
1173                                 // sorted so we need to do this check for every member.
1174                                 BindingFlags new_bf = bf;
1175                                 if (member.DeclaringType == type)
1176                                         new_bf |= BindingFlags.DeclaredOnly;
1177
1178                                 list.Add (new CacheEntry (Container, member, MemberTypes.Method, new_bf));
1179                         }
1180
1181                         
1182                 }
1183
1184                 /// <summary>
1185                 ///   Compute and return a appropriate `EntryType' magic number for the given
1186                 ///   MemberTypes and BindingFlags.
1187                 /// </summary>
1188                 protected static EntryType GetEntryType (MemberTypes mt, BindingFlags bf)
1189                 {
1190                         EntryType type = EntryType.None;
1191
1192                         if ((mt & MemberTypes.Constructor) != 0)
1193                                 type |= EntryType.Constructor;
1194                         if ((mt & MemberTypes.Event) != 0)
1195                                 type |= EntryType.Event;
1196                         if ((mt & MemberTypes.Field) != 0)
1197                                 type |= EntryType.Field;
1198                         if ((mt & MemberTypes.Method) != 0)
1199                                 type |= EntryType.Method;
1200                         if ((mt & MemberTypes.Property) != 0)
1201                                 type |= EntryType.Property;
1202                         // Nested types are returned by static and instance searches.
1203                         if ((mt & MemberTypes.NestedType) != 0)
1204                                 type |= EntryType.NestedType | EntryType.Static | EntryType.Instance;
1205
1206                         if ((bf & BindingFlags.Instance) != 0)
1207                                 type |= EntryType.Instance;
1208                         if ((bf & BindingFlags.Static) != 0)
1209                                 type |= EntryType.Static;
1210                         if ((bf & BindingFlags.Public) != 0)
1211                                 type |= EntryType.Public;
1212                         if ((bf & BindingFlags.NonPublic) != 0)
1213                                 type |= EntryType.NonPublic;
1214                         if ((bf & BindingFlags.DeclaredOnly) != 0)
1215                                 type |= EntryType.Declared;
1216
1217                         return type;
1218                 }
1219
1220                 /// <summary>
1221                 ///   The `MemberTypes' enumeration type is a [Flags] type which means that it may
1222                 ///   denote multiple member types.  Returns true if the given flags value denotes a
1223                 ///   single member types.
1224                 /// </summary>
1225                 public static bool IsSingleMemberType (MemberTypes mt)
1226                 {
1227                         switch (mt) {
1228                         case MemberTypes.Constructor:
1229                         case MemberTypes.Event:
1230                         case MemberTypes.Field:
1231                         case MemberTypes.Method:
1232                         case MemberTypes.Property:
1233                         case MemberTypes.NestedType:
1234                                 return true;
1235
1236                         default:
1237                                 return false;
1238                         }
1239                 }
1240
1241                 /// <summary>
1242                 ///   We encode the MemberTypes and BindingFlags of each members in a "magic"
1243                 ///   number to speed up the searching process.
1244                 /// </summary>
1245                 [Flags]
1246                 protected enum EntryType {
1247                         None            = 0x000,
1248
1249                         Instance        = 0x001,
1250                         Static          = 0x002,
1251                         MaskStatic      = Instance|Static,
1252
1253                         Public          = 0x004,
1254                         NonPublic       = 0x008,
1255                         MaskProtection  = Public|NonPublic,
1256
1257                         Declared        = 0x010,
1258
1259                         Constructor     = 0x020,
1260                         Event           = 0x040,
1261                         Field           = 0x080,
1262                         Method          = 0x100,
1263                         Property        = 0x200,
1264                         NestedType      = 0x400,
1265
1266                         MaskType        = Constructor|Event|Field|Method|Property|NestedType
1267                 }
1268
1269                 protected struct CacheEntry {
1270                         public readonly IMemberContainer Container;
1271                         public readonly EntryType EntryType;
1272                         public readonly MemberInfo Member;
1273
1274                         public CacheEntry (IMemberContainer container, MemberInfo member,
1275                                            MemberTypes mt, BindingFlags bf)
1276                         {
1277                                 this.Container = container;
1278                                 this.Member = member;
1279                                 this.EntryType = GetEntryType (mt, bf);
1280                         }
1281                 }
1282
1283                 /// <summary>
1284                 ///   This is called each time we're walking up one level in the class hierarchy
1285                 ///   and checks whether we can abort the search since we've already found what
1286                 ///   we were looking for.
1287                 /// </summary>
1288                 protected bool DoneSearching (ArrayList list)
1289                 {
1290                         //
1291                         // We've found exactly one member in the current class and it's not
1292                         // a method or constructor.
1293                         //
1294                         if (list.Count == 1 && !(list [0] is MethodBase))
1295                                 return true;
1296
1297                         //
1298                         // Multiple properties: we query those just to find out the indexer
1299                         // name
1300                         //
1301                         if ((list.Count > 0) && (list [0] is PropertyInfo))
1302                                 return true;
1303
1304                         return false;
1305                 }
1306
1307                 /// <summary>
1308                 ///   Looks up members with name `name'.  If you provide an optional
1309                 ///   filter function, it'll only be called with members matching the
1310                 ///   requested member name.
1311                 ///
1312                 ///   This method will try to use the cache to do the lookup if possible.
1313                 ///
1314                 ///   Unlike other FindMembers implementations, this method will always
1315                 ///   check all inherited members - even when called on an interface type.
1316                 ///
1317                 ///   If you know that you're only looking for methods, you should use
1318                 ///   MemberTypes.Method alone since this speeds up the lookup a bit.
1319                 ///   When doing a method-only search, it'll try to use a special method
1320                 ///   cache (unless it's a dynamic type or an interface) and the returned
1321                 ///   MemberInfo's will have the correct ReflectedType for inherited methods.
1322                 ///   The lookup process will automatically restart itself in method-only
1323                 ///   search mode if it discovers that it's about to return methods.
1324                 /// </summary>
1325                 ArrayList global = new ArrayList ();
1326                 bool using_global = false;
1327                 
1328                 public MemberList FindMembers (MemberTypes mt, BindingFlags bf, string name,
1329                                                MemberFilter filter, object criteria)
1330                 {
1331                         if (using_global)
1332                                 throw new Exception ();
1333                         
1334                         bool declared_only = (bf & BindingFlags.DeclaredOnly) != 0;
1335                         bool method_search = mt == MemberTypes.Method;
1336                         // If we have a method cache and we aren't already doing a method-only search,
1337                         // then we restart a method search if the first match is a method.
1338                         bool do_method_search = !method_search && (method_hash != null);
1339
1340                         ArrayList applicable;
1341
1342                         // If this is a method-only search, we try to use the method cache if
1343                         // possible; a lookup in the method cache will return a MemberInfo with
1344                         // the correct ReflectedType for inherited methods.
1345                         
1346                         if (method_search && (method_hash != null))
1347                                 applicable = (ArrayList) method_hash [name];
1348                         else
1349                                 applicable = (ArrayList) member_hash [name];
1350                         
1351                         if (applicable == null)
1352                                 return MemberList.Empty;
1353
1354                         //
1355                         // 32  slots gives 53 rss/54 size
1356                         // 2/4 slots gives 55 rss
1357                         //
1358                         // Strange: from 25,000 calls, only 1,800
1359                         // are above 2.  Why does this impact it?
1360                         //
1361                         global.Clear ();
1362                         using_global = true;
1363
1364                         Timer.StartTimer (TimerType.CachedLookup);
1365
1366                         EntryType type = GetEntryType (mt, bf);
1367
1368                         IMemberContainer current = Container;
1369
1370                         // `applicable' is a list of all members with the given member name `name'
1371                         // in the current class and all its parent classes.  The list is sorted in
1372                         // reverse order due to the way how the cache is initialy created (to speed
1373                         // things up, we're doing a deep-copy of our parent).
1374
1375                         for (int i = applicable.Count-1; i >= 0; i--) {
1376                                 CacheEntry entry = (CacheEntry) applicable [i];
1377
1378                                 // This happens each time we're walking one level up in the class
1379                                 // hierarchy.  If we're doing a DeclaredOnly search, we must abort
1380                                 // the first time this happens (this may already happen in the first
1381                                 // iteration of this loop if there are no members with the name we're
1382                                 // looking for in the current class).
1383                                 if (entry.Container != current) {
1384                                         if (declared_only || DoneSearching (global))
1385                                                 break;
1386
1387                                         current = entry.Container;
1388                                 }
1389
1390                                 // Is the member of the correct type ?
1391                                 if ((entry.EntryType & type & EntryType.MaskType) == 0)
1392                                         continue;
1393
1394                                 // Is the member static/non-static ?
1395                                 if ((entry.EntryType & type & EntryType.MaskStatic) == 0)
1396                                         continue;
1397
1398                                 // Apply the filter to it.
1399                                 if (filter (entry.Member, criteria)) {
1400                                         if ((entry.EntryType & EntryType.MaskType) != EntryType.Method)
1401                                                 do_method_search = false;
1402                                         global.Add (entry.Member);
1403                                 }
1404                         }
1405
1406                         Timer.StopTimer (TimerType.CachedLookup);
1407
1408                         // If we have a method cache and we aren't already doing a method-only
1409                         // search, we restart in method-only search mode if the first match is
1410                         // a method.  This ensures that we return a MemberInfo with the correct
1411                         // ReflectedType for inherited methods.
1412                         if (do_method_search && (global.Count > 0)){
1413                                 using_global = false;
1414
1415                                 return FindMembers (MemberTypes.Method, bf, name, filter, criteria);
1416                         }
1417
1418                         using_global = false;
1419                         MemberInfo [] copy = new MemberInfo [global.Count];
1420                         global.CopyTo (copy);
1421                         return new MemberList (copy);
1422                 }
1423         }
1424 }