Lint: stop relying on multiple android.jar
Change-Id: Ib184a2620e9c7c37b46c66f3bff8f5a1f6e0eb3e
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java
index fb97359..1b732da 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java
@@ -47,6 +47,9 @@
return computeApiLevelForLibraryReference(reference, ComputedApiLevel.unknown());
}
+ public abstract ComputedApiLevel computeApiLevelForLibraryReferenceIgnoringDesugaredLibrary(
+ DexReference reference, ComputedApiLevel unknownValue);
+
public ComputedApiLevel computeApiLevelForDefinition(Iterable<DexType> types) {
return computeApiLevelForDefinition(types, ComputedApiLevel.unknown());
}
@@ -102,6 +105,12 @@
}
@Override
+ public ComputedApiLevel computeApiLevelForLibraryReferenceIgnoringDesugaredLibrary(
+ DexReference reference, ComputedApiLevel unknownValue) {
+ return ComputedApiLevel.notSet();
+ }
+
+ @Override
public ComputedApiLevel computeInitialMinApiLevel(InternalOptions options) {
return ComputedApiLevel.notSet();
}
@@ -141,6 +150,12 @@
}
@Override
+ public ComputedApiLevel computeApiLevelForLibraryReferenceIgnoringDesugaredLibrary(
+ DexReference reference, ComputedApiLevel unknownValue) {
+ return cache.lookupIgnoringDesugaredLibrary(reference, unknownValue);
+ }
+
+ @Override
public void reportUnknownApiReferences() {
cache
.getUnknownReferencesToReport()
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
index 0e267e5..627063c 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
@@ -67,12 +67,22 @@
}
public ComputedApiLevel lookup(DexReference reference, ComputedApiLevel unknownValue) {
+ return lookup(reference, unknownValue, false);
+ }
+
+ public ComputedApiLevel lookupIgnoringDesugaredLibrary(
+ DexReference reference, ComputedApiLevel unknownValue) {
+ return lookup(reference, unknownValue, true);
+ }
+
+ private ComputedApiLevel lookup(
+ DexReference reference, ComputedApiLevel unknownValue, boolean ignoringDesugaredLibrary) {
DexType contextType = reference.getContextType();
if (contextType.isArrayType()) {
if (reference.isDexMethod() && reference.asDexMethod().match(factory.objectMembers.clone)) {
return appView.computedMinApiLevel();
}
- return lookup(contextType.toBaseType(factory), unknownValue);
+ return lookup(contextType.toBaseType(factory), unknownValue, ignoringDesugaredLibrary);
}
if (contextType.isPrimitiveType() || contextType.isVoidType()) {
return appView.computedMinApiLevel();
@@ -87,10 +97,11 @@
if (reference.getContextType() == factory.objectType) {
return appView.computedMinApiLevel();
}
- if (appView
- .options()
- .machineDesugaredLibrarySpecification
- .isContextTypeMaintainedOrRewritten(reference)) {
+ if (!ignoringDesugaredLibrary
+ && appView
+ .options()
+ .machineDesugaredLibrarySpecification
+ .isContextTypeMaintainedOrRewritten(reference)) {
// If we end up desugaring the reference, the library classes is bridged by j$ which is part
// of the program.
return appView.computedMinApiLevel();
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 b5edd71..e1e72d8 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
@@ -263,9 +263,15 @@
SupportedClasses supportedMethods =
new SupportedClassesGenerator(options, androidJar)
.run(desugaredLibraryImplementation, desugaredLibrarySpecificationPath);
- System.out.println("Generating lint files for compile API " + compilationLevel);
+ System.out.println(
+ "Generating lint files for "
+ + desugaredLibrarySpecification.getIdentifier()
+ + " (compile API "
+ + compilationLevel
+ + ")");
generateLintFiles(compilationLevel, AndroidApiLevel.B, supportedMethods);
generateLintFiles(compilationLevel, AndroidApiLevel.L, supportedMethods);
+ System.out.println();
return compilationLevel;
}
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 ccbcfed..f384fd7 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
@@ -10,8 +10,9 @@
import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.features.ClassToFeatureSplitMap;
+import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
@@ -28,14 +29,12 @@
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
-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.SupportedClasses.ClassAnnotation;
import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses.FieldAnnotation;
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;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
@@ -46,7 +45,6 @@
import com.google.common.collect.Sets;
import java.io.IOException;
import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -55,36 +53,28 @@
public class SupportedClassesGenerator {
- private static final String ANDROID_JAR_PATTERN = "third_party/android_jar/lib-v%d/android.jar";
-
private final InternalOptions options;
- private final Path androidJar;
+ private final DirectMappedDexApplication appForMax;
+ private final SupportedClasses.Builder builder = SupportedClasses.builder();
- private DexApplication loadingAppCache;
-
- public SupportedClassesGenerator(InternalOptions options, Path androidJar) {
+ public SupportedClassesGenerator(InternalOptions options, Path androidJar) throws IOException {
this.options = options;
- this.androidJar = androidJar;
+ this.appForMax = createAppForMax(androidJar);
}
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.
- collectSupportedMembersInB(desugaredLibraryImplementation, specification, builder);
+ collectSupportedMembersInB(desugaredLibraryImplementation, specification);
// Second annotate all apis which are partially and/or fully supported.
- AndroidApp library = AndroidApp.builder().addProgramFiles(androidJar).build();
- DirectMappedDexApplication appForMax =
- new ApplicationReader(library, options, Timing.empty()).read().toDirect();
- annotateMethodsNotOnLatestAndroidJar(appForMax, builder);
- annotateParallelMethods(builder);
- annotatePartialDesugaringMembers(builder, specification);
- annotateClasses(builder, appForMax);
+ annotateMethodsNotOnLatestAndroidJar();
+ annotateParallelMethods();
+ annotatePartialDesugaringMembers(specification);
+ annotateClasses();
return builder.build();
}
- private void annotateClasses(
- SupportedClasses.Builder builder, DirectMappedDexApplication appForMax) {
+ private void annotateClasses() {
builder.forEachClassFieldsAndMethods(
(clazz, fields, methods) -> {
ClassAnnotation classAnnotation = builder.getClassAnnotation(clazz.type);
@@ -124,34 +114,27 @@
return fullySupported;
}
- private void annotatePartialDesugaringMembers(
- SupportedClasses.Builder builder, Path specification) throws IOException {
- for (int api = AndroidApiLevel.K.getLevel();
+ private void annotatePartialDesugaringMembers(Path specification) throws IOException {
+ // The first difference should be at 18 so we're safe starting at J and not B.
+ for (int api = AndroidApiLevel.J.getLevel();
api <= MAX_TESTED_ANDROID_API_LEVEL.getLevel();
api++) {
- if (api == 20) {
- // Missing android.jar.
- continue;
- }
AndroidApiLevel androidApiLevel = AndroidApiLevel.getAndroidApiLevel(api);
MachineDesugaredLibrarySpecification machineSpecification =
getMachineSpecification(androidApiLevel, specification);
options.setMinApiLevel(androidApiLevel);
options.resetDesugaredLibrarySpecificationForTesting();
options.setDesugaredLibrarySpecification(machineSpecification);
- AndroidApp library =
- AndroidApp.builder().addProgramFiles(getAndroidJarPath(androidApiLevel)).build();
- DirectMappedDexApplication dexApplication =
- new ApplicationReader(library, options, Timing.empty()).read().toDirect();
- AppInfoWithClassHierarchy appInfo =
- AppInfoWithClassHierarchy.createInitialAppInfoWithClassHierarchy(
- dexApplication,
- ClassToFeatureSplitMap.createEmptyClassToFeatureSplitMap(),
- MainDexInfo.none(),
- GlobalSyntheticsStrategy.forNonSynthesizing());
+ AppInfo initialAppInfo =
+ AppInfo.createInitialAppInfo(appForMax, GlobalSyntheticsStrategy.forNonSynthesizing());
+ AppView<?> appView =
+ AppView.createForD8(initialAppInfo, options.getTypeRewriter(), Timing.empty());
+ AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring();
+
+ // This should depend only on machine specification and min api.
List<DexMethod> backports =
- BackportedMethodRewriter.generateListOfBackportedMethods(dexApplication, options);
+ BackportedMethodRewriter.generateListOfBackportedMethods(appForMax, options);
int finalApi = api;
builder.forEachClassAndMethod(
@@ -171,13 +154,31 @@
// interface is supported.
return;
}
+ // This is not supported through desugared library. We look-up to see if it is
+ // supported at the given min-api level by the library.
MethodResolutionResult methodResolutionResult =
appInfo.resolveMethod(
dexMethod,
appInfo
.contextIndependentDefinitionFor(dexMethod.getHolderType())
.isInterface());
- if (methodResolutionResult.isFailedResolution()) {
+ if (methodResolutionResult.isSingleResolution()) {
+ ComputedApiLevel computedApiLevel =
+ appView
+ .apiLevelCompute()
+ .computeApiLevelForLibraryReferenceIgnoringDesugaredLibrary(
+ methodResolutionResult.getResolvedMethod().getReference(),
+ ComputedApiLevel.unknown());
+ if (!computedApiLevel.isKnownApiLevel()) {
+ throw new RuntimeException(
+ "API database does not recognize the method "
+ + encodedMethod.getReference().toSourceString());
+ }
+ if (finalApi < computedApiLevel.asKnownApiLevel().getApiLevel().getLevel()) {
+ builder.annotateMethod(dexMethod, MethodAnnotation.createMissingInMinApi(finalApi));
+ }
+ } else {
+ assert methodResolutionResult.isFailedResolution();
builder.annotateMethod(dexMethod, MethodAnnotation.createMissingInMinApi(finalApi));
}
});
@@ -193,7 +194,24 @@
}
FieldResolutionResult fieldResolutionResult =
appInfo.resolveField(encodedField.getReference());
- if (fieldResolutionResult.isFailedResolution()) {
+ if (fieldResolutionResult.isSingleFieldResolutionResult()) {
+ DexEncodedField resolvedField = fieldResolutionResult.getResolvedField();
+ ComputedApiLevel computedApiLevel =
+ appView
+ .apiLevelCompute()
+ .computeApiLevelForLibraryReferenceIgnoringDesugaredLibrary(
+ resolvedField.getReference(), ComputedApiLevel.unknown());
+ if (!computedApiLevel.isKnownApiLevel()) {
+ throw new RuntimeException(
+ "API database does not recognize the field "
+ + encodedField.getReference().toSourceString());
+ }
+ if (finalApi < computedApiLevel.asKnownApiLevel().getApiLevel().getLevel()) {
+ builder.annotateField(
+ encodedField.getReference(), FieldAnnotation.createMissingInMinApi(finalApi));
+ }
+ } else {
+ assert fieldResolutionResult.isFailedResolution();
builder.annotateField(
encodedField.getReference(), FieldAnnotation.createMissingInMinApi(finalApi));
}
@@ -201,14 +219,13 @@
}
}
- private void annotateParallelMethods(SupportedClasses.Builder builder) {
+ private void annotateParallelMethods() {
for (DexMethod parallelMethod : getParallelMethods()) {
builder.annotateMethodIfPresent(parallelMethod, MethodAnnotation.getParallelStreamMethod());
}
}
- private void annotateMethodsNotOnLatestAndroidJar(
- DirectMappedDexApplication appForMax, SupportedClasses.Builder builder) {
+ private void annotateMethodsNotOnLatestAndroidJar() {
builder.forEachClassAndMethod(
(clazz, method) -> {
DexClass dexClass = appForMax.definitionFor(clazz.type);
@@ -220,19 +237,8 @@
});
}
- static Path getAndroidJarPath(AndroidApiLevel apiLevel) {
- String jar =
- apiLevel == AndroidApiLevel.MASTER
- ? "third_party/android_jar/lib-master/android.jar"
- : String.format(ANDROID_JAR_PATTERN, apiLevel.getLevel());
- return Paths.get(jar);
- }
-
private void collectSupportedMembersInB(
- Collection<Path> desugaredLibraryImplementation,
- Path specification,
- SupportedClasses.Builder builder)
- throws IOException {
+ Collection<Path> desugaredLibraryImplementation, Path specification) throws IOException {
MachineDesugaredLibrarySpecification machineSpecification =
getMachineSpecification(AndroidApiLevel.B, specification);
@@ -246,19 +252,8 @@
DirectMappedDexApplication implementationApplication =
new ApplicationReader(implementation, options, Timing.empty()).read().toDirect();
- AndroidApp library = AndroidApp.builder().addLibraryFiles(androidJar).build();
- DirectMappedDexApplication amendedAppForMax =
- new ApplicationReader(library, options, Timing.empty()).read().toDirect();
-
List<DexMethod> backports =
- BackportedMethodRewriter.generateListOfBackportedMethods(amendedAppForMax, options);
-
- DesugaredLibraryAmender.run(
- machineSpecification.getAmendLibraryMethods(),
- machineSpecification.getAmendLibraryFields(),
- amendedAppForMax,
- options.reporter,
- ComputedApiLevel.unknown());
+ BackportedMethodRewriter.generateListOfBackportedMethods(appForMax, options);
for (DexProgramClass clazz : implementationApplication.classes()) {
// All emulated interfaces static and default methods are supported.
@@ -283,20 +278,20 @@
}
builder.addSupportedMethod(clazz, method);
}
- addBackports(clazz, backports, builder, amendedAppForMax);
+ addBackports(clazz, backports);
builder.annotateClass(clazz.type, ClassAnnotation.getAdditionnalMembersOnClass());
} else {
// All methods in maintained or rewritten classes are supported.
if ((clazz.accessFlags.isPublic() || clazz.accessFlags.isProtected())
&& machineSpecification.isContextTypeMaintainedOrRewritten(clazz.type)
- && amendedAppForMax.definitionFor(clazz.type) != null) {
+ && appForMax.definitionFor(clazz.type) != null) {
for (DexEncodedMethod method : clazz.methods()) {
if (!method.isPublic() && !method.isProtectedMethod()) {
continue;
}
builder.addSupportedMethod(clazz, method);
}
- addBackports(clazz, backports, builder, amendedAppForMax);
+ addBackports(clazz, backports);
for (DexEncodedField field : clazz.fields()) {
if (!field.isPublic() && !field.isProtected()) {
continue;
@@ -319,7 +314,7 @@
return;
}
}
- dexClass = amendedAppForMax.definitionFor(method.getHolderType());
+ dexClass = appForMax.definitionFor(method.getHolderType());
DexEncodedMethod dexEncodedMethod = dexClass.lookupMethod(method);
assert dexEncodedMethod != null;
builder.addSupportedMethod(dexClass, dexEncodedMethod);
@@ -340,7 +335,7 @@
return;
}
}
- dexClass = amendedAppForMax.definitionFor(field.getHolderType());
+ dexClass = appForMax.definitionFor(field.getHolderType());
DexEncodedField dexEncodedField = dexClass.lookupField(field);
assert dexEncodedField != null;
builder.addSupportedField(dexClass, dexEncodedField);
@@ -348,14 +343,10 @@
});
}
- private void addBackports(
- DexProgramClass clazz,
- List<DexMethod> backports,
- SupportedClasses.Builder builder,
- DirectMappedDexApplication amendedAppForMax) {
+ private void addBackports(DexProgramClass clazz, List<DexMethod> backports) {
for (DexMethod backport : backports) {
if (clazz.type == backport.getHolderType()) {
- DexClass maxClass = amendedAppForMax.definitionFor(clazz.type);
+ DexClass maxClass = appForMax.definitionFor(clazz.type);
DexEncodedMethod dexEncodedMethod = maxClass.lookupMethod(backport);
// Some backports are not in amendedAppForMax, such as Stream#ofNullable and recent ones
// introduced in U.
@@ -400,28 +391,19 @@
options.reporter,
false,
api.getLevel());
- DexApplication app = getLoadingApp(androidJar, options);
- return librarySpecification.toMachineSpecification(app, Timing.empty());
+ return librarySpecification.toMachineSpecification(appForMax, Timing.empty());
}
- private DexApplication getLoadingApp(Path androidJar, InternalOptions options)
- throws IOException {
- return loadingAppCache == null
- ? loadingAppCache = createLoadingApp(androidJar, options)
- : loadingAppCache;
- }
-
- private DexApplication createLoadingApp(Path androidJar, InternalOptions options)
- throws IOException {
+ private DirectMappedDexApplication createAppForMax(Path androidJar) throws IOException {
AndroidApp.Builder builder = AndroidApp.builder();
AndroidApp inputApp = builder.addLibraryFiles(androidJar).build();
ApplicationReader applicationReader = new ApplicationReader(inputApp, options, Timing.empty());
ExecutorService executorService = ThreadUtils.getExecutorService(options);
assert !options.ignoreJavaLibraryOverride;
options.ignoreJavaLibraryOverride = true;
- DexApplication loadingApp = applicationReader.read(executorService);
+ DexApplication appForMax = applicationReader.read(executorService);
options.ignoreJavaLibraryOverride = false;
- return loadingApp;
+ return appForMax.toDirect();
}
private Set<DexMethod> getParallelMethods() {
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 4ad1523..eebc725 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
@@ -216,10 +216,6 @@
if (librarySpecification == JDK8 && api.isGreaterThanOrEqualTo(AndroidApiLevel.O)) {
expectedFailures.addAll(FAILURES_ERA);
}
- if (jdk11NonMinimal && api.isGreaterThanOrEqualTo(AndroidApiLevel.T)) {
- // The method is present, but not in android.jar...
- expectedFailures.addAll(FAILURES_ERA);
- }
return expectedFailures;
}
}