Reorder parameter annotations during proto normalization
Bug: 195112263
Change-Id: I7c8d075a3694f6ecb572300f9a9ce0035e2f1f5e
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index c326dc0..c33d18d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -44,6 +44,7 @@
import com.android.tools.r8.graph.DexAnnotation.AnnotatedKind;
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.ir.code.ValueType;
@@ -85,7 +86,6 @@
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
-import java.util.function.IntPredicate;
import org.objectweb.asm.Opcodes;
public class DexEncodedMethod extends DexEncodedMember<DexEncodedMethod, DexMethod>
@@ -1470,21 +1470,31 @@
return this;
}
- public Builder removeParameterAnnotations(IntPredicate predicate) {
+ public Builder rewriteParameterAnnotations(
+ DexEncodedMethod method, ArgumentInfoCollection argumentInfoCollection) {
if (parameterAnnotations.isEmpty()) {
// Nothing to do.
return this;
}
+ if (!argumentInfoCollection.hasArgumentPermutation()
+ && !argumentInfoCollection.hasRemovedArguments()) {
+ // Nothing to do.
+ return this;
+ }
- List<DexAnnotationSet> newParameterAnnotations = new ArrayList<>();
+ List<DexAnnotationSet> newParameterAnnotations =
+ new ArrayList<>(parameterAnnotations.countNonMissing());
int newNumberOfMissingParameterAnnotations = 0;
- for (int oldIndex = 0; oldIndex < parameterAnnotations.size(); oldIndex++) {
- if (!predicate.test(oldIndex)) {
- if (parameterAnnotations.isMissing(oldIndex)) {
+ for (int parameterIndex = 0;
+ parameterIndex < method.getParameters().size();
+ parameterIndex++) {
+ int argumentIndex = parameterIndex + method.getFirstNonReceiverArgumentIndex();
+ if (!argumentInfoCollection.isArgumentRemoved(argumentIndex)) {
+ if (parameterAnnotations.isMissing(parameterIndex)) {
newNumberOfMissingParameterAnnotations++;
} else {
- newParameterAnnotations.add(parameterAnnotations.get(oldIndex));
+ newParameterAnnotations.add(parameterAnnotations.get(parameterIndex));
}
}
}
@@ -1493,6 +1503,23 @@
return setParameterAnnotations(ParameterAnnotationsList.empty());
}
+ if (argumentInfoCollection.hasArgumentPermutation()) {
+ List<DexAnnotationSet> newPermutedParameterAnnotations =
+ Arrays.asList(new DexAnnotationSet[method.getParameters().size()]);
+ for (int parameterIndex = newNumberOfMissingParameterAnnotations;
+ parameterIndex < method.getParameters().size();
+ parameterIndex++) {
+ int argumentIndex = parameterIndex + method.getFirstNonReceiverArgumentIndex();
+ int newArgumentIndex = argumentInfoCollection.getNewArgumentIndex(argumentIndex, 0);
+ int newParameterIndex = newArgumentIndex - method.getFirstNonReceiverArgumentIndex();
+ newPermutedParameterAnnotations.set(
+ newParameterIndex,
+ newParameterAnnotations.get(parameterIndex - newNumberOfMissingParameterAnnotations));
+ }
+ newParameterAnnotations = newPermutedParameterAnnotations;
+ newNumberOfMissingParameterAnnotations = 0;
+ }
+
return setParameterAnnotations(
ParameterAnnotationsList.create(
newParameterAnnotations.toArray(DexAnnotationSet.EMPTY_ARRAY),
diff --git a/src/main/java/com/android/tools/r8/graph/proto/ArgumentInfoCollection.java b/src/main/java/com/android/tools/r8/graph/proto/ArgumentInfoCollection.java
index 0a9fcbf..cbfa83d 100644
--- a/src/main/java/com/android/tools/r8/graph/proto/ArgumentInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/proto/ArgumentInfoCollection.java
@@ -11,7 +11,6 @@
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfoFixer;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.ConsumerUtils;
import com.android.tools.r8.utils.IntObjConsumer;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap.Entry;
import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap;
@@ -344,13 +343,6 @@
*/
public Consumer<DexEncodedMethod.Builder> createParameterAnnotationsRemover(
DexEncodedMethod method) {
- if (numberOfRemovedArguments() > 0 && !method.parameterAnnotationsList.isEmpty()) {
- return builder -> {
- int firstArgumentIndex = method.getFirstNonReceiverArgumentIndex();
- builder.removeParameterAnnotations(
- oldIndex -> getArgumentInfo(oldIndex + firstArgumentIndex).isRemovedArgumentInfo());
- };
- }
- return ConsumerUtils.emptyConsumer();
+ return builder -> builder.rewriteParameterAnnotations(method, this);
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java b/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java
index e28c76b..b1237ed 100644
--- a/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java
@@ -14,7 +14,9 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.IterableUtils;
@@ -81,9 +83,22 @@
return method;
}
DexMethod newMethodReference = newMethodSignature.withHolder(clazz, dexItemFactory);
- lensBuilder.recordNewMethodSignature(method, newMethodReference);
- // TODO(b/195112263): Fixup any optimization info and parameter annotations.
- return method.toTypeSubstitutedMethod(newMethodReference);
+ RewrittenPrototypeDescription prototypeChanges =
+ lensBuilder.recordNewMethodSignature(method, newMethodReference);
+ // TODO(b/195112263): Assert that the method does not have any optimization info.
+ // If/when enabling proto normalization after the final round of tree shaking, this
+ // should simply clear the optimization info, or replace it by a
+ // ThrowingMethodOptimizationInfo since we should never use the optimization info
+ // after this point.
+ return method.toTypeSubstitutedMethod(
+ newMethodReference,
+ builder -> {
+ if (!prototypeChanges.isEmpty()) {
+ builder
+ .apply(prototypeChanges.createParameterAnnotationsRemover(method))
+ .setGenericSignature(MethodTypeSignature.noSignature());
+ }
+ });
});
}
diff --git a/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizerGraphLens.java b/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizerGraphLens.java
index dc91a10..bfeef49 100644
--- a/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizerGraphLens.java
@@ -62,20 +62,26 @@
@Override
public DexMethod getRenamedMethodSignature(DexMethod originalMethod, GraphLens applied) {
+ if (this == applied) {
+ return originalMethod;
+ }
return newMethodSignatures.getOrDefault(originalMethod, originalMethod);
}
@Override
public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(
DexMethod method, GraphLens codeLens) {
+ if (this == codeLens) {
+ return RewrittenPrototypeDescription.none();
+ }
DexMethod previousMethodSignature = getPreviousMethodSignature(method);
RewrittenPrototypeDescription previousPrototypeChanges =
getPrevious().lookupPrototypeChangesForMethodDefinition(previousMethodSignature);
if (previousMethodSignature == method) {
return previousPrototypeChanges;
}
- assert prototypeChanges.containsKey(method);
- return previousPrototypeChanges.combine(prototypeChanges.get(method));
+ return previousPrototypeChanges.combine(
+ prototypeChanges.getOrDefault(method, RewrittenPrototypeDescription.none()));
}
@Override
@@ -132,11 +138,17 @@
this.appView = appView;
}
- public Builder recordNewMethodSignature(DexEncodedMethod method, DexMethod newMethodSignature) {
+ public RewrittenPrototypeDescription recordNewMethodSignature(
+ DexEncodedMethod method, DexMethod newMethodSignature) {
assert method.getReference() != newMethodSignature;
- prototypeChanges.put(newMethodSignature, computePrototypeChanges(method, newMethodSignature));
newMethodSignatures.put(method.getReference(), newMethodSignature);
- return this;
+ if (!method.getParameters().equals(newMethodSignature.getParameters())) {
+ RewrittenPrototypeDescription prototypeChangesForMethod =
+ computePrototypeChanges(method, newMethodSignature);
+ prototypeChanges.put(newMethodSignature, prototypeChangesForMethod);
+ return prototypeChangesForMethod;
+ }
+ return RewrittenPrototypeDescription.none();
}
// TODO(b/195112263): Canonicalize the permutation maps.
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithParameterAnnotationsTest.java b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithParameterAnnotationsTest.java
new file mode 100644
index 0000000..a9b9af9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithParameterAnnotationsTest.java
@@ -0,0 +1,153 @@
+// 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.optimize.proto;
+
+import static com.android.tools.r8.utils.codeinspector.AnnotationMatchers.hasParameterAnnotationTypes;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.MethodMatchers.hasParameters;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.android.tools.r8.utils.codeinspector.TypeSubject;
+import com.google.common.collect.ImmutableList;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ProtoNormalizationWithParameterAnnotationsTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8Compat(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addKeepClassAndMembersRules(Foo.class, Bar.class)
+ .addKeepRuntimeVisibleAnnotations()
+ .addOptionsModification(
+ options -> options.testing.enableExperimentalProtoNormalization = true)
+ .enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ // TODO(b/173398086): uniqueMethodWithName() does not work with proto changes.
+ .noMinification()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject mainClassSubject = inspector.clazz(Main.class);
+ assertThat(mainClassSubject, isPresent());
+
+ TypeSubject aTypeSubject = inspector.clazz(A.class).asTypeSubject();
+ TypeSubject bTypeSubject = inspector.clazz(B.class).asTypeSubject();
+ TypeSubject fooTypeSubject = inspector.clazz(Foo.class).asTypeSubject();
+ TypeSubject barTypeSubject = inspector.clazz(Bar.class).asTypeSubject();
+
+ // Main.bar() has parameter annotations [@Bar, @Foo].
+ MethodSubject barMethodSubject = mainClassSubject.uniqueMethodWithName("bar");
+ assertThat(barMethodSubject, isPresent());
+ assertThat(barMethodSubject, hasParameters(aTypeSubject, bTypeSubject));
+ assertThat(
+ barMethodSubject,
+ hasParameterAnnotationTypes(
+ ImmutableList.of(barTypeSubject), ImmutableList.of(fooTypeSubject)));
+
+ // Main.baz() has parameter annotations [, @Foo].
+ MethodSubject bazMethodSubject = mainClassSubject.uniqueMethodWithName("baz");
+ assertThat(bazMethodSubject, isPresent());
+ assertThat(bazMethodSubject, hasParameters(aTypeSubject, bTypeSubject));
+ assertThat(
+ bazMethodSubject,
+ hasParameterAnnotationTypes(
+ ImmutableList.of(), ImmutableList.of(fooTypeSubject)));
+
+ // Main.qux() has parameter annotations [@Foo, ].
+ MethodSubject quxMethodSubject = mainClassSubject.uniqueMethodWithName("qux");
+ assertThat(quxMethodSubject, isPresent());
+ assertThat(quxMethodSubject, hasParameters(aTypeSubject, bTypeSubject));
+ assertThat(
+ quxMethodSubject,
+ hasParameterAnnotationTypes(
+ ImmutableList.of(fooTypeSubject), ImmutableList.of()));
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A", "B", "A", "B", "A", "B", "A", "B");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ foo(new A(), new B());
+ bar(new B(), new A());
+ baz(new B(), new A());
+ qux(new B(), new A());
+ }
+
+ @NeverInline
+ static void foo(A a, B b) {
+ System.out.println(a);
+ System.out.println(b);
+ }
+
+ @NeverInline
+ static void bar(@Foo B b, @Bar A a) {
+ System.out.println(a);
+ System.out.println(b);
+ }
+
+ @NeverInline
+ static void baz(@Foo B b, A a) {
+ System.out.println(a);
+ System.out.println(b);
+ }
+
+ @NeverInline
+ static void qux(B b, @Foo A a) {
+ System.out.println(a);
+ System.out.println(b);
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class A {
+
+ @Override
+ public String toString() {
+ return "A";
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class B {
+
+ @Override
+ public String toString() {
+ return "B";
+ }
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface Foo {}
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface Bar {}
+}
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithoutSharingTest.java b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithoutSharingTest.java
index 0088b3b..d9190df 100644
--- a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithoutSharingTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithoutSharingTest.java
@@ -41,8 +41,6 @@
options -> options.testing.enableExperimentalProtoNormalization = true)
.enableInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
- // TODO(b/173398086): uniqueMethodWithName() does not work with proto changes.
- .noMinification()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
index 7f35d90..61c6fb5 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
@@ -115,7 +115,7 @@
}
@Override
- public List<AnnotationSubject> annotations() {
+ public List<FoundAnnotationSubject> annotations() {
throw new Unreachable("Cannot determine if an absent class has annotations");
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentFieldSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentFieldSubject.java
index 8d91f3c..6eeab19 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentFieldSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentFieldSubject.java
@@ -60,7 +60,7 @@
}
@Override
- public List<AnnotationSubject> annotations() {
+ public List<FoundAnnotationSubject> annotations() {
throw new Unreachable("Cannot determine if an absent field has annotations");
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
index 9be6468..a4fb7b4 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
@@ -87,6 +87,16 @@
}
@Override
+ public List<List<FoundAnnotationSubject>> getParameterAnnotations() {
+ throw new Unreachable("Cannot get the parameter annotations for an absent method");
+ }
+
+ @Override
+ public List<FoundAnnotationSubject> getParameterAnnotations(int index) {
+ throw new Unreachable("Cannot get the parameter annotations for an absent method");
+ }
+
+ @Override
public ProgramMethod getProgramMethod() {
return null;
}
@@ -127,7 +137,7 @@
}
@Override
- public List<AnnotationSubject> annotations() {
+ public List<FoundAnnotationSubject> annotations() {
throw new Unreachable("Cannot determine if an absent method has annotations");
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AnnotationMatchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AnnotationMatchers.java
new file mode 100644
index 0000000..8965f41
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AnnotationMatchers.java
@@ -0,0 +1,103 @@
+// 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.utils.codeinspector;
+
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
+import java.util.List;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+
+public class AnnotationMatchers {
+
+ public static Matcher<MethodSubject> hasParameterAnnotationTypes(
+ List<TypeSubject>... typeSubjects) {
+ return hasParameterAnnotationTypes(Arrays.asList(typeSubjects));
+ }
+
+ public static Matcher<MethodSubject> hasParameterAnnotationTypes(
+ List<List<TypeSubject>> typeSubjects) {
+ return new TypeSafeMatcher<MethodSubject>() {
+
+ @Override
+ protected boolean matchesSafely(MethodSubject subject) {
+ List<List<FoundAnnotationSubject>> parameterAnnotations = subject.getParameterAnnotations();
+ if (parameterAnnotations.size() != typeSubjects.size()) {
+ return false;
+ }
+ for (int parameterIndex = 0;
+ parameterIndex < parameterAnnotations.size();
+ parameterIndex++) {
+ List<FoundAnnotationSubject> parameterAnnotationsForParameter =
+ parameterAnnotations.get(parameterIndex);
+ List<TypeSubject> typeSubjectsForParameter = typeSubjects.get(parameterIndex);
+ if (!hasAnnotationTypes(typeSubjectsForParameter)
+ .matches(parameterAnnotationsForParameter)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText(
+ "has parameter annotation types ["
+ + StringUtils.join(
+ "; ",
+ typeSubjects,
+ typeSubjectsForParameter ->
+ StringUtils.join(", ", typeSubjectsForParameter, TypeSubject::getTypeName))
+ + "]");
+ }
+
+ @Override
+ public void describeMismatchSafely(MethodSubject subject, Description description) {
+ description.appendText("method did not");
+ }
+ };
+ }
+
+ public static Matcher<List<FoundAnnotationSubject>> hasAnnotationTypes(
+ TypeSubject... typeSubjects) {
+ return hasAnnotationTypes(Arrays.asList(typeSubjects));
+ }
+
+ public static Matcher<List<FoundAnnotationSubject>> hasAnnotationTypes(
+ List<TypeSubject> typeSubjects) {
+ return new TypeSafeMatcher<List<FoundAnnotationSubject>>() {
+
+ @Override
+ protected boolean matchesSafely(List<FoundAnnotationSubject> subjects) {
+ if (subjects.size() != typeSubjects.size()) {
+ return false;
+ }
+ for (int i = 0; i < subjects.size(); i++) {
+ FoundAnnotationSubject subject = subjects.get(i);
+ TypeSubject typeSubject = typeSubjects.get(i);
+ if (!subject.getType().equals(typeSubject)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText(
+ "has annotation types ["
+ + StringUtils.join(", ", typeSubjects, TypeSubject::getTypeName)
+ + "]");
+ }
+
+ @Override
+ public void describeMismatchSafely(
+ List<FoundAnnotationSubject> subjects, Description description) {
+ description.appendText("annotations did not");
+ }
+ };
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassOrMemberSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassOrMemberSubject.java
index 754b470..2c38d35 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassOrMemberSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassOrMemberSubject.java
@@ -10,7 +10,7 @@
public abstract class ClassOrMemberSubject extends Subject {
- public abstract List<AnnotationSubject> annotations();
+ public abstract List<FoundAnnotationSubject> annotations();
public abstract AnnotationSubject annotation(String name);
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 d06b289..e73c25a 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
@@ -14,13 +14,22 @@
public class FoundAnnotationSubject extends AnnotationSubject {
private final DexAnnotation annotation;
+ private final CodeInspector codeInspector;
- FoundAnnotationSubject(DexAnnotation annotation) {
+ FoundAnnotationSubject(DexAnnotation annotation, CodeInspector codeInspector) {
this.annotation = annotation;
+ this.codeInspector = codeInspector;
}
- public static List<AnnotationSubject> listFromDex(DexAnnotationSet annotations) {
- return ListUtils.map(annotations.annotations, FoundAnnotationSubject::new);
+ public static List<FoundAnnotationSubject> listFromDex(
+ DexAnnotationSet annotations, CodeInspector codeInspector) {
+ return ListUtils.map(
+ annotations.annotations,
+ annotation -> new FoundAnnotationSubject(annotation, codeInspector));
+ }
+
+ public TypeSubject getType() {
+ return new TypeSubject(codeInspector, annotation.getAnnotationType());
}
@Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
index cdc420e..fbf91d6 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
@@ -359,8 +359,8 @@
}
@Override
- public List<AnnotationSubject> annotations() {
- return FoundAnnotationSubject.listFromDex(dexClass.annotations());
+ public List<FoundAnnotationSubject> annotations() {
+ return FoundAnnotationSubject.listFromDex(dexClass.annotations(), codeInspector);
}
@Override
@@ -372,7 +372,7 @@
DexAnnotation annotation = codeInspector.findAnnotation(name, dexClass.annotations());
return annotation == null
? new AbsentAnnotationSubject()
- : new FoundAnnotationSubject(annotation);
+ : new FoundAnnotationSubject(annotation, codeInspector);
}
@Override
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 2ea0107..c2daded 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
@@ -114,7 +114,7 @@
}
@Override
- public List<AnnotationSubject> annotations() {
+ public List<FoundAnnotationSubject> annotations() {
throw new Unimplemented();
}
@@ -123,7 +123,7 @@
DexAnnotation annotation = codeInspector.findAnnotation(name, dexField.annotations());
return annotation == null
? new AbsentAnnotationSubject()
- : new FoundAnnotationSubject(annotation);
+ : new FoundAnnotationSubject(annotation, codeInspector);
}
@Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
index b99b090..b33f4a3 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -42,6 +42,7 @@
import com.google.common.collect.ImmutableList;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
@@ -136,6 +137,24 @@
}
@Override
+ public List<FoundAnnotationSubject> getParameterAnnotations(int index) {
+ return FoundAnnotationSubject.listFromDex(
+ getMethod().getParameterAnnotation(index), codeInspector);
+ }
+
+ @Override
+ public List<List<FoundAnnotationSubject>> getParameterAnnotations() {
+ List<List<FoundAnnotationSubject>> parameterAnnotations =
+ new ArrayList<>(getMethod().getParameters().size());
+ for (int parameterIndex = 0;
+ parameterIndex < getMethod().getParameters().size();
+ parameterIndex++) {
+ parameterAnnotations.add(getParameterAnnotations(parameterIndex));
+ }
+ return parameterAnnotations;
+ }
+
+ @Override
public ProgramMethod getProgramMethod() {
return new ProgramMethod(clazz.getDexProgramClass(), getMethod());
}
@@ -343,8 +362,8 @@
}
@Override
- public List<AnnotationSubject> annotations() {
- return FoundAnnotationSubject.listFromDex(dexMethod.annotations());
+ public List<FoundAnnotationSubject> annotations() {
+ return FoundAnnotationSubject.listFromDex(dexMethod.annotations(), codeInspector);
}
@Override
@@ -352,7 +371,7 @@
DexAnnotation annotation = codeInspector.findAnnotation(name, dexMethod.annotations());
return annotation == null
? new AbsentAnnotationSubject()
- : new FoundAnnotationSubject(annotation);
+ : new FoundAnnotationSubject(annotation, codeInspector);
}
@Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
index 3b7fe05..dda3e35 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
@@ -56,6 +56,10 @@
public abstract List<TypeSubject> getParameters();
+ public abstract List<List<FoundAnnotationSubject>> getParameterAnnotations();
+
+ public abstract List<FoundAnnotationSubject> getParameterAnnotations(int index);
+
public abstract ProgramMethod getProgramMethod();
public Iterator<InstructionSubject> iterateInstructions() {