//
// pending.cs: Pending method implementation
//
-// Author:
+// Authors:
// Miguel de Icaza (miguel@gnu.org)
+// Marek Safar (marek.safar@gmail.com)
//
-// Licensed under the terms of the GNU GPL
-//
-// (C) 2001, 2002 Ximian, Inc (http://www.ximian.com)
+// Dual licensed under the terms of the MIT X11 or GNU GPL
//
+// Copyright 2001, 2002 Ximian, Inc (http://www.ximian.com)
+// Copyright 2003-2008 Novell, Inc.
+// Copyright 2011 Xamarin Inc
//
using System;
-using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+
+#if STATIC
+using IKVM.Reflection;
+using IKVM.Reflection.Emit;
+#else
using System.Reflection;
using System.Reflection.Emit;
+#endif
namespace Mono.CSharp {
struct TypeAndMethods {
- public Type type;
- public MethodInfo [] methods;
+ public TypeSpec type;
+ public IList<MethodSpec> methods;
//
// Whether it is optional, this is used to allow the explicit/implicit
// class X : IA { } class Y : X, IA { IA.Explicit (); }
//
public bool optional;
-
- // Far from ideal, but we want to avoid creating a copy
- // of methods above.
- public Type [][] args;
-
- //This is used to store the modifiers of arguments
- public Parameter.Modifier [][] mods;
-
+
//
// This flag on the method says `We found a match, but
// because it was private, we could not use the match
//
- public bool [] found;
+ public MethodData [] found;
// If a method is defined here, then we always need to
// create a proxy for it. This is used when implementing
// an interface's indexer with a different IndexerName.
- public MethodInfo [] need_proxy;
-
- //
- // The name of the indexer (if it exists), precompute set/get, because
- // they would be recomputed many times inside a loop later on.
- //
- public string set_indexer_name;
- public string get_indexer_name;
+ public MethodSpec [] need_proxy;
}
- public class PendingImplementation {
- /// <summary>
- /// The container for this PendingImplementation
- /// </summary>
- TypeContainer container;
-
- /// <summary>
- /// This filter is used by FindMembers, and it is used to
- /// extract only virtual/abstract fields
- /// </summary>
- static MemberFilter virtual_method_filter;
-
- /// <summary>
- /// This is the array of TypeAndMethods that describes the pending implementations
- /// (both interfaces and abstract methods in base class)
- /// </summary>
- TypeAndMethods [] pending_implementations;
+ struct ProxyMethodContext : IMemberContext
+ {
+ readonly TypeContainer container;
- static bool IsVirtualFilter (MemberInfo m, object filterCriteria)
+ public ProxyMethodContext (TypeContainer container)
{
- MethodInfo mi = m as MethodInfo;
- return (mi == null) ? false : mi.IsVirtual;
+ this.container = container;
}
- /// <summary>
- /// Inits the virtual_method_filter
- /// </summary>
- static PendingImplementation ()
- {
- virtual_method_filter = new MemberFilter (IsVirtualFilter);
+ public TypeSpec CurrentType {
+ get {
+ throw new NotImplementedException ();
+ }
}
- // <remarks>
- // Returns a list of the abstract methods that are exposed by all of our
- // bases that we must implement. Notice that this `flattens' the
- // method search space, and takes into account overrides.
- // </remarks>
- static ArrayList GetAbstractMethods (Type t)
- {
- ArrayList list = null;
- bool searching = true;
- Type current_type = t;
-
- do {
- MemberList mi;
-
- mi = TypeContainer.FindMembers (
- current_type, MemberTypes.Method,
- BindingFlags.Public | BindingFlags.NonPublic |
- BindingFlags.Instance | BindingFlags.DeclaredOnly,
- virtual_method_filter, null);
-
- if (current_type == TypeManager.object_type)
- searching = false;
- else {
- current_type = current_type.BaseType;
- if (!current_type.IsAbstract)
- searching = false;
- }
+ public TypeParameters CurrentTypeParameters {
+ get {
+ throw new NotImplementedException ();
+ }
+ }
- if (mi.Count == 0)
- continue;
+ public MemberCore CurrentMemberDefinition {
+ get {
+ throw new NotImplementedException ();
+ }
+ }
- if (mi.Count == 1 && !(mi [0] is MethodBase))
- searching = false;
- else
- list = TypeManager.CopyNewMethods (list, mi);
- } while (searching);
+ public bool IsObsolete {
+ get {
+ return false;
+ }
+ }
- if (list == null)
- return null;
-
- for (int i = 0; i < list.Count; i++){
- while (list.Count > i && !((MethodInfo) list [i]).IsAbstract)
- list.RemoveAt (i);
+ public bool IsUnsafe {
+ get {
+ throw new NotImplementedException ();
}
+ }
- if (list.Count == 0)
- return null;
+ public bool IsStatic {
+ get {
+ return false;
+ }
+ }
- return list;
+ 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 ();
}
- PendingImplementation (TypeContainer container, MissingInterfacesInfo [] missing_ifaces, ArrayList abstract_methods, int total)
+ public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc)
{
- TypeBuilder type_builder = container.TypeBuilder;
+ throw new NotImplementedException ();
+ }
+
+ public FullNamedExpression LookupNamespaceAlias (string name)
+ {
+ throw new NotImplementedException ();
+ }
+ }
+
+ public class PendingImplementation
+ {
+ /// <summary>
+ /// The container for this PendingImplementation
+ /// </summary>
+ readonly TypeDefinition container;
+
+ /// <summary>
+ /// This is the array of TypeAndMethods that describes the pending implementations
+ /// (both interfaces and abstract methods in base class)
+ /// </summary>
+ TypeAndMethods [] pending_implementations;
+
+ PendingImplementation (TypeDefinition container, MissingInterfacesInfo[] missing_ifaces, MethodSpec[] abstract_methods, int total)
+ {
+ var type_builder = container.Definition;
this.container = container;
pending_implementations = new TypeAndMethods [total];
int i = 0;
- foreach (MissingInterfacesInfo missing in missing_ifaces){
- MethodInfo [] mi;
- Type t = missing.Type;
-
- if (!t.IsInterface)
- continue;
+ if (abstract_methods != null) {
+ int count = abstract_methods.Length;
+ pending_implementations [i].methods = new MethodSpec [count];
+ pending_implementations [i].need_proxy = new MethodSpec [count];
- if (t is TypeBuilder){
- TypeContainer iface;
-
- iface = TypeManager.LookupInterface (t);
+ pending_implementations [i].methods = abstract_methods;
+ pending_implementations [i].found = new MethodData [count];
+ pending_implementations [i].type = type_builder;
+ ++i;
+ }
- mi = iface.GetMethods ();
- } else
- mi = t.GetMethods ();
+ foreach (MissingInterfacesInfo missing in missing_ifaces) {
+ var iface = missing.Type;
+ var mi = MemberCache.GetInterfaceMethods (iface);
- int count = mi.Length;
- pending_implementations [i].type = t;
+ int count = mi.Count;
+ pending_implementations [i].type = iface;
pending_implementations [i].optional = missing.Optional;
pending_implementations [i].methods = mi;
- pending_implementations [i].args = new Type [count][];
- pending_implementations [i].mods = new Parameter.Modifier [count][];
- pending_implementations [i].found = new bool [count];
- pending_implementations [i].need_proxy = new MethodInfo [count];
- string indexer_name = TypeManager.IndexerPropertyName (t);
-
- pending_implementations [i].set_indexer_name = "set_" + indexer_name;
- pending_implementations [i].get_indexer_name = "get_" + indexer_name;
-
- int j = 0;
- foreach (MethodInfo m in mi){
- pending_implementations [i].args [j] = TypeManager.NoTypes;
- pending_implementations [i].mods [j] = null;
-
- // If there is a previous error, just ignore
- if (m == null)
- continue;
-
- ParameterData pd = TypeManager.GetParameterData (m);
- pending_implementations [i].args [j] = pd.Types;
-
- if (pd.Count > 0){
- Parameter.Modifier [] pm = new Parameter.Modifier [pd.Count];
- for (int k = 0; k < pd.Count; k++)
- pm [k] = pd.ParameterModifier (k);
- pending_implementations [i].mods [j] = pm;
- }
-
- j++;
- }
+ pending_implementations [i].found = new MethodData [count];
+ pending_implementations [i].need_proxy = new MethodSpec [count];
i++;
}
+ }
- if (abstract_methods != null){
- int count = abstract_methods.Count;
- pending_implementations [i].methods = new MethodInfo [count];
- pending_implementations [i].need_proxy = new MethodInfo [count];
-
- abstract_methods.CopyTo (pending_implementations [i].methods, 0);
- pending_implementations [i].found = new bool [count];
- pending_implementations [i].args = new Type [count][];
- pending_implementations [i].mods = new Parameter.Modifier [count][];
- pending_implementations [i].type = type_builder;
-
- string indexer_name = TypeManager.IndexerPropertyName (type_builder);
- pending_implementations [i].set_indexer_name = "set_" + indexer_name;
- pending_implementations [i].get_indexer_name = "get_" + indexer_name;
-
- int j = 0;
- foreach (MemberInfo m in abstract_methods){
- MethodInfo mi = (MethodInfo) m;
-
- ParameterData pd = TypeManager.GetParameterData (mi);
- Type [] types = pd.Types;
-
- pending_implementations [i].args [j] = types;
- pending_implementations [i].mods [j] = null;
- if (pd.Count > 0){
- Parameter.Modifier [] pm = new Parameter.Modifier [pd.Count];
- for (int k = 0; k < pd.Count; k++)
- pm [k] = pd.ParameterModifier (k);
- pending_implementations [i].mods [j] = pm;
- }
-
- j++;
- }
+ Report Report {
+ get {
+ return container.Module.Compiler.Report;
}
}
struct MissingInterfacesInfo {
- public Type Type;
+ public TypeSpec Type;
public bool Optional;
- public MissingInterfacesInfo (Type t)
+ public MissingInterfacesInfo (TypeSpec t)
{
Type = t;
Optional = false;
}
}
- static MissingInterfacesInfo [] EmptyMissingInterfacesInfo = new MissingInterfacesInfo [0];
+ static readonly MissingInterfacesInfo [] EmptyMissingInterfacesInfo = new MissingInterfacesInfo [0];
- static MissingInterfacesInfo [] GetMissingInterfaces (TypeBuilder type_builder)
+ static MissingInterfacesInfo [] GetMissingInterfaces (TypeDefinition container)
{
//
- // Notice that TypeBuilders will only return the interfaces that the Type
- // is supposed to implement, not all the interfaces that the type implements.
- //
- // Even better -- on MS it returns an empty array, no matter what.
+ // Interfaces will return all interfaces that the container
+ // implements including any inherited interfaces
//
- // Completely broken. So we do it ourselves!
- //
- Type [] impl = TypeManager.GetExplicitInterfaces (type_builder);
+ var impl = container.Definition.Interfaces;
- if (impl == null || impl.Length == 0)
+ if (impl == null || impl.Count == 0)
return EmptyMissingInterfacesInfo;
- MissingInterfacesInfo [] ret = new MissingInterfacesInfo [impl.Length];
+ var ret = new MissingInterfacesInfo[impl.Count];
- for (int i = 0; i < impl.Length; i++)
+ for (int i = 0; i < ret.Length; i++)
ret [i] = new MissingInterfacesInfo (impl [i]);
-
+
// we really should not get here because Object doesnt implement any
// interfaces. But it could implement something internal, so we have
// to handle that case.
- if (type_builder.BaseType == null)
+ if (container.BaseType == null)
return ret;
- Type [] base_impls = TypeManager.GetInterfaces (type_builder.BaseType);
-
- foreach (Type t in base_impls) {
- for (int i = 0; i < ret.Length; i ++) {
- if (t == ret [i].Type) {
- ret [i].Optional = true;
- break;
+ var base_impls = container.BaseType.Interfaces;
+ if (base_impls != null) {
+ foreach (TypeSpec t in base_impls) {
+ for (int i = 0; i < ret.Length; i++) {
+ if (t == ret[i].Type) {
+ ret[i].Optional = true;
+ break;
+ }
}
}
}
+
return ret;
}
// 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)
{
- TypeBuilder type_builder = container.TypeBuilder;
- MissingInterfacesInfo [] missing_interfaces;
- Type b = type_builder.BaseType;
+ TypeSpec b = container.BaseType;
- missing_interfaces = GetMissingInterfaces (type_builder);
+ var missing_interfaces = GetMissingInterfaces (container);
//
// If we are implementing an abstract class, and we are not
//
// We also pre-compute the methods.
//
- bool implementing_abstract = ((b != null) && b.IsAbstract && !type_builder.IsAbstract);
- ArrayList abstract_methods = null;
+ bool implementing_abstract = ((b != null) && b.IsAbstract && (container.ModFlags & Modifiers.ABSTRACT) == 0);
+ MethodSpec[] abstract_methods = null;
if (implementing_abstract){
- abstract_methods = GetAbstractMethods (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 MethodInfo IsInterfaceMethod (Type t, string name, Type ret_type, ParameterData args)
+ public MethodSpec IsInterfaceMethod (MemberName name, TypeSpec ifaceType, MethodData method, out MethodSpec ambiguousCandidate, ref bool optional)
{
- return InterfaceMethod (t, name, ret_type, args, Operation.Lookup, null);
+ return InterfaceMethod (name, ifaceType, method, Operation.Lookup, out ambiguousCandidate, ref optional);
}
- public MethodInfo IsInterfaceIndexer (Type t, Type ret_type, ParameterData args)
+ public void ImplementMethod (MemberName name, TypeSpec ifaceType, MethodData method, bool clear_one, out MethodSpec ambiguousCandidate, ref bool optional)
{
- return InterfaceMethod (t, null, ret_type, args, Operation.Lookup, null);
+ InterfaceMethod (name, ifaceType, method, clear_one ? Operation.ClearOne : Operation.ClearAll, out ambiguousCandidate, ref optional);
}
- public void ImplementMethod (Type t, string name, Type ret_type, ParameterData args, bool clear_one)
- {
- InterfaceMethod (t, name, ret_type, args,
- clear_one ? Operation.ClearOne : Operation.ClearAll, null);
- }
-
- public void ImplementIndexer (Type t, MethodInfo mi, Type ret_type, ParameterData args, bool clear_one)
- {
- InterfaceMethod (t, null, ret_type, args,
- clear_one ? Operation.ClearOne : Operation.ClearAll, mi);
- }
-
/// <remarks>
/// If a method in Type `t' (or null to look in all interfaces
/// and the base abstract class) with name `Name', return type `ret_type' and
/// that was used in the interface, then we always need to create a proxy for it.
///
/// </remarks>
- public MethodInfo InterfaceMethod (Type t, string name, Type ret_type, ParameterData args,
- Operation op, MethodInfo need_proxy)
+ public MethodSpec InterfaceMethod (MemberName name, TypeSpec iType, MethodData method, Operation op, out MethodSpec ambiguousCandidate, ref bool optional)
{
- int arg_len = args.Count;
+ 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 (!(t == null || tm.type == t))
+ if (!(iType == null || tm.type == iType))
continue;
- int method_count = tm.methods.Length;
- MethodInfo m;
+ int method_count = tm.methods.Count;
for (int i = 0; i < method_count; i++){
m = tm.methods [i];
if (m == null)
continue;
+ if (is_indexer) {
+ if (!m.IsAccessor || m.Parameters.IsEmpty)
+ continue;
+ } else {
+ if (name.Name != m.Name)
+ continue;
+
+ if (m.Arity != name.Arity)
+ continue;
+ }
+
+ if (!TypeSpecComparer.Override.IsEqual (m.Parameters, args))
+ continue;
+
+ if (!TypeSpecComparer.Override.IsEqual (m.ReturnType, ret_type)) {
+ tm.found[i] = method;
+ continue;
+ }
+
//
// `need_proxy' is not null when we're implementing an
// interface indexer and this is Clear(One/All) operation.
// signature and not on the name (this is done in the Lookup
// for an interface indexer).
//
- if (name == null){
- if (m.Name != tm.get_indexer_name && m.Name != tm.set_indexer_name)
+ if (op != Operation.Lookup) {
+ if (m.IsAccessor != method.method.IsAccessor)
continue;
- } else if ((need_proxy == null) && (name != m.Name))
- continue;
- if (ret_type != m.ReturnType &&
- !(ret_type == null && m.ReturnType == TypeManager.void_type) &&
- !(m.ReturnType == null && ret_type == TypeManager.void_type))
- continue;
-
- //
- // Check if we have the same parameters
- //
-
- if (tm.args [i] == null && arg_len != 0)
- continue;
- if (tm.args [i] != null && tm.args [i].Length != arg_len)
- continue;
-
- int j;
-
- for (j = 0; j < arg_len; j++) {
- if (tm.args [i][j] != args.ParameterType (j))
- break;
- if (tm.mods [i][j] == args.ParameterModifier (j))
- continue;
- // The modifiers are different, but if one of them
- // is a PARAMS modifier, and the other isn't, ignore
- // the difference.
- if (tm.mods [i][j] != Parameter.Modifier.PARAMS &&
- args.ParameterModifier (j) != Parameter.Modifier.PARAMS)
- break;
- }
- if (j != arg_len)
- continue;
-
- if (op != Operation.Lookup){
// 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
// interface indexer. In this case, we need to create
// a proxy if the implementation's IndexerName doesn't
// match the IndexerName in the interface.
- bool name_matches = false;
- if (name == m.Name || m.Name == tm.get_indexer_name || m.Name == tm.set_indexer_name)
- name_matches = true;
-
- if ((t == null) && (need_proxy != null) && !name_matches)
- tm.need_proxy [i] = need_proxy;
- else
- tm.methods [i] = null;
+ if (m.DeclaringType.IsInterface && iType == null && name.Name != m.Name) { // TODO: This is very expensive comparison
+ tm.need_proxy[i] = method.method.Spec;
+ } else {
+ tm.methods[i] = null;
+ }
+ } else {
+ tm.found [i] = method;
+ optional = tm.optional;
+ }
+
+ if (op == Operation.Lookup && name.ExplicitInterface != null && ambiguousCandidate == null) {
+ ambiguousCandidate = m;
+ continue;
}
- tm.found [i] = true;
//
// Lookups and ClearOne return
}
// If a specific type was requested, we can stop now.
- if (tm.type == t)
- return null;
+ if (tm.type == iType)
+ break;
}
- return null;
+
+ m = ambiguousCandidate;
+ ambiguousCandidate = null;
+ return m;
}
/// <summary>
/// For that case, we create an explicit implementation function
/// I.M in Y.
/// </summary>
- void DefineProxy (Type iface, MethodInfo base_method, MethodInfo iface_method,
- Type [] args)
+ void DefineProxy (TypeSpec iface, MethodSpec base_method, MethodSpec iface_method)
{
- MethodBuilder proxy;
+ // TODO: Handle nested iface names
+ string proxy_name;
+ var ns = iface.MemberDefinition.Namespace;
+ if (string.IsNullOrEmpty (ns))
+ proxy_name = iface.MemberDefinition.Name + "." + iface_method.Name;
+ else
+ proxy_name = ns + "." + iface.MemberDefinition.Name + "." + iface_method.Name;
- string proxy_name = iface.Name + "." + iface_method.Name;
+ var param = iface_method.Parameters;
- proxy = container.TypeBuilder.DefineMethod (
+ MethodBuilder proxy = container.TypeBuilder.DefineMethod (
proxy_name,
+ MethodAttributes.Private |
MethodAttributes.HideBySig |
MethodAttributes.NewSlot |
- MethodAttributes.Virtual,
+ MethodAttributes.CheckAccessOnOverride |
+ MethodAttributes.Virtual | MethodAttributes.Final,
CallingConventions.Standard | CallingConventions.HasThis,
- base_method.ReturnType, args);
+ base_method.ReturnType.GetMetaInfo (), param.GetMetaInfo ());
- int top = args.Length;
- ILGenerator ig = proxy.GetILGenerator ();
+ if (iface_method.IsGeneric) {
+ var gnames = iface_method.GenericDefinition.TypeParameters.Select (l => l.Name).ToArray ();
+ proxy.DefineGenericParameters (gnames);
+ }
+
+ for (int i = 0; i < param.Count; i++) {
+ string name = param.FixedParameters [i].Name;
+ ParameterAttributes attr = ParametersCompiled.GetParameterAttribute (param.FixedParameters [i].ModFlags);
+ proxy.DefineParameter (i + 1, attr, name);
+ }
- for (int i = 0; i <= top; i++)
- ParameterReference.EmitLdArg (ig, i);
+ int top = param.Count;
+ 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);
- ig.Emit (OpCodes.Call, base_method);
- ig.Emit (OpCodes.Ret);
+ ec.Emit (OpCodes.Call, base_method);
+ ec.Emit (OpCodes.Ret);
- container.TypeBuilder.DefineMethodOverride (proxy, iface_method);
+ container.TypeBuilder.DefineMethodOverride (proxy, (MethodInfo) iface_method.GetMetaInfo ());
}
/// <summary>
/// the given method (which turns out, it is valid to have an interface
/// implementation in a base
/// </summary>
- bool BaseImplements (Type iface_type, MethodInfo mi)
+ bool BaseImplements (TypeSpec iface_type, MethodSpec mi, out MethodSpec base_method)
{
- MethodSignature ms;
-
- Type [] args = TypeManager.GetParameterData (mi).Types;
- ms = new MethodSignature (mi.Name, mi.ReturnType, args);
- MemberList list = TypeContainer.FindMembers (
- container.TypeBuilder.BaseType, MemberTypes.Method | MemberTypes.Property,
- BindingFlags.Public | BindingFlags.Instance,
- MethodSignature.method_signature_filter, ms);
-
- if (list.Count == 0)
- return false;
-
- if (TypeManager.ImplementsInterface (container.TypeBuilder.BaseType, iface_type))
- return true;
+ base_method = null;
+ var base_type = container.BaseType;
//
- // FIXME: We should be creating fewer proxies. The runtime can handle most cases.
- // At worst, if we can't avoid creating the proxy, we may need to make the
- // proxy use Callvirt.
+ // Setup filter with no return type to give better error message
+ // about mismatch at return type when the check bellow rejects them
//
- MethodInfo base_method = (MethodInfo) list [0];
+ var parameters = mi.Parameters;
+ MethodSpec close_match = null;
+
+ while (true) {
+ var candidates = MemberCache.FindMembers (base_type, mi.Name, false);
+ if (candidates == null) {
+ base_method = close_match;
+ return false;
+ }
- if (!base_method.IsAbstract && !base_method.IsVirtual)
- DefineProxy (iface_type, base_method, mi, args);
+ MethodSpec similar_candidate = null;
+ foreach (var candidate in candidates) {
+ if (candidate.Kind != MemberKind.Method)
+ continue;
+
+ 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;
+
+ modifiers_match = false;
+
+ //
+ // 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
+ //
+ var m = (MethodSpec) candidate;
+
+ if (!m.IsPublic) {
+ if (close_match == null)
+ close_match = m;
+
+ continue;
+ }
+
+ if (!TypeSpecComparer.Override.IsEqual (mi.ReturnType, m.ReturnType)) {
+ if (close_match == null)
+ close_match = m;
+
+ continue;
+ }
+
+ base_method = m;
+
+ if (mi.IsGeneric && !Method.CheckImplementingMethodConstraints (container, m, 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) {
+ base_method = close_match;
+ 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;
}
int i;
for (i = 0; i < top; i++){
- Type type = pending_implementations [i].type;
- int j = 0;
+ TypeSpec type = pending_implementations [i].type;
- foreach (MethodInfo mi in pending_implementations [i].methods){
+ bool base_implements_type = type.IsInterface &&
+ container.BaseType != null &&
+ container.BaseType.ImplementsInterface (type, false);
+
+ for (int j = 0; j < pending_implementations [i].methods.Count; ++j) {
+ var mi = pending_implementations[i].methods[j];
if (mi == null)
continue;
if (type.IsInterface){
- MethodInfo need_proxy =
+ var need_proxy =
pending_implementations [i].need_proxy [j];
if (need_proxy != null) {
- Type [] args = TypeManager.GetParameterData (mi).Types;
- DefineProxy (type, need_proxy, mi, args);
+ DefineProxy (type, need_proxy, mi);
continue;
}
- if (BaseImplements (type, mi))
- continue;
-
if (pending_implementations [i].optional)
continue;
+ MethodSpec candidate;
+ if (base_implements_type || BaseImplements (type, mi, out candidate))
+ continue;
+
+ if (candidate == null) {
+ MethodData md = pending_implementations [i].found [j];
+ if (md != null)
+ candidate = md.method.Spec;
+ }
+
Report.SymbolRelatedToPreviousError (mi);
- if (pending_implementations [i].found [j]) {
- if (mi.IsSpecialName) {
- string name = TypeManager.CSharpName (mi.DeclaringType) + '.' + mi.Name.Substring (4);
- Report.Error (551, container.Location, "Explicit interface implementation `{0}.{1}' is missing accessor `{2}'",
- container.GetSignatureForError (), name, TypeManager.CSharpSignature (mi, true));
+ if (candidate != null) {
+ Report.SymbolRelatedToPreviousError (candidate);
+ 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 (), 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}' is not public",
+ container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError ());
} else {
- string[] methodLabel = TypeManager.CSharpSignature (mi).Split ('.');
- Report.Error (536, container.Location,
- "`{0}' does not implement interface member `{1}'. `{2}.{3}' " +
- "is either static, not public, or has the wrong return type",
- container.Name, TypeManager.CSharpSignature (mi),
- container.Name, methodLabel[methodLabel.Length - 1]);
+ 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 (), candidate.GetSignatureForError (),
+ candidate.ReturnType.GetSignatureForError (), mi.ReturnType.GetSignatureForError ());
}
- }
- else {
+ } else {
Report.Error (535, container.Location, "`{0}' does not implement interface member `{1}'",
- container.GetSignatureForError (), TypeManager.CSharpSignature (mi));
+ container.GetSignatureForError (), mi.GetSignatureForError ());
}
} else {
+ Report.SymbolRelatedToPreviousError (mi);
Report.Error (534, container.Location, "`{0}' does not implement inherited abstract member `{1}'",
- container.GetSignatureForError (), TypeManager.CSharpSignature (mi, true));
+ container.GetSignatureForError (), mi.GetSignatureForError ());
}
errors = true;
- j++;
}
}
return errors;
}
- } /* end of class */
+ }
}