use PsiFileGist instead of time-expensive indices for contract inference idea/171.783
authorpeter <peter@jetbrains.com>
Mon, 7 Nov 2016 10:10:19 +0000 (11:10 +0100)
committerpeter <peter@jetbrains.com>
Mon, 7 Nov 2016 10:10:58 +0000 (11:10 +0100)
java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInferenceIndex.kt
java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodDataExternalizer.kt
resources/src/idea/RichPlatformPlugin.xml

index 8701952d1b17f2f80ecb7856c3ba96c35473cd43..6da68ecf86e1a0151cd1b51882975ed9897b3128 100644 (file)
  */
 package com.intellij.codeInspection.dataFlow
 
-import com.intellij.ide.highlighter.JavaFileType
 import com.intellij.lang.LighterAST
 import com.intellij.lang.LighterASTNode
-import com.intellij.lang.TreeBackedLighterAST
-import com.intellij.openapi.util.Ref
 import com.intellij.psi.PsiMethod
-import com.intellij.psi.impl.source.JavaFileElementType
 import com.intellij.psi.impl.source.JavaLightStubBuilder
 import com.intellij.psi.impl.source.PsiFileImpl
 import com.intellij.psi.impl.source.PsiMethodImpl
@@ -29,53 +25,36 @@ import com.intellij.psi.impl.source.tree.JavaElementType
 import com.intellij.psi.impl.source.tree.JavaElementType.*
 import com.intellij.psi.impl.source.tree.LightTreeUtil
 import com.intellij.psi.impl.source.tree.RecursiveLighterASTNodeWalkingVisitor
-import com.intellij.psi.search.GlobalSearchScope
-import com.intellij.psi.util.CachedValueProvider
-import com.intellij.psi.util.CachedValuesManager
-import com.intellij.util.indexing.*
-import com.intellij.util.io.DataExternalizer
-import com.intellij.util.io.IntInlineKeyDescriptor
+import com.intellij.util.gist.GistManager
 import java.util.*
 
 /**
  * @author peter
  */
 
