Add codegeneration of methods with covariant return types

Contents of README.google:
Name: libcore
URL: https://source.android.com/
Version: Libcore AOSP master 0685278b97dd2592e3419f317a773da6c697272e
Revision: NA
License: The GNU General Public License (GPL)

Description:
This is the core-oj.jar pulled from building aosp master.

Bug: b/234579501
Bug: b/232891189
Change-Id: I63b240bb3d589af73efd14a1b0d1aea754a8870d
diff --git a/.gitignore b/.gitignore
index b19c671..bd447f4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,6 +41,8 @@
 third_party/android_jar/lib-v[0-9][0-9]
 third_party/android_jar/lib-v[0-9][0-9].tar.gz
 third_party/android_jar/lib.tar.gz
+third_party/android_jar/libcore_latest
+third_party/android_jar/libcore_latest.tar.gz
 third_party/android_jar/api-versions.tar.gz
 third_party/android_jar/api-versions
 third_party/android_sdk
diff --git a/build.gradle b/build.gradle
index 71a73a6..aad1cb2 100644
--- a/build.gradle
+++ b/build.gradle
@@ -303,6 +303,7 @@
         ],
         "third_party": [
                 "android_cts_baseline",
+                "android_jar/libcore_latest",
                 "android_jar/lib-v14",
                 "android_jar/lib-v15",
                 "android_jar/lib-v19",
diff --git a/src/main/java/com/android/tools/r8/androidapi/CovariantReturnTypeMethods.java b/src/main/java/com/android/tools/r8/androidapi/CovariantReturnTypeMethods.java
new file mode 100644
index 0000000..077c1be
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/androidapi/CovariantReturnTypeMethods.java
@@ -0,0 +1,289 @@
+// 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.
+
+// ***********************************************************************************
+// GENERATED FILE. DO NOT EDIT! See GenerateCovariantReturnTypeMethodsTest.java.
+// ***********************************************************************************
+
+package com.android.tools.r8.androidapi;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import java.util.function.Consumer;
+
+public class CovariantReturnTypeMethods {
+  public static void registerMethodsWithCovariantReturnType(
+      DexItemFactory factory, Consumer<DexMethod> consumer) {
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/ByteBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/ByteBuffer;")),
+            "clear"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/ByteBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/ByteBuffer;")),
+            "flip"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/ByteBuffer;"),
+            factory.createProto(
+                factory.createType("Ljava/nio/ByteBuffer;"), factory.createType("I")),
+            "limit"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/ByteBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/ByteBuffer;")),
+            "mark"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/ByteBuffer;"),
+            factory.createProto(
+                factory.createType("Ljava/nio/ByteBuffer;"), factory.createType("I")),
+            "position"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/ByteBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/ByteBuffer;")),
+            "reset"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/ByteBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/ByteBuffer;")),
+            "rewind"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/CharBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/CharBuffer;")),
+            "clear"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/CharBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/CharBuffer;")),
+            "flip"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/CharBuffer;"),
+            factory.createProto(
+                factory.createType("Ljava/nio/CharBuffer;"), factory.createType("I")),
+            "limit"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/CharBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/CharBuffer;")),
+            "mark"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/CharBuffer;"),
+            factory.createProto(
+                factory.createType("Ljava/nio/CharBuffer;"), factory.createType("I")),
+            "position"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/CharBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/CharBuffer;")),
+            "reset"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/CharBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/CharBuffer;")),
+            "rewind"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/DoubleBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/DoubleBuffer;")),
+            "clear"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/DoubleBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/DoubleBuffer;")),
+            "flip"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/DoubleBuffer;"),
+            factory.createProto(
+                factory.createType("Ljava/nio/DoubleBuffer;"), factory.createType("I")),
+            "limit"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/DoubleBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/DoubleBuffer;")),
+            "mark"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/DoubleBuffer;"),
+            factory.createProto(
+                factory.createType("Ljava/nio/DoubleBuffer;"), factory.createType("I")),
+            "position"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/DoubleBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/DoubleBuffer;")),
+            "reset"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/DoubleBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/DoubleBuffer;")),
+            "rewind"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/FloatBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/FloatBuffer;")),
+            "clear"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/FloatBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/FloatBuffer;")),
+            "flip"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/FloatBuffer;"),
+            factory.createProto(
+                factory.createType("Ljava/nio/FloatBuffer;"), factory.createType("I")),
+            "limit"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/FloatBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/FloatBuffer;")),
+            "mark"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/FloatBuffer;"),
+            factory.createProto(
+                factory.createType("Ljava/nio/FloatBuffer;"), factory.createType("I")),
+            "position"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/FloatBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/FloatBuffer;")),
+            "reset"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/FloatBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/FloatBuffer;")),
+            "rewind"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/IntBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/IntBuffer;")),
+            "clear"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/IntBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/IntBuffer;")),
+            "flip"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/IntBuffer;"),
+            factory.createProto(
+                factory.createType("Ljava/nio/IntBuffer;"), factory.createType("I")),
+            "limit"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/IntBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/IntBuffer;")),
+            "mark"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/IntBuffer;"),
+            factory.createProto(
+                factory.createType("Ljava/nio/IntBuffer;"), factory.createType("I")),
+            "position"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/IntBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/IntBuffer;")),
+            "reset"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/IntBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/IntBuffer;")),
+            "rewind"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/LongBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/LongBuffer;")),
+            "clear"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/LongBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/LongBuffer;")),
+            "flip"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/LongBuffer;"),
+            factory.createProto(
+                factory.createType("Ljava/nio/LongBuffer;"), factory.createType("I")),
+            "limit"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/LongBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/LongBuffer;")),
+            "mark"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/LongBuffer;"),
+            factory.createProto(
+                factory.createType("Ljava/nio/LongBuffer;"), factory.createType("I")),
+            "position"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/LongBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/LongBuffer;")),
+            "reset"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/LongBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/LongBuffer;")),
+            "rewind"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/ShortBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/ShortBuffer;")),
+            "clear"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/ShortBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/ShortBuffer;")),
+            "flip"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/ShortBuffer;"),
+            factory.createProto(
+                factory.createType("Ljava/nio/ShortBuffer;"), factory.createType("I")),
+            "limit"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/ShortBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/ShortBuffer;")),
+            "mark"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/ShortBuffer;"),
+            factory.createProto(
+                factory.createType("Ljava/nio/ShortBuffer;"), factory.createType("I")),
+            "position"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/ShortBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/ShortBuffer;")),
+            "reset"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/nio/ShortBuffer;"),
+            factory.createProto(factory.createType("Ljava/nio/ShortBuffer;")),
+            "rewind"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/time/LocalDate;"),
+            factory.createProto(factory.createType("Ljava/time/chrono/IsoEra;")),
+            "getEra"));
+    consumer.accept(
+        factory.createMethod(
+            factory.createType("Ljava/util/concurrent/ConcurrentHashMap;"),
+            factory.createProto(
+                factory.createType("Ljava/util/concurrent/ConcurrentHashMap$KeySetView;")),
+            "keySet"));
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
index 0d0dd97..5d774b2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
@@ -260,7 +260,12 @@
     }
   }
 
