Add a handler for providing evaluation context for scripts

use it on refinement.
also some refactoring on context class and around
This commit is contained in:
Ilya Chernikov
2019-07-25 11:46:17 +02:00
parent 2a5c4a2409
commit 288fdc0952
5 changed files with 63 additions and 6 deletions
@@ -36,7 +36,7 @@ open class ScriptCompilationConfiguration(baseConfigurations: Iterable<ScriptCom
object Default : ScriptCompilationConfiguration()
override fun toString(): String {
return "ScriptCompilationConfiguration($providedProperties)"
return "ScriptCompilationConfiguration($properties)"
}
}
@@ -106,11 +106,43 @@ interface ScriptEvaluationContextDataKeys
* The container for script evaluation context data
* Used for transferring data to the evaluation refinement callbacks
*/
class ScriptEvaluationContextData(properties: Map<PropertiesCollection.Key<*>, Any>) : PropertiesCollection(properties) {
class ScriptEvaluationContextData(baseConfigurations: Iterable<ScriptEvaluationContextData>, body: Builder.() -> Unit = {}) :
PropertiesCollection(Builder(baseConfigurations).apply(body).data) {
constructor(body: Builder.() -> Unit = {}) : this(emptyList(), body)
constructor(
vararg baseConfigurations: ScriptEvaluationContextData, body: Builder.() -> Unit = {}
) : this(baseConfigurations.asIterable(), body)
class Builder internal constructor(baseConfigurations: Iterable<ScriptEvaluationContextData>) :
ScriptEvaluationContextDataKeys,
PropertiesCollection.Builder(baseConfigurations)
companion object : ScriptEvaluationContextDataKeys
}
/**
* optimized alternative to the constructor with multiple base configurations
*/
fun merge(vararg contexts: ScriptEvaluationContextData?): ScriptEvaluationContextData? {
val nonEmpty = ArrayList<ScriptEvaluationContextData>()
for (data in contexts) {
if (data != null && !data.isEmpty()) {
nonEmpty.add(data)
}
}
return when {
nonEmpty.isEmpty() -> null
nonEmpty.size == 1 -> nonEmpty.first()
else -> ScriptEvaluationContextData(nonEmpty.asIterable())
}
}
/**
* Command line arguments of the current process, could be provided by an evaluation host
*/
val ScriptEvaluationContextDataKeys.commandLineArgs by PropertiesCollection.key<List<String>>()
/**
* The facade to the script data for evaluation configuration refinement callbacks
*/
@@ -10,6 +10,7 @@ package kotlin.script.experimental.api
import java.io.Serializable
import kotlin.reflect.KClass
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.host.getEvaluationContext
import kotlin.script.experimental.util.PropertiesCollection
interface ScriptEvaluationConfigurationKeys
@@ -121,10 +122,14 @@ data class RefineEvaluationConfigurationData(
fun ScriptEvaluationConfiguration.refineBeforeEvaluation(
script: CompiledScript<*>,
contextData: ScriptEvaluationContextData? = null
): ResultWithDiagnostics<ScriptEvaluationConfiguration> =
simpleRefineImpl(ScriptEvaluationConfiguration.refineConfigurationBeforeEvaluate) { config, refineData ->
refineData.handler.invoke(ScriptEvaluationConfigurationRefinementContext(script, config, contextData))
): ResultWithDiagnostics<ScriptEvaluationConfiguration> {
val hostConfiguration = get(ScriptEvaluationConfiguration.hostConfiguration)
val baseContextData = hostConfiguration?.get(ScriptingHostConfiguration.getEvaluationContext)?.invoke(hostConfiguration)
val actualContextData = merge(baseContextData, contextData)
return simpleRefineImpl(ScriptEvaluationConfiguration.refineConfigurationBeforeEvaluate) { config, refineData ->
refineData.handler.invoke(ScriptEvaluationConfigurationRefinementContext(script, config, actualContextData))
}
}
/**
* The script evaluation result value
@@ -8,6 +8,7 @@ package kotlin.script.experimental.host
import kotlin.reflect.KClass
import kotlin.script.experimental.api.KotlinType
import kotlin.script.experimental.api.ScriptDependency
import kotlin.script.experimental.api.ScriptEvaluationContextData
import kotlin.script.experimental.util.PropertiesCollection
interface ScriptingHostConfigurationKeys
@@ -52,6 +53,11 @@ val ScriptingHostConfigurationKeys.configurationDependencies by PropertiesCollec
*/
val ScriptingHostConfigurationKeys.getScriptingClass by PropertiesCollection.key<GetScriptingClass>(isTransient = true)
/**
* Evaluation context getter, allows to provide data to the evaluation configuration refinement functions
*/
val ScriptingHostConfigurationKeys.getEvaluationContext by PropertiesCollection.key<GetEvaluationContext>(isTransient = true)
/**
* The interface to the generic "class loader" for the types used in the script configurations
*/
@@ -59,6 +65,18 @@ interface GetScriptingClass {
operator fun invoke(classType: KotlinType, contextClass: KClass<*>, hostConfiguration: ScriptingHostConfiguration): KClass<*>
}
/**
* A helper to enable passing lambda directly to the getEvaluationContext "keyword"
*/
fun ScriptingHostConfiguration.Builder.getEvaluationContext(handler: GetEvaluationContext) {
ScriptingHostConfiguration.getEvaluationContext.put(handler)
}
/**
* The interface to an evaluation context getter
*/
typealias GetEvaluationContext = (hostConfiguration: ScriptingHostConfiguration) -> ScriptEvaluationContextData
// helper method
fun ScriptingHostConfiguration.getScriptingClass(type: KotlinType, contextClass: KClass<*>): KClass<*> {
val getClass = get(ScriptingHostConfiguration.getScriptingClass)
@@ -13,7 +13,7 @@ import kotlin.reflect.KProperty
import kotlin.reflect.KType
import kotlin.script.experimental.api.KotlinType
open class PropertiesCollection(private var properties: Map<Key<*>, Any?> = emptyMap()) : Serializable {
open class PropertiesCollection(protected var properties: Map<Key<*>, Any?> = emptyMap()) : Serializable {
open class Key<T>(
val name: String,
@@ -64,6 +64,8 @@ open class PropertiesCollection(private var properties: Map<Key<*>, Any?> = empt
val notTransientData: Map<Key<*>, Any?> get() = properties.filterKeys { it !is TransientKey<*>}
fun isEmpty(): Boolean = properties.isEmpty()
override fun equals(other: Any?): Boolean =
(other as? PropertiesCollection)?.let { it.properties == properties } == true