Add new REPL API JVM implementation
This commit is contained in:
committed by
Ilya Chernikov
parent
4c2c44b106
commit
d2fec96f38
+3
-2
@@ -6,6 +6,7 @@
|
||||
package kotlin.script.experimental.jvmhost.jsr223
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.repl.*
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplCompilerWithoutCheck
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import javax.script.ScriptContext
|
||||
import javax.script.ScriptEngineFactory
|
||||
@@ -61,8 +62,8 @@ class KotlinJsr223ScriptEngineImpl(
|
||||
}
|
||||
}
|
||||
|
||||
override val replCompiler: ReplCompiler by lazy {
|
||||
JvmReplCompiler(compilationConfiguration)
|
||||
override val replCompiler: ReplCompilerWithoutCheck by lazy {
|
||||
JvmReplCompiler(compilationConfiguration, true)
|
||||
}
|
||||
|
||||
private val localEvaluator by lazy {
|
||||
|
||||
+4
-4
@@ -27,8 +27,8 @@ open class BasicJvmScriptClassFilesGenerator(val outputDir: File) : ScriptEvalua
|
||||
try {
|
||||
if (compiledScript !is KJvmCompiledScript)
|
||||
return failure("Cannot generate classes: unsupported compiled script type $compiledScript")
|
||||
val module = (compiledScript.compiledModule as? KJvmCompiledModuleInMemory)
|
||||
?: return failure("Cannot generate classes: unsupported module type ${compiledScript.compiledModule}")
|
||||
val module = (compiledScript.getCompiledModule() as? KJvmCompiledModuleInMemory)
|
||||
?: return failure("Cannot generate classes: unsupported module type ${compiledScript.getCompiledModule()}")
|
||||
for ((path, bytes) in module.compilerOutputFiles) {
|
||||
File(outputDir, path).apply {
|
||||
if (!parentFile.isDirectory) {
|
||||
@@ -47,8 +47,8 @@ open class BasicJvmScriptClassFilesGenerator(val outputDir: File) : ScriptEvalua
|
||||
}
|
||||
|
||||
fun KJvmCompiledScript.saveToJar(outputJar: File) {
|
||||
val module = (compiledModule as? KJvmCompiledModuleInMemory)
|
||||
?: throw IllegalArgumentException("Unsupported module type $compiledModule")
|
||||
val module = (getCompiledModule() as? KJvmCompiledModuleInMemory)
|
||||
?: throw IllegalArgumentException("Unsupported module type ${getCompiledModule()}")
|
||||
val dependenciesFromScript = compilationConfiguration[ScriptCompilationConfiguration.dependencies]
|
||||
?.filterIsInstance<JvmDependency>()
|
||||
?.flatMap { it.classpath }
|
||||
|
||||
+67
-37
@@ -5,66 +5,96 @@
|
||||
|
||||
package kotlin.script.experimental.jvmhost.repl
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.jetbrains.kotlin.backend.common.push
|
||||
import org.jetbrains.kotlin.cli.common.repl.*
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.KJvmReplCompilerImpl
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplCompilerWithoutCheck
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.KJvmReplCompilerBase
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.JvmReplCompilerStageHistory
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.JvmReplCompilerState
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.KJvmReplCompilerProxy
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplCodeAnalyzerBase
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.write
|
||||
import kotlin.script.experimental.api.*
|
||||
import kotlin.script.experimental.host.ScriptingHostConfiguration
|
||||
import kotlin.script.experimental.host.withDefaultsFrom
|
||||
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
|
||||
import kotlin.script.experimental.jvm.util.isIncomplete
|
||||
|
||||
/**
|
||||
* REPL Compilation wrapper for "legacy" REPL APIs defined in the org.jetbrains.kotlin.cli.common.repl package
|
||||
*/
|
||||
class JvmReplCompiler(
|
||||
val scriptCompilationConfiguration: ScriptCompilationConfiguration,
|
||||
val allowReInit: Boolean = true,
|
||||
val hostConfiguration: ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration,
|
||||
val replCompilerProxy: KJvmReplCompilerProxy = KJvmReplCompilerImpl(
|
||||
var replCompiler: KJvmReplCompilerBase<ReplCodeAnalyzerBase> = KJvmReplCompilerBase.create(
|
||||
hostConfiguration.withDefaultsFrom(defaultJvmScriptingHostConfiguration)
|
||||
)
|
||||
) : ReplCompiler {
|
||||
) : ReplCompilerWithoutCheck {
|
||||
|
||||
override fun createState(lock: ReentrantReadWriteLock): IReplStageState<*> = JvmReplCompilerState(replCompilerProxy, lock)
|
||||
private val compilers = mutableListOf(replCompiler)
|
||||
|
||||
override fun check(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCheckResult = state.lock.write {
|
||||
override fun createState(lock: ReentrantReadWriteLock): IReplStageState<*> =
|
||||
if (allowReInit) {
|
||||
JvmReplCompilerState({ replCompiler.createReplCompilationState(it, replCompiler.initAnalyzer) }, lock)
|
||||
} else {
|
||||
replCompiler.state
|
||||
}
|
||||
|
||||
override fun compile(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCompileResult = replCompiler.state.lock.write {
|
||||
val replCompilerState = state.asState(JvmReplCompilerState::class.java)
|
||||
val compilation = replCompilerState.getCompilationState(scriptCompilationConfiguration)
|
||||
val res =
|
||||
replCompilerProxy.checkSyntax(
|
||||
codeLine.toSourceCode(scriptCompilationConfiguration),
|
||||
compilation.baseScriptCompilationConfiguration,
|
||||
compilation.environment.project
|
||||
)
|
||||
when {
|
||||
// TODO: implement diagnostics rendering
|
||||
res is ResultWithDiagnostics.Success && res.value -> ReplCheckResult.Ok()
|
||||
res is ResultWithDiagnostics.Success && !res.value -> ReplCheckResult.Incomplete()
|
||||
else -> ReplCheckResult.Error(res.reports.joinToString("\n") { it.message })
|
||||
val snippet = codeLine.toSourceCode(scriptCompilationConfiguration)
|
||||
|
||||
if (allowReInit) {
|
||||
replCompiler = compilers.find { historiesEq(it.history, replCompilerState.history) } ?: {
|
||||
compilers.push(
|
||||
KJvmReplCompilerBase.create(
|
||||
hostConfiguration.withDefaultsFrom(defaultJvmScriptingHostConfiguration)
|
||||
)
|
||||
)
|
||||
compilers.last()
|
||||
}()
|
||||
}
|
||||
|
||||
when (val res = runBlocking { replCompiler.compile(listOf(snippet), scriptCompilationConfiguration) }) {
|
||||
is ResultWithDiagnostics.Success -> {
|
||||
val lineId = LineId(codeLine.no, 0, snippet.hashCode())
|
||||
replCompilerState.apply {
|
||||
lock.write {
|
||||
val compilerHistory = history as JvmReplCompilerStageHistory<*>
|
||||
compilerHistory.push(lineId, replCompiler.history.last().item)
|
||||
}
|
||||
}
|
||||
ReplCompileResult.CompiledClasses(
|
||||
lineId,
|
||||
replCompiler.history.map { it.id },
|
||||
snippet.name!!,
|
||||
emptyList(),
|
||||
res.value.get().resultField != null,
|
||||
emptyList(),
|
||||
res.value.get().resultField?.second?.typeName,
|
||||
res.value
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
val message = res.reports.joinToString("\n") { it.message }
|
||||
if (res.isIncomplete()) {
|
||||
ReplCompileResult.Incomplete(message)
|
||||
} else {
|
||||
ReplCompileResult.Error(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun compile(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCompileResult = state.lock.write {
|
||||
val replCompilerState = state.asState(JvmReplCompilerState::class.java)
|
||||
val compilation = replCompilerState.getCompilationState(scriptCompilationConfiguration)
|
||||
val snippet = codeLine.toSourceCode(scriptCompilationConfiguration)
|
||||
val snippetId = ReplSnippetIdImpl(codeLine.no, codeLine.generation, snippet)
|
||||
when (val res = replCompilerProxy.compileReplSnippet(compilation, snippet, snippetId, replCompilerState.history)) {
|
||||
is ResultWithDiagnostics.Success ->
|
||||
ReplCompileResult.CompiledClasses(
|
||||
LineId(codeLine),
|
||||
replCompilerState.history.map { it.id },
|
||||
snippet.name!!,
|
||||
emptyList(),
|
||||
res.value.resultField != null,
|
||||
emptyList(),
|
||||
res.value.resultField?.second?.typeName,
|
||||
res.value
|
||||
)
|
||||
else -> ReplCompileResult.Error(res.reports.joinToString("\n") { it.message })
|
||||
}
|
||||
companion object {
|
||||
fun historiesEq(history1: IReplStageHistory<*>, history2: IReplStageHistory<*>) =
|
||||
history1.count() == history2.count() &&
|
||||
history1.zip(history2).all {
|
||||
val (it1, it2) = it
|
||||
it1.item === it2.item
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+8
-2
@@ -7,6 +7,7 @@ package kotlin.script.experimental.jvmhost.repl
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.jetbrains.kotlin.cli.common.repl.*
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplEvaluator
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.write
|
||||
import kotlin.reflect.KClass
|
||||
@@ -15,6 +16,7 @@ import kotlin.script.experimental.jvm.BasicJvmScriptEvaluator
|
||||
import kotlin.script.experimental.jvm.baseClassLoader
|
||||
import kotlin.script.experimental.jvm.impl.KJvmCompiledScript
|
||||
import kotlin.script.experimental.jvm.jvm
|
||||
import kotlin.script.experimental.util.LinkedSnippetImpl
|
||||
|
||||
/**
|
||||
* REPL Evaluation wrapper for "legacy" REPL APIs defined in the org.jetbrains.kotlin.cli.common.repl package
|
||||
@@ -35,8 +37,12 @@ class JvmReplEvaluator(
|
||||
): ReplEvalResult = state.lock.write {
|
||||
val evalState = state.asState(JvmReplEvaluatorState::class.java)
|
||||
val history = evalState.history as ReplStageHistoryWithReplace
|
||||
val compiledScript = (compileResult.data as? KJvmCompiledScript)
|
||||
?: return ReplEvalResult.Error.CompileTime("Unable to access compiled script: ${compileResult.data}")
|
||||
val compiledScriptList = (compileResult.data as? LinkedSnippetImpl<*>)
|
||||
?: return ReplEvalResult.Error.CompileTime("Unable to access compiled list script: ${compileResult.data}")
|
||||
|
||||
val compiledScript = (compiledScriptList.get() as? KJvmCompiledScript)
|
||||
?: return ReplEvalResult.Error.CompileTime("Unable to access compiled script: ${compiledScriptList.get()}")
|
||||
|
||||
|
||||
val lastSnippetClass = history.peek()?.item?.first
|
||||
val historyBeforeSnippet = history.previousItems(compileResult.lineId).map { it.second }.toList()
|
||||
|
||||
Reference in New Issue
Block a user