Scripting: avoid definitions discovery when compiling regular kotlin

If only regular kotlin .kt (and .java) files are compiled, do not
register file extensions that may come from discovered script
definitions. Since the discovery is lazy by itself, this should
skip jars processing for regular compilation scenarios without scripts.
#KT-47816 fixed
This commit is contained in:
Ilya Chernikov
2022-06-10 14:27:00 +02:00
committed by teamcity
parent 5caf2a2aca
commit c2316ca305
11 changed files with 102 additions and 17 deletions
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.cli.jvm.compiler
import com.intellij.ide.highlighter.JavaClassFileType
import com.intellij.ide.highlighter.JavaFileType
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.StandardFileSystems
@@ -19,9 +21,11 @@ import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.CompilerConfigurationKey
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.extensions.CompilerConfigurationExtension
import org.jetbrains.kotlin.extensions.PreprocessedFileCreator
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.modules.Module
import org.jetbrains.kotlin.parsing.KotlinParserDefinition
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.multiplatform.isCommonSource
import java.io.File
@@ -43,6 +47,8 @@ inline fun List<KotlinSourceRoot>.forAllFiles(
val virtualFileCreator = PreprocessedFileCreator(project)
var pluginsConfigured = false
for ((sourceRootPath, isCommon) in this) {
val sourceRoot = File(sourceRootPath)
val vFile = localFileSystem.findFileByPath(sourceRoot.normalize().path)
@@ -59,9 +65,15 @@ inline fun List<KotlinSourceRoot>.forAllFiles(
continue
}
if (!vFile.isDirectory && vFile.extension != KotlinFileType.EXTENSION && vFile.fileType != KotlinFileType.INSTANCE) {
configuration.report(CompilerMessageSeverity.ERROR, "Source entry is not a Kotlin file: $sourceRootPath", reportLocation)
continue
if (!vFile.isDirectory && vFile.extension != KotlinFileType.EXTENSION) {
if (!pluginsConfigured) {
vFile.registerPluginsSuppliedExtensionsIfNeeded(project)
pluginsConfigured = true
}
if (vFile.fileType != KotlinFileType.INSTANCE) {
configuration.report(CompilerMessageSeverity.ERROR, "Source entry is not a Kotlin file: $sourceRootPath", reportLocation)
continue
}
}
for (file in sourceRoot.walkTopDown()) {
@@ -69,12 +81,29 @@ inline fun List<KotlinSourceRoot>.forAllFiles(
val virtualFile = localFileSystem.findFileByPath(file.absoluteFile.normalize().path)?.let(virtualFileCreator::create)
if (virtualFile != null && processedFiles.add(virtualFile)) {
if (!pluginsConfigured) {
virtualFile.registerPluginsSuppliedExtensionsIfNeeded(project)
pluginsConfigured = true
}
body(virtualFile, isCommon)
}
}
}
}
fun VirtualFile.registerPluginsSuppliedExtensionsIfNeeded(project: Project) {
if (
extension == null ||
extension == KotlinFileType.EXTENSION ||
extension == JavaFileType.INSTANCE.defaultExtension ||
extension == JavaClassFileType.INSTANCE.defaultExtension ||
fileType == KotlinFileType.INSTANCE
) return
for (extension in CompilerConfigurationExtension.getInstances(project)) {
extension.updateFileRegistry()
}
}
fun createSourceFilesFromSourceRoots(
configuration: CompilerConfiguration,
project: Project,
@@ -15,4 +15,6 @@ interface CompilerConfigurationExtension {
)
fun updateConfiguration(configuration: CompilerConfiguration)
fun updateFileRegistry() {}
}
@@ -5,7 +5,6 @@ build:
[kotlin2js] Compiling [[TestData]/root1] => [[Temp]/out.js]
[kotlin2js] logging: using Kotlin home directory [KotlinProjectHome]/dist/kotlinc
[kotlin2js] logging: exception on loading scripting plugin: java.lang.ClassNotFoundException: org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCompilerConfigurationComponentRegistrar
[kotlin2js] logging: configure scripting: Added template org.jetbrains.kotlin.mainKts.MainKtsScript from [[CompilerLib]/kotlin-main-kts.jar, [CompilerLib]/kotlin-reflect.jar, [CompilerLib]/kotlin-script-runtime.jar, [CompilerLib]/kotlin-stdlib.jar]
[kotlin2js] logging: compiling source files: [TestData]/root1/foo.kt
BUILD SUCCESSFUL
@@ -7,7 +7,6 @@ build:
[kotlinc] logging: exception on loading scripting plugin: java.lang.ClassNotFoundException: org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCompilerConfigurationComponentRegistrar
[kotlinc] logging: using JVM IR backend
[kotlinc] logging: configuring the compilation environment
[kotlinc] logging: configure scripting: Added template org.jetbrains.kotlin.mainKts.MainKtsScript from [[CompilerLib]/kotlin-main-kts.jar, [CompilerLib]/kotlin-reflect.jar, [CompilerLib]/kotlin-script-runtime.jar, [CompilerLib]/kotlin-stdlib.jar]
BUILD SUCCESSFUL
Total time: [time]
@@ -6,10 +6,18 @@
package org.jetbrains.kotlin.test
import kotlin.script.experimental.annotations.*
import kotlin.script.experimental.api.ScriptCompilationConfiguration
import kotlin.script.experimental.api.isStandalone
@KotlinScript(fileExtension = "greet.kts")
@KotlinScript(fileExtension = "greet.kts", compilationConfiguration = GreetScriptDefinition::class)
abstract class GreetScriptTemplate {
fun greet(subject: String) {
println("Hello, $subject!")
}
}
}
object GreetScriptDefinition : ScriptCompilationConfiguration(
{
isStandalone(false)
}
)
@@ -5,11 +5,19 @@
package org.jetbrains.kotlin.test
import kotlin.script.experimental.annotations.*
import kotlin.script.experimental.annotations.KotlinScript
import kotlin.script.experimental.api.ScriptCompilationConfiguration
import kotlin.script.experimental.api.isStandalone
@KotlinScript(fileExtension = "greet")
@KotlinScript(fileExtension = "greet", compilationConfiguration = GreetScriptDefinition::class)
abstract class GreetScriptTemplate {
fun greet(subject: String) {
println("Hello, $subject!")
}
}
}
object GreetScriptDefinition : ScriptCompilationConfiguration(
{
isStandalone(false)
}
)
@@ -39,6 +39,7 @@ class ScriptDefinitionsFromClasspathDiscoverySource(
private const val MANIFEST_RESOURCE_NAME = "/META-INF/MANIFEST.MF"
@Suppress("unused") // TODO: remove if really unused
fun discoverScriptTemplatesInClassLoader(
classLoader: ClassLoader,
hostConfiguration: ScriptingHostConfiguration,
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.scripting.definitions
import com.intellij.ide.highlighter.JavaClassFileType
import com.intellij.ide.highlighter.JavaFileType
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.fileTypes.FileTypeRegistry
import com.intellij.openapi.project.Project
@@ -56,6 +57,7 @@ fun findScriptDefinition(project: Project, script: SourceCode): ScriptDefinition
fun VirtualFile.isNonScript(): Boolean =
isDirectory ||
extension == KotlinFileType.EXTENSION ||
extension == JavaFileType.INSTANCE.defaultExtension ||
extension == JavaClassFileType.INSTANCE.defaultExtension ||
!this.isKotlinFileType()
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.scripting.compiler.plugin.impl.reporter
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.definitions.*
import java.io.File
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.script.experimental.host.ScriptingHostConfiguration
class ScriptingCompilerConfigurationExtension(
@@ -88,15 +89,20 @@ class ScriptingCompilerConfigurationExtension(
if (scriptDefinitionProvider != null) {
scriptDefinitionProvider.setScriptDefinitionsSources(configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS_SOURCES))
scriptDefinitionProvider.setScriptDefinitions(configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS))
}
}
}
// Register new file extensions
val fileTypeRegistry = FileTypeRegistry.getInstance() as CoreFileTypeRegistry
override fun updateFileRegistry() {
val scriptDefinitionProvider = ScriptDefinitionProvider.getInstance(project) as? CliScriptDefinitionProvider
if (scriptDefinitionProvider != null) {
// Register new file extensions
val fileTypeRegistry = FileTypeRegistry.getInstance() as CoreFileTypeRegistry
scriptDefinitionProvider.getKnownFilenameExtensions().filter {
fileTypeRegistry.getFileTypeByExtension(it) != KotlinFileType.INSTANCE
}.forEach {
fileTypeRegistry.registerFileType(KotlinFileType.INSTANCE, it)
}
scriptDefinitionProvider.getKnownFilenameExtensions().filter {
fileTypeRegistry.getFileTypeByExtension(it) != KotlinFileType.INSTANCE
}.forEach {
fileTypeRegistry.registerFileType(KotlinFileType.INSTANCE, it)
}
}
}
@@ -0,0 +1,4 @@
fun main() {
println("OK")
}
@@ -240,6 +240,33 @@ class ScriptingWithCliCompilerTest {
}
}
@Test
fun testScriptMainKtsDiscovery() {
withTempDir { tmpdir ->
fun compileSuccessfullyGetStdErr(fileArg: String): List<String> {
val (_, err, ret) = captureOutErrRet {
CLITool.doMainNoExit(
K2JVMCompiler(),
arrayOf(
"-P", "plugin:kotlin.scripting:disable-script-definitions-autoloading=true",
"-cp", getMainKtsClassPath().joinToString(File.pathSeparator), "-d", tmpdir.path, "-verbose", fileArg)
)
}
Assert.assertEquals(0, ret.code)
return err.linesSplitTrim()
}
val loadMainKtsMessage = "logging: configure scripting: loading script definition class org.jetbrains.kotlin.mainKts.MainKtsScript using classpath"
val res1 = compileSuccessfullyGetStdErr("$TEST_DATA_DIR/compiler/mixedCompilation/nonScript.kt")
Assert.assertTrue(res1.none { it.startsWith(loadMainKtsMessage) })
val res2 = compileSuccessfullyGetStdErr("$TEST_DATA_DIR/compiler/mixedCompilation/simpleScript.main.kts")
Assert.assertTrue(res2.any { it.startsWith(loadMainKtsMessage) })
}
}
private fun getMainKtsClassPath(): List<File> {
return listOf(
File("dist/kotlinc/lib/kotlin-main-kts.jar").also {