diff --git a/libraries/scripting/common/src/kotlin/script/experimental/api/scriptData.kt b/libraries/scripting/common/src/kotlin/script/experimental/api/scriptData.kt index 493b12abd86..24c3c6345db 100644 --- a/libraries/scripting/common/src/kotlin/script/experimental/api/scriptData.kt +++ b/libraries/scripting/common/src/kotlin/script/experimental/api/scriptData.kt @@ -92,10 +92,18 @@ class ScriptCollectedData(properties: Map, Any>) : P val ScriptCollectedDataKeys.foundAnnotations by PropertiesCollection.key>() /** - * The facade to the script data for refinement callbacks + * The facade to the script data for compilation configuration refinement callbacks */ class ScriptConfigurationRefinementContext( val script: SourceCode, val compilationConfiguration: ScriptCompilationConfiguration, val collectedData: ScriptCollectedData? = null ) + +/** + * The facade to the script data for evaluation configuration refinement callbacks + */ +class ScriptEvaluationConfigurationRefinementContext( + val compiledScript: CompiledScript<*>, + val evaluationConfiguration: ScriptEvaluationConfiguration +) diff --git a/libraries/scripting/common/src/kotlin/script/experimental/api/scriptEvaluation.kt b/libraries/scripting/common/src/kotlin/script/experimental/api/scriptEvaluation.kt index b90cbc5e209..ac1b0cfc171 100644 --- a/libraries/scripting/common/src/kotlin/script/experimental/api/scriptEvaluation.kt +++ b/libraries/scripting/common/src/kotlin/script/experimental/api/scriptEvaluation.kt @@ -7,6 +7,7 @@ package kotlin.script.experimental.api +import java.io.Serializable import kotlin.reflect.KClass 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 * 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, Any>>() +val ScriptEvaluationConfigurationKeys.scriptsInstancesSharingMap by PropertiesCollection.key, EvaluationResult>>() + +/** + * The callback that will be called on the script compilation immediately before starting the compilation + */ +val ScriptEvaluationConfigurationKeys.refineConfigurationBeforeEvaluate by PropertiesCollection.key() /** * 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 + +data class RefineEvaluationConfigurationData( + val handler: RefineScriptEvaluationConfigurationHandler +) : Serializable { + companion object { private const val serialVersionUID: Long = 1L } +} + /** * The script evaluation result value */ diff --git a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/jvmScriptEvaluation.kt b/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/jvmScriptEvaluation.kt index 9be530aaa04..2470501cba7 100644 --- a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/jvmScriptEvaluation.kt +++ b/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/jvmScriptEvaluation.kt @@ -7,6 +7,7 @@ package kotlin.script.experimental.jvmhost +import kotlin.reflect.KClass import kotlin.script.experimental.api.* import kotlin.script.experimental.util.PropertiesCollection @@ -30,8 +31,8 @@ open class BasicJvmScriptEvaluator : ScriptEvaluator { scriptEvaluationConfiguration: ScriptEvaluationConfiguration? ): ResultWithDiagnostics = try { - val actualEvaluationConfiguration = scriptEvaluationConfiguration ?: ScriptEvaluationConfiguration() - compiledScript.getClass(actualEvaluationConfiguration).onSuccess { scriptClass -> + val actualEvalConfiguration = scriptEvaluationConfiguration ?: ScriptEvaluationConfiguration() + compiledScript.getClass(actualEvalConfiguration).onSuccess { scriptClass -> // in the future, when (if) we'll stop to compile everything into constructor // run as SAM // return res @@ -39,55 +40,63 @@ open class BasicJvmScriptEvaluator : ScriptEvaluator { // for other scripts we need evaluation configuration with actualClassloader set, // so they are loaded in the same classloader as the "main" script val updatedEvalConfiguration = - if (actualEvaluationConfiguration.containsKey(ScriptEvaluationConfiguration.jvm.actualClassLoader)) - actualEvaluationConfiguration + if (actualEvalConfiguration.containsKey(ScriptEvaluationConfiguration.jvm.actualClassLoader)) + actualEvalConfiguration else - ScriptEvaluationConfiguration(actualEvaluationConfiguration) { + ScriptEvaluationConfiguration(actualEvalConfiguration) { ScriptEvaluationConfiguration.jvm.actualClassLoader(scriptClass.java.classLoader) } - val sharedScripts = actualEvaluationConfiguration[ScriptEvaluationConfiguration.scriptsInstancesSharingMap] + val sharedScripts = actualEvalConfiguration[ScriptEvaluationConfiguration.scriptsInstancesSharingMap] - val instanceFromShared = sharedScripts?.get(scriptClass) - - if (instanceFromShared != null) { - instanceFromShared.asSuccess(updatedEvalConfiguration) - } else { - - val args = ArrayList() - - 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 { + sharedScripts?.get(scriptClass)?.asSuccess() + ?: compiledScript.otherScripts.mapSuccess { invoke(it, updatedEvalConfiguration) }.onSuccess { importedScriptsEvalResults -> - importedScriptsEvalResults.forEach { - args.add((it.returnValue as ResultValue.Value).scriptInstance) + val refinedEvalConfiguration = + 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) { ResultWithDiagnostics.Failure(e.asDiagnostics("Error evaluating script", path = compiledScript.sourceLocationId)) } + + private fun KClass<*>.evalWithConfigAndOtherScriptsResults( + refinedEvalConfiguration: ScriptEvaluationConfiguration, + importedScriptsEvalResults: List + ): EvaluationResult { + val args = ArrayList() + + 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))