writing your first kotlin compiler plugin · are these basically annotation processors? •...

155
Writing Your First Kotlin Compiler Plugin Kevin Most

Upload: others

Post on 29-May-2020

21 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

Writing Your First Kotlin Compiler Plugin

Kevin Most

Page 2: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

A brief intro

Page 3: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

Are these basically annotation processors?

• Annotation Processors:

• Your code runs at compile-time

• Public, documented API

• Emit Java source code

• Works on Kotlin/Java source code

• Multiplatform not supported

• Compiler Plugins:

• Your code runs at compile-time

• Private, undocumented API

• Emit Java bytecode (or LLVM IR)

• Works on Kotlin source code only

• Multiplatform supported

Page 4: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

Why write compiler plugins?

• Incredibly powerful API; you can modify function/class internals

• Enables you to solve new classes of metaprogramming problems

• Annotation processors are JVM-only, while compiler plugins aren't

Page 5: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

Why NOT write compiler plugins?

• Annotation processors are much easier to write (if you only care about JVM)

• Compiler plugins are a lot of work. You need to write:

• An IntelliJ plugin (if creating synthetic members)

• A Gradle (or Maven, or other build tool) plugin

• Slightly different extensions for JVM, JS, and Native targets

Page 6: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

Examples of compiler plugins

• allopen: Modifies annotated class to be open

• noarg: Modifies annotated class to have a zero-argument constructor

• android-extensions: findViewById(R.id.foo) aliased, and automatic Parcelable impl generation via @Parcelize

• kotlin-serialization: Automatic generation of Serializable impl

• First multiplatform-ready plugin (generates LLVM IR for native too)

Page 7: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

• All existing compiler plugins are 1st party (github.com/JetBrains/kotlin)

• plugins/{name}/... for the actual plugin business logic

• libraries/tools/kotlin-{name}/... for the Gradle wrappers

• libraries/tools/kotlin-maven-{name}/... for the Maven wrappers

Examples of compiler plugins

Page 8: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

Plugin ArchitecturesPlugin

Subplugin

CommandLineProcessor

ComponentRegistrar

Extension Extension

Page 9: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

Plugin Architecture

Plugin

Subplugin

CommandLineProcessor

ComponentRegistrar

Extension Extension

Page 10: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

Plugin Architecture

Plugin

Subplugin

CommandLineProcessor

ComponentRegistrar

Extension Extension

• Gradle API (totally unrelated to Kotlin)

• Provides an entry point from a build.gradle script

• Allows configuration via Gradle extensions

Page 11: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

Plugin Architecture

Plugin

Subplugin

CommandLineProcessor

ComponentRegistrar

Extension Extension

• The interface between the Gradle and Kotlin APIs

• Read Gradle extension options

• Write out Kotlin SubpluginOptions

• Define the compiler plugin's ID (internal unique key)

• Define the Kotlin plugin's Maven coordinates so the compiler can download it

Page 12: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

Plugin Architecture

Plugin

Subplugin

CommandLineProcessor

ComponentRegistrar

Extension Extension

• Reads kotlinc -Xplugin args

• Subplugin options actually get passed through this pipeline

• Write CompilerConfigurationKeys

Page 13: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

Plugin Architecture

Plugin

Subplugin

CommandLineProcessor

ComponentRegistrar

Extension Extension

• Read CompilerConfigurationKeys

• Register Extensions

Page 14: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

Plugin Architecture

Plugin

Subplugin

CommandLineProcessor

ComponentRegistrar

Extension Extension

• Generates code (finally!)

• Multiple types of extensions, such as:

• ExpressionCodegenExtension

• ClassBuilderInterceptorExtension

• StorageComponentContainerContributor

• IrGenerationExtension (!!)

• Write bytecode (or LLVM IR!!)

Page 15: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

Let's build our own!

Page 16: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

Let's build our own!• We'll build a compiler plugin that traces method calls

• A method annotated with a debug-log annotation will have its method body modified to include logging

• Could not be an annotation processor; modifies the function body

• Prior art:

• Hugo: github.com/jakewharton/hugo

• Firebase Performance Monitoring: firebase.google.com/docs/perf-mon

• Both use AspectJ bytecode weaving + Android Gradle Transform API

Page 17: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

fun prime(n: Int): Long { println("⇢ prime(n=$n)") val startTime = System.currentTimeMillis() val result = primeNumberSequence.take(n).last() val timeToRun = System.currentTimeMillis() - startTime println("⇠ prime [ran in $timeToRun ms]") return result}

The goal

Page 18: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

fun prime(n: Int): Long { println("⇢ prime(n=$n)") val startTime = System.currentTimeMillis() val result = primeNumberSequence.take(n).last() val timeToRun = System.currentTimeMillis() - startTime println("⇠ prime [ran in $timeToRun ms]") return result}

The goal

Page 19: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@DebugLog fun prime(n: Int): Long = primeNumberSequence.take(n).last()

fun prime(n: Int): Long { println("⇢ prime(n=$n)") val startTime = System.currentTimeMillis() val result = primeNumberSequence.take(n).last() val timeToRun = System.currentTimeMillis() - startTime println("⇠ prime [ran in $timeToRun ms]") return result}

The goal

Page 20: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

Let's build our own!

Page 21: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

Plugin

Subplugin

CommandLineProcessor

ComponentRegistrar

Extension Extension

Page 22: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

gradle-plugin/build.gradle

apply plugin: "java-gradle-plugin"apply plugin: "org.jetbrains.kotlin.jvm"apply plugin: "kotlin-kapt"

gradlePlugin { plugins { simplePlugin { id = "debuglog.plugin" implementationClass = "debuglog.DebugLogGradlePlugin" } }}

dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$ktVersion" implementation "org.jetbrains.kotlin:kotlin-gradle-plugin-api:$ktVersion"

compileOnly "com.google.auto.service:auto-service:1.0-rc4" kapt "com.google.auto.service:auto-service:1.0-rc4"}

Page 23: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

apply plugin: "java-gradle-plugin"apply plugin: "org.jetbrains.kotlin.jvm"apply plugin: "kotlin-kapt"

gradlePlugin { plugins { simplePlugin { id = "debuglog.plugin" implementationClass = "debuglog.DebugLogGradlePlugin" } }}

dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$ktVersion" implementation "org.jetbrains.kotlin:kotlin-gradle-plugin-api:$ktVersion"

compileOnly "com.google.auto.service:auto-service:1.0-rc4" kapt "com.google.auto.service:auto-service:1.0-rc4"}

gradle-plugin/build.gradle

Page 24: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

apply plugin: "java-gradle-plugin"apply plugin: "org.jetbrains.kotlin.jvm"apply plugin: "kotlin-kapt"

