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