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 foreach (MissingInterfacesInfo missing
in missing_ifaces
){
153 Type t
= missing
.Type
;
158 if (t
is TypeBuilder
){
161 iface
= TypeManager
.LookupInterface (t
);
163 mi
= iface
.GetMethods ();
165 mi
= t
.GetMethods ();
167 int count
= mi
.Length
;
168 pending_implementations
[i
].type
= t
;
169 pending_implementations
[i
].optional
= missing
.Optional
;
170 pending_implementations
[i
].methods
= mi
;
171 pending_implementations
[i
].args
= new Type
[count
][];
172 pending_implementations
[i
].mods
= new Parameter
.Modifier
[count
][];
173 pending_implementations
[i
].found
= new bool [count
];
174 pending_implementations
[i
].need_proxy
= new MethodInfo
[count
];
175 string indexer_name
= TypeManager
.IndexerPropertyName (t
);
177 pending_implementations
[i
].set_indexer_name
= "set_" + indexer_name
;
178 pending_implementations
[i
].get_indexer_name
= "get_" + indexer_name
;
181 foreach (MethodInfo m
in mi
){
182 pending_implementations
[i
].args
[j
] = Type
.EmptyTypes
;
183 pending_implementations
[i
].mods
[j
] = null;
185 // If there is a previous error, just ignore
189 ParameterData pd
= TypeManager
.GetParameterData (m
);
190 pending_implementations
[i
].args
[j
] = pd
.Types
;
193 Parameter
.Modifier
[] pm
= new Parameter
.Modifier
[pd
.Count
];
194 for (int k
= 0; k
< pd
.Count
; k
++)
195 pm
[k
] = pd
.ParameterModifier (k
);
196 pending_implementations
[i
].mods
[j
] = pm
;
204 if (abstract_methods
!= null){
205 int count
= abstract_methods
.Count
;
206 pending_implementations
[i
].methods
= new MethodInfo
[count
];
207 pending_implementations
[i
].need_proxy
= new MethodInfo
[count
];
209 abstract_methods
.CopyTo (pending_implementations
[i
].methods
, 0);
210 pending_implementations
[i
].found
= new bool [count
];
211 pending_implementations
[i
].args
= new Type
[count
][];
212 pending_implementations
[i
].mods
= new Parameter
.Modifier
[count
][];
213 pending_implementations
[i
].type
= type_builder
;
215 string indexer_name
= TypeManager
.IndexerPropertyName (type_builder
);
216 pending_implementations
[i
].set_indexer_name
= "set_" + indexer_name
;
217 pending_implementations
[i
].get_indexer_name
= "get_" + indexer_name
;
220 foreach (MemberInfo m
in abstract_methods
){
221 MethodInfo mi
= (MethodInfo
) m
;
223 ParameterData pd
= TypeManager
.GetParameterData (mi
);
224 Type
[] types
= pd
.Types
;
226 pending_implementations
[i
].args
[j
] = types
;
227 pending_implementations
[i
].mods
[j
] = null;
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
;
240 struct MissingInterfacesInfo
{
242 public bool Optional
;
244 public MissingInterfacesInfo (Type t
)
251 static MissingInterfacesInfo
[] EmptyMissingInterfacesInfo
= new MissingInterfacesInfo
[0];
253 static MissingInterfacesInfo
[] GetMissingInterfaces (TypeBuilder type_builder
)
256 // Notice that TypeBuilders will only return the interfaces that the Type
257 // is supposed to implement, not all the interfaces that the type implements.
259 // Even better -- on MS it returns an empty array, no matter what.
261 // Completely broken. So we do it ourselves!
263 Type
[] impl
= TypeManager
.GetExplicitInterfaces (type_builder
);
265 if (impl
== null || impl
.Length
== 0)
266 return EmptyMissingInterfacesInfo
;
268 MissingInterfacesInfo
[] ret
= new MissingInterfacesInfo
[impl
.Length
];
270 for (int i
= 0; i
< impl
.Length
; i
++)
271 ret
[i
] = new MissingInterfacesInfo (impl
[i
]);
273 // we really should not get here because Object doesnt implement any
274 // interfaces. But it could implement something internal, so we have
275 // to handle that case.
276 if (type_builder
.BaseType
== null)
279 Type
[] base_impls
= TypeManager
.GetInterfaces (type_builder
.BaseType
);
281 foreach (Type t
in base_impls
) {
282 for (int i
= 0; i
< ret
.Length
; i
++) {
283 if (t
== ret
[i
].Type
) {
284 ret
[i
].Optional
= true;
293 // Factory method: if there are pending implementation methods, we return a PendingImplementation
294 // object, otherwise we return null.
296 // Register method implementations are either abstract methods
297 // flagged as such on the base class or interface methods
299 static public PendingImplementation
GetPendingImplementations (TypeContainer container
)
301 TypeBuilder type_builder
= container
.TypeBuilder
;
302 MissingInterfacesInfo
[] missing_interfaces
;
303 Type b
= type_builder
.BaseType
;
305 missing_interfaces
= GetMissingInterfaces (type_builder
);
308 // If we are implementing an abstract class, and we are not
309 // ourselves abstract, and there are abstract methods (C# allows
310 // abstract classes that have no abstract methods), then allocate
313 // We also pre-compute the methods.
315 bool implementing_abstract
= ((b
!= null) && b
.IsAbstract
&& !type_builder
.IsAbstract
);
316 ArrayList abstract_methods
= null;
318 if (implementing_abstract
){
319 abstract_methods
= GetAbstractMethods (b
);
321 if (abstract_methods
== null)
322 implementing_abstract
= false;
325 int total
= missing_interfaces
.Length
+ (implementing_abstract
? 1 : 0);
329 return new PendingImplementation (container
, missing_interfaces
, abstract_methods
, total
);
332 public enum Operation
{
334 // If you change this, review the whole InterfaceMethod routine as there
335 // are a couple of assumptions on these three states
337 Lookup
, ClearOne
, ClearAll
341 /// Whether the specified method is an interface method implementation
343 public MethodInfo
IsInterfaceMethod (Type t
, string name
, Type ret_type
, ParameterData args
)
345 return InterfaceMethod (t
, name
, ret_type
, args
, Operation
.Lookup
, null);
348 public MethodInfo
IsInterfaceIndexer (Type t
, Type ret_type
, ParameterData args
)
350 return InterfaceMethod (t
, null, ret_type
, args
, Operation
.Lookup
, null);
353 public void ImplementMethod (Type t
, string name
, Type ret_type
, ParameterData args
, bool clear_one
)
355 InterfaceMethod (t
, name
, ret_type
, args
,
356 clear_one
? Operation
.ClearOne
: Operation
.ClearAll
, null);
359 public void ImplementIndexer (Type t
, MethodInfo mi
, Type ret_type
, ParameterData args
, bool clear_one
)
361 InterfaceMethod (t
, null, ret_type
, args
,
362 clear_one
? Operation
.ClearOne
: Operation
.ClearAll
, mi
);
366 /// If a method in Type `t' (or null to look in all interfaces
367 /// and the base abstract class) with name `Name', return type `ret_type' and
368 /// arguments `args' implements an interface, this method will
369 /// return the MethodInfo that this method implements.
371 /// If `name' is null, we operate solely on the method's signature. This is for
372 /// instance used when implementing indexers.
374 /// The `Operation op' controls whether to lookup, clear the pending bit, or clear
375 /// all the methods with the given signature.
377 /// The `MethodInfo need_proxy' is used when we're implementing an interface's
378 /// indexer in a class. If the new indexer's IndexerName does not match the one
379 /// that was used in the interface, then we always need to create a proxy for it.
382 public MethodInfo
InterfaceMethod (Type t
, string name
, Type ret_type
, ParameterData args
,
383 Operation op
, MethodInfo need_proxy
)
385 int arg_len
= args
.Count
;
387 if (pending_implementations
== null)
390 foreach (TypeAndMethods tm
in pending_implementations
){
391 if (!(t
== null || tm
.type
== t
))
394 int method_count
= tm
.methods
.Length
;
396 for (int i
= 0; i
< method_count
; i
++){
402 string mname
= TypeManager
.GetMethodName (m
);
405 // `need_proxy' is not null when we're implementing an
406 // interface indexer and this is Clear(One/All) operation.
408 // If `name' is null, then we do a match solely based on the
409 // signature and not on the name (this is done in the Lookup
410 // for an interface indexer).
413 if (mname
!= tm
.get_indexer_name
&& mname
!= tm
.set_indexer_name
)
415 } else if ((need_proxy
== null) && (name
!= mname
))
418 if (!TypeManager
.IsEqual (ret_type
, m
.ReturnType
) &&
419 !(ret_type
== null && m
.ReturnType
== TypeManager
.void_type
) &&
420 !(m
.ReturnType
== null && ret_type
== TypeManager
.void_type
))
424 // Check if we have the same parameters
427 if (tm
.args
[i
] == null && arg_len
!= 0)
429 if (tm
.args
[i
] != null && tm
.args
[i
].Length
!= arg_len
)
434 for (j
= 0; j
< arg_len
; j
++) {
435 if (!TypeManager
.IsEqual (tm
.args
[i
][j
], args
.ParameterType (j
)))
437 if (tm
.mods
[i
][j
] == args
.ParameterModifier (j
))
439 // The modifiers are different, but if one of them
440 // is a PARAMS modifier, and the other isn't, ignore
442 if (tm
.mods
[i
][j
] != Parameter
.Modifier
.PARAMS
&&
443 args
.ParameterModifier (j
) != Parameter
.Modifier
.PARAMS
)
449 if (op
!= Operation
.Lookup
){
450 // If `t != null', then this is an explicitly interface
451 // implementation and we can always clear the method.
452 // `need_proxy' is not null if we're implementing an
453 // interface indexer. In this case, we need to create
454 // a proxy if the implementation's IndexerName doesn't
455 // match the IndexerName in the interface.
456 bool name_matches
= false;
457 if (name
== mname
|| mname
== tm
.get_indexer_name
|| mname
== tm
.set_indexer_name
)
460 if ((t
== null) && (need_proxy
!= null) && !name_matches
)
461 tm
.need_proxy
[i
] = need_proxy
;
463 tm
.methods
[i
] = null;
468 // Lookups and ClearOne return
470 if (op
!= Operation
.ClearAll
)
474 // If a specific type was requested, we can stop now.
482 /// C# allows this kind of scenarios:
483 /// interface I { void M (); }
484 /// class X { public void M (); }
485 /// class Y : X, I { }
487 /// For that case, we create an explicit implementation function
490 void DefineProxy (Type iface
, MethodInfo base_method
, MethodInfo iface_method
,
495 string proxy_name
= SimpleName
.RemoveGenericArity (iface
.Name
) + '.' + iface_method
.Name
;
497 proxy
= container
.TypeBuilder
.DefineMethod (
499 MethodAttributes
.HideBySig
|
500 MethodAttributes
.NewSlot
|
501 MethodAttributes
.Virtual
,
502 CallingConventions
.Standard
| CallingConventions
.HasThis
,
503 base_method
.ReturnType
, args
);
505 ParameterData pd
= TypeManager
.GetParameterData (iface_method
);
506 proxy
.DefineParameter (0, ParameterAttributes
.None
, "");
507 for (int i
= 0; i
< pd
.Count
; i
++) {
508 string name
= pd
.ParameterName (i
);
509 ParameterAttributes attr
= Parameter
.GetParameterAttributes (pd
.ParameterModifier (i
));
510 proxy
.DefineParameter (i
+ 1, attr
, name
);
513 int top
= args
.Length
;
514 ILGenerator ig
= proxy
.GetILGenerator ();
516 for (int i
= 0; i
<= top
; i
++)
517 ParameterReference
.EmitLdArg (ig
, i
);
519 ig
.Emit (OpCodes
.Call
, base_method
);
520 ig
.Emit (OpCodes
.Ret
);
522 container
.TypeBuilder
.DefineMethodOverride (proxy
, iface_method
);
526 /// This function tells whether one of our base classes implements
527 /// the given method (which turns out, it is valid to have an interface
528 /// implementation in a base
530 bool BaseImplements (Type iface_type
, MethodInfo mi
)
534 Type
[] args
= TypeManager
.GetParameterData (mi
).Types
;
535 ms
= new MethodSignature (mi
.Name
, mi
.ReturnType
, args
);
536 MemberList list
= TypeContainer
.FindMembers (
537 container
.TypeBuilder
.BaseType
, MemberTypes
.Method
| MemberTypes
.Property
,
538 BindingFlags
.Public
| BindingFlags
.Instance
,
539 MethodSignature
.method_signature_filter
, ms
);
544 if (TypeManager
.ImplementsInterface (container
.TypeBuilder
.BaseType
, iface_type
))
548 // FIXME: We should be creating fewer proxies. The runtime can handle most cases.
549 // At worst, if we can't avoid creating the proxy, we may need to make the
550 // proxy use Callvirt.
552 MethodInfo base_method
= (MethodInfo
) list
[0];
554 if (base_method
.DeclaringType
.IsInterface
)
557 if (!base_method
.IsAbstract
&& !base_method
.IsVirtual
)
558 DefineProxy (iface_type
, base_method
, mi
, args
);
564 /// Verifies that any pending abstract methods or interface methods
565 /// were implemented.
567 public bool VerifyPendingMethods ()
569 int top
= pending_implementations
.Length
;
573 for (i
= 0; i
< top
; i
++){
574 Type type
= pending_implementations
[i
].type
;
577 bool base_implements_type
= type
.IsInterface
&&
578 container
.TypeBuilder
.BaseType
!= null &&
579 TypeManager
.ImplementsInterface (container
.TypeBuilder
.BaseType
, type
);
581 foreach (MethodInfo mi
in pending_implementations
[i
].methods
){
585 if (type
.IsInterface
){
586 MethodInfo need_proxy
=
587 pending_implementations
[i
].need_proxy
[j
];
589 if (need_proxy
!= null) {
590 Type
[] args
= TypeManager
.GetParameterData (mi
).Types
;
591 DefineProxy (type
, need_proxy
, mi
, args
);
595 if (base_implements_type
|| BaseImplements (type
, mi
))
598 if (pending_implementations
[i
].optional
)
601 Report
.SymbolRelatedToPreviousError (mi
);
602 if (pending_implementations
[i
].found
[j
]) {
603 if (mi
.IsSpecialName
) {
604 string name
= TypeManager
.CSharpName (mi
.DeclaringType
) + '.' + mi
.Name
.Substring (4);
605 Report
.Error (551, container
.Location
, "Explicit interface implementation `{0}.{1}' is missing accessor `{2}'",
606 container
.GetSignatureForError (), name
, TypeManager
.CSharpSignature (mi
, true));
608 string[] methodLabel
= TypeManager
.CSharpSignature (mi
).Split ('.');
609 Report
.Error (536, container
.Location
,
610 "`{0}' does not implement interface member `{1}'. `{2}.{3}' " +
611 "is either static, not public, or has the wrong return type",
612 container
.Name
, TypeManager
.CSharpSignature (mi
),
613 container
.Name
, methodLabel
[methodLabel
.Length
- 1]);
617 Report
.Error (535, container
.Location
, "`{0}' does not implement interface member `{1}'",
618 container
.GetSignatureForError (), TypeManager
.CSharpSignature (mi
));
621 Report
.Error (534, container
.Location
, "`{0}' does not implement inherited abstract member `{1}'",
622 container
.GetSignatureForError (), TypeManager
.CSharpSignature (mi
, true));