gradlePlugin { plugins { simplePlugin { id = "debuglog.plugin" // `apply plugin: "debuglog.plugin"` implementationClass = "debuglog.DebugLogGradlePlugin" } }}

dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$ktVersion" implementation "org.jetbrains.kotlin:kotlin-gradle-plugin-api:$ktVersion"

compileOnly "com.google.auto.service:auto-service:1.0-rc4" kapt "com.google.auto.service:auto-service:1.0-rc4"}

gradle-plugin/build.gradle

Page 25: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

apply plugin: "java-gradle-plugin"apply plugin: "org.jetbrains.kotlin.jvm"apply plugin: "kotlin-kapt"

gradlePlugin { plugins { simplePlugin { id = "debuglog.plugin" // `apply plugin: "debuglog.plugin"` implementationClass = "debuglog.DebugLogGradlePlugin" // entry-point class } }}

dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$ktVersion" implementation "org.jetbrains.kotlin:kotlin-gradle-plugin-api:$ktVersion"

compileOnly "com.google.auto.service:auto-service:1.0-rc4" kapt "com.google.auto.service:auto-service:1.0-rc4"}

gradle-plugin/build.gradle

Page 26: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

gradle-plugin/build.gradle

apply plugin: "java-gradle-plugin"apply plugin: "org.jetbrains.kotlin.jvm"apply plugin: "kotlin-kapt"

gradlePlugin { plugins { simplePlugin { id = "debuglog.plugin" // `apply plugin: "debuglog.plugin"` implementationClass = "debuglog.DebugLogGradlePlugin" // entry-point class } }}

dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$ktVersion" implementation "org.jetbrains.kotlin:kotlin-gradle-plugin-api:$ktVersion"

compileOnly "com.google.auto.service:auto-service:1.0-rc4" kapt "com.google.auto.service:auto-service:1.0-rc4"}

Page 27: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

gradle-plugin/build.gradle

apply plugin: "java-gradle-plugin"apply plugin: "org.jetbrains.kotlin.jvm"apply plugin: "kotlin-kapt"

gradlePlugin { plugins { simplePlugin { id = "debuglog.plugin" // `apply plugin: "debuglog.plugin"` implementationClass = "debuglog.DebugLogGradlePlugin" // entry-point class } }}

dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$ktVersion" implementation "org.jetbrains.kotlin:kotlin-gradle-plugin-api:$ktVersion"

compileOnly "com.google.auto.service:auto-service:1.0-rc4" kapt "com.google.auto.service:auto-service:1.0-rc4"}

Page 28: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

Plugin

Subplugin

CommandLineProcessor

ComponentRegistrar

Extension Extension

Page 29: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

class DebugLogGradlePlugin : org.gradle.api.Plugin<Project> { override fun apply(project: Project) { project.extensions.create( "debugLog", DebugLogGradleExtension::class.java ) }}

open class DebugLogGradleExtension { var enabled: Boolean = true var annotations: List<String> = emptyList()}

Page 30: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

Plugin

Subplugin

CommandLineProcessor

ComponentRegistrar

Extension Extension

Page 31: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(KotlinGradleSubplugin::class) // don't forget!class DebugLogGradleSubplugin : KotlinGradleSubplugin<AbstractCompile> {

override fun isApplicable( project: Project, task: AbstractCompile ): Boolean = TODO()

override fun getCompilerPluginId(): String = TODO()

override fun getPluginArtifact(): SubpluginArtifact = TODO() override fun apply(project: Project, /*...*/): List<SubpluginOption> { TODO() }}

Page 32: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(KotlinGradleSubplugin::class) // don't forget!class DebugLogGradleSubplugin : KotlinGradleSubplugin<AbstractCompile> { override fun isApplicable( project: Project, task: AbstractCompile ): Boolean = TODO() override fun getCompilerPluginId(): String = TODO() override fun getPluginArtifact(): SubpluginArtifact = TODO() override fun apply(project: Project, /*...*/): List<SubpluginOption> { TODO() }}

Page 33: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(KotlinGradleSubplugin::class) // don't forget!class DebugLogGradleSubplugin : KotlinGradleSubplugin<AbstractCompile> {

override fun isApplicable( project: Project, task: AbstractCompile ): Boolean = project.plugins.hasPlugin(DebugLogGradlePlugin::class.java)

override fun getCompilerPluginId(): String = TODO()

override fun getPluginArtifact(): SubpluginArtifact = TODO() override fun apply(project: Project, /*...*/): List<SubpluginOption> { TODO() }}

Page 34: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(KotlinGradleSubplugin::class) // don't forget!class DebugLogGradleSubplugin : KotlinGradleSubplugin<AbstractCompile> { override fun isApplicable( project: Project, task: AbstractCompile ): Boolean = project.plugins.hasPlugin(DebugLogGradlePlugin::class.java)

override fun getCompilerPluginId(): String = TODO()

override fun getPluginArtifact(): SubpluginArtifact = TODO() override fun apply(project: Project, /*...*/): List<SubpluginOption> { TODO() }}

Page 35: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(KotlinGradleSubplugin::class) // don't forget!class DebugLogGradleSubplugin : KotlinGradleSubplugin<AbstractCompile> { override fun isApplicable( project: Project, task: AbstractCompile ): Boolean = project.plugins.hasPlugin(DebugLogGradlePlugin::class.java)

override fun getCompilerPluginId(): String = TODO()

override fun getPluginArtifact(): SubpluginArtifact = TODO() override fun apply(project: Project, /*...*/): List<SubpluginOption> { TODO() }}

Page 36: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(KotlinGradleSubplugin::class) // don't forget!class DebugLogGradleSubplugin : KotlinGradleSubplugin<AbstractCompile> { override fun isApplicable( project: Project, task: AbstractCompile ): Boolean = project.plugins.hasPlugin(DebugLogGradlePlugin::class.java)

override fun getCompilerPluginId(): String = "debuglog"

override fun getPluginArtifact(): SubpluginArtifact = TODO() override fun apply(project: Project, /*...*/): List<SubpluginOption> { TODO() }}

Page 37: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(KotlinGradleSubplugin::class) // don't forget!class DebugLogGradleSubplugin : KotlinGradleSubplugin<AbstractCompile> { override fun isApplicable( project: Project, task: AbstractCompile ): Boolean = project.plugins.hasPlugin(DebugLogGradlePlugin::class.java)

override fun getCompilerPluginId(): String = "debuglog"

override fun getPluginArtifact(): SubpluginArtifact = TODO() override fun apply(project: Project, /*...*/): List<SubpluginOption> { TODO() }}

Page 38: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(KotlinGradleSubplugin::class) // don't forget!class DebugLogGradleSubplugin : KotlinGradleSubplugin<AbstractCompile> { override fun isApplicable( project: Project, task: AbstractCompile ): Boolean = project.plugins.hasPlugin(DebugLogGradlePlugin::class.java)

override fun getCompilerPluginId(): String = "debuglog"

override fun getPluginArtifact(): SubpluginArtifact = TODO() override fun apply(project: Project, /*...*/): List<SubpluginOption> { TODO() }2}1

Page 39: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(KotlinGradleSubplugin::class) // don't forget!class DebugLogGradleSubplugin : KotlinGradleSubplugin<AbstractCompile> { override fun isApplicable( project: Project, task: AbstractCompile ): Boolean = project.plugins.hasPlugin(DebugLogGradlePlugin::class.java)

override fun getCompilerPluginId(): String = "debuglog"

override fun getPluginArtifact(): SubpluginArtifact = SubpluginArtifact( groupId = "debuglog", artifactId = "kotlin-plugin", version = "0.0.1" ) override fun apply(project: Project, /*...*/): List<SubpluginOption> { TODO() }2}1

Page 40: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(KotlinGradleSubplugin::class) // don't forget!class DebugLogGradleSubplugin : KotlinGradleSubplugin<AbstractCompile> { override fun isApplicable( project: Project, task: AbstractCompile ): Boolean = project.plugins.hasPlugin(DebugLogGradlePlugin::class.java)

override fun getCompilerPluginId(): String = "debuglog"

override fun getPluginArtifact(): SubpluginArtifact = SubpluginArtifact( groupId = "debuglog", artifactId = "kotlin-plugin", version = "0.0.1" ) override fun apply(project: Project, /*...*/): List<SubpluginOption> { TODO() }}

Page 41: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(KotlinGradleSubplugin::class) // don't forget!class DebugLogGradleSubplugin1:1KotlinGradleSubplugin<AbstractCompile>1{1 override fun isApplicable( project: Project, task: AbstractCompile ): Boolean = project.plugins.hasPlugin(DebugLogGradlePlugin::class.java)

override fun getCompilerPluginId(): String = "debuglog"

override fun getPluginArtifact(): SubpluginArtifact = SubpluginArtifact( groupId = "debuglog", artifactId = "kotlin-plugin", version = "0.0.1" ) override fun apply(project: Project, /*...*/): List<SubpluginOption>1{ TODO() }2}1

Page 42: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(KotlinGradleSubplugin::class) // don't forget!class DebugLogGradleSubplugin1:1KotlinGradleSubplugin<AbstractCompile>1{1

// other method impls override fun apply(project: Project, /*...*/): List<SubpluginOption>1{ TODO()Z }2}1

Page 43: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(KotlinGradleSubplugin::class) // don't forget!class DebugLogGradleSubplugin1:1KotlinGradleSubplugin<AbstractCompile>1{1

// other method impls override fun apply(project: Project, /*...*/): List<SubpluginOption>1{ val extension = project.extensions.findByType<DebugLogGradleExtension>() ?: DebugLogGradleExtension() if (extension.enabled && extension.annotations.isEmpty()) error("DebugLog is enabled, but no annotations were set")

val annotationOptions = extension.annotations .map { SubpluginOption(key = "debugLogAnnotation", value = it) } val enabledOption = SubpluginOption( key = "enabled", value = extension.enabled.toString()) return annotationOptions + enabledOption }2}1

