Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Writing Your First Kotlin Compiler Plugin

Kevin Most
October 04, 2018

Writing Your First Kotlin Compiler Plugin

The Kotlin compiler plugin API gives us powerful features like Parcelize and the synthetic view accessor methods in kotlinx.android. These features could not be built using similar, but more limited, mechanisms, such as annotation processing.

The Kotlin compiler plugin API is not currently well-documented, but that doesn't mean that it can't be explored! In this talk, we will start from scratch and show how we can build a compiler plugin and deploy an artifact to a public location, so that we can build plugins that can be applied as easily as the official compiler plugins.

This version was presented at KotlinConf 2018: https://kotlinconf.com/schedule/#date=4-october&session=41262

Kevin Most

October 04, 2018
Tweet

More Decks by Kevin Most

Other Decks in Programming

Transcript

  1. Are these basically annotation processors? • Annotation Processors: • Your

    code runs at compile-time • Public, documented API
  2. Are these basically annotation processors? • Annotation Processors: • Your

    code runs at compile-time • Public, documented API • Emit Java source code
  3. 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
  4. 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
  5. 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:
  6. 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
  7. 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
  8. 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)
  9. 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
  10. 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
  11. Why write compiler plugins? • Incredibly powerful API; you can

    modify function/class internals • Enables you to solve new classes of metaprogramming problems
  12. 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
  13. Why NOT write compiler plugins? • Annotation processors are much

    easier to write (if you only care about JVM)
  14. 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:
  15. 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)
  16. 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
  17. 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
  18. Examples of compiler plugins • allopen: Modifies annotated class to

    be open • noarg: Modifies annotated class to have a zero-argument constructor
  19. 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
  20. 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
  21. 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)
  22. • All existing compiler plugins are 1st party (github.com/JetBrains/kotlin) •

    plugins/{name}/ ... for the actual plugin business logic Examples of compiler plugins
  23. • 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 Examples of compiler plugins
  24. • 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
  25. Plugin Architecture Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension • Gradle

    API (totally unrelated to Kotlin) • Provides an entry point from a build.gradle script
  26. 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
  27. Plugin Architecture Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension • The

    interface between the Gradle and Kotlin APIs • Read Gradle extension options
  28. Plugin Architecture Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension • The

    interface between the Gradle and Kotlin APIs • Read Gradle extension options • Write out Kotlin SubpluginOptions
  29. 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)
  30. 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
  31. Plugin Architecture Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension • Reads

    kotlinc -Xplugin args • Subplugin options actually get passed through this pipeline
  32. Plugin Architecture Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension • Reads

    kotlinc -Xplugin args • Subplugin options actually get passed through this pipeline • Write CompilerConfigurationKeys
  33. Plugin Architecture Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension • Generates

    code (finally!) • Multiple types of extensions, such as: • ExpressionCodegenExtension
  34. Plugin Architecture Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension • Generates

    code (finally!) • Multiple types of extensions, such as: • ExpressionCodegenExtension • ClassBuilderInterceptorExtension
  35. Plugin Architecture Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension • Generates

    code (finally!) • Multiple types of extensions, such as: • ExpressionCodegenExtension • ClassBuilderInterceptorExtension • StorageComponentContainerContributor
  36. Plugin Architecture Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension • Generates

    code (finally!) • Multiple types of extensions, such as: • ExpressionCodegenExtension • ClassBuilderInterceptorExtension • StorageComponentContainerContributor • IrGenerationExtension ( !!)
  37. 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!!)
  38. 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
  39. 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
  40. 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:
  41. 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
  42. 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
  43. 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
  44. 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
  45. 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
  46. @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
  47. 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" }
  48. 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
  49. 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
  50. 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
  51. 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" }
  52. 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" }
  53. 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() }
  54. @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() } }
  55. @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() } }
  56. @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() } }
  57. @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() } }
  58. @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() } }
  59. @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() } }
  60. @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() } }
  61. @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
  62. @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
  63. @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() } }
  64. @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
  65. @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
  66. @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
  67. @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
  68. @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
  69. 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" }
  70. 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" }
  71. 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" }
  72. 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" }
  73. @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() } }
  74. @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() } }
  75. @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() } }
  76. @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() } }
  77. @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
  78. @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
  79. @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() } }
  80. @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() } }
  81. @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) { } }
  82. @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
  83. @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
  84. @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
  85. @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") ) ) } }
  86. @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") ) ) } }
  87. 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
  88. 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
  89. 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
  90. 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
  91. 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
  92. 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
  93. 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
  94. 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
  95. What now? • You write bytecode • Uses the ObjectWeb

    ASM API • Neither related to ASM (assembly) or Web ASM (wasm) in any way
  96. 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
  97. 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
  98. 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
  99. 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
  100. What does bytecode look like? fun printSimpleSum() { val sum

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

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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
  102. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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
  103. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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
  104. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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()
  105. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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()
  106. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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()
  107. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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()
  108. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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
  109. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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()
  110. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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()
  111. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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
  112. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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
  113. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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
  114. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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
  115. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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
  116. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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
  117. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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
  118. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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
  119. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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
  120. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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
  121. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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 "
  122. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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 "
  123. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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
  124. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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
  125. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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
  126. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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
  127. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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()
  128. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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()
  129. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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
  130. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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
  131. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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
  132. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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
  133. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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
  134. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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
  135. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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
  136. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") } INVOKESTATIC myapp/RunnerKt.v1 ()I INVOKESTATIC myapp/RunnerKt.v2 ()I IADD ISTORE 1 GETSTATIC j/l/System.out : Lj/io/PrintStream; NEW j/l/StringBuilder DUP INVOKESPECIAL j/l/StringBuilder.<init> ()V LDC "sum of values was " INVOKEVIRTUAL j/l/StringBuilder.append (Lj/l/String;)Lj/l/StringBuilder; ILOAD 1 INVOKEVIRTUAL 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
  137. @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
  138. 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
  139. 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) } }
  140. 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("
  141. 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
  142. 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
  143. 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
  144. 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
  145. 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="
  146. 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="
  147. 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
  148. 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
  149. 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
  150. 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
  151. 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
  152. 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
  153. 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
  154. 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
  155. 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
  156. 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
  157. 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 invokevirtual("j/l/StringBuilder", "toString", "()Lj/l/String;") }1 stdout PrintStream StringBuilder
  158. 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 invokevirtual("j/l/StringBuilder", "toString", "()Lj/l/String;") }1 stdout PrintStream
  159. 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 invokevirtual("j/l/StringBuilder", "toString", "()Lj/l/String;") }1 stdout PrintStream String
  160. 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 invokevirtual("j/l/StringBuilder", "toString", "()Lj/l/String;") invokevirtual("j/io/PrintStream", "println", "(Lj/l/String;)V") }1 stdout PrintStream String
  161. 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 invokevirtual("j/l/StringBuilder", "toString", "()Lj/l/String;") invokevirtual("j/io/PrintStream", "println", "(Lj/l/String;)V") }1
  162. 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;") }R invokevirtual("j/l/StringBuilder", "toString", "()Lj/l/String;") invokevirtual("j/io/PrintStream", "println", "(Lj/l/String;)V") }1
  163. 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
  164. 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
  165. 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
  166. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("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 "
  167. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("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
  168. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("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
  169. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("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
  170. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("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
  171. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("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
  172. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("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
  173. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("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
  174. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("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
  175. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("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
  176. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("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
  177. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("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
  178. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("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
  179. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("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
  180. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("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
  181. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("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]"
  182. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("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]"
  183. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("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
  184. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("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
  185. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("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
  186. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("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 StringBuilder
  187. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("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
  188. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("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 String
  189. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("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
  190. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("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
  191. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("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
  192. 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
  193. 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
  194. 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