Enable Automated Shadowed API Desugaring for the API usages of android.app.admin.FreezePeriod API

#desugar #automation

- Meanwhile, shadowed/mirrored core type converter classes are written by the desugar tool like runtime library classes, instead of from pre-stocked library dependency.

PiperOrigin-RevId: 304239033
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java b/src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java
index d70d5b5..16b14f1 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java
@@ -31,6 +31,8 @@
 import com.google.common.io.Resources;
 import com.google.devtools.build.android.Converters.ExistingPathConverter;
 import com.google.devtools.build.android.Converters.PathConverter;
+import com.google.devtools.build.android.desugar.corelibadapter.InvocationSiteTransformationRecord;
+import com.google.devtools.build.android.desugar.corelibadapter.InvocationSiteTransformationRecord.InvocationSiteTransformationRecordBuilder;
 import com.google.devtools.build.android.desugar.corelibadapter.ShadowedApiAdaptersGenerator;
 import com.google.devtools.build.android.desugar.corelibadapter.ShadowedApiInvocationSite;
 import com.google.devtools.build.android.desugar.corelibadapter.ShadowedApiInvocationSite.ImmutableLabelRemover;
@@ -45,8 +47,6 @@
 import com.google.devtools.build.android.desugar.io.ThrowingClassLoader;
 import com.google.devtools.build.android.desugar.langmodel.ClassMemberUseCounter;
 import com.google.devtools.build.android.desugar.langmodel.ClassName;
-import com.google.devtools.build.android.desugar.langmodel.InvocationSiteTransformationRecord;
-import com.google.devtools.build.android.desugar.langmodel.InvocationSiteTransformationRecord.InvocationSiteTransformationRecordBuilder;
 import com.google.devtools.build.android.desugar.nest.NestAnalyzer;
 import com.google.devtools.build.android.desugar.nest.NestDesugaring;
 import com.google.devtools.build.android.desugar.nest.NestDigest;
@@ -554,14 +554,14 @@
 
       copyRuntimeClasses(outputFileProvider, coreLibrarySupport);
 
