Refactor scriptDefinition in the common part
- get rid of selector (because of confusions), move it's data into definition itself and make it more generic with heterogeneous map - make main KotlinScript annotation accept complete definition - make additional annotation for building definition from parts - add ScriptingEnvironment and pass it as a constructor parameter to the script handlers - rename some classes for clarity
This commit is contained in:
+26
-9
@@ -8,20 +8,37 @@
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("unused")
|
||||
|
||||
package kotlin.script.experimental.annotations
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.script.experimental.api.ScriptConfigurator
|
||||
import kotlin.script.experimental.api.ScriptRunner
|
||||
import kotlin.script.experimental.api.ScriptSelector
|
||||
import kotlin.script.experimental.basic.DefaultScriptSelector
|
||||
import kotlin.script.experimental.basic.DummyRunner
|
||||
import kotlin.script.experimental.basic.PassThroughConfigurator
|
||||
import kotlin.script.experimental.api.ScriptCompilationConfigurator
|
||||
import kotlin.script.experimental.api.ScriptDefinition
|
||||
import kotlin.script.experimental.api.ScriptEvaluator
|
||||
import kotlin.script.experimental.definitions.ScriptDefinitionFromAnnotatedBaseClass
|
||||
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class KotlinScript(
|
||||
val selector: KClass<out ScriptSelector> = DefaultScriptSelector::class,
|
||||
val configurator: KClass<out ScriptConfigurator> = PassThroughConfigurator::class,
|
||||
val runner: KClass<out ScriptRunner<*>> = DummyRunner::class
|
||||
val definition: KClass<out ScriptDefinition> = ScriptDefinitionFromAnnotatedBaseClass::class
|
||||
)
|
||||
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class KotlinScriptFileExtension(
|
||||
val extension: String
|
||||
)
|
||||
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class KotlinScriptCompilationConfigurator(
|
||||
val compilationConfigurator: KClass<out ScriptCompilationConfigurator>
|
||||
)
|
||||
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class KotlinScriptEvaluator(
|
||||
val evaluator: KClass<out ScriptEvaluator<*>>
|
||||
)
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ interface ScriptCompiler {
|
||||
|
||||
suspend fun compile(
|
||||
configuration: ScriptCompileConfiguration,
|
||||
configurator: ScriptConfigurator? = null
|
||||
configurator: ScriptCompilationConfigurator? = null
|
||||
): ResultWithDiagnostics<CompiledScript<*>>
|
||||
}
|
||||
|
||||
|
||||
+8
-3
@@ -7,14 +7,19 @@
|
||||
|
||||
package kotlin.script.experimental.api
|
||||
|
||||
|
||||
typealias ScriptCompileConfiguration = HeterogeneousMap
|
||||
|
||||
typealias ProcessedScriptData = HeterogeneousMap
|
||||
|
||||
interface ScriptConfigurator {
|
||||
|
||||
// with null scriptSource should return a generic configuration for the script type
|
||||
suspend fun baseConfiguration(scriptSource: ScriptSource?) : ResultWithDiagnostics<ScriptCompileConfiguration>
|
||||
interface ScriptCompilationConfigurator {
|
||||
|
||||
// constructor(environment: ScriptingEnvironment) // the constructor is expected from implementations
|
||||
|
||||
val defaultConfiguration: ScriptCompileConfiguration
|
||||
|
||||
suspend fun baseConfiguration(scriptSource: ScriptSource): ResultWithDiagnostics<ScriptCompileConfiguration>
|
||||
|
||||
suspend fun refineConfiguration(
|
||||
configuration: ScriptCompileConfiguration,
|
||||
|
||||
+3
@@ -10,6 +10,9 @@ package kotlin.script.experimental.api
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
object ScriptCompileConfigurationParams {
|
||||
|
||||
val baseClass by typedKey<KClass<*>>()
|
||||
|
||||
val scriptSourceFragments by typedKey<ScriptSourceFragments>()
|
||||
|
||||
val scriptSignature by typedKey<ScriptSignature>()
|
||||
|
||||
@@ -7,12 +7,15 @@
|
||||
|
||||
package kotlin.script.experimental.api
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
interface ScriptDefinition {
|
||||
val baseClass: KClass<*>
|
||||
val selector: ScriptSelector
|
||||
val configurator: ScriptConfigurator
|
||||
val runner: ScriptRunner<*>?
|
||||
|
||||
// constructor(environment: ScriptingEnvironment) // the constructor is expected from implementations
|
||||
|
||||
val properties: ScriptDefinitionPropertiesBag
|
||||
|
||||
val compilationConfigurator: ScriptCompilationConfigurator
|
||||
|
||||
val evaluator: ScriptEvaluator<*>?
|
||||
}
|
||||
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@file:Suppress("unused")
|
||||
|
||||
package kotlin.script.experimental.api
|
||||
|
||||
|
||||
typealias ScriptDefinitionPropertiesBag = HeterogeneousMap
|
||||
|
||||
|
||||
object ScriptDefinitionProperties {
|
||||
|
||||
val name by typedKey<String>()
|
||||
|
||||
val fileExtension by typedKey<String>()
|
||||
|
||||
val makeTitle by typedKey<(String) -> String>()
|
||||
|
||||
open class Builder : HeterogeneousMapBuilder() {
|
||||
inline fun <reified T> makeTitle(noinline fn: (String) -> String) {
|
||||
add(makeTitle to fn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,9 +27,11 @@ typealias ScriptEvaluationEnvironment = HeterogeneousMap
|
||||
data class EvaluationResult(val returnValue: Any?, val environment: ScriptEvaluationEnvironment)
|
||||
|
||||
// NOTE: name inconsistency: run vs evaluate
|
||||
interface ScriptRunner<in ScriptBase : Any> {
|
||||
interface ScriptEvaluator<in ScriptBase : Any> {
|
||||
|
||||
suspend fun run(
|
||||
// constructor(environment: ScriptingEnvironment) // the constructor is expected from implementations
|
||||
|
||||
suspend fun eval(
|
||||
compiledScript: CompiledScript<ScriptBase>,
|
||||
scriptEvaluationEnvironment: ScriptEvaluationEnvironment
|
||||
): ResultWithDiagnostics<EvaluationResult>
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-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.
|
||||
*/
|
||||
|
||||
@file:Suppress("unused")
|
||||
|
||||
package kotlin.script.experimental.api
|
||||
|
||||
interface ScriptSelector {
|
||||
|
||||
val name: String
|
||||
|
||||
val fileExtension: String // for preliminary selection by file type, e.g. in ide
|
||||
|
||||
fun makeScriptName(scriptFileName: String?): String
|
||||
|
||||
fun isKnownScript(script: ScriptSource): Boolean
|
||||
}
|
||||
@@ -31,6 +31,7 @@ class HeterogeneousMap(private val data: Map<TypedKey<*>, Any?> = hashMapOf()) {
|
||||
|
||||
fun HeterogeneousMap.cloneWith(vararg pairs: Pair<TypedKey<*>, Any?>) = HeterogeneousMap(this, *pairs)
|
||||
|
||||
fun HeterogeneousMap.cloneWith(pairs: Iterable<Pair<TypedKey<*>, Any?>>) = HeterogeneousMap(this, pairs)
|
||||
|
||||
open class HeterogeneousMapBuilder {
|
||||
val pairs: MutableList<Pair<TypedKey<*>, Any?>> = arrayListOf()
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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 kotlin.script.experimental.api
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
typealias ScriptingEnvironment = HeterogeneousMap
|
||||
|
||||
object ScriptingEnvironmentParams {
|
||||
|
||||
// required by definitions that extract data from script base class annotations
|
||||
val baseClass by typedKey<KClass<*>>()
|
||||
|
||||
open class Builder : HeterogeneousMapBuilder()
|
||||
}
|
||||
|
||||
@@ -5,34 +5,17 @@
|
||||
|
||||
package kotlin.script.experimental.basic
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.full.findAnnotation
|
||||
import kotlin.script.experimental.api.*
|
||||
|
||||
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class KotlinScriptFileExtension(val extension: String)
|
||||
class PassThroughCompilationConfigurator(val environment: ScriptingEnvironment) : ScriptCompilationConfigurator {
|
||||
|
||||
open class DefaultScriptSelector(val baseClass: KClass<Any>? = null) : ScriptSelector {
|
||||
override val defaultConfiguration = ScriptCompileConfiguration(
|
||||
ScriptCompileConfigurationParams.baseClass to environment[ScriptingEnvironmentParams.baseClass]
|
||||
)
|
||||
|
||||
override val name: String = "Kotlin script"
|
||||
|
||||
override val fileExtension: String = baseClass?.findAnnotation<KotlinScriptFileExtension>()?.extension ?: "kts"
|
||||
|
||||
override fun makeScriptName(scriptFileName: String?): String = scriptFileName?.removeSuffix(".$fileExtension") ?: "Kotlin script"
|
||||
|
||||
override fun isKnownScript(script: ScriptSource): Boolean =
|
||||
script.location?.file?.endsWith(fileExtension) ?: true
|
||||
}
|
||||
|
||||
class PassThroughConfigurator(val baseClass: KClass<Any>? = null) : ScriptConfigurator {
|
||||
|
||||
override suspend fun baseConfiguration(scriptSource: ScriptSource?): ResultWithDiagnostics<ScriptCompileConfiguration> =
|
||||
(when (scriptSource) {
|
||||
null -> ScriptCompileConfiguration()
|
||||
else -> ScriptCompileConfiguration(scriptSource.toConfigEntry())
|
||||
}).asSuccess()
|
||||
override suspend fun baseConfiguration(scriptSource: ScriptSource): ResultWithDiagnostics<ScriptCompileConfiguration> =
|
||||
defaultConfiguration.asSuccess()
|
||||
|
||||
override suspend fun refineConfiguration(
|
||||
configuration: ScriptCompileConfiguration,
|
||||
@@ -41,8 +24,8 @@ class PassThroughConfigurator(val baseClass: KClass<Any>? = null) : ScriptConfig
|
||||
configuration.asSuccess()
|
||||
}
|
||||
|
||||
class DummyRunner<ScriptBase : Any>(val baseClass: KClass<ScriptBase>? = null) : ScriptRunner<ScriptBase> {
|
||||
override suspend fun run(
|
||||
class DummyEvaluator<ScriptBase : Any>(val environment: ScriptingEnvironment) : ScriptEvaluator<ScriptBase> {
|
||||
override suspend fun eval(
|
||||
compiledScript: CompiledScript<ScriptBase>,
|
||||
scriptEvaluationEnvironment: ScriptEvaluationEnvironment
|
||||
): ResultWithDiagnostics<EvaluationResult> =
|
||||
|
||||
+38
-8
@@ -6,23 +6,53 @@
|
||||
package kotlin.script.experimental.definitions
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.full.findAnnotation
|
||||
import kotlin.reflect.full.primaryConstructor
|
||||
import kotlin.script.experimental.annotations.KotlinScript
|
||||
import kotlin.script.experimental.api.ScriptDefinition
|
||||
import kotlin.script.experimental.annotations.KotlinScriptCompilationConfigurator
|
||||
import kotlin.script.experimental.annotations.KotlinScriptEvaluator
|
||||
import kotlin.script.experimental.annotations.KotlinScriptFileExtension
|
||||
import kotlin.script.experimental.api.*
|
||||
import kotlin.script.experimental.basic.DummyEvaluator
|
||||
import kotlin.script.experimental.basic.PassThroughCompilationConfigurator
|
||||
|
||||
open class ScriptDefinitionFromAnnotatedBaseClass(final override val baseClass: KClass<*>) : ScriptDefinition {
|
||||
private val annotation = baseClass.java.getAnnotation(KotlinScript::class.java)
|
||||
?: throw IllegalArgumentException("Expecting KotlinScript on the $baseClass")
|
||||
private const val ERROR_MSG_PREFIX = "Unable to construct script definition: "
|
||||
|
||||
override val selector = annotation.selector.instantiateScriptHandler()
|
||||
override val configurator = annotation.configurator.instantiateScriptHandler()
|
||||
override val runner = annotation.runner.instantiateScriptHandler()
|
||||
open class ScriptDefinitionFromAnnotatedBaseClass(val environment: ScriptingEnvironment) : ScriptDefinition {
|
||||
|
||||
private val baseClass: KClass<*> = environment.getOrNull(ScriptingEnvironmentParams.baseClass)
|
||||
?: throw IllegalArgumentException("${ERROR_MSG_PREFIX}Expecting baseClass parameter in the scripting environment")
|
||||
|
||||
private val mainAnnotation = baseClass.findAnnotation<KotlinScript>()
|
||||
?: throw IllegalArgumentException("${ERROR_MSG_PREFIX}Expecting KotlinScript on the $baseClass")
|
||||
|
||||
private val explicitDefinition: ScriptDefinition? =
|
||||
mainAnnotation.definition.takeIf { it != this::class }?.let { it.instantiateScriptHandler() }
|
||||
|
||||
override val properties = (explicitDefinition?.properties ?: ScriptDefinitionPropertiesBag()).also { properties ->
|
||||
val toAdd = arrayListOf<Pair<TypedKey<*>, Any>>()
|
||||
baseClass.findAnnotation<KotlinScriptFileExtension>()?.let { toAdd += ScriptDefinitionProperties.fileExtension to it }
|
||||
if (properties.getOrNull(ScriptDefinitionProperties.name) == null) {
|
||||
toAdd += ScriptDefinitionProperties.name to baseClass.simpleName!!
|
||||
}
|
||||
properties.cloneWith(toAdd)
|
||||
}
|
||||
|
||||
override val compilationConfigurator =
|
||||
baseClass.findAnnotation<KotlinScriptCompilationConfigurator>()?.compilationConfigurator?.instantiateScriptHandler()
|
||||
?: explicitDefinition?.compilationConfigurator
|
||||
?: PassThroughCompilationConfigurator::class.instantiateScriptHandler()
|
||||
|
||||
override val evaluator =
|
||||
baseClass.findAnnotation<KotlinScriptEvaluator>()?.evaluator?.instantiateScriptHandler()
|
||||
?: explicitDefinition?.evaluator
|
||||
?: DummyEvaluator::class.instantiateScriptHandler()
|
||||
|
||||
private fun <T : Any> KClass<T>.instantiateScriptHandler(): T {
|
||||
val fqn = this.qualifiedName!!
|
||||
val klass: KClass<T> = (baseClass.java.classLoader.loadClass(fqn) as Class<T>).kotlin
|
||||
// TODO: fix call after deciding on constructor parameters
|
||||
return klass.objectInstance ?: klass.primaryConstructor!!.call(baseClass)
|
||||
return klass.objectInstance ?: klass.primaryConstructor!!.call(environment)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -23,9 +23,9 @@ import kotlinx.coroutines.experimental.runBlocking
|
||||
import kotlin.script.experimental.api.*
|
||||
|
||||
abstract class BasicScriptingHost<ScriptBase : Any>(
|
||||
val configurator: ScriptConfigurator,
|
||||
val configurator: ScriptCompilationConfigurator,
|
||||
val compiler: ScriptCompiler,
|
||||
val runner: ScriptRunner<ScriptBase>
|
||||
val evaluator: ScriptEvaluator<ScriptBase>
|
||||
) {
|
||||
open fun <T> runInCoroutineContext(block: suspend CoroutineScope.() -> T): T = runBlocking { block() }
|
||||
|
||||
@@ -36,7 +36,7 @@ abstract class BasicScriptingHost<ScriptBase : Any>(
|
||||
is ResultWithDiagnostics.Failure -> compiled
|
||||
is ResultWithDiagnostics.Success -> {
|
||||
val compiledScript = compiled.value!! as CompiledScript<ScriptBase>
|
||||
runner.run(compiledScript, environment)
|
||||
evaluator.eval(compiledScript, environment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user