Scripting: support compiler plugins in hosted script compiler

Now the plugin-related compiler arguments are respected then used in
the compiler configuration (only in the static part, not supported
if set in a refinement callback)
Note that the "embeddable" version of the plugin should be used if
embeddable compiler is used.
#KT-54095 fixed
diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/common/CLICompiler.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/common/CLICompiler.kt
index 8abbeef..c424503 100644
--- a/compiler/cli/src/org/jetbrains/kotlin/cli/common/CLICompiler.kt
+++ b/compiler/cli/src/org/jetbrains/kotlin/cli/common/CLICompiler.kt
@@ -173,43 +173,8 @@
         val pluginConfigurations = arguments.pluginConfigurations.orEmpty().toMutableList()
         val messageCollector = configuration.getNotNull(MESSAGE_COLLECTOR_KEY)
 
-        for (classpath in pluginClasspaths) {
-            if (!File(classpath).exists()) {
-                messageCollector.report(ERROR, "Plugin classpath entry points to a non-existent location: $classpath")
-            }
-        }
-
-        if (pluginConfigurations.isNotEmpty()) {
-            var hasErrors = false
-            messageCollector.report(WARNING, "Argument -Xcompiler-plugin is experimental")
-            if (!arguments.useK2) {
-                hasErrors = true
-                messageCollector.report(
-                    ERROR,
-                    "-Xcompiler-plugin argument is allowed only for for K2 compiler. Please use -Xplugin argument or enable -Xuse-k2"
-                )
-            }
-            if (pluginClasspaths.isNotEmpty() || pluginOptions.isNotEmpty()) {
-                hasErrors = true
-                val message = buildString {
-                    appendLine("Mixing legacy and modern plugin arguments is prohibited. Please use only one syntax")
-                    appendLine("Legacy arguments:")
-                    if (pluginClasspaths.isNotEmpty()) {
-                        appendLine("  -Xplugin=${pluginClasspaths.joinToString(",")}")
-                    }
-                    pluginOptions.forEach {
-                        appendLine("  -P $it")
-                    }
-                    appendLine("Modern arguments:")
-                    pluginConfigurations.forEach {
-                        appendLine("  -Xcompiler-plugin=$it")
-                    }
-                }
-                messageCollector.report(ERROR, message)
-            }
-            if (hasErrors) {
-                return INTERNAL_ERROR
-            }
+        if (!checkPluginsArguments(messageCollector, arguments.useK2, pluginClasspaths, pluginOptions, pluginConfigurations)) {
+            return INTERNAL_ERROR
         }
 
         if (!arguments.disableDefaultScriptingPlugin) {
@@ -259,3 +224,49 @@
         }
 }
 
+fun checkPluginsArguments(
+    messageCollector: MessageCollector,
+    useK2: Boolean,
+    pluginClasspaths: List<String>,
+    pluginOptions: List<String>,
+    pluginConfigurations: MutableList<String>
+): Boolean {
+    var hasErrors = false
+
+    for (classpath in pluginClasspaths) {
+        if (!File(classpath).exists()) {
+            messageCollector.report(ERROR, "Plugin classpath entry points to a non-existent location: $classpath")
+        }
+    }
+
+    if (pluginConfigurations.isNotEmpty()) {
+        messageCollector.report(WARNING, "Argument -Xcompiler-plugin is experimental")
+        if (!useK2) {
+            hasErrors = true
+            messageCollector.report(
+                ERROR,
+                "-Xcompiler-plugin argument is allowed only for for K2 compiler. Please use -Xplugin argument or enable -Xuse-k2"
+            )
+        }
+        if (pluginClasspaths.isNotEmpty() || pluginOptions.isNotEmpty()) {
+            hasErrors = true
+            val message = buildString {
+                appendLine("Mixing legacy and modern plugin arguments is prohibited. Please use only one syntax")
+                appendLine("Legacy arguments:")
+                if (pluginClasspaths.isNotEmpty()) {
+                    appendLine("  -Xplugin=${pluginClasspaths.joinToString(",")}")
+                }
+                pluginOptions.forEach {
+                    appendLine("  -P $it")
+                }
+                appendLine("Modern arguments:")
+                pluginConfigurations.forEach {
+                    appendLine("  -Xcompiler-plugin=$it")
+                }
+            }
+            messageCollector.report(ERROR, message)
+        }
+    }
+    return !hasErrors
+}
+
diff --git a/libraries/tools/kotlin-main-kts-test/build.gradle.kts b/libraries/tools/kotlin-main-kts-test/build.gradle.kts
index 62459f4..23834f6 100644
--- a/libraries/tools/kotlin-main-kts-test/build.gradle.kts
+++ b/libraries/tools/kotlin-main-kts-test/build.gradle.kts
@@ -5,6 +5,8 @@
     kotlin("jvm")
 }
 
