[xbuild] Add new reserved properties $(MSBuildThisFile*).
[mono.git] / mcs / mcs / pending.cs
1 //
2 // pending.cs: Pending method implementation
3 //
4 // Authors:
5 //   Miguel de Icaza (miguel@gnu.org)
6 //   Marek Safar (marek.safar@gmail.com)
7 //
8 // Dual licensed under the terms of the MIT X11 or GNU GPL
9 //
10 // Copyright 2001, 2002 Ximian, Inc (http://www.ximian.com)
11 // Copyright 2003-2008 Novell, Inc.
12 //
13
14 using System.Collections.Generic;
15 using System.Linq;
16
17 #if STATIC
18 using IKVM.Reflection;
19 using IKVM.Reflection.Emit;
20 #else
21 using System.Reflection;
22 using System.Reflection.Emit;
23 #endif
24
25 namespace Mono.CSharp {
26
27         struct TypeAndMethods {
28                 public TypeSpec          type;
29                 public IList<MethodSpec> methods;
30
31                 // 
32                 // Whether it is optional, this is used to allow the explicit/implicit
33                 // implementation when a base class already implements an interface. 
34                 //
35                 // For example:
36                 //
37                 // class X : IA { }  class Y : X, IA { IA.Explicit (); }
38                 //
39                 public bool          optional;
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 MethodSpec [] need_proxy;
51         }
52
53         public class PendingImplementation
54         {
55                 /// <summary>
56                 ///   The container for this PendingImplementation
57                 /// </summary>
58                 TypeContainer container;
59                 
60                 /// <summary>
61                 ///   This is the array of TypeAndMethods that describes the pending implementations
62                 ///   (both interfaces and abstract methods in base class)
63                 /// </summary>
64                 TypeAndMethods [] pending_implementations;
65
66                 PendingImplementation (TypeContainer container, MissingInterfacesInfo[] missing_ifaces, MethodSpec[] abstract_methods, int total)
67                 {
68                         var type_builder = container.Definition;
69                         
70                         this.container = container;
71                         pending_implementations = new TypeAndMethods [total];
72
73                         int i = 0;
74                         if (abstract_methods != null) {
75                                 int count = abstract_methods.Length;
76                                 pending_implementations [i].methods = new MethodSpec [count];
77                                 pending_implementations [i].need_proxy = new MethodSpec [count];
78
79                                 pending_implementations [i].methods = abstract_methods;
80                                 pending_implementations [i].found = new MethodData [count];
81                                 pending_implementations [i].type = type_builder;
82                                 ++i;
83                         }
84
85                         foreach (MissingInterfacesInfo missing in missing_ifaces) {
86                                 var iface = missing.Type;
87                                 var mi = MemberCache.GetInterfaceMethods (iface);
88
89                                 int count = mi.Count;
90                                 pending_implementations [i].type = iface;
91                                 pending_implementations [i].optional = missing.Optional;
92                                 pending_implementations [i].methods = mi;
93                                 pending_implementations [i].found = new MethodData [count];
94                                 pending_implementations [i].need_proxy = new MethodSpec [count];
95                                 i++;
96                         }
97                 }
98
99                 struct MissingInterfacesInfo {
100                         public TypeSpec Type;
101                         public bool Optional;
102
103                         public MissingInterfacesInfo (TypeSpec t)
104                         {
105                                 Type = t;
106                                 Optional = false;
107                         }
108                 }
109
110                 static MissingInterfacesInfo [] EmptyMissingInterfacesInfo = new MissingInterfacesInfo [0];
111                 
112                 static MissingInterfacesInfo [] GetMissingInterfaces (TypeContainer container)
113                 {
114                         //
115                         // Notice that Interfaces will only return the interfaces that the Type
116                         // is supposed to implement, not all the interfaces that the type implements.
117                         //
118                         var impl = container.Definition.Interfaces;
119
120                         if (impl == null || impl.Count == 0)
121                                 return EmptyMissingInterfacesInfo;
122
123                         MissingInterfacesInfo[] ret = new MissingInterfacesInfo[impl.Count];
124
125                         for (int i = 0; i < impl.Count; i++)
126                                 ret [i] = new MissingInterfacesInfo (impl [i]);
127
128                         // we really should not get here because Object doesnt implement any
129                         // interfaces. But it could implement something internal, so we have
130                         // to handle that case.
131                         if (container.BaseType == null)
132                                 return ret;
133                         
134                         var base_impls = container.BaseType.Interfaces;
135                         if (base_impls != null) {
136                                 foreach (TypeSpec t in base_impls) {
137                                         for (int i = 0; i < ret.Length; i++) {
138                                                 if (t == ret[i].Type) {
139                                                         ret[i].Optional = true;
140                                                         break;
141                                                 }
142                                         }
143                                 }
144                         }
145
146                         return ret;
147                 }
148                 
149                 //
150                 // Factory method: if there are pending implementation methods, we return a PendingImplementation
151                 // object, otherwise we return null.
152                 //
153                 // Register method implementations are either abstract methods
154                 // flagged as such on the base class or interface methods
155                 //
156                 static public PendingImplementation GetPendingImplementations (TypeContainer container)
157                 {
158                         TypeSpec b = container.BaseType;
159
160                         var missing_interfaces = GetMissingInterfaces (container);
161
162                         //
163                         // If we are implementing an abstract class, and we are not
164                         // ourselves abstract, and there are abstract methods (C# allows
165                         // abstract classes that have no abstract methods), then allocate
166                         // one slot.
167                         //
168                         // We also pre-compute the methods.
169                         //
170                         bool implementing_abstract = ((b != null) && b.IsAbstract && (container.ModFlags & Modifiers.ABSTRACT) == 0);
171                         MethodSpec[] abstract_methods = null;
172
173                         if (implementing_abstract){
174                                 var am = MemberCache.GetNotImplementedAbstractMethods (b);
175
176                                 if (am == null) {
177                                         implementing_abstract = false;
178                                 } else {
179                                         abstract_methods = new MethodSpec[am.Count];
180                                         am.CopyTo (abstract_methods, 0);
181                                 }
182                         }
183                         
184                         int total = missing_interfaces.Length +  (implementing_abstract ? 1 : 0);
185                         if (total == 0)
186                                 return null;
187
188                         return new PendingImplementation (container, missing_interfaces, abstract_methods, total);
189                 }
190
191                 public enum Operation {
192                         //
193                         // If you change this, review the whole InterfaceMethod routine as there
194                         // are a couple of assumptions on these three states
195                         //
196                         Lookup, ClearOne, ClearAll
197                 }
198
199                 /// <summary>
200                 ///   Whether the specified method is an interface method implementation
201                 /// </summary>
202                 public MethodSpec IsInterfaceMethod (MemberName name, TypeSpec ifaceType, MethodData method)
203                 {
204                         return InterfaceMethod (name, ifaceType, method, Operation.Lookup);
205                 }
206
207                 public void ImplementMethod (MemberName name, TypeSpec ifaceType, MethodData method, bool clear_one) 
208                 {
209                         InterfaceMethod (name, ifaceType, method, clear_one ? Operation.ClearOne : Operation.ClearAll);
210                 }
211
212                 /// <remarks>
213                 ///   If a method in Type `t' (or null to look in all interfaces
214                 ///   and the base abstract class) with name `Name', return type `ret_type' and
215                 ///   arguments `args' implements an interface, this method will
216                 ///   return the MethodInfo that this method implements.
217                 ///
218                 ///   If `name' is null, we operate solely on the method's signature.  This is for
219                 ///   instance used when implementing indexers.
220                 ///
221                 ///   The `Operation op' controls whether to lookup, clear the pending bit, or clear
222                 ///   all the methods with the given signature.
223                 ///
224                 ///   The `MethodInfo need_proxy' is used when we're implementing an interface's
225                 ///   indexer in a class.  If the new indexer's IndexerName does not match the one
226                 ///   that was used in the interface, then we always need to create a proxy for it.
227                 ///
228                 /// </remarks>
229                 public MethodSpec InterfaceMethod (MemberName name, TypeSpec iType, MethodData method, Operation op)
230                 {
231                         if (pending_implementations == null)
232                                 return null;
233
234                         TypeSpec ret_type = method.method.ReturnType;
235                         ParametersCompiled args = method.method.ParameterInfo;
236                         bool is_indexer = method.method is Indexer.SetIndexerMethod || method.method is Indexer.GetIndexerMethod;
237
238                         foreach (TypeAndMethods tm in pending_implementations){
239                                 if (!(iType == null || tm.type == iType))
240                                         continue;
241
242                                 int method_count = tm.methods.Count;
243                                 MethodSpec m;
244                                 for (int i = 0; i < method_count; i++){
245                                         m = tm.methods [i];
246
247                                         if (m == null)
248                                                 continue;
249
250                                         if (is_indexer) {
251                                                 if (!m.IsAccessor || m.Parameters.IsEmpty)
252                                                         continue;
253                                         } else {
254                                                 if (name.Name != m.Name)
255                                                         continue;
256
257                                                 if (m.Arity != name.Arity)
258                                                         continue;
259                                         }
260
261                                         if (!TypeSpecComparer.Override.IsEqual (m.Parameters, args))
262                                                 continue;
263
264                                         if (!TypeSpecComparer.Override.IsEqual (m.ReturnType, ret_type)) {
265                                                 tm.found[i] = method;
266                                                 continue;
267                                         }
268
269                                         //
270                                         // `need_proxy' is not null when we're implementing an
271                                         // interface indexer and this is Clear(One/All) operation.
272                                         //
273                                         // If `name' is null, then we do a match solely based on the
274                                         // signature and not on the name (this is done in the Lookup
275                                         // for an interface indexer).
276                                         //
277                                         if (op != Operation.Lookup) {
278                                                 // If `t != null', then this is an explicitly interface
279                                                 // implementation and we can always clear the method.
280                                                 // `need_proxy' is not null if we're implementing an
281                                                 // interface indexer.  In this case, we need to create
282                                                 // a proxy if the implementation's IndexerName doesn't
283                                                 // match the IndexerName in the interface.
284                                                 if (m.DeclaringType.IsInterface && iType == null && name.Name != m.Name) {      // TODO: This is very expensive comparison
285                                                         tm.need_proxy[i] = method.method.Spec;
286                                                 } else {
287                                                         tm.methods[i] = null;
288                                                 }
289                                         } else {
290                                                 tm.found [i] = method;
291                                         }
292
293                                         //
294                                         // Lookups and ClearOne return
295                                         //
296                                         if (op != Operation.ClearAll)
297                                                 return m;
298                                 }
299
300                                 // If a specific type was requested, we can stop now.
301                                 if (tm.type == iType)
302                                         return null;
303                         }
304                         return null;
305                 }
306
307                 /// <summary>
308                 ///   C# allows this kind of scenarios:
309                 ///   interface I { void M (); }
310                 ///   class X { public void M (); }
311                 ///   class Y : X, I { }
312                 ///
313                 ///   For that case, we create an explicit implementation function
314                 ///   I.M in Y.
315                 /// </summary>
316                 void DefineProxy (TypeSpec iface, MethodSpec base_method, MethodSpec iface_method)
317                 {
318                         // TODO: Handle nested iface names
319                         string proxy_name;
320                         var ns = iface.MemberDefinition.Namespace;
321                         if (string.IsNullOrEmpty (ns))
322                                 proxy_name = iface.MemberDefinition.Name + "." + iface_method.Name;
323                         else
324                                 proxy_name = ns + "." + iface.MemberDefinition.Name + "." + iface_method.Name;
325
326                         var param = iface_method.Parameters;
327
328                         MethodBuilder proxy = container.TypeBuilder.DefineMethod (
329                                 proxy_name,
330                                 MethodAttributes.HideBySig |
331                                 MethodAttributes.NewSlot |
332                                 MethodAttributes.CheckAccessOnOverride |
333                                 MethodAttributes.Virtual,
334                                 CallingConventions.Standard | CallingConventions.HasThis,
335                                 base_method.ReturnType.GetMetaInfo (), param.GetMetaInfo ());
336
337                         if (iface_method.IsGeneric) {
338                                 var gnames = iface_method.GenericDefinition.TypeParameters.Select (l => l.Name).ToArray ();
339                                 proxy.DefineGenericParameters (gnames);
340                         }
341
342                         for (int i = 0; i < param.Count; i++) {
343                                 string name = param.FixedParameters [i].Name;
344                                 ParameterAttributes attr = ParametersCompiled.GetParameterAttribute (param.FixedParameters [i].ModFlags);
345                                 proxy.DefineParameter (i + 1, attr, name);
346                         }
347
348                         int top = param.Count;
349                         var ec = new EmitContext (null, proxy.GetILGenerator (), null);
350                         // TODO: GetAllParametersArguments
351                         for (int i = 0; i <= top; i++)
352                                 ParameterReference.EmitLdArg (ec, i);
353
354                         ec.Emit (OpCodes.Call, base_method);
355                         ec.Emit (OpCodes.Ret);
356
357                         container.TypeBuilder.DefineMethodOverride (proxy, (MethodInfo) iface_method.GetMetaInfo ());
358                 }
359                 
360                 /// <summary>
361                 ///   This function tells whether one of our base classes implements
362                 ///   the given method (which turns out, it is valid to have an interface
363                 ///   implementation in a base
364                 /// </summary>
365                 bool BaseImplements (TypeSpec iface_type, MethodSpec mi, out MethodSpec base_method)
366                 {
367                         var base_type = container.BaseType;
368
369                         //
370                         // Setup filter with no return type to give better error message
371                         // about mismatch at return type when the check bellow rejects them
372                         //
373                         var filter = new MemberFilter (mi.Name, mi.Arity, MemberKind.Method, mi.Parameters, null);
374
375                         base_method = (MethodSpec) MemberCache.FindMember (base_type, filter, BindingRestriction.None);
376
377                         if (base_method == null || (base_method.Modifiers & Modifiers.PUBLIC) == 0)
378                                 return false;
379
380                         if (base_method.DeclaringType.IsInterface)
381                                 return false;
382
383                         if (!TypeSpecComparer.Override.IsEqual (mi.ReturnType, base_method.ReturnType))
384                                 return false;
385
386                         if (!base_method.IsAbstract && !base_method.IsVirtual)
387                                 // FIXME: We can avoid creating a proxy if base_method can be marked 'final virtual' instead.
388                                 //        However, it's too late now, the MethodBuilder has already been created (see bug 377519)
389                                 DefineProxy (iface_type, base_method, mi);
390
391                         return true;
392                 }
393
394                 /// <summary>
395                 ///   Verifies that any pending abstract methods or interface methods
396                 ///   were implemented.
397                 /// </summary>
398                 public bool VerifyPendingMethods (Report Report)
399                 {
400                         int top = pending_implementations.Length;
401                         bool errors = false;
402                         int i;
403                         
404                         for (i = 0; i < top; i++){
405                                 TypeSpec type = pending_implementations [i].type;
406
407                                 bool base_implements_type = type.IsInterface &&
408                                         container.BaseType != null &&
409                                         container.BaseType.ImplementsInterface (type, false);
410
411                                 for (int j = 0; j < pending_implementations [i].methods.Count; ++j) {
412                                         var mi = pending_implementations[i].methods[j];
413                                         if (mi == null)
414                                                 continue;
415
416                                         if (type.IsInterface){
417                                                 var need_proxy =
418                                                         pending_implementations [i].need_proxy [j];
419
420                                                 if (need_proxy != null) {
421                                                         DefineProxy (type, need_proxy, mi);
422                                                         continue;
423                                                 }
424
425                                                 if (pending_implementations [i].optional)
426                                                         continue;
427
428                                                 MethodSpec candidate = null;
429                                                 if (base_implements_type || BaseImplements (type, mi, out candidate))
430                                                         continue;
431
432                                                 if (candidate == null) {
433                                                         MethodData md = pending_implementations [i].found [j];
434                                                         if (md != null)
435                                                                 candidate = md.method.Spec;
436                                                 }
437
438                                                 Report.SymbolRelatedToPreviousError (mi);
439                                                 if (candidate != null) {
440                                                         Report.SymbolRelatedToPreviousError (candidate);
441                                                         if (candidate.IsStatic) {
442                                                                 Report.Error (736, container.Location,
443                                                                         "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' is static",
444                                                                         container.GetSignatureForError (), mi.GetSignatureForError (), TypeManager.CSharpSignature (candidate));
445                                                         } else if ((candidate.Modifiers & Modifiers.PUBLIC) == 0) {
446                                                                 Report.Error (737, container.Location,
447                                                                         "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' in not public",
448                                                                         container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError ());
449                                                         } else {
450                                                                 Report.Error (738, container.Location,
451                                                                         "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' return type `{3}' does not match interface member return type `{4}'",
452                                                                         container.GetSignatureForError (), mi.GetSignatureForError (), TypeManager.CSharpSignature (candidate),
453                                                                         TypeManager.CSharpName (candidate.ReturnType), TypeManager.CSharpName (mi.ReturnType));
454                                                         }
455                                                 } else {
456                                                         Report.Error (535, container.Location, "`{0}' does not implement interface member `{1}'",
457                                                                 container.GetSignatureForError (), mi.GetSignatureForError ());
458                                                 }
459                                         } else {
460                                                 Report.SymbolRelatedToPreviousError (mi);
461                                                 Report.Error (534, container.Location, "`{0}' does not implement inherited abstract member `{1}'",
462                                                         container.GetSignatureForError (), mi.GetSignatureForError ());
463                                         }
464                                         errors = true;
465                                 }
466                         }
467                         return errors;
468                 }
469         }
470 }