diff --git a/libraries/scripting/common/src/kotlin/script/experimental/annotations/scriptAnnotations.kt b/libraries/scripting/common/src/kotlin/script/experimental/annotations/scriptAnnotations.kt index c6c2abb56ef..fbaa4e37165 100644 --- a/libraries/scripting/common/src/kotlin/script/experimental/annotations/scriptAnnotations.kt +++ b/libraries/scripting/common/src/kotlin/script/experimental/annotations/scriptAnnotations.kt @@ -9,6 +9,7 @@ package kotlin.script.experimental.annotations import kotlin.reflect.KClass import kotlin.script.experimental.api.ScriptCompilationConfiguration +import kotlin.script.experimental.api.ScriptEvaluationConfiguration /** * The annotation for declaring a script definition (template) @@ -26,6 +27,8 @@ import kotlin.script.experimental.api.ScriptCompilationConfiguration * default - "kts" * @param compilationConfiguration an object or a class with default constructor containing initial script compilation configuration * default - {@link ScriptCompilationConfiguration#Default} + * @param evaluationConfiguration an object or a class with default constructor containing initial script evaluation configuration + * default - {@link ScriptEvaluationConfiguration#Default} * * Simple usage example: *
@@ -44,6 +47,7 @@ import kotlin.script.experimental.api.ScriptCompilationConfiguration
 annotation class KotlinScript(
     val displayName: String = "Kotlin script",
     val fileExtension: String = "kts",
-    val compilationConfiguration: KClass = ScriptCompilationConfiguration.Default::class
+    val compilationConfiguration: KClass = ScriptCompilationConfiguration.Default::class,
+    val evaluationConfiguration: KClass = ScriptEvaluationConfiguration.Default::class
 )
 
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 ac1b0cfc171..9810b59ff4e 100644
--- a/libraries/scripting/common/src/kotlin/script/experimental/api/scriptEvaluation.kt
+++ b/libraries/scripting/common/src/kotlin/script/experimental/api/scriptEvaluation.kt
@@ -17,7 +17,7 @@ interface ScriptEvaluationConfigurationKeys
  * The container for script evaluation configuration
  * For usages see actual code examples
  */
-class ScriptEvaluationConfiguration(baseEvaluationConfigurations: Iterable, body: Builder.() -> Unit) :
+open class ScriptEvaluationConfiguration(baseEvaluationConfigurations: Iterable, body: Builder.() -> Unit) :
     PropertiesCollection(Builder(baseEvaluationConfigurations).apply(body).data) {
 
     constructor(body: Builder.() -> Unit = {}) : this(emptyList(), body)
@@ -30,6 +30,8 @@ class ScriptEvaluationConfiguration(baseEvaluationConfigurations: Iterable,
-        scriptEvaluationConfiguration: ScriptEvaluationConfiguration?
+        scriptEvaluationConfiguration: ScriptEvaluationConfiguration = ScriptEvaluationConfiguration.Default
     ): ResultWithDiagnostics
 }