+val kotlinxSerializationGradlePluginClasspath by configurations.creating
+
 dependencies {
     testApi(project(":kotlin-main-kts"))
     testCompileOnly(project(":compiler:cli"))
@@ -13,6 +15,7 @@
     testApi(commonDependency("junit"))
     testApi(projectTests(":kotlin-scripting-compiler")) { isTransitive = false }
     testImplementation(project(":kotlin-compiler-embeddable"))
+    kotlinxSerializationGradlePluginClasspath(project(":kotlin-serialization")) { isTransitive = false }
 }
 
 sourceSets {
@@ -21,7 +24,11 @@
 }
 
 projectTest(parallel = true) {
-    dependsOn(":dist")
+    dependsOn(":dist", ":kotlin-serialization:jar")
+    val localKotlinxSerializationPluginClasspath: FileCollection = kotlinxSerializationGradlePluginClasspath
+    doFirst {
+        systemProperty("kotlin.script.test.kotlinx.serialization.plugin.classpath", localKotlinxSerializationPluginClasspath.asPath)
+    }
     workingDir = rootDir
 }
 
diff --git a/libraries/tools/kotlin-main-kts-test/test/org/jetbrains/kotlin/mainKts/test/mainKtsTest.kt b/libraries/tools/kotlin-main-kts-test/test/org/jetbrains/kotlin/mainKts/test/mainKtsTest.kt
index c39e9a7..6235439 100644
--- a/libraries/tools/kotlin-main-kts-test/test/org/jetbrains/kotlin/mainKts/test/mainKtsTest.kt
+++ b/libraries/tools/kotlin-main-kts-test/test/org/jetbrains/kotlin/mainKts/test/mainKtsTest.kt
@@ -5,15 +5,17 @@
 package org.jetbrains.kotlin.mainKts.test
 
 import org.jetbrains.kotlin.mainKts.COMPILED_SCRIPTS_CACHE_DIR_PROPERTY
-import org.jetbrains.kotlin.mainKts.impl.Directories
 import org.jetbrains.kotlin.mainKts.MainKtsScript
 import org.jetbrains.kotlin.mainKts.SCRIPT_FILE_LOCATION_DEFAULT_VARIABLE_NAME
+import org.jetbrains.kotlin.mainKts.impl.Directories
 import org.jetbrains.kotlin.scripting.compiler.plugin.assertTrue
 import org.junit.Assert
 import org.junit.Assert.assertEquals
 import org.junit.Ignore
 import org.junit.Test
-import java.io.*
+import java.io.ByteArrayOutputStream
+import java.io.File
+import java.io.PrintStream
 import java.util.*
 import kotlin.script.experimental.api.*
 import kotlin.script.experimental.host.toScriptSource
