CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-jetbrains-kotlin--kotlin-compiler

The Kotlin compiler infrastructure providing JVM, JavaScript, and metadata compilation capabilities

Pending
Overview
Eval results
Files

plugin-development.mddocs/

Plugin Development

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.

Capabilities

Modern Plugin Architecture (K2-Compatible)

The current plugin system designed for the K2 compiler frontend with improved performance and maintainability.

/**
 * Modern plugin registration interface for K2 compiler
 * Provides clean separation between compiler phases and IDE integration
 */
interface CompilerPluginRegistrar {
    /**
     * Register compiler plugin components
     * Called during compiler initialization to set up plugin infrastructure
     * 
     * @param project MockProject instance for component registration
     * @param configuration Compiler configuration with plugin settings
     */
    fun registerProjectComponents(
        project: MockProject,
        configuration: CompilerConfiguration
    )
    
    companion object {
        /**
         * Extension point for plugin registrars
         * Used by the compiler to discover and load plugins
         */
        val EXTENSION_POINT_NAME: ExtensionPointName<CompilerPluginRegistrar>
    }
}

Command Line Processing

Plugin command-line option processing for configuration and parameter passing.

/**
 * Processes command-line options for compiler plugins
 * Handles option parsing and configuration setup
 */
interface CommandLineProcessor {
    /** Unique identifier for the plugin */
    val pluginId: String
    
    /** Available plugin options */
    val pluginOptions: Collection<PluginOption>
    
    /**
     * Process a single command-line option
     * 
     * @param option The CLI option being processed
     * @param value The option value from command line
     * @param configuration Compiler configuration to update
     * @return true if option was handled, false otherwise
     */
    fun processOption(
        option: AbstractCliOption,
        value: String,
        configuration: CompilerConfiguration
    ): Boolean
}

/**
 * Definition of a plugin command-line option
 */
class PluginOption(
    /** Option name (without -- prefix) */
    val optionName: String,
    
    /** Value description for help text */
    val valueDescription: String,
    
    /** Help description for the option */
    val description: String,
    
    /** Whether option requires a value */
    val required: Boolean = true,
    
    /** Allow multiple values for this option */
    val allowMultipleOccurrences: Boolean = false
)

Legacy Plugin Architecture (K1)

Legacy plugin system maintained for backward compatibility.

/**
 * Legacy plugin registration interface (deprecated)
 * Maintained for K1 compiler compatibility
 */
@Deprecated("Use CompilerPluginRegistrar for K2 compatibility")
interface ComponentRegistrar {
    /**
     * Register plugin components with the compiler
     * 
     * @param project Project instance
     * @param configuration Compiler configuration
     */
    fun registerProjectComponents(
        project: MockProject,
        configuration: CompilerConfiguration
    )
    
    companion object {
        val EXTENSION_POINT_NAME: ExtensionPointName<ComponentRegistrar>
    }
}

Plugin Extension Points

Core extension points for implementing plugin functionality.

/**
 * Extension for IR generation and transformation
 * Allows plugins to generate and modify intermediate representation
 */
interface IrGenerationExtension {
    /**
     * Generate additional IR declarations
     * Called during IR generation phase
     */
    fun generate(
        moduleFragment: IrModuleFragment,
        pluginContext: IrPluginContext
    )
}

/**
 * Extension for frontend analysis and resolution
 * Enables custom declarations and synthetic elements
 */
interface AnalysisExtension {
    /**
     * Perform additional analysis
     * Called during frontend analysis phase
     */
    fun doAnalysis(
        project: Project,
        module: ModuleDescriptor,
        bindingTrace: BindingTrace,
        files: Collection<KtFile>
    ): AnalysisResult?
}

/**
 * Extension for code generation
 * Allows custom bytecode generation and optimization
 */
interface CodegenExtension {
    /**
     * Generate additional code
     * Called during code generation phase
     */
    fun generateCode(
        codegen: ExpressionCodegen,
        expression: KtExpression,
        type: Type
    ): StackValue?
}

Synthetic Declaration Provider

Plugin support for synthetic declarations and members.

/**
 * Provides synthetic declarations not present in source code
 * Useful for frameworks that generate members at compile time
 */
interface SyntheticResolveExtension {
    /**
     * Generate synthetic companion object
     */
    fun generateSyntheticClasses(
        moduleDescriptor: ModuleDescriptor,
        name: Name,
        ctx: LazyClassContext,
        declarationProvider: PackageMemberDeclarationProvider,
        result: MutableSet<ClassDescriptor>
    )
    
    /**
     * Generate synthetic members for existing classes
     */
    fun generateSyntheticMethods(
        thisDescriptor: ClassDescriptor,
        name: Name,
        bindingContext: BindingContext,
        fromSupertypes: List<SimpleFunctionDescriptor>,
        result: MutableCollection<SimpleFunctionDescriptor>
    )
    
    /**
     * Generate synthetic properties
     */
    fun generateSyntheticProperties(
        thisDescriptor: ClassDescriptor,
        name: Name,
        bindingContext: BindingContext,
        fromSupertypes: ArrayList<PropertyDescriptor>,
        result: MutableSet<PropertyDescriptor>
    )
}

