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

Annotation Processing in a Kotlin World

85a1166e93654865b4dcafdafe2b2dfd?s=47 Zac Sweers
October 04, 2018

Annotation Processing in a Kotlin World

Annotation processing is a powerful tool to extend your codebase at build time, and the advent of Kotlin in the android community introduces a dimension to this. Kapt works with existing Java processors, but it doesn’t come without its quirks at the interop layer. This talk will dive into how you can leverage Kotlin bytecode’s @Metadata annotations to better understand Kotlin code during annotation processing, covering anywhere from case studies in Dagger and Moshi to leveraging tools like KotlinPoet to even generate Kotlin code that seamlessly leverages language features.

85a1166e93654865b4dcafdafe2b2dfd?s=128

Zac Sweers

October 04, 2018
Tweet

More Decks by Zac Sweers

Other Decks in Programming

Transcript

  1. Annotation Processing in a Kotlin World Zac Sweers @pandanomic

  2. Annotation Processing in a Kotlin World

  3. https://goo.gl/rSHTn4

  4. None
  5. Annotation Processing in a Kotlin World

  6. Annotation Processing in a Kotlin World

  7. Annotation Processing in a Kotlin World

  8. Dagger @Inject @Qualifier

  9. @Provides fun provideList(): List<Taco> { return emptyList() }a

  10. @Provides fun provideList(): List<Taco> { return emptyList() }a @Inject lateinit

    var tacoList: List<Taco>
  11. @Provides fun provideList(): List<Taco> { return emptyList() }a @Inject lateinit

    var tacoList: List<@SuppressJvmWildcard Taco>
  12. @Provides fun provideList(): List<Taco> { return emptyList() }a @Inject lateinit

    var tacoList: List<@SuppressJvmWildcard Taco>
  13. @Provides fun provideTaco(): Taco { return BreakfastTaco() }a @Inject lateinit

    var taco: Taco
  14. @Breakfast @Provides fun provideTaco(): Taco { return BreakfastTaco() }a @Breakfast

    @Inject lateinit var taco: Taco
  15. @Breakfast @Provides fun provideTaco(): Taco { return BreakfastTaco() }a @field:Breakfast

    @Inject lateinit var taco: Taco
  16. @Breakfast @Provides fun provideTaco(): Taco { return BreakfastTaco() }a @field:Breakfast

    @Inject lateinit var taco: Taco
  17. @Module class NetworkModule { @Provides static HttpClient provideHttpClient() { return

    HttpClient.create(); }a }b
  18. @Module class NetworkModule { @Provides static HttpClient provideHttpClient() { return

    HttpClient.create(); }a companion object { }c }b
  19. @Module class NetworkModule { companion object { @Provides fun provideHttpClient():

    HttpClient { return HttpClient.create() }a }c }b
  20. @Module class NetworkModule { companion object { @JvmStatic @Provides fun

    provideHttpClient(): HttpClient { return HttpClient.create() }a }c }b
  21. @Module class NetworkModule { @Module companion object { @JvmStatic @Provides

    fun provideHttpClient(): HttpClient { return HttpClient.create() }a }c }b
  22. @Module class NetworkModule { @Module companion object { @JvmStatic @Provides

    fun provideHttpClient(): HttpClient { return HttpClient.create() }a }c }b
  23. @Module class NetworkModule { @Module companion object { @JvmStatic @Provides

    fun provideHttpClient(): HttpClient { return HttpClient.create() }a }c }b
  24. Quick refresher

  25. Quick refresher • Compile time

  26. Quick refresher • Compile time • Since Java 5ish (APIs

    in Java 6)
  27. Quick refresher • Compile time • Since Java 5ish (APIs

    in Java 6) • Reads Java (byte) code in intermediate format
  28. Quick refresher • Compile time • Since Java 5ish (APIs

    in Java 6) • Reads Java (byte) code in intermediate format • Elements, Types, Filer, etc APIs
  29. Quick refresher • Compile time • Since Java 5ish (APIs

    in Java 6) • Reads Java (byte) code in intermediate format • Elements, Types, Filer, etc APIs • Cannot modify code, only generate more
  30. Quick refresher • Compile time • Since Java 5ish (APIs

    in Java 6) • Reads Java (byte) code in intermediate format • Elements, Types, Filer, etc APIs • Cannot modify code, only generate more • Multiple rounds
  31. Quick refresher • Compile time • Since Java 5ish (APIs

    in Java 6) • Reads Java (byte) code in intermediate format • Elements, Types, Filer, etc APIs • Cannot modify code, only generate more • Multiple rounds
  32. Quick refresher • Compile time • Since Java 5ish (APIs

    in Java 6) • Reads Java (byte) code in intermediate format • Elements, Types, Filer, etc APIs • Cannot modify code, only generate more • Multiple rounds
  33. class Person(val name: String)

  34. @kotlin.Metadata( mv = {1, 1, 11}, bv = {1, 0,

    2}, k = 1, d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0010\u000e\n\u0002\b\u0004\b\u0007\u0018 \u00002\u00020\u0001B\r\u0012\u0006\u0010\u0002\u001a\u00020\u0003\u00a2\u0006\u0002\u0010\u0004R\u0011\u0010\u0 002\u001a\u00020\u0003\u00a2\u0006\b\n\u0000\u001a\u0004\b\u0005\u0010\u0006\u00a8\u0006\u0007"}, d2 = {"Ltest/Person;", "", "name", "", "(Ljava/lang/String;)V", "getName", "()Ljava/lang/String;", "test- project"} ) public final class Person { @org.jetbrains.annotations.NotNull() private final java.lang.String name = null; @org.jetbrains.annotations.NotNull() public final java.lang.String getName() { return null; } public Person(@org.jetbrains.annotations.NotNull() java.lang.String name) { super(); } }
  35. public final class Person { @org.jetbrains.annotations.NotNull() private final java.lang.String name

    = null; @org.jetbrains.annotations.NotNull() public final java.lang.String getName() { return null; } public Person(@org.jetbrains.annotations.NotNull() java.lang.String name) { super(); } }
  36. class Person(val name: String)

  37. public final class Person { @org.jetbrains.annotations.NotNull() private final java.lang.String name

    = null; @org.jetbrains.annotations.NotNull() public final java.lang.String getName() { return null; }c public Person(@org.jetbrains.annotations.NotNull() java.lang.String name) { super(); }a }b
  38. public final class Person { @org.jetbrains.annotations.NotNull() private final java.lang.String name

    = null; @org.jetbrains.annotations.NotNull() public final java.lang.String getName() { return null; }c public Person(@org.jetbrains.annotations.NotNull() java.lang.String name) { super(); }a }b
  39. public final class Person { @org.jetbrains.annotations.NotNull() private final java.lang.String name

    = null; @org.jetbrains.annotations.NotNull() public final java.lang.String getName() { return null; }c public Person(@org.jetbrains.annotations.NotNull() java.lang.String name) { super(); }a }b
  40. class Person(val name: String = "Joe")

  41. public final class Person { @org.jetbrains.annotations.NotNull() private final java.lang.String name

    = null; @org.jetbrains.annotations.NotNull() public final java.lang.String getName() { return null; }c public Person(@org.jetbrains.annotations.NotNull() java.lang.String name) { super(); }a }b
  42. public final class Person { @org.jetbrains.annotations.NotNull() private final java.lang.String name

    = null; @org.jetbrains.annotations.NotNull() public final java.lang.String getName() { return null; }c public Person(@org.jetbrains.annotations.NotNull() java.lang.String name) { super(); }a public Person() { super(); }d }b
  43. public final class Person { @org.jetbrains.annotations.NotNull() private final java.lang.String name

    = null; @org.jetbrains.annotations.NotNull() public final java.lang.String getName() { return null; }c public Person(@org.jetbrains.annotations.NotNull() java.lang.String name) { super(); }a public Person() { super(); }d }b
  44. class Person(val names: List<String>)

  45. class Person(val names: List<String>) public Person(@NotNull List<String> names)

  46. class Person(val names: MutableList<String>) public Person(@NotNull List<String> names)

  47. public final class Person { @org.jetbrains.annotations.NotNull() private final java.lang.String name

    = null; @org.jetbrains.annotations.NotNull() public final java.lang.String getName() { return null; } public Person(@org.jetbrains.annotations.NotNull() java.lang.String name) { super(); } }
  48. @kotlin.Metadata( mv = {1, 1, 11}, bv = {1, 0,

    2}, k = 1, d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0010\u000e\n\u0002\b\u0004\b\u0007\u0018 \u00002\u00020\u0001B\r\u0012\u0006\u0010\u0002\u001a\u00020\u0003\u00a2\u0006\u0002\u0010\u0004R\u0011\u0010\u0 002\u001a\u00020\u0003\u00a2\u0006\b\n\u0000\u001a\u0004\b\u0005\u0010\u0006\u00a8\u0006\u0007"}, d2 = {"Ltest/Person;", "", "name", "", "(Ljava/lang/String;)V", "getName", "()Ljava/lang/String;", "test- project"} )a public final class Person { @org.jetbrains.annotations.NotNull() private final java.lang.String name = null; @org.jetbrains.annotations.NotNull() public final java.lang.String getName() { return null; } public Person(@org.jetbrains.annotations.NotNull() java.lang.String name) { super(); } }
  49. @kotlin.Metadata( mv = {1, 1, 11}, bv = {1, 0,

    2}, k = 1, d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0010\u000e\n\u0002\b\u0004\b\u0007\u0018 \u00002\u00020\u0001B\r\u0012\u0006\u0010\u0002\u001a\u00020\u0003\u00a2\u0006\u0002\u0010\u0004R\u0011\u0010\u0 002\u001a\u00020\u0003\u00a2\u0006\b\n\u0000\u001a\u0004\b\u0005\u0010\u0006\u00a8\u0006\u0007"}, d2 = {"Ltest/Person;", "", "name", "", "(Ljava/lang/String;)V", "getName", "()Ljava/lang/String;", "test- project"} )a public final class Person { @org.jetbrains.annotations.NotNull() private final java.lang.String name = null; @org.jetbrains.annotations.NotNull() public final java.lang.String getName() { return null; } public Person(@org.jetbrains.annotations.NotNull() java.lang.String name) { super(); } }
  50. annotation class Metadata( )a

  51. annotation class Metadata( val k: Int = 1 )a

  52. annotation class Metadata( val k: Int = 1, val mv:

    IntArray = [] )a
  53. annotation class Metadata( val k: Int = 1, val mv:

    IntArray = [], val bv: IntArray = [] )a
  54. annotation class Metadata( val k: Int = 1, val mv:

    IntArray = [], val bv: IntArray = [], val d1: Array<String> = [] )a
  55. annotation class Metadata( val k: Int = 1, val mv:

    IntArray = [], val bv: IntArray = [], val d1: Array<String> = [], val d2: Array<String> = [] )a
  56. internal annotation class Metadata( val k: Int = 1, val

    mv: IntArray = [], val bv: IntArray = [], val d1: Array<String> = [], val d2: Array<String> = [] )a
  57. @kotlin.Metadata( mv = {1, 1, 11}, bv = {1, 0,

    2}, k = 1, d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0010\u000e\n\u0002\b\u0004\b\u0007\u0018 \u00002\u00020\u0001B\r\u0012\u0006\u0010\u0002\u001a\u00020\u0003\u00a2\u0006\u0002\u0010\u0004R\u0011\u0010\u0 002\u001a\u00020\u0003\u00a2\u0006\b\n\u0000\u001a\u0004\b\u0005\u0010\u0006\u00a8\u0006\u0007"}, d2 = {"Ltest/Person;", "", "name", "", "(Ljava/lang/String;)V", "getName", "()Ljava/lang/String;", "test- project"} )a public final class Person { @org.jetbrains.annotations.NotNull() private final java.lang.String name = null; @org.jetbrains.annotations.NotNull() public final java.lang.String getName() { return null; } public Person(@org.jetbrains.annotations.NotNull() java.lang.String name) { super(); } }
  58. @kotlin.Metadata( mv = {1, 1, 11}, bv = {1, 0,

    2}, k = 1, d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n \u0000\n\u0002\u0010\u000e\n\u0002\b\u0004\b\u0007\u0018 \u00002\u00020\u0001B\r\u0012\u0006\u0010\u0002\u001a\u0 0020\u0003\u00a2\u0006\u0002\u0010\u0004R\u0011\u0010\u0 002\u001a\u00020\u0003\u00a2\u0006\b\n\u0000\u001a\u0004 \b\u0005\u0010\u0006\u00a8\u0006\u0007"}, d2 = {"Ltest/Person;", "", "name", "", "(Ljava/lang/ String;)V", "getName", "()Ljava/lang/String;", "test- project"} )a
  59. @kotlin.Metadata( mv = {1, 1, 11}, bv = {1, 0,

    2}, k = 1, d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n \u0000\n\u0002\u0010\u000e\n\u0002\b\u0004\b\u0007\u0018 \u00002\u00020\u0001B\r\u0012\u0006\u0010\u0002\u001a\u0 0020\u0003\u00a2\u0006\u0002\u0010\u0004R\u0011\u0010\u0 002\u001a\u00020\u0003\u00a2\u0006\b\n\u0000\u001a\u0004 \b\u0005\u0010\u0006\u00a8\u0006\u0007"}, d2 = {"Ltest/Person;", "", "name", "", "(Ljava/lang/ String;)V", "getName", "()Ljava/lang/String;", "test- project"} )a
  60. @kotlin.Metadata( mv = {1, 1, 11}, bv = {1, 0,

    2}, k = 1, d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n \u0000\n\u0002\u0010\u000e\n\u0002\b\u0004\b\u0007\u0018 \u00002\u00020\u0001B\r\u0012\u0006\u0010\u0002\u001a\u0 0020\u0003\u00a2\u0006\u0002\u0010\u0004R\u0011\u0010\u0 002\u001a\u00020\u0003\u00a2\u0006\b\n\u0000\u001a\u0004 \b\u0005\u0010\u0006\u00a8\u0006\u0007"}, d2 = {"Ltest/Person;", "", "name", "", "(Ljava/lang/ String;)V", "getName", "()Ljava/lang/String;", "test- project"} )a
  61. @kotlin.Metadata( mv = {1, 1, 11}, bv = {1, 0,

    2}, k = 1, d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n \u0000\n\u0002\u0010\u000e\n\u0002\b\u0004\b\u0007\u0018 \u00002\u00020\u0001B\r\u0012\u0006\u0010\u0002\u001a\u0 0020\u0003\u00a2\u0006\u0002\u0010\u0004R\u0011\u0010\u0 002\u001a\u00020\u0003\u00a2\u0006\b\n\u0000\u001a\u0004 \b\u0005\u0010\u0006\u00a8\u0006\u0007"}, d2 = {"Ltest/Person;", "", "name", "", "(Ljava/lang/ String;)V", "getName", "()Ljava/lang/String;", "test- project"} )a
  62. @kotlin.Metadata( mv = {1, 1, 11}, bv = {1, 0,

    2}, k = 1, d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n \u0000\n\u0002\u0010\u000e\n\u0002\b\u0004\b\u0007\u0018 \u00002\u00020\u0001B\r\u0012\u0006\u0010\u0002\u001a\u0 0020\u0003\u00a2\u0006\u0002\u0010\u0004R\u0011\u0010\u0 002\u001a\u00020\u0003\u00a2\u0006\b\n\u0000\u001a\u0004 \b\u0005\u0010\u0006\u00a8\u0006\u0007"}, d2 = {"Ltest/Person;", "", "name", "", "(Ljava/lang/ String;)V", "getName", "()Ljava/lang/String;", "test- project"} )a
  63. @kotlin.Metadata( mv = {1, 1, 11}, bv = {1, 0,

    2}, k = 1, d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n \u0000\n\u0002\u0010\u000e\n\u0002\b\u0004\b\u0007\u0018 \u00002\u00020\u0001B\r\u0012\u0006\u0010\u0002\u001a\u0 0020\u0003\u00a2\u0006\u0002\u0010\u0004R\u0011\u0010\u0 002\u001a\u00020\u0003\u00a2\u0006\b\n\u0000\u001a\u0004 \b\u0005\u0010\u0006\u00a8\u0006\u0007"}, d2 = {"Ltest/Person;", "", "name", "", "(Ljava/lang/ String;)V", "getName", "()Ljava/lang/String;", "test- project"} )a
  64. @kotlin.Metadata( mv = {1, 1, 11}, bv = {1, 0,

    2}, k = 1, d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n \u0000\n\u0002\u0010\u000e\n\u0002\b\u0004\b\u0007\u0018 \u00002\u00020\u0001B\r\u0012\u0006\u0010\u0002\u001a\u0 0020\u0003\u00a2\u0006\u0002\u0010\u0004R\u0011\u0010\u0 002\u001a\u00020\u0003\u00a2\u0006\b\n\u0000\u001a\u0004 \b\u0005\u0010\u0006\u00a8\u0006\u0007"}, d2 = {"Ltest/Person;", "", "name", "", "(Ljava/lang/ String;)V", "getName", "()Ljava/lang/String;", "test- project"} )a
  65. @kotlin.Metadata( mv = {1, 1, 11}, bv = {1, 0,

    2}, k = 1, d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n \u0000\n\u0002\u0010\u000e\n\u0002\b\u0004\b\u0007\u0018 \u00002\u00020\u0001B\r\u0012\u0006\u0010\u0002\u001a\u0 0020\u0003\u00a2\u0006\u0002\u0010\u0004R\u0011\u0010\u0 002\u001a\u00020\u0003\u00a2\u0006\b\n\u0000\u001a\u0004 \b\u0005\u0010\u0006\u00a8\u0006\u0007"}, d2 = {"Ltest/Person;", "", "name", "", "(Ljava/lang/ String;)V", "getName", "()Ljava/lang/String;", "test- project"} )a
  66. @kotlin.Metadata( mv = {1, 1, 11}, bv = {1, 0,

    2}, k = 1, d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n \u0000\n\u0002\u0010\u000e\n\u0002\b\u0004\b\u0007\u0018 \u00002\u00020\u0001B\r\u0012\u0006\u0010\u0002\u001a\u0 0020\u0003\u00a2\u0006\u0002\u0010\u0004R\u0011\u0010\u0 002\u001a\u00020\u0003\u00a2\u0006\b\n\u0000\u001a\u0004 \b\u0005\u0010\u0006\u00a8\u0006\u0007"}, d2 = {"Ltest/Person;", "", "name", "", "(Ljava/lang/ String;)V", "getName", "()Ljava/lang/String;", "test- project"} )a
  67. @kotlin.Metadata( mv = {1, 1, 11}, bv = {1, 0,

    2}, k = 1, d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n \u0000\n\u0002\u0010\u000e\n\u0002\b\u0004\b\u0007\u0018 \u00002\u00020\u0001B\r\u0012\u0006\u0010\u0002\u001a\u0 0020\u0003\u00a2\u0006\u0002\u0010\u0004R\u0011\u0010\u0 002\u001a\u00020\u0003\u00a2\u0006\b\n\u0000\u001a\u0004 \b\u0005\u0010\u0006\u00a8\u0006\u0007"}, d2 = {"Ltest/Person;", "", "name", "", "(Ljava/lang/ String;)V", "getName", "()Ljava/lang/String;", "test- project"} )a
  68. @kotlin.Metadata( mv = {1, 1, 11}, bv = {1, 0,

    2}, k = 1, d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n \u0000\n\u0002\u0010\u000e\n\u0002\b\u0004\b\u0007\u0018 \u00002\u00020\u0001B\r\u0012\u0006\u0010\u0002\u001a\u0 0020\u0003\u00a2\u0006\u0002\u0010\u0004R\u0011\u0010\u0 002\u001a\u00020\u0003\u00a2\u0006\b\n\u0000\u001a\u0004 \b\u0005\u0010\u0006\u00a8\u0006\u0007"}, d2 = {"Ltest/Person;", "", "name", "", "(Ljava/lang/ String;)V", "getName", "()Ljava/lang/String;", "test- project"} )a
  69. None
  70. None
  71. None
  72. None
  73. val metadata = typeElement.getAnnotation(Metadata::class.java)

  74. "Reading information from an annotation, you say?"

  75. Compiler Plugins

  76. Compiler Plugins • Faster/incremental

  77. Compiler Plugins • Faster/incremental • No stubs generation

  78. Compiler Plugins • Faster/incremental • No stubs generation • No

    kapt
  79. Compiler Plugins • Faster/incremental • No stubs generation • No

    kapt
  80. BUT!

  81. BUT!

  82. BUT! • Undocumented and not stable

  83. BUT! • Undocumented and not stable • Only applies to

    Kotlin sources
  84. BUT! • Undocumented and not stable • Only applies to

    Kotlin sources • Tooling can be painful
  85. BUT! • Undocumented and not stable • Only applies to

    Kotlin sources • Tooling can be painful • github.com/hzsweers/redacted-compiler- plugin
  86. BUT! • Undocumented and not stable • Only applies to

    Kotlin sources • Tooling can be painful • github.com/hzsweers/redacted-compiler- plugin • The talk title was Annotation Processing in a Kotlin
  87. BUT!* • Undocumented and not stable • Only applies to

    Kotlin sources • Tooling can be painful • github.com/hzsweers/redacted-compiler- plugin • The talk title was Annotation Processing in a Kotlin
  88. "Reading information from an annotation, you say?"

  89. kotlin-metadata https://github.com/takhion/kotlin-metadata

  90. val metadata = typeElement.getAnnotation(Metadata::class.java)

  91. val Element.kotlinMetadata: KotlinMetadata? val metadata = typeElement.getAnnotation(Metadata::class.java)

  92. val Element.kotlinMetadata: KotlinMetadata? val metadata = typeElement.kotlinMetadata

  93. when (typeElement.kotlinMetadata) { is KotlinPackageMetadata -> is KotlinClassMetadata -> is

    KotlinFileMetadata -> is KotlinMultiFileClassPartMetadata -> is KotlinMultiFileClassFacadeMetadata -> is KotlinSyntheticClassMetadata -> is KotlinUnknownMetadata -> }a
  94. when (typeElement.kotlinMetadata) { is KotlinClassMetadata -> }a

  95. when (metadata) { is KotlinClassMetadata -> { val classData: ClassData

    = metadata.data }a }b
  96. when (metadata) { is KotlinClassMetadata -> { val classData: ClassData

    = metadata.data val proto: ProtoBuf.Class = classData.classProto }a }b
  97. None
  98. when (metadata) { is KotlinClassMetadata -> { val classData: ClassData

    = metadata.data val proto: ProtoBuf.Class = classData.classProto }a }b
  99. when (metadata) { is KotlinClassMetadata -> { val classData: ClassData

    = metadata.data val proto: ProtoBuf.Class = classData.classProto val nameResolver = classData.nameResolver }a }b
  100. @kotlin.Metadata( mv = {1, 1, 11}, bv = {1, 0,

    2}, k = 1, d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n \u0000\n\u0002\u0010\u000e\n\u0002\b\u0004\b\u0007\u0018 \u00002\u00020\u0001B\r\u0012\u0006\u0010\u0002\u001a\u0 0020\u0003\u00a2\u0006\u0002\u0010\u0004R\u0011\u0010\u0 002\u001a\u00020\u0003\u00a2\u0006\b\n\u0000\u001a\u0004 \b\u0005\u0010\u0006\u00a8\u0006\u0007"}, d2 = {"Ltest/Person;", "", "name", "", "(Ljava/lang/ String;)V", "getName", "()Ljava/lang/String;", "test- project"} )a
  101. @kotlin.Metadata( mv = {1, 1, 11}, bv = {1, 0,

    2}, k = 1, d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n \u0000\n\u0002\u0010\u000e\n\u0002\b\u0004\b\u0007\u0018 \u00002\u00020\u0001B\r\u0012\u0006\u0010\u0002\u001a\u0 0020\u0003\u00a2\u0006\u0002\u0010\u0004R\u0011\u0010\u0 002\u001a\u00020\u0003\u00a2\u0006\b\n\u0000\u001a\u0004 \b\u0005\u0010\u0006\u00a8\u0006\u0007"}, d2 = {"Ltest/Person;", "", "name", "", "(Ljava/lang/ String;)V", "getName", "()Ljava/lang/String;", "test- project"} )a
  102. @kotlin.Metadata( mv = {1, 1, 11}, bv = {1, 0,

    2}, k = 1, d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n \u0000\n\u0002\u0010\u000e\n\u0002\b\u0004\b\u0007\u0018 \u00002\u00020\u0001B\r\u0012\u0006\u0010\u0002\u001a\u0 0020\u0003\u00a2\u0006\u0002\u0010\u0004R\u0011\u0010\u0 002\u001a\u00020\u0003\u00a2\u0006\b\n\u0000\u001a\u0004 \b\u0005\u0010\u0006\u00a8\u0006\u0007"}, d2 = {"Ltest/Person;", "", "name", "", "(Ljava/lang/ String;)V", "getName", "()Ljava/lang/String;", "test- project"} )a
  103. when (metadata) { is KotlinClassMetadata -> { val classData: ClassData

    = metadata.data val proto: ProtoBuf.Class = classData.classProto val nameResolver = classData.nameResolver }a }b
  104. val proto: ProtoBuf.Class = classData.classProto val constructors: List<Constructor> = proto.constructorList

    val properties: List<Property> = proto.propertyList val functions: List<Function> = proto.functionList
  105. class Person(val name: String = "Joe")

  106. class Person(val name: String = "Joe") val name = TODO()

  107. class Person(val name: String = "Joe") val name = TODO()

    val mutable = TODO()
  108. class Person(val name: String = "Joe") val name = TODO()

    val mutable = TODO() val nullable = TODO()
  109. class Person(val name: String = "Joe") val name = TODO()

    val mutable = TODO() val nullable = TODO() val hasDefault = TODO()
  110. class Person(val name: String = "Joe") val name = TODO()

    val mutable = TODO() val nullable = TODO() val hasDefault = TODO()
  111. class Person(val name: String = "Joe") val proto = classData.classProto

    val constr = proto.constructorList.find { it.isPrimary } val name = TODO() val mutable = TODO() val nullable = TODO() val hasDefault = TODO()
  112. it.isPrimary

  113. val Constructor.isPrimary: Boolean get() { return !Flags.IS_SECONDARY[flags] }

  114. class Person(val name: String = "Joe") val proto = classData.classProto

    val constr = proto.constructorList.find { it.isPrimary } val name = TODO() val mutable = TODO() val nullable = TODO() val hasDefault = TODO()
  115. class Person(val name: String = "Joe") val proto = classData.classProto

    val constr = proto.constructorList.find { it.isPrimary } val nameParam = constr.valueParameterList[0] val name = TODO() val mutable = TODO() val nullable = TODO() val hasDefault = TODO()
  116. class Person(val name: String = "Joe") val proto = classData.classProto

    val constr = proto.constructorList.find { it.isPrimary } val nameParam = constr.valueParameterList[0] val name = nameResolver.getString(nameParam.name) val mutable = TODO() val nullable = TODO() val hasDefault = TODO()
  117. class Person(val name: String = "Joe") val proto = classData.classProto

    val constr = proto.constructorList.find { it.isPrimary } val nameParam = constr.valueParameterList[0] val name = nameResolver.getString(nameParam.name) val mutable = TODO() val nullable = TODO() val hasDefault = nameParam.declaresDefaultValue
  118. class Person(val name: String = "Joe") val proto = classData.classProto

    val constr = proto.constructorList.find { it.isPrimary } val nameParam = constr.valueParameterList[0] val name = nameResolver.getString(nameParam.name) val mutable = TODO() val nullable = nameParam.type.nullable val hasDefault = nameParam.declaresDefaultValue
  119. class Person(val name: String = "Joe") val proto = classData.classProto

    val constr = proto.constructorList.find { it.isPrimary } val nameParam = constr.valueParameterList[0] val name = nameResolver.getString(nameParam.name) val mutable = proto.propertyList[0].isVar val nullable = nameParam.type.nullable val hasDefault = nameParam.declaresDefaultValue
  120. class Person(val name: String = "Joe") val proto = classData.classProto

    val constr = proto.constructorList.find { it.isPrimary } val nameParam = constr.valueParameterList[0] val name = nameResolver.getString(nameParam.name) val mutable = proto.propertyList[0].isVar val nullable = nameParam.type.nullable val hasDefault = nameParam.declaresDefaultValue
  121. kotlin-metadata

  122. kotlinx-metadata

  123. kotlinx-metadata https://github.com/JetBrains/kotlin/tree/master/libraries/kotlinx-metadata/jvm

  124. kotlinx-metadata? https://github.com/JetBrains/kotlin/tree/master/libraries/kotlinx-metadata/jvm

  125. kotlinx-metadata? https://github.com/JetBrains/kotlin/tree/master/libraries/kotlinx-metadata/jvm https://github.com/square/moshi/pull/642

  126. Case Studies

  127. copydynamic https://github.com/hzsweers/copydynamic

  128. –Chris Banes, somewhere online once upon a time “I wish

    I could use copy but like, conditionally set properties”
  129. data class Taco( val seasoning: Set<String>, val isBreakfast: Boolean )a

  130. data class Taco( val seasoning: Set<String>, val isBreakfast: Boolean )a

    val taco = Taco(setOf("el paso"), true)
  131. data class Taco( val seasoning: Set<String>, val isBreakfast: Boolean )a

    val taco = Taco(setOf("el paso"), true) val newTaco = taco.copyDynamic({ if (isBreakfastTime) { isBreakfast = true } }
  132. class TacoDynamicBuilder constructor( private val source: Taco) { var seasoning:

    Set<String> = source.seasoning var isBreakfast: Boolean = source.isBreakfast fun build(): Taco = source.copy(seasoning, isBreakfast) }a fun Taco.copyDynamic( copyBlock: TacoDynamicBuilder.() -> Unit): Taco { return TacoDynamicBuilder(this) .also { copyBlock(it) } .build() }b
  133. class TacoDynamicBuilder constructor( private val source: Taco) { var seasoning:

    Set<String> = source.seasoning var isBreakfast: Boolean = source.isBreakfast fun build(): Taco = source.copy(seasoning, isBreakfast) }a fun Taco.copyDynamic( copyBlock: TacoDynamicBuilder.() -> Unit): Taco { return TacoDynamicBuilder(this) .also { copyBlock(it) } .build() }b
  134. class TacoDynamicBuilder constructor( private val source: Taco) { var seasoning:

    Set<String> = source.seasoning var isBreakfast: Boolean = source.isBreakfast fun build(): Taco = source.copy(seasoning, isBreakfast) }a fun Taco.copyDynamic( copyBlock: TacoDynamicBuilder.() -> Unit): Taco { return TacoDynamicBuilder(this) .also { copyBlock(it) } .build() }b
  135. data class Taco( val seasoning: Set<String>, val isBreakfast: Boolean )a

  136. @CopyDynamic data class Taco( val seasoning: Set<String>, val isBreakfast: Boolean

    )aa
  137. @CopyDynamic data class Taco( val seasoning: Set<String>, val isBreakfast: Boolean

    )aa •Read the proto off Taco •Ensure it's a data class •Find constructor •Get all params + matching properties •Generate with KotlinPoet
  138. @CopyDynamic data class Taco( val seasoning: Set<String>, val isBreakfast: Boolean

    )a •Read the proto off Taco •Ensure it's a data class •Find constructor •Get all params + matching properties •Generate with KotlinPoet
  139. @CopyDynamic data class Taco( val seasoning: Set<String>, val isBreakfast: Boolean

    )a •Read the proto off Taco •Ensure it's a data class •Find constructor •Get all params + matching properties •Generate with KotlinPoet
  140. @CopyDynamic data class Taco( val seasoning: Set<String>, val isBreakfast: Boolean

    )a •Read the proto off Taco •Ensure it's a data class •Find constructor •Get all params + matching properties •Generate with KotlinPoet
  141. @CopyDynamic data class Taco( val seasoning: Set<String>, val isBreakfast: Boolean

    )a •Read the proto off Taco •Ensure it's a data class •Find constructor •Get all params + matching properties •Generate with KotlinPoet
  142. @CopyDynamic data class Taco( val seasoning: Set<String>, val isBreakfast: Boolean

    )a •Read the proto off Taco •Ensure it's a data class •Find constructor •Get all params + matching properties •Generate with KotlinPoet
  143. @CopyDynamic data class Taco( val seasoning: Set<String>, val isBreakfast: Boolean

    )a •Read the proto off Taco •Ensure it's a data class •Find constructor •Get all params + matching properties •Generate with KotlinPoet "kapt.kotlin.generated"
  144. Moshi https://github.com/square/moshi API overview blog post: https://goo.gl/dRjqFb

  145. Guidelines

  146. Metadata is added signal

  147. Abstract your renderer

  148. Be mindful of mixed source sets

  149. Lean on language features

  150. Be mindful of quirks and caveats

  151. Quirks & Caveats • Not public yet

  152. Quirks & Caveats • Not public yet • Not incremental

  153. Quirks & Caveats • Not public yet • Not incremental

    • No multiplatform
  154. Quirks & Caveats • Not public yet • Not incremental

    • No multiplatform • (Kapt) No multiple rounds*
  155. Quirks & Caveats • Not public yet • Not incremental

    • No multiplatform • (Kapt) No multiple rounds* • (Kapt) Varying build system support
  156. Quirks & Caveats • Not public yet • Not incremental

    • No multiplatform • (Kapt) No multiple rounds* • (Kapt) Varying build system support
  157. –you? “I have a processor and want to support kotlin

    today! What should I use?”
  158. “I have a processor and want to support kotlin today!

    What should I use?” “kotlin-metadata, probably”
  159. “I have a processor and want to support kotlin today!

    What should I use?” “kotlin-metadata, probably” “and KotlinPoet”
  160. Thanks! • kotlin-metadata: github.com/takhion/kotlin-metadata • kotlinx-metadata: https://github.com/JetBrains/kotlin/tree/master/ libraries/kotlinx-metadata/jvm • Moshi:

    github.com/square/moshi • Km API prototype: github.com/square/moshi/pull/642 • CopyDynamic: github.com/hzsweers/copydynamic • FR for value type API: youtrack.jetbrains.com/issue/KT-26602 • Multiple rounds demo: github.com/Takhion/generate-kotlin-multiple-rounds • redacted compiler plugin: github.com/hzsweers/redacted-compiler-plugin • KotlinPoet: github.com/square/kotlinpoet
  161. Thanks! Special thanks to Eugenio Marletti (@workingkills)

  162. Thanks! Zac Sweers @pandanomic