RELNOTES: Add Desugar support for FreezePeriod#<init>

PiperOrigin-RevId: 279843528
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/CoreLibraryInvocationRewriter.java b/src/tools/android/java/com/google/devtools/build/android/desugar/CoreLibraryInvocationRewriter.java
index 7a2e27d..944d37e 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/CoreLibraryInvocationRewriter.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/CoreLibraryInvocationRewriter.java
@@ -20,6 +20,7 @@
 import org.objectweb.asm.ClassVisitor;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
 
 /**
  * Rewriter of default and static interface methods defined in some core libraries.
@@ -32,6 +33,22 @@
 
   private final CoreLibrarySupport support;
 
+  @Override
+  public void visit(
+      int version,
+      int access,
+      String name,
+      String signature,
+      String superName,
+      String[] interfaces) {
+    super.visit(version, access, name, signature, superName, interfaces);
+    if (support.getMoveTarget(superName, "<init>") != null) {
+      throw new UnsupportedOperationException(
+          String.format(
+              "Subclassing %s is unsupported: Contains a desugared factory method.", superName));
+    }
+  }
+
   public CoreLibraryInvocationRewriter(ClassVisitor cv, CoreLibrarySupport support) {
     super(Opcodes.ASM7, cv);
     this.support = support;
@@ -45,6 +62,7 @@
   }
 
   private class CoreLibraryMethodInvocationRewriter extends MethodVisitor {
+
     public CoreLibraryMethodInvocationRewriter(MethodVisitor mv) {
       super(Opcodes.ASM7, mv);
     }
@@ -53,7 +71,7 @@
     public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
       Class<?> coreInterface =
           support.getCoreInterfaceRewritingTarget(opcode, owner, name, desc, itf);
-
+      boolean replaceConstructorUseWithFactoryMethod = false;
       if (coreInterface != null) {
         String coreInterfaceName = coreInterface.getName().replace('.', '/');
         if (opcode == Opcodes.INVOKESTATIC) {
@@ -63,8 +81,12 @@
         }
 
         if (opcode == Opcodes.INVOKESTATIC || opcode == Opcodes.INVOKESPECIAL) {
-          checkArgument(itf || opcode == Opcodes.INVOKESPECIAL,
-              "Expected interface to rewrite %s.%s : %s", owner, name, desc);
+          checkArgument(
+              itf || opcode == Opcodes.INVOKESPECIAL,
+              "Expected interface to rewrite %s.%s : %s",
+              owner,
+              name,
+              desc);
           if (coreInterface.isInterface()) {
             owner = InterfaceDesugaring.getCompanionClassName(coreInterfaceName);
             name =
@@ -83,7 +105,17 @@
       } else {
         String newOwner = support.getMoveTarget(owner, name);
         if (newOwner != null) {
-          if (opcode != Opcodes.INVOKESTATIC) {
+          if ("<init>".equals(name)) {
+            // Replace the invocation of a constructor with that of a static factory method.
+            replaceConstructorUseWithFactoryMethod = true;
+            opcode = Opcodes.INVOKESTATIC;
+            // Assumes the re-mapped factory method names are in the convention create$<simplename>
+            // defined in the desugar runtime library.
+            name = "create$" + owner.substring(owner.lastIndexOf('/') + 1);
+            desc =
+                Type.getMethodType(Type.getObjectType(owner), Type.getArgumentTypes(desc))
+                    .getDescriptor();
+          } else if (opcode != Opcodes.INVOKESTATIC) {
             // assuming a static method
             desc = InterfaceDesugaring.companionDefaultMethodDescriptor(owner, desc);
             opcode = Opcodes.INVOKESTATIC;
@@ -93,6 +125,19 @@
         }
       }
       super.visitMethodInsn(opcode, owner, name, desc, itf);
+
+      // Compared with the constructor invocation, a factory method invocation pushes (areturn) the
+      // constructed object reference onto the operand stack of the invoker frame, leaving the
+      // operand stack in state {uninitialized 0, uninitialized 0, objectref}, the following
+      // instructions clears the extra values on the stack.
+      //
+      // TODO(b/144304047): Remove these extra byte code instructions once the preceding consecutive
+      // {new, dup} instructions are stripped out.
+      if (replaceConstructorUseWithFactoryMethod) {
+        super.visitInsn(Opcodes.DUP_X2);
+        super.visitInsn(Opcodes.POP);
+        super.visitInsn(Opcodes.POP2);
+      }
     }
   }
 }
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 ecd3075..3021c0d 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,6 +27,15 @@
     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());
   }
@@ -46,6 +55,12 @@
         : 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;