mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
javac ast indices: javac class processing order may be differ from by-file order (no tests)
This commit is contained in:
+85
-44
@@ -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();
|
||||
}
|
||||
}
|
||||
+31
@@ -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")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user