or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-scripting-host.mdindex.mdjsr223-integration.mdrepl-support.mdscript-caching.mdscript-compilation.mdscript-persistence.md

script-caching.mddocs/

0

# Script Caching

1

2

The script caching system provides persistent storage mechanisms for compiled scripts to improve performance through JAR-based caching and avoid recompilation of unchanged scripts.

3

4

## Capabilities

5

6

### CompiledScriptJarsCache

7

8

JAR-based cache implementation that stores compiled scripts as JAR files with dependency information and metadata.

9

10

```kotlin { .api }

11

/**

12

* Cache implementation that stores compiled scripts as JAR files

13

* @param scriptToFile Function mapping script and configuration to cache file location

14

*/

15

open class CompiledScriptJarsCache(

16

val scriptToFile: (SourceCode, ScriptCompilationConfiguration) -> File?

17

) : CompiledJvmScriptsCache {

18

19

/**

20

* Retrieves cached compiled script from JAR file

21

* @param script Source code to look up

22

* @param scriptCompilationConfiguration Compilation configuration for cache key

23

* @returns Cached compiled script or null if not found/invalid

24

*/

25

override fun get(script: SourceCode, scriptCompilationConfiguration: ScriptCompilationConfiguration): CompiledScript?

26

27

/**

28

* Stores compiled script to JAR file cache

29

* @param compiledScript Compiled script to cache

30

* @param script Original source code

31

* @param scriptCompilationConfiguration Compilation configuration used

32

*/

33

override fun store(

34

compiledScript: CompiledScript,

35

script: SourceCode,

36

scriptCompilationConfiguration: ScriptCompilationConfiguration

37

)

38

}

39

```

40

41

**Usage Examples:**

42

43

```kotlin

44

import kotlin.script.experimental.jvmhost.CompiledScriptJarsCache

45

import kotlin.script.experimental.api.*

46

import java.io.File

47

import java.security.MessageDigest

48

49

// Create cache with hash-based file mapping

50

val cache = CompiledScriptJarsCache { script, config ->

51

// Generate cache file path based on script content and configuration

52

val hash = generateScriptHash(script, config)

53

File("cache/scripts", "$hash.jar")

54

}

55

56

fun generateScriptHash(script: SourceCode, config: ScriptCompilationConfiguration): String {

57

val digest = MessageDigest.getInstance("SHA-256")

58

digest.update(script.text.toByteArray())

59

digest.update(config.toString().toByteArray())

60

return digest.digest().joinToString("") { "%02x".format(it) }

61

}

62

63

// Use cache with compilation

64

val compiler = JvmScriptCompiler()

65

val script = "println(\"Hello, cached world!\")".toScriptSource()

66

val config = ScriptCompilationConfiguration {

67

dependencies(JvmDependency(kotlinStdlib))

68

}

69

70

// Try to get from cache first

71

val cachedScript = cache.get(script, config)

72

val compiledScript = if (cachedScript != null) {

73

println("Using cached script")

74

cachedScript

75

} else {

76

println("Compiling and caching script")

77

val compilationResult = compiler(script, config)

78

when (compilationResult) {

79

is ResultWithDiagnostics.Success -> {

80

val compiled = compilationResult.value

81

cache.store(compiled, script, config)

82

compiled

83

}

84

is ResultWithDiagnostics.Failure -> {

85

compilationResult.reports.forEach { println("Error: ${it.message}") }

86

return

87

}

88

}

89

}

90

91

// Advanced cache with versioning

92

class VersionedScriptCache(private val baseDir: File) : CompiledJvmScriptsCache {

93

94

private val scriptToFile: (SourceCode, ScriptCompilationConfiguration) -> File? = { script, config ->

95

val scriptHash = generateScriptHash(script, config)

96

val versionDir = File(baseDir, "v1") // Version for cache format

97

versionDir.mkdirs()

98

File(versionDir, "$scriptHash.jar")

99

}

100

101

private val delegate = CompiledScriptJarsCache(scriptToFile)

102

103

override fun get(script: SourceCode, scriptCompilationConfiguration: ScriptCompilationConfiguration): CompiledScript? {

104

return try {

105

delegate.get(script, scriptCompilationConfiguration)

106

} catch (e: Exception) {

107

// Handle cache corruption or version mismatch

108

println("Cache miss due to error: ${e.message}")

109

null

110

}

111

}

112

113

override fun store(compiledScript: CompiledScript, script: SourceCode, scriptCompilationConfiguration: ScriptCompilationConfiguration) {

114

try {

115

delegate.store(compiledScript, script, scriptCompilationConfiguration)

116

} catch (e: Exception) {

117

println("Failed to cache script: ${e.message}")

118

}

119

}

120

}

121

122

val versionedCache = VersionedScriptCache(File("versioned-cache"))

123

```

