//
// Copyright 2001, 2002 Ximian, Inc (http://www.ximian.com)
// Copyright 2003-2008 Novell, Inc.
+// Copyright 2011 Xamarin Inc
//
using System;
using System.Collections.Generic;
+using System.Linq;
+
+#if STATIC
+using IKVM.Reflection;
+using IKVM.Reflection.Emit;
+#else
using System.Reflection;
using System.Reflection.Emit;
-using System.Linq;
+#endif
namespace Mono.CSharp {
public MethodSpec [] need_proxy;
}
+ struct ProxyMethodContext : IMemberContext
+ {
+ readonly TypeContainer container;
+
+ public ProxyMethodContext (TypeContainer container)
+ {
+ this.container = container;
+ }
+
+ public TypeSpec CurrentType {
+ get {
+ throw new NotImplementedException ();
+ }
+ }
+
+ public TypeParameters CurrentTypeParameters {
+ get {
+ throw new NotImplementedException ();
+ }
+ }
+
+ public MemberCore CurrentMemberDefinition {
+ get {
+ throw new NotImplementedException ();
+ }
+ }
+
+ public bool IsObsolete {
+ get {
+ return false;
+ }
+ }
+
+ public bool IsUnsafe {
+ get {
+ throw new NotImplementedException ();
+ }
+ }
+
+ public bool IsStatic {
+ get {
+ return false;
+ }
+ }
+
+ public ModuleContainer Module {
+ get {
+ return container.Module;
+ }
+ }
+
+ public string GetSignatureForError ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ public ExtensionMethodCandidates LookupExtensionMethod (TypeSpec extensionType, string name, int arity)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public FullNamedExpression LookupNamespaceAlias (string name)
+ {
+ throw new NotImplementedException ();
+ }
+ }
+
public class PendingImplementation
{
/// <summary>
/// The container for this PendingImplementation
/// </summary>
- TypeContainer container;
+ readonly TypeDefinition container;
/// <summary>
/// This is the array of TypeAndMethods that describes the pending implementations
/// </summary>
TypeAndMethods [] pending_implementations;
- PendingImplementation (TypeContainer container, MissingInterfacesInfo[] missing_ifaces, IList<MethodSpec> abstract_methods, int total)
+ PendingImplementation (TypeDefinition container, MissingInterfacesInfo[] missing_ifaces, MethodSpec[] abstract_methods, int total)
{
var type_builder = container.Definition;
int i = 0;
if (abstract_methods != null) {
- int count = abstract_methods.Count;
+ int count = abstract_methods.Length;
pending_implementations [i].methods = new MethodSpec [count];
pending_implementations [i].need_proxy = new MethodSpec [count];
foreach (MissingInterfacesInfo missing in missing_ifaces) {
var iface = missing.Type;
- var mi = MemberCache.GetInterfaceMembers (iface);
+ var mi = MemberCache.GetInterfaceMethods (iface);
int count = mi.Count;
pending_implementations [i].type = iface;
}
}
+ Report Report {
+ get {
+ return container.Module.Compiler.Report;
+ }
+ }
+
struct MissingInterfacesInfo {
public TypeSpec Type;
public bool Optional;
}
}
- static MissingInterfacesInfo [] EmptyMissingInterfacesInfo = new MissingInterfacesInfo [0];
+ static readonly MissingInterfacesInfo [] EmptyMissingInterfacesInfo = new MissingInterfacesInfo [0];
- static MissingInterfacesInfo [] GetMissingInterfaces (TypeContainer container)
+ static MissingInterfacesInfo [] GetMissingInterfaces (TypeDefinition container)
{
//
// Notice that Interfaces will only return the interfaces that the Type
// Register method implementations are either abstract methods
// flagged as such on the base class or interface methods
//
- static public PendingImplementation GetPendingImplementations (TypeContainer container)
+ static public PendingImplementation GetPendingImplementations (TypeDefinition container)
{
TypeSpec b = container.BaseType;
// We also pre-compute the methods.
//
bool implementing_abstract = ((b != null) && b.IsAbstract && (container.ModFlags & Modifiers.ABSTRACT) == 0);
- IList<MethodSpec> abstract_methods = null;
+ MethodSpec[] abstract_methods = null;
if (implementing_abstract){
- abstract_methods = MemberCache.GetNotImplementedAbstractMethods (b);
-
- if (abstract_methods == null)
+ var am = MemberCache.GetNotImplementedAbstractMethods (b);
+
+ if (am == null) {
implementing_abstract = false;
+ } else {
+ abstract_methods = new MethodSpec[am.Count];
+ am.CopyTo (abstract_methods, 0);
+ }
}
int total = missing_interfaces.Length + (implementing_abstract ? 1 : 0);
if (total == 0)
return null;
- return new PendingImplementation (container, missing_interfaces, abstract_methods, total);
+ var pending = new PendingImplementation (container, missing_interfaces, abstract_methods, total);
+
+ //
+ // check for inherited conflicting methods
+ //
+ foreach (var p in pending.pending_implementations) {
+ //
+ // It can happen for generic interfaces only
+ //
+ if (!p.type.IsGeneric)
+ continue;
+
+ //
+ // CLR does not distinguishes between ref and out
+ //
+ for (int i = 0; i < p.methods.Count; ++i) {
+ MethodSpec compared_method = p.methods[i];
+ if (compared_method.Parameters.IsEmpty)
+ continue;
+
+ for (int ii = i + 1; ii < p.methods.Count; ++ii) {
+ MethodSpec tested_method = p.methods[ii];
+ if (compared_method.Name != tested_method.Name)
+ continue;
+
+ if (p.type != tested_method.DeclaringType)
+ continue;
+
+ if (!TypeSpecComparer.Override.IsSame (compared_method.Parameters.Types, tested_method.Parameters.Types))
+ continue;
+
+ bool exact_match = true;
+ bool ref_only_difference = false;
+ var cp = compared_method.Parameters.FixedParameters;
+ var tp = tested_method.Parameters.FixedParameters;
+
+ for (int pi = 0; pi < cp.Length; ++pi) {
+ //
+ // First check exact modifiers match
+ //
+ if ((cp[pi].ModFlags & Parameter.Modifier.RefOutMask) == (tp[pi].ModFlags & Parameter.Modifier.RefOutMask))
+ continue;
+
+ if (((cp[pi].ModFlags | tp[pi].ModFlags) & Parameter.Modifier.RefOutMask) == Parameter.Modifier.RefOutMask) {
+ ref_only_difference = true;
+ continue;
+ }
+
+ exact_match = false;
+ break;
+ }
+
+ if (!exact_match || !ref_only_difference)
+ continue;
+
+ pending.Report.SymbolRelatedToPreviousError (compared_method);
+ pending.Report.SymbolRelatedToPreviousError (tested_method);
+ pending.Report.Error (767, container.Location,
+ "Cannot implement interface `{0}' with the specified type parameters because it causes method `{1}' to differ on parameter modifiers only",
+ p.type.GetDefinition().GetSignatureForError (), compared_method.GetSignatureForError ());
+
+ break;
+ }
+ }
+ }
+
+ return pending;
}
public enum Operation {
/// <summary>
/// Whether the specified method is an interface method implementation
/// </summary>
- public MethodSpec IsInterfaceMethod (MemberName name, TypeSpec ifaceType, MethodData method)
+ public MethodSpec IsInterfaceMethod (MemberName name, TypeSpec ifaceType, MethodData method, out MethodSpec ambiguousCandidate, ref bool optional)
{
- return InterfaceMethod (name, ifaceType, method, Operation.Lookup);
+ return InterfaceMethod (name, ifaceType, method, Operation.Lookup, out ambiguousCandidate, ref optional);
}
- public void ImplementMethod (MemberName name, TypeSpec ifaceType, MethodData method, bool clear_one)
+ public void ImplementMethod (MemberName name, TypeSpec ifaceType, MethodData method, bool clear_one, out MethodSpec ambiguousCandidate, ref bool optional)
{
- InterfaceMethod (name, ifaceType, method, clear_one ? Operation.ClearOne : Operation.ClearAll);
+ InterfaceMethod (name, ifaceType, method, clear_one ? Operation.ClearOne : Operation.ClearAll, out ambiguousCandidate, ref optional);
}
/// <remarks>
/// that was used in the interface, then we always need to create a proxy for it.
///
/// </remarks>
- public MethodSpec InterfaceMethod (MemberName name, TypeSpec iType, MethodData method, Operation op)
+ public MethodSpec InterfaceMethod (MemberName name, TypeSpec iType, MethodData method, Operation op, out MethodSpec ambiguousCandidate, ref bool optional)
{
+ ambiguousCandidate = null;
+
if (pending_implementations == null)
return null;
TypeSpec ret_type = method.method.ReturnType;
ParametersCompiled args = method.method.ParameterInfo;
bool is_indexer = method.method is Indexer.SetIndexerMethod || method.method is Indexer.GetIndexerMethod;
+ MethodSpec m;
foreach (TypeAndMethods tm in pending_implementations){
if (!(iType == null || tm.type == iType))
continue;
int method_count = tm.methods.Count;
- MethodSpec m;
for (int i = 0; i < method_count; i++){
m = tm.methods [i];
// for an interface indexer).
//
if (op != Operation.Lookup) {
+ if (m.IsAccessor != method.method.IsAccessor)
+ continue;
+
// If `t != null', then this is an explicitly interface
// implementation and we can always clear the method.
// `need_proxy' is not null if we're implementing an
}
} else {
tm.found [i] = method;
+ optional = tm.optional;
+ }
+
+ if (op == Operation.Lookup && name.ExplicitInterface != null && ambiguousCandidate == null) {
+ ambiguousCandidate = m;
+ continue;
}
//
// If a specific type was requested, we can stop now.
if (tm.type == iType)
- return null;
+ break;
}
- return null;
+
+ m = ambiguousCandidate;
+ ambiguousCandidate = null;
+ return m;
}
/// <summary>
MethodBuilder proxy = container.TypeBuilder.DefineMethod (
proxy_name,
+ MethodAttributes.Private |
MethodAttributes.HideBySig |
MethodAttributes.NewSlot |
MethodAttributes.CheckAccessOnOverride |
- MethodAttributes.Virtual,
+ MethodAttributes.Virtual | MethodAttributes.Final,
CallingConventions.Standard | CallingConventions.HasThis,
base_method.ReturnType.GetMetaInfo (), param.GetMetaInfo ());
}
int top = param.Count;
- var ec = new EmitContext (null, proxy.GetILGenerator (), null);
-
- for (int i = 0; i <= top; i++)
- ParameterReference.EmitLdArg (ec, i);
+ var ec = new EmitContext (new ProxyMethodContext (container), proxy.GetILGenerator (), null, null);
+ ec.EmitThis ();
+ // TODO: GetAllParametersArguments
+ for (int i = 0; i < top; i++)
+ ec.EmitArgumentLoad (i);
ec.Emit (OpCodes.Call, base_method);
ec.Emit (OpCodes.Ret);
/// </summary>
bool BaseImplements (TypeSpec iface_type, MethodSpec mi, out MethodSpec base_method)
{
+ base_method = null;
var base_type = container.BaseType;
- base_method = (MethodSpec) MemberCache.FindMember (base_type, new MemberFilter (mi), BindingRestriction.None);
- if (base_method == null || (base_method.Modifiers & Modifiers.PUBLIC) == 0)
- return false;
+ //
+ // Setup filter with no return type to give better error message
+ // about mismatch at return type when the check bellow rejects them
+ //
+ var parameters = mi.Parameters;
+ while (true) {
+ var candidates = MemberCache.FindMembers (base_type, mi.Name, false);
+ if (candidates == null)
+ return false;
+
+ MethodSpec similar_candidate = null;
+ foreach (var candidate in candidates) {
+ if (candidate.Kind != MemberKind.Method)
+ continue;
- if (base_method.DeclaringType.IsInterface)
- return false;
+ if (candidate.Arity != mi.Arity)
+ continue;
+
+ var candidate_param = ((MethodSpec) candidate).Parameters;
+ if (!TypeSpecComparer.Override.IsEqual (parameters.Types, candidate_param.Types))
+ continue;
+
+ bool modifiers_match = true;
+ for (int i = 0; i < parameters.Count; ++i) {
+ //
+ // First check exact ref/out match
+ //
+ if ((parameters.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask) == (candidate_param.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask))
+ continue;
- // Why was it here ????
- //if (TypeManager.ImplementsInterface (base_type, iface_type)) {
- // return true;
- //}
+ modifiers_match = false;
- if (!base_method.IsAbstract && !base_method.IsVirtual)
- // FIXME: We can avoid creating a proxy if base_method can be marked 'final virtual' instead.
- // However, it's too late now, the MethodBuilder has already been created (see bug 377519)
+ //
+ // Different in ref/out only
+ //
+ if ((parameters.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask) != (candidate_param.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask)) {
+ if (similar_candidate == null) {
+ if (!candidate.IsPublic)
+ break;
+
+ if (!TypeSpecComparer.Override.IsEqual (mi.ReturnType, ((MethodSpec) candidate).ReturnType))
+ break;
+
+ // It's used for ref/out ambiguity overload check
+ similar_candidate = (MethodSpec) candidate;
+ }
+
+ continue;
+ }
+
+ similar_candidate = null;
+ break;
+ }
+
+ if (!modifiers_match)
+ continue;
+
+ //
+ // From this point the candidate is used for detailed error reporting
+ // because it's very close match to what we are looking for
+ //
+ base_method = (MethodSpec) candidate;
+
+ if (!candidate.IsPublic)
+ return false;
+
+ if (!TypeSpecComparer.Override.IsEqual (mi.ReturnType, base_method.ReturnType))
+ return false;
+
+ if (mi.IsGeneric && !Method.CheckImplementingMethodConstraints (container, base_method, mi)) {
+ return true;
+ }
+ }
+
+ if (base_method != null) {
+ if (similar_candidate != null) {
+ Report.SymbolRelatedToPreviousError (similar_candidate);
+ Report.SymbolRelatedToPreviousError (mi);
+ Report.SymbolRelatedToPreviousError (container);
+ Report.Warning (1956, 1, ((MemberCore) base_method.MemberDefinition).Location,
+ "The interface method `{0}' implementation is ambiguous between following methods: `{1}' and `{2}' in type `{3}'",
+ mi.GetSignatureForError (), base_method.GetSignatureForError (), similar_candidate.GetSignatureForError (), container.GetSignatureForError ());
+ }
+
+ break;
+ }
+
+ base_type = candidates[0].DeclaringType.BaseType;
+ if (base_type == null)
+ return false;
+ }
+
+ if (!base_method.IsVirtual) {
+#if STATIC
+ var base_builder = base_method.GetMetaInfo () as MethodBuilder;
+ if (base_builder != null) {
+ //
+ // We can avoid creating a proxy if base_method can be marked 'final virtual'. This can
+ // be done for all methods from compiled assembly
+ //
+ base_builder.__SetAttributes (base_builder.Attributes | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.NewSlot);
+ return true;
+ }
+#endif
DefineProxy (iface_type, base_method, mi);
+ }
return true;
}
/// Verifies that any pending abstract methods or interface methods
/// were implemented.
/// </summary>
- public bool VerifyPendingMethods (Report Report)
+ public bool VerifyPendingMethods ()
{
int top = pending_implementations.Length;
bool errors = false;
bool base_implements_type = type.IsInterface &&
container.BaseType != null &&
- container.BaseType.ImplementsInterface (type);
+ container.BaseType.ImplementsInterface (type, false);
for (int j = 0; j < pending_implementations [i].methods.Count; ++j) {
var mi = pending_implementations[i].methods[j];
if (candidate.IsStatic) {
Report.Error (736, container.Location,
"`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' is static",
- container.GetSignatureForError (), mi.GetSignatureForError (), TypeManager.CSharpSignature (candidate));
+ container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError ());
} else if ((candidate.Modifiers & Modifiers.PUBLIC) == 0) {
Report.Error (737, container.Location,
- "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' in not public",
+ "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' is not public",
container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError ());
} else {
Report.Error (738, container.Location,
"`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' return type `{3}' does not match interface member return type `{4}'",
- container.GetSignatureForError (), mi.GetSignatureForError (), TypeManager.CSharpSignature (candidate),
- TypeManager.CSharpName (candidate.ReturnType), TypeManager.CSharpName (mi.ReturnType));
+ container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError (),
+ candidate.ReturnType.GetSignatureForError (), mi.ReturnType.GetSignatureForError ());
}
} else {
Report.Error (535, container.Location, "`{0}' does not implement interface member `{1}'",