2002-07-22 Tim Coleman <tim@timcoleman.com>
[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
35         public class PendingImplementation {
36                 /// <summary>
37                 ///   The container for this PendingImplementation
38                 /// </summary>
39                 TypeContainer container;
40                 
41                 /// <summary>
42                 ///   This filter is used by FindMembers, and it is used to
43                 ///   extract only virtual/abstract fields
44                 /// </summary>
45                 static MemberFilter virtual_method_filter;
46
47                 /// <summary>
48                 ///   This is the array of TypeAndMethods that describes the pending implementations
49                 ///   (both interfaces and abstract methods in parent class)
50                 /// </summary>
51                 TypeAndMethods [] pending_implementations;
52
53                 static bool IsVirtualFilter (MemberInfo m, object filterCriteria)
54                 {
55                         if (!(m is MethodInfo))
56                                 return false;
57
58                         return ((MethodInfo) m).IsVirtual;
59                 }
60
61                 /// <summary>
62                 ///   Inits the virtual_method_filter
63                 /// </summary>
64                 static PendingImplementation ()
65                 {
66                         virtual_method_filter = new MemberFilter (IsVirtualFilter);
67                 }
68
69                 // <remarks>
70                 //   Returns a list of the abstract methods that are exposed by all of our
71                 //   parents that we must implement.  Notice that this `flattens' the
72                 //   method search space, and takes into account overrides.  
73                 // </remarks>
74                 static ArrayList GetAbstractMethods (Type t)
75                 {
76                         ArrayList list = null;
77                         bool searching = true;
78                         Type current_type = t;
79                         
80                         do {
81                                 MemberInfo [] mi;
82                                 
83                                 mi = TypeContainer.FindMembers (
84                                         current_type, MemberTypes.Method,
85                                         BindingFlags.Public | BindingFlags.Instance |
86                                         BindingFlags.DeclaredOnly,
87                                         virtual_method_filter, null);
88
89                                 if (current_type == TypeManager.object_type)
90                                         searching = false;
91                                 else {
92                                         current_type = current_type.BaseType;
93                                         if (!current_type.IsAbstract)
94                                                 searching = false;
95                                 }
96
97                                 if (mi == null)
98                                         continue;
99
100                                 int count = mi.Length;
101                                 if (count == 0)
102                                         continue;
103
104                                 if (count == 1 && !(mi [0] is MethodBase))
105                                         searching = false;
106                                 else 
107                                         list = TypeManager.CopyNewMethods (list, mi);
108                         } while (searching);
109
110                         if (list == null)
111                                 return null;
112                         
113                         for (int i = 0; i < list.Count; i++){
114                                 while (list.Count > i && !((MethodInfo) list [i]).IsAbstract)
115                                         list.RemoveAt (i);
116                         }
117
118                         if (list.Count == 0)
119                                 return null;
120
121                         return list;
122                 }
123
124                 PendingImplementation (TypeContainer container, Type [] ifaces, ArrayList abstract_methods, int total)
125                 {
126                         TypeBuilder type_builder = container.TypeBuilder;
127                         
128                         this.container = container;
129                         pending_implementations = new TypeAndMethods [total];
130
131                         int i = 0;
132                         if (ifaces != null){
133                                 foreach (Type t in ifaces){
134                                         MethodInfo [] mi;
135
136                                         if (t is TypeBuilder){
137                                                 Interface iface;
138
139                                                 iface = TypeManager.LookupInterface (t);
140                                                 
141                                                 mi = iface.GetMethods ();
142                                         } else
143                                                 mi = t.GetMethods ();
144
145                                         int count = mi.Length;
146                                         pending_implementations [i].type = t;
147                                         pending_implementations [i].methods = mi;
148                                         pending_implementations [i].args = new Type [count][];
149                                         pending_implementations [i].found = new bool [count];
150
151                                         int j = 0;
152                                         foreach (MethodInfo m in mi){
153                                                 Type [] types = TypeManager.GetArgumentTypes (m);
154
155                                                 pending_implementations [i].args [j] = types;
156                                                 j++;
157                                         }
158                                         i++;
159                                 }
160                         }
161
162                         if (abstract_methods != null){
163                                 int count = abstract_methods.Count;
164                                 pending_implementations [i].methods = new MethodInfo [count];
165                                 
166                                 abstract_methods.CopyTo (pending_implementations [i].methods, 0);
167                                 pending_implementations [i].found = new bool [count];
168                                 pending_implementations [i].args = new Type [count][];
169                                 pending_implementations [i].type = type_builder;
170                                 
171                                 int j = 0;
172                                 foreach (MemberInfo m in abstract_methods){
173                                         MethodInfo mi = (MethodInfo) m;
174                                         
175                                         Type [] types = TypeManager.GetArgumentTypes (mi);
176                                         
177                                         pending_implementations [i].args [j] = types;
178                                         j++;
179                                 }
180                         }
181                 }
182                 
183                 //
184                 // Factory method: if there are pending implementation methods, we return a PendingImplementation
185                 // object, otherwise we return null.
186                 //
187                 // Register method implementations are either abstract methods
188                 // flagged as such on the base class or interface methods
189                 //
190                 static public PendingImplementation GetPendingImplementations (TypeContainer container)
191                 {
192                         TypeBuilder type_builder = container.TypeBuilder;
193                         Type [] ifaces;
194                         Type b = type_builder.BaseType;
195                         int icount = 0;
196
197                         //
198                         // Notice that TypeBuilders will only return the interfaces that the Type
199                         // is supposed to implement, not all the interfaces that the type implements.
200                         //
201                         // Completely broken.  Anyways, we take advantage of this, so we only register
202                         // the implementations that we need, as they are those that are listed by the
203                         // TypeBuilder.
204                         //
205                         ifaces = type_builder.GetInterfaces ();
206 #if DEBUG
207                         {
208                                 Type x = type_builder;
209
210                                 while (x != null){
211                                         Type [] iff = x.GetInterfaces ();
212                                         Console.WriteLine ("Type: " + x.Name);
213                                         
214                                         foreach (Type tt in iff){
215                                                 Console.WriteLine ("  Iface: " + tt.Name);
216                                         }
217                                         x = x.BaseType;
218                                 }
219                         }
220 #endif
221                                         
222                         icount = ifaces.Length;
223
224                         //
225                         // If we are implementing an abstract class, and we are not
226                         // ourselves abstract, and there are abstract methods (C# allows
227                         // abstract classes that have no abstract methods), then allocate
228                         // one slot.
229                         //
230                         // We also pre-compute the methods.
231                         //
232                         bool implementing_abstract = ((b != null) && b.IsAbstract && !type_builder.IsAbstract);
233                         ArrayList abstract_methods = null;
234
235                         if (implementing_abstract){
236                                 abstract_methods = GetAbstractMethods (b);
237                                 
238                                 if (abstract_methods == null)
239                                         implementing_abstract = false;
240                         }
241                         
242                         int total = icount +  (implementing_abstract ? 1 : 0);
243                         if (total == 0)
244                                 return null;
245
246                         return new PendingImplementation (container, ifaces, abstract_methods, total);
247                 }
248
249                 public enum Operation {
250                         //
251                         // If you change this, review the whole InterfaceMethod routine as there
252                         // are a couple of assumptions on these three states
253                         //
254                         Lookup, ClearOne, ClearAll
255                 }
256                 
257                 /// <summary>
258                 ///   Whether the specified method is an interface method implementation
259                 /// </summary>
260                 public MethodInfo IsInterfaceMethod (Type t, string name, Type ret_type, Type [] args)
261                 {
262                         return InterfaceMethod (t, name, ret_type, args, Operation.Lookup);
263                 }
264
265                 public void ImplementMethod (Type t, string name, Type ret_type, Type [] args, bool clear_one) 
266                 {
267                         InterfaceMethod (t, name, ret_type, args, clear_one ? Operation.ClearOne : Operation.ClearAll);
268                 }
269                 
270                 /// <remarks>
271                 ///   If a method in Type `t' (or null to look in all interfaces
272                 ///   and the base abstract class) with name `Name', return type `ret_type' and
273                 ///   arguments `args' implements an interface, this method will
274                 ///   return the MethodInfo that this method implements.
275                 ///
276                 ///   The `Operation op' controls whether to lookup, clear the pending bit, or clear
277                 ///   all the methods with the given signature.
278                 /// </remarks>
279                 public MethodInfo InterfaceMethod (Type t, string name, Type ret_type, Type [] args, Operation op)
280                 {
281                         int arg_len = args.Length;
282
283                         if (pending_implementations == null)
284                                 return null;
285
286                         foreach (TypeAndMethods tm in pending_implementations){
287                                 if (!(t == null || tm.type == t))
288                                         continue;
289
290                                 int i = 0;
291                                 foreach (MethodInfo m in tm.methods){
292                                         if (m == null){
293                                                 i++;
294                                                 continue;
295                                         }
296
297                                         if (name != m.Name){
298                                                 i++;
299                                                 continue;
300                                         }
301
302                                         if (ret_type != m.ReturnType){
303                                                 if (!((ret_type == null && m.ReturnType == TypeManager.void_type) ||
304                                                       (m.ReturnType == null && ret_type == TypeManager.void_type)))
305                                                 {
306                                                         i++;
307                                                         continue;
308                                                 }
309                                         }
310
311                                         //
312                                         // Check if we have the same parameters
313                                         //
314                                         if (tm.args [i].Length != arg_len){
315                                                 i++;
316                                                 continue;
317                                         }
318
319                                         int j, top = args.Length;
320                                         bool fail = false;
321                                         
322                                         for (j = 0; j < top; j++){
323                                                 if (tm.args [i][j] != args[j]){
324                                                         fail = true;
325                                                         break;
326                                                 }
327                                         }
328                                         if (fail){
329                                                 i++;
330                                                 continue;
331                                         }
332
333                                         if (op != Operation.Lookup)
334                                                 tm.methods [i] = null;
335                                         tm.found [i] = true;
336
337                                         //
338                                         // Lookups and ClearOne return
339                                         //
340                                         if (op != Operation.ClearAll)
341                                                 return m;
342                                 }
343
344                                 // If a specific type was requested, we can stop now.
345                                 if (tm.type == t)
346                                         return null;
347                         }
348                         return null;
349                 }
350
351                 /// <summary>
352                 ///   C# allows this kind of scenarios:
353                 ///   interface I { void M (); }
354                 ///   class X { public void M (); }
355                 ///   class Y : X, I { }
356                 ///
357                 ///   For that case, we create an explicit implementation function
358                 ///   I.M in Y.
359                 /// </summary>
360                 void DefineProxy (Type iface, MethodInfo parent_method, MethodInfo iface_method,
361                                   Type [] args)
362                 {
363                         MethodBuilder proxy;
364
365                         string proxy_name = iface.Name + "." + iface_method.Name;
366
367                         proxy = container.TypeBuilder.DefineMethod (
368                                 proxy_name,
369                                 MethodAttributes.HideBySig |
370                                 MethodAttributes.NewSlot |
371                                 MethodAttributes.Virtual,
372                                 CallingConventions.Standard | CallingConventions.HasThis,
373                                 parent_method.ReturnType, args);
374
375                         int top = args.Length;
376                         ILGenerator ig = proxy.GetILGenerator ();
377
378                         ig.Emit (OpCodes.Ldarg_0);
379                         for (int i = 0; i < top; i++){
380                                 switch (i){
381                                 case 0:
382                                         ig.Emit (OpCodes.Ldarg_1); break;
383                                 case 1:
384                                         ig.Emit (OpCodes.Ldarg_2); break;
385                                 case 2:
386                                         ig.Emit (OpCodes.Ldarg_3); break;
387                                 default:
388                                         ig.Emit (OpCodes.Ldarg, i - 1); break;
389                                 }
390                         }
391                         ig.Emit (OpCodes.Call, parent_method);
392                         ig.Emit (OpCodes.Ret);
393
394                         container.TypeBuilder.DefineMethodOverride (proxy, iface_method);
395                 }
396                 
397                 /// <summary>
398                 ///   This function tells whether one of our parent classes implements
399                 ///   the given method (which turns out, it is valid to have an interface
400                 ///   implementation in a parent
401                 /// </summary>
402                 bool ParentImplements (Type iface_type, MethodInfo mi)
403                 {
404                         MethodSignature ms;
405                         
406                         Type [] args = TypeManager.GetArgumentTypes (mi);
407                         ms = new MethodSignature (mi.Name, mi.ReturnType, args);
408                         MemberInfo [] list = TypeContainer.FindMembers (
409                                 container.TypeBuilder.BaseType, MemberTypes.Method | MemberTypes.Property,
410                                 BindingFlags.Public | BindingFlags.Instance,
411                                 MethodSignature.method_signature_filter, ms);
412
413                         if (list == null || list.Length == 0)
414                                 return false;
415                         
416                         DefineProxy (iface_type, (MethodInfo) list [0], mi, args);
417                         return true;
418                 }
419                 
420                 /// <summary>
421                 ///   Verifies that any pending abstract methods or interface methods
422                 ///   were implemented.
423                 /// </summary>
424                 public bool VerifyPendingMethods ()
425                 {
426                         int top = pending_implementations.Length;
427                         bool errors = false;
428                         int i;
429                         
430                         for (i = 0; i < top; i++){
431                                 Type type = pending_implementations [i].type;
432                                 int j = 0;
433                                 
434                                 foreach (MethodInfo mi in pending_implementations [i].methods){
435                                         if (mi == null)
436                                                 continue;
437
438                                         if (type.IsInterface){
439                                                 if (ParentImplements (type, mi))
440                                                         continue;
441  
442                                                 string extra = "";
443                                                 
444                                                 if (pending_implementations [i].found [j])
445                                                         extra = ".  (method might be private or static)";
446                                                 Report.Error (
447                                                         536, container.Location,
448                                                         "`" + container.Name + "' does not implement " +
449                                                         "interface member `" +
450                                                         type.FullName + "." + mi.Name + "'" + extra);
451                                         } else {
452                                                 Report.Error (
453                                                         534, container.Location,
454                                                         "`" + container.Name + "' does not implement " +
455                                                         "inherited abstract member `" +
456                                                         type.FullName + "." + mi.Name + "'");
457                                         }
458                                         errors = true;
459                                         j++;
460                                 }
461                         }
462                         return errors;
463                 }
464         } /* end of class */
465 }