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