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.jetbrains.kotlin.scripting.compiler.plugin.impl.KJvmReplCompilerImpl
import org.junit.Assert import org.junit.Assert
import org.junit.Test import org.junit.Test
import kotlin.script.experimental.annotations.KotlinScript
import kotlin.script.experimental.api.* import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.toScriptSource import kotlin.script.experimental.host.toScriptSource
import kotlin.script.experimental.jvm.*import kotlin.script.experimental.jvm.util.classpathFromClass import kotlin.script.experimental.jvm.BasicJvmScriptEvaluator
import kotlin.script.experimental.jvmhost.createJvmCompilationConfigurationFromTemplate import kotlin.script.experimental.jvm.baseClassLoader
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.experimental.jvm.jvm
class ReplTest : TestCase() { class ReplTest : TestCase() {
@@ -101,67 +102,67 @@ class ReplTest : TestCase() {
sequenceOf(RuntimeException("abc"), null, 4) sequenceOf(RuntimeException("abc"), null, 4)
) )
} }
}
fun evaluateInRepl( fun evaluateInRepl(
compilationConfiguration: ScriptCompilationConfiguration, compilationConfiguration: ScriptCompilationConfiguration,
evaluationConfiguration: ScriptEvaluationConfiguration, evaluationConfiguration: ScriptEvaluationConfiguration?,
snippets: Sequence<String> snippets: Sequence<String>
): Sequence<ResultWithDiagnostics<EvaluationResult>> { ): Sequence<ResultWithDiagnostics<EvaluationResult>> {
val replCompilerProxy = val replCompilerProxy =
KJvmReplCompilerImpl(defaultJvmScriptingHostConfiguration) KJvmReplCompilerImpl(defaultJvmScriptingHostConfiguration)
val compilationState = replCompilerProxy.createReplCompilationState(compilationConfiguration) val compilationState = replCompilerProxy.createReplCompilationState(compilationConfiguration)
val compilationHistory = BasicReplStageHistory<ScriptDescriptor>() val compilationHistory = BasicReplStageHistory<ScriptDescriptor>()
val replEvaluator = BasicJvmScriptEvaluator() val replEvaluator = BasicJvmScriptEvaluator()
var currentEvalConfig = evaluationConfiguration var currentEvalConfig = evaluationConfiguration ?: ScriptEvaluationConfiguration()
return snippets.mapIndexed { snippetNo, snippetText -> return snippets.mapIndexed { snippetNo, snippetText ->
val snippetSource = snippetText.toScriptSource("Line_$snippetNo.simplescript.kts") val snippetSource = snippetText.toScriptSource("Line_$snippetNo.${compilationConfiguration[ScriptCompilationConfiguration.fileExtension]}")
val snippetId = ReplSnippetIdImpl(snippetNo, 0, snippetSource) val snippetId = ReplSnippetIdImpl(snippetNo, 0, snippetSource)
replCompilerProxy.compileReplSnippet(compilationState, snippetSource, snippetId, compilationHistory) replCompilerProxy.compileReplSnippet(compilationState, snippetSource, snippetId, compilationHistory)
.onSuccess { .onSuccess {
runBlocking { runBlocking {
replEvaluator(it, currentEvalConfig) replEvaluator(it, currentEvalConfig)
}
} }
.onSuccess { }
val snippetClass = it.returnValue.scriptClass .onSuccess {
currentEvalConfig = ScriptEvaluationConfiguration(currentEvalConfig) { val snippetClass = it.returnValue.scriptClass
previousSnippets.append(it.returnValue.scriptInstance) currentEvalConfig = ScriptEvaluationConfiguration(currentEvalConfig) {
if (snippetClass != null) { previousSnippets.append(it.returnValue.scriptInstance)
jvm { if (snippetClass != null) {
baseClassLoader(snippetClass.java.classLoader) jvm {
} baseClassLoader(snippetClass.java.classLoader)
} }
} }
it.asSuccess()
} }
} it.asSuccess()
}
} }
}
fun chechEvaluateInRepl( fun chechEvaluateInRepl(
compilationConfiguration: ScriptCompilationConfiguration, compilationConfiguration: ScriptCompilationConfiguration,
evaluationConfiguration: ScriptEvaluationConfiguration, evaluationConfiguration: ScriptEvaluationConfiguration?,
snippets: Sequence<String>, snippets: Sequence<String>,
expected: Sequence<Any?> expected: Sequence<Any?>
) { ) {
val expectedIter = expected.iterator() val expectedIter = expected.iterator()
evaluateInRepl(compilationConfiguration, evaluationConfiguration, snippets).forEachIndexed { index, res -> evaluateInRepl(compilationConfiguration, evaluationConfiguration, snippets).forEachIndexed { index, res ->
when (res) { when (res) {
is ResultWithDiagnostics.Failure -> Assert.fail("#$index: Expected result, got $res") is ResultWithDiagnostics.Failure -> Assert.fail("#$index: Expected result, got $res")
is ResultWithDiagnostics.Success -> { is ResultWithDiagnostics.Success -> {
val expectedVal = expectedIter.next() val expectedVal = expectedIter.next()
when (val resVal = res.value.returnValue) { when (val resVal = res.value.returnValue) {
is ResultValue.Value -> Assert.assertEquals( is ResultValue.Value -> Assert.assertEquals(
"#$index: Expected $expectedVal, got $resVal", "#$index: Expected $expectedVal, got $resVal",
expectedVal, expectedVal,
resVal.value resVal.value
) )
is ResultValue.Unit -> Assert.assertTrue("#$index: Expected $expectedVal, got Unit", expectedVal == null) is ResultValue.Unit -> Assert.assertTrue("#$index: Expected $expectedVal, got Unit", expectedVal == null)
is ResultValue.Error -> Assert.assertTrue( is ResultValue.Error -> Assert.assertTrue(
"#$index: Expected $expectedVal, got Error: ${resVal.error}", "#$index: Expected $expectedVal, got Error: ${resVal.error}",
expectedVal is Throwable && expectedVal.message == resVal.error.message expectedVal is Throwable && expectedVal.message == resVal.error.message
) )
else -> Assert.assertTrue("#$index: Expected $expectedVal, got unknown result $resVal", expectedVal == null) else -> Assert.assertTrue("#$index: Expected $expectedVal, got unknown result $resVal", expectedVal == null)
}
} }
} }
} }
@@ -6,7 +6,6 @@
package kotlin.script.experimental.jvmhost.test package kotlin.script.experimental.jvmhost.test
import junit.framework.TestCase import junit.framework.TestCase
import org.junit.Ignore
import org.junit.Test import org.junit.Test
import java.io.File import java.io.File
import kotlin.script.experimental.api.* import kotlin.script.experimental.api.*
@@ -30,15 +29,16 @@ class ResolveDependenciesTest : TestCase() {
private val classAccessScript = "${thisPackage}.ShouldBeVisibleFromScript().x".toScriptSource() private val classAccessScript = "${thisPackage}.ShouldBeVisibleFromScript().x".toScriptSource()
private val classImportScript = "import ${thisPackage}.ShouldBeVisibleFromScript\nShouldBeVisibleFromScript().x".toScriptSource() private val classImportScript = "import ${thisPackage}.ShouldBeVisibleFromScript\nShouldBeVisibleFromScript().x".toScriptSource()
private val funAndValAccessScript = val funAndValAccessScriptText = "$thisPackage.funShouldBeVisibleFromScript($thisPackage.valShouldBeVisibleFromScript)"
"$thisPackage.funShouldBeVisibleFromScript($thisPackage.valShouldBeVisibleFromScript)".toScriptSource() private val funAndValAccessScript = funAndValAccessScriptText.toScriptSource()
private val funAndValImportScript = private val funAndValImportScriptText =
""" """
import $thisPackage.funShouldBeVisibleFromScript import $thisPackage.funShouldBeVisibleFromScript
import $thisPackage.valShouldBeVisibleFromScript import $thisPackage.valShouldBeVisibleFromScript
funShouldBeVisibleFromScript(valShouldBeVisibleFromScript) funShouldBeVisibleFromScript(valShouldBeVisibleFromScript)
""".trimMargin().toScriptSource() """.trimMargin()
private val funAndValImportScript = funAndValImportScriptText.toScriptSource()
@Test @Test
fun testResolveClassFromClassloader() { fun testResolveClassFromClassloader() {
@@ -53,12 +53,26 @@ class ResolveDependenciesTest : TestCase() {
} }
@Test @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() { fun testResolveFunAndValFromClassloader() {
runScriptAndCheckResult(funAndValAccessScript, configurationWithDependenciesFromClassloader, null, 42) runScriptAndCheckResult(funAndValAccessScript, configurationWithDependenciesFromClassloader, null, 42)
runScriptAndCheckResult(funAndValImportScript, 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 @Test
fun testResolveFunAndValFromClasspath() { fun testResolveFunAndValFromClasspath() {
runScriptAndCheckResult(funAndValAccessScript, configurationWithDependenciesFromClasspath, null, 42) runScriptAndCheckResult(funAndValAccessScript, configurationWithDependenciesFromClasspath, null, 42)
@@ -105,7 +119,6 @@ class ResolveDependenciesTest : TestCase() {
else -> throw Exception("Unexpected evaluation result: $res") else -> throw Exception("Unexpected evaluation result: $res")
} }
} }
} }
@Suppress("unused") @Suppress("unused")
@@ -103,6 +103,13 @@ class KJvmReplCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) :
messageCollector messageCollector
) )
if (history.isEmpty()) {
val updatedConfiguration = ScriptDependenciesProvider.getInstance(context.environment.project)
?.getScriptConfiguration(snippetKtFile)?.configuration
?: context.baseScriptCompilationConfiguration
registerPackageFragmetProvidersIfNeeded(updatedConfiguration, context.environment)
}
val analysisResult = val analysisResult =
compilationState.analyzerEngine.analyzeReplLineWithImportedScripts(snippetKtFile, sourceFiles.drop(1), codeLine) compilationState.analyzerEngine.analyzeReplLineWithImportedScripts(snippetKtFile, sourceFiles.drop(1), codeLine)
AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder) AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder)