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

Exploiting Kotlin Metadata + Annotation Processing

Exploiting Kotlin Metadata + Annotation Processing

When compiling Kotlin code for the JVM, the resulting bytecode doesn't understand the advanced features of the language – like the difference between List and MutableList, or List<String> and List<String?>.

A year ago a prototype was developed to use these "metadata" during annotation processing; today, a few popular libraries (even from Google!) employ it under the hood to take their Kotlin integration to the next level.

Join us to explore what's possible, and have fun digging into some practical examples.

[ sequel to last year's presentation at Kotlin Night Berlin: "Unleash the secret power of Kotlin Metadata" ]

Eugenio Marletti

June 15, 2018
Tweet

More Decks by Eugenio Marletti

Other Decks in Technology

Transcript

  1. View Slide

  2. View Slide

  3. List
    List
    MutableList
    MyTypeAlias
    !"
    !"
    !"
    !"
    List
    List
    List
    List

    View Slide

  4. class
    object
    data class
    sealed class
    !"
    !"
    !"
    !"
    class
    class
    class
    abstract class

    View Slide

  5. data class Sample(val counter: Int)

    View Slide

  6. View Slide

  7. View Slide

  8. View Slide

  9. public final class Sample {
    private final int counter;
    public final int getCounter() {
    return this.counter;
    }
    public Sample(int counter) {
    this.counter = counter;
    }
    public final int component1() {
    return this.counter;
    }
    @NotNull
    public final Sample copy(int counter) {
    return new Sample(counter);
    }
    // $FF: synthetic method
    // $FF: bridge method
    @NotNull
    public static Sample copy$default(Sample var0, int var1, int var2, Object var3) {
    if((var2 & 1) != 0) {
    var1 = var0.counter;
    }
    return var0.copy(var1);
    }
    public String toString() {
    return "Sample(counter=" + this.counter + ")";
    }
    public int hashCode() {
    return this.counter;
    }
    public boolean equals(Object var1) {
    if(this != var1) {
    if(var1 instanceof Sample) {
    Sample var2 = (Sample)var1;
    if(this.counter == var2.counter) {
    return true;
    }
    }
    return false;
    } else {
    return true;
    }
    }
    }
    @Metadata(
    k = 1,
    mv = {1, 1, 10},
    bv = {1, 0, 2},
    d1 = {"\u0000 \n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0010\b\n\u0002\b\u0006\n\u0002\u0010\u000b\n\u0002\b\u0003\n\u0002\u0010\u000e\n\u0000\b\u0086\b\u0018\u00002\u00020\u0001B\r\u0012\u0006\u0010\u0002\u001a\u0010\
    d2 = {"LSample;", "", "counter", "", "(I)V", "getCounter", "()I", "component1", "copy", "equals", "", "other", "hashCode", "toString", "", "production sources for module app"}
    ))

    View Slide

  10. @Metadata(
    k = 1,
    mv = {1, 1, 10},
    bv = {1, 0, 2},
    d1 = {"\u0000 \n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0
    d2 = {"LSample;", "", "counter", "", "(I)V", "getCounter", "()I", "compon
    ))

    View Slide

  11. @Metadata
    k
    mv
    bv
    d1
    d2
    xs
    xi
    pn

    View Slide

  12. @Metadata
    1. kind
    2. metadata version
    3. bytecode version
    4. data1
    5. data2
    6. extra string
    7. extra int
    8. package name

    View Slide

  13. 8. package name fully qualified package (if != from Java)
    (ie. @JvmPackageName)
    (no classes!)

    View Slide

  14. 7. extra int
    0 -> multi-file class facade or part
    1 -> compiled by a pre-release version
    2 -> Kotlin script source file (.kts)

    View Slide

  15. View Slide

  16. “it depends” mostly Protocol Buffers
    4. data1
    5. data2
    6. extra string

    View Slide

  17. 2. metadata version
    3. bytecode version
    ...boooring

    View Slide

  18. 1. kind
    1 -> Class
    2 -> File
    3 -> Synthetic class
    4 -> Multi-file class facade
    5 -> Multi-file class part

    View Slide

  19. * nullability (also nested)
    * secondary constructors
    * companion object name
    * properties list
    * delegated types
    * internal visibility
    * sealed class hierarchy
    * type aliases (declaration and usage)
    * generic types (also in/out /*, reified)
    * classes (data/sealed/object/companion)
    * property (var/getter/setter/const/lateinit)
    * value parameter (default/crossinline/noinline)
    * function (operator/infix/inline/tailrec/external)

    View Slide

  20. View Slide

  21. ( )

    View Slide

  22. square/moshi

    View Slide

  23. square/moshi
    *
    * not the actual logo

    View Slide

  24. google/room

    View Slide

  25. google/room

    View Slide

  26. google/room

    View Slide

  27. jetbrains/kotlinx-metadata-jvm

    View Slide

  28. kotlin-metadata roadmap
    - use jetbrains/kotlinx-metadata-jvm (shadowed)
    - wrap Types/Elements in idiomatic Kotlin API
    - expose type mapping Java <-> Kotlin
    - KotlinPoet integration
    - documentation–HAHAHA JUST KIDDING

    View Slide

  29. goo.gl/nEcGmR -> JetBrains/kotlinx-metadata-jvm
    goo.gl/V26u5C -> Takhion/kotlin-metadata
    goo.gl/yWgvor -> Exploring Moshi’s Kotlin Code Gen
    goo.gl/mZdccu -> Moshi Kotlin Code Gen: An Open Source Story
    goo.gl/FPKqZ7 -> Λrrow annotation processors
    goo.gl/UDNmvh -> Moshi annotation processor
    goo.gl/GBHMqs -> Room annotation processor

    View Slide

  30. Eugenio
    Marle!i
    @workingkills
    bit.ly/kotlin-metadata-apt

    View Slide