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

script-compilation.mddocs/

Script Compilation

The script compilation system provides JVM script compilation capabilities using the Kotlin compiler infrastructure with comprehensive configuration support and proxy-based compilation.

Capabilities

JvmScriptCompiler

JVM script compiler implementation that handles script compilation with configurable host configuration and compiler proxy.

/**
 * JVM script compiler for compiling Kotlin scripts with proxy-based compilation
 * @param baseHostConfiguration Base host configuration for compilation
 * @param compilerProxy Optional custom compiler proxy, defaults to ScriptJvmCompilerIsolated
 */
open class JvmScriptCompiler(
    baseHostConfiguration: ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration,
    compilerProxy: ScriptCompilerProxy? = null
) : ScriptCompiler {
    
    /** Final host configuration with defaults applied */
    val hostConfiguration: ScriptingHostConfiguration
    
    /** Compiler proxy for performing actual compilation */
    val compilerProxy: ScriptCompilerProxy
    
    /**
     * Compiles a script with the given configuration
     * @param script Source code to compile
     * @param scriptCompilationConfiguration Compilation configuration
     * @returns Result with diagnostics containing compiled script or errors
     */
    override suspend operator fun invoke(
        script: SourceCode,
        scriptCompilationConfiguration: ScriptCompilationConfiguration
    ): ResultWithDiagnostics<CompiledScript>
}

Usage Examples:

import kotlin.script.experimental.jvmhost.JvmScriptCompiler
import kotlin.script.experimental.api.*
import kotlin.script.experimental.jvm.*

// Basic compiler usage
val compiler = JvmScriptCompiler()

val script = """
    val message = "Hello from compiled script!"
    println(message)
    message.length
""".trimIndent()

val compilationConfig = ScriptCompilationConfiguration {
    dependencies(JvmDependency(kotlinStdlib))
    compilerOptions.append("-opt-in=kotlin.RequiresOptIn")
}

val compilationResult = compiler(script.toScriptSource(), compilationConfig)

when (compilationResult) {
    is ResultWithDiagnostics.Success -> {
        val compiledScript = compilationResult.value
        println("Compilation successful: ${compiledScript.sourceLocationId}")
        
        // Get script class
        val classResult = compiledScript.getClass()
        when (classResult) {
            is ResultWithDiagnostics.Success -> {
                println("Script class: ${classResult.value.simpleName}")
            }
            is ResultWithDiagnostics.Failure -> {
                println("Failed to get script class")
            }
        }
    }
    is ResultWithDiagnostics.Failure -> {
        compilationResult.reports.forEach { 
            println("Compilation error: ${it.message}")
        }
    }
}

// Advanced compiler with custom configuration
val customHostConfiguration = ScriptingHostConfiguration {
    jvm {
        // Custom JVM configuration
        compilationCache(FileBasedScriptCache(File("cache")))
    }
}

val advancedCompiler = JvmScriptCompiler(
    baseHostConfiguration = customHostConfiguration
)

val advancedCompilationConfig = ScriptCompilationConfiguration {
    // Template-based configuration
    baseClass(KotlinType(Any::class))
    
    // Dependencies
    dependencies(
        JvmDependency(File("lib1.jar")),
        JvmDependency(File("lib2.jar"))
    )
    
    // Import scripts
    importScripts(FileBasedScriptSource(File("common.kts")))
    
    // Compiler options
    compilerOptions.append(
        "-language-version", "2.0",
        "-api-version", "2.0"
    )
    
    // IDE support
    ide {
        acceptedLocations(ScriptAcceptedLocation.Everywhere)
    }
}

val advancedResult = advancedCompiler(script.toScriptSource(), advancedCompilationConfig)

Compilation Configuration

The compilation system uses ScriptCompilationConfiguration to control various aspects of script compilation.

Key Configuration Options

// Core configuration builders (imported from kotlin.script.experimental.api)
fun ScriptCompilationConfiguration.Builder.dependencies(vararg dependencies: ScriptDependency): ScriptCompilationConfiguration.Builder
fun ScriptCompilationConfiguration.Builder.importScripts(vararg scripts: SourceCode): ScriptCompilationConfiguration.Builder
fun ScriptCompilationConfiguration.Builder.compilerOptions(vararg options: String): ScriptCompilationConfiguration.Builder
fun ScriptCompilationConfiguration.Builder.baseClass(baseClass: KotlinType): ScriptCompilationConfiguration.Builder
fun ScriptCompilationConfiguration.Builder.hostConfiguration(configuration: ScriptingHostConfiguration): ScriptCompilationConfiguration.Builder

Configuration Examples:

// Dependency configuration
val configWithDependencies = ScriptCompilationConfiguration {
    dependencies(
        JvmDependency(kotlinStdlib),
        JvmDependency(File("my-library.jar")),
        JvmDependency("org.apache.commons:commons-lang3:3.12.0") // Maven coordinates
    )
}

// Import scripts configuration
val configWithImports = ScriptCompilationConfiguration {
    importScripts(
        FileBasedScriptSource(File("utilities.kts")),
        StringScriptSource("val commonConstant = 42", "common.kts")
    )
}

// Compiler options configuration
val configWithOptions = ScriptCompilationConfiguration {
    compilerOptions.append(
        "-opt-in=kotlin.RequiresOptIn",
        "-Xmulti-platform",
        "-language-version", "2.0"
    )
}

// Template-based configuration
@KotlinScript(
    fileExtension = "custom.kts",
    compilationConfiguration = CustomScriptConfiguration::class
)
class CustomScriptTemplate

object CustomScriptConfiguration : ScriptCompilationConfiguration({
    baseClass(KotlinType(CustomScriptTemplate::class))
    dependencies(JvmDependency(kotlinStdlib))
    compilerOptions.append("-opt-in=kotlin.ExperimentalStdlibApi")
})

