Add support for type annotations when compiling to CF with R8
Bug: b/271543766
Change-Id: I55d20d6b4b2b8b1562a49a0b6e9f0283c232aa50
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
index e24a22c..f72877a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
@@ -62,7 +62,7 @@
private static final int UNKNOWN_API_LEVEL = -1;
private static final int NOT_SET_API_LEVEL = -2;
- private static void specify(StructuralSpecification<DexAnnotation, ?> spec) {
+ protected static void specify(StructuralSpecification<DexAnnotation, ?> spec) {
spec.withItem(a -> a.annotation).withInt(a -> a.visibility);
}
@@ -71,6 +71,14 @@
this.annotation = annotation;
}
+ public boolean isTypeAnnotation() {
+ return false;
+ }
+
+ public DexTypeAnnotation asTypeAnnotation() {
+ return null;
+ }
+
@Override
public DexAnnotation self() {
return this;
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
index bcddd66..1cd2f4b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
@@ -81,6 +81,8 @@
public static DexType findDuplicateEntryType(List<DexAnnotation> annotations) {
Set<DexType> seenTypes = Sets.newIdentityHashSet();
for (DexAnnotation annotation : annotations) {
+ // This is only reachable from DEX where type annotations are not supported.
+ assert !annotation.isTypeAnnotation();
if (!seenTypes.add(annotation.annotation.type)) {
return annotation.annotation.type;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexTypeAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexTypeAnnotation.java
new file mode 100644
index 0000000..99dadb8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexTypeAnnotation.java
@@ -0,0 +1,70 @@
+// Copyright (c) 2023, 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.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.utils.structural.StructuralMapping;
+import org.objectweb.asm.TypePath;
+
+public class DexTypeAnnotation extends DexAnnotation {
+
+ private final int typeRef;
+ private final TypePath typePath;
+
+ public DexTypeAnnotation(
+ int visibility, DexEncodedAnnotation annotation, int typeRef, TypePath typePath) {
+ super(visibility, annotation);
+ this.typeRef = typeRef;
+ this.typePath = typePath;
+ }
+
+ @Override
+ public boolean isTypeAnnotation() {
+ return true;
+ }
+
+ @Override
+ public DexTypeAnnotation asTypeAnnotation() {
+ return this;
+ }
+
+ @Override
+ public void collectIndexedItems(AppView<?> appView, IndexedItemCollection indexedItems) {
+ throw new Unreachable("Should not collect type annotation in DEX");
+ }
+
+ @Override
+ void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+ throw new Unreachable("Should not collect type annotation in DEX");
+ }
+
+ public int getTypeRef() {
+ return typeRef;
+ }
+
+ public TypePath getTypePath() {
+ return typePath;
+ }
+
+ @Override
+ public StructuralMapping<DexAnnotation> getStructuralMapping() {
+ return spec ->
+ spec.withInt(t -> typeRef)
+ .withIntArray(
+ annotation -> {
+ int totalCount = typePath.getLength() * 2;
+ int[] serializedArr = new int[totalCount];
+ for (int i = 0; i < totalCount; i++) {
+ int startIndex = i * 2;
+ serializedArr[startIndex] = typePath.getStep(i);
+ serializedArr[startIndex + 1] = typePath.getStepArgument(i);
+ }
+ return serializedArr;
+ })
+ .withSpec(DexAnnotation::specify);
+ }
+}
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 76ca9ac..368ffea 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -56,6 +56,7 @@
import java.util.Objects;
import java.util.Set;
import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.zip.CRC32;
import org.objectweb.asm.AnnotationVisitor;
@@ -175,22 +176,46 @@
return MethodAccessFlags.fromCfAccessFlags(cleanAccessFlags(access), isConstructor);
}
- private static AnnotationVisitor createAnnotationVisitor(String desc, boolean visible,
+ private static AnnotationVisitor createAnnotationVisitor(
+ String desc,
+ boolean visible,
List<DexAnnotation> annotations,
- JarApplicationReader application) {
- assert annotations != null;
+ JarApplicationReader application,
+ BiFunction<Integer, DexEncodedAnnotation, DexAnnotation> newAnnotationConstructor) {
+ assert newAnnotationConstructor != null;
if (visible || retainCompileTimeAnnotation(desc, application)) {
- int visiblity = visible ? DexAnnotation.VISIBILITY_RUNTIME : DexAnnotation.VISIBILITY_BUILD;
+ int visibility = visible ? DexAnnotation.VISIBILITY_RUNTIME : DexAnnotation.VISIBILITY_BUILD;
return new CreateAnnotationVisitor(
application,
(names, values) ->
annotations.add(
- new DexAnnotation(
- visiblity, createEncodedAnnotation(desc, names, values, application))));
+ newAnnotationConstructor.apply(
+ visibility, createEncodedAnnotation(desc, names, values, application))));
}
return null;
}
+ private static AnnotationVisitor createTypeAnnotationVisitor(
+ String desc,
+ boolean visible,
+ List<DexAnnotation> annotations,
+ JarApplicationReader application,
+ int typeRef,
+ TypePath typePath) {
+ assert annotations != null;
+ // Java 8 type annotations are not supported by Dex. Ignore them.
+ if (!application.options.isGeneratingClassFiles()) {
+ return null;
+ }
+ return createAnnotationVisitor(
+ desc,
+ visible,
+ annotations,
+ application,
+ (visibility, annotation) ->
+ new DexTypeAnnotation(visibility, annotation, typeRef, typePath));
+ }
+
private static boolean retainCompileTimeAnnotation(
String desc, JarApplicationReader application) {
return DexAnnotation.retainCompileTimeAnnotation(
@@ -446,14 +471,15 @@
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
- return createAnnotationVisitor(desc, visible, getAnnotations(), application);
+ return createAnnotationVisitor(
+ desc, visible, getAnnotations(), application, DexAnnotation::new);
}
@Override
public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc,
boolean visible) {
- // Java 8 type annotations are not supported by Dex, thus ignore them.
- return null;
+ return createTypeAnnotationVisitor(
+ desc, visible, getAnnotations(), application, typeRef, typePath);
}
@Override
@@ -674,14 +700,15 @@
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
- return createAnnotationVisitor(desc, visible, getAnnotations(), parent.application);
+ return createAnnotationVisitor(
+ desc, visible, getAnnotations(), parent.application, DexAnnotation::new);
}
@Override
public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc,
boolean visible) {
- // Java 8 type annotations are not supported by Dex, thus ignore them.
- return null;
+ return createTypeAnnotationVisitor(
+ desc, visible, getAnnotations(), parent.application, typeRef, typePath);
}
@Override
@@ -815,7 +842,8 @@
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
- return createAnnotationVisitor(desc, visible, getAnnotations(), parent.application);
+ return createAnnotationVisitor(
+ desc, visible, getAnnotations(), parent.application, DexAnnotation::new);
}
@Override
@@ -827,13 +855,6 @@
}
@Override
- public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc,
- boolean visible) {
- // Java 8 type annotations are not supported by Dex, thus ignore them.
- return null;
- }
-
- @Override
public void visitAnnotableParameterCount(int parameterCount, boolean visible) {
if (annotableParameterCount != -1) {
// TODO(113565942): We assume that the runtime visible and runtime invisible parameter
@@ -856,28 +877,42 @@
}
}
assert mv == null;
- return createAnnotationVisitor(desc, visible,
- parameterAnnotationsLists.get(parameter), parent.application);
+ return createAnnotationVisitor(
+ desc,
+ visible,
+ parameterAnnotationsLists.get(parameter),
+ parent.application,
+ DexAnnotation::new);
+ }
+
+ @Override
+ public AnnotationVisitor visitTypeAnnotation(
+ int typeRef, TypePath typePath, String desc, boolean visible) {
+ return createTypeAnnotationVisitor(
+ desc, visible, getAnnotations(), parent.application, typeRef, typePath);
}
@Override
public AnnotationVisitor visitInsnAnnotation(int typeRef, TypePath typePath, String desc,
boolean visible) {
- // Java 8 type annotations are not supported by Dex, thus ignore them.
+ // We do not support code type annotations since that would require us to maintain these
+ // through IR where we may as well invalidate any assumptions made on the code.
return null;
}
@Override
public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath,
Label[] start, Label[] end, int[] index, String desc, boolean visible) {
- // Java 8 type annotations are not supported by Dex, thus ignore them.
+ // We do not support code type annotations since that would require us to maintain these
+ // through IR where we may as well invalidate any assumptions made on the code.
return null;
}
@Override
public AnnotationVisitor visitTryCatchAnnotation(int typeRef, TypePath typePath, String desc,
boolean visible) {
- // Java 8 type annotations are not supported by Dex, thus ignore them.
+ // We do not support code type annotations since that would require us to maintain these
+ // through IR where we may as well invalidate any assumptions made on the code.
return null;
}
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index cc88ca9..f10a6cf 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -30,6 +30,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeAnnotation;
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexValue.DexValueAnnotation;
import com.android.tools.r8.graph.DexValue.DexValueArray;
@@ -72,6 +73,7 @@
import org.objectweb.asm.MethodTooLargeException;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
+import org.objectweb.asm.TypePath;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.util.CheckClassAdapter;
@@ -268,7 +270,8 @@
assert SyntheticNaming.verifyNotInternalSynthetic(name);
writer.visit(version.raw(), access, name, signature, superName, interfaces);
appView.getSyntheticItems().writeAttributeIfIntermediateSyntheticClass(writer, clazz, appView);
- writeAnnotations(writer::visitAnnotation, clazz.annotations().annotations);
+ writeAnnotations(
+ writer::visitAnnotation, writer::visitTypeAnnotation, clazz.annotations().annotations);
ImmutableMap<DexString, DexValue> defaults = getAnnotationDefaults(clazz.annotations());
if (clazz.getEnclosingMethodAttribute() != null) {
@@ -454,7 +457,8 @@
String signature = field.getGenericSignature().toRenamedString(getNamingLens(), isTypeMissing);
Object value = getStaticValue(field);
FieldVisitor visitor = writer.visitField(access, name, desc, signature, value);
- writeAnnotations(visitor::visitAnnotation, field.annotations().annotations);
+ writeAnnotations(
+ visitor::visitAnnotation, visitor::visitTypeAnnotation, field.annotations().annotations);
visitor.visitEnd();
}
@@ -488,7 +492,10 @@
}
}
writeMethodParametersAnnotation(visitor, definition.annotations().annotations);
- writeAnnotations(visitor::visitAnnotation, definition.annotations().annotations);
+ writeAnnotations(
+ visitor::visitAnnotation,
+ visitor::visitTypeAnnotation,
+ definition.annotations().annotations);
writeParameterAnnotations(visitor, definition.parameterAnnotationsList);
if (!definition.shouldNotHaveCode()) {
writeCode(method, classFileVersion, namingLens, rewriter, visitor);
@@ -525,11 +532,13 @@
parameterAnnotations.getAnnotableParameterCount(), true);
visitor.visitAnnotableParameterCount(
parameterAnnotations.getAnnotableParameterCount(), false);
-
for (int i = 0; i < parameterAnnotations.size(); i++) {
int iFinal = i;
writeAnnotations(
(d, vis) -> visitor.visitParameterAnnotation(iFinal, d, vis),
+ (typeRef, typePath, desc, visible) -> {
+ throw new Unreachable("Type annotations are not placed on parameters");
+ },
parameterAnnotations.get(i).annotations);
}
}
@@ -538,7 +547,14 @@
AnnotationVisitor visit(String desc, boolean visible);
}
- private void writeAnnotations(AnnotationConsumer visitor, DexAnnotation[] annotations) {
+ private interface TypeAnnotationConsumer {
+ AnnotationVisitor visit(int typeRef, TypePath typePath, String desc, boolean visible);
+ }
+
+ private void writeAnnotations(
+ AnnotationConsumer visitor,
+ TypeAnnotationConsumer typeAnnotationVisitor,
+ DexAnnotation[] annotations) {
for (DexAnnotation dexAnnotation : annotations) {
if (dexAnnotation.visibility == DexAnnotation.VISIBILITY_SYSTEM) {
// Annotations with VISIBILITY_SYSTEM are not annotations in CF, but are special
@@ -546,10 +562,14 @@
// signature, throws.
continue;
}
+ String desc = getNamingLens().lookupDescriptor(dexAnnotation.annotation.type).toString();
+ boolean visible = dexAnnotation.visibility == DexAnnotation.VISIBILITY_RUNTIME;
+ DexTypeAnnotation dexTypeAnnotation = dexAnnotation.asTypeAnnotation();
AnnotationVisitor v =
- visitor.visit(
- getNamingLens().lookupDescriptor(dexAnnotation.annotation.type).toString(),
- dexAnnotation.visibility == DexAnnotation.VISIBILITY_RUNTIME);
+ dexTypeAnnotation == null
+ ? visitor.visit(desc, visible)
+ : typeAnnotationVisitor.visit(
+ dexTypeAnnotation.getTypeRef(), dexTypeAnnotation.getTypePath(), desc, visible);
if (v != null) {
writeAnnotation(v, dexAnnotation.annotation);
v.visitEnd();
diff --git a/src/test/java/com/android/tools/r8/annotations/TypeUseAnnotationWithGenericsTest.java b/src/test/java/com/android/tools/r8/annotations/TypeUseAnnotationWithGenericsTest.java
index b5dbbe9..307e052 100644
--- a/src/test/java/com/android/tools/r8/annotations/TypeUseAnnotationWithGenericsTest.java
+++ b/src/test/java/com/android/tools/r8/annotations/TypeUseAnnotationWithGenericsTest.java
@@ -4,18 +4,31 @@
package com.android.tools.r8.annotations;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.annotations.testclasses.MainWithTypeAndGeneric;
import com.android.tools.r8.annotations.testclasses.NotNullTestClass;
import com.android.tools.r8.annotations.testclasses.NotNullTestRuntime;
import com.android.tools.r8.annotations.testclasses.SuperInterface;
import com.android.tools.r8.annotations.testclasses.TestClassWithTypeAndGenericAnnotations;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FieldSubject;
+import com.android.tools.r8.utils.codeinspector.FoundAnnotationSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.lang.reflect.AnnotatedType;
+import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -25,34 +38,6 @@
@RunWith(Parameterized.class)
public class TypeUseAnnotationWithGenericsTest extends TestBase {
- private final String EXPECTED_JVM =
- StringUtils.joinLines(
- "printAnnotation - Class: " + typeName(NotNullTestRuntime.class),
- "printAnnotation - Class: NULL",
- "printAnnotation - Extends(0): " + typeName(NotNullTestRuntime.class),
- "printAnnotation - Implements(0): " + typeName(NotNullTestRuntime.class),
- "printAnnotation - Field: NULL",
- "printAnnotation - Field: NULL",
- "printAnnotation - Field(0): " + typeName(NotNullTestRuntime.class),
- "printAnnotation - Method: NULL",
- "printAnnotation - Method: NULL",
- "printAnnotation - MethodReturnType(0): " + typeName(NotNullTestRuntime.class),
- "printAnnotation - MethodParameter at 0(0): " + typeName(NotNullTestRuntime.class),
- "printAnnotation - MethodParameter at 1(0): " + typeName(NotNullTestRuntime.class),
- "printAnnotation - MethodException at 0(0): " + typeName(NotNullTestRuntime.class),
- "printAnnotation - MethodException at 1(0): " + typeName(NotNullTestRuntime.class),
- "Hello World!");
-
- private final String EXPECTED_R8 =
- StringUtils.joinLines(
- "printAnnotation - Class: " + typeName(NotNullTestRuntime.class),
- "printAnnotation - Class: NULL",
- "printAnnotation - Field: NULL",
- "printAnnotation - Field: NULL",
- "printAnnotation - Method: NULL",
- "printAnnotation - Method: NULL",
- "Hello World!");
-
@Parameter() public TestParameters parameters;
@Parameters(name = "{0}")
@@ -71,7 +56,26 @@
TestClassWithTypeAndGenericAnnotations.class,
SuperInterface.class)
.run(parameters.getRuntime(), MainWithTypeAndGeneric.class)
- .assertSuccessWithOutputLines(EXPECTED_JVM);
+ .assertSuccessWithOutputLines(getExpected(typeName(NotNullTestRuntime.class)));
+ }
+
+ private String getExpected(String notNullTestRuntimeTypeName) {
+ return StringUtils.joinLines(
+ "printAnnotation - Class: " + notNullTestRuntimeTypeName,
+ "printAnnotation - Class: NULL",
+ "printAnnotation - Extends(0): " + notNullTestRuntimeTypeName,
+ "printAnnotation - Implements(0): " + notNullTestRuntimeTypeName,
+ "printAnnotation - Field: NULL",
+ "printAnnotation - Field: NULL",
+ "printAnnotation - Field(0): " + notNullTestRuntimeTypeName,
+ "printAnnotation - Method: NULL",
+ "printAnnotation - Method: NULL",
+ "printAnnotation - MethodReturnType(0): " + notNullTestRuntimeTypeName,
+ "printAnnotation - MethodParameter at 0(0): " + notNullTestRuntimeTypeName,
+ "printAnnotation - MethodParameter at 1(0): " + notNullTestRuntimeTypeName,
+ "printAnnotation - MethodException at 0(0): " + notNullTestRuntimeTypeName,
+ "printAnnotation - MethodException at 1(0): " + notNullTestRuntimeTypeName,
+ "Hello World!");
}
@Test
@@ -91,6 +95,22 @@
@Test
public void testR8() throws Exception {
+ setupR8Test(
+ builder ->
+ builder.addKeepClassRules(
+ NotNullTestClass.class, NotNullTestRuntime.class, SuperInterface.class));
+ }
+
+ @Test
+ public void testR8WithRenaming() throws Exception {
+ setupR8Test(
+ builder ->
+ builder.addKeepClassRulesWithAllowObfuscation(
+ NotNullTestClass.class, NotNullTestRuntime.class, SuperInterface.class));
+ }
+
+ private void setupR8Test(ThrowableConsumer<R8FullTestBuilder> modifier) throws Exception {
+ Box<String> finalNotNullTestRuntimeName = new Box<>();
testForR8(parameters.getBackend())
.addProgramClasses(
MainWithTypeAndGeneric.class,
@@ -99,22 +119,121 @@
TestClassWithTypeAndGenericAnnotations.class,
SuperInterface.class)
.setMinApi(parameters)
- .addKeepClassRules(NotNullTestClass.class, NotNullTestRuntime.class, SuperInterface.class)
+ .apply(modifier)
.addKeepRuntimeVisibleAnnotations()
.addKeepRuntimeInvisibleAnnotations()
.addKeepRuntimeVisibleTypeAnnotations()
.addKeepRuntimeInvisibleTypeAnnotations()
.addKeepAttributeSignature()
+ .addKeepAttributeExceptions()
.addKeepMainRule(MainWithTypeAndGeneric.class)
.addKeepClassAndMembersRules(TestClassWithTypeAndGenericAnnotations.class)
.applyIf(parameters.isDexRuntime(), b -> b.addDontWarn(AnnotatedType.class))
.compile()
- .inspect(
+ .inspectWithOptions(
inspector -> {
- // TODO(b/271543766): Add inspection of annotated types, even runtime invisible.
- })
+ ClassSubject notNullTestClass = inspector.clazz(NotNullTestClass.class);
+ assertThat(notNullTestClass, isPresent());
+ ClassSubject notNullTestRuntime = inspector.clazz(NotNullTestRuntime.class);
+ assertThat(notNullTestRuntime, isPresent());
+ finalNotNullTestRuntimeName.set(notNullTestRuntime.getFinalName());
+ ClassSubject clazz = inspector.clazz(TestClassWithTypeAndGenericAnnotations.class);
+ assertThat(clazz, isPresent());
+ if (parameters.isDexRuntime()) {
+ inspectAnnotations(
+ clazz.annotations(),
+ notNullTestRuntime.getFinalReference(),
+ notNullTestClass.getFinalReference(),
+ 2,
+ 0,
+ 1,
+ 1);
+ } else {
+ inspectAnnotations(
+ clazz.annotations(),
+ notNullTestRuntime.getFinalReference(),
+ notNullTestClass.getFinalReference(),
+ 10,
+ 8,
+ 5,
+ 5);
+ }
+ FieldSubject field = clazz.uniqueFieldWithOriginalName("field");
+ assertThat(field, isPresent());
+ if (parameters.isDexRuntime()) {
+ inspectAnnotations(
+ field.annotations(),
+ notNullTestRuntime.getFinalReference(),
+ notNullTestClass.getFinalReference(),
+ 0,
+ 0,
+ 0,
+ 0);
+ } else {
+ inspectAnnotations(
+ field.annotations(),
+ notNullTestRuntime.getFinalReference(),
+ notNullTestClass.getFinalReference(),
+ 4,
+ 4,
+ 2,
+ 2);
+ }
+ MethodSubject method = clazz.uniqueMethodWithOriginalName("method");
+ assertThat(method, isPresent());
+ // We create a dex annotation for the checked exception.
+ if (parameters.isDexRuntime()) {
+ inspectAnnotations(
+ method.annotations(),
+ notNullTestRuntime.getFinalReference(),
+ notNullTestClass.getFinalReference(),
+ 1,
+ 0,
+ 0,
+ 0);
+ } else {
+ inspectAnnotations(
+ method.annotations(),
+ notNullTestRuntime.getFinalReference(),
+ notNullTestClass.getFinalReference(),
+ 17,
+ 16,
+ 8,
+ 8);
+ }
+ },
+ options -> options.programConsumer = ClassFileConsumer.emptyConsumer())
.run(parameters.getRuntime(), MainWithTypeAndGeneric.class)
.assertFailureWithErrorThatThrowsIf(parameters.isDexRuntime(), NoSuchMethodError.class)
- .assertSuccessWithOutputLinesIf(parameters.isCfRuntime(), EXPECTED_R8);
+ .assertSuccessWithOutputLinesIf(
+ parameters.isCfRuntime(), getExpected(finalNotNullTestRuntimeName.get()));
+ }
+
+ private void inspectAnnotations(
+ List<FoundAnnotationSubject> annotations,
+ ClassReference notNullRuntime,
+ ClassReference notNullClass,
+ int expectedCount,
+ int expectedTypeAnnotationCount,
+ int expectedNotNullTestRuntimeCount,
+ int expectedNotNullTestClassCount) {
+ assertEquals(expectedCount, annotations.size());
+ assertEquals(
+ expectedTypeAnnotationCount,
+ annotations.stream().filter(FoundAnnotationSubject::isTypeAnnotation).count());
+ assertEquals(
+ expectedNotNullTestRuntimeCount,
+ annotations.stream()
+ .filter(
+ annotation ->
+ annotation.getAnnotation().type.asClassReference().equals(notNullRuntime))
+ .count());
+ assertEquals(
+ expectedNotNullTestClassCount,
+ annotations.stream()
+ .filter(
+ annotation ->
+ annotation.getAnnotation().type.asClassReference().equals(notNullClass))
+ .count());
}
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentAnnotationSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentAnnotationSubject.java
index 610b054..2ad1eb6 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentAnnotationSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentAnnotationSubject.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexEncodedAnnotation;
+import com.android.tools.r8.graph.DexTypeAnnotation;
public class AbsentAnnotationSubject extends AnnotationSubject {
@@ -28,4 +29,19 @@
public DexEncodedAnnotation getAnnotation() {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public int isVisible() {
+ throw new Unreachable("Subject is absent");
+ }
+
+ @Override
+ public boolean isTypeAnnotation() {
+ throw new Unreachable("Subject is absent");
+ }
+
+ @Override
+ public DexTypeAnnotation asDexTypeAnnotation() {
+ throw new Unreachable("Subject is absent");
+ }
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AnnotationSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AnnotationSubject.java
index 4bffcca..ddd737b 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AnnotationSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AnnotationSubject.java
@@ -5,8 +5,15 @@
package com.android.tools.r8.utils.codeinspector;
import com.android.tools.r8.graph.DexEncodedAnnotation;
+import com.android.tools.r8.graph.DexTypeAnnotation;
public abstract class AnnotationSubject extends Subject {
public abstract DexEncodedAnnotation getAnnotation();
+
+ public abstract int isVisible();
+
+ public abstract boolean isTypeAnnotation();
+
+ public abstract DexTypeAnnotation asDexTypeAnnotation();
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundAnnotationSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundAnnotationSubject.java
index e73c25a..4d67e5d 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundAnnotationSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundAnnotationSubject.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexEncodedAnnotation;
+import com.android.tools.r8.graph.DexTypeAnnotation;
import com.android.tools.r8.utils.ListUtils;
import java.util.List;
@@ -51,4 +52,19 @@
public DexEncodedAnnotation getAnnotation() {
return annotation.annotation;
}
+
+ @Override
+ public int isVisible() {
+ return annotation.getVisibility();
+ }
+
+ @Override
+ public boolean isTypeAnnotation() {
+ return annotation.isTypeAnnotation();
+ }
+
+ @Override
+ public DexTypeAnnotation asDexTypeAnnotation() {
+ return annotation.asTypeAnnotation();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
index d16221e..abc83be 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.utils.codeinspector;
-import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.graph.AccessFlags;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexEncodedField;
@@ -115,7 +114,7 @@
@Override
public List<FoundAnnotationSubject> annotations() {
- throw new Unimplemented();
+ return FoundAnnotationSubject.listFromDex(dexField.annotations(), codeInspector);
}
@Override
@@ -133,9 +132,7 @@
@Override
public String getJvmFieldSignatureAsString() {
- return dexField.getReference().name.toString()
- + ":"
- + dexField.getReference().type.toDescriptorString();
+ return dexField.getReference().name + ":" + dexField.getReference().type.toDescriptorString();
}
@Override