[KeepAnno] Add reading of non-extracted annotations in R8
This also extends the tests with a variant that will continue to extract out
keep rules with the current compiler version, so that we continue to track rule
interpretation.
Bug: b/323816623
Change-Id: I8cbf6b1a668fbb68aa8b92a35d41f73e0491d22e
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 85f651f..05f103b 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
@@ -89,6 +89,37 @@
public static int ASM_VERSION = ASM9;
+ public static boolean isClassKeepAnnotation(String descriptor, boolean visible) {
+ return !visible && (isExtractedAnnotation(descriptor) || isEmbeddedAnnotation(descriptor));
+ }
+
+ public static boolean isFieldKeepAnnotation(String descriptor, boolean visible) {
+ return !visible && isEmbeddedAnnotation(descriptor);
+ }
+
+ public static boolean isMethodKeepAnnotation(String descriptor, boolean visible) {
+ return !visible && isEmbeddedAnnotation(descriptor);
+ }
+
+ private static boolean isExtractedAnnotation(String descriptor) {
+ return ExtractedAnnotations.DESCRIPTOR.equals(descriptor);
+ }
+
+ private static boolean isEmbeddedAnnotation(String descriptor) {
+ switch (descriptor) {
+ case AnnotationConstants.Edge.DESCRIPTOR:
+ case AnnotationConstants.UsesReflection.DESCRIPTOR:
+ case AnnotationConstants.ForApi.DESCRIPTOR:
+ case AnnotationConstants.UsedByReflection.DESCRIPTOR:
+ case AnnotationConstants.UsedByNative.DESCRIPTOR:
+ case AnnotationConstants.CheckRemoved.DESCRIPTOR:
+ case AnnotationConstants.CheckOptimizedOut.DESCRIPTOR:
+ return true;
+ default:
+ return false;
+ }
+ }
+
public static List<KeepDeclaration> readKeepEdges(byte[] classFileBytes) {
return internalReadKeepEdges(classFileBytes, true, false);
}
@@ -107,6 +138,84 @@
return declarations;
}
+ public static AnnotationVisitor createClassKeepAnnotationVisitor(
+ String descriptor,
+ boolean visible,
+ boolean readEmbedded,
+ boolean readExtracted,
+ String className,
+ AnnotationParsingContext parsingContext,
+ Consumer<KeepDeclaration> callback) {
+ return KeepEdgeClassVisitor.createAnnotationVisitor(
+ descriptor,
+ visible,
+ readEmbedded,
+ readExtracted,
+ callback,
+ parsingContext,
+ className,
+ builder -> {
+ builder.setContextFromClassDescriptor(
+ KeepEdgeReaderUtils.getDescriptorFromClassTypeName(className));
+ });
+ }
+
+ public static AnnotationVisitor createFieldKeepAnnotationVisitor(
+ String descriptor,
+ boolean visible,
+ boolean readEmbedded,
+ boolean readExtracted,
+ String className,
+ String fieldName,
+ String fieldTypeDescriptor,
+ AnnotationParsingContext parsingContext,
+ Consumer<KeepDeclaration> callback) {
+ return KeepEdgeFieldVisitor.createAnnotationVisitor(
+ descriptor,
+ visible,
+ readEmbedded,
+ readExtracted,
+ callback::accept,
+ parsingContext,
+ className,
+ fieldName,
+ fieldTypeDescriptor,
+ builder -> {
+ builder.setContextFromFieldDescriptor(
+ KeepEdgeReaderUtils.getDescriptorFromClassTypeName(className),
+ fieldName,
+ fieldTypeDescriptor);
+ });
+ }
+
+ public static AnnotationVisitor createMethodKeepAnnotationVisitor(
+ String descriptor,
+ boolean visible,
+ boolean readEmbedded,
+ boolean readExtracted,
+ String className,
+ String methodName,
+ String methodDescriptor,
+ AnnotationParsingContext parsingContext,
+ Consumer<KeepDeclaration> callback) {
+ return KeepEdgeMethodVisitor.createAnnotationVisitor(
+ descriptor,
+ visible,
+ readEmbedded,
+ readExtracted,
+ callback::accept,
+ parsingContext,
+ className,
+ methodName,
+ methodDescriptor,
+ (KeepEdgeMetaInfo.Builder builder) -> {
+ builder.setContextFromMethodDescriptor(
+ KeepEdgeReaderUtils.getDescriptorFromClassTypeName(className),
+ methodName,
+ methodDescriptor);
+ });
+ }
+
private static KeepClassItemReference classReferenceFromName(String className) {
return KeepClassItemReference.fromClassNamePattern(
KeepQualifiedClassNamePattern.exact(className));
@@ -223,7 +332,7 @@
String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
className = binaryNameToTypeName(name);
- parsingContext = new ClassParsingContext(className);
+ parsingContext = ClassParsingContext.fromName(className);
}
private AnnotationParsingContext annotationParsingContext(String descriptor) {
@@ -232,56 +341,62 @@
@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
+ return createAnnotationVisitor(
+ descriptor,
+ visible,
+ readEmbedded,
+ readExtracted,
+ parent::accept,
+ annotationParsingContext(descriptor),
+ className,
+ this::setContext);
+ }
+
+ private static AnnotationVisitorBase createAnnotationVisitor(
+ String descriptor,
+ boolean visible,
+ boolean readEmbedded,
+ boolean readExtracted,
+ Consumer<KeepDeclaration> parent,
+ AnnotationParsingContext parsingContext,
+ String className,
+ Consumer<KeepEdgeMetaInfo.Builder> setContext) {
// Skip any visible annotations as @KeepEdge is not runtime visible.
if (visible) {
return null;
}
- if (readExtracted && descriptor.equals(ExtractedAnnotations.DESCRIPTOR)) {
- return new ExtractedAnnotationsVisitor(
- annotationParsingContext(descriptor), parent::accept);
+
+ if (readExtracted && isExtractedAnnotation(descriptor)) {
+ return new ExtractedAnnotationsVisitor(parsingContext, parent::accept);
}
- if (!readEmbedded) {
+ if (!readEmbedded || !isEmbeddedAnnotation(descriptor)) {
return null;
}
if (descriptor.equals(Edge.DESCRIPTOR)) {
- return new KeepEdgeVisitor(
- annotationParsingContext(descriptor), parent::accept, this::setContext);
+ return new KeepEdgeVisitor(parsingContext, parent::accept, setContext);
}
if (descriptor.equals(AnnotationConstants.UsesReflection.DESCRIPTOR)) {
KeepClassItemPattern classItem =
KeepClassItemPattern.builder()
.setClassNamePattern(KeepQualifiedClassNamePattern.exact(className))
.build();
- return new UsesReflectionVisitor(
- annotationParsingContext(descriptor), parent::accept, this::setContext, classItem);
+ return new UsesReflectionVisitor(parsingContext, parent::accept, setContext, classItem);
}
- if (descriptor.equals(AnnotationConstants.ForApi.DESCRIPTOR)) {
- return new ForApiClassVisitor(
- annotationParsingContext(descriptor), parent::accept, this::setContext, className);
+ if (descriptor.equals(ForApi.DESCRIPTOR)) {
+ return new ForApiClassVisitor(parsingContext, parent::accept, setContext, className);
}
- if (descriptor.equals(AnnotationConstants.UsedByReflection.DESCRIPTOR)
+ if (descriptor.equals(UsedByReflection.DESCRIPTOR)
|| descriptor.equals(AnnotationConstants.UsedByNative.DESCRIPTOR)) {
return new UsedByReflectionClassVisitor(
- annotationParsingContext(descriptor),
- parent::accept,
- this::setContext,
- className);
+ parsingContext, parent::accept, setContext, className);
}
if (descriptor.equals(AnnotationConstants.CheckRemoved.DESCRIPTOR)) {
return new CheckRemovedClassVisitor(
- annotationParsingContext(descriptor),
- parent::accept,
- this::setContext,
- className,
- KeepCheckKind.REMOVED);
+ parsingContext, parent::accept, setContext, className, KeepCheckKind.REMOVED);
}
if (descriptor.equals(AnnotationConstants.CheckOptimizedOut.DESCRIPTOR)) {
return new CheckRemovedClassVisitor(
- annotationParsingContext(descriptor),
- parent::accept,
- this::setContext,
- className,
- KeepCheckKind.OPTIMIZED_OUT);
+ parsingContext, parent::accept, setContext, className, KeepCheckKind.OPTIMIZED_OUT);
}
return null;
}
@@ -334,7 +449,8 @@
new MethodParsingContext(classParsingContext, methodName, methodDescriptor);
}
- private KeepMemberItemPattern createMethodItemContext() {
+ private static KeepMemberItemPattern createMethodItemContext(
+ String className, String methodName, String methodDescriptor) {
String returnTypeDescriptor = Type.getReturnType(methodDescriptor).getDescriptor();
Type[] argumentTypes = Type.getArgumentTypes(methodDescriptor);
KeepMethodParametersPattern.Builder builder = KeepMethodParametersPattern.builder();
@@ -363,50 +479,77 @@
@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
+ return createAnnotationVisitor(
+ descriptor,
+ visible,
+ true,
+ false,
+ parent::accept,
+ annotationParsingContext(descriptor),
+ className,
+ methodName,
+ methodDescriptor,
+ this::setContext);
+ }
+
+ public static AnnotationVisitor createAnnotationVisitor(
+ String descriptor,
+ boolean visible,
+ boolean readEmbedded,
+ boolean readExtracted,
+ Consumer<KeepDeclaration> parent,
+ AnnotationParsingContext parsingContext,
+ String className,
+ String methodName,
+ String methodDescriptor,
+ Consumer<KeepEdgeMetaInfo.Builder> setContext) {
// Skip any visible annotations as @KeepEdge is not runtime visible.
if (visible) {
return null;
}
+ if (!readEmbedded) {
+ // Only the embedded annotations can be on fields.
+ return null;
+ }
if (descriptor.equals(Edge.DESCRIPTOR)) {
- return new KeepEdgeVisitor(
- annotationParsingContext(descriptor), parent::accept, this::setContext);
+ return new KeepEdgeVisitor(parsingContext, parent::accept, setContext);
}
if (descriptor.equals(AnnotationConstants.UsesReflection.DESCRIPTOR)) {
return new UsesReflectionVisitor(
- annotationParsingContext(descriptor),
+ parsingContext,
parent::accept,
- this::setContext,
- createMethodItemContext());
+ setContext,
+ createMethodItemContext(className, methodName, methodDescriptor));
}
if (descriptor.equals(AnnotationConstants.ForApi.DESCRIPTOR)) {
return new ForApiMemberVisitor(
- annotationParsingContext(descriptor),
+ parsingContext,
parent::accept,
- this::setContext,
- createMethodItemContext());
+ setContext,
+ createMethodItemContext(className, methodName, methodDescriptor));
}
if (descriptor.equals(AnnotationConstants.UsedByReflection.DESCRIPTOR)
|| descriptor.equals(AnnotationConstants.UsedByNative.DESCRIPTOR)) {
return new UsedByReflectionMemberVisitor(
- annotationParsingContext(descriptor),
+ parsingContext,
parent::accept,
- this::setContext,
- createMethodItemContext());
+ setContext,
+ createMethodItemContext(className, methodName, methodDescriptor));
}
if (descriptor.equals(AnnotationConstants.CheckRemoved.DESCRIPTOR)) {
return new CheckRemovedMemberVisitor(
- annotationParsingContext(descriptor),
+ parsingContext,
parent::accept,
- this::setContext,
- createMethodItemContext(),
+ setContext,
+ createMethodItemContext(className, methodName, methodDescriptor),
KeepCheckKind.REMOVED);
}
if (descriptor.equals(AnnotationConstants.CheckOptimizedOut.DESCRIPTOR)) {
return new CheckRemovedMemberVisitor(
- annotationParsingContext(descriptor),
+ parsingContext,
parent::accept,
- this::setContext,
- createMethodItemContext(),
+ setContext,
+ createMethodItemContext(className, methodName, methodDescriptor),
KeepCheckKind.OPTIMIZED_OUT);
}
return null;
@@ -422,7 +565,7 @@
private final Parent<KeepEdge> parent;
private final String className;
private final String fieldName;
- private final String fieldDescriptor;
+ private final String fieldTypeDescriptor;
private final FieldParsingContext parsingContext;
KeepEdgeFieldVisitor(
@@ -430,23 +573,24 @@
Parent<KeepEdge> parent,
String className,
String fieldName,
- String fieldDescriptor) {
+ String fieldTypeDescriptor) {
super(ASM_VERSION);
this.parent = parent;
this.className = className;
this.fieldName = fieldName;
- this.fieldDescriptor = fieldDescriptor;
+ this.fieldTypeDescriptor = fieldTypeDescriptor;
this.parsingContext =
- new FieldParsingContext(classParsingContext, fieldName, fieldDescriptor);
+ new FieldParsingContext(classParsingContext, fieldName, fieldTypeDescriptor);
}
private AnnotationParsingContext annotationParsingContext(String descriptor) {
return parsingContext.annotation(descriptor);
}
- private KeepMemberItemPattern createMemberItemContext() {
+ private static KeepMemberItemPattern createMemberItemContext(
+ String className, String fieldName, String fieldTypeDescriptor) {
KeepFieldTypePattern typePattern =
- KeepFieldTypePattern.fromType(KeepTypePattern.fromDescriptor(fieldDescriptor));
+ KeepFieldTypePattern.fromType(KeepTypePattern.fromDescriptor(fieldTypeDescriptor));
return KeepMemberItemPattern.builder()
.setClassReference(classReferenceFromName(className))
.setMemberPattern(
@@ -459,39 +603,67 @@
private void setContext(KeepEdgeMetaInfo.Builder builder) {
builder.setContextFromFieldDescriptor(
- KeepEdgeReaderUtils.getDescriptorFromJavaType(className), fieldName, fieldDescriptor);
+ KeepEdgeReaderUtils.getDescriptorFromJavaType(className), fieldName, fieldTypeDescriptor);
}
@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
+ return createAnnotationVisitor(
+ descriptor,
+ visible,
+ true,
+ false,
+ parent::accept,
+ annotationParsingContext(descriptor),
+ className,
+ fieldName,
+ fieldTypeDescriptor,
+ this::setContext);
+ }
+
+ public static AnnotationVisitorBase createAnnotationVisitor(
+ String descriptor,
+ boolean visible,
+ boolean readEmbedded,
+ boolean readExtracted,
+ Consumer<KeepEdge> parent,
+ AnnotationParsingContext parsingContext,
+ String className,
+ String fieldName,
+ String fieldTypeDescriptor,
+ Consumer<KeepEdgeMetaInfo.Builder> setContext) {
// Skip any visible annotations as @KeepEdge is not runtime visible.
if (visible) {
return null;
}
+ if (!readEmbedded) {
+ // Only the embedded annotations can be on fields.
+ return null;
+ }
if (descriptor.equals(Edge.DESCRIPTOR)) {
- return new KeepEdgeVisitor(annotationParsingContext(descriptor), parent, this::setContext);
+ return new KeepEdgeVisitor(parsingContext, parent::accept, setContext);
}
if (descriptor.equals(AnnotationConstants.UsesReflection.DESCRIPTOR)) {
return new UsesReflectionVisitor(
- annotationParsingContext(descriptor),
- parent,
- this::setContext,
- createMemberItemContext());
+ parsingContext,
+ parent::accept,
+ setContext,
+ createMemberItemContext(className, fieldName, fieldTypeDescriptor));
}
- if (descriptor.equals(AnnotationConstants.ForApi.DESCRIPTOR)) {
+ if (descriptor.equals(ForApi.DESCRIPTOR)) {
return new ForApiMemberVisitor(
- annotationParsingContext(descriptor),
- parent,
- this::setContext,
- createMemberItemContext());
+ parsingContext,
+ parent::accept,
+ setContext,
+ createMemberItemContext(className, fieldName, fieldTypeDescriptor));
}
- if (descriptor.equals(AnnotationConstants.UsedByReflection.DESCRIPTOR)
+ if (descriptor.equals(UsedByReflection.DESCRIPTOR)
|| descriptor.equals(AnnotationConstants.UsedByNative.DESCRIPTOR)) {
return new UsedByReflectionMemberVisitor(
- annotationParsingContext(descriptor),
- parent,
- this::setContext,
- createMemberItemContext());
+ parsingContext,
+ parent::accept,
+ setContext,
+ createMemberItemContext(className, fieldName, fieldTypeDescriptor));
}
return null;
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReaderUtils.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReaderUtils.java
index 21645d5..78eddd6 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReaderUtils.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReaderUtils.java
@@ -20,7 +20,11 @@
}
public static String getDescriptorFromClassTypeName(String classTypeName) {
- return "L" + getBinaryNameFromClassTypeName(classTypeName) + ";";
+ return getDescriptorFromBinaryName(getBinaryNameFromClassTypeName(classTypeName));
+ }
+
+ public static String getDescriptorFromBinaryName(String binaryName) {
+ return "L" + binaryName + ";";
}
public static String getJavaTypeFromDescriptor(String descriptor) {
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/ParsingContext.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/ParsingContext.java
index 5f6bb55..11eefa5 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/ParsingContext.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/ParsingContext.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.keepanno.asm.KeepEdgeReaderUtils.getJavaTypeFromDescriptor;
+import com.android.tools.r8.keepanno.asm.KeepEdgeReaderUtils;
import org.objectweb.asm.Type;
public abstract class ParsingContext {
@@ -62,10 +63,19 @@
public static class ClassParsingContext extends ParsingContext {
private final String className;
- public ClassParsingContext(String className) {
+ private ClassParsingContext(String className) {
this.className = className;
}
+ public static ClassParsingContext fromName(String className) {
+ return new ClassParsingContext(className);
+ }
+
+ public static ClassParsingContext fromDescriptor(String descriptor) {
+ return ClassParsingContext.fromName(
+ KeepEdgeReaderUtils.getJavaTypeFromDescriptor(descriptor));
+ }
+
@Override
public String getHolderName() {
return className;
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index 978932d..4941b96 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -35,9 +35,11 @@
import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.jar.CfApplicationWriter;
-import com.android.tools.r8.keepanno.asm.KeepEdgeReader.ExtractedAnnotationsVisitor;
-import com.android.tools.r8.keepanno.ast.AnnotationConstants.ExtractedAnnotations;
+import com.android.tools.r8.keepanno.asm.KeepEdgeReader;
+import com.android.tools.r8.keepanno.ast.ParsingContext.AnnotationParsingContext;
import com.android.tools.r8.keepanno.ast.ParsingContext.ClassParsingContext;
+import com.android.tools.r8.keepanno.ast.ParsingContext.FieldParsingContext;
+import com.android.tools.r8.keepanno.ast.ParsingContext.MethodParsingContext;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.synthesis.SyntheticMarker;
import com.android.tools.r8.utils.AsmUtils;
@@ -453,17 +455,25 @@
return new CreateMethodVisitor(access, name, desc, signature, exceptions, this);
}
+ public boolean shouldSkipKeepAnnotations() {
+ return classKind != ClassKind.PROGRAM
+ || !application.options.testing.isKeepAnnotationsEnabled();
+ }
+
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
- if (!visible && ExtractedAnnotations.DESCRIPTOR.equals(desc)) {
- if (!application.options.testing.enableExtractedKeepAnnotations) {
+ if (KeepEdgeReader.isClassKeepAnnotation(desc, visible)) {
+ if (shouldSkipKeepAnnotations()) {
return null;
}
- if (classKind != ClassKind.PROGRAM) {
- return null;
- }
- return new ExtractedAnnotationsVisitor(
- new ClassParsingContext(type.getName()).annotation(desc),
+ String className = type.getTypeName();
+ return KeepEdgeReader.createClassKeepAnnotationVisitor(
+ desc,
+ visible,
+ application.options.testing.enableEmbeddedKeepAnnotations,
+ application.options.testing.enableExtractedKeepAnnotations,
+ className,
+ ClassParsingContext.fromName(className).annotation(desc),
application::addKeepDeclaration);
}
return createAnnotationVisitor(
@@ -660,7 +670,7 @@
private final CreateDexClassVisitor<?> parent;
private final int access;
private final String name;
- private final String desc;
+ private final String fieldTypeDescriptor;
private final Object value;
private final FieldTypeSignature fieldSignature;
private List<DexAnnotation> annotations = null;
@@ -676,7 +686,7 @@
this.parent = parent;
this.access = access;
this.name = name;
- this.desc = desc;
+ this.fieldTypeDescriptor = desc;
this.value = value;
this.fieldSignature =
parent.application.options.parseSignatureAttribute()
@@ -691,6 +701,26 @@
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ if (KeepEdgeReader.isFieldKeepAnnotation(desc, visible)) {
+ if (parent.shouldSkipKeepAnnotations()) {
+ return null;
+ }
+ String className = parent.type.getTypeName();
+ AnnotationParsingContext parsingContext =
+ new FieldParsingContext(
+ ClassParsingContext.fromName(className), name, fieldTypeDescriptor)
+ .annotation(desc);
+ return KeepEdgeReader.createFieldKeepAnnotationVisitor(
+ desc,
+ visible,
+ parent.application.options.testing.enableEmbeddedKeepAnnotations,
+ parent.application.options.testing.enableExtractedKeepAnnotations,
+ className,
+ name,
+ fieldTypeDescriptor,
+ parsingContext,
+ parent.application::addKeepDeclaration);
+ }
return createAnnotationVisitor(
desc, visible, getAnnotations(), parent.application, DexAnnotation::new);
}
@@ -705,7 +735,7 @@
@Override
public void visitEnd() {
FieldAccessFlags flags = createFieldAccessFlags(access);
- DexField dexField = parent.application.getField(parent.type, name, desc);
+ DexField dexField = parent.application.getField(parent.type, name, fieldTypeDescriptor);
parent.application.checkFieldForRecord(dexField, parent.classKind);
parent.application.checkFieldForMethodHandlesLookup(dexField, parent.classKind);
parent.application.checkFieldForVarHandle(dexField, parent.classKind);
@@ -785,6 +815,7 @@
private static class CreateMethodVisitor extends MethodVisitor {
private final String name;
+ private final String methodDescriptor;
final CreateDexClassVisitor<?> parent;
private final int parameterCount;
private List<DexAnnotation> annotations = null;
@@ -808,6 +839,7 @@
CreateDexClassVisitor<?> parent) {
super(ASM_VERSION);
this.name = name;
+ this.methodDescriptor = desc;
this.parent = parent;
this.method = parent.application.getMethod(parent.type, name, desc);
this.flags = createMethodAccessFlags(name, access);
@@ -834,6 +866,26 @@
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ if (KeepEdgeReader.isMethodKeepAnnotation(desc, visible)) {
+ if (parent.shouldSkipKeepAnnotations()) {
+ return null;
+ }
+ String className = parent.type.getTypeName();
+ AnnotationParsingContext parsingContext =
+ new MethodParsingContext(
+ ClassParsingContext.fromName(className), name, methodDescriptor)
+ .annotation(desc);
+ return KeepEdgeReader.createMethodKeepAnnotationVisitor(
+ desc,
+ visible,
+ parent.application.options.testing.enableEmbeddedKeepAnnotations,
+ parent.application.options.testing.enableExtractedKeepAnnotations,
+ className,
+ name,
+ methodDescriptor,
+ parsingContext,
+ parent.application::addKeepDeclaration);
+ }
return createAnnotationVisitor(
desc, visible, getAnnotations(), parent.application, DexAnnotation::new);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 760cede..fdc9cbd 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -1637,7 +1637,7 @@
}
void traceMethodPosition(com.android.tools.r8.ir.code.Position position, ProgramMethod context) {
- if (!options.testing.enableExtractedKeepAnnotations) {
+ if (!options.testing.isKeepAnnotationsEnabled()) {
// Currently inlining is only intended for the evaluation of keep annotation edges.
return;
}
@@ -3452,6 +3452,7 @@
}
public boolean isOriginalReferenceEffectivelyLive(DexReference reference) {
+ assert options.testing.isKeepAnnotationsEnabled();
// The effectively-live original set contains types, fields and methods witnessed by
// instructions, such as method inlining positions.
return effectivelyLiveOriginalReferences.contains(reference);
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 8e9e3e1..f284b2c 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -2184,6 +2184,11 @@
public static class TestingOptions {
public boolean enableExtractedKeepAnnotations = false;
+ public boolean enableEmbeddedKeepAnnotations = false;
+
+ public boolean isKeepAnnotationsEnabled() {
+ return enableExtractedKeepAnnotations || enableEmbeddedKeepAnnotations;
+ }
public boolean enableNumberUnboxer = false;
public boolean printNumberUnboxed = false;
diff --git a/src/test/java/com/android/tools/r8/keepanno/CheckOptimizedOutAnnotationTest.java b/src/test/java/com/android/tools/r8/keepanno/CheckOptimizedOutAnnotationTest.java
index 59915bc9..656bb6b 100644
--- a/src/test/java/com/android/tools/r8/keepanno/CheckOptimizedOutAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/CheckOptimizedOutAnnotationTest.java
@@ -10,6 +10,7 @@
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
@@ -54,12 +55,12 @@
}
@Test
- public void testR8Native() throws Throwable {
- assumeTrue(parameters.isR8() && parameters.isNative());
+ public void testCurrentR8() throws Throwable {
+ assumeTrue(parameters.isR8() && parameters.isCurrentR8());
testForKeepAnno(parameters)
.addProgramClasses(getInputClasses())
.addKeepMainRule(TestClass.class)
- .applyIfR8Native(
+ .applyIfR8Current(
b ->
b.allowDiagnosticWarningMessages()
.setDiagnosticsLevelModifier(
@@ -86,8 +87,9 @@
}
@Test
- public void testR8Extract() throws Throwable {
- assumeTrue(parameters.isR8() && !parameters.isNative());
+ public void testLegacyR8() throws Throwable {
+ assumeTrue(parameters.isR8() && !parameters.isCurrentR8());
+ assertTrue(parameters.isLegacyR8());
try {
testForKeepAnno(parameters)
.addProgramClasses(getInputClasses())
diff --git a/src/test/java/com/android/tools/r8/keepanno/CheckRemovedAnnotationTest.java b/src/test/java/com/android/tools/r8/keepanno/CheckRemovedAnnotationTest.java
index b3bedf1..4010ef8 100644
--- a/src/test/java/com/android/tools/r8/keepanno/CheckRemovedAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/CheckRemovedAnnotationTest.java
@@ -10,6 +10,7 @@
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
@@ -53,12 +54,12 @@
}
@Test
- public void testR8Native() throws Exception {
- assumeTrue(parameters.isR8() && parameters.isNative());
+ public void testCurrentR8() throws Exception {
+ assumeTrue(parameters.isR8() && parameters.isCurrentR8());
testForKeepAnno(parameters)
.addProgramClasses(getInputClasses())
.addKeepMainRule(TestClass.class)
- .applyIfR8Native(
+ .applyIfR8Current(
b ->
b.allowDiagnosticWarningMessages()
.setDiagnosticsLevelModifier(
@@ -89,8 +90,9 @@
}
@Test
- public void testR8Extract() throws Throwable {
- assumeTrue(parameters.isR8() && !parameters.isNative());
+ public void testLegacyR8() throws Throwable {
+ assumeTrue(parameters.isR8() && !parameters.isCurrentR8());
+ assertTrue(parameters.isLegacyR8());
try {
testForKeepAnno(parameters)
.addProgramClasses(getInputClasses())
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepAnnoParameters.java b/src/test/java/com/android/tools/r8/keepanno/KeepAnnoParameters.java
index 53c7634..04e7bf8 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepAnnoParameters.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepAnnoParameters.java
@@ -13,7 +13,8 @@
public enum KeepAnnoConfig {
REFERENCE,
R8_DIRECT,
- R8_EXTRACT,
+ R8_NORMALIZED,
+ R8_RULES,
R8_LEGACY,
PG;
}
@@ -56,21 +57,27 @@
}
public boolean isR8() {
- return config == KeepAnnoConfig.R8_DIRECT
- || config == KeepAnnoConfig.R8_EXTRACT
- || config == KeepAnnoConfig.R8_LEGACY;
+ return isCurrentR8() || isLegacyR8();
}
public boolean isPG() {
return config == KeepAnnoConfig.PG;
}
- public boolean isNative() {
- return config == KeepAnnoConfig.R8_DIRECT || config == KeepAnnoConfig.R8_EXTRACT;
+ public boolean isCurrentR8() {
+ return isNativeR8() || config == KeepAnnoConfig.R8_RULES;
}
- public boolean isExtract() {
- return config == KeepAnnoConfig.R8_EXTRACT
+ public boolean isLegacyR8() {
+ return config == KeepAnnoConfig.R8_LEGACY;
+ }
+
+ public boolean isNativeR8() {
+ return config == KeepAnnoConfig.R8_DIRECT || config == KeepAnnoConfig.R8_NORMALIZED;
+ }
+
+ public boolean isExtractRules() {
+ return config == KeepAnnoConfig.R8_RULES
|| config == KeepAnnoConfig.R8_LEGACY
|| config == KeepAnnoConfig.PG;
}
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBase.java b/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBase.java
index 86d9529..35c00ff 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBase.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBase.java
@@ -22,7 +22,9 @@
keepAnnoParams.add(
new KeepAnnoParameters(parameters, KeepAnnoParameters.KeepAnnoConfig.R8_DIRECT));
keepAnnoParams.add(
- new KeepAnnoParameters(parameters, KeepAnnoParameters.KeepAnnoConfig.R8_EXTRACT));
+ new KeepAnnoParameters(parameters, KeepAnnoParameters.KeepAnnoConfig.R8_NORMALIZED));
+ keepAnnoParams.add(
+ new KeepAnnoParameters(parameters, KeepAnnoParameters.KeepAnnoConfig.R8_RULES));
keepAnnoParams.add(
new KeepAnnoParameters(parameters, KeepAnnoParameters.KeepAnnoConfig.R8_LEGACY));
if (parameters.isCfRuntime()) {
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBuilder.java b/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBuilder.java
index 9a3c1e8..1105bd3 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBuilder.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.ClassAccessFlags;
+import com.android.tools.r8.keepanno.KeepAnnoParameters.KeepAnnoConfig;
import com.android.tools.r8.keepanno.asm.KeepEdgeReader;
import com.android.tools.r8.keepanno.asm.KeepEdgeWriter;
import com.android.tools.r8.keepanno.ast.KeepDeclaration;
@@ -41,9 +42,9 @@
case REFERENCE:
return new ReferenceBuilder(params, temp);
case R8_DIRECT:
- return new R8NativeBuilder(false, params, temp);
- case R8_EXTRACT:
- return new R8NativeBuilder(true, params, temp);
+ case R8_NORMALIZED:
+ case R8_RULES:
+ return new R8NativeBuilder(params, temp);
case R8_LEGACY:
return new R8LegacyBuilder(params, temp);
case PG:
@@ -96,7 +97,7 @@
return this;
}
- public KeepAnnoTestBuilder applyIfR8Native(ThrowableConsumer<R8TestBuilder<?>> builderConsumer) {
+ public KeepAnnoTestBuilder applyIfR8Current(ThrowableConsumer<R8TestBuilder<?>> builderConsumer) {
return this;
}
@@ -113,7 +114,7 @@
}
public final KeepAnnoTestBuilder allowUnusedProguardConfigurationRules() {
- return applyIfR8Native(R8TestBuilder::allowUnusedProguardConfigurationRules);
+ return applyIfR8Current(R8TestBuilder::allowUnusedProguardConfigurationRules);
}
public final KeepAnnoTestBuilder allowAccessModification() {
@@ -167,17 +168,18 @@
private final R8FullTestBuilder builder;
private List<Consumer<R8TestCompileResult>> compileResultConsumers = new ArrayList<>();
- private final boolean useEdgeExtraction;
+ private final boolean normalizeEdges;
+ private final boolean extractRules;
- private R8NativeBuilder(
- boolean useEdgeExtraction, KeepAnnoParameters params, TemporaryFolder temp) {
+ private R8NativeBuilder(KeepAnnoParameters params, TemporaryFolder temp) {
super(params, temp);
builder =
TestBase.testForR8(temp, parameters().getBackend())
.enableExperimentalKeepAnnotations()
.setMinApi(parameters());
- this.useEdgeExtraction = useEdgeExtraction;
- if (useEdgeExtraction) {
+ extractRules = params.config() == KeepAnnoConfig.R8_RULES;
+ normalizeEdges = params.config() == KeepAnnoConfig.R8_NORMALIZED;
+ if (normalizeEdges) {
builder.getBuilder().setEnableExperimentalKeepAnnotations(false);
builder.getBuilder().setEnableExperimentalExtractedKeepAnnotations(true);
} else {
@@ -188,12 +190,18 @@
@Override
public KeepAnnoTestBuilder enableNativeInterpretation() {
- if (useEdgeExtraction) {
- // This enables native interpretation of the extracted edges.
- builder.addOptionsModification(o -> o.testing.enableExtractedKeepAnnotations = true);
- // This disables converting the extracted edges to PG rules in the command reader.
- builder.getBuilder().setEnableExperimentalExtractedKeepAnnotations(false);
+ if (extractRules) {
+ return this;
}
+ // This enables native interpretation of all keep annotations.
+ builder.addOptionsModification(
+ o -> {
+ o.testing.enableExtractedKeepAnnotations = true;
+ o.testing.enableEmbeddedKeepAnnotations = true;
+ });
+ // This disables all reading of annotations in the command reader.
+ builder.getBuilder().setEnableExperimentalKeepAnnotations(false);
+ builder.getBuilder().setEnableExperimentalExtractedKeepAnnotations(false);
return this;
}
@@ -205,7 +213,7 @@
}
@Override
- public KeepAnnoTestBuilder applyIfR8Native(
+ public KeepAnnoTestBuilder applyIfR8Current(
ThrowableConsumer<R8TestBuilder<?>> builderConsumer) {
builderConsumer.acceptWithRuntimeException(builder);
return this;
@@ -237,7 +245,7 @@
private void extractAndAdd(byte[] classFileData) {
builder.addProgramClassFileData(classFileData);
- if (useEdgeExtraction) {
+ if (normalizeEdges) {
List<KeepDeclaration> declarations = KeepEdgeReader.readKeepEdges(classFileData);
if (!declarations.isEmpty()) {
String binaryName =
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepEmptyClassTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepEmptyClassTest.java
index 44306ed..a80be3c 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepEmptyClassTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepEmptyClassTest.java
@@ -3,8 +3,11 @@
// 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.isPresentAndNotRenamed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.keepanno.annotations.KeepItemKind;
import com.android.tools.r8.keepanno.annotations.KeepTarget;
@@ -13,6 +16,7 @@
import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
import java.util.List;
@@ -53,6 +57,18 @@
private void checkOutput(CodeInspector inspector) {
assertThat(inspector.clazz(B.class), isPresentAndNotRenamed());
+ ClassSubject classA = inspector.clazz(A.class);
+ if (parameters.isNativeR8()) {
+ assertThat(classA, isAbsent());
+ } else {
+ assertTrue(parameters.isExtractRules());
+ // PG and R8 with keep rules will keep the residual class.
+ assertThat(classA, isPresentAndRenamed());
+ // R8 using keep rules will soft-pin the precondition method too.
+ assertThat(
+ classA.uniqueMethodWithOriginalName("foo"),
+ parameters.isPG() ? isAbsent() : isPresentAndRenamed());
+ }
}
static class A {
@@ -60,7 +76,7 @@
// Pattern includes any members
@UsesReflection(@KeepTarget(classConstant = B.class, kind = KeepItemKind.CLASS_AND_MEMBERS))
public void foo() throws Exception {
- String typeName = B.class.getTypeName();
+ String typeName = B.class.getName();
int memberCount = B.class.getDeclaredMethods().length + B.class.getDeclaredFields().length;
System.out.println(typeName.substring(typeName.length() - 1) + ", #members: " + memberCount);
}