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