Version 1.4.56

Cherry-pick: Support modifiers synthetic and bridge in Proguard configuration files
CL: https://r8-review.googlesource.com/c/r8/+/34571

Bug: 124802124
Change-Id: I705dcefbb92e2540ebcfaebc2b1d25a503376e1e
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 8439083..6b47efc 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.4.55";
+  public static final String LABEL = "1.4.56";
 
   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 9dfddb2..8522f1f 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java
@@ -26,7 +26,9 @@
       "transient",
       "synchronized",
       "native",
-      "strictfp"
+      "strictfp",
+      "synthetic",
+      "bridge"
   );
 
   // Get ordered list of flag predicates. Must be consistent with getNames.
@@ -42,7 +44,9 @@
         this::isTransient,
         this::isSynchronized,
         this::isNative,
-        this::isStrict);
+        this::isStrict,
+        this::isSynthetic,
+        this::isBridge);
   }
 
   private boolean containsAll(int other) {
@@ -163,6 +167,22 @@
     return isSet(Constants.ACC_STRICT);
   }
 
+  public void setSynthetic() {
+    set(Constants.ACC_SYNTHETIC);
+  }
+
+  public boolean isSynthetic() {
+    return isSet(Constants.ACC_SYNTHETIC);
+  }
+
+  public void setBridge() {
+    set(Constants.ACC_BRIDGE);
+  }
+
+  public boolean isBridge() {
+    return isSet(Constants.ACC_BRIDGE);
+  }
+
   private boolean isSet(int flag) {
     return (flags & flag) != 0;
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index eaad9a4..a27f4b0 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -953,6 +953,11 @@
               flags.setAbstract();
             }
             break;
+          case 'b':
+            if ((found = acceptString("bridge"))) {
+              flags.setBridge();
+            }
+            break;
           case 'f':
             if ((found = acceptString("final"))) {
               flags.setFinal();
@@ -979,6 +984,8 @@
               flags.setStatic();
             } else if ((found = acceptString("strictfp"))) {
               flags.setStrict();
+            } else if ((found = acceptString("synthetic"))) {
+              flags.setSynthetic();
             }
             break;
           case 't':
diff --git a/src/test/java/com/android/tools/r8/shaking/modifiers/SyntheticAndBridgeModifiersTest.java b/src/test/java/com/android/tools/r8/shaking/modifiers/SyntheticAndBridgeModifiersTest.java
new file mode 100644
index 0000000..7f205f1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/modifiers/SyntheticAndBridgeModifiersTest.java
@@ -0,0 +1,133 @@
+// 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 com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.function.Consumer;
+import org.junit.Test;
+
+public class SyntheticAndBridgeModifiersTest extends TestBase {
+
+  static final List<Class<?>> CLASSES = ImmutableList.of(
+      SyntheticAndBridgeModifiersTestClass.class,
+      SyntheticAndBridgeModifiersTestClass.Super.class,
+      SyntheticAndBridgeModifiersTestClass.TestClass.class);
+
+  private void runTest(String keepRules, Consumer<ClassSubject> classConsumer) throws Exception {
+    testForProguard()
+        .addProgramClasses(CLASSES)
+        .addKeepRules(keepRules)
+        .noMinification()
+        .compile()
+        .disassemble()
+        .inspect(inspector -> {
+          ClassSubject clazz =
+              inspector.clazz(SyntheticAndBridgeModifiersTestClass.TestClass.class);
+          assertThat(clazz, isPresent());
+          classConsumer.accept(clazz);
+        });
+
+    testForR8(Backend.DEX)
+        .addProgramClasses(CLASSES)
+        .addKeepRules(keepRules)
+        .noMinification()
+        .compile()
+        .disassemble()
+        .inspect(inspector -> {
+          ClassSubject clazz =
+              inspector.clazz(SyntheticAndBridgeModifiersTestClass.TestClass.class);
+          assertThat(clazz, isPresent());
+          classConsumer.accept(clazz);
+        });
+  }
+
+  private long methodsWithNameStartingWith(ClassSubject clazz, String prefix) {
+    return clazz
+        .allMethods()
+        .stream()
+        .filter(method -> method.getOriginalName().startsWith(prefix))
+        .count();
+  }
+
+  @Test
+  public void testBridgeNotKept() throws Exception {
+    runTest(
+        "-keep class **TestClass$TestClass { java.lang.String x(); }",
+        clazz -> {
+          assertThat(clazz.method("java.lang.Object", "x", ImmutableList.of()), not(isPresent()));
+          assertThat(clazz.method("java.lang.String", "x", ImmutableList.of()), isPresent());
+        });
+  }
+
+  @Test
+  public void testBridgeKept() throws Exception {
+    runTest(
+        "-keep class **TestClass$TestClass { bridge ** x(); }",
+        clazz ->
+            assertThat(clazz.method("java.lang.Object", "x", ImmutableList.of()), isPresent()));
+  }
+
+  @Test
+  public void testSyntheticFieldNotKept() throws Exception {
+    runTest(
+        "-keep class **TestClass$TestClass { java.lang.String x(); }",
+        clazz -> assertEquals(0, clazz.allFields().size()));
+  }
+
+  @Test
+  public void testSyntheticFieldKept() throws Exception {
+    runTest(
+        "-keep class **TestClass$TestClass { synthetic ** this*; }",
+        clazz -> assertEquals(1, clazz.allFields().size()));
+  }
+
+  @Test
+  public void testSyntheticAccessorNotKept() throws Exception {
+    runTest(
+        "-keep class **TestClass$TestClass { java.lang.String x(); }",
+        clazz -> assertEquals(0, methodsWithNameStartingWith(clazz, "access")));
+  }
+
+  @Test
+  public void testSyntheticAccessorKept() throws Exception {
+    runTest(
+        "-keep class **TestClass$TestClass { static synthetic *; }",
+        clazz -> assertEquals(1, methodsWithNameStartingWith(clazz, "access")));
+  }
+}
+
+class SyntheticAndBridgeModifiersTestClass {
+  class Super {
+    Object x() {
+      return null;
+    }
+  }
+
+  // Non static inner classes will have a synthetic field for the outer instance (javac
+  // named this$X).
+  class TestClass extends Super {
+    // This will have a synthetic static accessor (javac named access$XXX).
+    private void test() {
+
+    }
+
+    // javac will create a forwarding bridge with signature 'Object x()'.
+    String x() {
+      return null;
+    }
+  }
+
+  void accessPrivate() {
+    new TestClass().test();
+  }
+}
\ No newline at end of file