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) {