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:
+23
-57
@@ -5,44 +5,48 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.incremental.parsing
|
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.Disposable
|
||||||
import com.intellij.openapi.extensions.Extensions
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.openapi.util.Disposer
|
import com.intellij.openapi.util.Disposer
|
||||||
import com.intellij.openapi.vfs.StandardFileSystems
|
import com.intellij.openapi.vfs.StandardFileSystems
|
||||||
import com.intellij.openapi.vfs.VirtualFileManager
|
import com.intellij.openapi.vfs.VirtualFileManager
|
||||||
import com.intellij.openapi.vfs.local.CoreLocalFileSystem
|
import com.intellij.openapi.vfs.local.CoreLocalFileSystem
|
||||||
import com.intellij.psi.PsiManager
|
import com.intellij.psi.PsiManager
|
||||||
import com.intellij.psi.SingleRootFileViewProvider
|
import com.intellij.psi.SingleRootFileViewProvider
|
||||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||||
import org.jetbrains.kotlin.parsing.KotlinParserDefinition
|
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.KtClassOrObject
|
||||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.script.KotlinScriptDefinition
|
|
||||||
import org.jetbrains.kotlin.script.ScriptDefinitionProvider
|
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
fun classesFqNames(files: Set<File>): Set<String> = withPsiSetup {
|
fun classesFqNames(files: Set<File>): Set<String> {
|
||||||
val psiManager = PsiManager.getInstance(project)
|
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 fileManager = VirtualFileManager.getInstance()
|
||||||
val localFS = fileManager.getFileSystem(StandardFileSystems.FILE_PROTOCOL) as CoreLocalFileSystem
|
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>()
|
val result = HashSet<String>()
|
||||||
|
|
||||||
for (file in files) {
|
for (file in kotlinFiles) {
|
||||||
if (!file.name.endsWith(".kt", ignoreCase = true)) continue
|
val virtualFile = localFS.findFileByIoFile(file)!!
|
||||||
|
|
||||||
val virtualFile = localFS.findFileByIoFile(file) ?: continue
|
|
||||||
|
|
||||||
for (psiFile in SingleRootFileViewProvider(psiManager, virtualFile).allFiles) {
|
for (psiFile in SingleRootFileViewProvider(psiManager, virtualFile).allFiles) {
|
||||||
if (psiFile !is KtFile) continue
|
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>) {
|
private fun Collection<KtDeclaration>.filterClassesTo(classes: Deque<KtClassOrObject>) {
|
||||||
filterIsInstanceTo<KtClassOrObject, Deque<KtClassOrObject>>(classes)
|
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,
|
environment: JpsCompilerEnvironment,
|
||||||
files: Set<File>
|
files: Set<File>
|
||||||
): Set<String> = withCompilerClassloader(environment) { classLoader ->
|
): 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)
|
val method = klass.getMethod("classesFqNames", Set::class.java)
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
method.invoke(klass, files) as? Set<String>
|
method.invoke(klass, files) as? Set<String>
|
||||||
|
|||||||
Reference in New Issue
Block a user