Add tests for accessing fields in previous cells.
Fix compiler issues arising after allowing a test to define multipl cells.
diff --git a/libraries/scripting/jvm-host-test/test/kotlin/script/experimental/jvmhost/test/k2repl/ReplPropertySmokeTest.kt b/libraries/scripting/jvm-host-test/test/kotlin/script/experimental/jvmhost/test/k2repl/ReplPropertySmokeTest.kt
index ed26913..281cc19 100644
--- a/libraries/scripting/jvm-host-test/test/kotlin/script/experimental/jvmhost/test/k2repl/ReplPropertySmokeTest.kt
+++ b/libraries/scripting/jvm-host-test/test/kotlin/script/experimental/jvmhost/test/k2repl/ReplPropertySmokeTest.kt
@@ -48,7 +48,7 @@
import kotlin.script.experimental.jvmhost.repl.k2.ReplState
import kotlin.script.experimental.jvmhost.repl.k2.ExecutableReplSnippet
- class Snippet0: ExecutableReplSnippet {
+ object Snippet0 : ExecutableReplSnippet {
override suspend fun evaluate(`${'$'}replState`: ReplState) {
println("Hello, world!")
`${'$'}replState`.setUnitOutput(this)
@@ -78,7 +78,7 @@
import kotlin.script.experimental.jvmhost.repl.k2.ReplState
import kotlin.script.experimental.jvmhost.repl.k2.ExecutableReplSnippet
- class Snippet0: ExecutableReplSnippet {
+ object Snippet0 : ExecutableReplSnippet {
val `field1${'$'}replProperty` = createMockProperty<String>("field1")
@@ -114,7 +114,7 @@
import kotlin.script.experimental.jvmhost.repl.k2.ReplState
import kotlin.script.experimental.jvmhost.repl.k2.ExecutableReplSnippet
- class Snippet0: ExecutableReplSnippet {
+ object Snippet0 : ExecutableReplSnippet {
val `field1${'$'}replProperty` = createMockProperty<String>("field1")
@@ -154,7 +154,7 @@
import kotlin.script.experimental.jvmhost.repl.k2.ReplState
import kotlin.script.experimental.jvmhost.repl.k2.ExecutableReplSnippet
- class Snippet0: ExecutableReplSnippet {
+ object Snippet0 : ExecutableReplSnippet {
val `field1${'$'}replProperty` = createMockProperty<String>("field1")
@@ -196,7 +196,7 @@
import kotlin.script.experimental.jvmhost.repl.k2.ReplState
import kotlin.script.experimental.jvmhost.repl.k2.ExecutableReplSnippet
- class Snippet0: ExecutableReplSnippet {
+ object Snippet0 : ExecutableReplSnippet {
val `field1${'$'}replProperty` = createMockProperty<String>("field1")
@@ -230,7 +230,7 @@
import kotlin.script.experimental.jvmhost.repl.k2.ReplState
import kotlin.script.experimental.jvmhost.repl.k2.ExecutableReplSnippet
- class Snippet0: ExecutableReplSnippet {
+ object Snippet0 : ExecutableReplSnippet {
val `field1${'$'}replProperty` = createMockProperty<String?>("field1")
@@ -265,7 +265,7 @@
import kotlin.script.experimental.jvmhost.repl.k2.ReplState
import kotlin.script.experimental.jvmhost.repl.k2.ExecutableReplSnippet
- class Snippet0: ExecutableReplSnippet {
+ object Snippet0 : ExecutableReplSnippet {
val `field1${'$'}replProperty` = createMockProperty<String?>("field1")
@@ -304,7 +304,7 @@
import kotlin.script.experimental.jvmhost.repl.k2.ReplState
import kotlin.script.experimental.jvmhost.repl.k2.ExecutableReplSnippet
- class Snippet0: ExecutableReplSnippet {
+ object Snippet0 : ExecutableReplSnippet {
val `field1${'$'}replProperty` = createMockProperty<String?>("field1")
@@ -347,7 +347,7 @@
import kotlin.script.experimental.jvmhost.repl.k2.ReplState
import kotlin.script.experimental.jvmhost.repl.k2.ExecutableReplSnippet
- class Snippet0: ExecutableReplSnippet {
+ object Snippet0 : ExecutableReplSnippet {
val `field1${'$'}replProperty` = createMockProperty<String>("field1")
@@ -385,7 +385,7 @@
import kotlin.script.experimental.jvmhost.repl.k2.ReplState
import kotlin.script.experimental.jvmhost.repl.k2.ExecutableReplSnippet
- class Snippet0: ExecutableReplSnippet {
+ object Snippet0 : ExecutableReplSnippet {
val `field1${'$'}replProperty` = createMockProperty<String>("field1")
@@ -427,7 +427,7 @@
import kotlin.script.experimental.jvmhost.repl.k2.ReplState
import kotlin.script.experimental.jvmhost.repl.k2.ExecutableReplSnippet
- class Snippet0: ExecutableReplSnippet {
+ object Snippet0 : ExecutableReplSnippet {
val `field1${'$'}replProperty` = createMockProperty<String>("field1")
@@ -469,7 +469,7 @@
import kotlin.script.experimental.jvmhost.repl.k2.ReplState
import kotlin.script.experimental.jvmhost.repl.k2.ExecutableReplSnippet
- class Snippet0: ExecutableReplSnippet {
+ object Snippet0 : ExecutableReplSnippet {
val `field1${'$'}replProperty` = createMockProperty<Int>("field1")
@@ -513,7 +513,7 @@
import kotlin.script.experimental.jvmhost.repl.k2.ExecutableReplSnippet
import kotlin.properties.Delegates
- class Snippet0: ExecutableReplSnippet {
+ object Snippet0 : ExecutableReplSnippet {
val `field1${'$'}replProperty` = createMockProperty<String>("field1")
@@ -555,7 +555,7 @@
import kotlin.script.experimental.jvmhost.repl.k2.ReplState
import kotlin.script.experimental.jvmhost.repl.k2.ExecutableReplSnippet
- class Snippet0: ExecutableReplSnippet {
+ object Snippet0 : ExecutableReplSnippet {
private val `name${'$'}replProperty` = createMockProperty<String>("name")
private val `age${'$'}replProperty` = createMockProperty<Int>("age")
diff --git a/libraries/scripting/jvm-host-test/test/kotlin/script/experimental/jvmhost/test/k2repl/testUtils.kt b/libraries/scripting/jvm-host-test/test/kotlin/script/experimental/jvmhost/test/k2repl/testUtils.kt
index 71f854b..836befc 100644
--- a/libraries/scripting/jvm-host-test/test/kotlin/script/experimental/jvmhost/test/k2repl/testUtils.kt
+++ b/libraries/scripting/jvm-host-test/test/kotlin/script/experimental/jvmhost/test/k2repl/testUtils.kt
@@ -16,9 +16,7 @@
import kotlin.script.experimental.jvmhost.test.simpleScriptEvaluationConfiguration
import kotlin.script.experimental.jvmhost.test.throwOnFailure
import kotlin.script.experimental.jvmhost.test.withTempDir
-import kotlin.script.experimental.util.LinkedSnippet
-import kotlin.script.experimental.util.toLinkedSnippets
-import kotlin.script.experimental.util.toList
+import kotlin.script.experimental.util.*
/**
* @param loweredCells A list of user cells to evaluate. The input should be in the lowered
@@ -30,18 +28,19 @@
loweredSnippets: List<String>,
compilationConfiguration: ScriptCompilationConfiguration = simpleScriptCompilationConfiguration,
evaluationConfiguration: ScriptEvaluationConfiguration? = simpleScriptEvaluationConfiguration,
- cellIndex: Int = 1,
): List<ResultWithDiagnostics<EvaluatedSnippet>> {
+ var lastCompiledSnippet: LinkedSnippetImpl<K2CompiledSnippet>? = null
val outputs = mutableListOf<ResultWithDiagnostics<EvaluatedSnippet>>()
- loweredSnippets.forEachIndexed { cellId: Int, snippet: String ->
- // In the final version, the compiler should automatically handle splitting the initial
- // code into multiple classes and compile all of them.
- // For now, we need to manually compile all the snippet classes and combine the final list
- withTempDir("k2ReplTest") { tempDir ->
+ val replEvaluator = K2ReplEvaluator()
+ withTempDir("k2ReplTest") { tempDir ->
+ loweredSnippets.toList().forEachIndexed { cellId: Int, snippet: String ->
+ // In the final version, the compiler should automatically handle splitting the initial
+ // code into multiple classes and compile all of them.
+ // For now, we need to manually compile all the snippet classes and combine the final list
val currentEvalConfig = evaluationConfiguration ?: ScriptEvaluationConfiguration()
currentEvalConfig.with {
// Work-around for sending in the top-level cell index
- this.cellIndex.put(cellIndex)
+ this.cellIndex.put(cellId)
}
val compiledClasses = snippet.let {
// Hard code extension for now so it is just simple files
@@ -51,9 +50,10 @@
)
compileSnippet(snippetSource, tempDir).toList().toLinkedSnippets()
}
+ lastCompiledSnippet = lastCompiledSnippet.add(compiledClasses.get())
runBlocking {
- val replEvaluator = K2ReplEvaluator()
- val result = replEvaluator.eval(compiledClasses, currentEvalConfig)
+ val code = lastCompiledSnippet as LinkedSnippet<K2CompiledSnippet>
+ val result = replEvaluator.eval(code, currentEvalConfig)
.onSuccess { it.get().asSuccess() }
.throwOnFailure()
outputs.add(result)
diff --git a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/repl/k2/K2ReplCompiler.kt b/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/repl/k2/K2ReplCompiler.kt
index 59bdf0b..14ec7ff3 100644
--- a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/repl/k2/K2ReplCompiler.kt
+++ b/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/repl/k2/K2ReplCompiler.kt
@@ -9,11 +9,7 @@
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.PrintStream
-import kotlin.script.experimental.api.ReplCompiler
-import kotlin.script.experimental.api.ResultWithDiagnostics
-import kotlin.script.experimental.api.ScriptCompilationConfiguration
-import kotlin.script.experimental.api.SourceCode
-import kotlin.script.experimental.api.asSuccess
+import kotlin.script.experimental.api.*
import kotlin.script.experimental.jvm.util.KotlinJars.k2ReplTestsClassPath
import kotlin.script.experimental.util.LinkedSnippet
import kotlin.script.experimental.util.LinkedSnippetImpl
@@ -51,10 +47,30 @@
// Just hardcode the FQN for the snippet for now
// Make sure this matches the one in `ReplSmokeTests`
- val packageName = "repl.snippet$i.Snippet$i"
- val buildDir = File(outputDir, "out/repl/snippet$i")
- compiledSnippets = compiledSnippets.add(K2CompiledSnippet(buildDir, packageName))
+ val classFQN = extractClassFullyQualifiedName(snippet.text)
+ val buildDir = extractBuildDir(classFQN)
+ compiledSnippets = compiledSnippets.add(K2CompiledSnippet(buildDir, classFQN))
}
return compiledSnippets!!.asSuccess()
}
+
+ private fun extractBuildDir(classFQN: String): File {
+ // Right now this assumes that the number matches the class name
+ val regex = Regex("repl\\.snippet\\d*.Snippet(\\d*)")
+ val snippetNumber = regex.matchEntire(classFQN)!!.groups[1]!!.value.toInt()
+ return File(outputDir, "out/repl/snippet$snippetNumber")
+ }
+
+ private fun extractClassFullyQualifiedName(sourceCode: String): String {
+ // Assume package name is always the first line
+ val packageName = sourceCode.lineSequence().firstOrNull()?.removePrefix("package ")?.trim() ?: ""
+
+ // Assume that compiled Repl snippets are always object classes
+ val className = sourceCode.lineSequence()
+ .first { it.startsWith("object") }
+ .removePrefix("object ")
+ .removeSuffix(" : ExecutableReplSnippet {")
+
+ return "$packageName.$className"
+ }
}
diff --git a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/repl/k2/K2ReplEvaluator.kt b/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/repl/k2/K2ReplEvaluator.kt
index 82c850f..ad69b9c 100644
--- a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/repl/k2/K2ReplEvaluator.kt
+++ b/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/repl/k2/K2ReplEvaluator.kt
@@ -6,21 +6,28 @@
package kotlin.script.experimental.jvmhost.repl.k2
import kotlin.reflect.KClass
-import kotlin.reflect.full.createInstance
import kotlin.script.experimental.api.*
import kotlin.script.experimental.jvm.KJvmEvaluatedSnippet
+import kotlin.script.experimental.jvm.jvm
+import kotlin.script.experimental.jvm.lastSnippetClassLoader
+import kotlin.script.experimental.jvm.util.SnippetsHistory
import kotlin.script.experimental.util.LinkedSnippet
import kotlin.script.experimental.util.LinkedSnippetImpl
import kotlin.script.experimental.util.add
-import kotlin.script.experimental.util.toList
/**
* Dummy class only used for experimentation, while the final API for K2 Repl is being
* developed.
*/
-class K2ReplEvaluator: ReplEvaluator<CompiledSnippet, EvaluatedSnippet> {
- override val lastEvaluatedSnippet: LinkedSnippet<EvaluatedSnippet>?
- get() = TODO("Not yet implemented")
+class K2ReplEvaluator : ReplEvaluator<CompiledSnippet, EvaluatedSnippet> {
+ override var lastEvaluatedSnippet: LinkedSnippetImpl<EvaluatedSnippet>? = null
+ private set
+
+ private val history = SnippetsHistory<KClass<*>?, Any?>()
+
+ // ReplState is the same for the entire lifetime of the ReplEvaluator.
+ // Which should mirror the lifetime of the Repl session.
+ val state = ReplState()
override suspend fun eval(
snippet: LinkedSnippet<out CompiledSnippet>,
@@ -32,13 +39,23 @@
val snippetElement = snippet.get()
var snippetResults: LinkedSnippetImpl<EvaluatedSnippet>? = null
+ val lastSnippetClass = history.lastItem()?.first
+ val historyBeforeSnippet = history.items.map { it.second }
+ val currentConfiguration = ScriptEvaluationConfiguration(configuration) {
+ previousSnippets.put(historyBeforeSnippet)
+ if (lastSnippetClass != null) {
+ jvm {
+ lastSnippetClassLoader(lastSnippetClass.java.classLoader)
+ }
+ }
+ }
+
// Construct a starting ReplState and run through the entire code
// In a real use case, the ReplState should be part of a ReplSession
// and preserved for the lifetime of the repl, so it can be parsed into
// new code being run
- val state = ReplState()
- val snippetClass: KClass<*> = snippetElement.getClass(configuration).valueOrThrow()
- val snippetObj = snippetClass.createInstance() as ExecutableReplSnippet
+ val snippetClass: KClass<*> = snippetElement.getClass(currentConfiguration).valueOrThrow()
+ val snippetObj = snippetClass.objectInstance as ExecutableReplSnippet
try {
snippetObj.evaluate(state)
} catch (e: Throwable) {