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

R8/ProGuard 徹底比較

Sato Shun
February 07, 2019

R8/ProGuard 徹底比較

DroidKaigi2019:

R8はJavaコードを最適化されたdexコードに変換するためのシュリンカーです。Proguardを置き換える目的で作成されました。R8ではコンパイルタイムの軽減、dexコードのさらなる最適化を目指しています。
dexコードのさらなる最適化とは具体的にどのようなものでしょうか?

本セッションでは、R8でどのような最適化が行われているかをバイトコードレベルから説明します。また、Kotlinに関する最適化など、R8の特徴について説明し、Proguardと比べどこが進化したのかを紹介します。

具体的に以下のことを学ぶことが出来ます。

- R8の内部実装
- R8とProguardの違い
- R8ではどのような最適化が行われているか?

Sato Shun

February 07, 2019
Tweet

More Decks by Sato Shun

Other Decks in Technology

Transcript

  1. R8/ProGuard
    పఈൺֱ
    ࠤ౻ ൏ / Sato Shun

    View full-size slide

  2. ΰʔϧ
    • R8ͷجຊߏ੒ɺ໨తΛ஌Δ

    • ؆୯ͳDalvikόΠτίʔυΛαϙʔτ͋ΓͰಡΊΔΑ͏ʹͳΔ

    • R8Ͱ৽͘͠௥Ճ͞ΕͨઃఆɺKotlinͷ࠷దԽΛ஌Δ

    • (͕࣌ؒ͋Ε͹࣮ફฤ΋…)

    View full-size slide

  3. R8ͷجຊߏ੒

    View full-size slide

  4. R8ɾProGuardͷ΍͍ͬͯΔ͜ͱ
    • όΠτίʔυ࠷దԽ / optimize

    • ෆཁͳίʔυͷ࡟আ / shrink

    • ೉ಡԽ / obfuscate

    View full-size slide

  5. ௨ৗͷϏϧυ
    Dalvik
    όΠτίʔυ
    (.dex)
    javac

    kotlinc
    D8
    Java
    όΠτίʔυ
    (.class)
    ιʔε
    ίʔυ
    (.java/.kt)

    View full-size slide

  6. ProGuardΛ࢖ͬͨϏϧυ
    Optimized
    Dalvik
    όΠτίʔυ
    (.dex)
    javac

    kotlinc
    D8
    Java
    όΠτίʔυ
    (.class)
    ιʔε
    ίʔυ
    (.java/.kt)
    Optimized
    Java
    όΠτίʔυ
    (.class)
    ProGuard

    View full-size slide

  7. R8Λ࢖ͬͨϏϧυ
    Optimized
    Dalvik
    όΠτίʔυ
    (.dex)
    javac

    kotlinc
    R8
    Java
    όΠτίʔυ
    (.class)
    ιʔε
    ίʔυ
    (.java/.kt)

    View full-size slide

  8. R8ͷجຊߏ੒
    • R8͸dexԽͱ࠷దԽ͢Δػೳ͕౷߹ͨ͠

    • εςοϓ਺͕ݮΔ͜ͱͰɺI/Oͷίετ͕Լ͕ΔͳͲɺϏϧυ࣌ؒͷ୹ॖ
    ʹͭͳ͕Δ

    • R8ͱD8͸ಉ͡ϦϙδτϦͰ։ൃ͞Ε͓ͯΓɺଟ͘ͷίʔυΛ
    ڞ༗͍ͯ͠Δ

    View full-size slide

  9. JackΛ࢖ͬͨϏϧυ
    Optimized
    Dalvik
    όΠτίʔυ
    (.dex)
    Jack/Jill
    ιʔε
    ίʔυ
    (.java/.kt)

    View full-size slide

  10. Jack্͕ख͍͔͘ͳ͔ͬͨཧ༝
    • JavaͷΤίγεςϜʹ৐Δ͜ͱ͕ग़དྷͳ͔ͬͨ

    • ͍͔ͭ͘ͷϥΠϒϥϦ͕ಈ͔ͳ͍ͳͲ

    • R8Ͱ͸JavaͷΤίγεςϜʹߟྀͨ͠ߏ੒ʹͳ͍ͬͯΔʂ

    View full-size slide

  11. R8ͱProGuardઃఆͷޓ׵ੑ

    View full-size slide

  12. R8ͱProGuardઃఆͷޓ׵ੑ
    • R8ͱProGuard͸ޓ׵ੑ͕͋Δ

    • ࠓ࢖͍ͬͯΔProGuardͷઃఆϑΝΠϧΛͦͷ··࢖͑Δ

    • ͨͩ͠ɺ͍͔ͭ͘ͷઃఆ͕ແޮʹͳ͍ͬͯΔ

    • ·ͨɺ͍͔ͭ͘ͷઃఆ͕௥Ճ͞Ε͍ͯΔ

    View full-size slide

  13. R8Ͱແޮͳओͳઃఆ
    • addconfigurationdebuggingɺdumpͳͲͷσόοάʹศརͳΦ
    ϓγϣϯ͕࢖͑ͳ͍

    View full-size slide

  14. addconfigurationdebugging?
    • Runtime࣌ʹΫϥογϡͨ͠Βɺ଍Γ͍ͯͳ͍ProGuardͷઃఆ
    ΛఏҊͯ͘͠ΕΔ

    View full-size slide

  15. addconfigurationdebuggingͷྫ
    • GsonͰઆ໌͢Δ

    • Gson͸ҎԼͷProGuardઃఆ͕ඞཁ

    • -keep class * implements com.google.gson.JsonDeserializer

    • ͜ͷઃఆ͕ͳ͍৔߹ʹͲ͏ͳΔ͔?

    View full-size slide

  16. E Process: com.github.satoshun.example, PID: 11495
    E java.lang.RuntimeException: Unable to start activity
    ComponentInfo{com.github.satoshun.example/com.github.satoshun.example.MainActivity}:
    java.lang.Il
    legalArgumentException: Invalid attempt to bind an instance of
    com.github.satoshun.example.TestDeserializer as a @JsonAdapter for com.github.satoshun.
    example.Hoge. @JsonAdapter value must be a TypeAdapter, TypeAdapterFactory,
    JsonSerializer or JsonDeserializer.
    E at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913)
    E at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)

    View full-size slide

  17. E Process: com.github.satoshun.example, PID: 11495
    E java.lang.RuntimeException: Unable to start activity
    ComponentInfo{com.github.satoshun.example/com.github.satoshun.example.MainActivity}:
    java.lang.Il
    legalArgumentException: Invalid attempt to bind an instance of
    com.github.satoshun.example.TestDeserializer as a @JsonAdapter for com.github.satoshun.
    example.Hoge. @JsonAdapter value must be a TypeAdapter, TypeAdapterFactory,
    JsonSerializer or JsonDeserializer.
    E at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913)
    E at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
    -addconfigurationdebuggingΛ
    ઃఆʹ௥Ճ

    View full-size slide

  18. ProGuard W The class 'com.google.gson.internal.ConstructorConstructor' is calling
    Class.getDeclaredConstructor
    W on class 'com.github.satoshun.example.TestDeserializer' to retrieve
    W the constructor with signature (), but the latter could not be found.
    W It may have been obfuscated or shrunk.
    W You should consider preserving the constructor, with a setting like:
    W -keepclassmembers class com.github.satoshun.example.TestDeserializer {
    W public ();
    W }

    View full-size slide

  19. ProGuard W The class 'com.google.gson.internal.ConstructorConstructor' is calling
    Class.getDeclaredConstructor
    W on class 'com.github.satoshun.example.TestDeserializer' to retrieve
    W the constructor with signature (), but the latter could not be found.
    W It may have been obfuscated or shrunk.
    W You should consider preserving the constructor, with a setting like:
    W -keepclassmembers class com.github.satoshun.example.TestDeserializer {
    W public ();
    W }

    View full-size slide

  20. R8Ͱݱঢ়ແޮͳओͳઃఆ
    • ։ൃऀ͕ҙࣝ͢Δແޮͳઃఆ͸σόοάपΓ͘Β͍ͩͱࢥ͏

    • جຊతʹR8/ProGuard͸ޓ׵ੑ͕͋ΔͷͰɺ໰୊͕͓͖ͨΒɺ
    ProGuardΛONʹͯ͠ɺσόοάઃఆΛ௥Ճͯ͠ௐࠪ͢Δͷ͸
    ͋Γ

    View full-size slide

  21. R8Ͱ௥Ճ͞Εͨઃఆ
    • identifiernamestring

    • checkdiscard

    • assumevalues

    View full-size slide

  22. identifiernamestring?
    • Ϋϥεɺϝϯόʔ໊͕೉ಡԽͰมߋ͞Εͨͱ͖ʹɺจࣈྻ΋௥
    ैͯ͠มߋͯ͘͠ΕΔ

    • ϦϑϨΫγϣϯͳͲͰ༗ޮ

    View full-size slide

  23. checkdiscard
    • ର৅ͷΫϥεɺϝϯόʔ͕
    ফ͍͑ͯΔ͔Λ֬ೝ͢Δ

    • ΠϯϥΠϯԽͰ͋ͬͨΓɺ
    unusedͰফ͞Ε͍ͯΕ͹͓̺
    fun noInline(i: Int) {

    }
    —-
    -checkdiscard class ** {
    *** noInline(***);
    }
    —-
    Item void CheckDiscardKt.noInline(int) was
    not discarded.
    Error: Discard checks failed.

    View full-size slide

  24. assumevalues
    • ஋ͷऔΓ͏ΔൣғΛࢦఆ͢Δ͜ͱ͕ग़དྷΔ

    • Ϣʔεέʔε: android.os.Build.VERSION.SDK_INT

    • ͜ͷ஋͸minSdkʹΑͬͯൣғ஋͕ܾ·ΔͷͰɺassumevaluesઃఆͰ࠷ద
    Խ͢Δ͜ͱ͕Ͱ͖Δ

    View full-size slide

  25. assumevaluesͷྫ
    ͜ͷͱ͖ɺminSdk͕16ͩ
    ͱɺelse۟ʹདྷΔ͜ͱ͸ͳ
    ͍
    if (Build.VERSION.SDK_INT >= 16) {
    println("true")
    } else {
    println("false")
    }

    View full-size slide

  26. assumevaluesͷྫ
    if (Build.VERSION.SDK_INT >= 16) {
    println("true")
    } else {
    println("false")
    }
    -assumevalues class android.os.Build$VERSION {
    int SDK_INT return 16..2147483647;
    }

    View full-size slide

  27. assumevaluesͷྫ
    if (Build.VERSION.SDK_INT >= 16) {
    println("true")
    } else {
    println("false")
    }
    -assumevalues class android.os.Build$VERSION {
    int SDK_INT return 16..2147483647;
    }
    if (true) {
    println("true")
    } else {
    println("false")
    }

    View full-size slide

  28. assumevaluesͷྫ
    if (Build.VERSION.SDK_INT >= 16) {
    println("true")
    } else {
    println("false")
    }
    -assumevalues class android.os.Build$VERSION {
    int SDK_INT return 16..2147483647;
    }
    if (true) {
    println("true")
    } else {
    println("false")
    }

    View full-size slide

  29. assumevaluesͷྫ
    if (Build.VERSION.SDK_INT >= 16) {
    println("true")
    } else {
    println("false")
    }
    -assumevalues class android.os.Build$VERSION {
    int SDK_INT return 16..2147483647;
    }
    println("true") CompatܥͷϥΠϒϥϦͰ͸ԼҐޓ׵ʹ഑ྀ͍ͯ͠Δ
    ίʔυ͕ଟ͍ͨΊɺ͔ͳΓͷίʔυ࡟ݮ͕ظ଴Ͱ͖
    Δɻ
    ·ͨɺAGP 3.4.0-beta02͔Β͸্هͷઃఆ͕σ
    ϑΥϧτͰೖΔΑ͏ʹͳͬͨʂʂ

    View full-size slide

  30. DalvikόΠτίʔυΛಡΉ

    View full-size slide

  31. DalvikόΠτίʔυ?
    • https://source.android.com/devices/tech/dalvik/dalvik-
    bytecode

    • Dalvik/ART VM্Ͱ࣮ߦ͢Δ͜ͱ͕ग़དྷΔػցޠ

    • D8/R8ίϚϯυͰม׵͞ΕΔ

    View full-size slide

  32. DalvikόΠτίʔυΛಡΉ
    • R8/ProGuardʹ͓͍ͯɺಛʹॏཁͳϙΠϯτ͸

    • ΫϥεɺϝιουɺϑΟʔϧυͷએݴ

    • ϝιουͷத਎

    • ͷͨΊɺ͜ͷ2ͭΛத৺ʹઆ໌͠·͢

    View full-size slide

  33. public class HelloWorld {
    private int a = 111;
    public void main() {
    System.out.println("Hello World!!" + a);
    }
    }

    View full-size slide

  34. $ javac *.java

    View full-size slide

  35. $ javac *.java
    $ ls
    HelloWorld.class …

    View full-size slide

  36. $ javac *.java
    $ ls
    HelloWorld.class …
    $ java -jar $R8_HOME/build/libs/d8.jar
    \ --release
    \ *.class
    $ ls
    classes.dex …

    View full-size slide

  37. $ javac *.java
    $ ls
    HelloWorld.class …
    $ java -jar $R8_HOME/build/libs/d8.jar
    \ --release
    \ *.class
    $ ls
    classes.dex …
    $ $ANDROID_HOME/build-tools/28.0.3/dexdump -d classes.dex
    Opened 'classes.dex', DEX version '035'
    Class #0 -
    Class descriptor : ‘LHelloWorld;'

    View full-size slide

  38. Class #0 -
    Class descriptor : 'LHelloWorld;'
    Access flags : 0x0001 (PUBLIC)
    Superclass : 'Ljava/lang/Object;'
    Interfaces -
    Static fields -
    Instance fields -
    #0 : (in LHelloWorld;)
    name : 'a'
    type : 'I'
    access : 0x0002 (PRIVATE)

    View full-size slide

  39. Class #0 -
    Class descriptor : 'LHelloWorld;'
    Access flags : 0x0001 (PUBLIC)
    Superclass : 'Ljava/lang/Object;'
    Interfaces -
    Static fields -
    Instance fields -
    #0 : (in LHelloWorld;)
    name : 'a'
    type : 'I'
    access : 0x0002 (PRIVATE)
    class HelloWorld

    View full-size slide

  40. Class #0 -
    Class descriptor : 'LHelloWorld;'
    Access flags : 0x0001 (PUBLIC)
    Superclass : 'Ljava/lang/Object;'
    Interfaces -
    Static fields -
    Instance fields -
    #0 : (in LHelloWorld;)
    name : 'a'
    type : 'I'
    access : 0x0002 (PRIVATE)
    public class HelloWorld

    View full-size slide

  41. Class #0 -
    Class descriptor : 'LHelloWorld;'
    Access flags : 0x0001 (PUBLIC)
    Superclass : 'Ljava/lang/Object;'
    Interfaces -
    Static fields -
    Instance fields -
    #0 : (in LHelloWorld;)
    name : 'a'
    type : 'I'
    access : 0x0002 (PRIVATE)
    public class HelloWorld

    View full-size slide

  42. Class #0 -
    Class descriptor : 'LHelloWorld;'
    Access flags : 0x0001 (PUBLIC)
    Superclass : 'Ljava/lang/Object;'
    Interfaces -
    Static fields -
    Instance fields -
    #0 : (in LHelloWorld;)
    name : 'a'
    type : 'I'
    access : 0x0002 (PRIVATE)
    public class HelloWorld

    View full-size slide

  43. Class #0 -
    Class descriptor : 'LHelloWorld;'
    Access flags : 0x0001 (PUBLIC)
    Superclass : 'Ljava/lang/Object;'
    Interfaces -
    Static fields -
    Instance fields -
    #0 : (in LHelloWorld;)
    name : 'a'
    type : 'I'
    access : 0x0002 (PRIVATE)
    public class HelloWorld

    View full-size slide

  44. Class #0 -
    Class descriptor : 'LHelloWorld;'
    Access flags : 0x0001 (PUBLIC)
    Superclass : 'Ljava/lang/Object;'
    Interfaces -
    Static fields -
    Instance fields -
    #0 : (in LHelloWorld;)
    name : 'a'
    type : 'I'
    access : 0x0002 (PRIVATE)
    public class HelloWorld {
    private int a;
    }

    View full-size slide

  45. Direct methods -
    #0 : (in LHelloWorld;)
    name : ''
    type : '()V'
    access : 0x10001 (PUBLIC CONSTRUCTOR)

    [000194] HelloWorld.:()V
    0000: invoke-direct {v1}, Ljava/lang/Object;.:()V // method@0003
    0003: const/16 v0, #int 111 // #6f
    0005: iput v0, v1, LHelloWorld;.a:I // field@0000
    0007: return-void

    View full-size slide

  46. Direct methods -
    #0 : (in LHelloWorld;)
    name : ''
    type : '()V'
    access : 0x10001 (PUBLIC CONSTRUCTOR)

    [000194] HelloWorld.:()V
    0000: invoke-direct {v1}, Ljava/lang/Object;.:()V // method@0003
    0003: const/16 v0, #int 111 // #6f
    0005: iput v0, v1, LHelloWorld;.a:I // field@0000
    0007: return-void

    public class HelloWorld {
    private int a;
    public HelloWorld() {
    }
    }

    View full-size slide


  47. [000194] HelloWorld.:()V
    0000: invoke-direct {v1}, Ljava/lang/Object;.:()V // method@0003
    0003: const/16 v0, #int 111 // #6f
    0005: iput v0, v1, LHelloWorld;.a:I // field@0000
    0007: return-void

    View full-size slide


  48. [000194] HelloWorld.:()V
    0000: invoke-direct {v1}, Ljava/lang/
    Object;.:()V // method@0003
    0003: const/16 v0, #int 111 // #6f
    0005: iput v0, v1, LHelloWorld;.a:I // field@0000
    0007: return-void
    … public class HelloWorld {
    private int a;
    public HelloWorld() {
    super();
    }
    }

    View full-size slide


  49. [000194] HelloWorld.:()V
    0000: invoke-direct {v1}, Ljava/lang/Object;.:()V // method@0003
    0003: const/16 v0, #int 111 // #6f
    0005: iput v0, v1, LHelloWorld;.a:I // field@0000
    0007: return-void

    public class HelloWorld {
    private int a;
    public HelloWorld() {
    super();
    int v0 = 111;
    }
    }

    View full-size slide


  50. [000194] HelloWorld.:()V
    0000: invoke-direct {v1}, Ljava/lang/Object;.:()V // method@0003
    0003: const/16 v0, #int 111 // #6f
    0005: iput v0, v1, LHelloWorld;.a:I // field@0000
    0007: return-void

    public class HelloWorld {
    private int a;
    public HelloWorld() {
    super();
    (int v0 = 111;)
    this.a = v0;
    }
    }

    View full-size slide


  51. [000194] HelloWorld.:()V
    0000: invoke-direct {v1}, Ljava/lang/Object;.:()V // method@0003
    0003: const/16 v0, #int 111 // #6f
    0005: iput v0, v1, LHelloWorld;.a:I // field@0000
    0007: return-void

    public class HelloWorld {
    private int a;
    public HelloWorld() {
    super();
    (int v0 = 111;)
    this.a = v0;
    }
    }

    View full-size slide

  52. Virtual methods -
    #0 : (in LHelloWorld;)
    name : 'main'
    type : '()V'
    access : 0x0001 (PUBLIC)

    [0001b4] HelloWorld.main:()V
    0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0001
    0002: new-instance v1, Ljava/lang/StringBuilder; // type@0005
    0004: invoke-direct {v1}, Ljava/lang/StringBuilder;.:()V // method@0004
    0007: const-string v2, "Hello World!!" // string@0001
    0009: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/
    lang/StringBuilder; // method@0006
    000c: iget v2, v3, LHelloWorld;.a:I // field@0000
    000e: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/
    StringBuilder; // method@0005
    0011: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; //
    method@0007
    0014: move-result-object v1
    0015: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V //
    method@0002
    0018: return-void

    View full-size slide

  53. Virtual methods -
    #0 : (in LHelloWorld;)
    name : 'main'
    type : '()V'
    access : 0x0001 (PUBLIC)

    [0001b4] HelloWorld.main:()V
    0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0001
    0002: new-instance v1, Ljava/lang/StringBuilder; // type@0005
    0004: invoke-direct {v1}, Ljava/lang/StringBuilder;.:()V // method@0004
    0007: const-string v2, "Hello World!!" // string@0001
    0009: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/
    lang/StringBuilder; // method@0006
    000c: iget v2, v3, LHelloWorld;.a:I // field@0000
    000e: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/
    StringBuilder; // method@0005
    0011: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; //
    method@0007
    0014: move-result-object v1
    0015: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V //
    method@0002
    0018: return-void

    public class HelloWorld {
    private int a;

    public void main() {
    }
    }

    View full-size slide

  54. [0001b4] HelloWorld.main:()V
    0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0001
    0002: new-instance v1, Ljava/lang/StringBuilder; // type@0005
    0004: invoke-direct {v1}, Ljava/lang/StringBuilder;.:()V // method@0004
    0007: const-string v2, "Hello World!!" // string@0001
    0009: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/
    lang/StringBuilder; // method@0006
    000c: iget v2, v3, LHelloWorld;.a:I // field@0000
    000e: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/
    StringBuilder; // method@0005
    0011: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; //
    method@0007
    0014: move-result-object v1
    0015: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V //
    method@0002
    0018: return-void

    View full-size slide

  55. [0001b4] HelloWorld.main:()V
    0000: sget-object v0, Ljava/lang/System;.out:Ljava/
    io/PrintStream; // field@0001
    0002: new-instance v1, Ljava/lang/StringBuilder; // type@0005
    0004: invoke-direct {v1}, Ljava/lang/StringBuilder;.:()V // method@0004
    0007: const-string v2, "Hello World!!" // string@0001
    0009: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/
    lang/StringBuilder; // method@0006
    000c: iget v2, v3, LHelloWorld;.a:I // field@0000
    000e: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/
    StringBuilder; // method@0005
    0011: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; //
    method@0007
    0014: move-result-object v1
    0015: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V //
    method@0002
    0018: return-void

    public class HelloWorld {
    private int a;

    public void main() {
    java.io.PrintStream v0 = System.out;
    }
    }

    View full-size slide

  56. [0001b4] HelloWorld.main:()V
    0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0001
    0002: new-instance v1, Ljava/lang/StringBuilder; //
    type@0005
    0004: invoke-direct {v1}, Ljava/lang/
    StringBuilder;.:()V // method@0004
    0007: const-string v2, "Hello World!!" // string@0001
    0009: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/
    lang/StringBuilder; // method@0006
    000c: iget v2, v3, LHelloWorld;.a:I // field@0000
    000e: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/
    StringBuilder; // method@0005
    0011: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; //
    method@0007
    0014: move-result-object v1
    0015: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V //
    method@0002
    0018: return-void

    public class HelloWorld {
    private int a;

    public void main() {
    java.io.PrintStream v0 = System.out;
    java.lang.StringBuilder v1 = new StringBuilder();
    }
    }

    View full-size slide

  57. [0001b4] HelloWorld.main:()V
    0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0001
    0002: new-instance v1, Ljava/lang/StringBuilder; // type@0005
    0004: invoke-direct {v1}, Ljava/lang/StringBuilder;.:()V // method@0004
    0007: const-string v2, "Hello World!!" //
    string@0001
    0009: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/
    lang/StringBuilder; // method@0006
    000c: iget v2, v3, LHelloWorld;.a:I // field@0000
    000e: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/
    StringBuilder; // method@0005
    0011: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; //
    method@0007
    0014: move-result-object v1
    0015: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V //
    method@0002
    0018: return-void

    public class HelloWorld {
    private int a;

    public void main() {
    java.io.PrintStream v0 = System.out;
    java.lang.StringBuilder v1 = new StringBuilder();
    String v2 = "Hello World!!”;
    }
    }

    View full-size slide


  58. 0009: invoke-virtual {v1, v2}, Ljava/lang/
    StringBuilder;.append:(Ljava/lang/String;)Ljava/
    lang/StringBuilder; // method@0006
    000c: iget v2, v3, LHelloWorld;.a:I // field@0000
    000e: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/
    StringBuilder; // method@0005
    0011: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; //
    method@0007
    0014: move-result-object v1
    0015: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V //
    method@0002
    0018: return-void

    public class HelloWorld {
    private int a;

    public void main() {
    java.io.PrintStream v0 = System.out;
    java.lang.StringBuilder v1 = new StringBuilder();
    String v2 = "Hello World!!”;
    v1.append(v2);
    }
    }

    View full-size slide


  59. lang/StringBuilder; // method@0006
    000c: iget v2, v3, LHelloWorld;.a:I // field@0000
    000e: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/
    StringBuilder; // method@0005
    0011: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; //
    method@0007
    0014: move-result-object v1
    0015: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V //
    method@0002
    0018: return-void

    public class HelloWorld {
    private int a;

    public void main() {
    java.io.PrintStream v0 = System.out;
    java.lang.StringBuilder v1 = new StringBuilder();
    String v2 = "Hello World!!”;
    v1.append(v2);
    int v2 = this.a;
    }
    }

    View full-size slide


  60. 000e: invoke-virtual {v1, v2}, Ljava/lang/
    StringBuilder;.append:(I)Ljava/lang/
    StringBuilder; // method@0005
    0011: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; //
    method@0007
    0014: move-result-object v1
    0015: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V //
    method@0002
    0018: return-void

    public class HelloWorld {
    private int a;

    public void main() {
    java.io.PrintStream v0 = System.out;
    java.lang.StringBuilder v1 = new StringBuilder();
    String v2 = "Hello World!!”;
    v1.append(v2);
    int v2 = this.a;
    v1.append(v2);
    }

    View full-size slide


  61. 0011: invoke-virtual {v1}, Ljava/lang/
    StringBuilder;.toString:()Ljava/lang/String; //
    method@0007
    0014: move-result-object v1
    0015: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V //
    method@0002
    0018: return-void

    public class HelloWorld {
    private int a;

    public void main() {

    v1.append(v2);
    v1.toString();
    }
    }

    View full-size slide

  62. [0001b4] HelloWorld.main:()V

    0014: move-result-object v1
    0015: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V //
    method@0002
    0018: return-void

    public class HelloWorld {
    private int a;

    public void main() {

    v1.append(v2);
    v1.toString();
    }
    }

    View full-size slide

  63. [0001b4] HelloWorld.main:()V

    0015: invoke-virtual {v0, v1}, Ljava/io/
    PrintStream;.println:(Ljava/lang/String;)V //
    method@0002
    0018: return-void
    … public class HelloWorld {
    private int a;

    public void main() {

    v1.append(v2);
    v0.println(v1.toString());
    }
    }

    View full-size slide

  64. [0001b4] HelloWorld.main:()V
    0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0001
    0002: new-instance v1, Ljava/lang/StringBuilder; // type@0005
    0004: invoke-direct {v1}, Ljava/lang/StringBuilder;.:()V // method@0004
    0007: const-string v2, "Hello World!!" // string@0001
    0009: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/
    lang/StringBuilder; // method@0006
    000c: iget v2, v3, LHelloWorld;.a:I // field@0000
    000e: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/
    StringBuilder; // method@0005
    0011: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; //
    method@0007
    0014: move-result-object v1
    0015: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V //
    method@0002
    0018: return-void

    View full-size slide

  65. public class HelloWorld {
    private int a;
    public HelloWorld() {
    super();
    (int v0 = 111;)
    this.a = v0;
    }
    public void main() {
    java.io.PrintStream v0 = System.out;
    java.lang.StringBuilder v1 = new StringBuilder();
    String v2 = "Hello World!!”;
    v1.append(v2);
    int v2 = this.a;
    v1.append(v2);
    v0.println(v1.toString());
    }
    }

    View full-size slide

  66. DalvikόΠτίʔυ·ͱΊ
    • dexdumpɺD8Λ࢖͍DalvikόΠτίʔυʢdumpͨ͠ίʔυʣ
    ΛಡΜͩ

    • ϝιουͷத਎ɺએݴΛಡΉ͚ͩͳΒɺDalvikόΠτίʔυͦ
    Μͳʹ೉͘͠ͳ͍Α!!

    View full-size slide

  67. R8ͷKotlin࠷దԽ

    View full-size slide

  68. Kotlin Companion Object

    View full-size slide

  69. Kotlin Companion Object?
    • JavaͰ͍͏ͱ͜Ζͷstaticϝιου͕දݱͰ͖Δ

    • ΫϥεܧঝɺΠϯλʔϑΣʔε࣮૷͕Ͱ͖Δ

    JavaΑΓ΋ػೳ͕૿͕͑ͨɺ

    ΠϯελϯεΛ಺෦తʹੜ੒͢ΔͷͰίετ͕͔͔Δ

    View full-size slide

  70. class CompanionTest {
    companion object {
    fun show(i: Int) {
    println("started show method")
    println("processing show method action $i")
    println("finished show method")
    }
    }
    }

    View full-size slide

  71. public final class CompanionTest {
    public static final Companion Companion = new Companion();
    public static final class Companion {
    private Companion() {
    }
    public /* synthetic */ Companion(DefaultConstructorMarker
    defaultConstructorMarker) {
    this();
    }
    public final void show(int i) {
    System.out.println("started show method");
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append("processing show method action ");
    stringBuilder.append(i);
    System.out.println(stringBuilder.toString());
    System.out.println("finished show method");
    }
    }
    }

    View full-size slide

  72. public final class CompanionTest {
    public static final Companion Companion = new Companion();
    public static final class Companion {
    private Companion() {
    }
    public /* synthetic */ Companion(DefaultConstructorMarker
    defaultConstructorMarker) {
    this();
    }
    public final void show(int i) {
    System.out.println("started show method");
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append("processing show method action ");
    stringBuilder.append(i);
    System.out.println(stringBuilder.toString());
    System.out.println("finished show method");
    }
    }
    }
    CompanionΠϯελϯε͕ੜ੒͞ΕΔ &
    NestedΫϥε͕ఆٛ͞ΕΔ

    View full-size slide

  73. public final class CompanionTest {
    public static final Companion Companion = new Companion();
    public static final class Companion {
    private Companion() {
    }
    public /* synthetic */ Companion(DefaultConstructorMarker
    defaultConstructorMarker) {
    this();
    }
    public final void show(int i) {
    System.out.println("started show method");
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append("processing show method action ");
    stringBuilder.append(i);
    System.out.println(stringBuilder.toString());
    System.out.println("finished show method");
    }
    }
    }
    CompanionΠϯελϯε͕ੜ੒͞ΕΔ &
    NestedΫϥε͕ఆٛ͞ΕΔ
    ͜ΕΛR8ͰίϯύΠϧ͢Δ

    View full-size slide

  74. public abstract class CompanionTest {
    public static final void show(int i) {
    System.out.println("started show method");
    StringBuilder stringBuilder = new
    StringBuilder();
    stringBuilder.append("processing show method
    action ");
    stringBuilder.append(i);
    System.out.println(stringBuilder.toString());
    System.out.println("finished show method");
    }
    }

    View full-size slide

  75. public abstract class CompanionTest {
    public static final void show(int i) {
    System.out.println("started show method");
    StringBuilder stringBuilder = new
    StringBuilder();
    stringBuilder.append("processing show method
    action ");
    stringBuilder.append(i);
    System.out.println(stringBuilder.toString());
    System.out.println("finished show method");
    }
    }
    Πϯελϯεੜ੒͕ແ͘ͳͬͨ &
    NestedΫϥε͕ແ͘ͳͬͨ

    View full-size slide

  76. public abstract class CompanionTest {
    public static final void show(int i) {
    System.out.println("started show method");
    StringBuilder stringBuilder = new
    StringBuilder();
    stringBuilder.append("processing show method
    action ");
    stringBuilder.append(i);
    System.out.println(stringBuilder.toString());
    System.out.println("finished show method");
    }
    }
    R8Ͱ͸staticϝιουʹม׵͞ΕΔͨΊɺ
    ΠϯελϯεΛੜ੒͠ͳͯ͘΋ྑ͘ͳ͍ͬͯΔ

    View full-size slide

  77. public final class CompanionTest {
    public static final Companion Companion = new Companion();
    public static final class Companion {
    private Companion() {
    }
    public /* synthetic */ Companion(byte b) {
    this();
    }
    public static void show(int i) {
    System.out.println("started show method");
    System.out.println("processing show method action
    ".concat(String.valueOf(i)));
    System.out.println("finished show method");
    }
    }
    }

    View full-size slide

  78. public final class CompanionTest {
    public static final Companion Companion = new Companion();
    public static final class Companion {
    private Companion() {
    }
    public /* synthetic */ Companion(byte b) {
    this();
    }
    public static void show(int i) {
    System.out.println("started show method");
    System.out.println("processing show method action
    ".concat(String.valueOf(i)));
    System.out.println("finished show method");
    }
    }
    }

    View full-size slide

  79. public final class CompanionTest {
    public static final Companion Companion = new Companion();
    public static final class Companion {
    private Companion() {
    }
    public /* synthetic */ Companion(byte b) {
    this();
    }
    public static void show(int i) {
    System.out.println("started show method");
    System.out.println("processing show method action
    ".concat(String.valueOf(i)));
    System.out.println("finished show method");
    }
    }
    }
    ProGuradͰ͸CompanionΠϯελϯε͕ফ͑ͳ͍ &
    NestedΫϥε΋ఆٛ͞Εͨ··

    View full-size slide

  80. Companion Object·ͱΊ
    • KotlinͰstaticϝιουΛදݱ͢ΔͷʹCompanion ObjectΛ࢖
    ͏ͱɺJavaΑΓ΋ύϑΥʔϚϯε໘ͰෆརʹͳΔ

    • R8͸ͦͷ఺Λߟྀ͠ɺ࠷దԽΛߦ͏

    • Javaͷstaticϝιουͱಉ͡࢖͍ํΛ͍ͯ͠Δͱ͖ͷΈ༗ޮͳ
    ࠷దԽ

    • ΠϯλʔϑΣʔεͷ࣮૷Λ͍ͯ͠Δ৔߹ʹ͸CompanionΠϯελϯε͸ফ
    ͑ͳ͍

    View full-size slide

  81. Kotlin lambda࠷దԽ

    View full-size slide

  82. Kotlin lambda?
    • KotlinͰ͸ϥϜμ͕ࣜ࢖͑Δ

    • ͔ؔ͠͠਺ݺͼग़͠ɺΠϯελϯεੜ੒͸ίετֻ͕͔Δ

    View full-size slide

  83. fun main() {
    lambdaTest { println("Kotlin lambda1") }
    lambdaTest { println("Kotlin lambda2") }
    lambdaTest { println("Kotlin lambda3") }
    }
    private fun lambdaTest(body: () -> Unit) {
    println("before lambdaTest")
    body()
    println("after lambdaTest")
    }

    View full-size slide

  84. sget-object v0, LLambdaTestKt$main$1;.INSTANCE:LLambdaTestKt$main$1;
    invoke-static {v0}, LLambdaTestKt;.lambdaTest:(Lkotlin/jvm/functions/
    Function0;)V
    sget-object v0, LLambdaTestKt$main$2;.INSTANCE:LLambdaTestKt$main$2;
    invoke-static {v0}, LLambdaTestKt;.lambdaTest:(Lkotlin/jvm/functions/
    Function0;)V
    sget-object v0, LLambdaTestKt$main$3;.INSTANCE:LLambdaTestKt$main$3;
    invoke-static {v0}, LLambdaTestKt;.lambdaTest:(Lkotlin/jvm/functions/
    Function0;)V

    View full-size slide

  85. sget-object v0, LLambdaTestKt$main$1;.INSTANCE:LLambdaTestKt$main$1;
    invoke-static {v0}, LLambdaTestKt;.lambdaTest:(Lkotlin/jvm/functions/
    Function0;)V
    sget-object v0, LLambdaTestKt$main$2;.INSTANCE:LLambdaTestKt$main$2;
    invoke-static {v0}, LLambdaTestKt;.lambdaTest:(Lkotlin/jvm/functions/
    Function0;)V
    sget-object v0, LLambdaTestKt$main$3;.INSTANCE:LLambdaTestKt$main$3;
    invoke-static {v0}, LLambdaTestKt;.lambdaTest:(Lkotlin/jvm/functions/
    Function0;)V

    View full-size slide

  86. sget-object v0, LLambdaTestKt$main$1;.INSTANCE:LLambdaTestKt$main$1;
    invoke-static {v0}, LLambdaTestKt;.lambdaTest:(Lkotlin/jvm/functions/
    Function0;)V
    sget-object v0, LLambdaTestKt$main$2;.INSTANCE:LLambdaTestKt$main$2;
    invoke-static {v0}, LLambdaTestKt;.lambdaTest:(Lkotlin/jvm/functions/
    Function0;)V
    sget-object v0, LLambdaTestKt$main$3;.INSTANCE:LLambdaTestKt$main$3;
    invoke-static {v0}, LLambdaTestKt;.lambdaTest:(Lkotlin/jvm/functions/
    Function0;)V
    3ͭͷΫϥε͕ੜ੒͞Εͨ
    - ϥϜμຖʹΫϥε͕ੜ੒͞ΕΔ

    View full-size slide

  87. sget-object v0, L-$
    $LambdaGroup$ks$BpS7w8o_KOkdSUy0gHGt84S7irI;.INSTANCE$0:L-$
    $LambdaGroup$ks$BpS7w8o_KOkdSUy0gHGt84S7irI;
    invoke-static {v0}, LLambdaTestKt;.lambdaTest:(Lkotlin/jvm/functions/
    Function0;)V
    sget-object v0, L-$
    $LambdaGroup$ks$BpS7w8o_KOkdSUy0gHGt84S7irI;.INSTANCE$1:L-$
    $LambdaGroup$ks$BpS7w8o_KOkdSUy0gHGt84S7irI;
    invoke-static {v0}, LLambdaTestKt;.lambdaTest:(Lkotlin/jvm/functions/
    Function0;)V
    sget-object v0, L-$
    $LambdaGroup$ks$BpS7w8o_KOkdSUy0gHGt84S7irI;.INSTANCE$2:L-$
    $LambdaGroup$ks$BpS7w8o_KOkdSUy0gHGt84S7irI;
    invoke-static {v0}, LLambdaTestKt;.lambdaTest:(Lkotlin/jvm/functions/
    Function0;)V

    View full-size slide

  88. sget-object v0, L-$
    $LambdaGroup$ks$BpS7w8o_KOkdSUy0gHGt84S7irI;.INSTANCE$0:L-$
    $LambdaGroup$ks$BpS7w8o_KOkdSUy0gHGt84S7irI;
    invoke-static {v0}, LLambdaTestKt;.lambdaTest:(Lkotlin/jvm/functions/
    Function0;)V
    sget-object v0, L-$
    $LambdaGroup$ks$BpS7w8o_KOkdSUy0gHGt84S7irI;.INSTANCE$1:L-$
    $LambdaGroup$ks$BpS7w8o_KOkdSUy0gHGt84S7irI;
    invoke-static {v0}, LLambdaTestKt;.lambdaTest:(Lkotlin/jvm/functions/
    Function0;)V
    sget-object v0, L-$
    $LambdaGroup$ks$BpS7w8o_KOkdSUy0gHGt84S7irI;.INSTANCE$2:L-$
    $LambdaGroup$ks$BpS7w8o_KOkdSUy0gHGt84S7irI;
    invoke-static {v0}, LLambdaTestKt;.lambdaTest:(Lkotlin/jvm/functions/
    Function0;)V

    View full-size slide

  89. sget-object v0, L-$
    $LambdaGroup$ks$BpS7w8o_KOkdSUy0gHGt84S7irI;.INSTANCE$0:L-$
    $LambdaGroup$ks$BpS7w8o_KOkdSUy0gHGt84S7irI;
    invoke-static {v0}, LLambdaTestKt;.lambdaTest:(Lkotlin/jvm/functions/
    Function0;)V
    sget-object v0, L-$
    $LambdaGroup$ks$BpS7w8o_KOkdSUy0gHGt84S7irI;.INSTANCE$1:L-$
    $LambdaGroup$ks$BpS7w8o_KOkdSUy0gHGt84S7irI;
    invoke-static {v0}, LLambdaTestKt;.lambdaTest:(Lkotlin/jvm/functions/
    Function0;)V
    sget-object v0, L-$
    $LambdaGroup$ks$BpS7w8o_KOkdSUy0gHGt84S7irI;.INSTANCE$2:L-$
    $LambdaGroup$ks$BpS7w8o_KOkdSUy0gHGt84S7irI;
    invoke-static {v0}, LLambdaTestKt;.lambdaTest:(Lkotlin/jvm/functions/
    Function0;)V
    LambdaGroupͱݺ͹ΕΔ࠷దԽ
    1ͭͷΫϥεʹϥϜμΛ·ͱΊΔ͜ͱͰɺ
    ΫϥεɺϝιουΛݮΒ͢͜ͱ͕Ͱ͖Δ

    View full-size slide

  90. // R8.java
    private void run(
    AndroidApp inputApp,
    ExecutorService executorService)
    throws IOException {

    View full-size slide

  91. // R8.java

    IRConverter converter =
    new IRConverter(appView, options, timing,
    printer, mainDexClasses, rootSet);
    converter.optimize(application, executorService);

    View full-size slide

  92. // R8.java

    IRConverter converter =
    new IRConverter(appView, options, timing,
    printer, mainDexClasses, rootSet);
    converter.optimize(application, executorService);
    IR: Intermediate Representations
    ιʔείʔυͱ࠷ऴ੒Ռͷؒʹ͋Δதؒදݱܗࣜ

    View full-size slide

  93. // R8.java

    IRConverter converter =
    new IRConverter(appView, options, timing,
    printer, mainDexClasses, rootSet);
    converter.optimize(application, executorService);

    View full-size slide

  94. // IRConverter.java
    public DexApplication optimize(
    DexApplication application,
    ExecutorService executorService
    ) throws ExecutionException {
    computeReachabilitySensitivity(application);
    removeLambdaDeserializationMethods();
    collectLambdaMergingCandidates(application);
    collectStaticizerCandidates(application);

    View full-size slide

  95. // IRConverter.java
    public DexApplication optimize(
    DexApplication application,
    ExecutorService executorService
    ) throws ExecutionException {
    computeReachabilitySensitivity(application);
    removeLambdaDeserializationMethods();
    collectLambdaMergingCandidates(application);
    collectStaticizerCandidates(application);

    View full-size slide

  96. private void collectLambdaMergingCandidates(
    DexApplication application
    ) {
    if (lambdaMerger != null) {
    lambdaMerger.collectGroupCandidates(
    application,
    appInfo.withLiveness(),
    options
    );
    }
    }

    View full-size slide

  97. private void collectLambdaMergingCandidates(
    DexApplication application
    ) {
    if (lambdaMerger != null) {
    lambdaMerger.collectGroupCandidates(
    application,
    appInfo.withLiveness(),
    options
    );
    }
    }

    View full-size slide

  98. private void collectLambdaMergingCandidates(
    DexApplication application
    ) {
    if (lambdaMerger != null) {
    lambdaMerger.collectGroupCandidates(
    application,
    appInfo.withLiveness(),
    options
    );
    }
    } LambdaMerger:
    ෳ਺ͷϥϜμΛ1ͭͷάϧʔϓʹϚʔδ͢Δ

    View full-size slide

  99. // LambdaMerger.collectGroupCandidates
    app.classes()
    .stream()
    .filter(cls -> !infoWithLiveness.isPinned(cls.type))
    .filter(cls -> cls.hasKotlinInfo() &&
    cls.getKotlinInfo().isSyntheticClass() &&
    cls.getKotlinInfo().asSyntheticClass().isLambda())
    .sorted((a, b) -> a.type.slowCompareTo(b.type))
    .forEachOrdered(lambda -> {
    try {
    LambdaGroupId id =
    KotlinLambdaGroupIdFactory.create(kotlin, lambda, options);
    LambdaGroup group =
    groups.computeIfAbsent(id, LambdaGroupId::createGroup);
    group.add(lambda);
    lambdas.put(lambda.type, group);
    } catch (LambdaStructureError error) {

    }
    });

    View full-size slide

  100. app.classes()
    .stream()
    .filter(cls -> !infoWithLiveness.isPinned(cls.type))
    .filter(cls -> cls.hasKotlinInfo() &&
    cls.getKotlinInfo().isSyntheticClass() &&
    cls.getKotlinInfo().asSyntheticClass().isLambda())
    .sorted((a, b) -> a.type.slowCompareTo(b.type))
    .forEachOrdered(lambda -> {
    try {
    LambdaGroupId id =
    KotlinLambdaGroupIdFactory.create(kotlin, lambda, options);
    LambdaGroup group =
    groups.computeIfAbsent(id, LambdaGroupId::createGroup);
    group.add(lambda);
    lambdas.put(lambda.type, group);
    } catch (LambdaStructureError error) {

    }
    });

    View full-size slide

  101. app.classes()
    .stream()
    .filter(cls -> !infoWithLiveness.isPinned(cls.type))
    .filter(cls -> cls.hasKotlinInfo() &&
    cls.getKotlinInfo().isSyntheticClass() &&
    cls.getKotlinInfo().asSyntheticClass().isLambda())
    .sorted((a, b) -> a.type.slowCompareTo(b.type))
    .forEachOrdered(lambda -> {
    try {
    LambdaGroupId id =
    KotlinLambdaGroupIdFactory.create(kotlin, lambda, options);
    LambdaGroup group =
    groups.computeIfAbsent(id, LambdaGroupId::createGroup);
    group.add(lambda);
    lambdas.put(lambda.type, group);
    } catch (LambdaStructureError error) {

    }
    });
    pin͞Ε͍ͯͳ͍ == มܗ͕ՄೳͳΫϥεΛநग़

    View full-size slide

  102. app.classes()
    .stream()
    .filter(cls -> !infoWithLiveness.isPinned(cls.type))
    .filter(cls -> cls.hasKotlinInfo() &&
    cls.getKotlinInfo().isSyntheticClass() &&
    cls.getKotlinInfo().asSyntheticClass().isLambda())
    .sorted((a, b) -> a.type.slowCompareTo(b.type))
    .forEachOrdered(lambda -> {
    try {
    LambdaGroupId id =
    KotlinLambdaGroupIdFactory.create(kotlin, lambda, options);
    LambdaGroup group =
    groups.computeIfAbsent(id, LambdaGroupId::createGroup);
    group.add(lambda);
    lambdas.put(lambda.type, group);
    } catch (LambdaStructureError error) {

    }
    });
    Kotlin &&
    ߹੒Ϋϥε &&
    ϥϜμࣜ

    View full-size slide

  103. app.classes()
    .stream()
    .filter(cls -> !infoWithLiveness.isPinned(cls.type))
    .filter(cls -> cls.hasKotlinInfo() &&
    cls.getKotlinInfo().isSyntheticClass() &&
    cls.getKotlinInfo().asSyntheticClass().isLambda())
    .sorted((a, b) -> a.type.slowCompareTo(b.type))
    .forEachOrdered(lambda -> {
    try {
    LambdaGroupId id =
    KotlinLambdaGroupIdFactory.create(kotlin, lambda, options);
    LambdaGroup group =
    groups.computeIfAbsent(id, LambdaGroupId::createGroup);
    group.add(lambda);
    lambdas.put(lambda.type, group);
    } catch (LambdaStructureError error) {

    }
    });
    classͷॱ൪Λιʔτ

    View full-size slide

  104. app.classes()
    .stream()
    .filter(cls -> !infoWithLiveness.isPinned(cls.type))
    .filter(cls -> cls.hasKotlinInfo() &&
    cls.getKotlinInfo().isSyntheticClass() &&
    cls.getKotlinInfo().asSyntheticClass().isLambda())
    .sorted((a, b) -> a.type.slowCompareTo(b.type))
    .forEachOrdered(lambda -> {
    try {
    LambdaGroupId id =
    KotlinLambdaGroupIdFactory.create(kotlin, lambda, options);
    LambdaGroup group =
    groups.computeIfAbsent(id, LambdaGroupId::createGroup);
    group.add(lambda);
    lambdas.put(lambda.type, group);
    } catch (LambdaStructureError error) {

    }
    });
    LambdaGroupIdͱLambdaGroup
    Λ࡞Δ

    View full-size slide

  105. app.classes()
    .stream()
    .filter(cls -> !infoWithLiveness.isPinned(cls.type))
    .filter(cls -> cls.hasKotlinInfo() &&
    cls.getKotlinInfo().isSyntheticClass() &&
    cls.getKotlinInfo().asSyntheticClass().isLambda())
    .sorted((a, b) -> a.type.slowCompareTo(b.type))
    .forEachOrdered(lambda -> {
    try {
    LambdaGroupId id =
    KotlinLambdaGroupIdFactory.create(kotlin, lambda, options);
    LambdaGroup group =
    groups.computeIfAbsent(id, LambdaGroupId::createGroup);
    group.add(lambda);
    lambdas.put(lambda.type, group);
    } catch (LambdaStructureError error) {

    }
    });
    LambdaGroupIdΠϯελϯεͷग़ྗ
    capture:
    interface: Lkotlin/jvm/functions/Function0;
    package:
    signature: null
    main method name: invoke
    main method: Proto V void
    main annotations: com.android.tools.r8.graph.DexAnnotationSet@1
    main param annotations:
    com.android.tools.r8.graph.ParameterAnnotationsList@1
    inner: none

    View full-size slide

  106. app.classes()
    .stream()
    .filter(cls -> !infoWithLiveness.isPinned(cls.type))
    .filter(cls -> cls.hasKotlinInfo() &&
    cls.getKotlinInfo().isSyntheticClass() &&
    cls.getKotlinInfo().asSyntheticClass().isLambda())
    .sorted((a, b) -> a.type.slowCompareTo(b.type))
    .forEachOrdered(lambda -> {
    try {
    LambdaGroupId id =
    KotlinLambdaGroupIdFactory.create(kotlin, lambda, options);
    LambdaGroup group =
    groups.computeIfAbsent(id, LambdaGroupId::createGroup);
    group.add(lambda);
    lambdas.put(lambda.type, group);
    } catch (LambdaStructureError error) {

    }
    });

    View full-size slide

  107. // IRConverter.java
    public DexApplication optimize(
    DexApplication application,
    ExecutorService executorService
    ) throws ExecutionException {
    computeReachabilitySensitivity(application);
    removeLambdaDeserializationMethods();
    collectLambdaMergingCandidates(application);
    collectStaticizerCandidates(application);

    View full-size slide

  108. // IRConverter.java
    public DexApplication optimize(
    DexApplication application,
    ExecutorService executorService
    ) throws ExecutionException {

    printPhase("Lambda merging finalization");
    finalizeLambdaMerging(
    application, feedback, builder,
    executorService, synthesizedClasses
    );

    View full-size slide

  109. private void finalizeLambdaMerging(
    DexApplication application,
    OptimizationFeedback directFeedback,
    Builder> builder,
    ExecutorService executorService,
    Map
    synthesizedClasses)
    throws ExecutionException {
    if (lambdaMerger != null) {
    lambdaMerger.applyLambdaClassMapping(
    application, this, directFeedback, builder,
    executorService, synthesizedClasses);
    }
    }

    View full-size slide

  110. private void finalizeLambdaMerging(
    DexApplication application,
    OptimizationFeedback directFeedback,
    Builder> builder,
    ExecutorService executorService,
    Map
    synthesizedClasses)
    throws ExecutionException {
    if (lambdaMerger != null) {
    lambdaMerger.applyLambdaClassMapping(
    application, this, directFeedback, builder,
    executorService, synthesizedClasses);
    }
    }

    View full-size slide

  111. // LambdaMerger.java
    public final void applyLambdaClassMapping(…) {


    View full-size slide

  112. // LambdaMerger.java
    public final void applyLambdaClassMapping(…) {


    rewriteLambdaReferences(converter, synthesizedClasses, feedback);

    View full-size slide

  113. // LambdaMerger.java
    private void rewriteLambdaReferences(…) {

    View full-size slide

  114. // LambdaMerger.java
    private void rewriteLambdaReferences(…) {

    for (DexEncodedMethod method : methods) {
    DexEncodedMethod mappedMethod =
    converter.graphLense().mapDexEncodedMethod(
    method, converter.appInfo, synthesizedClasses);

    }

    View full-size slide

  115. // LambdaMerger.java
    private void rewriteLambdaReferences(…) {

    for (DexEncodedMethod method : methods) {
    DexEncodedMethod mappedMethod =
    converter.graphLense().mapDexEncodedMethod(
    method, converter.appInfo, synthesizedClasses);

    }

    View full-size slide

  116. // LambdaMerger.java
    private void rewriteLambdaReferences(…) {

    for (DexEncodedMethod method : methods) {
    DexEncodedMethod mappedMethod =
    converter.graphLense().mapDexEncodedMethod(
    method, converter.appInfo, synthesizedClasses);

    } GraphLense:
    ίʔυ৘ใΛ֨ೲ͓ͯ͠Γɺ
    ϝιουɺϑΟʔϧυͷҠಈɺ
    ΫϥεͷmappingͳͲΛߦ͏ɻ

    View full-size slide

  117. private void rewriteLambdaReferences(…) {

    for (DexEncodedMethod method : methods) {
    DexEncodedMethod mappedMethod =
    converter.graphLense().mapDexEncodedMethod(
    method, converter.appInfo, synthesizedClasses);

    }
    mapDexEncodedMethod:
    ੜ੒ͨ͠߹੒ΫϥεʢLambdaGroupʣΛGraphLenseʹ
    ొ࿥͢Δ

    View full-size slide

  118. private void rewriteLambdaReferences(…) {

    for (DexEncodedMethod method : methods) {
    DexEncodedMethod mappedMethod =
    converter.graphLense().mapDexEncodedMethod(
    method, converter.appInfo, synthesizedClasses);

    }

    View full-size slide

  119. LambdaGroupςΫχοΫ·ͱΊ
    • KotlinͷϥϜμ͸ɺϥϜμຖʹΫϥεΛ࡞ͬͯ͠·͏ͷͰΫϥ
    εɺϝιου਺͕૿͑Δ

    • 1ΫϥεʹϥϜμΛ·ͱΊΔ͜ͱͰޮ཰తʹ͢Δ

    • 1ػೳʹߜͬͯॲཧΛ௥͑͹ɺR8ίϯύΠϥͷίʔυ΋ͳΜͱ
    ͳ͘ಡΊΔ

    View full-size slide

  120. inlineϝιουԽ
    • R8Ͱ͸ϝιου͕1Օॴ͔Β͔͠ίʔϧ͞Ε͍ͯͳ͍ or ϝιο
    υ͕୹͍৔߹ʹࣗಈతʹΠϯϥΠϯԽ͞ΕΔ

    • ProGuardͰ΋ಉ༷ʹ͞ΕΔ

    • ࣮ࡍʹͲͷΑ͏ʹม׵͞ΕΔ͔ɺڻ͖ͱڞʹઆ໌͢Δ

    • Retrofitͷ࿩

    • Daggerͷ࿩

    View full-size slide

  121. Retrofitฤ

    View full-size slide


  122. val retrofit = Retrofit
    .Builder()
    .build()
    val api = retrofit.create(Api::class.java)
    api.hoge().execute()
    }

    interface Api {
    @GET fun hoge(): Call
    }

    View full-size slide

  123. .method public onCreate(Landroid/os/Bundle;)V
    .line 1
    sget-object p1, Lretrofit2/Platform;->a:Lretrofit2/Platform;
    .line 2
    new-instance p1, Ljava/util/ArrayList;
    invoke-direct {p1}, Ljava/util/ArrayList;->()V
    new-instance p1, Ljava/util/ArrayList;
    invoke-direct {p1}, Ljava/util/ArrayList;->()V
    .line 3
    new-instance p1, Ljava/lang/IllegalStateException;
    const-string v0, "Base URL required."
    invoke-direct {p1, v0}, Ljava/lang/IllegalStateException;-
    >(Ljava/lang/String;)V
    throw p1
    .end method

    View full-size slide

  124. ...
    Platform platform = Platform.a;
    ArrayList arrayList = new ArrayList();
    arrayList = new ArrayList();
    throw new IllegalStateException("Base URL
    required.");
    }

    View full-size slide

  125. ...
    Platform platform = Platform.a;
    ArrayList arrayList = new ArrayList();
    arrayList = new ArrayList();
    throw new IllegalStateException("Base URL
    required.");
    }
    IllegalStateExceptionͰϝιου͕
    ऴΘͬͯΔ

    View full-size slide

  126. public static final class Builder {
    private @Nullable HttpUrl baseUrl;
    public Builder baseUrl(String baseUrl) {
    checkNotNull(baseUrl, "baseUrl == null");
    return baseUrl(HttpUrl.get(baseUrl));
    }
    public Retrofit build() {
    if (baseUrl == null) {
    throw new IllegalStateException("Base URL
    required.");
    }

    }
    }

    View full-size slide

  127. public static final class Builder {
    private @Nullable HttpUrl baseUrl;
    public Builder baseUrl(String baseUrl) {
    checkNotNull(baseUrl, "baseUrl == null");
    return baseUrl(HttpUrl.get(baseUrl));
    }
    public Retrofit build() {
    if (baseUrl == null) {
    throw new IllegalStateException("Base URL
    required.");
    }

    }
    }
    baseUrl͕nullͳΒthrow͍ͯ͠Δ

    View full-size slide


  128. val retrofit = Retrofit
    .Builder()
    .build()
    val api = retrofit.create(Api::class.java)
    api.hoge().execute()
    }

    View full-size slide


  129. Platform platform = Platform.a;
    ArrayList arrayList = new ArrayList();
    arrayList = new ArrayList();
    if (baseUrl == null) {
    throw new IllegalStateException("Base URL
    required.");
    }
    val api = retrofit.create(Api::class.java)
    api.hoge().execute()
    }

    View full-size slide


  130. Platform platform = Platform.a;
    ArrayList arrayList = new ArrayList();
    arrayList = new ArrayList();
    if (null == null) {
    throw new IllegalStateException("Base URL
    required.");
    }
    val api = retrofit.create(Api::class.java)
    api.hoge().execute()
    }

    View full-size slide


  131. Platform platform = Platform.a;
    ArrayList arrayList = new ArrayList();
    arrayList = new ArrayList();
    if (true) {
    throw new IllegalStateException("Base URL
    required.");
    }
    val api = retrofit.create(Api::class.java)
    api.hoge().execute()
    }

    View full-size slide


  132. Platform platform = Platform.a;
    ArrayList arrayList = new ArrayList();
    arrayList = new ArrayList();
    throw new IllegalStateException("Base URL
    required.");
    }

    View full-size slide

  133. class MainActivity : AppCompatActivity() {
    @Inject lateinit var appService: AppService
    @Inject lateinit var appService2: AppService2
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    val appComponent = DaggerAppComponent.builder().build()
    appComponent.inject(this)
    appService.test().execute()
    appService2.test().execute()
    }
    }
    interface AppService {
    @GET("todos/1")
    fun test(): Call
    }
    interface AppService2 {
    @GET("todos/1")
    fun test(): Call
    }

    View full-size slide

  134. method public onCreate(Landroid/os/Bundle;)V
    .registers 3
    invoke-super {p0, p1}, La/b/e/a/m;->onCreate(Landroid/os/Bundle;)V
    const p1, 0x7f09001c
    invoke-virtual {p0, p1}, La/b/e/a/m;->setContentView(I)V
    .line 1
    new-instance p1, Lcom/github/satoshun/example/AppModule1;
    invoke-direct {p1}, Lcom/github/satoshun/example/AppModule1;->()V
    .line 2
    invoke-virtual {p1}, Lcom/github/satoshun/example/AppModule1;->a()Lcom/github/satoshun/example/AppService;
    move-result-object p1
    const-string v0, "Cannot return null from a non-@Nullable @Provides method"
    invoke-static {p1, v0}, La/b/b/a/a/a;->a(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;
    .line 3
    iput-object p1, p0, Lcom/github/satoshun/example/MainActivity;->o:Lcom/github/satoshun/example/AppService;
    .line 4
    invoke-static {}, Lcom/github/satoshun/example/AppModule2_ProvideService2Factory;->a()Lcom/github/satoshun/example/AppService2;
    move-result-object p1
    .line 5
    iput-object p1, p0, Lcom/github/satoshun/example/MainActivity;->p:Lcom/github/satoshun/example/AppService2;
    .line 6
    iget-object p1, p0, Lcom/github/satoshun/example/MainActivity;->o:Lcom/github/satoshun/example/AppService;
    const/4 v0, 0x0
    if-eqz p1, :cond_3d
    invoke-interface {p1}, Lcom/github/satoshun/example/AppService;->a()Lretrofit2/Call;
    move-result-object p1
    invoke-interface {p1}, Lretrofit2/Call;->execute()Lretrofit2/Response;
    iget-object p1, p0, Lcom/github/satoshun/example/MainActivity;->p:Lcom/github/satoshun/example/AppService2;
    if-eqz p1, :cond_37
    invoke-interface {p1}, Lcom/github/satoshun/example/AppService2;->a()Lretrofit2/Call;

    View full-size slide

  135. public final class MainActivity extends m {
    public AppService o;
    public AppService2 p;
    public void onCreate(Bundle bundle) {
    super.onCreate(bundle);
    setContentView((int) R.layout.activity_main);
    Object a = new AppModule1().a();
    a.a(a, "Cannot return null from a non-@Nullable @Provides method");
    this.o = a;
    this.p = AppModule2_ProvideService2Factory.a();
    AppService appService = this.o;
    if (appService != null) {
    appService.a().execute();
    AppService2 appService2 = this.p;
    if (appService2 != null) {
    appService2.a().execute();
    return;
    } else {
    e.a.a.a.a("appService2");
    throw …;
    }
    }
    e.a.a.a.a("appService");
    throw …;
    }
    }

    View full-size slide

  136. public final class MainActivity extends m {
    public AppService o;
    public AppService2 p;
    public void onCreate(Bundle bundle) {
    super.onCreate(bundle);
    setContentView((int) R.layout.activity_main);
    Object a = new AppModule1().a();
    a.a(a, "Cannot return null from a non-@Nullable @Provides method");
    this.o = a;
    this.p = AppModule2_ProvideService2Factory.a();
    AppService appService = this.o;
    if (appService != null) {
    appService.a().execute();
    AppService2 appService2 = this.p;
    if (appService2 != null) {
    appService2.a().execute();
    return;
    } else {
    e.a.a.a.a("appService2");
    throw …;
    }
    }
    e.a.a.a.a("appService");
    throw …;
    }
    }
    AppComponent͕ফ͑ͨ!!
    AppComponentͷ֤ϝιου͕
    inlineԽ͞ΕɺऔΓআ͔Εͨɻ

    View full-size slide

  137. ·ͱΊ/ײ૝
    • R8͸Ϗϧυ΋ૣ͘ͳΔ͠ɺapkαΠζ΋খ͘͞ͳΔͷͰྑ͞

    • ݱঢ়ͩͱυΩϡϝϯτΒ͍͠υΩϡϝϯτͳ͍ͷ͕ਏ͍

    • Happy R8 Life

    View full-size slide

  138. ࣗݾ঺հ
    • ࠤ౻ ൏/͞ͱ͏ ͠ΎΜ

    • ϚονϯάΤʔδΣϯτɺCyberAgent, inc. ॴଐ

    • @stsn_jp

    • github.com/satoshun

    View full-size slide