0
# Script Compilation
1
2
The script compilation system provides JVM script compilation capabilities using the Kotlin compiler infrastructure with comprehensive configuration support and proxy-based compilation.
3
4
## Capabilities
5
6
### JvmScriptCompiler
7
8
JVM script compiler implementation that handles script compilation with configurable host configuration and compiler proxy.
9
10
```kotlin { .api }
11
/**
12
* JVM script compiler for compiling Kotlin scripts with proxy-based compilation
13
* @param baseHostConfiguration Base host configuration for compilation
14
* @param compilerProxy Optional custom compiler proxy, defaults to ScriptJvmCompilerIsolated
15
*/
16
open class JvmScriptCompiler(
17
baseHostConfiguration: ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration,
18
compilerProxy: ScriptCompilerProxy? = null
19
) : ScriptCompiler {
20
21
/** Final host configuration with defaults applied */
22
val hostConfiguration: ScriptingHostConfiguration
23
24
/** Compiler proxy for performing actual compilation */
25
val compilerProxy: ScriptCompilerProxy
26
27
/**
28
* Compiles a script with the given configuration
29
* @param script Source code to compile
30
* @param scriptCompilationConfiguration Compilation configuration
31
* @returns Result with diagnostics containing compiled script or errors
32
*/
33
override suspend operator fun invoke(
34
script: SourceCode,
35
scriptCompilationConfiguration: ScriptCompilationConfiguration
36
): ResultWithDiagnostics<CompiledScript>
37
}
38
```
39
40
**Usage Examples:**
41
42
```kotlin
43
import kotlin.script.experimental.jvmhost.JvmScriptCompiler
44
import kotlin.script.experimental.api.*
45
import kotlin.script.experimental.jvm.*
46
47
// Basic compiler usage
48
val compiler = JvmScriptCompiler()
49
50
val script = """
51
val message = "Hello from compiled script!"
52
println(message)
53
message.length
54
""".trimIndent()
55
56
val compilationConfig = ScriptCompilationConfiguration {
57
dependencies(JvmDependency(kotlinStdlib))
58
compilerOptions.append("-opt-in=kotlin.RequiresOptIn")
59
}
60
61
val compilationResult = compiler(script.toScriptSource(), compilationConfig)
62
63
when (compilationResult) {
64
is ResultWithDiagnostics.Success -> {
65
val compiledScript = compilationResult.value
66
println("Compilation successful: ${compiledScript.sourceLocationId}")
67
68
// Get script class
69
val classResult = compiledScript.getClass()
70
when (classResult) {
71
is ResultWithDiagnostics.Success -> {
72
println("Script class: ${classResult.value.simpleName}")
73
}
74
is ResultWithDiagnostics.Failure -> {
75
println("Failed to get script class")
76
}
77
}
78
}
79
is ResultWithDiagnostics.Failure -> {
80
compilationResult.reports.forEach {
81
println("Compilation error: ${it.message}")
82
}
83
}
84
}
85
86
// Advanced compiler with custom configuration
87
val customHostConfiguration = ScriptingHostConfiguration {
88
jvm {
89
// Custom JVM configuration
90
compilationCache(FileBasedScriptCache(File("cache")))
91
}
92
}
93
94
val advancedCompiler = JvmScriptCompiler(
95
baseHostConfiguration = customHostConfiguration
96
)
97
98
val advancedCompilationConfig = ScriptCompilationConfiguration {
99
// Template-based configuration
100
baseClass(KotlinType(Any::class))
101
102
// Dependencies
103
dependencies(
104
JvmDependency(File("lib1.jar")),
105
JvmDependency(File("lib2.jar"))
106
)
107
108
// Import scripts
109
importScripts(FileBasedScriptSource(File("common.kts")))
110
111
// Compiler options
112
compilerOptions.append(
113
"-language-version", "2.0",
114
"-api-version", "2.0"
115
)
116
117
// IDE support
118
ide {
119
acceptedLocations(ScriptAcceptedLocation.Everywhere)
120
}
121
}
122
123
val advancedResult = advancedCompiler(script.toScriptSource(), advancedCompilationConfig)
124
```
125
126
### Compilation Configuration
127
128
The compilation system uses `ScriptCompilationConfiguration` to control various aspects of script compilation.
129
130
#### Key Configuration Options
131
132
```kotlin { .api }
133
// Core configuration builders (imported from kotlin.script.experimental.api)
134
fun ScriptCompilationConfiguration.Builder.dependencies(vararg dependencies: ScriptDependency): ScriptCompilationConfiguration.Builder
135
fun ScriptCompilationConfiguration.Builder.importScripts(vararg scripts: SourceCode): ScriptCompilationConfiguration.Builder
136
fun ScriptCompilationConfiguration.Builder.compilerOptions(vararg options: String): ScriptCompilationConfiguration.Builder
137
fun ScriptCompilationConfiguration.Builder.baseClass(baseClass: KotlinType): ScriptCompilationConfiguration.Builder
138
fun ScriptCompilationConfiguration.Builder.hostConfiguration(configuration: ScriptingHostConfiguration): ScriptCompilationConfiguration.Builder
139
```
140
141
**Configuration Examples:**
142
143
```kotlin
144
// Dependency configuration
145
val configWithDependencies = ScriptCompilationConfiguration {
146
dependencies(
147
JvmDependency(kotlinStdlib),
148
JvmDependency(File("my-library.jar")),
149
JvmDependency("org.apache.commons:commons-lang3:3.12.0") // Maven coordinates
150
)
151
}
152
153
// Import scripts configuration
154
val configWithImports = ScriptCompilationConfiguration {
155
importScripts(
156
FileBasedScriptSource(File("utilities.kts")),
157
StringScriptSource("val commonConstant = 42", "common.kts")
158
)
159
}
160
161
// Compiler options configuration
162
val configWithOptions = ScriptCompilationConfiguration {
163
compilerOptions.append(
164
"-opt-in=kotlin.RequiresOptIn",
165
"-Xmulti-platform",
166
"-language-version", "2.0"
167
)
168
}
169
170
// Template-based configuration
171
@KotlinScript(
172
fileExtension = "custom.kts",
173
compilationConfiguration = CustomScriptConfiguration::class
174
)
175
class CustomScriptTemplate
176
177
object CustomScriptConfiguration : ScriptCompilationConfiguration({
178
baseClass(KotlinType(CustomScriptTemplate::class))
179
dependencies(JvmDependency(kotlinStdlib))
180
compilerOptions.append("-opt-in=kotlin.ExperimentalStdlibApi")
181
})
182
183
val templateConfig = ScriptCompilationConfiguration {
184
baseClass(KotlinType(CustomScriptTemplate::class))
185
refineConfiguration {
186
// Dynamic configuration refinement
187
onAnnotations(DependsOn::class, Repository::class) { context ->
188
// Process annotations to add dependencies
189
context.collectedData?.get(DependsOn::class)?.let { dependsOnList ->
190
ScriptCompilationConfiguration(context.compilationConfiguration) {
191
dependencies.append(JvmDependency(dependsOnList.flatMap { it.artifacts }))
192
}.asSuccess()
193
} ?: context.compilationConfiguration.asSuccess()
194
}
195
}
196
}
197
```
198
199
### Compiler Proxy Integration
200
201
The JVM script compiler uses a proxy pattern to delegate actual compilation to pluggable compiler implementations.
202
203
```kotlin { .api }
204
// Compiler proxy interface (from kotlin.scripting.compiler.plugin)
205
interface ScriptCompilerProxy {
206
suspend fun compile(
207
script: SourceCode,
208
scriptCompilationConfiguration: ScriptCompilationConfiguration
209
): ResultWithDiagnostics<CompiledScript>
210
}
211
```
212
213
**Custom Compiler Proxy Example:**
214
215
```kotlin
216
import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptCompilerProxy
217
218
class CustomScriptCompilerProxy(
219
private val hostConfiguration: ScriptingHostConfiguration
220
) : ScriptCompilerProxy {
221
222
override suspend fun compile(
223
script: SourceCode,
224
scriptCompilationConfiguration: ScriptCompilationConfiguration
225
): ResultWithDiagnostics<CompiledScript> {
226
// Custom compilation logic
227
return try {
228
// Perform custom pre-processing
229
val processedScript = preprocessScript(script)
230
231
// Delegate to default compiler
232
val defaultProxy = ScriptJvmCompilerIsolated(hostConfiguration)
233
defaultProxy.compile(processedScript, scriptCompilationConfiguration)
234
} catch (e: Exception) {
235
ResultWithDiagnostics.Failure(
236
listOf(e.asErrorDiagnostics("Custom compilation failed"))
237
)
238
}
239
}
240
241
private fun preprocessScript(script: SourceCode): SourceCode {
242
// Custom preprocessing logic
243
return StringScriptSource(
244
script.text.replace("@CustomMacro", "// Expanded macro"),
245
script.name
246
)
247
}
248
}
249
250
// Use custom proxy
251
val customCompiler = JvmScriptCompiler(
252
compilerProxy = CustomScriptCompilerProxy(defaultJvmScriptingHostConfiguration)
253
)
254
```
255
256
## Compilation Results
257
258
The compilation process returns `CompiledScript` instances that encapsulate the compiled script artifacts.
259
260
### CompiledScript Interface
261
262
```kotlin { .api }
263
interface CompiledScript {
264
/** Compilation configuration used to compile this script */
265
val compilationConfiguration: ScriptCompilationConfiguration
266
267
/** Source location identifier for error reporting */
268
val sourceLocationId: String?
269
270
/** Other scripts that were compiled together with this script */
271
val otherScripts: List<CompiledScript>
272
273
/** Result field information if script has a result expression */
274
val resultField: Pair<String, KotlinType>?
275
276
/**
277
* Gets the compiled script class
278
* @param scriptEvaluationConfiguration Optional evaluation configuration
279
* @returns Result containing the script class or compilation errors
280
*/
281
suspend fun getClass(scriptEvaluationConfiguration: ScriptEvaluationConfiguration? = null): ResultWithDiagnostics<KClass<*>>
282
}
283
```
284
285
**Working with Compiled Scripts:**
286
287
```kotlin
288
val compilationResult = compiler(script.toScriptSource(), compilationConfig)
289
290
when (compilationResult) {
291
is ResultWithDiagnostics.Success -> {
292
val compiledScript = compilationResult.value
293
294
// Access compilation metadata
295
println("Source: ${compiledScript.sourceLocationId}")
296
println("Has result field: ${compiledScript.resultField != null}")
297
println("Other scripts: ${compiledScript.otherScripts.size}")
298
299
// Get script class for evaluation
300
val classResult = compiledScript.getClass()
301
when (classResult) {
302
is ResultWithDiagnostics.Success -> {
303
val scriptClass = classResult.value
304
305
// Create script instance
306
val constructor = scriptClass.constructors.first()
307
val scriptInstance = constructor.call()
308
309
// Access result field if available
310
compiledScript.resultField?.let { (fieldName, fieldType) ->
311
val field = scriptClass.java.getDeclaredField(fieldName)
312
field.isAccessible = true
313
val result = field.get(scriptInstance)
314
println("Script result: $result (${fieldType.typeName})")
315
}
316
}
317
is ResultWithDiagnostics.Failure -> {
318
println("Failed to get script class")
319
}
320
}
321
}
322
is ResultWithDiagnostics.Failure -> {
323
println("Compilation failed")
324
}
325
}
326
```
327
328
## Error Handling
329
330
Compilation errors are reported through the diagnostic system with detailed location information and error categories.
331
332
### Common Compilation Errors
333
334
- **Syntax Errors**: Invalid Kotlin syntax in script code
335
- **Type Errors**: Type mismatches, unresolved references
336
- **Dependency Errors**: Missing or invalid dependencies
337
- **Import Errors**: Failed to import or resolve imported scripts
338
- **Configuration Errors**: Invalid compilation configuration
339
340
**Detailed Error Handling:**
341
342
```kotlin
343
val result = compiler(script.toScriptSource(), compilationConfig)
344
345
when (result) {
346
is ResultWithDiagnostics.Failure -> {
347
result.reports.groupBy { it.severity }.forEach { (severity, reports) ->
348
println("$severity errors:")
349
reports.forEach { diagnostic ->
350
val location = diagnostic.location?.let { loc ->
351
" at line ${loc.start.line}, column ${loc.start.col}"
352
} ?: ""
353
354
println(" ${diagnostic.message}$location")
355
356
// Show exception details if available
357
diagnostic.exception?.let { ex ->
358
println(" Caused by: ${ex.javaClass.simpleName}: ${ex.message}")
359
ex.stackTrace.take(3).forEach { frame ->
360
println(" at $frame")
361
}
362
}
363
}
364
}
365
}
366
}
367
```