Refactor script definitions and resolving/refining infrastructure:

- implement wrappers to wrap old and new API providers and resolvers
- make old API deprecated (with error where possible)
- drop old internal classes related to the old API
- refactor usages accordingly
- fix and add missing features to the scripting API where necessary
This commit is contained in:
Ilya Chernikov
2019-05-12 22:26:37 +02:00
parent e5054f9648
commit e542c9ea84
94 changed files with 1554 additions and 764 deletions
@@ -13,7 +13,7 @@ import com.intellij.testFramework.TestDataFile;
import kotlin.collections.ArraysKt; import kotlin.collections.ArraysKt;
import kotlin.collections.CollectionsKt; import kotlin.collections.CollectionsKt;
import kotlin.io.FilesKt; import kotlin.io.FilesKt;
import kotlin.script.experimental.dependencies.ScriptDependencies; import kotlin.script.experimental.api.ErrorHandlingKt;
import kotlin.text.Charsets; import kotlin.text.Charsets;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil;
import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.KtFile; import org.jetbrains.kotlin.psi.KtFile;
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider; import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider;
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper;
import org.jetbrains.kotlin.test.ConfigurationKind; import org.jetbrains.kotlin.test.ConfigurationKind;
import org.jetbrains.kotlin.test.InTextDirectivesUtils; import org.jetbrains.kotlin.test.InTextDirectivesUtils;
import org.jetbrains.kotlin.test.KotlinTestUtils; import org.jetbrains.kotlin.test.KotlinTestUtils;
@@ -433,9 +434,9 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
if (externalImportsProvider != null) { if (externalImportsProvider != null) {
myEnvironment.getSourceFiles().forEach( myEnvironment.getSourceFiles().forEach(
file -> { file -> {
ScriptDependencies dependencies = externalImportsProvider.getScriptDependencies(file); ScriptCompilationConfigurationWrapper refinedConfiguration = ErrorHandlingKt.valueOrNull(externalImportsProvider.getScriptConfigurationResult(file));
if (dependencies != null) { if (refinedConfiguration != null) {
files.addAll(dependencies.getClasspath()); files.addAll(refinedConfiguration.getDependenciesClassPath());
} }
} }
); );
@@ -24,6 +24,7 @@ import kotlin.script.experimental.annotations.KotlinScript
import kotlin.script.experimental.api.ScriptCompilationConfiguration import kotlin.script.experimental.api.ScriptCompilationConfiguration
import kotlin.script.experimental.api.implicitReceivers import kotlin.script.experimental.api.implicitReceivers
import kotlin.script.experimental.api.providedProperties import kotlin.script.experimental.api.providedProperties
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.experimental.jvm.util.scriptCompilationClasspathFromContextOrStdlib import kotlin.script.experimental.jvm.util.scriptCompilationClasspathFromContextOrStdlib
abstract class AbstractCustomScriptCodegenTest : CodegenTestCase() { abstract class AbstractCustomScriptCodegenTest : CodegenTestCase() {
@@ -37,7 +38,9 @@ abstract class AbstractCustomScriptCodegenTest : CodegenTestCase() {
override fun updateConfiguration(configuration: CompilerConfiguration) { override fun updateConfiguration(configuration: CompilerConfiguration) {
if (scriptDefinitions.isNotEmpty()) { if (scriptDefinitions.isNotEmpty()) {
configureScriptDefinitions(scriptDefinitions, configuration, this::class.java.classLoader, MessageCollector.NONE, emptyMap()) configureScriptDefinitions(
scriptDefinitions, configuration, this::class.java.classLoader, MessageCollector.NONE, defaultJvmScriptingHostConfiguration
)
} }
configuration.addJvmClasspathRoots(additionalDependencies.orEmpty()) configuration.addJvmClasspathRoots(additionalDependencies.orEmpty())
@@ -21,6 +21,7 @@ import java.io.File
import java.lang.reflect.Constructor import java.lang.reflect.Constructor
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.script.experimental.annotations.KotlinScript import kotlin.script.experimental.annotations.KotlinScript
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.experimental.jvm.util.scriptCompilationClasspathFromContextOrStdlib import kotlin.script.experimental.jvm.util.scriptCompilationClasspathFromContextOrStdlib
class CustomScriptCodegenTest : CodegenTestCase() { class CustomScriptCodegenTest : CodegenTestCase() {
@@ -65,7 +66,8 @@ class CustomScriptCodegenTest : CodegenTestCase() {
if (scriptDefinitions.isNotEmpty()) { if (scriptDefinitions.isNotEmpty()) {
configureScriptDefinitions( configureScriptDefinitions(
scriptDefinitions.asList(), configuration, this::class.java.classLoader, MessageCollector.NONE, emptyMap() scriptDefinitions.asList(), configuration, this::class.java.classLoader,
MessageCollector.NONE, defaultJvmScriptingHostConfiguration
) )
} }
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
import org.jetbrains.kotlin.config.JVMConfigurationKeys import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.script.loadScriptingPlugin import org.jetbrains.kotlin.script.loadScriptingPlugin
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
import org.jetbrains.kotlin.scripts.TestKotlinScriptDependenciesResolver import org.jetbrains.kotlin.scripts.TestKotlinScriptDependenciesResolver
import org.jetbrains.kotlin.test.ConfigurationKind import org.jetbrains.kotlin.test.ConfigurationKind
@@ -33,14 +34,21 @@ import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.TestJdkKind import org.jetbrains.kotlin.test.TestJdkKind
import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Opcodes
import java.io.File import java.io.File
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.templates.ScriptTemplateDefinition import kotlin.script.templates.ScriptTemplateDefinition
class ScriptGenTest : CodegenTestCase() { class ScriptGenTest : CodegenTestCase() {
companion object { companion object {
private val FIB_SCRIPT_DEFINITION = private val FIB_SCRIPT_DEFINITION =
KotlinScriptDefinitionFromAnnotatedTemplate(ScriptWithIntParam::class) ScriptDefinition.FromLegacy(
defaultJvmScriptingHostConfiguration,
KotlinScriptDefinitionFromAnnotatedTemplate(ScriptWithIntParam::class)
)
private val NO_PARAM_SCRIPT_DEFINITION = private val NO_PARAM_SCRIPT_DEFINITION =
KotlinScriptDefinitionFromAnnotatedTemplate(Any::class) ScriptDefinition.FromLegacy(
defaultJvmScriptingHostConfiguration,
KotlinScriptDefinitionFromAnnotatedTemplate(Any::class)
)
} }
override fun setUp() { override fun setUp() {
@@ -12,16 +12,13 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
import org.jetbrains.kotlin.script.loadScriptingPlugin import org.jetbrains.kotlin.script.loadScriptingPlugin
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinitionAdapterFromNewAPI
import org.jetbrains.kotlin.test.ConfigurationKind import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.TestJdkKind import org.jetbrains.kotlin.test.TestJdkKind
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
import java.io.File import java.io.File
import kotlin.script.experimental.api.KotlinType
import kotlin.script.experimental.host.ScriptingHostConfiguration import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.host.createCompilationConfigurationFromTemplate
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
private const val testDataPath = "compiler/testData/script/collectDependencies" private const val testDataPath = "compiler/testData/script/collectDependencies"
@@ -58,15 +55,14 @@ class CollectScriptCompilationDependenciesTest : KtUsefulTestCase() {
private fun runTest(scriptFile: String, expectedDependencies: List<String>, classPath: List<File> = emptyList()) { private fun runTest(scriptFile: String, expectedDependencies: List<String>, classPath: List<File> = emptyList()) {
val configuration = KotlinTestUtils.newConfiguration(ConfigurationKind.NO_KOTLIN_REFLECT, TestJdkKind.MOCK_JDK).apply { val configuration = KotlinTestUtils.newConfiguration(ConfigurationKind.NO_KOTLIN_REFLECT, TestJdkKind.MOCK_JDK).apply {
val hostConfiguration = ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) add(
val scriptDefinition = KotlinScriptDefinitionAdapterFromNewAPI( ScriptingConfigurationKeys.SCRIPT_DEFINITIONS,
createCompilationConfigurationFromTemplate( ScriptDefinition.FromTemplate(
KotlinType(TestScriptWithRequire::class), ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration),
hostConfiguration, KotlinScriptDefinition::class TestScriptWithRequire::class,
), ScriptDefinition::class
hostConfiguration )
) )
add(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition)
addKotlinSourceRoot(File(testDataPath, scriptFile).path) addKotlinSourceRoot(File(testDataPath, scriptFile).path)
@@ -15,8 +15,7 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
import org.jetbrains.kotlin.daemon.TestMessageCollector import org.jetbrains.kotlin.daemon.TestMessageCollector
import org.jetbrains.kotlin.script.loadScriptingPlugin import org.jetbrains.kotlin.script.loadScriptingPlugin
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinitionAdapterFromNewAPI
import org.jetbrains.kotlin.test.ConfigurationKind import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.TestJdkKind import org.jetbrains.kotlin.test.TestJdkKind
@@ -27,10 +26,10 @@ import java.io.File
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.script.experimental.annotations.KotlinScript import kotlin.script.experimental.annotations.KotlinScript
import kotlin.script.experimental.api.* import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.FileBasedScriptSource
import kotlin.script.experimental.host.FileScriptSource import kotlin.script.experimental.host.FileScriptSource
import kotlin.script.experimental.host.ScriptingHostConfiguration import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.host.configurationDependencies import kotlin.script.experimental.host.configurationDependencies
import kotlin.script.experimental.host.createCompilationConfigurationFromTemplate
import kotlin.script.experimental.jvm.* import kotlin.script.experimental.jvm.*
private const val testDataPath = "compiler/testData/script/cliCompilation" private const val testDataPath = "compiler/testData/script/cliCompilation"
@@ -75,14 +74,10 @@ class ScriptCliCompilationTest : KtUsefulTestCase() {
val hostConfiguration = ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) { val hostConfiguration = ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) {
configurationDependencies(JvmDependency(classpath)) configurationDependencies(JvmDependency(classpath))
} }
val scriptDefinition = KotlinScriptDefinitionAdapterFromNewAPI( add(
createCompilationConfigurationFromTemplate( ScriptingConfigurationKeys.SCRIPT_DEFINITIONS,
KotlinType(scriptDef), ScriptDefinition.FromTemplate(hostConfiguration, scriptDef, ScriptDefinition::class)
hostConfiguration, KotlinScriptDefinition::class
),
hostConfiguration
) )
add(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition)
} }
loadScriptingPlugin(this) loadScriptingPlugin(this)
} }
@@ -122,7 +117,7 @@ object TestScriptWithRequireConfiguration : ScriptCompilationConfiguration(
} }
refineConfiguration { refineConfiguration {
onAnnotations(Import::class, DependsOn::class) { context: ScriptConfigurationRefinementContext -> onAnnotations(Import::class, DependsOn::class) { context: ScriptConfigurationRefinementContext ->
val scriptBaseDir = (context.script as? FileScriptSource)?.file?.parentFile val scriptBaseDir = (context.script as? FileBasedScriptSource)?.file?.parentFile
val sources = context.collectedData?.get(ScriptCollectedData.foundAnnotations) val sources = context.collectedData?.get(ScriptCollectedData.foundAnnotations)
?.flatMap { ?.flatMap {
(it as? Import)?.sources?.map { sourceName -> (it as? Import)?.sources?.map { sourceName ->
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.daemon.toFile
import org.jetbrains.kotlin.script.loadScriptingPlugin import org.jetbrains.kotlin.script.loadScriptingPlugin
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.resolve.InvalidScriptResolverAnnotation import org.jetbrains.kotlin.scripting.resolve.InvalidScriptResolverAnnotation
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
import org.jetbrains.kotlin.test.ConfigurationKind import org.jetbrains.kotlin.test.ConfigurationKind
@@ -50,6 +51,7 @@ import kotlin.reflect.KClass
import kotlin.script.dependencies.* import kotlin.script.dependencies.*
import kotlin.script.experimental.dependencies.* import kotlin.script.experimental.dependencies.*
import kotlin.script.experimental.dependencies.DependenciesResolver.ResolveResult import kotlin.script.experimental.dependencies.DependenciesResolver.ResolveResult
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.templates.AcceptedAnnotations import kotlin.script.templates.AcceptedAnnotations
import kotlin.script.templates.ScriptTemplateDefinition import kotlin.script.templates.ScriptTemplateDefinition
import kotlin.script.templates.standard.ScriptTemplateWithArgs import kotlin.script.templates.standard.ScriptTemplateWithArgs
@@ -348,7 +350,13 @@ class ScriptTemplateTest : KtUsefulTestCase() {
*additionalClasspath) *additionalClasspath)
configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector) configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector)
configuration.addKotlinSourceRoot(scriptPath) configuration.addKotlinSourceRoot(scriptPath)
configuration.add(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition) configuration.add(
ScriptingConfigurationKeys.SCRIPT_DEFINITIONS,
ScriptDefinition.FromLegacy(
defaultJvmScriptingHostConfiguration,
scriptDefinition
)
)
configuration.put(JVMConfigurationKeys.DISABLE_STANDARD_SCRIPT_DEFINITION, true) configuration.put(JVMConfigurationKeys.DISABLE_STANDARD_SCRIPT_DEFINITION, true)
configuration.put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true) configuration.put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true)
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.script.loadScriptingPlugin import org.jetbrains.kotlin.script.loadScriptingPlugin
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.StandardScriptDefinition import org.jetbrains.kotlin.scripting.definitions.StandardScriptDefinition
import org.jetbrains.kotlin.test.ConfigurationKind import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.KotlinTestUtils
@@ -37,6 +38,7 @@ import org.jetbrains.kotlin.utils.tryConstructClassFromStringArgs
import org.junit.Assert import org.junit.Assert
import java.io.File import java.io.File
import java.net.URLClassLoader import java.net.URLClassLoader
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
class ScriptTest : KtUsefulTestCase() { class ScriptTest : KtUsefulTestCase() {
fun testStandardScriptWithParams() { fun testStandardScriptWithParams() {
@@ -102,7 +104,13 @@ class ScriptTest : KtUsefulTestCase() {
val configuration = KotlinTestUtils.newConfiguration(ConfigurationKind.ALL, TestJdkKind.FULL_JDK) val configuration = KotlinTestUtils.newConfiguration(ConfigurationKind.ALL, TestJdkKind.FULL_JDK)
configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector) configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector)
configuration.addKotlinSourceRoot("compiler/testData/script/$scriptPath") configuration.addKotlinSourceRoot("compiler/testData/script/$scriptPath")
configuration.add(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition) configuration.add(
ScriptingConfigurationKeys.SCRIPT_DEFINITIONS,
ScriptDefinition.FromLegacy(
defaultJvmScriptingHostConfiguration,
scriptDefinition
)
)
configuration.put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true) configuration.put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true)
if (saveClassesDir != null) { if (saveClassesDir != null) {
configuration.put(JVMConfigurationKeys.OUTPUT_DIRECTORY, saveClassesDir) configuration.put(JVMConfigurationKeys.OUTPUT_DIRECTORY, saveClassesDir)
@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesManager
import org.jetbrains.kotlin.idea.core.script.dependencies.ScriptAdditionalIdeaDependenciesProvider import org.jetbrains.kotlin.idea.core.script.dependencies.ScriptAdditionalIdeaDependenciesProvider
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices
import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices
data class ScriptModuleInfo( data class ScriptModuleInfo(
val project: Project, val project: Project,
val scriptFile: VirtualFile, val scriptFile: VirtualFile,
val scriptDefinition: KotlinScriptDefinition val scriptDefinition: ScriptDefinition
) : IdeaModuleInfo { ) : IdeaModuleInfo {
override val moduleOrigin: ModuleOrigin override val moduleOrigin: ModuleOrigin
get() = ModuleOrigin.OTHER get() = ModuleOrigin.OTHER
@@ -85,7 +85,7 @@ sealed class ScriptDependenciesInfo(val project: Project) : IdeaModuleInfo, Bina
class ForFile( class ForFile(
project: Project, project: Project,
val scriptFile: VirtualFile, val scriptFile: VirtualFile,
val scriptDefinition: KotlinScriptDefinition val scriptDefinition: ScriptDefinition
) : ScriptDependenciesInfo(project) { ) : ScriptDependenciesInfo(project) {
override val sdk: Sdk? override val sdk: Sdk?
get() { get() {
@@ -27,7 +27,7 @@ import org.jetbrains.kotlin.idea.util.isInSourceContentWithoutInjected
import org.jetbrains.kotlin.idea.util.isKotlinBinary import org.jetbrains.kotlin.idea.util.isKotlinBinary
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.scripting.definitions.scriptDefinitionByFileName import org.jetbrains.kotlin.scripting.definitions.findScriptDefinitionByFileName
import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.sure import org.jetbrains.kotlin.utils.sure
import org.jetbrains.kotlin.utils.yieldIfNotNull import org.jetbrains.kotlin.utils.yieldIfNotNull
@@ -174,7 +174,7 @@ private fun <T> PsiElement.collectInfos(c: ModuleInfoCollector<T>): T {
return c.onResult(it) return c.onResult(it)
} }
containingKtFile.script?.let { containingKtFile.script?.let {
val definition = scriptDefinitionByFileName(project, containingKtFile.name) val definition = findScriptDefinitionByFileName(project, containingKtFile.name)
return c.onResult(ScriptModuleInfo(project, virtualFile, definition)) return c.onResult(ScriptModuleInfo(project, virtualFile, definition))
} }
} }
@@ -37,7 +37,7 @@ import org.jetbrains.kotlin.idea.caches.project.*
import org.jetbrains.kotlin.idea.project.getLanguageVersionSettings import org.jetbrains.kotlin.idea.project.getLanguageVersionSettings
import org.jetbrains.kotlin.idea.project.languageVersionSettings import org.jetbrains.kotlin.idea.project.languageVersionSettings
import org.jetbrains.kotlin.idea.project.platform import org.jetbrains.kotlin.idea.project.platform
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.platform.jvm.JdkPlatform import org.jetbrains.kotlin.platform.jvm.JdkPlatform
import org.jetbrains.kotlin.platform.subplatformOfType import org.jetbrains.kotlin.platform.subplatformOfType
import org.jetbrains.kotlin.utils.Jsr305State import org.jetbrains.kotlin.utils.Jsr305State
@@ -92,8 +92,8 @@ private data class ScriptLanguageSettings(
private val SCRIPT_LANGUAGE_SETTINGS = Key.create<CachedValue<ScriptLanguageSettings>>("SCRIPT_LANGUAGE_SETTINGS") private val SCRIPT_LANGUAGE_SETTINGS = Key.create<CachedValue<ScriptLanguageSettings>>("SCRIPT_LANGUAGE_SETTINGS")
private fun getLanguageSettingsForScripts(project: Project, scriptDefinition: KotlinScriptDefinition): ScriptLanguageSettings { private fun getLanguageSettingsForScripts(project: Project, scriptDefinition: ScriptDefinition): ScriptLanguageSettings {
val args = scriptDefinition.additionalCompilerArguments val args = scriptDefinition.compilerOptions
return if (args == null || args.none()) { return if (args == null || args.none()) {
ScriptLanguageSettings(project.getLanguageVersionSettings(), TargetPlatformVersion.NoVersion) ScriptLanguageSettings(project.getLanguageVersionSettings(), TargetPlatformVersion.NoVersion)
} else { } else {
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.idea.caches.project.NotUnderContentRootModuleInfo
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
import org.jetbrains.kotlin.idea.core.script.IdeScriptReportSink import org.jetbrains.kotlin.idea.core.script.IdeScriptReportSink
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager
import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesUpdater import org.jetbrains.kotlin.idea.core.script.ScriptsCompilationConfigurationUpdater
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.idea.util.isRunningInCidrIde import org.jetbrains.kotlin.idea.util.isRunningInCidrIde
import org.jetbrains.kotlin.psi.KtCodeFragment import org.jetbrains.kotlin.psi.KtCodeFragment
@@ -70,7 +70,7 @@ object KotlinHighlightingUtil {
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
private fun shouldHighlightScript(ktFile: KtFile): Boolean { private fun shouldHighlightScript(ktFile: KtFile): Boolean {
if (isRunningInCidrIde) return false // There is no Java support in CIDR. So do not highlight errors in KTS if running in CIDR. if (isRunningInCidrIde) return false // There is no Java support in CIDR. So do not highlight errors in KTS if running in CIDR.
if (!ScriptDependenciesUpdater.areDependenciesCached(ktFile)) return false if (!ScriptsCompilationConfigurationUpdater.areDependenciesCached(ktFile)) return false
if (ktFile.virtualFile.getUserData(IdeScriptReportSink.Reports)?.any { it.severity == ScriptReport.Severity.FATAL } == true) { if (ktFile.virtualFile.getUserData(IdeScriptReportSink.Reports)?.any { it.severity == ScriptReport.Severity.FATAL } == true) {
return false return false
} }
@@ -39,9 +39,8 @@ import com.intellij.util.ui.UIUtil
import org.jetbrains.kotlin.idea.core.script.IdeScriptReportSink import org.jetbrains.kotlin.idea.core.script.IdeScriptReportSink
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager
import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesManager import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesManager
import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesUpdater import org.jetbrains.kotlin.idea.core.script.ScriptsCompilationConfigurationUpdater
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import kotlin.script.experimental.dependencies.ScriptDependencies
import kotlin.script.experimental.dependencies.ScriptReport import kotlin.script.experimental.dependencies.ScriptReport
class ScriptExternalHighlightingPass( class ScriptExternalHighlightingPass(
@@ -62,9 +61,10 @@ class ScriptExternalHighlightingPass(
) )
} }
if (!ScriptDependenciesUpdater.areDependenciesCached(file)) { if (!ScriptsCompilationConfigurationUpdater.areDependenciesCached(file)) {
val scriptDependencies = ScriptDependenciesManager.getInstance(file.project).getScriptDependencies(file.virtualFile) // initiate configuration refinement, if needed
if (scriptDependencies == ScriptDependencies.Empty) { ScriptDependenciesManager.getInstance(file.project).getRefinedCompilationConfiguration(file.virtualFile)
if (!ScriptsCompilationConfigurationUpdater.areDependenciesCached(file)) {
showNotification( showNotification(
file, file,
"Highlighting in scripts is not available until all Script Dependencies are loaded" "Highlighting in scripts is not available until all Script Dependencies are loaded"
@@ -33,7 +33,7 @@ import org.jetbrains.kotlin.psi.KtPsiFactoryKt;
import org.jetbrains.kotlin.platform.TargetPlatform; import org.jetbrains.kotlin.platform.TargetPlatform;
import org.jetbrains.kotlin.platform.SimplePlatform; import org.jetbrains.kotlin.platform.SimplePlatform;
import org.jetbrains.kotlin.scripting.definitions.DefinitionsKt; import org.jetbrains.kotlin.scripting.definitions.DefinitionsKt;
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition; import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition;
public class TargetPlatformDetector { public class TargetPlatformDetector {
public static final TargetPlatformDetector INSTANCE = new TargetPlatformDetector(); public static final TargetPlatformDetector INSTANCE = new TargetPlatformDetector();
@@ -62,7 +62,7 @@ public class TargetPlatformDetector {
} }
if (file.isScript()) { if (file.isScript()) {
KotlinScriptDefinition scriptDefinition = DefinitionsKt.scriptDefinition(file); ScriptDefinition scriptDefinition = DefinitionsKt.findScriptDefinition(file);
if (scriptDefinition != null) { if (scriptDefinition != null) {
String platformNameFromScriptDefinition = scriptDefinition.getPlatform(); String platformNameFromScriptDefinition = scriptDefinition.getPlatform();
for (TargetPlatform compilerPlatform : IdePlatformKind.Companion.getAll_PLATFORMS()) { for (TargetPlatform compilerPlatform : IdePlatformKind.Companion.getAll_PLATFORMS()) {
@@ -71,7 +71,8 @@ object ProjectRootsUtil {
): Boolean { ): Boolean {
val scriptDefinition = file.findScriptDefinition(project) val scriptDefinition = file.findScriptDefinition(project)
if (scriptDefinition != null) { if (scriptDefinition != null) {
val scriptScope = scriptDefinition.scriptExpectedLocations // TODO: rewrite to ScriptAcceptedLocation and without legacyDefinition
val scriptScope = scriptDefinition.legacyDefinition.scriptExpectedLocations
val includeAll = scriptScope.contains(ScriptExpectedLocation.Everywhere) val includeAll = scriptScope.contains(ScriptExpectedLocation.Everywhere)
|| scriptScope.contains(ScriptExpectedLocation.Project) || scriptScope.contains(ScriptExpectedLocation.Project)
|| ScratchUtil.isScratch(file) || ScratchUtil.isScratch(file)
@@ -115,7 +116,8 @@ object ProjectRootsUtil {
if (ProjectRootManager.getInstance(project).fileIndex.isInContent(file) || ScratchUtil.isScratch(file)) { if (ProjectRootManager.getInstance(project).fileIndex.isInContent(file) || ScratchUtil.isScratch(file)) {
return true return true
} }
return file.findScriptDefinition(project)?.scriptExpectedLocations?.contains(ScriptExpectedLocation.Everywhere) == true // TODO: rewrite to ScriptAcceptedLocation and without legacyDefinition
return file.findScriptDefinition(project)?.legacyDefinition?.scriptExpectedLocations?.contains(ScriptExpectedLocation.Everywhere) == true
} }
if (!includeLibraryClasses && !includeLibrarySource) return false if (!includeLibraryClasses && !includeLibrarySource) return false
@@ -39,11 +39,7 @@ import org.jetbrains.kotlin.idea.caches.project.SdkInfo
import org.jetbrains.kotlin.idea.caches.project.getScriptRelatedModuleInfo import org.jetbrains.kotlin.idea.caches.project.getScriptRelatedModuleInfo
import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings
import org.jetbrains.kotlin.script.ScriptTemplatesProvider import org.jetbrains.kotlin.script.ScriptTemplatesProvider
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition import org.jetbrains.kotlin.scripting.definitions.*
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinitionAdapterFromNewAPI
import org.jetbrains.kotlin.scripting.definitions.LazyScriptDefinitionProvider
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
import org.jetbrains.kotlin.utils.PathUtil import org.jetbrains.kotlin.utils.PathUtil
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.utils.addToStdlib.flattenTo import org.jetbrains.kotlin.utils.addToStdlib.flattenTo
@@ -53,35 +49,33 @@ import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.write import kotlin.concurrent.write
import kotlin.script.dependencies.Environment import kotlin.script.dependencies.Environment
import kotlin.script.dependencies.ScriptContents import kotlin.script.dependencies.ScriptContents
import kotlin.script.experimental.api.KotlinType
import kotlin.script.experimental.dependencies.DependenciesResolver import kotlin.script.experimental.dependencies.DependenciesResolver
import kotlin.script.experimental.dependencies.ScriptDependencies import kotlin.script.experimental.dependencies.ScriptDependencies
import kotlin.script.experimental.dependencies.asSuccess import kotlin.script.experimental.dependencies.asSuccess
import kotlin.script.experimental.host.ScriptingHostConfiguration import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.host.configurationDependencies import kotlin.script.experimental.host.configurationDependencies
import kotlin.script.experimental.host.createCompilationConfigurationFromTemplate
import kotlin.script.experimental.jvm.JvmDependency import kotlin.script.experimental.jvm.JvmDependency
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.experimental.jvm.util.scriptCompilationClasspathFromContextOrStdlib import kotlin.script.experimental.jvm.util.scriptCompilationClasspathFromContextOrStdlib
import kotlin.script.templates.standard.ScriptTemplateWithArgs import kotlin.script.templates.standard.ScriptTemplateWithArgs
class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinitionProvider() { class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinitionProvider() {
private var definitionsByContributor = mutableMapOf<ScriptDefinitionContributor, List<KotlinScriptDefinition>>() private var definitionsBySource = mutableMapOf<ScriptDefinitionsSource, List<ScriptDefinition>>()
private var definitions: List<KotlinScriptDefinition>? = null private var definitions: List<ScriptDefinition>? = null
private val failedContributorsHashes = HashSet<Int>() private val failedContributorsHashes = HashSet<Int>()
private val scriptDefinitionsCacheLock = ReentrantReadWriteLock() private val scriptDefinitionsCacheLock = ReentrantReadWriteLock()
private val scriptDefinitionsCache = SLRUMap<String, KotlinScriptDefinition>(10, 10) private val scriptDefinitionsCache = SLRUMap<String, ScriptDefinition>(10, 10)
override fun findScriptDefinition(fileName: String): KotlinScriptDefinition? { override fun findDefinition(fileName: String): ScriptDefinition? {
if (nonScriptFileName(fileName)) return null if (nonScriptFileName(fileName)) return null
if (!isReady()) return null if (!isReady()) return null
val cached = scriptDefinitionsCacheLock.write { scriptDefinitionsCache.get(fileName) } val cached = scriptDefinitionsCacheLock.write { scriptDefinitionsCache.get(fileName) }
if (cached != null) return cached if (cached != null) return cached
val definition = super.findScriptDefinition(fileName) ?: return null val definition = super.findDefinition(fileName) ?: return null
scriptDefinitionsCacheLock.write { scriptDefinitionsCacheLock.write {
scriptDefinitionsCache.put(fileName, definition) scriptDefinitionsCache.put(fileName, definition)
@@ -90,48 +84,43 @@ class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinit
return definition return definition
} }
fun reloadDefinitionsBy(contributor: ScriptDefinitionContributor) = lock.write { override fun findScriptDefinition(fileName: String): KotlinScriptDefinition? = findDefinition(fileName)?.legacyDefinition
fun reloadDefinitionsBy(source: ScriptDefinitionsSource) = lock.write {
if (definitions == null) return // not loaded yet if (definitions == null) return // not loaded yet
if (contributor !in definitionsByContributor) error("Unknown contributor: ${contributor.id}") if (source !in definitionsBySource) error("Unknown script definition source: $source")
definitionsByContributor[contributor] = contributor.safeGetDefinitions() definitionsBySource[source] = source.safeGetDefinitions()
definitions = definitionsByContributor.values.flattenTo(mutableListOf()) definitions = definitionsBySource.values.flattenTo(mutableListOf())
updateDefinitions() updateDefinitions()
} }
fun getDefinitionsBy(contributor: ScriptDefinitionContributor): List<KotlinScriptDefinition> = lock.write { override val currentDefinitions
if (definitions == null) return emptyList() // not loaded yet
if (contributor !in definitionsByContributor) error("Unknown contributor: ${contributor.id}")
return definitionsByContributor[contributor] ?: emptyList()
}
override val currentDefinitions: Sequence<KotlinScriptDefinition>
get() = get() =
(definitions ?: kotlin.run { (definitions ?: kotlin.run {
reloadScriptDefinitions() reloadScriptDefinitions()
definitions!! definitions!!
}).asSequence().filter { KotlinScriptingSettings.getInstance(project).isScriptDefinitionEnabled(it) } }).asSequence().filter { KotlinScriptingSettings.getInstance(project).isScriptDefinitionEnabled(it) }
private fun getContributors(): List<ScriptDefinitionContributor> { private fun getSources(): List<ScriptDefinitionsSource> {
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
val fromDeprecatedEP = Extensions.getArea(project).getExtensionPoint(ScriptTemplatesProvider.EP_NAME).extensions.toList() val fromDeprecatedEP = Extensions.getArea(project).getExtensionPoint(ScriptTemplatesProvider.EP_NAME).extensions.toList()
.map(::ScriptTemplatesProviderAdapter) .map { ScriptTemplatesProviderAdapter(it).asSource() }
val fromNewEp = Extensions.getArea(project).getExtensionPoint(ScriptDefinitionContributor.EP_NAME).extensions.toList() val fromNewEp = Extensions.getArea(project).getExtensionPoint(ScriptDefinitionContributor.EP_NAME).extensions.toList()
.map { it.asSource() }
return fromNewEp.dropLast(1) + fromDeprecatedEP + fromNewEp.last() return fromNewEp.dropLast(1) + fromDeprecatedEP + fromNewEp.last()
} }
fun reloadScriptDefinitions() = lock.write { fun reloadScriptDefinitions() = lock.write {
for (contributor in getContributors()) { for (source in getSources()) {
val definitions = contributor.safeGetDefinitions() val definitions = source.safeGetDefinitions()
definitionsByContributor[contributor] = definitions definitionsBySource[source] = definitions
} }
definitions = definitionsByContributor.values.flattenTo(mutableListOf()) definitions = definitionsBySource.values.flattenTo(mutableListOf())
updateDefinitions() updateDefinitions()
} }
@@ -140,7 +129,7 @@ class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinit
updateDefinitions() updateDefinitions()
} }
fun getAllDefinitions(): List<KotlinScriptDefinition> { fun getAllDefinitions(): List<ScriptDefinition> {
return definitions ?: kotlin.run { return definitions ?: kotlin.run {
reloadScriptDefinitions() reloadScriptDefinitions()
definitions!! definitions!!
@@ -148,15 +137,16 @@ class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinit
} }
fun isReady(): Boolean { fun isReady(): Boolean {
return definitionsByContributor.keys.all { contributor -> return definitionsBySource.keys.all { source ->
contributor.isReady() // TODO: implement another API for readiness checking
(source as? ScriptDefinitionContributor)?.isReady() != false
} }
} }
override fun getDefaultScriptDefinition(): KotlinScriptDefinition { override fun getDefaultDefinition(): ScriptDefinition {
val standardScriptDefinitionContributor = ScriptDefinitionContributor.find<StandardScriptDefinitionContributor>(project) val standardScriptDefinitionContributor = ScriptDefinitionContributor.find<StandardScriptDefinitionContributor>(project)
?: error("StandardScriptDefinitionContributor should be registered is plugin.xml") ?: error("StandardScriptDefinitionContributor should be registered is plugin.xml")
return standardScriptDefinitionContributor.getDefinitions().last() return ScriptDefinition.FromLegacy(getScriptingHostConfiguration(), standardScriptDefinitionContributor.getDefinitions().last())
} }
private fun updateDefinitions() { private fun updateDefinitions() {
@@ -187,7 +177,7 @@ class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinit
scriptDefinitionsCacheLock.write { scriptDefinitionsCache.clear() } scriptDefinitionsCacheLock.write { scriptDefinitionsCache.clear() }
// TODO: clear by script type/definition // TODO: clear by script type/definition
ServiceManager.getService(project, ScriptDependenciesCache::class.java).clear() ServiceManager.getService(project, ScriptsCompilationConfigurationCache::class.java).clear()
ApplicationManager.getApplication().invokeLater { ApplicationManager.getApplication().invokeLater {
if (!project.isDisposed) { if (!project.isDisposed) {
@@ -196,9 +186,9 @@ class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinit
} }
} }
private fun ScriptDefinitionContributor.safeGetDefinitions(): List<KotlinScriptDefinition> { private fun ScriptDefinitionsSource.safeGetDefinitions(): List<ScriptDefinition> {
if (!failedContributorsHashes.contains(this@safeGetDefinitions.hashCode())) try { if (!failedContributorsHashes.contains(this@safeGetDefinitions.hashCode())) try {
return getDefinitions() return definitions.toList()
} catch (t: Throwable) { } catch (t: Throwable) {
// reporting failed loading only once // reporting failed loading only once
LOG.error("[kts] cannot load script definitions using $this", t) LOG.error("[kts] cannot load script definitions using $this", t)
@@ -207,6 +197,15 @@ class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinit
return emptyList() return emptyList()
} }
@Suppress("unused") // used in the 182/as33 bunches
fun getDefinitionsBy(source: ScriptDefinitionsSource): List<ScriptDefinition> = lock.write {
if (definitions == null) return emptyList() // not loaded yet
if (source !in definitionsBySource) error("Unknown source: ${source::class.java.name}")
return definitionsBySource[source] ?: emptyList()
}
companion object { companion object {
fun getInstance(project: Project): ScriptDefinitionsManager = fun getInstance(project: Project): ScriptDefinitionsManager =
ServiceManager.getService(project, ScriptDefinitionProvider::class.java) as ScriptDefinitionsManager ServiceManager.getService(project, ScriptDefinitionProvider::class.java) as ScriptDefinitionsManager
@@ -216,10 +215,11 @@ class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinit
private val LOG = Logger.getInstance("ScriptTemplatesProviders") private val LOG = Logger.getInstance("ScriptTemplatesProviders")
// TODO: consider rewriting to return sequence
fun loadDefinitionsFromTemplates( fun loadDefinitionsFromTemplates(
templateClassNames: List<String>, templateClassNames: List<String>,
templateClasspath: List<File>, templateClasspath: List<File>,
environment: Environment = emptyMap(), baseHostConfiguration: ScriptingHostConfiguration,
// TODO: need to provide a way to specify this in compiler/repl .. etc // TODO: need to provide a way to specify this in compiler/repl .. etc
/* /*
* Allows to specify additional jars needed for DependenciesResolver (and not script template). * Allows to specify additional jars needed for DependenciesResolver (and not script template).
@@ -227,7 +227,7 @@ fun loadDefinitionsFromTemplates(
* i.e. gradle resolver may depend on some jars that 'built.gradle.kts' files should not depend on. * i.e. gradle resolver may depend on some jars that 'built.gradle.kts' files should not depend on.
*/ */
additionalResolverClasspath: List<File> = emptyList() additionalResolverClasspath: List<File> = emptyList()
): List<KotlinScriptDefinition> { ): List<ScriptDefinition> {
val classpath = templateClasspath + additionalResolverClasspath val classpath = templateClasspath + additionalResolverClasspath
LOG.info("[kts] loading script definitions $templateClassNames using cp: ${classpath.joinToString(File.pathSeparator)}") LOG.info("[kts] loading script definitions $templateClassNames using cp: ${classpath.joinToString(File.pathSeparator)}")
val baseLoader = ScriptDefinitionContributor::class.java.classLoader val baseLoader = ScriptDefinitionContributor::class.java.classLoader
@@ -240,24 +240,13 @@ fun loadDefinitionsFromTemplates(
val template = loader.loadClass(templateClassName).kotlin val template = loader.loadClass(templateClassName).kotlin
when { when {
template.annotations.firstIsInstanceOrNull<kotlin.script.templates.ScriptTemplateDefinition>() != null -> { template.annotations.firstIsInstanceOrNull<kotlin.script.templates.ScriptTemplateDefinition>() != null -> {
KotlinScriptDefinitionFromAnnotatedTemplate( ScriptDefinition.FromLegacyTemplate(baseHostConfiguration, template, templateClasspath)
template,
environment,
templateClasspath
)
} }
template.annotations.firstIsInstanceOrNull<kotlin.script.experimental.annotations.KotlinScript>() != null -> { template.annotations.firstIsInstanceOrNull<kotlin.script.experimental.annotations.KotlinScript>() != null -> {
val hostConfiguration = ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) { val hostConfiguration = ScriptingHostConfiguration(baseHostConfiguration) {
configurationDependencies(JvmDependency(classpath)) configurationDependencies(JvmDependency(classpath))
} }
KotlinScriptDefinitionAdapterFromNewAPI( ScriptDefinition.FromTemplate(hostConfiguration, template, ScriptDefinition::class)
createCompilationConfigurationFromTemplate(
KotlinType(
template
), hostConfiguration, KotlinScriptDefinition::class
),
hostConfiguration
)
} }
else -> { else -> {
LOG.warn("[kts] cannot find a valid script definition annotation on the class $template") LOG.warn("[kts] cannot find a valid script definition annotation on the class $template")
@@ -276,10 +265,16 @@ fun loadDefinitionsFromTemplates(
} }
} }
@Deprecated("migrating to new configuration refinement: use ScriptDefinitionsSource internally and kotlin.script.experimental.intellij.ScriptDefinitionsProvider as a providing extension point")
interface ScriptDefinitionContributor { interface ScriptDefinitionContributor {
@Deprecated("migrating to new configuration refinement: drop usages")
val id: String val id: String
@Deprecated("migrating to new configuration refinement: use ScriptDefinitionsSource instead")
fun getDefinitions(): List<KotlinScriptDefinition> fun getDefinitions(): List<KotlinScriptDefinition>
@Deprecated("migrating to new configuration refinement: drop usages")
fun isReady() = true fun isReady() = true
companion object { companion object {
@@ -289,9 +284,29 @@ interface ScriptDefinitionContributor {
inline fun <reified T> find(project: Project) = inline fun <reified T> find(project: Project) =
Extensions.getArea(project).getExtensionPoint(ScriptDefinitionContributor.EP_NAME).extensions.filterIsInstance<T>().firstOrNull() Extensions.getArea(project).getExtensionPoint(ScriptDefinitionContributor.EP_NAME).extensions.filterIsInstance<T>().firstOrNull()
} }
} }
@Deprecated("migrating to new configuration refinement: use ScriptDefinitionsSource directly instead")
interface ScriptDefinitionSourceAsContributor : ScriptDefinitionContributor, ScriptDefinitionsSource {
override fun getDefinitions(): List<KotlinScriptDefinition> = definitions.map { it.legacyDefinition }.toList()
}
@Deprecated("migrating to new configuration refinement: convert all contributors to ScriptDefinitionsSource/ScriptDefinitionsProvider")
class ScriptDefinitionSourceFromContributor(
val contributor: ScriptDefinitionContributor,
val hostConfiguration: ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration
) : ScriptDefinitionsSource {
override val definitions: Sequence<ScriptDefinition>
get() =
if (contributor is ScriptDefinitionsSource) contributor.definitions
else contributor.getDefinitions().asSequence().map { ScriptDefinition.FromLegacy(hostConfiguration, it) }
}
fun ScriptDefinitionContributor.asSource(): ScriptDefinitionsSource =
if (this is ScriptDefinitionsSource) this
else ScriptDefinitionSourceFromContributor(this)
class StandardScriptDefinitionContributor(project: Project) : ScriptDefinitionContributor { class StandardScriptDefinitionContributor(project: Project) : ScriptDefinitionContributor {
private val standardIdeScriptDefinition = StandardIdeScriptDefinition(project) private val standardIdeScriptDefinition = StandardIdeScriptDefinition(project)
@@ -18,8 +18,9 @@ package org.jetbrains.kotlin.idea.core.script
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.idea.core.util.* import org.jetbrains.kotlin.idea.core.util.*
import java.io.DataInput import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
import java.io.DataOutput import java.io.*
import kotlin.script.experimental.api.ScriptCompilationConfiguration
import kotlin.script.experimental.dependencies.ScriptDependencies import kotlin.script.experimental.dependencies.ScriptDependencies
var VirtualFile.scriptDependencies: ScriptDependencies? by cachedFileAttribute( var VirtualFile.scriptDependencies: ScriptDependencies? by cachedFileAttribute(
@@ -43,4 +44,27 @@ var VirtualFile.scriptDependencies: ScriptDependencies? by cachedFileAttribute(
writeFileList(sources) writeFileList(sources)
} }
} }
) )
var VirtualFile.scriptCompilationConfiguration: ScriptCompilationConfiguration? by cachedFileAttribute(
name = "kotlin-script-compilation-configuration",
version = 1,
read = {
val size = readInt()
val bytes = ByteArray(size)
read(bytes, 0, size)
val bis = ByteArrayInputStream(bytes)
ObjectInputStream(bis).use { ois ->
ois.readObject() as ScriptCompilationConfiguration
}
},
write = {
val os = ByteArrayOutputStream()
ObjectOutputStream(os).use { oos ->
oos.writeObject((it as? ScriptCompilationConfigurationWrapper.FromCompilationConfiguration)?.configuration)
}
val bytes = os.toByteArray()
writeInt(bytes.size)
write(bytes)
}
)
@@ -30,8 +30,10 @@ import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.idea.caches.project.getAllProjectSdks import org.jetbrains.kotlin.idea.caches.project.getAllProjectSdks
import org.jetbrains.kotlin.idea.core.script.dependencies.SyncScriptDependenciesLoader import org.jetbrains.kotlin.idea.core.script.dependencies.SyncScriptDependenciesLoader
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationResult
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
import java.io.File import java.io.File
import kotlin.script.experimental.dependencies.ScriptDependencies import kotlin.script.experimental.api.valueOrNull
// NOTE: this service exists exclusively because ScriptDependencyManager // NOTE: this service exists exclusively because ScriptDependencyManager
@@ -39,18 +41,21 @@ import kotlin.script.experimental.dependencies.ScriptDependencies
class IdeScriptDependenciesProvider( class IdeScriptDependenciesProvider(
private val scriptDependenciesManager: ScriptDependenciesManager private val scriptDependenciesManager: ScriptDependenciesManager
) : ScriptDependenciesProvider { ) : ScriptDependenciesProvider {
override fun getScriptDependencies(file: VirtualFile): ScriptDependencies? { override fun getScriptConfigurationResult(file: VirtualFile): ScriptCompilationConfigurationResult? = scriptDependenciesManager.getRefinedCompilationConfiguration(file)
return scriptDependenciesManager.getScriptDependencies(file)
}
} }
// TODO: rename and provide alias for compatibility - this is not only about dependencies anymore
class ScriptDependenciesManager internal constructor( class ScriptDependenciesManager internal constructor(
private val cacheUpdater: ScriptDependenciesUpdater, private val cacheUpdater: ScriptsCompilationConfigurationUpdater,
private val cache: ScriptDependenciesCache private val cache: ScriptsCompilationConfigurationCache
) { ) {
fun getScriptClasspath(file: VirtualFile): List<VirtualFile> = toVfsRoots(cacheUpdater.getCurrentDependencies(file).classpath) fun getScriptClasspath(file: VirtualFile): List<VirtualFile> =
fun getScriptDependencies(file: VirtualFile): ScriptDependencies = cacheUpdater.getCurrentDependencies(file) toVfsRoots(cacheUpdater.getCurrentCompilationConfiguration(file)?.valueOrNull()?.dependenciesClassPath.orEmpty())
fun getScriptSdk(file: VirtualFile): Sdk? = getScriptSdk(getScriptDependencies(file))
fun getRefinedCompilationConfiguration(file: VirtualFile): ScriptCompilationConfigurationResult? =
cacheUpdater.getCurrentCompilationConfiguration(file)
fun getScriptSdk(file: VirtualFile): Sdk? = Companion.getScriptSdk(getRefinedCompilationConfiguration(file)?.valueOrNull())
fun getScriptDependenciesClassFilesScope(file: VirtualFile) = cache.scriptDependenciesClassFilesScope(file) fun getScriptDependenciesClassFilesScope(file: VirtualFile) = cache.scriptDependenciesClassFilesScope(file)
@@ -67,35 +72,29 @@ class ScriptDependenciesManager internal constructor(
fun getInstance(project: Project): ScriptDependenciesManager = fun getInstance(project: Project): ScriptDependenciesManager =
ServiceManager.getService(project, ScriptDependenciesManager::class.java) ServiceManager.getService(project, ScriptDependenciesManager::class.java)
fun getScriptSdk(dependencies: ScriptDependencies): Sdk? { fun getScriptSdk(compilationConfiguration: ScriptCompilationConfigurationWrapper?): Sdk? {
// workaround for mismatched gradle wrapper and plugin version // workaround for mismatched gradle wrapper and plugin version
try { val javaHome = try {
val javaHome = dependencies.javaHome compilationConfiguration?.javaHome?.let { VfsUtil.findFileByIoFile(it, true) }
?.let { VfsUtil.findFileByIoFile(it, true) }
?: return null
return getAllProjectSdks().find { it.homeDirectory == javaHome }
} catch (e: Throwable) { } catch (e: Throwable) {
return null null
} } ?: return null
return getAllProjectSdks().find { it.homeDirectory == javaHome }
} }
fun getScriptDefaultSdk(project: Project): Sdk? {
val projectSdk = getProjectSdk(project)
if (projectSdk != null) return projectSdk
val anyJavaSdk = getAllProjectSdks().find { it.canBeUsedForScript() } fun getScriptDefaultSdk(project: Project): Sdk? =
if (anyJavaSdk != null) { getProjectSdk(project)
return anyJavaSdk ?: getAllProjectSdks().find { it.canBeUsedForScript() }
} ?: run {
log.warn(
log.warn( "Default Script SDK is null: " +
"Default Script SDK is null: " + "projectSdk = ${ProjectRootManager.getInstance(project).projectSdk}, " +
"projectSdk = ${ProjectRootManager.getInstance(project).projectSdk}, " + "all sdks = ${getAllProjectSdks().joinToString("; ")}"
"all sdks = ${getAllProjectSdks().joinToString("\n")}" )
) null
return null }
}
fun getProjectSdk(project: Project) = ProjectRootManager.getInstance(project).projectSdk?.takeIf { it.canBeUsedForScript() } fun getProjectSdk(project: Project) = ProjectRootManager.getInstance(project).projectSdk?.takeIf { it.canBeUsedForScript() }
@@ -16,7 +16,7 @@ import com.intellij.ui.EditorNotificationPanel
import com.intellij.ui.HyperlinkLabel import com.intellij.ui.HyperlinkLabel
import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings
import org.jetbrains.kotlin.psi.UserDataProperty import org.jetbrains.kotlin.psi.UserDataProperty
import kotlin.script.experimental.dependencies.DependenciesResolver.ResolveResult import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationResult
fun VirtualFile.removeScriptDependenciesNotificationPanel(project: Project) { fun VirtualFile.removeScriptDependenciesNotificationPanel(project: Project) {
withSelectedEditor(project) { manager -> withSelectedEditor(project) { manager ->
@@ -28,14 +28,14 @@ fun VirtualFile.removeScriptDependenciesNotificationPanel(project: Project) {
} }
fun VirtualFile.addScriptDependenciesNotificationPanel( fun VirtualFile.addScriptDependenciesNotificationPanel(
resolveResult: ResolveResult, compilationConfigurationResult: ScriptCompilationConfigurationResult?,
project: Project, project: Project,
onClick: (ResolveResult) -> Unit onClick: (ScriptCompilationConfigurationResult?) -> Unit
) { ) {
withSelectedEditor(project) { manager -> withSelectedEditor(project) { manager ->
val existingPanel = notificationPanel val existingPanel = notificationPanel
if (existingPanel != null) { if (existingPanel != null) {
if (existingPanel.resolveResult.dependencies == resolveResult.dependencies) { if (existingPanel.compilationConfigurationResult == compilationConfigurationResult) {
return@withSelectedEditor return@withSelectedEditor
} }
notificationPanel?.let { notificationPanel?.let {
@@ -43,7 +43,7 @@ fun VirtualFile.addScriptDependenciesNotificationPanel(
} }
} }
val panel = NewScriptDependenciesNotificationPanel(onClick, resolveResult, project) val panel = NewScriptDependenciesNotificationPanel(onClick, compilationConfigurationResult, project)
notificationPanel = panel notificationPanel = panel
manager.addTopComponent(this, panel) manager.addTopComponent(this, panel)
} }
@@ -61,19 +61,19 @@ private fun VirtualFile.withSelectedEditor(project: Project, f: FileEditor.(File
private var FileEditor.notificationPanel: NewScriptDependenciesNotificationPanel? by UserDataProperty<FileEditor, NewScriptDependenciesNotificationPanel>(Key.create("script.dependencies.panel")) private var FileEditor.notificationPanel: NewScriptDependenciesNotificationPanel? by UserDataProperty<FileEditor, NewScriptDependenciesNotificationPanel>(Key.create("script.dependencies.panel"))
private class NewScriptDependenciesNotificationPanel( private class NewScriptDependenciesNotificationPanel(
onClick: (ResolveResult) -> Unit, onClick: (ScriptCompilationConfigurationResult?) -> Unit,
val resolveResult: ResolveResult, val compilationConfigurationResult: ScriptCompilationConfigurationResult?,
project: Project project: Project
) : EditorNotificationPanel() { ) : EditorNotificationPanel() {
init { init {
setText("There are new script dependencies available.") setText("There is a new script context available.")
createComponentActionLabel("Apply dependencies") { createComponentActionLabel("Apply context") {
onClick(resolveResult) onClick(compilationConfigurationResult)
} }
createComponentActionLabel("Enable auto-reload") { createComponentActionLabel("Enable auto-reload") {
onClick(resolveResult) onClick(compilationConfigurationResult)
KotlinScriptingSettings.getInstance(project).isAutoReloadEnabled = true KotlinScriptingSettings.getInstance(project).isAutoReloadEnabled = true
} }
} }
@@ -6,17 +6,25 @@
package org.jetbrains.kotlin.idea.core.script package org.jetbrains.kotlin.idea.core.script
import org.jetbrains.kotlin.script.ScriptTemplatesProvider import org.jetbrains.kotlin.script.ScriptTemplatesProvider
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.getEnvironment
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
class ScriptTemplatesProviderAdapter(private val templatesProvider: ScriptTemplatesProvider) : ScriptDefinitionSourceAsContributor {
class ScriptTemplatesProviderAdapter(private val templatesProvider: ScriptTemplatesProvider) :
ScriptDefinitionContributor {
override val id: String override val id: String
get() = templatesProvider.id get() = templatesProvider.id
override fun getDefinitions(): List<KotlinScriptDefinition> { override val definitions: Sequence<ScriptDefinition>
return loadDefinitionsFromTemplates( get() =
templatesProvider.templateClassNames.toList(), templatesProvider.templateClasspath, loadDefinitionsFromTemplates(
templatesProvider.environment.orEmpty(), templatesProvider.additionalResolverClasspath templatesProvider.templateClassNames.toList(), templatesProvider.templateClasspath,
) ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) {
} getEnvironment {
templatesProvider.environment
}
},
templatesProvider.additionalResolverClasspath
).asSequence()
} }
@@ -29,15 +29,17 @@ import com.intellij.util.containers.SLRUMap
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.jetbrains.kotlin.idea.core.util.EDT import org.jetbrains.kotlin.idea.core.util.EDT
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationResult
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
import java.util.concurrent.locks.ReentrantReadWriteLock import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.write import kotlin.concurrent.write
import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
import kotlin.reflect.KProperty0 import kotlin.reflect.KProperty0
import kotlin.reflect.jvm.isAccessible import kotlin.reflect.jvm.isAccessible
import kotlin.script.experimental.dependencies.ScriptDependencies import kotlin.script.experimental.api.valueOrNull
class ScriptDependenciesCache(private val project: Project) { class ScriptsCompilationConfigurationCache(private val project: Project) {
companion object { companion object {
const val MAX_SCRIPTS_CACHED = 50 const val MAX_SCRIPTS_CACHED = 50
@@ -45,10 +47,10 @@ class ScriptDependenciesCache(private val project: Project) {
private val cacheLock = ReentrantReadWriteLock() private val cacheLock = ReentrantReadWriteLock()
private val scriptDependenciesCache = SLRUCacheWithLock<ScriptDependencies>() private val scriptDependenciesCache = SLRUCacheWithLock<ScriptCompilationConfigurationResult>()
private val scriptsModificationStampsCache = SLRUCacheWithLock<Long>() private val scriptsModificationStampsCache = SLRUCacheWithLock<Long>()
operator fun get(virtualFile: VirtualFile): ScriptDependencies? = scriptDependenciesCache.get(virtualFile) operator fun get(virtualFile: VirtualFile): ScriptCompilationConfigurationResult? = scriptDependenciesCache.get(virtualFile)
fun shouldRunDependenciesUpdate(file: VirtualFile): Boolean { fun shouldRunDependenciesUpdate(file: VirtualFile): Boolean {
return scriptsModificationStampsCache.replace(file, file.modificationStamp) != file.modificationStamp return scriptsModificationStampsCache.replace(file, file.modificationStamp) != file.modificationStamp
@@ -58,10 +60,10 @@ class ScriptDependenciesCache(private val project: Project) {
fun scriptDependenciesClassFilesScope(file: VirtualFile): GlobalSearchScope { fun scriptDependenciesClassFilesScope(file: VirtualFile): GlobalSearchScope {
return scriptsDependenciesClasspathScopeCache.getOrPut(file) { return scriptsDependenciesClasspathScopeCache.getOrPut(file) {
val dependencies = scriptDependenciesCache.get(file) ?: return@getOrPut GlobalSearchScope.EMPTY_SCOPE val compilationConfiguration = scriptDependenciesCache.get(file)?.valueOrNull() ?: return@getOrPut GlobalSearchScope.EMPTY_SCOPE
val roots = dependencies.classpath val roots = compilationConfiguration.dependenciesClassPath
val sdk = ScriptDependenciesManager.getScriptSdk(dependencies) val sdk = ScriptDependenciesManager.getScriptSdk(compilationConfiguration)
@Suppress("FoldInitializerAndIfToElvis") @Suppress("FoldInitializerAndIfToElvis")
if (sdk == null) { if (sdk == null) {
@@ -86,7 +88,8 @@ class ScriptDependenciesCache(private val project: Project) {
.filter { it != ScriptDependenciesManager.getProjectSdk(project) } .filter { it != ScriptDependenciesManager.getProjectSdk(project) }
.flatMap { it.rootProvider.getFiles(OrderRootType.CLASSES).toList() } .flatMap { it.rootProvider.getFiles(OrderRootType.CLASSES).toList() }
val scriptDependenciesClasspath = scriptDependenciesCache.getAll().flatMap { it.value.classpath }.distinct() val scriptDependenciesClasspath = scriptDependenciesCache.getAll()
.flatMap { it.value.valueOrNull()?.dependenciesClassPath ?: emptyList() }.distinct()
sdkFiles + ScriptDependenciesManager.toVfsRoots(scriptDependenciesClasspath) sdkFiles + ScriptDependenciesManager.toVfsRoots(scriptDependenciesClasspath)
} }
@@ -96,7 +99,8 @@ class ScriptDependenciesCache(private val project: Project) {
.filter { it != ScriptDependenciesManager.getProjectSdk(project) } .filter { it != ScriptDependenciesManager.getProjectSdk(project) }
.flatMap { it.rootProvider.getFiles(OrderRootType.SOURCES).toList() } .flatMap { it.rootProvider.getFiles(OrderRootType.SOURCES).toList() }
val scriptDependenciesSources = scriptDependenciesCache.getAll().flatMap { it.value.sources }.distinct() val scriptDependenciesSources = scriptDependenciesCache.getAll()
.flatMap { it.value.valueOrNull()?.dependenciesSources ?: emptyList() }.distinct()
sdkSources + ScriptDependenciesManager.toVfsRoots(scriptDependenciesSources) sdkSources + ScriptDependenciesManager.toVfsRoots(scriptDependenciesSources)
} }
@@ -140,11 +144,10 @@ class ScriptDependenciesCache(private val project: Project) {
} }
} }
fun hasNotCachedRoots(scriptDependencies: ScriptDependencies): Boolean { fun hasNotCachedRoots(compilationConfiguration: ScriptCompilationConfigurationWrapper): Boolean {
return !allSdks.contains(ScriptDependenciesManager.getScriptSdk(scriptDependencies)) || return !allSdks.contains(ScriptDependenciesManager.getScriptSdk(compilationConfiguration)) ||
!allDependenciesClassFiles.containsAll(ScriptDependenciesManager.toVfsRoots(scriptDependencies.classpath)) || !allDependenciesClassFiles.containsAll(ScriptDependenciesManager.toVfsRoots(compilationConfiguration.dependenciesClassPath)) ||
!allDependenciesSources.containsAll(ScriptDependenciesManager.toVfsRoots(scriptDependencies.sources)) !allDependenciesSources.containsAll(ScriptDependenciesManager.toVfsRoots(compilationConfiguration.dependenciesSources))
} }
fun clear() { fun clear() {
@@ -155,7 +158,7 @@ class ScriptDependenciesCache(private val project: Project) {
onChange(keys) onChange(keys)
} }
fun save(virtualFile: VirtualFile, new: ScriptDependencies): Boolean { fun save(virtualFile: VirtualFile, new: ScriptCompilationConfigurationResult): Boolean {
val old = scriptDependenciesCache.replace(virtualFile, new) val old = scriptDependenciesCache.replace(virtualFile, new)
val changed = new != old val changed = new != old
if (changed) { if (changed) {
@@ -207,8 +210,8 @@ private class SLRUCacheWithLock<T> {
private val lock = ReentrantReadWriteLock() private val lock = ReentrantReadWriteLock()
val cache = SLRUMap<VirtualFile, T>( val cache = SLRUMap<VirtualFile, T>(
ScriptDependenciesCache.MAX_SCRIPTS_CACHED, ScriptsCompilationConfigurationCache.MAX_SCRIPTS_CACHED,
ScriptDependenciesCache.MAX_SCRIPTS_CACHED ScriptsCompilationConfigurationCache.MAX_SCRIPTS_CACHED
) )
fun get(value: VirtualFile): T? = lock.write { fun get(value: VirtualFile): T? = lock.write {
@@ -41,14 +41,15 @@ import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.NotNullableUserDataProperty import org.jetbrains.kotlin.psi.NotNullableUserDataProperty
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
import org.jetbrains.kotlin.scripting.resolve.LegacyResolverWrapper import org.jetbrains.kotlin.scripting.resolve.LegacyResolverWrapper
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationResult
import kotlin.script.experimental.dependencies.AsyncDependenciesResolver import kotlin.script.experimental.dependencies.AsyncDependenciesResolver
import kotlin.script.experimental.dependencies.ScriptDependencies
class ScriptDependenciesUpdater( class ScriptsCompilationConfigurationUpdater(
private val project: Project, private val project: Project,
private val cache: ScriptDependenciesCache private val cache: ScriptsCompilationConfigurationCache
) { ) {
private val scriptsQueue = Alarm(Alarm.ThreadToUse.SWING_THREAD, project) private val scriptsQueue = Alarm(Alarm.ThreadToUse.SWING_THREAD, project)
private val scriptChangesListenerDelay = 1400 private val scriptChangesListenerDelay = 1400
@@ -64,13 +65,13 @@ class ScriptDependenciesUpdater(
listenForChangesInScripts() listenForChangesInScripts()
} }
fun getCurrentDependencies(file: VirtualFile): ScriptDependencies { fun getCurrentCompilationConfiguration(file: VirtualFile): ScriptCompilationConfigurationResult? {
cache[file]?.let { return it } cache[file]?.let { return it }
updateDependencies(file) updateDependencies(file)
makeRootsChangeIfNeeded() makeRootsChangeIfNeeded()
return cache[file] ?: ScriptDependencies.Empty return cache[file]
} }
fun updateDependenciesIfNeeded(files: List<VirtualFile>): Boolean { fun updateDependenciesIfNeeded(files: List<VirtualFile>): Boolean {
@@ -174,15 +175,15 @@ class ScriptDependenciesUpdater(
return cache[file] != null || file.scriptDependencies != null return cache[file] != null || file.scriptDependencies != null
} }
fun isAsyncDependencyResolver(scriptDef: KotlinScriptDefinition): Boolean { fun isAsyncDependencyResolver(scriptDef: ScriptDefinition): Boolean =
val dependencyResolver = scriptDef.dependencyResolver scriptDef.asLegacyOrNull<KotlinScriptDefinition>()?.dependencyResolver?.let {
return dependencyResolver is AsyncDependenciesResolver || dependencyResolver is LegacyResolverWrapper it is AsyncDependenciesResolver || it is LegacyResolverWrapper
} } ?: false
companion object { companion object {
@JvmStatic @JvmStatic
fun getInstance(project: Project): ScriptDependenciesUpdater = fun getInstance(project: Project): ScriptsCompilationConfigurationUpdater =
ServiceManager.getService(project, ScriptDependenciesUpdater::class.java) ServiceManager.getService(project, ScriptsCompilationConfigurationUpdater::class.java)
fun areDependenciesCached(file: KtFile): Boolean { fun areDependenciesCached(file: KtFile): Boolean {
return getInstance(file.project).areDependenciesCached(file.virtualFile) return getInstance(file.project).areDependenciesCached(file.virtualFile)
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.idea.core.script.dependencies package org.jetbrains.kotlin.idea.core.script.dependencies
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.progress.EmptyProgressIndicator import com.intellij.openapi.progress.EmptyProgressIndicator
import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.Task import com.intellij.openapi.progress.Task
@@ -13,18 +12,16 @@ import com.intellij.openapi.progress.util.BackgroundTaskUtil
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import kotlinx.coroutines.Runnable import kotlinx.coroutines.Runnable
import kotlinx.coroutines.runBlocking import org.jetbrains.kotlin.idea.core.script.ScriptsCompilationConfigurationUpdater
import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesUpdater
import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
import org.jetbrains.kotlin.scripting.resolve.asResolveFailure import org.jetbrains.kotlin.scripting.resolve.VirtualFileScriptSource
import org.jetbrains.kotlin.scripting.resolve.refineScriptCompilationConfiguration
import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.locks.ReentrantReadWriteLock import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.write import kotlin.concurrent.write
import kotlin.script.experimental.dependencies.AsyncDependenciesResolver
import kotlin.script.experimental.dependencies.DependenciesResolver
// TODO: rename and provide alias for compatibility - this is not only about dependencies anymore
class AsyncScriptDependenciesLoader internal constructor(project: Project) : ScriptDependenciesLoader(project) { class AsyncScriptDependenciesLoader internal constructor(project: Project) : ScriptDependenciesLoader(project) {
private val lock = ReentrantReadWriteLock() private val lock = ReentrantReadWriteLock()
@@ -33,7 +30,7 @@ class AsyncScriptDependenciesLoader internal constructor(project: Project) : Scr
override fun isApplicable(file: VirtualFile): Boolean { override fun isApplicable(file: VirtualFile): Boolean {
val scriptDefinition = file.findScriptDefinition(project) ?: return false val scriptDefinition = file.findScriptDefinition(project) ?: return false
return ScriptDependenciesUpdater.getInstance(project).isAsyncDependencyResolver(scriptDefinition) return ScriptsCompilationConfigurationUpdater.getInstance(project).isAsyncDependencyResolver(scriptDefinition)
} }
override fun loadDependencies(file: VirtualFile) { override fun loadDependencies(file: VirtualFile) {
@@ -73,35 +70,15 @@ class AsyncScriptDependenciesLoader internal constructor(project: Project) : Scr
} }
private fun runDependenciesUpdate(file: VirtualFile) { private fun runDependenciesUpdate(file: VirtualFile) {
val scriptDef = runReadAction { file.findScriptDefinition(project) } ?: return val scriptDef = file.findScriptDefinition(project) ?: return
debug(file) { "start async dependencies loading" } debug(file) { "start async dependencies loading" }
// runBlocking is using there to avoid loading dependencies asynchronously
// because it leads to starting more than one gradle daemon in case of resolving dependencies in build.gradle.kts val result = refineScriptCompilationConfiguration(VirtualFileScriptSource(file), scriptDef, project)
// It is more efficient to use one hot daemon consistently than multiple daemon in parallel
val result = runBlocking {
try {
resolveDependencies(file, scriptDef)
} catch (t: Throwable) {
t.asResolveFailure(scriptDef)
}
}
debug(file) { "finish async dependencies loading" } debug(file) { "finish async dependencies loading" }
processResult(result, file, scriptDef) processRefinedConfiguration(result, file)
}
private suspend fun resolveDependencies(file: VirtualFile, scriptDef: KotlinScriptDefinition): DependenciesResolver.ResolveResult {
val dependenciesResolver = scriptDef.dependencyResolver
val scriptContents = contentLoader.getScriptContents(scriptDef, file)
val environment = contentLoader.getEnvironment(scriptDef)
return if (dependenciesResolver is AsyncDependenciesResolver) {
dependenciesResolver.resolveAsync(scriptContents, environment)
} else {
// TODO: shouldn't come here after dropping legacy resolvers, refactor accordingly
dependenciesResolver.resolve(scriptContents, environment)
}
} }
private inner class LoaderBackgroundTask { private inner class LoaderBackgroundTask {
@@ -7,8 +7,13 @@ package org.jetbrains.kotlin.idea.core.script.dependencies
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.idea.core.script.scriptCompilationConfiguration
import org.jetbrains.kotlin.idea.core.script.scriptDependencies import org.jetbrains.kotlin.idea.core.script.scriptDependencies
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
import org.jetbrains.kotlin.scripting.resolve.VirtualFileScriptSource
import kotlin.script.experimental.api.asSuccess
// TODO: rename and provide alias for compatibility - this is not only about dependencies anymore
class FromFileAttributeScriptDependenciesLoader(project: Project) : ScriptDependenciesLoader(project) { class FromFileAttributeScriptDependenciesLoader(project: Project) : ScriptDependenciesLoader(project) {
override fun isApplicable(file: VirtualFile): Boolean { override fun isApplicable(file: VirtualFile): Boolean {
@@ -16,9 +21,17 @@ class FromFileAttributeScriptDependenciesLoader(project: Project) : ScriptDepend
} }
override fun loadDependencies(file: VirtualFile) { override fun loadDependencies(file: VirtualFile) {
val deserializedDependencies = file.scriptDependencies ?: return file.scriptCompilationConfiguration?.let {
debug(file) { "dependencies from fileAttributes = $deserializedDependencies" } ScriptCompilationConfigurationWrapper.FromCompilationConfiguration(VirtualFileScriptSource(file), it).apply {
saveToCache(file, deserializedDependencies) debug(file) { "refined configuration from fileAttributes = $it" }
}
} ?: file.scriptDependencies?.let {
ScriptCompilationConfigurationWrapper.FromLegacy(VirtualFileScriptSource(file), it).apply {
debug(file) { "dependencies from fileAttributes = $it" }
}
}?.let {
saveToCache(file, it.asSuccess(), skipSaveToAttributes = true)
}
} }
override fun shouldShowNotification(): Boolean = false override fun shouldShowNotification(): Boolean = false
@@ -22,6 +22,7 @@ import com.intellij.psi.ResolveScopeProvider
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesManager import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesManager
import org.jetbrains.kotlin.idea.core.script.StandardIdeScriptDefinition import org.jetbrains.kotlin.idea.core.script.StandardIdeScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
@@ -36,11 +37,13 @@ class KotlinScriptResolveScopeProvider : ResolveScopeProvider() {
// TODO: this should get this particular scripts dependencies // TODO: this should get this particular scripts dependencies
return when { return when {
scriptDefinition == null -> null scriptDefinition == null -> null
// This is a workaround for completion in scripts and REPL to provide module dependencies // This is a workaround for completion in scripts and REPL to provide module dependencies
scriptDefinition.template == Any::class -> null scriptDefinition.baseClassType.fromClass == Any::class -> null
scriptDefinition is StandardIdeScriptDefinition -> null scriptDefinition.asLegacyOrNull<StandardIdeScriptDefinition>() != null -> null
scriptDefinition is KotlinScriptDefinitionFromAnnotatedTemplate -> // TODO: should include the file itself scriptDefinition is ScriptDefinition.FromConfigurations || scriptDefinition.asLegacyOrNull<KotlinScriptDefinitionFromAnnotatedTemplate>() != null -> {
// TODO: should include the file itself
ScriptDependenciesManager.getInstance(project).getAllScriptsDependenciesClassFilesScope() ScriptDependenciesManager.getInstance(project).getAllScriptsDependenciesClassFilesScope()
}
else -> null else -> null
} }
} }
@@ -18,9 +18,8 @@ class OutsiderFileDependenciesLoader(project: Project) : ScriptDependenciesLoade
override fun loadDependencies(file: VirtualFile) { override fun loadDependencies(file: VirtualFile) {
val fileOrigin = OutsidersPsiFileSupportUtils.getOutsiderFileOrigin(project, file) ?: return val fileOrigin = OutsidersPsiFileSupportUtils.getOutsiderFileOrigin(project, file) ?: return
val originDependencies = ScriptDependenciesManager.getInstance(project).getScriptDependencies(fileOrigin) val compilationConfiguration = ScriptDependenciesManager.getInstance(project).getRefinedCompilationConfiguration(fileOrigin) ?: return
debug(file) { "dependencies for outsider file = $originDependencies" } saveToCache(file, compilationConfiguration)
saveToCache(file, originDependencies)
} }
override fun shouldShowNotification(): Boolean = false override fun shouldShowNotification(): Boolean = false
@@ -15,13 +15,14 @@ import com.intellij.openapi.util.EmptyRunnable
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.idea.core.script.* import org.jetbrains.kotlin.idea.core.script.*
import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationResult
import org.jetbrains.kotlin.scripting.resolve.ScriptContentLoader import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
import org.jetbrains.kotlin.scripting.resolve.ScriptReportSink import org.jetbrains.kotlin.scripting.resolve.ScriptReportSink
import org.jetbrains.kotlin.scripting.resolve.adjustByDefinition import kotlin.script.experimental.api.ResultWithDiagnostics
import kotlin.script.experimental.dependencies.DependenciesResolver import kotlin.script.experimental.api.valueOrNull
import kotlin.script.experimental.dependencies.ScriptDependencies import kotlin.script.experimental.jvm.compat.mapToLegacyReports
// TODO: rename and provide alias for compatibility - this is not only about dependencies anymore
abstract class ScriptDependenciesLoader(protected val project: Project) { abstract class ScriptDependenciesLoader(protected val project: Project) {
abstract fun isApplicable(file: VirtualFile): Boolean abstract fun isApplicable(file: VirtualFile): Boolean
@@ -31,40 +32,40 @@ abstract class ScriptDependenciesLoader(protected val project: Project) {
protected var shouldNotifyRootsChanged = false protected var shouldNotifyRootsChanged = false
protected val contentLoader = ScriptContentLoader(project) protected val cache: ScriptsCompilationConfigurationCache = ServiceManager.getService(project, ScriptsCompilationConfigurationCache::class.java)
protected val cache: ScriptDependenciesCache = ServiceManager.getService(project, ScriptDependenciesCache::class.java)
private val reporter: ScriptReportSink = ServiceManager.getService(project, ScriptReportSink::class.java) private val reporter: ScriptReportSink = ServiceManager.getService(project, ScriptReportSink::class.java)
protected fun processResult(result: DependenciesResolver.ResolveResult, file: VirtualFile, scriptDef: KotlinScriptDefinition) { protected fun processRefinedConfiguration(result: ScriptCompilationConfigurationResult, file: VirtualFile) {
debug(file) { "dependencies from ${this.javaClass} received = $result" } debug(file) { "refined script compilation configuration from ${this.javaClass} received = $result" }
if (cache[file] == null) { val oldResult = cache[file]
saveDependencies(result, file, scriptDef)
attachReportsIfChanged(result, file, scriptDef) if (oldResult == null) {
save(result, file)
attachReportsIfChanged(result, file)
return return
} }
val newDependencies = result.dependencies?.adjustByDefinition(scriptDef) if (oldResult != result) {
if (cache[file] != newDependencies) {
if (shouldShowNotification() && !ApplicationManager.getApplication().isUnitTestMode) { if (shouldShowNotification() && !ApplicationManager.getApplication().isUnitTestMode) {
debug(file) { debug(file) {
"dependencies changed, notification was shown: old = ${cache[file]}, new = $newDependencies" "dependencies changed, notification is shown: old = $oldResult, new = $result"
} }
file.addScriptDependenciesNotificationPanel(result, project) { file.addScriptDependenciesNotificationPanel(result, project) {
saveDependencies(it, file, scriptDef) save(it, file)
attachReportsIfChanged(it, file, scriptDef) attachReportsIfChanged(result, file)
submitMakeRootsChange() submitMakeRootsChange()
} }
} else { } else {
debug(file) { debug(file) {
"dependencies changed, new dependencies were applied automatically: old = ${cache[file]}, new = $newDependencies" "dependencies changed, new dependencies are applied automatically: old = $oldResult, new = $result"
} }
saveDependencies(result, file, scriptDef) save(result, file)
attachReportsIfChanged(result, file, scriptDef) attachReportsIfChanged(result, file)
} }
} else { } else {
attachReportsIfChanged(result, file, scriptDef) attachReportsIfChanged(result, file)
if (shouldShowNotification()) { if (shouldShowNotification()) {
file.removeScriptDependenciesNotificationPanel(project) file.removeScriptDependenciesNotificationPanel(project)
@@ -72,28 +73,36 @@ abstract class ScriptDependenciesLoader(protected val project: Project) {
} }
} }
private fun attachReportsIfChanged(result: DependenciesResolver.ResolveResult, file: VirtualFile, scriptDef: KotlinScriptDefinition) { private fun attachReportsIfChanged(result: ResultWithDiagnostics<*>, file: VirtualFile) {
if (file.getUserData(IdeScriptReportSink.Reports) != result.reports.takeIf { it.isNotEmpty() }) { if (file.getUserData(IdeScriptReportSink.Reports) != result.reports.takeIf { it.isNotEmpty() }) {
reporter.attachReports(file, result.reports) reporter.attachReports(file, result.reports.mapToLegacyReports())
} }
} }
private fun saveDependencies(result: DependenciesResolver.ResolveResult, file: VirtualFile, scriptDef: KotlinScriptDefinition) { private fun save(compilationConfigurationResult: ScriptCompilationConfigurationResult?, file: VirtualFile) {
if (shouldShowNotification()) { if (shouldShowNotification()) {
file.removeScriptDependenciesNotificationPanel(project) file.removeScriptDependenciesNotificationPanel(project)
} }
if (compilationConfigurationResult != null) {
val dependencies = result.dependencies?.adjustByDefinition(scriptDef) ?: return saveToCache(file, compilationConfigurationResult)
saveToCache(file, dependencies) }
} }
protected fun saveToCache(file: VirtualFile, dependencies: ScriptDependencies) { protected fun saveToCache(
val rootsChanged = cache.hasNotCachedRoots(dependencies) file: VirtualFile, compilationConfigurationResult: ScriptCompilationConfigurationResult, skipSaveToAttributes: Boolean = false
if (cache.save(file, dependencies)) { ) {
val rootsChanged = compilationConfigurationResult.valueOrNull()?.let { cache.hasNotCachedRoots(it) } ?: false
if (cache.save(file, compilationConfigurationResult)
&& !skipSaveToAttributes
&& compilationConfigurationResult is ResultWithDiagnostics.Success
) {
debug(file) { debug(file) {
"dependencies were saved to file attributes: dependencies = $dependencies" "refined configuration is saved to file attributes: $compilationConfigurationResult"
} }
file.scriptDependencies = dependencies if (compilationConfigurationResult.value is ScriptCompilationConfigurationWrapper.FromLegacy)
file.scriptDependencies = compilationConfigurationResult.value.legacyDependencies
else
file.scriptCompilationConfiguration = compilationConfigurationResult.value.configuration
} }
if (rootsChanged) { if (rootsChanged) {
@@ -101,6 +110,7 @@ abstract class ScriptDependenciesLoader(protected val project: Project) {
} }
} }
open fun notifyRootsChanged(): Boolean = submitMakeRootsChange() open fun notifyRootsChanged(): Boolean = submitMakeRootsChange()
protected fun submitMakeRootsChange(): Boolean { protected fun submitMakeRootsChange(): Boolean {
@@ -7,21 +7,23 @@ package org.jetbrains.kotlin.idea.core.script.dependencies
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesUpdater import org.jetbrains.kotlin.idea.core.script.ScriptsCompilationConfigurationUpdater
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
import org.jetbrains.kotlin.scripting.resolve.VirtualFileScriptSource
import org.jetbrains.kotlin.scripting.resolve.refineScriptCompilationConfiguration
class SyncScriptDependenciesLoader internal constructor(project: Project) : ScriptDependenciesLoader(project) { class SyncScriptDependenciesLoader(project: Project) : ScriptDependenciesLoader(project) {
override fun isApplicable(file: VirtualFile): Boolean { override fun isApplicable(file: VirtualFile): Boolean {
val scriptDefinition = file.findScriptDefinition(project) ?: return false val scriptDefinition = file.findScriptDefinition(project) ?: return false
return !ScriptDependenciesUpdater.getInstance(project).isAsyncDependencyResolver(scriptDefinition) return !ScriptsCompilationConfigurationUpdater.getInstance(project).isAsyncDependencyResolver(scriptDefinition)
} }
override fun loadDependencies(file: VirtualFile) { override fun loadDependencies(file: VirtualFile) {
val scriptDef = file.findScriptDefinition(project) ?: return val scriptDef = file.findScriptDefinition(project) ?: return
debug(file) { "start sync dependencies loading" } debug(file) { "start sync dependencies loading" }
val result = contentLoader.loadContentsAndResolveDependencies(scriptDef, file) val result = refineScriptCompilationConfiguration(VirtualFileScriptSource(file), scriptDef, project)
debug(file) { "finish sync dependencies loading" } debug(file) { "finish sync dependencies loading" }
processResult(result, file, scriptDef) processRefinedConfiguration(result, file)
} }
override fun shouldShowNotification(): Boolean = false override fun shouldShowNotification(): Boolean = false
@@ -14,7 +14,7 @@ import com.intellij.util.addOptionTag
import com.intellij.util.attribute import com.intellij.util.attribute
import com.intellij.util.getAttributeBooleanValue import com.intellij.util.getAttributeBooleanValue
import org.jdom.Element import org.jdom.Element
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
@State( @State(
name = "KotlinScriptingSettings", name = "KotlinScriptingSettings",
@@ -72,22 +72,22 @@ class KotlinScriptingSettings : PersistentStateComponent<Element> {
} }
} }
fun setOrder(scriptDefinition: KotlinScriptDefinition, order: Int) { fun setOrder(scriptDefinition: ScriptDefinition, order: Int) {
scriptDefinitions[scriptDefinition.toKey()] = scriptDefinitions[scriptDefinition.toKey()]?.copy(order = order) ?: scriptDefinitions[scriptDefinition.toKey()] = scriptDefinitions[scriptDefinition.toKey()]?.copy(order = order) ?:
KotlinScriptDefinitionValue(order) KotlinScriptDefinitionValue(order)
} }
fun setEnabled(scriptDefinition: KotlinScriptDefinition, isEnabled: Boolean) { fun setEnabled(scriptDefinition: ScriptDefinition, isEnabled: Boolean) {
scriptDefinitions[scriptDefinition.toKey()] = scriptDefinitions[scriptDefinition.toKey()]?.copy(isEnabled = isEnabled) ?: scriptDefinitions[scriptDefinition.toKey()] = scriptDefinitions[scriptDefinition.toKey()]?.copy(isEnabled = isEnabled) ?:
KotlinScriptDefinitionValue(scriptDefinitions.size, isEnabled) KotlinScriptDefinitionValue(scriptDefinitions.size, isEnabled)
} }
fun getScriptDefinitionOrder(scriptDefinition: KotlinScriptDefinition): Int { fun getScriptDefinitionOrder(scriptDefinition: ScriptDefinition): Int {
return scriptDefinitions[scriptDefinition.toKey()]?.order ?: Integer.MAX_VALUE return scriptDefinitions[scriptDefinition.toKey()]?.order ?: Integer.MAX_VALUE
} }
fun isScriptDefinitionEnabled(scriptDefinition: KotlinScriptDefinition): Boolean { fun isScriptDefinitionEnabled(scriptDefinition: ScriptDefinition): Boolean {
return scriptDefinitions[scriptDefinition.toKey()]?.isEnabled ?: true return scriptDefinitions[scriptDefinition.toKey()]?.isEnabled ?: true
} }
@@ -99,8 +99,8 @@ class KotlinScriptingSettings : PersistentStateComponent<Element> {
getAttributeValue(KotlinScriptDefinitionKey::className.name) getAttributeValue(KotlinScriptDefinitionKey::className.name)
) )
private fun KotlinScriptDefinition.toKey() = private fun ScriptDefinition.toKey() =
KotlinScriptDefinitionKey(this.name, this::class.qualifiedName ?: "unknown") KotlinScriptDefinitionKey(this.name, this.definitionId)
private fun Element.addScriptDefinitionContentElement(definition: KotlinScriptDefinitionKey, settings: KotlinScriptDefinitionValue) { private fun Element.addScriptDefinitionContentElement(definition: KotlinScriptDefinitionKey, settings: KotlinScriptDefinitionValue) {
addElement(SCRIPT_DEFINITION_TAG).apply { addElement(SCRIPT_DEFINITION_TAG).apply {
@@ -27,8 +27,9 @@ import com.intellij.util.EnvironmentUtil
import org.jetbrains.kotlin.idea.framework.GRADLE_SYSTEM_ID import org.jetbrains.kotlin.idea.framework.GRADLE_SYSTEM_ID
import org.jetbrains.kotlin.lexer.KotlinLexer import org.jetbrains.kotlin.lexer.KotlinLexer
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinitionAdapterFromNewAPIBase import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinitionAdapterFromNewAPIBase
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.getEnvironment
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
import org.jetbrains.plugins.gradle.config.GradleSettingsListenerAdapter import org.jetbrains.plugins.gradle.config.GradleSettingsListenerAdapter
import org.jetbrains.plugins.gradle.service.execution.GradleExecutionHelper import org.jetbrains.plugins.gradle.service.execution.GradleExecutionHelper
@@ -38,10 +39,8 @@ import org.jetbrains.plugins.gradle.settings.GradleProjectSettings
import org.jetbrains.plugins.gradle.settings.GradleSettingsListener import org.jetbrains.plugins.gradle.settings.GradleSettingsListener
import org.jetbrains.plugins.gradle.util.GradleConstants import org.jetbrains.plugins.gradle.util.GradleConstants
import java.io.File import java.io.File
import java.lang.IllegalStateException
import java.util.* import java.util.*
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
import kotlin.collections.LinkedHashSet
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.script.dependencies.Environment import kotlin.script.dependencies.Environment
import kotlin.script.dependencies.ScriptContents import kotlin.script.dependencies.ScriptContents
@@ -54,7 +53,7 @@ import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.experimental.location.ScriptExpectedLocation import kotlin.script.experimental.location.ScriptExpectedLocation
import kotlin.script.templates.standard.ScriptTemplateWithArgs import kotlin.script.templates.standard.ScriptTemplateWithArgs
class GradleScriptDefinitionsContributor(private val project: Project) : ScriptDefinitionContributor { class GradleScriptDefinitionsContributor(private val project: Project) : ScriptDefinitionSourceAsContributor {
override val id: String = "Gradle Kotlin DSL" override val id: String = "Gradle Kotlin DSL"
private val failedToLoad = AtomicBoolean(false) private val failedToLoad = AtomicBoolean(false)
@@ -86,55 +85,52 @@ class GradleScriptDefinitionsContributor(private val project: Project) : ScriptD
initializeScriptModificationListener(project) initializeScriptModificationListener(project)
} }
override fun getDefinitions(): List<KotlinScriptDefinition> {
return loadDefinitions()
}
// NOTE: control flow here depends on suppressing exceptions from loadGradleTemplates calls // NOTE: control flow here depends on suppressing exceptions from loadGradleTemplates calls
// TODO: possibly combine exceptions from every loadGradleTemplates call, be mindful of KT-19276 // TODO: possibly combine exceptions from every loadGradleTemplates call, be mindful of KT-19276
private fun loadDefinitions(): List<KotlinScriptDefinition> { override val definitions: Sequence<ScriptDefinition>
val kotlinDslDependencySelector = Regex("^gradle-(?:kotlin-dsl|core).*\\.jar\$") get() {
val kotlinDslAdditionalResolverCp = ::kotlinStdlibAndCompiler val kotlinDslDependencySelector = Regex("^gradle-(?:kotlin-dsl|core).*\\.jar\$")
val kotlinDslAdditionalResolverCp = ::kotlinStdlibAndCompiler
failedToLoad.set(false) failedToLoad.set(false)
val kotlinDslTemplates = LinkedHashSet<KotlinScriptDefinition>() val kotlinDslTemplates = LinkedHashSet<ScriptDefinition>()
loadGradleTemplates( loadGradleTemplates(
templateClass = "org.gradle.kotlin.dsl.KotlinInitScript", templateClass = "org.gradle.kotlin.dsl.KotlinInitScript",
dependencySelector = kotlinDslDependencySelector, dependencySelector = kotlinDslDependencySelector,
additionalResolverClasspath = kotlinDslAdditionalResolverCp additionalResolverClasspath = kotlinDslAdditionalResolverCp
).let { kotlinDslTemplates.addAll(it) } ).let { kotlinDslTemplates.addAll(it) }
loadGradleTemplates( loadGradleTemplates(
templateClass = "org.gradle.kotlin.dsl.KotlinSettingsScript", templateClass = "org.gradle.kotlin.dsl.KotlinSettingsScript",
dependencySelector = kotlinDslDependencySelector, dependencySelector = kotlinDslDependencySelector,
additionalResolverClasspath = kotlinDslAdditionalResolverCp additionalResolverClasspath = kotlinDslAdditionalResolverCp
).let { kotlinDslTemplates.addAll(it) } ).let { kotlinDslTemplates.addAll(it) }
// KotlinBuildScript should be last because it has wide scriptFilePattern // KotlinBuildScript should be last because it has wide scriptFilePattern
loadGradleTemplates( loadGradleTemplates(
templateClass = "org.gradle.kotlin.dsl.KotlinBuildScript", templateClass = "org.gradle.kotlin.dsl.KotlinBuildScript",
dependencySelector = kotlinDslDependencySelector, dependencySelector = kotlinDslDependencySelector,
additionalResolverClasspath = kotlinDslAdditionalResolverCp additionalResolverClasspath = kotlinDslAdditionalResolverCp
).let { kotlinDslTemplates.addAll(it) } ).let { kotlinDslTemplates.addAll(it) }
if (kotlinDslTemplates.isNotEmpty()) { if (kotlinDslTemplates.isNotEmpty()) {
return kotlinDslTemplates.toList() return kotlinDslTemplates.asSequence()
}
val default = tryToLoadOldBuildScriptDefinition()
if (default.isNotEmpty()) {
return default.asSequence()
}
return sequenceOf(ErrorGradleScriptDefinition())
} }
val default = tryToLoadOldBuildScriptDefinition() private fun tryToLoadOldBuildScriptDefinition(): List<ScriptDefinition> {
if (default.isNotEmpty()) {
return default
}
return listOf(ErrorGradleScriptDefinition())
}
private fun tryToLoadOldBuildScriptDefinition(): List<KotlinScriptDefinition> {
failedToLoad.set(false) failedToLoad.set(false)
return loadGradleTemplates( return loadGradleTemplates(
@@ -154,7 +150,7 @@ class GradleScriptDefinitionsContributor(private val project: Project) : ScriptD
private fun loadGradleTemplates( private fun loadGradleTemplates(
templateClass: String, dependencySelector: Regex, templateClass: String, dependencySelector: Regex,
additionalResolverClasspath: (gradleLibDir: File) -> List<File> additionalResolverClasspath: (gradleLibDir: File) -> List<File>
): List<KotlinScriptDefinition> = try { ): List<ScriptDefinition> = try {
doLoadGradleTemplates(templateClass, dependencySelector, additionalResolverClasspath) doLoadGradleTemplates(templateClass, dependencySelector, additionalResolverClasspath)
} catch (t: Throwable) { } catch (t: Throwable) {
// TODO: review exception handling // TODO: review exception handling
@@ -170,11 +166,11 @@ class GradleScriptDefinitionsContributor(private val project: Project) : ScriptD
private fun doLoadGradleTemplates( private fun doLoadGradleTemplates(
templateClass: String, dependencySelector: Regex, templateClass: String, dependencySelector: Regex,
additionalResolverClasspath: (gradleLibDir: File) -> List<File> additionalResolverClasspath: (gradleLibDir: File) -> List<File>
): List<KotlinScriptDefinition> { ): List<ScriptDefinition> {
fun createEnvironment( fun createHostConfiguration(
gradleExeSettings: GradleExecutionSettings, gradleExeSettings: GradleExecutionSettings,
projectSettings: GradleProjectSettings projectSettings: GradleProjectSettings
): Environment { ): ScriptingHostConfiguration {
val gradleJvmOptions = gradleExeSettings.daemonVmOptions?.let { vmOptions -> val gradleJvmOptions = gradleExeSettings.daemonVmOptions?.let { vmOptions ->
CommandLineTokenizer(vmOptions).toList() CommandLineTokenizer(vmOptions).toList()
.mapNotNull { it?.let { it as? String } } .mapNotNull { it?.let { it as? String } }
@@ -182,7 +178,8 @@ class GradleScriptDefinitionsContributor(private val project: Project) : ScriptD
.distinct() .distinct()
} ?: emptyList() } ?: emptyList()
return mapOf(
val environment = mapOf(
"gradleHome" to gradleExeSettings.gradleHome?.let(::File), "gradleHome" to gradleExeSettings.gradleHome?.let(::File),
"gradleJavaHome" to gradleExeSettings.javaHome, "gradleJavaHome" to gradleExeSettings.javaHome,
@@ -194,6 +191,9 @@ class GradleScriptDefinitionsContributor(private val project: Project) : ScriptD
"getScriptSectionTokens" to ::topLevelSectionCodeTextTokens "getScriptSectionTokens" to ::topLevelSectionCodeTextTokens
) )
return ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) {
getEnvironment { environment }
}
} }
val gradleSettings = ExternalSystemApiUtil.getSettings(project, GradleConstants.SYSTEM_ID) val gradleSettings = ExternalSystemApiUtil.getSettings(project, GradleConstants.SYSTEM_ID)
@@ -221,14 +221,17 @@ class GradleScriptDefinitionsContributor(private val project: Project) : ScriptD
return loadDefinitionsFromTemplates( return loadDefinitionsFromTemplates(
listOf(templateClass), listOf(templateClass),
templateClasspath, templateClasspath,
createEnvironment(gradleExeSettings, projectSettings), createHostConfiguration(gradleExeSettings, projectSettings),
additionalResolverClasspath(gradleLibDir) additionalResolverClasspath(gradleLibDir)
).map { ).map {
// Expand scope for old gradle script definition it.asLegacyOrNull<KotlinScriptDefinitionFromAnnotatedTemplate>()?.let { legacyDef ->
if (it is KotlinScriptDefinitionFromAnnotatedTemplate && !it.scriptExpectedLocations.contains(ScriptExpectedLocation.Project)) @Suppress("DEPRECATION")
GradleKotlinScriptDefinitionFromAnnotatedTemplate(it) if (legacyDef.scriptExpectedLocations.contains(ScriptExpectedLocation.Project)) null
else else {
it // Expand scope for old gradle script definition
ScriptDefinition.FromLegacy(it.hostConfiguration, GradleKotlinScriptDefinitionFromAnnotatedTemplate(legacyDef))
}
} ?: it
} }
} }
@@ -242,23 +245,28 @@ class GradleScriptDefinitionsContributor(private val project: Project) : ScriptD
ScriptDefinitionsManager.getInstance(project).reloadDefinitionsBy(this) ScriptDefinitionsManager.getInstance(project).reloadDefinitionsBy(this)
} }
private class ErrorGradleScriptDefinition(message: String? = null) : KotlinScriptDefinitionAdapterFromNewAPIBase() { // TODO: refactor - minimize
companion object { private class ErrorGradleScriptDefinition(message: String? = null) :
private const val KOTLIN_DSL_SCRIPT_EXTENSION = "gradle.kts" ScriptDefinition.FromLegacy(ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration), LegacyDefinition(message)) {
private class LegacyDefinition(message: String?) : KotlinScriptDefinitionAdapterFromNewAPIBase() {
companion object {
private const val KOTLIN_DSL_SCRIPT_EXTENSION = "gradle.kts"
}
override val name: String = "Default Kotlin Gradle Script"
override val fileExtension: String = KOTLIN_DSL_SCRIPT_EXTENSION
override val scriptCompilationConfiguration: ScriptCompilationConfiguration = ScriptCompilationConfiguration.Default
override val hostConfiguration: ScriptingHostConfiguration = ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration)
override val baseClass: KClass<*> = ScriptTemplateWithArgs::class
override val dependencyResolver: DependenciesResolver = ErrorScriptDependenciesResolver(message)
override fun toString(): String = "ErrorGradleScriptDefinition"
override fun equals(other: Any?): Boolean = other is ErrorGradleScriptDefinition
override fun hashCode(): Int = name.hashCode()
} }
override val name: String = "Default Kotlin Gradle Script"
override val fileExtension: String = KOTLIN_DSL_SCRIPT_EXTENSION
override val scriptCompilationConfiguration: ScriptCompilationConfiguration = ScriptCompilationConfiguration.Default
override val hostConfiguration: ScriptingHostConfiguration = ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration)
override val baseClass: KClass<*> = ScriptTemplateWithArgs::class
override val dependencyResolver: DependenciesResolver = ErrorScriptDependenciesResolver(message)
override fun toString(): String = "ErrorGradleScriptDefinition"
override fun equals(other: Any?): Boolean = other is ErrorGradleScriptDefinition
override fun hashCode(): Int = name.hashCode()
} }
private class ErrorScriptDependenciesResolver(private val message: String? = null) : DependenciesResolver { private class ErrorScriptDependenciesResolver(private val message: String? = null) : DependenciesResolver {
@@ -280,6 +288,7 @@ internal class GradleSyncState {
class GradleKotlinScriptDefinitionFromAnnotatedTemplate( class GradleKotlinScriptDefinitionFromAnnotatedTemplate(
base: KotlinScriptDefinitionFromAnnotatedTemplate base: KotlinScriptDefinitionFromAnnotatedTemplate
) : KotlinScriptDefinitionFromAnnotatedTemplate(base.template, base.environment, base.templateClasspath) { ) : KotlinScriptDefinitionFromAnnotatedTemplate(base.template, base.environment, base.templateClasspath) {
@Suppress("DEPRECATION")
override val scriptExpectedLocations: List<ScriptExpectedLocation> override val scriptExpectedLocations: List<ScriptExpectedLocation>
get() = listOf(ScriptExpectedLocation.Project) get() = listOf(ScriptExpectedLocation.Project)
} }
@@ -48,6 +48,7 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.resolve.AnalyzingUtils import org.jetbrains.kotlin.resolve.AnalyzingUtils
import java.io.File import java.io.File
import kotlin.script.experimental.api.valueOrNull
class KtCompilingExecutor(file: ScratchFile) : ScratchExecutor(file) { class KtCompilingExecutor(file: ScratchFile) : ScratchExecutor(file) {
companion object { companion object {
@@ -186,8 +187,10 @@ class KtCompilingExecutor(file: ScratchFile) : ScratchExecutor(file) {
javaParameters.classPath.addAll(JavaParametersBuilder.getModuleDependencies(module)) javaParameters.classPath.addAll(JavaParametersBuilder.getModuleDependencies(module))
} }
val scriptDependencies = ScriptDependenciesManager.getInstance(originalFile.project).getScriptDependencies(originalFile.virtualFile) ScriptDependenciesManager.getInstance(originalFile.project)
javaParameters.classPath.addAll(scriptDependencies.classpath.map { it.absolutePath }) .getRefinedCompilationConfiguration(originalFile.virtualFile)?.valueOrNull()?.let {
javaParameters.classPath.addAll(it.dependenciesClassPath.map { f -> f.absolutePath })
}
return javaParameters.toCommandLine() return javaParameters.toCommandLine()
} }
@@ -62,6 +62,7 @@ import org.jetbrains.kotlin.idea.caches.project.productionSourceInfo
import org.jetbrains.kotlin.idea.caches.project.testSourceInfo import org.jetbrains.kotlin.idea.caches.project.testSourceInfo
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionContributor import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionContributor
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionSourceAsContributor
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager
import org.jetbrains.kotlin.idea.project.KOTLIN_CONSOLE_KEY import org.jetbrains.kotlin.idea.project.KOTLIN_CONSOLE_KEY
import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.idea.util.application.runReadAction
@@ -72,11 +73,13 @@ import org.jetbrains.kotlin.psi.KtScript
import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil
import org.jetbrains.kotlin.resolve.repl.ReplState import org.jetbrains.kotlin.resolve.repl.ReplState
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import java.awt.Color import java.awt.Color
import java.awt.Font import java.awt.Font
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.properties.Delegates import kotlin.properties.Delegates
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
private val KOTLIN_SHELL_EXECUTE_ACTION_ID = "KotlinShellExecute" private val KOTLIN_SHELL_EXECUTE_ACTION_ID = "KotlinShellExecute"
@@ -305,20 +308,21 @@ class KotlinConsoleRunner(
} }
} }
class ConsoleScriptDefinitionContributor: ScriptDefinitionContributor { class ConsoleScriptDefinitionContributor: ScriptDefinitionSourceAsContributor {
private val definitions = ContainerUtil.newConcurrentSet<KotlinScriptDefinition>()
val definitionsSet = ContainerUtil.newConcurrentSet<ScriptDefinition>()
override val definitions: Sequence<ScriptDefinition>
get() = definitionsSet.asSequence()
override val id: String = "IDEA Console" override val id: String = "IDEA Console"
override fun getDefinitions(): List<KotlinScriptDefinition> { // TODO: rewrite to ScriptDefinition
return definitions.toList()
}
fun registerDefinition(definition: KotlinScriptDefinition) { fun registerDefinition(definition: KotlinScriptDefinition) {
definitions.add(definition) definitionsSet.add(ScriptDefinition.FromLegacy(defaultJvmScriptingHostConfiguration, definition))
} }
fun unregisterDefinition(definition: KotlinScriptDefinition) { fun unregisterDefinition(definition: KotlinScriptDefinition) {
definitions.remove(definition) definitionsSet.removeIf { it.asLegacyOrNull<KotlinScriptDefinition>() == definition }
} }
} }
+4 -4
View File
@@ -284,11 +284,11 @@
<projectService serviceInterface="org.jetbrains.kotlin.idea.core.script.ScriptDependenciesManager" <projectService serviceInterface="org.jetbrains.kotlin.idea.core.script.ScriptDependenciesManager"
serviceImplementation="org.jetbrains.kotlin.idea.core.script.ScriptDependenciesManager"/> serviceImplementation="org.jetbrains.kotlin.idea.core.script.ScriptDependenciesManager"/>
<projectService serviceInterface="org.jetbrains.kotlin.idea.core.script.ScriptDependenciesCache" <projectService serviceInterface="org.jetbrains.kotlin.idea.core.script.ScriptsCompilationConfigurationCache"
serviceImplementation="org.jetbrains.kotlin.idea.core.script.ScriptDependenciesCache"/> serviceImplementation="org.jetbrains.kotlin.idea.core.script.ScriptsCompilationConfigurationCache"/>
<projectService serviceInterface="org.jetbrains.kotlin.idea.core.script.ScriptDependenciesUpdater" <projectService serviceInterface="org.jetbrains.kotlin.idea.core.script.ScriptsCompilationConfigurationUpdater"
serviceImplementation="org.jetbrains.kotlin.idea.core.script.ScriptDependenciesUpdater"/> serviceImplementation="org.jetbrains.kotlin.idea.core.script.ScriptsCompilationConfigurationUpdater"/>
<projectService serviceInterface="org.jetbrains.kotlin.scripting.resolve.ScriptReportSink" <projectService serviceInterface="org.jetbrains.kotlin.scripting.resolve.ScriptReportSink"
serviceImplementation="org.jetbrains.kotlin.idea.core.script.IdeScriptReportSink"/> serviceImplementation="org.jetbrains.kotlin.idea.core.script.IdeScriptReportSink"/>
@@ -43,7 +43,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.core.isInheritable import org.jetbrains.kotlin.idea.core.isInheritable
import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesUpdater import org.jetbrains.kotlin.idea.core.script.ScriptsCompilationConfigurationUpdater
import org.jetbrains.kotlin.idea.core.toDescriptor import org.jetbrains.kotlin.idea.core.toDescriptor
import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory
import org.jetbrains.kotlin.idea.findUsages.handlers.KotlinFindClassUsagesHandler import org.jetbrains.kotlin.idea.findUsages.handlers.KotlinFindClassUsagesHandler
@@ -127,7 +127,7 @@ class UnusedSymbolInspection : AbstractKotlinInspection() {
val usedScripts = findScriptsWithUsages(declaration) val usedScripts = findScriptsWithUsages(declaration)
if (usedScripts.isNotEmpty()) { if (usedScripts.isNotEmpty()) {
if (ScriptDependenciesUpdater.getInstance(declaration.project).updateDependenciesIfNeeded(usedScripts)) { if (ScriptsCompilationConfigurationUpdater.getInstance(declaration.project).updateDependenciesIfNeeded(usedScripts)) {
return TOO_MANY_OCCURRENCES return TOO_MANY_OCCURRENCES
} }
} }
@@ -9,29 +9,30 @@ import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.Task import com.intellij.openapi.progress.Task
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionContributor import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionSourceAsContributor
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import java.util.concurrent.locks.ReentrantReadWriteLock import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read import kotlin.concurrent.read
import kotlin.concurrent.write import kotlin.concurrent.write
abstract class AsyncScriptDefinitionsContributor(protected val project: Project) : ScriptDefinitionContributor { abstract class AsyncScriptDefinitionsContributor(protected val project: Project) : ScriptDefinitionSourceAsContributor {
abstract val progressMessage: String abstract val progressMessage: String
override fun isReady(): Boolean = definitions != null override fun isReady(): Boolean = _definitions != null
override fun getDefinitions(): List<KotlinScriptDefinition> { override val definitions: Sequence<ScriptDefinition>
definitionsLock.read { get() {
if (definitions != null) { definitionsLock.read {
return definitions!! if (_definitions != null) {
return _definitions!!.asSequence()
}
} }
}
forceStartUpdate = false forceStartUpdate = false
asyncRunUpdateScriptTemplates() asyncRunUpdateScriptTemplates()
return emptyList() return emptySequence()
} }
protected fun asyncRunUpdateScriptTemplates() { protected fun asyncRunUpdateScriptTemplates() {
val backgroundTask = inProgressLock.write { val backgroundTask = inProgressLock.write {
@@ -50,9 +51,9 @@ abstract class AsyncScriptDefinitionsContributor(protected val project: Project)
} }
} }
protected abstract fun loadScriptDefinitions(previous: List<KotlinScriptDefinition>?): List<KotlinScriptDefinition> protected abstract fun loadScriptDefinitions(previous: List<ScriptDefinition>?): List<ScriptDefinition>
private var definitions: List<KotlinScriptDefinition>? = null private var _definitions: List<ScriptDefinition>? = null
private val definitionsLock = ReentrantReadWriteLock() private val definitionsLock = ReentrantReadWriteLock()
protected var forceStartUpdate = false protected var forceStartUpdate = false
@@ -77,10 +78,10 @@ abstract class AsyncScriptDefinitionsContributor(protected val project: Project)
val wasRunning = definitionsLock.isWriteLocked val wasRunning = definitionsLock.isWriteLocked
val needReload = definitionsLock.write { val needReload = definitionsLock.write {
if (wasRunning && !forceStartUpdate && definitions != null) return@write false if (wasRunning && !forceStartUpdate && _definitions != null) return@write false
val newDefinitions = loadScriptDefinitions(definitions) val newDefinitions = loadScriptDefinitions(_definitions)
if (newDefinitions != definitions) { if (newDefinitions != _definitions) {
definitions = newDefinitions _definitions = newDefinitions
return@write true return@write true
} }
return@write false return@write false
@@ -11,35 +11,38 @@ import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionContributor import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionSourceAsContributor
import org.jetbrains.kotlin.idea.core.script.loadDefinitionsFromTemplates import org.jetbrains.kotlin.idea.core.script.loadDefinitionsFromTemplates
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionsFromClasspathDiscoverySource import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionsFromClasspathDiscoverySource
import org.jetbrains.kotlin.scripting.definitions.reporter import org.jetbrains.kotlin.scripting.definitions.reporter
import kotlin.script.experimental.intellij.ScriptDefinitionsProvider import kotlin.script.experimental.intellij.ScriptDefinitionsProvider
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
class BridgeScriptDefinitionsContributor(private val project: Project) : ScriptDefinitionContributor { class BridgeScriptDefinitionsContributor(private val project: Project) : ScriptDefinitionSourceAsContributor {
override val id: String = "BridgeScriptDefinitionsContributor" override val id: String = "BridgeScriptDefinitionsContributor"
override fun getDefinitions(): List<KotlinScriptDefinition> { override val definitions: Sequence<ScriptDefinition>
val extensions = Extensions.getArea(project).getExtensionPoint(ScriptDefinitionsProvider.EP_NAME).extensions get() {
val messageCollector = LoggingMessageCollector() val extensions = Extensions.getArea(project).getExtensionPoint(ScriptDefinitionsProvider.EP_NAME).extensions
return extensions.flatMap { provider -> val messageCollector = LoggingMessageCollector()
val explicitClasses = provider.getDefinitionClasses().toList() return extensions.asSequence().flatMap { provider ->
val classPath = provider.getDefinitionsClassPath().toList() val explicitClasses = provider.getDefinitionClasses().toList()
val explicitDefinitions = val classPath = provider.getDefinitionsClassPath().toList()
if (explicitClasses.isNotEmpty()) loadDefinitionsFromTemplates(explicitClasses, classPath) val hostConfiguration = defaultJvmScriptingHostConfiguration
else emptyList() val explicitDefinitions =
val discoveredDefinitions = if (explicitClasses.isEmpty()) emptySequence()
if (provider.useDiscovery()) emptySequence() else loadDefinitionsFromTemplates(explicitClasses, classPath, hostConfiguration).asSequence()
else ScriptDefinitionsFromClasspathDiscoverySource( val discoveredDefinitions =
classPath, if (provider.useDiscovery()) emptySequence()
emptyMap(), else ScriptDefinitionsFromClasspathDiscoverySource(
messageCollector.reporter classPath,
).definitions hostConfiguration,
explicitDefinitions + discoveredDefinitions messageCollector.reporter
).definitions
explicitDefinitions + discoveredDefinitions
}
} }
}
} }
private class LoggingMessageCollector : MessageCollector { private class LoggingMessageCollector : MessageCollector {
@@ -12,11 +12,10 @@ import com.intellij.util.ui.ListTableModel
import org.jetbrains.kotlin.idea.core.script.StandardIdeScriptDefinition import org.jetbrains.kotlin.idea.core.script.StandardIdeScriptDefinition
import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings
import org.jetbrains.kotlin.parsing.KotlinParserDefinition import org.jetbrains.kotlin.parsing.KotlinParserDefinition
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinitionAdapterFromNewAPIBase
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
class KotlinScriptDefinitionsModelDescriptor(val definition: KotlinScriptDefinition, var isEnabled: Boolean) class KotlinScriptDefinitionsModelDescriptor(val definition: ScriptDefinition, var isEnabled: Boolean)
class KotlinScriptDefinitionsModel private constructor(definitions: MutableList<KotlinScriptDefinitionsModelDescriptor>) : class KotlinScriptDefinitionsModel private constructor(definitions: MutableList<KotlinScriptDefinitionsModelDescriptor>) :
ListTableModel<KotlinScriptDefinitionsModelDescriptor>( ListTableModel<KotlinScriptDefinitionsModelDescriptor>(
@@ -30,7 +29,7 @@ class KotlinScriptDefinitionsModel private constructor(definitions: MutableList<
) { ) {
fun getDefinitions() = items.map { it.definition } fun getDefinitions() = items.map { it.definition }
fun setDefinitions(definitions: List<KotlinScriptDefinition>, settings: KotlinScriptingSettings) { fun setDefinitions(definitions: List<ScriptDefinition>, settings: KotlinScriptingSettings) {
items = definitions.mapTo(arrayListOf()) { KotlinScriptDefinitionsModelDescriptor(it, settings.isScriptDefinitionEnabled(it)) } items = definitions.mapTo(arrayListOf()) { KotlinScriptDefinitionsModelDescriptor(it, settings.isScriptDefinitionEnabled(it)) }
} }
@@ -41,12 +40,9 @@ class KotlinScriptDefinitionsModel private constructor(definitions: MutableList<
private class ScriptDefinitionPattern : ColumnInfo<KotlinScriptDefinitionsModelDescriptor, String>("Pattern/Extension") { private class ScriptDefinitionPattern : ColumnInfo<KotlinScriptDefinitionsModelDescriptor, String>("Pattern/Extension") {
override fun valueOf(item: KotlinScriptDefinitionsModelDescriptor): String { override fun valueOf(item: KotlinScriptDefinitionsModelDescriptor): String {
val definition = item.definition val definition = item.definition
return when (definition) { return definition.asLegacyOrNull<KotlinScriptDefinitionFromAnnotatedTemplate>()?.scriptFilePattern?.pattern
is KotlinScriptDefinitionFromAnnotatedTemplate -> definition.scriptFilePattern.pattern ?: definition.asLegacyOrNull<StandardIdeScriptDefinition>()?.let { KotlinParserDefinition.STD_SCRIPT_EXT }
is KotlinScriptDefinitionAdapterFromNewAPIBase -> definition.fileExtension ?: definition.fileExtension
is StandardIdeScriptDefinition -> KotlinParserDefinition.STD_SCRIPT_EXT
else -> ""
}
} }
} }
@@ -58,11 +54,12 @@ class KotlinScriptDefinitionsModel private constructor(definitions: MutableList<
override fun getEditor(item: KotlinScriptDefinitionsModelDescriptor?) = BooleanTableCellEditor() override fun getEditor(item: KotlinScriptDefinitionsModelDescriptor?) = BooleanTableCellEditor()
override fun getRenderer(item: KotlinScriptDefinitionsModelDescriptor?) = BooleanTableCellRenderer() override fun getRenderer(item: KotlinScriptDefinitionsModelDescriptor?) = BooleanTableCellRenderer()
override fun isCellEditable(item: KotlinScriptDefinitionsModelDescriptor) = item.definition !is StandardIdeScriptDefinition override fun isCellEditable(item: KotlinScriptDefinitionsModelDescriptor) =
item.definition.asLegacyOrNull<StandardIdeScriptDefinition>() == null
} }
companion object { companion object {
fun createModel(definitions: List<KotlinScriptDefinition>, settings: KotlinScriptingSettings): KotlinScriptDefinitionsModel { fun createModel(definitions: List<ScriptDefinition>, settings: KotlinScriptingSettings): KotlinScriptDefinitionsModel {
return KotlinScriptDefinitionsModel(definitions.mapTo(arrayListOf()) { return KotlinScriptDefinitionsModel(definitions.mapTo(arrayListOf()) {
KotlinScriptDefinitionsModelDescriptor( KotlinScriptDefinitionsModelDescriptor(
it, it,
@@ -14,7 +14,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager; import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager;
import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings; import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings;
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition; import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
@@ -64,7 +64,7 @@ public class KotlinScriptingSettingsConfigurable implements SearchableConfigurab
if (isScriptDefinitionsChanged()) { if (isScriptDefinitionsChanged()) {
for (KotlinScriptDefinitionsModelDescriptor item : model.getItems()) { for (KotlinScriptDefinitionsModelDescriptor item : model.getItems()) {
KotlinScriptDefinition definition = item.getDefinition(); ScriptDefinition definition = item.getDefinition();
settings.setOrder(definition, model.getItems().indexOf(item)); settings.setOrder(definition, model.getItems().indexOf(item));
settings.setEnabled(definition, item.isEnabled()); settings.setEnabled(definition, item.isEnabled());
} }
@@ -23,8 +23,7 @@ import org.jetbrains.kotlin.idea.core.script.StandardIdeScriptDefinition
import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings
import org.jetbrains.kotlin.parsing.KotlinParserDefinition import org.jetbrains.kotlin.parsing.KotlinParserDefinition
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinitionAdapterFromNewAPIBase
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
class MultipleScriptDefinitionsChecker(private val project: Project) : EditorNotifications.Provider<EditorNotificationPanel>() { class MultipleScriptDefinitionsChecker(private val project: Project) : EditorNotifications.Provider<EditorNotificationPanel>() {
@@ -42,8 +41,7 @@ class MultipleScriptDefinitionsChecker(private val project: Project) : EditorNot
val allApplicableDefinitions = ScriptDefinitionsManager.getInstance(project) val allApplicableDefinitions = ScriptDefinitionsManager.getInstance(project)
.getAllDefinitions() .getAllDefinitions()
.filter { .filter {
it !is StandardIdeScriptDefinition && it.asLegacyOrNull<StandardIdeScriptDefinition>() == null && it.isScript(ktFile.name) &&
it.isScript(ktFile.name) &&
KotlinScriptingSettings.getInstance(project).isScriptDefinitionEnabled(it) KotlinScriptingSettings.getInstance(project).isScriptDefinitionEnabled(it)
} }
.toList() .toList()
@@ -56,10 +54,10 @@ class MultipleScriptDefinitionsChecker(private val project: Project) : EditorNot
) )
} }
private fun areDefinitionsForGradleKts(allApplicableDefinitions: List<KotlinScriptDefinition>): Boolean { private fun areDefinitionsForGradleKts(allApplicableDefinitions: List<ScriptDefinition>): Boolean {
if (allApplicableDefinitions.size == 2) { if (allApplicableDefinitions.size == 2) {
return (allApplicableDefinitions[0] as? KotlinScriptDefinitionFromAnnotatedTemplate)?.scriptFilePattern?.pattern == "^(settings|.+\\.settings)\\.gradle\\.kts\$" return allApplicableDefinitions[0].asLegacyOrNull<KotlinScriptDefinitionFromAnnotatedTemplate>()?.scriptFilePattern?.pattern == "^(settings|.+\\.settings)\\.gradle\\.kts\$"
&& (allApplicableDefinitions[1] as? KotlinScriptDefinitionFromAnnotatedTemplate)?.scriptFilePattern?.pattern == ".*\\.gradle\\.kts" && allApplicableDefinitions[1].asLegacyOrNull<KotlinScriptDefinitionFromAnnotatedTemplate>()?.scriptFilePattern?.pattern == ".*\\.gradle\\.kts"
} }
return false return false
} }
@@ -67,25 +65,18 @@ class MultipleScriptDefinitionsChecker(private val project: Project) : EditorNot
companion object { companion object {
private val KEY = Key.create<EditorNotificationPanel>("MultipleScriptDefinitionsChecker") private val KEY = Key.create<EditorNotificationPanel>("MultipleScriptDefinitionsChecker")
private fun createNotification(psiFile: KtFile, defs: List<KotlinScriptDefinition>): EditorNotificationPanel { private fun createNotification(psiFile: KtFile, defs: List<ScriptDefinition>): EditorNotificationPanel {
return EditorNotificationPanel().apply { return EditorNotificationPanel().apply {
setText("Multiple script definitions are applicable for this script. ${defs.first().name} is used") setText("Multiple script definitions are applicable for this script. ${defs.first().name} is used")
createComponentActionLabel("Show all") { createComponentActionLabel("Show all") {
val list = JBPopupFactory.getInstance().createListPopup( val list = JBPopupFactory.getInstance().createListPopup(
object : BaseListPopupStep<KotlinScriptDefinition>(null, defs) { object : BaseListPopupStep<ScriptDefinition>(null, defs) {
override fun getTextFor(value: KotlinScriptDefinition): String { override fun getTextFor(value: ScriptDefinition): String {
return when (value) { return value.asLegacyOrNull<KotlinScriptDefinitionFromAnnotatedTemplate>()?.let {
is KotlinScriptDefinitionFromAnnotatedTemplate -> { it.name + " (${it.scriptFilePattern})"
value.name + " (${value.scriptFilePattern})" } ?: value.asLegacyOrNull<StandardIdeScriptDefinition>()?.let {
} it.name + " (${KotlinParserDefinition.STD_SCRIPT_EXT})"
is KotlinScriptDefinitionAdapterFromNewAPIBase -> { } ?: value.name + " (${value.fileExtension})"
value.name + " (${value.fileExtension})"
}
is StandardIdeScriptDefinition -> {
value.name + " (${KotlinParserDefinition.STD_SCRIPT_EXT})"
}
else -> value.name
}
} }
} }
) )
@@ -20,16 +20,19 @@ import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.config.CompilerSettings import org.jetbrains.kotlin.config.CompilerSettings
import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCompilerSettings import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCompilerSettings
import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCompilerSettingsListener import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCompilerSettingsListener
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionContributor import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionSourceAsContributor
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager
import org.jetbrains.kotlin.idea.core.script.loadDefinitionsFromTemplates import org.jetbrains.kotlin.idea.core.script.loadDefinitionsFromTemplates
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.getEnvironment
import java.io.File import java.io.File
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
class ScriptTemplatesFromCompilerSettingsProvider( class ScriptTemplatesFromCompilerSettingsProvider(
private val project: Project, private val project: Project,
private val compilerSettings: KotlinCompilerSettings private val compilerSettings: KotlinCompilerSettings
) : ScriptDefinitionContributor { ) : ScriptDefinitionSourceAsContributor {
init { init {
project.messageBus.connect().subscribe(KotlinCompilerSettingsListener.TOPIC, object : KotlinCompilerSettingsListener { project.messageBus.connect().subscribe(KotlinCompilerSettingsListener.TOPIC, object : KotlinCompilerSettingsListener {
@@ -41,17 +44,22 @@ class ScriptTemplatesFromCompilerSettingsProvider(
}) })
} }
override fun getDefinitions(): List<KotlinScriptDefinition> { override val definitions: Sequence<ScriptDefinition>
val kotlinSettings = compilerSettings.settings get() {
return if (kotlinSettings.scriptTemplates.isBlank()) emptyList() val kotlinSettings = compilerSettings.settings
else loadDefinitionsFromTemplates( return if (kotlinSettings.scriptTemplates.isBlank()) emptySequence()
templateClassNames = kotlinSettings.scriptTemplates.split(',', ' '), else loadDefinitionsFromTemplates(
templateClasspath = kotlinSettings.scriptTemplatesClasspath.split(File.pathSeparator).map(::File), templateClassNames = kotlinSettings.scriptTemplates.split(',', ' '),
environment = mapOf( templateClasspath = kotlinSettings.scriptTemplatesClasspath.split(File.pathSeparator).map(::File),
"projectRoot" to (project.basePath ?: project.baseDir.canonicalPath)?.let(::File) baseHostConfiguration = ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) {
) getEnvironment {
) mapOf(
} "projectRoot" to (project.basePath ?: project.baseDir.canonicalPath)?.let(::File)
)
}
}
).asSequence()
}
override val id: String = "KotlinCompilerScriptTemplatesSettings" override val id: String = "KotlinCompilerScriptTemplatesSettings"
} }
@@ -14,12 +14,12 @@ import com.intellij.openapi.vfs.JarFileSystem
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.idea.core.script.loadDefinitionsFromTemplates import org.jetbrains.kotlin.idea.core.script.loadDefinitionsFromTemplates
import org.jetbrains.kotlin.idea.util.projectStructure.allModules import org.jetbrains.kotlin.idea.util.projectStructure.allModules
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition import org.jetbrains.kotlin.scripting.definitions.*
import org.jetbrains.kotlin.scripting.definitions.SCRIPT_DEFINITION_MARKERS_EXTENSION_WITH_DOT
import org.jetbrains.kotlin.scripting.definitions.SCRIPT_DEFINITION_MARKERS_PATH
import java.io.File import java.io.File
import java.util.concurrent.locks.ReentrantReadWriteLock import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.write import kotlin.concurrent.write
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
class ScriptTemplatesFromDependenciesProvider(project: Project) : AsyncScriptDefinitionsContributor(project) { class ScriptTemplatesFromDependenciesProvider(project: Project) : AsyncScriptDefinitionsContributor(project) {
@@ -41,7 +41,7 @@ class ScriptTemplatesFromDependenciesProvider(project: Project) : AsyncScriptDef
override val id = "ScriptTemplatesFromDependenciesProvider" override val id = "ScriptTemplatesFromDependenciesProvider"
override val progressMessage = "Kotlin: scanning dependencies for script definitions..." override val progressMessage = "Kotlin: scanning dependencies for script definitions..."
override fun loadScriptDefinitions(previous: List<KotlinScriptDefinition>?): List<KotlinScriptDefinition> { override fun loadScriptDefinitions(previous: List<ScriptDefinition>?): List<ScriptDefinition> {
val templatesCopy = templatesLock.write { val templatesCopy = templatesLock.write {
val newTemplates = scriptDefinitionsFromDependencies(project) val newTemplates = scriptDefinitionsFromDependencies(project)
if (newTemplates != templates) { if (newTemplates != templates) {
@@ -51,12 +51,17 @@ class ScriptTemplatesFromDependenciesProvider(project: Project) : AsyncScriptDef
return@write null return@write null
} }
if (templatesCopy != null) { if (templatesCopy != null) {
val hostConfiguration = ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) {
getEnvironment {
mapOf(
"projectRoot" to (project.basePath ?: project.baseDir.canonicalPath)?.let(::File)
)
}
}
return loadDefinitionsFromTemplates( return loadDefinitionsFromTemplates(
templateClassNames = templatesCopy.templates, templateClassNames = templatesCopy.templates,
templateClasspath = templatesCopy.classpath, templateClasspath = templatesCopy.classpath,
environment = mapOf( baseHostConfiguration = hostConfiguration
"projectRoot" to (project.basePath ?: project.baseDir.canonicalPath)?.let(::File)
)
) )
} }
return previous ?: emptyList() return previous ?: emptyList()
@@ -49,6 +49,7 @@ import org.jetbrains.kotlin.resolve.scopes.utils.findVariable
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.* import java.util.*
import kotlin.script.experimental.api.valueOrNull
class ImportInsertHelperImpl(private val project: Project) : ImportInsertHelper() { class ImportInsertHelperImpl(private val project: Project) : ImportInsertHelper() {
@@ -64,8 +65,9 @@ class ImportInsertHelperImpl(private val project: Project) : ImportInsertHelper(
val allDefaultImports = platform.findAnalyzerServices.getDefaultImports(languageVersionSettings, includeLowPriorityImports = true) val allDefaultImports = platform.findAnalyzerServices.getDefaultImports(languageVersionSettings, includeLowPriorityImports = true)
val scriptExtraImports = contextFile.takeIf { it.isScript() }?.let { ktFile -> val scriptExtraImports = contextFile.takeIf { it.isScript() }?.let { ktFile ->
val scriptDependencies = ScriptDependenciesProvider.getInstance(ktFile.project)?.getScriptDependencies(ktFile.originalFile) val scriptDependencies =
scriptDependencies?.imports?.map { ImportPath.fromString(it) } ScriptDependenciesProvider.getInstance(ktFile.project)?.getScriptConfigurationResult(ktFile.originalFile)?.valueOrNull()
scriptDependencies?.defaultImports?.map { ImportPath.fromString(it) }
}.orEmpty() }.orEmpty()
return importPath.isImported(allDefaultImports + scriptExtraImports, platform.findAnalyzerServices.excludedImports) return importPath.isImported(allDefaultImports + scriptExtraImports, platform.findAnalyzerServices.excludedImports)
@@ -1,4 +1,3 @@
<error descr="TestDependenciesResolver threw exception IllegalStateException: <error descr="Exception from resolver (java.lang.IllegalStateException: Exception from resolver)"></error>
Exception from resolver"></error>
val s = 3 val s = 3
val g = 4 val g = 4
@@ -22,7 +22,7 @@ import com.intellij.testFramework.LightProjectDescriptor;
import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesUpdaterKt; import org.jetbrains.kotlin.idea.core.script.ScriptsCompilationConfigurationUpdaterKt;
import org.jetbrains.kotlin.test.InTextDirectivesUtils; import org.jetbrains.kotlin.test.InTextDirectivesUtils;
import org.jetbrains.kotlin.test.KotlinTestUtils; import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.plugins.groovy.GroovyFileType; import org.jetbrains.plugins.groovy.GroovyFileType;
@@ -42,12 +42,12 @@ public abstract class AbstractConfigureProjectByChangingFileTest<C extends Kotli
ApplicationManager.getApplication().runWriteAction( ApplicationManager.getApplication().runWriteAction(
() -> FileTypeManager.getInstance().associateExtension(GroovyFileType.GROOVY_FILE_TYPE, "gradle") () -> FileTypeManager.getInstance().associateExtension(GroovyFileType.GROOVY_FILE_TYPE, "gradle")
); );
ScriptDependenciesUpdaterKt.setScriptDependenciesUpdaterDisabled(ApplicationManager.getApplication(), true); ScriptsCompilationConfigurationUpdaterKt.setScriptDependenciesUpdaterDisabled(ApplicationManager.getApplication(), true);
} }
@Override @Override
protected void tearDown() throws Exception { protected void tearDown() throws Exception {
ScriptDependenciesUpdaterKt.setScriptDependenciesUpdaterDisabled(ApplicationManager.getApplication(), false); ScriptsCompilationConfigurationUpdaterKt.setScriptDependenciesUpdaterDisabled(ApplicationManager.getApplication(), false);
moduleInfoFile = null; moduleInfoFile = null;
super.tearDown(); super.tearDown();
} }
@@ -5,32 +5,41 @@
package org.jetbrains.kotlin.idea.script package org.jetbrains.kotlin.idea.script
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionContributor import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionSourceAsContributor
import org.jetbrains.kotlin.idea.core.script.loadDefinitionsFromTemplates import org.jetbrains.kotlin.idea.core.script.loadDefinitionsFromTemplates
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.getEnvironment
import java.io.File import java.io.File
import kotlin.script.dependencies.Environment import kotlin.script.dependencies.Environment
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
class CustomScriptTemplateProvider( class CustomScriptTemplateProvider(val environment: Environment) : ScriptDefinitionSourceAsContributor {
val environment: Environment
) : ScriptDefinitionContributor {
override val id = "Test" override val id = "Test"
override fun getDefinitions() = loadDefinitionsFromTemplates( override val definitions: Sequence<ScriptDefinition>
get() = loadDefinitionsFromTemplates(
templateClassNames = environment["template-classes-names"] as List<String>, templateClassNames = environment["template-classes-names"] as List<String>,
templateClasspath = listOfNotNull(environment["template-classes"] as? File), templateClasspath = listOfNotNull(environment["template-classes"] as? File),
environment = environment baseHostConfiguration = ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) {
) getEnvironment { environment }
}
).asSequence()
} }
class FromTextTemplateProvider( class FromTextTemplateProvider(val environment: Map<String, Any?>) : ScriptDefinitionSourceAsContributor {
val environment: Map<String, Any?>
) : ScriptDefinitionContributor {
override val id = "Test" override val id = "Test"
override fun getDefinitions() = loadDefinitionsFromTemplates(
override val definitions: Sequence<ScriptDefinition>
get() = loadDefinitionsFromTemplates(
templateClassNames = listOf("org.jetbrains.kotlin.idea.script.Template"), templateClassNames = listOf("org.jetbrains.kotlin.idea.script.Template"),
templateClasspath = listOfNotNull(environment["template-classes"] as? File), templateClasspath = listOfNotNull(environment["template-classes"] as? File),
environment = environment baseHostConfiguration = ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) {
) getEnvironment { environment }
}
).asSequence()
} }
@@ -76,6 +76,10 @@ sealed class ResultWithDiagnostics<out R> {
) : ResultWithDiagnostics<Nothing>() { ) : ResultWithDiagnostics<Nothing>() {
constructor(vararg reports: ScriptDiagnostic) : this(reports.asList()) constructor(vararg reports: ScriptDiagnostic) : this(reports.asList())
} }
override fun equals(other: Any?): Boolean = this === other || (other is ResultWithDiagnostics<*> && this.reports == other.reports)
override fun hashCode(): Int = reports.hashCode()
} }
/** /**
@@ -162,9 +166,10 @@ fun makeFailureResult(message: String, path: String? = null, location: SourceCod
fun Throwable.asDiagnostics( fun Throwable.asDiagnostics(
customMessage: String? = null, customMessage: String? = null,
path: String? = null, path: String? = null,
location: SourceCode.Location? = null location: SourceCode.Location? = null,
severity: ScriptDiagnostic.Severity = ScriptDiagnostic.Severity.ERROR
): ScriptDiagnostic = ): ScriptDiagnostic =
ScriptDiagnostic(customMessage ?: message ?: "$this", ScriptDiagnostic.Severity.ERROR, path, location, this) ScriptDiagnostic(customMessage ?: message ?: "$this", severity, path, location, this)
/** /**
* Converts the receiver String to error diagnostic report with optional [path] and [location] * Converts the receiver String to error diagnostic report with optional [path] and [location]
@@ -175,7 +180,7 @@ fun String.asErrorDiagnostics(path: String? = null, location: SourceCode.Locatio
/** /**
* Extracts the result value from the receiver wrapper or null if receiver represents a Failure * Extracts the result value from the receiver wrapper or null if receiver represents a Failure
*/ */
fun <R> ResultWithDiagnostics<R>.resultOrNull(): R? = when (this) { fun <R> ResultWithDiagnostics<R>.valueOrNull(): R? = when (this) {
is ResultWithDiagnostics.Success<R> -> value is ResultWithDiagnostics.Success<R> -> value
else -> null else -> null
} }
@@ -183,7 +188,7 @@ fun <R> ResultWithDiagnostics<R>.resultOrNull(): R? = when (this) {
/** /**
* Extracts the result value from the receiver wrapper or run non-returning lambda if receiver represents a Failure * Extracts the result value from the receiver wrapper or run non-returning lambda if receiver represents a Failure
*/ */
inline fun <R> ResultWithDiagnostics<R>.resultOr(body: (ResultWithDiagnostics.Failure) -> Nothing): R = when (this) { inline fun <R> ResultWithDiagnostics<R>.valueOr(body: (ResultWithDiagnostics.Failure) -> Nothing): R = when (this) {
is ResultWithDiagnostics.Success<R> -> value is ResultWithDiagnostics.Success<R> -> value
is ResultWithDiagnostics.Failure -> body(this) is ResultWithDiagnostics.Failure -> body(this)
} }
@@ -59,7 +59,7 @@ val ScriptCompilationConfigurationKeys.fileExtension by PropertiesCollection.key
/** /**
* The superclass for target script class * The superclass for target script class
*/ */
val ScriptCompilationConfigurationKeys.baseClass by PropertiesCollection.key<KotlinType>() // script base class val ScriptCompilationConfigurationKeys.baseClass by PropertiesCollection.key<KotlinType>(KotlinType(Any::class)) // script base class
/** /**
* The list of classes that will be used as implicit receivers in the script body, as if the whole body is wrapped with "with" calls: * The list of classes that will be used as implicit receivers in the script body, as if the whole body is wrapped with "with" calls:
@@ -41,10 +41,14 @@ fun getMergedScriptText(script: SourceCode, configuration: ScriptCompilationConf
} }
} }
abstract class FileBasedScriptSource() : ExternalSourceCode {
abstract val file: File
}
/** /**
* The implementation of the SourceCode for a script located in a file * The implementation of the SourceCode for a script located in a file
*/ */
open class FileScriptSource(val file: File, private val preloadedText: String? = null) : ExternalSourceCode { open class FileScriptSource(override val file: File, private val preloadedText: String? = null) : FileBasedScriptSource() {
override val externalLocation: URL get() = file.toURI().toURL() override val externalLocation: URL get() = file.toURI().toURL()
override val text: String by lazy { preloadedText ?: file.readText() } override val text: String by lazy { preloadedText ?: file.readText() }
override val name: String? get() = file.name override val name: String? get() = file.name
@@ -18,13 +18,13 @@ import java.util.jar.JarFile
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.script.experimental.api.* import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.BasicScriptingHost import kotlin.script.experimental.host.BasicScriptingHost
import kotlin.script.experimental.host.FileScriptSource import kotlin.script.experimental.host.FileBasedScriptSource
import kotlin.script.experimental.host.toScriptSource import kotlin.script.experimental.host.toScriptSource
import kotlin.script.experimental.jvm.BasicJvmScriptEvaluator import kotlin.script.experimental.jvm.BasicJvmScriptEvaluator
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.experimental.jvm.impl.KJvmCompiledScript
import kotlin.script.experimental.jvmhost.* import kotlin.script.experimental.jvmhost.*
import kotlin.script.experimental.jvmhost.impl.CompiledScriptClassLoader import kotlin.script.experimental.jvmhost.impl.CompiledScriptClassLoader
import kotlin.script.experimental.jvm.impl.KJvmCompiledScript
import kotlin.script.templates.standard.SimpleScriptTemplate import kotlin.script.templates.standard.SimpleScriptTemplate
class ScriptingHostTest : TestCase() { class ScriptingHostTest : TestCase() {
@@ -86,7 +86,7 @@ class ScriptingHostTest : TestCase() {
val scriptName = "SavedRunnableScript" val scriptName = "SavedRunnableScript"
val compiledScript = runBlocking { val compiledScript = runBlocking {
compiler("println(\"$greeting\")".toScriptSource(name = "$scriptName.kts"), compilationConfiguration).throwOnFailure() compiler("println(\"$greeting\")".toScriptSource(name = "$scriptName.kts"), compilationConfiguration).throwOnFailure()
.resultOrNull()!! .valueOrNull()!!
} }
val saver = BasicJvmScriptJarGenerator(outJar) val saver = BasicJvmScriptJarGenerator(outJar)
runBlocking { runBlocking {
@@ -353,8 +353,8 @@ class ScriptingHostTest : TestCase() {
val compiledScriptClassRes = runBlocking { compiledScript!!.getClass(null) } val compiledScriptClassRes = runBlocking { compiledScript!!.getClass(null) }
val cachedScriptClassRes = runBlocking { cachedScript!!.getClass(null) } val cachedScriptClassRes = runBlocking { cachedScript!!.getClass(null) }
val compiledScriptClass = compiledScriptClassRes.resultOrNull() val compiledScriptClass = compiledScriptClassRes.valueOrNull()
val cachedScriptClass = cachedScriptClassRes.resultOrNull() val cachedScriptClass = cachedScriptClassRes.valueOrNull()
Assert.assertEquals(compiledScriptClass!!.qualifiedName, cachedScriptClass!!.qualifiedName) Assert.assertEquals(compiledScriptClass!!.qualifiedName, cachedScriptClass!!.qualifiedName)
Assert.assertEquals(compiledScriptClass!!.supertypes, cachedScriptClass!!.supertypes) Assert.assertEquals(compiledScriptClass!!.supertypes, cachedScriptClass!!.supertypes)
@@ -380,7 +380,7 @@ class ScriptingHostTest : TestCase() {
val res = compiler(script.toScriptSource(), scriptCompilationConfiguration).throwOnFailure() val res = compiler(script.toScriptSource(), scriptCompilationConfiguration).throwOnFailure()
(res as ResultWithDiagnostics.Success<CompiledScript<*>>).value (res as ResultWithDiagnostics.Success<CompiledScript<*>>).value
} }
val compiledScriptClass = runBlocking { compiledScript.getClass(null).throwOnFailure().resultOrNull()!! as KClass<*> } val compiledScriptClass = runBlocking { compiledScript.getClass(null).throwOnFailure().valueOrNull()!! as KClass<*> }
val classLoader = compiledScriptClass.java.classLoader val classLoader = compiledScriptClass.java.classLoader
Assert.assertTrue(classLoader is CompiledScriptClassLoader) Assert.assertTrue(classLoader is CompiledScriptClassLoader)
@@ -463,7 +463,7 @@ private fun ScriptCompilationConfiguration.Builder.makeSimpleConfigurationWithTe
refineConfiguration { refineConfiguration {
beforeCompiling { ctx -> beforeCompiling { ctx ->
val importedScript = File(ScriptingHostTest.TEST_DATA_DIR, "importTest/helloWithVal.kts") val importedScript = File(ScriptingHostTest.TEST_DATA_DIR, "importTest/helloWithVal.kts")
if ((ctx.script as? FileScriptSource)?.file?.canonicalFile == importedScript.canonicalFile) { if ((ctx.script as? FileBasedScriptSource)?.file?.canonicalFile == importedScript.canonicalFile) {
ctx.compilationConfiguration ctx.compilationConfiguration
} else { } else {
ScriptCompilationConfiguration(ctx.compilationConfiguration) { ScriptCompilationConfiguration(ctx.compilationConfiguration) {
@@ -33,7 +33,7 @@ class KJvmCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) : KJvm
val mainKtFile = val mainKtFile =
getScriptKtFile(script, context.baseScriptCompilationConfiguration, context.environment.project, messageCollector) getScriptKtFile(script, context.baseScriptCompilationConfiguration, context.environment.project, messageCollector)
.resultOr { return it } .valueOr { return it }
context.scriptCompilationState.configureFor(script, context.baseScriptCompilationConfiguration) context.scriptCompilationState.configureFor(script, context.baseScriptCompilationConfiguration)
@@ -31,7 +31,7 @@ class KJvmReplCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) :
override fun createReplCompilationState(scriptCompilationConfiguration: ScriptCompilationConfiguration): JvmReplCompilerState.Compilation { override fun createReplCompilationState(scriptCompilationConfiguration: ScriptCompilationConfiguration): JvmReplCompilerState.Compilation {
val context = withMessageCollectorAndDisposable(disposeOnSuccess = false) { messageCollector, disposable -> val context = withMessageCollectorAndDisposable(disposeOnSuccess = false) { messageCollector, disposable ->
createSharedCompilationContext(scriptCompilationConfiguration, hostConfiguration, messageCollector, disposable).asSuccess() createSharedCompilationContext(scriptCompilationConfiguration, hostConfiguration, messageCollector, disposable).asSuccess()
}.resultOr { throw IllegalStateException("Unable to initialize repl compiler:\n ${it.reports.joinToString("\n ")}") } }.valueOr { throw IllegalStateException("Unable to initialize repl compiler:\n ${it.reports.joinToString("\n ")}") }
return ReplCompilationState(context) return ReplCompilationState(context)
} }
@@ -42,7 +42,7 @@ class KJvmReplCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) :
): ResultWithDiagnostics<Boolean> = ): ResultWithDiagnostics<Boolean> =
withMessageCollector(script) { messageCollector -> withMessageCollector(script) { messageCollector ->
val ktFile = getScriptKtFile(script, scriptCompilationConfiguration, project, messageCollector) val ktFile = getScriptKtFile(script, scriptCompilationConfiguration, project, messageCollector)
.resultOr { return it } .valueOr { return it }
val errorHolder = object : MessageCollectorBasedReporter { val errorHolder = object : MessageCollectorBasedReporter {
override val messageCollector = messageCollector override val messageCollector = messageCollector
} }
@@ -80,7 +80,7 @@ class KJvmReplCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) :
val snippetKtFile = val snippetKtFile =
getScriptKtFile(snippet, context.baseScriptCompilationConfiguration, context.environment.project, messageCollector) getScriptKtFile(snippet, context.baseScriptCompilationConfiguration, context.environment.project, messageCollector)
.resultOr { return it } .valueOr { return it }
val (sourceFiles, sourceDependencies) = collectRefinedSourcesAndUpdateEnvironment(context, snippetKtFile, messageCollector) val (sourceFiles, sourceDependencies) = collectRefinedSourcesAndUpdateEnvironment(context, snippetKtFile, messageCollector)
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCompilerConfigurationComponentRegistrar import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCompilerConfigurationComponentRegistrar
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.dependencies.ScriptsCompilationDependencies import org.jetbrains.kotlin.scripting.dependencies.ScriptsCompilationDependencies
import org.jetbrains.kotlin.scripting.dependencies.collectScriptsCompilationDependencies import org.jetbrains.kotlin.scripting.dependencies.collectScriptsCompilationDependencies
import kotlin.script.experimental.api.ScriptCompilationConfiguration import kotlin.script.experimental.api.ScriptCompilationConfiguration
@@ -88,10 +89,13 @@ internal fun createInitialConfigurations(
kotlinCompilerConfiguration.add( kotlinCompilerConfiguration.add(
ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, ScriptingConfigurationKeys.SCRIPT_DEFINITIONS,
BridgeScriptDefinition( ScriptDefinition.FromLegacy(
scriptCompilationConfiguration,
hostConfiguration, hostConfiguration,
scriptCompilationState BridgeScriptDefinition(
scriptCompilationConfiguration,
hostConfiguration,
scriptCompilationState
)
) )
) )
@@ -8,11 +8,8 @@ package kotlin.script.experimental.jvmhost.impl
import com.intellij.openapi.Disposable import com.intellij.openapi.Disposable
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.CharsetToolkit
import com.intellij.psi.PsiFileFactory import com.intellij.psi.PsiFileFactory
import com.intellij.psi.impl.PsiFileFactoryImpl import com.intellij.psi.impl.PsiFileFactoryImpl
import com.intellij.testFramework.LightVirtualFile
import org.jetbrains.kotlin.cli.common.environment.setIdeaIoUseFallback import org.jetbrains.kotlin.cli.common.environment.setIdeaIoUseFallback
import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.idea.KotlinLanguage
@@ -20,24 +17,15 @@ import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtScript import org.jetbrains.kotlin.psi.KtScript
import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.scripting.dependencies.ScriptsCompilationDependencies import org.jetbrains.kotlin.scripting.dependencies.ScriptsCompilationDependencies
import org.jetbrains.kotlin.scripting.resolve.ScriptLightVirtualFile
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import java.util.* import java.util.*
import kotlin.script.experimental.api.* import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.FileBasedScriptSource
import kotlin.script.experimental.host.FileScriptSource import kotlin.script.experimental.host.FileScriptSource
import kotlin.script.experimental.host.getMergedScriptText import kotlin.script.experimental.host.getMergedScriptText
import kotlin.script.experimental.jvm.impl.KJvmCompiledScript import kotlin.script.experimental.jvm.impl.KJvmCompiledScript
internal class ScriptLightVirtualFile(name: String, private val _path: String?, text: String) :
LightVirtualFile(name, KotlinLanguage.INSTANCE, StringUtil.convertLineSeparators(text)) {
init {
charset = CharsetToolkit.UTF8_CHARSET
}
override fun getPath(): String = _path ?: super.getPath()
override fun getCanonicalPath(): String? = path
}
internal fun makeCompiledModule(generationState: GenerationState) = internal fun makeCompiledModule(generationState: GenerationState) =
KJvmCompiledModuleInMemory( KJvmCompiledModuleInMemory(
generationState.factory.asList() generationState.factory.asList()
@@ -98,7 +86,7 @@ internal fun getScriptKtFile(
val scriptText = getMergedScriptText(script, scriptCompilationConfiguration) val scriptText = getMergedScriptText(script, scriptCompilationConfiguration)
val virtualFile = ScriptLightVirtualFile( val virtualFile = ScriptLightVirtualFile(
script.scriptFileName(script, scriptCompilationConfiguration), script.scriptFileName(script, scriptCompilationConfiguration),
(script as? FileScriptSource)?.file?.path, (script as? FileBasedScriptSource)?.file?.path,
scriptText scriptText
) )
val ktFile = psiFileFactory.trySetupPsiForFile(virtualFile, KotlinLanguage.INSTANCE, true, false) as KtFile? val ktFile = psiFileFactory.trySetupPsiForFile(virtualFile, KotlinLanguage.INSTANCE, true, false) as KtFile?
@@ -40,7 +40,7 @@ open class BasicJvmScriptEvaluator : ScriptEvaluator {
?.onFailure { ?.onFailure {
return@invoke ResultWithDiagnostics.Failure(it.reports) return@invoke ResultWithDiagnostics.Failure(it.reports)
} }
?.resultOrNull() ?.valueOrNull()
?: configuration ?: configuration
val instance = val instance =
@@ -48,8 +48,13 @@ fun mapLegacyScriptPosition(pos: ScriptReport.Position?): SourceCode.Location? =
} }
fun mapToLegacyScriptReportPosition(pos: SourceCode.Location?): ScriptReport.Position? = fun mapToLegacyScriptReportPosition(pos: SourceCode.Location?): ScriptReport.Position? =
pos?.let { ScriptReport.Position(pos.start.line, pos.start.col) } pos?.let { ScriptReport.Position(pos.start.line, pos.start.col, pos.end?.line, pos.end?.col) }
fun Iterable<ScriptReport>.mapToDiagnostics(): List<ScriptDiagnostic> = map { (message, severity, position: ScriptReport.Position?) -> fun Iterable<ScriptReport>.mapToDiagnostics(): List<ScriptDiagnostic> = map { (message, severity, position) ->
ScriptDiagnostic(message, mapLegacyDiagnosticSeverity(severity), null, mapLegacyScriptPosition(position)) ScriptDiagnostic(message, mapLegacyDiagnosticSeverity(severity), null, mapLegacyScriptPosition(position))
} }
fun Iterable<ScriptDiagnostic>.mapToLegacyReports(): List<ScriptReport> = map { (message, severity, _, location, exception) ->
val reportMessage = if (exception == null) message else "$message ($exception)"
ScriptReport(reportMessage, mapToLegacyScriptReportSeverity(severity), mapToLegacyScriptReportPosition(location))
}
@@ -41,16 +41,6 @@ class BridgeDependenciesResolver(
) )
) )
val defaultImports = scriptCompilationConfiguration[ScriptCompilationConfiguration.defaultImports]?.toList() ?: emptyList()
fun ScriptCompilationConfiguration.toDependencies(classpath: List<File>): ScriptDependencies =
ScriptDependencies(
classpath = classpath,
sources = this[ScriptCompilationConfiguration.ide.dependenciesSources].toClassPathOrEmpty(),
imports = defaultImports,
scripts = this[ScriptCompilationConfiguration.importScripts].toFilesOrEmpty()
)
val script = getScriptSource(scriptContents) ?: scriptContents.toScriptSource() val script = getScriptSource(scriptContents) ?: scriptContents.toScriptSource()
val refineResults = scriptCompilationConfiguration.refineWith( val refineResults = scriptCompilationConfiguration.refineWith(
@@ -92,6 +82,17 @@ class BridgeDependenciesResolver(
} }
} }
fun ScriptCompilationConfiguration.toDependencies(classpath: List<File>): ScriptDependencies {
val defaultImports = this[ScriptCompilationConfiguration.defaultImports]?.toList() ?: emptyList()
return ScriptDependencies(
classpath = classpath,
sources = this[ScriptCompilationConfiguration.ide.dependenciesSources].toClassPathOrEmpty(),
imports = defaultImports,
scripts = this[ScriptCompilationConfiguration.importScripts].toFilesOrEmpty()
)
}
internal fun List<ScriptDiagnostic>.mapScriptReportsToDiagnostics() = internal fun List<ScriptDiagnostic>.mapScriptReportsToDiagnostics() =
map { ScriptReport(it.message, mapToLegacyScriptReportSeverity(it.severity), mapToLegacyScriptReportPosition(it.location)) } map { ScriptReport(it.message, mapToLegacyScriptReportSeverity(it.severity), mapToLegacyScriptReportPosition(it.location)) }
@@ -101,7 +102,7 @@ internal fun ScriptContents.toScriptSource(): SourceCode = when {
else -> throw IllegalArgumentException("Unable to convert script contents $this into script source") else -> throw IllegalArgumentException("Unable to convert script contents $this into script source")
} }
internal fun List<ScriptDependency>?.toClassPathOrEmpty() = this?.flatMap { (it as JvmDependency).classpath } ?: emptyList() fun List<ScriptDependency>?.toClassPathOrEmpty() = this?.flatMap { (it as JvmDependency).classpath } ?: emptyList()
internal fun List<SourceCode>?.toFilesOrEmpty() = this?.map { internal fun List<SourceCode>?.toFilesOrEmpty() = this?.map {
val externalSource = it as? ExternalSourceCode val externalSource = it as? ExternalSourceCode
@@ -9,6 +9,7 @@ package kotlin.script.experimental.jvm
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.script.experimental.api.* import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.util.PropertiesCollection import kotlin.script.experimental.util.PropertiesCollection
interface JvmScriptEvaluationConfigurationKeys interface JvmScriptEvaluationConfigurationKeys
@@ -21,7 +22,10 @@ open class JvmScriptEvaluationConfigurationBuilder : PropertiesCollection.Builde
/** /**
* The base classloader to use for script classes loading * The base classloader to use for script classes loading
*/ */
val JvmScriptEvaluationConfigurationKeys.baseClassLoader by PropertiesCollection.key<ClassLoader?>(Thread.currentThread().contextClassLoader) val JvmScriptEvaluationConfigurationKeys.baseClassLoader by PropertiesCollection.key<ClassLoader?> {
get(ScriptEvaluationConfiguration.hostConfiguration)?.get(ScriptingHostConfiguration.jvm.baseClassLoader)
?: Thread.currentThread().contextClassLoader
}
/** /**
* Load script dependencies before evaluation, true by default * Load script dependencies before evaluation, true by default
@@ -11,6 +11,7 @@ import java.net.URLClassLoader
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.script.experimental.api.* import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.* import kotlin.script.experimental.host.*
import kotlin.script.experimental.jvm.impl.toClassPathOrEmpty
import kotlin.script.experimental.util.PropertiesCollection import kotlin.script.experimental.util.PropertiesCollection
interface JvmScriptingHostConfigurationKeys interface JvmScriptingHostConfigurationKeys
@@ -25,6 +26,12 @@ val JvmScriptingHostConfigurationKeys.javaHome by PropertiesCollection.key<File>
val JvmScriptingHostConfigurationKeys.jdkHome by PropertiesCollection.key<File>() val JvmScriptingHostConfigurationKeys.jdkHome by PropertiesCollection.key<File>()
val JvmScriptingHostConfigurationKeys.baseClassLoader by PropertiesCollection.key<ClassLoader> {
get(ScriptingHostConfiguration.configurationDependencies)?.let {
URLClassLoader(it.toClassPathOrEmpty().map { f -> f.toURI().toURL() }.toTypedArray())
}
}
@Suppress("unused") @Suppress("unused")
val ScriptingHostConfigurationKeys.jvm val ScriptingHostConfigurationKeys.jvm
get() = JvmScriptingHostConfigurationBuilder() get() = JvmScriptingHostConfigurationBuilder()
@@ -49,15 +56,17 @@ class JvmGetScriptingClass : GetScriptingClass, Serializable {
@Transient @Transient
private var baseClassLoader: ClassLoader? = null private var baseClassLoader: ClassLoader? = null
override fun invoke(classType: KotlinType, contextClass: KClass<*>, hostConfiguration: ScriptingHostConfiguration): KClass<*> =
invoke(classType, contextClass.java.classLoader, hostConfiguration)
@Synchronized @Synchronized
override fun invoke(classType: KotlinType, contextClass: KClass<*>, hostConfiguration: ScriptingHostConfiguration): KClass<*> { operator fun invoke(classType: KotlinType, contextClassLoader: ClassLoader?, hostConfiguration: ScriptingHostConfiguration): KClass<*> {
// checking if class already loaded in the same context // checking if class already loaded in the same context
val contextClassloader = contextClass.java.classLoader
val fromClass = classType.fromClass val fromClass = classType.fromClass
if (fromClass != null) { if (fromClass != null) {
if (fromClass.java.classLoader == null) return fromClass // root classloader if (fromClass.java.classLoader == null) return fromClass // root classloader
val actualClassLoadersChain = generateSequence(contextClassloader) { it.parent } val actualClassLoadersChain = generateSequence(contextClassLoader) { it.parent }
if (actualClassLoadersChain.any { it == fromClass.java.classLoader }) return fromClass if (actualClassLoadersChain.any { it == fromClass.java.classLoader }) return fromClass
} }
@@ -71,7 +80,7 @@ class JvmGetScriptingClass : GetScriptingClass, Serializable {
} }
if (baseClassLoaderIsInitialized != true) { if (baseClassLoaderIsInitialized != true) {
baseClassLoader = contextClassloader baseClassLoader = contextClassLoader
baseClassLoaderIsInitialized = true baseClassLoaderIsInitialized = true
} }
// TODO: this check breaks testLazyScriptDefinition, find out the reason and fix // TODO: this check breaks testLazyScriptDefinition, find out the reason and fix
@@ -88,12 +97,12 @@ class JvmGetScriptingClass : GetScriptingClass, Serializable {
} }
} }
classLoader = classLoader =
if (classpath == null || classpath.isEmpty()) baseClassLoader if (classpath == null || classpath.isEmpty()) baseClassLoader
else URLClassLoader(classpath.toTypedArray(), baseClassLoader) else URLClassLoader(classpath.toTypedArray(), baseClassLoader)
} }
return try { return try {
classLoader!!.loadClass(classType.typeName).kotlin (classLoader ?: ClassLoader.getSystemClassLoader()).loadClass(classType.typeName).kotlin
} catch (e: Throwable) { } catch (e: Throwable) {
throw IllegalArgumentException("unable to load class $classType", e) throw IllegalArgumentException("unable to load class $classType", e)
} }
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionsFromClasspathDiscoverySource import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionsFromClasspathDiscoverySource
import org.jetbrains.kotlin.scripting.definitions.reporter import org.jetbrains.kotlin.scripting.definitions.reporter
import java.io.File import java.io.File
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
private const val MIN_SUPPORTED_GRADLE_MAJOR_VERSION = 5 private const val MIN_SUPPORTED_GRADLE_MAJOR_VERSION = 5
private const val MIN_SUPPORTED_GRADLE_MINOR_VERSION = 0 private const val MIN_SUPPORTED_GRADLE_MINOR_VERSION = 0
@@ -166,7 +167,8 @@ internal class DiscoverScriptExtensionsTransform : ArtifactTransform() {
override fun transform(input: File): List<File> { override fun transform(input: File): List<File> {
val definitions = val definitions =
ScriptDefinitionsFromClasspathDiscoverySource( ScriptDefinitionsFromClasspathDiscoverySource(
listOf(input), emptyMap(), listOf(input),
defaultJvmScriptingHostConfiguration,
PrintingMessageCollector(System.out, MessageRenderer.WITHOUT_PATHS, false).reporter PrintingMessageCollector(System.out, MessageRenderer.WITHOUT_PATHS, false).reporter
).definitions ).definitions
val extensions = definitions.mapTo(arrayListOf()) { it.fileExtension } val extensions = definitions.mapTo(arrayListOf()) { it.fileExtension }
@@ -14,6 +14,7 @@ import kotlin.script.dependencies.ScriptContents
import kotlin.script.dependencies.ScriptDependenciesResolver import kotlin.script.dependencies.ScriptDependenciesResolver
import kotlin.script.experimental.annotations.KotlinScript import kotlin.script.experimental.annotations.KotlinScript
import kotlin.script.experimental.api.* import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.FileBasedScriptSource
import kotlin.script.experimental.host.FileScriptSource import kotlin.script.experimental.host.FileScriptSource
import kotlin.script.experimental.jvm.compat.mapLegacyDiagnosticSeverity import kotlin.script.experimental.jvm.compat.mapLegacyDiagnosticSeverity
import kotlin.script.experimental.jvm.compat.mapLegacyScriptPosition import kotlin.script.experimental.jvm.compat.mapLegacyScriptPosition
@@ -59,7 +60,7 @@ class MainKtsConfigurator : RefineScriptCompilationConfigurationHandler {
val annotations = context.collectedData?.get(ScriptCollectedData.foundAnnotations)?.takeIf { it.isNotEmpty() } val annotations = context.collectedData?.get(ScriptCollectedData.foundAnnotations)?.takeIf { it.isNotEmpty() }
?: return context.compilationConfiguration.asSuccess() ?: return context.compilationConfiguration.asSuccess()
val scriptBaseDir = (context.script as? FileScriptSource)?.file?.parentFile val scriptBaseDir = (context.script as? FileBasedScriptSource)?.file?.parentFile
val importedSources = annotations.flatMap { val importedSources = annotations.flatMap {
(it as? Import)?.paths?.map { sourceName -> (it as? Import)?.paths?.map { sourceName ->
FileScriptSource(scriptBaseDir?.resolve(sourceName) ?: File(sourceName)) FileScriptSource(scriptBaseDir?.resolve(sourceName) ?: File(sourceName))
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.maven;
import com.intellij.openapi.Disposable; import com.intellij.openapi.Disposable;
import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Disposer;
import kotlin.script.experimental.jvm.JvmScriptingHostConfigurationKt;
import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.DefaultArtifact; import org.apache.maven.artifact.DefaultArtifact;
import org.apache.maven.artifact.handler.ArtifactHandler; import org.apache.maven.artifact.handler.ArtifactHandler;
@@ -188,7 +189,8 @@ public class ExecuteKotlinScriptMojo extends AbstractMojo {
configuration.put(CommonConfigurationKeys.MODULE_NAME, JvmProtoBufUtil.DEFAULT_MODULE_NAME); configuration.put(CommonConfigurationKeys.MODULE_NAME, JvmProtoBufUtil.DEFAULT_MODULE_NAME);
ConfigurationKt.configureScriptDefinitions( ConfigurationKt.configureScriptDefinitions(
scriptTemplates, configuration, this.getClass().getClassLoader(), messageCollector, new HashMap<>() scriptTemplates, configuration, this.getClass().getClassLoader(), messageCollector,
JvmScriptingHostConfigurationKt.getDefaultJvmScriptingHostConfiguration()
); );
KotlinCoreEnvironment environment = KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES); KotlinCoreEnvironment environment = KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES);
@@ -36,6 +36,7 @@ import org.jetbrains.kotlin.script.util.templates.StandardArgsScriptTemplateWith
import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCompilerConfigurationComponentRegistrar import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCompilerConfigurationComponentRegistrar
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
import org.jetbrains.kotlin.utils.PathUtil.getResourcePathForClass import org.jetbrains.kotlin.utils.PathUtil.getResourcePathForClass
import org.junit.Assert import org.junit.Assert
@@ -44,6 +45,7 @@ import java.io.ByteArrayOutputStream
import java.io.OutputStream import java.io.OutputStream
import java.io.PrintStream import java.io.PrintStream
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
const val KOTLIN_JAVA_RUNTIME_JAR = "kotlin-stdlib.jar" const val KOTLIN_JAVA_RUNTIME_JAR = "kotlin-stdlib.jar"
@@ -155,7 +157,12 @@ done
} }
} }
put(CommonConfigurationKeys.MODULE_NAME, "kotlin-script-util-test") put(CommonConfigurationKeys.MODULE_NAME, "kotlin-script-util-test")
add(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition) add(
ScriptingConfigurationKeys.SCRIPT_DEFINITIONS,
ScriptDefinition.FromLegacy(
defaultJvmScriptingHostConfiguration, scriptDefinition
)
)
put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true) put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true)
add(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, ScriptingCompilerConfigurationComponentRegistrar()) add(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, ScriptingCompilerConfigurationComponentRegistrar())
@@ -19,8 +19,10 @@ package org.jetbrains.kotlin.samWithReceiver
import org.jetbrains.kotlin.checkers.AbstractDiagnosticsTest import org.jetbrains.kotlin.checkers.AbstractDiagnosticsTest
import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
import java.io.File import java.io.File
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.extensions.SamWithReceiverAnnotations import kotlin.script.extensions.SamWithReceiverAnnotations
import kotlin.script.templates.ScriptTemplateDefinition import kotlin.script.templates.ScriptTemplateDefinition
@@ -31,7 +33,10 @@ abstract class AbstractSamWithReceiverScriptTest : AbstractDiagnosticsTest() {
override fun createEnvironment(file: File) = super.createEnvironment(file).apply { override fun createEnvironment(file: File) = super.createEnvironment(file).apply {
StorageComponentContainerContributor.registerExtension(project, CliSamWithReceiverComponentContributor(TEST_ANNOTATIONS)) StorageComponentContainerContributor.registerExtension(project, CliSamWithReceiverComponentContributor(TEST_ANNOTATIONS))
val def = KotlinScriptDefinitionFromAnnotatedTemplate(ScriptForSamWithReceivers::class, emptyMap()) val def = ScriptDefinition.FromLegacy(
defaultJvmScriptingHostConfiguration,
KotlinScriptDefinitionFromAnnotatedTemplate(ScriptForSamWithReceivers::class, emptyMap())
)
configuration.add(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, def) configuration.add(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, def)
} }
} }
@@ -65,12 +65,12 @@ class IdeSamWithReceiverComponentContributor(val project: Project) : StorageComp
val moduleInfo = moduleDescriptor.getCapability(ModuleInfo.Capability) val moduleInfo = moduleDescriptor.getCapability(ModuleInfo.Capability)
val annotations = val annotations =
when (moduleInfo) { when (moduleInfo) {
is ScriptModuleInfo -> moduleInfo.scriptDefinition.annotationsForSamWithReceivers is ScriptModuleInfo -> moduleInfo.scriptDefinition.legacyDefinition.annotationsForSamWithReceivers
is ScriptDependenciesInfo.ForFile -> moduleInfo.scriptDefinition.annotationsForSamWithReceivers is ScriptDependenciesInfo.ForFile -> moduleInfo.scriptDefinition.legacyDefinition.annotationsForSamWithReceivers
is ModuleProductionSourceInfo -> getAnnotationsForModule(moduleInfo.module) is ModuleProductionSourceInfo -> getAnnotationsForModule(moduleInfo.module)
else -> null else -> null
} ?: return } ?: return
container.useInstance(SamWithReceiverResolverExtension(annotations)) container.useInstance(SamWithReceiverResolverExtension(annotations))
} }
@@ -6,13 +6,13 @@
package org.jetbrains.kotlin.scripting.configuration package org.jetbrains.kotlin.scripting.configuration
import org.jetbrains.kotlin.config.CompilerConfigurationKey import org.jetbrains.kotlin.config.CompilerConfigurationKey
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionsSource import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionsSource
import java.io.File import java.io.File
object ScriptingConfigurationKeys { object ScriptingConfigurationKeys {
val SCRIPT_DEFINITIONS = CompilerConfigurationKey.create<List<KotlinScriptDefinition>>("script definitions") val SCRIPT_DEFINITIONS = CompilerConfigurationKey.create<List<ScriptDefinition>>("script definitions")
val SCRIPT_DEFINITIONS_SOURCES = val SCRIPT_DEFINITIONS_SOURCES =
CompilerConfigurationKey.create<List<ScriptDefinitionsSource>>("script definitions sources") CompilerConfigurationKey.create<List<ScriptDefinitionsSource>>("script definitions sources")
@@ -5,11 +5,14 @@
package org.jetbrains.kotlin.scripting.configuration package org.jetbrains.kotlin.scripting.configuration
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.scripting.definitions.loadScriptTemplatesFromClasspath import org.jetbrains.kotlin.scripting.definitions.loadScriptTemplatesFromClasspath
import org.jetbrains.kotlin.scripting.definitions.reporter import org.jetbrains.kotlin.scripting.definitions.reporter
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
const val KOTLIN_SCRIPTING_PLUGIN_ID = "kotlin.scripting" const val KOTLIN_SCRIPTING_PLUGIN_ID = "kotlin.scripting"
@@ -18,12 +21,16 @@ fun configureScriptDefinitions(
configuration: CompilerConfiguration, configuration: CompilerConfiguration,
baseClassloader: ClassLoader, baseClassloader: ClassLoader,
messageCollector: MessageCollector, messageCollector: MessageCollector,
scriptResolverEnv: Map<String, Any?> hostConfiguration: ScriptingHostConfiguration
) { ) {
// TODO: consider using escaping to allow kotlin escaped names in class names // TODO: consider using escaping to allow kotlin escaped names in class names
val templatesFromClasspath = loadScriptTemplatesFromClasspath( val templatesFromClasspath = loadScriptTemplatesFromClasspath(
scriptTemplates, configuration.jvmClasspathRoots, emptyList(), baseClassloader, scriptResolverEnv, messageCollector.reporter scriptTemplates, configuration.jvmClasspathRoots, emptyList(), baseClassloader, hostConfiguration, messageCollector.reporter
) )
configuration.addAll(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, templatesFromClasspath.toList()) configuration.addAll(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, templatesFromClasspath.toList())
} }
fun makeHostConfiguration(project: Project, configuration: CompilerConfiguration): ScriptingHostConfiguration =
ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) {
// TODO: add jdk path and other params if needed
}
@@ -17,6 +17,8 @@ import kotlin.reflect.KType
import kotlin.script.experimental.dependencies.DependenciesResolver import kotlin.script.experimental.dependencies.DependenciesResolver
import kotlin.script.templates.standard.ScriptTemplateWithArgs import kotlin.script.templates.standard.ScriptTemplateWithArgs
// Legacy definition, will be obsolete soon
// TODO: make deprecated and drop usages where possible
open class KotlinScriptDefinition(open val template: KClass<out Any>) : UserDataHolderBase() { open class KotlinScriptDefinition(open val template: KClass<out Any>) : UserDataHolderBase() {
open val name: String = "Kotlin Script" open val name: String = "Kotlin Script"
@@ -17,6 +17,7 @@ import kotlin.script.experimental.api.*
import kotlin.script.experimental.dependencies.DependenciesResolver import kotlin.script.experimental.dependencies.DependenciesResolver
import kotlin.script.experimental.host.ScriptingHostConfiguration import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.host.getScriptingClass import kotlin.script.experimental.host.getScriptingClass
import kotlin.script.experimental.jvm.compat.mapToLegacyExpectedLocations
import kotlin.script.experimental.jvm.impl.BridgeDependenciesResolver import kotlin.script.experimental.jvm.impl.BridgeDependenciesResolver
import kotlin.script.experimental.location.ScriptExpectedLocation import kotlin.script.experimental.location.ScriptExpectedLocation
import kotlin.script.experimental.util.getOrError import kotlin.script.experimental.util.getOrError
@@ -80,15 +81,8 @@ abstract class KotlinScriptDefinitionAdapterFromNewAPIBase : KotlinScriptDefinit
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
override val scriptExpectedLocations: List<ScriptExpectedLocation> override val scriptExpectedLocations: List<ScriptExpectedLocation>
get() = scriptCompilationConfiguration[ScriptCompilationConfiguration.ide.acceptedLocations]?.map { get() = scriptCompilationConfiguration[ScriptCompilationConfiguration.ide.acceptedLocations]?.mapToLegacyExpectedLocations()
when (it) { ?: listOf(ScriptExpectedLocation.SourcesOnly, ScriptExpectedLocation.TestsOnly)
ScriptAcceptedLocation.Sources -> ScriptExpectedLocation.SourcesOnly
ScriptAcceptedLocation.Tests -> ScriptExpectedLocation.TestsOnly
ScriptAcceptedLocation.Libraries -> ScriptExpectedLocation.Libraries
ScriptAcceptedLocation.Project -> ScriptExpectedLocation.Project
ScriptAcceptedLocation.Everywhere -> ScriptExpectedLocation.Everywhere
}
} ?: listOf(ScriptExpectedLocation.SourcesOnly, ScriptExpectedLocation.TestsOnly)
private val scriptingClassGetter by lazy(LazyThreadSafetyMode.PUBLICATION) { private val scriptingClassGetter by lazy(LazyThreadSafetyMode.PUBLICATION) {
hostConfiguration[ScriptingHostConfiguration.getScriptingClass] hostConfiguration[ScriptingHostConfiguration.getScriptingClass]
@@ -9,10 +9,17 @@ import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
interface ScriptDefinitionProvider { interface ScriptDefinitionProvider {
@Deprecated("Migrating to configuration refinement", level = DeprecationLevel.ERROR)
fun findScriptDefinition(fileName: String): KotlinScriptDefinition? fun findScriptDefinition(fileName: String): KotlinScriptDefinition?
fun isScript(fileName: String): Boolean
@Deprecated("Migrating to configuration refinement", level = DeprecationLevel.ERROR)
fun getDefaultScriptDefinition(): KotlinScriptDefinition fun getDefaultScriptDefinition(): KotlinScriptDefinition
fun isScript(fileName: String): Boolean
fun findDefinition(fileName: String): ScriptDefinition?
fun getDefaultDefinition(): ScriptDefinition
fun getKnownFilenameExtensions(): Sequence<String> fun getKnownFilenameExtensions(): Sequence<String>
companion object { companion object {
@@ -21,4 +28,3 @@ interface ScriptDefinitionProvider {
} }
} }
@@ -10,44 +10,56 @@ import java.io.File
import kotlin.script.experimental.annotations.KotlinScript import kotlin.script.experimental.annotations.KotlinScript
import kotlin.script.experimental.api.KotlinType import kotlin.script.experimental.api.KotlinType
import kotlin.script.experimental.api.ScriptCompilationConfiguration import kotlin.script.experimental.api.ScriptCompilationConfiguration
import kotlin.script.experimental.api.ScriptEvaluationConfiguration
import kotlin.script.experimental.api.fileExtension import kotlin.script.experimental.api.fileExtension
import kotlin.script.experimental.host.ScriptingHostConfiguration import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.host.configurationDependencies import kotlin.script.experimental.host.configurationDependencies
import kotlin.script.experimental.host.createCompilationConfigurationFromTemplate import kotlin.script.experimental.host.createCompilationConfigurationFromTemplate
import kotlin.script.experimental.host.createEvaluationConfigurationFromTemplate
import kotlin.script.experimental.jvm.JvmDependency import kotlin.script.experimental.jvm.JvmDependency
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
class LazyScriptDefinitionFromDiscoveredClass internal constructor( class LazyScriptDefinitionFromDiscoveredClass internal constructor(
baseHostConfiguration: ScriptingHostConfiguration,
private val annotationsFromAsm: ArrayList<BinAnnData>, private val annotationsFromAsm: ArrayList<BinAnnData>,
private val className: String, private val className: String,
private val classpath: List<File>, private val classpath: List<File>,
private val messageReporter: MessageReporter private val messageReporter: MessageReporter
) : KotlinScriptDefinitionAdapterFromNewAPIBase() { ) : ScriptDefinition.FromConfigurationsBase() {
constructor( constructor(
baseHostConfiguration: ScriptingHostConfiguration,
classBytes: ByteArray, classBytes: ByteArray,
className: String, className: String,
classpath: List<File>, classpath: List<File>,
messageReporter: MessageReporter messageReporter: MessageReporter
) : this(loadAnnotationsFromClass(classBytes), className, classpath, messageReporter) ) : this(baseHostConfiguration, loadAnnotationsFromClass(classBytes), className, classpath, messageReporter)
override val hostConfiguration: ScriptingHostConfiguration by lazy(LazyThreadSafetyMode.PUBLICATION) { override val hostConfiguration: ScriptingHostConfiguration by lazy(LazyThreadSafetyMode.PUBLICATION) {
ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) { ScriptingHostConfiguration(baseHostConfiguration) {
configurationDependencies.append(JvmDependency(classpath)) configurationDependencies.append(JvmDependency(classpath))
} }
} }
override val scriptCompilationConfiguration: ScriptCompilationConfiguration by lazy(LazyThreadSafetyMode.PUBLICATION) { private val configurations by lazy(LazyThreadSafetyMode.PUBLICATION) {
messageReporter( messageReporter(
CompilerMessageSeverity.LOGGING, CompilerMessageSeverity.LOGGING,
"Configure scripting: loading script definition class $className using classpath $classpath\n. ${Thread.currentThread().stackTrace}" "Configure scripting: loading script definition class $className using classpath $classpath\n. ${Thread.currentThread().stackTrace}"
) )
try { try {
createCompilationConfigurationFromTemplate( val compileCfg =
KotlinType(className), createCompilationConfigurationFromTemplate(
hostConfiguration, KotlinType(className),
LazyScriptDefinitionFromDiscoveredClass::class hostConfiguration,
) LazyScriptDefinitionFromDiscoveredClass::class
)
val evalCfg =
createEvaluationConfigurationFromTemplate(
KotlinType(className),
hostConfiguration,
LazyScriptDefinitionFromDiscoveredClass::class
)
compileCfg to evalCfg
} catch (ex: ClassNotFoundException) { } catch (ex: ClassNotFoundException) {
messageReporter(CompilerMessageSeverity.ERROR, "Cannot find script definition class $className") messageReporter(CompilerMessageSeverity.ERROR, "Cannot find script definition class $className")
InvalidScriptDefinition InvalidScriptDefinition
@@ -60,10 +72,13 @@ class LazyScriptDefinitionFromDiscoveredClass internal constructor(
} }
} }
override val compilationConfiguration: ScriptCompilationConfiguration get() = configurations.first
override val evaluationConfiguration: ScriptEvaluationConfiguration get() = configurations.second
override val fileExtension: String by lazy(LazyThreadSafetyMode.PUBLICATION) { override val fileExtension: String by lazy(LazyThreadSafetyMode.PUBLICATION) {
annotationsFromAsm.find { it.name == KotlinScript::class.simpleName }?.args annotationsFromAsm.find { it.name == KotlinScript::class.simpleName }?.args
?.find { it.name == "fileExtension" }?.value ?.find { it.name == "fileExtension" }?.value
?: scriptCompilationConfiguration.let { ?: compilationConfiguration.let {
it[ScriptCompilationConfiguration.fileExtension] ?: super.fileExtension it[ScriptCompilationConfiguration.fileExtension] ?: super.fileExtension
} }
} }
@@ -74,4 +89,4 @@ class LazyScriptDefinitionFromDiscoveredClass internal constructor(
} }
} }
val InvalidScriptDefinition = ScriptCompilationConfiguration() val InvalidScriptDefinition = ScriptCompilationConfiguration() to ScriptEvaluationConfiguration()
@@ -10,15 +10,22 @@ import org.jetbrains.kotlin.idea.KotlinFileType
import java.util.concurrent.locks.ReentrantReadWriteLock import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read import kotlin.concurrent.read
import kotlin.concurrent.write import kotlin.concurrent.write
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
abstract class LazyScriptDefinitionProvider : ScriptDefinitionProvider { abstract class LazyScriptDefinitionProvider : ScriptDefinitionProvider {
protected val lock = ReentrantReadWriteLock() protected val lock = ReentrantReadWriteLock()
protected abstract val currentDefinitions: Sequence<KotlinScriptDefinition> protected abstract val currentDefinitions: Sequence<ScriptDefinition>
private var _cachedDefinitions: Sequence<KotlinScriptDefinition>? = null protected open fun getScriptingHostConfiguration(): ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration
private val cachedDefinitions: Sequence<KotlinScriptDefinition>
override fun getDefaultDefinition(): ScriptDefinition =
ScriptDefinition.FromLegacy(getScriptingHostConfiguration(), StandardScriptDefinition)
private var _cachedDefinitions: Sequence<ScriptDefinition>? = null
private val cachedDefinitions: Sequence<ScriptDefinition>
get() { get() {
assert(lock.readLockCount > 0) { "cachedDefinitions should only be used under the read lock" } assert(lock.readLockCount > 0) { "cachedDefinitions should only be used under the read lock" }
if (_cachedDefinitions == null) lock.write { if (_cachedDefinitions == null) lock.write {
@@ -37,18 +44,22 @@ abstract class LazyScriptDefinitionProvider : ScriptDefinitionProvider {
fileName.endsWith(it, ignoreCase = true) fileName.endsWith(it, ignoreCase = true)
} }
override fun findScriptDefinition(fileName: String): KotlinScriptDefinition? = override fun findDefinition(fileName: String): ScriptDefinition? =
if (nonScriptFileName(fileName)) null if (nonScriptFileName(fileName)) null
else lock.read { else lock.read {
cachedDefinitions.firstOrNull { it.isScript(fileName) } cachedDefinitions.firstOrNull { it.isScript(fileName) }
} }
override fun isScript(fileName: String) = findScriptDefinition(fileName) != null override fun findScriptDefinition(fileName: String): KotlinScriptDefinition? = findDefinition(fileName)?.legacyDefinition
override fun isScript(fileName: String) = findDefinition(fileName) != null
override fun getKnownFilenameExtensions(): Sequence<String> = lock.read { override fun getKnownFilenameExtensions(): Sequence<String> = lock.read {
cachedDefinitions.map { it.fileExtension } cachedDefinitions.map { it.fileExtension }
} }
override fun getDefaultScriptDefinition(): KotlinScriptDefinition = getDefaultDefinition().legacyDefinition
companion object { companion object {
// TODO: find a common place for storing kotlin-related extensions and reuse values from it everywhere // TODO: find a common place for storing kotlin-related extensions and reuse values from it everywhere
protected val nonScriptFilenameSuffixes = arrayOf(".${KotlinFileType.EXTENSION}", ".${JavaFileType.DEFAULT_EXTENSION}") protected val nonScriptFilenameSuffixes = arrayOf(".${KotlinFileType.EXTENSION}", ".${JavaFileType.DEFAULT_EXTENSION}")
@@ -0,0 +1,90 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.scripting.definitions
import java.io.File
import kotlin.script.dependencies.Environment
import kotlin.script.dependencies.ScriptContents
import kotlin.script.experimental.api.*
import kotlin.script.experimental.dependencies.DependenciesResolver
import kotlin.script.experimental.host.FileBasedScriptSource
import kotlin.script.experimental.host.FileScriptSource
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.host.ScriptingHostConfigurationKeys
import kotlin.script.experimental.jvm.JvmDependency
import kotlin.script.experimental.jvm.compat.mapLegacyDiagnosticSeverity
import kotlin.script.experimental.jvm.compat.mapLegacyExpectedLocations
import kotlin.script.experimental.jvm.jdkHome
import kotlin.script.experimental.jvm.jvm
import kotlin.script.experimental.util.PropertiesCollection
class ScriptCompilationConfigurationFromDefinition(
val hostConfiguration: ScriptingHostConfiguration,
val scriptDefinition: KotlinScriptDefinition
) : ScriptCompilationConfiguration(
{
hostConfiguration(hostConfiguration)
displayName(scriptDefinition.name)
fileExtension(scriptDefinition.fileExtension)
baseClass(KotlinType(scriptDefinition.template))
implicitReceivers.putIfAny(scriptDefinition.implicitReceivers.map(::KotlinType))
providedProperties.putIfAny(scriptDefinition.providedProperties.map { it.first to KotlinType(it.second) })
annotationsForSamWithReceivers.put(scriptDefinition.annotationsForSamWithReceivers.map(::KotlinType))
platform(scriptDefinition.platform)
compilerOptions.putIfAny(scriptDefinition.additionalCompilerArguments)
ide {
acceptedLocations.put(scriptDefinition.scriptExpectedLocations.mapLegacyExpectedLocations())
}
if (scriptDefinition.dependencyResolver != DependenciesResolver.NoDependencies) {
refineConfiguration {
onAnnotations(scriptDefinition.acceptedAnnotations.map(::KotlinType)) { context ->
val resolveResult: DependenciesResolver.ResolveResult = scriptDefinition.dependencyResolver.resolve(
ScriptContentsFromRefinementContext(context),
context.compilationConfiguration[ScriptCompilationConfiguration.hostConfiguration]?.let {
it[ScriptingHostConfiguration.getEnvironment]?.invoke()
}.orEmpty()
)
val reports = resolveResult.reports.map { ScriptDiagnostic(it.message, mapLegacyDiagnosticSeverity(it.severity)) }
val resolvedDeps = (resolveResult as? DependenciesResolver.ResolveResult.Success)?.dependencies
if (resolvedDeps == null) ResultWithDiagnostics.Failure(reports)
else ScriptCompilationConfiguration(context.compilationConfiguration) {
if (resolvedDeps.classpath.isNotEmpty()) {
dependencies.append(JvmDependency(resolvedDeps.classpath))
}
defaultImports.append(resolvedDeps.imports)
importScripts.append(resolvedDeps.scripts.map { FileScriptSource(it) })
jvm {
jdkHome.putIfNotNull(resolvedDeps.javaHome) // TODO: check if it is correct to supply javaHome as jdkHome
}
if (resolvedDeps.sources.isNotEmpty()) {
ide {
dependenciesSources.append(JvmDependency(resolvedDeps.sources))
}
}
}.asSuccess(reports)
}
}
}
}
)
private class ScriptContentsFromRefinementContext(val context: ScriptConfigurationRefinementContext) : ScriptContents {
override val file: File?
get() = (context.script as? FileBasedScriptSource)?.file
override val annotations: Iterable<Annotation>
get() = context.collectedData?.get(ScriptCollectedData.foundAnnotations) ?: emptyList()
override val text: CharSequence?
get() = context.script.text
}
val ScriptCompilationConfigurationKeys.annotationsForSamWithReceivers by PropertiesCollection.key<List<KotlinType>>()
val ScriptCompilationConfigurationKeys.platform by PropertiesCollection.key<String>()
val ScriptingHostConfigurationKeys.getEnvironment by PropertiesCollection.key<() -> Environment?>()
@@ -0,0 +1,164 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.scripting.definitions
import com.intellij.openapi.util.UserDataHolderBase
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
import java.io.File
import kotlin.reflect.KClass
import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.host.createCompilationConfigurationFromTemplate
import kotlin.script.experimental.host.createEvaluationConfigurationFromTemplate
import kotlin.script.experimental.jvm.baseClassLoader
import kotlin.script.experimental.jvm.jvm
// Transitional class/implementation - migrating to the new API
// TODO: deprecate KotlinScriptDefinition
// TODO: name could be confused with KotlinScriptDefinition, discuss naming
abstract class ScriptDefinition : UserDataHolderBase() {
abstract val legacyDefinition: KotlinScriptDefinition
abstract val hostConfiguration: ScriptingHostConfiguration
abstract val compilationConfiguration: ScriptCompilationConfiguration
abstract val evaluationConfiguration: ScriptEvaluationConfiguration
abstract fun isScript(fileName: String): Boolean
abstract val fileExtension: String
abstract val name: String
// TODO: used in settings, find out the reason and refactor accordingly
abstract val definitionId: String
abstract val contextClassLoader: ClassLoader?
// Target platform for script, ex. "JVM", "JS", "NATIVE"
open val platform: String
get() = "JVM"
open val isDefault = false
abstract val baseClassType: KotlinType
abstract val compilerOptions: Iterable<String>
inline fun <reified T : KotlinScriptDefinition> asLegacyOrNull(): T? =
if (this is FromLegacy) legacyDefinition as? T else null
open class FromLegacy(
override val hostConfiguration: ScriptingHostConfiguration,
override val legacyDefinition: KotlinScriptDefinition
) : ScriptDefinition() {
override val compilationConfiguration by lazy {
ScriptCompilationConfigurationFromDefinition(
hostConfiguration,
legacyDefinition
)
}
override val evaluationConfiguration by lazy {
ScriptEvaluationConfigurationFromDefinition(
hostConfiguration,
legacyDefinition
)
}
override fun isScript(fileName: String): Boolean = legacyDefinition.isScript(fileName)
override val fileExtension: String get() = legacyDefinition.fileExtension
override val name: String get() = legacyDefinition.name
override val definitionId: String get() = legacyDefinition::class.qualifiedName ?: "unknown"
override val platform: String
get() = legacyDefinition.platform
override val contextClassLoader: ClassLoader?
get() = legacyDefinition.template.java.classLoader
override val baseClassType: KotlinType
get() = KotlinType(legacyDefinition.template)
override val compilerOptions: Iterable<String>
get() = legacyDefinition.additionalCompilerArguments ?: emptyList()
override fun equals(other: Any?): Boolean = this === other || legacyDefinition == (other as? FromLegacy)?.legacyDefinition
override fun hashCode(): Int = legacyDefinition.hashCode()
}
open class FromLegacyTemplate(
hostConfiguration: ScriptingHostConfiguration,
template: KClass<*>,
templateClasspath: List<File> = emptyList()
) : FromLegacy(
hostConfiguration,
KotlinScriptDefinitionFromAnnotatedTemplate(
template,
hostConfiguration[ScriptingHostConfiguration.getEnvironment]?.invoke(),
templateClasspath
)
)
abstract class FromConfigurationsBase : ScriptDefinition() {
override val legacyDefinition by lazy {
KotlinScriptDefinitionAdapterFromNewAPI(
compilationConfiguration,
hostConfiguration
)
}
override fun isScript(fileName: String): Boolean = fileName.endsWith(".$fileExtension")
override val fileExtension: String get() = compilationConfiguration[ScriptCompilationConfiguration.fileExtension]!!
override val name: String get() = compilationConfiguration[ScriptCompilationConfiguration.displayName]!!
override val definitionId: String get() = compilationConfiguration[ScriptCompilationConfiguration.baseClass]!!.typeName
override val contextClassLoader: ClassLoader? by lazy {
compilationConfiguration[ScriptCompilationConfiguration.baseClass]?.fromClass?.java?.classLoader
?: hostConfiguration[ScriptingHostConfiguration.jvm.baseClassLoader]
}
override val baseClassType: KotlinType
get() = compilationConfiguration[ScriptCompilationConfiguration.baseClass]!!
override val compilerOptions: Iterable<String>
get() = compilationConfiguration[ScriptCompilationConfiguration.compilerOptions].orEmpty()
override fun equals(other: Any?): Boolean = this === other ||
(other as? FromConfigurations)?.let {
compilationConfiguration == it.compilationConfiguration && evaluationConfiguration == it.evaluationConfiguration
} == true
override fun hashCode(): Int = compilationConfiguration.hashCode() + 37 * evaluationConfiguration.hashCode()
}
open class FromConfigurations(
override val hostConfiguration: ScriptingHostConfiguration,
override val compilationConfiguration: ScriptCompilationConfiguration,
override val evaluationConfiguration: ScriptEvaluationConfiguration
) : FromConfigurationsBase()
open class FromTemplate(
hostConfiguration: ScriptingHostConfiguration,
template: KClass<*>,
contextClass: KClass<*> = ScriptCompilationConfiguration::class
) : FromConfigurations(
hostConfiguration,
createCompilationConfigurationFromTemplate(KotlinType(template), hostConfiguration, contextClass),
createEvaluationConfigurationFromTemplate(KotlinType(template), hostConfiguration, contextClass)
)
companion object {
fun getDefault(hostConfiguration: ScriptingHostConfiguration) =
object : FromLegacy(hostConfiguration, StandardScriptDefinition) {
override val isDefault = true
}
}
}
@@ -7,5 +7,5 @@ package org.jetbrains.kotlin.scripting.definitions
interface ScriptDefinitionsSource { interface ScriptDefinitionsSource {
val definitions: Sequence<KotlinScriptDefinition> val definitions: Sequence<ScriptDefinition>
} }
@@ -9,11 +9,24 @@ import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationResult
import kotlin.script.experimental.api.valueOrNull
import kotlin.script.experimental.dependencies.ScriptDependencies import kotlin.script.experimental.dependencies.ScriptDependencies
interface ScriptDependenciesProvider { interface ScriptDependenciesProvider {
fun getScriptDependencies(file: VirtualFile): ScriptDependencies?
fun getScriptDependencies(file: PsiFile) = getScriptDependencies(file.virtualFile ?: file.originalFile.virtualFile) @Deprecated("Migrating to configuration refinement", level = DeprecationLevel.ERROR)
fun getScriptDependencies(file: VirtualFile): ScriptDependencies? =
getScriptConfigurationResult(file)?.valueOrNull()?.legacyDependencies
@Deprecated("Migrating to configuration refinement", level = DeprecationLevel.ERROR)
fun getScriptDependencies(file: PsiFile): ScriptDependencies? =
getScriptConfigurationResult(file.virtualFile ?: file.originalFile.virtualFile)?.valueOrNull()?.legacyDependencies
fun getScriptConfigurationResult(file: VirtualFile): ScriptCompilationConfigurationResult? = null
fun getScriptConfigurationResult(file: PsiFile): ScriptCompilationConfigurationResult? =
getScriptConfigurationResult(file.virtualFile ?: file.originalFile.virtualFile)
companion object { companion object {
fun getInstance(project: Project): ScriptDependenciesProvider? = fun getInstance(project: Project): ScriptDependenciesProvider? =
@@ -0,0 +1,19 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.scripting.definitions
import kotlin.script.experimental.api.ScriptEvaluationConfiguration
import kotlin.script.experimental.api.hostConfiguration
import kotlin.script.experimental.host.ScriptingHostConfiguration
class ScriptEvaluationConfigurationFromDefinition(
val hostConfiguration: ScriptingHostConfiguration,
val scriptDefinition: KotlinScriptDefinition
) : ScriptEvaluationConfiguration(
{
hostConfiguration(hostConfiguration)
}
)
@@ -11,13 +11,13 @@ import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotate
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.lang.IllegalArgumentException
import java.net.URLClassLoader import java.net.URLClassLoader
import java.util.jar.JarFile import java.util.jar.JarFile
import kotlin.script.experimental.annotations.KotlinScript import kotlin.script.experimental.annotations.KotlinScript
import kotlin.script.experimental.api.KotlinType import kotlin.script.experimental.api.KotlinType
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.host.createCompilationConfigurationFromTemplate import kotlin.script.experimental.host.createCompilationConfigurationFromTemplate
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration import kotlin.script.experimental.host.createEvaluationConfigurationFromTemplate
import kotlin.script.templates.ScriptTemplateDefinition import kotlin.script.templates.ScriptTemplateDefinition
const val SCRIPT_DEFINITION_MARKERS_PATH = "META-INF/kotlin/script/templates/" const val SCRIPT_DEFINITION_MARKERS_PATH = "META-INF/kotlin/script/templates/"
@@ -32,15 +32,15 @@ val MessageCollector.reporter: MessageReporter
class ScriptDefinitionsFromClasspathDiscoverySource( class ScriptDefinitionsFromClasspathDiscoverySource(
private val classpath: List<File>, private val classpath: List<File>,
private val scriptResolverEnv: Map<String, Any?>, private val hostConfiguration: ScriptingHostConfiguration,
private val messageReporter: MessageReporter private val messageReporter: MessageReporter
) : ScriptDefinitionsSource { ) : ScriptDefinitionsSource {
override val definitions: Sequence<KotlinScriptDefinition> = run { override val definitions: Sequence<ScriptDefinition> = run {
discoverScriptTemplatesInClasspath( discoverScriptTemplatesInClasspath(
classpath, classpath,
this::class.java.classLoader, this::class.java.classLoader,
scriptResolverEnv, hostConfiguration,
messageReporter messageReporter
) )
} }
@@ -50,9 +50,9 @@ private const val MANIFEST_RESOURCE_NAME = "/META-INF/MANIFEST.MF"
fun discoverScriptTemplatesInClassLoader( fun discoverScriptTemplatesInClassLoader(
classLoader: ClassLoader, classLoader: ClassLoader,
scriptResolverEnv: Map<String, Any?>, hostConfiguration: ScriptingHostConfiguration,
messageReporter: MessageReporter messageReporter: MessageReporter
): Sequence<KotlinScriptDefinition> { ): Sequence<ScriptDefinition> {
val classpath = classLoader.getResources(MANIFEST_RESOURCE_NAME).asSequence().mapNotNull { val classpath = classLoader.getResources(MANIFEST_RESOURCE_NAME).asSequence().mapNotNull {
try { try {
File(it.toURI()).takeIf(File::exists) File(it.toURI()).takeIf(File::exists)
@@ -61,26 +61,26 @@ fun discoverScriptTemplatesInClassLoader(
} }
} }
val classpathWithLoader = SimpleClasspathWithClassLoader(classpath.toList(), classLoader) val classpathWithLoader = SimpleClasspathWithClassLoader(classpath.toList(), classLoader)
return scriptTemplatesDiscoverySequence(classpathWithLoader, scriptResolverEnv, messageReporter) return scriptTemplatesDiscoverySequence(classpathWithLoader, hostConfiguration, messageReporter)
} }
fun discoverScriptTemplatesInClasspath( fun discoverScriptTemplatesInClasspath(
classpath: List<File>, classpath: List<File>,
baseClassLoader: ClassLoader?, baseClassLoader: ClassLoader?,
scriptResolverEnv: Map<String, Any?>, hostConfiguration: ScriptingHostConfiguration,
messageReporter: MessageReporter messageReporter: MessageReporter
): Sequence<KotlinScriptDefinition> { ): Sequence<ScriptDefinition> {
// TODO: try to find a way to reduce classpath (and classloader) to minimal one needed to load script definition and its dependencies // TODO: try to find a way to reduce classpath (and classloader) to minimal one needed to load script definition and its dependencies
val classpathWithLoader = LazyClasspathWithClassLoader(baseClassLoader) { classpath } val classpathWithLoader = LazyClasspathWithClassLoader(baseClassLoader) { classpath }
return scriptTemplatesDiscoverySequence(classpathWithLoader, scriptResolverEnv, messageReporter) return scriptTemplatesDiscoverySequence(classpathWithLoader, hostConfiguration, messageReporter)
} }
private fun scriptTemplatesDiscoverySequence( private fun scriptTemplatesDiscoverySequence(
classpathWithLoader: ClasspathWithClassLoader, classpathWithLoader: ClasspathWithClassLoader,
scriptResolverEnv: Map<String, Any?>, hostConfiguration: ScriptingHostConfiguration,
messageReporter: MessageReporter messageReporter: MessageReporter
): Sequence<KotlinScriptDefinition> { ): Sequence<ScriptDefinition> {
return sequence { return sequence {
// for jar files the definition class is expected in the same jar as the discovery file // for jar files the definition class is expected in the same jar as the discovery file
// in case of directories, the class output may come separate from the resources, so some candidates should be deffered and processed later // in case of directories, the class output may come separate from the resources, so some candidates should be deffered and processed later
@@ -102,7 +102,7 @@ private fun scriptTemplatesDiscoverySequence(
definitionNames.partitionLoadJarDefinitions( definitionNames.partitionLoadJarDefinitions(
jar, jar,
classpathWithLoader, classpathWithLoader,
scriptResolverEnv, hostConfiguration,
messageReporter messageReporter
) )
if (notFoundClasses.isNotEmpty()) { if (notFoundClasses.isNotEmpty()) {
@@ -122,7 +122,7 @@ private fun scriptTemplatesDiscoverySequence(
val discoveryMarkers = File(dep, SCRIPT_DEFINITION_MARKERS_PATH).listFiles() val discoveryMarkers = File(dep, SCRIPT_DEFINITION_MARKERS_PATH).listFiles()
if (discoveryMarkers?.isEmpty() == false) { if (discoveryMarkers?.isEmpty() == false) {
val (foundDefinitionClasses, notFoundDefinitions) = discoveryMarkers.map { it.name } val (foundDefinitionClasses, notFoundDefinitions) = discoveryMarkers.map { it.name }
.partitionLoadDirDefinitions(dep, classpathWithLoader, scriptResolverEnv, messageReporter) .partitionLoadDirDefinitions(dep, classpathWithLoader, hostConfiguration, messageReporter)
foundDefinitionClasses.forEach { foundDefinitionClasses.forEach {
yield(it) yield(it)
} }
@@ -145,7 +145,7 @@ private fun scriptTemplatesDiscoverySequence(
if (remainingDefinitionCandidates.isEmpty()) break if (remainingDefinitionCandidates.isEmpty()) break
try { try {
val (foundDefinitionClasses, notFoundDefinitions) = val (foundDefinitionClasses, notFoundDefinitions) =
remainingDefinitionCandidates.partitionLoadDirDefinitions(dep, classpathWithLoader, scriptResolverEnv, messageReporter) remainingDefinitionCandidates.partitionLoadDirDefinitions(dep, classpathWithLoader, hostConfiguration, messageReporter)
foundDefinitionClasses.forEach { foundDefinitionClasses.forEach {
yield(it) yield(it)
} }
@@ -170,9 +170,9 @@ fun loadScriptTemplatesFromClasspath(
classpath: List<File>, classpath: List<File>,
dependenciesClasspath: List<File>, dependenciesClasspath: List<File>,
baseClassLoader: ClassLoader, baseClassLoader: ClassLoader,
scriptResolverEnv: Map<String, Any?>, hostConfiguration: ScriptingHostConfiguration,
messageReporter: MessageReporter messageReporter: MessageReporter
): Sequence<KotlinScriptDefinition> = ): Sequence<ScriptDefinition> =
if (scriptTemplates.isEmpty()) emptySequence() if (scriptTemplates.isEmpty()) emptySequence()
else sequence { else sequence {
// trying the direct classloading from baseClassloader first, since this is the most performant variant // trying the direct classloading from baseClassloader first, since this is the most performant variant
@@ -180,7 +180,7 @@ fun loadScriptTemplatesFromClasspath(
loadScriptDefinition( loadScriptDefinition(
baseClassLoader, baseClassLoader,
it, it,
scriptResolverEnv, hostConfiguration,
messageReporter messageReporter
) )
} }
@@ -199,11 +199,11 @@ fun loadScriptTemplatesFromClasspath(
val (loadedDefinitions, notFoundTemplates) = when { val (loadedDefinitions, notFoundTemplates) = when {
dep.isFile && dep.extension == "jar" -> { // checking for extension is the compiler current behaviour, so the same logic is implemented here dep.isFile && dep.extension == "jar" -> { // checking for extension is the compiler current behaviour, so the same logic is implemented here
JarFile(dep).use { jar -> JarFile(dep).use { jar ->
remainingTemplates.partitionLoadJarDefinitions(jar, classpathWithLoader, scriptResolverEnv, messageReporter) remainingTemplates.partitionLoadJarDefinitions(jar, classpathWithLoader, hostConfiguration, messageReporter)
} }
} }
dep.isDirectory -> { dep.isDirectory -> {
remainingTemplates.partitionLoadDirDefinitions(dep, classpathWithLoader, scriptResolverEnv, messageReporter) remainingTemplates.partitionLoadDirDefinitions(dep, classpathWithLoader, hostConfiguration, messageReporter)
} }
else -> { else -> {
// assuming that invalid classpath entries will be reported elsewhere anyway, so do not spam user with additional warnings here // assuming that invalid classpath entries will be reported elsewhere anyway, so do not spam user with additional warnings here
@@ -237,17 +237,17 @@ fun loadScriptTemplatesFromClasspath(
} }
private data class DefinitionsLoadPartitionResult( private data class DefinitionsLoadPartitionResult(
val loaded: List<KotlinScriptDefinition>, val loaded: List<ScriptDefinition>,
val notFound: List<String> val notFound: List<String>
) )
private inline fun List<String>.partitionLoadDefinitions( private inline fun List<String>.partitionLoadDefinitions(
classpathWithLoader: ClasspathWithClassLoader, classpathWithLoader: ClasspathWithClassLoader,
scriptResolverEnv: Map<String, Any?>, hostConfiguration: ScriptingHostConfiguration,
noinline messageReporter: MessageReporter, noinline messageReporter: MessageReporter,
getBytes: (String) -> ByteArray? getBytes: (String) -> ByteArray?
): DefinitionsLoadPartitionResult { ): DefinitionsLoadPartitionResult {
val loaded = ArrayList<KotlinScriptDefinition>() val loaded = ArrayList<ScriptDefinition>()
val notFound = ArrayList<String>() val notFound = ArrayList<String>()
for (definitionName in this) { for (definitionName in this) {
val classBytes = getBytes(definitionName) val classBytes = getBytes(definitionName)
@@ -256,7 +256,7 @@ private inline fun List<String>.partitionLoadDefinitions(
it, it,
definitionName, definitionName,
classpathWithLoader, classpathWithLoader,
scriptResolverEnv, hostConfiguration,
messageReporter messageReporter
) )
} }
@@ -273,18 +273,18 @@ private inline fun List<String>.partitionLoadDefinitions(
private fun List<String>.partitionLoadJarDefinitions( private fun List<String>.partitionLoadJarDefinitions(
jar: JarFile, jar: JarFile,
classpathWithLoader: ClasspathWithClassLoader, classpathWithLoader: ClasspathWithClassLoader,
scriptResolverEnv: Map<String, Any?>, hostConfiguration: ScriptingHostConfiguration,
messageReporter: MessageReporter messageReporter: MessageReporter
): DefinitionsLoadPartitionResult = partitionLoadDefinitions(classpathWithLoader, scriptResolverEnv, messageReporter) { definitionName -> ): DefinitionsLoadPartitionResult = partitionLoadDefinitions(classpathWithLoader, hostConfiguration, messageReporter) { definitionName ->
jar.getJarEntry("${definitionName.replace('.', '/')}.class")?.let { jar.getInputStream(it).readBytes() } jar.getJarEntry("${definitionName.replace('.', '/')}.class")?.let { jar.getInputStream(it).readBytes() }
} }
private fun List<String>.partitionLoadDirDefinitions( private fun List<String>.partitionLoadDirDefinitions(
dir: File, dir: File,
classpathWithLoader: ClasspathWithClassLoader, classpathWithLoader: ClasspathWithClassLoader,
scriptResolverEnv: Map<String, Any?>, hostConfiguration: ScriptingHostConfiguration,
messageReporter: MessageReporter messageReporter: MessageReporter
): DefinitionsLoadPartitionResult = partitionLoadDefinitions(classpathWithLoader, scriptResolverEnv, messageReporter) { definitionName -> ): DefinitionsLoadPartitionResult = partitionLoadDefinitions(classpathWithLoader, hostConfiguration, messageReporter) { definitionName ->
File(dir, "${definitionName.replace('.', '/')}.class").takeIf { it.exists() && it.isFile }?.readBytes() File(dir, "${definitionName.replace('.', '/')}.class").takeIf { it.exists() && it.isFile }?.readBytes()
} }
@@ -292,14 +292,15 @@ private fun loadScriptDefinition(
templateClassBytes: ByteArray, templateClassBytes: ByteArray,
templateClassName: String, templateClassName: String,
classpathWithLoader: ClasspathWithClassLoader, classpathWithLoader: ClasspathWithClassLoader,
scriptResolverEnv: Map<String, Any?>, hostConfiguration: ScriptingHostConfiguration,
messageReporter: MessageReporter messageReporter: MessageReporter
): KotlinScriptDefinition? { ): ScriptDefinition? {
val anns = loadAnnotationsFromClass(templateClassBytes) val anns = loadAnnotationsFromClass(templateClassBytes)
for (ann in anns) { for (ann in anns) {
var def: KotlinScriptDefinition? = null var def: ScriptDefinition? = null
if (ann.name == KotlinScript::class.simpleName) { if (ann.name == KotlinScript::class.simpleName) {
def = LazyScriptDefinitionFromDiscoveredClass( def = LazyScriptDefinitionFromDiscoveredClass(
hostConfiguration,
anns, anns,
templateClassName, templateClassName,
classpathWithLoader.classpath, classpathWithLoader.classpath,
@@ -307,7 +308,14 @@ private fun loadScriptDefinition(
) )
} else if (ann.name == ScriptTemplateDefinition::class.simpleName) { } else if (ann.name == ScriptTemplateDefinition::class.simpleName) {
val templateClass = classpathWithLoader.classLoader.loadClass(templateClassName).kotlin val templateClass = classpathWithLoader.classLoader.loadClass(templateClassName).kotlin
def = KotlinScriptDefinitionFromAnnotatedTemplate(templateClass, scriptResolverEnv, classpathWithLoader.classpath) def = ScriptDefinition.FromLegacy(
hostConfiguration,
KotlinScriptDefinitionFromAnnotatedTemplate(
templateClass,
hostConfiguration[ScriptingHostConfiguration.getEnvironment]?.invoke().orEmpty(),
classpathWithLoader.classpath
)
)
} }
if (def != null) { if (def != null) {
messageReporter( messageReporter(
@@ -327,29 +335,20 @@ private fun loadScriptDefinition(
private fun loadScriptDefinition( private fun loadScriptDefinition(
classLoader: ClassLoader, classLoader: ClassLoader,
template: String, template: String,
scriptResolverEnv: Map<String, Any?>, hostConfiguration: ScriptingHostConfiguration,
messageReporter: MessageReporter messageReporter: MessageReporter
): KotlinScriptDefinition? { ): ScriptDefinition? {
try { try {
val cls = classLoader.loadClass(template) val cls = classLoader.loadClass(template)
val def = val def =
if (cls.annotations.firstIsInstanceOrNull<KotlinScript>() != null) { if (cls.annotations.firstIsInstanceOrNull<KotlinScript>() != null) {
val environment = defaultJvmScriptingHostConfiguration ScriptDefinition.FromTemplate(hostConfiguration, cls.kotlin, ScriptDefinition::class)
KotlinScriptDefinitionAdapterFromNewAPI(
createCompilationConfigurationFromTemplate(
KotlinType(cls.kotlin),
environment,
KotlinScriptDefinition::class
),
environment
)
} else { } else {
KotlinScriptDefinitionFromAnnotatedTemplate(cls.kotlin, scriptResolverEnv) ScriptDefinition.FromLegacyTemplate(hostConfiguration, cls.kotlin)
} }
messageReporter( messageReporter(
CompilerMessageSeverity.INFO, CompilerMessageSeverity.INFO,
"Added script definition $template to configuration: name = ${def.name}, " + "Added script definition $template to configuration: name = ${def.name}"
"resolver = ${def.dependencyResolver.javaClass.name}"
) )
return def return def
} catch (ex: ClassNotFoundException) { } catch (ex: ClassNotFoundException) {
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.scripting.definitions package org.jetbrains.kotlin.scripting.definitions
import com.intellij.ide.highlighter.JavaClassFileType import com.intellij.ide.highlighter.JavaClassFileType
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.fileTypes.FileTypeRegistry import com.intellij.openapi.fileTypes.FileTypeRegistry
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
@@ -14,30 +15,31 @@ import com.intellij.psi.PsiManager
import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
fun PsiFile.scriptDefinition(): KotlinScriptDefinition? { fun PsiFile.findScriptDefinition(): ScriptDefinition? {
// Do not use psiFile.script, see comments in findScriptDefinition // Do not use psiFile.script, see comments in findScriptDefinition
if (this !is KtFile/* || this.script == null*/) return null if (this !is KtFile/* || this.script == null*/) return null
val file = virtualFile ?: originalFile.virtualFile ?: return null val file = virtualFile ?: originalFile.virtualFile ?: return null
if (file.isNonScript()) return null if (file.isNonScript()) return null
return scriptDefinitionByFileName(project, file.name) return findScriptDefinitionByFileName(project, file.name)
} }
fun VirtualFile.findScriptDefinition(project: Project): KotlinScriptDefinition? { fun VirtualFile.findScriptDefinition(project: Project): ScriptDefinition? {
if (!isValid || isNonScript()) return null if (!isValid || isNonScript()) return null
// Do not use psiFile.script here because this method can be called during indexes access // Do not use psiFile.script here because this method can be called during indexes access
// and accessing stubs may cause deadlock // and accessing stubs may cause deadlock
// TODO: measure performance effect and if necessary consider detecting indexing here or using separate logic for non-IDE operations to speed up filtering // TODO: measure performance effect and if necessary consider detecting indexing here or using separate logic for non-IDE operations to speed up filtering
if ((PsiManager.getInstance(project).findFile(this) as? KtFile)/*?.script*/ == null) return null
return scriptDefinitionByFileName(project, name) if (runReadAction { PsiManager.getInstance(project).findFile(this) as? KtFile }/*?.script*/ == null) return null
return findScriptDefinitionByFileName(project, name)
} }
fun scriptDefinitionByFileName(project: Project, fileName: String): KotlinScriptDefinition { fun findScriptDefinitionByFileName(project: Project, fileName: String): ScriptDefinition {
val scriptDefinitionProvider = ScriptDefinitionProvider.getInstance(project) ?: return null val scriptDefinitionProvider = ScriptDefinitionProvider.getInstance(project) ?: return null
?: throw IllegalStateException("Unable to get script definition: ScriptDefinitionProvider is not configured.") ?: throw IllegalStateException("Unable to get script definition: ScriptDefinitionProvider is not configured.")
return scriptDefinitionProvider.findScriptDefinition(fileName) ?: scriptDefinitionProvider.getDefaultScriptDefinition() return scriptDefinitionProvider.findDefinition(fileName) ?: scriptDefinitionProvider.getDefaultDefinition()
} }
private fun VirtualFile.isNonScript(): Boolean = private fun VirtualFile.isNonScript(): Boolean =
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
import java.io.File import java.io.File
import kotlin.script.experimental.api.valueOrNull
data class ScriptsCompilationDependencies( data class ScriptsCompilationDependencies(
val classpath: List<File>, val classpath: List<File>,
@@ -41,11 +42,11 @@ fun collectScriptsCompilationDependencies(
while (true) { while (true) {
val newRemainingSources = ArrayList<KtFile>() val newRemainingSources = ArrayList<KtFile>()
for (source in remainingSources) { for (source in remainingSources) {
val dependencies = importsProvider.getScriptDependencies(source) val refinedConfiguration = importsProvider.getScriptConfigurationResult(source)?.valueOrNull()
if (dependencies != null) { if (refinedConfiguration != null) {
collectedClassPath.addAll(dependencies.classpath) collectedClassPath.addAll(refinedConfiguration.dependenciesClassPath)
val sourceDependenciesRoots = dependencies.scripts.map { val sourceDependenciesRoots = refinedConfiguration.importedScripts.map {
KotlinSourceRoot(it.path, false) KotlinSourceRoot(it.path, false)
} }
val sourceDependencies = val sourceDependencies =
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.psi.KtImportInfo
import org.jetbrains.kotlin.resolve.ImportPath import org.jetbrains.kotlin.resolve.ImportPath
import org.jetbrains.kotlin.resolve.extensions.ExtraImportsProviderExtension import org.jetbrains.kotlin.resolve.extensions.ExtraImportsProviderExtension
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
import kotlin.script.experimental.api.valueOrNull
class ScriptExtraImportsProviderExtension : ExtraImportsProviderExtension { class ScriptExtraImportsProviderExtension : ExtraImportsProviderExtension {
@@ -28,8 +29,8 @@ class ScriptExtraImportsProviderExtension : ExtraImportsProviderExtension {
override fun getExtraImports(ktFile: KtFile): Collection<KtImportInfo> = override fun getExtraImports(ktFile: KtFile): Collection<KtImportInfo> =
ktFile.takeIf { it.isScript() }?.let { file -> ktFile.takeIf { it.isScript() }?.let { file ->
val scriptDependencies = ScriptDependenciesProvider.getInstance(file.project)?.getScriptDependencies(file.originalFile) val refinedConfiguration = ScriptDependenciesProvider.getInstance(file.project)?.getScriptConfigurationResult(file.originalFile)?.valueOrNull()
scriptDependencies?.imports?.map { refinedConfiguration?.defaultImports?.map {
ScriptExtraImportImpl( ScriptExtraImportImpl(
ImportPath.fromString(it) ImportPath.fromString(it)
) )
@@ -25,8 +25,10 @@ import org.jetbrains.kotlin.parsing.KotlinParserDefinition
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.repl.messages.ConsoleDiagnosticMessageHolder import org.jetbrains.kotlin.scripting.repl.messages.ConsoleDiagnosticMessageHolder
import kotlin.concurrent.write import kotlin.concurrent.write
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
const val KOTLIN_REPL_JVM_TARGET_PROPERTY = "kotlin.repl.jvm.target" const val KOTLIN_REPL_JVM_TARGET_PROPERTY = "kotlin.repl.jvm.target"
@@ -39,7 +41,7 @@ open class GenericReplChecker(
internal val environment = run { internal val environment = run {
compilerConfiguration.apply { compilerConfiguration.apply {
add(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition) add(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, ScriptDefinition.FromLegacy(defaultJvmScriptingHostConfiguration, scriptDefinition))
put<MessageCollector>(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector) put<MessageCollector>(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector)
put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true) put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true)
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
import java.io.File import java.io.File
import java.util.concurrent.locks.ReentrantReadWriteLock import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.write import kotlin.concurrent.write
import kotlin.script.experimental.api.valueOrNull
// WARNING: not thread safe, assuming external synchronization // WARNING: not thread safe, assuming external synchronization
@@ -64,7 +65,9 @@ open class GenericReplCompiler(
Pair(compilerState.lastLineState!!.psiFile, compilerState.lastLineState!!.errorHolder) Pair(compilerState.lastLineState!!.psiFile, compilerState.lastLineState!!.errorHolder)
} }
val newDependencies = ScriptDependenciesProvider.getInstance(checker.environment.project)?.getScriptDependencies(psiFile) val newDependencies =
ScriptDependenciesProvider.getInstance(checker.environment.project)?.getScriptConfigurationResult(psiFile)?.valueOrNull()
?.legacyDependencies
var classpathAddendum: List<File>? = null var classpathAddendum: List<File>? = null
if (compilerState.lastDependencies != newDependencies) { if (compilerState.lastDependencies != newDependencies) {
compilerState.lastDependencies = newDependencies compilerState.lastDependencies = newDependencies
@@ -33,10 +33,10 @@ import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.resolve.scopes.LexicalScopeImpl import org.jetbrains.kotlin.resolve.scopes.LexicalScopeImpl
import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind
import org.jetbrains.kotlin.resolve.source.toSourceElement import org.jetbrains.kotlin.resolve.source.toSourceElement
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
import org.jetbrains.kotlin.scripting.definitions.ScriptPriorities import org.jetbrains.kotlin.scripting.definitions.ScriptPriorities
import org.jetbrains.kotlin.scripting.definitions.scriptDefinitionByFileName import org.jetbrains.kotlin.scripting.definitions.findScriptDefinitionByFileName
import org.jetbrains.kotlin.types.TypeSubstitutor import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.typeUtil.isNothing import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.isUnit import org.jetbrains.kotlin.types.typeUtil.isUnit
@@ -44,6 +44,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import java.io.File import java.io.File
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.reflect.KType import kotlin.reflect.KType
import kotlin.script.experimental.api.valueOrNull
class LazyScriptDescriptor( class LazyScriptDescriptor(
@@ -104,8 +105,9 @@ class LazyScriptDescriptor(
override fun getPriority() = priority override fun getPriority() = priority
val scriptDefinition: () -> KotlinScriptDefinition = resolveSession.storageManager.createLazyValue { // rewrite without using legacyDefinition below
scriptDefinitionByFileName( val scriptDefinition: () -> ScriptDefinition = resolveSession.storageManager.createLazyValue {
findScriptDefinitionByFileName(
resolveSession.project, resolveSession.project,
scriptInfo.script.containingKtFile.name scriptInfo.script.containingKtFile.name
) )
@@ -128,7 +130,7 @@ class LazyScriptDescriptor(
override fun getUnsubstitutedPrimaryConstructor() = super.getUnsubstitutedPrimaryConstructor()!! override fun getUnsubstitutedPrimaryConstructor() = super.getUnsubstitutedPrimaryConstructor()!!
internal val baseClassDescriptor: () -> ClassDescriptor? = resolveSession.storageManager.createNullableLazyValue { internal val baseClassDescriptor: () -> ClassDescriptor? = resolveSession.storageManager.createNullableLazyValue {
val template = scriptDefinition().template val template = scriptDefinition().legacyDefinition.template
findTypeDescriptor( findTypeDescriptor(
template, template,
if (template.qualifiedName?.startsWith("kotlin.script.templates.standard") == true) Errors.MISSING_SCRIPT_STANDARD_TEMPLATE if (template.qualifiedName?.startsWith("kotlin.script.templates.standard") == true) Errors.MISSING_SCRIPT_STANDARD_TEMPLATE
@@ -167,7 +169,7 @@ class LazyScriptDescriptor(
val res = ArrayList<ClassDescriptor>() val res = ArrayList<ClassDescriptor>()
val importedScriptsFiles = ScriptDependenciesProvider.getInstance(scriptInfo.script.project) val importedScriptsFiles = ScriptDependenciesProvider.getInstance(scriptInfo.script.project)
?.getScriptDependencies(scriptInfo.script.containingKtFile)?.scripts ?.getScriptConfigurationResult(scriptInfo.script.containingKtFile)?.valueOrNull()?.importedScripts
if (importedScriptsFiles != null) { if (importedScriptsFiles != null) {
val findImportedScriptDescriptor = ImportedScriptDescriptorsFinder() val findImportedScriptDescriptor = ImportedScriptDescriptorsFinder()
importedScriptsFiles.mapNotNullTo(res) { importedScriptsFiles.mapNotNullTo(res) {
@@ -175,7 +177,7 @@ class LazyScriptDescriptor(
} }
} }
scriptDefinition().implicitReceivers.mapNotNullTo(res) { receiver -> scriptDefinition().legacyDefinition.implicitReceivers.mapNotNullTo(res) { receiver ->
findTypeDescriptor(receiver, Errors.MISSING_SCRIPT_RECEIVER_CLASS) findTypeDescriptor(receiver, Errors.MISSING_SCRIPT_RECEIVER_CLASS)
} }
@@ -222,7 +224,7 @@ class LazyScriptDescriptor(
private val scriptOuterScope: () -> LexicalScope = resolveSession.storageManager.createLazyValue { private val scriptOuterScope: () -> LexicalScope = resolveSession.storageManager.createLazyValue {
var outerScope = super.getOuterScope() var outerScope = super.getOuterScope()
val outerScopeReceivers = implicitReceivers.let { val outerScopeReceivers = implicitReceivers.let {
if (scriptDefinition().providedProperties.isEmpty()) { if (scriptDefinition().legacyDefinition.providedProperties.isEmpty()) {
it it
} else { } else {
it + ScriptProvidedPropertiesDescriptor(this) it + ScriptProvidedPropertiesDescriptor(this)
@@ -5,16 +5,10 @@
package org.jetbrains.kotlin.scripting.resolve package org.jetbrains.kotlin.scripting.resolve
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiManager
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import java.io.File import java.io.File
import kotlin.reflect.KClass
import kotlin.script.dependencies.ScriptContents import kotlin.script.dependencies.ScriptContents
import kotlin.script.experimental.dependencies.DependenciesResolver import kotlin.script.experimental.dependencies.DependenciesResolver
import kotlin.script.experimental.dependencies.DependenciesResolver.ResolveResult.Failure import kotlin.script.experimental.dependencies.DependenciesResolver.ResolveResult.Failure
@@ -22,43 +16,20 @@ import kotlin.script.experimental.dependencies.ScriptDependencies
import kotlin.script.experimental.dependencies.ScriptReport import kotlin.script.experimental.dependencies.ScriptReport
class ScriptContentLoader(private val project: Project) { class ScriptContentLoader(private val project: Project) {
fun getScriptContents(scriptDefinition: KotlinScriptDefinition, file: VirtualFile) fun getScriptContents(scriptDefinition: KotlinScriptDefinition, file: VirtualFile) =
= BasicScriptContents( makeScriptContents(
file, file,
getAnnotations = { loadAnnotations(scriptDefinition, file) }) scriptDefinition,
project,
private fun loadAnnotations(scriptDefinition: KotlinScriptDefinition, file: VirtualFile): List<Annotation> { scriptDefinition.template::class.java.classLoader
val classLoader = scriptDefinition.template.java.classLoader )
// TODO_R: report error on failure to load annotation class
return ApplicationManager.getApplication().runReadAction<List<Annotation>> {
getAnnotationEntries(file, project)
.mapNotNull { psiAnn ->
// TODO: consider advanced matching using semantic similar to actual resolving
scriptDefinition.acceptedAnnotations.find { ann ->
psiAnn.typeName.let { it == ann.simpleName || it == ann.qualifiedName }
}?.let {
@Suppress("UNCHECKED_CAST")
constructAnnotation(
psiAnn,
classLoader.loadClass(it.qualifiedName).kotlin as KClass<out Annotation>,
project
)
}
}
}
}
private fun getAnnotationEntries(file: VirtualFile, project: Project): Iterable<KtAnnotationEntry> {
val psiFile: PsiFile = PsiManager.getInstance(project).findFile(file)
?: throw IllegalArgumentException("Unable to load PSI from ${file.canonicalPath}")
return (psiFile as? KtFile)?.annotationEntries
?: throw IllegalArgumentException("Unable to extract kotlin annotations from ${file.name} (${file.fileType})")
}
class BasicScriptContents(virtualFile: VirtualFile, getAnnotations: () -> Iterable<Annotation>) : ScriptContents { class BasicScriptContents(virtualFile: VirtualFile, getAnnotations: () -> Iterable<Annotation>) : ScriptContents {
override val file: File = File(virtualFile.path) override val file: File = File(virtualFile.path)
override val annotations: Iterable<Annotation> by lazy(LazyThreadSafetyMode.PUBLICATION) { getAnnotations() } override val annotations: Iterable<Annotation> by lazy(LazyThreadSafetyMode.PUBLICATION) { getAnnotations() }
override val text: CharSequence? by lazy(LazyThreadSafetyMode.PUBLICATION) { virtualFile.inputStream.reader(charset = virtualFile.charset).readText() } override val text: CharSequence? by lazy(LazyThreadSafetyMode.PUBLICATION) {
virtualFile.inputStream.reader(charset = virtualFile.charset).readText()
}
} }
fun loadContentsAndResolveDependencies( fun loadContentsAndResolveDependencies(
@@ -80,11 +51,11 @@ class ScriptContentLoader(private val project: Project) {
} }
fun getEnvironment(scriptDef: KotlinScriptDefinition) = fun getEnvironment(scriptDef: KotlinScriptDefinition) =
(scriptDef as? KotlinScriptDefinitionFromAnnotatedTemplate)?.environment.orEmpty() (scriptDef as? KotlinScriptDefinitionFromAnnotatedTemplate)?.environment.orEmpty()
} }
fun ScriptDependencies.adjustByDefinition( fun ScriptDependencies.adjustByDefinition(
scriptDef: KotlinScriptDefinition scriptDef: KotlinScriptDefinition
): ScriptDependencies { ): ScriptDependencies {
val additionalClasspath = (scriptDef as? KotlinScriptDefinitionFromAnnotatedTemplate)?.templateClasspath ?: return this val additionalClasspath = (scriptDef as? KotlinScriptDefinitionFromAnnotatedTemplate)?.templateClasspath ?: return this
if (additionalClasspath.isEmpty()) return this if (additionalClasspath.isEmpty()) return this
@@ -42,7 +42,7 @@ class ScriptProvidedPropertiesDescriptor(script: LazyScriptDescriptor) :
override fun getUnsubstitutedMemberScope(): MemberScope = memberScope() override fun getUnsubstitutedMemberScope(): MemberScope = memberScope()
val properties: () -> List<ScriptProvidedPropertyDescriptor> = script.resolveSession.storageManager.createLazyValue { val properties: () -> List<ScriptProvidedPropertyDescriptor> = script.resolveSession.storageManager.createLazyValue {
script.scriptDefinition().providedProperties.mapNotNull { (name, type) -> script.scriptDefinition().legacyDefinition.providedProperties.mapNotNull { (name, type) ->
script.findTypeDescriptor(type, Errors.MISSING_SCRIPT_PROVIDED_PROPERTY_CLASS)?.let { script.findTypeDescriptor(type, Errors.MISSING_SCRIPT_PROVIDED_PROPERTY_CLASS)?.let {
name to it name to it
} }
@@ -0,0 +1,338 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.scripting.resolve
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.CharsetToolkit
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiManager
import com.intellij.testFramework.LightVirtualFile
import kotlinx.coroutines.runBlocking
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import java.io.File
import java.net.URL
import kotlin.reflect.KClass
import kotlin.script.experimental.api.*
import kotlin.script.experimental.dependencies.AsyncDependenciesResolver
import kotlin.script.experimental.dependencies.DependenciesResolver
import kotlin.script.experimental.dependencies.ScriptDependencies
import kotlin.script.experimental.host.*
import kotlin.script.experimental.jvm.JvmGetScriptingClass
import kotlin.script.experimental.jvm.compat.mapToDiagnostics
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.experimental.jvm.impl.refineWith
import kotlin.script.experimental.jvm.impl.toClassPathOrEmpty
import kotlin.script.experimental.jvm.impl.toDependencies
import kotlin.script.experimental.jvm.jdkHome
import kotlin.script.experimental.jvm.jvm
internal fun VirtualFile.loadAnnotations(
acceptedAnnotations: List<KClass<out Annotation>>,
project: Project,
classLoader: ClassLoader?
): List<Annotation> =
// TODO_R: report error on failure to load annotation class
ApplicationManager.getApplication().runReadAction<List<Annotation>> {
this.getAnnotationEntries(project).construct(classLoader, acceptedAnnotations, project)
}
internal fun VirtualFile.getAnnotationEntries(project: Project): Iterable<KtAnnotationEntry> {
val psiFile: PsiFile = PsiManager.getInstance(project).findFile(this)
?: throw IllegalArgumentException("Unable to load PSI from $canonicalPath")
return (psiFile as? KtFile)?.annotationEntries
?: throw IllegalArgumentException("Unable to extract kotlin annotations from $name ($fileType)")
}
/**
* The implementation of the SourceCode for a script located in a virtual file
*/
open class VirtualFileScriptSource(val virtualFile: VirtualFile, private val preloadedText: String? = null) :
FileBasedScriptSource()
{
override val file: File get() = File(virtualFile.path)
override val externalLocation: URL get() = URL(virtualFile.url)
override val text: String by lazy { preloadedText ?: virtualFile.inputStream.bufferedReader().readText() }
override val name: String? get() = virtualFile.name
override val locationId: String? get() = virtualFile.path
override fun equals(other: Any?): Boolean =
this === other || (other as? VirtualFileScriptSource)?.let { virtualFile == it.virtualFile } == true
override fun hashCode(): Int = virtualFile.hashCode()
}
/**
* The implementation of the SourceCode for a script located in a KtFile
*/
open class KtFileScriptSource(val ktFile: KtFile, preloadedText: String? = null) :
VirtualFileScriptSource(ktFile.virtualFile ?: ktFile.originalFile.virtualFile, preloadedText) {
override val text: String by lazy { preloadedText ?: ktFile.text }
override val name: String? get() = ktFile.name
override fun equals(other: Any?): Boolean =
this === other
|| (other as? KtFileScriptSource)?.let { ktFile == it.ktFile } == true
|| super.equals(other)
override fun hashCode(): Int = ktFile.hashCode()
}
class ScriptLightVirtualFile(name: String, private val _path: String?, text: String) :
LightVirtualFile(
name,
KotlinLanguage.INSTANCE,
StringUtil.convertLineSeparators(text)
) {
init {
charset = CharsetToolkit.UTF8_CHARSET
}
override fun getPath(): String = _path ?: super.getPath()
override fun getCanonicalPath(): String? = path
}
abstract class ScriptCompilationConfigurationWrapper(val script: SourceCode) {
abstract val configuration: ScriptCompilationConfiguration?
@Deprecated("Use configuration collection instead")
abstract val legacyDependencies: ScriptDependencies?
// optimizing most common ops for the IDE
// TODO: consider dropping after complete migration
abstract val dependenciesClassPath: List<File>
abstract val dependenciesSources: List<File>
abstract val javaHome: File?
abstract val defaultImports: List<String>
abstract val importedScripts: List<File>
override fun equals(other: Any?): Boolean = script == (other as? ScriptCompilationConfigurationWrapper)?.script
override fun hashCode(): Int = script.hashCode()
class FromCompilationConfiguration(
script: SourceCode,
override val configuration: ScriptCompilationConfiguration?
) : ScriptCompilationConfigurationWrapper(script) {
// TODO: check whether implemented optimization for frequent calls makes sense here
override val dependenciesClassPath: List<File> by lazy {
configuration?.get(ScriptCompilationConfiguration.dependencies).toClassPathOrEmpty()
}
// TODO: check whether implemented optimization for frequent calls makes sense here
override val dependenciesSources: List<File> by lazy {
configuration?.get(ScriptCompilationConfiguration.ide.dependenciesSources).toClassPathOrEmpty()
}
override val javaHome: File?
get() = configuration?.get(ScriptCompilationConfiguration.hostConfiguration)?.get(ScriptingHostConfiguration.jvm.jdkHome)
override val defaultImports: List<String>
get() = configuration?.get(ScriptCompilationConfiguration.defaultImports).orEmpty()
override val importedScripts: List<File>
get() = configuration?.get(ScriptCompilationConfiguration.importScripts)
?.mapNotNull { (it as? FileBasedScriptSource)?.file }.orEmpty()
override val legacyDependencies: ScriptDependencies?
get() = configuration?.toDependencies(dependenciesClassPath)
override fun equals(other: Any?): Boolean =
super.equals(other) && other is FromCompilationConfiguration && configuration == other.configuration
override fun hashCode(): Int = super.hashCode() + 23 * (configuration?.hashCode() ?: 1)
}
class FromLegacy(
script: SourceCode,
override val legacyDependencies: ScriptDependencies?
) : ScriptCompilationConfigurationWrapper(script) {
override val dependenciesClassPath: List<File>
get() = legacyDependencies?.classpath.orEmpty()
override val dependenciesSources: List<File>
get() = legacyDependencies?.sources.orEmpty()
override val javaHome: File?
get() = legacyDependencies?.javaHome
override val defaultImports: List<String>
get() = legacyDependencies?.imports.orEmpty()
override val importedScripts: List<File>
get() = legacyDependencies?.scripts.orEmpty()
override val configuration: ScriptCompilationConfiguration?
get() = legacyDependencies?.let {
TODO("drop or implement")
}
override fun equals(other: Any?): Boolean =
super.equals(other) && other is FromLegacy && legacyDependencies == other.legacyDependencies
override fun hashCode(): Int = super.hashCode() + 31 * (legacyDependencies?.hashCode() ?: 1)
}
}
typealias ScriptCompilationConfigurationResult = ResultWithDiagnostics<ScriptCompilationConfigurationWrapper>
fun refineScriptCompilationConfiguration(
script: SourceCode,
definition: ScriptDefinition,
project: Project
): ScriptCompilationConfigurationResult {
val ktFileSource = script.toKtFileSource(definition, project)
val legacyDefinition = definition.asLegacyOrNull<KotlinScriptDefinition>()
if (legacyDefinition == null) {
val compilationConfiguration = definition.compilationConfiguration
val collectedData =
getScriptCollectedData(ktFileSource.ktFile, compilationConfiguration, project, definition.contextClassLoader)
return compilationConfiguration.refineWith(
compilationConfiguration[ScriptCompilationConfiguration.refineConfigurationOnAnnotations]?.handler, collectedData, script
).onSuccess {
it.refineWith(
compilationConfiguration[ScriptCompilationConfiguration.refineConfigurationBeforeCompiling]?.handler, collectedData, script
)
}.onSuccess {
ScriptCompilationConfigurationWrapper.FromCompilationConfiguration(ktFileSource, it).asSuccess()
}
} else {
val file = script.getVirtualFile(definition)
val scriptContents =
makeScriptContents(file, legacyDefinition, project, definition.contextClassLoader)
val environment = (legacyDefinition as? KotlinScriptDefinitionFromAnnotatedTemplate)?.environment.orEmpty()
val result: DependenciesResolver.ResolveResult = try {
val resolver = legacyDefinition.dependencyResolver
if (resolver is AsyncDependenciesResolver) {
// since the only known async resolver is gradle, the following logic is taken from AsyncScriptDependenciesLoader
// runBlocking is using there to avoid loading dependencies asynchronously
// because it leads to starting more than one gradle daemon in case of resolving dependencies in build.gradle.kts
// It is more efficient to use one hot daemon consistently than multiple daemon in parallel
runBlocking {
resolver.resolveAsync(scriptContents, environment)
}
} else {
resolver.resolve(scriptContents, environment)
}
} catch (e: Throwable) {
return makeFailureResult(e.asDiagnostics(severity = ScriptDiagnostic.Severity.FATAL))
}
return if (result is DependenciesResolver.ResolveResult.Failure)
makeFailureResult(
result.reports.mapToDiagnostics()
)
else
ScriptCompilationConfigurationWrapper.FromLegacy(
ktFileSource,
result.dependencies?.adjustByDefinition(definition.legacyDefinition)
).asSuccess(result.reports.mapToDiagnostics())
}
}
internal fun makeScriptContents(
file: VirtualFile,
legacyDefinition: KotlinScriptDefinition,
project: Project,
classLoader: ClassLoader?
): ScriptContentLoader.BasicScriptContents =
ScriptContentLoader.BasicScriptContents(
file,
getAnnotations = {
file.loadAnnotations(legacyDefinition.acceptedAnnotations, project, classLoader)
})
fun SourceCode.getVirtualFile(definition: ScriptDefinition): VirtualFile {
if (this is VirtualFileScriptSource) return virtualFile
if (this is KtFileScriptSource) {
val vFile = virtualFile
if (vFile != null) return vFile
}
if (this is FileScriptSource) {
val vFile = LocalFileSystem.getInstance().findFileByIoFile(file)
if (vFile != null) return vFile
}
val scriptName = name ?: "script.${definition.fileExtension}"
val scriptPath = when (this) {
is FileScriptSource -> file.path
is ExternalSourceCode -> externalLocation.toString()
else -> null
}
val scriptText = definition.asLegacyOrNull<KotlinScriptDefinition>()?.let { text }
?: getMergedScriptText(this, definition.compilationConfiguration)
return ScriptLightVirtualFile(scriptName, scriptPath, scriptText)
}
fun SourceCode.getKtFile(definition: ScriptDefinition, project: Project): KtFile =
if (this is KtFileScriptSource) ktFile
else {
val file = getVirtualFile(definition)
ApplicationManager.getApplication().runReadAction<KtFile> {
val psiFile: PsiFile = PsiManager.getInstance(project).findFile(file)
?: throw IllegalArgumentException("Unable to load PSI from ${file.path}")
(psiFile as? KtFile)
?: throw IllegalArgumentException("Not a kotlin file ${file.path} (${file.fileType})")
}
}
fun SourceCode.toKtFileSource(definition: ScriptDefinition, project: Project): KtFileScriptSource =
if (this is KtFileScriptSource) this
else {
KtFileScriptSource(this.getKtFile(definition, project))
}
fun getScriptCollectedData(
scriptFile: KtFile,
compilationConfiguration: ScriptCompilationConfiguration,
project: Project,
contextClassLoader: ClassLoader?
): ScriptCollectedData {
val hostConfiguration =
compilationConfiguration[ScriptCompilationConfiguration.hostConfiguration] ?: defaultJvmScriptingHostConfiguration
val getScriptingClass = hostConfiguration[ScriptingHostConfiguration.getScriptingClass]
val jvmGetScriptingClass = (getScriptingClass as? JvmGetScriptingClass)
?: throw IllegalArgumentException("Expecting JvmGetScriptingClass in the hostConfiguration[getScriptingClass], got $getScriptingClass")
val acceptedAnnotations =
compilationConfiguration[ScriptCompilationConfiguration.refineConfigurationOnAnnotations]?.annotations?.mapNotNull {
jvmGetScriptingClass(it, contextClassLoader, hostConfiguration) as? KClass<Annotation> // TODO errors
}.orEmpty()
val annotations = scriptFile.annotationEntries.construct(contextClassLoader, acceptedAnnotations, project)
return ScriptCollectedData(
mapOf(
ScriptCollectedData.foundAnnotations to annotations
)
)
}
private fun Iterable<KtAnnotationEntry>.construct(
classLoader: ClassLoader?, acceptedAnnotations: List<KClass<out Annotation>>, project: Project
): List<Annotation> =
mapNotNull { psiAnn ->
// TODO: consider advanced matching using semantic similar to actual resolving
acceptedAnnotations.find { ann ->
psiAnn.typeName.let { it == ann.simpleName || it == ann.qualifiedName }
}?.let {
@Suppress("UNCHECKED_CAST")
(constructAnnotation(
psiAnn,
(classLoader ?: ClassLoader.getSystemClassLoader()).loadClass(it.qualifiedName).kotlin as KClass<out Annotation>,
project
))
}
}
@@ -43,7 +43,7 @@ class JvmCliScriptEvaluationExtension : ScriptEvaluationExtension {
val scriptFile = File(sourcePath) val scriptFile = File(sourcePath)
if (scriptFile.isDirectory || !scriptDefinitionProvider.isScript(scriptFile.name)) { if (scriptFile.isDirectory || !scriptDefinitionProvider.isScript(scriptFile.name)) {
val extensionHint = val extensionHint =
if (configuration.get(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS) == listOf(StandardScriptDefinition)) " (.kts)" if (configuration.get(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS)?.let { it.size == 1 && it.first().isDefault } == true) " (.kts)"
else "" else ""
messageCollector.report(ERROR, "Specify path to the script file$extensionHint as the first argument") messageCollector.report(ERROR, "Specify path to the script file$extensionHint as the first argument")
return COMPILATION_ERROR return COMPILATION_ERROR
@@ -18,13 +18,14 @@ import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.CliScriptDefinitionProvider import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.CliScriptDefinitionProvider
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.configuration.configureScriptDefinitions import org.jetbrains.kotlin.scripting.configuration.configureScriptDefinitions
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider import org.jetbrains.kotlin.scripting.definitions.*
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionsFromClasspathDiscoverySource
import org.jetbrains.kotlin.scripting.definitions.StandardScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.reporter
import java.io.File import java.io.File
import kotlin.script.experimental.host.ScriptingHostConfiguration
class ScriptingCompilerConfigurationExtension(val project: MockProject) : CompilerConfigurationExtension { class ScriptingCompilerConfigurationExtension(
val project: MockProject,
val baseHostConfiguration: ScriptingHostConfiguration
) : CompilerConfigurationExtension {
override fun updateConfiguration(configuration: CompilerConfiguration) { override fun updateConfiguration(configuration: CompilerConfiguration) {
@@ -39,7 +40,11 @@ class ScriptingCompilerConfigurationExtension(val project: MockProject) : Compil
projectRoot projectRoot
) )
} }
val scriptResolverEnv = configuration.getMap(ScriptingConfigurationKeys.LEGACY_SCRIPT_RESOLVER_ENVIRONMENT_OPTION) val hostConfiguration = ScriptingHostConfiguration(baseHostConfiguration) {
getEnvironment {
configuration.getMap(ScriptingConfigurationKeys.LEGACY_SCRIPT_RESOLVER_ENVIRONMENT_OPTION)
}
}
val explicitScriptDefinitions = configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS_CLASSES) val explicitScriptDefinitions = configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS_CLASSES)
@@ -49,16 +54,16 @@ class ScriptingCompilerConfigurationExtension(val project: MockProject) : Compil
configuration, configuration,
this::class.java.classLoader, this::class.java.classLoader,
messageCollector, messageCollector,
scriptResolverEnv hostConfiguration
) )
} }
// If not disabled explicitly, we should always support at least the standard script definition // If not disabled explicitly, we should always support at least the standard script definition
if (!configuration.getBoolean(JVMConfigurationKeys.DISABLE_STANDARD_SCRIPT_DEFINITION) && if (!configuration.getBoolean(JVMConfigurationKeys.DISABLE_STANDARD_SCRIPT_DEFINITION) &&
!configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS).contains(StandardScriptDefinition) configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS).none { it.isDefault }
) { ) {
configuration.add( configuration.add(
ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, ScriptingConfigurationKeys.SCRIPT_DEFINITIONS,
StandardScriptDefinition ScriptDefinition.getDefault(hostConfiguration)
) )
} }
@@ -66,37 +71,26 @@ class ScriptingCompilerConfigurationExtension(val project: MockProject) : Compil
ScriptingConfigurationKeys.SCRIPT_DEFINITIONS_SOURCES, ScriptingConfigurationKeys.SCRIPT_DEFINITIONS_SOURCES,
ScriptDefinitionsFromClasspathDiscoverySource( ScriptDefinitionsFromClasspathDiscoverySource(
configuration.jvmClasspathRoots, configuration.jvmClasspathRoots,
configuration.get(ScriptingConfigurationKeys.LEGACY_SCRIPT_RESOLVER_ENVIRONMENT_OPTION) ?: emptyMap(), hostConfiguration,
messageCollector.reporter messageCollector.reporter
) )
) )
}
// If not disabled explicitly, we should always support at least the standard script definition val scriptDefinitionProvider = ScriptDefinitionProvider.getInstance(project) as? CliScriptDefinitionProvider
if (!configuration.getBoolean(JVMConfigurationKeys.DISABLE_STANDARD_SCRIPT_DEFINITION) && if (scriptDefinitionProvider != null) {
StandardScriptDefinition !in configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS) scriptDefinitionProvider.setScriptDefinitionsSources(configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS_SOURCES))
) { scriptDefinitionProvider.setScriptDefinitions(configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS))
configuration.add(
ScriptingConfigurationKeys.SCRIPT_DEFINITIONS,
StandardScriptDefinition
)
}
val scriptDefinitionProvider = ScriptDefinitionProvider.getInstance(project) as? CliScriptDefinitionProvider // Register new file extensions
if (scriptDefinitionProvider != null) { val fileTypeRegistry = FileTypeRegistry.getInstance() as CoreFileTypeRegistry
scriptDefinitionProvider.setScriptDefinitionsSources(configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS_SOURCES))
scriptDefinitionProvider.setScriptDefinitions(configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS))
// Register new file extensions scriptDefinitionProvider.getKnownFilenameExtensions().filter {
val fileTypeRegistry = FileTypeRegistry.getInstance() as CoreFileTypeRegistry fileTypeRegistry.getFileTypeByExtension(it) != KotlinFileType.INSTANCE
}.forEach {
scriptDefinitionProvider.getKnownFilenameExtensions().filter { fileTypeRegistry.registerFileType(KotlinFileType.INSTANCE, it)
fileTypeRegistry.getFileTypeByExtension(it) != KotlinFileType.INSTANCE }
}.forEach {
fileTypeRegistry.registerFileType(KotlinFileType.INSTANCE, it)
} }
} }
} }
} }
@@ -5,33 +5,29 @@
package org.jetbrains.kotlin.scripting.compiler.plugin.definitions package org.jetbrains.kotlin.scripting.compiler.plugin.definitions
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.LazyScriptDefinitionProvider import org.jetbrains.kotlin.scripting.definitions.LazyScriptDefinitionProvider
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionsSource import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionsSource
import org.jetbrains.kotlin.scripting.definitions.StandardScriptDefinition
import kotlin.concurrent.write import kotlin.concurrent.write
open class CliScriptDefinitionProvider : LazyScriptDefinitionProvider() { open class CliScriptDefinitionProvider : LazyScriptDefinitionProvider() {
private val definitionsFromSources: MutableList<Sequence<KotlinScriptDefinition>> = arrayListOf() private val definitionsFromSources: MutableList<Sequence<ScriptDefinition>> = arrayListOf()
private val definitions: MutableList<KotlinScriptDefinition> = arrayListOf() private val definitions: MutableList<ScriptDefinition> = arrayListOf()
private var hasStandardDefinition = true private var defaultDefinition: ScriptDefinition? = null
override val currentDefinitions: Sequence<KotlinScriptDefinition> override val currentDefinitions: Sequence<ScriptDefinition>
get() { get() {
val base = definitions.asSequence() + definitionsFromSources.asSequence().flatMap { it } val base = definitions.asSequence() + definitionsFromSources.asSequence().flatMap { it }
return if (hasStandardDefinition) base + getDefaultScriptDefinition() else base return base + getDefaultDefinition()
} }
override fun getDefaultScriptDefinition(): KotlinScriptDefinition { fun setScriptDefinitions(newDefinitions: List<ScriptDefinition>) {
return StandardScriptDefinition
}
fun setScriptDefinitions(newDefinitions: List<KotlinScriptDefinition>) {
lock.write { lock.write {
definitions.clear() definitions.clear()
val (withoutStdDef, stdDef) = newDefinitions.partition { it != getDefaultScriptDefinition() } val (withoutStdDef, stdDef) = newDefinitions.partition { !it.isDefault }
definitions.addAll(withoutStdDef) definitions.addAll(withoutStdDef)
hasStandardDefinition = stdDef.isNotEmpty() // TODO: consider reporting an error when several default definitions are supplied
defaultDefinition = stdDef.firstOrNull()
} }
} }
@@ -43,4 +39,6 @@ open class CliScriptDefinitionProvider : LazyScriptDefinitionProvider() {
} }
} }
} }
override fun getDefaultDefinition(): ScriptDefinition = defaultDefinition ?: super.getDefaultDefinition()
} }
@@ -11,44 +11,45 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
import org.jetbrains.kotlin.scripting.resolve.ScriptContentLoader import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationResult
import org.jetbrains.kotlin.scripting.resolve.ScriptReportSink import org.jetbrains.kotlin.scripting.resolve.ScriptReportSink
import org.jetbrains.kotlin.scripting.resolve.adjustByDefinition import org.jetbrains.kotlin.scripting.resolve.VirtualFileScriptSource
import org.jetbrains.kotlin.scripting.resolve.refineScriptCompilationConfiguration
import java.io.File import java.io.File
import java.util.concurrent.locks.ReentrantReadWriteLock import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read import kotlin.concurrent.read
import kotlin.concurrent.write import kotlin.concurrent.write
import kotlin.script.experimental.dependencies.ScriptDependencies import kotlin.script.experimental.api.ResultWithDiagnostics
import kotlin.script.experimental.jvm.compat.mapToLegacyReports
class CliScriptDependenciesProvider(private val project: Project) : ScriptDependenciesProvider { class CliScriptDependenciesProvider(private val project: Project) : ScriptDependenciesProvider {
private val cacheLock = ReentrantReadWriteLock() private val cacheLock = ReentrantReadWriteLock()
private val cache = hashMapOf<String, ScriptDependencies?>() private val cache = hashMapOf<String, ScriptCompilationConfigurationResult?>()
private val scriptContentLoader = ScriptContentLoader(project)
override fun getScriptDependencies(file: VirtualFile): ScriptDependencies? = cacheLock.read { override fun getScriptConfigurationResult(file: VirtualFile): ScriptCompilationConfigurationResult? = cacheLock.read {
calculateExternalDependencies(file) calculateRefinedConfiguration(file)
} }
private fun calculateExternalDependencies(file: VirtualFile): ScriptDependencies? { private fun calculateRefinedConfiguration(file: VirtualFile): ScriptCompilationConfigurationResult? {
val path = file.path val path = file.path
val cached = cache[path] val cached = cache[path]
return if (cached != null) cached return if (cached != null) cached
else { else {
val scriptDef = file.findScriptDefinition(project) val scriptDef = file.findScriptDefinition(project)
if (scriptDef != null) { if (scriptDef != null) {
val result = scriptContentLoader.loadContentsAndResolveDependencies(scriptDef, file) val result = refineScriptCompilationConfiguration(VirtualFileScriptSource(file), scriptDef, project)
ServiceManager.getService(project, ScriptReportSink::class.java)?.attachReports(file, result.reports) ServiceManager.getService(project, ScriptReportSink::class.java)?.attachReports(file, result.reports.mapToLegacyReports())
val deps = result.dependencies?.adjustByDefinition(scriptDef) if (result is ResultWithDiagnostics.Success) {
log.info("[kts] new cached deps for $path: ${result.value.dependenciesClassPath.joinToString(File.pathSeparator)}")
if (deps != null) { } else {
log.info("[kts] new cached deps for $path: ${deps.classpath.joinToString(File.pathSeparator)}") log.info("[kts] new cached errors for $path:\n ${result.reports.joinToString("\n ") { it.message + if (it.exception == null) "" else ": ${it.exception}" }}")
} }
cacheLock.write { cacheLock.write {
cache.put(path, deps) cache.put(path, result)
} }
deps result
} else null } else null
} }
} }
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.CliScriptDefinitionProvider import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.CliScriptDefinitionProvider
import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.CliScriptDependenciesProvider import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.CliScriptDependenciesProvider
import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.CliScriptReportSink import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.CliScriptReportSink
import org.jetbrains.kotlin.scripting.configuration.makeHostConfiguration
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
import org.jetbrains.kotlin.scripting.extensions.* import org.jetbrains.kotlin.scripting.extensions.*
@@ -39,8 +40,9 @@ private fun <T> ProjectExtensionDescriptor<T>.registerExtensionIfRequired(projec
class ScriptingCompilerConfigurationComponentRegistrar : ComponentRegistrar { class ScriptingCompilerConfigurationComponentRegistrar : ComponentRegistrar {
override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) { override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY) val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
val hostConfiguration = makeHostConfiguration(project, configuration)
withClassloadingProblemsReporting(messageCollector) { withClassloadingProblemsReporting(messageCollector) {
CompilerConfigurationExtension.registerExtension(project, ScriptingCompilerConfigurationExtension(project)) CompilerConfigurationExtension.registerExtension(project, ScriptingCompilerConfigurationExtension(project, hostConfiguration))
CollectAdditionalSourcesExtension.registerExtension(project, ScriptingCollectAdditionalSourcesExtension(project)) CollectAdditionalSourcesExtension.registerExtension(project, ScriptingCollectAdditionalSourcesExtension(project))
ScriptEvaluationExtension.registerExtensionIfRequired(project, JvmCliScriptEvaluationExtension()) ScriptEvaluationExtension.registerExtensionIfRequired(project, JvmCliScriptEvaluationExtension())
ShellExtension.registerExtensionIfRequired(project, JvmCliReplShellExtension()) ShellExtension.registerExtensionIfRequired(project, JvmCliReplShellExtension())
@@ -7,11 +7,13 @@ package org.jetbrains.kotlin.scripting.compiler.plugin
import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.CliScriptDefinitionProvider import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.CliScriptDefinitionProvider
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionsSource import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionsSource
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
import org.junit.Assert import org.junit.Assert
import org.junit.Test import org.junit.Test
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.templates.standard.ScriptTemplateWithArgs import kotlin.script.templates.standard.ScriptTemplateWithArgs
class ScriptProviderTest : KtUsefulTestCase() { class ScriptProviderTest : KtUsefulTestCase() {
@@ -64,11 +66,16 @@ class ScriptProviderTest : KtUsefulTestCase() {
} }
} }
private open class FakeScriptDefinition(val suffix: String = ".kts") : KotlinScriptDefinition(ScriptTemplateWithArgs::class) { private open class FakeScriptDefinition(val suffix: String = ".kts") :
ScriptDefinition.FromLegacy(defaultJvmScriptingHostConfiguration, KotlinScriptDefinition(ScriptTemplateWithArgs::class))
{
val matchCounter = AtomicInteger() val matchCounter = AtomicInteger()
override fun isScript(fileName: String): Boolean = fileName.endsWith(suffix).also { override fun isScript(fileName: String): Boolean = fileName.endsWith(suffix).also {
if (it) matchCounter.incrementAndGet() if (it) matchCounter.incrementAndGet()
} }
override val isDefault: Boolean
get() = suffix == ".kts"
} }
private class TestScriptDefinitionSource(val counter: AtomicInteger, val defGens: Iterable<() -> FakeScriptDefinition>) : private class TestScriptDefinitionSource(val counter: AtomicInteger, val defGens: Iterable<() -> FakeScriptDefinition>) :
@@ -80,7 +87,7 @@ private class TestScriptDefinitionSource(val counter: AtomicInteger, val defGens
) )
} }) } })
override val definitions: Sequence<KotlinScriptDefinition> = sequence { override val definitions: Sequence<ScriptDefinition> = sequence {
for (gen in defGens) { for (gen in defGens) {
counter.incrementAndGet() counter.incrementAndGet()
yield(gen()) yield(gen())
@@ -88,6 +95,6 @@ private class TestScriptDefinitionSource(val counter: AtomicInteger, val defGens
} }
} }
private class TestCliScriptDefinitionProvider(private val standardDef: KotlinScriptDefinition) : CliScriptDefinitionProvider() { private class TestCliScriptDefinitionProvider(private val standardDef: ScriptDefinition) : CliScriptDefinitionProvider() {
override fun getDefaultScriptDefinition(): KotlinScriptDefinition = standardDef override fun getDefaultScriptDefinition(): KotlinScriptDefinition = standardDef.legacyDefinition
} }
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.utils.KotlinPaths
import org.jetbrains.kotlin.utils.PathUtil import org.jetbrains.kotlin.utils.PathUtil
import org.junit.Assert import org.junit.Assert
import java.io.File import java.io.File
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
class ScriptingCompilerPluginTest : TestCaseWithTmpdir() { class ScriptingCompilerPluginTest : TestCaseWithTmpdir() {
@@ -116,7 +117,7 @@ class ScriptingCompilerPluginTest : TestCaseWithTmpdir() {
loadScriptTemplatesFromClasspath( loadScriptTemplatesFromClasspath(
listOf("TestScriptWithReceivers", "TestScriptWithSimpleEnvVars"), listOf("TestScriptWithReceivers", "TestScriptWithSimpleEnvVars"),
listOf(defsOut), emptyList(), this::class.java.classLoader, emptyMap(), messageCollector.reporter listOf(defsOut), emptyList(), this::class.java.classLoader, defaultJvmScriptingHostConfiguration, messageCollector.reporter
).toList() ).toList()
for (def in defClasses) { for (def in defClasses) {
@@ -141,7 +142,7 @@ class ScriptingCompilerPluginTest : TestCaseWithTmpdir() {
discoverScriptTemplatesInClasspath( discoverScriptTemplatesInClasspath(
listOf(defsOut), listOf(defsOut),
this::class.java.classLoader, this::class.java.classLoader,
emptyMap(), defaultJvmScriptingHostConfiguration,
messageCollector.reporter messageCollector.reporter
) )
@@ -212,7 +213,7 @@ class ScriptingCompilerPluginTest : TestCaseWithTmpdir() {
discoverScriptTemplatesInClasspath( discoverScriptTemplatesInClasspath(
listOf(defsOut), listOf(defsOut),
this::class.java.classLoader, this::class.java.classLoader,
emptyMap(), defaultJvmScriptingHostConfiguration,
messageCollector.reporter messageCollector.reporter
).toList() ).toList()
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.daemon.common.*
import org.jetbrains.kotlin.integration.KotlinIntegrationTestBase.getKotlinPaths import org.jetbrains.kotlin.integration.KotlinIntegrationTestBase.getKotlinPaths
import org.jetbrains.kotlin.script.loadScriptingPlugin import org.jetbrains.kotlin.script.loadScriptingPlugin
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.StandardScriptDefinition import org.jetbrains.kotlin.scripting.definitions.StandardScriptDefinition
import org.jetbrains.kotlin.test.ConfigurationKind import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.KotlinTestUtils
@@ -46,6 +47,7 @@ import java.lang.management.ManagementFactory
import java.net.URLClassLoader import java.net.URLClassLoader
import java.nio.charset.Charset import java.nio.charset.Charset
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
class SourceSectionsTest : TestCaseWithTmpdir() { class SourceSectionsTest : TestCaseWithTmpdir() {
@@ -73,7 +75,7 @@ class SourceSectionsTest : TestCaseWithTmpdir() {
CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY,
PrintingMessageCollector(System.err, MessageRenderer.PLAIN_FULL_PATHS, false) PrintingMessageCollector(System.err, MessageRenderer.PLAIN_FULL_PATHS, false)
) )
configuration.add(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, StandardScriptDefinition) configuration.add(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, ScriptDefinition.getDefault(defaultJvmScriptingHostConfiguration))
if (withSourceSectionsPlugin) { if (withSourceSectionsPlugin) {
configuration.addAll(SourceSectionsConfigurationKeys.SECTIONS_OPTION, TEST_ALLOWED_SECTIONS) configuration.addAll(SourceSectionsConfigurationKeys.SECTIONS_OPTION, TEST_ALLOWED_SECTIONS)
configuration.add(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, SourceSectionsComponentRegistrar()) configuration.add(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, SourceSectionsComponentRegistrar())