Only pass mapping file lines needed in resource shrinker

This should greatly reduce the memory needed to do resource shrinking
and avoid building up a potentially very large mapping.


Bug: b/406525499
Change-Id: Ie7e64c3c2645ce9e6a6516520b88e79113c7b6d5
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 8ef2201..47298f9 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -6,6 +6,7 @@
 import static com.android.tools.r8.utils.InternalOptions.DETERMINISTIC_DEBUGGING;
 import static com.android.tools.r8.utils.MapConsumerUtils.wrapExistingMapConsumerIfNotNull;
 
+import com.android.build.shrinker.r8integration.LegacyResourceShrinker;
 import com.android.tools.r8.ProgramResource.Kind;
 import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.dump.DumpOptions;
@@ -24,6 +25,8 @@
 import com.android.tools.r8.keepanno.ast.KeepDeclaration;
 import com.android.tools.r8.keepanno.keeprules.KeepRuleExtractor;
 import com.android.tools.r8.metadata.R8BuildMetadata;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.ClassNamingForNameMapper;
 import com.android.tools.r8.naming.MapConsumer;
 import com.android.tools.r8.naming.ProguardMapStringConsumer;
 import com.android.tools.r8.naming.SourceFileRewriter;
@@ -77,6 +80,7 @@
 import java.util.function.BiPredicate;
 import java.util.function.Consumer;
 import java.util.function.Function;
+import java.util.function.Predicate;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
@@ -1397,10 +1401,7 @@
         wrapExistingMapConsumerIfNotNull(
             mapConsumer,
             androidResourceConsumer,
-            nonNulStringConsumer ->
-                ProguardMapStringConsumer.builder()
-                    .setStringConsumer(new ResourceShrinkerMapStringConsumer(internal))
-                    .build());
+            nonNulStringConsumer -> new ResourceShrinkerMapStringConsumer(internal));
     // Amend the usage information consumer with options from the proguard configuration.
     internal.usageInformationConsumer =
         wrapStringConsumer(
@@ -1539,42 +1540,31 @@
     return optionConsumer;
   }
 