Page 44: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(KotlinGradleSubplugin::class) // don't forget!class DebugLogGradleSubplugin1:1KotlinGradleSubplugin<AbstractCompile>1{1

// other method impls override fun apply(project: Project, /*...*/): List<SubpluginOption>1{ val extension = project.extensions.findByType<DebugLogGradleExtension>() ?: DebugLogGradleExtension() if (extension.enabled && extension.annotations.isEmpty()) error("DebugLog is enabled, but no annotations were set")

val annotationOptions = extension.annotations .map { SubpluginOption(key = "debugLogAnnotation", value = it) } val enabledOption = SubpluginOption( key = "enabled", value = extension.enabled.toString()) return annotationOptions + enabledOption }2}1

Page 45: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(KotlinGradleSubplugin::class) // don't forget!class DebugLogGradleSubplugin1:1KotlinGradleSubplugin<AbstractCompile>1{1

// other method impls override fun apply(project: Project, /*...*/): List<SubpluginOption>1{ val extension = project.extensions.findByType<DebugLogGradleExtension>() ?: DebugLogGradleExtension() if (extension.enabled && extension.annotations.isEmpty()) error("DebugLog is enabled, but no annotations were set")

val annotationOptions = extension.annotations .map { SubpluginOption(key = "debugLogAnnotation", value = it) } val enabledOption = SubpluginOption( key = "enabled", value = extension.enabled.toString()) return annotationOptions + enabledOption }2}1

Page 46: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(KotlinGradleSubplugin::class) // don't forget!class DebugLogGradleSubplugin1:1KotlinGradleSubplugin<AbstractCompile>1{1

// other method impls override fun apply(project: Project, /*...*/): List<SubpluginOption>1{ val extension = project.extensions.findByType<DebugLogGradleExtension>() ?: DebugLogGradleExtension() if (extension.enabled && extension.annotations.isEmpty()) error("DebugLog is enabled, but no annotations were set")

val annotationOptions = extension.annotations .map { SubpluginOption(key = "debugLogAnnotation", value = it) } val enabledOption = SubpluginOption( key = "enabled", value = extension.enabled.toString()) return annotationOptions + enabledOption }2}1

Page 47: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

Plugin

Subplugin

CommandLineProcessor

ComponentRegistrar

Extension Extension

Page 48: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/build.gradle

apply plugin: "org.jetbrains.kotlin.jvm"apply plugin: "kotlin-kapt"

dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$ktVersion" compileOnly "org.jetbrains.kotlin:kotlin-compiler-embeddable:$ktVersion"

compileOnly "com.google.auto.service:auto-service:1.0-rc4" kapt "com.google.auto.service:auto-service:1.0-rc4"}

Page 49: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/build.gradle

apply plugin: "org.jetbrains.kotlin.jvm"apply plugin: "kotlin-kapt"

dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$ktVersion" compileOnly "org.jetbrains.kotlin:kotlin-compiler-embeddable:$ktVersion"

compileOnly "com.google.auto.service:auto-service:1.0-rc4" kapt "com.google.auto.service:auto-service:1.0-rc4"}

Page 50: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/build.gradle

apply plugin: "org.jetbrains.kotlin.jvm"apply plugin: "kotlin-kapt"

dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$ktVersion" compileOnly "org.jetbrains.kotlin:kotlin-compiler-embeddable:$ktVersion"

compileOnly "com.google.auto.service:auto-service:1.0-rc4" kapt "com.google.auto.service:auto-service:1.0-rc4"}

Page 51: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/build.gradle

apply plugin: "org.jetbrains.kotlin.jvm"apply plugin: "kotlin-kapt"

dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$ktVersion" compileOnly "org.jetbrains.kotlin:kotlin-compiler-embeddable:$ktVersion"

compileOnly "com.google.auto.service:auto-service:1.0-rc4" kapt "com.google.auto.service:auto-service:1.0-rc4"}

Page 52: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

Plugin

Subplugin

CommandLineProcessor

ComponentRegistrar

Extension Extension

Page 53: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(CommandLineProcessor::class)class DebugLogCommandLineProcessor : CommandLineProcessor {

override val pluginId: String = TODO()

override val pluginOptions: Collection<CliOption> = listOf( )

override fun processOption( option: CliOption, value: String, configuration: CompilerConfiguration ) { TODO() }}

Page 54: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(CommandLineProcessor::class)class DebugLogCommandLineProcessor : CommandLineProcessor {

override val pluginId: String = TODO()

override val pluginOptions: Collection<CliOption> = listOf( )

override fun processOption( option: CliOption, value: String, configuration: CompilerConfiguration ) { TODO() }}

Page 55: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(CommandLineProcessor::class)class DebugLogCommandLineProcessor : CommandLineProcessor {

override val pluginId: String = "debuglog" // same as ID from subplugin

override val pluginOptions: Collection<CliOption> = listOf( )

override fun processOption( option: CliOption, value: String, configuration: CompilerConfiguration ) { TODO() }}

Page 56: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(CommandLineProcessor::class)class DebugLogCommandLineProcessor : CommandLineProcessor {

override val pluginId: String = "debuglog" // same as ID from subplugin

override val pluginOptions: Collection<CliOption> = listOf( )

override fun processOption( option: CliOption, value: String, configuration: CompilerConfiguration ) { TODO() }}

Page 57: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(CommandLineProcessor::class)class DebugLogCommandLineProcessor : CommandLineProcessor {

override val pluginId: String = "debuglog" // same as ID from subplugin

override val pluginOptions: Collection<CliOption> = listOf( )P

override fun processOption( option: CliOption, value: String, configuration: CompilerConfiguration ) { TODO() }2}1

Page 58: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(CommandLineProcessor::class)class DebugLogCommandLineProcessor : CommandLineProcessor {

override val pluginId: String = "debuglog" // same as ID from subplugin

override val pluginOptions: Collection<CliOption> = listOf( CliOption("enabled", "<true|false>", "whether plugin is enabled"), CliOption( "debugLogAnnotation", "<fqname>", "debug-log annotation names", required = true, allowMultipleOccurrences = true))P

override fun processOption( option: CliOption, value: String, configuration: CompilerConfiguration ) { TODO() }2}1

Page 59: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(CommandLineProcessor::class)class DebugLogCommandLineProcessor : CommandLineProcessor {

override val pluginId: String = "debuglog" // same as ID from subplugin

override val pluginOptions: Collection<CliOption> = listOf( CliOption("enabled", "<true|false>", "whether plugin is enabled"), CliOption( "debugLogAnnotation", "<fqname>", "debug-log annotation names", required = true, allowMultipleOccurrences = true))P

override fun processOption( option: CliOption, value: String, configuration: CompilerConfiguration ) { TODO() }}

Page 60: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(CommandLineProcessor::class)class DebugLogCommandLineProcessor : CommandLineProcessor {

override val pluginId: String = "debuglog" // same as ID from subplugin

override val pluginOptions: Collection<CliOption> = listOf( CliOption("enabled", "<true|false>", "whether plugin is enabled"), CliOption( "debugLogAnnotation", "<fqname>", "debug-log annotation names", required = true, allowMultipleOccurrences = true))P

override fun processOption( option: CliOption, value: String, configuration: CompilerConfiguration ) { TODO() }}

Page 61: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(CommandLineProcessor::class)class DebugLogCommandLineProcessor : CommandLineProcessor {

override val pluginId: String = "debuglog" // same as ID from subplugin

override val pluginOptions: Collection<CliOption> = listOf( CliOption("enabled", "<true|false>", "whether plugin is enabled"), CliOption( "debugLogAnnotation", "<fqname>", "debug-log annotation names", required = true, allowMultipleOccurrences = true))P

override fun processOption( option: CliOption, value: String, configuration: CompilerConfiguration ) = when (option.name) {

}}

Page 62: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(CommandLineProcessor::class)class DebugLogCommandLineProcessor : CommandLineProcessor {

override val pluginId: String = "debuglog" // same as ID from subplugin

override val pluginOptions: Collection<CliOption> = listOf( CliOption("enabled", "<true|false>", "whether plugin is enabled"), CliOption( "debugLogAnnotation", "<fqname>", "debug-log annotation names", required = true, allowMultipleOccurrences = true))P

override fun processOption( option: CliOption, value: String, configuration: CompilerConfiguration ) = when (option.name) { "enabled" -> configuration.put(KEY_ENABLED, value.toBoolean()) }2}1