-      InvocationSiteTransformationRecord callSiteTransRecord = callSiteTransCollector.build();
-      ImmutableList<FileContentProvider<ByteArrayInputStream>> coreLibAdapters =
-          ShadowedApiAdaptersGenerator.generateAdapterClasses(callSiteTransRecord);
-
-      for (FileContentProvider<ByteArrayInputStream> fileContent : coreLibAdapters) {
+      ShadowedApiAdaptersGenerator adaptersGenerator =
+          ShadowedApiAdaptersGenerator.create(callSiteTransCollector.build());
+      for (FileContentProvider<ByteArrayInputStream> fileContent :
+          adaptersGenerator.getApiAdapters()) {
         outputFileProvider.write(
             fileContent.getBinaryPathName(), ByteStreams.toByteArray(fileContent.get()));
       }
+      copyTypeConverterClasses(outputFileProvider, adaptersGenerator.getTypeConverters());
 
       byte[] depsInfo = depsCollector.toByteArray();
       if (depsInfo != null) {
@@ -575,6 +575,18 @@
     checkState(generatedLeftBehind.isEmpty(), "Didn't process %s", generatedLeftBehind.keySet());
   }
 
+  private static void copyTypeConverterClasses(
+      OutputFileProvider outputFileProvider, ImmutableList<ClassName> converterClasses) {
+    for (ClassName className : converterClasses) {
+      String resourceName = className.classFilePathName();
+      try (InputStream stream = Resources.getResource(resourceName).openStream()) {
+        outputFileProvider.write(resourceName, ByteStreams.toByteArray(stream));
+      } catch (IOException e) {
+        throw new IOError(e);
+      }
+    }
+  }
+
   /**
    * Returns a dependency collector for use with a single input Jar. If {@link
    * DesugarOptions#emitDependencyMetadata} is set, this method instantiates the collector
@@ -706,7 +718,7 @@
         // The runtime library typically uses constructs we'd otherwise desugar, so it's easier
         // to just skip it should it appear as a regular input (for idempotency).
         if (inputFilename.endsWith(".class")
-            && ClassName.fromClassFileName(inputFilename).isDesugarEligible(options.coreLibrary)) {
+            && ClassName.fromClassFileName(inputFilename).isDesugarEligible()) {
           ClassReader reader = rewriter.reader(content);
           UnprefixingClassWriter writer = rewriter.writer(ClassWriter.COMPUTE_MAXS);
           ClassVisitor visitor =
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/InvocationSiteTransformationRecord.java b/src/tools/android/java/com/google/devtools/build/android/desugar/corelibadapter/InvocationSiteTransformationRecord.java
similarity index 61%
rename from src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/InvocationSiteTransformationRecord.java
rename to src/tools/android/java/com/google/devtools/build/android/desugar/corelibadapter/InvocationSiteTransformationRecord.java
index b95ea0d..091d6f1 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/InvocationSiteTransformationRecord.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/corelibadapter/InvocationSiteTransformationRecord.java
@@ -1,12 +1,9 @@
 /*
  * Copyright 2020 The Bazel Authors. All rights reserved.
- *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- *
  *    http://www.apache.org/licenses/LICENSE-2.0
- *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -14,16 +11,19 @@
  * limitations under the License.
  */
 
-package com.google.devtools.build.android.desugar.langmodel;
+package com.google.devtools.build.android.desugar.corelibadapter;
 
 import com.google.auto.value.AutoValue;
 import com.google.common.collect.ImmutableSet;
+import com.google.devtools.build.android.desugar.langmodel.MethodInvocationSite;
 
 /** A record that tracks the method invocation transformations. */
 @AutoValue
 public abstract class InvocationSiteTransformationRecord {
 
-  public abstract ImmutableSet<MethodInvocationSite> record();
+  abstract ImmutableSet<MethodInvocationSite> adapterReplacements();
+
+  abstract ImmutableSet<MethodInvocationSite> inlineConversions();
 
   public static InvocationSiteTransformationRecordBuilder builder() {
     return new AutoValue_InvocationSiteTransformationRecord.Builder();
@@ -33,11 +33,19 @@
   @AutoValue.Builder
   public abstract static class InvocationSiteTransformationRecordBuilder {
 
-    abstract ImmutableSet.Builder<MethodInvocationSite> recordBuilder();
+    abstract ImmutableSet.Builder<MethodInvocationSite> adapterReplacementsBuilder();
 
-    public final InvocationSiteTransformationRecordBuilder addTransformation(
+    final InvocationSiteTransformationRecordBuilder addAdapterReplacement(
         MethodInvocationSite originalMethodInvocationSite) {
-      recordBuilder().add(originalMethodInvocationSite);
+      adapterReplacementsBuilder().add(originalMethodInvocationSite);
+      return this;
+    }
+
+    abstract ImmutableSet.Builder<MethodInvocationSite> inlineConversionsBuilder();
+
+    final InvocationSiteTransformationRecordBuilder addInlineConversion(
+        MethodInvocationSite originalMethodInvocationSite) {
+      inlineConversionsBuilder().add(originalMethodInvocationSite);
       return this;
     }
 
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/corelibadapter/ShadowedApiAdaptersGenerator.java b/src/tools/android/java/com/google/devtools/build/android/desugar/corelibadapter/ShadowedApiAdaptersGenerator.java
index 732b732..66923f7 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/corelibadapter/ShadowedApiAdaptersGenerator.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/corelibadapter/ShadowedApiAdaptersGenerator.java
@@ -28,11 +28,11 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.devtools.build.android.desugar.io.FileContentProvider;
 import com.google.devtools.build.android.desugar.langmodel.ClassName;
-import com.google.devtools.build.android.desugar.langmodel.InvocationSiteTransformationRecord;
 import com.google.devtools.build.android.desugar.langmodel.MethodDeclInfo;
 import com.google.devtools.build.android.desugar.langmodel.MethodInvocationSite;
 import com.google.devtools.build.android.desugar.langmodel.MethodKey;
 import java.io.ByteArrayInputStream;
+import java.util.stream.Stream;
 import org.objectweb.asm.ClassVisitor;
 import org.objectweb.asm.ClassWriter;
 import org.objectweb.asm.MethodVisitor;
@@ -41,8 +41,8 @@
 
 /**
  * Generates type adapter classes with methods that bridge the interactions between
- * desugared-mirrored types and their desugar-shadowed built-in types, and delivers the generated
- * classes to runtime library.
+ * desugared-mirrored types and their desugar-shadowed built-in types. Together with the necessary
+ * type converter classes, the class delivers the generated adapter classes to runtime library.
  */
 public final class ShadowedApiAdaptersGenerator {
 
@@ -55,15 +55,6 @@
   /** A record with evolving map values that track adapter classes to be generated. */
   private final ImmutableMap<ClassName, ClassWriter> typeAdapters;
 
-  /** The public API that provides the file content of generate adapter classes. */
-  public static ImmutableList<FileContentProvider<ByteArrayInputStream>> generateAdapterClasses(
-      InvocationSiteTransformationRecord callSiteTransformations) {
-    return emitClassWriters(callSiteTransformations)
-        .emitAdapterMethods()
-        .closeClassWriters()
-        .provideFileContents();
-  }
-
   private ShadowedApiAdaptersGenerator(
       InvocationSiteTransformationRecord invocationAdapterSites,
       ImmutableMap<ClassName, ClassWriter> typeAdapters) {
@@ -71,11 +62,17 @@
     this.typeAdapters = typeAdapters;
   }
 
+  /** The public factory method to construct {@link ShadowedApiAdaptersGenerator}. */
+  public static ShadowedApiAdaptersGenerator create(
+      InvocationSiteTransformationRecord callSiteTransformations) {
+    return emitClassWriters(callSiteTransformations).emitAdapterMethods().closeClassWriters();
+  }
+
   private static ShadowedApiAdaptersGenerator emitClassWriters(
       InvocationSiteTransformationRecord callSiteTransformations) {
     return new ShadowedApiAdaptersGenerator(
         callSiteTransformations,
-        callSiteTransformations.record().stream()
+        callSiteTransformations.adapterReplacements().stream()
             .map(ShadowedApiAdapterHelper::getAdapterInvocationSite)
             .map(MethodInvocationSite::owner)
             .distinct()
@@ -97,8 +94,36 @@
     return cw;
   }
 
+  /** Returns desugar-shadowed API adapters with desugar-mirrored types. */
+  public ImmutableList<FileContentProvider<ByteArrayInputStream>> getApiAdapters() {
+    return typeAdapters.entrySet().stream()
+        .map(
+            e ->
+                new FileContentProvider<>(
+                    e.getKey().classFilePathName(),
+                    () -> new ByteArrayInputStream(e.getValue().toByteArray())))
+        .collect(toImmutableList());
+  }
+
+  /**
+   * Returns type conversion classes that converts between a desugar-shadowed type and its
+   * deusgar-mirrored counterpart.
+   */
+  public ImmutableList<ClassName> getTypeConverters() {
+    return Stream.concat(
+            invocationAdapterSites.inlineConversions().stream(),
+            invocationAdapterSites.adapterReplacements().stream())
+        .flatMap(
+            site ->
+                Stream.concat(Stream.of(site.returnTypeName()), site.argumentTypeNames().stream()))
+        .filter(ClassName::isDesugarShadowedType)
+        .distinct()
+        .map(ClassName::typeConverterOwner)
+        .collect(toImmutableList());
+  }
+
   private ShadowedApiAdaptersGenerator emitAdapterMethods() {
-    for (MethodInvocationSite invocationSite : invocationAdapterSites.record()) {
+    for (MethodInvocationSite invocationSite : invocationAdapterSites.adapterReplacements()) {
       MethodInvocationSite adapterSite =
           ShadowedApiAdapterHelper.getAdapterInvocationSite(invocationSite);
       ClassName adapterOwner = adapterSite.owner();
@@ -154,14 +179,4 @@
     typeAdapters.values().forEach(ClassVisitor::visitEnd);
     return this;
   }
-
-  private ImmutableList<FileContentProvider<ByteArrayInputStream>> provideFileContents() {
-    return typeAdapters.entrySet().stream()
-        .map(
-            e ->
-                new FileContentProvider<>(
-                    e.getKey().classFilePathName(),
-                    () -> new ByteArrayInputStream(e.getValue().toByteArray())))
-        .collect(toImmutableList());
-  }
 }
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/corelibadapter/ShadowedApiInvocationSite.java b/src/tools/android/java/com/google/devtools/build/android/desugar/corelibadapter/ShadowedApiInvocationSite.java
index 163cdcf..4c81fd3 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/corelibadapter/ShadowedApiInvocationSite.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/corelibadapter/ShadowedApiInvocationSite.java
@@ -25,8 +25,8 @@
 import static com.google.devtools.build.android.desugar.langmodel.ClassName.SHADOWED_TO_MIRRORED_TYPE_MAPPER;
 
 import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.android.desugar.corelibadapter.InvocationSiteTransformationRecord.InvocationSiteTransformationRecordBuilder;
 import com.google.devtools.build.android.desugar.langmodel.ClassName;
-import com.google.devtools.build.android.desugar.langmodel.InvocationSiteTransformationRecord.InvocationSiteTransformationRecordBuilder;
 import com.google.devtools.build.android.desugar.langmodel.LangModelHelper;
 import com.google.devtools.build.android.desugar.langmodel.MethodInvocationSite;
 import com.google.devtools.build.android.desugar.langmodel.MethodKey;
@@ -134,6 +134,7 @@
             SHADOWED_TO_MIRRORED_TYPE_MAPPER.map(verbatimInvocationSite.argumentTypeNames()),
             verbatimInvocationSite.argumentTypeNames(),
             INLINE_PARAM_TYPE_CONVERSION_TAG + verbatimInvocationSite.method().encode());
+        invocationSiteRecord.addInlineConversion(verbatimInvocationSite);
         verbatimInvocationSite.accept(paramInlineInstructionsContainer);
 
         MethodRemapper methodRemapper = new MethodRemapper(mv, IMMUTABLE_LABEL_ATTACHER);
@@ -145,7 +146,7 @@
         checkState(!immutableLabelApplicator.isSwitchOn());
         MethodInvocationSite adapterSite =
             ShadowedApiAdapterHelper.getAdapterInvocationSite(verbatimInvocationSite);
-        invocationSiteRecord.addTransformation(verbatimInvocationSite);
+        invocationSiteRecord.addAdapterReplacement(verbatimInvocationSite);
         adapterSite.acceptTypeMapper(IMMUTABLE_LABEL_ATTACHER).accept(mv);
         return;
       }
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassName.java b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassName.java
index 01ec25e..a7a9179 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassName.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassName.java
@@ -83,7 +83,10 @@
           .build();
 
   private static final ImmutableBiMap<String, String> SHADOWED_MIRRORED_TYPE_PREFIX_MAPPINGS =
-      ImmutableBiMap.<String, String>builder().put("javadesugar/", "jd$/").build();
+      ImmutableBiMap.<String, String>builder()
+          .put("java/", "j$/")
+          .put("javadesugar/", "jd$/")
+          .build();
   public static final TypeMapper SHADOWED_TO_MIRRORED_TYPE_MAPPER =
       new TypeMapper(ClassName::shadowedToMirrored);
   public static final TypeMapper IMMUTABLE_LABEL_ATTACHER =
@@ -273,19 +276,14 @@
     return prefixes.stream().anyMatch(this::hasPackagePrefix);
   }
 
-  public final boolean isDesugarEligible(boolean enableDesugarBuiltinJdk) {
-    if (isDesugarShadowedType()) {
-      return enableDesugarBuiltinJdk;
-    }
-    return !isInPackageEligibleForTypeAdapter()
-        && !isInDesugarRuntimeLibrary()
-        && !isDesugarMirroredType();
+  public final boolean isDesugarEligible() {
+    return !isInDesugarRuntimeLibrary();
   }
 
   public final boolean isInPackageEligibleForTypeAdapter() {
     // TODO(b/152573900): Update to hasPackagePrefix("android/") once all package-wise incremental
     // rollouts are complete.
-    return hasAnyPackagePrefix("android/testing/");
+    return hasAnyPackagePrefix("android/testing/", "android/app/admin/FreezePeriod");
   }
 
   public final boolean isInDesugarRuntimeLibrary() {
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/runtime/TimeConversions.java b/src/tools/android/java/com/google/devtools/build/android/desugar/runtime/TimeConversions.java
index 8292c2e..9040f26 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/runtime/TimeConversions.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/runtime/TimeConversions.java
@@ -27,40 +27,11 @@
     return builder.setReferenceTime(toZonedDateTime(arg));
   }
 
-  /** The factory method for the {@link android.app.admin.FreezePeriod}. */
-  @SuppressWarnings("MethodName") // synthetic method.
-  public static android.app.admin.FreezePeriod create$FreezePeriod(
-      j$.time.MonthDay jStart, j$.time.MonthDay jEnd) {
-    java.time.MonthDay start = toMonthDay(jStart);
-    java.time.MonthDay end = toMonthDay(jEnd);
-    return new android.app.admin.FreezePeriod(start, end);
-  }
-
-  public static j$.time.MonthDay getStart(android.app.admin.FreezePeriod freezePeriod) {
-    return fromMonthDay(freezePeriod.getStart());
-  }
-
-  public static j$.time.MonthDay getEnd(android.app.admin.FreezePeriod freezePeriod) {
-    return fromMonthDay(freezePeriod.getEnd());
-  }
-
   public static j$.time.ZonedDateTime getReferenceTime(
       android.view.textclassifier.TextClassification.Request request) {
     return fromZonedDateTime(request.getReferenceTime());
   }
 
-  private static j$.time.MonthDay fromMonthDay(java.time.MonthDay monthDay) {
-    return monthDay == null
-        ? null
-        : j$.time.MonthDay.of(monthDay.getMonthValue(), monthDay.getDayOfMonth());
-  }
-
-  private static java.time.MonthDay toMonthDay(j$.time.MonthDay monthDay) {
-    return monthDay == null
-        ? null
-        : java.time.MonthDay.of(monthDay.getMonthValue(), monthDay.getDayOfMonth());
-  }
-
   private static j$.time.ZonedDateTime fromZonedDateTime(java.time.ZonedDateTime dateTime) {
     if (dateTime == null) {
       return null;
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/runtime/typeadapter/java/time/LocalDateConverter.java b/src/tools/android/java/com/google/devtools/build/android/desugar/runtime/typeadapter/java/time/LocalDateConverter.java
new file mode 100644
index 0000000..cb44558
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/runtime/typeadapter/java/time/LocalDateConverter.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2020 The Bazel Authors. All rights reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package desugar.runtime.typeadapter.java.time;
+
+/** Converts types between the desugar-mirrored and desugar-shadowed {@link java.time.LocalDate}. */
+@SuppressWarnings("AndroidJdkLibsChecker")
+public abstract class LocalDateConverter {
+
+  private LocalDateConverter() {}
+
+  public static j$.time.LocalDate from(java.time.LocalDate localDate) {
+    return localDate == null
+        ? null
+        : j$.time.LocalDate.of(
+            localDate.getYear(), localDate.getMonthValue(), localDate.getDayOfMonth());
+  }
+
+  public static java.time.LocalDate to(j$.time.LocalDate localDate) {
+    return localDate == null
+        ? null
+        : java.time.LocalDate.of(
+            localDate.getYear(), localDate.getMonthValue(), localDate.getDayOfMonth());
+  }
+}
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/runtime/typeadapter/java/time/MonthDayConverter.java b/src/tools/android/java/com/google/devtools/build/android/desugar/runtime/typeadapter/java/time/MonthDayConverter.java
new file mode 100644
index 0000000..917d49a
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/runtime/typeadapter/java/time/MonthDayConverter.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2020 The Bazel Authors. All rights reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package desugar.runtime.typeadapter.java.time;
+
+/** Converts types between the desugar-mirrored and desugar-shadowed {@link java.time.MonthDay}. */
+@SuppressWarnings("AndroidJdkLibsChecker")
+public abstract class MonthDayConverter {
+
+  private MonthDayConverter() {}
+
+  public static j$.time.MonthDay from(java.time.MonthDay monthDay) {
+    return monthDay == null
+        ? null
+        : j$.time.MonthDay.of(monthDay.getMonthValue(), monthDay.getDayOfMonth());
+  }
+
+  public static java.time.MonthDay to(j$.time.MonthDay monthDay) {
+    return monthDay == null
+        ? null
+        : java.time.MonthDay.of(monthDay.getMonthValue(), monthDay.getDayOfMonth());
+  }
+}