Cherry pick: Desugared lib: Fix conversions keep rules

CL: https://r8-review.googlesource.com/c/r8/+/48000
Cherry pick hash: 890fbe2f8ca0b36d90d51f2cdb7d399dce3b67d3

Bug:148373997
Change-Id: I8f7f464bdc7ddcb6076842e40f2cf8d4283a3fec
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 62988d6..d5cf7a4 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "2.0.22";
+  public static final String LABEL = "2.0.23";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
index 7a2c325..f327f21 100644
--- a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
+++ b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
@@ -43,25 +43,28 @@
   public static class DesugaredLibraryCodeToKeep extends CodeToKeep {
 
     private static class KeepStruct {
+
       Set<DexField> fields = Sets.newConcurrentHashSet();
       Set<DexMethod> methods = Sets.newConcurrentHashSet();
       boolean all = false;
     }
 
     private final NamingLens namingLens;
-    private final Set<DexType> emulatedInterfaces = Sets.newIdentityHashSet();
+    private final Set<DexType> potentialTypesToKeep = Sets.newIdentityHashSet();
     private final Map<DexType, KeepStruct> toKeep = new ConcurrentHashMap<>();
     private final InternalOptions options;
 
     public DesugaredLibraryCodeToKeep(NamingLens namingLens, InternalOptions options) {
-      emulatedInterfaces.addAll(
-          options.desugaredLibraryConfiguration.getEmulateLibraryInterface().values());
       this.namingLens = namingLens;
       this.options = options;
+      potentialTypesToKeep.addAll(
+          options.desugaredLibraryConfiguration.getEmulateLibraryInterface().values());
+      potentialTypesToKeep.addAll(
+          options.desugaredLibraryConfiguration.getCustomConversions().values());
     }
 
     private boolean shouldKeep(DexType type) {
-      return namingLens.prefixRewrittenType(type) != null || emulatedInterfaces.contains(type);
+      return namingLens.prefixRewrittenType(type) != null || potentialTypesToKeep.contains(type);
     }
 
     @Override
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
index 61296c9..2c5e7e6 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
@@ -16,7 +16,9 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestDiagnosticMessagesImpl;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApiLevel;
@@ -30,6 +32,22 @@
 
 public class DesugaredLibraryTestBase extends TestBase {
 
+  protected static TestParametersCollection getConversionParametersFrom(AndroidApiLevel apiLevel) {
+    if (apiLevel == AndroidApiLevel.N) {
+      return getTestParameters()
+          .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
+          .withApiLevelsEndingAtExcluding(AndroidApiLevel.N)
+          .build();
+    }
+    if (apiLevel == AndroidApiLevel.O) {
+      return getTestParameters()
+          .withDexRuntimesStartingFromIncluding(Version.V8_1_0)
+          .withApiLevelsEndingAtExcluding(AndroidApiLevel.O)
+          .build();
+    }
+    throw new Error("Unsupported conversion parameters");
+  }
+
   protected boolean requiresEmulatedInterfaceCoreLibDesugaring(TestParameters parameters) {
     return parameters.getApiLevel().getLevel() < AndroidApiLevel.N.getLevel();
   }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicTimeConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicTimeConversionTest.java
index 1e66f26..6f6d2f9 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicTimeConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicTimeConversionTest.java
@@ -4,62 +4,84 @@
 
 package com.android.tools.r8.desugar.desugaredlibrary.conversiontests;
 
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static junit.framework.TestCase.assertEquals;
 import static junit.framework.TestCase.assertTrue;
-import static org.hamcrest.MatcherAssert.assertThat;
 
-import com.android.tools.r8.L8Command;
-import com.android.tools.r8.OutputMode;
-import com.android.tools.r8.StringResource;
-import com.android.tools.r8.TestDiagnosticMessagesImpl;
-import com.android.tools.r8.TestRuntime.DexRuntime;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import java.nio.file.Path;
 import java.time.ZoneId;
+import java.util.List;
 import java.util.TimeZone;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
+@RunWith(Parameterized.class)
 public class BasicTimeConversionTest extends DesugaredLibraryTestBase {
 
-  @Test
-  public void testTimeGeneratedDex() throws Exception {
-    TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
-    Path desugaredLib = temp.newFolder().toPath().resolve("conversion_dex.zip");
-    L8Command.Builder l8Builder =
-        L8Command.builder(diagnosticsHandler)
-            .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
-            .addProgramFiles(ToolHelper.DESUGAR_LIB_CONVERSIONS)
-            .addDesugaredLibraryConfiguration(
-                StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
-            .setMinApiLevel(AndroidApiLevel.B.getLevel())
-            .setOutput(desugaredLib, OutputMode.DexIndexed);
-    ToolHelper.runL8(l8Builder.build(), x -> {});
-    this.checkTimeConversionGeneratedDex(new CodeInspector(desugaredLib));
+  private final TestParameters parameters;
+  private final boolean shrinkDesugaredLibrary;
+  private static final String GMT = StringUtils.lines("GMT");
+
+  @Parameters(name = "{0}, shrinkDesugaredLibrary: {1}")
+  public static List<Object[]> data() {
+    return buildParameters(getConversionParametersFrom(AndroidApiLevel.O), BooleanUtils.values());
   }
 
-  private void checkTimeConversionGeneratedDex(CodeInspector inspector) {
-    ClassSubject clazz = inspector.clazz("j$.time.TimeConversions");
-    assertThat(clazz, isPresent());
-    assertEquals(13, clazz.allMethods().size());
+  public BasicTimeConversionTest(TestParameters parameters, boolean shrinkDesugaredLibrary) {
+    this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+    this.parameters = parameters;
   }
 
   @Test
-  public void testRewrittenAPICalls() throws Exception {
+  public void testRewrittenAPICallsD8() throws Exception {
+    KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
     testForD8()
-        .setMinApi(AndroidApiLevel.B)
+        .setMinApi(parameters.getApiLevel())
         .addInnerClasses(BasicTimeConversionTest.class)
-        .enableCoreLibraryDesugaring(AndroidApiLevel.B)
+        .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
         .compile()
+        .addDesugaredCoreLibraryRunClassPath(
+            this::buildDesugaredLibrary,
+            parameters.getApiLevel(),
+            keepRuleConsumer.get(),
+            shrinkDesugaredLibrary)
         .inspect(this::checkAPIRewritten)
-        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
-        .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class);
+        .run(parameters.getRuntime(), Executor.class)
+        .assertSuccessWithOutput(GMT);
+    if (shrinkDesugaredLibrary) {
+      checkKeepRules(keepRuleConsumer.get());
+    }
+  }
+
+  private void checkKeepRules(String keepRules) {
+    assertTrue(keepRules.contains("TimeConversion"));
+  }
+
+  @Test
+  public void testRewrittenAPICallsR8() throws Exception {
+    KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+    testForR8(parameters.getBackend())
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(Executor.class)
+        .addInnerClasses(BasicTimeConversionTest.class)
+        .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+        .compile()
+        .addDesugaredCoreLibraryRunClassPath(
+            this::buildDesugaredLibrary,
+            parameters.getApiLevel(),
+            keepRuleConsumer.get(),
+            shrinkDesugaredLibrary)
+        .run(parameters.getRuntime(), Executor.class)
+        .assertSuccessWithOutput(GMT);
+    if (shrinkDesugaredLibrary) {
+      checkKeepRules(keepRuleConsumer.get());
+    }
   }
 
   private void checkAPIRewritten(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionsPresentTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionsPresentTest.java
new file mode 100644
index 0000000..43d5b9a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionsPresentTest.java
@@ -0,0 +1,77 @@
+// Copyright (c) 2020, 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.desugar.desugaredlibrary.conversiontests;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
+
+import com.android.tools.r8.L8Command;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ConversionsPresentTest extends DesugaredLibraryTestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+  }
+
+  public ConversionsPresentTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testConversionsDex() throws Exception {
+    TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
+    Path desugaredLib = temp.newFolder().toPath().resolve("conversion_dex.zip");
+    L8Command.Builder l8Builder =
+        L8Command.builder(diagnosticsHandler)
+            .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+            .addProgramFiles(ToolHelper.DESUGAR_LIB_CONVERSIONS)
+            .addDesugaredLibraryConfiguration(
+                StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
+            .setMinApiLevel(parameters.getApiLevel().getLevel())
+            .setOutput(desugaredLib, OutputMode.DexIndexed);
+    ToolHelper.runL8(l8Builder.build(), x -> {});
+    this.checkConversionGeneratedDex(new CodeInspector(desugaredLib));
+  }
+
+  private void checkConversionGeneratedDex(CodeInspector inspector) {
+    List<FoundClassSubject> conversionsClasses =
+        inspector.allClasses().stream()
+            .filter(c -> c.getOriginalName().contains("Conversions"))
+            .collect(Collectors.toList());
+    if (parameters.getApiLevel().isLessThan(AndroidApiLevel.N)) {
+      assertEquals(5, conversionsClasses.size());
+      assertTrue(inspector.clazz("j$.util.OptionalConversions").isPresent());
+      assertTrue(inspector.clazz("j$.time.TimeConversions").isPresent());
+      assertTrue(inspector.clazz("j$.util.LongSummaryStatisticsConversions").isPresent());
+      assertTrue(inspector.clazz("j$.util.IntSummaryStatisticsConversions").isPresent());
+      assertTrue(inspector.clazz("j$.util.DoubleSummaryStatisticsConversions").isPresent());
+    } else if (parameters.getApiLevel().isLessThan(AndroidApiLevel.O)) {
+      assertEquals(1, conversionsClasses.size());
+      assertTrue(inspector.clazz("j$.time.TimeConversions").isPresent());
+    } else {
+      assertEquals(0, inspector.allClasses().size());
+    }
+  }
+}