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