CtrlK
BlogDocsLog inGet started
Tessl Logo

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

Kotlin JVM scripting support library that provides core functionality for executing and evaluating Kotlin scripts on the JVM platform

Pending
Overview
Eval results
Files

caching.mddocs/

Caching System

Script compilation caching interface with configurable cache implementations for improved performance in repeated script execution scenarios. The caching system supports both in-memory and persistent storage strategies.

Capabilities

Compiled Scripts Cache Interface

Core interface for caching compiled JVM scripts with flexible storage and retrieval mechanisms.

/**
 * Cache interface for compiled JVM scripts
 * Provides storage and retrieval of compiled scripts to improve performance
 */
interface CompiledJvmScriptsCache {
    /**
     * Retrieve cached compiled script
     * @param script Source code of the script
     * @param scriptCompilationConfiguration Configuration used for compilation
     * @return Cached compiled script or null if not found
     */
    fun get(
        script: SourceCode,
        scriptCompilationConfiguration: ScriptCompilationConfiguration
    ): CompiledScript?
    
    /**
     * Store compiled script in cache
     * @param compiledScript The compiled script to cache
     * @param script Original source code
     * @param scriptCompilationConfiguration Configuration used for compilation
     */
    fun store(
        compiledScript: CompiledScript,
        script: SourceCode,
        scriptCompilationConfiguration: ScriptCompilationConfiguration
    )
    
    /**
     * No-operation cache implementation
     * Does not store or retrieve any scripts
     */
    object NoCache : CompiledJvmScriptsCache {
        override fun get(
            script: SourceCode,
            scriptCompilationConfiguration: ScriptCompilationConfiguration
        ): CompiledScript? = null
        
        override fun store(
            compiledScript: CompiledScript,
            script: SourceCode,
            scriptCompilationConfiguration: ScriptCompilationConfiguration
        ) = Unit
    }
}

Usage Examples:

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

// Custom in-memory cache implementation
class InMemoryScriptCache : CompiledJvmScriptsCache {
    private val cache = mutableMapOf<String, CompiledScript>()
    
    private fun generateCacheKey(
        script: SourceCode,
        config: ScriptCompilationConfiguration
    ): String {
        return "${script.text.hashCode()}-${config.hashCode()}"
    }
    
    override fun get(
        script: SourceCode,
        scriptCompilationConfiguration: ScriptCompilationConfiguration
    ): CompiledScript? {
        val key = generateCacheKey(script, scriptCompilationConfiguration)
        return cache[key]
    }
    
    override fun store(
        compiledScript: CompiledScript,
        script: SourceCode,
        scriptCompilationConfiguration: ScriptCompilationConfiguration
    ) {
        val key = generateCacheKey(script, scriptCompilationConfiguration)
        cache[key] = compiledScript
    }
}

// Use cache in compilation configuration
val scriptCache = InMemoryScriptCache()

val compilationConfig = ScriptCompilationConfiguration {
    jvm {
        compilationCache = scriptCache
    }
}

Cache Configuration Property

Configuration property for specifying cache implementation in script compilation settings.

/**
 * Property key for compiled scripts cache in compilation configuration
 */
val ScriptCompilationConfiguration.compilationCache: PropertiesCollection.Key<CompiledJvmScriptsCache>

Usage Example:

// Configure cache in compilation settings
val config = ScriptCompilationConfiguration {
    compilationCache = InMemoryScriptCache()
    // ... other configuration
}

// Use NoCache for no caching
val noCacheConfig = ScriptCompilationConfiguration {
    compilationCache = CompiledJvmScriptsCache.NoCache
}

Implementation Examples

File-Based Persistent Cache

import java.io.File
import java.security.MessageDigest

class FileBasedScriptCache(private val cacheDirectory: File) : CompiledJvmScriptsCache {
    
    init {
        cacheDirectory.mkdirs()
    }
    
    private fun generateCacheKey(
        script: SourceCode,
        config: ScriptCompilationConfiguration
    ): String {
        val content = "${script.text}-${config.toString()}"
        val digest = MessageDigest.getInstance("SHA-256")
        val hash = digest.digest(content.toByteArray())
        return hash.joinToString("") { "%02x".format(it) }
    }
    
    private fun getCacheFile(key: String): File {
        return File(cacheDirectory, "$key.cached")
    }
    
