Core interfaces and data structures for Kotlin script compilation, evaluation, and IDE integration
—
Host system utilities for integrating Kotlin scripting into applications with script source implementations, configuration management, and execution environments. Provides the foundation for embedding scripting capabilities in host applications.
Abstract base class providing fundamental scripting host functionality with coroutine support and script evaluation.
/**
* Abstract base class for scripting hosts
*/
abstract class BasicScriptingHost {
/**
* Execute code within a coroutine context
* @param body Suspend function to execute
*/
open suspend fun runInCoroutineContext(body: suspend () -> Unit) = body()
/**
* Evaluate a script with compilation and evaluation configurations
* @param script Source code to evaluate
* @param compilationConfiguration Configuration for compilation
* @param evaluationConfiguration Optional configuration for evaluation
* @return Result of script evaluation
*/
suspend fun eval(
script: SourceCode,
compilationConfiguration: ScriptCompilationConfiguration,
evaluationConfiguration: ScriptEvaluationConfiguration? = null
): ResultWithDiagnostics<EvaluationResult>
}Configuration container for host-level scripting settings and services.
/**
* Configuration for scripting host
*/
class ScriptingHostConfiguration : PropertiesCollection {
companion object {
val Default = ScriptingHostConfiguration()
}
}Data structures and functions for creating script definitions from templates.
/**
* Complete definition of a script type with compilation and evaluation configurations
*/
data class ScriptDefinition(
val compilationConfiguration: ScriptCompilationConfiguration,
val evaluationConfiguration: ScriptEvaluationConfiguration
)
/**
* Create script definition from annotated template class
* @param template Class annotated with @KotlinScript
* @param hostConfiguration Optional host configuration
* @return Script definition with resolved configurations
*/
fun createScriptDefinitionFromTemplate(
template: KClass<out Any>,
hostConfiguration: ScriptingHostConfiguration = ScriptingHostConfiguration.Default
): ScriptDefinition
/**
* Create compilation configuration from template class
* @param template Class annotated with @KotlinScript
* @param hostConfiguration Optional host configuration
* @return Resolved compilation configuration
*/
fun createCompilationConfigurationFromTemplate(
template: KClass<out Any>,
hostConfiguration: ScriptingHostConfiguration = ScriptingHostConfiguration.Default
): ScriptCompilationConfiguration
/**
* Create evaluation configuration from template class
* @param template Class annotated with @KotlinScript
* @param hostConfiguration Optional host configuration
* @return Resolved evaluation configuration
*/
fun createEvaluationConfigurationFromTemplate(
template: KClass<out Any>,
hostConfiguration: ScriptingHostConfiguration = ScriptingHostConfiguration.Default
): ScriptEvaluationConfigurationReady-to-use implementations for different types of script sources.
/**
* Abstract base class for file-based script sources
*/
abstract class FileBasedScriptSource : ExternalSourceCode {
/** The file containing the script source */
abstract val file: File
override val text: String get() = file.readText()
override val name: String? get() = file.name
override val locationId: String? get() = file.absolutePath
}
/**
* Script source backed by a file
*/
class FileScriptSource(override val file: File) : FileBasedScriptSource() {
override val externalLocation: String? get() = file.absolutePath
}
/**
* Script source backed by a URL
*/
class UrlScriptSource(
private val url: URL,
override val text: String
) : ExternalSourceCode {
override val name: String? get() = url.file.substringAfterLast('/')
override val locationId: String? get() = url.toString()
override val externalLocation: String? get() = url.toString()
}
/**
* Script source backed by a string
*/
class StringScriptSource(
override val text: String,
override val name: String? = null
) : SourceCode {
override val locationId: String? = name
}Configuration keys for host-level scripting settings.
/**
* Configuration keys for scripting host
*/
interface ScriptingHostConfigurationKeys {
val jvmTarget: PropertiesCollection.Key<String>
val getScriptingClass: PropertiesCollection.Key<GetScriptingClass>
val configurationDependencies: PropertiesCollection.Key<List<ScriptDependency>>
}
/**
* Interface for loading classes in the scripting environment
*/
interface GetScriptingClass {
/**
* Load a class by name
* @param className Fully qualified class name
* @return Loaded class or null if not found
*/
operator fun invoke(className: String): KClass<*>?
}Utility extension functions for host integration.
/**
* Convert File to script source
*/
fun File.toScriptSource(): FileScriptSource
/**
* Convert String to script source with optional name
*/
fun String.toScriptSource(name: String? = null): StringScriptSource
/**
* Get scripting class loader from host configuration
*/
fun ScriptingHostConfiguration.getScriptingClass(): GetScriptingClass?Usage Examples:
import kotlin.script.experimental.host.*
import kotlin.script.experimental.api.*
// Custom scripting host implementation
class MyScriptingHost : BasicScriptingHost() {
private val compiler: ScriptCompiler = TODO("Implementation")
private val evaluator: ScriptEvaluator = TODO("Implementation")
override suspend fun runInCoroutineContext(body: suspend () -> Unit) {
// Custom coroutine context setup
withContext(Dispatchers.IO) {
body()
}
}
suspend fun executeScript(scriptFile: File): ResultWithDiagnostics<Any?> {
val source = scriptFile.toScriptSource()
val compilationConfig = ScriptCompilationConfiguration {
defaultImports("kotlin.math.*")
}
val evaluationConfig = ScriptEvaluationConfiguration.Default
return eval(source, compilationConfig, evaluationConfig).map { result ->
when (val returnValue = result.returnValue) {
is ResultValue.Value -> returnValue.value
is ResultValue.Unit -> Unit
is ResultValue.Error -> throw returnValue.error
is ResultValue.NotEvaluated -> null
}
}
}
}
// Using the custom host
val host = MyScriptingHost()
val scriptFile = File("script.kts")
runBlocking {
when (val result = host.executeScript(scriptFile)) {
is ResultWithDiagnostics.Success -> {
println("Script result: ${result.value}")
}
is ResultWithDiagnostics.Failure -> {
result.reports.forEach { diagnostic ->
println("${diagnostic.severity}: ${diagnostic.message}")
}
}
}
}// Define a script template
@KotlinScript(
displayName = "Data Processing Script",
fileExtension = "data.kts",
compilationConfiguration = DataScriptCompilationConfig::class,
evaluationConfiguration = DataScriptEvaluationConfig::class
)
abstract class DataScript
class DataScriptCompilationConfig : ScriptCompilationConfiguration({
defaultImports("kotlin.collections.*", "kotlin.io.*")
dependencies.append(JvmDependency("org.apache.commons:commons-csv:1.9.0"))
})
class DataScriptEvaluationConfig : ScriptEvaluationConfiguration({
contextVariables.put("dataDir", File("./data"))
})
// Create script definition from template
val scriptDefinition = createScriptDefinitionFromTemplate(DataScript::class)
// Use the definition
val host = MyScriptingHost()
val script = File("process-data.data.kts").toScriptSource()
val result = host.eval(
script,
scriptDefinition.compilationConfiguration,
scriptDefinition.evaluationConfiguration
)// Custom class loader for scripting
class CustomScriptingClassLoader : GetScriptingClass {
private val additionalClassPath = mutableListOf<URL>()
fun addToClassPath(url: URL) {
additionalClassPath.add(url)
}
override fun invoke(className: String): KClass<*>? {
return try {
val classLoader = URLClassLoader(additionalClassPath.toTypedArray())
Class.forName(className, false, classLoader).kotlin
} catch (e: ClassNotFoundException) {
null
}
}
}
// Host configuration with custom class loader
val customClassLoader = CustomScriptingClassLoader().apply {
addToClassPath(File("lib/custom.jar").toURI().toURL())
}
val hostConfig = ScriptingHostConfiguration {
getScriptingClass(customClassLoader)
jvmTarget("1.8")
configurationDependencies(listOf(
JvmDependency("org.jetbrains.kotlin:kotlin-scripting-jvm:1.8.0")
))
}
// Create configurations with custom host config
val compilationConfig = createCompilationConfigurationFromTemplate(
DataScript::class,
hostConfig
)
val evaluationConfig = createEvaluationConfigurationFromTemplate(
DataScript::class,
hostConfig
)// Various ways to create script sources
val fileSource = File("script.kts").toScriptSource()
val stringSource = "println(\"Hello World\")".toScriptSource("hello.kts")
// URL-based script source
val url = URL("https://example.com/script.kts")
val urlText = url.readText()
val urlSource = UrlScriptSource(url, urlText)
// Custom script source with caching
class CachedFileScriptSource(override val file: File) : FileBasedScriptSource() {
private var cachedText: String? = null
private var lastModified: Long = 0
override val text: String
get() {
val currentModified = file.lastModified()
if (cachedText == null || currentModified > lastModified) {
cachedText = file.readText()
lastModified = currentModified
}
return cachedText!!
}
override val externalLocation: String? get() = file.absolutePath
}
// Database-backed script source
class DatabaseScriptSource(
private val scriptId: String,
private val database: ScriptDatabase
) : ExternalSourceCode {
override val text: String by lazy { database.getScriptContent(scriptId) }
override val name: String? = "script_$scriptId"
override val locationId: String? = "db:$scriptId"
override val externalLocation: String? = "database://scripts/$scriptId"
}// Plugin-based scripting system
class PluginScriptingHost : BasicScriptingHost() {
private val plugins = mutableMapOf<String, ScriptPlugin>()
fun registerPlugin(name: String, plugin: ScriptPlugin) {
plugins[name] = plugin
}
suspend fun executeWithPlugins(script: SourceCode): ResultWithDiagnostics<Any?> {
// Pre-execution plugin hooks
plugins.values.forEach { it.beforeExecution(script) }
val result = eval(script, getCompilationConfig(), getEvaluationConfig())
// Post-execution plugin hooks
plugins.values.forEach { it.afterExecution(script, result) }
return result.map { evaluationResult ->
when (val returnValue = evaluationResult.returnValue) {
is ResultValue.Value -> returnValue.value
else -> null
}
}
}
}
interface ScriptPlugin {
suspend fun beforeExecution(script: SourceCode)
suspend fun afterExecution(script: SourceCode, result: ResultWithDiagnostics<EvaluationResult>)
}
// Service-based scripting host
class ServiceBasedScriptingHost(
private val compiler: ScriptCompiler,
private val evaluator: ScriptEvaluator,
private val logger: Logger
) : BasicScriptingHost() {
override suspend fun runInCoroutineContext(body: suspend () -> Unit) {
logger.info("Starting script execution")
try {
withTimeout(30000) { // 30 second timeout
body()
}
} catch (e: TimeoutCancellationException) {
logger.error("Script execution timed out")
throw e
} finally {
logger.info("Script execution completed")
}
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-jetbrains-kotlin--kotlin-scripting-common