-private val INDEX_ID = ID.create<Int, MethodData>("java.inferred.contracts")
-
-class ContractInferenceIndex : FileBasedIndexExtension<Int, MethodData>(), PsiDependentIndex {
-  override fun getName() = INDEX_ID
-  override fun getVersion() = 0
-  override fun dependsOnFileContent() = true
-  override fun getKeyDescriptor() = IntInlineKeyDescriptor()
-  override fun getValueExternalizer(): DataExternalizer<MethodData> = MethodDataExternalizer
-
-  override fun getInputFilter() = FileBasedIndex.InputFilter {
-    it.fileType == JavaFileType.INSTANCE && JavaFileElementType.isInSourceContent(it)
-  }
-
-  override fun getIndexer() = DataIndexer<Int, MethodData, FileContent> { fc ->
-    val result = HashMap<Int, MethodData>()
-
-    val tree = (fc as FileContentImpl).lighterASTForPsiDependentIndex
-    object : RecursiveLighterASTNodeWalkingVisitor(tree) {
-      var methodIndex = 0
+private val gist = GistManager.getInstance().newPsiFileGist("contractInference", 0, MethodDataExternalizer) { file ->
+  indexFile(file.node.lighterAST)
+}
 
-      override fun visitNode(element: LighterASTNode) {
-        if (element.tokenType === JavaElementType.METHOD) {
-          calcData(tree, element)?.let { data -> result[methodIndex] = data }
-          methodIndex++
-        }
+private fun indexFile(tree: LighterAST): Map<Int, MethodData> {
+  val result = HashMap<Int, MethodData>()
 
-        if (JavaLightStubBuilder.isCodeBlockWithoutStubs(element)) return
+  object : RecursiveLighterASTNodeWalkingVisitor(tree) {
+    var methodIndex = 0
 
-        super.visitNode(element)
+    override fun visitNode(element: LighterASTNode) {
+      if (element.tokenType === METHOD) {
+        calcData(tree, element)?.let { data -> result[methodIndex] = data }
+        methodIndex++
       }
-    }.visitNode(tree.root)
 
-    result
-  }
+      if (JavaLightStubBuilder.isCodeBlockWithoutStubs(element)) return
+
+      super.visitNode(element)
+    }
+  }.visitNode(tree.root)
 
+  return result
 }
 
 private fun calcData(tree: LighterAST, method: LighterASTNode): MethodData? {
@@ -116,22 +95,12 @@ private fun createData(body: LighterASTNode,
 
 fun getIndexedData(method: PsiMethod): MethodData? {
   if (method !is PsiMethodImpl || !InferenceFromSourceUtil.shouldInferFromSource(method)) return null
-  val vFile = method.containingFile.virtualFile ?: return calcNonPhysicalMethodData(method)
 
-  val ref = Ref<MethodData>()
-  val scope = GlobalSearchScope.fileScope(method.project, vFile)
-  FileBasedIndex.getInstance().processValues(INDEX_ID, methodIndex(method), vFile, { file, data -> ref.set(data); true }, scope)
-  return ref.get()
+  return gist.getFileData(method.containingFile)?.get(methodIndex(method))
 }
 
 private fun methodIndex(method: PsiMethodImpl): Int {
   val file = method.containingFile as PsiFileImpl
   val stubTree = file.stubTree ?: file.calcStubTree()
   return stubTree.plainList.filter { it.stubType == JavaElementType.METHOD }.map { it.psi }.indexOf(method)
-}
-
-private fun calcNonPhysicalMethodData(method: PsiMethodImpl): MethodData? {
-  return CachedValuesManager.getCachedValue(method) {
-    CachedValueProvider.Result(calcData(method.containingFile.node.lighterAST, TreeBackedLighterAST.wrap(method.node)), method)
-  }
 }
\ No newline at end of file
index c094d311239cbd0aab1177bf19ffd7584b5953e4..621d0234c0c3b9676f4fc6390848ddb177a3b4a6 100644 (file)
@@ -17,16 +17,21 @@ package com.intellij.codeInspection.dataFlow
 
 import com.intellij.util.io.DataExternalizer
 import com.intellij.util.io.DataInputOutputUtil
-import com.intellij.util.io.IOUtil
 import java.io.DataInput
 import java.io.DataOutput
 
 /**
  * @author peter
  */
-internal object MethodDataExternalizer : DataExternalizer<MethodData> {
+internal object MethodDataExternalizer : DataExternalizer<Map<Int, MethodData>> {
 
-  override fun save(out: DataOutput, data: MethodData) {
+  override fun save(out: DataOutput, value: Map<Int, MethodData>?) {
+    writeList(out, value!!.toList()) { DataInputOutputUtil.writeINT(out, it.first); writeMethod(out, it.second) }
+  }
+
+  override fun read(input: DataInput) = readList(input) { DataInputOutputUtil.readINT(input) to readMethod(input) }.toMap()
+
+  private fun writeMethod(out: DataOutput, data: MethodData) {
     writeNullable(out, data.nullity) { writeNullity(out, it) }
     writeNullable(out, data.purity) { writePurity(out, it) }
     writeList(out, data.contracts) { writeContract(out, it) }
@@ -34,7 +39,7 @@ internal object MethodDataExternalizer : DataExternalizer<MethodData> {
     DataInputOutputUtil.writeINT(out, data.bodyEnd)
   }
 
-  override fun read(input: DataInput): MethodData {
+  private fun readMethod(input: DataInput): MethodData {
     val nullity = readNullable(input) { readNullity(input) }
     val purity = readNullable(input) { readPurity(input) }
     val contracts = readList(input) { readContract(input) }
index d2a9d01ca2c13f6c5204f93ebd4e083d45379515..79f8d9d9e66a48d4eafbcaec5ccb19d0f0085fa2 100644 (file)
     <projectService serviceInterface="com.intellij.codeInsight.InferredAnnotationsManager"
                     serviceImplementation="com.intellij.codeInsight.InferredAnnotationsManagerImpl"/>
 
-    <fileBasedIndex implementation="com.intellij.codeInspection.dataFlow.ContractInferenceIndex"/>
-
     <projectService serviceInterface="com.intellij.task.ProjectTaskManager"
                     serviceImplementation="com.intellij.task.impl.ProjectTaskManagerImpl"/>