Split rewriting of record invokedynamic from DexItemBasedXXXString

Bug: b/293592205
Change-Id: I8c0136b093874d77602624454e3bb8bf005696b5
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 91a8318..5f11e97 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -59,9 +59,12 @@
 import com.android.tools.r8.jar.CfApplicationWriter;
 import com.android.tools.r8.kotlin.KotlinMetadataRewriter;
 import com.android.tools.r8.kotlin.KotlinMetadataUtils;
+import com.android.tools.r8.naming.IdentifierMinifier;
 import com.android.tools.r8.naming.Minifier;
+import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.naming.PrefixRewritingNamingLens;
 import com.android.tools.r8.naming.ProguardMapMinifier;
+import com.android.tools.r8.naming.RecordInvokeDynamicRewriter;
 import com.android.tools.r8.naming.RecordRewritingNamingLens;
 import com.android.tools.r8.naming.signature.GenericSignatureRewriter;
 import com.android.tools.r8.optimize.LegacyAccessModifier;
@@ -767,8 +770,12 @@
         appView.setNamingLens(new Minifier(appView.withLiveness()).run(executorService, timing));
         timing.end();
       } else {
-        new Minifier(appView.withLiveness())
-            .replaceDexItemBasedConstString(executorService, timing);
+        timing.begin("MinifyIdentifiers");
+        new IdentifierMinifier(appView, NamingLens.getIdentityLens()).run(executorService);
+        timing.end();
+        timing.begin("RecordInvokeDynamicRewrite");
+        new RecordInvokeDynamicRewriter(appView, NamingLens.getIdentityLens()).run(executorService);
+        timing.end();
       }
       appView.appInfo().notifyMinifierFinished();
 
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
index 8b4970f..ba443fe 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
@@ -22,7 +22,6 @@
 import com.android.tools.r8.graph.DexValue.DexItemBasedValueString;
 import com.android.tools.r8.graph.DexValue.DexValueString;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.desugar.records.RecordCfToCfRewriter;
 import com.android.tools.r8.shaking.ProguardClassFilter;
 import com.android.tools.r8.utils.ArrayUtils;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -36,21 +35,19 @@
  * Replaces all instances of DexItemBasedConstString by ConstString, and all instances of
  * DexItemBasedValueString by DexValueString.
  */
-class IdentifierMinifier {
+public class IdentifierMinifier {
 
   private final AppView<?> appView;
   private final ProguardClassFilter adaptClassStrings;
-  private final RecordCfToCfRewriter recordCfToCfRewriter;
   private final NamingLens lens;
 
-  IdentifierMinifier(AppView<?> appView, NamingLens lens) {
+  public IdentifierMinifier(AppView<?> appView, NamingLens lens) {
     this.appView = appView;
     this.adaptClassStrings = appView.options().getProguardConfiguration().getAdaptClassStrings();
-    this.recordCfToCfRewriter = RecordCfToCfRewriter.create(appView);
     this.lens = lens;
   }
 
-  void run(ExecutorService executorService) throws ExecutionException {
+  public void run(ExecutorService executorService) throws ExecutionException {
     if (!adaptClassStrings.isEmpty()) {
       adaptClassStrings(executorService);
     }
@@ -211,9 +208,6 @@
                   return new CfConstString(
                       cnst.getNameComputationInfo()
                           .computeNameFor(cnst.getItem(), appView, appView.graphLens(), lens));
-                } else if (recordCfToCfRewriter != null && instruction.isInvokeDynamic()) {
-                  return recordCfToCfRewriter.rewriteRecordInvokeDynamic(
-                      instruction.asInvokeDynamic(), programMethod, lens);
                 }
                 return instruction;
               },
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index 229de6a..40490d4 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -86,18 +86,14 @@
     new IdentifierMinifier(appView, lens).run(executorService);
     timing.end();
 
+    timing.begin("RecordInvokeDynamicRewrite");
+    new RecordInvokeDynamicRewriter(appView, lens).run(executorService);
+    timing.end();
+
     appView.notifyOptimizationFinishedForTesting();
     return lens;
   }
 
-  public void replaceDexItemBasedConstString(ExecutorService executorService, Timing timing)
-      throws ExecutionException {
-    timing.begin("ReplaceDexItemBasedConstString");
-    new IdentifierMinifier(appView, NamingLens.getIdentityLens())
-        .replaceDexItemBasedConstString(executorService);
-    timing.end();
-  }
-
   abstract static class BaseMinificationNamingStrategy {
 
     // We have to ensure that the names proposed by the minifier is not used in the obfuscation
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
index f08df03..423978d 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -156,6 +156,10 @@
     new IdentifierMinifier(appView, lens).run(executorService);
     timing.end();
 
+    timing.begin("RecordInvokeDynamicRewrite");
+    new RecordInvokeDynamicRewriter(appView, lens).run(executorService);
+    timing.begin("MinifyIdentifiers");
+
     appView.notifyOptimizationFinishedForTesting();
     return lens;
   }
diff --git a/src/main/java/com/android/tools/r8/naming/RecordInvokeDynamicRewriter.java b/src/main/java/com/android/tools/r8/naming/RecordInvokeDynamicRewriter.java
new file mode 100644
index 0000000..c59aaf0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/RecordInvokeDynamicRewriter.java
@@ -0,0 +1,67 @@
+// Copyright (c) 2023, 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.naming;
+
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.records.RecordCfToCfRewriter;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.ThreadUtils;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+/** Rewrites the record invokedynamic in hashCode, equals and toString. */
+public class RecordInvokeDynamicRewriter {
+
+  private final AppView<?> appView;
+  private final RecordCfToCfRewriter recordCfToCfRewriter;
+  private final NamingLens lens;
+
+  public RecordInvokeDynamicRewriter(AppView<?> appView, NamingLens lens) {
+    this.appView = appView;
+    this.recordCfToCfRewriter = RecordCfToCfRewriter.create(appView);
+    this.lens = lens;
+  }
+
+  public void run(ExecutorService executorService) throws ExecutionException {
+    ThreadUtils.processItems(
+        appView.appInfo().classes(),
+        clazz -> {
+          clazz.forEachProgramMethodMatching(
+              DexEncodedMethod::hasCode, this::rewriteRecordInvokeDynamicInMethod);
+        },
+        executorService);
+  }
+
+  private void rewriteRecordInvokeDynamicInMethod(ProgramMethod programMethod) {
+    if (recordCfToCfRewriter == null) {
+      return;
+    }
+    if (!programMethod.getHolder().isRecord()) {
+      return;
+    }
+    Code code = programMethod.getDefinition().getCode();
+    assert code != null;
+    if (code.isDexCode()) {
+      return;
+    }
+    List<CfInstruction> instructions = code.asCfCode().getInstructions();
+    List<CfInstruction> newInstructions =
+        ListUtils.mapOrElse(
+            instructions,
+            (int index, CfInstruction instruction) -> {
+              if (instruction.isInvokeDynamic()) {
+                return recordCfToCfRewriter.rewriteRecordInvokeDynamic(
+                    instruction.asInvokeDynamic(), programMethod, lens);
+              }
+              return instruction;
+            },
+            instructions);
+    code.asCfCode().setInstructions(newInstructions);
+  }
+}