Version 1.5.13-q1


Cherry-pick: Ensure compatdx and compatproguard proper extensions of R8.
CL: https://r8-review.googlesource.com/c/r8/+/38640

Cherry-pick: Correctly handle multiple access modifiers in Proguard rules
CL: https://r8-review.googlesource.com/c/r8/+/36702

Bug: 132911943
Bug: 129781627
Change-Id: Id2d87642bb18bb08e5fa252f3e51575b8f7f9113
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 3e368b0..a0e04a4 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "1.5.13";
+  public static final String LABEL = "1.5.13-q1";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java b/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java
index e857165..1593543 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java
@@ -12,6 +12,9 @@
 
 public class ProguardAccessFlags {
 
+  private final static int PPP_MASK =
+      new ProguardAccessFlags().setPublic().setProtected().setPrivate().flags;
+
   private int flags = 0;
 
   // Ordered list of flag names. Must be consistent with getPredicates.
@@ -50,7 +53,12 @@
   }
 
   private boolean containsAll(int other) {
-    return (flags & other) == flags;
+    // ppp flags are the flags public, protected and private.
+    return
+        // All non-ppp flags set must match (a 0 in non-ppp flags means don't care).
+        (((flags & ~PPP_MASK) & (other & ~PPP_MASK)) == (flags & ~PPP_MASK))
+            // With no ppp flags any flags match, with ppp flags there must be an overlap to match.
+            && (((flags & PPP_MASK) == 0) || ((flags & PPP_MASK) & (other & PPP_MASK)) != 0);
   }
 
   private boolean containsNone(int other) {
@@ -69,24 +77,27 @@
     this.flags = other.getOriginalAccessFlags();
   }
 