124

125

### Cache Integration with Host

126

127

Integrating script caching with the scripting host for automatic cache management.

128

129

**Cache-Enabled Host Example:**

130

131

```kotlin

132

import kotlin.script.experimental.jvmhost.BasicJvmScriptingHost

133

import kotlin.script.experimental.jvm.*

134

135

// Create host configuration with caching

136

val hostConfigWithCache = ScriptingHostConfiguration {

137

jvm {

138

compilationCache(CompiledScriptJarsCache { script, config ->

139

val scriptName = script.name?.substringBeforeLast('.') ?: "unnamed"

140

val hash = generateScriptHash(script, config).take(8)

141

File("cache/compiled", "${scriptName}_$hash.jar")

142

})

143

}

144

}

145

146

// Create host with caching enabled

147

val cachingHost = BasicJvmScriptingHost(

148

baseHostConfiguration = hostConfigWithCache

149

)

150

151

// Scripts will be automatically cached and retrieved

152

@KotlinScript(fileExtension = "kts")

153

class CachedScript

154

155

val result1 = cachingHost.evalWithTemplate<CachedScript>("println(\"First execution\")".toScriptSource())

156

val result2 = cachingHost.evalWithTemplate<CachedScript>("println(\"Second execution - cached!\")".toScriptSource())

157

```

158

159

### JAR Storage Format

160

161

The cache stores compiled scripts as executable JAR files with embedded metadata and dependency information.

162

163

#### JAR Structure

164

165

```text

166

cached-script.jar

167

├── META-INF/

168

│ └── MANIFEST.MF # Manifest with Main-Class and Class-Path

169

├── script_metadata.json # Script compilation metadata

170

└── compiled/ # Compiled class files

171

├── Script.class # Main script class

172

└── Script$*.class # Inner classes

173

```

174

175

#### Manifest Format

176

177

```text

178

Manifest-Version: 1.0

179

Created-By: JetBrains Kotlin

180

Main-Class: Script

181

Class-Path: /path/to/kotlin-stdlib.jar /path/to/dependency.jar

182

```

183

184

### Loading Scripts from Cache

185

186

The cache automatically handles loading scripts from JAR files with dependency resolution and validation.

187

188

```kotlin { .api }

189

/**

190

* Loads compiled script from JAR file

191

* @param checkMissingDependencies Whether to validate all dependencies exist

192

* @returns Loaded compiled script or null if invalid

193

*/

194

fun File.loadScriptFromJar(checkMissingDependencies: Boolean = true): CompiledScript?

195

```

196

197

**Manual JAR Loading Example:**

198

199

