* roottypes.cs: Rename from tree.cs.
[mono.git] / mcs / mbas / pending.cs
1 //
2 // pending.cs: Pending method implementation
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@gnu.org)
6 //   Anirban Bhattacharjee (banirban@novell.com)
7 //
8 // Licensed under the terms of the GNU GPL
9 //
10 // (C) 2001, 2002 Ximian, Inc (http://www.ximian.com)
11 //
12 //
13
14 using System;
15 using System.Collections;
16 using System.Reflection;
17 using System.Reflection.Emit;
18
19 namespace Mono.MonoBASIC {
20
21         struct TypeAndMethods {
22                 public Type          type;
23                 public MethodInfo [] methods;
24                 
25                 // Far from ideal, but we want to avoid creating a copy
26                 // of methods above.
27                 public Type [][]     args;
28                 
29                 //
30                 // This flag on the method says `We found a match, but
31                 // because it was private, we could not use the match
32                 //
33                 public bool []       found;
34
35                 // If a method is defined here, then we always need to
36                 // create a proxy for it.  This is used when implementing
37                 // an interface's indexer with a different DefaultPropName.
38                 public MethodInfo [] need_proxy;
39         }
40
41         public class PendingImplementation {
42                 /// <summary>
43                 ///   The container for this PendingImplementation
44                 /// </summary>
45                 TypeContainer container;
46                 
47                 /// <summary>
48                 ///   This filter is used by FindMembers, and it is used to
49                 ///   extract only virtual/abstract fields
50                 /// </summary>
51                 static MemberFilter virtual_method_filter;
52
53                 /// <summary>
54                 ///   This is the array of TypeAndMethods that describes the pending implementations
55                 ///   (both interfaces and abstract methods in parent class)
56                 /// </summary>
57                 TypeAndMethods [] pending_implementations;
58
59                 static bool IsVirtualFilter (MemberInfo m, object filterCriteria)
60                 {
61                         if (!(m is MethodInfo))
62                                 return false;
63
64                         return ((MethodInfo) m).IsVirtual;
65                 }
66
67                 /// <summary>
68                 ///   Inits the virtual_method_filter
69                 /// </summary>
70                 static PendingImplementation ()
71                 {
72                         virtual_method_filter = new MemberFilter (IsVirtualFilter);
73                 }
74
75                 // <remarks>
76                 //   Returns a list of the abstract methods that are exposed by all of our
77                 //   parents that we must implement.  Notice that this `flattens' the
78                 //   method search space, and takes into account overrides.  
79                 // </remarks>
80                 static ArrayList GetAbstractMethods (Type t)
81                 {
82                         ArrayList list = null;
83                         bool searching = true;
84                         Type current_type = t;
85                         
86                         do {
87                                 MemberList mi;
88                                 
89                                 mi = TypeContainer.FindMembers (
90                                         current_type, MemberTypes.Method,
91                                         BindingFlags.Public | BindingFlags.Instance |
92                                         BindingFlags.DeclaredOnly,
93                                         virtual_method_filter, null);
94
95                                 if (current_type == TypeManager.object_type)
96                                         searching = false;
97                                 else {
98                                         current_type = current_type.BaseType;
99                                         if (!current_type.IsAbstract)
100                                                 searching = false;
101                                 }
102
103                                 if (mi.Count == 0)
104                                         continue;
105
106                                 if (mi.Count == 1 && !(mi [0] is MethodBase))
107                                         searching = false;
108                                 else 
109                                         list = TypeManager.CopyNewMethods (list, mi);
110                         } while (searching);
111
112                         if (list == null)
113                                 return null;
114                         
115                         for (int i = 0; i < list.Count; i++){
116                                 while (list.Count > i && !((MethodInfo) list [i]).IsAbstract)
117                                         list.RemoveAt (i);
118                         }
119
120                         if (list.Count == 0)
121                                 return null;
122
123                         return list;
124                 }
125
126                 PendingImplementation (TypeContainer container, Type [] ifaces, ArrayList abstract_methods, int total)
127                 {
128                         TypeBuilder type_builder = container.TypeBuilder;
129                         
130                         this.container = container;
131                         pending_implementations = new TypeAndMethods [total];
132
133                         int i = 0;
134                         if (ifaces != null){
135                                 foreach (Type t in ifaces){
136                                         MethodInfo [] mi;
137                                         /*if (t is TypeBuilder){
138                                                 Interface iface;
139
140                                                 iface = TypeManager.LookupInterface (t);
141                                                 
142                                                 mi = iface.GetMethods (container);
143                                         } else*/
144                                         mi = t.GetMethods ();
145
146                                         int count = mi.Length;
147                                         pending_implementations [i].type = t;
148                                         pending_implementations [i].methods = mi;
149                                         pending_implementations [i].args = new Type [count][];
150                                         pending_implementations [i].found = new bool [count];
151                                         pending_implementations [i].need_proxy = new MethodInfo [count];
152
153                                         int j = 0;
154                                         foreach (MethodInfo m in mi){
155                                                 Type [] types = TypeManager.GetArgumentTypes (m);
156
157                                                 pending_implementations [i].args [j] = types;
158                                                 j++;
159                                         }
160                                         i++;
161                                 }
162                         }
163
164                         if (abstract_methods != null){
165                                 int count = abstract_methods.Count;
166                                 pending_implementations [i].methods = new MethodInfo [count];
167                                 pending_implementations [i].need_proxy = new MethodInfo [count];
168                                 
169                                 abstract_methods.CopyTo (pending_implementations [i].methods, 0);
170                                 pending_implementations [i].found = new bool [count];
171                                 pending_implementations [i].args = new Type [count][];
172                                 pending_implementations [i].type = type_builder.BaseType;
173                                 
174                                 int j = 0;
175                                 foreach (MemberInfo m in abstract_methods){
176                                         MethodInfo mi = (MethodInfo) m;
177
178                                         Type [] types = TypeManager.GetArgumentTypes (mi);
179                                         
180                                         pending_implementations [i].args [j] = types;
181                                         j++;
182                                 }
183                         }
184                 }
185                 
186                 //
187                 // Factory method: if there are pending implementation methods, we return a PendingImplementation
188                 // object, otherwise we return null.
189                 //
190                 // Register method implementations are either abstract methods
191                 // flagged as such on the base class or interface methods
192                 //
193                 static public PendingImplementation GetPendingImplementations (TypeContainer container)
194                 {
195                         TypeBuilder type_builder = container.TypeBuilder;
196                         Type [] ifaces;
197                         Type b = type_builder.BaseType;
198                         int icount = 0;
199
200                         //
201                         // Notice that TypeBuilders will only return the interfaces that the Type
202                         // is supposed to implement, not all the interfaces that the type implements.
203                         //
204                         // Completely broken.  Anyways, we take advantage of this, so we only register
205                         // the implementations that we need, as they are those that are listed by the
206                         // TypeBuilder.
207                         //
208                         ifaces = type_builder.GetInterfaces ();
209
210 #if DEBUG
211                         {
212                                 Type x = type_builder;
213
214                                 while (x != null){
215                                         Type [] iff = x.GetInterfaces ();
216                                         Console.WriteLine ("Type: " + x.Name);
217                                         
218                                         foreach (Type tt in iff){
219                                                 Console.WriteLine ("  Iface: " + tt.Name);
220                                         }
221                                         x = x.BaseType;
222                                 }
223                         }
224 #endif
225
226                         
227                         ifaces = TypeManager.ExpandInterfaces (ifaces);
228                         icount = ifaces.Length;
229                         //
230                         // If we are implementing an abstract class, and we are not
231                         // ourselves abstract, and there are abstract methods (C# allows
232                         // abstract classes that have no abstract methods), then allocate
233                         // one slot.
234                         //
235                         // We also pre-compute the methods.
236                         //
237                         bool implementing_abstract = ((b != null) && b.IsAbstract && !type_builder.IsAbstract);
238                         ArrayList abstract_methods = null;
239
240                         if (implementing_abstract){
241                                 abstract_methods = GetAbstractMethods (b);
242                                 
243                                 if (abstract_methods == null)
244                                         implementing_abstract = false;
245                         }
246                         
247                         int total = icount +  (implementing_abstract ? 1 : 0);
248                         if (total == 0)
249                                 return null;
250
251                         return new PendingImplementation (container, ifaces, abstract_methods, total);
252                 }
253
254                 public enum Operation {
255                         //
256                         // If you change this, review the whole InterfaceMethod routine as there
257                         // are a couple of assumptions on these three states
258                         //
259                         Lookup, ClearOne, ClearAll
260                 }
261
262                 /// <summary>
263                 ///   Whether the specified method is an interface method implementation
264                 /// </summary>
265                 public MethodInfo IsAbstractMethod (Type t, string name, Type ret_type, Type [] args)
266                 {
267                         return InterfaceMethod (t, name, ret_type, args, Operation.Lookup, null);
268                 }
269
270                 public MethodInfo IsAbstractIndexer (Type t, Type ret_type, Type [] args)
271                 {
272                         return InterfaceMethod (t, null, ret_type, args, Operation.Lookup, null);
273                 }
274
275                 public void ImplementMethod (Type t, string name, Type ret_type, Type [] args, bool clear_one) 
276                 {
277                         InterfaceMethod (t, name, ret_type, args,
278                                          clear_one ? Operation.ClearOne : Operation.ClearAll, null);
279                 }
280
281                 public void ImplementIndexer (Type t, MethodInfo mi, Type ret_type, Type [] args, bool clear_one) 
282                 {
283                         InterfaceMethod (t, mi.Name, ret_type, args,
284                                          clear_one ? Operation.ClearOne : Operation.ClearAll, mi);
285                 }
286                 
287                 /// <remarks>
288                 ///   If a method in Type `t' (or null to look in all interfaces
289                 ///   and the base abstract class) with name `Name', return type `ret_type' and
290                 ///   arguments `args' implements an interface, this method will
291                 ///   return the MethodInfo that this method implements.
292                 ///
293                 ///   If `name' is null, we operate solely on the method's signature.  This is for
294                 ///   instance used when implementing indexers.
295                 ///
296                 ///   The `Operation op' controls whether to lookup, clear the pending bit, or clear
297                 ///   all the methods with the given signature.
298                 ///
299                 ///   The `MethodInfo need_proxy' is used when we're implementing an interface's
300                 ///   indexer in a class.  If the new indexer's DefaultPropName does not match the one
301                 ///   that was used in the interface, then we always need to create a proxy for it.
302                 ///
303                 /// </remarks>
304                 public MethodInfo InterfaceMethod (Type t, string name, Type ret_type, Type [] args,
305                                                    Operation op, MethodInfo need_proxy)
306                 {
307                         int arg_len = args.Length;
308
309                         if (pending_implementations == null)
310                                 return null;
311
312                         foreach (TypeAndMethods tm in pending_implementations){
313                                 if (!(t == null || tm.type == t))
314                                         continue;
315
316                                 int i = 0;
317                                 foreach (MethodInfo m in tm.methods){
318                                         if (m == null){
319                                                 i++;
320                                                 continue;
321                                         }
322
323                                         // `need_proxy' is not null when we're implementing an
324                                         // interface indexer and this is Clear(One/All) operation.
325                                         // If `name' is null, then we do a match solely based on the
326                                         // signature and not on the name (this is done in the Lookup
327                                         // for an interface indexer).
328                                         if ((name != null) && (need_proxy == null) && (name != m.Name)){
329                                                 i++;
330                                                 continue;
331                                         }
332
333                                         if (ret_type != m.ReturnType){
334                                                 if (!((ret_type == null && m.ReturnType == TypeManager.void_type) ||
335                                                       (m.ReturnType == null && ret_type == TypeManager.void_type)))
336                                                 {
337                                                         i++;
338                                                         continue;
339                                                 }
340                                         }
341
342                                         //
343                                         // Check if we have the same parameters
344                                         //
345                                         if (tm.args [i].Length != arg_len){
346                                                 i++;
347                                                 continue;
348                                         }
349
350                                         int j, top = args.Length;
351                                         bool fail = false;
352                                         
353                                         for (j = 0; j < top; j++){
354                                                 if (tm.args [i][j] != args[j]){
355                                                         fail = true;
356                                                         break;
357                                                 }
358                                         }
359                                         if (fail){
360                                                 i++;
361                                                 continue;
362                                         }
363
364                                         if (op != Operation.Lookup){
365                                                 // If `t != null', then this is an explicitly interface
366                                                 // implementation and we can always clear the method.
367                                                 // `need_proxy' is not null if we're implementing an
368                                                 // interface indexer.  In this case, we need to create
369                                                 // a proxy if the implementation's DefaultPropName doesn't
370                                                 // match the DefaultPropName in the interface.
371                                                 if ((t == null) && (need_proxy != null) && (name != m.Name))
372                                                         tm.need_proxy [i] = need_proxy;
373                                                 else
374                                                         tm.methods [i] = null;
375                                         }
376                                         tm.found [i] = true;
377
378                                         //
379                                         // Lookups and ClearOne return
380                                         //
381                                         if (op != Operation.ClearAll)
382                                                 return m;
383                                 }
384
385                                 // If a specific type was requested, we can stop now.
386                                 if (tm.type == t)
387                                         return null;
388                         }
389                         return null;
390                 }
391
392                 /// <summary>
393                 ///   C# allows this kind of scenarios:
394                 ///   interface I { void M (); }
395                 ///   class X { public void M (); }
396                 ///   class Y : X, I { }
397                 ///
398                 ///   For that case, we create an explicit implementation function
399                 ///   I.M in Y.
400                 /// </summary>
401                 void DefineProxy (Type iface, MethodInfo parent_method, MethodInfo iface_method,
402                                   Type [] args)
403                 {
404                         MethodBuilder proxy;
405
406                         string proxy_name = iface.Name + "." + iface_method.Name;
407
408                         proxy = container.TypeBuilder.DefineMethod (
409                                 proxy_name,
410                                 MethodAttributes.HideBySig |
411                                 MethodAttributes.NewSlot |
412                                 MethodAttributes.Virtual,
413                                 CallingConventions.Standard | CallingConventions.HasThis,
414                                 parent_method.ReturnType, args);
415
416                         int top = args.Length;
417                         ILGenerator ig = proxy.GetILGenerator ();
418
419                         ig.Emit (OpCodes.Ldarg_0);
420                         for (int i = 0; i < top; i++){
421                                 switch (i){
422                                 case 0:
423                                         ig.Emit (OpCodes.Ldarg_1); break;
424                                 case 1:
425                                         ig.Emit (OpCodes.Ldarg_2); break;
426                                 case 2:
427                                         ig.Emit (OpCodes.Ldarg_3); break;
428                                 default:
429                                         ig.Emit (OpCodes.Ldarg, i - 1); break;
430                                 }
431                         }
432                         ig.Emit (OpCodes.Call, parent_method);
433                         ig.Emit (OpCodes.Ret);
434
435                         container.TypeBuilder.DefineMethodOverride (proxy, iface_method);
436                 }
437                 
438                 static bool IsPropertyGetMethod (string m)
439                 {
440                         return (m.Substring (0, 4) == "get_");  
441                 }
442
443                 static bool IsPropertySetMethod (string m)
444                 {
445                         return (m.Substring (0, 4) == "set_");  
446                 }
447
448                 MethodInfo FindExplicitImplementation (string iface_name, string method_name)
449                 {
450                         if (container.Properties != null) {
451                                 foreach (Property p in container.Properties) 
452                                 {
453                                         if (p.Implements != null) {                                     
454                                                 if (IsPropertyGetMethod (method_name) && (container.Namespace.Name +  "." + p.Implements.ToString() == iface_name + "." + method_name.Substring(4))) 
455                                                                 return p.PropertyBuilder.GetGetMethod(true);
456
457                                                 if (IsPropertySetMethod (method_name) && (container.Namespace.Name +  "." + p.Implements.ToString() == iface_name + "." + method_name.Substring(4))) 
458                                                                 return p.PropertyBuilder.GetSetMethod(true);
459                                         }
460                                 }
461                         }
462
463                         if (container.Methods != null) 
464                         {
465                                 foreach (Method m in container.Methods) 
466                                 {
467                                         if (m.Implements != null) 
468                                         {                                       
469                                                 if (container.Namespace.Name +  "." + m.Implements.ToString() == iface_name + "." + method_name)
470                                                         return (MethodInfo) m.MethodBuilder;
471                                         }
472                                 }
473                         }
474                         return null;
475                 }
476
477                 /// <summary>
478                 ///   This function tells whether one of our parent classes implements
479                 ///   the given method (which turns out, it is valid to have an interface
480                 ///   implementation in a parent
481                 /// </summary>
482                 bool ParentImplements (Type iface_type, MethodInfo mi)
483                 {
484                         MethodSignature ms;
485                         MethodInfo mr;
486                         
487                         Type [] args = TypeManager.GetArgumentTypes (mi);
488
489                         ms = new MethodSignature (mi.Name, mi.ReturnType, args);
490                         MemberList list = TypeContainer.FindMembers (
491                                 container.TypeBuilder.BaseType, MemberTypes.Method | MemberTypes.Property,
492                                 BindingFlags.Public | BindingFlags.Instance,
493                                 MethodSignature.method_signature_filter, ms);
494
495                         if (list.Count == 0) 
496                         {
497                                 mr = FindExplicitImplementation (iface_type.ToString(), mi.Name);
498                                 if (mr == null)
499                                         return false;
500                         }
501                         else
502                                 mr = (MethodInfo) list[0];
503
504                         DefineProxy (iface_type, mr, mi, args);
505                         return true;                                            
506                 }
507
508                 /// <summary>
509                 ///   Verifies that any pending abstract methods or interface methods
510                 ///   were implemented.
511                 /// </summary>
512                 public bool VerifyPendingMethods ()
513                 {
514                         int top = pending_implementations.Length;
515                         bool errors = false;
516                         int i;
517                         
518                         for (i = 0; i < top; i++){
519                                 Type type = pending_implementations [i].type;
520                                 int j = 0;
521                                 
522                                 foreach (MethodInfo mi in pending_implementations [i].methods){
523                                         if (mi == null)
524                                                 continue;
525
526                                         if (type.IsInterface){
527                                                 MethodInfo need_proxy =
528                                                         pending_implementations [i].need_proxy [j];
529
530                                                 if (need_proxy != null) {
531                                                         Type [] args = TypeManager.GetArgumentTypes (mi);
532                                                         DefineProxy (type, need_proxy, mi, args);
533                                                         continue;
534                                                 }
535
536                                                 if (ParentImplements (type, mi))
537                                                         continue;
538
539                                                 string extra = "";
540                                                 
541                                                 if (pending_implementations [i].found [j])
542                                                         extra = ".  (method might be private or static)";
543                                                 Report.Error (
544                                                         536, container.Location,
545                                                         "`" + container.Name + "' does not implement " +
546                                                         "interface member `" +
547                                                         type.FullName + "." + mi.Name + "'" + extra);
548                                         } else {
549                                                 Report.Error (
550                                                         30610, container.Location,
551                                                         "`" + container.Name + "' does not implement " +
552                                                         "inherited 'MustOverride' member `" +
553                                                         type.FullName + "." + mi.Name + "'");
554                                         }
555                                         errors = true;
556                                         j++;
557                                 }
558                         }
559                         return errors;
560                 }
561         } /* end of class */
562 }