private ForEachAncestor ( bool>.Func |
||
action | bool>.Func | |
리턴 | bool |
/// <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; }
private bool IsMethodVisible(RubyMemberInfo/*!*/ method, RubyModule/*!*/ owner, RubyClass visibilityContext, bool foundCallerSelf) { // call with implicit self => all methods are visible if (visibilityContext == RubyClass.IgnoreVisibility) { return true; } if (method.Visibility == RubyMethodVisibility.Protected) { // A protected method is visible if the caller's self immediate class is a descendant of the method owner. if (foundCallerSelf) { return true; } // walk ancestors from caller's self class (visibilityContext) // until the method owner is found or this module is found (this module is a descendant of the owner): return visibilityContext.ForEachAncestor((module) => module == owner || module == this); } return method.Visibility == RubyMethodVisibility.Public; }