Page 63: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(CommandLineProcessor::class)class DebugLogCommandLineProcessor : CommandLineProcessor {

override val pluginId: String = "debuglog" // same as ID from subplugin

override val pluginOptions: Collection<CliOption> = listOf( CliOption("enabled", "<true|false>", "whether plugin is enabled"), CliOption( "debugLogAnnotation", "<fqname>", "debug-log annotation names", required = true, allowMultipleOccurrences = true))P

override fun processOption( option: CliOption, value: String, configuration: CompilerConfiguration ) = when (option.name) { "enabled" -> configuration.put(KEY_ENABLED, value.toBoolean()) "debugLogAnnotation" -> configuration.appendList(KEY_ANNOTATIONS, value) }2}1

Page 64: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(CommandLineProcessor::class)class DebugLogCommandLineProcessor : CommandLineProcessor {

override val pluginId: String = "debuglog" // same as ID from subplugin

override val pluginOptions: Collection<CliOption> = listOf( CliOption("enabled", "<true|false>", "whether plugin is enabled"), CliOption( "debugLogAnnotation", "<fqname>", "debug-log annotation names", required = true, allowMultipleOccurrences = true))P

override fun processOption( option: CliOption, value: String, configuration: CompilerConfiguration ) = when (option.name) { "enabled" -> configuration.put(KEY_ENABLED, value.toBoolean()) "debugLogAnnotation" -> configuration.appendList(KEY_ANNOTATIONS, value) else -> error("Unexpected config option ${option.name}") }2}1

Page 65: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

Plugin

Subplugin

CommandLineProcessor

ComponentRegistrar

Extension Extension

Page 66: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(ComponentRegistrar::class)class DebugLogComponentRegistrar : ComponentRegistrar { override fun registerProjectComponents( project: MockProject, configuration: CompilerConfiguration ) { if (configuration[KEY_ENABLED] == false) { return } ClassBuilderInterceptorExtension.registerExtension( project, DebugLogClassGenerationInterceptor( debugLogAnnotations = configuration[KEY_ANNOTATIONS] ?: error("debuglog plugin requires at least one annotation class option passed to it") ) ) }}

Page 67: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@AutoService(ComponentRegistrar::class)class DebugLogComponentRegistrar : ComponentRegistrar { override fun registerProjectComponents( project: MockProject, configuration: CompilerConfiguration ) { if (configuration[KEY_ENABLED] == false) { return } ClassBuilderInterceptorExtension.registerExtension( project, DebugLogClassGenerationInterceptor( debugLogAnnotations = configuration[KEY_ANNOTATIONS] ?: error("debuglog plugin requires at least one annotation class option passed to it") ) ) }}

Page 68: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

Plugin

Subplugin

CommandLineProcessor

ComponentRegistrar

Extension Extension

Page 69: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

class DebugLogClassGenerationInterceptor( val debugLogAnnotations: List<String>) : ClassBuilderInterceptorExtension { override fun interceptClassBuilderFactory( interceptedFactory: ClassBuilderFactory, bindingContext: BindingContext, diagnostics: DiagnosticSink ): ClassBuilderFactory = TODO()}

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassGenerationInterceptor.kt

Page 70: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassGenerationInterceptor.kt

class DebugLogClassGenerationInterceptor( val debugLogAnnotations: List<String>) : ClassBuilderInterceptorExtension { override fun interceptClassBuilderFactory( interceptedFactory: ClassBuilderFactory, bindingContext: BindingContext, diagnostics: DiagnosticSink ): ClassBuilderFactory = object: ClassBuilderFactory by interceptedFactory}1

Page 71: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassGenerationInterceptor.kt

class DebugLogClassGenerationInterceptor( val debugLogAnnotations: List<String>) : ClassBuilderInterceptorExtension { override fun interceptClassBuilderFactory( interceptedFactory: ClassBuilderFactory, bindingContext: BindingContext, diagnostics: DiagnosticSink ): ClassBuilderFactory = object: ClassBuilderFactory by interceptedFactory { override fun newClassBuilder(origin: JvmDeclarationOrigin) = DebugLogClassBuilder( annotations = debugLogAnnotations, delegateBuilder = interceptedFactory.newClassBuilder(origin)) }}1

Page 72: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

private class DebugLogClassBuilder( val annotations: List<String>, delegateBuilder: ClassBuilder) : DelegatingClassBuilder(delegateBuilder) { override fun newMethod( origin: JvmDeclarationOrigin, access: Int, name: String, desc: String, signature: String?, exceptions: Array<out String>? ): MethodVisitor { }1}2

Page 73: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

private class DebugLogClassBuilder( val annotations: List<String>, delegateBuilder: ClassBuilder) : DelegatingClassBuilder(delegateBuilder) { override fun newMethod( origin: JvmDeclarationOrigin,... ): MethodVisitor { }1}2

Page 74: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

private class DebugLogClassBuilder( val annotations: List<String>, delegateBuilder: ClassBuilder) : DelegatingClassBuilder(delegateBuilder) { override fun newMethod( origin: JvmDeclarationOrigin,... ): MethodVisitor { val original = super.newMethod(origin, ...) val function = origin.descriptor as? FunctionDescriptor ?: return original if (annotations.none { descriptor.annotations.hasAnnotation(it) }) { return original } }1}2

Page 75: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

private class DebugLogClassBuilder( val annotations: List<String>, delegateBuilder: ClassBuilder) : DelegatingClassBuilder(delegateBuilder) { override fun newMethod( origin: JvmDeclarationOrigin,... ): MethodVisitor { val original = super.newMethod(origin, ...) val function = origin.descriptor as? FunctionDescriptor ?: return original if (annotations.none { descriptor.annotations.hasAnnotation(it) }) { return original } return object : MethodVisitor(Opcodes.ASM5, original) { }3 }1}2

Page 76: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

return object : MethodVisitor(Opcodes.ASM5, original) { }3

Page 77: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

return object : MethodVisitor(Opcodes.ASM5, original) { override fun visitCode() { super.visitCode() InstructionAdapter(this).apply { TODO("on method entry") } }4}3

Page 78: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

return object : MethodVisitor(Opcodes.ASM5, original) { override fun visitCode() { super.visitCode() InstructionAdapter(this).apply { TODO("on method entry") } }4 override fun visitInsn(opcode: Int) { when (opcode) { RETURN /* void */, ARETURN /* object */, IRETURN /* int */ -> { InstructionAdapter(this).apply { TODO("on method exit") } } } super.visitInsn(opcode) }}3

Page 79: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

What now?• You write bytecode

• Uses the ObjectWeb ASM API

• Neither related to ASM (assembly) or Web ASM (wasm) in any way

• An API for modifying JVM bytecode

• The JVM is a stack machine

• One stack that methods operate upon

• You can also read arbitrary variables from the Local Variable Array

Page 80: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

What does bytecode look like?fun printSimpleSum() { val sum = v1() + v2() println("sum of values was $sum")}

INVOKESTATIC myapp/RunnerKt.v1 ()IINVOKESTATIC myapp/RunnerKt.v2 ()IIADDISTORE 1GETSTATIC j/l/System.out : Lj/io/PrintStream;NEW j/l/StringBuilderDUPINVOKESPECIAL j/l/StringBuilder.<init> ()VLDC "sum of values was "INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder;ILOAD 1INVOKEVIRTUAL j/l/StringBuilder.append (I)Lj/l/StringBuilder;INVOKEVIRTUAL j/l/StringBuilder.toString ()Lj/l/String;INVOKEVIRTUAL j/io/PrintStream.println (Lj/l/String;)V

Page 81: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

What does bytecode look like?fun printSimpleSum() { val sum = v1() + v2() println("sum of values was $sum")}

INVOKESTATIC myapp/RunnerKt.v1 ()IINVOKESTATIC myapp/RunnerKt.v2 ()IIADDISTORE 1GETSTATIC j/l/System.out : Lj/io/PrintStream;NEW j/l/StringBuilderDUPINVOKESPECIAL j/l/StringBuilder.<init> ()VLDC "sum of values was "INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder;ILOAD 1INVOKEVIRTUAL j/l/StringBuilder.append (I)Lj/l/StringBuilder;INVOKEVIRTUAL j/l/StringBuilder.toString ()Lj/l/String;INVOKEVIRTUAL j/io/PrintStream.println (Lj/l/String;)V

