Kotlin Scripting JVM host for executing and compiling Kotlin scripts in JVM environments with JSR-223 integration and comprehensive caching mechanisms
—
Legacy REPL API compatibility layer for interactive script evaluation and compilation with incremental execution capabilities.
REPL compilation wrapper that provides legacy REPL API compatibility for incremental script compilation.
/**
* REPL compiler implementation for legacy REPL APIs
* @param scriptCompilationConfiguration Compilation configuration for REPL scripts
* @param hostConfiguration Host configuration with JVM-specific settings
*/
class JvmReplCompiler(
val scriptCompilationConfiguration: ScriptCompilationConfiguration,
val hostConfiguration: ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration
) : ReplCompilerWithoutCheck {
/**
* Creates new REPL compilation state
* @param lock Read-write lock for thread synchronization
* @returns New REPL stage state for compilation
*/
override fun createState(lock: ReentrantReadWriteLock): IReplStageState<*>
/**
* Compiles a single REPL code line
* @param state Current REPL compilation state
* @param codeLine REPL code line to compile
* @returns Compilation result with compiled classes or errors
*/
override fun compile(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCompileResult
}REPL evaluation wrapper that executes compiled REPL scripts with state management and history tracking.
/**
* REPL evaluator implementation for legacy REPL APIs
* @param baseScriptEvaluationConfiguration Base evaluation configuration
* @param scriptEvaluator Script evaluator for executing compiled scripts
*/
class JvmReplEvaluator(
val baseScriptEvaluationConfiguration: ScriptEvaluationConfiguration,
val scriptEvaluator: ScriptEvaluator = BasicJvmScriptEvaluator()
) : ReplEvaluator {
/**
* Creates new REPL evaluation state
* @param lock Read-write lock for thread synchronization
* @returns New REPL stage state for evaluation
*/
override fun createState(lock: ReentrantReadWriteLock): IReplStageState<*>
/**
* Evaluates compiled REPL script classes
* @param state Current REPL evaluation state
* @param compileResult Compiled classes from REPL compiler
* @param scriptArgs Optional script arguments with types
* @param invokeWrapper Optional wrapper for function invocation
* @returns Evaluation result with value or error
*/
override fun eval(
state: IReplStageState<*>,
compileResult: ReplCompileResult.CompiledClasses,
scriptArgs: ScriptArgsWithTypes? = null,
invokeWrapper: InvokeWrapper? = null
): ReplEvalResult
}Usage Examples:
import kotlin.script.experimental.jvmhost.repl.*
import kotlin.script.experimental.api.*
import kotlin.script.experimental.jvm.util.*
import java.util.concurrent.locks.ReentrantReadWriteLock
// Create REPL compiler and evaluator
val replConfig = ScriptCompilationConfiguration {
dependencies(JvmDependency(kotlinStdlib))
compilerOptions.append("-language-version", "2.0")
}
val evalConfig = ScriptEvaluationConfiguration {
// REPL-specific evaluation configuration
enableScriptsInstancesSharing()
}
val replCompiler = JvmReplCompiler(
scriptCompilationConfiguration = replConfig,
hostConfiguration = defaultJvmScriptingHostConfiguration
)
val replEvaluator = JvmReplEvaluator(
baseScriptEvaluationConfiguration = evalConfig
)
// Initialize REPL states
val lock = ReentrantReadWriteLock()
val compilerState = replCompiler.createState(lock)
val evaluatorState = replEvaluator.createState(lock)
// Interactive REPL session
val replLines = listOf(
"val x = 42",
"val y = x * 2",
"data class Person(val name: String, val age: Int)",
"val person = Person(\"Alice\", 30)",
"println(\"Person: \$person, y = \$y\")",
"person.age + y"
)
replLines.forEachIndexed { index, code ->
println("[$index] $code")
// Compile line
val codeLine = ReplCodeLine(index, 0, code)
val compileResult = replCompiler.compile(compilerState, codeLine)
when (compileResult) {
is ReplCompileResult.CompiledClasses -> {
println(" Compiled successfully")
// Evaluate compiled code
val evalResult = replEvaluator.eval(evaluatorState, compileResult)
when (evalResult) {
is ReplEvalResult.ValueResult -> {
println(" Result: ${evalResult.value} : ${evalResult.type}")
}
is ReplEvalResult.UnitResult -> {
println(" Executed successfully")
}
is ReplEvalResult.Error -> {
println(" Evaluation error: ${evalResult.error.message}")
}
is ReplEvalResult.Incomplete -> {
println(" Incomplete input")
}
}
}
is ReplCompileResult.Error -> {
println(" Compilation error: ${compileResult.message}")
}
is ReplCompileResult.Incomplete -> {
println(" Incomplete input - expecting more")
}
}
println()
}REPL evaluator state implementation that manages execution history and supports script instance sharing.
/**
* REPL evaluator state with history management
* @param scriptEvaluationConfiguration Evaluation configuration for REPL scripts
* @param lock Read-write lock for thread synchronization
*/
open class JvmReplEvaluatorState(
val scriptEvaluationConfiguration: ScriptEvaluationConfiguration,
lock: ReentrantReadWriteLock
) : IReplStageState<Any> {
/** History of evaluated script instances and their results */
val history: IReplStageHistory<Pair<KClass<*>?, Any?>>
/** Current generation/version of the REPL state */
val currentGeneration: Int
}Enhanced REPL history implementation that supports replacing and updating previous evaluations.
/**
* REPL stage history with replacement capabilities
* @param lock Read-write lock for thread synchronization
*/
open class ReplStageHistoryWithReplace<T>(lock: ReentrantReadWriteLock) : IReplStageHistory<T> {
/**
* Replaces an existing history entry
* @param id Line identifier to replace
* @param item New item to replace with
* @returns True if replacement succeeded, false if not found
*/
fun replace(id: ILineId, item: T): Boolean
/**
* Replaces existing entry or pushes new one if not found
* @param id Line identifier
* @param item Item to replace or add
*/
fun replaceOrPush(id: ILineId, item: T)
/**
* Gets sequence of items that were evaluated before the given line
* @param id Line identifier to get previous items for
* @returns Sequence of previous items in order
*/
fun previousItems(id: ILineId): Sequence<T>
}Advanced REPL Example with History Management:
import kotlin.script.experimental.jvmhost.repl.*
// Create REPL with custom history management
class InteractiveReplSession {
private val lock = ReentrantReadWriteLock()
private val replCompiler = JvmReplCompiler(
ScriptCompilationConfiguration {
dependencies(JvmDependency(kotlinStdlib))
// Enable script instance sharing for REPL
refineConfiguration {
onAnnotations { context ->
ScriptCompilationConfiguration(context.compilationConfiguration) {
implicitReceivers(String::class) // Example: implicit string receiver
}.asSuccess()
}
}
}
)
private val replEvaluator = JvmReplEvaluator(
ScriptEvaluationConfiguration {
enableScriptsInstancesSharing()
// Provide previous results as context
providedProperties("results" to List::class)
}
)
private val compilerState = replCompiler.createState(lock)
private val evaluatorState = replEvaluator.createState(lock)
private val results = mutableListOf<Any?>()
fun evalLine(lineNumber: Int, code: String): ReplEvalResult {
val codeLine = ReplCodeLine(lineNumber, 0, code)
// Compile
val compileResult = replCompiler.compile(compilerState, codeLine)
return when (compileResult) {
is ReplCompileResult.CompiledClasses -> {
// Evaluate with context
val evalResult = replEvaluator.eval(evaluatorState, compileResult)
// Store result for future reference
when (evalResult) {
is ReplEvalResult.ValueResult -> {
results.add(evalResult.value)
}
is ReplEvalResult.UnitResult -> {
results.add(Unit)
}
else -> {
results.add(null)
}
}
evalResult
}
is ReplCompileResult.Error -> {
ReplEvalResult.Error.CompileTime(
"Compilation failed: ${compileResult.message}"
)
}
is ReplCompileResult.Incomplete -> {
ReplEvalResult.Incomplete()
}
}
}
fun replaceLineAndReevaluate(lineNumber: Int, newCode: String): List<ReplEvalResult> {
// This would require more sophisticated state management
// to re-evaluate dependent lines after replacement
val results = mutableListOf<ReplEvalResult>()
// Replace the specific line
val newResult = evalLine(lineNumber, newCode)
results.add(newResult)
// Re-evaluate subsequent lines that might depend on this change
// This is a simplified example - real implementation would need
// dependency tracking and selective re-evaluation
return results
}
fun getHistory(): List<Any?> = results.toList()
fun getCurrentGeneration(): Int {
return (evaluatorState as? JvmReplEvaluatorState)?.currentGeneration ?: 0
}
}
// Usage example
val replSession = InteractiveReplSession()
// Interactive session
val inputs = listOf(
"val numbers = listOf(1, 2, 3, 4, 5)",
"val doubled = numbers.map { it * 2 }",
"println(\"Original: \$numbers\")",
"println(\"Doubled: \$doubled\")",
"doubled.sum()"
)
inputs.forEachIndexed { index, input ->
println("> $input")
val result = replSession.evalLine(index, input)
when (result) {
is ReplEvalResult.ValueResult -> {
println(" = ${result.value} : ${result.type}")
}
is ReplEvalResult.UnitResult -> {
println(" (executed)")
}
is ReplEvalResult.Error -> {
println(" Error: ${result.message}")
}
is ReplEvalResult.Incomplete -> {
println(" (incomplete)")
}
}
}
println("\nSession history: ${replSession.getHistory()}")
println("Generation: ${replSession.getCurrentGeneration()}")Utility classes for converting between REPL code lines and standard source code representations.
/**
* Internal source code implementation from REPL code line
* @param codeLine REPL code line to wrap
* @param compilationConfiguration Compilation configuration for context
*/
internal class SourceCodeFromReplCodeLine(
val codeLine: ReplCodeLine,
compilationConfiguration: ScriptCompilationConfiguration
) : SourceCode {
/** Script text content from REPL line */
override val text: String
/** Script name derived from REPL line identifier */
override val name: String
/** Location identifier for error reporting */
override val locationId: String?
}/**
* Converts REPL code line to source code for compilation
* @param compilationConfiguration Compilation configuration for context
* @returns SourceCode representation of the REPL line
*/
internal fun ReplCodeLine.toSourceCode(
compilationConfiguration: ScriptCompilationConfiguration
): SourceCodeThe REPL support includes several internal utility classes for bridging between REPL-specific types and standard scripting API types.
/**
* Internal source code implementation that wraps REPL code lines
* @param codeLine The REPL code line to wrap
* @param compilationConfiguration Compilation configuration for context
*/
internal class SourceCodeFromReplCodeLine(
val codeLine: ReplCodeLine,
compilationConfiguration: ScriptCompilationConfiguration
) : SourceCode {
/** Script text content from the REPL line */
override val text: String
/** Script name derived from REPL line identifier and sequence number */
override val name: String
/** Location identifier for error reporting and source tracking */
override val locationId: String?
}Note: This class is internal to the REPL implementation and is used to bridge between REPL-specific ReplCodeLine objects and the standard SourceCode interface used by the compilation system.
Source Code Integration Example:
import kotlin.script.experimental.jvmhost.repl.*
// Custom REPL implementation with source code tracking
class SourceTrackingRepl {
private val sourceHistory = mutableMapOf<Int, SourceCode>()
fun compileWithSourceTracking(
compiler: JvmReplCompiler,
state: IReplStageState<*>,
lineNumber: Int,
code: String
): ReplCompileResult {
val codeLine = ReplCodeLine(lineNumber, 0, code)
// Convert to source code for tracking
val sourceCode = codeLine.toSourceCode(compiler.scriptCompilationConfiguration)
sourceHistory[lineNumber] = sourceCode
// Compile normally
val result = compiler.compile(state, codeLine)
// Log compilation with source information
when (result) {
is ReplCompileResult.CompiledClasses -> {
println("Compiled line $lineNumber: ${sourceCode.name}")
println(" Source location: ${sourceCode.locationId}")
println(" Text length: ${sourceCode.text.length} characters")
}
is ReplCompileResult.Error -> {
println("Compilation failed for line $lineNumber")
println(" Source: ${sourceCode.name}")
println(" Error: ${result.message}")
}
}
return result
}
fun getSourceForLine(lineNumber: Int): SourceCode? {
return sourceHistory[lineNumber]
}
fun getAllSources(): Map<Int, SourceCode> {
return sourceHistory.toMap()
}
}
// Usage
val sourceTracker = SourceTrackingRepl()
val compiler = JvmReplCompiler(ScriptCompilationConfiguration.Default)
val state = compiler.createState(ReentrantReadWriteLock())
// Compile with source tracking
val result = sourceTracker.compileWithSourceTracking(
compiler, state, 1, "val greeting = \"Hello, REPL!\""
)
// Access source information
val source = sourceTracker.getSourceForLine(1)
println("Source name: ${source?.name}")
println("Source location: ${source?.locationId}")The REPL support provides bridge functionality between legacy REPL APIs and modern Kotlin scripting infrastructure.
// Legacy REPL usage (deprecated)
// val legacyRepl = KotlinJsr223JvmLocalScriptEngine()
// Modern REPL usage through compatibility layer
val modernReplCompiler = JvmReplCompiler(
ScriptCompilationConfiguration {
dependencies(JvmDependency(kotlinStdlib))
baseClass(KotlinType(Any::class))
}
)
val modernReplEvaluator = JvmReplEvaluator(
ScriptEvaluationConfiguration {
enableScriptsInstancesSharing()
jvm {
baseClassLoader(Thread.currentThread().contextClassLoader)
}
}
)
// Bridge to JSR-223 if needed
val jsr223Engine = KotlinJsr223ScriptEngineImpl(
factory = KotlinJsr223DefaultScriptEngineFactory(),
baseCompilationConfiguration = modernReplCompiler.scriptCompilationConfiguration,
baseEvaluationConfiguration = modernReplEvaluator.baseScriptEvaluationConfiguration
) { context ->
// Extract arguments from JSR-223 context
val args = context.getBindings(ScriptContext.ENGINE_SCOPE)["args"] as? Array<String>
args?.let { ScriptArgsWithTypes(it, arrayOf(Array<String>::class)) }
}REPL support is optimized for interactive use but has specific performance characteristics:
Performance Optimization Example:
class OptimizedReplSession {
// Reuse configurations to avoid repeated setup
private val sharedCompilationConfig = ScriptCompilationConfiguration {
dependencies(JvmDependency(kotlinStdlib))
// Cache dependencies
hostConfiguration(ScriptingHostConfiguration {
jvm {
compilationCache(FileBasedScriptCache(File("repl-cache")))
}
})
}
// Batch compilation for related lines
fun compileMultipleLines(lines: List<String>): List<ReplCompileResult> {
val compiler = JvmReplCompiler(sharedCompilationConfig)
val state = compiler.createState(ReentrantReadWriteLock())
return lines.mapIndexed { index, code ->
val codeLine = ReplCodeLine(index, 0, code)
compiler.compile(state, codeLine)
}
}
// Lazy evaluation for expensive operations
fun evaluateWithLazyResults(
compileResults: List<ReplCompileResult.CompiledClasses>
): Sequence<ReplEvalResult> {
val evaluator = JvmReplEvaluator(ScriptEvaluationConfiguration.Default)
val state = evaluator.createState(ReentrantReadWriteLock())
return compileResults.asSequence().map { compiled ->
evaluator.eval(state, compiled)
}
}
}// Imported from kotlin.script.experimental.api and related packages
sealed class ReplEvalResult : Serializable {
/** Successful evaluation with a returned value */
class ValueResult(
val name: String,
val value: Any?,
val type: String?,
val snippetInstance: Any? = null
) : ReplEvalResult()
/** Successful evaluation with no return value */
class UnitResult : ReplEvalResult()
/** Incomplete input requiring more code */
class Incomplete(val message: String) : ReplEvalResult()
/** History state mismatch error */
class HistoryMismatch(val lineNo: Int) : ReplEvalResult()
/** Evaluation errors */
sealed class Error(val message: String) : ReplEvalResult() {
/** Runtime execution error */
class Runtime(message: String, val cause: Throwable? = null) : Error(message)
/** Compile-time error */
class CompileTime(message: String, val location: CompilerMessageLocation? = null) : Error(message)
}
}
/** Compilation result for REPL code lines */
sealed class ReplCompileResult {
class CompiledClasses(
val lineId: ReplCodeLine.Id,
val data: Any,
val hasResult: Boolean,
val classpathAddendum: List<File> = emptyList()
) : ReplCompileResult()
class Incomplete : ReplCompileResult()
class Error(
val message: String,
val location: CompilerMessageLocation? = null
) : ReplCompileResult()
}Install with Tessl CLI
npx tessl i tessl/maven-org-jetbrains-kotlin--kotlin-scripting-jvm-host