-  private static class ResourceShrinkerMapStringConsumer implements StringConsumer {
+  private static class ResourceShrinkerMapStringConsumer implements MapConsumer {
 
     private final InternalOptions internal;
-    private List<String> mappingStrings = new ArrayList<>();
-    private StringBuilder current = new StringBuilder();
+    private final Predicate<String> classNamePredicate;
+    private StringBuilder resultBuilder = new StringBuilder();
 
     public ResourceShrinkerMapStringConsumer(InternalOptions internal) {
       this.internal = internal;
-    }
-
-    @Override
-    public void accept(String string, DiagnosticsHandler handler) {
-      current.append(string);
-      if (string.endsWith("\n")) {
-        addNoneCommentLinesAndReset();
-      }
-    }
-
-    private void addNoneCommentLinesAndReset() {
-      StringUtils.splitLines(
-          current.toString(),
-          false,
-          line -> {
-            if (!line.startsWith("#")) {
-              mappingStrings.add(line);
-            }
-          });
-      current.setLength(0);
+      this.classNamePredicate =
+          LegacyResourceShrinker.classNamesNeededForResourceShrinkingPredicate();
     }
 
     @Override
     public void finished(DiagnosticsHandler handler) {
-      if (current.length() != 0) {
-        addNoneCommentLinesAndReset();
+      internal.androidResourceProguardMapStrings = StringUtils.splitLines(resultBuilder.toString());
+      resultBuilder = null;
+    }
+
+    @Override
+    public void accept(DiagnosticsHandler diagnosticsHandler, ClassNameMapper classNameMapper) {
+      for (ClassNamingForNameMapper entry : classNameMapper.getClassNameMappings().values()) {
+        if (classNamePredicate.test(entry.originalName)) {
+          resultBuilder.append(entry.toProguardMapSource());
+        }
       }
-      internal.androidResourceProguardMapStrings = mappingStrings;
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
index 60222b7..f917f03 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
@@ -550,6 +550,10 @@
 
   @Override
   public String toString() {
+    return toProguardMapSource();
+  }
+
+  public String toProguardMapSource() {
     StringBuilder builder = new StringBuilder();
     write(ChainableStringConsumer.wrap(builder::append));
     return builder.toString();
diff --git a/src/resourceshrinker/java/com/android/build/shrinker/r8integration/LegacyResourceShrinker.java b/src/resourceshrinker/java/com/android/build/shrinker/r8integration/LegacyResourceShrinker.java
index baf1f72..0ac9b44 100644
--- a/src/resourceshrinker/java/com/android/build/shrinker/r8integration/LegacyResourceShrinker.java
+++ b/src/resourceshrinker/java/com/android/build/shrinker/r8integration/LegacyResourceShrinker.java
@@ -16,7 +16,9 @@
 import com.android.build.shrinker.graph.ProtoResourcesGraphBuilder;
 import com.android.build.shrinker.obfuscation.ProguardMappingsRecorder;
 import com.android.build.shrinker.r8integration.R8ResourceShrinkerState.R8ResourceShrinkerModel;
+import com.android.build.shrinker.usages.AppCompat;
 import com.android.build.shrinker.usages.DexFileAnalysisCallback;
+import com.android.build.shrinker.usages.DexUsageRecorderKt;
 import com.android.build.shrinker.usages.ProtoAndroidManifestUsageRecorderKt;
 import com.android.build.shrinker.usages.R8ResourceShrinker;
 import com.android.build.shrinker.usages.ToolsAttributeUsageRecorderKt;
@@ -43,6 +45,7 @@
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import javax.xml.parsers.ParserConfigurationException;
 import org.xml.sax.SAXException;
@@ -163,6 +166,12 @@
     return new Builder();
   }
 
+  public static Predicate<String> classNamesNeededForResourceShrinkingPredicate() {
+    return AppCompat.INSTANCE
+        .getRequiredClassNamesPredicate()
+        .or(DexUsageRecorderKt.getRequiredClassNamesPredicate());
+  }
+
   public ShrinkerResult run() throws IOException, ParserConfigurationException, SAXException {
     R8ResourceShrinkerModel model = new R8ResourceShrinkerModel(debugReporter, true);
     for (PathAndBytes pathAndBytes : resourceTables.keySet()) {
diff --git a/src/resourceshrinker/java/com/android/build/shrinker/usages/AppCompat.kt b/src/resourceshrinker/java/com/android/build/shrinker/usages/AppCompat.kt
index bbfea7c..6038eff 100644
--- a/src/resourceshrinker/java/com/android/build/shrinker/usages/AppCompat.kt
+++ b/src/resourceshrinker/java/com/android/build/shrinker/usages/AppCompat.kt
@@ -17,6 +17,7 @@
 package com.android.build.shrinker.usages
 
 import com.android.build.shrinker.obfuscation.ObfuscatedClasses
+import java.util.function.Predicate
 
 internal object AppCompat {
     // Known AppCompat classes which use Resources.getIdentifier but should not trigger mode that
@@ -30,4 +31,10 @@
 
     internal fun isAppCompatClass(name: String, obfuscation: ObfuscatedClasses): Boolean =
         APP_COMPAT_CLASSES_ALLOWED_FOR_GET_IDENTIFIER.contains(obfuscation.resolveOriginalClass(name))
+
+    fun getRequiredClassNamesPredicate(): Predicate<String> {
+        return Predicate {
+                className -> APP_COMPAT_CLASSES_ALLOWED_FOR_GET_IDENTIFIER.contains(className)
+        }
+    }
 }
diff --git a/src/resourceshrinker/java/com/android/build/shrinker/usages/DexUsageRecorder.kt b/src/resourceshrinker/java/com/android/build/shrinker/usages/DexUsageRecorder.kt
index 5d6a0df..2ac070f 100644
--- a/src/resourceshrinker/java/com/android/build/shrinker/usages/DexUsageRecorder.kt
+++ b/src/resourceshrinker/java/com/android/build/shrinker/usages/DexUsageRecorder.kt
@@ -25,6 +25,7 @@
 import com.android.tools.r8.references.MethodReference
 import java.nio.file.Files
 import java.nio.file.Path
+import java.util.function.Predicate
 
 /**
  * Records resource usages, detects usages of WebViews and {@code Resources#getIdentifier},
@@ -56,6 +57,10 @@
     }
 }
 
+fun getRequiredClassNamesPredicate(): Predicate<String> {
+    return Predicate { className -> className.contains("R$") }
+}
+
 class DexFileAnalysisCallback(
         private val path: Path,
         private val model: ResourceShrinkerModel