2002-08-13 Martin Baulig <martin@gnome.org>
[mono.git] / mcs / mcs / 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 ();
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 #if DEBUG
210                         {
211                                 Type x = type_builder;
212
213                                 while (x != null){
214                                         Type [] iff = x.GetInterfaces ();
215                                         Console.WriteLine ("Type: " + x.Name);
216                                         
217                                         foreach (Type tt in iff){
218                                                 Console.WriteLine ("  Iface: " + tt.Name);
219                                         }
220                                         x = x.BaseType;
221                                 }
222                         }
223 #endif
224                                         
225                         icount = ifaces.Length;
226
227                         //
228                         // If we are implementing an abstract class, and we are not
229                         // ourselves abstract, and there are abstract methods (C# allows
230                         // abstract classes that have no abstract methods), then allocate
231                         // one slot.
232                         //
233                         // We also pre-compute the methods.
234                         //
235                         bool implementing_abstract = ((b != null) && b.IsAbstract && !type_builder.IsAbstract);
236                         ArrayList abstract_methods = null;
237
238                         if (implementing_abstract){
239                                 abstract_methods = GetAbstractMethods (b);
240                                 
241                                 if (abstract_methods == null)
242                                         implementing_abstract = false;
243                         }
244                         
245                         int total = icount +  (implementing_abstract ? 1 : 0);
246                         if (total == 0)
247                                 return null;
248
249                         return new PendingImplementation (container, ifaces, abstract_methods, total);
250                 }
251
252                 public enum Operation {
253                         //
254                         // If you change this, review the whole InterfaceMethod routine as there
255                         // are a couple of assumptions on these three states
256                         //
257                         Lookup, ClearOne, ClearAll
258                 }
259
260                 /// <summary>
261                 ///   Whether the specified method is an interface method implementation
262                 /// </summary>
263                 public MethodInfo IsInterfaceMethod (Type t, string name, Type ret_type, Type [] args)
264                 {
265                         return InterfaceMethod (t, name, ret_type, args, Operation.Lookup, null);
266                 }
267
268                 public MethodInfo IsInterfaceIndexer (Type t, Type ret_type, Type [] args)
269                 {
270                         return InterfaceMethod (t, null, ret_type, args, Operation.Lookup, null);
271                 }
272
273                 public void ImplementMethod (Type t, string name, Type ret_type, Type [] args, bool clear_one) 
274                 {
275                         InterfaceMethod (t, name, ret_type, args,
276                                          clear_one ? Operation.ClearOne : Operation.ClearAll, null);
277                 }
278
279                 public void ImplementIndexer (Type t, MethodInfo mi, Type ret_type, Type [] args, bool clear_one) 
280                 {
281                         InterfaceMethod (t, mi.Name, ret_type, args,
282                                          clear_one ? Operation.ClearOne : Operation.ClearAll, mi);
283                 }
284                 
285                 /// <remarks>
286                 ///   If a method in Type `t' (or null to look in all interfaces
287                 ///   and the base abstract class) with name `Name', return type `ret_type' and
288                 ///   arguments `args' implements an interface, this method will
289                 ///   return the MethodInfo that this method implements.
290                 ///
291                 ///   If `name' is null, we operate solely on the method's signature.  This is for
292                 ///   instance used when implementing indexers.
293                 ///
294                 ///   The `Operation op' controls whether to lookup, clear the pending bit, or clear
295                 ///   all the methods with the given signature.
296                 ///
297                 ///   The `MethodInfo need_proxy' is used when we're implementing an interface's
298                 ///   indexer in a class.  If the new indexer's IndexerName does not match the one
299                 ///   that was used in the interface, then we always need to create a proxy for it.
300                 ///
301                 /// </remarks>
302                 public MethodInfo InterfaceMethod (Type t, string name, Type ret_type, Type [] args,
303                                                    Operation op, MethodInfo need_proxy)
304                 {
305                         int arg_len = args.Length;
306
307                         if (pending_implementations == null)
308                                 return null;
309
310                         foreach (TypeAndMethods tm in pending_implementations){
311                                 if (!(t == null || tm.type == t))
312                                         continue;
313
314                                 int i = 0;
315                                 foreach (MethodInfo m in tm.methods){
316                                         if (m == null){
317                                                 i++;
318                                                 continue;
319                                         }
320
321                                         // `need_proxy' is not null when we're implementing an
322                                         // interface indexer and this is Clear(One/All) operation.
323                                         // If `name' is null, then we do a match solely based on the
324                                         // signature and not on the name (this is done in the Lookup
325                                         // for an interface indexer).
326                                         if ((name != null) && (need_proxy == null) && (name != m.Name)){
327                                                 i++;
328                                                 continue;
329                                         }
330
331                                         if (ret_type != m.ReturnType){
332                                                 if (!((ret_type == null && m.ReturnType == TypeManager.void_type) ||
333                                                       (m.ReturnType == null && ret_type == TypeManager.void_type)))
334                                                 {
335                                                         i++;
336                                                         continue;
337                                                 }
338                                         }
339
340                                         //
341                                         // Check if we have the same parameters
342                                         //
343                                         if (tm.args [i].Length != arg_len){
344                                                 i++;
345                                                 continue;
346                                         }
347
348                                         int j, top = args.Length;
349                                         bool fail = false;
350                                         
351                                         for (j = 0; j < top; j++){
352                                                 if (tm.args [i][j] != args[j]){
353                                                         fail = true;
354                                                         break;
355                                                 }
356                                         }
357                                         if (fail){
358                                                 i++;
359                                                 continue;
360                                         }
361
362                                         if (op != Operation.Lookup){
363                                                 // If `t != null', then this is an explicitly interface
364                                                 // implementation and we can always clear the method.
365                                                 // `need_proxy' is not null if we're implementing an
366                                                 // interface indexer.  In this case, we need to create
367                                                 // a proxy if the implementation's IndexerName doesn't
368                                                 // match the IndexerName in the interface.
369                                                 if ((t == null) && (need_proxy != null) && (name != m.Name))
370                                                         tm.need_proxy [i] = need_proxy;
371                                                 else
372                                                         tm.methods [i] = null;
373                                         }
374                                         tm.found [i] = true;
375
376                                         //
377                                         // Lookups and ClearOne return
378                                         //
379                                         if (op != Operation.ClearAll)
380                                                 return m;
381                                 }
382
383                                 // If a specific type was requested, we can stop now.
384                                 if (tm.type == t)
385                                         return null;
386                         }
387                         return null;
388                 }
389
390                 /// <summary>
391                 ///   C# allows this kind of scenarios:
392                 ///   interface I { void M (); }
393                 ///   class X { public void M (); }
394                 ///   class Y : X, I { }
395                 ///
396                 ///   For that case, we create an explicit implementation function
397                 ///   I.M in Y.
398                 /// </summary>
399                 void DefineProxy (Type iface, MethodInfo parent_method, MethodInfo iface_method,
400                                   Type [] args)
401                 {
402                         MethodBuilder proxy;
403
404                         string proxy_name = iface.Name + "." + iface_method.Name;
405
406                         proxy = container.TypeBuilder.DefineMethod (
407                                 proxy_name,
408                                 MethodAttributes.HideBySig |
409                                 MethodAttributes.NewSlot |
410                                 MethodAttributes.Virtual,
411                                 CallingConventions.Standard | CallingConventions.HasThis,
412                                 parent_method.ReturnType, args);
413
414                         int top = args.Length;
415                         ILGenerator ig = proxy.GetILGenerator ();
416
417                         ig.Emit (OpCodes.Ldarg_0);
418                         for (int i = 0; i < top; i++){
419                                 switch (i){
420                                 case 0:
421                                         ig.Emit (OpCodes.Ldarg_1); break;
422                                 case 1:
423                                         ig.Emit (OpCodes.Ldarg_2); break;
424                                 case 2:
425                                         ig.Emit (OpCodes.Ldarg_3); break;
426                                 default:
427                                         ig.Emit (OpCodes.Ldarg, i - 1); break;
428                                 }
429                         }
430                         ig.Emit (OpCodes.Call, parent_method);
431                         ig.Emit (OpCodes.Ret);
432
433                         container.TypeBuilder.DefineMethodOverride (proxy, iface_method);
434                 }
435                 
436                 /// <summary>
437                 ///   This function tells whether one of our parent classes implements
438                 ///   the given method (which turns out, it is valid to have an interface
439                 ///   implementation in a parent
440                 /// </summary>
441                 bool ParentImplements (Type iface_type, MethodInfo mi)
442                 {
443                         MethodSignature ms;
444                         
445                         Type [] args = TypeManager.GetArgumentTypes (mi);
446                         ms = new MethodSignature (mi.Name, mi.ReturnType, args);
447                         MemberList list = TypeContainer.FindMembers (
448                                 container.TypeBuilder.BaseType, MemberTypes.Method | MemberTypes.Property,
449                                 BindingFlags.Public | BindingFlags.Instance,
450                                 MethodSignature.method_signature_filter, ms);
451
452                         if (list.Count == 0)
453                                 return false;
454
455                         DefineProxy (iface_type, (MethodInfo) list [0], mi, args);
456                         return true;
457                 }
458
459                 /// <summary>
460                 ///   Verifies that any pending abstract methods or interface methods
461                 ///   were implemented.
462                 /// </summary>
463                 public bool VerifyPendingMethods ()
464                 {
465                         int top = pending_implementations.Length;
466                         bool errors = false;
467                         int i;
468                         
469                         for (i = 0; i < top; i++){
470                                 Type type = pending_implementations [i].type;
471                                 int j = 0;
472                                 
473                                 foreach (MethodInfo mi in pending_implementations [i].methods){
474                                         if (mi == null)
475                                                 continue;
476
477                                         if (type.IsInterface){
478                                                 MethodInfo need_proxy =
479                                                         pending_implementations [i].need_proxy [j];
480
481                                                 if (need_proxy != null) {
482                                                         Type [] args = TypeManager.GetArgumentTypes (mi);
483                                                         DefineProxy (type, need_proxy, mi, args);
484                                                         continue;
485                                                 }
486
487                                                 if (ParentImplements (type, mi))
488                                                         continue;
489
490                                                 string extra = "";
491                                                 
492                                                 if (pending_implementations [i].found [j])
493                                                         extra = ".  (method might be private or static)";
494                                                 Report.Error (
495                                                         536, container.Location,
496                                                         "`" + container.Name + "' does not implement " +
497                                                         "interface member `" +
498                                                         type.FullName + "." + mi.Name + "'" + extra);
499                                         } else {
500                                                 Report.Error (
501                                                         534, container.Location,
502                                                         "`" + container.Name + "' does not implement " +
503                                                         "inherited abstract member `" +
504                                                         type.FullName + "." + mi.Name + "'");
505                                         }
506                                         errors = true;
507                                         j++;
508                                 }
509                         }
510                         return errors;
511                 }
512         } /* end of class */
513 }