/// <summary>
/// There are basically 4 cases:
/// 1) CLR method of the given name is not defined in the specified type.
/// Do nothing, the method will be found as we traverse the hierarhy towards the Kernel module.
/// 2) Otherwise
/// 1) There is no RubyMemberInfo of given <c>name</c> present in the (type..Kernel] ancestors.
/// We need to search all types in (type..Object] for CLR method overloads.
/// 2) There is a RubyMemberInfo in a class, say C, in (type..Kernel].
/// We need to get CLR methods from (type..C) in addition to the members in the type.
/// 1) C.HidesInheritedOverloads == true
/// All overloads of the method we look for are in [type..C).
/// 2) C.HidesInheritedOverloads == false
/// All overloads of the method we look for are in [type..C) and in the RubyMemberInfo.
/// </summary>
internal bool TryGetClrMethod(RubyClass/*!*/ cls, Type/*!*/ type, BindingFlags bindingFlags, string/*!*/ name, string/*!*/ clrName, out RubyMemberInfo method) {
// declared only:
MemberInfo[] initialMembers = GetDeclaredClrMethods(type, bindingFlags, clrName);
int initialVisibleMemberCount = GetVisibleMethodCount(initialMembers);
if (initialVisibleMemberCount == 0) {
// case [1]
method = null;
return false;
}
// inherited overloads:
List<RubyClass> ancestors = new List<RubyClass>();
RubyMemberInfo inheritedRubyMember = null;
bool skipHidden = false;
cls.ForEachAncestor((module) => {
if (module != cls) {
if (module.TryGetDefinedMethod(name, ref skipHidden, out inheritedRubyMember) && !inheritedRubyMember.IsSuperForwarder) {
return true;
}
// Skip classes that have no tracker, e.g. Fixnum(tracker) <: Integer(null) <: Numeric(null) <: Object(tracker).
// Skip interfaces, their methods are not callable => do not include them into a method group.
// Skip all classes once hidden sentinel is encountered (no CLR overloads are visible since then).
if (!skipHidden && module.TypeTracker != null && module.IsClass) {
ancestors.Add((RubyClass)module);
}
}
// continue:
return false;
});
_allMethods = null;
if (inheritedRubyMember != null) {
// case [2.2.2]: add CLR methods from the Ruby member:
var inheritedGroup = inheritedRubyMember as RubyMethodGroupInfo;
if (inheritedGroup != null) {
AddMethodsOverwriteExisting(inheritedGroup.MethodBases, inheritedGroup.OverloadOwners);
}
}
// populate classes in (type..Kernel] or (type..C) with method groups:
for (int i = ancestors.Count - 1; i >= 0; i--) {
var declared = GetDeclaredClrMethods(ancestors[i].TypeTracker.Type, bindingFlags, clrName);
if (declared.Length != 0 && AddMethodsOverwriteExisting(declared, null)) {
// There is no cached method that needs to be invalidated.
//
// Proof:
// Suppose the group being created here overridden an existing method that is cached in a dynamic site invoked on some target class.
// Then either the target class is above all ancestors[i] or below some. If it is above then the new group doesn't
// invalidate validity of the site. If it is below then the method resolution for the cached method would create
// and store to method tables all method groups in between the target class and the owner of the cached method, including the
// one that contain overloads of ancestors[i]. But no module below inheritedRubyMember contains a method group of the name
// being currently resolved.
ancestors[i].AddMethodNoCacheInvalidation(name, MakeAllMethodsGroup(ancestors[i]));
}
}
if (_allMethods != null) {
// add members declared in self:
AddMethodsOverwriteExisting(initialMembers, null);
// return the group, it will be stored in the method table by the caller:
method = MakeAllMethodsGroup(cls);
} else {
method = MakeSingleOwnerGroup(cls, initialMembers, initialVisibleMemberCount);
}
return true;
}