[KeepAnno] Support string patterns for field names
This CL also simplifies the method variant to not use an inner subclass.
Bug: b/248408342
Change-Id: I58723cebb83548144ef293e6751a3f38ad8c79e5
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepBinding.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepBinding.java
index b98f4ab..177990e 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepBinding.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepBinding.java
@@ -431,11 +431,27 @@
* <p>If none, and other properties define this item as a field, the default matches any field
* name.
*
+ * <p>Mutually exclusive with the property `fieldNamePattern` also defining field-name.
+ *
* @return The exact field name of the field.
*/
String fieldName() default "";
/**
+ * Define the field-name pattern by a string pattern.
+ *
+ * <p>Mutually exclusive with all method properties.
+ *
+ * <p>If none, and other properties define this item as a field, the default matches any field
+ * name.
+ *
+ * <p>Mutually exclusive with the property `fieldName` also defining field-name.
+ *
+ * @return The string pattern of the field name.
+ */
+ StringPattern fieldNamePattern() default @StringPattern(exact = "");
+
+ /**
* Define the field-type pattern by a fully qualified type.
*
* <p>Mutually exclusive with all method properties.
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java
index bab5ca7..a35d08b 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java
@@ -432,13 +432,37 @@
* <p>If none, and other properties define this item as a field, the default matches any field
* name.
*
- * <p>Mutually exclusive with the property `memberFromBinding` also defining field-name.
+ * <p>Mutually exclusive with the following other properties defining field-name:
+ *
+ * <ul>
+ * <li>fieldNamePattern
+ * <li>memberFromBinding
+ * </ul>
*
* @return The exact field name of the field.
*/
String fieldName() default "";
/**
+ * Define the field-name pattern by a string pattern.
+ *
+ * <p>Mutually exclusive with all method properties.
+ *
+ * <p>If none, and other properties define this item as a field, the default matches any field
+ * name.
+ *
+ * <p>Mutually exclusive with the following other properties defining field-name:
+ *
+ * <ul>
+ * <li>fieldName
+ * <li>memberFromBinding
+ * </ul>
+ *
+ * @return The string pattern of the field name.
+ */
+ StringPattern fieldNamePattern() default @StringPattern(exact = "");
+
+ /**
* Define the field-type pattern by a fully qualified type.
*
* <p>Mutually exclusive with all method properties.
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepForApi.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepForApi.java
index 9d097f0..4b56b2e 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepForApi.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepForApi.java
@@ -210,11 +210,27 @@
* <p>If none, and other properties define this item as a field, the default matches any field
* name.
*
+ * <p>Mutually exclusive with the property `fieldNamePattern` also defining field-name.
+ *
* @return The exact field name of the field.
*/
String fieldName() default "";
/**
+ * Define the field-name pattern by a string pattern.
+ *
+ * <p>Mutually exclusive with all method properties.
+ *
+ * <p>If none, and other properties define this item as a field, the default matches any field
+ * name.
+ *
+ * <p>Mutually exclusive with the property `fieldName` also defining field-name.
+ *
+ * @return The string pattern of the field name.
+ */
+ StringPattern fieldNamePattern() default @StringPattern(exact = "");
+
+ /**
* Define the field-type pattern by a fully qualified type.
*
* <p>Mutually exclusive with all method properties.
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepTarget.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepTarget.java
index d601a6a..e2ebc3b 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepTarget.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepTarget.java
@@ -529,13 +529,37 @@
* <p>If none, and other properties define this item as a field, the default matches any field
* name.
*
- * <p>Mutually exclusive with the property `memberFromBinding` also defining field-name.
+ * <p>Mutually exclusive with the following other properties defining field-name:
+ *
+ * <ul>
+ * <li>fieldNamePattern
+ * <li>memberFromBinding
+ * </ul>
*
* @return The exact field name of the field.
*/
String fieldName() default "";
/**
+ * Define the field-name pattern by a string pattern.
+ *
+ * <p>Mutually exclusive with all method properties.
+ *
+ * <p>If none, and other properties define this item as a field, the default matches any field
+ * name.
+ *
+ * <p>Mutually exclusive with the following other properties defining field-name:
+ *
+ * <ul>
+ * <li>fieldName
+ * <li>memberFromBinding
+ * </ul>
+ *
+ * @return The string pattern of the field name.
+ */
+ StringPattern fieldNamePattern() default @StringPattern(exact = "");
+
+ /**
* Define the field-type pattern by a fully qualified type.
*
* <p>Mutually exclusive with all method properties.
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByNative.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByNative.java
index 229d0f1..f025c0e 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByNative.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByNative.java
@@ -253,11 +253,27 @@
* <p>If none, and other properties define this item as a field, the default matches any field
* name.
*
+ * <p>Mutually exclusive with the property `fieldNamePattern` also defining field-name.
+ *
* @return The exact field name of the field.
*/
String fieldName() default "";
/**
+ * Define the field-name pattern by a string pattern.
+ *
+ * <p>Mutually exclusive with all method properties.
+ *
+ * <p>If none, and other properties define this item as a field, the default matches any field
+ * name.
+ *
+ * <p>Mutually exclusive with the property `fieldName` also defining field-name.
+ *
+ * @return The string pattern of the field name.
+ */
+ StringPattern fieldNamePattern() default @StringPattern(exact = "");
+
+ /**
* Define the field-type pattern by a fully qualified type.
*
* <p>Mutually exclusive with all method properties.
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByReflection.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByReflection.java
index 7b8fa8e..8076e51 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByReflection.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByReflection.java
@@ -253,11 +253,27 @@
* <p>If none, and other properties define this item as a field, the default matches any field
* name.
*
+ * <p>Mutually exclusive with the property `fieldNamePattern` also defining field-name.
+ *
* @return The exact field name of the field.
*/
String fieldName() default "";
/**
+ * Define the field-name pattern by a string pattern.
+ *
+ * <p>Mutually exclusive with all method properties.
+ *
+ * <p>If none, and other properties define this item as a field, the default matches any field
+ * name.
+ *
+ * <p>Mutually exclusive with the property `fieldName` also defining field-name.
+ *
+ * @return The string pattern of the field name.
+ */
+ StringPattern fieldNamePattern() default @StringPattern(exact = "");
+
+ /**
* Define the field-type pattern by a fully qualified type.
*
* <p>Mutually exclusive with all method properties.
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
index 7f03de2..d4b3f1d 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
@@ -1567,6 +1567,7 @@
private static class FieldDeclaration extends Declaration<KeepFieldPattern> {
private final ParsingContext parsingContext;
+ private final StringPatternParser nameParser;
private final FieldTypeParser typeParser;
private KeepFieldAccessPattern.Builder accessBuilder = null;
private KeepFieldPattern.Builder builder = null;
@@ -1574,12 +1575,16 @@
public FieldDeclaration(ParsingContext parsingContext) {
this.parsingContext = parsingContext;
+ nameParser = new StringPatternParser(parsingContext.group(Item.fieldNameGroup));
+ nameParser.setProperty(Item.fieldName, StringProperty.EXACT);
+ nameParser.setProperty(Item.fieldNamePattern, StringProperty.PATTERN);
+
typeParser = new FieldTypeParser(parsingContext.group(Item.fieldTypeGroup));
typeParser.setProperty(Item.fieldTypePattern, TypeProperty.TYPE_PATTERN);
typeParser.setProperty(Item.fieldType, TypeProperty.TYPE_NAME);
typeParser.setProperty(Item.fieldTypeConstant, TypeProperty.TYPE_CONSTANT);
- parsers = Collections.singletonList(typeParser);
+ parsers = ImmutableList.of(nameParser, typeParser);
}
@Override
@@ -1603,6 +1608,9 @@
if (accessBuilder != null) {
getBuilder().setAccessPattern(accessBuilder.build());
}
+ if (!nameParser.isDefault()) {
+ getBuilder().setNamePattern(KeepFieldNamePattern.fromStringPattern(nameParser.getValue()));
+ }
if (!typeParser.isDefault()) {
getBuilder().setTypePattern(typeParser.getValue());
}
@@ -1610,15 +1618,6 @@
}
@Override
- boolean tryParse(String name, Object value) {
- if (name.equals(Item.fieldName) && value instanceof String) {
- getBuilder().setNamePattern(KeepFieldNamePattern.exact((String) value));
- return true;
- }
- return super.tryParse(name, value);
- }
-
- @Override
AnnotationVisitor tryParseArray(String name) {
if (name.equals(Item.fieldAccess)) {
accessBuilder = KeepFieldAccessPattern.builder();
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java
index c714960..332e101 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java
@@ -12,7 +12,6 @@
import com.android.tools.r8.keepanno.ast.KeepConsequences;
import com.android.tools.r8.keepanno.ast.KeepEdge;
import com.android.tools.r8.keepanno.ast.KeepEdgeException;
-import com.android.tools.r8.keepanno.ast.KeepFieldNamePattern.KeepFieldNameExactPattern;
import com.android.tools.r8.keepanno.ast.KeepFieldPattern;
import com.android.tools.r8.keepanno.ast.KeepItemPattern;
import com.android.tools.r8.keepanno.ast.KeepMemberItemPattern;
@@ -187,9 +186,9 @@
}
private void writeField(KeepFieldPattern field, AnnotationVisitor targetVisitor) {
- KeepFieldNameExactPattern exactFieldName = field.getNamePattern().asExact();
+ String exactFieldName = field.getNamePattern().asExactString();
if (exactFieldName != null) {
- targetVisitor.visit(Item.fieldName, exactFieldName.getName());
+ targetVisitor.visit(Item.fieldName, exactFieldName);
} else {
throw new Unimplemented();
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/AnnotationConstants.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/AnnotationConstants.java
index c489701..f0bdccf 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/AnnotationConstants.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/AnnotationConstants.java
@@ -94,7 +94,9 @@
public static final String methodParameters = "methodParameters";
public static final String methodParameterTypePatterns = "methodParameterTypePatterns";
public static final String fieldAccess = "fieldAccess";
+ public static final String fieldNameGroup = "field-name";
public static final String fieldName = "fieldName";
+ public static final String fieldNamePattern = "fieldNamePattern";
public static final String fieldTypeGroup = "field-type";
public static final String fieldType = "fieldType";
public static final String fieldTypeConstant = "fieldTypeConstant";
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldNamePattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldNamePattern.java
index 0945764..5223e60 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldNamePattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldNamePattern.java
@@ -3,96 +3,44 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.keepanno.ast;
-public abstract class KeepFieldNamePattern {
+public class KeepFieldNamePattern {
+
+ private static final KeepFieldNamePattern ANY = new KeepFieldNamePattern(KeepStringPattern.any());
public static KeepFieldNamePattern any() {
- return Any.getInstance();
+ return ANY;
}
public static KeepFieldNamePattern exact(String methodName) {
- return new KeepFieldNameExactPattern(methodName);
+ return fromStringPattern(KeepStringPattern.exact(methodName));
}
- private KeepFieldNamePattern() {}
+ public static KeepFieldNamePattern fromStringPattern(KeepStringPattern pattern) {
+ if (pattern.isAny()) {
+ return ANY;
+ }
+ return new KeepFieldNamePattern(pattern);
+ }
+
+ private final KeepStringPattern pattern;
+
+ private KeepFieldNamePattern(KeepStringPattern pattern) {
+ this.pattern = pattern;
+ }
+
+ public KeepStringPattern asStringPattern() {
+ return pattern;
+ }
public boolean isAny() {
- return false;
+ return ANY == this;
}
public final boolean isExact() {
- return asExact() != null;
+ return pattern.isExact();
}
- public KeepFieldNameExactPattern asExact() {
- return null;
- }
-
- private static class Any extends KeepFieldNamePattern {
- private static final Any INSTANCE = new Any();
-
- public static Any getInstance() {
- return INSTANCE;
- }
-
- @Override
- public boolean isAny() {
- return true;
- }
-
- @Override
- public boolean equals(Object obj) {
- return this == obj;
- }
-
- @Override
- public int hashCode() {
- return System.identityHashCode(this);
- }
-
- @Override
- public String toString() {
- return "*";
- }
- }
-
- public static class KeepFieldNameExactPattern extends KeepFieldNamePattern {
- private final String name;
-
- public KeepFieldNameExactPattern(String name) {
- assert name != null;
- this.name = name;
- }
-
- @Override
- public KeepFieldNameExactPattern asExact() {
- return this;
- }
-
- public String getName() {
- return name;
- }
-
- @Override
- @SuppressWarnings("EqualsGetClass")
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- KeepFieldNameExactPattern that = (KeepFieldNameExactPattern) o;
- return name.equals(that.name);
- }
-
- @Override
- public int hashCode() {
- return name.hashCode();
- }
-
- @Override
- public String toString() {
- return name;
- }
+ public String asExactString() {
+ return pattern.asExactString();
}
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodNamePattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodNamePattern.java
index 6843e61..b79b616 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodNamePattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodNamePattern.java
@@ -3,17 +3,27 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.keepanno.ast;
+public class KeepMethodNamePattern {
-public abstract class KeepMethodNamePattern {
private static final String INIT_STRING = "<init>";
private static final String CLINIT_STRING = "<clinit>";
+ private static final KeepMethodNamePattern ANY =
+ new KeepMethodNamePattern(KeepStringPattern.any());
+ private static final KeepMethodNamePattern INSTANCE_INIT =
+ new KeepMethodNamePattern(KeepStringPattern.exact(INIT_STRING));
+ private static final KeepMethodNamePattern CLASS_INIT =
+ new KeepMethodNamePattern(KeepStringPattern.exact(CLINIT_STRING));
public static KeepMethodNamePattern any() {
- return SomePattern.ANY;
+ return KeepMethodNamePattern.ANY;
}
- public static KeepMethodNamePattern initializer() {
- return SomePattern.INSTANCE_INIT;
+ public static KeepMethodNamePattern instanceInitializer() {
+ return KeepMethodNamePattern.INSTANCE_INIT;
+ }
+
+ public static KeepMethodNamePattern classInitializer() {
+ return KeepMethodNamePattern.CLASS_INIT;
}
public static KeepMethodNamePattern exact(String methodName) {
@@ -22,98 +32,70 @@
public static KeepMethodNamePattern fromStringPattern(KeepStringPattern pattern) {
if (pattern.isAny()) {
- return SomePattern.ANY;
+ return KeepMethodNamePattern.ANY;
}
if (pattern.isExact()) {
String exact = pattern.asExactString();
if (INIT_STRING.equals(exact)) {
- return SomePattern.INSTANCE_INIT;
+ return KeepMethodNamePattern.INSTANCE_INIT;
}
if (CLINIT_STRING.equals(exact)) {
- return SomePattern.CLASS_INIT;
+ return KeepMethodNamePattern.CLASS_INIT;
}
}
- return new SomePattern(pattern);
+ return new KeepMethodNamePattern(pattern);
}
- private KeepMethodNamePattern() {}
+ private final KeepStringPattern pattern;
- public abstract boolean isAny();
+ private KeepMethodNamePattern(KeepStringPattern pattern) {
+ assert pattern != null;
+ this.pattern = pattern;
+ }
- public abstract boolean isInstanceInitializer();
+ public KeepStringPattern asStringPattern() {
+ return pattern;
+ }
- public abstract boolean isClassInitializer();
-
- public abstract boolean isExact();
-
- public abstract String asExactString();
-
- public abstract KeepStringPattern asStringPattern();
-
- private static class SomePattern extends KeepMethodNamePattern {
- private static final SomePattern ANY = new SomePattern(KeepStringPattern.any());
- private static final KeepMethodNamePattern INSTANCE_INIT =
- new SomePattern(KeepStringPattern.exact("<init>"));
- private static final KeepMethodNamePattern CLASS_INIT =
- new SomePattern(KeepStringPattern.exact("<clinit>"));
-
- private final KeepStringPattern pattern;
-
- public SomePattern(KeepStringPattern pattern) {
- assert pattern != null;
- this.pattern = pattern;
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
}
-
- @Override
- public KeepStringPattern asStringPattern() {
- return pattern;
+ if (!(o instanceof KeepMethodNamePattern)) {
+ return false;
}
+ KeepMethodNamePattern that = (KeepMethodNamePattern) o;
+ return pattern.equals(that.pattern);
+ }
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (!(o instanceof SomePattern)) {
- return false;
- }
- SomePattern that = (SomePattern) o;
- return pattern.equals(that.pattern);
- }
+ @Override
+ public int hashCode() {
+ return pattern.hashCode();
+ }
- @Override
- public int hashCode() {
- return pattern.hashCode();
- }
+ @Override
+ public String toString() {
+ return pattern.toString();
+ }
- @Override
- public String toString() {
- return pattern.toString();
- }
+ public boolean isAny() {
+ return ANY == this;
+ }
- @Override
- public boolean isAny() {
- return ANY == this;
- }
+ public boolean isClassInitializer() {
+ return CLASS_INIT == this;
+ }
- @Override
- public boolean isClassInitializer() {
- return CLASS_INIT == this;
- }
+ public boolean isInstanceInitializer() {
+ return INSTANCE_INIT == this;
+ }
- @Override
- public boolean isInstanceInitializer() {
- return INSTANCE_INIT == this;
- }
+ public boolean isExact() {
+ return pattern.isExact();
+ }
- @Override
- public boolean isExact() {
- return pattern.isExact();
- }
-
- @Override
- public String asExactString() {
- return pattern.asExactString();
- }
+ public String asExactString() {
+ return pattern.asExactString();
}
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/RulePrintingUtils.java b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/RulePrintingUtils.java
index f16d9dd..7737744 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/RulePrintingUtils.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/RulePrintingUtils.java
@@ -176,9 +176,7 @@
}
private static RulePrinter printFieldName(RulePrinter builder, KeepFieldNamePattern namePattern) {
- return namePattern.isAny()
- ? builder.appendStar()
- : builder.append(namePattern.asExact().getName());
+ return printStringPattern(builder, namePattern.asStringPattern());
}
private static RulePrinter printMethodName(
diff --git a/src/test/java/com/android/tools/r8/keepanno/FieldNameStringPatternsTest.java b/src/test/java/com/android/tools/r8/keepanno/FieldNameStringPatternsTest.java
new file mode 100644
index 0000000..520c65c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/FieldNameStringPatternsTest.java
@@ -0,0 +1,117 @@
+// Copyright (c) 2024, 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.keepanno;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+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.keepanno.annotations.KeepItemKind;
+import com.android.tools.r8.keepanno.annotations.KeepTarget;
+import com.android.tools.r8.keepanno.annotations.StringPattern;
+import com.android.tools.r8.keepanno.annotations.UsedByReflection;
+import com.android.tools.r8.keepanno.annotations.UsesReflection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.lang.reflect.Field;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class FieldNameStringPatternsTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("1", "2");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build();
+ }
+
+ public FieldNameStringPatternsTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(getInputClasses())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .enableExperimentalKeepAnnotations()
+ .addProgramClasses(getInputClasses())
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED)
+ .inspect(this::checkOutput);
+ }
+
+ public List<Class<?>> getInputClasses() {
+ return ImmutableList.of(TestClass.class, A.class, B.class);
+ }
+
+ private void checkOutput(CodeInspector inspector) {
+ assertThat(inspector.clazz(B.class), isPresentAndRenamed());
+ assertThat(inspector.clazz(B.class).field("int", "bar"), isAbsent());
+ assertThat(inspector.clazz(B.class).field("int", "hiddenOneI"), isPresent());
+ assertThat(inspector.clazz(B.class).field("int", "hiddenTwoI"), isPresent());
+ assertThat(inspector.clazz(B.class).field("java.lang.String", "hiddenSomeS"), isAbsent());
+ }
+
+ static class A {
+
+ @UsesReflection(
+ @KeepTarget(
+ classConstant = B.class,
+ fieldNamePattern = @StringPattern(startsWith = "hidden", endsWith = "I")))
+ public void foo() throws Exception {
+ int counter = 1;
+ for (Field field : B.class.getDeclaredFields()) {
+ String name = field.getName();
+ if (name.startsWith("hidden")) {
+ if (name.endsWith("I")) {
+ field.set(null, counter++);
+ }
+ }
+ }
+ for (Field field : B.class.getDeclaredFields()) {
+ String name = field.getName();
+ if (name.startsWith("hidden")) {
+ if (name.endsWith("I")) {
+ System.out.println(field.get(null));
+ }
+ }
+ }
+ }
+ }
+
+ static class B {
+ static int hiddenOneI = 9;
+ static int hiddenTwoI = 18;
+ static String hiddenSomeS = "foo";
+ static int bar = 7;
+ }
+
+ static class TestClass {
+
+ @UsedByReflection(kind = KeepItemKind.CLASS_AND_METHODS)
+ public static void main(String[] args) throws Exception {
+ new A().foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java b/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java
index 8a83cf9..dae2c6b 100644
--- a/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java
@@ -250,7 +250,7 @@
private KeepMemberPattern defaultInitializerPattern() {
return KeepMethodPattern.builder()
- .setNamePattern(KeepMethodNamePattern.initializer())
+ .setNamePattern(KeepMethodNamePattern.instanceInitializer())
.setParametersPattern(KeepMethodParametersPattern.none())
.setReturnTypeVoid()
.build();
diff --git a/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java b/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java
index 0ab89fa..61265cb 100644
--- a/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java
+++ b/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java
@@ -821,7 +821,14 @@
.addParagraph(getMutuallyExclusiveForFieldProperties())
.addParagraph(getFieldDefaultDoc("any field name"))
.setDocReturn("The exact field name of the field.")
- .defaultEmptyString());
+ .defaultEmptyString())
+ .addMember(
+ new GroupMember("fieldNamePattern")
+ .setDocTitle("Define the field-name pattern by a string pattern.")
+ .addParagraph(getMutuallyExclusiveForFieldProperties())
+ .addParagraph(getFieldDefaultDoc("any field name"))
+ .setDocReturn("The string pattern of the field name.")
+ .defaultValue(StringPattern.class, DEFAULT_INVALID_STRING_PATTERN));
}
private Group createFieldTypeGroup() {