@@ -24,21 +26,31 @@
 
 fun evalFile(scriptFile: File, cacheDir: File? = null): ResultWithDiagnostics<EvaluationResult> =
     withProperty(COMPILED_SCRIPTS_CACHE_DIR_PROPERTY, cacheDir?.absolutePath ?: "") {
-        val scriptDefinition = createJvmScriptDefinitionFromTemplate<MainKtsScript>(
-            evaluation = {
-                jvm {
-                    baseClassLoader(null)
-                }
-                constructorArgs(emptyArray<String>())
-                enableScriptsInstancesSharing()
-            }
-        )
-
-        BasicJvmScriptingHost().eval(
-            scriptFile.toScriptSource(), scriptDefinition.compilationConfiguration, scriptDefinition.evaluationConfiguration
-        )
+        evalFileWithConfigurations(scriptFile)
     }
 
+fun evalFileWithConfigurations(
+    scriptFile: File,
+    compilation: ScriptCompilationConfiguration.Builder.() -> Unit = {},
+    evaluation: ScriptEvaluationConfiguration.Builder.() -> Unit = {}
+): ResultWithDiagnostics<EvaluationResult> {
+    val scriptDefinition = createJvmScriptDefinitionFromTemplate<MainKtsScript>(
+        compilation = compilation,
+        evaluation = {
+            evaluation()
+            jvm {
+                baseClassLoader(null)
+            }
+            constructorArgs(emptyArray<String>())
+            enableScriptsInstancesSharing()
+        }
+    )
+
+    return BasicJvmScriptingHost().eval(
+        scriptFile.toScriptSource(), scriptDefinition.compilationConfiguration, scriptDefinition.evaluationConfiguration
+    )
+}
+
 
 const val TEST_DATA_ROOT = "libraries/tools/kotlin-main-kts-test/testData"
 val OUT_FROM_IMPORT_TEST = listOf("Hi from common", "Hi from middle", "Hi from main", "sharedVar == 5")
@@ -231,9 +243,32 @@
         assertSucceeded(res)
     }
 
+    @Test
+    fun testHelloSerialization() {
+        // the embeddable plugin is needed for this test, because embeddable compiler is used.
+        // furtunately appropriate gradle plugin can serve as an embeddable compiler plugin now
+        // so, the path to it is prepared in the build file
+        val serializationPluginClasspath = System.getProperty("kotlin.script.test.kotlinx.serialization.plugin.classpath")!!
+        val out = captureOut {
+            val res = evalFileWithConfigurations(
+                File("$TEST_DATA_ROOT/hello-kotlinx-serialization.main.kts"),
+                compilation = {
+                    compilerOptions(
+                        "-Xplugin", serializationPluginClasspath
+                    )
+                }
+            )
+            assertSucceeded(res)
+        }.lines()
+        assertEquals(
+            listOf("""{"firstName":"James","lastName":"Bond"}""", "User(firstName=James, lastName=Bond)"),
+            out
+        )
+    }
+
     private fun assertSucceeded(res: ResultWithDiagnostics<EvaluationResult>) {
         Assert.assertTrue(
-            "test failed:\n  ${res.reports.joinToString("\n  ") { it.message + if (it.exception == null) "" else ": ${it.exception}" }}",
+            "test failed:\n  ${res.reports.joinToString("\n  ") { it.severity.name + ": " + it.message + if (it.exception == null) "" else ": ${it.exception}" }}",
             res is ResultWithDiagnostics.Success
         )
     }
diff --git a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/compilationContext.kt b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/compilationContext.kt
index eb18d9f..0623632 100644
--- a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/compilationContext.kt
+++ b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/compilationContext.kt
@@ -16,6 +16,7 @@
 import org.jetbrains.kotlin.cli.common.messages.MessageCollector
 import org.jetbrains.kotlin.cli.common.reportArgumentParseProblems
 import org.jetbrains.kotlin.cli.common.setupCommonArguments
+import org.jetbrains.kotlin.cli.common.checkPluginsArguments
 import org.jetbrains.kotlin.cli.jvm.*
 import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
 import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
@@ -23,6 +24,7 @@
 import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
 import org.jetbrains.kotlin.cli.jvm.config.configureJdkClasspathRoots
 import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
+import org.jetbrains.kotlin.cli.jvm.plugins.PluginCliParser
 import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
 import org.jetbrains.kotlin.config.CommonConfigurationKeys
 import org.jetbrains.kotlin.config.CompilerConfiguration
