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