2 // pending.cs: Pending method implementation
5 // Miguel de Icaza (miguel@gnu.org)
6 // Marek Safar (marek.safar@gmail.com)
8 // Dual licensed under the terms of the MIT X11 or GNU GPL
10 // Copyright 2001, 2002 Ximian, Inc (http://www.ximian.com)
11 // Copyright 2003-2008 Novell, Inc.
14 using System.Collections.Generic;
18 using IKVM.Reflection;
19 using IKVM.Reflection.Emit;
21 using System.Reflection;
22 using System.Reflection.Emit;
25 namespace Mono.CSharp {
27 struct TypeAndMethods {
29 public IList<MethodSpec> methods;
32 // Whether it is optional, this is used to allow the explicit/implicit
33 // implementation when a base class already implements an interface.
37 // class X : IA { } class Y : X, IA { IA.Explicit (); }
42 // This flag on the method says `We found a match, but
43 // because it was private, we could not use the match
45 public MethodData [] found;
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;
53 public class PendingImplementation
56 /// The container for this PendingImplementation
58 TypeContainer container;
61 /// This is the array of TypeAndMethods that describes the pending implementations
62 /// (both interfaces and abstract methods in base class)
64 TypeAndMethods [] pending_implementations;
66 PendingImplementation (TypeContainer container, MissingInterfacesInfo[] missing_ifaces, MethodSpec[] abstract_methods, int total)
68 var type_builder = container.Definition;
70 this.container = container;
71 pending_implementations = new TypeAndMethods [total];
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];
79 pending_implementations [i].methods = abstract_methods;
80 pending_implementations [i].found = new MethodData [count];
81 pending_implementations [i].type = type_builder;
85 foreach (MissingInterfacesInfo missing in missing_ifaces) {
86 var iface = missing.Type;
87 var mi = MemberCache.GetInterfaceMethods (iface);
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];
99 struct MissingInterfacesInfo {
100 public TypeSpec Type;
101 public bool Optional;
103 public MissingInterfacesInfo (TypeSpec t)
110 static MissingInterfacesInfo [] EmptyMissingInterfacesInfo = new MissingInterfacesInfo [0];
112 static MissingInterfacesInfo [] GetMissingInterfaces (TypeContainer container)
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.
118 var impl = container.Definition.Interfaces;
120 if (impl == null || impl.Count == 0)
121 return EmptyMissingInterfacesInfo;
123 MissingInterfacesInfo[] ret = new MissingInterfacesInfo[impl.Count];
125 for (int i = 0; i < impl.Count; i++)
126 ret [i] = new MissingInterfacesInfo (impl [i]);
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)
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;
150 // Factory method: if there are pending implementation methods, we return a PendingImplementation
151 // object, otherwise we return null.
153 // Register method implementations are either abstract methods
154 // flagged as such on the base class or interface methods
156 static public PendingImplementation GetPendingImplementations (TypeContainer container)
158 TypeSpec b = container.BaseType;
160 var missing_interfaces = GetMissingInterfaces (container);
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
168 // We also pre-compute the methods.
170 bool implementing_abstract = ((b != null) && b.IsAbstract && (container.ModFlags & Modifiers.ABSTRACT) == 0);
171 MethodSpec[] abstract_methods = null;
173 if (implementing_abstract){
174 var am = MemberCache.GetNotImplementedAbstractMethods (b);
177 implementing_abstract = false;
179 abstract_methods = new MethodSpec[am.Count];
180 am.CopyTo (abstract_methods, 0);
184 int total = missing_interfaces.Length + (implementing_abstract ? 1 : 0);
188 return new PendingImplementation (container, missing_interfaces, abstract_methods, total);
191 public enum Operation {
193 // If you change this, review the whole InterfaceMethod routine as there
194 // are a couple of assumptions on these three states
196 Lookup, ClearOne, ClearAll
200 /// Whether the specified method is an interface method implementation
202 public MethodSpec IsInterfaceMethod (MemberName name, TypeSpec ifaceType, MethodData method)
204 return InterfaceMethod (name, ifaceType, method, Operation.Lookup);
207 public void ImplementMethod (MemberName name, TypeSpec ifaceType, MethodData method, bool clear_one)
209 InterfaceMethod (name, ifaceType, method, clear_one ? Operation.ClearOne : Operation.ClearAll);
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.
218 /// If `name' is null, we operate solely on the method's signature. This is for
219 /// instance used when implementing indexers.
221 /// The `Operation op' controls whether to lookup, clear the pending bit, or clear
222 /// all the methods with the given signature.
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.
229 public MethodSpec InterfaceMethod (MemberName name, TypeSpec iType, MethodData method, Operation op)
231 if (pending_implementations == null)
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;
238 foreach (TypeAndMethods tm in pending_implementations){
239 if (!(iType == null || tm.type == iType))
242 int method_count = tm.methods.Count;
244 for (int i = 0; i < method_count; i++){
251 if (!m.IsAccessor || m.Parameters.IsEmpty)
254 if (name.Name != m.Name)
257 if (m.Arity != name.Arity)
261 if (!TypeSpecComparer.Override.IsEqual (m.Parameters, args))
264 if (!TypeSpecComparer.Override.IsEqual (m.ReturnType, ret_type)) {
265 tm.found[i] = method;
270 // `need_proxy' is not null when we're implementing an
271 // interface indexer and this is Clear(One/All) operation.
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).
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;
287 tm.methods[i] = null;
290 tm.found [i] = method;
294 // Lookups and ClearOne return
296 if (op != Operation.ClearAll)
300 // If a specific type was requested, we can stop now.
301 if (tm.type == iType)
308 /// C# allows this kind of scenarios:
309 /// interface I { void M (); }
310 /// class X { public void M (); }
311 /// class Y : X, I { }
313 /// For that case, we create an explicit implementation function
316 void DefineProxy (TypeSpec iface, MethodSpec base_method, MethodSpec iface_method)
318 // TODO: Handle nested iface names
320 var ns = iface.MemberDefinition.Namespace;
321 if (string.IsNullOrEmpty (ns))
322 proxy_name = iface.MemberDefinition.Name + "." + iface_method.Name;
324 proxy_name = ns + "." + iface.MemberDefinition.Name + "." + iface_method.Name;
326 var param = iface_method.Parameters;
328 MethodBuilder proxy = container.TypeBuilder.DefineMethod (
330 MethodAttributes.HideBySig |
331 MethodAttributes.NewSlot |
332 MethodAttributes.CheckAccessOnOverride |
333 MethodAttributes.Virtual,
334 CallingConventions.Standard | CallingConventions.HasThis,
335 base_method.ReturnType.GetMetaInfo (), param.GetMetaInfo ());
337 if (iface_method.IsGeneric) {
338 var gnames = iface_method.GenericDefinition.TypeParameters.Select (l => l.Name).ToArray ();
339 proxy.DefineGenericParameters (gnames);
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);
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);
354 ec.Emit (OpCodes.Call, base_method);
355 ec.Emit (OpCodes.Ret);
357 container.TypeBuilder.DefineMethodOverride (proxy, (MethodInfo) iface_method.GetMetaInfo ());
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
365 bool BaseImplements (TypeSpec iface_type, MethodSpec mi, out MethodSpec base_method)
367 var base_type = container.BaseType;
370 // Setup filter with no return type to give better error message
371 // about mismatch at return type when the check bellow rejects them
373 var filter = new MemberFilter (mi.Name, mi.Arity, MemberKind.Method, mi.Parameters, null);
375 base_method = (MethodSpec) MemberCache.FindMember (base_type, filter, BindingRestriction.None);
377 if (base_method == null || (base_method.Modifiers & Modifiers.PUBLIC) == 0)
380 if (base_method.DeclaringType.IsInterface)
383 if (!TypeSpecComparer.Override.IsEqual (mi.ReturnType, base_method.ReturnType))
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);
395 /// Verifies that any pending abstract methods or interface methods
396 /// were implemented.
398 public bool VerifyPendingMethods (Report Report)
400 int top = pending_implementations.Length;
404 for (i = 0; i < top; i++){
405 TypeSpec type = pending_implementations [i].type;
407 bool base_implements_type = type.IsInterface &&
408 container.BaseType != null &&
409 container.BaseType.ImplementsInterface (type, false);
411 for (int j = 0; j < pending_implementations [i].methods.Count; ++j) {
412 var mi = pending_implementations[i].methods[j];
416 if (type.IsInterface){
418 pending_implementations [i].need_proxy [j];
420 if (need_proxy != null) {
421 DefineProxy (type, need_proxy, mi);
425 if (pending_implementations [i].optional)
428 MethodSpec candidate = null;
429 if (base_implements_type || BaseImplements (type, mi, out candidate))
432 if (candidate == null) {
433 MethodData md = pending_implementations [i].found [j];
435 candidate = md.method.Spec;
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 ());
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));
456 Report.Error (535, container.Location, "`{0}' does not implement interface member `{1}'",
457 container.GetSignatureForError (), mi.GetSignatureForError ());
460 Report.SymbolRelatedToPreviousError (mi);
461 Report.Error (534, container.Location, "`{0}' does not implement inherited abstract member `{1}'",
462 container.GetSignatureForError (), mi.GetSignatureForError ());