diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java index 336a810ae087..18cba3a5ef6d 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java @@ -532,7 +532,7 @@ public class Mappings { } @Nullable - private Boolean isInheritorOf(final int who, final int whom) { + private Boolean isInheritorOf(final int who, final int whom, TIntHashSet visitedClasses) { if (who == whom) { return Boolean.TRUE; } @@ -540,8 +540,15 @@ public class Mappings { final ClassRepr repr = reprByName(who); if (repr != null) { + if (visitedClasses == null) { + visitedClasses = new TIntHashSet(); + visitedClasses.add(who); + } for (int s : repr.getSupers()) { - final Boolean inheritorOf = isInheritorOf(s, whom); + if (!visitedClasses.add(s)) { + continue; + } + final Boolean inheritorOf = isInheritorOf(s, whom, visitedClasses); if (inheritorOf != null && inheritorOf) { return inheritorOf; } @@ -576,7 +583,7 @@ public class Mappings { } if (whom instanceof TypeRepr.ClassType) { - return isInheritorOf(((TypeRepr.ClassType)who).className, ((TypeRepr.ClassType)whom).className); + return isInheritorOf(((TypeRepr.ClassType)who).className, ((TypeRepr.ClassType)whom).className, null); } return Boolean.FALSE; @@ -594,18 +601,19 @@ public class Mappings { return hasOverriddenFields(field, r, null); } - void collectSupersRecursively(@NotNull final int className, @NotNull final TIntHashSet container) { + void collectSupersRecursively(final int className, @NotNull final TIntHashSet container) { final ClassRepr classRepr = reprByName(className); if (classRepr != null) { final int[] supers = classRepr.getSupers(); - container.addAll(supers); - for (int aSuper : supers) { - collectSupersRecursively(aSuper, container); + if (container.addAll(supers)) { + for (int aSuper : supers) { + collectSupersRecursively(aSuper, container); + } } } } - void affectSubclasses(final int className, final Collection affectedFiles, final Collection affectedUsages, final TIntHashSet dependants, final boolean usages, final Collection alreadyCompiledFiles) { + void affectSubclasses(final int className, final Collection affectedFiles, final Collection affectedUsages, final TIntHashSet dependants, final boolean usages, final Collection alreadyCompiledFiles, TIntHashSet visitedClasses) { debug("Affecting subclasses of class: ", className); final Collection allSources = myClassToSourceFile.get(className); @@ -639,10 +647,17 @@ public class Mappings { final TIntHashSet directSubclasses = myClassToSubclasses.get(className); if (directSubclasses != null) { + if (visitedClasses == null) { + visitedClasses = new TIntHashSet(); + visitedClasses.add(className); + } + final TIntHashSet _visitedClasses = visitedClasses; directSubclasses.forEach(new TIntProcedure() { @Override public boolean execute(int subClass) { - affectSubclasses(subClass, affectedFiles, affectedUsages, dependants, usages, alreadyCompiledFiles); + if (_visitedClasses.add(subClass)) { + affectSubclasses(subClass, affectedFiles, affectedUsages, dependants, usages, alreadyCompiledFiles, _visitedClasses); + } return true; } }); @@ -715,7 +730,7 @@ public class Mappings { @Override public boolean checkResidence(final int residence) { - final Boolean inheritorOf = isInheritorOf(residence, rootClass); + final Boolean inheritorOf = isInheritorOf(residence, rootClass, null); return inheritorOf == null || !inheritorOf || super.checkResidence(residence); } } @@ -1115,7 +1130,7 @@ public class Mappings { debug("Method: ", m.name); if (it.isInterface() || it.isAbstract() || m.isAbstract()) { debug("Class is abstract, or is interface, or added method in abstract => affecting all subclasses"); - myFuture.affectSubclasses(it.name, myAffectedFiles, state.myAffectedUsages, state.myDependants, false, myCompiledFiles); + myFuture.affectSubclasses(it.name, myAffectedFiles, state.myAffectedUsages, state.myDependants, false, myCompiledFiles, null); } TIntHashSet propagated = null; @@ -1160,7 +1175,7 @@ public class Mappings { if (methodClass == MOCK_CLASS) { continue; } - final Boolean inheritorOf = myPresent.isInheritorOf(methodClass.name, it.name); + final Boolean inheritorOf = myPresent.isInheritorOf(methodClass.name, it.name, null); final boolean isInheritor = inheritorOf != null && inheritorOf; debug("Method: ", method.name); @@ -1413,7 +1428,7 @@ public class Mappings { if ((d.addedModifiers() & Opcodes.ACC_STATIC) > 0) { debug("Added static specifier --- affecting subclasses"); - myFuture.affectSubclasses(it.name, myAffectedFiles, state.myAffectedUsages, state.myDependants, false, myCompiledFiles); + myFuture.affectSubclasses(it.name, myAffectedFiles, state.myAffectedUsages, state.myDependants, false, myCompiledFiles, null); } } else { @@ -1421,7 +1436,7 @@ public class Mappings { (d.addedModifiers() & Opcodes.ACC_PUBLIC) > 0 || (d.addedModifiers() & Opcodes.ACC_ABSTRACT) > 0) { debug("Added final, public or abstract specifier --- affecting subclasses"); - myFuture.affectSubclasses(it.name, myAffectedFiles, state.myAffectedUsages, state.myDependants, false, myCompiledFiles); + myFuture.affectSubclasses(it.name, myAffectedFiles, state.myAffectedUsages, state.myDependants, false, myCompiledFiles, null); } if ((d.addedModifiers() & Opcodes.ACC_PROTECTED) > 0 && !((d.removedModifiers() & Opcodes.ACC_PRIVATE) > 0)) { @@ -1717,7 +1732,7 @@ public class Mappings { debug("Extends changed: ", extendsChanged); debug("Interfaces removed: ", interfacesRemoved); - myFuture.affectSubclasses(changedClass.name, myAffectedFiles, state.myAffectedUsages, state.myDependants, extendsChanged || interfacesRemoved || signatureChanged, myCompiledFiles); + myFuture.affectSubclasses(changedClass.name, myAffectedFiles, state.myAffectedUsages, state.myDependants, extendsChanged || interfacesRemoved || signatureChanged, myCompiledFiles, null); if (!changedClass.isAnonymous()) { final TIntHashSet parents = new TIntHashSet();