Page 82: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

What does bytecode look like?fun printSimpleSum() { val sum = v1() + v2() println("sum of values was $sum")}

INVOKESTATIC myapp/RunnerKt.v1 ()IINVOKESTATIC myapp/RunnerKt.v2 ()IIADDISTORE 1GETSTATIC j/l/System.out : Lj/io/PrintStream;NEW j/l/StringBuilderDUPINVOKESPECIAL j/l/StringBuilder.<init> ()VLDC "sum of values was "INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder;ILOAD 1INVOKEVIRTUAL j/l/StringBuilder.append (I)Lj/l/StringBuilder;INVOKEVIRTUAL j/l/StringBuilder.toString ()Lj/l/String;INVOKEVIRTUAL j/io/PrintStream.println (Lj/l/String;)V

return value of v1()

Page 83: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

What does bytecode look like?fun printSimpleSum() { val sum = v1() + v2() println("sum of values was $sum")}

INVOKESTATIC myapp/RunnerKt.v1 ()IINVOKESTATIC myapp/RunnerKt.v2 ()IIADDISTORE 1GETSTATIC j/l/System.out : Lj/io/PrintStream;NEW j/l/StringBuilderDUPINVOKESPECIAL j/l/StringBuilder.<init> ()VLDC "sum of values was "INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder;ILOAD 1INVOKEVIRTUAL j/l/StringBuilder.append (I)Lj/l/StringBuilder;INVOKEVIRTUAL j/l/StringBuilder.toString ()Lj/l/String;INVOKEVIRTUAL j/io/PrintStream.println (Lj/l/String;)V

return value of v1()

return value of v2()

Page 84: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

What does bytecode look like?fun printSimpleSum() { val sum = v1() + v2() println("sum of values was $sum")}

INVOKESTATIC myapp/RunnerKt.v1 ()IINVOKESTATIC myapp/RunnerKt.v2 ()IIADDISTORE 1GETSTATIC j/l/System.out : Lj/io/PrintStream;NEW j/l/StringBuilderDUPINVOKESPECIAL j/l/StringBuilder.<init> ()VLDC "sum of values was "INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder;ILOAD 1INVOKEVIRTUAL j/l/StringBuilder.append (I)Lj/l/StringBuilder;INVOKEVIRTUAL j/l/StringBuilder.toString ()Lj/l/String;INVOKEVIRTUAL j/io/PrintStream.println (Lj/l/String;)V

return value of v1()

return value of v2()

v1() + v2()

Page 85: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

What does bytecode look like?fun printSimpleSum() { val sum = v1() + v2() println("sum of values was $sum")}

INVOKESTATIC myapp/RunnerKt.v1 ()IINVOKESTATIC myapp/RunnerKt.v2 ()IIADDISTORE 1GETSTATIC j/l/System.out : Lj/io/PrintStream;NEW j/l/StringBuilderDUPINVOKESPECIAL j/l/StringBuilder.<init> ()VLDC "sum of values was "INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder;ILOAD 1INVOKEVIRTUAL j/l/StringBuilder.append (I)Lj/l/StringBuilder;INVOKEVIRTUAL j/l/StringBuilder.toString ()Lj/l/String;INVOKEVIRTUAL j/io/PrintStream.println (Lj/l/String;)V

v1() + v2()

Page 86: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

What does bytecode look like?fun printSimpleSum() { val sum = v1() + v2() println("sum of values was $sum")}

INVOKESTATIC myapp/RunnerKt.v1 ()IINVOKESTATIC myapp/RunnerKt.v2 ()IIADDISTORE 1GETSTATIC j/l/System.out : Lj/io/PrintStream;NEW j/l/StringBuilderDUPINVOKESPECIAL j/l/StringBuilder.<init> ()VLDC "sum of values was "INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder;ILOAD 1INVOKEVIRTUAL j/l/StringBuilder.append (I)Lj/l/StringBuilder;INVOKEVIRTUAL j/l/StringBuilder.toString ()Lj/l/String;INVOKEVIRTUAL j/io/PrintStream.println (Lj/l/String;)V

stdout PrintStream

Page 87: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

What does bytecode look like?fun printSimpleSum() { val sum = v1() + v2() println("sum of values was $sum")}

INVOKESTATIC myapp/RunnerKt.v1 ()IINVOKESTATIC myapp/RunnerKt.v2 ()IIADDISTORE 1GETSTATIC j/l/System.out : Lj/io/PrintStream;NEW j/l/StringBuilderDUPINVOKESPECIAL j/l/StringBuilder.<init> ()VLDC "sum of values was "INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder;ILOAD 1INVOKEVIRTUAL j/l/StringBuilder.append (I)Lj/l/StringBuilder;INVOKEVIRTUAL j/l/StringBuilder.toString ()Lj/l/String;INVOKEVIRTUAL j/io/PrintStream.println (Lj/l/String;)V

stdout PrintStream

StringBuilder

Page 88: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

What does bytecode look like?fun printSimpleSum() { val sum = v1() + v2() println("sum of values was $sum")}

INVOKESTATIC myapp/RunnerKt.v1 ()IINVOKESTATIC myapp/RunnerKt.v2 ()IIADDISTORE 1GETSTATIC j/l/System.out : Lj/io/PrintStream;NEW j/l/StringBuilderDUPINVOKESPECIAL j/l/StringBuilder.<init> ()VLDC "sum of values was "INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder;ILOAD 1INVOKEVIRTUAL j/l/StringBuilder.append (I)Lj/l/StringBuilder;INVOKEVIRTUAL j/l/StringBuilder.toString ()Lj/l/String;INVOKEVIRTUAL j/io/PrintStream.println (Lj/l/String;)V

stdout PrintStream

StringBuilder

StringBuilder

Page 89: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

What does bytecode look like?fun printSimpleSum() { val sum = v1() + v2() println("sum of values was $sum")}

INVOKESTATIC myapp/RunnerKt.v1 ()IINVOKESTATIC myapp/RunnerKt.v2 ()IIADDISTORE 1GETSTATIC j/l/System.out : Lj/io/PrintStream;NEW j/l/StringBuilderDUPINVOKESPECIAL j/l/StringBuilder.<init> ()VLDC "sum of values was "INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder;ILOAD 1INVOKEVIRTUAL j/l/StringBuilder.append (I)Lj/l/StringBuilder;INVOKEVIRTUAL j/l/StringBuilder.toString ()Lj/l/String;INVOKEVIRTUAL j/io/PrintStream.println (Lj/l/String;)V

stdout PrintStream

StringBuilder

StringBuilder

Page 90: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

What does bytecode look like?fun printSimpleSum() { val sum = v1() + v2() println("sum of values was $sum")}

INVOKESTATIC myapp/RunnerKt.v1 ()IINVOKESTATIC myapp/RunnerKt.v2 ()IIADDISTORE 1GETSTATIC j/l/System.out : Lj/io/PrintStream;NEW j/l/StringBuilderDUPINVOKESPECIAL j/l/StringBuilder.<init> ()VLDC "sum of values was "INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder;ILOAD 1INVOKEVIRTUAL j/l/StringBuilder.append (I)Lj/l/StringBuilder;INVOKEVIRTUAL j/l/StringBuilder.toString ()Lj/l/String;INVOKEVIRTUAL j/io/PrintStream.println (Lj/l/String;)V

stdout PrintStream

StringBuilder

"sum of values was "

Page 91: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

What does bytecode look like?fun printSimpleSum() { val sum = v1() + v2() println("sum of values was $sum")}

INVOKESTATIC myapp/RunnerKt.v1 ()IINVOKESTATIC myapp/RunnerKt.v2 ()IIADDISTORE 1GETSTATIC j/l/System.out : Lj/io/PrintStream;NEW j/l/StringBuilderDUPINVOKESPECIAL j/l/StringBuilder.<init> ()VLDC "sum of values was "INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder;ILOAD 1INVOKEVIRTUAL j/l/StringBuilder.append (I)Lj/l/StringBuilder;INVOKEVIRTUAL j/l/StringBuilder.toString ()Lj/l/String;INVOKEVIRTUAL j/io/PrintStream.println (Lj/l/String;)V

stdout PrintStream

StringBuilder

"sum of values was "

Page 92: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

What does bytecode look like?fun printSimpleSum() { val sum = v1() + v2() println("sum of values was $sum")}

