Implement support for expression evaluation in jvm cli compiler

This commit is contained in:
Ilya Chernikov
2019-11-19 20:00:43 +01:00
parent 7bb9f97b11
commit 531ff92791
8 changed files with 80 additions and 26 deletions
@@ -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<String>()
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 {
@@ -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)
}
@@ -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,
@@ -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> {
return listOf(
File("dist/kotlinc/lib/kotlin-main-kts.jar").also {
@@ -112,15 +112,26 @@ fun runWithK2JVMCompiler(
add("-script")
add(scriptPath)
}
runWithK2JVMCompiler(args.toTypedArray(), expectedOutPatterns, expectedExitCode)
}
fun runWithK2JVMCompiler(
args: Array<String>,
expectedOutPatterns: List<String> = 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\"",