Use KotlinCoreEnvironment for parsing Kotlin in IC

Creating and disposing CoreApplicationEnvironment on each call leads to
problems with parallel builds, while KotlinCoreEnvironment
avoids disposing application environment in case of parallel builds.

    #KT-23694 fixed
This commit is contained in:
Alexey Tsvetkov
2018-04-10 16:08:11 +03:00
parent 13ac64b84a
commit bd50ad87ba
2 changed files with 24 additions and 58 deletions
@@ -5,44 +5,48 @@
package org.jetbrains.kotlin.incremental.parsing
import com.intellij.core.CoreApplicationEnvironment
import com.intellij.core.CoreProjectEnvironment
import com.intellij.lang.MetaLanguage
import com.intellij.openapi.Disposable
import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.vfs.StandardFileSystems
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.openapi.vfs.local.CoreLocalFileSystem
import com.intellij.psi.PsiManager
import com.intellij.psi.SingleRootFileViewProvider
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.parsing.KotlinParserDefinition
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.script.KotlinScriptDefinition
import org.jetbrains.kotlin.script.ScriptDefinitionProvider
import java.io.File
import java.util.*
fun classesFqNames(files: Set<File>): Set<String> = withPsiSetup {
val psiManager = PsiManager.getInstance(project)
fun classesFqNames(files: Set<File>): Set<String> {
val existingKotlinFiles = files.filter { it.name.endsWith(".kt", ignoreCase = true) && it.isFile }
if (existingKotlinFiles.isEmpty()) return emptySet()
val disposable = Disposer.newDisposable()
return try {
classesFqNames(existingKotlinFiles, disposable)
} finally {
Disposer.dispose(disposable)
}
}
private fun classesFqNames(kotlinFiles: Collection<File>, disposable: Disposable): Set<String> {
val config = CompilerConfiguration()
val configFiles = EnvironmentConfigFiles.JVM_CONFIG_FILES
val environment = KotlinCoreEnvironment.createForProduction(disposable, config, configFiles)
val psiManager = PsiManager.getInstance(environment.project)
val fileManager = VirtualFileManager.getInstance()
val localFS = fileManager.getFileSystem(StandardFileSystems.FILE_PROTOCOL) as CoreLocalFileSystem
classesFqNames(files, psiManager, localFS)
}
private fun classesFqNames(files: Collection<File>, psiManager: PsiManager, localFS: CoreLocalFileSystem): Set<String> {
val result = HashSet<String>()
for (file in files) {
if (!file.name.endsWith(".kt", ignoreCase = true)) continue
val virtualFile = localFS.findFileByIoFile(file) ?: continue
for (file in kotlinFiles) {
val virtualFile = localFS.findFileByIoFile(file)!!
for (psiFile in SingleRootFileViewProvider(psiManager, virtualFile).allFiles) {
if (psiFile !is KtFile) continue
@@ -65,41 +69,3 @@ private fun classesFqNames(files: Collection<File>, psiManager: PsiManager, loca
private fun Collection<KtDeclaration>.filterClassesTo(classes: Deque<KtClassOrObject>) {
filterIsInstanceTo<KtClassOrObject, Deque<KtClassOrObject>>(classes)
}
private data class PsiSetup(
val applicationEnvironment: CoreApplicationEnvironment,
val projectEnvironment: CoreProjectEnvironment,
val project: Project,
val disposable: Disposable
)
private inline fun <T> withPsiSetup(fn: PsiSetup.() -> T): T {
val disposable = Disposer.newDisposable()
return try {
val applicationEnvironment = CoreApplicationEnvironment(disposable, false)
val projectEnvironment = CoreProjectEnvironment(disposable, applicationEnvironment)
CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), MetaLanguage.EP_NAME, MetaLanguage::class.java)
applicationEnvironment.registerApplicationService(ScriptDefinitionProvider::class.java,
NoopScriptDefinitionProvider()
)
applicationEnvironment.registerFileType(KotlinFileType.INSTANCE, "kt")
applicationEnvironment.registerParserDefinition(KotlinParserDefinition())
val project = projectEnvironment.project
val setup = PsiSetup(applicationEnvironment, projectEnvironment, project, disposable)
setup.fn()
} finally {
Disposer.dispose(disposable)
}
}
private class NoopScriptDefinitionProvider : ScriptDefinitionProvider {
override fun isScript(fileName: String): Boolean {
return false
}
override fun findScriptDefinition(fileName: String): KotlinScriptDefinition? {
return null
}
}
@@ -104,7 +104,7 @@ object CompilerRunnerUtil {
environment: JpsCompilerEnvironment,
files: Set<File>
): Set<String> = withCompilerClassloader(environment) { classLoader ->
val klass = Class.forName("org.jetbrains.kotlin.parsing.util.ParseFileUtilsKt", true, classLoader)
val klass = Class.forName("org.jetbrains.kotlin.incremental.parsing.ParseFileUtilsKt", true, classLoader)
val method = klass.getMethod("classesFqNames", Set::class.java)
@Suppress("UNCHECKED_CAST")
method.invoke(klass, files) as? Set<String>