    override fun get(
        script: SourceCode,
        scriptCompilationConfiguration: ScriptCompilationConfiguration
    ): CompiledScript? {
        val key = generateCacheKey(script, scriptCompilationConfiguration)
        val cacheFile = getCacheFile(key)
        
        return if (cacheFile.exists()) {
            try {
                // Deserialize cached script
                deserializeCompiledScript(cacheFile.readBytes())
            } catch (e: Exception) {
                // Cache corruption, remove file
                cacheFile.delete()
                null
            }
        } else {
            null
        }
    }
    
    override fun store(
        compiledScript: CompiledScript,
        script: SourceCode,
        scriptCompilationConfiguration: ScriptCompilationConfiguration
    ) {
        val key = generateCacheKey(script, scriptCompilationConfiguration)
        val cacheFile = getCacheFile(key)
        
        try {
            val serializedScript = (compiledScript as KJvmCompiledScript).toBytes()
            cacheFile.writeBytes(serializedScript)
        } catch (e: Exception) {
            // Failed to cache, continue without caching
            println("Warning: Failed to cache script: ${e.message}")
        }
    }
    
    fun clearCache() {
        cacheDirectory.listFiles()?.forEach { it.delete() }
    }
    
    fun getCacheSize(): Long {
        return cacheDirectory.listFiles()?.sumOf { it.length() } ?: 0L
    }
}

LRU In-Memory Cache

import java.util.LinkedHashMap

class LRUScriptCache(private val maxSize: Int = 100) : CompiledJvmScriptsCache {
    
    private val cache = object : LinkedHashMap<String, CompiledScript>(16, 0.75f, true) {
        override fun removeEldestEntry(eldest: MutableMap.MutableEntry<String, CompiledScript>): Boolean {
            return size > maxSize
        }
    }
    
    private fun generateCacheKey(
        script: SourceCode,
        config: ScriptCompilationConfiguration
    ): String {
        return "${script.text.hashCode()}-${config.hashCode()}"
    }
    
    @Synchronized
    override fun get(
        script: SourceCode,
        scriptCompilationConfiguration: ScriptCompilationConfiguration
    ): CompiledScript? {
        val key = generateCacheKey(script, scriptCompilationConfiguration)
        return cache[key]
    }
    
    @Synchronized
    override fun store(
        compiledScript: CompiledScript,
        script: SourceCode,
        scriptCompilationConfiguration: ScriptCompilationConfiguration
    ) {
        val key = generateCacheKey(script, scriptCompilationConfiguration)
        cache[key] = compiledScript
    }
    
    @Synchronized
    fun clear() {
        cache.clear()
    }
    
    @Synchronized
    fun size(): Int = cache.size
    
    @Synchronized
    fun getStatistics(): CacheStatistics {
        return CacheStatistics(
            size = cache.size,
            maxSize = maxSize,
            hitRate = calculateHitRate()
        )
    }
}

data class CacheStatistics(
    val size: Int,
    val maxSize: Int,
    val hitRate: Double
)

Composite Cache Implementation

class CompositeScriptCache(
    private val primaryCache: CompiledJvmScriptsCache,
    private val secondaryCache: CompiledJvmScriptsCache
) : CompiledJvmScriptsCache {
    
    override fun get(
        script: SourceCode,
        scriptCompilationConfiguration: ScriptCompilationConfiguration
    ): CompiledScript? {
        // Try primary cache first
        primaryCache.get(script, scriptCompilationConfiguration)?.let { return it }
        
        // Try secondary cache
        val result = secondaryCache.get(script, scriptCompilationConfiguration)
        
        // If found in secondary, promote to primary
        result?.let { 
            primaryCache.store(it, script, scriptCompilationConfiguration)
        }
        
        return result
    }
    
    override fun store(
        compiledScript: CompiledScript,
        script: SourceCode,
        scriptCompilationConfiguration: ScriptCompilationConfiguration
    ) {
        // Store in both caches
        primaryCache.store(compiledScript, script, scriptCompilationConfiguration)
        secondaryCache.store(compiledScript, script, scriptCompilationConfiguration)
    }
}

// Usage: Fast in-memory cache with persistent backup
val compositeCache = CompositeScriptCache(
    primaryCache = LRUScriptCache(maxSize = 50),
    secondaryCache = FileBasedScriptCache(File("script-cache"))
)

Time-Based Expiring Cache

import java.time.Instant
import java.time.Duration
import java.util.concurrent.ConcurrentHashMap

