Implement script evaluation configuration refinement
This commit is contained in:
@@ -92,10 +92,18 @@ class ScriptCollectedData(properties: Map<PropertiesCollection.Key<*>, Any>) : P
|
|||||||
val ScriptCollectedDataKeys.foundAnnotations by PropertiesCollection.key<List<Annotation>>()
|
val ScriptCollectedDataKeys.foundAnnotations by PropertiesCollection.key<List<Annotation>>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The facade to the script data for refinement callbacks
|
* The facade to the script data for compilation configuration refinement callbacks
|
||||||
*/
|
*/
|
||||||
class ScriptConfigurationRefinementContext(
|
class ScriptConfigurationRefinementContext(
|
||||||
val script: SourceCode,
|
val script: SourceCode,
|
||||||
val compilationConfiguration: ScriptCompilationConfiguration,
|
val compilationConfiguration: ScriptCompilationConfiguration,
|
||||||
val collectedData: ScriptCollectedData? = null
|
val collectedData: ScriptCollectedData? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The facade to the script data for evaluation configuration refinement callbacks
|
||||||
|
*/
|
||||||
|
class ScriptEvaluationConfigurationRefinementContext(
|
||||||
|
val compiledScript: CompiledScript<*>,
|
||||||
|
val evaluationConfiguration: ScriptEvaluationConfiguration
|
||||||
|
)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
package kotlin.script.experimental.api
|
package kotlin.script.experimental.api
|
||||||
|
|
||||||
|
import java.io.Serializable
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.script.experimental.util.PropertiesCollection
|
import kotlin.script.experimental.util.PropertiesCollection
|
||||||
|
|
||||||
@@ -52,7 +53,12 @@ val ScriptEvaluationConfigurationKeys.constructorArgs by PropertiesCollection.ke
|
|||||||
* only create/evaluate instances if not found, and evaluator will put newly created instances into the map
|
* only create/evaluate instances if not found, and evaluator will put newly created instances into the map
|
||||||
* This allows to have a single instance of the script if it is imported several times via different import paths.
|
* This allows to have a single instance of the script if it is imported several times via different import paths.
|
||||||
*/
|
*/
|
||||||
val ScriptEvaluationConfigurationKeys.scriptsInstancesSharingMap by PropertiesCollection.key<MutableMap<KClass<*>, Any>>()
|
val ScriptEvaluationConfigurationKeys.scriptsInstancesSharingMap by PropertiesCollection.key<MutableMap<KClass<*>, EvaluationResult>>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The callback that will be called on the script compilation immediately before starting the compilation
|
||||||
|
*/
|
||||||
|
val ScriptEvaluationConfigurationKeys.refineConfigurationBeforeEvaluate by PropertiesCollection.key<RefineEvaluationConfigurationData>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A helper to enable scriptsInstancesSharingMap with default implementation
|
* A helper to enable scriptsInstancesSharingMap with default implementation
|
||||||
@@ -63,6 +69,25 @@ fun ScriptEvaluationConfiguration.Builder.enableScriptsInstancesSharing() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper to enable passing lambda directly to the refinement "keyword"
|
||||||
|
*/
|
||||||
|
fun ScriptEvaluationConfiguration.Builder.refineConfigurationBeforeEvaluate(handler: RefineScriptEvaluationConfigurationHandler) {
|
||||||
|
set(ScriptEvaluationConfiguration.refineConfigurationBeforeEvaluate, RefineEvaluationConfigurationData(handler))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The refinement callback function signature
|
||||||
|
*/
|
||||||
|
typealias RefineScriptEvaluationConfigurationHandler =
|
||||||
|
(ScriptEvaluationConfigurationRefinementContext) -> ResultWithDiagnostics<ScriptEvaluationConfiguration>
|
||||||
|
|
||||||
|
data class RefineEvaluationConfigurationData(
|
||||||
|
val handler: RefineScriptEvaluationConfigurationHandler
|
||||||
|
) : Serializable {
|
||||||
|
companion object { private const val serialVersionUID: Long = 1L }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The script evaluation result value
|
* The script evaluation result value
|
||||||
*/
|
*/
|
||||||
|
|||||||
+47
-38
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
package kotlin.script.experimental.jvmhost
|
package kotlin.script.experimental.jvmhost
|
||||||
|
|
||||||
|
import kotlin.reflect.KClass
|
||||||
import kotlin.script.experimental.api.*
|
import kotlin.script.experimental.api.*
|
||||||
import kotlin.script.experimental.util.PropertiesCollection
|
import kotlin.script.experimental.util.PropertiesCollection
|
||||||
|
|
||||||
@@ -30,8 +31,8 @@ open class BasicJvmScriptEvaluator : ScriptEvaluator {
|
|||||||
scriptEvaluationConfiguration: ScriptEvaluationConfiguration?
|
scriptEvaluationConfiguration: ScriptEvaluationConfiguration?
|
||||||
): ResultWithDiagnostics<EvaluationResult> =
|
): ResultWithDiagnostics<EvaluationResult> =
|
||||||
try {
|
try {
|
||||||
val actualEvaluationConfiguration = scriptEvaluationConfiguration ?: ScriptEvaluationConfiguration()
|
val actualEvalConfiguration = scriptEvaluationConfiguration ?: ScriptEvaluationConfiguration()
|
||||||
compiledScript.getClass(actualEvaluationConfiguration).onSuccess { scriptClass ->
|
compiledScript.getClass(actualEvalConfiguration).onSuccess { scriptClass ->
|
||||||
// in the future, when (if) we'll stop to compile everything into constructor
|
// in the future, when (if) we'll stop to compile everything into constructor
|
||||||
// run as SAM
|
// run as SAM
|
||||||
// return res
|
// return res
|
||||||
@@ -39,55 +40,63 @@ open class BasicJvmScriptEvaluator : ScriptEvaluator {
|
|||||||
// for other scripts we need evaluation configuration with actualClassloader set,
|
// for other scripts we need evaluation configuration with actualClassloader set,
|
||||||
// so they are loaded in the same classloader as the "main" script
|
// so they are loaded in the same classloader as the "main" script
|
||||||
val updatedEvalConfiguration =
|
val updatedEvalConfiguration =
|
||||||
if (actualEvaluationConfiguration.containsKey(ScriptEvaluationConfiguration.jvm.actualClassLoader))
|
if (actualEvalConfiguration.containsKey(ScriptEvaluationConfiguration.jvm.actualClassLoader))
|
||||||
actualEvaluationConfiguration
|
actualEvalConfiguration
|
||||||
else
|
else
|
||||||
ScriptEvaluationConfiguration(actualEvaluationConfiguration) {
|
ScriptEvaluationConfiguration(actualEvalConfiguration) {
|
||||||
ScriptEvaluationConfiguration.jvm.actualClassLoader(scriptClass.java.classLoader)
|
ScriptEvaluationConfiguration.jvm.actualClassLoader(scriptClass.java.classLoader)
|
||||||
}
|
}
|
||||||
|
|
||||||
val sharedScripts = actualEvaluationConfiguration[ScriptEvaluationConfiguration.scriptsInstancesSharingMap]
|
val sharedScripts = actualEvalConfiguration[ScriptEvaluationConfiguration.scriptsInstancesSharingMap]
|
||||||
|
|
||||||
val instanceFromShared = sharedScripts?.get(scriptClass)
|
sharedScripts?.get(scriptClass)?.asSuccess()
|
||||||
|
?: compiledScript.otherScripts.mapSuccess {
|
||||||
if (instanceFromShared != null) {
|
|
||||||
instanceFromShared.asSuccess(updatedEvalConfiguration)
|
|
||||||
} else {
|
|
||||||
|
|
||||||
val args = ArrayList<Any?>()
|
|
||||||
|
|
||||||
updatedEvalConfiguration[ScriptEvaluationConfiguration.constructorArgs]?.let {
|
|
||||||
args.addAll(it)
|
|
||||||
}
|
|
||||||
actualEvaluationConfiguration[ScriptEvaluationConfiguration.implicitReceivers]?.let {
|
|
||||||
args.addAll(it)
|
|
||||||
}
|
|
||||||
actualEvaluationConfiguration[ScriptEvaluationConfiguration.providedProperties]?.forEach {
|
|
||||||
args.add(it.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
compiledScript.otherScripts.mapSuccess {
|
|
||||||
invoke(it, updatedEvalConfiguration)
|
invoke(it, updatedEvalConfiguration)
|
||||||
}.onSuccess { importedScriptsEvalResults ->
|
}.onSuccess { importedScriptsEvalResults ->
|
||||||
|
|
||||||
importedScriptsEvalResults.forEach {
|
val refinedEvalConfiguration =
|
||||||
args.add((it.returnValue as ResultValue.Value).scriptInstance)
|
updatedEvalConfiguration[ScriptEvaluationConfiguration.refineConfigurationBeforeEvaluate]
|
||||||
|
?.handler?.invoke(ScriptEvaluationConfigurationRefinementContext(compiledScript, updatedEvalConfiguration))
|
||||||
|
?.onFailure {
|
||||||
|
return@invoke ResultWithDiagnostics.Failure(it.reports)
|
||||||
|
}
|
||||||
|
?.resultOrNull()
|
||||||
|
?: updatedEvalConfiguration
|
||||||
|
|
||||||
|
scriptClass.evalWithConfigAndOtherScriptsResults(refinedEvalConfiguration, importedScriptsEvalResults).let {
|
||||||
|
sharedScripts?.put(scriptClass, it)
|
||||||
|
ResultWithDiagnostics.Success(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
val ctor = scriptClass.java.constructors.single()
|
|
||||||
val instance = ctor.newInstance(*args.toArray())
|
|
||||||
|
|
||||||
sharedScripts?.put(scriptClass, instance)
|
|
||||||
|
|
||||||
instance.asSuccess(updatedEvalConfiguration)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
ResultWithDiagnostics.Failure(e.asDiagnostics("Error evaluating script", path = compiledScript.sourceLocationId))
|
ResultWithDiagnostics.Failure(e.asDiagnostics("Error evaluating script", path = compiledScript.sourceLocationId))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun KClass<*>.evalWithConfigAndOtherScriptsResults(
|
||||||
|
refinedEvalConfiguration: ScriptEvaluationConfiguration,
|
||||||
|
importedScriptsEvalResults: List<EvaluationResult>
|
||||||
|
): EvaluationResult {
|
||||||
|
val args = ArrayList<Any?>()
|
||||||
|
|
||||||
|
refinedEvalConfiguration[ScriptEvaluationConfiguration.constructorArgs]?.let {
|
||||||
|
args.addAll(it)
|
||||||
|
}
|
||||||
|
refinedEvalConfiguration[ScriptEvaluationConfiguration.implicitReceivers]?.let {
|
||||||
|
args.addAll(it)
|
||||||
|
}
|
||||||
|
refinedEvalConfiguration[ScriptEvaluationConfiguration.providedProperties]?.forEach {
|
||||||
|
args.add(it.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
importedScriptsEvalResults.forEach {
|
||||||
|
args.add((it.returnValue as ResultValue.Value).scriptInstance)
|
||||||
|
}
|
||||||
|
|
||||||
|
val ctor = java.constructors.single()
|
||||||
|
val instance = ctor.newInstance(*args.toArray())
|
||||||
|
|
||||||
|
return EvaluationResult(ResultValue.Value("", instance, "", instance), refinedEvalConfiguration)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Any.asSuccess(updatedEvalConfiguration: ScriptEvaluationConfiguration) =
|
|
||||||
// TODO: fix result value when ready
|
|
||||||
ResultWithDiagnostics.Success(EvaluationResult(ResultValue.Value("", this, "", this), updatedEvalConfiguration))
|
|
||||||
|
|||||||
Reference in New Issue
Block a user