Scripting: switch legacy CLI REPL to the new infrastructure
This commit is contained in:
+1
-1
@@ -98,7 +98,7 @@ abstract class KotlinJsr223JvmScriptEngineBase(protected val myFactory: ScriptEn
|
|||||||
is ReplEvalResult.Error ->
|
is ReplEvalResult.Error ->
|
||||||
when {
|
when {
|
||||||
result is ReplEvalResult.Error.Runtime && result.cause != null ->
|
result is ReplEvalResult.Error.Runtime && result.cause != null ->
|
||||||
throw ScriptException(result.cause)
|
throw ScriptException((result.cause as? java.lang.Exception) ?: RuntimeException(result.cause))
|
||||||
result is ReplEvalResult.Error.CompileTime && result.location != null ->
|
result is ReplEvalResult.Error.CompileTime && result.location != null ->
|
||||||
throw ScriptException(result.message, result.location.path, result.location.line, result.location.column)
|
throw ScriptException(result.message, result.location.path, result.location.line, result.location.column)
|
||||||
else -> throw ScriptException(result.message)
|
else -> throw ScriptException(result.message)
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ sealed class ReplEvalResult : Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sealed class Error(val message: String) : ReplEvalResult() {
|
sealed class Error(val message: String) : ReplEvalResult() {
|
||||||
class Runtime(message: String, val cause: Exception? = null) : Error(message) {
|
class Runtime(message: String, val cause: Throwable? = null) : Error(message) {
|
||||||
companion object { private val serialVersionUID: Long = 1L }
|
companion object { private val serialVersionUID: Long = 1L }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
>>> throw Exception("hi there")
|
>>> throw Exception("hi there")
|
||||||
java.lang.Exception: hi there
|
java.lang.Exception: hi there
|
||||||
|
at Line_0.<init>(Line_0.kts:1)
|
||||||
>>> fun foo() = 2
|
>>> fun foo() = 2
|
||||||
>>> foo()
|
>>> foo()
|
||||||
2
|
2
|
||||||
@@ -7,3 +8,4 @@ java.lang.Exception: hi there
|
|||||||
>>> bar()
|
>>> bar()
|
||||||
java.lang.AssertionError
|
java.lang.AssertionError
|
||||||
at Line_3.bar(Line_3.kts:1)
|
at Line_3.bar(Line_3.kts:1)
|
||||||
|
at Line_4.<init>(Line_4.kts:1)
|
||||||
|
|||||||
@@ -3,3 +3,4 @@
|
|||||||
java.lang.IllegalStateException: message
|
java.lang.IllegalStateException: message
|
||||||
at Line_0$B.foo(Line_0.kts:1)
|
at Line_0$B.foo(Line_0.kts:1)
|
||||||
at Line_0$B.toString(Line_0.kts:1)
|
at Line_0$B.toString(Line_0.kts:1)
|
||||||
|
at Line_1.<init>(Line_1.kts:1)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
java.lang.RuntimeException
|
java.lang.RuntimeException
|
||||||
at Line_0$x$1.invoke(Line_0.kts:1)
|
at Line_0$x$1.invoke(Line_0.kts:1)
|
||||||
at Line_0$x$1.invoke(Line_0.kts:1)
|
at Line_0$x$1.invoke(Line_0.kts:1)
|
||||||
|
at Line_0.<init>(Line_0.kts:1)
|
||||||
>>> x
|
>>> x
|
||||||
error: unresolved reference: x
|
java.lang.NullPointerException
|
||||||
x
|
at Line_1.<init>(Line_1.kts:1)
|
||||||
^
|
|
||||||
|
|||||||
-3
@@ -2,9 +2,6 @@
|
|||||||
error: expecting an element
|
error: expecting an element
|
||||||
)(
|
)(
|
||||||
^
|
^
|
||||||
error: expecting an expression
|
|
||||||
)(
|
|
||||||
^
|
|
||||||
>>> fun foo() = 98
|
>>> fun foo() = 98
|
||||||
>>> foo()
|
>>> foo()
|
||||||
98
|
98
|
||||||
|
|||||||
@@ -17,7 +17,10 @@
|
|||||||
package org.jetbrains.kotlin.repl
|
package org.jetbrains.kotlin.repl
|
||||||
|
|
||||||
import com.intellij.openapi.util.text.StringUtil
|
import com.intellij.openapi.util.text.StringUtil
|
||||||
|
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||||
|
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
|
||||||
import org.jetbrains.kotlin.cli.common.repl.ReplEvalResult
|
import org.jetbrains.kotlin.cli.common.repl.ReplEvalResult
|
||||||
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||||
import org.jetbrains.kotlin.script.loadScriptingPlugin
|
import org.jetbrains.kotlin.script.loadScriptingPlugin
|
||||||
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplInterpreter
|
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplInterpreter
|
||||||
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.configuration.ConsoleReplConfiguration
|
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.configuration.ConsoleReplConfiguration
|
||||||
@@ -86,8 +89,14 @@ abstract class AbstractReplInterpreterTest : KtUsefulTestCase() {
|
|||||||
protected fun doTest(path: String) {
|
protected fun doTest(path: String) {
|
||||||
val configuration = KotlinTestUtils.newConfiguration(ConfigurationKind.ALL, TestJdkKind.MOCK_JDK)
|
val configuration = KotlinTestUtils.newConfiguration(ConfigurationKind.ALL, TestJdkKind.MOCK_JDK)
|
||||||
loadScriptingPlugin(configuration)
|
loadScriptingPlugin(configuration)
|
||||||
|
val projectEnvironment =
|
||||||
|
KotlinCoreEnvironment.ProjectEnvironment(
|
||||||
|
testRootDisposable,
|
||||||
|
KotlinCoreEnvironment.getOrCreateApplicationEnvironmentForTests(testRootDisposable, configuration),
|
||||||
|
configuration
|
||||||
|
)
|
||||||
val repl = ReplInterpreter(
|
val repl = ReplInterpreter(
|
||||||
testRootDisposable, configuration,
|
projectEnvironment, configuration,
|
||||||
ConsoleReplConfiguration()
|
ConsoleReplConfiguration()
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -100,6 +109,7 @@ abstract class AbstractReplInterpreterTest : KtUsefulTestCase() {
|
|||||||
|
|
||||||
val actual = when (lineResult) {
|
val actual = when (lineResult) {
|
||||||
is ReplEvalResult.ValueResult -> lineResult.value.toString()
|
is ReplEvalResult.ValueResult -> lineResult.value.toString()
|
||||||
|
is ReplEvalResult.Error.CompileTime -> MessageRenderer.WITHOUT_PATHS.render(CompilerMessageSeverity.ERROR, lineResult.message, lineResult.location)
|
||||||
is ReplEvalResult.Error -> lineResult.message
|
is ReplEvalResult.Error -> lineResult.message
|
||||||
is ReplEvalResult.Incomplete -> INCOMPLETE_LINE_MESSAGE
|
is ReplEvalResult.Incomplete -> INCOMPLETE_LINE_MESSAGE
|
||||||
is ReplEvalResult.UnitResult -> ""
|
is ReplEvalResult.UnitResult -> ""
|
||||||
|
|||||||
@@ -98,6 +98,10 @@ data class ScriptDiagnostic(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun ScriptDiagnostic.isError() =
|
||||||
|
(severity == ScriptDiagnostic.Severity.ERROR || severity == ScriptDiagnostic.Severity.FATAL) &&
|
||||||
|
(code == ScriptDiagnostic.unspecifiedException || code == ScriptDiagnostic.unspecifiedError)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The result wrapper with diagnostics container
|
* The result wrapper with diagnostics container
|
||||||
*/
|
*/
|
||||||
|
|||||||
+4
-2
@@ -456,8 +456,10 @@ class ReplTest : TestCase() {
|
|||||||
is ResultValue.Unit -> Assert.assertNull("#$index: Expected $expectedVal, got Unit", expectedVal)
|
is ResultValue.Unit -> Assert.assertNull("#$index: Expected $expectedVal, got Unit", expectedVal)
|
||||||
is ResultValue.Error -> Assert.assertTrue(
|
is ResultValue.Error -> Assert.assertTrue(
|
||||||
"#$index: Expected $expectedVal, got Error: ${actualVal.error}",
|
"#$index: Expected $expectedVal, got Error: ${actualVal.error}",
|
||||||
expectedVal is Throwable && expectedVal.message == actualVal.error.message
|
((expectedVal as? Throwable) ?: (expectedVal as? ResultValue.Error)?.error).let {
|
||||||
&& expectedVal.cause?.message == actualVal.error.cause?.message
|
it != null && it.message == actualVal.error.message
|
||||||
|
&& it.cause?.message == actualVal.error.cause?.message
|
||||||
|
}
|
||||||
)
|
)
|
||||||
is ResultValue.NotEvaluated -> Assert.assertEquals(
|
is ResultValue.NotEvaluated -> Assert.assertEquals(
|
||||||
"#$index: Expected $expectedVal, got NotEvaluated",
|
"#$index: Expected $expectedVal, got NotEvaluated",
|
||||||
|
|||||||
+1
-1
@@ -298,7 +298,7 @@ class ScriptingHostTest : TestCase() {
|
|||||||
"println(\"$greeting\")".toScriptSource(),
|
"println(\"$greeting\")".toScriptSource(),
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
scriptExecutionWrapper {
|
scriptExecutionWrapper<Any?> {
|
||||||
val outStream = ByteArrayOutputStream()
|
val outStream = ByteArrayOutputStream()
|
||||||
val prevOut = System.out
|
val prevOut = System.out
|
||||||
System.setOut(PrintStream(outStream))
|
System.setOut(PrintStream(outStream))
|
||||||
|
|||||||
@@ -57,8 +57,13 @@ class BasicJvmReplEvaluator(val scriptEvaluator: ScriptEvaluator = BasicJvmScrip
|
|||||||
}
|
}
|
||||||
KJvmEvaluatedSnippet(snippetVal, currentConfiguration, retVal)
|
KJvmEvaluatedSnippet(snippetVal, currentConfiguration, retVal)
|
||||||
}
|
}
|
||||||
else ->
|
else -> {
|
||||||
KJvmEvaluatedSnippet(snippetVal, currentConfiguration, ResultValue.NotEvaluated)
|
val firstError = evalRes.reports.find { it.isError() }
|
||||||
|
KJvmEvaluatedSnippet(
|
||||||
|
snippetVal, currentConfiguration,
|
||||||
|
firstError?.exception?.let { ResultValue.Error(it) } ?: ResultValue.NotEvaluated
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val newNode = lastEvaluatedSnippet.add(newEvalRes)
|
val newNode = lastEvaluatedSnippet.add(newEvalRes)
|
||||||
|
|||||||
+9
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
package kotlin.script.experimental.jvm.util
|
package kotlin.script.experimental.jvm.util
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
import java.io.PrintStream
|
import java.io.PrintStream
|
||||||
import kotlin.script.experimental.api.ResultValue
|
import kotlin.script.experimental.api.ResultValue
|
||||||
|
|
||||||
@@ -42,3 +43,11 @@ fun ResultValue.Error.renderError(stream: PrintStream) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun ResultValue.Error.renderError(): String =
|
||||||
|
ByteArrayOutputStream().use { os ->
|
||||||
|
val ps = PrintStream(os)
|
||||||
|
renderError(ps)
|
||||||
|
ps.flush()
|
||||||
|
os.toString()
|
||||||
|
}
|
||||||
|
|||||||
+1
-3
@@ -10,7 +10,6 @@ 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.extensions.ShellExtension
|
import org.jetbrains.kotlin.cli.common.extensions.ShellExtension
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
|
||||||
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplFromTerminal
|
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplFromTerminal
|
||||||
|
|
||||||
class JvmCliReplShellExtension : ShellExtension {
|
class JvmCliReplShellExtension : ShellExtension {
|
||||||
@@ -22,8 +21,7 @@ class JvmCliReplShellExtension : ShellExtension {
|
|||||||
configuration: CompilerConfiguration,
|
configuration: CompilerConfiguration,
|
||||||
projectEnvironment: JavaCoreProjectEnvironment
|
projectEnvironment: JavaCoreProjectEnvironment
|
||||||
): ExitCode {
|
): ExitCode {
|
||||||
configuration.put(JVMConfigurationKeys.IR, false)
|
ReplFromTerminal.run(projectEnvironment, configuration)
|
||||||
ReplFromTerminal.run(projectEnvironment.parentDisposable, configuration)
|
|
||||||
return ExitCode.OK
|
return ExitCode.OK
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -81,13 +81,13 @@ open class GenericReplCompiler(
|
|||||||
val analysisResult = compilerState.analyzerEngine.analyzeReplLine(psiFile, codeLine)
|
val analysisResult = compilerState.analyzerEngine.analyzeReplLine(psiFile, codeLine)
|
||||||
AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder, renderDiagnosticName = false)
|
AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder, renderDiagnosticName = false)
|
||||||
val scriptDescriptor = when (analysisResult) {
|
val scriptDescriptor = when (analysisResult) {
|
||||||
is ReplCodeAnalyzerBase.ReplLineAnalysisResult.WithErrors -> {
|
|
||||||
return ReplCompileResult.Error(errorHolder.renderMessage())
|
|
||||||
}
|
|
||||||
is ReplCodeAnalyzerBase.ReplLineAnalysisResult.Successful -> {
|
is ReplCodeAnalyzerBase.ReplLineAnalysisResult.Successful -> {
|
||||||
(analysisResult.scriptDescriptor as? ScriptDescriptor)
|
(analysisResult.scriptDescriptor as? ScriptDescriptor)
|
||||||
?: error("Unexpected script descriptor type ${analysisResult.scriptDescriptor::class}")
|
?: error("Unexpected script descriptor type ${analysisResult.scriptDescriptor::class}")
|
||||||
}
|
}
|
||||||
|
is ReplCodeAnalyzerBase.ReplLineAnalysisResult.WithErrors -> {
|
||||||
|
return ReplCompileResult.Error(errorHolder.renderMessage())
|
||||||
|
}
|
||||||
else -> error("Unexpected result ${analysisResult::class.java}")
|
else -> error("Unexpected result ${analysisResult::class.java}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-6
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.scripting.compiler.plugin.repl
|
package org.jetbrains.kotlin.scripting.compiler.plugin.repl
|
||||||
|
|
||||||
import com.intellij.openapi.Disposable
|
import com.intellij.core.JavaCoreProjectEnvironment
|
||||||
import com.intellij.openapi.util.io.FileUtil
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||||
import org.jetbrains.kotlin.cli.common.messages.GroupingMessageCollector
|
import org.jetbrains.kotlin.cli.common.messages.GroupingMessageCollector
|
||||||
@@ -24,12 +24,12 @@ import java.util.concurrent.Executors
|
|||||||
import java.util.concurrent.Future
|
import java.util.concurrent.Future
|
||||||
|
|
||||||
class ReplFromTerminal(
|
class ReplFromTerminal(
|
||||||
disposable: Disposable,
|
projectEnvironment: JavaCoreProjectEnvironment,
|
||||||
compilerConfiguration: CompilerConfiguration,
|
compilerConfiguration: CompilerConfiguration,
|
||||||
private val replConfiguration: ReplConfiguration
|
private val replConfiguration: ReplConfiguration
|
||||||
) {
|
) {
|
||||||
private val replInitializer: Future<ReplInterpreter> = Executors.newSingleThreadExecutor().submit(Callable {
|
private val replInitializer: Future<ReplInterpreter> = Executors.newSingleThreadExecutor().submit(Callable {
|
||||||
ReplInterpreter(disposable, compilerConfiguration, replConfiguration)
|
ReplInterpreter(projectEnvironment, compilerConfiguration, replConfiguration)
|
||||||
})
|
})
|
||||||
|
|
||||||
private val replInterpreter: ReplInterpreter
|
private val replInterpreter: ReplInterpreter
|
||||||
@@ -109,7 +109,7 @@ class ReplFromTerminal(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is ReplEvalResult.Error.Runtime -> writer.outputRuntimeError(evalResult.message)
|
is ReplEvalResult.Error.Runtime -> writer.outputRuntimeError(evalResult.message)
|
||||||
is ReplEvalResult.Error.CompileTime -> writer.outputRuntimeError(evalResult.message)
|
is ReplEvalResult.Error.CompileTime -> writer.outputCompileError(evalResult.message)
|
||||||
is ReplEvalResult.Incomplete -> writer.notifyIncomplete()
|
is ReplEvalResult.Incomplete -> writer.notifyIncomplete()
|
||||||
is ReplEvalResult.HistoryMismatch -> {} // assuming handled elsewhere
|
is ReplEvalResult.HistoryMismatch -> {} // assuming handled elsewhere
|
||||||
}
|
}
|
||||||
@@ -153,11 +153,11 @@ class ReplFromTerminal(
|
|||||||
return listOf(*command.split(" ".toRegex()).dropLastWhile(String::isEmpty).toTypedArray())
|
return listOf(*command.split(" ".toRegex()).dropLastWhile(String::isEmpty).toTypedArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun run(disposable: Disposable, configuration: CompilerConfiguration) {
|
fun run(projectEnvironment: JavaCoreProjectEnvironment, configuration: CompilerConfiguration) {
|
||||||
val replIdeMode = System.getProperty("kotlin.repl.ideMode") == "true"
|
val replIdeMode = System.getProperty("kotlin.repl.ideMode") == "true"
|
||||||
val replConfiguration = if (replIdeMode) IdeReplConfiguration() else ConsoleReplConfiguration()
|
val replConfiguration = if (replIdeMode) IdeReplConfiguration() else ConsoleReplConfiguration()
|
||||||
return try {
|
return try {
|
||||||
ReplFromTerminal(disposable, configuration, replConfiguration).doRun()
|
ReplFromTerminal(projectEnvironment, configuration, replConfiguration).doRun()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
replConfiguration.exceptionReporter.report(e)
|
replConfiguration.exceptionReporter.report(e)
|
||||||
throw e
|
throw e
|
||||||
|
|||||||
+131
-36
@@ -5,29 +5,98 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.scripting.compiler.plugin.repl
|
package org.jetbrains.kotlin.scripting.compiler.plugin.repl
|
||||||
|
|
||||||
import com.intellij.openapi.Disposable
|
import com.intellij.core.JavaCoreProjectEnvironment
|
||||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation
|
import org.jetbrains.kotlin.cli.common.messages.*
|
||||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
import org.jetbrains.kotlin.cli.common.repl.ReplClassLoader
|
||||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
import org.jetbrains.kotlin.cli.common.repl.ReplEvalResult
|
||||||
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
|
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||||
import org.jetbrains.kotlin.cli.common.repl.*
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||||
|
import org.jetbrains.kotlin.cli.jvm.compiler.messageCollector
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
|
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.JvmModulePathRoot
|
import org.jetbrains.kotlin.cli.jvm.config.JvmModulePathRoot
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
|
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.KJvmReplCompilerBase
|
||||||
|
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.ReplCompilationState
|
||||||
|
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.ScriptDiagnosticsMessageCollector
|
||||||
|
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.createCompilationContextFromEnvironment
|
||||||
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.configuration.ReplConfiguration
|
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.configuration.ReplConfiguration
|
||||||
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
|
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
|
||||||
|
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider
|
||||||
import java.io.PrintWriter
|
import java.io.PrintWriter
|
||||||
import java.net.URLClassLoader
|
import java.net.URLClassLoader
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
import kotlin.script.experimental.api.*
|
||||||
|
import kotlin.script.experimental.host.ScriptingHostConfiguration
|
||||||
|
import kotlin.script.experimental.host.toScriptSource
|
||||||
|
import kotlin.script.experimental.host.withDefaultsFrom
|
||||||
|
import kotlin.script.experimental.impl.internalScriptingRunSuspend
|
||||||
|
import kotlin.script.experimental.jvm.BasicJvmReplEvaluator
|
||||||
|
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
|
||||||
|
import kotlin.script.experimental.jvm.util.renderError
|
||||||
|
|
||||||
class ReplInterpreter(
|
class ReplInterpreter(
|
||||||
disposable: Disposable,
|
projectEnvironment: JavaCoreProjectEnvironment,
|
||||||
private val configuration: CompilerConfiguration,
|
private val configuration: CompilerConfiguration,
|
||||||
private val replConfiguration: ReplConfiguration
|
private val replConfiguration: ReplConfiguration
|
||||||
) {
|
) {
|
||||||
|
private val hostConfiguration: ScriptingHostConfiguration
|
||||||
|
private val compilationConfiguration: ScriptCompilationConfiguration
|
||||||
|
private val evaluationConfiguration: ScriptEvaluationConfiguration
|
||||||
|
|
||||||
|
private val replState: JvmReplCompilerState<*>
|
||||||
|
|
||||||
|
init {
|
||||||
|
val scriptDefinition: ScriptDefinition? =
|
||||||
|
ScriptDefinitionProvider.getInstance(projectEnvironment.project)?.getDefaultDefinition()
|
||||||
|
hostConfiguration = scriptDefinition?.hostConfiguration.withDefaultsFrom(defaultJvmScriptingHostConfiguration)
|
||||||
|
|
||||||
|
val environment = (projectEnvironment as? KotlinCoreEnvironment.ProjectEnvironment)?.let {
|
||||||
|
KotlinCoreEnvironment.createForProduction(it, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
|
||||||
|
}
|
||||||
|
?: KotlinCoreEnvironment.createForProduction(
|
||||||
|
projectEnvironment.parentDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES
|
||||||
|
)
|
||||||
|
|
||||||
|
val context =
|
||||||
|
createCompilationContextFromEnvironment(
|
||||||
|
scriptDefinition?.compilationConfiguration.with {
|
||||||
|
hostConfiguration(this@ReplInterpreter.hostConfiguration)
|
||||||
|
},
|
||||||
|
environment,
|
||||||
|
ScriptDiagnosticsMessageCollector(environment.messageCollector)
|
||||||
|
)
|
||||||
|
|
||||||
|
compilationConfiguration = context.baseScriptCompilationConfiguration
|
||||||
|
evaluationConfiguration = scriptDefinition?.evaluationConfiguration.with {
|
||||||
|
hostConfiguration(this@ReplInterpreter.hostConfiguration)
|
||||||
|
scriptExecutionWrapper<Any> { replConfiguration.executionInterceptor.execute(it) }
|
||||||
|
constructorArgs(emptyArray<String>())
|
||||||
|
}
|
||||||
|
|
||||||
|
replState = JvmReplCompilerState(
|
||||||
|
{
|
||||||
|
ReplCompilationState(
|
||||||
|
context,
|
||||||
|
analyzerInit = { context1, resolutionFilter ->
|
||||||
|
ReplCodeAnalyzerBase(context1.environment, implicitsResolutionFilter = resolutionFilter)
|
||||||
|
},
|
||||||
|
implicitsResolutionFilter = ReplImplicitsExtensionsResolutionFilter()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val compiler = KJvmReplCompilerBase<ReplCodeAnalyzerBase>(hostConfiguration, replState)
|
||||||
|
private val evaluator = BasicJvmReplEvaluator()
|
||||||
|
|
||||||
private val lineNumber = AtomicInteger()
|
private val lineNumber = AtomicInteger()
|
||||||
|
|
||||||
|
private fun nextSnippet(code: String) =
|
||||||
|
code.toScriptSource(
|
||||||
|
"Line_${lineNumber.getAndIncrement()}.${compilationConfiguration[ScriptCompilationConfiguration.fileExtension]}"
|
||||||
|
)
|
||||||
|
|
||||||
private val previousIncompleteLines = arrayListOf<String>()
|
private val previousIncompleteLines = arrayListOf<String>()
|
||||||
|
|
||||||
private val classpathRoots = configuration.getList(CLIConfigurationKeys.CONTENT_ROOTS).mapNotNull { root ->
|
private val classpathRoots = configuration.getList(CLIConfigurationKeys.CONTENT_ROOTS).mapNotNull { root ->
|
||||||
@@ -75,33 +144,65 @@ class ReplInterpreter(
|
|||||||
override fun hasErrors(): Boolean = hasErrors
|
override fun hasErrors(): Boolean = hasErrors
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add script definition with project-based resolving for IDEA repl
|
|
||||||
private val scriptCompiler: ReplCompilerWithoutCheck by lazy {
|
|
||||||
GenericReplCompiler(
|
|
||||||
disposable,
|
|
||||||
REPL_LINE_AS_SCRIPT_DEFINITION,
|
|
||||||
configuration,
|
|
||||||
messageCollector
|
|
||||||
)
|
|
||||||
}
|
|
||||||
private val scriptEvaluator: ReplFullEvaluator by lazy {
|
|
||||||
GenericReplCompilingEvaluator(scriptCompiler, classpathRoots, classLoader, null, ReplRepeatingMode.REPEAT_ANY_PREVIOUS)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val evalState by lazy { scriptEvaluator.createState() }
|
|
||||||
|
|
||||||
fun eval(line: String): ReplEvalResult {
|
fun eval(line: String): ReplEvalResult {
|
||||||
val fullText = (previousIncompleteLines + line).joinToString(separator = "\n")
|
val fullText = (previousIncompleteLines + line).joinToString(separator = "\n")
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val evalRes = scriptEvaluator.compileAndEval(
|
val snippet = nextSnippet(fullText)
|
||||||
evalState,
|
|
||||||
ReplCodeLine(lineNumber.getAndIncrement(), 0, fullText),
|
fun SourceCode.Location.toCompilerMessageLocation() =
|
||||||
null,
|
CompilerMessageLocation.create(
|
||||||
object : InvokeWrapper {
|
snippet.name,
|
||||||
override fun <T> invoke(body: () -> T): T = replConfiguration.executionInterceptor.execute(body)
|
start.line, start.col,
|
||||||
|
snippet.text.lines().getOrNull(start.line - 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
fun ResultWithDiagnostics<*>.reportToMessageCollector() {
|
||||||
|
for (it in reports) {
|
||||||
|
val diagnosticSeverity = when (it.severity) {
|
||||||
|
ScriptDiagnostic.Severity.ERROR -> CompilerMessageSeverity.ERROR
|
||||||
|
ScriptDiagnostic.Severity.FATAL -> CompilerMessageSeverity.EXCEPTION
|
||||||
|
ScriptDiagnostic.Severity.WARNING -> CompilerMessageSeverity.WARNING
|
||||||
|
else -> continue
|
||||||
|
}
|
||||||
|
messageCollector.report(diagnosticSeverity, it.message, it.location?.toCompilerMessageLocation())
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION_ERROR")
|
||||||
|
val evalRes: ReplEvalResult = internalScriptingRunSuspend {
|
||||||
|
when (val compileResult = compiler.compile(listOf(snippet), compilationConfiguration)) {
|
||||||
|
is ResultWithDiagnostics.Failure -> {
|
||||||
|
val incompleteReport = compileResult.reports.find { it.code == ScriptDiagnostic.incompleteCode }
|
||||||
|
val firstError = compileResult.reports.find { it.isError() }
|
||||||
|
if (incompleteReport != null)
|
||||||
|
ReplEvalResult.Incomplete(incompleteReport.message)
|
||||||
|
else {
|
||||||
|
compileResult.reportToMessageCollector()
|
||||||
|
ReplEvalResult.Error.CompileTime(firstError?.message ?: "", firstError?.location?.toCompilerMessageLocation())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is ResultWithDiagnostics.Success -> {
|
||||||
|
compileResult.reportToMessageCollector()
|
||||||
|
val evalResult = evaluator.eval(compileResult.value, evaluationConfiguration)
|
||||||
|
when (evalResult) {
|
||||||
|
is ResultWithDiagnostics.Success -> {
|
||||||
|
when (val evalValue = evalResult.value.get().result) {
|
||||||
|
is ResultValue.Unit -> ReplEvalResult.UnitResult()
|
||||||
|
is ResultValue.Value -> ReplEvalResult.ValueResult(evalValue.name, evalValue.value, evalValue.type)
|
||||||
|
is ResultValue.Error -> ReplEvalResult.Error.Runtime(evalValue.renderError())
|
||||||
|
else -> ReplEvalResult.Error.Runtime("Error: snippet is not evaluated")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val firstError = evalResult.reports.find { it.isError() }
|
||||||
|
evalResult.reportToMessageCollector()
|
||||||
|
ReplEvalResult.Error.Runtime(firstError?.message ?: "", firstError?.exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
when {
|
when {
|
||||||
evalRes !is ReplEvalResult.Incomplete -> previousIncompleteLines.clear()
|
evalRes !is ReplEvalResult.Incomplete -> previousIncompleteLines.clear()
|
||||||
@@ -120,10 +221,4 @@ class ReplInterpreter(
|
|||||||
fun dumpClasses(out: PrintWriter) {
|
fun dumpClasses(out: PrintWriter) {
|
||||||
classLoader.dumpClasses(out)
|
classLoader.dumpClasses(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val REPL_LINE_AS_SCRIPT_DEFINITION = object : KotlinScriptDefinition(Any::class) {
|
|
||||||
override val name = "Kotlin REPL"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user