javac ast refs index idea/163.5030
authorDmitry Batkovich <dmitry.batkovich@jetbrains.com>
Thu, 22 Sep 2016 16:07:38 +0000 (19:07 +0300)
committerDmitry Batkovich <dmitry.batkovich@jetbrains.com>
Thu, 22 Sep 2016 16:07:38 +0000 (19:07 +0300)
76 files changed:
build/scripts/layouts.gant
java/compiler/impl/src/com/intellij/compiler/CompilerReferenceConverter.java [new file with mode: 0644]
java/compiler/impl/src/com/intellij/compiler/CompilerReferenceIndexBuildParametersProvider.java [new file with mode: 0644]
java/compiler/impl/src/com/intellij/compiler/CompilerReferenceReader.java [new file with mode: 0644]
java/compiler/impl/src/com/intellij/compiler/CompilerReferenceServiceImpl.java [new file with mode: 0644]
java/compiler/impl/src/com/intellij/compiler/JavaCompilerReferenceConverter.java [new file with mode: 0644]
java/java-tests/testData/compiler/bytecodeReferences/testIsNotReady/Foo.java [new file with mode: 0644]
java/java-tests/testData/compiler/bytecodeReferences/testSimpleUsagesInFullyCompiledProject/Bar.java [new file with mode: 0644]
java/java-tests/testData/compiler/bytecodeReferences/testSimpleUsagesInFullyCompiledProject/Baz.java [new file with mode: 0644]
java/java-tests/testData/compiler/bytecodeReferences/testSimpleUsagesInFullyCompiledProject/Foo.java [new file with mode: 0644]
java/java-tests/testData/compiler/bytecodeReferences/testSimpleUsagesInFullyCompiledProject/FooImpl.java [new file with mode: 0644]
java/java-tests/testSrc/com/intellij/codeInsight/completion/AbstractCompilerAwareTest.java
java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesTest.java [new file with mode: 0644]
jps/javac-ref-scanner-8/javac-ref-scanner-8.iml [new file with mode: 0644]
jps/javac-ref-scanner-8/src/org/jetbrains/jps/javac/ast/Javac8RefScanner.java [new file with mode: 0644]
jps/javac-ref-scanner-8/test/org/jetbrains/references/Java8ReferenceIndexTest.kt [new file with mode: 0644]
jps/javac-ref-scanner-8/testData/referenceIndex/lambda/Lambda.java [new file with mode: 0644]
jps/javac-ref-scanner-8/testData/referenceIndex/lambda/initialIndex.txt [new file with mode: 0644]
jps/javac-ref-scanner-8/testData/referenceIndex/methodReference/MethodReference.java [new file with mode: 0644]
jps/javac-ref-scanner-8/testData/referenceIndex/methodReference/initialIndex.txt [new file with mode: 0644]
jps/jps-builders/src/META-INF/services/org.jetbrains.jps.javac.ast.api.JavacFileReferencesRegistrar [new file with mode: 0644]
jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceIndexBuilder.java [new file with mode: 0644]
jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceIndexWriter.java [new file with mode: 0644]
jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceRegistrar.java [new file with mode: 0644]
jps/jps-builders/src/org/jetbrains/jps/backwardRefs/ByteArrayEnumerator.java [new file with mode: 0644]
jps/jps-builders/src/org/jetbrains/jps/backwardRefs/CompilerBackwardReferenceIndex.java [new file with mode: 0644]
jps/jps-builders/src/org/jetbrains/jps/backwardRefs/CompilerElement.java [new file with mode: 0644]
jps/jps-builders/src/org/jetbrains/jps/backwardRefs/LightUsage.java [new file with mode: 0644]
jps/jps-builders/src/org/jetbrains/jps/builders/impl/java/JavacCompilerTool.java
jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/IntIntPersistentMultiMaplet.java
jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/IntObjectPersistentMultiMaplet.java
jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ObjectObjectMultiMaplet.java
jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ObjectObjectPersistentMultiMaplet.java
jps/jps-builders/src/org/jetbrains/jps/cmdline/ClasspathBootstrap.java
jps/jps-builders/src/org/jetbrains/jps/incremental/JavaBuilderService.java
jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java
jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacReferencesCollector.java [new file with mode: 0644]
jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacTreeRefScanner.java [new file with mode: 0644]
jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacTreeScannerSink.java [new file with mode: 0644]
jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacFileReferencesRegistrar.java [new file with mode: 0644]
jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacRefSymbol.java [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/arrayRefs/Array.java [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/arrayRefs/Bar.java [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/arrayRefs/Foo.java [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/arrayRefs/initialIndex.txt [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/deadCode/Bar.java [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/deadCode/initialIndex.txt [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/fileCaseOnlyRename/Bar.java [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/fileCaseOnlyRename/afterRename.txt [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/fileCaseOnlyRename/initialIndex.txt [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/incrementalIndexUpdate/Bar.java [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/incrementalIndexUpdate/Bar_1.java [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/incrementalIndexUpdate/Foo.java [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/incrementalIndexUpdate/FooImpl.java [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/incrementalIndexUpdate/FooImpl_2.java [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/incrementalIndexUpdate/afterMakeIndex.txt [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/incrementalIndexUpdate/afterSecondMakeIndex.txt [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/incrementalIndexUpdate/initialIndex.txt [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/packageInfo/initialIndex.txt [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/packageInfo/myPackage/package-info.java [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/typeParameterRefs/TypeParam.java [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/typeParameterRefs/initialIndex.txt [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/unusedImports/Bar.java [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/unusedImports/initialIndex.txt [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/unusedImports2/Main.java [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/unusedImports2/com/ru/Some.java [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/unusedImports2/com/ru/Some2.java [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/unusedImports2/com/ru/Some3.java [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/unusedImports2/initialIndex.txt [new file with mode: 0644]
jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTest.kt [new file with mode: 0644]
jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTestBase.kt [new file with mode: 0644]
platform/indexing-impl/src/com/intellij/find/bytecode/CompilerReferenceScopeOptimizer.java [new file with mode: 0644]
platform/indexing-impl/src/com/intellij/find/bytecode/CompilerReferenceService.java [new file with mode: 0644]
platform/util/resources/misc/registry.properties
resources/src/META-INF/IdeaPlugin.xml
resources/src/idea/RichPlatformPlugin.xml

index 5d1482bd0116412bfc8c62551f390386ead8a4c8..765dcf5e09dd213a3b1e847e563d7cc5ca2d832d 100644 (file)
@@ -208,6 +208,7 @@ def layoutFull(BuildContext context) {
         module("forms_rt")
         module("instrumentation-util")
         module("instrumentation-util-8")
+        module("javac-ref-scanner-8")
       }
 
       jar("jps-model.jar") {
@@ -886,6 +887,7 @@ def layoutJps(String home, String targetDir, String buildNumber, Closure additio
         module("forms-compiler")
         module("instrumentation-util")
         module("instrumentation-util-8")
+        module("javac-ref-scanner-8")
         module("jps-builders")
         module("jps-standalone-builder")
         module("java-runtime")
diff --git a/java/compiler/impl/src/com/intellij/compiler/CompilerReferenceConverter.java b/java/compiler/impl/src/com/intellij/compiler/CompilerReferenceConverter.java
new file mode 100644 (file)
index 0000000..0d4742d
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.compiler;
+
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.psi.PsiElement;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.jps.backwardRefs.CompilerElement;
+
+public interface CompilerReferenceConverter {
+  @NotNull
+  FileType getAvailabilitySrcFileType();
+
+  @Nullable
+  CompilerElement sourceElementAsCompilerElement(@NotNull PsiElement element);
+
+  @NotNull
+  CompilerElement[] libraryElementAsCompilerElements(@NotNull PsiElement element);
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/CompilerReferenceIndexBuildParametersProvider.java b/java/compiler/impl/src/com/intellij/compiler/CompilerReferenceIndexBuildParametersProvider.java
new file mode 100644 (file)
index 0000000..09dc96e
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.compiler;
+
+import com.intellij.compiler.server.BuildProcessParametersProvider;
+import com.intellij.find.bytecode.CompilerReferenceService;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.jps.backwardRefs.BackwardReferenceIndexWriter;
+
+import java.util.Collections;
+import java.util.List;
+
+public class CompilerReferenceIndexBuildParametersProvider extends BuildProcessParametersProvider {
+  @NotNull
+  @Override
+  public List<String> getVMArguments() {
+    return CompilerReferenceService.isEnabled()
+           ? Collections.singletonList("-D" + BackwardReferenceIndexWriter.PROP_KEY + "=true")
+           : Collections.emptyList();
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/compiler/CompilerReferenceReader.java b/java/compiler/impl/src/com/intellij/compiler/CompilerReferenceReader.java
new file mode 100644 (file)
index 0000000..f2cd212
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.compiler;
+
+import com.intellij.compiler.server.BuildManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileWithId;
+import com.intellij.util.containers.Queue;
+import gnu.trove.TIntHashSet;
+import gnu.trove.TIntProcedure;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.jps.backwardRefs.CompilerElement;
+import org.jetbrains.jps.backwardRefs.CompilerBackwardReferenceIndex;
+import org.jetbrains.jps.backwardRefs.LightUsage;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+
+public class CompilerReferenceReader {
+  private final CompilerBackwardReferenceIndex myIndex;
+
+  private CompilerReferenceReader(File buildDir) throws IOException {
+    myIndex = new CompilerBackwardReferenceIndex(buildDir);
+  }
+
+  @NotNull
+  public TIntHashSet findReferentFileIds(@NotNull CompilerElement element) {
+    LightUsage usage = element.asUsage(myIndex.getByteSeqEum());
+
+    TIntHashSet set = new TIntHashSet();
+    for (int classId : getWholeHierarchy(usage.getOwner())) {
+      final LightUsage overriderUsage = usage.override(classId);
+      final Collection<Integer> usageFiles = myIndex.getBackwardReferenceMap().get(overriderUsage);
+      if (usageFiles != null) {
+        for (int fileId : usageFiles) {
+          final VirtualFile file = findFile(fileId);
+          if (file != null) {
+            set.add(((VirtualFileWithId)file).getId());
+          }
+        }
+      }
+    }
+    return set;
+  }
+
+  public void close() {
+    myIndex.close();
+  }
+
+  public static CompilerReferenceReader create(Project project) {
+    File buildDir = BuildManager.getInstance().getProjectSystemDirectory(project);
+    if (buildDir == null || CompilerBackwardReferenceIndex.versionDiffers(buildDir)) {
+      return null;
+    }
+    try {
+      return new CompilerReferenceReader(buildDir);
+    }
+    catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  private VirtualFile findFile(int id) {
+    try {
+      String path = myIndex.getFilePathEnumerator().valueOf(id);
+      assert path != null;
+      return VfsUtil.findFileByIoFile(new File(path), false);
+    }
+    catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  private int[] getWholeHierarchy(int classId) {
+    TIntHashSet result = new TIntHashSet();
+    Queue<Integer> q = new Queue<>(10);
+    q.addLast(classId);
+    while (!q.isEmpty()) {
+      int curId = q.pullFirst();
+      if (result.add(curId)) {
+        final TIntHashSet subclasses = myIndex.getBackwardHierarchyMap().get(curId);
+        if (subclasses != null) {
+          subclasses.forEach(new TIntProcedure() {
+            @Override
+            public boolean execute(int nextId) {
+              q.addLast(nextId);
+              return true;
+            }
+          });
+        }
+      }
+    }
+    return result.toArray();
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/compiler/CompilerReferenceServiceImpl.java b/java/compiler/impl/src/com/intellij/compiler/CompilerReferenceServiceImpl.java
new file mode 100644 (file)
index 0000000..5bb7b15
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.compiler;
+
+import com.intellij.find.bytecode.CompilerReferenceService;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompileTask;
+import com.intellij.openapi.compiler.CompilerManager;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.vfs.*;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.search.ProjectScope;
+import com.intellij.psi.util.CachedValueProvider;
+import com.intellij.psi.util.CachedValuesManager;
+import com.intellij.psi.util.PsiModificationTracker;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.indexing.FileBasedIndex;
+import gnu.trove.THashSet;
+import gnu.trove.TIntHashSet;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.TestOnly;
+import org.jetbrains.jps.backwardRefs.CompilerElement;
+
+import java.util.Collections;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class CompilerReferenceServiceImpl extends CompilerReferenceService {
+  private static final CompilerReferenceConverter[] BYTECODE_CONVERTERS =
+    new CompilerReferenceConverter[]{new JavaCompilerReferenceConverter()};
+
+  private final ProjectFileIndex myProjectFileIndex;
+  private final Set<Module> myChangedModules = ContainerUtil.newConcurrentSet();
+  private final Set<FileType> myFileTypes;
+
+  private volatile CompilerReferenceReader myReader;
+
+  private final Object myLock = new Object();
+
+  public CompilerReferenceServiceImpl(Project project) {
+    super(project);
+    myProjectFileIndex = ProjectRootManager.getInstance(project).getFileIndex();
+    myFileTypes = Collections.unmodifiableSet(
+      Stream.of(BYTECODE_CONVERTERS).map(CompilerReferenceConverter::getAvailabilitySrcFileType).collect(Collectors.toSet()));
+  }
+
+  @Override
+  public void projectOpened() {
+    if (isEnabled()) {
+      CompilerManager.getInstance(myProject).addBeforeTask(new CompileTask() {
+        @Override
+        public boolean execute(CompileContext context) {
+          closeReaderIfNeed();
+          return true;
+        }
+      });
+      CompilerManager.getInstance(myProject).addAfterTask(new CompileTask() {
+        @Override
+        public boolean execute(CompileContext context) {
+          myChangedModules.clear();
+          openReaderIfNeed();
+          return true;
+        }
+      });
+    }
+
+    VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileAdapter() {
+      @Override
+      public void fileCreated(@NotNull VirtualFileEvent event) {
+        processChange(event.getFile());
+      }
+
+      @Override
+      public void fileCopied(@NotNull VirtualFileCopyEvent event) {
+        processChange(event.getFile());
+      }
+
+      @Override
+      public void fileMoved(@NotNull VirtualFileMoveEvent event) {
+        processChange(event.getFile());
+      }
+
+      @Override
+      public void beforePropertyChange(@NotNull VirtualFilePropertyEvent event) {
+        if (VirtualFile.PROP_NAME.equals(event.getPropertyName()) || VirtualFile.PROP_SYMLINK_TARGET.equals(event.getPropertyName())) {
+          processChange(event.getFile());
+        }
+      }
+
+      @Override
+      public void beforeContentsChange(@NotNull VirtualFileEvent event) {
+        processChange(event.getFile());
+      }
+
+      @Override
+      public void beforeFileDeletion(@NotNull VirtualFileEvent event) {
+        processChange(event.getFile());
+      }
+
+      @Override
+      public void beforeFileMovement(@NotNull VirtualFileMoveEvent event) {
+        processChange(event.getFile());
+      }
+
+      private void processChange(VirtualFile file) {
+        if (myReader != null && myProjectFileIndex.isInSourceContent(file) && myFileTypes.contains(file.getFileType())) {
+          final Module module = myProjectFileIndex.getModuleForFile(file);
+          if (module != null) {
+            myChangedModules.add(module);
+          }
+        }
+      }
+    }, myProject);
+  }
+
+  @Override
+  public void projectClosed() {
+    closeReaderIfNeed();
+  }
+
+  @Override
+  public GlobalSearchScope getScopeWithoutReferences(@NotNull PsiElement element) {
+    if (!isServiceEnabled()) return null;
+    return CachedValuesManager.getCachedValue(element, () -> CachedValueProvider.Result.create(calculateScopeWithoutReferences(element),
+                                                                                               PsiModificationTracker.MODIFICATION_COUNT));
+  }
+
+  private boolean isServiceEnabled() {
+    return myReader != null && isEnabled();
+  }
+
+  @Nullable
+  private GlobalSearchScope calculateScopeWithoutReferences(@NotNull PsiElement element) {
+    TIntHashSet referentFileIds = getReferentFileIds(element);
+    if (referentFileIds == null) return null;
+
+    final GlobalSearchScope everythingIsClearScope = GlobalSearchScope
+      .getScopeRestrictedByFileTypes(ProjectScope.getContentScope(myProject), myFileTypes.toArray(new FileType[myFileTypes.size()]));
+    return new ScopeWithoutBytecodeReferences(referentFileIds).intersectWith(everythingIsClearScope);
+  }
+
+  @Nullable
+  private TIntHashSet getReferentFileIds(@NotNull PsiElement element) {
+    final PsiFile file = element.getContainingFile();
+    if (file == null) return null;
+    final VirtualFile vFile = file.getVirtualFile();
+    if (vFile == null) return null;
+
+    ElementPlace place = ElementPlace.get(vFile, myProjectFileIndex);
+    if (place == null) {
+      return null;
+    }
+    if (!myChangedModules.isEmpty()) {
+      final Module module = myProjectFileIndex.getModuleForFile(vFile);
+      if (module == null || areDependenciesOrSelfChanged(module, new THashSet<>())) {
+        return null;
+      }
+    }
+    final FileType type = vFile.getFileType();
+    CompilerElement[] compilerElements = null;
+    if (place == ElementPlace.SRC) {
+      for (CompilerReferenceConverter converter : BYTECODE_CONVERTERS) {
+        if (converter.getAvailabilitySrcFileType().equals(type)) {
+          final CompilerElement compilerElement = converter.sourceElementAsCompilerElement(element);
+          compilerElements = compilerElement == null ? CompilerElement.EMPTY_ARRAY : new CompilerElement[]{compilerElement};
+          break;
+        }
+      }
+    }
+    else {
+      for (CompilerReferenceConverter converter : BYTECODE_CONVERTERS) {
+        compilerElements = converter.libraryElementAsCompilerElements(element);
+        if (compilerElements.length != 0) {
+          break;
+        }
+      }
+    }
+    if (compilerElements == null || compilerElements.length == 0) return null;
+
+    synchronized (myLock) {
+      if (myReader == null) return null;
+      TIntHashSet referentFileIds = new TIntHashSet();
+      for (CompilerElement compilerElement : compilerElements) {
+        referentFileIds.addAll(myReader.findReferentFileIds(compilerElement).toArray());
+      }
+      return referentFileIds;
+    }
+  }
+
+  private void closeReaderIfNeed() {
+    synchronized (myLock) {
+      if (myReader != null) {
+        myReader.close();
+        myReader = null;
+      }
+    }
+  }
+
+  private void openReaderIfNeed() {
+    synchronized (myLock) {
+      if (myProject.isOpen()) {
+        myReader = CompilerReferenceReader.create(myProject);
+      }
+    }
+  }
+
+  @TestOnly
+  @Nullable
+  public Set<VirtualFile> getReferentFiles(@NotNull PsiElement element) {
+    FileBasedIndex fileIndex = FileBasedIndex.getInstance();
+    final TIntHashSet ids = getReferentFileIds(element);
+    if (ids == null) return null;
+    Set<VirtualFile> fileSet = new THashSet<>();
+    ids.forEach(id -> {
+      final VirtualFile vFile = fileIndex.findFileById(myProject, id);
+      assert vFile != null;
+      fileSet.add(vFile);
+      return true;
+    });
+    return fileSet;
+  }
+
+  private enum ElementPlace {
+    SRC, LIB;
+
+    private static ElementPlace get(VirtualFile file, ProjectFileIndex index) {
+      return index.isInSourceContent(file) ? SRC :
+             ((index.isInLibrarySource(file) || index.isInLibraryClasses(file)) ? LIB : null);
+    }
+  }
+
+  private static class ScopeWithoutBytecodeReferences extends GlobalSearchScope {
+    private final TIntHashSet myReferentIds;
+
+    private ScopeWithoutBytecodeReferences(TIntHashSet ids) {
+      myReferentIds = ids;
+    }
+
+    @Override
+    public boolean contains(@NotNull VirtualFile file) {
+      return !(file instanceof VirtualFileWithId) || !myReferentIds.contains(((VirtualFileWithId)file).getId());
+    }
+
+    @Override
+    public int compare(@NotNull VirtualFile file1, @NotNull VirtualFile file2) {
+      return 0;
+    }
+
+    @Override
+    public boolean isSearchInModuleContent(@NotNull Module aModule) {
+      return true;
+    }
+
+    @Override
+    public boolean isSearchInLibraries() {
+      return false;
+    }
+  }
+
+  private boolean areDependenciesOrSelfChanged(Module module, THashSet<Module> uniqueModules) {
+    if (!uniqueModules.add(module)) {
+      return false;
+    }
+    if (myChangedModules.contains(module)) {
+      return true;
+    }
+    for (Module dependency : ModuleRootManager.getInstance(module).getDependencies()) {
+      if (areDependenciesOrSelfChanged(dependency, uniqueModules)) {
+        return true;
+      }
+    }
+    return false;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/JavaCompilerReferenceConverter.java b/java/compiler/impl/src/com/intellij/compiler/JavaCompilerReferenceConverter.java
new file mode 100644 (file)
index 0000000..f1645b4
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.compiler;
+
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.fileTypes.StdFileTypes;
+import com.intellij.openapi.roots.impl.LibraryScopeCache;
+import com.intellij.psi.*;
+import com.intellij.psi.search.searches.ClassInheritorsSearch;
+import com.intellij.psi.util.ClassUtil;
+import com.intellij.psi.util.PsiUtil;
+import com.intellij.util.Processor;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.jps.backwardRefs.CompilerElement;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Function;
+
+public class JavaCompilerReferenceConverter implements CompilerReferenceConverter {
+  @NotNull
+  @Override
+  public FileType getAvailabilitySrcFileType() {
+    return StdFileTypes.JAVA;
+  }
+
+  @Nullable
+  @Override
+  public CompilerElement sourceElementAsCompilerElement(@NotNull PsiElement element) {
+    if (mayBeVisibleOutsideOwnerFile(element)) {
+      if (element instanceof PsiField) {
+        final PsiField field = (PsiField)element;
+        final PsiClass aClass = field.getContainingClass();
+        if (PsiUtil.isCompileTimeConstant((PsiVariable)field)) {
+          return null;
+        }
+        if (aClass == null || aClass instanceof PsiAnonymousClass) return null;
+        final String jvmOwnerName = aClass.getQualifiedName();
+        final String name = field.getName();
+        if (name == null || jvmOwnerName == null) return null;
+        return new CompilerElement.CompilerField(jvmOwnerName, name);
+      }
+      else if (element instanceof PsiMethod) {
+        final PsiClass aClass = ((PsiMethod)element).getContainingClass();
+        if (aClass == null || aClass instanceof PsiAnonymousClass) return null;
+        final String jvmOwnerName = aClass.getQualifiedName();
+        if (jvmOwnerName == null) return null;
+        final PsiMethod method = (PsiMethod)element;
+        final String name = method.isConstructor() ? "<init>" : method.getName();
+        final int parametersCount = method.getParameterList().getParametersCount();
+        return new CompilerElement.CompilerMethod(jvmOwnerName, name, parametersCount);
+      }
+      else if (element instanceof PsiClass) {
+        final String jvmClassName = ClassUtil.getJVMClassName((PsiClass)element);
+        if (jvmClassName != null) {
+          return new CompilerElement.CompilerClass(jvmClassName);
+        }
+      }
+    }
+    return null;
+  }
+
+  @NotNull
+  @Override
+  public CompilerElement[] libraryElementAsCompilerElements(@NotNull PsiElement element) {
+    if (mayBeVisibleOutsideOwnerFile(element)) {
+      if (element instanceof PsiField || element instanceof PsiMethod) {
+        final String name = ((PsiMember)element).getName();
+
+        final Function<String, CompilerElement> builder;
+        if (element instanceof PsiField) {
+          builder = (ownerJvmName) -> new CompilerElement.CompilerField(ownerJvmName, name);
+        }
+        else {
+          final int parametersCount = ((PsiMethod)element).getParameterList().getParametersCount();
+          builder = (ownerJvmName) -> new CompilerElement.CompilerMethod(ownerJvmName, name, parametersCount);
+        }
+
+        final List<CompilerElement> result = new ArrayList<>();
+        inLibrariesHierarchy(((PsiMember)element).getContainingClass(), aClass -> {
+          final String jvmClassName = aClass.getQualifiedName();
+          if (jvmClassName != null) {
+            result.add(builder.apply(jvmClassName));
+          }
+          return true;
+        });
+        return result.toArray(new CompilerElement[result.size()]);
+      }
+      else if (element instanceof PsiClass) {
+        final List<CompilerElement> result = new ArrayList<>();
+        inLibrariesHierarchy((PsiClass)element, aClass -> {
+          final String jvmClassName = aClass.getQualifiedName();
+          if (jvmClassName != null) {
+            result.add(new CompilerElement.CompilerClass(jvmClassName));
+          }
+          return true;
+        });
+        return result.toArray(new CompilerElement[result.size()]);
+      }
+    }
+    return CompilerElement.EMPTY_ARRAY;
+  }
+
+  private static boolean mayBeVisibleOutsideOwnerFile(@NotNull PsiElement element) {
+    if (!(element instanceof PsiModifierListOwner)) return true;
+    if (((PsiModifierListOwner)element).hasModifierProperty(PsiModifier.PRIVATE)) return false;
+    return true;
+  }
+
+  private static void inLibrariesHierarchy(PsiClass aClass, Processor<PsiClass> processor) {
+    if (aClass != null) {
+      processor.process(aClass);
+      ClassInheritorsSearch.search(aClass, LibraryScopeCache.getInstance(aClass.getProject()).getLibrariesOnlyScope(), true)
+        .forEach(processor);
+    }
+  }
+}
diff --git a/java/java-tests/testData/compiler/bytecodeReferences/testIsNotReady/Foo.java b/java/java-tests/testData/compiler/bytecodeReferences/testIsNotReady/Foo.java
new file mode 100644 (file)
index 0000000..1bcf9f3
--- /dev/null
@@ -0,0 +1,7 @@
+public class Foo {
+
+  public void b<caret>ar() {
+
+  }
+
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/compiler/bytecodeReferences/testSimpleUsagesInFullyCompiledProject/Bar.java b/java/java-tests/testData/compiler/bytecodeReferences/testSimpleUsagesInFullyCompiledProject/Bar.java
new file mode 100644 (file)
index 0000000..baa4792
--- /dev/null
@@ -0,0 +1,8 @@
+public class Bar {
+
+  public String getMyName() {
+    return "Bar...";
+  };
+
+
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/compiler/bytecodeReferences/testSimpleUsagesInFullyCompiledProject/Baz.java b/java/java-tests/testData/compiler/bytecodeReferences/testSimpleUsagesInFullyCompiledProject/Baz.java
new file mode 100644 (file)
index 0000000..fccbed9
--- /dev/null
@@ -0,0 +1,5 @@
+public class Baz {
+  public void main(FooImpl f) {
+    System.out.println(f.getMyName());
+  }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/compiler/bytecodeReferences/testSimpleUsagesInFullyCompiledProject/Foo.java b/java/java-tests/testData/compiler/bytecodeReferences/testSimpleUsagesInFullyCompiledProject/Foo.java
new file mode 100644 (file)
index 0000000..707a997
--- /dev/null
@@ -0,0 +1,5 @@
+public interface Foo {
+
+  String getM<caret>yName();
+
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/compiler/bytecodeReferences/testSimpleUsagesInFullyCompiledProject/FooImpl.java b/java/java-tests/testData/compiler/bytecodeReferences/testSimpleUsagesInFullyCompiledProject/FooImpl.java
new file mode 100644 (file)
index 0000000..35c48e6
--- /dev/null
@@ -0,0 +1,7 @@
+public class FooImpl implements Foo {
+
+  public String getMyName() {
+    return "FooImpl is my name";
+  };
+
+}
\ No newline at end of file
index d460c73baee2c438476466db86b1cab179a52e06..6fbfa5224a0cd17f9cb66c9aad8c3b3053b1d209 100644 (file)
@@ -56,6 +56,10 @@ public abstract class AbstractCompilerAwareTest extends JavaCodeInsightFixtureTe
     catch (IOException e) {
       throw new RuntimeException(e);
     }
+    rebuildProject();
+  }
+
+  protected void rebuildProject() {
     for (final CompilerMessage compilerMessage : myCompilerTester.rebuild()) {
       assertNotSame(compilerMessage.getMessage(), CompilerMessageCategory.ERROR, compilerMessage.getCategory());
     }
diff --git a/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesTest.java b/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesTest.java
new file mode 100644 (file)
index 0000000..9841dea
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.compiler;
+
+import com.intellij.JavaTestUtil;
+import com.intellij.codeInsight.completion.AbstractCompilerAwareTest;
+import com.intellij.find.bytecode.CompilerReferenceService;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiMember;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.testFramework.SkipSlowTestLocally;
+import com.intellij.util.containers.ContainerUtil;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@SkipSlowTestLocally
+public class CompilerReferencesTest extends AbstractCompilerAwareTest {
+  private boolean myDefaultEnableState;
+
+  @Override
+  public void setUp() throws Exception {
+    myDefaultEnableState = CompilerReferenceService.IS_ENABLED_KEY.asBoolean();
+    CompilerReferenceService.IS_ENABLED_KEY.setValue(true);
+    super.setUp();
+  }
+
+  @Override
+  public void tearDown() throws Exception {
+    CompilerReferenceService.IS_ENABLED_KEY.setValue(myDefaultEnableState);
+    super.tearDown();
+  }
+
+  protected String getTestDataPath() {
+    return JavaTestUtil.getJavaTestDataPath() + "/compiler/bytecodeReferences/";
+  }
+
+  public void testIsNotReady() {
+    myFixture.configureByFile(getName() + "/Foo.java");
+    assertNull(getReferentFilesForElementUnderCaret());
+  }
+
+  public void testSimpleUsagesInFullyCompiledProject() {
+    myFixture.configureByFiles(getName() + "/Foo.java", getName() + "/Bar.java", getName() + "/Baz.java", getName() + "/FooImpl.java");
+    rebuildProject();
+
+    final Set<VirtualFile> referents = getReferentFilesForElementUnderCaret();
+    assertNotNull(referents);
+    final Set<String> filesWithReferences = referents.stream().map(VirtualFile::getName).collect(Collectors.toSet());
+
+    assertEquals(filesWithReferences, ContainerUtil.set("Baz.java", "Foo.java", "FooImpl.java"));
+    myFixture.addFileToProject("SomeModification.java", "");
+    assertNull(getReferentFilesForElementUnderCaret());
+  }
+
+  private Set<VirtualFile> getReferentFilesForElementUnderCaret() {
+    final PsiElement atCaret = myFixture.getElementAtCaret();
+    assertNotNull(atCaret);
+    final PsiMember memberAtCaret = PsiTreeUtil.getParentOfType(atCaret, PsiMember.class, false);
+    assertNotNull(memberAtCaret);
+    return ((CompilerReferenceServiceImpl)CompilerReferenceService.getInstance(myFixture.getProject())).getReferentFiles(memberAtCaret);
+  }
+}
diff --git a/jps/javac-ref-scanner-8/javac-ref-scanner-8.iml b/jps/javac-ref-scanner-8/javac-ref-scanner-8.iml
new file mode 100644 (file)
index 0000000..e87665d
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="module" module-name="jps-builders" />
+    <orderEntry type="library" scope="TEST" name="KotlinJavaRuntime" level="project" />
+    <orderEntry type="module" module-name="core-api" scope="TEST" />
+    <orderEntry type="module" module-name="testFramework" scope="TEST" />
+  </component>
+</module>
\ No newline at end of file
diff --git a/jps/javac-ref-scanner-8/src/org/jetbrains/jps/javac/ast/Javac8RefScanner.java b/jps/javac-ref-scanner-8/src/org/jetbrains/jps/javac/ast/Javac8RefScanner.java
new file mode 100644 (file)
index 0000000..52a8f17
--- /dev/null
@@ -0,0 +1,33 @@
+package org.jetbrains.jps.javac.ast;
+
+import com.sun.source.tree.LambdaExpressionTree;
+import com.sun.source.tree.MemberReferenceTree;
+import com.sun.source.tree.Tree;
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.tree.JCTree;
+import org.jetbrains.jps.javac.ast.api.JavacRefSymbol;
+
+/**
+ * Used via reflection in {@link JavacTreeRefScanner#createASTScanner()}
+ */
+@SuppressWarnings("unused")
+public class Javac8RefScanner extends JavacTreeRefScanner {
+  @Override
+  public Tree visitLambdaExpression(LambdaExpressionTree node, JavacTreeScannerSink sink) {
+    final Type type = ((JCTree.JCLambda)node).type;
+    final Symbol.TypeSymbol symbol = type.asElement();
+    sink.sinkReference(new JavacRefSymbol(symbol, Tree.Kind.LAMBDA_EXPRESSION));
+    //for (Symbol member : symbol.members().getElements()) {
+    //  sink.mySymbols.add(new JavacRefSymbol(member, Tree.Kind.LAMBDA_EXPRESSION));
+    //}
+    return super.visitLambdaExpression(node, sink);
+  }
+
+  @Override
+  public Tree visitMemberReference(MemberReferenceTree node, JavacTreeScannerSink sink) {
+    final Symbol methodSymbol = ((JCTree.JCMemberReference)node).sym;
+    sink.sinkReference(new JavacRefSymbol(methodSymbol, Tree.Kind.MEMBER_REFERENCE));
+    return super.visitMemberReference(node, sink);
+  }
+}
diff --git a/jps/javac-ref-scanner-8/test/org/jetbrains/references/Java8ReferenceIndexTest.kt b/jps/javac-ref-scanner-8/test/org/jetbrains/references/Java8ReferenceIndexTest.kt
new file mode 100644 (file)
index 0000000..24466fa
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.references
+
+import com.intellij.openapi.application.ex.PathManagerEx
+import com.intellij.openapi.util.io.FileUtil
+
+class Java8ReferenceIndexTest : ReferenceIndexTestBase() {
+  fun testLambda() {
+    assertIndexOnRebuild("Lambda.java")
+  }
+
+  fun testMethodReference() {
+    assertIndexOnRebuild("MethodReference.java")
+  }
+
+  override fun getTestDataRootPath(): String {
+    return FileUtil.toCanonicalPath(PathManagerEx.findFileUnderCommunityHome("jps/javac-ref-scanner-8/testData/referenceIndex").absolutePath, '/')
+  }
+}
+
+
diff --git a/jps/javac-ref-scanner-8/testData/referenceIndex/lambda/Lambda.java b/jps/javac-ref-scanner-8/testData/referenceIndex/lambda/Lambda.java
new file mode 100644 (file)
index 0000000..23db4d7
--- /dev/null
@@ -0,0 +1,9 @@
+public class Lambda {
+  public static void main(String[] args) {
+    new Thread(() -> {
+
+      //Do nothing
+
+    });
+  }
+}
\ No newline at end of file
diff --git a/jps/javac-ref-scanner-8/testData/referenceIndex/lambda/initialIndex.txt b/jps/javac-ref-scanner-8/testData/referenceIndex/lambda/initialIndex.txt
new file mode 100644 (file)
index 0000000..8aa7b1c
--- /dev/null
@@ -0,0 +1,11 @@
+Backward Hierarchy:
+java.lang.Object -> Lambda
+
+Backward References:
+Lambda in Lambda
+Lambda.<init>(0) in Lambda
+Lambda.main(1) in Lambda
+java.lang.Object.<init>(0) in Lambda
+java.lang.Runnable in Lambda
+java.lang.String in Lambda
+java.lang.Thread in Lambda
\ No newline at end of file
diff --git a/jps/javac-ref-scanner-8/testData/referenceIndex/methodReference/MethodReference.java b/jps/javac-ref-scanner-8/testData/referenceIndex/methodReference/MethodReference.java
new file mode 100644 (file)
index 0000000..dc12e79
--- /dev/null
@@ -0,0 +1,7 @@
+import java.util.Collections;
+
+public class MethodReference {
+  public static void main(String[] args) {
+    new Thread(Collections::emptyList);
+  }
+}
\ No newline at end of file
diff --git a/jps/javac-ref-scanner-8/testData/referenceIndex/methodReference/initialIndex.txt b/jps/javac-ref-scanner-8/testData/referenceIndex/methodReference/initialIndex.txt
new file mode 100644 (file)
index 0000000..4acfc9f
--- /dev/null
@@ -0,0 +1,12 @@
+Backward Hierarchy:
+java.lang.Object -> MethodReference
+
+Backward References:
+MethodReference in MethodReference
+MethodReference.<init>(0) in MethodReference
+MethodReference.main(1) in MethodReference
+java.lang.Object.<init>(0) in MethodReference
+java.lang.String in MethodReference
+java.lang.Thread in MethodReference
+java.util.Collections in MethodReference
+java.util.Collections.emptyList(0) in MethodReference
\ No newline at end of file
diff --git a/jps/jps-builders/src/META-INF/services/org.jetbrains.jps.javac.ast.api.JavacFileReferencesRegistrar b/jps/jps-builders/src/META-INF/services/org.jetbrains.jps.javac.ast.api.JavacFileReferencesRegistrar
new file mode 100644 (file)
index 0000000..25736bf
--- /dev/null
@@ -0,0 +1,16 @@
+#
+# Copyright 2000-2016 JetBrains s.r.o.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+org.jetbrains.jps.backwardRefs.BackwardReferenceRegistrar
diff --git a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceIndexBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceIndexBuilder.java
new file mode 100644 (file)
index 0000000..7dc22f6
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.jps.backwardRefs;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.jps.ModuleChunk;
+import org.jetbrains.jps.builders.DirtyFilesHolder;
+import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor;
+import org.jetbrains.jps.incremental.*;
+
+import java.io.IOException;
+
+public class BackwardReferenceIndexBuilder extends ModuleLevelBuilder {
+  public BackwardReferenceIndexBuilder() {
+    super(BuilderCategory.INITIAL);
+  }
+
+  @NotNull
+  @Override
+  public String getPresentableName() {
+    return "backward-references indexer";
+  }
+
+  @Override
+  public void buildStarted(CompileContext context) {
+    BackwardReferenceIndexWriter.initialize(context);
+  }
+
+  @Override
+  public void buildFinished(CompileContext context) {
+    final BackwardReferenceIndexWriter writer = BackwardReferenceIndexWriter.getInstance();
+    if (writer != null) {
+      writer.closeIfNeed();
+    }
+  }
+
+  @Override
+  public ExitCode build(CompileContext context,
+                        ModuleChunk chunk,
+                        DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder,
+                        OutputConsumer outputConsumer) throws ProjectBuildException, IOException {
+    return null;
+  }
+}
diff --git a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceIndexWriter.java b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceIndexWriter.java
new file mode 100644 (file)
index 0000000..2d56e34
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.jps.backwardRefs;
+
+import com.intellij.util.SystemProperties;
+import com.sun.tools.javac.code.Symbol;
+import gnu.trove.TIntHashSet;
+import gnu.trove.TIntProcedure;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.jps.builders.java.JavaBuilderUtil;
+import org.jetbrains.jps.builders.storage.BuildDataCorruptedException;
+import org.jetbrains.jps.incremental.CompileContext;
+import org.jetbrains.jps.incremental.java.JavaBuilder;
+import org.jetbrains.jps.incremental.storage.BuildDataManager;
+import org.jetbrains.jps.javac.ast.api.JavacRefSymbol;
+import org.jetbrains.jps.model.java.compiler.JavaCompilers;
+
+import javax.tools.*;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Set;
+
+public class BackwardReferenceIndexWriter {
+  public static final String PROP_KEY = "ref.index.builder";
+
+  private static volatile BackwardReferenceIndexWriter ourInstance;
+
+  private final CompilerBackwardReferenceIndex myIndex;
+  private final boolean myRebuild;
+
+  private BackwardReferenceIndexWriter(CompilerBackwardReferenceIndex index, boolean rebuild) {
+    myIndex = index;
+    myRebuild = rebuild;
+  }
+
+  static BackwardReferenceIndexWriter getInstance() {
+    return ourInstance;
+  }
+
+  static void initialize(@NotNull CompileContext context) {
+    if (isEnabled()) {
+      final BuildDataManager dataManager = context.getProjectDescriptor().dataManager;
+      final File buildDir = dataManager.getDataPaths().getDataStorageRoot();
+      boolean isRebuild = JavaBuilderUtil.isForcedRecompilationAllJavaModules(context);
+
+      if (!JavaCompilers.JAVAC_ID.equals(JavaBuilder.getUsedCompilerId(context))) {
+        CompilerBackwardReferenceIndex.removeIndexFiles(buildDir);
+        return;
+      }
+      if (isRebuild) {
+        CompilerBackwardReferenceIndex.removeIndexFiles(buildDir);
+      }
+      else if (CompilerBackwardReferenceIndex.versionDiffers(buildDir)) {
+        throw new BuildDataCorruptedException(new IOException("backward reference index should be updated to actual version"));
+      }
+
+      if (CompilerBackwardReferenceIndex.exist(buildDir) || isRebuild) {
+        ourInstance = new BackwardReferenceIndexWriter(new CompilerBackwardReferenceIndex(buildDir), isRebuild);
+      }
+    }
+  }
+
+  static boolean isEnabled() {
+    return SystemProperties.getBooleanProperty(PROP_KEY, false);
+  }
+
+  void closeIfNeed() {
+    myIndex.close();
+  }
+
+  synchronized void writeReferences(JavaFileObject file, Set<JavacRefSymbol> refs) {
+    final LightUsage[] usages = new LightUsage[refs.size()];
+    int idx = 0;
+    final ByteArrayEnumerator byteSeqEum = myIndex.getByteSeqEum();
+    for (JavacRefSymbol ref : refs) {
+      usages[idx++] = LightUsage.fromSymbol(ref.getSymbol(), byteSeqEum);
+    }
+
+    final int fileId = enumerateFile(file);
+    if (myRebuild) {
+      for (LightUsage usage : usages) {
+        myIndex.getBackwardReferenceMap().put(usage, fileId);
+        myIndex.getReferenceMap().put(fileId, usage);
+      }
+    }
+    else {
+      updateReferenceIndicesIncrementally(fileId, usages);
+    }
+  }
+
+  private void updateReferenceIndicesIncrementally(int fileId, LightUsage[] usages) {
+    final Collection<LightUsage> rawOldUsages = myIndex.getReferenceMap().get(fileId);
+    Collection<LightUsage> oldUsages = rawOldUsages == null ? null : new ArrayList<LightUsage>(rawOldUsages);
+    for (LightUsage usage : usages) {
+      if (oldUsages == null || !oldUsages.remove(usage)) {
+        myIndex.getBackwardReferenceMap().put(usage, fileId);
+        myIndex.getReferenceMap().put(fileId, usage);
+      }
+    }
+    if (oldUsages != null && !oldUsages.isEmpty()) {
+      myIndex.getReferenceMap().removeAll(fileId, oldUsages);
+      for (LightUsage usage : oldUsages) {
+        myIndex.getBackwardReferenceMap().removeFrom(usage, fileId);
+      }
+    }
+  }
+
+  synchronized void writeHierarchy(Symbol name, Symbol[] supers) {
+    if (supers.length == 0) {
+      return;
+    }
+    final int classId = classId(name);
+    final int[] superIds = new int[supers.length];
+    for (int i = 0; i < supers.length; i++) {
+      superIds[i] = classId(supers[i]);
+    }
+
+    if (myRebuild) {
+      directlyWriteHierarchyIndices(classId, superIds);
+    }
+    else {
+      updateHierarchyIndicesIncrementally(classId, superIds);
+    }
+  }
+
+  private void directlyWriteHierarchyIndices(int classId, int[] superIds) {
+    for (int superId : superIds) {
+      myIndex.getBackwardHierarchyMap().put(superId, classId);
+      myIndex.getHierarchyMap().put(classId, superId);
+    }
+  }
+
+  private void updateHierarchyIndicesIncrementally(final int classId, int[] superIds) {
+    final TIntHashSet rawOldSupers = myIndex.getHierarchyMap().get(classId);
+    TIntHashSet oldSuperClasses = rawOldSupers == null ? null : new TIntHashSet(rawOldSupers);
+    for (int superId: superIds) {
+      if (oldSuperClasses == null || !oldSuperClasses.remove(superId)) {
+        myIndex.getBackwardHierarchyMap().put(superId, classId);
+        myIndex.getHierarchyMap().put(classId, superId);
+      }
+    }
+    if (oldSuperClasses != null && !oldSuperClasses.isEmpty()) {
+      myIndex.getHierarchyMap().removeAll(classId, oldSuperClasses);
+      oldSuperClasses.forEach(new TIntProcedure() {
+        @Override
+        public boolean execute(int oldSuperId) {
+          myIndex.getBackwardHierarchyMap().put(oldSuperId, classId);
+          return true;
+        }
+      });
+    }
+  }
+
+  private int classId(Symbol name) {
+    return myIndex.getByteSeqEum().enumerate(LightUsage.bytes(name));
+  }
+
+  private int enumerateFile(JavaFileObject file) {
+    try {
+      return myIndex.getFilePathEnumerator().enumerate(file.getName());
+    }
+    catch (IOException e) {
+      throw new BuildDataCorruptedException(e);
+    }
+  }
+}
+
+
diff --git a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceRegistrar.java b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceRegistrar.java
new file mode 100644 (file)
index 0000000..b589c76
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.jps.backwardRefs;
+
+import com.sun.tools.javac.code.Symbol;
+import org.jetbrains.jps.javac.ast.api.JavacFileReferencesRegistrar;
+import org.jetbrains.jps.javac.ast.api.JavacRefSymbol;
+
+import javax.tools.*;
+import java.util.Set;
+
+public class BackwardReferenceRegistrar implements JavacFileReferencesRegistrar {
+  private BackwardReferenceIndexWriter myWriter;
+
+  @Override
+  public boolean initialize() {
+    myWriter = BackwardReferenceIndexWriter.getInstance();
+    return myWriter != null;
+  }
+
+  @Override
+  public boolean onlyImports() {
+    return false;
+  }
+
+  @Override
+  public void registerReferences(JavaFileObject file, Set<JavacRefSymbol> refs) {
+    myWriter.writeReferences(file, refs);
+  }
+
+  @Override
+  public void registerClassDeclaration(Symbol className, Symbol[] supers) {
+    myWriter.writeHierarchy(className, supers);
+  }
+}
diff --git a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/ByteArrayEnumerator.java b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/ByteArrayEnumerator.java
new file mode 100644 (file)
index 0000000..6b0e38c
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.jps.backwardRefs;
+
+import com.intellij.util.io.*;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.jps.builders.storage.BuildDataCorruptedException;
+
+import java.io.*;
+import java.util.Arrays;
+
+public class ByteArrayEnumerator extends PersistentEnumeratorDelegate<byte[]> {
+  @Nullable private final CachingEnumerator<byte[]> myCache;
+
+  public ByteArrayEnumerator(@NotNull final File file) throws IOException {
+    super(file, ByteSequenceDataExternalizer.INSTANCE, 1024 * 4, null);
+    myCache = new CachingEnumerator<byte[]>(new DataEnumerator<byte[]>() {
+      @Override
+      public int enumerate(@Nullable byte[] value) throws IOException {
+        return ByteArrayEnumerator.super.enumerate(value);
+      }
+
+      @Nullable
+      @Override
+      public byte[] valueOf(int idx) throws IOException {
+        return ByteArrayEnumerator.super.valueOf(idx);
+      }
+    }, ByteSequenceDataExternalizer.INSTANCE);
+  }
+
+  @Override
+  public int enumerate(@Nullable byte[] value) {
+    try {
+      return myCache != null ? myCache.enumerate(value) : super.enumerate(value);
+    }
+    catch (IOException e) {
+      throw new BuildDataCorruptedException(e);
+    }
+  }
+
+  @Nullable
+  @Override
+  public byte[] valueOf(int idx) throws IOException {
+    return myCache != null ? myCache.valueOf(idx) : super.valueOf(idx);
+  }
+
+  @Override
+  public void close() throws IOException {
+    super.close();
+    if (myCache != null) myCache.close();
+  }
+
+
+  private static class ByteSequenceDataExternalizer implements KeyDescriptor<byte[]>, DifferentSerializableBytesImplyNonEqualityPolicy {
+    private static final ByteSequenceDataExternalizer INSTANCE = new ByteSequenceDataExternalizer();
+
+    @Override
+    public void save(@NotNull DataOutput out, byte[] value) throws IOException {
+      out.writeInt(value.length);
+      out.write(value);
+    }
+
+    @Override
+    public byte[] read(@NotNull DataInput in) throws IOException {
+      final int len = in.readInt();
+      final byte[] buf = new byte[len];
+      in.readFully(buf);
+      return buf;
+    }
+
+    @Override
+    public int getHashCode(byte[] value) {
+      return Arrays.hashCode(value);
+    }
+
+    @Override
+    public boolean isEqual(byte[] val1, byte[] val2) {
+      return Arrays.equals(val1, val2);
+    }
+  }
+}
+
diff --git a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/CompilerBackwardReferenceIndex.java b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/CompilerBackwardReferenceIndex.java
new file mode 100644 (file)
index 0000000..5d96f6d
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.jps.backwardRefs;
+
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.util.io.EnumeratorIntegerDescriptor;
+import com.intellij.util.io.PersistentStringEnumerator;
+import gnu.trove.THashSet;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.jps.builders.java.dependencyView.*;
+import org.jetbrains.jps.builders.storage.BuildDataCorruptedException;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+
+public class CompilerBackwardReferenceIndex {
+  private static final int VERSION = 0;
+  private final static String FILE_ENUM_TAB = "file.path.enum.tab";
+  private final static String INCOMPLETE_FILES_TAB = "incomplete.files.tab";
+  private final static String USAGES_TAB = "refs.tab";
+  private final static String BACK_USAGES_TAB = "back.refs.tab";
+  private final static String HIERARCHY_TAB = "hierarchy.tab";
+  private final static String BACK_HIERARCHY_TAB = "back.hierarchy.tab";
+  public static final String VERSION_FILE = ".version";
+
+  private final IntObjectPersistentMultiMaplet<LightUsage> myReferenceMap;
+  private final IntIntPersistentMultiMaplet myHierarchyMap;
+
+  private final ObjectObjectPersistentMultiMaplet<LightUsage, Integer> myBackwardReferenceMap;
+  private final IntIntPersistentMultiMaplet myBackwardHierarchyMap;
+
+  private final ByteArrayEnumerator myNameEnumerator;
+  private final PersistentStringEnumerator myFilePathEnumerator;
+
+  private final File myIndicesDir;
+
+
+  public CompilerBackwardReferenceIndex(File buildDir) {
+    myIndicesDir = getIndexDir(buildDir);
+    if (!myIndicesDir.exists() && !myIndicesDir.mkdirs()) {
+      throw new RuntimeException("Can't create dir: " + buildDir.getAbsolutePath());
+    }
+    try {
+      if (versionDiffers(buildDir)) {
+        FileUtil.writeToFile(new File(myIndicesDir, VERSION_FILE), String.valueOf(VERSION));
+      }
+      myFilePathEnumerator = new PersistentStringEnumerator(new File(myIndicesDir, FILE_ENUM_TAB));
+
+      myBackwardReferenceMap = new ObjectObjectPersistentMultiMaplet<LightUsage, Integer>(new File(myIndicesDir, BACK_USAGES_TAB),
+                                                                                          LightUsage.createDescriptor(),
+                                                                                          EnumeratorIntegerDescriptor.INSTANCE,
+                                                                                          new CollectionFactory<Integer>() {
+                                                                                        @Override
+                                                                                        public Collection<Integer> create() {
+                                                                                          return new THashSet<Integer>();
+                                                                                        }
+                                                                                      });
+      myBackwardHierarchyMap = new IntIntPersistentMultiMaplet(new File(myIndicesDir, BACK_HIERARCHY_TAB),
+                                                               EnumeratorIntegerDescriptor.INSTANCE);
+
+      myReferenceMap = new IntObjectPersistentMultiMaplet<LightUsage>(new File(myIndicesDir, USAGES_TAB),
+                                                                      EnumeratorIntegerDescriptor.INSTANCE,
+                                                                      LightUsage.createDescriptor(), new CollectionFactory<LightUsage>() {
+        @Override
+        public Collection<LightUsage> create() {
+          return new THashSet<LightUsage>();
+        }
+      });
+      myHierarchyMap = new IntIntPersistentMultiMaplet(new File(myIndicesDir, HIERARCHY_TAB), EnumeratorIntegerDescriptor.INSTANCE);
+
+      myNameEnumerator = new ByteArrayEnumerator(new File(myIndicesDir, INCOMPLETE_FILES_TAB));
+    }
+    catch (IOException e) {
+      removeIndexFiles(myIndicesDir);
+      throw new BuildDataCorruptedException(e);
+    }
+  }
+
+  @NotNull
+  public ObjectObjectPersistentMultiMaplet<LightUsage, Integer> getBackwardReferenceMap() {
+    return myBackwardReferenceMap;
+  }
+
+  @NotNull
+  public IntIntPersistentMultiMaplet getBackwardHierarchyMap() {
+    return myBackwardHierarchyMap;
+  }
+
+  @NotNull
+  public IntObjectPersistentMultiMaplet<LightUsage> getReferenceMap() {
+    return myReferenceMap;
+  }
+
+  @NotNull
+  public IntIntPersistentMultiMaplet getHierarchyMap() {
+    return myHierarchyMap;
+  }
+
+  @NotNull
+  public ByteArrayEnumerator getByteSeqEum() {
+    return myNameEnumerator;
+  }
+
+  @NotNull
+  public PersistentStringEnumerator getFilePathEnumerator() {
+    return myFilePathEnumerator;
+  }
+
+  public void close() {
+    try {
+      myFilePathEnumerator.close();
+      myBackwardHierarchyMap.close();
+      myBackwardReferenceMap.close();
+      myReferenceMap.close();
+      myHierarchyMap.close();
+      myNameEnumerator.close();
+    }
+    catch (IOException e) {
+      removeIndexFiles(myIndicesDir);
+      throw new BuildDataCorruptedException(e);
+    }
+  }
+
+  public static void removeIndexFiles(File buildDir) {
+    final File indexDir = getIndexDir(buildDir);
+    if (indexDir.exists()) {
+      FileUtil.delete(indexDir);
+    }
+  }
+
+  private static File getIndexDir(@NotNull File buildDir) {
+    return new File(buildDir, "backward-refs");
+  }
+
+  public static boolean exist(@NotNull File buildDir) {
+    return getIndexDir(buildDir).exists();
+  }
+
+  public static boolean versionDiffers(@NotNull File buildDir) {
+    File versionFile = new File(getIndexDir(buildDir), VERSION_FILE);
+    try {
+      return Integer.parseInt(FileUtil.loadFile(versionFile)) != VERSION;
+    }
+    catch (final IOException e) {
+      return true;
+    }
+  }
+}
diff --git a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/CompilerElement.java b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/CompilerElement.java
new file mode 100644 (file)
index 0000000..4e91a47
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.jps.backwardRefs;
+
+import com.sun.tools.javac.util.Convert;
+import org.jetbrains.annotations.NotNull;
+
+public abstract class CompilerElement {
+  public static final CompilerElement[] EMPTY_ARRAY = new CompilerElement[0];
+
+  @NotNull
+  public abstract LightUsage asUsage(ByteArrayEnumerator byteArrayEnumerator);
+
+  public static class CompilerMethod extends CompilerElement {
+    private final byte[] myJavacClassName;
+    private final byte[] myJavacMethodName;
+    private final int myJavacParameterCount;
+
+    public CompilerMethod(String javacClassName, String javacMethodName, int count) {
+      this(bytes(javacClassName), bytes(javacMethodName), count);
+    }
+
+    CompilerMethod(byte[] javacClassName, byte[] javacMethodName, int javacParameterCount) {
+      myJavacClassName = javacClassName;
+      myJavacMethodName = javacMethodName;
+      myJavacParameterCount = javacParameterCount;
+    }
+
+    @NotNull
+    @Override
+    public LightUsage asUsage(ByteArrayEnumerator byteArrayEnumerator) {
+      return new LightUsage.LightMethodUsage(byteArrayEnumerator.enumerate(myJavacClassName),
+                                             byteArrayEnumerator.enumerate(myJavacMethodName),
+                                             myJavacParameterCount);
+    }
+  }
+
+  public static class CompilerClass extends CompilerElement {
+    private final byte[] myJavacName;
+
+    public CompilerClass(String name) {
+      this(bytes(name));
+    }
+
+    CompilerClass(byte[] javacName) {
+      myJavacName = javacName;
+    }
+
+    @NotNull
+    @Override
+    public LightUsage asUsage(ByteArrayEnumerator byteArrayEnumerator) {
+      return new LightUsage.LightClassUsage(byteArrayEnumerator.enumerate(myJavacName));
+    }
+  }
+
+  public static class CompilerField extends CompilerElement {
+    private final byte[] myJavacClassName;
+    private final byte[] myJavacName;
+
+    public CompilerField(String javacClassName, String name) {
+      this(bytes(javacClassName), bytes(name));
+    }
+
+    CompilerField(byte[] javacClassName, byte[] javacName) {
+      myJavacClassName = javacClassName;
+      myJavacName = javacName;
+    }
+
+    @NotNull
+    @Override
+    public LightUsage asUsage(ByteArrayEnumerator byteArrayEnumerator) {
+      return new LightUsage.LightFieldUsage(byteArrayEnumerator.enumerate(myJavacClassName),
+                                            byteArrayEnumerator.enumerate(myJavacName));
+    }
+  }
+
+  static byte[] bytes(String str) {
+    //TODO
+    return Convert.string2utf(str);
+  }
+}
diff --git a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/LightUsage.java b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/LightUsage.java
new file mode 100644 (file)
index 0000000..4bd6eff
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.jps.backwardRefs;
+
+import com.intellij.util.io.DataInputOutputUtil;
+import com.intellij.util.io.KeyDescriptor;
+import com.sun.tools.javac.code.Symbol;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.jps.builders.java.dependencyView.RW;
+import org.jetbrains.jps.builders.storage.BuildDataCorruptedException;
+
+import javax.lang.model.element.ElementKind;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+public abstract class LightUsage implements RW.Savable {
+  private final static byte CLASS_MARKER = 0x0;
+  private final static byte METHOD_MARKER = 0x1;
+  private final static byte FIELD_MARKER = 0x2;
+
+  public final int myOwner;
+
+  protected LightUsage(int owner) {
+    myOwner = owner;
+  }
+
+  public int getOwner() {
+    return myOwner;
+  }
+
+  @NotNull
+  public abstract LightUsage override(int ownerOverrider);
+
+  public static class LightMethodUsage extends LightUsage {
+    private final int myName;
+    private final int myParameterCount;
+
+    LightMethodUsage(int owner, int name, int parameterCount) {
+      super(owner);
+      myName = name;
+      myParameterCount = parameterCount;
+    }
+
+    public int getName() {
+      return myName;
+    }
+
+    public int getParameterCount() {
+      return myParameterCount;
+    }
+
+    @Override
+    public void save(DataOutput out) {
+      try {
+        out.writeByte(METHOD_MARKER);
+        DataInputOutputUtil.writeINT(out, getOwner());
+        DataInputOutputUtil.writeINT(out, getName());
+        DataInputOutputUtil.writeINT(out, getParameterCount());
+      }
+      catch (IOException e) {
+        throw new BuildDataCorruptedException(e);
+      }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+
+      LightMethodUsage usage = (LightMethodUsage)o;
+
+      if (myOwner != usage.myOwner) return false;
+      if (myName != usage.myName) return false;
+      if (myParameterCount != usage.myParameterCount) return false;
+
+      return true;
+    }
+
+    @Override
+    public int hashCode() {
+      int result = myName;
+      result = 31 * result + myParameterCount;
+      result = 31 * result + myOwner;
+      return result;
+    }
+
+    @NotNull
+    @Override
+    public LightMethodUsage override(int ownerOverrider) {
+      return new LightMethodUsage(ownerOverrider, getName(), getParameterCount());
+    }
+  }
+
+  public static class LightFieldUsage extends LightUsage {
+    private final int myName;
+
+    LightFieldUsage(int owner, int name) {
+      super(owner);
+      myName = name;
+    }
+
+    public int getName() {
+      return myName;
+    }
+
+    @Override
+    public void save(DataOutput out) {
+      try {
+        out.writeByte(FIELD_MARKER);
+        DataInputOutputUtil.writeINT(out, getOwner());
+        DataInputOutputUtil.writeINT(out, getName());
+      }
+      catch (IOException e) {
+        throw new BuildDataCorruptedException(e);
+      }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+
+      LightFieldUsage usage = (LightFieldUsage)o;
+
+      if (myOwner != usage.myOwner) return false;
+      if (myName != usage.myName) return false;
+
+      return true;
+    }
+
+    @Override
+    public int hashCode() {
+      return myName + 31 * myOwner;
+    }
+
+    @NotNull
+    @Override
+    public LightFieldUsage override(int ownerOverrider) {
+      return new LightFieldUsage(ownerOverrider, getName());
+    }
+
+  }
+
+  public static class LightClassUsage extends LightUsage {
+    LightClassUsage(int owner) {
+      super(owner);
+    }
+
+    @NotNull
+    @Override
+    public LightClassUsage override(int ownerOverrider) {
+      return new LightClassUsage(ownerOverrider);
+    }
+
+    @Override
+    public void save(DataOutput out) {
+      try {
+        out.writeByte(CLASS_MARKER);
+        DataInputOutputUtil.writeINT(out, getOwner());
+      }
+      catch (IOException e) {
+        throw new BuildDataCorruptedException(e);
+      }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+
+      LightClassUsage usage = (LightClassUsage)o;
+
+      if (myOwner != usage.myOwner) return false;
+
+      return true;
+    }
+
+    @Override
+    public int hashCode() {
+      return myOwner;
+    }
+  }
+
+  static KeyDescriptor<LightUsage> createDescriptor() {
+    return new KeyDescriptor<LightUsage>() {
+      @Override
+      public int getHashCode(LightUsage value) {
+        return value.hashCode();
+      }
+
+      @Override
+      public boolean isEqual(LightUsage val1, LightUsage val2) {
+        return val1.equals(val2);
+      }
+
+      @Override
+      public void save(@NotNull DataOutput out, LightUsage value) throws IOException {
+        value.save(out);
+      }
+
+      @Override
+      public LightUsage read(@NotNull DataInput in) throws IOException {
+        final byte type = in.readByte();
+        switch (type) {
+          case CLASS_MARKER:
+            return new LightClassUsage(DataInputOutputUtil.readINT(in));
+          case METHOD_MARKER:
+            return new LightMethodUsage(DataInputOutputUtil.readINT(in), DataInputOutputUtil.readINT(in), DataInputOutputUtil.readINT(in));
+          case FIELD_MARKER:
+            return new LightFieldUsage(DataInputOutputUtil.readINT(in), DataInputOutputUtil.readINT(in));
+        }
+        throw new AssertionError();
+      }
+    };
+  }
+
+  static byte[] bytes(Symbol symbol) {
+    return symbol.getQualifiedName().toUtf();
+  }
+
+  static LightUsage fromSymbol(Symbol symbol, ByteArrayEnumerator byteArrayEnumerator) {
+    final ElementKind kind = symbol.getKind();
+    if (symbol instanceof Symbol.ClassSymbol) {
+      return new LightClassUsage(id(symbol, byteArrayEnumerator));
+    }
+    else if (symbol instanceof Symbol.VarSymbol) {
+      return new LightFieldUsage(id(symbol.owner, byteArrayEnumerator), id(symbol, byteArrayEnumerator));
+    }
+    else if (symbol instanceof Symbol.MethodSymbol) {
+      int paramCount = ((Symbol.MethodSymbol)symbol).type.getParameterTypes().size();
+      return new LightMethodUsage(id(symbol.owner, byteArrayEnumerator), id(symbol, byteArrayEnumerator), paramCount);
+    }
+    else {
+      throw new AssertionError("unexpected symbol: " + symbol + " class: " + symbol.getClass() + " kind: " + kind);
+    }
+  }
+
+  private static int id(Symbol symbol, ByteArrayEnumerator byteArrayEnumerator) {
+    return byteArrayEnumerator.enumerate(bytes(symbol));
+  }
+}
index 976e750fe15d8cc368bb153ab9acbf3217174813..79934f84cf92e079b75919339d00331a3df15906 100644 (file)
 package org.jetbrains.jps.builders.impl.java;
 
 import com.intellij.util.ExceptionUtil;
+import com.sun.source.util.JavacTask;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.jetbrains.jps.builders.java.CannotCreateJavaCompilerException;
 import org.jetbrains.jps.builders.java.JavaCompilingTool;
 import org.jetbrains.jps.javac.JavacMain;
+import org.jetbrains.jps.javac.ast.JavacReferencesCollector;
 import org.jetbrains.jps.model.java.compiler.JavaCompilers;
 
 import javax.tools.*;
 import java.io.File;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 
@@ -79,4 +82,9 @@ public class JavacCompilerTool extends JavaCompilingTool {
   public List<String> getDefaultCompilerOptions() {
     return Collections.singletonList("-implicit:class");
   }
+
+  @Override
+  public void prepareCompilationTask(@NotNull JavaCompiler.CompilationTask task, @NotNull Collection<String> options) {
+    JavacReferencesCollector.installOn((JavacTask)task);
+  }
 }
index 560c4cecc0fd743c5e77184282e05ae24e1c132f..ff50594c7e3d3b2ce43f00c458d17937e129f6ec 100644 (file)
@@ -34,7 +34,7 @@ import java.io.*;
  * @author: db
  * Date: 08.03.11
  */
-class IntIntPersistentMultiMaplet extends IntIntMultiMaplet {
+public class IntIntPersistentMultiMaplet extends IntIntMultiMaplet {
   private static final TIntHashSet NULL_COLLECTION = new TIntHashSet();
   private static final int CACHE_SIZE = 128;
   private final PersistentHashMap<Integer, TIntHashSet> myMap;
index 39fbd5b91e424ba31e14ecaf0c507b4f0853aced..b36590f5c27b6d6efaab6a8340d912faddde8b70 100644 (file)
@@ -32,7 +32,7 @@ import java.util.Collections;
  * @author: db
  * Date: 08.03.11
  */
-class IntObjectPersistentMultiMaplet<V> extends IntObjectMultiMaplet<V> {
+public class IntObjectPersistentMultiMaplet<V> extends IntObjectMultiMaplet<V> {
   private static final Collection NULL_COLLECTION = Collections.emptySet();
   private static final int CACHE_SIZE = 128;
   private final PersistentHashMap<Integer, Collection<V>> myMap;
index 4f24b5cffdd754be965879d89195395fc80c3d43..6295e103e59f517397226d477804c948c3d817a6 100644 (file)
@@ -28,7 +28,7 @@ import java.util.*;
  * @author: db
  * Date: 03.11.11
  */
-abstract class ObjectObjectMultiMaplet<K, V extends Streamable> implements Streamable {
+abstract class ObjectObjectMultiMaplet<K, V> implements Streamable {
   abstract boolean containsKey(final K key);
 
   abstract Collection<V> get(final K key);
@@ -87,12 +87,12 @@ abstract class ObjectObjectMultiMaplet<K, V extends Streamable> implements Strea
       final List<String> list = new LinkedList<String>();
 
       for (final V value : b) {
-        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        final PrintStream s = new PrintStream(baos);
-
-        value.toStream(context, s);
-
-        list.add(baos.toString());
+        if (value instanceof Streamable) {
+          final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+          final PrintStream s = new PrintStream(baos);
+          ((Streamable) value).toStream(context, s);
+          list.add(baos.toString());
+        }
       }
 
       Collections.sort(list);
index 2dfc9bdaa6302f524e5e6483b57ec0f74ca7e020..2d5aed313c5bedb9895e7802d053eb80b18f4748 100644 (file)
@@ -32,7 +32,7 @@ import java.util.Collections;
  * @author Eugene Zhuravlev
  *         Date: 9/10/12
  */
-public class ObjectObjectPersistentMultiMaplet<K, V extends Streamable> extends ObjectObjectMultiMaplet<K, V>{
+public class ObjectObjectPersistentMultiMaplet<K, V> extends ObjectObjectMultiMaplet<K, V>{
   private static final Collection NULL_COLLECTION = Collections.emptySet();
   private static final int CACHE_SIZE = 128;
   private final PersistentHashMap<K, Collection<V>> myMap;
index a4e044eb0533c0810da2d774e192a67c76bf774e..4ccf9ab769bf49ce7b89637f4d1d530ce6f26f5c 100644 (file)
@@ -144,7 +144,7 @@ public class ClasspathBootstrap {
     cp.addAll(getInstrumentationUtilRoots());
     cp.add(getResourcePath(IXMLBuilder.class));  // nano-xml
     cp.add(getJpsPluginSystemClassesPath().getAbsolutePath().replace('\\', '/'));
-    
+    appendReferenceTypeScannerClasspath(cp);
     //don't forget to update layoutCommunityJps() in layouts.gant accordingly
 
     if (!isLauncherUsed) {
@@ -161,6 +161,16 @@ public class ClasspathBootstrap {
     return ContainerUtil.newArrayList(cp);
   }
 
+  private static void appendReferenceTypeScannerClasspath(Set<String> cp) {
+    try {
+      final String path = getResourcePath(Class.forName("org.jetbrains.jps.javac.ast.Javac8ReferenceTypeScanner"));
+      if (path != null) {
+        cp.add(path);
+      }
+    } catch (Exception ignored) {
+    }
+  }
+
   public static void appendJavaCompilerClasspath(Collection<String> cp) {
     final Class<StandardJavaFileManager> optimizedFileManagerClass = getOptimizedFileManagerClass();
     if (optimizedFileManagerClass != null) {
index 7d2cc2868a2c2af75b0b77c89357ef98d24b8edb..94786aea05c3550974ad283cbd7bfd403b69b6ed 100644 (file)
@@ -20,6 +20,7 @@ import org.jetbrains.jps.builders.BuildTargetType;
 import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType;
 import org.jetbrains.jps.builders.java.ResourcesTargetType;
 import org.jetbrains.jps.classFilesIndex.indexer.api.ClassFilesIndicesBuilder;
+import org.jetbrains.jps.backwardRefs.BackwardReferenceIndexBuilder;
 import org.jetbrains.jps.incremental.instrumentation.NotNullInstrumentingBuilder;
 import org.jetbrains.jps.incremental.instrumentation.RmiStubsGenerator;
 import org.jetbrains.jps.incremental.java.JavaBuilder;
@@ -49,7 +50,8 @@ public class JavaBuilderService extends BuilderService {
     return Arrays.asList(new JavaBuilder(SharedThreadPool.getInstance()),
                          new NotNullInstrumentingBuilder(),
                          new RmiStubsGenerator(),
-                         new ClassFilesIndicesBuilder());
+                         new ClassFilesIndicesBuilder(),
+                         new BackwardReferenceIndexBuilder());
   }
 
   @NotNull
index 07cdee93cf75b65494a375cc1edd4939e81f0e00..1060cb5d6a4c96963778ddb3a402f4fda30b69a3 100644 (file)
@@ -141,9 +141,7 @@ public class JavaBuilder extends ModuleLevelBuilder {
 
   @Override
   public void buildStarted(CompileContext context) {
-    final JpsProject project = context.getProjectDescriptor().getProject();
-    final JpsJavaCompilerConfiguration config = JpsJavaExtensionService.getInstance().getCompilerConfiguration(project);
-    final String compilerId = config == null? JavaCompilers.JAVAC_ID : config.getJavaCompilerId();
+    final String compilerId = getUsedCompilerId(context);
     if (LOG.isDebugEnabled()) {
       LOG.debug("Java compiler ID: " + compilerId);
     }
@@ -746,6 +744,12 @@ public class JavaBuilder extends ModuleLevelBuilder {
     return true;
   }
 
+  @NotNull
+  public static String getUsedCompilerId(CompileContext context) {
+    final JpsProject project = context.getProjectDescriptor().getProject();
+    final JpsJavaCompilerConfiguration config = JpsJavaExtensionService.getInstance().getCompilerConfiguration(project);
+    return config == null ? JavaCompilers.JAVAC_ID : config.getJavaCompilerId();
+  }
 
   private static void addCrossCompilationOptions(final int compilerSdkVersion, final List<String> options, final CompileContext context, final ModuleChunk chunk) {
     final JpsJavaCompilerConfiguration compilerConfiguration = JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(
diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacReferencesCollector.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacReferencesCollector.java
new file mode 100644 (file)
index 0000000..f3ff9d9
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.jps.javac.ast;
+
+import com.intellij.util.ReflectionUtil;
+import com.intellij.util.SmartList;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.JavacTask;
+import com.sun.source.util.TaskEvent;
+import com.sun.source.util.TaskListener;
+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.THashSet;
+import org.jetbrains.jps.javac.ast.api.JavacFileReferencesRegistrar;
+import org.jetbrains.jps.javac.ast.api.JavacRefSymbol;
+import org.jetbrains.jps.service.JpsServiceManager;
+
+import javax.lang.model.element.ElementKind;
+import javax.tools.*;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Set;
+
+public class JavacReferencesCollector {
+  public static void installOn(JavacTask task) {
+    List<JavacFileReferencesRegistrar> fullASTListeners = new SmartList<JavacFileReferencesRegistrar>();
+    List<JavacFileReferencesRegistrar> onlyImportsListeners = new SmartList<JavacFileReferencesRegistrar>();
+    for (JavacFileReferencesRegistrar listener : JpsServiceManager.getInstance().getExtensions(JavacFileReferencesRegistrar.class)) {
+      if (!listener.initialize()) {
+        continue;
+      }
+      (listener.onlyImports() ? onlyImportsListeners : fullASTListeners).add(listener);
+    }
+
+    final JavacFileReferencesRegistrar[] fullASTListenerArray = fullASTListeners.toArray(new JavacFileReferencesRegistrar[fullASTListeners.size()]);
+    final JavacFileReferencesRegistrar[] onlyImportsListenerArray = onlyImportsListeners.toArray(new JavacFileReferencesRegistrar[onlyImportsListeners.size()]);
+    if (fullASTListenerArray.length == 0 && onlyImportsListenerArray.length == 0) {
+      return;
+    }
+
+    Method addTaskMethod = ReflectionUtil.getMethod(JavacTask.class, "addTaskListener", TaskListener.class); // jdk >= 8
+    if (addTaskMethod == null) {
+      addTaskMethod = ReflectionUtil.getMethod(JavacTask.class, "setTaskListener", TaskListener.class); // jdk 6-7
+    }
+    assert addTaskMethod != null;
+
+    try {
+      addTaskMethod.invoke(task, new MyTaskListener(fullASTListenerArray, onlyImportsListenerArray));
+    }
+    catch (IllegalAccessException e) {
+      throw new RuntimeException(e);
+    }
+    catch (InvocationTargetException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  private static final class MyTaskListener implements TaskListener {
+    private JCTree.JCCompilationUnit myCurrentCompilationUnit;
+    private final JavacFileReferencesRegistrar[] myFullASTListeners;
+    private final JavacFileReferencesRegistrar[] myOnlyImportsListeners;
+    private final JavacTreeRefScanner myAstScanner;
+
+    private int myCurrentSize;
+    private Name myAsteriks;
+
+    public MyTaskListener(JavacFileReferencesRegistrar[] fullASTListenerArray, JavacFileReferencesRegistrar[] importsListenerArray) {
+      myFullASTListeners = fullASTListenerArray;
+      myOnlyImportsListeners = importsListenerArray;
+      myAstScanner = JavacTreeRefScanner.createASTScanner();
+    }
+
+    @Override
+    public void started(TaskEvent e) {
+
+    }
+
+    @Override
+    public void finished(TaskEvent e) {
+      try {
+        if (e.getKind() == TaskEvent.Kind.ANALYZE) {
+          // javac creates event on each processed class not file
+          if (myCurrentCompilationUnit != e.getCompilationUnit()) {
+            myCurrentCompilationUnit = (JCTree.JCCompilationUnit)e.getCompilationUnit();
+            myCurrentSize = myCurrentCompilationUnit.getTypeDecls().size() - 1;
+          }
+          else {
+            myCurrentSize--;
+          }
+
+          if (myCurrentSize == 0) {
+            final JavaFileObject sourceFile = e.getSourceFile();
+            final Set<JavacRefSymbol> symbols = new THashSet<JavacRefSymbol>();
+            scanImports(myCurrentCompilationUnit, symbols);
+            for (JavacFileReferencesRegistrar listener : myOnlyImportsListeners) {
+              listener.registerReferences(sourceFile, symbols);
+            }
+            if (myFullASTListeners.length != 0) {
+              myAstScanner.scan(myCurrentCompilationUnit, new JavacTreeScannerSink() {
+                @Override
+                public void sinkReference(JavacRefSymbol ref) {
+                  symbols.add(ref);
+                }
+
+                @Override
+                public void sinkClassDeclaration(Symbol className, Symbol[] supers) {
+                  for (JavacFileReferencesRegistrar listener : myFullASTListeners) {
+                    listener.registerClassDeclaration(className, supers);
+                  }
+                }
+              });
+              for (JavacFileReferencesRegistrar listener : myFullASTListeners) {
+                listener.registerReferences(sourceFile, symbols);
+              }
+            }
+          }
+        }
+      }
+      catch (Exception ex) {
+        throw new ClientCodeException(ex);
+      }
+    }
+
+    private Name getAsteriksFromCurrentNameTable(Name tableRepresentative) {
+      if (myAsteriks == null) {
+        myAsteriks = tableRepresentative.table.fromChars(new char[]{'*'}, 0, 1);
+      }
+      return myAsteriks;
+    }
+
+    private void scanImports(JCTree.JCCompilationUnit compilationUnit, Set<JavacRefSymbol> symbols) {
+      for (JCTree.JCImport anImport : compilationUnit.getImports()) {
+        final JCTree.JCFieldAccess id = (JCTree.JCFieldAccess)anImport.getQualifiedIdentifier();
+        final Symbol sym = id.sym;
+        if (sym == null) {
+          final JCTree.JCFieldAccess classImport = (JCTree.JCFieldAccess)id.getExpression();
+          final Symbol ownerSym = classImport.sym;
+          final Name name = id.getIdentifier();
+          if (name != getAsteriksFromCurrentNameTable(name)) {
+            // member import
+            for (Symbol memberSymbol : ownerSym.members().getElements()) {
+              if (memberSymbol.getSimpleName() == name) {
+                symbols.add(new JavacRefSymbol(memberSymbol, Tree.Kind.IMPORT));
+              }
+            }
+          }
+          collectClassImports(ownerSym, symbols);
+        } else {
+          // class import
+          collectClassImports(sym, symbols);
+        }
+      }
+    }
+  }
+
+  private static void collectClassImports(Symbol baseImport, Set<JavacRefSymbol> collector) {
+    for (Symbol symbol = baseImport;
+         symbol != null && symbol.getKind() != ElementKind.PACKAGE;
+         symbol = symbol.owner) {
+      collector.add(new JavacRefSymbol(symbol, Tree.Kind.IMPORT));
+    }
+  }
+}
diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacTreeRefScanner.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacTreeRefScanner.java
new file mode 100644 (file)
index 0000000..ac18822
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.jps.javac.ast;
+
+import com.sun.source.tree.*;
+import com.sun.source.util.TreeScanner;
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.tree.JCTree;
+import org.jetbrains.jps.javac.ast.api.JavacRefSymbol;
+
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.type.TypeKind;
+import java.util.List;
+
+class JavacTreeRefScanner extends TreeScanner<Tree, JavacTreeScannerSink> {
+  private static final Symbol[] EMPTY_SYMBOL_ARRAY = new Symbol[0];
+  
+  @Override
+  public Tree visitCompilationUnit(CompilationUnitTree node, JavacTreeScannerSink sink) {
+    scan(node.getPackageAnnotations(), sink);
+    scan(node.getTypeDecls(), sink);
+    return node;
+  }
+
+  @Override
+  public Tree visitIdentifier(IdentifierTree node, JavacTreeScannerSink sink) {
+    final JCTree.JCIdent javacIdentifier = (JCTree.JCIdent)node;
+    final Type type = javacIdentifier.type;
+    if (type == null) {
+      return null;
+    }
+    if (type.getKind() == TypeKind.PACKAGE) {
+      return null;
+    }
+    final Symbol sym = javacIdentifier.sym;
+    if (sym.getKind() == ElementKind.PARAMETER ||
+        sym.getKind() == ElementKind.LOCAL_VARIABLE ||
+        sym.getKind() == ElementKind.FIELD ||
+        sym.getKind() == ElementKind.EXCEPTION_PARAMETER ||
+        sym.getKind() == ElementKind.TYPE_PARAMETER) {
+      return null;
+    }
+    sink.sinkReference(new JavacRefSymbol(sym, Tree.Kind.IDENTIFIER));
+    return null;
+  }
+
+  @Override
+  public Tree visitVariable(VariableTree node, JavacTreeScannerSink sink) {
+    final Symbol.VarSymbol sym = ((JCTree.JCVariableDecl)node).sym;
+    if (sym.getKind() == ElementKind.FIELD) {
+      sink.sinkReference(new JavacRefSymbol(sym, Tree.Kind.VARIABLE));
+    }
+    return super.visitVariable(node, sink);
+  }
+
+  @Override
+  public Tree visitMemberSelect(MemberSelectTree node, JavacTreeScannerSink sink) {
+    final Symbol sym = ((JCTree.JCFieldAccess)node).sym;
+    if (sym.getKind() != ElementKind.PACKAGE) {
+      sink.sinkReference(new JavacRefSymbol(sym, Tree.Kind.MEMBER_SELECT));
+    }
+    return super.visitMemberSelect(node, sink);
+  }
+
+  @Override
+  public Tree visitMethod(MethodTree node, JavacTreeScannerSink sink) {
+    final Symbol.MethodSymbol sym = ((JCTree.JCMethodDecl)node).sym;
+    sink.sinkReference(new JavacRefSymbol(sym, Tree.Kind.METHOD));
+    return super.visitMethod(node, sink);
+  }
+  
+  
+  @Override
+  public Tree visitClass(ClassTree node, JavacTreeScannerSink sink) {
+    Symbol.ClassSymbol sym = ((JCTree.JCClassDecl)node).sym;
+    sink.sinkReference(new JavacRefSymbol(sym, Tree.Kind.CLASS));
+
+    Type superclass = sym.getSuperclass();
+    List<Type> interfaces = sym.getInterfaces();
+
+    final Symbol[] supers;
+    if (superclass != Type.noType) {
+      supers = new Symbol[interfaces.size() + 1];
+      supers[interfaces.size()] = superclass.asElement();
+    } else {
+      supers = interfaces.isEmpty() ? EMPTY_SYMBOL_ARRAY : new Symbol[interfaces.size()];
+    }
+
+    int i = 0;
+    for (Type anInterface : interfaces) {
+      supers[i++] = anInterface.asElement();
+    }
+    sink.sinkClassDeclaration(sym, supers);
+
+    return super.visitClass(node, sink);
+  }
+
+  static JavacTreeRefScanner createASTScanner() {
+    try {
+      Class aClass = Class.forName("org.jetbrains.jps.javac.ast.Javac8RefScanner");
+      return (JavacTreeRefScanner) aClass.newInstance();
+    }
+    catch (Throwable ignored) {
+      return new JavacTreeRefScanner();
+    }
+  }
+}
diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacTreeScannerSink.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacTreeScannerSink.java
new file mode 100644 (file)
index 0000000..af10cad
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.jps.javac.ast;
+
+import com.sun.tools.javac.code.Symbol;
+import org.jetbrains.jps.javac.ast.api.JavacRefSymbol;
+
+interface JavacTreeScannerSink {
+
+  void sinkReference(JavacRefSymbol ref);
+
+  void sinkClassDeclaration(Symbol className, Symbol[] supers);
+}
diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacFileReferencesRegistrar.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacFileReferencesRegistrar.java
new file mode 100644 (file)
index 0000000..bc6431f
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.jps.javac.ast.api;
+
+import com.sun.tools.javac.code.Symbol;
+
+import javax.tools.*;
+import java.util.Set;
+
+public interface JavacFileReferencesRegistrar {
+
+  boolean initialize();
+
+  boolean onlyImports();
+
+  void registerReferences(JavaFileObject file, Set<JavacRefSymbol> refs);
+
+  void registerClassDeclaration(Symbol className, Symbol[] supers);
+}
diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacRefSymbol.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacRefSymbol.java
new file mode 100644 (file)
index 0000000..68a0e51
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.jps.javac.ast.api;
+
+import com.sun.source.tree.Tree;
+import com.sun.tools.javac.code.Symbol;
+
+public class JavacRefSymbol {
+
+  private final Symbol mySymbol;
+  private final Tree.Kind myPlaceKind;
+
+  public JavacRefSymbol(Symbol symbol, Tree.Kind kind) {
+    mySymbol = symbol;
+    myPlaceKind = kind;
+  }
+
+  public Symbol getSymbol() {
+    return mySymbol;
+  }
+
+  public Tree.Kind getPlaceKind() {
+    return myPlaceKind;
+  }
+}
diff --git a/jps/jps-builders/testData/referencesIndex/arrayRefs/Array.java b/jps/jps-builders/testData/referencesIndex/arrayRefs/Array.java
new file mode 100644 (file)
index 0000000..857ea59
--- /dev/null
@@ -0,0 +1,7 @@
+class Array {
+
+  void method(Foo[] param) {
+    Bar[] bars = null;
+  }
+
+}
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/arrayRefs/Bar.java b/jps/jps-builders/testData/referencesIndex/arrayRefs/Bar.java
new file mode 100644 (file)
index 0000000..a0e430b
--- /dev/null
@@ -0,0 +1,3 @@
+class Bar {
+
+}
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/arrayRefs/Foo.java b/jps/jps-builders/testData/referencesIndex/arrayRefs/Foo.java
new file mode 100644 (file)
index 0000000..576000e
--- /dev/null
@@ -0,0 +1,3 @@
+class Foo {
+
+}
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/arrayRefs/initialIndex.txt b/jps/jps-builders/testData/referencesIndex/arrayRefs/initialIndex.txt
new file mode 100644 (file)
index 0000000..c5752da
--- /dev/null
@@ -0,0 +1,12 @@
+Backward Hierarchy:
+java.lang.Object -> Array Bar Foo
+
+Backward References:
+Array in Array
+Array.<init>(0) in Array
+Array.method(1) in Array
+Bar in Array Bar
+Bar.<init>(0) in Bar
+Foo in Array Foo
+Foo.<init>(0) in Foo
+java.lang.Object.<init>(0) in Array Bar Foo
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/deadCode/Bar.java b/jps/jps-builders/testData/referencesIndex/deadCode/Bar.java
new file mode 100644 (file)
index 0000000..f0f67ca
--- /dev/null
@@ -0,0 +1,11 @@
+public class Bar {
+  public static final boolean DEBUG = false;
+
+  public static void m() {
+    if (DEBUG) {
+      System.out.println("I'm dead");
+    } else {
+      new java.util.ArrayList();
+    }
+  }
+}
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/deadCode/initialIndex.txt b/jps/jps-builders/testData/referencesIndex/deadCode/initialIndex.txt
new file mode 100644 (file)
index 0000000..84d9345
--- /dev/null
@@ -0,0 +1,13 @@
+Backward Hierarchy:
+java.lang.Object -> Bar
+
+Backward References:
+Bar in Bar
+Bar.<init>(0) in Bar
+Bar.DEBUG in Bar
+Bar.m(0) in Bar
+java.io.PrintStream.println(1) in Bar
+java.lang.Object.<init>(0) in Bar
+java.lang.System in Bar
+java.lang.System.out in Bar
+java.util.ArrayList in Bar
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/fileCaseOnlyRename/Bar.java b/jps/jps-builders/testData/referencesIndex/fileCaseOnlyRename/Bar.java
new file mode 100644 (file)
index 0000000..fb5e246
--- /dev/null
@@ -0,0 +1,7 @@
+class Inner {
+
+  void m() {
+    System.out.println("ololo");
+  }
+
+}
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/fileCaseOnlyRename/afterRename.txt b/jps/jps-builders/testData/referencesIndex/fileCaseOnlyRename/afterRename.txt
new file mode 100644 (file)
index 0000000..607cd41
--- /dev/null
@@ -0,0 +1,11 @@
+Backward Hierarchy:
+java.lang.Object -> Inner
+
+Backward References:
+Inner in Bar bar
+Inner.<init>(0) in Bar bar
+Inner.m(0) in Bar bar
+java.io.PrintStream.println(1) in Bar bar
+java.lang.Object.<init>(0) in Bar bar
+java.lang.System in Bar bar
+java.lang.System.out in Bar bar
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/fileCaseOnlyRename/initialIndex.txt b/jps/jps-builders/testData/referencesIndex/fileCaseOnlyRename/initialIndex.txt
new file mode 100644 (file)
index 0000000..a9e446b
--- /dev/null
@@ -0,0 +1,11 @@
+Backward Hierarchy:
+java.lang.Object -> Inner
+
+Backward References:
+Inner in Bar
+Inner.<init>(0) in Bar
+Inner.m(0) in Bar
+java.io.PrintStream.println(1) in Bar
+java.lang.Object.<init>(0) in Bar
+java.lang.System in Bar
+java.lang.System.out in Bar
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/incrementalIndexUpdate/Bar.java b/jps/jps-builders/testData/referencesIndex/incrementalIndexUpdate/Bar.java
new file mode 100644 (file)
index 0000000..665fcdd
--- /dev/null
@@ -0,0 +1,6 @@
+class Bar {
+  static void m(FooImpl f) {
+    System.out.println(FooImpl.TITLE);
+    System.out.println(f.getSomeText());
+  }
+}
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/incrementalIndexUpdate/Bar_1.java b/jps/jps-builders/testData/referencesIndex/incrementalIndexUpdate/Bar_1.java
new file mode 100644 (file)
index 0000000..4d9d8cc
--- /dev/null
@@ -0,0 +1,4 @@
+class Bar {
+  static void m(FooImpl f) {
+  }
+}
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/incrementalIndexUpdate/Foo.java b/jps/jps-builders/testData/referencesIndex/incrementalIndexUpdate/Foo.java
new file mode 100644 (file)
index 0000000..62c4583
--- /dev/null
@@ -0,0 +1,3 @@
+interface Foo {
+  String getSomeText();
+}
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/incrementalIndexUpdate/FooImpl.java b/jps/jps-builders/testData/referencesIndex/incrementalIndexUpdate/FooImpl.java
new file mode 100644 (file)
index 0000000..c55d7d9
--- /dev/null
@@ -0,0 +1,7 @@
+class FooImpl implements Foo {
+  static String TITLE = "Foo.title";
+
+  public String getSomeText() {
+    return "text";
+  }
+}
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/incrementalIndexUpdate/FooImpl_2.java b/jps/jps-builders/testData/referencesIndex/incrementalIndexUpdate/FooImpl_2.java
new file mode 100644 (file)
index 0000000..0fe65ff
--- /dev/null
@@ -0,0 +1,7 @@
+class FooImpl {
+  static String TITLE = "Foo.title";
+
+  public String getSomeText() {
+    return "text";
+  }
+}
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/incrementalIndexUpdate/afterMakeIndex.txt b/jps/jps-builders/testData/referencesIndex/incrementalIndexUpdate/afterMakeIndex.txt
new file mode 100644 (file)
index 0000000..acb5796
--- /dev/null
@@ -0,0 +1,16 @@
+Backward Hierarchy:
+Foo -> FooImpl
+java.lang.Object -> Bar FooImpl
+
+Backward References:
+Bar in Bar
+Bar.<init>(0) in Bar
+Bar.m(1) in Bar
+Foo in Foo FooImpl
+Foo.getSomeText(0) in Foo
+FooImpl in Bar FooImpl
+FooImpl.<init>(0) in FooImpl
+FooImpl.TITLE in FooImpl
+FooImpl.getSomeText(0) in FooImpl
+java.lang.Object.<init>(0) in Bar FooImpl
+java.lang.String in Foo FooImpl
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/incrementalIndexUpdate/afterSecondMakeIndex.txt b/jps/jps-builders/testData/referencesIndex/incrementalIndexUpdate/afterSecondMakeIndex.txt
new file mode 100644 (file)
index 0000000..097dcae
--- /dev/null
@@ -0,0 +1,16 @@
+Backward Hierarchy:
+Foo -> FooImpl
+java.lang.Object -> Bar FooImpl
+
+Backward References:
+Bar in Bar
+Bar.<init>(0) in Bar
+Bar.m(1) in Bar
+Foo in Foo
+Foo.getSomeText(0) in Foo
+FooImpl in Bar FooImpl
+FooImpl.<init>(0) in FooImpl
+FooImpl.TITLE in FooImpl
+FooImpl.getSomeText(0) in FooImpl
+java.lang.Object.<init>(0) in Bar FooImpl
+java.lang.String in Foo FooImpl
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/incrementalIndexUpdate/initialIndex.txt b/jps/jps-builders/testData/referencesIndex/incrementalIndexUpdate/initialIndex.txt
new file mode 100644 (file)
index 0000000..936a568
--- /dev/null
@@ -0,0 +1,19 @@
+Backward Hierarchy:
+Foo -> FooImpl
+java.lang.Object -> Bar FooImpl
+
+Backward References:
+Bar in Bar
+Bar.<init>(0) in Bar
+Bar.m(1) in Bar
+Foo in Foo FooImpl
+Foo.getSomeText(0) in Foo
+FooImpl in Bar FooImpl
+FooImpl.<init>(0) in FooImpl
+FooImpl.TITLE in Bar FooImpl
+FooImpl.getSomeText(0) in Bar FooImpl
+java.io.PrintStream.println(1) in Bar
+java.lang.Object.<init>(0) in Bar FooImpl
+java.lang.String in Foo FooImpl
+java.lang.System in Bar
+java.lang.System.out in Bar
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/packageInfo/initialIndex.txt b/jps/jps-builders/testData/referencesIndex/packageInfo/initialIndex.txt
new file mode 100644 (file)
index 0000000..679eefa
--- /dev/null
@@ -0,0 +1,7 @@
+Backward Hierarchy:
+java.lang.Object -> myPackage.A
+
+Backward References:
+java.lang.Deprecated in package-info
+myPackage.A in package-info
+myPackage.A.<init>(0) in package-info
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/packageInfo/myPackage/package-info.java b/jps/jps-builders/testData/referencesIndex/packageInfo/myPackage/package-info.java
new file mode 100644 (file)
index 0000000..4830892
--- /dev/null
@@ -0,0 +1,6 @@
+@Deprecated
+package myPackage;
+
+class A {
+
+}
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/typeParameterRefs/TypeParam.java b/jps/jps-builders/testData/referencesIndex/typeParameterRefs/TypeParam.java
new file mode 100644 (file)
index 0000000..c5007b4
--- /dev/null
@@ -0,0 +1,9 @@
+import java.util.List;
+
+class TypeParam {
+
+  void method(List<String> strings) {
+
+  }
+
+}
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/typeParameterRefs/initialIndex.txt b/jps/jps-builders/testData/referencesIndex/typeParameterRefs/initialIndex.txt
new file mode 100644 (file)
index 0000000..514696e
--- /dev/null
@@ -0,0 +1,10 @@
+Backward Hierarchy:
+java.lang.Object -> TypeParam
+
+Backward References:
+TypeParam in TypeParam
+TypeParam.<init>(0) in TypeParam
+TypeParam.method(1) in TypeParam
+java.lang.Object.<init>(0) in TypeParam
+java.lang.String in TypeParam
+java.util.List in TypeParam
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/unusedImports/Bar.java b/jps/jps-builders/testData/referencesIndex/unusedImports/Bar.java
new file mode 100644 (file)
index 0000000..f3e9871
--- /dev/null
@@ -0,0 +1,12 @@
+import java.util.List;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.EMPTY_LIST;
+import java.util.ArrayList.*;
+import static java.util.LinkedList.*;
+
+/**
+ * {@link Collection#add(Object)}
+ */
+public class Bar {
+
+}
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/unusedImports/initialIndex.txt b/jps/jps-builders/testData/referencesIndex/unusedImports/initialIndex.txt
new file mode 100644 (file)
index 0000000..a329710
--- /dev/null
@@ -0,0 +1,13 @@
+Backward Hierarchy:
+java.lang.Object -> Bar
+
+Backward References:
+Bar in Bar
+Bar.<init>(0) in Bar
+java.lang.Object.<init>(0) in Bar
+java.util.ArrayList in Bar
+java.util.Collections in Bar
+java.util.Collections.EMPTY_LIST in Bar
+java.util.Collections.emptyList(0) in Bar
+java.util.LinkedList in Bar
+java.util.List in Bar
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/unusedImports2/Main.java b/jps/jps-builders/testData/referencesIndex/unusedImports2/Main.java
new file mode 100644 (file)
index 0000000..7616df8
--- /dev/null
@@ -0,0 +1,8 @@
+import static com.ru.Some.I1.I2.I3;
+import static com.ru.Some3.CONST;
+import static com.ru.Some2.I1.I2.utilityMethod;
+
+public class Main {
+    public static void main(String[] args) {
+    }
+}
diff --git a/jps/jps-builders/testData/referencesIndex/unusedImports2/com/ru/Some.java b/jps/jps-builders/testData/referencesIndex/unusedImports2/com/ru/Some.java
new file mode 100644 (file)
index 0000000..d81c0ca
--- /dev/null
@@ -0,0 +1,14 @@
+package com.ru;
+
+public class Some {
+
+    public static class I1 {
+
+        public static class I2 {
+
+            public static class I3 {
+
+            }
+        }
+    }
+}
diff --git a/jps/jps-builders/testData/referencesIndex/unusedImports2/com/ru/Some2.java b/jps/jps-builders/testData/referencesIndex/unusedImports2/com/ru/Some2.java
new file mode 100644 (file)
index 0000000..cdbf8ea
--- /dev/null
@@ -0,0 +1,18 @@
+package com.ru;
+
+public class Some2 {
+
+    public static class I1 {
+
+        public static class I2 {
+
+            public static void utilityMethod() {
+
+            }
+
+            public static void utilityMethod(int i) {
+
+            }
+        }
+    }
+}
diff --git a/jps/jps-builders/testData/referencesIndex/unusedImports2/com/ru/Some3.java b/jps/jps-builders/testData/referencesIndex/unusedImports2/com/ru/Some3.java
new file mode 100644 (file)
index 0000000..424460a
--- /dev/null
@@ -0,0 +1,5 @@
+package com.ru;
+
+public class Some3 {
+  public static final int CONST = 10;
+}
diff --git a/jps/jps-builders/testData/referencesIndex/unusedImports2/initialIndex.txt b/jps/jps-builders/testData/referencesIndex/unusedImports2/initialIndex.txt
new file mode 100644 (file)
index 0000000..e38ca9c
--- /dev/null
@@ -0,0 +1,28 @@
+Backward Hierarchy:
+java.lang.Object -> Main com.ru.Some com.ru.Some.I1 com.ru.Some.I1.I2 com.ru.Some.I1.I2.I3 com.ru.Some2 com.ru.Some2.I1 com.ru.Some2.I1.I2 com.ru.Some3
+
+Backward References:
+Main in Main
+Main.<init>(0) in Main
+Main.main(1) in Main
+com.ru.Some in Main Some
+com.ru.Some.<init>(0) in Some
+com.ru.Some.I1 in Main Some
+com.ru.Some.I1.<init>(0) in Some
+com.ru.Some.I1.I2 in Main Some
+com.ru.Some.I1.I2.<init>(0) in Some
+com.ru.Some.I1.I2.I3 in Main Some
+com.ru.Some.I1.I2.I3.<init>(0) in Some
+com.ru.Some2 in Main Some2
+com.ru.Some2.<init>(0) in Some2
+com.ru.Some2.I1 in Main Some2
+com.ru.Some2.I1.<init>(0) in Some2
+com.ru.Some2.I1.I2 in Main Some2
+com.ru.Some2.I1.I2.<init>(0) in Some2
+com.ru.Some2.I1.I2.utilityMethod(0) in Main Some2
+com.ru.Some2.I1.I2.utilityMethod(1) in Main Some2
+com.ru.Some3 in Main Some3
+com.ru.Some3.<init>(0) in Some3
+com.ru.Some3.CONST in Main Some3
+java.lang.Object.<init>(0) in Main Some Some2 Some3
+java.lang.String in Main
\ No newline at end of file
diff --git a/jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTest.kt b/jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTest.kt
new file mode 100644 (file)
index 0000000..0c22366
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.references
+
+import com.intellij.openapi.application.ex.PathManagerEx
+import com.intellij.openapi.util.io.FileUtil
+
+class ReferenceIndexTest : ReferenceIndexTestBase() {
+  override fun getTestDataRootPath(): String {
+    return FileUtil.toCanonicalPath(PathManagerEx.findFileUnderCommunityHome("jps/jps-builders/testData/referencesIndex").absolutePath, '/')
+  }
+
+  fun testIncrementalIndexUpdate() {
+    assertIndexOnRebuild("Bar.java", "Foo.java", "FooImpl.java")
+    changeFileContent("Bar.java", "Bar_1.java")
+    buildAllModules()
+    assertIndexEquals("afterMakeIndex.txt")
+    changeFileContent("FooImpl.java", "FooImpl_2.java")
+    buildAllModules()
+    assertIndexEquals("afterSecondMakeIndex.txt")
+  }
+
+  fun testFileCaseOnlyRename() {
+    assertIndexOnRebuild("Bar.java")
+    renameFile("Bar.java", "bar.java")
+    buildAllModules()
+    assertIndexEquals("afterRename.txt")
+  }
+
+  fun testUnusedImports() {
+    assertIndexOnRebuild("Bar.java")
+  }
+
+  fun testUnusedImports2() {
+    assertIndexOnRebuild("Main.java",
+                         "com/ru/Some.java",
+                         "com/ru/Some2.java",
+                         "com/ru/Some3.java")
+  }
+
+  fun testDeadCode() {
+    assertIndexOnRebuild("Bar.java")
+  }
+
+  fun testPackageInfo() {
+    assertIndexOnRebuild("myPackage/package-info.java")
+  }
+
+  fun testArrayRefs() {
+    assertIndexOnRebuild("Array.java", "Foo.java", "Bar.java")
+  }
+
+  fun testTypeParameterRefs() {
+    assertIndexOnRebuild("TypeParam.java")
+  }
+}
+
diff --git a/jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTestBase.kt b/jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTestBase.kt
new file mode 100644 (file)
index 0000000..2767ab9
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.references
+
+import com.intellij.openapi.util.io.FileUtil
+import com.intellij.openapi.vfs.CharsetToolkit
+import com.intellij.util.PathUtil
+import com.sun.tools.javac.util.Convert
+import org.jetbrains.jps.builders.JpsBuildTestCase
+import org.jetbrains.jps.builders.TestProjectBuilderLogger
+import org.jetbrains.jps.builders.logging.BuildLoggingManager
+import org.jetbrains.jps.backwardRefs.ByteArrayEnumerator
+import org.jetbrains.jps.backwardRefs.CompilerBackwardReferenceIndex
+import org.jetbrains.jps.backwardRefs.LightUsage
+import org.jetbrains.jps.backwardRefs.BackwardReferenceIndexWriter
+import java.io.File
+
+abstract class ReferenceIndexTestBase : JpsBuildTestCase() {
+  public override fun setUp() {
+    super.setUp()
+    System.setProperty(BackwardReferenceIndexWriter.PROP_KEY, true.toString())
+  }
+
+  public override fun tearDown() {
+    super.tearDown()
+    System.clearProperty(BackwardReferenceIndexWriter.PROP_KEY)
+  }
+
+  protected fun assertIndexOnRebuild(vararg files: String) {
+    var representativeFile: String? = null
+    for (file in files) {
+      val addedFile = addFile(file)
+      if (representativeFile == null) {
+        representativeFile = addedFile
+      }
+    }
+    addModule("m", PathUtil.getParentPath(representativeFile!!))
+    rebuildAllModules()
+    assertIndexEquals("initialIndex.txt")
+  }
+
+  protected fun renameFile(fileToRename: String, newName: String) {
+    rename(orCreateProjectDir.path + "/m/" + fileToRename, newName)
+  }
+
+  protected fun changeFileContent(name: String, changesSourceFile: String) {
+    changeFile("m/" + name, FileUtil.loadFile(File(testDataRootPath + "/" + getTestName(true) + "/" + changesSourceFile), CharsetToolkit.UTF8_CHARSET))
+  }
+
+  protected fun addFile(name: String): String {
+    return createFile("m/" + name, FileUtil.loadFile(File(getTestDataPath() + name), CharsetToolkit.UTF8_CHARSET))
+  }
+
+
+  protected fun assertIndexEquals(expectedIndexDumpFile: String) {
+    assertSameLinesWithFile(testDataRootPath + "/" + getTestName(true) + "/" + expectedIndexDumpFile, indexAsText())
+  }
+
+  protected fun indexAsText(): String {
+    val pd = createProjectDescriptor(BuildLoggingManager(TestProjectBuilderLogger()))
+    val manager = pd.dataManager
+    val buildDir = manager.dataPaths.dataStorageRoot
+    val index = CompilerBackwardReferenceIndex(buildDir)
+
+    try {
+      val fileEnumerator = index.filePathEnumerator
+      val nameEnumerator = index.byteSeqEum
+
+      val result = StringBuilder()
+      result.append("Backward Hierarchy:\n")
+      val hierarchyText = mutableListOf<String>()
+      index.backwardHierarchyMap.forEachEntry { superClass, inheritors ->
+        val superClassName = superClass.asName(nameEnumerator)
+        val inheritorsText = mutableListOf<String>()
+        inheritors.forEach { id ->
+          inheritorsText.add(id.asName(nameEnumerator))
+          true
+        }
+        inheritorsText.sort()
+        hierarchyText.add(superClassName + " -> " + inheritorsText.joinToString(separator = " "))
+        true
+      }
+      hierarchyText.sort()
+      result.append(hierarchyText.joinToString(separator = "\n"))
+
+      result.append("\n\nBackward References:\n")
+      val referencesText = mutableListOf<String>()
+      index.backwardReferenceMap.forEachEntry { usage, files ->
+        val referents = mutableListOf<String>()
+        files.forEach { id ->
+          val file = File(fileEnumerator.valueOf(id))
+          val fileName = FileUtil.getNameWithoutExtension(file)
+          referents.add(fileName)
+        }
+        referents.sort()
+        referencesText.add(usage.asText(nameEnumerator) + " in " + referents.joinToString(separator = " "))
+        true
+      }
+      referencesText.sort()
+      result.append(referencesText.joinToString(separator = "\n"))
+
+      return result.toString()
+    } finally {
+      index.close()
+    }
+  }
+
+  private fun getTestDataPath() = testDataRootPath + "/" + getTestName(true) + "/"
+
+  fun Int.asName(byteArrayEnumerator: ByteArrayEnumerator): String = Convert.utf2string(
+      byteArrayEnumerator.valueOf(this))
+
+  fun LightUsage.asText(byteArrayEnumerator: ByteArrayEnumerator): String =
+      when (this) {
+        is LightUsage.LightMethodUsage -> this.owner.asName(byteArrayEnumerator) + "." + this.name.asName(
+            byteArrayEnumerator) + "(" + this.parameterCount + ")"
+        is LightUsage.LightFieldUsage -> this.owner.asName(byteArrayEnumerator) + "." + this.name.asName(byteArrayEnumerator)
+        is LightUsage.LightClassUsage -> this.owner.asName(byteArrayEnumerator)
+        else -> throw UnsupportedOperationException()
+      }
+}
\ No newline at end of file
diff --git a/platform/indexing-impl/src/com/intellij/find/bytecode/CompilerReferenceScopeOptimizer.java b/platform/indexing-impl/src/com/intellij/find/bytecode/CompilerReferenceScopeOptimizer.java
new file mode 100644 (file)
index 0000000..03f492f
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.find.bytecode;
+
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.search.UseScopeOptimizer;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class CompilerReferenceScopeOptimizer extends UseScopeOptimizer {
+  @Nullable
+  @Override
+  public GlobalSearchScope getScopeToExclude(@NotNull PsiElement element) {
+    return CompilerReferenceService.getInstance(element.getProject()).getScopeWithoutReferences(element);
+  }
+}
diff --git a/platform/indexing-impl/src/com/intellij/find/bytecode/CompilerReferenceService.java b/platform/indexing-impl/src/com/intellij/find/bytecode/CompilerReferenceService.java
new file mode 100644 (file)
index 0000000..1abbacd
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.find.bytecode;
+
+import com.intellij.openapi.components.AbstractProjectComponent;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.registry.Registry;
+import com.intellij.openapi.util.registry.RegistryValue;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.search.GlobalSearchScope;
+import org.jetbrains.annotations.NotNull;
+
+public abstract class CompilerReferenceService extends AbstractProjectComponent {
+  public static final RegistryValue IS_ENABLED_KEY = Registry.get("bytecode.ref.index");
+
+  protected CompilerReferenceService(Project project) {
+    super(project);
+  }
+
+  public static CompilerReferenceService getInstance(@NotNull Project project) {
+    return project.getComponent(CompilerReferenceService.class);
+  }
+
+  public abstract GlobalSearchScope getScopeWithoutReferences(@NotNull PsiElement element);
+
+  public static boolean isEnabled() {
+    return IS_ENABLED_KEY.asBoolean();
+  }
+}
index a5f60e84099a957864bd3b2b4804667f8ad235c5..d23dd8fd51ceb3cf50ab1929e9822e891d48ab5a 100644 (file)
@@ -862,3 +862,7 @@ low.memory.watcher.sync.description=Whether LowMemoryWatcher runnables should be
 
 typing.freeze.report.dumps=false
 typing.freeze.report.dumps.description=Automatically reports thread dumps to our statistics server
+
+bytecode.ref.index=false
+bytecode.ref.index.description=Enables find usages using references from bytecode
+bytecode.ref.index.restartRequired=true
\ No newline at end of file
index 0b33d2ba6e7557a016c8d48af99df6352f31aedb..1491573a13845977cb647e9cbd8bd3fedb93cf95 100644 (file)
       <implementation-class>com.intellij.codeInsight.daemon.impl.ParameterHintsPassFactory</implementation-class>
       <skipForDefaultProject/>
     </component>
+
+    <component>
+      <interface-class>com.intellij.find.bytecode.CompilerReferenceService</interface-class>
+      <implementation-class>com.intellij.compiler.CompilerReferenceServiceImpl</implementation-class>
+    </component>
   </project-components>
 
   <extensionPoints>
     <library.javaSourceRootDetector implementation="com.intellij.openapi.roots.ui.configuration.LibraryJavaSourceRootDetector"/>
 
     <fileBasedIndex implementation="com.intellij.psi.impl.search.JavaNullMethodArgumentIndex"/>
+
+    <useScopeOptimizer implementation="com.intellij.find.bytecode.CompilerReferenceScopeOptimizer"/>
   </extensions>
 
   <actions>
index bdb74e98958da4e94320acb28a9061bbeedf9307..b7988cc356c6a3657daf9e253ce7c160f9d5329e 100644 (file)
     <projectStructure.sourceRootEditHandler implementation="com.intellij.openapi.roots.ui.configuration.JavaTestResourceRootEditHandler"/>
 
     <buildProcess.parametersProvider implementation="com.intellij.compiler.classFilesIndex.api.index.ClassFilesIndexerBuilderParametersProvider"/>
-
+    <buildProcess.parametersProvider implementation="com.intellij.compiler.CompilerReferenceIndexBuildParametersProvider"/>
   </extensions>
   <extensions defaultExtensionNs="org.jetbrains">
     <webServerRootsProvider implementation="org.jetbrains.builtInWebServer.ArtifactWebServerRootsProvider" order="last"/>