Fix fields in html and lint
- Do not mark the class as fully supported if a field is partially
supported
- Register missing fields
- Update html doc to document missing fields
- Update lint with an option to print field
Bug: b/267449090
Change-Id: I687047c60544a4653e683fb768b891ea097cd152
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 45aa53f..c62d6b1 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
@@ -507,6 +507,15 @@
.append(" Also supported with covariant return type.")
.append(HTML_SPLIT);
}
+ if (!classAnnotation.getUnsupportedFields().isEmpty()) {
+ commentBuilder
+ .append("Some fields (")
+ .append(classAnnotation.getUnsupportedFields().size())
+ .append(") present in Android ")
+ .append(MAX_TESTED_ANDROID_API_LEVEL)
+ .append(" are not supported.")
+ .append(HTML_SPLIT);
+ }
if (!classAnnotation.getUnsupportedMethods().isEmpty()) {
commentBuilder
.append("Some methods (")
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 63dec3c..5bfbe46 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
@@ -51,6 +51,9 @@
public class GenerateLintFiles extends AbstractGenerateFiles {
+ // Only recent versions of studio support the format with fields.
+ private static final boolean FORMAT_WITH_FIELD = true;
+
public static GenerateLintFiles createForTesting(
Path specification, Set<Path> implementation, Path outputDirectory) throws Exception {
return new GenerateLintFiles(specification, implementation, outputDirectory);
@@ -181,6 +184,15 @@
+ method.getReference().proto.toDescriptorString());
}
});
+ if (FORMAT_WITH_FIELD) {
+ supportedClass.forEachFieldAndAnnotation(
+ (field, fieldAnnotation) -> {
+ if (fieldAnnotation == null || !fieldAnnotation.unsupportedInMinApiRange) {
+ desugaredApisSignatures.add(
+ classBinaryName + '#' + field.getReference().name);
+ }
+ });
+ }
} else {
desugaredApisSignatures.add(classBinaryName);
}
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
index 8d01c4b..307def9 100644
--- 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
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.TriConsumer;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSortedMap;
import java.util.Collection;
@@ -121,8 +122,10 @@
this.clazz = clazz;
}
- void forEachMethods(BiConsumer<DexClass, Collection<DexEncodedMethod>> biConsumer) {
- biConsumer.accept(clazz, supportedMethods.values());
+ public void forEachFieldsAndMethods(
+ TriConsumer<DexClass, Collection<DexEncodedField>, Collection<DexEncodedMethod>>
+ triConsumer) {
+ triConsumer.accept(clazz, supportedFields.values(), supportedMethods.values());
}
void forEachMethod(BiConsumer<DexClass, DexEncodedMethod> biConsumer) {
@@ -166,10 +169,6 @@
fieldAnnotations.put(field, annotation.combine(prev));
}
- MethodAnnotation getMethodAnnotation(DexMethod method) {
- return methodAnnotations.get(method);
- }
-
SupportedClass build() {
return new SupportedClass(
clazz,
@@ -196,10 +195,12 @@
return builder.classAnnotation;
}
- void forEachClassAndMethods(BiConsumer<DexClass, Collection<DexEncodedMethod>> biConsumer) {
+ void forEachClassFieldsAndMethods(
+ TriConsumer<DexClass, Collection<DexEncodedField>, Collection<DexEncodedMethod>>
+ triConsumer) {
supportedClassBuilders
.values()
- .forEach(classBuilder -> classBuilder.forEachMethods(biConsumer));
+ .forEach(classBuilder -> classBuilder.forEachFieldsAndMethods(triConsumer));
}
void forEachClassAndMethod(BiConsumer<DexClass, DexEncodedMethod> biConsumer) {
@@ -254,10 +255,16 @@
annotateMethod(method, annotation);
}
- MethodAnnotation getMethodAnnotation(DexMethod method) {
- SupportedClass.Builder classBuilder = supportedClassBuilders.get(method.getHolderType());
+ public Map<DexField, FieldAnnotation> getFieldAnnotations(DexClass clazz) {
+ SupportedClass.Builder classBuilder = supportedClassBuilders.get(clazz.getType());
assert classBuilder != null;
- return classBuilder.getMethodAnnotation(method);
+ return classBuilder.fieldAnnotations;
+ }
+
+ Map<DexMethod, MethodAnnotation> getMethodAnnotations(DexClass clazz) {
+ SupportedClass.Builder classBuilder = supportedClassBuilders.get(clazz.getType());
+ assert classBuilder != null;
+ return classBuilder.methodAnnotations;
}
SupportedClasses build() {
@@ -274,12 +281,18 @@
private final boolean additionalMembersOnClass;
private final boolean fullySupported;
- // Methods in latest android.jar but unsupported.
+ // Members in latest android.jar but unsupported.
+ private final List<DexField> unsupportedFields;
private final List<DexMethod> unsupportedMethods;
- public ClassAnnotation(boolean fullySupported, List<DexMethod> unsupportedMethods) {
+ public ClassAnnotation(
+ boolean fullySupported,
+ List<DexField> unsupportedFields,
+ List<DexMethod> unsupportedMethods) {
this.additionalMembersOnClass = false;
this.fullySupported = fullySupported;
+ unsupportedFields.sort(Comparator.naturalOrder());
+ this.unsupportedFields = unsupportedFields;
unsupportedMethods.sort(Comparator.naturalOrder());
this.unsupportedMethods = ImmutableList.copyOf(unsupportedMethods);
}
@@ -287,6 +300,7 @@
private ClassAnnotation() {
this.additionalMembersOnClass = true;
this.fullySupported = false;
+ this.unsupportedFields = ImmutableList.of();
this.unsupportedMethods = ImmutableList.of();
}
@@ -304,6 +318,10 @@
return fullySupported;
}
+ public List<DexField> getUnsupportedFields() {
+ return unsupportedFields;
+ }
+
public List<DexMethod> getUnsupportedMethods() {
return unsupportedMethods;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClassesGenerator.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClassesGenerator.java
index f286bae..dec96bf 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClassesGenerator.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClassesGenerator.java
@@ -16,8 +16,11 @@
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMember;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMember;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
@@ -82,36 +85,45 @@
private void annotateClasses(
SupportedClasses.Builder builder, DirectMappedDexApplication appForMax) {
- builder.forEachClassAndMethods(
- (clazz, methods) -> {
+ builder.forEachClassFieldsAndMethods(
+ (clazz, fields, methods) -> {
ClassAnnotation classAnnotation = builder.getClassAnnotation(clazz.type);
if (classAnnotation != null && classAnnotation.isAdditionalMembersOnClass()) {
return;
}
DexClass maxClass = appForMax.definitionFor(clazz.type);
- List<DexMethod> missing = new ArrayList<>();
+ List<DexField> missingFields = new ArrayList<>();
+ List<DexMethod> missingMethods = new ArrayList<>();
boolean fullySupported = true;
- for (DexEncodedMethod method : maxClass.methods()) {
- if (!(method.isPublic() || method.isProtectedMethod())) {
- continue;
- }
- // If the method is in android.jar but not in the desugared library, or annotated, then
- // the class is not marked as fully supported.
- if (methods.stream().noneMatch(em -> em.getReference() == method.getReference())) {
- missing.add(method.getReference());
- fullySupported = false;
- }
+ fullySupported &= analyzeMissingMembers(maxClass.fields(), fields, missingFields);
+ fullySupported &= analyzeMissingMembers(maxClass.methods(), methods, missingMethods);
+ fullySupported &= builder.getFieldAnnotations(clazz).isEmpty();
+ for (MethodAnnotation methodAnnotation : builder.getMethodAnnotations(clazz).values()) {
+ fullySupported &= !methodAnnotation.isCovariantReturnSupported();
}
- for (DexEncodedMethod method : clazz.methods()) {
- MethodAnnotation methodAnnotation = builder.getMethodAnnotation(method.getReference());
- if (methodAnnotation != null && !methodAnnotation.isCovariantReturnSupported()) {
- fullySupported = false;
- }
- }
- builder.annotateClass(clazz.type, new ClassAnnotation(fullySupported, missing));
+ builder.annotateClass(
+ clazz.type, new ClassAnnotation(fullySupported, missingFields, missingMethods));
});
}
+ private <EM extends DexEncodedMember<EM, M>, M extends DexMember<EM, M>>
+ boolean analyzeMissingMembers(
+ Iterable<EM> maxClassMembers, Collection<EM> referenceMembers, List<M> missingMembers) {
+ boolean fullySupported = true;
+ for (EM member : maxClassMembers) {
+ if (!(member.getAccessFlags().isPublic() || member.getAccessFlags().isProtected())) {
+ continue;
+ }
+ // If the field is in android.jar but not in the desugared library, then
+ // the class is not marked as fully supported.
+ if (referenceMembers.stream().noneMatch(em -> em.getReference() == member.getReference())) {
+ missingMembers.add(member.getReference());
+ fullySupported = false;
+ }
+ }
+ return fullySupported;
+ }
+
private void annotatePartialDesugaringMembers(
SupportedClasses.Builder builder, Path specification) throws IOException {
for (int api = AndroidApiLevel.K.getLevel();