diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt index aad36d7dfa4..9a35458b42e 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt @@ -49,6 +49,9 @@ class K2JVMCompilerArguments : CommonCompilerArguments() { @Argument(value = "-no-reflect", description = "Don't automatically include Kotlin reflection into the classpath") var noReflect: Boolean by FreezableVar(false) + @Argument(value = "-expression", description = "Evaluate the given string as a Kotlin script") + var expressions: Array? by FreezableVar(null) + @Argument( value = "-script-templates", valueDescription = "", diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt index df87436a5e7..a0358e15d82 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt @@ -35,9 +35,7 @@ import org.jetbrains.kotlin.cli.jvm.compiler.CompileEnvironmentUtil import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler -import org.jetbrains.kotlin.cli.jvm.plugins.PluginCliParser import org.jetbrains.kotlin.codegen.CompilationException -import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar import org.jetbrains.kotlin.config.* import org.jetbrains.kotlin.incremental.components.ExpectActualTracker import org.jetbrains.kotlin.incremental.components.LookupTracker @@ -48,9 +46,7 @@ import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil import org.jetbrains.kotlin.modules.JavaRootPath import org.jetbrains.kotlin.utils.KotlinPaths -import org.jetbrains.kotlin.utils.PathUtil import java.io.File -import java.util.* class K2JVMCompiler : CLICompiler() { @@ -80,7 +76,9 @@ class K2JVMCompiler : CLICompiler() { configuration.configureStandardLibs(paths, arguments) configuration.configureAdvancedJvmOptions(arguments) - if (arguments.buildFile == null && !arguments.version && !arguments.allowNoSourceFiles && (arguments.script || arguments.freeArgs.isEmpty())) { + if (arguments.buildFile == null && !arguments.version && !arguments.allowNoSourceFiles && + (arguments.script || arguments.expressions != null || arguments.freeArgs.isEmpty())) { + // script or repl if (arguments.script && arguments.freeArgs.isEmpty()) { messageCollector.report(ERROR, "Specify script source path to evaluate") @@ -94,7 +92,7 @@ class K2JVMCompiler : CLICompiler() { ) projectEnvironment.registerExtensionsFromPlugins(configuration) - if (arguments.script) { + if (arguments.script || arguments.expressions != null) { val scriptingEvaluator = ScriptEvaluationExtension.getInstances(projectEnvironment.project).find { it.isAccepted(arguments) } if (scriptingEvaluator == null) { messageCollector.report(ERROR, "Unable to evaluate script, no scripting plugin loaded") diff --git a/compiler/testData/cli/jvm/help.out b/compiler/testData/cli/jvm/help.out index e3dc1f09062..30211de5171 100644 --- a/compiler/testData/cli/jvm/help.out +++ b/compiler/testData/cli/jvm/help.out @@ -2,6 +2,7 @@ Usage: kotlinc-jvm where possible options include: -classpath (-cp) List of directories and JAR/ZIP archives to search for user class files -d Destination for generated class files + -expression Evaluate the given string as a Kotlin script -include-runtime Include Kotlin runtime into the resulting JAR -java-parameters Generate metadata for Java 1.8 reflection on method parameters -jdk-home Include a custom JDK from the specified location into the classpath instead of the default JAVA_HOME diff --git a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/AbstractScriptEvaluationExtension.kt b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/AbstractScriptEvaluationExtension.kt index c7361ea3e29..001392ff17f 100644 --- a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/AbstractScriptEvaluationExtension.kt +++ b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/AbstractScriptEvaluationExtension.kt @@ -10,8 +10,10 @@ import kotlinx.coroutines.runBlocking import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.common.ExitCode import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments +import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments import org.jetbrains.kotlin.cli.common.extensions.ScriptEvaluationExtension import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity +import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys @@ -19,6 +21,7 @@ import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider import java.io.File import java.io.PrintStream import kotlin.script.experimental.api.* +import kotlin.script.experimental.host.StringScriptSource import kotlin.script.experimental.host.toScriptSource abstract class AbstractScriptEvaluationExtension : ScriptEvaluationExtension { @@ -46,37 +49,59 @@ abstract class AbstractScriptEvaluationExtension : ScriptEvaluationExtension { messageCollector.report(CompilerMessageSeverity.ERROR, "Unable to process the script, scripting plugin is not configured") return ExitCode.COMPILATION_ERROR } - val sourcePath = arguments.freeArgs.first() setupScriptConfiguration(configuration) + val script = when { + arguments is K2JVMCompilerArguments && arguments.expressions != null -> { + StringScriptSource(arguments.expressions!!.joinToString("\n"), "script.kts") + } + arguments.script -> { + val scriptFile = File(arguments.freeArgs.first()) + val script = scriptFile.toScriptSource() + + if (scriptFile.isDirectory || !scriptDefinitionProvider.isScript(script)) { + val extensionHint = + if (configuration.get(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS)?.let { it.size == 1 && it.first().isDefault } == true) " (.kts)" + else "" + messageCollector.report(CompilerMessageSeverity.ERROR, "Specify path to the script file$extensionHint as the first argument") + return ExitCode.COMPILATION_ERROR + } + script + } + else -> { + messageCollector.report(CompilerMessageSeverity.ERROR, "Illegal set of arguments: either -script or -expression arguments expected at this point") + return ExitCode.COMPILATION_ERROR + } + } + val environment = createEnvironment(projectEnvironment, configuration) if (messageCollector.hasErrors()) return ExitCode.COMPILATION_ERROR - val scriptFile = File(sourcePath) - val script = scriptFile.toScriptSource() - - if (scriptFile.isDirectory || !scriptDefinitionProvider.isScript(script)) { - val extensionHint = - if (configuration.get(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS)?.let { it.size == 1 && it.first().isDefault } == true) " (.kts)" - else "" - messageCollector.report(CompilerMessageSeverity.ERROR, "Specify path to the script file$extensionHint as the first argument") - return ExitCode.COMPILATION_ERROR - } - val definition = scriptDefinitionProvider.findDefinition(script) ?: scriptDefinitionProvider.getDefaultDefinition() + val scriptCompilationConfiguration = definition.compilationConfiguration + val scriptArgs = - if (arguments.freeArgs.isNotEmpty()) arguments.freeArgs.subList(1, arguments.freeArgs.size) - else emptyList() + if (arguments.script) arguments.freeArgs.subList(1, arguments.freeArgs.size) + else arguments.freeArgs val evaluationConfiguration = definition.evaluationConfiguration.with { constructorArgs(scriptArgs.toTypedArray()) platformEvaluationConfiguration() } - val scriptCompilationConfiguration = definition.compilationConfiguration + return doEval(script, scriptCompilationConfiguration, evaluationConfiguration, environment, messageCollector) + } + + private fun doEval( + script: SourceCode, + scriptCompilationConfiguration: ScriptCompilationConfiguration, + evaluationConfiguration: ScriptEvaluationConfiguration, + environment: KotlinCoreEnvironment, + messageCollector: MessageCollector + ): ExitCode { val scriptCompiler = createScriptCompiler(environment) return runBlocking { diff --git a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/JvmCliScriptEvaluationExtension.kt b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/JvmCliScriptEvaluationExtension.kt index ba6a8378cfc..1c2ad99a71e 100644 --- a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/JvmCliScriptEvaluationExtension.kt +++ b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/JvmCliScriptEvaluationExtension.kt @@ -13,7 +13,8 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.JVMConfigurationKeys import org.jetbrains.kotlin.scripting.compiler.plugin.impl.ScriptJvmCompilerFromEnvironment -import kotlin.script.experimental.api.* +import kotlin.script.experimental.api.ScriptEvaluationConfiguration +import kotlin.script.experimental.api.ScriptEvaluator import kotlin.script.experimental.jvm.BasicJvmScriptEvaluator import kotlin.script.experimental.jvm.baseClassLoader import kotlin.script.experimental.jvm.jvm @@ -46,6 +47,6 @@ class JvmCliScriptEvaluationExtension : AbstractScriptEvaluationExtension() { } override fun isAccepted(arguments: CommonCompilerArguments): Boolean = - arguments is K2JVMCompilerArguments && arguments.script + arguments is K2JVMCompilerArguments && (arguments.script || arguments.expressions != null) } 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 2b7c25e8c87..9572187142c 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 @@ -6,7 +6,6 @@ package org.jetbrains.kotlin.scripting.compiler.plugin.impl import org.jetbrains.kotlin.cli.common.arguments.Argument -import org.jetbrains.kotlin.cli.common.arguments.ArgumentParseErrors import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity @@ -100,6 +99,7 @@ internal fun reportArgumentsIgnoredGenerally( K2JVMCompilerArguments::commonSources, K2JVMCompilerArguments::allWarningsAsErrors, K2JVMCompilerArguments::script, + K2JVMCompilerArguments::expressions, K2JVMCompilerArguments::scriptTemplates, K2JVMCompilerArguments::scriptResolverEnvironment, K2JVMCompilerArguments::disableStandardScript, diff --git a/plugins/scripting/scripting-compiler/tests/org/jetbrains/kotlin/scripting/compiler/plugin/ScriptingWithCliCompilerTest.kt b/plugins/scripting/scripting-compiler/tests/org/jetbrains/kotlin/scripting/compiler/plugin/ScriptingWithCliCompilerTest.kt index 474986c4674..e34d82eead6 100644 --- a/plugins/scripting/scripting-compiler/tests/org/jetbrains/kotlin/scripting/compiler/plugin/ScriptingWithCliCompilerTest.kt +++ b/plugins/scripting/scripting-compiler/tests/org/jetbrains/kotlin/scripting/compiler/plugin/ScriptingWithCliCompilerTest.kt @@ -46,6 +46,21 @@ class ScriptingWithCliCompilerTest { ) } + @Test + fun testExpression() { + runWithK2JVMCompiler( + arrayOf( + "-expression", + "val x = 7; println(x * 6); for (arg in args) println(arg)", + "--", + "hi", + "there" + ), + listOf("42", "hi", "there") + ) + } + + private fun getMainKtsClassPath(): List { return listOf( File("dist/kotlinc/lib/kotlin-main-kts.jar").also { diff --git a/plugins/scripting/scripting-compiler/tests/org/jetbrains/kotlin/scripting/compiler/plugin/testUtil.kt b/plugins/scripting/scripting-compiler/tests/org/jetbrains/kotlin/scripting/compiler/plugin/testUtil.kt index 3e893a1504e..e25b76e6cb6 100644 --- a/plugins/scripting/scripting-compiler/tests/org/jetbrains/kotlin/scripting/compiler/plugin/testUtil.kt +++ b/plugins/scripting/scripting-compiler/tests/org/jetbrains/kotlin/scripting/compiler/plugin/testUtil.kt @@ -112,15 +112,26 @@ fun runWithK2JVMCompiler( add("-script") add(scriptPath) } + runWithK2JVMCompiler(args.toTypedArray(), expectedOutPatterns, expectedExitCode) +} + +fun runWithK2JVMCompiler( + args: Array, + expectedOutPatterns: List = emptyList(), + expectedExitCode: Int = 0 +) { val (out, err, ret) = captureOutErrRet { CLITool.doMainNoExit( K2JVMCompiler(), - args.toTypedArray() + args ) } try { val outLines = out.lines() - Assert.assertEquals(expectedOutPatterns.size, outLines.size) + Assert.assertEquals( + "Expecting pattern:\n ${expectedOutPatterns.joinToString("\n ")}\nGot:\n ${outLines.joinToString("\n ")}", + expectedOutPatterns.size, outLines.size + ) for ((expectedPattern, actualLine) in expectedOutPatterns.zip(outLines)) { Assert.assertTrue( "line \"$actualLine\" do not match with expected pattern \"$expectedPattern\"",