INVOKESTATIC myapp/RunnerKt.v1 ()IINVOKESTATIC myapp/RunnerKt.v2 ()IIADDISTORE 1GETSTATIC j/l/System.out : Lj/io/PrintStream;NEW j/l/StringBuilderDUPINVOKESPECIAL j/l/StringBuilder.<init> ()VLDC "sum of values was "INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder;ILOAD 1INVOKEVIRTUAL j/l/StringBuilder.append (I)Lj/l/StringBuilder;INVOKEVIRTUAL j/l/StringBuilder.toString ()Lj/l/String;INVOKEVIRTUAL j/io/PrintStream.println (Lj/l/String;)V

stdout PrintStream

StringBuilder

Page 93: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

What does bytecode look like?fun printSimpleSum() { val sum = v1() + v2() println("sum of values was $sum")}

INVOKESTATIC myapp/RunnerKt.v1 ()IINVOKESTATIC myapp/RunnerKt.v2 ()IIADDISTORE 1GETSTATIC j/l/System.out : Lj/io/PrintStream;NEW j/l/StringBuilderDUPINVOKESPECIAL j/l/StringBuilder.<init> ()VLDC "sum of values was "INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder;ILOAD 1INVOKEVIRTUAL j/l/StringBuilder.append (I)Lj/l/StringBuilder;INVOKEVIRTUAL j/l/StringBuilder.toString ()Lj/l/String;INVOKEVIRTUAL j/io/PrintStream.println (Lj/l/String;)V

stdout PrintStream

StringBuilder

v1() + v2()

Page 94: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

What does bytecode look like?fun printSimpleSum() { val sum = v1() + v2() println("sum of values was $sum")}

INVOKESTATIC myapp/RunnerKt.v1 ()IINVOKESTATIC myapp/RunnerKt.v2 ()IIADDISTORE 1GETSTATIC j/l/System.out : Lj/io/PrintStream;NEW j/l/StringBuilderDUPINVOKESPECIAL j/l/StringBuilder.<init> ()VLDC "sum of values was "INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder;ILOAD 1INVOKEVIRTUAL j/l/StringBuilder.append (I)Lj/l/StringBuilder;INVOKEVIRTUAL j/l/StringBuilder.toString ()Lj/l/String;INVOKEVIRTUAL j/io/PrintStream.println (Lj/l/String;)V

stdout PrintStream

StringBuilder

v1() + v2()

Page 95: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

What does bytecode look like?fun printSimpleSum() { val sum = v1() + v2() println("sum of values was $sum")}

INVOKESTATIC myapp/RunnerKt.v1 ()IINVOKESTATIC myapp/RunnerKt.v2 ()IIADDISTORE 1GETSTATIC j/l/System.out : Lj/io/PrintStream;NEW j/l/StringBuilderDUPINVOKESPECIAL j/l/StringBuilder.<init> ()VLDC "sum of values was "INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder;ILOAD 1INVOKEVIRTUAL j/l/StringBuilder.append (I)Lj/l/StringBuilder;INVOKEVIRTUAL j/l/StringBuilder.toString ()Lj/l/String;INVOKEVIRTUAL j/io/PrintStream.println (Lj/l/String;)V

stdout PrintStream

StringBuilder

Page 96: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

What does bytecode look like?fun printSimpleSum() { val sum = v1() + v2() println("sum of values was $sum")}

INVOKESTATIC myapp/RunnerKt.v1 ()IINVOKESTATIC myapp/RunnerKt.v2 ()IIADDISTORE 1GETSTATIC j/l/System.out : Lj/io/PrintStream;NEW j/l/StringBuilderDUPINVOKESPECIAL j/l/StringBuilder.<init> ()VLDC "sum of values was "INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder;ILOAD 1INVOKEVIRTUAL j/l/StringBuilder.append (I)Lj/l/StringBuilder;INVOKEVIRTUAL j/l/StringBuilder.toString ()Lj/l/String;INVOKEVIRTUAL j/io/PrintStream.println (Lj/l/String;)V

stdout PrintStream

StringBuilderString

Page 97: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

What does bytecode look like?fun printSimpleSum() { val sum = v1() + v2() println("sum of values was $sum")}

INVOKESTATIC myapp/RunnerKt.v1 ()IINVOKESTATIC myapp/RunnerKt.v2 ()IIADDISTORE 1GETSTATIC j/l/System.out : Lj/io/PrintStream;NEW j/l/StringBuilderDUPINVOKESPECIAL j/l/StringBuilder.<init> ()VLDC "sum of values was "INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder;ILOAD 1INVOKEVIRTUAL j/l/StringBuilder.append (I)Lj/l/StringBuilder;INVOKEVIRTUAL j/l/StringBuilder.toString ()Lj/l/String;INVOKEVIRTUAL j/io/PrintStream.println (Lj/l/String;)V

stdout PrintStream

String

Page 98: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

@DebugLog fun prime(n: Int): Long = primeNumberSequence.take(n).last()

fun prime(n: Int): Long { println("⇢ prime(n=$n)") val startTime = System.currentTimeMillis() val result = primeNumberSequence.take(n).last() val timeToRun = System.currentTimeMillis() - startTime println("⇠ prime [ran in $timeToRun ms]") return result}

Remember the goal

Page 99: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

Back to our MethodVisitor!

Page 100: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

return object : MethodVisitor(Opcodes.ASM5, original) { override fun visitCode() { super.visitCode() InstructionAdapter(this).apply { TODO("on method entry") } }4 override fun visitInsn(opcode: Int) { when (opcode) { RETURN , ARETURN, IRETURN -> { InstructionAdapter(this).apply { TODO("on method exit") } } } super.visitInsn(opcode) }}3

Page 101: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

return object : MethodVisitor(Opcodes.ASM5, original) { override fun visitCode() { super.visitCode() InstructionAdapter(this).apply { TODO("on method entry") } } override fun visitInsn(opcode: Int) { when (opcode) { RETURN , ARETURN, IRETURN -> { InstructionAdapter(this).apply { TODO("on method exit") } } } super.visitInsn(opcode) }}

Page 102: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).apply { TODO("on method entry") }1

Page 103: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).apply { getstatic("j/l/System", "out", "Ljava/io/PrintStream;")}1

stdout PrintStream

Page 104: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).apply { getstatic("j/l/System", "out", "Ljava/io/PrintStream;")anew("j/l/StringBuilder")}1

stdout PrintStream

StringBuilder

Page 105: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).apply { getstatic("j/l/System", "out", "Ljava/io/PrintStream;")anew("j/l/StringBuilder")dup()}1

stdout PrintStream

StringBuilder

StringBuilder

Page 106: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).apply { getstatic("j/l/System", "out", "Ljava/io/PrintStream;")anew("j/l/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")}1

stdout PrintStream

StringBuilder

StringBuilder

Page 107: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).apply { getstatic("j/l/System", "out", "Ljava/io/PrintStream;")anew("j/l/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇢ ${function.name}(")}1

stdout PrintStream

StringBuilder

"⇢ prime("

Page 108: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).apply { getstatic("j/l/System", "out", "Ljava/io/PrintStream;")anew("j/l/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇢ ${function.name}(")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/Object;)Lj/l/StringBuilder;")}1

stdout PrintStream

StringBuilder

"⇢ prime("

Page 109: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).apply { getstatic("j/l/System", "out", "Ljava/io/PrintStream;")anew("j/l/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇢ ${function.name}(")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/Object;)Lj/l/StringBuilder;")}1

stdout PrintStream

StringBuilder

Page 110: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).apply { getstatic("j/l/System", "out", "Ljava/io/PrintStream;")anew("j/l/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇢ ${function.name}(")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/Object;)Lj/l/StringBuilder;")}1

stdout PrintStream

StringBuilder

Page 111: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).apply { getstatic("j/l/System", "out", "Ljava/io/PrintStream;")anew("j/l/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇢ ${function.name}(")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/Object;)Lj/l/StringBuilder;")function.valueParameters.forEachIndexed { i, param -> visitLdcInsn(" ${param.name}=") invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/SB;") visitVarInsn(ALOAD, i + 1) invokevirtual("j/l/StringBuilder", "append", "(Lj/l/Object;)Lj/l/SB;")}}1

stdout PrintStream

StringBuilder