@@ -199,13 +201,6 @@
     val initialScriptCompilationConfiguration =
         scriptCompilationConfiguration.withUpdatesFromCompilerConfiguration(kotlinCompilerConfiguration)
 
-    kotlinCompilerConfiguration.add(
-        ScriptingConfigurationKeys.SCRIPT_DEFINITIONS,
-        ScriptDefinition.FromConfigurations(hostConfiguration, scriptCompilationConfiguration, null)
-    )
-
-    kotlinCompilerConfiguration.loadPlugins()
-
     initialScriptCompilationConfiguration[ScriptCompilationConfiguration.compilerOptions]?.let { compilerOptions ->
         kotlinCompilerConfiguration.updateWithCompilerOptions(compilerOptions, messageCollector, ignoredOptionsReportingState, false)
     }
@@ -339,6 +334,22 @@
         configureJdkClasspathRoots()
 
         put(JVMConfigurationKeys.USE_FAST_JAR_FILE_SYSTEM, true)
+
+        add(
+            ScriptingConfigurationKeys.SCRIPT_DEFINITIONS,
+            ScriptDefinition.FromConfigurations(hostConfiguration, scriptCompilationConfiguration, null)
+        )
+
+        val pluginClasspaths = baseArguments.pluginClasspaths?.asList().orEmpty()
+        val pluginOptions = baseArguments.pluginOptions?.asList().orEmpty()
+        val pluginConfigurations = baseArguments.pluginConfigurations.orEmpty().toMutableList()
+
+        checkPluginsArguments(messageCollector, false, pluginClasspaths, pluginOptions, pluginConfigurations)
+        if (pluginClasspaths.isNotEmpty() || pluginConfigurations.isNotEmpty()) {
+            PluginCliParser.loadPluginsSafe(pluginClasspaths, pluginOptions, pluginConfigurations, this)
+        } else {
+            loadPlugins()
+        }
     }
 }
 
diff --git a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/errorReporting.kt b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/errorReporting.kt
index 414d642..9d24bc4 100644
--- a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/errorReporting.kt
+++ b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/errorReporting.kt
@@ -160,7 +160,6 @@
         K2JVMCompilerArguments::disableStandardScript,
         K2JVMCompilerArguments::defaultScriptExtension,
         K2JVMCompilerArguments::disableDefaultScriptingPlugin,
-        K2JVMCompilerArguments::pluginClasspaths,
         K2JVMCompilerArguments::useJavac,
         K2JVMCompilerArguments::compileJava,
         K2JVMCompilerArguments::reportPerf,
@@ -181,7 +180,10 @@
         K2JVMCompilerArguments::javaModulePath,
         K2JVMCompilerArguments::classpath,
         K2JVMCompilerArguments::noStdlib,
-        K2JVMCompilerArguments::noReflect
+        K2JVMCompilerArguments::noReflect,
+        K2JVMCompilerArguments::pluginClasspaths,
+        K2JVMCompilerArguments::pluginOptions,
+        K2JVMCompilerArguments::pluginConfigurations,
     )
 
 
diff --git a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/loadCompilerPlugins.kt b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/loadCompilerPlugins.kt
index 21b70cf..12faf74 100644
--- a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/loadCompilerPlugins.kt
+++ b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/loadCompilerPlugins.kt
@@ -30,8 +30,7 @@
         ScriptingCommandLineProcessor::class.java.name
     )
 
-internal fun CompilerConfiguration.loadPlugins() {
-    val classLoader = CompilerConfiguration::class.java.classLoader
+internal fun CompilerConfiguration.loadPlugins(classLoader: ClassLoader = CompilerConfiguration::class.java.classLoader) {
     val registrars =
         classLoader.loadServices<ComponentRegistrar>(scriptCompilationDisabledPlugins, SCRIPT_COMPILATION_DISABLE_PLUGINS_PROPERTY)
     addAll(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, registrars)