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