[KeepAnno] Add field member patterns.
Bug: b/248408342
Change-Id: I1b9895f33a6465d1057386dfc1b43b5d90b93178
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepConstants.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepConstants.java
index 24e49d1..5b6de6e 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepConstants.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepConstants.java
@@ -31,5 +31,6 @@
public static final String DESCRIPTOR = getDescriptor(CLASS);
public static final String classConstant = "classConstant";
public static final String methodName = "methodName";
+ public static final String fieldName = "fieldName";
}
}
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 e20f288..f87e32c 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
@@ -16,4 +16,6 @@
String classTypeName() default "";
String methodName() default "";
+
+ String fieldName() default "";
}
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 029ec75..cc6e4ee 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
@@ -8,6 +8,8 @@
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;
+import com.android.tools.r8.keepanno.ast.KeepFieldPattern;
import com.android.tools.r8.keepanno.ast.KeepItemPattern;
import com.android.tools.r8.keepanno.ast.KeepItemPattern.Builder;
import com.android.tools.r8.keepanno.ast.KeepMethodNamePattern;
@@ -143,6 +145,7 @@
private final Parent<KeepTarget> parent;
private KeepQualifiedClassNamePattern classNamePattern = null;
private KeepMethodNamePattern methodName = null;
+ private KeepFieldNamePattern fieldName = null;
public KeepTargetVisitor(Parent<KeepTarget> parent) {
this.parent = parent;
@@ -158,6 +161,10 @@
methodName = KeepMethodNamePattern.exact((String) value);
return;
}
+ if (name.equals(Target.fieldName) && value instanceof String) {
+ fieldName = KeepFieldNamePattern.exact((String) value);
+ return;
+ }
super.visit(name, value);
}
@@ -167,10 +174,16 @@
if (classNamePattern != null) {
itemBuilder.setClassPattern(classNamePattern);
}
+ if (methodName != null && fieldName != null) {
+ throw new KeepEdgeException("Cannot define both a field and a method pattern.");
+ }
if (methodName != null) {
itemBuilder.setMemberPattern(
KeepMethodPattern.builder().setNamePattern(methodName).build());
}
+ if (fieldName != null) {
+ itemBuilder.setMemberPattern(KeepFieldPattern.builder().setNamePattern(fieldName).build());
+ }
KeepTarget target = KeepTarget.builder().setItem(itemBuilder.build()).build();
parent.accept(target);
}
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 8307265..0a0ccc8 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
@@ -7,6 +7,9 @@
import com.android.tools.r8.keepanno.annotations.KeepConstants.Target;
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.KeepMemberPattern;
import com.android.tools.r8.keepanno.ast.KeepMethodNamePattern.KeepMethodNameExactPattern;
@@ -84,7 +87,31 @@
if (memberPattern.isAll()) {
throw new Unimplemented();
}
- KeepMethodPattern method = memberPattern.asMethod();
+ if (memberPattern.isMethod()) {
+ writeMethod(memberPattern.asMethod(), targetVisitor);
+ } else if (memberPattern.isField()) {
+ writeField(memberPattern.asField(), targetVisitor);
+ } else {
+ throw new KeepEdgeException("Unexpected member pattern: " + memberPattern);
+ }
+ }
+
+ private void writeField(KeepFieldPattern field, AnnotationVisitor targetVisitor) {
+ KeepFieldNameExactPattern exactFieldName = field.getNamePattern().asExact();
+ if (exactFieldName != null) {
+ targetVisitor.visit(Target.fieldName, exactFieldName.getName());
+ } else {
+ throw new Unimplemented();
+ }
+ if (!field.getAccessPattern().isAny()) {
+ throw new Unimplemented();
+ }
+ if (!field.getTypePattern().isAny()) {
+ throw new Unimplemented();
+ }
+ }
+
+ private void writeMethod(KeepMethodPattern method, AnnotationVisitor targetVisitor) {
KeepMethodNameExactPattern exactMethodName = method.getNamePattern().asExact();
if (exactMethodName != null) {
targetVisitor.visit(Target.methodName, exactMethodName.getName());
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
index 89be76b..b2da25a 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
@@ -39,7 +39,12 @@
* UNQUALIFIED_CLASS_NAME_PATTERN ::= any | exact simple-class-name
* EXTENDS_PATTERN ::= any | QUALIFIED_CLASS_NAME_PATTERN
*
- * MEMBER_PATTERN ::= none | all | METHOD_PATTERN
+ * MEMBER_PATTERN ::= none | all | FIELD_PATTERN | METHOD_PATTERN
+ *
+ * FIELD_PATTERN
+ * ::= FIELD_ACCESS_PATTERN
+ * FIELD_TYPE_PATTERN
+ * FIELD_NAME_PATTERN;
*
* METHOD_PATTERN
* ::= METHOD_ACCESS_PATTERN
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldAccessPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldAccessPattern.java
new file mode 100644
index 0000000..056430b
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldAccessPattern.java
@@ -0,0 +1,43 @@
+// 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.keepanno.ast;
+
+// TODO: finish this.
+public abstract class KeepFieldAccessPattern {
+
+ public static KeepFieldAccessPattern any() {
+ return Any.getInstance();
+ }
+
+ public abstract boolean isAny();
+
+ private static class Any extends KeepFieldAccessPattern {
+
+ private static final Any INSTANCE = new Any();
+
+ private 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 "*";
+ }
+ }
+}
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
new file mode 100644
index 0000000..3067966
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldNamePattern.java
@@ -0,0 +1,97 @@
+// 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.keepanno.ast;
+
+public abstract class KeepFieldNamePattern {
+
+ public static KeepFieldNamePattern any() {
+ return Any.getInstance();
+ }
+
+ public static KeepFieldNamePattern exact(String methodName) {
+ return new KeepFieldNameExactPattern(methodName);
+ }
+
+ private KeepFieldNamePattern() {}
+
+ public boolean isAny() {
+ return false;
+ }
+
+ public final boolean isExact() {
+ return asExact() != null;
+ }
+
+ 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
+ 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;
+ }
+ }
+}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldPattern.java
new file mode 100644
index 0000000..7137033
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldPattern.java
@@ -0,0 +1,116 @@
+// 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.keepanno.ast;
+
+import java.util.Objects;
+
+public final class KeepFieldPattern extends KeepMemberPattern {
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+
+ private KeepFieldAccessPattern accessPattern = KeepFieldAccessPattern.any();
+ private KeepFieldNamePattern namePattern = null;
+ private KeepFieldTypePattern typePattern = KeepFieldTypePattern.any();
+
+ private Builder() {}
+
+ public Builder self() {
+ return this;
+ }
+
+ public Builder setAccessPattern(KeepFieldAccessPattern accessPattern) {
+ this.accessPattern = accessPattern;
+ return self();
+ }
+
+ public Builder setNamePattern(KeepFieldNamePattern namePattern) {
+ this.namePattern = namePattern;
+ return self();
+ }
+
+ public Builder setTypePattern(KeepFieldTypePattern typePattern) {
+ this.typePattern = typePattern;
+ return self();
+ }
+
+ public KeepFieldPattern build() {
+ if (namePattern == null) {
+ throw new KeepEdgeException("Field pattern must declare a name pattern");
+ }
+ return new KeepFieldPattern(accessPattern, namePattern, typePattern);
+ }
+ }
+
+ private final KeepFieldAccessPattern accessPattern;
+ private final KeepFieldNamePattern namePattern;
+ private final KeepFieldTypePattern typePattern;
+
+ private KeepFieldPattern(
+ KeepFieldAccessPattern accessPattern,
+ KeepFieldNamePattern namePattern,
+ KeepFieldTypePattern typePattern) {
+ assert accessPattern != null;
+ assert namePattern != null;
+ assert typePattern != null;
+ this.accessPattern = accessPattern;
+ this.namePattern = namePattern;
+ this.typePattern = typePattern;
+ }
+
+ @Override
+ public KeepFieldPattern asField() {
+ return this;
+ }
+
+ public boolean isAnyField() {
+ return accessPattern.isAny() && namePattern.isAny() && typePattern.isAny();
+ }
+
+ public KeepFieldAccessPattern getAccessPattern() {
+ return accessPattern;
+ }
+
+ public KeepFieldNamePattern getNamePattern() {
+ return namePattern;
+ }
+
+ public KeepFieldTypePattern getTypePattern() {
+ return typePattern;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ KeepFieldPattern that = (KeepFieldPattern) o;
+ return accessPattern.equals(that.accessPattern)
+ && namePattern.equals(that.namePattern)
+ && typePattern.equals(that.typePattern);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(accessPattern, namePattern, typePattern);
+ }
+
+ @Override
+ public String toString() {
+ return "KeepFieldPattern{"
+ + "access="
+ + accessPattern
+ + ", name="
+ + namePattern
+ + ", type="
+ + typePattern
+ + '}';
+ }
+}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldTypePattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldTypePattern.java
new file mode 100644
index 0000000..beaaa55
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldTypePattern.java
@@ -0,0 +1,62 @@
+// 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.keepanno.ast;
+
+public abstract class KeepFieldTypePattern {
+
+ public static KeepFieldTypePattern any() {
+ return SomeType.ANY_TYPE_INSTANCE;
+ }
+
+ public boolean isAny() {
+ return isType() && asType().isAny();
+ }
+
+ public boolean isType() {
+ return asType() != null;
+ }
+
+ public KeepTypePattern asType() {
+ return null;
+ }
+
+ private static class SomeType extends KeepFieldTypePattern {
+
+ private static final SomeType ANY_TYPE_INSTANCE = new SomeType(KeepTypePattern.any());
+
+ private final KeepTypePattern typePattern;
+
+ private SomeType(KeepTypePattern typePattern) {
+ assert typePattern != null;
+ this.typePattern = typePattern;
+ }
+
+ @Override
+ public KeepTypePattern asType() {
+ return typePattern;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ SomeType someType = (SomeType) o;
+ return typePattern.equals(someType.typePattern);
+ }
+
+ @Override
+ public int hashCode() {
+ return typePattern.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return typePattern.toString();
+ }
+ }
+}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberPattern.java
index 25a57ab..b0f205e 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberPattern.java
@@ -88,4 +88,12 @@
public KeepMethodPattern asMethod() {
return null;
}
+
+ public final boolean isField() {
+ return asField() != null;
+ }
+
+ public KeepFieldPattern asField() {
+ return null;
+ }
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java
index 7c8c032..6781a22 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java
@@ -5,6 +5,9 @@
import com.android.tools.r8.keepanno.ast.KeepConsequences;
import com.android.tools.r8.keepanno.ast.KeepEdge;
+import com.android.tools.r8.keepanno.ast.KeepFieldAccessPattern;
+import com.android.tools.r8.keepanno.ast.KeepFieldNamePattern;
+import com.android.tools.r8.keepanno.ast.KeepFieldPattern;
import com.android.tools.r8.keepanno.ast.KeepItemPattern;
import com.android.tools.r8.keepanno.ast.KeepMemberPattern;
import com.android.tools.r8.keepanno.ast.KeepMethodAccessPattern;
@@ -114,9 +117,25 @@
printMethod(builder.append(' '), member.asMethod());
return builder.append(" }");
}
+ if (member.isField()) {
+ builder.append(" {");
+ printField(builder.append(' '), member.asField());
+ return builder.append(" }");
+ }
throw new Unimplemented();
}
+ private static StringBuilder printField(StringBuilder builder, KeepFieldPattern fieldPattern) {
+ if (fieldPattern.isAnyField()) {
+ return builder.append("<fields>;");
+ }
+ printAccess(builder, " ", fieldPattern.getAccessPattern());
+ printType(builder, fieldPattern.getTypePattern().asType());
+ builder.append(' ');
+ printFieldName(builder, fieldPattern.getNamePattern());
+ return builder.append(';');
+ }
+
private static StringBuilder printMethod(StringBuilder builder, KeepMethodPattern methodPattern) {
if (methodPattern.isAnyMethod()) {
return builder.append("<methods>;");
@@ -143,6 +162,13 @@
.append(')');
}
+ private static StringBuilder printFieldName(
+ StringBuilder builder, KeepFieldNamePattern namePattern) {
+ return namePattern.isAny()
+ ? builder.append("*")
+ : builder.append(namePattern.asExact().getName());
+ }
+
private static StringBuilder printMethodName(
StringBuilder builder, KeepMethodNamePattern namePattern) {
return namePattern.isAny()
@@ -175,6 +201,16 @@
throw new Unimplemented();
}
+ private static StringBuilder printAccess(
+ StringBuilder builder, String indent, KeepFieldAccessPattern accessPattern) {
+ if (accessPattern.isAny()) {
+ // No text will match any access pattern.
+ // Don't print the indent in this case.
+ return builder;
+ }
+ throw new Unimplemented();
+ }
+
private static StringBuilder printClassName(
StringBuilder builder, KeepQualifiedClassNamePattern classNamePattern) {
if (classNamePattern.isAny()) {
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/processor/KeepEdgeProcessor.java b/src/keepanno/java/com/android/tools/r8/keepanno/processor/KeepEdgeProcessor.java
index c86857d..18d3932 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/processor/KeepEdgeProcessor.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/processor/KeepEdgeProcessor.java
@@ -16,6 +16,8 @@
import com.android.tools.r8.keepanno.ast.KeepEdge;
import com.android.tools.r8.keepanno.ast.KeepEdge.Builder;
import com.android.tools.r8.keepanno.ast.KeepEdgeException;
+import com.android.tools.r8.keepanno.ast.KeepFieldNamePattern;
+import com.android.tools.r8.keepanno.ast.KeepFieldPattern;
import com.android.tools.r8.keepanno.ast.KeepItemPattern;
import com.android.tools.r8.keepanno.ast.KeepMethodNamePattern;
import com.android.tools.r8.keepanno.ast.KeepMethodPattern;
@@ -148,14 +150,21 @@
itemBuilder.setClassPattern(KeepQualifiedClassNamePattern.exact(typeName));
}
AnnotationValue methodNameValue = getAnnotationValue(mirror, Target.methodName);
+ AnnotationValue fieldNameValue = getAnnotationValue(mirror, Target.fieldName);
+ if (methodNameValue != null && fieldNameValue != null) {
+ throw new KeepEdgeException("Cannot define both a method and a field name pattern");
+ }
if (methodNameValue != null) {
String methodName = AnnotationStringValueVisitor.getString(methodNameValue);
itemBuilder.setMemberPattern(
KeepMethodPattern.builder()
.setNamePattern(KeepMethodNamePattern.exact(methodName))
.build());
+ } else if (fieldNameValue != null) {
+ String fieldName = AnnotationStringValueVisitor.getString(fieldNameValue);
+ itemBuilder.setMemberPattern(
+ KeepFieldPattern.builder().setNamePattern(KeepFieldNamePattern.exact(fieldName)).build());
}
-
builder.setItem(itemBuilder.build());
}
diff --git a/src/test/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractorTest.java b/src/test/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractorTest.java
index 94001ec..4fa9b42 100644
--- a/src/test/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractorTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractorTest.java
@@ -3,20 +3,32 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.keepanno.keeprules;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.JavaCompilerTool;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.keepanno.asm.KeepEdgeReader;
import com.android.tools.r8.keepanno.ast.KeepEdge;
+import com.android.tools.r8.keepanno.processor.KeepEdgeProcessor;
import com.android.tools.r8.keepanno.testsource.KeepClassAndDefaultConstructorSource;
+import com.android.tools.r8.keepanno.testsource.KeepFieldSource;
import com.android.tools.r8.keepanno.testsource.KeepSourceEdges;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.ZipUtils;
+import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Set;
+import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -24,33 +36,77 @@
@RunWith(Parameterized.class)
public class KeepRuleExtractorTest extends TestBase {
- private static final Class<?> SOURCE = KeepClassAndDefaultConstructorSource.class;
- private static final String EXPECTED = KeepSourceEdges.getExpected(SOURCE);
+ private static class ParamWrapper {
+ private final Class<?> clazz;
+ private final TestParameters params;
+
+ public ParamWrapper(Class<?> clazz, TestParameters params) {
+ this.clazz = clazz;
+ this.params = params;
+ }
+
+ @Override
+ public String toString() {
+ return clazz.getSimpleName() + ", " + params.toString();
+ }
+ }
+
private static final Path KEEP_ANNO_PATH =
Paths.get(ToolHelper.BUILD_DIR, "classes", "java", "keepanno");
- private final TestParameters parameters;
-
- @Parameterized.Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+ private static List<Class<?>> getTestClasses() {
+ return ImmutableList.of(KeepClassAndDefaultConstructorSource.class, KeepFieldSource.class);
}
- public KeepRuleExtractorTest(TestParameters parameters) {
- this.parameters = parameters;
+ private final TestParameters parameters;
+ private final Class<?> source;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static List<ParamWrapper> data() {
+ TestParametersCollection params =
+ getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build();
+ return getTestClasses().stream()
+ .flatMap(c -> params.stream().map(p -> new ParamWrapper(c, p)))
+ .collect(Collectors.toList());
+ }
+
+ public KeepRuleExtractorTest(ParamWrapper wrapper) {
+ this.parameters = wrapper.params;
+ this.source = wrapper.clazz;
}
@Test
- public void test() throws Exception {
- List<String> rules = getKeepRulesForClass(SOURCE);
+ public void testProcessor() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ Path out =
+ JavaCompilerTool.create(parameters.getRuntime().asCf(), temp)
+ .addAnnotationProcessors(typeName(KeepEdgeProcessor.class))
+ .addClasspathFiles(KEEP_ANNO_PATH)
+ .addClassNames(Collections.singletonList(typeName(source)))
+ .addClasspathFiles(Paths.get(ToolHelper.BUILD_DIR, "classes", "java", "test"))
+ .addClasspathFiles(ToolHelper.DEPS)
+ .compile();
+
+ String synthesizedEdgesClassName =
+ KeepEdgeProcessor.getClassTypeNameForSynthesizedEdges(source.getTypeName());
+ String entry =
+ ZipUtils.zipEntryNameForClass(Reference.classFromTypeName(synthesizedEdgesClassName));
+ byte[] bytes = ZipUtils.readSingleEntry(out, entry);
+ Set<KeepEdge> keepEdges = KeepEdgeReader.readKeepEdges(bytes);
+ assertEquals(KeepSourceEdges.getExpectedEdges(source), keepEdges);
+ }
+
+ @Test
+ public void testExtract() throws Exception {
+ List<String> rules = getKeepRulesForClass(source);
testForR8(parameters.getBackend())
.addClasspathFiles(KEEP_ANNO_PATH)
- .addProgramClassesAndInnerClasses(SOURCE)
+ .addProgramClassesAndInnerClasses(source)
.addKeepRules(rules)
- .addKeepMainRule(SOURCE)
+ .addKeepMainRule(source)
.setMinApi(parameters.getApiLevel())
- .run(parameters.getRuntime(), SOURCE)
- .assertSuccessWithOutput(EXPECTED);
+ .run(parameters.getRuntime(), source)
+ .assertSuccessWithOutput(KeepSourceEdges.getExpected(source));
}
private List<String> getKeepRulesForClass(Class<?> clazz) throws IOException {
diff --git a/src/test/java/com/android/tools/r8/keepanno/testsource/KeepFieldSource.java b/src/test/java/com/android/tools/r8/keepanno/testsource/KeepFieldSource.java
new file mode 100644
index 0000000..6f577e4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/testsource/KeepFieldSource.java
@@ -0,0 +1,35 @@
+// 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.keepanno.testsource;
+
+import com.android.tools.r8.keepanno.annotations.KeepEdge;
+import com.android.tools.r8.keepanno.annotations.KeepTarget;
+import java.lang.reflect.Field;
+
+@KeepEdge(
+ consequences = {
+ // Keep the reflectively accessed field.
+ @KeepTarget(classConstant = KeepFieldSource.A.class, fieldName = "f")
+ })
+public class KeepFieldSource {
+
+ public static class A {
+
+ public int f;
+
+ public A(int x) {
+ f = x;
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ int x = 42 + args.length;
+ Object o = System.nanoTime() > 0 ? new A(x) : null;
+ Field f = o.getClass().getDeclaredField("f");
+ int y = f.getInt(o);
+ if (x == y) {
+ System.out.println("The values match!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/testsource/KeepSourceEdges.java b/src/test/java/com/android/tools/r8/keepanno/testsource/KeepSourceEdges.java
index 5637edf..7af9aee 100644
--- a/src/test/java/com/android/tools/r8/keepanno/testsource/KeepSourceEdges.java
+++ b/src/test/java/com/android/tools/r8/keepanno/testsource/KeepSourceEdges.java
@@ -5,6 +5,8 @@
import com.android.tools.r8.keepanno.ast.KeepConsequences;
import com.android.tools.r8.keepanno.ast.KeepEdge;
+import com.android.tools.r8.keepanno.ast.KeepFieldNamePattern;
+import com.android.tools.r8.keepanno.ast.KeepFieldPattern;
import com.android.tools.r8.keepanno.ast.KeepItemPattern;
import com.android.tools.r8.keepanno.ast.KeepMethodNamePattern;
import com.android.tools.r8.keepanno.ast.KeepMethodPattern;
@@ -25,6 +27,9 @@
if (clazz.equals(KeepClassAndDefaultConstructorSource.class)) {
return getKeepClassAndDefaultConstructorSourceEdges();
}
+ if (clazz.equals(KeepFieldSource.class)) {
+ return getKeepFieldSourceEdges();
+ }
throw new RuntimeException();
}
@@ -32,6 +37,9 @@
if (clazz.equals(KeepClassAndDefaultConstructorSource.class)) {
return getKeepClassAndDefaultConstructorSourceExpected();
}
+ if (clazz.equals(KeepFieldSource.class)) {
+ return getKeepFieldSourceExpected();
+ }
throw new RuntimeException();
}
@@ -57,4 +65,21 @@
KeepEdge edge = KeepEdge.builder().setConsequences(consequences).build();
return Collections.singleton(edge);
}
+
+ public static String getKeepFieldSourceExpected() {
+ return StringUtils.lines("The values match!");
+ }
+
+ public static Set<KeepEdge> getKeepFieldSourceEdges() {
+ Class<?> clazz = KeepFieldSource.A.class;
+ KeepQualifiedClassNamePattern name = KeepQualifiedClassNamePattern.exact(clazz.getTypeName());
+ KeepFieldPattern fieldPattern =
+ KeepFieldPattern.builder().setNamePattern(KeepFieldNamePattern.exact("f")).build();
+ KeepItemPattern fieldItem =
+ KeepItemPattern.builder().setClassPattern(name).setMemberPattern(fieldPattern).build();
+ KeepTarget fieldTarget = KeepTarget.builder().setItem(fieldItem).build();
+ KeepConsequences consequences = KeepConsequences.builder().addTarget(fieldTarget).build();
+ KeepEdge edge = KeepEdge.builder().setConsequences(consequences).build();
+ return Collections.singleton(edge);
+ }
}