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
, IList
<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
.Count
;
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
.GetInterfaceMembers (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 IList
<MethodSpec
> abstract_methods
= null;
168 if (implementing_abstract
){
169 abstract_methods
= MemberCache
.GetNotImplementedAbstractMethods (b
);
171 if (abstract_methods
== null)
172 implementing_abstract
= false;
175 int total
= missing_interfaces
.Length
+ (implementing_abstract
? 1 : 0);
179 return new PendingImplementation (container
, missing_interfaces
, abstract_methods
, total
);
182 public enum Operation
{
184 // If you change this, review the whole InterfaceMethod routine as there
185 // are a couple of assumptions on these three states
187 Lookup
, ClearOne
, ClearAll
191 /// Whether the specified method is an interface method implementation
193 public MethodSpec
IsInterfaceMethod (MemberName name
, TypeSpec ifaceType
, MethodData method
)
195 return InterfaceMethod (name
, ifaceType
, method
, Operation
.Lookup
);
198 public void ImplementMethod (MemberName name
, TypeSpec ifaceType
, MethodData method
, bool clear_one
)
200 InterfaceMethod (name
, ifaceType
, method
, clear_one
? Operation
.ClearOne
: Operation
.ClearAll
);
204 /// If a method in Type `t' (or null to look in all interfaces
205 /// and the base abstract class) with name `Name', return type `ret_type' and
206 /// arguments `args' implements an interface, this method will
207 /// return the MethodInfo that this method implements.
209 /// If `name' is null, we operate solely on the method's signature. This is for
210 /// instance used when implementing indexers.
212 /// The `Operation op' controls whether to lookup, clear the pending bit, or clear
213 /// all the methods with the given signature.
215 /// The `MethodInfo need_proxy' is used when we're implementing an interface's
216 /// indexer in a class. If the new indexer's IndexerName does not match the one
217 /// that was used in the interface, then we always need to create a proxy for it.
220 public MethodSpec
InterfaceMethod (MemberName name
, TypeSpec iType
, MethodData method
, Operation op
)
222 if (pending_implementations
== null)
225 TypeSpec ret_type
= method
.method
.ReturnType
;
226 ParametersCompiled args
= method
.method
.ParameterInfo
;
227 bool is_indexer
= method
.method
is Indexer
.SetIndexerMethod
|| method
.method
is Indexer
.GetIndexerMethod
;
229 foreach (TypeAndMethods tm
in pending_implementations
){
230 if (!(iType
== null || tm
.type
== iType
))
233 int method_count
= tm
.methods
.Count
;
235 for (int i
= 0; i
< method_count
; i
++){
242 if (!m
.IsAccessor
|| m
.Parameters
.IsEmpty
)
245 if (name
.Name
!= m
.Name
)
248 if (m
.Arity
!= name
.Arity
)
252 if (!TypeSpecComparer
.Override
.IsEqual (m
.Parameters
, args
))
255 if (!TypeSpecComparer
.Override
.IsEqual (m
.ReturnType
, ret_type
)) {
256 tm
.found
[i
] = method
;
261 // `need_proxy' is not null when we're implementing an
262 // interface indexer and this is Clear(One/All) operation.
264 // If `name' is null, then we do a match solely based on the
265 // signature and not on the name (this is done in the Lookup
266 // for an interface indexer).
268 if (op
!= Operation
.Lookup
) {
269 // If `t != null', then this is an explicitly interface
270 // implementation and we can always clear the method.
271 // `need_proxy' is not null if we're implementing an
272 // interface indexer. In this case, we need to create
273 // a proxy if the implementation's IndexerName doesn't
274 // match the IndexerName in the interface.
275 if (m
.DeclaringType
.IsInterface
&& iType
== null && name
.Name
!= m
.Name
) { // TODO: This is very expensive comparison
276 tm
.need_proxy
[i
] = method
.method
.Spec
;
278 tm
.methods
[i
] = null;
281 tm
.found
[i
] = method
;
285 // Lookups and ClearOne return
287 if (op
!= Operation
.ClearAll
)
291 // If a specific type was requested, we can stop now.
292 if (tm
.type
== iType
)
299 /// C# allows this kind of scenarios:
300 /// interface I { void M (); }
301 /// class X { public void M (); }
302 /// class Y : X, I { }
304 /// For that case, we create an explicit implementation function
307 void DefineProxy (TypeSpec iface
, MethodSpec base_method
, MethodSpec iface_method
)
309 // TODO: Handle nested iface names
311 var ns
= iface
.MemberDefinition
.Namespace
;
312 if (string.IsNullOrEmpty (ns
))
313 proxy_name
= iface
.MemberDefinition
.Name
+ "." + iface_method
.Name
;
315 proxy_name
= ns
+ "." + iface
.MemberDefinition
.Name
+ "." + iface_method
.Name
;
317 var param
= iface_method
.Parameters
;
319 MethodBuilder proxy
= container
.TypeBuilder
.DefineMethod (
321 MethodAttributes
.HideBySig
|
322 MethodAttributes
.NewSlot
|
323 MethodAttributes
.CheckAccessOnOverride
|
324 MethodAttributes
.Virtual
,
325 CallingConventions
.Standard
| CallingConventions
.HasThis
,
326 base_method
.ReturnType
.GetMetaInfo (), param
.GetMetaInfo ());
328 if (iface_method
.IsGeneric
) {
329 var gnames
= iface_method
.GenericDefinition
.TypeParameters
.Select (l
=> l
.Name
).ToArray ();
330 proxy
.DefineGenericParameters (gnames
);
333 for (int i
= 0; i
< param
.Count
; i
++) {
334 string name
= param
.FixedParameters
[i
].Name
;
335 ParameterAttributes attr
= ParametersCompiled
.GetParameterAttribute (param
.FixedParameters
[i
].ModFlags
);
336 proxy
.DefineParameter (i
+ 1, attr
, name
);
339 int top
= param
.Count
;
340 var ec
= new EmitContext (null, proxy
.GetILGenerator (), null);
342 for (int i
= 0; i
<= top
; i
++)
343 ParameterReference
.EmitLdArg (ec
, i
);
345 ec
.Emit (OpCodes
.Call
, base_method
);
346 ec
.Emit (OpCodes
.Ret
);
348 container
.TypeBuilder
.DefineMethodOverride (proxy
, (MethodInfo
) iface_method
.GetMetaInfo ());
352 /// This function tells whether one of our base classes implements
353 /// the given method (which turns out, it is valid to have an interface
354 /// implementation in a base
356 bool BaseImplements (TypeSpec iface_type
, MethodSpec mi
, out MethodSpec base_method
)
358 var base_type
= container
.BaseType
;
359 base_method
= (MethodSpec
) MemberCache
.FindMember (base_type
, new MemberFilter (mi
), BindingRestriction
.None
);
361 if (base_method
== null || (base_method
.Modifiers
& Modifiers
.PUBLIC
) == 0)
364 if (base_method
.DeclaringType
.IsInterface
)
367 // Why was it here ????
368 //if (TypeManager.ImplementsInterface (base_type, iface_type)) {
372 if (!base_method
.IsAbstract
&& !base_method
.IsVirtual
)
373 // FIXME: We can avoid creating a proxy if base_method can be marked 'final virtual' instead.
374 // However, it's too late now, the MethodBuilder has already been created (see bug 377519)
375 DefineProxy (iface_type
, base_method
, mi
);
381 /// Verifies that any pending abstract methods or interface methods
382 /// were implemented.
384 public bool VerifyPendingMethods (Report Report
)
386 int top
= pending_implementations
.Length
;
390 for (i
= 0; i
< top
; i
++){
391 TypeSpec type
= pending_implementations
[i
].type
;
393 bool base_implements_type
= type
.IsInterface
&&
394 container
.BaseType
!= null &&
395 container
.BaseType
.ImplementsInterface (type
);
397 for (int j
= 0; j
< pending_implementations
[i
].methods
.Count
; ++j
) {
398 var mi
= pending_implementations
[i
].methods
[j
];
402 if (type
.IsInterface
){
404 pending_implementations
[i
].need_proxy
[j
];
406 if (need_proxy
!= null) {
407 DefineProxy (type
, need_proxy
, mi
);
411 if (pending_implementations
[i
].optional
)
414 MethodSpec candidate
= null;
415 if (base_implements_type
|| BaseImplements (type
, mi
, out candidate
))
418 if (candidate
== null) {
419 MethodData md
= pending_implementations
[i
].found
[j
];
421 candidate
= md
.method
.Spec
;
424 Report
.SymbolRelatedToPreviousError (mi
);
425 if (candidate
!= null) {
426 Report
.SymbolRelatedToPreviousError (candidate
);
427 if (candidate
.IsStatic
) {
428 Report
.Error (736, container
.Location
,
429 "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' is static",
430 container
.GetSignatureForError (), mi
.GetSignatureForError (), TypeManager
.CSharpSignature (candidate
));
431 } else if ((candidate
.Modifiers
& Modifiers
.PUBLIC
) == 0) {
432 Report
.Error (737, container
.Location
,
433 "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' in not public",
434 container
.GetSignatureForError (), mi
.GetSignatureForError (), candidate
.GetSignatureForError ());
436 Report
.Error (738, container
.Location
,
437 "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' return type `{3}' does not match interface member return type `{4}'",
438 container
.GetSignatureForError (), mi
.GetSignatureForError (), TypeManager
.CSharpSignature (candidate
),
439 TypeManager
.CSharpName (candidate
.ReturnType
), TypeManager
.CSharpName (mi
.ReturnType
));
442 Report
.Error (535, container
.Location
, "`{0}' does not implement interface member `{1}'",
443 container
.GetSignatureForError (), mi
.GetSignatureForError ());
446 Report
.SymbolRelatedToPreviousError (mi
);
447 Report
.Error (534, container
.Location
, "`{0}' does not implement inherited abstract member `{1}'",
448 container
.GetSignatureForError (), mi
.GetSignatureForError ());