class ExpiringScriptCache(
    private val ttl: Duration = Duration.ofHours(1)
) : CompiledJvmScriptsCache {
    
    private data class CacheEntry(
        val script: CompiledScript,
        val timestamp: Instant
    ) {
        fun isExpired(now: Instant): Boolean {
            return Duration.between(timestamp, now) > ttl
        }
    }
    
    private val cache = ConcurrentHashMap<String, CacheEntry>()
    
    private fun generateCacheKey(
        script: SourceCode,
        config: ScriptCompilationConfiguration
    ): String {
        return "${script.text.hashCode()}-${config.hashCode()}"
    }
    
    override fun get(
        script: SourceCode,
        scriptCompilationConfiguration: ScriptCompilationConfiguration
    ): CompiledScript? {
        val key = generateCacheKey(script, scriptCompilationConfiguration)
        val entry = cache[key]
        val now = Instant.now()
        
        return if (entry != null && !entry.isExpired(now)) {
            entry.script
        } else {
            // Remove expired entry
            cache.remove(key)
            null
        }
    }
    
    override fun store(
        compiledScript: CompiledScript,
        script: SourceCode,
        scriptCompilationConfiguration: ScriptCompilationConfiguration
    ) {
        val key = generateCacheKey(script, scriptCompilationConfiguration)
        val entry = CacheEntry(compiledScript, Instant.now())
        cache[key] = entry
    }
    
    fun cleanExpiredEntries() {
        val now = Instant.now()
        cache.entries.removeIf { (_, entry) -> entry.isExpired(now) }
    }
}

Configuration-Based Cache Factory

enum class CacheType {
    NONE, IN_MEMORY, FILE_BASED, LRU, EXPIRING, COMPOSITE
}

data class CacheConfiguration(
    val type: CacheType = CacheType.IN_MEMORY,
    val maxSize: Int = 100,
    val ttlHours: Long = 1,
    val cacheDirectory: File? = null
)

object ScriptCacheFactory {
    fun createCache(config: CacheConfiguration): CompiledJvmScriptsCache {
        return when (config.type) {
            CacheType.NONE -> CompiledJvmScriptsCache.NoCache
            
            CacheType.IN_MEMORY -> InMemoryScriptCache()
            
            CacheType.FILE_BASED -> {
                val dir = config.cacheDirectory ?: File("script-cache")
                FileBasedScriptCache(dir)
            }
            
            CacheType.LRU -> LRUScriptCache(config.maxSize)
            
            CacheType.EXPIRING -> ExpiringScriptCache(Duration.ofHours(config.ttlHours))
            
            CacheType.COMPOSITE -> CompositeScriptCache(
                primaryCache = LRUScriptCache(config.maxSize),
                secondaryCache = FileBasedScriptCache(
                    config.cacheDirectory ?: File("script-cache")
                )
            )
        }
    }
}

// Usage
val cacheConfig = CacheConfiguration(
    type = CacheType.COMPOSITE,
    maxSize = 200,
    cacheDirectory = File("/tmp/kotlin-script-cache")
)

val cache = ScriptCacheFactory.createCache(cacheConfig)

val compilationConfig = ScriptCompilationConfiguration {
    compilationCache = cache
}

Best Practices

Cache Key Generation

// Good: Include all relevant compilation factors
fun generateSecureCacheKey(
    script: SourceCode,
    config: ScriptCompilationConfiguration
): String {
    val content = buildString {
        append(script.text)
        append(config.jvm.jvmTarget.value ?: "")
        append(config.dependencies.joinToString { it.toString() })
        append(config.jvm.jdkHome.value?.absolutePath ?: "")
    }
    
    return MessageDigest.getInstance("SHA-256")
        .digest(content.toByteArray())
        .joinToString("") { "%02x".format(it) }
}

Cache Monitoring

class MonitoredScriptCache(
    private val delegate: CompiledJvmScriptsCache,
    private val metrics: CacheMetrics
) : CompiledJvmScriptsCache {
    
    override fun get(
        script: SourceCode,
        scriptCompilationConfiguration: ScriptCompilationConfiguration
    ): CompiledScript? {
        val result = delegate.get(script, scriptCompilationConfiguration)
        if (result != null) {
            metrics.recordHit()
        } else {
            metrics.recordMiss()
        }
        return result
    }
    
    override fun store(
        compiledScript: CompiledScript,
        script: SourceCode,
        scriptCompilationConfiguration: ScriptCompilationConfiguration
    ) {
        delegate.store(compiledScript, script, scriptCompilationConfiguration)
        metrics.recordStore()
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-org-jetbrains-kotlin--kotlin-scripting-jvm

docs

caching.md

compiled-scripts.md

configuration.md

dependencies.md

evaluators.md

index.md

utilities.md

tile.json