-  public void setPublic() {
+  public ProguardAccessFlags setPublic() {
     set(Constants.ACC_PUBLIC);
+    return this;
   }
 
   public boolean isPublic() {
     return isSet(Constants.ACC_PUBLIC);
   }
 
-  public void setPrivate() {
+  public ProguardAccessFlags setPrivate() {
     set(Constants.ACC_PRIVATE);
+    return this;
   }
 
   public boolean isPrivate() {
     return isSet(Constants.ACC_PRIVATE);
   }
 
-  public void setProtected() {
+  public ProguardAccessFlags setProtected() {
     set(Constants.ACC_PROTECTED);
+    return this;
   }
 
   public boolean isProtected() {
diff --git a/src/main/keep-compatdx.txt b/src/main/keep-compatdx.txt
index 75a8012..e4c5688 100644
--- a/src/main/keep-compatdx.txt
+++ b/src/main/keep-compatdx.txt
@@ -2,5 +2,9 @@
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
 
--keep public class com.android.tools.r8.compatdx.CompatDx { public static void main(java.lang.String[]); }
--keepattributes LineNumberTable
+# This entry must remain a proper extension of R8 to ensure all required parts are kept.
+-include keep.txt
+
+-keep public class com.android.tools.r8.compatdx.CompatDx {
+  public static void main(java.lang.String[]);
+}
diff --git a/src/main/keep-compatproguard.txt b/src/main/keep-compatproguard.txt
index e29d13e..80ca009 100644
--- a/src/main/keep-compatproguard.txt
+++ b/src/main/keep-compatproguard.txt
@@ -2,5 +2,9 @@
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
 
--keep public class com.android.tools.r8.compatproguard.CompatProguard { public static void main(java.lang.String[]); }
--keepattributes LineNumberTable
+# This entry must remain a proper extension of R8 to ensure all required parts are kept.
+-include keep.txt
+
+-keep public class com.android.tools.r8.compatproguard.CompatProguard {
+  public static void main(java.lang.String[]);
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/modifiers/AccessFlagsCombinationsTest.java b/src/test/java/com/android/tools/r8/shaking/modifiers/AccessFlagsCombinationsTest.java
new file mode 100644
index 0000000..fc70ae2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/modifiers/AccessFlagsCombinationsTest.java
@@ -0,0 +1,209 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking.modifiers;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.function.Consumer;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class AccessFlagsCombinationsTest extends TestBase {
+
+  enum Expect {
+    STATICS_ONLY,
+    NON_STATICS_ONLY,
+    BOTH_STATICS_AND_NON_STATICS
+  }
+
+  private final TestParameters parameters;
+  private static final int PPP =
+      Constants.ACC_PUBLIC | Constants.ACC_PROTECTED | Constants.ACC_PRIVATE;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().build();
+  }
+
+  public AccessFlagsCombinationsTest(TestParameters parameters) {
+    assertEquals(7, PPP);
+    this.parameters = parameters;
+  }
+
+  private void checkKeptMembers(CodeInspector inspector, Integer flags, Expect expect) {
+    ClassSubject classSubject = inspector.clazz(TestClass.class);
+
+    boolean includeStatic =
+        expect == Expect.STATICS_ONLY || expect == Expect.BOTH_STATICS_AND_NON_STATICS;
+    boolean includeNonStatic =
+        expect == Expect.NON_STATICS_ONLY || expect == Expect.BOTH_STATICS_AND_NON_STATICS;
+
+
+    boolean includePublic = (flags & PPP) == 0 || (flags & Constants.ACC_PUBLIC) != 0;
+    boolean includeProtected = (flags & PPP) == 0 || (flags & Constants.ACC_PROTECTED) != 0;
+    boolean includePrivate = (flags & PPP) == 0 || (flags & Constants.ACC_PRIVATE) != 0;
+    boolean includePackagePrivate = (flags & PPP) == 0;
+
+    assertEquals(
+        includePublic && includeNonStatic,
+        classSubject.uniqueMethodWithName("publicMethod").isPresent());
+    assertEquals(
+        includeProtected && includeNonStatic,
+        classSubject.uniqueMethodWithName("protectedMethod").isPresent());
+    assertEquals(
+        includePrivate && includeNonStatic,
+        classSubject.uniqueMethodWithName("privateMethod").isPresent());
+    assertEquals(
+        includePackagePrivate && includeNonStatic,
+        classSubject.uniqueMethodWithName("packagePrivateMethod").isPresent());
+    assertEquals(
+        includePublic && includeStatic,
+        classSubject.uniqueMethodWithName("publicStaticMethod").isPresent());
+    assertEquals(
+        includeProtected && includeStatic,
+        classSubject.uniqueMethodWithName("protectedStaticMethod").isPresent());
+    assertEquals(
+        includePrivate && includeStatic,
+        classSubject.uniqueMethodWithName("privateStaticMethod").isPresent());
+    assertEquals(
+        includePackagePrivate && includeStatic,
+        classSubject.uniqueMethodWithName("packagePrivateStaticMethod").isPresent());
+    assertEquals(
+        includePublic && includeNonStatic,
+        classSubject.uniqueFieldWithName("publicField").isPresent());
+    assertEquals(
+        includeProtected && includeNonStatic,
+        classSubject.uniqueFieldWithName("protectedField").isPresent());
+    assertEquals(
+        includePrivate && includeNonStatic,
+        classSubject.uniqueFieldWithName("privateField").isPresent());
+    assertEquals(
+        includePackagePrivate && includeNonStatic,
+        classSubject.uniqueFieldWithName("packagePrivateField").isPresent());
+    assertEquals(
+        includePublic && includeStatic,
+        classSubject.uniqueFieldWithName("publicStaticField").isPresent());
+    assertEquals(
+        includeProtected && includeStatic,
+        classSubject.uniqueFieldWithName("protectedStaticField").isPresent());
+    assertEquals(
+        includePrivate && includeStatic,
+        classSubject.uniqueFieldWithName("privateStaticField").isPresent());
+    assertEquals(
+        includePackagePrivate && includeStatic,
+        classSubject.uniqueFieldWithName("packagePrivateStaticField").isPresent());
+  }
+
+  public void runTest(List<String> keepRules, Consumer<CodeInspector> inspector) throws Exception {
+    String expectedOutput = StringUtils.lines("Hello, world");
+    testForR8(parameters.getBackend())
+        .addInnerClasses(AccessFlagsCombinationsTest.class)
+        .addKeepMainRule(TestClass.class)
+        .addKeepRules(keepRules)
+        // Commented out for the 1.5.13 branch as this is currently not merged.
+        //.setMinApi(parameters.getRuntime())
+        .compile()
+        .inspect(inspector)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(expectedOutput);
+  }
+
+  private String flagString(int flags) {
+    return new StringBuilder()
+        .append((flags & Constants.ACC_PUBLIC) != 0 ? " public" : "")
+        .append((flags & Constants.ACC_PROTECTED) != 0 ? " protected" : "")
+        .append((flags & Constants.ACC_PRIVATE) != 0 ? " private" : "")
+        .toString();
+  }
+
+  @Test
+  public void testImplicitBothStaticAndNonStaticMembers() throws Exception {
+    for (int flags = 0; flags <= PPP; flags++) {
+      Integer finalFlags = flags;
+      runTest(
+          ImmutableList.of("-keep class **.*TestClass {" + flagString(flags) + " *; }"),
+          inspector ->
+              checkKeptMembers(inspector, finalFlags, Expect.BOTH_STATICS_AND_NON_STATICS));
+    }
+  }
+
+  @Test
+  public void testExplicitNotStaticMembers() throws Exception {
+    for (int flags = 0; flags <= PPP; flags++) {
+      Integer finalFlags = flags;
+      runTest(
+          ImmutableList.of("-keep class **.*TestClass { !static" + flagString(flags) + " *; }"),
+          inspector -> checkKeptMembers(inspector, finalFlags, Expect.NON_STATICS_ONLY));
+    }
+  }
+
+  @Test
+  public void testStaticMembers() throws Exception {
+    for (int flags = 0; flags <= PPP; flags++) {
+      Integer finalFlags = flags | Constants.ACC_STATIC;
+      runTest(
+          ImmutableList.of("-keep class **.*TestClass { static" + flagString(flags) + " *; }"),
+          inspector -> checkKeptMembers(inspector, finalFlags, Expect.STATICS_ONLY));
+    }
+  }
+
+  static class TestClass {
+    public static int publicStaticField;
+    protected static int protectedStaticField;
+    private static int privateStaticField;
+    static int packagePrivateStaticField;
+    public int publicField;
+    protected int protectedField;
+    private int privateField;
+    int packagePrivateField;
+
+    public static void publicStaticMethod() {
+
+    }
+
+    protected static void protectedStaticMethod() {
+
+    }
+
+    private static void privateStaticMethod() {
+
+    }
+
+    static void packagePrivateStaticMethod() {
+
+    }
+
+    public void publicMethod() {
+
+    }
+
+    protected void protectedMethod() {
+
+    }
+
+    private void privateMethod() {
+
+    }
+
+    void packagePrivateMethod() {
+
+    }
+
+    public static void main(String[] args) {
+      System.out.println("Hello, world");
+    }
+  }
+}
\ No newline at end of file