Self-contained embeddable Kotlin compiler with shaded dependencies for integration into applications without classpath conflicts
—
Extension framework for compiler plugins, custom compilation phases, and code generation hooks. The plugin system allows extending the Kotlin compiler with custom functionality for code analysis, transformation, and generation.
Modern plugin registration interface for K2 compiler supporting extension-based architecture.
/**
* Modern plugin registration interface for K2 compiler
* Replaces ComponentRegistrar for new plugin development
*/
interface CompilerPluginRegistrar {
/** Extension storage for registering compiler extensions */
val supportsK2: Boolean get() = false
/** Register extensions in the compiler's extension storage */
fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration): Unit
companion object {
/** Create registrar instance from configuration */
fun create(configuration: CompilerConfiguration): CompilerPluginRegistrar?
}
}Legacy plugin registration interface for K1 compiler, being phased out in favor of CompilerPluginRegistrar.
/**
* Legacy plugin registration interface for K1 compiler
* Being replaced by CompilerPluginRegistrar for K2
*/
@Deprecated("Use CompilerPluginRegistrar for K2 compatibility")
interface ComponentRegistrar {
/** Register project components with the compiler */
fun registerProjectComponents(
project: MockProject,
configuration: CompilerConfiguration
): Unit
/** Whether this registrar supports K2 compiler */
val supportsK2: Boolean get() = false
}Interface for processing plugin-specific command-line arguments.
/**
* Interface for processing plugin-specific command-line arguments
* Allows plugins to define and parse their own CLI options
*/
abstract class CommandLineProcessor {
/** Unique plugin identifier */
abstract val pluginId: String
/** Plugin options that can be specified on command line */
abstract val pluginOptions: Collection<CliOption>
/** Process plugin options and update configuration */
abstract fun processOption(
option: AbstractCliOption,
value: String,
configuration: CompilerConfiguration
): Unit
companion object {
/** Create processor instance */
fun createInstance(): CommandLineProcessor?
}
}
/**
* Definition of a command-line option for plugins
*/
data class CliOption(
/** Option name (without dashes) */
val optionName: String,
/** Value description for help text */
val valueDescription: String,
/** Help description */
val description: String,
/** Whether option is required */
val required: Boolean = false,
/** Whether option allows multiple values */
val allowMultipleOccurrences: Boolean = false
)Plugin Development Examples:
// Modern K2 plugin using CompilerPluginRegistrar
class MyCompilerPlugin : CompilerPluginRegistrar {
override val supportsK2: Boolean = true
override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) {
// Register FIR extensions for K2
FirExtensionRegistrarAdapter.registerExtension(MyFirExtensionRegistrar())
// Register backend extensions
IrGenerationExtension.registerExtension(MyIrGenerationExtension())
// Register additional extensions
registerExtension(MyAnalysisHandlerExtension())
}
}
// Command line processor for plugin options
class MyPluginCommandLineProcessor : CommandLineProcessor() {
override val pluginId: String = "my-plugin"
override val pluginOptions: Collection<CliOption> = listOf(
CliOption(
optionName = "enable-feature",
valueDescription = "true|false",
description = "Enable the special feature"
),
CliOption(
optionName = "output-dir",
valueDescription = "path",
description = "Output directory for generated files",
required = true
)
)
override fun processOption(
option: AbstractCliOption,
value: String,
configuration: CompilerConfiguration
) {
when (option.optionName) {
"enable-feature" -> {
configuration.put(MyPluginConfigurationKeys.ENABLE_FEATURE, value.toBoolean())
}
"output-dir" -> {
configuration.put(MyPluginConfigurationKeys.OUTPUT_DIR, File(value))
}
}
}
}
// Plugin configuration keys
object MyPluginConfigurationKeys {
val ENABLE_FEATURE = CompilerConfigurationKey.create<Boolean>("enable feature")
val OUTPUT_DIR = CompilerConfigurationKey.create<File>("output directory")
}Core extension interfaces for hooking into different compilation phases.
/**
* Extension for analysis phase - called during semantic analysis
*/
interface AnalysisHandlerExtension {
/** Perform analysis after resolution is complete */
fun analysisCompleted(
project: Project,
module: ModuleDescriptor,
bindingTrace: BindingTrace,
files: Collection<KtFile>
): AnalysisResult?
/** Check if extension should be applied to given files */
fun doAnalysis(
project: Project,
module: ModuleDescriptor,
projectContext: ProjectContext,
files: Collection<KtFile>,
bindingTrace: BindingTrace,
componentProvider: ComponentProvider
): AnalysisResult?
}
/**
* Extension for IR generation phase - transform intermediate representation
*/
interface IrGenerationExtension {
/** Generate or transform IR after initial generation */
fun generate(
moduleFragment: IrModuleFragment,
pluginContext: IrPluginContext
): Unit
companion object {
/** Register extension instance */
fun registerExtension(extension: IrGenerationExtension): Unit
}
}
/**
* Extension for class generation phase - modify generated JVM bytecode
*/
interface ClassGeneratorExtension {
/** Called for each generated class */
fun generateClass(
codegen: ImplementationBodyCodegen,
classDescriptor: ClassDescriptor
): Unit
/** Called when generating class file factory */
fun createClassFileFactory(
project: Project,
bindingContext: BindingContext,
classFileFactory: ClassFileFactory
): ClassFileFactory
}
/**
* Extension for synthetic symbol resolution
*/
interface SyntheticResolveExtension {
/** Generate synthetic members for classes */
fun generateSyntheticMethods(
thisDescriptor: ClassDescriptor,
result: MutableList<FunctionDescriptor>
): Unit
/** Generate synthetic properties for classes */
fun generateSyntheticProperties(
thisDescriptor: ClassDescriptor,
result: MutableList<PropertyDescriptor>
): Unit
}Extensions for the new K2 compiler frontend (FIR - Frontend Intermediate Representation).
/**
* Registrar for FIR extensions in K2 compiler
*/
interface FirExtensionRegistrar {
/** Register FIR extensions */
fun ExtensionRegistrarContext.configurePlugin(): Unit
}
/**
* Context for registering FIR extensions
*/
interface ExtensionRegistrarContext {
/** Register declaration generation extension */
fun <T : FirDeclaration> registerDeclarationGenerators(
vararg generators: FirDeclarationGenerationExtension<T>
): Unit
/** Register additional checkers */
fun registerAdditionalCheckers(checkers: AdditionalCheckers): Unit
/** Register status transformer */
fun registerStatusTransformer(transformer: FirStatusTransformer): Unit
}
/**
* Extension for generating additional FIR declarations
*/
interface FirDeclarationGenerationExtension<T : FirDeclaration> {
/** Generate additional declarations */
fun generateDeclarations(
callableId: CallableId,
context: DeclarationGenerationContext
): List<T>
/** Check if extension applies to symbol */
fun matches(callableId: CallableId): Boolean
}Extensions for Kotlin script compilation and evaluation.
/**
* Extension for script evaluation
*/
interface ScriptEvaluationExtension {
/** Evaluate compiled script */
fun eval(
script: KtScript,
scriptCompilationConfiguration: ScriptCompilationConfiguration,
evaluationConfiguration: ScriptEvaluationConfiguration
): ResultWithDiagnostics<EvaluationResult>
/** Check if extension can evaluate the script */
fun canEvaluate(script: KtScript): Boolean
}
/**
* Extension for shell interaction (REPL)
*/
interface ShellExtension {
/** Execute shell command */
fun execute(
command: String,
context: ShellExecutionContext
): ShellExecutionResult
/** Get completion suggestions */
fun getCompletions(
code: String,
cursor: Int
): List<CompletionSuggestion>
}Advanced Plugin Examples:
// Code generation plugin
class CodeGenerationPlugin : CompilerPluginRegistrar {
override val supportsK2: Boolean = true
override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) {
val outputDir = configuration.get(MyPluginConfigurationKeys.OUTPUT_DIR)
IrGenerationExtension.registerExtension(object : IrGenerationExtension {
override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
// Transform IR to generate additional code
moduleFragment.transform(MyIrTransformer(pluginContext, outputDir), null)
}
})
ClassGeneratorExtension.registerExtension(object : ClassGeneratorExtension {
override fun generateClass(
codegen: ImplementationBodyCodegen,
classDescriptor: ClassDescriptor
) {
// Generate additional methods or modify bytecode
if (classDescriptor.hasAnnotation("GenerateToString")) {
generateToStringMethod(codegen, classDescriptor)
}
}
})
}
}
// Analysis plugin for code inspection
class AnalysisPlugin : CompilerPluginRegistrar {
override val supportsK2: Boolean = true
override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) {
AnalysisHandlerExtension.registerExtension(object : AnalysisHandlerExtension {
override fun analysisCompleted(
project: Project,
module: ModuleDescriptor,
bindingTrace: BindingTrace,
files: Collection<KtFile>
): AnalysisResult? {
// Perform custom analysis
files.forEach { file ->
analyzeFile(file, bindingTrace)
}
return null
}
})
}
private fun analyzeFile(file: KtFile, bindingTrace: BindingTrace) {
file.accept(object : KtVisitorVoid() {
override fun visitClass(klass: KtClass) {
super.visitClass(klass)
// Check for specific patterns
if (hasDeprecatedPattern(klass)) {
bindingTrace.report(Errors.DEPRECATED_USAGE.on(klass))
}
}
})
}
}
// FIR-based plugin for K2
class FirPlugin : FirExtensionRegistrar {
override fun ExtensionRegistrarContext.configurePlugin() {
registerDeclarationGenerators(MyDeclarationGenerator())
registerAdditionalCheckers(MyAdditionalCheckers())
registerStatusTransformer(MyStatusTransformer())
}
}Utilities for plugin discovery and registration.
/**
* Plugin loading utilities
*/
object PluginCliParser {
/** Parse plugin options from command line arguments */
fun parsePluginOptions(
pluginClasspaths: List<String>,
pluginOptions: List<String>,
configuration: CompilerConfiguration,
messageCollector: MessageCollector
): ExitCode
/** Load plugin from classpath */
fun loadPluginFromClasspath(
classpath: String,
configuration: CompilerConfiguration
): CompilerPluginRegistrar?
}
/**
* Plugin registry for managing loaded plugins
*/
interface CompilerPluginRegistry {
/** Register plugin instance */
fun registerPlugin(plugin: CompilerPluginRegistrar): Unit
/** Get all registered plugins */
fun getAllPlugins(): List<CompilerPluginRegistrar>
/** Find plugin by ID */
fun findPlugin(pluginId: String): CompilerPluginRegistrar?
}// Plugin descriptor for service loading
// META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar
class MyPlugin : CompilerPluginRegistrar {
override val supportsK2: Boolean = true
override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) {
// Plugin implementation
}
}
// Gradle plugin for easy distribution
class MyKotlinCompilerPlugin : KotlinCompilerPluginSupportPlugin {
override fun apply(target: Project) {
target.extensions.create("myPlugin", MyPluginExtension::class.java)
}
override fun getCompilerPluginId(): String = "my-plugin"
override fun getPluginArtifact(): SubpluginArtifact {
return SubpluginArtifact(
groupId = "com.example",
artifactId = "my-kotlin-plugin",
version = "1.0.0"
)
}
override fun applyToCompilation(kotlinCompilation: KotlinCompilation<*>): Provider<List<SubpluginOption>> {
val extension = kotlinCompilation.target.project.extensions.getByType<MyPluginExtension>()
return kotlinCompilation.target.project.provider {
listOf(
SubpluginOption("enable-feature", extension.enableFeature.toString()),
SubpluginOption("output-dir", extension.outputDir.absolutePath)
)
}
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-jetbrains-kotlin--kotlin-compiler-embeddable