-  private boolean isCovariantReturnTypeAnnotation(DexEncodedAnnotation annotation) {
+  public boolean isCovariantReturnTypeAnnotation(DexEncodedAnnotation annotation) {
+    return isCovariantReturnTypeAnnotation(annotation, factory);
+  }
+
+  public static boolean isCovariantReturnTypeAnnotation(
+      DexEncodedAnnotation annotation, DexItemFactory factory) {
     return isCovariantReturnTypeAnnotation(annotation.type, factory);
   }
 
diff --git a/src/main/java/com/android/tools/r8/utils/EntryUtils.java b/src/main/java/com/android/tools/r8/utils/EntryUtils.java
index e1d28b1..c096d00 100644
--- a/src/main/java/com/android/tools/r8/utils/EntryUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/EntryUtils.java
@@ -5,11 +5,12 @@
 package com.android.tools.r8.utils;
 
 import java.util.Map.Entry;
-import java.util.function.BiFunction;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
 
 public class EntryUtils {
 
-  public static <K, V, R> R accept(Entry<K, V> entry, BiFunction<K, V, R> consumer) {
-    return consumer.apply(entry.getKey(), entry.getValue());
+  public static <K, V> Consumer<Entry<K, V>> accept(BiConsumer<K, V> consumer) {
+    return entry -> consumer.accept(entry.getKey(), entry.getValue());
   }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/StringUtils.java b/src/main/java/com/android/tools/r8/utils/StringUtils.java
index 77618df..59011c5 100644
--- a/src/main/java/com/android/tools/r8/utils/StringUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/StringUtils.java
@@ -355,6 +355,10 @@
     return subject.replaceAll(Pattern.quote(target), Matcher.quoteReplacement(replacement));
   }
 
+  public static String quote(String string) {
+    return "\"" + string + "\"";
+  }
+
   public static String stacktraceAsString(Throwable throwable) {
     StringWriter sw = new StringWriter();
     throwable.printStackTrace(new PrintWriter(sw));
diff --git a/src/test/java/com/android/tools/r8/androidapi/GenerateCovariantReturnTypeMethodsTest.java b/src/test/java/com/android/tools/r8/androidapi/GenerateCovariantReturnTypeMethodsTest.java
new file mode 100644
index 0000000..db640a2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/androidapi/GenerateCovariantReturnTypeMethodsTest.java
@@ -0,0 +1,230 @@
+// 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.androidapi;
+
+import static com.android.tools.r8.apimodel.JavaSourceCodePrinter.Type.fromType;
+import static com.android.tools.r8.ir.desugar.CovariantReturnTypeAnnotationTransformer.isCovariantReturnTypeAnnotation;
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.apimodel.JavaSourceCodePrinter;
+import com.android.tools.r8.apimodel.JavaSourceCodePrinter.JavaSourceCodeMethodPrinter;
+import com.android.tools.r8.apimodel.JavaSourceCodePrinter.KnownType;
+import com.android.tools.r8.apimodel.JavaSourceCodePrinter.MethodParameter;
+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.DexItemFactory;
+import com.android.tools.r8.graph.DexValue.DexValueType;
+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.EntryUtils;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import com.google.common.collect.ImmutableList;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
+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 GenerateCovariantReturnTypeMethodsTest extends TestBase {
+
+  private static final String CLASS_NAME = "CovariantReturnTypeMethods";
+  private static final String PACKAGE_NAME = "com.android.tools.r8.androidapi";
+  // When updating to support a new api level build libcore in aosp and update the cloud dependency.
+  private static final Path PATH_TO_CORE_JAR =
+      Paths.get(ToolHelper.THIRD_PARTY_DIR, "android_jar", "libcore_latest", "core-oj.jar");
+  private static final Path DESTINATION_FILE =
+      Paths.get(ToolHelper.SOURCE_DIR)
+          .resolve(PACKAGE_NAME.replace('.', '/'))
+          .resolve(CLASS_NAME + ".java");
+  private static final AndroidApiLevel GENERATED_FOR_API_LEVEL = AndroidApiLevel.T;
+
+  @Parameter public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  @Test
+  public void testLibCoreNeedsUpgrading() {
+    assertEquals(GENERATED_FOR_API_LEVEL, AndroidApiLevel.LATEST);
+  }
+
+  @Test
+  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());
+  }
+
+  @Test
+  public void testGeneratedCodeUpToDate() throws Exception {
+    assertEquals(FileUtils.readTextFile(DESTINATION_FILE, StandardCharsets.UTF_8), generateCode());
+  }
+
+  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()));
+    JavaSourceCodePrinter printer =
+        JavaSourceCodePrinter.builder()
+            .setHeader(
+                MethodGenerationBase.getHeaderString(
+                    2022, GenerateCovariantReturnTypeMethodsTest.class.getSimpleName()))
+            .setPackageName(PACKAGE_NAME)
+            .setClassName(CLASS_NAME)
+            .build();
+    String javaSourceCode =
+        printer
+            .addMethod(
+                "public static",
+                null,
+                "registerMethodsWithCovariantReturnType",
+                ImmutableList.of(
+                    MethodParameter.build(fromType(KnownType.DexItemFactory), "factory"),
+                    MethodParameter.build(
+                        ParameterizedType.fromType(
+                            KnownType.Consumer, fromType(KnownType.DexMethod)),
+                        "consumer")),
+                methodPrinter ->
+                    entries.forEach(
+                        EntryUtils.accept(
+                            (ignored, covariations) ->
+                                covariations.forEach(
+                                    covariant ->
+                                        registerCovariantMethod(methodPrinter, covariant)))))
+            .toString();
+    Path tempFile = Files.createTempFile("output-", ".java");
+    Files.write(tempFile, javaSourceCode.getBytes(StandardCharsets.UTF_8));
+    return MethodGenerationBase.formatRawOutput(tempFile);
+  }
+
+  private static void registerCovariantMethod(
+      JavaSourceCodeMethodPrinter methodPrinter, MethodReference covariant) {
+    methodPrinter
+        .addInstanceMethodCall(
+            "consumer",
+            "accept",
+            () ->
+                methodPrinter.addInstanceMethodCall(
+                    "factory",
+                    "createMethod",
+                    callCreateType(methodPrinter, covariant.getHolderClass().getDescriptor()),
+                    callCreateProto(
+                        methodPrinter,
+                        covariant.getReturnType().getDescriptor(),
+                        covariant.getFormalTypes().stream()
+                            .map(TypeReference::getDescriptor)
+                            .collect(Collectors.toList())),
+                    methodPrinter.literal(covariant.getMethodName())))
+        .addSemicolon()
+        .newLine();
+  }
+
+  private static Action callCreateType(
+      JavaSourceCodeMethodPrinter methodPrinter, String descriptor) {
+    return () ->
+        methodPrinter.addInstanceMethodCall(
+            "factory", "createType", methodPrinter.literal(descriptor));
+  }
+
+  private static Action callCreateProto(
+      JavaSourceCodeMethodPrinter methodPrinter,
+      String returnTypeDescriptor,
+      Collection<String> args) {
+    List<Action> actionList = new ArrayList<>();
+    actionList.add(callCreateType(methodPrinter, returnTypeDescriptor));
+    for (String arg : args) {
+      actionList.add(callCreateType(methodPrinter, arg));
+    }
+    return () -> methodPrinter.addInstanceMethodCall("factory", "createProto", actionList);
+  }
+
+  public static void main(String[] args) throws Exception {
+    Files.write(DESTINATION_FILE, generateCode().getBytes(StandardCharsets.UTF_8));
+  }
+
+  public static class CovariantMethodsInJarResult {
+    private final Map<MethodReference, List<MethodReference>> methodReferenceMap;
+
+    private CovariantMethodsInJarResult(
+        Map<MethodReference, List<MethodReference>> methodReferenceMap) {
+      this.methodReferenceMap = methodReferenceMap;
+    }
+
+    public static CovariantMethodsInJarResult create() throws Exception {
+      Map<MethodReference, List<MethodReference>> methodReferenceMap = new HashMap<>();
+      CodeInspector inspector = new CodeInspector(PATH_TO_CORE_JAR);
+      DexItemFactory factory = inspector.getFactory();
+      for (FoundClassSubject clazz : inspector.allClasses()) {
+        clazz.forAllMethods(
+            method -> {
+              List<DexAnnotation> covariantAnnotations =
+                  inspector.findAnnotations(
+                      method.getMethod().annotations(),
+                      annotation ->
+                          isCovariantReturnTypeAnnotation(annotation.annotation, factory));
+              if (!covariantAnnotations.isEmpty()) {
+                MethodReference methodReference = method.asMethodReference();
+                for (DexAnnotation covariantAnnotation : covariantAnnotations) {
+                  if (covariantAnnotation.annotation.type
+                      == factory.annotationCovariantReturnType) {
+                    createCovariantMethodReference(
+                        methodReference, covariantAnnotation, methodReferenceMap);
+                  } else {
+                    fail("There are no such annotations present in libcore");
+                  }
+                }
+              }
+            });
+      }
+      return new CovariantMethodsInJarResult(methodReferenceMap);
+    }
+
+    private static void createCovariantMethodReference(
+        MethodReference methodReference,
+        DexAnnotation covariantAnnotation,
+        Map<MethodReference, List<MethodReference>> methodReferenceMap) {
+      DexValueType newReturnType =
+          covariantAnnotation.annotation.getElement(0).getValue().asDexValueType();
+      methodReferenceMap
+          .computeIfAbsent(methodReference, ignoreKey(ArrayList::new))
+          .add(
+              Reference.method(
+                  methodReference.getHolderClass(),
+                  methodReference.getMethodName(),
+                  methodReference.getFormalTypes(),
+                  newReturnType.value.asClassReference()));
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/JavaSourceCodePrinter.java b/src/test/java/com/android/tools/r8/apimodel/JavaSourceCodePrinter.java
new file mode 100644
index 0000000..fe1c78a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/JavaSourceCodePrinter.java
@@ -0,0 +1,264 @@
+// 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.apimodel;
+
+import com.android.tools.r8.utils.Action;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.StringUtils.BraceType;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+public final class JavaSourceCodePrinter {
+
+  private final Set<String> imports = new HashSet<>();
+  private final String className;
+  private final String packageName;
+  private final String classModifiers;
+  private final String header;
+  private final StringBuilder bodyPrinter = new StringBuilder();
+
+  public enum KnownType {
+    Consumer("java.util.function", "Consumer", false),
+    DexItemFactory("com.android.tools.r8.graph", "DexItemFactory", false),
+    DexMethod("com.android.tools.r8.graph", "DexMethod", false);
+
+    private String packageName;
+    private String simpleName;
+    private boolean isPrimitive;
+
+    KnownType(String packageName, String simpleName, boolean isPrimitive) {
+      this.packageName = packageName;
+      this.simpleName = simpleName;
+      this.isPrimitive = isPrimitive;
+    }
+
+    boolean isPrimitive() {
+      return isPrimitive;
+    }
+
+    String getCanonicalName() {
+      return packageName + "." + simpleName;
+    }
+
+    String getSimpleName() {
+      return simpleName;
+    }
+  }
+
+  public JavaSourceCodePrinter(
+      String className, String packageName, String classModifiers, String header) {
+    this.className = className;
+    this.packageName = packageName;
+    this.classModifiers = classModifiers;
+    this.header = header;
+  }
+
+  public void addClassImport(KnownType type) {
+    if (!type.isPrimitive) {
+      imports.add(type.getCanonicalName());
+    }
+  }
+
+  public JavaSourceCodePrinter addMethod(
+      String modifiers,
+      Type returnType,
+      String name,
+      List<MethodParameter> parameters,
+      Consumer<JavaSourceCodeMethodPrinter> content) {
+    bodyPrinter.append(modifiers).append(" ");
+    if (returnType != null) {
+      bodyPrinter.append(returnType.toString(this::addClassImport));
+    } else {
+      bodyPrinter.append("void");
+    }
+    JavaSourceCodeMethodPrinter javaSourceCodeMethodPrinter = new JavaSourceCodeMethodPrinter();
+    content.accept(javaSourceCodeMethodPrinter);
+    bodyPrinter
+        .append(" ")
+        .append(name)
+        .append(
+            StringUtils.join(
+                ", ",
+                parameters,
+                parameter -> parameter.toString(this::addClassImport),
+                BraceType.PARENS))
+        .append(" {")
+        .append(StringUtils.LINE_SEPARATOR)
+        .append(javaSourceCodeMethodPrinter)
+        .append("}")
+        .append(StringUtils.LINE_SEPARATOR);
+    return this;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder(header);
+    if (packageName != null) {
+      sb.append("package ").append(packageName).append(";").append(StringUtils.LINE_SEPARATOR);
+    }
+    sb.append(
+            StringUtils.joinLines(
+                imports.stream()
+                    .sorted()
+                    .map(imp -> "import " + imp + ";")
+                    .collect(Collectors.toList())))
+        .append(StringUtils.LINE_SEPARATOR);
+    return sb.append(StringUtils.LINE_SEPARATOR)
+        .append(classModifiers)
+        .append(" ")
+        .append("class ")
+        .append(className)
+        .append(" {")
+        .append(StringUtils.LINE_SEPARATOR)
+        .append(bodyPrinter)
+        .append("}")
+        .toString();
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static class Builder {
+    private String className;
+    private String packageName = null;
+    private final String classModifiers = "public";
+    private String header = "";
+
+    public Builder setClassName(String className) {
+      this.className = className;
+      return this;
+    }
+
+    public Builder setPackageName(String packageName) {
+      this.packageName = packageName;
+      return this;
+    }
+
+    public Builder setHeader(String header) {
+      this.header = header;
+      return this;
+    }
+
+    public JavaSourceCodePrinter build() {
+      assert className != null;
+      return new JavaSourceCodePrinter(className, packageName, classModifiers, header);
+    }
+  }
+
+  public static class JavaSourceCodeMethodPrinter {
+
+    private final StringBuilder sb = new StringBuilder();
+
+    public JavaSourceCodeMethodPrinter addInstanceMethodCall(
+        String variable, String method, Action... content) {
+      return addInstanceMethodCall(variable, method, Arrays.asList(content));
+    }
+
+    public JavaSourceCodeMethodPrinter addInstanceMethodCall(
+        String variable, String method, Collection<Action> content) {
+      sb.append(variable).append(".").append(method).append("(");
+      boolean insertSeparator = false;
+      for (Action action : content) {
+        if (insertSeparator) {
+          sb.append(", ");
+        }
+        action.execute();
+        insertSeparator = true;
+      }
+      sb.append(")");
+      return this;
+    }
+
+    public Action literal(String constant) {
+      return () -> sb.append(StringUtils.quote(constant));
+    }
+
+    public JavaSourceCodeMethodPrinter addSemicolon() {
+      sb.append(";");
+      return this;
+    }
+
+    public JavaSourceCodeMethodPrinter newLine() {
+      sb.append(StringUtils.LINE_SEPARATOR);
+      return this;
+    }
+
+    @Override
+    public String toString() {
+      return sb.toString();
+    }
+  }
+
+  public static class Type {
+
+    private final KnownType type;
+
+    private Type(KnownType type) {
+      this.type = type;
+    }
+
+    public static Type fromType(KnownType type) {
+      return new Type(type);
+    }
+
+    public String toString(Consumer<KnownType> classConsumer) {
+      classConsumer.accept(type);
+      return type.getSimpleName();
+    }
+  }
+
+  public static class ParameterizedType extends Type {
+
+    private final Type[] arguments;
+
+    private ParameterizedType(KnownType clazz, Type[] arguments) {
+      super(clazz);
+      this.arguments = arguments;
+    }
+
+    public static ParameterizedType fromType(KnownType type, Type... arguments) {
+      return new ParameterizedType(type, arguments);
+    }
+
+    @Override
+    public String toString(Consumer<KnownType> classConsumer) {
+      StringBuilder sb = new StringBuilder();
+      sb.append(super.toString(classConsumer));
+      if (arguments.length == 0) {
+        return sb.toString();
+      }
+      sb.append("<");
+      sb.append(
+          StringUtils.join(", ", Arrays.asList(arguments), type -> type.toString(classConsumer)));
+      sb.append(">");
+      return sb.toString();
+    }
+  }
+
+  public static class MethodParameter {
+
+    private final Type type;
+    private final String name;
+
+    private MethodParameter(Type type, String name) {
+      this.type = type;
+      this.name = name;
+    }
+
+    public static MethodParameter build(Type type, String name) {
+      return new MethodParameter(type, name);
+    }
+
+    private String toString(Consumer<KnownType> classConsumer) {
+      return type.toString(classConsumer) + " " + name;
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java b/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
index 2c58d15..692773c 100644
--- a/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
+++ b/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
@@ -52,16 +52,20 @@
 
   public String getHeaderString() {
     String simpleName = getClass().getSimpleName();
+    return getHeaderString(getYear(), simpleName)
+        + StringUtils.lines("package " + getGeneratedClassPackageName() + ";");
+  }
+
+  public static String getHeaderString(int year, String simpeNameOfGenerator) {
     return StringUtils.lines(
-        "// Copyright (c) " + getYear() + ", the R8 project authors. Please see the AUTHORS file",
+        "// Copyright (c) " + year + ", 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.",
         "",
         "// ***********************************************************************************",
-        "// GENERATED FILE. DO NOT EDIT! See " + simpleName + ".java.",
+        "// GENERATED FILE. DO NOT EDIT! See " + simpeNameOfGenerator + ".java.",
         "// ***********************************************************************************",
-        "",
-        "package " + getGeneratedClassPackageName() + ";");
+        "");
   }
 
   protected Path getGeneratedFile() {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index 44942ab..402ef93 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -51,6 +51,7 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -59,6 +60,8 @@
 import java.util.function.BiFunction;
 import java.util.function.Consumer;
 import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
 
 public class CodeInspector {
 
@@ -232,15 +235,26 @@
     }
   }
 
-  DexAnnotation findAnnotation(String name, DexAnnotationSet annotations) {
-    for (DexAnnotation annotation : annotations.annotations) {
-      DexType type = annotation.annotation.type;
-      String original = mapping == null ? type.toSourceString() : mapping.originalNameOf(type);
-      if (original.equals(name)) {
-        return annotation;
-      }
-    }
-    return null;
+  public DexAnnotation findAnnotation(DexAnnotationSet annotationSet, String name) {
+    return findAnnotation(
+        annotationSet,
+        annotation -> {
+          DexType type = annotation.annotation.type;
+          String original = mapping == null ? type.toSourceString() : mapping.originalNameOf(type);
+          return original.equals(name);
+        });
+  }
+
+  public DexAnnotation findAnnotation(
+      DexAnnotationSet annotationSet, Predicate<DexAnnotation> predicate) {
+    List<DexAnnotation> annotations = findAnnotations(annotationSet, predicate);
+    assert annotations.size() <= 1;
+    return annotations.isEmpty() ? null : annotations.get(0);
+  }
+
+  public List<DexAnnotation> findAnnotations(
+      DexAnnotationSet annotationSet, Predicate<DexAnnotation> predicate) {
+    return Arrays.stream(annotationSet.annotations).filter(predicate).collect(Collectors.toList());
   }
 
   public String getOriginalSignatureAttribute(
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 dd39316..cb65261 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
@@ -371,7 +371,7 @@
     assert !name.endsWith("EnclosingClass")
         && !name.endsWith("EnclosingMethod")
         && !name.endsWith("InnerClass");
-    DexAnnotation annotation = codeInspector.findAnnotation(name, dexClass.annotations());
+    DexAnnotation annotation = codeInspector.findAnnotation(dexClass.annotations(), name);
     return annotation == null
         ? new AbsentAnnotationSubject()
         : new FoundAnnotationSubject(annotation, codeInspector);
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 c2daded..d16221e 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
@@ -120,7 +120,7 @@
 
   @Override
   public AnnotationSubject annotation(String name) {
-    DexAnnotation annotation = codeInspector.findAnnotation(name, dexField.annotations());
+    DexAnnotation annotation = codeInspector.findAnnotation(dexField.annotations(), name);
     return annotation == null
         ? new AbsentAnnotationSubject()
         : new FoundAnnotationSubject(annotation, codeInspector);
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 8a27919..82ff966 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
@@ -372,7 +372,7 @@
 
   @Override
   public AnnotationSubject annotation(String name) {
-    DexAnnotation annotation = codeInspector.findAnnotation(name, dexMethod.annotations());
+    DexAnnotation annotation = codeInspector.findAnnotation(dexMethod.annotations(), name);
     return annotation == null
         ? new AbsentAnnotationSubject()
         : new FoundAnnotationSubject(annotation, codeInspector);
diff --git a/third_party/android_jar/libcore_latest.tar.gz.sha1 b/third_party/android_jar/libcore_latest.tar.gz.sha1
new file mode 100644
index 0000000..c300639
--- /dev/null
+++ b/third_party/android_jar/libcore_latest.tar.gz.sha1
@@ -0,0 +1 @@
+f7570bec27977e786b637241778e6cfdadf25f7f
\ No newline at end of file