Implement support for expression evaluation in jvm cli compiler
This commit is contained in:
+3
@@ -49,6 +49,9 @@ class K2JVMCompilerArguments : CommonCompilerArguments() {
|
|||||||
@Argument(value = "-no-reflect", description = "Don't automatically include Kotlin reflection into the classpath")
|
@Argument(value = "-no-reflect", description = "Don't automatically include Kotlin reflection into the classpath")
|
||||||
var noReflect: Boolean by FreezableVar(false)
|
var noReflect: Boolean by FreezableVar(false)
|
||||||
|
|
||||||
|
@Argument(value = "-expression", description = "Evaluate the given string as a Kotlin script")
|
||||||
|
var expressions: Array<String>? by FreezableVar(null)
|
||||||
|
|
||||||
@Argument(
|
@Argument(
|
||||||
value = "-script-templates",
|
value = "-script-templates",
|
||||||
valueDescription = "<fully qualified class name[,]>",
|
valueDescription = "<fully qualified class name[,]>",
|
||||||
|
|||||||
@@ -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.EnvironmentConfigFiles
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
|
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.codegen.CompilationException
|
||||||
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
|
||||||
import org.jetbrains.kotlin.config.*
|
import org.jetbrains.kotlin.config.*
|
||||||
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
|
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
|
||||||
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
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.metadata.jvm.deserialization.JvmProtoBufUtil
|
||||||
import org.jetbrains.kotlin.modules.JavaRootPath
|
import org.jetbrains.kotlin.modules.JavaRootPath
|
||||||
import org.jetbrains.kotlin.utils.KotlinPaths
|
import org.jetbrains.kotlin.utils.KotlinPaths
|
||||||
import org.jetbrains.kotlin.utils.PathUtil
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
|
class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
|
||||||
|
|
||||||
@@ -80,7 +76,9 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
|
|||||||
configuration.configureStandardLibs(paths, arguments)
|
configuration.configureStandardLibs(paths, arguments)
|
||||||
configuration.configureAdvancedJvmOptions(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
|
// script or repl
|
||||||
if (arguments.script && arguments.freeArgs.isEmpty()) {
|
if (arguments.script && arguments.freeArgs.isEmpty()) {
|
||||||
messageCollector.report(ERROR, "Specify script source path to evaluate")
|
messageCollector.report(ERROR, "Specify script source path to evaluate")
|
||||||
@@ -94,7 +92,7 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
|
|||||||
)
|
)
|
||||||
projectEnvironment.registerExtensionsFromPlugins(configuration)
|
projectEnvironment.registerExtensionsFromPlugins(configuration)
|
||||||
|
|
||||||
if (arguments.script) {
|
if (arguments.script || arguments.expressions != null) {
|
||||||
val scriptingEvaluator = ScriptEvaluationExtension.getInstances(projectEnvironment.project).find { it.isAccepted(arguments) }
|
val scriptingEvaluator = ScriptEvaluationExtension.getInstances(projectEnvironment.project).find { it.isAccepted(arguments) }
|
||||||
if (scriptingEvaluator == null) {
|
if (scriptingEvaluator == null) {
|
||||||
messageCollector.report(ERROR, "Unable to evaluate script, no scripting plugin loaded")
|
messageCollector.report(ERROR, "Unable to evaluate script, no scripting plugin loaded")
|
||||||
|
|||||||
Vendored
+1
@@ -2,6 +2,7 @@ Usage: kotlinc-jvm <options> <source files>
|
|||||||
where possible options include:
|
where possible options include:
|
||||||
-classpath (-cp) <path> List of directories and JAR/ZIP archives to search for user class files
|
-classpath (-cp) <path> List of directories and JAR/ZIP archives to search for user class files
|
||||||
-d <directory|jar> Destination for generated class files
|
-d <directory|jar> Destination for generated class files
|
||||||
|
-expression Evaluate the given string as a Kotlin script
|
||||||
-include-runtime Include Kotlin runtime into the resulting JAR
|
-include-runtime Include Kotlin runtime into the resulting JAR
|
||||||
-java-parameters Generate metadata for Java 1.8 reflection on method parameters
|
-java-parameters Generate metadata for Java 1.8 reflection on method parameters
|
||||||
-jdk-home <path> Include a custom JDK from the specified location into the classpath instead of the default JAVA_HOME
|
-jdk-home <path> Include a custom JDK from the specified location into the classpath instead of the default JAVA_HOME
|
||||||
|
|||||||
+40
-15
@@ -10,8 +10,10 @@ import kotlinx.coroutines.runBlocking
|
|||||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||||
import org.jetbrains.kotlin.cli.common.ExitCode
|
import org.jetbrains.kotlin.cli.common.ExitCode
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
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.extensions.ScriptEvaluationExtension
|
||||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
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.cli.jvm.compiler.KotlinCoreEnvironment
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
|
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.File
|
||||||
import java.io.PrintStream
|
import java.io.PrintStream
|
||||||
import kotlin.script.experimental.api.*
|
import kotlin.script.experimental.api.*
|
||||||
|
import kotlin.script.experimental.host.StringScriptSource
|
||||||
import kotlin.script.experimental.host.toScriptSource
|
import kotlin.script.experimental.host.toScriptSource
|
||||||
|
|
||||||
abstract class AbstractScriptEvaluationExtension : ScriptEvaluationExtension {
|
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")
|
messageCollector.report(CompilerMessageSeverity.ERROR, "Unable to process the script, scripting plugin is not configured")
|
||||||
return ExitCode.COMPILATION_ERROR
|
return ExitCode.COMPILATION_ERROR
|
||||||
}
|
}
|
||||||
val sourcePath = arguments.freeArgs.first()
|
|
||||||
|
|
||||||
setupScriptConfiguration(configuration)
|
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)
|
val environment = createEnvironment(projectEnvironment, configuration)
|
||||||
|
|
||||||
if (messageCollector.hasErrors()) return ExitCode.COMPILATION_ERROR
|
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 definition = scriptDefinitionProvider.findDefinition(script) ?: scriptDefinitionProvider.getDefaultDefinition()
|
||||||
|
|
||||||
|
val scriptCompilationConfiguration = definition.compilationConfiguration
|
||||||
|
|
||||||
val scriptArgs =
|
val scriptArgs =
|
||||||
if (arguments.freeArgs.isNotEmpty()) arguments.freeArgs.subList(1, arguments.freeArgs.size)
|
if (arguments.script) arguments.freeArgs.subList(1, arguments.freeArgs.size)
|
||||||
else emptyList<String>()
|
else arguments.freeArgs
|
||||||
|
|
||||||
val evaluationConfiguration = definition.evaluationConfiguration.with {
|
val evaluationConfiguration = definition.evaluationConfiguration.with {
|
||||||
constructorArgs(scriptArgs.toTypedArray())
|
constructorArgs(scriptArgs.toTypedArray())
|
||||||
platformEvaluationConfiguration()
|
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)
|
val scriptCompiler = createScriptCompiler(environment)
|
||||||
|
|
||||||
return runBlocking {
|
return runBlocking {
|
||||||
|
|||||||
+3
-2
@@ -13,7 +13,8 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
|||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||||
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.ScriptJvmCompilerFromEnvironment
|
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.BasicJvmScriptEvaluator
|
||||||
import kotlin.script.experimental.jvm.baseClassLoader
|
import kotlin.script.experimental.jvm.baseClassLoader
|
||||||
import kotlin.script.experimental.jvm.jvm
|
import kotlin.script.experimental.jvm.jvm
|
||||||
@@ -46,6 +47,6 @@ class JvmCliScriptEvaluationExtension : AbstractScriptEvaluationExtension() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun isAccepted(arguments: CommonCompilerArguments): Boolean =
|
override fun isAccepted(arguments: CommonCompilerArguments): Boolean =
|
||||||
arguments is K2JVMCompilerArguments && arguments.script
|
arguments is K2JVMCompilerArguments && (arguments.script || arguments.expressions != null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,6 @@
|
|||||||
package org.jetbrains.kotlin.scripting.compiler.plugin.impl
|
package org.jetbrains.kotlin.scripting.compiler.plugin.impl
|
||||||
|
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.Argument
|
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.arguments.K2JVMCompilerArguments
|
||||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
||||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||||
@@ -100,6 +99,7 @@ internal fun reportArgumentsIgnoredGenerally(
|
|||||||
K2JVMCompilerArguments::commonSources,
|
K2JVMCompilerArguments::commonSources,
|
||||||
K2JVMCompilerArguments::allWarningsAsErrors,
|
K2JVMCompilerArguments::allWarningsAsErrors,
|
||||||
K2JVMCompilerArguments::script,
|
K2JVMCompilerArguments::script,
|
||||||
|
K2JVMCompilerArguments::expressions,
|
||||||
K2JVMCompilerArguments::scriptTemplates,
|
K2JVMCompilerArguments::scriptTemplates,
|
||||||
K2JVMCompilerArguments::scriptResolverEnvironment,
|
K2JVMCompilerArguments::scriptResolverEnvironment,
|
||||||
K2JVMCompilerArguments::disableStandardScript,
|
K2JVMCompilerArguments::disableStandardScript,
|
||||||
|
|||||||
+15
@@ -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<File> {
|
private fun getMainKtsClassPath(): List<File> {
|
||||||
return listOf(
|
return listOf(
|
||||||
File("dist/kotlinc/lib/kotlin-main-kts.jar").also {
|
File("dist/kotlinc/lib/kotlin-main-kts.jar").also {
|
||||||
|
|||||||
+13
-2
@@ -112,15 +112,26 @@ fun runWithK2JVMCompiler(
|
|||||||
add("-script")
|
add("-script")
|
||||||
add(scriptPath)
|
add(scriptPath)
|
||||||
}
|
}
|
||||||
|
runWithK2JVMCompiler(args.toTypedArray(), expectedOutPatterns, expectedExitCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun runWithK2JVMCompiler(
|
||||||
|
args: Array<String>,
|
||||||
|
expectedOutPatterns: List<String> = emptyList(),
|
||||||
|
expectedExitCode: Int = 0
|
||||||
|
) {
|
||||||
val (out, err, ret) = captureOutErrRet {
|
val (out, err, ret) = captureOutErrRet {
|
||||||
CLITool.doMainNoExit(
|
CLITool.doMainNoExit(
|
||||||
K2JVMCompiler(),
|
K2JVMCompiler(),
|
||||||
args.toTypedArray()
|
args
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
val outLines = out.lines()
|
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)) {
|
for ((expectedPattern, actualLine) in expectedOutPatterns.zip(outLines)) {
|
||||||
Assert.assertTrue(
|
Assert.assertTrue(
|
||||||
"line \"$actualLine\" do not match with expected pattern \"$expectedPattern\"",
|
"line \"$actualLine\" do not match with expected pattern \"$expectedPattern\"",
|
||||||
|
|||||||
Reference in New Issue
Block a user