2
// pending.cs: Pending method implementation
5
// Miguel de Icaza (miguel@gnu.org)
6
// Marek Safar (marek.safar@gmail.com)
8
// Dual licensed under the terms of the MIT X11 or GNU GPL
10
// Copyright 2001, 2002 Ximian, Inc (http://www.ximian.com)
11
// Copyright 2003-2008 Novell, Inc.
12
// Copyright 2011 Xamarin Inc
16
using System.Collections.Generic;
20
using IKVM.Reflection;
21
using IKVM.Reflection.Emit;
23
using System.Reflection;
24
using System.Reflection.Emit;
27
namespace Mono.CSharp {
29
struct TypeAndMethods {
31
public IList<MethodSpec> methods;
34
// Whether it is optional, this is used to allow the explicit/implicit
35
// implementation when a base class already implements an interface.
39
// class X : IA { } class Y : X, IA { IA.Explicit (); }
44
// This flag on the method says `We found a match, but
45
// because it was private, we could not use the match
47
public MethodData [] found;
49
// If a method is defined here, then we always need to
50
// create a proxy for it. This is used when implementing
51
// an interface's indexer with a different IndexerName.
52
public MethodSpec [] need_proxy;
55
struct ProxyMethodContext : IMemberContext
57
readonly TypeContainer container;
59
public ProxyMethodContext (TypeContainer container)
61
this.container = container;
64
public TypeSpec CurrentType {
66
throw new NotImplementedException ();
70
public TypeParameters CurrentTypeParameters {
72
throw new NotImplementedException ();
76
public MemberCore CurrentMemberDefinition {
78
throw new NotImplementedException ();
82
public bool IsObsolete {
88
public bool IsUnsafe {
90
throw new NotImplementedException ();
94
public bool IsStatic {
100
public ModuleContainer Module {
102
return container.Module;
106
public string GetSignatureForError ()
108
throw new NotImplementedException ();
111
public ExtensionMethodCandidates LookupExtensionMethod (TypeSpec extensionType, string name, int arity)
113
throw new NotImplementedException ();
116
public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc)
118
throw new NotImplementedException ();
121
public FullNamedExpression LookupNamespaceAlias (string name)
123
throw new NotImplementedException ();
127
public class PendingImplementation
130
/// The container for this PendingImplementation
132
readonly TypeDefinition container;
135
/// This is the array of TypeAndMethods that describes the pending implementations
136
/// (both interfaces and abstract methods in base class)
138
TypeAndMethods [] pending_implementations;
140
PendingImplementation (TypeDefinition container, MissingInterfacesInfo[] missing_ifaces, MethodSpec[] abstract_methods, int total)
142
var type_builder = container.Definition;
144
this.container = container;
145
pending_implementations = new TypeAndMethods [total];
148
if (abstract_methods != null) {
149
int count = abstract_methods.Length;
150
pending_implementations [i].methods = new MethodSpec [count];
151
pending_implementations [i].need_proxy = new MethodSpec [count];
153
pending_implementations [i].methods = abstract_methods;
154
pending_implementations [i].found = new MethodData [count];
155
pending_implementations [i].type = type_builder;
159
foreach (MissingInterfacesInfo missing in missing_ifaces) {
160
var iface = missing.Type;
161
var mi = MemberCache.GetInterfaceMethods (iface);
163
int count = mi.Count;
164
pending_implementations [i].type = iface;
165
pending_implementations [i].optional = missing.Optional;
166
pending_implementations [i].methods = mi;
167
pending_implementations [i].found = new MethodData [count];
168
pending_implementations [i].need_proxy = new MethodSpec [count];
175
return container.Module.Compiler.Report;
179
struct MissingInterfacesInfo {
180
public TypeSpec Type;
181
public bool Optional;
183
public MissingInterfacesInfo (TypeSpec t)
190
static readonly MissingInterfacesInfo [] EmptyMissingInterfacesInfo = new MissingInterfacesInfo [0];
192
static MissingInterfacesInfo [] GetMissingInterfaces (TypeDefinition container)
195
// Interfaces will return all interfaces that the container
196
// implements including any inherited interfaces
198
var impl = container.Definition.Interfaces;
200
if (impl == null || impl.Count == 0)
201
return EmptyMissingInterfacesInfo;
203
var ret = new MissingInterfacesInfo[impl.Count];
205
for (int i = 0; i < ret.Length; i++)
206
ret [i] = new MissingInterfacesInfo (impl [i]);
208
// we really should not get here because Object doesnt implement any
209
// interfaces. But it could implement something internal, so we have
210
// to handle that case.
211
if (container.BaseType == null)
214
var base_impls = container.BaseType.Interfaces;
215
if (base_impls != null) {
216
foreach (TypeSpec t in base_impls) {
217
for (int i = 0; i < ret.Length; i++) {
218
if (t == ret[i].Type) {
219
ret[i].Optional = true;
230
// Factory method: if there are pending implementation methods, we return a PendingImplementation
231
// object, otherwise we return null.
233
// Register method implementations are either abstract methods
234
// flagged as such on the base class or interface methods
236
static public PendingImplementation GetPendingImplementations (TypeDefinition container)
238
TypeSpec b = container.BaseType;
240
var missing_interfaces = GetMissingInterfaces (container);
243
// If we are implementing an abstract class, and we are not
244
// ourselves abstract, and there are abstract methods (C# allows
245
// abstract classes that have no abstract methods), then allocate
248
// We also pre-compute the methods.
250
bool implementing_abstract = ((b != null) && b.IsAbstract && (container.ModFlags & Modifiers.ABSTRACT) == 0);
251
MethodSpec[] abstract_methods = null;
253
if (implementing_abstract){
254
var am = MemberCache.GetNotImplementedAbstractMethods (b);
257
implementing_abstract = false;
259
abstract_methods = new MethodSpec[am.Count];
260
am.CopyTo (abstract_methods, 0);
264
int total = missing_interfaces.Length + (implementing_abstract ? 1 : 0);
268
var pending = new PendingImplementation (container, missing_interfaces, abstract_methods, total);
271
// check for inherited conflicting methods
273
foreach (var p in pending.pending_implementations) {
275
// It can happen for generic interfaces only
277
if (!p.type.IsGeneric)
281
// CLR does not distinguishes between ref and out
283
for (int i = 0; i < p.methods.Count; ++i) {
284
MethodSpec compared_method = p.methods[i];
285
if (compared_method.Parameters.IsEmpty)
288
for (int ii = i + 1; ii < p.methods.Count; ++ii) {
289
MethodSpec tested_method = p.methods[ii];
290
if (compared_method.Name != tested_method.Name)
293
if (p.type != tested_method.DeclaringType)
296
if (!TypeSpecComparer.Override.IsSame (compared_method.Parameters.Types, tested_method.Parameters.Types))
299
bool exact_match = true;
300
bool ref_only_difference = false;
301
var cp = compared_method.Parameters.FixedParameters;
302
var tp = tested_method.Parameters.FixedParameters;
304
for (int pi = 0; pi < cp.Length; ++pi) {
306
// First check exact modifiers match
308
if ((cp[pi].ModFlags & Parameter.Modifier.RefOutMask) == (tp[pi].ModFlags & Parameter.Modifier.RefOutMask))
311
if (((cp[pi].ModFlags | tp[pi].ModFlags) & Parameter.Modifier.RefOutMask) == Parameter.Modifier.RefOutMask) {
312
ref_only_difference = true;
320
if (!exact_match || !ref_only_difference)
323
pending.Report.SymbolRelatedToPreviousError (compared_method);
324
pending.Report.SymbolRelatedToPreviousError (tested_method);
325
pending.Report.Error (767, container.Location,
326
"Cannot implement interface `{0}' with the specified type parameters because it causes method `{1}' to differ on parameter modifiers only",
327
p.type.GetDefinition().GetSignatureForError (), compared_method.GetSignatureForError ());
337
public enum Operation {
339
// If you change this, review the whole InterfaceMethod routine as there
340
// are a couple of assumptions on these three states
342
Lookup, ClearOne, ClearAll
346
/// Whether the specified method is an interface method implementation
348
public MethodSpec IsInterfaceMethod (MemberName name, TypeSpec ifaceType, MethodData method, out MethodSpec ambiguousCandidate, ref bool optional)
350
return InterfaceMethod (name, ifaceType, method, Operation.Lookup, out ambiguousCandidate, ref optional);
353
public void ImplementMethod (MemberName name, TypeSpec ifaceType, MethodData method, bool clear_one, out MethodSpec ambiguousCandidate, ref bool optional)
355
InterfaceMethod (name, ifaceType, method, clear_one ? Operation.ClearOne : Operation.ClearAll, out ambiguousCandidate, ref optional);
359
/// If a method in Type `t' (or null to look in all interfaces
360
/// and the base abstract class) with name `Name', return type `ret_type' and
361
/// arguments `args' implements an interface, this method will
362
/// return the MethodInfo that this method implements.
364
/// If `name' is null, we operate solely on the method's signature. This is for
365
/// instance used when implementing indexers.
367
/// The `Operation op' controls whether to lookup, clear the pending bit, or clear
368
/// all the methods with the given signature.
370
/// The `MethodInfo need_proxy' is used when we're implementing an interface's
371
/// indexer in a class. If the new indexer's IndexerName does not match the one
372
/// that was used in the interface, then we always need to create a proxy for it.
375
public MethodSpec InterfaceMethod (MemberName name, TypeSpec iType, MethodData method, Operation op, out MethodSpec ambiguousCandidate, ref bool optional)
377
ambiguousCandidate = null;
379
if (pending_implementations == null)
382
TypeSpec ret_type = method.method.ReturnType;
383
ParametersCompiled args = method.method.ParameterInfo;
384
bool is_indexer = method.method is Indexer.SetIndexerMethod || method.method is Indexer.GetIndexerMethod;
387
foreach (TypeAndMethods tm in pending_implementations){
388
if (!(iType == null || tm.type == iType))
391
int method_count = tm.methods.Count;
392
for (int i = 0; i < method_count; i++){
399
if (!m.IsAccessor || m.Parameters.IsEmpty)
402
if (name.Name != m.Name)
405
if (m.Arity != name.Arity)
409
if (!TypeSpecComparer.Override.IsEqual (m.Parameters, args))
412
if (!TypeSpecComparer.Override.IsEqual (m.ReturnType, ret_type)) {
413
tm.found[i] = method;
418
// `need_proxy' is not null when we're implementing an
419
// interface indexer and this is Clear(One/All) operation.
421
// If `name' is null, then we do a match solely based on the
422
// signature and not on the name (this is done in the Lookup
423
// for an interface indexer).
425
if (op != Operation.Lookup) {
426
if (m.IsAccessor != method.method.IsAccessor)
429
// If `t != null', then this is an explicitly interface
430
// implementation and we can always clear the method.
431
// `need_proxy' is not null if we're implementing an
432
// interface indexer. In this case, we need to create
433
// a proxy if the implementation's IndexerName doesn't
434
// match the IndexerName in the interface.
435
if (m.DeclaringType.IsInterface && iType == null && name.Name != m.Name) { // TODO: This is very expensive comparison
436
tm.need_proxy[i] = method.method.Spec;
438
tm.methods[i] = null;
441
tm.found [i] = method;
442
optional = tm.optional;
445
if (op == Operation.Lookup && name.ExplicitInterface != null && ambiguousCandidate == null) {
446
ambiguousCandidate = m;
451
// Lookups and ClearOne return
453
if (op != Operation.ClearAll)
457
// If a specific type was requested, we can stop now.
458
if (tm.type == iType)
462
m = ambiguousCandidate;
463
ambiguousCandidate = null;
468
/// C# allows this kind of scenarios:
469
/// interface I { void M (); }
470
/// class X { public void M (); }
471
/// class Y : X, I { }
473
/// For that case, we create an explicit implementation function
476
void DefineProxy (TypeSpec iface, MethodSpec base_method, MethodSpec iface_method)
478
// TODO: Handle nested iface names
480
var ns = iface.MemberDefinition.Namespace;
481
if (string.IsNullOrEmpty (ns))
482
proxy_name = iface.MemberDefinition.Name + "." + iface_method.Name;
484
proxy_name = ns + "." + iface.MemberDefinition.Name + "." + iface_method.Name;
486
var param = iface_method.Parameters;
488
MethodBuilder proxy = container.TypeBuilder.DefineMethod (
490
MethodAttributes.Private |
491
MethodAttributes.HideBySig |
492
MethodAttributes.NewSlot |
493
MethodAttributes.CheckAccessOnOverride |
494
MethodAttributes.Virtual | MethodAttributes.Final,
495
CallingConventions.Standard | CallingConventions.HasThis,
496
base_method.ReturnType.GetMetaInfo (), param.GetMetaInfo ());
498
if (iface_method.IsGeneric) {
499
var gnames = iface_method.GenericDefinition.TypeParameters.Select (l => l.Name).ToArray ();
500
proxy.DefineGenericParameters (gnames);
503
for (int i = 0; i < param.Count; i++) {
504
string name = param.FixedParameters [i].Name;
505
ParameterAttributes attr = ParametersCompiled.GetParameterAttribute (param.FixedParameters [i].ModFlags);
506
proxy.DefineParameter (i + 1, attr, name);
509
int top = param.Count;
510
var ec = new EmitContext (new ProxyMethodContext (container), proxy.GetILGenerator (), null, null);
512
// TODO: GetAllParametersArguments
513
for (int i = 0; i < top; i++)
514
ec.EmitArgumentLoad (i);
516
ec.Emit (OpCodes.Call, base_method);
517
ec.Emit (OpCodes.Ret);
519
container.TypeBuilder.DefineMethodOverride (proxy, (MethodInfo) iface_method.GetMetaInfo ());
523
/// This function tells whether one of our base classes implements
524
/// the given method (which turns out, it is valid to have an interface
525
/// implementation in a base
527
bool BaseImplements (TypeSpec iface_type, MethodSpec mi, out MethodSpec base_method)
530
var base_type = container.BaseType;
533
// Setup filter with no return type to give better error message
534
// about mismatch at return type when the check bellow rejects them
536
var parameters = mi.Parameters;
538
var candidates = MemberCache.FindMembers (base_type, mi.Name, false);
539
if (candidates == null)
542
MethodSpec similar_candidate = null;
543
foreach (var candidate in candidates) {
544
if (candidate.Kind != MemberKind.Method)
547
if (candidate.Arity != mi.Arity)
550
var candidate_param = ((MethodSpec) candidate).Parameters;
551
if (!TypeSpecComparer.Override.IsEqual (parameters.Types, candidate_param.Types))
554
bool modifiers_match = true;
555
for (int i = 0; i < parameters.Count; ++i) {
557
// First check exact ref/out match
559
if ((parameters.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask) == (candidate_param.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask))
562
modifiers_match = false;
565
// Different in ref/out only
567
if ((parameters.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask) != (candidate_param.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask)) {
568
if (similar_candidate == null) {
569
if (!candidate.IsPublic)
572
if (!TypeSpecComparer.Override.IsEqual (mi.ReturnType, ((MethodSpec) candidate).ReturnType))
575
// It's used for ref/out ambiguity overload check
576
similar_candidate = (MethodSpec) candidate;
582
similar_candidate = null;
586
if (!modifiers_match)
590
// From this point the candidate is used for detailed error reporting
591
// because it's very close match to what we are looking for
593
base_method = (MethodSpec) candidate;
595
if (!candidate.IsPublic)
598
if (!TypeSpecComparer.Override.IsEqual (mi.ReturnType, base_method.ReturnType))
601
if (mi.IsGeneric && !Method.CheckImplementingMethodConstraints (container, base_method, mi)) {
606
if (base_method != null) {
607
if (similar_candidate != null) {
608
Report.SymbolRelatedToPreviousError (similar_candidate);
609
Report.SymbolRelatedToPreviousError (mi);
610
Report.SymbolRelatedToPreviousError (container);
611
Report.Warning (1956, 1, ((MemberCore) base_method.MemberDefinition).Location,
612
"The interface method `{0}' implementation is ambiguous between following methods: `{1}' and `{2}' in type `{3}'",
613
mi.GetSignatureForError (), base_method.GetSignatureForError (), similar_candidate.GetSignatureForError (), container.GetSignatureForError ());
619
base_type = candidates[0].DeclaringType.BaseType;
620
if (base_type == null)
624
if (!base_method.IsVirtual) {
626
var base_builder = base_method.GetMetaInfo () as MethodBuilder;
627
if (base_builder != null) {
629
// We can avoid creating a proxy if base_method can be marked 'final virtual'. This can
630
// be done for all methods from compiled assembly
632
base_builder.__SetAttributes (base_builder.Attributes | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.NewSlot);
636
DefineProxy (iface_type, base_method, mi);
643
/// Verifies that any pending abstract methods or interface methods
644
/// were implemented.
646
public bool VerifyPendingMethods ()
648
int top = pending_implementations.Length;
652
for (i = 0; i < top; i++){
653
TypeSpec type = pending_implementations [i].type;
655
bool base_implements_type = type.IsInterface &&
656
container.BaseType != null &&
657
container.BaseType.ImplementsInterface (type, false);
659
for (int j = 0; j < pending_implementations [i].methods.Count; ++j) {
660
var mi = pending_implementations[i].methods[j];
664
if (type.IsInterface){
666
pending_implementations [i].need_proxy [j];
668
if (need_proxy != null) {
669
DefineProxy (type, need_proxy, mi);
673
if (pending_implementations [i].optional)
676
MethodSpec candidate = null;
677
if (base_implements_type || BaseImplements (type, mi, out candidate))
680
if (candidate == null) {
681
MethodData md = pending_implementations [i].found [j];
683
candidate = md.method.Spec;
686
Report.SymbolRelatedToPreviousError (mi);
687
if (candidate != null) {
688
Report.SymbolRelatedToPreviousError (candidate);
689
if (candidate.IsStatic) {
690
Report.Error (736, container.Location,
691
"`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' is static",
692
container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError ());
693
} else if ((candidate.Modifiers & Modifiers.PUBLIC) == 0) {
694
Report.Error (737, container.Location,
695
"`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' is not public",
696
container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError ());
698
Report.Error (738, container.Location,
699
"`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' return type `{3}' does not match interface member return type `{4}'",
700
container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError (),
701
candidate.ReturnType.GetSignatureForError (), mi.ReturnType.GetSignatureForError ());
704
Report.Error (535, container.Location, "`{0}' does not implement interface member `{1}'",
705
container.GetSignatureForError (), mi.GetSignatureForError ());
708
Report.SymbolRelatedToPreviousError (mi);
709
Report.Error (534, container.Location, "`{0}' does not implement inherited abstract member `{1}'",
710
container.GetSignatureForError (), mi.GetSignatureForError ());