javac ast indices: javac class processing order may be differ from by-file order (no tests)

This commit is contained in:
Dmitry Batkovich
2016-11-08 16:57:40 +03:00
parent 784de3c3f4
commit 9b36c15308
6 changed files with 159 additions and 44 deletions
@@ -15,6 +15,7 @@
*/
package org.jetbrains.jps.javac.ast;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ReflectionUtil;
import com.sun.source.tree.Tree;
import com.sun.source.util.JavacTask;
@@ -24,6 +25,7 @@ import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.util.ClientCodeException;
import com.sun.tools.javac.util.Name;
import gnu.trove.THashMap;
import gnu.trove.THashSet;
import org.jetbrains.jps.javac.ast.api.JavacFileReferencesRegistrar;
import org.jetbrains.jps.javac.ast.api.JavacRefSymbol;
@@ -33,9 +35,7 @@ import javax.lang.model.element.TypeElement;
import javax.tools.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.*;
final class JavacReferenceCollectorListener implements TaskListener {
private final JavacFileReferencesRegistrar[] myFullASTListeners;
@@ -44,10 +44,7 @@ final class JavacReferenceCollectorListener implements TaskListener {
private Name myAsterisk;
private int myRemainDeclarations;
private JCTree.JCCompilationUnit myCurrentCompilationUnit;
private Set<JavacRefSymbol> myCollectedReferences;
private List<JavacRefSymbol> myCollectedDefinitions;
private final Map<String, IncompletelyProcessedFile> myIncompletelyProcessedFiles = new THashMap<String, IncompletelyProcessedFile>(10);
static void installOn(JavaCompiler.CompilationTask task,
JavacFileReferencesRegistrar[] fullASTListenerArray,
@@ -87,57 +84,83 @@ final class JavacReferenceCollectorListener implements TaskListener {
try {
if (e.getKind() == TaskEvent.Kind.ANALYZE) {
// javac creates an event on each processed top level declared class not file
if (myCurrentCompilationUnit != e.getCompilationUnit()) {
myCurrentCompilationUnit = (JCTree.JCCompilationUnit)e.getCompilationUnit();
myCollectedDefinitions = new ArrayList<JavacRefSymbol>();
myCollectedReferences = new THashSet<JavacRefSymbol>();
myRemainDeclarations = myCurrentCompilationUnit.getTypeDecls().size() - 1;
scanImports(myCurrentCompilationUnit, myCollectedReferences);
for(JavacFileReferencesRegistrar r: myOnlyImportsListeners) {
r.registerFile(e.getSourceFile().getName(), myCollectedReferences, myCollectedDefinitions);
}
}
else {
myRemainDeclarations--;
final JCTree.JCCompilationUnit unit = (JCTree.JCCompilationUnit)e.getCompilationUnit();
final String fileName = e.getSourceFile().getName();
final List<JCTree> declarations = unit.getTypeDecls();
final int size = declarations.size();
boolean isFileDataComplete = true;
boolean submitImportsOnlyData = true;
JCTree declarationToProcess = null;
final Set<JavacRefSymbol> collectedReferences;
final List<JavacRefSymbol> collectedDefinitions;
final TypeElement analyzedElement = e.getTypeElement();
switch (size) {
case 0:
collectedReferences = IncompletelyProcessedFile.createReferenceHolder();
collectedDefinitions = IncompletelyProcessedFile.createDefinitionHolder();
break;
case 1:
collectedReferences = IncompletelyProcessedFile.createReferenceHolder();
collectedDefinitions = IncompletelyProcessedFile.createDefinitionHolder();
declarationToProcess = declarations.get(0);
break;
default:
IncompletelyProcessedFile incompletelyProcessedFile = myIncompletelyProcessedFiles.get(fileName);
if (incompletelyProcessedFile == null) {
myIncompletelyProcessedFiles.put(fileName, incompletelyProcessedFile = new IncompletelyProcessedFile(size));
} else {
submitImportsOnlyData = false;
}
if (--incompletelyProcessedFile.remainDeclarations == 0) {
myIncompletelyProcessedFiles.remove(fileName);
} else {
isFileDataComplete = false;
}
collectedReferences = incompletelyProcessedFile.collectedReferences;
collectedDefinitions = incompletelyProcessedFile.collectedDefinitions;
for (JCTree declaration : declarations) {
if (declaration.type != null && declaration.type.tsym == analyzedElement) {
declarationToProcess = declaration;
break;
}
}
if (declarationToProcess == null) throw new IllegalStateException("Can't find tree for " + analyzedElement.getQualifiedName());
}
if (submitImportsOnlyData) {
scanImports(unit, collectedReferences);
for (JavacFileReferencesRegistrar r : myOnlyImportsListeners) {
r.registerFile(fileName, collectedReferences, collectedDefinitions);
}
}
if (myFullASTListeners.length == 0) return;
JavacTreeScannerSink sink = new JavacTreeScannerSink() {
@Override
public void sinkReference(JavacRefSymbol ref) {
myCollectedReferences.add(ref);
collectedReferences.add(ref);
}
@Override
public void sinkDeclaration(JavacRefSymbol def) {
myCollectedDefinitions.add(def);
collectedDefinitions.add(def);
}
};
myAstScanner.scan(declarationToProcess, sink);
if (myFullASTListeners.length != 0) {
TypeElement analyzedElement = e.getTypeElement();
for (JCTree tree : myCurrentCompilationUnit.getTypeDecls()) {
if (tree.type != null && tree.type.tsym == analyzedElement) {
myAstScanner.scan(tree, sink);
}
if (isFileDataComplete) {
for (JCTree.JCAnnotation annotation : unit.getPackageAnnotations()) {
myAstScanner.scan(annotation, sink);
}
for (JavacFileReferencesRegistrar r : myFullASTListeners) {
r.registerFile(e.getSourceFile().getName(), collectedReferences, collectedDefinitions);
}
}
if (myRemainDeclarations == 0) {
if (myFullASTListeners.length != 0) {
for (JCTree.JCAnnotation annotation : myCurrentCompilationUnit.getPackageAnnotations()) {
myAstScanner.scan(annotation, sink);
}
}
for(JavacFileReferencesRegistrar r: myFullASTListeners) {
r.registerFile(e.getSourceFile().getName(), myCollectedReferences, myCollectedDefinitions);
}
myCurrentCompilationUnit = null;
myCollectedDefinitions = null;
myCollectedReferences = null;
}
}
}
catch (Exception ex) {
@@ -186,4 +209,22 @@ final class JavacReferenceCollectorListener implements TaskListener {
collector.add(new JavacRefSymbol(symbol, Tree.Kind.IMPORT));
}
}
private static class IncompletelyProcessedFile {
private final Set<JavacRefSymbol> collectedReferences = createReferenceHolder();
private final List<JavacRefSymbol> collectedDefinitions = createDefinitionHolder();
private int remainDeclarations;
private IncompletelyProcessedFile(int remainDeclarations) {
this.remainDeclarations = remainDeclarations;
}
private static Set<JavacRefSymbol> createReferenceHolder() {
return new THashSet<JavacRefSymbol>();
}
private static List<JavacRefSymbol> createDefinitionHolder() {
return new ArrayList<JavacRefSymbol>();
}
}
}
@@ -0,0 +1,13 @@
class Bar0 {
void mm() {
Boo1 b = new Boo1();
b.mm();
}
}
class Bar1 {
void mm() {
Foo0 f = new Foo0();
f.mm();
}
}
@@ -0,0 +1,13 @@
class Boo0 {
void mm() {
Foo1 f = new Foo1();
f.mm();
}
}
class Boo1 {
void mm() {
Bar1 b = new Bar1();
b.mm();
}
}
@@ -0,0 +1,13 @@
class Foo0 {
void mm() {
Boo0 b = new Boo0();
b.mm();
}
}
class Foo1 {
void mm() {
Bar0 b = new Bar0();
b.mm();
}
}
@@ -0,0 +1,31 @@
Backward Hierarchy:
java.lang.Object -> Bar0 Bar1 Boo0 Boo1 Foo0 Foo1
Backward References:
Bar0 in Bar Foo
Bar0.<init>(0) in Bar
Bar0.mm(0) in Bar Foo
Bar1 in Bar Boo
Bar1.<init>(0) in Bar
Bar1.mm(0) in Bar Boo
Boo0 in Boo Foo
Boo0.<init>(0) in Boo
Boo0.mm(0) in Boo Foo
Boo1 in Bar Boo
Boo1.<init>(0) in Boo
Boo1.mm(0) in Bar Boo
Foo0 in Bar Foo
Foo0.<init>(0) in Foo
Foo0.mm(0) in Bar Foo
Foo1 in Boo Foo
Foo1.<init>(0) in Foo
Foo1.mm(0) in Boo Foo
java.lang.Object.<init>(0) in Bar Boo Foo
Class Definitions:
Bar0 in Bar
Bar1 in Bar
Boo0 in Boo
Boo1 in Boo
Foo0 in Foo
Foo1 in Foo
@@ -94,6 +94,10 @@ class ReferenceIndexTest : ReferenceIndexTestBase() {
assertIndexOnRebuild("Foo.java")
}
fun testMultiFileMultiUnitCompilation() {
assertIndexOnRebuild("Foo.java", "Boo.java", "Bar.java")
}
fun testNestedClasses() {
assertIndexOnRebuild("Foo.java")
}