Add resolving from classloader to REPL pipeline

This commit is contained in:
Ilya Chernikov
2019-10-09 14:34:02 +02:00
parent 2219b950f1
commit 54caf4bf16
3 changed files with 85 additions and 64 deletions
@@ -12,11 +12,12 @@ import org.jetbrains.kotlin.descriptors.ScriptDescriptor
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.KJvmReplCompilerImpl
import org.junit.Assert
import org.junit.Test
import kotlin.script.experimental.annotations.KotlinScript
import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.toScriptSource
import kotlin.script.experimental.jvm.*import kotlin.script.experimental.jvm.util.classpathFromClass
import kotlin.script.experimental.jvmhost.createJvmCompilationConfigurationFromTemplate
import kotlin.script.experimental.jvm.BasicJvmScriptEvaluator
import kotlin.script.experimental.jvm.baseClassLoader
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.experimental.jvm.jvm
class ReplTest : TestCase() {
@@ -101,67 +102,67 @@ class ReplTest : TestCase() {
sequenceOf(RuntimeException("abc"), null, 4)
)
}
}
fun evaluateInRepl(
compilationConfiguration: ScriptCompilationConfiguration,
evaluationConfiguration: ScriptEvaluationConfiguration,
snippets: Sequence<String>
): Sequence<ResultWithDiagnostics<EvaluationResult>> {
val replCompilerProxy =
KJvmReplCompilerImpl(defaultJvmScriptingHostConfiguration)
val compilationState = replCompilerProxy.createReplCompilationState(compilationConfiguration)
val compilationHistory = BasicReplStageHistory<ScriptDescriptor>()
val replEvaluator = BasicJvmScriptEvaluator()
var currentEvalConfig = evaluationConfiguration
return snippets.mapIndexed { snippetNo, snippetText ->
val snippetSource = snippetText.toScriptSource("Line_$snippetNo.simplescript.kts")
val snippetId = ReplSnippetIdImpl(snippetNo, 0, snippetSource)
replCompilerProxy.compileReplSnippet(compilationState, snippetSource, snippetId, compilationHistory)
.onSuccess {
runBlocking {
replEvaluator(it, currentEvalConfig)
}
fun evaluateInRepl(
compilationConfiguration: ScriptCompilationConfiguration,
evaluationConfiguration: ScriptEvaluationConfiguration?,
snippets: Sequence<String>
): Sequence<ResultWithDiagnostics<EvaluationResult>> {
val replCompilerProxy =
KJvmReplCompilerImpl(defaultJvmScriptingHostConfiguration)
val compilationState = replCompilerProxy.createReplCompilationState(compilationConfiguration)
val compilationHistory = BasicReplStageHistory<ScriptDescriptor>()
val replEvaluator = BasicJvmScriptEvaluator()
var currentEvalConfig = evaluationConfiguration ?: ScriptEvaluationConfiguration()
return snippets.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)
}
}
.onSuccess {
val snippetClass = it.returnValue.scriptClass
currentEvalConfig = ScriptEvaluationConfiguration(currentEvalConfig) {
previousSnippets.append(it.returnValue.scriptInstance)
if (snippetClass != null) {
jvm {
baseClassLoader(snippetClass.java.classLoader)
}
}
it.asSuccess()
}
}
it.asSuccess()
}
}
}
fun chechEvaluateInRepl(
compilationConfiguration: ScriptCompilationConfiguration,
evaluationConfiguration: ScriptEvaluationConfiguration,
snippets: Sequence<String>,
expected: Sequence<Any?>
) {
val expectedIter = expected.iterator()
evaluateInRepl(compilationConfiguration, evaluationConfiguration, snippets).forEachIndexed { index, res ->
when (res) {
is ResultWithDiagnostics.Failure -> Assert.fail("#$index: Expected result, got $res")
is ResultWithDiagnostics.Success -> {
val expectedVal = expectedIter.next()
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)
}
fun chechEvaluateInRepl(
compilationConfiguration: ScriptCompilationConfiguration,
evaluationConfiguration: ScriptEvaluationConfiguration?,
snippets: Sequence<String>,
expected: Sequence<Any?>
) {
val expectedIter = expected.iterator()
evaluateInRepl(compilationConfiguration, evaluationConfiguration, snippets).forEachIndexed { index, res ->
when (res) {
is ResultWithDiagnostics.Failure -> Assert.fail("#$index: Expected result, got $res")
is ResultWithDiagnostics.Success -> {
val expectedVal = expectedIter.next()
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)
}
}
}
@@ -6,7 +6,6 @@
package kotlin.script.experimental.jvmhost.test
import junit.framework.TestCase
import org.junit.Ignore
import org.junit.Test
import java.io.File
import kotlin.script.experimental.api.*
@@ -30,15 +29,16 @@ class ResolveDependenciesTest : TestCase() {
private val classAccessScript = "${thisPackage}.ShouldBeVisibleFromScript().x".toScriptSource()
private val classImportScript = "import ${thisPackage}.ShouldBeVisibleFromScript\nShouldBeVisibleFromScript().x".toScriptSource()
private val funAndValAccessScript =
"$thisPackage.funShouldBeVisibleFromScript($thisPackage.valShouldBeVisibleFromScript)".toScriptSource()
val funAndValAccessScriptText = "$thisPackage.funShouldBeVisibleFromScript($thisPackage.valShouldBeVisibleFromScript)"
private val funAndValAccessScript = funAndValAccessScriptText.toScriptSource()
private val funAndValImportScript =
private val funAndValImportScriptText =
"""
import $thisPackage.funShouldBeVisibleFromScript
import $thisPackage.valShouldBeVisibleFromScript
funShouldBeVisibleFromScript(valShouldBeVisibleFromScript)
""".trimMargin().toScriptSource()
""".trimMargin()
private val funAndValImportScript = funAndValImportScriptText.toScriptSource()
@Test
fun testResolveClassFromClassloader() {
@@ -53,12 +53,26 @@ class ResolveDependenciesTest : TestCase() {
}
@Test
// This doesn't work since there is no way to resolve a top-level function/property via reflection now (see #KT-33892)
fun testResolveFunAndValFromClassloader() {
runScriptAndCheckResult(funAndValAccessScript, configurationWithDependenciesFromClassloader, null, 42)
runScriptAndCheckResult(funAndValImportScript, configurationWithDependenciesFromClassloader, null, 42)
}
@Test
fun testReplResolveFunAndValFromClassloader() {
chechEvaluateInRepl(
configurationWithDependenciesFromClassloader, null,
sequenceOf(funAndValAccessScriptText, funAndValAccessScriptText),
sequenceOf(42, 42)
)
chechEvaluateInRepl(
configurationWithDependenciesFromClassloader, null,
funAndValImportScriptText.split('\n').asSequence(),
sequenceOf(null, null, 42)
)
runScriptAndCheckResult(funAndValImportScript, configurationWithDependenciesFromClassloader, null, 42)
}
@Test
fun testResolveFunAndValFromClasspath() {
runScriptAndCheckResult(funAndValAccessScript, configurationWithDependenciesFromClasspath, null, 42)
@@ -105,7 +119,6 @@ class ResolveDependenciesTest : TestCase() {
else -> throw Exception("Unexpected evaluation result: $res")
}
}
}
@Suppress("unused")
@@ -103,6 +103,13 @@ class KJvmReplCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) :
messageCollector
)
if (history.isEmpty()) {
val updatedConfiguration = ScriptDependenciesProvider.getInstance(context.environment.project)
?.getScriptConfiguration(snippetKtFile)?.configuration
?: context.baseScriptCompilationConfiguration
registerPackageFragmetProvidersIfNeeded(updatedConfiguration, context.environment)
}
val analysisResult =
compilationState.analyzerEngine.analyzeReplLineWithImportedScripts(snippetKtFile, sourceFiles.drop(1), codeLine)
AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder)