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