Introducing transient properties in scripting API

This commit is contained in:
Ilya Chernikov
2019-07-24 19:57:47 +02:00
parent 5fe843d754
commit e41bbe9328
8 changed files with 89 additions and 43 deletions
@@ -130,17 +130,17 @@ val ScriptCompilationConfigurationKeys.compilerOptions by PropertiesCollection.k
/** /**
* The callback that will be called on the script compilation before parsing the script * The callback that will be called on the script compilation before parsing the script
*/ */
val ScriptCompilationConfigurationKeys.refineConfigurationBeforeParsing by PropertiesCollection.key<List<RefineConfigurationUnconditionallyData>>() val ScriptCompilationConfigurationKeys.refineConfigurationBeforeParsing by PropertiesCollection.key<List<RefineConfigurationUnconditionallyData>>(isTransient = true)
/** /**
* The callback that will be called on the script compilation after parsing script file annotations * The callback that will be called on the script compilation after parsing script file annotations
*/ */
val ScriptCompilationConfigurationKeys.refineConfigurationOnAnnotations by PropertiesCollection.key<List<RefineConfigurationOnAnnotationsData>>() val ScriptCompilationConfigurationKeys.refineConfigurationOnAnnotations by PropertiesCollection.key<List<RefineConfigurationOnAnnotationsData>>(isTransient = true)
/** /**
* The callback that will be called on the script compilation immediately before starting the compilation * The callback that will be called on the script compilation immediately before starting the compilation
*/ */
val ScriptCompilationConfigurationKeys.refineConfigurationBeforeCompiling by PropertiesCollection.key<List<RefineConfigurationUnconditionallyData>>() val ScriptCompilationConfigurationKeys.refineConfigurationBeforeCompiling by PropertiesCollection.key<List<RefineConfigurationUnconditionallyData>>(isTransient = true)
/** /**
* The list of script fragments that should be compiled intead of the whole text * The list of script fragments that should be compiled intead of the whole text
@@ -151,7 +151,7 @@ val ScriptCompilationConfigurationKeys.sourceFragments by PropertiesCollection.k
/** /**
* Scripting host configuration * Scripting host configuration
*/ */
val ScriptCompilationConfigurationKeys.hostConfiguration by PropertiesCollection.key<ScriptingHostConfiguration>() val ScriptCompilationConfigurationKeys.hostConfiguration by PropertiesCollection.key<ScriptingHostConfiguration>(isTransient = true)
/** /**
* The sub-builder DSL for configuring refinement callbacks * The sub-builder DSL for configuring refinement callbacks
@@ -68,10 +68,10 @@ val ScriptEvaluationConfigurationKeys.constructorArgs by PropertiesCollection.ke
* For the first snippet in a REPL an empty list should be passed explicitly * For the first snippet in a REPL an empty list should be passed explicitly
* An array of the previous snippets will be passed to the current snippet constructor * An array of the previous snippets will be passed to the current snippet constructor
*/ */
val ScriptEvaluationConfigurationKeys.previousSnippets by PropertiesCollection.key<List<Any>>() val ScriptEvaluationConfigurationKeys.previousSnippets by PropertiesCollection.key<List<Any>>(isTransient = true)
@Deprecated("use scriptsInstancesSharing flag instead", level = DeprecationLevel.ERROR) @Deprecated("use scriptsInstancesSharing flag instead", level = DeprecationLevel.ERROR)
val ScriptEvaluationConfigurationKeys.scriptsInstancesSharingMap by PropertiesCollection.key<MutableMap<KClass<*>, EvaluationResult>>() val ScriptEvaluationConfigurationKeys.scriptsInstancesSharingMap by PropertiesCollection.key<MutableMap<KClass<*>, EvaluationResult>>(isTransient = true)
/** /**
* If enabled - the evaluator will try to get imported script from a shared container * If enabled - the evaluator will try to get imported script from a shared container
@@ -83,12 +83,12 @@ val ScriptEvaluationConfigurationKeys.scriptsInstancesSharing by PropertiesColle
/** /**
* Scripting host configuration * Scripting host configuration
*/ */
val ScriptEvaluationConfigurationKeys.hostConfiguration by PropertiesCollection.key<ScriptingHostConfiguration>() val ScriptEvaluationConfigurationKeys.hostConfiguration by PropertiesCollection.key<ScriptingHostConfiguration>(isTransient = true)
/** /**
* The callback that will be called on the script compilation immediately before starting the compilation * The callback that will be called on the script compilation immediately before starting the compilation
*/ */
val ScriptEvaluationConfigurationKeys.refineConfigurationBeforeEvaluate by PropertiesCollection.key<List<RefineEvaluationConfigurationData>>() val ScriptEvaluationConfigurationKeys.refineConfigurationBeforeEvaluate by PropertiesCollection.key<List<RefineEvaluationConfigurationData>>(isTransient = true)
/** /**
* A helper to enable scriptsInstancesSharingMap with default implementation * A helper to enable scriptsInstancesSharingMap with default implementation
@@ -50,7 +50,7 @@ val ScriptingHostConfigurationKeys.configurationDependencies by PropertiesCollec
/** /**
* The pointer to the generic "class loader" for the types used in the script configurations * The pointer to the generic "class loader" for the types used in the script configurations
*/ */
val ScriptingHostConfigurationKeys.getScriptingClass by PropertiesCollection.key<GetScriptingClass>() val ScriptingHostConfigurationKeys.getScriptingClass by PropertiesCollection.key<GetScriptingClass>(isTransient = true)
/** /**
* The interface to the generic "class loader" for the types used in the script configurations * The interface to the generic "class loader" for the types used in the script configurations
@@ -5,15 +5,20 @@
package kotlin.script.experimental.util package kotlin.script.experimental.util
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
import java.io.Serializable import java.io.Serializable
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
import kotlin.reflect.KType import kotlin.reflect.KType
import kotlin.script.experimental.api.KotlinType import kotlin.script.experimental.api.KotlinType
open class PropertiesCollection(private val properties: Map<Key<*>, Any?> = emptyMap()) : Serializable { open class PropertiesCollection(private var properties: Map<Key<*>, Any?> = emptyMap()) : Serializable {
class Key<T>(val name: String, @Transient val getDefaultValue: PropertiesCollection.() -> T?) : Serializable { open class Key<T>(
val name: String,
@Transient val getDefaultValue: PropertiesCollection.() -> T?
) : Serializable {
constructor(name: String, defaultValue: T? = null) : this(name, { defaultValue }) constructor(name: String, defaultValue: T? = null) : this(name, { defaultValue })
@@ -27,11 +32,17 @@ open class PropertiesCollection(private val properties: Map<Key<*>, Any?> = empt
} }
} }
class PropertyKeyDelegate<T>(private val getDefaultValue: PropertiesCollection.() -> T?) { class TransientKey<T>(
constructor(defaultValue: T?) : this({ defaultValue }) name: String,
getDefaultValue: PropertiesCollection.() -> T?
) : Key<T>(name, getDefaultValue)
class PropertyKeyDelegate<T>(private val getDefaultValue: PropertiesCollection.() -> T?, val isTransient: Boolean = false) {
constructor(defaultValue: T?, isTransient: Boolean = false) : this({ defaultValue }, isTransient)
operator fun getValue(thisRef: Any?, property: KProperty<*>): Key<T> = operator fun getValue(thisRef: Any?, property: KProperty<*>): Key<T> =
Key(property.name, getDefaultValue) if (isTransient) TransientKey(property.name, getDefaultValue)
else Key(property.name, getDefaultValue)
} }
class PropertyKeyCopyDelegate<T>(val source: Key<T>) { class PropertyKeyCopyDelegate<T>(val source: Key<T>) {
@@ -51,14 +62,29 @@ open class PropertiesCollection(private val properties: Map<Key<*>, Any?> = empt
fun entries(): Set<Map.Entry<Key<*>, Any?>> = properties.entries fun entries(): Set<Map.Entry<Key<*>, Any?>> = properties.entries
val notTransientData: Map<Key<*>, Any?> get() = properties.filterKeys { it !is TransientKey<*>}
override fun equals(other: Any?): Boolean = override fun equals(other: Any?): Boolean =
(other as? PropertiesCollection)?.let { it.properties == properties } == true (other as? PropertiesCollection)?.let { it.properties == properties } == true
override fun hashCode(): Int = properties.hashCode() override fun hashCode(): Int = properties.hashCode()
private fun writeObject(outputStream: ObjectOutputStream) {
outputStream.writeObject(notTransientData)
}
private fun readObject(inputStream: ObjectInputStream) {
@Suppress("UNCHECKED_CAST")
properties = inputStream.readObject() as Map<Key<*>, Any?>
}
companion object { companion object {
fun <T> key(defaultValue: T? = null): PropertyKeyDelegate<T> = PropertyKeyDelegate(defaultValue) fun <T> key(defaultValue: T? = null, isTransient: Boolean = false): PropertyKeyDelegate<T> =
fun <T> key(getDefaultValue: PropertiesCollection.() -> T?): PropertyKeyDelegate<T> = PropertyKeyDelegate(getDefaultValue) PropertyKeyDelegate(defaultValue, isTransient)
fun <T> key(getDefaultValue: PropertiesCollection.() -> T?, isTransient: Boolean = false): PropertyKeyDelegate<T> =
PropertyKeyDelegate(getDefaultValue, isTransient)
fun <T> keyCopy(source: Key<T>): PropertyKeyCopyDelegate<T> = PropertyKeyCopyDelegate(source) fun <T> keyCopy(source: Key<T>): PropertyKeyCopyDelegate<T> = PropertyKeyCopyDelegate(source)
@JvmStatic @JvmStatic
@@ -128,7 +128,7 @@ class CachingTest : TestCase() {
Assert.assertEquals(1, cache.storedScripts) Assert.assertEquals(1, cache.storedScripts)
Assert.assertEquals(0, cache.retrievedScripts) Assert.assertEquals(0, cache.retrievedScripts)
val cachedScript = cache.get(script.toScriptSource(), scriptCompilationConfiguration) val cachedScript = cache.get(script.toScriptSource(), compiledScript!!.compilationConfiguration)
Assert.assertNotNull(cachedScript) Assert.assertNotNull(cachedScript)
Assert.assertEquals(1, cache.retrievedScripts) Assert.assertEquals(1, cache.retrievedScripts)
@@ -164,7 +164,7 @@ private interface ScriptingCacheWithCounters : CompiledJvmScriptsCache {
private class SimpleMemoryScriptsCache : ScriptingCacheWithCounters { private class SimpleMemoryScriptsCache : ScriptingCacheWithCounters {
internal val data = hashMapOf<Pair<SourceCode, ScriptCompilationConfiguration>, CompiledScript<*>>() internal val data = hashMapOf<Pair<SourceCode, Map<*, *>>, CompiledScript<*>>()
private var _storedScripts = 0 private var _storedScripts = 0
private var _retrievedScripts = 0 private var _retrievedScripts = 0
@@ -176,14 +176,14 @@ private class SimpleMemoryScriptsCache : ScriptingCacheWithCounters {
get() = _retrievedScripts get() = _retrievedScripts
override fun get(script: SourceCode, scriptCompilationConfiguration: ScriptCompilationConfiguration): CompiledScript<*>? = override fun get(script: SourceCode, scriptCompilationConfiguration: ScriptCompilationConfiguration): CompiledScript<*>? =
data[script to scriptCompilationConfiguration]?.also { _retrievedScripts++ } data[script to scriptCompilationConfiguration.notTransientData]?.also { _retrievedScripts++ }
override fun store( override fun store(
compiledScript: CompiledScript<*>, compiledScript: CompiledScript<*>,
script: SourceCode, script: SourceCode,
scriptCompilationConfiguration: ScriptCompilationConfiguration scriptCompilationConfiguration: ScriptCompilationConfiguration
) { ) {
data[script to scriptCompilationConfiguration] = compiledScript data[script to scriptCompilationConfiguration.notTransientData] = compiledScript
_storedScripts++ _storedScripts++
} }
} }
@@ -242,10 +242,12 @@ class TestCompiledScriptJarsCache(val baseDir: File) : CompiledScriptJarsCache(
internal fun uniqueScriptHash(script: SourceCode, scriptCompilationConfiguration: ScriptCompilationConfiguration): String { internal fun uniqueScriptHash(script: SourceCode, scriptCompilationConfiguration: ScriptCompilationConfiguration): String {
val digestWrapper = MessageDigest.getInstance("MD5") val digestWrapper = MessageDigest.getInstance("MD5")
digestWrapper.update(script.text.toByteArray()) digestWrapper.update(script.text.toByteArray())
scriptCompilationConfiguration.entries().sortedBy { it.key.name }.forEach { scriptCompilationConfiguration.notTransientData.entries
digestWrapper.update(it.key.name.toByteArray()) .sortedBy { it.key.name }
digestWrapper.update(it.value.toString().toByteArray()) .forEach {
} digestWrapper.update(it.key.name.toByteArray())
digestWrapper.update(it.value.toString().toByteArray())
}
return digestWrapper.digest().toHexString() return digestWrapper.digest().toHexString()
} }
@@ -32,9 +32,12 @@ open class Jsr223CompilationConfigurationBuilder : PropertiesCollection.Builder(
val ScriptCompilationConfigurationKeys.jsr223 get() = Jsr223CompilationConfigurationBuilder() val ScriptCompilationConfigurationKeys.jsr223 get() = Jsr223CompilationConfigurationBuilder()
val Jsr223CompilationConfigurationKeys.getScriptContext by PropertiesCollection.key<() -> ScriptContext?> { val Jsr223CompilationConfigurationKeys.getScriptContext by PropertiesCollection.key<() -> ScriptContext?>(
get(ScriptCompilationConfiguration.hostConfiguration)?.get(ScriptingHostConfiguration.jsr223.getScriptContext) {
} get(ScriptCompilationConfiguration.hostConfiguration)?.get(ScriptingHostConfiguration.jsr223.getScriptContext)
},
isTransient = true
)
val Jsr223CompilationConfigurationKeys.importAllBindings by PropertiesCollection.key<Boolean>(false) val Jsr223CompilationConfigurationKeys.importAllBindings by PropertiesCollection.key<Boolean>(false)
@@ -47,8 +50,11 @@ open class Jsr223EvaluationConfigurationBuilder : PropertiesCollection.Builder()
val ScriptEvaluationConfigurationKeys.jsr223 get() = Jsr223EvaluationConfigurationBuilder() val ScriptEvaluationConfigurationKeys.jsr223 get() = Jsr223EvaluationConfigurationBuilder()
val Jsr223EvaluationConfigurationKeys.getScriptContext by PropertiesCollection.key<() -> ScriptContext?> { val Jsr223EvaluationConfigurationKeys.getScriptContext by PropertiesCollection.key<() -> ScriptContext?>(
get(ScriptEvaluationConfiguration.hostConfiguration)?.get(ScriptingHostConfiguration.jsr223.getScriptContext) {
} get(ScriptEvaluationConfiguration.hostConfiguration)?.get(ScriptingHostConfiguration.jsr223.getScriptContext)
},
isTransient = true
)
@@ -8,7 +8,10 @@
package kotlin.script.experimental.jvm package kotlin.script.experimental.jvm
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.script.experimental.api.* import kotlin.script.experimental.api.EvaluationResult
import kotlin.script.experimental.api.ScriptEvaluationConfiguration
import kotlin.script.experimental.api.ScriptEvaluationConfigurationKeys
import kotlin.script.experimental.api.hostConfiguration
import kotlin.script.experimental.host.ScriptingHostConfiguration import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.util.PropertiesCollection import kotlin.script.experimental.util.PropertiesCollection
@@ -22,10 +25,13 @@ open class JvmScriptEvaluationConfigurationBuilder : PropertiesCollection.Builde
/** /**
* The base classloader to use for script classes loading * The base classloader to use for script classes loading
*/ */
val JvmScriptEvaluationConfigurationKeys.baseClassLoader by PropertiesCollection.key<ClassLoader?> { val JvmScriptEvaluationConfigurationKeys.baseClassLoader by PropertiesCollection.key<ClassLoader?>(
get(ScriptEvaluationConfiguration.hostConfiguration)?.get(ScriptingHostConfiguration.jvm.baseClassLoader) {
?: Thread.currentThread().contextClassLoader get(ScriptEvaluationConfiguration.hostConfiguration)?.get(ScriptingHostConfiguration.jvm.baseClassLoader)
} ?: Thread.currentThread().contextClassLoader
},
isTransient = true
)
/** /**
* Load script dependencies before evaluation, true by default * Load script dependencies before evaluation, true by default
@@ -38,8 +44,10 @@ val JvmScriptEvaluationConfigurationKeys.loadDependencies by PropertiesCollectio
*/ */
val JvmScriptEvaluationConfigurationKeys.mainArguments by PropertiesCollection.key<Array<out String>>() val JvmScriptEvaluationConfigurationKeys.mainArguments by PropertiesCollection.key<Array<out String>>()
internal val JvmScriptEvaluationConfigurationKeys.actualClassLoader by PropertiesCollection.key<ClassLoader?>() internal val JvmScriptEvaluationConfigurationKeys.actualClassLoader by PropertiesCollection.key<ClassLoader?>(isTransient = true)
internal val JvmScriptEvaluationConfigurationKeys.scriptsInstancesSharingMap by PropertiesCollection.key<MutableMap<KClass<*>, EvaluationResult>>() internal val JvmScriptEvaluationConfigurationKeys.scriptsInstancesSharingMap by PropertiesCollection.key<MutableMap<KClass<*>, EvaluationResult>>(
isTransient = true
)
val ScriptEvaluationConfigurationKeys.jvm get() = JvmScriptEvaluationConfigurationBuilder() val ScriptEvaluationConfigurationKeys.jvm get() = JvmScriptEvaluationConfigurationBuilder()
@@ -9,7 +9,8 @@ import java.io.File
import java.io.Serializable import java.io.Serializable
import java.net.URLClassLoader import java.net.URLClassLoader
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.script.experimental.api.* import kotlin.script.experimental.api.KotlinType
import kotlin.script.experimental.api.ScriptDependency
import kotlin.script.experimental.host.* import kotlin.script.experimental.host.*
import kotlin.script.experimental.jvm.impl.toClassPathOrEmpty import kotlin.script.experimental.jvm.impl.toClassPathOrEmpty
import kotlin.script.experimental.util.PropertiesCollection import kotlin.script.experimental.util.PropertiesCollection
@@ -26,11 +27,14 @@ val JvmScriptingHostConfigurationKeys.javaHome by PropertiesCollection.key<File>
val JvmScriptingHostConfigurationKeys.jdkHome by PropertiesCollection.key<File>() val JvmScriptingHostConfigurationKeys.jdkHome by PropertiesCollection.key<File>()
val JvmScriptingHostConfigurationKeys.baseClassLoader by PropertiesCollection.key<ClassLoader> { val JvmScriptingHostConfigurationKeys.baseClassLoader by PropertiesCollection.key<ClassLoader>(
get(ScriptingHostConfiguration.configurationDependencies)?.let { {
URLClassLoader(it.toClassPathOrEmpty().map { f -> f.toURI().toURL() }.toTypedArray()) get(ScriptingHostConfiguration.configurationDependencies)?.let {
} URLClassLoader(it.toClassPathOrEmpty().map { f -> f.toURI().toURL() }.toTypedArray())
} }
},
isTransient = true
)
@Suppress("unused") @Suppress("unused")
val ScriptingHostConfigurationKeys.jvm val ScriptingHostConfigurationKeys.jvm