Allow matching on dex constructor access flag in rules
Bug: 233137460
Change-Id: I208d57ba7ff8f3d2255864d74d11257725958c8d
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 7f9ff8c..9bd37a5 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java
@@ -18,21 +18,22 @@
private int flags = 0;
// Ordered list of flag names. Must be consistent with getPredicates.
- private static final List<String> NAMES = ImmutableList.of(
- "public",
- "private",
- "protected",
- "static",
- "final",
- "abstract",
- "volatile",
- "transient",
- "synchronized",
- "native",
- "strictfp",
- "synthetic",
- "bridge"
- );
+ private static final List<String> NAMES =
+ ImmutableList.of(
+ "public",
+ "private",
+ "protected",
+ "static",
+ "final",
+ "abstract",
+ "volatile",
+ "transient",
+ "synchronized",
+ "native",
+ "strictfp",
+ "synthetic",
+ "bridge",
+ "constructor");
// Get ordered list of flag predicates. Must be consistent with getNames.
private List<BooleanSupplier> getPredicates() {
@@ -49,7 +50,8 @@
this::isNative,
this::isStrict,
this::isSynthetic,
- this::isBridge);
+ this::isBridge,
+ this::isConstructor);
}
private boolean containsAll(int other) {
@@ -194,6 +196,14 @@
return isSet(Constants.ACC_BRIDGE);
}
+ public void setConstructor() {
+ set(Constants.ACC_CONSTRUCTOR);
+ }
+
+ public boolean isConstructor() {
+ return isSet(Constants.ACC_CONSTRUCTOR);
+ }
+
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 c4af05f..0a45e17 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -1275,6 +1275,11 @@
flags.setBridge();
}
break;
+ case 'c':
+ if ((found = acceptString("constructor"))) {
+ flags.setConstructor();
+ }
+ break;
case 'f':
if ((found = acceptString("final"))) {
flags.setFinal();
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
index bf21d1f..94ab239 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
@@ -4,89 +4,16 @@
package com.android.tools.r8.shaking;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.ProguardConfigurationParser.IdentifierPatternWithWildcards;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.LongInterval;
import com.google.common.collect.ImmutableList;
-import java.util.Arrays;
import java.util.List;
-import java.util.stream.Collectors;
public class ProguardConfigurationUtils {
- private static Origin proguardCompatOrigin =
- new Origin(Origin.root()) {
- @Override
- public String part() {
- return "<PROGUARD_COMPATIBILITY_RULE>";
- }
- };
-
- private static Origin synthesizedRecompilationOrigin =
- new Origin(Origin.root()) {
- @Override
- public String part() {
- return "<SYNTHESIZED_RECOMPILATION_RULE>";
- }
- };
-
- public static ProguardKeepRule buildDefaultInitializerKeepRule(DexClass clazz) {
- ProguardKeepRule.Builder builder = ProguardKeepRule.builder();
- builder.setOrigin(proguardCompatOrigin);
- builder.setType(ProguardKeepRuleType.KEEP);
- builder.getModifiersBuilder().setAllowsObfuscation(true);
- builder.getModifiersBuilder().setAllowsOptimization(true);
- builder.getClassAccessFlags().setVisibility(clazz.accessFlags);
- builder.setClassType(ProguardClassType.CLASS);
- builder.setClassNames(
- ProguardClassNameList.singletonList(ProguardTypeMatcher.create(clazz.type)));
- if (clazz.hasDefaultInitializer()) {
- ProguardMemberRule.Builder memberRuleBuilder = ProguardMemberRule.builder();
- memberRuleBuilder.setRuleType(ProguardMemberType.INIT);
- memberRuleBuilder.setName(IdentifierPatternWithWildcards.withoutWildcards("<init>"));
- memberRuleBuilder.setArguments(ImmutableList.of());
- builder.getMemberRules().add(memberRuleBuilder.build());
- }
- return builder.build();
- }
-
- public static ProguardKeepRule buildMethodKeepRule(DexClass clazz, DexEncodedMethod method) {
- // TODO(b/122295241): These generated rules should be linked into the graph, eg, the method
- // using identified reflection should be the source keeping the target alive.
- assert clazz.type == method.getHolderType();
- ProguardKeepRule.Builder builder = ProguardKeepRule.builder();
- builder.setOrigin(proguardCompatOrigin);
- builder.setType(ProguardKeepRuleType.KEEP_CLASS_MEMBERS);
- builder.getModifiersBuilder().setAllowsObfuscation(true);
- builder.getModifiersBuilder().setAllowsOptimization(true);
- builder.getClassAccessFlags().setVisibility(clazz.accessFlags);
- if (clazz.isInterface()) {
- builder.setClassType(ProguardClassType.INTERFACE);
- } else {
- builder.setClassType(ProguardClassType.CLASS);
- }
- builder.setClassNames(
- ProguardClassNameList.singletonList(ProguardTypeMatcher.create(clazz.type)));
- ProguardMemberRule.Builder memberRuleBuilder = ProguardMemberRule.builder();
- memberRuleBuilder.setRuleType(ProguardMemberType.METHOD);
- memberRuleBuilder.getAccessFlags().setFlags(method.accessFlags);
- memberRuleBuilder.setName(
- IdentifierPatternWithWildcards.withoutWildcards(method.getReference().name.toString()));
- memberRuleBuilder.setTypeMatcher(
- ProguardTypeMatcher.create(method.getReference().proto.returnType));
- List<ProguardTypeMatcher> arguments =
- Arrays.stream(method.getReference().proto.parameters.values)
- .map(ProguardTypeMatcher::create)
- .collect(Collectors.toList());
- memberRuleBuilder.setArguments(arguments);
- builder.getMemberRules().add(memberRuleBuilder.build());
- return builder.build();
- }
-
public static ProguardAssumeNoSideEffectRule buildAssumeNoSideEffectsRuleForApiLevel(
DexItemFactory factory, AndroidApiLevel apiLevel) {
Origin synthesizedFromApiLevel =
diff --git a/src/test/java/com/android/tools/r8/shaking/KeepNonConstructorsTest.java b/src/test/java/com/android/tools/r8/shaking/KeepNonConstructorsTest.java
new file mode 100644
index 0000000..a1f492b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/KeepNonConstructorsTest.java
@@ -0,0 +1,71 @@
+// Copyright (c) 2022, 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;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertEquals;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class KeepNonConstructorsTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ @Test
+ public void keepConstructorTest() throws Exception {
+ testForR8(Backend.DEX)
+ .addInnerClasses(getClass())
+ .addKeepRules("-keep class " + A.class.getTypeName() + " { constructor *** *(...); }")
+ .setMinApi(AndroidApiLevel.LATEST)
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ assertThat(aClassSubject.init(), isPresent());
+ assertEquals(1, aClassSubject.allMethods().size());
+ });
+ }
+
+ @Test
+ public void keepNonConstructorsTest() throws Exception {
+ testForR8(Backend.DEX)
+ .addInnerClasses(getClass())
+ .addKeepRules("-keep class " + A.class.getTypeName() + " { !constructor *** *(...); }")
+ .setMinApi(AndroidApiLevel.LATEST)
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ assertThat(aClassSubject.uniqueMethodWithName("m"), isPresent());
+ assertEquals(1, aClassSubject.allMethods().size());
+ });
+ }
+
+ static class A {
+
+ A() {}
+
+ void m() {}
+ }
+}