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.CollectionsKt;
import kotlin.io.FilesKt;
import kotlin.script.experimental.dependencies.ScriptDependencies;
import kotlin.script.experimental.api.ErrorHandlingKt;
import kotlin.text.Charsets;
import org.jetbrains.annotations.NotNull;
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.psi.KtFile;
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.InTextDirectivesUtils;
import org.jetbrains.kotlin.test.KotlinTestUtils;
@@ -433,9 +434,9 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
if (externalImportsProvider != null) {
myEnvironment.getSourceFiles().forEach(
file -> {
ScriptDependencies dependencies = externalImportsProvider.getScriptDependencies(file);
if (dependencies != null) {
files.addAll(dependencies.getClasspath());
ScriptCompilationConfigurationWrapper refinedConfiguration = ErrorHandlingKt.valueOrNull(externalImportsProvider.getScriptConfigurationResult(file));
if (refinedConfiguration != null) {
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.implicitReceivers
import kotlin.script.experimental.api.providedProperties
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.experimental.jvm.util.scriptCompilationClasspathFromContextOrStdlib
abstract class AbstractCustomScriptCodegenTest : CodegenTestCase() {
@@ -37,7 +38,9 @@ abstract class AbstractCustomScriptCodegenTest : CodegenTestCase() {
override fun updateConfiguration(configuration: CompilerConfiguration) {
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())
@@ -21,6 +21,7 @@ import java.io.File
import java.lang.reflect.Constructor
import kotlin.reflect.KClass
import kotlin.script.experimental.annotations.KotlinScript
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.experimental.jvm.util.scriptCompilationClasspathFromContextOrStdlib
class CustomScriptCodegenTest : CodegenTestCase() {
@@ -65,7 +66,8 @@ class CustomScriptCodegenTest : CodegenTestCase() {
if (scriptDefinitions.isNotEmpty()) {
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.script.loadScriptingPlugin
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.scripts.TestKotlinScriptDependenciesResolver
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.org.objectweb.asm.Opcodes
import java.io.File
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.templates.ScriptTemplateDefinition
class ScriptGenTest : CodegenTestCase() {
companion object {
private val FIB_SCRIPT_DEFINITION =
KotlinScriptDefinitionFromAnnotatedTemplate(ScriptWithIntParam::class)
ScriptDefinition.FromLegacy(
defaultJvmScriptingHostConfiguration,
KotlinScriptDefinitionFromAnnotatedTemplate(ScriptWithIntParam::class)
)
private val NO_PARAM_SCRIPT_DEFINITION =
KotlinScriptDefinitionFromAnnotatedTemplate(Any::class)
ScriptDefinition.FromLegacy(
defaultJvmScriptingHostConfiguration,
KotlinScriptDefinitionFromAnnotatedTemplate(Any::class)
)
}
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.script.loadScriptingPlugin
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinitionAdapterFromNewAPI
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.TestJdkKind
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
import java.io.File
import kotlin.script.experimental.api.KotlinType
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.host.createCompilationConfigurationFromTemplate
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
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()) {
val configuration = KotlinTestUtils.newConfiguration(ConfigurationKind.NO_KOTLIN_REFLECT, TestJdkKind.MOCK_JDK).apply {
val hostConfiguration = ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration)
val scriptDefinition = KotlinScriptDefinitionAdapterFromNewAPI(
createCompilationConfigurationFromTemplate(
KotlinType(TestScriptWithRequire::class),
hostConfiguration, KotlinScriptDefinition::class
),
hostConfiguration
add(
ScriptingConfigurationKeys.SCRIPT_DEFINITIONS,
ScriptDefinition.FromTemplate(
ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration),
TestScriptWithRequire::class,
ScriptDefinition::class
)
)
add(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition)
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.script.loadScriptingPlugin
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinitionAdapterFromNewAPI
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.TestJdkKind
@@ -27,10 +26,10 @@ import java.io.File
import kotlin.reflect.KClass
import kotlin.script.experimental.annotations.KotlinScript
import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.FileBasedScriptSource
import kotlin.script.experimental.host.FileScriptSource
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.host.configurationDependencies
import kotlin.script.experimental.host.createCompilationConfigurationFromTemplate
import kotlin.script.experimental.jvm.*
private const val testDataPath = "compiler/testData/script/cliCompilation"
@@ -75,14 +74,10 @@ class ScriptCliCompilationTest : KtUsefulTestCase() {
val hostConfiguration = ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) {
configurationDependencies(JvmDependency(classpath))
}
val scriptDefinition = KotlinScriptDefinitionAdapterFromNewAPI(
createCompilationConfigurationFromTemplate(
KotlinType(scriptDef),
hostConfiguration, KotlinScriptDefinition::class
),
hostConfiguration
add(
ScriptingConfigurationKeys.SCRIPT_DEFINITIONS,
ScriptDefinition.FromTemplate(hostConfiguration, scriptDef, ScriptDefinition::class)
)
add(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition)
}
loadScriptingPlugin(this)
}
@@ -122,7 +117,7 @@ object TestScriptWithRequireConfiguration : ScriptCompilationConfiguration(
}
refineConfiguration {
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)
?.flatMap {
(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.scripting.configuration.ScriptingConfigurationKeys
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.KotlinScriptDefinitionFromAnnotatedTemplate
import org.jetbrains.kotlin.test.ConfigurationKind
@@ -50,6 +51,7 @@ import kotlin.reflect.KClass
import kotlin.script.dependencies.*
import kotlin.script.experimental.dependencies.*
import kotlin.script.experimental.dependencies.DependenciesResolver.ResolveResult
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.templates.AcceptedAnnotations
import kotlin.script.templates.ScriptTemplateDefinition
import kotlin.script.templates.standard.ScriptTemplateWithArgs
@@ -348,7 +350,13 @@ class ScriptTemplateTest : KtUsefulTestCase() {
*additionalClasspath)
configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector)
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.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.scripting.configuration.ScriptingConfigurationKeys
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.test.ConfigurationKind
import org.jetbrains.kotlin.test.KotlinTestUtils
@@ -37,6 +38,7 @@ import org.jetbrains.kotlin.utils.tryConstructClassFromStringArgs
import org.junit.Assert
import java.io.File
import java.net.URLClassLoader
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
class ScriptTest : KtUsefulTestCase() {
fun testStandardScriptWithParams() {
@@ -102,7 +104,13 @@ class ScriptTest : KtUsefulTestCase() {
val configuration = KotlinTestUtils.newConfiguration(ConfigurationKind.ALL, TestJdkKind.FULL_JDK)
configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector)
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)
if (saveClassesDir != null) {
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.stubindex.KotlinSourceFilterScope
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.platform.TargetPlatform
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices
data class ScriptModuleInfo(
val project: Project,
val scriptFile: VirtualFile,
val scriptDefinition: KotlinScriptDefinition
val scriptDefinition: ScriptDefinition
) : IdeaModuleInfo {
override val moduleOrigin: ModuleOrigin
get() = ModuleOrigin.OTHER
@@ -85,7 +85,7 @@ sealed class ScriptDependenciesInfo(val project: Project) : IdeaModuleInfo, Bina
class ForFile(
project: Project,
val scriptFile: VirtualFile,
val scriptDefinition: KotlinScriptDefinition
val scriptDefinition: ScriptDefinition
) : ScriptDependenciesInfo(project) {
override val sdk: Sdk?
get() {
@@ -27,7 +27,7 @@ import org.jetbrains.kotlin.idea.util.isInSourceContentWithoutInjected
import org.jetbrains.kotlin.idea.util.isKotlinBinary
import org.jetbrains.kotlin.psi.*
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.sure
import org.jetbrains.kotlin.utils.yieldIfNotNull
@@ -174,7 +174,7 @@ private fun <T> PsiElement.collectInfos(c: ModuleInfoCollector<T>): T {
return c.onResult(it)
}
containingKtFile.script?.let {
val definition = scriptDefinitionByFileName(project, containingKtFile.name)
val definition = findScriptDefinitionByFileName(project, containingKtFile.name)
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.languageVersionSettings
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.subplatformOfType
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 fun getLanguageSettingsForScripts(project: Project, scriptDefinition: KotlinScriptDefinition): ScriptLanguageSettings {
val args = scriptDefinition.additionalCompilerArguments
private fun getLanguageSettingsForScripts(project: Project, scriptDefinition: ScriptDefinition): ScriptLanguageSettings {
val args = scriptDefinition.compilerOptions
return if (args == null || args.none()) {
ScriptLanguageSettings(project.getLanguageVersionSettings(), TargetPlatformVersion.NoVersion)
} 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.core.script.IdeScriptReportSink
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.isRunningInCidrIde
import org.jetbrains.kotlin.psi.KtCodeFragment
@@ -70,7 +70,7 @@ object KotlinHighlightingUtil {
@Suppress("DEPRECATION")
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 (!ScriptDependenciesUpdater.areDependenciesCached(ktFile)) return false
if (!ScriptsCompilationConfigurationUpdater.areDependenciesCached(ktFile)) return false
if (ktFile.virtualFile.getUserData(IdeScriptReportSink.Reports)?.any { it.severity == ScriptReport.Severity.FATAL } == true) {
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.ScriptDefinitionsManager
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 kotlin.script.experimental.dependencies.ScriptDependencies
import kotlin.script.experimental.dependencies.ScriptReport
class ScriptExternalHighlightingPass(
@@ -62,9 +61,10 @@ class ScriptExternalHighlightingPass(
)
}
if (!ScriptDependenciesUpdater.areDependenciesCached(file)) {
val scriptDependencies = ScriptDependenciesManager.getInstance(file.project).getScriptDependencies(file.virtualFile)
if (scriptDependencies == ScriptDependencies.Empty) {
if (!ScriptsCompilationConfigurationUpdater.areDependenciesCached(file)) {
// initiate configuration refinement, if needed
ScriptDependenciesManager.getInstance(file.project).getRefinedCompilationConfiguration(file.virtualFile)
if (!ScriptsCompilationConfigurationUpdater.areDependenciesCached(file)) {
showNotification(
file,
"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.SimplePlatform;
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 static final TargetPlatformDetector INSTANCE = new TargetPlatformDetector();
@@ -62,7 +62,7 @@ public class TargetPlatformDetector {
}
if (file.isScript()) {
KotlinScriptDefinition scriptDefinition = DefinitionsKt.scriptDefinition(file);
ScriptDefinition scriptDefinition = DefinitionsKt.findScriptDefinition(file);
if (scriptDefinition != null) {
String platformNameFromScriptDefinition = scriptDefinition.getPlatform();
for (TargetPlatform compilerPlatform : IdePlatformKind.Companion.getAll_PLATFORMS()) {
@@ -71,7 +71,8 @@ object ProjectRootsUtil {
): Boolean {
val scriptDefinition = file.findScriptDefinition(project)
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)
|| scriptScope.contains(ScriptExpectedLocation.Project)
|| ScratchUtil.isScratch(file)
@@ -115,7 +116,8 @@ object ProjectRootsUtil {
if (ProjectRootManager.getInstance(project).fileIndex.isInContent(file) || ScratchUtil.isScratch(file)) {
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
@@ -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.core.script.settings.KotlinScriptingSettings
import org.jetbrains.kotlin.script.ScriptTemplatesProvider
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
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.scripting.definitions.*
import org.jetbrains.kotlin.utils.PathUtil
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.utils.addToStdlib.flattenTo
@@ -53,35 +49,33 @@ import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.write
import kotlin.script.dependencies.Environment
import kotlin.script.dependencies.ScriptContents
import kotlin.script.experimental.api.KotlinType
import kotlin.script.experimental.dependencies.DependenciesResolver
import kotlin.script.experimental.dependencies.ScriptDependencies
import kotlin.script.experimental.dependencies.asSuccess
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.host.configurationDependencies
import kotlin.script.experimental.host.createCompilationConfigurationFromTemplate
import kotlin.script.experimental.jvm.JvmDependency
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.experimental.jvm.util.scriptCompilationClasspathFromContextOrStdlib
import kotlin.script.templates.standard.ScriptTemplateWithArgs
class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinitionProvider() {
private var definitionsByContributor = mutableMapOf<ScriptDefinitionContributor, List<KotlinScriptDefinition>>()
private var definitions: List<KotlinScriptDefinition>? = null
private var definitionsBySource = mutableMapOf<ScriptDefinitionsSource, List<ScriptDefinition>>()
private var definitions: List<ScriptDefinition>? = null
private val failedContributorsHashes = HashSet<Int>()
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 (!isReady()) return null
val cached = scriptDefinitionsCacheLock.write { scriptDefinitionsCache.get(fileName) }
if (cached != null) return cached
val definition = super.findScriptDefinition(fileName) ?: return null
val definition = super.findDefinition(fileName) ?: return null
scriptDefinitionsCacheLock.write {
scriptDefinitionsCache.put(fileName, definition)
@@ -90,48 +84,43 @@ class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinit
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 (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()
}
fun getDefinitionsBy(contributor: ScriptDefinitionContributor): List<KotlinScriptDefinition> = lock.write {
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>
override val currentDefinitions
get() =
(definitions ?: kotlin.run {
reloadScriptDefinitions()
definitions!!
}).asSequence().filter { KotlinScriptingSettings.getInstance(project).isScriptDefinitionEnabled(it) }
private fun getContributors(): List<ScriptDefinitionContributor> {
private fun getSources(): List<ScriptDefinitionsSource> {
@Suppress("DEPRECATION")
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()
.map { it.asSource() }
return fromNewEp.dropLast(1) + fromDeprecatedEP + fromNewEp.last()
}
fun reloadScriptDefinitions() = lock.write {
for (contributor in getContributors()) {
val definitions = contributor.safeGetDefinitions()
definitionsByContributor[contributor] = definitions
for (source in getSources()) {
val definitions = source.safeGetDefinitions()
definitionsBySource[source] = definitions
}
definitions = definitionsByContributor.values.flattenTo(mutableListOf())
definitions = definitionsBySource.values.flattenTo(mutableListOf())
updateDefinitions()
}
@@ -140,7 +129,7 @@ class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinit
updateDefinitions()
}
fun getAllDefinitions(): List<KotlinScriptDefinition> {
fun getAllDefinitions(): List<ScriptDefinition> {
return definitions ?: kotlin.run {
reloadScriptDefinitions()
definitions!!
@@ -148,15 +137,16 @@ class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinit
}
fun isReady(): Boolean {
return definitionsByContributor.keys.all { contributor ->
contributor.isReady()
return definitionsBySource.keys.all { source ->
// 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)
?: error("StandardScriptDefinitionContributor should be registered is plugin.xml")
return standardScriptDefinitionContributor.getDefinitions().last()
return ScriptDefinition.FromLegacy(getScriptingHostConfiguration(), standardScriptDefinitionContributor.getDefinitions().last())
}
private fun updateDefinitions() {
@@ -187,7 +177,7 @@ class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinit
scriptDefinitionsCacheLock.write { scriptDefinitionsCache.clear() }
// TODO: clear by script type/definition
ServiceManager.getService(project, ScriptDependenciesCache::class.java).clear()
ServiceManager.getService(project, ScriptsCompilationConfigurationCache::class.java).clear()
ApplicationManager.getApplication().invokeLater {
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 {
return getDefinitions()
return definitions.toList()
} catch (t: Throwable) {
// reporting failed loading only once
LOG.error("[kts] cannot load script definitions using $this", t)
@@ -207,6 +197,15 @@ class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinit
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 {
fun getInstance(project: Project): 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")
// TODO: consider rewriting to return sequence
fun loadDefinitionsFromTemplates(
templateClassNames: List<String>,
templateClasspath: List<File>,
environment: Environment = emptyMap(),
baseHostConfiguration: ScriptingHostConfiguration,
// 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).
@@ -227,7 +227,7 @@ fun loadDefinitionsFromTemplates(
* i.e. gradle resolver may depend on some jars that 'built.gradle.kts' files should not depend on.
*/
additionalResolverClasspath: List<File> = emptyList()
): List<KotlinScriptDefinition> {
): List<ScriptDefinition> {
val classpath = templateClasspath + additionalResolverClasspath
LOG.info("[kts] loading script definitions $templateClassNames using cp: ${classpath.joinToString(File.pathSeparator)}")
val baseLoader = ScriptDefinitionContributor::class.java.classLoader
@@ -240,24 +240,13 @@ fun loadDefinitionsFromTemplates(
val template = loader.loadClass(templateClassName).kotlin
when {
template.annotations.firstIsInstanceOrNull<kotlin.script.templates.ScriptTemplateDefinition>() != null -> {
KotlinScriptDefinitionFromAnnotatedTemplate(
template,
environment,
templateClasspath
)
ScriptDefinition.FromLegacyTemplate(baseHostConfiguration, template, templateClasspath)
}
template.annotations.firstIsInstanceOrNull<kotlin.script.experimental.annotations.KotlinScript>() != null -> {
val hostConfiguration = ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) {
val hostConfiguration = ScriptingHostConfiguration(baseHostConfiguration) {
configurationDependencies(JvmDependency(classpath))
}
KotlinScriptDefinitionAdapterFromNewAPI(
createCompilationConfigurationFromTemplate(
KotlinType(
template
), hostConfiguration, KotlinScriptDefinition::class
),
hostConfiguration
)
ScriptDefinition.FromTemplate(hostConfiguration, template, ScriptDefinition::class)
}
else -> {
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 {
@Deprecated("migrating to new configuration refinement: drop usages")
val id: String
@Deprecated("migrating to new configuration refinement: use ScriptDefinitionsSource instead")
fun getDefinitions(): List<KotlinScriptDefinition>
@Deprecated("migrating to new configuration refinement: drop usages")
fun isReady() = true
companion object {
@@ -289,9 +284,29 @@ interface ScriptDefinitionContributor {
inline fun <reified T> find(project: Project) =
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 {
private val standardIdeScriptDefinition = StandardIdeScriptDefinition(project)
@@ -18,8 +18,9 @@ package org.jetbrains.kotlin.idea.core.script
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.idea.core.util.*
import java.io.DataInput
import java.io.DataOutput
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
import java.io.*
import kotlin.script.experimental.api.ScriptCompilationConfiguration
import kotlin.script.experimental.dependencies.ScriptDependencies
var VirtualFile.scriptDependencies: ScriptDependencies? by cachedFileAttribute(
@@ -43,4 +44,27 @@ var VirtualFile.scriptDependencies: ScriptDependencies? by cachedFileAttribute(
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.core.script.dependencies.SyncScriptDependenciesLoader
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 kotlin.script.experimental.dependencies.ScriptDependencies
import kotlin.script.experimental.api.valueOrNull
// NOTE: this service exists exclusively because ScriptDependencyManager
@@ -39,18 +41,21 @@ import kotlin.script.experimental.dependencies.ScriptDependencies
class IdeScriptDependenciesProvider(
private val scriptDependenciesManager: ScriptDependenciesManager
) : ScriptDependenciesProvider {
override fun getScriptDependencies(file: VirtualFile): ScriptDependencies? {
return scriptDependenciesManager.getScriptDependencies(file)
}
override fun getScriptConfigurationResult(file: VirtualFile): ScriptCompilationConfigurationResult? = scriptDependenciesManager.getRefinedCompilationConfiguration(file)
}
// TODO: rename and provide alias for compatibility - this is not only about dependencies anymore
class ScriptDependenciesManager internal constructor(
private val cacheUpdater: ScriptDependenciesUpdater,
private val cache: ScriptDependenciesCache
private val cacheUpdater: ScriptsCompilationConfigurationUpdater,
private val cache: ScriptsCompilationConfigurationCache
) {
fun getScriptClasspath(file: VirtualFile): List<VirtualFile> = toVfsRoots(cacheUpdater.getCurrentDependencies(file).classpath)
fun getScriptDependencies(file: VirtualFile): ScriptDependencies = cacheUpdater.getCurrentDependencies(file)
fun getScriptSdk(file: VirtualFile): Sdk? = getScriptSdk(getScriptDependencies(file))
fun getScriptClasspath(file: VirtualFile): List<VirtualFile> =
toVfsRoots(cacheUpdater.getCurrentCompilationConfiguration(file)?.valueOrNull()?.dependenciesClassPath.orEmpty())
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)
@@ -67,35 +72,29 @@ class ScriptDependenciesManager internal constructor(
fun getInstance(project: Project): ScriptDependenciesManager =
ServiceManager.getService(project, ScriptDependenciesManager::class.java)
fun getScriptSdk(dependencies: ScriptDependencies): Sdk? {
fun getScriptSdk(compilationConfiguration: ScriptCompilationConfigurationWrapper?): Sdk? {
// workaround for mismatched gradle wrapper and plugin version
try {
val javaHome = dependencies.javaHome
?.let { VfsUtil.findFileByIoFile(it, true) }
?: return null
return getAllProjectSdks().find { it.homeDirectory == javaHome }
val javaHome = try {
compilationConfiguration?.javaHome?.let { VfsUtil.findFileByIoFile(it, true) }
} 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() }
if (anyJavaSdk != null) {
return anyJavaSdk
}
log.warn(
"Default Script SDK is null: " +
"projectSdk = ${ProjectRootManager.getInstance(project).projectSdk}, " +
"all sdks = ${getAllProjectSdks().joinToString("\n")}"
)
return null
}
fun getScriptDefaultSdk(project: Project): Sdk? =
getProjectSdk(project)
?: getAllProjectSdks().find { it.canBeUsedForScript() }
?: run {
log.warn(
"Default Script SDK is null: " +
"projectSdk = ${ProjectRootManager.getInstance(project).projectSdk}, " +
"all sdks = ${getAllProjectSdks().joinToString("; ")}"
)
null
}
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 org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings
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) {
withSelectedEditor(project) { manager ->
@@ -28,14 +28,14 @@ fun VirtualFile.removeScriptDependenciesNotificationPanel(project: Project) {
}
fun VirtualFile.addScriptDependenciesNotificationPanel(
resolveResult: ResolveResult,
compilationConfigurationResult: ScriptCompilationConfigurationResult?,
project: Project,
onClick: (ResolveResult) -> Unit
onClick: (ScriptCompilationConfigurationResult?) -> Unit
) {
withSelectedEditor(project) { manager ->
val existingPanel = notificationPanel
if (existingPanel != null) {
if (existingPanel.resolveResult.dependencies == resolveResult.dependencies) {
if (existingPanel.compilationConfigurationResult == compilationConfigurationResult) {
return@withSelectedEditor
}
notificationPanel?.let {
@@ -43,7 +43,7 @@ fun VirtualFile.addScriptDependenciesNotificationPanel(
}
}
val panel = NewScriptDependenciesNotificationPanel(onClick, resolveResult, project)
val panel = NewScriptDependenciesNotificationPanel(onClick, compilationConfigurationResult, project)
notificationPanel = 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 class NewScriptDependenciesNotificationPanel(
onClick: (ResolveResult) -> Unit,
val resolveResult: ResolveResult,
onClick: (ScriptCompilationConfigurationResult?) -> Unit,
val compilationConfigurationResult: ScriptCompilationConfigurationResult?,
project: Project
) : EditorNotificationPanel() {
init {
setText("There are new script dependencies available.")
createComponentActionLabel("Apply dependencies") {
onClick(resolveResult)
setText("There is a new script context available.")
createComponentActionLabel("Apply context") {
onClick(compilationConfigurationResult)
}
createComponentActionLabel("Enable auto-reload") {
onClick(resolveResult)
onClick(compilationConfigurationResult)
KotlinScriptingSettings.getInstance(project).isAutoReloadEnabled = true
}
}
@@ -6,17 +6,25 @@
package org.jetbrains.kotlin.idea.core.script
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
get() = templatesProvider.id
override fun getDefinitions(): List<KotlinScriptDefinition> {
return loadDefinitionsFromTemplates(
templatesProvider.templateClassNames.toList(), templatesProvider.templateClasspath,
templatesProvider.environment.orEmpty(), templatesProvider.additionalResolverClasspath
)
}
override val definitions: Sequence<ScriptDefinition>
get() =
loadDefinitionsFromTemplates(
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.launch
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 kotlin.concurrent.write
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
import kotlin.reflect.KProperty0
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 {
const val MAX_SCRIPTS_CACHED = 50
@@ -45,10 +47,10 @@ class ScriptDependenciesCache(private val project: Project) {
private val cacheLock = ReentrantReadWriteLock()
private val scriptDependenciesCache = SLRUCacheWithLock<ScriptDependencies>()
private val scriptDependenciesCache = SLRUCacheWithLock<ScriptCompilationConfigurationResult>()
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 {
return scriptsModificationStampsCache.replace(file, file.modificationStamp) != file.modificationStamp
@@ -58,10 +60,10 @@ class ScriptDependenciesCache(private val project: Project) {
fun scriptDependenciesClassFilesScope(file: VirtualFile): GlobalSearchScope {
return scriptsDependenciesClasspathScopeCache.getOrPut(file) {
val dependencies = scriptDependenciesCache.get(file) ?: return@getOrPut GlobalSearchScope.EMPTY_SCOPE
val roots = dependencies.classpath
val compilationConfiguration = scriptDependenciesCache.get(file)?.valueOrNull() ?: return@getOrPut GlobalSearchScope.EMPTY_SCOPE
val roots = compilationConfiguration.dependenciesClassPath
val sdk = ScriptDependenciesManager.getScriptSdk(dependencies)
val sdk = ScriptDependenciesManager.getScriptSdk(compilationConfiguration)
@Suppress("FoldInitializerAndIfToElvis")
if (sdk == null) {
@@ -86,7 +88,8 @@ class ScriptDependenciesCache(private val project: Project) {
.filter { it != ScriptDependenciesManager.getProjectSdk(project) }
.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)
}
@@ -96,7 +99,8 @@ class ScriptDependenciesCache(private val project: Project) {
.filter { it != ScriptDependenciesManager.getProjectSdk(project) }
.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)
}
@@ -140,11 +144,10 @@ class ScriptDependenciesCache(private val project: Project) {
}
}
fun hasNotCachedRoots(scriptDependencies: ScriptDependencies): Boolean {
return !allSdks.contains(ScriptDependenciesManager.getScriptSdk(scriptDependencies)) ||
!allDependenciesClassFiles.containsAll(ScriptDependenciesManager.toVfsRoots(scriptDependencies.classpath)) ||
!allDependenciesSources.containsAll(ScriptDependenciesManager.toVfsRoots(scriptDependencies.sources))
fun hasNotCachedRoots(compilationConfiguration: ScriptCompilationConfigurationWrapper): Boolean {
return !allSdks.contains(ScriptDependenciesManager.getScriptSdk(compilationConfiguration)) ||
!allDependenciesClassFiles.containsAll(ScriptDependenciesManager.toVfsRoots(compilationConfiguration.dependenciesClassPath)) ||
!allDependenciesSources.containsAll(ScriptDependenciesManager.toVfsRoots(compilationConfiguration.dependenciesSources))
}
fun clear() {
@@ -155,7 +158,7 @@ class ScriptDependenciesCache(private val project: Project) {
onChange(keys)
}
fun save(virtualFile: VirtualFile, new: ScriptDependencies): Boolean {
fun save(virtualFile: VirtualFile, new: ScriptCompilationConfigurationResult): Boolean {
val old = scriptDependenciesCache.replace(virtualFile, new)
val changed = new != old
if (changed) {
@@ -207,8 +210,8 @@ private class SLRUCacheWithLock<T> {
private val lock = ReentrantReadWriteLock()
val cache = SLRUMap<VirtualFile, T>(
ScriptDependenciesCache.MAX_SCRIPTS_CACHED,
ScriptDependenciesCache.MAX_SCRIPTS_CACHED
ScriptsCompilationConfigurationCache.MAX_SCRIPTS_CACHED,
ScriptsCompilationConfigurationCache.MAX_SCRIPTS_CACHED
)
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.NotNullableUserDataProperty
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.resolve.LegacyResolverWrapper
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationResult
import kotlin.script.experimental.dependencies.AsyncDependenciesResolver
import kotlin.script.experimental.dependencies.ScriptDependencies
class ScriptDependenciesUpdater(
class ScriptsCompilationConfigurationUpdater(
private val project: Project,
private val cache: ScriptDependenciesCache
private val cache: ScriptsCompilationConfigurationCache
) {
private val scriptsQueue = Alarm(Alarm.ThreadToUse.SWING_THREAD, project)
private val scriptChangesListenerDelay = 1400
@@ -64,13 +65,13 @@ class ScriptDependenciesUpdater(
listenForChangesInScripts()
}
fun getCurrentDependencies(file: VirtualFile): ScriptDependencies {
fun getCurrentCompilationConfiguration(file: VirtualFile): ScriptCompilationConfigurationResult? {
cache[file]?.let { return it }
updateDependencies(file)
makeRootsChangeIfNeeded()
return cache[file] ?: ScriptDependencies.Empty
return cache[file]
}
fun updateDependenciesIfNeeded(files: List<VirtualFile>): Boolean {
@@ -174,15 +175,15 @@ class ScriptDependenciesUpdater(
return cache[file] != null || file.scriptDependencies != null
}
fun isAsyncDependencyResolver(scriptDef: KotlinScriptDefinition): Boolean {
val dependencyResolver = scriptDef.dependencyResolver
return dependencyResolver is AsyncDependenciesResolver || dependencyResolver is LegacyResolverWrapper
}
fun isAsyncDependencyResolver(scriptDef: ScriptDefinition): Boolean =
scriptDef.asLegacyOrNull<KotlinScriptDefinition>()?.dependencyResolver?.let {
it is AsyncDependenciesResolver || it is LegacyResolverWrapper
} ?: false
companion object {
@JvmStatic
fun getInstance(project: Project): ScriptDependenciesUpdater =
ServiceManager.getService(project, ScriptDependenciesUpdater::class.java)
fun getInstance(project: Project): ScriptsCompilationConfigurationUpdater =
ServiceManager.getService(project, ScriptsCompilationConfigurationUpdater::class.java)
fun areDependenciesCached(file: KtFile): Boolean {
return getInstance(file.project).areDependenciesCached(file.virtualFile)
@@ -5,7 +5,6 @@
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.ProgressIndicator
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.vfs.VirtualFile
import kotlinx.coroutines.Runnable
import kotlinx.coroutines.runBlocking
import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesUpdater
import org.jetbrains.kotlin.idea.core.script.ScriptsCompilationConfigurationUpdater
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.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.locks.ReentrantReadWriteLock
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) {
private val lock = ReentrantReadWriteLock()
@@ -33,7 +30,7 @@ class AsyncScriptDependenciesLoader internal constructor(project: Project) : Scr
override fun isApplicable(file: VirtualFile): Boolean {
val scriptDefinition = file.findScriptDefinition(project) ?: return false
return ScriptDependenciesUpdater.getInstance(project).isAsyncDependencyResolver(scriptDefinition)
return ScriptsCompilationConfigurationUpdater.getInstance(project).isAsyncDependencyResolver(scriptDefinition)
}
override fun loadDependencies(file: VirtualFile) {
@@ -73,35 +70,15 @@ class AsyncScriptDependenciesLoader internal constructor(project: Project) : Scr
}
private fun runDependenciesUpdate(file: VirtualFile) {
val scriptDef = runReadAction { file.findScriptDefinition(project) } ?: return
val scriptDef = file.findScriptDefinition(project) ?: return
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
// 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)
}
}
val result = refineScriptCompilationConfiguration(VirtualFileScriptSource(file), scriptDef, project)
debug(file) { "finish async dependencies loading" }
processResult(result, file, scriptDef)
}
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)
}
processRefinedConfiguration(result, file)
}
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.vfs.VirtualFile
import org.jetbrains.kotlin.idea.core.script.scriptCompilationConfiguration
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) {
override fun isApplicable(file: VirtualFile): Boolean {
@@ -16,9 +21,17 @@ class FromFileAttributeScriptDependenciesLoader(project: Project) : ScriptDepend
}
override fun loadDependencies(file: VirtualFile) {
val deserializedDependencies = file.scriptDependencies ?: return
debug(file) { "dependencies from fileAttributes = $deserializedDependencies" }
saveToCache(file, deserializedDependencies)
file.scriptCompilationConfiguration?.let {
ScriptCompilationConfigurationWrapper.FromCompilationConfiguration(VirtualFileScriptSource(file), it).apply {
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
@@ -22,6 +22,7 @@ import com.intellij.psi.ResolveScopeProvider
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesManager
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.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
@@ -36,11 +37,13 @@ class KotlinScriptResolveScopeProvider : ResolveScopeProvider() {
// TODO: this should get this particular scripts dependencies
return when {
scriptDefinition == null -> null
// This is a workaround for completion in scripts and REPL to provide module dependencies
scriptDefinition.template == Any::class -> null
scriptDefinition is StandardIdeScriptDefinition -> null
scriptDefinition is KotlinScriptDefinitionFromAnnotatedTemplate -> // TODO: should include the file itself
// This is a workaround for completion in scripts and REPL to provide module dependencies
scriptDefinition.baseClassType.fromClass == Any::class -> null
scriptDefinition.asLegacyOrNull<StandardIdeScriptDefinition>() != null -> null
scriptDefinition is ScriptDefinition.FromConfigurations || scriptDefinition.asLegacyOrNull<KotlinScriptDefinitionFromAnnotatedTemplate>() != null -> {
// TODO: should include the file itself
ScriptDependenciesManager.getInstance(project).getAllScriptsDependenciesClassFilesScope()
}
else -> null
}
}
@@ -18,9 +18,8 @@ class OutsiderFileDependenciesLoader(project: Project) : ScriptDependenciesLoade
override fun loadDependencies(file: VirtualFile) {
val fileOrigin = OutsidersPsiFileSupportUtils.getOutsiderFileOrigin(project, file) ?: return
val originDependencies = ScriptDependenciesManager.getInstance(project).getScriptDependencies(fileOrigin)
debug(file) { "dependencies for outsider file = $originDependencies" }
saveToCache(file, originDependencies)
val compilationConfiguration = ScriptDependenciesManager.getInstance(project).getRefinedCompilationConfiguration(fileOrigin) ?: return
saveToCache(file, compilationConfiguration)
}
override fun shouldShowNotification(): Boolean = false
@@ -15,13 +15,14 @@ import com.intellij.openapi.util.EmptyRunnable
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.idea.core.script.*
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.resolve.ScriptContentLoader
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationResult
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
import org.jetbrains.kotlin.scripting.resolve.ScriptReportSink
import org.jetbrains.kotlin.scripting.resolve.adjustByDefinition
import kotlin.script.experimental.dependencies.DependenciesResolver
import kotlin.script.experimental.dependencies.ScriptDependencies
import kotlin.script.experimental.api.ResultWithDiagnostics
import kotlin.script.experimental.api.valueOrNull
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 fun isApplicable(file: VirtualFile): Boolean
@@ -31,40 +32,40 @@ abstract class ScriptDependenciesLoader(protected val project: Project) {
protected var shouldNotifyRootsChanged = false
protected val contentLoader = ScriptContentLoader(project)
protected val cache: ScriptDependenciesCache = ServiceManager.getService(project, ScriptDependenciesCache::class.java)
protected val cache: ScriptsCompilationConfigurationCache = ServiceManager.getService(project, ScriptsCompilationConfigurationCache::class.java)
private val reporter: ScriptReportSink = ServiceManager.getService(project, ScriptReportSink::class.java)
protected fun processResult(result: DependenciesResolver.ResolveResult, file: VirtualFile, scriptDef: KotlinScriptDefinition) {
debug(file) { "dependencies from ${this.javaClass} received = $result" }
protected fun processRefinedConfiguration(result: ScriptCompilationConfigurationResult, file: VirtualFile) {
debug(file) { "refined script compilation configuration from ${this.javaClass} received = $result" }
if (cache[file] == null) {
saveDependencies(result, file, scriptDef)
attachReportsIfChanged(result, file, scriptDef)
val oldResult = cache[file]
if (oldResult == null) {
save(result, file)
attachReportsIfChanged(result, file)
return
}
val newDependencies = result.dependencies?.adjustByDefinition(scriptDef)
if (cache[file] != newDependencies) {
if (oldResult != result) {
if (shouldShowNotification() && !ApplicationManager.getApplication().isUnitTestMode) {
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) {
saveDependencies(it, file, scriptDef)
attachReportsIfChanged(it, file, scriptDef)
save(it, file)
attachReportsIfChanged(result, file)
submitMakeRootsChange()
}
} else {
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)
attachReportsIfChanged(result, file, scriptDef)
save(result, file)
attachReportsIfChanged(result, file)
}
} else {
attachReportsIfChanged(result, file, scriptDef)
attachReportsIfChanged(result, file)
if (shouldShowNotification()) {
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() }) {
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()) {
file.removeScriptDependenciesNotificationPanel(project)
}
val dependencies = result.dependencies?.adjustByDefinition(scriptDef) ?: return
saveToCache(file, dependencies)
if (compilationConfigurationResult != null) {
saveToCache(file, compilationConfigurationResult)
}
}
protected fun saveToCache(file: VirtualFile, dependencies: ScriptDependencies) {
val rootsChanged = cache.hasNotCachedRoots(dependencies)
if (cache.save(file, dependencies)) {
protected fun saveToCache(
file: VirtualFile, compilationConfigurationResult: ScriptCompilationConfigurationResult, skipSaveToAttributes: Boolean = false
) {
val rootsChanged = compilationConfigurationResult.valueOrNull()?.let { cache.hasNotCachedRoots(it) } ?: false
if (cache.save(file, compilationConfigurationResult)
&& !skipSaveToAttributes
&& compilationConfigurationResult is ResultWithDiagnostics.Success
) {
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) {
@@ -101,6 +110,7 @@ abstract class ScriptDependenciesLoader(protected val project: Project) {
}
}
open fun notifyRootsChanged(): Boolean = submitMakeRootsChange()
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.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.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 {
val scriptDefinition = file.findScriptDefinition(project) ?: return false
return !ScriptDependenciesUpdater.getInstance(project).isAsyncDependencyResolver(scriptDefinition)
return !ScriptsCompilationConfigurationUpdater.getInstance(project).isAsyncDependencyResolver(scriptDefinition)
}
override fun loadDependencies(file: VirtualFile) {
val scriptDef = file.findScriptDefinition(project) ?: return
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" }
processResult(result, file, scriptDef)
processRefinedConfiguration(result, file)
}
override fun shouldShowNotification(): Boolean = false
@@ -14,7 +14,7 @@ import com.intellij.util.addOptionTag
import com.intellij.util.attribute
import com.intellij.util.getAttributeBooleanValue
import org.jdom.Element
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
@State(
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) ?:
KotlinScriptDefinitionValue(order)
}
fun setEnabled(scriptDefinition: KotlinScriptDefinition, isEnabled: Boolean) {
fun setEnabled(scriptDefinition: ScriptDefinition, isEnabled: Boolean) {
scriptDefinitions[scriptDefinition.toKey()] = scriptDefinitions[scriptDefinition.toKey()]?.copy(isEnabled = isEnabled) ?:
KotlinScriptDefinitionValue(scriptDefinitions.size, isEnabled)
}
fun getScriptDefinitionOrder(scriptDefinition: KotlinScriptDefinition): Int {
fun getScriptDefinitionOrder(scriptDefinition: ScriptDefinition): Int {
return scriptDefinitions[scriptDefinition.toKey()]?.order ?: Integer.MAX_VALUE
}
fun isScriptDefinitionEnabled(scriptDefinition: KotlinScriptDefinition): Boolean {
fun isScriptDefinitionEnabled(scriptDefinition: ScriptDefinition): Boolean {
return scriptDefinitions[scriptDefinition.toKey()]?.isEnabled ?: true
}
@@ -99,8 +99,8 @@ class KotlinScriptingSettings : PersistentStateComponent<Element> {
getAttributeValue(KotlinScriptDefinitionKey::className.name)
)
private fun KotlinScriptDefinition.toKey() =
KotlinScriptDefinitionKey(this.name, this::class.qualifiedName ?: "unknown")
private fun ScriptDefinition.toKey() =
KotlinScriptDefinitionKey(this.name, this.definitionId)
private fun Element.addScriptDefinitionContentElement(definition: KotlinScriptDefinitionKey, settings: KotlinScriptDefinitionValue) {
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.lexer.KotlinLexer
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.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.getEnvironment
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
import org.jetbrains.plugins.gradle.config.GradleSettingsListenerAdapter
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.util.GradleConstants
import java.io.File
import java.lang.IllegalStateException
import java.util.*
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.collections.LinkedHashSet
import kotlin.reflect.KClass
import kotlin.script.dependencies.Environment
import kotlin.script.dependencies.ScriptContents
@@ -54,7 +53,7 @@ import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.experimental.location.ScriptExpectedLocation
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"
private val failedToLoad = AtomicBoolean(false)
@@ -86,55 +85,52 @@ class GradleScriptDefinitionsContributor(private val project: Project) : ScriptD
initializeScriptModificationListener(project)
}
override fun getDefinitions(): List<KotlinScriptDefinition> {
return loadDefinitions()
}
// NOTE: control flow here depends on suppressing exceptions from loadGradleTemplates calls
// TODO: possibly combine exceptions from every loadGradleTemplates call, be mindful of KT-19276
private fun loadDefinitions(): List<KotlinScriptDefinition> {
val kotlinDslDependencySelector = Regex("^gradle-(?:kotlin-dsl|core).*\\.jar\$")
val kotlinDslAdditionalResolverCp = ::kotlinStdlibAndCompiler
override val definitions: Sequence<ScriptDefinition>
get() {
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(
templateClass = "org.gradle.kotlin.dsl.KotlinInitScript",
dependencySelector = kotlinDslDependencySelector,
additionalResolverClasspath = kotlinDslAdditionalResolverCp
loadGradleTemplates(
templateClass = "org.gradle.kotlin.dsl.KotlinInitScript",
dependencySelector = kotlinDslDependencySelector,
additionalResolverClasspath = kotlinDslAdditionalResolverCp
).let { kotlinDslTemplates.addAll(it) }
).let { kotlinDslTemplates.addAll(it) }
loadGradleTemplates(
templateClass = "org.gradle.kotlin.dsl.KotlinSettingsScript",
dependencySelector = kotlinDslDependencySelector,
additionalResolverClasspath = kotlinDslAdditionalResolverCp
loadGradleTemplates(
templateClass = "org.gradle.kotlin.dsl.KotlinSettingsScript",
dependencySelector = kotlinDslDependencySelector,
additionalResolverClasspath = kotlinDslAdditionalResolverCp
).let { kotlinDslTemplates.addAll(it) }
).let { kotlinDslTemplates.addAll(it) }
// KotlinBuildScript should be last because it has wide scriptFilePattern
loadGradleTemplates(
templateClass = "org.gradle.kotlin.dsl.KotlinBuildScript",
dependencySelector = kotlinDslDependencySelector,
additionalResolverClasspath = kotlinDslAdditionalResolverCp
).let { kotlinDslTemplates.addAll(it) }
// KotlinBuildScript should be last because it has wide scriptFilePattern
loadGradleTemplates(
templateClass = "org.gradle.kotlin.dsl.KotlinBuildScript",
dependencySelector = kotlinDslDependencySelector,
additionalResolverClasspath = kotlinDslAdditionalResolverCp
).let { kotlinDslTemplates.addAll(it) }
if (kotlinDslTemplates.isNotEmpty()) {
return kotlinDslTemplates.toList()
if (kotlinDslTemplates.isNotEmpty()) {
return kotlinDslTemplates.asSequence()
}
val default = tryToLoadOldBuildScriptDefinition()
if (default.isNotEmpty()) {
return default.asSequence()
}
return sequenceOf(ErrorGradleScriptDefinition())
}
val default = tryToLoadOldBuildScriptDefinition()
if (default.isNotEmpty()) {
return default
}
return listOf(ErrorGradleScriptDefinition())
}
private fun tryToLoadOldBuildScriptDefinition(): List<KotlinScriptDefinition> {
private fun tryToLoadOldBuildScriptDefinition(): List<ScriptDefinition> {
failedToLoad.set(false)
return loadGradleTemplates(
@@ -154,7 +150,7 @@ class GradleScriptDefinitionsContributor(private val project: Project) : ScriptD
private fun loadGradleTemplates(
templateClass: String, dependencySelector: Regex,
additionalResolverClasspath: (gradleLibDir: File) -> List<File>
): List<KotlinScriptDefinition> = try {
): List<ScriptDefinition> = try {
doLoadGradleTemplates(templateClass, dependencySelector, additionalResolverClasspath)
} catch (t: Throwable) {
// TODO: review exception handling
@@ -170,11 +166,11 @@ class GradleScriptDefinitionsContributor(private val project: Project) : ScriptD
private fun doLoadGradleTemplates(
templateClass: String, dependencySelector: Regex,
additionalResolverClasspath: (gradleLibDir: File) -> List<File>
): List<KotlinScriptDefinition> {
fun createEnvironment(
): List<ScriptDefinition> {
fun createHostConfiguration(
gradleExeSettings: GradleExecutionSettings,
projectSettings: GradleProjectSettings
): Environment {
): ScriptingHostConfiguration {
val gradleJvmOptions = gradleExeSettings.daemonVmOptions?.let { vmOptions ->
CommandLineTokenizer(vmOptions).toList()
.mapNotNull { it?.let { it as? String } }
@@ -182,7 +178,8 @@ class GradleScriptDefinitionsContributor(private val project: Project) : ScriptD
.distinct()
} ?: emptyList()
return mapOf(
val environment = mapOf(
"gradleHome" to gradleExeSettings.gradleHome?.let(::File),
"gradleJavaHome" to gradleExeSettings.javaHome,
@@ -194,6 +191,9 @@ class GradleScriptDefinitionsContributor(private val project: Project) : ScriptD
"getScriptSectionTokens" to ::topLevelSectionCodeTextTokens
)
return ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) {
getEnvironment { environment }
}
}
val gradleSettings = ExternalSystemApiUtil.getSettings(project, GradleConstants.SYSTEM_ID)
@@ -221,14 +221,17 @@ class GradleScriptDefinitionsContributor(private val project: Project) : ScriptD
return loadDefinitionsFromTemplates(
listOf(templateClass),
templateClasspath,
createEnvironment(gradleExeSettings, projectSettings),
createHostConfiguration(gradleExeSettings, projectSettings),
additionalResolverClasspath(gradleLibDir)
).map {
// Expand scope for old gradle script definition
if (it is KotlinScriptDefinitionFromAnnotatedTemplate && !it.scriptExpectedLocations.contains(ScriptExpectedLocation.Project))
GradleKotlinScriptDefinitionFromAnnotatedTemplate(it)
else
it
it.asLegacyOrNull<KotlinScriptDefinitionFromAnnotatedTemplate>()?.let { legacyDef ->
@Suppress("DEPRECATION")
if (legacyDef.scriptExpectedLocations.contains(ScriptExpectedLocation.Project)) null
else {
// 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)
}
private class ErrorGradleScriptDefinition(message: String? = null) : KotlinScriptDefinitionAdapterFromNewAPIBase() {
companion object {
private const val KOTLIN_DSL_SCRIPT_EXTENSION = "gradle.kts"
// TODO: refactor - minimize
private class ErrorGradleScriptDefinition(message: String? = null) :
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 {
@@ -280,6 +288,7 @@ internal class GradleSyncState {
class GradleKotlinScriptDefinitionFromAnnotatedTemplate(
base: KotlinScriptDefinitionFromAnnotatedTemplate
) : KotlinScriptDefinitionFromAnnotatedTemplate(base.template, base.environment, base.templateClasspath) {
@Suppress("DEPRECATION")
override val scriptExpectedLocations: List<ScriptExpectedLocation>
get() = listOf(ScriptExpectedLocation.Project)
}
@@ -48,6 +48,7 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.resolve.AnalyzingUtils
import java.io.File
import kotlin.script.experimental.api.valueOrNull
class KtCompilingExecutor(file: ScratchFile) : ScratchExecutor(file) {
companion object {
@@ -186,8 +187,10 @@ class KtCompilingExecutor(file: ScratchFile) : ScratchExecutor(file) {
javaParameters.classPath.addAll(JavaParametersBuilder.getModuleDependencies(module))
}
val scriptDependencies = ScriptDependenciesManager.getInstance(originalFile.project).getScriptDependencies(originalFile.virtualFile)
javaParameters.classPath.addAll(scriptDependencies.classpath.map { it.absolutePath })
ScriptDependenciesManager.getInstance(originalFile.project)
.getRefinedCompilationConfiguration(originalFile.virtualFile)?.valueOrNull()?.let {
javaParameters.classPath.addAll(it.dependenciesClassPath.map { f -> f.absolutePath })
}
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.resolve.unsafeResolveToDescriptor
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.project.KOTLIN_CONSOLE_KEY
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.repl.ReplState
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import java.awt.Color
import java.awt.Font
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import kotlin.properties.Delegates
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
private val KOTLIN_SHELL_EXECUTE_ACTION_ID = "KotlinShellExecute"
@@ -305,20 +308,21 @@ class KotlinConsoleRunner(
}
}
class ConsoleScriptDefinitionContributor: ScriptDefinitionContributor {
private val definitions = ContainerUtil.newConcurrentSet<KotlinScriptDefinition>()
class ConsoleScriptDefinitionContributor: ScriptDefinitionSourceAsContributor {
val definitionsSet = ContainerUtil.newConcurrentSet<ScriptDefinition>()
override val definitions: Sequence<ScriptDefinition>
get() = definitionsSet.asSequence()
override val id: String = "IDEA Console"
override fun getDefinitions(): List<KotlinScriptDefinition> {
return definitions.toList()
}
// TODO: rewrite to ScriptDefinition
fun registerDefinition(definition: KotlinScriptDefinition) {
definitions.add(definition)
definitionsSet.add(ScriptDefinition.FromLegacy(defaultJvmScriptingHostConfiguration, definition))
}
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"
serviceImplementation="org.jetbrains.kotlin.idea.core.script.ScriptDependenciesManager"/>
<projectService serviceInterface="org.jetbrains.kotlin.idea.core.script.ScriptDependenciesCache"
serviceImplementation="org.jetbrains.kotlin.idea.core.script.ScriptDependenciesCache"/>
<projectService serviceInterface="org.jetbrains.kotlin.idea.core.script.ScriptsCompilationConfigurationCache"
serviceImplementation="org.jetbrains.kotlin.idea.core.script.ScriptsCompilationConfigurationCache"/>
<projectService serviceInterface="org.jetbrains.kotlin.idea.core.script.ScriptDependenciesUpdater"
serviceImplementation="org.jetbrains.kotlin.idea.core.script.ScriptDependenciesUpdater"/>
<projectService serviceInterface="org.jetbrains.kotlin.idea.core.script.ScriptsCompilationConfigurationUpdater"
serviceImplementation="org.jetbrains.kotlin.idea.core.script.ScriptsCompilationConfigurationUpdater"/>
<projectService serviceInterface="org.jetbrains.kotlin.scripting.resolve.ScriptReportSink"
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.resolveToDescriptorIfAny
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.findUsages.KotlinFindUsagesHandlerFactory
import org.jetbrains.kotlin.idea.findUsages.handlers.KotlinFindClassUsagesHandler
@@ -127,7 +127,7 @@ class UnusedSymbolInspection : AbstractKotlinInspection() {
val usedScripts = findScriptsWithUsages(declaration)
if (usedScripts.isNotEmpty()) {
if (ScriptDependenciesUpdater.getInstance(declaration.project).updateDependenciesIfNeeded(usedScripts)) {
if (ScriptsCompilationConfigurationUpdater.getInstance(declaration.project).updateDependenciesIfNeeded(usedScripts)) {
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.Task
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.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
import kotlin.concurrent.write
abstract class AsyncScriptDefinitionsContributor(protected val project: Project) : ScriptDefinitionContributor {
abstract class AsyncScriptDefinitionsContributor(protected val project: Project) : ScriptDefinitionSourceAsContributor {
abstract val progressMessage: String
override fun isReady(): Boolean = definitions != null
override fun isReady(): Boolean = _definitions != null
override fun getDefinitions(): List<KotlinScriptDefinition> {
definitionsLock.read {
if (definitions != null) {
return definitions!!
override val definitions: Sequence<ScriptDefinition>
get() {
definitionsLock.read {
if (_definitions != null) {
return _definitions!!.asSequence()
}
}
}
forceStartUpdate = false
asyncRunUpdateScriptTemplates()
return emptyList()
}
forceStartUpdate = false
asyncRunUpdateScriptTemplates()
return emptySequence()
}
protected fun asyncRunUpdateScriptTemplates() {
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()
protected var forceStartUpdate = false
@@ -77,10 +78,10 @@ abstract class AsyncScriptDefinitionsContributor(protected val project: Project)
val wasRunning = definitionsLock.isWriteLocked
val needReload = definitionsLock.write {
if (wasRunning && !forceStartUpdate && definitions != null) return@write false
val newDefinitions = loadScriptDefinitions(definitions)
if (newDefinitions != definitions) {
definitions = newDefinitions
if (wasRunning && !forceStartUpdate && _definitions != null) return@write false
val newDefinitions = loadScriptDefinitions(_definitions)
if (newDefinitions != _definitions) {
_definitions = newDefinitions
return@write true
}
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.CompilerMessageSeverity
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.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionsFromClasspathDiscoverySource
import org.jetbrains.kotlin.scripting.definitions.reporter
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 fun getDefinitions(): List<KotlinScriptDefinition> {
val extensions = Extensions.getArea(project).getExtensionPoint(ScriptDefinitionsProvider.EP_NAME).extensions
val messageCollector = LoggingMessageCollector()
return extensions.flatMap { provider ->
val explicitClasses = provider.getDefinitionClasses().toList()
val classPath = provider.getDefinitionsClassPath().toList()
val explicitDefinitions =
if (explicitClasses.isNotEmpty()) loadDefinitionsFromTemplates(explicitClasses, classPath)
else emptyList()
val discoveredDefinitions =
if (provider.useDiscovery()) emptySequence()
else ScriptDefinitionsFromClasspathDiscoverySource(
classPath,
emptyMap(),
messageCollector.reporter
).definitions
explicitDefinitions + discoveredDefinitions
override val definitions: Sequence<ScriptDefinition>
get() {
val extensions = Extensions.getArea(project).getExtensionPoint(ScriptDefinitionsProvider.EP_NAME).extensions
val messageCollector = LoggingMessageCollector()
return extensions.asSequence().flatMap { provider ->
val explicitClasses = provider.getDefinitionClasses().toList()
val classPath = provider.getDefinitionsClassPath().toList()
val hostConfiguration = defaultJvmScriptingHostConfiguration
val explicitDefinitions =
if (explicitClasses.isEmpty()) emptySequence()
else loadDefinitionsFromTemplates(explicitClasses, classPath, hostConfiguration).asSequence()
val discoveredDefinitions =
if (provider.useDiscovery()) emptySequence()
else ScriptDefinitionsFromClasspathDiscoverySource(
classPath,
hostConfiguration,
messageCollector.reporter
).definitions
explicitDefinitions + discoveredDefinitions
}
}
}
}
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.settings.KotlinScriptingSettings
import org.jetbrains.kotlin.parsing.KotlinParserDefinition
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinitionAdapterFromNewAPIBase
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
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>) :
ListTableModel<KotlinScriptDefinitionsModelDescriptor>(
@@ -30,7 +29,7 @@ class KotlinScriptDefinitionsModel private constructor(definitions: MutableList<
) {
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)) }
}
@@ -41,12 +40,9 @@ class KotlinScriptDefinitionsModel private constructor(definitions: MutableList<
private class ScriptDefinitionPattern : ColumnInfo<KotlinScriptDefinitionsModelDescriptor, String>("Pattern/Extension") {
override fun valueOf(item: KotlinScriptDefinitionsModelDescriptor): String {
val definition = item.definition
return when (definition) {
is KotlinScriptDefinitionFromAnnotatedTemplate -> definition.scriptFilePattern.pattern
is KotlinScriptDefinitionAdapterFromNewAPIBase -> definition.fileExtension
is StandardIdeScriptDefinition -> KotlinParserDefinition.STD_SCRIPT_EXT
else -> ""
}
return definition.asLegacyOrNull<KotlinScriptDefinitionFromAnnotatedTemplate>()?.scriptFilePattern?.pattern
?: definition.asLegacyOrNull<StandardIdeScriptDefinition>()?.let { KotlinParserDefinition.STD_SCRIPT_EXT }
?: definition.fileExtension
}
}
@@ -58,11 +54,12 @@ class KotlinScriptDefinitionsModel private constructor(definitions: MutableList<
override fun getEditor(item: KotlinScriptDefinitionsModelDescriptor?) = BooleanTableCellEditor()
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 {
fun createModel(definitions: List<KotlinScriptDefinition>, settings: KotlinScriptingSettings): KotlinScriptDefinitionsModel {
fun createModel(definitions: List<ScriptDefinition>, settings: KotlinScriptingSettings): KotlinScriptDefinitionsModel {
return KotlinScriptDefinitionsModel(definitions.mapTo(arrayListOf()) {
KotlinScriptDefinitionsModelDescriptor(
it,
@@ -14,7 +14,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager;
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 java.awt.*;
@@ -64,7 +64,7 @@ public class KotlinScriptingSettingsConfigurable implements SearchableConfigurab
if (isScriptDefinitionsChanged()) {
for (KotlinScriptDefinitionsModelDescriptor item : model.getItems()) {
KotlinScriptDefinition definition = item.getDefinition();
ScriptDefinition definition = item.getDefinition();
settings.setOrder(definition, model.getItems().indexOf(item));
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.parsing.KotlinParserDefinition
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinitionAdapterFromNewAPIBase
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
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)
.getAllDefinitions()
.filter {
it !is StandardIdeScriptDefinition &&
it.isScript(ktFile.name) &&
it.asLegacyOrNull<StandardIdeScriptDefinition>() == null && it.isScript(ktFile.name) &&
KotlinScriptingSettings.getInstance(project).isScriptDefinitionEnabled(it)
}
.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) {
return (allApplicableDefinitions[0] as? KotlinScriptDefinitionFromAnnotatedTemplate)?.scriptFilePattern?.pattern == "^(settings|.+\\.settings)\\.gradle\\.kts\$"
&& (allApplicableDefinitions[1] as? KotlinScriptDefinitionFromAnnotatedTemplate)?.scriptFilePattern?.pattern == ".*\\.gradle\\.kts"
return allApplicableDefinitions[0].asLegacyOrNull<KotlinScriptDefinitionFromAnnotatedTemplate>()?.scriptFilePattern?.pattern == "^(settings|.+\\.settings)\\.gradle\\.kts\$"
&& allApplicableDefinitions[1].asLegacyOrNull<KotlinScriptDefinitionFromAnnotatedTemplate>()?.scriptFilePattern?.pattern == ".*\\.gradle\\.kts"
}
return false
}
@@ -67,25 +65,18 @@ class MultipleScriptDefinitionsChecker(private val project: Project) : EditorNot
companion object {
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 {
setText("Multiple script definitions are applicable for this script. ${defs.first().name} is used")
createComponentActionLabel("Show all") {
val list = JBPopupFactory.getInstance().createListPopup(
object : BaseListPopupStep<KotlinScriptDefinition>(null, defs) {
override fun getTextFor(value: KotlinScriptDefinition): String {
return when (value) {
is KotlinScriptDefinitionFromAnnotatedTemplate -> {
value.name + " (${value.scriptFilePattern})"
}
is KotlinScriptDefinitionAdapterFromNewAPIBase -> {
value.name + " (${value.fileExtension})"
}
is StandardIdeScriptDefinition -> {
value.name + " (${KotlinParserDefinition.STD_SCRIPT_EXT})"
}
else -> value.name
}
object : BaseListPopupStep<ScriptDefinition>(null, defs) {
override fun getTextFor(value: ScriptDefinition): String {
return value.asLegacyOrNull<KotlinScriptDefinitionFromAnnotatedTemplate>()?.let {
it.name + " (${it.scriptFilePattern})"
} ?: value.asLegacyOrNull<StandardIdeScriptDefinition>()?.let {
it.name + " (${KotlinParserDefinition.STD_SCRIPT_EXT})"
} ?: value.name + " (${value.fileExtension})"
}
}
)
@@ -20,16 +20,19 @@ import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.config.CompilerSettings
import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCompilerSettings
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.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 kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
class ScriptTemplatesFromCompilerSettingsProvider(
private val project: Project,
private val compilerSettings: KotlinCompilerSettings
) : ScriptDefinitionContributor {
) : ScriptDefinitionSourceAsContributor {
init {
project.messageBus.connect().subscribe(KotlinCompilerSettingsListener.TOPIC, object : KotlinCompilerSettingsListener {
@@ -41,17 +44,22 @@ class ScriptTemplatesFromCompilerSettingsProvider(
})
}
override fun getDefinitions(): List<KotlinScriptDefinition> {
val kotlinSettings = compilerSettings.settings
return if (kotlinSettings.scriptTemplates.isBlank()) emptyList()
else loadDefinitionsFromTemplates(
templateClassNames = kotlinSettings.scriptTemplates.split(',', ' '),
templateClasspath = kotlinSettings.scriptTemplatesClasspath.split(File.pathSeparator).map(::File),
environment = mapOf(
"projectRoot" to (project.basePath ?: project.baseDir.canonicalPath)?.let(::File)
)
)
}
override val definitions: Sequence<ScriptDefinition>
get() {
val kotlinSettings = compilerSettings.settings
return if (kotlinSettings.scriptTemplates.isBlank()) emptySequence()
else loadDefinitionsFromTemplates(
templateClassNames = kotlinSettings.scriptTemplates.split(',', ' '),
templateClasspath = kotlinSettings.scriptTemplatesClasspath.split(File.pathSeparator).map(::File),
baseHostConfiguration = ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) {
getEnvironment {
mapOf(
"projectRoot" to (project.basePath ?: project.baseDir.canonicalPath)?.let(::File)
)
}
}
).asSequence()
}
override val id: String = "KotlinCompilerScriptTemplatesSettings"
}
@@ -14,12 +14,12 @@ import com.intellij.openapi.vfs.JarFileSystem
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.idea.core.script.loadDefinitionsFromTemplates
import org.jetbrains.kotlin.idea.util.projectStructure.allModules
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.SCRIPT_DEFINITION_MARKERS_EXTENSION_WITH_DOT
import org.jetbrains.kotlin.scripting.definitions.SCRIPT_DEFINITION_MARKERS_PATH
import org.jetbrains.kotlin.scripting.definitions.*
import java.io.File
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.write
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
class ScriptTemplatesFromDependenciesProvider(project: Project) : AsyncScriptDefinitionsContributor(project) {
@@ -41,7 +41,7 @@ class ScriptTemplatesFromDependenciesProvider(project: Project) : AsyncScriptDef
override val id = "ScriptTemplatesFromDependenciesProvider"
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 newTemplates = scriptDefinitionsFromDependencies(project)
if (newTemplates != templates) {
@@ -51,12 +51,17 @@ class ScriptTemplatesFromDependenciesProvider(project: Project) : AsyncScriptDef
return@write null
}
if (templatesCopy != null) {
val hostConfiguration = ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) {
getEnvironment {
mapOf(
"projectRoot" to (project.basePath ?: project.baseDir.canonicalPath)?.let(::File)
)
}
}
return loadDefinitionsFromTemplates(
templateClassNames = templatesCopy.templates,
templateClasspath = templatesCopy.classpath,
environment = mapOf(
"projectRoot" to (project.basePath ?: project.baseDir.canonicalPath)?.let(::File)
)
baseHostConfiguration = hostConfiguration
)
}
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.utils.addIfNotNull
import java.util.*
import kotlin.script.experimental.api.valueOrNull
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 scriptExtraImports = contextFile.takeIf { it.isScript() }?.let { ktFile ->
val scriptDependencies = ScriptDependenciesProvider.getInstance(ktFile.project)?.getScriptDependencies(ktFile.originalFile)
scriptDependencies?.imports?.map { ImportPath.fromString(it) }
val scriptDependencies =
ScriptDependenciesProvider.getInstance(ktFile.project)?.getScriptConfigurationResult(ktFile.originalFile)?.valueOrNull()
scriptDependencies?.defaultImports?.map { ImportPath.fromString(it) }
}.orEmpty()
return importPath.isImported(allDefaultImports + scriptExtraImports, platform.findAnalyzerServices.excludedImports)
@@ -1,4 +1,3 @@
<error descr="TestDependenciesResolver threw exception IllegalStateException:
Exception from resolver"></error>
<error descr="Exception from resolver (java.lang.IllegalStateException: Exception from resolver)"></error>
val s = 3
val g = 4
@@ -22,7 +22,7 @@ import com.intellij.testFramework.LightProjectDescriptor;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
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.KotlinTestUtils;
import org.jetbrains.plugins.groovy.GroovyFileType;
@@ -42,12 +42,12 @@ public abstract class AbstractConfigureProjectByChangingFileTest<C extends Kotli
ApplicationManager.getApplication().runWriteAction(
() -> FileTypeManager.getInstance().associateExtension(GroovyFileType.GROOVY_FILE_TYPE, "gradle")
);
ScriptDependenciesUpdaterKt.setScriptDependenciesUpdaterDisabled(ApplicationManager.getApplication(), true);
ScriptsCompilationConfigurationUpdaterKt.setScriptDependenciesUpdaterDisabled(ApplicationManager.getApplication(), true);
}
@Override
protected void tearDown() throws Exception {
ScriptDependenciesUpdaterKt.setScriptDependenciesUpdaterDisabled(ApplicationManager.getApplication(), false);
ScriptsCompilationConfigurationUpdaterKt.setScriptDependenciesUpdaterDisabled(ApplicationManager.getApplication(), false);
moduleInfoFile = null;
super.tearDown();
}
@@ -5,32 +5,41 @@
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.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.getEnvironment
import java.io.File
import kotlin.script.dependencies.Environment
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
class CustomScriptTemplateProvider(
val environment: Environment
) : ScriptDefinitionContributor {
class CustomScriptTemplateProvider(val environment: Environment) : ScriptDefinitionSourceAsContributor {
override val id = "Test"
override fun getDefinitions() = loadDefinitionsFromTemplates(
override val definitions: Sequence<ScriptDefinition>
get() = loadDefinitionsFromTemplates(
templateClassNames = environment["template-classes-names"] as List<String>,
templateClasspath = listOfNotNull(environment["template-classes"] as? File),
environment = environment
)
baseHostConfiguration = ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) {
getEnvironment { environment }
}
).asSequence()
}
class FromTextTemplateProvider(
val environment: Map<String, Any?>
) : ScriptDefinitionContributor {
class FromTextTemplateProvider(val environment: Map<String, Any?>) : ScriptDefinitionSourceAsContributor {
override val id = "Test"
override fun getDefinitions() = loadDefinitionsFromTemplates(
override val definitions: Sequence<ScriptDefinition>
get() = loadDefinitionsFromTemplates(
templateClassNames = listOf("org.jetbrains.kotlin.idea.script.Template"),
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>() {
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(
customMessage: String? = null,
path: String? = null,
location: SourceCode.Location? = null
location: SourceCode.Location? = null,
severity: ScriptDiagnostic.Severity = ScriptDiagnostic.Severity.ERROR
): 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]
@@ -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
*/
fun <R> ResultWithDiagnostics<R>.resultOrNull(): R? = when (this) {
fun <R> ResultWithDiagnostics<R>.valueOrNull(): R? = when (this) {
is ResultWithDiagnostics.Success<R> -> value
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
*/
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.Failure -> body(this)
}
@@ -59,7 +59,7 @@ val ScriptCompilationConfigurationKeys.fileExtension by PropertiesCollection.key
/**
* 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:
@@ -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
*/
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 text: String by lazy { preloadedText ?: file.readText() }
override val name: String? get() = file.name
@@ -18,13 +18,13 @@ import java.util.jar.JarFile
import kotlin.reflect.KClass
import kotlin.script.experimental.api.*
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.jvm.BasicJvmScriptEvaluator
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.experimental.jvm.impl.KJvmCompiledScript
import kotlin.script.experimental.jvmhost.*
import kotlin.script.experimental.jvmhost.impl.CompiledScriptClassLoader
import kotlin.script.experimental.jvm.impl.KJvmCompiledScript
import kotlin.script.templates.standard.SimpleScriptTemplate
class ScriptingHostTest : TestCase() {
@@ -86,7 +86,7 @@ class ScriptingHostTest : TestCase() {
val scriptName = "SavedRunnableScript"
val compiledScript = runBlocking {
compiler("println(\"$greeting\")".toScriptSource(name = "$scriptName.kts"), compilationConfiguration).throwOnFailure()
.resultOrNull()!!
.valueOrNull()!!
}
val saver = BasicJvmScriptJarGenerator(outJar)
runBlocking {
@@ -353,8 +353,8 @@ class ScriptingHostTest : TestCase() {
val compiledScriptClassRes = runBlocking { compiledScript!!.getClass(null) }
val cachedScriptClassRes = runBlocking { cachedScript!!.getClass(null) }
val compiledScriptClass = compiledScriptClassRes.resultOrNull()
val cachedScriptClass = cachedScriptClassRes.resultOrNull()
val compiledScriptClass = compiledScriptClassRes.valueOrNull()
val cachedScriptClass = cachedScriptClassRes.valueOrNull()
Assert.assertEquals(compiledScriptClass!!.qualifiedName, cachedScriptClass!!.qualifiedName)
Assert.assertEquals(compiledScriptClass!!.supertypes, cachedScriptClass!!.supertypes)
@@ -380,7 +380,7 @@ class ScriptingHostTest : TestCase() {
val res = compiler(script.toScriptSource(), scriptCompilationConfiguration).throwOnFailure()
(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
Assert.assertTrue(classLoader is CompiledScriptClassLoader)
@@ -463,7 +463,7 @@ private fun ScriptCompilationConfiguration.Builder.makeSimpleConfigurationWithTe
refineConfiguration {
beforeCompiling { ctx ->
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
} else {
ScriptCompilationConfiguration(ctx.compilationConfiguration) {
@@ -33,7 +33,7 @@ class KJvmCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) : KJvm
val mainKtFile =
getScriptKtFile(script, context.baseScriptCompilationConfiguration, context.environment.project, messageCollector)
.resultOr { return it }
.valueOr { return it }
context.scriptCompilationState.configureFor(script, context.baseScriptCompilationConfiguration)
@@ -31,7 +31,7 @@ class KJvmReplCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) :
override fun createReplCompilationState(scriptCompilationConfiguration: ScriptCompilationConfiguration): JvmReplCompilerState.Compilation {
val context = withMessageCollectorAndDisposable(disposeOnSuccess = false) { messageCollector, disposable ->
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)
}
@@ -42,7 +42,7 @@ class KJvmReplCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) :
): ResultWithDiagnostics<Boolean> =
withMessageCollector(script) { messageCollector ->
val ktFile = getScriptKtFile(script, scriptCompilationConfiguration, project, messageCollector)
.resultOr { return it }
.valueOr { return it }
val errorHolder = object : MessageCollectorBasedReporter {
override val messageCollector = messageCollector
}
@@ -80,7 +80,7 @@ class KJvmReplCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) :
val snippetKtFile =
getScriptKtFile(snippet, context.baseScriptCompilationConfiguration, context.environment.project, messageCollector)
.resultOr { return it }
.valueOr { return it }
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.scripting.compiler.plugin.ScriptingCompilerConfigurationComponentRegistrar
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.collectScriptsCompilationDependencies
import kotlin.script.experimental.api.ScriptCompilationConfiguration
@@ -88,10 +89,13 @@ internal fun createInitialConfigurations(
kotlinCompilerConfiguration.add(
ScriptingConfigurationKeys.SCRIPT_DEFINITIONS,
BridgeScriptDefinition(
scriptCompilationConfiguration,
ScriptDefinition.FromLegacy(
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.project.Project
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.impl.PsiFileFactoryImpl
import com.intellij.testFramework.LightVirtualFile
import org.jetbrains.kotlin.cli.common.environment.setIdeaIoUseFallback
import org.jetbrains.kotlin.codegen.state.GenerationState
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.renderer.DescriptorRenderer
import org.jetbrains.kotlin.scripting.dependencies.ScriptsCompilationDependencies
import org.jetbrains.kotlin.scripting.resolve.ScriptLightVirtualFile
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import java.util.*
import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.FileBasedScriptSource
import kotlin.script.experimental.host.FileScriptSource
import kotlin.script.experimental.host.getMergedScriptText
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) =
KJvmCompiledModuleInMemory(
generationState.factory.asList()
@@ -98,7 +86,7 @@ internal fun getScriptKtFile(
val scriptText = getMergedScriptText(script, scriptCompilationConfiguration)
val virtualFile = ScriptLightVirtualFile(
script.scriptFileName(script, scriptCompilationConfiguration),
(script as? FileScriptSource)?.file?.path,
(script as? FileBasedScriptSource)?.file?.path,
scriptText
)
val ktFile = psiFileFactory.trySetupPsiForFile(virtualFile, KotlinLanguage.INSTANCE, true, false) as KtFile?
@@ -40,7 +40,7 @@ open class BasicJvmScriptEvaluator : ScriptEvaluator {
?.onFailure {
return@invoke ResultWithDiagnostics.Failure(it.reports)
}
?.resultOrNull()
?.valueOrNull()
?: configuration
val instance =
@@ -48,8 +48,13 @@ fun mapLegacyScriptPosition(pos: ScriptReport.Position?): SourceCode.Location? =
}
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))
}
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 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() =
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")
}
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 {
val externalSource = it as? ExternalSourceCode
@@ -9,6 +9,7 @@ package kotlin.script.experimental.jvm
import kotlin.reflect.KClass
import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.util.PropertiesCollection
interface JvmScriptEvaluationConfigurationKeys
@@ -21,7 +22,10 @@ open class JvmScriptEvaluationConfigurationBuilder : PropertiesCollection.Builde
/**
* 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
@@ -11,6 +11,7 @@ import java.net.URLClassLoader
import kotlin.reflect.KClass
import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.*
import kotlin.script.experimental.jvm.impl.toClassPathOrEmpty
import kotlin.script.experimental.util.PropertiesCollection
interface JvmScriptingHostConfigurationKeys
@@ -25,6 +26,12 @@ val JvmScriptingHostConfigurationKeys.javaHome 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")
val ScriptingHostConfigurationKeys.jvm
get() = JvmScriptingHostConfigurationBuilder()
@@ -49,15 +56,17 @@ class JvmGetScriptingClass : GetScriptingClass, Serializable {
@Transient
private var baseClassLoader: ClassLoader? = null
override fun invoke(classType: KotlinType, contextClass: KClass<*>, hostConfiguration: ScriptingHostConfiguration): KClass<*> =
invoke(classType, contextClass.java.classLoader, hostConfiguration)
@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
val contextClassloader = contextClass.java.classLoader
val fromClass = classType.fromClass
if (fromClass != null) {
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
}
@@ -71,7 +80,7 @@ class JvmGetScriptingClass : GetScriptingClass, Serializable {
}
if (baseClassLoaderIsInitialized != true) {
baseClassLoader = contextClassloader
baseClassLoader = contextClassLoader
baseClassLoaderIsInitialized = true
}
// TODO: this check breaks testLazyScriptDefinition, find out the reason and fix
@@ -88,12 +97,12 @@ class JvmGetScriptingClass : GetScriptingClass, Serializable {
}
}
classLoader =
if (classpath == null || classpath.isEmpty()) baseClassLoader
else URLClassLoader(classpath.toTypedArray(), baseClassLoader)
if (classpath == null || classpath.isEmpty()) baseClassLoader
else URLClassLoader(classpath.toTypedArray(), baseClassLoader)
}
return try {
classLoader!!.loadClass(classType.typeName).kotlin
(classLoader ?: ClassLoader.getSystemClassLoader()).loadClass(classType.typeName).kotlin
} catch (e: Throwable) {
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.reporter
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_MINOR_VERSION = 0
@@ -166,7 +167,8 @@ internal class DiscoverScriptExtensionsTransform : ArtifactTransform() {
override fun transform(input: File): List<File> {
val definitions =
ScriptDefinitionsFromClasspathDiscoverySource(
listOf(input), emptyMap(),
listOf(input),
defaultJvmScriptingHostConfiguration,
PrintingMessageCollector(System.out, MessageRenderer.WITHOUT_PATHS, false).reporter
).definitions
val extensions = definitions.mapTo(arrayListOf()) { it.fileExtension }
@@ -14,6 +14,7 @@ import kotlin.script.dependencies.ScriptContents
import kotlin.script.dependencies.ScriptDependenciesResolver
import kotlin.script.experimental.annotations.KotlinScript
import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.FileBasedScriptSource
import kotlin.script.experimental.host.FileScriptSource
import kotlin.script.experimental.jvm.compat.mapLegacyDiagnosticSeverity
import kotlin.script.experimental.jvm.compat.mapLegacyScriptPosition
@@ -59,7 +60,7 @@ class MainKtsConfigurator : RefineScriptCompilationConfigurationHandler {
val annotations = context.collectedData?.get(ScriptCollectedData.foundAnnotations)?.takeIf { it.isNotEmpty() }
?: return context.compilationConfiguration.asSuccess()
val scriptBaseDir = (context.script as? FileScriptSource)?.file?.parentFile
val scriptBaseDir = (context.script as? FileBasedScriptSource)?.file?.parentFile
val importedSources = annotations.flatMap {
(it as? Import)?.paths?.map { 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.util.Disposer;
import kotlin.script.experimental.jvm.JvmScriptingHostConfigurationKt;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.DefaultArtifact;
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);
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);
@@ -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.configuration.ScriptingConfigurationKeys
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.utils.PathUtil.getResourcePathForClass
import org.junit.Assert
@@ -44,6 +45,7 @@ import java.io.ByteArrayOutputStream
import java.io.OutputStream
import java.io.PrintStream
import kotlin.reflect.KClass
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
const val KOTLIN_JAVA_RUNTIME_JAR = "kotlin-stdlib.jar"
@@ -155,7 +157,12 @@ done
}
}
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)
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.extensions.StorageComponentContainerContributor
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
import java.io.File
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.extensions.SamWithReceiverAnnotations
import kotlin.script.templates.ScriptTemplateDefinition
@@ -31,7 +33,10 @@ abstract class AbstractSamWithReceiverScriptTest : AbstractDiagnosticsTest() {
override fun createEnvironment(file: File) = super.createEnvironment(file).apply {
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)
}
}
@@ -65,12 +65,12 @@ class IdeSamWithReceiverComponentContributor(val project: Project) : StorageComp
val moduleInfo = moduleDescriptor.getCapability(ModuleInfo.Capability)
val annotations =
when (moduleInfo) {
is ScriptModuleInfo -> moduleInfo.scriptDefinition.annotationsForSamWithReceivers
is ScriptDependenciesInfo.ForFile -> moduleInfo.scriptDefinition.annotationsForSamWithReceivers
is ModuleProductionSourceInfo -> getAnnotationsForModule(moduleInfo.module)
else -> null
} ?: return
when (moduleInfo) {
is ScriptModuleInfo -> moduleInfo.scriptDefinition.legacyDefinition.annotationsForSamWithReceivers
is ScriptDependenciesInfo.ForFile -> moduleInfo.scriptDefinition.legacyDefinition.annotationsForSamWithReceivers
is ModuleProductionSourceInfo -> getAnnotationsForModule(moduleInfo.module)
else -> null
} ?: return
container.useInstance(SamWithReceiverResolverExtension(annotations))
}
@@ -6,13 +6,13 @@
package org.jetbrains.kotlin.scripting.configuration
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 java.io.File
object ScriptingConfigurationKeys {
val SCRIPT_DEFINITIONS = CompilerConfigurationKey.create<List<KotlinScriptDefinition>>("script definitions")
val SCRIPT_DEFINITIONS = CompilerConfigurationKey.create<List<ScriptDefinition>>("script definitions")
val SCRIPT_DEFINITIONS_SOURCES =
CompilerConfigurationKey.create<List<ScriptDefinitionsSource>>("script definitions sources")
@@ -5,11 +5,14 @@
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.jvm.config.jvmClasspathRoots
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.scripting.definitions.loadScriptTemplatesFromClasspath
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"
@@ -18,12 +21,16 @@ fun configureScriptDefinitions(
configuration: CompilerConfiguration,
baseClassloader: ClassLoader,
messageCollector: MessageCollector,
scriptResolverEnv: Map<String, Any?>
hostConfiguration: ScriptingHostConfiguration
) {
// TODO: consider using escaping to allow kotlin escaped names in class names
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())
}
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.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 val name: String = "Kotlin Script"
@@ -17,6 +17,7 @@ import kotlin.script.experimental.api.*
import kotlin.script.experimental.dependencies.DependenciesResolver
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.host.getScriptingClass
import kotlin.script.experimental.jvm.compat.mapToLegacyExpectedLocations
import kotlin.script.experimental.jvm.impl.BridgeDependenciesResolver
import kotlin.script.experimental.location.ScriptExpectedLocation
import kotlin.script.experimental.util.getOrError
@@ -80,15 +81,8 @@ abstract class KotlinScriptDefinitionAdapterFromNewAPIBase : KotlinScriptDefinit
@Suppress("DEPRECATION")
override val scriptExpectedLocations: List<ScriptExpectedLocation>
get() = scriptCompilationConfiguration[ScriptCompilationConfiguration.ide.acceptedLocations]?.map {
when (it) {
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)
get() = scriptCompilationConfiguration[ScriptCompilationConfiguration.ide.acceptedLocations]?.mapToLegacyExpectedLocations()
?: listOf(ScriptExpectedLocation.SourcesOnly, ScriptExpectedLocation.TestsOnly)
private val scriptingClassGetter by lazy(LazyThreadSafetyMode.PUBLICATION) {
hostConfiguration[ScriptingHostConfiguration.getScriptingClass]
@@ -9,10 +9,17 @@ import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project
interface ScriptDefinitionProvider {
@Deprecated("Migrating to configuration refinement", level = DeprecationLevel.ERROR)
fun findScriptDefinition(fileName: String): KotlinScriptDefinition?
fun isScript(fileName: String): Boolean
@Deprecated("Migrating to configuration refinement", level = DeprecationLevel.ERROR)
fun getDefaultScriptDefinition(): KotlinScriptDefinition
fun isScript(fileName: String): Boolean
fun findDefinition(fileName: String): ScriptDefinition?
fun getDefaultDefinition(): ScriptDefinition
fun getKnownFilenameExtensions(): Sequence<String>
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.api.KotlinType
import kotlin.script.experimental.api.ScriptCompilationConfiguration
import kotlin.script.experimental.api.ScriptEvaluationConfiguration
import kotlin.script.experimental.api.fileExtension
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.host.configurationDependencies
import kotlin.script.experimental.host.createCompilationConfigurationFromTemplate
import kotlin.script.experimental.host.createEvaluationConfigurationFromTemplate
import kotlin.script.experimental.jvm.JvmDependency
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
class LazyScriptDefinitionFromDiscoveredClass internal constructor(
baseHostConfiguration: ScriptingHostConfiguration,
private val annotationsFromAsm: ArrayList<BinAnnData>,
private val className: String,
private val classpath: List<File>,
private val messageReporter: MessageReporter
) : KotlinScriptDefinitionAdapterFromNewAPIBase() {
) : ScriptDefinition.FromConfigurationsBase() {
constructor(
baseHostConfiguration: ScriptingHostConfiguration,
classBytes: ByteArray,
className: String,
classpath: List<File>,
messageReporter: MessageReporter
) : this(loadAnnotationsFromClass(classBytes), className, classpath, messageReporter)
) : this(baseHostConfiguration, loadAnnotationsFromClass(classBytes), className, classpath, messageReporter)
override val hostConfiguration: ScriptingHostConfiguration by lazy(LazyThreadSafetyMode.PUBLICATION) {
ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) {
ScriptingHostConfiguration(baseHostConfiguration) {
configurationDependencies.append(JvmDependency(classpath))
}
}
override val scriptCompilationConfiguration: ScriptCompilationConfiguration by lazy(LazyThreadSafetyMode.PUBLICATION) {
private val configurations by lazy(LazyThreadSafetyMode.PUBLICATION) {
messageReporter(
CompilerMessageSeverity.LOGGING,
"Configure scripting: loading script definition class $className using classpath $classpath\n. ${Thread.currentThread().stackTrace}"
)
try {
createCompilationConfigurationFromTemplate(
KotlinType(className),
hostConfiguration,
LazyScriptDefinitionFromDiscoveredClass::class
)
val compileCfg =
createCompilationConfigurationFromTemplate(
KotlinType(className),
hostConfiguration,
LazyScriptDefinitionFromDiscoveredClass::class
)
val evalCfg =
createEvaluationConfigurationFromTemplate(
KotlinType(className),
hostConfiguration,
LazyScriptDefinitionFromDiscoveredClass::class
)
compileCfg to evalCfg
} catch (ex: ClassNotFoundException) {
messageReporter(CompilerMessageSeverity.ERROR, "Cannot find script definition class $className")
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) {
annotationsFromAsm.find { it.name == KotlinScript::class.simpleName }?.args
?.find { it.name == "fileExtension" }?.value
?: scriptCompilationConfiguration.let {
?: compilationConfiguration.let {
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 kotlin.concurrent.read
import kotlin.concurrent.write
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
abstract class LazyScriptDefinitionProvider : ScriptDefinitionProvider {
protected val lock = ReentrantReadWriteLock()
protected abstract val currentDefinitions: Sequence<KotlinScriptDefinition>
protected abstract val currentDefinitions: Sequence<ScriptDefinition>
private var _cachedDefinitions: Sequence<KotlinScriptDefinition>? = null
private val cachedDefinitions: Sequence<KotlinScriptDefinition>
protected open fun getScriptingHostConfiguration(): ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration
override fun getDefaultDefinition(): ScriptDefinition =
ScriptDefinition.FromLegacy(getScriptingHostConfiguration(), StandardScriptDefinition)
private var _cachedDefinitions: Sequence<ScriptDefinition>? = null
private val cachedDefinitions: Sequence<ScriptDefinition>
get() {
assert(lock.readLockCount > 0) { "cachedDefinitions should only be used under the read lock" }
if (_cachedDefinitions == null) lock.write {
@@ -37,18 +44,22 @@ abstract class LazyScriptDefinitionProvider : ScriptDefinitionProvider {
fileName.endsWith(it, ignoreCase = true)
}
override fun findScriptDefinition(fileName: String): KotlinScriptDefinition? =
override fun findDefinition(fileName: String): ScriptDefinition? =
if (nonScriptFileName(fileName)) null
else lock.read {
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 {
cachedDefinitions.map { it.fileExtension }
}
override fun getDefaultScriptDefinition(): KotlinScriptDefinition = getDefaultDefinition().legacyDefinition
companion object {
// 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}")
@@ -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 {
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.vfs.VirtualFile
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationResult
import kotlin.script.experimental.api.valueOrNull
import kotlin.script.experimental.dependencies.ScriptDependencies
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 {
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 java.io.File
import java.io.IOException
import java.lang.IllegalArgumentException
import java.net.URLClassLoader
import java.util.jar.JarFile
import kotlin.script.experimental.annotations.KotlinScript
import kotlin.script.experimental.api.KotlinType
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.host.createCompilationConfigurationFromTemplate
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.experimental.host.createEvaluationConfigurationFromTemplate
import kotlin.script.templates.ScriptTemplateDefinition
const val SCRIPT_DEFINITION_MARKERS_PATH = "META-INF/kotlin/script/templates/"
@@ -32,15 +32,15 @@ val MessageCollector.reporter: MessageReporter
class ScriptDefinitionsFromClasspathDiscoverySource(
private val classpath: List<File>,
private val scriptResolverEnv: Map<String, Any?>,
private val hostConfiguration: ScriptingHostConfiguration,
private val messageReporter: MessageReporter
) : ScriptDefinitionsSource {
override val definitions: Sequence<KotlinScriptDefinition> = run {
override val definitions: Sequence<ScriptDefinition> = run {
discoverScriptTemplatesInClasspath(
classpath,
this::class.java.classLoader,
scriptResolverEnv,
hostConfiguration,
messageReporter
)
}
@@ -50,9 +50,9 @@ private const val MANIFEST_RESOURCE_NAME = "/META-INF/MANIFEST.MF"
fun discoverScriptTemplatesInClassLoader(
classLoader: ClassLoader,
scriptResolverEnv: Map<String, Any?>,
hostConfiguration: ScriptingHostConfiguration,
messageReporter: MessageReporter
): Sequence<KotlinScriptDefinition> {
): Sequence<ScriptDefinition> {
val classpath = classLoader.getResources(MANIFEST_RESOURCE_NAME).asSequence().mapNotNull {
try {
File(it.toURI()).takeIf(File::exists)
@@ -61,26 +61,26 @@ fun discoverScriptTemplatesInClassLoader(
}
}
val classpathWithLoader = SimpleClasspathWithClassLoader(classpath.toList(), classLoader)
return scriptTemplatesDiscoverySequence(classpathWithLoader, scriptResolverEnv, messageReporter)
return scriptTemplatesDiscoverySequence(classpathWithLoader, hostConfiguration, messageReporter)
}
fun discoverScriptTemplatesInClasspath(
classpath: List<File>,
baseClassLoader: ClassLoader?,
scriptResolverEnv: Map<String, Any?>,
hostConfiguration: ScriptingHostConfiguration,
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
val classpathWithLoader = LazyClasspathWithClassLoader(baseClassLoader) { classpath }
return scriptTemplatesDiscoverySequence(classpathWithLoader, scriptResolverEnv, messageReporter)
return scriptTemplatesDiscoverySequence(classpathWithLoader, hostConfiguration, messageReporter)
}
private fun scriptTemplatesDiscoverySequence(
classpathWithLoader: ClasspathWithClassLoader,
scriptResolverEnv: Map<String, Any?>,
hostConfiguration: ScriptingHostConfiguration,
messageReporter: MessageReporter
): Sequence<KotlinScriptDefinition> {
): Sequence<ScriptDefinition> {
return sequence {
// 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
@@ -102,7 +102,7 @@ private fun scriptTemplatesDiscoverySequence(
definitionNames.partitionLoadJarDefinitions(
jar,
classpathWithLoader,
scriptResolverEnv,
hostConfiguration,
messageReporter
)
if (notFoundClasses.isNotEmpty()) {
@@ -122,7 +122,7 @@ private fun scriptTemplatesDiscoverySequence(
val discoveryMarkers = File(dep, SCRIPT_DEFINITION_MARKERS_PATH).listFiles()
if (discoveryMarkers?.isEmpty() == false) {
val (foundDefinitionClasses, notFoundDefinitions) = discoveryMarkers.map { it.name }
.partitionLoadDirDefinitions(dep, classpathWithLoader, scriptResolverEnv, messageReporter)
.partitionLoadDirDefinitions(dep, classpathWithLoader, hostConfiguration, messageReporter)
foundDefinitionClasses.forEach {
yield(it)
}
@@ -145,7 +145,7 @@ private fun scriptTemplatesDiscoverySequence(
if (remainingDefinitionCandidates.isEmpty()) break
try {
val (foundDefinitionClasses, notFoundDefinitions) =
remainingDefinitionCandidates.partitionLoadDirDefinitions(dep, classpathWithLoader, scriptResolverEnv, messageReporter)
remainingDefinitionCandidates.partitionLoadDirDefinitions(dep, classpathWithLoader, hostConfiguration, messageReporter)
foundDefinitionClasses.forEach {
yield(it)
}
@@ -170,9 +170,9 @@ fun loadScriptTemplatesFromClasspath(
classpath: List<File>,
dependenciesClasspath: List<File>,
baseClassLoader: ClassLoader,
scriptResolverEnv: Map<String, Any?>,
hostConfiguration: ScriptingHostConfiguration,
messageReporter: MessageReporter
): Sequence<KotlinScriptDefinition> =
): Sequence<ScriptDefinition> =
if (scriptTemplates.isEmpty()) emptySequence()
else sequence {
// trying the direct classloading from baseClassloader first, since this is the most performant variant
@@ -180,7 +180,7 @@ fun loadScriptTemplatesFromClasspath(
loadScriptDefinition(
baseClassLoader,
it,
scriptResolverEnv,
hostConfiguration,
messageReporter
)
}
@@ -199,11 +199,11 @@ fun loadScriptTemplatesFromClasspath(
val (loadedDefinitions, notFoundTemplates) = when {
dep.isFile && dep.extension == "jar" -> { // checking for extension is the compiler current behaviour, so the same logic is implemented here
JarFile(dep).use { jar ->
remainingTemplates.partitionLoadJarDefinitions(jar, classpathWithLoader, scriptResolverEnv, messageReporter)
remainingTemplates.partitionLoadJarDefinitions(jar, classpathWithLoader, hostConfiguration, messageReporter)
}
}
dep.isDirectory -> {
remainingTemplates.partitionLoadDirDefinitions(dep, classpathWithLoader, scriptResolverEnv, messageReporter)
remainingTemplates.partitionLoadDirDefinitions(dep, classpathWithLoader, hostConfiguration, messageReporter)
}
else -> {
// 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(
val loaded: List<KotlinScriptDefinition>,
val loaded: List<ScriptDefinition>,
val notFound: List<String>
)
private inline fun List<String>.partitionLoadDefinitions(
classpathWithLoader: ClasspathWithClassLoader,
scriptResolverEnv: Map<String, Any?>,
hostConfiguration: ScriptingHostConfiguration,
noinline messageReporter: MessageReporter,
getBytes: (String) -> ByteArray?
): DefinitionsLoadPartitionResult {
val loaded = ArrayList<KotlinScriptDefinition>()
val loaded = ArrayList<ScriptDefinition>()
val notFound = ArrayList<String>()
for (definitionName in this) {
val classBytes = getBytes(definitionName)
@@ -256,7 +256,7 @@ private inline fun List<String>.partitionLoadDefinitions(
it,
definitionName,
classpathWithLoader,
scriptResolverEnv,
hostConfiguration,
messageReporter
)
}
@@ -273,18 +273,18 @@ private inline fun List<String>.partitionLoadDefinitions(
private fun List<String>.partitionLoadJarDefinitions(
jar: JarFile,
classpathWithLoader: ClasspathWithClassLoader,
scriptResolverEnv: Map<String, Any?>,
hostConfiguration: ScriptingHostConfiguration,
messageReporter: MessageReporter
): DefinitionsLoadPartitionResult = partitionLoadDefinitions(classpathWithLoader, scriptResolverEnv, messageReporter) { definitionName ->
): DefinitionsLoadPartitionResult = partitionLoadDefinitions(classpathWithLoader, hostConfiguration, messageReporter) { definitionName ->
jar.getJarEntry("${definitionName.replace('.', '/')}.class")?.let { jar.getInputStream(it).readBytes() }
}
private fun List<String>.partitionLoadDirDefinitions(
dir: File,
classpathWithLoader: ClasspathWithClassLoader,
scriptResolverEnv: Map<String, Any?>,
hostConfiguration: ScriptingHostConfiguration,
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()
}
@@ -292,14 +292,15 @@ private fun loadScriptDefinition(
templateClassBytes: ByteArray,
templateClassName: String,
classpathWithLoader: ClasspathWithClassLoader,
scriptResolverEnv: Map<String, Any?>,
hostConfiguration: ScriptingHostConfiguration,
messageReporter: MessageReporter
): KotlinScriptDefinition? {
): ScriptDefinition? {
val anns = loadAnnotationsFromClass(templateClassBytes)
for (ann in anns) {
var def: KotlinScriptDefinition? = null
var def: ScriptDefinition? = null
if (ann.name == KotlinScript::class.simpleName) {
def = LazyScriptDefinitionFromDiscoveredClass(
hostConfiguration,
anns,
templateClassName,
classpathWithLoader.classpath,
@@ -307,7 +308,14 @@ private fun loadScriptDefinition(
)
} else if (ann.name == ScriptTemplateDefinition::class.simpleName) {
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) {
messageReporter(
@@ -327,29 +335,20 @@ private fun loadScriptDefinition(
private fun loadScriptDefinition(
classLoader: ClassLoader,
template: String,
scriptResolverEnv: Map<String, Any?>,
hostConfiguration: ScriptingHostConfiguration,
messageReporter: MessageReporter
): KotlinScriptDefinition? {
): ScriptDefinition? {
try {
val cls = classLoader.loadClass(template)
val def =
if (cls.annotations.firstIsInstanceOrNull<KotlinScript>() != null) {
val environment = defaultJvmScriptingHostConfiguration
KotlinScriptDefinitionAdapterFromNewAPI(
createCompilationConfigurationFromTemplate(
KotlinType(cls.kotlin),
environment,
KotlinScriptDefinition::class
),
environment
)
ScriptDefinition.FromTemplate(hostConfiguration, cls.kotlin, ScriptDefinition::class)
} else {
KotlinScriptDefinitionFromAnnotatedTemplate(cls.kotlin, scriptResolverEnv)
ScriptDefinition.FromLegacyTemplate(hostConfiguration, cls.kotlin)
}
messageReporter(
CompilerMessageSeverity.INFO,
"Added script definition $template to configuration: name = ${def.name}, " +
"resolver = ${def.dependencyResolver.javaClass.name}"
"Added script definition $template to configuration: name = ${def.name}"
)
return def
} catch (ex: ClassNotFoundException) {
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.scripting.definitions
import com.intellij.ide.highlighter.JavaClassFileType
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.fileTypes.FileTypeRegistry
import com.intellij.openapi.project.Project
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.psi.KtFile
fun PsiFile.scriptDefinition(): KotlinScriptDefinition? {
fun PsiFile.findScriptDefinition(): ScriptDefinition? {
// Do not use psiFile.script, see comments in findScriptDefinition
if (this !is KtFile/* || this.script == null*/) return null
val file = virtualFile ?: originalFile.virtualFile ?: 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
// Do not use psiFile.script here because this method can be called during indexes access
// 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
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
?: 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 =
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
import java.io.File
import kotlin.script.experimental.api.valueOrNull
data class ScriptsCompilationDependencies(
val classpath: List<File>,
@@ -41,11 +42,11 @@ fun collectScriptsCompilationDependencies(
while (true) {
val newRemainingSources = ArrayList<KtFile>()
for (source in remainingSources) {
val dependencies = importsProvider.getScriptDependencies(source)
if (dependencies != null) {
collectedClassPath.addAll(dependencies.classpath)
val refinedConfiguration = importsProvider.getScriptConfigurationResult(source)?.valueOrNull()
if (refinedConfiguration != null) {
collectedClassPath.addAll(refinedConfiguration.dependenciesClassPath)
val sourceDependenciesRoots = dependencies.scripts.map {
val sourceDependenciesRoots = refinedConfiguration.importedScripts.map {
KotlinSourceRoot(it.path, false)
}
val sourceDependencies =
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.psi.KtImportInfo
import org.jetbrains.kotlin.resolve.ImportPath
import org.jetbrains.kotlin.resolve.extensions.ExtraImportsProviderExtension
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
import kotlin.script.experimental.api.valueOrNull
class ScriptExtraImportsProviderExtension : ExtraImportsProviderExtension {
@@ -28,8 +29,8 @@ class ScriptExtraImportsProviderExtension : ExtraImportsProviderExtension {
override fun getExtraImports(ktFile: KtFile): Collection<KtImportInfo> =
ktFile.takeIf { it.isScript() }?.let { file ->
val scriptDependencies = ScriptDependenciesProvider.getInstance(file.project)?.getScriptDependencies(file.originalFile)
scriptDependencies?.imports?.map {
val refinedConfiguration = ScriptDependenciesProvider.getInstance(file.project)?.getScriptConfigurationResult(file.originalFile)?.valueOrNull()
refinedConfiguration?.defaultImports?.map {
ScriptExtraImportImpl(
ImportPath.fromString(it)
)
@@ -25,8 +25,10 @@ import org.jetbrains.kotlin.parsing.KotlinParserDefinition
import org.jetbrains.kotlin.psi.KtFile
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.repl.messages.ConsoleDiagnosticMessageHolder
import kotlin.concurrent.write
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
const val KOTLIN_REPL_JVM_TARGET_PROPERTY = "kotlin.repl.jvm.target"
@@ -39,7 +41,7 @@ open class GenericReplChecker(
internal val environment = run {
compilerConfiguration.apply {
add(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition)
add(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, ScriptDefinition.FromLegacy(defaultJvmScriptingHostConfiguration, scriptDefinition))
put<MessageCollector>(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector)
put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true)
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
import java.io.File
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.write
import kotlin.script.experimental.api.valueOrNull
// WARNING: not thread safe, assuming external synchronization
@@ -64,7 +65,9 @@ open class GenericReplCompiler(
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
if (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.LexicalScopeKind
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.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.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.isUnit
@@ -44,6 +44,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import java.io.File
import kotlin.reflect.KClass
import kotlin.reflect.KType
import kotlin.script.experimental.api.valueOrNull
class LazyScriptDescriptor(
@@ -104,8 +105,9 @@ class LazyScriptDescriptor(
override fun getPriority() = priority
val scriptDefinition: () -> KotlinScriptDefinition = resolveSession.storageManager.createLazyValue {
scriptDefinitionByFileName(
// rewrite without using legacyDefinition below
val scriptDefinition: () -> ScriptDefinition = resolveSession.storageManager.createLazyValue {
findScriptDefinitionByFileName(
resolveSession.project,
scriptInfo.script.containingKtFile.name
)
@@ -128,7 +130,7 @@ class LazyScriptDescriptor(
override fun getUnsubstitutedPrimaryConstructor() = super.getUnsubstitutedPrimaryConstructor()!!
internal val baseClassDescriptor: () -> ClassDescriptor? = resolveSession.storageManager.createNullableLazyValue {
val template = scriptDefinition().template
val template = scriptDefinition().legacyDefinition.template
findTypeDescriptor(
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 importedScriptsFiles = ScriptDependenciesProvider.getInstance(scriptInfo.script.project)
?.getScriptDependencies(scriptInfo.script.containingKtFile)?.scripts
?.getScriptConfigurationResult(scriptInfo.script.containingKtFile)?.valueOrNull()?.importedScripts
if (importedScriptsFiles != null) {
val findImportedScriptDescriptor = ImportedScriptDescriptorsFinder()
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)
}
@@ -222,7 +224,7 @@ class LazyScriptDescriptor(
private val scriptOuterScope: () -> LexicalScope = resolveSession.storageManager.createLazyValue {
var outerScope = super.getOuterScope()
val outerScopeReceivers = implicitReceivers.let {
if (scriptDefinition().providedProperties.isEmpty()) {
if (scriptDefinition().legacyDefinition.providedProperties.isEmpty()) {
it
} else {
it + ScriptProvidedPropertiesDescriptor(this)
@@ -5,16 +5,10 @@
package org.jetbrains.kotlin.scripting.resolve
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.Project
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 java.io.File
import kotlin.reflect.KClass
import kotlin.script.dependencies.ScriptContents
import kotlin.script.experimental.dependencies.DependenciesResolver
import kotlin.script.experimental.dependencies.DependenciesResolver.ResolveResult.Failure
@@ -22,43 +16,20 @@ import kotlin.script.experimental.dependencies.ScriptDependencies
import kotlin.script.experimental.dependencies.ScriptReport
class ScriptContentLoader(private val project: Project) {
fun getScriptContents(scriptDefinition: KotlinScriptDefinition, file: VirtualFile)
= BasicScriptContents(
file,
getAnnotations = { loadAnnotations(scriptDefinition, file) })
private fun loadAnnotations(scriptDefinition: KotlinScriptDefinition, file: VirtualFile): List<Annotation> {
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})")
}
fun getScriptContents(scriptDefinition: KotlinScriptDefinition, file: VirtualFile) =
makeScriptContents(
file,
scriptDefinition,
project,
scriptDefinition.template::class.java.classLoader
)
class BasicScriptContents(virtualFile: VirtualFile, getAnnotations: () -> Iterable<Annotation>) : ScriptContents {
override val file: File = File(virtualFile.path)
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(
@@ -80,11 +51,11 @@ class ScriptContentLoader(private val project: Project) {
}
fun getEnvironment(scriptDef: KotlinScriptDefinition) =
(scriptDef as? KotlinScriptDefinitionFromAnnotatedTemplate)?.environment.orEmpty()
(scriptDef as? KotlinScriptDefinitionFromAnnotatedTemplate)?.environment.orEmpty()
}
fun ScriptDependencies.adjustByDefinition(
scriptDef: KotlinScriptDefinition
scriptDef: KotlinScriptDefinition
): ScriptDependencies {
val additionalClasspath = (scriptDef as? KotlinScriptDefinitionFromAnnotatedTemplate)?.templateClasspath ?: return this
if (additionalClasspath.isEmpty()) return this
@@ -42,7 +42,7 @@ class ScriptProvidedPropertiesDescriptor(script: LazyScriptDescriptor) :
override fun getUnsubstitutedMemberScope(): MemberScope = memberScope()
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 {
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)
if (scriptFile.isDirectory || !scriptDefinitionProvider.isScript(scriptFile.name)) {
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 ""
messageCollector.report(ERROR, "Specify path to the script file$extensionHint as the first argument")
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.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.configuration.configureScriptDefinitions
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionsFromClasspathDiscoverySource
import org.jetbrains.kotlin.scripting.definitions.StandardScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.reporter
import org.jetbrains.kotlin.scripting.definitions.*
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) {
@@ -39,7 +40,11 @@ class ScriptingCompilerConfigurationExtension(val project: MockProject) : Compil
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)
@@ -49,16 +54,16 @@ class ScriptingCompilerConfigurationExtension(val project: MockProject) : Compil
configuration,
this::class.java.classLoader,
messageCollector,
scriptResolverEnv
hostConfiguration
)
}
// If not disabled explicitly, we should always support at least the 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(
ScriptingConfigurationKeys.SCRIPT_DEFINITIONS,
StandardScriptDefinition
ScriptDefinition.getDefault(hostConfiguration)
)
}
@@ -66,37 +71,26 @@ class ScriptingCompilerConfigurationExtension(val project: MockProject) : Compil
ScriptingConfigurationKeys.SCRIPT_DEFINITIONS_SOURCES,
ScriptDefinitionsFromClasspathDiscoverySource(
configuration.jvmClasspathRoots,
configuration.get(ScriptingConfigurationKeys.LEGACY_SCRIPT_RESOLVER_ENVIRONMENT_OPTION) ?: emptyMap(),
hostConfiguration,
messageCollector.reporter
)
)
}
// If not disabled explicitly, we should always support at least the standard script definition
if (!configuration.getBoolean(JVMConfigurationKeys.DISABLE_STANDARD_SCRIPT_DEFINITION) &&
StandardScriptDefinition !in configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS)
) {
configuration.add(
ScriptingConfigurationKeys.SCRIPT_DEFINITIONS,
StandardScriptDefinition
)
}
val scriptDefinitionProvider = ScriptDefinitionProvider.getInstance(project) as? CliScriptDefinitionProvider
if (scriptDefinitionProvider != null) {
scriptDefinitionProvider.setScriptDefinitionsSources(configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS_SOURCES))
scriptDefinitionProvider.setScriptDefinitions(configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS))
val scriptDefinitionProvider = ScriptDefinitionProvider.getInstance(project) as? CliScriptDefinitionProvider
if (scriptDefinitionProvider != null) {
scriptDefinitionProvider.setScriptDefinitionsSources(configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS_SOURCES))
scriptDefinitionProvider.setScriptDefinitions(configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS))
// Register new file extensions
val fileTypeRegistry = FileTypeRegistry.getInstance() as CoreFileTypeRegistry
// Register new file extensions
val fileTypeRegistry = FileTypeRegistry.getInstance() as CoreFileTypeRegistry
scriptDefinitionProvider.getKnownFilenameExtensions().filter {
fileTypeRegistry.getFileTypeByExtension(it) != KotlinFileType.INSTANCE
}.forEach {
fileTypeRegistry.registerFileType(KotlinFileType.INSTANCE, it)
scriptDefinitionProvider.getKnownFilenameExtensions().filter {
fileTypeRegistry.getFileTypeByExtension(it) != KotlinFileType.INSTANCE
}.forEach {
fileTypeRegistry.registerFileType(KotlinFileType.INSTANCE, it)
}
}
}
}
}
@@ -5,33 +5,29 @@
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.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionsSource
import org.jetbrains.kotlin.scripting.definitions.StandardScriptDefinition
import kotlin.concurrent.write
open class CliScriptDefinitionProvider : LazyScriptDefinitionProvider() {
private val definitionsFromSources: MutableList<Sequence<KotlinScriptDefinition>> = arrayListOf()
private val definitions: MutableList<KotlinScriptDefinition> = arrayListOf()
private var hasStandardDefinition = true
private val definitionsFromSources: MutableList<Sequence<ScriptDefinition>> = arrayListOf()
private val definitions: MutableList<ScriptDefinition> = arrayListOf()
private var defaultDefinition: ScriptDefinition? = null
override val currentDefinitions: Sequence<KotlinScriptDefinition>
override val currentDefinitions: Sequence<ScriptDefinition>
get() {
val base = definitions.asSequence() + definitionsFromSources.asSequence().flatMap { it }
return if (hasStandardDefinition) base + getDefaultScriptDefinition() else base
return base + getDefaultDefinition()
}
override fun getDefaultScriptDefinition(): KotlinScriptDefinition {
return StandardScriptDefinition
}
fun setScriptDefinitions(newDefinitions: List<KotlinScriptDefinition>) {
fun setScriptDefinitions(newDefinitions: List<ScriptDefinition>) {
lock.write {
definitions.clear()
val (withoutStdDef, stdDef) = newDefinitions.partition { it != getDefaultScriptDefinition() }
val (withoutStdDef, stdDef) = newDefinitions.partition { !it.isDefault }
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 org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
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.adjustByDefinition
import org.jetbrains.kotlin.scripting.resolve.VirtualFileScriptSource
import org.jetbrains.kotlin.scripting.resolve.refineScriptCompilationConfiguration
import java.io.File
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
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 {
private val cacheLock = ReentrantReadWriteLock()
private val cache = hashMapOf<String, ScriptDependencies?>()
private val scriptContentLoader = ScriptContentLoader(project)
private val cache = hashMapOf<String, ScriptCompilationConfigurationResult?>()
override fun getScriptDependencies(file: VirtualFile): ScriptDependencies? = cacheLock.read {
calculateExternalDependencies(file)
override fun getScriptConfigurationResult(file: VirtualFile): ScriptCompilationConfigurationResult? = cacheLock.read {
calculateRefinedConfiguration(file)
}
private fun calculateExternalDependencies(file: VirtualFile): ScriptDependencies? {
private fun calculateRefinedConfiguration(file: VirtualFile): ScriptCompilationConfigurationResult? {
val path = file.path
val cached = cache[path]
return if (cached != null) cached
else {
val scriptDef = file.findScriptDefinition(project)
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 (deps != null) {
log.info("[kts] new cached deps for $path: ${deps.classpath.joinToString(File.pathSeparator)}")
if (result is ResultWithDiagnostics.Success) {
log.info("[kts] new cached deps for $path: ${result.value.dependenciesClassPath.joinToString(File.pathSeparator)}")
} else {
log.info("[kts] new cached errors for $path:\n ${result.reports.joinToString("\n ") { it.message + if (it.exception == null) "" else ": ${it.exception}" }}")
}
cacheLock.write {
cache.put(path, deps)
cache.put(path, result)
}
deps
result
} 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.CliScriptDependenciesProvider
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.ScriptDependenciesProvider
import org.jetbrains.kotlin.scripting.extensions.*
@@ -39,8 +40,9 @@ private fun <T> ProjectExtensionDescriptor<T>.registerExtensionIfRequired(projec
class ScriptingCompilerConfigurationComponentRegistrar : ComponentRegistrar {
override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
val hostConfiguration = makeHostConfiguration(project, configuration)
withClassloadingProblemsReporting(messageCollector) {
CompilerConfigurationExtension.registerExtension(project, ScriptingCompilerConfigurationExtension(project))
CompilerConfigurationExtension.registerExtension(project, ScriptingCompilerConfigurationExtension(project, hostConfiguration))
CollectAdditionalSourcesExtension.registerExtension(project, ScriptingCollectAdditionalSourcesExtension(project))
ScriptEvaluationExtension.registerExtensionIfRequired(project, JvmCliScriptEvaluationExtension())
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.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionsSource
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
import org.junit.Assert
import org.junit.Test
import java.util.concurrent.atomic.AtomicInteger
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.templates.standard.ScriptTemplateWithArgs
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()
override fun isScript(fileName: String): Boolean = fileName.endsWith(suffix).also {
if (it) matchCounter.incrementAndGet()
}
override val isDefault: Boolean
get() = suffix == ".kts"
}
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) {
counter.incrementAndGet()
yield(gen())
@@ -88,6 +95,6 @@ private class TestScriptDefinitionSource(val counter: AtomicInteger, val defGens
}
}
private class TestCliScriptDefinitionProvider(private val standardDef: KotlinScriptDefinition) : CliScriptDefinitionProvider() {
override fun getDefaultScriptDefinition(): KotlinScriptDefinition = standardDef
private class TestCliScriptDefinitionProvider(private val standardDef: ScriptDefinition) : CliScriptDefinitionProvider() {
override fun getDefaultScriptDefinition(): KotlinScriptDefinition = standardDef.legacyDefinition
}
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.utils.KotlinPaths
import org.jetbrains.kotlin.utils.PathUtil
import org.junit.Assert
import java.io.File
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
class ScriptingCompilerPluginTest : TestCaseWithTmpdir() {
@@ -116,7 +117,7 @@ class ScriptingCompilerPluginTest : TestCaseWithTmpdir() {
loadScriptTemplatesFromClasspath(
listOf("TestScriptWithReceivers", "TestScriptWithSimpleEnvVars"),
listOf(defsOut), emptyList(), this::class.java.classLoader, emptyMap(), messageCollector.reporter
listOf(defsOut), emptyList(), this::class.java.classLoader, defaultJvmScriptingHostConfiguration, messageCollector.reporter
).toList()
for (def in defClasses) {
@@ -141,7 +142,7 @@ class ScriptingCompilerPluginTest : TestCaseWithTmpdir() {
discoverScriptTemplatesInClasspath(
listOf(defsOut),
this::class.java.classLoader,
emptyMap(),
defaultJvmScriptingHostConfiguration,
messageCollector.reporter
)
@@ -212,7 +213,7 @@ class ScriptingCompilerPluginTest : TestCaseWithTmpdir() {
discoverScriptTemplatesInClasspath(
listOf(defsOut),
this::class.java.classLoader,
emptyMap(),
defaultJvmScriptingHostConfiguration,
messageCollector.reporter
).toList()
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.daemon.common.*
import org.jetbrains.kotlin.integration.KotlinIntegrationTestBase.getKotlinPaths
import org.jetbrains.kotlin.script.loadScriptingPlugin
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.test.ConfigurationKind
import org.jetbrains.kotlin.test.KotlinTestUtils
@@ -46,6 +47,7 @@ import java.lang.management.ManagementFactory
import java.net.URLClassLoader
import java.nio.charset.Charset
import java.util.concurrent.TimeUnit
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
class SourceSectionsTest : TestCaseWithTmpdir() {
@@ -73,7 +75,7 @@ class SourceSectionsTest : TestCaseWithTmpdir() {
CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY,
PrintingMessageCollector(System.err, MessageRenderer.PLAIN_FULL_PATHS, false)
)
configuration.add(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, StandardScriptDefinition)
configuration.add(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, ScriptDefinition.getDefault(defaultJvmScriptingHostConfiguration))
if (withSourceSectionsPlugin) {
configuration.addAll(SourceSectionsConfigurationKeys.SECTIONS_OPTION, TEST_ALLOWED_SECTIONS)
configuration.add(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, SourceSectionsComponentRegistrar())