CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-jetbrains-kotlin--kotlin-scripting-jvm-host

Kotlin Scripting JVM host for executing and compiling Kotlin scripts in JVM environments with JSR-223 integration and comprehensive caching mechanisms

Pending
Overview
Eval results
Files

repl-support.mddocs/

REPL Support

Legacy REPL API compatibility layer for interactive script evaluation and compilation with incremental execution capabilities.

Capabilities

JvmReplCompiler

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
}

JvmReplEvaluator

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()
}

JvmReplEvaluatorState

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
}

ReplStageHistoryWithReplace

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()}")

Source Code Integration

Utility classes for converting between REPL code lines and standard source code representations.

SourceCodeFromReplCodeLine

/**
 * 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?
}

REPL Extension Functions

/**
 * 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
): SourceCode

Internal Utility Classes

The REPL support includes several internal utility classes for bridging between REPL-specific types and standard scripting API types.

SourceCodeFromReplCodeLine

/**
 * 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}")

Integration with Modern Scripting API

The REPL support provides bridge functionality between legacy REPL APIs and modern Kotlin scripting infrastructure.

Migration from Legacy REPL

// 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)) }
}

Performance Considerations

REPL support is optimized for interactive use but has specific performance characteristics:

  • Compilation Overhead: Each line requires separate compilation
  • State Management: History tracking increases memory usage
  • Thread Safety: Synchronization adds latency to operations
  • Dependency Resolution: Repeated dependency loading for each line

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

Types

REPL Evaluation Result Types

// 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

docs

core-scripting-host.md

index.md

jsr223-integration.md

repl-support.md

script-caching.md

script-compilation.md

script-persistence.md

tile.json