Check desugared library implementation when generating lint files

Bug: 171971176
Change-Id: Idaf4b089fb2bfcf0b091693455a4ad37b716a5ec
diff --git a/src/main/java/com/android/tools/r8/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
index b77db45..44a8d6e 100644
--- a/src/main/java/com/android/tools/r8/GenerateLintFiles.java
+++ b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
@@ -30,6 +30,7 @@
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.LazyLoadedDexApplication;
 import com.android.tools.r8.graph.ParameterAnnotationsList;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
 import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser;
 import com.android.tools.r8.jar.CfApplicationWriter;
@@ -44,7 +45,6 @@
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.Sets;
 import java.io.File;
-import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -56,7 +56,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.ExecutionException;
 import java.util.function.BiPredicate;
 import java.util.function.Predicate;
 
@@ -69,15 +68,21 @@
   private final InternalOptions options = new InternalOptions(factory, reporter);
 
   private final DesugaredLibraryConfiguration desugaredLibraryConfiguration;
-  private final String outputDirectory;
+  private final Path desugaredLibraryImplementation;
+  private final Path outputDirectory;
 
   private final Set<DexMethod> parallelMethods = Sets.newIdentityHashSet();
 
-  public GenerateLintFiles(String desugarConfigurationPath, String outputDirectory) {
+  public GenerateLintFiles(
+      String desugarConfigurationPath, String desugarImplementationPath, String outputDirectory)
+      throws Exception {
     this.desugaredLibraryConfiguration =
         readDesugaredLibraryConfiguration(desugarConfigurationPath);
-    this.outputDirectory =
-        outputDirectory.endsWith("/") ? outputDirectory : outputDirectory + File.separator;
+    this.desugaredLibraryImplementation = Paths.get(desugarImplementationPath);
+    this.outputDirectory = Paths.get(outputDirectory);
+    if (!Files.isDirectory(this.outputDirectory)) {
+      throw new Exception("Output directory " + outputDirectory + " is not a directory");
+    }
 
     DexType streamType = factory.createType(factory.createString("Ljava/util/stream/Stream;"));
     DexMethod parallelMethod =
@@ -201,15 +206,18 @@
   }
 
   private SupportedMethods collectSupportedMethods(
-      AndroidApiLevel compilationApiLevel, Predicate<DexEncodedMethod> supported)
-      throws IOException, ExecutionException {
+      AndroidApiLevel compilationApiLevel, Predicate<DexEncodedMethod> supported) throws Exception {
 
-    // Read the android.jar for the compilation API level.
     AndroidApp library =
         AndroidApp.builder().addLibraryFiles(getAndroidJarPath(compilationApiLevel)).build();
     DirectMappedDexApplication dexApplication =
         new ApplicationReader(library, options, Timing.empty()).read().toDirect();
 
+    AndroidApp implementation =
+        AndroidApp.builder().addProgramFiles(desugaredLibraryImplementation).build();
+    DirectMappedDexApplication implementationApplication =
+        new ApplicationReader(implementation, options, Timing.empty()).read().toDirect();
+
     // Collect all the methods that the library desugar configuration adds support for.
     Set<DexClass> classesWithAllMethodsSupported = Sets.newIdentityHashSet();
     Map<DexClass, List<DexEncodedMethod>> supportedMethods = new LinkedHashMap<>();
@@ -218,9 +226,20 @@
       // All the methods with the rewritten prefix are supported.
       for (String prefix : desugaredLibraryConfiguration.getRewritePrefix().keySet()) {
         if (clazz.accessFlags.isPublic() && className.startsWith(prefix)) {
+          DexProgramClass implementationClass =
+              implementationApplication.programDefinitionFor(clazz.getType());
+          if (implementationClass == null) {
+            throw new Exception("Implementation class not found for " + clazz.toSourceString());
+          }
           boolean allMethodsAddad = true;
           for (DexEncodedMethod method : clazz.methods()) {
-            if (supported.test(method)) {
+            if (!method.isPublic()) {
+              continue;
+            }
+            ProgramMethod implementationMethod =
+                implementationClass.lookupProgramMethod(method.method);
+            // Don't include methods which are not implemented by the desugared library.
+            if (supported.test(method) && implementationMethod != null) {
               supportedMethods.computeIfAbsent(clazz, k -> new ArrayList<>()).add(method);
             } else {
               allMethodsAddad = false;
@@ -274,8 +293,7 @@
   private Path lintFile(
       AndroidApiLevel compilationApiLevel, AndroidApiLevel minApiLevel, String extension)
       throws Exception {
-    Path directory =
-        Paths.get(outputDirectory + "compile_api_level_" + compilationApiLevel.getLevel());
+    Path directory = outputDirectory.resolve("compile_api_level_" + compilationApiLevel.getLevel());
     Files.createDirectories(directory);
     return Paths.get(
         directory
@@ -299,6 +317,10 @@
               DescriptorUtils.getClassBinaryNameFromDescriptor(clazz.type.descriptor.toString());
           if (!supportedMethods.classesWithAllMethodsSupported.contains(clazz)) {
             for (DexEncodedMethod method : methods) {
+              if (method.isInstanceInitializer() || method.isClassInitializer()) {
+                // No new constructors are added.
+                continue;
+              }
               desugaredApisSignatures.add(
                   classBinaryName
                       + '#'
@@ -379,10 +401,12 @@
   }
 
   public static void main(String[] args) throws Exception {
-    if (args.length != 2) {
-      System.out.println("Usage: GenerateLineFiles <desuage configuration> <output directory>");
+    if (args.length != 3) {
+      System.out.println(
+          "Usage: GenerateLineFiles <desugar configuration> <desugar implementation> <output"
+              + " directory>");
       System.exit(1);
     }
-    new GenerateLintFiles(args[0], args[1]).run();
+    new GenerateLintFiles(args[0], args[1], args[2]).run();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
index 2225ceb..d5e8946 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
@@ -271,7 +271,10 @@
   private CodeInspector getDesugaredApiJar() throws Exception {
     Path out = temp.newFolder().toPath();
     GenerateLintFiles desugaredApi =
-        new GenerateLintFiles(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString(), out.toString());
+        new GenerateLintFiles(
+            ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString(),
+            ToolHelper.DESUGAR_JDK_LIBS,
+            out.toString());
     desugaredApi.run(targetApi.getLevel());
     return new CodeInspector(
         out.resolve("compile_api_level_" + targetApi.getLevel())
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
index b726614..43f53ab 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
@@ -37,6 +37,9 @@
     assertTrue(methods.contains("java/util/Optional"));
     assertTrue(methods.contains("java/util/OptionalInt"));
 
+    // ConcurrentHashMap is not fully supported.
+    assertFalse(methods.contains("java/util/concurrent/ConcurrentHashMap"));
+
     // No parallel* methods pre L, and all stream methods supported from L.
     assertEquals(
         minApiLevel == AndroidApiLevel.L,
@@ -62,6 +65,22 @@
         methods.contains(
             "java/util/stream/IntStream#allMatch(Ljava/util/function/IntPredicate;)Z"));
 
+    // Supported methods on ConcurrentHashMap.
+    assertTrue(
+        methods.contains(
+            "java/util/concurrent/ConcurrentHashMap#getOrDefault(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"));
+
+    // Don't include constructors.
+    assertFalse(methods.contains("java/util/concurrent/ConcurrentHashMap#<init>()V"));
+
+    // Unsupported methods on ConcurrentHashMap.
+    assertFalse(
+        methods.contains(
+            "java/util/concurrent/ConcurrentHashMap#reduce(JLjava/util/function/BiFunction;Ljava/util/function/BiFunction;)Ljava/lang/Object;"));
+    assertFalse(
+        methods.contains(
+            "java/util/concurrent/ConcurrentHashMap#newKeySet()Ljava/util/concurrent/ConcurrentHashMap$KeySetView;"));
+
     // Emulated interface default method.
     assertTrue(methods.contains("java/util/List#spliterator()Ljava/util/Spliterator;"));
 
@@ -85,7 +104,11 @@
 
     Path directory = temp.newFolder().toPath();
     GenerateLintFiles.main(
-        new String[] {ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString(), directory.toString()});
+        new String[] {
+          ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString(),
+          ToolHelper.DESUGAR_JDK_LIBS,
+          directory.toString()
+        });
     InternalOptions options = new InternalOptions(new DexItemFactory(), new Reporter());
     DesugaredLibraryConfiguration desugaredLibraryConfiguration =
         new DesugaredLibraryConfigurationParser(
diff --git a/tools/utils.py b/tools/utils.py
index 3aad6c4..96708d2 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -56,6 +56,8 @@
 
 DESUGAR_CONFIGURATION = os.path.join(
       'src', 'library_desugar', 'desugar_jdk_libs.json')
+DESUGAR_IMPLEMENTATION = os.path.join(
+      'third_party', 'openjdk', 'desugar_jdk_libs', 'libjava.jar')
 DESUGAR_CONFIGURATION_MAVEN_ZIP = os.path.join(
   LIBS, 'desugar_jdk_libs_configuration.zip')
 GENERATED_LICENSE = os.path.join(GENERATED_LICENSE_DIR, 'LICENSE')