0
# Plugin Development
1
2
The Kotlin compiler provides a comprehensive plugin architecture enabling custom compilation phases, code generation, IDE integration, and language extensions. The modern K2-compatible plugin system uses CompilerPluginRegistrar for clean separation of concerns and improved performance.
3
4
## Capabilities
5
6
### Modern Plugin Architecture (K2-Compatible)
7
8
The current plugin system designed for the K2 compiler frontend with improved performance and maintainability.
9
10
```kotlin { .api }
11
/**
12
* Modern plugin registration interface for K2 compiler
13
* Provides clean separation between compiler phases and IDE integration
14
*/
15
interface CompilerPluginRegistrar {
16
/**
17
* Register compiler plugin components
18
* Called during compiler initialization to set up plugin infrastructure
19
*
20
* @param project MockProject instance for component registration
21
* @param configuration Compiler configuration with plugin settings
22
*/
23
fun registerProjectComponents(
24
project: MockProject,
25
configuration: CompilerConfiguration
26
)
27
28
companion object {
29
/**
30
* Extension point for plugin registrars
31
* Used by the compiler to discover and load plugins
32
*/
33
val EXTENSION_POINT_NAME: ExtensionPointName<CompilerPluginRegistrar>
34
}
35
}
36
```
37
38
### Command Line Processing
39
40
Plugin command-line option processing for configuration and parameter passing.
41
42
```kotlin { .api }
43
/**
44
* Processes command-line options for compiler plugins
45
* Handles option parsing and configuration setup
46
*/
47
interface CommandLineProcessor {
48
/** Unique identifier for the plugin */
49
val pluginId: String
50
51
/** Available plugin options */
52
val pluginOptions: Collection<PluginOption>
53
54
/**
55
* Process a single command-line option
56
*
57
* @param option The CLI option being processed
58
* @param value The option value from command line
59
* @param configuration Compiler configuration to update
60
* @return true if option was handled, false otherwise
61
*/
62
fun processOption(
63
option: AbstractCliOption,
64
value: String,
65
configuration: CompilerConfiguration
66
): Boolean
67
}
68
69
/**
70
* Definition of a plugin command-line option
71
*/
72
class PluginOption(
73
/** Option name (without -- prefix) */
74
val optionName: String,
75
76
/** Value description for help text */
77
val valueDescription: String,
78
79
/** Help description for the option */
80
val description: String,
81
82
/** Whether option requires a value */
83
val required: Boolean = true,
84
85
/** Allow multiple values for this option */
86
val allowMultipleOccurrences: Boolean = false
87
)
88
```
89
90
### Legacy Plugin Architecture (K1)
91
92
Legacy plugin system maintained for backward compatibility.
93
94
```kotlin { .api }
95
/**
96
* Legacy plugin registration interface (deprecated)
97
* Maintained for K1 compiler compatibility
98
*/
99
@Deprecated("Use CompilerPluginRegistrar for K2 compatibility")
100
interface ComponentRegistrar {
101
/**
102
* Register plugin components with the compiler
103
*
104
* @param project Project instance
105
* @param configuration Compiler configuration
106
*/
107
fun registerProjectComponents(
108
project: MockProject,
109
configuration: CompilerConfiguration
110
)
111
112
companion object {
113
val EXTENSION_POINT_NAME: ExtensionPointName<ComponentRegistrar>
114
}
115
}
116
```
117
118
### Plugin Extension Points
119
120
Core extension points for implementing plugin functionality.
121
122
```kotlin { .api }
123
/**
124
* Extension for IR generation and transformation
125
* Allows plugins to generate and modify intermediate representation
126
*/
127
interface IrGenerationExtension {
128
/**
129
* Generate additional IR declarations
130
* Called during IR generation phase
131
*/
132
fun generate(
133
moduleFragment: IrModuleFragment,
134
pluginContext: IrPluginContext
135
)
136
}
137
138
/**
139
* Extension for frontend analysis and resolution
140
* Enables custom declarations and synthetic elements
141
*/
142
interface AnalysisExtension {
143
/**
144
* Perform additional analysis
145
* Called during frontend analysis phase
146
*/
147
fun doAnalysis(
148
project: Project,
149
module: ModuleDescriptor,
150
bindingTrace: BindingTrace,
151
files: Collection<KtFile>
152
): AnalysisResult?
153
}
154
155
/**
156
* Extension for code generation
157
* Allows custom bytecode generation and optimization
158
*/
159
interface CodegenExtension {
160
/**
161
* Generate additional code
162
* Called during code generation phase
163
*/
164
fun generateCode(
165
codegen: ExpressionCodegen,
166
expression: KtExpression,
167
type: Type
168
): StackValue?
169
}
170
```
171
172
### Synthetic Declaration Provider
173
174
Plugin support for synthetic declarations and members.
175
176
```kotlin { .api }
177
/**
178
* Provides synthetic declarations not present in source code
179
* Useful for frameworks that generate members at compile time
180
*/
181
interface SyntheticResolveExtension {
182
/**
183
* Generate synthetic companion object
184
*/
185
fun generateSyntheticClasses(
186
moduleDescriptor: ModuleDescriptor,
187
name: Name,
188
ctx: LazyClassContext,
189
declarationProvider: PackageMemberDeclarationProvider,
190
result: MutableSet<ClassDescriptor>
191
)
192
193
/**
194
* Generate synthetic members for existing classes
195
*/
196
fun generateSyntheticMethods(
197
thisDescriptor: ClassDescriptor,
198
name: Name,
199
bindingContext: BindingContext,
200
fromSupertypes: List<SimpleFunctionDescriptor>,
201
result: MutableCollection<SimpleFunctionDescriptor>
202
)
203
204
/**
205
* Generate synthetic properties
206
*/
207
fun generateSyntheticProperties(
208
thisDescriptor: ClassDescriptor,
209
name: Name,
210
bindingContext: BindingContext,
211
fromSupertypes: ArrayList<PropertyDescriptor>,
212
result: MutableSet<PropertyDescriptor>
213
)
214
}
215
```
216
217
### Plugin Context and Services
218
219
Context and service access for plugin implementations.
220
221
```kotlin { .api }
222
/**
223
* Plugin context providing access to compiler services
224
* Available during plugin execution phases
225
*/
226
interface IrPluginContext {
227
/** Module descriptor being compiled */
228
val moduleDescriptor: ModuleDescriptor
229
230
/** Symbol table for IR operations */
231
val symbolTable: SymbolTable
232
233
/** Type translator for IR types */
234
val typeTranslator: TypeTranslator
235
236
/** IR factory for creating IR elements */
237
val irFactory: IrFactory
238
239
/** Access to built-in types and symbols */
240
val irBuiltIns: IrBuiltIns
241
}
242
243
/**
244
* Configuration keys specific to plugins
245
*/
246
object PluginConfigurationKeys {
247
/** Plugin options from command line */
248
val PLUGIN_OPTIONS: CompilerConfigurationKey<List<PluginOption>>
249
250
/** Plugin classpath */
251
val PLUGIN_CLASSPATH: CompilerConfigurationKey<List<String>>
252
}
253
```
254
255
## Usage Examples
256
257
### Creating a Basic Plugin
258
259
```kotlin
260
import org.jetbrains.kotlin.compiler.plugin.*
261
import org.jetbrains.kotlin.config.CompilerConfiguration
262
import com.intellij.mock.MockProject
263
264
// 1. Define plugin options
265
class MyPluginCommandLineProcessor : CommandLineProcessor {
266
override val pluginId = "my-plugin"
267
268
override val pluginOptions = listOf(
269
PluginOption(
270
optionName = "enabled",
271
valueDescription = "true|false",
272
description = "Enable my plugin functionality"
273
),
274
PluginOption(
275
optionName = "output-dir",
276
valueDescription = "path",
277
description = "Output directory for generated files"
278
)
279
)
280
281
override fun processOption(
282
option: AbstractCliOption,
283
value: String,
284
configuration: CompilerConfiguration
285
): Boolean {
286
return when (option.optionName) {
287
"enabled" -> {
288
configuration.put(MyPluginConfigurationKeys.ENABLED, value.toBoolean())
289
true
290
}
291
"output-dir" -> {
292
configuration.put(MyPluginConfigurationKeys.OUTPUT_DIR, value)
293
true
294
}
295
else -> false
296
}
297
}
298
}
299
300
// 2. Define configuration keys
301
object MyPluginConfigurationKeys {
302
val ENABLED = CompilerConfigurationKey<Boolean>("my.plugin.enabled")
303
val OUTPUT_DIR = CompilerConfigurationKey<String>("my.plugin.output.dir")
304
}
305
306
// 3. Implement plugin registrar
307
class MyCompilerPluginRegistrar : CompilerPluginRegistrar {
308
override fun registerProjectComponents(
309
project: MockProject,
310
configuration: CompilerConfiguration
311
) {
312
val enabled = configuration.get(MyPluginConfigurationKeys.ENABLED) ?: false
313
if (!enabled) return
314
315
// Register IR generation extension
316
IrGenerationExtension.registerExtension(
317
project,
318
MyIrGenerationExtension(configuration)
319
)
320
321
// Register synthetic resolve extension
322
SyntheticResolveExtension.registerExtension(
323
project,
324
MySyntheticResolveExtension()
325
)
326
}
327
}
328
```
329
330
### IR Generation Extension
331
332
```kotlin
333
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
334
import org.jetbrains.kotlin.ir.declarations.*
335
import org.jetbrains.kotlin.ir.util.*
336
337
class MyIrGenerationExtension(
338
private val configuration: CompilerConfiguration
339
) : IrGenerationExtension {
340
341
override fun generate(
342
moduleFragment: IrModuleFragment,
343
pluginContext: IrPluginContext
344
) {
345
// Generate additional IR declarations
346
moduleFragment.files.forEach { file ->
347
file.declarations.filterIsInstance<IrClass>().forEach { irClass ->
348
if (shouldProcessClass(irClass)) {
349
generateHelperMethod(irClass, pluginContext)
350
}
351
}
352
}
353
}
354
355
private fun shouldProcessClass(irClass: IrClass): Boolean {
356
// Check if class has specific annotation
357
return irClass.hasAnnotation(FqName("com.example.MyAnnotation"))
358
}
359
360
private fun generateHelperMethod(
361
irClass: IrClass,
362
pluginContext: IrPluginContext
363
) {
364
val function = pluginContext.irFactory.buildFun {
365
name = Name.identifier("generatedHelper")
366
returnType = pluginContext.irBuiltIns.unitType
367
visibility = Visibilities.PUBLIC
368
}
369
370
// Build function body
371
function.body = DeclarationIrBuilder(pluginContext, function.symbol).irBlockBody {
372
// Generate IR statements
373
+irReturn(irUnit())
374
}
375
376
irClass.declarations.add(function)
377
}
378
}
379
```
380
381
### Synthetic Member Generation
382
383
```kotlin
384
import org.jetbrains.kotlin.descriptors.*
385
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
386
387
class MySyntheticResolveExtension : SyntheticResolveExtension {
388
389
override fun generateSyntheticMethods(
390
thisDescriptor: ClassDescriptor,
391
name: Name,
392
bindingContext: BindingContext,
393
fromSupertypes: List<SimpleFunctionDescriptor>,
394
result: MutableCollection<SimpleFunctionDescriptor>
395
) {
396
// Generate synthetic getter/setter methods
397
if (name.asString().startsWith("get") || name.asString().startsWith("set")) {
398
val propertyName = name.asString().removePrefix("get").removePrefix("set")
399
400
if (hasPropertyAnnotation(thisDescriptor, propertyName)) {
401
val syntheticFunction = createSyntheticFunction(
402
thisDescriptor,
403
name,
404
propertyName
405
)
406
result.add(syntheticFunction)
407
}
408
}
409
}
410
411
override fun generateSyntheticProperties(
412
thisDescriptor: ClassDescriptor,
413
name: Name,
414
bindingContext: BindingContext,
415
fromSupertypes: ArrayList<PropertyDescriptor>,
416
result: MutableSet<PropertyDescriptor>
417
) {
418
// Generate synthetic properties based on annotations
419
if (hasGeneratePropertyAnnotation(thisDescriptor, name)) {
420
val syntheticProperty = createSyntheticProperty(thisDescriptor, name)
421
result.add(syntheticProperty)
422
}
423
}
424
425
private fun hasPropertyAnnotation(
426
classDescriptor: ClassDescriptor,
427
propertyName: String
428
): Boolean {
429
return classDescriptor.annotations.hasAnnotation(
430
FqName("com.example.GenerateProperty")
431
)
432
}
433
434
private fun createSyntheticFunction(
435
owner: ClassDescriptor,
436
name: Name,
437
propertyName: String
438
): SimpleFunctionDescriptor {
439
return SimpleFunctionDescriptorImpl.create(
440
owner,
441
Annotations.EMPTY,
442
name,
443
CallableMemberDescriptor.Kind.SYNTHESIZED,
444
owner.source
445
).apply {
446
// Configure function parameters and return type
447
initialize(
448
null, // extension receiver
449
owner.thisAsReceiverParameter,
450
emptyList(), // type parameters
451
emptyList(), // value parameters
452
owner.builtIns.stringType, // return type
453
Modality.FINAL,
454
Visibilities.PUBLIC
455
)
456
}
457
}
458
}
459
```
460
461
### Plugin Registration and Discovery
462
463
```kotlin
464
// META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar
465
com.example.MyCompilerPluginRegistrar
466
467
// META-INF/services/org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor
468
com.example.MyPluginCommandLineProcessor
469
```
470
471
### Plugin Usage in Build Scripts
472
473
**Gradle (build.gradle.kts):**
474
```kotlin
475
plugins {
476
kotlin("jvm")
477
id("my-kotlin-plugin") version "1.0.0"
478
}
479
480
kotlinCompilerPluginOptions {
481
option("my-plugin", "enabled", "true")
482
option("my-plugin", "output-dir", "build/generated")
483
}
484
```
485
486
**Command Line:**
487
```bash
488
kotlinc \
489
-Xplugin=my-plugin.jar \
490
-P plugin:my-plugin:enabled=true \
491
-P plugin:my-plugin:output-dir=build/generated \
492
src/main/kotlin/Main.kt
493
```
494
495
### Advanced Plugin Patterns
496
497
```kotlin
498
// Multi-phase plugin with dependency ordering
499
class AdvancedPluginRegistrar : CompilerPluginRegistrar {
500
501
override fun registerProjectComponents(
502
project: MockProject,
503
configuration: CompilerConfiguration
504
) {
505
// Register multiple extensions with specific ordering
506
val analysisExtension = MyAnalysisExtension(configuration)
507
val irExtension = MyIrGenerationExtension(configuration)
508
val codegenExtension = MyCodegenExtension(configuration)
509
510
// Analysis phase
511
AnalysisExtension.registerExtension(project, analysisExtension)
512
513
// IR generation phase
514
IrGenerationExtension.registerExtension(project, irExtension)
515
516
// Code generation phase
517
CodegenExtension.registerExtension(project, codegenExtension)
518
519
// Cross-phase communication via configuration
520
configuration.put(ANALYSIS_RESULTS, analysisExtension.results)
521
}
522
}
523
524
// Plugin with custom diagnostic reporting
525
class DiagnosticPlugin : IrGenerationExtension {
526
527
override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
528
moduleFragment.files.forEach { file ->
529
validateFile(file, pluginContext)
530
}
531
}
532
533
private fun validateFile(file: IrFile, context: IrPluginContext) {
534
file.declarations.forEach { declaration ->
535
if (hasIssue(declaration)) {
536
// Report custom diagnostic
537
context.messageCollector.report(
538
CompilerMessageSeverity.WARNING,
539
"Custom plugin warning: ${declaration.name}",
540
CompilerMessageSourceLocation.create(
541
file.path,
542
declaration.startOffset,
543
declaration.endOffset,
544
null
545
)
546
)
547
}
548
}
549
}
550
}
551
```
552
553
## Plugin Distribution
554
555
### JAR Packaging
556
557
Package plugin with service registration files:
558
559
```
560
my-plugin.jar
561
├── com/example/MyPluginRegistrar.class
562
├── com/example/MyCommandLineProcessor.class
563
├── META-INF/
564
│ └── services/
565
│ ├── org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar
566
│ └── org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor
567
└── plugin.xml (optional, for IDE integration)
568
```
569
570
### Error Handling
571
572
```kotlin { .api }
573
class PluginException(message: String, cause: Throwable? = null) : Exception(message, cause)
574
575
class InvalidPluginConfigurationException(message: String) : PluginException(message)
576
```
577
578
Common plugin error scenarios:
579
- **PLUGIN_LOADING_ERROR**: Plugin JAR not found or invalid service registration
580
- **CONFIGURATION_ERROR**: Invalid plugin options or missing required parameters
581
- **COMPILATION_PHASE_ERROR**: Plugin errors during specific compilation phases
582
- **EXTENSION_REGISTRATION_ERROR**: Extension point registration failures