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. Annotation Processing
    in a Kotlin World
    Zac Sweers
    @pandanomic

    View Slide

  2. Annotation Processing
    in a Kotlin World

    View Slide

  3. https://goo.gl/rSHTn4

    View Slide

  4. View Slide

  5. Annotation Processing
    in a Kotlin World

    View Slide

  6. Annotation Processing
    in a Kotlin World

    View Slide

  7. Annotation Processing
    in a Kotlin World

    View Slide

  8. Dagger
    @Inject
    @Qualifier

    View Slide

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

    View Slide

  10. @Provides
    fun provideList(): List {
    return emptyList()
    }a
    @Inject lateinit var tacoList: List

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  24. Quick refresher

    View Slide

  25. Quick refresher
    • Compile time

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  33. class Person(val name: String)

    View Slide

  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();
    }
    }

    View Slide

  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();
    }
    }

    View Slide

  36. class Person(val name: String)

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  44. class Person(val names: List)

    View Slide

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

    View Slide

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

    View Slide

  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();
    }
    }

    View Slide

  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();
    }
    }

    View Slide

  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();
    }
    }

    View Slide

  50. annotation class Metadata(
    )a

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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();
    }
    }

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  69. View Slide

  70. View Slide

  71. View Slide

  72. View Slide

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

    View Slide

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

    View Slide

  75. Compiler Plugins

    View Slide

  76. Compiler Plugins
    • Faster/incremental

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  80. BUT!

    View Slide

  81. BUT!

    View Slide

  82. BUT!
    • Undocumented and not stable

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  94. when (typeElement.kotlinMetadata) {
    is KotlinClassMetadata ->
    }a

    View Slide

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

    View Slide

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

    View Slide

  97. View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  104. val proto: ProtoBuf.Class = classData.classProto
    val constructors: List = proto.constructorList
    val properties: List = proto.propertyList
    val functions: List = proto.functionList

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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()

    View Slide

  112. it.isPrimary

    View Slide

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

    View Slide

  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()

    View Slide

  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()

    View Slide

  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()

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  121. kotlin-metadata

    View Slide

  122. kotlinx-metadata

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  126. Case Studies

    View Slide

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

    View Slide

  128. –Chris Banes, somewhere online once upon a time
    “I wish I could use copy but like,
    conditionally set properties”

    View Slide

  129. data class Taco(
    val seasoning: Set,
    val isBreakfast: Boolean
    )a

    View Slide

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

    View Slide

  131. data class Taco(
    val seasoning: Set,
    val isBreakfast: Boolean
    )a
    val taco = Taco(setOf("el paso"), true)
    val newTaco = taco.copyDynamic({
    if (isBreakfastTime) {
    isBreakfast = true
    }
    }

    View Slide

  132. class TacoDynamicBuilder constructor(
    private val source: Taco) {
    var seasoning: Set = 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

    View Slide

  133. class TacoDynamicBuilder constructor(
    private val source: Taco) {
    var seasoning: Set = 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

    View Slide

  134. class TacoDynamicBuilder constructor(
    private val source: Taco) {
    var seasoning: Set = 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

    View Slide

  135. data class Taco(
    val seasoning: Set,
    val isBreakfast: Boolean
    )a

    View Slide

  136. @CopyDynamic
    data class Taco(
    val seasoning: Set,
    val isBreakfast: Boolean
    )aa

    View Slide

  137. @CopyDynamic
    data class Taco(
    val seasoning: Set,
    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

    View Slide

  138. @CopyDynamic
    data class Taco(
    val seasoning: Set,
    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

    View Slide

  139. @CopyDynamic
    data class Taco(
    val seasoning: Set,
    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

    View Slide

  140. @CopyDynamic
    data class Taco(
    val seasoning: Set,
    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

    View Slide

  141. @CopyDynamic
    data class Taco(
    val seasoning: Set,
    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

    View Slide

  142. @CopyDynamic
    data class Taco(
    val seasoning: Set,
    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

    View Slide

  143. @CopyDynamic
    data class Taco(
    val seasoning: Set,
    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"

    View Slide

  144. Moshi
    https://github.com/square/moshi
    API overview blog post: https://goo.gl/dRjqFb

    View Slide

  145. Guidelines

    View Slide

  146. Metadata is
    added signal

    View Slide

  147. Abstract your
    renderer

    View Slide

  148. Be mindful of
    mixed source sets

    View Slide

  149. Lean on language
    features

    View Slide

  150. Be mindful of
    quirks and caveats

    View Slide

  151. Quirks & Caveats
    • Not public yet

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  161. Thanks!
    Special thanks to Eugenio Marletti (@workingkills)

    View Slide

  162. Thanks!
    Zac Sweers
    @pandanomic

    View Slide