Merge commit 'a7b4f924' into dev-release

Change-Id: I9bcbe908dc03b3ecc28e2a5c575d59809cbd1ffd
diff --git a/.gitignore b/.gitignore
index e310747..d3eb304 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,7 +2,6 @@
 !third_party/gmscore/*.sha1
 !third_party/internal/*.sha1
 !third_party/nest/*.sha1
-!third_party/proguard/*.sha1
 !third_party/youtube/*sha1
 #*#
 *.dex
@@ -290,6 +289,7 @@
 third_party/opensource-apps/android/nowinandroid
 third_party/opensource-apps/android/nowinandroid.tar.gz
 third_party/proguard/*
+!third_party/proguard/*.sha1
 third_party/proguardsettings.tar.gz
 third_party/proguardsettings/
 third_party/proto/runtime/edition2023
diff --git a/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt b/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
index 17f6c74..000ca2b 100644
--- a/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
+++ b/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
@@ -1089,7 +1089,7 @@
 }
 
 fun getThirdPartyProguards(): List<ThirdPartyDependency> {
-  return listOf("proguard-7.0.0", "proguard-7.3.2", "proguard-7.4.1").map {
+  return listOf("proguard-7.0.0", "proguard-7.7.0").map {
     ThirdPartyDependency(
       it,
       Paths.get("third_party", "proguard", it).toFile(),
diff --git a/infra/config/global/generated/cr-buildbucket.cfg b/infra/config/global/generated/cr-buildbucket.cfg
index 72dfcc9..3a60dff 100644
--- a/infra/config/global/generated/cr-buildbucket.cfg
+++ b/infra/config/global/generated/cr-buildbucket.cfg
@@ -779,7 +779,7 @@
       swarming_tags: "vpython:native-python-wrapper"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-20.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
@@ -817,7 +817,7 @@
       swarming_tags: "vpython:native-python-wrapper"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-20.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
diff --git a/infra/config/global/main.star b/infra/config/global/main.star
index 4b51ba9..862f835 100755
--- a/infra/config/global/main.star
+++ b/infra/config/global/main.star
@@ -404,7 +404,7 @@
 r8_tester_with_default(
     "linux-android-5",
     ["--dex_vm=5.1.1", "--all_tests", "--command_cache_dir=/tmp/ccache"],
-    dimensions = get_dimensions(noble=False),
+    dimensions = get_dimensions(noble=True),
 )
 
 r8_tester_with_default(
diff --git a/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessField.kt b/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessField.kt
index b02671c..b453d72 100644
--- a/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessField.kt
+++ b/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessField.kt
@@ -36,8 +36,8 @@
  * @see UsesReflectionToConstruct
  * @see UsesReflectionToAccessMethod
  */
-@Repeatable
 @Retention(AnnotationRetention.BINARY)
+@Repeatable
 @Target(
     AnnotationTarget.CLASS,
     AnnotationTarget.FIELD,
@@ -51,14 +51,14 @@
      *
      * Mutually exclusive with [className].
      */
-    val classConstant: KClass<*> = Unspecified::class,
+    @Suppress("KotlinDefaultParameterOrder") val classConstant: KClass<*> = Unspecified::class,
 
     /**
      * Class name (or class name pattern) containing the field accessed by reflection.
      *
      * Mutually exclusive with [classConstant].
      */
-    val className: String = "",
+    @Suppress("KotlinDefaultParameterOrder") val className: String = "",
 
     /** Name (or name pattern) of field accessed by reflection. */
     val fieldName: String,
diff --git a/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessMethod.kt b/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessMethod.kt
index 60b4e71..ddaefbf 100644
--- a/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessMethod.kt
+++ b/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessMethod.kt
@@ -36,8 +36,8 @@
  * @see UsesReflectionToConstruct
  * @see UsesReflectionToAccessField
  */
-@Repeatable
 @Retention(AnnotationRetention.BINARY)
+@Repeatable
 @Target(
     AnnotationTarget.CLASS,
     AnnotationTarget.FIELD,
@@ -51,14 +51,14 @@
      *
      * Mutually exclusive with [className].
      */
-    val classConstant: KClass<*> = Unspecified::class,
+    @Suppress("KotlinDefaultParameterOrder") val classConstant: KClass<*> = Unspecified::class,
 
     /**
      * Class name (or class name pattern) containing the method accessed by reflection.
      *
      * Mutually exclusive with [classConstant].
      */
-    val className: String = "",
+    @Suppress("KotlinDefaultParameterOrder") val className: String = "",
 
     /** Name (or name pattern) of method accessed by reflection. */
     val methodName: String,
@@ -66,22 +66,22 @@
     /**
      * Defines which method to keep by specifying set of parameter classes passed.
      *
-     * If neither `param` nor `paramTypeNames` is specified then methods with all parameter lists
-     * are kept.
+     * If neither `parameterTypes` nor `parameterTypeNames` is specified then methods with all
+     * parameter lists are kept.
      *
-     * Mutually exclusive with [paramTypeNames].
+     * Mutually exclusive with [parameterTypeNames].
      */
-    val params: Array<KClass<*>> = [Unspecified::class],
+    val parameterTypes: Array<KClass<*>> = [Unspecified::class],
 
     /**
      * Defines which method to keep by specifying set of parameter classes passed.
      *
-     * If neither `param` nor `paramTypeNames` is specified then methods with all parameter lists
-     * are kept.
+     * If neither `parameterTypes` nor `parameterTypeNames` is specified then methods with all
+     * parameter lists are kept.
      *
-     * Mutually exclusive with [params].
+     * Mutually exclusive with [parameterTypes].
      */
-    val paramTypeNames: Array<String> = [""],
+    val parameterTypeNames: Array<String> = [""],
 
     /**
      * Return type of the method accessed by reflection.
diff --git a/src/keepanno/java/androidx/annotation/keep/UsesReflectionToConstruct.kt b/src/keepanno/java/androidx/annotation/keep/UsesReflectionToConstruct.kt
index 75f4d07..8aabcb5 100644
--- a/src/keepanno/java/androidx/annotation/keep/UsesReflectionToConstruct.kt
+++ b/src/keepanno/java/androidx/annotation/keep/UsesReflectionToConstruct.kt
@@ -39,8 +39,8 @@
  * @see UsesReflectionToAccessMethod
  * @see UsesReflectionToAccessField
  */
-@Repeatable
 @Retention(AnnotationRetention.BINARY)
+@Repeatable
 @Target(
     AnnotationTarget.CLASS,
     AnnotationTarget.FIELD,
@@ -66,20 +66,20 @@
     /**
      * Defines which constructor to keep by specifying the parameter list types.
      *
-     * If neither `param` nor `paramTypeNames` is specified then constructors with all parameter
-     * lists are kept.
+     * If neither `parameterTypes` nor `parameterTypeNames` is specified then constructors with all
+     * parameter lists are kept.
      *
-     * Mutually exclusive with [paramTypeNames].
+     * Mutually exclusive with [parameterTypeNames].
      */
-    val params: Array<KClass<*>> = [Unspecified::class],
+    val parameterTypes: Array<KClass<*>> = [Unspecified::class],
 
     /**
      * Defines which constructor to keep by specifying the parameter list types.
      *
-     * If neither `param` nor `paramTypeNames` is specified then constructors with all parameter
-     * lists are kept.
+     * If neither `parameterTypes` nor `parameterTypeNames` is specified then constructors with all
+     * parameter lists are kept.
      *
-     * Mutually exclusive with [params].
+     * Mutually exclusive with [parameterTypes].
      */
-    val paramTypeNames: Array<String> = [""],
+    val parameterTypeNames: Array<String> = [""],
 )
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
index d251880..6b0c939 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
@@ -34,6 +34,7 @@
 import com.android.tools.r8.keepanno.ast.KeepCondition;
 import com.android.tools.r8.keepanno.ast.KeepConsequences;
 import com.android.tools.r8.keepanno.ast.KeepConstraint;
+import com.android.tools.r8.keepanno.ast.KeepConstraint.Annotation;
 import com.android.tools.r8.keepanno.ast.KeepConstraints;
 import com.android.tools.r8.keepanno.ast.KeepDeclaration;
 import com.android.tools.r8.keepanno.ast.KeepEdge;
@@ -67,6 +68,7 @@
 import com.android.tools.r8.keepanno.ast.ParsingContext.MethodParsingContext;
 import com.android.tools.r8.keepanno.ast.ParsingContext.PropertyParsingContext;
 import com.google.common.collect.ImmutableList;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -109,7 +111,9 @@
         || AnnotationConstants.UsedByReflection.isDescriptor(descriptor)
         || AnnotationConstants.UsedByNative.isDescriptor(descriptor)
         || AnnotationConstants.CheckRemoved.isDescriptor(descriptor)
-        || AnnotationConstants.CheckOptimizedOut.isDescriptor(descriptor)) {
+        || AnnotationConstants.CheckOptimizedOut.isDescriptor(descriptor)
+        || AnnotationConstants.UsesReflectionToConstruct.isKotlinRepeatableContainerDescriptor(
+            descriptor)) {
       return true;
     }
     return false;
@@ -1544,11 +1548,11 @@
     @Override
     public AnnotationVisitor visitArray(String name) {
       PropertyParsingContext propertyParsingContext = parsingContext.property(name);
-      if (name.equals(UsesReflectionToConstruct.params)) {
+      if (name.equals(UsesReflectionToConstruct.parameterTypes)) {
         return new ParametersClassVisitor(
             propertyParsingContext, parameters -> this.parameters = parameters);
       }
-      if (name.equals(UsesReflectionToConstruct.paramTypeNames)) {
+      if (name.equals(UsesReflectionToConstruct.parameterTypeNames)) {
         return new ParametersClassNamesVisitor(
             propertyParsingContext, parameters -> this.parameters = parameters);
       }
@@ -1557,39 +1561,79 @@
 
     @Override
     public void visitEnd() {
-      KeepClassItemPattern classItemPattern =
-          KeepClassItemPattern.builder()
-              .setClassPattern(
-                  KeepClassPattern.builder().setClassNamePattern(qualifiedName).build())
-              .build();
+      String kotlinMetadataDescriptor = "Lkotlin/Metadata;";
+
       KeepClassBindingReference classBinding =
-          bindingsHelper.defineFreshClassBinding(classItemPattern);
-      KeepMemberItemPattern keepMemberItemPattern =
-          KeepMemberItemPattern.builder()
-              .setClassReference(classBinding)
-              .setMemberPattern(
-                  KeepMethodPattern.builder()
-                      .setNamePattern(KeepMethodNamePattern.instanceInitializer())
-                      .setParametersPattern(parameters)
-                      .setReturnTypeVoid()
-                      .build())
-              .build();
+          bindingsHelper.defineFreshClassBinding(
+              KeepClassItemPattern.builder()
+                  .setClassPattern(
+                      KeepClassPattern.builder().setClassNamePattern(qualifiedName).build())
+                  .build());
       KeepMemberBindingReference memberBinding =
-          bindingsHelper.defineFreshMemberBinding(keepMemberItemPattern);
-      builder.setConsequences(
+          bindingsHelper.defineFreshMemberBinding(
+              KeepMemberItemPattern.builder()
+                  .setClassReference(classBinding)
+                  .setMemberPattern(
+                      KeepMethodPattern.builder()
+                          .setNamePattern(KeepMethodNamePattern.instanceInitializer())
+                          .setParametersPattern(parameters)
+                          .setReturnTypeVoid()
+                          .build())
+                  .build());
+
+      KeepClassBindingReference kotlinMetadataBinding =
+          bindingsHelper.defineFreshClassBinding(
+              KeepClassItemPattern.builder()
+                  .setClassPattern(KeepClassPattern.exactFromDescriptor(kotlinMetadataDescriptor))
+                  .build());
+      KeepMemberBindingReference kotlinMetadataMembersBinding =
+          bindingsHelper.defineFreshMemberBinding(
+              KeepMemberItemPattern.builder()
+                  .setClassReference(kotlinMetadataBinding)
+                  .setMemberPattern(KeepMemberPattern.allMembers())
+                  .build());
+
+      Annotation keepConstraintKotlinMetadataAnnotation =
+          KeepConstraint.annotation(
+              KeepAnnotationPattern.builder()
+                  .setNamePattern(
+                      KeepQualifiedClassNamePattern.exactFromDescriptor(kotlinMetadataDescriptor))
+                  .addRetentionPolicy(RetentionPolicy.RUNTIME)
+                  .build());
+
+      KeepConsequences.Builder consequencesBuilder =
           KeepConsequences.builder()
               .addTarget(
                   KeepTarget.builder()
                       .setItemReference(classBinding)
-                      .setItemReference(memberBinding)
+                      .setConstraints(
+                          KeepConstraints.defaultAdditions(
+                              KeepConstraints.builder()
+                                  .add(keepConstraintKotlinMetadataAnnotation)
+                                  .build()))
                       .build())
-              .addTarget(KeepTarget.builder().setItemReference(classBinding).build())
-              .build());
+              .addTarget(
+                  KeepTarget.builder()
+                      .setItemReference(memberBinding)
+                      // Keeping the kotlin.Metadata annotation on the members is not really needed,
+                      // as the annotation is only supported on classes. However, having it here
+                      // makes the keep rule extraction generate more compact rules.
+                      .setConstraints(
+                          KeepConstraints.defaultAdditions(
+                              KeepConstraints.builder()
+                                  .add(keepConstraintKotlinMetadataAnnotation)
+                                  .build()))
+                      .build())
+              .addTarget(KeepTarget.builder().setItemReference(kotlinMetadataBinding).build())
+              .addTarget(
+                  KeepTarget.builder().setItemReference(kotlinMetadataMembersBinding).build());
+
       parent.accept(
           builder
               .setMetaInfo(metaInfoBuilder.build())
               .setBindings(bindingsHelper.build())
               .setPreconditions(preconditions.build())
+              .setConsequences(consequencesBuilder.build())
               .build());
     }
   }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/AnnotationConstants.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/AnnotationConstants.java
index 73077b7..4d813f5 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/AnnotationConstants.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/AnnotationConstants.java
@@ -154,8 +154,8 @@
     public static final String classConstant = "classConstant";
     public static final String className = "className";
     public static final String constructorParametersGroup = "constructor-parameters";
-    public static final String params = "params";
-    public static final String paramTypeNames = "paramTypeNames";
+    public static final String parameterTypes = "parameterTypes";
+    public static final String parameterTypeNames = "parameterTypeNames";
   }
 
   public static final class UsesReflectionToAccessMethod {
@@ -183,8 +183,8 @@
     public static final String className = "className";
     public static final String methodName = "methodName";
     public static final String constructorParametersGroup = "constructor-parameters";
-    public static final String params = "params";
-    public static final String paramTypeNames = "paramTypeNames";
+    public static final String parameterTypes = "parameterTypes";
+    public static final String parameterTypeNames = "parameterTypeNames";
     public static final String returnSelectionGroup = "return-selection";
     public static final String returnType = "returnType";
     public static final String returnTypeName = "returnTypeName";
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepPackageComponentPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepPackageComponentPattern.java
index d60c8d0..f2e5c58 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepPackageComponentPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepPackageComponentPattern.java
@@ -85,4 +85,12 @@
     KeepPackageComponentPattern other = (KeepPackageComponentPattern) obj;
     return Objects.equals(singlePattern, other.singlePattern);
   }
+
+  @Override
+  public String toString() {
+    if (isZeroOrMore()) {
+      return "**";
+    }
+    return singlePattern.toString();
+  }
 }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepPackagePattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepPackagePattern.java
index e1d24e1..2c3497d 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepPackagePattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepPackagePattern.java
@@ -209,4 +209,11 @@
   public int hashCode() {
     return Objects.hash(isExact, componentPatterns);
   }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    componentPatterns.forEach(p -> sb.append(p).append("|"));
+    return sb.toString();
+  }
 }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepQualifiedClassNamePattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepQualifiedClassNamePattern.java
index fc1a44a..d99d59a 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepQualifiedClassNamePattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepQualifiedClassNamePattern.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.keepanno.ast;
 
 import com.android.tools.r8.keepanno.proto.KeepSpecProtos.ClassNamePattern;
+import com.android.tools.r8.keepanno.utils.DescriptorUtils;
 import java.util.Objects;
 import java.util.function.Consumer;
 
