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.
15 using System
.Collections
.Generic
;
16 using System
.Reflection
;
17 using System
.Reflection
.Emit
;
20 namespace Mono
.CSharp
{
22 struct TypeAndMethods
{
24 public IList
<MethodSpec
> methods
;
27 // Whether it is optional, this is used to allow the explicit/implicit
28 // implementation when a base class already implements an interface.
32 // class X : IA { } class Y : X, IA { IA.Explicit (); }
37 // This flag on the method says `We found a match, but
38 // because it was private, we could not use the match
40 public MethodData
[] found
;
42 // If a method is defined here, then we always need to
43 // create a proxy for it. This is used when implementing
44 // an interface's indexer with a different IndexerName.
45 public MethodSpec
[] need_proxy
;
48 public class PendingImplementation
51 /// The container for this PendingImplementation
53 TypeContainer container
;
56 /// This is the array of TypeAndMethods that describes the pending implementations
57 /// (both interfaces and abstract methods in base class)
59 TypeAndMethods
[] pending_implementations
;
61 PendingImplementation (TypeContainer container
, MissingInterfacesInfo
[] missing_ifaces
, MethodSpec
[] abstract_methods
, int total
)
63 var type_builder
= container
.Definition
;
65 this.container
= container
;
66 pending_implementations
= new TypeAndMethods
[total
];
69 if (abstract_methods
!= null) {
70 int count
= abstract_methods
.Length
;
71 pending_implementations
[i
].methods
= new MethodSpec
[count
];
72 pending_implementations
[i
].need_proxy
= new MethodSpec
[count
];
74 pending_implementations
[i
].methods
= abstract_methods
;
75 pending_implementations
[i
].found
= new MethodData
[count
];
76 pending_implementations
[i
].type
= type_builder
;
80 foreach (MissingInterfacesInfo missing
in missing_ifaces
) {
81 var iface
= missing
.Type
;
82 var mi
= MemberCache
.GetInterfaceMethods (iface
);
85 pending_implementations
[i
].type
= iface
;
86 pending_implementations
[i
].optional
= missing
.Optional
;
87 pending_implementations
[i
].methods
= mi
;
88 pending_implementations
[i
].found
= new MethodData
[count
];
89 pending_implementations
[i
].need_proxy
= new MethodSpec
[count
];
94 struct MissingInterfacesInfo
{
98 public MissingInterfacesInfo (TypeSpec t
)
105 static MissingInterfacesInfo
[] EmptyMissingInterfacesInfo
= new MissingInterfacesInfo
[0];
107 static MissingInterfacesInfo
[] GetMissingInterfaces (TypeContainer container
)
110 // Notice that Interfaces will only return the interfaces that the Type
111 // is supposed to implement, not all the interfaces that the type implements.
113 var impl
= container
.Definition
.Interfaces
;
115 if (impl
== null || impl
.Count
== 0)
116 return EmptyMissingInterfacesInfo
;
118 MissingInterfacesInfo
[] ret
= new MissingInterfacesInfo
[impl
.Count
];
120 for (int i
= 0; i
< impl
.Count
; i
++)
121 ret
[i
] = new MissingInterfacesInfo (impl
[i
]);
123 // we really should not get here because Object doesnt implement any
124 // interfaces. But it could implement something internal, so we have
125 // to handle that case.
126 if (container
.BaseType
== null)
129 var base_impls
= container
.BaseType
.Interfaces
;
130 if (base_impls
!= null) {
131 foreach (TypeSpec t
in base_impls
) {
132 for (int i
= 0; i
< ret
.Length
; i
++) {
133 if (t
== ret
[i
].Type
) {
134 ret
[i
].Optional
= true;
145 // Factory method: if there are pending implementation methods, we return a PendingImplementation
146 // object, otherwise we return null.
148 // Register method implementations are either abstract methods
149 // flagged as such on the base class or interface methods
151 static public PendingImplementation
GetPendingImplementations (TypeContainer container
)
153 TypeSpec b
= container
.BaseType
;
155 var missing_interfaces
= GetMissingInterfaces (container
);
158 // If we are implementing an abstract class, and we are not
159 // ourselves abstract, and there are abstract methods (C# allows
160 // abstract classes that have no abstract methods), then allocate
163 // We also pre-compute the methods.
165 bool implementing_abstract
= ((b
!= null) && b
.IsAbstract
&& (container
.ModFlags
& Modifiers
.ABSTRACT
) == 0);
166 MethodSpec
[] abstract_methods
= null;
168 if (implementing_abstract
){
169 var am
= MemberCache
.GetNotImplementedAbstractMethods (b
);
172 implementing_abstract
= false;
174 abstract_methods
= new MethodSpec
[am
.Count
];
175 am
.CopyTo (abstract_methods
, 0);
179 int total
= missing_interfaces
.Length
+ (implementing_abstract
? 1 : 0);
183 return new PendingImplementation (container
, missing_interfaces
, abstract_methods
, total
);
186 public enum Operation
{
188 // If you change this, review the whole InterfaceMethod routine as there
189 // are a couple of assumptions on these three states
191 Lookup
, ClearOne
, ClearAll
195 /// Whether the specified method is an interface method implementation
197 public MethodSpec
IsInterfaceMethod (MemberName name
, TypeSpec ifaceType
, MethodData method
)
199 return InterfaceMethod (name
, ifaceType
, method
, Operation
.Lookup
);
202 public void ImplementMethod (MemberName name
, TypeSpec ifaceType
, MethodData method
, bool clear_one
)
204 InterfaceMethod (name
, ifaceType
, method
, clear_one
? Operation
.ClearOne
: Operation
.ClearAll
);
208 /// If a method in Type `t' (or null to look in all interfaces
209 /// and the base abstract class) with name `Name', return type `ret_type' and
210 /// arguments `args' implements an interface, this method will
211 /// return the MethodInfo that this method implements.
213 /// If `name' is null, we operate solely on the method's signature. This is for
214 /// instance used when implementing indexers.
216 /// The `Operation op' controls whether to lookup, clear the pending bit, or clear
217 /// all the methods with the given signature.
219 /// The `MethodInfo need_proxy' is used when we're implementing an interface's
220 /// indexer in a class. If the new indexer's IndexerName does not match the one
221 /// that was used in the interface, then we always need to create a proxy for it.
224 public MethodSpec
InterfaceMethod (MemberName name
, TypeSpec iType
, MethodData method
, Operation op
)
226 if (pending_implementations
== null)
229 TypeSpec ret_type
= method
.method
.ReturnType
;
230 ParametersCompiled args
= method
.method
.ParameterInfo
;
231 bool is_indexer
= method
.method
is Indexer
.SetIndexerMethod
|| method
.method
is Indexer
.GetIndexerMethod
;
233 foreach (TypeAndMethods tm
in pending_implementations
){
234 if (!(iType
== null || tm
.type
== iType
))
237 int method_count
= tm
.methods
.Count
;
239 for (int i
= 0; i
< method_count
; i
++){
246 if (!m
.IsAccessor
|| m
.Parameters
.IsEmpty
)
249 if (name
.Name
!= m
.Name
)
252 if (m
.Arity
!= name
.Arity
)
256 if (!TypeSpecComparer
.Override
.IsEqual (m
.Parameters
, args
))
259 if (!TypeSpecComparer
.Override
.IsEqual (m
.ReturnType
, ret_type
)) {
260 tm
.found
[i
] = method
;
265 // `need_proxy' is not null when we're implementing an
266 // interface indexer and this is Clear(One/All) operation.
268 // If `name' is null, then we do a match solely based on the
269 // signature and not on the name (this is done in the Lookup
270 // for an interface indexer).
272 if (op
!= Operation
.Lookup
) {
273 // If `t != null', then this is an explicitly interface
274 // implementation and we can always clear the method.
275 // `need_proxy' is not null if we're implementing an
276 // interface indexer. In this case, we need to create
277 // a proxy if the implementation's IndexerName doesn't
278 // match the IndexerName in the interface.
279 if (m
.DeclaringType
.IsInterface
&& iType
== null && name
.Name
!= m
.Name
) { // TODO: This is very expensive comparison
280 tm
.need_proxy
[i
] = method
.method
.Spec
;
282 tm
.methods
[i
] = null;
285 tm
.found
[i
] = method
;
289 // Lookups and ClearOne return
291 if (op
!= Operation
.ClearAll
)
295 // If a specific type was requested, we can stop now.
296 if (tm
.type
== iType
)
303 /// C# allows this kind of scenarios:
304 /// interface I { void M (); }
305 /// class X { public void M (); }
306 /// class Y : X, I { }
308 /// For that case, we create an explicit implementation function
311 void DefineProxy (TypeSpec iface
, MethodSpec base_method
, MethodSpec iface_method
)
313 // TODO: Handle nested iface names
315 var ns
= iface
.MemberDefinition
.Namespace
;
316 if (string.IsNullOrEmpty (ns
))
317 proxy_name
= iface
.MemberDefinition
.Name
+ "." + iface_method
.Name
;
319 proxy_name
= ns
+ "." + iface
.MemberDefinition
.Name
+ "." + iface_method
.Name
;
321 var param
= iface_method
.Parameters
;
323 MethodBuilder proxy
= container
.TypeBuilder
.DefineMethod (
325 MethodAttributes
.HideBySig
|
326 MethodAttributes
.NewSlot
|
327 MethodAttributes
.CheckAccessOnOverride
|
328 MethodAttributes
.Virtual
,
329 CallingConventions
.Standard
| CallingConventions
.HasThis
,
330 base_method
.ReturnType
.GetMetaInfo (), param
.GetMetaInfo ());
332 if (iface_method
.IsGeneric
) {
333 var gnames
= iface_method
.GenericDefinition
.TypeParameters
.Select (l
=> l
.Name
).ToArray ();
334 proxy
.DefineGenericParameters (gnames
);
337 for (int i
= 0; i
< param
.Count
; i
++) {
338 string name
= param
.FixedParameters
[i
].Name
;
339 ParameterAttributes attr
= ParametersCompiled
.GetParameterAttribute (param
.FixedParameters
[i
].ModFlags
);
340 proxy
.DefineParameter (i
+ 1, attr
, name
);
343 int top
= param
.Count
;
344 var ec
= new EmitContext (null, proxy
.GetILGenerator (), null);
346 for (int i
= 0; i
<= top
; i
++)
347 ParameterReference
.EmitLdArg (ec
, i
);
349 ec
.Emit (OpCodes
.Call
, base_method
);
350 ec
.Emit (OpCodes
.Ret
);
352 container
.TypeBuilder
.DefineMethodOverride (proxy
, (MethodInfo
) iface_method
.GetMetaInfo ());
356 /// This function tells whether one of our base classes implements
357 /// the given method (which turns out, it is valid to have an interface
358 /// implementation in a base
360 bool BaseImplements (TypeSpec iface_type
, MethodSpec mi
, out MethodSpec base_method
)
362 var base_type
= container
.BaseType
;
363 base_method
= (MethodSpec
) MemberCache
.FindMember (base_type
, new MemberFilter (mi
), BindingRestriction
.None
);
365 if (base_method
== null || (base_method
.Modifiers
& Modifiers
.PUBLIC
) == 0)
368 if (base_method
.DeclaringType
.IsInterface
)
371 // Why was it here ????
372 //if (TypeManager.ImplementsInterface (base_type, iface_type)) {
376 if (!base_method
.IsAbstract
&& !base_method
.IsVirtual
)
377 // FIXME: We can avoid creating a proxy if base_method can be marked 'final virtual' instead.
378 // However, it's too late now, the MethodBuilder has already been created (see bug 377519)
379 DefineProxy (iface_type
, base_method
, mi
);
385 /// Verifies that any pending abstract methods or interface methods
386 /// were implemented.
388 public bool VerifyPendingMethods (Report Report
)
390 int top
= pending_implementations
.Length
;
394 for (i
= 0; i
< top
; i
++){
395 TypeSpec type
= pending_implementations
[i
].type
;
397 bool base_implements_type
= type
.IsInterface
&&
398 container
.BaseType
!= null &&
399 container
.BaseType
.ImplementsInterface (type
);
401 for (int j
= 0; j
< pending_implementations
[i
].methods
.Count
; ++j
) {
402 var mi
= pending_implementations
[i
].methods
[j
];
406 if (type
.IsInterface
){
408 pending_implementations
[i
].need_proxy
[j
];
410 if (need_proxy
!= null) {
411 DefineProxy (type
, need_proxy
, mi
);
415 if (pending_implementations
[i
].optional
)
418 MethodSpec candidate
= null;
419 if (base_implements_type
|| BaseImplements (type
, mi
, out candidate
))
422 if (candidate
== null) {
423 MethodData md
= pending_implementations
[i
].found
[j
];
425 candidate
= md
.method
.Spec
;
428 Report
.SymbolRelatedToPreviousError (mi
);
429 if (candidate
!= null) {
430 Report
.SymbolRelatedToPreviousError (candidate
);
431 if (candidate
.IsStatic
) {
432 Report
.Error (736, container
.Location
,
433 "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' is static",
434 container
.GetSignatureForError (), mi
.GetSignatureForError (), TypeManager
.CSharpSignature (candidate
));
435 } else if ((candidate
.Modifiers
& Modifiers
.PUBLIC
) == 0) {
436 Report
.Error (737, container
.Location
,
437 "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' in not public",
438 container
.GetSignatureForError (), mi
.GetSignatureForError (), candidate
.GetSignatureForError ());
440 Report
.Error (738, container
.Location
,
441 "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' return type `{3}' does not match interface member return type `{4}'",
442 container
.GetSignatureForError (), mi
.GetSignatureForError (), TypeManager
.CSharpSignature (candidate
),
443 TypeManager
.CSharpName (candidate
.ReturnType
), TypeManager
.CSharpName (mi
.ReturnType
));
446 Report
.Error (535, container
.Location
, "`{0}' does not implement interface member `{1}'",
447 container
.GetSignatureForError (), mi
.GetSignatureForError ());
450 Report
.SymbolRelatedToPreviousError (mi
);
451 Report
.Error (534, container
.Location
, "`{0}' does not implement inherited abstract member `{1}'",
452 container
.GetSignatureForError (), mi
.GetSignatureForError ());