Add new REPL API JVM implementation
This commit is contained in:
committed by
Ilya Chernikov
parent
4c2c44b106
commit
d2fec96f38
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package kotlin.script.experimental.jvm
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.script.experimental.api.*
|
||||
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
|
||||
|
||||
class BasicJvmReplEvaluator(val scriptEvaluator: ScriptEvaluator = BasicJvmScriptEvaluator()) :
|
||||
ReplEvaluator<CompiledSnippet, KJvmEvaluatedSnippet> {
|
||||
override var lastEvaluatedSnippet: LinkedSnippetImpl<KJvmEvaluatedSnippet>? = null
|
||||
private set
|
||||
|
||||
private val history = SnippetsHistory<KClass<*>?, Any?>()
|
||||
|
||||
override suspend fun eval(
|
||||
snippet: LinkedSnippet<out CompiledSnippet>,
|
||||
configuration: ScriptEvaluationConfiguration
|
||||
): ResultWithDiagnostics<LinkedSnippet<KJvmEvaluatedSnippet>> {
|
||||
|
||||
val lastSnippetClass = history.lastItem()?.first
|
||||
val historyBeforeSnippet = history.items.map { it.second }
|
||||
val currentConfiguration = ScriptEvaluationConfiguration(configuration) {
|
||||
if (historyBeforeSnippet.isNotEmpty()) {
|
||||
previousSnippets.put(historyBeforeSnippet)
|
||||
}
|
||||
if (lastSnippetClass != null) {
|
||||
jvm {
|
||||
baseClassLoader(lastSnippetClass.java.classLoader)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val snippetVal = snippet.get()
|
||||
val newEvalRes = when (val res = scriptEvaluator(snippetVal, currentConfiguration)) {
|
||||
is ResultWithDiagnostics.Success -> {
|
||||
val retVal = res.value.returnValue
|
||||
when (retVal) {
|
||||
is ResultValue.Error -> history.add(retVal.scriptClass, null)
|
||||
is ResultValue.Value, is ResultValue.Unit -> history.add(retVal.scriptClass, retVal.scriptInstance)
|
||||
}
|
||||
KJvmEvaluatedSnippet(snippetVal, currentConfiguration, retVal)
|
||||
}
|
||||
else ->
|
||||
KJvmEvaluatedSnippet(snippetVal, currentConfiguration, ResultValue.NotEvaluated)
|
||||
}
|
||||
|
||||
val newNode = lastEvaluatedSnippet.add(newEvalRes)
|
||||
lastEvaluatedSnippet = newNode
|
||||
return newNode.asSuccess()
|
||||
}
|
||||
}
|
||||
|
||||
class KJvmEvaluatedSnippet(
|
||||
override val compiledSnippet: CompiledSnippet,
|
||||
override val configuration: ScriptEvaluationConfiguration,
|
||||
override val result: ResultValue
|
||||
) : EvaluatedSnippet
|
||||
+8
-4
@@ -5,6 +5,7 @@
|
||||
|
||||
package kotlin.script.experimental.jvm.impl
|
||||
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import java.io.*
|
||||
import java.net.URL
|
||||
import java.net.URLClassLoader
|
||||
@@ -39,13 +40,13 @@ internal class KJvmCompiledScriptData(
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
private val serialVersionUID = 4L
|
||||
private val serialVersionUID = 5L
|
||||
}
|
||||
}
|
||||
|
||||
class KJvmCompiledScript internal constructor(
|
||||
open class KJvmCompiledScript internal constructor(
|
||||
internal var data: KJvmCompiledScriptData,
|
||||
var compiledModule: KJvmCompiledModule? // module should be null for imported (other) scripts, so only one reference to the module is kept
|
||||
internal var compiledModule: KJvmCompiledModule? // module should be null for imported (other) scripts, so only one reference to the module is kept
|
||||
) : CompiledScript, Serializable {
|
||||
|
||||
constructor(
|
||||
@@ -93,6 +94,9 @@ class KJvmCompiledScript internal constructor(
|
||||
)
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
fun getCompiledModule() = compiledModule
|
||||
|
||||
private fun writeObject(outputStream: ObjectOutputStream) {
|
||||
outputStream.writeObject(data)
|
||||
outputStream.writeObject(compiledModule)
|
||||
@@ -177,7 +181,7 @@ fun KJvmCompiledScript.toBytes(): ByteArray {
|
||||
oos = ObjectOutputStream(bos)
|
||||
oos.writeObject(this)
|
||||
oos.flush()
|
||||
return bos.toByteArray()!!
|
||||
return bos.toByteArray()
|
||||
} finally {
|
||||
try {
|
||||
oos?.close()
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package kotlin.script.experimental.jvm.util
|
||||
|
||||
import java.io.Serializable
|
||||
import java.util.*
|
||||
|
||||
|
||||
typealias CompiledHistoryItem<CompiledT, ResultT> = Pair<CompiledT, ResultT>
|
||||
typealias CompiledHistoryStorage<CompiledT, ResultT> = ArrayList<CompiledHistoryItem<CompiledT, ResultT>>
|
||||
typealias CompiledHistoryList<CompiledT, ResultT> = List<CompiledHistoryItem<CompiledT, ResultT>>
|
||||
|
||||
/*
|
||||
WARNING: Not thread safe, the caller is assumed to lock access.
|
||||
*/
|
||||
open class SnippetsHistory<CompiledT, ResultT>(startingHistory: CompiledHistoryList<CompiledT, ResultT> = emptyList()) : Serializable {
|
||||
protected val history: CompiledHistoryStorage<CompiledT, ResultT> = ArrayList(startingHistory)
|
||||
|
||||
fun add(line: CompiledT, value: ResultT) {
|
||||
history.add(line to value)
|
||||
}
|
||||
|
||||
fun lastItem(): CompiledHistoryItem<CompiledT, ResultT>? = history.lastOrNull()
|
||||
fun lastValue(): ResultT? = lastItem()?.second
|
||||
|
||||
val items: List<CompiledHistoryItem<CompiledT, ResultT>> = history
|
||||
|
||||
fun isEmpty(): Boolean = history.isEmpty()
|
||||
fun isNotEmpty(): Boolean = history.isNotEmpty()
|
||||
|
||||
companion object {
|
||||
private const val serialVersionUID: Long = 8328353001L
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package kotlin.script.experimental.jvm.util
|
||||
|
||||
import kotlin.script.experimental.api.ResultWithDiagnostics
|
||||
import kotlin.script.experimental.api.ScriptDiagnostic
|
||||
|
||||
fun <T> ResultWithDiagnostics<T>.isIncomplete() = this.reports.any { it.code == ScriptDiagnostic.incompleteCode }
|
||||
|
||||
fun <T> ResultWithDiagnostics<T>.isError() = this is ResultWithDiagnostics.Failure
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package kotlin.script.experimental.jvm.util
|
||||
|
||||
import java.io.Serializable
|
||||
import kotlin.script.experimental.api.SourceCode
|
||||
|
||||
data class AbsSourceCodePosition(val line: Int, val col: Int, val absolutePos: Int) : Serializable
|
||||
|
||||
internal fun String.findNth(s: String, n: Int, start: Int = 0): Int {
|
||||
if (n < 1) return -1
|
||||
|
||||
var i = start
|
||||
|
||||
for (k in 1..n) {
|
||||
i = indexOf(s, i)
|
||||
if (i == -1) return -1
|
||||
i += s.length
|
||||
}
|
||||
|
||||
return i - s.length
|
||||
}
|
||||
|
||||
fun Int.toSourceCodePosition(code: SourceCode): SourceCode.Position {
|
||||
val substr = code.text.substring(0, this)
|
||||
val line = 1 + substr.count { it == '\n' }
|
||||
val sep = code.text.determineSep()
|
||||
val col = 1 + substr.length - substr.lastIndexOf(sep) - sep.length
|
||||
return SourceCode.Position(line, col, this)
|
||||
}
|
||||
|
||||
fun String.determineSep() = if (indexOf("\r\n") == -1) "\n" else "\r\n"
|
||||
|
||||
fun SourceCode.Position.calcAbsolute(code: SourceCode): Int {
|
||||
if (absolutePos != null)
|
||||
return absolutePos!!
|
||||
|
||||
if (line == 1)
|
||||
return col - 1
|
||||
|
||||
val sep = code.text.determineSep()
|
||||
return code.text.findNth(sep, line - 1) + sep.length + col - 1
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package kotlin.script.experimental.jvm.test
|
||||
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
import kotlin.script.experimental.api.SourceCode
|
||||
import kotlin.script.experimental.jvm.util.calcAbsolute
|
||||
import kotlin.script.experimental.jvm.util.toSourceCodePosition
|
||||
|
||||
class CalcAbsoluteTest {
|
||||
|
||||
@Test
|
||||
fun testMultiline() {
|
||||
val source = """
|
||||
abcdefg
|
||||
hij
|
||||
klmnopqrst
|
||||
covid19
|
||||
uv
|
||||
""".trimIndent().toSource()
|
||||
|
||||
val pos = SourceCode.Position(4, 6)
|
||||
|
||||
val absPos = pos.calcAbsolute(source)
|
||||
|
||||
Assert.assertEquals('1', source.text[absPos])
|
||||
Assert.assertEquals(17, 17.toSourceCodePosition(source).calcAbsolute(source))
|
||||
}
|
||||
|
||||
fun String.toSource() = SourceCodeTestImpl(this)
|
||||
|
||||
class SourceCodeTestImpl(override val text: String) : SourceCode {
|
||||
override val name: String? = null
|
||||
override val locationId: String? = null
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user