private bool TryGetClrMethod(Type/*!*/ type, BindingFlags bindingFlags, bool specialNameOnly,
string/*!*/ name, string clrNamePrefix, string/*!*/ clrName, string altClrName, out RubyMemberInfo method) {
Context.RequiresClassHierarchyLock();
// declared only:
List<OverloadInfo> initialMembers = new List<OverloadInfo>(GetDeclaredClrMethods(type, bindingFlags, clrNamePrefix, clrName, altClrName, specialNameOnly));
if (initialMembers.Count == 0) {
// case [1]
//
// Note: This failure might be cached (see CacheFailure) based on the type and name,
// therefore it must not depend on any other mutable state:
method = null;
return false;
}
// If all CLR inherited members are to be returned we are done.
// (creates a detached info; used by Kernel#clr_member)
if ((bindingFlags & BindingFlags.DeclaredOnly) == 0) {
method = MakeGroup(initialMembers, initialMembers.Count, specialNameOnly, true);
return true;
}
// inherited overloads:
List<RubyClass> ancestors = new List<RubyClass>();
RubyMemberInfo inheritedRubyMember = null;
bool skipHidden = false;
ForEachAncestor((module) => {
if (module != this) {
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 CLR modules, 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;
});
// (method clr name, parameter types) => (overload, owner)
Dictionary<Key<string, ValueArray<Type>>, ClrOverloadInfo> allMethods = null;
if (inheritedRubyMember != null) {
// case [2.2.2]: add CLR methods from the Ruby member:
var inheritedGroup = inheritedRubyMember as RubyOverloadGroupInfo;
if (inheritedGroup != null) {
AddMethodsOverwriteExisting(ref allMethods, inheritedGroup.MethodBases, inheritedGroup.OverloadOwners, specialNameOnly);
} else if (inheritedRubyMember.IsRemovable) {
// The groups created below won't contain overloads defined above (if there are any).
// If this method is removed we need to invalidate them.
inheritedRubyMember.InvalidateGroupsOnRemoval = true;
}
}
// populate classes in (type..Kernel] or (type..C) with method groups:
for (int i = ancestors.Count - 1; i >= 0; i--) {
var declared = ancestors[i].GetDeclaredClrMethods(ancestors[i].TypeTracker.Type, bindingFlags, clrNamePrefix, clrName, altClrName, specialNameOnly);
if (AddMethodsOverwriteExisting(ref allMethods, declared, null, specialNameOnly)) {
// 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, ancestors[i].MakeGroup(allMethods.Values));
}
}
if (allMethods != null) {
// add members declared in self:
AddMethodsOverwriteExisting(ref allMethods, initialMembers, null, specialNameOnly);
// return the group, it will be stored in the method table by the caller:
method = MakeGroup(allMethods.Values);
} else {
method = MakeGroup(initialMembers, initialMembers.Count, specialNameOnly, false);
}
return true;
}