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() {}
+  }
+}