@@ -21,7 +22,7 @@
   }
 
   public static KeepQualifiedClassNamePattern exactFromDescriptor(String classDescriptor) {
-    if (!classDescriptor.startsWith("L") && classDescriptor.endsWith(";")) {
+    if (!DescriptorUtils.isValidClassDescriptor(classDescriptor)) {
       throw new KeepEdgeException("Invalid class descriptor: " + classDescriptor);
     }
     return exact(classDescriptor.substring(1, classDescriptor.length() - 1).replace('/', '.'));
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/utils/DescriptorUtils.java b/src/keepanno/java/com/android/tools/r8/keepanno/utils/DescriptorUtils.java
new file mode 100644
index 0000000..6694cbc
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/utils/DescriptorUtils.java
@@ -0,0 +1,25 @@
+// Copyright (c) 2025, 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.keepanno.utils;
+
+public class DescriptorUtils {
+  public static boolean isValidClassDescriptor(String string) {
+    if (string.length() < 3
+        || string.charAt(0) != 'L'
+        || string.charAt(string.length() - 1) != ';') {
+      return false;
+    }
+    if (string.charAt(1) == '/' || string.charAt(string.length() - 2) == '/') {
+      return false;
+    }
+    int cp;
+    for (int i = 1; i < string.length() - 1; i += Character.charCount(cp)) {
+      cp = string.codePointAt(i);
+      if (cp != '/' && !IdentifierUtils.isRelaxedDexIdentifierPart(cp)) {
+        return false;
+      }
+    }
+    return true;
+  }
+}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/utils/IdentifierUtils.java b/src/keepanno/java/com/android/tools/r8/keepanno/utils/IdentifierUtils.java
new file mode 100644
index 0000000..50ae175
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/utils/IdentifierUtils.java
@@ -0,0 +1,37 @@
+// Copyright (c) 2025, 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.keepanno.utils;
+
+public class IdentifierUtils {
+  public static boolean isRelaxedDexIdentifierPart(int cp) {
+    return isSimpleNameChar(cp) || isUnicodeSpace(cp);
+  }
+
+  public static boolean isUnicodeSpace(int cp) {
+    // Unicode 'Zs' category
+    return cp == ' '
+        || cp == 0x00a0
+        || cp == 0x1680
+        || (0x2000 <= cp && cp <= 0x200a)
+        || cp == 0x202f
+        || cp == 0x205f
+        || cp == 0x3000;
+  }
+
+  public static boolean isSimpleNameChar(int cp) {
+    // See https://source.android.com/devices/tech/dalvik/dex-format#string-syntax.
+    return ('A' <= cp && cp <= 'Z')
+        || ('a' <= cp && cp <= 'z')
+        || ('0' <= cp && cp <= '9')
+        || cp == '$'
+        || cp == '-'
+        || cp == '_'
+        || (0x00a1 <= cp && cp <= 0x1fff)
+        || (0x2010 <= cp && cp <= 0x2027)
+        || (0x2030 <= cp && cp <= 0xd7ff)
+        || (0xe000 <= cp && cp < 0xfeff) // Don't include BOM.
+        || (0xfeff < cp && cp <= 0xffef)
+        || (0x10000 <= cp && cp <= 0x10ffff);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 1d2c0ec..402e18b 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -467,7 +467,8 @@
         }
 
         // Compute after initial round of tree shaking to not trigger on pruned classes.
-        enableListIterationRewriter = ListIterationRewriter.shouldEnable(appView, subtypingInfo);
+        enableListIterationRewriter =
+            ListIterationRewriter.shouldEnableForR8(appView, subtypingInfo);
 
         timing.end();
       } finally {
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 0499fe5..1e5c7d9 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -52,6 +52,7 @@
 import com.android.tools.r8.shaking.MainDexInfo;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.ArrayUtils;
+import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.DexVersion;
 import com.android.tools.r8.utils.ExceptionUtils;
@@ -66,6 +67,7 @@
 import com.android.tools.r8.utils.PredicateUtils;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
+import com.android.tools.r8.utils.SupplierUtils;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.timing.Timing;
 import com.android.tools.r8.utils.timing.TimingMerger;
@@ -88,6 +90,7 @@
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
+import java.util.function.Supplier;
 
 public class ApplicationWriter {
 
@@ -723,18 +726,25 @@
   private void insertAttributeAnnotationsForClass(DexProgramClass clazz) {
     EnclosingMethodAttribute enclosingMethod = clazz.getEnclosingMethodAttribute();
     List<InnerClassAttribute> innerClasses = clazz.getInnerClasses();
-    if (enclosingMethod == null
-        && innerClasses.isEmpty()
-        && clazz.getClassSignature().hasNoSignature()
-        && !clazz.isInANest()
-        && !clazz.isRecord()
-        && !clazz.hasPermittedSubclassAttributes()) {
-      return;
-    }
+
+    IntBox allocatedSize = new IntBox();
+    Supplier<List<DexAnnotation>> annotationsSupplier =
+        SupplierUtils.nonThreadSafeMemoize(
+            () -> {
+              allocatedSize.set(
+                  BooleanUtils.intValue(enclosingMethod != null)
+                      + innerClasses.size()
+                      + 1
+                      + BooleanUtils.intValue(clazz.getClassSignature().hasSignature())
+                      + BooleanUtils.intValue(options.canUseNestBasedAccess() && clazz.isInANest())
+                      + BooleanUtils.intValue(
+                          clazz.hasPermittedSubclassAttributes() && options.canUseSealedClasses()));
+              return new ArrayList<>(allocatedSize.get());
+            });
 
     // EnclosingMember translates directly to an enclosing class/method if present.
-    List<DexAnnotation> annotations = new ArrayList<>(2 + innerClasses.size());
     if (enclosingMethod != null) {
+      List<DexAnnotation> annotations = annotationsSupplier.get();
       if (enclosingMethod.getEnclosingMethod() != null) {
         annotations.add(
             DexAnnotation.createEnclosingMethodAnnotation(
@@ -752,6 +762,7 @@
     // it relates to the present class. If it relates to the outer-type (and is named) it becomes
     // part of the member-classes annotation.
     if (!innerClasses.isEmpty()) {
+      List<DexAnnotation> annotations = annotationsSupplier.get();
       List<DexType> memberClasses = new ArrayList<>(innerClasses.size());
       for (InnerClassAttribute innerClass : innerClasses) {
         if (clazz.type == innerClass.getInner()) {
@@ -782,6 +793,7 @@
     }
 
     if (clazz.getClassSignature().hasSignature()) {
+      List<DexAnnotation> annotations = annotationsSupplier.get();
       annotations.add(
           DexAnnotation.createSignatureAnnotation(
               clazz.getClassSignature().toRenamedString(getNamingLens(), isTypeMissing),
@@ -790,12 +802,14 @@
 
     if (options.canUseNestBasedAccess()) {
       if (clazz.isNestHost()) {
+        List<DexAnnotation> annotations = annotationsSupplier.get();
         annotations.add(
             DexAnnotation.createNestMembersAnnotation(
                 clazz.getNestMembersClassAttributes(), options.itemFactory));
       }
 
       if (clazz.isNestMember()) {
+        List<DexAnnotation> annotations = annotationsSupplier.get();
         annotations.add(
             DexAnnotation.createNestHostAnnotation(
                 clazz.getNestHostClassAttribute(), options.itemFactory));
@@ -803,16 +817,21 @@
     }
 
     if (clazz.hasPermittedSubclassAttributes() && options.canUseSealedClasses()) {
+      List<DexAnnotation> annotations = annotationsSupplier.get();
       annotations.add(
           DexAnnotation.createPermittedSubclassesAnnotation(
               clazz.getPermittedSubclassAttributes(), options.itemFactory));
     }
 
     if (clazz.isRecord() && options.emitRecordAnnotationsInDex) {
+      List<DexAnnotation> annotations = annotationsSupplier.get();
       annotations.add(DexAnnotation.createRecordAnnotation(clazz, appView));
     }
 
-    if (!annotations.isEmpty()) {
+    if (allocatedSize.get() > 0) {
+      List<DexAnnotation> annotations = annotationsSupplier.get();
+      assert allocatedSize.get() >= annotations.size();
+
       // Append the annotations to annotations array of the class.
       DexAnnotation[] copy =
           ObjectArrays.concat(
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index db3a696..78e812d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -569,6 +569,10 @@
     return methodCollection.getMethod(method);
   }
 
+  public DexEncodedMethod lookupMethod(DexMethodSignature method) {
+    return lookupMethod(method.getProto(), method.getName());
+  }
+
   public DexEncodedMethod lookupMethod(DexProto methodProto, DexString methodName) {
     return methodCollection.getMethod(methodProto, methodName);
   }
diff --git a/src/main/java/com/android/tools/r8/graph/MethodResolution.java b/src/main/java/com/android/tools/r8/graph/MethodResolution.java
index 9a75cfe..fad11a2 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodResolution.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodResolution.java
@@ -509,7 +509,7 @@
    * Section 5.4.3.4 of the JVM Spec</a>.
    *
    * <p>The resolved method is not the method that will actually be invoked. Which methods gets
-   * invoked depends on the invoke instruction used. However, it is always save to rewrite any
+   * invoked depends on the invoke instruction used. However, it is always safe to rewrite any
    * invoke on the given descriptor to a corresponding invoke on the resolved descriptor, as the
    * resolved method is used as basis for dispatch.
    */
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/UndoConstructorInlining.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/UndoConstructorInlining.java
index 70a2a9a..af621ab 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/UndoConstructorInlining.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/UndoConstructorInlining.java
@@ -63,7 +63,6 @@
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
-import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
@@ -247,9 +246,8 @@
                     info.getProgramClass(),
                     info.getInvokedMethod(),
                     ensureConstructorsOnClasses,
-                    newConstructor ->
-                        profileCollectionAdditions.addMethodIfContextIsInProfile(
-                            newConstructor, method));
+                    method,
+                    profileCollectionAdditions);
         if (newInvokedMethod.getArity() != info.getInvokedMethod().getArity()) {
           assert newInvokedMethod.getArity() > info.getInvokedMethod().getArity();
           return rewriteIR(method, code);
@@ -310,9 +308,8 @@
                       noSkipClass,
                       invokedMethod,
                       ensureConstructorsOnClasses,
-                      newConstructor ->
-                          profileCollectionAdditions.addMethodIfContextIsInProfile(
-                              newConstructor, method));
+                      method,
+                      profileCollectionAdditions);
           InvokeDirect.Builder invokeDirectBuilder =
               InvokeDirect.builder()
                   .setArguments(invoke.arguments())
@@ -484,19 +481,30 @@
         DexProgramClass clazz,
         DexMethod target,
         Map<DexType, DexProgramClass> ensureConstructorsOnClasses,
-        Consumer<ProgramMethod> creationConsumer) {
-      return constructorCache
-          .computeIfAbsent(clazz, ignoreKey(IdentityHashMap::new))
-          .computeIfAbsent(
-              target,
-              k -> createConstructor(clazz, target, ensureConstructorsOnClasses, creationConsumer));
+        ProgramMethod context,
+        ProfileCollectionAdditions profileCollectionAdditions) {
+      ProgramMethod constructor =
+          constructorCache
+              .computeIfAbsent(clazz, ignoreKey(IdentityHashMap::new))
+              .computeIfAbsent(
+                  target,
+                  k ->
+                      createConstructor(
+                          clazz,
+                          target,
+                          ensureConstructorsOnClasses,
+                          context,
+                          profileCollectionAdditions));
+      profileCollectionAdditions.addMethodIfContextIsInProfile(constructor, context);
+      return constructor;
     }
 
     private ProgramMethod createConstructor(
         DexProgramClass clazz,
         DexMethod target,
         Map<DexType, DexProgramClass> ensureConstructorsOnClasses,
-        Consumer<ProgramMethod> creationConsumer) {
+        ProgramMethod context,
+        ProfileCollectionAdditions profileCollectionAdditions) {
       // Create a fresh constructor on the given class that calls target. If there is a class in the
       // hierarchy inbetween `clazz` and `target.holder`, which is also subject to class merging,
       // then we must create a constructor that calls a constructor on that intermediate class,
@@ -510,7 +518,11 @@
         if (ensureConstructorsOnClasses.containsKey(currentType)) {
           target =
               getOrCreateConstructor(
-                      currentClass, target, ensureConstructorsOnClasses, creationConsumer)
+                      currentClass,
+                      target,
+                      ensureConstructorsOnClasses,
+                      context,
+                      profileCollectionAdditions)
                   .getReference();
           break;
         }
@@ -538,9 +550,7 @@
               .setApiLevelForDefinition(
                   appView.apiLevelCompute().computeInitialMinApiLevel(appView.options()))
               .build();
-      ProgramMethod programMethod = method.asProgramMethod(clazz);
-      creationConsumer.accept(programMethod);
-      return programMethod;
+      return method.asProgramMethod(clazz);
     }
 
     private LirCode<Integer> createConstructorCode(DexMethod methodReference, DexMethod target) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
index 8ee1cd3..29925a7 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
@@ -30,6 +30,7 @@
 import com.android.tools.r8.ir.desugar.itf.InterfaceProcessor;
 import com.android.tools.r8.ir.desugar.itf.L8InnerOuterAttributeEraser;
 import com.android.tools.r8.ir.desugar.lambda.LambdaDeserializationMethodRemover;
+import com.android.tools.r8.ir.optimize.ListIterationRewriter;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
 import com.android.tools.r8.position.MethodPosition;
 import com.android.tools.r8.profile.rewriting.ProfileCollectionAdditions;
@@ -51,6 +52,9 @@
   public PrimaryD8L8IRConverter(AppView<AppInfo> appView, Timing timing) {
     super(appView);
     this.timing = timing;
+    if (ListIterationRewriter.shouldEnableForD8(appView)) {
+      rewriterPassCollection.enableListIterationRewriter(appView);
+    }
   }
 
   @SuppressWarnings("BadImport")
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/CodeRewriterPass.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/CodeRewriterPass.java
index 2bef810..de9c246 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/CodeRewriterPass.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/CodeRewriterPass.java
@@ -33,6 +33,10 @@
     return (AppView<T>) appView;
   }
 
+  protected T appInfo() {
+    return appView().appInfo();
+  }
+
   public final CodeRewriterResult run(
       IRCode code,
       MethodProcessor methodProcessor,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ListIterationRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/ListIterationRewriter.java
index 9f4445e..60e9dea 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ListIterationRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ListIterationRewriter.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.ir.optimize;
 
 import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+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.DexClass;
@@ -34,7 +35,6 @@
 import com.android.tools.r8.ir.conversion.MethodProcessor;
 import com.android.tools.r8.ir.conversion.passes.CodeRewriterPass;
 import com.android.tools.r8.ir.conversion.passes.result.CodeRewriterResult;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.InternalOptions.TestingOptions;
 import com.android.tools.r8.utils.TraversalContinuation;
 import com.google.common.collect.ImmutableList;
@@ -82,7 +82,7 @@
  * The reason for the transformation is that the code runs ~3x faster and saves an allocation.
  * This transformation requires 2 extra registers and saves 2 bytes.
  */
-public class ListIterationRewriter extends CodeRewriterPass<AppInfoWithLiveness> {
+public class ListIterationRewriter extends CodeRewriterPass<AppInfo> {
   private final DexString iteratorName;
   private final DexProto iteratorProto;
   private final DexType listType;
@@ -114,12 +114,17 @@
     this.immutableListType = dexItemFactory.comGoogleCommonCollectImmutableListType;
   }
 
+  public static boolean shouldEnableForD8(AppView<AppInfo> appView) {
+    return appView.options().isRelease()
+        && appView.testing().listIterationRewritingRewriteCustomIterators;
+  }
+
   /**
    * Returns whether to enable the optimization.
    *
    * @param subtypingInfo May contain pruned items, but must not be missing any types.
    */
-  public static boolean shouldEnable(
+  public static boolean shouldEnableForR8(
       AppView<? extends AppInfoWithClassHierarchy> appView,
       ImmediateAppSubtypingInfo subtypingInfo) {
     TestingOptions opts = appView.options().testing;
@@ -154,6 +159,11 @@
   }
 
   @Override
+  protected AppInfoWithClassHierarchy appInfo() {
+    return appView.appInfoForDesugaring();
+  }
+
+  @Override
   protected String getRewriterId() {
     return "ListIterationRewriter";
   }
@@ -211,14 +221,14 @@
       // go to from 1010 to 1731.
       // While this would be safe for lists like: List.of(), singletonList(), etc, it would require
       // expensive intra-procedural analysis to track when it is safe.
-      return appView().appInfo().isSubtype(valueType, listType)
+      return appInfo().isSubtype(valueType, listType)
           // LinkedList.get() is not O(1).
           && valueType.isNotIdenticalTo(linkedListType)
           // CopyOnWriteArrayList.iterator() provides a snapshot of the list.
           && valueType.isNotIdenticalTo(copyOnWriteArrayListType);
     }
     // TODO(b/145280859): Add support for kotlin.collections.ArrayList.
-    return appView().appInfo().isSubtype(valueType, arrayListType)
+    return appInfo().isSubtype(valueType, arrayListType)
         || valueType.isIdenticalTo(immutableListType);
   }
 
@@ -379,7 +389,7 @@
     MethodResolutionResult resolvedSizeMethod =
         listClass == null || listClass.isInterface()
             ? null
-            : appView().appInfo().resolveMethodOnClass(listClass, sizeMethod);
+            : appInfo().resolveMethodOnClass(listClass, sizeMethod);
     InvokeMethodWithReceiver sizeInstr =
         resolvedSizeMethod == null || resolvedSizeMethod.getResolvedHolder().isInterface()
             ? new InvokeInterface(sizeMethod, sizeValue, sizeArgs)
@@ -426,7 +436,7 @@
     MethodResolutionResult resolvedGetMethod =
         listClass == null || listClass.isInterface()
             ? null
-            : appView().appInfo().resolveMethodOnClass(listClass, getMethod);
+            : appInfo().resolveMethodOnClass(listClass, getMethod);
     InvokeMethodWithReceiver getInstr =
         resolvedGetMethod == null || resolvedGetMethod.getResolvedHolder().isInterface()
             ? new InvokeInterface(getMethod, elementValue, getArgs)
diff --git a/src/main/java/com/android/tools/r8/naming/KotlinModuleSynthesizer.java b/src/main/java/com/android/tools/r8/naming/KotlinModuleSynthesizer.java
index 1421e46..f38fdaf 100644
--- a/src/main/java/com/android/tools/r8/naming/KotlinModuleSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/naming/KotlinModuleSynthesizer.java
@@ -16,6 +16,7 @@
 import com.android.tools.r8.kotlin.KotlinMultiFileClassPartInfo;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.Pair;
 import com.google.common.collect.ImmutableList;
 import java.util.ArrayList;
@@ -43,7 +44,7 @@
   }
 
   public boolean isKotlinModuleFile(DataEntryResource file) {
-    return file.getName().endsWith(".kotlin_module");
+    return FileUtils.isKotlinModuleFile(file.getName());
   }
 
   public List<DataEntryResource> synthesizeKotlinModuleFiles() {
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index e18fa27..7fcedfc 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -321,40 +321,58 @@
         originalClass.accessFlags.isPublic() ? null : method.holder.getPackageDescriptor();
     if (packageDescriptor == null
         || !packageDescriptor.equals(target.getHolderType().getPackageDescriptor())) {
-      DexProgramClass bridgeHolder =
+      DexClass bridgeHolder =
           findHolderForVisibilityBridge(originalClass, target.getHolder(), packageDescriptor);
       assert bridgeHolder != null;
-      bridges.accept(bridgeHolder, method, target);
-      return target.getReference().withHolder(bridgeHolder.getType(), appView.dexItemFactory());
+      if (bridgeHolder.isClasspathClass()) {
+        // Intentionally empty. We do not need to insert a bridge on a classpath class.
+      } else if (bridgeHolder.isLibraryClass()) {
+        // Caution is needed when member rebinding into the library.
+        // This is handled by validMemberRebindingTargetForNonProgramMethod.
+        return null;
+      } else {
+        // Insert a bridge on this program class.
+        bridges.accept(bridgeHolder.asProgramClass(), method, target);
+      }
+      return target.getReference().withHolder(bridgeHolder, appView.dexItemFactory());
     }
     return target.getReference();
   }
 
-  private DexProgramClass findHolderForVisibilityBridge(
-      DexClass originalClass, DexClass targetClass, String packageDescriptor) {
-    if (originalClass == targetClass || originalClass.isNotProgramClass()) {
+  private DexClass findHolderForVisibilityBridge(
+      DexClass currentClass, DexClass resolvedHolder, String initialResolutionPackage) {
+    if (currentClass == resolvedHolder) {
       return null;
     }
-    DexProgramClass newHolder = null;
+    DexClass newHolder = null;
     // Recurse through supertype chain.
-    if (appView.appInfo().isSubtype(originalClass.superType, targetClass.type)) {
-      DexClass superClass = appView.definitionFor(originalClass.superType);
-      newHolder = findHolderForVisibilityBridge(superClass, targetClass, packageDescriptor);
+    if (appView.appInfo().isSubtype(currentClass.superType, resolvedHolder.type)) {
+      DexClass superClass = appView.definitionFor(currentClass.superType);
+      newHolder =
+          findHolderForVisibilityBridge(superClass, resolvedHolder, initialResolutionPackage);
     } else {
-      for (DexType iface : originalClass.interfaces.values) {
-        if (appView.appInfo().isSubtype(iface, targetClass.type)) {
+      for (DexType iface : currentClass.interfaces.values) {
+        if (appView.appInfo().isSubtype(iface, resolvedHolder.type)) {
           DexClass interfaceClass = appView.definitionFor(iface);
-          newHolder = findHolderForVisibilityBridge(interfaceClass, targetClass, packageDescriptor);
+          newHolder =
+              findHolderForVisibilityBridge(
+                  interfaceClass, resolvedHolder, initialResolutionPackage);
         }
       }
     }
     if (newHolder != null) {
       // A supertype fulfills the visibility requirements.
       return newHolder;
-    } else if (originalClass.accessFlags.isPublic()
-        || originalClass.type.getPackageDescriptor().equals(packageDescriptor)) {
-      // This class is visible. Return it if it is a program class, otherwise null.
-      return originalClass.asProgramClass();
+    } else {
+      boolean hasAccessToTarget =
+          AccessControl.isClassAccessible(resolvedHolder, currentClass, appView).isTrue();
+      boolean isVisibleToCallers =
+          currentClass.isPublic()
+              || currentClass.type.getPackageDescriptor().equals(initialResolutionPackage);
+      if (hasAccessToTarget && isVisibleToCallers) {
+        // This class is visible and can access the target class.
+        return currentClass;
+      }
     }
     return null;
   }
diff --git a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java
index 617a1ed..a7203b9 100644
--- a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java
+++ b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.optimize.bridgehoisting;
 
-
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.BottomUpClassHierarchyTraversal;
 import com.android.tools.r8.graph.DexClass;
@@ -11,6 +10,7 @@
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
 import com.android.tools.r8.graph.MethodResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -19,8 +19,11 @@
 import com.android.tools.r8.ir.optimize.info.bridge.VirtualBridgeInfo;
 import com.android.tools.r8.lightir.LirCode;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.DepthFirstSearchWorkListBase.DepthFirstSearchWorkList;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.MethodSignatureEquivalence;
+import com.android.tools.r8.utils.TraversalContinuation;
+import com.android.tools.r8.utils.collections.DexMethodSignatureSet;
 import com.android.tools.r8.utils.timing.Timing;
 import com.google.common.base.Equivalence;
 import com.google.common.base.Equivalence.Wrapper;
@@ -29,12 +32,14 @@
 import java.util.Collection;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.IdentityHashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
+import java.util.function.Function;
 
 /**
  * An optimization pass that hoists bridges upwards with the purpose of sharing redundant bridge
@@ -66,6 +71,16 @@
 
   private final AppView<AppInfoWithLiveness> appView;
 
+  // Cache that for a given non-interface class stores the default interface methods present on the
+  // class and all of its subclasses.
+  private final Map<DexProgramClass, DexMethodSignatureSet>
+      defaultInterfaceMethodsInClassAndSubclassesCache = new IdentityHashMap<>();
+
+  // Cache that for a given interface stores the default methods on that interface and all of its
+  // transitive superinterfaces.
+  private final Map<DexClass, DexMethodSignatureSet> inheritedDefaultInterfaceMethodsCache =
+      new IdentityHashMap<>();
+
   // Structure that keeps track of the changes for construction of the Proguard map and
   // AppInfoWithLiveness maintenance.
   private final BridgeHoistingResult result;
@@ -103,7 +118,7 @@
       }
     }
     for (ProgramMethod candidate : getCandidatesForHoisting(eligibleSubclasses)) {
-      hoistBridgeIfPossible(candidate, clazz, eligibleSubclasses);
+      hoistBridgeIfPossible(candidate, clazz, eligibleSubclasses, immediateSubtypingInfo);
     }
   }
 
@@ -125,7 +140,10 @@
   }
 
   private void hoistBridgeIfPossible(
-      ProgramMethod method, DexProgramClass clazz, List<DexProgramClass> subclasses) {
+      ProgramMethod method,
+      DexProgramClass clazz,
+      List<DexProgramClass> subclasses,
+      ImmediateProgramSubtypingInfo immediateSubtypingInfo) {
     // If the method is defined on the parent class, we cannot hoist the bridge.
     // TODO(b/153147967): If the declared method is abstract, we could replace it by the bridge.
     //  Add a test.
@@ -160,23 +178,38 @@
                 .appInfo()
                 .resolveMethodOnClassLegacy(subclass, methodReference)
                 .getSingleTarget();
-        if (resolutionTarget == null || resolutionTarget.isAbstract()) {
-          // The fact that this class does not declare the bridge (or the bridge is abstract) should
-          // not prevent us from hoisting the bridge.
-          //
-          // Strictly speaking, there could be an invoke instruction that targets the bridge on this
-          // subclass and fails with an AbstractMethodError or a NoSuchMethodError in the input
-          // program. After hoisting the bridge to the superclass such an instruction would no
-          // longer fail with an error in the generated program.
-          //
-          // If this ever turns out be an issue, it would be possible to track if there is an invoke
-          // instruction targeting the bridge on this subclass that fails in the Enqueuer, but this
-          // should never be the case in practice.
-          continue;
+        if (resolutionTarget != null && !resolutionTarget.isAbstract()) {
+          // Hoisting would change the program behavior.
+          return;
         }
 
-        // Hoisting would change the program behavior.
-        return;
+        if (appView.options().canUseDefaultAndStaticInterfaceMethods()) {
+          DexMethodSignatureSet defaultInterfaceMethodsBelowSubclass =
+              getOrComputeDefaultInterfaceMethodsOnClassAndSubclasses(
+                  subclass, immediateSubtypingInfo);
+          if (defaultInterfaceMethodsBelowSubclass.contains(method)) {
+            // Hoisting would change the program behavior. Virtual methods on classes takes
+            // precedence over default interface methods. By hoisting the current bridge method to
+            // the superclass, we may change virtual calls that would previously have dispatched to
+            // a default interface method into calling the hoisted bridge method.
+            //
+            // See also b/369040938.
+            return;
+          }
+        }
+
+        // The fact that this class does not declare the bridge (or the bridge is abstract) should
+        // not prevent us from hoisting the bridge.
+        //
+        // Strictly speaking, there could be an invoke instruction that targets the bridge on this
+        // subclass and fails with an AbstractMethodError or a NoSuchMethodError in the input
+        // program. After hoisting the bridge to the superclass such an instruction would no
+        // longer fail with an error in the generated program.
+        //
+        // If this ever turns out be an issue, it would be possible to track if there is an invoke
+        // instruction targeting the bridge on this subclass that fails in the Enqueuer, but this
+        // should never be the case in practice.
+        continue;
       }
 
       BridgeInfo currentBridgeInfo = definition.getOptimizationInfo().getBridgeInfo();
@@ -320,4 +353,104 @@
           return item;
         });
   }
+
+  private DexMethodSignatureSet getOrComputeDefaultInterfaceMethodsOnClassAndSubclasses(
+      DexProgramClass clazz, ImmediateProgramSubtypingInfo immediateSubtypingInfo) {
+    if (!defaultInterfaceMethodsInClassAndSubclassesCache.containsKey(clazz)) {
+      new DefaultInterfaceMethodsTraversal(immediateSubtypingInfo).run(List.of(clazz));
+    }
+    DexMethodSignatureSet cachedResult =
+        defaultInterfaceMethodsInClassAndSubclassesCache.get(clazz);
+    assert cachedResult != null;
+    return cachedResult;
+  }
+
+  // A depth-first traversal over the class hierarchy that will collect and cache the default
+  // interface method present on a given class and its subclasses.
+  private class DefaultInterfaceMethodsTraversal
+      extends DepthFirstSearchWorkList<DexProgramClass, Void, Void> {
+
+    private final ImmediateProgramSubtypingInfo immediateSubtypingInfo;
+
+    DefaultInterfaceMethodsTraversal(ImmediateProgramSubtypingInfo immediateSubtypingInfo) {
+      this.immediateSubtypingInfo = immediateSubtypingInfo;
+    }
+
+    @Override
+    protected TraversalContinuation<Void, Void> process(
+        DFSNode<DexProgramClass> node,
+        Function<DexProgramClass, DFSNode<DexProgramClass>> childNodeConsumer) {
+      // Enqueue unseen subclasses for processing.
+      DexProgramClass currentClass = node.getNode();
+      assert !currentClass.isInterface();
+      assert !defaultInterfaceMethodsInClassAndSubclassesCache.containsKey(currentClass);
+      for (DexProgramClass subclass : immediateSubtypingInfo.getSubclasses(currentClass)) {
+        if (!defaultInterfaceMethodsInClassAndSubclassesCache.containsKey(subclass)) {
+          DFSNode<DexProgramClass> unusedChildNode = childNodeConsumer.apply(subclass);
+        }
+      }
+      return TraversalContinuation.doContinue();
+    }
+
+    @Override
+    public TraversalContinuation<Void, Void> joiner(DFSNode<DexProgramClass> node) {
+      // All subclasses of the current class have now been processed.
+      DexProgramClass currentClass = node.getNode();
+      assert !currentClass.isInterface();
+      assert !defaultInterfaceMethodsInClassAndSubclassesCache.containsKey(currentClass);
+      // Compute the default interface methods that this class inherits from its implemented
+      // interfaces.
+      DexMethodSignatureSet defaultInterfaceMethods = DexMethodSignatureSet.create();
+      for (DexType implementedType : currentClass.getInterfaces()) {
+        DexClass implementedInterface = appView.definitionFor(implementedType);
+        if (implementedInterface != null) {
+          defaultInterfaceMethods.addAll(
+              getOrComputeInheritedDefaultInterfaceMethodsForInterface(implementedInterface));
+        }
+      }
+      // Include the default interface methods that are present on the class' subclasses.
+      for (DexProgramClass subclass : immediateSubtypingInfo.getSubclasses(currentClass)) {
+        DexMethodSignatureSet defaultInterfaceMethodsInSubclass =
+            defaultInterfaceMethodsInClassAndSubclassesCache.get(subclass);
+        assert defaultInterfaceMethodsInSubclass != null;
+        defaultInterfaceMethods.addAll(defaultInterfaceMethodsInSubclass);
+      }
+      // Remove all virtual methods declared by the class itself, since we are only interested in
+      // the occurrence of default interface method that are not overridden by a non-default
+      // interface method.
+      defaultInterfaceMethods.removeIf(m -> currentClass.lookupMethod(m) != null);
+      // Cache the computed result.
+      defaultInterfaceMethodsInClassAndSubclassesCache.put(currentClass, defaultInterfaceMethods);
+      return TraversalContinuation.doContinue();
+    }
+
+    // For a given interface, returns the default interface methods present on that interface and
+    // all transitive superinterfaces.
+    private DexMethodSignatureSet getOrComputeInheritedDefaultInterfaceMethodsForInterface(
+        DexClass itf) {
+      assert itf.isInterface();
+      // Check if we have already computed the default interface methods in the current interface
+      // and its superinterfaces.
+      DexMethodSignatureSet cachedResult = inheritedDefaultInterfaceMethodsCache.get(itf);
+      if (cachedResult != null) {
+        return cachedResult;
+      }
+      DexMethodSignatureSet inheritedDefaultInterfaceMethods = DexMethodSignatureSet.create();
+      // First add the default interface methods that are present on the interface directly.
+      for (DexEncodedMethod defaultInterfaceMethod :
+          itf.virtualMethods(DexEncodedMethod::hasCode)) {
+        inheritedDefaultInterfaceMethods.add(defaultInterfaceMethod);
+      }
+      // Then add the default interface methods that are present on the superinterfaces.
+      for (DexType implementedType : itf.getInterfaces()) {
+        DexClass implementedInterface = appView.definitionFor(implementedType);
+        if (implementedInterface != null) {
+          inheritedDefaultInterfaceMethods.addAll(
+              getOrComputeInheritedDefaultInterfaceMethodsForInterface(implementedInterface));
+        }
+      }
+      inheritedDefaultInterfaceMethodsCache.put(itf, inheritedDefaultInterfaceMethods);
+      return inheritedDefaultInterfaceMethods;
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index c5238b0..f2bf9a4 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -4600,11 +4600,20 @@
       if (clazz.isLibraryClass()) {
         libraryClasses.add(clazz.asLibraryClass());
       } else if (clazz.isClasspathClass()) {
-        classpathClasses.add(clazz.asClasspathClass());
+        DexClasspathClass classpathClass = clazz.asClasspathClass();
+        if (appView.getSyntheticItems().isPendingSynthetic(classpathClass.getType())) {
+          // This class will be committed later.
+        } else {
+          classpathClasses.add(classpathClass);
+        }
       } else {
         assert false;
       }
     }
+    // Verify the synthetic classpath classes are live before we commit them to the app below.
+    assert appView.getSyntheticItems().getPendingSyntheticClasses().stream()
+        .filter(DexClass::isClasspathClass)
+        .allMatch(liveNonProgramTypes::contains);
 
     // Add just referenced non-program types. We can't replace the program classes at this point as
     // they are needed in tree pruning.
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index f7cc1e5..f2eea5b 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -870,7 +870,6 @@
     return true;
   }
 
-  @SuppressWarnings("MixedMutabilityReturnType")
   private DexType createExternalType(
       SyntheticKind kind,
       String externalSyntheticTypePrefix,
@@ -879,7 +878,8 @@
       Predicate<DexType> reserved) {
     DexItemFactory factory = appView.dexItemFactory();
     if (kind.isFixedSuffixSynthetic()) {
-      return SyntheticNaming.createExternalType(kind, externalSyntheticTypePrefix, "", factory);
+      return SyntheticNaming.createExternalType(
+          kind, externalSyntheticTypePrefix, "", factory, appView.options());
     }
     NumberGenerator generator =
         generators.computeIfAbsent(externalSyntheticTypePrefix, k -> new NumberGenerator());
@@ -887,7 +887,11 @@
     do {
       externalType =
           SyntheticNaming.createExternalType(
-              kind, externalSyntheticTypePrefix, Integer.toString(generator.next()), factory);
+              kind,
+              externalSyntheticTypePrefix,
+              Integer.toString(generator.next()),
+              factory,
+              appView.options());
       // If the generated external type matches an external synthetic from the input, which is kept,
       // then continue.
       if (reserved.test(externalType)) {
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index 676989f..f50b1db 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.synthesis;
 
+import static com.android.tools.r8.utils.DescriptorUtils.INNER_CLASS_SEPARATOR;
+
 import com.android.tools.r8.Version;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
@@ -12,6 +14,7 @@
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.structural.Equatable;
 import com.android.tools.r8.utils.structural.Ordered;
 import com.google.common.hash.Hasher;
@@ -478,14 +481,23 @@
   }
 
   static DexType createExternalType(
-      SyntheticKind kind, String externalSyntheticTypePrefix, String id, DexItemFactory factory) {
+      SyntheticKind kind,
+      String externalSyntheticTypePrefix,
+      String id,
+      DexItemFactory factory,
+      InternalOptions options) {
     assert kind.isFixedSuffixSynthetic() == id.isEmpty();
-    return createType(
-        kind.isFixedSuffixSynthetic() ? "" : EXTERNAL_SYNTHETIC_CLASS_SEPARATOR,
-        kind,
-        externalSyntheticTypePrefix,
-        id,
-        factory);
+    if (kind.isFixedSuffixSynthetic()) {
+      assert id.isEmpty();
+      return createType("", kind, externalSyntheticTypePrefix, id, factory);
+    } else if (options.desugarSpecificOptions().minimizeSyntheticNames) {
+      return factory.createType(
+          DescriptorUtils.getDescriptorFromClassBinaryName(
+              externalSyntheticTypePrefix + INNER_CLASS_SEPARATOR + id));
+    } else {
+      return createType(
+          EXTERNAL_SYNTHETIC_CLASS_SEPARATOR, kind, externalSyntheticTypePrefix, id, factory);
+    }
   }
 
   private static DexType createType(
@@ -505,7 +517,7 @@
   public static String createDescriptor(
       String separator, SyntheticKind kind, String externalSyntheticTypePrefix, String id) {
     return DescriptorUtils.getDescriptorFromClassBinaryName(
-        externalSyntheticTypePrefix + separator + kind.descriptor + id);
+        externalSyntheticTypePrefix + separator + (kind != null ? kind.descriptor : "") + id);
   }
 
   public static boolean verifyNotInternalSynthetic(DexType type) {
@@ -542,6 +554,12 @@
         createDescriptor(EXTERNAL_SYNTHETIC_CLASS_SEPARATOR, kind, context.getBinaryName(), id));
   }
 
+  static ClassReference makeMinimalSyntheticReferenceForTest(ClassReference context, String id) {
+    return Reference.classFromDescriptor(
+        createDescriptor(
+            Character.toString(INNER_CLASS_SEPARATOR), null, context.getBinaryName(), id));
+  }
+
   static boolean isSynthetic(ClassReference clazz, Phase phase, SyntheticKind kind) {
     String typeName = clazz.getTypeName();
     if (kind.isFixedSuffixSynthetic()) {
diff --git a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
index 08da5f1..39624d2 100644
--- a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
@@ -734,22 +734,7 @@
   }
 
   public static boolean isValidClassDescriptor(String string) {
-    if (string.length() < 3
-        || string.charAt(0) != 'L'
-        || string.charAt(string.length() - 1) != ';') {
-      return false;
-    }
-    if (string.charAt(1) == '/' || string.charAt(string.length() - 2) == '/') {
-      return false;
-    }
-    int cp;
-    for (int i = 1; i < string.length() - 1; i += Character.charCount(cp)) {
-      cp = string.codePointAt(i);
-      if (cp != '/' && !IdentifierUtils.isRelaxedDexIdentifierPart(cp)) {
-        return false;
-      }
-    }
-    return true;
+    return com.android.tools.r8.keepanno.utils.DescriptorUtils.isValidClassDescriptor(string);
   }
 
   public static boolean isValidBinaryName(String binaryName) {
diff --git a/src/main/java/com/android/tools/r8/utils/FileUtils.java b/src/main/java/com/android/tools/r8/utils/FileUtils.java
index 9153858..72ac1a3 100644
--- a/src/main/java/com/android/tools/r8/utils/FileUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/FileUtils.java
@@ -31,6 +31,9 @@
   public static final String JAVA_EXTENSION = ".java";
   public static final String KT_EXTENSION = ".kt";
   public static final String MODULE_INFO_CLASS = "module-info.class";
+  public static final String KOTLIN_MODULE = ".kotlin_module";
+  public static final String KOTLIN_BUILTINS = ".kotlin_builtins";
+
   public static final String MODULES_PREFIX = "/modules";
   public static final String GLOBAL_SYNTHETIC_EXTENSION = ".global";
 
@@ -93,6 +96,14 @@
         || name.endsWith(AAR_EXTENSION);
   }
 
+  public static boolean isKotlinModuleFile(String name) {
+    return name.endsWith(KOTLIN_MODULE);
+  }
+
+  public static boolean isKotlinBuiltinsFile(String name) {
+    return name.endsWith(KOTLIN_BUILTINS);
+  }
+
   public static String readTextFile(Path file) throws IOException {
     return readTextFile(file, StandardCharsets.UTF_8);
   }
diff --git a/src/main/java/com/android/tools/r8/utils/IdentifierUtils.java b/src/main/java/com/android/tools/r8/utils/IdentifierUtils.java
index 5591c14..3acd909 100644
--- a/src/main/java/com/android/tools/r8/utils/IdentifierUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/IdentifierUtils.java
@@ -24,29 +24,10 @@
   }
 
   public static boolean isUnicodeSpace(int cp) {
-    // Unicode 'Zs' category
-    return cp == ' '
-        || cp == 0x00a0
-        || cp == 0x1680
-        || (0x2000 <= cp && cp <= 0x200a)
-        || cp == 0x202f
-        || cp == 0x205f
-        || cp == 0x3000;
+    return com.android.tools.r8.keepanno.utils.IdentifierUtils.isUnicodeSpace(cp);
   }
 
   private static boolean isSimpleNameChar(int cp) {
-    // See https://source.android.com/devices/tech/dalvik/dex-format#string-syntax.
-    return ('A' <= cp && cp <= 'Z')
-        || ('a' <= cp && cp <= 'z')
-        || ('0' <= cp && cp <= '9')
-        || cp == '$'
-        || cp == '-'
-        || cp == '_'
-        || (0x00a1 <= cp && cp <= 0x1fff)
-        || (0x2010 <= cp && cp <= 0x2027)
-        || (0x2030 <= cp && cp <= 0xd7ff)
-        || (0xe000 <= cp && cp < 0xfeff) // Don't include BOM.
-        || (0xfeff < cp && cp <= 0xffef)
-        || (0x10000 <= cp && cp <= 0x10ffff);
+    return com.android.tools.r8.keepanno.utils.IdentifierUtils.isSimpleNameChar(cp);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 07fd942..6bedcb4 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1683,6 +1683,9 @@
     // See b/182065081 for why this is here.
     public boolean lambdaClassFieldsFinal =
         System.getProperty("com.android.tools.r8.lambdaClassFieldsNotFinal") == null;
+    public boolean minimizeSyntheticNames =
+        SystemPropertyUtils.parseSystemPropertyOrDefault(
+            "com.android.tools.r8.desugar.minimizeSyntheticNames", false);
   }
 
   public class RewriteArrayOptions {
diff --git a/src/main/java/com/android/tools/r8/utils/ZipUtils.java b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
index fb33e96..b77eaff 100644
--- a/src/main/java/com/android/tools/r8/utils/ZipUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.androidapi.AndroidApiDataAccess;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
 import com.google.common.io.ByteStreams;
 import com.google.common.io.Closer;
 import java.io.BufferedOutputStream;
@@ -392,4 +393,12 @@
     }
     return -1;
   }
+
+  public static ClassReference entryToClassReference(ZipEntry entry) {
+    if (!FileUtils.isClassFile(entry.getName())) {
+      return null;
+    }
+    return Reference.classFromBinaryName(
+        entry.getName().substring(0, entry.getName().length() - CLASS_EXTENSION.length()));
+  }
 }
diff --git a/src/test/bootstrap/com/android/tools/r8/bootstrap/SanityCheck.java b/src/test/bootstrap/com/android/tools/r8/bootstrap/SanityCheck.java
index d8429ea..3047ca7 100644
--- a/src/test/bootstrap/com/android/tools/r8/bootstrap/SanityCheck.java
+++ b/src/test/bootstrap/com/android/tools/r8/bootstrap/SanityCheck.java
@@ -16,6 +16,7 @@
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.ZipUtils;
 import com.google.common.collect.Sets;
 import java.io.IOException;
@@ -70,7 +71,7 @@
     while (entries.hasMoreElements()) {
       ZipEntry entry = entries.nextElement();
       String name = entry.getName();
-      if (ZipUtils.isClassFile(name) || name.endsWith(".kotlin_builtins")) {
+      if (ZipUtils.isClassFile(name) || FileUtils.isKotlinBuiltinsFile(name)) {
         assertThat(name, startsWith("com/android/tools/r8/"));
       } else if (name.equals("META-INF/MANIFEST.MF")) {
         // Allow.
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingWithDifferentProfileFlagsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingWithDifferentProfileFlagsTest.java
new file mode 100644
index 0000000..a0b1bc5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingWithDifferentProfileFlagsTest.java
@@ -0,0 +1,128 @@
+// Copyright (c) 2025, 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.profile.art.ArtProfileMethodRuleInfoImpl;
+import com.android.tools.r8.profile.art.model.ExternalArtProfile;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ConstructorMergingWithDifferentProfileFlagsTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters)
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addArtProfileForRewriting(
+            ExternalArtProfile.builder()
+                .addMethodRule(
+                    MethodReferenceUtils.instanceConstructor(
+                        Reference.classFromClass(A.class), Reference.INT),
+                    ArtProfileMethodRuleInfoImpl.builder().setIsHot().build())
+                .addMethodRule(
+                    MethodReferenceUtils.instanceConstructor(
+                        Reference.classFromClass(B.class), Reference.INT),
+                    ArtProfileMethodRuleInfoImpl.builder().setIsPostStartup().build())
+                .addMethodRule(
+                    MethodReferenceUtils.instanceConstructor(
+                        Reference.classFromClass(C.class), Reference.INT),
+                    ArtProfileMethodRuleInfoImpl.builder().setIsStartup().build())
+                .build())
+        .addHorizontallyMergedClassesInspector(
+            inspector ->
+                inspector
+                    .assertIsCompleteMergeGroup(A.class, B.class, C.class)
+                    .assertNoOtherClassesMerged())
+        .enableInliningAnnotations()
+        .enableNeverClassInliningAnnotations()
+        .compile()
+        .inspectResidualArtProfile(
+            (profile, inspector) -> {
+              ClassSubject classSubject = inspector.clazz(A.class);
+              assertThat(classSubject, isPresent());
+
+              MethodSubject switchInitializerSubject = classSubject.init("int", "int");
+              assertThat(switchInitializerSubject, isPresent());
+              assertTrue(
+                  switchInitializerSubject
+                      .streamInstructions()
+                      .anyMatch(InstructionSubject::isSwitch));
+
+              profile
+                  .assertContainsMethodRule(switchInitializerSubject)
+                  .inspectMethodRule(
+                      switchInitializerSubject,
+                      methodRuleInspector ->
+                          methodRuleInspector
+                              .assertIsHot()
+                              .assertIsPostStartup()
+                              .assertIsStartup());
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("A.f=0", "B.f=0", "C.f=0");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      new A(args.length);
+      new B(args.length);
+      new C(args.length);
+    }
+  }
+
+  @NeverClassInline
+  static class A {
+
+    @NeverInline
+    A(int f) {
+      System.out.println("A.f=" + f);
+    }
+  }
+
+  @NeverClassInline
+  static class B {
+
+    @NeverInline
+    B(int f) {
+      System.out.println("B.f=" + f);
+    }
+  }
+
+  @NeverClassInline
+  static class C {
+
+    @NeverInline
+    C(int f) {
+      System.out.println("C.f=" + f);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithDifferentProfileFlagsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithDifferentProfileFlagsTest.java
new file mode 100644
index 0000000..775e2f8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithDifferentProfileFlagsTest.java
@@ -0,0 +1,138 @@
+// Copyright (c) 2025, 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.profile.art.ArtProfileMethodRuleInfoImpl;
+import com.android.tools.r8.profile.art.model.ExternalArtProfile;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class EquivalentConstructorsWithDifferentProfileFlagsTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters)
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addArtProfileForRewriting(
+            ExternalArtProfile.builder()
+                .addMethodRule(
+                    MethodReferenceUtils.instanceConstructor(
+                        Reference.classFromClass(A.class), Reference.INT),
+                    ArtProfileMethodRuleInfoImpl.builder().setIsHot().build())
+                .addMethodRule(
+                    MethodReferenceUtils.instanceConstructor(
+                        Reference.classFromClass(B.class), Reference.INT),
+                    ArtProfileMethodRuleInfoImpl.builder().setIsPostStartup().build())
+                .addMethodRule(
+                    MethodReferenceUtils.instanceConstructor(
+                        Reference.classFromClass(C.class), Reference.INT),
+                    ArtProfileMethodRuleInfoImpl.builder().setIsStartup().build())
+                .build())
+        .addHorizontallyMergedClassesInspector(
+            inspector ->
+                inspector
+                    .assertIsCompleteMergeGroup(A.class, B.class, C.class)
+                    .assertNoOtherClassesMerged())
+        .enableInliningAnnotations()
+        .compile()
+        .inspectResidualArtProfile(
+            (profile, inspector) -> {
+              ClassSubject classSubject = inspector.clazz(A.class);
+              assertThat(classSubject, isPresent());
+
+              MethodSubject initializerSubject = classSubject.init("int", "int");
+              assertThat(initializerSubject, isPresent());
+
+              profile
+                  .assertContainsMethodRule(initializerSubject)
+                  .inspectMethodRule(
+                      initializerSubject,
+                      methodRuleInspector ->
+                          methodRuleInspector
+                              .assertIsHot()
+                              .assertIsPostStartup()
+                              .assertIsStartup());
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("f=0", "f=0", "f=0");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      System.out.println(new A(args.length));
+      System.out.println(new B(args.length));
+      System.out.println(new C(args.length));
+    }
+  }
+
+  static class A {
+
+    int f;
+
+    @NeverInline
+    A(int f) {
+      this.f = f;
+    }
+
+    @Override
+    public String toString() {
+      return "f=" + f;
+    }
+  }
+
+  static class B {
+
+    int f;
+
+    @NeverInline
+    B(int f) {
+      this.f = f;
+    }
+
+    @Override
+    public String toString() {
+      return "f=" + f;
+    }
+  }
+
+  static class C {
+
+    int f;
+
+    @NeverInline
+    C(int f) {
+      this.f = f;
+    }
+
+    @Override
+    public String toString() {
+      return "f=" + f;
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingVirtualMethodMergingWithLibraryTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingVirtualMethodMergingWithLibraryTest.java
index e44992d..7861ac8 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingVirtualMethodMergingWithLibraryTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingVirtualMethodMergingWithLibraryTest.java
@@ -50,11 +50,7 @@
         .enableNoVerticalClassMergingAnnotations()
         .compile()
         .run(parameters.getRuntime(), Main.class)
-        .applyIf(
-            parameters.canUseDefaultAndStaticInterfaceMethods(),
-            // TODO(b/369040938): Disallow bridge hoisting of B.m().
-            rr -> rr.assertSuccessWithOutputLines("A.bridgeTarget()", "A.bridgeTarget()"),
-            rr -> rr.assertSuccessWithOutputLines("A.bridgeTarget()", "I.m()"));
+        .assertSuccessWithOutputLines("A.bridgeTarget()", "I.m()");
   }
 
   static class Main {
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/JavaLambdaMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/JavaLambdaMergingTest.java
index b156a3c..1427b8c 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/JavaLambdaMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/JavaLambdaMergingTest.java
@@ -24,6 +24,7 @@
 
   @Test
   public void test() throws Exception {
+    parameters.assumeDexRuntime();
     testForR8(parameters.getBackend())
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/UndoConstructorInliningProfileFlagsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/UndoConstructorInliningProfileFlagsTest.java
new file mode 100644
index 0000000..68c15f2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/UndoConstructorInliningProfileFlagsTest.java
@@ -0,0 +1,118 @@
+// Copyright (c) 2025, 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.profile.art.ArtProfileMethodRuleInfoImpl;
+import com.android.tools.r8.profile.art.model.ExternalArtProfile;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class UndoConstructorInliningProfileFlagsTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntimesAndAllApiLevels().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    assumeTrue(parameters.canInitNewInstanceUsingSuperclassConstructor());
+    testForR8(parameters)
+        .addInnerClasses(getClass())
+        .addKeepClassAndMembersRules(Main.class)
+        .addArtProfileForRewriting(
+            ExternalArtProfile.builder()
+                .addMethodRule(
+                    Reference.methodFromMethod(Main.class.getDeclaredMethod("hot")),
+                    ArtProfileMethodRuleInfoImpl.builder().setIsHot().build())
+                .addMethodRule(
+                    Reference.methodFromMethod(Main.class.getDeclaredMethod("postStartup")),
+                    ArtProfileMethodRuleInfoImpl.builder().setIsPostStartup().build())
+                .addMethodRule(
+                    Reference.methodFromMethod(Main.class.getDeclaredMethod("startup")),
+                    ArtProfileMethodRuleInfoImpl.builder().setIsStartup().build())
+                .build())
+        .addHorizontallyMergedClassesInspector(
+            inspector ->
+                inspector.assertIsCompleteMergeGroup(A.class, B.class).assertNoOtherClassesMerged())
+        .addOptionsModification(options -> options.getSingleCallerInlinerOptions().setEnable(false))
+        .compile()
+        .inspectResidualArtProfile(
+            (profile, inspector) -> {
+              ClassSubject classSubject = inspector.clazz(A.class);
+              assertThat(classSubject, isPresent());
+
+              MethodSubject initializerSubject = classSubject.init("int");
+              assertThat(initializerSubject, isPresent());
+
+              profile
+                  .assertContainsMethodRule(initializerSubject)
+                  .inspectMethodRule(
+                      initializerSubject,
+                      methodRuleInspector ->
+                          methodRuleInspector
+                              .assertIsHot()
+                              .assertIsPostStartup()
+                              .assertIsStartup());
+            });
+  }
+
+  static class Main {
+
+    static void hot() {
+      System.out.println(new A());
+      System.out.println(new B());
+    }
+
+    static void postStartup() {
+      System.out.println(new A());
+      System.out.println(new B());
+    }
+
+    static void startup() {
+      System.out.println(new A());
+      System.out.println(new B());
+    }
+  }
+
+  // Needed to ensure that all merge classes are in the same SCC.
+  static class Base {}
+
+  static class A extends Base {
+
+    A() {}
+
+    @Override
+    public String toString() {
+      return "A";
+    }
+  }
+
+  static class B extends Base {
+
+    B() {}
+
+    @Override
+    public String toString() {
+      return "B";
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DayOfWeekTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DayOfWeekTest.java
new file mode 100644
index 0000000..64c20c7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DayOfWeekTest.java
@@ -0,0 +1,80 @@
+// Copyright (c) 2025, 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;
+
+import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.DEFAULT_SPECIFICATIONS;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.getJdk8Jdk11;
+
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
+import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.IsoFields;
+import java.time.temporal.TemporalAccessor;
+import java.util.List;
+import java.util.Locale;
+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 DayOfWeekTest extends DesugaredLibraryTestBase {
+  private final TestParameters parameters;
+  private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+  private final CompilationSpecification compilationSpecification;
+
+  @Parameters(name = "{0}, spec: {1}, {2}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimes().withAllApiLevels().build(),
+        getJdk8Jdk11(),
+        DEFAULT_SPECIFICATIONS);
+  }
+
+  public DayOfWeekTest(
+      TestParameters parameters,
+      LibraryDesugaringSpecification libraryDesugaringSpecification,
+      CompilationSpecification compilationSpecification) {
+    this.parameters = parameters;
+    this.libraryDesugaringSpecification = libraryDesugaringSpecification;
+    this.compilationSpecification = compilationSpecification;
+  }
+
+  @Test
+  public void testDay() throws Exception {
+    if (parameters.isCfRuntime()) {
+      testForJvm(parameters)
+          .addInnerClasses(DayOfWeekTest.class)
+          .run(parameters.getRuntime(), Main.class)
+          .assertSuccessWithOutputLines("6", "7");
+      return;
+    }
+    testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .run(parameters.getRuntime(), Main.class)
+        .applyIf(
+            // TODO(b/432689492): Fix non standalone names.
+            libraryDesugaringSpecification == LibraryDesugaringSpecification.JDK8
+                || parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.O),
+            b -> b.assertSuccessWithOutputLines("6", "7"),
+            SingleTestRunResult::assertFailure);
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      DateTimeFormatter formatter =
+          DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
+      TemporalAccessor access1 = formatter.parse("Sun, 06 Jul 2025 10:07:45 GMT");
+      System.out.println(access1.get(IsoFields.DAY_OF_QUARTER));
+      TemporalAccessor access2 = formatter.parse("Mon, 07 Jul 2025 10:07:45 GMT");
+      System.out.println(access2.get(IsoFields.DAY_OF_QUARTER));
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaMinimizeSyntheticNamesTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaMinimizeSyntheticNamesTest.java
new file mode 100644
index 0000000..11a51e5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaMinimizeSyntheticNamesTest.java
@@ -0,0 +1,73 @@
+// Copyright (c) 2025, 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.lambdas;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertFalse;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class LambdaMinimizeSyntheticNamesTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntimesAndAllApiLevels().build();
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    testForD8(parameters)
+        .addInnerClasses(getClass())
+        .addOptionsModification(this::configure)
+        .release()
+        .compile()
+        .inspect(this::inspect);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters)
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addDontObfuscate()
+        .addOptionsModification(this::configure)
+        .compile()
+        .inspect(this::inspect);
+  }
+
+  private void configure(InternalOptions options) {
+    assertFalse(options.desugarSpecificOptions().minimizeSyntheticNames);
+    options.desugarSpecificOptions().minimizeSyntheticNames = true;
+  }
+
+  private void inspect(CodeInspector inspector) {
+    ClassSubject lambdaClass =
+        inspector.clazz(SyntheticItemsTestUtils.syntheticClassWithMinimalName(Main.class, 0));
+    assertThat(lambdaClass, isPresentAndNotRenamed());
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      Runnable runnable = () -> {};
+      System.out.println(runnable);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBuilder.java b/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBuilder.java
index 282b101..74380cf 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBuilder.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.ExternalR8TestBuilder;
 import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
 import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
+import com.android.tools.r8.ProgramResourceProvider;
 import com.android.tools.r8.ProguardTestBuilder;
 import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.R8PartialTestBuilder;
@@ -89,6 +90,9 @@
 
   public abstract KeepAnnoTestBuilder addProgramFiles(List<Path> programFiles) throws IOException;
 
+  public abstract KeepAnnoTestBuilder addProgramResourceProviders(
+      ProgramResourceProvider... providers);
+
   public final KeepAnnoTestBuilder addProgramClasses(Class<?>... programClasses)
       throws IOException {
     return addProgramClasses(Arrays.asList(programClasses));
@@ -126,6 +130,14 @@
 
   public abstract SingleTestRunResult<?> run(String mainClass) throws Exception;
 
+  public KeepAnnoTestBuilder applyIf(
+      boolean condition, ThrowableConsumer<KeepAnnoTestBuilder> consumer) {
+    if (condition) {
+      consumer.acceptWithRuntimeException(this);
+    }
+    return this;
+  }
+
   public KeepAnnoTestBuilder applyIfShrinker(
       ThrowableConsumer<TestShrinkerBuilder<?, ?, ?, ?, ?>> builderConsumer) {
     applyIfR8(builderConsumer);
@@ -222,6 +234,12 @@
     }
 
     @Override
+    public KeepAnnoTestBuilder addProgramResourceProviders(ProgramResourceProvider... providers) {
+      assert false : "not supported";
+      return this;
+    }
+
+    @Override
     public KeepAnnoTestBuilder addProgramClasses(List<Class<?>> programClasses) {
       builder.addProgramClasses(programClasses);
       return this;
@@ -343,6 +361,12 @@
     }
 
     @Override
+    public KeepAnnoTestBuilder addProgramResourceProviders(ProgramResourceProvider... providers) {
+      builder.addProgramResourceProviders(Arrays.asList(providers));
+      return this;
+    }
+
+    @Override
     public KeepAnnoTestBuilder addProgramClasses(List<Class<?>> programClasses) throws IOException {
       for (Class<?> programClass : programClasses) {
         extractAndAdd(ToolHelper.getClassAsBytes(programClass));
@@ -561,6 +585,12 @@
     }
 
     @Override
+    public KeepAnnoTestBuilder addProgramResourceProviders(ProgramResourceProvider... providers) {
+      builder.addProgramResourceProviders(Arrays.asList(providers));
+      return this;
+    }
+
+    @Override
     public KeepAnnoTestBuilder addProgramClasses(List<Class<?>> programClasses) throws IOException {
       List<String> rules = KeepAnnoTestUtils.extractRules(programClasses, extractorOptions);
       builder.addProgramClasses(programClasses);
@@ -659,6 +689,12 @@
     }
 
     @Override
+    public KeepAnnoTestBuilder addProgramResourceProviders(ProgramResourceProvider... providers) {
+      builder.addProgramResourceProviders(Arrays.asList(providers));
+      return this;
+    }
+
+    @Override
     public KeepAnnoTestBuilder addProgramClasses(List<Class<?>> programClasses) throws IOException {
       List<String> rules = KeepAnnoTestUtils.extractRules(programClasses, extractorOptions);
       builder.addProgramClasses(programClasses);
diff --git a/src/test/java/com/android/tools/r8/keepanno/androidx/KeepAnnoTestExtractedRulesBase.java b/src/test/java/com/android/tools/r8/keepanno/androidx/KeepAnnoTestExtractedRulesBase.java
index 4103ea9..2497a5b 100644
--- a/src/test/java/com/android/tools/r8/keepanno/androidx/KeepAnnoTestExtractedRulesBase.java
+++ b/src/test/java/com/android/tools/r8/keepanno/androidx/KeepAnnoTestExtractedRulesBase.java
@@ -3,23 +3,42 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.keepanno.androidx;
 
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assume.assumeFalse;
 
+import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.DataResourceProvider;
 import com.android.tools.r8.KotlinCompileMemoizer;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
 import com.android.tools.r8.KotlinTestParameters;
-import com.android.tools.r8.R8TestBuilder;
+import com.android.tools.r8.ProgramResource;
+import com.android.tools.r8.ProgramResourceProvider;
+import com.android.tools.r8.ResourceException;
 import com.android.tools.r8.keepanno.KeepAnnoParameters;
 import com.android.tools.r8.keepanno.KeepAnnoTestBase;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.transformers.ClassFileTransformer;
+import com.android.tools.r8.transformers.ClassFileTransformer.AnnotationBuilder;
+import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.ZipUtils;
 import com.google.common.collect.ImmutableList;
+import com.google.common.io.ByteStreams;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
 import java.util.stream.Collectors;
 import org.junit.runners.Parameterized.Parameter;
 
 public abstract class KeepAnnoTestExtractedRulesBase extends KeepAnnoTestBase {
+
   @Parameter(0)
   public KeepAnnoParameters parameters;
 
@@ -29,36 +48,120 @@
   protected static KotlinCompileMemoizer compilationResults;
   protected static KotlinCompileMemoizer compilationResultsClassName;
 
-  protected abstract String getExpectedOutput();
+  protected String getExpectedOutput() {
+    assert false
+        : "implement either this or both of getExpectedOutputForJava and"
+            + " getExpectedOutputForKotlin";
+    return null;
+  }
+
+  protected String getExpectedOutputForJava() {
+    return getExpectedOutput();
+  }
+
+  protected String getExpectedOutputForKotlin() {
+    return getExpectedOutput();
+  }
 
   protected static List<String> trimRules(List<String> rules) {
     List<String> trimmedRules =
         rules.stream()
             .flatMap(s -> Arrays.stream(s.split("\n")))
             .filter(rule -> !rule.startsWith("#"))
+            .sorted()
+            .distinct()
             .collect(Collectors.toList());
     return trimmedRules;
   }
 
-  public static class ExpectedRule {
+  protected static byte[] setAnnotationOnClass(
+      ClassFileTransformer transformer,
+      Class<?> annotationClass,
+      Consumer<AnnotationBuilder> builderConsumer) {
+    return transformer.setAnnotation(annotationClass, builderConsumer).transform();
+  }
+
+  protected static byte[] setAnnotationOnMethod(
+      ClassFileTransformer transformer,
+      MethodPredicate methodPredicate,
+      Class<?> annotationClass,
+      Consumer<AnnotationBuilder> builderConsumer) {
+    return transformer.setAnnotation(methodPredicate, annotationClass, builderConsumer).transform();
+  }
+
+  protected static byte[] setAnnotationOnClass(
+      Class<?> clazz, Class<?> annotationClass, Consumer<AnnotationBuilder> builderConsumer)
+      throws IOException {
+    return setAnnotationOnClass(transformer(clazz), annotationClass, builderConsumer);
+  }
+
+  protected static byte[] setAnnotationOnMethod(
+      Class<?> clazz,
+      MethodPredicate methodPredicate,
+      Class<?> annotationClass,
+      Consumer<AnnotationBuilder> builderConsumer)
+      throws IOException {
+    return setAnnotationOnMethod(
+        transformer(clazz), methodPredicate, annotationClass, builderConsumer);
+  }
+
+  protected static byte[] setAnnotationOnClass(
+      ClassReference classReference,
+      byte[] classFileBytes,
+      ClassReference classReferenceToTransform,
+      Class<?> annotationClass,
+      Consumer<AnnotationBuilder> builderConsumer) {
+    if (!classReference.equals(classReferenceToTransform)) {
+      return classFileBytes;
+    }
+    return setAnnotationOnClass(
+        transformer(classFileBytes, classReference), annotationClass, builderConsumer);
+  }
+
+  protected static byte[] setAnnotationOnMethod(
+      ClassReference classReference,
+      byte[] classFileBytes,
+      ClassReference classReferenceToTransform,
+      MethodPredicate methodPredicate,
+      Class<?> annotationClass,
+      Consumer<AnnotationBuilder> builderConsumer) {
+    if (!classReference.equals(classReferenceToTransform)) {
+      return classFileBytes;
+    }
+    return setAnnotationOnMethod(
+        transformer(classFileBytes, classReference),
+        methodPredicate,
+        annotationClass,
+        builderConsumer);
+  }
+
+  public abstract static class ExpectedRule {
+    public abstract String getRule(boolean r8);
+  }
+
+  public static class ExpectedKeepRule extends ExpectedRule {
+
+    private final String keepVariant;
     private final String conditionClass;
     private final String conditionMembers;
     private final String consequentClass;
     private final String consequentMembers;
 
-    private ExpectedRule(Builder builder) {
+    private ExpectedKeepRule(Builder builder) {
+      this.keepVariant = builder.keepVariant;
       this.conditionClass = builder.conditionClass;
       this.conditionMembers = builder.conditionMembers;
       this.consequentClass = builder.consequentClass;
       this.consequentMembers = builder.consequentMembers;
     }
 
+    @Override
     public String getRule(boolean r8) {
       return "-if class "
           + conditionClass
+          + (conditionMembers != null ? " " + conditionMembers : "")
           + " "
-          + conditionMembers
-          + " -keepclasseswithmembers"
+          + keepVariant
           + (r8 ? ",allowaccessmodification" : "")
           + " class "
           + consequentClass
@@ -71,6 +174,8 @@
     }
 
     public static class Builder {
+
+      private String keepVariant = "-keepclasseswithmembers";
       private String conditionClass;
       private String conditionMembers;
       private String consequentClass;
@@ -78,6 +183,11 @@
 
       private Builder() {}
 
+      public Builder setKeepVariant(String keepVariant) {
+        this.keepVariant = keepVariant;
+        return this;
+      }
+
       public Builder setConditionClass(Class<?> conditionClass) {
         this.conditionClass = conditionClass.getTypeName();
         return this;
@@ -108,85 +218,305 @@
         return this;
       }
 
-      public ExpectedRule build() {
-        return new ExpectedRule(this);
+      public Builder apply(Consumer<Builder> fn) {
+        fn.accept(this);
+        return this;
+      }
+
+      public ExpectedKeepRule build() {
+        return new ExpectedKeepRule(this);
       }
     }
   }
 
-  protected void runTestExtractedRulesJava(List<Class<?>> testClasses, ExpectedRule expectedRule)
+  public static class ExpectedKeepAttributesRule extends ExpectedRule {
+
+    private final boolean runtimeVisibleAnnotations;
+    private final boolean runtimeVisibleParameterAnnotations;
+    private final boolean runtimeVisibleTypeAnnotations;
+
+    private ExpectedKeepAttributesRule(Builder builder) {
+      this.runtimeVisibleAnnotations = builder.runtimeVisibleAnnotations;
+      this.runtimeVisibleParameterAnnotations = builder.runtimeVisibleParameterAnnotations;
+      this.runtimeVisibleTypeAnnotations = builder.runtimeVisibleTypeAnnotations;
+    }
+
+    @Override
+    public String getRule(boolean r8) {
+      List<String> attributes = new ArrayList<>();
+      if (runtimeVisibleAnnotations) {
+        attributes.add(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS);
+      }
+      if (runtimeVisibleParameterAnnotations) {
+        attributes.add(ProguardKeepAttributes.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS);
+      }
+      if (runtimeVisibleTypeAnnotations) {
+        attributes.add(ProguardKeepAttributes.RUNTIME_VISIBLE_TYPE_ANNOTATIONS);
+      }
+      return "-keepattributes " + String.join(",", attributes);
+    }
+
+    public static Builder builder() {
+      return new Builder();
+    }
+
+    public static ExpectedKeepAttributesRule buildAllRuntimeVisibleAnnotations() {
+      return builder()
+          .setRuntimeVisibleAnnotations()
+          .setRuntimeVisibleParameterAnnotations()
+          .setRuntimeVisibleTypeAnnotations()
+          .build();
+    }
+
+    public static class Builder {
+
+      private boolean runtimeVisibleAnnotations = false;
+      private boolean runtimeVisibleParameterAnnotations = false;
+      private boolean runtimeVisibleTypeAnnotations = false;
+
+      public Builder setRuntimeVisibleAnnotations() {
+        this.runtimeVisibleAnnotations = true;
+        return this;
+      }
+
+      public Builder setRuntimeVisibleParameterAnnotations() {
+        this.runtimeVisibleParameterAnnotations = true;
+        return this;
+      }
+
+      public Builder setRuntimeVisibleTypeAnnotations() {
+        this.runtimeVisibleTypeAnnotations = true;
+        return this;
+      }
+
+      public ExpectedKeepAttributesRule build() {
+        return new ExpectedKeepAttributesRule(this);
+      }
+    }
+  }
+
+  public static class ExpectedRules {
+
+    private final ImmutableList<ExpectedRule> rules;
+
+    private ExpectedRules(Builder builder) {
+      this.rules = builder.rules.build();
+    }
+
+    public ImmutableList<String> getRules(boolean r8) {
+      return rules.stream()
+          .map(rule -> rule.getRule(r8))
+          .sorted()
+          .collect(ImmutableList.toImmutableList());
+    }
+
+    public static Builder builder() {
+      return new Builder();
+    }
+
+    public static ExpectedRules singleRule(ExpectedRule rule) {
+      return new Builder().add(rule).build();
+    }
+
+    public static class Builder {
+
+      private final ImmutableList.Builder<ExpectedRule> rules = ImmutableList.builder();
+
+      public Builder add(ExpectedRule rule) {
+        rules.add(rule);
+        return this;
+      }
+
+      public Builder apply(Consumer<Builder> fn) {
+        fn.accept(this);
+        return this;
+      }
+
+      public ExpectedRules build() {
+        return new ExpectedRules(this);
+      }
+    }
+  }
+
+  // Add the expected rules for kotlin.Metadata (the class with members and the required
+  // attributes).
+  protected static void addConsequentKotlinMetadata(
+      ExpectedRules.Builder b, Consumer<ExpectedKeepRule.Builder> fn) {
+    b.add(ExpectedKeepAttributesRule.buildAllRuntimeVisibleAnnotations())
+        .add(
+            ExpectedKeepRule.builder()
+                .setKeepVariant("-keep")
+                .setConsequentClass("kotlin.Metadata")
+                .setConsequentMembers("{ *; }")
+                .apply(fn)
+                .build());
+  }
+
+  static class ArchiveResourceProviderClassFilesOnly
+      implements ProgramResourceProvider, DataResourceProvider {
+
+    private final List<ProgramResource> programResources = new ArrayList<>();
+    private final List<DataEntryResource> dataResources = new ArrayList<>();
+
+    ArchiveResourceProviderClassFilesOnly(Path path) throws ResourceException {
+      try {
+        ZipUtils.iter(
+            path,
+            (entry, inputStream) -> {
+              if (ZipUtils.isClassFile(entry.getName())) {
+                programResources.add(
+                    ProgramResource.fromBytes(
+                        Origin.unknown(),
+                        ProgramResource.Kind.CF,
+                        ByteStreams.toByteArray(inputStream),
+                        Collections.singleton(
+                            DescriptorUtils.guessTypeDescriptor(entry.getName()))));
+              } else if (FileUtils.isKotlinModuleFile(entry.getName())) {
+                dataResources.add(
+                    DataEntryResource.fromBytes(
+                        ByteStreams.toByteArray(inputStream), entry.getName(), Origin.unknown()));
+              } else if (FileUtils.isKotlinBuiltinsFile(entry.getName())) {
+                dataResources.add(
+                    DataEntryResource.fromBytes(
+                        ByteStreams.toByteArray(inputStream), entry.getName(), Origin.unknown()));
+              }
+            });
+      } catch (IOException e) {
+        throw new ResourceException(Origin.unknown(), "Caught IOException", e);
+      }
+    }
+
+    @Override
+    public Collection<ProgramResource> getProgramResources() throws ResourceException {
+      return programResources;
+    }
+
+    @Override
+    public DataResourceProvider getDataResourceProvider() {
+      return this;
+    }
+
+    @Override
+    public void accept(Visitor visitor) throws ResourceException {
+      dataResources.forEach(visitor::visit);
+    }
+  }
+
+  protected void runTestExtractedRulesJava(
+      Class<?> mainClass,
+      List<Class<?>> testClasses,
+      List<byte[]> testClassFileData,
+      ExpectedRules expectedRules)
       throws Exception {
-    Class<?> mainClass = testClasses.iterator().next();
+    // TODO(b/392865072): Proguard 7.4.1 fails with "Encountered corrupt @kotlin/Metadata for class
+    // <binary name> (version 2.1.0)", as ti avoid missing classes warnings from ProGuard some of
+    // the Kotlin libraries has to be included.
+    assumeFalse(parameters.isPG());
     testForKeepAnnoAndroidX(parameters)
-        .applyIfPG(
-            b -> {
-              KotlinCompiler kotlinc =
-                  new KotlinCompiler(KotlinCompilerVersion.MAX_SUPPORTED_VERSION);
-              b.addLibraryFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar());
-            })
         .addProgramClasses(testClasses)
+        .addProgramClassFileData(testClassFileData)
         .addKeepMainRule(mainClass)
         .setExcludedOuterClass(getClass())
         .inspectExtractedRules(
             rules -> {
               if (parameters.isExtractRules()) {
-                assertEquals(
-                    ImmutableList.of(expectedRule.getRule(parameters.isR8())), trimRules(rules));
+                assertListsAreEqual(expectedRules.getRules(parameters.isR8()), trimRules(rules));
               }
             })
         .run(mainClass)
-        .assertSuccessWithOutput(getExpectedOutput());
+        .assertSuccessWithOutput(getExpectedOutputForJava());
+  }
+
+  protected void runTestExtractedRulesJava(List<Class<?>> testClasses, ExpectedRules expectedRules)
+      throws Exception {
+    runTestExtractedRulesJava(
+        testClasses.iterator().next(), testClasses, ImmutableList.of(), expectedRules);
   }
 
   protected void runTestExtractedRulesKotlin(
-      KotlinCompileMemoizer compilation, String mainClass, ExpectedRule expectedRule)
+      KotlinCompileMemoizer compilation,
+      List<byte[]> classFileData,
+      String mainClass,
+      ExpectedRules expectedRules)
       throws Exception {
     // TODO(b/392865072): Legacy R8 fails with AssertionError: Synthetic class kinds should agree.
     assumeFalse(parameters.isLegacyR8());
     // TODO(b/392865072): Reference fails with AssertionError: Built-in class kotlin.Any is not
     // found (in kotlin.reflect code).
     assumeFalse(parameters.isReference());
-    // TODO(b/392865072): Proguard 7.4.1 fails with "Encountered corrupt @kotlin/Metadata for class
-    // <binary name> (version 2.1.0)".
+    // TODO(b/392865072): Proguard 7.7.0 compiled code fails with fails with
+    //  "java.lang.annotation.IncompleteAnnotationException: kotlin.Metadata missing element bv".
     assumeFalse(parameters.isPG());
     testForKeepAnnoAndroidX(parameters)
-        .addProgramFiles(ImmutableList.of(compilation.getForConfiguration(kotlinParameters)))
-        .addProgramFilesWithoutAnnotations(
-            ImmutableList.of(
-                kotlinParameters.getCompiler().getKotlinStdlibJar(),
-                kotlinParameters.getCompiler().getKotlinReflectJar(),
+        .applyIf(
+            compilation != null,
+            b ->
+                b.addProgramFiles(
+                    ImmutableList.of(compilation.getForConfiguration(kotlinParameters))))
+        .applyIfPG(
+            b ->
+                b.addProgramFiles(
+                        ImmutableList.of(
+                            kotlinParameters.getCompiler().getKotlinStdlibJar(),
+                            kotlinParameters.getCompiler().getKotlinReflectJar(),
+                            kotlinParameters.getCompiler().getKotlinAnnotationJar()))
+                    .addKeepEnumsRule())
+        // addProgramResourceProviders is not implemented for ProGuard, so effectively exclusive
+        // from addProgramFiles above. If this changed ProGuard will report duplicate classes.
+        .addProgramResourceProviders(
+            // Only add class files from Kotlin libraries to ensure the keep annotations does not
+            // rely on rules like `-keepattributes RuntimeVisibleAnnotations` and
+            // `-keep class kotlin.Metadata { *; }` but are self-contained.
+            new ArchiveResourceProviderClassFilesOnly(
+                kotlinParameters.getCompiler().getKotlinStdlibJar()),
+            new ArchiveResourceProviderClassFilesOnly(
+                kotlinParameters.getCompiler().getKotlinReflectJar()),
+            new ArchiveResourceProviderClassFilesOnly(
                 kotlinParameters.getCompiler().getKotlinAnnotationJar()))
-        .applyIfR8Current(R8TestBuilder::allowDiagnosticWarningMessages)
-        .addKeepRules("-keepattributes RuntimeVisibleAnnotations")
-        .addKeepRules("-keep class kotlin.Metadata { *; }")
+        .addProgramClassFileData(classFileData)
+        // TODO(b/323816623): With native interpretation kotlin.Metadata still gets stripped
+        //  without -keepattributes RuntimeVisibleAnnotations`.
+        .applyIfR8(
+            b ->
+                b.applyIf(
+                    parameters.isNativeR8(),
+                    bb -> bb.addKeepRules("-keepattributes RuntimeVisibleAnnotations")))
+        // Keep the main entry point for the test.
         .addKeepRules(
             "-keep class " + mainClass + " { public static void main(java.lang.String[]); }")
-        .applyIfR8OrR8Partial(
-            b ->
-                b.addOptionsModification(
-                    options -> {
-                      options.testing.allowedUnusedDontWarnPatterns.add(
-                          "kotlin.reflect.jvm.internal.**");
-                      options.testing.allowedUnusedDontWarnPatterns.add("java.lang.ClassValue");
-                    }),
-            b ->
-                b.addR8PartialR8OptionsModification(
-                    options -> {
-                      options.testing.allowedUnusedDontWarnPatterns.add(
-                          "kotlin.reflect.jvm.internal.**");
-                      options.testing.allowedUnusedDontWarnPatterns.add("java.lang.ClassValue");
-                    }))
         .inspectExtractedRules(
             rules -> {
               if (parameters.isExtractRules()) {
-                assertEquals(
-                    ImmutableList.of(expectedRule.getRule(parameters.isR8())), trimRules(rules));
+                assertListsAreEqual(expectedRules.getRules(parameters.isR8()), trimRules(rules));
               }
             })
         .run(mainClass)
-        .applyIf(
-            parameters.isExtractRules(),
-            b -> b.assertSuccessWithOutput(getExpectedOutput()),
-            b -> b.assertSuccessWithOutput(""));
+        .assertSuccessWithOutput(getExpectedOutputForKotlin());
+  }
+
+  protected void runTestExtractedRulesKotlin(
+      KotlinCompileMemoizer compilation, String mainClass, ExpectedRules expectedRules)
+      throws Exception {
+    runTestExtractedRulesKotlin(compilation, ImmutableList.of(), mainClass, expectedRules);
+  }
+
+  protected void runTestExtractedRulesKotlin(
+      KotlinCompileMemoizer compilation,
+      BiFunction<ClassReference, byte[], byte[]> transformerForClass,
+      String mainClass,
+      ExpectedRules expectedRules)
+      throws Exception {
+    List<byte[]> result = new ArrayList<>();
+    ZipUtils.iter(
+        compilation.getForConfiguration(kotlinParameters),
+        (entry, inputStream) -> {
+          ClassReference classReference = ZipUtils.entryToClassReference(entry);
+          if (classReference == null) {
+            return;
+          }
+          result.add(
+              transformerForClass.apply(classReference, ByteStreams.toByteArray(inputStream)));
+        });
+    runTestExtractedRulesKotlin(null, result, mainClass, expectedRules);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/keepanno/androidx/KeepUsesReflectionForInstantiationMultipleConstructorsTest.java b/src/test/java/com/android/tools/r8/keepanno/androidx/KeepUsesReflectionForInstantiationMultipleConstructorsTest.java
new file mode 100644
index 0000000..80ea899
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/androidx/KeepUsesReflectionForInstantiationMultipleConstructorsTest.java
@@ -0,0 +1,227 @@
+// Copyright (c) 2025, 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.keepanno.androidx;
+
+import static com.android.tools.r8.ToolHelper.getFilesInTestFolderRelativeToClass;
+import static org.junit.Assert.assertEquals;
+
+import androidx.annotation.keep.UsesReflectionToConstruct;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.function.Consumer;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class KeepUsesReflectionForInstantiationMultipleConstructorsTest
+    extends KeepAnnoTestExtractedRulesBase {
+
+  // String constant to be referenced from annotations.
+  static final String classNameOfKeptClass =
+      "com.android.tools.r8.keepanno.androidx.KeepUsesReflectionForInstantiationMultipleConstructorsTest$KeptClass";
+
+  @Parameterized.Parameters(name = "{0}, {1}")
+  public static Collection<Object[]> data() {
+    assertEquals(KeptClass.class.getTypeName(), classNameOfKeptClass);
+    return buildParameters(
+        createParameters(
+            getTestParameters()
+                .withDexRuntime(Version.V14_0_0)
+                .withDefaultCfRuntime()
+                .withMaximumApiLevel()
+                .build()),
+        getKotlinTestParameters().withLatestCompiler().build());
+  }
+
+  @Override
+  protected String getExpectedOutputForJava() {
+    return StringUtils.lines("<init>(int)", "<init>(long)");
+  }
+
+  @Override
+  protected String getExpectedOutputForKotlin() {
+    // Kotlin secondary constructors has to delegate to the primary constructor.
+    return StringUtils.lines(
+        "fun `<init>`(kotlin.Int): com.android.tools.r8.keepanno.androidx.kt.KeptClass",
+        "<init>()",
+        "<init>(Int)",
+        "fun `<init>`(kotlin.Long): com.android.tools.r8.keepanno.androidx.kt.KeptClass",
+        "<init>()",
+        "<init>(Long)");
+  }
+
+  private static Collection<Path> getKotlinSources() {
+    try {
+      return getFilesInTestFolderRelativeToClass(
+          KeepUsesReflectionForInstantiationMultipleConstructorsTest.class,
+          "kt",
+          "IntAndLongArgsConstructors.kt",
+          "KeptClass.kt");
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  private static Collection<Path> getKotlinSourcesClassName() {
+    try {
+      return getFilesInTestFolderRelativeToClass(
+          KeepUsesReflectionForInstantiationMultipleConstructorsTest.class,
+          "kt",
+          "IntAndLongArgsConstructorsClassName.kt",
+          "KeptClass.kt");
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    compilationResults = getCompileMemoizerWithKeepAnnoLib(getKotlinSources());
+    compilationResultsClassName = getCompileMemoizerWithKeepAnnoLib(getKotlinSourcesClassName());
+  }
+
+  private ExpectedRules expectedRulesJava(Class<?> conditionClass) {
+    Consumer<ExpectedKeepRule.Builder> setCondition =
+        b ->
+            b.setConditionClass(conditionClass)
+                .setConditionMembers("{ void foo(java.lang.Class); }");
+    return ExpectedRules.builder()
+        .add(
+            ExpectedKeepRule.builder()
+                .apply(setCondition)
+                .setConsequentClass(KeptClass.class)
+                .setConsequentMembers("{ void <init>(int); }")
+                .build())
+        .add(
+            ExpectedKeepRule.builder()
+                .apply(setCondition)
+                .setConsequentClass(KeptClass.class)
+                .setConsequentMembers("{ void <init>(long); }")
+                .build())
+        .apply(b -> addConsequentKotlinMetadata(b, bb -> bb.apply(setCondition)))
+        .build();
+  }
+
+  private ExpectedRules expectedRulesKotlin(String conditionClass) {
+    String conditionMember = "{ void foo(kotlin.reflect.KClass); }";
+    return ExpectedRules.builder()
+        .add(
+            ExpectedKeepRule.builder()
+                .setConditionClass(conditionClass)
+                .setConditionMembers(conditionMember)
+                .setConsequentClass("com.android.tools.r8.keepanno.androidx.kt.KeptClass")
+                .setConsequentMembers("{ void <init>(int); }")
+                .build())
+        .add(
+            ExpectedKeepRule.builder()
+                .setConditionClass(conditionClass)
+                .setConditionMembers(conditionMember)
+                .setConsequentClass("com.android.tools.r8.keepanno.androidx.kt.KeptClass")
+                .setConsequentMembers("{ void <init>(long); }")
+                .build())
+        .apply(
+            b ->
+                addConsequentKotlinMetadata(
+                    b,
+                    bb ->
+                        bb.setConditionClass(conditionClass).setConditionMembers(conditionMember)))
+        .build();
+  }
+
+  @Test
+  public void testIntAndLongArgsConstructors() throws Exception {
+    runTestExtractedRulesJava(
+        ImmutableList.of(IntAndLongArgsConstructors.class, KeptClass.class),
+        expectedRulesJava(IntAndLongArgsConstructors.class));
+  }
+
+  static class IntAndLongArgsConstructors {
+
+    @UsesReflectionToConstruct(
+        classConstant = KeptClass.class,
+        parameterTypes = {int.class})
+    @UsesReflectionToConstruct(
+        classConstant = KeptClass.class,
+        parameterTypes = {long.class})
+    public void foo(Class<KeptClass> clazz) throws Exception {
+      if (clazz != null) {
+        clazz.getDeclaredConstructor(int.class).newInstance(1);
+        clazz.getDeclaredConstructor(long.class).newInstance(2L);
+      }
+    }
+
+    public static void main(String[] args) throws Exception {
+      new IntAndLongArgsConstructors().foo(System.nanoTime() > 0 ? KeptClass.class : null);
+    }
+  }
+
+  @Test
+  public void testIntLongArgsConstructorsClassNames() throws Exception {
+    runTestExtractedRulesJava(
+        ImmutableList.of(IntAndLongConstructorsClassName.class, KeptClass.class),
+        expectedRulesJava(IntAndLongConstructorsClassName.class));
+  }
+
+  static class IntAndLongConstructorsClassName {
+
+    @UsesReflectionToConstruct(
+        className = classNameOfKeptClass,
+        parameterTypes = {int.class})
+    @UsesReflectionToConstruct(
+        className = classNameOfKeptClass,
+        parameterTypes = {long.class})
+    public void foo(Class<KeptClass> clazz) throws Exception {
+      if (clazz != null) {
+        clazz.getDeclaredConstructor(int.class).newInstance(1);
+        clazz.getDeclaredConstructor(long.class).newInstance(2L);
+      }
+    }
+
+    public static void main(String[] args) throws Exception {
+      new IntAndLongConstructorsClassName().foo(System.nanoTime() > 0 ? KeptClass.class : null);
+    }
+  }
+
+  @Test
+  public void testIntLongArgsConstructorsKotlin() throws Exception {
+    runTestExtractedRulesKotlin(
+        compilationResults,
+        "com.android.tools.r8.keepanno.androidx.kt.IntAndLongArgsConstructorsKt",
+        expectedRulesKotlin(
+            "com.android.tools.r8.keepanno.androidx.kt.IntAndLongArgsConstructors"));
+  }
+
+  @Test
+  public void testIntLongArgsConstructorsKotlinClassName() throws Exception {
+    runTestExtractedRulesKotlin(
+        compilationResultsClassName,
+        "com.android.tools.r8.keepanno.androidx.kt.IntAndLongArgsConstructorsClassNameKt",
+        expectedRulesKotlin(
+            "com.android.tools.r8.keepanno.androidx.kt.IntAndLongArgsConstructorsClassName"));
+  }
+
+  static class KeptClass {
+    KeptClass() {
+      System.out.println("<init>()");
+    }
+
+    KeptClass(int i) {
+      System.out.println("<init>(int)");
+    }
+
+    KeptClass(long j) {
+      System.out.println("<init>(long)");
+    }
+
+    KeptClass(String s) {
+      System.out.println("<init>(String)");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/androidx/KeepUsesReflectionForInstantiationNoArgsConstructorTest.java b/src/test/java/com/android/tools/r8/keepanno/androidx/KeepUsesReflectionForInstantiationNoArgsConstructorTest.java
index dfa7b78..6309009 100644
--- a/src/test/java/com/android/tools/r8/keepanno/androidx/KeepUsesReflectionForInstantiationNoArgsConstructorTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/androidx/KeepUsesReflectionForInstantiationNoArgsConstructorTest.java
@@ -7,15 +7,23 @@
 import static org.junit.Assert.assertEquals;
 
 import androidx.annotation.keep.UsesReflectionToConstruct;
+import com.android.tools.r8.KotlinCompileMemoizer;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.transformers.ClassFileTransformer.AnnotationBuilder;
+import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
+import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.StringUtils;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
 import java.nio.file.Path;
 import java.util.Collection;
+import java.util.function.Consumer;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
+import org.objectweb.asm.Type;
 
 @RunWith(Parameterized.class)
 public class KeepUsesReflectionForInstantiationNoArgsConstructorTest
@@ -28,15 +36,32 @@
   @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     assertEquals(KeptClass.class.getTypeName(), classNameOfKeptClass);
+    // Test with Android 14, which has `java.lang.ClassValue` to avoid having to deal with R8
+    // missing class warnings for tests using the kotlin-reflect library.
     return buildParameters(
-        createParameters(getTestParameters().withDefaultRuntimes().withMaximumApiLevel().build()),
+        createParameters(
+            getTestParameters()
+                .withDexRuntime(Version.V14_0_0)
+                .withDefaultCfRuntime()
+                .withMaximumApiLevel()
+                .build()),
         getKotlinTestParameters().withLatestCompiler().build());
   }
 
-  protected String getExpectedOutput() {
+  @Override
+  protected String getExpectedOutputForJava() {
     return StringUtils.lines("<init>()");
   }
 
+  @Override
+  protected String getExpectedOutputForKotlin() {
+    return StringUtils.lines(
+        "fun `<init>`(): com.android.tools.r8.keepanno.androidx.kt.KeptClass",
+        "<init>()",
+        "fun `<init>`(): com.android.tools.r8.keepanno.androidx.kt.KeptClass",
+        "<init>()");
+  }
+
   private static Collection<Path> getKotlinSources() {
     try {
       return getFilesInTestFolderRelativeToClass(
@@ -61,29 +86,78 @@
     }
   }
 
+  protected static KotlinCompileMemoizer compilationResultsWithoutAnnotation;
+
+  private static Collection<Path> getKotlinSourcesWithoutAnnotation() {
+    try {
+      return getFilesInTestFolderRelativeToClass(
+          KeepUsesReflectionForInstantiationNoArgsConstructorTest.class,
+          "kt",
+          "OnlyNoArgsConstructorWithoutAnnotation.kt",
+          "KeptClass.kt");
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
   @BeforeClass
   public static void beforeClass() throws Exception {
     compilationResults = getCompileMemoizerWithKeepAnnoLib(getKotlinSources());
     compilationResultsClassName = getCompileMemoizerWithKeepAnnoLib(getKotlinSourcesClassName());
+    compilationResultsWithoutAnnotation =
+        new KotlinCompileMemoizer(getKotlinSourcesWithoutAnnotation());
+  }
+
+  private static ExpectedRules getExpectedRulesJava(Class<?> conditionClass) {
+    return getExpectedRulesJava(conditionClass, null);
+  }
+
+  private static ExpectedRules getExpectedRulesJava(
+      Class<?> conditionClass, String contitionMembers) {
+    Consumer<ExpectedKeepRule.Builder> setCondition =
+        b -> b.setConditionClass(conditionClass).setConditionMembers(contitionMembers);
+
+    ExpectedRules.Builder builder =
+        ExpectedRules.builder()
+            .add(
+                ExpectedKeepRule.builder()
+                    .apply(setCondition)
+                    .setConsequentClass(KeptClass.class)
+                    .setConsequentMembers("{ void <init>(); }")
+                    .build());
+    addConsequentKotlinMetadata(builder, b -> b.apply(setCondition));
+    return builder.build();
+  }
+
+  private static ExpectedRules getExpectedRulesKotlin(String conditionClass) {
+    Consumer<ExpectedKeepRule.Builder> setCondition =
+        b ->
+            b.setConditionClass(conditionClass)
+                .setConditionMembers("{ void foo(kotlin.reflect.KClass); }");
+    ExpectedRules.Builder builder =
+        ExpectedRules.builder()
+            .add(
+                ExpectedKeepRule.builder()
+                    .apply(setCondition)
+                    .setConsequentClass("com.android.tools.r8.keepanno.androidx.kt.KeptClass")
+                    .setConsequentMembers("{ void <init>(); }")
+                    .build());
+    addConsequentKotlinMetadata(builder, b -> b.apply(setCondition));
+    return builder.build();
   }
 
   @Test
   public void testOnlyNoArgsConstructor() throws Exception {
     runTestExtractedRulesJava(
         ImmutableList.of(OnlyNoArgsConstructor.class, KeptClass.class),
-        ExpectedRule.builder()
-            .setConditionClass(OnlyNoArgsConstructor.class)
-            .setConditionMembers("{ void foo(java.lang.Class); }")
-            .setConsequentClass(KeptClass.class)
-            .setConsequentMembers("{ void <init>(); }")
-            .build());
+        getExpectedRulesJava(OnlyNoArgsConstructor.class, "{ void foo(java.lang.Class); }"));
   }
 
   static class OnlyNoArgsConstructor {
 
     @UsesReflectionToConstruct(
-        className = classNameOfKeptClass,
-        params = {})
+        classConstant = KeptClass.class,
+        parameterTypes = {})
     public void foo(Class<KeptClass> clazz) throws Exception {
       if (clazz != null) {
         clazz.getDeclaredConstructor().newInstance();
@@ -96,22 +170,18 @@
   }
 
   @Test
-  public void testOnlyNoArgsConstructorClassNames() throws Exception {
+  public void testOnlyNoArgsConstructorClassName() throws Exception {
     runTestExtractedRulesJava(
-        ImmutableList.of(OnlyNoArgsConstructorClassNames.class, KeptClass.class),
-        ExpectedRule.builder()
-            .setConditionClass(OnlyNoArgsConstructorClassNames.class)
-            .setConditionMembers("{ void foo(java.lang.Class); }")
-            .setConsequentClass(KeptClass.class)
-            .setConsequentMembers("{ void <init>(); }")
-            .build());
+        ImmutableList.of(OnlyNoArgsConstructorClassName.class, KeptClass.class),
+        getExpectedRulesJava(
+            OnlyNoArgsConstructorClassName.class, "{ void foo(java.lang.Class); }"));
   }
 
-  static class OnlyNoArgsConstructorClassNames {
+  static class OnlyNoArgsConstructorClassName {
 
     @UsesReflectionToConstruct(
         className = classNameOfKeptClass,
-        params = {})
+        parameterTypes = {})
     public void foo(Class<KeptClass> clazz) throws Exception {
       if (clazz != null) {
         clazz.getDeclaredConstructor().newInstance();
@@ -119,7 +189,7 @@
     }
 
     public static void main(String[] args) throws Exception {
-      new OnlyNoArgsConstructorClassNames().foo(System.nanoTime() > 0 ? KeptClass.class : null);
+      new OnlyNoArgsConstructorClassName().foo(System.nanoTime() > 0 ? KeptClass.class : null);
     }
   }
 
@@ -128,12 +198,7 @@
     runTestExtractedRulesKotlin(
         compilationResults,
         "com.android.tools.r8.keepanno.androidx.kt.OnlyNoArgsConstructorKt",
-        ExpectedRule.builder()
-            .setConditionClass("com.android.tools.r8.keepanno.androidx.kt.OnlyNoArgsConstructor")
-            .setConditionMembers("{ void foo(kotlin.reflect.KClass); }")
-            .setConsequentClass("com.android.tools.r8.keepanno.androidx.kt.KeptClass")
-            .setConsequentMembers("{ void <init>(); }")
-            .build());
+        getExpectedRulesKotlin("com.android.tools.r8.keepanno.androidx.kt.OnlyNoArgsConstructor"));
   }
 
   @Test
@@ -141,13 +206,138 @@
     runTestExtractedRulesKotlin(
         compilationResultsClassName,
         "com.android.tools.r8.keepanno.androidx.kt.OnlyNoArgsConstructorClassNameKt",
-        ExpectedRule.builder()
-            .setConditionClass(
-                "com.android.tools.r8.keepanno.androidx.kt.OnlyNoArgsConstructorClassName")
-            .setConditionMembers("{ void foo(kotlin.reflect.KClass); }")
-            .setConsequentClass("com.android.tools.r8.keepanno.androidx.kt.KeptClass")
-            .setConsequentMembers("{ void <init>(); }")
-            .build());
+        getExpectedRulesKotlin(
+            "com.android.tools.r8.keepanno.androidx.kt.OnlyNoArgsConstructorClassName"));
+  }
+
+  private static void buildNoArgsConstructor(AnnotationBuilder builder, Object clazz) {
+    if (clazz instanceof String) {
+      builder.setField("className", clazz);
+    } else {
+      assert clazz instanceof Class<?> || clazz instanceof Type;
+      builder.setField("classConstant", clazz);
+    }
+    // Set the empty array for the no args constructor.
+    builder.setArray("parameterTypes");
+  }
+
+  @Test
+  // This test is similar to testOnlyNoArgsConstructor() except that the annotation is inserted
+  // by a transformer.
+  public void testOnlyNoArgsConstructorUsingTransformer() throws Exception {
+    runTestExtractedRulesJava(
+        OnlyNoArgsConstructorWithoutAnnotation.class,
+        ImmutableList.of(KeptClass.class),
+        ImmutableList.of(
+            setAnnotationOnMethod(
+                OnlyNoArgsConstructorWithoutAnnotation.class,
+                MethodPredicate.onName("foo"),
+                UsesReflectionToConstruct.class,
+                builder -> buildNoArgsConstructor(builder, KeptClass.class))),
+        getExpectedRulesJava(
+            OnlyNoArgsConstructorWithoutAnnotation.class, "{ void foo(java.lang.Class); }"));
+  }
+
+  @Test
+  public void testOnlyNoArgsConstructorOnClass() throws Exception {
+    runTestExtractedRulesJava(
+        OnlyNoArgsConstructorWithoutAnnotation.class,
+        ImmutableList.of(KeptClass.class),
+        ImmutableList.of(
+            setAnnotationOnClass(
+                OnlyNoArgsConstructorWithoutAnnotation.class,
+                UsesReflectionToConstruct.class,
+                builder -> buildNoArgsConstructor(builder, KeptClass.class))),
+        getExpectedRulesJava(OnlyNoArgsConstructorWithoutAnnotation.class));
+  }
+
+  @Test
+  // This test is similar to testOnlyNoArgsConstructorClassName() except that the annotation is
+  // inserted by a transformer.
+  public void testOnlyNoArgsConstructorClassNameUsingTransformer() throws Exception {
+    runTestExtractedRulesJava(
+        OnlyNoArgsConstructorWithoutAnnotation.class,
+        ImmutableList.of(KeptClass.class),
+        ImmutableList.of(
+            setAnnotationOnMethod(
+                OnlyNoArgsConstructorWithoutAnnotation.class,
+                MethodPredicate.onName("foo"),
+                UsesReflectionToConstruct.class,
+                builder -> buildNoArgsConstructor(builder, classNameOfKeptClass))),
+        getExpectedRulesJava(
+            OnlyNoArgsConstructorWithoutAnnotation.class, "{ void foo(java.lang.Class); }"));
+  }
+
+  @Test
+  public void testOnlyNoArgsConstructorClassNameOnClass() throws Exception {
+    runTestExtractedRulesJava(
+        OnlyNoArgsConstructorWithoutAnnotation.class,
+        ImmutableList.of(KeptClass.class),
+        ImmutableList.of(
+            setAnnotationOnClass(
+                OnlyNoArgsConstructorWithoutAnnotation.class,
+                UsesReflectionToConstruct.class,
+                builder -> buildNoArgsConstructor(builder, classNameOfKeptClass))),
+        getExpectedRulesJava(OnlyNoArgsConstructorWithoutAnnotation.class));
+  }
+
+  @Test
+  public void testOnlyNoArgsConstructorKotlinUsingTransformer() throws Exception {
+    runTestExtractedRulesKotlin(
+        compilationResultsWithoutAnnotation,
+        (classReference, classFileBytes) ->
+            setAnnotationOnMethod(
+                classReference,
+                classFileBytes,
+                Reference.classFromTypeName(
+                    "com.android.tools.r8.keepanno.androidx.kt.OnlyNoArgsConstructorWithoutAnnotation"),
+                MethodPredicate.onName("foo"),
+                UsesReflectionToConstruct.class,
+                builder ->
+                    buildNoArgsConstructor(
+                        builder,
+                        Type.getType(
+                            DescriptorUtils.javaTypeToDescriptor(
+                                "com.android.tools.r8.keepanno.androidx.kt.KeptClass")))),
+        "com.android.tools.r8.keepanno.androidx.kt.OnlyNoArgsConstructorWithoutAnnotationKt",
+        getExpectedRulesKotlin(
+            "com.android.tools.r8.keepanno.androidx.kt.OnlyNoArgsConstructorWithoutAnnotation"));
+  }
+
+  @Test
+  public void testOnlyNoArgsConstructorKotlinClassNameUsingTransformer() throws Exception {
+    runTestExtractedRulesKotlin(
+        compilationResultsWithoutAnnotation,
+        (classReference, classFileBytes) ->
+            setAnnotationOnMethod(
+                classReference,
+                classFileBytes,
+                Reference.classFromTypeName(
+                    "com.android.tools.r8.keepanno.androidx.kt.OnlyNoArgsConstructorWithoutAnnotation"),
+                MethodPredicate.onName("foo"),
+                UsesReflectionToConstruct.class,
+                builder ->
+                    buildNoArgsConstructor(
+                        builder, "com.android.tools.r8.keepanno.androidx.kt.KeptClass")),
+        "com.android.tools.r8.keepanno.androidx.kt.OnlyNoArgsConstructorWithoutAnnotationKt",
+        getExpectedRulesKotlin(
+            "com.android.tools.r8.keepanno.androidx.kt.OnlyNoArgsConstructorWithoutAnnotation"));
+  }
+
+  // Test class without annotation to be used by multiple tests inserting annotations using a
+  // transformer.
+  static class OnlyNoArgsConstructorWithoutAnnotation {
+
+    public void foo(Class<KeptClass> clazz) throws Exception {
+      if (clazz != null) {
+        clazz.getDeclaredConstructor().newInstance();
+      }
+    }
+
+    public static void main(String[] args) throws Exception {
+      new OnlyNoArgsConstructorWithoutAnnotation()
+          .foo(System.nanoTime() > 0 ? KeptClass.class : null);
+    }
   }
 
   static class KeptClass {
diff --git a/src/test/java/com/android/tools/r8/keepanno/androidx/kt/IntAndLongArgsConstructors.kt b/src/test/java/com/android/tools/r8/keepanno/androidx/kt/IntAndLongArgsConstructors.kt
new file mode 100644
index 0000000..2f0dcd2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/androidx/kt/IntAndLongArgsConstructors.kt
@@ -0,0 +1,32 @@
+// Copyright (c) 2025, 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.keepanno.androidx.kt
+
+import androidx.annotation.keep.UsesReflectionToConstruct
+import kotlin.reflect.KClass
+import kotlin.reflect.full.createType
+
+class IntAndLongArgsConstructors {
+
+  @UsesReflectionToConstruct(classConstant = KeptClass::class, parameterTypes = [Int::class])
+  @UsesReflectionToConstruct(classConstant = KeptClass::class, parameterTypes = [Long::class])
+  fun foo(clazz: KClass<KeptClass>?) {
+    val intConstructor =
+      clazz?.constructors?.first {
+        it.parameters.size == 1 && it.parameters.first().type == Int::class.createType()
+      }
+    println(intConstructor)
+    intConstructor?.call(1)
+    val longConstructor =
+      clazz?.constructors?.first {
+        it.parameters.size == 1 && it.parameters.first().type == Long::class.createType()
+      }
+    println(longConstructor)
+    longConstructor?.call(2L)
+  }
+}
+
+fun main() {
+  IntAndLongArgsConstructors().foo(if (System.nanoTime() > 0) KeptClass::class else null)
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/androidx/kt/IntAndLongArgsConstructorsClassName.kt b/src/test/java/com/android/tools/r8/keepanno/androidx/kt/IntAndLongArgsConstructorsClassName.kt
new file mode 100644
index 0000000..0737754
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/androidx/kt/IntAndLongArgsConstructorsClassName.kt
@@ -0,0 +1,38 @@
+// Copyright (c) 2025, 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.keepanno.androidx.kt
+
+import androidx.annotation.keep.UsesReflectionToConstruct
+import kotlin.reflect.KClass
+import kotlin.reflect.full.createType
+
+class IntAndLongArgsConstructorsClassName {
+
+  @UsesReflectionToConstruct(
+    className = "com.android.tools.r8.keepanno.androidx.kt.KeptClass",
+    parameterTypes = [Int::class],
+  )
+  @UsesReflectionToConstruct(
+    className = "com.android.tools.r8.keepanno.androidx.kt.KeptClass",
+    parameterTypes = [Long::class],
+  )
+  fun foo(clazz: KClass<KeptClass>?) {
+    val intConstructor =
+      clazz?.constructors?.first {
+        it.parameters.size == 1 && it.parameters.first().type == Int::class.createType()
+      }
+    println(intConstructor)
+    intConstructor?.call(1)
+    val longConstructor =
+      clazz?.constructors?.first {
+        it.parameters.size == 1 && it.parameters.first().type == Long::class.createType()
+      }
+    println(longConstructor)
+    longConstructor?.call(2L)
+  }
+}
+
+fun main() {
+  IntAndLongArgsConstructorsClassName().foo(if (System.nanoTime() > 0) KeptClass::class else null)
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/androidx/kt/KeptClass.kt b/src/test/java/com/android/tools/r8/keepanno/androidx/kt/KeptClass.kt
index fb55316..89e2d02 100644
--- a/src/test/java/com/android/tools/r8/keepanno/androidx/kt/KeptClass.kt
+++ b/src/test/java/com/android/tools/r8/keepanno/androidx/kt/KeptClass.kt
@@ -1,3 +1,6 @@
+// Copyright (c) 2025, 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.keepanno.androidx.kt
 
 class KeptClass() {
@@ -12,4 +15,8 @@
   constructor(l: Long) : this() {
     println("<init>(Long)")
   }
+
+  constructor(s: String) : this() {
+    println("<init>(String)")
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/keepanno/androidx/kt/OnlyNoArgsConstructor.kt b/src/test/java/com/android/tools/r8/keepanno/androidx/kt/OnlyNoArgsConstructor.kt
index 4dd03cc..4e9e3eb 100644
--- a/src/test/java/com/android/tools/r8/keepanno/androidx/kt/OnlyNoArgsConstructor.kt
+++ b/src/test/java/com/android/tools/r8/keepanno/androidx/kt/OnlyNoArgsConstructor.kt
@@ -1,3 +1,6 @@
+// Copyright (c) 2025, 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.keepanno.androidx.kt
 
 import androidx.annotation.keep.UsesReflectionToConstruct
@@ -5,9 +8,13 @@
 import kotlin.reflect.full.primaryConstructor
 
 class OnlyNoArgsConstructor {
-  @UsesReflectionToConstruct(classConstant = KeptClass::class, params = [])
+  @UsesReflectionToConstruct(classConstant = KeptClass::class, parameterTypes = [])
   fun foo(clazz: KClass<KeptClass>?) {
+    println(clazz?.primaryConstructor)
     clazz?.primaryConstructor?.call()
+    val noArgsConstructor = clazz?.constructors?.find { it.parameters.isEmpty() }
+    println(noArgsConstructor)
+    noArgsConstructor?.call()
   }
 }
 
diff --git a/src/test/java/com/android/tools/r8/keepanno/androidx/kt/OnlyNoArgsConstructorClassName.kt b/src/test/java/com/android/tools/r8/keepanno/androidx/kt/OnlyNoArgsConstructorClassName.kt
index 8652223..6eb2c08 100644
--- a/src/test/java/com/android/tools/r8/keepanno/androidx/kt/OnlyNoArgsConstructorClassName.kt
+++ b/src/test/java/com/android/tools/r8/keepanno/androidx/kt/OnlyNoArgsConstructorClassName.kt
@@ -1,3 +1,6 @@
+// Copyright (c) 2025, 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.keepanno.androidx.kt
 
 import androidx.annotation.keep.UsesReflectionToConstruct
@@ -7,10 +10,14 @@
 class OnlyNoArgsConstructorClassName {
   @UsesReflectionToConstruct(
     className = "com.android.tools.r8.keepanno.androidx.kt.KeptClass",
-    params = [],
+    parameterTypes = [],
   )
   fun foo(clazz: KClass<KeptClass>?) {
+    println(clazz?.primaryConstructor)
     clazz?.primaryConstructor?.call()
+    val noArgsConstructor = clazz?.constructors?.find { it.parameters.isEmpty() }
+    println(noArgsConstructor)
+    noArgsConstructor?.call()
   }
 }
 
diff --git a/src/test/java/com/android/tools/r8/keepanno/androidx/kt/OnlyNoArgsConstructorWithoutAnnotation.kt b/src/test/java/com/android/tools/r8/keepanno/androidx/kt/OnlyNoArgsConstructorWithoutAnnotation.kt
new file mode 100644
index 0000000..3ada64f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/androidx/kt/OnlyNoArgsConstructorWithoutAnnotation.kt
@@ -0,0 +1,22 @@
+// Copyright (c) 2025, 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.keepanno.androidx.kt
+
+import kotlin.reflect.KClass
+import kotlin.reflect.full.primaryConstructor
+
+class OnlyNoArgsConstructorWithoutAnnotation {
+  fun foo(clazz: KClass<KeptClass>?) {
+    println(clazz?.primaryConstructor)
+    clazz?.primaryConstructor?.call()
+    val noArgsConstructor = clazz?.constructors?.find { it.parameters.isEmpty() }
+    println(noArgsConstructor)
+    noArgsConstructor?.call()
+  }
+}
+
+fun main() {
+  OnlyNoArgsConstructorWithoutAnnotation()
+    .foo(if (System.nanoTime() > 0) KeptClass::class else null)
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java b/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java
index 3ff6678..e060bbf 100644
--- a/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java
+++ b/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java
@@ -134,6 +134,7 @@
     final String kotlinName;
     String valueType = null;
     String valueDefault = null;
+    boolean suppressKotlinDefaultParameterOrder = false;
 
     GroupMember(String name) {
       this(name, name);
@@ -154,6 +155,11 @@
       return this;
     }
 
+    public GroupMember setSuppressKotlinDefaultParameterOrder() {
+      this.suppressKotlinDefaultParameterOrder = true;
+      return this;
+    }
+
     @Override
     public GroupMember self() {
       return this;
@@ -165,12 +171,17 @@
         generator.println("@Deprecated");
       }
       if (generator.generateKotlin()) {
-        if (kotlinValueDefault() == null) {
-          generator.println("val " + kotlinName + ": " + kotlinValueType() + ",");
-        } else {
-          generator.println(
-              "val " + kotlinName + ": " + kotlinValueType() + " = " + kotlinValueDefault() + ",");
+        StringBuilder builder = new StringBuilder();
+        if (suppressKotlinDefaultParameterOrder) {
+          builder.append("@Suppress(\"KotlinDefaultParameterOrder\") ");
         }
+        builder.append("val ").append(kotlinName).append(": ").append(kotlinValueType());
+        if (kotlinValueDefault() == null) {
+          builder.append(",");
+        } else {
+          builder.append(" = ").append(kotlinValueDefault()).append(",");
+        }
+        generator.println(builder.toString());
       } else {
         if (valueDefault == null) {
           generator.println(valueType + " " + name + "();");
@@ -688,22 +699,30 @@
       }
     }
 
-    private void printOpenAnnotationClassTargetingClassFieldMethodCtor(String clazz) {
+    private void printOpenAnnotationClassTargetingClassFieldMethodCtor(ClassReference clazz) {
+      String unqualifiedName = getUnqualifiedName(clazz);
       if (generateKotlin()) {
         println("@Retention(AnnotationRetention.BINARY)");
+        if (REPEATABLE_ANNOTATIONS.contains(clazz)) {
+          println("@Repeatable");
+        }
         println("@Target(");
         println("  AnnotationTarget.CLASS,");
         println("  AnnotationTarget.FIELD,");
         println("  AnnotationTarget.FUNCTION,");
         println("  AnnotationTarget.CONSTRUCTOR,");
         println(")");
-        println("public annotation class " + clazz + "(");
+        println("public annotation class " + unqualifiedName + "(");
       } else {
+        if (REPEATABLE_ANNOTATIONS.contains(clazz)) {
+          throw new RuntimeException(
+              "Repeatable annotations not supported for Java code generation");
+        }
         println(
             "@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD,"
                 + " ElementType.CONSTRUCTOR})");
         println("@Retention(RetentionPolicy.CLASS)");
-        println("public @interface " + clazz + " {");
+        println("public @interface " + unqualifiedName + " {");
       }
     }
 
@@ -1745,7 +1764,7 @@
               "When a member is annotated, the member patterns cannot be used as the annotated"
                   + " member itself fully defines the item to be kept (i.e., itself).")
           .printDoc(this::println);
-      printOpenAnnotationClassTargetingClassFieldMethodCtor("KeepForApi");
+      printOpenAnnotationClassTargetingClassFieldMethodCtor(KEEP_FOR_API);
       println();
       withIndent(
           () -> {
@@ -1834,7 +1853,7 @@
               "    // unreachable",
               "  }")
           .printDoc(this::println);
-      printOpenAnnotationClassTargetingClassFieldMethodCtor(getUnqualifiedName(USES_REFLECTION));
+      printOpenAnnotationClassTargetingClassFieldMethodCtor(USES_REFLECTION);
       println();
       withIndent(
           () -> {
@@ -1861,10 +1880,10 @@
 
     private Group createAndroidXParameterSelection(
         Consumer<GroupMember> paramsConsumer, Consumer<GroupMember> paramTypeNamesConsumer) {
-      GroupMember params = new GroupMember("params").defaultUnspecifiedArray();
+      GroupMember params = new GroupMember("parameterTypes").defaultUnspecifiedArray();
       paramsConsumer.accept(params);
       GroupMember paramTypeNames =
-          new GroupMember("paramTypeNames")
+          new GroupMember("parameterTypeNames")
               .defaultArrayValue(Reference.classFromClass(String.class), "\"\"");
       paramTypeNamesConsumer.accept(paramTypeNames);
       return new Group("constructor-parameters")
@@ -1941,9 +1960,7 @@
           .addSection("@see UsesReflectionToAccessMethod")
           .addSection("@see UsesReflectionToAccessField")
           .printDoc(this::println);
-      println("@Repeatable");
-      printOpenAnnotationClassTargetingClassFieldMethodCtor(
-          getUnqualifiedName(USES_REFLECTION_TO_CONSTRUCT));
+      printOpenAnnotationClassTargetingClassFieldMethodCtor(USES_REFLECTION_TO_CONSTRUCT);
       println();
       withIndent(
           () -> {
@@ -1958,15 +1975,15 @@
                                 "Defines which constructor to keep by specifying the parameter list"
                                     + " types.")
                             .addSection(
-                                "If neither `param` nor `paramTypeNames` is specified then"
-                                    + " constructors with all parameter lists are kept."),
+                                "If neither `parameterTypes` nor `parameterTypeNames` is specified"
+                                    + " then constructors with all parameter lists are kept."),
                     g ->
                         g.setDocTitle(
                                 "Defines which constructor to keep by specifying the parameter list"
                                     + " types.")
                             .addSection(
-                                "If neither `param` nor `paramTypeNames` is specified then"
-                                    + " constructors with all parameter lists are kept."))
+                                "If neither `parameterTypes` nor `parameterTypeNames` is specified"
+                                    + " then constructors with all parameter lists are kept."))
                 .generate(this);
           });
       printCloseAnnotationClass();
@@ -1990,18 +2007,19 @@
           .addSection("@see UsesReflectionToConstruct")
           .addSection("@see UsesReflectionToAccessField")
           .printDoc(this::println);
-      println("@Repeatable");
-      printOpenAnnotationClassTargetingClassFieldMethodCtor(
-          getUnqualifiedName(USES_REFLECTION_TO_ACCESS_METHOD));
+      printOpenAnnotationClassTargetingClassFieldMethodCtor(USES_REFLECTION_TO_ACCESS_METHOD);
       println();
       withIndent(
           () -> {
             createAndroidXClassSelection(
-                    g -> g.setDocTitle("Class containing the method accessed by reflection."),
                     g ->
-                        g.setDocTitle(
-                            "Class name (or class name pattern) containing the method accessed by"
-                                + " reflection."))
+                        g.setSuppressKotlinDefaultParameterOrder()
+                            .setDocTitle("Class containing the method accessed by reflection."),
+                    g ->
+                        g.setSuppressKotlinDefaultParameterOrder()
+                            .setDocTitle(
+                                "Class name (or class name pattern) containing the method accessed"
+                                    + " by reflection."))
                 .generate(this);
             println();
             createMethodNameSelection().generate(this);
@@ -2012,15 +2030,15 @@
                                 "Defines which method to keep by specifying set of parameter"
                                     + " classes passed.")
                             .addSection(
-                                "If neither `param` nor `paramTypeNames` is specified then"
-                                    + " methods with all parameter lists are kept."),
+                                "If neither `parameterTypes` nor `parameterTypeNames` is specified"
+                                    + " then methods with all parameter lists are kept."),
                     g ->
                         g.setDocTitle(
                                 "Defines which method to keep by specifying set of parameter"
                                     + " classes passed.")
                             .addSection(
-                                "If neither `param` nor `paramTypeNames` is specified then"
-                                    + " methods with all parameter lists are kept."))
+                                "If neither `parameterTypes` nor `parameterTypeNames` is specified"
+                                    + " then methods with all parameter lists are kept."))
                 .generate(this);
             println();
             createAndroidXReturnTypeSelection().generate(this);
@@ -2044,18 +2062,19 @@
                   + " preserved if the annotated code is reachable in the final application build.")
           .addSection("@see UsesReflectionToConstruct", "@see UsesReflectionToAccessMethod")
           .printDoc(this::println);
-      println("@Repeatable");
-      printOpenAnnotationClassTargetingClassFieldMethodCtor(
-          getUnqualifiedName(USES_REFLECTION_TO_ACCESS_FIELD));
+      printOpenAnnotationClassTargetingClassFieldMethodCtor(USES_REFLECTION_TO_ACCESS_FIELD);
       println();
       withIndent(
           () -> {
             createAndroidXClassSelection(
-                    g -> g.setDocTitle("Class containing the field accessed by reflection."),
                     g ->
-                        g.setDocTitle(
-                            "Class name (or class name pattern) containing the field accessed by"
-                                + " reflection."))
+                        g.setSuppressKotlinDefaultParameterOrder()
+                            .setDocTitle("Class containing the field accessed by reflection."),
+                    g ->
+                        g.setSuppressKotlinDefaultParameterOrder()
+                            .setDocTitle(
+                                "Class name (or class name pattern) containing the field accessed"
+                                    + " by reflection."))
                 .generate(this);
             println();
             createAndroidXFieldNameSelection().generate(this);
@@ -2090,8 +2109,7 @@
               "@see UsesReflectionToAccessMethod",
               "@see UsesReflectionToAccessField")
           .printDoc(this::println);
-      printOpenAnnotationClassTargetingClassFieldMethodCtor(
-          getUnqualifiedName(UNCONDITIONALLY_KEEP));
+      printOpenAnnotationClassTargetingClassFieldMethodCtor(UNCONDITIONALLY_KEEP);
       println();
       withIndent(
           () -> {
@@ -2108,7 +2126,7 @@
       printCloseAnnotationClass();
     }
 
-    private void generateUsedByX(String annotationClassName, String doc) {
+    private void generateUsedByX(ClassReference clazz, String doc) {
       printCopyRight(2023);
       printPackage();
       printAnnotationImports();
@@ -2137,7 +2155,7 @@
               "When a member is annotated, the member patterns cannot be used as the annotated"
                   + " member itself fully defines the item to be kept (i.e., itself).")
           .printDoc(this::println);
-      printOpenAnnotationClassTargetingClassFieldMethodCtor(annotationClassName);
+      printOpenAnnotationClassTargetingClassFieldMethodCtor(clazz);
       println();
       withIndent(
           () -> {
@@ -2746,15 +2764,16 @@
     private static void writeFile(
         String pkg,
         Function<Generator, ClassReference> f,
-        Consumer<Generator> fn,
+        BiConsumer<Generator, ClassReference> fn,
         BiConsumer<Path, String> write)
         throws IOException {
       ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
       PrintStream printStream = new PrintStream(byteStream);
       Generator generator = new Generator(printStream, pkg);
-      fn.accept(generator);
+      ClassReference clazz = f.apply(generator);
+      fn.accept(generator, clazz);
       String formatted = byteStream.toString();
-      Path file = source(f.apply(generator));
+      Path file = source(clazz);
       if (file.toString().endsWith(".kt")) {
         formatted = CodeGenerationBase.kotlinFormatRawOutput(formatted);
       } else if (file.toString().endsWith(".java")) {
@@ -2783,7 +2802,7 @@
       writeFile(
           ANDROIDX_ANNO_PKG,
           generator -> generator.ANNOTATION_CONSTANTS,
-          Generator::generateConstants,
+          (generator, clazz) -> generator.generateConstants(),
           write);
       // Create a copy of the non-generated classes in the androidx namespace.
       // This is currently disabled. Will potentially be re-introduced by converting these classes
@@ -2791,61 +2810,86 @@
       // copyNonGeneratedMethods();
       for (String pkg : new String[] {R8_ANNO_PKG, ANDROIDX_ANNO_PKG}) {
         writeFile(
-            pkg, generator -> generator.STRING_PATTERN, Generator::generateStringPattern, write);
-        writeFile(pkg, generator -> generator.TYPE_PATTERN, Generator::generateTypePattern, write);
+            pkg,
+            generator -> generator.STRING_PATTERN,
+            (generator, clazz) -> generator.generateStringPattern(),
+            write);
+        writeFile(
+            pkg,
+            generator -> generator.TYPE_PATTERN,
+            (generator, clazz) -> generator.generateTypePattern(),
+            write);
         writeFile(
             pkg,
             generator -> generator.CLASS_NAME_PATTERN,
-            Generator::generateClassNamePattern,
+            (generator, clazz) -> generator.generateClassNamePattern(),
             write);
         writeFile(
             pkg,
             generator -> generator.INSTANCE_OF_PATTERN,
-            Generator::generateInstanceOfPattern,
+            (generator, clazz) -> generator.generateInstanceOfPattern(),
             write);
         writeFile(
             pkg,
             generator -> generator.ANNOTATION_PATTERN,
-            Generator::generateAnnotationPattern,
+            (generator, clazz) -> generator.generateAnnotationPattern(),
             write);
-        writeFile(pkg, generator -> generator.KEEP_BINDING, Generator::generateKeepBinding, write);
-        writeFile(pkg, generator -> generator.KEEP_TARGET, Generator::generateKeepTarget, write);
         writeFile(
-            pkg, generator -> generator.KEEP_CONDITION, Generator::generateKeepCondition, write);
-        writeFile(pkg, generator -> generator.KEEP_FOR_API, Generator::generateKeepForApi, write);
+            pkg,
+            generator -> generator.KEEP_BINDING,
+            (generator, clazz) -> generator.generateKeepBinding(),
+            write);
         writeFile(
-            pkg, generator -> generator.USES_REFLECTION, Generator::generateUsesReflection, write);
+            pkg,
+            generator -> generator.KEEP_TARGET,
+            (generator, clazz) -> generator.generateKeepTarget(),
+            write);
+        writeFile(
+            pkg,
+            generator -> generator.KEEP_CONDITION,
+            (generator, clazz) -> generator.generateKeepCondition(),
+            write);
+        writeFile(
+            pkg,
+            generator -> generator.KEEP_FOR_API,
+            (generator, clazz) -> generator.generateKeepForApi(),
+            write);
+        writeFile(
+            pkg,
+            generator -> generator.USES_REFLECTION,
+            (generator, clazz) -> generator.generateUsesReflection(),
+            write);
         writeFile(
             pkg,
             generator -> generator.USED_BY_REFLECTION,
-            generator -> generator.generateUsedByX("UsedByReflection", "accessed reflectively"),
+            (generator, clazz) -> generator.generateUsedByX(clazz, "accessed reflectively"),
             write);
         writeFile(
             pkg,
             generator -> generator.USED_BY_NATIVE,
-            generator ->
-                generator.generateUsedByX("UsedByNative", "accessed from native code via JNI"),
+            (generator, clazz) ->
+                generator.generateUsedByX(clazz, "accessed from native code via JNI"),
             write);
       }
       writeFile(
           ANDROIDX_ANNO_PKG,
           generator -> generator.USES_REFLECTION_TO_CONSTRUCT,
-          Generator::generateUsesReflectionToConstruct,
+          (generator, clazz) -> generator.generateUsesReflectionToConstruct(),
           write);
       writeFile(
           ANDROIDX_ANNO_PKG,
           generator -> generator.USES_REFLECTION_TO_ACCESS_METHOD,
-          Generator::generateUsesReflectionToAccessMethod,
+          (generator, clazz) -> generator.generateUsesReflectionToAccessMethod(),
           write);
       writeFile(
           ANDROIDX_ANNO_PKG,
           generator -> generator.USES_REFLECTION_TO_ACCESS_FIELD,
-          Generator::generateUsesReflectionToAccessField,
+          (generator, clazz) -> generator.generateUsesReflectionToAccessField(),
           write);
       writeFile(
           ANDROIDX_ANNO_PKG,
           generator -> generator.UNCONDITIONALLY_KEEP,
-          Generator::generateUnconditionallyKeep,
+          (generator, clazz) -> generator.generateUnconditionallyKeep(),
           write);
     }
   }
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingClasspathSplitTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingClasspathSplitTest.java
new file mode 100644
index 0000000..e87a04d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingClasspathSplitTest.java
@@ -0,0 +1,143 @@
+// Copyright (c) 2025, 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.memberrebinding;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestBuilder;
+import com.android.tools.r8.TestCompileResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ThrowableConsumer;
+import com.android.tools.r8.memberrebinding.testclasses.MemberRebindingClasspathSplitTestClasses;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MemberRebindingClasspathSplitTest extends TestBase {
+  @Parameter(0)
+  public TestParameters parameters;
+
+  private static class TestConfig {
+    private final String desc;
+
+    private final ThrowableConsumer<? super TestBuilder<?, ?>> addToClasspath;
+    private final ThrowableConsumer<? super TestBuilder<?, ?>> addToProgrampath;
+    private final ThrowableConsumer<? super TestCompileResult<?, ?>> addToRunClasspath;
+
+    private TestConfig(
+        String desc,
+        ThrowableConsumer<? super TestBuilder<?, ?>> addToClasspath,
+        ThrowableConsumer<? super TestBuilder<?, ?>> addToProgrampath,
+        ThrowableConsumer<? super TestCompileResult<?, ?>> addToRunClasspath) {
+      this.desc = desc;
+      this.addToClasspath = addToClasspath;
+      this.addToProgrampath = addToProgrampath;
+      this.addToRunClasspath = addToRunClasspath;
+    }
+
+    @Override
+    public String toString() {
+      return desc;
+    }
+  }
+
+  @Parameter(1)
+  public TestConfig split;
+
+  @Parameters(name = "{0}, classpathsplit: {1}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimesAndApiLevels().build(),
+        ImmutableList.of(
+            new TestConfig(
+                "Both A and B on classpath",
+                b -> {
+                  b.addClasspathClasses(
+                      MemberRebindingClasspathSplitTestClasses.getA(),
+                      MemberRebindingClasspathSplitTestClasses.getB());
+                },
+                b -> {},
+                b -> {
+                  b.addRunClasspathClasses(
+                      MemberRebindingClasspathSplitTestClasses.getA(),
+                      MemberRebindingClasspathSplitTestClasses.getB());
+                }),
+            new TestConfig(
+                "Both A and B on classpath, m() bridge removed from B",
+                b -> {
+                  b.addClasspathClasses(MemberRebindingClasspathSplitTestClasses.getA());
+                  b.addClasspathClassFileData(
+                      transformer(MemberRebindingClasspathSplitTestClasses.getB())
+                          .removeMethodsWithName("m")
+                          .transform());
+                },
+                b -> {},
+                b -> {
+                  b.addRunClasspathClasses(MemberRebindingClasspathSplitTestClasses.getA());
+                  b.addRunClasspathClassFileData(
+                      transformer(MemberRebindingClasspathSplitTestClasses.getB())
+                          .removeMethodsWithName("m")
+                          .transform());
+                }),
+            new TestConfig(
+                "A on classpath and B on programpath",
+                b -> {
+                  b.addClasspathClasses(MemberRebindingClasspathSplitTestClasses.getA());
+                },
+                b -> {
+                  b.addProgramClasses(MemberRebindingClasspathSplitTestClasses.getB());
+                },
+                b -> {
+                  b.addRunClasspathClasses(MemberRebindingClasspathSplitTestClasses.getA());
+                }),
+            new TestConfig(
+                "A on classpath and B on programpath, m() bridge removed from B",
+                b -> {
+                  b.addClasspathClasses(MemberRebindingClasspathSplitTestClasses.getA());
+                },
+                b -> {
+                  b.addProgramClassFileData(
+                      transformer(MemberRebindingClasspathSplitTestClasses.getB())
+                          .removeMethodsWithName("m")
+                          .transform());
+                },
+                b -> {
+                  b.addRunClasspathClasses(MemberRebindingClasspathSplitTestClasses.getA());
+                })));
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters)
+        .addInnerClasses(getClass())
+        .apply(split.addToClasspath)
+        .apply(split.addToProgrampath)
+        .allowStdoutMessages()
+        .addDontObfuscate()
+        .addDontOptimize()
+        .addKeepRules("-keep class " + Main.class.getTypeName() + " { void main(...); }")
+        .compile()
+        .apply(split.addToRunClasspath)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("A", "A");
+  }
+
+  public static class C extends MemberRebindingClasspathSplitTestClasses.B {
+    public String superm() {
+      return super.m();
+    }
+  }
+
+  public static class Main {
+    public static void main(String[] strArr) {
+      C c = new C();
+      System.out.println(c.m());
+      System.out.println(c.superm());
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/testclasses/MemberRebindingClasspathSplitTestClasses.java b/src/test/java/com/android/tools/r8/memberrebinding/testclasses/MemberRebindingClasspathSplitTestClasses.java
new file mode 100644
index 0000000..5c0d27c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/memberrebinding/testclasses/MemberRebindingClasspathSplitTestClasses.java
@@ -0,0 +1,22 @@
+// Copyright (c) 2025, 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.memberrebinding.testclasses;
+
+public class MemberRebindingClasspathSplitTestClasses {
+  public static Class<?> getA() {
+    return A.class;
+  }
+
+  public static Class<?> getB() {
+    return B.class;
+  }
+
+  static class A {
+    public String m() {
+      return "A";
+    }
+  }
+
+  public static class B extends A {}
+}
diff --git a/src/test/java/com/android/tools/r8/optimize/listiteration/ListIterationRewriterTest.java b/src/test/java/com/android/tools/r8/optimize/listiteration/ListIterationRewriterTest.java
index 96e2020..3caf31f 100644
--- a/src/test/java/com/android/tools/r8/optimize/listiteration/ListIterationRewriterTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/listiteration/ListIterationRewriterTest.java
@@ -4,12 +4,14 @@
 
 package com.android.tools.r8.optimize.listiteration;
 
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
 import java.util.ArrayList;
@@ -548,16 +550,34 @@
   }
 
   @Test
-  public void testRewrites() throws Exception {
+  public void testRewritesD8() throws Exception {
+    parameters.assumeDexRuntime();
     // Run all test classes in one compilation for faster tests compared to compiling each one
     // separately.
-    Class[] classes =
-        Arrays.stream(RewritesMain.CASES).map(x -> x.getClass()).toArray(Class[]::new);
-    String[] expectedOutput = getExpectedTestOutput(RewritesMain.CASES);
+    testForD8(parameters)
+        .addProgramClassesAndInnerClasses(ListUtils.map(RewritesMain.CASES, TestCase::getClass))
+        .addProgramClasses(RewritesMain.class, TestCase.class)
+        .addOptionsModification(
+            o -> {
+              assertFalse(o.testing.listIterationRewritingRewriteCustomIterators);
+              o.testing.listIterationRewritingRewriteCustomIterators = true;
+              assertFalse(o.testing.listIterationRewritingRewriteInterfaces);
+              o.testing.listIterationRewritingRewriteInterfaces = true;
+            })
+        .release()
+        .compile()
+        .run(parameters.getRuntime(), RewritesMain.class)
+        .assertSuccessWithOutputLines(getExpectedTestOutput(RewritesMain.CASES))
+        .inspect(
+            inspector -> inspector.forAllClasses(ListIterationRewriterTest::checkNoIteratorInvoke));
+  }
 
-    testForR8(parameters.getBackend())
-        .setMinApi(parameters)
-        .addProgramClassesAndInnerClasses(classes)
+  @Test
+  public void testRewritesR8() throws Exception {
+    // Run all test classes in one compilation for faster tests compared to compiling each one
+    // separately.
+    testForR8(parameters)
+        .addProgramClassesAndInnerClasses(ListUtils.map(RewritesMain.CASES, TestCase::getClass))
         .addProgramClasses(RewritesMain.class, TestCase.class)
         .addKeepMainRule(RewritesMain.class)
         .addDontObfuscate()
@@ -565,7 +585,7 @@
         .enableInliningAnnotations()
         .compile()
         .run(parameters.getRuntime(), RewritesMain.class)
-        .assertSuccessWithOutputLines(expectedOutput)
+        .assertSuccessWithOutputLines(getExpectedTestOutput(RewritesMain.CASES))
         .inspect(
             inspector -> inspector.forAllClasses(ListIterationRewriterTest::checkNoIteratorInvoke));
   }
@@ -620,21 +640,39 @@
   }
 
   @Test
-  public void testNoRewrites() throws Exception {
+  public void testNoRewritesD8() throws Exception {
+    parameters.assumeDexRuntime();
     // Run all test classes in one compilation for faster tests compared to compiling each one
     // separately.
-    Class[] classes =
-        Arrays.stream(NoRewritesMain.CASES).map(x -> x.getClass()).toArray(Class[]::new);
-    String[] expectedOutput = getExpectedTestOutput(NoRewritesMain.CASES);
+    testForD8(parameters)
+        .addOptionsModification(
+            o -> {
+              // Ensure tests do not pass due to the optimization being disabled.
+              assertFalse(o.testing.listIterationRewritingRewriteCustomIterators);
+              o.testing.listIterationRewritingRewriteCustomIterators = true;
+              assertFalse(o.testing.listIterationRewritingRewriteInterfaces);
+              o.testing.listIterationRewritingRewriteInterfaces = true;
+            })
+        .addProgramClassesAndInnerClasses(ListUtils.map(NoRewritesMain.CASES, TestCase::getClass))
+        .addProgramClasses(NoRewritesMain.class, TestCase.class)
+        .release()
+        .compile()
+        .run(parameters.getRuntime(), NoRewritesMain.class)
+        .assertSuccessWithOutputLines(getExpectedTestOutput(NoRewritesMain.CASES))
+        .inspect(
+            inspector ->
+                inspector.forAllClasses(ListIterationRewriterTest::checkIteratorInvokeExists));
+  }
 
-    testForR8(parameters.getBackend())
-        .setMinApi(parameters)
+  @Test
+  public void testNoRewritesR8() throws Exception {
+    testForR8(parameters)
         .addOptionsModification(
             o -> {
               // Ensure tests do not pass due to the optimization being disabled.
               o.testing.listIterationRewritingRewriteCustomIterators = true;
             })
-        .addProgramClassesAndInnerClasses(classes)
+        .addProgramClassesAndInnerClasses(ListUtils.map(NoRewritesMain.CASES, TestCase::getClass))
         .addProgramClasses(NoRewritesMain.class, TestCase.class)
         .addKeepMainRule(NoRewritesMain.class)
         .addDontObfuscate()
@@ -642,7 +680,7 @@
         .noHorizontalClassMerging()
         .compile()
         .run(parameters.getRuntime(), NoRewritesMain.class)
-        .assertSuccessWithOutputLines(expectedOutput)
+        .assertSuccessWithOutputLines(getExpectedTestOutput(NoRewritesMain.CASES))
         .inspect(
             inspector ->
                 inspector.forAllClasses(ListIterationRewriterTest::checkIteratorInvokeExists));
diff --git a/src/test/java/com/android/tools/r8/regress/b426351560/Regress426351560Test.java b/src/test/java/com/android/tools/r8/regress/b426351560/Regress426351560Test.java
index 9da5d33..c11e2a0 100644
--- a/src/test/java/com/android/tools/r8/regress/b426351560/Regress426351560Test.java
+++ b/src/test/java/com/android/tools/r8/regress/b426351560/Regress426351560Test.java
@@ -31,8 +31,6 @@
 
   @Test
   public void testR8() throws Exception {
-    // TODO(b/427887773)
-    assumeFalse(parameters.isRandomPartialCompilation());
     testForR8(parameters)
         .addInnerClasses(Regress426351560TestClasses.class, getClass())
         .addKeepRules("-keep class " + Main.class.getTypeName() + " { *; }")
diff --git a/src/test/java/com/android/tools/r8/regress/b426351560/Regress427887773Test.java b/src/test/java/com/android/tools/r8/regress/b426351560/Regress427887773Test.java
index 67019be..a15a998 100644
--- a/src/test/java/com/android/tools/r8/regress/b426351560/Regress427887773Test.java
+++ b/src/test/java/com/android/tools/r8/regress/b426351560/Regress427887773Test.java
@@ -3,16 +3,10 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.regress.b426351560;
 
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.regress.b426351560.testclasses.Regress426351560TestClasses;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -32,29 +26,14 @@
 
   @Test
   public void testR8() throws Exception {
-    try {
-      testForR8(parameters)
-          .addInnerClasses(getClass())
-          .addClasspathClasses(Regress426351560TestClasses.getClasses())
-          .addKeepRules("-keep class " + Main.class.getTypeName() + " { *; }")
-          .compile()
-          .addRunClasspathClasses(Regress426351560TestClasses.getClasses())
-          .run(parameters.getRuntime(), Main.class)
-          .applyIf(
-              parameters.isDexRuntime()
-                  && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N),
-              rr -> rr.assertFailureWithErrorThatThrows(IllegalAccessError.class),
-              // for jdk based tests: java.lang.VerifyError: Bad invokespecial instruction:
-              // interface method reference is in an indirect superinterface.
-              rr -> rr.assertFailureWithErrorThatThrows(VerifyError.class));
-    } catch (CompilationFailedException e) {
-      if (parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.N)) {
-        assertTrue(e.getCause() instanceof AssertionError);
-        assertThat(e.getCause().getMessage(), containsString("CC was already present."));
-      } else {
-        throw e;
-      }
-    }
+    testForR8(parameters)
+        .addInnerClasses(getClass())
+        .addClasspathClasses(Regress426351560TestClasses.getClasses())
+        .addKeepRules("-keep class " + Main.class.getTypeName() + " { *; }")
+        .compile()
+        .addRunClasspathClasses(Regress426351560TestClasses.getClasses())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Hello, world!");
   }
 
   public static class Main extends Regress426351560TestClasses.A {
diff --git a/src/test/testbase/java/com/android/tools/r8/ProguardVersion.java b/src/test/testbase/java/com/android/tools/r8/ProguardVersion.java
index 5d75149..569e22a 100644
--- a/src/test/testbase/java/com/android/tools/r8/ProguardVersion.java
+++ b/src/test/testbase/java/com/android/tools/r8/ProguardVersion.java
@@ -11,8 +11,7 @@
 
 public enum ProguardVersion {
   V7_0_0("7.0.0"),
-  V7_3_2("7.3.2"),
-  V7_4_1("7.4.1");
+  V7_7_0("7.7.0");
 
   private final String version;
 
@@ -21,7 +20,7 @@
   }
 
   public static ProguardVersion getLatest() {
-    return V7_4_1;
+    return V7_7_0;
   }
 
   public Path getProguardScript() {
diff --git a/src/test/testbase/java/com/android/tools/r8/TestBase.java b/src/test/testbase/java/com/android/tools/r8/TestBase.java
index aa40c91..f95cbc1 100644
--- a/src/test/testbase/java/com/android/tools/r8/TestBase.java
+++ b/src/test/testbase/java/com/android/tools/r8/TestBase.java
@@ -2210,13 +2210,9 @@
   }
 
   protected void assertListsAreEqual(List<String> expected, List<String> actual) {
-    int minLines = Math.min(expected.size(), actual.size());
-    for (int i = 0; i < minLines; i++) {
-      assertEquals(expected.get(i), actual.get(i));
-    }
-    if (expected.size() != actual.size()) {
-      assertEquals(
-          "", expected.size() > actual.size() ? expected.get(minLines) : actual.get(minLines));
+    assertEquals("List sizes differ: ", expected.size(), actual.size());
+    for (int i = 0; i < expected.size(); i++) {
+      assertEquals("Index: " + i, expected.get(i), actual.get(i));
     }
   }
 
diff --git a/src/test/testbase/java/com/android/tools/r8/keepanno/KeepAnnoTestUtils.java b/src/test/testbase/java/com/android/tools/r8/keepanno/KeepAnnoTestUtils.java
index 36968f6..101a316 100644
--- a/src/test/testbase/java/com/android/tools/r8/keepanno/KeepAnnoTestUtils.java
+++ b/src/test/testbase/java/com/android/tools/r8/keepanno/KeepAnnoTestUtils.java
@@ -36,7 +36,7 @@
 
 public class KeepAnnoTestUtils {
 
-  public static ProguardVersion PG_VERSION = ProguardVersion.V7_4_1;
+  public static ProguardVersion PG_VERSION = ProguardVersion.V7_7_0;
 
   public static final String DESCRIPTOR_PREFIX = "Landroidx/annotation/keep/";
   public static final String DESCRIPTOR_LEGACY_PREFIX =
diff --git a/src/test/testbase/java/com/android/tools/r8/profile/art/utils/ArtProfileInspector.java b/src/test/testbase/java/com/android/tools/r8/profile/art/utils/ArtProfileInspector.java
index fb96468..6666333 100644
--- a/src/test/testbase/java/com/android/tools/r8/profile/art/utils/ArtProfileInspector.java
+++ b/src/test/testbase/java/com/android/tools/r8/profile/art/utils/ArtProfileInspector.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.profile.art.utils;
 
-import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 
@@ -123,6 +122,11 @@
   }
 
   public ArtProfileInspector inspectMethodRule(
+      MethodSubject methodSubject, Consumer<ArtProfileMethodRuleInspector> inspector) {
+    return inspectMethodRule(methodSubject.getFinalReference(), inspector);
+  }
+
+  public ArtProfileInspector inspectMethodRule(
       MethodReference methodReference, Consumer<ArtProfileMethodRuleInspector> inspector) {
     assertContainsMethodRule(methodReference);
     ExternalArtProfileMethodRule methodRule = artProfile.getMethodRule(methodReference);
diff --git a/src/test/testbase/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java b/src/test/testbase/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
index f73a896..753027f 100644
--- a/src/test/testbase/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
+++ b/src/test/testbase/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
@@ -52,6 +52,11 @@
         InterfaceDesugaringForTesting.getCompanionClassDescriptor(clazz.getDescriptor()));
   }
 
+  public static ClassReference syntheticClassWithMinimalName(Class<?> clazz, int id) {
+    return SyntheticNaming.makeMinimalSyntheticReferenceForTest(
+        Reference.classFromClass(clazz), Integer.toString(id));
+  }
+
   private static ClassReference syntheticClass(Class<?> clazz, SyntheticKind kind, int id) {
     return syntheticClass(Reference.classFromClass(clazz), kind, id);
   }
diff --git a/src/test/testbase/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/testbase/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index 143a44d..424e8c5 100644
--- a/src/test/testbase/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/testbase/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -1772,4 +1772,86 @@
           }
         });
   }
+
+  public static class AnnotationBuilder {
+    private final AnnotationVisitor av;
+
+    private AnnotationBuilder(AnnotationVisitor av) {
+      this.av = av;
+    }
+
+    public AnnotationBuilder setField(String name, Object value) {
+      if (value instanceof Class) {
+        av.visit(name, Type.getType((Class<?>) value));
+      } else {
+        av.visit(name, value);
+      }
+      return this;
+    }
+
+    public AnnotationBuilder setArray(String name, Object... values) {
+      AnnotationVisitor subAv = av.visitArray(name);
+      Arrays.stream(values).forEach(value -> subAv.visit(null, value));
+      return this;
+    }
+  }
+
+  public ClassFileTransformer setAnnotation(
+      Class<?> annotationClass, Consumer<AnnotationBuilder> annotationBuilderConsumer) {
+    return addClassTransformer(
+        new ClassTransformer() {
+
+          @Override
+          public void visit(
+              int version,
+              int access,
+              String name,
+              String signature,
+              String superName,
+              String[] interfaces) {
+            super.visit(version, access, name, signature, superName, interfaces);
+            AnnotationVisitor av =
+                super.visitAnnotation(
+                    Reference.classFromClass(annotationClass).getDescriptor(), false);
+            annotationBuilderConsumer.accept(new AnnotationBuilder(av));
+            av.visitEnd();
+          }
+
+          @Override
+          public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
+            return null;
+            // Remove all other annotations.
+          }
+        });
+  }
+
+  public ClassFileTransformer setAnnotation(
+      MethodPredicate predicate,
+      Class<?> annotationClass,
+      Consumer<AnnotationBuilder> annotationBuilderConsumer) {
+    return addClassTransformer(
+        new ClassTransformer() {
+          @Override
+          public MethodVisitor visitMethod(
+              int access, String name, String descriptor, String signature, String[] exceptions) {
+            MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
+            if (predicate.test(access, name, descriptor, signature, exceptions)) {
+              AnnotationVisitor av =
+                  mv.visitAnnotation(
+                      Reference.classFromClass(annotationClass).getDescriptor(), false);
+              annotationBuilderConsumer.accept(new AnnotationBuilder(av));
+              av.visitEnd();
+              return new MethodVisitor(ASM_VERSION, mv) {
+                @Override
+                public AnnotationVisitor visitAnnotation(
+                    final String descriptor, final boolean visible) {
+                  return null;
+                  // Remove all other annotations.
+                }
+              };
+            }
+            return mv;
+          }
+        });
+  }
 }
diff --git a/third_party/chrome/monochrome_public_minimal_apks/chrome_200520.tar.gz.sha1 b/third_party/chrome/monochrome_public_minimal_apks/chrome_200520.tar.gz.sha1
deleted file mode 100644
index a4d4e8f..0000000
--- a/third_party/chrome/monochrome_public_minimal_apks/chrome_200520.tar.gz.sha1
+++ /dev/null
@@ -1 +0,0 @@
-4a9de4ba961c2f0c953c0a88675f29959e8602a7
\ No newline at end of file
diff --git a/third_party/proguard/README.google b/third_party/proguard/README.google
index 4501b28..e510b61 100644
--- a/third_party/proguard/README.google
+++ b/third_party/proguard/README.google
@@ -1,9 +1,8 @@
 URL: https://github.com/Guardsquare/proguard/releases/download/v7.0.0/proguard-7.0.0.tar.gz
-URL: https://github.com/Guardsquare/proguard/releases/download/v7.3.2/proguard-7.3.2.tar.gz
-URL: https://github.com/Guardsquare/proguard/releases/download/v7.4.1/proguard-7.4.1.tar.gz
-Version: 7.0.0, 7.3.2, 7.4.1
+URL: https://github.com/Guardsquare/proguard/releases/download/v7.4.1/proguard-7.7.0.tar.gz
+Version: 7.0.0, 7.7.0
 License: GPL
-License File: proguard-7.0.0/docs/license.md, proguard-7.3.2/LICENSE, proguard-7.4.1/LICENSE
+License File: proguard-7.0.0/docs/license.md, proguard-7.7.0/LICENSE
 
 Description:
 ProGuard Java Optimizer and Obfuscator
diff --git a/third_party/proguard/proguard-7.3.2.tar.gz.sha1 b/third_party/proguard/proguard-7.3.2.tar.gz.sha1
deleted file mode 100644
index 8eafeac..0000000
--- a/third_party/proguard/proguard-7.3.2.tar.gz.sha1
+++ /dev/null
@@ -1 +0,0 @@
-87ab4bba457938929d5a5cafb579a8aa7630b428
\ No newline at end of file
diff --git a/third_party/proguard/proguard-7.4.1.tar.gz.sha1 b/third_party/proguard/proguard-7.4.1.tar.gz.sha1
deleted file mode 100644
index 4c33fe2..0000000
--- a/third_party/proguard/proguard-7.4.1.tar.gz.sha1
+++ /dev/null
@@ -1 +0,0 @@
-95f547ec8e6d15338fb6c7c32cc7cf0b27e049d6
\ No newline at end of file
diff --git a/third_party/proguard/proguard-7.7.0.tar.gz.sha1 b/third_party/proguard/proguard-7.7.0.tar.gz.sha1
new file mode 100644
index 0000000..2ebfe2c
--- /dev/null
+++ b/third_party/proguard/proguard-7.7.0.tar.gz.sha1
@@ -0,0 +1 @@
+16e41bfab4da5ebf3e8cc612382a5a38d1770714
\ No newline at end of file
diff --git a/tools/update-androidx-keep-annotations.py b/tools/update-androidx-keep-annotations.py
index 29bf464..3f5da7c 100755
--- a/tools/update-androidx-keep-annotations.py
+++ b/tools/update-androidx-keep-annotations.py
@@ -15,10 +15,17 @@
 
 def parse_options():
     parser = argparse.ArgumentParser(description='Update androidx keep annotations')
-    parser.add_argument('--androidx',
-                        metavar=('<path>'),
-                        required=True,
-                        help='Path to the androidx checkout')
+    parser.add_argument(
+        '--androidx',
+        metavar=('<path>'),
+        required=True,
+        help='Path to the androidx checkout')
+    parser.add_argument(
+        '--dry-run',
+        '--dry_run',
+        help="Don't copy, just print what to copy",
+        default=False,
+        action='store_true')
     return parser.parse_args()
 
 
@@ -59,9 +66,22 @@
                     .format(filename=os.path.join(root, filename)))
                 sys.exit(1)
 
-            shutil.copyfile(
-                os.path.join(root, filename),
-                os.path.join(dest_dir, filename))
+            files = (
+                'UnconditionallyKeep.kt',
+                'Unspecified.kt',
+                'UsesReflectionToAccessField.kt',
+                'UsesReflectionToAccessMethod.kt',
+                'UsesReflectionToConstruct.kt',
+            )
+            if not filename in files:
+                print('Skipping {filename}'.format(filename=filename))
+                continue
+
+            src = os.path.join(root, filename)
+            dest = os.path.join(dest_dir, filename)
+            print("Copying '{src}' to '{dest}'".format(src=src, dest=dest))
+            if not args.dry_run:
+                shutil.copyfile(src, dest)
 
 
 if __name__ == '__main__':