Writing Your First Kotlin Compiler Plugin

7789a17312fed04fc16312c75386faf9?s=47 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

7789a17312fed04fc16312c75386faf9?s=128

Kevin Most

October 04, 2018
Tweet

Transcript

  1. Writing Your First Kotlin Compiler Plugin Kevin Most

  2. A brief intro

  3. Are these basically annotation processors?

  4. Are these basically annotation processors? • Annotation Processors:

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

    code runs at compile-time
  6. Are these basically annotation processors? • Annotation Processors: • Your

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

    code runs at compile-time • Public, documented API • Emit Java source code
  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
  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
  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:
  11. 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
  12. 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
  13. 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)
  14. 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
  15. 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
  16. Why write compiler plugins?

  17. Why write compiler plugins? • Incredibly powerful API; you can

    modify function/class internals
  18. Why write compiler plugins? • Incredibly powerful API; you can

    modify function/class internals • Enables you to solve new classes of metaprogramming problems
  19. 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
  20. Why NOT write compiler plugins?

  21. Why NOT write compiler plugins? • Annotation processors are much

    easier to write (if you only care about JVM)
  22. 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:
  23. 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)
  24. 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
  25. 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
  26. Examples of compiler plugins

  27. Examples of compiler plugins • allopen: Modifies annotated class to

    be open
  28. Examples of compiler plugins • allopen: Modifies annotated class to

    be open • noarg: Modifies annotated class to have a zero-argument constructor
  29. 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
  30. 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
  31. 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)
  32. Examples of compiler plugins

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

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

    plugins/{name}/ ... for the actual plugin business logic Examples of compiler plugins
  35. • 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
  36. • 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
  37. Plugin Architecture s Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension

  38. Plugin Architecture Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension

  39. Plugin Architecture Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension

  40. Plugin Architecture Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension • Gradle

    API (totally unrelated to Kotlin)
  41. Plugin Architecture Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension • Gradle

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

  44. Plugin Architecture Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension • The

    interface between the Gradle and Kotlin APIs
  45. Plugin Architecture Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension • The

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

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

  50. Plugin Architecture Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension • Reads

    kotlinc -Xplugin args
  51. Plugin Architecture Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension • Reads

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

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

  54. Plugin Architecture Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension • Read

    CompilerConfigurationKeys
  55. Plugin Architecture Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension • Read

    CompilerConfigurationKeys • Register Extensions
  56. Plugin Architecture Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension

  57. Plugin Architecture Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension • Generates

    code (finally!)
  58. Plugin Architecture Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension • Generates

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

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

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

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

    code (finally!) • Multiple types of extensions, such as: • ExpressionCodegenExtension • ClassBuilderInterceptorExtension • StorageComponentContainerContributor • IrGenerationExtension ( !!)
  63. 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!!)
  64. Let's build our own!

  65. Let's build our own!

  66. Let's build our own! • We'll build a compiler plugin

    that traces method calls
  67. 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
  68. 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
  69. 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:
  70. 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
  71. 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
  72. 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
  73. 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
  74. 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
  75. @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
  76. Let's build our own!

  77. Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension

  78. 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" }
  79. 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
  80. 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
  81. 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
  82. 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" }
  83. 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" }
  84. Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension

  85. 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() }
  86. Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension

  87. @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() } }
  88. @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() } }
  89. @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() } }
  90. @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() } }
  91. @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() } }
  92. @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() } }
  93. @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() } }
  94. @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
  95. @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
  96. @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() } }
  97. @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
  98. @AutoService(KotlinGradleSubplugin ::class) // don't forget! class DebugLogGradleSubplugin1:1KotlinGradleSubplugin<AbstractCompile>1{1 // other method

    impls override fun apply(project: Project, /* ... */): List<SubpluginOption>1{ TODO()Z }2 }1
  99. @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
  100. @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
  101. @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
  102. @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
  103. Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension

  104. 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" }
  105. 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" }
  106. 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" }
  107. 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" }
  108. Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension

  109. @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() } }
  110. @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() } }
  111. @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() } }
  112. @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() } }
  113. @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
  114. @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
  115. @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() } }
  116. @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() } }
  117. @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) { } }
  118. @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
  119. @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
  120. @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
  121. Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension

  122. @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") ) ) } }
  123. @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") ) ) } }
  124. Plugin Subplugin CommandLineProcessor ComponentRegistrar Extension Extension

  125. 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
  126. 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
  127. 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
  128. 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
  129. 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
  130. 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
  131. 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
  132. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt return object : MethodVisitor(Opcodes.ASM5, original) { }3

  133. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt return object : MethodVisitor(Opcodes.ASM5, original) { override fun visitCode()

    { super.visitCode() InstructionAdapter(this).apply { TODO("on method entry") } }4 }3
  134. 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
  135. What now?

  136. What now? • You write bytecode

  137. What now? • You write bytecode • Uses the ObjectWeb

    ASM API
  138. What now? • You write bytecode • Uses the ObjectWeb

    ASM API • Neither related to ASM (assembly) or Web ASM (wasm) in any way
  139. 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
  140. 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
  141. 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
  142. 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
  143. What does bytecode look like? fun printSimpleSum() { val sum

    = v1() + v2() println("sum of values was $sum") }
  144. 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
  145. 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
  146. 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
  147. 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()
  148. 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()
  149. 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()
  150. 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()
  151. 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
  152. 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()
  153. 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()
  154. 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
  155. 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
  156. 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
  157. 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
  158. 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
  159. 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
  160. 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
  161. 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
  162. 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
  163. 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
  164. 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 "
  165. 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 "
  166. 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
  167. 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
  168. 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
  169. 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
  170. 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()
  171. 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()
  172. 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
  173. 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
  174. 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
  175. 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
  176. 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
  177. 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
  178. 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
  179. 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
  180. @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
  181. Back to our MethodVisitor!

  182. 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
  183. 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) } }
  184. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).apply { TODO("on method entry") }1

  185. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).apply { getstatic("j/l/System", "out", "Ljava/io/PrintStream;") }1

  186. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).apply { getstatic("j/l/System", "out", "Ljava/io/PrintStream;") }1 stdout PrintStream

  187. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).apply { getstatic("j/l/System", "out", "Ljava/io/PrintStream;") anew("j/l/StringBuilder") }1 stdout PrintStream

  188. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).apply { getstatic("j/l/System", "out", "Ljava/io/PrintStream;") anew("j/l/StringBuilder") }1 stdout PrintStream

    StringBuilder
  189. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).apply { getstatic("j/l/System", "out", "Ljava/io/PrintStream;") anew("j/l/StringBuilder") dup() }1 stdout

    PrintStream StringBuilder
  190. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).apply { getstatic("j/l/System", "out", "Ljava/io/PrintStream;") anew("j/l/StringBuilder") dup() }1 stdout

    PrintStream StringBuilder StringBuilder
  191. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).apply { getstatic("j/l/System", "out", "Ljava/io/PrintStream;") anew("j/l/StringBuilder") dup() invokespecial("j/l/StringBuilder", "<init>",

    "()V") }1 stdout PrintStream StringBuilder StringBuilder
  192. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).apply { getstatic("j/l/System", "out", "Ljava/io/PrintStream;") anew("j/l/StringBuilder") dup() invokespecial("j/l/StringBuilder", "<init>",

    "()V") }1 stdout PrintStream StringBuilder
  193. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).apply { getstatic("j/l/System", "out", "Ljava/io/PrintStream;") anew("j/l/StringBuilder") dup() invokespecial("j/l/StringBuilder", "<init>",

    "()V") visitLdcInsn("⇢ ${function.name}(") }1 stdout PrintStream StringBuilder
  194. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).apply { getstatic("j/l/System", "out", "Ljava/io/PrintStream;") anew("j/l/StringBuilder") dup() invokespecial("j/l/StringBuilder", "<init>",

    "()V") visitLdcInsn("⇢ ${function.name}(") }1 stdout PrintStream StringBuilder "⇢ prime("
  195. 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("
  196. 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
  197. 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
  198. 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
  199. 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
  200. 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
  201. 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
  202. 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="
  203. 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="
  204. 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
  205. 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
  206. 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
  207. 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
  208. 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
  209. 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
  210. 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
  211. 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
  212. 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
  213. 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
  214. 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
  215. 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
  216. 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
  217. 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
  218. 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
  219. 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
  220. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).apply {L // ... method-trace-printing code }1

  221. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).apply {L // ... method-trace-printing code invokestatic("j/l/System", "currentTimeMillis", "()J")

    }1
  222. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).apply {L // ... method-trace-printing code invokestatic("j/l/System", "currentTimeMillis", "()J")

    }1 currentTimeMillis
  223. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).apply {L // ... method-trace-printing code invokestatic("j/l/System", "currentTimeMillis", "()J")

    store(9001, LONG_TYPE) }1 currentTimeMillis
  224. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).apply {L // ... method-trace-printing code invokestatic("j/l/System", "currentTimeMillis", "()J")

    store(9001, LONG_TYPE) }1
  225. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyL{L // ... method-trace-printing code invokestatic("j/l/System", "currentTimeMillis", "()J") store(9001,

    LONG_TYPE) }1
  226. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyL{L // ... method-trace-printing code // ... timestamp-storing code

    }W
  227. 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
  228. 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
  229. 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
  230. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M TODO("on method exit") }M

  231. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("j/l/System", "out", "Lj/io/PrintStream;") }M

  232. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("j/l/System", "out", "Lj/io/PrintStream;") }M stdout PrintStream

  233. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("j/l/System", "out", "Lj/io/PrintStream;") anew("java/lang/StringBuilder") }M stdout PrintStream

  234. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M getstatic("j/l/System", "out", "Lj/io/PrintStream;") anew("java/lang/StringBuilder") }M stdout PrintStream StringBuilder

  235. 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() }M stdout PrintStream

    StringBuilder
  236. 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() }M stdout PrintStream

    StringBuilder StringBuilder
  237. 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")

    }M stdout PrintStream StringBuilder StringBuilder
  238. 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")

    }M stdout PrintStream StringBuilder
  239. 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 ") }M stdout PrintStream StringBuilder
  240. 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 ") }M stdout PrintStream StringBuilder "⇠ prime [ran in "
  241. 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 "
  242. 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
  243. 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
  244. 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
  245. 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
  246. 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
  247. 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
  248. 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
  249. 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
  250. 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
  251. 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
  252. 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
  253. 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
  254. 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
  255. 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
  256. 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
  257. 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
  258. 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]"
  259. 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]"
  260. 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
  261. 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
  262. 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
  263. 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
  264. 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
  265. 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
  266. 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
  267. 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
  268. 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
  269. kotlin-plugin/src/main/kotlin/debuglog/DebugLogClassBuilder.kt InstructionAdapter(this).applyM{M //G ...Gbenchmark-printingGcode }M

  270. 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
  271. 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
  272. Done! Demo time!!!

  273. Grab this code at: github.com/kevinmost/debuglog

  274. 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
  275. Thank you! Kevin Most