Plugin Context and Services

Context and service access for plugin implementations.

/**
 * Plugin context providing access to compiler services
 * Available during plugin execution phases
 */
interface IrPluginContext {
    /** Module descriptor being compiled */
    val moduleDescriptor: ModuleDescriptor
    
    /** Symbol table for IR operations */
    val symbolTable: SymbolTable
    
    /** Type translator for IR types */
    val typeTranslator: TypeTranslator
    
    /** IR factory for creating IR elements */
    val irFactory: IrFactory
    
    /** Access to built-in types and symbols */
    val irBuiltIns: IrBuiltIns
}

/**
 * Configuration keys specific to plugins
 */
object PluginConfigurationKeys {
    /** Plugin options from command line */
    val PLUGIN_OPTIONS: CompilerConfigurationKey<List<PluginOption>>
    
    /** Plugin classpath */
    val PLUGIN_CLASSPATH: CompilerConfigurationKey<List<String>>
}

Usage Examples

Creating a Basic Plugin

import org.jetbrains.kotlin.compiler.plugin.*
import org.jetbrains.kotlin.config.CompilerConfiguration
import com.intellij.mock.MockProject

// 1. Define plugin options
class MyPluginCommandLineProcessor : CommandLineProcessor {
    override val pluginId = "my-plugin"
    
    override val pluginOptions = listOf(
        PluginOption(
            optionName = "enabled",
            valueDescription = "true|false", 
            description = "Enable my plugin functionality"
        ),
        PluginOption(
            optionName = "output-dir",
            valueDescription = "path",
            description = "Output directory for generated files"
        )
    )
    
    override fun processOption(
        option: AbstractCliOption,
        value: String,
        configuration: CompilerConfiguration
    ): Boolean {
        return when (option.optionName) {
            "enabled" -> {
                configuration.put(MyPluginConfigurationKeys.ENABLED, value.toBoolean())
                true
            }
            "output-dir" -> {
                configuration.put(MyPluginConfigurationKeys.OUTPUT_DIR, value)
                true
            }
            else -> false
        }
    }
}

// 2. Define configuration keys
object MyPluginConfigurationKeys {
    val ENABLED = CompilerConfigurationKey<Boolean>("my.plugin.enabled")
    val OUTPUT_DIR = CompilerConfigurationKey<String>("my.plugin.output.dir")
}

// 3. Implement plugin registrar
class MyCompilerPluginRegistrar : CompilerPluginRegistrar {
    override fun registerProjectComponents(
        project: MockProject,
        configuration: CompilerConfiguration
    ) {
        val enabled = configuration.get(MyPluginConfigurationKeys.ENABLED) ?: false
        if (!enabled) return
        
        // Register IR generation extension
        IrGenerationExtension.registerExtension(
            project,
            MyIrGenerationExtension(configuration)
        )
        
        // Register synthetic resolve extension
        SyntheticResolveExtension.registerExtension(
            project,
            MySyntheticResolveExtension()
        )
    }
}

IR Generation Extension

import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.*

class MyIrGenerationExtension(
    private val configuration: CompilerConfiguration
) : IrGenerationExtension {
    
    override fun generate(
        moduleFragment: IrModuleFragment,
        pluginContext: IrPluginContext
    ) {
        // Generate additional IR declarations
        moduleFragment.files.forEach { file ->
            file.declarations.filterIsInstance<IrClass>().forEach { irClass ->
                if (shouldProcessClass(irClass)) {
                    generateHelperMethod(irClass, pluginContext)
                }
            }
        }
    }
    
    private fun shouldProcessClass(irClass: IrClass): Boolean {
        // Check if class has specific annotation
        return irClass.hasAnnotation(FqName("com.example.MyAnnotation"))
    }
    
    private fun generateHelperMethod(
        irClass: IrClass,
        pluginContext: IrPluginContext
    ) {
        val function = pluginContext.irFactory.buildFun {
            name = Name.identifier("generatedHelper")
            returnType = pluginContext.irBuiltIns.unitType
            visibility = Visibilities.PUBLIC
        }
        
        // Build function body
        function.body = DeclarationIrBuilder(pluginContext, function.symbol).irBlockBody {
            // Generate IR statements
            +irReturn(irUnit())
        }
        
        irClass.declarations.add(function)
    }
}

Synthetic Member Generation

import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension

class MySyntheticResolveExtension : SyntheticResolveExtension {
    
    override fun generateSyntheticMethods(
        thisDescriptor: ClassDescriptor,
        name: Name,
        bindingContext: BindingContext,
        fromSupertypes: List<SimpleFunctionDescriptor>,
        result: MutableCollection<SimpleFunctionDescriptor>
    ) {
        // Generate synthetic getter/setter methods
        if (name.asString().startsWith("get") || name.asString().startsWith("set")) {
            val propertyName = name.asString().removePrefix("get").removePrefix("set")
            
            if (hasPropertyAnnotation(thisDescriptor, propertyName)) {
                val syntheticFunction = createSyntheticFunction(
                    thisDescriptor,
                    name,
                    propertyName
                )
                result.add(syntheticFunction)
            }
        }
    }
    
