[ApiModel] Generate covariant library methods in database
Bug: b/232891189
Change-Id: I32ef515e16467c5327840a05a777618aabf98d09
diff --git a/src/test/java/com/android/tools/r8/androidapi/GenerateCovariantReturnTypeMethodsTest.java b/src/test/java/com/android/tools/r8/androidapi/GenerateCovariantReturnTypeMethodsTest.java
index db640a2..cf867dd 100644
--- a/src/test/java/com/android/tools/r8/androidapi/GenerateCovariantReturnTypeMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/androidapi/GenerateCovariantReturnTypeMethodsTest.java
@@ -21,13 +21,16 @@
import com.android.tools.r8.apimodel.JavaSourceCodePrinter.ParameterizedType;
import com.android.tools.r8.cfmethodgeneration.MethodGenerationBase;
import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotationElement;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexValue.DexValueType;
+import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.utils.Action;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.ClassReferenceUtils;
import com.android.tools.r8.utils.EntryUtils;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.MethodReferenceUtils;
@@ -40,10 +43,12 @@
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -81,7 +86,9 @@
public void testCanFindAnnotatedMethodsInJar() throws Exception {
CovariantMethodsInJarResult covariantMethodsInJar = CovariantMethodsInJarResult.create();
// These assertions are here to ensure we produce a sane result.
- assertEquals(51, covariantMethodsInJar.methodReferenceMap.size());
+ assertEquals(9, covariantMethodsInJar.methodReferenceMap.keySet().size());
+ assertEquals(
+ 51, covariantMethodsInJar.methodReferenceMap.values().stream().mapToLong(List::size).sum());
}
@Test
@@ -91,11 +98,9 @@
public static String generateCode() throws Exception {
CovariantMethodsInJarResult covariantMethodsInJar = CovariantMethodsInJarResult.create();
- Map<MethodReference, List<MethodReference>> methodReferenceMap =
- covariantMethodsInJar.methodReferenceMap;
- List<Entry<MethodReference, List<MethodReference>>> entries =
- new ArrayList<>(methodReferenceMap.entrySet());
- entries.sort(Entry.comparingByKey(MethodReferenceUtils.getMethodReferenceComparator()));
+ List<Entry<ClassReference, List<MethodReferenceWithApiLevel>>> entries =
+ new ArrayList<>(covariantMethodsInJar.methodReferenceMap.entrySet());
+ entries.sort(Entry.comparingByKey(ClassReferenceUtils.getClassReferenceComparator()));
JavaSourceCodePrinter printer =
JavaSourceCodePrinter.builder()
.setHeader(
@@ -119,10 +124,16 @@
methodPrinter ->
entries.forEach(
EntryUtils.accept(
- (ignored, covariations) ->
- covariations.forEach(
- covariant ->
- registerCovariantMethod(methodPrinter, covariant)))))
+ (ignored, covariations) -> {
+ covariations.sort(
+ Comparator.comparing(
+ MethodReferenceWithApiLevel::getMethodReference,
+ MethodReferenceUtils.getMethodReferenceComparator()));
+ covariations.forEach(
+ covariant ->
+ registerCovariantMethod(
+ methodPrinter, covariant.methodReference));
+ })))
.toString();
Path tempFile = Files.createTempFile("output-", ".java");
Files.write(tempFile, javaSourceCode.getBytes(StandardCharsets.UTF_8));
@@ -175,15 +186,15 @@
}
public static class CovariantMethodsInJarResult {
- private final Map<MethodReference, List<MethodReference>> methodReferenceMap;
+ private final Map<ClassReference, List<MethodReferenceWithApiLevel>> methodReferenceMap;
private CovariantMethodsInJarResult(
- Map<MethodReference, List<MethodReference>> methodReferenceMap) {
+ Map<ClassReference, List<MethodReferenceWithApiLevel>> methodReferenceMap) {
this.methodReferenceMap = methodReferenceMap;
}
public static CovariantMethodsInJarResult create() throws Exception {
- Map<MethodReference, List<MethodReference>> methodReferenceMap = new HashMap<>();
+ Map<ClassReference, List<MethodReferenceWithApiLevel>> methodReferenceMap = new HashMap<>();
CodeInspector inspector = new CodeInspector(PATH_TO_CORE_JAR);
DexItemFactory factory = inspector.getFactory();
for (FoundClassSubject clazz : inspector.allClasses()) {
@@ -196,6 +207,7 @@
isCovariantReturnTypeAnnotation(annotation.annotation, factory));
if (!covariantAnnotations.isEmpty()) {
MethodReference methodReference = method.asMethodReference();
+ ClassReference holder = clazz.getOriginalReference();
for (DexAnnotation covariantAnnotation : covariantAnnotations) {
if (covariantAnnotation.annotation.type
== factory.annotationCovariantReturnType) {
@@ -214,17 +226,55 @@
private static void createCovariantMethodReference(
MethodReference methodReference,
DexAnnotation covariantAnnotation,
- Map<MethodReference, List<MethodReference>> methodReferenceMap) {
+ Map<ClassReference, List<MethodReferenceWithApiLevel>> methodReferenceMap) {
DexValueType newReturnType =
covariantAnnotation.annotation.getElement(0).getValue().asDexValueType();
+ DexAnnotationElement element = covariantAnnotation.annotation.getElement(1);
+ assert element.name.toString().equals("presentAfter");
+ AndroidApiLevel apiLevel =
+ AndroidApiLevel.getAndroidApiLevel(element.getValue().asDexValueInt().value);
methodReferenceMap
- .computeIfAbsent(methodReference, ignoreKey(ArrayList::new))
+ .computeIfAbsent(methodReference.getHolderClass(), ignoreKey(ArrayList::new))
.add(
- Reference.method(
- methodReference.getHolderClass(),
- methodReference.getMethodName(),
- methodReference.getFormalTypes(),
- newReturnType.value.asClassReference()));
+ new MethodReferenceWithApiLevel(
+ Reference.method(
+ methodReference.getHolderClass(),
+ methodReference.getMethodName(),
+ methodReference.getFormalTypes(),
+ newReturnType.value.asClassReference()),
+ apiLevel));
+ }
+
+ public void visitCovariantMethodsForHolder(
+ ClassReference reference, Consumer<MethodReferenceWithApiLevel> consumer) {
+ List<MethodReferenceWithApiLevel> methodReferences = methodReferenceMap.get(reference);
+ if (methodReferences != null) {
+ methodReferences.stream()
+ .sorted(
+ Comparator.comparing(
+ MethodReferenceWithApiLevel::getMethodReference,
+ MethodReferenceUtils.getMethodReferenceComparator()))
+ .forEach(consumer);
+ }
+ }
+ }
+
+ public static class MethodReferenceWithApiLevel {
+
+ private final MethodReference methodReference;
+ private final AndroidApiLevel apiLevel;
+
+ private MethodReferenceWithApiLevel(MethodReference methodReference, AndroidApiLevel apiLevel) {
+ this.methodReference = methodReference;
+ this.apiLevel = apiLevel;
+ }
+
+ public MethodReference getMethodReference() {
+ return methodReference;
+ }
+
+ public AndroidApiLevel getApiLevel() {
+ return apiLevel;
}
}
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java
index 708858e..423850e 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.androidapi.AndroidApiDataAccess;
+import com.android.tools.r8.androidapi.GenerateCovariantReturnTypeMethodsTest.CovariantMethodsInJarResult;
import com.android.tools.r8.apimodel.AndroidApiVersionsXmlParser.ParsedApiClass;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -81,6 +82,8 @@
computeAppViewWithClassHierarchy(AndroidApp.builder().addLibraryFile(androidJar).build());
DexItemFactory factory = appView.dexItemFactory();
+ CovariantMethodsInJarResult covariantMethodsInJar = CovariantMethodsInJarResult.create();
+
for (ParsedApiClass apiClass : apiClasses) {
Map<DexMethod, AndroidApiLevel> methodsForApiClass = new HashMap<>();
apiClass.visitMethodReferences(
@@ -88,6 +91,18 @@
methods.forEach(
method -> methodsForApiClass.put(factory.createMethod(method), apiLevel));
});
+ covariantMethodsInJar.visitCovariantMethodsForHolder(
+ apiClass.getClassReference(),
+ methodReferenceWithApiLevel -> {
+ DexMethod method =
+ factory.createMethod(methodReferenceWithApiLevel.getMethodReference());
+ if (!methodsForApiClass.containsKey(method)) {
+ apiClass.amendCovariantMethod(
+ methodReferenceWithApiLevel.getMethodReference(),
+ methodReferenceWithApiLevel.getApiLevel());
+ methodsForApiClass.put(method, methodReferenceWithApiLevel.getApiLevel());
+ }
+ });
Map<DexField, AndroidApiLevel> fieldsForApiClass = new HashMap<>();
apiClass.visitFieldReferences(
(apiLevel, fields) -> {
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
index 53f5fda..7de78a2 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
@@ -6,6 +6,7 @@
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestDiagnosticMessagesImpl;
@@ -110,7 +111,7 @@
@Test
public void testDatabaseGenerationUpToDate() throws Exception {
GenerateDatabaseResourceFilesResult result = generateResourcesFiles();
- TestBase.filesAreEqual(result.apiLevels, API_DATABASE);
+ assertTrue(TestBase.filesAreEqual(result.apiLevels, API_DATABASE));
}
@Test
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java
index 4a3cfa2..8e8f4ee 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java
@@ -247,6 +247,10 @@
interfaces.forEach(consumer);
}
+ public void amendCovariantMethod(MethodReference methodReference, AndroidApiLevel apiLevel) {
+ register(methodReference, apiLevel);
+ }
+
public boolean isInterface() {
return isInterface;
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelCovariantReturnTypeTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelCovariantReturnTypeTest.java
index ddba615..524147f 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelCovariantReturnTypeTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelCovariantReturnTypeTest.java
@@ -5,12 +5,13 @@
package com.android.tools.r8.apimodel;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.addTracedApiReferenceLevelCallBack;
-import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.AndroidApiLevel;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentHashMap.KeySetView;
@@ -45,8 +46,11 @@
addTracedApiReferenceLevelCallBack(
(method, apiLevel) -> {
if (Reference.methodFromMethod(main).equals(method)) {
- // TODO(b/232891189): Should be api level 28.
- assertNull(apiLevel);
+ assertEquals(
+ parameters.isCfRuntime()
+ ? AndroidApiLevel.P
+ : parameters.getApiLevel().max(AndroidApiLevel.P),
+ apiLevel);
}
}))
.compile();
diff --git a/third_party/api_database/api_database.tar.gz.sha1 b/third_party/api_database/api_database.tar.gz.sha1
index ee68444..3a106aa 100644
--- a/third_party/api_database/api_database.tar.gz.sha1
+++ b/third_party/api_database/api_database.tar.gz.sha1
@@ -1 +1 @@
-72bdd118be45bbd99762b5dfea457c90052e0f22
\ No newline at end of file
+da5a2b797563b19461f62db48bf1e45e2f86752f
\ No newline at end of file