2 // pending.cs: Pending method implementation
5 // Miguel de Icaza (miguel@gnu.org)
7 // Licensed under the terms of the GNU GPL
9 // (C) 2001, 2002 Ximian, Inc (http://www.ximian.com)
14 using System
.Collections
;
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
;
39 // This flag on the method says `We found a match, but
40 // because it was private, we could not use the match
44 // If a method is defined here, then we always need to
45 // create a proxy for it. This is used when implementing
46 // an interface's indexer with a different IndexerName.
47 public MethodInfo
[] need_proxy
;
50 // The name of the indexer (if it exists), precompute set/get, because
51 // they would be recomputed many times inside a loop later on.
53 public string set_indexer_name
;
54 public string get_indexer_name
;
57 public class PendingImplementation
{
59 /// The container for this PendingImplementation
61 TypeContainer container
;
64 /// This filter is used by FindMembers, and it is used to
65 /// extract only virtual/abstract fields
67 static MemberFilter virtual_method_filter
;
70 /// This is the array of TypeAndMethods that describes the pending implementations
71 /// (both interfaces and abstract methods in base class)
73 TypeAndMethods
[] pending_implementations
;
75 static bool IsVirtualFilter (MemberInfo m
, object filterCriteria
)
77 MethodInfo mi
= m
as MethodInfo
;
78 return (mi
== null) ? false : mi
.IsVirtual
;
82 /// Inits the virtual_method_filter
84 static PendingImplementation ()
86 virtual_method_filter
= new MemberFilter (IsVirtualFilter
);
90 // Returns a list of the abstract methods that are exposed by all of our
91 // bases that we must implement. Notice that this `flattens' the
92 // method search space, and takes into account overrides.
94 static ArrayList
GetAbstractMethods (Type t
)
96 ArrayList list
= null;
97 bool searching
= true;
98 Type current_type
= t
;
103 mi
= TypeContainer
.FindMembers (
104 current_type
, MemberTypes
.Method
,
105 BindingFlags
.Public
| BindingFlags
.Instance
|
106 BindingFlags
.DeclaredOnly
,
107 virtual_method_filter
, null);
109 if (current_type
== TypeManager
.object_type
)
112 current_type
= current_type
.BaseType
;
113 if (!current_type
.IsAbstract
)
120 if (mi
.Count
== 1 && !(mi
[0] is MethodBase
))
123 list
= TypeManager
.CopyNewMethods (list
, mi
);
129 for (int i
= 0; i
< list
.Count
; i
++){
130 while (list
.Count
> i
&& !((MethodInfo
) list
[i
]).IsAbstract
)
140 PendingImplementation (TypeContainer container
, MissingInterfacesInfo
[] missing_ifaces
, ArrayList abstract_methods
, int total
)
142 TypeBuilder type_builder
= container
.TypeBuilder
;
144 this.container
= container
;
145 pending_implementations
= new TypeAndMethods
[total
];
148 foreach (MissingInterfacesInfo missing
in missing_ifaces
){
150 Type t
= missing
.Type
;
155 if (t
is TypeBuilder
){
158 iface
= TypeManager
.LookupInterface (t
);
160 mi
= iface
.GetMethods ();
162 mi
= t
.GetMethods ();
164 int count
= mi
.Length
;
165 pending_implementations
[i
].type
= t
;
166 pending_implementations
[i
].optional
= missing
.Optional
;
167 pending_implementations
[i
].methods
= mi
;
168 pending_implementations
[i
].args
= new Type
[count
][];
169 pending_implementations
[i
].found
= new bool [count
];
170 pending_implementations
[i
].need_proxy
= new MethodInfo
[count
];
171 string indexer_name
= TypeManager
.IndexerPropertyName (t
);
173 pending_implementations
[i
].set_indexer_name
= "set_" + indexer_name
;
174 pending_implementations
[i
].get_indexer_name
= "get_" + indexer_name
;
177 foreach (MethodInfo m
in mi
){
178 Type
[] types
= TypeManager
.GetArgumentTypes (m
);
180 pending_implementations
[i
].args
[j
] = types
;
186 if (abstract_methods
!= null){
187 int count
= abstract_methods
.Count
;
188 pending_implementations
[i
].methods
= new MethodInfo
[count
];
189 pending_implementations
[i
].need_proxy
= new MethodInfo
[count
];
191 abstract_methods
.CopyTo (pending_implementations
[i
].methods
, 0);
192 pending_implementations
[i
].found
= new bool [count
];
193 pending_implementations
[i
].args
= new Type
[count
][];
194 pending_implementations
[i
].type
= type_builder
;
196 string indexer_name
= TypeManager
.IndexerPropertyName (type_builder
);
197 pending_implementations
[i
].set_indexer_name
= "set_" + indexer_name
;
198 pending_implementations
[i
].get_indexer_name
= "get_" + indexer_name
;
201 foreach (MemberInfo m
in abstract_methods
){
202 MethodInfo mi
= (MethodInfo
) m
;
204 Type
[] types
= TypeManager
.GetArgumentTypes (mi
);
206 pending_implementations
[i
].args
[j
] = types
;
212 struct MissingInterfacesInfo
{
214 public bool Optional
;
216 public MissingInterfacesInfo (Type t
)
223 static MissingInterfacesInfo
[] EmptyMissingInterfacesInfo
= new MissingInterfacesInfo
[0];
225 static MissingInterfacesInfo
[] GetMissingInterfaces (TypeBuilder type_builder
)
228 // Notice that TypeBuilders will only return the interfaces that the Type
229 // is supposed to implement, not all the interfaces that the type implements.
231 // Even better -- on MS it returns an empty array, no matter what.
233 // Completely broken. So we do it ourselves!
235 Type
[] impl
= TypeManager
.GetExplicitInterfaces (type_builder
);
237 if (impl
== null || impl
.Length
== 0)
238 return EmptyMissingInterfacesInfo
;
240 MissingInterfacesInfo
[] ret
= new MissingInterfacesInfo
[impl
.Length
];
242 for (int i
= 0; i
< impl
.Length
; i
++)
243 ret
[i
] = new MissingInterfacesInfo (impl
[i
]);
245 // we really should not get here because Object doesnt implement any
246 // interfaces. But it could implement something internal, so we have
247 // to handle that case.
248 if (type_builder
.BaseType
== null)
251 Type
[] base_impls
= TypeManager
.GetInterfaces (type_builder
.BaseType
);
253 foreach (Type t
in base_impls
) {
254 for (int i
= 0; i
< ret
.Length
; i
++) {
255 if (t
== ret
[i
].Type
) {
256 ret
[i
].Optional
= true;
265 // Factory method: if there are pending implementation methods, we return a PendingImplementation
266 // object, otherwise we return null.
268 // Register method implementations are either abstract methods
269 // flagged as such on the base class or interface methods
271 static public PendingImplementation
GetPendingImplementations (TypeContainer container
)
273 TypeBuilder type_builder
= container
.TypeBuilder
;
274 MissingInterfacesInfo
[] missing_interfaces
;
275 Type b
= type_builder
.BaseType
;
277 missing_interfaces
= GetMissingInterfaces (type_builder
);
280 // If we are implementing an abstract class, and we are not
281 // ourselves abstract, and there are abstract methods (C# allows
282 // abstract classes that have no abstract methods), then allocate
285 // We also pre-compute the methods.
287 bool implementing_abstract
= ((b
!= null) && b
.IsAbstract
&& !type_builder
.IsAbstract
);
288 ArrayList abstract_methods
= null;
290 if (implementing_abstract
){
291 abstract_methods
= GetAbstractMethods (b
);
293 if (abstract_methods
== null)
294 implementing_abstract
= false;
297 int total
= missing_interfaces
.Length
+ (implementing_abstract
? 1 : 0);
301 return new PendingImplementation (container
, missing_interfaces
, abstract_methods
, total
);
304 public enum Operation
{
306 // If you change this, review the whole InterfaceMethod routine as there
307 // are a couple of assumptions on these three states
309 Lookup
, ClearOne
, ClearAll
313 /// Whether the specified method is an interface method implementation
315 public MethodInfo
IsInterfaceMethod (Type t
, string name
, Type ret_type
, Type
[] args
)
317 return InterfaceMethod (t
, name
, ret_type
, args
, Operation
.Lookup
, null);
320 public MethodInfo
IsInterfaceIndexer (Type t
, Type ret_type
, Type
[] args
)
322 return InterfaceMethod (t
, null, ret_type
, args
, Operation
.Lookup
, null);
325 public void ImplementMethod (Type t
, string name
, Type ret_type
, Type
[] args
, bool clear_one
)
327 InterfaceMethod (t
, name
, ret_type
, args
,
328 clear_one
? Operation
.ClearOne
: Operation
.ClearAll
, null);
331 public void ImplementIndexer (Type t
, MethodInfo mi
, Type ret_type
, Type
[] args
, bool clear_one
)
333 InterfaceMethod (t
, null, ret_type
, args
,
334 clear_one
? Operation
.ClearOne
: Operation
.ClearAll
, mi
);
338 /// If a method in Type `t' (or null to look in all interfaces
339 /// and the base abstract class) with name `Name', return type `ret_type' and
340 /// arguments `args' implements an interface, this method will
341 /// return the MethodInfo that this method implements.
343 /// If `name' is null, we operate solely on the method's signature. This is for
344 /// instance used when implementing indexers.
346 /// The `Operation op' controls whether to lookup, clear the pending bit, or clear
347 /// all the methods with the given signature.
349 /// The `MethodInfo need_proxy' is used when we're implementing an interface's
350 /// indexer in a class. If the new indexer's IndexerName does not match the one
351 /// that was used in the interface, then we always need to create a proxy for it.
354 public MethodInfo
InterfaceMethod (Type t
, string name
, Type ret_type
, Type
[] args
,
355 Operation op
, MethodInfo need_proxy
)
357 int arg_len
= args
.Length
;
359 if (pending_implementations
== null)
362 foreach (TypeAndMethods tm
in pending_implementations
){
363 if (!(t
== null || tm
.type
== t
))
366 int method_count
= tm
.methods
.Length
;
368 for (int i
= 0; i
< method_count
; i
++){
374 string mname
= TypeManager
.GetMethodName (m
);
377 // `need_proxy' is not null when we're implementing an
378 // interface indexer and this is Clear(One/All) operation.
380 // If `name' is null, then we do a match solely based on the
381 // signature and not on the name (this is done in the Lookup
382 // for an interface indexer).
385 if (mname
!= tm
.get_indexer_name
&& mname
!= tm
.set_indexer_name
)
387 } else if ((need_proxy
== null) && (name
!= mname
))
390 if (!TypeManager
.IsEqual (ret_type
, m
.ReturnType
)){
391 if (!((ret_type
== null && m
.ReturnType
== TypeManager
.void_type
) ||
392 (m
.ReturnType
== null && ret_type
== TypeManager
.void_type
)))
397 // Check if we have the same parameters
399 if (tm
.args
[i
].Length
!= arg_len
)
402 int j
, top
= args
.Length
;
405 for (j
= 0; j
< top
; j
++){
406 if (!TypeManager
.IsEqual (tm
.args
[i
][j
], args
[j
])){
414 if (op
!= Operation
.Lookup
){
415 // If `t != null', then this is an explicitly interface
416 // implementation and we can always clear the method.
417 // `need_proxy' is not null if we're implementing an
418 // interface indexer. In this case, we need to create
419 // a proxy if the implementation's IndexerName doesn't
420 // match the IndexerName in the interface.
421 bool name_matches
= false;
422 if (name
== mname
|| mname
== tm
.get_indexer_name
|| mname
== tm
.set_indexer_name
)
425 if ((t
== null) && (need_proxy
!= null) && !name_matches
)
426 tm
.need_proxy
[i
] = need_proxy
;
428 tm
.methods
[i
] = null;
433 // Lookups and ClearOne return
435 if (op
!= Operation
.ClearAll
)
439 // If a specific type was requested, we can stop now.
447 /// C# allows this kind of scenarios:
448 /// interface I { void M (); }
449 /// class X { public void M (); }
450 /// class Y : X, I { }
452 /// For that case, we create an explicit implementation function
455 void DefineProxy (Type iface
, MethodInfo base_method
, MethodInfo iface_method
,
460 string proxy_name
= iface
.Name
+ "." + iface_method
.Name
;
462 proxy
= container
.TypeBuilder
.DefineMethod (
464 MethodAttributes
.HideBySig
|
465 MethodAttributes
.NewSlot
|
466 MethodAttributes
.Virtual
,
467 CallingConventions
.Standard
| CallingConventions
.HasThis
,
468 base_method
.ReturnType
, args
);
470 ParameterData pd
= Invocation
.GetParameterData (iface_method
);
471 proxy
.DefineParameter (0, ParameterAttributes
.None
, "");
472 for (int i
= 0; i
< pd
.Count
; i
++) {
473 string name
= pd
.ParameterName (i
);
474 ParameterAttributes attr
= Parameter
.GetParameterAttributes (pd
.ParameterModifier (i
));
475 proxy
.DefineParameter (i
+ 1, attr
, name
);
478 int top
= args
.Length
;
479 ILGenerator ig
= proxy
.GetILGenerator ();
481 for (int i
= 0; i
<= top
; i
++)
482 ParameterReference
.EmitLdArg (ig
, i
);
484 ig
.Emit (OpCodes
.Call
, base_method
);
485 ig
.Emit (OpCodes
.Ret
);
487 container
.TypeBuilder
.DefineMethodOverride (proxy
, iface_method
);
491 /// This function tells whether one of our base classes implements
492 /// the given method (which turns out, it is valid to have an interface
493 /// implementation in a base
495 bool BaseImplements (Type iface_type
, MethodInfo mi
)
499 Type
[] args
= TypeManager
.GetArgumentTypes (mi
);
500 ms
= new MethodSignature (mi
.Name
, mi
.ReturnType
, args
);
501 MemberList list
= TypeContainer
.FindMembers (
502 container
.TypeBuilder
.BaseType
, MemberTypes
.Method
| MemberTypes
.Property
,
503 BindingFlags
.Public
| BindingFlags
.Instance
,
504 MethodSignature
.method_signature_filter
, ms
);
509 MethodInfo base_method
= (MethodInfo
) list
[0];
510 if (!base_method
.IsAbstract
)
511 DefineProxy (iface_type
, base_method
, mi
, args
);
516 /// Verifies that any pending abstract methods or interface methods
517 /// were implemented.
519 public bool VerifyPendingMethods ()
521 int top
= pending_implementations
.Length
;
525 for (i
= 0; i
< top
; i
++){
526 Type type
= pending_implementations
[i
].type
;
529 foreach (MethodInfo mi
in pending_implementations
[i
].methods
){
533 if (type
.IsInterface
){
534 MethodInfo need_proxy
=
535 pending_implementations
[i
].need_proxy
[j
];
537 if (need_proxy
!= null) {
538 Type
[] args
= TypeManager
.GetArgumentTypes (mi
);
539 DefineProxy (type
, need_proxy
, mi
, args
);
543 if (BaseImplements (type
, mi
))
546 if (pending_implementations
[i
].optional
)
549 if (pending_implementations
[i
].found
[j
]) {
550 string[] methodLabel
= TypeManager
.CSharpSignature (mi
).Split ('.');
551 Report
.Error (536, container
.Location
,
552 "'{0}' does not implement interface member '{1}'. '{2}.{3}' " +
553 "is either static, not public, or has the wrong return type",
554 container
.Name
, TypeManager
.CSharpSignature (mi
),
555 container
.Name
, methodLabel
[methodLabel
.Length
- 1]);
558 Report
.Error (535, container
.Location
, "'{0}' does not implement interface member '{1}'",
559 container
.Name
, TypeManager
.CSharpSignature (mi
));
562 Report
.Error (534, container
.Location
, "'{0}' does not implement inherited abstract member '{1}'",
563 container
.Name
, TypeManager
.CSharpSignature (mi
));