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

Annotation Processing in a Kotlin World

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.

Zac Sweers

October 04, 2018
Tweet

More Decks by Zac Sweers

Other Decks in Programming

Transcript

  1. @Module class NetworkModule { companion object { @JvmStatic @Provides fun

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

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

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

    fun provideHttpClient(): HttpClient { return HttpClient.create() }a }c }b
  5. Quick refresher • Compile time • Since Java 5ish (APIs

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

    in Java 6) • Reads Java (byte) code in intermediate format • Elements, Types, Filer, etc APIs
  7. 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
  8. 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
  9. 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
  10. 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
  11. @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(); } }
  12. 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(); } }
  13. 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
  14. 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
  15. 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
  16. 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
  17. 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
  18. 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
  19. 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(); } }
  20. @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(); } }
  21. @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(); } }
  22. annotation class Metadata( val k: Int = 1, val mv:

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

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

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

    mv: IntArray = [], val bv: IntArray = [], val d1: Array<String> = [], val d2: Array<String> = [] )a
  26. @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(); } }
  27. @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
  28. @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
  29. @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
  30. @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
  31. @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
  32. @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
  33. @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
  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\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
  35. @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
  36. @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
  37. @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
  38. BUT! • Undocumented and not stable • Only applies to

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

    Kotlin sources • Tooling can be painful • github.com/hzsweers/redacted-compiler- plugin
  40. 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
  41. 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
  42. when (typeElement.kotlinMetadata) { is KotlinPackageMetadata -> is KotlinClassMetadata -> is

    KotlinFileMetadata -> is KotlinMultiFileClassPartMetadata -> is KotlinMultiFileClassFacadeMetadata -> is KotlinSyntheticClassMetadata -> is KotlinUnknownMetadata -> }a
  43. when (metadata) { is KotlinClassMetadata -> { val classData: ClassData

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

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

    = metadata.data val proto: ProtoBuf.Class = classData.classProto val nameResolver = classData.nameResolver }a }b
  46. @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
  47. @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
  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\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
  49. when (metadata) { is KotlinClassMetadata -> { val classData: ClassData

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

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

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

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

    val mutable = TODO() val nullable = TODO() val hasDefault = TODO()
  54. 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()
  55. 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()
  56. 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()
  57. 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()
  58. 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
  59. 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
  60. 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
  61. 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
  62. –Chris Banes, somewhere online once upon a time “I wish

    I could use copy but like, conditionally set properties”
  63. 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 } }
  64. 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
  65. 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
  66. 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
  67. @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
  68. @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
  69. @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
  70. @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
  71. @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
  72. @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
  73. @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"
  74. Quirks & Caveats • Not public yet • Not incremental

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

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

    • No multiplatform • (Kapt) No multiple rounds* • (Kapt) Varying build system support
  77. “I have a processor and want to support kotlin today!

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

    What should I use?” “kotlin-metadata, probably” “and KotlinPoet”
  79. 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