Page 112: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).apply { getstatic("j/l/System", "out", "Ljava/io/PrintStream;")anew("j/l/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇢ ${function.name}(")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/Object;)Lj/l/StringBuilder;")function.valueParameters.forEachIndexed { i, param -> visitLdcInsn(" ${param.name}=") invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/SB;") visitVarInsn(ALOAD, i + 1) invokevirtual("j/l/StringBuilder", "append", "(Lj/l/Object;)Lj/l/SB;")}}1

stdout PrintStream

StringBuilder

" n="

Page 113: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).apply { getstatic("j/l/System", "out", "Ljava/io/PrintStream;")anew("j/l/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇢ ${function.name}(")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/Object;)Lj/l/StringBuilder;")function.valueParameters.forEachIndexed { i, param -> visitLdcInsn(" ${param.name}=") invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/SB;") visitVarInsn(ALOAD, i + 1) invokevirtual("j/l/StringBuilder", "append", "(Lj/l/Object;)Lj/l/SB;")}}1

stdout PrintStream

StringBuilder

" n="

Page 114: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).apply { getstatic("j/l/System", "out", "Ljava/io/PrintStream;")anew("j/l/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇢ ${function.name}(")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/Object;)Lj/l/StringBuilder;")function.valueParameters.forEachIndexed { i, param -> visitLdcInsn(" ${param.name}=") invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/SB;") visitVarInsn(ALOAD, i + 1) invokevirtual("j/l/StringBuilder", "append", "(Lj/l/Object;)Lj/l/SB;")}}1

stdout PrintStream

StringBuilder

Page 115: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).apply { getstatic("j/l/System", "out", "Ljava/io/PrintStream;")anew("j/l/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇢ ${function.name}(")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/Object;)Lj/l/StringBuilder;")function.valueParameters.forEachIndexed { i, param -> visitLdcInsn(" ${param.name}=") invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/SB;") visitVarInsn(ALOAD, i + 1) invokevirtual("j/l/StringBuilder", "append", "(Lj/l/Object;)Lj/l/SB;")}}1

stdout PrintStream

StringBuilder

value of n

Page 116: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).apply { getstatic("j/l/System", "out", "Ljava/io/PrintStream;")anew("j/l/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇢ ${function.name}(")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/Object;)Lj/l/StringBuilder;")function.valueParameters.forEachIndexed { i, param -> visitLdcInsn(" ${param.name}=") invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/SB;") visitVarInsn(ALOAD, i + 1) invokevirtual("j/l/StringBuilder", "append", "(Lj/l/Object;)Lj/l/SB;")}}1

stdout PrintStream

StringBuilder

value of n

Page 117: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).apply { getstatic("j/l/System", "out", "Ljava/io/PrintStream;")anew("j/l/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇢ ${function.name}(")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/Object;)Lj/l/StringBuilder;")function.valueParameters.forEachIndexed { i, param -> visitLdcInsn(" ${param.name}=") invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/SB;") visitVarInsn(ALOAD, i + 1) invokevirtual("j/l/StringBuilder", "append", "(Lj/l/Object;)Lj/l/SB;")}}1

stdout PrintStream

StringBuilder

Page 118: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).apply { getstatic("j/l/System", "out", "Ljava/io/PrintStream;")anew("j/l/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇢ ${function.name}(")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/Object;)Lj/l/StringBuilder;")function.valueParameters.forEachIndexed { i, param -> visitLdcInsn(" ${param.name}=") invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/SB;") visitVarInsn(ALOAD, i + 1) invokevirtual("j/l/StringBuilder", "append", "(Lj/l/Object;)Lj/l/SB;")}R}1

stdout PrintStream

StringBuilder

Page 119: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).apply { getstatic("j/l/System", "out", "Ljava/io/PrintStream;")anew("j/l/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇢ ${function.name}(")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/Object;)Lj/l/StringBuilder;")function.valueParameters.forEachIndexed { i, param -> visitLdcInsn(" ${param.name}=") invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/SB;") visitVarInsn(ALOAD, i + 1) invokevirtual("j/l/StringBuilder", "append", "(Lj/l/Object;)Lj/l/SB;")}Rinvokevirtual("j/l/StringBuilder", "toString", "()Lj/l/String;")}1

stdout PrintStream

StringBuilderString

Page 120: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).apply { getstatic("j/l/System", "out", "Ljava/io/PrintStream;")anew("j/l/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇢ ${function.name}(")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/Object;)Lj/l/StringBuilder;")function.valueParameters.forEachIndexed { i, param -> visitLdcInsn(" ${param.name}=") invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/SB;") visitVarInsn(ALOAD, i + 1) invokevirtual("j/l/StringBuilder", "append", "(Lj/l/Object;)Lj/l/SB;")}Rinvokevirtual("j/l/StringBuilder", "toString", "()Lj/l/String;")invokevirtual("j/io/PrintStream", "println", "(Lj/l/String;)V")}1

stdout PrintStream

String

Page 121: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).apply {L getstatic("j/l/System", "out", "Ljava/io/PrintStream;")anew("j/l/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇢ ${function.name}(")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/Object;)Lj/l/StringBuilder;")function.valueParameters.forEachIndexed { i, param -> visitLdcInsn(" ${param.name}=") invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/SB;") visitVarInsn(ALOAD, i + 1) invokevirtual("j/l/StringBuilder", "append", "(Lj/l/Object;)Lj/l/SB;")}Rinvokevirtual("j/l/StringBuilder", "toString", "()Lj/l/String;")invokevirtual("j/io/PrintStream", "println", "(Lj/l/String;)V")}1

