Refactor supportedClasses to introduce fields
- fix minor issues
Change-Id: I69ab6177d7ac3d886f4fc23b5c9b0f7a3c4e1653
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/AbstractGenerateFiles.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/AbstractGenerateFiles.java
index fdc1f13..c76c17f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/AbstractGenerateFiles.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/AbstractGenerateFiles.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
@@ -20,13 +19,11 @@
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Sets;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
-import java.util.Set;
import java.util.concurrent.ExecutorService;
public abstract class AbstractGenerateFiles {
@@ -44,7 +41,6 @@
final Path desugaredLibrarySpecificationPath;
final Collection<Path> desugaredLibraryImplementation;
final Path outputDirectory;
- final Set<DexMethod> parallelMethods = Sets.newIdentityHashSet();
public AbstractGenerateFiles(
String desugarConfigurationPath, String desugarImplementationPath, String outputDirectory)
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateHtmlDoc.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateHtmlDoc.java
index ead34cb..859bc95 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateHtmlDoc.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateHtmlDoc.java
@@ -9,13 +9,13 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.FieldAccessFlags;
import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedMethodsWithAnnotations.ClassAnnotation;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedMethodsWithAnnotations.MethodAnnotation;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses.ClassAnnotation;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses.MethodAnnotation;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses.SupportedClass;
import com.android.tools.r8.utils.AndroidApiLevel;
import java.io.PrintStream;
import java.nio.file.Files;
@@ -467,7 +467,7 @@
}
builder.end("ul").end("td");
StringBuilder commentBuilder = new StringBuilder();
- if (classAnnotation.fullySupported) {
+ if (classAnnotation.isFullySupported()) {
commentBuilder.append("Fully implemented class.").append(HTML_SPLIT);
}
if (parallelStreamMethod) {
@@ -496,10 +496,10 @@
.append(" Also supported with covariant return type.")
.append(HTML_SPLIT);
}
- if (!classAnnotation.unsupportedMethods.isEmpty()) {
+ if (!classAnnotation.getUnsupportedMethods().isEmpty()) {
commentBuilder
.append("Some methods (")
- .append(classAnnotation.unsupportedMethods.size())
+ .append(classAnnotation.getUnsupportedMethods().size())
.append(") present in Android ")
.append(MAX_TESTED_ANDROID_API_LEVEL)
.append(" are not supported.");
@@ -510,25 +510,22 @@
}
}
- private void generateClassHTML(
- PrintStream ps,
- DexClass clazz,
- List<DexEncodedMethod> methods,
- ClassAnnotation classAnnotation,
- Map<DexMethod, MethodAnnotation> methodAnnotationMap) {
- SourceBuilder<HTMLSourceBuilder> builder = new HTMLSourceBuilder(clazz, classAnnotation);
+ private void generateClassHTML(PrintStream ps, SupportedClass supportedClass) {
+ DexClass clazz = supportedClass.getClazz();
+ SourceBuilder<HTMLSourceBuilder> builder =
+ new HTMLSourceBuilder(clazz, supportedClass.getClassAnnotation());
// We need to extend to support fields.
StreamSupport.stream(clazz.fields().spliterator(), false)
.filter(field -> field.accessFlags.isPublic() || field.accessFlags.isProtected())
.sorted(Comparator.comparing(DexEncodedField::toSourceString))
.forEach(builder::addField);
- methods.stream()
- .filter(
- method ->
- (method.accessFlags.isPublic() || method.accessFlags.isProtected())
- && !method.accessFlags.isBridge())
- .sorted(Comparator.comparing(DexEncodedMethod::toSourceString))
- .forEach(m -> builder.addMethod(m, methodAnnotationMap.get(m.getReference())));
+ supportedClass.forEachMethodAndAnnotation(
+ (method, methodAnnotation) -> {
+ if ((method.accessFlags.isPublic() || method.accessFlags.isProtected())
+ && !method.accessFlags.isBridge()) {
+ builder.addMethod(method, methodAnnotation);
+ }
+ });
ps.println(builder);
}
@@ -536,20 +533,12 @@
AndroidApiLevel run() throws Exception {
PrintStream ps = new PrintStream(Files.newOutputStream(outputDirectory.resolve("apis.html")));
- SupportedMethodsWithAnnotations supportedMethods =
+ SupportedClasses supportedClasses =
new SupportedMethodsGenerator(options)
.run(desugaredLibraryImplementation, desugaredLibrarySpecificationPath);
// Full classes added.
- supportedMethods.supportedMethods.forEach(
- (clazz, methods) -> {
- generateClassHTML(
- ps,
- clazz,
- methods,
- supportedMethods.annotatedClasses.get(clazz.type),
- supportedMethods.annotatedMethods);
- });
+ supportedClasses.forEachClass(supportedClass -> generateClassHTML(ps, supportedClass));
return MAX_TESTED_ANDROID_API_LEVEL;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateLintFiles.java
index 721a5d9..b7a1a27 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateLintFiles.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateLintFiles.java
@@ -5,10 +5,15 @@
package com.android.tools.r8.ir.desugar.desugaredlibrary.lint;
import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.Version;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.cf.code.CfConstNull;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfThrow;
+import com.android.tools.r8.dex.Marker;
+import com.android.tools.r8.dex.Marker.Backend;
+import com.android.tools.r8.dex.Marker.Tool;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
@@ -24,7 +29,7 @@
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.LazyLoadedDexApplication;
import com.android.tools.r8.graph.MethodCollection.MethodCollectionFactory;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedMethodsWithAnnotations.MethodAnnotation;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses.MethodAnnotation;
import com.android.tools.r8.jar.CfApplicationWriter;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.synthesis.SyntheticItems.GlobalSyntheticsStrategy;
@@ -150,37 +155,38 @@
private void writeLintFiles(
AndroidApiLevel compilationApiLevel,
AndroidApiLevel minApiLevel,
- SupportedMethodsWithAnnotations supportedMethods)
+ SupportedClasses supportedClasses)
throws Exception {
// Build a plain text file with the desugared APIs.
List<String> desugaredApisSignatures = new ArrayList<>();
LazyLoadedDexApplication.Builder builder = DexApplication.builder(options, Timing.empty());
- supportedMethods.supportedMethods.forEach(
- (clazz, methods) -> {
+ supportedClasses.forEachClass(
+ (supportedClass) -> {
String classBinaryName =
- DescriptorUtils.getClassBinaryNameFromDescriptor(clazz.type.descriptor.toString());
- if (!supportedMethods.annotatedClasses.get(clazz.type).fullySupported) {
- for (DexEncodedMethod method : methods) {
- if (method.isInstanceInitializer() || method.isClassInitializer()) {
- // No new constructors are added.
- continue;
- }
- MethodAnnotation methodAnnotation =
- supportedMethods.annotatedMethods.get(method.getReference());
- if (shouldAddMethodToLint(methodAnnotation, minApiLevel)) {
- desugaredApisSignatures.add(
- classBinaryName
- + '#'
- + method.getReference().name
- + method.getReference().proto.toDescriptorString());
- }
- }
+ DescriptorUtils.getClassBinaryNameFromDescriptor(
+ supportedClass.getType().descriptor.toString());
+ if (!supportedClass.getClassAnnotation().isFullySupported()) {
+ supportedClass.forEachMethodAndAnnotation(
+ (method, methodAnnotation) -> {
+ if (method.isInstanceInitializer() || method.isClassInitializer()) {
+ // No new constructors are added.
+ return;
+ }
+ if (shouldAddMethodToLint(methodAnnotation, minApiLevel)) {
+ desugaredApisSignatures.add(
+ classBinaryName
+ + '#'
+ + method.getReference().name
+ + method.getReference().proto.toDescriptorString());
+ }
+ });
} else {
desugaredApisSignatures.add(classBinaryName);
}
- addMethodsToHeaderJar(builder, clazz, methods);
+ addMethodsToHeaderJar(
+ builder, supportedClass.getClazz(), supportedClass.getSupportedMethods());
});
// Write a plain text file with the desugared APIs.
@@ -193,7 +199,12 @@
AppView.createForD8(
AppInfo.createInitialAppInfo(
builder.build(), GlobalSyntheticsStrategy.forNonSynthesizing()));
- CfApplicationWriter writer = new CfApplicationWriter(appView, options.getMarker());
+ Marker marker =
+ new Marker(Tool.D8)
+ .setVersion(Version.LABEL)
+ .setCompilationMode(CompilationMode.DEBUG)
+ .setBackend(Backend.CF);
+ CfApplicationWriter writer = new CfApplicationWriter(appView, marker);
ClassFileConsumer consumer =
new ClassFileConsumer.ArchiveConsumer(
lintFile(compilationApiLevel, minApiLevel, FileUtils.JAR_EXTENSION));
@@ -221,7 +232,7 @@
private void generateLintFiles(
AndroidApiLevel compilationApiLevel,
AndroidApiLevel minApiLevel,
- SupportedMethodsWithAnnotations supportedMethods)
+ SupportedClasses supportedMethods)
throws Exception {
System.out.print(" - generating for min API:");
System.out.print(" " + minApiLevel);
@@ -232,7 +243,7 @@
public AndroidApiLevel run() throws Exception {
AndroidApiLevel compilationLevel =
desugaredLibrarySpecification.getRequiredCompilationApiLevel();
- SupportedMethodsWithAnnotations supportedMethods =
+ SupportedClasses supportedMethods =
new SupportedMethodsGenerator(options)
.run(desugaredLibraryImplementation, desugaredLibrarySpecificationPath);
System.out.println("Generating lint files for compile API " + compilationLevel);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClasses.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClasses.java
new file mode 100644
index 0000000..3c1cfd6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClasses.java
@@ -0,0 +1,332 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.desugaredlibrary.lint;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSortedMap;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+public class SupportedClasses {
+ private final Map<DexType, SupportedClass> supportedClasses;
+
+ public void forEachClass(Consumer<SupportedClass> consumer) {
+ supportedClasses.values().forEach(consumer);
+ }
+
+ SupportedClasses(Map<DexType, SupportedClass> supportedClasses) {
+ this.supportedClasses = supportedClasses;
+ }
+
+ public static class SupportedClass {
+
+ private final DexClass clazz;
+ private final ClassAnnotation classAnnotation;
+ private final List<DexEncodedMethod> supportedMethods;
+ private final Map<DexMethod, MethodAnnotation> methodAnnotations;
+
+ private SupportedClass(
+ DexClass clazz,
+ ClassAnnotation classAnnotation,
+ List<DexEncodedMethod> supportedMethods,
+ Map<DexMethod, MethodAnnotation> methodAnnotations) {
+ this.clazz = clazz;
+ this.classAnnotation = classAnnotation;
+ this.supportedMethods = supportedMethods;
+ this.methodAnnotations = methodAnnotations;
+ }
+
+ public DexType getType() {
+ return clazz.type;
+ }
+
+ public DexClass getClazz() {
+ return clazz;
+ }
+
+ public ClassAnnotation getClassAnnotation() {
+ return classAnnotation;
+ }
+
+ public List<DexEncodedMethod> getSupportedMethods() {
+ return supportedMethods;
+ }
+
+ public void forEachMethodAndAnnotation(
+ BiConsumer<DexEncodedMethod, MethodAnnotation> biConsumer) {
+ for (DexEncodedMethod supportedMethod : supportedMethods) {
+ biConsumer.accept(supportedMethod, getMethodAnnotation(supportedMethod.getReference()));
+ }
+ }
+
+ public MethodAnnotation getMethodAnnotation(DexMethod method) {
+ return methodAnnotations.get(method);
+ }
+
+ static Builder builder(DexClass clazz) {
+ return new Builder(clazz);
+ }
+
+ private static class Builder {
+
+ private final DexClass clazz;
+ private ClassAnnotation classAnnotation;
+ private final List<DexEncodedMethod> supportedMethods = new ArrayList<>();
+ private final Map<DexMethod, MethodAnnotation> methodAnnotations = new HashMap<>();
+
+ private Builder(DexClass clazz) {
+ this.clazz = clazz;
+ }
+
+ void forEachMethods(BiConsumer<DexClass, List<DexEncodedMethod>> biConsumer) {
+ biConsumer.accept(clazz, supportedMethods);
+ }
+
+ void forEachMethod(BiConsumer<DexClass, DexEncodedMethod> biConsumer) {
+ for (DexEncodedMethod dexEncodedMethod : supportedMethods) {
+ biConsumer.accept(clazz, dexEncodedMethod);
+ }
+ }
+
+ void addSupportedMethod(DexEncodedMethod method) {
+ assert method.getHolderType() == clazz.type;
+ supportedMethods.add(method);
+ }
+
+ void annotateClass(ClassAnnotation annotation) {
+ assert annotation != null;
+ assert classAnnotation == null;
+ classAnnotation = annotation;
+ }
+
+ void annotateMethod(DexMethod method, MethodAnnotation annotation) {
+ assert method.getHolderType() == clazz.type;
+ MethodAnnotation prev =
+ methodAnnotations.getOrDefault(method, MethodAnnotation.getDefault());
+ methodAnnotations.put(method, annotation.combine(prev));
+ }
+
+ MethodAnnotation getMethodAnnotation(DexMethod method) {
+ return methodAnnotations.get(method);
+ }
+
+ SupportedClass build() {
+ supportedMethods.sort(Comparator.comparing(DexEncodedMethod::getReference));
+ return new SupportedClass(
+ clazz, classAnnotation, ImmutableList.copyOf(supportedMethods), methodAnnotations);
+ }
+ }
+ }
+
+ static Builder builder() {
+ return new Builder();
+ }
+
+ static class Builder {
+
+ Map<DexType, SupportedClass.Builder> supportedClassBuilders = new IdentityHashMap<>();
+
+ void forEachClassAndMethods(BiConsumer<DexClass, List<DexEncodedMethod>> biConsumer) {
+ supportedClassBuilders
+ .values()
+ .forEach(classBuilder -> classBuilder.forEachMethods(biConsumer));
+ }
+
+ void forEachClassAndMethod(BiConsumer<DexClass, DexEncodedMethod> biConsumer) {
+ supportedClassBuilders
+ .values()
+ .forEach(classBuilder -> classBuilder.forEachMethod(biConsumer));
+ }
+
+ void addSupportedMethod(DexClass holder, DexEncodedMethod method) {
+ SupportedClass.Builder classBuilder =
+ supportedClassBuilders.computeIfAbsent(
+ holder.type, clazz -> SupportedClass.builder(holder));
+ classBuilder.addSupportedMethod(method);
+ }
+
+ void annotateClass(DexType type, ClassAnnotation annotation) {
+ SupportedClass.Builder classBuilder = supportedClassBuilders.get(type);
+ assert classBuilder != null;
+ classBuilder.annotateClass(annotation);
+ }
+
+ void annotateMethod(DexMethod method, MethodAnnotation annotation) {
+ SupportedClass.Builder classBuilder = supportedClassBuilders.get(method.getHolderType());
+ assert classBuilder != null;
+ classBuilder.annotateMethod(method, annotation);
+ }
+
+ void annotateMethodIfPresent(DexMethod method, MethodAnnotation annotation) {
+ SupportedClass.Builder classBuilder = supportedClassBuilders.get(method.getHolderType());
+ if (classBuilder == null) {
+ return;
+ }
+ annotateMethod(method, annotation);
+ }
+
+ MethodAnnotation getMethodAnnotation(DexMethod method) {
+ SupportedClass.Builder classBuilder = supportedClassBuilders.get(method.getHolderType());
+ assert classBuilder != null;
+ return classBuilder.getMethodAnnotation(method);
+ }
+
+ SupportedClasses build() {
+ Map<DexType, SupportedClass> map = new IdentityHashMap<>();
+ supportedClassBuilders.forEach(
+ (type, classBuilder) -> {
+ map.put(type, classBuilder.build());
+ });
+ return new SupportedClasses(ImmutableSortedMap.copyOf(map));
+ }
+ }
+
+ static class ClassAnnotation {
+
+ private final boolean fullySupported;
+ // Methods in latest android.jar but unsupported.
+ private final List<DexMethod> unsupportedMethods;
+
+ public ClassAnnotation(boolean fullySupported, List<DexMethod> unsupportedMethods) {
+ this.fullySupported = fullySupported;
+ unsupportedMethods.sort(Comparator.naturalOrder());
+ this.unsupportedMethods = ImmutableList.copyOf(unsupportedMethods);
+ }
+
+ public boolean isFullySupported() {
+ return fullySupported;
+ }
+
+ public List<DexMethod> getUnsupportedMethods() {
+ return unsupportedMethods;
+ }
+ }
+
+ public static class MethodAnnotation {
+
+ private static final MethodAnnotation COVARIANT_RETURN_SUPPORTED =
+ new MethodAnnotation(false, false, true, false, -1, -1);
+ private static final MethodAnnotation DEFAULT =
+ new MethodAnnotation(false, false, false, false, -1, -1);
+ private static final MethodAnnotation PARALLEL_STREAM_METHOD =
+ new MethodAnnotation(true, false, false, false, -1, -1);
+ private static final MethodAnnotation MISSING_FROM_LATEST_ANDROID_JAR =
+ new MethodAnnotation(false, true, false, false, -1, -1);
+
+ // ParallelStream methods are not supported when the runtime api level is strictly below 21.
+ final boolean parallelStreamMethod;
+ // Methods not in the latest android jar but still fully supported.
+ final boolean missingFromLatestAndroidJar;
+ // Methods not supported in a given min api range.
+ final boolean unsupportedInMinApiRange;
+ final boolean covariantReturnSupported;
+ final int minRange;
+ final int maxRange;
+
+ MethodAnnotation(
+ boolean parallelStreamMethod,
+ boolean missingFromLatestAndroidJar,
+ boolean covariantReturnSupported,
+ boolean unsupportedInMinApiRange,
+ int minRange,
+ int maxRange) {
+ this.parallelStreamMethod = parallelStreamMethod;
+ this.missingFromLatestAndroidJar = missingFromLatestAndroidJar;
+ this.covariantReturnSupported = covariantReturnSupported;
+ this.unsupportedInMinApiRange = unsupportedInMinApiRange;
+ this.minRange = minRange;
+ this.maxRange = maxRange;
+ }
+
+ public static MethodAnnotation getCovariantReturnSupported() {
+ return COVARIANT_RETURN_SUPPORTED;
+ }
+
+ public static MethodAnnotation getDefault() {
+ return DEFAULT;
+ }
+
+ public static MethodAnnotation getParallelStreamMethod() {
+ return PARALLEL_STREAM_METHOD;
+ }
+
+ public static MethodAnnotation getMissingFromLatestAndroidJar() {
+ return MISSING_FROM_LATEST_ANDROID_JAR;
+ }
+
+ public static MethodAnnotation createMissingInMinApi(int api) {
+ return new MethodAnnotation(false, false, false, true, api, api);
+ }
+
+ public boolean isUnsupportedInMinApiRange() {
+ return unsupportedInMinApiRange;
+ }
+
+ public int getMinRange() {
+ return minRange;
+ }
+
+ public int getMaxRange() {
+ return maxRange;
+ }
+
+ public boolean isCovariantReturnSupported() {
+ return covariantReturnSupported;
+ }
+
+ public MethodAnnotation combine(MethodAnnotation other) {
+ if (this == getDefault()) {
+ return other;
+ }
+ if (other == getDefault()) {
+ return this;
+ }
+ int newMin, newMax;
+ if (!unsupportedInMinApiRange && !other.unsupportedInMinApiRange) {
+ newMin = newMax = -1;
+ } else if (!unsupportedInMinApiRange || !other.unsupportedInMinApiRange) {
+ newMin = unsupportedInMinApiRange ? minRange : other.minRange;
+ newMax = unsupportedInMinApiRange ? maxRange : other.maxRange;
+ } else {
+ // Merge ranges if contiguous or throw.
+ if (maxRange == other.minRange - 1) {
+ newMin = minRange;
+ newMax = other.maxRange;
+ } else if (other.maxRange == minRange - 1) {
+ newMin = other.minRange;
+ newMax = maxRange;
+ } else {
+ // 20 is missing, so if maxRange or minRange are 19 the following is 21.
+ if (maxRange == 19 && other.minRange == 21) {
+ newMin = minRange;
+ newMax = other.maxRange;
+ } else if (other.maxRange == 19 && minRange == 21) {
+ newMin = other.minRange;
+ newMax = maxRange;
+ } else {
+ throw new RuntimeException("Cannot merge ranges.");
+ }
+ }
+ }
+ return new MethodAnnotation(
+ parallelStreamMethod || other.parallelStreamMethod,
+ missingFromLatestAndroidJar || other.missingFromLatestAndroidJar,
+ covariantReturnSupported || other.covariantReturnSupported,
+ unsupportedInMinApiRange || other.unsupportedInMinApiRange,
+ newMin,
+ newMax);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedMethodsGenerator.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedMethodsGenerator.java
index c196fe9..752262f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedMethodsGenerator.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedMethodsGenerator.java
@@ -27,8 +27,8 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAmender;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedMethodsWithAnnotations.ClassAnnotation;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedMethodsWithAnnotations.MethodAnnotation;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses.ClassAnnotation;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses.MethodAnnotation;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
import com.android.tools.r8.shaking.MainDexInfo;
import com.android.tools.r8.synthesis.SyntheticItems.GlobalSyntheticsStrategy;
@@ -57,9 +57,9 @@
this.options = options;
}
- public SupportedMethodsWithAnnotations run(
- Collection<Path> desugaredLibraryImplementation, Path specification) throws IOException {
- SupportedMethodsWithAnnotations.Builder builder = SupportedMethodsWithAnnotations.builder();
+ public SupportedClasses run(Collection<Path> desugaredLibraryImplementation, Path specification)
+ throws IOException {
+ SupportedClasses.Builder builder = SupportedClasses.builder();
// First analyze everything which is supported when desugaring for api 1.
collectSupportedMethodsInB(desugaredLibraryImplementation, specification, builder);
// Second annotate all apis which are partially and/or fully supported.
@@ -77,7 +77,7 @@
}
private void annotateClasses(
- SupportedMethodsWithAnnotations.Builder builder, DirectMappedDexApplication appForMax) {
+ SupportedClasses.Builder builder, DirectMappedDexApplication appForMax) {
builder.forEachClassAndMethods(
(clazz, methods) -> {
@@ -94,9 +94,8 @@
missing.add(method.getReference());
fullySupported = false;
}
- if (builder.annotatedMethods.containsKey(method.getReference())
- && builder.annotatedMethods.get(method.getReference())
- != MethodAnnotation.getCovariantReturnSupported()) {
+ MethodAnnotation methodAnnotation = builder.getMethodAnnotation(method.getReference());
+ if (methodAnnotation != null && !methodAnnotation.isCovariantReturnSupported()) {
fullySupported = false;
}
}
@@ -105,7 +104,7 @@
}
private void annotatePartialDesugaringMethods(
- SupportedMethodsWithAnnotations.Builder builder, Path specification) throws IOException {
+ SupportedClasses.Builder builder, Path specification) throws IOException {
for (int api = AndroidApiLevel.K.getLevel();
api <= MAX_TESTED_ANDROID_API_LEVEL.getLevel();
api++) {
@@ -129,6 +128,7 @@
getMachineSpecification(androidApiLevel, specification);
options.setMinApiLevel(androidApiLevel);
+ options.resetDesugaredLibrarySpecificationForTesting();
options.setDesugaredLibrarySpecification(machineSpecification);
List<DexMethod> backports =
BackportedMethodRewriter.generateListOfBackportedMethods(
@@ -166,14 +166,14 @@
}
}
- private void annotateParallelMethods(SupportedMethodsWithAnnotations.Builder builder) {
+ private void annotateParallelMethods(SupportedClasses.Builder builder) {
for (DexMethod parallelMethod : getParallelMethods()) {
- builder.annotateMethod(parallelMethod, MethodAnnotation.getParallelStreamMethod());
+ builder.annotateMethodIfPresent(parallelMethod, MethodAnnotation.getParallelStreamMethod());
}
}
private void annotateMethodsNotOnLatestAndroidJar(
- DirectMappedDexApplication appForMax, SupportedMethodsWithAnnotations.Builder builder) {
+ DirectMappedDexApplication appForMax, SupportedClasses.Builder builder) {
builder.forEachClassAndMethod(
(clazz, method) -> {
DexClass dexClass = appForMax.definitionFor(clazz.type);
@@ -196,7 +196,7 @@
private void collectSupportedMethodsInB(
Collection<Path> desugaredLibraryImplementation,
Path specification,
- SupportedMethodsWithAnnotations.Builder builder)
+ SupportedClasses.Builder builder)
throws IOException {
AndroidApp implementation =
@@ -215,6 +215,7 @@
getMachineSpecification(AndroidApiLevel.B, specification);
options.setMinApiLevel(AndroidApiLevel.B);
+ options.resetDesugaredLibrarySpecificationForTesting();
options.setDesugaredLibrarySpecification(machineSpecification);
List<DexMethod> backports =
BackportedMethodRewriter.generateListOfBackportedMethods(
@@ -288,7 +289,7 @@
private void addBackports(
DexProgramClass clazz,
List<DexMethod> backports,
- SupportedMethodsWithAnnotations.Builder builder,
+ SupportedClasses.Builder builder,
DirectMappedDexApplication amendedAppForMax) {
for (DexMethod backport : backports) {
if (clazz.type == backport.getHolderType()) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedMethodsWithAnnotations.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedMethodsWithAnnotations.java
deleted file mode 100644
index 0cd6887..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedMethodsWithAnnotations.java
+++ /dev/null
@@ -1,215 +0,0 @@
-// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.desugar.desugaredlibrary.lint;
-
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedMember;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSortedMap;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.IdentityHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.function.BiConsumer;
-
-public class SupportedMethodsWithAnnotations {
-
- public final Map<DexClass, List<DexEncodedMethod>> supportedMethods;
- public final Map<DexMethod, MethodAnnotation> annotatedMethods;
- // A fully supported class has no annotated methods, and all the methods from the latest
- // android.jar are supported.
- public final Map<DexType, ClassAnnotation> annotatedClasses;
-
- SupportedMethodsWithAnnotations(
- Map<DexClass, List<DexEncodedMethod>> supportedMethods,
- Map<DexMethod, MethodAnnotation> annotatedMethods,
- Map<DexType, ClassAnnotation> annotatedClasses) {
- this.supportedMethods = supportedMethods;
- this.annotatedMethods = annotatedMethods;
- this.annotatedClasses = annotatedClasses;
- }
-
- static Builder builder() {
- return new Builder();
- }
-
- static class Builder {
-
- Map<DexClass, List<DexEncodedMethod>> supportedMethods = new IdentityHashMap<>();
- Map<DexMethod, MethodAnnotation> annotatedMethods = new IdentityHashMap<>();
- Map<DexType, ClassAnnotation> annotatedClasses = new IdentityHashMap<>();
-
- void forEachClassAndMethods(BiConsumer<DexClass, List<DexEncodedMethod>> biConsumer) {
- supportedMethods.forEach(biConsumer);
- }
-
- void forEachClassAndMethod(BiConsumer<DexClass, DexEncodedMethod> biConsumer) {
- supportedMethods.forEach(
- (clazz, methods) -> {
- for (DexEncodedMethod method : methods) {
- biConsumer.accept(clazz, method);
- }
- });
- }
-
- void addSupportedMethod(DexClass holder, DexEncodedMethod method) {
- List<DexEncodedMethod> methods =
- supportedMethods.computeIfAbsent(holder, f -> new ArrayList<>());
- methods.add(method);
- }
-
- void annotateClass(DexType type, ClassAnnotation annotation) {
- annotatedClasses.put(type, annotation);
- }
-
- void annotateMethod(DexMethod method, MethodAnnotation annotation) {
- MethodAnnotation prev = annotatedMethods.getOrDefault(method, MethodAnnotation.getDefault());
- annotatedMethods.put(method, annotation.combine(prev));
- }
-
- SupportedMethodsWithAnnotations build() {
- supportedMethods.forEach(
- (k, v) -> v.sort(Comparator.comparing(DexEncodedMember::getReference)));
- return new SupportedMethodsWithAnnotations(
- ImmutableSortedMap.copyOf(supportedMethods, Comparator.comparing(DexClass::getType)),
- ImmutableMap.copyOf(annotatedMethods),
- ImmutableMap.copyOf(annotatedClasses));
- }
- }
-
- static class ClassAnnotation {
-
- final boolean fullySupported;
- // Methods in latest android.jar but unsupported.
- final List<DexMethod> unsupportedMethods;
-
- public ClassAnnotation(boolean fullySupported, List<DexMethod> unsupportedMethods) {
- this.fullySupported = fullySupported;
- unsupportedMethods.sort(Comparator.naturalOrder());
- this.unsupportedMethods = ImmutableList.copyOf(unsupportedMethods);
- }
- }
-
- public static class MethodAnnotation {
-
- private static final MethodAnnotation COVARIANT_RETURN_SUPPORTED =
- new MethodAnnotation(false, false, true, false, -1, -1);
- private static final MethodAnnotation DEFAULT =
- new MethodAnnotation(false, false, false, false, -1, -1);
- private static final MethodAnnotation PARALLEL_STREAM_METHOD =
- new MethodAnnotation(true, false, false, false, -1, -1);
- private static final MethodAnnotation MISSING_FROM_LATEST_ANDROID_JAR =
- new MethodAnnotation(false, true, false, false, -1, -1);
-
- // ParallelStream methods are not supported when the runtime api level is strictly below 21.
- final boolean parallelStreamMethod;
- // Methods not in the latest android jar but still fully supported.
- final boolean missingFromLatestAndroidJar;
- // Methods not supported in a given min api range.
- final boolean unsupportedInMinApiRange;
- final boolean covariantReturnSupported;
- final int minRange;
- final int maxRange;
-
- MethodAnnotation(
- boolean parallelStreamMethod,
- boolean missingFromLatestAndroidJar,
- boolean covariantReturnSupported,
- boolean unsupportedInMinApiRange,
- int minRange,
- int maxRange) {
- this.parallelStreamMethod = parallelStreamMethod;
- this.missingFromLatestAndroidJar = missingFromLatestAndroidJar;
- this.covariantReturnSupported = covariantReturnSupported;
- this.unsupportedInMinApiRange = unsupportedInMinApiRange;
- this.minRange = minRange;
- this.maxRange = maxRange;
- }
-
- public static MethodAnnotation getCovariantReturnSupported() {
- return COVARIANT_RETURN_SUPPORTED;
- }
-
- public static MethodAnnotation getDefault() {
- return DEFAULT;
- }
-
- public static MethodAnnotation getParallelStreamMethod() {
- return PARALLEL_STREAM_METHOD;
- }
-
- public static MethodAnnotation getMissingFromLatestAndroidJar() {
- return MISSING_FROM_LATEST_ANDROID_JAR;
- }
-
- public static MethodAnnotation createMissingInMinApi(int api) {
- return new MethodAnnotation(false, false, false, true, api, api);
- }
-
- public boolean isUnsupportedInMinApiRange() {
- return unsupportedInMinApiRange;
- }
-
- public int getMinRange() {
- return minRange;
- }
-
- public int getMaxRange() {
- return maxRange;
- }
-
- public boolean isCovariantReturnSupported() {
- return covariantReturnSupported;
- }
-
- public MethodAnnotation combine(MethodAnnotation other) {
- if (this == getDefault()) {
- return other;
- }
- if (other == getDefault()) {
- return this;
- }
- int newMin, newMax;
- if (!unsupportedInMinApiRange && !other.unsupportedInMinApiRange) {
- newMin = newMax = -1;
- } else if (!unsupportedInMinApiRange || !other.unsupportedInMinApiRange) {
- newMin = unsupportedInMinApiRange ? minRange : other.minRange;
- newMax = unsupportedInMinApiRange ? maxRange : other.maxRange;
- } else {
- // Merge ranges if contiguous or throw.
- if (maxRange == other.minRange - 1) {
- newMin = minRange;
- newMax = other.maxRange;
- } else if (other.maxRange == minRange - 1) {
- newMin = other.minRange;
- newMax = maxRange;
- } else {
- // 20 is missing, so if maxRange or minRange are 19 the following is 21.
- if (maxRange == 19 && other.minRange == 21) {
- newMin = minRange;
- newMax = other.maxRange;
- } else if (other.maxRange == 19 && minRange == 21) {
- newMin = other.minRange;
- newMax = maxRange;
- } else {
- throw new RuntimeException("Cannot merge ranges.");
- }
- }
- }
- return new MethodAnnotation(
- parallelStreamMethod || other.parallelStreamMethod,
- missingFromLatestAndroidJar || other.missingFromLatestAndroidJar,
- covariantReturnSupported || other.covariantReturnSupported,
- unsupportedInMinApiRange || other.unsupportedInMinApiRange,
- newMin,
- newMax);
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 2c06cad..e7dfeee 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1012,6 +1012,11 @@
// If non-null, configuration must be passed to the consumer.
public StringConsumer configurationConsumer = null;
+ public void resetDesugaredLibrarySpecificationForTesting() {
+ loadMachineDesugaredLibrarySpecification = null;
+ machineDesugaredLibrarySpecification = MachineDesugaredLibrarySpecification.empty();
+ }
+
public void setDesugaredLibrarySpecification(DesugaredLibrarySpecification specification) {
if (specification.isEmpty()) {
return;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java
index 2b52f07..7240267 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java
@@ -13,8 +13,8 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses;
import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedMethodsGenerator;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedMethodsWithAnnotations;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableList;
@@ -118,21 +118,23 @@
@Test
public void test() throws Exception {
- SupportedMethodsWithAnnotations supportedMethods =
+ SupportedClasses supportedClasses =
new SupportedMethodsGenerator(new InternalOptions())
.run(librarySpecification.getDesugarJdkLibs(), librarySpecification.getSpecification());
for (AndroidApiLevel api : getRelevantApiLevels()) {
Set<DexMethod> localFailures = Sets.newIdentityHashSet();
- supportedMethods.annotatedMethods.forEach(
- (method, annotation) -> {
- if (annotation.isUnsupportedInMinApiRange()) {
- if (api.getLevel() >= annotation.getMinRange()
- && api.getLevel() <= annotation.getMaxRange()) {
- localFailures.add(method);
- }
- }
- });
+ supportedClasses.forEachClass(
+ supportedClass ->
+ supportedClass.forEachMethodAndAnnotation(
+ (method, annotation) -> {
+ if (annotation != null && annotation.isUnsupportedInMinApiRange()) {
+ if (api.getLevel() >= annotation.getMinRange()
+ && api.getLevel() <= annotation.getMaxRange()) {
+ localFailures.add(method.getReference());
+ }
+ }
+ }));
Set<String> expectedFailures = getExpectedFailures(api);
Set<String> apiFailuresString =
localFailures.stream().map(DexMethod::toString).collect(Collectors.toSet());