```kotlin

200

import kotlin.script.experimental.jvmhost.loadScriptFromJar

201

import java.io.File

202

203

// Load script from cached JAR

204

val jarFile = File("cache/my-script.jar")

205

val loadedScript = jarFile.loadScriptFromJar(checkMissingDependencies = true)

206

207

when (loadedScript) {

208

null -> {

209

println("Failed to load script from JAR - possibly corrupted or missing dependencies")

210

}

211

else -> {

212

println("Successfully loaded script from cache")

213

214

// Get script class

215

val classResult = loadedScript.getClass()

216

when (classResult) {

217

is ResultWithDiagnostics.Success -> {

218

val scriptClass = classResult.value

219

println("Loaded script class: ${scriptClass.simpleName}")

220

221

// Execute if needed

222

val evaluator = BasicJvmScriptEvaluator()

223

val evalResult = evaluator(loadedScript, ScriptEvaluationConfiguration.Default)

224

// Handle evaluation result...

225

}

226

is ResultWithDiagnostics.Failure -> {

227

println("Failed to get script class from cached JAR")

228

}

229

}

230

}

231

}

232

233

// Load with dependency checking disabled (faster but less safe)

234

val quickLoadedScript = jarFile.loadScriptFromJar(checkMissingDependencies = false)

235

```

236

237

## Cache Management

238

239

### Cache Invalidation

240

241

Cached scripts are automatically invalidated when:

242

243

- Source script content changes

244

- Compilation configuration changes

245

- Dependencies are updated or missing

246

- JAR file is corrupted or unreadable

247

248

**Custom Cache Invalidation:**

249

250

```kotlin

251

class SmartScriptCache(private val baseDir: File) {

252

private val cache = CompiledScriptJarsCache { script, config ->

253

File(baseDir, "${script.name}-${config.hashCode()}.jar")

254

}

255

256

fun invalidateScript(scriptName: String) {

257

baseDir.listFiles { _, name ->

258

name.startsWith("$scriptName-") && name.endsWith(".jar")

259

}?.forEach { jarFile ->

260

if (jarFile.delete()) {

261

println("Invalidated cached script: ${jarFile.name}")

262

}

263

}

264

}

265

266

fun clearCache() {

267

baseDir.listFiles { _, name -> name.endsWith(".jar") }

268

?.forEach { it.delete() }

269

println("Cache cleared")

270

}

271

272

fun getCacheStats(): CacheStats {

273

val jarFiles = baseDir.listFiles { _, name -> name.endsWith(".jar") } ?: emptyArray()

274

return CacheStats(

275

totalFiles = jarFiles.size,

276

totalSize = jarFiles.sumOf { it.length() },

277

oldestFile = jarFiles.minByOrNull { it.lastModified() }?.lastModified(),

278

newestFile = jarFiles.maxByOrNull { it.lastModified() }?.lastModified()

279

)

280

}

281

}

282

283

data class CacheStats(

284

val totalFiles: Int,

285

val totalSize: Long,

286

val oldestFile: Long?,

287

val newestFile: Long?

288

)

289

```

290

291

### Performance Considerations

292

293

- **Cache Hit Rate**: Monitor cache effectiveness through hit/miss ratios

294

- **Storage Space**: JAR files include full dependency information, can be large

295

- **Load Time**: Loading from cache is significantly faster than compilation but still involves class loading

296

- **Dependency Validation**: Checking dependencies on each load impacts performance but ensures correctness

297

298

**Performance Monitoring Example:**

299

300

```kotlin

301

class MonitoredScriptCache(private val delegate: CompiledJvmScriptsCache) : CompiledJvmScriptsCache {

302

private var hits = 0

303

private var misses = 0

304

private var stores = 0

305

306

override fun get(script: SourceCode, scriptCompilationConfiguration: ScriptCompilationConfiguration): CompiledScript? {

307

val result = delegate.get(script, scriptCompilationConfiguration)

308

if (result != null) {

309

hits++

310

println("Cache hit (${hits}/${hits + misses} = ${(hits * 100) / (hits + misses)}%)")

311

} else {

312

misses++

313

println("Cache miss")

314

}

315

return result

316

}

317

318

override fun store(compiledScript: CompiledScript, script: SourceCode, scriptCompilationConfiguration: ScriptCompilationConfiguration) {

319

delegate.store(compiledScript, script, scriptCompilationConfiguration)

320

stores++

321

println("Cached script (total stored: $stores)")

322

}

323

324

fun getStats() = "Cache stats: $hits hits, $misses misses, $stores stored"

325

}

326

```