Add new REPL API JVM implementation

This commit is contained in:
Ilya Muradyan
2020-02-28 19:45:20 +03:00
committed by Ilya Chernikov
parent 4c2c44b106
commit d2fec96f38
51 changed files with 2557 additions and 503 deletions
@@ -21,8 +21,8 @@ import kotlin.script.experimental.jvmhost.repl.JvmReplEvaluator
class LegacyReplTest : TestCase() {
fun testReplBasics() {
LegacyTestRepl().use { repl ->
val res1 = repl.replCompiler.check(repl.state, ReplCodeLine(0, 0, "val x ="))
TestCase.assertTrue("Unexpected check results: $res1", res1 is ReplCheckResult.Incomplete)
val res1 = repl.replCompiler.compile(repl.state, ReplCodeLine(0, 0, "val x ="))
TestCase.assertTrue("Unexpected check results: $res1", res1 is ReplCompileResult.Incomplete)
assertEvalResult(repl, "val l1 = listOf(1 + 2)\nl1.first()", 3)
@@ -54,8 +54,8 @@ class LegacyReplTest : TestCase() {
fun testReplCodeFormat() {
LegacyTestRepl().use { repl ->
val codeLine0 = ReplCodeLine(0, 0, "val l1 = 1\r\nl1\r\n")
val res0 = repl.replCompiler.check(repl.state, codeLine0)
val res0c = res0 as? ReplCheckResult.Ok
val res0 = repl.replCompiler.compile(repl.state, codeLine0)
val res0c = res0 as? ReplCompileResult.CompiledClasses
TestCase.assertNotNull("Unexpected compile result: $res0", res0c)
}
}
@@ -125,7 +125,7 @@ internal class LegacyTestRepl : Closeable {
fun nextCodeLine(code: String): ReplCodeLine = ReplCodeLine(currentLineCounter.getAndIncrement(), 0, code)
val replCompiler: JvmReplCompiler by lazy {
JvmReplCompiler(simpleScriptCompilationConfiguration)
JvmReplCompiler(simpleScriptCompilationConfiguration, false)
}
val compiledEvaluator: ReplEvaluator by lazy {
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -7,24 +7,16 @@ package kotlin.script.experimental.jvmhost.test
import junit.framework.TestCase
import kotlinx.coroutines.runBlocking
import org.jetbrains.kotlin.cli.common.repl.BasicReplStageHistory
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.KJvmReplCompilerImpl
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.KJvmReplCompilerBase
import org.junit.Assert
import org.junit.Test
import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.toScriptSource
import kotlin.script.experimental.jvm.BasicJvmScriptEvaluator
import kotlin.script.experimental.jvm.baseClassLoader
import kotlin.script.experimental.jvm.BasicJvmReplEvaluator
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.experimental.jvm.jvm
class ReplTest : TestCase() {
companion object {
const val TEST_DATA_DIR = "libraries/scripting/jvm-host-test/testData"
}
@Test
fun testCompileAndEval() {
val out = captureOut {
@@ -156,103 +148,98 @@ class ReplTest : TestCase() {
limit = 100
)
}
}
fun evaluateInRepl(
snippets: Sequence<String>,
compilationConfiguration: ScriptCompilationConfiguration = simpleScriptCompilationConfiguration,
evaluationConfiguration: ScriptEvaluationConfiguration? = simpleScriptEvaluationConfiguration,
limit: Int = 0
): Sequence<ResultWithDiagnostics<EvaluationResult>> {
val replCompilerProxy =
KJvmReplCompilerImpl(defaultJvmScriptingHostConfiguration)
val compilationState = replCompilerProxy.createReplCompilationState(compilationConfiguration)
val compilationHistory = BasicReplStageHistory<ScriptDescriptor>()
val replEvaluator = BasicJvmScriptEvaluator()
var currentEvalConfig = evaluationConfiguration ?: ScriptEvaluationConfiguration()
val snipetsLimited = if (limit == 0) snippets else snippets.take(limit)
return snipetsLimited.mapIndexed { snippetNo, snippetText ->
val snippetSource =
snippetText.toScriptSource("Line_$snippetNo.${compilationConfiguration[ScriptCompilationConfiguration.fileExtension]}")
val snippetId = ReplSnippetIdImpl(snippetNo, 0, snippetSource)
replCompilerProxy.compileReplSnippet(compilationState, snippetSource, snippetId, compilationHistory)
.onSuccess {
runBlocking {
replEvaluator(it, currentEvalConfig)
}
}
.onSuccess {
val snippetClass = it.returnValue.scriptClass
currentEvalConfig = ScriptEvaluationConfiguration(currentEvalConfig) {
previousSnippets.append(it.returnValue.scriptInstance)
if (snippetClass != null) {
jvm {
baseClassLoader(snippetClass.java.classLoader)
}
companion object {
private fun evaluateInRepl(
snippets: Sequence<String>,
compilationConfiguration: ScriptCompilationConfiguration = simpleScriptCompilationConfiguration,
evaluationConfiguration: ScriptEvaluationConfiguration? = simpleScriptEvaluationConfiguration,
limit: Int = 0
): Sequence<ResultWithDiagnostics<EvaluatedSnippet>> {
val replCompiler = KJvmReplCompilerBase.create(defaultJvmScriptingHostConfiguration)
val replEvaluator = BasicJvmReplEvaluator()
val currentEvalConfig = evaluationConfiguration ?: ScriptEvaluationConfiguration()
val snipetsLimited = if (limit == 0) snippets else snippets.take(limit)
return snipetsLimited.mapIndexed { snippetNo, snippetText ->
val snippetSource =
snippetText.toScriptSource("Line_$snippetNo.${compilationConfiguration[ScriptCompilationConfiguration.fileExtension]}")
runBlocking { replCompiler.compile(snippetSource, compilationConfiguration) }
.onSuccess {
runBlocking { replEvaluator.eval(it, currentEvalConfig) }
}
}
it.asSuccess()
}
}
}
fun checkEvaluateInReplDiags(
snippets: Sequence<String>,
expected: Sequence<ResultWithDiagnostics<Any?>>,
compilationConfiguration: ScriptCompilationConfiguration = simpleScriptCompilationConfiguration,
evaluationConfiguration: ScriptEvaluationConfiguration? = simpleScriptEvaluationConfiguration,
limit: Int = 0
) {
val expectedIter = (if (limit == 0) expected else expected.take(limit)).iterator()
evaluateInRepl(snippets, compilationConfiguration, evaluationConfiguration, limit).forEachIndexed { index, res ->
val expectedRes = expectedIter.next()
when {
res is ResultWithDiagnostics.Failure && expectedRes is ResultWithDiagnostics.Failure -> {
Assert.assertTrue(
"#$index: Expected $expectedRes, got $res. Messages are different",
res.reports.map { it.message } == expectedRes.reports.map { it.message }
)
Assert.assertTrue(
"#$index: Expected $expectedRes, got $res. Locations are different",
res.reports.map { it.location }.zip(expectedRes.reports.map { it.location }).all {
it.second == null || it.second == it.first
.onSuccess {
it.get().asSuccess()
}
)
}
res is ResultWithDiagnostics.Success && expectedRes is ResultWithDiagnostics.Success -> {
val expectedVal = expectedRes.value
when (val resVal = res.value.returnValue) {
is ResultValue.Value -> Assert.assertEquals(
"#$index: Expected $expectedVal, got $resVal",
expectedVal,
resVal.value
)
is ResultValue.Unit -> Assert.assertTrue("#$index: Expected $expectedVal, got Unit", expectedVal == null)
is ResultValue.Error -> Assert.assertTrue(
"#$index: Expected $expectedVal, got Error: ${resVal.error}",
expectedVal is Throwable && expectedVal.message == resVal.error.message
)
else -> Assert.assertTrue("#$index: Expected $expectedVal, got unknown result $resVal", expectedVal == null)
}
}
else -> {
Assert.fail("#$index: Expected $expectedRes, got $res")
}
}
}
if (expectedIter.hasNext()) {
Assert.fail("Expected ${expectedIter.next()} got end of results stream")
fun checkEvaluateInReplDiags(
snippets: Sequence<String>,
expected: Sequence<ResultWithDiagnostics<Any?>>,
compilationConfiguration: ScriptCompilationConfiguration = simpleScriptCompilationConfiguration,
evaluationConfiguration: ScriptEvaluationConfiguration? = simpleScriptEvaluationConfiguration,
limit: Int = 0
) {
val expectedIter = (if (limit == 0) expected else expected.take(limit)).iterator()
evaluateInRepl(snippets, compilationConfiguration, evaluationConfiguration, limit).forEachIndexed { index, res ->
val expectedRes = expectedIter.next()
when {
res is ResultWithDiagnostics.Failure && expectedRes is ResultWithDiagnostics.Failure -> {
val resReports = res.reports.filter {
it.code != ScriptDiagnostic.incompleteCode
}
Assert.assertTrue(
"#$index: Expected $expectedRes, got $res. Messages are different",
resReports.map { it.message } == expectedRes.reports.map { it.message }
)
Assert.assertTrue(
"#$index: Expected $expectedRes, got $res. Locations are different",
resReports.map { it.location }.zip(expectedRes.reports.map { it.location }).all {
it.second == null || it.second == it.first
}
)
}
res is ResultWithDiagnostics.Success && expectedRes is ResultWithDiagnostics.Success -> {
val expectedVal = expectedRes.value
val resVal = res.value.result
when (resVal) {
is ResultValue.Value -> Assert.assertEquals(
"#$index: Expected $expectedVal, got $resVal",
expectedVal,
resVal.value
)
is ResultValue.Unit -> Assert.assertNull("#$index: Expected $expectedVal, got Unit", expectedVal)
is ResultValue.Error -> Assert.assertTrue(
"#$index: Expected $expectedVal, got Error: ${resVal.error}",
expectedVal is Throwable && expectedVal.message == resVal.error?.message
)
else -> Assert.assertTrue("#$index: Expected $expectedVal, got unknown result $resVal", expectedVal == null)
}
}
else -> {
Assert.fail("#$index: Expected $expectedRes, got $res")
}
}
}
if (expectedIter.hasNext()) {
Assert.fail("Expected ${expectedIter.next()} got end of results stream")
}
}
fun checkEvaluateInRepl(
snippets: Sequence<String>,
expected: Sequence<Any?>,
compilationConfiguration: ScriptCompilationConfiguration = simpleScriptCompilationConfiguration,
evaluationConfiguration: ScriptEvaluationConfiguration? = simpleScriptEvaluationConfiguration,
limit: Int = 0
) = checkEvaluateInReplDiags(
snippets, expected.map { ResultWithDiagnostics.Success(it) }, compilationConfiguration, evaluationConfiguration, limit
)
class TestReceiver(
@Suppress("unused")
val prop1: Int = 3
)
}
}
fun checkEvaluateInRepl(
snippets: Sequence<String>,
expected: Sequence<Any?>,
compilationConfiguration: ScriptCompilationConfiguration = simpleScriptCompilationConfiguration,
evaluationConfiguration: ScriptEvaluationConfiguration? = simpleScriptEvaluationConfiguration,
limit: Int = 0
) = checkEvaluateInReplDiags(
snippets, expected.map { ResultWithDiagnostics.Success(it) }, compilationConfiguration, evaluationConfiguration, limit
)
class TestReceiver(val prop1: Int = 3)
@@ -13,6 +13,7 @@ import kotlin.script.experimental.host.toScriptSource
import kotlin.script.experimental.jvm.*
import kotlin.script.experimental.jvm.util.classpathFromClass
import kotlin.script.experimental.jvmhost.BasicJvmScriptingHost
import kotlin.script.experimental.jvmhost.test.ReplTest.Companion.checkEvaluateInRepl
class ResolveDependenciesTest : TestCase() {
@@ -21,7 +21,6 @@ import java.net.URLClassLoader
import java.nio.file.Files
import java.util.concurrent.TimeUnit
import java.util.jar.JarFile
import kotlin.reflect.KClass
import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.BasicScriptingHost
import kotlin.script.experimental.host.FileBasedScriptSource
@@ -348,7 +347,7 @@ class ScriptingHostTest : TestCase() {
assertTrue(compiledScript is ResultWithDiagnostics.Success)
val jvmCompiledScript = compiledScript.valueOrNull()!! as KJvmCompiledScript
val jvmCompiledModule = jvmCompiledScript.compiledModule as KJvmCompiledModuleInMemoryImpl
val jvmCompiledModule = jvmCompiledScript.getCompiledModule() as KJvmCompiledModuleInMemoryImpl
val bytes = jvmCompiledModule.compilerOutputFiles["SavedScript.class"]!!
var classFileVersion: Int? = null