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
;
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
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 // The name of the indexer (if it exists), precompute set/get, because
54 // they would be recomputed many times inside a loop later on.
56 public string set_indexer_name
;
57 public string get_indexer_name
;
60 public class PendingImplementation
{
62 /// The container for this PendingImplementation
64 TypeContainer container
;
67 /// This filter is used by FindMembers, and it is used to
68 /// extract only virtual/abstract fields
70 static MemberFilter virtual_method_filter
;
73 /// This is the array of TypeAndMethods that describes the pending implementations
74 /// (both interfaces and abstract methods in base class)
76 TypeAndMethods
[] pending_implementations
;
78 static bool IsVirtualFilter (MemberInfo m
, object filterCriteria
)
80 MethodInfo mi
= m
as MethodInfo
;
81 return (mi
== null) ? false : mi
.IsVirtual
;
85 /// Inits the virtual_method_filter
87 static PendingImplementation ()
89 virtual_method_filter
= new MemberFilter (IsVirtualFilter
);
93 // Returns a list of the abstract methods that are exposed by all of our
94 // bases that we must implement. Notice that this `flattens' the
95 // method search space, and takes into account overrides.
97 static ArrayList
GetAbstractMethods (Type t
)
99 ArrayList list
= null;
100 bool searching
= true;
101 Type current_type
= t
;
106 mi
= TypeContainer
.FindMembers (
107 current_type
, MemberTypes
.Method
,
108 BindingFlags
.Public
| BindingFlags
.NonPublic
|
109 BindingFlags
.Instance
| BindingFlags
.DeclaredOnly
,
110 virtual_method_filter
, null);
112 if (current_type
== TypeManager
.object_type
)
115 current_type
= current_type
.BaseType
;
116 if (!current_type
.IsAbstract
)
123 if (mi
.Count
== 1 && !(mi
[0] is MethodBase
))
126 list
= TypeManager
.CopyNewMethods (list
, mi
);
132 for (int i
= 0; i
< list
.Count
; i
++){
133 while (list
.Count
> i
&& !((MethodInfo
) list
[i
]).IsAbstract
)
143 PendingImplementation (TypeContainer container
, MissingInterfacesInfo
[] missing_ifaces
, ArrayList abstract_methods
, int total
)
145 TypeBuilder type_builder
= container
.TypeBuilder
;
147 this.container
= container
;
148 pending_implementations
= new TypeAndMethods
[total
];
151 if (abstract_methods
!= null) {
152 int count
= abstract_methods
.Count
;
153 pending_implementations
[i
].methods
= new MethodInfo
[count
];
154 pending_implementations
[i
].need_proxy
= new MethodInfo
[count
];
156 abstract_methods
.CopyTo (pending_implementations
[i
].methods
, 0);
157 pending_implementations
[i
].found
= new bool [count
];
158 pending_implementations
[i
].args
= new Type
[count
][];
159 pending_implementations
[i
].mods
= new Parameter
.Modifier
[count
][];
160 pending_implementations
[i
].type
= type_builder
;
162 string indexer_name
= TypeManager
.IndexerPropertyName (type_builder
);
163 pending_implementations
[i
].set_indexer_name
= "set_" + indexer_name
;
164 pending_implementations
[i
].get_indexer_name
= "get_" + indexer_name
;
167 foreach (MemberInfo m
in abstract_methods
) {
168 MethodInfo mi
= (MethodInfo
) m
;
170 ParameterData pd
= TypeManager
.GetParameterData (mi
);
171 Type
[] types
= pd
.Types
;
173 pending_implementations
[i
].args
[j
] = types
;
174 pending_implementations
[i
].mods
[j
] = null;
176 Parameter
.Modifier
[] pm
= new Parameter
.Modifier
[pd
.Count
];
177 for (int k
= 0; k
< pd
.Count
; k
++)
178 pm
[k
] = pd
.ParameterModifier (k
);
179 pending_implementations
[i
].mods
[j
] = pm
;
187 foreach (MissingInterfacesInfo missing
in missing_ifaces
) {
189 Type t
= missing
.Type
;
194 if (t
is TypeBuilder
){
197 iface
= TypeManager
.LookupInterface (t
);
199 mi
= iface
.GetMethods ();
201 mi
= t
.GetMethods ();
203 int count
= mi
.Length
;
204 pending_implementations
[i
].type
= t
;
205 pending_implementations
[i
].optional
= missing
.Optional
;
206 pending_implementations
[i
].methods
= mi
;
207 pending_implementations
[i
].args
= new Type
[count
][];
208 pending_implementations
[i
].mods
= new Parameter
.Modifier
[count
][];
209 pending_implementations
[i
].found
= new bool [count
];
210 pending_implementations
[i
].need_proxy
= new MethodInfo
[count
];
211 string indexer_name
= TypeManager
.IndexerPropertyName (t
);
213 pending_implementations
[i
].set_indexer_name
= "set_" + indexer_name
;
214 pending_implementations
[i
].get_indexer_name
= "get_" + indexer_name
;
217 foreach (MethodInfo m
in mi
){
218 pending_implementations
[i
].args
[j
] = Type
.EmptyTypes
;
219 pending_implementations
[i
].mods
[j
] = null;
221 // If there is a previous error, just ignore
225 ParameterData pd
= TypeManager
.GetParameterData (m
);
226 pending_implementations
[i
].args
[j
] = pd
.Types
;
229 Parameter
.Modifier
[] pm
= new Parameter
.Modifier
[pd
.Count
];
230 for (int k
= 0; k
< pd
.Count
; k
++)
231 pm
[k
] = pd
.ParameterModifier (k
);
232 pending_implementations
[i
].mods
[j
] = pm
;
241 struct MissingInterfacesInfo
{
243 public bool Optional
;
245 public MissingInterfacesInfo (Type t
)
252 static MissingInterfacesInfo
[] EmptyMissingInterfacesInfo
= new MissingInterfacesInfo
[0];
254 static MissingInterfacesInfo
[] GetMissingInterfaces (TypeBuilder type_builder
)
257 // Notice that TypeBuilders will only return the interfaces that the Type
258 // is supposed to implement, not all the interfaces that the type implements.
260 // Even better -- on MS it returns an empty array, no matter what.
262 // Completely broken. So we do it ourselves!
264 Type
[] impl
= TypeManager
.GetExplicitInterfaces (type_builder
);
266 if (impl
== null || impl
.Length
== 0)
267 return EmptyMissingInterfacesInfo
;
269 MissingInterfacesInfo
[] ret
= new MissingInterfacesInfo
[impl
.Length
];
271 for (int i
= 0; i
< impl
.Length
; i
++)
272 ret
[i
] = new MissingInterfacesInfo (impl
[i
]);
274 // we really should not get here because Object doesnt implement any
275 // interfaces. But it could implement something internal, so we have
276 // to handle that case.
277 if (type_builder
.BaseType
== null)
280 Type
[] base_impls
= TypeManager
.GetInterfaces (type_builder
.BaseType
);
282 foreach (Type t
in base_impls
) {
283 for (int i
= 0; i
< ret
.Length
; i
++) {
284 if (t
== ret
[i
].Type
) {
285 ret
[i
].Optional
= true;
294 // Factory method: if there are pending implementation methods, we return a PendingImplementation
295 // object, otherwise we return null.
297 // Register method implementations are either abstract methods
298 // flagged as such on the base class or interface methods
300 static public PendingImplementation
GetPendingImplementations (TypeContainer container
)
302 TypeBuilder type_builder
= container
.TypeBuilder
;
303 MissingInterfacesInfo
[] missing_interfaces
;
304 Type b
= type_builder
.BaseType
;
306 missing_interfaces
= GetMissingInterfaces (type_builder
);
309 // If we are implementing an abstract class, and we are not
310 // ourselves abstract, and there are abstract methods (C# allows
311 // abstract classes that have no abstract methods), then allocate
314 // We also pre-compute the methods.
316 bool implementing_abstract
= ((b
!= null) && b
.IsAbstract
&& !type_builder
.IsAbstract
);
317 ArrayList abstract_methods
= null;
319 if (implementing_abstract
){
320 abstract_methods
= GetAbstractMethods (b
);
322 if (abstract_methods
== null)
323 implementing_abstract
= false;
326 int total
= missing_interfaces
.Length
+ (implementing_abstract
? 1 : 0);
330 return new PendingImplementation (container
, missing_interfaces
, abstract_methods
, total
);
333 public enum Operation
{
335 // If you change this, review the whole InterfaceMethod routine as there
336 // are a couple of assumptions on these three states
338 Lookup
, ClearOne
, ClearAll
342 /// Whether the specified method is an interface method implementation
344 public MethodInfo
IsInterfaceMethod (Type t
, string name
, Type ret_type
, ParameterData args
)
346 return InterfaceMethod (t
, name
, ret_type
, args
, Operation
.Lookup
, null);
349 public MethodInfo
IsInterfaceIndexer (Type t
, Type ret_type
, ParameterData args
)
351 return InterfaceMethod (t
, null, ret_type
, args
, Operation
.Lookup
, null);
354 public void ImplementMethod (Type t
, string name
, Type ret_type
, ParameterData args
, bool clear_one
)
356 InterfaceMethod (t
, name
, ret_type
, args
,
357 clear_one
? Operation
.ClearOne
: Operation
.ClearAll
, null);
360 public void ImplementIndexer (Type t
, MethodInfo mi
, Type ret_type
, ParameterData args
, bool clear_one
)
362 InterfaceMethod (t
, null, ret_type
, args
,
363 clear_one
? Operation
.ClearOne
: Operation
.ClearAll
, mi
);
367 /// If a method in Type `t' (or null to look in all interfaces
368 /// and the base abstract class) with name `Name', return type `ret_type' and
369 /// arguments `args' implements an interface, this method will
370 /// return the MethodInfo that this method implements.
372 /// If `name' is null, we operate solely on the method's signature. This is for
373 /// instance used when implementing indexers.
375 /// The `Operation op' controls whether to lookup, clear the pending bit, or clear
376 /// all the methods with the given signature.
378 /// The `MethodInfo need_proxy' is used when we're implementing an interface's
379 /// indexer in a class. If the new indexer's IndexerName does not match the one
380 /// that was used in the interface, then we always need to create a proxy for it.
383 public MethodInfo
InterfaceMethod (Type t
, string name
, Type ret_type
, ParameterData args
,
384 Operation op
, MethodInfo need_proxy
)
386 int arg_len
= args
.Count
;
388 if (pending_implementations
== null)
391 foreach (TypeAndMethods tm
in pending_implementations
){
392 if (!(t
== null || tm
.type
== t
))
395 int method_count
= tm
.methods
.Length
;
397 for (int i
= 0; i
< method_count
; i
++){
403 string mname
= TypeManager
.GetMethodName (m
);
406 // `need_proxy' is not null when we're implementing an
407 // interface indexer and this is Clear(One/All) operation.
409 // If `name' is null, then we do a match solely based on the
410 // signature and not on the name (this is done in the Lookup
411 // for an interface indexer).
414 if (mname
!= tm
.get_indexer_name
&& mname
!= tm
.set_indexer_name
)
416 } else if ((need_proxy
== null) && (name
!= mname
))
419 if (!TypeManager
.IsEqual (ret_type
, m
.ReturnType
) &&
420 !(ret_type
== null && m
.ReturnType
== TypeManager
.void_type
) &&
421 !(m
.ReturnType
== null && ret_type
== TypeManager
.void_type
))
425 // Check if we have the same parameters
428 if (tm
.args
[i
] == null && arg_len
!= 0)
430 if (tm
.args
[i
] != null && tm
.args
[i
].Length
!= arg_len
)
435 for (j
= 0; j
< arg_len
; j
++) {
436 if (!TypeManager
.IsEqual (tm
.args
[i
][j
], args
.ParameterType (j
)))
438 if (tm
.mods
[i
][j
] == args
.ParameterModifier (j
))
440 // The modifiers are different, but if one of them
441 // is a PARAMS modifier, and the other isn't, ignore
443 if (tm
.mods
[i
][j
] != Parameter
.Modifier
.PARAMS
&&
444 args
.ParameterModifier (j
) != Parameter
.Modifier
.PARAMS
)
450 if (op
!= Operation
.Lookup
){
451 // If `t != null', then this is an explicitly interface
452 // implementation and we can always clear the method.
453 // `need_proxy' is not null if we're implementing an
454 // interface indexer. In this case, we need to create
455 // a proxy if the implementation's IndexerName doesn't
456 // match the IndexerName in the interface.
457 bool name_matches
= false;
458 if (name
== mname
|| mname
== tm
.get_indexer_name
|| mname
== tm
.set_indexer_name
)
461 if ((t
== null) && (need_proxy
!= null) && !name_matches
)
462 tm
.need_proxy
[i
] = need_proxy
;
464 tm
.methods
[i
] = null;
469 // Lookups and ClearOne return
471 if (op
!= Operation
.ClearAll
)
475 // If a specific type was requested, we can stop now.
483 /// C# allows this kind of scenarios:
484 /// interface I { void M (); }
485 /// class X { public void M (); }
486 /// class Y : X, I { }
488 /// For that case, we create an explicit implementation function
491 void DefineProxy (Type iface
, MethodInfo base_method
, MethodInfo iface_method
,
496 string proxy_name
= SimpleName
.RemoveGenericArity (iface
.Name
) + '.' + iface_method
.Name
;
498 proxy
= container
.TypeBuilder
.DefineMethod (
500 MethodAttributes
.HideBySig
|
501 MethodAttributes
.NewSlot
|
502 MethodAttributes
.Virtual
,
503 CallingConventions
.Standard
| CallingConventions
.HasThis
,
504 base_method
.ReturnType
, args
);
506 ParameterData pd
= TypeManager
.GetParameterData (iface_method
);
507 proxy
.DefineParameter (0, ParameterAttributes
.None
, "");
508 for (int i
= 0; i
< pd
.Count
; i
++) {
509 string name
= pd
.ParameterName (i
);
510 ParameterAttributes attr
= Parameter
.GetParameterAttributes (pd
.ParameterModifier (i
));
511 proxy
.DefineParameter (i
+ 1, attr
, name
);
514 int top
= args
.Length
;
515 ILGenerator ig
= proxy
.GetILGenerator ();
517 for (int i
= 0; i
<= top
; i
++)
518 ParameterReference
.EmitLdArg (ig
, i
);
520 ig
.Emit (OpCodes
.Call
, base_method
);
521 ig
.Emit (OpCodes
.Ret
);
523 container
.TypeBuilder
.DefineMethodOverride (proxy
, iface_method
);
527 /// This function tells whether one of our base classes implements
528 /// the given method (which turns out, it is valid to have an interface
529 /// implementation in a base
531 bool BaseImplements (Type iface_type
, MethodInfo mi
)
535 Type
[] args
= TypeManager
.GetParameterData (mi
).Types
;
536 ms
= new MethodSignature (mi
.Name
, mi
.ReturnType
, args
);
537 MemberList list
= TypeContainer
.FindMembers (
538 container
.TypeBuilder
.BaseType
, MemberTypes
.Method
| MemberTypes
.Property
,
539 BindingFlags
.Public
| BindingFlags
.Instance
,
540 MethodSignature
.method_signature_filter
, ms
);
545 if (TypeManager
.ImplementsInterface (container
.TypeBuilder
.BaseType
, iface_type
))
549 // FIXME: We should be creating fewer proxies. The runtime can handle most cases.
550 // At worst, if we can't avoid creating the proxy, we may need to make the
551 // proxy use Callvirt.
553 MethodInfo base_method
= (MethodInfo
) list
[0];
555 if (base_method
.DeclaringType
.IsInterface
)
558 if (!base_method
.IsAbstract
&& !base_method
.IsVirtual
)
559 DefineProxy (iface_type
, base_method
, mi
, args
);
565 /// Verifies that any pending abstract methods or interface methods
566 /// were implemented.
568 public bool VerifyPendingMethods ()
570 int top
= pending_implementations
.Length
;
574 for (i
= 0; i
< top
; i
++){
575 Type type
= pending_implementations
[i
].type
;
578 bool base_implements_type
= type
.IsInterface
&&
579 container
.TypeBuilder
.BaseType
!= null &&
580 TypeManager
.ImplementsInterface (container
.TypeBuilder
.BaseType
, type
);
582 foreach (MethodInfo mi
in pending_implementations
[i
].methods
){
586 if (type
.IsInterface
){
587 MethodInfo need_proxy
=
588 pending_implementations
[i
].need_proxy
[j
];
590 if (need_proxy
!= null) {
591 Type
[] args
= TypeManager
.GetParameterData (mi
).Types
;
592 DefineProxy (type
, need_proxy
, mi
, args
);
596 if (base_implements_type
|| BaseImplements (type
, mi
))
599 if (pending_implementations
[i
].optional
)
602 Report
.SymbolRelatedToPreviousError (mi
);
603 if (pending_implementations
[i
].found
[j
]) {
604 if (mi
.IsSpecialName
) {
605 string name
= TypeManager
.CSharpName (mi
.DeclaringType
) + '.' + mi
.Name
.Substring (4);
606 Report
.Error (551, container
.Location
, "Explicit interface implementation `{0}.{1}' is missing accessor `{2}'",
607 container
.GetSignatureForError (), name
, TypeManager
.CSharpSignature (mi
, true));
609 string[] methodLabel
= TypeManager
.CSharpSignature (mi
).Split ('.');
610 Report
.Error (536, container
.Location
,
611 "`{0}' does not implement interface member `{1}'. `{2}.{3}' " +
612 "is either static, not public, or has the wrong return type",
613 container
.Name
, TypeManager
.CSharpSignature (mi
),
614 container
.Name
, methodLabel
[methodLabel
.Length
- 1]);
618 Report
.Error (535, container
.Location
, "`{0}' does not implement interface member `{1}'",
619 container
.GetSignatureForError (), TypeManager
.CSharpSignature (mi
));
622 Report
.Error (534, container
.Location
, "`{0}' does not implement inherited abstract member `{1}'",
623 container
.GetSignatureForError (), TypeManager
.CSharpSignature (mi
, true));