Page 122: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).apply {L// ... method-trace-printing code}1

Page 123: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).apply {L// ... method-trace-printing codeinvokestatic("j/l/System", "currentTimeMillis", "()J")}1

currentTimeMillis

Page 124: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).apply {L// ... method-trace-printing codeinvokestatic("j/l/System", "currentTimeMillis", "()J")store(9001, LONG_TYPE)}1

currentTimeMillis

Page 125: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).applyL{L// ... method-trace-printing codeinvokestatic("j/l/System", "currentTimeMillis", "()J")store(9001, LONG_TYPE)}1

Page 126: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).applyL{L// ... method-trace-printing code// ... timestamp-storing code}W

Page 127: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

return object : MethodVisitor(Opcodes.ASM5, original) { override fun visitCode() { super.visitCode() InstructionAdapter(this).applyL{L // ... method-trace-printing code // ... timestamp-storing code }W }4 override fun visitInsn(opcode: Int) { when (opcode) { RETURN , ARETURN, IRETURN -> { InstructionAdapter(this).apply { TODO("on method exit") } } } super.visitInsn(opcode) }}3

Page 128: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

return object : MethodVisitor(Opcodes.ASM5, original) { override fun visitCode() { super.visitCode() InstructionAdapter(this).applyL{L // ... method-trace-printing code // ... timestamp-storing code }W }4 override fun visitInsn(opcode: Int) { when (opcode) { RETURN , ARETURN, IRETURN -> { InstructionAdapter(this).apply { TODO("on method exit") } } } super.visitInsn(opcode) }}3

Page 129: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

return object : MethodVisitor(Opcodes.ASM5, original) { override fun visitCode() { super.visitCode() InstructionAdapter(this).apply { // ... method-trace-printing code // ... timestamp-storing code }W }4 override fun visitInsn(opcode: Int) { when (opcode) { RETURN , ARETURN, IRETURN -> { InstructionAdapter(this).applyM{MTODO("on method exit")M}M } } super.visitInsn(opcode) }}3

Page 130: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).applyM{MTODO("on method exit")}M

Page 131: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).applyM{Mgetstatic("j/l/System", "out", "Lj/io/PrintStream;")}M

stdout PrintStream

Page 132: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).applyM{Mgetstatic("j/l/System", "out", "Lj/io/PrintStream;")anew("java/lang/StringBuilder")}M

stdout PrintStream

StringBuilder

Page 133: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).applyM{Mgetstatic("j/l/System", "out", "Lj/io/PrintStream;")anew("java/lang/StringBuilder")dup()}M

stdout PrintStream

StringBuilder

StringBuilder

Page 134: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).applyM{Mgetstatic("j/l/System", "out", "Lj/io/PrintStream;")anew("java/lang/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")}M

stdout PrintStream

StringBuilder

StringBuilder

Page 135: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).applyM{Mgetstatic("j/l/System", "out", "Lj/io/PrintStream;")anew("java/lang/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇠ ${function.name} [ran in ")}M

stdout PrintStream

StringBuilder

"⇠ prime [ran in "

Page 136: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).applyM{Mgetstatic("j/l/System", "out", "Lj/io/PrintStream;")anew("java/lang/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇠ ${function.name} [ran in ")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/StringBuilder;")}M

stdout PrintStream

StringBuilder

"⇠ prime [ran in "

Page 137: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).applyM{Mgetstatic("j/l/System", "out", "Lj/io/PrintStream;")anew("java/lang/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇠ ${function.name} [ran in ")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/StringBuilder;")}M

stdout PrintStream

StringBuilder

Page 138: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).applyM{Mgetstatic("j/l/System", "out", "Lj/io/PrintStream;")anew("java/lang/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇠ ${function.name} [ran in ")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/StringBuilder;")invokestatic("j/l/System", "currentTimeMillis", "()J")}M stdout PrintStream

StringBuilder

current timestamp

Page 139: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).applyM{Mgetstatic("j/l/System", "out", "Lj/io/PrintStream;")anew("java/lang/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇠ ${function.name} [ran in ")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/StringBuilder;")invokestatic("j/l/System", "currentTimeMillis", "()J")load(9001, LONG_TYPE)}M

stdout PrintStream

StringBuilder

current timestamp

start timestamp

Page 140: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).applyM{Mgetstatic("j/l/System", "out", "Lj/io/PrintStream;")anew("java/lang/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇠ ${function.name} [ran in ")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/StringBuilder;")invokestatic("j/l/System", "currentTimeMillis", "()J")load(9001, LONG_TYPE)sub(LONG_TYPE)}M

stdout PrintStream

StringBuilder

current timestamp

start timestamp

Page 141: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).applyM{Mgetstatic("j/l/System", "out", "Lj/io/PrintStream;")anew("java/lang/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇠ ${function.name} [ran in ")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/StringBuilder;")invokestatic("j/l/System", "currentTimeMillis", "()J")load(9001, LONG_TYPE)sub(LONG_TYPE)}M

stdout PrintStream

StringBuilder

(current - start) time

Page 142: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).applyM{Mgetstatic("j/l/System", "out", "Lj/io/PrintStream;")anew("java/lang/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇠ ${function.name} [ran in ")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/StringBuilder;")invokestatic("j/l/System", "currentTimeMillis", "()J")load(9001, LONG_TYPE)sub(LONG_TYPE)invokevirtual("j/l/StringBuilder", "append", "(J)Lj/l/StringBuilder;")}M

stdout PrintStream

StringBuilder

(current - start) time

Page 143: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).applyM{Mgetstatic("j/l/System", "out", "Lj/io/PrintStream;")anew("java/lang/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇠ ${function.name} [ran in ")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/StringBuilder;")invokestatic("j/l/System", "currentTimeMillis", "()J")load(9001, LONG_TYPE)sub(LONG_TYPE)invokevirtual("j/l/StringBuilder", "append", "(J)Lj/l/StringBuilder;")}M

stdout PrintStream

StringBuilder

Page 144: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).applyM{Mgetstatic("j/l/System", "out", "Lj/io/PrintStream;")anew("java/lang/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇠ ${function.name} [ran in ")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/StringBuilder;")invokestatic("j/l/System", "currentTimeMillis", "()J")load(9001, LONG_TYPE)sub(LONG_TYPE)invokevirtual("j/l/StringBuilder", "append", "(J)Lj/l/StringBuilder;")visitLdcInsn(" ms]")}M

stdout PrintStream

StringBuilder

" ms]"

Page 145: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).applyM{Mgetstatic("j/l/System", "out", "Lj/io/PrintStream;")anew("java/lang/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇠ ${function.name} [ran in ")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/StringBuilder;")invokestatic("j/l/System", "currentTimeMillis", "()J")load(9001, LONG_TYPE)sub(LONG_TYPE)invokevirtual("j/l/StringBuilder", "append", "(J)Lj/l/StringBuilder;")visitLdcInsn(" ms]")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/SB;")}M

stdout PrintStream

StringBuilder

" ms]"

Page 146: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).applyM{Mgetstatic("j/l/System", "out", "Lj/io/PrintStream;")anew("java/lang/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇠ ${function.name} [ran in ")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/StringBuilder;")invokestatic("j/l/System", "currentTimeMillis", "()J")load(9001, LONG_TYPE)sub(LONG_TYPE)invokevirtual("j/l/StringBuilder", "append", "(J)Lj/l/StringBuilder;")visitLdcInsn(" ms]")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/SB;")}M

stdout PrintStream

StringBuilder

Page 147: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).applyM{Mgetstatic("j/l/System", "out", "Lj/io/PrintStream;")anew("java/lang/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇠ ${function.name} [ran in ")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/StringBuilder;")invokestatic("j/l/System", "currentTimeMillis", "()J")load(9001, LONG_TYPE)sub(LONG_TYPE)invokevirtual("j/l/StringBuilder", "append", "(J)Lj/l/StringBuilder;")visitLdcInsn(" ms]")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/SB;")invokevirtual("j/l/StringBuilder", "toString", "()Lj/l/String;")}M

stdout PrintStream

StringBuilderString

Page 148: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).applyM{Mgetstatic("j/l/System", "out", "Lj/io/PrintStream;")anew("java/lang/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇠ ${function.name} [ran in ")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/StringBuilder;")invokestatic("j/l/System", "currentTimeMillis", "()J")load(9001, LONG_TYPE)sub(LONG_TYPE)invokevirtual("j/l/StringBuilder", "append", "(J)Lj/l/StringBuilder;")visitLdcInsn(" ms]")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/SB;")invokevirtual("j/l/StringBuilder", "toString", "()Lj/l/String;")invokevirtual("j/io/PrintStream", "println", "(Lj/l/String;)V")}M

stdout PrintStream

String

Page 149: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).applyM{Mgetstatic("j/l/System", "out", "Lj/io/PrintStream;")anew("java/lang/StringBuilder")dup()invokespecial("j/l/StringBuilder", "<init>", "()V")visitLdcInsn("⇠ ${function.name} [ran in ")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/StringBuilder;")invokestatic("j/l/System", "currentTimeMillis", "()J")load(9001, LONG_TYPE)sub(LONG_TYPE)invokevirtual("j/l/StringBuilder", "append", "(J)Lj/l/StringBuilder;")visitLdcInsn(" ms]")invokevirtual("j/l/StringBuilder", "append", "(Lj/l/String;)Lj/l/SB;")invokevirtual("j/l/StringBuilder", "toString", "()Lj/l/String;")invokevirtual("j/io/PrintStream", "println", "(Lj/l/String;)V")}M

Page 150: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

InstructionAdapter(this).applyM{M//G...Gbenchmark-printingGcode}M

Page 151: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

return object : MethodVisitor(Opcodes.ASM5, original) { override fun visitCode() { super.visitCode() InstructionAdapter(this).applyL{L // ... method-trace-printing code // ... timestamp-storing code }W }4 override fun visitInsn(opcode: Int) { when (opcode) { RETURN , ARETURN, IRETURN -> { InstructionAdapter(this).applyM{M//G...Gbenchmark-printingGcode }M } } super.visitInsn(opcode) }}3

Page 152: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt

return object : MethodVisitor(Opcodes.ASM5, original) { override fun visitCode() { super.visitCode() InstructionAdapter(this).applyL{L // ... method-trace-printing code // ... timestamp-storing code }W }4 override fun visitInsn(opcode: Int) { when (opcode) { RETURN , ARETURN, IRETURN -> { InstructionAdapter(this).applyM{M//G...Gbenchmark-printingGcode }M } } super.visitInsn(opcode) }}3

Page 153: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

Done! Demo time!!!

Page 154: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

Resources• https://github.com/JetBrains/kotlin/tree/master/plugins. Specifically:

• noarg: One of the simplest ones

• android-extensions: Good prior art for many of the extension types

• kotlin-serialization: Newest one, documented well, generates LLVM

• https://github.com/JetBrains/kotlin/tree/master/libraries/tools

• All look pretty similar; noarg and allopen are the simplest to grok

Page 155: Writing Your First Kotlin Compiler Plugin · Are these basically annotation processors? • Annotation Processors: • Your code runs at compile-time • Public, documented API •

Thank you!

Kevin Most