2 // pending.cs: Pending method implementation
5 // Miguel de Icaza (miguel@gnu.org)
6 // Anirban Bhattacharjee (banirban@novell.com)
8 // Licensed under the terms of the GNU GPL
10 // (C) 2001, 2002 Ximian, Inc (http://www.ximian.com)
15 using System
.Collections
;
16 using System
.Reflection
;
17 using System
.Reflection
.Emit
;
19 namespace Mono
.MonoBASIC
{
21 struct TypeAndMethods
{
23 public MethodInfo
[] methods
;
25 // Far from ideal, but we want to avoid creating a copy
27 public Type
[][] args
;
30 // This flag on the method says `We found a match, but
31 // because it was private, we could not use the match
35 // If a method is defined here, then we always need to
36 // create a proxy for it. This is used when implementing
37 // an interface's indexer with a different IndexerName.
38 public MethodInfo
[] need_proxy
;
41 public class PendingImplementation
{
43 /// The container for this PendingImplementation
45 TypeContainer container
;
48 /// This filter is used by FindMembers, and it is used to
49 /// extract only virtual/abstract fields
51 static MemberFilter virtual_method_filter
;
54 /// This is the array of TypeAndMethods that describes the pending implementations
55 /// (both interfaces and abstract methods in parent class)
57 TypeAndMethods
[] pending_implementations
;
59 static bool IsVirtualFilter (MemberInfo m
, object filterCriteria
)
61 if (!(m
is MethodInfo
))
64 return ((MethodInfo
) m
).IsVirtual
;
68 /// Inits the virtual_method_filter
70 static PendingImplementation ()
72 virtual_method_filter
= new MemberFilter (IsVirtualFilter
);
76 // Returns a list of the abstract methods that are exposed by all of our
77 // parents that we must implement. Notice that this `flattens' the
78 // method search space, and takes into account overrides.
80 static ArrayList
GetAbstractMethods (Type t
)
82 ArrayList list
= null;
83 bool searching
= true;
84 Type current_type
= t
;
89 mi
= TypeContainer
.FindMembers (
90 current_type
, MemberTypes
.Method
,
91 BindingFlags
.Public
| BindingFlags
.Instance
|
92 BindingFlags
.DeclaredOnly
,
93 virtual_method_filter
, null);
95 if (current_type
== TypeManager
.object_type
)
98 current_type
= current_type
.BaseType
;
99 if (!current_type
.IsAbstract
)
106 if (mi
.Count
== 1 && !(mi
[0] is MethodBase
))
109 list
= TypeManager
.CopyNewMethods (list
, mi
);
115 for (int i
= 0; i
< list
.Count
; i
++){
116 while (list
.Count
> i
&& !((MethodInfo
) list
[i
]).IsAbstract
)
126 PendingImplementation (TypeContainer container
, Type
[] ifaces
, ArrayList abstract_methods
, int total
)
128 TypeBuilder type_builder
= container
.TypeBuilder
;
130 this.container
= container
;
131 pending_implementations
= new TypeAndMethods
[total
];
135 foreach (Type t
in ifaces
){
137 /*if (t is TypeBuilder){
140 iface = TypeManager.LookupInterface (t);
142 mi = iface.GetMethods (container);
144 mi
= t
.GetMethods ();
146 int count
= mi
.Length
;
147 pending_implementations
[i
].type
= t
;
148 pending_implementations
[i
].methods
= mi
;
149 pending_implementations
[i
].args
= new Type
[count
][];
150 pending_implementations
[i
].found
= new bool [count
];
151 pending_implementations
[i
].need_proxy
= new MethodInfo
[count
];
154 foreach (MethodInfo m
in mi
){
155 Type
[] types
= TypeManager
.GetArgumentTypes (m
);
157 pending_implementations
[i
].args
[j
] = types
;
164 if (abstract_methods
!= null){
165 int count
= abstract_methods
.Count
;
166 pending_implementations
[i
].methods
= new MethodInfo
[count
];
167 pending_implementations
[i
].need_proxy
= new MethodInfo
[count
];
169 abstract_methods
.CopyTo (pending_implementations
[i
].methods
, 0);
170 pending_implementations
[i
].found
= new bool [count
];
171 pending_implementations
[i
].args
= new Type
[count
][];
172 pending_implementations
[i
].type
= type_builder
.BaseType
;
175 foreach (MemberInfo m
in abstract_methods
){
176 MethodInfo mi
= (MethodInfo
) m
;
178 Type
[] types
= TypeManager
.GetArgumentTypes (mi
);
180 pending_implementations
[i
].args
[j
] = types
;
187 // Factory method: if there are pending implementation methods, we return a PendingImplementation
188 // object, otherwise we return null.
190 // Register method implementations are either abstract methods
191 // flagged as such on the base class or interface methods
193 static public PendingImplementation
GetPendingImplementations (TypeContainer container
)
195 TypeBuilder type_builder
= container
.TypeBuilder
;
197 Type b
= type_builder
.BaseType
;
201 // Notice that TypeBuilders will only return the interfaces that the Type
202 // is supposed to implement, not all the interfaces that the type implements.
204 // Completely broken. Anyways, we take advantage of this, so we only register
205 // the implementations that we need, as they are those that are listed by the
208 ifaces
= type_builder
.GetInterfaces ();
212 Type x
= type_builder
;
215 Type
[] iff
= x
.GetInterfaces ();
216 Console
.WriteLine ("Type: " + x
.Name
);
218 foreach (Type tt
in iff
){
219 Console
.WriteLine (" Iface: " + tt
.Name
);
227 ifaces
= TypeManager
.ExpandInterfaces (ifaces
);
228 icount
= ifaces
.Length
;
230 // If we are implementing an abstract class, and we are not
231 // ourselves abstract, and there are abstract methods (C# allows
232 // abstract classes that have no abstract methods), then allocate
235 // We also pre-compute the methods.
237 bool implementing_abstract
= ((b
!= null) && b
.IsAbstract
&& !type_builder
.IsAbstract
);
238 ArrayList abstract_methods
= null;
240 if (implementing_abstract
){
241 abstract_methods
= GetAbstractMethods (b
);
243 if (abstract_methods
== null)
244 implementing_abstract
= false;
247 int total
= icount
+ (implementing_abstract
? 1 : 0);
251 return new PendingImplementation (container
, ifaces
, abstract_methods
, total
);
254 public enum Operation
{
256 // If you change this, review the whole InterfaceMethod routine as there
257 // are a couple of assumptions on these three states
259 Lookup
, ClearOne
, ClearAll
263 /// Whether the specified method is an interface method implementation
265 public MethodInfo
IsAbstractMethod (Type t
, string name
, Type ret_type
, Type
[] args
)
267 return InterfaceMethod (t
, name
, ret_type
, args
, Operation
.Lookup
, null);
270 public MethodInfo
IsAbstractIndexer (Type t
, Type ret_type
, Type
[] args
)
272 return InterfaceMethod (t
, null, ret_type
, args
, Operation
.Lookup
, null);
275 public void ImplementMethod (Type t
, string name
, Type ret_type
, Type
[] args
, bool clear_one
)
277 InterfaceMethod (t
, name
, ret_type
, args
,
278 clear_one
? Operation
.ClearOne
: Operation
.ClearAll
, null);
281 public void ImplementIndexer (Type t
, MethodInfo mi
, Type ret_type
, Type
[] args
, bool clear_one
)
283 InterfaceMethod (t
, mi
.Name
, ret_type
, args
,
284 clear_one
? Operation
.ClearOne
: Operation
.ClearAll
, mi
);
288 /// If a method in Type `t' (or null to look in all interfaces
289 /// and the base abstract class) with name `Name', return type `ret_type' and
290 /// arguments `args' implements an interface, this method will
291 /// return the MethodInfo that this method implements.
293 /// If `name' is null, we operate solely on the method's signature. This is for
294 /// instance used when implementing indexers.
296 /// The `Operation op' controls whether to lookup, clear the pending bit, or clear
297 /// all the methods with the given signature.
299 /// The `MethodInfo need_proxy' is used when we're implementing an interface's
300 /// indexer in a class. If the new indexer's IndexerName does not match the one
301 /// that was used in the interface, then we always need to create a proxy for it.
304 public MethodInfo
InterfaceMethod (Type t
, string name
, Type ret_type
, Type
[] args
,
305 Operation op
, MethodInfo need_proxy
)
307 int arg_len
= args
.Length
;
309 if (pending_implementations
== null)
312 foreach (TypeAndMethods tm
in pending_implementations
){
313 if (!(t
== null || tm
.type
== t
))
317 foreach (MethodInfo m
in tm
.methods
){
323 // `need_proxy' is not null when we're implementing an
324 // interface indexer and this is Clear(One/All) operation.
325 // If `name' is null, then we do a match solely based on the
326 // signature and not on the name (this is done in the Lookup
327 // for an interface indexer).
328 if ((name
!= null) && (need_proxy
== null) && (name
!= m
.Name
)){
333 if (ret_type
!= m
.ReturnType
){
334 if (!((ret_type
== null && m
.ReturnType
== TypeManager
.void_type
) ||
335 (m
.ReturnType
== null && ret_type
== TypeManager
.void_type
)))
343 // Check if we have the same parameters
345 if (tm
.args
[i
].Length
!= arg_len
){
350 int j
, top
= args
.Length
;
353 for (j
= 0; j
< top
; j
++){
354 if (tm
.args
[i
][j
] != args
[j
]){
364 if (op
!= Operation
.Lookup
){
365 // If `t != null', then this is an explicitly interface
366 // implementation and we can always clear the method.
367 // `need_proxy' is not null if we're implementing an
368 // interface indexer. In this case, we need to create
369 // a proxy if the implementation's IndexerName doesn't
370 // match the IndexerName in the interface.
371 if ((t
== null) && (need_proxy
!= null) && (name
!= m
.Name
))
372 tm
.need_proxy
[i
] = need_proxy
;
374 tm
.methods
[i
] = null;
379 // Lookups and ClearOne return
381 if (op
!= Operation
.ClearAll
)
385 // If a specific type was requested, we can stop now.
393 /// C# allows this kind of scenarios:
394 /// interface I { void M (); }
395 /// class X { public void M (); }
396 /// class Y : X, I { }
398 /// For that case, we create an explicit implementation function
401 void DefineProxy (Type iface
, MethodInfo parent_method
, MethodInfo iface_method
,
406 string proxy_name
= iface
.Name
+ "." + iface_method
.Name
;
408 proxy
= container
.TypeBuilder
.DefineMethod (
410 MethodAttributes
.HideBySig
|
411 MethodAttributes
.NewSlot
|
412 MethodAttributes
.Virtual
,
413 CallingConventions
.Standard
| CallingConventions
.HasThis
,
414 parent_method
.ReturnType
, args
);
416 int top
= args
.Length
;
417 ILGenerator ig
= proxy
.GetILGenerator ();
419 ig
.Emit (OpCodes
.Ldarg_0
);
420 for (int i
= 0; i
< top
; i
++){
423 ig
.Emit (OpCodes
.Ldarg_1
); break;
425 ig
.Emit (OpCodes
.Ldarg_2
); break;
427 ig
.Emit (OpCodes
.Ldarg_3
); break;
429 ig
.Emit (OpCodes
.Ldarg
, i
- 1); break;
432 ig
.Emit (OpCodes
.Call
, parent_method
);
433 ig
.Emit (OpCodes
.Ret
);
435 container
.TypeBuilder
.DefineMethodOverride (proxy
, iface_method
);
438 static bool IsPropertyGetMethod (string m
)
440 return (m
.Substring (0, 4) == "get_");
443 static bool IsPropertySetMethod (string m
)
445 return (m
.Substring (0, 4) == "set_");
448 MethodInfo
FindExplicitImplementation (string iface_name
, string method_name
)
450 if (container
.Properties
!= null) {
451 foreach (Property p
in container
.Properties
)
453 if (p
.Implements
!= null) {
454 if (IsPropertyGetMethod (method_name
) && (container
.Namespace
.Name
+ "." + p
.Implements
.ToString() == iface_name
+ "." + method_name
.Substring(4)))
455 return p
.PropertyBuilder
.GetGetMethod(true);
457 if (IsPropertySetMethod (method_name
) && (container
.Namespace
.Name
+ "." + p
.Implements
.ToString() == iface_name
+ "." + method_name
.Substring(4)))
458 return p
.PropertyBuilder
.GetSetMethod(true);
463 if (container
.Methods
!= null)
465 foreach (Method m
in container
.Methods
)
467 if (m
.Implements
!= null)
469 if (container
.Namespace
.Name
+ "." + m
.Implements
.ToString() == iface_name
+ "." + method_name
)
470 return (MethodInfo
) m
.MethodBuilder
;
478 /// This function tells whether one of our parent classes implements
479 /// the given method (which turns out, it is valid to have an interface
480 /// implementation in a parent
482 bool ParentImplements (Type iface_type
, MethodInfo mi
)
487 Type
[] args
= TypeManager
.GetArgumentTypes (mi
);
489 ms
= new MethodSignature (mi
.Name
, mi
.ReturnType
, args
);
490 MemberList list
= TypeContainer
.FindMembers (
491 container
.TypeBuilder
.BaseType
, MemberTypes
.Method
| MemberTypes
.Property
,
492 BindingFlags
.Public
| BindingFlags
.Instance
,
493 MethodSignature
.method_signature_filter
, ms
);
497 mr
= FindExplicitImplementation (iface_type
.ToString(), mi
.Name
);
502 mr
= (MethodInfo
) list
[0];
504 DefineProxy (iface_type
, mr
, mi
, args
);
509 /// Verifies that any pending abstract methods or interface methods
510 /// were implemented.
512 public bool VerifyPendingMethods ()
514 int top
= pending_implementations
.Length
;
518 for (i
= 0; i
< top
; i
++){
519 Type type
= pending_implementations
[i
].type
;
522 foreach (MethodInfo mi
in pending_implementations
[i
].methods
){
526 if (type
.IsInterface
){
527 MethodInfo need_proxy
=
528 pending_implementations
[i
].need_proxy
[j
];
530 if (need_proxy
!= null) {
531 Type
[] args
= TypeManager
.GetArgumentTypes (mi
);
532 DefineProxy (type
, need_proxy
, mi
, args
);
536 if (ParentImplements (type
, mi
))
541 if (pending_implementations
[i
].found
[j
])
542 extra
= ". (method might be private or static)";
544 536, container
.Location
,
545 "`" + container
.Name
+ "' does not implement " +
546 "interface member `" +
547 type
.FullName
+ "." + mi
.Name
+ "'" + extra
);
550 30610, container
.Location
,
551 "`" + container
.Name
+ "' does not implement " +
552 "inherited 'MustOverride' member `" +
553 type
.FullName
+ "." + mi
.Name
+ "'");