Scripting: avoid creating multiple PSIs for every imported script

#KT-53009 fixed
#KT-42810 should be possible now too, but more testing is needed
#KT-42101 can also be addressed now, but first the serialization
 of the imported scripts property should be solved
This commit is contained in:
Ilya Chernikov
2022-07-20 19:42:53 +02:00
committed by teamcity
parent 9bc057afb2
commit ceea563d63
5 changed files with 90 additions and 42 deletions
@@ -10,10 +10,7 @@ import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
import org.jetbrains.kotlin.scripting.resolve.KtFileScriptSource
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationResult
import org.jetbrains.kotlin.scripting.resolve.ScriptReportSink
import org.jetbrains.kotlin.scripting.resolve.refineScriptCompilationConfiguration
import org.jetbrains.kotlin.scripting.resolve.*
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
import kotlin.concurrent.write
@@ -22,6 +19,7 @@ import kotlin.script.experimental.api.ScriptCompilationConfiguration
class CliScriptDependenciesProvider(project: Project) : ScriptDependenciesProvider(project) {
private val cacheLock = ReentrantReadWriteLock()
private val cache = hashMapOf<String, ScriptCompilationConfigurationResult?>()
private val knownVirtualFileSources = mutableMapOf<String, VirtualFileScriptSource>()
override fun getScriptConfigurationResult(file: KtFile): ScriptCompilationConfigurationResult? = cacheLock.read {
calculateRefinedConfiguration(file, null)
@@ -43,7 +41,10 @@ class CliScriptDependenciesProvider(project: Project) : ScriptDependenciesProvid
else {
val scriptDef = file.findScriptDefinition()
if (scriptDef != null) {
val result = refineScriptCompilationConfiguration(KtFileScriptSource(file), scriptDef, project, providedConfiguration)
val result =
refineScriptCompilationConfiguration(
KtFileScriptSource(file), scriptDef, project, providedConfiguration, knownVirtualFileSources
)
ServiceManager.getService(project, ScriptReportSink::class.java)?.attachReports(file.virtualFile, result.reports)
@@ -6,12 +6,17 @@
package org.jetbrains.kotlin.scripting.compiler.plugin.dependencies
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiManager
import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.jvm.compiler.createSourceFilesFromSourceRoots
import org.jetbrains.kotlin.cli.jvm.compiler.report
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
import org.jetbrains.kotlin.scripting.resolve.KtFileScriptSource
import org.jetbrains.kotlin.scripting.resolve.VirtualFileScriptSource
import java.io.File
import kotlin.script.experimental.api.ResultWithDiagnostics
import kotlin.script.experimental.api.ScriptCompilationConfiguration
@@ -42,6 +47,7 @@ fun collectScriptsCompilationDependencies(
var remainingSources = initialSources
val knownSourcePaths = initialSources.mapNotNullTo(HashSet()) { it.virtualFile?.path }
val importsProvider = ScriptDependenciesProvider.getInstance(project)
val psiManager by lazy(LazyThreadSafetyMode.NONE) { PsiManager.getInstance(project) }
if (importsProvider != null) {
while (true) {
val newRemainingSources = ArrayList<KtFile>()
@@ -54,17 +60,23 @@ fun collectScriptsCompilationDependencies(
is ResultWithDiagnostics.Success -> {
collectedClassPath.addAll(refinedConfiguration.value.dependenciesClassPath)
val sourceDependenciesRoots = refinedConfiguration.value.importedScripts.mapNotNull {
// TODO: support any kind of ScriptSource.
val path = (it as? FileBasedScriptSource)?.file?.absolutePath ?: return@mapNotNull null
KotlinSourceRoot(path, false)
val sourceDependencies = refinedConfiguration.value.importedScripts.mapNotNull {
if (it is KtFileScriptSource) it.ktFile
else {
val virtualFileSource = (it as? VirtualFileScriptSource)
?: error("expecting script sources resolved to virtual files here")
(psiManager.findFile(virtualFileSource.virtualFile) as? KtFile).also {
if (it == null) {
configuration.report(
CompilerMessageSeverity.ERROR,
"imported file is not kotlin source: ${virtualFileSource.virtualFile.path}",
// TODO: consider receiving and using precise location from the resolver in the future
CompilerMessageLocation.create(source.virtualFile.path)
)
}
}
}
}
val sourceDependencies =
createSourceFilesFromSourceRoots(
configuration, project, sourceDependenciesRoots,
// TODO: consider receiving and using precise location from the resolver in the future
source.virtualFile?.path?.let { CompilerMessageLocation.create(it) }
)
if (sourceDependencies.isNotEmpty()) {
collectedSourceDependencies.add(
ScriptsCompilationDependencies.SourceDependencies(