2006-04-28 Marek Safar <marek.safar@seznam.cz>
[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                 // 
25                 // Whether it is optional, this is used to allow the explicit/implicit
26                 // implementation when a base class already implements an interface. 
27                 //
28                 // For example:
29                 //
30                 // class X : IA { }  class Y : X, IA { IA.Explicit (); }
31                 //
32                 public bool          optional;
33                 
34                 // Far from ideal, but we want to avoid creating a copy
35                 // of methods above.
36                 public Type [][]     args;
37
38                 //This is used to store the modifiers of arguments
39                 public Parameter.Modifier [][] mods;
40                 
41                 //
42                 // This flag on the method says `We found a match, but
43                 // because it was private, we could not use the match
44                 //
45                 public bool []       found;
46
47                 // If a method is defined here, then we always need to
48                 // create a proxy for it.  This is used when implementing
49                 // an interface's indexer with a different IndexerName.
50                 public MethodInfo [] need_proxy;
51
52                 //
53                 // The name of the indexer (if it exists), precompute set/get, because
54                 // they would be recomputed many times inside a loop later on.
55                 //
56                 public string set_indexer_name;
57                 public string get_indexer_name;
58         }
59
60         public class PendingImplementation {
61                 /// <summary>
62                 ///   The container for this PendingImplementation
63                 /// </summary>
64                 TypeContainer container;
65                 
66                 /// <summary>
67                 ///   This filter is used by FindMembers, and it is used to
68                 ///   extract only virtual/abstract fields
69                 /// </summary>
70                 static MemberFilter virtual_method_filter;
71
72                 /// <summary>
73                 ///   This is the array of TypeAndMethods that describes the pending implementations
74                 ///   (both interfaces and abstract methods in base class)
75                 /// </summary>
76                 TypeAndMethods [] pending_implementations;
77
78                 static bool IsVirtualFilter (MemberInfo m, object filterCriteria)
79                 {
80                         MethodInfo mi = m as MethodInfo;
81                         return (mi == null) ? false : mi.IsVirtual;
82                 }
83
84                 /// <summary>
85                 ///   Inits the virtual_method_filter
86                 /// </summary>
87                 static PendingImplementation ()
88                 {
89                         virtual_method_filter = new MemberFilter (IsVirtualFilter);
90                 }
91
92                 // <remarks>
93                 //   Returns a list of the abstract methods that are exposed by all of our
94                 //   bases that we must implement.  Notice that this `flattens' the
95                 //   method search space, and takes into account overrides.  
96                 // </remarks>
97                 static ArrayList GetAbstractMethods (Type t)
98                 {
99                         ArrayList list = null;
100                         bool searching = true;
101                         Type current_type = t;
102                         
103                         do {
104                                 MemberList mi;
105                                 
106                                 mi = TypeContainer.FindMembers (
107                                         current_type, MemberTypes.Method,
108                                         BindingFlags.Public | BindingFlags.NonPublic |
109                                         BindingFlags.Instance | BindingFlags.DeclaredOnly,
110                                         virtual_method_filter, null);
111
112                                 if (current_type == TypeManager.object_type)
113                                         searching = false;
114                                 else {
115                                         current_type = current_type.BaseType;
116                                         if (!current_type.IsAbstract)
117                                                 searching = false;
118                                 }
119
120                                 if (mi.Count == 0)
121                                         continue;
122
123                                 if (mi.Count == 1 && !(mi [0] is MethodBase))
124                                         searching = false;
125                                 else 
126                                         list = TypeManager.CopyNewMethods (list, mi);
127                         } while (searching);
128
129                         if (list == null)
130                                 return null;
131                         
132                         for (int i = 0; i < list.Count; i++){
133                                 while (list.Count > i && !((MethodInfo) list [i]).IsAbstract)
134                                         list.RemoveAt (i);
135                         }
136
137                         if (list.Count == 0)
138                                 return null;
139
140                         return list;
141                 }
142
143                 PendingImplementation (TypeContainer container, MissingInterfacesInfo [] missing_ifaces, ArrayList abstract_methods, int total)
144                 {
145                         TypeBuilder type_builder = container.TypeBuilder;
146                         
147                         this.container = container;
148                         pending_implementations = new TypeAndMethods [total];
149
150                         int i = 0;
151                         foreach (MissingInterfacesInfo missing in missing_ifaces){
152                                 MethodInfo [] mi;
153                                 Type t = missing.Type;
154
155                                 if (!t.IsInterface)
156                                         continue;
157
158                                 if (t is TypeBuilder){
159                                         TypeContainer iface;
160
161                                         iface = TypeManager.LookupInterface (t);
162
163                                         mi = iface.GetMethods ();
164                                 } else 
165                                         mi = t.GetMethods ();
166
167                                 int count = mi.Length;
168                                 pending_implementations [i].type = t;
169                                 pending_implementations [i].optional = missing.Optional;
170                                 pending_implementations [i].methods = mi;
171                                 pending_implementations [i].args = new Type [count][];
172                                 pending_implementations [i].mods = new Parameter.Modifier [count][];
173                                 pending_implementations [i].found = new bool [count];
174                                 pending_implementations [i].need_proxy = new MethodInfo [count];
175                                 string indexer_name = TypeManager.IndexerPropertyName (t);
176
177                                 pending_implementations [i].set_indexer_name = "set_" + indexer_name;
178                                 pending_implementations [i].get_indexer_name = "get_" + indexer_name;
179                                 
180                                 int j = 0;
181                                 foreach (MethodInfo m in mi){
182                                         pending_implementations [i].args [j] = Type.EmptyTypes;
183                                         pending_implementations [i].mods [j] = null;
184
185                                         // If there is a previous error, just ignore
186                                         if (m == null)
187                                                 continue;
188
189                                         ParameterData pd = TypeManager.GetParameterData (m);
190                                         pending_implementations [i].args [j] = pd.Types;
191                                         
192                                         if (pd.Count > 0){
193                                                 Parameter.Modifier [] pm = new Parameter.Modifier [pd.Count];
194                                                 for (int k = 0; k < pd.Count; k++)
195                                                         pm [k] = pd.ParameterModifier (k);
196                                                 pending_implementations [i].mods [j] = pm;
197                                         }
198                         
199                                         j++;
200                                 }
201                                 i++;
202                         }
203
204                         if (abstract_methods != null){
205                                 int count = abstract_methods.Count;
206                                 pending_implementations [i].methods = new MethodInfo [count];
207                                 pending_implementations [i].need_proxy = new MethodInfo [count];
208                                 
209                                 abstract_methods.CopyTo (pending_implementations [i].methods, 0);
210                                 pending_implementations [i].found = new bool [count];
211                                 pending_implementations [i].args = new Type [count][];
212                                 pending_implementations [i].mods = new Parameter.Modifier [count][];
213                                 pending_implementations [i].type = type_builder;
214
215                                 string indexer_name = TypeManager.IndexerPropertyName (type_builder);
216                                 pending_implementations [i].set_indexer_name = "set_" + indexer_name;
217                                 pending_implementations [i].get_indexer_name = "get_" + indexer_name;
218                                 
219                                 int j = 0;
220                                 foreach (MemberInfo m in abstract_methods){
221                                         MethodInfo mi = (MethodInfo) m;
222                                         
223                                         ParameterData pd = TypeManager.GetParameterData (mi);
224                                         Type [] types = pd.Types;
225                                         
226                                         pending_implementations [i].args [j] = types;
227                                         pending_implementations [i].mods [j] = null;
228                                         if (pd.Count > 0){
229                                                 Parameter.Modifier [] pm = new Parameter.Modifier [pd.Count];
230                                                 for (int k = 0; k < pd.Count; k++)
231                                                         pm [k] = pd.ParameterModifier (k);
232                                                 pending_implementations [i].mods [j] = pm;
233                                         }
234                                                 
235                                         j++;
236                                 }
237                         }
238                 }
239
240                 struct MissingInterfacesInfo {
241                         public Type Type;
242                         public bool Optional;
243
244                         public MissingInterfacesInfo (Type t)
245                         {
246                                 Type = t;
247                                 Optional = false;
248                         }
249                 }
250
251                 static MissingInterfacesInfo [] EmptyMissingInterfacesInfo = new MissingInterfacesInfo [0];
252                 
253                 static MissingInterfacesInfo [] GetMissingInterfaces (TypeBuilder type_builder)
254                 {
255                         //
256                         // Notice that TypeBuilders will only return the interfaces that the Type
257                         // is supposed to implement, not all the interfaces that the type implements.
258                         //
259                         // Even better -- on MS it returns an empty array, no matter what.
260                         //
261                         // Completely broken.  So we do it ourselves!
262                         //
263                         Type [] impl = TypeManager.GetExplicitInterfaces (type_builder);
264
265                         if (impl == null || impl.Length == 0)
266                                 return EmptyMissingInterfacesInfo;
267
268                         MissingInterfacesInfo [] ret = new MissingInterfacesInfo [impl.Length];
269
270                         for (int i = 0; i < impl.Length; i++)
271                                 ret [i] = new MissingInterfacesInfo (impl [i]);
272                         
273                         // we really should not get here because Object doesnt implement any
274                         // interfaces. But it could implement something internal, so we have
275                         // to handle that case.
276                         if (type_builder.BaseType == null)
277                                 return ret;
278                         
279                         Type [] base_impls = TypeManager.GetInterfaces (type_builder.BaseType);
280                         
281                         foreach (Type t in base_impls) {
282                                 for (int i = 0; i < ret.Length; i ++) {
283                                         if (t == ret [i].Type) {
284                                                 ret [i].Optional = true;
285                                                 break;
286                                         }
287                                 }
288                         }
289                         return ret;
290                 }
291                 
292                 //
293                 // Factory method: if there are pending implementation methods, we return a PendingImplementation
294                 // object, otherwise we return null.
295                 //
296                 // Register method implementations are either abstract methods
297                 // flagged as such on the base class or interface methods
298                 //
299                 static public PendingImplementation GetPendingImplementations (TypeContainer container)
300                 {
301                         TypeBuilder type_builder = container.TypeBuilder;
302                         MissingInterfacesInfo [] missing_interfaces;
303                         Type b = type_builder.BaseType;
304
305                         missing_interfaces = GetMissingInterfaces (type_builder);
306
307                         //
308                         // If we are implementing an abstract class, and we are not
309                         // ourselves abstract, and there are abstract methods (C# allows
310                         // abstract classes that have no abstract methods), then allocate
311                         // one slot.
312                         //
313                         // We also pre-compute the methods.
314                         //
315                         bool implementing_abstract = ((b != null) && b.IsAbstract && !type_builder.IsAbstract);
316                         ArrayList abstract_methods = null;
317
318                         if (implementing_abstract){
319                                 abstract_methods = GetAbstractMethods (b);
320                                 
321                                 if (abstract_methods == null)
322                                         implementing_abstract = false;
323                         }
324                         
325                         int total = missing_interfaces.Length +  (implementing_abstract ? 1 : 0);
326                         if (total == 0)
327                                 return null;
328
329                         return new PendingImplementation (container, missing_interfaces, abstract_methods, total);
330                 }
331
332                 public enum Operation {
333                         //
334                         // If you change this, review the whole InterfaceMethod routine as there
335                         // are a couple of assumptions on these three states
336                         //
337                         Lookup, ClearOne, ClearAll
338                 }
339
340                 /// <summary>
341                 ///   Whether the specified method is an interface method implementation
342                 /// </summary>
343                 public MethodInfo IsInterfaceMethod (Type t, string name, Type ret_type, ParameterData args)
344                 {
345                         return InterfaceMethod (t, name, ret_type, args, Operation.Lookup, null);
346                 }
347
348                 public MethodInfo IsInterfaceIndexer (Type t, Type ret_type, ParameterData args)
349                 {
350                         return InterfaceMethod (t, null, ret_type, args, Operation.Lookup, null);
351                 }
352
353                 public void ImplementMethod (Type t, string name, Type ret_type, ParameterData args, bool clear_one) 
354                 {
355                         InterfaceMethod (t, name, ret_type, args,
356                                          clear_one ? Operation.ClearOne : Operation.ClearAll, null);
357                 }
358
359                 public void ImplementIndexer (Type t, MethodInfo mi, Type ret_type, ParameterData args, bool clear_one) 
360                 {
361                         InterfaceMethod (t, null, ret_type, args,
362                                          clear_one ? Operation.ClearOne : Operation.ClearAll, mi);
363                 }
364                 
365                 /// <remarks>
366                 ///   If a method in Type `t' (or null to look in all interfaces
367                 ///   and the base abstract class) with name `Name', return type `ret_type' and
368                 ///   arguments `args' implements an interface, this method will
369                 ///   return the MethodInfo that this method implements.
370                 ///
371                 ///   If `name' is null, we operate solely on the method's signature.  This is for
372                 ///   instance used when implementing indexers.
373                 ///
374                 ///   The `Operation op' controls whether to lookup, clear the pending bit, or clear
375                 ///   all the methods with the given signature.
376                 ///
377                 ///   The `MethodInfo need_proxy' is used when we're implementing an interface's
378                 ///   indexer in a class.  If the new indexer's IndexerName does not match the one
379                 ///   that was used in the interface, then we always need to create a proxy for it.
380                 ///
381                 /// </remarks>
382                 public MethodInfo InterfaceMethod (Type t, string name, Type ret_type, ParameterData args,
383                                                    Operation op, MethodInfo need_proxy)
384                 {
385                         int arg_len = args.Count;
386
387                         if (pending_implementations == null)
388                                 return null;
389
390                         foreach (TypeAndMethods tm in pending_implementations){
391                                 if (!(t == null || tm.type == t))
392                                         continue;
393
394                                 int method_count = tm.methods.Length;
395                                 MethodInfo m;
396                                 for (int i = 0; i < method_count; i++){
397                                         m = tm.methods [i];
398
399                                         if (m == null)
400                                                 continue;
401
402                                         //
403                                         // `need_proxy' is not null when we're implementing an
404                                         // interface indexer and this is Clear(One/All) operation.
405                                         //
406                                         // If `name' is null, then we do a match solely based on the
407                                         // signature and not on the name (this is done in the Lookup
408                                         // for an interface indexer).
409                                         //
410                                         if (name == null){
411                                                 if (m.Name != tm.get_indexer_name && m.Name != tm.set_indexer_name)
412                                                         continue;
413                                         } else if ((need_proxy == null) && (name != m.Name))
414                                                 continue;
415
416                                         if (ret_type != m.ReturnType &&
417                                             !(ret_type == null && m.ReturnType == TypeManager.void_type) &&
418                                             !(m.ReturnType == null && ret_type == TypeManager.void_type))
419                                                 continue;
420
421                                         //
422                                         // Check if we have the same parameters
423                                         //
424
425                                         if (tm.args [i] == null && arg_len != 0)
426                                                 continue;
427                                         if (tm.args [i] != null && tm.args [i].Length != arg_len)
428                                                 continue;
429
430                                         int j;
431
432                                         for (j = 0; j < arg_len; j++) {
433                                                 if (tm.args [i][j] != args.ParameterType (j))
434                                                         break;
435                                                 if (tm.mods [i][j] == args.ParameterModifier (j))
436                                                         continue;
437                                                 // The modifiers are different, but if one of them
438                                                 // is a PARAMS modifier, and the other isn't, ignore
439                                                 // the difference.
440                                                 if (tm.mods [i][j] != Parameter.Modifier.PARAMS &&
441                                                     args.ParameterModifier (j) != Parameter.Modifier.PARAMS)
442                                                         break;
443                                         }
444                                         if (j != arg_len)
445                                                 continue;
446
447                                         if (op != Operation.Lookup){
448                                                 // If `t != null', then this is an explicitly interface
449                                                 // implementation and we can always clear the method.
450                                                 // `need_proxy' is not null if we're implementing an
451                                                 // interface indexer.  In this case, we need to create
452                                                 // a proxy if the implementation's IndexerName doesn't
453                                                 // match the IndexerName in the interface.
454                                                 bool name_matches = false;
455                                                 if (name == m.Name || m.Name == tm.get_indexer_name || m.Name == tm.set_indexer_name)
456                                                         name_matches = true;
457                                                 
458                                                 if ((t == null) && (need_proxy != null) && !name_matches)
459                                                         tm.need_proxy [i] = need_proxy;
460                                                 else 
461                                                         tm.methods [i] = null;
462                                         }
463                                         tm.found [i] = true;
464
465                                         //
466                                         // Lookups and ClearOne return
467                                         //
468                                         if (op != Operation.ClearAll)
469                                                 return m;
470                                 }
471
472                                 // If a specific type was requested, we can stop now.
473                                 if (tm.type == t)
474                                         return null;
475                         }
476                         return null;
477                 }
478
479                 /// <summary>
480                 ///   C# allows this kind of scenarios:
481                 ///   interface I { void M (); }
482                 ///   class X { public void M (); }
483                 ///   class Y : X, I { }
484                 ///
485                 ///   For that case, we create an explicit implementation function
486                 ///   I.M in Y.
487                 /// </summary>
488                 void DefineProxy (Type iface, MethodInfo base_method, MethodInfo iface_method,
489                                   Type [] args)
490                 {
491                         MethodBuilder proxy;
492
493                         string proxy_name = iface.Name + "." + iface_method.Name;
494
495                         proxy = container.TypeBuilder.DefineMethod (
496                                 proxy_name,
497                                 MethodAttributes.HideBySig |
498                                 MethodAttributes.NewSlot |
499                                 MethodAttributes.Virtual,
500                                 CallingConventions.Standard | CallingConventions.HasThis,
501                                 base_method.ReturnType, args);
502
503                         int top = args.Length;
504                         ILGenerator ig = proxy.GetILGenerator ();
505
506                         for (int i = 0; i <= top; i++)
507                                 ParameterReference.EmitLdArg (ig, i);
508
509                         ig.Emit (OpCodes.Call, base_method);
510                         ig.Emit (OpCodes.Ret);
511
512                         container.TypeBuilder.DefineMethodOverride (proxy, iface_method);
513                 }
514                 
515                 /// <summary>
516                 ///   This function tells whether one of our base classes implements
517                 ///   the given method (which turns out, it is valid to have an interface
518                 ///   implementation in a base
519                 /// </summary>
520                 bool BaseImplements (Type iface_type, MethodInfo mi)
521                 {
522                         MethodSignature ms;
523                         
524                         Type [] args = TypeManager.GetParameterData (mi).Types;
525                         ms = new MethodSignature (mi.Name, mi.ReturnType, args);
526                         MemberList list = TypeContainer.FindMembers (
527                                 container.TypeBuilder.BaseType, MemberTypes.Method | MemberTypes.Property,
528                                 BindingFlags.Public | BindingFlags.Instance,
529                                 MethodSignature.method_signature_filter, ms);
530
531                         if (list.Count == 0)
532                                 return false;
533
534                         if (TypeManager.ImplementsInterface (container.TypeBuilder.BaseType, iface_type))
535                                 return true;
536
537                         //
538                         // FIXME: We should be creating fewer proxies.  The runtime can handle most cases.  
539                         //        At worst, if we can't avoid creating the proxy, we may need to make the 
540                         //        proxy use Callvirt.
541                         //
542                         MethodInfo base_method = (MethodInfo) list [0];
543
544                         if (!base_method.IsAbstract && !base_method.IsVirtual)
545                                 DefineProxy (iface_type, base_method, mi, args);
546
547                         return true;
548                 }
549
550                 /// <summary>
551                 ///   Verifies that any pending abstract methods or interface methods
552                 ///   were implemented.
553                 /// </summary>
554                 public bool VerifyPendingMethods ()
555                 {
556                         int top = pending_implementations.Length;
557                         bool errors = false;
558                         int i;
559                         
560                         for (i = 0; i < top; i++){
561                                 Type type = pending_implementations [i].type;
562                                 int j = 0;
563
564                                 foreach (MethodInfo mi in pending_implementations [i].methods){
565                                         if (mi == null)
566                                                 continue;
567
568                                         if (type.IsInterface){
569                                                 MethodInfo need_proxy =
570                                                         pending_implementations [i].need_proxy [j];
571
572                                                 if (need_proxy != null) {
573                                                         Type [] args = TypeManager.GetParameterData (mi).Types;
574                                                         DefineProxy (type, need_proxy, mi, args);
575                                                         continue;
576                                                 }
577
578                                                 if (BaseImplements (type, mi))
579                                                         continue;
580                                                 
581                                                 if (pending_implementations [i].optional)
582                                                         continue;
583
584                                                 Report.SymbolRelatedToPreviousError (mi);
585                                                 if (pending_implementations [i].found [j]) {
586                                                         if (mi.IsSpecialName) {
587                                                                 string name = TypeManager.CSharpName (mi.DeclaringType) + '.' + mi.Name.Substring (4);
588                                                                 Report.Error (551, container.Location, "Explicit interface implementation `{0}.{1}' is missing accessor `{2}'",
589                                                                         container.GetSignatureForError (), name, TypeManager.CSharpSignature (mi, true));
590                                                         } else {
591                                                                 string[] methodLabel = TypeManager.CSharpSignature (mi).Split ('.');
592                                                                 Report.Error (536, container.Location,
593                                                                         "`{0}' does not implement interface member `{1}'. `{2}.{3}' " +
594                                                                         "is either static, not public, or has the wrong return type",
595                                                                         container.Name, TypeManager.CSharpSignature (mi),
596                                                                         container.Name, methodLabel[methodLabel.Length - 1]);
597                                                         }
598                                                 }
599                                                 else {
600                                                         Report.Error (535, container.Location, "`{0}' does not implement interface member `{1}'",
601                                                                 container.GetSignatureForError (), TypeManager.CSharpSignature (mi));
602                                                 }
603                                         } else {
604                                                 Report.Error (534, container.Location, "`{0}' does not implement inherited abstract member `{1}'",
605                                                         container.GetSignatureForError (), TypeManager.CSharpSignature (mi, true));
606                                         }
607                                         errors = true;
608                                         j++;
609                                 }
610                         }
611                         return errors;
612                 }
613         } /* end of class */
614 }