    override fun generateSyntheticProperties(
        thisDescriptor: ClassDescriptor,
        name: Name,
        bindingContext: BindingContext,
        fromSupertypes: ArrayList<PropertyDescriptor>,
        result: MutableSet<PropertyDescriptor>
    ) {
        // Generate synthetic properties based on annotations
        if (hasGeneratePropertyAnnotation(thisDescriptor, name)) {
            val syntheticProperty = createSyntheticProperty(thisDescriptor, name)
            result.add(syntheticProperty)
        }
    }
    
    private fun hasPropertyAnnotation(
        classDescriptor: ClassDescriptor,
        propertyName: String
    ): Boolean {
        return classDescriptor.annotations.hasAnnotation(
            FqName("com.example.GenerateProperty")
        )
    }
    
    private fun createSyntheticFunction(
        owner: ClassDescriptor,
        name: Name,
        propertyName: String
    ): SimpleFunctionDescriptor {
        return SimpleFunctionDescriptorImpl.create(
            owner,
            Annotations.EMPTY,
            name,
            CallableMemberDescriptor.Kind.SYNTHESIZED,
            owner.source
        ).apply {
            // Configure function parameters and return type
            initialize(
                null, // extension receiver
                owner.thisAsReceiverParameter,
                emptyList(), // type parameters
                emptyList(), // value parameters
                owner.builtIns.stringType, // return type
                Modality.FINAL,
                Visibilities.PUBLIC
            )
        }
    }
}

Plugin Registration and Discovery

// META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar
com.example.MyCompilerPluginRegistrar

// META-INF/services/org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor  
com.example.MyPluginCommandLineProcessor

Plugin Usage in Build Scripts

Gradle (build.gradle.kts):

plugins {
    kotlin("jvm")
    id("my-kotlin-plugin") version "1.0.0"
}

kotlinCompilerPluginOptions {
    option("my-plugin", "enabled", "true")
    option("my-plugin", "output-dir", "build/generated")
}

Command Line:

kotlinc \
  -Xplugin=my-plugin.jar \
  -P plugin:my-plugin:enabled=true \
  -P plugin:my-plugin:output-dir=build/generated \
  src/main/kotlin/Main.kt

Advanced Plugin Patterns

// Multi-phase plugin with dependency ordering
class AdvancedPluginRegistrar : CompilerPluginRegistrar {
    
    override fun registerProjectComponents(
        project: MockProject,
        configuration: CompilerConfiguration
    ) {
        // Register multiple extensions with specific ordering
        val analysisExtension = MyAnalysisExtension(configuration)
        val irExtension = MyIrGenerationExtension(configuration) 
        val codegenExtension = MyCodegenExtension(configuration)
        
        // Analysis phase
        AnalysisExtension.registerExtension(project, analysisExtension)
        
        // IR generation phase  
        IrGenerationExtension.registerExtension(project, irExtension)
        
        // Code generation phase
        CodegenExtension.registerExtension(project, codegenExtension)
        
        // Cross-phase communication via configuration
        configuration.put(ANALYSIS_RESULTS, analysisExtension.results)
    }
}

// Plugin with custom diagnostic reporting
class DiagnosticPlugin : IrGenerationExtension {
    
    override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
        moduleFragment.files.forEach { file ->
            validateFile(file, pluginContext)
        }
    }
    
    private fun validateFile(file: IrFile, context: IrPluginContext) {
        file.declarations.forEach { declaration ->
            if (hasIssue(declaration)) {
                // Report custom diagnostic
                context.messageCollector.report(
                    CompilerMessageSeverity.WARNING,
                    "Custom plugin warning: ${declaration.name}",
                    CompilerMessageSourceLocation.create(
                        file.path,
                        declaration.startOffset,
                        declaration.endOffset,
                        null
                    )
                )
            }
        }
    }
}

Plugin Distribution

JAR Packaging

Package plugin with service registration files:

my-plugin.jar
├── com/example/MyPluginRegistrar.class
├── com/example/MyCommandLineProcessor.class  
├── META-INF/
│   └── services/
│       ├── org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar
│       └── org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor
└── plugin.xml (optional, for IDE integration)

Error Handling

class PluginException(message: String, cause: Throwable? = null) : Exception(message, cause)

class InvalidPluginConfigurationException(message: String) : PluginException(message)

Common plugin error scenarios:

  • PLUGIN_LOADING_ERROR: Plugin JAR not found or invalid service registration
  • CONFIGURATION_ERROR: Invalid plugin options or missing required parameters
  • COMPILATION_PHASE_ERROR: Plugin errors during specific compilation phases
  • EXTENSION_REGISTRATION_ERROR: Extension point registration failures

Install with Tessl CLI

npx tessl i tessl/maven-org-jetbrains-kotlin--kotlin-compiler

docs

configuration.md

environment.md

index.md

js-wasm-compilation.md

jvm-compilation.md

message-handling.md

plugin-development.md

tile.json