2 // pending.cs: Pending method implementation
5 // Miguel de Icaza (miguel@gnu.org)
7 // Dual licensed under the terms of the MIT X11 or GNU GPL
9 // Copyright 2001, 2002 Ximian, Inc (http://www.ximian.com)
10 // Copyright 2003-2008 Novell, Inc.
14 using System
.Collections
.Generic
;
15 using System
.Reflection
;
16 using System
.Reflection
.Emit
;
18 namespace Mono
.CSharp
{
20 struct TypeAndMethods
{
22 public MethodInfo
[] methods
;
25 // Whether it is optional, this is used to allow the explicit/implicit
26 // implementation when a base class already implements an interface.
30 // class X : IA { } class Y : X, IA { IA.Explicit (); }
34 // Far from ideal, but we want to avoid creating a copy
36 public Type
[][] args
;
38 //This is used to store the modifiers of arguments
39 public Parameter
.Modifier
[][] mods
;
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 MethodInfo
[] need_proxy
;
53 public class PendingImplementation
{
55 /// The container for this PendingImplementation
57 TypeContainer container
;
60 /// This filter is used by FindMembers, and it is used to
61 /// extract only virtual/abstract fields
63 static MemberFilter virtual_method_filter
;
66 /// This is the array of TypeAndMethods that describes the pending implementations
67 /// (both interfaces and abstract methods in base class)
69 TypeAndMethods
[] pending_implementations
;
71 static bool IsVirtualFilter (MemberInfo m
, object filterCriteria
)
73 MethodInfo mi
= m
as MethodInfo
;
74 return (mi
== null) ? false : mi
.IsVirtual
;
78 /// Inits the virtual_method_filter
80 static PendingImplementation ()
82 virtual_method_filter
= new MemberFilter (IsVirtualFilter
);
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.
90 static IList
<MethodBase
> GetAbstractMethods (Type t
)
92 List
<MethodBase
> list
= null;
93 bool searching
= true;
94 Type current_type
= t
;
99 mi
= TypeContainer
.FindMembers (
100 current_type
, MemberTypes
.Method
,
101 BindingFlags
.Public
| BindingFlags
.NonPublic
|
102 BindingFlags
.Instance
| BindingFlags
.DeclaredOnly
,
103 virtual_method_filter
, null);
105 if (current_type
== TypeManager
.object_type
)
108 current_type
= current_type
.BaseType
;
109 if (!current_type
.IsAbstract
)
116 if (mi
.Count
== 1 && !(mi
[0] is MethodBase
))
119 list
= TypeManager
.CopyNewMethods (list
, mi
);
125 for (int i
= 0; i
< list
.Count
; i
++){
126 while (list
.Count
> i
&& !((MethodInfo
) list
[i
]).IsAbstract
)
136 PendingImplementation (TypeContainer container
, MissingInterfacesInfo
[] missing_ifaces
, IList
<MethodBase
> abstract_methods
, int total
)
138 TypeBuilder type_builder
= container
.TypeBuilder
;
140 this.container
= container
;
141 pending_implementations
= new TypeAndMethods
[total
];
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
];
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
;
156 foreach (MemberInfo m
in abstract_methods
) {
157 MethodInfo mi
= (MethodInfo
) m
;
159 AParametersCollection pd
= TypeManager
.GetParameterData (mi
);
160 Type
[] types
= pd
.Types
;
162 pending_implementations
[i
].args
[j
] = types
;
163 pending_implementations
[i
].mods
[j
] = null;
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
;
176 foreach (MissingInterfacesInfo missing
in missing_ifaces
) {
178 Type t
= missing
.Type
;
183 if (t
is TypeBuilder
){
186 iface
= TypeManager
.LookupInterface (t
);
188 mi
= iface
.GetMethods ();
190 mi
= t
.GetMethods ();
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
];
202 foreach (MethodInfo m
in mi
){
203 pending_implementations
[i
].args
[j
] = Type
.EmptyTypes
;
204 pending_implementations
[i
].mods
[j
] = null;
206 // If there is a previous error, just ignore
210 AParametersCollection pd
= TypeManager
.GetParameterData (m
);
211 pending_implementations
[i
].args
[j
] = pd
.Types
;
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
;
226 struct MissingInterfacesInfo
{
228 public bool Optional
;
230 public MissingInterfacesInfo (Type t
)
237 static MissingInterfacesInfo
[] EmptyMissingInterfacesInfo
= new MissingInterfacesInfo
[0];
239 static MissingInterfacesInfo
[] GetMissingInterfaces (TypeBuilder type_builder
)
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.
245 // Even better -- on MS it returns an empty array, no matter what.
247 // Completely broken. So we do it ourselves!
249 Type
[] impl
= TypeManager
.GetExplicitInterfaces (type_builder
);
251 if (impl
== null || impl
.Length
== 0)
252 return EmptyMissingInterfacesInfo
;
254 MissingInterfacesInfo
[] ret
= new MissingInterfacesInfo
[impl
.Length
];
256 for (int i
= 0; i
< impl
.Length
; i
++)
257 ret
[i
] = new MissingInterfacesInfo (impl
[i
]);
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)
265 Type
[] base_impls
= TypeManager
.GetInterfaces (type_builder
.BaseType
);
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;
279 // Factory method: if there are pending implementation methods, we return a PendingImplementation
280 // object, otherwise we return null.
282 // Register method implementations are either abstract methods
283 // flagged as such on the base class or interface methods
285 static public PendingImplementation
GetPendingImplementations (TypeContainer container
)
287 TypeBuilder type_builder
= container
.TypeBuilder
;
288 MissingInterfacesInfo
[] missing_interfaces
;
289 Type b
= type_builder
.BaseType
;
291 missing_interfaces
= GetMissingInterfaces (type_builder
);
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
299 // We also pre-compute the methods.
301 bool implementing_abstract
= ((b
!= null) && b
.IsAbstract
&& !type_builder
.IsAbstract
);
302 IList
<MethodBase
> abstract_methods
= null;
304 if (implementing_abstract
){
305 abstract_methods
= GetAbstractMethods (b
);
307 if (abstract_methods
== null)
308 implementing_abstract
= false;
311 int total
= missing_interfaces
.Length
+ (implementing_abstract
? 1 : 0);
315 return new PendingImplementation (container
, missing_interfaces
, abstract_methods
, total
);
318 public enum Operation
{
320 // If you change this, review the whole InterfaceMethod routine as there
321 // are a couple of assumptions on these three states
323 Lookup
, ClearOne
, ClearAll
327 /// Whether the specified method is an interface method implementation
329 public MethodInfo
IsInterfaceMethod (string name
, Type ifaceType
, MethodData method
)
331 return InterfaceMethod (name
, ifaceType
, method
, Operation
.Lookup
);
334 public void ImplementMethod (string name
, Type ifaceType
, MethodData method
, bool clear_one
)
336 InterfaceMethod (name
, ifaceType
, method
, clear_one
? Operation
.ClearOne
: Operation
.ClearAll
);
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.
345 /// If `name' is null, we operate solely on the method's signature. This is for
346 /// instance used when implementing indexers.
348 /// The `Operation op' controls whether to lookup, clear the pending bit, or clear
349 /// all the methods with the given signature.
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.
356 public MethodInfo
InterfaceMethod (string name
, Type iType
, MethodData method
, Operation op
)
358 if (pending_implementations
== null)
361 Type ret_type
= method
.method
.ReturnType
;
362 ParametersCompiled args
= method
.method
.ParameterInfo
;
363 int arg_len
= args
.Count
;
364 bool is_indexer
= method
.method
is Indexer
.SetIndexerMethod
|| method
.method
is Indexer
.GetIndexerMethod
;
366 foreach (TypeAndMethods tm
in pending_implementations
){
367 if (!(iType
== null || tm
.type
== iType
))
370 int method_count
= tm
.methods
.Length
;
372 for (int i
= 0; i
< method_count
; i
++){
379 // Check if we have the same parameters
382 if (tm
.args
[i
] == null && arg_len
!= 0)
384 if (tm
.args
[i
] != null && tm
.args
[i
].Length
!= arg_len
)
387 string mname
= TypeManager
.GetMethodName (m
);
390 // `need_proxy' is not null when we're implementing an
391 // interface indexer and this is Clear(One/All) operation.
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).
399 IMethodData md
= TypeManager
.GetMethod (m
);
401 if (!(md
is Indexer
.SetIndexerMethod
|| md
is Indexer
.GetIndexerMethod
))
404 if (TypeManager
.GetPropertyFromAccessor (m
) == null)
407 } else if (name
!= mname
) {
413 for (j
= 0; j
< arg_len
; j
++) {
414 if (!TypeManager
.IsEqual (tm
.args
[i
][j
], args
.Types
[j
]))
416 if (tm
.mods
[i
][j
] == args
.FixedParameters
[j
].ModFlags
)
418 // The modifiers are different, but if one of them
419 // is a PARAMS modifier, and the other isn't, ignore
421 if (tm
.mods
[i
][j
] != Parameter
.Modifier
.PARAMS
&&
422 args
.FixedParameters
[j
].ModFlags
!= Parameter
.Modifier
.PARAMS
)
428 Type rt
= TypeManager
.TypeToCoreType (m
.ReturnType
);
429 if (!TypeManager
.IsEqual (ret_type
, rt
) &&
430 !(ret_type
== null && rt
== TypeManager
.void_type
) &&
431 !(rt
== null && ret_type
== TypeManager
.void_type
)) {
432 tm
.found
[i
] = method
;
436 if (op
!= Operation
.Lookup
) {
437 // If `t != null', then this is an explicitly interface
438 // implementation and we can always clear the method.
439 // `need_proxy' is not null if we're implementing an
440 // interface indexer. In this case, we need to create
441 // a proxy if the implementation's IndexerName doesn't
442 // match the IndexerName in the interface.
443 if (iType
== null && name
!= mname
)
444 tm
.need_proxy
[i
] = method
.MethodBuilder
;
446 tm
.methods
[i
] = null;
448 tm
.found
[i
] = method
;
452 // Lookups and ClearOne return
454 if (op
!= Operation
.ClearAll
)
458 // If a specific type was requested, we can stop now.
459 if (tm
.type
== iType
)
466 /// C# allows this kind of scenarios:
467 /// interface I { void M (); }
468 /// class X { public void M (); }
469 /// class Y : X, I { }
471 /// For that case, we create an explicit implementation function
474 void DefineProxy (Type iface
, MethodInfo base_method
, MethodInfo iface_method
,
475 AParametersCollection param
)
477 // TODO: Handle nested iface names
478 string proxy_name
= SimpleName
.RemoveGenericArity (iface
.FullName
) + "." + iface_method
.Name
;
480 MethodBuilder proxy
= container
.TypeBuilder
.DefineMethod (
482 MethodAttributes
.HideBySig
|
483 MethodAttributes
.NewSlot
|
484 MethodAttributes
.CheckAccessOnOverride
|
485 MethodAttributes
.Virtual
,
486 CallingConventions
.Standard
| CallingConventions
.HasThis
,
487 base_method
.ReturnType
, param
.GetEmitTypes ());
489 Type
[] gargs
= TypeManager
.GetGenericArguments (iface_method
);
490 if (gargs
.Length
> 0) {
491 string[] gnames
= new string[gargs
.Length
];
492 for (int i
= 0; i
< gargs
.Length
; ++i
)
493 gnames
[i
] = gargs
[i
].Name
;
495 proxy
.DefineGenericParameters (gnames
);
498 for (int i
= 0; i
< param
.Count
; i
++) {
499 string name
= param
.FixedParameters
[i
].Name
;
500 ParameterAttributes attr
= ParametersCompiled
.GetParameterAttribute (param
.FixedParameters
[i
].ModFlags
);
501 proxy
.DefineParameter (i
+ 1, attr
, name
);
504 int top
= param
.Count
;
505 ILGenerator ig
= proxy
.GetILGenerator ();
507 for (int i
= 0; i
<= top
; i
++)
508 ParameterReference
.EmitLdArg (ig
, i
);
510 ig
.Emit (OpCodes
.Call
, base_method
);
511 ig
.Emit (OpCodes
.Ret
);
513 container
.TypeBuilder
.DefineMethodOverride (proxy
, iface_method
);
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
521 bool BaseImplements (Type iface_type
, MethodInfo mi
, out MethodInfo base_method
)
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
);
532 if (list
.Count
== 0) {
537 if (TypeManager
.ImplementsInterface (container
.TypeBuilder
.BaseType
, iface_type
)) {
542 base_method
= (MethodInfo
) list
[0];
544 if (base_method
.DeclaringType
.IsInterface
)
547 if (!base_method
.IsPublic
)
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
);
559 /// Verifies that any pending abstract methods or interface methods
560 /// were implemented.
562 public bool VerifyPendingMethods (Report Report
)
564 int top
= pending_implementations
.Length
;
568 for (i
= 0; i
< top
; i
++){
569 Type type
= pending_implementations
[i
].type
;
572 bool base_implements_type
= type
.IsInterface
&&
573 container
.TypeBuilder
.BaseType
!= null &&
574 TypeManager
.ImplementsInterface (container
.TypeBuilder
.BaseType
, type
);
576 foreach (MethodInfo mi
in pending_implementations
[i
].methods
){
580 if (type
.IsInterface
){
581 MethodInfo need_proxy
=
582 pending_implementations
[i
].need_proxy
[j
];
584 if (need_proxy
!= null) {
585 DefineProxy (type
, need_proxy
, mi
, TypeManager
.GetParameterData (mi
));
589 if (pending_implementations
[i
].optional
)
592 MethodInfo candidate
= null;
593 if (base_implements_type
|| BaseImplements (type
, mi
, out candidate
))
596 if (candidate
== null) {
597 MethodData md
= pending_implementations
[i
].found
[j
];
599 candidate
= md
.MethodBuilder
;
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));
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
));
620 Report
.Error (535, container
.Location
, "`{0}' does not implement interface member `{1}'",
621 container
.GetSignatureForError (), TypeManager
.CSharpSignature (mi
, true));
624 Report
.Error (534, container
.Location
, "`{0}' does not implement inherited abstract member `{1}'",
625 container
.GetSignatureForError (), TypeManager
.CSharpSignature (mi
, true));