diff --git a/libraries/scripting/common/src/kotlin/script/experimental/host/BasicScriptingHost.kt b/libraries/scripting/common/src/kotlin/script/experimental/host/BasicScriptingHost.kt
index cef092a5337..a2c0efe5ce4 100644
--- a/libraries/scripting/common/src/kotlin/script/experimental/host/BasicScriptingHost.kt
+++ b/libraries/scripting/common/src/kotlin/script/experimental/host/BasicScriptingHost.kt
@@ -39,12 +39,12 @@ abstract class BasicScriptingHost(
      */
     open fun eval(
         script: SourceCode,
-        scriptCompilationConfiguration: ScriptCompilationConfiguration,
-        configuration: ScriptEvaluationConfiguration?
+        compilationConfiguration: ScriptCompilationConfiguration,
+        evaluationConfiguration: ScriptEvaluationConfiguration?
     ): ResultWithDiagnostics =
         runInCoroutineContext {
-            compiler(script, scriptCompilationConfiguration).onSuccess {
-                evaluator(it, configuration)
+            compiler(script, compilationConfiguration).onSuccess {
+                evaluator(it, evaluationConfiguration ?: ScriptEvaluationConfiguration.Default)
             }
         }
 }
diff --git a/libraries/scripting/common/src/kotlin/script/experimental/host/configurationFromTemplate.kt b/libraries/scripting/common/src/kotlin/script/experimental/host/configurationFromTemplate.kt
index 6af633c4ca7..6d1a0e75bd0 100644
--- a/libraries/scripting/common/src/kotlin/script/experimental/host/configurationFromTemplate.kt
+++ b/libraries/scripting/common/src/kotlin/script/experimental/host/configurationFromTemplate.kt
@@ -8,6 +8,7 @@ package kotlin.script.experimental.host
 import kotlin.reflect.KClass
 import kotlin.script.experimental.annotations.KotlinScript
 import kotlin.script.experimental.api.*
+import kotlin.script.experimental.util.PropertiesCollection
 
 private const val ERROR_MSG_PREFIX = "Unable to construct script definition: "
 
@@ -20,7 +21,7 @@ private const val SCRIPT_RUNTIME_TEMPLATES_PACKAGE = "kotlin.script.templates.st
 private abstract class DummyScriptTemplate
 
 /**
- * Creates the compilation configuration from annotated script base class
+ * Creates compilation configuration from annotated script base class
  * @param baseClassType the annotated script base class to construct the configuration from
  * @param hostConfiguration scripting host configuration properties
  * @param contextClass optional context class to extract classloading strategy from
@@ -33,48 +34,70 @@ fun createCompilationConfigurationFromTemplate(
     body: ScriptCompilationConfiguration.Builder.() -> Unit = {}
 ): ScriptCompilationConfiguration {
 
+    val templateClass: KClass<*> = baseClassType.getTemplateClass(hostConfiguration, contextClass)
+
+    val mainAnnotation: KotlinScript = templateClass.kotlinScriptAnnotation
+
+    return ScriptCompilationConfiguration(scriptConfigInstance(mainAnnotation.compilationConfiguration)) {
+        propertiesFromTemplate(templateClass, baseClassType, mainAnnotation)
+        body()
+    }
+}
+
+/**
+ * Creates evaluation configuration from annotated script base class
+ * @param baseClassType the annotated script base class to construct the configuration from
+ * @param hostConfiguration scripting host configuration properties
+ * @param contextClass optional context class to extract classloading strategy from
+ * @param body optional configuration function to add more properties to the evaluation configuration
+ */
+fun createEvaluationConfigurationFromTemplate(
+    baseClassType: KotlinType,
+    hostConfiguration: ScriptingHostConfiguration,
+    contextClass: KClass<*> = ScriptEvaluationConfiguration::class,
+    body: ScriptEvaluationConfiguration.Builder.() -> Unit = {}
+): ScriptEvaluationConfiguration {
+
+    val templateClass: KClass<*> = baseClassType.getTemplateClass(hostConfiguration, contextClass)
+
+    val mainAnnotation = templateClass.kotlinScriptAnnotation
+
+    return ScriptEvaluationConfiguration(scriptConfigInstance(mainAnnotation.evaluationConfiguration), body = body)
+}
+
+private fun ScriptCompilationConfiguration.Builder.propertiesFromTemplate(
+    templateClass: KClass<*>, baseClassType: KotlinType, mainAnnotation: KotlinScript
+) {
+    if (baseClass() == null) {
+        baseClass(if (templateClass == baseClassType.fromClass) baseClassType else KotlinType(templateClass))
+    }
+    if (fileExtension() == null) {
+        fileExtension(mainAnnotation.fileExtension)
+    }
+    if (displayName() == null) {
+        displayName(mainAnnotation.displayName)
+    }
+}
+
+private val KClass<*>.kotlinScriptAnnotation: KotlinScript
+    get() = findAnnotation()
+        ?: when (this@kotlinScriptAnnotation.qualifiedName) {
+            // transitions to the new scripting API: substituting annotations for standard templates from script-runtime
+            "$SCRIPT_RUNTIME_TEMPLATES_PACKAGE.SimpleScriptTemplate",
+            "$SCRIPT_RUNTIME_TEMPLATES_PACKAGE.ScriptTemplateWithArgs",
+            "$SCRIPT_RUNTIME_TEMPLATES_PACKAGE.ScriptTemplateWithBindings" -> DummyScriptTemplate::class.findAnnotation()
+            else -> null
+        }
+        ?: throw IllegalArgumentException("${ERROR_MSG_PREFIX}Expecting KotlinScript annotation on the ${this}")
+
+private fun KotlinType.getTemplateClass(hostConfiguration: ScriptingHostConfiguration, contextClass: KClass<*>): KClass<*> {
     val getScriptingClass = hostConfiguration[ScriptingHostConfiguration.getScriptingClass]
         ?: throw IllegalArgumentException("${ERROR_MSG_PREFIX}Expecting 'getScriptingClass' parameter in the scripting host configuration")
 
-    val baseClass: KClass<*> =
-        try {
-            getScriptingClass(baseClassType, contextClass, hostConfiguration)
-        } catch (e: Throwable) {
-            throw IllegalArgumentException("${ERROR_MSG_PREFIX}Unable to load base class $baseClassType", e)
-        }
-    val loadedBaseClassType = if (baseClass == baseClassType.fromClass) baseClassType else KotlinType(baseClass)
-
-    val mainAnnotation = baseClass.findAnnotation()
-        ?: run {
-            // transitions to the new scripting API:
-            // substituting annotations for standard templates from script-runtime
-            when (baseClass.qualifiedName) {
-                "$SCRIPT_RUNTIME_TEMPLATES_PACKAGE.SimpleScriptTemplate",
-                "$SCRIPT_RUNTIME_TEMPLATES_PACKAGE.ScriptTemplateWithArgs",
-                "$SCRIPT_RUNTIME_TEMPLATES_PACKAGE.ScriptTemplateWithBindings" -> DummyScriptTemplate::class
-                else -> null
-            }?.findAnnotation()
-        }
-        ?: throw IllegalArgumentException("${ERROR_MSG_PREFIX}Expecting KotlinScript annotation on the $baseClass")
-
-    fun scriptConfigInstance(kclass: KClass): ScriptCompilationConfiguration = try {
-        kclass.objectInstance ?: kclass.createInstance()
+    return try {
+        getScriptingClass(this, contextClass, hostConfiguration)
     } catch (e: Throwable) {
-        throw IllegalArgumentException("$ILLEGAL_CONFIG_ANN_ARG: ${e.message}", e)
-    }
-
-    return ScriptCompilationConfiguration(scriptConfigInstance(mainAnnotation.compilationConfiguration)) {
-        if (baseClass() == null) {
-            baseClass(loadedBaseClassType)
-        }
-        if (fileExtension() == null) {
-            fileExtension(mainAnnotation.fileExtension)
-        }
-        if (displayName() == null) {
-            displayName(mainAnnotation.displayName)
-        }
-
-        body()
+        throw IllegalArgumentException("${ERROR_MSG_PREFIX}Unable to load base class ${this}", e)
     }
 }
 
@@ -90,3 +113,10 @@ private fun  KClass.createInstance(): T {
     @Suppress("UNCHECKED_CAST")
     return noArgsConstructor.newInstance() as T
 }
+
+private fun  scriptConfigInstance(kclass: KClass): T = try {
+    kclass.objectInstance ?: kclass.createInstance()
+} catch (e: Throwable) {
+    throw IllegalArgumentException("$ILLEGAL_CONFIG_ANN_ARG: ${e.message}", e)
+}
+
diff --git a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/BasicJvmScriptingHost.kt b/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/BasicJvmScriptingHost.kt
index 03c458b5b15..16ab08ad192 100644
--- a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/BasicJvmScriptingHost.kt
+++ b/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/BasicJvmScriptingHost.kt
@@ -5,19 +5,30 @@
 
 package kotlin.script.experimental.jvmhost
 
-import kotlin.script.experimental.api.KotlinType
-import kotlin.script.experimental.api.ScriptCompilationConfiguration
-import kotlin.script.experimental.api.ScriptEvaluator
+import kotlin.script.experimental.api.*
 import kotlin.script.experimental.host.ScriptingHostConfiguration
 import kotlin.script.experimental.host.createCompilationConfigurationFromTemplate
 import kotlin.script.experimental.host.BasicScriptingHost
+import kotlin.script.experimental.host.createEvaluationConfigurationFromTemplate
 import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
 
 open class BasicJvmScriptingHost(
-    hostConfiguration: ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration,
+    val hostConfiguration: ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration,
     compiler: JvmScriptCompiler = JvmScriptCompiler(hostConfiguration),
     evaluator: ScriptEvaluator = BasicJvmScriptEvaluator()
-) : BasicScriptingHost(compiler, evaluator)
+) : BasicScriptingHost(compiler, evaluator) {
+
+    inline fun  evalWithTemplate(
+        script: SourceCode,
+        noinline compilation: ScriptCompilationConfiguration.Builder.() -> Unit = {},
+        noinline evaluation: ScriptEvaluationConfiguration.Builder.() -> Unit = {}
+    ): ResultWithDiagnostics =
+        eval(
+            script,
+            createJvmCompilationConfigurationFromTemplate(hostConfiguration, compilation),
+            createJvmEvaluationConfigurationFromTemplate(hostConfiguration, evaluation)
+        )
+}
 
 
 inline fun  createJvmCompilationConfigurationFromTemplate(
@@ -29,3 +40,13 @@ inline fun  createJvmCompilationConfigurationFromTemplate(
     ScriptCompilationConfiguration::class,
     body
 )
+
+inline fun  createJvmEvaluationConfigurationFromTemplate(
+    hostConfiguration: ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration,
+    noinline body: ScriptEvaluationConfiguration.Builder.() -> Unit = {}
+): ScriptEvaluationConfiguration = createEvaluationConfigurationFromTemplate(
+    KotlinType(T::class),
+    hostConfiguration,
+    ScriptCompilationConfiguration::class,
+    body
+)
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 2470501cba7..ce31d7cdb68 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
@@ -28,11 +28,10 @@ open class BasicJvmScriptEvaluator : ScriptEvaluator {
 
     override suspend operator fun invoke(
         compiledScript: CompiledScript<*>,
-        scriptEvaluationConfiguration: ScriptEvaluationConfiguration?
+        scriptEvaluationConfiguration: ScriptEvaluationConfiguration
     ): ResultWithDiagnostics =
         try {
-            val actualEvalConfiguration = scriptEvaluationConfiguration ?: ScriptEvaluationConfiguration()
-            compiledScript.getClass(actualEvalConfiguration).onSuccess { scriptClass ->
+            compiledScript.getClass(scriptEvaluationConfiguration).onSuccess { scriptClass ->
                 // in the future, when (if) we'll stop to compile everything into constructor
                 // run as SAM
                 // return res
@@ -40,14 +39,14 @@ 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 (actualEvalConfiguration.containsKey(ScriptEvaluationConfiguration.jvm.actualClassLoader))
-                        actualEvalConfiguration
+                    if (scriptEvaluationConfiguration.containsKey(ScriptEvaluationConfiguration.jvm.actualClassLoader))
+                        scriptEvaluationConfiguration
                     else
-                        ScriptEvaluationConfiguration(actualEvalConfiguration) {
+                        ScriptEvaluationConfiguration(scriptEvaluationConfiguration) {
                             ScriptEvaluationConfiguration.jvm.actualClassLoader(scriptClass.java.classLoader)
                         }
 
-                val sharedScripts = actualEvalConfiguration[ScriptEvaluationConfiguration.scriptsInstancesSharingMap]
+                val sharedScripts = scriptEvaluationConfiguration[ScriptEvaluationConfiguration.scriptsInstancesSharingMap]
 
                 sharedScripts?.get(scriptClass)?.asSuccess()
                     ?: compiledScript.otherScripts.mapSuccess {
diff --git a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/jvmScriptSaving.kt b/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/jvmScriptSaving.kt
index 3ed42180365..95be84054f5 100644
--- a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/jvmScriptSaving.kt
+++ b/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/jvmScriptSaving.kt
@@ -20,7 +20,7 @@ open class BasicJvmScriptClassFilesGenerator(val outputDir: File) : ScriptEvalua
 
     override suspend operator fun invoke(
         compiledScript: CompiledScript<*>,
-        scriptEvaluationConfiguration: ScriptEvaluationConfiguration?
+        scriptEvaluationConfiguration: ScriptEvaluationConfiguration
     ): ResultWithDiagnostics {
         try {
             if (compiledScript !is KJvmCompiledScript<*>)
@@ -48,7 +48,7 @@ open class BasicJvmScriptJarGenerator(val outputJar: File) : ScriptEvaluator {
 
     override suspend operator fun invoke(
         compiledScript: CompiledScript<*>,
-        scriptEvaluationConfiguration: ScriptEvaluationConfiguration?
+        scriptEvaluationConfiguration: ScriptEvaluationConfiguration
     ): ResultWithDiagnostics {
         try {
             if (compiledScript !is KJvmCompiledScript<*>)
diff --git a/libraries/scripting/jvm-host/test/kotlin/script/experimental/jvmhost/test/ScriptingHostTest.kt b/libraries/scripting/jvm-host/test/kotlin/script/experimental/jvmhost/test/ScriptingHostTest.kt
index 686e5905e39..9a52b6b9e77 100644
--- a/libraries/scripting/jvm-host/test/kotlin/script/experimental/jvmhost/test/ScriptingHostTest.kt
+++ b/libraries/scripting/jvm-host/test/kotlin/script/experimental/jvmhost/test/ScriptingHostTest.kt
@@ -37,6 +37,11 @@ class ScriptingHostTest : TestCase() {
             evalScript("println(\"$greeting\")").throwOnFailure()
         }
         Assert.assertEquals(greeting, output)
+        // another API
+        val output2 = captureOut {
+            BasicJvmScriptingHost().evalWithTemplate("println(\"$greeting\")".toScriptSource()).throwOnFailure()
+        }
+        Assert.assertEquals(greeting, output2)
     }
 
     @Test
@@ -276,7 +281,7 @@ class ScriptingHostTest : TestCase() {
             runBlocking {
                 compiler(script.toScriptSource(), scriptCompilationConfiguration).onSuccess {
                     compiledScript = it
-                    evaluator(it, null)
+                    evaluator(it)
                 }.throwOnFailure()
             }
         }.lines()
@@ -296,7 +301,7 @@ class ScriptingHostTest : TestCase() {
 
         val output2 = captureOut {
             runBlocking {
-                evaluator(cachedScript!!, null).throwOnFailure()
+                evaluator(cachedScript!!).throwOnFailure()
             }
         }.lines()
         Assert.assertEquals(output, output2)