From 2e2d02473df352f37a66a6cbde4666fae77337d6 Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Mon, 10 Aug 2015 11:54:35 +0200 Subject: [PATCH] when parsing signatures, create GenericBoundUsage only for classes mentioned in signature bounds; mark dependencies caused by GenericBoundUsage only in chunk being compiled and all chunks that depend on it recursively --- .../incremental/generics/changeExtends.log | 4 +- .../generics/changeExtends/Super2.java.new | 3 + .../generics/changeExtends/src/Client4.java | 2 + .../generics/changeExtends/src/Super2.java | 3 + .../dependencyView/ClassfileAnalyzer.java | 118 +++++++++++------- .../java/dependencyView/Mappings.java | 82 ++++++++++-- 6 files changed, 158 insertions(+), 54 deletions(-) create mode 100644 java/java-tests/testData/compileServer/incremental/generics/changeExtends/Super2.java.new create mode 100644 java/java-tests/testData/compileServer/incremental/generics/changeExtends/src/Client4.java create mode 100644 java/java-tests/testData/compileServer/incremental/generics/changeExtends/src/Super2.java diff --git a/java/java-tests/testData/compileServer/incremental/generics/changeExtends.log b/java/java-tests/testData/compileServer/incremental/generics/changeExtends.log index 0cba040f482a..34c270510a8b 100644 --- a/java/java-tests/testData/compileServer/incremental/generics/changeExtends.log +++ b/java/java-tests/testData/compileServer/incremental/generics/changeExtends.log @@ -1,8 +1,10 @@ Cleaning output files: out/production/ChangeExtends/Super.class +out/production/ChangeExtends/Super2.class End of files Compiling files: src/Super.java +src/Super2.java End of files Cleaning output files: out/production/ChangeExtends/Client.class @@ -13,4 +15,4 @@ Compiling files: src/Client.java src/Client2.java src/Client3.java -End of files +End of files \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/generics/changeExtends/Super2.java.new b/java/java-tests/testData/compileServer/incremental/generics/changeExtends/Super2.java.new new file mode 100644 index 000000000000..55dd08a7fffb --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/generics/changeExtends/Super2.java.new @@ -0,0 +1,3 @@ +class Super2 { + char s = 's'; +} \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/generics/changeExtends/src/Client4.java b/java/java-tests/testData/compileServer/incremental/generics/changeExtends/src/Client4.java new file mode 100644 index 000000000000..9c68d9aab48c --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/generics/changeExtends/src/Client4.java @@ -0,0 +1,2 @@ +class Client4 extends Hyper{ +} \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/generics/changeExtends/src/Super2.java b/java/java-tests/testData/compileServer/incremental/generics/changeExtends/src/Super2.java new file mode 100644 index 000000000000..0045f0cb5ca6 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/generics/changeExtends/src/Super2.java @@ -0,0 +1,3 @@ +class Super2 extends Hyper { + char s = 's'; +} \ No newline at end of file diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ClassfileAnalyzer.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ClassfileAnalyzer.java index 2b11ac6bd546..c0d00b8b058c 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ClassfileAnalyzer.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ClassfileAnalyzer.java @@ -217,61 +217,21 @@ class ClassfileAnalyzer { } } - private final SignatureVisitor mySignatureCrawler = new SignatureVisitor(Opcodes.ASM5) { - public void visitFormalTypeParameter(String name) { - } - + private final SignatureVisitor mySignatureCrawler = new BaseSignatureVisitor() { public SignatureVisitor visitClassBound() { - return this; + return mySignatureWithGenericBoundUsageCrawler; } public SignatureVisitor visitInterfaceBound() { - return this; - } - - public SignatureVisitor visitSuperclass() { - return this; - } - - public SignatureVisitor visitInterface() { - return this; - } - - public SignatureVisitor visitParameterType() { - return this; - } - - public SignatureVisitor visitReturnType() { - return this; - } - - public SignatureVisitor visitExceptionType() { - return this; - } - - public void visitBaseType(char descriptor) { - } - - public void visitTypeVariable(String name) { - } - - public SignatureVisitor visitArrayType() { - return this; - } - - public void visitInnerClassType(String name) { - } - - public void visitTypeArgument() { + return mySignatureWithGenericBoundUsageCrawler; } public SignatureVisitor visitTypeArgument(char wildcard) { - return this; - } - - public void visitEnd() { + return mySignatureWithGenericBoundUsageCrawler; } + }; + private final SignatureVisitor mySignatureWithGenericBoundUsageCrawler = new BaseSignatureVisitor() { public void visitClassType(String name) { final int className = myContext.get(name); myUsages.add(UsageRepr.createClassUsage(myContext, className)); @@ -653,6 +613,72 @@ class ClassfileAnalyzer { myLocalClassFlag.set(true); } } + + private class BaseSignatureVisitor extends SignatureVisitor { + + public BaseSignatureVisitor() { + super(Opcodes.ASM5); + } + + public void visitFormalTypeParameter(String name) { + } + + public SignatureVisitor visitClassBound() { + return this; + } + + public SignatureVisitor visitInterfaceBound() { + return this; + } + + public SignatureVisitor visitSuperclass() { + return this; + } + + public SignatureVisitor visitInterface() { + return this; + } + + public SignatureVisitor visitParameterType() { + return this; + } + + public SignatureVisitor visitReturnType() { + return this; + } + + public SignatureVisitor visitExceptionType() { + return this; + } + + public void visitBaseType(char descriptor) { + } + + public void visitTypeVariable(String name) { + } + + public SignatureVisitor visitArrayType() { + return this; + } + + public void visitInnerClassType(String name) { + } + + public void visitTypeArgument() { + } + + public SignatureVisitor visitTypeArgument(char wildcard) { + return this; + } + + public void visitEnd() { + } + + public void visitClassType(String name) { + final int className = myContext.get(name); + myUsages.add(UsageRepr.createClassUsage(myContext, className)); + } + } } public Pair> analyze(final int fileName, final ClassReader cr) { 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 dd87660bcd6b..583406b372d2 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 @@ -716,6 +716,28 @@ public class Mappings { public abstract boolean checkResidence(final int residence); } + public class FileFilterConstraint extends UsageConstraint { + @NotNull + private final DependentFilesFilter myFilter; + + public FileFilterConstraint(@NotNull DependentFilesFilter filter) { + myFilter = filter; + } + + public boolean checkResidence(int residence) { + final Collection fNames = myClassToSourceFile.get(residence); + if (fNames == null || fNames.isEmpty()) { + return true; + } + for (File fName : fNames) { + if (myFilter.accept(fName)) { + return true; + } + } + return false; + } + } + public class PackageConstraint extends UsageConstraint { public final String packageName; @@ -1132,6 +1154,11 @@ public class Mappings { debug("Class is annotation, skipping method analysis"); return; } + + assert myFuture != null; + assert myPresent != null; + assert myAffectedFiles != null; + Ref oldItRef = null; for (final MethodRepr m : added) { debug("Method: ", m.name); @@ -1256,6 +1283,10 @@ public class Mappings { if (removed.isEmpty()) { return; } + assert myFuture != null; + assert myAffectedFiles != null; + assert myCompiledFiles != null; + debug("Processing removed methods:"); for (final MethodRepr m : removed) { debug("Method ", m.name); @@ -1362,6 +1393,10 @@ public class Mappings { return; } debug("Processing changed methods:"); + + assert myFuture != null; + assert myAffectedFiles != null; + for (final Pair mr : changed) { final MethodRepr m = mr.first; final MethodRepr.Diff d = (MethodRepr.Diff)mr.second; @@ -1473,6 +1508,11 @@ public class Mappings { } debug("Processing added fields"); + assert myFuture != null; + assert myPresent != null; + assert myCompiledFiles != null; + assert myAffectedFiles != null; + for (final FieldRepr f : added) { debug("Field: ", f.name); @@ -1535,23 +1575,23 @@ public class Mappings { // nothing } else { - Util.UsageConstraint constaint; + Util.UsageConstraint constraint; if ((ff.isProtected() && f.isPublic()) || (f.isProtected() && ff.isPublic()) || (ff.isPackageLocal() && f.isProtected())) { - constaint = myFuture.new NegationConstraint(myFuture.new InheritanceConstraint(cc.name)); + constraint = myFuture.new NegationConstraint(myFuture.new InheritanceConstraint(cc.name)); } else if (ff.isPublic() && ff.isPackageLocal()) { - constaint = myFuture.new NegationConstraint(myFuture.new PackageConstraint(cc.getPackageName())); + constraint = myFuture.new NegationConstraint(myFuture.new PackageConstraint(cc.getPackageName())); } else { - constaint = + constraint = myFuture.new IntersectionConstraint(myFuture.new NegationConstraint(myFuture.new InheritanceConstraint(cc.name)), myFuture.new NegationConstraint( myFuture.new PackageConstraint(cc.getPackageName()))); } for (final UsageRepr.Usage usage : localUsages) { - state.myUsageConstraints.put(usage, constaint); + state.myUsageConstraints.put(usage, constraint); } } @@ -1569,6 +1609,8 @@ public class Mappings { if (removed.isEmpty()) { return true; } + assert myFuture != null; + debug("Processing removed fields:"); for (final FieldRepr f : removed) { @@ -1577,6 +1619,7 @@ public class Mappings { if (!f.isPrivate() && (f.access & DESPERATE_MASK) == DESPERATE_MASK && f.hasValue()) { debug("Field had value and was (non-private) final static => a switch to non-incremental mode requested"); if (myConstantSearch != null) { + assert myDelayedWorks != null; myDelayedWorks.addConstantWork(it.name, f, true, false); } else { @@ -1601,6 +1644,7 @@ public class Mappings { return true; } debug("Processing changed fields:"); + assert myFuture != null; for (final Pair f : changed) { final Difference d = f.second; @@ -1619,6 +1663,7 @@ public class Mappings { if (harmful || valueChanged || becameLessAccessible) { debug("Inline field changed it's access or value => a switch to non-incremental mode requested"); if (myConstantSearch != null) { + assert myDelayedWorks != null; myDelayedWorks.addConstantWork(it.name, field, false, accessChanged); } else { @@ -1687,6 +1732,10 @@ public class Mappings { final Collection> changedClasses = state.myClassDiff.changed(); if (!changedClasses.isEmpty()) { debug("Processing changed classes:"); + assert myFuture != null; + assert myPresent != null; + + final Util.FileFilterConstraint fileFilterConstraint = myFilter != null? myPresent.new FileFilterConstraint(myFilter) : null; for (final Pair changed : changedClasses) { final ClassRepr changedClass = changed.first; @@ -1753,7 +1802,11 @@ public class Mappings { @Override public boolean execute(int className) { debug("Affecting usages in generic type parameter bounds of class: ", className); - state.myAffectedUsages.add(UsageRepr.createClassAsGenericBoundUsage(myContext, className)); + final UsageRepr.Usage usage = UsageRepr.createClassAsGenericBoundUsage(myContext, className); + state.myAffectedUsages.add(usage); + if (fileFilterConstraint != null) { + state.myUsageConstraints.put(usage, fileFilterConstraint); + } final TIntHashSet depClasses = myClassToClassDependency.get(className); if (depClasses != null) { @@ -1869,6 +1922,9 @@ public class Mappings { if (removed.isEmpty()) { return; } + assert myPresent != null; + assert myDelta.myChangedFiles != null; + myDelta.myChangedFiles.add(fileName); debug("Processing removed classes:"); @@ -1896,6 +1952,9 @@ public class Mappings { if (!myEasyMode && myFilter != null) { // checking if this newly added class duplicates already existing one + assert myCompiledFiles != null; + assert myAffectedFiles != null; + for (ClassRepr c : addedClasses) { if (!c.isLocal() && !c.isAnonymous() && isEmpty(c.getOuterClassName())) { final Set candidates = new THashSet(FileUtil.FILE_HASHING_STRATEGY); @@ -1976,6 +2035,8 @@ public class Mappings { } private void affectCorrespondingSourceFiles(TIntHashSet toAffect) { + assert myAffectedFiles != null; + toAffect.forEach(new TIntProcedure() { @Override public boolean execute(int depClass) { @@ -1995,6 +2056,8 @@ public class Mappings { private void calculateAffectedFiles(final DiffState state) { debug("Checking dependent classes:"); + assert myAffectedFiles != null; + assert myCompiledFiles != null; state.myDependants.forEach(new TIntProcedure() { @Override @@ -2106,6 +2169,8 @@ public class Mappings { if (myEasyMode) { return false; } + assert myAffectedFiles != null; + assert myDelayedWorks != null; final Collection removed = myDelta.myRemovedFiles; if (removed != null) { @@ -2117,6 +2182,7 @@ public class Mappings { } finally { if (myFilesToCompile != null) { + assert myDelta.myChangedFiles != null; // if some class is associated with several sources, // some of them may not have been compiled in this round, so such files should be considered unchanged myDelta.myChangedFiles.retainAll(myFilesToCompile); @@ -2463,7 +2529,9 @@ public class Mappings { } for (final String s : staticImports) { int i = s.length() - 1; - for (; s.charAt(i) != '.'; i--) ; + while (s.charAt(i) != '.') { + i--; + } final String anImport = s.substring(0, i); if (!anImport.endsWith("*")) { allImports.add(anImport); // filter out wildcard imports