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:
+2
-2
@@ -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")
|
||||
|
||||
+9
-2
@@ -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
|
||||
}
|
||||
+2
@@ -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"
|
||||
|
||||
+3
-9
@@ -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]
|
||||
|
||||
+8
-2
@@ -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 {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+26
-11
@@ -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()
|
||||
|
||||
+16
-5
@@ -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}")
|
||||
|
||||
+90
@@ -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?>()
|
||||
+164
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -7,5 +7,5 @@ package org.jetbrains.kotlin.scripting.definitions
|
||||
|
||||
interface ScriptDefinitionsSource {
|
||||
|
||||
val definitions: Sequence<KotlinScriptDefinition>
|
||||
val definitions: Sequence<ScriptDefinition>
|
||||
}
|
||||
+15
-2
@@ -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? =
|
||||
|
||||
+19
@@ -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)
|
||||
}
|
||||
)
|
||||
+46
-47
@@ -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) {
|
||||
|
||||
+9
-7
@@ -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 =
|
||||
|
||||
+5
-4
@@ -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 =
|
||||
|
||||
+3
-2
@@ -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)
|
||||
)
|
||||
|
||||
+3
-1
@@ -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)
|
||||
|
||||
|
||||
+4
-1
@@ -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
|
||||
|
||||
+10
-8
@@ -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)
|
||||
|
||||
+12
-41
@@ -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
|
||||
|
||||
+1
-1
@@ -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
|
||||
}
|
||||
|
||||
+338
@@ -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
|
||||
))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user