Implement quick non-script detection and lazy script discovery..

so ".kt" and ".java" files are not considered as scripts and quickly
filtered out, and for the other files the the checks are implemented
using sequences, mechanisms provided to supply script definitions
lazily, and script discovery is implemented using this mechanisms.
This commit is contained in:
Ilya Chernikov
2018-04-18 17:23:39 +02:00
parent 969340167f
commit 15f9ad9531
12 changed files with 322 additions and 128 deletions
@@ -16,24 +16,15 @@
package org.jetbrains.kotlin.cli.common.script
import org.jetbrains.kotlin.script.KotlinScriptDefinition
import org.jetbrains.kotlin.script.ScriptDefinitionProvider
import org.jetbrains.kotlin.script.StandardScriptDefinition
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
import org.jetbrains.kotlin.script.*
import kotlin.concurrent.write
class CliScriptDefinitionProvider : ScriptDefinitionProvider {
class CliScriptDefinitionProvider : LazyScriptDefinitionProvider() {
private val definitionsFromSources: MutableList<Sequence<KotlinScriptDefinition>> = arrayListOf()
private val definitions: MutableList<KotlinScriptDefinition> = arrayListOf(StandardScriptDefinition)
private val lock = ReentrantReadWriteLock()
override fun findScriptDefinition(fileName: String) = lock.read {
definitions.firstOrNull { it.isScript(fileName) }
}
override fun isScript(fileName: String) = lock.read {
definitions.any { it.isScript(fileName) }
}
override val currentDefinitions: Sequence<KotlinScriptDefinition> =
definitionsFromSources.asSequence().flatMap { it } + definitions.asSequence()
fun setScriptDefinitions(newDefinitions: List<KotlinScriptDefinition>) {
lock.write {
@@ -41,4 +32,13 @@ class CliScriptDefinitionProvider : ScriptDefinitionProvider {
definitions.addAll(newDefinitions)
}
}
fun setScriptDefinitionsSources(newSources: List<ScriptDefinitionsSource>) {
lock.write {
definitionsFromSources.clear()
for (it in newSources) {
definitionsFromSources.add(it.definitions.constrainOnce())
}
}
}
}
@@ -19,10 +19,8 @@ package org.jetbrains.kotlin.cli.common.script
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.script.ScriptContentLoader
import org.jetbrains.kotlin.script.ScriptDefinitionProvider
import org.jetbrains.kotlin.script.ScriptDependenciesProvider
import org.jetbrains.kotlin.script.adjustByDefinition
import org.jetbrains.kotlin.script.findScriptDefinition
import org.jetbrains.kotlin.script.*
import java.io.File
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
@@ -209,6 +209,9 @@ class KotlinCoreEnvironment private constructor(
val scriptDefinitionProvider = ScriptDefinitionProvider.getInstance(project) as? CliScriptDefinitionProvider
if (scriptDefinitionProvider != null) {
scriptDefinitionProvider.setScriptDefinitionsSources(
configuration.getList(JVMConfigurationKeys.SCRIPT_DEFINITIONS_SOURCES)
)
scriptDefinitionProvider.setScriptDefinitions(
configuration.getList(JVMConfigurationKeys.SCRIPT_DEFINITIONS))
@@ -210,6 +210,9 @@ class KotlinCoreEnvironment private constructor(
val scriptDefinitionProvider = ScriptDefinitionProvider.getInstance(project) as? CliScriptDefinitionProvider
if (scriptDefinitionProvider != null) {
scriptDefinitionProvider.setScriptDefinitionsSources(
configuration.getList(JVMConfigurationKeys.SCRIPT_DEFINITIONS_SOURCES)
)
scriptDefinitionProvider.setScriptDefinitions(
configuration.getList(JVMConfigurationKeys.SCRIPT_DEFINITIONS))
@@ -212,6 +212,9 @@ class KotlinCoreEnvironment private constructor(
val scriptDefinitionProvider = ScriptDefinitionProvider.getInstance(project) as? CliScriptDefinitionProvider
if (scriptDefinitionProvider != null) {
scriptDefinitionProvider.setScriptDefinitionsSources(
configuration.getList(JVMConfigurationKeys.SCRIPT_DEFINITIONS_SOURCES)
)
scriptDefinitionProvider.setScriptDefinitions(
configuration.getList(JVMConfigurationKeys.SCRIPT_DEFINITIONS))
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.load.java.JavaClassesTracker;
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents;
import org.jetbrains.kotlin.modules.Module;
import org.jetbrains.kotlin.script.KotlinScriptDefinition;
import org.jetbrains.kotlin.script.ScriptDefinitionsSource;
import java.io.File;
import java.util.List;
@@ -49,6 +50,9 @@ public class JVMConfigurationKeys {
public static final CompilerConfigurationKey<List<KotlinScriptDefinition>> SCRIPT_DEFINITIONS =
CompilerConfigurationKey.create("script definitions");
public static final CompilerConfigurationKey<List<ScriptDefinitionsSource>> SCRIPT_DEFINITIONS_SOURCES =
CompilerConfigurationKey.create("script definitions sources");
public static final CompilerConfigurationKey<Boolean> DISABLE_STANDARD_SCRIPT_DEFINITION =
CompilerConfigurationKey.create("Disable standard kotlin script support");
@@ -20,11 +20,13 @@ import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiFile
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
import kotlin.concurrent.write
interface ScriptDefinitionProvider {
fun findScriptDefinition(fileName: String): KotlinScriptDefinition?
fun isScript(fileName: String): Boolean
fun findScriptDefinition(file: VirtualFile): KotlinScriptDefinition? = findScriptDefinition(file.name)
companion object {
fun getInstance(project: Project): ScriptDefinitionProvider =
@@ -32,8 +34,79 @@ interface ScriptDefinitionProvider {
}
}
fun ScriptDefinitionProvider.findScriptDefinition(file: VirtualFile): KotlinScriptDefinition? = findScriptDefinition(file.name)
fun getScriptDefinition(file: VirtualFile, project: Project): KotlinScriptDefinition? =
ScriptDefinitionProvider.getInstance(project).findScriptDefinition(file)
fun getScriptDefinition(psiFile: PsiFile): KotlinScriptDefinition? =
ScriptDefinitionProvider.getInstance(psiFile.project).findScriptDefinition(psiFile.name)
abstract class LazyScriptDefinitionProvider : ScriptDefinitionProvider {
protected val lock = ReentrantReadWriteLock()
protected abstract val currentDefinitions: Sequence<KotlinScriptDefinition>
private var _cachedDefinitions: Sequence<KotlinScriptDefinition>? = null
private val cachedDefinitions: Sequence<KotlinScriptDefinition>
get() {
// assuming it is always called under read lock
assert(lock.readLockCount > 0)
if (_cachedDefinitions == null) lock.write {
_cachedDefinitions = CashingSequence(currentDefinitions.constrainOnce())
}
return _cachedDefinitions!!
}
protected fun clearCache() {
lock.write {
_cachedDefinitions = null
}
}
protected open fun nonScriptFileName(fileName: String) = nonScriptFilenameSuffixes.any {
fileName.endsWith( it, ignoreCase = true)
}
override fun findScriptDefinition(fileName: String): KotlinScriptDefinition? =
if (nonScriptFileName(fileName)) null
else lock.read {
cachedDefinitions.firstOrNull { it.isScript(fileName) }
}
override fun isScript(fileName: String) =
if (nonScriptFileName(fileName)) false
else lock.read {
cachedDefinitions.any { it.isScript(fileName) }
}
companion object {
// TODO: find a common place for storing kotlin-related extensions and reuse values from it everywhere
protected val nonScriptFilenameSuffixes = arrayOf(".kt", ".java")
}
}
private class CashingSequence<T>(from: Sequence<T>) : Sequence<T> {
private val lock = ReentrantReadWriteLock()
private val sequenceIterator = from.iterator()
private val cache = arrayListOf<T>()
private inner class CashingIterator : Iterator<T> {
private val cacheIterator: Iterator<T> = cache.iterator()
private var cacheRunOut = !cacheIterator.hasNext()
private fun cacheHasNext() = !cacheRunOut && (cacheIterator.hasNext().also { if (!it) cacheRunOut = true })
override fun hasNext(): Boolean = lock.read { cacheHasNext() || sequenceIterator.hasNext() }
override fun next(): T = lock.read {
if (cacheHasNext()) cacheIterator.next()
else sequenceIterator.next().also { lock.write { cache.add(it) } }
}
}
override fun iterator(): Iterator<T> = CashingIterator()
}
@@ -0,0 +1,11 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.script
interface ScriptDefinitionsSource {
val definitions: Sequence<KotlinScriptDefinition>
}
@@ -0,0 +1,80 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.scripts
import org.jetbrains.kotlin.cli.common.script.CliScriptDefinitionProvider
import org.jetbrains.kotlin.script.KotlinScriptDefinition
import org.jetbrains.kotlin.script.ScriptDefinitionsSource
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
import org.junit.Assert
import org.junit.Test
import java.util.concurrent.atomic.AtomicInteger
import kotlin.coroutines.experimental.buildSequence
import kotlin.script.templates.standard.ScriptTemplateWithArgs
class ScriptProviderTest : KtUsefulTestCase() {
@Test
fun testLazyScriptDefinitionsProvider() {
val genDefCounter = AtomicInteger()
val standardDef = FakeScriptDefinition()
val shadedDef = FakeScriptDefinition(".x.kts")
val provider = CliScriptDefinitionProvider().apply {
setScriptDefinitions(listOf(shadedDef, standardDef))
setScriptDefinitionsSources(listOf(TestScriptDefinitionSource(genDefCounter, ".y.kts", ".x.kts")))
}
Assert.assertEquals(0, genDefCounter.get())
provider.isScript("a.kt").let {
Assert.assertFalse(it)
Assert.assertEquals(0, genDefCounter.get())
}
provider.isScript("a.y.kts").let {
Assert.assertTrue(it)
Assert.assertEquals(1, genDefCounter.get())
}
provider.isScript("a.x.kts").let {
Assert.assertTrue(it)
Assert.assertEquals(2, genDefCounter.get())
Assert.assertEquals(0, shadedDef.matchCounter.get())
}
provider.isScript("a.z.kts").let {
Assert.assertTrue(it)
Assert.assertEquals(2, genDefCounter.get())
Assert.assertEquals(1, standardDef.matchCounter.get())
}
provider.isScript("a.ktx").let {
Assert.assertFalse(it)
Assert.assertEquals(2, genDefCounter.get())
}
}
}
private class FakeScriptDefinition(val suffix: String = ".kts") : KotlinScriptDefinition(ScriptTemplateWithArgs::class) {
val matchCounter = AtomicInteger()
override fun isScript(fileName: String): Boolean = fileName.endsWith(suffix).also {
if (it) matchCounter.incrementAndGet()
}
}
private class TestScriptDefinitionSource(val counter: AtomicInteger, val defGens: Iterable<() -> FakeScriptDefinition>) :
ScriptDefinitionsSource
{
constructor(counter: AtomicInteger, vararg suffixes: String) : this(counter, suffixes.map { { FakeScriptDefinition(it) } })
override val definitions: Sequence<KotlinScriptDefinition> = buildSequence {
for (gen in defGens) {
counter.incrementAndGet()
yield(gen())
}
}
}
@@ -29,10 +29,7 @@ import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.idea.caches.project.SdkInfo
import org.jetbrains.kotlin.idea.caches.project.getScriptRelatedModuleInfo
import org.jetbrains.kotlin.script.KotlinScriptDefinition
import org.jetbrains.kotlin.script.KotlinScriptDefinitionFromAnnotatedTemplate
import org.jetbrains.kotlin.script.ScriptDefinitionProvider
import org.jetbrains.kotlin.script.ScriptTemplatesProvider
import org.jetbrains.kotlin.script.*
import org.jetbrains.kotlin.scripting.compiler.plugin.KotlinScriptDefinitionAdapterFromNewAPI
import org.jetbrains.kotlin.utils.PathUtil
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
@@ -40,8 +37,6 @@ import org.jetbrains.kotlin.utils.addToStdlib.flattenTo
import java.io.File
import java.lang.reflect.InvocationTargetException
import java.net.URLClassLoader
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
import kotlin.concurrent.write
import kotlin.script.dependencies.Environment
import kotlin.script.dependencies.ScriptContents
@@ -53,14 +48,12 @@ import kotlin.script.experimental.dependencies.ScriptDependencies
import kotlin.script.experimental.dependencies.asSuccess
import kotlin.script.templates.standard.ScriptTemplateWithArgs
class ScriptDefinitionsManager(private val project: Project) : ScriptDefinitionProvider {
private val lock = ReentrantReadWriteLock()
class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinitionProvider() {
private var definitionsByContributor = mutableMapOf<ScriptDefinitionContributor, List<KotlinScriptDefinition>>()
private var definitions: List<KotlinScriptDefinition> = emptyList()
private var definitions: Sequence<KotlinScriptDefinition>? = null
fun reloadDefinitionsBy(contributor: ScriptDefinitionContributor) = lock.write {
val notLoadedYet = definitions.isEmpty()
if (notLoadedYet) return
if (definitions == null) return // not loaded yet
if (contributor !in definitionsByContributor) error("Unknown contributor: ${contributor.id}")
@@ -70,32 +63,19 @@ class ScriptDefinitionsManager(private val project: Project) : ScriptDefinitionP
}
fun getDefinitionsBy(contributor: ScriptDefinitionContributor): List<KotlinScriptDefinition> = lock.write {
val notLoadedYet = definitions.isEmpty()
if (notLoadedYet) return emptyList()
if (definitions == null) return emptyList() // not loaded yet
if (contributor !in definitionsByContributor) error("Unknown contributor: ${contributor.id}")
return definitionsByContributor[contributor] ?: emptyList()
}
private fun currentDefinitions(): List<KotlinScriptDefinition> {
val hasDefinitions = definitions.isNotEmpty()
when {
hasDefinitions -> return definitions
else -> {
override val currentDefinitions: Sequence<KotlinScriptDefinition>
get() =
definitions ?: kotlin.run {
reloadScriptDefinitions()
return definitions
definitions!!
}
}
}
override fun findScriptDefinition(fileName: String): KotlinScriptDefinition? = lock.read {
currentDefinitions().firstOrNull { it.isScript(fileName) }
}
override fun isScript(fileName: String) = lock.read {
currentDefinitions().any { it.isScript(fileName) }
}
private fun getContributors(): List<ScriptDefinitionContributor> {
@Suppress("DEPRECATION")
@@ -115,7 +95,8 @@ class ScriptDefinitionsManager(private val project: Project) : ScriptDefinitionP
}
private fun updateDefinitions() {
definitions = definitionsByContributor.values.flattenTo(mutableListOf())
definitions = definitionsByContributor.values.flattenTo(mutableListOf()).asSequence()
clearCache()
// TODO: clear by script type/definition
ServiceManager.getService(project, ScriptDependenciesUpdater::class.java).clear()
}
@@ -25,6 +25,7 @@ import com.intellij.util.io.URLUtil
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.script.ScriptDefinitionProvider
import org.jetbrains.kotlin.script.ScriptDependenciesProvider
import org.jetbrains.kotlin.script.findScriptDefinition
import java.io.File
import kotlin.script.experimental.dependencies.ScriptDependencies
@@ -14,13 +14,17 @@ import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.extensions.CompilerConfigurationExtension
import org.jetbrains.kotlin.script.KotlinScriptDefinition
import org.jetbrains.kotlin.script.KotlinScriptDefinitionFromAnnotatedTemplate
import org.jetbrains.kotlin.script.ScriptDefinitionsSource
import org.jetbrains.kotlin.script.StandardScriptDefinition
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import java.io.File
import java.io.IOException
import java.net.URLClassLoader
import java.util.jar.JarFile
import kotlin.coroutines.experimental.buildSequence
import kotlin.script.experimental.annotations.KotlinScript
import kotlin.script.experimental.api.ScriptingEnvironment
import kotlin.script.experimental.api.ScriptingEnvironmentProperties
import kotlin.script.experimental.definitions.ScriptDefinitionFromAnnotatedBaseClass
@@ -32,28 +36,24 @@ class ScriptingCompilerConfigurationExtension(val project: MockProject) : Compil
if (!configuration.getBoolean(ScriptingConfigurationKeys.DISABLE_SCRIPTING_PLUGIN_OPTION)) {
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY) ?: MessageCollector.NONE
val projectRoot = project.run { basePath ?: baseDir?.canonicalPath }?.let(::File)
if (projectRoot != null) {
configuration.put(
ScriptingConfigurationKeys.LEGACY_SCRIPT_RESOLVER_ENVIRONMENT_OPTION,
"projectRoot",
projectRoot
)
}
val scriptResolverEnv = configuration.getMap(ScriptingConfigurationKeys.LEGACY_SCRIPT_RESOLVER_ENVIRONMENT_OPTION)
val explicitScriptDefinitions = configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS)
val scriptDefinitions =
if (configuration.getBoolean(ScriptingConfigurationKeys.DISABLE_SCRIPT_DEFINITIONS_FROM_CLASSPATH_OPTION))
explicitScriptDefinitions
else
explicitScriptDefinitions + discoverScriptTemplatesInClasspath(configuration, messageCollector)
if (scriptDefinitions.isNotEmpty()) {
val projectRoot = project.run { basePath ?: baseDir?.canonicalPath }?.let(::File)
if (projectRoot != null) {
configuration.put(
ScriptingConfigurationKeys.LEGACY_SCRIPT_RESOLVER_ENVIRONMENT_OPTION,
"projectRoot",
projectRoot
)
}
if (explicitScriptDefinitions.isNotEmpty()) {
configureScriptDefinitions(
scriptDefinitions,
explicitScriptDefinitions,
configuration,
messageCollector,
configuration.getMap(ScriptingConfigurationKeys.LEGACY_SCRIPT_RESOLVER_ENVIRONMENT_OPTION)
scriptResolverEnv
)
}
// If not disabled explicitly, we should always support at least the standard script definition
@@ -62,6 +62,15 @@ class ScriptingCompilerConfigurationExtension(val project: MockProject) : Compil
) {
configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, StandardScriptDefinition)
}
configuration.add(
JVMConfigurationKeys.SCRIPT_DEFINITIONS_SOURCES,
ScriptDefinitionsFromClasspathDiscoverySource(
configuration,
scriptResolverEnv,
messageCollector
)
)
}
}
}
@@ -72,54 +81,71 @@ class ScriptingCompilerConfigurationComponentRegistrar : ComponentRegistrar {
}
}
private fun discoverScriptTemplatesInClasspath(configuration: CompilerConfiguration, messageCollector: MessageCollector): Iterable<String> {
val templates = arrayListOf<String>()
class ScriptDefinitionsFromClasspathDiscoverySource(
private val configuration: CompilerConfiguration,
private val scriptResolverEnv: Map<String, Any?>,
private val messageCollector: MessageCollector
) : ScriptDefinitionsSource {
override val definitions: Sequence<KotlinScriptDefinition> = run {
val classpath = configuration.jvmClasspathRoots
// TODO: consider using escaping to allow kotlin escaped names in class names
val classloader =
URLClassLoader(classpath.map { it.toURI().toURL() }.toTypedArray(), Thread.currentThread().contextClassLoader)
discoverScriptTemplatesInClasspath(configuration, messageCollector).mapNotNull {
loadScriptDefinition(classloader, it, scriptResolverEnv, messageCollector)
}
}
}
private fun discoverScriptTemplatesInClasspath(configuration: CompilerConfiguration, messageCollector: MessageCollector): Sequence<String> {
val templatesPath = "META-INF/kotlin/script/templates/"
for (dep in configuration.jvmClasspathRoots) {
when {
dep.isFile -> {
// this is the compiler behaviour, so the same logic implemented here
if (dep.extension == "jar") {
try {
with(JarFile(dep)) {
for (template in entries()) {
if (!template.isDirectory && template.name.startsWith(templatesPath)) {
val templateClassName = template.name.removePrefix(templatesPath)
templates.add(templateClassName)
messageCollector.report(
CompilerMessageSeverity.LOGGING,
"Configure scripting: Added template $templateClassName from $dep"
)
return buildSequence {
for (dep in configuration.jvmClasspathRoots) {
when {
dep.isFile -> {
// this is the compiler behaviour, so the same logic implemented here
if (dep.extension == "jar") {
try {
with(JarFile(dep)) {
for (template in entries()) {
if (!template.isDirectory && template.name.startsWith(templatesPath)) {
val templateClassName = template.name.removePrefix(templatesPath)
yield(templateClassName)
messageCollector.report(
CompilerMessageSeverity.LOGGING,
"Configure scripting: Added template $templateClassName from $dep"
)
}
}
}
} catch (e: IOException) {
messageCollector.report(
CompilerMessageSeverity.WARNING,
"Configure scripting: unable to process classpath entry $dep: $e"
)
}
} catch (e: IOException) {
messageCollector.report(
CompilerMessageSeverity.WARNING,
"Configure scripting: unable to process classpath entry $dep: $e"
)
}
}
}
dep.isDirectory -> {
val dir = File(dep, templatesPath)
if (dir.isDirectory) {
dir.listFiles().forEach {
templates.add(it.name)
messageCollector.report(
CompilerMessageSeverity.LOGGING,
"Configure scripting: Added template ${it.name} from $dep"
)
dep.isDirectory -> {
val dir = File(dep, templatesPath)
if (dir.isDirectory) {
dir.listFiles().forEach {
yield(it.name)
messageCollector.report(
CompilerMessageSeverity.LOGGING,
"Configure scripting: Added template ${it.name} from $dep"
)
}
}
}
}
else -> {
// assuming that invalid classpath entries will be reported elsewhere anyway, so do not spam user with additional warnings here
messageCollector.report(CompilerMessageSeverity.LOGGING, "Configure scripting: Unknown classpath entry $dep")
else -> {
// assuming that invalid classpath entries will be reported elsewhere anyway, so do not spam user with additional warnings here
messageCollector.report(CompilerMessageSeverity.LOGGING, "Configure scripting: Unknown classpath entry $dep")
}
}
}
}
return templates
}
fun configureScriptDefinitions(
@@ -135,32 +161,10 @@ fun configureScriptDefinitions(
URLClassLoader(classpath.map { it.toURI().toURL() }.toTypedArray(), Thread.currentThread().contextClassLoader)
var hasErrors = false
for (template in scriptTemplates) {
try {
val cls = classloader.loadClass(template)
val def =
if (cls.annotations.firstIsInstanceOrNull<kotlin.script.experimental.annotations.KotlinScript>() != null) {
KotlinScriptDefinitionAdapterFromNewAPI(
ScriptDefinitionFromAnnotatedBaseClass(ScriptingEnvironment(ScriptingEnvironmentProperties.baseClass to cls.kotlin))
)
} else {
KotlinScriptDefinitionFromAnnotatedTemplate(cls.kotlin, scriptResolverEnv)
}
val def = loadScriptDefinition(classloader, template, scriptResolverEnv, messageCollector)
if (!hasErrors && def == null) hasErrors = true
if (def != null) {
configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, def)
messageCollector.report(
CompilerMessageSeverity.INFO,
"Added script definition $template to configuration: name = ${def.name}, " +
"resolver = ${def.dependencyResolver.javaClass.name}"
)
} catch (ex: ClassNotFoundException) {
messageCollector.report(CompilerMessageSeverity.ERROR, "Cannot find script definition template class $template")
hasErrors = true
} catch (ex: Exception) {
messageCollector.report(
CompilerMessageSeverity.ERROR,
"Error processing script definition template $template: ${ex.message}"
)
hasErrors = true
break
}
}
if (hasErrors) {
@@ -169,3 +173,36 @@ fun configureScriptDefinitions(
}
}
}
private fun loadScriptDefinition(
classloader: URLClassLoader,
template: String,
scriptResolverEnv: Map<String, Any?>,
messageCollector: MessageCollector
): KotlinScriptDefinition? {
try {
val cls = classloader.loadClass(template)
val def =
if (cls.annotations.firstIsInstanceOrNull<KotlinScript>() != null) {
KotlinScriptDefinitionAdapterFromNewAPI(
ScriptDefinitionFromAnnotatedBaseClass(ScriptingEnvironment(ScriptingEnvironmentProperties.baseClass to cls.kotlin))
)
} else {
KotlinScriptDefinitionFromAnnotatedTemplate(cls.kotlin, scriptResolverEnv)
}
messageCollector.report(
CompilerMessageSeverity.INFO,
"Added script definition $template to configuration: name = ${def.name}, " +
"resolver = ${def.dependencyResolver.javaClass.name}"
)
return def
} catch (ex: ClassNotFoundException) {
messageCollector.report(CompilerMessageSeverity.ERROR, "Cannot find script definition template class $template")
} catch (ex: Exception) {
messageCollector.report(
CompilerMessageSeverity.ERROR,
"Error processing script definition template $template: ${ex.message}"
)
}
return null
}