val templateConfig = ScriptCompilationConfiguration {
    baseClass(KotlinType(CustomScriptTemplate::class))
    refineConfiguration {
        // Dynamic configuration refinement
        onAnnotations(DependsOn::class, Repository::class) { context ->
            // Process annotations to add dependencies
            context.collectedData?.get(DependsOn::class)?.let { dependsOnList ->
                ScriptCompilationConfiguration(context.compilationConfiguration) {
                    dependencies.append(JvmDependency(dependsOnList.flatMap { it.artifacts }))
                }.asSuccess()
            } ?: context.compilationConfiguration.asSuccess()
        }
    }
}

Compiler Proxy Integration

The JVM script compiler uses a proxy pattern to delegate actual compilation to pluggable compiler implementations.

// Compiler proxy interface (from kotlin.scripting.compiler.plugin)
interface ScriptCompilerProxy {
    suspend fun compile(
        script: SourceCode,
        scriptCompilationConfiguration: ScriptCompilationConfiguration
    ): ResultWithDiagnostics<CompiledScript>
}

Custom Compiler Proxy Example:

import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptCompilerProxy

class CustomScriptCompilerProxy(
    private val hostConfiguration: ScriptingHostConfiguration
) : ScriptCompilerProxy {
    
    override suspend fun compile(
        script: SourceCode,
        scriptCompilationConfiguration: ScriptCompilationConfiguration
    ): ResultWithDiagnostics<CompiledScript> {
        // Custom compilation logic
        return try {
            // Perform custom pre-processing
            val processedScript = preprocessScript(script)
            
            // Delegate to default compiler
            val defaultProxy = ScriptJvmCompilerIsolated(hostConfiguration)
            defaultProxy.compile(processedScript, scriptCompilationConfiguration)
        } catch (e: Exception) {
            ResultWithDiagnostics.Failure(
                listOf(e.asErrorDiagnostics("Custom compilation failed"))
            )
        }
    }
    
    private fun preprocessScript(script: SourceCode): SourceCode {
        // Custom preprocessing logic
        return StringScriptSource(
            script.text.replace("@CustomMacro", "// Expanded macro"),
            script.name
        )
    }
}

// Use custom proxy
val customCompiler = JvmScriptCompiler(
    compilerProxy = CustomScriptCompilerProxy(defaultJvmScriptingHostConfiguration)
)

Compilation Results

The compilation process returns CompiledScript instances that encapsulate the compiled script artifacts.

CompiledScript Interface

interface CompiledScript {
    /** Compilation configuration used to compile this script */
    val compilationConfiguration: ScriptCompilationConfiguration
    
    /** Source location identifier for error reporting */
    val sourceLocationId: String?
    
    /** Other scripts that were compiled together with this script */
    val otherScripts: List<CompiledScript>
    
    /** Result field information if script has a result expression */
    val resultField: Pair<String, KotlinType>?
    
    /**
     * Gets the compiled script class
     * @param scriptEvaluationConfiguration Optional evaluation configuration
     * @returns Result containing the script class or compilation errors
     */
    suspend fun getClass(scriptEvaluationConfiguration: ScriptEvaluationConfiguration? = null): ResultWithDiagnostics<KClass<*>>
}

Working with Compiled Scripts:

val compilationResult = compiler(script.toScriptSource(), compilationConfig)

when (compilationResult) {
    is ResultWithDiagnostics.Success -> {
        val compiledScript = compilationResult.value
        
        // Access compilation metadata
        println("Source: ${compiledScript.sourceLocationId}")
        println("Has result field: ${compiledScript.resultField != null}")
        println("Other scripts: ${compiledScript.otherScripts.size}")
        
        // Get script class for evaluation
        val classResult = compiledScript.getClass()
        when (classResult) {
            is ResultWithDiagnostics.Success -> {
                val scriptClass = classResult.value
                
                // Create script instance
                val constructor = scriptClass.constructors.first()
                val scriptInstance = constructor.call()
                
                // Access result field if available
                compiledScript.resultField?.let { (fieldName, fieldType) ->
                    val field = scriptClass.java.getDeclaredField(fieldName)
                    field.isAccessible = true
                    val result = field.get(scriptInstance)
                    println("Script result: $result (${fieldType.typeName})")
                }
            }
            is ResultWithDiagnostics.Failure -> {
                println("Failed to get script class")
            }
        }
    }
    is ResultWithDiagnostics.Failure -> {
        println("Compilation failed")
    }
}

Error Handling

Compilation errors are reported through the diagnostic system with detailed location information and error categories.

Common Compilation Errors

  • Syntax Errors: Invalid Kotlin syntax in script code
  • Type Errors: Type mismatches, unresolved references
  • Dependency Errors: Missing or invalid dependencies
  • Import Errors: Failed to import or resolve imported scripts
  • Configuration Errors: Invalid compilation configuration

Detailed Error Handling:

val result = compiler(script.toScriptSource(), compilationConfig)

when (result) {
    is ResultWithDiagnostics.Failure -> {
        result.reports.groupBy { it.severity }.forEach { (severity, reports) ->
            println("$severity errors:")
            reports.forEach { diagnostic ->
                val location = diagnostic.location?.let { loc ->
                    " at line ${loc.start.line}, column ${loc.start.col}"
                } ?: ""
                
                println("  ${diagnostic.message}$location")
                
                // Show exception details if available
                diagnostic.exception?.let { ex ->
                    println("    Caused by: ${ex.javaClass.simpleName}: ${ex.message}")
                    ex.stackTrace.take(3).forEach { frame ->
                        println("      at $frame")
                    }
                }
            }
        }
    }
}

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